From 64b04e5c6aeba2b820e585ec57d4aef40727b90b Mon Sep 17 00:00:00 2001 From: Mayhem Date: Thu, 23 May 2013 23:06:58 +0200 Subject: [PATCH 001/172] Remove $.open workaround. --- lib/$.coffee | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/$.coffee b/lib/$.coffee index a37a9c494..16e2b82ae 100644 --- a/lib/$.coffee +++ b/lib/$.coffee @@ -147,9 +147,7 @@ $.off = (el, events, handler) -> $.event = (event, detail, root=d) -> root.dispatchEvent new CustomEvent event, {bubbles: true, detail} <% if (type === 'userscript') { %> -# XXX fix GM opening file://// for protocol-less URLs. -# https://github.com/greasemonkey/greasemonkey/issues/1719 -$.open = (URL) -> GM_openInTab ($.el 'a', href: URL).href +$.open = (URL) -> GM_openInTab URL <% } else { %> $.open = (URL) -> window.open URL, '_blank' <% } %> From 8200c10840a999f61b101de9afd5afb110d5b1cd Mon Sep 17 00:00:00 2001 From: Mayhem Date: Tue, 11 Jun 2013 01:08:43 +0200 Subject: [PATCH 002/172] Fix #1150 --- 4chan_x.user.js | 94 ++++++++++++++++++++++++++++++++++++++----------- changelog | 2 ++ script.coffee | 4 +-- 3 files changed, 77 insertions(+), 23 deletions(-) diff --git a/4chan_x.user.js b/4chan_x.user.js index 2d813fe86..520dc72c7 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -2291,13 +2291,13 @@ this.onready = function() { return _this.ready(); }; - return $.on($.id('recaptcha_widget_div'), 'DOMNodeInserted', this.onready); + return $.on($.id('captchaContainer'), 'DOMNodeInserted', this.onready); } }, ready: function() { var _this = this; if (this.challenge = $.id('recaptcha_challenge_field_holder')) { - $.off($.id('recaptcha_widget_div'), 'DOMNodeInserted', this.onready); + $.off($.id('captchaContainer'), 'DOMNodeInserted', this.onready); delete this.onready; } else { return; @@ -4940,20 +4940,33 @@ return "//archive.thedarkcave.org/" + board + "/full_image/" + filename; case 'hr': case 'tv': + case 'x': return "http://archive.4plebs.org/" + board + "/full_image/" + filename; case 'c': case 'w': case 'wg': return "//archive.nyafuu.org/" + board + "/full_image/" + filename; - case 'vg': - return "http://archive.nihil-ad-rem.net/" + board + "/full_image/" + filename; case 'd': + case 'h': + case 'v': return "//loveisover.me/" + board + "/full_image/" + filename; - case 'ck': - case 'fa': - case 'lit': + case 'vg': + return "http://nth.pensivenonsen.se/" + board + "/full_image/" + filename; + case 'adv': + case 'asp': + case 'cm': + case 'e': + case 'i': + case 'lgbt': + case 'n': + case 'o': + case 'p': + case 's': case 's4s': - return "//fuuka.warosu.org/" + board + "/full_image/" + filename; + case 't': + case 'trv': + case 'y': + return "//archive.foolzashit.com/" + board + "/full_image/" + filename; case 'cgl': case 'g': case 'mu': @@ -4961,8 +4974,13 @@ case 'an': case 'k': case 'toy': - case 'x': return "http://archive.heinessen.com/" + board + "/full_image/" + filename; + case '3': + case 'ck': + case 'fa': + case 'ic': + case 'lit': + return "//fuuka.warosu.org/" + board + "/full_image/" + filename; } }, post: function(board, postID) { @@ -4993,11 +5011,27 @@ case 'w': case 'wg': return "//archive.nyafuu.org/_/api/chan/post/?board=" + board + "&num=" + postID; - case 'v': - case 'vg': - return "http://archive.nihil-ad-rem.net/_/api/chan/post/?board=" + board + "&num=" + postID; case 'd': + case 'h': + case 'v': return "//loveisover.me/_/api/chan/post/?board=" + board + "&num=" + postID; + case 'vg': + return "http://nth.pensivenonsen.se/_/api/chan/post/?board=" + board + "&num=" + postID; + case 'adv': + case 'asp': + case 'cm': + case 'e': + case 'i': + case 'lgbt': + case 'n': + case 'o': + case 'p': + case 's': + case 's4s': + case 't': + case 'trv': + case 'y': + return "//archive.foolzashit.com/_/api/chan/post/?board=" + board + "&num=" + postID; } }, to: function(data) { @@ -5030,6 +5064,7 @@ url = Redirect.path('//archive.thedarkcave.org', 'foolfuuka', data); break; case 'hr': + case 'x': url = Redirect.path('http://archive.4plebs.org', 'foolfuuka', data); break; case 'c': @@ -5037,18 +5072,29 @@ case 'wg': url = Redirect.path('//archive.nyafuu.org', 'foolfuuka', data); break; - case 'v': - case 'vg': - url = Redirect.path('http://archive.nihil-ad-rem.net', 'foolfuuka', data); - break; case 'd': + case 'h': + case 'v': url = Redirect.path('//loveisover.me', 'foolfuuka', data); break; - case 'ck': - case 'fa': - case 'lit': + case 'vg': + url = Redirect.path('http://nth.pensivenonsen.se', 'foolfuuka', data); + break; + case 'adv': + case 'asp': + case 'cm': + case 'e': + case 'i': + case 'lgbt': + case 'n': + case 'o': + case 'p': + case 's': case 's4s': - url = Redirect.path('//fuuka.warosu.org', 'fuuka', data); + case 't': + case 'trv': + case 'y': + url = Redirect.path('//archive.foolzashit.com', 'foolfuuka', data); break; case 'diy': case 'g': @@ -5065,9 +5111,15 @@ case 'mlp': case 'r9k': case 'toy': - case 'x': url = Redirect.path('http://archive.heinessen.com', 'fuuka', data); break; + case '3': + case 'ck': + case 'fa': + case 'ic': + case 'lit': + url = Redirect.path('//fuuka.warosu.org', 'fuuka', data); + break; default: if (threadID) { url = "//boards.4chan.org/" + board + "/"; diff --git a/changelog b/changelog index 2c8d39353..0a39a9216 100644 --- a/changelog +++ b/changelog @@ -1,4 +1,6 @@ master +- Mayhem + Fix QR. 2.39.5 - Mayhem diff --git a/script.coffee b/script.coffee index 302d99bec..1996dfba1 100644 --- a/script.coffee +++ b/script.coffee @@ -1787,10 +1787,10 @@ QR = @ready() else @onready = => @ready() - $.on $.id('recaptcha_widget_div'), 'DOMNodeInserted', @onready + $.on $.id('captchaContainer'), 'DOMNodeInserted', @onready ready: -> if @challenge = $.id 'recaptcha_challenge_field_holder' - $.off $.id('recaptcha_widget_div'), 'DOMNodeInserted', @onready + $.off $.id('captchaContainer'), 'DOMNodeInserted', @onready delete @onready else return From b117ad5e159b8d6800fe1460ba53a43cd4928a76 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Tue, 11 Jun 2013 01:10:46 +0200 Subject: [PATCH 003/172] Release 2.39.6. --- 4chan_x.user.js | 6 +++--- Cakefile | 2 +- changelog | 2 ++ latest.js | 2 +- script.coffee | 2 +- 5 files changed, 8 insertions(+), 6 deletions(-) diff --git a/4chan_x.user.js b/4chan_x.user.js index 520dc72c7..cd803f6f0 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -1,6 +1,6 @@ // ==UserScript== // @name 4chan x -// @version 2.39.5 +// @version 2.39.6 // @namespace aeosynth // @description Adds various features. // @copyright 2009-2011 James Campos @@ -27,7 +27,7 @@ * Copyright (c) 2009-2011 James Campos * Copyright (c) 2012-2013 Nicolas Stepien * http://mayhemydg.github.io/4chan-x/ - * 4chan X 2.39.5 + * 4chan X 2.39.6 * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -5893,7 +5893,7 @@ return $.globalEval(("(" + code + ")()").replace('_id_', bq.id)); }, namespace: '4chan_x.', - version: '2.39.5', + version: '2.39.6', callbacks: [], css: '\ /* dialog styling */\ diff --git a/Cakefile b/Cakefile index 9c25140f4..6d46055a8 100644 --- a/Cakefile +++ b/Cakefile @@ -2,7 +2,7 @@ {exec} = require 'child_process' fs = require 'fs' -VERSION = '2.39.5' +VERSION = '2.39.6' HEADER = """ // ==UserScript== diff --git a/changelog b/changelog index 0a39a9216..ebde5d4bb 100644 --- a/changelog +++ b/changelog @@ -1,4 +1,6 @@ master + +2.39.6 - Mayhem Fix QR. diff --git a/latest.js b/latest.js index 30b8eefce..719099c19 100644 --- a/latest.js +++ b/latest.js @@ -1 +1 @@ -postMessage({version:'2.39.5'},'*') \ No newline at end of file +postMessage({version:'2.39.6'},'*') \ No newline at end of file diff --git a/script.coffee b/script.coffee index 1996dfba1..40681cad5 100644 --- a/script.coffee +++ b/script.coffee @@ -4756,7 +4756,7 @@ Main = $.globalEval "(#{code})()".replace '_id_', bq.id namespace: '4chan_x.' - version: '2.39.5' + version: '2.39.6' callbacks: [] css: ' /* dialog styling */ From 2efd04f47e4194884036c2e81256f690ecaa533f Mon Sep 17 00:00:00 2001 From: James Campos Date: Sat, 15 Jun 2013 00:47:17 -0700 Subject: [PATCH 004/172] format ThreadStats.html --- html/Monitoring/ThreadStats.html | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/html/Monitoring/ThreadStats.html b/html/Monitoring/ThreadStats.html index 73904d14f..f6d932e43 100644 --- a/html/Monitoring/ThreadStats.html +++ b/html/Monitoring/ThreadStats.html @@ -1 +1,3 @@ -
... / ... / ...
+
+ ... / ... / ... +
From 41285b4ebd5c10b2e2db157b3df00114bcfb300e Mon Sep 17 00:00:00 2001 From: Mayhem Date: Sun, 16 Jun 2013 21:31:21 +0200 Subject: [PATCH 005/172] RIP in peace nth-chan. /vg/ is back on foolz. --- json/archives.json | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/json/archives.json b/json/archives.json index 4bd0477bf..c86d8dfab 100644 --- a/json/archives.json +++ b/json/archives.json @@ -5,8 +5,8 @@ "http": true, "https": true, "software": "foolfuuka", - "boards": ["a", "co", "gd", "jp", "m", "q", "sp", "tg", "tv", "vp", "vr", "wsg"], - "files": ["a", "gd", "jp", "m", "q", "tg", "vp", "vr", "wsg"] + "boards": ["a", "co", "gd", "jp", "m", "q", "sp", "tg", "tv", "vg", "vp", "vr", "wsg"], + "files": ["a", "gd", "jp", "m", "q", "tg", "vg", "vp", "vr", "wsg"] }, { "uid": 1, "name": "NSFW Foolz", @@ -52,15 +52,6 @@ "software": "foolfuuka", "boards": ["d", "h", "v"], "files": ["d", "h", "v"] -}, { - "uid": 6, - "name": "nth-chan", - "domain": "nth.pensivenonsen.se", - "http": true, - "https": false, - "software": "foolfuuka", - "boards": ["vg"], - "files": ["vg"] }, { "uid": 11, "name": "Foolz a Shit", From 4ba431b1dbf2d5d153b7b1d4224e993a2735c64d Mon Sep 17 00:00:00 2001 From: noface Date: Tue, 18 Jun 2013 15:03:48 +0200 Subject: [PATCH 006/172] Fix twitter not being selectable in Custom Navigation. --- src/General/Header.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/General/Header.coffee b/src/General/Header.coffee index 863e5c987..5158288de 100644 --- a/src/General/Header.coffee +++ b/src/General/Header.coffee @@ -111,7 +111,7 @@ Header = list = $ '#custom-board-list', Header.bar $.rmAll list return unless text - as = $$('#full-board-list a', Header.bar)[0...-2] # ignore the Settings and Home links + as = $$('#full-board-list a', Header.bar)[0...-1] # ignore the Settings and Home links nodes = text.match(/[\w@]+(-(all|title|replace|full|index|catalog|text:"[^"]+"))*|[^\w@]+/g).map (t) -> if /^[^\w@]/.test t return $.tn t From e053e4d0bd0a9a2de5f0cfe1044de1c97c73d43c Mon Sep 17 00:00:00 2001 From: Mayhem Date: Tue, 18 Jun 2013 19:40:13 +0200 Subject: [PATCH 007/172] Simplification of the previous fix. --- src/General/Header.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/General/Header.coffee b/src/General/Header.coffee index 5158288de..c57debf06 100644 --- a/src/General/Header.coffee +++ b/src/General/Header.coffee @@ -111,7 +111,7 @@ Header = list = $ '#custom-board-list', Header.bar $.rmAll list return unless text - as = $$('#full-board-list a', Header.bar)[0...-1] # ignore the Settings and Home links + as = $$ '#full-board-list a[title]', Header.bar nodes = text.match(/[\w@]+(-(all|title|replace|full|index|catalog|text:"[^"]+"))*|[^\w@]+/g).map (t) -> if /^[^\w@]/.test t return $.tn t From 86b44f1c60439edc18568799c120fced6a1b3e7d Mon Sep 17 00:00:00 2001 From: Mayhem Date: Tue, 18 Jun 2013 22:04:05 +0200 Subject: [PATCH 008/172] Update archive redirection for /pol/ and /vg/. --- 4chan_x.user.js | 12 +++++------- changelog | 2 ++ script.coffee | 16 +++++----------- 3 files changed, 12 insertions(+), 18 deletions(-) diff --git a/4chan_x.user.js b/4chan_x.user.js index cd803f6f0..4e6e32f8b 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -4930,6 +4930,7 @@ case 'm': case 'q': case 'tg': + case 'vg': case 'vp': case 'vr': case 'wsg': @@ -4950,8 +4951,6 @@ case 'h': case 'v': return "//loveisover.me/" + board + "/full_image/" + filename; - case 'vg': - return "http://nth.pensivenonsen.se/" + board + "/full_image/" + filename; case 'adv': case 'asp': case 'cm': @@ -4994,6 +4993,7 @@ case 'sp': case 'tg': case 'tv': + case 'vg': case 'vp': case 'vr': case 'wsg': @@ -5015,8 +5015,6 @@ case 'h': case 'v': return "//loveisover.me/_/api/chan/post/?board=" + board + "&num=" + postID; - case 'vg': - return "http://nth.pensivenonsen.se/_/api/chan/post/?board=" + board + "&num=" + postID; case 'adv': case 'asp': case 'cm': @@ -5026,6 +5024,7 @@ case 'n': case 'o': case 'p': + case 'pol': case 's': case 's4s': case 't': @@ -5050,6 +5049,7 @@ case 'sp': case 'tg': case 'tv': + case 'vg': case 'vp': case 'vr': case 'wsg': @@ -5077,9 +5077,6 @@ case 'v': url = Redirect.path('//loveisover.me', 'foolfuuka', data); break; - case 'vg': - url = Redirect.path('http://nth.pensivenonsen.se', 'foolfuuka', data); - break; case 'adv': case 'asp': case 'cm': @@ -5089,6 +5086,7 @@ case 'n': case 'o': case 'p': + case 'pol': case 's': case 's4s': case 't': diff --git a/changelog b/changelog index ebde5d4bb..4aca36703 100644 --- a/changelog +++ b/changelog @@ -1,4 +1,6 @@ master +- Mayhem + Update archive redirection for /pol/ and /vg/. 2.39.6 - Mayhem diff --git a/script.coffee b/script.coffee index 40681cad5..d2101622a 100644 --- a/script.coffee +++ b/script.coffee @@ -4086,7 +4086,7 @@ Redirect = image: (board, filename) -> # Do not use g.BOARD, the image url can originate from a cross-quote. switch board - when 'a', 'gd', 'jp', 'm', 'q', 'tg', 'vp', 'vr', 'wsg' + when 'a', 'gd', 'jp', 'm', 'q', 'tg', 'vg', 'vp', 'vr', 'wsg' "//archive.foolz.us/#{board}/full_image/#{filename}" when 'u' "//nsfw.foolz.us/#{board}/full_image/#{filename}" @@ -4098,8 +4098,6 @@ Redirect = "//archive.nyafuu.org/#{board}/full_image/#{filename}" when 'd', 'h', 'v' "//loveisover.me/#{board}/full_image/#{filename}" - when 'vg' - "http://nth.pensivenonsen.se/#{board}/full_image/#{filename}" when 'adv', 'asp', 'cm', 'e', 'i', 'lgbt', 'n', 'o', 'p', 's', 's4s', 't', 'trv', 'y' "//archive.foolzashit.com/#{board}/full_image/#{filename}" when 'cgl', 'g', 'mu' @@ -4112,7 +4110,7 @@ Redirect = # XXX foolz had HSTS set for 120 days, which broke XHR+CORS+Redirection when on HTTP. # Remove necessary HTTPS procotol in September 2013. switch board - when 'a', 'co', 'gd', 'jp', 'm', 'q', 'sp', 'tg', 'tv', 'vp', 'vr', 'wsg' + when 'a', 'co', 'gd', 'jp', 'm', 'q', 'sp', 'tg', 'tv', 'vg', 'vp', 'vr', 'wsg' "https://archive.foolz.us/_/api/chan/post/?board=#{board}&num=#{postID}" when 'u' "https://nsfw.foolz.us/_/api/chan/post/?board=#{board}&num=#{postID}" @@ -4124,16 +4122,14 @@ Redirect = "//archive.nyafuu.org/_/api/chan/post/?board=#{board}&num=#{postID}" when 'd', 'h', 'v' "//loveisover.me/_/api/chan/post/?board=#{board}&num=#{postID}" - when 'vg' - "http://nth.pensivenonsen.se/_/api/chan/post/?board=#{board}&num=#{postID}" - when 'adv', 'asp', 'cm', 'e', 'i', 'lgbt', 'n', 'o', 'p', 's', 's4s', 't', 'trv', 'y' + when 'adv', 'asp', 'cm', 'e', 'i', 'lgbt', 'n', 'o', 'p', 'pol', 's', 's4s', 't', 'trv', 'y' "//archive.foolzashit.com/_/api/chan/post/?board=#{board}&num=#{postID}" to: (data) -> unless data.isSearch {threadID} = data {board} = data switch board - when 'a', 'co', 'gd', 'jp', 'm', 'q', 'sp', 'tg', 'tv', 'vp', 'vr', 'wsg' + when 'a', 'co', 'gd', 'jp', 'm', 'q', 'sp', 'tg', 'tv', 'vg', 'vp', 'vr', 'wsg' url = Redirect.path '//archive.foolz.us', 'foolfuuka', data when 'u' url = Redirect.path '//nsfw.foolz.us', 'foolfuuka', data @@ -4145,9 +4141,7 @@ Redirect = url = Redirect.path '//archive.nyafuu.org', 'foolfuuka', data when 'd', 'h', 'v' url = Redirect.path '//loveisover.me', 'foolfuuka', data - when 'vg' - url = Redirect.path 'http://nth.pensivenonsen.se', 'foolfuuka', data - when 'adv', 'asp', 'cm', 'e', 'i', 'lgbt', 'n', 'o', 'p', 's', 's4s', 't', 'trv', 'y' + when 'adv', 'asp', 'cm', 'e', 'i', 'lgbt', 'n', 'o', 'p', 'pol', 's', 's4s', 't', 'trv', 'y' url = Redirect.path '//archive.foolzashit.com', 'foolfuuka', data when 'diy', 'g', 'sci' url = Redirect.path '//archive.installgentoo.net', 'fuuka', data From cf8b73eaf53676e351ee572a26373b52400e713c Mon Sep 17 00:00:00 2001 From: Mayhem Date: Tue, 18 Jun 2013 22:18:32 +0200 Subject: [PATCH 009/172] Fix support for the report window. --- 4chan_x.user.js | 12 ++++++++---- script.coffee | 7 +++++-- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/4chan_x.user.js b/4chan_x.user.js index 4e6e32f8b..caa19d14e 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -5484,7 +5484,7 @@ Main = { init: function() { - var key, path, pathname, settings, temp, val; + var asap, key, path, pathname, settings, temp, val; Main.flatten(null, Config); path = location.pathname; pathname = path.slice(1).split('/'); @@ -5504,10 +5504,13 @@ switch (location.hostname) { case 'sys.4chan.org': if (/report/.test(location.search)) { - $.ready(function() { + asap = function() { var field, form; + if (!(field = $.id('recaptcha_response_field'))) { + setTimeout(asap, 200); + return; + } form = $('form'); - field = $.id('recaptcha_response_field'); $.on(field, 'keydown', function(e) { if (e.keyCode === 8 && !e.target.value) { return window.location = 'javascript:Recaptcha.reload()'; @@ -5522,7 +5525,8 @@ } return form.submit(); }); - }); + }; + asap(); } return; case 'images.4chan.org': diff --git a/script.coffee b/script.coffee index d2101622a..e9d589403 100644 --- a/script.coffee +++ b/script.coffee @@ -4434,9 +4434,11 @@ Main = switch location.hostname when 'sys.4chan.org' if /report/.test location.search - $.ready -> + asap = -> + unless field = $.id 'recaptcha_response_field' + setTimeout asap, 200 + return form = $ 'form' - field = $.id 'recaptcha_response_field' $.on field, 'keydown', (e) -> window.location = 'javascript:Recaptcha.reload()' if e.keyCode is 8 and not e.target.value $.on form, 'submit', (e) -> @@ -4444,6 +4446,7 @@ Main = response = field.value.trim() field.value = "#{response} #{response}" unless /\s/.test response form.submit() + asap() return when 'images.4chan.org' $.ready -> From 2365616563ebc45c15b6c1f8ea13463ab590de52 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Tue, 18 Jun 2013 22:19:08 +0200 Subject: [PATCH 010/172] Release 2.39.7. --- 4chan_x.user.js | 6 +++--- Cakefile | 2 +- changelog | 2 ++ latest.js | 2 +- script.coffee | 2 +- 5 files changed, 8 insertions(+), 6 deletions(-) diff --git a/4chan_x.user.js b/4chan_x.user.js index caa19d14e..6bdd9d0ab 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -1,6 +1,6 @@ // ==UserScript== // @name 4chan x -// @version 2.39.6 +// @version 2.39.7 // @namespace aeosynth // @description Adds various features. // @copyright 2009-2011 James Campos @@ -27,7 +27,7 @@ * Copyright (c) 2009-2011 James Campos * Copyright (c) 2012-2013 Nicolas Stepien * http://mayhemydg.github.io/4chan-x/ - * 4chan X 2.39.6 + * 4chan X 2.39.7 * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -5895,7 +5895,7 @@ return $.globalEval(("(" + code + ")()").replace('_id_', bq.id)); }, namespace: '4chan_x.', - version: '2.39.6', + version: '2.39.7', callbacks: [], css: '\ /* dialog styling */\ diff --git a/Cakefile b/Cakefile index 6d46055a8..3e6c1bbc1 100644 --- a/Cakefile +++ b/Cakefile @@ -2,7 +2,7 @@ {exec} = require 'child_process' fs = require 'fs' -VERSION = '2.39.6' +VERSION = '2.39.7' HEADER = """ // ==UserScript== diff --git a/changelog b/changelog index 4aca36703..598fa09a1 100644 --- a/changelog +++ b/changelog @@ -1,4 +1,6 @@ master + +2.39.7 - Mayhem Update archive redirection for /pol/ and /vg/. diff --git a/latest.js b/latest.js index 719099c19..2c161ee45 100644 --- a/latest.js +++ b/latest.js @@ -1 +1 @@ -postMessage({version:'2.39.6'},'*') \ No newline at end of file +postMessage({version:'2.39.7'},'*') \ No newline at end of file diff --git a/script.coffee b/script.coffee index e9d589403..f12023697 100644 --- a/script.coffee +++ b/script.coffee @@ -4753,7 +4753,7 @@ Main = $.globalEval "(#{code})()".replace '_id_', bq.id namespace: '4chan_x.' - version: '2.39.6' + version: '2.39.7' callbacks: [] css: ' /* dialog styling */ From f95c94381822b70ba1723a90a2c4615f6f055d0e Mon Sep 17 00:00:00 2001 From: Mayhem Date: Wed, 19 Jun 2013 21:15:45 +0200 Subject: [PATCH 011/172] Update deps. --- Gruntfile.js | 32 +++++++++++++++++--------------- package.json | 6 +++--- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 38dc200ba..43c735333 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -1,9 +1,8 @@ module.exports = function(grunt) { - var pkg = grunt.file.readJSON('package.json'); var concatOptions = { process: { - data: pkg + get data() { return grunt.config('pkg'); } } }; var shellOptions = { @@ -14,7 +13,7 @@ module.exports = function(grunt) { // Project configuration. grunt.initConfig({ - pkg: pkg, + pkg: grunt.file.readJSON('package.json'), concat: { coffee: { options: concatOptions, @@ -82,7 +81,7 @@ module.exports = function(grunt) { crx: { src: 'img/*.png', dest: 'builds/crx/', - expand: true, + expand: true, flatten: true } }, @@ -95,6 +94,14 @@ module.exports = function(grunt) { concurrent: { build: ['build-crx', 'build-userjs', 'build-userscript'] }, + bump: { + options: { + updateConfigs: ['pkg'], + commit: false, + createTag: false, + push: false + } + }, shell: { commit: { options: shellOptions, @@ -162,7 +169,9 @@ module.exports = function(grunt) { grunt.registerTask('default', ['build']); grunt.registerTask('set-build', 'Set the build type variable', function(type) { + var pkg = grunt.config('pkg'); pkg.type = type; + grunt.config('pkg', pkg); grunt.log.ok('pkg.type = %s', type); }); grunt.registerTask('build', ['concurrent:build']); @@ -190,22 +199,15 @@ module.exports = function(grunt) { ]); grunt.registerTask('release', ['shell:commit', 'shell:push', 'build-crx', 'compress:crx']); - grunt.registerTask('patch', ['bump', 'reloadPkg', 'updcl:3', 'release']); - grunt.registerTask('minor', ['bump:minor', 'reloadPkg', 'updcl:2', 'release']); - grunt.registerTask('major', ['bump:major', 'reloadPkg', 'updcl:1', 'release']); - - grunt.registerTask('reloadPkg', 'Reload the package', function() { - // Update the `pkg` object with the new version. - pkg = grunt.file.readJSON('package.json'); - grunt.config.data.pkg = concatOptions.process.data = pkg; - grunt.log.ok('pkg reloaded.'); - }); + grunt.registerTask('patch', ['bump', 'updcl:3', 'release']); + grunt.registerTask('minor', ['bump:minor', 'updcl:2', 'release']); + grunt.registerTask('major', ['bump:major', 'updcl:1', 'release']); grunt.registerTask('updcl', 'Update the changelog', function(i) { // i is the number of #s for markdown. + var pkg = grunt.config('pkg'); var version = new Array(+i + 1).join('#') + ' ' + pkg.version + ' - *' + grunt.template.today('yyyy-mm-dd') + '*'; grunt.file.write('CHANGELOG.md', version + '\n\n' + grunt.file.read('CHANGELOG.md')); grunt.log.ok('Changelog updated for v' + pkg.version + '.'); }); - }; diff --git a/package.json b/package.json index 0c838ac45..753d8763d 100644 --- a/package.json +++ b/package.json @@ -17,11 +17,11 @@ }, "devDependencies": { "grunt": "~0.4.1", - "grunt-bump": "~0.0.2", - "grunt-concurrent": "~0.2.0", + "grunt-bump": "~0.0.6", + "grunt-concurrent": "~0.3.0", "grunt-contrib-clean": "~0.4.1", "grunt-contrib-coffee": "~0.7.0", - "grunt-contrib-compress": "~0.5.1", + "grunt-contrib-compress": "~0.5.2", "grunt-contrib-concat": "~0.3.0", "grunt-contrib-copy": "~0.4.1", "grunt-contrib-watch": "~0.4.4", From 16a94a261dd74ab17a886f69d319e0d17d19c42c Mon Sep 17 00:00:00 2001 From: Mayhem Date: Thu, 20 Jun 2013 18:43:45 +0200 Subject: [PATCH 012/172] Fix missing call to auto-update archives. --- CHANGELOG.md | 2 ++ src/Archive/Redirect.coffee | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 01fcd2492..860564fcd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +- Minor fixes. + ### 3.5.4 - *2013-06-11* - Fix regression. diff --git a/src/Archive/Redirect.coffee b/src/Archive/Redirect.coffee index 3e4ef732e..106b85803 100644 --- a/src/Archive/Redirect.coffee +++ b/src/Archive/Redirect.coffee @@ -22,7 +22,8 @@ Redirect = Redirect.post[boardID] = archive unless boardID of Redirect.file or boardID not in archive.files Redirect.file[boardID] = archive - return + + Redirect.update() update: (cb) -> $.get 'lastarchivecheck', 0, ({lastarchivecheck}) -> From 19473174992d00a3401100c4ebec7e52eba6bdfb Mon Sep 17 00:00:00 2001 From: Mayhem Date: Thu, 20 Jun 2013 18:44:46 +0200 Subject: [PATCH 013/172] Release 4chan X v3.5.5. --- CHANGELOG.md | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 860564fcd..e2c932700 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +### 3.5.5 - *2013-06-20* + - Minor fixes. ### 3.5.4 - *2013-06-11* diff --git a/package.json b/package.json index 753d8763d..2d242f7fb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "4chan-X", - "version": "3.5.4", + "version": "3.5.5", "description": "Cross-browser extension for productive lurking on 4chan.", "meta": { "name": "4chan X", From 1412fca1385e61e67d2fdeb0af0d888ed90668d0 Mon Sep 17 00:00:00 2001 From: Mayhem Date: Fri, 21 Jun 2013 20:07:43 +0200 Subject: [PATCH 014/172] Element #apply-css doesn't exist in the document when opening via `Edit custom board navigation`. --- src/General/Settings.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/General/Settings.coffee b/src/General/Settings.coffee index 4a42e183d..68ce01c77 100644 --- a/src/General/Settings.coffee +++ b/src/General/Settings.coffee @@ -355,7 +355,7 @@ Settings = Settings[key].call input return $.on $('input[name="Custom CSS"]', section), 'change', Settings.togglecss - $.on $.id('apply-css'), 'click', Settings.usercss + $.on $('#apply-css', section), 'click', Settings.usercss boardnav: -> Header.generateBoardList @value time: -> From 1e592dc24f00a2c119108f425ca8ca801481201c Mon Sep 17 00:00:00 2001 From: Mayhem Date: Sat, 22 Jun 2013 18:32:24 +0200 Subject: [PATCH 015/172] Fix the URL var not being actually used. --- src/Posting/QR.coffee | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/Posting/QR.coffee b/src/Posting/QR.coffee index dec55d596..7362be864 100644 --- a/src/Posting/QR.coffee +++ b/src/Posting/QR.coffee @@ -1113,15 +1113,11 @@ QR = QR.cooldown.set {req, post, isReply} - if threadID is postID # new thread - URL = "/#{g.BOARD}/res/#{threadID}" + URL = if threadID is postID # new thread + "/#{g.BOARD}/res/#{threadID}" else if g.VIEW is 'index' and !QR.cooldown.auto and Conf['Open Post in New Tab'] # replying from the index - URL = "/#{g.BOARD}/res/#{threadID}#p#{postID}" - if URL - if Conf['Open Post in New Tab'] - $.open "/#{g.BOARD}/res/#{threadID}" - else - window.location = "/#{g.BOARD}/res/#{threadID}" + "/#{g.BOARD}/res/#{threadID}#p#{postID}" + (if Conf['Open Post in New Tab'] then $.open else location.assign) URL if URL QR.status() From d154bab227cdcace1f3c4e8ef0536c61dc877d7f Mon Sep 17 00:00:00 2001 From: Mayhem Date: Sat, 22 Jun 2013 22:14:49 +0200 Subject: [PATCH 016/172] Simpler d.readyState usage. --- lib/$.coffee | 2 +- src/General/Header.coffee | 2 +- src/General/Main.coffee | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/$.coffee b/lib/$.coffee index 74b76e04c..40b283713 100644 --- a/lib/$.coffee +++ b/lib/$.coffee @@ -13,7 +13,7 @@ $.DAY = 1000 * 60 * 60 * 24 $.id = (id) -> d.getElementById id $.ready = (fc) -> - if d.readyState in ['interactive', 'complete'] + unless d.readyState is 'loading' $.queueTask fc return cb = -> diff --git a/src/General/Header.coffee b/src/General/Header.coffee index c57debf06..abb3468d8 100644 --- a/src/General/Header.coffee +++ b/src/General/Header.coffee @@ -74,7 +74,7 @@ Header = return unless Main.isThisPageLegit() # Wait for #boardNavMobile instead of #boardNavDesktop, # it might be incomplete otherwise. - $.asap (-> $.id('boardNavMobile') or d.readyState in ['interactive', 'complete']), Header.setBoardList + $.asap (-> $.id('boardNavMobile') or d.readyState isnt 'loading'), Header.setBoardList $.prepend d.body, headerEl $.ready -> diff --git a/src/General/Main.coffee b/src/General/Main.coffee index 95333e029..915a6098e 100644 --- a/src/General/Main.coffee +++ b/src/General/Main.coffee @@ -35,7 +35,7 @@ Main = $.on d, '4chanMainInit', Main.initStyle # comes after the stylesheets on the index/thread pages, but before on the catalog. # <link favicon> comes after the stylesheets on the catalog, but before on the index/thread pages. - $.asap (-> d.head and $('title', d.head) and $('link[rel="shortcut icon"]', d.head) or d.readyState in ['interactive', 'complete']), + $.asap (-> d.head and $('title', d.head) and $('link[rel="shortcut icon"]', d.head) or d.readyState isnt 'loading' Main.initStyle initFeatures: (items) -> From 46b770c658f4ace2faba2d2942081c2bcd8a1d1a Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Sat, 22 Jun 2013 23:21:52 +0200 Subject: [PATCH 017/172] Remove the full path to the script in Firefox stack traces. Close #1143. --- src/General/Main.coffee | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/General/Main.coffee b/src/General/Main.coffee index 915a6098e..3179b7443 100644 --- a/src/General/Main.coffee +++ b/src/General/Main.coffee @@ -324,7 +324,17 @@ Main = postErrors: -> return if Main.v2Detected - errors = Main.errors.map (d) -> d.message + ' ' + d.error.stack + errors = Main.errors.map (d) -> + {stack} = d.error + <% if (type === 'userscript') { %> + # Before: + # someFn@file:///C:/Users/<USER>/AppData/Roaming/Mozilla/Firefox/Profiles/<garbage>.default/gm_scripts/4chan_X/4chan-X.user.js:line_number + # someFn@file:///home/<USER>/.mozilla/firefox/<garbage>.default/gm_scripts/4chan_X/4chan-X.user.js:line_number + # After: + # someFn@4chan-X.user.js:line_number + stack.replace /file:\/{3}.+\//g, '' if stack + <% } %> + "#{d.message} #{stack}" $.ajax '<%= meta.page %>errors', {}, sync: true form: $.formData From cc9e63482af543406e74d742319d6324f2024479 Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Sun, 23 Jun 2013 02:03:35 +0300 Subject: [PATCH 018/172] Add devDependency status badge. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c8c176c3d..8b4a245cd 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# 4chan X +# 4chan X [![devDependency Status](https://david-dm.org/MayhemYDG/4chan-x/dev-status.png)](https://david-dm.org/MayhemYDG/4chan-x#info=devDependencies) Get it [here](https://4chan-x.just-believe.in/). From 4f6bb8062dde25450e1f1d87b43130d24ed728c1 Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Sun, 23 Jun 2013 14:27:07 +0200 Subject: [PATCH 019/172] warosu archives /tg/ images now. --- json/archives.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/json/archives.json b/json/archives.json index c86d8dfab..5c2530d04 100644 --- a/json/archives.json +++ b/json/archives.json @@ -96,5 +96,5 @@ "https": true, "software": "fuuka", "boards": ["3", "cgl", "ck", "fa", "ic", "jp", "lit", "q", "tg", "vr"], - "files": ["3", "cgl", "ck", "fa", "ic", "jp", "lit", "q", "vr"] + "files": ["3", "cgl", "ck", "fa", "ic", "jp", "lit", "q", "tg", "vr"] }] From 36b84402291ab16f11cd3d8617f5dc23ea62efec Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Sun, 23 Jun 2013 17:39:14 +0200 Subject: [PATCH 020/172] installgentoo went https-only. @GXTX --- json/archives.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/json/archives.json b/json/archives.json index 5c2530d04..86613ddc9 100644 --- a/json/archives.json +++ b/json/archives.json @@ -65,7 +65,7 @@ "uid": 7, "name": "Install Gentoo", "domain": "archive.installgentoo.net", - "http": true, + "http": false, "https": true, "software": "fuuka", "boards": ["diy", "g", "sci"], From 32691031002e072e196fe61b92a557a32e0e36e7 Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Mon, 24 Jun 2013 19:41:46 +0200 Subject: [PATCH 021/172] Silghtly faster Post::parseQuotes by not checking if in .capcodeReplies in replies. --- src/General/Post.coffee | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/General/Post.coffee b/src/General/Post.coffee index afdde6968..e6c8b7ad7 100644 --- a/src/General/Post.coffee +++ b/src/General/Post.coffee @@ -16,6 +16,11 @@ class Post quotelinks: [] backlinks: info.getElementsByClassName 'backlink' + unless @isReply = $.hasClass post, 'reply' + @thread.OP = @ + @thread.isSticky = !!$ '.stickyIcon', info + @thread.isClosed = !!$ '.closedIcon', info + @info = {} if subject = $ '.subject', info @nodes.subject = subject @@ -77,11 +82,6 @@ class Post if @file.isImage = /(jpg|png|gif)$/i.test @file.name @file.dimensions = @file.text.textContent.match(/\d+x\d+/)[0] - unless @isReply = $.hasClass post, 'reply' - @thread.OP = @ - @thread.isSticky = !!$ '.stickyIcon', @nodes.info - @thread.isClosed = !!$ '.closedIcon', @nodes.info - @clones = [] g.posts[@fullID] = thread.posts[@] = board.posts[@] = @ @kill() if that.isArchived @@ -110,11 +110,11 @@ class Post quotes = {} for quotelink in $$ '.quotelink', @nodes.comment # Don't add board links. (>>>/b/) - hash = quotelink.hash + {hash} = quotelink continue unless hash # Don't add catalog links. (>>>/b/catalog or >>>/b/search) - pathname = quotelink.pathname + {pathname} = quotelink continue if /catalog$/.test pathname # Don't add rules links. (>>>/a/rules) @@ -123,8 +123,8 @@ class Post @nodes.quotelinks.push quotelink - # Don't count capcode replies as quotes. (Admin/Mod/Dev Replies: ...) - continue if quotelink.parentNode.parentNode.className is 'capcodeReplies' + # Don't count capcode replies as quotes in OPs. (Admin/Mod/Dev Replies: ...) + continue if !@isReply and $.hasClass quotelink.parentNode.parentNode, 'capcodeReplies' # Basically, only add quotes that link to posts on an imageboard. quotes["#{pathname.split('/')[1]}.#{hash[2..]}"] = true From 80afed86b6b2f86ec6c8ad7c3ac48382c6d2f2f5 Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Mon, 24 Jun 2013 19:51:47 +0200 Subject: [PATCH 022/172] Fix missing ), --- src/General/Main.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/General/Main.coffee b/src/General/Main.coffee index 3179b7443..32c8d14b2 100644 --- a/src/General/Main.coffee +++ b/src/General/Main.coffee @@ -35,7 +35,7 @@ Main = $.on d, '4chanMainInit', Main.initStyle # <title> comes after the stylesheets on the index/thread pages, but before on the catalog. # <link favicon> comes after the stylesheets on the catalog, but before on the index/thread pages. - $.asap (-> d.head and $('title', d.head) and $('link[rel="shortcut icon"]', d.head) or d.readyState isnt 'loading' + $.asap (-> d.head and $('title', d.head) and $('link[rel="shortcut icon"]', d.head) or d.readyState isnt 'loading'), Main.initStyle initFeatures: (items) -> From a7346b6103cf510168171497d3f3be724dd947a0 Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Mon, 24 Jun 2013 22:33:47 +0200 Subject: [PATCH 023/172] Fix #377 with a workaround. --- src/General/UI.coffee | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/General/UI.coffee b/src/General/UI.coffee index 42cc139f6..a67746ed8 100644 --- a/src/General/UI.coffee +++ b/src/General/UI.coffee @@ -278,12 +278,12 @@ UI = do -> hoverstart = ({root, el, latestEvent, endEvents, asapTest, cb}) -> o = { - root: root - el: el - style: el.style - cb: cb - endEvents: endEvents - latestEvent: latestEvent + root + el + style: el.style + cb + endEvents + latestEvent clientHeight: doc.clientHeight clientWidth: doc.clientWidth } @@ -297,6 +297,11 @@ UI = do -> $.on root, endEvents, o.hoverend $.on root, 'mousemove', o.hover + <% if (type === 'userscript') { %> + # Workaround for https://github.com/MayhemYDG/4chan-x/issues/377 + o.workaround = -> o.hoverend() unless doc.contains root + $.on doc, 'mousemove', o.workaround + <% } %> hover = (e) -> @latestEvent = e height = @el.offsetHeight @@ -323,6 +328,10 @@ UI = do -> $.rm @el $.off @root, @endEvents, @hoverend $.off @root, 'mousemove', @hover + <% if (type === 'userscript') { %> + # Workaround for https://github.com/MayhemYDG/4chan-x/issues/377 + $.off doc, 'mousemove', @workaround + <% } %> @cb.call @ if @cb From 626c140113eed1f8c6d13ab350626d441e738903 Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Tue, 25 Jun 2013 15:55:56 +0300 Subject: [PATCH 024/172] Typo --- src/Archive/Redirect.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Archive/Redirect.coffee b/src/Archive/Redirect.coffee index 106b85803..8cf6954bb 100644 --- a/src/Archive/Redirect.coffee +++ b/src/Archive/Redirect.coffee @@ -29,7 +29,7 @@ Redirect = $.get 'lastarchivecheck', 0, ({lastarchivecheck}) -> now = Date.now() # Update the list of archives every 4 days. - # The list is also update when 4chan X gets updated. + # The list is also updated when 4chan X gets updated. return if lastarchivecheck > now - 4 * $.DAY $.ajax '<%= meta.page %>json/archives.json', onload: -> return unless @status is 200 From 90f75de0e24c5e30bffb0e9c1980a984bcab8de0 Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Tue, 25 Jun 2013 16:42:03 +0200 Subject: [PATCH 025/172] Simplify Menu code. --- src/Menu/Menu.coffee | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/src/Menu/Menu.coffee b/src/Menu/Menu.coffee index 6aa41bff4..0c5bc4af9 100644 --- a/src/Menu/Menu.coffee +++ b/src/Menu/Menu.coffee @@ -8,29 +8,21 @@ Menu = cb: @node node: -> - button = Menu.makeButton @ if @isClone - $.replace $('.menu-button', @nodes.info), button - return - $.add @nodes.info, [$.tn('\u00A0'), button] + button = $ '.menu-button', @nodes.info + else + button = Menu.makeButton @ + $.add @nodes.info, [$.tn('\u00A0'), button] + $.on button, 'click', Menu.toggle makeButton: do -> a = null - (post) -> + -> a or= $.el 'a', className: 'menu-button' innerHTML: '[<i></i>]' href: 'javascript:;' - clone = a.cloneNode true - clone.setAttribute 'data-postid', post.fullID - clone.setAttribute 'data-clone', true if post.isClone - $.on clone, 'click', Menu.toggle - clone + a.cloneNode true toggle: (e) -> - post = - if @dataset.clone - Get.postFromNode @ - else - g.posts[@dataset.postid] - Menu.menu.toggle e, @, post + Menu.menu.toggle e, @, Get.postFromNode @ From 51358e328af2a3b3a944d13e36aa93f99d99aeb8 Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Tue, 25 Jun 2013 20:45:34 +0200 Subject: [PATCH 026/172] Shave a couple of lines. I probably did this for perf reasons, but it most likely doesn't make much of a difference. --- src/General/Main.coffee | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/General/Main.coffee b/src/General/Main.coffee index 32c8d14b2..bd6239ba7 100644 --- a/src/General/Main.coffee +++ b/src/General/Main.coffee @@ -181,20 +181,18 @@ Main = threads = [] posts = [] - for boardChild in board.children - continue unless $.hasClass boardChild, 'thread' - thread = new Thread boardChild.id[1..], g.BOARD + for threadRoot in $$ '.board > .thread', board + thread = new Thread threadRoot.id[1..], g.BOARD threads.push thread - for threadChild in boardChild.children - continue unless $.hasClass threadChild, 'postContainer' + for postRoot in $$ '.thread > .postContainer', threadRoot try - posts.push new Post threadChild, thread, g.BOARD + posts.push new Post postRoot, thread, g.BOARD catch err # Skip posts that we failed to parse. unless errors errors = [] errors.push - message: "Parsing of Post No.#{threadChild.id.match(/\d+/)} failed. Post will be skipped." + message: "Parsing of Post No.#{postRoot.id.match(/\d+/)} failed. Post will be skipped." error: err Main.handleErrors errors if errors From 6213853f6769782daf40d4191ed65f61f8be4f59 Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Tue, 25 Jun 2013 20:50:11 +0200 Subject: [PATCH 027/172] Always give integers for thread IDs. --- src/General/Get.coffee | 2 +- src/General/Main.coffee | 2 +- src/General/Thread.coffee | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/General/Get.coffee b/src/General/Get.coffee index 781b066fa..f572c10a3 100644 --- a/src/General/Get.coffee +++ b/src/General/Get.coffee @@ -185,7 +185,7 @@ Get = # quotes .replace /((>){2}(>\/[a-z\d]+\/)?\d+)/g, '<span class=deadlink>$1</span>' - threadID = data.thread_num + threadID = +data.thread_num o = # id postID: "#{postID}" diff --git a/src/General/Main.coffee b/src/General/Main.coffee index bd6239ba7..5987b1d08 100644 --- a/src/General/Main.coffee +++ b/src/General/Main.coffee @@ -182,7 +182,7 @@ Main = posts = [] for threadRoot in $$ '.board > .thread', board - thread = new Thread threadRoot.id[1..], g.BOARD + thread = new Thread +threadRoot.id[1..], g.BOARD threads.push thread for postRoot in $$ '.thread > .postContainer', threadRoot try diff --git a/src/General/Thread.coffee b/src/General/Thread.coffee index e3e28a1e1..57d82a316 100644 --- a/src/General/Thread.coffee +++ b/src/General/Thread.coffee @@ -2,8 +2,7 @@ class Thread callbacks: [] toString: -> @ID - constructor: (ID, @board) -> - @ID = +ID + constructor: (@ID, @board) -> @fullID = "#{@board}.#{@ID}" @posts = {} From 03e3dc5ceb08d89fc3b15002479a89c4dfd58ca3 Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Wed, 26 Jun 2013 01:10:42 +0200 Subject: [PATCH 028/172] Post::parseFile --- src/General/Post.coffee | 68 ++++++++++++++++++++++------------------- 1 file changed, 36 insertions(+), 32 deletions(-) diff --git a/src/General/Post.coffee b/src/General/Post.coffee index e6c8b7ad7..170764b2d 100644 --- a/src/General/Post.coffee +++ b/src/General/Post.coffee @@ -49,38 +49,7 @@ class Post @parseComment() @parseQuotes() - - if (file = $ '.file', post) and thumb = $ 'img[data-md5]', file - # Supports JPG/PNG/GIF/PDF. - # Flash files are not supported. - alt = thumb.alt - anchor = thumb.parentNode - fileInfo = file.firstElementChild - @file = - info: fileInfo - text: fileInfo.firstElementChild - thumb: thumb - URL: anchor.href - size: alt.match(/[\d.]+\s\w+/)[0] - MD5: thumb.dataset.md5 - isSpoiler: $.hasClass anchor, 'imgspoiler' - size = +@file.size.match(/[\d.]+/)[0] - unit = ['B', 'KB', 'MB', 'GB'].indexOf @file.size.match(/\w+$/)[0] - size *= 1024 while unit-- > 0 - @file.sizeInBytes = size - @file.thumbURL = - if that.isArchived - thumb.src - else - "#{location.protocol}//thumbs.4chan.org/#{board}/thumb/#{@file.URL.match(/(\d+)\./)[1]}s.jpg" - # replace %22 with quotes, see: - # crbug.com/81193 - # webk.it/62107 - # https://www.w3.org/Bugs/Public/show_bug.cgi?id=16909 - # http://www.whatwg.org/specs/web-apps/current-work/#multipart-form-data - @file.name = $('span[title]', fileInfo).title.replace /%22/g, '"' - if @file.isImage = /(jpg|png|gif)$/i.test @file.name - @file.dimensions = @file.text.textContent.match(/\d+x\d+/)[0] + @parseFile() @clones = [] g.posts[@fullID] = thread.posts[@] = board.posts[@] = @ @@ -131,6 +100,41 @@ class Post return if @isClone @quotes = Object.keys quotes + parseFile: -> + return unless (fileEl = $ '.file', post) and thumb = $ 'img[data-md5]', fileEl + # Supports JPG/PNG/GIF/PDF. + # Flash files are not supported. + alt = thumb.alt + anchor = thumb.parentNode + fileInfo = fileEl.firstElementChild + @file = + info: fileInfo + text: fileInfo.firstElementChild + thumb: thumb + URL: anchor.href + size: alt.match(/[\d.]+\s\w+/)[0] + MD5: thumb.dataset.md5 + isSpoiler: $.hasClass anchor, 'imgspoiler' + size = +@file.size.match(/[\d.]+/)[0] + unit = ['B', 'KB', 'MB', 'GB'].indexOf @file.size.match(/\w+$/)[0] + size *= 1024 while unit-- > 0 + @file.sizeInBytes = size + @file.thumbURL = if that.isArchived + thumb.src + else + "#{location.protocol}//thumbs.4chan.org/#{board}/thumb/#{@file.URL.match(/(\d+)\./)[1]}s.jpg" + @file.name = $('span[title]', fileInfo).title + <% if (type === 'crx') { %> + # replace %22 with quotes, see: + # crbug.com/81193 + # webk.it/62107 + # https://www.w3.org/Bugs/Public/show_bug.cgi?id=16909 + # http://www.whatwg.org/specs/web-apps/current-work/#multipart-form-data + @file.name = @file.name.replace /%22/g, '"' + <% } %> + if @file.isImage = /(jpg|png|gif)$/i.test @file.name + @file.dimensions = @file.text.textContent.match(/\d+x\d+/)[0] + kill: (file, now) -> now or= new Date() if file From 9fd03c7a47ab0b9f93eb33caefcc39795cea35f0 Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Wed, 26 Jun 2013 01:42:17 +0200 Subject: [PATCH 029/172] Opera add-ons store wants `.nex`. I know this is beta but cumon. --- Gruntfile.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Gruntfile.js b/Gruntfile.js index 43c735333..a1853e944 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -83,6 +83,16 @@ module.exports = function(grunt) { dest: 'builds/crx/', expand: true, flatten: true + }, + // for_d19 + // 19 juin 2013 10:32:22 + // We're currently sniffing the type of the file based on file extension. + // We have a different type of content, Themes, which use a pure zip-file with a .zip ending. + // This solution is sub-optimal and will be changed in the future. + // For now, upload an unsigned ZIP-file with the ending .nex or .crx. + nex: { + src: 'builds/<%= pkg.name %>.zip', + dest: 'builds/<%= pkg.name %>.nex' } }, coffee: { @@ -198,7 +208,7 @@ module.exports = function(grunt) { 'clean:tmpuserscript' ]); - grunt.registerTask('release', ['shell:commit', 'shell:push', 'build-crx', 'compress:crx']); + grunt.registerTask('release', ['shell:commit', 'shell:push', 'build-crx', 'compress:crx', 'copy:nex']); grunt.registerTask('patch', ['bump', 'updcl:3', 'release']); grunt.registerTask('minor', ['bump:minor', 'updcl:2', 'release']); grunt.registerTask('major', ['bump:major', 'updcl:1', 'release']); From 827bdf9f25f03b812fa4de0bb50adcdb0f0688e9 Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Wed, 26 Jun 2013 01:50:06 +0200 Subject: [PATCH 030/172] Top-class Opera moderators. --- src/General/Main.coffee | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/General/Main.coffee b/src/General/Main.coffee index 5987b1d08..f8b77e1c0 100644 --- a/src/General/Main.coffee +++ b/src/General/Main.coffee @@ -277,8 +277,29 @@ Main = $.set 'lastupdate', now return $.set 'lastchecked', now + # dmichnowicz + # 19 juin 2013 09:39:28 + # innerHTML is not allowed in conjunction with external resources: [...] + # + # mayhemydg + # 19 juin 2013 09:43:14 + # Will it be good if I make sure to match a version number? + # + # dmichnowicz + # 19 juin 2013 09:46:12 + # Yes. + # + # mayhemydg + # 19 juin 2013 13:04:30 + # Well actually there is nothing to fix, the following code makes sure this is a + # valid version number and precedes the innerHTML part: [see /^\d\.\d+\.\d+$/.test] + # + # dmichnowicz + # 21 juin 2013 04:52:37 + # OK but anyway please remove that line of code. el = $.el 'span', - innerHTML: "Update: <%= meta.name %> v#{version} is out, get it <a href=<%= meta.page %> target=_blank>here</a>." + textContent: "Update: <%= meta.name %> v#{version} is out, get it " + el.innerHTML += '<a href=<%= meta.page %> target=_blank>here</a>.' new Notification 'info', el, 120 handleErrors: (errors) -> From 20f6aba5f2886e6cad7f6cc9b78e44d3e549401e Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Wed, 26 Jun 2013 10:04:20 +0200 Subject: [PATCH 031/172] Fix Post::parseFile --- src/General/Post.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/General/Post.coffee b/src/General/Post.coffee index 170764b2d..b46dc7de7 100644 --- a/src/General/Post.coffee +++ b/src/General/Post.coffee @@ -101,7 +101,7 @@ class Post @quotes = Object.keys quotes parseFile: -> - return unless (fileEl = $ '.file', post) and thumb = $ 'img[data-md5]', fileEl + return unless (fileEl = $ '.file', @nodes.post) and thumb = $ 'img[data-md5]', fileEl # Supports JPG/PNG/GIF/PDF. # Flash files are not supported. alt = thumb.alt From 539ff3bcf41e6f8fcf4040e4f1ecb4be3a71a3bd Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Wed, 26 Jun 2013 11:01:38 +0200 Subject: [PATCH 032/172] Fix Post::parseFile again --- src/General/Post.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/General/Post.coffee b/src/General/Post.coffee index b46dc7de7..bc97d1e9f 100644 --- a/src/General/Post.coffee +++ b/src/General/Post.coffee @@ -49,7 +49,7 @@ class Post @parseComment() @parseQuotes() - @parseFile() + @parseFile(that) @clones = [] g.posts[@fullID] = thread.posts[@] = board.posts[@] = @ @@ -100,7 +100,7 @@ class Post return if @isClone @quotes = Object.keys quotes - parseFile: -> + parseFile: (that) -> return unless (fileEl = $ '.file', @nodes.post) and thumb = $ 'img[data-md5]', fileEl # Supports JPG/PNG/GIF/PDF. # Flash files are not supported. From 0f494812a3681c0d12b1411a3e0ad1051a3d2218 Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Wed, 26 Jun 2013 11:08:59 +0200 Subject: [PATCH 033/172] Fix Post::parseFile, one last time --- src/General/Post.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/General/Post.coffee b/src/General/Post.coffee index bc97d1e9f..de4fa19ee 100644 --- a/src/General/Post.coffee +++ b/src/General/Post.coffee @@ -122,7 +122,7 @@ class Post @file.thumbURL = if that.isArchived thumb.src else - "#{location.protocol}//thumbs.4chan.org/#{board}/thumb/#{@file.URL.match(/(\d+)\./)[1]}s.jpg" + "#{location.protocol}//thumbs.4chan.org/#{@board}/thumb/#{@file.URL.match(/(\d+)\./)[1]}s.jpg" @file.name = $('span[title]', fileInfo).title <% if (type === 'crx') { %> # replace %22 with quotes, see: From fefc928385c6a4fc166afd129d7132e0b36a58df Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Wed, 26 Jun 2013 11:15:03 +0200 Subject: [PATCH 034/172] Fix censoring of stack traces not actually being taken into account. --- src/General/Main.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/General/Main.coffee b/src/General/Main.coffee index f8b77e1c0..ec9243352 100644 --- a/src/General/Main.coffee +++ b/src/General/Main.coffee @@ -351,7 +351,7 @@ Main = # someFn@file:///home/<USER>/.mozilla/firefox/<garbage>.default/gm_scripts/4chan_X/4chan-X.user.js:line_number # After: # someFn@4chan-X.user.js:line_number - stack.replace /file:\/{3}.+\//g, '' if stack + stack = stack.replace /file:\/{3}.+\//g, '' if stack <% } %> "#{d.message} #{stack}" $.ajax '<%= meta.page %>errors', {}, From f0504247c5f8777640ddad41105e72a9d27b2ca1 Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Wed, 26 Jun 2013 21:06:47 +0200 Subject: [PATCH 035/172] Truly fix #377. --- src/General/UI.coffee | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/General/UI.coffee b/src/General/UI.coffee index a67746ed8..9ae40e195 100644 --- a/src/General/UI.coffee +++ b/src/General/UI.coffee @@ -299,7 +299,12 @@ UI = do -> $.on root, 'mousemove', o.hover <% if (type === 'userscript') { %> # Workaround for https://github.com/MayhemYDG/4chan-x/issues/377 - o.workaround = -> o.hoverend() unless doc.contains root + o.workaround = (e) -> + node = e.target + while node + return if node is root + node = node.parentNode + o.hoverend() $.on doc, 'mousemove', o.workaround <% } %> hover = (e) -> From fb6573dd3ef0383f8508ed722f71811841e59198 Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Thu, 27 Jun 2013 01:08:27 +0200 Subject: [PATCH 036/172] Keybind fields should be monospace'd. --- css/style.css | 3 +++ 1 file changed, 3 insertions(+) diff --git a/css/style.css b/css/style.css index 1baa06d03..ce398bc5e 100644 --- a/css/style.css +++ b/css/style.css @@ -370,6 +370,9 @@ a[href="javascript:;"] { .section-archives select { width: 90%; } +.section-keybinds .field { + font-family: monospace; +} #fourchanx-settings fieldset { border: 1px solid; border-radius: 3px; From d1d214925875e54e383105cde357bf2c84915a6d Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Thu, 27 Jun 2013 01:25:46 +0200 Subject: [PATCH 037/172] Simplify #377 fix. --- src/General/UI.coffee | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/General/UI.coffee b/src/General/UI.coffee index 9ae40e195..7c6c752e0 100644 --- a/src/General/UI.coffee +++ b/src/General/UI.coffee @@ -299,12 +299,7 @@ UI = do -> $.on root, 'mousemove', o.hover <% if (type === 'userscript') { %> # Workaround for https://github.com/MayhemYDG/4chan-x/issues/377 - o.workaround = (e) -> - node = e.target - while node - return if node is root - node = node.parentNode - o.hoverend() + o.workaround = (e) -> o.hoverend() unless root.contains e.target $.on doc, 'mousemove', o.workaround <% } %> hover = (e) -> From b66a911a039f884bbf3b13400673ab32e5582fe0 Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Thu, 27 Jun 2013 21:37:13 +0200 Subject: [PATCH 038/172] Update deps. --- Gruntfile.js | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index a1853e944..9bf70810d 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -105,7 +105,7 @@ module.exports = function(grunt) { build: ['build-crx', 'build-userjs', 'build-userscript'] }, bump: { - options: { + options: { updateConfigs: ['pkg'], commit: false, createTag: false, diff --git a/package.json b/package.json index 2d242f7fb..693013090 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "grunt-contrib-concat": "~0.3.0", "grunt-contrib-copy": "~0.4.1", "grunt-contrib-watch": "~0.4.4", - "grunt-shell": "~0.2.2" + "grunt-shell": "~0.3.0" }, "repository": { "type": "git", From badc11d4c0d194e6b5beeac75a1b2bce78062a22 Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Sat, 29 Jun 2013 16:33:48 +0200 Subject: [PATCH 039/172] The `4chanMainInit` event is now dispatched in the catalog too. --- src/General/Main.coffee | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/General/Main.coffee b/src/General/Main.coffee index ec9243352..7544b9637 100644 --- a/src/General/Main.coffee +++ b/src/General/Main.coffee @@ -33,10 +33,6 @@ Main = $.get Conf, Main.initFeatures $.on d, '4chanMainInit', Main.initStyle - # <title> comes after the stylesheets on the index/thread pages, but before on the catalog. - # <link favicon> comes after the stylesheets on the catalog, but before on the index/thread pages. - $.asap (-> d.head and $('title', d.head) and $('link[rel="shortcut icon"]', d.head) or d.readyState isnt 'loading'), - Main.initStyle initFeatures: (items) -> Conf = items From 573bd6f294a86a5867ed8a1985dbbe181b0ca9cf Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Sat, 29 Jun 2013 18:04:23 +0200 Subject: [PATCH 040/172] Fix #1146. --- src/Miscellaneous/Keybinds.coffee | 4 ++-- src/Miscellaneous/Nav.coffee | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Miscellaneous/Keybinds.coffee b/src/Miscellaneous/Keybinds.coffee index 7f33b2b74..50177e169 100644 --- a/src/Miscellaneous/Keybinds.coffee +++ b/src/Miscellaneous/Keybinds.coffee @@ -75,10 +75,10 @@ Keybinds = window.location = form.action # Thread Navigation when Conf['Next thread'] - return if g.VIEW is 'thread' + return if g.VIEW isnt 'index' Nav.scroll +1 when Conf['Previous thread'] - return if g.VIEW is 'thread' + return if g.VIEW isnt 'index' Nav.scroll -1 when Conf['Expand thread'] ExpandThread.toggle thread diff --git a/src/Miscellaneous/Nav.coffee b/src/Miscellaneous/Nav.coffee index 972f66b3c..2b7e23354 100644 --- a/src/Miscellaneous/Nav.coffee +++ b/src/Miscellaneous/Nav.coffee @@ -60,8 +60,7 @@ Nav = # 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 - unless (delta is -1 and Math.ceil(top) < 0) or (delta is +1 and top > 1) - i += delta + if (delta is -1 and top > -5) or (delta is +1 and top < 5) + top = threads[i + delta]?.getBoundingClientRect().top - topMargin - top = threads[i]?.getBoundingClientRect().top - topMargin window.scrollBy 0, top From 125e1bdee777cfda02df3c673aafaa496528080e Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Mon, 1 Jul 2013 22:07:03 +0200 Subject: [PATCH 041/172] 504s are not legit pages. --- src/General/Main.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/General/Main.coffee b/src/General/Main.coffee index 7544b9637..5a3d9b652 100644 --- a/src/General/Main.coffee +++ b/src/General/Main.coffee @@ -364,7 +364,7 @@ Main = unless 'thisPageIsLegit' of Main Main.thisPageIsLegit = location.hostname is 'boards.4chan.org' and !$('link[href*="favicon-status.ico"]', d.head) and - d.title not in ['4chan - Temporarily Offline', '4chan - Error'] + d.title not in ['4chan - Temporarily Offline', '4chan - Error', '504 Gateway Time-out'] Main.thisPageIsLegit css: """ From 2939a81a21ec99f7366492caf40cc7bae4c84496 Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Tue, 2 Jul 2013 10:06:17 +0200 Subject: [PATCH 042/172] Fix #1177 --- src/General/Post.coffee | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/General/Post.coffee b/src/General/Post.coffee index de4fa19ee..6f2a8369c 100644 --- a/src/General/Post.coffee +++ b/src/General/Post.coffee @@ -22,28 +22,28 @@ class Post @thread.isClosed = !!$ '.closedIcon', info @info = {} - if subject = $ '.subject', info + if subject = $ '.subject', info @nodes.subject = subject @info.subject = subject.textContent - if name = $ '.name', info + if name = $ '.name', info @nodes.name = name @info.name = name.textContent - if email = $ '.useremail', info + if email = $ '.useremail', info @nodes.email = email @info.email = decodeURIComponent email.href[7..] - if tripcode = $ '.postertrip', info + if tripcode = $ '.postertrip', info @nodes.tripcode = tripcode @info.tripcode = tripcode.textContent - if uniqueID = $ '.posteruid', info + if uniqueID = $ '.posteruid', info @nodes.uniqueID = uniqueID @info.uniqueID = uniqueID.firstElementChild.textContent - if capcode = $ '.capcode.hand', info + if capcode = $ '.capcode.hand', info @nodes.capcode = capcode @info.capcode = capcode.textContent.replace '## ', '' - if flag = $ '.countryFlag', info + if flag = $ '.flag, .countryFlag', info @nodes.flag = flag @info.flag = flag.title - if date = $ '.dateTime', info + if date = $ '.dateTime', info @nodes.date = date @info.date = new Date date.dataset.utc * 1000 From 97087af42f2ba16370eb4f2cc6c4593929b850a7 Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Tue, 2 Jul 2013 15:19:15 +0200 Subject: [PATCH 043/172] Simpler QR quoting code. --- src/Posting/QR.coffee | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/Posting/QR.coffee b/src/Posting/QR.coffee index 7362be864..a7d917a64 100644 --- a/src/Posting/QR.coffee +++ b/src/Posting/QR.coffee @@ -326,13 +326,10 @@ QR = e?.preventDefault() return unless QR.postingIsEnabled - sel = d.getSelection() - selectionRoot = $.x 'ancestor::div[contains(@class,"postContainer")][1]', sel.anchorNode - post = Get.postFromNode @ - {OP} = Get.contextFromLink(@).thread - - text = ">>#{post}\n" - if (s = sel.toString().trim()) and post.nodes.root is selectionRoot + sel = d.getSelection() + post = Get.postFromNode @ + text = ">>#{post}\n" + if (s = sel.toString().trim()) and post is Get.postFromNode sel.anchorNode # XXX Opera doesn't retain `\n`s? s = s.replace /\n/g, '\n>' text += ">#{s}\n" @@ -344,7 +341,7 @@ QR = $.addClass QR.nodes.el, 'dump' QR.cooldown.auto = true {com, thread} = QR.nodes - thread.value = OP.ID unless com.value + thread.value = Get.contextFromLink(@).thread unless com.value caretPos = com.selectionStart # Replace selection for text. From b3d38cbabd800bc382debaf0ca17848a67cfd81d Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Tue, 2 Jul 2013 18:21:05 +0200 Subject: [PATCH 044/172] contextFromLink -> contextFromNode --- src/General/Get.coffee | 2 +- src/Posting/QR.coffee | 2 +- src/Quotelinks/QuoteInline.coffee | 2 +- src/Quotelinks/QuotePreview.coffee | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/General/Get.coffee b/src/General/Get.coffee index f572c10a3..7c8d84cb1 100644 --- a/src/General/Get.coffee +++ b/src/General/Get.coffee @@ -19,7 +19,7 @@ Get = if index then post.clones[index] else post postFromNode: (root) -> Get.postFromRoot $.x 'ancestor::div[contains(@class,"postContainer")][1]', root - contextFromLink: (quotelink) -> + contextFromNode: (quotelink) -> Get.postFromRoot $.x 'ancestor::div[parent::div[@class="thread"]][1]', quotelink postDataFromLink: (link) -> if link.hostname is 'boards.4chan.org' diff --git a/src/Posting/QR.coffee b/src/Posting/QR.coffee index a7d917a64..fb5bce620 100644 --- a/src/Posting/QR.coffee +++ b/src/Posting/QR.coffee @@ -341,7 +341,7 @@ QR = $.addClass QR.nodes.el, 'dump' QR.cooldown.auto = true {com, thread} = QR.nodes - thread.value = Get.contextFromLink(@).thread unless com.value + thread.value = Get.contextFromNode(@).thread unless com.value caretPos = com.selectionStart # Replace selection for text. diff --git a/src/Quotelinks/QuoteInline.coffee b/src/Quotelinks/QuoteInline.coffee index dcff9553c..6a00bdba8 100644 --- a/src/Quotelinks/QuoteInline.coffee +++ b/src/Quotelinks/QuoteInline.coffee @@ -13,7 +13,7 @@ QuoteInline = return if e.shiftKey or e.altKey or e.ctrlKey or e.metaKey or e.button isnt 0 e.preventDefault() {boardID, threadID, postID} = Get.postDataFromLink @ - context = Get.contextFromLink @ + context = Get.contextFromNode @ if $.hasClass @, 'inlined' QuoteInline.rm @, boardID, threadID, postID, context else diff --git a/src/Quotelinks/QuotePreview.coffee b/src/Quotelinks/QuotePreview.coffee index 399bf5ce6..fcd0bfd98 100644 --- a/src/Quotelinks/QuotePreview.coffee +++ b/src/Quotelinks/QuotePreview.coffee @@ -18,7 +18,7 @@ QuotePreview = id: 'qp' className: 'dialog' $.add d.body, qp - Get.postClone boardID, threadID, postID, qp, Get.contextFromLink @ + Get.postClone boardID, threadID, postID, qp, Get.contextFromNode @ UI.hover root: @ From 2a99db7cdd2241db659b4395de93e97db29fcabc Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Tue, 2 Jul 2013 18:23:12 +0200 Subject: [PATCH 045/172] src/Miscellaneous/Sauce.coffee -> src/Images/Sauce.coffee --- src/{Miscellaneous => Images}/Sauce.coffee | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/{Miscellaneous => Images}/Sauce.coffee (100%) diff --git a/src/Miscellaneous/Sauce.coffee b/src/Images/Sauce.coffee similarity index 100% rename from src/Miscellaneous/Sauce.coffee rename to src/Images/Sauce.coffee From 8854f9a06fb91da950bb54988b021e72925a021d Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Tue, 2 Jul 2013 18:50:46 +0200 Subject: [PATCH 046/172] Fix flag building. --- src/General/Build.coffee | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/General/Build.coffee b/src/General/Build.coffee index 786e931fb..713e0586a 100644 --- a/src/General/Build.coffee +++ b/src/General/Build.coffee @@ -104,12 +104,12 @@ Build = capcodeStart = '' capcode = '' - flag = - if flagCode - " <img src='#{staticPath}country/#{if boardID is 'pol' then 'troll/' else ''}" + - flagCode.toLowerCase() + ".gif' alt=#{flagCode} title='#{flagName}' class=countryFlag>" - else - '' + flag = unless flagCode + '' + else if boardID is 'pol' + " <img src='#{staticPath}country/troll/#{flagCode.toLowerCase()}.gif' alt=#{flagCode} title='#{flagName}' class=countryFlag>" + else + " <span title='#{flagName}' class='flag flag-#{flagCode.toLowerCase()}'></span>" if file?.isDeleted fileHTML = if isOP From 9dae27cb1d0289708e70160aef3815842a38e6f7 Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Tue, 2 Jul 2013 18:52:53 +0200 Subject: [PATCH 047/172] Changelog. --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e2c932700..68136ab4d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +- Fix flag filtering on /sp/ and /int/. + ### 3.5.5 - *2013-06-20* - Minor fixes. From 70daa0352d021e50b6c9a882b4cbb24d8eccb127 Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Tue, 2 Jul 2013 18:53:29 +0200 Subject: [PATCH 048/172] Release 4chan X v3.5.6. --- CHANGELOG.md | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 68136ab4d..5bda0c706 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +### 3.5.6 - *2013-07-02* + - Fix flag filtering on /sp/ and /int/. ### 3.5.5 - *2013-06-20* diff --git a/package.json b/package.json index 693013090..1ab59b398 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "4chan-X", - "version": "3.5.5", + "version": "3.5.6", "description": "Cross-browser extension for productive lurking on 4chan.", "meta": { "name": "4chan X", From 48a783733857c6637b2e286e51edcb4bdcb57a17 Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Tue, 2 Jul 2013 21:23:49 +0200 Subject: [PATCH 049/172] Fix #1179. --- src/Menu/DeleteLink.coffee | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Menu/DeleteLink.coffee b/src/Menu/DeleteLink.coffee index 9427d4076..7f71d7f6e 100644 --- a/src/Menu/DeleteLink.coffee +++ b/src/Menu/DeleteLink.coffee @@ -44,9 +44,8 @@ DeleteLink = return if DeleteLink.cooldown.counting is post $.off @, 'click', DeleteLink.delete - @textContent = "Deleting #{@textContent}..." - fileOnly = $.hasClass @, 'delete-file' + @textContent = "Deleting #{if fileOnly then 'file' else 'post'}..." form = mode: 'usrdel' From b307072907e08a178ccf686d32838cd51883db81 Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Wed, 3 Jul 2013 12:04:29 +0200 Subject: [PATCH 050/172] Somewhat more discernible forwardlinks. --- css/style.css | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/css/style.css b/css/style.css index ce398bc5e..7b92c6fdf 100644 --- a/css/style.css +++ b/css/style.css @@ -482,6 +482,16 @@ a.hide-announcement { text-decoration: none; border-bottom: 1px dashed; } +@supports (text-decoration-style: double) or (-moz-text-decoration-style: double) { + .quotelink.forwardlink, + .backlink.forwardlink { + border-bottom: none; + -moz-text-decoration-line: underline; + -moz-text-decoration-style: double; + text-decoration-line: underline; + text-decoration-style: double; + } +} .filtered { text-decoration: underline line-through; } From 515953e934c1691f243d8914e9a631063773b47c Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Thu, 4 Jul 2013 15:48:39 +0200 Subject: [PATCH 051/172] Update deps. Actually this is just an excuse to test something. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1ab59b398..2955dc206 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ }, "devDependencies": { "grunt": "~0.4.1", - "grunt-bump": "~0.0.6", + "grunt-bump": "~0.0.7", "grunt-concurrent": "~0.3.0", "grunt-contrib-clean": "~0.4.1", "grunt-contrib-coffee": "~0.7.0", From 009bb9f74da929b9d9ebc84ada02d90184063f3f Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Fri, 5 Jul 2013 22:16:46 +0200 Subject: [PATCH 052/172] Revert "Somewhat more discernible forwardlinks." This reverts commit b307072907e08a178ccf686d32838cd51883db81. See crbug.com/257695 --- css/style.css | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/css/style.css b/css/style.css index 7b92c6fdf..ce398bc5e 100644 --- a/css/style.css +++ b/css/style.css @@ -482,16 +482,6 @@ a.hide-announcement { text-decoration: none; border-bottom: 1px dashed; } -@supports (text-decoration-style: double) or (-moz-text-decoration-style: double) { - .quotelink.forwardlink, - .backlink.forwardlink { - border-bottom: none; - -moz-text-decoration-line: underline; - -moz-text-decoration-style: double; - text-decoration-line: underline; - text-decoration-style: double; - } -} .filtered { text-decoration: underline line-through; } From fa500873237657886514e50f5f8053fbedbfa82f Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Fri, 5 Jul 2013 23:18:04 +0200 Subject: [PATCH 053/172] Never unread-scroll to OPs. Also fix a dumb loop assignment. --- src/Monitoring/Unread.coffee | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Monitoring/Unread.coffee b/src/Monitoring/Unread.coffee index b381a4441..6ad07f03e 100644 --- a/src/Monitoring/Unread.coffee +++ b/src/Monitoring/Unread.coffee @@ -38,11 +38,11 @@ Unread = return if (hash = location.hash.match /\d+/) and hash[0] of Unread.thread.posts if Unread.posts.length # Scroll to before the first unread post. - prevID = 0 - while root = $.x 'preceding-sibling::div[contains(@class,"postContainer")][1]', Unread.posts[0].nodes.root + {root} = Unread.posts[0].nodes + while root = $.x 'preceding-sibling::div[contains(@class,"postContainer")][1]', root post = Get.postFromRoot root - break if prevID is post.ID - prevID = post.ID + # Don't scroll if we have 0 posts read in this thread. + return unless post.isReply break unless post.isHidden onload = -> root.scrollIntoView false if checkPosition root else From e16fefb31fed8f94801fd2368f02f2977e5fcc26 Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Sun, 7 Jul 2013 21:45:30 +0200 Subject: [PATCH 054/172] Firefox has stack-less errors. Filter those errors out, and when possible don't send anything. --- src/General/Main.coffee | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/General/Main.coffee b/src/General/Main.coffee index 5a3d9b652..b023d704f 100644 --- a/src/General/Main.coffee +++ b/src/General/Main.coffee @@ -339,7 +339,7 @@ Main = postErrors: -> return if Main.v2Detected - errors = Main.errors.map (d) -> + errors = Main.errors.filter((d) -> !!d.error.stack).map((d) -> {stack} = d.error <% if (type === 'userscript') { %> # Before: @@ -350,6 +350,8 @@ Main = stack = stack.replace /file:\/{3}.+\//g, '' if stack <% } %> "#{d.message} #{stack}" + ).join '\n' + return unless errors $.ajax '<%= meta.page %>errors', {}, sync: true form: $.formData @@ -357,7 +359,7 @@ Main = t: '<%= type %>' ua: window.navigator.userAgent url: window.location.href - e: errors.join '\n' + e: errors isThisPageLegit: -> # 404 error page or similar. From 8560f204b2baea84f9469fcc55db89de7349f4af Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Sun, 9 Jun 2013 17:21:36 +0200 Subject: [PATCH 055/172] Remove userjs support and Presto-related code. Add `minimum_opera_version` in the manifest. Up the min Chrome version for unprefixed MutationObserver support. --- .gitignore | 1 - CONTRIBUTING.md | 3 +- Gruntfile.js | 19 +------- css/style.css | 54 ++++++++++++----------- html/Posting/QR.html | 2 +- lib/$.coffee | 68 +++++------------------------ src/General/Globals.coffee | 6 --- src/General/Main.coffee | 18 +++----- src/Images/ImageExpand.coffee | 13 ------ src/Meta/manifest.json | 3 +- src/Monitoring/ThreadUpdater.coffee | 3 +- src/Monitoring/Unread.coffee | 4 +- src/Posting/QR.coffee | 59 +++++-------------------- src/Quotelinks/QuotePreview.coffee | 15 ------- 14 files changed, 64 insertions(+), 204 deletions(-) diff --git a/.gitignore b/.gitignore index 868b74e84..7820bd9be 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ builds/ node_modules/ tmp-crx/ -tmp-userjs/ tmp-userscript/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a1164bc88..735b0841c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -11,9 +11,8 @@ Reporting bugs: 4. Your exported settings. If your settings contains sensible information (e.g. personas), edit the text file manually. Open your console with: -- `Ctrl + Shift + J` on Chrome. +- `Ctrl + Shift + J` on Chrome and Opera. - `Ctrl + Shift + K` on Firefox. -- `Ctrl + Shift + O` on Opera. Respect these guidelines: - Describe the issue clearly, put some effort into it. A one-liner isn't a good enough description. diff --git a/Gruntfile.js b/Gruntfile.js index 9bf70810d..69100ad0f 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -56,15 +56,6 @@ module.exports = function(grunt) { ] } }, - userjs: { - options: concatOptions, - src: [ - 'src/Meta/metadata.js', - 'src/Meta/banner.js', - 'tmp-<%= pkg.type %>/script.js' - ], - dest: 'builds/<%= pkg.name %>.js' - }, userscript: { options: concatOptions, files: { @@ -102,7 +93,7 @@ module.exports = function(grunt) { } }, concurrent: { - build: ['build-crx', 'build-userjs', 'build-userscript'] + build: ['build-crx', 'build-userscript'] }, bump: { options: { @@ -161,7 +152,6 @@ module.exports = function(grunt) { clean: { builds: 'builds', tmpcrx: 'tmp-crx', - tmpuserjs: 'tmp-userjs', tmpuserscript: 'tmp-userscript' } }); @@ -193,13 +183,6 @@ module.exports = function(grunt) { 'copy:crx', 'clean:tmpcrx' ]); - grunt.registerTask('build-userjs', [ - 'set-build:userjs', - 'concat:coffee', - 'coffee:script', - 'concat:userjs', - 'clean:tmpuserjs' - ]); grunt.registerTask('build-userscript', [ 'set-build:userscript', 'concat:coffee', diff --git a/css/style.css b/css/style.css index ce398bc5e..28dfb5bed 100644 --- a/css/style.css +++ b/css/style.css @@ -278,10 +278,8 @@ a[href="javascript:;"] { box-sizing: border-box; box-shadow: 0 0 15px rgba(0, 0, 0, .15); height: 600px; - min-height: 0; max-height: 100%; width: 900px; - min-width: 0; max-width: 100%; margin: auto; padding: 3px; @@ -468,7 +466,8 @@ a.hide-announcement { .deadlink { text-decoration: none !important; } -.backlink.deadlink:not(.forwardlink), .quotelink.deadlink:not(.forwardlink) { +.backlink.deadlink:not(.forwardlink), +.quotelink.deadlink:not(.forwardlink) { text-decoration: underline !important; } .inlined { @@ -508,8 +507,6 @@ a.hide-announcement { padding: 2px 2px 5px; } #qp img { - max-height: 300px; - max-width: 500px; max-height: 80vh; max-width: 50vw; } @@ -541,8 +538,7 @@ a.hide-announcement { :root.fit-width .full-image { max-width: 100%; } -:root.gecko.fit-width .full-image, -:root.presto.fit-width .full-image { +:root.gecko.fit-width .full-image { width: 100%; } #ihover { @@ -616,9 +612,6 @@ a.hide-announcement { color: #000; background-color: #F7F7F7; } -.presto #qr select { - height: 1em; -} #qr .close { padding: 0 3px; } @@ -649,13 +642,15 @@ a.hide-announcement { outline: none; width: 30px; } -#dump-button:hover, #dump-button:focus { +#dump-button:hover, +#dump-button:focus { background: linear-gradient(#FFF, #DDD); } -#dump-button:active, .dump #dump-button:not(:hover):not(:focus) { +#dump-button:active, +.dump #dump-button:not(:hover):not(:focus) { background: linear-gradient(#CCC, #DDD); } -.gecko #dump-button { +:root.gecko #dump-button { padding: 0; } #qr:not(.dump) #dump-list-container { @@ -670,7 +665,10 @@ a.hide-announcement { } #dump-list { counter-reset: qrpreviews; - top: 0; right: 0; bottom: 0; left: 0; + top: 0; + right: 0; + bottom: 0; + left: 0; overflow: hidden; position: absolute; white-space: nowrap; @@ -696,8 +694,10 @@ a.hide-announcement { box-sizing: border-box; cursor: move; display: inline-block; - height: 92px; width: 92px; - margin: 4px; padding: 2px; + height: 92px; + width: 92px; + margin: 4px; + padding: 2px; opacity: .6; outline: none; overflow: hidden; @@ -707,7 +707,8 @@ a.hide-announcement { vertical-align: top; white-space: pre; } -.qr-preview:hover, .qr-preview:focus { +.qr-preview:hover, +.qr-preview:focus { opacity: .9; color: #FFF !important; } @@ -720,7 +721,8 @@ a.hide-announcement { font-weight: 700; text-shadow: 0 0 3px #000, 0 0 5px #000; position: absolute; - top: 3px; right: 3px; + top: 3px; + right: 3px; } .qr-preview.drag { border-color: red; @@ -740,7 +742,9 @@ a.hide-announcement { } .qr-preview > label { background: rgba(0, 0, 0, .5); - right: 0; bottom: 0; left: 0; + right: 0; + bottom: 0; + left: 0; position: absolute; text-align: center; } @@ -756,7 +760,8 @@ a.hide-announcement { line-height: 1; text-align: center; position: absolute; - right: 0; bottom: 0; + right: 0; + bottom: 0; z-index: 1; } #qr textarea { @@ -822,7 +827,10 @@ a.hide-announcement { } #qr-filename { position: absolute; - top: 0; right: 0; bottom: 0; left: 0; + top: 0; + right: 0; + bottom: 0; + left: 0; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; @@ -838,10 +846,6 @@ a.hide-announcement { -webkit-order: 1; order: 1; } -#qr input[type='file'] { - position: absolute; - visibility: hidden; -} /* Menu */ .menu-button { diff --git a/html/Posting/QR.html b/html/Posting/QR.html index b02ef3b5f..b34fb36b3 100644 --- a/html/Posting/QR.html +++ b/html/Posting/QR.html @@ -31,7 +31,7 @@ <a id="qr-filerm" href="javascript:;" title="Remove file">×</a> <input type="checkbox" id="qr-file-spoiler" title="Spoiler image"> </div> - <input type="file" multiple> + <input type="file" multiple hidden> </form> <datalist id="list-name"></datalist> <datalist id="list-email"></datalist> diff --git a/lib/$.coffee b/lib/$.coffee index 40b283713..de39560c9 100644 --- a/lib/$.coffee +++ b/lib/$.coffee @@ -208,26 +208,19 @@ $.bytesToString = (size) -> # Round to an integer otherwise. Math.round size "#{size} #{['B', 'KB', 'MB', 'GB'][unit]}" +$.item = (key, val) -> + item = {} + item[key] = val + item $.syncing = {} -$.sync = do -> <% if (type === 'crx') { %> +$.sync = do -> chrome.storage.onChanged.addListener (changes) -> for key of changes if cb = $.syncing[key] cb changes[key].newValue return (key, cb) -> $.syncing[key] = cb -<% } else { %> - $.on window, 'storage', (e) -> - if cb = $.syncing[e.key] - cb JSON.parse e.newValue - (key, cb) -> $.syncing[g.NAMESPACE + key] = cb -<% } %> -$.item = (key, val) -> - item = {} - item[key] = val - item -<% if (type === 'crx') { %> $.localKeys = [ # filters 'name', @@ -300,52 +293,13 @@ $.set = do -> else $.extend items, key set() -<% } else if (type === 'userjs') { %> -do -> - # http://www.opera.com/docs/userjs/specs/#scriptstorage - # http://www.opera.com/docs/userjs/using/#securepages - # The scriptStorage object is available only during - # the main User JavaScript thread, being therefore - # accessible only in the main body of the user script. - # To access the storage object later, keep a reference - # to the object. - {scriptStorage} = opera - $.delete = (keys) -> - unless keys instanceof Array - keys = [keys] - for key in keys - key = g.NAMESPACE + key - localStorage.removeItem key - delete scriptStorage[key] - return - $.get = (key, val, cb) -> - if typeof cb is 'function' - items = $.item key, val - else - items = key - cb = val - $.queueTask -> - for key of items - if val = scriptStorage[g.NAMESPACE + key] - items[key] = JSON.parse val - cb items - $.set = do -> - set = (key, val) -> - key = g.NAMESPACE + key - val = JSON.stringify val - if key of $.syncing - # for `storage` events - localStorage.setItem key, val - scriptStorage[key] = val - (keys, val) -> - if typeof keys is 'string' - set keys, val - return - for key, val of keys - set key, val - return <% } else { %> - # http://wiki.greasespot.net/Main_Page +# http://wiki.greasespot.net/Main_Page +$.sync = do -> + $.on window, 'storage', (e) -> + if cb = $.syncing[e.key] + cb JSON.parse e.newValue + (key, cb) -> $.syncing[g.NAMESPACE + key] = cb $.delete = (keys) -> unless keys instanceof Array keys = [keys] diff --git a/src/General/Globals.coffee b/src/General/Globals.coffee index ad48281e4..d5d09eaa6 100644 --- a/src/General/Globals.coffee +++ b/src/General/Globals.coffee @@ -1,9 +1,3 @@ -<% if (type === 'userjs') { %> -# Opera doesn't support the @match metadata key, -# return 4chan X here if we're not on 4chan. -return unless /^(boards|images|sys)\.4chan\.org$/.test location.hostname -<% } %> - Conf = {} c = console d = document diff --git a/src/General/Main.coffee b/src/General/Main.coffee index b023d704f..09fe39241 100644 --- a/src/General/Main.coffee +++ b/src/General/Main.coffee @@ -127,8 +127,6 @@ Main = <% if (type === 'crx') { %> $.addClass doc, 'webkit' $.addClass doc, 'blink' - <% } else if (type === 'userjs') { %> - $.addClass doc, 'presto' <% } else { %> $.addClass doc, 'gecko' <% } %> @@ -151,13 +149,9 @@ Main = $.addClass doc, style setStyle() return unless mainStyleSheet - if window.MutationObserver - observer = new MutationObserver setStyle - observer.observe mainStyleSheet, - attributes: true - attributeFilter: ['href'] - else - $.on mainStyleSheet, 'DOMAttrModified', setStyle + new MutationObserver(setStyle).observe mainStyleSheet, + attributes: true + attributeFilter: ['href'] initReady: -> if d.title is '4chan - 404 Not Found' @@ -252,12 +246,10 @@ Main = checkUpdate: -> return unless Conf['Check for Updates'] and Main.isThisPageLegit() - # Check for updates after: - # - 6 hours since the last update on Opera because it lacks auto-updating. - # - 7 days since the last update on Chrome/Firefox. + # Check for updates after 7 days since the last update. # After that, check for updates every day if we still haven't updated. now = Date.now() - freq = <% if (type === 'userjs') { %>6 * $.HOUR<% } else { %>7 * $.DAY<% } %> + freq = 7 * $.DAY items = lastupdate: 0 lastchecked: 0 diff --git a/src/Images/ImageExpand.coffee b/src/Images/ImageExpand.coffee index 18d91fcc1..777cceea8 100644 --- a/src/Images/ImageExpand.coffee +++ b/src/Images/ImageExpand.coffee @@ -52,19 +52,6 @@ ImageExpand = return setFitness: -> (if @checked then $.addClass else $.rmClass) doc, @name.toLowerCase().replace /\s+/g, '-' -<% if (type === 'userjs') { %> -# XXX Opera doesn't support CSS vh. - return unless @name is 'Fit height' - if @checked - $.on window, 'resize', ImageExpand.resize - unless ImageExpand.style - ImageExpand.style = $.addStyle null - ImageExpand.resize() - else - $.off window, 'resize', ImageExpand.resize - resize: -> - ImageExpand.style.textContent = ":root.fit-height .full-image {max-height:#{doc.clientHeight}px}" -<% } %> toggle: (post) -> {thumb} = post.file diff --git a/src/Meta/manifest.json b/src/Meta/manifest.json index b9793bda3..fb8cfba4e 100644 --- a/src/Meta/manifest.json +++ b/src/Meta/manifest.json @@ -15,7 +15,8 @@ "run_at": "document_start" }], "homepage_url": "<%= meta.page %>", - "minimum_chrome_version": "26", + "minimum_chrome_version": "27", + "minimum_opera_version": "15", "permissions": [ "storage" ] diff --git a/src/Monitoring/ThreadUpdater.coffee b/src/Monitoring/ThreadUpdater.coffee index 0c3f5317c..7dab2983a 100644 --- a/src/Monitoring/ThreadUpdater.coffee +++ b/src/Monitoring/ThreadUpdater.coffee @@ -114,8 +114,7 @@ ThreadUpdater = By sending the `If-Modified-Since` header we get a proper status code, and no response. This saves bandwidth for both the user and the servers and avoid unnecessary computation. ### - # XXX 304 -> 0 in Opera - [text, klass] = if req.status in [0, 304] + [text, klass] = if req.status is 304 [null, null] else ["#{req.statusText} (#{req.status})", 'warning'] diff --git a/src/Monitoring/Unread.coffee b/src/Monitoring/Unread.coffee index 6ad07f03e..1e0b63fd6 100644 --- a/src/Monitoring/Unread.coffee +++ b/src/Monitoring/Unread.coffee @@ -184,9 +184,7 @@ Unread = else Favicon.default - <% if (type !== 'crx') { %> + <% if (type === 'userscript') { %> # `favicon.href = href` doesn't work on Firefox. - # `favicon.href = href` isn't enough on Opera. - # Opera won't always update the favicon if the href didn't change. $.add d.head, Favicon.el <% } %> diff --git a/src/Posting/QR.coffee b/src/Posting/QR.coffee index fb5bce620..981053704 100644 --- a/src/Posting/QR.coffee +++ b/src/Posting/QR.coffee @@ -330,7 +330,6 @@ QR = post = Get.postFromNode @ text = ">>#{post}\n" if (s = sel.toString().trim()) and post is Get.postFromNode sel.anchorNode - # XXX Opera doesn't retain `\n`s? s = s.replace /\n/g, '\n>' text += ">#{s}\n" @@ -501,7 +500,6 @@ QR = else if @ is QR.selected (QR.posts[index-1] or QR.posts[index+1]).select() QR.posts.splice index, 1 - return unless window.URL URL.revokeObjectURL @URL lock: (lock=true) -> @isLocked = lock @@ -558,25 +556,14 @@ QR = @filename = "#{file.name} (#{$.bytesToString file.size})" @nodes.el.title = @filename @nodes.label.hidden = false if QR.spoiler - URL.revokeObjectURL @URL if window.URL + URL.revokeObjectURL @URL @showFileData() unless /^image/.test file.type @nodes.el.style.backgroundImage = null return @setThumbnail() - setThumbnail: (fileURL) -> - # XXX Opera does not support blob URL + setThumbnail: -> # Create a redimensioned thumbnail. - unless window.URL - unless fileURL - reader = new FileReader() - reader.onload = (e) => - @setThumbnail e.target.result - reader.readAsDataURL @file - return - else - fileURL = URL.createObjectURL @file - img = $.el 'img' img.onload = => @@ -588,7 +575,7 @@ QR = s *= 3 if @file.type is 'image/gif' # let them animate {height, width} = img if height < s or width < s - @URL = fileURL if window.URL + @URL = fileURL @nodes.el.style.backgroundImage = "url(#{@URL})" return if height <= width @@ -601,10 +588,6 @@ QR = cv.height = img.height = height cv.width = img.width = width cv.getContext('2d').drawImage img, 0, 0, width, height - unless window.URL - @nodes.el.style.backgroundImage = "url(#{cv.toDataURL()})" - delete @URL - return URL.revokeObjectURL fileURL applyBlob = (blob) => @URL = URL.createObjectURL blob @@ -622,6 +605,7 @@ QR = applyBlob new Blob [ui8a], type: 'image/png' + fileURL = URL.createObjectURL @file img.src = fileURL rmFile: -> delete @file @@ -630,7 +614,6 @@ QR = @nodes.el.style.backgroundImage = null @nodes.label.hidden = true if QR.spoiler @showFileData() - return unless window.URL URL.revokeObjectURL @URL showFileData: -> if @file @@ -652,22 +635,17 @@ QR = QR.nodes.com.value = @com @nodes.span.textContent = @com reader.readAsText file - dragStart: -> - $.addClass @, 'drag' - dragEnd: -> - $.rmClass @, 'drag' - dragEnter: -> - $.addClass @, 'over' - dragLeave: -> - $.rmClass @, 'over' + dragStart: -> $.addClass @, 'drag' + dragEnd: -> $.rmClass @, 'drag' + dragEnter: -> $.addClass @, 'over' + dragLeave: -> $.rmClass @, 'over' dragOver: (e) -> e.preventDefault() e.dataTransfer.dropEffect = 'move' drop: -> - el = $ '.drag', @parentNode - $.rmClass el, 'drag' # Opera doesn't fire dragEnd if we drop it on something else - $.rmClass @, 'over' + $.rmClass @, 'over' return unless @draggable + el = $ '.drag', @parentNode index = (el) -> [el.parentNode.children...].indexOf el oldIndex = index el newIndex = index @ @@ -700,12 +678,8 @@ QR = img: imgContainer.firstChild input: input - if window.MutationObserver - observer = new MutationObserver @load.bind @ - observer.observe @nodes.challenge, - childList: true - else - $.on @nodes.challenge, 'DOMNodeInserted', @load.bind @ + new MutationObserver(@load.bind @).observe @nodes.challenge, + childList: true $.on imgContainer, 'click', @reload.bind @ $.on input, 'keydown', @keydown.bind @ @@ -836,10 +810,6 @@ QR = # Add empty mimeType to avoid errors with URLs selected in Window's file dialog. QR.mimeTypes.push '' nodes.fileInput.max = $('input[name=MAX_FILE_SIZE]').value - <% if (type !== 'userjs') { %> - # Opera's accept attribute is fucked up - nodes.fileInput.accept = "text/*, #{mimeTypes}" - <% } %> QR.spoiler = !!$ 'input[name=spoiler]' nodes.spoiler.hidden = !QR.spoiler @@ -1018,11 +988,6 @@ QR = QR.status() response: -> - <% if (type === 'userjs') { %> - # The upload.onload callback is not called - # or at least not in time with Opera. - QR.req.upload.onload() - <% } %> {req} = QR delete QR.req diff --git a/src/Quotelinks/QuotePreview.coffee b/src/Quotelinks/QuotePreview.coffee index fcd0bfd98..ab21dfa78 100644 --- a/src/Quotelinks/QuotePreview.coffee +++ b/src/Quotelinks/QuotePreview.coffee @@ -28,21 +28,6 @@ QuotePreview = cb: QuotePreview.mouseout asapTest: -> qp.firstElementChild - <% if (type === 'userjs') { %> - # XXX Opera workaround for "no mouseout fired" bug. - # Remove it once Opera uses Blink. - root = @ - workaround = (e) -> - if @ is root - e.stopPropagation() - return - $.event 'mouseout', null, root - $.off d, 'mousemove', workaround - $.off root, 'mousemove', workaround - $.on d, 'mousemove', workaround - $.on root, 'mousemove', workaround - <% } %> - return unless origin = g.posts["#{boardID}.#{postID}"] if Conf['Quote Highlighting'] From d93a4ebf4ba81fc434d37c158b782d955d20b50c Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Tue, 25 Jun 2013 17:15:23 +0200 Subject: [PATCH 056/172] Shave two lines in Sauce.coffee. --- src/Images/Sauce.coffee | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Images/Sauce.coffee b/src/Images/Sauce.coffee index 7c4892862..2f0167682 100644 --- a/src/Images/Sauce.coffee +++ b/src/Images/Sauce.coffee @@ -4,12 +4,10 @@ Sauce = links = [] for link in Conf['sauces'].split '\n' - continue if link[0] is '#' try - links.push @createSauceLink link.trim() + links.push @createSauceLink link.trim() if link[0] isnt '#' catch err # Don't add random text plz. - continue return unless links.length @links = links @link = $.el 'a', target: '_blank' From 7d38f0502c136d5c02b6a3b0e9483859eb98b196 Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Mon, 8 Jul 2013 23:40:48 +0200 Subject: [PATCH 057/172] More thoughtful unread-scroll code. --- src/Monitoring/Unread.coffee | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/Monitoring/Unread.coffee b/src/Monitoring/Unread.coffee index 6ad07f03e..34ad6180a 100644 --- a/src/Monitoring/Unread.coffee +++ b/src/Monitoring/Unread.coffee @@ -37,13 +37,11 @@ Unread = # Let the header's onload callback handle it. return if (hash = location.hash.match /\d+/) and hash[0] of Unread.thread.posts if Unread.posts.length - # Scroll to before the first unread post. - {root} = Unread.posts[0].nodes - while root = $.x 'preceding-sibling::div[contains(@class,"postContainer")][1]', root - post = Get.postFromRoot root - # Don't scroll if we have 0 posts read in this thread. - return unless post.isReply - break unless post.isHidden + # Scroll to a non-hidden, non-OP post that's before the first unread post. + post = Unread.posts[0] + while root = $.x 'preceding-sibling::div[contains(@class,"replyContainer")][1]', post.nodes.root + break unless (post = Get.postFromRoot root).isHidden + return unless root onload = -> root.scrollIntoView false if checkPosition root else # Scroll to the last read post. From a501bd70af74b698de9cf0932d1a893ae5fe113e Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Tue, 9 Jul 2013 21:35:33 +0200 Subject: [PATCH 058/172] Fix #1184. --- src/Posting/QR.coffee | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Posting/QR.coffee b/src/Posting/QR.coffee index 981053704..d73178dd8 100644 --- a/src/Posting/QR.coffee +++ b/src/Posting/QR.coffee @@ -1079,7 +1079,11 @@ QR = "/#{g.BOARD}/res/#{threadID}" else if g.VIEW is 'index' and !QR.cooldown.auto and Conf['Open Post in New Tab'] # replying from the index "/#{g.BOARD}/res/#{threadID}#p#{postID}" - (if Conf['Open Post in New Tab'] then $.open else location.assign) URL if URL + if URL + if Conf['Open Post in New Tab'] + $.open URL + else + window.location = URL QR.status() From 527c4ae3bbedbcaa390910bb983ed035ea20123b Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Sat, 13 Jul 2013 16:45:36 +0200 Subject: [PATCH 059/172] Changelog. --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5bda0c706..233760263 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +- Drop Opera <15 support. + ### 3.5.6 - *2013-07-02* - Fix flag filtering on /sp/ and /int/. From 95df86d83b7f7230431c48344f33cf3e96ee848a Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Sat, 13 Jul 2013 16:46:24 +0200 Subject: [PATCH 060/172] Release 4chan X v3.5.7. --- CHANGELOG.md | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 233760263..ae9e2b39f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +### 3.5.7 - *2013-07-13* + - Drop Opera <15 support. ### 3.5.6 - *2013-07-02* diff --git a/package.json b/package.json index 2955dc206..958bf08fe 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "4chan-X", - "version": "3.5.6", + "version": "3.5.7", "description": "Cross-browser extension for productive lurking on 4chan.", "meta": { "name": "4chan X", From 697cc36d8ea5b4bf6900d93dab5a576478daaa5b Mon Sep 17 00:00:00 2001 From: thebladeee <bladekiro@yahoo.com> Date: Sat, 13 Jul 2013 18:02:56 -0400 Subject: [PATCH 061/172] remove LiO --- json/archives.json | 9 --------- 1 file changed, 9 deletions(-) diff --git a/json/archives.json b/json/archives.json index 86613ddc9..a69a2dee0 100644 --- a/json/archives.json +++ b/json/archives.json @@ -43,15 +43,6 @@ "software": "foolfuuka", "boards": ["c", "w", "wg"], "files": ["c", "w", "wg"] -}, { - "uid": 5, - "name": "Love is Over", - "domain": "loveisover.me", - "http": true, - "https": true, - "software": "foolfuuka", - "boards": ["d", "h", "v"], - "files": ["d", "h", "v"] }, { "uid": 11, "name": "Foolz a Shit", From ddbec61e1bb9d92d4dd8594851755c812f19f5fe Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Sun, 14 Jul 2013 18:51:02 +0200 Subject: [PATCH 062/172] Use dataset setters instead of setAttribute. --- src/Filtering/Filter.coffee | 2 +- src/Filtering/ThreadHiding.coffee | 2 +- src/General/Clone.coffee | 2 +- src/General/Header.coffee | 2 +- src/General/Post.coffee | 2 +- src/General/Settings.coffee | 6 +++--- src/Images/ImageHover.coffee | 2 +- src/Quotelinks/Quotify.coffee | 14 ++++++++------ 8 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/Filtering/Filter.coffee b/src/Filtering/Filter.coffee index 8ac6e18c9..70feefb3b 100644 --- a/src/Filtering/Filter.coffee +++ b/src/Filtering/Filter.coffee @@ -206,7 +206,7 @@ Filter = el = $.el 'a', href: 'javascript:;' textContent: text - el.setAttribute 'data-type', type + el.dataset.type = type $.on el, 'click', Filter.menu.makeFilter return { diff --git a/src/Filtering/ThreadHiding.coffee b/src/Filtering/ThreadHiding.coffee index cd2ea2960..92b6677a1 100644 --- a/src/Filtering/ThreadHiding.coffee +++ b/src/Filtering/ThreadHiding.coffee @@ -96,7 +96,7 @@ ThreadHiding = className: "#{type}-thread-button" innerHTML: "<span>[ #{if type is 'hide' then '-' else '+'} ]</span>" href: 'javascript:;' - a.setAttribute 'data-fullid', thread.fullID + a.dataset.fullid = thread.fullID $.on a, 'click', ThreadHiding.toggle a diff --git a/src/General/Clone.coffee b/src/General/Clone.coffee index 178c1dc86..abb51c327 100644 --- a/src/General/Clone.coffee +++ b/src/General/Clone.coffee @@ -60,4 +60,4 @@ class Clone extends Post @isDead = true if origin.isDead @isClone = true index = origin.clones.push(@) - 1 - root.setAttribute 'data-clone', index + root.dataset.clone = index diff --git a/src/General/Header.coffee b/src/General/Header.coffee index abb3468d8..5ba74529f 100644 --- a/src/General/Header.coffee +++ b/src/General/Header.coffee @@ -140,7 +140,7 @@ Header = a.textContent if m = t.match /-(index|catalog)/ - a.setAttribute 'data-only', m[1] + a.dataset.only = m[1] a.href = "//boards.4chan.org/#{board}/" a.href += 'catalog' if m[1] is 'catalog' diff --git a/src/General/Post.coffee b/src/General/Post.coffee index 6f2a8369c..23a0dac0c 100644 --- a/src/General/Post.coffee +++ b/src/General/Post.coffee @@ -194,5 +194,5 @@ class Post rmClone: (index) -> @clones.splice index, 1 for clone in @clones[index..] - clone.nodes.root.setAttribute 'data-clone', index++ + clone.nodes.root.dataset.clone = index++ return diff --git a/src/General/Settings.coffee b/src/General/Settings.coffee index 68ce01c77..e98c6dfe4 100644 --- a/src/General/Settings.coffee +++ b/src/General/Settings.coffee @@ -458,9 +458,9 @@ Settings = td.innerHTML = '<select></select>' select = td.firstElementChild unless select.disabled = length is 1 - # XXX GM can't into datasets - select.setAttribute 'data-boardid', boardID - select.setAttribute 'data-type', type + $.extend select.dataset, + boardid: boardID + type: type $.on select, 'change', Settings.saveSelectedArchive $.add select, options else diff --git a/src/Images/ImageHover.coffee b/src/Images/ImageHover.coffee index cff961806..37a19ce56 100644 --- a/src/Images/ImageHover.coffee +++ b/src/Images/ImageHover.coffee @@ -13,7 +13,7 @@ ImageHover = el = $.el 'img', id: 'ihover' src: post.file.URL - el.setAttribute 'data-fullid', post.fullID + el.dataset.fullid = post.fullID $.add d.body, el UI.hover root: @ diff --git a/src/Quotelinks/Quotify.coffee b/src/Quotelinks/Quotify.coffee index 0acd189f9..808eedbca 100644 --- a/src/Quotelinks/Quotify.coffee +++ b/src/Quotelinks/Quotify.coffee @@ -44,9 +44,10 @@ Quotify = className: 'quotelink deadlink' target: '_blank' textContent: "#{quote}\u00A0(Dead)" - a.setAttribute 'data-boardid', boardID - a.setAttribute 'data-threadid', post.thread.ID - a.setAttribute 'data-postid', postID + $.extend a.dataset, + boardid: boardID + threadid: post.thread.ID + postid: postID else if redirect = Redirect.to 'thread', {boardID, threadID: 0, postID} # Replace the .deadlink span if we can redirect. a = $.el 'a', @@ -56,9 +57,10 @@ Quotify = textContent: "#{quote}\u00A0(Dead)" if Redirect.to 'post', {boardID, postID} # Make it function as a normal quote if we can fetch the post. - $.addClass a, 'quotelink' - a.setAttribute 'data-boardid', boardID - a.setAttribute 'data-postid', postID + $.addClass a, 'quotelink' + $.extend a.dataset, + boardid: boardID + postid: postID unless quoteID in @quotes @quotes.push quoteID From 33d3518e76c4935ac0f9ab8f9d02cd56026cc212 Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Sun, 14 Jul 2013 19:01:14 +0200 Subject: [PATCH 063/172] "id" -> "ID" in dataset suffixes. --- src/Filtering/ThreadHiding.coffee | 4 ++-- src/General/Get.coffee | 5 ++--- src/General/Settings.coffee | 8 +++----- src/Images/ImageHover.coffee | 4 ++-- src/Quotelinks/Quotify.coffee | 9 ++------- 5 files changed, 11 insertions(+), 19 deletions(-) diff --git a/src/Filtering/ThreadHiding.coffee b/src/Filtering/ThreadHiding.coffee index 92b6677a1..49d3607f3 100644 --- a/src/Filtering/ThreadHiding.coffee +++ b/src/Filtering/ThreadHiding.coffee @@ -96,7 +96,7 @@ ThreadHiding = className: "#{type}-thread-button" innerHTML: "<span>[ #{if type is 'hide' then '-' else '+'} ]</span>" href: 'javascript:;' - a.dataset.fullid = thread.fullID + a.dataset.fullID = thread.fullID $.on a, 'click', ThreadHiding.toggle a @@ -117,7 +117,7 @@ ThreadHiding = toggle: (thread) -> unless thread instanceof Thread - thread = g.threads[@dataset.fullid] + thread = g.threads[@dataset.fullID] if thread.isHidden ThreadHiding.show thread else diff --git a/src/General/Get.coffee b/src/General/Get.coffee index 7c8d84cb1..5e9cfbbcf 100644 --- a/src/General/Get.coffee +++ b/src/General/Get.coffee @@ -28,9 +28,8 @@ Get = threadID = path[3] postID = link.hash[2..] else # resurrected quote - boardID = link.dataset.boardid - threadID = link.dataset.threadid or 0 - postID = link.dataset.postid + {boardID, threadID, postID} = link.dataset + threadID or= 0 return { boardID: boardID threadID: +threadID diff --git a/src/General/Settings.coffee b/src/General/Settings.coffee index e98c6dfe4..d0360db9e 100644 --- a/src/General/Settings.coffee +++ b/src/General/Settings.coffee @@ -443,7 +443,7 @@ Settings = $.get 'selectedArchives', Conf['selectedArchives'], ({selectedArchives}) -> for boardID, data of selectedArchives for type, uid of data - if option = $ "select[data-boardid='#{boardID}'][data-type='#{type}'] > option[value='#{uid}']", section + if option = $ "select[data-board-i-d='#{boardID}'][data-type='#{type}'] > option[value='#{uid}']", section option.selected = true return addArchiveCell: (row, boardID, data, type) -> @@ -458,9 +458,7 @@ Settings = td.innerHTML = '<select></select>' select = td.firstElementChild unless select.disabled = length is 1 - $.extend select.dataset, - boardid: boardID - type: type + $.extend select.dataset, {boardID, type} $.on select, 'change', Settings.saveSelectedArchive $.add select, options else @@ -468,7 +466,7 @@ Settings = $.add row, td saveSelectedArchive: -> $.get 'selectedArchives', Conf['selectedArchives'], ({selectedArchives}) => - (selectedArchives[@dataset.boardid] or= {})[@dataset.type] = +@value + (selectedArchives[@dataset.boardID] or= {})[@dataset.type] = +@value $.set 'selectedArchives', selectedArchives keybinds: (section) -> diff --git a/src/Images/ImageHover.coffee b/src/Images/ImageHover.coffee index 37a19ce56..50745f51b 100644 --- a/src/Images/ImageHover.coffee +++ b/src/Images/ImageHover.coffee @@ -13,7 +13,7 @@ ImageHover = el = $.el 'img', id: 'ihover' src: post.file.URL - el.dataset.fullid = post.fullID + el.dataset.fullID = post.fullID $.add d.body, el UI.hover root: @ @@ -24,7 +24,7 @@ ImageHover = $.on el, 'error', ImageHover.error error: -> return unless doc.contains @ - post = g.posts[@dataset.fullid] + post = g.posts[@dataset.fullID] src = @src.split '/' if src[2] is 'images.4chan.org' diff --git a/src/Quotelinks/Quotify.coffee b/src/Quotelinks/Quotify.coffee index 808eedbca..c0d9dada9 100644 --- a/src/Quotelinks/Quotify.coffee +++ b/src/Quotelinks/Quotify.coffee @@ -44,10 +44,7 @@ Quotify = className: 'quotelink deadlink' target: '_blank' textContent: "#{quote}\u00A0(Dead)" - $.extend a.dataset, - boardid: boardID - threadid: post.thread.ID - postid: postID + $.extend a.dataset, {boardID, threadID: post.thread.ID, postID} else if redirect = Redirect.to 'thread', {boardID, threadID: 0, postID} # Replace the .deadlink span if we can redirect. a = $.el 'a', @@ -58,9 +55,7 @@ Quotify = if Redirect.to 'post', {boardID, postID} # Make it function as a normal quote if we can fetch the post. $.addClass a, 'quotelink' - $.extend a.dataset, - boardid: boardID - postid: postID + $.extend a.dataset, {boardID, postID} unless quoteID in @quotes @quotes.push quoteID From 1640ab5109624e5bb70af0031e4289537f2dd4e1 Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Sun, 14 Jul 2013 23:13:40 +0200 Subject: [PATCH 064/172] No need to allocate data to see if it's truthy in Post::parseComment. --- src/General/Post.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/General/Post.coffee b/src/General/Post.coffee index 23a0dac0c..e78c6ac4f 100644 --- a/src/General/Post.coffee +++ b/src/General/Post.coffee @@ -72,7 +72,7 @@ class Post # XPathResult.ORDERED_NODE_SNAPSHOT_TYPE === 7 nodes = d.evaluate './/br|.//text()', bq, null, 7, null for i in [0...nodes.snapshotLength] - text.push if data = nodes.snapshotItem(i).data then data else '\n' + text.push nodes.snapshotItem(i).data or '\n' @info.comment = text.join('').trim().replace /\s+$/gm, '' parseQuotes: -> From d1f3aa78b803495fb635f6c58d834e744664fb72 Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Mon, 15 Jul 2013 11:47:10 +0200 Subject: [PATCH 065/172] instanceof Event works fine now in GM --- 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 d73178dd8..87040aba6 100644 --- a/src/Posting/QR.coffee +++ b/src/Posting/QR.coffee @@ -389,7 +389,7 @@ QR = openFileInput: -> QR.nodes.fileInput.click() fileInput: (files) -> - if @ instanceof Element #or files instanceof Event # file input + if files instanceof Event # file input files = [@files...] QR.nodes.fileInput.value = null # Don't hold the files from being modified on windows {length} = files From 0366718b9407ac17e77879ffb8f8f162423dc8b3 Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Thu, 18 Jul 2013 01:01:56 +0200 Subject: [PATCH 066/172] Update RegExp MDN guide URL. --- html/General/Settings-section-Filter-guide.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/html/General/Settings-section-Filter-guide.html b/html/General/Settings-section-Filter-guide.html index e3c9c2979..ae73a66da 100644 --- a/html/General/Settings-section-Filter-guide.html +++ b/html/General/Settings-section-Filter-guide.html @@ -1,6 +1,6 @@ <div class="warning" #{if Conf['Filter'] then 'hidden' else ''}><code>Filter</code> is disabled.</div> <p> - Use <a href="https://developer.mozilla.org/en/JavaScript/Guide/Regular_Expressions">regular expressions</a>, one per line.<br> + Use <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions">regular expressions</a>, one per line.<br> Lines starting with a <code>#</code> will be ignored.<br> For example, <code>/weeaboo/i</code> will filter posts containing the string `<code>weeaboo</code>`, case-insensitive.<br> MD5 filtering uses exact string matching, not regular expressions. From 1941fcad507a41e964d13666382ef4530dc63d32 Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Thu, 18 Jul 2013 20:38:31 +0200 Subject: [PATCH 067/172] Add fuuka.worldathleticproject.org. Close #1195. --- json/archives.json | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/json/archives.json b/json/archives.json index a69a2dee0..59efbf14a 100644 --- a/json/archives.json +++ b/json/archives.json @@ -52,6 +52,15 @@ "software": "foolfuuka", "boards": ["adv", "asp", "cm", "e", "i", "lgbt", "n", "o", "p", "pol", "s", "s4s", "t", "trv", "y"], "files": ["adv", "asp", "cm", "e", "i", "lgbt", "n", "o", "p", "s", "s4s", "t", "trv", "y"] +}, { + "uid": 12, + "name": "worldathleticproject", + "domain": "fuuka.worldathleticproject.org", + "http": true, + "https": false, + "software": "foolfuuka", + "boards": ["e", "h", "p", "s", "u"], + "files": ["e", "h", "p", "s", "u"] }, { "uid": 7, "name": "Install Gentoo", From 6c87f852be667f057937a362bf6aadba86e155fa Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Fri, 19 Jul 2013 15:45:04 +0200 Subject: [PATCH 068/172] Add comment about 4chan's issue concerning deadlinks inside code tags. --- src/Quotelinks/Quotify.coffee | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Quotelinks/Quotify.coffee b/src/Quotelinks/Quotify.coffee index c0d9dada9..1d26c6e79 100644 --- a/src/Quotelinks/Quotify.coffee +++ b/src/Quotelinks/Quotify.coffee @@ -18,6 +18,9 @@ Quotify = if deadlink.parentNode.className is 'prettyprint' # Don't quotify deadlinks inside code tags, # un-`span` them. + # This won't be necessary once 4chan + # stops quotifying inside code tags: + # https://github.com/4chan/4chan-JS/issues/77 $.replace deadlink, [deadlink.childNodes...] return From 56d44bc310d44eef0ceb8a97aec584fdcf9356ae Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Sat, 20 Jul 2013 13:23:59 +0200 Subject: [PATCH 069/172] Fix #1189. --- src/Posting/QR.coffee | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Posting/QR.coffee b/src/Posting/QR.coffee index 87040aba6..0b455cb1b 100644 --- a/src/Posting/QR.coffee +++ b/src/Posting/QR.coffee @@ -683,8 +683,8 @@ QR = $.on imgContainer, 'click', @reload.bind @ $.on input, 'keydown', @keydown.bind @ - $.get 'captchas', [], (item) => - @sync item['captchas'] + $.get 'captchas', [], ({captchas}) => + @sync captchas $.sync 'captchas', @sync # start with an uncached captcha @reload() @@ -697,7 +697,8 @@ QR = $.addClass QR.nodes.el, 'has-captcha' $.after QR.nodes.com.parentNode, [imgContainer, input] - sync: (@captchas) -> + sync: (captchas) -> + QR.captcha.captchas = captchas QR.captcha.count() getOne: -> @clear() From b903117ea92666e6d3f155cef22ca38395340db2 Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Sat, 20 Jul 2013 16:26:03 +0200 Subject: [PATCH 070/172] Update deps. --- package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 958bf08fe..b01920f2d 100644 --- a/package.json +++ b/package.json @@ -17,15 +17,15 @@ }, "devDependencies": { "grunt": "~0.4.1", - "grunt-bump": "~0.0.7", + "grunt-bump": "~0.0.11", "grunt-concurrent": "~0.3.0", - "grunt-contrib-clean": "~0.4.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.4.4", - "grunt-shell": "~0.3.0" + "grunt-contrib-watch": "~0.5.0", + "grunt-shell": "~0.3.1" }, "repository": { "type": "git", From dc8551bcf181aea35e07d814edca2f7169d467ed Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Sat, 20 Jul 2013 18:08:53 +0200 Subject: [PATCH 071/172] Fix #1194. --- CHANGELOG.md | 2 ++ src/Posting/QR.coffee | 26 ++++++++++++++++---------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ae9e2b39f..793177903 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +- Fix impossibility to create new threads when in dead threads. + ### 3.5.7 - *2013-07-13* - Drop Opera <15 support. diff --git a/src/Posting/QR.coffee b/src/Posting/QR.coffee index 0b455cb1b..fdaa06e0c 100644 --- a/src/Posting/QR.coffee +++ b/src/Posting/QR.coffee @@ -115,7 +115,8 @@ QR = status: -> return unless QR.nodes - if g.DEAD + {thread} = QR.posts[0] + if thread isnt 'new' and g.threads["#{g.BOARD}.#{thread}"].isDead value = 404 disabled = true QR.cooldown.auto = false @@ -501,6 +502,7 @@ QR = (QR.posts[index-1] or QR.posts[index+1]).select() QR.posts.splice index, 1 URL.revokeObjectURL @URL + QR.status() lock: (lock=true) -> @isLocked = lock return unless @ is QR.selected @@ -536,15 +538,18 @@ QR = if input.type is 'checkbox' @spoiler = input.checked return - {value} = input - @[input.dataset.name] = value - return if input.nodeName isnt 'TEXTAREA' - @nodes.span.textContent = value - QR.characterCount() - # Disable auto-posting if you're typing in the first post - # during the last 5 seconds of the cooldown. - if QR.cooldown.auto and @ is QR.posts[0] and 0 < QR.cooldown.seconds <= 5 - QR.cooldown.auto = false + {name} = input.dataset + @[name] = input.value + switch name + when 'thread' + QR.status() + when 'com' + @nodes.span.textContent = @com + QR.characterCount() + # Disable auto-posting if you're typing in the first post + # during the last 5 seconds of the cooldown. + if QR.cooldown.auto and @ is QR.posts[0] and 0 < QR.cooldown.seconds <= 5 + QR.cooldown.auto = false forceSave: -> return unless @ is QR.selected # Do this in case people use extensions @@ -652,6 +657,7 @@ QR = (if oldIndex < newIndex then $.after else $.before) @, el post = QR.posts.splice(oldIndex, 1)[0] QR.posts.splice newIndex, 0, post + QR.status() captcha: init: -> From 8a9df9d15a8d3e42280fe39836800d99f76b17f7 Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Sat, 20 Jul 2013 18:19:51 +0200 Subject: [PATCH 072/172] Tiny optimization to avoid calling QR.status() multiple times when closing the QR. --- src/Posting/QR.coffee | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Posting/QR.coffee b/src/Posting/QR.coffee index fdaa06e0c..8319392a2 100644 --- a/src/Posting/QR.coffee +++ b/src/Posting/QR.coffee @@ -74,8 +74,8 @@ QR = QR.cleanNotifications() d.activeElement.blur() $.rmClass QR.nodes.el, 'dump' - for i in QR.posts - QR.posts[0].rm() + for post in QR.posts.splice 0, QR.posts.length, new QR.post true + post.delete() QR.cooldown.auto = false QR.status() focusin: -> @@ -494,15 +494,17 @@ QR = @select() if select @unlock() rm: -> - $.rm @nodes.el + @delete() index = QR.posts.indexOf @ if QR.posts.length is 1 new QR.post true else if @ is QR.selected (QR.posts[index-1] or QR.posts[index+1]).select() QR.posts.splice index, 1 - URL.revokeObjectURL @URL QR.status() + delete: -> + $.rm @nodes.el + URL.revokeObjectURL @URL lock: (lock=true) -> @isLocked = lock return unless @ is QR.selected From 9a2692d47a0a78c41e71ebbbeda14aff2e224da8 Mon Sep 17 00:00:00 2001 From: Zixaphir <zixaphirmoxphar@gmail.com> Date: Sun, 21 Jul 2013 08:47:29 -0700 Subject: [PATCH 073/172] I'm tired. --- CHANGELOG.md | 1 - CONTRIBUTING.md | 49 -- changelog-old | 1327 ----------------------------------------------- 3 files changed, 1377 deletions(-) delete mode 100644 CONTRIBUTING.md delete mode 100644 changelog-old diff --git a/CHANGELOG.md b/CHANGELOG.md index f7dd86cf7..fab47159e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,7 +16,6 @@ **seaweedchan**: - Update archives ->>>>>>> b3db117d714416c4f178c0359372861a1b5bab71 ### v1.2.17 *2013-06-17* diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index 735b0841c..000000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,49 +0,0 @@ -## Reporting bugs and suggestions - -Reporting bugs: - -1. Make sure both your **browser** and **4chan X** are up to date. -2. Disable your other extensions & scripts to identify [conflicts](https://github.com/MayhemYDG/4chan-x/wiki/FAQ#known-conflicting-extensions). -3. If your issue persists, open a [new issue](https://github.com/MayhemYDG/4chan-x/issues) with the following information: - 1. Precise steps to reproduce the problem, with the expected and actual results. - 2. Console errors, if any. - 3. Browser version. - 4. Your exported settings. If your settings contains sensible information (e.g. personas), edit the text file manually. - -Open your console with: -- `Ctrl + Shift + J` on Chrome and Opera. -- `Ctrl + Shift + K` on Firefox. - -Respect these guidelines: -- Describe the issue clearly, put some effort into it. A one-liner isn't a good enough description. -- If you want to get your suggestion implemented sooner, make it convincing. -- If you want to criticize, make it convincing and constructive. -- Be mature. Act like an idiot and you will be blocked without warning. - -## Development & Contribution - -### Get started - -- Install [node.js](http://nodejs.org/). -- Install [Grunt's CLI](http://gruntjs.com/) with `npm install -g grunt-cli`. -- Clone 4chan X. -- `cd` into it. -- Install/Update 4chan X dependencies with `npm install`. - -### Build - -- Build with `grunt`. -- Continuously build with `grunt watch`. - -### Release - -- Update the version with `grunt patch`, `grunt minor` or `grunt major`. -- Release with `grunt release`. - -Note: this is only used to release new 4chan X versions, and is **not** needed or wanted in pull requests. - -### Contribute - -- Edit the CoffeeScript sources. -- If the edits affect regular users, edit the changelog. -- Open a pull request. diff --git a/changelog-old b/changelog-old deleted file mode 100644 index 598fa09a1..000000000 --- a/changelog-old +++ /dev/null @@ -1,1327 +0,0 @@ -master - -2.39.7 -- Mayhem - Update archive redirection for /pol/ and /vg/. - -2.39.6 -- Mayhem - Fix QR. - -2.39.5 -- Mayhem - Update archive redirection for even more boards. - -2.39.4 -- Mayhem - Update archive redirection for many boards. - -2.39.3 -- Mayhem - Add /fa/ and /s4s/ archive redirection. - -2.39.2 -- Mayhem - Fix importing settings containing unicode characters. - -2.39.1 -- Mayhem - Add /gd/, /out/, /vp/ and /vr/ archive redirection. - -2.39.0 -- Queue - Fix rare bug in Relative Post Dates. -- Mayhem - Add Import/Export settings. - -2.38.1 -- Mayhem - Fix a little regression introduced in 2.38.0 for webkit browsers. - -2.38.0 -- Queue - Add Relative Post Dates ("35 seconds ago"), disabled by default. -- Mayhem - Add /int/ archive redirection for threads, and post resurrection. - -2.37.6 -- Mayhem - Fix image expanding. - -2.37.5 -- Mayhem - Fix quoting inside inlined backlinks. - -2.37.4 -- James Campos - Don't expand pdfs -- Mayhem - Add /po/ archive redirection for threads, images and post resurrection. - Fix quoting. - -2.37.3 -- Mayhem - Fix successful posting causing errors. - Fix 4chan X trying to interact with >>>/board/rules links. - -2.37.2 -- aeosynth - Beep on new post to completely read thread -- Mayhem - Fix dead quotes. - -2.37.1 -- noface - Fix Anonymize not working on stubs. -- Mayhem - Fix selection quoting on Opera. - Fix history bug with Persistent QR enabled on Chrome. - Fix posting warning not displaying the reason. - Fix deadquotes showing up in code-tags. - -2.37.0 -- noface - Add Catalog Links toggle. - Fix Anonymize not working on hovered posts. -- Mayhem - Added catalog support. - Sync thread hiding between index and catalog. - Add /c/ archived thread and image redirection. - -2.36.3 -- Mayhem - Fix next/previous page keybinds. - -2.36.2 -- noface - Add tags support on /f/. -- Mayhem - Add /mu/ archived image redirection. - -2.36.1 -- noface - The Menu now has search links for Archivers. -- Mayhem - Added possibility to display unix timestamps with File Info Formatting. - -2.36.0 -- Mayhem - Added thread creation QR cooldown. - Fix QR cooldown timer between non-sage and sage posts. You can submit a non-sage post 30 seconds after a sage one. - Fix /q/ QR cooldowns for image and sage posts. - -2.35.4 -- Mayhem - Removed the obnoxious 4chan Pass ad in captcha errors when posting. - Fix 'Administrator/Moderator/Developer Replies' creating extra backlinks on /q/, again. - -2.35.3 -- Mayhem - Larger Comment text input by default for 4chan Pass users and on /f/ (no captcha). - -2.35.2 -- Mayhem - Fix 4chan Pass with QR on Firefox. - -2.35.1 -- Mayhem - Add support for 4chan Pass. - You can now use 'Enter' in keybind combinations. - -2.35.0 -- Mayhem - Use 4chan's API to fetch posts for: - - Thread Updater. - - Quote Inlining. - - Quote Previewing. - - Thread Expansion. - - Comment Expansion. - This will make fetching faster, and reduce bandwidth usage. - Add an option to disable 4chan's inline extension. Enabled by default. - Fix compatibility with Scriptish's auto-udpater. - -2.34.10 -- Mayhem - Fix 4chan X. Blame moot. - -2.34.9 -- Mayhem - Add /g/, /k/, /w/, /an/, /cgl/, /ck/, /lit/, /toy/ and /x/ archived image redirection. - One-word-captcha now works in the report window. - Fix duplicate file upload error link. - -2.34.8 -- Mayhem - One-word-captcha: you don't need to input an extra space anymore, the true word will be duplicated. - -2.34.7 -- Mayhem - Fix one-word-captcha, you'll need to leave a space for the fake word now. - -2.34.6 -- Mayhem - Fix error caused by change in 4chan's HTML about hidden filename in case of spoiler. - -2.34.5 -- Mayhem - Fix cooldown on /q/. - Fix thread creation with no file on /q/. - Fix 'Administrator/Moderator/Developer Replies' creating extra backlinks on /q/. - Add /mlp/ archive redirection. - -2.34.4 -- Mayhem - Add /q/ archive redirection. - -2.34.3 -- Mayhem - Update /k/ archive redirection. - -2.34.2 -- Mayhem - Adjust background tabs max update interval down to 5 minutes instead of 10. - Divide the Delete Link in the Menu into a Post and Image deletion links. - The Delete Links in the Menu now have a cooldown. - Add /fit/ archive redirection. - -2.34.1 -- Mayhem - Add /wsg/ archive redirection. - -2.34.0 -- Mayhem - New feature: Menu, which - - replaces and includes Report Button and Delete Button. - - add one-click Filter buttons. - - add download links to automatically save the file with its original filename. Chrome-only currently. - - add archive links. - - can integrate features from external userscripts/extensions, see https://github.com/MayhemYDG/4chan-x/wiki/Menu-API - The updater's refresh interval will now increase gradually in inactive threads: - - "Inactive thread" defines a thread that has not received any replies since its last refresh. - - Threads receiving a reply will have its updater interval reset to user's setting. - - The refresh interval will grow up to 90 seconds on visible tabs. - - Unfocused tabs will grow up to 120, 300, then 600 seconds. - - It takes at least 230 seconds to get to 120, at least 350 seconds to get to 300, and at least 650 seconds to get to 600. - - Focusing back to a tab will reset its inactivity state to normal. - - It basically changes nothing, but it salvages 4chan from being slaughtered by a massive amount of concurrent connections. - - see http://www.4chan.org/tmp/extensions.html - The updater's refresh interval is now limited to 5 seconds minimum. - Fix the Settings' window size on small screens, should be useable on Opera Mobile. - -2.33.8 -- Mayhem - Add Country filtering. - -2.33.7 -- Mayhem - Add /ck/ archive redirection. - -2.33.6 -- Mayhem - Update/fix archive redirection method. - -2.33.5 -- Mayhem - Add /sp/ archive redirection. - -2.33.4 -- Mayhem - Fix QR with the new captcha loading method. - The QR will now work on /f/ too. - -2.33.3 -- Mayhem - Revert changes that broke fetchers in Firefox. - -2.33.2 -- Mayhem - After 1000+ characters, a character counter will appear in the QR. - Add /soc/ archive redirection. - -2.33.1 -- Mayhem - Add /r9k/ archive redirection. - -2.33.0 -- btmcsweeney - Allow users to specify text for sauce links. -- Mayhem - Dead quotes can now be previewed or inlined with compatible archivers. - Opera fixes. - /f/ fixes. - -2.32.1 -- Mayhem - Fix images uploaded as spoilers. - -2.32.0 -- aeosynth - delete button -- Mayhem - Fix spoiler/code tag keybinds being ignored on post submission. - -2.31.6 -- Mayhem - Update captcha longevity to 5 minutes max, as according to 4chan's change. - -2.31.5 -- Mayhem - Fix spoiler and code tag keybinds regression. - -2.31.4 -- Mayhem - Add /an/ and /toy/ archive redirection. - -2.31.3 -- Mayhem - Add /cgl/, /e/, /mu/ and /w/ archive redirection. - -2.31.2 -- Mayhem - Add /x/ archive redirection. - -2.31.1 -- Mayhem - Fix some stub issues. - -2.31.0 -- Mayhem - Add a per filter stub setting. - -2.30.8 -- Mayhem - Fix quote previewing of forward hidden posts. - -2.30.7 -- aeosynth - Fix expanding comments. -- Mayhem - Fix file size info in case of spoiler image. - -2.30.6 -- Mayhem - Fix file size formatting always using integers. - -2.30.5 -- Mayhem - Fix syntax highlighting in code of fetched posts on /g/. - Add quick [code] tags keybind (alt+c). - -2.30.4 -- Mayhem - Add /co/ and /k/ archive redirection. - Fix quote resurrection of existing posts. - -2.30.3 -- Mayhem - Various bug fixes. - -2.30.2 -- Mayhem - Various bug fixes. - Add seconds as a supported Time Formatting specifier. - -2.30.1 -- Mayhem - Fix 4chan X not affecting fetched posts on Firefox Stable and Opera. - -2.30.0 -- Mayhem - Support 4chan's new HTML. - Add Spoiler indicator option in File Info Formatting - Remove archive.no-ip.org archive redirections. - -2.29.5 -- Mayhem - Fix QR filetype checking on /w/. - -2.29.4 -- Mayhem - Auto-GIF will not run in /gif/. - Fix QR filetype checking. - -2.29.3 -- Mayhem - Update Quick Reply posting method, this fixes compatibility for uncommon browsers such as - Opera Mobile 12 and Luakit for example. - -2.29.2 -- Mayhem - Add HTTPS support. - Ban support improvements and fixes. - -2.29.1 -- Mayhem - Update posting support to use https with sys.4chan.org according to 4chan's latest change. This fixes the 'referer' error. - Update redirection to installgentoo's archives. - -2.29.0 -- Mayhem - New feature: Quote Resurrection, automatically linkifying dead quotes to archives. - -2.28.1 -- Mayhem - General performance improvements. - Threads will now be updated instantly after posting through the QR. - Your own posts will not count toward the unread count after posting through the QR. - Performance issues are now lessened with QR thumbnails of high-res pictures. - You can now use Shift+Click on the file input to remove the selected reply's file. - Reply navigation keybinds will now scroll as you navigate. -- noface - Add unique ID to filter. - -2.28.0 -- ahodesuka - Reply/Thread File Info Formatting: - - Link: %l, %L (Original file names are shown inside threads). - - Size: %B (Bytes), %K (KB), %M (MB), %s (4chan default). - - Resolution/PDF: %r - - Original filename: %n, %N. -- noface - Update imagelimit for /mlp/. - Fix stubs if poster has unique ID. -- Mayhem - You can now filter or highlight admin/mod posts. - New sauce parameter. $4: Current board. - -2.27.1 -- Mayhem - Fix stubs with the new filter. - Fix mail filtering. - The MD5 will now check for exact string matching, it will not use regular expressions. - -2.27.0 -- aeosynth / ahodesuka - new option: expand images from current position -- ahodesuka - Add Open Reply in New Tab option for replies made from the main board (not dumping). - Scroll back up (top of anchor - 42px) when unexpanding images. -- Mayhem - The Filter now has per filter settings: - - Filter the OP only along its thread, replies only, or both. - - Per boards, or global. - - Highlight, or hide. - - Highlighted OPs will have their threads put on top of the board page by default. - New filter group: Image dimensions. - Fix posting on Safari. - Fix rare case where the QR would not accept certain image types. - -2.26.4 -- Mayhem - Add /vg/ archive redirection. - Update ban support with the QR. - Fix history issues on Chrome. - -2.26.3 -- Mayhem - Ensure fresh captcha on QR load. - There is now a reply counter in the QR dump list. - Fix unXXXifier on pages starting with not XXXed numbers. - Update image limit indicator for /vg/. - -2.26.2 -- Mayhem - New option to fix XXX'd post numbers. - Fix post number quoting on /b/ and /v/. - Update archive redirection for /v/. - -2.26.1 -- Mayhem - You can now drag replies in the dump list to reorder them. - Fix sauce links with spoiled thumbnails. - -2.26.0 -- desuwa - New option: remember the size of the QR on Firefox. -- aeosynth - prevent post form flicker -- Mayhem - Load QR's iframe to sys.4chan.org faster, unless you use Greasemonkey. Thanks desuwa. - Increase Sauce linking possibilites: - Thumbnails, full images, MD5 hashes. - New option: Recursive Filtering: Filter replies of filtered posts. - Unread Favicon is now optional, independent of Unread Count. - Fix some compatibility issues with file drag and drop, notably with QuickDrag extension. - -2.25.5 -- Mayhem - Hide the normal post form by default, optional. - -2.25.4 -- Mayhem - Fix text inputs not sent/saved correctly in the QR when pasted for example. - Revert hidding normal post form. - -2.25.3 -- Mayhem - Fix image spoiler always enabled, bug introduced in 2.25.2. - -2.25.2 -- Mayhem - Reverted updater's scrolling behavior. - Fix image posting on Firefox along with Unicode characters in the form. - Ghettofix Greasemonkey compatibility with the new QR. - Greasemonkey is still NOT RECOMMENDED, use Scriptish instead. - -2.25.1 -- Mayhem - Fix missing image filename uploads. Thanks desuwa. - -2.25.0 -- Mayhem - Fix 4chan X on /f/. - Support for the normal post form has been phased out in favor of the new QR. - New rewritten and redesigned Quick Reply. Highlights include: - - Easy dumping for image, text, or samefagging. - - Toggle auto-posting by trying to submit during the cooldown. - - Multiple file selection. - - Image thumbnails. - - Drag and drop files on the page will add these files to the QR. - - Cache captchas with Shift + Enter. - - Create new threads with the QR, will auto-noko. - - Also includes all the previous features: - - Cooldown and auto-posting. - - Posting error detection and prevention. - - Captcha reloading on backspace. - - Auto Watch on reply/thread creation. - - Text selection to quote. - - Etc... - CAUTION: - Images posted with the new QR on Firefox/Aurora/Nightly will have their filename missing. - see https://github.com/MayhemYDG/4chan-x/issues/137 - Greasemonkey is buggy, use Scriptish instead. - -2.24.5 -- Mayhem - Index Navigation and the See next/previous thread keybinds will not cycle through board pages anymore. - Fix archive redirection in Opera. - Opera support will now be temporarily on hold. - see https://github.com/MayhemYDG/4chan-x/issues/136 - -2.24.4 -- ahodesuka - Scroll back up when unexpanding images. -- e000 - Prevent absurd cooldown durations. -- Mayhem - Better image expanding reload, should fool CloudFlare's cache. -- seaweed - Prevent the hover image to be partially masked by the url preview/status bar. - -2.24.3 -- Mayhem - Set image limit in Thread Stats to 252 for /a/ and /v/, default to 152. - Fix 4chan X in locked threads. - -2.24.2 -- mayhem - fix options popping up everytime a page loads - -2.24.1 -- mayhem - fix Open thread in new tab keybind for Safari with Ninjakit - fix Index/Reply Navigation working in both cases when only one is enabled - -2.24.0 -- mayhem - redirect 404'd pictures to archives when possible - new keybind to open the options: ctrl+o - the unread count will decrease when inlining quotes of unread posts - the report button can open multiple popups again - add omploader to the list of optional flavors (http://ompldr.org/upload?url1=) - update archive redirections, add /lit/ and /u/ - fit horizontally for Image Hover - -2.23.7 -- mayhem - update archive redirections - -2.23.6 -- mayhem - fix empty sauce or all flavors commented out from breaking 4chan X - -2.23.5 -- mayhem - increase the thread updater retry timeout limit at each retry - fix selection to quote when selecting started from the end of a line on Firefox - -2.23.4 -- mayhem - thread updater network optimization - prevent regexp errors with the filter - -2.23.3 -- mayhem - fix 2.32.2 regression duplicating new posts in rare cases - -2.23.2 -- mayhem - hide original posts from inlined backlinks - optional - enable autoposting when submitting a captcha while on cooldown - fix caret position when quoting on Opera - -2.23.1 -- mayhem - fix favicon updating on Opera - fix compatibility with Tampermonkey - -2.23.0 -- mayhem - multiple unread favicons to chose in the options - quotes are now inserted at the caret position in the QR - quotes also replace the text selection in the QR - open QR focused when using the `Open QR without post number inserted` keybind - fix thread updater for Opera -- aeosynth - update the captcha caching expiration date to 30mins - -2.22.2 -- mayhem - indicate if the settings require a feature to be enabled - fix obscure and continuous prompts to auto update - -2.22.1 -- mayhem - change 'Duckroll' for 'Cross-thread' - fix image expanding fitness with an inlined backlink on Firefox - -2.22.0 -- mayhem - new Indicate Duckrolls feature - put regex.info sauce back - disabled by default - fix for auto image reloading in 404'd threads on Firefox - -2.21.4 -- mayhem - fix 4chan X version updater - -2.21.3 -- mayhem - fix locked thread icons with fit width/screen enabled on Firefox - fix fit width on Opera - for userstylers: you can use the rendering engine body class - -2.21.2 -- mayhem - fix time formatting year in Opera - fix QR keybinds - fix QR posts getting swallowed by sys.4chan.org - -2.21.1 -- mayhem - fix Opera - -2.21.0 -- mayhem - initiate 4chan X earlier - performance improvements - regular expressions based filter - remove image preloading - automatically reload expanded pictures on error - handle bans with the thread updater - use unread favicons by ferongr - -2.20.3 -- mayhem - fix DST for two days of the year - -2.20.2 -- mayhem - update archive redirection -- aeosynth - hopefully fix qr error / update messages - rm support throd link - -2.20.1 -- mayhem - fix regression: qr not preventing errors - -2.20.0 -- mayhem - do not display inlined quotes within the quote preview - fix cross threads quotes in expanded threads or inlined cross quotes - default post styling for quote previews -- aeosynth - script auto updating - -2.19.3 -- mayhem - quote inlining default styling (by xat) - add up/down/right/left keybinding support -- aeosynth - fixed bug that caused script to fail when time formatting enabled - -2.19.2 -- mayhem - update archives redirections -- aeosynth - change unread favicons (by xat-) - -2.19.1 -- mayhem - fix OP indication in expanded comments - fix no.id links within cross thread/board inlined quote - -2.19.0 -- mayhem - backlink formatting - distinguishable unread favicons on white backgrounds - fix updater refreshing 404'd threads - fix backlinks added into inlined quotes -- aeosynth - fix tab title for 404d threads - -2.18.3 -- mayhem - fix quote features in expanded comments - fix scrolling onto a quote not showing the preview - -2.18.2 -- aeosynth: - fix long thread watcher titles - fix normal form errors with an open empty QR - -2.18.1 -- mayhem: - fix persistent qr not cleaning the file input -- aeosynth: - updater: scroll background tabs - -2.18.0 -- mayhem: - back to normal versioning - bring back auto posting - don't start the cooldown on thread creation - limit the file upload dialog to the accepted file types (qr) -- aeosynth: - show linebreaks as spaces in title & watcher - auto posting fixes - option to remember qr spoiler state - -11.8.15 -- aeosynth: - convert qr from hidden iframes to ajax - derp, nevermind. ajaxing reverted, still working on it in dev branch - -11.8.11.1 -- aeosynth: - fix qr autohiding - -11.8.11.0 -- aeosynth: - - rm auto post checkbox - if (captcha filled or cached) and (text or file) - auto post after cooldown - - options updated immediately instead of requiring page refresh - -11.8.10.1 -- aeosynth: - - persistent captchas (expiring after 4 hours, 55 minutes) - - files upload in 'correct' order - - manually submitting will use cached captchas - -11.8.10.0 -- aeosynth: - - fix captcha caching on blank content - -11.8.9.0 -- aeosynth: - - change green oval archive to gentoomen - - validate filesize asap - - show correct captcha cache length when creating qr - - 1s delay when autoposting after errors to not look so automated - - cache captcha on blank text / file - -11.8.6.0 -- mayhem: - - fix post links in expanded threads - - fix 4chan X in closed threads -- aeosynth: - - only auto scroll focused tabs - - quote inlining: only work on unmodified left-click - - select multiple files (one at a time) - - captcha caching - - qr: optional auto hiding - - copy old textarea value - - scroll to bottom of page if post isn't found (thumbnail generation takes - time) - - only scroll focused tabs - - time: %e, %k, %l - - reverted hovering fix - -2.17.1 -- mayhem: - - fix updater when there is a hash in the url -- aeosynth: - - better hovering fix - -2.17.0 -- mayhem: - - Make updater's settings dynamic - - Multi-line quoting - - keybinds: z: reset unread count (useful when page is not scrollable) - - fix remember updater's interval settings - - fix wrong keybind input in options - - fix time preview - - fix backlink inlining removing its container - - fix options key/keybinds key variables. -- aeosynth: - - new imgur upload link commented out in the sauces - - link to dup file in the QR error - - fix bug with hovering elements not disappearing - -2.16.1 -- mayhem: - - fix updater's custom settings - -2.16.0 -- mayhem: - - Thread Stats performance fix, especially on long threads - - Sauce performance improvement - - fix Quote Inlining for a pattern - - other minor performance improvements and bug fixes -- aeosynth: - - fix an upgrading problem (1.x -> 2.x) - - fix minor bugs with auto-posting - - add updater scrolling - - enable auto-updating by default - - alphabetize option groups - -2.15.1 -- mayhem: - - prevent upload of too large files - - fix options height for netbooks - - /a/ is now archived on easymodo - -2.15.0 -- mayhem: - - custom hotkey binding - - image spoiler revealer - - optional auto noko - - add a class for reply stubs - - fix options centering on Opera - - fix append '#watch' only when auto watch is enabled - - fix cooldown with the normal post form - - fix `Select next reply` hotkey behaviour -- aeosynth: - - keep options dialog at constant size - - drop firefox 3.6 support (again...) - - comment out tineye - - trying to post during cooldown will enable auto-post - - fix errors w/ noscript - -2.14.0 -- mayhem: - - firstrun dialog - - fix backlinking future posts - - fix op non-backlinking - - fix thread expansion on /t/ - - fix sage cooldown - - fix extra link area around images - - fix persistent qr -- aeosynth: - - reply navigation - - fix time formatting of xhr posts - - fix %P time formatting - -2.13.0 -- mayhem: - - fix various bugs - - keybinds: 0: actually refresh page 0 - - auto posting - - add google sauce -- arbitrary time formatting -- rewrite options dialog, lightboxing -- various fixes - -2.12.0 -- revert the status bar blocking changes -- add a 5px padding-bottom to #qp - -2.11.4 -- fix another quoting bug - -2.11.3 -- really fix quoting - -2.11.2 -- fix quoting stuff - -2.11.1 -- work on firefox < 6.0 - -2.11.0 -mayhem: - - fix quote highlighting -- block status bar when hovering quotes / images (may break image leeching) - -2.10.0 -mayhem: - space between backlinks (to prevent them from spilling out of the page) - thread stats: fix wrong selector id when image limit reached - fix localized time + inlined quotes -- fix edge hovering (flip to left of mouse @ right screen edge) -- work on closed threads (some stickies) -- op backlinking option -- quote highlighting option -- (maybe) work on fx3 - -2.9.0 -- mayhem: - better placement of inlined backquotes - fix qr in expanded threads - inline quote fixes - fetch name, mail and pass in cookies at each qr opening - highlight quoted post during quote preview - add ' (OP)' to op quotes - am/pm localized time - give visual feedback on which quote has been inlined - fix paypal link - cooldown: alert only when posting from normal post form - fix cooldown - thread stats - auto noko - fix jumping hover dialogs -- forwardlinks -- rewrite image expansion/fitting -- image expansion option: fit height -- keybind: e - toggle thread expansion -- fix bug where x-board links on chrome are unclickable -- fix x-board / x-thread quoting - -2.8.1 -- tab over captcha - -2.8.0 -- mayhem: - redesigned options - don't backlink op on index pages - base64 donate button -- fix qr replying to wrong thread -- small tweaks - -2.7.0 -- mayhem: - add class to reply hider and report buttons - fix anonymizer -- inline quoting -- don't break on >>>/board/ links (links w/o an id) -- remove op backlinking -- fix qr on expanded posts -- image hover: fit height - -2.6.0 -- mayhem: - start backlinks - fix watcher refresh false positive -- backlinks -- cross-board previews -- fix menu for opera -- slightly bigger qr textarea - -2.5.0 -- mayhem: - qp: better cross-thread op quoting - qp: fix overflowing preview - qr: prevent email field from popping out - qr: fix cloning values -- keybinds: fix m - -2.4.2 -- fix nodeInserted (work on new posts) -- qr: remember name - -2.4.1 -- fix image expansion bug - -2.4.0 -- mayhem: - auto watch reply - fix report button splitting over two lines - start merging /b/ackwash -- quote previews -- qr remix - -2.3.0 -- mayhem: - refresh watcher list on un/watch - auto refresh watcher list - update no-ip.org archive - fix auto-watch -- flavor comments - -2.2.2 -- hopefully fix upgrading issues - -2.2.1 -- mayhem: - fix op image expansion bug - fix op comment expansion - -- fix /b/ackwash multiple links bug -- make cooldown timer optional, disabled by default - -2.2.0 -- mayhem: - - don't select text when moving dialogs - - fix thread watcher's padding - - remove captcha logos from quick reply - - post in title - show default title when no subject/comment - - update tab title on 404 - - chrome - fix favicons - - fix 404 favicon - - fix updater retrying - - set timer to 0 on manual update - - fix threading when op's file is deleted - -- fix navigating past hidden threads -- fix updater option - verbose -- only navigate to pages that exist -- use Greasemonkey api (sigh) -- rewrite, reenable cooldown -- better image resize algorithm - -2.1.0 -- added back the 0 keybind - go to board's front page - -2.0.0 -- [chrome] qr error notifications -- x-browser auto-watch -- image expansion w/o horizontal scrollbars -- removed reply nav -- floating thread nav -- fix /b/ thread nav -- ignore middle clicks on images -- smarter redirect -- remove all GM_ functions -- [chrome] fix slow scrolling w/ unread post count in titlebar -- keybind: x: hide thread -- qr: remove `auto` -- rewrite / reorganize code - -1.27.8 -- fix qr image posting -- fix thread hiding -- fix movement - -1.27.7 -- fix qr persist -- fix updater defaults - -1.27.6 -- fix 'update now' button - -1.27.5 -- fix regression - reload captcha after posting - -1.27.4 -- fix imageHover on chrome - -1.27.3 -- fix regression - movement - -1.27.2 -- fix regression - close / refresh qr on chrome -- add updateURL for scriptish - -1.27.1 -- fix regression - unhide qr when quoting - -1.27.0 -- x-browser qr error checking (except for batshit insane chrome - see - http://code.google.com/p/chromium/issues/detail?id=20773) -- fix opera movement, again -- bigger options button -- move updater to bottom-right by default - -1.26.3 -- don't enable reply nav by default -- reset file input after posting (persistent qr) -- remove restore ids -- check for dst - -1.26.2 -- fix thread watcher position; remember new position - -1.26.1 -- fix unread count for opera -- fix default thread watcher placement - -1.26.0 -- image hover -- support/donate - -1.25.0 -- auto gif -- only preload in threads -- fix autohide -- sage cooldown = 60s - -1.24.0 -- image preloading - -1.23.1 -- fix updater notifications - -1.23.0 -- 'verbose' updater option -- remove /new/, /r9k/ from archivers -- tweak 'fit width' to get rid of horizontal scrollbar, only partially working - -1.22.0 -- update archives -- tweak updater feedback/ui - -1.21.0 -- image expansion types -- move global auto update option to updater dialog - -1.20.4 -- fix options - -1.20.3 -- update archives -- fool the cache - -1.20.2 -- fix qr again - -1.20.1 -- fix disappearing updater bug -- only restore ids on /b/ and /v/ -- fix JK keybinds - -1.20.0 -- keybinds - - add u - update now - - remove hHlLjk, numerical prefixes - - use [vimus](http://userscripts.org/scripts/show/93187) for non-chan keybinds -- fix slow scrolling -- add replies in original order so backlinks resolve properly -- fix qr quoting - -1.19.0 -- restore ids -- thread updater - -1.18.4 -- fix qr quoting of selected text - -1.18.3 -- fix w / i / I keybinds -- git rid of body padding - -1.18.2 -- logic fail - -1.18.1 -- only adding padding when not in replies - -1.18.0 -- body { padding-bottom } when Thread Navigation -- keybinds - - insert mode - ^s - quick spoiler - escape - remove quick reply - - normal mode - h/l - scroll left/right, H/L - move pages - m/M - expand selected / all images - w - watch thread (changed from m) - -1.17.5 -- fix threading -- fix images - -1.17.4 -- yeah i guess margin is cool too - -1.17.3 -- a img { float: left; } - -1.17.2 -- expand images in new posts - -1.17.1 -- don't affect image clicks when ctrl | alt | shift held -- add .hide class so thumbnail hiding works - -1.17.0 -- image expansion - -1.16.0 -- I - open blank quick reply - -1.15.0 -- add archive at 173.74.0.45 - -1.14.1 -- fix thread nav - -1.14.0 -- reply keybinds: i, J, K - -1.13.0 -- localize time - -1.12.1 -- actually disable keybinds by default - -1.12.0 -- disable keybinds by default -- qr to replies -- J/K - select next / previous reply -- smarter scrolling - -1.11.1 -- fix reply mode switching (bringing up quick reply now correctly disables -keybinds) - -1.11.0 -- reply keybinds -- repeatable keybinds -- change keybinds: - - o = open thread in new tab - - i = enter insert mode and open quick reply - -1.10.1 -- fix kb thread nav -- hack to focus qr -- absolute positioning for `g` keybind - -1.10.0 -- keyboard actions: - - m to _mark (watch) threads - - t to open threads in _tabs - - o to _open quick reply -- better mode switching -- turn on keyboard actions - -1.9.1 -- add `count` for keynav -- disable keynav when quick replying -- make keynav disabled by default - -1.9.0 -- add keyboard navigation - -1.8.3 -- try to fix remaining bugs by making global variables explicitly global - -1.8.2 -- fix chromium - -1.8.1 -- fix bugs in 1.8.0 - -1.8.0 -- add backspace recaptcha refreshing to reports page - -1.7.4 -- fix thread watching - -1.7.3 -- fixed descriptions - -1.7.2 -- add option descriptions - -1.7.0 -- add auto in qr - -1.6.0 -- add cooldown timer - -1.5.0 -1.4.4 -- mirror options button on bottom of page -- add 4chan sauce -- add 404 redirect -- add post in title -- reload captcha on backspace - -1.4.3 -- clear captcha on submit - -1.4.2 -- fix for firefox 4.0b6 - -1.4.1 -- unhide qr when reply link clicked -- clear textarea on successful qr -- reload captcha AFTER removing qr - -1.4 -- add 'Persistent QR' -- QR autohiding - -1.3 -- auto reload captcha on success - -1.2 -- alert on no captcha - -1.1 -- re-add button to clear hidden posts. - -1.0 -- Complete rewrite (well, like 98%). -- Split off updater - 4chan x Updater - -0.26 -- Fix /b/ quick reply -- Add initial 'Auto Watch' implementation - -0.25 -- fix Opera 10.50 thread updater bug -- require Opera 10.50 -- remove 'Quick Post' (just use noko) -- remove / automate 'manual clear' -- make thread stubs clickable - -0.24 - Worksafe threads show the correct favicon. Thanks for the art, Ongpot! - -0.23 - Show post count for hidden threads - -0.22 - thread navigating now can navigate to previous pages, fixed opera issue - -0.21 - chrome compatibility - -0.20 - added selection quoting, quick post, unread reply count in title. - -0.19 - added quick reply error notifications - -0.17 - added 'Update Favicon' option. Thanks thisisanon! - -0.16 - added new reply notifications. scroll to end of thread to remove them. - -0.15 - added thread updater button - -0.14 - moved reply hiding to 4chan filter - -0.13 - added thread watcher close button, thread watcher toggle button - -0.12 - added Show Hidden (NOTE: not fully compatible w/ 4chan filter yet); thread updater works through connection problems - -0.11 - made hidden replies persistent, added Clear Hidden. - -0.10 - report button added - -0.9 - forced anon added - -0.8 - all watched threads visible all the time - -0.7 - added reply nav buttons - -0.6 - added thread updating - -0.4.1/0.5 - added options, quick reply shading - -0.4 - added quick reply - -0.3 - added thread watching - -0.2 - added post expanding - -0.1 - added post hiding - -0.0 - thread hiding, expanding, navigating From d583c74049de491a0ee3957bbfd2546ab1896cd7 Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Mon, 22 Jul 2013 11:48:20 +0200 Subject: [PATCH 074/172] Add the error name and error message in Firefox error reports. Chrome gives the error name and error message in the stack trace already. Also, no need to check `if stack` since we already filter them. --- src/General/Main.coffee | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/General/Main.coffee b/src/General/Main.coffee index 09fe39241..197e7aa50 100644 --- a/src/General/Main.coffee +++ b/src/General/Main.coffee @@ -332,16 +332,18 @@ Main = postErrors: -> return if Main.v2Detected errors = Main.errors.filter((d) -> !!d.error.stack).map((d) -> - {stack} = d.error <% if (type === 'userscript') { %> # Before: # someFn@file:///C:/Users/<USER>/AppData/Roaming/Mozilla/Firefox/Profiles/<garbage>.default/gm_scripts/4chan_X/4chan-X.user.js:line_number # someFn@file:///home/<USER>/.mozilla/firefox/<garbage>.default/gm_scripts/4chan_X/4chan-X.user.js:line_number # After: # someFn@4chan-X.user.js:line_number - stack = stack.replace /file:\/{3}.+\//g, '' if stack + {name, message, stack} = d.error + stack = stack.replace /file:\/{3}.+\//g, '' + "#{d.message} #{name}: #{message} #{stack}" + <% } else { %> + "#{d.message} #{d.error.stack}" <% } %> - "#{d.message} #{stack}" ).join '\n' return unless errors $.ajax '<%= meta.page %>errors', {}, From b628cd7ebae34e1b62793730afce20eb38a1486b Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Mon, 22 Jul 2013 15:06:44 +0200 Subject: [PATCH 075/172] Faster Post::parseQuotes What changed is about 3~4 times faster now. Resulting code is also less complex. --- src/General/Post.coffee | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/General/Post.coffee b/src/General/Post.coffee index e78c6ac4f..0f486e997 100644 --- a/src/General/Post.coffee +++ b/src/General/Post.coffee @@ -78,25 +78,26 @@ class Post parseQuotes: -> quotes = {} for quotelink in $$ '.quotelink', @nodes.comment - # Don't add board links. (>>>/b/) - {hash} = quotelink - continue unless hash - - # Don't add catalog links. (>>>/b/catalog or >>>/b/search) - {pathname} = quotelink - continue if /catalog$/.test pathname - - # Don't add rules links. (>>>/a/rules) - # Don't add text-board quotelinks. (>>>/img/1234) - continue if quotelink.hostname isnt 'boards.4chan.org' + # Only add quotes that link to posts on an imageboard. + # Don't add: + # - board links. (>>>/b/) + # - catalog links. (>>>/b/catalog or >>>/b/search) + # - rules links. (>>>/a/rules) + # - text-board quotelinks. (>>>/img/1234) + continue unless match = quotelink.href.match /// + boards\.4chan\.org/ + ([^/]+) # boardID + /res/\d+#p + (\d+) # postID + $ + /// @nodes.quotelinks.push quotelink # Don't count capcode replies as quotes in OPs. (Admin/Mod/Dev Replies: ...) continue if !@isReply and $.hasClass quotelink.parentNode.parentNode, 'capcodeReplies' - # Basically, only add quotes that link to posts on an imageboard. - quotes["#{pathname.split('/')[1]}.#{hash[2..]}"] = true + quotes["#{match[1]}.#{match[2]}"] = true return if @isClone @quotes = Object.keys quotes From a344ef4afa6853ec3e111dd12c7e7af0af440115 Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Mon, 22 Jul 2013 15:31:23 +0200 Subject: [PATCH 076/172] Faster Post::parseQuotes What changed is 4~6 times faster. --- src/General/Post.coffee | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/General/Post.coffee b/src/General/Post.coffee index 0f486e997..1d94ec6bd 100644 --- a/src/General/Post.coffee +++ b/src/General/Post.coffee @@ -76,7 +76,7 @@ class Post @info.comment = text.join('').trim().replace /\s+$/gm, '' parseQuotes: -> - quotes = {} + @quotes = [] for quotelink in $$ '.quotelink', @nodes.comment # Only add quotes that link to posts on an imageboard. # Don't add: @@ -95,11 +95,12 @@ class Post @nodes.quotelinks.push quotelink # Don't count capcode replies as quotes in OPs. (Admin/Mod/Dev Replies: ...) - continue if !@isReply and $.hasClass quotelink.parentNode.parentNode, 'capcodeReplies' - - quotes["#{match[1]}.#{match[2]}"] = true - return if @isClone - @quotes = Object.keys quotes + continue if @isClone or !@isReply and $.hasClass quotelink.parentNode.parentNode, 'capcodeReplies' + + # ES6 Set when? + fullID = "#{match[1]}.#{match[2]}" + @quotes.push fullID if @quotes.indexOf(fullID) is -1 + return parseFile: (that) -> return unless (fileEl = $ '.file', @nodes.post) and thumb = $ 'img[data-md5]', fileEl From d0dac27ca713462b7a1568e40076d0007fcca0d8 Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Mon, 22 Jul 2013 15:37:25 +0200 Subject: [PATCH 077/172] Factor Post::parseQuote out of Post::parseQuotes --- src/General/Post.coffee | 47 ++++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/src/General/Post.coffee b/src/General/Post.coffee index 1d94ec6bd..7faf5f343 100644 --- a/src/General/Post.coffee +++ b/src/General/Post.coffee @@ -78,29 +78,32 @@ class Post parseQuotes: -> @quotes = [] for quotelink in $$ '.quotelink', @nodes.comment - # Only add quotes that link to posts on an imageboard. - # Don't add: - # - board links. (>>>/b/) - # - catalog links. (>>>/b/catalog or >>>/b/search) - # - rules links. (>>>/a/rules) - # - text-board quotelinks. (>>>/img/1234) - continue unless match = quotelink.href.match /// - boards\.4chan\.org/ - ([^/]+) # boardID - /res/\d+#p - (\d+) # postID - $ - /// - - @nodes.quotelinks.push quotelink - - # Don't count capcode replies as quotes in OPs. (Admin/Mod/Dev Replies: ...) - continue if @isClone or !@isReply and $.hasClass quotelink.parentNode.parentNode, 'capcodeReplies' - - # ES6 Set when? - fullID = "#{match[1]}.#{match[2]}" - @quotes.push fullID if @quotes.indexOf(fullID) is -1 + @parseQuote quotelink return + + parseQuote: (quotelink) -> + # Only add quotes that link to posts on an imageboard. + # Don't add: + # - board links. (>>>/b/) + # - catalog links. (>>>/b/catalog or >>>/b/search) + # - rules links. (>>>/a/rules) + # - text-board quotelinks. (>>>/img/1234) + return unless match = quotelink.href.match /// + boards\.4chan\.org/ + ([^/]+) # boardID + /res/\d+#p + (\d+) # postID + $ + /// + + @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' + + # ES6 Set when? + fullID = "#{match[1]}.#{match[2]}" + @quotes.push fullID if @quotes.indexOf(fullID) is -1 parseFile: (that) -> return unless (fileEl = $ '.file', @nodes.post) and thumb = $ 'img[data-md5]', fileEl From 282f0b8639a47044cfbbf70890c3a820f4c906a6 Mon Sep 17 00:00:00 2001 From: Enzo Moretti <woxxy@foolz.us> Date: Mon, 22 Jul 2013 22:29:45 +0200 Subject: [PATCH 078/172] Readding /v/ to Foolz Archive --- json/archives.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/json/archives.json b/json/archives.json index 59efbf14a..8fd76aebf 100644 --- a/json/archives.json +++ b/json/archives.json @@ -5,7 +5,7 @@ "http": true, "https": true, "software": "foolfuuka", - "boards": ["a", "co", "gd", "jp", "m", "q", "sp", "tg", "tv", "vg", "vp", "vr", "wsg"], + "boards": ["a", "co", "gd", "jp", "m", "q", "sp", "tg", "tv", "v", "vg", "vp", "vr", "wsg"], "files": ["a", "gd", "jp", "m", "q", "tg", "vg", "vp", "vr", "wsg"] }, { "uid": 1, From dd4d979276e91fc0fc6fc13f4852c3e66c153173 Mon Sep 17 00:00:00 2001 From: Zixaphir <zixaphirmoxphar@gmail.com> Date: Wed, 24 Jul 2013 22:01:48 -0700 Subject: [PATCH 079/172] Still being different for the sake of being difficult --- builds/4chan-X.user.js | 2 +- builds/crx/script.js | 2 +- src/General/lib/post.class | 7 +++---- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js index 055d303ea..2e2ebc176 100644 --- a/builds/4chan-X.user.js +++ b/builds/4chan-X.user.js @@ -1005,7 +1005,7 @@ return; } fullID = "" + match[1] + "." + match[2]; - if (this.quotes.indexOf(fullID) === -1) { + if (!this.quotes.contains(fullID)) { return this.quotes.push(fullID); } }; diff --git a/builds/crx/script.js b/builds/crx/script.js index 16b927adf..a3911f014 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -1011,7 +1011,7 @@ return; } fullID = "" + match[1] + "." + match[2]; - if (this.quotes.indexOf(fullID) === -1) { + if (!this.quotes.contains(fullID)) { return this.quotes.push(fullID); } }; diff --git a/src/General/lib/post.class b/src/General/lib/post.class index a90cf6ce9..57fdddcf2 100644 --- a/src/General/lib/post.class +++ b/src/General/lib/post.class @@ -52,7 +52,6 @@ class Post threadID: @thread postID: @ID - @parseComment() @parseQuotes() @parseFile(that) @@ -87,7 +86,7 @@ class Post for quotelink in $$ '.quotelink', @nodes.comment @parseQuote quotelink return - + parseQuote: (quotelink) -> # Only add quotes that link to posts on an imageboard. # Don't add: @@ -107,10 +106,10 @@ class Post # Don't count capcode replies as quotes in OPs. (Admin/Mod/Dev Replies: ...) return if @isClone or !@isReply and $.hasClass quotelink.parentNode.parentNode, 'capcodeReplies' - + # ES6 Set when? fullID = "#{match[1]}.#{match[2]}" - @quotes.push fullID if @quotes.indexOf(fullID) is -1 + @quotes.push fullID unless @quotes.contains fullID parseFile: (that) -> return unless (fileEl = $ '.file', @nodes.post) and thumb = $ 'img[data-md5]', fileEl From d01c811fe2d66a3a0d3ddf564cf52145651230f4 Mon Sep 17 00:00:00 2001 From: Zixaphir <zixaphirmoxphar@gmail.com> Date: Wed, 24 Jul 2013 22:06:08 -0700 Subject: [PATCH 080/172] Another indexOf I missed. --- builds/4chan-X.user.js | 2 +- builds/crx/script.js | 2 +- src/Miscellaneous/Report.coffee | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js index 2e2ebc176..c443d26fa 100644 --- a/builds/4chan-X.user.js +++ b/builds/4chan-X.user.js @@ -9364,7 +9364,7 @@ Report = { init: function() { - if (!(/report/.test(location.search) && d.cookie.indexOf('pass_enabled=1') === -1)) { + if (!(/report/.test(location.search) && !d.cookie.contains('pass_enabled=1'))) { return; } return $.asap((function() { diff --git a/builds/crx/script.js b/builds/crx/script.js index a3911f014..2bba076ae 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -9345,7 +9345,7 @@ Report = { init: function() { - if (!(/report/.test(location.search) && d.cookie.indexOf('pass_enabled=1') === -1)) { + if (!(/report/.test(location.search) && !d.cookie.contains('pass_enabled=1'))) { return; } return $.asap((function() { diff --git a/src/Miscellaneous/Report.coffee b/src/Miscellaneous/Report.coffee index af505998d..f0782aab5 100644 --- a/src/Miscellaneous/Report.coffee +++ b/src/Miscellaneous/Report.coffee @@ -1,6 +1,6 @@ Report = init: -> - return unless /report/.test(location.search) and d.cookie.indexOf('pass_enabled=1') is -1 + return unless /report/.test(location.search) and not d.cookie.contains 'pass_enabled=1' $.asap (-> $.id 'recaptcha_response_field'), Report.ready ready: -> field = $.id 'recaptcha_response_field' From 22fde6fdb05ca17393477d79c1134e61c8471f53 Mon Sep 17 00:00:00 2001 From: Zixaphir <zixaphirmoxphar@gmail.com> Date: Thu, 25 Jul 2013 14:51:30 -0700 Subject: [PATCH 081/172] Fix quotelinks inside of resurrected posts @MayhemYDG --- LICENSE | 2 +- builds/4chan-X.user.js | 6 +++--- builds/crx/script.js | 6 +++--- src/Quotelinks/Quotify.coffee | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/LICENSE b/LICENSE index 8758af562..bc43a4ec5 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,5 @@ /* -* 4chan X - Version 1.2.24 - 2013-07-24 +* 4chan X - Version 1.2.24 - 2013-07-25 * * Licensed under the MIT license. * https://github.com/seaweedchan/4chan-x/blob/master/LICENSE diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js index c443d26fa..760591baa 100644 --- a/builds/4chan-X.user.js +++ b/builds/4chan-X.user.js @@ -19,7 +19,7 @@ // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwAgMAAAAqbBEUAAAACVBMVEUAAGcAAABmzDNZt9VtAAAAAXRSTlMAQObYZgAAAHFJREFUKFOt0LENACEIBdBv4Qju4wgWanEj3D6OcIVMKaitYHEU/jwTCQj8W75kiVCSBvdQ5/AvfVHBin11BgdRq3ysBgfwBDRrj3MCIA+oAQaku/Q1cNctrAmyDl577tOThYt/Y1RBM4DgOHzM0HFTAyLukH/cmRnqAAAAAElFTkSuQmCC // ==/UserScript== /* -* 4chan X - Version 1.2.24 - 2013-07-24 +* 4chan X - Version 1.2.24 - 2013-07-25 * * Licensed under the MIT license. * https://github.com/seaweedchan/4chan-x/blob/master/LICENSE @@ -4245,13 +4245,13 @@ if (post = g.posts[quoteID]) { if (!post.isDead) { a = $.el('a', { - href: "/" + boardID + "/" + post.thread + "/res/#p" + postID, + href: "/" + boardID + "/res/" + post.thread + "#p" + postID, className: 'quotelink', textContent: quote }); } else { 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)" diff --git a/builds/crx/script.js b/builds/crx/script.js index 2bba076ae..11d727ab7 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -1,6 +1,6 @@ // Generated by CoffeeScript /* -* 4chan X - Version 1.2.24 - 2013-07-24 +* 4chan X - Version 1.2.24 - 2013-07-25 * * Licensed under the MIT license. * https://github.com/seaweedchan/4chan-x/blob/master/LICENSE @@ -4245,13 +4245,13 @@ if (post = g.posts[quoteID]) { if (!post.isDead) { a = $.el('a', { - href: "/" + boardID + "/" + post.thread + "/res/#p" + postID, + href: "/" + boardID + "/res/" + post.thread + "#p" + postID, className: 'quotelink', textContent: quote }); } else { 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)" diff --git a/src/Quotelinks/Quotify.coffee b/src/Quotelinks/Quotify.coffee index 524f74f6c..2b20e4dcc 100644 --- a/src/Quotelinks/Quotify.coffee +++ b/src/Quotelinks/Quotify.coffee @@ -40,13 +40,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 5cd7e0c5e05e26827c325fe3574b9e9ef2bf1250 Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Sun, 28 Jul 2013 19:53:41 +0200 Subject: [PATCH 082/172] Whitespace fixes. --- .gitattributes | 2 +- src/General/Post.coffee | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitattributes b/.gitattributes index 212566614..176a458f9 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1 @@ -* text=auto \ No newline at end of file +* text=auto diff --git a/src/General/Post.coffee b/src/General/Post.coffee index 7faf5f343..6da7f33dc 100644 --- a/src/General/Post.coffee +++ b/src/General/Post.coffee @@ -80,7 +80,7 @@ class Post for quotelink in $$ '.quotelink', @nodes.comment @parseQuote quotelink return - + parseQuote: (quotelink) -> # Only add quotes that link to posts on an imageboard. # Don't add: @@ -100,7 +100,7 @@ class Post # Don't count capcode replies as quotes in OPs. (Admin/Mod/Dev Replies: ...) return if @isClone or !@isReply and $.hasClass quotelink.parentNode.parentNode, 'capcodeReplies' - + # ES6 Set when? fullID = "#{match[1]}.#{match[2]}" @quotes.push fullID if @quotes.indexOf(fullID) is -1 From 392d6e255ebdccef9ba273e2a5fe70cae2991366 Mon Sep 17 00:00:00 2001 From: Zixaphir <zixaphirmoxphar@gmail.com> Date: Mon, 29 Jul 2013 11:36:07 -0700 Subject: [PATCH 083/172] Reduce loops in Redirect.init --- LICENSE | 2 +- builds/4chan-X.user.js | 56 ++++++++++++++----------------------- builds/crx/script.js | 56 ++++++++++++++----------------------- src/Archive/Redirect.coffee | 12 ++++---- 4 files changed, 48 insertions(+), 78 deletions(-) diff --git a/LICENSE b/LICENSE index bc43a4ec5..258cc05ce 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,5 @@ /* -* 4chan X - Version 1.2.24 - 2013-07-25 +* 4chan X - Version 1.2.24 - 2013-07-29 * * Licensed under the MIT license. * https://github.com/seaweedchan/4chan-x/blob/master/LICENSE diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js index 760591baa..8711b89f2 100644 --- a/builds/4chan-X.user.js +++ b/builds/4chan-X.user.js @@ -19,7 +19,7 @@ // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwAgMAAAAqbBEUAAAACVBMVEUAAGcAAABmzDNZt9VtAAAAAXRSTlMAQObYZgAAAHFJREFUKFOt0LENACEIBdBv4Qju4wgWanEj3D6OcIVMKaitYHEU/jwTCQj8W75kiVCSBvdQ5/AvfVHBin11BgdRq3ysBgfwBDRrj3MCIA+oAQaku/Q1cNctrAmyDl577tOThYt/Y1RBM4DgOHzM0HFTAyLukH/cmRnqAAAAAElFTkSuQmCC // ==/UserScript== /* -* 4chan X - Version 1.2.24 - 2013-07-25 +* 4chan X - Version 1.2.24 - 2013-07-29 * * Licensed under the MIT license. * https://github.com/seaweedchan/4chan-x/blob/master/LICENSE @@ -7945,53 +7945,39 @@ post: {}, file: {}, init: function() { - var archive, arr, boardID, data, id, name, type, _ref, _ref1, _ref2, _results; + var archive, boardID, boards, data, id, name, type, _i, _len, _ref, _ref1, _ref2; _ref = Conf['selectedArchives']; for (boardID in _ref) { data = _ref[boardID]; for (type in data) { id = data[type]; - _ref1 = Redirect.archives; - for (name in _ref1) { - archive = _ref1[name]; - if (name !== id || type === 'post' && archive.software !== 'foolfuuka') { + if (archive = Redirect.archives[id]) { + boards = archive[type] || archive['boards']; + if (!boards.contains(boardID)) { continue; } - arr = type === 'file' ? archive.files : archive.boards; - if (arr.contains(boardID)) { - Redirect[type][boardID] = archive; - } + Redirect[type][boardID] = archive; } } } - _ref2 = Redirect.archives; - _results = []; - for (name in _ref2) { - archive = _ref2[name]; - _results.push((function() { - var _i, _len, _ref3, _results1; - - _ref3 = archive.boards; - _results1 = []; - for (_i = 0, _len = _ref3.length; _i < _len; _i++) { - boardID = _ref3[_i]; - if (!(boardID in Redirect.thread)) { - Redirect.thread[boardID] = archive; - } - if (!(boardID in Redirect.post || archive.software !== 'foolfuuka')) { - Redirect.post[boardID] = archive; - } - if (!(boardID in Redirect.file || !archive.files.contains(boardID))) { - _results1.push(Redirect.file[boardID] = archive); - } else { - _results1.push(void 0); - } + _ref1 = Redirect.archives; + for (name in _ref1) { + archive = _ref1[name]; + _ref2 = archive.boards; + for (_i = 0, _len = _ref2.length; _i < _len; _i++) { + boardID = _ref2[_i]; + if (!(boardID in Redirect.thread)) { + Redirect.thread[boardID] = archive; } - return _results1; - })()); + if (!(boardID in Redirect.post || archive.software !== 'foolfuuka')) { + Redirect.post[boardID] = archive; + } + if (!(boardID in Redirect.file || !archive.files.contains(boardID))) { + Redirect.file[boardID] = archive; + } + } } - return _results; }, archives: { 'Foolz': { diff --git a/builds/crx/script.js b/builds/crx/script.js index 11d727ab7..a22e8b5d6 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -1,6 +1,6 @@ // Generated by CoffeeScript /* -* 4chan X - Version 1.2.24 - 2013-07-25 +* 4chan X - Version 1.2.24 - 2013-07-29 * * Licensed under the MIT license. * https://github.com/seaweedchan/4chan-x/blob/master/LICENSE @@ -7926,53 +7926,39 @@ post: {}, file: {}, init: function() { - var archive, arr, boardID, data, id, name, type, _ref, _ref1, _ref2, _results; + var archive, boardID, boards, data, id, name, type, _i, _len, _ref, _ref1, _ref2; _ref = Conf['selectedArchives']; for (boardID in _ref) { data = _ref[boardID]; for (type in data) { id = data[type]; - _ref1 = Redirect.archives; - for (name in _ref1) { - archive = _ref1[name]; - if (name !== id || type === 'post' && archive.software !== 'foolfuuka') { + if (archive = Redirect.archives[id]) { + boards = archive[type] || archive['boards']; + if (!boards.contains(boardID)) { continue; } - arr = type === 'file' ? archive.files : archive.boards; - if (arr.contains(boardID)) { - Redirect[type][boardID] = archive; - } + Redirect[type][boardID] = archive; } } } - _ref2 = Redirect.archives; - _results = []; - for (name in _ref2) { - archive = _ref2[name]; - _results.push((function() { - var _i, _len, _ref3, _results1; - - _ref3 = archive.boards; - _results1 = []; - for (_i = 0, _len = _ref3.length; _i < _len; _i++) { - boardID = _ref3[_i]; - if (!(boardID in Redirect.thread)) { - Redirect.thread[boardID] = archive; - } - if (!(boardID in Redirect.post || archive.software !== 'foolfuuka')) { - Redirect.post[boardID] = archive; - } - if (!(boardID in Redirect.file || !archive.files.contains(boardID))) { - _results1.push(Redirect.file[boardID] = archive); - } else { - _results1.push(void 0); - } + _ref1 = Redirect.archives; + for (name in _ref1) { + archive = _ref1[name]; + _ref2 = archive.boards; + for (_i = 0, _len = _ref2.length; _i < _len; _i++) { + boardID = _ref2[_i]; + if (!(boardID in Redirect.thread)) { + Redirect.thread[boardID] = archive; } - return _results1; - })()); + if (!(boardID in Redirect.post || archive.software !== 'foolfuuka')) { + Redirect.post[boardID] = archive; + } + if (!(boardID in Redirect.file || !archive.files.contains(boardID))) { + Redirect.file[boardID] = archive; + } + } } - return _results; }, archives: { 'Foolz': { diff --git a/src/Archive/Redirect.coffee b/src/Archive/Redirect.coffee index 8fc00eafc..57de06ba7 100644 --- a/src/Archive/Redirect.coffee +++ b/src/Archive/Redirect.coffee @@ -6,13 +6,10 @@ Redirect = init: -> for boardID, data of Conf['selectedArchives'] for type, id of data - for name, archive of Redirect.archives - continue if name isnt id or type is 'post' and archive.software isnt 'foolfuuka' - arr = if type is 'file' - archive.files - else - archive.boards - Redirect[type][boardID] = archive if arr.contains boardID + if archive = Redirect.archives[id] + boards = archive[type] or archive['boards'] + continue unless boards.contains boardID + Redirect[type][boardID] = archive for name, archive of Redirect.archives for boardID in archive.boards unless boardID of Redirect.thread @@ -21,6 +18,7 @@ Redirect = Redirect.post[boardID] = archive unless boardID of Redirect.file or !archive.files.contains boardID Redirect.file[boardID] = archive + return archives: 'Foolz': From 6ee8dc83a7f036dda627e1998a18eaa59c749103 Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Tue, 30 Jul 2013 15:13:33 +0200 Subject: [PATCH 084/172] Fix #1210 We were inserting the same Post twice in QR.posts, once in the constructor, and once in the splice. --- 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 8319392a2..c77bdda1e 100644 --- a/src/Posting/QR.coffee +++ b/src/Posting/QR.coffee @@ -74,7 +74,8 @@ QR = QR.cleanNotifications() d.activeElement.blur() $.rmClass QR.nodes.el, 'dump' - for post in QR.posts.splice 0, QR.posts.length, new QR.post true + new QR.post true + for post in QR.posts.splice 0, QR.posts.length - 1 post.delete() QR.cooldown.auto = false QR.status() From 7064d0b54e27f9c64706c6253914a0fc0efcedf6 Mon Sep 17 00:00:00 2001 From: Zixaphir <zixaphirmoxphar@gmail.com> Date: Wed, 31 Jul 2013 17:26:31 -0700 Subject: [PATCH 085/172] Fix QuoteThreading toggle. --- LICENSE | 2 +- builds/4chan-X.user.js | 35 ++++++++++++++-------------- builds/crx/script.js | 35 ++++++++++++++-------------- src/Quotelinks/QuoteThreading.coffee | 22 +++++++++-------- 4 files changed, 47 insertions(+), 47 deletions(-) diff --git a/LICENSE b/LICENSE index 258cc05ce..041018353 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,5 @@ /* -* 4chan X - Version 1.2.24 - 2013-07-29 +* 4chan X - Version 1.2.24 - 2013-07-31 * * Licensed under the MIT license. * https://github.com/seaweedchan/4chan-x/blob/master/LICENSE diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js index 8711b89f2..76df67523 100644 --- a/builds/4chan-X.user.js +++ b/builds/4chan-X.user.js @@ -19,7 +19,7 @@ // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwAgMAAAAqbBEUAAAACVBMVEUAAGcAAABmzDNZt9VtAAAAAXRSTlMAQObYZgAAAHFJREFUKFOt0LENACEIBdBv4Qju4wgWanEj3D6OcIVMKaitYHEU/jwTCQj8W75kiVCSBvdQ5/AvfVHBin11BgdRq3ysBgfwBDRrj3MCIA+oAQaku/Q1cNctrAmyDl577tOThYt/Y1RBM4DgOHzM0HFTAyLukH/cmRnqAAAAAElFTkSuQmCC // ==/UserScript== /* -* 4chan X - Version 1.2.24 - 2013-07-29 +* 4chan X - Version 1.2.24 - 2013-07-31 * * Licensed under the MIT license. * https://github.com/seaweedchan/4chan-x/blob/master/LICENSE @@ -4084,10 +4084,9 @@ return this.cb = QuoteThreading.nodeinsert; }, nodeinsert: function() { - var bottom, height, posts, qpost, qroot, threadContainer, top, _ref; + var bottom, height, qpost, qroot, threadContainer, top, _ref; - posts = g.posts; - qpost = posts[this.threaded]; + qpost = g.posts[this.threaded]; delete this.threaded; delete this.cb; if (this.thread.OP === qpost) { @@ -4114,26 +4113,21 @@ return true; }, toggle: function() { - var container, containers, node, nodes, replies, reply, thread, _i, _j, _len, _len1; + var container, containers, node, post, replies, reply, thread, _i, _j, _k, _len, _len1, _len2, _ref; thread = $('.thread'); replies = $$('.thread > .replyContainer, .threadContainer > .replyContainer', thread); QuoteThreading.enabled = this.checked; if (this.checked) { - nodes = (function() { - var _i, _len, _results; - - _results = []; - for (_i = 0, _len = replies.length; _i < _len; _i++) { - reply = replies[_i]; - _results.push(Get.postFromNode(reply)); + QuoteThreading.hasRun = false; + for (_i = 0, _len = replies.length; _i < _len; _i++) { + reply = replies[_i]; + QuoteThreading.node.call(node = Get.postFromRoot(reply)); + if (node.cb) { + node.cb(); } - return _results; - })(); - for (_i = 0, _len = nodes.length; _i < _len; _i++) { - node = nodes[_i]; - QuoteThreading.node(node); } + QuoteThreading.hasRun = true; } else { replies.sort(function(a, b) { var aID, bID; @@ -4148,8 +4142,13 @@ container = containers[_j]; $.rm(container); } - Unread.update(true); + _ref = $$('.threadOP'); + for (_k = 0, _len2 = _ref.length; _k < _len2; _k++) { + post = _ref[_k]; + $.rmClass(post, 'threadOP'); + } } + return Unread.update(true); }, kb: function() { var control; diff --git a/builds/crx/script.js b/builds/crx/script.js index a22e8b5d6..13578f64b 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -1,6 +1,6 @@ // Generated by CoffeeScript /* -* 4chan X - Version 1.2.24 - 2013-07-29 +* 4chan X - Version 1.2.24 - 2013-07-31 * * Licensed under the MIT license. * https://github.com/seaweedchan/4chan-x/blob/master/LICENSE @@ -4084,10 +4084,9 @@ return this.cb = QuoteThreading.nodeinsert; }, nodeinsert: function() { - var bottom, height, posts, qpost, qroot, threadContainer, top, _ref; + var bottom, height, qpost, qroot, threadContainer, top, _ref; - posts = g.posts; - qpost = posts[this.threaded]; + qpost = g.posts[this.threaded]; delete this.threaded; delete this.cb; if (this.thread.OP === qpost) { @@ -4114,26 +4113,21 @@ return true; }, toggle: function() { - var container, containers, node, nodes, replies, reply, thread, _i, _j, _len, _len1; + var container, containers, node, post, replies, reply, thread, _i, _j, _k, _len, _len1, _len2, _ref; thread = $('.thread'); replies = $$('.thread > .replyContainer, .threadContainer > .replyContainer', thread); QuoteThreading.enabled = this.checked; if (this.checked) { - nodes = (function() { - var _i, _len, _results; - - _results = []; - for (_i = 0, _len = replies.length; _i < _len; _i++) { - reply = replies[_i]; - _results.push(Get.postFromNode(reply)); + QuoteThreading.hasRun = false; + for (_i = 0, _len = replies.length; _i < _len; _i++) { + reply = replies[_i]; + QuoteThreading.node.call(node = Get.postFromRoot(reply)); + if (node.cb) { + node.cb(); } - return _results; - })(); - for (_i = 0, _len = nodes.length; _i < _len; _i++) { - node = nodes[_i]; - QuoteThreading.node(node); } + QuoteThreading.hasRun = true; } else { replies.sort(function(a, b) { var aID, bID; @@ -4148,8 +4142,13 @@ container = containers[_j]; $.rm(container); } - Unread.update(true); + _ref = $$('.threadOP'); + for (_k = 0, _len2 = _ref.length; _k < _len2; _k++) { + post = _ref[_k]; + $.rmClass(post, 'threadOP'); + } } + return Unread.update(true); }, kb: function() { var control; diff --git a/src/Quotelinks/QuoteThreading.coffee b/src/Quotelinks/QuoteThreading.coffee index a03406bb9..f977a82a3 100644 --- a/src/Quotelinks/QuoteThreading.coffee +++ b/src/Quotelinks/QuoteThreading.coffee @@ -56,14 +56,13 @@ QuoteThreading = @cb = QuoteThreading.nodeinsert nodeinsert: -> - {posts} = g - qpost = posts[@threaded] + qpost = g.posts[@threaded] delete @threaded delete @cb - return false if @thread.OP is qpost - + return false if @thread.OP is qpost + if QuoteThreading.hasRun height = doc.clientHeight {bottom, top} = qpost.nodes.root.getBoundingClientRect() @@ -88,8 +87,11 @@ QuoteThreading = replies = $$ '.thread > .replyContainer, .threadContainer > .replyContainer', thread QuoteThreading.enabled = @checked if @checked - nodes = (Get.postFromNode reply for reply in replies) - QuoteThreading.node node for node in nodes + QuoteThreading.hasRun = false + for reply in replies + QuoteThreading.node.call node = Get.postFromRoot reply + node.cb() if node.cb + QuoteThreading.hasRun = true else replies.sort (a, b) -> aID = Number a.id[2..] @@ -98,9 +100,9 @@ QuoteThreading = $.add thread, replies containers = $$ '.threadContainer', thread $.rm container for container in containers - Unread.update true - return + $.rmClass post, 'threadOP' for post in $$ '.threadOP' + Unread.update true kb: -> - control = $.id 'threadingControl' - control.click() \ No newline at end of file + control = $.id 'threadingControl' + control.click() \ No newline at end of file From 093fd544137648cc709f7700cfd69f39b128e9e6 Mon Sep 17 00:00:00 2001 From: Zixaphir <zixaphirmoxphar@gmail.com> Date: Thu, 1 Aug 2013 10:26:06 -0700 Subject: [PATCH 086/172] Fix $.open Should fix the issue with opening things in new tabs. Yes, I'm dumb --- LICENSE | 2 +- builds/4chan-X.user.js | 6 ++---- builds/crx/script.js | 2 +- src/General/lib/$.coffee | 2 +- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/LICENSE b/LICENSE index 041018353..97bce1cc7 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,5 @@ /* -* 4chan X - Version 1.2.24 - 2013-07-31 +* 4chan X - Version 1.2.24 - 2013-08-01 * * Licensed under the MIT license. * https://github.com/seaweedchan/4chan-x/blob/master/LICENSE diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js index 1dd030857..909c75b3f 100644 --- a/builds/4chan-X.user.js +++ b/builds/4chan-X.user.js @@ -19,7 +19,7 @@ // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwAgMAAAAqbBEUAAAACVBMVEUAAGcAAABmzDNZt9VtAAAAAXRSTlMAQObYZgAAAHFJREFUKFOt0LENACEIBdBv4Qju4wgWanEj3D6OcIVMKaitYHEU/jwTCQj8W75kiVCSBvdQ5/AvfVHBin11BgdRq3ysBgfwBDRrj3MCIA+oAQaku/Q1cNctrAmyDl577tOThYt/Y1RBM4DgOHzM0HFTAyLukH/cmRnqAAAAAElFTkSuQmCC // ==/UserScript== /* -* 4chan X - Version 1.2.24 - 2013-07-31 +* 4chan X - Version 1.2.24 - 2013-08-01 * * Licensed under the MIT license. * https://github.com/seaweedchan/4chan-x/blob/master/LICENSE @@ -675,9 +675,7 @@ }; $.open = function(URL) { - return $.open = function(URL) { - return GM_openInTab(URL); - }; + return GM_openInTab(URL); }; $.debounce = function(wait, fn) { diff --git a/builds/crx/script.js b/builds/crx/script.js index 0b06b2da7..b56ccc5f4 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -1,6 +1,6 @@ // Generated by CoffeeScript /* -* 4chan X - Version 1.2.24 - 2013-07-31 +* 4chan X - Version 1.2.24 - 2013-08-01 * * Licensed under the MIT license. * https://github.com/seaweedchan/4chan-x/blob/master/LICENSE diff --git a/src/General/lib/$.coffee b/src/General/lib/$.coffee index c9d50c4ff..a04926155 100644 --- a/src/General/lib/$.coffee +++ b/src/General/lib/$.coffee @@ -221,7 +221,7 @@ $.event = (event, detail, root=d) -> $.open = (URL) -> <% if (type === 'userscript') { %> - $.open = (URL) -> GM_openInTab URL + GM_openInTab URL <% } else { %> window.open URL, '_blank' <% } %> From b83e536c86a5a6d98dffba76ffae56f1c8e3d523 Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Thu, 1 Aug 2013 20:34:00 +0200 Subject: [PATCH 087/172] Don't save the unread count if the thread is dead. --- src/Monitoring/Unread.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Monitoring/Unread.coffee b/src/Monitoring/Unread.coffee index ff816c6b8..c31c63b56 100644 --- a/src/Monitoring/Unread.coffee +++ b/src/Monitoring/Unread.coffee @@ -132,6 +132,7 @@ Unread = Unread.update() if e saveLastReadPost: -> + return if Unread.thread.isDead Unread.db.set boardID: Unread.thread.board.ID threadID: Unread.thread.ID From e07346a35f3a3ef564754e42787ca408e4514b10 Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Thu, 1 Aug 2013 21:42:53 +0200 Subject: [PATCH 088/172] 'use strict'; --- Gruntfile.js | 2 ++ src/Meta/usestrict.js | 1 + 2 files changed, 3 insertions(+) create mode 100644 src/Meta/usestrict.js diff --git a/Gruntfile.js b/Gruntfile.js index 69100ad0f..7eeca8da7 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -52,6 +52,7 @@ module.exports = function(grunt) { 'builds/crx/manifest.json': 'src/Meta/manifest.json', 'builds/crx/script.js': [ 'src/Meta/banner.js', + 'src/Meta/usestrict.js', 'tmp-<%= pkg.type %>/script.js' ] } @@ -63,6 +64,7 @@ module.exports = function(grunt) { 'builds/<%= pkg.name %>.user.js': [ 'src/Meta/metadata.js', 'src/Meta/banner.js', + 'src/Meta/usestrict.js', 'tmp-<%= pkg.type %>/script.js' ] } diff --git a/src/Meta/usestrict.js b/src/Meta/usestrict.js new file mode 100644 index 000000000..ad9a93a7c --- /dev/null +++ b/src/Meta/usestrict.js @@ -0,0 +1 @@ +'use strict'; From e5db696b87674e91cdeaa30f713b325fddbbfc57 Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Thu, 1 Aug 2013 21:51:26 +0200 Subject: [PATCH 089/172] Remove the .webkit class. Use .blink instead. --- src/General/Main.coffee | 1 - 1 file changed, 1 deletion(-) diff --git a/src/General/Main.coffee b/src/General/Main.coffee index 197e7aa50..0105d43f6 100644 --- a/src/General/Main.coffee +++ b/src/General/Main.coffee @@ -125,7 +125,6 @@ Main = # disable the mobile layout $('link[href*=mobile]', d.head)?.disabled = true <% if (type === 'crx') { %> - $.addClass doc, 'webkit' $.addClass doc, 'blink' <% } else { %> $.addClass doc, 'gecko' From 03a8435a94fee6c68214ab8c6510cbd5890b2103 Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Thu, 1 Aug 2013 22:10:51 +0200 Subject: [PATCH 090/172] Fix duplicate data property in object literal not allowed in strict mode. --- src/Archive/Redirect.coffee | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/Archive/Redirect.coffee b/src/Archive/Redirect.coffee index 8cf6954bb..edc4e621c 100644 --- a/src/Archive/Redirect.coffee +++ b/src/Archive/Redirect.coffee @@ -1,8 +1,9 @@ Redirect = archives: `<%= JSON.stringify(grunt.file.readJSON('json/archives.json')) %>` - thread: {} - post: {} - file: {} + data: + thread: {} + post: {} + file: {} init: -> for boardID, data of Conf['selectedArchives'] @@ -13,15 +14,15 @@ Redirect = archive.files else archive.boards - Redirect[type][boardID] = archive if boardID in arr + Redirect.data[type][boardID] = archive if boardID in arr for archive in Conf['archives'] for boardID in archive.boards - unless boardID of Redirect.thread - Redirect.thread[boardID] = archive - unless boardID of Redirect.post or archive.software isnt 'foolfuuka' - Redirect.post[boardID] = archive - unless boardID of Redirect.file or boardID not in archive.files - Redirect.file[boardID] = archive + unless boardID of Redirect.data.thread + Redirect.data.thread[boardID] = archive + unless boardID of Redirect.data.post or archive.software isnt 'foolfuuka' + Redirect.data.post[boardID] = archive + unless boardID of Redirect.data.file or boardID not in archive.files + Redirect.data.file[boardID] = archive Redirect.update() @@ -40,7 +41,7 @@ Redirect = cb? now to: (dest, data) -> - archive = (if dest is 'search' then Redirect.thread else Redirect[dest])[data.boardID] + archive = (if dest is 'search' then Redirect.data.thread else Redirect.data[dest])[data.boardID] return '' unless archive Redirect[dest] archive, data From 4c97408c26f9cdac7db3b59f040b0414dbc0d734 Mon Sep 17 00:00:00 2001 From: NemDiggers <horo@ipwnage.com> Date: Fri, 2 Aug 2013 03:08:52 -0400 Subject: [PATCH 091/172] Rename archive; add support for /hc/ --- json/archives.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/json/archives.json b/json/archives.json index 8fd76aebf..42edc592c 100644 --- a/json/archives.json +++ b/json/archives.json @@ -54,13 +54,13 @@ "files": ["adv", "asp", "cm", "e", "i", "lgbt", "n", "o", "p", "s", "s4s", "t", "trv", "y"] }, { "uid": 12, - "name": "worldathleticproject", + "name": "fap archive", "domain": "fuuka.worldathleticproject.org", "http": true, "https": false, "software": "foolfuuka", - "boards": ["e", "h", "p", "s", "u"], - "files": ["e", "h", "p", "s", "u"] + "boards": ["e", "h", "p", "s", "u", "hc"], + "files": ["e", "h", "p", "s", "u", "hc"] }, { "uid": 7, "name": "Install Gentoo", From 46c57947ce77320ee6f03e7b7f4dbd9fa776cfce Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Fri, 2 Aug 2013 09:53:25 +0200 Subject: [PATCH 092/172] Sort "fap archive" boards & files arrays. --- json/archives.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/json/archives.json b/json/archives.json index 42edc592c..b59fa5145 100644 --- a/json/archives.json +++ b/json/archives.json @@ -59,8 +59,8 @@ "http": true, "https": false, "software": "foolfuuka", - "boards": ["e", "h", "p", "s", "u", "hc"], - "files": ["e", "h", "p", "s", "u", "hc"] + "boards": ["e", "h", "hc", "p", "s", "u"], + "files": ["e", "h", "hc", "p", "s", "u"] }, { "uid": 7, "name": "Install Gentoo", From 6d3afcd005b66aaf1f13a8cc8db27b37f862fd6b Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Fri, 2 Aug 2013 21:13:32 +0200 Subject: [PATCH 093/172] Simplify $.open with GM_openInTab. --- lib/$.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/$.coffee b/lib/$.coffee index 2d7da3708..811614b78 100644 --- a/lib/$.coffee +++ b/lib/$.coffee @@ -147,7 +147,7 @@ $.off = (el, events, handler) -> $.event = (event, detail, root=d) -> root.dispatchEvent new CustomEvent event, {bubbles: true, detail} <% if (type === 'userscript') { %> -$.open = (URL) -> GM_openInTab URL +$.open = GM_openInTab <% } else { %> $.open = (URL) -> window.open URL, '_blank' <% } %> From eef94f971cc75c9350b2ef200b398feeeed18851 Mon Sep 17 00:00:00 2001 From: James Campos <james.r.campos@gmail.com> Date: Fri, 2 Aug 2013 15:12:10 -0700 Subject: [PATCH 094/172] Gruntfile.js -> Gruntfile.coffee Close #1209 --- Gruntfile.coffee | 181 +++++++++++++++++++++++++++++++++++++++++ Gruntfile.js | 208 ----------------------------------------------- 2 files changed, 181 insertions(+), 208 deletions(-) create mode 100644 Gruntfile.coffee delete mode 100644 Gruntfile.js diff --git a/Gruntfile.coffee b/Gruntfile.coffee new file mode 100644 index 000000000..ce531b6c7 --- /dev/null +++ b/Gruntfile.coffee @@ -0,0 +1,181 @@ +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: + coffee: + options: concatOptions + src: [ + 'src/General/Config.coffee' + 'src/General/Globals.coffee' + 'lib/**/*' + 'src/General/UI.coffee' + 'src/General/Header.coffee' + 'src/General/Notification.coffee' + 'src/General/Settings.coffee' + 'src/General/Get.coffee' + 'src/General/Build.coffee' + # Features --> + 'src/Filtering/**/*' + 'src/Quotelinks/**/*' + 'src/Posting/**/*' + 'src/Images/**/*' + 'src/Menu/**/*' + 'src/Monitoring/**/*' + 'src/Archive/**/*' + 'src/Miscellaneous/**/*' + # <--| + 'src/General/Board.coffee' + 'src/General/Thread.coffee' + 'src/General/Post.coffee' + 'src/General/Clone.coffee' + 'src/General/DataBoard.coffee' + 'src/General/Main.coffee' + ] + dest: 'tmp-<%= pkg.type %>/script.coffee' + crx: + options: concatOptions + files: + 'builds/crx/manifest.json': 'src/Meta/manifest.json' + 'builds/crx/script.js': [ + 'src/Meta/banner.js' + 'src/Meta/usestrict.js' + 'tmp-<%= pkg.type %>/script.js' + ] + userscript: + options: concatOptions + files: + 'builds/<%= pkg.name %>.meta.js': 'src/Meta/metadata.js' + 'builds/<%= pkg.name %>.user.js': [ + 'src/Meta/metadata.js' + 'src/Meta/banner.js' + 'src/Meta/usestrict.js' + 'tmp-<%= pkg.type %>/script.js' + ] + copy: + crx: + src: 'img/*.png' + dest: 'builds/crx/' + expand: true + flatten: true + # for_d19 + # 19 juin 2013 10:32:22 + # We're currently sniffing the type of the file based on file extension. + # We have a different type of content, Themes, which use a pure zip-file with a .zip ending. + # This solution is sub-optimal and will be changed in the future. + # For now, upload an unsigned ZIP-file with the ending .nex or .crx. + nex: + src: 'builds/<%= pkg.name %>.zip' + dest: 'builds/<%= pkg.name %>.nex' + coffee: + script: + src: 'tmp-<%= pkg.type %>/script.coffee' + dest: 'tmp-<%= pkg.type %>/script.js' + concurrent: + build: ['build-crx', 'build-userscript'] + bump: + options: + updateConfigs: ['pkg'] + commit: false + createTag: false + push: false + shell: + commit: + options: shellOptions + 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(' && ') + push: + options: shellOptions + command: 'git push origin --tags -f && git push origin --all' + watch: + all: + options: + interrupt: true + files: [ + 'Gruntfile.coffee' + 'package.json' + 'lib/**/*' + 'src/**/*' + 'html/**/*' + 'css/**/*' + 'json/**/*' + 'img/**/*' + ] + tasks: 'build' + compress: + crx: + options: + archive: 'builds/<%= pkg.name %>.zip' + level: 9 + pretty: true + expand: true + flatten: true + src: 'builds/crx/*' + dest: '/' + clean: + builds: 'builds' + tmpcrx: 'tmp-crx' + tmpuserscript: 'tmp-userscript' + + grunt.loadNpmTasks 'grunt-bump' + grunt.loadNpmTasks 'grunt-concurrent' + grunt.loadNpmTasks 'grunt-contrib-clean' + grunt.loadNpmTasks 'grunt-contrib-coffee' + grunt.loadNpmTasks 'grunt-contrib-compress' + grunt.loadNpmTasks 'grunt-contrib-concat' + grunt.loadNpmTasks 'grunt-contrib-copy' + grunt.loadNpmTasks 'grunt-contrib-watch' + grunt.loadNpmTasks 'grunt-shell' + + grunt.registerTask 'default', ['build'] + + grunt.registerTask 'set-build', 'Set the build type variable', (type) -> + pkg = grunt.config 'pkg' + pkg.type = type + grunt.config 'pkg', pkg + grunt.log.ok 'pkg.type = %s', type + grunt.registerTask 'build', ['concurrent:build'] + grunt.registerTask 'build-crx', [ + 'set-build:crx' + 'concat:coffee' + 'coffee:script' + 'concat:crx' + 'copy:crx' + 'clean:tmpcrx' + ] + grunt.registerTask 'build-userscript', [ + 'set-build:userscript' + 'concat:coffee' + 'coffee:script' + 'concat:userscript' + 'clean:tmpuserscript' + ] + + grunt.registerTask 'release', ['shell:commit', 'shell:push', 'build-crx', 'compress:crx', 'copy:nex'] + grunt.registerTask 'patch', ['bump', 'updcl:3', 'release'] + grunt.registerTask 'minor', ['bump:minor', 'updcl:2', 'release'] + grunt.registerTask 'major', ['bump:major', 'updcl:1', 'release'] + + grunt.registerTask 'updcl', 'Update the changelog', (headerLevel) -> + headerPrefix = new Array(+headerLevel + 1).join '#' + {version} = grunt.config 'pkg' + today = grunt.template.today 'yyyy-mm-dd' + changelog = grunt.file.read 'CHANGELOG.md' + + grunt.file.write 'CHANGELOG.md', "#{headerPrefix} #{version} - *#{today}*\n\n#{changelog}" + grunt.log.ok "Changelog updated for v#{version}." diff --git a/Gruntfile.js b/Gruntfile.js deleted file mode 100644 index 7eeca8da7..000000000 --- a/Gruntfile.js +++ /dev/null @@ -1,208 +0,0 @@ -module.exports = function(grunt) { - - var concatOptions = { - process: { - get data() { return grunt.config('pkg'); } - } - }; - var shellOptions = { - stdout: true, - stderr: true, - failOnError: true - }; - - // Project configuration. - grunt.initConfig({ - pkg: grunt.file.readJSON('package.json'), - concat: { - coffee: { - options: concatOptions, - src: [ - 'src/General/Config.coffee', - 'src/General/Globals.coffee', - 'lib/**/*', - 'src/General/UI.coffee', - 'src/General/Header.coffee', - 'src/General/Notification.coffee', - 'src/General/Settings.coffee', - 'src/General/Get.coffee', - 'src/General/Build.coffee', - // Features --> - 'src/Filtering/**/*', - 'src/Quotelinks/**/*', - 'src/Posting/**/*', - 'src/Images/**/*', - 'src/Menu/**/*', - 'src/Monitoring/**/*', - 'src/Archive/**/*', - 'src/Miscellaneous/**/*', - // <--| - 'src/General/Board.coffee', - 'src/General/Thread.coffee', - 'src/General/Post.coffee', - 'src/General/Clone.coffee', - 'src/General/DataBoard.coffee', - 'src/General/Main.coffee' - ], - dest: 'tmp-<%= pkg.type %>/script.coffee' - }, - crx: { - options: concatOptions, - files: { - 'builds/crx/manifest.json': 'src/Meta/manifest.json', - 'builds/crx/script.js': [ - 'src/Meta/banner.js', - 'src/Meta/usestrict.js', - 'tmp-<%= pkg.type %>/script.js' - ] - } - }, - userscript: { - options: concatOptions, - files: { - 'builds/<%= pkg.name %>.meta.js': 'src/Meta/metadata.js', - 'builds/<%= pkg.name %>.user.js': [ - 'src/Meta/metadata.js', - 'src/Meta/banner.js', - 'src/Meta/usestrict.js', - 'tmp-<%= pkg.type %>/script.js' - ] - } - } - }, - copy: { - crx: { - src: 'img/*.png', - dest: 'builds/crx/', - expand: true, - flatten: true - }, - // for_d19 - // 19 juin 2013 10:32:22 - // We're currently sniffing the type of the file based on file extension. - // We have a different type of content, Themes, which use a pure zip-file with a .zip ending. - // This solution is sub-optimal and will be changed in the future. - // For now, upload an unsigned ZIP-file with the ending .nex or .crx. - nex: { - src: 'builds/<%= pkg.name %>.zip', - dest: 'builds/<%= pkg.name %>.nex' - } - }, - coffee: { - script: { - src: 'tmp-<%= pkg.type %>/script.coffee', - dest: 'tmp-<%= pkg.type %>/script.js' - } - }, - concurrent: { - build: ['build-crx', 'build-userscript'] - }, - bump: { - options: { - updateConfigs: ['pkg'], - commit: false, - createTag: false, - push: false - } - }, - shell: { - commit: { - options: shellOptions, - 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(' && ') - }, - push: { - options: shellOptions, - command: 'git push origin --tags -f && git push origin --all' - } - }, - watch: { - all: { - options: { - interrupt: true - }, - files: [ - 'Gruntfile.js', - 'package.json', - 'lib/**/*', - 'src/**/*', - 'html/**/*', - 'css/**/*', - 'json/**/*', - 'img/**/*' - ], - tasks: 'build' - } - }, - compress: { - crx: { - options: { - archive: 'builds/<%= pkg.name %>.zip', - level: 9, - pretty: true - }, - expand: true, - flatten: true, - src: 'builds/crx/*', - dest: '/' - } - }, - clean: { - builds: 'builds', - tmpcrx: 'tmp-crx', - tmpuserscript: 'tmp-userscript' - } - }); - - grunt.loadNpmTasks('grunt-bump'); - grunt.loadNpmTasks('grunt-concurrent'); - grunt.loadNpmTasks('grunt-contrib-clean'); - grunt.loadNpmTasks('grunt-contrib-coffee'); - grunt.loadNpmTasks('grunt-contrib-compress'); - grunt.loadNpmTasks('grunt-contrib-concat'); - grunt.loadNpmTasks('grunt-contrib-copy'); - grunt.loadNpmTasks('grunt-contrib-watch'); - grunt.loadNpmTasks('grunt-shell'); - - grunt.registerTask('default', ['build']); - - grunt.registerTask('set-build', 'Set the build type variable', function(type) { - var pkg = grunt.config('pkg'); - pkg.type = type; - grunt.config('pkg', pkg); - grunt.log.ok('pkg.type = %s', type); - }); - grunt.registerTask('build', ['concurrent:build']); - grunt.registerTask('build-crx', [ - 'set-build:crx', - 'concat:coffee', - 'coffee:script', - 'concat:crx', - 'copy:crx', - 'clean:tmpcrx' - ]); - grunt.registerTask('build-userscript', [ - 'set-build:userscript', - 'concat:coffee', - 'coffee:script', - 'concat:userscript', - 'clean:tmpuserscript' - ]); - - grunt.registerTask('release', ['shell:commit', 'shell:push', 'build-crx', 'compress:crx', 'copy:nex']); - grunt.registerTask('patch', ['bump', 'updcl:3', 'release']); - grunt.registerTask('minor', ['bump:minor', 'updcl:2', 'release']); - grunt.registerTask('major', ['bump:major', 'updcl:1', 'release']); - - grunt.registerTask('updcl', 'Update the changelog', function(i) { - // i is the number of #s for markdown. - var pkg = grunt.config('pkg'); - var version = new Array(+i + 1).join('#') + ' ' + pkg.version + ' - *' + grunt.template.today('yyyy-mm-dd') + '*'; - grunt.file.write('CHANGELOG.md', version + '\n\n' + grunt.file.read('CHANGELOG.md')); - grunt.log.ok('Changelog updated for v' + pkg.version + '.'); - }); -}; From 0b6f66ca81142c9c96b9fb6a6a71c02cc01ce93f Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Sat, 3 Aug 2013 00:27:36 +0200 Subject: [PATCH 095/172] A few alignement tweaks. --- Gruntfile.coffee | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Gruntfile.coffee b/Gruntfile.coffee index ce531b6c7..268a8826c 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -65,9 +65,9 @@ module.exports = (grunt) -> ] copy: crx: - src: 'img/*.png' + src: 'img/*.png' dest: 'builds/crx/' - expand: true + expand: true flatten: true # for_d19 # 19 juin 2013 10:32:22 @@ -76,20 +76,20 @@ module.exports = (grunt) -> # This solution is sub-optimal and will be changed in the future. # For now, upload an unsigned ZIP-file with the ending .nex or .crx. nex: - src: 'builds/<%= pkg.name %>.zip' + src: 'builds/<%= pkg.name %>.zip' dest: 'builds/<%= pkg.name %>.nex' coffee: script: - src: 'tmp-<%= pkg.type %>/script.coffee' + src: 'tmp-<%= pkg.type %>/script.coffee' dest: 'tmp-<%= pkg.type %>/script.js' concurrent: build: ['build-crx', 'build-userscript'] bump: options: updateConfigs: ['pkg'] - commit: false + commit: false createTag: false - push: false + push: false shell: commit: options: shellOptions @@ -98,7 +98,7 @@ module.exports = (grunt) -> '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(' && ') + ].join ' && ' push: options: shellOptions command: 'git push origin --tags -f && git push origin --all' @@ -123,7 +123,7 @@ module.exports = (grunt) -> archive: 'builds/<%= pkg.name %>.zip' level: 9 pretty: true - expand: true + expand: true flatten: true src: 'builds/crx/*' dest: '/' @@ -167,7 +167,7 @@ module.exports = (grunt) -> ] grunt.registerTask 'release', ['shell:commit', 'shell:push', 'build-crx', 'compress:crx', 'copy:nex'] - grunt.registerTask 'patch', ['bump', 'updcl:3', 'release'] + grunt.registerTask 'patch', ['bump', 'updcl:3', 'release'] grunt.registerTask 'minor', ['bump:minor', 'updcl:2', 'release'] grunt.registerTask 'major', ['bump:major', 'updcl:1', 'release'] From 82d3afba0adfa77ba2ad6bae77a561376360fd9a Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Sat, 3 Aug 2013 00:52:16 +0200 Subject: [PATCH 096/172] Tweak Gruntfile tasks loading. --- Gruntfile.coffee | 10 +--------- package.json | 3 ++- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/Gruntfile.coffee b/Gruntfile.coffee index 268a8826c..9410f0c6b 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -132,15 +132,7 @@ module.exports = (grunt) -> tmpcrx: 'tmp-crx' tmpuserscript: 'tmp-userscript' - grunt.loadNpmTasks 'grunt-bump' - grunt.loadNpmTasks 'grunt-concurrent' - grunt.loadNpmTasks 'grunt-contrib-clean' - grunt.loadNpmTasks 'grunt-contrib-coffee' - grunt.loadNpmTasks 'grunt-contrib-compress' - grunt.loadNpmTasks 'grunt-contrib-concat' - grunt.loadNpmTasks 'grunt-contrib-copy' - grunt.loadNpmTasks 'grunt-contrib-watch' - grunt.loadNpmTasks 'grunt-shell' + require('matchdep').filterDev('grunt-*').forEach grunt.loadNpmTasks grunt.registerTask 'default', ['build'] diff --git a/package.json b/package.json index b01920f2d..33b87d4d9 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,8 @@ "grunt-contrib-concat": "~0.3.0", "grunt-contrib-copy": "~0.4.1", "grunt-contrib-watch": "~0.5.0", - "grunt-shell": "~0.3.1" + "grunt-shell": "~0.3.1", + "matchdep": "~0.1.2" }, "repository": { "type": "git", From 68214969d8707f919b0b689ffb6924bd3a7c6b48 Mon Sep 17 00:00:00 2001 From: NemDiggers <horo@ipwnage.com> Date: Sat, 3 Aug 2013 01:04:38 -0400 Subject: [PATCH 097/172] I'm crazy I'm going to give it a shot. I've been archiving /b/ for awhile now and see nothing blatantly illegal. I'm hoping users will report anything van worthy. If shit hits the fan, I'll just submit another PR removing it. --- json/archives.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/json/archives.json b/json/archives.json index b59fa5145..a5cee414f 100644 --- a/json/archives.json +++ b/json/archives.json @@ -59,8 +59,8 @@ "http": true, "https": false, "software": "foolfuuka", - "boards": ["e", "h", "hc", "p", "s", "u"], - "files": ["e", "h", "hc", "p", "s", "u"] + "boards": ["b" "e", "h", "hc", "p", "s", "u"], + "files": ["b" "e", "h", "hc", "p", "s", "u"] }, { "uid": 7, "name": "Install Gentoo", From fbebf6cbd376b09d2ac94d6b2c4bd720902c20cb Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Sat, 3 Aug 2013 18:05:53 +0200 Subject: [PATCH 098/172] Fix missing comma. @Proplex --- json/archives.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/json/archives.json b/json/archives.json index a5cee414f..f0c788108 100644 --- a/json/archives.json +++ b/json/archives.json @@ -59,8 +59,8 @@ "http": true, "https": false, "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", "u"], + "files": ["b", "e", "h", "hc", "p", "s", "u"] }, { "uid": 7, "name": "Install Gentoo", From 8e79a53701e07064abd56882945df5902506fd91 Mon Sep 17 00:00:00 2001 From: Enzo Moretti <woxxy@foolz.us> Date: Sun, 4 Aug 2013 15:32:52 +0200 Subject: [PATCH 099/172] Adding Foolz Archive Beta as archive option. --- json/archives.json | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/json/archives.json b/json/archives.json index f0c788108..e83401a89 100644 --- a/json/archives.json +++ b/json/archives.json @@ -97,4 +97,13 @@ "software": "fuuka", "boards": ["3", "cgl", "ck", "fa", "ic", "jp", "lit", "q", "tg", "vr"], "files": ["3", "cgl", "ck", "fa", "ic", "jp", "lit", "q", "tg", "vr"] +}, { + "uid": 11, + "name": "Foolz Beta", + "domain": "beta.foolz.us", + "http": true, + "https": true, + "software": "foolfuuka", + "boards": ["a", "co", "gd", "jp", "m", "q", "sp", "tg", "tv", "v", "vg", "vp", "vr", "wsg"], + "files": ["a", "gd", "jp", "m", "q", "tg", "vg", "vp", "vr", "wsg"] }] From 6b0ba6f96e59eda8ce85c6d566c83c1a11b69a14 Mon Sep 17 00:00:00 2001 From: Enzo Moretti <woxxy@foolz.us> Date: Sun, 4 Aug 2013 16:00:12 +0200 Subject: [PATCH 100/172] Adding also /mlp/, /h/ and /u/, and fixing the unique id. --- json/archives.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/json/archives.json b/json/archives.json index e83401a89..15c52e369 100644 --- a/json/archives.json +++ b/json/archives.json @@ -98,12 +98,12 @@ "boards": ["3", "cgl", "ck", "fa", "ic", "jp", "lit", "q", "tg", "vr"], "files": ["3", "cgl", "ck", "fa", "ic", "jp", "lit", "q", "tg", "vr"] }, { - "uid": 11, + "uid": 13, "name": "Foolz Beta", "domain": "beta.foolz.us", "http": true, "https": true, "software": "foolfuuka", - "boards": ["a", "co", "gd", "jp", "m", "q", "sp", "tg", "tv", "v", "vg", "vp", "vr", "wsg"], - "files": ["a", "gd", "jp", "m", "q", "tg", "vg", "vp", "vr", "wsg"] + "boards": ["a", "co", "gd", "h", "jp", "m", "mlp", "q", "sp", "tg", "tv", "u", "v", "vg", "vp", "vr", "wsg"], + "files": ["a", "gd", "h", "jp", "m", "q", "tg", "u", "vg", "vp", "vr", "wsg"] }] From a6f6f44d580a3c6b243a69b8177dcec8b4c42eef Mon Sep 17 00:00:00 2001 From: Zixaphir <zixaphirmoxphar@gmail.com> Date: Sun, 4 Aug 2013 18:47:04 -0700 Subject: [PATCH 101/172] Linkifier Rewrite --- LICENSE | 2 +- builds/4chan-X.user.js | 365 +++++++++++++++++-------------- builds/crx/script.js | 365 +++++++++++++++++-------------- src/Linkification/Linkify.coffee | 337 +++++++++++++++------------- 4 files changed, 593 insertions(+), 476 deletions(-) diff --git a/LICENSE b/LICENSE index 97bce1cc7..391e259a5 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,5 @@ /* -* 4chan X - Version 1.2.24 - 2013-08-01 +* 4chan X - Version 1.2.24 - 2013-08-04 * * Licensed under the MIT license. * https://github.com/seaweedchan/4chan-x/blob/master/LICENSE diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js index 909c75b3f..8eafa30f9 100644 --- a/builds/4chan-X.user.js +++ b/builds/4chan-X.user.js @@ -19,7 +19,7 @@ // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwAgMAAAAqbBEUAAAACVBMVEUAAGcAAABmzDNZt9VtAAAAAXRSTlMAQObYZgAAAHFJREFUKFOt0LENACEIBdBv4Qju4wgWanEj3D6OcIVMKaitYHEU/jwTCQj8W75kiVCSBvdQ5/AvfVHBin11BgdRq3ysBgfwBDRrj3MCIA+oAQaku/Q1cNctrAmyDl577tOThYt/Y1RBM4DgOHzM0HFTAyLukH/cmRnqAAAAAElFTkSuQmCC // ==/UserScript== /* -* 4chan X - Version 1.2.24 - 2013-08-01 +* 4chan X - Version 1.2.24 - 2013-08-04 * * Licensed under the MIT license. * https://github.com/seaweedchan/4chan-x/blob/master/LICENSE @@ -4300,122 +4300,235 @@ if (g.VIEW === 'catalog' || !Conf['Linkify']) { return; } - this.regString = Conf['Allow False Positives'] ? /(\b([a-z]+:\/\/|[a-z]{3,}\.[-a-z0-9]+\.[a-z]+|[-a-z0-9]+\.[a-z]|[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+|[a-z]{3,}:[a-z0-9?]|[a-z0-9._%+-:]+@[a-z0-9.-]+\.[a-z0-9])[^\s'"]+)/gi : /(((magnet|mailto)\:|(www\.)|(news|(ht|f)tp(s?))\:\/\/){1}\S+)/gi; + this.regString = Conf['Allow False Positives'] ? /(\b([a-z]+:\/\/|[a-z]{3,}\.[-a-z0-9]+\.[a-z]|[-a-z0-9]+\.[a-z]|[0-9]+\.[0-9]+\.[0-9]+\.[0-9]|[a-z]{3,}:[a-z0-9?]|[\S]+@[a-z0-9.-]+\.[a-z0-9])[^\s'"]+)/gi : /(((magnet|mailto)\:|(www\.)|(news|(ht|f)tp(s?))\:\/\/){1}\S+)/gi; if (Conf['Comment Expansion']) { ExpandComment.callbacks.push(this.node); } + if (Conf['Title Link']) { + $.sync('CachedTitles', Linkify.titleSync); + } return Post.prototype.callbacks.push({ name: 'Linkify', cb: this.node }); }, - cypher: $.el('div'), node: function() { - var a, child, cypher, cypherText, data, embed, embedder, embeds, i, index, len, link, links, lookahead, name, next, node, nodes, snapshot, spoiler, text, _i, _j, _k, _l, _len, _len1, _len2, _len3, _len4, _m, _ref, _ref1, _ref2; + var data, embedder, i, len, node, range, snapshot, _i, _j, _len, _len1, _ref, _ref1; if (this.isClone && Conf['Embedding']) { _ref = $$('.embedder', this.nodes.comment); for (_i = 0, _len = _ref.length; _i < _len; _i++) { embedder = _ref[_i]; - $.on(embedder, "click", Linkify.toggle); + $.on(embedder, "click", Linkify.cb.toggle); } return; } snapshot = $.X('.//text()', this.nodes.comment); - cypher = Linkify.cypher; i = -1; len = snapshot.snapshotLength; while (++i < len) { - nodes = $.frag(); node = snapshot.snapshotItem(i); data = node.data; - if (!(node.parentNode && Linkify.regString.test(data))) { - continue; + if (Linkify.regString.test(data)) { + Linkify.regString.lastIndex = 0; + Linkify.gatherLinks(node, this); } - Linkify.regString.lastIndex = 0; - cypherText = []; - if (next = node.nextSibling) { - cypher.textContent = node.textContent; - cypherText[0] = cypher.innerHTML; - while ((next.nodeName.toLowerCase() === 'wbr' || next.nodeName.toLowerCase() === 's') && (lookahead = next.nextSibling) && ((name = lookahead.nodeName) === "#text" || name.toLowerCase() === 'br')) { - cypher.textContent = lookahead.textContent; - cypherText.push((spoiler = next.innerHTML) ? "<s>" + (spoiler.replace(/</g, ' <')) + "</s>" : '<wbr>'); - cypherText.push(cypher.innerHTML); - $.rm(next); - next = lookahead.nextSibling; - if (lookahead.nodeName === "#text") { - $.rm(lookahead); - } - if (!next) { - break; - } - } - } - if (cypherText.length) { - data = cypherText.join(''); - } - links = data.match(Linkify.regString); - for (_j = 0, _len1 = links.length; _j < _len1; _j++) { - link = links[_j]; - index = data.indexOf(link); - if (text = data.slice(0, index)) { - cypher.innerHTML = text; - _ref1 = __slice.call(cypher.childNodes); - for (_k = 0, _len2 = _ref1.length; _k < _len2; _k++) { - child = _ref1[_k]; - $.add(nodes, child); - } - } - cypher.innerHTML = (link.indexOf(':') < 0 ? (link.indexOf('@') > 0 ? 'mailto:' + link : 'http://' + link) : link).replace(/<(wbr|s|\/s)>/g, ''); - a = $.el('a', { - innerHTML: link, - className: 'linkify', - rel: 'nofollow noreferrer', - target: '_blank', - href: cypher.textContent - }); - $.add(nodes, Linkify.embedder(a)); - data = data.slice(index + link.length); - } - if (data) { - cypher.innerHTML = data; - _ref2 = __slice.call(cypher.childNodes); - for (_l = 0, _len3 = _ref2.length; _l < _len3; _l++) { - child = _ref2[_l]; - $.add(nodes, child); - } - } - $.replace(node, nodes); } - if (Conf['Auto-embed']) { - embeds = $$('.embedder', this.nodes.comment); - for (_m = 0, _len4 = embeds.length; _m < _len4; _m++) { - embed = embeds[_m]; - embed.click(); + if (!(Conf['Embedding'] || Conf['Link Title'])) { + return; + } + _ref1 = this.nodes.links; + for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { + range = _ref1[_j]; + if (data = Linkify.services(range)) { + if (Conf['Embedding']) { + Linkify.embed(data); + } + if (Conf['Link Title']) { + Linkify.title(data); + } } } }, - toggle: function() { - var el, embed, style, type, url; + gatherLinks: function(node, post) { + var data, index, len, len2, link, links, match, range, _i, _len, _ref; + + data = node.data; + len = data.length; + links = []; + while ((match = Linkify.regString.exec(data))) { + index = match.index; + link = match[0]; + len2 = index + link.length; + if (len - len2 === 0) { + break; + } + range = document.createRange(); + range.setStart(node, index); + range.setEnd(node, len2); + links.push(range); + } + if (match) { + Linkify.seek(match, node, post); + } + _ref = links.reverse(); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + range = _ref[_i]; + Linkify.makeLink(range, post); + } + }, + seek: function(match, node, post) { + var data, index, link, next, range, result; + + index = match.index; + link = match[0]; + range = document.createRange(); + range.setStart(node, index); + while ((next = node.nextSibling) && next.nodeName !== 'BR') { + node = next; + data = node.data; + if (result = /[\s'"]/.exec(data)) { + range.setEnd(node, result.index); + } + } + if (range.collapsed) { + if (node.nodeName === 'WBR') { + node = node.previousSibling; + } + range.setEnd(node, node.length); + } + return Linkify.makeLink(range, post); + }, + makeLink: function(range, post) { + var a, link; + + link = range.toString(); + link = link.contains(':') ? link : (link.contains('@') ? 'mailto:' : 'http://') + link; + a = $.el('a', { + className: 'linkify', + rel: 'nofollow noreferrer', + target: '_blank', + href: link + }); + range.surroundContents(a); + post.nodes.links.push(a); + }, + services: function(link) { + var href, key, match, type, _ref; + + href = link.href; + _ref = Linkify.types; + for (key in _ref) { + type = _ref[key]; + if (!(match = type.regExp.exec(href))) { + continue; + } + return [key, match[1], match[2], link]; + } + }, + embed: function(data) { + var embed, key, link, options, uid; + + key = data[0], uid = data[1], options = data[2], link = data[3]; + embed = $.el('a', { + name: uid, + option: options, + className: 'embedder', + href: 'javascript:;', + textContent: '(embed)' + }); + embed.dataset.service = key; + embed.dataset.originalurl = link.href; + $.addClass(link, "" + embed.dataset.service); + $.on(embed, 'click', Linkify.cb.toggle); + return $.after(link, [$.tn(' '), embed]); + }, + title: function(data) { + var err, key, link, options, service, title, titles, uid; + + key = data[0], uid = data[1], options = data[2], link = data[3]; + if (!(service = Linkify.types[key].title)) { + return; + } + titles = Conf['CachedTitles']; + if (title = titles[uid]) { + link.textContent = title[0]; + if (Conf['Embedding']) { + return link.nextElementSibling.dataset.title = title[0]; + } + } else { + try { + $.cache(service.api(uid), function() { + return title = Linkify.cb.title.apply(this, [data]); + }); + } catch (_error) { + err = _error; + link.innerHTML = "[" + key + "] <span class=warning>Title Link Blocked</span> (are you using NoScript?)</a>"; + return; + } + if (title) { + titles[uid] = [title, Date.now()]; + return $.set('CachedTitles', titles); + } + } + }, + titleSync: function(value) { + return Conf['CachedTitles'] = value; + }, + cb: { + toggle: function() { + var el, embed; + + embed = this.previousElementSibling; + el = !this.className.contains("embedded") ? Linkify.cb.embed(this) : Linkify.cb.unembed(this); + $.replace(embed, el); + return $.toggleClass(this, 'embedded'); + }, + embed: function(a) { + var el, style, type; + + el = (type = Linkify.types[a.dataset.service]).el.call(a); + el.style.cssText = (style = type.style) ? style : "border: 0; width: 640px; height: 390px"; + a.textContent = '(unembed)'; + return el; + }, + unembed: function(a) { + var el, url; - embed = this.previousElementSibling; - if (this.className.contains("embedded")) { el = $.el('a', { rel: 'nofollow noreferrer', target: 'blank', className: 'linkify', - href: url = this.getAttribute("data-originalURL"), - textContent: this.getAttribute("data-title") || url + href: url = a.dataset.originalurl, + textContent: a.dataset.title || url }); - this.textContent = '(embed)'; - $.addClass(el, "" + (this.getAttribute('data-service'))); - } else { - el = (type = Linkify.types[this.getAttribute("data-service")]).el.call(this); - el.style.cssText = (style = type.style) ? style : "border: 0; width: 640px; height: 390px"; - this.textContent = '(unembed)'; + a.textContent = '(embed)'; + $.addClass(el, "" + a.dataset.service); + return el; + }, + title: function(data) { + var key, link, options, service, text, uid; + + key = data[0], uid = data[1], options = data[2], link = data[3]; + service = Linkify.types[key].title; + return link.textContent = (function() { + switch (this.status) { + case 200: + case 304: + text = "" + (service.text.call(this)); + if (Conf['Embedding']) { + link.nextElementSibling.dataset.title = text; + } + return text; + case 404: + return "[" + key + "] Not Found"; + case 403: + return "[" + key + "] Forbidden or Private"; + default: + return "[" + key + "] " + this.status + "'d"; + } + }).call(this); } - $.replace(embed, el); - return $.toggleClass(this, 'embedded'); }, types: { YouTube: { @@ -4426,8 +4539,8 @@ }); }, title: { - api: function() { - return "https://gdata.youtube.com/feeds/api/videos/" + this.name + "?alt=json&fields=title/text(),yt:noembed,app:control/yt:state/@reasonCode"; + api: function(uid) { + return "https://gdata.youtube.com/feeds/api/videos/" + uid + "?alt=json&fields=title/text(),yt:noembed,app:control/yt:state/@reasonCode"; }, text: function() { return JSON.parse(this.responseText).entry.title.$t; @@ -4451,8 +4564,8 @@ }); }, title: { - api: function() { - return "https://vimeo.com/api/oembed.json?url=http://vimeo.com/" + this.name; + api: function(uid) { + return "https://vimeo.com/api/oembed.json?url=http://vimeo.com/" + uid; }, text: function() { return JSON.parse(this.responseText).title; @@ -4482,7 +4595,7 @@ style: 'border: 0; width: auto; height: auto;', el: function() { return $.el('div', { - innerHTML: "<a target=_blank href='" + (this.getAttribute('data-originalURL')) + "'><img src='" + (this.getAttribute('data-originalURL')) + "'></a>" + innerHTML: "<a target=_blank href='" + this.dataset.originalurl + "'><img src='" + this.dataset.originalurl + "'></a>" }); } }, @@ -4505,8 +4618,8 @@ return div; }, title: { - api: function() { - return "//soundcloud.com/oembed?show_artwork=false&&maxwidth=500px&show_comments=false&format=json&url=https://www.soundcloud.com/" + this.name; + api: function(uid) { + return "//soundcloud.com/oembed?show_artwork=false&&maxwidth=500px&show_comments=false&format=json&url=https://www.soundcloud.com/" + uid; }, text: function() { return JSON.parse(this.responseText).title; @@ -4533,8 +4646,8 @@ }); }, title: { - api: function() { - return "https://api.github.com/gists/" + this.name; + api: function(uid) { + return "https://api.github.com/gists/" + uid; }, text: function() { var file, response; @@ -4556,76 +4669,6 @@ }); } } - }, - embedder: function(a) { - var callbacks, embed, key, match, service, titles, type, _ref; - - if (!Conf['Link Title']) { - return [a]; - } - titles = {}; - callbacks = function() { - var title; - - return a.textContent = (function() { - switch (this.status) { - case 200: - case 304: - title = "" + (service.text.call(this)); - embed.setAttribute('data-title', title); - titles[embed.name] = [title, Date.now()]; - $.set('CachedTitles', titles); - return title; - case 404: - return "[" + key + "] Not Found"; - case 403: - return "[" + key + "] Forbidden or Private"; - default: - return "[" + key + "] " + this.status + "'d"; - } - }).call(this); - }; - _ref = Linkify.types; - for (key in _ref) { - type = _ref[key]; - if (!(match = a.href.match(type.regExp))) { - continue; - } - embed = $.el('a', { - name: (a.name = match[1]), - option: match[2], - className: 'embedder', - href: 'javascript:;', - textContent: '(embed)' - }); - embed.setAttribute('data-service', key); - embed.setAttribute('data-originalURL', a.href); - $.addClass(a, "" + (embed.getAttribute('data-service'))); - $.on(embed, 'click', Linkify.toggle); - if (!Conf['Embedding']) { - embed.hidden = true; - } - if (Conf['Link Title'] && (service = type.title)) { - $.get('CachedTitles', {}, function(item) { - var err, title; - - titles = item['CachedTitles']; - if (title = titles[match[1]]) { - a.textContent = title[0]; - return embed.setAttribute('data-title', title[0]); - } else { - try { - return $.cache(service.api.call(a), callbacks); - } catch (_error) { - err = _error; - return a.innerHTML = "[" + key + "] <span class=warning>Title Link Blocked</span> (are you using NoScript?)</a>"; - } - } - }); - } - return [a, $.tn(' '), embed]; - } - return [a]; } }; diff --git a/builds/crx/script.js b/builds/crx/script.js index b56ccc5f4..77e9eb973 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -1,6 +1,6 @@ // Generated by CoffeeScript /* -* 4chan X - Version 1.2.24 - 2013-08-01 +* 4chan X - Version 1.2.24 - 2013-08-04 * * Licensed under the MIT license. * https://github.com/seaweedchan/4chan-x/blob/master/LICENSE @@ -4302,122 +4302,235 @@ if (g.VIEW === 'catalog' || !Conf['Linkify']) { return; } - this.regString = Conf['Allow False Positives'] ? /(\b([a-z]+:\/\/|[a-z]{3,}\.[-a-z0-9]+\.[a-z]+|[-a-z0-9]+\.[a-z]|[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+|[a-z]{3,}:[a-z0-9?]|[a-z0-9._%+-:]+@[a-z0-9.-]+\.[a-z0-9])[^\s'"]+)/gi : /(((magnet|mailto)\:|(www\.)|(news|(ht|f)tp(s?))\:\/\/){1}\S+)/gi; + this.regString = Conf['Allow False Positives'] ? /(\b([a-z]+:\/\/|[a-z]{3,}\.[-a-z0-9]+\.[a-z]|[-a-z0-9]+\.[a-z]|[0-9]+\.[0-9]+\.[0-9]+\.[0-9]|[a-z]{3,}:[a-z0-9?]|[\S]+@[a-z0-9.-]+\.[a-z0-9])[^\s'"]+)/gi : /(((magnet|mailto)\:|(www\.)|(news|(ht|f)tp(s?))\:\/\/){1}\S+)/gi; if (Conf['Comment Expansion']) { ExpandComment.callbacks.push(this.node); } + if (Conf['Title Link']) { + $.sync('CachedTitles', Linkify.titleSync); + } return Post.prototype.callbacks.push({ name: 'Linkify', cb: this.node }); }, - cypher: $.el('div'), node: function() { - var a, child, cypher, cypherText, data, embed, embedder, embeds, i, index, len, link, links, lookahead, name, next, node, nodes, snapshot, spoiler, text, _i, _j, _k, _l, _len, _len1, _len2, _len3, _len4, _m, _ref, _ref1, _ref2; + var data, embedder, i, len, node, range, snapshot, _i, _j, _len, _len1, _ref, _ref1; if (this.isClone && Conf['Embedding']) { _ref = $$('.embedder', this.nodes.comment); for (_i = 0, _len = _ref.length; _i < _len; _i++) { embedder = _ref[_i]; - $.on(embedder, "click", Linkify.toggle); + $.on(embedder, "click", Linkify.cb.toggle); } return; } snapshot = $.X('.//text()', this.nodes.comment); - cypher = Linkify.cypher; i = -1; len = snapshot.snapshotLength; while (++i < len) { - nodes = $.frag(); node = snapshot.snapshotItem(i); data = node.data; - if (!(node.parentNode && Linkify.regString.test(data))) { - continue; + if (Linkify.regString.test(data)) { + Linkify.regString.lastIndex = 0; + Linkify.gatherLinks(node, this); } - Linkify.regString.lastIndex = 0; - cypherText = []; - if (next = node.nextSibling) { - cypher.textContent = node.textContent; - cypherText[0] = cypher.innerHTML; - while ((next.nodeName.toLowerCase() === 'wbr' || next.nodeName.toLowerCase() === 's') && (lookahead = next.nextSibling) && ((name = lookahead.nodeName) === "#text" || name.toLowerCase() === 'br')) { - cypher.textContent = lookahead.textContent; - cypherText.push((spoiler = next.innerHTML) ? "<s>" + (spoiler.replace(/</g, ' <')) + "</s>" : '<wbr>'); - cypherText.push(cypher.innerHTML); - $.rm(next); - next = lookahead.nextSibling; - if (lookahead.nodeName === "#text") { - $.rm(lookahead); - } - if (!next) { - break; - } - } - } - if (cypherText.length) { - data = cypherText.join(''); - } - links = data.match(Linkify.regString); - for (_j = 0, _len1 = links.length; _j < _len1; _j++) { - link = links[_j]; - index = data.indexOf(link); - if (text = data.slice(0, index)) { - cypher.innerHTML = text; - _ref1 = __slice.call(cypher.childNodes); - for (_k = 0, _len2 = _ref1.length; _k < _len2; _k++) { - child = _ref1[_k]; - $.add(nodes, child); - } - } - cypher.innerHTML = (link.indexOf(':') < 0 ? (link.indexOf('@') > 0 ? 'mailto:' + link : 'http://' + link) : link).replace(/<(wbr|s|\/s)>/g, ''); - a = $.el('a', { - innerHTML: link, - className: 'linkify', - rel: 'nofollow noreferrer', - target: '_blank', - href: cypher.textContent - }); - $.add(nodes, Linkify.embedder(a)); - data = data.slice(index + link.length); - } - if (data) { - cypher.innerHTML = data; - _ref2 = __slice.call(cypher.childNodes); - for (_l = 0, _len3 = _ref2.length; _l < _len3; _l++) { - child = _ref2[_l]; - $.add(nodes, child); - } - } - $.replace(node, nodes); } - if (Conf['Auto-embed']) { - embeds = $$('.embedder', this.nodes.comment); - for (_m = 0, _len4 = embeds.length; _m < _len4; _m++) { - embed = embeds[_m]; - embed.click(); + if (!(Conf['Embedding'] || Conf['Link Title'])) { + return; + } + _ref1 = this.nodes.links; + for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { + range = _ref1[_j]; + if (data = Linkify.services(range)) { + if (Conf['Embedding']) { + Linkify.embed(data); + } + if (Conf['Link Title']) { + Linkify.title(data); + } } } }, - toggle: function() { - var el, embed, style, type, url; + gatherLinks: function(node, post) { + var data, index, len, len2, link, links, match, range, _i, _len, _ref; + + data = node.data; + len = data.length; + links = []; + while ((match = Linkify.regString.exec(data))) { + index = match.index; + link = match[0]; + len2 = index + link.length; + if (len - len2 === 0) { + break; + } + range = document.createRange(); + range.setStart(node, index); + range.setEnd(node, len2); + links.push(range); + } + if (match) { + Linkify.seek(match, node, post); + } + _ref = links.reverse(); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + range = _ref[_i]; + Linkify.makeLink(range, post); + } + }, + seek: function(match, node, post) { + var data, index, link, next, range, result; + + index = match.index; + link = match[0]; + range = document.createRange(); + range.setStart(node, index); + while ((next = node.nextSibling) && next.nodeName !== 'BR') { + node = next; + data = node.data; + if (result = /[\s'"]/.exec(data)) { + range.setEnd(node, result.index); + } + } + if (range.collapsed) { + if (node.nodeName === 'WBR') { + node = node.previousSibling; + } + range.setEnd(node, node.length); + } + return Linkify.makeLink(range, post); + }, + makeLink: function(range, post) { + var a, link; + + link = range.toString(); + link = link.contains(':') ? link : (link.contains('@') ? 'mailto:' : 'http://') + link; + a = $.el('a', { + className: 'linkify', + rel: 'nofollow noreferrer', + target: '_blank', + href: link + }); + range.surroundContents(a); + post.nodes.links.push(a); + }, + services: function(link) { + var href, key, match, type, _ref; + + href = link.href; + _ref = Linkify.types; + for (key in _ref) { + type = _ref[key]; + if (!(match = type.regExp.exec(href))) { + continue; + } + return [key, match[1], match[2], link]; + } + }, + embed: function(data) { + var embed, key, link, options, uid; + + key = data[0], uid = data[1], options = data[2], link = data[3]; + embed = $.el('a', { + name: uid, + option: options, + className: 'embedder', + href: 'javascript:;', + textContent: '(embed)' + }); + embed.dataset.service = key; + embed.dataset.originalurl = link.href; + $.addClass(link, "" + embed.dataset.service); + $.on(embed, 'click', Linkify.cb.toggle); + return $.after(link, [$.tn(' '), embed]); + }, + title: function(data) { + var err, key, link, options, service, title, titles, uid; + + key = data[0], uid = data[1], options = data[2], link = data[3]; + if (!(service = Linkify.types[key].title)) { + return; + } + titles = Conf['CachedTitles']; + if (title = titles[uid]) { + link.textContent = title[0]; + if (Conf['Embedding']) { + return link.nextElementSibling.dataset.title = title[0]; + } + } else { + try { + $.cache(service.api(uid), function() { + return title = Linkify.cb.title.apply(this, [data]); + }); + } catch (_error) { + err = _error; + link.innerHTML = "[" + key + "] <span class=warning>Title Link Blocked</span> (are you using NoScript?)</a>"; + return; + } + if (title) { + titles[uid] = [title, Date.now()]; + return $.set('CachedTitles', titles); + } + } + }, + titleSync: function(value) { + return Conf['CachedTitles'] = value; + }, + cb: { + toggle: function() { + var el, embed; + + embed = this.previousElementSibling; + el = !this.className.contains("embedded") ? Linkify.cb.embed(this) : Linkify.cb.unembed(this); + $.replace(embed, el); + return $.toggleClass(this, 'embedded'); + }, + embed: function(a) { + var el, style, type; + + el = (type = Linkify.types[a.dataset.service]).el.call(a); + el.style.cssText = (style = type.style) ? style : "border: 0; width: 640px; height: 390px"; + a.textContent = '(unembed)'; + return el; + }, + unembed: function(a) { + var el, url; - embed = this.previousElementSibling; - if (this.className.contains("embedded")) { el = $.el('a', { rel: 'nofollow noreferrer', target: 'blank', className: 'linkify', - href: url = this.getAttribute("data-originalURL"), - textContent: this.getAttribute("data-title") || url + href: url = a.dataset.originalurl, + textContent: a.dataset.title || url }); - this.textContent = '(embed)'; - $.addClass(el, "" + (this.getAttribute('data-service'))); - } else { - el = (type = Linkify.types[this.getAttribute("data-service")]).el.call(this); - el.style.cssText = (style = type.style) ? style : "border: 0; width: 640px; height: 390px"; - this.textContent = '(unembed)'; + a.textContent = '(embed)'; + $.addClass(el, "" + a.dataset.service); + return el; + }, + title: function(data) { + var key, link, options, service, text, uid; + + key = data[0], uid = data[1], options = data[2], link = data[3]; + service = Linkify.types[key].title; + return link.textContent = (function() { + switch (this.status) { + case 200: + case 304: + text = "" + (service.text.call(this)); + if (Conf['Embedding']) { + link.nextElementSibling.dataset.title = text; + } + return text; + case 404: + return "[" + key + "] Not Found"; + case 403: + return "[" + key + "] Forbidden or Private"; + default: + return "[" + key + "] " + this.status + "'d"; + } + }).call(this); } - $.replace(embed, el); - return $.toggleClass(this, 'embedded'); }, types: { YouTube: { @@ -4428,8 +4541,8 @@ }); }, title: { - api: function() { - return "https://gdata.youtube.com/feeds/api/videos/" + this.name + "?alt=json&fields=title/text(),yt:noembed,app:control/yt:state/@reasonCode"; + api: function(uid) { + return "https://gdata.youtube.com/feeds/api/videos/" + uid + "?alt=json&fields=title/text(),yt:noembed,app:control/yt:state/@reasonCode"; }, text: function() { return JSON.parse(this.responseText).entry.title.$t; @@ -4453,8 +4566,8 @@ }); }, title: { - api: function() { - return "https://vimeo.com/api/oembed.json?url=http://vimeo.com/" + this.name; + api: function(uid) { + return "https://vimeo.com/api/oembed.json?url=http://vimeo.com/" + uid; }, text: function() { return JSON.parse(this.responseText).title; @@ -4484,7 +4597,7 @@ style: 'border: 0; width: auto; height: auto;', el: function() { return $.el('div', { - innerHTML: "<a target=_blank href='" + (this.getAttribute('data-originalURL')) + "'><img src='" + (this.getAttribute('data-originalURL')) + "'></a>" + innerHTML: "<a target=_blank href='" + this.dataset.originalurl + "'><img src='" + this.dataset.originalurl + "'></a>" }); } }, @@ -4507,8 +4620,8 @@ return div; }, title: { - api: function() { - return "//soundcloud.com/oembed?show_artwork=false&&maxwidth=500px&show_comments=false&format=json&url=https://www.soundcloud.com/" + this.name; + api: function(uid) { + return "//soundcloud.com/oembed?show_artwork=false&&maxwidth=500px&show_comments=false&format=json&url=https://www.soundcloud.com/" + uid; }, text: function() { return JSON.parse(this.responseText).title; @@ -4535,8 +4648,8 @@ }); }, title: { - api: function() { - return "https://api.github.com/gists/" + this.name; + api: function(uid) { + return "https://api.github.com/gists/" + uid; }, text: function() { var file, response; @@ -4558,76 +4671,6 @@ }); } } - }, - embedder: function(a) { - var callbacks, embed, key, match, service, titles, type, _ref; - - if (!Conf['Link Title']) { - return [a]; - } - titles = {}; - callbacks = function() { - var title; - - return a.textContent = (function() { - switch (this.status) { - case 200: - case 304: - title = "" + (service.text.call(this)); - embed.setAttribute('data-title', title); - titles[embed.name] = [title, Date.now()]; - $.set('CachedTitles', titles); - return title; - case 404: - return "[" + key + "] Not Found"; - case 403: - return "[" + key + "] Forbidden or Private"; - default: - return "[" + key + "] " + this.status + "'d"; - } - }).call(this); - }; - _ref = Linkify.types; - for (key in _ref) { - type = _ref[key]; - if (!(match = a.href.match(type.regExp))) { - continue; - } - embed = $.el('a', { - name: (a.name = match[1]), - option: match[2], - className: 'embedder', - href: 'javascript:;', - textContent: '(embed)' - }); - embed.setAttribute('data-service', key); - embed.setAttribute('data-originalURL', a.href); - $.addClass(a, "" + (embed.getAttribute('data-service'))); - $.on(embed, 'click', Linkify.toggle); - if (!Conf['Embedding']) { - embed.hidden = true; - } - if (Conf['Link Title'] && (service = type.title)) { - $.get('CachedTitles', {}, function(item) { - var err, title; - - titles = item['CachedTitles']; - if (title = titles[match[1]]) { - a.textContent = title[0]; - return embed.setAttribute('data-title', title[0]); - } else { - try { - return $.cache(service.api.call(a), callbacks); - } catch (_error) { - err = _error; - return a.innerHTML = "[" + key + "] <span class=warning>Title Link Blocked</span> (are you using NoScript?)</a>"; - } - } - }); - } - return [a, $.tn(' '), embed]; - } - return [a]; } }; diff --git a/src/Linkification/Linkify.coffee b/src/Linkification/Linkify.coffee index 776cb059e..4938a532c 100644 --- a/src/Linkification/Linkify.coffee +++ b/src/Linkification/Linkify.coffee @@ -2,20 +2,20 @@ Linkify = init: -> return if g.VIEW is 'catalog' or not Conf['Linkify'] - @regString = if Conf['Allow False Positives'] + @regString = if Conf['Allow False Positives'] ///( \b( [a-z]+:// | - [a-z]{3,}\.[-a-z0-9]+\.[a-z]+ + [a-z]{3,}\.[-a-z0-9]+\.[a-z] | [-a-z0-9]+\.[a-z] | - [0-9]+\.[0-9]+\.[0-9]+\.[0-9]+ + [0-9]+\.[0-9]+\.[0-9]+\.[0-9] | [a-z]{3,}:[a-z0-9?] | - [a-z0-9._%+-:]+@[a-z0-9.-]+\.[a-z0-9] + [\S]+@[a-z0-9.-]+\.[a-z0-9] ) [^\s'"]+ )///gi @@ -25,115 +25,170 @@ Linkify = if Conf['Comment Expansion'] ExpandComment.callbacks.push @node + if Conf['Title Link'] + $.sync 'CachedTitles', Linkify.titleSync + Post::callbacks.push name: 'Linkify' cb: @node - cypher: $.el 'div' - node: -> if @isClone and Conf['Embedding'] for embedder in $$ '.embedder', @nodes.comment - $.on embedder, "click", Linkify.toggle + $.on embedder, "click", Linkify.cb.toggle return + snapshot = $.X './/text()', @nodes.comment - cypher = Linkify.cypher - i = -1 - len = snapshot.snapshotLength + i = -1 + len = snapshot.snapshotLength while ++i < len - nodes = $.frag() - node = snapshot.snapshotItem i - data = node.data + node = snapshot.snapshotItem i + data = node.data - # Test for valid links + if Linkify.regString.test data + Linkify.regString.lastIndex = 0 + Linkify.gatherLinks node, @ - continue unless node.parentNode and Linkify.regString.test data + return unless Conf['Embedding'] or Conf['Link Title'] - Linkify.regString.lastIndex = 0 + for range in @nodes.links + if data = Linkify.services range + Linkify.embed data if Conf['Embedding'] + Linkify.title data if Conf['Link Title'] - cypherText = [] - - if next = node.nextSibling - cypher.textContent = node.textContent - cypherText[0] = cypher.innerHTML - - while (next.nodeName.toLowerCase() is 'wbr' or next.nodeName.toLowerCase() is 's') and (lookahead = next.nextSibling) and ((name = lookahead.nodeName) is "#text" or name.toLowerCase() is 'br') - cypher.textContent = lookahead.textContent - - cypherText.push if spoiler = next.innerHTML then "<s>#{spoiler.replace /</g, ' <'}</s>" else '<wbr>' - cypherText.push cypher.innerHTML - - $.rm next - next = lookahead.nextSibling - $.rm lookahead if lookahead.nodeName is "#text" - - unless next - break - - if cypherText.length - data = cypherText.join '' - - links = data.match Linkify.regString - - for link in links - index = data.indexOf link - - if text = data[...index] - # press button get bacon - cypher.innerHTML = text - for child in [cypher.childNodes...] - $.add nodes, child - - cypher.innerHTML = (if link.indexOf(':') < 0 then (if link.indexOf('@') > 0 then 'mailto:' + link else 'http://' + link) else link).replace /<(wbr|s|\/s)>/g, '' - - a = $.el 'a', - innerHTML: link - className: 'linkify' - rel: 'nofollow noreferrer' - target: '_blank' - href: cypher.textContent - - $.add nodes, Linkify.embedder a - - data = data[index + link.length..] - - if data - # Potential text after the last valid link. - cypher.innerHTML = data - - # Convert <wbr> into elements - for child in [cypher.childNodes...] - $.add nodes, child - - $.replace node, nodes - - if Conf['Auto-embed'] - embeds = $$ '.embedder', @nodes.comment - for embed in embeds - embed.click() return - toggle: -> - # We setup the link to be replaced by the embedded video - embed = @previousElementSibling + gatherLinks: (node, post) -> + {data} = node + len = data.length + links = [] - # Unembed. - if @className.contains "embedded" - # Recreate the original link. - el = $.el 'a', - rel: 'nofollow noreferrer' - target: 'blank' - className: 'linkify' - href: url = @getAttribute("data-originalURL") - textContent: @getAttribute("data-title") or url + while (match = Linkify.regString.exec data) + {index} = match + link = match[0] + len2 = index + link.length - @textContent = '(embed)' - $.addClass el, "#{@getAttribute 'data-service'}" + break if len - len2 is 0 + range = document.createRange(); + range.setStart node, index + range.setEnd node, len2 + links.push range + + if match + Linkify.seek match, node, post + + for range in links.reverse() + Linkify.makeLink range, post + + return + + seek: (match, node, post) -> + {index} = match + link = match[0] + range = document.createRange() + range.setStart node, index + + while (next = node.nextSibling) and next.nodeName isnt 'BR' + node = next + data = node.data + if result = /[\s'"]/.exec data + range.setEnd node, result.index + + if range.collapsed + if node.nodeName is 'WBR' + node = node.previousSibling + range.setEnd node, node.length + + Linkify.makeLink range, post + + makeLink: (range, post) -> + link = range.toString() + link = + if link.contains ':' + link + else ( + if link.contains '@' + 'mailto:' + else + 'http://' + ) + link + + a = $.el 'a', + className: 'linkify' + rel: 'nofollow noreferrer' + target: '_blank' + href: link + range.surroundContents a + post.nodes.links.push a + return + + services: (link) -> + href = link.href + + for key, type of Linkify.types + continue unless match = type.regExp.exec href + return [key, match[1], match[2], link] + + return + + embed: (data) -> + [key, uid, options, link] = data + embed = $.el 'a', + name: uid + option: options + className: 'embedder' + href: 'javascript:;' + textContent: '(embed)' + + embed.dataset.service = key + embed.dataset.originalurl = link.href + + $.addClass link, "#{embed.dataset.service}" + + $.on embed, 'click', Linkify.cb.toggle + $.after link, [$.tn(' '), embed] + + title: (data) -> + [key, uid, options, link] = data + return unless service = Linkify.types[key].title + titles = Conf['CachedTitles'] + if title = titles[uid] + link.textContent = title[0] + if Conf['Embedding'] + link.nextElementSibling.dataset.title = title[0] else + try + $.cache service.api(uid), -> + title = Linkify.cb.title.apply @, [data] + catch err + link.innerHTML = "[#{key}] <span class=warning>Title Link Blocked</span> (are you using NoScript?)</a>" + return + if title + titles[uid] = [title, Date.now()] + $.set 'CachedTitles', titles + + titleSync: (value) -> + Conf['CachedTitles'] = value + + cb: + toggle: -> + # We setup the link to be replaced by the embedded video + embed = @previousElementSibling + + # Unembed. + el = unless @className.contains "embedded" + Linkify.cb.embed @ + else + Linkify.cb.unembed @ + + $.replace embed, el + $.toggleClass @, 'embedded' + + embed: (a) -> # We create an element to embed - el = (type = Linkify.types[@getAttribute("data-service")]).el.call @ + el = (type = Linkify.types[a.dataset.service]).el.call a # Set style values. el.style.cssText = if style = type.style @@ -141,10 +196,39 @@ Linkify = else "border: 0; width: 640px; height: 390px" - @textContent = '(unembed)' + a.textContent = '(unembed)' - $.replace embed, el - $.toggleClass @, 'embedded' + return el + + unembed: (a) -> + # Recreate the original link. + el = $.el 'a', + rel: 'nofollow noreferrer' + target: 'blank' + className: 'linkify' + href: url = a.dataset.originalurl + textContent: a.dataset.title or url + + a.textContent = '(embed)' + $.addClass el, "#{a.dataset.service}" + + return el + + title: (data) -> + [key, uid, options, link] = data + service = Linkify.types[key].title + link.textContent = switch @status + when 200, 304 + text = "#{service.text.call @}" + if Conf['Embedding'] + link.nextElementSibling.dataset.title = text + text + when 404 + "[#{key}] Not Found" + when 403 + "[#{key}] Forbidden or Private" + else + "[#{key}] #{@status}'d" types: YouTube: @@ -153,7 +237,7 @@ Linkify = $.el 'iframe', src: "//www.youtube.com/embed/#{@name}#{if @option then '#' + @option else ''}?wmode=opaque" title: - api: -> "https://gdata.youtube.com/feeds/api/videos/#{@name}?alt=json&fields=title/text(),yt:noembed,app:control/yt:state/@reasonCode" + api: (uid) -> "https://gdata.youtube.com/feeds/api/videos/#{uid}?alt=json&fields=title/text(),yt:noembed,app:control/yt:state/@reasonCode" text: -> JSON.parse(@responseText).entry.title.$t Vocaroo: @@ -169,7 +253,7 @@ Linkify = $.el 'iframe', src: "//player.vimeo.com/video/#{@name}?wmode=opaque" title: - api: -> "https://vimeo.com/api/oembed.json?url=http://vimeo.com/#{@name}" + api: (uid) -> "https://vimeo.com/api/oembed.json?url=http://vimeo.com/#{uid}" text: -> JSON.parse(@responseText).title LiveLeak: @@ -191,7 +275,7 @@ Linkify = style: 'border: 0; width: auto; height: auto;' el: -> $.el 'div', - innerHTML: "<a target=_blank href='#{@getAttribute 'data-originalURL'}'><img src='#{@getAttribute 'data-originalURL'}'></a>" + innerHTML: "<a target=_blank href='#{@dataset.originalurl}'><img src='#{@dataset.originalurl}'></a>" SoundCloud: regExp: /.*(?:soundcloud.com\/|snd.sc\/)([^#\&\?]*).*/ @@ -208,7 +292,7 @@ Linkify = false) div title: - api: -> "//soundcloud.com/oembed?show_artwork=false&&maxwidth=500px&show_comments=false&format=json&url=https://www.soundcloud.com/#{@name}" + api: (uid) -> "//soundcloud.com/oembed?show_artwork=false&&maxwidth=500px&show_comments=false&format=json&url=https://www.soundcloud.com/#{uid}" text: -> JSON.parse(@responseText).title pastebin: @@ -224,7 +308,7 @@ Linkify = # Github doesn't allow embedding straight from the site, so we use an external site to bypass that. src: "http://www.purplegene.com/script?url=https://gist.github.com/#{@name}.js" title: - api: -> "https://api.github.com/gists/#{@name}" + api: (uid) -> "https://api.github.com/gists/#{uid}" text: -> response = JSON.parse(@responseText).files return file for file of response when response.hasOwnProperty file @@ -233,57 +317,4 @@ Linkify = regExp: /.*(?:paste.installgentoo.com\/view\/)([0-9a-z_]+)/ el: -> $.el 'iframe', - src: "http://paste.installgentoo.com/view/embed/#{@name}" - - embedder: (a) -> - return [a] unless Conf['Link Title'] - titles = {} - - callbacks = -> - a.textContent = switch @status - when 200, 304 - title = "#{service.text.call @}" - embed.setAttribute 'data-title', title - titles[embed.name] = [title, Date.now()] - $.set 'CachedTitles', titles - title - when 404 - "[#{key}] Not Found" - when 403 - "[#{key}] Forbidden or Private" - else - "[#{key}] #{@status}'d" - - for key, type of Linkify.types - continue unless match = a.href.match type.regExp - - embed = $.el 'a', - name: (a.name = match[1]) - option: match[2] - className: 'embedder' - href: 'javascript:;' - textContent: '(embed)' - - embed.setAttribute 'data-service', key - embed.setAttribute 'data-originalURL', a.href - $.addClass a, "#{embed.getAttribute 'data-service'}" - - $.on embed, 'click', Linkify.toggle - - unless Conf['Embedding'] - embed.hidden = true - - if Conf['Link Title'] and (service = type.title) - $.get 'CachedTitles', {}, (item) -> - titles = item['CachedTitles'] - if title = titles[match[1]] - a.textContent = title[0] - embed.setAttribute 'data-title', title[0] - else - try - $.cache service.api.call(a), callbacks - catch err - a.innerHTML = "[#{key}] <span class=warning>Title Link Blocked</span> (are you using NoScript?)</a>" - - return [a, $.tn(' '), embed] - return [a] \ No newline at end of file + src: "http://paste.installgentoo.com/view/embed/#{@name}" \ No newline at end of file From b60ddfe9075b1c1c379f3785e399b2f5f2854fa8 Mon Sep 17 00:00:00 2001 From: Zixaphir <zixaphirmoxphar@gmail.com> Date: Sun, 4 Aug 2013 18:55:10 -0700 Subject: [PATCH 102/172] I think this is safer. --- builds/4chan-X.user.js | 2 +- builds/crx/script.js | 2 +- src/Linkification/Linkify.coffee | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js index 8eafa30f9..2be937096 100644 --- a/builds/4chan-X.user.js +++ b/builds/4chan-X.user.js @@ -4300,7 +4300,7 @@ if (g.VIEW === 'catalog' || !Conf['Linkify']) { return; } - this.regString = Conf['Allow False Positives'] ? /(\b([a-z]+:\/\/|[a-z]{3,}\.[-a-z0-9]+\.[a-z]|[-a-z0-9]+\.[a-z]|[0-9]+\.[0-9]+\.[0-9]+\.[0-9]|[a-z]{3,}:[a-z0-9?]|[\S]+@[a-z0-9.-]+\.[a-z0-9])[^\s'"]+)/gi : /(((magnet|mailto)\:|(www\.)|(news|(ht|f)tp(s?))\:\/\/){1}\S+)/gi; + this.regString = Conf['Allow False Positives'] ? /(\b([a-z]+:\/\/|[a-z]{3,}\.[-a-z0-9]+\.[a-z]|[-a-z0-9]+\.[a-z]|[0-9]+\.[0-9]+\.[0-9]+\.[0-9]|[a-z]{3,}:[a-z0-9?]|[^\s@]+@[a-z0-9.-]+\.[a-z0-9])[^\s'"]+)/gi : /(((magnet|mailto)\:|(www\.)|(news|(ht|f)tp(s?))\:\/\/){1}\S+)/gi; if (Conf['Comment Expansion']) { ExpandComment.callbacks.push(this.node); } diff --git a/builds/crx/script.js b/builds/crx/script.js index 77e9eb973..c2be42ede 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -4302,7 +4302,7 @@ if (g.VIEW === 'catalog' || !Conf['Linkify']) { return; } - this.regString = Conf['Allow False Positives'] ? /(\b([a-z]+:\/\/|[a-z]{3,}\.[-a-z0-9]+\.[a-z]|[-a-z0-9]+\.[a-z]|[0-9]+\.[0-9]+\.[0-9]+\.[0-9]|[a-z]{3,}:[a-z0-9?]|[\S]+@[a-z0-9.-]+\.[a-z0-9])[^\s'"]+)/gi : /(((magnet|mailto)\:|(www\.)|(news|(ht|f)tp(s?))\:\/\/){1}\S+)/gi; + this.regString = Conf['Allow False Positives'] ? /(\b([a-z]+:\/\/|[a-z]{3,}\.[-a-z0-9]+\.[a-z]|[-a-z0-9]+\.[a-z]|[0-9]+\.[0-9]+\.[0-9]+\.[0-9]|[a-z]{3,}:[a-z0-9?]|[^\s@]+@[a-z0-9.-]+\.[a-z0-9])[^\s'"]+)/gi : /(((magnet|mailto)\:|(www\.)|(news|(ht|f)tp(s?))\:\/\/){1}\S+)/gi; if (Conf['Comment Expansion']) { ExpandComment.callbacks.push(this.node); } diff --git a/src/Linkification/Linkify.coffee b/src/Linkification/Linkify.coffee index 4938a532c..599a11245 100644 --- a/src/Linkification/Linkify.coffee +++ b/src/Linkification/Linkify.coffee @@ -15,7 +15,7 @@ Linkify = | [a-z]{3,}:[a-z0-9?] | - [\S]+@[a-z0-9.-]+\.[a-z0-9] + [^\s@]+@[a-z0-9.-]+\.[a-z0-9] ) [^\s'"]+ )///gi From f41a40c1ad564beee56f6d53857bd70eb7eb2c61 Mon Sep 17 00:00:00 2001 From: Zixaphir <zixaphirmoxphar@gmail.com> Date: Sun, 4 Aug 2013 19:47:07 -0700 Subject: [PATCH 103/172] Forgot to add links to the post datatype --- builds/4chan-X.user.js | 1 + builds/crx/script.js | 1 + src/General/lib/post.class | 1 + 3 files changed, 3 insertions(+) diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js index d82d9b6d6..04d7f5e66 100644 --- a/builds/4chan-X.user.js +++ b/builds/4chan-X.user.js @@ -904,6 +904,7 @@ post: post, info: info, comment: $('.postMessage', post), + links: [], quotelinks: [], backlinks: info.getElementsByClassName('backlink') }; diff --git a/builds/crx/script.js b/builds/crx/script.js index fbb0c45b9..4f2213b40 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -915,6 +915,7 @@ post: post, info: info, comment: $('.postMessage', post), + links: [], quotelinks: [], backlinks: info.getElementsByClassName('backlink') }; diff --git a/src/General/lib/post.class b/src/General/lib/post.class index 57fdddcf2..2b5c26aa4 100644 --- a/src/General/lib/post.class +++ b/src/General/lib/post.class @@ -13,6 +13,7 @@ class Post post: post info: info comment: $ '.postMessage', post + links: [] quotelinks: [] backlinks: info.getElementsByClassName 'backlink' From 37cf68f43d6244b2647c088c091a06c1f668d368 Mon Sep 17 00:00:00 2001 From: Zixaphir <zixaphirmoxphar@gmail.com> Date: Sun, 4 Aug 2013 19:54:56 -0700 Subject: [PATCH 104/172] Update changelog --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fd1f993ec..0a3c64220 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,18 @@ **MayhemYDG**: - Fix impossibility to create new threads when in dead threads. - Fix flag filtering on /sp/ and /int/. +- Update archives. (with woxxy and proplex) - Minor fixes. +- Minor optimizations. + +**Zixaphir**: +- Linkifier Rewrite. +- Fix Quote Threading toggle. +- Minor optimizations. +- Minor fixes. + +**aeosynth**: +- Update Gruntfile.coffee. ### v1.2.25 *2013-08-04* From 1640ef5da127cc534fe440329d8fc9339fabf599 Mon Sep 17 00:00:00 2001 From: Zixaphir <zixaphirmoxphar@gmail.com> Date: Sun, 4 Aug 2013 23:05:01 -0700 Subject: [PATCH 105/172] Add cachedTitles to Conf --- builds/4chan-X.user.js | 1 + builds/crx/script.js | 1 + src/General/Main.coffee | 1 + 3 files changed, 3 insertions(+) diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js index 04d7f5e66..407f59774 100644 --- a/builds/4chan-X.user.js +++ b/builds/4chan-X.user.js @@ -10209,6 +10209,7 @@ }; } Conf['selectedArchives'] = {}; + Conf['CachedTitles'] = []; $.get(Conf, Main.initFeatures); $.on(d, '4chanMainInit', Main.initStyle); return $.asap((function() { diff --git a/builds/crx/script.js b/builds/crx/script.js index 4f2213b40..2c177fdce 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -10193,6 +10193,7 @@ }; } Conf['selectedArchives'] = {}; + Conf['CachedTitles'] = []; $.get(Conf, Main.initFeatures); $.on(d, '4chanMainInit', Main.initStyle); return $.asap((function() { diff --git a/src/General/Main.coffee b/src/General/Main.coffee index 71611a95c..41b2679ce 100644 --- a/src/General/Main.coffee +++ b/src/General/Main.coffee @@ -15,6 +15,7 @@ Main = for db in DataBoards Conf[db] = boards: {} Conf['selectedArchives'] = {} + Conf['CachedTitles'] = [] $.get Conf, Main.initFeatures From 9eda79c24c8b566200c16b8790217e6d61d118ce Mon Sep 17 00:00:00 2001 From: Zixaphir <zixaphirmoxphar@gmail.com> Date: Mon, 5 Aug 2013 00:05:02 -0700 Subject: [PATCH 106/172] Fix Linkify in Chrome Was skipping every other URL if they were on trailing lines --- builds/4chan-X.user.js | 3 ++- builds/crx/script.js | 3 ++- src/Linkification/Linkify.coffee | 2 ++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js index 407f59774..f5ce94ae5 100644 --- a/builds/4chan-X.user.js +++ b/builds/4chan-X.user.js @@ -18,7 +18,7 @@ // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwAgMAAAAqbBEUAAAACVBMVEUAAGcAAABmzDNZt9VtAAAAAXRSTlMAQObYZgAAAHFJREFUKFOt0LENACEIBdBv4Qju4wgWanEj3D6OcIVMKaitYHEU/jwTCQj8W75kiVCSBvdQ5/AvfVHBin11BgdRq3ysBgfwBDRrj3MCIA+oAQaku/Q1cNctrAmyDl577tOThYt/Y1RBM4DgOHzM0HFTAyLukH/cmRnqAAAAAElFTkSuQmCC // ==/UserScript== /* -* 4chan X - Version 1.2.25 - 2013-08-04 +* 4chan X - Version 1.2.25 - 2013-08-05 * * Licensed under the MIT license. * https://github.com/seaweedchan/4chan-x/blob/master/LICENSE @@ -4368,6 +4368,7 @@ range.setEnd(node, len2); links.push(range); } + Linkify.regString.lastIndex = 0; if (match) { Linkify.seek(match, node, post); } diff --git a/builds/crx/script.js b/builds/crx/script.js index 2c177fdce..8fd0d325e 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -1,6 +1,6 @@ // Generated by CoffeeScript /* -* 4chan X - Version 1.2.25 - 2013-08-04 +* 4chan X - Version 1.2.25 - 2013-08-05 * * Licensed under the MIT license. * https://github.com/seaweedchan/4chan-x/blob/master/LICENSE @@ -4373,6 +4373,7 @@ range.setEnd(node, len2); links.push(range); } + Linkify.regString.lastIndex = 0; if (match) { Linkify.seek(match, node, post); } diff --git a/src/Linkification/Linkify.coffee b/src/Linkification/Linkify.coffee index 599a11245..c32ec484e 100644 --- a/src/Linkification/Linkify.coffee +++ b/src/Linkification/Linkify.coffee @@ -76,6 +76,8 @@ Linkify = range.setEnd node, len2 links.push range + Linkify.regString.lastIndex = 0 + if match Linkify.seek match, node, post From d2eef4c4c5e2d0d4b0e2ee519c938baee620cf58 Mon Sep 17 00:00:00 2001 From: Zixaphir <zixaphirmoxphar@gmail.com> Date: Mon, 5 Aug 2013 00:30:32 -0700 Subject: [PATCH 107/172] Restore concat:meta --- Gruntfile.coffee | 7 +++++++ LICENSE | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Gruntfile.coffee b/Gruntfile.coffee index 74bb4e669..9dda11d9a 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -38,6 +38,12 @@ module.exports = (grunt) -> ] dest: 'tmp-<%= pkg.type %>/script.coffee' + meta: + options: concatOptions + files: + 'LICENSE': 'src/General/meta/banner.js', + 'latest.js': 'src/General/meta/latest.js' + crx: options: concatOptions files: @@ -75,6 +81,7 @@ module.exports = (grunt) -> concurrent: build: [ + 'concat:meta' 'build-crx' 'build-userscript' ] diff --git a/LICENSE b/LICENSE index dd8831faf..633bc8ac7 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,5 @@ /* -* 4chan X - Version 1.2.25 - 2013-08-04 +* 4chan X - Version 1.2.25 - 2013-08-05 * * Licensed under the MIT license. * https://github.com/seaweedchan/4chan-x/blob/master/LICENSE From 6cf7373b938cf8d4841198584d7b709b45f27396 Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Mon, 5 Aug 2013 21:19:20 +0200 Subject: [PATCH 108/172] Fix #1162. --- src/General/Build.coffee | 52 +++++++++++++++++++++++++++------------- 1 file changed, 35 insertions(+), 17 deletions(-) diff --git a/src/General/Build.coffee b/src/General/Build.coffee index 713e0586a..0d39bc343 100644 --- a/src/General/Build.coffee +++ b/src/General/Build.coffee @@ -27,6 +27,7 @@ Build = date: data.now dateUTC: data.time comment: data.com + capReps: data.capcode_replies # thread status isSticky: !!data.sticky isClosed: !!data.closed @@ -54,7 +55,7 @@ Build = postID, threadID, boardID name, capcode, tripcode, uniqueID, email, subject, flagCode, flagName, date, dateUTC isSticky, isClosed - comment + comment, capReps file } = o isOP = postID is threadID @@ -172,22 +173,39 @@ Build = else fileHTML = '' - tripcode = - if tripcode - " <span class=postertrip>#{tripcode}</span>" - else - '' + tripcode = if tripcode + " <span class=postertrip>#{tripcode}</span>" + else + '' - sticky = - if isSticky - " <img src=#{staticPath}sticky.gif alt=Sticky title=Sticky class=stickyIcon>" - else - '' - closed = - if isClosed - " <img src=#{staticPath}closed.gif alt=Closed title=Closed class=closedIcon>" - else - '' + sticky = if isSticky + " <img src=#{staticPath}sticky.gif alt=Sticky title=Sticky class=stickyIcon>" + else + '' + closed = if isClosed + " <img src=#{staticPath}closed.gif alt=Closed title=Closed class=closedIcon>" + else + '' + + capcodeReplies = '' + if capReps + generateCapcodeReplies = (capcodeType, array) -> + "<span class=smaller><span class=bold>#{ + switch capcodeType + when 'admin' + 'Administrator' + when 'mod' + 'Moderator' + when 'developer' + 'Developer' + } Repl#{if array.length > 1 then 'ies' else 'y'}:</span> #{ + array.map (ID) -> + "<a href='/#{boardID}/res/#{threadID}#p#{ID}' class=quotelink>>>#{ID}</a>" + .join ' ' + }</span><br>" + for capcodeType, array of capReps + capcodeReplies += generateCapcodeReplies capcodeType, array + capcodeReplies = "<br><br><span class=capcodeReplies>#{capcodeReplies}</span>" container = $.el 'div', id: "pc#{postID}" @@ -241,7 +259,7 @@ Build = (if isOP then '' else fileHTML) + - "<blockquote class=postMessage id=m#{postID}>#{comment or ''}</blockquote> " + + "<blockquote class=postMessage id=m#{postID}>#{comment or ''}#{capcodeReplies}</blockquote> " + '</div>' From 250323d304dece37c55fffdd5922fbe165318f14 Mon Sep 17 00:00:00 2001 From: Zixaphir <zixaphirmoxphar@gmail.com> Date: Mon, 5 Aug 2013 12:19:26 -0700 Subject: [PATCH 109/172] Vine --- builds/4chan-X.user.js | 155 ++++++++++++++++--------------- builds/crx/script.js | 155 ++++++++++++++++--------------- src/Linkification/Linkify.coffee | 123 ++++++++++++++---------- 3 files changed, 236 insertions(+), 197 deletions(-) diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js index f5ce94ae5..71277dcf5 100644 --- a/builds/4chan-X.user.js +++ b/builds/4chan-X.user.js @@ -4532,55 +4532,6 @@ } }, types: { - YouTube: { - regExp: /.*(?:youtu.be\/|youtube.*v=|youtube.*\/embed\/|youtube.*\/v\/|youtube.*videos\/)([^#\&\?]*)\??(t\=.*)?/, - el: function() { - return $.el('iframe', { - src: "//www.youtube.com/embed/" + this.name + (this.option ? '#' + this.option : '') + "?wmode=opaque" - }); - }, - title: { - api: function(uid) { - return "https://gdata.youtube.com/feeds/api/videos/" + uid + "?alt=json&fields=title/text(),yt:noembed,app:control/yt:state/@reasonCode"; - }, - text: function() { - return JSON.parse(this.responseText).entry.title.$t; - } - } - }, - Vocaroo: { - regExp: /.*(?:vocaroo.com\/)([^#\&\?]*).*/, - style: 'border: 0; width: 150px; height: 45px;', - el: function() { - return $.el('object', { - innerHTML: "<embed src='http://vocaroo.com/player.swf?playMediaID=" + (this.name.replace(/^i\//, '')) + "&autoplay=0' wmode='opaque' width='150' height='45' pluginspage='http://get.adobe.com/flashplayer/' type='application/x-shockwave-flash'></embed>" - }); - } - }, - Vimeo: { - regExp: /.*(?:vimeo.com\/)([^#\&\?]*).*/, - el: function() { - return $.el('iframe', { - src: "//player.vimeo.com/video/" + this.name + "?wmode=opaque" - }); - }, - title: { - api: function(uid) { - return "https://vimeo.com/api/oembed.json?url=http://vimeo.com/" + uid; - }, - text: function() { - return JSON.parse(this.responseText).title; - } - } - }, - LiveLeak: { - regExp: /.*(?:liveleak.com\/view.+i=)([0-9a-z_]+)/, - el: function() { - return $.el('object', { - innerHTML: "<embed src='http://www.liveleak.com/e/" + this.name + "?autostart=true' wmode='opaque' width='640' height='390' pluginspage='http://get.adobe.com/flashplayer/' type='application/x-shockwave-flash'></embed>" - }); - } - }, audio: { regExp: /(.*\.(mp3|ogg|wav))$/, el: function() { @@ -4591,6 +4542,31 @@ }); } }, + gist: { + regExp: /.*(?:gist.github.com.*\/)([^\/][^\/]*)$/, + el: function() { + var div; + + return div = $.el('iframe', { + src: "http://www.purplegene.com/script?url=https://gist.github.com/" + this.name + ".js" + }); + }, + title: { + api: function(uid) { + return "https://api.github.com/gists/" + uid; + }, + text: function() { + var file, response; + + response = JSON.parse(this.responseText).files; + for (file in response) { + if (response.hasOwnProperty(file)) { + return file; + } + } + } + } + }, image: { regExp: /(http|www).*\.(gif|png|jpg|jpeg|bmp)$/, style: 'border: 0; width: auto; height: auto;', @@ -4600,6 +4576,32 @@ }); } }, + InstallGentoo: { + regExp: /.*(?:paste.installgentoo.com\/view\/)([0-9a-z_]+)/, + el: function() { + return $.el('iframe', { + src: "http://paste.installgentoo.com/view/embed/" + this.name + }); + } + }, + LiveLeak: { + regExp: /.*(?:liveleak.com\/view.+i=)([0-9a-z_]+)/, + el: function() { + return $.el('object', { + innerHTML: "<embed src='http://www.liveleak.com/e/" + this.name + "?autostart=true' wmode='opaque' width='640' height='390' pluginspage='http://get.adobe.com/flashplayer/' type='application/x-shockwave-flash'></embed>" + }); + } + }, + pastebin: { + regExp: /.*(?:pastebin.com\/(?!u\/))([^#\&\?]*).*/, + el: function() { + var div; + + return div = $.el('iframe', { + src: "http://pastebin.com/embed_iframe.php?i=" + this.name + }); + } + }, SoundCloud: { regExp: /.*(?:soundcloud.com\/|snd.sc\/)([^#\&\?]*).*/, style: 'height: auto; width: 500px; display: inline-block;', @@ -4627,48 +4629,55 @@ } } }, - pastebin: { - regExp: /.*(?:pastebin.com\/(?!u\/))([^#\&\?]*).*/, + Vocaroo: { + regExp: /.*(?:vocaroo.com\/)([^#\&\?]*).*/, + style: 'border: 0; width: 150px; height: 45px;', el: function() { - var div; - - return div = $.el('iframe', { - src: "http://pastebin.com/embed_iframe.php?i=" + this.name + return $.el('object', { + innerHTML: "<embed src='http://vocaroo.com/player.swf?playMediaID=" + (this.name.replace(/^i\//, '')) + "&autoplay=0' wmode='opaque' width='150' height='45' pluginspage='http://get.adobe.com/flashplayer/' type='application/x-shockwave-flash'></embed>" }); } }, - gist: { - regExp: /.*(?:gist.github.com.*\/)([^\/][^\/]*)$/, + Vimeo: { + regExp: /.*(?:vimeo.com\/)([^#\&\?]*).*/, el: function() { - var div; - - return div = $.el('iframe', { - src: "http://www.purplegene.com/script?url=https://gist.github.com/" + this.name + ".js" + return $.el('iframe', { + src: "//player.vimeo.com/video/" + this.name + "?wmode=opaque" }); }, title: { api: function(uid) { - return "https://api.github.com/gists/" + uid; + return "https://vimeo.com/api/oembed.json?url=http://vimeo.com/" + uid; }, text: function() { - var file, response; - - response = JSON.parse(this.responseText).files; - for (file in response) { - if (response.hasOwnProperty(file)) { - return file; - } - } + return JSON.parse(this.responseText).title; } } }, - InstallGentoo: { - regExp: /.*(?:paste.installgentoo.com\/view\/)([0-9a-z_]+)/, + Vine: { + regExp: /.*(?:vine.co\/)([^#\&\?]*).*/, + style: 'border: none; width: 500px; height: 500px;', el: function() { return $.el('iframe', { - src: "http://paste.installgentoo.com/view/embed/" + this.name + src: "https://vine.co/" + this.name + "/card" }); } + }, + YouTube: { + regExp: /.*(?:youtu.be\/|youtube.*v=|youtube.*\/embed\/|youtube.*\/v\/|youtube.*videos\/)([^#\&\?]*)\??(t\=.*)?/, + el: function() { + return $.el('iframe', { + src: "//www.youtube.com/embed/" + this.name + (this.option ? '#' + this.option : '') + "?wmode=opaque" + }); + }, + title: { + api: function(uid) { + return "https://gdata.youtube.com/feeds/api/videos/" + uid + "?alt=json&fields=title/text(),yt:noembed,app:control/yt:state/@reasonCode"; + }, + text: function() { + return JSON.parse(this.responseText).entry.title.$t; + } + } } } }; diff --git a/builds/crx/script.js b/builds/crx/script.js index 8fd0d325e..7fe3cdbf8 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -4537,55 +4537,6 @@ } }, types: { - YouTube: { - regExp: /.*(?:youtu.be\/|youtube.*v=|youtube.*\/embed\/|youtube.*\/v\/|youtube.*videos\/)([^#\&\?]*)\??(t\=.*)?/, - el: function() { - return $.el('iframe', { - src: "//www.youtube.com/embed/" + this.name + (this.option ? '#' + this.option : '') + "?wmode=opaque" - }); - }, - title: { - api: function(uid) { - return "https://gdata.youtube.com/feeds/api/videos/" + uid + "?alt=json&fields=title/text(),yt:noembed,app:control/yt:state/@reasonCode"; - }, - text: function() { - return JSON.parse(this.responseText).entry.title.$t; - } - } - }, - Vocaroo: { - regExp: /.*(?:vocaroo.com\/)([^#\&\?]*).*/, - style: 'border: 0; width: 150px; height: 45px;', - el: function() { - return $.el('object', { - innerHTML: "<embed src='http://vocaroo.com/player.swf?playMediaID=" + (this.name.replace(/^i\//, '')) + "&autoplay=0' wmode='opaque' width='150' height='45' pluginspage='http://get.adobe.com/flashplayer/' type='application/x-shockwave-flash'></embed>" - }); - } - }, - Vimeo: { - regExp: /.*(?:vimeo.com\/)([^#\&\?]*).*/, - el: function() { - return $.el('iframe', { - src: "//player.vimeo.com/video/" + this.name + "?wmode=opaque" - }); - }, - title: { - api: function(uid) { - return "https://vimeo.com/api/oembed.json?url=http://vimeo.com/" + uid; - }, - text: function() { - return JSON.parse(this.responseText).title; - } - } - }, - LiveLeak: { - regExp: /.*(?:liveleak.com\/view.+i=)([0-9a-z_]+)/, - el: function() { - return $.el('object', { - innerHTML: "<embed src='http://www.liveleak.com/e/" + this.name + "?autostart=true' wmode='opaque' width='640' height='390' pluginspage='http://get.adobe.com/flashplayer/' type='application/x-shockwave-flash'></embed>" - }); - } - }, audio: { regExp: /(.*\.(mp3|ogg|wav))$/, el: function() { @@ -4596,6 +4547,31 @@ }); } }, + gist: { + regExp: /.*(?:gist.github.com.*\/)([^\/][^\/]*)$/, + el: function() { + var div; + + return div = $.el('iframe', { + src: "http://www.purplegene.com/script?url=https://gist.github.com/" + this.name + ".js" + }); + }, + title: { + api: function(uid) { + return "https://api.github.com/gists/" + uid; + }, + text: function() { + var file, response; + + response = JSON.parse(this.responseText).files; + for (file in response) { + if (response.hasOwnProperty(file)) { + return file; + } + } + } + } + }, image: { regExp: /(http|www).*\.(gif|png|jpg|jpeg|bmp)$/, style: 'border: 0; width: auto; height: auto;', @@ -4605,6 +4581,32 @@ }); } }, + InstallGentoo: { + regExp: /.*(?:paste.installgentoo.com\/view\/)([0-9a-z_]+)/, + el: function() { + return $.el('iframe', { + src: "http://paste.installgentoo.com/view/embed/" + this.name + }); + } + }, + LiveLeak: { + regExp: /.*(?:liveleak.com\/view.+i=)([0-9a-z_]+)/, + el: function() { + return $.el('object', { + innerHTML: "<embed src='http://www.liveleak.com/e/" + this.name + "?autostart=true' wmode='opaque' width='640' height='390' pluginspage='http://get.adobe.com/flashplayer/' type='application/x-shockwave-flash'></embed>" + }); + } + }, + pastebin: { + regExp: /.*(?:pastebin.com\/(?!u\/))([^#\&\?]*).*/, + el: function() { + var div; + + return div = $.el('iframe', { + src: "http://pastebin.com/embed_iframe.php?i=" + this.name + }); + } + }, SoundCloud: { regExp: /.*(?:soundcloud.com\/|snd.sc\/)([^#\&\?]*).*/, style: 'height: auto; width: 500px; display: inline-block;', @@ -4632,48 +4634,55 @@ } } }, - pastebin: { - regExp: /.*(?:pastebin.com\/(?!u\/))([^#\&\?]*).*/, + Vocaroo: { + regExp: /.*(?:vocaroo.com\/)([^#\&\?]*).*/, + style: 'border: 0; width: 150px; height: 45px;', el: function() { - var div; - - return div = $.el('iframe', { - src: "http://pastebin.com/embed_iframe.php?i=" + this.name + return $.el('object', { + innerHTML: "<embed src='http://vocaroo.com/player.swf?playMediaID=" + (this.name.replace(/^i\//, '')) + "&autoplay=0' wmode='opaque' width='150' height='45' pluginspage='http://get.adobe.com/flashplayer/' type='application/x-shockwave-flash'></embed>" }); } }, - gist: { - regExp: /.*(?:gist.github.com.*\/)([^\/][^\/]*)$/, + Vimeo: { + regExp: /.*(?:vimeo.com\/)([^#\&\?]*).*/, el: function() { - var div; - - return div = $.el('iframe', { - src: "http://www.purplegene.com/script?url=https://gist.github.com/" + this.name + ".js" + return $.el('iframe', { + src: "//player.vimeo.com/video/" + this.name + "?wmode=opaque" }); }, title: { api: function(uid) { - return "https://api.github.com/gists/" + uid; + return "https://vimeo.com/api/oembed.json?url=http://vimeo.com/" + uid; }, text: function() { - var file, response; - - response = JSON.parse(this.responseText).files; - for (file in response) { - if (response.hasOwnProperty(file)) { - return file; - } - } + return JSON.parse(this.responseText).title; } } }, - InstallGentoo: { - regExp: /.*(?:paste.installgentoo.com\/view\/)([0-9a-z_]+)/, + Vine: { + regExp: /.*(?:vine.co\/)([^#\&\?]*).*/, + style: 'border: none; width: 500px; height: 500px;', el: function() { return $.el('iframe', { - src: "http://paste.installgentoo.com/view/embed/" + this.name + src: "https://vine.co/" + this.name + "/card" }); } + }, + YouTube: { + regExp: /.*(?:youtu.be\/|youtube.*v=|youtube.*\/embed\/|youtube.*\/v\/|youtube.*videos\/)([^#\&\?]*)\??(t\=.*)?/, + el: function() { + return $.el('iframe', { + src: "//www.youtube.com/embed/" + this.name + (this.option ? '#' + this.option : '') + "?wmode=opaque" + }); + }, + title: { + api: function(uid) { + return "https://gdata.youtube.com/feeds/api/videos/" + uid + "?alt=json&fields=title/text(),yt:noembed,app:control/yt:state/@reasonCode"; + }, + text: function() { + return JSON.parse(this.responseText).entry.title.$t; + } + } } } }; diff --git a/src/Linkification/Linkify.coffee b/src/Linkification/Linkify.coffee index c32ec484e..48dbb705a 100644 --- a/src/Linkification/Linkify.coffee +++ b/src/Linkification/Linkify.coffee @@ -233,52 +233,51 @@ Linkify = "[#{key}] #{@status}'d" types: - YouTube: - regExp: /.*(?:youtu.be\/|youtube.*v=|youtube.*\/embed\/|youtube.*\/v\/|youtube.*videos\/)([^#\&\?]*)\??(t\=.*)?/ - el: -> - $.el 'iframe', - src: "//www.youtube.com/embed/#{@name}#{if @option then '#' + @option else ''}?wmode=opaque" - title: - api: (uid) -> "https://gdata.youtube.com/feeds/api/videos/#{uid}?alt=json&fields=title/text(),yt:noembed,app:control/yt:state/@reasonCode" - text: -> JSON.parse(@responseText).entry.title.$t - - Vocaroo: - regExp: /.*(?:vocaroo.com\/)([^#\&\?]*).*/ - style: 'border: 0; width: 150px; height: 45px;' - el: -> - $.el 'object', - innerHTML: "<embed src='http://vocaroo.com/player.swf?playMediaID=#{@name.replace /^i\//, ''}&autoplay=0' wmode='opaque' width='150' height='45' pluginspage='http://get.adobe.com/flashplayer/' type='application/x-shockwave-flash'></embed>" - - Vimeo: - regExp: /.*(?:vimeo.com\/)([^#\&\?]*).*/ - el: -> - $.el 'iframe', - src: "//player.vimeo.com/video/#{@name}?wmode=opaque" - title: - api: (uid) -> "https://vimeo.com/api/oembed.json?url=http://vimeo.com/#{uid}" - text: -> JSON.parse(@responseText).title - - LiveLeak: - regExp: /.*(?:liveleak.com\/view.+i=)([0-9a-z_]+)/ - el: -> - $.el 'object', - innerHTML: "<embed src='http://www.liveleak.com/e/#{@name}?autostart=true' wmode='opaque' width='640' height='390' pluginspage='http://get.adobe.com/flashplayer/' type='application/x-shockwave-flash'></embed>" - audio: - regExp: /(.*\.(mp3|ogg|wav))$/ + regExp: /(.*\.(mp3|ogg|wav))$/ el: -> $.el 'audio', controls: 'controls' preload: 'auto' src: @name + gist: + regExp: /.*(?:gist.github.com.*\/)([^\/][^\/]*)$/ + el: -> + div = $.el 'iframe', + # Github doesn't allow embedding straight from the site, so we use an external site to bypass that. + src: "http://www.purplegene.com/script?url=https://gist.github.com/#{@name}.js" + title: + api: (uid) -> "https://api.github.com/gists/#{uid}" + text: -> + response = JSON.parse(@responseText).files + return file for file of response when response.hasOwnProperty file + image: - regExp: /(http|www).*\.(gif|png|jpg|jpeg|bmp)$/ + regExp: /(http|www).*\.(gif|png|jpg|jpeg|bmp)$/ style: 'border: 0; width: auto; height: auto;' el: -> $.el 'div', innerHTML: "<a target=_blank href='#{@dataset.originalurl}'><img src='#{@dataset.originalurl}'></a>" + InstallGentoo: + regExp: /.*(?:paste.installgentoo.com\/view\/)([0-9a-z_]+)/ + el: -> + $.el 'iframe', + src: "http://paste.installgentoo.com/view/embed/#{@name}" + + LiveLeak: + regExp: /.*(?:liveleak.com\/view.+i=)([0-9a-z_]+)/ + el: -> + $.el 'object', + innerHTML: "<embed src='http://www.liveleak.com/e/#{@name}?autostart=true' wmode='opaque' width='640' height='390' pluginspage='http://get.adobe.com/flashplayer/' type='application/x-shockwave-flash'></embed>" + + pastebin: + regExp: /.*(?:pastebin.com\/(?!u\/))([^#\&\?]*).*/ + el: -> + div = $.el 'iframe', + src: "http://pastebin.com/embed_iframe.php?i=#{@name}" + SoundCloud: regExp: /.*(?:soundcloud.com\/|snd.sc\/)([^#\&\?]*).*/ style: 'height: auto; width: 500px; display: inline-block;' @@ -297,26 +296,48 @@ Linkify = api: (uid) -> "//soundcloud.com/oembed?show_artwork=false&&maxwidth=500px&show_comments=false&format=json&url=https://www.soundcloud.com/#{uid}" text: -> JSON.parse(@responseText).title - pastebin: - regExp: /.*(?:pastebin.com\/(?!u\/))([^#\&\?]*).*/ - el: -> - div = $.el 'iframe', - src: "http://pastebin.com/embed_iframe.php?i=#{@name}" +# WIP +# +# TwitchTV: +# regExp: /twitch\.tv\/(\w+)\/(?:b\/)?(\d+)/i +# style: "border: none; width: 640px; height: 360px;" +# el: -> +# [_, channel, archive] = @result +# el = $.el 'object', +# data: 'http://www.twitch.tv/widgets/archive_embed_player.swf' +# innerHTML: """ +#<param name='allowFullScreen' value='true' /> +#<param name='flashvars' value='channel=#{channel}&start_volume=25&auto_play=false&archive_id=#{archive}' /> +#""" - gist: - regExp: /.*(?:gist.github.com.*\/)([^\/][^\/]*)$/ + Vocaroo: + regExp: /.*(?:vocaroo.com\/)([^#\&\?]*).*/ + style: 'border: 0; width: 150px; height: 45px;' el: -> - div = $.el 'iframe', - # Github doesn't allow embedding straight from the site, so we use an external site to bypass that. - src: "http://www.purplegene.com/script?url=https://gist.github.com/#{@name}.js" - title: - api: (uid) -> "https://api.github.com/gists/#{uid}" - text: -> - response = JSON.parse(@responseText).files - return file for file of response when response.hasOwnProperty file + $.el 'object', + innerHTML: "<embed src='http://vocaroo.com/player.swf?playMediaID=#{@name.replace /^i\//, ''}&autoplay=0' wmode='opaque' width='150' height='45' pluginspage='http://get.adobe.com/flashplayer/' type='application/x-shockwave-flash'></embed>" - InstallGentoo: - regExp: /.*(?:paste.installgentoo.com\/view\/)([0-9a-z_]+)/ + Vimeo: + regExp: /.*(?:vimeo.com\/)([^#\&\?]*).*/ el: -> $.el 'iframe', - src: "http://paste.installgentoo.com/view/embed/#{@name}" \ No newline at end of file + src: "//player.vimeo.com/video/#{@name}?wmode=opaque" + title: + api: (uid) -> "https://vimeo.com/api/oembed.json?url=http://vimeo.com/#{uid}" + text: -> JSON.parse(@responseText).title + + Vine: + regExp: /.*(?:vine.co\/)([^#\&\?]*).*/ + style: 'border: none; width: 500px; height: 500px;' + el: -> + $.el 'iframe', + src: "https://vine.co/#{@name}/card" + + YouTube: + regExp: /.*(?:youtu.be\/|youtube.*v=|youtube.*\/embed\/|youtube.*\/v\/|youtube.*videos\/)([^#\&\?]*)\??(t\=.*)?/ + el: -> + $.el 'iframe', + src: "//www.youtube.com/embed/#{@name}#{if @option then '#' + @option else ''}?wmode=opaque" + title: + api: (uid) -> "https://gdata.youtube.com/feeds/api/videos/#{uid}?alt=json&fields=title/text(),yt:noembed,app:control/yt:state/@reasonCode" + text: -> JSON.parse(@responseText).entry.title.$t \ No newline at end of file From c44014b19ec9dcc4d16869ec9c22ee416b7ce179 Mon Sep 17 00:00:00 2001 From: Zixaphir <zixaphirmoxphar@gmail.com> Date: Mon, 5 Aug 2013 13:01:44 -0700 Subject: [PATCH 110/172] Lets stop filling syntactical fields with non-meta-data --- builds/4chan-X.user.js | 54 ++++++++++++++++++-------------- builds/crx/script.js | 54 ++++++++++++++++++-------------- src/Linkification/Linkify.coffee | 41 ++++++++++++------------ 3 files changed, 83 insertions(+), 66 deletions(-) diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js index 71277dcf5..bea416866 100644 --- a/builds/4chan-X.user.js +++ b/builds/4chan-X.user.js @@ -4428,21 +4428,28 @@ } }, embed: function(data) { - var embed, key, link, options, uid; + var embed, href, key, link, name, options, uid, value, _ref; key = data[0], uid = data[1], options = data[2], link = data[3]; + href = link.href; embed = $.el('a', { - name: uid, - option: options, className: 'embedder', href: 'javascript:;', textContent: '(embed)' }); - embed.dataset.service = key; - embed.dataset.originalurl = link.href; - $.addClass(link, "" + embed.dataset.service); + _ref = { + key: key, + href: href, + uid: uid, + options: options + }; + for (name in _ref) { + value = _ref[name]; + embed.dataset[name] = value; + } + $.addClass(link, "" + embed.dataset.key); $.on(embed, 'click', Linkify.cb.toggle); - return $.after(link, [$.tn(' '), embed]); + $.after(link, [$.tn(' '), embed]); }, title: function(data) { var err, key, link, options, service, title, titles, uid; @@ -4488,23 +4495,24 @@ embed: function(a) { var el, style, type; - el = (type = Linkify.types[a.dataset.service]).el.call(a); + el = (type = Linkify.types[a.dataset.key]).el.call(a); el.style.cssText = (style = type.style) ? style : "border: 0; width: 640px; height: 390px"; a.textContent = '(unembed)'; return el; }, unembed: function(a) { - var el, url; + var el, href; + href = a.dataset.href; el = $.el('a', { rel: 'nofollow noreferrer', target: 'blank', className: 'linkify', - href: url = a.dataset.originalurl, - textContent: a.dataset.title || url + href: href, + textContent: a.dataset.title || href }); a.textContent = '(embed)'; - $.addClass(el, "" + a.dataset.service); + $.addClass(el, "" + a.dataset.key); return el; }, title: function(data) { @@ -4538,7 +4546,7 @@ return $.el('audio', { controls: 'controls', preload: 'auto', - src: this.name + src: this.dataset.uid }); } }, @@ -4548,7 +4556,7 @@ var div; return div = $.el('iframe', { - src: "http://www.purplegene.com/script?url=https://gist.github.com/" + this.name + ".js" + src: "http://www.purplegene.com/script?url=https://gist.github.com/" + this.dataset.uid + ".js" }); }, title: { @@ -4572,7 +4580,7 @@ style: 'border: 0; width: auto; height: auto;', el: function() { return $.el('div', { - innerHTML: "<a target=_blank href='" + this.dataset.originalurl + "'><img src='" + this.dataset.originalurl + "'></a>" + innerHTML: "<a target=_blank href='" + this.dataset.href + "'><img src='" + this.dataset.href + "'></a>" }); } }, @@ -4580,7 +4588,7 @@ regExp: /.*(?:paste.installgentoo.com\/view\/)([0-9a-z_]+)/, el: function() { return $.el('iframe', { - src: "http://paste.installgentoo.com/view/embed/" + this.name + src: "http://paste.installgentoo.com/view/embed/" + this.dataset.uid }); } }, @@ -4588,7 +4596,7 @@ regExp: /.*(?:liveleak.com\/view.+i=)([0-9a-z_]+)/, el: function() { return $.el('object', { - innerHTML: "<embed src='http://www.liveleak.com/e/" + this.name + "?autostart=true' wmode='opaque' width='640' height='390' pluginspage='http://get.adobe.com/flashplayer/' type='application/x-shockwave-flash'></embed>" + innerHTML: "<embed src='http://www.liveleak.com/e/" + this.dataset.uid + "?autostart=true' wmode='opaque' width='640' height='390' pluginspage='http://get.adobe.com/flashplayer/' type='application/x-shockwave-flash'></embed>" }); } }, @@ -4598,7 +4606,7 @@ var div; return div = $.el('iframe', { - src: "http://pastebin.com/embed_iframe.php?i=" + this.name + src: "http://pastebin.com/embed_iframe.php?i=" + this.dataset.uid }); } }, @@ -4612,7 +4620,7 @@ className: "soundcloud", name: "soundcloud" }); - $.ajax("//soundcloud.com/oembed?show_artwork=false&&maxwidth=500px&show_comments=false&format=json&url=https://www.soundcloud.com/" + this.name, { + $.ajax("//soundcloud.com/oembed?show_artwork=false&&maxwidth=500px&show_comments=false&format=json&url=https://www.soundcloud.com/" + this.dataset.uid, { div: div, onloadend: function() { return this.div.innerHTML = JSON.parse(this.responseText).html; @@ -4634,7 +4642,7 @@ style: 'border: 0; width: 150px; height: 45px;', el: function() { return $.el('object', { - innerHTML: "<embed src='http://vocaroo.com/player.swf?playMediaID=" + (this.name.replace(/^i\//, '')) + "&autoplay=0' wmode='opaque' width='150' height='45' pluginspage='http://get.adobe.com/flashplayer/' type='application/x-shockwave-flash'></embed>" + innerHTML: "<embed src='http://vocaroo.com/player.swf?playMediaID=" + (this.dataset.uid.replace(/^i\//, '')) + "&autoplay=0' wmode='opaque' width='150' height='45' pluginspage='http://get.adobe.com/flashplayer/' type='application/x-shockwave-flash'></embed>" }); } }, @@ -4642,7 +4650,7 @@ regExp: /.*(?:vimeo.com\/)([^#\&\?]*).*/, el: function() { return $.el('iframe', { - src: "//player.vimeo.com/video/" + this.name + "?wmode=opaque" + src: "//player.vimeo.com/video/" + this.dataset.uid + "?wmode=opaque" }); }, title: { @@ -4659,7 +4667,7 @@ style: 'border: none; width: 500px; height: 500px;', el: function() { return $.el('iframe', { - src: "https://vine.co/" + this.name + "/card" + src: "https://vine.co/" + this.dataset.uid + "/card" }); } }, @@ -4667,7 +4675,7 @@ regExp: /.*(?:youtu.be\/|youtube.*v=|youtube.*\/embed\/|youtube.*\/v\/|youtube.*videos\/)([^#\&\?]*)\??(t\=.*)?/, el: function() { return $.el('iframe', { - src: "//www.youtube.com/embed/" + this.name + (this.option ? '#' + this.option : '') + "?wmode=opaque" + src: "//www.youtube.com/embed/" + this.dataset.uid + (this.dataset.option ? '#' + this.dataset.option : '') + "?wmode=opaque" }); }, title: { diff --git a/builds/crx/script.js b/builds/crx/script.js index 7fe3cdbf8..a41e296df 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -4433,21 +4433,28 @@ } }, embed: function(data) { - var embed, key, link, options, uid; + var embed, href, key, link, name, options, uid, value, _ref; key = data[0], uid = data[1], options = data[2], link = data[3]; + href = link.href; embed = $.el('a', { - name: uid, - option: options, className: 'embedder', href: 'javascript:;', textContent: '(embed)' }); - embed.dataset.service = key; - embed.dataset.originalurl = link.href; - $.addClass(link, "" + embed.dataset.service); + _ref = { + key: key, + href: href, + uid: uid, + options: options + }; + for (name in _ref) { + value = _ref[name]; + embed.dataset[name] = value; + } + $.addClass(link, "" + embed.dataset.key); $.on(embed, 'click', Linkify.cb.toggle); - return $.after(link, [$.tn(' '), embed]); + $.after(link, [$.tn(' '), embed]); }, title: function(data) { var err, key, link, options, service, title, titles, uid; @@ -4493,23 +4500,24 @@ embed: function(a) { var el, style, type; - el = (type = Linkify.types[a.dataset.service]).el.call(a); + el = (type = Linkify.types[a.dataset.key]).el.call(a); el.style.cssText = (style = type.style) ? style : "border: 0; width: 640px; height: 390px"; a.textContent = '(unembed)'; return el; }, unembed: function(a) { - var el, url; + var el, href; + href = a.dataset.href; el = $.el('a', { rel: 'nofollow noreferrer', target: 'blank', className: 'linkify', - href: url = a.dataset.originalurl, - textContent: a.dataset.title || url + href: href, + textContent: a.dataset.title || href }); a.textContent = '(embed)'; - $.addClass(el, "" + a.dataset.service); + $.addClass(el, "" + a.dataset.key); return el; }, title: function(data) { @@ -4543,7 +4551,7 @@ return $.el('audio', { controls: 'controls', preload: 'auto', - src: this.name + src: this.dataset.uid }); } }, @@ -4553,7 +4561,7 @@ var div; return div = $.el('iframe', { - src: "http://www.purplegene.com/script?url=https://gist.github.com/" + this.name + ".js" + src: "http://www.purplegene.com/script?url=https://gist.github.com/" + this.dataset.uid + ".js" }); }, title: { @@ -4577,7 +4585,7 @@ style: 'border: 0; width: auto; height: auto;', el: function() { return $.el('div', { - innerHTML: "<a target=_blank href='" + this.dataset.originalurl + "'><img src='" + this.dataset.originalurl + "'></a>" + innerHTML: "<a target=_blank href='" + this.dataset.href + "'><img src='" + this.dataset.href + "'></a>" }); } }, @@ -4585,7 +4593,7 @@ regExp: /.*(?:paste.installgentoo.com\/view\/)([0-9a-z_]+)/, el: function() { return $.el('iframe', { - src: "http://paste.installgentoo.com/view/embed/" + this.name + src: "http://paste.installgentoo.com/view/embed/" + this.dataset.uid }); } }, @@ -4593,7 +4601,7 @@ regExp: /.*(?:liveleak.com\/view.+i=)([0-9a-z_]+)/, el: function() { return $.el('object', { - innerHTML: "<embed src='http://www.liveleak.com/e/" + this.name + "?autostart=true' wmode='opaque' width='640' height='390' pluginspage='http://get.adobe.com/flashplayer/' type='application/x-shockwave-flash'></embed>" + innerHTML: "<embed src='http://www.liveleak.com/e/" + this.dataset.uid + "?autostart=true' wmode='opaque' width='640' height='390' pluginspage='http://get.adobe.com/flashplayer/' type='application/x-shockwave-flash'></embed>" }); } }, @@ -4603,7 +4611,7 @@ var div; return div = $.el('iframe', { - src: "http://pastebin.com/embed_iframe.php?i=" + this.name + src: "http://pastebin.com/embed_iframe.php?i=" + this.dataset.uid }); } }, @@ -4617,7 +4625,7 @@ className: "soundcloud", name: "soundcloud" }); - $.ajax("//soundcloud.com/oembed?show_artwork=false&&maxwidth=500px&show_comments=false&format=json&url=https://www.soundcloud.com/" + this.name, { + $.ajax("//soundcloud.com/oembed?show_artwork=false&&maxwidth=500px&show_comments=false&format=json&url=https://www.soundcloud.com/" + this.dataset.uid, { div: div, onloadend: function() { return this.div.innerHTML = JSON.parse(this.responseText).html; @@ -4639,7 +4647,7 @@ style: 'border: 0; width: 150px; height: 45px;', el: function() { return $.el('object', { - innerHTML: "<embed src='http://vocaroo.com/player.swf?playMediaID=" + (this.name.replace(/^i\//, '')) + "&autoplay=0' wmode='opaque' width='150' height='45' pluginspage='http://get.adobe.com/flashplayer/' type='application/x-shockwave-flash'></embed>" + innerHTML: "<embed src='http://vocaroo.com/player.swf?playMediaID=" + (this.dataset.uid.replace(/^i\//, '')) + "&autoplay=0' wmode='opaque' width='150' height='45' pluginspage='http://get.adobe.com/flashplayer/' type='application/x-shockwave-flash'></embed>" }); } }, @@ -4647,7 +4655,7 @@ regExp: /.*(?:vimeo.com\/)([^#\&\?]*).*/, el: function() { return $.el('iframe', { - src: "//player.vimeo.com/video/" + this.name + "?wmode=opaque" + src: "//player.vimeo.com/video/" + this.dataset.uid + "?wmode=opaque" }); }, title: { @@ -4664,7 +4672,7 @@ style: 'border: none; width: 500px; height: 500px;', el: function() { return $.el('iframe', { - src: "https://vine.co/" + this.name + "/card" + src: "https://vine.co/" + this.dataset.uid + "/card" }); } }, @@ -4672,7 +4680,7 @@ regExp: /.*(?:youtu.be\/|youtube.*v=|youtube.*\/embed\/|youtube.*\/v\/|youtube.*videos\/)([^#\&\?]*)\??(t\=.*)?/, el: function() { return $.el('iframe', { - src: "//www.youtube.com/embed/" + this.name + (this.option ? '#' + this.option : '') + "?wmode=opaque" + src: "//www.youtube.com/embed/" + this.dataset.uid + (this.dataset.option ? '#' + this.dataset.option : '') + "?wmode=opaque" }); }, title: { diff --git a/src/Linkification/Linkify.coffee b/src/Linkification/Linkify.coffee index 48dbb705a..34f46a4f9 100644 --- a/src/Linkification/Linkify.coffee +++ b/src/Linkification/Linkify.coffee @@ -137,20 +137,20 @@ Linkify = embed: (data) -> [key, uid, options, link] = data + href = link.href embed = $.el 'a', - name: uid - option: options className: 'embedder' href: 'javascript:;' textContent: '(embed)' - embed.dataset.service = key - embed.dataset.originalurl = link.href + for name, value of {key, href, uid, options} + embed.dataset[name] = value - $.addClass link, "#{embed.dataset.service}" + $.addClass link, "#{embed.dataset.key}" $.on embed, 'click', Linkify.cb.toggle $.after link, [$.tn(' '), embed] + return title: (data) -> [key, uid, options, link] = data @@ -190,7 +190,7 @@ Linkify = embed: (a) -> # We create an element to embed - el = (type = Linkify.types[a.dataset.service]).el.call a + el = (type = Linkify.types[a.dataset.key]).el.call a # Set style values. el.style.cssText = if style = type.style @@ -204,15 +204,16 @@ Linkify = unembed: (a) -> # Recreate the original link. + {href} = a.dataset el = $.el 'a', rel: 'nofollow noreferrer' target: 'blank' className: 'linkify' - href: url = a.dataset.originalurl - textContent: a.dataset.title or url + href: href + textContent: a.dataset.title or href a.textContent = '(embed)' - $.addClass el, "#{a.dataset.service}" + $.addClass el, "#{a.dataset.key}" return el @@ -239,14 +240,14 @@ Linkify = $.el 'audio', controls: 'controls' preload: 'auto' - src: @name + src: @dataset.uid gist: regExp: /.*(?:gist.github.com.*\/)([^\/][^\/]*)$/ el: -> div = $.el 'iframe', # Github doesn't allow embedding straight from the site, so we use an external site to bypass that. - src: "http://www.purplegene.com/script?url=https://gist.github.com/#{@name}.js" + src: "http://www.purplegene.com/script?url=https://gist.github.com/#{@dataset.uid}.js" title: api: (uid) -> "https://api.github.com/gists/#{uid}" text: -> @@ -258,25 +259,25 @@ Linkify = style: 'border: 0; width: auto; height: auto;' el: -> $.el 'div', - innerHTML: "<a target=_blank href='#{@dataset.originalurl}'><img src='#{@dataset.originalurl}'></a>" + innerHTML: "<a target=_blank href='#{@dataset.href}'><img src='#{@dataset.href}'></a>" InstallGentoo: regExp: /.*(?:paste.installgentoo.com\/view\/)([0-9a-z_]+)/ el: -> $.el 'iframe', - src: "http://paste.installgentoo.com/view/embed/#{@name}" + src: "http://paste.installgentoo.com/view/embed/#{@dataset.uid}" LiveLeak: regExp: /.*(?:liveleak.com\/view.+i=)([0-9a-z_]+)/ el: -> $.el 'object', - innerHTML: "<embed src='http://www.liveleak.com/e/#{@name}?autostart=true' wmode='opaque' width='640' height='390' pluginspage='http://get.adobe.com/flashplayer/' type='application/x-shockwave-flash'></embed>" + innerHTML: "<embed src='http://www.liveleak.com/e/#{@dataset.uid}?autostart=true' wmode='opaque' width='640' height='390' pluginspage='http://get.adobe.com/flashplayer/' type='application/x-shockwave-flash'></embed>" pastebin: regExp: /.*(?:pastebin.com\/(?!u\/))([^#\&\?]*).*/ el: -> div = $.el 'iframe', - src: "http://pastebin.com/embed_iframe.php?i=#{@name}" + src: "http://pastebin.com/embed_iframe.php?i=#{@dataset.uid}" SoundCloud: regExp: /.*(?:soundcloud.com\/|snd.sc\/)([^#\&\?]*).*/ @@ -286,7 +287,7 @@ Linkify = className: "soundcloud" name: "soundcloud" $.ajax( - "//soundcloud.com/oembed?show_artwork=false&&maxwidth=500px&show_comments=false&format=json&url=https://www.soundcloud.com/#{@name}" + "//soundcloud.com/oembed?show_artwork=false&&maxwidth=500px&show_comments=false&format=json&url=https://www.soundcloud.com/#{@dataset.uid}" div: div onloadend: -> @div.innerHTML = JSON.parse(@responseText).html @@ -315,13 +316,13 @@ Linkify = style: 'border: 0; width: 150px; height: 45px;' el: -> $.el 'object', - innerHTML: "<embed src='http://vocaroo.com/player.swf?playMediaID=#{@name.replace /^i\//, ''}&autoplay=0' wmode='opaque' width='150' height='45' pluginspage='http://get.adobe.com/flashplayer/' type='application/x-shockwave-flash'></embed>" + innerHTML: "<embed src='http://vocaroo.com/player.swf?playMediaID=#{@dataset.uid.replace /^i\//, ''}&autoplay=0' wmode='opaque' width='150' height='45' pluginspage='http://get.adobe.com/flashplayer/' type='application/x-shockwave-flash'></embed>" Vimeo: regExp: /.*(?:vimeo.com\/)([^#\&\?]*).*/ el: -> $.el 'iframe', - src: "//player.vimeo.com/video/#{@name}?wmode=opaque" + src: "//player.vimeo.com/video/#{@dataset.uid}?wmode=opaque" title: api: (uid) -> "https://vimeo.com/api/oembed.json?url=http://vimeo.com/#{uid}" text: -> JSON.parse(@responseText).title @@ -331,13 +332,13 @@ Linkify = style: 'border: none; width: 500px; height: 500px;' el: -> $.el 'iframe', - src: "https://vine.co/#{@name}/card" + src: "https://vine.co/#{@dataset.uid}/card" YouTube: regExp: /.*(?:youtu.be\/|youtube.*v=|youtube.*\/embed\/|youtube.*\/v\/|youtube.*videos\/)([^#\&\?]*)\??(t\=.*)?/ el: -> $.el 'iframe', - src: "//www.youtube.com/embed/#{@name}#{if @option then '#' + @option else ''}?wmode=opaque" + src: "//www.youtube.com/embed/#{@dataset.uid}#{if @dataset.option then '#' + @dataset.option else ''}?wmode=opaque" title: api: (uid) -> "https://gdata.youtube.com/feeds/api/videos/#{uid}?alt=json&fields=title/text(),yt:noembed,app:control/yt:state/@reasonCode" text: -> JSON.parse(@responseText).entry.title.$t \ No newline at end of file From d5553cf5fd97cfa7acd0268b22d01edd989744d6 Mon Sep 17 00:00:00 2001 From: Zixaphir <zixaphirmoxphar@gmail.com> Date: Mon, 5 Aug 2013 14:05:29 -0700 Subject: [PATCH 111/172] TwitchTV embedding (live and archive) --- builds/4chan-X.user.js | 31 +++++++++++++++++---- builds/crx/script.js | 31 +++++++++++++++++---- src/Linkification/Linkify.coffee | 46 ++++++++++++++++++++------------ 3 files changed, 81 insertions(+), 27 deletions(-) diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js index bea416866..db60047f5 100644 --- a/builds/4chan-X.user.js +++ b/builds/4chan-X.user.js @@ -4328,6 +4328,9 @@ len = snapshot.snapshotLength; while (++i < len) { node = snapshot.snapshotItem(i); + if (node.parentElement.nodeName === "A") { + continue; + } data = node.data; if (Linkify.regString.test(data)) { Linkify.regString.lastIndex = 0; @@ -4360,7 +4363,7 @@ index = match.index; link = match[0]; len2 = index + link.length; - if (len - len2 === 0) { + if (len === len2) { break; } range = document.createRange(); @@ -4393,10 +4396,7 @@ } } if (range.collapsed) { - if (node.nodeName === 'WBR') { - node = node.previousSibling; - } - range.setEnd(node, node.length); + range.setEndAfter(node); } return Linkify.makeLink(range, post); }, @@ -4637,6 +4637,27 @@ } } }, + TwitchTV: { + regExp: /.*(?:twitch.tv\/)([^#\&\?]*).*/, + style: "border: none; width: 640px; height: 360px;", + el: function() { + var channel, chapter, result, _; + + if (result = /(\w+)\/(?:[a-z]\/)?(\d+)/i.exec(this.dataset.uid)) { + _ = result[0], channel = result[1], chapter = result[2]; + return $.el('object', { + data: 'http://www.twitch.tv/widgets/archive_embed_player.swf', + innerHTML: "<param name='allowFullScreen' value='true' />\n<param name='flashvars' value='channel=" + channel + "&start_volume=25&auto_play=false" + (chapter ? "&chapter_id=" + chapter : "") + "' />" + }); + } else { + channel = (/(\w+)/.exec(this.dataset.uid))[0]; + return $.el('object', { + data: "http://www.twitch.tv/widgets/live_embed_player.swf?channel=" + channel, + innerHTML: "<param name=\"allowFullScreen\" value=\"true\" />\n<param name=\"movie\" value=\"http://www.twitch.tv/widgets/live_embed_player.swf\" />\n<param name=\"flashvars\" value=\"hostname=www.twitch.tv&channel=" + channel + "&auto_play=true&start_volume=25\" />" + }); + } + } + }, Vocaroo: { regExp: /.*(?:vocaroo.com\/)([^#\&\?]*).*/, style: 'border: 0; width: 150px; height: 45px;', diff --git a/builds/crx/script.js b/builds/crx/script.js index a41e296df..aee40189e 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -4333,6 +4333,9 @@ len = snapshot.snapshotLength; while (++i < len) { node = snapshot.snapshotItem(i); + if (node.parentElement.nodeName === "A") { + continue; + } data = node.data; if (Linkify.regString.test(data)) { Linkify.regString.lastIndex = 0; @@ -4365,7 +4368,7 @@ index = match.index; link = match[0]; len2 = index + link.length; - if (len - len2 === 0) { + if (len === len2) { break; } range = document.createRange(); @@ -4398,10 +4401,7 @@ } } if (range.collapsed) { - if (node.nodeName === 'WBR') { - node = node.previousSibling; - } - range.setEnd(node, node.length); + range.setEndAfter(node); } return Linkify.makeLink(range, post); }, @@ -4642,6 +4642,27 @@ } } }, + TwitchTV: { + regExp: /.*(?:twitch.tv\/)([^#\&\?]*).*/, + style: "border: none; width: 640px; height: 360px;", + el: function() { + var channel, chapter, result, _; + + if (result = /(\w+)\/(?:[a-z]\/)?(\d+)/i.exec(this.dataset.uid)) { + _ = result[0], channel = result[1], chapter = result[2]; + return $.el('object', { + data: 'http://www.twitch.tv/widgets/archive_embed_player.swf', + innerHTML: "<param name='allowFullScreen' value='true' />\n<param name='flashvars' value='channel=" + channel + "&start_volume=25&auto_play=false" + (chapter ? "&chapter_id=" + chapter : "") + "' />" + }); + } else { + channel = (/(\w+)/.exec(this.dataset.uid))[0]; + return $.el('object', { + data: "http://www.twitch.tv/widgets/live_embed_player.swf?channel=" + channel, + innerHTML: "<param name=\"allowFullScreen\" value=\"true\" />\n<param name=\"movie\" value=\"http://www.twitch.tv/widgets/live_embed_player.swf\" />\n<param name=\"flashvars\" value=\"hostname=www.twitch.tv&channel=" + channel + "&auto_play=true&start_volume=25\" />" + }); + } + } + }, Vocaroo: { regExp: /.*(?:vocaroo.com\/)([^#\&\?]*).*/, style: 'border: 0; width: 150px; height: 45px;', diff --git a/src/Linkification/Linkify.coffee b/src/Linkification/Linkify.coffee index 34f46a4f9..ba46a0c58 100644 --- a/src/Linkification/Linkify.coffee +++ b/src/Linkification/Linkify.coffee @@ -44,6 +44,9 @@ Linkify = while ++i < len node = snapshot.snapshotItem i + + continue if node.parentElement.nodeName is "A" + data = node.data if Linkify.regString.test data @@ -69,7 +72,7 @@ Linkify = link = match[0] len2 = index + link.length - break if len - len2 is 0 + break if len is len2 range = document.createRange(); range.setStart node, index @@ -99,9 +102,7 @@ Linkify = range.setEnd node, result.index if range.collapsed - if node.nodeName is 'WBR' - node = node.previousSibling - range.setEnd node, node.length + range.setEndAfter node Linkify.makeLink range, post @@ -297,19 +298,30 @@ Linkify = api: (uid) -> "//soundcloud.com/oembed?show_artwork=false&&maxwidth=500px&show_comments=false&format=json&url=https://www.soundcloud.com/#{uid}" text: -> JSON.parse(@responseText).title -# WIP -# -# TwitchTV: -# regExp: /twitch\.tv\/(\w+)\/(?:b\/)?(\d+)/i -# style: "border: none; width: 640px; height: 360px;" -# el: -> -# [_, channel, archive] = @result -# el = $.el 'object', -# data: 'http://www.twitch.tv/widgets/archive_embed_player.swf' -# innerHTML: """ -#<param name='allowFullScreen' value='true' /> -#<param name='flashvars' value='channel=#{channel}&start_volume=25&auto_play=false&archive_id=#{archive}' /> -#""" + TwitchTV: + regExp: /.*(?:twitch.tv\/)([^#\&\?]*).*/ + style: "border: none; width: 640px; height: 360px;" + el: -> + if result = /(\w+)\/(?:[a-z]\/)?(\d+)/i.exec @dataset.uid + [_, channel, chapter] = result + + $.el 'object', + data: 'http://www.twitch.tv/widgets/archive_embed_player.swf' + innerHTML: """ +<param name='allowFullScreen' value='true' /> +<param name='flashvars' value='channel=#{channel}&start_volume=25&auto_play=false#{if chapter then "&chapter_id=" + chapter else ""}' /> +""" + + else + channel = (/(\w+)/.exec @dataset.uid)[0] + + $.el 'object', + data: "http://www.twitch.tv/widgets/live_embed_player.swf?channel=#{channel}" + innerHTML: """ +<param name="allowFullScreen" value="true" /> +<param name="movie" value="http://www.twitch.tv/widgets/live_embed_player.swf" /> +<param name="flashvars" value="hostname=www.twitch.tv&channel=#{channel}&auto_play=true&start_volume=25" /> +""" Vocaroo: regExp: /.*(?:vocaroo.com\/)([^#\&\?]*).*/ From b36cf54d83874270cff0e4c41e350b6322e523dd Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Mon, 5 Aug 2013 23:30:39 +0200 Subject: [PATCH 112/172] Make use of a few CoffeeScript filters. --- lib/$.coffee | 3 +-- src/General/Header.coffee | 3 +-- src/General/Post.coffee | 3 +-- src/General/Settings.coffee | 3 +-- src/Posting/QR.coffee | 4 +--- 5 files changed, 5 insertions(+), 11 deletions(-) diff --git a/lib/$.coffee b/lib/$.coffee index 811614b78..59e08cbbd 100644 --- a/lib/$.coffee +++ b/lib/$.coffee @@ -24,8 +24,7 @@ $.formData = (form) -> if form instanceof HTMLFormElement return new FormData form fd = new FormData() - for key, val of form - continue unless val + for key, val of form when val # XXX GM bug # if val instanceof Blob if val.size and val.name diff --git a/src/General/Header.coffee b/src/General/Header.coffee index 5ba74529f..ae98518ed 100644 --- a/src/General/Header.coffee +++ b/src/General/Header.coffee @@ -199,8 +199,7 @@ Header = '#boardNavDesktopFoot a[href*="boards.4chan.org"]' ].join ', ' path = if useCatalog then 'catalog' else '' - for a in as - continue if a.dataset.only + for a in as when not a.dataset.only a.pathname = "/#{a.pathname.split('/')[1]}/#{path}" return toggleCatalogLinks: -> diff --git a/src/General/Post.coffee b/src/General/Post.coffee index 6da7f33dc..1b890c2ec 100644 --- a/src/General/Post.coffee +++ b/src/General/Post.coffee @@ -167,8 +167,7 @@ class Post return if file # Get quotelinks/backlinks to this post # and paint them (Dead). - for quotelink in Get.allQuotelinksLinkingTo @ - continue if $.hasClass quotelink, 'deadlink' + for quotelink in Get.allQuotelinksLinkingTo @ when not $.hasClass quotelink, 'deadlink' $.add quotelink, $.tn '\u00A0(Dead)' $.addClass quotelink, 'deadlink' return diff --git a/src/General/Settings.coffee b/src/General/Settings.coffee index d0360db9e..660d78f78 100644 --- a/src/General/Settings.coffee +++ b/src/General/Settings.coffee @@ -277,8 +277,7 @@ Settings = '%board' else c - for key, val of Config.hotkeys - continue unless key of data.Conf + for key, val of Config.hotkeys when key of data.Conf data.Conf[key] = data.Conf[key].replace(/ctrl|alt|meta/g, (s) -> "#{s[0].toUpperCase()}#{s[1..]}").replace /(^|.+\+)[A-Z]$/g, (s) -> "Shift+#{s[0...-1]}#{s[-1..].toLowerCase()}" data.Conf.WatchedThreads = data.WatchedThreads diff --git a/src/Posting/QR.coffee b/src/Posting/QR.coffee index c77bdda1e..791e7ded0 100644 --- a/src/Posting/QR.coffee +++ b/src/Posting/QR.coffee @@ -176,9 +176,7 @@ QR = types[type].push val loadPersonas: (type, arr) -> list = $ "#list-#{type}", QR.nodes.el - for val in arr - # XXX Firefox displays empty <option>s in the completion list. - continue unless val + for val in arr when val $.add list, $.el 'option', textContent: val return From 548bbdd84a3c9d6decff9891727cd15e3ddeeb4d Mon Sep 17 00:00:00 2001 From: Zixaphir <zixaphirmoxphar@gmail.com> Date: Mon, 5 Aug 2013 14:40:52 -0700 Subject: [PATCH 113/172] Changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a3c64220..2094d851c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ **Zixaphir**: - Linkifier Rewrite. - Fix Quote Threading toggle. +- Added Twitch.tv and Vine embedding (with @ihavenoface) - Minor optimizations. - Minor fixes. From 63cd0aa1641ff35e977c7522500d148edbe45888 Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Tue, 6 Aug 2013 15:27:16 +0200 Subject: [PATCH 114/172] ExpandThread tweaking. Also fix code trying to $.off `ExpandThread.cb.toggle`. --- src/Miscellaneous/ExpandThread.coffee | 82 +++++++++++++-------------- 1 file changed, 39 insertions(+), 43 deletions(-) diff --git a/src/Miscellaneous/ExpandThread.coffee b/src/Miscellaneous/ExpandThread.coffee index 4fd4d44a4..557770a15 100644 --- a/src/Miscellaneous/ExpandThread.coffee +++ b/src/Miscellaneous/ExpandThread.coffee @@ -6,7 +6,7 @@ ExpandThread = name: 'Thread Expansion' cb: @node node: -> - return unless span = $ '.summary', @OP.nodes.root.parentNode + return unless span = $.x 'following-sibling::span[contains(@class,"summary")][1]', @OP.nodes.root a = $.el 'a', textContent: "+ #{span.textContent}" className: 'summary' @@ -15,8 +15,7 @@ ExpandThread = $.replace span, a cbToggle: -> - op = Get.postFromRoot @previousElementSibling - ExpandThread.toggle op.thread + ExpandThread.toggle Get.threadFromRoot @parentNode toggle: (thread) -> threadRoot = thread.OP.nodes.root.parentNode @@ -24,7 +23,6 @@ ExpandThread = switch thread.isExpanded when false, undefined - thread.isExpanded = 'loading' for post in $$ '.thread > .postContainer', threadRoot ExpandComment.expand Get.postFromRoot post unless a @@ -42,60 +40,58 @@ ExpandThread = when true thread.isExpanded = false - if a - a.textContent = a.textContent.replace '-', '+' - #goddamit moot - num = if thread.isSticky - 1 - else switch g.BOARD.ID - # XXX boards config - when 'b', 'vg', 'q' then 3 - when 't' then 1 - else 5 - replies = $$('.thread > .replyContainer', threadRoot)[...-num] - for reply in replies - if Conf['Quote Inlining'] - # rm clones - inlined.click() while inlined = $ '.inlined', reply - $.rm reply - for post in $$ '.thread > .postContainer', threadRoot + #goddamit moot + num = if thread.isSticky + 1 + else switch g.BOARD.ID + # XXX boards config + when 'b', 'vg', 'q' then 3 + when 't' then 1 + else 5 + posts = $$ ".thread > .replyContainer", threadRoot + for post in [thread.OP.nodes.root].concat posts[-num..] ExpandComment.contract Get.postFromRoot post + return unless a + a.textContent = a.textContent.replace '-', '+' + for reply in posts[...-num] + if Conf['Quote Inlining'] + # rm clones + inlined.click() while inlined = $ '.inlined', reply + $.rm reply return parse: (req, thread, a) -> return if a.textContent[0] is '+' - {status} = req - if status not in [200, 304] - a.textContent = "Error #{req.statusText} (#{status})" - $.off a, 'click', ExpandThread.cb.toggle + if req.status not in [200, 304] + a.textContent = "Error #{req.statusText} (#{req.status})" + $.off a, 'click', ExpandThread.cbToggle return thread.isExpanded = true a.textContent = a.textContent.replace '× Loading...', '-' - posts = JSON.parse(req.response).posts - if spoilerRange = posts[0].custom_spoiler - Build.spoilerRange[g.BOARD] = spoilerRange + {posts} = JSON.parse req.response + if spoilerRange = posts.shift().custom_spoiler + Build.spoilerRange[thread.board] = spoilerRange - replies = posts[1..] - posts = [] - nodes = [] - for reply in replies - if post = thread.posts[reply.no] - nodes.push post.nodes.root + postsObj = [] + postsRoot = [] + for post in posts + if post = thread.posts[post.no] + postsRoot.push post.nodes.root continue - node = Build.postFromObject reply, thread.board.ID - post = new Post node, thread, thread.board - link = $ 'a[title="Highlight this post"]', node + root = Build.postFromObject post, thread.board.ID + post = new Post root, thread, thread.board + link = $ 'a[title="Highlight this post"]', root link.href = "res/#{thread}#p#{post}" link.nextSibling.href = "res/#{thread}#q#{post}" - posts.push post - nodes.push node - Main.callbackNodes Post, posts - $.after a, nodes + postsObj.push post + postsRoot.push root + Main.callbackNodes Post, postsObj + $.after a, postsRoot # Enable 4chan features. if Conf['Enable 4chan\'s Extension'] - $.globalEval "Parser.parseThread(#{thread.ID}, 1, #{nodes.length})" + $.globalEval "Parser.parseThread(#{thread.ID}, 1, #{postsRoot.length})" else - Fourchan.parseThread thread.ID, 1, nodes.length + Fourchan.parseThread thread.ID, 1, postsRoot.length From be7a4052fdb1dbf5acafb827e3247def572630b4 Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Tue, 6 Aug 2013 16:28:54 +0200 Subject: [PATCH 115/172] Close #1106 --- src/Miscellaneous/ExpandThread.coffee | 43 +++++++++++++++++++-------- 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/src/Miscellaneous/ExpandThread.coffee b/src/Miscellaneous/ExpandThread.coffee index 557770a15..c18bd8088 100644 --- a/src/Miscellaneous/ExpandThread.coffee +++ b/src/Miscellaneous/ExpandThread.coffee @@ -7,13 +7,21 @@ ExpandThread = cb: @node node: -> return unless span = $.x 'following-sibling::span[contains(@class,"summary")][1]', @OP.nodes.root + [posts, files] = span.textContent.match /\d+/g a = $.el 'a', - textContent: "+ #{span.textContent}" + textContent: ExpandThread.text '+', posts, files className: 'summary' href: 'javascript:;' $.on a, 'click', ExpandThread.cbToggle $.replace span, a + text: (status, posts, files) -> + text = [status] + text.push "#{posts} post#{if posts > 1 then 's' else ''}" + text.push "and #{files} image repl#{if files > 1 then 'ies' else 'y'}" if +files + text.push if status is '-' then 'shown' else 'omitted' + text.join(' ') + '.' + cbToggle: -> ExpandThread.toggle Get.threadFromRoot @parentNode @@ -29,14 +37,16 @@ ExpandThread = thread.isExpanded = true return thread.isExpanded = 'loading' - a.textContent = a.textContent.replace '+', '× Loading...' + [posts, files] = a.textContent.match /\d+/g + a.textContent = ExpandThread.text '...', posts, files $.cache "//api.4chan.org/#{thread.board}/res/#{thread}.json", -> ExpandThread.parse @, thread, a when 'loading' thread.isExpanded = false return unless a - a.textContent = a.textContent.replace '× Loading...', '+' + [posts, files] = a.textContent.match /\d+/g + a.textContent = ExpandThread.text '+', posts, files when true thread.isExpanded = false @@ -52,12 +62,16 @@ ExpandThread = for post in [thread.OP.nodes.root].concat posts[-num..] ExpandComment.contract Get.postFromRoot post return unless a - a.textContent = a.textContent.replace '-', '+' + postsCount = 0 + filesCount = 0 for reply in posts[...-num] if Conf['Quote Inlining'] # rm clones inlined.click() while inlined = $ '.inlined', reply + postsCount++ + filesCount++ if 'file' of Get.postFromRoot reply $.rm reply + a.textContent = ExpandThread.text '+', postsCount, filesCount return parse: (req, thread, a) -> @@ -68,30 +82,35 @@ ExpandThread = return thread.isExpanded = true - a.textContent = a.textContent.replace '× Loading...', '-' {posts} = JSON.parse req.response if spoilerRange = posts.shift().custom_spoiler Build.spoilerRange[thread.board] = spoilerRange - postsObj = [] - postsRoot = [] - for post in posts - if post = thread.posts[post.no] + postsObj = [] + postsRoot = [] + filesCount = 0 + for reply in posts + if post = thread.posts[reply.no] + filesCount++ if 'file' of post postsRoot.push post.nodes.root continue - root = Build.postFromObject post, thread.board.ID + root = Build.postFromObject reply, thread.board.ID post = new Post root, thread, thread.board link = $ 'a[title="Highlight this post"]', root link.href = "res/#{thread}#p#{post}" link.nextSibling.href = "res/#{thread}#q#{post}" + filesCount++ if 'file' of post postsObj.push post postsRoot.push root Main.callbackNodes Post, postsObj $.after a, postsRoot + postsCount = postsRoot.length + a.textContent = ExpandThread.text '-', postsCount, filesCount + # Enable 4chan features. if Conf['Enable 4chan\'s Extension'] - $.globalEval "Parser.parseThread(#{thread.ID}, 1, #{postsRoot.length})" + $.globalEval "Parser.parseThread(#{thread.ID}, 1, #{postsCount})" else - Fourchan.parseThread thread.ID, 1, postsRoot.length + Fourchan.parseThread thread.ID, 1, postsCount From 738a175f43b97c45ce471a2ff1e9ba7b51e9d800 Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Tue, 6 Aug 2013 21:11:03 +0200 Subject: [PATCH 116/172] Avoid using {d.body,doc}.scroll{Top,Left}, use window.scroll[...] instead. --- src/General/Header.coffee | 4 ++-- src/General/UI.coffee | 4 ++-- src/Images/ImageExpand.coffee | 22 +++++++++++----------- src/Monitoring/ThreadUpdater.coffee | 2 +- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/General/Header.coffee b/src/General/Header.coffee index ae98518ed..4c76991b1 100644 --- a/src/General/Header.coffee +++ b/src/General/Header.coffee @@ -248,8 +248,8 @@ Header = {top} = post.getBoundingClientRect() unless Conf['Bottom header'] headRect = Header.toggle.getBoundingClientRect() - top += - headRect.top - headRect.height - <% if (type === 'crx') { %>d.body<% } else { %>doc<% } %>.scrollTop += top + top -= headRect.top + headRect.height + window.scrollBy 0, top addShortcut: (el) -> shortcut = $.el 'span', diff --git a/src/General/UI.coffee b/src/General/UI.coffee index 7c6c752e0..84bc40a08 100644 --- a/src/General/UI.coffee +++ b/src/General/UI.coffee @@ -63,8 +63,8 @@ UI = do -> # Position mRect = menu.getBoundingClientRect() bRect = button.getBoundingClientRect() - bTop = doc.scrollTop + d.body.scrollTop + bRect.top - bLeft = doc.scrollLeft + d.body.scrollLeft + bRect.left + bTop = window.scrollY + bRect.top + bLeft = window.scrollX + bRect.left cHeight = doc.clientHeight cWidth = doc.clientWidth [top, bottom] = if bRect.top + bRect.height + mRect.height < cHeight diff --git a/src/Images/ImageExpand.coffee b/src/Images/ImageExpand.coffee index 777cceea8..9a4d8dd3f 100644 --- a/src/Images/ImageExpand.coffee +++ b/src/Images/ImageExpand.coffee @@ -59,17 +59,18 @@ ImageExpand = ImageExpand.expand post return ImageExpand.contract post - rect = post.nodes.root.getBoundingClientRect() - return unless rect.top <= 0 or rect.left <= 0 + # Scroll back to the thumbnail when contracting the image # to avoid being left miles away from the relevant post. - {top} = rect - unless Conf['Bottom header'] - headRect = Header.toggle.getBoundingClientRect() - top += - headRect.top - headRect.height - root = <% if (type === 'crx') { %>d.body<% } else { %>doc<% } %> - root.scrollTop += top if rect.top < 0 - root.scrollLeft = 0 if rect.left < 0 + rect = post.nodes.root.getBoundingClientRect() + if rect.top < 0 + y = rect.top + unless Conf['Bottom header'] + headRect = Header.toggle.getBoundingClientRect() + y -= headRect.top + headRect.height + if rect.left < 0 + x = -window.scrollX + window.scrollBy x, y if x or y contract: (post) -> $.rmClass post.nodes.root, 'expanded-image' @@ -109,9 +110,8 @@ ImageExpand = $.addClass post.nodes.root, 'expanded-image' $.rmClass post.file.thumb, 'expanding' return unless prev.top + prev.height <= 0 - root = <% if (type === 'crx') { %>d.body<% } else { %>doc<% } %> curr = post.nodes.root.getBoundingClientRect() - root.scrollTop += curr.height - prev.height + curr.top - prev.top + window.scrollBy 0, curr.height - prev.height + curr.top - prev.top error: -> post = Get.postFromNode @ diff --git a/src/Monitoring/ThreadUpdater.coffee b/src/Monitoring/ThreadUpdater.coffee index 7dab2983a..ceb1a68bd 100644 --- a/src/Monitoring/ThreadUpdater.coffee +++ b/src/Monitoring/ThreadUpdater.coffee @@ -262,7 +262,7 @@ ThreadUpdater = sendEvent() if scroll if Conf['Bottom Scroll'] - <% if (type === 'crx') { %>d.body<% } else { %>doc<% } %>.scrollTop = d.body.clientHeight + window.scrollTo 0, d.body.clientHeight else Header.scrollToPost nodes[0] From 9b5cb52619bf00ab70f128ef828a0682688ab142 Mon Sep 17 00:00:00 2001 From: Zixaphir <zixaphirmoxphar@gmail.com> Date: Tue, 6 Aug 2013 12:14:23 -0700 Subject: [PATCH 117/172] Personal choices --- builds/4chan-X.user.js | 10 +--------- builds/crx/script.js | 10 +--------- src/Miscellaneous/ExpandThread.coffee | 9 ++++----- 3 files changed, 6 insertions(+), 23 deletions(-) diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js index 497294377..6dbe56c2c 100644 --- a/builds/4chan-X.user.js +++ b/builds/4chan-X.user.js @@ -8627,15 +8627,7 @@ return $.replace(span, a); }, text: function(status, posts, files) { - var text; - - text = [status]; - text.push("" + posts + " post" + (posts > 1 ? 's' : '')); - if (+files) { - text.push("and " + files + " image repl" + (files > 1 ? 'ies' : 'y')); - } - text.push(status === '-' ? 'shown' : 'omitted'); - return text.join(' ') + '.'; + return ("" + status + " " + posts + " post" + (posts > 1 ? 's' : '')) + (+files ? " and " + files + " image repl" + (files > 1 ? 'ies' : 'y') : "") + (" " + (status === '-' ? 'shown' : 'omitted') + "."); }, cbToggle: function() { return ExpandThread.toggle(Get.threadFromRoot(this.parentNode)); diff --git a/builds/crx/script.js b/builds/crx/script.js index 4816da6d1..593d72a9f 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -8612,15 +8612,7 @@ return $.replace(span, a); }, text: function(status, posts, files) { - var text; - - text = [status]; - text.push("" + posts + " post" + (posts > 1 ? 's' : '')); - if (+files) { - text.push("and " + files + " image repl" + (files > 1 ? 'ies' : 'y')); - } - text.push(status === '-' ? 'shown' : 'omitted'); - return text.join(' ') + '.'; + return ("" + status + " " + posts + " post" + (posts > 1 ? 's' : '')) + (+files ? " and " + files + " image repl" + (files > 1 ? 'ies' : 'y') : "") + (" " + (status === '-' ? 'shown' : 'omitted') + "."); }, cbToggle: function() { return ExpandThread.toggle(Get.threadFromRoot(this.parentNode)); diff --git a/src/Miscellaneous/ExpandThread.coffee b/src/Miscellaneous/ExpandThread.coffee index 4d63a2989..3f3a7c19a 100644 --- a/src/Miscellaneous/ExpandThread.coffee +++ b/src/Miscellaneous/ExpandThread.coffee @@ -5,6 +5,7 @@ ExpandThread = Thread::callbacks.push name: 'Thread Expansion' cb: @node + node: -> return unless span = $.x 'following-sibling::span[contains(@class,"summary")][1]', @OP.nodes.root [posts, files] = span.textContent.match /\d+/g @@ -16,11 +17,9 @@ ExpandThread = $.replace span, a text: (status, posts, files) -> - text = [status] - text.push "#{posts} post#{if posts > 1 then 's' else ''}" - text.push "and #{files} image repl#{if files > 1 then 'ies' else 'y'}" if +files - text.push if status is '-' then 'shown' else 'omitted' - text.join(' ') + '.' + "#{status} #{posts} post#{if posts > 1 then 's' else ''}" + + (if +files then " and #{files} image repl#{if files > 1 then 'ies' else 'y'}" else "") + + " #{if status is '-' then 'shown' else 'omitted'}." cbToggle: -> ExpandThread.toggle Get.threadFromRoot @parentNode From 7939d24640dfa997a27e69c590fe4d18e105d47c Mon Sep 17 00:00:00 2001 From: Zixaphir <zixaphirmoxphar@gmail.com> Date: Tue, 6 Aug 2013 13:33:57 -0700 Subject: [PATCH 118/172] Fix "Advance on contract" option with Quote Threading --- builds/4chan-X.user.js | 29 +++++++++-------------------- builds/crx/script.js | 29 +++++++++-------------------- src/Images/ImageExpand.coffee | 17 ++++++----------- 3 files changed, 24 insertions(+), 51 deletions(-) diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js index ca9224a93..970a9ac5a 100644 --- a/builds/4chan-X.user.js +++ b/builds/4chan-X.user.js @@ -6283,7 +6283,7 @@ } }, toggle: function(post) { - var headRect, node, rect, thumb, x, y; + var headRect, rect, root, thumb, x, y; thumb = post.file.thumb; if (!(post.file.isExpanded || $.hasClass(thumb, 'expanding'))) { @@ -6291,28 +6291,17 @@ return; } ImageExpand.contract(post); - node = post.nodes.root; - rect = Conf['Advance on contract'] ? (function() { - while (node.nextElementSibling) { - if (!(node = node.nextElementSibling)) { - return post.nodes.root; - } - if (!$.hasClass(node, 'postContainer')) { - continue; - } - if (node.offsetHeight > 0 && !$('.stub', node)) { - break; - } - } - return node.getBoundingClientRect(); - })() : post.nodes.root.getBoundingClientRect(); - if (!(rect.top <= 0 || rect.left <= 0)) { - return; - } + root = post.nodes.root; + rect = (Conf['Advance on contract'] ? (function() { + var next; + + next = $.x("following::div[contains(@class,'postContainer')][1]", root); + return next || root; + })() : root).getBoundingClientRect(); if (rect.top < 0) { y = rect.top; if (Conf['Fixed Header'] && !Conf['Bottom Header']) { - headRect = Header.toggle.getBoundingClientRect(); + headRect = Header.bar.getBoundingClientRect(); y -= headRect.top + headRect.height; } } diff --git a/builds/crx/script.js b/builds/crx/script.js index d3384960c..0b6aa6596 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -6263,7 +6263,7 @@ } }, toggle: function(post) { - var headRect, node, rect, thumb, x, y; + var headRect, rect, root, thumb, x, y; thumb = post.file.thumb; if (!(post.file.isExpanded || $.hasClass(thumb, 'expanding'))) { @@ -6271,28 +6271,17 @@ return; } ImageExpand.contract(post); - node = post.nodes.root; - rect = Conf['Advance on contract'] ? (function() { - while (node.nextElementSibling) { - if (!(node = node.nextElementSibling)) { - return post.nodes.root; - } - if (!$.hasClass(node, 'postContainer')) { - continue; - } - if (node.offsetHeight > 0 && !$('.stub', node)) { - break; - } - } - return node.getBoundingClientRect(); - })() : post.nodes.root.getBoundingClientRect(); - if (!(rect.top <= 0 || rect.left <= 0)) { - return; - } + root = post.nodes.root; + rect = (Conf['Advance on contract'] ? (function() { + var next; + + next = $.x("following::div[contains(@class,'postContainer')][1]", root); + return next || root; + })() : root).getBoundingClientRect(); if (rect.top < 0) { y = rect.top; if (Conf['Fixed Header'] && !Conf['Bottom Header']) { - headRect = Header.toggle.getBoundingClientRect(); + headRect = Header.bar.getBoundingClientRect(); y -= headRect.top + headRect.height; } } diff --git a/src/Images/ImageExpand.coffee b/src/Images/ImageExpand.coffee index afbfcd0a7..7d6a627b1 100644 --- a/src/Images/ImageExpand.coffee +++ b/src/Images/ImageExpand.coffee @@ -59,24 +59,19 @@ ImageExpand = ImageExpand.expand post return ImageExpand.contract post - node = post.nodes.root # Scroll back to the thumbnail when contracting the image # to avoid being left miles away from the relevant post. - rect = if Conf['Advance on contract'] then do -> - # FIXME does not work with Quote Threading - while node.nextElementSibling - return post.nodes.root unless node = node.nextElementSibling - continue unless $.hasClass node, 'postContainer' - break if node.offsetHeight > 0 and not $ '.stub', node - node.getBoundingClientRect() + {root} = post.nodes + rect = (if Conf['Advance on contract'] then do -> + next = $.x "following::div[contains(@class,'postContainer')][1]", root + next or root else - post.nodes.root.getBoundingClientRect() - return unless rect.top <= 0 or rect.left <= 0 + root).getBoundingClientRect() if rect.top < 0 y = rect.top if Conf['Fixed Header'] and not Conf['Bottom Header'] - headRect = Header.toggle.getBoundingClientRect() + headRect = Header.bar.getBoundingClientRect() y -= headRect.top + headRect.height if rect.left < 0 From 1227cbd847054f12f6559fbd2debd44f9e3c1383 Mon Sep 17 00:00:00 2001 From: Zixaphir <zixaphirmoxphar@gmail.com> Date: Tue, 6 Aug 2013 13:37:24 -0700 Subject: [PATCH 119/172] Small simplify --- builds/4chan-X.user.js | 7 +------ builds/crx/script.js | 7 +------ src/Images/ImageExpand.coffee | 8 ++++---- 3 files changed, 6 insertions(+), 16 deletions(-) diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js index 970a9ac5a..50efb10d3 100644 --- a/builds/4chan-X.user.js +++ b/builds/4chan-X.user.js @@ -6292,12 +6292,7 @@ } ImageExpand.contract(post); root = post.nodes.root; - rect = (Conf['Advance on contract'] ? (function() { - var next; - - next = $.x("following::div[contains(@class,'postContainer')][1]", root); - return next || root; - })() : root).getBoundingClientRect(); + rect = (Conf['Advance on contract'] ? $.x("following::div[contains(@class,'postContainer')][1]", root) || root : root).getBoundingClientRect(); if (rect.top < 0) { y = rect.top; if (Conf['Fixed Header'] && !Conf['Bottom Header']) { diff --git a/builds/crx/script.js b/builds/crx/script.js index 0b6aa6596..bcdd59bae 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -6272,12 +6272,7 @@ } ImageExpand.contract(post); root = post.nodes.root; - rect = (Conf['Advance on contract'] ? (function() { - var next; - - next = $.x("following::div[contains(@class,'postContainer')][1]", root); - return next || root; - })() : root).getBoundingClientRect(); + rect = (Conf['Advance on contract'] ? $.x("following::div[contains(@class,'postContainer')][1]", root) || root : root).getBoundingClientRect(); if (rect.top < 0) { y = rect.top; if (Conf['Fixed Header'] && !Conf['Bottom Header']) { diff --git a/src/Images/ImageExpand.coffee b/src/Images/ImageExpand.coffee index 7d6a627b1..25105dd72 100644 --- a/src/Images/ImageExpand.coffee +++ b/src/Images/ImageExpand.coffee @@ -62,11 +62,11 @@ ImageExpand = # Scroll back to the thumbnail when contracting the image # to avoid being left miles away from the relevant post. {root} = post.nodes - rect = (if Conf['Advance on contract'] then do -> - next = $.x "following::div[contains(@class,'postContainer')][1]", root - next or root + rect = (if Conf['Advance on contract'] + $.x("following::div[contains(@class,'postContainer')][1]", root) or root else - root).getBoundingClientRect() + root + ).getBoundingClientRect() if rect.top < 0 y = rect.top From 5507168019e14608488b4ccbcfb311ff4413f75f Mon Sep 17 00:00:00 2001 From: Zixaphir <zixaphirmoxphar@gmail.com> Date: Tue, 6 Aug 2013 13:55:44 -0700 Subject: [PATCH 120/172] Forgot to account for hidden posts. --- builds/4chan-X.user.js | 11 +++++++++-- builds/crx/script.js | 11 +++++++++-- src/Images/ImageExpand.coffee | 10 +++++----- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js index 50efb10d3..707f493b1 100644 --- a/builds/4chan-X.user.js +++ b/builds/4chan-X.user.js @@ -6283,7 +6283,7 @@ } }, toggle: function(post) { - var headRect, rect, root, thumb, x, y; + var headRect, next, rect, root, thumb, x, y; thumb = post.file.thumb; if (!(post.file.isExpanded || $.hasClass(thumb, 'expanding'))) { @@ -6292,7 +6292,14 @@ } ImageExpand.contract(post); root = post.nodes.root; - rect = (Conf['Advance on contract'] ? $.x("following::div[contains(@class,'postContainer')][1]", root) || root : root).getBoundingClientRect(); + if (Conf['Advance on contract']) { + while (next = $.x("following::div[contains(@class,'postContainer')][1]", root)) { + if (!($('.stub', next) || next.offsetHeight === 0)) { + break; + } + } + } + rect = (next || root).getBoundingClientRect(); if (rect.top < 0) { y = rect.top; if (Conf['Fixed Header'] && !Conf['Bottom Header']) { diff --git a/builds/crx/script.js b/builds/crx/script.js index bcdd59bae..8aee581ea 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -6263,7 +6263,7 @@ } }, toggle: function(post) { - var headRect, rect, root, thumb, x, y; + var headRect, next, rect, root, thumb, x, y; thumb = post.file.thumb; if (!(post.file.isExpanded || $.hasClass(thumb, 'expanding'))) { @@ -6272,7 +6272,14 @@ } ImageExpand.contract(post); root = post.nodes.root; - rect = (Conf['Advance on contract'] ? $.x("following::div[contains(@class,'postContainer')][1]", root) || root : root).getBoundingClientRect(); + if (Conf['Advance on contract']) { + while (next = $.x("following::div[contains(@class,'postContainer')][1]", root)) { + if (!($('.stub', next) || next.offsetHeight === 0)) { + break; + } + } + } + rect = (next || root).getBoundingClientRect(); if (rect.top < 0) { y = rect.top; if (Conf['Fixed Header'] && !Conf['Bottom Header']) { diff --git a/src/Images/ImageExpand.coffee b/src/Images/ImageExpand.coffee index 25105dd72..53d11f035 100644 --- a/src/Images/ImageExpand.coffee +++ b/src/Images/ImageExpand.coffee @@ -62,11 +62,11 @@ ImageExpand = # Scroll back to the thumbnail when contracting the image # to avoid being left miles away from the relevant post. {root} = post.nodes - rect = (if Conf['Advance on contract'] - $.x("following::div[contains(@class,'postContainer')][1]", root) or root - else - root - ).getBoundingClientRect() + if Conf['Advance on contract'] + while next = $.x "following::div[contains(@class,'postContainer')][1]", root + break unless $('.stub', next) or next.offsetHeight is 0 + + rect = (next or root).getBoundingClientRect() if rect.top < 0 y = rect.top From 9159f7adbc199a49ad83fcd0d0bca8d2b267d0f7 Mon Sep 17 00:00:00 2001 From: Zixaphir <zixaphirmoxphar@gmail.com> Date: Tue, 6 Aug 2013 14:16:49 -0700 Subject: [PATCH 121/172] I think I'm satisfied with this now. --- builds/4chan-X.user.js | 18 +++++++++++------- builds/crx/script.js | 18 +++++++++++------- src/Images/ImageExpand.coffee | 14 +++++++++----- 3 files changed, 31 insertions(+), 19 deletions(-) diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js index 707f493b1..1f7c82496 100644 --- a/builds/4chan-X.user.js +++ b/builds/4chan-X.user.js @@ -6283,7 +6283,7 @@ } }, toggle: function(post) { - var headRect, next, rect, root, thumb, x, y; + var headRect, rect, root, thumb, x, y; thumb = post.file.thumb; if (!(post.file.isExpanded || $.hasClass(thumb, 'expanding'))) { @@ -6292,14 +6292,18 @@ } ImageExpand.contract(post); root = post.nodes.root; - if (Conf['Advance on contract']) { - while (next = $.x("following::div[contains(@class,'postContainer')][1]", root)) { - if (!($('.stub', next) || next.offsetHeight === 0)) { - break; + rect = (Conf['Advance on contract'] ? (function() { + var next; + + next = root; + while (next = $.x("following::div[contains(@class,'postContainer')][1]", next)) { + if ($('.stub', next) || next.offsetHeight === 0) { + continue; } + return next; } - } - rect = (next || root).getBoundingClientRect(); + return root; + })() : root).getBoundingClientRect(); if (rect.top < 0) { y = rect.top; if (Conf['Fixed Header'] && !Conf['Bottom Header']) { diff --git a/builds/crx/script.js b/builds/crx/script.js index 8aee581ea..0ec4ddd6f 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -6263,7 +6263,7 @@ } }, toggle: function(post) { - var headRect, next, rect, root, thumb, x, y; + var headRect, rect, root, thumb, x, y; thumb = post.file.thumb; if (!(post.file.isExpanded || $.hasClass(thumb, 'expanding'))) { @@ -6272,14 +6272,18 @@ } ImageExpand.contract(post); root = post.nodes.root; - if (Conf['Advance on contract']) { - while (next = $.x("following::div[contains(@class,'postContainer')][1]", root)) { - if (!($('.stub', next) || next.offsetHeight === 0)) { - break; + rect = (Conf['Advance on contract'] ? (function() { + var next; + + next = root; + while (next = $.x("following::div[contains(@class,'postContainer')][1]", next)) { + if ($('.stub', next) || next.offsetHeight === 0) { + continue; } + return next; } - } - rect = (next || root).getBoundingClientRect(); + return root; + })() : root).getBoundingClientRect(); if (rect.top < 0) { y = rect.top; if (Conf['Fixed Header'] && !Conf['Bottom Header']) { diff --git a/src/Images/ImageExpand.coffee b/src/Images/ImageExpand.coffee index 53d11f035..a02e5ec31 100644 --- a/src/Images/ImageExpand.coffee +++ b/src/Images/ImageExpand.coffee @@ -62,11 +62,15 @@ ImageExpand = # Scroll back to the thumbnail when contracting the image # to avoid being left miles away from the relevant post. {root} = post.nodes - if Conf['Advance on contract'] - while next = $.x "following::div[contains(@class,'postContainer')][1]", root - break unless $('.stub', next) or next.offsetHeight is 0 - - rect = (next or root).getBoundingClientRect() + rect = (if Conf['Advance on contract'] then do -> + next = root + while next = $.x "following::div[contains(@class,'postContainer')][1]", next + continue if $('.stub', next) or next.offsetHeight is 0 + return next + root + else + root + ).getBoundingClientRect() if rect.top < 0 y = rect.top From 1b890399776b1afc94f44bb82ff218c8044fea95 Mon Sep 17 00:00:00 2001 From: Zixaphir <zixaphirmoxphar@gmail.com> Date: Tue, 6 Aug 2013 17:17:34 -0700 Subject: [PATCH 122/172] I hate this. --- builds/4chan-X.user.js | 7 +++---- builds/crx/script.js | 4 ++-- src/Monitoring/Unread.coffee | 2 +- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js index 1f7c82496..dc1509a81 100644 --- a/builds/4chan-X.user.js +++ b/builds/4chan-X.user.js @@ -115,8 +115,7 @@ __slice = [].slice, __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, - __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, - __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; + __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; Config = { main: { @@ -7898,7 +7897,7 @@ return Unread.update(); }, addPosts: function(posts) { - var ID, data, post, _i, _len, _ref; + var ID, data, post, _i, _len; for (_i = 0, _len = posts.length; _i < _len; _i++) { post = posts[_i]; @@ -7920,7 +7919,7 @@ Unread.addPostQuotingYou(post); } if (Conf['Unread Line']) { - Unread.setLine((_ref = Unread.posts[0], __indexOf.call(posts, _ref) >= 0)); + Unread.setLine(posts.contains(Unread.posts[0])); } Unread.read(); return Unread.update(); diff --git a/builds/crx/script.js b/builds/crx/script.js index 0ec4ddd6f..38cada217 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -7878,7 +7878,7 @@ return Unread.update(); }, addPosts: function(posts) { - var ID, data, post, _i, _len, _ref; + var ID, data, post, _i, _len; for (_i = 0, _len = posts.length; _i < _len; _i++) { post = posts[_i]; @@ -7900,7 +7900,7 @@ Unread.addPostQuotingYou(post); } if (Conf['Unread Line']) { - Unread.setLine((_ref = Unread.posts[0], __indexOf.call(posts, _ref) >= 0)); + Unread.setLine(posts.contains(Unread.posts[0])); } Unread.read(); return Unread.update(); diff --git a/src/Monitoring/Unread.coffee b/src/Monitoring/Unread.coffee index 8263c835a..5ae97a370 100644 --- a/src/Monitoring/Unread.coffee +++ b/src/Monitoring/Unread.coffee @@ -84,7 +84,7 @@ Unread = Unread.addPostQuotingYou post if Conf['Unread Line'] # Force line on visible threads if there were no unread posts previously. - Unread.setLine Unread.posts[0] in posts + Unread.setLine posts.contains Unread.posts[0] Unread.read() Unread.update() From b47aea7055db7dcecd1512f1c41aa20387954f8e Mon Sep 17 00:00:00 2001 From: Zixaphir <zixaphirmoxphar@gmail.com> Date: Tue, 6 Aug 2013 19:47:23 -0700 Subject: [PATCH 123/172] Restore node to previous state instead of replacing node with url --- builds/4chan-X.user.js | 10 +++++----- builds/crx/script.js | 10 +++++----- src/Linkification/Linkify.coffee | 9 +++++---- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js index dc1509a81..5274289fc 100644 --- a/builds/4chan-X.user.js +++ b/builds/4chan-X.user.js @@ -4469,6 +4469,7 @@ value = _ref[name]; embed.dataset[name] = value; } + embed.dataset.nodedata = link.innerHTML; $.addClass(link, "" + embed.dataset.key); $.on(embed, 'click', Linkify.cb.toggle); $.after(link, [$.tn(' '), embed]); @@ -4523,18 +4524,17 @@ return el; }, unembed: function(a) { - var el, href; + var el; - href = a.dataset.href; el = $.el('a', { rel: 'nofollow noreferrer', target: 'blank', className: 'linkify', - href: href, - textContent: a.dataset.title || href + href: a.dataset.href, + innerHTML: a.dataset.title || a.dataset.nodedata }); a.textContent = '(embed)'; - $.addClass(el, "" + a.dataset.key); + $.addClass(el, a.dataset.key); return el; }, title: function(data) { diff --git a/builds/crx/script.js b/builds/crx/script.js index 38cada217..d2295bea6 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -4474,6 +4474,7 @@ value = _ref[name]; embed.dataset[name] = value; } + embed.dataset.nodedata = link.innerHTML; $.addClass(link, "" + embed.dataset.key); $.on(embed, 'click', Linkify.cb.toggle); $.after(link, [$.tn(' '), embed]); @@ -4528,18 +4529,17 @@ return el; }, unembed: function(a) { - var el, href; + var el; - href = a.dataset.href; el = $.el('a', { rel: 'nofollow noreferrer', target: 'blank', className: 'linkify', - href: href, - textContent: a.dataset.title || href + href: a.dataset.href, + innerHTML: a.dataset.title || a.dataset.nodedata }); a.textContent = '(embed)'; - $.addClass(el, "" + a.dataset.key); + $.addClass(el, a.dataset.key); return el; }, title: function(data) { diff --git a/src/Linkification/Linkify.coffee b/src/Linkification/Linkify.coffee index ba46a0c58..25742786e 100644 --- a/src/Linkification/Linkify.coffee +++ b/src/Linkification/Linkify.coffee @@ -146,6 +146,8 @@ Linkify = for name, value of {key, href, uid, options} embed.dataset[name] = value + + embed.dataset.nodedata = link.innerHTML $.addClass link, "#{embed.dataset.key}" @@ -205,16 +207,15 @@ Linkify = unembed: (a) -> # Recreate the original link. - {href} = a.dataset el = $.el 'a', rel: 'nofollow noreferrer' target: 'blank' className: 'linkify' - href: href - textContent: a.dataset.title or href + href: a.dataset.href + innerHTML: a.dataset.title or a.dataset.nodedata a.textContent = '(embed)' - $.addClass el, "#{a.dataset.key}" + $.addClass el, a.dataset.key return el From 4ff97f9435202325c408909f977877b3eb47ad4a Mon Sep 17 00:00:00 2001 From: Zixaphir <zixaphirmoxphar@gmail.com> Date: Tue, 6 Aug 2013 20:10:50 -0700 Subject: [PATCH 124/172] Fix issues with clones, re-add auto-embed --- builds/4chan-X.user.js | 39 ++++++++++++++--------------- builds/crx/script.js | 39 ++++++++++++++--------------- src/Linkification/Linkify.coffee | 42 +++++++++++++++----------------- 3 files changed, 60 insertions(+), 60 deletions(-) diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js index 5274289fc..b9e7014aa 100644 --- a/builds/4chan-X.user.js +++ b/builds/4chan-X.user.js @@ -4335,21 +4335,22 @@ }); }, node: function() { - var data, embedder, i, len, node, range, snapshot, _i, _j, _len, _len1, _ref, _ref1; + var data, el, i, items, node, range, snapshot, _i, _len, _ref; - if (this.isClone && Conf['Embedding']) { - _ref = $$('.embedder', this.nodes.comment); - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - embedder = _ref[_i]; - $.on(embedder, "click", Linkify.cb.toggle); + if (this.isClone) { + if (Conf['Embedding']) { + i = 0; + items = $$('.embedded', this.nodes.comment); + while (el = items[i++]) { + $.on(el, "click", Linkify.cb.toggle); + Linkify.cb.toggle.call(el); + } } return; } snapshot = $.X('.//text()', this.nodes.comment); - i = -1; - len = snapshot.snapshotLength; - while (++i < len) { - node = snapshot.snapshotItem(i); + i = 0; + while (node = snapshot.snapshotItem(i++)) { if (node.parentElement.nodeName === "A") { continue; } @@ -4362,9 +4363,9 @@ if (!(Conf['Embedding'] || Conf['Link Title'])) { return; } - _ref1 = this.nodes.links; - for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { - range = _ref1[_j]; + _ref = this.nodes.links; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + range = _ref[_i]; if (data = Linkify.services(range)) { if (Conf['Embedding']) { Linkify.embed(data); @@ -4473,6 +4474,9 @@ $.addClass(link, "" + embed.dataset.key); $.on(embed, 'click', Linkify.cb.toggle); $.after(link, [$.tn(' '), embed]); + if (Conf['Auto-embed']) { + Linkify.cb.toggle.call(embed); + } }, title: function(data) { var err, key, link, options, service, title, titles, uid; @@ -4508,11 +4512,10 @@ }, cb: { toggle: function() { - var el, embed; + var string, _ref; - embed = this.previousElementSibling; - el = !this.className.contains("embedded") ? Linkify.cb.embed(this) : Linkify.cb.unembed(this); - $.replace(embed, el); + _ref = $.hasClass(this, "embedded") ? ['unembed', '(embed)'] : ['embed', '(unembed)'], string = _ref[0], this.textContent = _ref[1]; + $.replace(this.previousElementSibling, Linkify.cb[string](this)); return $.toggleClass(this, 'embedded'); }, embed: function(a) { @@ -4520,7 +4523,6 @@ el = (type = Linkify.types[a.dataset.key]).el.call(a); el.style.cssText = (style = type.style) ? style : "border: 0; width: 640px; height: 390px"; - a.textContent = '(unembed)'; return el; }, unembed: function(a) { @@ -4533,7 +4535,6 @@ href: a.dataset.href, innerHTML: a.dataset.title || a.dataset.nodedata }); - a.textContent = '(embed)'; $.addClass(el, a.dataset.key); return el; }, diff --git a/builds/crx/script.js b/builds/crx/script.js index d2295bea6..c74ca4489 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -4340,21 +4340,22 @@ }); }, node: function() { - var data, embedder, i, len, node, range, snapshot, _i, _j, _len, _len1, _ref, _ref1; + var data, el, i, items, node, range, snapshot, _i, _len, _ref; - if (this.isClone && Conf['Embedding']) { - _ref = $$('.embedder', this.nodes.comment); - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - embedder = _ref[_i]; - $.on(embedder, "click", Linkify.cb.toggle); + if (this.isClone) { + if (Conf['Embedding']) { + i = 0; + items = $$('.embedded', this.nodes.comment); + while (el = items[i++]) { + $.on(el, "click", Linkify.cb.toggle); + Linkify.cb.toggle.call(el); + } } return; } snapshot = $.X('.//text()', this.nodes.comment); - i = -1; - len = snapshot.snapshotLength; - while (++i < len) { - node = snapshot.snapshotItem(i); + i = 0; + while (node = snapshot.snapshotItem(i++)) { if (node.parentElement.nodeName === "A") { continue; } @@ -4367,9 +4368,9 @@ if (!(Conf['Embedding'] || Conf['Link Title'])) { return; } - _ref1 = this.nodes.links; - for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { - range = _ref1[_j]; + _ref = this.nodes.links; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + range = _ref[_i]; if (data = Linkify.services(range)) { if (Conf['Embedding']) { Linkify.embed(data); @@ -4478,6 +4479,9 @@ $.addClass(link, "" + embed.dataset.key); $.on(embed, 'click', Linkify.cb.toggle); $.after(link, [$.tn(' '), embed]); + if (Conf['Auto-embed']) { + Linkify.cb.toggle.call(embed); + } }, title: function(data) { var err, key, link, options, service, title, titles, uid; @@ -4513,11 +4517,10 @@ }, cb: { toggle: function() { - var el, embed; + var string, _ref; - embed = this.previousElementSibling; - el = !this.className.contains("embedded") ? Linkify.cb.embed(this) : Linkify.cb.unembed(this); - $.replace(embed, el); + _ref = $.hasClass(this, "embedded") ? ['unembed', '(embed)'] : ['embed', '(unembed)'], string = _ref[0], this.textContent = _ref[1]; + $.replace(this.previousElementSibling, Linkify.cb[string](this)); return $.toggleClass(this, 'embedded'); }, embed: function(a) { @@ -4525,7 +4528,6 @@ el = (type = Linkify.types[a.dataset.key]).el.call(a); el.style.cssText = (style = type.style) ? style : "border: 0; width: 640px; height: 390px"; - a.textContent = '(unembed)'; return el; }, unembed: function(a) { @@ -4538,7 +4540,6 @@ href: a.dataset.href, innerHTML: a.dataset.title || a.dataset.nodedata }); - a.textContent = '(embed)'; $.addClass(el, a.dataset.key); return el; }, diff --git a/src/Linkification/Linkify.coffee b/src/Linkification/Linkify.coffee index 25742786e..9f4a7eaf2 100644 --- a/src/Linkification/Linkify.coffee +++ b/src/Linkification/Linkify.coffee @@ -33,20 +33,22 @@ Linkify = cb: @node node: -> - if @isClone and Conf['Embedding'] - for embedder in $$ '.embedder', @nodes.comment - $.on embedder, "click", Linkify.cb.toggle + if @isClone + if Conf['Embedding'] + i = 0 + items = $$ '.embedded', @nodes.comment + while el = items[i++] + $.on el, "click", Linkify.cb.toggle + Linkify.cb.toggle.call el + return snapshot = $.X './/text()', @nodes.comment - i = -1 - len = snapshot.snapshotLength + i = 0 + while node = snapshot.snapshotItem i++ - while ++i < len - node = snapshot.snapshotItem i - continue if node.parentElement.nodeName is "A" - + data = node.data if Linkify.regString.test data @@ -146,13 +148,17 @@ Linkify = for name, value of {key, href, uid, options} embed.dataset[name] = value - + embed.dataset.nodedata = link.innerHTML $.addClass link, "#{embed.dataset.key}" $.on embed, 'click', Linkify.cb.toggle $.after link, [$.tn(' '), embed] + + if Conf['Auto-embed'] + Linkify.cb.toggle.call embed + return title: (data) -> @@ -179,16 +185,11 @@ Linkify = cb: toggle: -> - # We setup the link to be replaced by the embedded video - embed = @previousElementSibling - - # Unembed. - el = unless @className.contains "embedded" - Linkify.cb.embed @ + [string, @textContent] = if $.hasClass @, "embedded" + ['unembed', '(embed)'] else - Linkify.cb.unembed @ - - $.replace embed, el + ['embed', '(unembed)'] + $.replace @previousElementSibling, Linkify.cb[string] @ $.toggleClass @, 'embedded' embed: (a) -> @@ -201,8 +202,6 @@ Linkify = else "border: 0; width: 640px; height: 390px" - a.textContent = '(unembed)' - return el unembed: (a) -> @@ -214,7 +213,6 @@ Linkify = href: a.dataset.href innerHTML: a.dataset.title or a.dataset.nodedata - a.textContent = '(embed)' $.addClass el, a.dataset.key return el From 463d6607f6a4f000b751176bdd5a679f5b40a3c4 Mon Sep 17 00:00:00 2001 From: Zixaphir <zixaphirmoxphar@gmail.com> Date: Tue, 6 Aug 2013 21:14:47 -0700 Subject: [PATCH 125/172] My recent preference of while loops is because of _len --- builds/4chan-X.user.js | 8 ++++---- builds/crx/script.js | 8 ++++---- src/Linkification/Linkify.coffee | 5 ++++- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js index b9e7014aa..9afb4a7e5 100644 --- a/builds/4chan-X.user.js +++ b/builds/4chan-X.user.js @@ -4335,7 +4335,7 @@ }); }, node: function() { - var data, el, i, items, node, range, snapshot, _i, _len, _ref; + var data, el, i, items, node, range, snapshot; if (this.isClone) { if (Conf['Embedding']) { @@ -4363,9 +4363,9 @@ if (!(Conf['Embedding'] || Conf['Link Title'])) { return; } - _ref = this.nodes.links; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - range = _ref[_i]; + items = this.nodes.links; + i = 0; + while (range = items[i++]) { if (data = Linkify.services(range)) { if (Conf['Embedding']) { Linkify.embed(data); diff --git a/builds/crx/script.js b/builds/crx/script.js index c74ca4489..5ef176a20 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -4340,7 +4340,7 @@ }); }, node: function() { - var data, el, i, items, node, range, snapshot, _i, _len, _ref; + var data, el, i, items, node, range, snapshot; if (this.isClone) { if (Conf['Embedding']) { @@ -4368,9 +4368,9 @@ if (!(Conf['Embedding'] || Conf['Link Title'])) { return; } - _ref = this.nodes.links; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - range = _ref[_i]; + items = this.nodes.links; + i = 0; + while (range = items[i++]) { if (data = Linkify.services(range)) { if (Conf['Embedding']) { Linkify.embed(data); diff --git a/src/Linkification/Linkify.coffee b/src/Linkification/Linkify.coffee index 9f4a7eaf2..963a70d7f 100644 --- a/src/Linkification/Linkify.coffee +++ b/src/Linkification/Linkify.coffee @@ -57,7 +57,10 @@ Linkify = return unless Conf['Embedding'] or Conf['Link Title'] - for range in @nodes.links + items = @nodes.links + i = 0 + + while range = items[i++] if data = Linkify.services range Linkify.embed data if Conf['Embedding'] Linkify.title data if Conf['Link Title'] From 1c4f4cea241bae2dac3772cb4f84f94488f2f0e3 Mon Sep 17 00:00:00 2001 From: Zixaphir <zixaphirmoxphar@gmail.com> Date: Tue, 6 Aug 2013 21:54:14 -0700 Subject: [PATCH 126/172] Fix some conflicts with auto-embed and link title Also, less `Function.call(this)` --- builds/4chan-X.user.js | 128 ++++++++++++++++--------------- builds/crx/script.js | 128 ++++++++++++++++--------------- src/Linkification/Linkify.coffee | 96 ++++++++++++----------- 3 files changed, 186 insertions(+), 166 deletions(-) diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js index 9afb4a7e5..52583ed31 100644 --- a/builds/4chan-X.user.js +++ b/builds/4chan-X.user.js @@ -4477,28 +4477,33 @@ if (Conf['Auto-embed']) { Linkify.cb.toggle.call(embed); } + data.push(embed); }, title: function(data) { - var err, key, link, options, service, title, titles, uid; + var embed, err, key, link, options, service, title, titles, uid; - key = data[0], uid = data[1], options = data[2], link = data[3]; + key = data[0], uid = data[1], options = data[2], link = data[3], embed = data[4]; if (!(service = Linkify.types[key].title)) { return; } titles = Conf['CachedTitles']; if (title = titles[uid]) { - link.textContent = title[0]; + if (link) { + link.textContent = title[0]; + } if (Conf['Embedding']) { - return link.nextElementSibling.dataset.title = title[0]; + return embed.dataset.title = title[0]; } } else { try { $.cache(service.api(uid), function() { - return title = Linkify.cb.title.apply(this, [data]); + return title = Linkify.cb.title(this, data); }); } catch (_error) { err = _error; - link.innerHTML = "[" + key + "] <span class=warning>Title Link Blocked</span> (are you using NoScript?)</a>"; + if (link) { + link.innerHTML = "[" + key + "] <span class=warning>Title Link Blocked</span> (are you using NoScript?)</a>"; + } return; } if (title) { @@ -4521,7 +4526,7 @@ embed: function(a) { var el, style, type; - el = (type = Linkify.types[a.dataset.key]).el.call(a); + el = (type = Linkify.types[a.dataset.key]).el(a); el.style.cssText = (style = type.style) ? style : "border: 0; width: 640px; height: 390px"; return el; }, @@ -4538,58 +4543,61 @@ $.addClass(el, a.dataset.key); return el; }, - title: function(data) { - var key, link, options, service, text, uid; + title: function(response, data) { + var embed, key, link, options, service, text, uid; - key = data[0], uid = data[1], options = data[2], link = data[3]; + key = data[0], uid = data[1], options = data[2], link = data[3], embed = data[4]; service = Linkify.types[key].title; - return link.textContent = (function() { - switch (this.status) { - case 200: - case 304: - text = "" + (service.text.call(this)); - if (Conf['Embedding']) { - link.nextElementSibling.dataset.title = text; - } - return text; - case 404: - return "[" + key + "] Not Found"; - case 403: - return "[" + key + "] Forbidden or Private"; - default: - return "[" + key + "] " + this.status + "'d"; - } - }).call(this); + switch (response.status) { + case 200: + case 304: + text = "" + (service.text(JSON.parse(response.responseText))); + if (Conf['Embedding']) { + embed.dataset.title = text; + } + break; + case 404: + text = "[" + key + "] Not Found"; + break; + case 403: + text = "[" + key + "] Forbidden or Private"; + break; + default: + text = "[" + key + "] " + this.status + "'d"; + } + if (link) { + return link.textContent = text; + } } }, types: { audio: { regExp: /(.*\.(mp3|ogg|wav))$/, - el: function() { + el: function(a) { return $.el('audio', { controls: 'controls', preload: 'auto', - src: this.dataset.uid + src: a.dataset.uid }); } }, gist: { regExp: /.*(?:gist.github.com.*\/)([^\/][^\/]*)$/, - el: function() { + el: function(a) { var div; return div = $.el('iframe', { - src: "http://www.purplegene.com/script?url=https://gist.github.com/" + this.dataset.uid + ".js" + src: "http://www.purplegene.com/script?url=https://gist.github.com/" + a.dataset.uid + ".js" }); }, title: { api: function(uid) { return "https://api.github.com/gists/" + uid; }, - text: function() { + text: function(data) { var file, response; - response = JSON.parse(this.responseText).files; + response = data.files; for (file in response) { if (response.hasOwnProperty(file)) { return file; @@ -4601,49 +4609,49 @@ image: { regExp: /(http|www).*\.(gif|png|jpg|jpeg|bmp)$/, style: 'border: 0; width: auto; height: auto;', - el: function() { + el: function(a) { return $.el('div', { - innerHTML: "<a target=_blank href='" + this.dataset.href + "'><img src='" + this.dataset.href + "'></a>" + innerHTML: "<a target=_blank href='" + a.dataset.href + "'><img src='" + a.dataset.href + "'></a>" }); } }, InstallGentoo: { regExp: /.*(?:paste.installgentoo.com\/view\/)([0-9a-z_]+)/, - el: function() { + el: function(a) { return $.el('iframe', { - src: "http://paste.installgentoo.com/view/embed/" + this.dataset.uid + src: "http://paste.installgentoo.com/view/embed/" + a.dataset.uid }); } }, LiveLeak: { regExp: /.*(?:liveleak.com\/view.+i=)([0-9a-z_]+)/, - el: function() { + el: function(a) { return $.el('object', { - innerHTML: "<embed src='http://www.liveleak.com/e/" + this.dataset.uid + "?autostart=true' wmode='opaque' width='640' height='390' pluginspage='http://get.adobe.com/flashplayer/' type='application/x-shockwave-flash'></embed>" + innerHTML: "<embed src='http://www.liveleak.com/e/" + a.dataset.uid + "?autostart=true' wmode='opaque' width='640' height='390' pluginspage='http://get.adobe.com/flashplayer/' type='application/x-shockwave-flash'></embed>" }); } }, pastebin: { regExp: /.*(?:pastebin.com\/(?!u\/))([^#\&\?]*).*/, - el: function() { + el: function(a) { var div; return div = $.el('iframe', { - src: "http://pastebin.com/embed_iframe.php?i=" + this.dataset.uid + src: "http://pastebin.com/embed_iframe.php?i=" + a.dataset.uid }); } }, SoundCloud: { regExp: /.*(?:soundcloud.com\/|snd.sc\/)([^#\&\?]*).*/, style: 'height: auto; width: 500px; display: inline-block;', - el: function() { + el: function(a) { var div; div = $.el('div', { className: "soundcloud", name: "soundcloud" }); - $.ajax("//soundcloud.com/oembed?show_artwork=false&&maxwidth=500px&show_comments=false&format=json&url=https://www.soundcloud.com/" + this.dataset.uid, { + $.ajax("//soundcloud.com/oembed?show_artwork=false&&maxwidth=500px&show_comments=false&format=json&url=https://www.soundcloud.com/" + a.dataset.uid, { div: div, onloadend: function() { return this.div.innerHTML = JSON.parse(this.responseText).html; @@ -4655,25 +4663,25 @@ api: function(uid) { return "//soundcloud.com/oembed?show_artwork=false&&maxwidth=500px&show_comments=false&format=json&url=https://www.soundcloud.com/" + uid; }, - text: function() { - return JSON.parse(this.responseText).title; + text: function(data) { + return data.title; } } }, TwitchTV: { regExp: /.*(?:twitch.tv\/)([^#\&\?]*).*/, style: "border: none; width: 640px; height: 360px;", - el: function() { + el: function(a) { var channel, chapter, result, _; - if (result = /(\w+)\/(?:[a-z]\/)?(\d+)/i.exec(this.dataset.uid)) { + if (result = /(\w+)\/(?:[a-z]\/)?(\d+)/i.exec(a.dataset.uid)) { _ = result[0], channel = result[1], chapter = result[2]; return $.el('object', { data: 'http://www.twitch.tv/widgets/archive_embed_player.swf', innerHTML: "<param name='allowFullScreen' value='true' />\n<param name='flashvars' value='channel=" + channel + "&start_volume=25&auto_play=false" + (chapter ? "&chapter_id=" + chapter : "") + "' />" }); } else { - channel = (/(\w+)/.exec(this.dataset.uid))[0]; + channel = (/(\w+)/.exec(a.dataset.uid))[0]; return $.el('object', { data: "http://www.twitch.tv/widgets/live_embed_player.swf?channel=" + channel, innerHTML: "<param name=\"allowFullScreen\" value=\"true\" />\n<param name=\"movie\" value=\"http://www.twitch.tv/widgets/live_embed_player.swf\" />\n<param name=\"flashvars\" value=\"hostname=www.twitch.tv&channel=" + channel + "&auto_play=true&start_volume=25\" />" @@ -4684,50 +4692,50 @@ Vocaroo: { regExp: /.*(?:vocaroo.com\/)([^#\&\?]*).*/, style: 'border: 0; width: 150px; height: 45px;', - el: function() { + el: function(a) { return $.el('object', { - innerHTML: "<embed src='http://vocaroo.com/player.swf?playMediaID=" + (this.dataset.uid.replace(/^i\//, '')) + "&autoplay=0' wmode='opaque' width='150' height='45' pluginspage='http://get.adobe.com/flashplayer/' type='application/x-shockwave-flash'></embed>" + innerHTML: "<embed src='http://vocaroo.com/player.swf?playMediaID=" + (a.dataset.uid.replace(/^i\//, '')) + "&autoplay=0' wmode='opaque' width='150' height='45' pluginspage='http://get.adobe.com/flashplayer/' type='application/x-shockwave-flash'></embed>" }); } }, Vimeo: { regExp: /.*(?:vimeo.com\/)([^#\&\?]*).*/, - el: function() { + el: function(a) { return $.el('iframe', { - src: "//player.vimeo.com/video/" + this.dataset.uid + "?wmode=opaque" + src: "//player.vimeo.com/video/" + a.dataset.uid + "?wmode=opaque" }); }, title: { api: function(uid) { return "https://vimeo.com/api/oembed.json?url=http://vimeo.com/" + uid; }, - text: function() { - return JSON.parse(this.responseText).title; + text: function(data) { + return data.title; } } }, Vine: { regExp: /.*(?:vine.co\/)([^#\&\?]*).*/, style: 'border: none; width: 500px; height: 500px;', - el: function() { + el: function(a) { return $.el('iframe', { - src: "https://vine.co/" + this.dataset.uid + "/card" + src: "https://vine.co/" + a.dataset.uid + "/card" }); } }, YouTube: { regExp: /.*(?:youtu.be\/|youtube.*v=|youtube.*\/embed\/|youtube.*\/v\/|youtube.*videos\/)([^#\&\?]*)\??(t\=.*)?/, - el: function() { + el: function(a) { return $.el('iframe', { - src: "//www.youtube.com/embed/" + this.dataset.uid + (this.dataset.option ? '#' + this.dataset.option : '') + "?wmode=opaque" + src: "//www.youtube.com/embed/" + a.dataset.uid + (a.dataset.option ? '#' + a.dataset.option : '') + "?wmode=opaque" }); }, title: { api: function(uid) { return "https://gdata.youtube.com/feeds/api/videos/" + uid + "?alt=json&fields=title/text(),yt:noembed,app:control/yt:state/@reasonCode"; }, - text: function() { - return JSON.parse(this.responseText).entry.title.$t; + text: function(data) { + return data.entry.title.$t; } } } diff --git a/builds/crx/script.js b/builds/crx/script.js index 5ef176a20..da265f148 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -4482,28 +4482,33 @@ if (Conf['Auto-embed']) { Linkify.cb.toggle.call(embed); } + data.push(embed); }, title: function(data) { - var err, key, link, options, service, title, titles, uid; + var embed, err, key, link, options, service, title, titles, uid; - key = data[0], uid = data[1], options = data[2], link = data[3]; + key = data[0], uid = data[1], options = data[2], link = data[3], embed = data[4]; if (!(service = Linkify.types[key].title)) { return; } titles = Conf['CachedTitles']; if (title = titles[uid]) { - link.textContent = title[0]; + if (link) { + link.textContent = title[0]; + } if (Conf['Embedding']) { - return link.nextElementSibling.dataset.title = title[0]; + return embed.dataset.title = title[0]; } } else { try { $.cache(service.api(uid), function() { - return title = Linkify.cb.title.apply(this, [data]); + return title = Linkify.cb.title(this, data); }); } catch (_error) { err = _error; - link.innerHTML = "[" + key + "] <span class=warning>Title Link Blocked</span> (are you using NoScript?)</a>"; + if (link) { + link.innerHTML = "[" + key + "] <span class=warning>Title Link Blocked</span> (are you using NoScript?)</a>"; + } return; } if (title) { @@ -4526,7 +4531,7 @@ embed: function(a) { var el, style, type; - el = (type = Linkify.types[a.dataset.key]).el.call(a); + el = (type = Linkify.types[a.dataset.key]).el(a); el.style.cssText = (style = type.style) ? style : "border: 0; width: 640px; height: 390px"; return el; }, @@ -4543,58 +4548,61 @@ $.addClass(el, a.dataset.key); return el; }, - title: function(data) { - var key, link, options, service, text, uid; + title: function(response, data) { + var embed, key, link, options, service, text, uid; - key = data[0], uid = data[1], options = data[2], link = data[3]; + key = data[0], uid = data[1], options = data[2], link = data[3], embed = data[4]; service = Linkify.types[key].title; - return link.textContent = (function() { - switch (this.status) { - case 200: - case 304: - text = "" + (service.text.call(this)); - if (Conf['Embedding']) { - link.nextElementSibling.dataset.title = text; - } - return text; - case 404: - return "[" + key + "] Not Found"; - case 403: - return "[" + key + "] Forbidden or Private"; - default: - return "[" + key + "] " + this.status + "'d"; - } - }).call(this); + switch (response.status) { + case 200: + case 304: + text = "" + (service.text(JSON.parse(response.responseText))); + if (Conf['Embedding']) { + embed.dataset.title = text; + } + break; + case 404: + text = "[" + key + "] Not Found"; + break; + case 403: + text = "[" + key + "] Forbidden or Private"; + break; + default: + text = "[" + key + "] " + this.status + "'d"; + } + if (link) { + return link.textContent = text; + } } }, types: { audio: { regExp: /(.*\.(mp3|ogg|wav))$/, - el: function() { + el: function(a) { return $.el('audio', { controls: 'controls', preload: 'auto', - src: this.dataset.uid + src: a.dataset.uid }); } }, gist: { regExp: /.*(?:gist.github.com.*\/)([^\/][^\/]*)$/, - el: function() { + el: function(a) { var div; return div = $.el('iframe', { - src: "http://www.purplegene.com/script?url=https://gist.github.com/" + this.dataset.uid + ".js" + src: "http://www.purplegene.com/script?url=https://gist.github.com/" + a.dataset.uid + ".js" }); }, title: { api: function(uid) { return "https://api.github.com/gists/" + uid; }, - text: function() { + text: function(data) { var file, response; - response = JSON.parse(this.responseText).files; + response = data.files; for (file in response) { if (response.hasOwnProperty(file)) { return file; @@ -4606,49 +4614,49 @@ image: { regExp: /(http|www).*\.(gif|png|jpg|jpeg|bmp)$/, style: 'border: 0; width: auto; height: auto;', - el: function() { + el: function(a) { return $.el('div', { - innerHTML: "<a target=_blank href='" + this.dataset.href + "'><img src='" + this.dataset.href + "'></a>" + innerHTML: "<a target=_blank href='" + a.dataset.href + "'><img src='" + a.dataset.href + "'></a>" }); } }, InstallGentoo: { regExp: /.*(?:paste.installgentoo.com\/view\/)([0-9a-z_]+)/, - el: function() { + el: function(a) { return $.el('iframe', { - src: "http://paste.installgentoo.com/view/embed/" + this.dataset.uid + src: "http://paste.installgentoo.com/view/embed/" + a.dataset.uid }); } }, LiveLeak: { regExp: /.*(?:liveleak.com\/view.+i=)([0-9a-z_]+)/, - el: function() { + el: function(a) { return $.el('object', { - innerHTML: "<embed src='http://www.liveleak.com/e/" + this.dataset.uid + "?autostart=true' wmode='opaque' width='640' height='390' pluginspage='http://get.adobe.com/flashplayer/' type='application/x-shockwave-flash'></embed>" + innerHTML: "<embed src='http://www.liveleak.com/e/" + a.dataset.uid + "?autostart=true' wmode='opaque' width='640' height='390' pluginspage='http://get.adobe.com/flashplayer/' type='application/x-shockwave-flash'></embed>" }); } }, pastebin: { regExp: /.*(?:pastebin.com\/(?!u\/))([^#\&\?]*).*/, - el: function() { + el: function(a) { var div; return div = $.el('iframe', { - src: "http://pastebin.com/embed_iframe.php?i=" + this.dataset.uid + src: "http://pastebin.com/embed_iframe.php?i=" + a.dataset.uid }); } }, SoundCloud: { regExp: /.*(?:soundcloud.com\/|snd.sc\/)([^#\&\?]*).*/, style: 'height: auto; width: 500px; display: inline-block;', - el: function() { + el: function(a) { var div; div = $.el('div', { className: "soundcloud", name: "soundcloud" }); - $.ajax("//soundcloud.com/oembed?show_artwork=false&&maxwidth=500px&show_comments=false&format=json&url=https://www.soundcloud.com/" + this.dataset.uid, { + $.ajax("//soundcloud.com/oembed?show_artwork=false&&maxwidth=500px&show_comments=false&format=json&url=https://www.soundcloud.com/" + a.dataset.uid, { div: div, onloadend: function() { return this.div.innerHTML = JSON.parse(this.responseText).html; @@ -4660,25 +4668,25 @@ api: function(uid) { return "//soundcloud.com/oembed?show_artwork=false&&maxwidth=500px&show_comments=false&format=json&url=https://www.soundcloud.com/" + uid; }, - text: function() { - return JSON.parse(this.responseText).title; + text: function(data) { + return data.title; } } }, TwitchTV: { regExp: /.*(?:twitch.tv\/)([^#\&\?]*).*/, style: "border: none; width: 640px; height: 360px;", - el: function() { + el: function(a) { var channel, chapter, result, _; - if (result = /(\w+)\/(?:[a-z]\/)?(\d+)/i.exec(this.dataset.uid)) { + if (result = /(\w+)\/(?:[a-z]\/)?(\d+)/i.exec(a.dataset.uid)) { _ = result[0], channel = result[1], chapter = result[2]; return $.el('object', { data: 'http://www.twitch.tv/widgets/archive_embed_player.swf', innerHTML: "<param name='allowFullScreen' value='true' />\n<param name='flashvars' value='channel=" + channel + "&start_volume=25&auto_play=false" + (chapter ? "&chapter_id=" + chapter : "") + "' />" }); } else { - channel = (/(\w+)/.exec(this.dataset.uid))[0]; + channel = (/(\w+)/.exec(a.dataset.uid))[0]; return $.el('object', { data: "http://www.twitch.tv/widgets/live_embed_player.swf?channel=" + channel, innerHTML: "<param name=\"allowFullScreen\" value=\"true\" />\n<param name=\"movie\" value=\"http://www.twitch.tv/widgets/live_embed_player.swf\" />\n<param name=\"flashvars\" value=\"hostname=www.twitch.tv&channel=" + channel + "&auto_play=true&start_volume=25\" />" @@ -4689,50 +4697,50 @@ Vocaroo: { regExp: /.*(?:vocaroo.com\/)([^#\&\?]*).*/, style: 'border: 0; width: 150px; height: 45px;', - el: function() { + el: function(a) { return $.el('object', { - innerHTML: "<embed src='http://vocaroo.com/player.swf?playMediaID=" + (this.dataset.uid.replace(/^i\//, '')) + "&autoplay=0' wmode='opaque' width='150' height='45' pluginspage='http://get.adobe.com/flashplayer/' type='application/x-shockwave-flash'></embed>" + innerHTML: "<embed src='http://vocaroo.com/player.swf?playMediaID=" + (a.dataset.uid.replace(/^i\//, '')) + "&autoplay=0' wmode='opaque' width='150' height='45' pluginspage='http://get.adobe.com/flashplayer/' type='application/x-shockwave-flash'></embed>" }); } }, Vimeo: { regExp: /.*(?:vimeo.com\/)([^#\&\?]*).*/, - el: function() { + el: function(a) { return $.el('iframe', { - src: "//player.vimeo.com/video/" + this.dataset.uid + "?wmode=opaque" + src: "//player.vimeo.com/video/" + a.dataset.uid + "?wmode=opaque" }); }, title: { api: function(uid) { return "https://vimeo.com/api/oembed.json?url=http://vimeo.com/" + uid; }, - text: function() { - return JSON.parse(this.responseText).title; + text: function(data) { + return data.title; } } }, Vine: { regExp: /.*(?:vine.co\/)([^#\&\?]*).*/, style: 'border: none; width: 500px; height: 500px;', - el: function() { + el: function(a) { return $.el('iframe', { - src: "https://vine.co/" + this.dataset.uid + "/card" + src: "https://vine.co/" + a.dataset.uid + "/card" }); } }, YouTube: { regExp: /.*(?:youtu.be\/|youtube.*v=|youtube.*\/embed\/|youtube.*\/v\/|youtube.*videos\/)([^#\&\?]*)\??(t\=.*)?/, - el: function() { + el: function(a) { return $.el('iframe', { - src: "//www.youtube.com/embed/" + this.dataset.uid + (this.dataset.option ? '#' + this.dataset.option : '') + "?wmode=opaque" + src: "//www.youtube.com/embed/" + a.dataset.uid + (a.dataset.option ? '#' + a.dataset.option : '') + "?wmode=opaque" }); }, title: { api: function(uid) { return "https://gdata.youtube.com/feeds/api/videos/" + uid + "?alt=json&fields=title/text(),yt:noembed,app:control/yt:state/@reasonCode"; }, - text: function() { - return JSON.parse(this.responseText).entry.title.$t; + text: function(data) { + return data.entry.title.$t; } } } diff --git a/src/Linkification/Linkify.coffee b/src/Linkification/Linkify.coffee index 963a70d7f..acef93011 100644 --- a/src/Linkification/Linkify.coffee +++ b/src/Linkification/Linkify.coffee @@ -59,7 +59,6 @@ Linkify = items = @nodes.links i = 0 - while range = items[i++] if data = Linkify.services range Linkify.embed data if Conf['Embedding'] @@ -162,22 +161,27 @@ Linkify = if Conf['Auto-embed'] Linkify.cb.toggle.call embed + data.push embed + return title: (data) -> - [key, uid, options, link] = data + [key, uid, options, link, embed] = data return unless service = Linkify.types[key].title titles = Conf['CachedTitles'] if title = titles[uid] - link.textContent = title[0] + # Auto-embed may destroy our links. + if link + link.textContent = title[0] if Conf['Embedding'] - link.nextElementSibling.dataset.title = title[0] + embed.dataset.title = title[0] else try $.cache service.api(uid), -> - title = Linkify.cb.title.apply @, [data] + title = Linkify.cb.title @, data catch err - link.innerHTML = "[#{key}] <span class=warning>Title Link Blocked</span> (are you using NoScript?)</a>" + if link + link.innerHTML = "[#{key}] <span class=warning>Title Link Blocked</span> (are you using NoScript?)</a>" return if title titles[uid] = [title, Date.now()] @@ -197,7 +201,7 @@ Linkify = embed: (a) -> # We create an element to embed - el = (type = Linkify.types[a.dataset.key]).el.call a + el = (type = Linkify.types[a.dataset.key]).el a # Set style values. el.style.cssText = if style = type.style @@ -220,77 +224,77 @@ Linkify = return el - title: (data) -> - [key, uid, options, link] = data + title: (response, data) -> + [key, uid, options, link, embed] = data service = Linkify.types[key].title - link.textContent = switch @status + switch response.status when 200, 304 - text = "#{service.text.call @}" + text = "#{service.text JSON.parse response.responseText}" if Conf['Embedding'] - link.nextElementSibling.dataset.title = text - text + embed.dataset.title = text when 404 - "[#{key}] Not Found" + text = "[#{key}] Not Found" when 403 - "[#{key}] Forbidden or Private" + text = "[#{key}] Forbidden or Private" else - "[#{key}] #{@status}'d" + text = "[#{key}] #{@status}'d" + link.textContent = text if link types: audio: regExp: /(.*\.(mp3|ogg|wav))$/ - el: -> + el: (a) -> $.el 'audio', controls: 'controls' preload: 'auto' - src: @dataset.uid + src: a.dataset.uid gist: regExp: /.*(?:gist.github.com.*\/)([^\/][^\/]*)$/ - el: -> + el: (a) -> div = $.el 'iframe', # Github doesn't allow embedding straight from the site, so we use an external site to bypass that. - src: "http://www.purplegene.com/script?url=https://gist.github.com/#{@dataset.uid}.js" + src: "http://www.purplegene.com/script?url=https://gist.github.com/#{a.dataset.uid}.js" title: api: (uid) -> "https://api.github.com/gists/#{uid}" - text: -> - response = JSON.parse(@responseText).files + text: (data) -> + response = data.files return file for file of response when response.hasOwnProperty file image: regExp: /(http|www).*\.(gif|png|jpg|jpeg|bmp)$/ style: 'border: 0; width: auto; height: auto;' - el: -> + el: (a) -> $.el 'div', - innerHTML: "<a target=_blank href='#{@dataset.href}'><img src='#{@dataset.href}'></a>" + innerHTML: "<a target=_blank href='#{a.dataset.href}'><img src='#{a.dataset.href}'></a>" InstallGentoo: regExp: /.*(?:paste.installgentoo.com\/view\/)([0-9a-z_]+)/ - el: -> + el: (a) -> $.el 'iframe', - src: "http://paste.installgentoo.com/view/embed/#{@dataset.uid}" + src: "http://paste.installgentoo.com/view/embed/#{a.dataset.uid}" LiveLeak: regExp: /.*(?:liveleak.com\/view.+i=)([0-9a-z_]+)/ - el: -> + el: (a) -> $.el 'object', - innerHTML: "<embed src='http://www.liveleak.com/e/#{@dataset.uid}?autostart=true' wmode='opaque' width='640' height='390' pluginspage='http://get.adobe.com/flashplayer/' type='application/x-shockwave-flash'></embed>" + innerHTML: "<embed src='http://www.liveleak.com/e/#{a.dataset.uid}?autostart=true' wmode='opaque' width='640' height='390' pluginspage='http://get.adobe.com/flashplayer/' type='application/x-shockwave-flash'></embed>" pastebin: regExp: /.*(?:pastebin.com\/(?!u\/))([^#\&\?]*).*/ - el: -> + el: (a) -> div = $.el 'iframe', - src: "http://pastebin.com/embed_iframe.php?i=#{@dataset.uid}" + src: "http://pastebin.com/embed_iframe.php?i=#{a.dataset.uid}" SoundCloud: regExp: /.*(?:soundcloud.com\/|snd.sc\/)([^#\&\?]*).*/ style: 'height: auto; width: 500px; display: inline-block;' - el: -> + el: (a) -> div = $.el 'div', className: "soundcloud" name: "soundcloud" $.ajax( - "//soundcloud.com/oembed?show_artwork=false&&maxwidth=500px&show_comments=false&format=json&url=https://www.soundcloud.com/#{@dataset.uid}" + "//soundcloud.com/oembed?show_artwork=false&&maxwidth=500px&show_comments=false&format=json&url=https://www.soundcloud.com/#{a.dataset.uid}" div: div onloadend: -> @div.innerHTML = JSON.parse(@responseText).html @@ -298,13 +302,13 @@ Linkify = div title: api: (uid) -> "//soundcloud.com/oembed?show_artwork=false&&maxwidth=500px&show_comments=false&format=json&url=https://www.soundcloud.com/#{uid}" - text: -> JSON.parse(@responseText).title + text: (data) -> data.title TwitchTV: regExp: /.*(?:twitch.tv\/)([^#\&\?]*).*/ style: "border: none; width: 640px; height: 360px;" - el: -> - if result = /(\w+)\/(?:[a-z]\/)?(\d+)/i.exec @dataset.uid + el: (a) -> + if result = /(\w+)\/(?:[a-z]\/)?(\d+)/i.exec a.dataset.uid [_, channel, chapter] = result $.el 'object', @@ -315,7 +319,7 @@ Linkify = """ else - channel = (/(\w+)/.exec @dataset.uid)[0] + channel = (/(\w+)/.exec a.dataset.uid)[0] $.el 'object', data: "http://www.twitch.tv/widgets/live_embed_player.swf?channel=#{channel}" @@ -328,31 +332,31 @@ Linkify = Vocaroo: regExp: /.*(?:vocaroo.com\/)([^#\&\?]*).*/ style: 'border: 0; width: 150px; height: 45px;' - el: -> + el: (a) -> $.el 'object', - innerHTML: "<embed src='http://vocaroo.com/player.swf?playMediaID=#{@dataset.uid.replace /^i\//, ''}&autoplay=0' wmode='opaque' width='150' height='45' pluginspage='http://get.adobe.com/flashplayer/' type='application/x-shockwave-flash'></embed>" + innerHTML: "<embed src='http://vocaroo.com/player.swf?playMediaID=#{a.dataset.uid.replace /^i\//, ''}&autoplay=0' wmode='opaque' width='150' height='45' pluginspage='http://get.adobe.com/flashplayer/' type='application/x-shockwave-flash'></embed>" Vimeo: regExp: /.*(?:vimeo.com\/)([^#\&\?]*).*/ - el: -> + el: (a) -> $.el 'iframe', - src: "//player.vimeo.com/video/#{@dataset.uid}?wmode=opaque" + src: "//player.vimeo.com/video/#{a.dataset.uid}?wmode=opaque" title: api: (uid) -> "https://vimeo.com/api/oembed.json?url=http://vimeo.com/#{uid}" - text: -> JSON.parse(@responseText).title + text: (data) -> data.title Vine: regExp: /.*(?:vine.co\/)([^#\&\?]*).*/ style: 'border: none; width: 500px; height: 500px;' - el: -> + el: (a) -> $.el 'iframe', - src: "https://vine.co/#{@dataset.uid}/card" + src: "https://vine.co/#{a.dataset.uid}/card" YouTube: regExp: /.*(?:youtu.be\/|youtube.*v=|youtube.*\/embed\/|youtube.*\/v\/|youtube.*videos\/)([^#\&\?]*)\??(t\=.*)?/ - el: -> + el: (a) -> $.el 'iframe', - src: "//www.youtube.com/embed/#{@dataset.uid}#{if @dataset.option then '#' + @dataset.option else ''}?wmode=opaque" + src: "//www.youtube.com/embed/#{a.dataset.uid}#{if a.dataset.option then '#' + a.dataset.option else ''}?wmode=opaque" title: api: (uid) -> "https://gdata.youtube.com/feeds/api/videos/#{uid}?alt=json&fields=title/text(),yt:noembed,app:control/yt:state/@reasonCode" - text: -> JSON.parse(@responseText).entry.title.$t \ No newline at end of file + text: (data) -> data.entry.title.$t \ No newline at end of file From 3384c1b362a919bb5c6ada4a4cdb304a984f298c Mon Sep 17 00:00:00 2001 From: Zixaphir <zixaphirmoxphar@gmail.com> Date: Tue, 6 Aug 2013 22:03:04 -0700 Subject: [PATCH 127/172] Unneeded. --- builds/4chan-X.user.js | 3 +-- builds/crx/script.js | 3 +-- src/Linkification/Linkify.coffee | 3 +-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js index 52583ed31..5c81fea31 100644 --- a/builds/4chan-X.user.js +++ b/builds/4chan-X.user.js @@ -4652,9 +4652,8 @@ name: "soundcloud" }); $.ajax("//soundcloud.com/oembed?show_artwork=false&&maxwidth=500px&show_comments=false&format=json&url=https://www.soundcloud.com/" + a.dataset.uid, { - div: div, onloadend: function() { - return this.div.innerHTML = JSON.parse(this.responseText).html; + return div.innerHTML = JSON.parse(this.responseText).html; } }, false); return div; diff --git a/builds/crx/script.js b/builds/crx/script.js index da265f148..78397cd07 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -4657,9 +4657,8 @@ name: "soundcloud" }); $.ajax("//soundcloud.com/oembed?show_artwork=false&&maxwidth=500px&show_comments=false&format=json&url=https://www.soundcloud.com/" + a.dataset.uid, { - div: div, onloadend: function() { - return this.div.innerHTML = JSON.parse(this.responseText).html; + return div.innerHTML = JSON.parse(this.responseText).html; } }, false); return div; diff --git a/src/Linkification/Linkify.coffee b/src/Linkification/Linkify.coffee index acef93011..109ff8d8b 100644 --- a/src/Linkification/Linkify.coffee +++ b/src/Linkification/Linkify.coffee @@ -295,9 +295,8 @@ Linkify = name: "soundcloud" $.ajax( "//soundcloud.com/oembed?show_artwork=false&&maxwidth=500px&show_comments=false&format=json&url=https://www.soundcloud.com/#{a.dataset.uid}" - div: div onloadend: -> - @div.innerHTML = JSON.parse(@responseText).html + div.innerHTML = JSON.parse(@responseText).html false) div title: From c325b9eeda1837dfa363b2c8a281b7604a1716f5 Mon Sep 17 00:00:00 2001 From: Zixaphir <zixaphirmoxphar@gmail.com> Date: Tue, 6 Aug 2013 22:07:21 -0700 Subject: [PATCH 128/172] Let's just add some CoffeeScript flavor where possible <3 --- builds/4chan-X.user.js | 24 +++++++++++++++--------- builds/crx/script.js | 24 +++++++++++++++--------- src/Linkification/Linkify.coffee | 9 ++++----- 3 files changed, 34 insertions(+), 23 deletions(-) diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js index 5c81fea31..b9b1f506a 100644 --- a/builds/4chan-X.user.js +++ b/builds/4chan-X.user.js @@ -4594,12 +4594,12 @@ api: function(uid) { return "https://api.github.com/gists/" + uid; }, - text: function(data) { - var file, response; + text: function(_arg) { + var file, files; - response = data.files; - for (file in response) { - if (response.hasOwnProperty(file)) { + files = _arg.files; + for (file in files) { + if (files.hasOwnProperty(file)) { return file; } } @@ -4662,8 +4662,11 @@ api: function(uid) { return "//soundcloud.com/oembed?show_artwork=false&&maxwidth=500px&show_comments=false&format=json&url=https://www.soundcloud.com/" + uid; }, - text: function(data) { - return data.title; + text: function(_arg) { + var title; + + title = _arg.title; + return title; } } }, @@ -4708,8 +4711,11 @@ api: function(uid) { return "https://vimeo.com/api/oembed.json?url=http://vimeo.com/" + uid; }, - text: function(data) { - return data.title; + text: function(_arg) { + var title; + + title = _arg.title; + return title; } } }, diff --git a/builds/crx/script.js b/builds/crx/script.js index 78397cd07..bdff208f6 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -4599,12 +4599,12 @@ api: function(uid) { return "https://api.github.com/gists/" + uid; }, - text: function(data) { - var file, response; + text: function(_arg) { + var file, files; - response = data.files; - for (file in response) { - if (response.hasOwnProperty(file)) { + files = _arg.files; + for (file in files) { + if (files.hasOwnProperty(file)) { return file; } } @@ -4667,8 +4667,11 @@ api: function(uid) { return "//soundcloud.com/oembed?show_artwork=false&&maxwidth=500px&show_comments=false&format=json&url=https://www.soundcloud.com/" + uid; }, - text: function(data) { - return data.title; + text: function(_arg) { + var title; + + title = _arg.title; + return title; } } }, @@ -4713,8 +4716,11 @@ api: function(uid) { return "https://vimeo.com/api/oembed.json?url=http://vimeo.com/" + uid; }, - text: function(data) { - return data.title; + text: function(_arg) { + var title; + + title = _arg.title; + return title; } } }, diff --git a/src/Linkification/Linkify.coffee b/src/Linkification/Linkify.coffee index 109ff8d8b..1ede82f6b 100644 --- a/src/Linkification/Linkify.coffee +++ b/src/Linkification/Linkify.coffee @@ -257,9 +257,8 @@ Linkify = src: "http://www.purplegene.com/script?url=https://gist.github.com/#{a.dataset.uid}.js" title: api: (uid) -> "https://api.github.com/gists/#{uid}" - text: (data) -> - response = data.files - return file for file of response when response.hasOwnProperty file + text: ({files}) -> + return file for file of files when files.hasOwnProperty file image: regExp: /(http|www).*\.(gif|png|jpg|jpeg|bmp)$/ @@ -301,7 +300,7 @@ Linkify = div title: api: (uid) -> "//soundcloud.com/oembed?show_artwork=false&&maxwidth=500px&show_comments=false&format=json&url=https://www.soundcloud.com/#{uid}" - text: (data) -> data.title + text: ({title}) -> title TwitchTV: regExp: /.*(?:twitch.tv\/)([^#\&\?]*).*/ @@ -342,7 +341,7 @@ Linkify = src: "//player.vimeo.com/video/#{a.dataset.uid}?wmode=opaque" title: api: (uid) -> "https://vimeo.com/api/oembed.json?url=http://vimeo.com/#{uid}" - text: (data) -> data.title + text: ({title}) -> title Vine: regExp: /.*(?:vine.co\/)([^#\&\?]*).*/ From 14d9bbaeff4acf9d039c1a0937a6df77218dda0a Mon Sep 17 00:00:00 2001 From: Zixaphir <zixaphirmoxphar@gmail.com> Date: Tue, 6 Aug 2013 22:13:13 -0700 Subject: [PATCH 129/172] We're allowed to be funny. --- builds/4chan-X.user.js | 18 ++++++------------ builds/crx/script.js | 18 ++++++------------ src/Linkification/Linkify.coffee | 3 +-- 3 files changed, 13 insertions(+), 26 deletions(-) diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js index b9b1f506a..feb77829d 100644 --- a/builds/4chan-X.user.js +++ b/builds/4chan-X.user.js @@ -4327,6 +4327,12 @@ ExpandComment.callbacks.push(this.node); } if (Conf['Title Link']) { + this.types.Vimeo.api.text = this.types.SoundCould.api.text = function(_arg) { + var title; + + title = _arg.title; + return title; + }; $.sync('CachedTitles', Linkify.titleSync); } return Post.prototype.callbacks.push({ @@ -4661,12 +4667,6 @@ title: { api: function(uid) { return "//soundcloud.com/oembed?show_artwork=false&&maxwidth=500px&show_comments=false&format=json&url=https://www.soundcloud.com/" + uid; - }, - text: function(_arg) { - var title; - - title = _arg.title; - return title; } } }, @@ -4710,12 +4710,6 @@ title: { api: function(uid) { return "https://vimeo.com/api/oembed.json?url=http://vimeo.com/" + uid; - }, - text: function(_arg) { - var title; - - title = _arg.title; - return title; } } }, diff --git a/builds/crx/script.js b/builds/crx/script.js index bdff208f6..aa7abc0da 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -4332,6 +4332,12 @@ ExpandComment.callbacks.push(this.node); } if (Conf['Title Link']) { + this.types.Vimeo.api.text = this.types.SoundCould.api.text = function(_arg) { + var title; + + title = _arg.title; + return title; + }; $.sync('CachedTitles', Linkify.titleSync); } return Post.prototype.callbacks.push({ @@ -4666,12 +4672,6 @@ title: { api: function(uid) { return "//soundcloud.com/oembed?show_artwork=false&&maxwidth=500px&show_comments=false&format=json&url=https://www.soundcloud.com/" + uid; - }, - text: function(_arg) { - var title; - - title = _arg.title; - return title; } } }, @@ -4715,12 +4715,6 @@ title: { api: function(uid) { return "https://vimeo.com/api/oembed.json?url=http://vimeo.com/" + uid; - }, - text: function(_arg) { - var title; - - title = _arg.title; - return title; } } }, diff --git a/src/Linkification/Linkify.coffee b/src/Linkification/Linkify.coffee index 1ede82f6b..b18037368 100644 --- a/src/Linkification/Linkify.coffee +++ b/src/Linkification/Linkify.coffee @@ -26,6 +26,7 @@ Linkify = ExpandComment.callbacks.push @node if Conf['Title Link'] + @types.Vimeo.api.text = @types.SoundCould.api.text = ({title}) -> title $.sync 'CachedTitles', Linkify.titleSync Post::callbacks.push @@ -300,7 +301,6 @@ Linkify = div title: api: (uid) -> "//soundcloud.com/oembed?show_artwork=false&&maxwidth=500px&show_comments=false&format=json&url=https://www.soundcloud.com/#{uid}" - text: ({title}) -> title TwitchTV: regExp: /.*(?:twitch.tv\/)([^#\&\?]*).*/ @@ -341,7 +341,6 @@ Linkify = src: "//player.vimeo.com/video/#{a.dataset.uid}?wmode=opaque" title: api: (uid) -> "https://vimeo.com/api/oembed.json?url=http://vimeo.com/#{uid}" - text: ({title}) -> title Vine: regExp: /.*(?:vine.co\/)([^#\&\?]*).*/ From 2234d9141f846f6643d756ddd25ecf22b75b2c39 Mon Sep 17 00:00:00 2001 From: Zixaphir <zixaphirmoxphar@gmail.com> Date: Tue, 6 Aug 2013 23:47:56 -0700 Subject: [PATCH 130/172] I don't like it. --- builds/4chan-X.user.js | 12 ++++++------ builds/crx/script.js | 12 ++++++------ src/Linkification/Linkify.coffee | 3 ++- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js index feb77829d..159f0fbb0 100644 --- a/builds/4chan-X.user.js +++ b/builds/4chan-X.user.js @@ -4327,12 +4327,6 @@ ExpandComment.callbacks.push(this.node); } if (Conf['Title Link']) { - this.types.Vimeo.api.text = this.types.SoundCould.api.text = function(_arg) { - var title; - - title = _arg.title; - return title; - }; $.sync('CachedTitles', Linkify.titleSync); } return Post.prototype.callbacks.push({ @@ -4667,6 +4661,9 @@ title: { api: function(uid) { return "//soundcloud.com/oembed?show_artwork=false&&maxwidth=500px&show_comments=false&format=json&url=https://www.soundcloud.com/" + uid; + }, + text: function(_) { + return _.title; } } }, @@ -4710,6 +4707,9 @@ title: { api: function(uid) { return "https://vimeo.com/api/oembed.json?url=http://vimeo.com/" + uid; + }, + text: function(_) { + return _.title; } } }, diff --git a/builds/crx/script.js b/builds/crx/script.js index aa7abc0da..d191b5e49 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -4332,12 +4332,6 @@ ExpandComment.callbacks.push(this.node); } if (Conf['Title Link']) { - this.types.Vimeo.api.text = this.types.SoundCould.api.text = function(_arg) { - var title; - - title = _arg.title; - return title; - }; $.sync('CachedTitles', Linkify.titleSync); } return Post.prototype.callbacks.push({ @@ -4672,6 +4666,9 @@ title: { api: function(uid) { return "//soundcloud.com/oembed?show_artwork=false&&maxwidth=500px&show_comments=false&format=json&url=https://www.soundcloud.com/" + uid; + }, + text: function(_) { + return _.title; } } }, @@ -4715,6 +4712,9 @@ title: { api: function(uid) { return "https://vimeo.com/api/oembed.json?url=http://vimeo.com/" + uid; + }, + text: function(_) { + return _.title; } } }, diff --git a/src/Linkification/Linkify.coffee b/src/Linkification/Linkify.coffee index b18037368..b2151e945 100644 --- a/src/Linkification/Linkify.coffee +++ b/src/Linkification/Linkify.coffee @@ -26,7 +26,6 @@ Linkify = ExpandComment.callbacks.push @node if Conf['Title Link'] - @types.Vimeo.api.text = @types.SoundCould.api.text = ({title}) -> title $.sync 'CachedTitles', Linkify.titleSync Post::callbacks.push @@ -301,6 +300,7 @@ Linkify = div title: api: (uid) -> "//soundcloud.com/oembed?show_artwork=false&&maxwidth=500px&show_comments=false&format=json&url=https://www.soundcloud.com/#{uid}" + text: (_) -> _.title TwitchTV: regExp: /.*(?:twitch.tv\/)([^#\&\?]*).*/ @@ -341,6 +341,7 @@ Linkify = src: "//player.vimeo.com/video/#{a.dataset.uid}?wmode=opaque" title: api: (uid) -> "https://vimeo.com/api/oembed.json?url=http://vimeo.com/#{uid}" + text: (_) -> _.title Vine: regExp: /.*(?:vine.co\/)([^#\&\?]*).*/ From 0325539d43ed79e06eb5256c49668291c3ea4382 Mon Sep 17 00:00:00 2001 From: Zixaphir <zixaphirmoxphar@gmail.com> Date: Wed, 7 Aug 2013 00:21:26 -0700 Subject: [PATCH 131/172] Let's dig. --- LICENSE | 2 +- builds/4chan-X.user.js | 22 +++++++++++----------- builds/crx/script.js | 22 +++++++++++----------- src/General/lib/$.coffee | 4 ++-- src/General/lib/post.class | 17 +++++++++-------- 5 files changed, 34 insertions(+), 33 deletions(-) diff --git a/LICENSE b/LICENSE index 43b9ec419..c83c43ea3 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,5 @@ /* -* 4chan X - Version 1.2.25 - 2013-08-06 +* 4chan X - Version 1.2.25 - 2013-08-07 * * Licensed under the MIT license. * https://github.com/seaweedchan/4chan-x/blob/master/LICENSE diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js index 159f0fbb0..e19d45057 100644 --- a/builds/4chan-X.user.js +++ b/builds/4chan-X.user.js @@ -19,7 +19,7 @@ // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwAgMAAAAqbBEUAAAACVBMVEUAAGcAAABmzDNZt9VtAAAAAXRSTlMAQObYZgAAAHFJREFUKFOt0LENACEIBdBv4Qju4wgWanEj3D6OcIVMKaitYHEU/jwTCQj8W75kiVCSBvdQ5/AvfVHBin11BgdRq3ysBgfwBDRrj3MCIA+oAQaku/Q1cNctrAmyDl577tOThYt/Y1RBM4DgOHzM0HFTAyLukH/cmRnqAAAAAElFTkSuQmCC // ==/UserScript== /* -* 4chan X - Version 1.2.25 - 2013-08-06 +* 4chan X - Version 1.2.25 - 2013-08-07 * * Licensed under the MIT license. * https://github.com/seaweedchan/4chan-x/blob/master/LICENSE @@ -551,7 +551,7 @@ $.X = function(path, root) { root || (root = d.body); - return d.evaluate(path, root, null, 6, null); + return d.evaluate(path, root, null, 7, null); }; $.addClass = function(el, className) { @@ -963,21 +963,21 @@ } Post.prototype.parseComment = function() { - var bq, i, node, nodes, text, _i, _len, _ref; + var bq, i, node, nodes, text; bq = this.nodes.comment.cloneNode(true); - _ref = $$('.abbr, .capcodeReplies, .exif, b', bq); - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - node = _ref[_i]; + nodes = $$('.abbr, .capcodeReplies, .exif, b', bq); + i = 0; + while (node = nodes[i++]) { $.rm(node); } - text = []; - nodes = d.evaluate('.//br|.//text()', bq, null, 7, null); + text = ""; + nodes = $.X('.//br|.//text()', bq); i = 0; - while (i < nodes.snapshotLength) { - text.push(nodes.snapshotItem(i++).data || '\n'); + while (node = nodes.snapshotItem(i++)) { + text = +node.data || '\n'; } - return this.info.comment = text.join('').trim().replace(/\s+$/gm, ''); + return this.info.comment = text.trim().replace(/\s+$/gm, ''); }; Post.prototype.parseQuotes = function() { diff --git a/builds/crx/script.js b/builds/crx/script.js index d191b5e49..5e80d1718 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -1,6 +1,6 @@ // Generated by CoffeeScript /* -* 4chan X - Version 1.2.25 - 2013-08-06 +* 4chan X - Version 1.2.25 - 2013-08-07 * * Licensed under the MIT license. * https://github.com/seaweedchan/4chan-x/blob/master/LICENSE @@ -532,7 +532,7 @@ $.X = function(path, root) { root || (root = d.body); - return d.evaluate(path, root, null, 6, null); + return d.evaluate(path, root, null, 7, null); }; $.addClass = function(el, className) { @@ -974,21 +974,21 @@ } Post.prototype.parseComment = function() { - var bq, i, node, nodes, text, _i, _len, _ref; + var bq, i, node, nodes, text; bq = this.nodes.comment.cloneNode(true); - _ref = $$('.abbr, .capcodeReplies, .exif, b', bq); - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - node = _ref[_i]; + nodes = $$('.abbr, .capcodeReplies, .exif, b', bq); + i = 0; + while (node = nodes[i++]) { $.rm(node); } - text = []; - nodes = d.evaluate('.//br|.//text()', bq, null, 7, null); + text = ""; + nodes = $.X('.//br|.//text()', bq); i = 0; - while (i < nodes.snapshotLength) { - text.push(nodes.snapshotItem(i++).data || '\n'); + while (node = nodes.snapshotItem(i++)) { + text = +node.data || '\n'; } - return this.info.comment = text.join('').trim().replace(/\s+$/gm, ''); + return this.info.comment = text.trim().replace(/\s+$/gm, ''); }; Post.prototype.parseQuotes = function() { diff --git a/src/General/lib/$.coffee b/src/General/lib/$.coffee index a9afda0bb..0bdf1df75 100644 --- a/src/General/lib/$.coffee +++ b/src/General/lib/$.coffee @@ -143,8 +143,8 @@ $.x = (path, root) -> $.X = (path, root) -> root or= d.body - # XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE === 6 - d.evaluate path, root, null, 6, null + # XPathResult.ORDERED_NODE_SNAPSHOT_TYPE === 7 + d.evaluate path, root, null, 7, null $.addClass = (el, className) -> el.classList.add className diff --git a/src/General/lib/post.class b/src/General/lib/post.class index 02e9fb9a0..8529473ba 100644 --- a/src/General/lib/post.class +++ b/src/General/lib/post.class @@ -72,15 +72,16 @@ class Post # Preceding and following new lines. # Trailing spaces. bq = @nodes.comment.cloneNode true - for node in $$ '.abbr, .capcodeReplies, .exif, b', bq - $.rm node - text = [] - # XPathResult.ORDERED_NODE_SNAPSHOT_TYPE === 7 - nodes = d.evaluate './/br|.//text()', bq, null, 7, null + nodes = $$ '.abbr, .capcodeReplies, .exif, b', bq i = 0 - while i < nodes.snapshotLength - text.push nodes.snapshotItem(i++).data or '\n' - @info.comment = text.join('').trim().replace /\s+$/gm, '' + while node = nodes[i++] + $.rm node + text = "" + nodes = $.X './/br|.//text()', bq + i = 0 + while node = nodes.snapshotItem i++ + text =+ node.data or '\n' + @info.comment = text.trim().replace /\s+$/gm, '' parseQuotes: -> @quotes = [] From 8b01a46f30a7d48749f93babc8c9cbbed720853c Mon Sep 17 00:00:00 2001 From: Zixaphir <zixaphirmoxphar@gmail.com> Date: Wed, 7 Aug 2013 01:54:29 -0700 Subject: [PATCH 132/172] I have a thing about these kinda things. --- builds/4chan-X.user.js | 57 +++++++++++++++--------------- builds/crx/script.js | 57 +++++++++++++++--------------- src/General/Build.coffee | 53 +--------------------------- src/General/Get.coffee | 49 +++++++++++++------------- src/General/html/Build/post.html | 59 ++++++++++++++++++++++++++++++++ src/General/lib/post.class | 2 +- 6 files changed, 144 insertions(+), 133 deletions(-) create mode 100644 src/General/html/Build/post.html diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js index e19d45057..a54e6a059 100644 --- a/builds/4chan-X.user.js +++ b/builds/4chan-X.user.js @@ -975,7 +975,7 @@ nodes = $.X('.//br|.//text()', bq); i = 0; while (node = nodes.snapshotItem(i++)) { - text = +node.data || '\n'; + text += node.data || '\n'; } return this.info.comment = text.trim().replace(/\s+$/gm, ''); }; @@ -1972,7 +1972,7 @@ container = $.el('div', { id: "pc" + postID, className: "postContainer " + (isOP ? 'op' : 'reply') + "Container", - innerHTML: (isOP ? '' : "<div class=sideArrows id=sa" + postID + ">>></div>") + ("<div id=p" + postID + " class='post " + (isOP ? 'op' : 'reply') + (capcode === 'admin_highlight' ? ' highlightPost' : '') + "'>") + ("<div class='postInfoM mobile' id=pim" + postID + ">") + ("<span class='nameBlock" + capcodeClass + "'>") + ("<span class=name>" + (name || '') + "</span>") + tripcode + capcodeStart + capcode + userID + flag + sticky + closed + ("<br>" + subject) + ("</span><span class='dateTime postNum' data-utc=" + dateUTC + ">" + date) + ("<a href=" + ("/" + boardID + "/res/" + threadID + "#p" + postID) + ">No.</a>") + ("<a href='" + (g.VIEW === 'thread' && g.THREADID === +threadID ? "javascript:quote(" + postID + ")" : "/" + boardID + "/res/" + threadID + "#q" + postID) + "'>" + postID + "</a>") + '</span>' + '</div>' + (isOP ? fileHTML : '') + ("<div class='postInfo desktop' id=pi" + postID + ">") + ("<input type=checkbox name=" + postID + " value=delete> ") + ("" + subject + " ") + ("<span class='nameBlock" + capcodeClass + "'>") + emailStart + ("<span class=name>" + (name || '') + "</span>") + tripcode + capcodeStart + emailEnd + capcode + userID + flag + sticky + closed + ' </span> ' + ("<span class=dateTime data-utc=" + dateUTC + ">" + date + "</span> ") + "<span class='postNum desktop'>" + ("<a href=" + ("/" + boardID + "/res/" + threadID + "#p" + postID) + " title='Highlight this post'>No.</a>") + ("<a href='" + (g.VIEW === 'thread' && g.THREADID === +threadID ? "javascript:quote(" + postID + ")" : "/" + boardID + "/res/" + threadID + "#q" + postID) + "' title='Quote this post'>" + postID + "</a>") + '</span>' + '</div>' + (isOP ? '' : fileHTML) + ("<blockquote class=postMessage id=m" + postID + ">" + (comment || '') + capcodeReplies + "</blockquote> ") + '</div>' + innerHTML: "" + (isOP ? '' : "<div class=sideArrows id=sa" + postID + ">>></div>") + "<div id=p" + postID + " class='post " + (isOP ? 'op' : 'reply') + (capcode === 'admin_highlight' ? ' highlightPost' : '') + "'><div class='postInfoM mobile' id=pim" + postID + "><span class='nameBlock" + capcodeClass + "'><span class=name>" + (name || '') + "</span>" + (tripcode + capcodeStart + capcode + userID + flag + sticky + closed) + "<br>" + subject + "</span><span class='dateTime postNum' data-utc=" + dateUTC + ">" + date + "<a href=" + ("/" + boardID + "/res/" + threadID + "#p" + postID) + ">No.</a><a href='" + (g.VIEW === 'thread' && g.THREADID === +threadID ? "javascript:quote(" + postID + ")" : "/" + boardID + "/res/" + threadID + "#q" + postID) + "'>" + postID + "</a></span></div>" + (isOP ? fileHTML : '') + "<div class='postInfo desktop' id=pi" + postID + "><input type=checkbox name=" + postID + " value=delete>" + subject + "<span class='nameBlock" + capcodeClass + "'>" + emailStart + "<span class=name>" + (name || '') + "</span>" + (tripcode + capcodeStart + emailEnd + capcode + userID + flag + sticky + closed) + "</span>" + " " + "<span class=dateTime data-utc=" + dateUTC + ">" + date + "</span>" + " " + "<span class='postNum desktop'><a href=" + ("/" + boardID + "/res/" + threadID + "#p" + postID) + " title='Highlight this post'>No.</a><a href='" + (g.VIEW === 'thread' && g.THREADID === +threadID ? "javascript:quote(" + postID + ")" : "/" + boardID + "/res/" + threadID + "#q" + postID) + "' title='Quote this post'>" + postID + "</a></span></div>" + (isOP ? '' : fileHTML) + "<blockquote class=postMessage id=m" + postID + ">" + (comment || '') + capcodeReplies + "</blockquote>" + " " + "</div>" }); _ref = $$('.quotelink', container); for (_i = 0, _len = _ref.length; _i < _len; _i++) { @@ -2176,32 +2176,7 @@ bq = $.el('blockquote', { textContent: data.comment }); - bq.innerHTML = bq.innerHTML.replace(/\n|\[\/?b\]|\[\/?spoiler\]|\[\/?code\]|\[\/?moot\]|\[\/?banned\]/g, function(text) { - switch (text) { - case '\n': - return '<br>'; - case '[b]': - return '<b>'; - case '[/b]': - return '</b>'; - case '[spoiler]': - return '<s>'; - case '[/spoiler]': - return '</s>'; - case '[code]': - return '<pre class=prettyprint>'; - case '[/code]': - return '</pre>'; - case '[moot]': - return '<div style="padding:5px;margin-left:.5em;border-color:#faa;border:2px dashed rgba(255,0,0,.1);border-radius:2px">'; - case '[/moot]': - return '</div>'; - case '[banned]': - return '<b style="color: red;">'; - case '[/banned]': - return '</b>'; - } - }); + bq.innerHTML = bq.innerHTML.replace(/\n|\[\/?b\]|\[\/?spoiler\]|\[\/?code\]|\[\/?moot\]|\[\/?banned\]/g, Get.parseMarkup); comment = bq.innerHTML.replace(/(^|>)(>[^<$]*)(<|$)/g, '$1<span class=quote>$2</span>$3').replace(/((>){2}(>\/[a-z\d]+\/)?\d+)/g, '<span class=deadlink>$1</span>'); threadID = +data.thread_num; o = { @@ -2251,6 +2226,32 @@ }); Main.callbackNodes(Post, [post]); return Get.insert(post, root, context); + }, + parseMarkup: function(text) { + switch (text) { + case '\n': + return '<br>'; + case '[b]': + return '<b>'; + case '[/b]': + return '</b>'; + case '[spoiler]': + return '<s>'; + case '[/spoiler]': + return '</s>'; + case '[code]': + return '<pre class=prettyprint>'; + case '[/code]': + return '</pre>'; + case '[moot]': + return '<div style="padding:5px;margin-left:.5em;border-color:#faa;border:2px dashed rgba(255,0,0,.1);border-radius:2px">'; + case '[/moot]': + return '</div>'; + case '[banned]': + return '<b style="color: red;">'; + case '[/banned]': + return '</b>'; + } } }; diff --git a/builds/crx/script.js b/builds/crx/script.js index 5e80d1718..29c696204 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -986,7 +986,7 @@ nodes = $.X('.//br|.//text()', bq); i = 0; while (node = nodes.snapshotItem(i++)) { - text = +node.data || '\n'; + text += node.data || '\n'; } return this.info.comment = text.trim().replace(/\s+$/gm, ''); }; @@ -1984,7 +1984,7 @@ container = $.el('div', { id: "pc" + postID, className: "postContainer " + (isOP ? 'op' : 'reply') + "Container", - innerHTML: (isOP ? '' : "<div class=sideArrows id=sa" + postID + ">>></div>") + ("<div id=p" + postID + " class='post " + (isOP ? 'op' : 'reply') + (capcode === 'admin_highlight' ? ' highlightPost' : '') + "'>") + ("<div class='postInfoM mobile' id=pim" + postID + ">") + ("<span class='nameBlock" + capcodeClass + "'>") + ("<span class=name>" + (name || '') + "</span>") + tripcode + capcodeStart + capcode + userID + flag + sticky + closed + ("<br>" + subject) + ("</span><span class='dateTime postNum' data-utc=" + dateUTC + ">" + date) + ("<a href=" + ("/" + boardID + "/res/" + threadID + "#p" + postID) + ">No.</a>") + ("<a href='" + (g.VIEW === 'thread' && g.THREADID === +threadID ? "javascript:quote(" + postID + ")" : "/" + boardID + "/res/" + threadID + "#q" + postID) + "'>" + postID + "</a>") + '</span>' + '</div>' + (isOP ? fileHTML : '') + ("<div class='postInfo desktop' id=pi" + postID + ">") + ("<input type=checkbox name=" + postID + " value=delete> ") + ("" + subject + " ") + ("<span class='nameBlock" + capcodeClass + "'>") + emailStart + ("<span class=name>" + (name || '') + "</span>") + tripcode + capcodeStart + emailEnd + capcode + userID + flag + sticky + closed + ' </span> ' + ("<span class=dateTime data-utc=" + dateUTC + ">" + date + "</span> ") + "<span class='postNum desktop'>" + ("<a href=" + ("/" + boardID + "/res/" + threadID + "#p" + postID) + " title='Highlight this post'>No.</a>") + ("<a href='" + (g.VIEW === 'thread' && g.THREADID === +threadID ? "javascript:quote(" + postID + ")" : "/" + boardID + "/res/" + threadID + "#q" + postID) + "' title='Quote this post'>" + postID + "</a>") + '</span>' + '</div>' + (isOP ? '' : fileHTML) + ("<blockquote class=postMessage id=m" + postID + ">" + (comment || '') + capcodeReplies + "</blockquote> ") + '</div>' + innerHTML: "" + (isOP ? '' : "<div class=sideArrows id=sa" + postID + ">>></div>") + "<div id=p" + postID + " class='post " + (isOP ? 'op' : 'reply') + (capcode === 'admin_highlight' ? ' highlightPost' : '') + "'><div class='postInfoM mobile' id=pim" + postID + "><span class='nameBlock" + capcodeClass + "'><span class=name>" + (name || '') + "</span>" + (tripcode + capcodeStart + capcode + userID + flag + sticky + closed) + "<br>" + subject + "</span><span class='dateTime postNum' data-utc=" + dateUTC + ">" + date + "<a href=" + ("/" + boardID + "/res/" + threadID + "#p" + postID) + ">No.</a><a href='" + (g.VIEW === 'thread' && g.THREADID === +threadID ? "javascript:quote(" + postID + ")" : "/" + boardID + "/res/" + threadID + "#q" + postID) + "'>" + postID + "</a></span></div>" + (isOP ? fileHTML : '') + "<div class='postInfo desktop' id=pi" + postID + "><input type=checkbox name=" + postID + " value=delete>" + subject + "<span class='nameBlock" + capcodeClass + "'>" + emailStart + "<span class=name>" + (name || '') + "</span>" + (tripcode + capcodeStart + emailEnd + capcode + userID + flag + sticky + closed) + "</span>" + " " + "<span class=dateTime data-utc=" + dateUTC + ">" + date + "</span>" + " " + "<span class='postNum desktop'><a href=" + ("/" + boardID + "/res/" + threadID + "#p" + postID) + " title='Highlight this post'>No.</a><a href='" + (g.VIEW === 'thread' && g.THREADID === +threadID ? "javascript:quote(" + postID + ")" : "/" + boardID + "/res/" + threadID + "#q" + postID) + "' title='Quote this post'>" + postID + "</a></span></div>" + (isOP ? '' : fileHTML) + "<blockquote class=postMessage id=m" + postID + ">" + (comment || '') + capcodeReplies + "</blockquote>" + " " + "</div>" }); _ref = $$('.quotelink', container); for (_i = 0, _len = _ref.length; _i < _len; _i++) { @@ -2188,32 +2188,7 @@ bq = $.el('blockquote', { textContent: data.comment }); - bq.innerHTML = bq.innerHTML.replace(/\n|\[\/?b\]|\[\/?spoiler\]|\[\/?code\]|\[\/?moot\]|\[\/?banned\]/g, function(text) { - switch (text) { - case '\n': - return '<br>'; - case '[b]': - return '<b>'; - case '[/b]': - return '</b>'; - case '[spoiler]': - return '<s>'; - case '[/spoiler]': - return '</s>'; - case '[code]': - return '<pre class=prettyprint>'; - case '[/code]': - return '</pre>'; - case '[moot]': - return '<div style="padding:5px;margin-left:.5em;border-color:#faa;border:2px dashed rgba(255,0,0,.1);border-radius:2px">'; - case '[/moot]': - return '</div>'; - case '[banned]': - return '<b style="color: red;">'; - case '[/banned]': - return '</b>'; - } - }); + bq.innerHTML = bq.innerHTML.replace(/\n|\[\/?b\]|\[\/?spoiler\]|\[\/?code\]|\[\/?moot\]|\[\/?banned\]/g, Get.parseMarkup); comment = bq.innerHTML.replace(/(^|>)(>[^<$]*)(<|$)/g, '$1<span class=quote>$2</span>$3').replace(/((>){2}(>\/[a-z\d]+\/)?\d+)/g, '<span class=deadlink>$1</span>'); threadID = +data.thread_num; o = { @@ -2263,6 +2238,32 @@ }); Main.callbackNodes(Post, [post]); return Get.insert(post, root, context); + }, + parseMarkup: function(text) { + switch (text) { + case '\n': + return '<br>'; + case '[b]': + return '<b>'; + case '[/b]': + return '</b>'; + case '[spoiler]': + return '<s>'; + case '[/spoiler]': + return '</s>'; + case '[code]': + return '<pre class=prettyprint>'; + case '[/code]': + return '</pre>'; + case '[moot]': + return '<div style="padding:5px;margin-left:.5em;border-color:#faa;border:2px dashed rgba(255,0,0,.1);border-radius:2px">'; + case '[/moot]': + return '</div>'; + case '[banned]': + return '<b style="color: red;">'; + case '[/banned]': + return '</b>'; + } } }; diff --git a/src/General/Build.coffee b/src/General/Build.coffee index bb0903db1..cd4f73e01 100644 --- a/src/General/Build.coffee +++ b/src/General/Build.coffee @@ -214,58 +214,7 @@ Build = container = $.el 'div', id: "pc#{postID}" className: "postContainer #{if isOP then 'op' else 'reply'}Container" - innerHTML: \ - (if isOP then '' else "<div class=sideArrows id=sa#{postID}>>></div>") + - "<div id=p#{postID} class='post #{if isOP then 'op' else 'reply'}#{ - if capcode is 'admin_highlight' - ' highlightPost' - else - '' - }'>" + - - "<div class='postInfoM mobile' id=pim#{postID}>" + - "<span class='nameBlock#{capcodeClass}'>" + - "<span class=name>#{name or ''}</span>" + tripcode + - capcodeStart + capcode + userID + flag + sticky + closed + - "<br>#{subject}" + - "</span><span class='dateTime postNum' data-utc=#{dateUTC}>#{date}" + - "<a href=#{"/#{boardID}/res/#{threadID}#p#{postID}"}>No.</a>" + - "<a href='#{ - if g.VIEW is 'thread' and g.THREADID is +threadID - "javascript:quote(#{postID})" - else - "/#{boardID}/res/#{threadID}#q#{postID}" - }'>#{postID}</a>" + - '</span>' + - '</div>' + - - (if isOP then fileHTML else '') + - - "<div class='postInfo desktop' id=pi#{postID}>" + - "<input type=checkbox name=#{postID} value=delete> " + - "#{subject} " + - "<span class='nameBlock#{capcodeClass}'>" + - emailStart + - "<span class=name>#{name or ''}</span>" + tripcode + - capcodeStart + emailEnd + capcode + userID + flag + sticky + closed + - ' </span> ' + - "<span class=dateTime data-utc=#{dateUTC}>#{date}</span> " + - "<span class='postNum desktop'>" + - "<a href=#{"/#{boardID}/res/#{threadID}#p#{postID}"} title='Highlight this post'>No.</a>" + - "<a href='#{ - if g.VIEW is 'thread' and g.THREADID is +threadID - "javascript:quote(#{postID})" - else - "/#{boardID}/res/#{threadID}#q#{postID}" - }' title='Quote this post'>#{postID}</a>" + - '</span>' + - '</div>' + - - (if isOP then '' else fileHTML) + - - "<blockquote class=postMessage id=m#{postID}>#{comment or ''}#{capcodeReplies}</blockquote> " + - - '</div>' + innerHTML: <%= grunt.file.read('src/General/html/Build/post.html').replace(/>\s+/g, '>').replace(/\s+</g, '<').replace(/\s+/g, ' ').trim() %> for quote in $$ '.quotelink', container href = quote.getAttribute 'href' diff --git a/src/General/Get.coffee b/src/General/Get.coffee index a5c1a54c1..50d32684b 100644 --- a/src/General/Get.coffee +++ b/src/General/Get.coffee @@ -153,30 +153,7 @@ Get = | \[/?code\] | \[/?moot\] | \[/?banned\] - ///g, (text) -> - switch text - when '\n' - '<br>' - when '[b]' - '<b>' - when '[/b]' - '</b>' - when '[spoiler]' - '<s>' - when '[/spoiler]' - '</s>' - when '[code]' - '<pre class=prettyprint>' - when '[/code]' - '</pre>' - when '[moot]' - '<div style="padding:5px;margin-left:.5em;border-color:#faa;border:2px dashed rgba(255,0,0,.1);border-radius:2px">' - when '[/moot]' - '</div>' - when '[banned]' - '<b style="color: red;">' - when '[/banned]' - '</b>' + ///g, Get.parseMarkup comment = bq.innerHTML # greentext @@ -228,3 +205,27 @@ Get = isArchived: true Main.callbackNodes Post, [post] Get.insert post, root, context + parseMarkup: (text) -> + switch text + when '\n' + '<br>' + when '[b]' + '<b>' + when '[/b]' + '</b>' + when '[spoiler]' + '<s>' + when '[/spoiler]' + '</s>' + when '[code]' + '<pre class=prettyprint>' + when '[/code]' + '</pre>' + when '[moot]' + '<div style="padding:5px;margin-left:.5em;border-color:#faa;border:2px dashed rgba(255,0,0,.1);border-radius:2px">' + when '[/moot]' + '</div>' + when '[banned]' + '<b style="color: red;">' + when '[/banned]' + '</b>' \ No newline at end of file diff --git a/src/General/html/Build/post.html b/src/General/html/Build/post.html new file mode 100644 index 000000000..cf833ebdd --- /dev/null +++ b/src/General/html/Build/post.html @@ -0,0 +1,59 @@ +"""#{if isOP then '' else "<div class=sideArrows id=sa#{postID}>>></div>"} +<div id=p#{postID} class='post #{if isOP then 'op' else 'reply'}#{ + if capcode is 'admin_highlight' then + ' highlightPost' + else + '' +}'> + + <div class='postInfoM mobile' id=pim#{postID}> + <span class='nameBlock#{capcodeClass}'> + <span class=name> + #{name or ''} + </span> + #{tripcode + capcodeStart + capcode + userID + flag + sticky + closed} + <br>#{subject} + </span> + <span class='dateTime postNum' data-utc=#{dateUTC}> + #{date} + <a href=#{"/#{boardID}/res/#{threadID}#p#{postID}"}> + No. + </a> + <a href='#{ + if g.VIEW is 'thread' and g.THREADID is +threadID then + "javascript:quote(#{postID})" + else + "/#{boardID}/res/#{threadID}#q#{postID}" + }'> + #{postID} + </a> + </span> + </div> + + #{if isOP then fileHTML else ''} + + <div class='postInfo desktop' id=pi#{postID}> + <input type=checkbox name=#{postID} value=delete> + #{subject} + <span class='nameBlock#{capcodeClass}'> + #{emailStart} + <span class=name>#{name or ''}</span> + #{tripcode + capcodeStart + emailEnd + capcode + userID + flag + sticky + closed} + </span>#{" "} + <span class=dateTime data-utc=#{dateUTC}>#{date}</span>#{" "} + <span class='postNum desktop'> + <a href=#{"/#{boardID}/res/#{threadID}#p#{postID}"} title='Highlight this post'>No.</a> + <a href='#{ + if g.VIEW is 'thread' and g.THREADID is +threadID then + "javascript:quote(#{postID})" + else + "/#{boardID}/res/#{threadID}#q#{postID}" + }' title='Quote this post'>#{postID}</a> + </span> + </div> + + #{if isOP then '' else fileHTML} + + <blockquote class=postMessage id=m#{postID}>#{comment or ''}#{capcodeReplies}</blockquote>#{" "} + +</div>""" \ No newline at end of file diff --git a/src/General/lib/post.class b/src/General/lib/post.class index 8529473ba..ee4391bbf 100644 --- a/src/General/lib/post.class +++ b/src/General/lib/post.class @@ -80,7 +80,7 @@ class Post nodes = $.X './/br|.//text()', bq i = 0 while node = nodes.snapshotItem i++ - text =+ node.data or '\n' + text += node.data or '\n' @info.comment = text.trim().replace /\s+$/gm, '' parseQuotes: -> From bbe93eb475a956c46b6d9b6f225b03e2f40317f8 Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Wed, 7 Aug 2013 15:24:21 +0200 Subject: [PATCH 133/172] Shave a line. --- src/Monitoring/Unread.coffee | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Monitoring/Unread.coffee b/src/Monitoring/Unread.coffee index c31c63b56..da1419be5 100644 --- a/src/Monitoring/Unread.coffee +++ b/src/Monitoring/Unread.coffee @@ -125,9 +125,8 @@ Unread = break if bottom > height # post is not completely read return unless i - Unread.lastReadPost = Unread.posts[i - 1].ID + Unread.lastReadPost = Unread.posts.splice(0, i)[i - 1].ID Unread.saveLastReadPost() - Unread.posts.splice 0, i Unread.readArray Unread.postsQuotingYou Unread.update() if e From 39f30c1d0edbec684c03d199a4466f26ba7a84a2 Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Wed, 7 Aug 2013 19:55:25 +0200 Subject: [PATCH 134/172] Slightly simplify $.ajax. --- lib/$.coffee | 14 +++----------- src/General/Main.coffee | 2 +- src/Menu/DeleteLink.coffee | 2 +- src/Posting/QR.coffee | 8 ++++---- 4 files changed, 9 insertions(+), 17 deletions(-) diff --git a/lib/$.coffee b/lib/$.coffee index 59e08cbbd..306eba722 100644 --- a/lib/$.coffee +++ b/lib/$.coffee @@ -36,23 +36,15 @@ $.extend = (object, properties) -> for key, val of properties object[key] = val return -$.ajax = (url, callbacks, opts={}) -> - {type, cred, headers, upCallbacks, form, sync} = opts +$.ajax = (url, options, extra={}) -> + {type, headers, upCallbacks, form, sync} = extra r = new XMLHttpRequest() type or= form and 'post' or 'get' r.open type, url, !sync for key, val of headers r.setRequestHeader key, val - $.extend r, callbacks + $.extend r, options $.extend r.upload, upCallbacks - try - # Firefox throws an error if you try - # to set this on a synchronous XHR. - # Only cookies from the remote domain - # are used in a request withCredentials. - r.withCredentials = cred - catch err - # do nothing r.send form r $.cache = do -> diff --git a/src/General/Main.coffee b/src/General/Main.coffee index 0105d43f6..a1dd45ae4 100644 --- a/src/General/Main.coffee +++ b/src/General/Main.coffee @@ -345,7 +345,7 @@ Main = <% } %> ).join '\n' return unless errors - $.ajax '<%= meta.page %>errors', {}, + $.ajax '<%= meta.page %>errors', null, sync: true form: $.formData n: "<%= meta.name %> v#{g.VERSION}" diff --git a/src/Menu/DeleteLink.coffee b/src/Menu/DeleteLink.coffee index 7f71d7f6e..b7678e48a 100644 --- a/src/Menu/DeleteLink.coffee +++ b/src/Menu/DeleteLink.coffee @@ -55,10 +55,10 @@ DeleteLink = link = @ $.ajax $.id('delform').action.replace("/#{g.BOARD}/", "/#{post.board}/"), + withCredentials: true onload: -> DeleteLink.load link, post, fileOnly, @response onerror: -> DeleteLink.error link , - cred: true form: $.formData form load: (link, post, fileOnly, html) -> tmpDoc = d.implementation.createHTMLDocument '' diff --git a/src/Posting/QR.coffee b/src/Posting/QR.coffee index 791e7ded0..6a1d339d9 100644 --- a/src/Posting/QR.coffee +++ b/src/Posting/QR.coffee @@ -959,7 +959,8 @@ QR = recaptcha_challenge_field: challenge recaptcha_response_field: response - callbacks = + options = + withCredentials: true onload: QR.response onerror: -> # Connection error, or @@ -973,8 +974,7 @@ QR = Connection error. You may have been <a href=//www.4chan.org/banned target=_blank>banned</a>. [<a href="https://github.com/MayhemYDG/4chan-x/wiki/FAQ#what-does-connection-error-you-may-have-been-banned-mean" target=_blank>FAQ</a>] """ - opts = - cred: true + extra = form: $.formData postData upCallbacks: onload: -> @@ -988,7 +988,7 @@ QR = QR.req.progress = "#{Math.round e.loaded / e.total * 100}%" QR.status() - QR.req = $.ajax $.id('postForm').parentNode.action, callbacks, opts + QR.req = $.ajax $.id('postForm').parentNode.action, options, extra # Starting to upload might take some time. # Provide some feedback that we're starting to submit. QR.req.uploadStartTime = Date.now() From 773f2cfe0fadb90ff31b9b703bbccc529e19905b Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Wed, 7 Aug 2013 20:04:47 +0200 Subject: [PATCH 135/172] Use xhr.responseType = 'document'. --- src/Menu/DeleteLink.coffee | 11 +++++------ src/Posting/QR.coffee | 26 +++++++++++++++----------- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/src/Menu/DeleteLink.coffee b/src/Menu/DeleteLink.coffee index b7678e48a..1912733c6 100644 --- a/src/Menu/DeleteLink.coffee +++ b/src/Menu/DeleteLink.coffee @@ -55,21 +55,20 @@ DeleteLink = link = @ $.ajax $.id('delform').action.replace("/#{g.BOARD}/", "/#{post.board}/"), + responseType: 'document' withCredentials: true onload: -> DeleteLink.load link, post, fileOnly, @response onerror: -> DeleteLink.error link , form: $.formData form - load: (link, post, fileOnly, html) -> - tmpDoc = d.implementation.createHTMLDocument '' - tmpDoc.documentElement.innerHTML = html - if tmpDoc.title is '4chan - Banned' # Ban/warn check + load: (link, post, fileOnly, resDoc) -> + if resDoc.title is '4chan - Banned' # Ban/warn check s = 'Banned!' - else if msg = tmpDoc.getElementById 'errmsg' # error! + else if msg = resDoc.getElementById 'errmsg' # error! s = msg.textContent $.on link, 'click', DeleteLink.delete else - if tmpDoc.title is 'Updating index...' + if resDoc.title is 'Updating index...' # We're 100% sure. (post.origin or post).kill fileOnly s = 'Deleted' diff --git a/src/Posting/QR.coffee b/src/Posting/QR.coffee index 6a1d339d9..fc7b0357b 100644 --- a/src/Posting/QR.coffee +++ b/src/Posting/QR.coffee @@ -960,6 +960,7 @@ QR = recaptcha_response_field: response options = + responseType: 'document' withCredentials: true onload: QR.response onerror: -> @@ -1002,20 +1003,23 @@ QR = post = QR.posts[0] post.unlock() - tmpDoc = d.implementation.createHTMLDocument '' - tmpDoc.documentElement.innerHTML = req.response - if ban = $ '.banType', tmpDoc # banned/warning - board = $('.board', tmpDoc).innerHTML + resDoc = req.response + if ban = $ '.banType', resDoc # banned/warning + board = $('.board', resDoc).innerHTML err = $.el 'span', innerHTML: if ban.textContent.toLowerCase() is 'banned' - "You are banned on #{board}! ;_;<br>" + - "Click <a href=//www.4chan.org/banned target=_blank>here</a> to see the reason." + """ + You are banned on #{board}! ;_;<br> + Click <a href=//www.4chan.org/banned target=_blank>here</a> to see the reason. + """ else - "You were issued a warning on #{board} as #{$('.nameBlock', tmpDoc).innerHTML}.<br>" + - "Reason: #{$('.reason', tmpDoc).innerHTML}" - else if err = tmpDoc.getElementById 'errmsg' # error! + """ + You were issued a warning on #{board} as #{$('.nameBlock', resDoc).innerHTML}.<br> + Reason: #{$('.reason', resDoc).innerHTML} + """ + else if err = resDoc.getElementById 'errmsg' # error! $('a', err)?.target = '_blank' # duplicate image link - else if tmpDoc.title isnt 'Post successful!' + else if resDoc.title isnt 'Post successful!' err = 'Connection error with sys.4chan.org.' else if req.status isnt 200 err = "Error #{req.statusText} (#{req.status})" @@ -1049,7 +1053,7 @@ QR = QR.error err return - h1 = $ 'h1', tmpDoc + h1 = $ 'h1', resDoc QR.cleanNotifications() QR.notifications.push new Notification 'success', h1.textContent, 5 From bcbae6c84f7131e42bdcc6109621159254292d7a Mon Sep 17 00:00:00 2001 From: carboncopy <carboncopycopy@gmail.com> Date: Fri, 19 Jul 2013 23:18:11 -0500 Subject: [PATCH 136/172] display rolled dice while on /tg/ --- src/General/Config.coffee | 1 + src/General/Main.coffee | 1 + src/Miscellaneous/Dice.coffee | 20 ++++++++++++++++++++ 3 files changed, 22 insertions(+) create mode 100644 src/Miscellaneous/Dice.coffee diff --git a/src/General/Config.coffee b/src/General/Config.coffee index f7a1e24e4..2badf110b 100644 --- a/src/General/Config.coffee +++ b/src/General/Config.coffee @@ -13,6 +13,7 @@ Config = 'Index Navigation': [false, 'Add buttons to navigate between threads.'] 'Reply Navigation': [false, 'Add buttons to navigate to top / bottom of thread.'] 'Check for Updates': [true, 'Notify when updated versions of <%= meta.name %> are available.'] + 'Show Dice Rolled': [true, 'Show dice that were entered into the email field.'] 'Filtering': 'Anonymize': [false, 'Make everyone Anonymous.'] 'Filter': [true, 'Self-moderation placebo.'] diff --git a/src/General/Main.coffee b/src/General/Main.coffee index a1dd45ae4..044796280 100644 --- a/src/General/Main.coffee +++ b/src/General/Main.coffee @@ -114,6 +114,7 @@ Main = initFeature 'Thread Watcher', ThreadWatcher initFeature 'Index Navigation', Nav initFeature 'Keybinds', Keybinds + initFeature 'Dice Checker', Dice # c.timeEnd 'All initializations' $.on d, 'AddCallback', Main.addCallback diff --git a/src/Miscellaneous/Dice.coffee b/src/Miscellaneous/Dice.coffee new file mode 100644 index 000000000..1614a6ca9 --- /dev/null +++ b/src/Miscellaneous/Dice.coffee @@ -0,0 +1,20 @@ +Dice = + init: -> + return if g.VIEW is 'catalog' or !Conf['Show Dice Rolled'] + # or !Conf['Show dice being rolled'] + return if g.BOARD.ID isnt 'tg' + Post::callbacks.push + name: 'Dice roller' + cb: @node + node: -> + return if @isClone + email = @info.email + dicestats = /dice\W(\d+)d(\d+)/.exec(email) + return if not dicestats + roll = ($$("b",@nodes.comment).filter (elem)->elem.innerHTML.lastIndexOf("Rolled", 0) == 0)[0] + return if not roll + rollResults = roll.innerHTML.slice 7 + dicestr = dicestats[1]+"d"+dicestats[2] + roll.innerHTML = "Rolled " + dicestr + " and got " + rollResults + + From 5eabd9ce0d5f44a3ff6bba4778931a921c0fe8f2 Mon Sep 17 00:00:00 2001 From: carboncopy <carboncopycopy@gmail.com> Date: Wed, 7 Aug 2013 15:55:57 -0500 Subject: [PATCH 137/172] updated changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 793177903..04a47aa59 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- Added dice monitoring on /tg/ - Fix impossibility to create new threads when in dead threads. ### 3.5.7 - *2013-07-13* From ce6e317c98c7de912b614b4db877e237fa55f580 Mon Sep 17 00:00:00 2001 From: Zixaphir <zixaphirmoxphar@gmail.com> Date: Wed, 7 Aug 2013 20:55:23 -0700 Subject: [PATCH 138/172] Time to stop being silly. --- builds/4chan-X.user.js | 53 +++++++++-------------------- builds/crx/script.js | 53 +++++++++-------------------- src/General/lib/$.coffee | 18 ---------- src/Monitoring/Unread.coffee | 19 ++++++++--- src/Quotelinks/QuoteBacklink.coffee | 2 +- 5 files changed, 49 insertions(+), 96 deletions(-) diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js index 41c542a2f..373cd2533 100644 --- a/builds/4chan-X.user.js +++ b/builds/4chan-X.user.js @@ -336,15 +336,6 @@ return this.indexOf(string) > -1; }; - Array.prototype.add = function(object, position) { - var keep; - - keep = this.slice(position); - this.length = position; - this.push(object); - return this.pushArrays(keep); - }; - Array.prototype.contains = function(object) { return this.indexOf(object) > -1; }; @@ -361,27 +352,6 @@ return i; }; - Array.prototype.pushArrays = function() { - var arg, args, _i, _len; - - args = arguments; - for (_i = 0, _len = args.length; _i < _len; _i++) { - arg = args[_i]; - this.push.apply(this, arg); - } - return this; - }; - - Array.prototype.remove = function(object) { - var index; - - if ((index = this.indexOf(object)) > -1) { - return this.splice(index, 1); - } else { - return false; - } - }; - $ = function(selector, root) { if (root == null) { root = d.body; @@ -3683,7 +3653,7 @@ if (Conf['Quote Inlining']) { $.on(link, 'click', QuoteInline.toggle); if (Conf['Quote Hash Navigation']) { - frag.pushArrays(QuoteInline.qiQuote(link, $.hasClass(link, 'filtered'))); + frag.push.apply(frag, QuoteInline.qiQuote(link, $.hasClass(link, 'filtered'))); } } $.add(container, frag); @@ -7992,17 +7962,28 @@ height = doc.clientHeight; posts = Unread.posts; i = 0; - while (post = posts[i++]) { + while (post = posts[i]) { bottom = post.nodes.root.getBoundingClientRect().bottom; - if (bottom < height) { - ID = post.ID; - posts.remove(post); + if (bottom > height) { + i++; + continue; } + ID = post.ID; + if (Conf['Quote Threading']) { + posts.splice(i, 1); + continue; + } else { + posts.splice(0, i); + break; + } + i++; } if (!ID) { return; } - Unread.lastReadPost = ID; + if (Unread.lastReadPost < ID || !Unread.lastReadPost) { + Unread.lastReadPost = ID; + } Unread.saveLastReadPost(); Unread.readArray(Unread.postsQuotingYou); return Unread.update(); diff --git a/builds/crx/script.js b/builds/crx/script.js index e88002f09..304eb227a 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -317,15 +317,6 @@ return this.indexOf(string) > -1; }; - Array.prototype.add = function(object, position) { - var keep; - - keep = this.slice(position); - this.length = position; - this.push(object); - return this.pushArrays(keep); - }; - Array.prototype.contains = function(object) { return this.indexOf(object) > -1; }; @@ -342,27 +333,6 @@ return i; }; - Array.prototype.pushArrays = function() { - var arg, args, _i, _len; - - args = arguments; - for (_i = 0, _len = args.length; _i < _len; _i++) { - arg = args[_i]; - this.push.apply(this, arg); - } - return this; - }; - - Array.prototype.remove = function(object) { - var index; - - if ((index = this.indexOf(object)) > -1) { - return this.splice(index, 1); - } else { - return false; - } - }; - $ = function(selector, root) { if (root == null) { root = d.body; @@ -3688,7 +3658,7 @@ if (Conf['Quote Inlining']) { $.on(link, 'click', QuoteInline.toggle); if (Conf['Quote Hash Navigation']) { - frag.pushArrays(QuoteInline.qiQuote(link, $.hasClass(link, 'filtered'))); + frag.push.apply(frag, QuoteInline.qiQuote(link, $.hasClass(link, 'filtered'))); } } $.add(container, frag); @@ -7973,17 +7943,28 @@ height = doc.clientHeight; posts = Unread.posts; i = 0; - while (post = posts[i++]) { + while (post = posts[i]) { bottom = post.nodes.root.getBoundingClientRect().bottom; - if (bottom < height) { - ID = post.ID; - posts.remove(post); + if (bottom > height) { + i++; + continue; } + ID = post.ID; + if (Conf['Quote Threading']) { + posts.splice(i, 1); + continue; + } else { + posts.splice(0, i); + break; + } + i++; } if (!ID) { return; } - Unread.lastReadPost = ID; + if (Unread.lastReadPost < ID || !Unread.lastReadPost) { + Unread.lastReadPost = ID; + } Unread.saveLastReadPost(); Unread.readArray(Unread.postsQuotingYou); return Unread.update(); diff --git a/src/General/lib/$.coffee b/src/General/lib/$.coffee index 1569f960e..34a0e8af7 100644 --- a/src/General/lib/$.coffee +++ b/src/General/lib/$.coffee @@ -4,12 +4,6 @@ String::capitalize = -> String::contains = (string) -> @indexOf(string) > -1 -Array::add = (object, position) -> - keep = @slice position - @length = position - @push object - @pushArrays keep - Array::contains = (object) -> @indexOf(object) > -1 @@ -19,18 +13,6 @@ Array::indexOf = (object) -> return i if @[i] is object return i -Array::pushArrays = -> - args = arguments - for arg in args - @push.apply @, arg - return @ - -Array::remove = (object) -> - if (index = @indexOf object) > -1 - @splice index, 1 - else - false - # loosely follows the jquery api: # http://api.jquery.com/ # not chainable diff --git a/src/Monitoring/Unread.coffee b/src/Monitoring/Unread.coffee index 1979f3bbb..788b41753 100644 --- a/src/Monitoring/Unread.coffee +++ b/src/Monitoring/Unread.coffee @@ -121,14 +121,23 @@ Unread = {posts} = Unread i = 0 - while post = posts[i++] + while post = posts[i] {bottom} = post.nodes.root.getBoundingClientRect() - if (bottom < height) # post is completely read - {ID} = post - posts.remove post + if bottom > height # post isnt completely read + i++ + continue + + {ID} = post + if Conf['Quote Threading'] + posts.splice i, 1 + continue + else + posts.splice 0, i + break + i++ return unless ID - Unread.lastReadPost = ID + Unread.lastReadPost = ID if Unread.lastReadPost < ID or !Unread.lastReadPost Unread.saveLastReadPost() Unread.readArray Unread.postsQuotingYou Unread.update() diff --git a/src/Quotelinks/QuoteBacklink.coffee b/src/Quotelinks/QuoteBacklink.coffee index 91bbb6e84..ae4a4b433 100644 --- a/src/Quotelinks/QuoteBacklink.coffee +++ b/src/Quotelinks/QuoteBacklink.coffee @@ -41,7 +41,7 @@ QuoteBacklink = $.on link, 'mouseover', QuotePreview.mouseover if Conf['Quote Inlining'] $.on link, 'click', QuoteInline.toggle - frag.pushArrays QuoteInline.qiQuote link, $.hasClass link, 'filtered' if Conf['Quote Hash Navigation'] + frag.push.apply frag, QuoteInline.qiQuote link, $.hasClass link, 'filtered' if Conf['Quote Hash Navigation'] $.add container, frag return secondNode: -> From 8c95fe1995899b7dd6c63502113149b908143cc9 Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Thu, 8 Aug 2013 11:25:03 +0200 Subject: [PATCH 139/172] Tweak Dice code. #1218 --- CHANGELOG.md | 3 ++- src/General/Config.coffee | 2 +- src/General/Main.coffee | 2 +- src/Miscellaneous/Dice.coffee | 28 ++++++++++++---------------- 4 files changed, 16 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 04a47aa59..353dce5e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ -- Added dice monitoring on /tg/ +- **New feature**: `Show Dice Roll` + - Shows dice that were entered into the email field on /tg/. - Fix impossibility to create new threads when in dead threads. ### 3.5.7 - *2013-07-13* diff --git a/src/General/Config.coffee b/src/General/Config.coffee index 2badf110b..7cbc64d18 100644 --- a/src/General/Config.coffee +++ b/src/General/Config.coffee @@ -12,8 +12,8 @@ Config = 'Thread Expansion': [true, 'Add buttons to expand threads.'] 'Index Navigation': [false, 'Add buttons to navigate between threads.'] 'Reply Navigation': [false, 'Add buttons to navigate to top / bottom of thread.'] + 'Show Dice Roll': [true, 'Show dice that were entered into the email field.'] 'Check for Updates': [true, 'Notify when updated versions of <%= meta.name %> are available.'] - 'Show Dice Rolled': [true, 'Show dice that were entered into the email field.'] 'Filtering': 'Anonymize': [false, 'Make everyone Anonymous.'] 'Filter': [true, 'Self-moderation placebo.'] diff --git a/src/General/Main.coffee b/src/General/Main.coffee index 044796280..92a543676 100644 --- a/src/General/Main.coffee +++ b/src/General/Main.coffee @@ -114,7 +114,7 @@ Main = initFeature 'Thread Watcher', ThreadWatcher initFeature 'Index Navigation', Nav initFeature 'Keybinds', Keybinds - initFeature 'Dice Checker', Dice + initFeature 'Show Dice Roll', Dice # c.timeEnd 'All initializations' $.on d, 'AddCallback', Main.addCallback diff --git a/src/Miscellaneous/Dice.coffee b/src/Miscellaneous/Dice.coffee index 1614a6ca9..67559eca4 100644 --- a/src/Miscellaneous/Dice.coffee +++ b/src/Miscellaneous/Dice.coffee @@ -1,20 +1,16 @@ Dice = init: -> - return if g.VIEW is 'catalog' or !Conf['Show Dice Rolled'] - # or !Conf['Show dice being rolled'] - return if g.BOARD.ID isnt 'tg' + return if g.BOARD.ID isnt 'tg' or g.VIEW is 'catalog' or !Conf['Show Dice Roll'] Post::callbacks.push - name: 'Dice roller' - cb: @node + name: 'Show Dice Roll' + cb: @node node: -> - return if @isClone - email = @info.email - dicestats = /dice\W(\d+)d(\d+)/.exec(email) - return if not dicestats - roll = ($$("b",@nodes.comment).filter (elem)->elem.innerHTML.lastIndexOf("Rolled", 0) == 0)[0] - return if not roll - rollResults = roll.innerHTML.slice 7 - dicestr = dicestats[1]+"d"+dicestats[2] - roll.innerHTML = "Rolled " + dicestr + " and got " + rollResults - - + return if @isClone or not dicestats = @info.email?.match /dice\+(\d+)d(\d+)/ + # Use the text node directly, as the <b> has two <br>. + roll = $('b', @nodes.comment).firstChild + # Stop here if it looks like this: + # Rolled 44 + # and not this: + # Rolled 9, 8, 10, 3, 5 = 35 + return unless rollResults = roll.textContent.match /^Rolled (\d+)$/ + roll.textContent = "Rolled #{dicestats[1]}d#{dicestats[2]} and got #{rollResults[1]}" From 2cbf3d909d251755924f43dcea4a44a580fc9f26 Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Thu, 8 Aug 2013 17:24:46 +0200 Subject: [PATCH 140/172] Tweak dice code further. #1218 Also use `data` instead of `textContent` as it performs faster. --- src/Miscellaneous/Dice.coffee | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/Miscellaneous/Dice.coffee b/src/Miscellaneous/Dice.coffee index 67559eca4..93bdb06cd 100644 --- a/src/Miscellaneous/Dice.coffee +++ b/src/Miscellaneous/Dice.coffee @@ -5,12 +5,7 @@ Dice = name: 'Show Dice Roll' cb: @node node: -> - return if @isClone or not dicestats = @info.email?.match /dice\+(\d+)d(\d+)/ + return if @isClone or not dicestats = @info.email?.match /dice[+\s](\d+)d(\d+)/ # Use the text node directly, as the <b> has two <br>. roll = $('b', @nodes.comment).firstChild - # Stop here if it looks like this: - # Rolled 44 - # and not this: - # Rolled 9, 8, 10, 3, 5 = 35 - return unless rollResults = roll.textContent.match /^Rolled (\d+)$/ - roll.textContent = "Rolled #{dicestats[1]}d#{dicestats[2]} and got #{rollResults[1]}" + roll.data = "Rolled #{dicestats[1]}d#{dicestats[2]} and got #{roll.data.slice 7}" From ee4ece2574edd7b01d244f34653f32665978a0e5 Mon Sep 17 00:00:00 2001 From: Zixaphir <zixaphirmoxphar@gmail.com> Date: Thu, 8 Aug 2013 14:18:44 -0700 Subject: [PATCH 141/172] Fix a few features identifying themselves incorrectly A few refactors to ColorUserIDs --- LICENSE | 2 +- builds/4chan-X.user.js | 23 +++--- builds/crx/script.js | 23 +++--- src/General/Main.coffee | 110 +++++++++++++------------- src/Miscellaneous/ColorUserIDs.coffee | 12 ++- 5 files changed, 79 insertions(+), 91 deletions(-) diff --git a/LICENSE b/LICENSE index c83c43ea3..8986054f8 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,5 @@ /* -* 4chan X - Version 1.2.25 - 2013-08-07 +* 4chan X - Version 1.2.25 - 2013-08-08 * * Licensed under the MIT license. * https://github.com/seaweedchan/4chan-x/blob/master/LICENSE diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js index 373cd2533..1d6dcd0c3 100644 --- a/builds/4chan-X.user.js +++ b/builds/4chan-X.user.js @@ -19,7 +19,7 @@ // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwAgMAAAAqbBEUAAAACVBMVEUAAGcAAABmzDNZt9VtAAAAAXRSTlMAQObYZgAAAHFJREFUKFOt0LENACEIBdBv4Qju4wgWanEj3D6OcIVMKaitYHEU/jwTCQj8W75kiVCSBvdQ5/AvfVHBin11BgdRq3ysBgfwBDRrj3MCIA+oAQaku/Q1cNctrAmyDl577tOThYt/Y1RBM4DgOHzM0HFTAyLukH/cmRnqAAAAAElFTkSuQmCC // ==/UserScript== /* -* 4chan X - Version 1.2.25 - 2013-08-07 +* 4chan X - Version 1.2.25 - 2013-08-08 * * Licensed under the MIT license. * https://github.com/seaweedchan/4chan-x/blob/master/LICENSE @@ -8355,20 +8355,18 @@ return; } return Post.prototype.callbacks.push({ - name: 'Reveal Spoilers', + name: 'Color User IDs', cb: this.node }); }, node: function(post) { - var str, uid; + var str, _ref; - if (!(uid = $('.hand', this.nodes.uniqueID))) { + if (((_ref = $('.hand', this.nodes.uniqueID)) != null ? _ref.nodeName : void 0) !== 'SPAN') { return; } str = this.info.uniqueID; - if (uid.nodeName === 'SPAN') { - return uid.style.cssText = IDColor.apply.call(str); - } + return uid.style.cssText = IDColor.css(IDColor.ids[str] || IDColor.compute(str)); }, ids: {}, compute: function(str) { @@ -8380,11 +8378,8 @@ this.ids[str] = rgb; return rgb; }, - apply: function() { - var rgb; - - rgb = IDColor.ids[this] || IDColor.compute(this); - return ("background-color: rgb(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + "); color: ") + (rgb[3] ? "black; border-radius: 3px; padding: 0px 2px;" : "white; border-radius: 3px; padding: 0px 2px;"); + css: function(rgb) { + return "background-color: rgb(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + "); color: " + (rgb[3] ? "black;" : "white;") + " border-radius: 3px; padding: 0px 2px;"; }, hash: function(str) { var i, j, msg; @@ -10347,7 +10342,7 @@ 'Fourchan thingies': Fourchan, 'Emoji': Emoji, 'Color User IDs': IDColor, - 'Remove Spoilers': RemoveSpoilers, + 'Reveal Spoilers': RemoveSpoilers, 'Custom CSS': CustomCSS, 'Linkify': Linkify, 'Resurrect Quotes': Quotify, @@ -10379,7 +10374,7 @@ 'Sauce': Sauce, 'Image Expansion': ImageExpand, 'Image Expansion (Menu)': ImageExpand.menu, - 'Reveal Spoilers': RevealSpoilers, + 'Reveal Spoiler Thumbnails': RevealSpoilers, 'Image Loading': ImageLoader, 'Image Hover': ImageHover, 'Comment Expansion': ExpandComment, diff --git a/builds/crx/script.js b/builds/crx/script.js index 304eb227a..016dac3df 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -1,6 +1,6 @@ // Generated by CoffeeScript /* -* 4chan X - Version 1.2.25 - 2013-08-07 +* 4chan X - Version 1.2.25 - 2013-08-08 * * Licensed under the MIT license. * https://github.com/seaweedchan/4chan-x/blob/master/LICENSE @@ -8341,20 +8341,18 @@ return; } return Post.prototype.callbacks.push({ - name: 'Reveal Spoilers', + name: 'Color User IDs', cb: this.node }); }, node: function(post) { - var str, uid; + var str, _ref; - if (!(uid = $('.hand', this.nodes.uniqueID))) { + if (((_ref = $('.hand', this.nodes.uniqueID)) != null ? _ref.nodeName : void 0) !== 'SPAN') { return; } str = this.info.uniqueID; - if (uid.nodeName === 'SPAN') { - return uid.style.cssText = IDColor.apply.call(str); - } + return uid.style.cssText = IDColor.css(IDColor.ids[str] || IDColor.compute(str)); }, ids: {}, compute: function(str) { @@ -8366,11 +8364,8 @@ this.ids[str] = rgb; return rgb; }, - apply: function() { - var rgb; - - rgb = IDColor.ids[this] || IDColor.compute(this); - return ("background-color: rgb(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + "); color: ") + (rgb[3] ? "black; border-radius: 3px; padding: 0px 2px;" : "white; border-radius: 3px; padding: 0px 2px;"); + css: function(rgb) { + return "background-color: rgb(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + "); color: " + (rgb[3] ? "black;" : "white;") + " border-radius: 3px; padding: 0px 2px;"; }, hash: function(str) { var i, j, msg; @@ -10331,7 +10326,7 @@ 'Fourchan thingies': Fourchan, 'Emoji': Emoji, 'Color User IDs': IDColor, - 'Remove Spoilers': RemoveSpoilers, + 'Reveal Spoilers': RemoveSpoilers, 'Custom CSS': CustomCSS, 'Linkify': Linkify, 'Resurrect Quotes': Quotify, @@ -10363,7 +10358,7 @@ 'Sauce': Sauce, 'Image Expansion': ImageExpand, 'Image Expansion (Menu)': ImageExpand.menu, - 'Reveal Spoilers': RevealSpoilers, + 'Reveal Spoiler Thumbnails': RevealSpoilers, 'Image Loading': ImageLoader, 'Image Hover': ImageHover, 'Comment Expansion': ExpandComment, diff --git a/src/General/Main.coffee b/src/General/Main.coffee index 41b2679ce..aeedede7d 100644 --- a/src/General/Main.coffee +++ b/src/General/Main.coffee @@ -73,61 +73,61 @@ Main = # c.time 'All initializations' init - 'Polyfill': Polyfill - 'Redirect': Redirect - 'Header': Header - 'Catalog Links': CatalogLinks - 'Settings': Settings - 'Announcement Hiding': PSAHiding - 'Fourchan thingies': Fourchan - 'Emoji': Emoji - 'Color User IDs': IDColor - 'Remove Spoilers': RemoveSpoilers - 'Custom CSS': CustomCSS - 'Linkify': Linkify - 'Resurrect Quotes': Quotify - 'Filter': Filter - 'Thread Hiding Buttons': ThreadHiding - 'Reply Hiding Buttons': PostHiding - 'Recursive': Recursive - 'Strike-through Quotes': QuoteStrikeThrough - 'Quick Reply': QR - 'Menu': Menu - 'Report Link': ReportLink - 'Thread Hiding (Menu)': ThreadHiding.menu - 'Reply Hiding (Menu)': PostHiding.menu - 'Delete Link': DeleteLink - 'Filter (Menu)': Filter.menu - 'Download Link': DownloadLink - 'Archive Link': ArchiveLink - 'Quote Inlining': QuoteInline - 'Quote Previewing': QuotePreview - 'Quote Backlinks': QuoteBacklink - 'Mark Quotes of You': QuoteYou - 'Mark OP Quotes': QuoteOP - 'Mark Cross-thread Quotes': QuoteCT - 'Anonymize': Anonymize - 'Time Formatting': Time - 'Relative Post Dates': RelativeDates - 'File Info Formatting': FileInfo - 'Fappe Tyme': FappeTyme - 'Sauce': Sauce - 'Image Expansion': ImageExpand - 'Image Expansion (Menu)': ImageExpand.menu - 'Reveal Spoilers': RevealSpoilers - 'Image Loading': ImageLoader - 'Image Hover': ImageHover - 'Comment Expansion': ExpandComment - 'Thread Expansion': ExpandThread - 'Thread Excerpt': ThreadExcerpt - 'Favicon': Favicon - 'Unread': Unread - 'Quote Threading': QuoteThreading - 'Thread Stats': ThreadStats - 'Thread Updater': ThreadUpdater - 'Thread Watcher': ThreadWatcher - 'Index Navigation': Nav - 'Keybinds': Keybinds + 'Polyfill': Polyfill + 'Redirect': Redirect + 'Header': Header + 'Catalog Links': CatalogLinks + 'Settings': Settings + 'Announcement Hiding': PSAHiding + 'Fourchan thingies': Fourchan + 'Emoji': Emoji + 'Color User IDs': IDColor + 'Reveal Spoilers': RemoveSpoilers + 'Custom CSS': CustomCSS + 'Linkify': Linkify + 'Resurrect Quotes': Quotify + 'Filter': Filter + 'Thread Hiding Buttons': ThreadHiding + 'Reply Hiding Buttons': PostHiding + 'Recursive': Recursive + 'Strike-through Quotes': QuoteStrikeThrough + 'Quick Reply': QR + 'Menu': Menu + 'Report Link': ReportLink + 'Thread Hiding (Menu)': ThreadHiding.menu + 'Reply Hiding (Menu)': PostHiding.menu + 'Delete Link': DeleteLink + 'Filter (Menu)': Filter.menu + 'Download Link': DownloadLink + 'Archive Link': ArchiveLink + 'Quote Inlining': QuoteInline + 'Quote Previewing': QuotePreview + 'Quote Backlinks': QuoteBacklink + 'Mark Quotes of You': QuoteYou + 'Mark OP Quotes': QuoteOP + 'Mark Cross-thread Quotes': QuoteCT + 'Anonymize': Anonymize + 'Time Formatting': Time + 'Relative Post Dates': RelativeDates + 'File Info Formatting': FileInfo + 'Fappe Tyme': FappeTyme + 'Sauce': Sauce + 'Image Expansion': ImageExpand + 'Image Expansion (Menu)': ImageExpand.menu + 'Reveal Spoiler Thumbnails': RevealSpoilers + 'Image Loading': ImageLoader + 'Image Hover': ImageHover + 'Comment Expansion': ExpandComment + 'Thread Expansion': ExpandThread + 'Thread Excerpt': ThreadExcerpt + 'Favicon': Favicon + 'Unread': Unread + 'Quote Threading': QuoteThreading + 'Thread Stats': ThreadStats + 'Thread Updater': ThreadUpdater + 'Thread Watcher': ThreadWatcher + 'Index Navigation': Nav + 'Keybinds': Keybinds # c.timeEnd 'All initializations' diff --git a/src/Miscellaneous/ColorUserIDs.coffee b/src/Miscellaneous/ColorUserIDs.coffee index ba7647686..b4b72e7dc 100644 --- a/src/Miscellaneous/ColorUserIDs.coffee +++ b/src/Miscellaneous/ColorUserIDs.coffee @@ -3,14 +3,13 @@ IDColor = return unless Conf['Color User IDs'] Post::callbacks.push - name: 'Reveal Spoilers' + name: 'Color User IDs' cb: @node node: (post) -> - return unless uid = $ '.hand', @nodes.uniqueID + return unless $('.hand', @nodes.uniqueID)?.nodeName is 'SPAN' str = @info.uniqueID - if uid.nodeName is 'SPAN' - uid.style.cssText = IDColor.apply.call str + uid.style.cssText = IDColor.css IDColor.ids[str] or IDColor.compute str ids: {} @@ -22,14 +21,13 @@ IDColor = (hash >> 16) & 0xFF (hash >> 8) & 0xFF ] + rgb[3] = ((rgb[0] * 0.299) + (rgb[1] * 0.587) + (rgb[2] * 0.114)) > 125 @ids[str] = rgb rgb - apply: -> - rgb = IDColor.ids[@] or IDColor.compute @ - "background-color: rgb(#{rgb[0]},#{rgb[1]},#{rgb[2]}); color: " + if rgb[3] then "black; border-radius: 3px; padding: 0px 2px;" else "white; border-radius: 3px; padding: 0px 2px;" + css: (rgb) -> "background-color: rgb(#{rgb[0]},#{rgb[1]},#{rgb[2]}); color: #{if rgb[3] then "black;" else "white;"} border-radius: 3px; padding: 0px 2px;" hash: (str) -> msg = 0 From 254fa9959b4229fdb5f51344be89eb727de10cd6 Mon Sep 17 00:00:00 2001 From: Zixaphir <zixaphirmoxphar@gmail.com> Date: Thu, 8 Aug 2013 14:27:19 -0700 Subject: [PATCH 142/172] And a couple more fixes #136 --- builds/4chan-X.user.js | 9 +++++---- builds/crx/script.js | 9 +++++---- src/Miscellaneous/ColorUserIDs.coffee | 5 +++-- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js index 1d6dcd0c3..b797c7a69 100644 --- a/builds/4chan-X.user.js +++ b/builds/4chan-X.user.js @@ -8359,13 +8359,14 @@ cb: this.node }); }, - node: function(post) { - var str, _ref; + node: function() { + var str, uid; - if (((_ref = $('.hand', this.nodes.uniqueID)) != null ? _ref.nodeName : void 0) !== 'SPAN') { + str = this.info.uniqueID; + uid = $('.hand', this.nodes.uniqueID); + if (!(str && uid && uid.nodeName === 'SPAN')) { return; } - str = this.info.uniqueID; return uid.style.cssText = IDColor.css(IDColor.ids[str] || IDColor.compute(str)); }, ids: {}, diff --git a/builds/crx/script.js b/builds/crx/script.js index 016dac3df..fdae8541e 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -8345,13 +8345,14 @@ cb: this.node }); }, - node: function(post) { - var str, _ref; + node: function() { + var str, uid; - if (((_ref = $('.hand', this.nodes.uniqueID)) != null ? _ref.nodeName : void 0) !== 'SPAN') { + str = this.info.uniqueID; + uid = $('.hand', this.nodes.uniqueID); + if (!(str && uid && uid.nodeName === 'SPAN')) { return; } - str = this.info.uniqueID; return uid.style.cssText = IDColor.css(IDColor.ids[str] || IDColor.compute(str)); }, ids: {}, diff --git a/src/Miscellaneous/ColorUserIDs.coffee b/src/Miscellaneous/ColorUserIDs.coffee index b4b72e7dc..a0b442a17 100644 --- a/src/Miscellaneous/ColorUserIDs.coffee +++ b/src/Miscellaneous/ColorUserIDs.coffee @@ -6,9 +6,10 @@ IDColor = name: 'Color User IDs' cb: @node - node: (post) -> - return unless $('.hand', @nodes.uniqueID)?.nodeName is 'SPAN' + node: -> str = @info.uniqueID + uid = $ '.hand', @nodes.uniqueID + return unless str and uid and uid.nodeName is 'SPAN' uid.style.cssText = IDColor.css IDColor.ids[str] or IDColor.compute str ids: {} From bd1dc8cb532587347421679ebda194a4a9981881 Mon Sep 17 00:00:00 2001 From: Zixaphir <zixaphirmoxphar@gmail.com> Date: Thu, 8 Aug 2013 14:35:26 -0700 Subject: [PATCH 143/172] Fix #298 --- builds/4chan-X.user.js | 6 +++++- builds/crx/script.js | 6 +++++- src/General/Header.coffee | 5 ++++- src/Miscellaneous/CatalogLinks.coffee | 2 +- 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js index b797c7a69..758523ea7 100644 --- a/builds/4chan-X.user.js +++ b/builds/4chan-X.user.js @@ -1616,7 +1616,11 @@ a.dataset.only = m[1]; a.href = "//boards.4chan.org/" + board + "/"; if (m[1] === 'catalog') { - a.href += 'catalog'; + if (Conf['External Catalog']) { + a.href = CatalogLinks.external(board); + } else { + a.href += 'catalog'; + } $.addClass(a, 'catalog'); } } diff --git a/builds/crx/script.js b/builds/crx/script.js index fdae8541e..2c5f99ebd 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -1628,7 +1628,11 @@ a.dataset.only = m[1]; a.href = "//boards.4chan.org/" + board + "/"; if (m[1] === 'catalog') { - a.href += 'catalog'; + if (Conf['External Catalog']) { + a.href = CatalogLinks.external(board); + } else { + a.href += 'catalog'; + } $.addClass(a, 'catalog'); } } diff --git a/src/General/Header.coffee b/src/General/Header.coffee index c1cea716a..d08b8cd99 100644 --- a/src/General/Header.coffee +++ b/src/General/Header.coffee @@ -169,7 +169,10 @@ Header = a.dataset.only = m[1] a.href = "//boards.4chan.org/#{board}/" if m[1] is 'catalog' - a.href += 'catalog' + if Conf['External Catalog'] + a.href = CatalogLinks.external board + else + a.href += 'catalog' $.addClass a, 'catalog' $.addClass a, 'navSmall' if board is '@' diff --git a/src/Miscellaneous/CatalogLinks.coffee b/src/Miscellaneous/CatalogLinks.coffee index b92983ce5..cb1e84535 100644 --- a/src/Miscellaneous/CatalogLinks.coffee +++ b/src/Miscellaneous/CatalogLinks.coffee @@ -35,7 +35,7 @@ CatalogLinks = continue if ['f', 'status', '4chan'].contains(board) or !board if Conf['External Catalog'] a.href = if useCatalog - CatalogLinks.external(board) + CatalogLinks.external board else "//boards.4chan.org/#{board}/" else From 73250fcc3d9e3571a8748a357318dddc971b5d30 Mon Sep 17 00:00:00 2001 From: Zixaphir <zixaphirmoxphar@gmail.com> Date: Thu, 8 Aug 2013 14:49:31 -0700 Subject: [PATCH 144/172] Fix #378 --- builds/4chan-X.user.js | 6 +++--- builds/crx/script.js | 6 +++--- src/Quotelinks/QuoteInline.coffee | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js index 758523ea7..f1fcb88a2 100644 --- a/builds/4chan-X.user.js +++ b/builds/4chan-X.user.js @@ -3733,9 +3733,6 @@ if (g.VIEW === 'catalog' || !Conf['Quote Inlining']) { return; } - if (Conf['Comment Expansion']) { - ExpandComment.callbacks.push(this.node); - } if (Conf['Quote Hash Navigation']) { this.node = function() { var link, _i, _len, _ref; @@ -3760,6 +3757,9 @@ } }; } + if (Conf['Comment Expansion']) { + ExpandComment.callbacks.push(this.node); + } return Post.prototype.callbacks.push({ name: 'Quote Inlining', cb: this.node diff --git a/builds/crx/script.js b/builds/crx/script.js index 2c5f99ebd..2785e07e9 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -3738,9 +3738,6 @@ if (g.VIEW === 'catalog' || !Conf['Quote Inlining']) { return; } - if (Conf['Comment Expansion']) { - ExpandComment.callbacks.push(this.node); - } if (Conf['Quote Hash Navigation']) { this.node = function() { var link, _i, _len, _ref; @@ -3765,6 +3762,9 @@ } }; } + if (Conf['Comment Expansion']) { + ExpandComment.callbacks.push(this.node); + } return Post.prototype.callbacks.push({ name: 'Quote Inlining', cb: this.node diff --git a/src/Quotelinks/QuoteInline.coffee b/src/Quotelinks/QuoteInline.coffee index 723f0c53b..95a225e38 100644 --- a/src/Quotelinks/QuoteInline.coffee +++ b/src/Quotelinks/QuoteInline.coffee @@ -2,9 +2,6 @@ QuoteInline = init: -> return if g.VIEW is 'catalog' or !Conf['Quote Inlining'] - if Conf['Comment Expansion'] - ExpandComment.callbacks.push @node - if Conf['Quote Hash Navigation'] @node = -> for link in @nodes.quotelinks.concat [@nodes.backlinks...] @@ -18,6 +15,9 @@ QuoteInline = $.on link, 'click', QuoteInline.toggle return + if Conf['Comment Expansion'] + ExpandComment.callbacks.push @node + Post::callbacks.push name: 'Quote Inlining' cb: @node From bcc68168b0515f48734de9fcdb7883b06d5947bb Mon Sep 17 00:00:00 2001 From: Zixaphir <zixaphirmoxphar@gmail.com> Date: Thu, 8 Aug 2013 16:32:59 -0700 Subject: [PATCH 145/172] Fix Unread.read --- builds/4chan-X.user.js | 24 ++++++++++++++---------- builds/crx/script.js | 24 ++++++++++++++---------- src/Monitoring/Unread.coffee | 22 ++++++++++++---------- 3 files changed, 40 insertions(+), 30 deletions(-) diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js index f1fcb88a2..a0525625e 100644 --- a/builds/4chan-X.user.js +++ b/builds/4chan-X.user.js @@ -7968,20 +7968,24 @@ i = 0; while (post = posts[i]) { bottom = post.nodes.root.getBoundingClientRect().bottom; - if (bottom > height) { - i++; - continue; - } - ID = post.ID; - if (Conf['Quote Threading']) { - posts.splice(i, 1); - continue; + if (bottom < height) { + ID = post.ID; + if (Conf['Quote Threading']) { + posts.splice(i, 1); + continue; + } } else { - posts.splice(0, i); - break; + if (!Conf['Quote Threading']) { + break; + } } i++; } + if (!Conf['Quote Threading']) { + if (i) { + posts.splice(0, i); + } + } if (!ID) { return; } diff --git a/builds/crx/script.js b/builds/crx/script.js index 2785e07e9..040be83ad 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -7949,20 +7949,24 @@ i = 0; while (post = posts[i]) { bottom = post.nodes.root.getBoundingClientRect().bottom; - if (bottom > height) { - i++; - continue; - } - ID = post.ID; - if (Conf['Quote Threading']) { - posts.splice(i, 1); - continue; + if (bottom < height) { + ID = post.ID; + if (Conf['Quote Threading']) { + posts.splice(i, 1); + continue; + } } else { - posts.splice(0, i); - break; + if (!Conf['Quote Threading']) { + break; + } } i++; } + if (!Conf['Quote Threading']) { + if (i) { + posts.splice(0, i); + } + } if (!ID) { return; } diff --git a/src/Monitoring/Unread.coffee b/src/Monitoring/Unread.coffee index 788b41753..4317502b5 100644 --- a/src/Monitoring/Unread.coffee +++ b/src/Monitoring/Unread.coffee @@ -123,18 +123,20 @@ Unread = while post = posts[i] {bottom} = post.nodes.root.getBoundingClientRect() - if bottom > height # post isnt completely read - i++ - continue - - {ID} = post - if Conf['Quote Threading'] - posts.splice i, 1 - continue + if bottom < height # post is completely read + {ID} = post + if Conf['Quote Threading'] + posts.splice i, 1 + continue else - posts.splice 0, i - break + unless Conf['Quote Threading'] + break i++ + + unless Conf['Quote Threading'] + if i + posts.splice 0, i + return unless ID Unread.lastReadPost = ID if Unread.lastReadPost < ID or !Unread.lastReadPost From 342970872562d9eccf3e55693be7d82d0a3edcb7 Mon Sep 17 00:00:00 2001 From: Zixaphir <zixaphirmoxphar@gmail.com> Date: Thu, 8 Aug 2013 19:09:54 -0700 Subject: [PATCH 146/172] Undo some changes to Unread.read --- builds/4chan-X.user.js | 9 ++++++--- builds/crx/script.js | 9 ++++++--- src/Monitoring/Unread.coffee | 5 +++-- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js index a0525625e..938550122 100644 --- a/builds/4chan-X.user.js +++ b/builds/4chan-X.user.js @@ -7907,7 +7907,8 @@ if (Conf['Unread Line']) { Unread.setLine(posts.contains(Unread.posts[0])); } - return Unread.read(); + Unread.read(); + return Unread.update(); }, addPostQuotingYou: function(post) { var quotelink, _i, _len, _ref; @@ -7957,7 +7958,7 @@ } return arr.splice(0, i); }, - read: $.debounce(50, function() { + read: $.debounce(50, function(e) { var ID, bottom, height, i, post, posts; if (d.hidden || !Unread.posts.length) { @@ -7994,7 +7995,9 @@ } Unread.saveLastReadPost(); Unread.readArray(Unread.postsQuotingYou); - return Unread.update(); + if (e) { + return Unread.update(); + } }), saveLastReadPost: $.debounce(2 * $.SECOND, function() { if (Unread.thread.isDead) { diff --git a/builds/crx/script.js b/builds/crx/script.js index 040be83ad..d0bb4551b 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -7888,7 +7888,8 @@ if (Conf['Unread Line']) { Unread.setLine(posts.contains(Unread.posts[0])); } - return Unread.read(); + Unread.read(); + return Unread.update(); }, addPostQuotingYou: function(post) { var quotelink, _i, _len, _ref; @@ -7938,7 +7939,7 @@ } return arr.splice(0, i); }, - read: $.debounce(50, function() { + read: $.debounce(50, function(e) { var ID, bottom, height, i, post, posts; if (d.hidden || !Unread.posts.length) { @@ -7975,7 +7976,9 @@ } Unread.saveLastReadPost(); Unread.readArray(Unread.postsQuotingYou); - return Unread.update(); + if (e) { + return Unread.update(); + } }), saveLastReadPost: $.debounce(2 * $.SECOND, function() { if (Unread.thread.isDead) { diff --git a/src/Monitoring/Unread.coffee b/src/Monitoring/Unread.coffee index 4317502b5..4ee9e6f90 100644 --- a/src/Monitoring/Unread.coffee +++ b/src/Monitoring/Unread.coffee @@ -86,6 +86,7 @@ Unread = # Force line on visible threads if there were no unread posts previously. Unread.setLine posts.contains Unread.posts[0] Unread.read() + Unread.update() addPostQuotingYou: (post) -> return unless QR.db @@ -115,7 +116,7 @@ Unread = break if post.ID > Unread.lastReadPost arr.splice 0, i - read: $.debounce 50, -> + read: $.debounce 50, (e) -> return if d.hidden or !Unread.posts.length height = doc.clientHeight {posts} = Unread @@ -142,7 +143,7 @@ Unread = Unread.lastReadPost = ID if Unread.lastReadPost < ID or !Unread.lastReadPost Unread.saveLastReadPost() Unread.readArray Unread.postsQuotingYou - Unread.update() + Unread.update() if e saveLastReadPost: $.debounce 2 * $.SECOND, -> return if Unread.thread.isDead From 191dc4911ff3fa799c68edf6b13001e735ce171f Mon Sep 17 00:00:00 2001 From: Zixaphir <zixaphirmoxphar@gmail.com> Date: Thu, 8 Aug 2013 20:25:36 -0700 Subject: [PATCH 147/172] Initial attempt at implementing 404 redirection when 4chan is down http://github.com/zixaphir/appchan-x/issues/394 --- builds/4chan-X.user.js | 2 +- builds/crx/script.js | 2 +- src/General/Main.coffee | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js index 938550122..f3cf1cc86 100644 --- a/builds/4chan-X.user.js +++ b/builds/4chan-X.user.js @@ -10314,7 +10314,7 @@ $.ready(function() { var URL; - if (Conf['404 Redirect'] && d.title === '4chan - 404 Not Found') { + if (Conf['404 Redirect'] && ['4chan - Temporarily Offline', '4chan - 404 Not Found'].contains(d.title)) { Redirect.init(); pathname = location.pathname.split('/'); URL = Redirect.to('file', { diff --git a/builds/crx/script.js b/builds/crx/script.js index d0bb4551b..f5a43b9e2 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -10298,7 +10298,7 @@ $.ready(function() { var URL; - if (Conf['404 Redirect'] && d.title === '4chan - 404 Not Found') { + if (Conf['404 Redirect'] && ['4chan - Temporarily Offline', '4chan - 404 Not Found'].contains(d.title)) { Redirect.init(); pathname = location.pathname.split('/'); URL = Redirect.to('file', { diff --git a/src/General/Main.coffee b/src/General/Main.coffee index aeedede7d..e4deba2bc 100644 --- a/src/General/Main.coffee +++ b/src/General/Main.coffee @@ -48,7 +48,7 @@ Main = return when 'images.4chan.org' $.ready -> - if Conf['404 Redirect'] and d.title is '4chan - 404 Not Found' + if Conf['404 Redirect'] and ['4chan - Temporarily Offline', '4chan - 404 Not Found'].contains d.title Redirect.init() pathname = location.pathname.split '/' URL = Redirect.to 'file', From 6ba8d9ae8cabad8fd49914b1756bc5c7818a1bde Mon Sep 17 00:00:00 2001 From: Zixaphir <zixaphirmoxphar@gmail.com> Date: Thu, 8 Aug 2013 23:53:48 -0700 Subject: [PATCH 148/172] Keybind to scroll to posts quoting you. --- builds/4chan-X.user.js | 49 +++++++++++++++++++++++++++++-- builds/crx/script.js | 49 +++++++++++++++++++++++++++++-- src/General/Config.coffee | 8 +++++ src/General/css/style.css | 4 +-- src/Miscellaneous/Keybinds.coffee | 4 +++ src/Monitoring/Unread.coffee | 3 ++ src/Quotelinks/QuoteYou.coffee | 27 ++++++++++++++++- 7 files changed, 137 insertions(+), 7 deletions(-) diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js index f3cf1cc86..51cb3c167 100644 --- a/builds/4chan-X.user.js +++ b/builds/4chan-X.user.js @@ -297,7 +297,9 @@ 'Next reply': ['j', 'Select next reply.'], 'Previous reply': ['k', 'Select previous reply.'], 'Deselect reply': ['Shift+d', 'Deselect reply.'], - 'Hide': ['x', 'Hide thread.'] + 'Hide': ['x', 'Hide thread.'], + 'Previous Post Quoting You': ['Alt+Up', 'Scroll to the previous post that quotes you.'], + 'Next Post Quoting You': ['Alt+Down', 'Scroll to the next post that quotes you.'] }, updater: { checkbox: { @@ -4196,6 +4198,38 @@ $.addClass(this.nodes.root, 'quotesYou'); } } + }, + cb: { + seek: function(type) { + var post, posts, result, str; + + if (!QuoteYou.lastRead) { + if (!(post = QuoteYou.lastRead = $('.quotesYou'))) { + new Notification('warning', 'No posts are currently quoting you, loser.', 20); + } + if (QuoteYou.cb.scroll(post)) { + return; + } + } + str = "" + type + "::div[contains(@class,'quotesYou')]"; + result = $.X(str, QuoteYou.lastRead); + while (post = result.snapshotItem(type === 'preceding' ? result.snapshotLength - 1 : 0)) { + if (QuoteYou.cb.scroll(post)) { + return; + } + } + posts = $$('.quotesYou'); + return QuoteYou.cb.scroll(posts[type === 'following' ? 0 : posts.length - 1]); + }, + scroll: function(post) { + QuoteYou.lastRead = post; + if (Get.postFromRoot(post).isHidden) { + return false; + } else { + Header.scrollToPost(post); + return true; + } + } } }; @@ -7971,6 +8005,11 @@ bottom = post.nodes.root.getBoundingClientRect().bottom; if (bottom < height) { ID = post.ID; + if (Conf['Mark Quotes of You']) { + if (post.info.yours) { + QuoteYou.lastRead = post.nodes.root; + } + } if (Conf['Quote Threading']) { posts.splice(i, 1); continue; @@ -9091,6 +9130,12 @@ ThreadHiding.toggle(thread); } break; + case Conf['Previous Post Quoting You']: + QuoteYou.cb.seek('preceding'); + break; + case Conf['Next Post Quoting You']: + QuoteYou.cb.seek('following'); + break; default: return; } @@ -10711,7 +10756,7 @@ } return Main.thisPageIsLegit; }, - css: "/* General */\n.dialog {\nbox-shadow: 0 1px 2px rgba(0, 0, 0, .15);\nborder: 1px solid;\ndisplay: block;\npadding: 0;\n}\n.captcha-img,\n.field {\nbackground-color: #FFF;\nborder: 1px solid #CCC;\n-moz-box-sizing: border-box;\nbox-sizing: border-box;\ncolor: #333;\nfont: 13px sans-serif;\noutline: none;\ntransition: color .25s, border-color .25s;\ntransition: color .25s, border-color .25s;\n}\n.field::-moz-placeholder,\n.field:hover::-moz-placeholder {\ncolor: #AAA !important;\nfont-size: 13px !important;\nopacity: 1.0 !important;\n}\n.captch-img:hover,\n.field:hover {\nborder-color: #999;\n}\n.field:hover, .field:focus {\ncolor: #000;\n}\n.field[disabled] {\nbackground-color: #F2F2F2;\ncolor: #888;\n}\n.move {\ncursor: move;\noverflow: hidden;\n}\nlabel, .favicon {\ncursor: pointer;\n}\na[href=\"javascript:;\"] {\ntext-decoration: none;\n}\n.warning {\ncolor: red;\n}\n#boardNavDesktop {\ndisplay: none !important;\n}\na {\noutline: none !important;\n}\n\n/* 4chan style fixes */\n.opContainer, .op {\ndisplay: block !important;\noverflow: visible !important;\n}\n[hidden] {\ndisplay: none !important;\n}\n\n/* fixed, z-index */\n#overlay,\n#fourchanx-settings,\n#qp, #ihover,\n#navlinks, .fixed #header-bar,\n:root.float #updater,\n:root.float #thread-stats,\n#qr {\nposition: fixed;\n}\n#fourchanx-settings {\nz-index: 999;\n}\n#overlay {\nz-index: 900;\n}\n#notifications {\nz-index: 70;\n}\n#qp, #ihover {\nz-index: 60;\n}\n#menu {\nz-index: 50;\n}\n#navlinks, #updater, #thread-stats {\nz-index: 40;\n}\n.fixed #header-bar.autohide {\nz-index: 35;\n}\n#qr {\nz-index: 30;\n}\n#watcher {\nz-index: 8;\n}\n:root.fixed-watcher #watcher {\nz-index: 20;\n}\n.fixed #header-bar {\nz-index: 10;\n}\n/* Header */\n.fixed.top body {\npadding-top: 2em;\n}\n.fixed.bottom body {\npadding-bottom: 2em;\n}\n.fixed #header-bar {\nright: 0;\nleft: 0;\npadding: 3px 4px 4px;\n}\n.fixed.top #header-bar {\ntop: 0;\n}\n.fixed.bottom #header-bar {\nbottom: 0;\n}\n#header-bar {\nborder-width: 0;\ntransition: all .1s .05s ease-in-out;\n}\n:root.centered-links #shortcuts {\nwidth: 300px;\ntext-align: right;\n}\n:root.centered-links #header-bar {\ntext-align: center;\n}\n:root.centered-links #custom-board-list {\nposition: relative;\nleft: 150px;\n}\n.fixed.top #header-bar {\nborder-bottom-width: 1px;\n}\n.fixed.bottom #header-bar {\nbox-shadow: 0 -1px 2px rgba(0, 0, 0, .15);\nborder-top-width: 1px;\n}\n.fixed.bottom #header-bar .menu-button i {\nborder-top: none;\nborder-bottom: 6px solid;\n}\n#board-list {\ntext-align: center;\n}\n.fixed #header-bar.autohide:not(:hover) {\nbox-shadow: none;\ntransition: all .8s .6s cubic-bezier(.55, .055, .675, .19);\n}\n.fixed.top #header-bar.autohide:not(:hover) {\nmargin-bottom: -1em;\n-webkit-transform: translateY(-100%);\ntransform: translateY(-100%);\n}\n.fixed.bottom #header-bar.autohide:not(:hover) {\n-webkit-transform: translateY(100%);\ntransform: translateY(100%);\n}\n#scroll-marker {\nleft: 0;\nright: 0;\nheight: 10px;\nposition: absolute;\n}\n:root:not(.autohide) #scroll-marker {\npointer-events: none;\n}\n#header-bar #scroll-marker {\ndisplay: none;\n}\n.fixed #header-bar #scroll-marker {\ndisplay: block;\n}\n.fixed.top #header-bar #scroll-marker {\ntop: 100%;\n}\n.fixed.bottom #header-bar #scroll-marker {\nbottom: 100%;\n}\n#header-bar a:not(.entry):not(.close) {\ntext-decoration: none;\npadding: 1px;\n}\n#header-bar input {\nmargin: 0;\nvertical-align: bottom;\n}\n#shortcuts:empty {\ndisplay: none;\n}\n.brackets-wrap::before {\ncontent: \"\\00a0[\";\n}\n.brackets-wrap::after {\ncontent: \"]\\00a0\";\n}\n.disabled,\n.expand-all-shortcut {\nopacity: .45;\n}\n#shortcuts {\nfloat: right;\n}\n.shortcut {\nmargin-left: 3px;\n}\n#navbotright,\n#navtopright {\ndisplay: none;\n}\n#toggleMsgBtn {\ndisplay: none !important;\n}\n.current {\nfont-weight: bold;\n}\n/* 4chan X link brackets */\n.fourchanx-link::after {\ncontent: \"]\";\n}\n.fourchanx-link::before {\ncontent: \"[\";\n}\n/* Notifications */\n#notifications {\nposition: fixed;\ntop: 0;\nheight: 0;\ntext-align: center;\nright: 0;\nleft: 0;\ntransition: all .8s .6s cubic-bezier(.55, .055, .675, .19);\n}\n.fixed.top #header-bar #notifications {\nposition: absolute;\ntop: 100%;\n}\n.notification {\ncolor: #FFF;\nfont-weight: 700;\ntext-shadow: 0 1px 2px rgba(0, 0, 0, .5);\nbox-shadow: 0 1px 2px rgba(0, 0, 0, .15);\nborder-radius: 2px;\nmargin: 1px auto;\nwidth: 500px;\nmax-width: 100%;\nposition: relative;\ntransition: all .25s ease-in-out;\n}\n.notification.error {\nbackground-color: hsla(0, 100%, 38%, .9);\n}\n.notification.warning {\nbackground-color: hsla(36, 100%, 38%, .9);\n}\n.notification.info {\nbackground-color: hsla(200, 100%, 38%, .9);\n}\n.notification.success {\nbackground-color: hsla(104, 100%, 38%, .9);\n}\n.notification a {\ncolor: white;\n}\n.notification > .close {\npadding: 6px;\ntop: 0;\nright: 5px;\nposition: absolute;\n}\n.message {\n-moz-box-sizing: border-box;\nbox-sizing: border-box;\npadding: 6px 20px;\nmax-height: 200px;\nwidth: 100%;\noverflow: auto;\n}\n\n/* Settings */\n:root.fourchan-x body {\n-moz-box-sizing: border-box;\nbox-sizing: border-box;\n}\n#overlay {\nbackground-color: rgba(0, 0, 0, .5);\ntop: 0;\nleft: 0;\nheight: 100%;\nwidth: 100%;\n}\n#fourchanx-settings {\n-moz-box-sizing: border-box;\nbox-sizing: border-box;\nbox-shadow: 0 0 15px rgba(0, 0, 0, .15);\nheight: 600px;\nmax-height: 100%;\nwidth: 900px;\nmax-width: 100%;\nmargin: auto;\npadding: 3px;\ntop: 50%;\nleft: 50%;\n-moz-transform: translate(-50%, -50%);\n-webkit-transform: translate(-50%, -50%);\ntransform: translate(-50%, -50%);\n}\n#fourchanx-settings > nav {\npadding: 2px 2px 0;\nheight: 15px;\n}\n#fourchanx-settings > nav a {\ntext-decoration: underline;\n}\n#fourchanx-settings > nav a.close {\ntext-decoration: none;\npadding: 2px;\n}\n.section-container {\noverflow: auto;\nposition: absolute;\ntop: 2.1em;\nright: 5px;\nbottom: 5px;\nleft: 5px;\npadding-right: 5px;\n}\n.sections-list {\npadding: 0 3px;\nfloat: left;\n}\n.credits {\nfloat: right;\n}\n.tab-selected {\nfont-weight: 700;\n}\n.section-sauce ul,\n.section-advanced ul {\nlist-style: none;\nmargin: 0;\n}\n.section-sauce ul {\npadding: 8px;\n}\n.section-advanced ul {\npadding: 0px;\n}\n.section-sauce li,\n.section-advanced li {\npadding-left: 4px;\n}\n.section-main label {\ntext-decoration: underline;\n}\n.section-filter ul {\npadding: 0;\n}\n.section-filter li {\nmargin: 10px 40px;\n}\n.section-filter textarea {\nheight: 500px;\n}\n.section-sauce textarea {\nheight: 350px;\n}\n.section-advanced .field[name=\"boardnav\"] {\nwidth: 100%;\n}\n.section-advanced textarea {\nheight: 150px;\n}\n.section-advanced .archive-cell {\nmin-width: 160px;\ntext-align: center;\n}\n.section-advanced #archive-board-select {\nposition: absolute;\n}\n.section-advanced .note {\nfont-size: 0.8em;\nfont-style: italic;\nmargin-left: 10px;\n}\n.section-advanced .note code {\nfont-style: normal;\nfont-size: 11px;\n}\n.section-keybinds .field {\nfont-family: monospace;\n} \n#fourchanx-settings fieldset {\nborder: 1px solid;\nborder-radius: 3px;\n}\n#fourchanx-settings legend {\nfont-weight: 700;\n}\n#fourchanx-settings textarea {\nfont-family: monospace;\nmin-width: 100%;\nmax-width: 100%;\n}\n#fourchanx-settings code {\ncolor: #000;\nbackground-color: #FFF;\npadding: 0 2px;\n}\n.unscroll {\noverflow: hidden;\n}\n\n/* Announcement Hiding */\n:root.hide-announcement #globalMessage {\ndisplay: none;\n}\na.hide-announcement {\nfloat: left;\n}\n\n/* Unread */\n#unread-line {\nmargin: 0;\nborder-color: rgb(255,0,0);\n}\n\n/* Thread Updater */\n#updater {\nbackground: none;\nborder: none;\nbox-shadow: none;\n}\n#updater > .move {\npadding: 5px 3px 0px;\nmargin-bottom: -3px;\n}\n#updater > div:last-child {\ntext-align: center;\n}\n#updater input[type=number] {\nwidth: 4em;\n}\n:root.float #updater {\npadding: 0px 3px;\n}\n.new {\ncolor: limegreen;\n}\n#update-status.new {\nmargin-right: 5px;\n}\n#update-timer {\ncursor: pointer;\n}\n\n/* Thread Watcher */\n#watcher {\nposition: absolute;\n}\n#watcher {\npadding-bottom: 3px;\noverflow: hidden;\nwhite-space: nowrap;\nmin-width: 120px;\nmax-height: 92%;\noverflow-y: auto;\n}\n:root.fixed-watcher #watcher {\nposition: fixed;\n}\n:root:not(.fixed-watcher) #watcher:not(:hover) {\nmax-height: 210px;\noverflow-y: hidden;\n}\n#watcher > .move {\npadding-top: 3px;\n}\n#watcher > div {\nmax-width: 250px;\noverflow: hidden;\npadding-left: 3px;\npadding-right: 3px;\ntext-overflow: ellipsis;\n}\n#watcher a {\ntext-decoration: none;\n}\n#watcher .move>.close {\nposition: absolute;\nright: 0px;\ntop: 0px;\npadding: 0px 4px;\n}\n.watch-thread-link {\npadding-top: 18px;\nwidth: 18px;\nheight: 0px;\ndisplay: inline-block;\nbackground-repeat: no-repeat;\nopacity: 0.2;\nposition: relative;\ntop: 1px;\n}\n.watch-thread-link.watched {\nopacity: 1;\n}\n\n/* Thread Stats */\n#thread-stats {\nbackground: none;\nborder: none;\nbox-shadow: none;\n}\n:root.float #post-count, :root.float #file-count {\npointer-events: none;\n}\n:root.float #thread-stats {\npadding: 0px 3px;\n}\n\n/* Quote */\n.deadlink {\ntext-decoration: none !important;\n}\n.backlink.deadlink:not(.forwardlink),\n.quotelink.deadlink:not(.forwardlink) {\ntext-decoration: underline !important;\n}\n.inlined {\nopacity: .5;\n}\n#qp input, .forwarded {\ndisplay: none;\n}\n.quotelink.forwardlink,\n.backlink.forwardlink {\ntext-decoration: none;\nborder-bottom: 1px dashed;\n}\n.filtered {\ntext-decoration: underline line-through;\n}\n:root.hide-backlinks .backlink.filtered {\ndisplay: none;\n}\n.inline {\nborder: 1px solid;\ndisplay: table;\nmargin: 2px 0;\n}\n.inline .post {\nborder: 0 !important;\nbackground-color: transparent !important;\ndisplay: table !important;\nmargin: 0 !important;\npadding: 1px 2px !important;\n}\n#qp > .opContainer::after {\ncontent: '';\nclear: both;\ndisplay: table;\n}\n#qp .post {\nborder: none;\nmargin: 0;\npadding: 2px 2px 5px;\n}\n#qp img {\nmax-height: 80vh;\nmax-width: 50vw;\n}\n.qphl {\noutline: 2px solid rgba(216, 94, 49, .7);\n}\n:root.highlight-own .yourPost>.reply,\n:root.highlight-you .quotesYou>.reply {\nborder-left: 2px solid rgba(221,0,0,.5);\n}\n/* Quote Threading */\n.threadContainer {\nmargin-left: 20px;\nborder-left: 1px solid rgba(128,128,128,.3);\n}\n.threadOP {\nclear: both;\n} \n\n/* File */\n.fileText:hover .fntrunc,\n.fileText:not(:hover) .fnfull,\n.expanded-image > .post > .file > .fileThumb > img[data-md5],\n:not(.expanded-image) > .post > .file > .fileThumb > .full-image {\ndisplay: none;\n}\n.expanding {\nopacity: .5;\n}\n:root.fit-height .full-image {\nmax-height: 100vh;\n}\n:root.fit-width .full-image {\nmax-width: 100%;\n}\n:root.gecko.fit-width .full-image {\nwidth: 100%;\n}\n#ihover {\n-moz-box-sizing: border-box;\nbox-sizing: border-box;\nmax-height: 100%;\nmax-width: 75%;\npadding-bottom: 16px;\n}\n.fappeTyme .thread > .noFile,\n.fappeTyme .threadContainer > .noFile {\ndisplay: none;\n}\n\n/* Index/Reply Navigation */\n#navlinks {\nfont-size: 16px;\ntop: 25px;\nright: 10px;\n}\n\n/* Filter */\n.opContainer.filter-highlight {\nbox-shadow: inset 5px 0 rgba(255, 0, 0, .5);\n}\n.filter-highlight > .reply {\nbox-shadow: -5px 0 rgba(255, 0, 0, .5);\n}\n\n/* Spoiler text */\n:root.reveal-spoilers s {\ncolor: white !important;\n}\n\n/* Thread & Reply Hiding */\n.hide-thread-button,\n.hide-reply-button {\nfloat: left;\nmargin-right: 2px;\n}\n.stub ~ * {\ndisplay: none !important;\n}\n.stub input {\ndisplay: inline-block;\n}\n\n/* QR */\n:root.hide-original-post-form #postForm,\n:root.hide-original-post-form .postingMode,\n:root.hide-original-post-form #togglePostForm,\n#qr.autohide:not(.has-focus):not(:hover) > form,\n.postingMode ~ #qr select,\n#file-n-submit:not(.has-file) #qr-filerm {\ndisplay: none;\n}\n#qr select,\n#dump-button,\n.remove,\n.captcha-img {\ncursor: pointer;\n}\n#qr {\nz-index: 20;\nposition: fixed;\npadding: 1px;\nborder: 1px solid transparent;\nmin-width: 300px;\nborder-radius: 3px 3px 0 0;\n}\n#qrtab {\nborder-radius: 3px 3px 0 0;\n}\n#qrtab {\nmargin-bottom: 1px;\n}\n#qr .close {\nfloat: right;\npadding: 0 3px;\n}\n#qr .warning {\nmin-height: 1.6em;\nvertical-align: middle;\npadding: 0 1px;\nborder-width: 1px;\nborder-style: solid;\n}\n.qr-link-container {\ntext-align: center;\n}\n.persona {\nwidth: 248px;\nmax-width: 100%;\nmin-width: 100%;\n}\n#dump-button {\nwidth: 10%;\nmargin: 0;\nmargin-right: 4px;\nfont: 13px sans-serif;\npadding: 1px 0px 2px;\nopacity: 0.6;\n}\n.persona .field:not(#dump) {\nwidth: 95px;\nmin-width: 33.3%;\nmax-width: 33.3%;\n}\n#qr textarea.field {\nheight: 14.8em;\nmin-height: 9em;\n}\n#qr.has-captcha textarea.field {\nheight: 9em;\n}\ninput.field.tripped:not(:hover):not(:focus) {\ncolor: transparent !important;\ntext-shadow: none !important;\n}\n#qr textarea {\nresize: both;\n}\n.captcha-img {\nmargin: 0px;\ntext-align: center;\nbackground-image: #fff;\nfont-size: 0px;\nmin-height: 59px;\nmin-width: 302px;\n}\n.captcha-input {\nwidth: 100%;\nmargin: 1px 0 0;\n}\n.captcha-input.error:focus {\nborder-color: rgb(255,0,0) !important;\n}\n.field {\n-moz-box-sizing: border-box;\nmargin: 0px;\npadding: 2px 4px 3px;\n}\n#qr textarea {\nmin-width: 100%;\n}\n#qr [type='submit'] {\nwidth: 25%;\nvertical-align: top;\n}\n:root.webkit #qr [type='submit'] {\nheight: 24px;\n}\n/* Fake File Input */\n#qr-filename,\n.has-file #qr-no-file {\ndisplay: none;\n}\n#qr-no-file,\n.has-file #qr-filename {\ndisplay: inline-block;\npadding: 0px 4px;\nmargin-bottom: 2px;\noverflow: hidden;\ntext-overflow: ellipsis;\nmax-width: 88%;\n}\n#qr-no-file {\ncolor: #AAA;\n}\n#qr-filename-container {\n-moz-box-sizing: border-box;\ndisplay: inline-block;\nposition: relative;\nwidth: 100px;\nmin-width: 74.6%;\nmax-width: 74.6%;\nmargin-right: 0.4%;\nmargin-top: 1px;\noverflow: hidden;\npadding: 2px 1px 0;\nheight: 22px;\n}\n#qr-filename-container:hover {\ncursor: text;\n}\n#qr-extras-container {\nposition: absolute;\nright: 0px;\n}\n#qr-filerm {\nmargin-right: 2px;\nz-index: 2;\n}\n#file-n-submit {\nheight: 23px;\n}\n#qr input[type=file] {\nvisibility: hidden;\nposition: absolute;\n}\n/* Thread Select / Spoiler Label */\n#qr select {\nfloat: right;\n}\n#qr.has-spoiler .has-file #qr-spoiler-label {\nwidth: 6.7%;\nmin-width: 6.7%;\nmax-width: 6.7%;\ndisplay: inline-block;\ntext-align: center;\nvertical-align: top;\n}\n#qr.has-spoiler #file-n-submit:not(.has-file) #qr-spoiler-label {\ndisplay: none;\n}\n#qr.has-spoiler .has-file #qr-filename-container {\nmax-width: 67.9%;\nmin-width: 67.9%;\n}\n#qr-spoiler-label input {\nposition: relative;\ntop: 3px;\n}\n/* Dumping UI */\n.dump #dump-list-container {\ndisplay: block;\n}\n#dump-list-container {\ndisplay: none;\nposition: relative;\noverflow-y: hidden;\nmargin-top: 1px;\n}\n#dump-list {\noverflow-x: auto;\noverflow-y: hidden;\nwhite-space: nowrap;\nwidth: 248px;\nmax-width: 100%;\nmin-width: 100%;\n}\n#dump-list:hover {\noverflow-x: auto;\n}\n.qr-preview {\n-moz-box-sizing: border-box;\ncounter-increment: thumbnails;\ncursor: move;\ndisplay: inline-block;\nheight: 90px;\nwidth: 90px;\npadding: 2px;\nopacity: .5;\noverflow: hidden;\nposition: relative;\ntext-shadow: 0 1px 1px #000;\n-moz-transition: opacity .25s ease-in-out;\nvertical-align: top;\nbackground-size: cover;\n}\n.qr-preview:hover,\n.qr-preview:focus {\nopacity: .9;\n}\n.qr-preview::before {\ncontent: counter(thumbnails);\ncolor: #fff;\nposition: absolute;\ntop: 3px;\nright: 3px;\ntext-shadow: 0 0 3px #000, 0 0 8px #000;\n}\n.qr-preview#selected {\nopacity: 1;\n}\n.qr-preview.drag {\nbox-shadow: 0 0 10px rgba(0,0,0,.5);\n}\n.qr-preview.over {\nborder-color: #fff;\n}\n.qr-preview > span {\ncolor: #fff;\n}\n.remove {\nbackground: none;\ncolor: #e00;\nfont-weight: 700;\npadding: 3px;\n}\na:only-of-type > .remove {\ndisplay: none;\n}\n.remove:hover::after {\ncontent: \" Remove\";\n}\n.qr-preview > label {\nbackground: rgba(0,0,0,.5);\ncolor: #fff;\nright: 0;\nbottom: 0;\nleft: 0;\nposition: absolute;\ntext-align: center;\n}\n.qr-preview > label > input {\nmargin: 0;\n}\n#add-post {\ncursor: pointer;\nfont-size: 2em;\nposition: absolute;\ntop: 50%;\nright: 10px;\n-moz-transform: translateY(-50%);\n}\n.textarea {\nposition: relative;\n}\n:root.webkit .textarea {\nmargin-bottom: -2px;\n}\n#char-count {\ncolor: #000;\nbackground: hsla(0, 0%, 100%, .5);\nfont-size: 8pt;\nposition: absolute;\nbottom: 1px;\nright: 1px;\npointer-events: none;\n}\n\n/* Menu */\n.menu-button {\ndisplay: inline-block;\nposition: relative;\ncursor: pointer;\n}\n.menu-button i {\nborder-top: 6px solid;\nborder-right: 4px solid transparent;\nborder-left: 4px solid transparent;\ndisplay: inline-block;\nmargin: 2px;\nvertical-align: middle;\n}\n#menu {\nposition: fixed;\noutline: none;\n}\n.entry {\nborder-bottom: 1px solid rgba(0,0,0,.25);\ncursor: pointer;\ndisplay: block;\noutline: none;\npadding: 3px 7px;\nposition: relative;\ntext-decoration: none;\nwhite-space: nowrap;\n}\n.left>.entry.has-submenu {\npadding-right: 17px !important;\n}\n.entry:last-child {\nborder-bottom: 0;\n}\n.has-submenu::after {\ncontent: \"\";\nborder-left: .5em solid;\nborder-top: .3em solid transparent;\nborder-bottom: .3em solid transparent;\ndisplay: inline-block;\nmargin: .3em;\nposition: absolute;\nright: 3px;\n}\n.left .has-submenu::after {\nborder-left: 0;\nborder-right: .5em solid;\n}\n.submenu {\ndisplay: none;\nposition: absolute;\nleft: 100%;\ntop: -1px;\n}\n.focused .submenu {\ndisplay: block;\n}\n.imp-exp-result {\nposition: absolute;\ntext-align: center;\nmargin: auto;\nright: 0px;\nleft: 0px;\nwidth: 200px;\n}\n.export, .import {\ncursor: pointer;\ntext-decoration: none !important;\n}\n/* Link Title Favicons */\n.linkify.YouTube {\nbackground: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAMCAYAAABr5z2BAAABIklEQVQoz53LvUrDUBjG8bOoOammSf1IoBSvoCB4JeIqOHgBLt6AIMRBBQelWurQ2kERnMRBsBUcIp5FJSBI5oQsJVkkUHh8W0o5nhaFHvjBgef/Mq+Q46RJBMkI/vE+aOus956tnEswIZe1LV0QyJ5sE2GzgZfVMtRNIdiDpccEssdlB1mW4bvTwdvWJtRdErM7U+8S/FJykCRJX5qm+KpVce8UMNLRLbulz4iSjTAMh6Iowsd5BeNadp3nUF0VlxAEwZBotXC0Usa4ll3meZdA1iguwvf9vpvDA2wvmKgYGtSud8suDB4TyGr2PF49D/vra9jRZ1BVdknMzgwuCGSnZEObwu6sBnVTCHZiaC7BhFx2PKdxUidiAH/4lLo9Mv0DELVs9qsOHXwAAAAASUVORK5CYII=') center left no-repeat!important;\npadding-left: 18px;\n}\n.linkify.Vimeo {\nbackground: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAASJJREFUOE9jYAAC7ln7/pODQXrBmq333PvPu/YaSRikB6QXbACpmmHqsRoAMll7+20UQ0H8tmuv/pdffPFfZtNNuByGASBFIPDh5x+4IV6HHoDFYGDJgw+YBoBMBUkgA5BtIKduuvvy//svX+FSB+88wTTAc+/t/83bj/0HScLA5BPXwc7lKJ36f+L6XXDxhUfOYxrAPWUnWKFp9UQUm3iWQxSDXAEDSX3zcIcB96wD/x+8eA1XDNKMHAYg20GW4Y0FkCIYAAUqzEBQOIBciRzlWKMxZelOlMCEcVxq+jHSC1YDJPs3YBgA8jey0/F6ARRwsFAHORukmat9NdbUijMpg/wKcrJodDFOzSBXwA3Alh9AToZFI7a8Asu98BxJbnYGAJb5vYLDANzSAAAAAElFTkSuQmCC') center left no-repeat!important;\npadding-left: 18px;\n}\n.linkify.SoundCloud {\nbackground: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABsklEQVQ4y5WTy2pUQRCGv2rbzDjJeAlIBmOyipGIIJqFEBDElwh4yULGeRFXPoEIBl/AvQ/gC2RnxCAoxijiwks852S6+3dxzslcHJCpTXVX11/Xv0097gLPgVNMJxnQNfX4zsqleWbnpoMf/oa9d988MM9MC/rp+E0a+A0dsVobMNMCOO8B6McRoABJI+A6gJmN3D2A8jgEBCEkSEMBrcrsDAzDWWn3AjgKFaDMmgRqniGFgsaDp1jrLOngDf1XT1D+A1dFc4MKAkkiCVKjjVu7g9+4Rzx4i1u6hjXbuMWr0O5QPNvCu7IaCZwEKQukLGDrm5x8uI0tr6MkiGlkiv7yLfzN+6S5i6QsIMABkEfcxhbWWYMkVAOjxvYAjc3HNHrbKI9VBQBFwF25XQKSBjqIf1YBuAurEMrczgDygD6/x2LCpFLXLUyQ+PoldphhBhYfIX09XU1+Flaukz7uYqs3SHs7cG4BmTsmkBUF9mmXEwa28BNLPaQPLepuNcbGSWQquQC2/Kdcox1FUGkcB0ykck1nA2+wTzMs8stGnP4rbWGw74EuS/GFQWfK7/wF6P4F7fzIAYkdmdEAAAAASUVORK5CYII=') center left no-repeat!important;\npadding-left: 18px;\n}\n.linkify.audio {\nbackground: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAitJREFUOE9jYCAWKJWwavr0KyXWb/FIbDtUFFyzJx6nVofE2Xo5nXsj0rqPNSR0nVkR2Hjmgmfd+U9Otdf+m5Vf/6+SfeU/R9ChVVgNYDRtlfJuuPA/rPfe/4QpD/6nznj0P27Kw/9unff/69Xf+69c/+C/SO7N/0z+OAxgMmmRCe++/r9i3ev/KWvf/vdY8PK/bt/9/wrNV3/IN5y/IVt1YqNg4pGTTP4HsbuA2bhZ2qvpyn+xjIObxAp3VwqlrgngLFyryVy5nhPmZJHANS2cwYexG8BmVC/pWn3hP4NZlzWuQDJI3dIiFnUUuwEsQAOcq87jNcC7fHeLUtJxHF4AGmBWeAavAWH1+1rUUk7giAWjOknllON4DXAs2NEiG4/DBQxAF/CFHfrPYI4jDFSLuJVjNrUJhB/B7gIGo1pJRt99GAZYJK7wLJ1z7Xzl4vu/7aqv/GRBj0bjqAX2qb0nJ7mXH17C4HcUxQA+hymWtSue/C5a9up/9Ozn/7Vr7v1nRY7GqMb91T3b3v6vWvPmf/S0p/9ZQk+DDLCBRSOz06Jqk+o7/21nvfqvsebDf7kZL/5zBaxphkezd+OFn7HzXvz3Wvjmv9a8N//5Ek//ZTBpVYUrMG2X5wjcdl68+uI/wa5Lr3hSNjczGFeywOVZ/bbcVGp//F9izfv/Ql03f3P4LC/HSEQquYwMFnUCDJ7dzBhyjGZNQpye89M5gpfnMvtNUyE2h4PUAQBovvT7lyNljwAAAABJRU5ErkJggg==') center left no-repeat!important;\npadding-left: 18px;\n}\n.linkify.LiveLeak {\nbackground: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAydJREFUOE9Nk1tIk2EYx79NyUNqTk0o6KYrnZeChodLDxfeZpCbJk4RXU5Nm7tYRYhiYXbQlaeGutyW2gxtpB1RIyKDEjKwA6Ti2dR5KNDn+fq/S6TBj/f93r3P732e53s/qfnkSdej4GB2SBLbwf+jmB+gUMgOheLg/z7EdCUnO6Ref392SpK8Hyh3I+gBwBo7lUp2xcbyQEoKD6alyQOpqd754/h4FjJXZCRJTl9ftmEzoK5/wdQJxPgkLY2WV1dpc2uLtnZ2eHNnhza3t2nd46GhjAzuValY6jx0iIfS03msoIDuQ9COQCtoUSjohU5HuwgaN5loeXycd3d3aW9vzwvW2K5SkdTi58fvzGb+3tdHFggA3QONEAzn59PvjQ1yqNX0zenkvX0B4ffWaGRraChJd/385JGqKvlzTw/fRqOaIGkEd1DjU52O/3g83BkTw5MOh7yJuUCUM2o0yi2hoSw1IIOhykr+YLNRHYKu4XQvyKA/N5c8yMCCDD7Z7bz26xcJ1rH2rKKCG0UJdRAMlJbyG6uVrkJQjWAB5tSbk0Nr2HwDgvcQiIYur6zQyvo6ucvLueHIEZKuQPBQr+dXra1kRuqXEOwFArtWSytra1QdFUVjNhvPLS3R3OIiLUDUD0F1WBhJJtwDW2Ehu5uaqBICI4IFlRB0QLCEzaboaHrd0cHzCBYsIIuesjK+LAQXkEFrXh676uupGCWcR6AeghLQptGQONUAwfOuLp6Zn6eZuTmaXVig7pISrhI90ENgQbdHhoep32JhFzLpu3WLio8epUYIfs7OUjF6UKJW88XERLqYkEBNej11oG8XhCAvMFAuOn5cNiclsTkhQTbhmpri4lgbEMANWi1DwC/xit3t7bK7rY0Fo4OD3G4wyEURESzloAdnceezlErK8vH5N4KzPj50PTOTfkxP0+THj/RlYoInJyZI8HVqim5qNFwQHk7SucBAPo2PKRMNPLM/4pnFszYkhJsNBu6uqWFHba1sr61lQSveQFZQkFx07BhJmhMnrLn4NLMPH/aSExR0QDbmWhwgyEapwDvXoDxdWBiXnjrV/Bdm2kYUxLwmEgAAAABJRU5ErkJggg==') center left no-repeat!important;\npadding-left: 18px;\n}\n.linkify.Vocaroo {\nbackground: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAw9JREFUOE9jYMABuMwYmCyTJKUCGlSnFSy02TTzeOyCiQcDViX26qVz2TAyYtWmEMwuoZ3M7V40LcB79pHkc0svpvzY8jD//87nxf+3Pyn8v/ZO8v+VNyP/2mZJumI1QCWSI8232Hjumitlfw5+qPp/9l8TCt76JP//xkdx/wsXWCzjtWFkwTCkbWFe9plPk/+ga4Txz/xt/D/hkN//gMXif21a+NbyWjIwoRiy6GDT5rP/mlFsPfyp5n/NpOj/22+0gMUXXIz/H7hC/L/bFKFbPDZMrHAD5H35OPt2J9zacDv/f3V7xv9FhwrBGubsT/1//Pjx/1GJ/mD+/nfl/1v3Ovy3KRJNQbHdOlXCvOO03/+pm1P/v3v37n90hhtYw9HPtf8Xb2v937cmHswHeWPRxYj/LvkK3igGKARwicTO07118H3V/5kbi/4vPZMJtK3s/6YH2f+Pfq1B8VbjWrdnMu5s4nAD9CNFhKwz5DTUvLl419zKvAcLtG1P84BRl/b/5M/6/6f/NPzf/qzo84yj0Uus0xUU4Zor54bm9+4OfZG02OCuoAMTb9ZkC9ull1Nvrr2Z+XvRpaRfc65H/68F+jl9svEhzyLFWoccWVc+eyTHq/twydjlKRln7jX9bNMkMJnbhoFRL1xCqmKx6/yi2fYXa/c5/e846PV/5fW0/7OPx/yfcjzop34ulxdGGvDuU8mMXaX507lBuiN6ueadmQeT/p/93vf/1O+G//sP5fw/eL3o/5JLif8zVxs+Tlir9S26UyeFQQvJGBE7FvaFZ9LfN+1y+WjbItSb3GmXvXd15v8zroH/HxgE/D+aGPx/18vi/z07PeZNPRKxe/Kh0Ae8toxscCO4zBkYXArk9C1SxJUYjBkYPPIVtbbuTftz3cz//2O9wP/75iSAXdO72/dt2HL5F6YlfBW4MiJYXMiBiW3t7azHBx+V/t89N+H/8a+1//e9K/9attDp5LQjYX8SuvVL8RoAkmxa65299Erq1FnHo0qrl7t4BddriIs4MrM3rfWcFd+pGwVSAwBZ0bKP8yrZPAAAAABJRU5ErkJggg==') center left no-repeat!important;\npadding-left: 18px;\n}\n.linkify.pastebin {\nbackground: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAtZJREFUOE+NU91LWmEc7sJtQew/2MUY7INg7CLY3W5GMHazyzEQo9UmfYxZTbAiVlgRqLMSZ+XnDC3z2+Y0+8JGakKZTtR0Tl2wtgtLLQh29cz3ZZ3h3Q68vOc95zzP73l+z+/U1f292O09DRxubxOH23P//1bvtQts3dPnry7LZnXJhcUl5Avf8dHtwY+fv2AyW5DOfIXFakMm+w0G4wISyRRm55TQG0y/Wzv6mikJ52Xf9TmVBoFAAD6fDwqFAqFQCJubmzCbzZiensbp6SmkUikikQi0Wi0kEgm6ewVaStDCfXPDandifn6egoaGhrCzswO1Wg2Hw4HBwUGk02kIBAL4/X4IhUJMTk6ii8dfYggy2RwymQzOz88Rj8dRLpexv7+PSqWCYDCIQqGAra0tJBIJrK2t0XdVAjNDEIl+wfj4OEqlEq2wt7dHrchkMmrBYDCAz+fTIjweD7FYrJbgIJOlgLOzM8jlcip1eXmZ2rFarVAqlRCLxcjlchCJRFRljYJYPAG32418Pg+n04lsNouVlRUcHh7C4/FQIOlHNBqlezgcJgQWxkIgGMbExASVNjY2hvX1dVo9mUzS5wREFLhcLrqTcw2B//M2RkdHodPp4PV6oVKpqH+SCom3v7+fNnF4eJiJusbCJ6+PviSyScakiaR5RIHRaKQpmEwmbAdCeD8zB6vdhebHT8SMhcUlC83bbrdTJRsbG3RwiCVCRNJJpDIoVeNNJJJQzKryV+rrmxiCtyNCCmaz2VhdXQWXy6XDpNfrodFoYLXZUTw+pk222Z3lW3ca26rgSwzBwqIZAwMDlITMAVEwNTVFR5fEJpK8Qyp1AJvDVbrTeLenCmxgfiZ22+urCtWHyu7uLp2wVCpFKx0dHaFYLOLk5KT6Y9kgk89kb95ubK0BX7A8a+1qannRLeW0daj/rU51S3tn9dypfvDw0QiLxbpX/Z7FVK7e/AEj4Wf24/2f5AAAAABJRU5ErkJggg==') center left no-repeat!important;\npadding-left: 18px;\n}\n.linkify.gist {\nbackground: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAk1JREFUeNqUkzuIE1EUhv955MnsbB6r4kYQLUQQFncV3SnCIqJsoWGDYOGjsIiCtY2Kla1sjLBIsFFcXJC1kaSwENQmXUQSRSUSjCQSTCbkbR4z47lXEgtBNwcu3DNzvvO8R8jlcj7LshKmaWqYQERRTAmCcEru9/sJr9er0QF92BJMAVGr1TQ6CeZAc7lcGAwGkyQAxpTLZU0eDoc8crfbRTgcRjAYRCQSYSmi1WpxY7fbjU6ng1gshmaziXg8zhnGIpVKWbquW9ls1mLZsaMoiqWq6lgnBxY55He/328Vi0XOMFZmqVMD4fF4QBAajcY48khY9JE4HA4enTGMFVkaTHmy+ZzD/5NSqYSNB484w1h55ODO3TVu4FXcWDywl24Cmp0e1WBhyuWELAtIf/qKUrWOONmev3Lpt4NRCXq1gplpBS/v3cDc0nGg9h1o1ZkfwO4Atu1B8cM7HLt8k37V/y5B2b4bJxf2Y+7oEbyJrkMvUjki0YYJ03LidfQxAt4dOHdCw5RdGZcgGobBlQtnV/BDr1GfDai7ZiHZZRi9PoY/e5SCCTUwC9gk1GmMh5YWOcNYkR4Sv1y9uAJbYB82N57h4OnDmN7phjQ0qUkWRJuB+TMaPn/5iFfvv+Ha7eucYey4iWw8q6tRJJNJ3Fp7ClUawEkViBTfkCR0YUNTVHD/4Tpm/P4/U2CeKpUKfD4fJDIMhUKEhP45St50XedZyLQY6Xw+v8AUemVb2oNqtYpCocCWKi2TLLfb7ReZTGZ+kmUi7i2VvfxLgAEAZChMriPcl+IAAAAASUVORK5CYII=') center left no-repeat!important;\npadding-left: 18px;\n}\n.linkify.image {\nbackground: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAs5JREFUOE+lk/tvi1EYx98/xT8gW4REIpGFMEQWl2FiM9ZMZhm2xRAyOsmujFFmdFRHu0tWm87UypxStr69zPauN5e5rHVp3IYhbOvHy+wHEQlxkm+ek+d8nm9OznkeSfrfldmgJC7QyUlTymsJTfuTZ25z4HdWYwyLreYhtpgekGPw0+kKvo1Eo+IXRSIiEhkWZuc9tqnsJD9EqTUopCxjSGTpB0iueczSo1HyW8cpsExQ1DbxI2pt45j9cXpexul4FEd79RnZphAa/SD7WvuFtO6UItbU9LC+YQxNI2w0wwYT5LRAdhOU3oBTIXC9gXP3oUSGgz2vST3gYHejR0jptT1C332f8yrUEYHrz8CgxDnpm6DKCUfc0KnmXa/AEVPPwnDcD0cvetA2uYRk67Ive/lpjO7YBO1PPuF8Df3vwf4cbNE4tqdw7YVq8HYyHx6FvhE1hkMEg8HDUqvFkjT4aIjMqkqyqkswDSrcfBfH+Q561YLAZ/B+BLda6FXlU/cPv0AoEPhuoP1h4Av7Wbh9E/Py15NWWUjeSR3nZDfeN+N0DY9hG/7K1eGP3P0S5/EYRFUF/IOTBrUXHPm9fT6mr1xEwupkZqxbzLyiDJYUZ5NSnkdqdSHpxyrYdFpPgdmAsdfJwPMI/Yr65bf7tZLGGBQ7DNdJWFtIYvoOZmbuZE7OXpIKKli86zAr9p9gTVktWTVnKTI2U95uRWe3U2IJUDbVB5p6hVm5x5m9Vc/cnedZUNzC8lILaQesZBy6hEZ3maKzgvJWFzVWD9XtXvVGQbSWASFtMATVRlJIKbOTWtlJXaeXepuPM1f6MNp9GLt8mLvvYLmp0OhQ2Fwvk6m7xaqDTvY0eYWUVtcnllXfYlGpnfklVuraHHg8HjxuN+6fktUHlWWZPaZeUo/ILK0UKttBcbNbSB9GP0yLxWJJUxoZGUn80zD9C/vXQ/4NHY10h3M1zmQAAAAASUVORK5CYII=') center left no-repeat!important;\npadding-left: 18px;\n}\n.linkify.InstallGentoo {\nbackground: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAklJREFUOE9jYEAAASBTCorZkcSRmTjVCDLziCwG4hfM3EIvGNm44oC6WNEM4WXi5FsEkmfhFX3BxMmfAJSHW9Qr55Px3aZp3X/btq3/hQydPzKysMcCFbBBDeFj4uBdqBJR/gskb1W34j+PmulLoJwbzBJJoMm7dNO7/ntMP/XfpW/v//SKvk+7tl7fvXfTpx5pCdWVSiHFv1wnHQbLi9sE/Wdk5SwBauaCGQB3gUPb5v+7Lr/8/+fvr/9fv/z+f+Pyr/9bV735l9Wy/79Dx/b/Nk0bsLoAHgbeAVHv/v77/f8f0IB7N7+cu3DuecK54z9+7lzz639e9pK/7HwSWMMA5BJwCJeXtOm/fvVj1fcfv369f//92cN7X6ZcPvf9x6Htv//vXP3r/+T245UEYgpskPTNq08LgN749/PH7/93rv/6f/rw7//nj//4f+bU0zQcUQwWBkdVbGz62y+fv3wHeeXrlz//H9798//qpY//M3KqfzGxc8djiWKwZnBUuWQ2/fr46fv/P39+///x/ff/d69//z97+s7fyMb5/+y7d2GLYriDZikFF/1qXXXj/4Pbv/8/f/jn/5MH316/eP6jVlBAaIt6VO1/jxmn/zv27P7Pp2HxEajLD90ra9Sj6/979O37X73w0n+vqOL/0lJyMVBFq0EGgDSD0oKAlu1/oHg4ugGzVCKqfouYuL1Xj676Iajr8AnJFricGqYc3Bw+Zi6BVUxsXLHAdL6QiYMPFNrwpIxHDsUhgtAMAopKDjQn4pPDF7P45QC4hSmc1eX8WgAAAABJRU5ErkJggg==') center left no-repeat!important;\npadding-left: 18px;\n}\n\n/* General */\n:root.yotsuba .dialog {\nbackground-color: #F0E0D6;\nborder-color: #D9BFB7;\n}\n:root.yotsuba .field:focus {\nborder-color: #EA8;\n}\n\n/* Header */\n:root.yotsuba #header-bar, :root.yotsuba #notifications {\nfont-size: 9pt;\ncolor: #B86;\n}\n:root.yotsuba #header-bar a, :root.yotsuba #notifications a {\ncolor: #800000;\n}\n\n/* Settings */\n:root.yotsuba #fourchanx-settings fieldset {\nborder-color: #D9BFB7;\n}\n\n/* Quote */\n:root.yotsuba .backlink.deadlink {\ncolor: #00E !important;\n}\n:root.yotsuba .inline {\nborder-color: #D9BFB7;\nbackground-color: rgba(255, 255, 255, .14);\n}\n\n/* QR */\n.yotsuba #dump-list::-webkit-scrollbar-thumb {\nbackground-color: #F0E0D6;\nborder-color: #D9BFB7;\n}\n:root.yotsuba .qr-preview {\nbackground-color: rgba(0, 0, 0, .15);\n}\n\n/* Menu */\n:root.yotsuba #menu {\ncolor: #800000;\n}\n:root.yotsuba .entry {\nborder-bottom: 1px solid #D9BFB7;\nfont-size: 10pt;\n}\n:root.yotsuba .focused.entry {\nbackground: rgba(255, 255, 255, .33);\n}\n\n/* Watcher Favicon */\n:root.yotsuba .watch-thread-link\n{\nbackground-image: url(\"data:image/svg+xml,<svg viewBox='0 0 26 26' preserveAspectRatio='true' xmlns='http://www.w3.org/2000/svg'><path fill='rgb(128,0,0)' d='M24.132,7.971c-2.203-2.205-5.916-2.098-8.25,0.235L15.5,8.588l-0.382-0.382c-2.334-2.333-6.047-2.44-8.25-0.235c-2.204,2.203-2.098,5.916,0.235,8.249l8.396,8.396l8.396-8.396C26.229,13.887,26.336,10.174,24.132,7.971z'/></svg>\");\n}\n\n/* General */\n:root.yotsuba-b .dialog {\nbackground-color: #D6DAF0;\nborder-color: #B7C5D9;\n}\n:root.yotsuba-b .field:focus {\nborder-color: #98E;\n}\n\n/* Header */\n:root.yotsuba-b #header-bar, :root.yotsuba-b #notifications {\nfont-size: 9pt;\ncolor: #89A;\n}\n:root.yotsuba-b #header-bar a, :root.yotsuba-b #notifications a {\ncolor: #34345C;\n}\n\n/* Settings */\n:root.yotsuba-b #fourchanx-settings fieldset {\nborder-color: #B7C5D9;\n}\n\n/* Quote */\n:root.yotsuba-b .backlink.deadlink {\ncolor: #34345C !important;\n}\n:root.yotsuba-b .inline {\nborder-color: #B7C5D9;\nbackground-color: rgba(255, 255, 255, .14);\n}\n\n/* QR */\n.yotsuba-b #dump-list::-webkit-scrollbar-thumb {\nbackground-color: #D6DAF0;\nborder-color: #B7C5D9;\n}\n:root.yotsuba-b .qr-preview {\nbackground-color: rgba(0, 0, 0, .15);\n}\n\n/* Menu */\n:root.yotsuba-b #menu {\ncolor: #000;\n}\n:root.yotsuba-b .entry {\nborder-bottom: 1px solid #B7C5D9;\nfont-size: 10pt;\n}\n:root.yotsuba-b .focused.entry {\nbackground: rgba(255, 255, 255, .33);\n}\n\n/* Watcher Favicon */\n:root.yotsuba-b .watch-thread-link\n{\nbackground-image: url(\"data:image/svg+xml,<svg viewBox='0 0 26 26' preserveAspectRatio='true' xmlns='http://www.w3.org/2000/svg'><path fill='rgb(0,0,0)' d='M24.132,7.971c-2.203-2.205-5.916-2.098-8.25,0.235L15.5,8.588l-0.382-0.382c-2.334-2.333-6.047-2.44-8.25-0.235c-2.204,2.203-2.098,5.916,0.235,8.249l8.396,8.396l8.396-8.396C26.229,13.887,26.336,10.174,24.132,7.971z'/></svg>\");\n}\n\n/* General */\n:root.futaba .dialog {\nbackground-color: #F0E0D6;\nborder-color: #D9BFB7;\n}\n:root.futaba .field:focus {\nborder-color: #EA8;\n}\n\n/* Header */\n:root.futaba #header-bar, :root.futaba #notifications {\nfont-size: 11pt;\ncolor: #B86;\n}\n:root.futaba #header-bar a, :root.futaba #notifications a {\ncolor: #800000;\n}\n\n/* Settings */\n:root.futaba #fourchanx-settings fieldset {\nborder-color: #D9BFB7;\n}\n\n/* Quote */\n:root.futaba .backlink.deadlink {\ncolor: #00E !important;\n}\n:root.futaba .inline {\nborder-color: #D9BFB7;\nbackground-color: rgba(255, 255, 255, .14);\n}\n\n/* QR */\n.futaba #dump-list::-webkit-scrollbar-thumb {\nbackground-color: #F0E0D6;\nborder-color: #D9BFB7;\n}\n:root.futaba .qr-preview {\nbackground-color: rgba(0, 0, 0, .15);\n}\n\n/* Menu */\n:root.futaba #menu {\ncolor: #800000;\n}\n:root.futaba .entry {\nborder-bottom: 1px solid #D9BFB7;\nfont-size: 12pt;\n}\n:root.futaba .focused.entry {\nbackground: rgba(255, 255, 255, .33);\n}\n\n/* Watcher Favicon */\n:root.futaba .watch-thread-link\n{\nbackground-image: url(\"data:image/svg+xml,<svg viewBox='0 0 26 26' preserveAspectRatio='true' xmlns='http://www.w3.org/2000/svg'><path fill='rgb(128,0,0)' d='M24.132,7.971c-2.203-2.205-5.916-2.098-8.25,0.235L15.5,8.588l-0.382-0.382c-2.334-2.333-6.047-2.44-8.25-0.235c-2.204,2.203-2.098,5.916,0.235,8.249l8.396,8.396l8.396-8.396C26.229,13.887,26.336,10.174,24.132,7.971z'/></svg>\");\n}\n\n/* General */\n:root.burichan .dialog {\nbackground-color: #D6DAF0;\nborder-color: #B7C5D9;\n}\n:root.burichan .field:focus {\nborder-color: #98E;\n}\n\n/* Header */\n:root.burichan #header-bar, :root.burichan #header-bar #notifications {\nfont-size: 11pt;\ncolor: #89A;\n}\n:root.burichan #header-bar a, :root.burichan #header-bar #notifications a {\ncolor: #34345C;\n}\n\n/* Settings */\n:root.burichan #fourchanx-settings fieldset {\nborder-color: #B7C5D9;\n}\n\n/* Quote */\n:root.burichan .backlink.deadlink {\ncolor: #34345C !important;\n}\n:root.burichan .inline {\nborder-color: #B7C5D9;\nbackground-color: rgba(255, 255, 255, .14);\n}\n\n/* QR */\n.burichan #dump-list::-webkit-scrollbar-thumb {\nbackground-color: #D6DAF0;\nborder-color: #B7C5D9;\n}\n:root.burichan .qr-preview {\nbackground-color: rgba(0, 0, 0, .15);\n}\n\n/* Menu */\n:root.burichan #menu {\ncolor: #000000;\n}\n:root.burichan .entry {\nborder-bottom: 1px solid #B7C5D9;\nfont-size: 12pt;\n}\n:root.burichan .focused.entry {\nbackground: rgba(255, 255, 255, .33);\n}\n\n/* Watcher Favicon */\n:root.burichan .watch-thread-link\n{\nbackground-image: url(\"data:image/svg+xml,<svg viewBox='0 0 26 26' preserveAspectRatio='true' xmlns='http://www.w3.org/2000/svg'><path fill='rgb(0,0,0)' d='M24.132,7.971c-2.203-2.205-5.916-2.098-8.25,0.235L15.5,8.588l-0.382-0.382c-2.334-2.333-6.047-2.44-8.25-0.235c-2.204,2.203-2.098,5.916,0.235,8.249l8.396,8.396l8.396-8.396C26.229,13.887,26.336,10.174,24.132,7.971z'/></svg>\");\n}\n\n/* General */\n:root.tomorrow .dialog {\nbackground-color: #282A2E;\nborder-color: #111;\n}\n\n/* Header */\n:root.tomorrow #header-bar, :root.tomorrow #notifications {\nfont-size: 9pt;\ncolor: #C5C8C6;\n}\n:root.tomorrow #header-bar a, :root.tomorrow #notifications a {\ncolor: #81A2BE;\n}\n\n/* Settings */\n:root.tomorrow #fourchanx-settings fieldset {\nborder-color: #111;\n}\n\n/* Quote */\n:root.tomorrow .backlink.deadlink {\ncolor: #81A2BE !important;\n}\n:root.tomorrow .inline {\nborder-color: #111;\nbackground-color: rgba(0, 0, 0, .14);\n}\n\n/* QR */\n.tomorrow #dump-list::-webkit-scrollbar-thumb {\nbackground-color: #282A2E;\nborder-color: #111;\n}\n:root.tomorrow .qr-preview {\nbackground-color: rgba(255, 255, 255, .15);\n}\n:root.tomorrow #qr .field {\nbackground-color: rgb(26, 27, 29);\ncolor: rgb(197,200,198);\nborder-color: rgb(40, 41, 42);\n}\n:root.tomorrow #qr .field:focus {\nborder-color: rgb(129, 162, 190) !important;\nbackground-color: rgb(30,32,36);\n}\n\n/* Menu */\n:root.tomorrow #menu {\ncolor: #C5C8C6;\n}\n:root.tomorrow .entry {\nborder-bottom: 1px solid #111;\nfont-size: 10pt;\n}\n:root.tomorrow .focused.entry {\nbackground: rgba(0, 0, 0, .33);\n}\n\n/* Watcher Favicon */\n:root.tomorrow .watch-thread-link\n{\nbackground-image: url(\"data:image/svg+xml,<svg viewBox='0 0 26 26' preserveAspectRatio='true' xmlns='http://www.w3.org/2000/svg'><path fill='rgb(197,200,198)' d='M24.132,7.971c-2.203-2.205-5.916-2.098-8.25,0.235L15.5,8.588l-0.382-0.382c-2.334-2.333-6.047-2.44-8.25-0.235c-2.204,2.203-2.098,5.916,0.235,8.249l8.396,8.396l8.396-8.396C26.229,13.887,26.336,10.174,24.132,7.971z'/></svg>\");\n}\n\n/* General */\n:root.photon .dialog {\nbackground-color: #DDD;\nborder-color: #CCC;\n}\n:root.photon .field:focus {\nborder-color: #EA8;\n}\n\n/* Header */\n:root.photon #header-bar, :root.photon #notifications {\nfont-size: 9pt;\ncolor: #333;\n}\n:root.photon #header-bar a, :root.photon #notifications a {\ncolor: #FF6600;\n}\n\n/* Settings */\n:root.photon #fourchanx-settings fieldset {\nborder-color: #CCC;\n}\n\n/* Quote */\n:root.photon .backlink.deadlink {\ncolor: #F60 !important;\n}\n:root.photon .inline {\nborder-color: #CCC;\nbackground-color: rgba(255, 255, 255, .14);\n}\n\n/* QR */\n.photon #dump-list::-webkit-scrollbar-thumb {\nbackground-color: #DDD;\nborder-color: #CCC;\n}\n:root.photon .qr-preview {\nbackground-color: rgba(0, 0, 0, .15);\n}\n\n/* Menu */\n:root.photon #menu {\ncolor: #333;\n}\n:root.photon .entry {\nborder-bottom: 1px solid #CCC;\nfont-size: 10pt;\n}\n:root.photon .focused.entry {\nbackground: rgba(255, 255, 255, .33);\n}\n\n/* Watcher Favicon */\n:root.photon .watch-thread-link\n{\nbackground-image: url(\"data:image/svg+xml,<svg viewBox='0 0 26 26' preserveAspectRatio='true' xmlns='http://www.w3.org/2000/svg'><path fill='rgb(51,51,51)' d='M24.132,7.971c-2.203-2.205-5.916-2.098-8.25,0.235L15.5,8.588l-0.382-0.382c-2.334-2.333-6.047-2.44-8.25-0.235c-2.204,2.203-2.098,5.916,0.235,8.249l8.396,8.396l8.396-8.396C26.229,13.887,26.336,10.174,24.132,7.971z'/></svg>\");\n}\n" + css: "/* General */\n.dialog {\nbox-shadow: 0 1px 2px rgba(0, 0, 0, .15);\nborder: 1px solid;\ndisplay: block;\npadding: 0;\n}\n.captcha-img,\n.field {\nbackground-color: #FFF;\nborder: 1px solid #CCC;\n-moz-box-sizing: border-box;\nbox-sizing: border-box;\ncolor: #333;\nfont: 13px sans-serif;\noutline: none;\ntransition: color .25s, border-color .25s;\ntransition: color .25s, border-color .25s;\n}\n.field::-moz-placeholder,\n.field:hover::-moz-placeholder {\ncolor: #AAA !important;\nfont-size: 13px !important;\nopacity: 1.0 !important;\n}\n.captch-img:hover,\n.field:hover {\nborder-color: #999;\n}\n.field:hover, .field:focus {\ncolor: #000;\n}\n.field[disabled] {\nbackground-color: #F2F2F2;\ncolor: #888;\n}\n.move {\ncursor: move;\noverflow: hidden;\n}\nlabel, .favicon {\ncursor: pointer;\n}\na[href=\"javascript:;\"] {\ntext-decoration: none;\n}\n.warning {\ncolor: red;\n}\n#boardNavDesktop {\ndisplay: none !important;\n}\na {\noutline: none !important;\n}\n\n/* 4chan style fixes */\n.opContainer, .op {\ndisplay: block !important;\noverflow: visible !important;\n}\n[hidden] {\ndisplay: none !important;\n}\n\n/* fixed, z-index */\n#overlay,\n#fourchanx-settings,\n#qp, #ihover,\n#navlinks, .fixed #header-bar,\n:root.float #updater,\n:root.float #thread-stats,\n#qr {\nposition: fixed;\n}\n#fourchanx-settings {\nz-index: 999;\n}\n#overlay {\nz-index: 900;\n}\n#notifications {\nz-index: 70;\n}\n#qp, #ihover {\nz-index: 60;\n}\n#menu {\nz-index: 50;\n}\n#navlinks, #updater, #thread-stats {\nz-index: 40;\n}\n.fixed #header-bar.autohide {\nz-index: 35;\n}\n#qr {\nz-index: 30;\n}\n#watcher {\nz-index: 8;\n}\n:root.fixed-watcher #watcher {\nz-index: 20;\n}\n.fixed #header-bar {\nz-index: 10;\n}\n/* Header */\n.fixed.top body {\npadding-top: 2em;\n}\n.fixed.bottom body {\npadding-bottom: 2em;\n}\n.fixed #header-bar {\nright: 0;\nleft: 0;\npadding: 3px 4px 4px;\n}\n.fixed.top #header-bar {\ntop: 0;\n}\n.fixed.bottom #header-bar {\nbottom: 0;\n}\n#header-bar {\nborder-width: 0;\ntransition: all .1s .05s ease-in-out;\n}\n:root.centered-links #shortcuts {\nwidth: 300px;\ntext-align: right;\n}\n:root.centered-links #header-bar {\ntext-align: center;\n}\n:root.centered-links #custom-board-list {\nposition: relative;\nleft: 150px;\n}\n.fixed.top #header-bar {\nborder-bottom-width: 1px;\n}\n.fixed.bottom #header-bar {\nbox-shadow: 0 -1px 2px rgba(0, 0, 0, .15);\nborder-top-width: 1px;\n}\n.fixed.bottom #header-bar .menu-button i {\nborder-top: none;\nborder-bottom: 6px solid;\n}\n#board-list {\ntext-align: center;\n}\n.fixed #header-bar.autohide:not(:hover) {\nbox-shadow: none;\ntransition: all .8s .6s cubic-bezier(.55, .055, .675, .19);\n}\n.fixed.top #header-bar.autohide:not(:hover) {\nmargin-bottom: -1em;\n-webkit-transform: translateY(-100%);\ntransform: translateY(-100%);\n}\n.fixed.bottom #header-bar.autohide:not(:hover) {\n-webkit-transform: translateY(100%);\ntransform: translateY(100%);\n}\n#scroll-marker {\nleft: 0;\nright: 0;\nheight: 10px;\nposition: absolute;\n}\n:root:not(.autohide) #scroll-marker {\npointer-events: none;\n}\n#header-bar #scroll-marker {\ndisplay: none;\n}\n.fixed #header-bar #scroll-marker {\ndisplay: block;\n}\n.fixed.top #header-bar #scroll-marker {\ntop: 100%;\n}\n.fixed.bottom #header-bar #scroll-marker {\nbottom: 100%;\n}\n#header-bar a:not(.entry):not(.close) {\ntext-decoration: none;\npadding: 1px;\n}\n#header-bar input {\nmargin: 0;\nvertical-align: bottom;\n}\n#shortcuts:empty {\ndisplay: none;\n}\n.brackets-wrap::before {\ncontent: \"\\00a0[\";\n}\n.brackets-wrap::after {\ncontent: \"]\\00a0\";\n}\n.disabled,\n.expand-all-shortcut {\nopacity: .45;\n}\n#shortcuts {\nfloat: right;\n}\n.shortcut {\nmargin-left: 3px;\n}\n#navbotright,\n#navtopright {\ndisplay: none;\n}\n#toggleMsgBtn {\ndisplay: none !important;\n}\n.current {\nfont-weight: bold;\n}\n/* 4chan X link brackets */\n.fourchanx-link::after {\ncontent: \"]\";\n}\n.fourchanx-link::before {\ncontent: \"[\";\n}\n/* Notifications */\n#notifications {\nposition: fixed;\ntop: 0;\nheight: 0;\ntext-align: center;\nright: 0;\nleft: 0;\ntransition: all .8s .6s cubic-bezier(.55, .055, .675, .19);\n}\n.fixed.top #header-bar #notifications {\nposition: absolute;\ntop: 100%;\n}\n.notification {\ncolor: #FFF;\nfont-weight: 700;\ntext-shadow: 0 1px 2px rgba(0, 0, 0, .5);\nbox-shadow: 0 1px 2px rgba(0, 0, 0, .15);\nborder-radius: 2px;\nmargin: 1px auto;\nwidth: 500px;\nmax-width: 100%;\nposition: relative;\ntransition: all .25s ease-in-out;\n}\n.notification.error {\nbackground-color: hsla(0, 100%, 38%, .9);\n}\n.notification.warning {\nbackground-color: hsla(36, 100%, 38%, .9);\n}\n.notification.info {\nbackground-color: hsla(200, 100%, 38%, .9);\n}\n.notification.success {\nbackground-color: hsla(104, 100%, 38%, .9);\n}\n.notification a {\ncolor: white;\n}\n.notification > .close {\npadding: 6px;\ntop: 0;\nright: 5px;\nposition: absolute;\n}\n.message {\n-moz-box-sizing: border-box;\nbox-sizing: border-box;\npadding: 6px 20px;\nmax-height: 200px;\nwidth: 100%;\noverflow: auto;\n}\n\n/* Settings */\n:root.fourchan-x body {\n-moz-box-sizing: border-box;\nbox-sizing: border-box;\n}\n#overlay {\nbackground-color: rgba(0, 0, 0, .5);\ntop: 0;\nleft: 0;\nheight: 100%;\nwidth: 100%;\n}\n#fourchanx-settings {\n-moz-box-sizing: border-box;\nbox-sizing: border-box;\nbox-shadow: 0 0 15px rgba(0, 0, 0, .15);\nheight: 600px;\nmax-height: 100%;\nwidth: 900px;\nmax-width: 100%;\nmargin: auto;\npadding: 3px;\ntop: 50%;\nleft: 50%;\n-moz-transform: translate(-50%, -50%);\n-webkit-transform: translate(-50%, -50%);\ntransform: translate(-50%, -50%);\n}\n#fourchanx-settings > nav {\npadding: 2px 2px 0;\nheight: 15px;\n}\n#fourchanx-settings > nav a {\ntext-decoration: underline;\n}\n#fourchanx-settings > nav a.close {\ntext-decoration: none;\npadding: 2px;\n}\n.section-container {\noverflow: auto;\nposition: absolute;\ntop: 2.1em;\nright: 5px;\nbottom: 5px;\nleft: 5px;\npadding-right: 5px;\n}\n.sections-list {\npadding: 0 3px;\nfloat: left;\n}\n.credits {\nfloat: right;\n}\n.tab-selected {\nfont-weight: 700;\n}\n.section-sauce ul,\n.section-advanced ul {\nlist-style: none;\nmargin: 0;\n}\n.section-sauce ul {\npadding: 8px;\n}\n.section-advanced ul {\npadding: 0px;\n}\n.section-sauce li,\n.section-advanced li {\npadding-left: 4px;\n}\n.section-main label {\ntext-decoration: underline;\n}\n.section-filter ul {\npadding: 0;\n}\n.section-filter li {\nmargin: 10px 40px;\n}\n.section-filter textarea {\nheight: 500px;\n}\n.section-sauce textarea {\nheight: 350px;\n}\n.section-advanced .field[name=\"boardnav\"] {\nwidth: 100%;\n}\n.section-advanced textarea {\nheight: 150px;\n}\n.section-advanced .archive-cell {\nmin-width: 160px;\ntext-align: center;\n}\n.section-advanced #archive-board-select {\nposition: absolute;\n}\n.section-advanced .note {\nfont-size: 0.8em;\nfont-style: italic;\nmargin-left: 10px;\n}\n.section-advanced .note code {\nfont-style: normal;\nfont-size: 11px;\n}\n.section-keybinds .field {\nfont-family: monospace;\n} \n#fourchanx-settings fieldset {\nborder: 1px solid;\nborder-radius: 3px;\n}\n#fourchanx-settings legend {\nfont-weight: 700;\n}\n#fourchanx-settings textarea {\nfont-family: monospace;\nmin-width: 100%;\nmax-width: 100%;\n}\n#fourchanx-settings code {\ncolor: #000;\nbackground-color: #FFF;\npadding: 0 2px;\n}\n.unscroll {\noverflow: hidden;\n}\n\n/* Announcement Hiding */\n:root.hide-announcement #globalMessage {\ndisplay: none;\n}\na.hide-announcement {\nfloat: left;\n}\n\n/* Unread */\n#unread-line {\nmargin: 0;\nborder-color: rgb(255,0,0);\n}\n\n/* Thread Updater */\n#updater {\nbackground: none;\nborder: none;\nbox-shadow: none;\n}\n#updater > .move {\npadding: 5px 3px 0px;\nmargin-bottom: -3px;\n}\n#updater > div:last-child {\ntext-align: center;\n}\n#updater input[type=number] {\nwidth: 4em;\n}\n:root.float #updater {\npadding: 0px 3px;\n}\n.new {\ncolor: limegreen;\n}\n#update-status.new {\nmargin-right: 5px;\n}\n#update-timer {\ncursor: pointer;\n}\n\n/* Thread Watcher */\n#watcher {\nposition: absolute;\n}\n#watcher {\npadding-bottom: 3px;\noverflow: hidden;\nwhite-space: nowrap;\nmin-width: 120px;\nmax-height: 92%;\noverflow-y: auto;\n}\n:root.fixed-watcher #watcher {\nposition: fixed;\n}\n:root:not(.fixed-watcher) #watcher:not(:hover) {\nmax-height: 210px;\noverflow-y: hidden;\n}\n#watcher > .move {\npadding-top: 3px;\n}\n#watcher > div {\nmax-width: 250px;\noverflow: hidden;\npadding-left: 3px;\npadding-right: 3px;\ntext-overflow: ellipsis;\n}\n#watcher a {\ntext-decoration: none;\n}\n#watcher .move>.close {\nposition: absolute;\nright: 0px;\ntop: 0px;\npadding: 0px 4px;\n}\n.watch-thread-link {\npadding-top: 18px;\nwidth: 18px;\nheight: 0px;\ndisplay: inline-block;\nbackground-repeat: no-repeat;\nopacity: 0.2;\nposition: relative;\ntop: 1px;\n}\n.watch-thread-link.watched {\nopacity: 1;\n}\n\n/* Thread Stats */\n#thread-stats {\nbackground: none;\nborder: none;\nbox-shadow: none;\n}\n:root.float #post-count, :root.float #file-count {\npointer-events: none;\n}\n:root.float #thread-stats {\npadding: 0px 3px;\n}\n\n/* Quote */\n.deadlink {\ntext-decoration: none !important;\n}\n.backlink.deadlink:not(.forwardlink),\n.quotelink.deadlink:not(.forwardlink) {\ntext-decoration: underline !important;\n}\n.inlined {\nopacity: .5;\n}\n#qp input, .forwarded {\ndisplay: none;\n}\n.quotelink.forwardlink,\n.backlink.forwardlink {\ntext-decoration: none;\nborder-bottom: 1px dashed;\n}\n.filtered {\ntext-decoration: underline line-through;\n}\n:root.hide-backlinks .backlink.filtered {\ndisplay: none;\n}\n.inline {\nborder: 1px solid;\ndisplay: table;\nmargin: 2px 0;\n}\n.inline .post {\nborder: 0 !important;\nbackground-color: transparent !important;\ndisplay: table !important;\nmargin: 0 !important;\npadding: 1px 2px !important;\n}\n#qp > .opContainer::after {\ncontent: '';\nclear: both;\ndisplay: table;\n}\n#qp .post {\nborder: none;\nmargin: 0;\npadding: 2px 2px 5px;\n}\n#qp img {\nmax-height: 80vh;\nmax-width: 50vw;\n}\n.qphl {\noutline: 2px solid rgba(216, 94, 49, .7);\n}\n:root.highlight-own .yourPost > .reply,\n:root.highlight-you .quotesYou > .reply {\nborder-left: 2px solid rgba(221,0,0,.5);\n}\n/* Quote Threading */\n.threadContainer {\nmargin-left: 20px;\nborder-left: 1px solid rgba(128,128,128,.3);\n}\n.threadOP {\nclear: both;\n} \n\n/* File */\n.fileText:hover .fntrunc,\n.fileText:not(:hover) .fnfull,\n.expanded-image > .post > .file > .fileThumb > img[data-md5],\n:not(.expanded-image) > .post > .file > .fileThumb > .full-image {\ndisplay: none;\n}\n.expanding {\nopacity: .5;\n}\n:root.fit-height .full-image {\nmax-height: 100vh;\n}\n:root.fit-width .full-image {\nmax-width: 100%;\n}\n:root.gecko.fit-width .full-image {\nwidth: 100%;\n}\n#ihover {\n-moz-box-sizing: border-box;\nbox-sizing: border-box;\nmax-height: 100%;\nmax-width: 75%;\npadding-bottom: 16px;\n}\n.fappeTyme .thread > .noFile,\n.fappeTyme .threadContainer > .noFile {\ndisplay: none;\n}\n\n/* Index/Reply Navigation */\n#navlinks {\nfont-size: 16px;\ntop: 25px;\nright: 10px;\n}\n\n/* Filter */\n.opContainer.filter-highlight {\nbox-shadow: inset 5px 0 rgba(255, 0, 0, .5);\n}\n.filter-highlight > .reply {\nbox-shadow: -5px 0 rgba(255, 0, 0, .5);\n}\n\n/* Spoiler text */\n:root.reveal-spoilers s {\ncolor: white !important;\n}\n\n/* Thread & Reply Hiding */\n.hide-thread-button,\n.hide-reply-button {\nfloat: left;\nmargin-right: 2px;\n}\n.stub ~ * {\ndisplay: none !important;\n}\n.stub input {\ndisplay: inline-block;\n}\n\n/* QR */\n:root.hide-original-post-form #postForm,\n:root.hide-original-post-form .postingMode,\n:root.hide-original-post-form #togglePostForm,\n#qr.autohide:not(.has-focus):not(:hover) > form,\n.postingMode ~ #qr select,\n#file-n-submit:not(.has-file) #qr-filerm {\ndisplay: none;\n}\n#qr select,\n#dump-button,\n.remove,\n.captcha-img {\ncursor: pointer;\n}\n#qr {\nz-index: 20;\nposition: fixed;\npadding: 1px;\nborder: 1px solid transparent;\nmin-width: 300px;\nborder-radius: 3px 3px 0 0;\n}\n#qrtab {\nborder-radius: 3px 3px 0 0;\n}\n#qrtab {\nmargin-bottom: 1px;\n}\n#qr .close {\nfloat: right;\npadding: 0 3px;\n}\n#qr .warning {\nmin-height: 1.6em;\nvertical-align: middle;\npadding: 0 1px;\nborder-width: 1px;\nborder-style: solid;\n}\n.qr-link-container {\ntext-align: center;\n}\n.persona {\nwidth: 248px;\nmax-width: 100%;\nmin-width: 100%;\n}\n#dump-button {\nwidth: 10%;\nmargin: 0;\nmargin-right: 4px;\nfont: 13px sans-serif;\npadding: 1px 0px 2px;\nopacity: 0.6;\n}\n.persona .field:not(#dump) {\nwidth: 95px;\nmin-width: 33.3%;\nmax-width: 33.3%;\n}\n#qr textarea.field {\nheight: 14.8em;\nmin-height: 9em;\n}\n#qr.has-captcha textarea.field {\nheight: 9em;\n}\ninput.field.tripped:not(:hover):not(:focus) {\ncolor: transparent !important;\ntext-shadow: none !important;\n}\n#qr textarea {\nresize: both;\n}\n.captcha-img {\nmargin: 0px;\ntext-align: center;\nbackground-image: #fff;\nfont-size: 0px;\nmin-height: 59px;\nmin-width: 302px;\n}\n.captcha-input {\nwidth: 100%;\nmargin: 1px 0 0;\n}\n.captcha-input.error:focus {\nborder-color: rgb(255,0,0) !important;\n}\n.field {\n-moz-box-sizing: border-box;\nmargin: 0px;\npadding: 2px 4px 3px;\n}\n#qr textarea {\nmin-width: 100%;\n}\n#qr [type='submit'] {\nwidth: 25%;\nvertical-align: top;\n}\n:root.webkit #qr [type='submit'] {\nheight: 24px;\n}\n/* Fake File Input */\n#qr-filename,\n.has-file #qr-no-file {\ndisplay: none;\n}\n#qr-no-file,\n.has-file #qr-filename {\ndisplay: inline-block;\npadding: 0px 4px;\nmargin-bottom: 2px;\noverflow: hidden;\ntext-overflow: ellipsis;\nmax-width: 88%;\n}\n#qr-no-file {\ncolor: #AAA;\n}\n#qr-filename-container {\n-moz-box-sizing: border-box;\ndisplay: inline-block;\nposition: relative;\nwidth: 100px;\nmin-width: 74.6%;\nmax-width: 74.6%;\nmargin-right: 0.4%;\nmargin-top: 1px;\noverflow: hidden;\npadding: 2px 1px 0;\nheight: 22px;\n}\n#qr-filename-container:hover {\ncursor: text;\n}\n#qr-extras-container {\nposition: absolute;\nright: 0px;\n}\n#qr-filerm {\nmargin-right: 2px;\nz-index: 2;\n}\n#file-n-submit {\nheight: 23px;\n}\n#qr input[type=file] {\nvisibility: hidden;\nposition: absolute;\n}\n/* Thread Select / Spoiler Label */\n#qr select {\nfloat: right;\n}\n#qr.has-spoiler .has-file #qr-spoiler-label {\nwidth: 6.7%;\nmin-width: 6.7%;\nmax-width: 6.7%;\ndisplay: inline-block;\ntext-align: center;\nvertical-align: top;\n}\n#qr.has-spoiler #file-n-submit:not(.has-file) #qr-spoiler-label {\ndisplay: none;\n}\n#qr.has-spoiler .has-file #qr-filename-container {\nmax-width: 67.9%;\nmin-width: 67.9%;\n}\n#qr-spoiler-label input {\nposition: relative;\ntop: 3px;\n}\n/* Dumping UI */\n.dump #dump-list-container {\ndisplay: block;\n}\n#dump-list-container {\ndisplay: none;\nposition: relative;\noverflow-y: hidden;\nmargin-top: 1px;\n}\n#dump-list {\noverflow-x: auto;\noverflow-y: hidden;\nwhite-space: nowrap;\nwidth: 248px;\nmax-width: 100%;\nmin-width: 100%;\n}\n#dump-list:hover {\noverflow-x: auto;\n}\n.qr-preview {\n-moz-box-sizing: border-box;\ncounter-increment: thumbnails;\ncursor: move;\ndisplay: inline-block;\nheight: 90px;\nwidth: 90px;\npadding: 2px;\nopacity: .5;\noverflow: hidden;\nposition: relative;\ntext-shadow: 0 1px 1px #000;\n-moz-transition: opacity .25s ease-in-out;\nvertical-align: top;\nbackground-size: cover;\n}\n.qr-preview:hover,\n.qr-preview:focus {\nopacity: .9;\n}\n.qr-preview::before {\ncontent: counter(thumbnails);\ncolor: #fff;\nposition: absolute;\ntop: 3px;\nright: 3px;\ntext-shadow: 0 0 3px #000, 0 0 8px #000;\n}\n.qr-preview#selected {\nopacity: 1;\n}\n.qr-preview.drag {\nbox-shadow: 0 0 10px rgba(0,0,0,.5);\n}\n.qr-preview.over {\nborder-color: #fff;\n}\n.qr-preview > span {\ncolor: #fff;\n}\n.remove {\nbackground: none;\ncolor: #e00;\nfont-weight: 700;\npadding: 3px;\n}\na:only-of-type > .remove {\ndisplay: none;\n}\n.remove:hover::after {\ncontent: \" Remove\";\n}\n.qr-preview > label {\nbackground: rgba(0,0,0,.5);\ncolor: #fff;\nright: 0;\nbottom: 0;\nleft: 0;\nposition: absolute;\ntext-align: center;\n}\n.qr-preview > label > input {\nmargin: 0;\n}\n#add-post {\ncursor: pointer;\nfont-size: 2em;\nposition: absolute;\ntop: 50%;\nright: 10px;\n-moz-transform: translateY(-50%);\n}\n.textarea {\nposition: relative;\n}\n:root.webkit .textarea {\nmargin-bottom: -2px;\n}\n#char-count {\ncolor: #000;\nbackground: hsla(0, 0%, 100%, .5);\nfont-size: 8pt;\nposition: absolute;\nbottom: 1px;\nright: 1px;\npointer-events: none;\n}\n\n/* Menu */\n.menu-button {\ndisplay: inline-block;\nposition: relative;\ncursor: pointer;\n}\n.menu-button i {\nborder-top: 6px solid;\nborder-right: 4px solid transparent;\nborder-left: 4px solid transparent;\ndisplay: inline-block;\nmargin: 2px;\nvertical-align: middle;\n}\n#menu {\nposition: fixed;\noutline: none;\n}\n.entry {\nborder-bottom: 1px solid rgba(0,0,0,.25);\ncursor: pointer;\ndisplay: block;\noutline: none;\npadding: 3px 7px;\nposition: relative;\ntext-decoration: none;\nwhite-space: nowrap;\n}\n.left>.entry.has-submenu {\npadding-right: 17px !important;\n}\n.entry:last-child {\nborder-bottom: 0;\n}\n.has-submenu::after {\ncontent: \"\";\nborder-left: .5em solid;\nborder-top: .3em solid transparent;\nborder-bottom: .3em solid transparent;\ndisplay: inline-block;\nmargin: .3em;\nposition: absolute;\nright: 3px;\n}\n.left .has-submenu::after {\nborder-left: 0;\nborder-right: .5em solid;\n}\n.submenu {\ndisplay: none;\nposition: absolute;\nleft: 100%;\ntop: -1px;\n}\n.focused .submenu {\ndisplay: block;\n}\n.imp-exp-result {\nposition: absolute;\ntext-align: center;\nmargin: auto;\nright: 0px;\nleft: 0px;\nwidth: 200px;\n}\n.export, .import {\ncursor: pointer;\ntext-decoration: none !important;\n}\n/* Link Title Favicons */\n.linkify.YouTube {\nbackground: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAMCAYAAABr5z2BAAABIklEQVQoz53LvUrDUBjG8bOoOammSf1IoBSvoCB4JeIqOHgBLt6AIMRBBQelWurQ2kERnMRBsBUcIp5FJSBI5oQsJVkkUHh8W0o5nhaFHvjBgef/Mq+Q46RJBMkI/vE+aOus956tnEswIZe1LV0QyJ5sE2GzgZfVMtRNIdiDpccEssdlB1mW4bvTwdvWJtRdErM7U+8S/FJykCRJX5qm+KpVce8UMNLRLbulz4iSjTAMh6Iowsd5BeNadp3nUF0VlxAEwZBotXC0Usa4ll3meZdA1iguwvf9vpvDA2wvmKgYGtSud8suDB4TyGr2PF49D/vra9jRZ1BVdknMzgwuCGSnZEObwu6sBnVTCHZiaC7BhFx2PKdxUidiAH/4lLo9Mv0DELVs9qsOHXwAAAAASUVORK5CYII=') center left no-repeat!important;\npadding-left: 18px;\n}\n.linkify.Vimeo {\nbackground: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAASJJREFUOE9jYAAC7ln7/pODQXrBmq333PvPu/YaSRikB6QXbACpmmHqsRoAMll7+20UQ0H8tmuv/pdffPFfZtNNuByGASBFIPDh5x+4IV6HHoDFYGDJgw+YBoBMBUkgA5BtIKduuvvy//svX+FSB+88wTTAc+/t/83bj/0HScLA5BPXwc7lKJ36f+L6XXDxhUfOYxrAPWUnWKFp9UQUm3iWQxSDXAEDSX3zcIcB96wD/x+8eA1XDNKMHAYg20GW4Y0FkCIYAAUqzEBQOIBciRzlWKMxZelOlMCEcVxq+jHSC1YDJPs3YBgA8jey0/F6ARRwsFAHORukmat9NdbUijMpg/wKcrJodDFOzSBXwA3Alh9AToZFI7a8Asu98BxJbnYGAJb5vYLDANzSAAAAAElFTkSuQmCC') center left no-repeat!important;\npadding-left: 18px;\n}\n.linkify.SoundCloud {\nbackground: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABsklEQVQ4y5WTy2pUQRCGv2rbzDjJeAlIBmOyipGIIJqFEBDElwh4yULGeRFXPoEIBl/AvQ/gC2RnxCAoxijiwks852S6+3dxzslcHJCpTXVX11/Xv0097gLPgVNMJxnQNfX4zsqleWbnpoMf/oa9d988MM9MC/rp+E0a+A0dsVobMNMCOO8B6McRoABJI+A6gJmN3D2A8jgEBCEkSEMBrcrsDAzDWWn3AjgKFaDMmgRqniGFgsaDp1jrLOngDf1XT1D+A1dFc4MKAkkiCVKjjVu7g9+4Rzx4i1u6hjXbuMWr0O5QPNvCu7IaCZwEKQukLGDrm5x8uI0tr6MkiGlkiv7yLfzN+6S5i6QsIMABkEfcxhbWWYMkVAOjxvYAjc3HNHrbKI9VBQBFwF25XQKSBjqIf1YBuAurEMrczgDygD6/x2LCpFLXLUyQ+PoldphhBhYfIX09XU1+Flaukz7uYqs3SHs7cG4BmTsmkBUF9mmXEwa28BNLPaQPLepuNcbGSWQquQC2/Kdcox1FUGkcB0ykck1nA2+wTzMs8stGnP4rbWGw74EuS/GFQWfK7/wF6P4F7fzIAYkdmdEAAAAASUVORK5CYII=') center left no-repeat!important;\npadding-left: 18px;\n}\n.linkify.audio {\nbackground: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAitJREFUOE9jYCAWKJWwavr0KyXWb/FIbDtUFFyzJx6nVofE2Xo5nXsj0rqPNSR0nVkR2Hjmgmfd+U9Otdf+m5Vf/6+SfeU/R9ChVVgNYDRtlfJuuPA/rPfe/4QpD/6nznj0P27Kw/9unff/69Xf+69c/+C/SO7N/0z+OAxgMmmRCe++/r9i3ev/KWvf/vdY8PK/bt/9/wrNV3/IN5y/IVt1YqNg4pGTTP4HsbuA2bhZ2qvpyn+xjIObxAp3VwqlrgngLFyryVy5nhPmZJHANS2cwYexG8BmVC/pWn3hP4NZlzWuQDJI3dIiFnUUuwEsQAOcq87jNcC7fHeLUtJxHF4AGmBWeAavAWH1+1rUUk7giAWjOknllON4DXAs2NEiG4/DBQxAF/CFHfrPYI4jDFSLuJVjNrUJhB/B7gIGo1pJRt99GAZYJK7wLJ1z7Xzl4vu/7aqv/GRBj0bjqAX2qb0nJ7mXH17C4HcUxQA+hymWtSue/C5a9up/9Ozn/7Vr7v1nRY7GqMb91T3b3v6vWvPmf/S0p/9ZQk+DDLCBRSOz06Jqk+o7/21nvfqvsebDf7kZL/5zBaxphkezd+OFn7HzXvz3Wvjmv9a8N//5Ek//ZTBpVYUrMG2X5wjcdl68+uI/wa5Lr3hSNjczGFeywOVZ/bbcVGp//F9izfv/Ql03f3P4LC/HSEQquYwMFnUCDJ7dzBhyjGZNQpye89M5gpfnMvtNUyE2h4PUAQBovvT7lyNljwAAAABJRU5ErkJggg==') center left no-repeat!important;\npadding-left: 18px;\n}\n.linkify.LiveLeak {\nbackground: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAydJREFUOE9Nk1tIk2EYx79NyUNqTk0o6KYrnZeChodLDxfeZpCbJk4RXU5Nm7tYRYhiYXbQlaeGutyW2gxtpB1RIyKDEjKwA6Ti2dR5KNDn+fq/S6TBj/f93r3P732e53s/qfnkSdej4GB2SBLbwf+jmB+gUMgOheLg/z7EdCUnO6Ref392SpK8Hyh3I+gBwBo7lUp2xcbyQEoKD6alyQOpqd754/h4FjJXZCRJTl9ftmEzoK5/wdQJxPgkLY2WV1dpc2uLtnZ2eHNnhza3t2nd46GhjAzuValY6jx0iIfS03msoIDuQ9COQCtoUSjohU5HuwgaN5loeXycd3d3aW9vzwvW2K5SkdTi58fvzGb+3tdHFggA3QONEAzn59PvjQ1yqNX0zenkvX0B4ffWaGRraChJd/385JGqKvlzTw/fRqOaIGkEd1DjU52O/3g83BkTw5MOh7yJuUCUM2o0yi2hoSw1IIOhykr+YLNRHYKu4XQvyKA/N5c8yMCCDD7Z7bz26xcJ1rH2rKKCG0UJdRAMlJbyG6uVrkJQjWAB5tSbk0Nr2HwDgvcQiIYur6zQyvo6ucvLueHIEZKuQPBQr+dXra1kRuqXEOwFArtWSytra1QdFUVjNhvPLS3R3OIiLUDUD0F1WBhJJtwDW2Ehu5uaqBICI4IFlRB0QLCEzaboaHrd0cHzCBYsIIuesjK+LAQXkEFrXh676uupGCWcR6AeghLQptGQONUAwfOuLp6Zn6eZuTmaXVig7pISrhI90ENgQbdHhoep32JhFzLpu3WLio8epUYIfs7OUjF6UKJW88XERLqYkEBNej11oG8XhCAvMFAuOn5cNiclsTkhQTbhmpri4lgbEMANWi1DwC/xit3t7bK7rY0Fo4OD3G4wyEURESzloAdnceezlErK8vH5N4KzPj50PTOTfkxP0+THj/RlYoInJyZI8HVqim5qNFwQHk7SucBAPo2PKRMNPLM/4pnFszYkhJsNBu6uqWFHba1sr61lQSveQFZQkFx07BhJmhMnrLn4NLMPH/aSExR0QDbmWhwgyEapwDvXoDxdWBiXnjrV/Bdm2kYUxLwmEgAAAABJRU5ErkJggg==') center left no-repeat!important;\npadding-left: 18px;\n}\n.linkify.Vocaroo {\nbackground: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAw9JREFUOE9jYMABuMwYmCyTJKUCGlSnFSy02TTzeOyCiQcDViX26qVz2TAyYtWmEMwuoZ3M7V40LcB79pHkc0svpvzY8jD//87nxf+3Pyn8v/ZO8v+VNyP/2mZJumI1QCWSI8232Hjumitlfw5+qPp/9l8TCt76JP//xkdx/wsXWCzjtWFkwTCkbWFe9plPk/+ga4Txz/xt/D/hkN//gMXif21a+NbyWjIwoRiy6GDT5rP/mlFsPfyp5n/NpOj/22+0gMUXXIz/H7hC/L/bFKFbPDZMrHAD5H35OPt2J9zacDv/f3V7xv9FhwrBGubsT/1//Pjx/1GJ/mD+/nfl/1v3Ovy3KRJNQbHdOlXCvOO03/+pm1P/v3v37n90hhtYw9HPtf8Xb2v937cmHswHeWPRxYj/LvkK3igGKARwicTO07118H3V/5kbi/4vPZMJtK3s/6YH2f+Pfq1B8VbjWrdnMu5s4nAD9CNFhKwz5DTUvLl419zKvAcLtG1P84BRl/b/5M/6/6f/NPzf/qzo84yj0Uus0xUU4Zor54bm9+4OfZG02OCuoAMTb9ZkC9ull1Nvrr2Z+XvRpaRfc65H/68F+jl9svEhzyLFWoccWVc+eyTHq/twydjlKRln7jX9bNMkMJnbhoFRL1xCqmKx6/yi2fYXa/c5/e846PV/5fW0/7OPx/yfcjzop34ulxdGGvDuU8mMXaX507lBuiN6ueadmQeT/p/93vf/1O+G//sP5fw/eL3o/5JLif8zVxs+Tlir9S26UyeFQQvJGBE7FvaFZ9LfN+1y+WjbItSb3GmXvXd15v8zroH/HxgE/D+aGPx/18vi/z07PeZNPRKxe/Kh0Ae8toxscCO4zBkYXArk9C1SxJUYjBkYPPIVtbbuTftz3cz//2O9wP/75iSAXdO72/dt2HL5F6YlfBW4MiJYXMiBiW3t7azHBx+V/t89N+H/8a+1//e9K/9attDp5LQjYX8SuvVL8RoAkmxa65299Erq1FnHo0qrl7t4BddriIs4MrM3rfWcFd+pGwVSAwBZ0bKP8yrZPAAAAABJRU5ErkJggg==') center left no-repeat!important;\npadding-left: 18px;\n}\n.linkify.pastebin {\nbackground: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAtZJREFUOE+NU91LWmEc7sJtQew/2MUY7INg7CLY3W5GMHazyzEQo9UmfYxZTbAiVlgRqLMSZ+XnDC3z2+Y0+8JGakKZTtR0Tl2wtgtLLQh29cz3ZZ3h3Q68vOc95zzP73l+z+/U1f292O09DRxubxOH23P//1bvtQts3dPnry7LZnXJhcUl5Avf8dHtwY+fv2AyW5DOfIXFakMm+w0G4wISyRRm55TQG0y/Wzv6mikJ52Xf9TmVBoFAAD6fDwqFAqFQCJubmzCbzZiensbp6SmkUikikQi0Wi0kEgm6ewVaStDCfXPDandifn6egoaGhrCzswO1Wg2Hw4HBwUGk02kIBAL4/X4IhUJMTk6ii8dfYggy2RwymQzOz88Rj8dRLpexv7+PSqWCYDCIQqGAra0tJBIJrK2t0XdVAjNDEIl+wfj4OEqlEq2wt7dHrchkMmrBYDCAz+fTIjweD7FYrJbgIJOlgLOzM8jlcip1eXmZ2rFarVAqlRCLxcjlchCJRFRljYJYPAG32418Pg+n04lsNouVlRUcHh7C4/FQIOlHNBqlezgcJgQWxkIgGMbExASVNjY2hvX1dVo9mUzS5wREFLhcLrqTcw2B//M2RkdHodPp4PV6oVKpqH+SCom3v7+fNnF4eJiJusbCJ6+PviSyScakiaR5RIHRaKQpmEwmbAdCeD8zB6vdhebHT8SMhcUlC83bbrdTJRsbG3RwiCVCRNJJpDIoVeNNJJJQzKryV+rrmxiCtyNCCmaz2VhdXQWXy6XDpNfrodFoYLXZUTw+pk222Z3lW3ca26rgSwzBwqIZAwMDlITMAVEwNTVFR5fEJpK8Qyp1AJvDVbrTeLenCmxgfiZ22+urCtWHyu7uLp2wVCpFKx0dHaFYLOLk5KT6Y9kgk89kb95ubK0BX7A8a+1qannRLeW0daj/rU51S3tn9dypfvDw0QiLxbpX/Z7FVK7e/AEj4Wf24/2f5AAAAABJRU5ErkJggg==') center left no-repeat!important;\npadding-left: 18px;\n}\n.linkify.gist {\nbackground: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAk1JREFUeNqUkzuIE1EUhv955MnsbB6r4kYQLUQQFncV3SnCIqJsoWGDYOGjsIiCtY2Kla1sjLBIsFFcXJC1kaSwENQmXUQSRSUSjCQSTCbkbR4z47lXEgtBNwcu3DNzvvO8R8jlcj7LshKmaWqYQERRTAmCcEru9/sJr9er0QF92BJMAVGr1TQ6CeZAc7lcGAwGkyQAxpTLZU0eDoc8crfbRTgcRjAYRCQSYSmi1WpxY7fbjU6ng1gshmaziXg8zhnGIpVKWbquW9ls1mLZsaMoiqWq6lgnBxY55He/328Vi0XOMFZmqVMD4fF4QBAajcY48khY9JE4HA4enTGMFVkaTHmy+ZzD/5NSqYSNB484w1h55ODO3TVu4FXcWDywl24Cmp0e1WBhyuWELAtIf/qKUrWOONmev3Lpt4NRCXq1gplpBS/v3cDc0nGg9h1o1ZkfwO4Atu1B8cM7HLt8k37V/y5B2b4bJxf2Y+7oEbyJrkMvUjki0YYJ03LidfQxAt4dOHdCw5RdGZcgGobBlQtnV/BDr1GfDai7ZiHZZRi9PoY/e5SCCTUwC9gk1GmMh5YWOcNYkR4Sv1y9uAJbYB82N57h4OnDmN7phjQ0qUkWRJuB+TMaPn/5iFfvv+Ha7eucYey4iWw8q6tRJJNJ3Fp7ClUawEkViBTfkCR0YUNTVHD/4Tpm/P4/U2CeKpUKfD4fJDIMhUKEhP45St50XedZyLQY6Xw+v8AUemVb2oNqtYpCocCWKi2TLLfb7ReZTGZ+kmUi7i2VvfxLgAEAZChMriPcl+IAAAAASUVORK5CYII=') center left no-repeat!important;\npadding-left: 18px;\n}\n.linkify.image {\nbackground: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAs5JREFUOE+lk/tvi1EYx98/xT8gW4REIpGFMEQWl2FiM9ZMZhm2xRAyOsmujFFmdFRHu0tWm87UypxStr69zPauN5e5rHVp3IYhbOvHy+wHEQlxkm+ek+d8nm9OznkeSfrfldmgJC7QyUlTymsJTfuTZ25z4HdWYwyLreYhtpgekGPw0+kKvo1Eo+IXRSIiEhkWZuc9tqnsJD9EqTUopCxjSGTpB0iueczSo1HyW8cpsExQ1DbxI2pt45j9cXpexul4FEd79RnZphAa/SD7WvuFtO6UItbU9LC+YQxNI2w0wwYT5LRAdhOU3oBTIXC9gXP3oUSGgz2vST3gYHejR0jptT1C332f8yrUEYHrz8CgxDnpm6DKCUfc0KnmXa/AEVPPwnDcD0cvetA2uYRk67Ive/lpjO7YBO1PPuF8Df3vwf4cbNE4tqdw7YVq8HYyHx6FvhE1hkMEg8HDUqvFkjT4aIjMqkqyqkswDSrcfBfH+Q561YLAZ/B+BLda6FXlU/cPv0AoEPhuoP1h4Av7Wbh9E/Py15NWWUjeSR3nZDfeN+N0DY9hG/7K1eGP3P0S5/EYRFUF/IOTBrUXHPm9fT6mr1xEwupkZqxbzLyiDJYUZ5NSnkdqdSHpxyrYdFpPgdmAsdfJwPMI/Yr65bf7tZLGGBQ7DNdJWFtIYvoOZmbuZE7OXpIKKli86zAr9p9gTVktWTVnKTI2U95uRWe3U2IJUDbVB5p6hVm5x5m9Vc/cnedZUNzC8lILaQesZBy6hEZ3maKzgvJWFzVWD9XtXvVGQbSWASFtMATVRlJIKbOTWtlJXaeXepuPM1f6MNp9GLt8mLvvYLmp0OhQ2Fwvk6m7xaqDTvY0eYWUVtcnllXfYlGpnfklVuraHHg8HjxuN+6fktUHlWWZPaZeUo/ILK0UKttBcbNbSB9GP0yLxWJJUxoZGUn80zD9C/vXQ/4NHY10h3M1zmQAAAAASUVORK5CYII=') center left no-repeat!important;\npadding-left: 18px;\n}\n.linkify.InstallGentoo {\nbackground: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAklJREFUOE9jYEAAASBTCorZkcSRmTjVCDLziCwG4hfM3EIvGNm44oC6WNEM4WXi5FsEkmfhFX3BxMmfAJSHW9Qr55Px3aZp3X/btq3/hQydPzKysMcCFbBBDeFj4uBdqBJR/gskb1W34j+PmulLoJwbzBJJoMm7dNO7/ntMP/XfpW/v//SKvk+7tl7fvXfTpx5pCdWVSiHFv1wnHQbLi9sE/Wdk5SwBauaCGQB3gUPb5v+7Lr/8/+fvr/9fv/z+f+Pyr/9bV735l9Wy/79Dx/b/Nk0bsLoAHgbeAVHv/v77/f8f0IB7N7+cu3DuecK54z9+7lzz639e9pK/7HwSWMMA5BJwCJeXtOm/fvVj1fcfv369f//92cN7X6ZcPvf9x6Htv//vXP3r/+T245UEYgpskPTNq08LgN749/PH7/93rv/6f/rw7//nj//4f+bU0zQcUQwWBkdVbGz62y+fv3wHeeXrlz//H9798//qpY//M3KqfzGxc8djiWKwZnBUuWQ2/fr46fv/P39+///x/ff/d69//z97+s7fyMb5/+y7d2GLYriDZikFF/1qXXXj/4Pbv/8/f/jn/5MH316/eP6jVlBAaIt6VO1/jxmn/zv27P7Pp2HxEajLD90ra9Sj6/979O37X73w0n+vqOL/0lJyMVBFq0EGgDSD0oKAlu1/oHg4ugGzVCKqfouYuL1Xj676Iajr8AnJFricGqYc3Bw+Zi6BVUxsXLHAdL6QiYMPFNrwpIxHDsUhgtAMAopKDjQn4pPDF7P45QC4hSmc1eX8WgAAAABJRU5ErkJggg==') center left no-repeat!important;\npadding-left: 18px;\n}\n\n/* General */\n:root.yotsuba .dialog {\nbackground-color: #F0E0D6;\nborder-color: #D9BFB7;\n}\n:root.yotsuba .field:focus {\nborder-color: #EA8;\n}\n\n/* Header */\n:root.yotsuba #header-bar, :root.yotsuba #notifications {\nfont-size: 9pt;\ncolor: #B86;\n}\n:root.yotsuba #header-bar a, :root.yotsuba #notifications a {\ncolor: #800000;\n}\n\n/* Settings */\n:root.yotsuba #fourchanx-settings fieldset {\nborder-color: #D9BFB7;\n}\n\n/* Quote */\n:root.yotsuba .backlink.deadlink {\ncolor: #00E !important;\n}\n:root.yotsuba .inline {\nborder-color: #D9BFB7;\nbackground-color: rgba(255, 255, 255, .14);\n}\n\n/* QR */\n.yotsuba #dump-list::-webkit-scrollbar-thumb {\nbackground-color: #F0E0D6;\nborder-color: #D9BFB7;\n}\n:root.yotsuba .qr-preview {\nbackground-color: rgba(0, 0, 0, .15);\n}\n\n/* Menu */\n:root.yotsuba #menu {\ncolor: #800000;\n}\n:root.yotsuba .entry {\nborder-bottom: 1px solid #D9BFB7;\nfont-size: 10pt;\n}\n:root.yotsuba .focused.entry {\nbackground: rgba(255, 255, 255, .33);\n}\n\n/* Watcher Favicon */\n:root.yotsuba .watch-thread-link\n{\nbackground-image: url(\"data:image/svg+xml,<svg viewBox='0 0 26 26' preserveAspectRatio='true' xmlns='http://www.w3.org/2000/svg'><path fill='rgb(128,0,0)' d='M24.132,7.971c-2.203-2.205-5.916-2.098-8.25,0.235L15.5,8.588l-0.382-0.382c-2.334-2.333-6.047-2.44-8.25-0.235c-2.204,2.203-2.098,5.916,0.235,8.249l8.396,8.396l8.396-8.396C26.229,13.887,26.336,10.174,24.132,7.971z'/></svg>\");\n}\n\n/* General */\n:root.yotsuba-b .dialog {\nbackground-color: #D6DAF0;\nborder-color: #B7C5D9;\n}\n:root.yotsuba-b .field:focus {\nborder-color: #98E;\n}\n\n/* Header */\n:root.yotsuba-b #header-bar, :root.yotsuba-b #notifications {\nfont-size: 9pt;\ncolor: #89A;\n}\n:root.yotsuba-b #header-bar a, :root.yotsuba-b #notifications a {\ncolor: #34345C;\n}\n\n/* Settings */\n:root.yotsuba-b #fourchanx-settings fieldset {\nborder-color: #B7C5D9;\n}\n\n/* Quote */\n:root.yotsuba-b .backlink.deadlink {\ncolor: #34345C !important;\n}\n:root.yotsuba-b .inline {\nborder-color: #B7C5D9;\nbackground-color: rgba(255, 255, 255, .14);\n}\n\n/* QR */\n.yotsuba-b #dump-list::-webkit-scrollbar-thumb {\nbackground-color: #D6DAF0;\nborder-color: #B7C5D9;\n}\n:root.yotsuba-b .qr-preview {\nbackground-color: rgba(0, 0, 0, .15);\n}\n\n/* Menu */\n:root.yotsuba-b #menu {\ncolor: #000;\n}\n:root.yotsuba-b .entry {\nborder-bottom: 1px solid #B7C5D9;\nfont-size: 10pt;\n}\n:root.yotsuba-b .focused.entry {\nbackground: rgba(255, 255, 255, .33);\n}\n\n/* Watcher Favicon */\n:root.yotsuba-b .watch-thread-link\n{\nbackground-image: url(\"data:image/svg+xml,<svg viewBox='0 0 26 26' preserveAspectRatio='true' xmlns='http://www.w3.org/2000/svg'><path fill='rgb(0,0,0)' d='M24.132,7.971c-2.203-2.205-5.916-2.098-8.25,0.235L15.5,8.588l-0.382-0.382c-2.334-2.333-6.047-2.44-8.25-0.235c-2.204,2.203-2.098,5.916,0.235,8.249l8.396,8.396l8.396-8.396C26.229,13.887,26.336,10.174,24.132,7.971z'/></svg>\");\n}\n\n/* General */\n:root.futaba .dialog {\nbackground-color: #F0E0D6;\nborder-color: #D9BFB7;\n}\n:root.futaba .field:focus {\nborder-color: #EA8;\n}\n\n/* Header */\n:root.futaba #header-bar, :root.futaba #notifications {\nfont-size: 11pt;\ncolor: #B86;\n}\n:root.futaba #header-bar a, :root.futaba #notifications a {\ncolor: #800000;\n}\n\n/* Settings */\n:root.futaba #fourchanx-settings fieldset {\nborder-color: #D9BFB7;\n}\n\n/* Quote */\n:root.futaba .backlink.deadlink {\ncolor: #00E !important;\n}\n:root.futaba .inline {\nborder-color: #D9BFB7;\nbackground-color: rgba(255, 255, 255, .14);\n}\n\n/* QR */\n.futaba #dump-list::-webkit-scrollbar-thumb {\nbackground-color: #F0E0D6;\nborder-color: #D9BFB7;\n}\n:root.futaba .qr-preview {\nbackground-color: rgba(0, 0, 0, .15);\n}\n\n/* Menu */\n:root.futaba #menu {\ncolor: #800000;\n}\n:root.futaba .entry {\nborder-bottom: 1px solid #D9BFB7;\nfont-size: 12pt;\n}\n:root.futaba .focused.entry {\nbackground: rgba(255, 255, 255, .33);\n}\n\n/* Watcher Favicon */\n:root.futaba .watch-thread-link\n{\nbackground-image: url(\"data:image/svg+xml,<svg viewBox='0 0 26 26' preserveAspectRatio='true' xmlns='http://www.w3.org/2000/svg'><path fill='rgb(128,0,0)' d='M24.132,7.971c-2.203-2.205-5.916-2.098-8.25,0.235L15.5,8.588l-0.382-0.382c-2.334-2.333-6.047-2.44-8.25-0.235c-2.204,2.203-2.098,5.916,0.235,8.249l8.396,8.396l8.396-8.396C26.229,13.887,26.336,10.174,24.132,7.971z'/></svg>\");\n}\n\n/* General */\n:root.burichan .dialog {\nbackground-color: #D6DAF0;\nborder-color: #B7C5D9;\n}\n:root.burichan .field:focus {\nborder-color: #98E;\n}\n\n/* Header */\n:root.burichan #header-bar, :root.burichan #header-bar #notifications {\nfont-size: 11pt;\ncolor: #89A;\n}\n:root.burichan #header-bar a, :root.burichan #header-bar #notifications a {\ncolor: #34345C;\n}\n\n/* Settings */\n:root.burichan #fourchanx-settings fieldset {\nborder-color: #B7C5D9;\n}\n\n/* Quote */\n:root.burichan .backlink.deadlink {\ncolor: #34345C !important;\n}\n:root.burichan .inline {\nborder-color: #B7C5D9;\nbackground-color: rgba(255, 255, 255, .14);\n}\n\n/* QR */\n.burichan #dump-list::-webkit-scrollbar-thumb {\nbackground-color: #D6DAF0;\nborder-color: #B7C5D9;\n}\n:root.burichan .qr-preview {\nbackground-color: rgba(0, 0, 0, .15);\n}\n\n/* Menu */\n:root.burichan #menu {\ncolor: #000000;\n}\n:root.burichan .entry {\nborder-bottom: 1px solid #B7C5D9;\nfont-size: 12pt;\n}\n:root.burichan .focused.entry {\nbackground: rgba(255, 255, 255, .33);\n}\n\n/* Watcher Favicon */\n:root.burichan .watch-thread-link\n{\nbackground-image: url(\"data:image/svg+xml,<svg viewBox='0 0 26 26' preserveAspectRatio='true' xmlns='http://www.w3.org/2000/svg'><path fill='rgb(0,0,0)' d='M24.132,7.971c-2.203-2.205-5.916-2.098-8.25,0.235L15.5,8.588l-0.382-0.382c-2.334-2.333-6.047-2.44-8.25-0.235c-2.204,2.203-2.098,5.916,0.235,8.249l8.396,8.396l8.396-8.396C26.229,13.887,26.336,10.174,24.132,7.971z'/></svg>\");\n}\n\n/* General */\n:root.tomorrow .dialog {\nbackground-color: #282A2E;\nborder-color: #111;\n}\n\n/* Header */\n:root.tomorrow #header-bar, :root.tomorrow #notifications {\nfont-size: 9pt;\ncolor: #C5C8C6;\n}\n:root.tomorrow #header-bar a, :root.tomorrow #notifications a {\ncolor: #81A2BE;\n}\n\n/* Settings */\n:root.tomorrow #fourchanx-settings fieldset {\nborder-color: #111;\n}\n\n/* Quote */\n:root.tomorrow .backlink.deadlink {\ncolor: #81A2BE !important;\n}\n:root.tomorrow .inline {\nborder-color: #111;\nbackground-color: rgba(0, 0, 0, .14);\n}\n\n/* QR */\n.tomorrow #dump-list::-webkit-scrollbar-thumb {\nbackground-color: #282A2E;\nborder-color: #111;\n}\n:root.tomorrow .qr-preview {\nbackground-color: rgba(255, 255, 255, .15);\n}\n:root.tomorrow #qr .field {\nbackground-color: rgb(26, 27, 29);\ncolor: rgb(197,200,198);\nborder-color: rgb(40, 41, 42);\n}\n:root.tomorrow #qr .field:focus {\nborder-color: rgb(129, 162, 190) !important;\nbackground-color: rgb(30,32,36);\n}\n\n/* Menu */\n:root.tomorrow #menu {\ncolor: #C5C8C6;\n}\n:root.tomorrow .entry {\nborder-bottom: 1px solid #111;\nfont-size: 10pt;\n}\n:root.tomorrow .focused.entry {\nbackground: rgba(0, 0, 0, .33);\n}\n\n/* Watcher Favicon */\n:root.tomorrow .watch-thread-link\n{\nbackground-image: url(\"data:image/svg+xml,<svg viewBox='0 0 26 26' preserveAspectRatio='true' xmlns='http://www.w3.org/2000/svg'><path fill='rgb(197,200,198)' d='M24.132,7.971c-2.203-2.205-5.916-2.098-8.25,0.235L15.5,8.588l-0.382-0.382c-2.334-2.333-6.047-2.44-8.25-0.235c-2.204,2.203-2.098,5.916,0.235,8.249l8.396,8.396l8.396-8.396C26.229,13.887,26.336,10.174,24.132,7.971z'/></svg>\");\n}\n\n/* General */\n:root.photon .dialog {\nbackground-color: #DDD;\nborder-color: #CCC;\n}\n:root.photon .field:focus {\nborder-color: #EA8;\n}\n\n/* Header */\n:root.photon #header-bar, :root.photon #notifications {\nfont-size: 9pt;\ncolor: #333;\n}\n:root.photon #header-bar a, :root.photon #notifications a {\ncolor: #FF6600;\n}\n\n/* Settings */\n:root.photon #fourchanx-settings fieldset {\nborder-color: #CCC;\n}\n\n/* Quote */\n:root.photon .backlink.deadlink {\ncolor: #F60 !important;\n}\n:root.photon .inline {\nborder-color: #CCC;\nbackground-color: rgba(255, 255, 255, .14);\n}\n\n/* QR */\n.photon #dump-list::-webkit-scrollbar-thumb {\nbackground-color: #DDD;\nborder-color: #CCC;\n}\n:root.photon .qr-preview {\nbackground-color: rgba(0, 0, 0, .15);\n}\n\n/* Menu */\n:root.photon #menu {\ncolor: #333;\n}\n:root.photon .entry {\nborder-bottom: 1px solid #CCC;\nfont-size: 10pt;\n}\n:root.photon .focused.entry {\nbackground: rgba(255, 255, 255, .33);\n}\n\n/* Watcher Favicon */\n:root.photon .watch-thread-link\n{\nbackground-image: url(\"data:image/svg+xml,<svg viewBox='0 0 26 26' preserveAspectRatio='true' xmlns='http://www.w3.org/2000/svg'><path fill='rgb(51,51,51)' d='M24.132,7.971c-2.203-2.205-5.916-2.098-8.25,0.235L15.5,8.588l-0.382-0.382c-2.334-2.333-6.047-2.44-8.25-0.235c-2.204,2.203-2.098,5.916,0.235,8.249l8.396,8.396l8.396-8.396C26.229,13.887,26.336,10.174,24.132,7.971z'/></svg>\");\n}\n" }; Main.init(); diff --git a/builds/crx/script.js b/builds/crx/script.js index f5a43b9e2..656b8d531 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -278,7 +278,9 @@ 'Next reply': ['j', 'Select next reply.'], 'Previous reply': ['k', 'Select previous reply.'], 'Deselect reply': ['Shift+d', 'Deselect reply.'], - 'Hide': ['x', 'Hide thread.'] + 'Hide': ['x', 'Hide thread.'], + 'Previous Post Quoting You': ['Alt+Up', 'Scroll to the previous post that quotes you.'], + 'Next Post Quoting You': ['Alt+Down', 'Scroll to the next post that quotes you.'] }, updater: { checkbox: { @@ -4201,6 +4203,38 @@ $.addClass(this.nodes.root, 'quotesYou'); } } + }, + cb: { + seek: function(type) { + var post, posts, result, str; + + if (!QuoteYou.lastRead) { + if (!(post = QuoteYou.lastRead = $('.quotesYou'))) { + new Notification('warning', 'No posts are currently quoting you, loser.', 20); + } + if (QuoteYou.cb.scroll(post)) { + return; + } + } + str = "" + type + "::div[contains(@class,'quotesYou')]"; + result = $.X(str, QuoteYou.lastRead); + while (post = result.snapshotItem(type === 'preceding' ? result.snapshotLength - 1 : 0)) { + if (QuoteYou.cb.scroll(post)) { + return; + } + } + posts = $$('.quotesYou'); + return QuoteYou.cb.scroll(posts[type === 'following' ? 0 : posts.length - 1]); + }, + scroll: function(post) { + QuoteYou.lastRead = post; + if (Get.postFromRoot(post).isHidden) { + return false; + } else { + Header.scrollToPost(post); + return true; + } + } } }; @@ -7952,6 +7986,11 @@ bottom = post.nodes.root.getBoundingClientRect().bottom; if (bottom < height) { ID = post.ID; + if (Conf['Mark Quotes of You']) { + if (post.info.yours) { + QuoteYou.lastRead = post.nodes.root; + } + } if (Conf['Quote Threading']) { posts.splice(i, 1); continue; @@ -9077,6 +9116,12 @@ ThreadHiding.toggle(thread); } break; + case Conf['Previous Post Quoting You']: + QuoteYou.cb.seek('preceding'); + break; + case Conf['Next Post Quoting You']: + QuoteYou.cb.seek('following'); + break; default: return; } @@ -10659,7 +10704,7 @@ } return Main.thisPageIsLegit; }, - css: "/* General */\n.dialog {\nbox-shadow: 0 1px 2px rgba(0, 0, 0, .15);\nborder: 1px solid;\ndisplay: block;\npadding: 0;\n}\n.captcha-img,\n.field {\nbackground-color: #FFF;\nborder: 1px solid #CCC;\n-moz-box-sizing: border-box;\nbox-sizing: border-box;\ncolor: #333;\nfont: 13px sans-serif;\noutline: none;\ntransition: color .25s, border-color .25s;\ntransition: color .25s, border-color .25s;\n}\n.field::-moz-placeholder,\n.field:hover::-moz-placeholder {\ncolor: #AAA !important;\nfont-size: 13px !important;\nopacity: 1.0 !important;\n}\n.captch-img:hover,\n.field:hover {\nborder-color: #999;\n}\n.field:hover, .field:focus {\ncolor: #000;\n}\n.field[disabled] {\nbackground-color: #F2F2F2;\ncolor: #888;\n}\n.move {\ncursor: move;\noverflow: hidden;\n}\nlabel, .favicon {\ncursor: pointer;\n}\na[href=\"javascript:;\"] {\ntext-decoration: none;\n}\n.warning {\ncolor: red;\n}\n#boardNavDesktop {\ndisplay: none !important;\n}\na {\noutline: none !important;\n}\n\n/* 4chan style fixes */\n.opContainer, .op {\ndisplay: block !important;\noverflow: visible !important;\n}\n[hidden] {\ndisplay: none !important;\n}\n\n/* fixed, z-index */\n#overlay,\n#fourchanx-settings,\n#qp, #ihover,\n#navlinks, .fixed #header-bar,\n:root.float #updater,\n:root.float #thread-stats,\n#qr {\nposition: fixed;\n}\n#fourchanx-settings {\nz-index: 999;\n}\n#overlay {\nz-index: 900;\n}\n#notifications {\nz-index: 70;\n}\n#qp, #ihover {\nz-index: 60;\n}\n#menu {\nz-index: 50;\n}\n#navlinks, #updater, #thread-stats {\nz-index: 40;\n}\n.fixed #header-bar.autohide {\nz-index: 35;\n}\n#qr {\nz-index: 30;\n}\n#watcher {\nz-index: 8;\n}\n:root.fixed-watcher #watcher {\nz-index: 20;\n}\n.fixed #header-bar {\nz-index: 10;\n}\n/* Header */\n.fixed.top body {\npadding-top: 2em;\n}\n.fixed.bottom body {\npadding-bottom: 2em;\n}\n.fixed #header-bar {\nright: 0;\nleft: 0;\npadding: 3px 4px 4px;\n}\n.fixed.top #header-bar {\ntop: 0;\n}\n.fixed.bottom #header-bar {\nbottom: 0;\n}\n#header-bar {\nborder-width: 0;\ntransition: all .1s .05s ease-in-out;\n}\n:root.centered-links #shortcuts {\nwidth: 300px;\ntext-align: right;\n}\n:root.centered-links #header-bar {\ntext-align: center;\n}\n:root.centered-links #custom-board-list {\nposition: relative;\nleft: 150px;\n}\n.fixed.top #header-bar {\nborder-bottom-width: 1px;\n}\n.fixed.bottom #header-bar {\nbox-shadow: 0 -1px 2px rgba(0, 0, 0, .15);\nborder-top-width: 1px;\n}\n.fixed.bottom #header-bar .menu-button i {\nborder-top: none;\nborder-bottom: 6px solid;\n}\n#board-list {\ntext-align: center;\n}\n.fixed #header-bar.autohide:not(:hover) {\nbox-shadow: none;\ntransition: all .8s .6s cubic-bezier(.55, .055, .675, .19);\n}\n.fixed.top #header-bar.autohide:not(:hover) {\nmargin-bottom: -1em;\n-webkit-transform: translateY(-100%);\ntransform: translateY(-100%);\n}\n.fixed.bottom #header-bar.autohide:not(:hover) {\n-webkit-transform: translateY(100%);\ntransform: translateY(100%);\n}\n#scroll-marker {\nleft: 0;\nright: 0;\nheight: 10px;\nposition: absolute;\n}\n:root:not(.autohide) #scroll-marker {\npointer-events: none;\n}\n#header-bar #scroll-marker {\ndisplay: none;\n}\n.fixed #header-bar #scroll-marker {\ndisplay: block;\n}\n.fixed.top #header-bar #scroll-marker {\ntop: 100%;\n}\n.fixed.bottom #header-bar #scroll-marker {\nbottom: 100%;\n}\n#header-bar a:not(.entry):not(.close) {\ntext-decoration: none;\npadding: 1px;\n}\n#header-bar input {\nmargin: 0;\nvertical-align: bottom;\n}\n#shortcuts:empty {\ndisplay: none;\n}\n.brackets-wrap::before {\ncontent: \"\\00a0[\";\n}\n.brackets-wrap::after {\ncontent: \"]\\00a0\";\n}\n.disabled,\n.expand-all-shortcut {\nopacity: .45;\n}\n#shortcuts {\nfloat: right;\n}\n.shortcut {\nmargin-left: 3px;\n}\n#navbotright,\n#navtopright {\ndisplay: none;\n}\n#toggleMsgBtn {\ndisplay: none !important;\n}\n.current {\nfont-weight: bold;\n}\n/* 4chan X link brackets */\n.fourchanx-link::after {\ncontent: \"]\";\n}\n.fourchanx-link::before {\ncontent: \"[\";\n}\n/* Notifications */\n#notifications {\nposition: fixed;\ntop: 0;\nheight: 0;\ntext-align: center;\nright: 0;\nleft: 0;\ntransition: all .8s .6s cubic-bezier(.55, .055, .675, .19);\n}\n.fixed.top #header-bar #notifications {\nposition: absolute;\ntop: 100%;\n}\n.notification {\ncolor: #FFF;\nfont-weight: 700;\ntext-shadow: 0 1px 2px rgba(0, 0, 0, .5);\nbox-shadow: 0 1px 2px rgba(0, 0, 0, .15);\nborder-radius: 2px;\nmargin: 1px auto;\nwidth: 500px;\nmax-width: 100%;\nposition: relative;\ntransition: all .25s ease-in-out;\n}\n.notification.error {\nbackground-color: hsla(0, 100%, 38%, .9);\n}\n.notification.warning {\nbackground-color: hsla(36, 100%, 38%, .9);\n}\n.notification.info {\nbackground-color: hsla(200, 100%, 38%, .9);\n}\n.notification.success {\nbackground-color: hsla(104, 100%, 38%, .9);\n}\n.notification a {\ncolor: white;\n}\n.notification > .close {\npadding: 6px;\ntop: 0;\nright: 5px;\nposition: absolute;\n}\n.message {\n-moz-box-sizing: border-box;\nbox-sizing: border-box;\npadding: 6px 20px;\nmax-height: 200px;\nwidth: 100%;\noverflow: auto;\n}\n\n/* Settings */\n:root.fourchan-x body {\n-moz-box-sizing: border-box;\nbox-sizing: border-box;\n}\n#overlay {\nbackground-color: rgba(0, 0, 0, .5);\ntop: 0;\nleft: 0;\nheight: 100%;\nwidth: 100%;\n}\n#fourchanx-settings {\n-moz-box-sizing: border-box;\nbox-sizing: border-box;\nbox-shadow: 0 0 15px rgba(0, 0, 0, .15);\nheight: 600px;\nmax-height: 100%;\nwidth: 900px;\nmax-width: 100%;\nmargin: auto;\npadding: 3px;\ntop: 50%;\nleft: 50%;\n-moz-transform: translate(-50%, -50%);\n-webkit-transform: translate(-50%, -50%);\ntransform: translate(-50%, -50%);\n}\n#fourchanx-settings > nav {\npadding: 2px 2px 0;\nheight: 15px;\n}\n#fourchanx-settings > nav a {\ntext-decoration: underline;\n}\n#fourchanx-settings > nav a.close {\ntext-decoration: none;\npadding: 2px;\n}\n.section-container {\noverflow: auto;\nposition: absolute;\ntop: 2.1em;\nright: 5px;\nbottom: 5px;\nleft: 5px;\npadding-right: 5px;\n}\n.sections-list {\npadding: 0 3px;\nfloat: left;\n}\n.credits {\nfloat: right;\n}\n.tab-selected {\nfont-weight: 700;\n}\n.section-sauce ul,\n.section-advanced ul {\nlist-style: none;\nmargin: 0;\n}\n.section-sauce ul {\npadding: 8px;\n}\n.section-advanced ul {\npadding: 0px;\n}\n.section-sauce li,\n.section-advanced li {\npadding-left: 4px;\n}\n.section-main label {\ntext-decoration: underline;\n}\n.section-filter ul {\npadding: 0;\n}\n.section-filter li {\nmargin: 10px 40px;\n}\n.section-filter textarea {\nheight: 500px;\n}\n.section-sauce textarea {\nheight: 350px;\n}\n.section-advanced .field[name=\"boardnav\"] {\nwidth: 100%;\n}\n.section-advanced textarea {\nheight: 150px;\n}\n.section-advanced .archive-cell {\nmin-width: 160px;\ntext-align: center;\n}\n.section-advanced #archive-board-select {\nposition: absolute;\n}\n.section-advanced .note {\nfont-size: 0.8em;\nfont-style: italic;\nmargin-left: 10px;\n}\n.section-advanced .note code {\nfont-style: normal;\nfont-size: 11px;\n}\n.section-keybinds .field {\nfont-family: monospace;\n} \n#fourchanx-settings fieldset {\nborder: 1px solid;\nborder-radius: 3px;\n}\n#fourchanx-settings legend {\nfont-weight: 700;\n}\n#fourchanx-settings textarea {\nfont-family: monospace;\nmin-width: 100%;\nmax-width: 100%;\n}\n#fourchanx-settings code {\ncolor: #000;\nbackground-color: #FFF;\npadding: 0 2px;\n}\n.unscroll {\noverflow: hidden;\n}\n\n/* Announcement Hiding */\n:root.hide-announcement #globalMessage {\ndisplay: none;\n}\na.hide-announcement {\nfloat: left;\n}\n\n/* Unread */\n#unread-line {\nmargin: 0;\nborder-color: rgb(255,0,0);\n}\n\n/* Thread Updater */\n#updater {\nbackground: none;\nborder: none;\nbox-shadow: none;\n}\n#updater > .move {\npadding: 5px 3px 0px;\nmargin-bottom: -3px;\n}\n#updater > div:last-child {\ntext-align: center;\n}\n#updater input[type=number] {\nwidth: 4em;\n}\n:root.float #updater {\npadding: 0px 3px;\n}\n.new {\ncolor: limegreen;\n}\n#update-status.new {\nmargin-right: 5px;\n}\n#update-timer {\ncursor: pointer;\n}\n\n/* Thread Watcher */\n#watcher {\nposition: absolute;\n}\n#watcher {\npadding-bottom: 3px;\noverflow: hidden;\nwhite-space: nowrap;\nmin-width: 120px;\nmax-height: 92%;\noverflow-y: auto;\n}\n:root.fixed-watcher #watcher {\nposition: fixed;\n}\n:root:not(.fixed-watcher) #watcher:not(:hover) {\nmax-height: 210px;\noverflow-y: hidden;\n}\n#watcher > .move {\npadding-top: 3px;\n}\n#watcher > div {\nmax-width: 250px;\noverflow: hidden;\npadding-left: 3px;\npadding-right: 3px;\ntext-overflow: ellipsis;\n}\n#watcher a {\ntext-decoration: none;\n}\n#watcher .move>.close {\nposition: absolute;\nright: 0px;\ntop: 0px;\npadding: 0px 4px;\n}\n.watch-thread-link {\npadding-top: 18px;\nwidth: 18px;\nheight: 0px;\ndisplay: inline-block;\nbackground-repeat: no-repeat;\nopacity: 0.2;\nposition: relative;\ntop: 1px;\n}\n.watch-thread-link.watched {\nopacity: 1;\n}\n\n/* Thread Stats */\n#thread-stats {\nbackground: none;\nborder: none;\nbox-shadow: none;\n}\n:root.float #post-count, :root.float #file-count {\npointer-events: none;\n}\n:root.float #thread-stats {\npadding: 0px 3px;\n}\n\n/* Quote */\n.deadlink {\ntext-decoration: none !important;\n}\n.backlink.deadlink:not(.forwardlink),\n.quotelink.deadlink:not(.forwardlink) {\ntext-decoration: underline !important;\n}\n.inlined {\nopacity: .5;\n}\n#qp input, .forwarded {\ndisplay: none;\n}\n.quotelink.forwardlink,\n.backlink.forwardlink {\ntext-decoration: none;\nborder-bottom: 1px dashed;\n}\n.filtered {\ntext-decoration: underline line-through;\n}\n:root.hide-backlinks .backlink.filtered {\ndisplay: none;\n}\n.inline {\nborder: 1px solid;\ndisplay: table;\nmargin: 2px 0;\n}\n.inline .post {\nborder: 0 !important;\nbackground-color: transparent !important;\ndisplay: table !important;\nmargin: 0 !important;\npadding: 1px 2px !important;\n}\n#qp > .opContainer::after {\ncontent: '';\nclear: both;\ndisplay: table;\n}\n#qp .post {\nborder: none;\nmargin: 0;\npadding: 2px 2px 5px;\n}\n#qp img {\nmax-height: 80vh;\nmax-width: 50vw;\n}\n.qphl {\noutline: 2px solid rgba(216, 94, 49, .7);\n}\n:root.highlight-own .yourPost>.reply,\n:root.highlight-you .quotesYou>.reply {\nborder-left: 2px solid rgba(221,0,0,.5);\n}\n/* Quote Threading */\n.threadContainer {\nmargin-left: 20px;\nborder-left: 1px solid rgba(128,128,128,.3);\n}\n.threadOP {\nclear: both;\n} \n\n/* File */\n.fileText:hover .fntrunc,\n.fileText:not(:hover) .fnfull,\n.expanded-image > .post > .file > .fileThumb > img[data-md5],\n:not(.expanded-image) > .post > .file > .fileThumb > .full-image {\ndisplay: none;\n}\n.expanding {\nopacity: .5;\n}\n:root.fit-height .full-image {\nmax-height: 100vh;\n}\n:root.fit-width .full-image {\nmax-width: 100%;\n}\n:root.gecko.fit-width .full-image {\nwidth: 100%;\n}\n#ihover {\n-moz-box-sizing: border-box;\nbox-sizing: border-box;\nmax-height: 100%;\nmax-width: 75%;\npadding-bottom: 16px;\n}\n.fappeTyme .thread > .noFile,\n.fappeTyme .threadContainer > .noFile {\ndisplay: none;\n}\n\n/* Index/Reply Navigation */\n#navlinks {\nfont-size: 16px;\ntop: 25px;\nright: 10px;\n}\n\n/* Filter */\n.opContainer.filter-highlight {\nbox-shadow: inset 5px 0 rgba(255, 0, 0, .5);\n}\n.filter-highlight > .reply {\nbox-shadow: -5px 0 rgba(255, 0, 0, .5);\n}\n\n/* Spoiler text */\n:root.reveal-spoilers s {\ncolor: white !important;\n}\n\n/* Thread & Reply Hiding */\n.hide-thread-button,\n.hide-reply-button {\nfloat: left;\nmargin-right: 2px;\n}\n.stub ~ * {\ndisplay: none !important;\n}\n.stub input {\ndisplay: inline-block;\n}\n\n/* QR */\n:root.hide-original-post-form #postForm,\n:root.hide-original-post-form .postingMode,\n:root.hide-original-post-form #togglePostForm,\n#qr.autohide:not(.has-focus):not(:hover) > form,\n.postingMode ~ #qr select,\n#file-n-submit:not(.has-file) #qr-filerm {\ndisplay: none;\n}\n#qr select,\n#dump-button,\n.remove,\n.captcha-img {\ncursor: pointer;\n}\n#qr {\nz-index: 20;\nposition: fixed;\npadding: 1px;\nborder: 1px solid transparent;\nmin-width: 300px;\nborder-radius: 3px 3px 0 0;\n}\n#qrtab {\nborder-radius: 3px 3px 0 0;\n}\n#qrtab {\nmargin-bottom: 1px;\n}\n#qr .close {\nfloat: right;\npadding: 0 3px;\n}\n#qr .warning {\nmin-height: 1.6em;\nvertical-align: middle;\npadding: 0 1px;\nborder-width: 1px;\nborder-style: solid;\n}\n.qr-link-container {\ntext-align: center;\n}\n.persona {\nwidth: 248px;\nmax-width: 100%;\nmin-width: 100%;\n}\n#dump-button {\nwidth: 10%;\nmargin: 0;\nmargin-right: 4px;\nfont: 13px sans-serif;\npadding: 1px 0px 2px;\nopacity: 0.6;\n}\n.persona .field:not(#dump) {\nwidth: 95px;\nmin-width: 33.3%;\nmax-width: 33.3%;\n}\n#qr textarea.field {\nheight: 14.8em;\nmin-height: 9em;\n}\n#qr.has-captcha textarea.field {\nheight: 9em;\n}\ninput.field.tripped:not(:hover):not(:focus) {\ncolor: transparent !important;\ntext-shadow: none !important;\n}\n#qr textarea {\nresize: both;\n}\n.captcha-img {\nmargin: 0px;\ntext-align: center;\nbackground-image: #fff;\nfont-size: 0px;\nmin-height: 59px;\nmin-width: 302px;\n}\n.captcha-input {\nwidth: 100%;\nmargin: 1px 0 0;\n}\n.captcha-input.error:focus {\nborder-color: rgb(255,0,0) !important;\n}\n.field {\n-moz-box-sizing: border-box;\nmargin: 0px;\npadding: 2px 4px 3px;\n}\n#qr textarea {\nmin-width: 100%;\n}\n#qr [type='submit'] {\nwidth: 25%;\nvertical-align: top;\n}\n:root.webkit #qr [type='submit'] {\nheight: 24px;\n}\n/* Fake File Input */\n#qr-filename,\n.has-file #qr-no-file {\ndisplay: none;\n}\n#qr-no-file,\n.has-file #qr-filename {\ndisplay: inline-block;\npadding: 0px 4px;\nmargin-bottom: 2px;\noverflow: hidden;\ntext-overflow: ellipsis;\nmax-width: 88%;\n}\n#qr-no-file {\ncolor: #AAA;\n}\n#qr-filename-container {\n-moz-box-sizing: border-box;\ndisplay: inline-block;\nposition: relative;\nwidth: 100px;\nmin-width: 74.6%;\nmax-width: 74.6%;\nmargin-right: 0.4%;\nmargin-top: 1px;\noverflow: hidden;\npadding: 2px 1px 0;\nheight: 22px;\n}\n#qr-filename-container:hover {\ncursor: text;\n}\n#qr-extras-container {\nposition: absolute;\nright: 0px;\n}\n#qr-filerm {\nmargin-right: 2px;\nz-index: 2;\n}\n#file-n-submit {\nheight: 23px;\n}\n#qr input[type=file] {\nvisibility: hidden;\nposition: absolute;\n}\n/* Thread Select / Spoiler Label */\n#qr select {\nfloat: right;\n}\n#qr.has-spoiler .has-file #qr-spoiler-label {\nwidth: 6.7%;\nmin-width: 6.7%;\nmax-width: 6.7%;\ndisplay: inline-block;\ntext-align: center;\nvertical-align: top;\n}\n#qr.has-spoiler #file-n-submit:not(.has-file) #qr-spoiler-label {\ndisplay: none;\n}\n#qr.has-spoiler .has-file #qr-filename-container {\nmax-width: 67.9%;\nmin-width: 67.9%;\n}\n#qr-spoiler-label input {\nposition: relative;\ntop: 3px;\n}\n/* Dumping UI */\n.dump #dump-list-container {\ndisplay: block;\n}\n#dump-list-container {\ndisplay: none;\nposition: relative;\noverflow-y: hidden;\nmargin-top: 1px;\n}\n#dump-list {\noverflow-x: auto;\noverflow-y: hidden;\nwhite-space: nowrap;\nwidth: 248px;\nmax-width: 100%;\nmin-width: 100%;\n}\n#dump-list:hover {\noverflow-x: auto;\n}\n.qr-preview {\n-moz-box-sizing: border-box;\ncounter-increment: thumbnails;\ncursor: move;\ndisplay: inline-block;\nheight: 90px;\nwidth: 90px;\npadding: 2px;\nopacity: .5;\noverflow: hidden;\nposition: relative;\ntext-shadow: 0 1px 1px #000;\n-moz-transition: opacity .25s ease-in-out;\nvertical-align: top;\nbackground-size: cover;\n}\n.qr-preview:hover,\n.qr-preview:focus {\nopacity: .9;\n}\n.qr-preview::before {\ncontent: counter(thumbnails);\ncolor: #fff;\nposition: absolute;\ntop: 3px;\nright: 3px;\ntext-shadow: 0 0 3px #000, 0 0 8px #000;\n}\n.qr-preview#selected {\nopacity: 1;\n}\n.qr-preview.drag {\nbox-shadow: 0 0 10px rgba(0,0,0,.5);\n}\n.qr-preview.over {\nborder-color: #fff;\n}\n.qr-preview > span {\ncolor: #fff;\n}\n.remove {\nbackground: none;\ncolor: #e00;\nfont-weight: 700;\npadding: 3px;\n}\na:only-of-type > .remove {\ndisplay: none;\n}\n.remove:hover::after {\ncontent: \" Remove\";\n}\n.qr-preview > label {\nbackground: rgba(0,0,0,.5);\ncolor: #fff;\nright: 0;\nbottom: 0;\nleft: 0;\nposition: absolute;\ntext-align: center;\n}\n.qr-preview > label > input {\nmargin: 0;\n}\n#add-post {\ncursor: pointer;\nfont-size: 2em;\nposition: absolute;\ntop: 50%;\nright: 10px;\n-moz-transform: translateY(-50%);\n}\n.textarea {\nposition: relative;\n}\n:root.webkit .textarea {\nmargin-bottom: -2px;\n}\n#char-count {\ncolor: #000;\nbackground: hsla(0, 0%, 100%, .5);\nfont-size: 8pt;\nposition: absolute;\nbottom: 1px;\nright: 1px;\npointer-events: none;\n}\n\n/* Menu */\n.menu-button {\ndisplay: inline-block;\nposition: relative;\ncursor: pointer;\n}\n.menu-button i {\nborder-top: 6px solid;\nborder-right: 4px solid transparent;\nborder-left: 4px solid transparent;\ndisplay: inline-block;\nmargin: 2px;\nvertical-align: middle;\n}\n#menu {\nposition: fixed;\noutline: none;\n}\n.entry {\nborder-bottom: 1px solid rgba(0,0,0,.25);\ncursor: pointer;\ndisplay: block;\noutline: none;\npadding: 3px 7px;\nposition: relative;\ntext-decoration: none;\nwhite-space: nowrap;\n}\n.left>.entry.has-submenu {\npadding-right: 17px !important;\n}\n.entry:last-child {\nborder-bottom: 0;\n}\n.has-submenu::after {\ncontent: \"\";\nborder-left: .5em solid;\nborder-top: .3em solid transparent;\nborder-bottom: .3em solid transparent;\ndisplay: inline-block;\nmargin: .3em;\nposition: absolute;\nright: 3px;\n}\n.left .has-submenu::after {\nborder-left: 0;\nborder-right: .5em solid;\n}\n.submenu {\ndisplay: none;\nposition: absolute;\nleft: 100%;\ntop: -1px;\n}\n.focused .submenu {\ndisplay: block;\n}\n.imp-exp-result {\nposition: absolute;\ntext-align: center;\nmargin: auto;\nright: 0px;\nleft: 0px;\nwidth: 200px;\n}\n.export, .import {\ncursor: pointer;\ntext-decoration: none !important;\n}\n/* Link Title Favicons */\n.linkify.YouTube {\nbackground: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAMCAYAAABr5z2BAAABIklEQVQoz53LvUrDUBjG8bOoOammSf1IoBSvoCB4JeIqOHgBLt6AIMRBBQelWurQ2kERnMRBsBUcIp5FJSBI5oQsJVkkUHh8W0o5nhaFHvjBgef/Mq+Q46RJBMkI/vE+aOus956tnEswIZe1LV0QyJ5sE2GzgZfVMtRNIdiDpccEssdlB1mW4bvTwdvWJtRdErM7U+8S/FJykCRJX5qm+KpVce8UMNLRLbulz4iSjTAMh6Iowsd5BeNadp3nUF0VlxAEwZBotXC0Usa4ll3meZdA1iguwvf9vpvDA2wvmKgYGtSud8suDB4TyGr2PF49D/vra9jRZ1BVdknMzgwuCGSnZEObwu6sBnVTCHZiaC7BhFx2PKdxUidiAH/4lLo9Mv0DELVs9qsOHXwAAAAASUVORK5CYII=') center left no-repeat!important;\npadding-left: 18px;\n}\n.linkify.Vimeo {\nbackground: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAASJJREFUOE9jYAAC7ln7/pODQXrBmq333PvPu/YaSRikB6QXbACpmmHqsRoAMll7+20UQ0H8tmuv/pdffPFfZtNNuByGASBFIPDh5x+4IV6HHoDFYGDJgw+YBoBMBUkgA5BtIKduuvvy//svX+FSB+88wTTAc+/t/83bj/0HScLA5BPXwc7lKJ36f+L6XXDxhUfOYxrAPWUnWKFp9UQUm3iWQxSDXAEDSX3zcIcB96wD/x+8eA1XDNKMHAYg20GW4Y0FkCIYAAUqzEBQOIBciRzlWKMxZelOlMCEcVxq+jHSC1YDJPs3YBgA8jey0/F6ARRwsFAHORukmat9NdbUijMpg/wKcrJodDFOzSBXwA3Alh9AToZFI7a8Asu98BxJbnYGAJb5vYLDANzSAAAAAElFTkSuQmCC') center left no-repeat!important;\npadding-left: 18px;\n}\n.linkify.SoundCloud {\nbackground: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABsklEQVQ4y5WTy2pUQRCGv2rbzDjJeAlIBmOyipGIIJqFEBDElwh4yULGeRFXPoEIBl/AvQ/gC2RnxCAoxijiwks852S6+3dxzslcHJCpTXVX11/Xv0097gLPgVNMJxnQNfX4zsqleWbnpoMf/oa9d988MM9MC/rp+E0a+A0dsVobMNMCOO8B6McRoABJI+A6gJmN3D2A8jgEBCEkSEMBrcrsDAzDWWn3AjgKFaDMmgRqniGFgsaDp1jrLOngDf1XT1D+A1dFc4MKAkkiCVKjjVu7g9+4Rzx4i1u6hjXbuMWr0O5QPNvCu7IaCZwEKQukLGDrm5x8uI0tr6MkiGlkiv7yLfzN+6S5i6QsIMABkEfcxhbWWYMkVAOjxvYAjc3HNHrbKI9VBQBFwF25XQKSBjqIf1YBuAurEMrczgDygD6/x2LCpFLXLUyQ+PoldphhBhYfIX09XU1+Flaukz7uYqs3SHs7cG4BmTsmkBUF9mmXEwa28BNLPaQPLepuNcbGSWQquQC2/Kdcox1FUGkcB0ykck1nA2+wTzMs8stGnP4rbWGw74EuS/GFQWfK7/wF6P4F7fzIAYkdmdEAAAAASUVORK5CYII=') center left no-repeat!important;\npadding-left: 18px;\n}\n.linkify.audio {\nbackground: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAitJREFUOE9jYCAWKJWwavr0KyXWb/FIbDtUFFyzJx6nVofE2Xo5nXsj0rqPNSR0nVkR2Hjmgmfd+U9Otdf+m5Vf/6+SfeU/R9ChVVgNYDRtlfJuuPA/rPfe/4QpD/6nznj0P27Kw/9unff/69Xf+69c/+C/SO7N/0z+OAxgMmmRCe++/r9i3ev/KWvf/vdY8PK/bt/9/wrNV3/IN5y/IVt1YqNg4pGTTP4HsbuA2bhZ2qvpyn+xjIObxAp3VwqlrgngLFyryVy5nhPmZJHANS2cwYexG8BmVC/pWn3hP4NZlzWuQDJI3dIiFnUUuwEsQAOcq87jNcC7fHeLUtJxHF4AGmBWeAavAWH1+1rUUk7giAWjOknllON4DXAs2NEiG4/DBQxAF/CFHfrPYI4jDFSLuJVjNrUJhB/B7gIGo1pJRt99GAZYJK7wLJ1z7Xzl4vu/7aqv/GRBj0bjqAX2qb0nJ7mXH17C4HcUxQA+hymWtSue/C5a9up/9Ozn/7Vr7v1nRY7GqMb91T3b3v6vWvPmf/S0p/9ZQk+DDLCBRSOz06Jqk+o7/21nvfqvsebDf7kZL/5zBaxphkezd+OFn7HzXvz3Wvjmv9a8N//5Ek//ZTBpVYUrMG2X5wjcdl68+uI/wa5Lr3hSNjczGFeywOVZ/bbcVGp//F9izfv/Ql03f3P4LC/HSEQquYwMFnUCDJ7dzBhyjGZNQpye89M5gpfnMvtNUyE2h4PUAQBovvT7lyNljwAAAABJRU5ErkJggg==') center left no-repeat!important;\npadding-left: 18px;\n}\n.linkify.LiveLeak {\nbackground: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAydJREFUOE9Nk1tIk2EYx79NyUNqTk0o6KYrnZeChodLDxfeZpCbJk4RXU5Nm7tYRYhiYXbQlaeGutyW2gxtpB1RIyKDEjKwA6Ti2dR5KNDn+fq/S6TBj/f93r3P732e53s/qfnkSdej4GB2SBLbwf+jmB+gUMgOheLg/z7EdCUnO6Ref392SpK8Hyh3I+gBwBo7lUp2xcbyQEoKD6alyQOpqd754/h4FjJXZCRJTl9ftmEzoK5/wdQJxPgkLY2WV1dpc2uLtnZ2eHNnhza3t2nd46GhjAzuValY6jx0iIfS03msoIDuQ9COQCtoUSjohU5HuwgaN5loeXycd3d3aW9vzwvW2K5SkdTi58fvzGb+3tdHFggA3QONEAzn59PvjQ1yqNX0zenkvX0B4ffWaGRraChJd/385JGqKvlzTw/fRqOaIGkEd1DjU52O/3g83BkTw5MOh7yJuUCUM2o0yi2hoSw1IIOhykr+YLNRHYKu4XQvyKA/N5c8yMCCDD7Z7bz26xcJ1rH2rKKCG0UJdRAMlJbyG6uVrkJQjWAB5tSbk0Nr2HwDgvcQiIYur6zQyvo6ucvLueHIEZKuQPBQr+dXra1kRuqXEOwFArtWSytra1QdFUVjNhvPLS3R3OIiLUDUD0F1WBhJJtwDW2Ehu5uaqBICI4IFlRB0QLCEzaboaHrd0cHzCBYsIIuesjK+LAQXkEFrXh676uupGCWcR6AeghLQptGQONUAwfOuLp6Zn6eZuTmaXVig7pISrhI90ENgQbdHhoep32JhFzLpu3WLio8epUYIfs7OUjF6UKJW88XERLqYkEBNej11oG8XhCAvMFAuOn5cNiclsTkhQTbhmpri4lgbEMANWi1DwC/xit3t7bK7rY0Fo4OD3G4wyEURESzloAdnceezlErK8vH5N4KzPj50PTOTfkxP0+THj/RlYoInJyZI8HVqim5qNFwQHk7SucBAPo2PKRMNPLM/4pnFszYkhJsNBu6uqWFHba1sr61lQSveQFZQkFx07BhJmhMnrLn4NLMPH/aSExR0QDbmWhwgyEapwDvXoDxdWBiXnjrV/Bdm2kYUxLwmEgAAAABJRU5ErkJggg==') center left no-repeat!important;\npadding-left: 18px;\n}\n.linkify.Vocaroo {\nbackground: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAw9JREFUOE9jYMABuMwYmCyTJKUCGlSnFSy02TTzeOyCiQcDViX26qVz2TAyYtWmEMwuoZ3M7V40LcB79pHkc0svpvzY8jD//87nxf+3Pyn8v/ZO8v+VNyP/2mZJumI1QCWSI8232Hjumitlfw5+qPp/9l8TCt76JP//xkdx/wsXWCzjtWFkwTCkbWFe9plPk/+ga4Txz/xt/D/hkN//gMXif21a+NbyWjIwoRiy6GDT5rP/mlFsPfyp5n/NpOj/22+0gMUXXIz/H7hC/L/bFKFbPDZMrHAD5H35OPt2J9zacDv/f3V7xv9FhwrBGubsT/1//Pjx/1GJ/mD+/nfl/1v3Ovy3KRJNQbHdOlXCvOO03/+pm1P/v3v37n90hhtYw9HPtf8Xb2v937cmHswHeWPRxYj/LvkK3igGKARwicTO07118H3V/5kbi/4vPZMJtK3s/6YH2f+Pfq1B8VbjWrdnMu5s4nAD9CNFhKwz5DTUvLl419zKvAcLtG1P84BRl/b/5M/6/6f/NPzf/qzo84yj0Uus0xUU4Zor54bm9+4OfZG02OCuoAMTb9ZkC9ull1Nvrr2Z+XvRpaRfc65H/68F+jl9svEhzyLFWoccWVc+eyTHq/twydjlKRln7jX9bNMkMJnbhoFRL1xCqmKx6/yi2fYXa/c5/e846PV/5fW0/7OPx/yfcjzop34ulxdGGvDuU8mMXaX507lBuiN6ueadmQeT/p/93vf/1O+G//sP5fw/eL3o/5JLif8zVxs+Tlir9S26UyeFQQvJGBE7FvaFZ9LfN+1y+WjbItSb3GmXvXd15v8zroH/HxgE/D+aGPx/18vi/z07PeZNPRKxe/Kh0Ae8toxscCO4zBkYXArk9C1SxJUYjBkYPPIVtbbuTftz3cz//2O9wP/75iSAXdO72/dt2HL5F6YlfBW4MiJYXMiBiW3t7azHBx+V/t89N+H/8a+1//e9K/9attDp5LQjYX8SuvVL8RoAkmxa65299Erq1FnHo0qrl7t4BddriIs4MrM3rfWcFd+pGwVSAwBZ0bKP8yrZPAAAAABJRU5ErkJggg==') center left no-repeat!important;\npadding-left: 18px;\n}\n.linkify.pastebin {\nbackground: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAtZJREFUOE+NU91LWmEc7sJtQew/2MUY7INg7CLY3W5GMHazyzEQo9UmfYxZTbAiVlgRqLMSZ+XnDC3z2+Y0+8JGakKZTtR0Tl2wtgtLLQh29cz3ZZ3h3Q68vOc95zzP73l+z+/U1f292O09DRxubxOH23P//1bvtQts3dPnry7LZnXJhcUl5Avf8dHtwY+fv2AyW5DOfIXFakMm+w0G4wISyRRm55TQG0y/Wzv6mikJ52Xf9TmVBoFAAD6fDwqFAqFQCJubmzCbzZiensbp6SmkUikikQi0Wi0kEgm6ewVaStDCfXPDandifn6egoaGhrCzswO1Wg2Hw4HBwUGk02kIBAL4/X4IhUJMTk6ii8dfYggy2RwymQzOz88Rj8dRLpexv7+PSqWCYDCIQqGAra0tJBIJrK2t0XdVAjNDEIl+wfj4OEqlEq2wt7dHrchkMmrBYDCAz+fTIjweD7FYrJbgIJOlgLOzM8jlcip1eXmZ2rFarVAqlRCLxcjlchCJRFRljYJYPAG32418Pg+n04lsNouVlRUcHh7C4/FQIOlHNBqlezgcJgQWxkIgGMbExASVNjY2hvX1dVo9mUzS5wREFLhcLrqTcw2B//M2RkdHodPp4PV6oVKpqH+SCom3v7+fNnF4eJiJusbCJ6+PviSyScakiaR5RIHRaKQpmEwmbAdCeD8zB6vdhebHT8SMhcUlC83bbrdTJRsbG3RwiCVCRNJJpDIoVeNNJJJQzKryV+rrmxiCtyNCCmaz2VhdXQWXy6XDpNfrodFoYLXZUTw+pk222Z3lW3ca26rgSwzBwqIZAwMDlITMAVEwNTVFR5fEJpK8Qyp1AJvDVbrTeLenCmxgfiZ22+urCtWHyu7uLp2wVCpFKx0dHaFYLOLk5KT6Y9kgk89kb95ubK0BX7A8a+1qannRLeW0daj/rU51S3tn9dypfvDw0QiLxbpX/Z7FVK7e/AEj4Wf24/2f5AAAAABJRU5ErkJggg==') center left no-repeat!important;\npadding-left: 18px;\n}\n.linkify.gist {\nbackground: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAk1JREFUeNqUkzuIE1EUhv955MnsbB6r4kYQLUQQFncV3SnCIqJsoWGDYOGjsIiCtY2Kla1sjLBIsFFcXJC1kaSwENQmXUQSRSUSjCQSTCbkbR4z47lXEgtBNwcu3DNzvvO8R8jlcj7LshKmaWqYQERRTAmCcEru9/sJr9er0QF92BJMAVGr1TQ6CeZAc7lcGAwGkyQAxpTLZU0eDoc8crfbRTgcRjAYRCQSYSmi1WpxY7fbjU6ng1gshmaziXg8zhnGIpVKWbquW9ls1mLZsaMoiqWq6lgnBxY55He/328Vi0XOMFZmqVMD4fF4QBAajcY48khY9JE4HA4enTGMFVkaTHmy+ZzD/5NSqYSNB484w1h55ODO3TVu4FXcWDywl24Cmp0e1WBhyuWELAtIf/qKUrWOONmev3Lpt4NRCXq1gplpBS/v3cDc0nGg9h1o1ZkfwO4Atu1B8cM7HLt8k37V/y5B2b4bJxf2Y+7oEbyJrkMvUjki0YYJ03LidfQxAt4dOHdCw5RdGZcgGobBlQtnV/BDr1GfDai7ZiHZZRi9PoY/e5SCCTUwC9gk1GmMh5YWOcNYkR4Sv1y9uAJbYB82N57h4OnDmN7phjQ0qUkWRJuB+TMaPn/5iFfvv+Ha7eucYey4iWw8q6tRJJNJ3Fp7ClUawEkViBTfkCR0YUNTVHD/4Tpm/P4/U2CeKpUKfD4fJDIMhUKEhP45St50XedZyLQY6Xw+v8AUemVb2oNqtYpCocCWKi2TLLfb7ReZTGZ+kmUi7i2VvfxLgAEAZChMriPcl+IAAAAASUVORK5CYII=') center left no-repeat!important;\npadding-left: 18px;\n}\n.linkify.image {\nbackground: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAs5JREFUOE+lk/tvi1EYx98/xT8gW4REIpGFMEQWl2FiM9ZMZhm2xRAyOsmujFFmdFRHu0tWm87UypxStr69zPauN5e5rHVp3IYhbOvHy+wHEQlxkm+ek+d8nm9OznkeSfrfldmgJC7QyUlTymsJTfuTZ25z4HdWYwyLreYhtpgekGPw0+kKvo1Eo+IXRSIiEhkWZuc9tqnsJD9EqTUopCxjSGTpB0iueczSo1HyW8cpsExQ1DbxI2pt45j9cXpexul4FEd79RnZphAa/SD7WvuFtO6UItbU9LC+YQxNI2w0wwYT5LRAdhOU3oBTIXC9gXP3oUSGgz2vST3gYHejR0jptT1C332f8yrUEYHrz8CgxDnpm6DKCUfc0KnmXa/AEVPPwnDcD0cvetA2uYRk67Ive/lpjO7YBO1PPuF8Df3vwf4cbNE4tqdw7YVq8HYyHx6FvhE1hkMEg8HDUqvFkjT4aIjMqkqyqkswDSrcfBfH+Q561YLAZ/B+BLda6FXlU/cPv0AoEPhuoP1h4Av7Wbh9E/Py15NWWUjeSR3nZDfeN+N0DY9hG/7K1eGP3P0S5/EYRFUF/IOTBrUXHPm9fT6mr1xEwupkZqxbzLyiDJYUZ5NSnkdqdSHpxyrYdFpPgdmAsdfJwPMI/Yr65bf7tZLGGBQ7DNdJWFtIYvoOZmbuZE7OXpIKKli86zAr9p9gTVktWTVnKTI2U95uRWe3U2IJUDbVB5p6hVm5x5m9Vc/cnedZUNzC8lILaQesZBy6hEZ3maKzgvJWFzVWD9XtXvVGQbSWASFtMATVRlJIKbOTWtlJXaeXepuPM1f6MNp9GLt8mLvvYLmp0OhQ2Fwvk6m7xaqDTvY0eYWUVtcnllXfYlGpnfklVuraHHg8HjxuN+6fktUHlWWZPaZeUo/ILK0UKttBcbNbSB9GP0yLxWJJUxoZGUn80zD9C/vXQ/4NHY10h3M1zmQAAAAASUVORK5CYII=') center left no-repeat!important;\npadding-left: 18px;\n}\n.linkify.InstallGentoo {\nbackground: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAklJREFUOE9jYEAAASBTCorZkcSRmTjVCDLziCwG4hfM3EIvGNm44oC6WNEM4WXi5FsEkmfhFX3BxMmfAJSHW9Qr55Px3aZp3X/btq3/hQydPzKysMcCFbBBDeFj4uBdqBJR/gskb1W34j+PmulLoJwbzBJJoMm7dNO7/ntMP/XfpW/v//SKvk+7tl7fvXfTpx5pCdWVSiHFv1wnHQbLi9sE/Wdk5SwBauaCGQB3gUPb5v+7Lr/8/+fvr/9fv/z+f+Pyr/9bV735l9Wy/79Dx/b/Nk0bsLoAHgbeAVHv/v77/f8f0IB7N7+cu3DuecK54z9+7lzz639e9pK/7HwSWMMA5BJwCJeXtOm/fvVj1fcfv369f//92cN7X6ZcPvf9x6Htv//vXP3r/+T245UEYgpskPTNq08LgN749/PH7/93rv/6f/rw7//nj//4f+bU0zQcUQwWBkdVbGz62y+fv3wHeeXrlz//H9798//qpY//M3KqfzGxc8djiWKwZnBUuWQ2/fr46fv/P39+///x/ff/d69//z97+s7fyMb5/+y7d2GLYriDZikFF/1qXXXj/4Pbv/8/f/jn/5MH316/eP6jVlBAaIt6VO1/jxmn/zv27P7Pp2HxEajLD90ra9Sj6/979O37X73w0n+vqOL/0lJyMVBFq0EGgDSD0oKAlu1/oHg4ugGzVCKqfouYuL1Xj676Iajr8AnJFricGqYc3Bw+Zi6BVUxsXLHAdL6QiYMPFNrwpIxHDsUhgtAMAopKDjQn4pPDF7P45QC4hSmc1eX8WgAAAABJRU5ErkJggg==') center left no-repeat!important;\npadding-left: 18px;\n}\n\n/* General */\n:root.yotsuba .dialog {\nbackground-color: #F0E0D6;\nborder-color: #D9BFB7;\n}\n:root.yotsuba .field:focus {\nborder-color: #EA8;\n}\n\n/* Header */\n:root.yotsuba #header-bar, :root.yotsuba #notifications {\nfont-size: 9pt;\ncolor: #B86;\n}\n:root.yotsuba #header-bar a, :root.yotsuba #notifications a {\ncolor: #800000;\n}\n\n/* Settings */\n:root.yotsuba #fourchanx-settings fieldset {\nborder-color: #D9BFB7;\n}\n\n/* Quote */\n:root.yotsuba .backlink.deadlink {\ncolor: #00E !important;\n}\n:root.yotsuba .inline {\nborder-color: #D9BFB7;\nbackground-color: rgba(255, 255, 255, .14);\n}\n\n/* QR */\n.yotsuba #dump-list::-webkit-scrollbar-thumb {\nbackground-color: #F0E0D6;\nborder-color: #D9BFB7;\n}\n:root.yotsuba .qr-preview {\nbackground-color: rgba(0, 0, 0, .15);\n}\n\n/* Menu */\n:root.yotsuba #menu {\ncolor: #800000;\n}\n:root.yotsuba .entry {\nborder-bottom: 1px solid #D9BFB7;\nfont-size: 10pt;\n}\n:root.yotsuba .focused.entry {\nbackground: rgba(255, 255, 255, .33);\n}\n\n/* Watcher Favicon */\n:root.yotsuba .watch-thread-link\n{\nbackground-image: url(\"data:image/svg+xml,<svg viewBox='0 0 26 26' preserveAspectRatio='true' xmlns='http://www.w3.org/2000/svg'><path fill='rgb(128,0,0)' d='M24.132,7.971c-2.203-2.205-5.916-2.098-8.25,0.235L15.5,8.588l-0.382-0.382c-2.334-2.333-6.047-2.44-8.25-0.235c-2.204,2.203-2.098,5.916,0.235,8.249l8.396,8.396l8.396-8.396C26.229,13.887,26.336,10.174,24.132,7.971z'/></svg>\");\n}\n\n/* General */\n:root.yotsuba-b .dialog {\nbackground-color: #D6DAF0;\nborder-color: #B7C5D9;\n}\n:root.yotsuba-b .field:focus {\nborder-color: #98E;\n}\n\n/* Header */\n:root.yotsuba-b #header-bar, :root.yotsuba-b #notifications {\nfont-size: 9pt;\ncolor: #89A;\n}\n:root.yotsuba-b #header-bar a, :root.yotsuba-b #notifications a {\ncolor: #34345C;\n}\n\n/* Settings */\n:root.yotsuba-b #fourchanx-settings fieldset {\nborder-color: #B7C5D9;\n}\n\n/* Quote */\n:root.yotsuba-b .backlink.deadlink {\ncolor: #34345C !important;\n}\n:root.yotsuba-b .inline {\nborder-color: #B7C5D9;\nbackground-color: rgba(255, 255, 255, .14);\n}\n\n/* QR */\n.yotsuba-b #dump-list::-webkit-scrollbar-thumb {\nbackground-color: #D6DAF0;\nborder-color: #B7C5D9;\n}\n:root.yotsuba-b .qr-preview {\nbackground-color: rgba(0, 0, 0, .15);\n}\n\n/* Menu */\n:root.yotsuba-b #menu {\ncolor: #000;\n}\n:root.yotsuba-b .entry {\nborder-bottom: 1px solid #B7C5D9;\nfont-size: 10pt;\n}\n:root.yotsuba-b .focused.entry {\nbackground: rgba(255, 255, 255, .33);\n}\n\n/* Watcher Favicon */\n:root.yotsuba-b .watch-thread-link\n{\nbackground-image: url(\"data:image/svg+xml,<svg viewBox='0 0 26 26' preserveAspectRatio='true' xmlns='http://www.w3.org/2000/svg'><path fill='rgb(0,0,0)' d='M24.132,7.971c-2.203-2.205-5.916-2.098-8.25,0.235L15.5,8.588l-0.382-0.382c-2.334-2.333-6.047-2.44-8.25-0.235c-2.204,2.203-2.098,5.916,0.235,8.249l8.396,8.396l8.396-8.396C26.229,13.887,26.336,10.174,24.132,7.971z'/></svg>\");\n}\n\n/* General */\n:root.futaba .dialog {\nbackground-color: #F0E0D6;\nborder-color: #D9BFB7;\n}\n:root.futaba .field:focus {\nborder-color: #EA8;\n}\n\n/* Header */\n:root.futaba #header-bar, :root.futaba #notifications {\nfont-size: 11pt;\ncolor: #B86;\n}\n:root.futaba #header-bar a, :root.futaba #notifications a {\ncolor: #800000;\n}\n\n/* Settings */\n:root.futaba #fourchanx-settings fieldset {\nborder-color: #D9BFB7;\n}\n\n/* Quote */\n:root.futaba .backlink.deadlink {\ncolor: #00E !important;\n}\n:root.futaba .inline {\nborder-color: #D9BFB7;\nbackground-color: rgba(255, 255, 255, .14);\n}\n\n/* QR */\n.futaba #dump-list::-webkit-scrollbar-thumb {\nbackground-color: #F0E0D6;\nborder-color: #D9BFB7;\n}\n:root.futaba .qr-preview {\nbackground-color: rgba(0, 0, 0, .15);\n}\n\n/* Menu */\n:root.futaba #menu {\ncolor: #800000;\n}\n:root.futaba .entry {\nborder-bottom: 1px solid #D9BFB7;\nfont-size: 12pt;\n}\n:root.futaba .focused.entry {\nbackground: rgba(255, 255, 255, .33);\n}\n\n/* Watcher Favicon */\n:root.futaba .watch-thread-link\n{\nbackground-image: url(\"data:image/svg+xml,<svg viewBox='0 0 26 26' preserveAspectRatio='true' xmlns='http://www.w3.org/2000/svg'><path fill='rgb(128,0,0)' d='M24.132,7.971c-2.203-2.205-5.916-2.098-8.25,0.235L15.5,8.588l-0.382-0.382c-2.334-2.333-6.047-2.44-8.25-0.235c-2.204,2.203-2.098,5.916,0.235,8.249l8.396,8.396l8.396-8.396C26.229,13.887,26.336,10.174,24.132,7.971z'/></svg>\");\n}\n\n/* General */\n:root.burichan .dialog {\nbackground-color: #D6DAF0;\nborder-color: #B7C5D9;\n}\n:root.burichan .field:focus {\nborder-color: #98E;\n}\n\n/* Header */\n:root.burichan #header-bar, :root.burichan #header-bar #notifications {\nfont-size: 11pt;\ncolor: #89A;\n}\n:root.burichan #header-bar a, :root.burichan #header-bar #notifications a {\ncolor: #34345C;\n}\n\n/* Settings */\n:root.burichan #fourchanx-settings fieldset {\nborder-color: #B7C5D9;\n}\n\n/* Quote */\n:root.burichan .backlink.deadlink {\ncolor: #34345C !important;\n}\n:root.burichan .inline {\nborder-color: #B7C5D9;\nbackground-color: rgba(255, 255, 255, .14);\n}\n\n/* QR */\n.burichan #dump-list::-webkit-scrollbar-thumb {\nbackground-color: #D6DAF0;\nborder-color: #B7C5D9;\n}\n:root.burichan .qr-preview {\nbackground-color: rgba(0, 0, 0, .15);\n}\n\n/* Menu */\n:root.burichan #menu {\ncolor: #000000;\n}\n:root.burichan .entry {\nborder-bottom: 1px solid #B7C5D9;\nfont-size: 12pt;\n}\n:root.burichan .focused.entry {\nbackground: rgba(255, 255, 255, .33);\n}\n\n/* Watcher Favicon */\n:root.burichan .watch-thread-link\n{\nbackground-image: url(\"data:image/svg+xml,<svg viewBox='0 0 26 26' preserveAspectRatio='true' xmlns='http://www.w3.org/2000/svg'><path fill='rgb(0,0,0)' d='M24.132,7.971c-2.203-2.205-5.916-2.098-8.25,0.235L15.5,8.588l-0.382-0.382c-2.334-2.333-6.047-2.44-8.25-0.235c-2.204,2.203-2.098,5.916,0.235,8.249l8.396,8.396l8.396-8.396C26.229,13.887,26.336,10.174,24.132,7.971z'/></svg>\");\n}\n\n/* General */\n:root.tomorrow .dialog {\nbackground-color: #282A2E;\nborder-color: #111;\n}\n\n/* Header */\n:root.tomorrow #header-bar, :root.tomorrow #notifications {\nfont-size: 9pt;\ncolor: #C5C8C6;\n}\n:root.tomorrow #header-bar a, :root.tomorrow #notifications a {\ncolor: #81A2BE;\n}\n\n/* Settings */\n:root.tomorrow #fourchanx-settings fieldset {\nborder-color: #111;\n}\n\n/* Quote */\n:root.tomorrow .backlink.deadlink {\ncolor: #81A2BE !important;\n}\n:root.tomorrow .inline {\nborder-color: #111;\nbackground-color: rgba(0, 0, 0, .14);\n}\n\n/* QR */\n.tomorrow #dump-list::-webkit-scrollbar-thumb {\nbackground-color: #282A2E;\nborder-color: #111;\n}\n:root.tomorrow .qr-preview {\nbackground-color: rgba(255, 255, 255, .15);\n}\n:root.tomorrow #qr .field {\nbackground-color: rgb(26, 27, 29);\ncolor: rgb(197,200,198);\nborder-color: rgb(40, 41, 42);\n}\n:root.tomorrow #qr .field:focus {\nborder-color: rgb(129, 162, 190) !important;\nbackground-color: rgb(30,32,36);\n}\n\n/* Menu */\n:root.tomorrow #menu {\ncolor: #C5C8C6;\n}\n:root.tomorrow .entry {\nborder-bottom: 1px solid #111;\nfont-size: 10pt;\n}\n:root.tomorrow .focused.entry {\nbackground: rgba(0, 0, 0, .33);\n}\n\n/* Watcher Favicon */\n:root.tomorrow .watch-thread-link\n{\nbackground-image: url(\"data:image/svg+xml,<svg viewBox='0 0 26 26' preserveAspectRatio='true' xmlns='http://www.w3.org/2000/svg'><path fill='rgb(197,200,198)' d='M24.132,7.971c-2.203-2.205-5.916-2.098-8.25,0.235L15.5,8.588l-0.382-0.382c-2.334-2.333-6.047-2.44-8.25-0.235c-2.204,2.203-2.098,5.916,0.235,8.249l8.396,8.396l8.396-8.396C26.229,13.887,26.336,10.174,24.132,7.971z'/></svg>\");\n}\n\n/* General */\n:root.photon .dialog {\nbackground-color: #DDD;\nborder-color: #CCC;\n}\n:root.photon .field:focus {\nborder-color: #EA8;\n}\n\n/* Header */\n:root.photon #header-bar, :root.photon #notifications {\nfont-size: 9pt;\ncolor: #333;\n}\n:root.photon #header-bar a, :root.photon #notifications a {\ncolor: #FF6600;\n}\n\n/* Settings */\n:root.photon #fourchanx-settings fieldset {\nborder-color: #CCC;\n}\n\n/* Quote */\n:root.photon .backlink.deadlink {\ncolor: #F60 !important;\n}\n:root.photon .inline {\nborder-color: #CCC;\nbackground-color: rgba(255, 255, 255, .14);\n}\n\n/* QR */\n.photon #dump-list::-webkit-scrollbar-thumb {\nbackground-color: #DDD;\nborder-color: #CCC;\n}\n:root.photon .qr-preview {\nbackground-color: rgba(0, 0, 0, .15);\n}\n\n/* Menu */\n:root.photon #menu {\ncolor: #333;\n}\n:root.photon .entry {\nborder-bottom: 1px solid #CCC;\nfont-size: 10pt;\n}\n:root.photon .focused.entry {\nbackground: rgba(255, 255, 255, .33);\n}\n\n/* Watcher Favicon */\n:root.photon .watch-thread-link\n{\nbackground-image: url(\"data:image/svg+xml,<svg viewBox='0 0 26 26' preserveAspectRatio='true' xmlns='http://www.w3.org/2000/svg'><path fill='rgb(51,51,51)' d='M24.132,7.971c-2.203-2.205-5.916-2.098-8.25,0.235L15.5,8.588l-0.382-0.382c-2.334-2.333-6.047-2.44-8.25-0.235c-2.204,2.203-2.098,5.916,0.235,8.249l8.396,8.396l8.396-8.396C26.229,13.887,26.336,10.174,24.132,7.971z'/></svg>\");\n}\n" + css: "/* General */\n.dialog {\nbox-shadow: 0 1px 2px rgba(0, 0, 0, .15);\nborder: 1px solid;\ndisplay: block;\npadding: 0;\n}\n.captcha-img,\n.field {\nbackground-color: #FFF;\nborder: 1px solid #CCC;\n-moz-box-sizing: border-box;\nbox-sizing: border-box;\ncolor: #333;\nfont: 13px sans-serif;\noutline: none;\ntransition: color .25s, border-color .25s;\ntransition: color .25s, border-color .25s;\n}\n.field::-moz-placeholder,\n.field:hover::-moz-placeholder {\ncolor: #AAA !important;\nfont-size: 13px !important;\nopacity: 1.0 !important;\n}\n.captch-img:hover,\n.field:hover {\nborder-color: #999;\n}\n.field:hover, .field:focus {\ncolor: #000;\n}\n.field[disabled] {\nbackground-color: #F2F2F2;\ncolor: #888;\n}\n.move {\ncursor: move;\noverflow: hidden;\n}\nlabel, .favicon {\ncursor: pointer;\n}\na[href=\"javascript:;\"] {\ntext-decoration: none;\n}\n.warning {\ncolor: red;\n}\n#boardNavDesktop {\ndisplay: none !important;\n}\na {\noutline: none !important;\n}\n\n/* 4chan style fixes */\n.opContainer, .op {\ndisplay: block !important;\noverflow: visible !important;\n}\n[hidden] {\ndisplay: none !important;\n}\n\n/* fixed, z-index */\n#overlay,\n#fourchanx-settings,\n#qp, #ihover,\n#navlinks, .fixed #header-bar,\n:root.float #updater,\n:root.float #thread-stats,\n#qr {\nposition: fixed;\n}\n#fourchanx-settings {\nz-index: 999;\n}\n#overlay {\nz-index: 900;\n}\n#notifications {\nz-index: 70;\n}\n#qp, #ihover {\nz-index: 60;\n}\n#menu {\nz-index: 50;\n}\n#navlinks, #updater, #thread-stats {\nz-index: 40;\n}\n.fixed #header-bar.autohide {\nz-index: 35;\n}\n#qr {\nz-index: 30;\n}\n#watcher {\nz-index: 8;\n}\n:root.fixed-watcher #watcher {\nz-index: 20;\n}\n.fixed #header-bar {\nz-index: 10;\n}\n/* Header */\n.fixed.top body {\npadding-top: 2em;\n}\n.fixed.bottom body {\npadding-bottom: 2em;\n}\n.fixed #header-bar {\nright: 0;\nleft: 0;\npadding: 3px 4px 4px;\n}\n.fixed.top #header-bar {\ntop: 0;\n}\n.fixed.bottom #header-bar {\nbottom: 0;\n}\n#header-bar {\nborder-width: 0;\ntransition: all .1s .05s ease-in-out;\n}\n:root.centered-links #shortcuts {\nwidth: 300px;\ntext-align: right;\n}\n:root.centered-links #header-bar {\ntext-align: center;\n}\n:root.centered-links #custom-board-list {\nposition: relative;\nleft: 150px;\n}\n.fixed.top #header-bar {\nborder-bottom-width: 1px;\n}\n.fixed.bottom #header-bar {\nbox-shadow: 0 -1px 2px rgba(0, 0, 0, .15);\nborder-top-width: 1px;\n}\n.fixed.bottom #header-bar .menu-button i {\nborder-top: none;\nborder-bottom: 6px solid;\n}\n#board-list {\ntext-align: center;\n}\n.fixed #header-bar.autohide:not(:hover) {\nbox-shadow: none;\ntransition: all .8s .6s cubic-bezier(.55, .055, .675, .19);\n}\n.fixed.top #header-bar.autohide:not(:hover) {\nmargin-bottom: -1em;\n-webkit-transform: translateY(-100%);\ntransform: translateY(-100%);\n}\n.fixed.bottom #header-bar.autohide:not(:hover) {\n-webkit-transform: translateY(100%);\ntransform: translateY(100%);\n}\n#scroll-marker {\nleft: 0;\nright: 0;\nheight: 10px;\nposition: absolute;\n}\n:root:not(.autohide) #scroll-marker {\npointer-events: none;\n}\n#header-bar #scroll-marker {\ndisplay: none;\n}\n.fixed #header-bar #scroll-marker {\ndisplay: block;\n}\n.fixed.top #header-bar #scroll-marker {\ntop: 100%;\n}\n.fixed.bottom #header-bar #scroll-marker {\nbottom: 100%;\n}\n#header-bar a:not(.entry):not(.close) {\ntext-decoration: none;\npadding: 1px;\n}\n#header-bar input {\nmargin: 0;\nvertical-align: bottom;\n}\n#shortcuts:empty {\ndisplay: none;\n}\n.brackets-wrap::before {\ncontent: \"\\00a0[\";\n}\n.brackets-wrap::after {\ncontent: \"]\\00a0\";\n}\n.disabled,\n.expand-all-shortcut {\nopacity: .45;\n}\n#shortcuts {\nfloat: right;\n}\n.shortcut {\nmargin-left: 3px;\n}\n#navbotright,\n#navtopright {\ndisplay: none;\n}\n#toggleMsgBtn {\ndisplay: none !important;\n}\n.current {\nfont-weight: bold;\n}\n/* 4chan X link brackets */\n.fourchanx-link::after {\ncontent: \"]\";\n}\n.fourchanx-link::before {\ncontent: \"[\";\n}\n/* Notifications */\n#notifications {\nposition: fixed;\ntop: 0;\nheight: 0;\ntext-align: center;\nright: 0;\nleft: 0;\ntransition: all .8s .6s cubic-bezier(.55, .055, .675, .19);\n}\n.fixed.top #header-bar #notifications {\nposition: absolute;\ntop: 100%;\n}\n.notification {\ncolor: #FFF;\nfont-weight: 700;\ntext-shadow: 0 1px 2px rgba(0, 0, 0, .5);\nbox-shadow: 0 1px 2px rgba(0, 0, 0, .15);\nborder-radius: 2px;\nmargin: 1px auto;\nwidth: 500px;\nmax-width: 100%;\nposition: relative;\ntransition: all .25s ease-in-out;\n}\n.notification.error {\nbackground-color: hsla(0, 100%, 38%, .9);\n}\n.notification.warning {\nbackground-color: hsla(36, 100%, 38%, .9);\n}\n.notification.info {\nbackground-color: hsla(200, 100%, 38%, .9);\n}\n.notification.success {\nbackground-color: hsla(104, 100%, 38%, .9);\n}\n.notification a {\ncolor: white;\n}\n.notification > .close {\npadding: 6px;\ntop: 0;\nright: 5px;\nposition: absolute;\n}\n.message {\n-moz-box-sizing: border-box;\nbox-sizing: border-box;\npadding: 6px 20px;\nmax-height: 200px;\nwidth: 100%;\noverflow: auto;\n}\n\n/* Settings */\n:root.fourchan-x body {\n-moz-box-sizing: border-box;\nbox-sizing: border-box;\n}\n#overlay {\nbackground-color: rgba(0, 0, 0, .5);\ntop: 0;\nleft: 0;\nheight: 100%;\nwidth: 100%;\n}\n#fourchanx-settings {\n-moz-box-sizing: border-box;\nbox-sizing: border-box;\nbox-shadow: 0 0 15px rgba(0, 0, 0, .15);\nheight: 600px;\nmax-height: 100%;\nwidth: 900px;\nmax-width: 100%;\nmargin: auto;\npadding: 3px;\ntop: 50%;\nleft: 50%;\n-moz-transform: translate(-50%, -50%);\n-webkit-transform: translate(-50%, -50%);\ntransform: translate(-50%, -50%);\n}\n#fourchanx-settings > nav {\npadding: 2px 2px 0;\nheight: 15px;\n}\n#fourchanx-settings > nav a {\ntext-decoration: underline;\n}\n#fourchanx-settings > nav a.close {\ntext-decoration: none;\npadding: 2px;\n}\n.section-container {\noverflow: auto;\nposition: absolute;\ntop: 2.1em;\nright: 5px;\nbottom: 5px;\nleft: 5px;\npadding-right: 5px;\n}\n.sections-list {\npadding: 0 3px;\nfloat: left;\n}\n.credits {\nfloat: right;\n}\n.tab-selected {\nfont-weight: 700;\n}\n.section-sauce ul,\n.section-advanced ul {\nlist-style: none;\nmargin: 0;\n}\n.section-sauce ul {\npadding: 8px;\n}\n.section-advanced ul {\npadding: 0px;\n}\n.section-sauce li,\n.section-advanced li {\npadding-left: 4px;\n}\n.section-main label {\ntext-decoration: underline;\n}\n.section-filter ul {\npadding: 0;\n}\n.section-filter li {\nmargin: 10px 40px;\n}\n.section-filter textarea {\nheight: 500px;\n}\n.section-sauce textarea {\nheight: 350px;\n}\n.section-advanced .field[name=\"boardnav\"] {\nwidth: 100%;\n}\n.section-advanced textarea {\nheight: 150px;\n}\n.section-advanced .archive-cell {\nmin-width: 160px;\ntext-align: center;\n}\n.section-advanced #archive-board-select {\nposition: absolute;\n}\n.section-advanced .note {\nfont-size: 0.8em;\nfont-style: italic;\nmargin-left: 10px;\n}\n.section-advanced .note code {\nfont-style: normal;\nfont-size: 11px;\n}\n.section-keybinds .field {\nfont-family: monospace;\n} \n#fourchanx-settings fieldset {\nborder: 1px solid;\nborder-radius: 3px;\n}\n#fourchanx-settings legend {\nfont-weight: 700;\n}\n#fourchanx-settings textarea {\nfont-family: monospace;\nmin-width: 100%;\nmax-width: 100%;\n}\n#fourchanx-settings code {\ncolor: #000;\nbackground-color: #FFF;\npadding: 0 2px;\n}\n.unscroll {\noverflow: hidden;\n}\n\n/* Announcement Hiding */\n:root.hide-announcement #globalMessage {\ndisplay: none;\n}\na.hide-announcement {\nfloat: left;\n}\n\n/* Unread */\n#unread-line {\nmargin: 0;\nborder-color: rgb(255,0,0);\n}\n\n/* Thread Updater */\n#updater {\nbackground: none;\nborder: none;\nbox-shadow: none;\n}\n#updater > .move {\npadding: 5px 3px 0px;\nmargin-bottom: -3px;\n}\n#updater > div:last-child {\ntext-align: center;\n}\n#updater input[type=number] {\nwidth: 4em;\n}\n:root.float #updater {\npadding: 0px 3px;\n}\n.new {\ncolor: limegreen;\n}\n#update-status.new {\nmargin-right: 5px;\n}\n#update-timer {\ncursor: pointer;\n}\n\n/* Thread Watcher */\n#watcher {\nposition: absolute;\n}\n#watcher {\npadding-bottom: 3px;\noverflow: hidden;\nwhite-space: nowrap;\nmin-width: 120px;\nmax-height: 92%;\noverflow-y: auto;\n}\n:root.fixed-watcher #watcher {\nposition: fixed;\n}\n:root:not(.fixed-watcher) #watcher:not(:hover) {\nmax-height: 210px;\noverflow-y: hidden;\n}\n#watcher > .move {\npadding-top: 3px;\n}\n#watcher > div {\nmax-width: 250px;\noverflow: hidden;\npadding-left: 3px;\npadding-right: 3px;\ntext-overflow: ellipsis;\n}\n#watcher a {\ntext-decoration: none;\n}\n#watcher .move>.close {\nposition: absolute;\nright: 0px;\ntop: 0px;\npadding: 0px 4px;\n}\n.watch-thread-link {\npadding-top: 18px;\nwidth: 18px;\nheight: 0px;\ndisplay: inline-block;\nbackground-repeat: no-repeat;\nopacity: 0.2;\nposition: relative;\ntop: 1px;\n}\n.watch-thread-link.watched {\nopacity: 1;\n}\n\n/* Thread Stats */\n#thread-stats {\nbackground: none;\nborder: none;\nbox-shadow: none;\n}\n:root.float #post-count, :root.float #file-count {\npointer-events: none;\n}\n:root.float #thread-stats {\npadding: 0px 3px;\n}\n\n/* Quote */\n.deadlink {\ntext-decoration: none !important;\n}\n.backlink.deadlink:not(.forwardlink),\n.quotelink.deadlink:not(.forwardlink) {\ntext-decoration: underline !important;\n}\n.inlined {\nopacity: .5;\n}\n#qp input, .forwarded {\ndisplay: none;\n}\n.quotelink.forwardlink,\n.backlink.forwardlink {\ntext-decoration: none;\nborder-bottom: 1px dashed;\n}\n.filtered {\ntext-decoration: underline line-through;\n}\n:root.hide-backlinks .backlink.filtered {\ndisplay: none;\n}\n.inline {\nborder: 1px solid;\ndisplay: table;\nmargin: 2px 0;\n}\n.inline .post {\nborder: 0 !important;\nbackground-color: transparent !important;\ndisplay: table !important;\nmargin: 0 !important;\npadding: 1px 2px !important;\n}\n#qp > .opContainer::after {\ncontent: '';\nclear: both;\ndisplay: table;\n}\n#qp .post {\nborder: none;\nmargin: 0;\npadding: 2px 2px 5px;\n}\n#qp img {\nmax-height: 80vh;\nmax-width: 50vw;\n}\n.qphl {\noutline: 2px solid rgba(216, 94, 49, .7);\n}\n:root.highlight-own .yourPost > .reply,\n:root.highlight-you .quotesYou > .reply {\nborder-left: 2px solid rgba(221,0,0,.5);\n}\n/* Quote Threading */\n.threadContainer {\nmargin-left: 20px;\nborder-left: 1px solid rgba(128,128,128,.3);\n}\n.threadOP {\nclear: both;\n} \n\n/* File */\n.fileText:hover .fntrunc,\n.fileText:not(:hover) .fnfull,\n.expanded-image > .post > .file > .fileThumb > img[data-md5],\n:not(.expanded-image) > .post > .file > .fileThumb > .full-image {\ndisplay: none;\n}\n.expanding {\nopacity: .5;\n}\n:root.fit-height .full-image {\nmax-height: 100vh;\n}\n:root.fit-width .full-image {\nmax-width: 100%;\n}\n:root.gecko.fit-width .full-image {\nwidth: 100%;\n}\n#ihover {\n-moz-box-sizing: border-box;\nbox-sizing: border-box;\nmax-height: 100%;\nmax-width: 75%;\npadding-bottom: 16px;\n}\n.fappeTyme .thread > .noFile,\n.fappeTyme .threadContainer > .noFile {\ndisplay: none;\n}\n\n/* Index/Reply Navigation */\n#navlinks {\nfont-size: 16px;\ntop: 25px;\nright: 10px;\n}\n\n/* Filter */\n.opContainer.filter-highlight {\nbox-shadow: inset 5px 0 rgba(255, 0, 0, .5);\n}\n.filter-highlight > .reply {\nbox-shadow: -5px 0 rgba(255, 0, 0, .5);\n}\n\n/* Spoiler text */\n:root.reveal-spoilers s {\ncolor: white !important;\n}\n\n/* Thread & Reply Hiding */\n.hide-thread-button,\n.hide-reply-button {\nfloat: left;\nmargin-right: 2px;\n}\n.stub ~ * {\ndisplay: none !important;\n}\n.stub input {\ndisplay: inline-block;\n}\n\n/* QR */\n:root.hide-original-post-form #postForm,\n:root.hide-original-post-form .postingMode,\n:root.hide-original-post-form #togglePostForm,\n#qr.autohide:not(.has-focus):not(:hover) > form,\n.postingMode ~ #qr select,\n#file-n-submit:not(.has-file) #qr-filerm {\ndisplay: none;\n}\n#qr select,\n#dump-button,\n.remove,\n.captcha-img {\ncursor: pointer;\n}\n#qr {\nz-index: 20;\nposition: fixed;\npadding: 1px;\nborder: 1px solid transparent;\nmin-width: 300px;\nborder-radius: 3px 3px 0 0;\n}\n#qrtab {\nborder-radius: 3px 3px 0 0;\n}\n#qrtab {\nmargin-bottom: 1px;\n}\n#qr .close {\nfloat: right;\npadding: 0 3px;\n}\n#qr .warning {\nmin-height: 1.6em;\nvertical-align: middle;\npadding: 0 1px;\nborder-width: 1px;\nborder-style: solid;\n}\n.qr-link-container {\ntext-align: center;\n}\n.persona {\nwidth: 248px;\nmax-width: 100%;\nmin-width: 100%;\n}\n#dump-button {\nwidth: 10%;\nmargin: 0;\nmargin-right: 4px;\nfont: 13px sans-serif;\npadding: 1px 0px 2px;\nopacity: 0.6;\n}\n.persona .field:not(#dump) {\nwidth: 95px;\nmin-width: 33.3%;\nmax-width: 33.3%;\n}\n#qr textarea.field {\nheight: 14.8em;\nmin-height: 9em;\n}\n#qr.has-captcha textarea.field {\nheight: 9em;\n}\ninput.field.tripped:not(:hover):not(:focus) {\ncolor: transparent !important;\ntext-shadow: none !important;\n}\n#qr textarea {\nresize: both;\n}\n.captcha-img {\nmargin: 0px;\ntext-align: center;\nbackground-image: #fff;\nfont-size: 0px;\nmin-height: 59px;\nmin-width: 302px;\n}\n.captcha-input {\nwidth: 100%;\nmargin: 1px 0 0;\n}\n.captcha-input.error:focus {\nborder-color: rgb(255,0,0) !important;\n}\n.field {\n-moz-box-sizing: border-box;\nmargin: 0px;\npadding: 2px 4px 3px;\n}\n#qr textarea {\nmin-width: 100%;\n}\n#qr [type='submit'] {\nwidth: 25%;\nvertical-align: top;\n}\n:root.webkit #qr [type='submit'] {\nheight: 24px;\n}\n/* Fake File Input */\n#qr-filename,\n.has-file #qr-no-file {\ndisplay: none;\n}\n#qr-no-file,\n.has-file #qr-filename {\ndisplay: inline-block;\npadding: 0px 4px;\nmargin-bottom: 2px;\noverflow: hidden;\ntext-overflow: ellipsis;\nmax-width: 88%;\n}\n#qr-no-file {\ncolor: #AAA;\n}\n#qr-filename-container {\n-moz-box-sizing: border-box;\ndisplay: inline-block;\nposition: relative;\nwidth: 100px;\nmin-width: 74.6%;\nmax-width: 74.6%;\nmargin-right: 0.4%;\nmargin-top: 1px;\noverflow: hidden;\npadding: 2px 1px 0;\nheight: 22px;\n}\n#qr-filename-container:hover {\ncursor: text;\n}\n#qr-extras-container {\nposition: absolute;\nright: 0px;\n}\n#qr-filerm {\nmargin-right: 2px;\nz-index: 2;\n}\n#file-n-submit {\nheight: 23px;\n}\n#qr input[type=file] {\nvisibility: hidden;\nposition: absolute;\n}\n/* Thread Select / Spoiler Label */\n#qr select {\nfloat: right;\n}\n#qr.has-spoiler .has-file #qr-spoiler-label {\nwidth: 6.7%;\nmin-width: 6.7%;\nmax-width: 6.7%;\ndisplay: inline-block;\ntext-align: center;\nvertical-align: top;\n}\n#qr.has-spoiler #file-n-submit:not(.has-file) #qr-spoiler-label {\ndisplay: none;\n}\n#qr.has-spoiler .has-file #qr-filename-container {\nmax-width: 67.9%;\nmin-width: 67.9%;\n}\n#qr-spoiler-label input {\nposition: relative;\ntop: 3px;\n}\n/* Dumping UI */\n.dump #dump-list-container {\ndisplay: block;\n}\n#dump-list-container {\ndisplay: none;\nposition: relative;\noverflow-y: hidden;\nmargin-top: 1px;\n}\n#dump-list {\noverflow-x: auto;\noverflow-y: hidden;\nwhite-space: nowrap;\nwidth: 248px;\nmax-width: 100%;\nmin-width: 100%;\n}\n#dump-list:hover {\noverflow-x: auto;\n}\n.qr-preview {\n-moz-box-sizing: border-box;\ncounter-increment: thumbnails;\ncursor: move;\ndisplay: inline-block;\nheight: 90px;\nwidth: 90px;\npadding: 2px;\nopacity: .5;\noverflow: hidden;\nposition: relative;\ntext-shadow: 0 1px 1px #000;\n-moz-transition: opacity .25s ease-in-out;\nvertical-align: top;\nbackground-size: cover;\n}\n.qr-preview:hover,\n.qr-preview:focus {\nopacity: .9;\n}\n.qr-preview::before {\ncontent: counter(thumbnails);\ncolor: #fff;\nposition: absolute;\ntop: 3px;\nright: 3px;\ntext-shadow: 0 0 3px #000, 0 0 8px #000;\n}\n.qr-preview#selected {\nopacity: 1;\n}\n.qr-preview.drag {\nbox-shadow: 0 0 10px rgba(0,0,0,.5);\n}\n.qr-preview.over {\nborder-color: #fff;\n}\n.qr-preview > span {\ncolor: #fff;\n}\n.remove {\nbackground: none;\ncolor: #e00;\nfont-weight: 700;\npadding: 3px;\n}\na:only-of-type > .remove {\ndisplay: none;\n}\n.remove:hover::after {\ncontent: \" Remove\";\n}\n.qr-preview > label {\nbackground: rgba(0,0,0,.5);\ncolor: #fff;\nright: 0;\nbottom: 0;\nleft: 0;\nposition: absolute;\ntext-align: center;\n}\n.qr-preview > label > input {\nmargin: 0;\n}\n#add-post {\ncursor: pointer;\nfont-size: 2em;\nposition: absolute;\ntop: 50%;\nright: 10px;\n-moz-transform: translateY(-50%);\n}\n.textarea {\nposition: relative;\n}\n:root.webkit .textarea {\nmargin-bottom: -2px;\n}\n#char-count {\ncolor: #000;\nbackground: hsla(0, 0%, 100%, .5);\nfont-size: 8pt;\nposition: absolute;\nbottom: 1px;\nright: 1px;\npointer-events: none;\n}\n\n/* Menu */\n.menu-button {\ndisplay: inline-block;\nposition: relative;\ncursor: pointer;\n}\n.menu-button i {\nborder-top: 6px solid;\nborder-right: 4px solid transparent;\nborder-left: 4px solid transparent;\ndisplay: inline-block;\nmargin: 2px;\nvertical-align: middle;\n}\n#menu {\nposition: fixed;\noutline: none;\n}\n.entry {\nborder-bottom: 1px solid rgba(0,0,0,.25);\ncursor: pointer;\ndisplay: block;\noutline: none;\npadding: 3px 7px;\nposition: relative;\ntext-decoration: none;\nwhite-space: nowrap;\n}\n.left>.entry.has-submenu {\npadding-right: 17px !important;\n}\n.entry:last-child {\nborder-bottom: 0;\n}\n.has-submenu::after {\ncontent: \"\";\nborder-left: .5em solid;\nborder-top: .3em solid transparent;\nborder-bottom: .3em solid transparent;\ndisplay: inline-block;\nmargin: .3em;\nposition: absolute;\nright: 3px;\n}\n.left .has-submenu::after {\nborder-left: 0;\nborder-right: .5em solid;\n}\n.submenu {\ndisplay: none;\nposition: absolute;\nleft: 100%;\ntop: -1px;\n}\n.focused .submenu {\ndisplay: block;\n}\n.imp-exp-result {\nposition: absolute;\ntext-align: center;\nmargin: auto;\nright: 0px;\nleft: 0px;\nwidth: 200px;\n}\n.export, .import {\ncursor: pointer;\ntext-decoration: none !important;\n}\n/* Link Title Favicons */\n.linkify.YouTube {\nbackground: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAMCAYAAABr5z2BAAABIklEQVQoz53LvUrDUBjG8bOoOammSf1IoBSvoCB4JeIqOHgBLt6AIMRBBQelWurQ2kERnMRBsBUcIp5FJSBI5oQsJVkkUHh8W0o5nhaFHvjBgef/Mq+Q46RJBMkI/vE+aOus956tnEswIZe1LV0QyJ5sE2GzgZfVMtRNIdiDpccEssdlB1mW4bvTwdvWJtRdErM7U+8S/FJykCRJX5qm+KpVce8UMNLRLbulz4iSjTAMh6Iowsd5BeNadp3nUF0VlxAEwZBotXC0Usa4ll3meZdA1iguwvf9vpvDA2wvmKgYGtSud8suDB4TyGr2PF49D/vra9jRZ1BVdknMzgwuCGSnZEObwu6sBnVTCHZiaC7BhFx2PKdxUidiAH/4lLo9Mv0DELVs9qsOHXwAAAAASUVORK5CYII=') center left no-repeat!important;\npadding-left: 18px;\n}\n.linkify.Vimeo {\nbackground: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAASJJREFUOE9jYAAC7ln7/pODQXrBmq333PvPu/YaSRikB6QXbACpmmHqsRoAMll7+20UQ0H8tmuv/pdffPFfZtNNuByGASBFIPDh5x+4IV6HHoDFYGDJgw+YBoBMBUkgA5BtIKduuvvy//svX+FSB+88wTTAc+/t/83bj/0HScLA5BPXwc7lKJ36f+L6XXDxhUfOYxrAPWUnWKFp9UQUm3iWQxSDXAEDSX3zcIcB96wD/x+8eA1XDNKMHAYg20GW4Y0FkCIYAAUqzEBQOIBciRzlWKMxZelOlMCEcVxq+jHSC1YDJPs3YBgA8jey0/F6ARRwsFAHORukmat9NdbUijMpg/wKcrJodDFOzSBXwA3Alh9AToZFI7a8Asu98BxJbnYGAJb5vYLDANzSAAAAAElFTkSuQmCC') center left no-repeat!important;\npadding-left: 18px;\n}\n.linkify.SoundCloud {\nbackground: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABsklEQVQ4y5WTy2pUQRCGv2rbzDjJeAlIBmOyipGIIJqFEBDElwh4yULGeRFXPoEIBl/AvQ/gC2RnxCAoxijiwks852S6+3dxzslcHJCpTXVX11/Xv0097gLPgVNMJxnQNfX4zsqleWbnpoMf/oa9d988MM9MC/rp+E0a+A0dsVobMNMCOO8B6McRoABJI+A6gJmN3D2A8jgEBCEkSEMBrcrsDAzDWWn3AjgKFaDMmgRqniGFgsaDp1jrLOngDf1XT1D+A1dFc4MKAkkiCVKjjVu7g9+4Rzx4i1u6hjXbuMWr0O5QPNvCu7IaCZwEKQukLGDrm5x8uI0tr6MkiGlkiv7yLfzN+6S5i6QsIMABkEfcxhbWWYMkVAOjxvYAjc3HNHrbKI9VBQBFwF25XQKSBjqIf1YBuAurEMrczgDygD6/x2LCpFLXLUyQ+PoldphhBhYfIX09XU1+Flaukz7uYqs3SHs7cG4BmTsmkBUF9mmXEwa28BNLPaQPLepuNcbGSWQquQC2/Kdcox1FUGkcB0ykck1nA2+wTzMs8stGnP4rbWGw74EuS/GFQWfK7/wF6P4F7fzIAYkdmdEAAAAASUVORK5CYII=') center left no-repeat!important;\npadding-left: 18px;\n}\n.linkify.audio {\nbackground: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAitJREFUOE9jYCAWKJWwavr0KyXWb/FIbDtUFFyzJx6nVofE2Xo5nXsj0rqPNSR0nVkR2Hjmgmfd+U9Otdf+m5Vf/6+SfeU/R9ChVVgNYDRtlfJuuPA/rPfe/4QpD/6nznj0P27Kw/9unff/69Xf+69c/+C/SO7N/0z+OAxgMmmRCe++/r9i3ev/KWvf/vdY8PK/bt/9/wrNV3/IN5y/IVt1YqNg4pGTTP4HsbuA2bhZ2qvpyn+xjIObxAp3VwqlrgngLFyryVy5nhPmZJHANS2cwYexG8BmVC/pWn3hP4NZlzWuQDJI3dIiFnUUuwEsQAOcq87jNcC7fHeLUtJxHF4AGmBWeAavAWH1+1rUUk7giAWjOknllON4DXAs2NEiG4/DBQxAF/CFHfrPYI4jDFSLuJVjNrUJhB/B7gIGo1pJRt99GAZYJK7wLJ1z7Xzl4vu/7aqv/GRBj0bjqAX2qb0nJ7mXH17C4HcUxQA+hymWtSue/C5a9up/9Ozn/7Vr7v1nRY7GqMb91T3b3v6vWvPmf/S0p/9ZQk+DDLCBRSOz06Jqk+o7/21nvfqvsebDf7kZL/5zBaxphkezd+OFn7HzXvz3Wvjmv9a8N//5Ek//ZTBpVYUrMG2X5wjcdl68+uI/wa5Lr3hSNjczGFeywOVZ/bbcVGp//F9izfv/Ql03f3P4LC/HSEQquYwMFnUCDJ7dzBhyjGZNQpye89M5gpfnMvtNUyE2h4PUAQBovvT7lyNljwAAAABJRU5ErkJggg==') center left no-repeat!important;\npadding-left: 18px;\n}\n.linkify.LiveLeak {\nbackground: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAydJREFUOE9Nk1tIk2EYx79NyUNqTk0o6KYrnZeChodLDxfeZpCbJk4RXU5Nm7tYRYhiYXbQlaeGutyW2gxtpB1RIyKDEjKwA6Ti2dR5KNDn+fq/S6TBj/f93r3P732e53s/qfnkSdej4GB2SBLbwf+jmB+gUMgOheLg/z7EdCUnO6Ref392SpK8Hyh3I+gBwBo7lUp2xcbyQEoKD6alyQOpqd754/h4FjJXZCRJTl9ftmEzoK5/wdQJxPgkLY2WV1dpc2uLtnZ2eHNnhza3t2nd46GhjAzuValY6jx0iIfS03msoIDuQ9COQCtoUSjohU5HuwgaN5loeXycd3d3aW9vzwvW2K5SkdTi58fvzGb+3tdHFggA3QONEAzn59PvjQ1yqNX0zenkvX0B4ffWaGRraChJd/385JGqKvlzTw/fRqOaIGkEd1DjU52O/3g83BkTw5MOh7yJuUCUM2o0yi2hoSw1IIOhykr+YLNRHYKu4XQvyKA/N5c8yMCCDD7Z7bz26xcJ1rH2rKKCG0UJdRAMlJbyG6uVrkJQjWAB5tSbk0Nr2HwDgvcQiIYur6zQyvo6ucvLueHIEZKuQPBQr+dXra1kRuqXEOwFArtWSytra1QdFUVjNhvPLS3R3OIiLUDUD0F1WBhJJtwDW2Ehu5uaqBICI4IFlRB0QLCEzaboaHrd0cHzCBYsIIuesjK+LAQXkEFrXh676uupGCWcR6AeghLQptGQONUAwfOuLp6Zn6eZuTmaXVig7pISrhI90ENgQbdHhoep32JhFzLpu3WLio8epUYIfs7OUjF6UKJW88XERLqYkEBNej11oG8XhCAvMFAuOn5cNiclsTkhQTbhmpri4lgbEMANWi1DwC/xit3t7bK7rY0Fo4OD3G4wyEURESzloAdnceezlErK8vH5N4KzPj50PTOTfkxP0+THj/RlYoInJyZI8HVqim5qNFwQHk7SucBAPo2PKRMNPLM/4pnFszYkhJsNBu6uqWFHba1sr61lQSveQFZQkFx07BhJmhMnrLn4NLMPH/aSExR0QDbmWhwgyEapwDvXoDxdWBiXnjrV/Bdm2kYUxLwmEgAAAABJRU5ErkJggg==') center left no-repeat!important;\npadding-left: 18px;\n}\n.linkify.Vocaroo {\nbackground: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAw9JREFUOE9jYMABuMwYmCyTJKUCGlSnFSy02TTzeOyCiQcDViX26qVz2TAyYtWmEMwuoZ3M7V40LcB79pHkc0svpvzY8jD//87nxf+3Pyn8v/ZO8v+VNyP/2mZJumI1QCWSI8232Hjumitlfw5+qPp/9l8TCt76JP//xkdx/wsXWCzjtWFkwTCkbWFe9plPk/+ga4Txz/xt/D/hkN//gMXif21a+NbyWjIwoRiy6GDT5rP/mlFsPfyp5n/NpOj/22+0gMUXXIz/H7hC/L/bFKFbPDZMrHAD5H35OPt2J9zacDv/f3V7xv9FhwrBGubsT/1//Pjx/1GJ/mD+/nfl/1v3Ovy3KRJNQbHdOlXCvOO03/+pm1P/v3v37n90hhtYw9HPtf8Xb2v937cmHswHeWPRxYj/LvkK3igGKARwicTO07118H3V/5kbi/4vPZMJtK3s/6YH2f+Pfq1B8VbjWrdnMu5s4nAD9CNFhKwz5DTUvLl419zKvAcLtG1P84BRl/b/5M/6/6f/NPzf/qzo84yj0Uus0xUU4Zor54bm9+4OfZG02OCuoAMTb9ZkC9ull1Nvrr2Z+XvRpaRfc65H/68F+jl9svEhzyLFWoccWVc+eyTHq/twydjlKRln7jX9bNMkMJnbhoFRL1xCqmKx6/yi2fYXa/c5/e846PV/5fW0/7OPx/yfcjzop34ulxdGGvDuU8mMXaX507lBuiN6ueadmQeT/p/93vf/1O+G//sP5fw/eL3o/5JLif8zVxs+Tlir9S26UyeFQQvJGBE7FvaFZ9LfN+1y+WjbItSb3GmXvXd15v8zroH/HxgE/D+aGPx/18vi/z07PeZNPRKxe/Kh0Ae8toxscCO4zBkYXArk9C1SxJUYjBkYPPIVtbbuTftz3cz//2O9wP/75iSAXdO72/dt2HL5F6YlfBW4MiJYXMiBiW3t7azHBx+V/t89N+H/8a+1//e9K/9attDp5LQjYX8SuvVL8RoAkmxa65299Erq1FnHo0qrl7t4BddriIs4MrM3rfWcFd+pGwVSAwBZ0bKP8yrZPAAAAABJRU5ErkJggg==') center left no-repeat!important;\npadding-left: 18px;\n}\n.linkify.pastebin {\nbackground: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAtZJREFUOE+NU91LWmEc7sJtQew/2MUY7INg7CLY3W5GMHazyzEQo9UmfYxZTbAiVlgRqLMSZ+XnDC3z2+Y0+8JGakKZTtR0Tl2wtgtLLQh29cz3ZZ3h3Q68vOc95zzP73l+z+/U1f292O09DRxubxOH23P//1bvtQts3dPnry7LZnXJhcUl5Avf8dHtwY+fv2AyW5DOfIXFakMm+w0G4wISyRRm55TQG0y/Wzv6mikJ52Xf9TmVBoFAAD6fDwqFAqFQCJubmzCbzZiensbp6SmkUikikQi0Wi0kEgm6ewVaStDCfXPDandifn6egoaGhrCzswO1Wg2Hw4HBwUGk02kIBAL4/X4IhUJMTk6ii8dfYggy2RwymQzOz88Rj8dRLpexv7+PSqWCYDCIQqGAra0tJBIJrK2t0XdVAjNDEIl+wfj4OEqlEq2wt7dHrchkMmrBYDCAz+fTIjweD7FYrJbgIJOlgLOzM8jlcip1eXmZ2rFarVAqlRCLxcjlchCJRFRljYJYPAG32418Pg+n04lsNouVlRUcHh7C4/FQIOlHNBqlezgcJgQWxkIgGMbExASVNjY2hvX1dVo9mUzS5wREFLhcLrqTcw2B//M2RkdHodPp4PV6oVKpqH+SCom3v7+fNnF4eJiJusbCJ6+PviSyScakiaR5RIHRaKQpmEwmbAdCeD8zB6vdhebHT8SMhcUlC83bbrdTJRsbG3RwiCVCRNJJpDIoVeNNJJJQzKryV+rrmxiCtyNCCmaz2VhdXQWXy6XDpNfrodFoYLXZUTw+pk222Z3lW3ca26rgSwzBwqIZAwMDlITMAVEwNTVFR5fEJpK8Qyp1AJvDVbrTeLenCmxgfiZ22+urCtWHyu7uLp2wVCpFKx0dHaFYLOLk5KT6Y9kgk89kb95ubK0BX7A8a+1qannRLeW0daj/rU51S3tn9dypfvDw0QiLxbpX/Z7FVK7e/AEj4Wf24/2f5AAAAABJRU5ErkJggg==') center left no-repeat!important;\npadding-left: 18px;\n}\n.linkify.gist {\nbackground: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAk1JREFUeNqUkzuIE1EUhv955MnsbB6r4kYQLUQQFncV3SnCIqJsoWGDYOGjsIiCtY2Kla1sjLBIsFFcXJC1kaSwENQmXUQSRSUSjCQSTCbkbR4z47lXEgtBNwcu3DNzvvO8R8jlcj7LshKmaWqYQERRTAmCcEru9/sJr9er0QF92BJMAVGr1TQ6CeZAc7lcGAwGkyQAxpTLZU0eDoc8crfbRTgcRjAYRCQSYSmi1WpxY7fbjU6ng1gshmaziXg8zhnGIpVKWbquW9ls1mLZsaMoiqWq6lgnBxY55He/328Vi0XOMFZmqVMD4fF4QBAajcY48khY9JE4HA4enTGMFVkaTHmy+ZzD/5NSqYSNB484w1h55ODO3TVu4FXcWDywl24Cmp0e1WBhyuWELAtIf/qKUrWOONmev3Lpt4NRCXq1gplpBS/v3cDc0nGg9h1o1ZkfwO4Atu1B8cM7HLt8k37V/y5B2b4bJxf2Y+7oEbyJrkMvUjki0YYJ03LidfQxAt4dOHdCw5RdGZcgGobBlQtnV/BDr1GfDai7ZiHZZRi9PoY/e5SCCTUwC9gk1GmMh5YWOcNYkR4Sv1y9uAJbYB82N57h4OnDmN7phjQ0qUkWRJuB+TMaPn/5iFfvv+Ha7eucYey4iWw8q6tRJJNJ3Fp7ClUawEkViBTfkCR0YUNTVHD/4Tpm/P4/U2CeKpUKfD4fJDIMhUKEhP45St50XedZyLQY6Xw+v8AUemVb2oNqtYpCocCWKi2TLLfb7ReZTGZ+kmUi7i2VvfxLgAEAZChMriPcl+IAAAAASUVORK5CYII=') center left no-repeat!important;\npadding-left: 18px;\n}\n.linkify.image {\nbackground: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAs5JREFUOE+lk/tvi1EYx98/xT8gW4REIpGFMEQWl2FiM9ZMZhm2xRAyOsmujFFmdFRHu0tWm87UypxStr69zPauN5e5rHVp3IYhbOvHy+wHEQlxkm+ek+d8nm9OznkeSfrfldmgJC7QyUlTymsJTfuTZ25z4HdWYwyLreYhtpgekGPw0+kKvo1Eo+IXRSIiEhkWZuc9tqnsJD9EqTUopCxjSGTpB0iueczSo1HyW8cpsExQ1DbxI2pt45j9cXpexul4FEd79RnZphAa/SD7WvuFtO6UItbU9LC+YQxNI2w0wwYT5LRAdhOU3oBTIXC9gXP3oUSGgz2vST3gYHejR0jptT1C332f8yrUEYHrz8CgxDnpm6DKCUfc0KnmXa/AEVPPwnDcD0cvetA2uYRk67Ive/lpjO7YBO1PPuF8Df3vwf4cbNE4tqdw7YVq8HYyHx6FvhE1hkMEg8HDUqvFkjT4aIjMqkqyqkswDSrcfBfH+Q561YLAZ/B+BLda6FXlU/cPv0AoEPhuoP1h4Av7Wbh9E/Py15NWWUjeSR3nZDfeN+N0DY9hG/7K1eGP3P0S5/EYRFUF/IOTBrUXHPm9fT6mr1xEwupkZqxbzLyiDJYUZ5NSnkdqdSHpxyrYdFpPgdmAsdfJwPMI/Yr65bf7tZLGGBQ7DNdJWFtIYvoOZmbuZE7OXpIKKli86zAr9p9gTVktWTVnKTI2U95uRWe3U2IJUDbVB5p6hVm5x5m9Vc/cnedZUNzC8lILaQesZBy6hEZ3maKzgvJWFzVWD9XtXvVGQbSWASFtMATVRlJIKbOTWtlJXaeXepuPM1f6MNp9GLt8mLvvYLmp0OhQ2Fwvk6m7xaqDTvY0eYWUVtcnllXfYlGpnfklVuraHHg8HjxuN+6fktUHlWWZPaZeUo/ILK0UKttBcbNbSB9GP0yLxWJJUxoZGUn80zD9C/vXQ/4NHY10h3M1zmQAAAAASUVORK5CYII=') center left no-repeat!important;\npadding-left: 18px;\n}\n.linkify.InstallGentoo {\nbackground: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAklJREFUOE9jYEAAASBTCorZkcSRmTjVCDLziCwG4hfM3EIvGNm44oC6WNEM4WXi5FsEkmfhFX3BxMmfAJSHW9Qr55Px3aZp3X/btq3/hQydPzKysMcCFbBBDeFj4uBdqBJR/gskb1W34j+PmulLoJwbzBJJoMm7dNO7/ntMP/XfpW/v//SKvk+7tl7fvXfTpx5pCdWVSiHFv1wnHQbLi9sE/Wdk5SwBauaCGQB3gUPb5v+7Lr/8/+fvr/9fv/z+f+Pyr/9bV735l9Wy/79Dx/b/Nk0bsLoAHgbeAVHv/v77/f8f0IB7N7+cu3DuecK54z9+7lzz639e9pK/7HwSWMMA5BJwCJeXtOm/fvVj1fcfv369f//92cN7X6ZcPvf9x6Htv//vXP3r/+T245UEYgpskPTNq08LgN749/PH7/93rv/6f/rw7//nj//4f+bU0zQcUQwWBkdVbGz62y+fv3wHeeXrlz//H9798//qpY//M3KqfzGxc8djiWKwZnBUuWQ2/fr46fv/P39+///x/ff/d69//z97+s7fyMb5/+y7d2GLYriDZikFF/1qXXXj/4Pbv/8/f/jn/5MH316/eP6jVlBAaIt6VO1/jxmn/zv27P7Pp2HxEajLD90ra9Sj6/979O37X73w0n+vqOL/0lJyMVBFq0EGgDSD0oKAlu1/oHg4ugGzVCKqfouYuL1Xj676Iajr8AnJFricGqYc3Bw+Zi6BVUxsXLHAdL6QiYMPFNrwpIxHDsUhgtAMAopKDjQn4pPDF7P45QC4hSmc1eX8WgAAAABJRU5ErkJggg==') center left no-repeat!important;\npadding-left: 18px;\n}\n\n/* General */\n:root.yotsuba .dialog {\nbackground-color: #F0E0D6;\nborder-color: #D9BFB7;\n}\n:root.yotsuba .field:focus {\nborder-color: #EA8;\n}\n\n/* Header */\n:root.yotsuba #header-bar, :root.yotsuba #notifications {\nfont-size: 9pt;\ncolor: #B86;\n}\n:root.yotsuba #header-bar a, :root.yotsuba #notifications a {\ncolor: #800000;\n}\n\n/* Settings */\n:root.yotsuba #fourchanx-settings fieldset {\nborder-color: #D9BFB7;\n}\n\n/* Quote */\n:root.yotsuba .backlink.deadlink {\ncolor: #00E !important;\n}\n:root.yotsuba .inline {\nborder-color: #D9BFB7;\nbackground-color: rgba(255, 255, 255, .14);\n}\n\n/* QR */\n.yotsuba #dump-list::-webkit-scrollbar-thumb {\nbackground-color: #F0E0D6;\nborder-color: #D9BFB7;\n}\n:root.yotsuba .qr-preview {\nbackground-color: rgba(0, 0, 0, .15);\n}\n\n/* Menu */\n:root.yotsuba #menu {\ncolor: #800000;\n}\n:root.yotsuba .entry {\nborder-bottom: 1px solid #D9BFB7;\nfont-size: 10pt;\n}\n:root.yotsuba .focused.entry {\nbackground: rgba(255, 255, 255, .33);\n}\n\n/* Watcher Favicon */\n:root.yotsuba .watch-thread-link\n{\nbackground-image: url(\"data:image/svg+xml,<svg viewBox='0 0 26 26' preserveAspectRatio='true' xmlns='http://www.w3.org/2000/svg'><path fill='rgb(128,0,0)' d='M24.132,7.971c-2.203-2.205-5.916-2.098-8.25,0.235L15.5,8.588l-0.382-0.382c-2.334-2.333-6.047-2.44-8.25-0.235c-2.204,2.203-2.098,5.916,0.235,8.249l8.396,8.396l8.396-8.396C26.229,13.887,26.336,10.174,24.132,7.971z'/></svg>\");\n}\n\n/* General */\n:root.yotsuba-b .dialog {\nbackground-color: #D6DAF0;\nborder-color: #B7C5D9;\n}\n:root.yotsuba-b .field:focus {\nborder-color: #98E;\n}\n\n/* Header */\n:root.yotsuba-b #header-bar, :root.yotsuba-b #notifications {\nfont-size: 9pt;\ncolor: #89A;\n}\n:root.yotsuba-b #header-bar a, :root.yotsuba-b #notifications a {\ncolor: #34345C;\n}\n\n/* Settings */\n:root.yotsuba-b #fourchanx-settings fieldset {\nborder-color: #B7C5D9;\n}\n\n/* Quote */\n:root.yotsuba-b .backlink.deadlink {\ncolor: #34345C !important;\n}\n:root.yotsuba-b .inline {\nborder-color: #B7C5D9;\nbackground-color: rgba(255, 255, 255, .14);\n}\n\n/* QR */\n.yotsuba-b #dump-list::-webkit-scrollbar-thumb {\nbackground-color: #D6DAF0;\nborder-color: #B7C5D9;\n}\n:root.yotsuba-b .qr-preview {\nbackground-color: rgba(0, 0, 0, .15);\n}\n\n/* Menu */\n:root.yotsuba-b #menu {\ncolor: #000;\n}\n:root.yotsuba-b .entry {\nborder-bottom: 1px solid #B7C5D9;\nfont-size: 10pt;\n}\n:root.yotsuba-b .focused.entry {\nbackground: rgba(255, 255, 255, .33);\n}\n\n/* Watcher Favicon */\n:root.yotsuba-b .watch-thread-link\n{\nbackground-image: url(\"data:image/svg+xml,<svg viewBox='0 0 26 26' preserveAspectRatio='true' xmlns='http://www.w3.org/2000/svg'><path fill='rgb(0,0,0)' d='M24.132,7.971c-2.203-2.205-5.916-2.098-8.25,0.235L15.5,8.588l-0.382-0.382c-2.334-2.333-6.047-2.44-8.25-0.235c-2.204,2.203-2.098,5.916,0.235,8.249l8.396,8.396l8.396-8.396C26.229,13.887,26.336,10.174,24.132,7.971z'/></svg>\");\n}\n\n/* General */\n:root.futaba .dialog {\nbackground-color: #F0E0D6;\nborder-color: #D9BFB7;\n}\n:root.futaba .field:focus {\nborder-color: #EA8;\n}\n\n/* Header */\n:root.futaba #header-bar, :root.futaba #notifications {\nfont-size: 11pt;\ncolor: #B86;\n}\n:root.futaba #header-bar a, :root.futaba #notifications a {\ncolor: #800000;\n}\n\n/* Settings */\n:root.futaba #fourchanx-settings fieldset {\nborder-color: #D9BFB7;\n}\n\n/* Quote */\n:root.futaba .backlink.deadlink {\ncolor: #00E !important;\n}\n:root.futaba .inline {\nborder-color: #D9BFB7;\nbackground-color: rgba(255, 255, 255, .14);\n}\n\n/* QR */\n.futaba #dump-list::-webkit-scrollbar-thumb {\nbackground-color: #F0E0D6;\nborder-color: #D9BFB7;\n}\n:root.futaba .qr-preview {\nbackground-color: rgba(0, 0, 0, .15);\n}\n\n/* Menu */\n:root.futaba #menu {\ncolor: #800000;\n}\n:root.futaba .entry {\nborder-bottom: 1px solid #D9BFB7;\nfont-size: 12pt;\n}\n:root.futaba .focused.entry {\nbackground: rgba(255, 255, 255, .33);\n}\n\n/* Watcher Favicon */\n:root.futaba .watch-thread-link\n{\nbackground-image: url(\"data:image/svg+xml,<svg viewBox='0 0 26 26' preserveAspectRatio='true' xmlns='http://www.w3.org/2000/svg'><path fill='rgb(128,0,0)' d='M24.132,7.971c-2.203-2.205-5.916-2.098-8.25,0.235L15.5,8.588l-0.382-0.382c-2.334-2.333-6.047-2.44-8.25-0.235c-2.204,2.203-2.098,5.916,0.235,8.249l8.396,8.396l8.396-8.396C26.229,13.887,26.336,10.174,24.132,7.971z'/></svg>\");\n}\n\n/* General */\n:root.burichan .dialog {\nbackground-color: #D6DAF0;\nborder-color: #B7C5D9;\n}\n:root.burichan .field:focus {\nborder-color: #98E;\n}\n\n/* Header */\n:root.burichan #header-bar, :root.burichan #header-bar #notifications {\nfont-size: 11pt;\ncolor: #89A;\n}\n:root.burichan #header-bar a, :root.burichan #header-bar #notifications a {\ncolor: #34345C;\n}\n\n/* Settings */\n:root.burichan #fourchanx-settings fieldset {\nborder-color: #B7C5D9;\n}\n\n/* Quote */\n:root.burichan .backlink.deadlink {\ncolor: #34345C !important;\n}\n:root.burichan .inline {\nborder-color: #B7C5D9;\nbackground-color: rgba(255, 255, 255, .14);\n}\n\n/* QR */\n.burichan #dump-list::-webkit-scrollbar-thumb {\nbackground-color: #D6DAF0;\nborder-color: #B7C5D9;\n}\n:root.burichan .qr-preview {\nbackground-color: rgba(0, 0, 0, .15);\n}\n\n/* Menu */\n:root.burichan #menu {\ncolor: #000000;\n}\n:root.burichan .entry {\nborder-bottom: 1px solid #B7C5D9;\nfont-size: 12pt;\n}\n:root.burichan .focused.entry {\nbackground: rgba(255, 255, 255, .33);\n}\n\n/* Watcher Favicon */\n:root.burichan .watch-thread-link\n{\nbackground-image: url(\"data:image/svg+xml,<svg viewBox='0 0 26 26' preserveAspectRatio='true' xmlns='http://www.w3.org/2000/svg'><path fill='rgb(0,0,0)' d='M24.132,7.971c-2.203-2.205-5.916-2.098-8.25,0.235L15.5,8.588l-0.382-0.382c-2.334-2.333-6.047-2.44-8.25-0.235c-2.204,2.203-2.098,5.916,0.235,8.249l8.396,8.396l8.396-8.396C26.229,13.887,26.336,10.174,24.132,7.971z'/></svg>\");\n}\n\n/* General */\n:root.tomorrow .dialog {\nbackground-color: #282A2E;\nborder-color: #111;\n}\n\n/* Header */\n:root.tomorrow #header-bar, :root.tomorrow #notifications {\nfont-size: 9pt;\ncolor: #C5C8C6;\n}\n:root.tomorrow #header-bar a, :root.tomorrow #notifications a {\ncolor: #81A2BE;\n}\n\n/* Settings */\n:root.tomorrow #fourchanx-settings fieldset {\nborder-color: #111;\n}\n\n/* Quote */\n:root.tomorrow .backlink.deadlink {\ncolor: #81A2BE !important;\n}\n:root.tomorrow .inline {\nborder-color: #111;\nbackground-color: rgba(0, 0, 0, .14);\n}\n\n/* QR */\n.tomorrow #dump-list::-webkit-scrollbar-thumb {\nbackground-color: #282A2E;\nborder-color: #111;\n}\n:root.tomorrow .qr-preview {\nbackground-color: rgba(255, 255, 255, .15);\n}\n:root.tomorrow #qr .field {\nbackground-color: rgb(26, 27, 29);\ncolor: rgb(197,200,198);\nborder-color: rgb(40, 41, 42);\n}\n:root.tomorrow #qr .field:focus {\nborder-color: rgb(129, 162, 190) !important;\nbackground-color: rgb(30,32,36);\n}\n\n/* Menu */\n:root.tomorrow #menu {\ncolor: #C5C8C6;\n}\n:root.tomorrow .entry {\nborder-bottom: 1px solid #111;\nfont-size: 10pt;\n}\n:root.tomorrow .focused.entry {\nbackground: rgba(0, 0, 0, .33);\n}\n\n/* Watcher Favicon */\n:root.tomorrow .watch-thread-link\n{\nbackground-image: url(\"data:image/svg+xml,<svg viewBox='0 0 26 26' preserveAspectRatio='true' xmlns='http://www.w3.org/2000/svg'><path fill='rgb(197,200,198)' d='M24.132,7.971c-2.203-2.205-5.916-2.098-8.25,0.235L15.5,8.588l-0.382-0.382c-2.334-2.333-6.047-2.44-8.25-0.235c-2.204,2.203-2.098,5.916,0.235,8.249l8.396,8.396l8.396-8.396C26.229,13.887,26.336,10.174,24.132,7.971z'/></svg>\");\n}\n\n/* General */\n:root.photon .dialog {\nbackground-color: #DDD;\nborder-color: #CCC;\n}\n:root.photon .field:focus {\nborder-color: #EA8;\n}\n\n/* Header */\n:root.photon #header-bar, :root.photon #notifications {\nfont-size: 9pt;\ncolor: #333;\n}\n:root.photon #header-bar a, :root.photon #notifications a {\ncolor: #FF6600;\n}\n\n/* Settings */\n:root.photon #fourchanx-settings fieldset {\nborder-color: #CCC;\n}\n\n/* Quote */\n:root.photon .backlink.deadlink {\ncolor: #F60 !important;\n}\n:root.photon .inline {\nborder-color: #CCC;\nbackground-color: rgba(255, 255, 255, .14);\n}\n\n/* QR */\n.photon #dump-list::-webkit-scrollbar-thumb {\nbackground-color: #DDD;\nborder-color: #CCC;\n}\n:root.photon .qr-preview {\nbackground-color: rgba(0, 0, 0, .15);\n}\n\n/* Menu */\n:root.photon #menu {\ncolor: #333;\n}\n:root.photon .entry {\nborder-bottom: 1px solid #CCC;\nfont-size: 10pt;\n}\n:root.photon .focused.entry {\nbackground: rgba(255, 255, 255, .33);\n}\n\n/* Watcher Favicon */\n:root.photon .watch-thread-link\n{\nbackground-image: url(\"data:image/svg+xml,<svg viewBox='0 0 26 26' preserveAspectRatio='true' xmlns='http://www.w3.org/2000/svg'><path fill='rgb(51,51,51)' d='M24.132,7.971c-2.203-2.205-5.916-2.098-8.25,0.235L15.5,8.588l-0.382-0.382c-2.334-2.333-6.047-2.44-8.25-0.235c-2.204,2.203-2.098,5.916,0.235,8.249l8.396,8.396l8.396-8.396C26.229,13.887,26.336,10.174,24.132,7.971z'/></svg>\");\n}\n" }; Main.init(); diff --git a/src/General/Config.coffee b/src/General/Config.coffee index 36ae0def2..dff2c4219 100644 --- a/src/General/Config.coffee +++ b/src/General/Config.coffee @@ -641,6 +641,14 @@ q-replace 'x' 'Hide thread.' ] + 'Previous Post Quoting You': [ + 'Alt+Up' + 'Scroll to the previous post that quotes you.' + ] + 'Next Post Quoting You': [ + 'Alt+Down' + 'Scroll to the next post that quotes you.' + ] updater: checkbox: diff --git a/src/General/css/style.css b/src/General/css/style.css index 6d418f4d1..dbed7eeb0 100644 --- a/src/General/css/style.css +++ b/src/General/css/style.css @@ -583,8 +583,8 @@ a.hide-announcement { .qphl { outline: 2px solid rgba(216, 94, 49, .7); } -:root.highlight-own .yourPost>.reply, -:root.highlight-you .quotesYou>.reply { +:root.highlight-own .yourPost > .reply, +:root.highlight-you .quotesYou > .reply { border-left: 2px solid rgba(221,0,0,.5); } /* Quote Threading */ diff --git a/src/Miscellaneous/Keybinds.coffee b/src/Miscellaneous/Keybinds.coffee index 4832510e6..336f711a1 100644 --- a/src/Miscellaneous/Keybinds.coffee +++ b/src/Miscellaneous/Keybinds.coffee @@ -115,6 +115,10 @@ Keybinds = Keybinds.hl 0, threadRoot when Conf['Hide'] ThreadHiding.toggle thread if g.VIEW is 'index' + when Conf['Previous Post Quoting You'] + QuoteYou.cb.seek 'preceding' + when Conf['Next Post Quoting You'] + QuoteYou.cb.seek 'following' else return e.preventDefault() diff --git a/src/Monitoring/Unread.coffee b/src/Monitoring/Unread.coffee index 4ee9e6f90..67ad8664a 100644 --- a/src/Monitoring/Unread.coffee +++ b/src/Monitoring/Unread.coffee @@ -126,6 +126,9 @@ Unread = {bottom} = post.nodes.root.getBoundingClientRect() if bottom < height # post is completely read {ID} = post + if Conf['Mark Quotes of You'] + if post.info.yours + QuoteYou.lastRead = post.nodes.root if Conf['Quote Threading'] posts.splice i, 1 continue diff --git a/src/Quotelinks/QuoteYou.coffee b/src/Quotelinks/QuoteYou.coffee index 31848acd2..c8a69684c 100644 --- a/src/Quotelinks/QuoteYou.coffee +++ b/src/Quotelinks/QuoteYou.coffee @@ -14,6 +14,7 @@ QuoteYou = Post::callbacks.push name: 'Mark Quotes of You' cb: @node + node: -> # Stop there if it's a clone. return if @isClone @@ -28,4 +29,28 @@ QuoteYou = if QR.db.get Get.postDataFromLink quotelink $.add quotelink, $.tn '\u00A0(You)' $.addClass @nodes.root, 'quotesYou' - return \ No newline at end of file + return + + cb: + seek: (type) -> + unless QuoteYou.lastRead + unless post = QuoteYou.lastRead = $ '.quotesYou' + new Notification 'warning', 'No posts are currently quoting you, loser.', 20 + return if QuoteYou.cb.scroll post + + str = "#{type}::div[contains(@class,'quotesYou')]" + + result = $.X(str, QuoteYou.lastRead) + while post = result.snapshotItem(if type is 'preceding' then result.snapshotLength - 1 else 0) + return if QuoteYou.cb.scroll post + + posts = $$ '.quotesYou' + QuoteYou.cb.scroll posts[if type is 'following' then 0 else posts.length - 1] + + scroll: (post) -> + QuoteYou.lastRead = post + if Get.postFromRoot(post).isHidden + return false + else + Header.scrollToPost post + return true \ No newline at end of file From 00fc8013bf80e7eb52cbb9cf1275533d2ac0a6ed Mon Sep 17 00:00:00 2001 From: Zixaphir <zixaphirmoxphar@gmail.com> Date: Thu, 8 Aug 2013 23:55:25 -0700 Subject: [PATCH 149/172] Changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2094d851c..92f9c1686 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - Linkifier Rewrite. - Fix Quote Threading toggle. - Added Twitch.tv and Vine embedding (with @ihavenoface) +- Keybinds to scroll to posts that quote you. - Minor optimizations. - Minor fixes. From c438ab6c3b8dc6d7bf4d084756b81d04ca1946a6 Mon Sep 17 00:00:00 2001 From: Zixaphir <zixaphirmoxphar@gmail.com> Date: Fri, 9 Aug 2013 01:05:10 -0700 Subject: [PATCH 150/172] Highlight posts that quote you when scrolling to them update window.location because lolwhynot --- LICENSE | 2 +- builds/4chan-X.user.js | 13 +++++++++---- builds/crx/script.js | 13 +++++++++---- src/Quotelinks/QuoteYou.coffee | 12 +++++++++--- 4 files changed, 28 insertions(+), 12 deletions(-) diff --git a/LICENSE b/LICENSE index 8986054f8..b6e66b546 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,5 @@ /* -* 4chan X - Version 1.2.25 - 2013-08-08 +* 4chan X - Version 1.2.25 - 2013-08-09 * * Licensed under the MIT license. * https://github.com/seaweedchan/4chan-x/blob/master/LICENSE diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js index 51cb3c167..971e68444 100644 --- a/builds/4chan-X.user.js +++ b/builds/4chan-X.user.js @@ -19,7 +19,7 @@ // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwAgMAAAAqbBEUAAAACVBMVEUAAGcAAABmzDNZt9VtAAAAAXRSTlMAQObYZgAAAHFJREFUKFOt0LENACEIBdBv4Qju4wgWanEj3D6OcIVMKaitYHEU/jwTCQj8W75kiVCSBvdQ5/AvfVHBin11BgdRq3ysBgfwBDRrj3MCIA+oAQaku/Q1cNctrAmyDl577tOThYt/Y1RBM4DgOHzM0HFTAyLukH/cmRnqAAAAAElFTkSuQmCC // ==/UserScript== /* -* 4chan X - Version 1.2.25 - 2013-08-08 +* 4chan X - Version 1.2.25 - 2013-08-09 * * Licensed under the MIT license. * https://github.com/seaweedchan/4chan-x/blob/master/LICENSE @@ -4206,14 +4206,17 @@ if (!QuoteYou.lastRead) { if (!(post = QuoteYou.lastRead = $('.quotesYou'))) { new Notification('warning', 'No posts are currently quoting you, loser.', 20); + return; } if (QuoteYou.cb.scroll(post)) { return; } + } else { + post = QuoteYou.lastRead; } + $.rmClass($('.highlight'), 'highlight'); str = "" + type + "::div[contains(@class,'quotesYou')]"; - result = $.X(str, QuoteYou.lastRead); - while (post = result.snapshotItem(type === 'preceding' ? result.snapshotLength - 1 : 0)) { + while (post = (result = $.X(str, post)).snapshotItem(type === 'preceding' ? result.snapshotLength - 1 : 0)) { if (QuoteYou.cb.scroll(post)) { return; } @@ -4222,11 +4225,13 @@ return QuoteYou.cb.scroll(posts[type === 'following' ? 0 : posts.length - 1]); }, scroll: function(post) { - QuoteYou.lastRead = post; if (Get.postFromRoot(post).isHidden) { return false; } else { + QuoteYou.lastRead = post; + window.location = "#" + post.id; Header.scrollToPost(post); + $.addClass($('.post', post), 'highlight'); return true; } } diff --git a/builds/crx/script.js b/builds/crx/script.js index 656b8d531..8aecb2dd6 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -1,6 +1,6 @@ // Generated by CoffeeScript /* -* 4chan X - Version 1.2.25 - 2013-08-08 +* 4chan X - Version 1.2.25 - 2013-08-09 * * Licensed under the MIT license. * https://github.com/seaweedchan/4chan-x/blob/master/LICENSE @@ -4211,14 +4211,17 @@ if (!QuoteYou.lastRead) { if (!(post = QuoteYou.lastRead = $('.quotesYou'))) { new Notification('warning', 'No posts are currently quoting you, loser.', 20); + return; } if (QuoteYou.cb.scroll(post)) { return; } + } else { + post = QuoteYou.lastRead; } + $.rmClass($('.highlight'), 'highlight'); str = "" + type + "::div[contains(@class,'quotesYou')]"; - result = $.X(str, QuoteYou.lastRead); - while (post = result.snapshotItem(type === 'preceding' ? result.snapshotLength - 1 : 0)) { + while (post = (result = $.X(str, post)).snapshotItem(type === 'preceding' ? result.snapshotLength - 1 : 0)) { if (QuoteYou.cb.scroll(post)) { return; } @@ -4227,11 +4230,13 @@ return QuoteYou.cb.scroll(posts[type === 'following' ? 0 : posts.length - 1]); }, scroll: function(post) { - QuoteYou.lastRead = post; if (Get.postFromRoot(post).isHidden) { return false; } else { + QuoteYou.lastRead = post; + window.location = "#" + post.id; Header.scrollToPost(post); + $.addClass($('.post', post), 'highlight'); return true; } } diff --git a/src/Quotelinks/QuoteYou.coffee b/src/Quotelinks/QuoteYou.coffee index c8a69684c..3cf656e80 100644 --- a/src/Quotelinks/QuoteYou.coffee +++ b/src/Quotelinks/QuoteYou.coffee @@ -36,21 +36,27 @@ QuoteYou = unless QuoteYou.lastRead unless post = QuoteYou.lastRead = $ '.quotesYou' new Notification 'warning', 'No posts are currently quoting you, loser.', 20 + return return if QuoteYou.cb.scroll post + else + post = QuoteYou.lastRead + + $.rmClass $('.highlight'), 'highlight' str = "#{type}::div[contains(@class,'quotesYou')]" - result = $.X(str, QuoteYou.lastRead) - while post = result.snapshotItem(if type is 'preceding' then result.snapshotLength - 1 else 0) + while post = (result = $.X(str, post)).snapshotItem(if type is 'preceding' then result.snapshotLength - 1 else 0) return if QuoteYou.cb.scroll post posts = $$ '.quotesYou' QuoteYou.cb.scroll posts[if type is 'following' then 0 else posts.length - 1] scroll: (post) -> - QuoteYou.lastRead = post if Get.postFromRoot(post).isHidden return false else + QuoteYou.lastRead = post + window.location = "##{post.id}" Header.scrollToPost post + $.addClass $('.post', post), 'highlight' return true \ No newline at end of file From 97f7cca2b1690d41159ef9c92a521850ae1ff968 Mon Sep 17 00:00:00 2001 From: Zixaphir <zixaphirmoxphar@gmail.com> Date: Fri, 9 Aug 2013 01:08:39 -0700 Subject: [PATCH 151/172] Just a couple checks --- builds/4chan-X.user.js | 5 +++-- builds/crx/script.js | 5 +++-- src/Quotelinks/QuoteYou.coffee | 7 ++++--- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js index 971e68444..00e4422d2 100644 --- a/builds/4chan-X.user.js +++ b/builds/4chan-X.user.js @@ -4161,7 +4161,7 @@ QuoteYou = { init: function() { - if (g.VIEW === 'catalog' || !Conf['Mark Quotes of You'] || !Conf['Quick Reply']) { + if (!(g.VIEW !== 'catalog' && Conf['Mark Quotes of You'] && Conf['Quick Reply'])) { return; } if (Conf['Highlight Own Posts']) { @@ -4203,6 +4203,8 @@ seek: function(type) { var post, posts, result, str; + return unlses(Conf['Mark Quotes of You'] && Conf['Quick Reply']); + $.rmClass($('.highlight'), 'highlight'); if (!QuoteYou.lastRead) { if (!(post = QuoteYou.lastRead = $('.quotesYou'))) { new Notification('warning', 'No posts are currently quoting you, loser.', 20); @@ -4214,7 +4216,6 @@ } else { post = QuoteYou.lastRead; } - $.rmClass($('.highlight'), 'highlight'); str = "" + type + "::div[contains(@class,'quotesYou')]"; while (post = (result = $.X(str, post)).snapshotItem(type === 'preceding' ? result.snapshotLength - 1 : 0)) { if (QuoteYou.cb.scroll(post)) { diff --git a/builds/crx/script.js b/builds/crx/script.js index 8aecb2dd6..111dc57fc 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -4166,7 +4166,7 @@ QuoteYou = { init: function() { - if (g.VIEW === 'catalog' || !Conf['Mark Quotes of You'] || !Conf['Quick Reply']) { + if (!(g.VIEW !== 'catalog' && Conf['Mark Quotes of You'] && Conf['Quick Reply'])) { return; } if (Conf['Highlight Own Posts']) { @@ -4208,6 +4208,8 @@ seek: function(type) { var post, posts, result, str; + return unlses(Conf['Mark Quotes of You'] && Conf['Quick Reply']); + $.rmClass($('.highlight'), 'highlight'); if (!QuoteYou.lastRead) { if (!(post = QuoteYou.lastRead = $('.quotesYou'))) { new Notification('warning', 'No posts are currently quoting you, loser.', 20); @@ -4219,7 +4221,6 @@ } else { post = QuoteYou.lastRead; } - $.rmClass($('.highlight'), 'highlight'); str = "" + type + "::div[contains(@class,'quotesYou')]"; while (post = (result = $.X(str, post)).snapshotItem(type === 'preceding' ? result.snapshotLength - 1 : 0)) { if (QuoteYou.cb.scroll(post)) { diff --git a/src/Quotelinks/QuoteYou.coffee b/src/Quotelinks/QuoteYou.coffee index 3cf656e80..b71d37a99 100644 --- a/src/Quotelinks/QuoteYou.coffee +++ b/src/Quotelinks/QuoteYou.coffee @@ -1,6 +1,6 @@ QuoteYou = init: -> - return if g.VIEW is 'catalog' or !Conf['Mark Quotes of You'] or !Conf['Quick Reply'] + return unless g.VIEW isnt 'catalog' and Conf['Mark Quotes of You'] and Conf['Quick Reply'] if Conf['Highlight Own Posts'] $.addClass doc, 'highlight-own' @@ -33,6 +33,9 @@ QuoteYou = cb: seek: (type) -> + return unlses Conf['Mark Quotes of You'] and Conf['Quick Reply'] + $.rmClass $('.highlight'), 'highlight' + unless QuoteYou.lastRead unless post = QuoteYou.lastRead = $ '.quotesYou' new Notification 'warning', 'No posts are currently quoting you, loser.', 20 @@ -41,8 +44,6 @@ QuoteYou = else post = QuoteYou.lastRead - $.rmClass $('.highlight'), 'highlight' - str = "#{type}::div[contains(@class,'quotesYou')]" while post = (result = $.X(str, post)).snapshotItem(if type is 'preceding' then result.snapshotLength - 1 else 0) From 0b55e34361c9126358c1b70ad0adbc945434deeb Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Fri, 9 Aug 2013 12:41:59 +0200 Subject: [PATCH 152/172] Allow fetching posts for archives that require credentials. --- json/archives.json | 1 + lib/$.coffee | 16 ++++++++-------- src/Archive/Redirect.coffee | 4 +++- src/General/Get.coffee | 18 ++++++++++++------ 4 files changed, 24 insertions(+), 15 deletions(-) diff --git a/json/archives.json b/json/archives.json index 15c52e369..95ac1a37a 100644 --- a/json/archives.json +++ b/json/archives.json @@ -103,6 +103,7 @@ "domain": "beta.foolz.us", "http": true, "https": true, + "withCredentials": true, "software": "foolfuuka", "boards": ["a", "co", "gd", "h", "jp", "m", "mlp", "q", "sp", "tg", "tv", "u", "v", "vg", "vp", "vr", "wsg"], "files": ["a", "gd", "h", "jp", "m", "q", "tg", "u", "vg", "vp", "vr", "wsg"] diff --git a/lib/$.coffee b/lib/$.coffee index 306eba722..7e25bb5f2 100644 --- a/lib/$.coffee +++ b/lib/$.coffee @@ -49,7 +49,7 @@ $.ajax = (url, options, extra={}) -> r $.cache = do -> reqs = {} - (url, cb) -> + (url, cb, options) -> if req = reqs[url] if req.readyState is 4 cb.call req, req.evt @@ -57,13 +57,13 @@ $.cache = do -> req.callbacks.push cb return rm = -> delete reqs[url] - req = $.ajax url, - onload: (e) -> - cb.call @, e for cb in @callbacks - @evt = e - delete @callbacks - onabort: rm - onerror: rm + req = $.ajax url, options + $.on req, 'load', (e) -> + cb.call @, e for cb in @callbacks + @evt = e + delete @callbacks + $.on req, 'abort', rm + $.on req, 'error', rm req.callbacks = [cb] reqs[url] = req $.cb = diff --git a/src/Archive/Redirect.coffee b/src/Archive/Redirect.coffee index edc4e621c..1da3f9f8a 100644 --- a/src/Archive/Redirect.coffee +++ b/src/Archive/Redirect.coffee @@ -74,7 +74,9 @@ Redirect = # Remove necessary HTTPS procotol in September 2013. if archive.name in ['Foolz', 'NSFW Foolz'] protocol = 'https://' - "#{protocol}#{archive.domain}/_/api/chan/post/?board=#{boardID}&num=#{postID}" + URL = new String "#{protocol}#{archive.domain}/_/api/chan/post/?board=#{boardID}&num=#{postID}" + URL.archive = archive + URL file: (archive, {boardID, filename}) -> "#{Redirect.protocol archive}#{archive.domain}/#{boardID}/full_image/#{filename}" diff --git a/src/General/Get.coffee b/src/General/Get.coffee index 5e9cfbbcf..fe2a056b0 100644 --- a/src/General/Get.coffee +++ b/src/General/Get.coffee @@ -71,8 +71,10 @@ Get = $.cache "//api.4chan.org/#{boardID}/res/#{threadID}.json", -> Get.fetchedPost @, boardID, threadID, postID, root, context else if url = Redirect.to 'post', {boardID, postID} - $.cache url, -> - Get.archivedPost @, boardID, postID, root, context + $.cache url, + -> Get.archivedPost @, boardID, postID, root, context + , + withCredentials: url.archive.withCredentials insert: (post, root, context) -> # Stop here if the container has been removed while loading. return unless root.parentNode @@ -97,8 +99,10 @@ Get = if status not in [200, 304] # The thread can die by the time we check a quote. if url = Redirect.to 'post', {boardID, postID} - $.cache url, -> - Get.archivedPost @, boardID, postID, root, context + $.cache url, + -> Get.archivedPost @, boardID, postID, root, context + , + withCredentials: url.archive.withCredentials else $.addClass root, 'warning' root.textContent = @@ -115,8 +119,10 @@ Get = 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 + $.cache url, + -> Get.archivedPost @, boardID, postID, root, context + , + withCredentials: url.archive.withCredentials else $.addClass root, 'warning' root.textContent = "Post No.#{postID} was not found." From 6f6c1be3b168c167072bed0a3ce9af9270c45919 Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Fri, 9 Aug 2013 18:44:22 +0200 Subject: [PATCH 153/172] Fix #1219. --- src/Miscellaneous/Time.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Miscellaneous/Time.coffee b/src/Miscellaneous/Time.coffee index 1f57d19d1..0f054283a 100644 --- a/src/Miscellaneous/Time.coffee +++ b/src/Miscellaneous/Time.coffee @@ -56,4 +56,4 @@ Time = p: -> if @getHours() < 12 then 'AM' else 'PM' P: -> if @getHours() < 12 then 'am' else 'pm' S: -> Time.zeroPad @getSeconds() - y: -> @getFullYear() - 2000 + y: -> @getFullYear().toString()[2..] From ee291bad0ce37f990416f596b75d40812c84e32f Mon Sep 17 00:00:00 2001 From: Zixaphir <zixaphirmoxphar@gmail.com> Date: Fri, 9 Aug 2013 19:21:26 -0700 Subject: [PATCH 154/172] Force stub menu to work --- builds/4chan-X.user.js | 156 +++++++++++++++++------------- builds/crx/script.js | 156 +++++++++++++++++------------- src/Filtering/PostHiding.coffee | 25 +++-- src/Filtering/ThreadHiding.coffee | 55 ++++++++--- src/General/Get.coffee | 2 +- src/Menu/Menu.coffee | 21 ++-- src/Quotelinks/QuoteYou.coffee | 2 +- 7 files changed, 244 insertions(+), 173 deletions(-) diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js index 00e4422d2..0f8fb21f4 100644 --- a/builds/4chan-X.user.js +++ b/builds/4chan-X.user.js @@ -1996,7 +1996,7 @@ } }, postFromNode: function(root) { - return Get.postFromRoot($.x('ancestor::div[contains(@class,"postContainer")][1]', root)); + return Get.postFromRoot($.x('(ancestor::div[contains(@class,"postContainer")]|following::div[contains(@class,"postContainer")])', root)); }, contextFromNode: function(quotelink) { return Get.postFromRoot($.x('ancestor::div[parent::div[@class="thread"]][1]', quotelink)); @@ -3225,15 +3225,11 @@ var post; post = Get.postFromNode(this); - if (post.isHidden) { - PostHiding.show(post); - } else { - PostHiding.hide(post); - } + PostHiding[(post.isHidden ? 'show' : 'hide')](post); return PostHiding.saveHiddenState(post, post.isHidden); }, hide: function(post, makeStub, hideRecursively) { - var a, postInfo, quotelink, _i, _len, _ref; + var a, button, postInfo, quotelink, _i, _len, _ref; if (makeStub == null) { makeStub = Conf['Stubs']; @@ -3259,15 +3255,12 @@ return; } a = PostHiding.makeButton(post, 'show'); - postInfo = Conf['Anonymize'] ? 'Anonymous' : $('.nameBlock', post.nodes.info).textContent; + postInfo = Conf['Anonymize'] ? 'Anonymous' : post.info.name; $.add(a, $.tn(" " + postInfo)); post.nodes.stub = $.el('div', { className: 'stub' }); - $.add(post.nodes.stub, a); - if (Conf['Menu']) { - $.add(post.nodes.stub, [$.tn(' '), Menu.makeButton(post)]); - } + $.add(post.nodes.stub, !Conf['Menu'] ? a : [a, $.tn(' '), button = Menu.makeButton(post)]); return $.prepend(post.nodes.root, post.nodes.stub); }, show: function(post, showRecursively) { @@ -3462,11 +3455,6 @@ makeStub = $.el('label', { innerHTML: "<input type=checkbox " + (Conf['Stubs'] ? 'checked' : '') + "> Make stub" }); - hideStubLink = $.el('a', { - textContent: 'Hide stub', - href: 'javascript:;' - }); - $.on(hideStubLink, 'click', ThreadHiding.menu.hideStub); $.event('AddMenuEntry', { type: 'post', el: div, @@ -3489,6 +3477,34 @@ } ] }); + div = $.el('a', { + className: 'show-thread-link', + textContent: 'Show thread', + href: 'javascript:;' + }); + $.on(show, 'click', ThreadHiding.menu.show); + $.event('AddMenuEntry', { + type: 'post' + }); + ({ + el: div, + order: 20, + open: function(_arg) { + var isReply, thread; + + thread = _arg.thread, isReply = _arg.isReply; + if (isReply || !thread.isHidden) { + return false; + } + ThreadHiding.menu.thread = thread; + return true; + } + }); + hideStubLink = $.el('a', { + textContent: 'Hide stub', + href: 'javascript:;' + }); + $.on(hideStubLink, 'click', ThreadHiding.menu.hideStub); return $.event('AddMenuEntry', { type: 'post', el: hideStubLink, @@ -3513,6 +3529,14 @@ ThreadHiding.saveHiddenState(thread, makeStub); return $.event('CloseMenu'); }, + show: function() { + var thread; + + thread = ThreadHiding.menu.thread; + ThreadHiding.show(thread); + ThreadHiding.saveHiddenState(thread); + return $.event('CloseMenu'); + }, hideStub: function() { var thread; @@ -3567,7 +3591,7 @@ return ThreadHiding.saveHiddenState(thread); }, hide: function(thread, makeStub) { - var OP, a, numReplies, opInfo, span, threadRoot; + var OP, a, button, numReplies, opInfo, span, threadRoot; if (makeStub == null) { makeStub = Conf['Stubs']; @@ -3579,22 +3603,15 @@ threadRoot.hidden = threadRoot.nextElementSibling.hidden = true; return; } - numReplies = 0; - if (span = $('.summary', threadRoot)) { - numReplies = +span.textContent.match(/\d+/); - } - numReplies += $$('.opContainer ~ .replyContainer', threadRoot).length; - numReplies = numReplies === 1 ? '1 reply' : "" + numReplies + " replies"; - opInfo = Conf['Anonymize'] ? 'Anonymous' : $('.nameBlock', OP.nodes.info).textContent; + numReplies = ((span = $('.summary', threadRoot)) ? +span.textContent.match(/\d+/) : 0) + $$('.opContainer ~ .replyContainer', threadRoot).length; + numReplies = numReplies === 1 ? '1 reply' : "" + (numReplies || 'No') + " replies"; + opInfo = Conf['Anonymize'] ? 'Anonymous' : OP.info.name; a = ThreadHiding.makeButton(thread, 'show'); $.add(a, $.tn(" " + opInfo + " (" + numReplies + ")")); thread.stub = $.el('div', { className: 'stub' }); - $.add(thread.stub, a); - if (Conf['Menu']) { - $.add(thread.stub, [$.tn(' '), Menu.makeButton(OP)]); - } + $.add(thread.stub, !Conf['Menu'] ? a : [a, $.tn(' '), button = Menu.makeButton(OP)]); return $.prepend(threadRoot, thread.stub); }, show: function(thread) { @@ -4203,7 +4220,9 @@ seek: function(type) { var post, posts, result, str; - return unlses(Conf['Mark Quotes of You'] && Conf['Quick Reply']); + if (!(Conf['Mark Quotes of You'] && Conf['Quick Reply'])) { + return; + } $.rmClass($('.highlight'), 'highlight'); if (!QuoteYou.lastRead) { if (!(post = QuoteYou.lastRead = $('.quotesYou'))) { @@ -6993,45 +7012,48 @@ } }; - Menu = { - init: function() { - if (g.VIEW === 'catalog' || !Conf['Menu']) { - return; - } - this.menu = new UI.Menu('post'); - return Post.prototype.callbacks.push({ - name: 'Menu', - cb: this.node - }); - }, - node: function() { - var button; + Menu = (function() { + var a; - if (this.isClone) { - button = $('.menu-button', this.nodes.info); - } else { - button = Menu.makeButton(this); - $.add(this.nodes.info, [$.tn('\u00A0'), button]); - } - return $.on(button, 'click', Menu.toggle); - }, - makeButton: (function() { - var a; + a = $.el('a', { + className: 'menu-button brackets-wrap', + innerHTML: '<span class=drop-marker></span>', + href: 'javascript:;' + }); + return { + init: function() { + if (g.VIEW === 'catalog' || !Conf['Menu']) { + return; + } + this.menu = new UI.Menu('post'); + return Post.prototype.callbacks.push({ + name: 'Menu', + cb: this.node + }); + }, + node: function() { + var button; - a = null; - return function() { - a || (a = $.el('a', { - className: 'menu-button fourchanx-link', - innerHTML: '<i></i>', - href: 'javascript:;' - })); - return a.cloneNode(true); - }; - })(), - toggle: function(e) { - return Menu.menu.toggle(e, this, Get.postFromNode(this)); - } - }; + if (this.isClone) { + button = $('.menu-button', this.nodes.info); + } else { + button = a.cloneNode(true); + $.add(this.nodes.info, [$.tn('\u00A0'), button]); + } + return $.on(button, 'click', Menu.toggle); + }, + makeButton: function() { + var el; + + el = a.cloneNode(true); + $.on(el, 'click', Menu.toggle); + return el; + }, + toggle: function(e) { + return Menu.menu.toggle(e, this, Get.postFromNode(this)); + } + }; + })(); ReportLink = { init: function() { diff --git a/builds/crx/script.js b/builds/crx/script.js index 111dc57fc..cd6c5b4cd 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -2008,7 +2008,7 @@ } }, postFromNode: function(root) { - return Get.postFromRoot($.x('ancestor::div[contains(@class,"postContainer")][1]', root)); + return Get.postFromRoot($.x('(ancestor::div[contains(@class,"postContainer")]|following::div[contains(@class,"postContainer")])', root)); }, contextFromNode: function(quotelink) { return Get.postFromRoot($.x('ancestor::div[parent::div[@class="thread"]][1]', quotelink)); @@ -3230,15 +3230,11 @@ var post; post = Get.postFromNode(this); - if (post.isHidden) { - PostHiding.show(post); - } else { - PostHiding.hide(post); - } + PostHiding[(post.isHidden ? 'show' : 'hide')](post); return PostHiding.saveHiddenState(post, post.isHidden); }, hide: function(post, makeStub, hideRecursively) { - var a, postInfo, quotelink, _i, _len, _ref; + var a, button, postInfo, quotelink, _i, _len, _ref; if (makeStub == null) { makeStub = Conf['Stubs']; @@ -3264,15 +3260,12 @@ return; } a = PostHiding.makeButton(post, 'show'); - postInfo = Conf['Anonymize'] ? 'Anonymous' : $('.nameBlock', post.nodes.info).textContent; + postInfo = Conf['Anonymize'] ? 'Anonymous' : post.info.name; $.add(a, $.tn(" " + postInfo)); post.nodes.stub = $.el('div', { className: 'stub' }); - $.add(post.nodes.stub, a); - if (Conf['Menu']) { - $.add(post.nodes.stub, [$.tn(' '), Menu.makeButton(post)]); - } + $.add(post.nodes.stub, !Conf['Menu'] ? a : [a, $.tn(' '), button = Menu.makeButton(post)]); return $.prepend(post.nodes.root, post.nodes.stub); }, show: function(post, showRecursively) { @@ -3467,11 +3460,6 @@ makeStub = $.el('label', { innerHTML: "<input type=checkbox " + (Conf['Stubs'] ? 'checked' : '') + "> Make stub" }); - hideStubLink = $.el('a', { - textContent: 'Hide stub', - href: 'javascript:;' - }); - $.on(hideStubLink, 'click', ThreadHiding.menu.hideStub); $.event('AddMenuEntry', { type: 'post', el: div, @@ -3494,6 +3482,34 @@ } ] }); + div = $.el('a', { + className: 'show-thread-link', + textContent: 'Show thread', + href: 'javascript:;' + }); + $.on(show, 'click', ThreadHiding.menu.show); + $.event('AddMenuEntry', { + type: 'post' + }); + ({ + el: div, + order: 20, + open: function(_arg) { + var isReply, thread; + + thread = _arg.thread, isReply = _arg.isReply; + if (isReply || !thread.isHidden) { + return false; + } + ThreadHiding.menu.thread = thread; + return true; + } + }); + hideStubLink = $.el('a', { + textContent: 'Hide stub', + href: 'javascript:;' + }); + $.on(hideStubLink, 'click', ThreadHiding.menu.hideStub); return $.event('AddMenuEntry', { type: 'post', el: hideStubLink, @@ -3518,6 +3534,14 @@ ThreadHiding.saveHiddenState(thread, makeStub); return $.event('CloseMenu'); }, + show: function() { + var thread; + + thread = ThreadHiding.menu.thread; + ThreadHiding.show(thread); + ThreadHiding.saveHiddenState(thread); + return $.event('CloseMenu'); + }, hideStub: function() { var thread; @@ -3572,7 +3596,7 @@ return ThreadHiding.saveHiddenState(thread); }, hide: function(thread, makeStub) { - var OP, a, numReplies, opInfo, span, threadRoot; + var OP, a, button, numReplies, opInfo, span, threadRoot; if (makeStub == null) { makeStub = Conf['Stubs']; @@ -3584,22 +3608,15 @@ threadRoot.hidden = threadRoot.nextElementSibling.hidden = true; return; } - numReplies = 0; - if (span = $('.summary', threadRoot)) { - numReplies = +span.textContent.match(/\d+/); - } - numReplies += $$('.opContainer ~ .replyContainer', threadRoot).length; - numReplies = numReplies === 1 ? '1 reply' : "" + numReplies + " replies"; - opInfo = Conf['Anonymize'] ? 'Anonymous' : $('.nameBlock', OP.nodes.info).textContent; + numReplies = ((span = $('.summary', threadRoot)) ? +span.textContent.match(/\d+/) : 0) + $$('.opContainer ~ .replyContainer', threadRoot).length; + numReplies = numReplies === 1 ? '1 reply' : "" + (numReplies || 'No') + " replies"; + opInfo = Conf['Anonymize'] ? 'Anonymous' : OP.info.name; a = ThreadHiding.makeButton(thread, 'show'); $.add(a, $.tn(" " + opInfo + " (" + numReplies + ")")); thread.stub = $.el('div', { className: 'stub' }); - $.add(thread.stub, a); - if (Conf['Menu']) { - $.add(thread.stub, [$.tn(' '), Menu.makeButton(OP)]); - } + $.add(thread.stub, !Conf['Menu'] ? a : [a, $.tn(' '), button = Menu.makeButton(OP)]); return $.prepend(threadRoot, thread.stub); }, show: function(thread) { @@ -4208,7 +4225,9 @@ seek: function(type) { var post, posts, result, str; - return unlses(Conf['Mark Quotes of You'] && Conf['Quick Reply']); + if (!(Conf['Mark Quotes of You'] && Conf['Quick Reply'])) { + return; + } $.rmClass($('.highlight'), 'highlight'); if (!QuoteYou.lastRead) { if (!(post = QuoteYou.lastRead = $('.quotesYou'))) { @@ -6974,45 +6993,48 @@ } }; - Menu = { - init: function() { - if (g.VIEW === 'catalog' || !Conf['Menu']) { - return; - } - this.menu = new UI.Menu('post'); - return Post.prototype.callbacks.push({ - name: 'Menu', - cb: this.node - }); - }, - node: function() { - var button; + Menu = (function() { + var a; - if (this.isClone) { - button = $('.menu-button', this.nodes.info); - } else { - button = Menu.makeButton(this); - $.add(this.nodes.info, [$.tn('\u00A0'), button]); - } - return $.on(button, 'click', Menu.toggle); - }, - makeButton: (function() { - var a; + a = $.el('a', { + className: 'menu-button brackets-wrap', + innerHTML: '<span class=drop-marker></span>', + href: 'javascript:;' + }); + return { + init: function() { + if (g.VIEW === 'catalog' || !Conf['Menu']) { + return; + } + this.menu = new UI.Menu('post'); + return Post.prototype.callbacks.push({ + name: 'Menu', + cb: this.node + }); + }, + node: function() { + var button; - a = null; - return function() { - a || (a = $.el('a', { - className: 'menu-button fourchanx-link', - innerHTML: '<i></i>', - href: 'javascript:;' - })); - return a.cloneNode(true); - }; - })(), - toggle: function(e) { - return Menu.menu.toggle(e, this, Get.postFromNode(this)); - } - }; + if (this.isClone) { + button = $('.menu-button', this.nodes.info); + } else { + button = a.cloneNode(true); + $.add(this.nodes.info, [$.tn('\u00A0'), button]); + } + return $.on(button, 'click', Menu.toggle); + }, + makeButton: function() { + var el; + + el = a.cloneNode(true); + $.on(el, 'click', Menu.toggle); + return el; + }, + toggle: function(e) { + return Menu.menu.toggle(e, this, Get.postFromNode(this)); + } + }; + })(); ReportLink = { init: function() { diff --git a/src/Filtering/PostHiding.coffee b/src/Filtering/PostHiding.coffee index 9d19ac74b..8c7be2ae3 100644 --- a/src/Filtering/PostHiding.coffee +++ b/src/Filtering/PostHiding.coffee @@ -17,7 +17,7 @@ PostHiding = PostHiding.hide @, data.makeStub, data.hideRecursively else Recursive.apply PostHiding.hide, @, data.makeStub, true - Recursive.add PostHiding.hide, @, data.makeStub, true + Recursive.add PostHiding.hide, @, data.makeStub, true return unless Conf['Reply Hiding Buttons'] $.replace $('.sideArrows', @nodes.root), PostHiding.makeButton @, 'hide' @@ -108,11 +108,12 @@ PostHiding = PostHiding.hide post, makeStub, replies else if replies Recursive.apply PostHiding.hide, post, makeStub, true - Recursive.add PostHiding.hide, post, makeStub, true + Recursive.add PostHiding.hide, post, makeStub, true else return PostHiding.saveHiddenState post, true, thisPost, makeStub, replies $.event 'CloseMenu' + show: -> parent = @parentNode thisPost = $('input[name=thisPost]', parent).checked @@ -122,7 +123,7 @@ PostHiding = PostHiding.show post, replies else if replies Recursive.apply PostHiding.show, post, true - Recursive.rm PostHiding.hide, post, true + Recursive.rm PostHiding.hide, post, true else return if data = PostHiding.db.get {boardID: post.board.ID, threadID: post.thread.ID, postID: post.ID} @@ -158,10 +159,7 @@ PostHiding = toggle: -> post = Get.postFromNode @ - if post.isHidden - PostHiding.show post - else - PostHiding.hide post + PostHiding[(if post.isHidden then 'show' else 'hide')] post PostHiding.saveHiddenState post, post.isHidden hide: (post, makeStub=Conf['Stubs'], hideRecursively=Conf['Recursive Hiding']) -> @@ -170,7 +168,7 @@ PostHiding = if hideRecursively Recursive.apply PostHiding.hide, post, makeStub, true - Recursive.add PostHiding.hide, post, makeStub, true + Recursive.add PostHiding.hide, post, makeStub, true for quotelink in Get.allQuotelinksLinkingTo post $.addClass quotelink, 'filtered' @@ -184,13 +182,14 @@ PostHiding = if Conf['Anonymize'] 'Anonymous' else - $('.nameBlock', post.nodes.info).textContent + post.info.name $.add a, $.tn " #{postInfo}" post.nodes.stub = $.el 'div', className: 'stub' - $.add post.nodes.stub, a - if Conf['Menu'] - $.add post.nodes.stub, [$.tn(' '), Menu.makeButton post] + $.add post.nodes.stub, unless Conf['Menu'] + a + else + [a, $.tn(' '), button = Menu.makeButton post] $.prepend post.nodes.root, post.nodes.stub show: (post, showRecursively=Conf['Recursive Hiding']) -> @@ -202,7 +201,7 @@ PostHiding = post.isHidden = false if showRecursively Recursive.apply PostHiding.show, post, true - Recursive.rm PostHiding.hide, post + Recursive.rm PostHiding.hide, post for quotelink in Get.allQuotelinksLinkingTo post $.rmClass quotelink, 'filtered' return \ No newline at end of file diff --git a/src/Filtering/ThreadHiding.coffee b/src/Filtering/ThreadHiding.coffee index a1d9a79f8..f193afbde 100644 --- a/src/Filtering/ThreadHiding.coffee +++ b/src/Filtering/ThreadHiding.coffee @@ -71,11 +71,6 @@ ThreadHiding = makeStub = $.el 'label', innerHTML: "<input type=checkbox #{if Conf['Stubs'] then 'checked' else ''}> Make stub" - hideStubLink = $.el 'a', - textContent: 'Hide stub' - href: 'javascript:;' - $.on hideStubLink, 'click', ThreadHiding.menu.hideStub - $.event 'AddMenuEntry', type: 'post' el: div @@ -87,6 +82,27 @@ ThreadHiding = true subEntries: [el: apply; el: makeStub] + div = $.el 'a', + className: 'show-thread-link' + textContent: 'Show thread' + href: 'javascript:;' + $.on show, 'click', ThreadHiding.menu.show + + $.event 'AddMenuEntry', + type: 'post' + el: div + order: 20 + open: ({thread, isReply}) -> + if isReply or !thread.isHidden + return false + ThreadHiding.menu.thread = thread + true + + hideStubLink = $.el 'a', + textContent: 'Hide stub' + href: 'javascript:;' + $.on hideStubLink, 'click', ThreadHiding.menu.hideStub + $.event 'AddMenuEntry', type: 'post' el: hideStubLink @@ -102,6 +118,13 @@ ThreadHiding = ThreadHiding.hide thread, makeStub ThreadHiding.saveHiddenState thread, makeStub $.event 'CloseMenu' + + show: -> + {thread} = ThreadHiding.menu + ThreadHiding.show thread + ThreadHiding.saveHiddenState thread + $.event 'CloseMenu' + hideStub: -> {thread} = ThreadHiding.menu ThreadHiding.hide thread, false @@ -150,24 +173,28 @@ ThreadHiding = threadRoot.hidden = threadRoot.nextElementSibling.hidden = true # <hr> return - numReplies = 0 - if span = $ '.summary', threadRoot - numReplies = +span.textContent.match /\d+/ - numReplies += $$('.opContainer ~ .replyContainer', threadRoot).length - numReplies = if numReplies is 1 then '1 reply' else "#{numReplies} replies" + numReplies = ( + if span = $ '.summary', threadRoot + +span.textContent.match /\d+/ + else + 0 + ) + + $$('.opContainer ~ .replyContainer', threadRoot).length + numReplies = if numReplies is 1 then '1 reply' else "#{numReplies or 'No'} replies" opInfo = if Conf['Anonymize'] 'Anonymous' else - $('.nameBlock', OP.nodes.info).textContent + OP.info.name a = ThreadHiding.makeButton thread, 'show' $.add a, $.tn " #{opInfo} (#{numReplies})" thread.stub = $.el 'div', className: 'stub' - $.add thread.stub, a - if Conf['Menu'] - $.add thread.stub, [$.tn(' '), Menu.makeButton OP] + $.add thread.stub, unless Conf['Menu'] + a + else + [a, $.tn(' '), button = Menu.makeButton OP] $.prepend threadRoot, thread.stub show: (thread) -> diff --git a/src/General/Get.coffee b/src/General/Get.coffee index 50d32684b..43787f62d 100644 --- a/src/General/Get.coffee +++ b/src/General/Get.coffee @@ -18,7 +18,7 @@ Get = post = g.posts["#{boardID}.#{postID}"] if index then post.clones[index] else post postFromNode: (root) -> - Get.postFromRoot $.x 'ancestor::div[contains(@class,"postContainer")][1]', root + Get.postFromRoot $.x '(ancestor::div[contains(@class,"postContainer")]|following::div[contains(@class,"postContainer")])', root contextFromNode: (quotelink) -> Get.postFromRoot $.x 'ancestor::div[parent::div[@class="thread"]][1]', quotelink postDataFromLink: (link) -> diff --git a/src/Menu/Menu.coffee b/src/Menu/Menu.coffee index 98e8865ad..29fc8399e 100644 --- a/src/Menu/Menu.coffee +++ b/src/Menu/Menu.coffee @@ -1,4 +1,9 @@ -Menu = +Menu = do -> + a = $.el 'a', + className: 'menu-button brackets-wrap' + innerHTML: '<span class=drop-marker></span>' + href: 'javascript:;' + init: -> return if g.VIEW is 'catalog' or !Conf['Menu'] @@ -11,18 +16,14 @@ Menu = if @isClone button = $ '.menu-button', @nodes.info else - button = Menu.makeButton @ + button = a.cloneNode true $.add @nodes.info, [$.tn('\u00A0'), button] $.on button, 'click', Menu.toggle - makeButton: do -> - a = null - -> - a or= $.el 'a', - className: 'menu-button fourchanx-link' - innerHTML: '<i></i>' - href: 'javascript:;' - a.cloneNode true + makeButton: -> + el = a.cloneNode true + $.on el, 'click', Menu.toggle + el toggle: (e) -> Menu.menu.toggle e, @, Get.postFromNode @ diff --git a/src/Quotelinks/QuoteYou.coffee b/src/Quotelinks/QuoteYou.coffee index b71d37a99..985940041 100644 --- a/src/Quotelinks/QuoteYou.coffee +++ b/src/Quotelinks/QuoteYou.coffee @@ -33,7 +33,7 @@ QuoteYou = cb: seek: (type) -> - return unlses Conf['Mark Quotes of You'] and Conf['Quick Reply'] + return unless Conf['Mark Quotes of You'] and Conf['Quick Reply'] $.rmClass $('.highlight'), 'highlight' unless QuoteYou.lastRead From 10a2a96a965855783e816141a0813f56d281b2e8 Mon Sep 17 00:00:00 2001 From: Zixaphir <zixaphirmoxphar@gmail.com> Date: Fri, 9 Aug 2013 19:40:21 -0700 Subject: [PATCH 155/172] Oops --- builds/4chan-X.user.js | 2 +- builds/crx/script.js | 2 +- src/General/Get.coffee | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js index cf4d6addc..2f997141e 100644 --- a/builds/4chan-X.user.js +++ b/builds/4chan-X.user.js @@ -1995,7 +1995,7 @@ } }, postFromNode: function(root) { - return Get.postFromRoot($.x('(ancestor::div[contains(@class,"postContainer")]|following::div[contains(@class,"postContainer")])', root)); + return Get.postFromRoot($.x('(ancestor::div[contains(@class,"postContainer")]|following::div[contains(@class,"postContainer")])[1]', root)); }, contextFromNode: function(quotelink) { return Get.postFromRoot($.x('ancestor::div[parent::div[@class="thread"]][1]', quotelink)); diff --git a/builds/crx/script.js b/builds/crx/script.js index 1e575d241..787755dde 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -2007,7 +2007,7 @@ } }, postFromNode: function(root) { - return Get.postFromRoot($.x('(ancestor::div[contains(@class,"postContainer")]|following::div[contains(@class,"postContainer")])', root)); + return Get.postFromRoot($.x('(ancestor::div[contains(@class,"postContainer")]|following::div[contains(@class,"postContainer")])[1]', root)); }, contextFromNode: function(quotelink) { return Get.postFromRoot($.x('ancestor::div[parent::div[@class="thread"]][1]', quotelink)); diff --git a/src/General/Get.coffee b/src/General/Get.coffee index 6b9d94e86..bedb80bd6 100644 --- a/src/General/Get.coffee +++ b/src/General/Get.coffee @@ -18,7 +18,7 @@ Get = post = g.posts["#{boardID}.#{postID}"] if index then post.clones[index] else post postFromNode: (root) -> - Get.postFromRoot $.x '(ancestor::div[contains(@class,"postContainer")]|following::div[contains(@class,"postContainer")])', root + Get.postFromRoot $.x '(ancestor::div[contains(@class,"postContainer")]|following::div[contains(@class,"postContainer")])[1]', root contextFromNode: (quotelink) -> Get.postFromRoot $.x 'ancestor::div[parent::div[@class="thread"]][1]', quotelink postDataFromLink: (link) -> From 1470c0e563a1da7cf599a290a5efd470ec6481b5 Mon Sep 17 00:00:00 2001 From: Zixaphir <zixaphirmoxphar@gmail.com> Date: Fri, 9 Aug 2013 20:19:08 -0700 Subject: [PATCH 156/172] More oops --- builds/4chan-X.user.js | 8 +++++--- builds/crx/script.js | 8 +++++--- src/Filtering/ThreadHiding.coffee | 2 +- src/Quotelinks/QuoteYou.coffee | 2 +- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js index 2f997141e..cd8261e6b 100644 --- a/builds/4chan-X.user.js +++ b/builds/4chan-X.user.js @@ -3487,7 +3487,7 @@ textContent: 'Show thread', href: 'javascript:;' }); - $.on(show, 'click', ThreadHiding.menu.show); + $.on(div, 'click', ThreadHiding.menu.show); $.event('AddMenuEntry', { type: 'post' }); @@ -4223,12 +4223,14 @@ }, cb: { seek: function(type) { - var post, posts, result, str; + var highlight, post, posts, result, str; if (!(Conf['Mark Quotes of You'] && Conf['Quick Reply'])) { return; } - $.rmClass($('.highlight'), 'highlight'); + if (highlight = $('.highlight')) { + $.rmClass(highlight, 'highlight'); + } if (!QuoteYou.lastRead) { if (!(post = QuoteYou.lastRead = $('.quotesYou'))) { new Notification('warning', 'No posts are currently quoting you, loser.', 20); diff --git a/builds/crx/script.js b/builds/crx/script.js index 787755dde..0dff7e082 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -3492,7 +3492,7 @@ textContent: 'Show thread', href: 'javascript:;' }); - $.on(show, 'click', ThreadHiding.menu.show); + $.on(div, 'click', ThreadHiding.menu.show); $.event('AddMenuEntry', { type: 'post' }); @@ -4228,12 +4228,14 @@ }, cb: { seek: function(type) { - var post, posts, result, str; + var highlight, post, posts, result, str; if (!(Conf['Mark Quotes of You'] && Conf['Quick Reply'])) { return; } - $.rmClass($('.highlight'), 'highlight'); + if (highlight = $('.highlight')) { + $.rmClass(highlight, 'highlight'); + } if (!QuoteYou.lastRead) { if (!(post = QuoteYou.lastRead = $('.quotesYou'))) { new Notification('warning', 'No posts are currently quoting you, loser.', 20); diff --git a/src/Filtering/ThreadHiding.coffee b/src/Filtering/ThreadHiding.coffee index f193afbde..724210f6d 100644 --- a/src/Filtering/ThreadHiding.coffee +++ b/src/Filtering/ThreadHiding.coffee @@ -86,7 +86,7 @@ ThreadHiding = className: 'show-thread-link' textContent: 'Show thread' href: 'javascript:;' - $.on show, 'click', ThreadHiding.menu.show + $.on div, 'click', ThreadHiding.menu.show $.event 'AddMenuEntry', type: 'post' diff --git a/src/Quotelinks/QuoteYou.coffee b/src/Quotelinks/QuoteYou.coffee index 985940041..949ac8c84 100644 --- a/src/Quotelinks/QuoteYou.coffee +++ b/src/Quotelinks/QuoteYou.coffee @@ -34,7 +34,7 @@ QuoteYou = cb: seek: (type) -> return unless Conf['Mark Quotes of You'] and Conf['Quick Reply'] - $.rmClass $('.highlight'), 'highlight' + $.rmClass highlight, 'highlight' if highlight = $ '.highlight' unless QuoteYou.lastRead unless post = QuoteYou.lastRead = $ '.quotesYou' From e21de118b1a09f33ffad5a0dbe8e2c663185d87b Mon Sep 17 00:00:00 2001 From: Zixaphir <zixaphirmoxphar@gmail.com> Date: Fri, 9 Aug 2013 21:06:04 -0700 Subject: [PATCH 157/172] XPath magic is hurd --- builds/4chan-X.user.js | 2 +- builds/crx/script.js | 2 +- src/General/Get.coffee | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js index cd8261e6b..eccc4fea7 100644 --- a/builds/4chan-X.user.js +++ b/builds/4chan-X.user.js @@ -1995,7 +1995,7 @@ } }, postFromNode: function(root) { - return Get.postFromRoot($.x('(ancestor::div[contains(@class,"postContainer")]|following::div[contains(@class,"postContainer")])[1]', root)); + return Get.postFromRoot($.x('(ancestor::div[contains(@class,"postContainer")][1]|following::div[contains(@class,"postContainer")][1])', root)); }, contextFromNode: function(quotelink) { return Get.postFromRoot($.x('ancestor::div[parent::div[@class="thread"]][1]', quotelink)); diff --git a/builds/crx/script.js b/builds/crx/script.js index 0dff7e082..0ff3f6cf7 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -2007,7 +2007,7 @@ } }, postFromNode: function(root) { - return Get.postFromRoot($.x('(ancestor::div[contains(@class,"postContainer")]|following::div[contains(@class,"postContainer")])[1]', root)); + return Get.postFromRoot($.x('(ancestor::div[contains(@class,"postContainer")][1]|following::div[contains(@class,"postContainer")][1])', root)); }, contextFromNode: function(quotelink) { return Get.postFromRoot($.x('ancestor::div[parent::div[@class="thread"]][1]', quotelink)); diff --git a/src/General/Get.coffee b/src/General/Get.coffee index bedb80bd6..fbc5cb37a 100644 --- a/src/General/Get.coffee +++ b/src/General/Get.coffee @@ -18,7 +18,7 @@ Get = post = g.posts["#{boardID}.#{postID}"] if index then post.clones[index] else post postFromNode: (root) -> - Get.postFromRoot $.x '(ancestor::div[contains(@class,"postContainer")]|following::div[contains(@class,"postContainer")])[1]', root + Get.postFromRoot $.x '(ancestor::div[contains(@class,"postContainer")][1]|following::div[contains(@class,"postContainer")][1])', root contextFromNode: (quotelink) -> Get.postFromRoot $.x 'ancestor::div[parent::div[@class="thread"]][1]', quotelink postDataFromLink: (link) -> From 08e7a8a6c43ccf82d43ade20e08652050ee4a4aa Mon Sep 17 00:00:00 2001 From: Zixaphir <zixaphirmoxphar@gmail.com> Date: Sat, 10 Aug 2013 02:04:41 -0700 Subject: [PATCH 158/172] This will get better when MediaCrush's API improves? --- LICENSE | 2 +- builds/4chan-X.user.js | 19 +++++++++++++---- builds/crx/script.js | 19 +++++++++++++---- src/Linkification/Linkify.coffee | 36 +++++++++++++++++++++++++++++--- 4 files changed, 64 insertions(+), 12 deletions(-) diff --git a/LICENSE b/LICENSE index b6e66b546..a98499ab6 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,5 @@ /* -* 4chan X - Version 1.2.25 - 2013-08-09 +* 4chan X - Version 1.2.25 - 2013-08-10 * * Licensed under the MIT license. * https://github.com/seaweedchan/4chan-x/blob/master/LICENSE diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js index eccc4fea7..d10334602 100644 --- a/builds/4chan-X.user.js +++ b/builds/4chan-X.user.js @@ -19,7 +19,7 @@ // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwAgMAAAAqbBEUAAAACVBMVEUAAGcAAABmzDNZt9VtAAAAAXRSTlMAQObYZgAAAHFJREFUKFOt0LENACEIBdBv4Qju4wgWanEj3D6OcIVMKaitYHEU/jwTCQj8W75kiVCSBvdQ5/AvfVHBin11BgdRq3ysBgfwBDRrj3MCIA+oAQaku/Q1cNctrAmyDl577tOThYt/Y1RBM4DgOHzM0HFTAyLukH/cmRnqAAAAAElFTkSuQmCC // ==/UserScript== /* -* 4chan X - Version 1.2.25 - 2013-08-09 +* 4chan X - Version 1.2.25 - 2013-08-10 * * Licensed under the MIT license. * https://github.com/seaweedchan/4chan-x/blob/master/LICENSE @@ -4385,10 +4385,12 @@ if (this.isClone) { if (Conf['Embedding']) { i = 0; - items = $$('.embedded', this.nodes.comment); + items = $$('.embed', this.nodes.comment); while (el = items[i++]) { - $.on(el, "click", Linkify.cb.toggle); - Linkify.cb.toggle.call(el); + $.on(el, 'click', Linkify.cb.toggle); + if ($.hasClass(el, 'embedded')) { + Linkify.cb.toggle.call(el); + } } } return; @@ -4676,6 +4678,15 @@ }); } }, + MediaCrush: { + regExp: /.*(?:mediacru.sh\/)([0-9a-z_]+)/i, + style: 'border: 0; width: 640px; height: 480px; resize: both;', + el: function(a) { + return $.el('iframe', { + src: "https://mediacru.sh/" + a.dataset.uid + }); + } + }, pastebin: { regExp: /.*(?:pastebin.com\/(?!u\/))([^#\&\?]*).*/, el: function(a) { diff --git a/builds/crx/script.js b/builds/crx/script.js index 0ff3f6cf7..233ab106b 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -1,6 +1,6 @@ // Generated by CoffeeScript /* -* 4chan X - Version 1.2.25 - 2013-08-09 +* 4chan X - Version 1.2.25 - 2013-08-10 * * Licensed under the MIT license. * https://github.com/seaweedchan/4chan-x/blob/master/LICENSE @@ -4390,10 +4390,12 @@ if (this.isClone) { if (Conf['Embedding']) { i = 0; - items = $$('.embedded', this.nodes.comment); + items = $$('.embed', this.nodes.comment); while (el = items[i++]) { - $.on(el, "click", Linkify.cb.toggle); - Linkify.cb.toggle.call(el); + $.on(el, 'click', Linkify.cb.toggle); + if ($.hasClass(el, 'embedded')) { + Linkify.cb.toggle.call(el); + } } } return; @@ -4681,6 +4683,15 @@ }); } }, + MediaCrush: { + regExp: /.*(?:mediacru.sh\/)([0-9a-z_]+)/i, + style: 'border: 0; width: 640px; height: 480px; resize: both;', + el: function(a) { + return $.el('iframe', { + src: "https://mediacru.sh/" + a.dataset.uid + }); + } + }, pastebin: { regExp: /.*(?:pastebin.com\/(?!u\/))([^#\&\?]*).*/, el: function(a) { diff --git a/src/Linkification/Linkify.coffee b/src/Linkification/Linkify.coffee index b2151e945..b935fba66 100644 --- a/src/Linkification/Linkify.coffee +++ b/src/Linkification/Linkify.coffee @@ -36,10 +36,10 @@ Linkify = if @isClone if Conf['Embedding'] i = 0 - items = $$ '.embedded', @nodes.comment + items = $$ '.embed', @nodes.comment while el = items[i++] - $.on el, "click", Linkify.cb.toggle - Linkify.cb.toggle.call el + $.on el, 'click', Linkify.cb.toggle + Linkify.cb.toggle.call el if $.hasClass el, 'embedded' return @@ -279,6 +279,36 @@ Linkify = $.el 'object', innerHTML: "<embed src='http://www.liveleak.com/e/#{a.dataset.uid}?autostart=true' wmode='opaque' width='640' height='390' pluginspage='http://get.adobe.com/flashplayer/' type='application/x-shockwave-flash'></embed>" + MediaCrush: + regExp: /.*(?:mediacru.sh\/)([0-9a-z_]+)/i + style: 'border: 0; width: 640px; height: 480px; resize: both;' + el: (a) -> + $.el 'iframe', + src: "https://mediacru.sh/#{a.dataset.uid}" +# MediaCrush CORS When? +# +# el = $.el 'div' +# $.cache "https://mediacru.sh/#{a.dataset.uid}.json", -> +# {status} = @ +# return unless [200, 304].contains status +# {files} = JSON.parse req.response +# file = file for file of files when files.hasOwnProperty file +# el.innerHTML = switch file.type +# when 'video/mp4', 'video/ogv' +# """ +#<video autoplay loop> +# <source src="https://mediacru.sh/#{a.dataset.uid}.mp4" type="video/mp4;"> +# <source src="https://mediacru.sh/#{a.dataset.uid}.ogv" type="video/ogg; codecs='theora, vorbis'"> +#</video>""" +# when 'image/png', 'image/gif', 'image/jpeg' +# "<a target=_blank href='#{a.dataset.href}'><img src='https://mediacru.sh/#{file.file}'></a>" +# when 'image/svg', 'image/svg+xml' +# "<embed src='https://mediacru.sh/#{file.file}' type='image/svg+xml' />" +# when 'audio/mpeg' +# "<audio controls><source src='https://mediacru.sh/#{file.file}'></audio>" +# el + + pastebin: regExp: /.*(?:pastebin.com\/(?!u\/))([^#\&\?]*).*/ el: (a) -> From c53b84709932f29c09c49583b374d6809ba670ab Mon Sep 17 00:00:00 2001 From: Zixaphir <zixaphirmoxphar@gmail.com> Date: Sat, 10 Aug 2013 02:06:42 -0700 Subject: [PATCH 159/172] Only Appchan has dropmarkers --- builds/4chan-X.user.js | 2 +- builds/crx/script.js | 2 +- src/Menu/Menu.coffee | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js index d10334602..8073cd317 100644 --- a/builds/4chan-X.user.js +++ b/builds/4chan-X.user.js @@ -7035,7 +7035,7 @@ a = $.el('a', { className: 'menu-button brackets-wrap', - innerHTML: '<span class=drop-marker></span>', + innerHTML: '<i></i>', href: 'javascript:;' }); return { diff --git a/builds/crx/script.js b/builds/crx/script.js index 233ab106b..68f8eea56 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -7016,7 +7016,7 @@ a = $.el('a', { className: 'menu-button brackets-wrap', - innerHTML: '<span class=drop-marker></span>', + innerHTML: '<i></i>', href: 'javascript:;' }); return { diff --git a/src/Menu/Menu.coffee b/src/Menu/Menu.coffee index 29fc8399e..2d7076f1b 100644 --- a/src/Menu/Menu.coffee +++ b/src/Menu/Menu.coffee @@ -1,7 +1,7 @@ Menu = do -> a = $.el 'a', className: 'menu-button brackets-wrap' - innerHTML: '<span class=drop-marker></span>' + innerHTML: '<i></i>' href: 'javascript:;' init: -> From a12d50981b3ee682a62a2f5c8266cc7447031d27 Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Sat, 10 Aug 2013 15:05:44 +0200 Subject: [PATCH 160/172] Shorter dice format. --- src/Miscellaneous/Dice.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Miscellaneous/Dice.coffee b/src/Miscellaneous/Dice.coffee index 93bdb06cd..7b7a3c386 100644 --- a/src/Miscellaneous/Dice.coffee +++ b/src/Miscellaneous/Dice.coffee @@ -8,4 +8,4 @@ Dice = return if @isClone or not dicestats = @info.email?.match /dice[+\s](\d+)d(\d+)/ # Use the text node directly, as the <b> has two <br>. roll = $('b', @nodes.comment).firstChild - roll.data = "Rolled #{dicestats[1]}d#{dicestats[2]} and got #{roll.data.slice 7}" + roll.data = "Rolled #{dicestats[1]}d#{dicestats[2]}: #{roll.data.slice 7}" From 4b332e19e0153887b28ff4292461fde68b488ab7 Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Sat, 10 Aug 2013 15:26:34 +0200 Subject: [PATCH 161/172] Fix #1196, #1222. --- src/Quotelinks/Quotify.coffee | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Quotelinks/Quotify.coffee b/src/Quotelinks/Quotify.coffee index 1d26c6e79..8f94ee226 100644 --- a/src/Quotelinks/Quotify.coffee +++ b/src/Quotelinks/Quotify.coffee @@ -21,11 +21,15 @@ Quotify = # This won't be necessary once 4chan # stops quotifying inside code tags: # https://github.com/4chan/4chan-JS/issues/77 - $.replace deadlink, [deadlink.childNodes...] + Quotify.fixDeadlink deadlink return quote = deadlink.textContent return unless postID = quote.match(/\d+$/)?[0] + if postID[0] is '0' + # Fix quotelinks that start with a `0`. + Quotify.fixDeadlink deadlink + return boardID = if m = quote.match /^>>>\/([a-z\d]+)/ m[1] else @@ -70,3 +74,6 @@ Quotify = $.replace deadlink, a if $.hasClass a, 'quotelink' @nodes.quotelinks.push a + + fixDeadlink: (deadlink) -> + $.replace deadlink, [deadlink.childNodes...] From 79aa6eb975d5be5f9bcfbd357c40556e3d7f7e75 Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Sat, 10 Aug 2013 15:34:22 +0200 Subject: [PATCH 162/172] Release 4chan X v3.6.0. --- CHANGELOG.md | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 353dce5e0..b3b04ffcf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +## 3.6.0 - *2013-08-10* + - **New feature**: `Show Dice Roll` - Shows dice that were entered into the email field on /tg/. - Fix impossibility to create new threads when in dead threads. diff --git a/package.json b/package.json index 33b87d4d9..2f097642f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "4chan-X", - "version": "3.5.7", + "version": "3.6.0", "description": "Cross-browser extension for productive lurking on 4chan.", "meta": { "name": "4chan X", From 1e439167d76d0f8d5fb7631405ee796110107287 Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Sat, 10 Aug 2013 15:46:48 +0200 Subject: [PATCH 163/172] Shorter QuoteYou code. Not sure why I had written it that way. --- src/Quotelinks/QuoteYou.coffee | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/Quotelinks/QuoteYou.coffee b/src/Quotelinks/QuoteYou.coffee index 3013abd03..11e4763dc 100644 --- a/src/Quotelinks/QuoteYou.coffee +++ b/src/Quotelinks/QuoteYou.coffee @@ -10,11 +10,6 @@ QuoteYou = node: -> # Stop there if it's a clone. return if @isClone - # Stop there if there's no quotes in that post. - return unless (quotes = @quotes).length - {quotelinks} = @nodes - - for quotelink in quotelinks - if QR.db.get Get.postDataFromLink quotelink - $.add quotelink, $.tn QuoteYou.text + for quotelink in quotelinks when QR.db.get Get.postDataFromLink quotelink + $.add quotelink, $.tn QuoteYou.text return From 1d83ccbc12c4ce0e04887ca55aacae042c9432b7 Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Sat, 10 Aug 2013 15:48:47 +0200 Subject: [PATCH 164/172] .nex files are not necessary anymore. --- Gruntfile.coffee | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/Gruntfile.coffee b/Gruntfile.coffee index 9410f0c6b..535eb2bca 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -69,15 +69,6 @@ module.exports = (grunt) -> dest: 'builds/crx/' expand: true flatten: true - # for_d19 - # 19 juin 2013 10:32:22 - # We're currently sniffing the type of the file based on file extension. - # We have a different type of content, Themes, which use a pure zip-file with a .zip ending. - # This solution is sub-optimal and will be changed in the future. - # For now, upload an unsigned ZIP-file with the ending .nex or .crx. - nex: - src: 'builds/<%= pkg.name %>.zip' - dest: 'builds/<%= pkg.name %>.nex' coffee: script: src: 'tmp-<%= pkg.type %>/script.coffee' @@ -158,7 +149,7 @@ module.exports = (grunt) -> 'clean:tmpuserscript' ] - grunt.registerTask 'release', ['shell:commit', 'shell:push', 'build-crx', 'compress:crx', 'copy:nex'] + grunt.registerTask 'release', ['shell:commit', 'shell:push', 'build-crx', 'compress:crx'] grunt.registerTask 'patch', ['bump', 'updcl:3', 'release'] grunt.registerTask 'minor', ['bump:minor', 'updcl:2', 'release'] grunt.registerTask 'major', ['bump:major', 'updcl:1', 'release'] From e890680dc4f187d1d010a8606961aa3e9126ae51 Mon Sep 17 00:00:00 2001 From: Mayhem <stepien.nicolas@gmail.com> Date: Sat, 10 Aug 2013 15:52:24 +0200 Subject: [PATCH 165/172] Fix missing `@nodes.`. --- src/Quotelinks/QuoteYou.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Quotelinks/QuoteYou.coffee b/src/Quotelinks/QuoteYou.coffee index 11e4763dc..acc4d2e64 100644 --- a/src/Quotelinks/QuoteYou.coffee +++ b/src/Quotelinks/QuoteYou.coffee @@ -10,6 +10,6 @@ QuoteYou = node: -> # Stop there if it's a clone. return if @isClone - for quotelink in quotelinks when QR.db.get Get.postDataFromLink quotelink + for quotelink in @nodes.quotelinks when QR.db.get Get.postDataFromLink quotelink $.add quotelink, $.tn QuoteYou.text return From 34e580f475048fab340d431fc77fdaa04a1b1581 Mon Sep 17 00:00:00 2001 From: Zixaphir <zixaphirmoxphar@gmail.com> Date: Sat, 10 Aug 2013 13:54:03 -0700 Subject: [PATCH 166/172] ~ --- builds/4chan-X.user.js | 14 +++++++------- builds/crx/script.js | 8 ++++---- src/Posting/QuickReply.coffee | 8 ++++++-- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js index 8073cd317..0378f4f4e 100644 --- a/builds/4chan-X.user.js +++ b/builds/4chan-X.user.js @@ -5904,7 +5904,7 @@ } }, dialog: function() { - var dialog, elm, mimeTypes, name, nodes, thread, _i, _j, _len, _len1, _ref, _ref1; + var dialog, elm, i, items, mimeTypes, name, nodes, thread; dialog = UI.dialog('qr', 'top:0;right:0;', " <div class=move><label><input type=checkbox id=autohide title=Auto-hide>\n Quick Reply\n</label><a href=javascript:; class=close title=Close>×</a><select data-name=thread title='Create a new thread / Reply'><option value=new>New thread</option></select></div><form><div class=persona><input name=name data-name=name list=\"list-name\" placeholder=Name class=field size=1 tabindex=10><input name=email data-name=email list=\"list-email\" placeholder=E-mail class=field size=1 tabindex=20><input name=sub data-name=sub list=\"list-sub\" placeholder=Subject class=field size=1 tabindex=30></div><div class=textarea><textarea data-name=com placeholder=Comment class=field tabindex=40></textarea><span id=char-count></span></div><div id=dump-list-container><div id=dump-list></div><a id=add-post href=javascript:; title=\"Add a post\" tabindex=50>+</a></div><div id=file-n-submit><span id=qr-filename-container class=field tabindex=60><span id=qr-no-file>No selected file</span><span id=qr-filename></span><span id=qr-extras-container><a id=qr-filerm href=javascript:; title='Remove file'>×</a><a id=dump-button title='Dump list'>+</a></span></span><label id=qr-spoiler-label><input type=checkbox id=qr-file-spoiler title='Spoiler image' tabindex=70></label><input type=submit tabindex=80></div><input type=file multiple></form><datalist id=\"list-name\"></datalist><datalist id=\"list-email\"></datalist><datalist id=\"list-sub\"></datalist>"); QR.nodes = nodes = { @@ -5965,9 +5965,9 @@ })); } $.on(nodes.filename.parentNode, 'click keyup', QR.openFileInput); - _ref = $$('*', QR.nodes.el); - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - elm = _ref[_i]; + items = $$('*', QR.nodes.el); + i = 0; + while (elm = items[i++]) { $.on(elm, 'blur', QR.focusout); $.on(elm, 'focus', QR.focusin); } @@ -5992,9 +5992,9 @@ return QR.selected.nodes.spoiler.click(); }); $.on(nodes.fileInput, 'change', QR.fileInput); - _ref1 = ['name', 'email', 'sub', 'com']; - for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { - name = _ref1[_j]; + items = ['name', 'email', 'sub', 'com']; + i = 0; + while (name = items[i++]) { $.on(nodes[name], 'input', function() { return QR.selected.save(this); }); diff --git a/builds/crx/script.js b/builds/crx/script.js index 68f8eea56..03cb0407c 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -5902,7 +5902,7 @@ } }, dialog: function() { - var dialog, mimeTypes, name, nodes, thread, _i, _len, _ref; + var dialog, i, items, mimeTypes, name, nodes, thread; dialog = UI.dialog('qr', 'top:0;right:0;', " <div class=move><label><input type=checkbox id=autohide title=Auto-hide>\n Quick Reply\n</label><a href=javascript:; class=close title=Close>×</a><select data-name=thread title='Create a new thread / Reply'><option value=new>New thread</option></select></div><form><div class=persona><input name=name data-name=name list=\"list-name\" placeholder=Name class=field size=1 tabindex=10><input name=email data-name=email list=\"list-email\" placeholder=E-mail class=field size=1 tabindex=20><input name=sub data-name=sub list=\"list-sub\" placeholder=Subject class=field size=1 tabindex=30></div><div class=textarea><textarea data-name=com placeholder=Comment class=field tabindex=40></textarea><span id=char-count></span></div><div id=dump-list-container><div id=dump-list></div><a id=add-post href=javascript:; title=\"Add a post\" tabindex=50>+</a></div><div id=file-n-submit><span id=qr-filename-container class=field tabindex=60><span id=qr-no-file>No selected file</span><span id=qr-filename></span><span id=qr-extras-container><a id=qr-filerm href=javascript:; title='Remove file'>×</a><a id=dump-button title='Dump list'>+</a></span></span><label id=qr-spoiler-label><input type=checkbox id=qr-file-spoiler title='Spoiler image' tabindex=70></label><input type=submit tabindex=80></div><input type=file multiple></form><datalist id=\"list-name\"></datalist><datalist id=\"list-email\"></datalist><datalist id=\"list-sub\"></datalist>"); QR.nodes = nodes = { @@ -5984,9 +5984,9 @@ return QR.selected.nodes.spoiler.click(); }); $.on(nodes.fileInput, 'change', QR.fileInput); - _ref = ['name', 'email', 'sub', 'com']; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - name = _ref[_i]; + items = ['name', 'email', 'sub', 'com']; + i = 0; + while (name = items[i++]) { $.on(nodes[name], 'input', function() { return QR.selected.save(this); }); diff --git a/src/Posting/QuickReply.coffee b/src/Posting/QuickReply.coffee index d0ccd85e2..332ab2fe8 100644 --- a/src/Posting/QuickReply.coffee +++ b/src/Posting/QuickReply.coffee @@ -930,7 +930,9 @@ QR = <% if (type === 'userscript') { %> # XXX Firefox lacks focusin/focusout support. - for elm in $$ '*', QR.nodes.el + items = $$ '*', QR.nodes.el + i = 0 + while elm = items[i++] $.on elm, 'blur', QR.focusout $.on elm, 'focus', QR.focusin <% } %> @@ -946,7 +948,9 @@ QR = $.on nodes.spoiler, 'change', -> QR.selected.nodes.spoiler.click() $.on nodes.fileInput, 'change', QR.fileInput # save selected post's data - for name in ['name', 'email', 'sub', 'com'] + items = ['name', 'email', 'sub', 'com'] + i = 0 + while name = items[i++] $.on nodes[name], 'input', -> QR.selected.save @ $.on nodes.thread, 'change', -> QR.selected.save @ From f8f91425765dba86be58518f579b12cc3ba98974 Mon Sep 17 00:00:00 2001 From: Zixaphir <zixaphirmoxphar@gmail.com> Date: Sat, 10 Aug 2013 15:34:57 -0700 Subject: [PATCH 167/172] We don't have stable-v3 like Mayhem does --- Gruntfile.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gruntfile.coffee b/Gruntfile.coffee index da3d1bf88..c3f074c60 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -100,7 +100,7 @@ module.exports = (grunt) -> '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 %>."' + 'git tag -af stable -m "<%= pkg.meta.name %> v<%= pkg.version %>."' ].join ' && ' push: From 00093ffd0c8087d49746c5f8c89fec692c7a1359 Mon Sep 17 00:00:00 2001 From: Zixaphir <zixaphirmoxphar@gmail.com> Date: Sun, 11 Aug 2013 14:00:01 -0700 Subject: [PATCH 168/172] Fix building before release --- Gruntfile.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/Gruntfile.coffee b/Gruntfile.coffee index c3f074c60..bc8f3251b 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -167,6 +167,7 @@ module.exports = (grunt) -> ] grunt.registerTask 'release', [ + 'build' 'shell:commit' 'shell:push' 'build-crx' From 708f56c759ed518dd583a628150d526b1f342e28 Mon Sep 17 00:00:00 2001 From: Zixaphir <zixaphirmoxphar@gmail.com> Date: Sun, 11 Aug 2013 23:25:43 -0700 Subject: [PATCH 169/172] Strengthen Linkifier Now can linkify within code tags, providing the initial 'check' is successful. --- LICENSE | 2 +- builds/4chan-X.user.js | 29 ++++++++++++++--------------- builds/crx/script.js | 29 ++++++++++++++--------------- src/Linkification/Linkify.coffee | 28 +++++++++++++--------------- 4 files changed, 42 insertions(+), 46 deletions(-) diff --git a/LICENSE b/LICENSE index a98499ab6..d2ae960d0 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,5 @@ /* -* 4chan X - Version 1.2.25 - 2013-08-10 +* 4chan X - Version 1.2.25 - 2013-08-11 * * Licensed under the MIT license. * https://github.com/seaweedchan/4chan-x/blob/master/LICENSE diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js index 9f95c99b1..9baed19bb 100644 --- a/builds/4chan-X.user.js +++ b/builds/4chan-X.user.js @@ -19,7 +19,7 @@ // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwAgMAAAAqbBEUAAAACVBMVEUAAGcAAABmzDNZt9VtAAAAAXRSTlMAQObYZgAAAHFJREFUKFOt0LENACEIBdBv4Qju4wgWanEj3D6OcIVMKaitYHEU/jwTCQj8W75kiVCSBvdQ5/AvfVHBin11BgdRq3ysBgfwBDRrj3MCIA+oAQaku/Q1cNctrAmyDl577tOThYt/Y1RBM4DgOHzM0HFTAyLukH/cmRnqAAAAAElFTkSuQmCC // ==/UserScript== /* -* 4chan X - Version 1.2.25 - 2013-08-10 +* 4chan X - Version 1.2.25 - 2013-08-11 * * Licensed under the MIT license. * https://github.com/seaweedchan/4chan-x/blob/master/LICENSE @@ -4375,7 +4375,7 @@ if (g.VIEW === 'catalog' || !Conf['Linkify']) { return; } - this.regString = Conf['Allow False Positives'] ? /(\b([a-z]+:\/\/|[a-z]{3,}\.[-a-z0-9]+\.[a-z]|[-a-z0-9]+\.[a-z]|[0-9]+\.[0-9]+\.[0-9]+\.[0-9]|[a-z]{3,}:[a-z0-9?]|[^\s@]+@[a-z0-9.-]+\.[a-z0-9])[^\s'"]+)/gi : /(((magnet|mailto)\:|(www\.)|(news|(ht|f)tp(s?))\:\/\/){1}\S+)/gi; + this.regString = Conf['Allow False Positives'] ? /(\b([-a-z]+:\/\/|[a-z]{3,}\.[-a-z0-9]+\.[a-z]|[-a-z0-9]+\.[a-z]|[\d]+\.[\d]+\.[\d]+\.[\d]+\/|[a-z]{3,}:[a-z0-9?]|[^\s@]+@[a-z0-9.-]+\.[a-z0-9])[^\s'"]+)/gi : /(((magnet|mailto)\:|(www\.)|(news|(ht|f)tp(s?))\:\/\/){1}\S+)/gi; if (Conf['Comment Expansion']) { ExpandComment.callbacks.push(this.node); } @@ -4403,16 +4403,15 @@ } return; } - snapshot = $.X('.//text()', this.nodes.comment); + snapshot = $.X('.//br|.//text()', this.nodes.comment); i = 0; while (node = snapshot.snapshotItem(i++)) { if (node.parentElement.nodeName === "A") { continue; } - data = node.data; - if (Linkify.regString.test(data)) { + if (Linkify.regString.test(node.data)) { Linkify.regString.lastIndex = 0; - Linkify.gatherLinks(node, this); + Linkify.gatherLinks(snapshot, this, node, i); } } if (!(Conf['Embedding'] || Conf['Link Title'])) { @@ -4431,7 +4430,7 @@ } } }, - gatherLinks: function(node, post) { + gatherLinks: function(snapshot, post, node, i) { var data, index, len, len2, link, links, match, range, _i, _len, _ref; data = node.data; @@ -4451,7 +4450,7 @@ } Linkify.regString.lastIndex = 0; if (match) { - Linkify.seek(match, node, post); + links.push(Linkify.seek(snapshot, node, match, i)); } _ref = links.reverse(); for (_i = 0, _len = _ref.length; _i < _len; _i++) { @@ -4459,14 +4458,13 @@ Linkify.makeLink(range, post); } }, - seek: function(match, node, post) { - var data, index, link, next, range, result; + seek: function(snapshot, node, match, i) { + var data, link, next, range, result; - index = match.index; link = match[0]; range = document.createRange(); - range.setStart(node, index); - while ((next = node.nextSibling) && next.nodeName !== 'BR') { + range.setStart(node, match.index); + while ((next = snapshot.snapshotItem(i++)) && next.nodeName !== 'BR') { node = next; data = node.data; if (result = /[\s'"]/.exec(data)) { @@ -4476,7 +4474,7 @@ if (range.collapsed) { range.setEndAfter(node); } - return Linkify.makeLink(range, post); + return range; }, makeLink: function(range, post) { var a, link; @@ -4489,7 +4487,8 @@ target: '_blank', href: link }); - range.surroundContents(a); + $.add(a, range.extractContents()); + range.insertNode(a); post.nodes.links.push(a); }, services: function(link) { diff --git a/builds/crx/script.js b/builds/crx/script.js index 00e3f50ad..d967cac01 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -1,6 +1,6 @@ // Generated by CoffeeScript /* -* 4chan X - Version 1.2.25 - 2013-08-10 +* 4chan X - Version 1.2.25 - 2013-08-11 * * Licensed under the MIT license. * https://github.com/seaweedchan/4chan-x/blob/master/LICENSE @@ -4380,7 +4380,7 @@ if (g.VIEW === 'catalog' || !Conf['Linkify']) { return; } - this.regString = Conf['Allow False Positives'] ? /(\b([a-z]+:\/\/|[a-z]{3,}\.[-a-z0-9]+\.[a-z]|[-a-z0-9]+\.[a-z]|[0-9]+\.[0-9]+\.[0-9]+\.[0-9]|[a-z]{3,}:[a-z0-9?]|[^\s@]+@[a-z0-9.-]+\.[a-z0-9])[^\s'"]+)/gi : /(((magnet|mailto)\:|(www\.)|(news|(ht|f)tp(s?))\:\/\/){1}\S+)/gi; + this.regString = Conf['Allow False Positives'] ? /(\b([-a-z]+:\/\/|[a-z]{3,}\.[-a-z0-9]+\.[a-z]|[-a-z0-9]+\.[a-z]|[\d]+\.[\d]+\.[\d]+\.[\d]+\/|[a-z]{3,}:[a-z0-9?]|[^\s@]+@[a-z0-9.-]+\.[a-z0-9])[^\s'"]+)/gi : /(((magnet|mailto)\:|(www\.)|(news|(ht|f)tp(s?))\:\/\/){1}\S+)/gi; if (Conf['Comment Expansion']) { ExpandComment.callbacks.push(this.node); } @@ -4408,16 +4408,15 @@ } return; } - snapshot = $.X('.//text()', this.nodes.comment); + snapshot = $.X('.//br|.//text()', this.nodes.comment); i = 0; while (node = snapshot.snapshotItem(i++)) { if (node.parentElement.nodeName === "A") { continue; } - data = node.data; - if (Linkify.regString.test(data)) { + if (Linkify.regString.test(node.data)) { Linkify.regString.lastIndex = 0; - Linkify.gatherLinks(node, this); + Linkify.gatherLinks(snapshot, this, node, i); } } if (!(Conf['Embedding'] || Conf['Link Title'])) { @@ -4436,7 +4435,7 @@ } } }, - gatherLinks: function(node, post) { + gatherLinks: function(snapshot, post, node, i) { var data, index, len, len2, link, links, match, range, _i, _len, _ref; data = node.data; @@ -4456,7 +4455,7 @@ } Linkify.regString.lastIndex = 0; if (match) { - Linkify.seek(match, node, post); + links.push(Linkify.seek(snapshot, node, match, i)); } _ref = links.reverse(); for (_i = 0, _len = _ref.length; _i < _len; _i++) { @@ -4464,14 +4463,13 @@ Linkify.makeLink(range, post); } }, - seek: function(match, node, post) { - var data, index, link, next, range, result; + seek: function(snapshot, node, match, i) { + var data, link, next, range, result; - index = match.index; link = match[0]; range = document.createRange(); - range.setStart(node, index); - while ((next = node.nextSibling) && next.nodeName !== 'BR') { + range.setStart(node, match.index); + while ((next = snapshot.snapshotItem(i++)) && next.nodeName !== 'BR') { node = next; data = node.data; if (result = /[\s'"]/.exec(data)) { @@ -4481,7 +4479,7 @@ if (range.collapsed) { range.setEndAfter(node); } - return Linkify.makeLink(range, post); + return range; }, makeLink: function(range, post) { var a, link; @@ -4494,7 +4492,8 @@ target: '_blank', href: link }); - range.surroundContents(a); + $.add(a, range.extractContents()); + range.insertNode(a); post.nodes.links.push(a); }, services: function(link) { diff --git a/src/Linkification/Linkify.coffee b/src/Linkification/Linkify.coffee index b935fba66..5da99d362 100644 --- a/src/Linkification/Linkify.coffee +++ b/src/Linkification/Linkify.coffee @@ -5,13 +5,13 @@ Linkify = @regString = if Conf['Allow False Positives'] ///( \b( - [a-z]+:// + [-a-z]+:// | [a-z]{3,}\.[-a-z0-9]+\.[a-z] | [-a-z0-9]+\.[a-z] | - [0-9]+\.[0-9]+\.[0-9]+\.[0-9] + [\d]+\.[\d]+\.[\d]+\.[\d]+/ | [a-z]{3,}:[a-z0-9?] | @@ -43,17 +43,15 @@ Linkify = return - snapshot = $.X './/text()', @nodes.comment + snapshot = $.X './/br|.//text()', @nodes.comment i = 0 while node = snapshot.snapshotItem i++ continue if node.parentElement.nodeName is "A" - data = node.data - - if Linkify.regString.test data + if Linkify.regString.test node.data Linkify.regString.lastIndex = 0 - Linkify.gatherLinks node, @ + Linkify.gatherLinks snapshot, @, node, i return unless Conf['Embedding'] or Conf['Link Title'] @@ -66,7 +64,7 @@ Linkify = return - gatherLinks: (node, post) -> + gatherLinks: (snapshot, post, node, i) -> {data} = node len = data.length links = [] @@ -86,20 +84,19 @@ Linkify = Linkify.regString.lastIndex = 0 if match - Linkify.seek match, node, post + links.push Linkify.seek snapshot, node, match, i for range in links.reverse() Linkify.makeLink range, post return - seek: (match, node, post) -> - {index} = match + seek: (snapshot, node, match, i) -> link = match[0] range = document.createRange() - range.setStart node, index + range.setStart node, match.index - while (next = node.nextSibling) and next.nodeName isnt 'BR' + while (next = snapshot.snapshotItem i++) and next.nodeName isnt 'BR' node = next data = node.data if result = /[\s'"]/.exec data @@ -108,7 +105,7 @@ Linkify = if range.collapsed range.setEndAfter node - Linkify.makeLink range, post + range makeLink: (range, post) -> link = range.toString() @@ -127,7 +124,8 @@ Linkify = rel: 'nofollow noreferrer' target: '_blank' href: link - range.surroundContents a + $.add a, range.extractContents() + range.insertNode a post.nodes.links.push a return From 045ba42296022d9492d2ce33f70c00b1ad6454ca Mon Sep 17 00:00:00 2001 From: Zixaphir <zixaphirmoxphar@gmail.com> Date: Sun, 11 Aug 2013 23:31:33 -0700 Subject: [PATCH 170/172] Single-handedly one of the most expensive features in 4chan X? LETS ADD RECURSION! --- builds/4chan-X.user.js | 11 +++++++---- builds/crx/script.js | 11 +++++++---- src/Linkification/Linkify.coffee | 9 ++++++--- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js index 9baed19bb..4b80565ec 100644 --- a/builds/4chan-X.user.js +++ b/builds/4chan-X.user.js @@ -4450,7 +4450,7 @@ } Linkify.regString.lastIndex = 0; if (match) { - links.push(Linkify.seek(snapshot, node, match, i)); + links.push(Linkify.seek(snapshot, post, node, match, i)); } _ref = links.reverse(); for (_i = 0, _len = _ref.length; _i < _len; _i++) { @@ -4458,8 +4458,8 @@ Linkify.makeLink(range, post); } }, - seek: function(snapshot, node, match, i) { - var data, link, next, range, result; + seek: function(snapshot, post, node, match, i) { + var data, index, link, next, range, result; link = match[0]; range = document.createRange(); @@ -4468,7 +4468,10 @@ node = next; data = node.data; if (result = /[\s'"]/.exec(data)) { - range.setEnd(node, result.index); + index = result.index; + range.setEnd(node, index); + Linkify.regString.lastIndex = index; + Linkify.gatherLinks(snapshot, post, node, i); } } if (range.collapsed) { diff --git a/builds/crx/script.js b/builds/crx/script.js index d967cac01..60987bcda 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -4455,7 +4455,7 @@ } Linkify.regString.lastIndex = 0; if (match) { - links.push(Linkify.seek(snapshot, node, match, i)); + links.push(Linkify.seek(snapshot, post, node, match, i)); } _ref = links.reverse(); for (_i = 0, _len = _ref.length; _i < _len; _i++) { @@ -4463,8 +4463,8 @@ Linkify.makeLink(range, post); } }, - seek: function(snapshot, node, match, i) { - var data, link, next, range, result; + seek: function(snapshot, post, node, match, i) { + var data, index, link, next, range, result; link = match[0]; range = document.createRange(); @@ -4473,7 +4473,10 @@ node = next; data = node.data; if (result = /[\s'"]/.exec(data)) { - range.setEnd(node, result.index); + index = result.index; + range.setEnd(node, index); + Linkify.regString.lastIndex = index; + Linkify.gatherLinks(snapshot, post, node, i); } } if (range.collapsed) { diff --git a/src/Linkification/Linkify.coffee b/src/Linkification/Linkify.coffee index 5da99d362..8996da4bd 100644 --- a/src/Linkification/Linkify.coffee +++ b/src/Linkification/Linkify.coffee @@ -84,14 +84,14 @@ Linkify = Linkify.regString.lastIndex = 0 if match - links.push Linkify.seek snapshot, node, match, i + links.push Linkify.seek snapshot, post, node, match, i for range in links.reverse() Linkify.makeLink range, post return - seek: (snapshot, node, match, i) -> + seek: (snapshot, post, node, match, i) -> link = match[0] range = document.createRange() range.setStart node, match.index @@ -100,7 +100,10 @@ Linkify = node = next data = node.data if result = /[\s'"]/.exec data - range.setEnd node, result.index + {index} = result + range.setEnd node, index + Linkify.regString.lastIndex = index + Linkify.gatherLinks snapshot, post, node, i if range.collapsed range.setEndAfter node From 256e20aea5147a7a20ed1b1d5ec367a2c1b31836 Mon Sep 17 00:00:00 2001 From: Zixaphir <zixaphirmoxphar@gmail.com> Date: Sun, 11 Aug 2013 23:33:44 -0700 Subject: [PATCH 171/172] Since we're adding recursion, we should probably move our variable declaration outside of the potential recursive function --- builds/4chan-X.user.js | 16 ++++++++-------- builds/crx/script.js | 16 ++++++++-------- src/Linkification/Linkify.coffee | 8 ++++---- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js index 4b80565ec..a9f20840c 100644 --- a/builds/4chan-X.user.js +++ b/builds/4chan-X.user.js @@ -4388,7 +4388,7 @@ }); }, node: function() { - var data, el, i, items, node, range, snapshot; + var data, el, i, items, links, node, range, snapshot, _i, _len, _ref; if (this.isClone) { if (Conf['Embedding']) { @@ -4409,10 +4409,16 @@ if (node.parentElement.nodeName === "A") { continue; } + links = []; if (Linkify.regString.test(node.data)) { Linkify.regString.lastIndex = 0; Linkify.gatherLinks(snapshot, this, node, i); } + _ref = links.reverse(); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + range = _ref[_i]; + Linkify.makeLink(range, post); + } } if (!(Conf['Embedding'] || Conf['Link Title'])) { return; @@ -4431,11 +4437,10 @@ } }, gatherLinks: function(snapshot, post, node, i) { - var data, index, len, len2, link, links, match, range, _i, _len, _ref; + var data, index, len, len2, link, match, range; data = node.data; len = data.length; - links = []; while ((match = Linkify.regString.exec(data))) { index = match.index; link = match[0]; @@ -4452,11 +4457,6 @@ if (match) { links.push(Linkify.seek(snapshot, post, node, match, i)); } - _ref = links.reverse(); - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - range = _ref[_i]; - Linkify.makeLink(range, post); - } }, seek: function(snapshot, post, node, match, i) { var data, index, link, next, range, result; diff --git a/builds/crx/script.js b/builds/crx/script.js index 60987bcda..567de0215 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -4393,7 +4393,7 @@ }); }, node: function() { - var data, el, i, items, node, range, snapshot; + var data, el, i, items, links, node, range, snapshot, _i, _len, _ref; if (this.isClone) { if (Conf['Embedding']) { @@ -4414,10 +4414,16 @@ if (node.parentElement.nodeName === "A") { continue; } + links = []; if (Linkify.regString.test(node.data)) { Linkify.regString.lastIndex = 0; Linkify.gatherLinks(snapshot, this, node, i); } + _ref = links.reverse(); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + range = _ref[_i]; + Linkify.makeLink(range, post); + } } if (!(Conf['Embedding'] || Conf['Link Title'])) { return; @@ -4436,11 +4442,10 @@ } }, gatherLinks: function(snapshot, post, node, i) { - var data, index, len, len2, link, links, match, range, _i, _len, _ref; + var data, index, len, len2, link, match, range; data = node.data; len = data.length; - links = []; while ((match = Linkify.regString.exec(data))) { index = match.index; link = match[0]; @@ -4457,11 +4462,6 @@ if (match) { links.push(Linkify.seek(snapshot, post, node, match, i)); } - _ref = links.reverse(); - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - range = _ref[_i]; - Linkify.makeLink(range, post); - } }, seek: function(snapshot, post, node, match, i) { var data, index, link, next, range, result; diff --git a/src/Linkification/Linkify.coffee b/src/Linkification/Linkify.coffee index 8996da4bd..9ab8209a5 100644 --- a/src/Linkification/Linkify.coffee +++ b/src/Linkification/Linkify.coffee @@ -48,11 +48,15 @@ Linkify = while node = snapshot.snapshotItem i++ continue if node.parentElement.nodeName is "A" + links = [] if Linkify.regString.test node.data Linkify.regString.lastIndex = 0 Linkify.gatherLinks snapshot, @, node, i + for range in links.reverse() + Linkify.makeLink range, post + return unless Conf['Embedding'] or Conf['Link Title'] items = @nodes.links @@ -67,7 +71,6 @@ Linkify = gatherLinks: (snapshot, post, node, i) -> {data} = node len = data.length - links = [] while (match = Linkify.regString.exec data) {index} = match @@ -86,9 +89,6 @@ Linkify = if match links.push Linkify.seek snapshot, post, node, match, i - for range in links.reverse() - Linkify.makeLink range, post - return seek: (snapshot, post, node, match, i) -> From a8ee9b39929320748ce69dd5b51f2a482c4941cf Mon Sep 17 00:00:00 2001 From: Zixaphir <zixaphirmoxphar@gmail.com> Date: Sun, 11 Aug 2013 23:44:05 -0700 Subject: [PATCH 172/172] Oops, accidentally got tired. --- builds/4chan-X.user.js | 17 +++++++++-------- builds/crx/script.js | 17 +++++++++-------- src/Linkification/Linkify.coffee | 18 +++++++++--------- 3 files changed, 27 insertions(+), 25 deletions(-) diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js index a9f20840c..b7c190b1f 100644 --- a/builds/4chan-X.user.js +++ b/builds/4chan-X.user.js @@ -4412,12 +4412,12 @@ links = []; if (Linkify.regString.test(node.data)) { Linkify.regString.lastIndex = 0; - Linkify.gatherLinks(snapshot, this, node, i); + Linkify.gatherLinks(snapshot, this, node, links, i); } _ref = links.reverse(); for (_i = 0, _len = _ref.length; _i < _len; _i++) { range = _ref[_i]; - Linkify.makeLink(range, post); + this.nodes.links.push(Linkify.makeLink(range, this)); } } if (!(Conf['Embedding'] || Conf['Link Title'])) { @@ -4436,7 +4436,7 @@ } } }, - gatherLinks: function(snapshot, post, node, i) { + gatherLinks: function(snapshot, post, node, links, i) { var data, index, len, len2, link, match, range; data = node.data; @@ -4455,10 +4455,10 @@ } Linkify.regString.lastIndex = 0; if (match) { - links.push(Linkify.seek(snapshot, post, node, match, i)); + links.push(Linkify.seek(snapshot, post, node, links, match, i)); } }, - seek: function(snapshot, post, node, match, i) { + seek: function(snapshot, post, node, links, match, i) { var data, index, link, next, range, result; link = match[0]; @@ -4471,7 +4471,8 @@ index = result.index; range.setEnd(node, index); Linkify.regString.lastIndex = index; - Linkify.gatherLinks(snapshot, post, node, i); + Linkify.gatherLinks(snapshot, post, node, links, i); + return range; } } if (range.collapsed) { @@ -4479,7 +4480,7 @@ } return range; }, - makeLink: function(range, post) { + makeLink: function(range) { var a, link; link = range.toString(); @@ -4492,7 +4493,7 @@ }); $.add(a, range.extractContents()); range.insertNode(a); - post.nodes.links.push(a); + return a; }, services: function(link) { var href, key, match, type, _ref; diff --git a/builds/crx/script.js b/builds/crx/script.js index 567de0215..d7146a8ab 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -4417,12 +4417,12 @@ links = []; if (Linkify.regString.test(node.data)) { Linkify.regString.lastIndex = 0; - Linkify.gatherLinks(snapshot, this, node, i); + Linkify.gatherLinks(snapshot, this, node, links, i); } _ref = links.reverse(); for (_i = 0, _len = _ref.length; _i < _len; _i++) { range = _ref[_i]; - Linkify.makeLink(range, post); + this.nodes.links.push(Linkify.makeLink(range, this)); } } if (!(Conf['Embedding'] || Conf['Link Title'])) { @@ -4441,7 +4441,7 @@ } } }, - gatherLinks: function(snapshot, post, node, i) { + gatherLinks: function(snapshot, post, node, links, i) { var data, index, len, len2, link, match, range; data = node.data; @@ -4460,10 +4460,10 @@ } Linkify.regString.lastIndex = 0; if (match) { - links.push(Linkify.seek(snapshot, post, node, match, i)); + links.push(Linkify.seek(snapshot, post, node, links, match, i)); } }, - seek: function(snapshot, post, node, match, i) { + seek: function(snapshot, post, node, links, match, i) { var data, index, link, next, range, result; link = match[0]; @@ -4476,7 +4476,8 @@ index = result.index; range.setEnd(node, index); Linkify.regString.lastIndex = index; - Linkify.gatherLinks(snapshot, post, node, i); + Linkify.gatherLinks(snapshot, post, node, links, i); + return range; } } if (range.collapsed) { @@ -4484,7 +4485,7 @@ } return range; }, - makeLink: function(range, post) { + makeLink: function(range) { var a, link; link = range.toString(); @@ -4497,7 +4498,7 @@ }); $.add(a, range.extractContents()); range.insertNode(a); - post.nodes.links.push(a); + return a; }, services: function(link) { var href, key, match, type, _ref; diff --git a/src/Linkification/Linkify.coffee b/src/Linkification/Linkify.coffee index 9ab8209a5..74dca4b22 100644 --- a/src/Linkification/Linkify.coffee +++ b/src/Linkification/Linkify.coffee @@ -52,10 +52,10 @@ Linkify = if Linkify.regString.test node.data Linkify.regString.lastIndex = 0 - Linkify.gatherLinks snapshot, @, node, i + Linkify.gatherLinks snapshot, @, node, links, i for range in links.reverse() - Linkify.makeLink range, post + @nodes.links.push Linkify.makeLink range, @ return unless Conf['Embedding'] or Conf['Link Title'] @@ -68,7 +68,7 @@ Linkify = return - gatherLinks: (snapshot, post, node, i) -> + gatherLinks: (snapshot, post, node, links, i) -> {data} = node len = data.length @@ -87,11 +87,11 @@ Linkify = Linkify.regString.lastIndex = 0 if match - links.push Linkify.seek snapshot, post, node, match, i + links.push Linkify.seek snapshot, post, node, links, match, i return - seek: (snapshot, post, node, match, i) -> + seek: (snapshot, post, node, links, match, i) -> link = match[0] range = document.createRange() range.setStart node, match.index @@ -103,14 +103,15 @@ Linkify = {index} = result range.setEnd node, index Linkify.regString.lastIndex = index - Linkify.gatherLinks snapshot, post, node, i + Linkify.gatherLinks snapshot, post, node, links, i + return range if range.collapsed range.setEndAfter node range - makeLink: (range, post) -> + makeLink: (range) -> link = range.toString() link = if link.contains ':' @@ -129,8 +130,7 @@ Linkify = href: link $.add a, range.extractContents() range.insertNode a - post.nodes.links.push a - return + a services: (link) -> href = link.href