From 0a33b5a6d534089a35b66c05e273f941c094c889 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Fri, 29 Mar 2013 05:59:23 +0100 Subject: [PATCH 001/104] Make $.get use the Chrome storage API. Close #503 Still need to fix $.sync, which I'll do tomorrow. Good night. --- lib/$.coffee | 45 +++++--- src/features.coffee | 244 +++++++++++++++++++++++++++----------------- src/main.coffee | 61 ++++++----- src/qr.coffee | 56 ++++++---- 4 files changed, 250 insertions(+), 156 deletions(-) diff --git a/lib/$.coffee b/lib/$.coffee index 9c12aba7a..68d6be255 100644 --- a/lib/$.coffee +++ b/lib/$.coffee @@ -200,15 +200,21 @@ $.extend $, # Round to an integer otherwise. Math.round size "#{size} #{['B', 'KB', 'MB', 'GB'][unit]}" - + item: (key, val) -> + item = {} + item[key] = val + item <% if (type === 'crx') { %> + # https://developer.chrome.com/extensions/storage.html delete: (keys) -> chrome.storage.sync.remove keys - get: (key, defaultVal) -> - if val = localStorage.getItem g.NAMESPACE + key - JSON.parse val + get: (key, val, cb) -> + if arguments.length is 2 + items = key + cb = val else - defaultVal + items = $.item key, val + chrome.storage.sync.get items, cb set: (key, val) -> item = {} item[key] = val @@ -231,11 +237,17 @@ do -> localStorage.removeItem key delete scriptStorage[key] return - $.get = (key, defaultVal) -> - if val = scriptStorage[g.NAMESPACE + key] - JSON.parse val + $.get = (key, val, cb) -> + if arguments.length is 2 + items = key + cb = val else - defaultVal + items = $.item key, val + $.queueTask -> + for key of items + if val = scriptStorage[g.NAMESPACE + key] + items[key] = JSON.parse val + cb items $.set = (key, val) -> key = g.NAMESPACE + key val = JSON.stringify val @@ -243,6 +255,7 @@ do -> localStorage.setItem key, val scriptStorage[key] = val <% } else { %> + # http://wiki.greasespot.net/Main_Page delete: (key) -> unless keys instanceof Array keys = [keys] @@ -251,11 +264,17 @@ do -> localStorage.removeItem key GM_deleteValue key return - get: (key, defaultVal) -> - if val = GM_getValue g.NAMESPACE + key - JSON.parse val + get: (key, val, cb) -> + if arguments.length is 2 + items = key + cb = val else - defaultVal + items = $.item key, val + $.queueTask -> + for key of items + if val = GM_getValue g.NAMESPACE + key + items[key] = JSON.parse val + cb items set: (key, val) -> key = g.NAMESPACE + key val = JSON.stringify val diff --git a/src/features.coffee b/src/features.coffee index 6df6b8de4..a6bcb8045 100644 --- a/src/features.coffee +++ b/src/features.coffee @@ -210,10 +210,11 @@ Settings = order: 110 open: -> Conf['Enable 4chan\'s Extension'] - if (prevVersion = $.get 'previousversion', null) isnt g.VERSION + $.get 'previousversion', null, (item) -> + return if item['previousversion'] is g.VERSION $.set 'lastupdate', Date.now() $.set 'previousversion', g.VERSION - $.on d, '4chanXInitFinished', Settings.open unless prevVersion + $.on d, '4chanXInitFinished', Settings.open unless item['previousversion'] Settings.addSection 'Main', Settings.main Settings.addSection 'Filter', Settings.filter @@ -309,37 +310,54 @@ Settings = $.on $('.import', section), 'click', Settings.import $.on $('input', section), 'change', Settings.onImport + items = {} + inputs = {} for key, obj of Config.main fs = $.el 'fieldset', innerHTML: "#{key}" for key, arr of obj - checked = if $.get(key, Conf[key]) then 'checked' else '' description = arr[1] div = $.el 'div', - innerHTML: ": #{description}" - $.on $('input', div), 'change', $.cb.checked + innerHTML: ": #{description}" + input = $ 'input', div + $.on input, 'change', $.cb.checked + items[key] = Conf[key] + inputs[key] = input $.add fs, div $.add section, fs - hiddenNum = 0 - for ID, thread of ThreadHiding.getHiddenThreads().threads - hiddenNum++ - for ID, thread of ReplyHiding.getHiddenPosts().threads - for ID, post of thread - hiddenNum++ + $.get items, (items) -> + for key, val of items + inputs[key].checked = val + return + div = $.el 'div', - innerHTML: ": Clear manually hidden threads and posts on /#{g.BOARD}/." - $.on $('button', div), 'click', -> + innerHTML: ": Clear manually hidden threads and posts on /#{g.BOARD}/." + button = $ 'button', div + hiddenNum = 0 + ThreadHiding.getHiddenThreads (hiddenThreads) -> + for ID, thread of hiddenThreads.threads + hiddenNum++ + button.textContent = "Hidden: #{hiddenNum}" + ReplyHiding.getHiddenPosts (hiddenPosts) -> + for ID, thread of hiddenPosts.threads + for ID, post of thread + hiddenNum++ + button.textContent = "Hidden: #{hiddenNum}" + $.on button, 'click', -> @textContent = 'Hidden: 0' $.delete ["hiddenThreads.#{g.BOARD}", "hiddenPosts.#{g.BOARD}"] $.after $('input[name="Stubs"]', section).parentNode.parentNode, div - export: -> - now = Date.now() - data = - version: g.VERSION - date: now - Conf: Conf - WatchedThreads: $.get('WatchedThreads', {}) + export: (now, data) -> + unless typeof now is 'number' + now = Date.now() + data = + version: g.VERSION + date: now + Conf: Conf + $.get 'WatchedThreads', {}, (item) -> + data.WatchedThreads = item.WatchedThreads + Settings.export now, data a = $.el 'a', className: 'warning' textContent: 'Save me!' @@ -478,8 +496,9 @@ Settings = ta = $.el 'textarea', name: name className: 'field' - value: $.get name, Conf[name] spellcheck: false + $.get name, Conf[name], (item) -> + ta.value = item[name] $.on ta, 'change', $.cb.value $.add div, ta return @@ -529,7 +548,8 @@ Settings = """ sauce = $ 'textarea', section - sauce.value = $.get 'sauces', Conf['sauces'] + $.get 'sauces', Conf['sauces'], (item) -> + sauce.value = item['sauces'] $.on sauce, 'change', $.cb.value rice: (section) -> @@ -592,17 +612,25 @@ Settings = """ + items = {} + inputs = {} for name in ['boardnav', 'time', 'backlink', 'fileInfo', 'favicon', 'usercss'] input = $ "[name=#{name}]", section - input.value = $.get name, Conf[name] + items[name] = Conf[name] + inputs[name] = input event = if name in ['favicon', 'usercss'] 'change' else 'input' $.on input, event, $.cb.value - unless name in ['usercss'] - $.on input, event, Settings[name] - Settings[name].call input + $.get items, (items) -> + for key, val of items + input = inputs[key] + input.value = val + unless key in ['usercss'] + $.on input, event, Settings[key] + Settings[key].call input + return $.on $('input[name="Custom CSS"]', section), 'change', Settings.togglecss $.on $.id('apply-css'), 'click', Settings.usercss boardnav: -> @@ -652,17 +680,23 @@ Settings = ActionsKeybinds """ - tbody = $ 'tbody', section + tbody = $ 'tbody', section + items = {} + inputs = {} for key, arr of Config.hotkeys tr = $.el 'tr', innerHTML: "#{arr[1]}" input = $ 'input', tr - input.name = key - input.value = $.get key, Conf[key] + input.name = key input.spellcheck = false + items[key] = Conf[key] + inputs[key] = input $.on input, 'keydown', Settings.keybind $.add tbody, tr - return + $.get items, (items) -> + for key, val of items + inputs[key].value = val + return keybind: (e) -> return if e.keyCode is 9 # tab e.preventDefault() @@ -990,13 +1024,14 @@ Filter = re += ';op:yes' # Add a new line before the regexp unless the text is empty. - save = $.get type, '' - save = - if save - "#{save}\n#{re}" - else - re - $.set type, save + $.get type, '', (item) -> + save = item[type] + save = + if save + "#{save}\n#{re}" + else + re + $.set type, save # Open the settings and display & focus the relevant filter textarea. Settings.open 'Filter' @@ -1026,8 +1061,10 @@ ThreadHiding = return unless Conf['Thread Hiding'] $.prepend @OP.nodes.root, ThreadHiding.makeButton @, 'hide' - getHiddenThreads: -> - ThreadHiding.hiddenThreads = $.get "hiddenThreads.#{g.BOARD}", threads: {} + getHiddenThreads: (cb) -> + $.get "hiddenThreads.#{g.BOARD}", threads: {}, (item) -> + ThreadHiding.hiddenThreads = item["hiddenThreads.#{g.BOARD}"] + cb ThreadHiding.hiddenThreads if cb syncFromCatalog: -> # Sync hidden threads from the catalog into the index. @@ -1093,16 +1130,16 @@ ThreadHiding = saveHiddenState: (thread, makeStub) -> # Get fresh hidden threads. - hiddenThreads = ThreadHiding.getHiddenThreads() - hiddenThreadsCatalog = JSON.parse(localStorage.getItem "4chan-hide-t-#{g.BOARD}") or {} - if thread.isHidden - hiddenThreads.threads[thread] = {makeStub} - hiddenThreadsCatalog[thread] = true - else - delete hiddenThreads.threads[thread] - delete hiddenThreadsCatalog[thread] - $.set "hiddenThreads.#{g.BOARD}", hiddenThreads - localStorage.setItem "4chan-hide-t-#{g.BOARD}", JSON.stringify hiddenThreadsCatalog + ThreadHiding.getHiddenThreads (hiddenThreads) -> + hiddenThreadsCatalog = JSON.parse(localStorage.getItem "4chan-hide-t-#{g.BOARD}") or {} + if thread.isHidden + hiddenThreads.threads[thread] = {makeStub} + hiddenThreadsCatalog[thread] = true + else + delete hiddenThreads.threads[thread] + delete hiddenThreadsCatalog[thread] + $.set "hiddenThreads.#{g.BOARD}", hiddenThreads + localStorage.setItem "4chan-hide-t-#{g.BOARD}", JSON.stringify hiddenThreadsCatalog toggle: (thread) -> unless thread instanceof Thread @@ -1173,8 +1210,10 @@ ReplyHiding = return unless Conf['Reply Hiding'] $.replace $('.sideArrows', @nodes.root), ReplyHiding.makeButton @, 'hide' - getHiddenPosts: -> - ReplyHiding.hiddenPosts = $.get "hiddenPosts.#{g.BOARD}", threads: {} + getHiddenPosts: (cb) -> + $.get "hiddenPosts.#{g.BOARD}", threads: {}, (item) -> + ReplyHiding.hiddenPosts = item["hiddenPosts.#{g.BOARD}"] + cb ReplyHiding.hiddenPosts if cb menu: init: -> @@ -1230,7 +1269,7 @@ ReplyHiding = open: (post) -> if !post.isReply or post.isClone return false - thread = ReplyHiding.getHiddenPosts().threads[post.thread] + thread = ReplyHiding.hiddenPosts.threads[post.thread] unless post.isHidden or data = thread?[post] return false ReplyHiding.menu.post = post @@ -1258,7 +1297,7 @@ ReplyHiding = thisPost = $('input[name=thisPost]', parent).checked replies = $('input[name=replies]', parent).checked {post} = ReplyHiding.menu - thread = ReplyHiding.getHiddenPosts().threads[post.thread] + thread = ReplyHiding.hiddenPosts.threads[post.thread] data = thread?[post] if thisPost ReplyHiding.show post, replies @@ -1281,20 +1320,20 @@ ReplyHiding = saveHiddenState: (post, isHiding, thisPost, makeStub, hideRecursively) -> # Get fresh hidden posts. - hiddenPosts = ReplyHiding.getHiddenPosts() - if isHiding - unless thread = hiddenPosts.threads[post.thread] - thread = hiddenPosts.threads[post.thread] = {} - thread[post] = - thisPost: thisPost isnt false # undefined -> true - makeStub: makeStub - hideRecursively: hideRecursively - else - thread = hiddenPosts.threads[post.thread] - delete thread[post] - unless Object.keys(thread).length - delete hiddenPosts.threads[post.thread] - $.set "hiddenPosts.#{g.BOARD}", hiddenPosts + ReplyHiding.getHiddenPosts (hiddenPosts) -> + if isHiding + unless thread = hiddenPosts.threads[post.thread] + thread = hiddenPosts.threads[post.thread] = {} + thread[post] = + thisPost: thisPost isnt false # undefined -> true + makeStub: makeStub + hideRecursively: hideRecursively + else + thread = hiddenPosts.threads[post.thread] + delete thread[post] + unless Object.keys(thread).length + delete hiddenPosts.threads[post.thread] + $.set "hiddenPosts.#{g.BOARD}", hiddenPosts toggle: -> post = Get.postFromNode @ @@ -2461,8 +2500,13 @@ Get = Get.insert post, root, context Misc = # super semantic - clearThreads: (key) -> - return unless data = $.get key + clearThreads: (key, data) -> + unless data + $.get key, null, (item) -> + data = item[key] + return unless data + Misc.clearThreads key, data + return unless Object.keys(data.threads).length $.delete key @@ -3571,20 +3615,21 @@ Unread = node: -> Unread.thread = @ - Unread.lastReadPost = $.get("lastReadPosts.#{@board}", threads: {}).threads[@] or 0 Unread.posts = [] Unread.postsQuotingYou = [] Unread.title = d.title posts = [] for ID, post of @posts posts.push post if post.isReply - Unread.addPosts posts - if Unread.posts.length - # Scroll to before the first unread post. - $.x('preceding-sibling::div[contains(@class,"postContainer")][1]', Unread.posts[0].nodes.root).scrollIntoView false - else if posts.length - # Scroll to the last read post. - posts[posts.length - 1].nodes.root.scrollIntoView() + $.get "lastReadPosts.#{@board}", threads: {}, (item) => + Unread.lastReadPost = item["lastReadPosts.#{@board}"].threads[@] or 0 + Unread.addPosts posts + if Unread.posts.length + # Scroll to before the first unread post. + $.x('preceding-sibling::div[contains(@class,"postContainer")][1]', Unread.posts[0].nodes.root).scrollIntoView false + else if posts.length + # Scroll to the last read post. + posts[posts.length - 1].nodes.root.scrollIntoView() $.on d, 'ThreadUpdate', Unread.onUpdate $.on d, 'scroll visibilitychange', Unread.read $.on d, 'visibilitychange', Unread.setLine if Conf['Unread Line'] @@ -3636,11 +3681,11 @@ Unread = Unread.postsQuotingYou = Unread.postsQuotingYou[i..] Unread.update() if e - saveLastReadPost: $.debounce($.SECOND, -> - lastReadPosts = $.get "lastReadPosts.#{Unread.thread.board}", threads: {} - lastReadPosts.threads[Unread.thread] = Unread.lastReadPost - $.set "lastReadPosts.#{Unread.thread.board}", lastReadPosts - ) + saveLastReadPost: $.debounce $.SECOND, -> + $.get "lastReadPosts.#{Unread.thread.board}", threads: {}, (item) -> + lastReadPosts = item["lastReadPosts.#{Unread.thread.board}"] + lastReadPosts.threads[Unread.thread] = Unread.lastReadPost + $.set "lastReadPosts.#{Unread.thread.board}", lastReadPosts setLine: (force) -> return unless d.hidden or force is true @@ -4068,7 +4113,9 @@ ThreadWatcher = className: 'favicon' $.on favicon, 'click', ThreadWatcher.cb.toggle $.before $('input', @OP.nodes.post), favicon - if g.VIEW is 'thread' and @ID is $.get 'AutoWatch', 0 + return if g.VIEW isnt 'thread' + $.get 'AutoWatch', 0, (item) => + return if item['AutoWatch'] isnt @ID ThreadWatcher.watch @ $.delete 'AutoWatch' @@ -4078,7 +4125,10 @@ ThreadWatcher = $.add d.body, ThreadWatcher.dialog refresh: (watched) -> - watched or= $.get 'WatchedThreads', {} + unless watched + $.get 'WatchedThreads', {}, (item) -> + ThreadWatcher.refresh item['WatchedThreads'] + return nodes = [$('.move', ThreadWatcher.dialog)] for board of watched for id, props of watched[board] @@ -4126,17 +4176,19 @@ ThreadWatcher = ThreadWatcher.unwatch thread.board, thread.ID unwatch: (board, threadID) -> - watched = $.get 'WatchedThreads', {} - delete watched[board][threadID] - delete watched[board] unless Object.keys(watched[board]).length - ThreadWatcher.refresh watched - $.set 'WatchedThreads', watched + $.get 'WatchedThreads', {}, (item) -> + watched = item['WatchedThreads'] + delete watched[board][threadID] + delete watched[board] unless Object.keys(watched[board]).length + ThreadWatcher.refresh watched + $.set 'WatchedThreads', watched watch: (thread) -> - watched = $.get 'WatchedThreads', {} - watched[thread.board] or= {} - watched[thread.board][thread] = - href: "/#{thread.board}/res/#{thread}" - textContent: Get.threadExcerpt thread - ThreadWatcher.refresh watched - $.set 'WatchedThreads', watched + $.get 'WatchedThreads', {}, (item) -> + watched = item['WatchedThreads'] + watched[thread.board] or= {} + watched[thread.board][thread] = + href: "/#{thread.board}/res/#{thread}" + textContent: Get.threadExcerpt thread + ThreadWatcher.refresh watched + $.set 'WatchedThreads', watched diff --git a/src/main.coffee b/src/main.coffee index 25639e6a6..66ff56550 100644 --- a/src/main.coffee +++ b/src/main.coffee @@ -281,21 +281,24 @@ class Clone extends Post Main = - init: -> - # flatten Config into Conf - # and get saved or default values - flatten = (parent, obj) -> - if obj instanceof Array - Conf[parent] = obj[0] - else if typeof obj is 'object' - for key, val of obj - flatten key, val - else # string or number - Conf[parent] = obj + init: (items) -> + unless items + # flatten Config into Conf + # and get saved or default values + flatten = (parent, obj) -> + if obj instanceof Array + Conf[parent] = obj[0] + else if typeof obj is 'object' + for key, val of obj + flatten key, val + else # string or number + Conf[parent] = obj + return + flatten null, Config + $.get Conf, Main.init return - flatten null, Config - for key, val of Conf - Conf[key] = $.get key, val + + Conf = items pathname = location.pathname.split '/' g.BOARD = new Board pathname[1] @@ -499,20 +502,24 @@ Main = # 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<% } %> - if $.get('lastupdate', 0) > now - freq or $.get('lastchecked', 0) > now - $.DAY - return - $.ajax '<%= meta.page %><%= meta.buildsPath %>version', onload: -> - return unless @status is 200 - version = @response - return unless /^\d\.\d+\.\d+$/.test version - if g.VERSION is version - # Don't check for updates too frequently if there wasn't one in a 'long' time. - $.set 'lastupdate', now + items = + lastupdate: 0 + lastchecked: 0 + $.get items, (items) -> + if items.lastupdate > now - freq or items.lastchecked > now - $.DAY return - $.set 'lastchecked', now - el = $.el 'span', - innerHTML: "Update: <%= meta.name %> v#{version} is out, get it target=_blank>here." - new Notification 'info', el, 2 * $.MINUTE + $.ajax '<%= meta.page %><%= meta.buildsPath %>version', onload: -> + return unless @status is 200 + version = @response + return unless /^\d\.\d+\.\d+$/.test version + if g.VERSION is version + # Don't check for updates too frequently if there wasn't one in a 'long' time. + $.set 'lastupdate', now + return + $.set 'lastchecked', now + el = $.el 'span', + innerHTML: "Update: <%= meta.name %> v#{version} is out, get it target=_blank>here." + new Notification 'info', el, 2 * $.MINUTE handleErrors: (errors) -> unless 'length' of errors diff --git a/src/qr.coffee b/src/qr.coffee index b52dffe9a..9341f3314 100644 --- a/src/qr.coffee +++ b/src/qr.coffee @@ -92,7 +92,8 @@ QR = if yourPosts QR.yourPosts = yourPosts return - QR.yourPosts = $.get "yourPosts.#{g.BOARD}", threads: {} + $.get "yourPosts.#{g.BOARD}", threads: {}, (item) -> + QR.syncYourPosts item["yourPosts.#{g.BOARD}"] $.sync "yourPosts.#{g.BOARD}", QR.syncYourPosts error: (err) -> @@ -145,10 +146,11 @@ QR = sage: if board is 'q' then 600 else 60 file: if board is 'q' then 300 else 30 post: if board is 'q' then 60 else 30 - QR.cooldown.cooldowns = $.get "cooldown.#{board}", {} QR.cooldown.upSpd = 0 QR.cooldown.upSpdAccuracy = .5 - QR.cooldown.start() + $.get "cooldown.#{board}", {}, (item) -> + QR.cooldown.cooldowns = item["cooldown.#{board}"] + QR.cooldown.start() $.sync "cooldown.#{board}", QR.cooldown.sync start: -> return if QR.cooldown.isCounting @@ -358,15 +360,6 @@ QR = posts: [] post: class constructor: -> - # set values, or null, to avoid 'undefined' values in inputs - prev = QR.posts[QR.posts.length - 1] - persona = $.get 'QR.persona', {} - @name = if prev then prev.name else persona.name or null - @email = if prev and !/^sage$/.test prev.email then prev.email else persona.email or null - @sub = if prev and Conf['Remember Subject'] then prev.sub else if Conf['Remember Subject'] then persona.sub else null - @spoiler = if prev and Conf['Remember Spoiler'] then prev.spoiler else false - @com = null - el = $.el 'a', className: 'qr-preview' draggable: true @@ -393,8 +386,29 @@ QR = for event in ['dragStart', 'dragEnter', 'dragLeave', 'dragOver', 'dragEnd', 'drop'] $.on el, event.toLowerCase(), @[event] - @unlock() QR.posts.push @ + # set values, or null, to avoid 'undefined' values in inputs + @com = null + @spoiler = if prev and Conf['Remember Spoiler'] + prev.spoiler + else + false + prev = QR.posts[QR.posts.length - 1] + $.get 'QR.persona', {}, (item) => + persona = item['QR.persona'] + @name = if prev + prev.name + else + persona.name or null + @email = if prev and !/^sage$/.test prev.email + prev.email + else + persona.email or null + @sub = if Conf['Remember Subject'] + if prev then prev.sub else persona.sub + else + null + @unlock() rm: -> $.rm @nodes.el index = QR.posts.indexOf @ @@ -606,8 +620,9 @@ QR = $.on imgContainer, 'click', @reload.bind @ $.on input, 'keydown', @keydown.bind @ + $.get 'captchas', [], (item) => + @sync item['captchas'] $.sync 'captchas', @sync - @sync $.get 'captchas', [] # start with an uncached captcha @reload() @@ -960,12 +975,13 @@ QR = QR.cleanNotifications() QR.notifications.push new Notification 'success', h1.textContent, 5 - persona = $.get 'QR.persona', {} - persona = - name: post.name - email: if /^sage$/.test post.email then persona.email else post.email - sub: if Conf['Remember Subject'] then post.sub else null - $.set 'QR.persona', persona + $.get 'QR.persona', {}, (item) -> + persona = item['QR.persona'] + persona = + name: post.name + email: if /^sage$/.test post.email then persona.email else post.email + sub: if Conf['Remember Subject'] then post.sub else null + $.set 'QR.persona', persona [_, threadID, postID] = h1.nextSibling.textContent.match /thread:(\d+),no:(\d+)/ postID = +postID From 9cf363ad8139d02408d53ca23999ec78edff35ed Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Fri, 29 Mar 2013 06:17:14 +0100 Subject: [PATCH 002/104] Fix persona loading. --- src/qr.coffee | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/qr.coffee b/src/qr.coffee index 9341f3314..e55d1a7ee 100644 --- a/src/qr.coffee +++ b/src/qr.coffee @@ -387,8 +387,6 @@ QR = $.on el, event.toLowerCase(), @[event] QR.posts.push @ - # set values, or null, to avoid 'undefined' values in inputs - @com = null @spoiler = if prev and Conf['Remember Spoiler'] prev.spoiler else @@ -399,15 +397,14 @@ QR = @name = if prev prev.name else - persona.name or null + persona.name @email = if prev and !/^sage$/.test prev.email prev.email else - persona.email or null - @sub = if Conf['Remember Subject'] - if prev then prev.sub else persona.sub - else - null + persona.email + if Conf['Remember Subject'] + @sub = if prev then prev.sub else persona.sub + @select() if QR.selected is @ # load persona @unlock() rm: -> $.rm @nodes.el @@ -444,7 +441,7 @@ QR = @nodes.el.parentNode.scrollLeft += rectEl.left + rectEl.width/2 - rectList.left - rectList.width/2 # Load this post's values. for name in ['name', 'email', 'sub', 'com'] - QR.nodes[name].value = @[name] + QR.nodes[name].value = @[name] or null @showFileData() QR.characterCount() save: (input) -> From 1b5695ac82d8052ff878f99f138394fd3e803aad Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Fri, 29 Mar 2013 06:28:26 +0100 Subject: [PATCH 003/104] Simpler $.set for the crx. --- lib/$.coffee | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/$.coffee b/lib/$.coffee index 68d6be255..70af75f99 100644 --- a/lib/$.coffee +++ b/lib/$.coffee @@ -216,9 +216,7 @@ $.extend $, items = $.item key, val chrome.storage.sync.get items, cb set: (key, val) -> - item = {} - item[key] = val - chrome.storage.sync.set item + chrome.storage.sync.set $.item key, val <% } else if (type === 'userjs') { %> do -> # http://www.opera.com/docs/userjs/specs/#scriptstorage From 64b5da652f630af7500eeedb0071e943f7e73c52 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Fri, 29 Mar 2013 13:35:05 +0100 Subject: [PATCH 004/104] Fix thread hiding init. --- src/features.coffee | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/features.coffee b/src/features.coffee index a6bcb8045..73130c8c9 100644 --- a/src/features.coffee +++ b/src/features.coffee @@ -1049,7 +1049,6 @@ ThreadHiding = return if g.VIEW isnt 'index' or !Conf['Thread Hiding'] and !Conf['Thread Hiding Link'] Misc.clearThreads "hiddenThreads.#{g.BOARD}" - @getHiddenThreads() @syncFromCatalog() Thread::callbacks.push name: 'Thread Hiding' @@ -1068,23 +1067,24 @@ ThreadHiding = syncFromCatalog: -> # Sync hidden threads from the catalog into the index. - hiddenThreadsOnCatalog = JSON.parse(localStorage.getItem "4chan-hide-t-#{g.BOARD}") or {} - {threads} = ThreadHiding.hiddenThreads + ThreadHiding.getHiddenThreads (hiddenThreads) -> + {threads} = hiddenThreads + hiddenThreadsOnCatalog = JSON.parse(localStorage.getItem "4chan-hide-t-#{g.BOARD}") or {} - # Add threads that were hidden in the catalog. - for threadID of hiddenThreadsOnCatalog - continue if threadID of threads - threads[threadID] = {} + # Add threads that were hidden in the catalog. + for threadID of hiddenThreadsOnCatalog + continue if threadID of threads + threads[threadID] = {} - # Remove threads that were un-hidden in the catalog. - for threadID of threads - continue if threadID of threads - delete threads[threadID] + # Remove threads that were un-hidden in the catalog. + for threadID of threads + continue if threadID of threads + delete threads[threadID] - if Object.keys(threads).length - $.set "hiddenThreads.#{g.BOARD}", ThreadHiding.hiddenThreads - else - $.delete "hiddenThreads.#{g.BOARD}" + if Object.keys(threads).length + $.set "hiddenThreads.#{g.BOARD}", ThreadHiding.hiddenThreads + else + $.delete "hiddenThreads.#{g.BOARD}" menu: init: -> From 4d886fe85ccc8e58dae0cd4c8e92ea1b0660a858 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Fri, 29 Mar 2013 14:18:44 +0100 Subject: [PATCH 005/104] Update $.sync to use the Chrome Storage API and only one event listener. --- lib/$.coffee | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/lib/$.coffee b/lib/$.coffee index 70af75f99..e8922e6a5 100644 --- a/lib/$.coffee +++ b/lib/$.coffee @@ -27,11 +27,6 @@ $.extend $, $.off d, 'DOMContentLoaded', cb fc() $.on d, 'DOMContentLoaded', cb - sync: (key, cb) -> - key = "#{g.NAMESPACE}#{key}" - $.on window, 'storage', (e) -> - if e.key is key - cb JSON.parse e.newValue formData: (form) -> if form instanceof HTMLFormElement return new FormData form @@ -200,6 +195,21 @@ $.extend $, # Round to an integer otherwise. Math.round size "#{size} #{['B', 'KB', 'MB', 'GB'][unit]}" + sync: do -> + cbs = {} +<% if (type === 'crx') { %> + chrome.storage.onChanged.addListener (changes) -> + for key of changes + if cb = cbs[key] + cb changes[key].newValue + return + (key, cb) -> cbs[key] = cb +<% } else { %> + $.on window, 'storage', (e) -> + if cb = cbs[e.key] + cb JSON.parse e.newValue + (key, cb) -> cbs[g.NAMESPACE + key] = cb +<% } %> item: (key, val) -> item = {} item[key] = val From be267adc574538c606eeb7f3bf1c6f52886232ae Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Fri, 29 Mar 2013 14:23:44 +0100 Subject: [PATCH 006/104] Only save to localStorage if it's a key we sync between tabs. Stay eco-friendly! --- lib/$.coffee | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/lib/$.coffee b/lib/$.coffee index e8922e6a5..bbb204da1 100644 --- a/lib/$.coffee +++ b/lib/$.coffee @@ -195,20 +195,20 @@ $.extend $, # Round to an integer otherwise. Math.round size "#{size} #{['B', 'KB', 'MB', 'GB'][unit]}" + syncing: {} sync: do -> - cbs = {} <% if (type === 'crx') { %> chrome.storage.onChanged.addListener (changes) -> for key of changes - if cb = cbs[key] + if cb = $.syncing[key] cb changes[key].newValue return - (key, cb) -> cbs[key] = cb + (key, cb) -> $.syncing[key] = cb <% } else { %> $.on window, 'storage', (e) -> - if cb = cbs[e.key] + if cb = $.syncing[e.key] cb JSON.parse e.newValue - (key, cb) -> cbs[g.NAMESPACE + key] = cb + (key, cb) -> $.syncing[g.NAMESPACE + key] = cb <% } %> item: (key, val) -> item = {} @@ -259,8 +259,9 @@ do -> $.set = (key, val) -> key = g.NAMESPACE + key val = JSON.stringify val - # for `storage` events - localStorage.setItem key, val + if key of $.syncing + # for `storage` events + localStorage.setItem key, val scriptStorage[key] = val <% } else { %> # http://wiki.greasespot.net/Main_Page @@ -286,7 +287,8 @@ do -> set: (key, val) -> key = g.NAMESPACE + key val = JSON.stringify val - # for `storage` events - localStorage.setItem key, val + if key of $.syncing + # for `storage` events + localStorage.setItem key, val GM_setValue key, val <% } %> From 33af2244dadb2dc583165acb4ad74597e4e102d0 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Fri, 29 Mar 2013 15:03:22 +0100 Subject: [PATCH 007/104] Fix crash on Fx/Opera --- lib/$.coffee | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/$.coffee b/lib/$.coffee index bbb204da1..470ef7035 100644 --- a/lib/$.coffee +++ b/lib/$.coffee @@ -205,9 +205,10 @@ $.extend $, return (key, cb) -> $.syncing[key] = cb <% } else { %> - $.on window, 'storage', (e) -> + window.addEventListener 'storage', ((e) -> if cb = $.syncing[e.key] cb JSON.parse e.newValue + ), false (key, cb) -> $.syncing[g.NAMESPACE + key] = cb <% } %> item: (key, val) -> From 0b3c9c662bbec171c4ab6fd3d90a94914baa1039 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Fri, 29 Mar 2013 15:11:00 +0100 Subject: [PATCH 008/104] Update changelog. #968 --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1db3b199e..4e8c84280 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ Header: - The board list can be customized. - The Header can be automatically hidden. +Extension-related changes for Chrome and Opera: + - Installing and updating is now painless on Chrome. + - Settings will persist on different subdomains and protocols (HTTP/HTTPS). + - Settings will persist in Incognito on Chrome. + - Clearing your cookies won't erase your settings anymore. + Egocentrism: - `(You)` will be added to quotes linking to your posts. - The Unread tab icon will indicate new unread posts quoting you with an exclamation mark. From 1fe947604264720b6a47d1a9e74da6b6ae321afa Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Fri, 29 Mar 2013 15:15:43 +0100 Subject: [PATCH 009/104] rm () --- lib/$.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/$.coffee b/lib/$.coffee index 470ef7035..8bbf177dc 100644 --- a/lib/$.coffee +++ b/lib/$.coffee @@ -205,10 +205,10 @@ $.extend $, return (key, cb) -> $.syncing[key] = cb <% } else { %> - window.addEventListener 'storage', ((e) -> + window.addEventListener 'storage', (e) -> if cb = $.syncing[e.key] cb JSON.parse e.newValue - ), false + , false (key, cb) -> $.syncing[g.NAMESPACE + key] = cb <% } %> item: (key, val) -> From 4ef7c875a76f1dac31e385c14f4f73f1f1d60627 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Fri, 29 Mar 2013 16:36:30 +0100 Subject: [PATCH 010/104] Notify users of newly installed versions. Link to the changelog. #968 --- src/features.coffee | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/features.coffee b/src/features.coffee index 73130c8c9..741acff62 100644 --- a/src/features.coffee +++ b/src/features.coffee @@ -212,6 +212,10 @@ Settings = $.get 'previousversion', null, (item) -> return if item['previousversion'] is g.VERSION + changelog = "<%= meta.repo %>blob/<%= meta.mainBranch %>/CHANGELOG.md##{g.VERSION.replace(/\./g, '')}" + el = $.el 'span', + innerHTML: "<%= meta.name %> has been updated to version #{g.VERSION}." + new Notification 'info', span, 1 * $.MINUTE $.set 'lastupdate', Date.now() $.set 'previousversion', g.VERSION $.on d, '4chanXInitFinished', Settings.open unless item['previousversion'] From ffc0b4831b8c03ef4a0409d3fac3dcf47addf77d Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Fri, 29 Mar 2013 16:43:07 +0100 Subject: [PATCH 011/104] Honor the `Check for Updates` config. --- src/config.coffee | 2 +- src/main.coffee | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/config.coffee b/src/config.coffee index 6bf9d3ec1..de0c6491f 100644 --- a/src/config.coffee +++ b/src/config.coffee @@ -11,7 +11,7 @@ Config = 'Comment Expansion': [true, 'Add buttons to expand too long comments.'] 'Thread Expansion': [true, 'Add buttons to expand threads.'] 'Index Navigation': [false, 'Add buttons to navigate between threads.'] - 'Check for Updates': [true, 'Check for updated versions of <%= meta.name %>.'] + 'Check for Updates': [true, 'Notify when updated versions of <%= meta.name %> are available.'] 'Filtering': 'Anonymize': [false, 'Make everyone Anonymous.'] 'Filter': [true, 'Self-moderation placebo.'] diff --git a/src/main.coffee b/src/main.coffee index 66ff56550..b1fdcb3a8 100644 --- a/src/main.coffee +++ b/src/main.coffee @@ -495,7 +495,7 @@ Main = Klass::callbacks.push obj.callback checkUpdate: -> - return unless Main.isThisPageLegit() + 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. From f930f5ccdd61eec38d29621a33557d313b8f8672 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Fri, 29 Mar 2013 16:59:55 +0100 Subject: [PATCH 012/104] span -> el --- src/features.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/features.coffee b/src/features.coffee index 741acff62..78ae26539 100644 --- a/src/features.coffee +++ b/src/features.coffee @@ -215,7 +215,7 @@ Settings = changelog = "<%= meta.repo %>blob/<%= meta.mainBranch %>/CHANGELOG.md##{g.VERSION.replace(/\./g, '')}" el = $.el 'span', innerHTML: "<%= meta.name %> has been updated to version #{g.VERSION}." - new Notification 'info', span, 1 * $.MINUTE + new Notification 'info', el, 1 * $.MINUTE $.set 'lastupdate', Date.now() $.set 'previousversion', g.VERSION $.on d, '4chanXInitFinished', Settings.open unless item['previousversion'] From 0ab6af2a01856ed1f62189a9982b28ad7ec25fab Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Fri, 29 Mar 2013 17:09:03 +0100 Subject: [PATCH 013/104] Avoid version conflicts between sync'd devices. Also fix some notification lifetime, teehee~. --- src/features.coffee | 9 ++++++++- src/main.coffee | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/features.coffee b/src/features.coffee index 78ae26539..633abcc06 100644 --- a/src/features.coffee +++ b/src/features.coffee @@ -212,10 +212,17 @@ Settings = $.get 'previousversion', null, (item) -> return if item['previousversion'] is g.VERSION + + # Avoid conflicts between sync'd newer versions + # and out of date extension on this device. + prev = item['previousversion'].match(/\d+/g).map Number + curr = g.VERSION.match(/\d+/g).map Number + return unless prev[0] >= curr[0] and prev[1] >= curr[1] and prev[2] >= curr[2] + changelog = "<%= meta.repo %>blob/<%= meta.mainBranch %>/CHANGELOG.md##{g.VERSION.replace(/\./g, '')}" el = $.el 'span', innerHTML: "<%= meta.name %> has been updated to version #{g.VERSION}." - new Notification 'info', el, 1 * $.MINUTE + new Notification 'info', el, 60 $.set 'lastupdate', Date.now() $.set 'previousversion', g.VERSION $.on d, '4chanXInitFinished', Settings.open unless item['previousversion'] diff --git a/src/main.coffee b/src/main.coffee index b1fdcb3a8..e853d17a5 100644 --- a/src/main.coffee +++ b/src/main.coffee @@ -519,7 +519,7 @@ Main = $.set 'lastchecked', now el = $.el 'span', innerHTML: "Update: <%= meta.name %> v#{version} is out, get it target=_blank>here." - new Notification 'info', el, 2 * $.MINUTE + new Notification 'info', el, 120 handleErrors: (errors) -> unless 'length' of errors From cc8e294686711a10b92ecd4b30c33c0bb539fd4d Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Fri, 29 Mar 2013 17:13:50 +0100 Subject: [PATCH 014/104] I'm going TOO FAST. --- src/features.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/features.coffee b/src/features.coffee index 633abcc06..2fa30eca8 100644 --- a/src/features.coffee +++ b/src/features.coffee @@ -217,7 +217,7 @@ Settings = # and out of date extension on this device. prev = item['previousversion'].match(/\d+/g).map Number curr = g.VERSION.match(/\d+/g).map Number - return unless prev[0] >= curr[0] and prev[1] >= curr[1] and prev[2] >= curr[2] + return unless prev[0] <= curr[0] and prev[1] <= curr[1] and prev[2] <= curr[2] changelog = "<%= meta.repo %>blob/<%= meta.mainBranch %>/CHANGELOG.md##{g.VERSION.replace(/\./g, '')}" el = $.el 'span', From 140aca54605b5c17c4a539caecbc0249e7704974 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Fri, 29 Mar 2013 17:26:33 +0100 Subject: [PATCH 015/104] More changelog. --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e8c84280..9b03098c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,10 +8,11 @@ Header: - The Header can be automatically hidden. Extension-related changes for Chrome and Opera: - - Installing and updating is now painless on Chrome. + - Installing and updating is now pain-free on Chrome. - Settings will persist on different subdomains and protocols (HTTP/HTTPS). - Settings will persist in Incognito on Chrome. - Clearing your cookies won't erase your settings anymore. + - Fixed Chrome's install warning saying that 4chan X would run on all web sites. Egocentrism: - `(You)` will be added to quotes linking to your posts. From e63b0705d3644e7c27c9458a19b3bdfe03551b70 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Fri, 29 Mar 2013 18:16:35 +0100 Subject: [PATCH 016/104] Better image contracting repositioning. It will scroll depending on the position of the post after contracting instead of the position of the image before contracting. --- src/features.coffee | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/src/features.coffee b/src/features.coffee index 2fa30eca8..c1c2410bf 100644 --- a/src/features.coffee +++ b/src/features.coffee @@ -3233,20 +3233,16 @@ ImageExpand = unless post.file.isExpanded or $.hasClass thumb, 'expanding' ImageExpand.expand post return - rect = thumb.parentNode.getBoundingClientRect() - if rect.bottom > 0 # Should be at least partially visible. - # Scroll back to the thumbnail when contracting the image - # to avoid being left miles away from the relevant post. - postRect = post.nodes.root.getBoundingClientRect() - headRect = Header.toggle.getBoundingClientRect() - top = postRect.top - headRect.top - headRect.height - 2 - root = if $.engine is 'webkit' - d.body - else - doc - root.scrollTop += top if rect.top < 0 - root.scrollLeft = 0 if rect.left < 0 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. + headRect = Header.toggle.getBoundingClientRect() + top = rect.top - headRect.top - headRect.height + root = if $.engine is 'webkit' then d.body else doc + root.scrollTop += top if rect.top < 0 + root.scrollLeft = 0 if rect.left < 0 contract: (post) -> $.rmClass post.nodes.root, 'expanded-image' From 4fa4ef530410b7bb5a1925650fec5fafd1b1fcb4 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Fri, 29 Mar 2013 20:46:15 +0100 Subject: [PATCH 017/104] Workaround the Unread bug on Chrome. #968 --- src/features.coffee | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/features.coffee b/src/features.coffee index c1c2410bf..a18b06d8b 100644 --- a/src/features.coffee +++ b/src/features.coffee @@ -3711,6 +3711,13 @@ Unread = "(#{Unread.posts.length}) /#{g.BOARD}/ - 404" else "(#{Unread.posts.length}) #{Unread.title}" + <% if (type === 'crx') { %> + title = d.title + setTimeout -> + d.title = '' + d.title = title + , $.SECOND + <% } %> return unless Conf['Unread Tab Icon'] From ee58a29d11204faf546b02589e12250bd0cbb950 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Fri, 29 Mar 2013 20:58:21 +0100 Subject: [PATCH 018/104] Forgot to add a comment with the workaround. --- src/features.coffee | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/features.coffee b/src/features.coffee index a18b06d8b..4e4c13286 100644 --- a/src/features.coffee +++ b/src/features.coffee @@ -3712,6 +3712,8 @@ Unread = else "(#{Unread.posts.length}) #{Unread.title}" <% if (type === 'crx') { %> + # XXX Chrome bug where it doesn't always update the tab title. + # crbug.com/124381 title = d.title setTimeout -> d.title = '' From 415525ae1f6db375a3d657fc135d46bcb358c8e5 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Sat, 30 Mar 2013 01:57:43 +0100 Subject: [PATCH 019/104] Fix bug at first install. --- src/features.coffee | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/features.coffee b/src/features.coffee index 4e4c13286..529f134ca 100644 --- a/src/features.coffee +++ b/src/features.coffee @@ -211,21 +211,22 @@ Settings = open: -> Conf['Enable 4chan\'s Extension'] $.get 'previousversion', null, (item) -> - return if item['previousversion'] is g.VERSION + if previous = item['previousversion'] + return if previous is g.VERSION + # Avoid conflicts between sync'd newer versions + # and out of date extension on this device. + prev = previous.match(/\d+/g).map Number + curr = g.VERSION.match(/\d+/g).map Number + return unless prev[0] <= curr[0] and prev[1] <= curr[1] and prev[2] <= curr[2] - # Avoid conflicts between sync'd newer versions - # and out of date extension on this device. - prev = item['previousversion'].match(/\d+/g).map Number - curr = g.VERSION.match(/\d+/g).map Number - return unless prev[0] <= curr[0] and prev[1] <= curr[1] and prev[2] <= curr[2] - - changelog = "<%= meta.repo %>blob/<%= meta.mainBranch %>/CHANGELOG.md##{g.VERSION.replace(/\./g, '')}" - el = $.el 'span', - innerHTML: "<%= meta.name %> has been updated to version #{g.VERSION}." - new Notification 'info', el, 60 + changelog = "<%= meta.repo %>blob/<%= meta.mainBranch %>/CHANGELOG.md##{g.VERSION.replace(/\./g, '')}" + el = $.el 'span', + innerHTML: "<%= meta.name %> has been updated to version #{g.VERSION}." + new Notification 'info', el, 60 + else + $.on d, '4chanXInitFinished', Settings.open $.set 'lastupdate', Date.now() $.set 'previousversion', g.VERSION - $.on d, '4chanXInitFinished', Settings.open unless item['previousversion'] Settings.addSection 'Main', Settings.main Settings.addSection 'Filter', Settings.filter From 04977c9cadab54e097656420578f3a931dbcc790 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Sat, 30 Mar 2013 03:08:35 +0100 Subject: [PATCH 020/104] Show delete links in the menu even if we don't know if it's your post or not. #968 @DumpAnon --- CHANGELOG.md | 1 - src/features.coffee | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b03098c4..58d73b94c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,6 @@ Extension-related changes for Chrome and Opera: Egocentrism: - `(You)` will be added to quotes linking to your posts. - The Unread tab icon will indicate new unread posts quoting you with an exclamation mark. - - Delete links in the post menu will only appear for your posts. Quick Reply changes: - Opening text files will insert their content in the comment field. diff --git a/src/features.coffee b/src/features.coffee index 529f134ca..5f32af041 100644 --- a/src/features.coffee +++ b/src/features.coffee @@ -1546,7 +1546,7 @@ DeleteLink = el: div order: 40 open: (post) -> - return false if post.isDead or !((thread = QR.yourPosts.threads[post.thread]) and post.ID in thread) + return false if post.isDead DeleteLink.post = post DeleteLink.cooldown.start post node = div.firstChild From eaedbbd004a869de745a7264f3ce4ada2513aa94 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Sat, 30 Mar 2013 03:59:42 +0100 Subject: [PATCH 021/104] rm log line --- src/qr.coffee | 1 - 1 file changed, 1 deletion(-) diff --git a/src/qr.coffee b/src/qr.coffee index e55d1a7ee..baa99c1cd 100644 --- a/src/qr.coffee +++ b/src/qr.coffee @@ -592,7 +592,6 @@ QR = $.on window, 'captcha:timeout', setLifetime $.globalEval 'window.dispatchEvent(new CustomEvent("captcha:timeout", {detail: RecaptchaState.timeout}))' $.off window, 'captcha:timeout', setLifetime - c.log @lifetime imgContainer = $.el 'div', className: 'captcha-img' From 46a23974b93d13c64e3afb1acdf123cc32afced7 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Sat, 30 Mar 2013 04:27:45 +0100 Subject: [PATCH 022/104] Move Google's RIS first in the default Sauces. --- src/config.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.coffee b/src/config.coffee index de0c6491f..ba0d378ab 100644 --- a/src/config.coffee +++ b/src/config.coffee @@ -115,8 +115,8 @@ Config = '' ].join '\n' sauces: [ - 'http://iqdb.org/?url=%TURL' 'https://www.google.com/searchbyimage?image_url=%TURL' + 'http://iqdb.org/?url=%TURL' '#//tineye.com/search?url=%TURL' '#http://saucenao.com/search.php?url=%TURL' '#http://3d.iqdb.org/?url=%TURL' From e4f428efebf557eed14ea8f4f4fba4d5591f4870 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Sat, 30 Mar 2013 05:08:12 +0100 Subject: [PATCH 023/104] Better image expanding repositioning. Images that get unsquashed have their .top changed, so it should adjust to that too. --- src/features.coffee | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/features.coffee b/src/features.coffee index 5f32af041..934b5f363 100644 --- a/src/features.coffee +++ b/src/features.coffee @@ -3271,16 +3271,14 @@ ImageExpand = completeExpand: (post) -> {thumb} = post.file return unless $.hasClass thumb, 'expanding' # contracted before the image loaded - rect = post.nodes.root.getBoundingClientRect() + prev = post.nodes.root.getBoundingClientRect() + post.file.isExpanded = true $.addClass post.nodes.root, 'expanded-image' $.rmClass post.file.thumb, 'expanding' - if rect.top + rect.height <= 0 - root = if $.engine is 'webkit' - d.body - else - doc - root.scrollTop += post.nodes.root.clientHeight - rect.height - post.file.isExpanded = true + return unless prev.top + prev.height <= 0 + root = if $.engine is 'webkit' then d.body else doc + curr = post.nodes.root.getBoundingClientRect() + root.scrollTop += curr.height - prev.height + curr.top - prev.top error: -> post = Get.postFromNode @ From e88e3e4707170420f7f3cca56bd89ca9fbcbd595 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Sat, 30 Mar 2013 05:10:41 +0100 Subject: [PATCH 024/104] Don't enlarge the name/mail/subject fields too much. --- css/style.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/css/style.css b/css/style.css index b591d90e0..50285bed7 100644 --- a/css/style.css +++ b/css/style.css @@ -551,8 +551,8 @@ a[href="javascript:;"] { } .persona .field:hover, .persona .field:focus { - -webkit-flex: 4; - flex: 4; + -webkit-flex: 3; + flex: 3; } #dump-button { background: -webkit-linear-gradient(#EEE, #CCC); From d4a0af9e21ffdaff9a0c502901e416a825629ba6 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Sat, 30 Mar 2013 19:06:40 +0100 Subject: [PATCH 025/104] Fix delete cooldown "jumps", only start the cooldown on our posts. #968 --- src/features.coffee | 41 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/src/features.coffee b/src/features.coffee index 934b5f363..f25408ea8 100644 --- a/src/features.coffee +++ b/src/features.coffee @@ -1548,20 +1548,15 @@ DeleteLink = open: (post) -> return false if post.isDead DeleteLink.post = post - DeleteLink.cooldown.start post node = div.firstChild - if seconds = DeleteLink.cooldown[post.fullID] - node.textContent = "Delete (#{seconds})" - DeleteLink.cooldown.el = node - else - node.textContent = 'Delete' - delete DeleteLink.cooldown.el + node.textContent = 'Delete' + DeleteLink.cooldown.start post, node true subEntries: [postEntry, fileEntry] delete: -> {post} = DeleteLink - return if DeleteLink.cooldown[post.fullID] + return if DeleteLink.cooldown.counting is post $.off @, 'click', DeleteLink.delete @textContent = "Deleting #{@textContent}..." @@ -1601,25 +1596,29 @@ DeleteLink = $.on link, 'click', DeleteLink.delete cooldown: - start: (post) -> - return if post.fullID of DeleteLink.cooldown + start: (post, node) -> + unless (thread = QR.yourPosts?.threads?[post.thread]) and post.ID in thread + # Only start counting on our posts. + delete DeleteLink.cooldown.counting + return + DeleteLink.cooldown.counting = post length = if post.board.ID is 'q' 600 else 30 seconds = Math.ceil (length * $.SECOND - (Date.now() - post.info.date)) / $.SECOND - DeleteLink.cooldown.count post.fullID, seconds, length - count: (fullID, seconds, length) -> - return unless 0 <= seconds <= length - setTimeout DeleteLink.cooldown.count, 1000, fullID, seconds-1, length - {el} = DeleteLink.cooldown - if seconds is 0 - el?.textContent = 'Delete' - delete DeleteLink.cooldown[fullID] - delete DeleteLink.cooldown.el + DeleteLink.cooldown.count post, seconds, length, node + count: (post, seconds, length, node) -> + return if DeleteLink.cooldown.counting isnt post + unless 0 <= seconds <= length + if DeleteLink.cooldown.counting is post + delete DeleteLink.cooldown.counting return - el?.textContent = "Delete (#{seconds})" - DeleteLink.cooldown[fullID] = seconds + setTimeout DeleteLink.cooldown.count, 1000, post, seconds - 1, length, node + if seconds is 0 + node.textContent = 'Delete' + return + node.textContent = "Delete (#{seconds})" DownloadLink = init: -> From 53ab33240615c4f942ec4b349e36df5527e28cea Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Sat, 30 Mar 2013 21:37:37 +0100 Subject: [PATCH 026/104] Improve performances when expanding all images and they were already loaded. --- src/features.coffee | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/features.coffee b/src/features.coffee index f25408ea8..33075054a 100644 --- a/src/features.coffee +++ b/src/features.coffee @@ -3272,12 +3272,13 @@ ImageExpand = return unless $.hasClass thumb, 'expanding' # contracted before the image loaded prev = post.nodes.root.getBoundingClientRect() post.file.isExpanded = true - $.addClass post.nodes.root, 'expanded-image' - $.rmClass post.file.thumb, 'expanding' - return unless prev.top + prev.height <= 0 - root = if $.engine is 'webkit' then d.body else doc - curr = post.nodes.root.getBoundingClientRect() - root.scrollTop += curr.height - prev.height + curr.top - prev.top + $.queueTask -> + $.addClass post.nodes.root, 'expanded-image' + $.rmClass post.file.thumb, 'expanding' + return unless prev.top + prev.height <= 0 + root = if $.engine is 'webkit' then d.body else doc + curr = post.nodes.root.getBoundingClientRect() + root.scrollTop += curr.height - prev.height + curr.top - prev.top error: -> post = Get.postFromNode @ From c0ba3246ce780117ef75131e973a5df2172cc801 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Sun, 31 Mar 2013 00:10:05 +0100 Subject: [PATCH 027/104] Sync unread status between tabs/devices. --- src/features.coffee | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/src/features.coffee b/src/features.coffee index 33075054a..cdbb9a19d 100644 --- a/src/features.coffee +++ b/src/features.coffee @@ -3615,15 +3615,18 @@ Unread = Unread.hr = $.el 'hr', id: 'unread-line' Misc.clearThreads "lastReadPosts.#{g.BOARD}" + $.sync "lastReadPosts.#{g.BOARD}", @sync + + Unread.posts = [] + Unread.postsQuotingYou = [] + Thread::callbacks.push name: 'Unread' cb: @node node: -> - Unread.thread = @ - Unread.posts = [] - Unread.postsQuotingYou = [] - Unread.title = d.title + Unread.thread = @ + Unread.title = d.title posts = [] for ID, post of @posts posts.push post if post.isReply @@ -3640,6 +3643,14 @@ Unread = $.on d, 'scroll visibilitychange', Unread.read $.on d, 'visibilitychange', Unread.setLine if Conf['Unread Line'] + sync: (lastReadPosts) -> + return unless (lastReadPost = lastReadPosts?.threads?[Unread.thread]) and Unread.lastReadPost < lastReadPost + Unread.lastReadPost = lastReadPost + Unread.readArray Unread.posts + Unread.readArray Unread.postsQuotingYou + Unread.setLine() + Unread.update() + addPosts: (newPosts) -> if Conf['Quick Reply'] {yourPosts} = QR @@ -3671,6 +3682,11 @@ Unread = else Unread.addPosts e.detail.newPosts + readArray: (arr) -> + for post, i in arr + break if post.ID > Unread.lastReadPost + arr.splice 0, i + read: (e) -> return if d.hidden or !Unread.posts.length height = doc.clientHeight @@ -3681,13 +3697,11 @@ Unread = Unread.lastReadPost = Unread.posts[i - 1].ID Unread.saveLastReadPost() - Unread.posts = Unread.posts[i..] - for post, i in Unread.postsQuotingYou - break if post.ID > Unread.lastReadPost - Unread.postsQuotingYou = Unread.postsQuotingYou[i..] + Unread.posts.splice 0, i + Unread.readArray Unread.postsQuotingYou Unread.update() if e - saveLastReadPost: $.debounce $.SECOND, -> + saveLastReadPost: $.debounce 2 * $.SECOND, -> $.get "lastReadPosts.#{Unread.thread.board}", threads: {}, (item) -> lastReadPosts = item["lastReadPosts.#{Unread.thread.board}"] lastReadPosts.threads[Unread.thread] = Unread.lastReadPost From a46ba080fd0503308a05f929ade1f9a1dad9a9f8 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Sun, 31 Mar 2013 00:17:24 +0100 Subject: [PATCH 028/104] Fix the Unread count jumping up and down because of the workaround on Chrome. That won't break the workaround, r-right? --- src/features.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/features.coffee b/src/features.coffee index cdbb9a19d..e7de9d402 100644 --- a/src/features.coffee +++ b/src/features.coffee @@ -3730,7 +3730,7 @@ Unread = title = d.title setTimeout -> d.title = '' - d.title = title + Unread.update() , $.SECOND <% } %> From 09642b63dae868f2ead43b3890ed55bb2b59c8c6 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Sun, 31 Mar 2013 00:29:28 +0100 Subject: [PATCH 029/104] When there is a valid hash, scroll to corresponding post instead of the unread posts. #968 --- src/features.coffee | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/features.coffee b/src/features.coffee index e7de9d402..91d4a6147 100644 --- a/src/features.coffee +++ b/src/features.coffee @@ -3633,7 +3633,9 @@ Unread = $.get "lastReadPosts.#{@board}", threads: {}, (item) => Unread.lastReadPost = item["lastReadPosts.#{@board}"].threads[@] or 0 Unread.addPosts posts - if Unread.posts.length + if (hash = location.hash.match /\d+/) and post = @posts[hash[0]] + post.nodes.root.scrollIntoView() + else if Unread.posts.length # Scroll to before the first unread post. $.x('preceding-sibling::div[contains(@class,"postContainer")][1]', Unread.posts[0].nodes.root).scrollIntoView false else if posts.length From 712676885561a7e3b61395994d2bb15d196edd25 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Sun, 31 Mar 2013 00:35:05 +0100 Subject: [PATCH 030/104] Use fullID when possible. --- src/main.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main.coffee b/src/main.coffee index e853d17a5..40f16ac29 100644 --- a/src/main.coffee +++ b/src/main.coffee @@ -16,7 +16,7 @@ class Thread @fullID = "#{@board}.#{@ID}" @posts = {} - g.threads["#{board}.#{@}"] = board.threads[@] = @ + g.threads[@fullID] = board.threads[@] = @ kill: -> @isDead = true @@ -107,7 +107,7 @@ class Post @thread.isClosed = !!$ '.closedIcon', @nodes.info @clones = [] - g.posts["#{board}.#{@}"] = thread.posts[@] = board.posts[@] = @ + g.posts[@fullID] = thread.posts[@] = board.posts[@] = @ @kill() if that.isArchived parseComment: -> From f308eed7149eb01dbd1390e540b7acaaf51d5c79 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Sun, 31 Mar 2013 01:26:02 +0100 Subject: [PATCH 031/104] Don't call Unread.update all the time. --- src/features.coffee | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/features.coffee b/src/features.coffee index 91d4a6147..dce1a2abd 100644 --- a/src/features.coffee +++ b/src/features.coffee @@ -3718,7 +3718,7 @@ Unread = else if Unread.hr.parentNode $.rm Unread.hr - update: -> + update: <% if (type === 'crx') { %>(dontrepeat) <% } %>-> count = Unread.posts.length if Conf['Unread Count'] @@ -3729,11 +3729,13 @@ Unread = <% if (type === 'crx') { %> # XXX Chrome bug where it doesn't always update the tab title. # crbug.com/124381 - title = d.title - setTimeout -> - d.title = '' - Unread.update() - , $.SECOND + # Call it one second later, + # but don't display outdated unread count. + unless dontrepeat + setTimeout -> + d.title = '' + Unread.update true + , $.SECOND <% } %> return unless Conf['Unread Tab Icon'] From 193059f8a4ea162a47cfaa843f7bcc21d52e157b Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Sun, 31 Mar 2013 03:29:13 +0200 Subject: [PATCH 032/104] Update the description. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index dc63399f9..e7800d59c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "4chan-X", "version": "3.0.0", - "description": "Cross-browser userscript for maximum lurking on 4chan.", + "description": "Cross-browser extension for productive lurking on 4chan.", "meta": { "name": "4chan X Beta", "repo": "https://github.com/MayhemYDG/4chan-x/", From 435c411a362212990a819b9962d87f33cdbd5914 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Sun, 31 Mar 2013 06:01:39 +0200 Subject: [PATCH 033/104] Use $ consistently in UI. --- lib/ui.coffee | 142 +++++++++++++++++++++----------------------------- 1 file changed, 60 insertions(+), 82 deletions(-) diff --git a/lib/ui.coffee b/lib/ui.coffee index c4fda1b35..64d99b529 100644 --- a/lib/ui.coffee +++ b/lib/ui.coffee @@ -1,13 +1,12 @@ UI = do -> dialog = (id, position, html) -> - el = d.createElement 'div' - el.className = 'dialog' - el.innerHTML = html - el.id = id + el = $.el 'div', + className: 'dialog' + innerHTML: html + id: id el.style.cssText = localStorage.getItem("#{g.NAMESPACE}#{id}.position") or position - move = el.querySelector '.move' - move.addEventListener 'touchstart', dragstart, false - move.addEventListener 'mousedown', dragstart, false + move = $ '.move', el + $.on move, 'touchstart mousedown', dragstart el @@ -103,8 +102,7 @@ UI = do -> $.rm currentMenu currentMenu = null lastToggledButton = null - $.off d, 'click', @close - $.off d, 'CloseMenu', @close + $.off d, 'click, CloseMenu', @close findNextEntry: (entry, direction) -> entries = [entry.parentNode.children...] @@ -156,18 +154,14 @@ UI = do -> eRect = entry.getBoundingClientRect() cHeight = doc.clientHeight cWidth = doc.clientWidth - if eRect.top + sRect.height < cHeight - top = '0px' - bottom = 'auto' + [top, bottom] = if eRect.top + sRect.height < cHeight + ['0px', 'auto'] else - top = 'auto' - bottom = '0px' - if eRect.right + sRect.width < cWidth - left = '100%' - right = 'auto' + ['auto', '0px'] + [left, right] = if eRect.right + sRect.width < cWidth + ['100%', 'auto'] else - left = 'auto' - right = '100%' + ['auto', '100%'] {style} = submenu style.top = top style.bottom = bottom @@ -201,10 +195,10 @@ UI = do -> return # prevent text selection e.preventDefault() - el = $.x 'ancestor::div[contains(@class,"dialog")][1]', @ if isTouching = e.type is 'touchstart' e = e.changedTouches[e.changedTouches.length - 1] # distance from pointer to el edge is constant; calculate it here. + el = $.x 'ancestor::div[contains(@class,"dialog")][1]', @ rect = el.getBoundingClientRect() screenHeight = doc.clientHeight screenWidth = doc.clientWidth @@ -223,14 +217,13 @@ UI = do -> o.identifier = e.identifier o.move = touchmove.bind o o.up = touchend.bind o - d.addEventListener 'touchmove', o.move, false - d.addEventListener 'touchend', o.up, false - d.addEventListener 'touchcancel', o.up, false + $.on d, 'touchmove', o.move + $.on d, 'touchend touchcancel', o.up else # mousedown o.move = drag.bind o o.up = dragend.bind o - d.addEventListener 'mousemove', o.move, false - d.addEventListener 'mouseup', o.up, false + $.on d, 'mousemove', o.move + $.on d, 'mouseup', o.up touchmove = (e) -> for touch in e.changedTouches if touch.identifier is @identifier @@ -240,33 +233,29 @@ UI = do -> {clientX, clientY} = e left = clientX - @dx - left = - if left < 10 - 0 - else if @width - left < 10 - null - else - left / @screenWidth * 100 + '%' + left = if left < 10 + 0 + else if @width - left < 10 + null + else + left / @screenWidth * 100 + '%' top = clientY - @dy - top = - if top < 10 - 0 - else if @height - top < 10 - null - else - top / @screenHeight * 100 + '%' + top = if top < 10 + 0 + else if @height - top < 10 + null + else + top / @screenHeight * 100 + '%' - right = - if left is null - 0 - else - null - bottom = - if top is null - 0 - else - null + right = if left is null + 0 + else + null + bottom = if top is null + 0 + else + null {style} = @ style.left = left @@ -280,12 +269,11 @@ UI = do -> return dragend = -> if @isTouching - d.removeEventListener 'touchmove', @move, false - d.removeEventListener 'touchend', @up, false - d.removeEventListener 'touchcancel', @up, false + $.off d, 'touchmove', @move + $.off d, 'touchend touchcancel', @up else # mouseup - d.removeEventListener 'mousemove', @move, false - d.removeEventListener 'mouseup', @up, false + $.off d, 'mousemove', @move + $.off d, 'mouseup', @up localStorage.setItem "#{g.NAMESPACE}#{@id}.position", @style.cssText hoverstart = ({root, el, latestEvent, endEvents, asapTest, cb}) -> @@ -294,7 +282,7 @@ UI = do -> el: el style: el.style cb: cb - endEvents: endEvents.split ' ' + endEvents: endEvents latestEvent: latestEvent clientHeight: doc.clientHeight clientWidth: doc.clientWidth @@ -302,47 +290,37 @@ UI = do -> o.hover = hover.bind o o.hoverend = hoverend.bind o - asap = -> - if asapTest() - o.hover o.latestEvent - else - o.timeout = setTimeout asap, 25 - asap() + $.asap asapTest, -> + o.hover o.latestEvent - for event in o.endEvents - root.addEventListener event, o.hoverend, false - root.addEventListener 'mousemove', o.hover, false + $.on root, endEvents, o.hoverend + $.on root, 'mousemove', o.hover hover = (e) -> @latestEvent = e height = @el.offsetHeight {clientX, clientY} = e top = clientY - 120 - top = - if @clientHeight <= height or top <= 0 - 0 - else if top + height >= @clientHeight - @clientHeight - height - else - top - - if clientX <= @clientWidth - 400 - left = clientX + 45 + 'px' - right = null + top = if @clientHeight <= height or top <= 0 + 0 + else if top + height >= @clientHeight + @clientHeight - height else - left = null - right = @clientWidth - clientX + 45 + 'px' + top + + [left, right] = if clientX <= @clientWidth - 400 + [clientX + 45 + 'px', null] + else + [null, @clientWidth - clientX + 45 + 'px'] {style} = @ style.top = top + 'px' style.left = left style.right = right hoverend = -> - @el.parentNode.removeChild @el - for event in @endEvents - @root.removeEventListener event, @hoverend, false - @root.removeEventListener 'mousemove', @hover, false - clearTimeout @timeout + $.rm @el + $.off @root, @endEvents, @hoverend + $.off @root, 'mousemove', @hover @cb.call @ if @cb From 0cc1c97868a90f9b566a170424eb1ea87711f097 Mon Sep 17 00:00:00 2001 From: Zixaphir Date: Sun, 31 Mar 2013 16:16:01 -0700 Subject: [PATCH 034/104] Fix image expansion --- 4chan-X.user.js | 4 ++-- src/features.coffee | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/4chan-X.user.js b/4chan-X.user.js index 25b8168b9..090121018 100644 --- a/4chan-X.user.js +++ b/4chan-X.user.js @@ -20,7 +20,7 @@ // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwAgMAAAAqbBEUAAAACVBMVEUAAGcAAABmzDNZt9VtAAAAAXRSTlMAQObYZgAAAHFJREFUKFOt0LENACEIBdBv4Qju4wgWanEj3D6OcIVMKaitYHEU/jwTCQj8W75kiVCSBvdQ5/AvfVHBin11BgdRq3ysBgfwBDRrj3MCIA+oAQaku/Q1cNctrAmyDl577tOThYt/Y1RBM4DgOHzM0HFTAyLukH/cmRnqAAAAAElFTkSuQmCC // ==/UserScript== -/* 4chan X Beta - Version 3.0.0 - 2013-03-28 +/* 4chan X Beta - Version 3.0.0 - 2013-03-31 * https://4chan-x.just-believe.in/ * * Copyright (c) 2009-2011 James Campos @@ -4951,7 +4951,7 @@ rect = thumb.parentNode.getBoundingClientRect(); if (rect.bottom > 0) { postRect = post.nodes.root.getBoundingClientRect(); - headRect = Header.toggle.getBoundingClientRect(); + headRect = Header.bar.getBoundingClientRect(); top = postRect.top - headRect.top - headRect.height - 2; root = $.engine === 'webkit' ? d.body : doc; if (rect.top < 0) { diff --git a/src/features.coffee b/src/features.coffee index 4828ed660..c0a162bfc 100644 --- a/src/features.coffee +++ b/src/features.coffee @@ -3197,7 +3197,7 @@ ImageExpand = # Scroll back to the thumbnail when contracting the image # to avoid being left miles away from the relevant post. postRect = post.nodes.root.getBoundingClientRect() - headRect = Header.toggle.getBoundingClientRect() + headRect = Header.bar.getBoundingClientRect() top = postRect.top - headRect.top - headRect.height - 2 root = if $.engine is 'webkit' d.body From 203bbf091d3b5a47b248aa6979e8413f06d85a28 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Mon, 1 Apr 2013 01:42:57 +0200 Subject: [PATCH 035/104] Fix rare race conditions with thread/reply hiding, unread, your posts. #968 Added DataBoards for simplifications and to reduce asyncings. Made it possible to export/import those. Made it possible for (You) to work with cross-board quotelinks. Replaced Misc.clearThreads. This is more v3-like. --- Gruntfile.js | 1 + src/databoard.coffee | 80 +++++++++++ src/features.coffee | 313 ++++++++++++++++++++----------------------- src/main.coffee | 33 ++--- src/qr.coffee | 18 +-- 5 files changed, 247 insertions(+), 198 deletions(-) create mode 100644 src/databoard.coffee diff --git a/Gruntfile.js b/Gruntfile.js index 1739cd574..8dabe7cd4 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -17,6 +17,7 @@ module.exports = function(grunt) { 'src/features.coffee', 'src/qr.coffee', 'src/report.coffee', + 'src/databoard.coffee', 'src/main.coffee' ], dest: 'tmp/script.coffee' diff --git a/src/databoard.coffee b/src/databoard.coffee new file mode 100644 index 000000000..09cbb9f31 --- /dev/null +++ b/src/databoard.coffee @@ -0,0 +1,80 @@ +DataBoards = ['hiddenThreads', 'hiddenPosts', 'lastReadPosts', 'yourPosts'] + +class DataBoard + constructor: (@key, sync) -> + @data = Conf[key] + $.sync key, @onSync.bind @ + @clean() + return unless sync + # Chrome also fires the onChanged callback on the current tab, + # so we only start syncing when we're ready. + $.on d, '4chanXInitFinished', => @sync = sync + + delete: ({boardID, threadID, postID}) -> + if postID + delete @data.boards[boardID][threadID][postID] + @deleteIfEmpty {boardID, threadID} + else if threadID + delete @data.boards[boardID][threadID] + @deleteIfEmpty {boardID} + else + delete @data.boards[boardID] + $.set @key, @data + deleteIfEmpty: ({boardID, threadID}) -> + if threadID + unless Object.keys(@data.boards[boardID][threadID]).length + delete @data.boards[boardID][threadID] + @deleteIfEmpty {boardID} + else unless Object.keys(@data.boards[boardID]).length + delete @data.boards[boardID] + set: ({boardID, threadID, postID, val}) -> + if postID + ((@data.boards[boardID] or= {})[threadID] or= {})[postID] = val + else if threadID + (@data.boards[boardID] or= {})[threadID] = val + else + @data.boards[boardID] = val + $.set @key, @data + get: ({boardID, threadID, postID, defaultValue}) -> + if board = @data.boards[boardID] + unless threadID + if postID + for ID, thread in board + if postID of thread + val = thread[postID] + break + else + val = board + else if thread = board[threadID] + val = if postID + thread[postID] + else + thread + val or defaultValue + + clean: -> + for boardID of @data.boards + @deleteIfEmpty {boardID} + + now = Date.now() + if @data.lastChecked < now - 12 * $.HOUR + @data.lastChecked = now + for boardID of @data.boards + @ajaxClean boardID + + $.set @key, @data + ajaxClean: (boardID) -> + $.ajax "//api.4chan.org/#{boardID}/threads.json", onload: (e) => + board = @data.boards[boardID] + threads = {} + for page in JSON.parse e.target.response + for thread in page.threads + if thread.no of board + threads[thread.no] = board[thread.no] + @data.boards[boardID] = threads + @deleteIfEmpty {boardID} + $.set @key, @data + + onSync: (data) -> + @data = data + @sync?() diff --git a/src/features.coffee b/src/features.coffee index dce1a2abd..666d3cef1 100644 --- a/src/features.coffee +++ b/src/features.coffee @@ -347,18 +347,20 @@ Settings = innerHTML: ": Clear manually hidden threads and posts on /#{g.BOARD}/." button = $ 'button', div hiddenNum = 0 - ThreadHiding.getHiddenThreads (hiddenThreads) -> - for ID, thread of hiddenThreads.threads - hiddenNum++ - button.textContent = "Hidden: #{hiddenNum}" - ReplyHiding.getHiddenPosts (hiddenPosts) -> - for ID, thread of hiddenPosts.threads - for ID, post of thread + $.get 'hiddenThreads', boards: {}, (item) -> + for ID, board of item.hiddenThreads.boards + for ID, thread of board hiddenNum++ button.textContent = "Hidden: #{hiddenNum}" + $.get 'hiddenPosts', boards: {}, (item) -> + for ID, board of item.hiddenPosts.boards + for ID, thread of board + for ID, post of thread + hiddenNum++ + button.textContent = "Hidden: #{hiddenNum}" $.on button, 'click', -> @textContent = 'Hidden: 0' - $.delete ["hiddenThreads.#{g.BOARD}", "hiddenPosts.#{g.BOARD}"] + $.delete ['hiddenThreads', 'hiddenPosts'] $.after $('input[name="Stubs"]', section).parentNode.parentNode, div export: (now, data) -> unless typeof now is 'number' @@ -367,9 +369,14 @@ Settings = version: g.VERSION date: now Conf: Conf - $.get 'WatchedThreads', {}, (item) -> - data.WatchedThreads = item.WatchedThreads + items = WatchedThreads: {} + for db in DataBoards + items[db] = boards: {} + $.get items (items) -> + for key, val in items + data[key] = val Settings.export now, data + return a = $.el 'a', className: 'warning' textContent: 'Save me!' @@ -472,6 +479,8 @@ Settings = "Shift+#{s[0...-1]}#{s[-1..].toLowerCase()}" for key, val of data.Conf $.set key, val + for db in DataBoards + $.set db, data[db] $.set 'WatchedThreads', data.WatchedThreads convertSettings: (data, map) -> for prevKey, newKey of map @@ -1060,43 +1069,38 @@ ThreadHiding = init: -> return if g.VIEW isnt 'index' or !Conf['Thread Hiding'] and !Conf['Thread Hiding Link'] - Misc.clearThreads "hiddenThreads.#{g.BOARD}" + @db = new DataBoard 'hiddenThreads' @syncFromCatalog() Thread::callbacks.push name: 'Thread Hiding' cb: @node node: -> - if data = ThreadHiding.hiddenThreads.threads[@] + if data = ThreadHiding.db.get {boardID: @board.ID, threadID: @ID} ThreadHiding.hide @, data.makeStub return unless Conf['Thread Hiding'] $.prepend @OP.nodes.root, ThreadHiding.makeButton @, 'hide' - getHiddenThreads: (cb) -> - $.get "hiddenThreads.#{g.BOARD}", threads: {}, (item) -> - ThreadHiding.hiddenThreads = item["hiddenThreads.#{g.BOARD}"] - cb ThreadHiding.hiddenThreads if cb - syncFromCatalog: -> # Sync hidden threads from the catalog into the index. - ThreadHiding.getHiddenThreads (hiddenThreads) -> - {threads} = hiddenThreads - hiddenThreadsOnCatalog = JSON.parse(localStorage.getItem "4chan-hide-t-#{g.BOARD}") or {} + hiddenThreads = ThreadHiding.db.get + boardID: g.BOARD.ID + defaultValue: {} + hiddenThreadsOnCatalog = JSON.parse(localStorage.getItem "4chan-hide-t-#{g.BOARD}") or {} - # Add threads that were hidden in the catalog. - for threadID of hiddenThreadsOnCatalog - continue if threadID of threads - threads[threadID] = {} + # Add threads that were hidden in the catalog. + for threadID of hiddenThreadsOnCatalog + unless threadID of hiddenThreads + hiddenThreads[threadID] = {} - # Remove threads that were un-hidden in the catalog. - for threadID of threads - continue if threadID of threads - delete threads[threadID] + # Remove threads that were un-hidden in the catalog. + for threadID of hiddenThreads + unless threadID of hiddenThreadsOnCatalog + delete hiddenThreads[threadID] - if Object.keys(threads).length - $.set "hiddenThreads.#{g.BOARD}", ThreadHiding.hiddenThreads - else - $.delete "hiddenThreads.#{g.BOARD}" + ThreadHiding.db.set + boardID: g.BOARD.ID + val: hiddenThreads menu: init: -> @@ -1141,17 +1145,19 @@ ThreadHiding = a saveHiddenState: (thread, makeStub) -> - # Get fresh hidden threads. - ThreadHiding.getHiddenThreads (hiddenThreads) -> - hiddenThreadsCatalog = JSON.parse(localStorage.getItem "4chan-hide-t-#{g.BOARD}") or {} - if thread.isHidden - hiddenThreads.threads[thread] = {makeStub} - hiddenThreadsCatalog[thread] = true - else - delete hiddenThreads.threads[thread] - delete hiddenThreadsCatalog[thread] - $.set "hiddenThreads.#{g.BOARD}", hiddenThreads - localStorage.setItem "4chan-hide-t-#{g.BOARD}", JSON.stringify hiddenThreadsCatalog + hiddenThreadsOnCatalog = JSON.parse(localStorage.getItem "4chan-hide-t-#{g.BOARD}") or {} + if thread.isHidden + ThreadHiding.db.set + boardID: thread.board.ID + threadID: thread.ID + val: {makeStub} + hiddenThreadsOnCatalog[thread] = true + else + ThreadHiding.db.delete + boardID: thread.board.ID + threadID: thread.ID + delete hiddenThreadsOnCatalog[thread] + localStorage.setItem "4chan-hide-t-#{g.BOARD}", JSON.stringify hiddenThreadsOnCatalog toggle: (thread) -> unless thread instanceof Thread @@ -1204,29 +1210,22 @@ ReplyHiding = init: -> return if g.VIEW is 'catalog' or !Conf['Reply Hiding'] and !Conf['Reply Hiding Link'] - Misc.clearThreads "hiddenPosts.#{g.BOARD}" - @getHiddenPosts() + @db = new DataBoard 'hiddenPosts' Post::callbacks.push name: 'Reply Hiding' cb: @node node: -> return if !@isReply or @isClone - if thread = ReplyHiding.hiddenPosts.threads[@thread] - if data = thread[@] - if data.thisPost - ReplyHiding.hide @, data.makeStub, data.hideRecursively - else - Recursive.apply ReplyHiding.hide, @, data.makeStub, true - Recursive.add ReplyHiding.hide, @, data.makeStub, true + if data = ReplyHiding.db.get {boardID: @board.ID, threadID: @thread.ID, postID: @ID} + if data.thisPost + ReplyHiding.hide @, data.makeStub, data.hideRecursively + else + Recursive.apply ReplyHiding.hide, @, data.makeStub, true + Recursive.add ReplyHiding.hide, @, data.makeStub, true return unless Conf['Reply Hiding'] $.replace $('.sideArrows', @nodes.root), ReplyHiding.makeButton @, 'hide' - getHiddenPosts: (cb) -> - $.get "hiddenPosts.#{g.BOARD}", threads: {}, (item) -> - ReplyHiding.hiddenPosts = item["hiddenPosts.#{g.BOARD}"] - cb ReplyHiding.hiddenPosts if cb - menu: init: -> return if g.VIEW is 'catalog' or !Conf['Menu'] or !Conf['Reply Hiding Link'] @@ -1279,10 +1278,9 @@ ReplyHiding = el: div order: 20 open: (post) -> - if !post.isReply or post.isClone + if !post.isReply or post.isClone or !post.isHidden return false - thread = ReplyHiding.hiddenPosts.threads[post.thread] - unless post.isHidden or data = thread?[post] + unless data = ReplyHiding.db.get {boardID: post.board.ID, threadID: post.thread.ID, postID: post.ID} return false ReplyHiding.menu.post = post thisPost.firstChild.checked = post.isHidden @@ -1309,8 +1307,6 @@ ReplyHiding = thisPost = $('input[name=thisPost]', parent).checked replies = $('input[name=replies]', parent).checked {post} = ReplyHiding.menu - thread = ReplyHiding.hiddenPosts.threads[post.thread] - data = thread?[post] if thisPost ReplyHiding.show post, replies else if replies @@ -1318,7 +1314,7 @@ ReplyHiding = Recursive.rm ReplyHiding.hide, post, true else return - if data + if data = ReplyHiding.db.get {boardID: post.board.ID, threadID: post.thread.ID, postID: post.ID} ReplyHiding.saveHiddenState post, !(thisPost and replies), !thisPost, data.makeStub, !replies $.event 'CloseMenu' @@ -1331,21 +1327,18 @@ ReplyHiding = a saveHiddenState: (post, isHiding, thisPost, makeStub, hideRecursively) -> - # Get fresh hidden posts. - ReplyHiding.getHiddenPosts (hiddenPosts) -> - if isHiding - unless thread = hiddenPosts.threads[post.thread] - thread = hiddenPosts.threads[post.thread] = {} - thread[post] = - thisPost: thisPost isnt false # undefined -> true - makeStub: makeStub - hideRecursively: hideRecursively - else - thread = hiddenPosts.threads[post.thread] - delete thread[post] - unless Object.keys(thread).length - delete hiddenPosts.threads[post.thread] - $.set "hiddenPosts.#{g.BOARD}", hiddenPosts + data = + boardID: post.board.ID + threadID: post.thread.ID + postID: post.ID + if isHiding + data.val = + thisPost: thisPost isnt false # undefined -> true + makeStub: makeStub + hideRecursively: hideRecursively + ReplyHiding.db.set data + else + ReplyHiding.db.delete data toggle: -> post = Get.postFromNode @ @@ -1448,8 +1441,8 @@ QuoteStrikeThrough = node: -> return if @isClone for quotelink in @nodes.quotelinks - {board, postID} = Get.postDataFromLink quotelink - if g.posts["#{board}.#{postID}"]?.isHidden + {boardID, postID} = Get.postDataFromLink quotelink + if g.posts["#{boardID}.#{postID}"]?.isHidden $.addClass quotelink, 'filtered' return @@ -1597,7 +1590,7 @@ DeleteLink = cooldown: start: (post, node) -> - unless (thread = QR.yourPosts?.threads?[post.thread]) and post.ID in thread + unless QR.db?.get {boardID: post.board.ID, threadID: post.thread.ID, postID: post.ID} # Only start counting on our posts. delete DeleteLink.cooldown.counting return @@ -2304,15 +2297,15 @@ Get = postDataFromLink: (link) -> if link.hostname is 'boards.4chan.org' path = link.pathname.split '/' - board = path[1] + boardID = path[1] threadID = path[3] postID = link.hash[2..] else # resurrected quote - board = link.dataset.board + boardID = link.dataset.board threadID = link.dataset.threadid or 0 postID = link.dataset.postid return { - board: board + boardID: boardID threadID: +threadID postID: +postID } @@ -2340,8 +2333,8 @@ Get = # Third: # Filter out irrelevant quotelinks. quotelinks.filter (quotelink) -> - {board, postID} = Get.postDataFromLink quotelink - board is post.board.ID and postID is post.ID + {boardID, postID} = Get.postDataFromLink quotelink + boardID is post.board.ID and postID is post.ID postClone: (board, threadID, postID, root, context) -> if post = g.posts["#{board}.#{postID}"] Get.insert post, root, context @@ -2510,34 +2503,6 @@ Get = Main.callbackNodes Post, [post] Get.insert post, root, context -Misc = # super semantic - clearThreads: (key, data) -> - unless data - $.get key, null, (item) -> - data = item[key] - return unless data - Misc.clearThreads key, data - return - - unless Object.keys(data.threads).length - $.delete key - return - - return if data.lastChecked > Date.now() - 12 * $.HOUR - - $.ajax "//api.4chan.org/#{g.BOARD}/threads.json", onload: -> - threads = {} - for page in JSON.parse @response - for thread in page.threads - if thread.no of data.threads - threads[thread.no] = data.threads[thread.no] - unless Object.keys(threads).length - $.delete key - return - data.threads = threads - data.lastChecked = Date.now() - $.set key, data - Quotify = init: -> return if g.VIEW is 'catalog' or !Conf['Resurrect Quotes'] @@ -2623,13 +2588,13 @@ QuoteInline = toggle: (e) -> return if e.shiftKey or e.altKey or e.ctrlKey or e.metaKey or e.button isnt 0 e.preventDefault() - {board, threadID, postID} = Get.postDataFromLink @ + {boardID, threadID, postID} = Get.postDataFromLink @ context = Get.contextFromLink @ if $.hasClass @, 'inlined' - QuoteInline.rm @, board, threadID, postID, context + QuoteInline.rm @, boardID, threadID, postID, context else return if $.x "ancestor::div[@id='p#{postID}']", @ - QuoteInline.add @, board, threadID, postID, context + QuoteInline.add @, boardID, threadID, postID, context @classList.toggle 'inlined' findRoot: (quotelink, isBacklink) -> @@ -2637,15 +2602,15 @@ QuoteInline = quotelink.parentNode.parentNode else $.x 'ancestor-or-self::*[parent::blockquote][1]', quotelink - add: (quotelink, board, threadID, postID, context) -> + add: (quotelink, boardID, threadID, postID, context) -> isBacklink = $.hasClass quotelink, 'backlink' inline = $.el 'div', id: "i#{postID}" className: 'inline' $.after QuoteInline.findRoot(quotelink, isBacklink), inline - Get.postClone board, threadID, postID, inline, context + Get.postClone boardID, threadID, postID, inline, context - return unless (post = g.posts["#{board}.#{postID}"]) and + return unless (post = g.posts["#{boardID}.#{postID}"]) and context.thread is post.thread # Hide forward post if it's a backlink of a post in this thread. @@ -2659,7 +2624,7 @@ QuoteInline = Unread.posts.splice i, 1 Unread.update() - rm: (quotelink, board, threadID, postID, context) -> + rm: (quotelink, boardID, threadID, postID, context) -> isBacklink = $.hasClass quotelink, 'backlink' # Select the corresponding inlined quote, and remove it. root = QuoteInline.findRoot quotelink, isBacklink @@ -2670,21 +2635,21 @@ QuoteInline = return unless el = root.firstElementChild # Dereference clone. - post = g.posts["#{board}.#{postID}"] + post = g.posts["#{boardID}.#{postID}"] post.rmClone el.dataset.clone # Decrease forward count and unhide. if Conf['Forward Hiding'] and isBacklink and - context.thread is g.threads["#{board}.#{threadID}"] and + context.thread is g.threads["#{boardID}.#{threadID}"] and not --post.forwarded delete post.forwarded $.rmClass post.nodes.root, 'forwarded' # Repeat. while inlined = $ '.inlined', el - {board, threadID, postID} = Get.postDataFromLink inlined - QuoteInline.rm inlined, board, threadID, postID, context + {boardID, threadID, postID} = Get.postDataFromLink inlined + QuoteInline.rm inlined, boardID, threadID, postID, context $.rmClass inlined, 'inlined' return @@ -2704,13 +2669,13 @@ QuotePreview = mouseover: (e) -> return if $.hasClass @, 'inlined' - {board, threadID, postID} = Get.postDataFromLink @ + {boardID, threadID, postID} = Get.postDataFromLink @ qp = $.el 'div', id: 'qp' className: 'dialog' $.add d.body, qp - Get.postClone board, threadID, postID, qp, Get.contextFromLink @ + Get.postClone boardID, threadID, postID, qp, Get.contextFromLink @ UI.hover root: @ @@ -2720,7 +2685,7 @@ QuotePreview = cb: QuotePreview.mouseout asapTest: -> qp.firstElementChild - return unless origin = g.posts["#{board}.#{postID}"] + return unless origin = g.posts["#{boardID}.#{postID}"] if Conf['Quote Highlighting'] posts = [origin].concat origin.clones @@ -2823,11 +2788,12 @@ QuoteYou = return if @isClone # Stop there if there's no quotes in that post. return unless (quotes = @quotes).length + {db} = QR + return unless db {quotelinks} = @nodes for quotelink in quotelinks - {threadID, postID} = Get.postDataFromLink quotelink - if (thread = QR.yourPosts.threads[threadID]) and postID in thread + if db.get Get.postDataFromLink quotelink $.add quotelink, $.tn QuoteYou.text return @@ -2856,8 +2822,8 @@ QuoteOP = # add (OP) to quotes quoting this context's OP. return unless op in quotes for quotelink in quotelinks - {board, postID} = Get.postDataFromLink quotelink - if "#{board}.#{postID}" is op + {boardID, postID} = Get.postDataFromLink quotelink + if "#{boardID}.#{postID}" is op $.add quotelink, $.tn QuoteOP.text return @@ -2879,11 +2845,11 @@ QuoteCT = {board, thread} = if @isClone then @context else @ for quotelink in quotelinks - data = Get.postDataFromLink quotelink - continue unless data.threadID # deadlink + {boardID, threadID} = Get.postDataFromLink quotelink + continue unless threadID # deadlink if @isClone quotelink.textContent = quotelink.textContent.replace QuoteCT.text, '' - if data.board is @board.ID and data.threadID isnt thread.ID + if boardID is @board.ID and threadID isnt thread.ID $.add quotelink, $.tn QuoteCT.text return @@ -3612,13 +3578,11 @@ Unread = init: -> return if g.VIEW isnt 'thread' or !Conf['Unread Count'] and !Conf['Unread Tab Icon'] - Unread.hr = $.el 'hr', + @db = new DataBoard 'lastReadPosts', @sync + @hr = $.el 'hr', id: 'unread-line' - Misc.clearThreads "lastReadPosts.#{g.BOARD}" - $.sync "lastReadPosts.#{g.BOARD}", @sync - - Unread.posts = [] - Unread.postsQuotingYou = [] + @posts = [] + @postsQuotingYou = [] Thread::callbacks.push name: 'Unread' @@ -3630,23 +3594,29 @@ Unread = posts = [] for ID, post of @posts posts.push post if post.isReply - $.get "lastReadPosts.#{@board}", threads: {}, (item) => - Unread.lastReadPost = item["lastReadPosts.#{@board}"].threads[@] or 0 - Unread.addPosts posts - if (hash = location.hash.match /\d+/) and post = @posts[hash[0]] - post.nodes.root.scrollIntoView() - else if Unread.posts.length - # Scroll to before the first unread post. - $.x('preceding-sibling::div[contains(@class,"postContainer")][1]', Unread.posts[0].nodes.root).scrollIntoView false - else if posts.length - # Scroll to the last read post. - posts[posts.length - 1].nodes.root.scrollIntoView() + Unread.lastReadPost = Unread.db.get + boardID: @board.ID + threadID: @ID + defaultValue: 0 + Unread.addPosts posts + if (hash = location.hash.match /\d+/) and post = @posts[hash[0]] + post.nodes.root.scrollIntoView() + else if Unread.posts.length + # Scroll to before the first unread post. + $.x('preceding-sibling::div[contains(@class,"postContainer")][1]', Unread.posts[0].nodes.root).scrollIntoView false + else if posts.length + # Scroll to the last read post. + posts[posts.length - 1].nodes.root.scrollIntoView() $.on d, 'ThreadUpdate', Unread.onUpdate $.on d, 'scroll visibilitychange', Unread.read $.on d, 'visibilitychange', Unread.setLine if Conf['Unread Line'] - sync: (lastReadPosts) -> - return unless (lastReadPost = lastReadPosts?.threads?[Unread.thread]) and Unread.lastReadPost < lastReadPost + sync: -> + lastReadPost = Unread.db.get + boardID: Unread.thread.board.ID + threadID: Unread.thread.ID + defaultValue: 0 + return unless Unread.lastReadPost < lastReadPost Unread.lastReadPost = lastReadPost Unread.readArray Unread.posts Unread.readArray Unread.postsQuotingYou @@ -3654,29 +3624,30 @@ Unread = Unread.update() addPosts: (newPosts) -> - if Conf['Quick Reply'] - {yourPosts} = QR - youInThisThread = yourPosts.threads[Unread.thread] for post in newPosts {ID} = post - if ID <= Unread.lastReadPost or post.isHidden or youInThisThread and ID in youInThisThread + if ID <= Unread.lastReadPost or post.isHidden continue + if QR.db + data = + boardID: post.board.ID + threadID: post.thread.ID + postID: post.ID + continue if QR.db.get data Unread.posts.push post - Unread.addPostQuotingYou post, yourPosts if yourPosts + 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 newPosts Unread.read() Unread.update() - addPostQuotingYou: (post, yourPosts) -> - for quote in post.quotes - [board, quoteID] = quote.split '.' - continue unless board is Unread.thread.board.ID - for thread, postIDs of yourPosts.threads - if +quoteID in postIDs - Unread.postsQuotingYou.push post - return + addPostQuotingYou: (post) -> + return unless QR.db + for quotelink in post.nodes.quotelinks + if QR.db.get Get.postDataFromLink quotelink + Unread.postsQuotingYou.push post + return onUpdate: (e) -> if e.detail[404] @@ -3704,10 +3675,10 @@ Unread = Unread.update() if e saveLastReadPost: $.debounce 2 * $.SECOND, -> - $.get "lastReadPosts.#{Unread.thread.board}", threads: {}, (item) -> - lastReadPosts = item["lastReadPosts.#{Unread.thread.board}"] - lastReadPosts.threads[Unread.thread] = Unread.lastReadPost - $.set "lastReadPosts.#{Unread.thread.board}", lastReadPosts + Unread.db.set + boardID: Unread.thread.board.ID + threadID: Unread.thread.ID + val: Unread.lastReadPost setLine: (force) -> return unless d.hidden or force is true diff --git a/src/main.coffee b/src/main.coffee index 40f16ac29..303232774 100644 --- a/src/main.coffee +++ b/src/main.coffee @@ -282,22 +282,25 @@ class Clone extends Post Main = init: (items) -> - unless items - # flatten Config into Conf - # and get saved or default values - flatten = (parent, obj) -> - if obj instanceof Array - Conf[parent] = obj[0] - else if typeof obj is 'object' - for key, val of obj - flatten key, val - else # string or number - Conf[parent] = obj - return - flatten null, Config - $.get Conf, Main.init + # flatten Config into Conf + # and get saved or default values + flatten = (parent, obj) -> + if obj instanceof Array + Conf[parent] = obj[0] + else if typeof obj is 'object' + for key, val of obj + flatten key, val + else # string or number + Conf[parent] = obj return + flatten null, Config + for db in DataBoards + Conf[db] = boards: {} + $.get Conf, Main.initFeatures + $.on d, '4chanMainInit', Main.initStyle + + initFeatures: (items) -> Conf = items pathname = location.pathname.split '/' @@ -385,10 +388,10 @@ Main = # c.timeEnd 'All initializations' $.on d, 'AddCallback', Main.addCallback - $.on d, '4chanMainInit', Main.initStyle $.ready Main.initReady initStyle: -> + $.off d, '4chanMainInit', Main.initStyle return unless Main.isThisPageLegit() # disable the mobile layout $('link[href*=mobile]', d.head)?.disabled = true diff --git a/src/qr.coffee b/src/qr.coffee index baa99c1cd..3d95f2495 100644 --- a/src/qr.coffee +++ b/src/qr.coffee @@ -2,8 +2,7 @@ QR = init: -> return if g.VIEW is 'catalog' or !Conf['Quick Reply'] - Misc.clearThreads "yourPosts.#{g.BOARD}" - @syncYourPosts() + @db = new DataBoard 'yourPosts' if Conf['Hide Original Post Form'] $.addClass doc, 'hide-original-post-form' @@ -88,14 +87,6 @@ QR = else QR.unhide() - syncYourPosts: (yourPosts) -> - if yourPosts - QR.yourPosts = yourPosts - return - $.get "yourPosts.#{g.BOARD}", threads: {}, (item) -> - QR.syncYourPosts item["yourPosts.#{g.BOARD}"] - $.sync "yourPosts.#{g.BOARD}", QR.syncYourPosts - error: (err) -> QR.open() if typeof err is 'string' @@ -984,8 +975,11 @@ QR = threadID = +threadID or postID isReply = threadID isnt postID - (QR.yourPosts.threads[threadID] or= []).push postID - $.set "yourPosts.#{g.BOARD}", QR.yourPosts + QR.db.set + boardID: g.BOARD.ID + threadID: threadID + postID: postID + val: true # Post/upload confirmed as successful. $.event 'QRPostSuccessful', { From e4460f01a0437e85522eabfafdc62a0a7335fc25 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Mon, 1 Apr 2013 02:18:34 +0200 Subject: [PATCH 036/104] Don't need to check that since (You) depends on the QR already. --- src/features.coffee | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/features.coffee b/src/features.coffee index 666d3cef1..21d976fb1 100644 --- a/src/features.coffee +++ b/src/features.coffee @@ -2788,12 +2788,10 @@ QuoteYou = return if @isClone # Stop there if there's no quotes in that post. return unless (quotes = @quotes).length - {db} = QR - return unless db {quotelinks} = @nodes for quotelink in quotelinks - if db.get Get.postDataFromLink quotelink + if QR.db.get Get.postDataFromLink quotelink $.add quotelink, $.tn QuoteYou.text return From c68f1a38090d8e2009fb17da0665161e4397580d Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Mon, 1 Apr 2013 05:00:30 +0200 Subject: [PATCH 037/104] More accurate changelog on optional spoiler expanding. --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 58d73b94c..7ccc588df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,7 +31,7 @@ Quick Reply changes: Image Expansion changes: - The toggle and settings are now located in the Header's shortcuts and menu. - - There is now a setting to allow expanding spoilers. + - Expanding spoilers along with all non-spoiler images is now optional, and disabled by default. - Expanding OP images won't squish replies anymore. Thread Updater changes: From e3ca8f280d99927f55865ba43e0ac2accf95e1ef Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Mon, 1 Apr 2013 05:05:04 +0200 Subject: [PATCH 038/104] Only toggle the Header on LMB. --- lib/ui.coffee | 3 +-- src/features.coffee | 3 ++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/ui.coffee b/lib/ui.coffee index 64d99b529..f83d84065 100644 --- a/lib/ui.coffee +++ b/lib/ui.coffee @@ -191,8 +191,7 @@ UI = do -> dragstart = (e) -> - if e.type is 'mousedown' and e.button isnt 0 # not LMB - return + return if e.type is 'mousedown' and e.button isnt 0 # not LMB # prevent text selection e.preventDefault() if isTouching = e.type is 'touchstart' diff --git a/src/features.coffee b/src/features.coffee index 21d976fb1..0fa251320 100644 --- a/src/features.coffee +++ b/src/features.coffee @@ -131,7 +131,8 @@ Header = setBarVisibility: (hide) -> Header.headerToggler.firstElementChild.checked = hide (if hide then $.addClass else $.rmClass) Header.bar, 'autohide' - toggleBarVisibility: -> + toggleBarVisibility: (e) -> + return if e.type is 'mousedown' and e.button isnt 0 # not LMB hide = if @nodeName is 'INPUT' @checked else From 709c1b69ebedff022bd3c8197c3c3e2490a13e20 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Mon, 1 Apr 2013 16:19:48 +0200 Subject: [PATCH 039/104] Add archive redirection for /gd/, /out/, /vp/ and /vr/. Update CS. Fix compiling on my laptop. (maxBuffer error) --- 4chan_x.user.js | 27 ++++++++++++++++++++------- Cakefile | 2 +- changelog | 2 ++ script.coffee | 10 +++++----- 4 files changed, 28 insertions(+), 13 deletions(-) diff --git a/4chan_x.user.js b/4chan_x.user.js index 546b31979..abba95907 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -581,7 +581,7 @@ Filter = { filters: {}, init: function() { - var boards, filter, hl, key, op, regexp, stub, top, _i, _len, _ref, _ref1, _ref2, _ref3, _ref4; + var boards, err, filter, hl, key, op, regexp, stub, top, _i, _len, _ref, _ref1, _ref2, _ref3, _ref4; for (key in Config.filter) { this.filters[key] = []; _ref = Conf[key].split('\n'); @@ -603,7 +603,8 @@ } else { try { regexp = RegExp(regexp[1], regexp[2]); - } catch (err) { + } catch (_error) { + err = _error; alert(err.message); continue; } @@ -2090,7 +2091,6 @@ }, replies: [], reply: (function() { - function _Class() { var persona, prev, _this = this; @@ -3044,14 +3044,15 @@ } reader = new FileReader(); reader.onload = function(e) { - var data; + var data, err; try { data = JSON.parse(decodeURIComponent(escape(e.target.result))); Options.loadSettings(data); if (confirm('Import successful. Refresh now?')) { return window.location.reload(); } - } catch (err) { + } catch (_error) { + err = _error; return output.textContent = 'Import failed due to an error.'; } }; @@ -4917,11 +4918,14 @@ image: function(board, filename) { switch (board) { case 'a': + case 'gd': case 'jp': case 'm': case 'q': case 'tg': case 'vg': + case 'vp': + case 'vr': case 'wsg': return "//archive.foolz.us/" + board + "/full_image/" + filename; case 'u': @@ -4949,6 +4953,7 @@ switch (board) { case 'a': case 'co': + case 'gd': case 'jp': case 'm': case 'q': @@ -4957,6 +4962,8 @@ case 'tv': case 'v': case 'vg': + case 'vp': + case 'vr': case 'wsg': case 'dev': case 'foolz': @@ -4966,6 +4973,7 @@ return "//nsfw.foolz.us/_/api/chan/post/?board=" + board + "&num=" + postID; case 'c': case 'int': + case 'out': case 'po': return "//archive.thedarkcave.org/_/api/chan/post/?board=" + board + "&num=" + postID; } @@ -4979,6 +4987,7 @@ switch (board) { case 'a': case 'co': + case 'gd': case 'jp': case 'm': case 'q': @@ -4987,6 +4996,8 @@ case 'tv': case 'v': case 'vg': + case 'vp': + case 'vr': case 'wsg': case 'dev': case 'foolz': @@ -4997,6 +5008,7 @@ url = Redirect.path('//nsfw.foolz.us', 'foolfuuka', data); break; case 'int': + case 'out': case 'po': url = Redirect.path('//archive.thedarkcave.org', 'foolfuuka', data); break; @@ -5742,7 +5754,7 @@ return post; }, node: function(nodes, notify) { - var callback, node, _i, _j, _len, _len1, _ref; + var callback, err, node, _i, _j, _len, _len1, _ref; _ref = Main.callbacks; for (_i = 0, _len = _ref.length; _i < _len; _i++) { callback = _ref[_i]; @@ -5751,7 +5763,8 @@ node = nodes[_j]; callback(node); } - } catch (err) { + } catch (_error) { + err = _error; if (notify) { alert("4chan X (" + Main.version + ") error: " + err.message + "\nReport the bug at mayhemydg.github.com/4chan-x/#bug-report\n\nURL: " + window.location + "\n" + err.stack); } diff --git a/Cakefile b/Cakefile index 775278944..228e00778 100644 --- a/Cakefile +++ b/Cakefile @@ -112,7 +112,7 @@ task 'upgrade', (options) -> exec "git commit -am 'Release #{version}.' && git tag -a #{version} -m '#{version}' && git tag -af stable -m '#{version}'" task 'build', -> - exec 'coffee --print script.coffee', (err, stdout, stderr) -> + exec 'coffee --print script.coffee', {maxBuffer: 500 * 1024}, (err, stdout, stderr) -> throw err if err fs.writeFile OUTFILE, HEADER + stdout, (err) -> throw err if err diff --git a/changelog b/changelog index e37aa34db..f1ac88e43 100644 --- a/changelog +++ b/changelog @@ -1,4 +1,6 @@ master +- Mayhem + Add /gd/, /out/, /vp/ and /vr/ archive redirection. 2.39.0 - Queue diff --git a/script.coffee b/script.coffee index 22c003812..2c67aa5b3 100644 --- a/script.coffee +++ b/script.coffee @@ -4080,7 +4080,7 @@ Redirect = image: (board, filename) -> # Do not use g.BOARD, the image url can originate from a cross-quote. switch board - when 'a', 'jp', 'm', 'q', 'tg', 'vg', '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}" @@ -4096,22 +4096,22 @@ Redirect = "//archive.nyafuu.org/#{board}/full_image/#{filename}" post: (board, postID) -> switch board - when 'a', 'co', 'jp', 'm', 'q', 'sp', 'tg', 'tv', 'v', 'vg', 'wsg', 'dev', 'foolz' + when 'a', 'co', 'gd', 'jp', 'm', 'q', 'sp', 'tg', 'tv', 'v', 'vg', 'vp', 'vr', 'wsg', 'dev', 'foolz' "//archive.foolz.us/_/api/chan/post/?board=#{board}&num=#{postID}" when 'u', 'kuku' "//nsfw.foolz.us/_/api/chan/post/?board=#{board}&num=#{postID}" - when 'c', 'int', 'po' + when 'c', 'int', 'out', 'po' "//archive.thedarkcave.org/_/api/chan/post/?board=#{board}&num=#{postID}" to: (data) -> unless data.isSearch {threadID} = data {board} = data switch board - when 'a', 'co', 'jp', 'm', 'q', 'sp', 'tg', 'tv', 'v', 'vg', 'wsg', 'dev', 'foolz' + when 'a', 'co', 'gd', 'jp', 'm', 'q', 'sp', 'tg', 'tv', 'v', 'vg', 'vp', 'vr', 'wsg', 'dev', 'foolz' url = Redirect.path '//archive.foolz.us', 'foolfuuka', data when 'u', 'kuku' url = Redirect.path '//nsfw.foolz.us', 'foolfuuka', data - when 'int', 'po' + when 'int', 'out', 'po' url = Redirect.path '//archive.thedarkcave.org', 'foolfuuka', data when 'ck', 'lit' url = Redirect.path '//fuuka.warosu.org', 'fuuka', data From ddee544ec0fd3b23bdac4ba9ac19ccd60013358b Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Mon, 1 Apr 2013 16:22:29 +0200 Subject: [PATCH 040/104] Release 2.39.1. --- 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 abba95907..c0bdc8909 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -1,6 +1,6 @@ // ==UserScript== // @name 4chan x -// @version 2.39.0 +// @version 2.39.1 // @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.com/4chan-x/ - * 4chan X 2.39.0 + * 4chan X 2.39.1 * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -5811,7 +5811,7 @@ return $.globalEval(("(" + code + ")()").replace('_id_', bq.id)); }, namespace: '4chan_x.', - version: '2.39.0', + version: '2.39.1', callbacks: [], css: '\ /* dialog styling */\ diff --git a/Cakefile b/Cakefile index 228e00778..224342336 100644 --- a/Cakefile +++ b/Cakefile @@ -2,7 +2,7 @@ {exec} = require 'child_process' fs = require 'fs' -VERSION = '2.39.0' +VERSION = '2.39.1' HEADER = """ // ==UserScript== diff --git a/changelog b/changelog index f1ac88e43..152109283 100644 --- a/changelog +++ b/changelog @@ -1,4 +1,6 @@ master + +2.39.1 - Mayhem Add /gd/, /out/, /vp/ and /vr/ archive redirection. diff --git a/latest.js b/latest.js index 9fafd3888..213c063d1 100644 --- a/latest.js +++ b/latest.js @@ -1 +1 @@ -postMessage({version:'2.39.0'},'*') \ No newline at end of file +postMessage({version:'2.39.1'},'*') \ No newline at end of file diff --git a/script.coffee b/script.coffee index 2c67aa5b3..76c722933 100644 --- a/script.coffee +++ b/script.coffee @@ -4722,7 +4722,7 @@ Main = $.globalEval "(#{code})()".replace '_id_', bq.id namespace: '4chan_x.' - version: '2.39.0' + version: '2.39.1' callbacks: [] css: ' /* dialog styling */ From 2592fc8ed0cdc8784bde2498317920391533016b Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Mon, 1 Apr 2013 20:12:39 +0200 Subject: [PATCH 041/104] Fix #978. It's magic! No need to unescape. --- 4chan_x.user.js | 2 +- changelog | 2 ++ script.coffee | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/4chan_x.user.js b/4chan_x.user.js index c0bdc8909..a1b2fe635 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -3046,7 +3046,7 @@ reader.onload = function(e) { var data, err; try { - data = JSON.parse(decodeURIComponent(escape(e.target.result))); + data = JSON.parse(e.target.result); Options.loadSettings(data); if (confirm('Import successful. Refresh now?')) { return window.location.reload(); diff --git a/changelog b/changelog index 152109283..2c112b37f 100644 --- a/changelog +++ b/changelog @@ -1,4 +1,6 @@ master +- Mayhem + Fix importing settings containing unicode characters. 2.39.1 - Mayhem diff --git a/script.coffee b/script.coffee index 76c722933..431aee8b6 100644 --- a/script.coffee +++ b/script.coffee @@ -2465,7 +2465,7 @@ Options = reader = new FileReader() reader.onload = (e) -> try - data = JSON.parse decodeURIComponent escape e.target.result + data = JSON.parse e.target.result Options.loadSettings data if confirm 'Import successful. Refresh now?' window.location.reload() From b5a3de3395991dd7ca09445c6b369ac0d101f092 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Mon, 1 Apr 2013 20:14:22 +0200 Subject: [PATCH 042/104] Release 2.39.2. --- 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 a1b2fe635..456ed5db7 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -1,6 +1,6 @@ // ==UserScript== // @name 4chan x -// @version 2.39.1 +// @version 2.39.2 // @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.com/4chan-x/ - * 4chan X 2.39.1 + * 4chan X 2.39.2 * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -5811,7 +5811,7 @@ return $.globalEval(("(" + code + ")()").replace('_id_', bq.id)); }, namespace: '4chan_x.', - version: '2.39.1', + version: '2.39.2', callbacks: [], css: '\ /* dialog styling */\ diff --git a/Cakefile b/Cakefile index 224342336..94ad020ee 100644 --- a/Cakefile +++ b/Cakefile @@ -2,7 +2,7 @@ {exec} = require 'child_process' fs = require 'fs' -VERSION = '2.39.1' +VERSION = '2.39.2' HEADER = """ // ==UserScript== diff --git a/changelog b/changelog index 2c112b37f..66c5e5205 100644 --- a/changelog +++ b/changelog @@ -1,4 +1,6 @@ master + +2.39.2 - Mayhem Fix importing settings containing unicode characters. diff --git a/latest.js b/latest.js index 213c063d1..bb4c76a77 100644 --- a/latest.js +++ b/latest.js @@ -1 +1 @@ -postMessage({version:'2.39.1'},'*') \ No newline at end of file +postMessage({version:'2.39.2'},'*') \ No newline at end of file diff --git a/script.coffee b/script.coffee index 431aee8b6..225c70bbc 100644 --- a/script.coffee +++ b/script.coffee @@ -4722,7 +4722,7 @@ Main = $.globalEval "(#{code})()".replace '_id_', bq.id namespace: '4chan_x.' - version: '2.39.1' + version: '2.39.2' callbacks: [] css: ' /* dialog styling */ From 8f433525db7f7a4e0f130a579ac7be78f3aeff13 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Tue, 2 Apr 2013 01:18:57 +0200 Subject: [PATCH 043/104] Give me the expected and actual results when reporting a bug/problem. --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 66cdd1973..b6ccc85bc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -3,7 +3,7 @@ 1. Make sure both your **browser** and **4chan X** are up to date. 2. Disable your other extensions & scripts to identify conflicts. 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. + 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. From f88724edfc1b9ee04805df82c49ae4370319c7ac Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Tue, 2 Apr 2013 01:22:59 +0200 Subject: [PATCH 044/104] Fix unhide-all button description. Refresh to apply. Close #975. --- src/features.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/features.coffee b/src/features.coffee index b8e08d49d..e92b3721e 100644 --- a/src/features.coffee +++ b/src/features.coffee @@ -345,7 +345,7 @@ Settings = return div = $.el 'div', - innerHTML: ": Clear manually hidden threads and posts on /#{g.BOARD}/." + innerHTML: ": Clear manually-hidden threads and posts on all boards. Refresh the page to apply." button = $ 'button', div hiddenNum = 0 $.get 'hiddenThreads', boards: {}, (item) -> From de7d923be8dee2daa32ca7983c9336cf23241c77 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Tue, 2 Apr 2013 01:40:48 +0200 Subject: [PATCH 045/104] Fix export/import of DataBoards. --- src/features.coffee | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/features.coffee b/src/features.coffee index e92b3721e..ae0efcf3b 100644 --- a/src/features.coffee +++ b/src/features.coffee @@ -373,9 +373,9 @@ Settings = items = WatchedThreads: {} for db in DataBoards items[db] = boards: {} - $.get items (items) -> - for key, val in items - data[key] = val + $.get items, (items) -> + for key, val of items + data.Conf[key] = val Settings.export now, data return a = $.el 'a', @@ -480,8 +480,6 @@ Settings = "Shift+#{s[0...-1]}#{s[-1..].toLowerCase()}" for key, val of data.Conf $.set key, val - for db in DataBoards - $.set db, data[db] $.set 'WatchedThreads', data.WatchedThreads convertSettings: (data, map) -> for prevKey, newKey of map From 512bdefbca70ebda64380c86226be4d53077badf Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Tue, 2 Apr 2013 03:18:04 +0200 Subject: [PATCH 046/104] Simplify exporting data. --- src/features.coffee | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/features.coffee b/src/features.coffee index ae0efcf3b..5ac509879 100644 --- a/src/features.coffee +++ b/src/features.coffee @@ -370,12 +370,12 @@ Settings = version: g.VERSION date: now Conf: Conf - items = WatchedThreads: {} + data.Conf.WatchedThreads = {} for db in DataBoards - items[db] = boards: {} - $.get items, (items) -> - for key, val of items - data.Conf[key] = val + data.Conf[db] = boards: {} + # Make sure to export the most recent data. + $.get data.Conf, (Conf) -> + data.Conf = Conf Settings.export now, data return a = $.el 'a', From 8dec16213e678387b845999eb2c85bc7f9b936f5 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Tue, 2 Apr 2013 03:19:27 +0200 Subject: [PATCH 047/104] Shave a line. --- src/features.coffee | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/features.coffee b/src/features.coffee index 5ac509879..075f30395 100644 --- a/src/features.coffee +++ b/src/features.coffee @@ -369,10 +369,9 @@ Settings = data = version: g.VERSION date: now - Conf: Conf - data.Conf.WatchedThreads = {} + Conf['WatchedThreads'] = {} for db in DataBoards - data.Conf[db] = boards: {} + Conf[db] = boards: {} # Make sure to export the most recent data. $.get data.Conf, (Conf) -> data.Conf = Conf From c084c66ee0e8aa94e534e6c5ef2ef0f37409d446 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Tue, 2 Apr 2013 10:31:28 +0200 Subject: [PATCH 048/104] Fix exporter exporting irrelevant data. --- src/features.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/features.coffee b/src/features.coffee index 075f30395..54081eeda 100644 --- a/src/features.coffee +++ b/src/features.coffee @@ -373,7 +373,7 @@ Settings = for db in DataBoards Conf[db] = boards: {} # Make sure to export the most recent data. - $.get data.Conf, (Conf) -> + $.get Conf, (Conf) -> data.Conf = Conf Settings.export now, data return From c15c7230dbf731727c3da72d26f78fa931d4ad16 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Tue, 2 Apr 2013 10:39:17 +0200 Subject: [PATCH 049/104] Fix QR keybind not focusing the comment input. #968 --- src/qr.coffee | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/qr.coffee b/src/qr.coffee index 3d95f2495..7cc355497 100644 --- a/src/qr.coffee +++ b/src/qr.coffee @@ -350,7 +350,7 @@ QR = posts: [] post: class - constructor: -> + constructor: (select) -> el = $.el 'a', className: 'qr-preview' draggable: true @@ -395,13 +395,13 @@ QR = persona.email if Conf['Remember Subject'] @sub = if prev then prev.sub else persona.sub - @select() if QR.selected is @ # load persona - @unlock() + @select() if select # load persona + @unlock() rm: -> $.rm @nodes.el index = QR.posts.indexOf @ if QR.posts.length is 1 - new QR.post().select() + new QR.post true else if @ is QR.selected (QR.posts[index-1] or QR.posts[index+1]).select() QR.posts.splice index, 1 @@ -791,13 +791,13 @@ QR = $.on nodes.autohide, 'change', QR.toggleHide $.on nodes.close, 'click', QR.close $.on nodes.dumpButton, 'click', -> nodes.el.classList.toggle 'dump' - $.on nodes.addPost, 'click', -> new QR.post().select() + $.on nodes.addPost, 'click', -> new QR.post true $.on nodes.form, 'submit', QR.submit $.on nodes.fileRM, 'click', -> QR.selected.rmFile() $.on nodes.spoiler, 'change', -> QR.selected.nodes.spoiler.click() $.on nodes.fileInput, 'change', QR.fileInput - new QR.post().select() + new QR.post true # save selected post's data for name in ['name', 'email', 'sub', 'com'] $.on nodes[name], 'input', -> QR.selected.save @ From a961c993a2b2599b2839c8ae29a90281641c125e Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Tue, 2 Apr 2013 11:04:18 +0200 Subject: [PATCH 050/104] Better consistency with board and boardID. --- src/features.coffee | 168 ++++++++++++++++++++++---------------------- src/main.coffee | 6 +- 2 files changed, 87 insertions(+), 87 deletions(-) diff --git a/src/features.coffee b/src/features.coffee index 54081eeda..19522f86f 100644 --- a/src/features.coffee +++ b/src/features.coffee @@ -1642,8 +1642,8 @@ ArchiveLink = type: 'post' el: div order: 90 - open: ({ID: postID, thread: threadID, board}) -> - redirect = Redirect.to {postID, threadID, board} + open: ({ID, thread, board}) -> + redirect = Redirect.to {ID, threadID: thread.ID, boardID: board.ID} redirect isnt "//boards.4chan.org/#{board}/" subEntries: [] @@ -1667,8 +1667,8 @@ ArchiveLink = target: '_blank' if type is 'post' - open = ({ID: postID, thread: threadID, board}) -> - el.href = Redirect.to {postID, threadID, board} + open: ({ID, thread, board}) -> + el.href = Redirect.to {ID, threadID: thread.ID, boardID: board.ID} true else open = (post) -> @@ -1676,7 +1676,7 @@ ArchiveLink = # We want to parse the exact same stuff as the filter does already. return false unless value el.href = Redirect.to - board: post.board + boardID: post.board.ID type: type value: value isSearch: true @@ -1934,36 +1934,36 @@ Nav = window.scrollBy 0, top Redirect = - image: (board, filename) -> + image: (boardID, filename) -> # Do not use g.BOARD, the image url can originate from a cross-quote. - switch "#{board}" + switch boardID when 'a', 'gd', 'jp', 'm', 'q', 'tg', 'vg', 'vp', 'vr', 'wsg' - "//archive.foolz.us/#{board}/full_image/#{filename}" + "//archive.foolz.us/#{boardID}/full_image/#{filename}" when 'u' - "//nsfw.foolz.us/#{board}/full_image/#{filename}" + "//nsfw.foolz.us/#{boardID}/full_image/#{filename}" when 'po' - "//archive.thedarkcave.org/#{board}/full_image/#{filename}" + "//archive.thedarkcave.org/#{boardID}/full_image/#{filename}" when 'ck', 'lit' - "//fuuka.warosu.org/#{board}/full_image/#{filename}" + "//fuuka.warosu.org/#{boardID}/full_image/#{filename}" when 'cgl', 'g', 'mu', 'w' - "//rbt.asia/#{board}/full_image/#{filename}" + "//rbt.asia/#{boardID}/full_image/#{filename}" when 'an', 'k', 'toy', 'x' - "http://archive.heinessen.com/#{board}/full_image/#{filename}" + "http://archive.heinessen.com/#{boardID}/full_image/#{filename}" when 'c' - "//archive.nyafuu.org/#{board}/full_image/#{filename}" - post: (board, postID) -> - switch "#{board}" + "//archive.nyafuu.org/#{boardID}/full_image/#{filename}" + post: (boardID, postID) -> + switch boardID when 'a', 'co', 'gd', 'jp', 'm', 'q', 'sp', 'tg', 'tv', 'v', 'vg', 'vp', 'vr', 'wsg' - "//archive.foolz.us/_/api/chan/post/?board=#{board}&num=#{postID}" + "//archive.foolz.us/_/api/chan/post/?board=#{boardID}&num=#{postID}" when 'u' - "//nsfw.foolz.us/_/api/chan/post/?board=#{board}&num=#{postID}" + "//nsfw.foolz.us/_/api/chan/post/?board=#{boardID}&num=#{postID}" when 'c', 'int', 'out', 'po' - "//archive.thedarkcave.org/_/api/chan/post/?board=#{board}&num=#{postID}" + "//archive.thedarkcave.org/_/api/chan/post/?board=#{boardID}&num=#{postID}" # for fuuka-based archives: # https://github.com/eksopl/fuuka/issues/27 to: (data) -> - {board} = data - switch "#{board}" + {boardID} = data + switch boardID when 'a', 'co', 'gd', 'jp', 'm', 'q', 'sp', 'tg', 'tv', 'v', 'vg', 'vp', 'vr', 'wsg' url = Redirect.path '//archive.foolz.us', 'foolfuuka', data when 'u' @@ -1982,11 +1982,11 @@ Redirect = url = Redirect.path '//archive.nyafuu.org', 'fuuka', data else if data.threadID - url = "//boards.4chan.org/#{board}/" + url = "//boards.4chan.org/#{boardID}/" url or '' path: (base, archiver, data) -> if data.isSearch - {board, type, value} = data + {boardID, type, value} = data type = if type is 'name' 'username' @@ -1996,20 +1996,20 @@ Redirect = type value = encodeURIComponent value return if archiver is 'foolfuuka' - "#{base}/#{board}/search/#{type}/#{value}" + "#{base}/#{boardID}/search/#{type}/#{value}" else if type is 'image' - "#{base}/#{board}/?task=search2&search_media_hash=#{value}" + "#{base}/#{boardID}/?task=search2&search_media_hash=#{value}" else - "#{base}/#{board}/?task=search2&search_#{type}=#{value}" + "#{base}/#{boardID}/?task=search2&search_#{type}=#{value}" - {board, threadID, postID} = data + {boardID, threadID, postID} = data # keep the number only if the location.hash was sent f.e. postID = postID.match(/\d+/)[0] if postID and typeof postID is 'string' path = if threadID - "#{board}/thread/#{threadID}" + "#{boardID}/thread/#{threadID}" else - "#{board}/post/#{postID}" + "#{boardID}/post/#{postID}" if archiver is 'foolfuuka' path += '/' if threadID and postID @@ -2031,12 +2031,12 @@ Build = "#{filename[...threshold - 5]}(...).#{filename[-3..]}" else filename - postFromObject: (data, board) -> + postFromObject: (data, boardID) -> o = # id postID: data.no threadID: data.resto or data.no - board: board + boardID: boardID # info name: data.name capcode: data.capcode @@ -2057,12 +2057,12 @@ Build = o.file = name: data.filename + data.ext timestamp: "#{data.tim}#{data.ext}" - url: "//images.4chan.org/#{board}/src/#{data.tim}#{data.ext}" + url: "//images.4chan.org/#{boardID}/src/#{data.tim}#{data.ext}" height: data.h width: data.w MD5: data.md5 size: data.fsize - turl: "//thumbs.4chan.org/#{board}/thumb/#{data.tim}s.jpg" + turl: "//thumbs.4chan.org/#{boardID}/thumb/#{data.tim}s.jpg" theight: data.tn_h twidth: data.tn_w isSpoiler: !!data.spoiler @@ -2074,7 +2074,7 @@ Build = @license: https://github.com/4chan/4chan-JS/blob/master/LICENSE ### { - postID, threadID, board + postID, threadID, boardID name, capcode, tripcode, uniqueID, email, subject, flagCode, flagName, date, dateUTC isSticky, isClosed comment @@ -2129,7 +2129,7 @@ Build = flag = if flagCode - " #{flagCode}" else '' @@ -2157,13 +2157,13 @@ Build = fileSize = "Spoiler Image, #{fileSize}" unless isArchived fileThumb = '//static.4chan.org/image/spoiler' - if spoilerRange = Build.spoilerRange[board] + if spoilerRange = Build.spoilerRange[boardID] # Randomize the spoiler image. - fileThumb += "-#{board}" + Math.floor 1 + spoilerRange * Math.random() + fileThumb += "-#{boardID}" + Math.floor 1 + spoilerRange * Math.random() fileThumb += '.png' file.twidth = file.theight = 100 - if board.ID isnt 'f' + if boardID.ID isnt 'f' imgSrc = "" + "#{fileSize}" @@ -2228,12 +2228,12 @@ Build = capcodeStart + capcode + userID + flag + sticky + closed + "
#{subject}" + "
#{date}" + - "No." + + "No." + "#{postID}" + '' + '' + @@ -2250,12 +2250,12 @@ Build = '
' + "#{date} " + "" + - "No." + + "No." + "#{postID}" + '' + '' + @@ -2269,7 +2269,7 @@ Build = for quote in $$ '.quotelink', container href = quote.getAttribute 'href' continue if href[0] is '/' # Cross-board quote, or board link - quote.href = "/#{board}/res/#{href}" # Fix pathnames + quote.href = "/#{boardID}/res/#{href}" # Fix pathnames container @@ -2282,11 +2282,11 @@ Get = $('.nameBlock', OP.nodes.info).textContent.trim() "/#{thread.board}/ - #{excerpt}" postFromRoot: (root) -> - link = $ 'a[title="Highlight this post"]', root - board = link.pathname.split('/')[1] - postID = link.hash[2..] - index = root.dataset.clone - post = g.posts["#{board}.#{postID}"] + link = $ 'a[title="Highlight this post"]', root + boardID = link.pathname.split('/')[1] + postID = link.hash[2..] + index = root.dataset.clone + 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 @@ -2299,7 +2299,7 @@ Get = threadID = path[3] postID = link.hash[2..] else # resurrected quote - boardID = link.dataset.board + boardID = link.dataset.boardid threadID = link.dataset.threadid or 0 postID = link.dataset.postid return { @@ -2333,18 +2333,18 @@ Get = quotelinks.filter (quotelink) -> {boardID, postID} = Get.postDataFromLink quotelink boardID is post.board.ID and postID is post.ID - postClone: (board, threadID, postID, root, context) -> - if post = g.posts["#{board}.#{postID}"] + postClone: (boardID, threadID, postID, root, context) -> + if post = g.posts["#{boardID}.#{postID}"] Get.insert post, root, context return root.textContent = "Loading post No.#{postID}..." if threadID - $.cache "//api.4chan.org/#{board}/res/#{threadID}.json", -> - Get.fetchedPost @, board, threadID, postID, root, context - else if url = Redirect.post board, postID + $.cache "//api.4chan.org/#{boardID}/res/#{threadID}.json", -> + Get.fetchedPost @, boardID, threadID, postID, root, context + else if url = Redirect.post boardID, postID $.cache url, -> - Get.archivedPost @, board, postID, root, context + Get.archivedPost @, boardID, postID, root, context insert: (post, root, context) -> # Stop here if the container has been removed while loading. return unless root.parentNode @@ -2358,19 +2358,19 @@ Get = root.innerHTML = null $.add root, nodes.root - fetchedPost: (req, board, threadID, postID, root, context) -> + fetchedPost: (req, boardID, threadID, postID, root, context) -> # In case of multiple callbacks for the same request, # don't parse the same original post more than once. - if post = g.posts["#{board}.#{postID}"] + if post = g.posts["#{boardID}.#{postID}"] Get.insert post, root, context return {status} = req if status not in [200, 304] # The thread can die by the time we check a quote. - if url = Redirect.post board, postID + if url = Redirect.post boardID, postID $.cache url, -> - Get.archivedPost @, board, postID, root, context + Get.archivedPost @, boardID, postID, root, context else $.addClass root, 'warning' root.textContent = @@ -2381,30 +2381,30 @@ Get = return posts = JSON.parse(req.response).posts - Build.spoilerRange[board] = posts[0].custom_spoiler + Build.spoilerRange[boardID] = posts[0].custom_spoiler for post in posts break if post.no is postID # we found it! if post.no > postID # The post can be deleted by the time we check a quote. - if url = Redirect.post board, postID + if url = Redirect.post boardID, postID $.cache url, -> - Get.archivedPost @, board, postID, root, context + Get.archivedPost @, boardID, postID, root, context else $.addClass root, 'warning' root.textContent = "Post No.#{postID} was not found." return - board = g.boards[board] or - new Board board - thread = g.threads["#{board}.#{threadID}"] or + board = g.boards[boardID] or + new Board boardID + thread = g.threads["#{boardID}.#{threadID}"] or new Thread threadID, board - post = new Post Build.postFromObject(post, board), thread, board + post = new Post Build.postFromObject(post, boardID), thread, board Main.callbackNodes Post, [post] Get.insert post, root, context - archivedPost: (req, board, postID, root, context) -> + archivedPost: (req, boardID, postID, root, context) -> # In case of multiple callbacks for the same request, # don't parse the same original post more than once. - if post = g.posts["#{board}.#{postID}"] + if post = g.posts["#{boardID}.#{postID}"] Get.insert post, root, context return @@ -2461,7 +2461,7 @@ Get = # id postID: "#{postID}" threadID: "#{threadID}" - board: board + boardID: boardID # info name: data.name_processed capcode: switch data.capcode @@ -2487,14 +2487,14 @@ Get = width: data.media.media_w MD5: data.media.media_hash size: data.media.media_size - turl: data.media.thumb_link or "//thumbs.4chan.org/#{board}/thumb/#{data.media.preview_orig}" + turl: data.media.thumb_link or "//thumbs.4chan.org/#{boardID}/thumb/#{data.media.preview_orig}" theight: data.media.preview_h twidth: data.media.preview_w isSpoiler: data.media.spoiler is '1' - board = g.boards[board] or - new Board board - thread = g.threads["#{board}.#{threadID}"] or + board = g.boards[boardID] or + new Board boardID + thread = g.threads["#{boardID}.#{threadID}"] or new Thread threadID, board post = new Post Build.post(o, true), thread, board, isArchived: true @@ -2518,13 +2518,13 @@ Quotify = continue quote = deadlink.textContent - continue unless ID = quote.match(/\d+$/)?[0] - board = + continue unless postID = quote.match(/\d+$/)?[0] + boardID = if m = quote.match /^>>>\/([a-z\d]+)/ m[1] else @board.ID - quoteID = "#{board}.#{ID}" + quoteID = "#{boardID}.#{postID}" # \u00A0 is nbsp if post = g.posts[quoteID] @@ -2532,31 +2532,31 @@ Quotify = # Don't (Dead) when quotifying in an archived post, # and we know the post still exists. a = $.el 'a', - href: "/#{board}/#{post.thread}/res/#p#{ID}" + href: "/#{boardID}/#{post.thread}/res/#p#{postID}" className: 'quotelink' textContent: quote else # Replace the .deadlink span if we can redirect. a = $.el 'a', - href: "/#{board}/#{post.thread}/res/#p#{ID}" + href: "/#{boardID}/#{post.thread}/res/#p#{postID}" className: 'quotelink deadlink' target: '_blank' textContent: "#{quote}\u00A0(Dead)" - a.setAttribute 'data-board', board + a.setAttribute 'data-boardid', boardID a.setAttribute 'data-threadid', post.thread.ID - a.setAttribute 'data-postid', ID - else if redirect = Redirect.to {board, threadID: 0, postID: ID} + a.setAttribute 'data-postid', postID + else if redirect = Redirect.to {boardID, threadID: 0, postID} # Replace the .deadlink span if we can redirect. a = $.el 'a', href: redirect className: 'deadlink' target: '_blank' textContent: "#{quote}\u00A0(Dead)" - if Redirect.post board, ID + if Redirect.post boardID, postID # Make it function as a normal quote if we can fetch the post. $.addClass a, 'quotelink' - a.setAttribute 'data-board', board - a.setAttribute 'data-postid', ID + a.setAttribute 'data-boardid', boardID + a.setAttribute 'data-postid', postID unless quoteID in @quotes @quotes.push quoteID diff --git a/src/main.coffee b/src/main.coffee index 303232774..49517b02b 100644 --- a/src/main.coffee +++ b/src/main.coffee @@ -428,9 +428,9 @@ Main = if d.title is '4chan - 404 Not Found' if Conf['404 Redirect'] and g.VIEW is 'thread' href = Redirect.to - board: g.BOARD - threadID: g.THREAD - postID: location.hash + boardID: g.BOARD.ID + threadID: g.THREAD.ID + postID: location.hash location.href = href or "/#{g.BOARD}/" return From ded92b94c2a0c2e5bcdf42bb29c5b7e83fb2951b Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Tue, 2 Apr 2013 13:34:34 +0200 Subject: [PATCH 051/104] Allow $.set to save multiple items at once. Fix importing watched threads from v2. --- lib/$.coffee | 48 +++++++++++++++++++++++++++++++-------------- src/features.coffee | 10 +++++----- 2 files changed, 38 insertions(+), 20 deletions(-) diff --git a/lib/$.coffee b/lib/$.coffee index 8bbf177dc..1e171fdfa 100644 --- a/lib/$.coffee +++ b/lib/$.coffee @@ -227,7 +227,11 @@ $.extend $, items = $.item key, val chrome.storage.sync.get items, cb set: (key, val) -> - chrome.storage.sync.set $.item key, val + items = if arguments.length is 2 + $.item key, val + else + key + chrome.storage.sync.set items <% } else if (type === 'userjs') { %> do -> # http://www.opera.com/docs/userjs/specs/#scriptstorage @@ -257,13 +261,20 @@ do -> if val = scriptStorage[g.NAMESPACE + key] items[key] = JSON.parse val cb items - $.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 + $.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 + (key, val) -> + if arguments.length is 1 + for key, val of key + set key, val + else + set key, val <% } else { %> # http://wiki.greasespot.net/Main_Page delete: (key) -> @@ -285,11 +296,18 @@ do -> if val = GM_getValue g.NAMESPACE + key items[key] = JSON.parse val cb items - set: (key, val) -> - key = g.NAMESPACE + key - val = JSON.stringify val - if key of $.syncing - # for `storage` events - localStorage.setItem key, val - GM_setValue key, val + set: do -> + set = (key, val) -> + key = g.NAMESPACE + key + val = JSON.stringify val + if key of $.syncing + # for `storage` events + localStorage.setItem key, val + GM_setValue key, val + (key, val) -> + if arguments.length is 1 + for key, val of key + set key, val + else + set key, val <% } %> diff --git a/src/features.coffee b/src/features.coffee index 19522f86f..503bb927b 100644 --- a/src/features.coffee +++ b/src/features.coffee @@ -226,8 +226,9 @@ Settings = new Notification 'info', el, 60 else $.on d, '4chanXInitFinished', Settings.open - $.set 'lastupdate', Date.now() - $.set 'previousversion', g.VERSION + $.set + lastupdate: Date.now() + previousversion: g.VERSION Settings.addSection 'Main', Settings.main Settings.addSection 'Filter', Settings.filter @@ -477,9 +478,8 @@ Settings = continue unless 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()}" - for key, val of data.Conf - $.set key, val - $.set 'WatchedThreads', data.WatchedThreads + data.Conf.WatchedThreads = data.WatchedThreads + $.set data.Conf convertSettings: (data, map) -> for prevKey, newKey of map data.Conf[newKey] = data.Conf[prevKey] if newKey From 7af57026bcd01256674679da3b49b026c034b644 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Tue, 2 Apr 2013 13:56:43 +0200 Subject: [PATCH 052/104] Fix UI.hover's asapTest firing constently in some cases. --- lib/ui.coffee | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/ui.coffee b/lib/ui.coffee index f83d84065..3985b8af6 100644 --- a/lib/ui.coffee +++ b/lib/ui.coffee @@ -289,8 +289,10 @@ UI = do -> o.hover = hover.bind o o.hoverend = hoverend.bind o - $.asap asapTest, -> - o.hover o.latestEvent + $.asap -> + !el.parentNode or asapTest() + , -> + o.hover o.latestEvent if el.parentNode $.on root, endEvents, o.hoverend $.on root, 'mousemove', o.hover From 4629864981d8aaf69d53172ec9387c6505a85b5d Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Tue, 2 Apr 2013 22:40:57 +0200 Subject: [PATCH 053/104] Fix archive redirection. Some simplifications. g.THREAD -> g.THREADID --- src/features.coffee | 76 +++++++++++++++++++++------------------------ src/main.coffee | 6 ++-- src/qr.coffee | 2 +- 3 files changed, 39 insertions(+), 45 deletions(-) diff --git a/src/features.coffee b/src/features.coffee index 503bb927b..206eb461e 100644 --- a/src/features.coffee +++ b/src/features.coffee @@ -1643,7 +1643,7 @@ ArchiveLink = el: div order: 90 open: ({ID, thread, board}) -> - redirect = Redirect.to {ID, threadID: thread.ID, boardID: board.ID} + redirect = Redirect.to {postID: ID, threadID: thread.ID, boardID: board.ID} redirect isnt "//boards.4chan.org/#{board}/" subEntries: [] @@ -1666,12 +1666,12 @@ ArchiveLink = textContent: text target: '_blank' - if type is 'post' - open: ({ID, thread, board}) -> - el.href = Redirect.to {ID, threadID: thread.ID, boardID: board.ID} + open = if type is 'post' + ({ID, thread, board}) -> + el.href = Redirect.to {postID: ID, threadID: thread.ID, boardID: board.ID} true else - open = (post) -> + (post) -> value = Filter[type] post # We want to parse the exact same stuff as the filter does already. return false unless value @@ -1965,59 +1965,53 @@ Redirect = {boardID} = data switch boardID when 'a', 'co', 'gd', 'jp', 'm', 'q', 'sp', 'tg', 'tv', 'v', 'vg', 'vp', 'vr', 'wsg' - url = Redirect.path '//archive.foolz.us', 'foolfuuka', data + Redirect.path '//archive.foolz.us', 'foolfuuka', data when 'u' - url = Redirect.path '//nsfw.foolz.us', 'foolfuuka', data + Redirect.path '//nsfw.foolz.us', 'foolfuuka', data when 'int', 'out', 'po' - url = Redirect.path '//archive.thedarkcave.org', 'foolfuuka', data + Redirect.path '//archive.thedarkcave.org', 'foolfuuka', data when 'ck', 'lit' - url = Redirect.path '//fuuka.warosu.org', 'fuuka', data + Redirect.path '//fuuka.warosu.org', 'fuuka', data when 'diy', 'sci' - url = Redirect.path '//archive.installgentoo.net', 'fuuka', data + Redirect.path '//archive.installgentoo.net', 'fuuka', data when 'cgl', 'g', 'mu', 'w' - url = Redirect.path '//rbt.asia', 'fuuka', data + Redirect.path '//rbt.asia', 'fuuka', data when 'an', 'fit', 'k', 'mlp', 'r9k', 'toy', 'x' - url = Redirect.path 'http://archive.heinessen.com', 'fuuka', data + Redirect.path 'http://archive.heinessen.com', 'fuuka', data when 'c' - url = Redirect.path '//archive.nyafuu.org', 'fuuka', data + Redirect.path '//archive.nyafuu.org', 'fuuka', data else - if data.threadID - url = "//boards.4chan.org/#{boardID}/" - url or '' + if data.threadID then "//boards.4chan.org/#{boardID}/" else '' path: (base, archiver, data) -> if data.isSearch {boardID, type, value} = data - type = - if type is 'name' - 'username' - else if type is 'MD5' - 'image' - else - type + type = if type is 'name' + 'username' + else if type is 'MD5' + 'image' + else + type value = encodeURIComponent value return if archiver is 'foolfuuka' - "#{base}/#{boardID}/search/#{type}/#{value}" - else if type is 'image' - "#{base}/#{boardID}/?task=search2&search_media_hash=#{value}" - else - "#{base}/#{boardID}/?task=search2&search_#{type}=#{value}" + "#{base}/#{boardID}/search/#{type}/#{value}" + else if type is 'image' + "#{base}/#{boardID}/?task=search2&search_media_hash=#{value}" + else + "#{base}/#{boardID}/?task=search2&search_#{type}=#{value}" {boardID, threadID, postID} = data # keep the number only if the location.hash was sent f.e. - postID = postID.match(/\d+/)[0] if postID and typeof postID is 'string' - path = - if threadID - "#{boardID}/thread/#{threadID}" - else - "#{boardID}/post/#{postID}" + path = if threadID + "#{boardID}/thread/#{threadID}" + else + "#{boardID}/post/#{postID}" if archiver is 'foolfuuka' path += '/' if threadID and postID - path += - if archiver is 'foolfuuka' - "##{postID}" - else - "#p#{postID}" + path += if archiver is 'foolfuuka' + "##{postID}" + else + "#p#{postID}" "#{base}/#{path}" Build = @@ -2230,7 +2224,7 @@ Build = "
#{date}" + "No." + "No." + " if g.VIEW is 'thread' - QR.nodes.thread.value = g.THREAD + QR.nodes.thread.value = g.THREADID else QR.nodes.thread.value = 'new' From 3e211c8ba7980e68b86a8ca2e3dd0ce775683aca Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Tue, 2 Apr 2013 23:28:22 +0200 Subject: [PATCH 054/104] Prevent file upload in threads that reached the file limit. Fix QR preventing from posting in sticky thread instead of locked thread. --- src/features.coffee | 21 +++++++++++---------- src/qr.coffee | 4 +++- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/features.coffee b/src/features.coffee index 206eb461e..0aac59152 100644 --- a/src/features.coffee +++ b/src/features.coffee @@ -3794,19 +3794,20 @@ ThreadStats = for ID, post of @posts postCount++ fileCount++ if post.file - ThreadStats.update postCount, fileCount ThreadStats.thread = @ + ThreadStats.update postCount, fileCount $.on d, 'ThreadUpdate', ThreadStats.onUpdate $.add d.body, ThreadStats.dialog onUpdate: (e) -> return if e.detail[404] - {postCount, fileCount, postLimit, fileLimit} = e.detail - ThreadStats.update postCount, fileCount, postLimit, fileLimit - update: (postCount, fileCount, postLimit, fileLimit) -> - ThreadStats.postCountEl.textContent = postCount - ThreadStats.fileCountEl.textContent = fileCount - (if postLimit and !ThreadStats.thread.isSticky then $.addClass else $.rmClass) ThreadStats.postCountEl, 'warning' - (if fileLimit and !ThreadStats.thread.isSticky then $.addClass else $.rmClass) ThreadStats.fileCountEl, 'warning' + {postCount, fileCount} = e.detail + ThreadStats.update postCount, fileCount + update: (postCount, fileCount) -> + {thread, postCountEl, fileCountEl} = ThreadStats + postCountEl.textContent = postCount + fileCountEl.textContent = fileCount + (if thread.postLimit and !thread.isSticky then $.addClass else $.rmClass) postCountEl, 'warning' + (if thread.fileLimit and !thread.isSticky then $.addClass else $.rmClass) fileCountEl, 'warning' ThreadUpdater = init: -> @@ -4010,6 +4011,8 @@ ThreadUpdater = ThreadUpdater.updateThreadStatus 'Sticky', OP ThreadUpdater.updateThreadStatus 'Closed', OP + ThreadUpdater.thread.postLimit = !!OP.bumplimit + ThreadUpdater.thread.fileLimit = !!OP.imagelimit nodes = [] # post container elements posts = [] # post objects @@ -4085,8 +4088,6 @@ ThreadUpdater = deletedFiles: deletedFiles postCount: OP.replies + 1 fileCount: OP.images + (!!ThreadUpdater.thread.OP.file and !ThreadUpdater.thread.OP.file.isDead) - postLimit: !!OP.bumplimit - fileLimit: !!OP.imagelimit ThreadWatcher = init: -> diff --git a/src/qr.coffee b/src/qr.coffee index e077266f8..c25304397 100644 --- a/src/qr.coffee +++ b/src/qr.coffee @@ -836,10 +836,12 @@ QR = err = 'New threads require a subject.' else unless post.file or textOnly = !!$ 'input[name=textonly]', $.id 'postForm' err = 'No file selected.' - else if g.BOARD.threads[threadID].isSticky + else if g.BOARD.threads[threadID].isClosed err = 'You can\'t reply to this thread anymore.' else unless post.com or post.file err = 'No file selected.' + else if post.file and g.BOARD.threads[threadID].fileLimit + err = 'Max limit of image replies has been reached.' if QR.captcha.isEnabled and !err {challenge, response} = QR.captcha.getOne() From a3face3746a2bfb19ebdc4652d238117d18987f4 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Tue, 2 Apr 2013 23:39:46 +0200 Subject: [PATCH 055/104] Fix (0) + Unread (You) favicon, in case you inlined a backlink quoting you for example. --- src/features.coffee | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/features.coffee b/src/features.coffee index 0aac59152..b6d191a7c 100644 --- a/src/features.coffee +++ b/src/features.coffee @@ -3710,10 +3710,11 @@ Unread = else Favicon.dead else - if Unread.postsQuotingYou.length - Favicon.unreadY - else if count - Favicon.unread + if count + if Unread.postsQuotingYou.length + Favicon.unreadY + else + Favicon.unread else Favicon.default From 2e1bb104e5498218b702159e444b628d3cf441dd Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Tue, 2 Apr 2013 23:56:19 +0200 Subject: [PATCH 056/104] Concat quotelinks and backlinks for less loops. --- src/features.coffee | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/features.coffee b/src/features.coffee index b6d191a7c..6afa9a8cb 100644 --- a/src/features.coffee +++ b/src/features.coffee @@ -2572,9 +2572,7 @@ QuoteInline = name: 'Quote Inlining' cb: @node node: -> - for link in @nodes.quotelinks - $.on link, 'click', QuoteInline.toggle - for link in @nodes.backlinks + for link in @nodes.quotelinks.concat [@nodes.backlinks...] $.on link, 'click', QuoteInline.toggle return toggle: (e) -> @@ -2653,9 +2651,7 @@ QuotePreview = name: 'Quote Previewing' cb: @node node: -> - for link in @nodes.quotelinks - $.on link, 'mouseover', QuotePreview.mouseover - for link in @nodes.backlinks + for link in @nodes.quotelinks.concat [@nodes.backlinks...] $.on link, 'mouseover', QuotePreview.mouseover return mouseover: (e) -> @@ -2688,10 +2684,7 @@ QuotePreview = quoterID = $.x('ancestor::*[@id][1]', @).id.match(/\d+$/)[0] clone = Get.postFromRoot qp.firstChild - for quote in clone.nodes.quotelinks - if quote.hash[2..] is quoterID - $.addClass quote, 'forwardlink' - for quote in clone.nodes.backlinks + for quote in clone.nodes.quotelinks.concat [clone.nodes.backlinks...] if quote.hash[2..] is quoterID $.addClass quote, 'forwardlink' return From 1cad4b8d17d34a34639452cc28a1c75d2273d6eb Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Wed, 3 Apr 2013 00:34:24 +0200 Subject: [PATCH 057/104] Completely fix the 'read arbitrary post' use case. --- src/features.coffee | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/features.coffee b/src/features.coffee index 6afa9a8cb..5d918ce58 100644 --- a/src/features.coffee +++ b/src/features.coffee @@ -2609,10 +2609,10 @@ QuoteInline = $.addClass post.nodes.root, 'forwarded' post.forwarded++ or post.forwarded = 1 - # Decrease the unread count if this post is in the array of unread posts. - if Unread.posts and (i = Unread.posts.indexOf post) isnt -1 - Unread.posts.splice i, 1 - Unread.update() + # Decrease the unread count if this post + # is in the array of unread posts. + return unless Unread.posts + Unread.readSinglePost post rm: (quotelink, boardID, threadID, postID, context) -> isBacklink = $.hasClass quotelink, 'backlink' @@ -3638,6 +3638,16 @@ Unread = else Unread.addPosts e.detail.newPosts + readSinglePost: (post) -> + return if (i = Unread.posts.indexOf post) is -1 + Unread.posts.splice i, 1 + if i is 0 + Unread.lastReadPost = post.ID + Unread.saveLastReadPost() + if (i = Unread.postsQuotingYou.indexOf post) isnt -1 + Unread.postsQuotingYou.splice i, 1 + Unread.update() + readArray: (arr) -> for post, i in arr break if post.ID > Unread.lastReadPost From 3d72a2331cef7f0175a8758e0c16aa58ec39a2fa Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Wed, 3 Apr 2013 02:29:07 +0200 Subject: [PATCH 058/104] Fix QR persona and quoting. #968 --- src/qr.coffee | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/qr.coffee b/src/qr.coffee index c25304397..a4aad9201 100644 --- a/src/qr.coffee +++ b/src/qr.coffee @@ -377,12 +377,12 @@ QR = for event in ['dragStart', 'dragEnter', 'dragLeave', 'dragOver', 'dragEnd', 'drop'] $.on el, event.toLowerCase(), @[event] + prev = QR.posts[QR.posts.length - 1] QR.posts.push @ @spoiler = if prev and Conf['Remember Spoiler'] prev.spoiler else false - prev = QR.posts[QR.posts.length - 1] $.get 'QR.persona', {}, (item) => persona = item['QR.persona'] @name = if prev @@ -395,7 +395,8 @@ QR = persona.email if Conf['Remember Subject'] @sub = if prev then prev.sub else persona.sub - @select() if select # load persona + @load() if QR.selected is @ # load persona + @select() if select @unlock() rm: -> $.rm @nodes.el @@ -430,6 +431,8 @@ QR = rectEl = @nodes.el.getBoundingClientRect() rectList = @nodes.el.parentNode.getBoundingClientRect() @nodes.el.parentNode.scrollLeft += rectEl.left + rectEl.width/2 - rectList.left - rectList.width/2 + @load() + load: -> # Load this post's values. for name in ['name', 'email', 'sub', 'com'] QR.nodes[name].value = @[name] or null @@ -993,7 +996,10 @@ QR = # Enable auto-posting if we have stuff to post, disable it otherwise. QR.cooldown.auto = QR.posts.length > 1 and isReply - post.rm() + unless Conf['Persistent QR'] or QR.cooldown.auto + QR.close() + else + post.rm() QR.cooldown.set {req, post, isReply} @@ -1007,9 +1013,6 @@ QR = else window.location = "/#{g.BOARD}/res/#{threadID}" - unless Conf['Persistent QR'] or QR.cooldown.auto - QR.close() - QR.status() abort: -> From 98e46dd4306c85e30d865b6825682b5f54b2bde8 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Wed, 3 Apr 2013 02:40:23 +0200 Subject: [PATCH 059/104] Mark posts as dead as soon as we delete them. --- src/features.coffee | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/features.coffee b/src/features.coffee index 5d918ce58..ba4fd03b4 100644 --- a/src/features.coffee +++ b/src/features.coffee @@ -1566,12 +1566,12 @@ DeleteLink = link = @ $.ajax $.id('delform').action.replace("/#{g.BOARD}/", "/#{post.board}/"), { - onload: -> DeleteLink.load link, @response - onerror: -> DeleteLink.error link - }, { - form: $.formData form - } - load: (link, html) -> + onload: -> DeleteLink.load link, post, @response + onerror: -> DeleteLink.error link + }, { + form: $.formData form + } + load: (link, post, html) -> tmpDoc = d.implementation.createHTMLDocument '' tmpDoc.documentElement.innerHTML = html if tmpDoc.title is '4chan - Banned' # Ban/warn check @@ -1580,6 +1580,9 @@ DeleteLink = s = msg.textContent $.on link, 'click', DeleteLink.delete else + if tmpDoc.title is 'Updating index...' + # We're 100% sure. + (post.origin or post).kill() s = 'Deleted' link.textContent = s error: (link) -> From 1e34674fdfa86594e34e08a08b71d840dad6d434 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Wed, 3 Apr 2013 02:44:49 +0200 Subject: [PATCH 060/104] Prevent a post from being killed twice, f.e. when the thread updater udpdates the post before we get an answer from the server. --- src/main.coffee | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main.coffee b/src/main.coffee index 6ca73d301..16966fe8f 100644 --- a/src/main.coffee +++ b/src/main.coffee @@ -158,10 +158,12 @@ class Post kill: (file, now) -> now or= new Date() if file + return if @file.isDead @file.isDead = true @file.timeOfDeath = now $.addClass @nodes.root, 'deleted-file' else + return if @isDead @isDead = true @timeOfDeath = now $.addClass @nodes.root, 'deleted-post' From cc27e36d98ff8022370d8dc6dc2c8763d1da0595 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Wed, 3 Apr 2013 03:01:06 +0200 Subject: [PATCH 061/104] Fix purging dead threads from DataBoards. --- src/databoard.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/databoard.coffee b/src/databoard.coffee index 09cbb9f31..42942af30 100644 --- a/src/databoard.coffee +++ b/src/databoard.coffee @@ -57,7 +57,7 @@ class DataBoard @deleteIfEmpty {boardID} now = Date.now() - if @data.lastChecked < now - 12 * $.HOUR + if (@data.lastChecked or 0) < now - 12 * $.HOUR @data.lastChecked = now for boardID of @data.boards @ajaxClean boardID From 9bd3ff0426ed9433cfec4408eeb68fe303162722 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Wed, 3 Apr 2013 16:59:47 +0200 Subject: [PATCH 062/104] Less array building. --- lib/$.coffee | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/$.coffee b/lib/$.coffee index 1e171fdfa..f29da985b 100644 --- a/lib/$.coffee +++ b/lib/$.coffee @@ -273,6 +273,7 @@ do -> if arguments.length is 1 for key, val of key set key, val + return else set key, val <% } else { %> @@ -308,6 +309,7 @@ do -> if arguments.length is 1 for key, val of key set key, val + return else set key, val <% } %> From dda20916b4bfec2b6f60caa2d2052fa5b0e9a596 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Wed, 3 Apr 2013 17:31:51 +0200 Subject: [PATCH 063/104] Fix $.set for userjs/userscript. #968 Fix typo in $.delete for userscript. Don't use `arguments` for $.get/$.set. --- lib/$.coffee | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/lib/$.coffee b/lib/$.coffee index f29da985b..e05c66c79 100644 --- a/lib/$.coffee +++ b/lib/$.coffee @@ -220,14 +220,14 @@ $.extend $, delete: (keys) -> chrome.storage.sync.remove keys get: (key, val, cb) -> - if arguments.length is 2 + if typeof cb is 'function' + items = $.item key, val + else items = key cb = val - else - items = $.item key, val chrome.storage.sync.get items, cb set: (key, val) -> - items = if arguments.length is 2 + items = if typeof key is 'string' $.item key, val else key @@ -251,11 +251,11 @@ do -> delete scriptStorage[key] return $.get = (key, val, cb) -> - if arguments.length is 2 + if typeof cb is 'function' + items = $.item key, val + else items = key cb = val - else - items = $.item key, val $.queueTask -> for key of items if val = scriptStorage[g.NAMESPACE + key] @@ -269,16 +269,16 @@ do -> # for `storage` events localStorage.setItem key, val scriptStorage[key] = val - (key, val) -> - if arguments.length is 1 - for key, val of key - set key, val + (keys, val) -> + if typeof keys is 'string' + set keys, val return - else + for key, val of keys set key, val + return <% } else { %> # http://wiki.greasespot.net/Main_Page - delete: (key) -> + delete: (keys) -> unless keys instanceof Array keys = [keys] for key in keys @@ -287,11 +287,11 @@ do -> GM_deleteValue key return get: (key, val, cb) -> - if arguments.length is 2 + if typeof cb is 'function' + items = $.item key, val + else items = key cb = val - else - items = $.item key, val $.queueTask -> for key of items if val = GM_getValue g.NAMESPACE + key @@ -305,11 +305,11 @@ do -> # for `storage` events localStorage.setItem key, val GM_setValue key, val - (key, val) -> - if arguments.length is 1 - for key, val of key - set key, val + (keys, val) -> + if typeof keys is 'string' + set keys, val return - else + for key, val of keys set key, val + return <% } %> From fd587bff22555965bba85ef94a0966a2af02d563 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Wed, 3 Apr 2013 18:27:06 +0200 Subject: [PATCH 064/104] ReplyHiding -> PostHiding --- src/features.coffee | 68 ++++++++++++++++++++++----------------------- src/main.coffee | 4 +-- 2 files changed, 36 insertions(+), 36 deletions(-) diff --git a/src/features.coffee b/src/features.coffee index ba4fd03b4..05cd5aeec 100644 --- a/src/features.coffee +++ b/src/features.coffee @@ -890,7 +890,7 @@ Filter = # Hide if result.hide if @isReply - ReplyHiding.hide @, result.stub + PostHiding.hide @, result.stub else if g.VIEW is 'index' ThreadHiding.hide @thread, result.stub else @@ -1204,7 +1204,7 @@ ThreadHiding = threadRoot.nextElementSibling.hidden = threadRoot.hidden = thread.isHidden = false -ReplyHiding = +PostHiding = init: -> return if g.VIEW is 'catalog' or !Conf['Reply Hiding'] and !Conf['Reply Hiding Link'] @@ -1215,14 +1215,14 @@ ReplyHiding = node: -> return if !@isReply or @isClone - if data = ReplyHiding.db.get {boardID: @board.ID, threadID: @thread.ID, postID: @ID} + if data = PostHiding.db.get {boardID: @board.ID, threadID: @thread.ID, postID: @ID} if data.thisPost - ReplyHiding.hide @, data.makeStub, data.hideRecursively + PostHiding.hide @, data.makeStub, data.hideRecursively else - Recursive.apply ReplyHiding.hide, @, data.makeStub, true - Recursive.add ReplyHiding.hide, @, data.makeStub, true + Recursive.apply PostHiding.hide, @, data.makeStub, true + Recursive.add PostHiding.hide, @, data.makeStub, true return unless Conf['Reply Hiding'] - $.replace $('.sideArrows', @nodes.root), ReplyHiding.makeButton @, 'hide' + $.replace $('.sideArrows', @nodes.root), PostHiding.makeButton @, 'hide' menu: init: -> @@ -1236,7 +1236,7 @@ ReplyHiding = apply = $.el 'a', textContent: 'Apply' href: 'javascript:;' - $.on apply, 'click', ReplyHiding.menu.hide + $.on apply, 'click', PostHiding.menu.hide thisPost = $.el 'label', innerHTML: ' This post' @@ -1252,7 +1252,7 @@ ReplyHiding = open: (post) -> if !post.isReply or post.isClone or post.isHidden return false - ReplyHiding.menu.post = post + PostHiding.menu.post = post true subEntries: [{el: apply}, {el: thisPost}, {el: replies}, {el: makeStub}] @@ -1264,7 +1264,7 @@ ReplyHiding = apply = $.el 'a', textContent: 'Apply' href: 'javascript:;' - $.on apply, 'click', ReplyHiding.menu.show + $.on apply, 'click', PostHiding.menu.show thisPost = $.el 'label', innerHTML: ' This post' @@ -1278,9 +1278,9 @@ ReplyHiding = open: (post) -> if !post.isReply or post.isClone or !post.isHidden return false - unless data = ReplyHiding.db.get {boardID: post.board.ID, threadID: post.thread.ID, postID: post.ID} + unless data = PostHiding.db.get {boardID: post.board.ID, threadID: post.thread.ID, postID: post.ID} return false - ReplyHiding.menu.post = post + PostHiding.menu.post = post thisPost.firstChild.checked = post.isHidden replies.firstChild.checked = if data?.hideRecursively? then data.hideRecursively else Conf['Recursive Hiding'] true @@ -1290,30 +1290,30 @@ ReplyHiding = thisPost = $('input[name=thisPost]', parent).checked replies = $('input[name=replies]', parent).checked makeStub = $('input[name=makeStub]', parent).checked - {post} = ReplyHiding.menu + {post} = PostHiding.menu if thisPost - ReplyHiding.hide post, makeStub, replies + PostHiding.hide post, makeStub, replies else if replies - Recursive.apply ReplyHiding.hide, post, makeStub, true - Recursive.add ReplyHiding.hide, post, makeStub, true + Recursive.apply PostHiding.hide, post, makeStub, true + Recursive.add PostHiding.hide, post, makeStub, true else return - ReplyHiding.saveHiddenState post, true, thisPost, makeStub, replies + PostHiding.saveHiddenState post, true, thisPost, makeStub, replies $.event 'CloseMenu' show: -> parent = @parentNode thisPost = $('input[name=thisPost]', parent).checked replies = $('input[name=replies]', parent).checked - {post} = ReplyHiding.menu + {post} = PostHiding.menu if thisPost - ReplyHiding.show post, replies + PostHiding.show post, replies else if replies - Recursive.apply ReplyHiding.show, post, true - Recursive.rm ReplyHiding.hide, post, true + Recursive.apply PostHiding.show, post, true + Recursive.rm PostHiding.hide, post, true else return - if data = ReplyHiding.db.get {boardID: post.board.ID, threadID: post.thread.ID, postID: post.ID} - ReplyHiding.saveHiddenState post, !(thisPost and replies), !thisPost, data.makeStub, !replies + if data = PostHiding.db.get {boardID: post.board.ID, threadID: post.thread.ID, postID: post.ID} + PostHiding.saveHiddenState post, !(thisPost and replies), !thisPost, data.makeStub, !replies $.event 'CloseMenu' makeButton: (post, type) -> @@ -1321,7 +1321,7 @@ ReplyHiding = className: "#{type}-reply-button" innerHTML: "[ #{if type is 'hide' then '-' else '+'} ]" href: 'javascript:;' - $.on a, 'click', ReplyHiding.toggle + $.on a, 'click', PostHiding.toggle a saveHiddenState: (post, isHiding, thisPost, makeStub, hideRecursively) -> @@ -1334,25 +1334,25 @@ ReplyHiding = thisPost: thisPost isnt false # undefined -> true makeStub: makeStub hideRecursively: hideRecursively - ReplyHiding.db.set data + PostHiding.db.set data else - ReplyHiding.db.delete data + PostHiding.db.delete data toggle: -> post = Get.postFromNode @ if post.isHidden - ReplyHiding.show post + PostHiding.show post else - ReplyHiding.hide post - ReplyHiding.saveHiddenState post, post.isHidden + PostHiding.hide post + PostHiding.saveHiddenState post, post.isHidden hide: (post, makeStub=Conf['Stubs'], hideRecursively=Conf['Recursive Hiding']) -> return if post.isHidden post.isHidden = true if hideRecursively - Recursive.apply ReplyHiding.hide, post, makeStub, true - Recursive.add ReplyHiding.hide, post, makeStub, true + Recursive.apply PostHiding.hide, post, makeStub, true + Recursive.add PostHiding.hide, post, makeStub, true for quotelink in Get.allQuotelinksLinkingTo post $.addClass quotelink, 'filtered' @@ -1361,7 +1361,7 @@ ReplyHiding = post.nodes.root.hidden = true return - a = ReplyHiding.makeButton post, 'show' + a = PostHiding.makeButton post, 'show' postInfo = if Conf['Anonymize'] 'Anonymous' @@ -1383,8 +1383,8 @@ ReplyHiding = post.nodes.root.hidden = false post.isHidden = false if showRecursively - Recursive.apply ReplyHiding.show, post, true - Recursive.rm ReplyHiding.hide, post + Recursive.apply PostHiding.show, post, true + Recursive.rm PostHiding.hide, post for quotelink in Get.allQuotelinksLinkingTo post $.rmClass quotelink, 'filtered' return diff --git a/src/main.coffee b/src/main.coffee index 16966fe8f..92cf94ae7 100644 --- a/src/main.coffee +++ b/src/main.coffee @@ -349,14 +349,14 @@ Main = initFeature 'Resurrect Quotes', Quotify initFeature 'Filter', Filter initFeature 'Thread Hiding', ThreadHiding - initFeature 'Reply Hiding', ReplyHiding + initFeature 'Reply Hiding', PostHiding initFeature 'Recursive', Recursive initFeature 'Strike-through Quotes', QuoteStrikeThrough initFeature 'Quick Reply', QR initFeature 'Menu', Menu initFeature 'Report Link', ReportLink initFeature 'Thread Hiding (Menu)', ThreadHiding.menu - initFeature 'Reply Hiding (Menu)', ReplyHiding.menu + initFeature 'Reply Hiding (Menu)', PostHiding.menu initFeature 'Delete Link', DeleteLink initFeature 'Filter (Menu)', Filter.menu initFeature 'Download Link', DownloadLink From 2be289a0ef4c41bce3690114397ab5a0819a99ce Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Wed, 3 Apr 2013 18:39:36 +0200 Subject: [PATCH 065/104] Fix exporting on Firefox. #968 --- src/features.coffee | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/features.coffee b/src/features.coffee index 05cd5aeec..329579b1c 100644 --- a/src/features.coffee +++ b/src/features.coffee @@ -388,9 +388,9 @@ Settings = a.click() return # XXX Firefox won't let us download automatically. - output = @parentNode.nextElementSibling - output.innerHTML = null - $.add output, a + p = $ '.imp-exp-result', Settings.dialog + p.innerHTML = null + $.add p, a import: -> @nextElementSibling.click() onImport: -> From ad816c579be1d3c88ef02a67010b8a984b7e7dc2 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Wed, 3 Apr 2013 18:45:51 +0200 Subject: [PATCH 066/104] Use console.error instead of console.log. --- src/features.coffee | 2 +- src/main.coffee | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/features.coffee b/src/features.coffee index 329579b1c..031d10e4b 100644 --- a/src/features.coffee +++ b/src/features.coffee @@ -408,7 +408,7 @@ Settings = window.location.reload() catch err output.textContent = 'Import failed due to an error.' - c.log err.stack + c.error err.stack reader.readAsText file loadSettings: (data) -> version = data.version.split '.' diff --git a/src/main.coffee b/src/main.coffee index 92cf94ae7..589806344 100644 --- a/src/main.coffee +++ b/src/main.coffee @@ -554,7 +554,7 @@ Main = parseError: (data) -> {message, error} = data - c.log message, error.stack + c.error message, error.stack message = $.el 'div', textContent: message error = $.el 'div', From 7b0b92accf0b1973e0c8bb4ce1e629847abfa8e8 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Wed, 3 Apr 2013 18:48:04 +0200 Subject: [PATCH 067/104] Don't break DataBoards when we $.delete them. --- src/databoard.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/databoard.coffee b/src/databoard.coffee index 42942af30..3323ac97d 100644 --- a/src/databoard.coffee +++ b/src/databoard.coffee @@ -76,5 +76,5 @@ class DataBoard $.set @key, @data onSync: (data) -> - @data = data + @data = data or boards: {} @sync?() From 9b06417c8140c55cf8ac50a1e7ce00cc36393d19 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Wed, 3 Apr 2013 19:05:32 +0200 Subject: [PATCH 068/104] Remove webkit prefixes for transition/gradients, bump min version requirement for Chrome to 26. --- css/style.css | 8 -------- src/manifest.json | 2 +- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/css/style.css b/css/style.css index 50285bed7..e494f5cd2 100644 --- a/css/style.css +++ b/css/style.css @@ -14,7 +14,6 @@ margin: 0; padding: 2px 4px 3px; outline: none; - -webkit-transition: color .25s, border-color .25s, -webkit-flex .25s; transition: color .25s, border-color .25s, flex .25s; } .field::-moz-placeholder, @@ -113,7 +112,6 @@ a[href="javascript:;"] { display: flex; padding: 3px 4px 4px; position: relative; - -webkit-transition: all .1s .05s ease-in-out; transition: all .1s .05s ease-in-out; } #board-list { @@ -126,7 +124,6 @@ a[href="javascript:;"] { margin-bottom: -1em; -webkit-transform: translateY(-100%); transform: translateY(-100%); - -webkit-transition: all .8s .6s cubic-bezier(.55, .055, .675, .19); transition: all .8s .6s cubic-bezier(.55, .055, .675, .19); } #toggle-header-bar { @@ -179,7 +176,6 @@ a[href="javascript:;"] { width: 500px; max-width: 100%; position: relative; - -webkit-transition: all .25s ease-in-out; transition: all .25s ease-in-out; } .notification.error { @@ -555,7 +551,6 @@ a[href="javascript:;"] { flex: 3; } #dump-button { - background: -webkit-linear-gradient(#EEE, #CCC); background: linear-gradient(#EEE, #CCC); border: 1px solid #CCC; margin: 0; @@ -564,11 +559,9 @@ a[href="javascript:;"] { width: 30px; } #dump-button:hover, #dump-button:focus { - background: -webkit-linear-gradient(#FFF, #DDD); background: linear-gradient(#FFF, #DDD); } #dump-button:active, .dump #dump-button:not(:hover):not(:focus) { - background: -webkit-linear-gradient(#CCC, #DDD); background: linear-gradient(#CCC, #DDD); } .gecko #dump-button { @@ -620,7 +613,6 @@ a[href="javascript:;"] { overflow: hidden; position: relative; text-shadow: 0 1px 1px #000; - -webkit-transition: opacity .25s ease-in-out; transition: opacity .25s ease-in-out; vertical-align: top; white-space: pre; diff --git a/src/manifest.json b/src/manifest.json index b17375947..c89510295 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -14,7 +14,7 @@ "run_at": "document_start" }], "homepage_url": "<%= meta.page %>", - "minimum_chrome_version": "25", + "minimum_chrome_version": "26", "permissions": [ "storage" ] From 3a1e9554c128ae45bb4860496f9ef3732cfeb0ba Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Wed, 3 Apr 2013 20:17:57 +0200 Subject: [PATCH 069/104] Fix a missing prefix for a transition. --- css/style.css | 1 + 1 file changed, 1 insertion(+) diff --git a/css/style.css b/css/style.css index e494f5cd2..10cf4c06d 100644 --- a/css/style.css +++ b/css/style.css @@ -14,6 +14,7 @@ margin: 0; padding: 2px 4px 3px; outline: none; + transition: color .25s, border-color .25s, -webkit-flex .25s; transition: color .25s, border-color .25s, flex .25s; } .field::-moz-placeholder, From 72be364d20b2fb8b765b473eeaf83caed9626274 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Wed, 3 Apr 2013 21:33:40 +0200 Subject: [PATCH 070/104] Don't reposition if the expanded image is in a quote preview. --- src/features.coffee | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/features.coffee b/src/features.coffee index 031d10e4b..c539a6b4e 100644 --- a/src/features.coffee +++ b/src/features.coffee @@ -3222,8 +3222,14 @@ ImageExpand = completeExpand: (post) -> {thumb} = post.file return unless $.hasClass thumb, 'expanding' # contracted before the image loaded - prev = post.nodes.root.getBoundingClientRect() post.file.isExpanded = true + unless post.nodes.root.parentNode + # Image might start/finish loading before the post is inserted. + # Don't scroll when it's expanded in a QP for example. + $.addClass post.nodes.root, 'expanded-image' + $.rmClass post.file.thumb, 'expanding' + return + prev = post.nodes.root.getBoundingClientRect() $.queueTask -> $.addClass post.nodes.root, 'expanded-image' $.rmClass post.file.thumb, 'expanding' From a3c370df6607e6259dcb36b9f6dd8d634d923570 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Wed, 3 Apr 2013 22:22:31 +0200 Subject: [PATCH 071/104] I won't need to build when releasing. --- Gruntfile.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gruntfile.js b/Gruntfile.js index 8dabe7cd4..4fee1fb2e 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -158,7 +158,7 @@ module.exports = function(grunt) { 'clean:tmp' ]); - grunt.registerTask('release', ['build', 'exec:commit', 'exec:push', 'compress:crx']); + grunt.registerTask('release', ['exec:commit', 'exec:push', 'compress:crx']); grunt.registerTask('patch', ['bump', 'updcl:3']); grunt.registerTask('minor', ['bump:minor', 'updcl:2']); grunt.registerTask('major', ['bump:major', 'updcl:1']); From f49a4b6c8733ba613926de13dfe498ce9303586f Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Thu, 4 Apr 2013 13:39:44 +0200 Subject: [PATCH 072/104] This should fix the 'unhide all' button not clearing threads. #968 --- src/features.coffee | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/features.coffee b/src/features.coffee index c539a6b4e..54cafe74d 100644 --- a/src/features.coffee +++ b/src/features.coffee @@ -362,7 +362,10 @@ Settings = button.textContent = "Hidden: #{hiddenNum}" $.on button, 'click', -> @textContent = 'Hidden: 0' - $.delete ['hiddenThreads', 'hiddenPosts'] + $.get 'hiddenThreads', boards: {}, (item) -> + for boardID of item.hiddenThreads.boards + localStorage.removeItem "4chan-hide-t-#{boardID}" + $.delete ['hiddenThreads', 'hiddenPosts'] $.after $('input[name="Stubs"]', section).parentNode.parentNode, div export: (now, data) -> unless typeof now is 'number' From 8bae9739314296991437090c3ae4f9661dfb8729 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Thu, 4 Apr 2013 13:44:25 +0200 Subject: [PATCH 073/104] Typo. Fix error mentioned by @saxamaphone69 in #968. --- lib/ui.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ui.coffee b/lib/ui.coffee index 3985b8af6..14b8fe025 100644 --- a/lib/ui.coffee +++ b/lib/ui.coffee @@ -102,7 +102,7 @@ UI = do -> $.rm currentMenu currentMenu = null lastToggledButton = null - $.off d, 'click, CloseMenu', @close + $.off d, 'click CloseMenu', @close findNextEntry: (entry, direction) -> entries = [entry.parentNode.children...] From a0c4d86deeb9e70c9722c4f69e48c0fbdd775881 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Thu, 4 Apr 2013 23:05:30 +0200 Subject: [PATCH 074/104] Delete entire boards from DataBoards when cleaning it gives us a 404. This would happen when a board gets deleted, like /s4s/ hopefully one day. --- src/databoard.coffee | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/databoard.coffee b/src/databoard.coffee index 3323ac97d..26743c68f 100644 --- a/src/databoard.coffee +++ b/src/databoard.coffee @@ -65,14 +65,18 @@ class DataBoard $.set @key, @data ajaxClean: (boardID) -> $.ajax "//api.4chan.org/#{boardID}/threads.json", onload: (e) => - board = @data.boards[boardID] - threads = {} - for page in JSON.parse e.target.response - for thread in page.threads - if thread.no of board - threads[thread.no] = board[thread.no] - @data.boards[boardID] = threads - @deleteIfEmpty {boardID} + if e.target.status is 404 + # Deleted board. + @delete boardID + else if e.target.status is 200 + board = @data.boards[boardID] + threads = {} + for page in JSON.parse e.target.response + for thread in page.threads + if thread.no of board + threads[thread.no] = board[thread.no] + @data.boards[boardID] = threads + @deleteIfEmpty {boardID} $.set @key, @data onSync: (data) -> From cd82bce655b6195551cba8d0a0fbd503b933e8ca Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Thu, 4 Apr 2013 23:51:17 +0200 Subject: [PATCH 075/104] Clean the catalog manually. Uuuuuuggghhhhh. --- lib/$.coffee | 7 ++++--- src/features.coffee | 24 ++++++++++++++++++++++-- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/lib/$.coffee b/lib/$.coffee index e05c66c79..a63573fbd 100644 --- a/lib/$.coffee +++ b/lib/$.coffee @@ -61,12 +61,13 @@ $.extend $, else req.callbacks.push cb return + rm = -> delete reqs[url] req = $.ajax url, - onload: -> + onload: -> cb.call @ for cb in @callbacks delete @callbacks - onabort: -> delete reqs[url] - onerror: -> delete reqs[url] + onabort: rm + onerror: rm req.callbacks = [cb] reqs[url] = req cb: diff --git a/src/features.coffee b/src/features.coffee index 54cafe74d..1e2c981d5 100644 --- a/src/features.coffee +++ b/src/features.coffee @@ -1071,7 +1071,7 @@ ThreadHiding = return if g.VIEW isnt 'index' or !Conf['Thread Hiding'] and !Conf['Thread Hiding Link'] @db = new DataBoard 'hiddenThreads' - @syncFromCatalog() + @syncCatalog() Thread::callbacks.push name: 'Thread Hiding' cb: @node @@ -1082,7 +1082,7 @@ ThreadHiding = return unless Conf['Thread Hiding'] $.prepend @OP.nodes.root, ThreadHiding.makeButton @, 'hide' - syncFromCatalog: -> + syncCatalog: -> # Sync hidden threads from the catalog into the index. hiddenThreads = ThreadHiding.db.get boardID: g.BOARD.ID @@ -1099,10 +1099,30 @@ ThreadHiding = unless threadID of hiddenThreadsOnCatalog delete hiddenThreads[threadID] + if (ThreadHiding.db.data.lastChecked or 0) > Date.now() - $.MINUTE + # Was cleaned just now. + ThreadHiding.cleanCatalog() + ThreadHiding.db.set boardID: g.BOARD.ID val: hiddenThreads + cleanCatalog: -> + # We need to clean hidden threads on the catalog ourselves, + # otherwise if we don't visit the catalog regularly + # it will pollute the localStorage and our data. + $.ajax "//api.4chan.org/#{g.BOARD}/threads.json", onload: (e) -> + return unless @status is 200 + threads = {} + for page in JSON.parse e.target.response + for thread in page.threads + if thread.no of hiddenThreadsOnCatalog + threads[thread.no] = hiddenThreadsOnCatalog[thread.no] + if Object.keys(threads).length + localStorage.setItem "4chan-hide-t-#{g.BOARD}", threads + else + localStorage.removeItem "4chan-hide-t-#{g.BOARD}" + menu: init: -> return if g.VIEW isnt 'index' or !Conf['Menu'] or !Conf['Thread Hiding Link'] From c79bb318b24ec00e2cb3c0bfb592dd0d3ad41bd3 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Fri, 5 Apr 2013 00:16:42 +0200 Subject: [PATCH 076/104] Cache requests to threads.json. --- lib/$.coffee | 4 ++-- src/databoard.coffee | 2 +- src/features.coffee | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/$.coffee b/lib/$.coffee index a63573fbd..30f26b20d 100644 --- a/lib/$.coffee +++ b/lib/$.coffee @@ -63,8 +63,8 @@ $.extend $, return rm = -> delete reqs[url] req = $.ajax url, - onload: -> - cb.call @ for cb in @callbacks + onload: (e) -> + cb.call @, e for cb in @callbacks delete @callbacks onabort: rm onerror: rm diff --git a/src/databoard.coffee b/src/databoard.coffee index 26743c68f..0e41af492 100644 --- a/src/databoard.coffee +++ b/src/databoard.coffee @@ -64,7 +64,7 @@ class DataBoard $.set @key, @data ajaxClean: (boardID) -> - $.ajax "//api.4chan.org/#{boardID}/threads.json", onload: (e) => + $.cache "//api.4chan.org/#{boardID}/threads.json", (e) => if e.target.status is 404 # Deleted board. @delete boardID diff --git a/src/features.coffee b/src/features.coffee index 1e2c981d5..0c7ddba84 100644 --- a/src/features.coffee +++ b/src/features.coffee @@ -1111,10 +1111,10 @@ ThreadHiding = # We need to clean hidden threads on the catalog ourselves, # otherwise if we don't visit the catalog regularly # it will pollute the localStorage and our data. - $.ajax "//api.4chan.org/#{g.BOARD}/threads.json", onload: (e) -> + $.cache "//api.4chan.org/#{g.BOARD}/threads.json", -> return unless @status is 200 threads = {} - for page in JSON.parse e.target.response + for page in JSON.parse @response for thread in page.threads if thread.no of hiddenThreadsOnCatalog threads[thread.no] = hiddenThreadsOnCatalog[thread.no] From caa79b5089b491d809a9dc20810e205ff67f470d Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Fri, 5 Apr 2013 01:26:48 +0200 Subject: [PATCH 077/104] Expand at least comments when the thread itself cannot be expanded. #968 --- src/features.coffee | 64 ++++++++++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 27 deletions(-) diff --git a/src/features.coffee b/src/features.coffee index 0c7ddba84..11fad7909 100644 --- a/src/features.coffee +++ b/src/features.coffee @@ -1101,13 +1101,13 @@ ThreadHiding = if (ThreadHiding.db.data.lastChecked or 0) > Date.now() - $.MINUTE # Was cleaned just now. - ThreadHiding.cleanCatalog() + ThreadHiding.cleanCatalog hiddenThreadsOnCatalog ThreadHiding.db.set boardID: g.BOARD.ID val: hiddenThreads - cleanCatalog: -> + cleanCatalog: (hiddenThreadsOnCatalog) -> # We need to clean hidden threads on the catalog ourselves, # otherwise if we don't visit the catalog regularly # it will pollute the localStorage and our data. @@ -3505,36 +3505,45 @@ ExpandThread = toggle: (thread) -> threadRoot = thread.OP.nodes.root.parentNode - url = "//api.4chan.org/#{thread.board}/res/#{thread}.json" - a = $ '.summary', threadRoot + a = $ '.summary', threadRoot - text = a.textContent - switch text[0] - when '+' - a.textContent = text.replace '+', '× Loading...' - $.cache url, -> ExpandThread.parse @, thread, a + + switch thread.isExpanded + when false, undefined + thread.isExpanded = 'loading' for post in $$ '.thread > .postContainer', threadRoot ExpandComment.expand Get.postFromRoot post + unless a + thread.isExpanded = true + return + thread.isExpanded = 'loading' + a.textContent = a.textContent.replace '+', '× Loading...' + $.cache "//api.4chan.org/#{thread.board}/res/#{thread}.json", -> + ExpandThread.parse @, thread, a - when '×' - a.textContent = text.replace '× Loading...', '+' + when 'loading' + thread.isExpanded = false + return unless a + a.textContent = a.textContent.replace '× Loading...', '+' - when '-' - a.textContent = text.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 + 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 ExpandComment.contract Get.postFromRoot post return @@ -3547,6 +3556,7 @@ ExpandThread = $.off a, 'click', ExpandThread.cb.toggle return + thread.isExpanded = true a.textContent = a.textContent.replace '× Loading...', '-' posts = JSON.parse(req.response).posts From 95c330f3024a2249d1e0727cee615e9da29d9319 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Fri, 5 Apr 2013 01:29:57 +0200 Subject: [PATCH 078/104] Decrease the "4chan X has been updated" notification lifetime down to 30 seconds. --- src/features.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/features.coffee b/src/features.coffee index 11fad7909..642410391 100644 --- a/src/features.coffee +++ b/src/features.coffee @@ -223,7 +223,7 @@ Settings = changelog = "<%= meta.repo %>blob/<%= meta.mainBranch %>/CHANGELOG.md##{g.VERSION.replace(/\./g, '')}" el = $.el 'span', innerHTML: "<%= meta.name %> has been updated to version #{g.VERSION}." - new Notification 'info', el, 60 + new Notification 'info', el, 30 else $.on d, '4chanXInitFinished', Settings.open $.set From cd4f789562b5220844ec754a1051c89b5f6a3c05 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Fri, 5 Apr 2013 22:30:06 +0200 Subject: [PATCH 079/104] Alert Firefox users if they didn't set up flexbox correctly. --- src/features.coffee | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/features.coffee b/src/features.coffee index 642410391..44e88704d 100644 --- a/src/features.coffee +++ b/src/features.coffee @@ -212,6 +212,16 @@ Settings = open: -> Conf['Enable 4chan\'s Extension'] $.get 'previousversion', null, (item) -> + <% if (type === 'userscript') { %> + el = $.el 'span' + el.style.flex = 'test' + if el.style.flex is 'test' + el.innerHTML = """ + Firefox is not correctly set up and some <%= meta.name %> features will be displayed incorrectly.
+ Follow the instructions of the install guide to fix it. + """ + new Notification 'warning', el, 30 + <% } %> if previous = item['previousversion'] return if previous is g.VERSION # Avoid conflicts between sync'd newer versions From e8659c6514f41208fa0cea0473900cdf8d9c8d98 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Fri, 5 Apr 2013 22:35:52 +0200 Subject: [PATCH 080/104] .com -> .io --- 4chan_x.user.js | 8 ++++---- Cakefile | 2 +- readme.md | 2 +- script.coffee | 6 +++--- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/4chan_x.user.js b/4chan_x.user.js index 456ed5db7..e38e3a301 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -26,7 +26,7 @@ * * Copyright (c) 2009-2011 James Campos * Copyright (c) 2012-2013 Nicolas Stepien - * http://mayhemydg.github.com/4chan-x/ + * http://mayhemydg.github.io/4chan-x/ * 4chan X 2.39.2 * * Permission is hereby granted, free of charge, to any person @@ -2722,9 +2722,9 @@ className: 'reply dialog', innerHTML: '
\ \
\ \ @@ -5766,7 +5766,7 @@ } catch (_error) { err = _error; if (notify) { - alert("4chan X (" + Main.version + ") error: " + err.message + "\nReport the bug at mayhemydg.github.com/4chan-x/#bug-report\n\nURL: " + window.location + "\n" + err.stack); + alert("4chan X (" + Main.version + ") error: " + err.message + "\nReport the bug at mayhemydg.github.io/4chan-x/#bug-report\n\nURL: " + window.location + "\n" + err.stack); } } } diff --git a/Cakefile b/Cakefile index 94ad020ee..29db36039 100644 --- a/Cakefile +++ b/Cakefile @@ -33,7 +33,7 @@ HEADER = """ * * Copyright (c) 2009-2011 James Campos * Copyright (c) 2012-2013 Nicolas Stepien - * http://mayhemydg.github.com/4chan-x/ + * http://mayhemydg.github.io/4chan-x/ * 4chan X #{VERSION} * * Permission is hereby granted, free of charge, to any person diff --git a/readme.md b/readme.md index c6012445a..9080a7307 100644 --- a/readme.md +++ b/readme.md @@ -1,4 +1,4 @@ -# Get 4chan X [HERE](http://mayhemydg.github.com/4chan-x/). +# Get 4chan X [HERE](http://mayhemydg.github.io/4chan-x/). # Building diff --git a/script.coffee b/script.coffee index 225c70bbc..3306f9a97 100644 --- a/script.coffee +++ b/script.coffee @@ -2170,9 +2170,9 @@ Options = className: 'reply dialog' innerHTML: '
@@ -4699,7 +4699,7 @@ Main = try callback node for node in nodes catch err - alert "4chan X (#{Main.version}) error: #{err.message}\nReport the bug at mayhemydg.github.com/4chan-x/#bug-report\n\nURL: #{window.location}\n#{err.stack}" if notify + alert "4chan X (#{Main.version}) error: #{err.message}\nReport the bug at mayhemydg.github.io/4chan-x/#bug-report\n\nURL: #{window.location}\n#{err.stack}" if notify return observer: (mutations) -> nodes = [] From 95427e247ee190a479d00c29706af8cee9d76fc5 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Sat, 6 Apr 2013 05:01:22 +0200 Subject: [PATCH 081/104] Add /fa/ and /s4s/ archive redirection. --- 4chan_x.user.js | 4 ++++ changelog | 2 ++ script.coffee | 4 ++-- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/4chan_x.user.js b/4chan_x.user.js index e38e3a301..b81f550ce 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -4933,7 +4933,9 @@ case 'po': return "//archive.thedarkcave.org/" + board + "/full_image/" + filename; case 'ck': + case 'fa': case 'lit': + case 's4s': return "//fuuka.warosu.org/" + board + "/full_image/" + filename; case 'cgl': case 'g': @@ -5013,7 +5015,9 @@ url = Redirect.path('//archive.thedarkcave.org', 'foolfuuka', data); break; case 'ck': + case 'fa': case 'lit': + case 's4s': url = Redirect.path('//fuuka.warosu.org', 'fuuka', data); break; case 'diy': diff --git a/changelog b/changelog index 66c5e5205..a16722e7a 100644 --- a/changelog +++ b/changelog @@ -1,4 +1,6 @@ master +- Mayhem + Add /fa/ and /s4s/ archive redirection. 2.39.2 - Mayhem diff --git a/script.coffee b/script.coffee index 3306f9a97..16e019089 100644 --- a/script.coffee +++ b/script.coffee @@ -4086,7 +4086,7 @@ Redirect = "//nsfw.foolz.us/#{board}/full_image/#{filename}" when 'po' "//archive.thedarkcave.org/#{board}/full_image/#{filename}" - when 'ck', 'lit' + when 'ck', 'fa', 'lit', 's4s' "//fuuka.warosu.org/#{board}/full_image/#{filename}" when 'cgl', 'g', 'mu', 'w' "//rbt.asia/#{board}/full_image/#{filename}" @@ -4113,7 +4113,7 @@ Redirect = url = Redirect.path '//nsfw.foolz.us', 'foolfuuka', data when 'int', 'out', 'po' url = Redirect.path '//archive.thedarkcave.org', 'foolfuuka', data - when 'ck', 'lit' + when 'ck', 'fa', 'lit', 's4s' url = Redirect.path '//fuuka.warosu.org', 'fuuka', data when 'diy', 'sci' url = Redirect.path '//archive.installgentoo.net', 'fuuka', data From 5a04bba12dabbdbfb3842eb67a7182c7618ca955 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Sat, 6 Apr 2013 05:25:44 +0200 Subject: [PATCH 082/104] Update deps. --- CONTRIBUTING.md | 2 +- package.json | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b6ccc85bc..685ca0727 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -21,7 +21,7 @@ Open your console with: - Install [Grunt's CLI](http://gruntjs.com/) with `npm install -g grunt-cli`. - Clone 4chan X. - `cd` into it. -- Install 4chan X dependencies with `npm install`. +- Install/Update 4chan X dependencies with `npm install`. ### Build diff --git a/package.json b/package.json index e7800d59c..56234377a 100644 --- a/package.json +++ b/package.json @@ -19,10 +19,10 @@ "grunt": "~0.4.1", "grunt-bump": "~0.0.0", "grunt-contrib-clean": "~0.4.0", - "grunt-contrib-coffee": "~0.6.4", - "grunt-contrib-compress": "~0.4.5", + "grunt-contrib-coffee": "~0.6.5", + "grunt-contrib-compress": "~0.4.7", "grunt-contrib-concat": "~0.1.3", - "grunt-contrib-copy": "~0.4.0", + "grunt-contrib-copy": "~0.4.1", "grunt-contrib-watch": "~0.3.1", "grunt-exec": "~0.4.0" }, From b09f7907679510d75e0c4b439fba055e7f52ff0e Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Sun, 7 Apr 2013 21:41:31 +0200 Subject: [PATCH 083/104] POST caught errors to me. --- lib/$.coffee | 4 ++-- src/features.coffee | 1 - src/main.coffee | 34 ++++++++++++++++++++++++---------- 3 files changed, 26 insertions(+), 13 deletions(-) diff --git a/lib/$.coffee b/lib/$.coffee index 30f26b20d..0bd4842b4 100644 --- a/lib/$.coffee +++ b/lib/$.coffee @@ -41,10 +41,10 @@ $.extend $, fd.append key, val fd ajax: (url, callbacks, opts={}) -> - {type, headers, upCallbacks, form} = opts + {type, headers, upCallbacks, form, sync} = opts r = new XMLHttpRequest() type or= form and 'post' or 'get' - r.open type, url, true + r.open type, url, if sync then false else true for key, val of headers r.setRequestHeader key, val $.extend r, callbacks diff --git a/src/features.coffee b/src/features.coffee index af0736699..4c7c48d85 100644 --- a/src/features.coffee +++ b/src/features.coffee @@ -3517,7 +3517,6 @@ ExpandThread = threadRoot = thread.OP.nodes.root.parentNode a = $ '.summary', threadRoot - switch thread.isExpanded when false, undefined thread.isExpanded = 'loading' diff --git a/src/main.coffee b/src/main.coffee index 589806344..8b81e1732 100644 --- a/src/main.coffee +++ b/src/main.coffee @@ -527,7 +527,7 @@ Main = new Notification 'info', el, 120 handleErrors: (errors) -> - unless 'length' of errors + unless errors instanceof Array error = errors else if errors.length is 1 error = errors[0] @@ -538,12 +538,10 @@ Main = div = $.el 'div', innerHTML: "#{errors.length} errors occurred. [show]" $.on div.lastElementChild, 'click', -> - if @textContent is 'show' - @textContent = 'hide' - logs.hidden = false + [@textContent, logs.hidden] = if @textContent is 'show' + ['hide', false] else - @textContent = 'show' - logs.hidden = true + ['show', true] logs = $.el 'div', hidden: true @@ -553,14 +551,30 @@ Main = new Notification 'error', [div, logs], 30 parseError: (data) -> - {message, error} = data - c.error message, error.stack + Main.logError data message = $.el 'div', - textContent: message + textContent: data.message error = $.el 'div', - textContent: error + textContent: data.error [message, error] + errors: [] + logError: (data) -> + unless Main.errors.length + $.on window, 'unload', Main.postErrors + c.error data.message, data.error.stack + Main.errors.push data + + postErrors: -> + errors = Main.errors.map (d) -> d.message + ' ' + d.error.stack + $.ajax '<%= meta.page %>errors', {}, + sync: true + form: $.formData + n: "<%= meta.name %> v#{g.VERSION}" + t: '<%= type %>' + ua: window.navigator.userAgent + e: errors.join '\n' + isThisPageLegit: -> # 404 error page or similar. unless 'thisPageIsLegit' of Main From a5c228fda8365dcc1c883cb55aba50da79e6e0fe Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Sun, 7 Apr 2013 21:46:04 +0200 Subject: [PATCH 084/104] !sync --- lib/$.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/$.coffee b/lib/$.coffee index 0bd4842b4..66211aefa 100644 --- a/lib/$.coffee +++ b/lib/$.coffee @@ -44,7 +44,7 @@ $.extend $, {type, headers, upCallbacks, form, sync} = opts r = new XMLHttpRequest() type or= form and 'post' or 'get' - r.open type, url, if sync then false else true + r.open type, url, !sync for key, val of headers r.setRequestHeader key, val $.extend r, callbacks From 30c76eb81d6732e5d57a828fc9680655b61aee92 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Sun, 7 Apr 2013 21:54:18 +0200 Subject: [PATCH 085/104] Don't send me your credentials when you POST errors thank you. --- lib/$.coffee | 4 ++-- src/features.coffee | 1 + src/qr.coffee | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/$.coffee b/lib/$.coffee index 66211aefa..4b2e3d293 100644 --- a/lib/$.coffee +++ b/lib/$.coffee @@ -41,7 +41,7 @@ $.extend $, fd.append key, val fd ajax: (url, callbacks, opts={}) -> - {type, headers, upCallbacks, form, sync} = opts + {type, cred, headers, upCallbacks, form, sync} = opts r = new XMLHttpRequest() type or= form and 'post' or 'get' r.open type, url, !sync @@ -49,7 +49,7 @@ $.extend $, r.setRequestHeader key, val $.extend r, callbacks $.extend r.upload, upCallbacks - r.withCredentials = type is 'post' + r.withCredentials = cred r.send form r cache: do -> diff --git a/src/features.coffee b/src/features.coffee index 4c7c48d85..b5cf3a9d4 100644 --- a/src/features.coffee +++ b/src/features.coffee @@ -1602,6 +1602,7 @@ DeleteLink = onload: -> DeleteLink.load link, post, @response onerror: -> DeleteLink.error link }, { + cred: true form: $.formData form } load: (link, post, html) -> diff --git a/src/qr.coffee b/src/qr.coffee index a4aad9201..94fff3323 100644 --- a/src/qr.coffee +++ b/src/qr.coffee @@ -895,6 +895,7 @@ QR = QR.error $.el 'span', innerHTML: 'Connection error. You may have been banned.' opts = + cred: true form: $.formData postData upCallbacks: onload: -> From 060dc71e4bfe9622b7b6ae7f97890888f828fe5d Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Sun, 7 Apr 2013 21:58:53 +0200 Subject: [PATCH 086/104] less {} --- src/features.coffee | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/features.coffee b/src/features.coffee index b5cf3a9d4..0ea03af9a 100644 --- a/src/features.coffee +++ b/src/features.coffee @@ -1598,13 +1598,12 @@ DeleteLink = form[post.ID] = 'delete' link = @ - $.ajax $.id('delform').action.replace("/#{g.BOARD}/", "/#{post.board}/"), { + $.ajax $.id('delform').action.replace("/#{g.BOARD}/", "/#{post.board}/"), onload: -> DeleteLink.load link, post, @response onerror: -> DeleteLink.error link - }, { + , cred: true form: $.formData form - } load: (link, post, html) -> tmpDoc = d.implementation.createHTMLDocument '' tmpDoc.documentElement.innerHTML = html From 0887fd146447cad057d1bfdbdaac53c106aa9b5d Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Sun, 7 Apr 2013 22:04:52 +0200 Subject: [PATCH 087/104] POST me the URL where you caught the errors too. --- src/main.coffee | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main.coffee b/src/main.coffee index 8b81e1732..02650cce5 100644 --- a/src/main.coffee +++ b/src/main.coffee @@ -572,7 +572,8 @@ Main = form: $.formData n: "<%= meta.name %> v#{g.VERSION}" t: '<%= type %>' - ua: window.navigator.userAgent + ua: window.navigator.userAgent + url: window.location.href e: errors.join '\n' isThisPageLegit: -> From cdb9b5575ea70ce096dd33b550732048c47212fc Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Sun, 7 Apr 2013 23:34:22 +0200 Subject: [PATCH 088/104] Close #837. --- css/style.css | 5 +++-- src/qr.coffee | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/css/style.css b/css/style.css index 10cf4c06d..7cb211f83 100644 --- a/css/style.css +++ b/css/style.css @@ -497,8 +497,9 @@ a[href="javascript:;"] { } /* QR */ -.hide-original-post-form #postForm, -.hide-original-post-form .postingMode, +:root.hide-original-post-form #postForm, +:root.hide-original-post-form .postingMode, +:root.hide-original-post-form #togglePostForm, #qr.autohide:not(:hover) > form { display: none; } diff --git a/src/qr.coffee b/src/qr.coffee index 94fff3323..15fc6ca6e 100644 --- a/src/qr.coffee +++ b/src/qr.coffee @@ -1,6 +1,6 @@ QR = init: -> - return if g.VIEW is 'catalog' or !Conf['Quick Reply'] + return if !Conf['Quick Reply'] @db = new DataBoard 'yourPosts' From 41eaac9217ea071ee7905244fcbad964d3128a9a Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Sun, 7 Apr 2013 23:59:51 +0200 Subject: [PATCH 089/104] Release 4chan X v3.0.0. --- CHANGELOG.md | 2 +- Gruntfile.js | 4 ++-- package.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ccc588df..c623ec2ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -# 3.0.0 +# 3.0.0 - *2013-04-07* **Major rewrite of 4chan X.** diff --git a/Gruntfile.js b/Gruntfile.js index 4fee1fb2e..f08c0bea6 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -166,8 +166,8 @@ module.exports = function(grunt) { // Update the `pkg` object with the new version. pkg = grunt.file.readJSON('package.json'); // i is the number of #s for markdown. - var version = new Array(+i + 1).join('#') + ' ' + pkg.version + ' *(' + grunt.template.today('yyyy-mm-dd') + ')*'; - grunt.file.write('CHANGELOG.md', version + '\n' + grunt.file.read('CHANGELOG.md')); + 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 56234377a..7196c6890 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "version": "3.0.0", "description": "Cross-browser extension for productive lurking on 4chan.", "meta": { - "name": "4chan X Beta", + "name": "4chan X", "repo": "https://github.com/MayhemYDG/4chan-x/", "page": "https://4chan-x.just-believe.in/", "buildsPath": "builds/", From 8a8231a4c7653b48d41f3a243179ef587c407ff6 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Mon, 8 Apr 2013 00:48:57 +0200 Subject: [PATCH 090/104] Release after updating. --- Gruntfile.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index f08c0bea6..7b3be72bf 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -158,10 +158,10 @@ module.exports = function(grunt) { 'clean:tmp' ]); - grunt.registerTask('release', ['exec:commit', 'exec:push', 'compress:crx']); - grunt.registerTask('patch', ['bump', 'updcl:3']); - grunt.registerTask('minor', ['bump:minor', 'updcl:2']); - grunt.registerTask('major', ['bump:major', 'updcl:1']); + grunt.registerTask('release', ['exec:commit', 'exec: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']); grunt.registerTask('updcl', 'Update the changelog', function(i) { // Update the `pkg` object with the new version. pkg = grunt.file.readJSON('package.json'); From 996878b509ba4e92c55f8ec7eb57d08d809df00f Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Mon, 8 Apr 2013 01:20:15 +0200 Subject: [PATCH 091/104] Add a notice that v2 is outdated. --- 4chan_x.user.js | 15 +++++++++++---- script.coffee | 12 +++++++++--- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/4chan_x.user.js b/4chan_x.user.js index b81f550ce..0d93894a9 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -2690,7 +2690,7 @@ return $.ready(Options.initReady); }, initReady: function() { - var a, setting, settings, _i, _len, _ref; + var a, notice, setting, settings, _i, _len, _ref; _ref = ['navtopright', 'navbotright']; for (_i = 0, _len = _ref.length; _i < _len; _i++) { settings = _ref[_i]; @@ -2702,10 +2702,17 @@ $.on(a, 'click', Options.dialog); setting = $.id(settings); if (Conf['Disable 4chan\'s extension']) { - $.replace(setting.childNodes[1], a); - continue; + $.replace(setting.firstElementChild, a); + } else { + $.prepend(setting, [$.tn('['), a, $.tn('] ')]); } - $.prepend(setting, [$.tn('['), a, $.tn('] ')]); + notice = $.el('a', { + textContent: 'v2 is outdated.', + href: 'https://4chan-x.just-believe.in/', + target: '_blank' + }); + notice.style.color = 'red'; + $.prepend(setting, [$.tn('['), notice, $.tn('] ')]); } if (!$.get('firstrun')) { $.set('firstrun', true); diff --git a/script.coffee b/script.coffee index 16e019089..d9111b150 100644 --- a/script.coffee +++ b/script.coffee @@ -2155,9 +2155,15 @@ Options = $.on a, 'click', Options.dialog setting = $.id settings if Conf['Disable 4chan\'s extension'] - $.replace setting.childNodes[1], a - continue - $.prepend setting, [$.tn('['), a, $.tn('] ')] + $.replace setting.firstElementChild, a + else + $.prepend setting, [$.tn('['), a, $.tn('] ')] + notice = $.el 'a', + textContent: 'v2 is outdated.' + href: 'https://4chan-x.just-believe.in/' + target: '_blank' + notice.style.color = 'red' + $.prepend setting, [$.tn('['), notice, $.tn('] ')] unless $.get 'firstrun' $.set 'firstrun', true # Prevent race conditions From aafe0fcc16d84c4df62b61feb4c5c2f86846bf4c Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Mon, 8 Apr 2013 01:22:17 +0200 Subject: [PATCH 092/104] Release 2.39.3. --- 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 0d93894a9..082a82d61 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -1,6 +1,6 @@ // ==UserScript== // @name 4chan x -// @version 2.39.2 +// @version 2.39.3 // @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.2 + * 4chan X 2.39.3 * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -5822,7 +5822,7 @@ return $.globalEval(("(" + code + ")()").replace('_id_', bq.id)); }, namespace: '4chan_x.', - version: '2.39.2', + version: '2.39.3', callbacks: [], css: '\ /* dialog styling */\ diff --git a/Cakefile b/Cakefile index 29db36039..a96ed378a 100644 --- a/Cakefile +++ b/Cakefile @@ -2,7 +2,7 @@ {exec} = require 'child_process' fs = require 'fs' -VERSION = '2.39.2' +VERSION = '2.39.3' HEADER = """ // ==UserScript== diff --git a/changelog b/changelog index a16722e7a..668d9c853 100644 --- a/changelog +++ b/changelog @@ -1,4 +1,6 @@ master + +2.39.3 - Mayhem Add /fa/ and /s4s/ archive redirection. diff --git a/latest.js b/latest.js index bb4c76a77..deefc42d9 100644 --- a/latest.js +++ b/latest.js @@ -1 +1 @@ -postMessage({version:'2.39.2'},'*') \ No newline at end of file +postMessage({version:'2.39.3'},'*') \ No newline at end of file diff --git a/script.coffee b/script.coffee index d9111b150..96cb6bb09 100644 --- a/script.coffee +++ b/script.coffee @@ -4728,7 +4728,7 @@ Main = $.globalEval "(#{code})()".replace '_id_', bq.id namespace: '4chan_x.' - version: '2.39.2' + version: '2.39.3' callbacks: [] css: ' /* dialog styling */ From 06cec72477388cba1d15f8a9a4aff41712c8a0dc Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Mon, 8 Apr 2013 01:28:43 +0200 Subject: [PATCH 093/104] Changelog that v3 supports thread creation in the catalog. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c623ec2ca..8fbbf6177 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ Quick Reply changes: - Closing the QR while uploading will abort the upload and won't close the QR anymore. - Creating threads outside of the index is now possible. - Selection-to-quote also applies to selected text inside the post, not just inside the comment. + - Added support for thread creation in the catalog. - Added thumbnailing support for Opera. Image Expansion changes: From b71ef38569591c7b7c79043121c231ec387aefe9 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Mon, 8 Apr 2013 09:27:50 +0200 Subject: [PATCH 094/104] Fix #983 --- CHANGELOG.md | 2 ++ src/features.coffee | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8fbbf6177..59fc6440c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +- Fix #983 + # 3.0.0 - *2013-04-07* **Major rewrite of 4chan X.** diff --git a/src/features.coffee b/src/features.coffee index 0ea03af9a..1fbcc8950 100644 --- a/src/features.coffee +++ b/src/features.coffee @@ -1129,7 +1129,7 @@ ThreadHiding = if thread.no of hiddenThreadsOnCatalog threads[thread.no] = hiddenThreadsOnCatalog[thread.no] if Object.keys(threads).length - localStorage.setItem "4chan-hide-t-#{g.BOARD}", threads + localStorage.setItem "4chan-hide-t-#{g.BOARD}", JSON.stringify threads else localStorage.removeItem "4chan-hide-t-#{g.BOARD}" From 2fc572434f7280ca8e774676ad1190b625361b3a Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Mon, 8 Apr 2013 09:39:26 +0200 Subject: [PATCH 095/104] Make the thread update button fill the width. --- css/style.css | 3 +++ 1 file changed, 3 insertions(+) diff --git a/css/style.css b/css/style.css index 7cb211f83..11fbebef1 100644 --- a/css/style.css +++ b/css/style.css @@ -347,6 +347,9 @@ a[href="javascript:;"] { #updater:not(:hover) > div:not(.move) { display: none; } +#updater input[type="button"] { + width: 100%; +} .new { color: limegreen; } From ab0bc6c43a6b8df2b44f7f2c3d98092edf27a6c0 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Mon, 8 Apr 2013 09:42:20 +0200 Subject: [PATCH 096/104] Can't link issues in the changelog file. --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 59fc6440c..27e4ce5d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -- Fix #983 +- Fix Thread Hiding initialization error, you'll have to clear all hidden threads in the settings window if you keep getting this bug. # 3.0.0 - *2013-04-07* From 2e8146a92f23814520f4e89578ef79e56355eef2 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Mon, 8 Apr 2013 09:52:29 +0200 Subject: [PATCH 097/104] Might as well go all the way to fix this. --- CHANGELOG.md | 2 +- src/features.coffee | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 27e4ce5d5..9e889e623 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -- Fix Thread Hiding initialization error, you'll have to clear all hidden threads in the settings window if you keep getting this bug. +- Fix Thread Hiding initialization error. # 3.0.0 - *2013-04-07* diff --git a/src/features.coffee b/src/features.coffee index 1fbcc8950..c633b2f32 100644 --- a/src/features.coffee +++ b/src/features.coffee @@ -1097,7 +1097,12 @@ ThreadHiding = hiddenThreads = ThreadHiding.db.get boardID: g.BOARD.ID defaultValue: {} - hiddenThreadsOnCatalog = JSON.parse(localStorage.getItem "4chan-hide-t-#{g.BOARD}") or {} + # XXX tmp fix + try + hiddenThreadsOnCatalog = JSON.parse(localStorage.getItem "4chan-hide-t-#{g.BOARD}") or {} + catch e + localStorage.setItem "4chan-hide-t-#{g.BOARD}", JSON.stringify {} + return ThreadHiding.syncCatalog() # Add threads that were hidden in the catalog. for threadID of hiddenThreadsOnCatalog From e5012d6b44eeee2831c8ffee3978ef1e1c35a802 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Mon, 8 Apr 2013 10:02:02 +0200 Subject: [PATCH 098/104] Close #982 --- CHANGELOG.md | 1 + src/features.coffee | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e889e623..f7f5f96e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- Added possibility to combine board-list toggle and custom text. - Fix Thread Hiding initialization error. # 3.0.0 - *2013-04-07* diff --git a/src/features.coffee b/src/features.coffee index c633b2f32..7e12a793a 100644 --- a/src/features.coffee +++ b/src/features.coffee @@ -78,10 +78,10 @@ Header = nodes = text.match(/[\w@]+(-(all|title|full|index|catalog|text:"[^"]+"))*|[^\w@]+/g).map (t) -> if /^[^\w@]/.test t return $.tn t - if t is 'toggle-all' + if /^toggle-all/.test t a = $.el 'a', className: 'show-board-list-button' - textContent: '+' + textContent: if m = t.match /-text:"(.+)"/ then m[1] else '+' href: 'javascript:;' $.on a, 'click', Header.toggleBoardList return a From 4cf7e55301b58b6bbea6e09bf6e140eb6b24b01a Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Mon, 8 Apr 2013 10:14:46 +0200 Subject: [PATCH 099/104] Close #986 --- CHANGELOG.md | 3 ++- src/config.coffee | 1 + src/features.coffee | 12 +++++++++--- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f7f5f96e8..529c43c0b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ - Added possibility to combine board-list toggle and custom text. -- Fix Thread Hiding initialization error. +- Added Reply Navigation back in, disabled by default. +- Fixed Thread Hiding initialization error. # 3.0.0 - *2013-04-07* diff --git a/src/config.coffee b/src/config.coffee index ba0d378ab..cdc746f3f 100644 --- a/src/config.coffee +++ b/src/config.coffee @@ -11,6 +11,7 @@ Config = 'Comment Expansion': [true, 'Add buttons to expand too long comments.'] '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.'] 'Check for Updates': [true, 'Notify when updated versions of <%= meta.name %> are available.'] 'Filtering': 'Anonymize': [false, 'Make everyone Anonymous.'] diff --git a/src/features.coffee b/src/features.coffee index 7e12a793a..b57ddf1af 100644 --- a/src/features.coffee +++ b/src/features.coffee @@ -1928,7 +1928,7 @@ Keybinds = Nav = init: -> - return if g.VIEW isnt 'index' or !Conf['Index Navigation'] + return if g.VIEW is 'index' and !Conf['Index Navigation'] or g.VIEW is 'thread' and !Conf['Reply Navigation'] span = $.el 'span', id: 'navlinks' @@ -1946,10 +1946,16 @@ Nav = $.on d, '4chanXInitFinished', -> $.add d.body, span prev: -> - Nav.scroll -1 + if g.VIEW is 'thread' + window.scrollTo 0, 0 + else + Nav.scroll -1 next: -> - Nav.scroll +1 + if g.VIEW is 'thread' + window.scrollTo 0, d.body.scrollHeight + else + Nav.scroll +1 getThread: (full) -> headRect = Header.bar.getBoundingClientRect() From 06464b1bb7e958a34d53079a88d6e87dc74a4e22 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Mon, 8 Apr 2013 10:19:13 +0200 Subject: [PATCH 100/104] Less characters. You could always send PRs, @Nami-Doc --- src/features.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/features.coffee b/src/features.coffee index b57ddf1af..4c8a46b3d 100644 --- a/src/features.coffee +++ b/src/features.coffee @@ -81,7 +81,7 @@ Header = if /^toggle-all/.test t a = $.el 'a', className: 'show-board-list-button' - textContent: if m = t.match /-text:"(.+)"/ then m[1] else '+' + textContent: (t.match(/-text:"(.+)"/) || [null, '+'])[1] href: 'javascript:;' $.on a, 'click', Header.toggleBoardList return a From ccdd4b5ec38e8b4c9e0f3498a01739c2604e968f Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Mon, 8 Apr 2013 10:35:45 +0200 Subject: [PATCH 101/104] Release 4chan X v3.0.1. --- CHANGELOG.md | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 529c43c0b..0442f0ce1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +### 3.0.1 - *2013-04-08* + - Added possibility to combine board-list toggle and custom text. - Added Reply Navigation back in, disabled by default. - Fixed Thread Hiding initialization error. diff --git a/package.json b/package.json index 7196c6890..6fcb5961b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "4chan-X", - "version": "3.0.0", + "version": "3.0.1", "description": "Cross-browser extension for productive lurking on 4chan.", "meta": { "name": "4chan X", From dc50008c46760aa6cbdba1cfe3c8a58b11ac91ed Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Mon, 8 Apr 2013 12:27:51 +0200 Subject: [PATCH 102/104] git push --tags needs to be forced. --- CHANGELOG.md | 2 +- Gruntfile.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0442f0ce1..eb6637b33 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ ### 3.0.1 - *2013-04-08* -- Added possibility to combine board-list toggle and custom text. +- Added the possibility to combine board-list toggle and custom text. - Added Reply Navigation back in, disabled by default. - Fixed Thread Hiding initialization error. diff --git a/Gruntfile.js b/Gruntfile.js index 7b3be72bf..9e7acb52b 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -81,7 +81,7 @@ module.exports = function(grunt) { stdout: true }, push: { - command: 'git push origin --all && git push origin --tags', + command: 'git push origin --all && git push origin --tags -f', stdout: true } }, From 263ed8a5f93d4a3c763abb9001fec9ebe38e590b Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Mon, 8 Apr 2013 12:28:09 +0200 Subject: [PATCH 103/104] Enable OP backlinks by default. --- src/config.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.coffee b/src/config.coffee index cdc746f3f..6e9294292 100644 --- a/src/config.coffee +++ b/src/config.coffee @@ -54,7 +54,7 @@ Config = 'Hide Original Post Form': [true, 'Hide the normal post form.'] 'Quote Links': 'Quote Backlinks': [true, 'Add quote backlinks.'] - 'OP Backlinks': [false, 'Add backlinks to the OP.'] + 'OP Backlinks': [true, 'Add backlinks to the OP.'] 'Quote Inlining': [true, 'Inline quoted post on click.'] 'Forward Hiding': [true, 'Hide original posts of inlined backlinks.'] 'Quote Previewing': [true, 'Show quoted post on hover.'] From 699e680c8bd4c7c61959c847b564d5aad7f16e3e Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Mon, 8 Apr 2013 12:29:48 +0200 Subject: [PATCH 104/104] Don't add hashes to changelog links. I forgot that I add the date next to version. --- src/features.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/features.coffee b/src/features.coffee index 4c8a46b3d..afa37b8e1 100644 --- a/src/features.coffee +++ b/src/features.coffee @@ -230,7 +230,7 @@ Settings = curr = g.VERSION.match(/\d+/g).map Number return unless prev[0] <= curr[0] and prev[1] <= curr[1] and prev[2] <= curr[2] - changelog = "<%= meta.repo %>blob/<%= meta.mainBranch %>/CHANGELOG.md##{g.VERSION.replace(/\./g, '')}" + changelog = '<%= meta.repo %>blob/<%= meta.mainBranch %>/CHANGELOG.md' el = $.el 'span', innerHTML: "<%= meta.name %> has been updated to version #{g.VERSION}." new Notification 'info', el, 30 @@ -264,7 +264,7 @@ Settings =