diff --git a/src/Filtering/Anonymize.coffee b/src/Filtering/Anonymize.coffee index 2a3c211a4..56a6eef7c 100755 --- a/src/Filtering/Anonymize.coffee +++ b/src/Filtering/Anonymize.coffee @@ -1,6 +1,6 @@ Anonymize = init: -> - return if g.VIEW not in ['index', 'thread', 'archive'] or !Conf['Anonymize'] + return unless g.VIEW in ['index', 'thread', 'archive'] and Conf['Anonymize'] return @archive() if g.VIEW is 'archive' Post.callbacks.push diff --git a/src/Filtering/Filter.coffee b/src/Filtering/Filter.coffee index 0a33af9fd..0088446e9 100755 --- a/src/Filtering/Filter.coffee +++ b/src/Filtering/Filter.coffee @@ -1,7 +1,7 @@ Filter = filters: {} init: -> - return if g.VIEW not in ['index', 'thread'] or !Conf['Filter'] + return unless g.VIEW in ['index', 'thread'] and Conf['Filter'] unless Conf['Filtered Backlinks'] $.addClass doc, 'hide-backlinks' @@ -100,10 +100,8 @@ Filter = node: -> return if @isClone or @isFetchedQuote - for key of Filter.filters - value = Filter[key] @ + for key of Filter.filters when (value = Filter[key] @) isnt false # Continue if there's nothing to filter (no tripcode for example). - continue if value is false for filter in Filter.filters[key] when result = filter value, @isReply # Hide @@ -171,7 +169,7 @@ Filter = menu: init: -> - return if g.VIEW not in ['index', 'thread'] or !Conf['Menu'] or !Conf['Filter'] + return unless g.VIEW in ['index', 'thread'] and Conf['Menu'] and Conf['Filter'] div = $.el 'div', textContent: 'Filter' diff --git a/src/Filtering/Recursive.coffee b/src/Filtering/Recursive.coffee index 7567f2f61..8a8be01b3 100755 --- a/src/Filtering/Recursive.coffee +++ b/src/Filtering/Recursive.coffee @@ -22,10 +22,9 @@ Recursive = rm: (recursive, post) -> return unless obj = Recursive.recursives[post.fullID] - for rec, i in obj.recursives - if rec is recursive - obj.recursives.splice i, 1 - obj.args.splice i, 1 + for rec, i in obj.recursives when rec is recursive + obj.recursives.splice i, 1 + obj.args.splice i, 1 return apply: (recursive, post, args...) -> diff --git a/src/General/Build.coffee b/src/General/Build.coffee index bd9313124..ad60b2e53 100755 --- a/src/General/Build.coffee +++ b/src/General/Build.coffee @@ -1,13 +1,12 @@ Build = staticPath: '//s.4cdn.org/image/' gifIcon: if window.devicePixelRatio >= 2 then '@2x.gif' else '.gif' - initPixelRatio: window.devicePixelRatio spoilerRange: {} unescape: (text) -> return text unless text? text.replace(/<[^>]*>/g, '').replace /&(amp|#039|quot|lt|gt);/g, (c) -> {'&': '&', ''': "'", '"': '"', '<': '<', '>': '>'}[c] - shortFilename: (filename, isReply) -> + shortFilename: (filename) -> threshold = 30 ext = filename.match(/\.?[^\.]*$/)[0] if filename.length - ext.length > threshold @@ -15,8 +14,8 @@ Build = else filename thumbRotate: do -> - n = 0 - -> n = (n + 1) % 2 + t = 0 + -> t = (if t then 0 else 1) sameThread: (boardID, threadID) -> g.VIEW is 'thread' and g.BOARD.ID is boardID and g.THREADID is +threadID postURL: (boardID, threadID, postID) -> @@ -83,8 +82,7 @@ Build = name or= '' subject or= '' isOP = postID is threadID - - retina = if Build.initPixelRatio >= 2 then '@2x' else '' + {staticPath, gifIcon} = Build ### Name Block ### @@ -92,19 +90,19 @@ Build = when 'admin', 'admin_highlight' capcodeClass = ' capcodeAdmin' capcodeStart = <%= html(' ## Admin') %> - capcodeIcon = <%= html(' Admin Icon') %> + capcodeIcon = <%= html(' Admin Icon') %> when 'mod' capcodeClass = ' capcodeMod' capcodeStart = <%= html(' ## Mod') %> - capcodeIcon = <%= html(' Mod Icon') %> + capcodeIcon = <%= html(' Mod Icon') %> when 'developer' capcodeClass = ' capcodeDeveloper' capcodeStart = <%= html(' ## Developer') %> - capcodeIcon = <%= html(' Developer Icon') %> + capcodeIcon = <%= html(' Developer Icon') %> when 'manager' capcodeClass = ' capcodeManager' capcodeStart = <%= html(' ## Manager') %> - capcodeIcon = <%= html(' Manager Icon') %> + capcodeIcon = <%= html(' Manager Icon') %> else capcodeClass = '' capcodeStart = <%= html('') %> @@ -155,7 +153,7 @@ Build = icons = for type in ['Sticky', 'Closed', 'Archived'] when o["is#{type}"] and !(type is 'Closed' and o.isArchived) typeLC = type.toLowerCase() - <%= html(' ${type}') %> + <%= html(' ${type}') %> replyLink = if isOP and g.VIEW is 'index' <%= html('   [Reply]') %> @@ -181,7 +179,7 @@ Build = fileCont = if file?.isDeleted <%= html( '' + - 'File deleted.' + + 'File deleted.' + '' ) %> else if file and boardID is 'f' @@ -196,9 +194,9 @@ Build = shortFilename = 'Spoiler Image' if spoilerRange = Build.spoilerRange[boardID] # Randomize the spoiler image. - fileThumb = "//s.4cdn.org/image/spoiler-#{boardID}#{Math.floor 1 + spoilerRange * Math.random()}.png" + fileThumb = "#{staticPath}spoiler-#{boardID}#{Math.floor 1 + spoilerRange * Math.random()}.png" else - fileThumb = '//s.4cdn.org/image/spoiler.png' + fileThumb = "#{staticPath}spoiler.png" file.twidth = file.theight = 100 else shortFilename = Build.shortFilename file.name, !isOP diff --git a/src/General/Config.coffee b/src/General/Config.coffee index 22e8f798b..cf1a3c681 100755 --- a/src/General/Config.coffee +++ b/src/General/Config.coffee @@ -53,7 +53,7 @@ Config = ] 'Comment Expansion': [ true - 'Add buttons to expand too long comments.' + 'Expand Comments that are too long to display on the index. Not applicable with JSON Navigation.' ] 'File Info Formatting': [ true @@ -118,6 +118,11 @@ Config = true 'Convert text into links where applicable.' ] + 'Link Title': [ + true + 'Replace the link of a supported site with its actual title. Currently Supported: YouTube, Vimeo, SoundCloud, and Github gists' + 1 + ] 'Embedding': [ true 'Embed supported services. Note: Some services don\'t work on HTTPS.' @@ -133,11 +138,6 @@ Config = 'Embed content in a frame that remains in place when the page is scrolled.' 2 ] - 'Link Title': [ - true - 'Replace the link of a supported site with its actual title. Currently Supported: YouTube, Vimeo, SoundCloud, and Github gists' - 1 - ] 'Filtering': 'Anonymize': [ @@ -384,8 +384,8 @@ Config = 'All-in-one form to reply, create threads, automate dumping and more.' ] 'QR Shortcut': [ - true, - 'Adds a small [QR] link in the header.' + true + 'Add a shortcut to the header to toggle the QR.' 1 ] 'Persistent QR': [ @@ -598,7 +598,7 @@ Config = ] 'Auto Prune': [ false - 'Automatically prune dead threads.' + 'Automatically remove dead threads.' ] 'Show Unread Count': [ true diff --git a/src/General/Globals.coffee b/src/General/Globals.coffee index 2f6033615..ad31a194a 100755 --- a/src/General/Globals.coffee +++ b/src/General/Globals.coffee @@ -9,6 +9,11 @@ g = FAQ: '<%= meta.faq %>' CHANGELOG: '<%= meta.repo %>blob/<%= meta.mainBranch %>/CHANGELOG.md' boards: {} -E = (text) -> - (text+'').replace /[&"'<>]/g, (x) -> - {'&': '&', "'": ''', '"': '"', '<': '<', '>': '>'}[x] + +E = do -> + str = {'&': '&', "'": ''', '"': '"', '<': '<', '>': '>'} + r = String::replace + regex = /[&"'<>]/g + fn = (x) -> + str[x] + (text) -> r.call text, regex, fn diff --git a/src/General/Main.coffee b/src/General/Main.coffee index a7ec5e25b..ee2fd9381 100755 --- a/src/General/Main.coffee +++ b/src/General/Main.coffee @@ -3,8 +3,8 @@ Main = if location.hostname is 'www.google.com' return $.ready -> Captcha.noscript.initFrame() - g.threads = new SimpleDict - g.posts = new SimpleDict + g.threads = new SimpleDict() + g.posts = new SimpleDict() pathname = location.pathname.split '/' g.BOARD = new Board pathname[1] @@ -90,6 +90,7 @@ Main = error: err # finally # c.timeEnd "#{name} initialization" + # c.timeEnd 'All initializations' $.ready Main.initReady @@ -163,19 +164,19 @@ Main = Settings.open() $.set 'previousversion', g.VERSION - return unless Conf['Show Support Message'] - <% if (type === 'userscript') { %> - GMver = GM_info.version.split '.' - for v, i in "<%= meta.min.greasemonkey %>".split '.' - continue if v is GMver[i] - (v < GMver[i]) or new Notice 'warning', "Your version of Greasemonkey is outdated (v#{GM_info.version} instead of v<%= meta.min.greasemonkey %> minimum) and <%= meta.name %> may not operate correctly.", 30 - break - <% } %> + if Conf['Show Support Message'] + <% if (type === 'userscript') { %> + GMver = GM_info.version.split '.' + for v, i in "<%= meta.min.greasemonkey %>".split '.' + continue if v is GMver[i] + (v < GMver[i]) or new Notice 'warning', "Your version of Greasemonkey is outdated (v#{GM_info.version} instead of v<%= meta.min.greasemonkey %> minimum) and <%= meta.name %> may not operate correctly.", 30 + break + <% } %> - try - localStorage.getItem '4chan-settings' - catch err - new Notice 'warning', 'Cookies need to be enabled on 4chan for <%= meta.name %> to operate properly.', 30 + try + localStorage.getItem '4chan-settings' + catch err + new Notice 'warning', 'Cookies need to be enabled on 4chan for <%= meta.name %> to operate properly.', 30 initThread: -> if board = $ '.board' diff --git a/src/General/Settings.coffee b/src/General/Settings.coffee index 1fc2db46f..c1c41311d 100755 --- a/src/General/Settings.coffee +++ b/src/General/Settings.coffee @@ -10,14 +10,16 @@ Settings = Header.addShortcut link - Settings.addSection 'Main', Settings.main - Settings.addSection 'Filter', Settings.filter - Settings.addSection 'Sauce', Settings.sauce - Settings.addSection 'Advanced', Settings.advanced - Settings.addSection 'Keybinds', Settings.keybinds + add = @addSection + + add 'Main', @main + add 'Filter', @filter + add 'Sauce', @sauce + add 'Advanced', @advanced + add 'Keybinds', @keybinds $.on d, 'AddSettingsSection', Settings.addSection - $.on d, 'OpenSettings', (e) -> Settings.open e.detail + $.on d, 'OpenSettings', (e) -> Settings.open e.detail if Conf['Disable Native Extension'] settings = JSON.parse(localStorage.getItem '4chan-settings') or {} @@ -26,22 +28,22 @@ Settings = localStorage.setItem '4chan-settings', JSON.stringify settings open: (openSection) -> - return if Settings.dialog + return if Settings.overlay $.event 'CloseMenu' - Settings.overlay = overlay = $.el 'div', - id: 'overlay' - Settings.dialog = dialog = $.el 'div', id: 'fourchanx-settings' className: 'dialog' $.extend dialog, <%= importHTML('Settings/Settings') %> $('a[href$="/CHANGELOG.md"]', dialog).textContent = g.VERSION - $.on $('.export', Settings.dialog), 'click', Settings.export - $.on $('.import', Settings.dialog), 'click', Settings.import - $.on $('.reset', Settings.dialog), 'click', Settings.reset - $.on $('input', Settings.dialog), 'change', Settings.onImport + Settings.overlay = overlay = $.el 'div', + id: 'overlay' + + $.on $('.export', dialog), 'click', Settings.export + $.on $('.import', dialog), 'click', Settings.import + $.on $('.reset', dialog), 'click', Settings.reset + $.on $('input', dialog), 'change', Settings.onImport links = [] for section in Settings.sections @@ -54,7 +56,7 @@ Settings = sectionToOpen = link if section.title is openSection links.pop() $.add $('.sections-list', dialog), links - (if sectionToOpen then sectionToOpen else links[0]).click() + (if sectionToOpen then sectionToOpen else links[0]).click() unless openSection is 'none' $.on $('.close', dialog), 'click', Settings.close $.on overlay, 'click', Settings.close @@ -103,8 +105,9 @@ Settings = div = $.el 'div', <%= html(': ${description}') %> input = $ 'input', div - $.on input, 'change', $.cb.checked - $.on input, 'change', -> @parentNode.parentNode.dataset.checked = @checked + $.on input, 'change', -> + @parentNode.parentNode.dataset.checked = @checked + $.cb.checked.call @ items[key] = Conf[key] inputs[key] = input level = arr[2] or 0 @@ -160,7 +163,7 @@ Settings = <% } %> a.click() import: -> - $('input', @parentNode).click() + $('input[type=file]', @parentNode).click() onImport: -> return unless file = @files[0] @@ -305,17 +308,21 @@ Settings = items = {} inputs = {} for name in ['boardnav', 'time', 'backlink', 'fileInfo', 'favicon', 'usercss'] - input = $ "[name=#{name}]", section + input = $ "[name='#{name}']", section items[name] = Conf[name] inputs[name] = input - event = if name in ['favicon', 'usercss'] - 'change' - else - 'input' - $.on input, event, $.cb.value + if name is 'usercss' + $.on input, 'change', $.cb.value + else if name is 'favicon' + $.on input, 'change', $.cb.value + $.on input, 'change', Settings[name] + else + $.on input, 'input', $.cb.value + $.on input, 'input', Settings[name] # Quick Reply Personas ta = $ '.personafield', section + $.get 'QR.personas', Conf['QR.personas'], (item) -> ta.value = item['QR.personas'] $.on ta, 'change', $.cb.value @@ -325,7 +332,6 @@ Settings = input = inputs[key] input.value = val continue if key is 'usercss' - $.on input, event, Settings[key] Settings[key].call input return @@ -484,6 +490,7 @@ Settings = inputs[key] = input $.on input, 'keydown', Settings.keybind $.add tbody, tr + $.get items, (items) -> for key, val of items inputs[key].value = val diff --git a/src/General/UI.coffee b/src/General/UI.coffee index b58f27fbe..8ef668b64 100755 --- a/src/General/UI.coffee +++ b/src/General/UI.coffee @@ -65,8 +65,7 @@ UI = do -> $.addClass lastToggledButton, 'active' - $.on d, 'click', @close - $.on d, 'CloseMenu', @close + $.one d, 'click scroll CloseMenu', @close $.add button, menu # Position @@ -121,7 +120,6 @@ UI = do -> $.rmClass lastToggledButton, 'active' currentMenu = null lastToggledButton = null - $.off d, 'click CloseMenu', @close findNextEntry: (entry, direction) -> entries = [entry.parentNode.children...] @@ -162,6 +160,7 @@ UI = do -> onFocus: (e) => e.stopPropagation() @focus e.target + focus: (entry) -> while focused = $.x 'parent::*/child::*[contains(@class,"focused")]', entry $.rmClass focused, 'focused' @@ -376,7 +375,6 @@ UI = do -> $.add label, [input, $.tn " #{text}"] label - return { dialog: dialog Menu: Menu diff --git a/src/General/html/Features/Gallery.html b/src/General/html/Features/Gallery.html index e8588d256..d52f129dd 100644 --- a/src/General/html/Features/Gallery.html +++ b/src/General/html/Features/Gallery.html @@ -6,7 +6,9 @@ × - / + + / +
diff --git a/src/General/html/Features/QuickReply.html b/src/General/html/Features/QuickReply.html index 7f7ff5d1c..c1401fc53 100755 --- a/src/General/html/Features/QuickReply.html +++ b/src/General/html/Features/QuickReply.html @@ -10,9 +10,9 @@
- + - +
diff --git a/src/General/html/Settings/Advanced.html b/src/General/html/Settings/Advanced.html index c93ac9c33..6a2b6c095 100755 --- a/src/General/html/Settings/Advanced.html +++ b/src/General/html/Settings/Advanced.html @@ -13,7 +13,7 @@
- Custom Board Navigation + Custom Board Navigation
New lines will be converted into spaces.

In the following examples for /g/, g can be changed to a different board ID (a, b, etc...), the current board (current), or the Twitter link (@).
diff --git a/src/General/lib/$.coffee b/src/General/lib/$.coffee index a243439dd..8d7700ee2 100755 --- a/src/General/lib/$.coffee +++ b/src/General/lib/$.coffee @@ -215,7 +215,7 @@ $.one = (el, events, handler) -> $.event = (event, detail, root=d) -> <% if (type === 'userscript') { %> if detail? and typeof cloneInto is 'function' - detail = cloneInto detail, document.defaultView + detail = cloneInto detail, d.defaultView <% } %> root.dispatchEvent new CustomEvent event, {bubbles: true, detail} diff --git a/src/General/lib/board.class b/src/General/lib/board.class index 4b4fdd720..7f811d463 100755 --- a/src/General/lib/board.class +++ b/src/General/lib/board.class @@ -2,7 +2,7 @@ class Board toString: -> @ID constructor: (@ID) -> - @threads = new SimpleDict - @posts = new SimpleDict + @threads = new SimpleDict() + @posts = new SimpleDict() - g.boards[@] = @ \ No newline at end of file + g.boards[@] = @ diff --git a/src/General/lib/catalogthread.class b/src/General/lib/catalogthread.class index 564f62bfb..40f6fcd94 100644 --- a/src/General/lib/catalogthread.class +++ b/src/General/lib/catalogthread.class @@ -1,5 +1,5 @@ class CatalogThread - @callbacks = new Callbacks 'CatalogThread' + @callbacks = new Callbacks 'Catalog Thread' toString: -> @ID constructor: (root, @thread) -> diff --git a/src/General/lib/connection.class b/src/General/lib/connection.class index 455948563..52e614136 100644 --- a/src/General/lib/connection.class +++ b/src/General/lib/connection.class @@ -1,11 +1,11 @@ class Connection constructor: (@target, @origin, @cb) -> - $.on window, 'message', @onMessage.bind @ + $.on window, 'message', @onMessage - send: (data) -> + send: (data) => @target.postMessage "#{g.NAMESPACE}#{JSON.stringify data}", @origin - onMessage: (e) -> + onMessage: (e) => return unless e.source is @target and e.origin is @origin and typeof e.data is 'string' and diff --git a/src/General/lib/fetcher.class b/src/General/lib/fetcher.class index 7e0db7c51..e55cc6a11 100644 --- a/src/General/lib/fetcher.class +++ b/src/General/lib/fetcher.class @@ -17,7 +17,7 @@ class Fetcher clone = post.addClone @context, ($.hasClass @root, 'dialog') Main.callbackNodes Clone, [clone] - # Get rid of the side arrows. + # Get rid of the side arrows/stubs. {nodes} = clone $.rmAll nodes.root $.add nodes.root, nodes.post @@ -36,13 +36,14 @@ class Fetcher {status} = req unless status in [200, 304] # The thread can die by the time we check a quote. - unless @archivedPost() - $.addClass @root, 'warning' - @root.textContent = - if status is 404 - "Thread No.#{@threadID} 404'd." - else - "Error #{req.statusText} (#{req.status})." + return if @archivedPost() + + $.addClass @root, 'warning' + @root.textContent = + if status is 404 + "Thread No.#{@threadID} 404'd." + else + "Error #{req.statusText} (#{req.status})." return {posts} = req.response @@ -60,9 +61,10 @@ class Fetcher return # The post can be deleted by the time we check a quote. - unless @archivedPost() - $.addClass @root, 'warning' - @root.textContent = "Post No.#{@postID} was not found." + return if @archivedPost() + + $.addClass @root, 'warning' + @root.textContent = "Post No.#{@postID} was not found." return board = g.boards[@boardID] or diff --git a/src/General/lib/post.class b/src/General/lib/post.class index 125bee370..063cb9090 100755 --- a/src/General/lib/post.class +++ b/src/General/lib/post.class @@ -234,10 +234,9 @@ class Post for clone in @clones clone.resurrect() - for quotelink in Get.allQuotelinksLinkingTo @ - if $.hasClass quotelink, 'deadlink' - quotelink.textContent = quotelink.textContent.replace '\u00A0(Dead)', '' - $.rmClass quotelink, 'deadlink' + for quotelink in Get.allQuotelinksLinkingTo @ when $.hasClass quotelink, 'deadlink' + quotelink.textContent = quotelink.textContent.replace '\u00A0(Dead)', '' + $.rmClass quotelink, 'deadlink' return collect: -> diff --git a/src/General/lib/thread.class b/src/General/lib/thread.class index d68c1c75a..f9741c7b0 100755 --- a/src/General/lib/thread.class +++ b/src/General/lib/thread.class @@ -4,7 +4,7 @@ class Thread constructor: (@ID, @board) -> @fullID = "#{@board}.#{@ID}" - @posts = new SimpleDict + @posts = new SimpleDict() @isDead = false @isHidden = false @isOnTop = false diff --git a/src/Menu/ArchiveLink.coffee b/src/Menu/ArchiveLink.coffee index 463549e73..f537d70c1 100755 --- a/src/Menu/ArchiveLink.coffee +++ b/src/Menu/ArchiveLink.coffee @@ -1,6 +1,6 @@ ArchiveLink = init: -> - return if g.VIEW not in ['index', 'thread'] or !Conf['Menu'] or !Conf['Archive Link'] + return unless g.VIEW in ['index', 'thread'] and Conf['Menu'] and Conf['Archive Link'] div = $.el 'div', textContent: 'Archive' diff --git a/src/Menu/DeleteLink.coffee b/src/Menu/DeleteLink.coffee index dc90da6ce..8a15c6e2b 100755 --- a/src/Menu/DeleteLink.coffee +++ b/src/Menu/DeleteLink.coffee @@ -1,6 +1,6 @@ DeleteLink = init: -> - return if g.VIEW not in ['index', 'thread'] or !Conf['Menu'] or !Conf['Delete Link'] + return unless g.VIEW in ['index', 'thread'] and Conf['Menu'] and Conf['Delete Link'] div = $.el 'div', className: 'delete-link' diff --git a/src/Menu/DownloadLink.coffee b/src/Menu/DownloadLink.coffee index 19743e3d8..d7c3f9411 100755 --- a/src/Menu/DownloadLink.coffee +++ b/src/Menu/DownloadLink.coffee @@ -1,6 +1,6 @@ DownloadLink = init: -> - return if g.VIEW not in ['index', 'thread'] or !Conf['Menu'] or !Conf['Download Link'] + return unless g.VIEW in ['index', 'thread'] and Conf['Menu'] and Conf['Download Link'] a = $.el 'a', className: 'download-link' diff --git a/src/Menu/Menu.coffee b/src/Menu/Menu.coffee index 7f86d6f4e..6ca40d1f5 100755 --- a/src/Menu/Menu.coffee +++ b/src/Menu/Menu.coffee @@ -1,6 +1,6 @@ Menu = init: -> - return if g.VIEW not in ['index', 'thread'] or !Conf['Menu'] + return unless g.VIEW in ['index', 'thread'] and Conf['Menu'] @button = $.el 'a', className: 'menu-button' diff --git a/src/Menu/ReportLink.coffee b/src/Menu/ReportLink.coffee index 429374566..73b60a661 100755 --- a/src/Menu/ReportLink.coffee +++ b/src/Menu/ReportLink.coffee @@ -1,6 +1,6 @@ ReportLink = init: -> - return if g.VIEW not in ['index', 'thread'] or !Conf['Menu'] or !Conf['Report Link'] + return unless g.VIEW in ['index', 'thread'] and Conf['Menu'] and Conf['Report Link'] a = $.el 'a', className: 'report-link' diff --git a/src/Miscellaneous/Banner.coffee b/src/Miscellaneous/Banner.coffee index 829861d40..5fcc5ea02 100644 --- a/src/Miscellaneous/Banner.coffee +++ b/src/Miscellaneous/Banner.coffee @@ -16,16 +16,15 @@ Banner = if g.BOARD.ID isnt 'f' and g.VIEW is 'thread' and Conf['Remove Thread Excerpt'] Banner.setTitle children[1].textContent - i = 0 - while child = children[i++] - if i is 1 + for child, i in children + if i is 0 child.title = "Click to change" $.on child, 'click', Banner.cb.toggle continue if Conf['Custom Board Titles'] - Banner.custom(child).title = "Ctrl/\u2318+click to edit board #{if i is 3 + Banner.custom(child).title = "Ctrl/\u2318+click to edit board #{if i is 2 'sub' else ''}title" diff --git a/src/Miscellaneous/CustomCSS.coffee b/src/Miscellaneous/CustomCSS.coffee index af3b94227..b3a7d4336 100755 --- a/src/Miscellaneous/CustomCSS.coffee +++ b/src/Miscellaneous/CustomCSS.coffee @@ -13,5 +13,5 @@ CustomCSS = update: -> unless @style - @addStyle() + return @addStyle() @style.textContent = Conf['usercss'] diff --git a/src/Miscellaneous/ExpandComment.coffee b/src/Miscellaneous/ExpandComment.coffee index bb62bacbe..9e6822b96 100755 --- a/src/Miscellaneous/ExpandComment.coffee +++ b/src/Miscellaneous/ExpandComment.coffee @@ -1,6 +1,6 @@ ExpandComment = init: -> - return if g.VIEW isnt 'index' or !Conf['Comment Expansion'] + return if g.VIEW isnt 'index' or !Conf['Comment Expansion'] or Conf['JSON Navigation'] @callbacks.push Fourchan.code if g.BOARD.ID is 'g' @callbacks.push Fourchan.math if g.BOARD.ID is 'sci' diff --git a/src/Miscellaneous/IDColor.coffee b/src/Miscellaneous/IDColor.coffee index 32f41ced4..47ba83b74 100755 --- a/src/Miscellaneous/IDColor.coffee +++ b/src/Miscellaneous/IDColor.coffee @@ -1,28 +1,26 @@ IDColor = init: -> - return if g.VIEW not in ['index', 'thread'] or not Conf['Color User IDs'] - @ids = {} + return unless g.VIEW in ['index', 'thread'] and Conf['Color User IDs'] + @ids = { + Heaven: [0, 0, 0, '#fff'] + } Post.callbacks.push name: 'Color User IDs' cb: @node node: -> - return if @isClone or not uid = @info.uniqueID - span = $ '.hand', @nodes.uniqueID - return unless span and span.nodeName is 'SPAN' - rgb = IDColor.compute uid + return if @isClone or !((uid = @info.uniqueID) and (span = $ 'span.hand', @nodes.uniqueID)) + + rgb = IDColor.ids[uid] or IDColor.compute uid # Style the damn node. {style} = span style.color = rgb[3] style.backgroundColor = "rgb(#{rgb[0]},#{rgb[1]},#{rgb[2]})" $.addClass span, 'painted' - span.title = 'Highlight posts by this ID' compute: (uid) -> - return IDColor.ids[uid] if IDColor.ids[uid] - # Convert chars to integers, bitshift and math to create a larger integer # Create a nice string of binary hash = IDColor.hash uid @@ -35,7 +33,7 @@ IDColor = ] # Weight color luminance values, assign a font color that should be readable. - rgb[3] = if (rgb[0] * 0.299 + rgb[1] * 0.587 + rgb[2] * 0.114) > 125 + rgb.push if (rgb[0] * 0.299 + rgb[1] * 0.587 + rgb[2] * 0.114) > 125 '#000' else '#fff' diff --git a/src/Miscellaneous/Keybinds.coffee b/src/Miscellaneous/Keybinds.coffee index 73ae10155..26dcaf314 100755 --- a/src/Miscellaneous/Keybinds.coffee +++ b/src/Miscellaneous/Keybinds.coffee @@ -46,7 +46,7 @@ Keybinds = else if (notifications = $$ '.notification').length for notification in notifications $('.close', notification).click() - else if QR.nodes and !QR.nodes.el.hidden and window.getComputedStyle(QR.nodes.form).display isnt 'none' + else if QR.nodes and not (QR.nodes.el.hidden or window.getComputedStyle(QR.nodes.form).display is 'none') if Conf['Persistent QR'] QR.hide() else @@ -139,19 +139,19 @@ Keybinds = Index.cycleSortType() # Thread Navigation when Conf['Next thread'] - return if g.VIEW isnt 'index' or !threadRoot + return unless g.VIEW is 'index' and threadRoot Nav.scroll +1 when Conf['Previous thread'] - return if g.VIEW isnt 'index' or !threadRoot + return unless g.VIEW is 'index' and threadRoot Nav.scroll -1 when Conf['Expand thread'] - return if g.VIEW isnt 'index' or !threadRoot + return unless g.VIEW is 'index' and threadRoot ExpandThread.toggle thread when Conf['Open thread'] - return if g.VIEW isnt 'index' or !threadRoot + return unless g.VIEW is 'index' and threadRoot Keybinds.open thread when Conf['Open thread tab'] - return if g.VIEW isnt 'index' or !threadRoot + return unless g.VIEW is 'index' and threadRoot Keybinds.open thread, true # Reply Navigation when Conf['Next reply'] diff --git a/src/Miscellaneous/Time.coffee b/src/Miscellaneous/Time.coffee index a2a565ca8..f31ce0d7a 100755 --- a/src/Miscellaneous/Time.coffee +++ b/src/Miscellaneous/Time.coffee @@ -1,6 +1,6 @@ Time = init: -> - return if g.VIEW not in ['index', 'thread'] or !Conf['Time Formatting'] + return unless g.VIEW in ['index', 'thread'] and Conf['Time Formatting'] Post.callbacks.push name: 'Time Formatting' diff --git a/src/Monitoring/MarkNewIPs.coffee b/src/Monitoring/MarkNewIPs.coffee index 1c6a4386f..263d7e53a 100644 --- a/src/Monitoring/MarkNewIPs.coffee +++ b/src/Monitoring/MarkNewIPs.coffee @@ -7,7 +7,7 @@ MarkNewIPs = node: -> MarkNewIPs.ipCount = @ipCount - MarkNewIPs.postIDs = @posts.keys.map (x) -> +x + MarkNewIPs.postIDs = (+x for x in @posts.keys) $.on d, 'ThreadUpdate', MarkNewIPs.onUpdate onUpdate: (e) -> @@ -35,11 +35,7 @@ MarkNewIPs = suffix = if (ipCount // 10) % 10 is 1 'th' else - switch ipCount % 10 - when 1 then 'st' - when 2 then 'nd' - when 3 then 'rd' - else 'th' + ['st', 'nd', 'rd'][ipCount % 10 - 1] or 'th' # fuck switches counter = $.el 'span', className: 'ip-counter' textContent: "(#{ipCount})" diff --git a/src/Monitoring/ThreadStats.coffee b/src/Monitoring/ThreadStats.coffee index 76795bf8b..9b821626c 100755 --- a/src/Monitoring/ThreadStats.coffee +++ b/src/Monitoring/ThreadStats.coffee @@ -84,5 +84,5 @@ ThreadStats = ThreadStats.pageCountEl.textContent = page.page (if page.page is @response.length then $.addClass else $.rmClass) ThreadStats.pageCountEl, 'warning' # Thread data may be stale (modification date given < time of last post). If so, try again on next thread update. - ThreadStats.lastPageUpdate = new Date thread.last_modified * 1000 + ThreadStats.lastPageUpdate = new Date thread.last_modified * $.SECOND return diff --git a/src/Monitoring/ThreadUpdater.coffee b/src/Monitoring/ThreadUpdater.coffee index 8ba2778b2..5fcb01568 100755 --- a/src/Monitoring/ThreadUpdater.coffee +++ b/src/Monitoring/ThreadUpdater.coffee @@ -12,7 +12,7 @@ ThreadUpdater = @dialog = sc = UI.dialog 'updater', 'bottom: 0px; left: 0px;', <%= html('
') %> $.addClass doc, 'float' - $.ready => + $.ready -> $.add d.body, sc @checkPostCount = 0 @@ -120,11 +120,11 @@ ThreadUpdater = -> not d.hidden autoUpdate: (e) -> ThreadUpdater.count ThreadUpdater.isUpdating = @checked - interval: -> + interval: (e) -> val = parseInt @value, 10 if val < 1 then val = 1 ThreadUpdater.interval = @value = val - $.cb.value.call @ + $.cb.value.call @ if e load: (e) -> {req} = ThreadUpdater switch req.status @@ -239,6 +239,7 @@ ThreadUpdater = ThreadUpdater.req?.abort() ThreadUpdater.req = $.ajax "//a.4cdn.org/#{ThreadUpdater.thread.board}/thread/#{ThreadUpdater.thread}.json", onloadend: ThreadUpdater.cb.load + timeout: $.MINUTE , whenModified: true @@ -344,7 +345,7 @@ ThreadUpdater = $.event 'ThreadUpdate', 404: false threadID: ThreadUpdater.thread.fullID - newPosts: posts.map (post) -> post.fullID + newPosts: (post.fullID for post in posts) postCount: OP.replies + 1 fileCount: OP.images + (!!ThreadUpdater.thread.OP.file and !ThreadUpdater.thread.OP.file.isDead) ipCount: OP.unique_ips diff --git a/src/Monitoring/ThreadWatcher.coffee b/src/Monitoring/ThreadWatcher.coffee index 9ed37c7eb..8839ae42c 100755 --- a/src/Monitoring/ThreadWatcher.coffee +++ b/src/Monitoring/ThreadWatcher.coffee @@ -14,7 +14,7 @@ ThreadWatcher = @status = $ '#watcher-status', @dialog @list = @dialog.lastElementChild - @refreshButton = $ '.move > .refresh', @dialog + @refreshButton = $ '.refresh', @dialog @unreaddb = Unread.db or new DataBoard 'lastReadPosts' $.on d, 'QRPostSuccessful', @cb.post @@ -36,13 +36,6 @@ ThreadWatcher = ThreadWatcher.fetchAuto() - Post.callbacks.push - name: 'Thread Watcher' - cb: @node - CatalogThread.callbacks.push - name: 'Thread Watcher' - cb: @catalogNode - if g.VIEW is 'index' and Conf['JSON Navigation'] and Conf['Menu'] and g.BOARD.ID isnt 'f' Menu.menu.addEntry el: $.el 'a', href: 'javascript:;' @@ -60,6 +53,13 @@ ThreadWatcher = $.on @el, 'click', @cb true + Post.callbacks.push + name: 'Thread Watcher' + cb: @node + CatalogThread.callbacks.push + name: 'Thread Watcher' + cb: @catalogNode + isWatched: (thread) -> ThreadWatcher.db?.get {boardID: thread.board.ID, threadID: thread.ID} diff --git a/src/Monitoring/Unread.coffee b/src/Monitoring/Unread.coffee index 51a030c71..5670fbff2 100755 --- a/src/Monitoring/Unread.coffee +++ b/src/Monitoring/Unread.coffee @@ -1,20 +1,21 @@ Unread = init: -> - return if g.VIEW isnt 'thread' or - !Conf['Unread Count'] and - !Conf['Unread Favicon'] and - !Conf['Unread Line'] and - !Conf['Scroll to Last Read Post'] and - !Conf['Thread Watcher'] and - !Conf['Desktop Notifications'] and - !Conf['Quote Threading'] + return unless g.VIEW is 'thread' and ( + Conf['Unread Count'] or + Conf['Unread Favicon'] or + Conf['Unread Line'] or + Conf['Scroll to Last Read Post'] or + Conf['Thread Watcher'] or + Conf['Desktop Notifications'] or + Conf['Quote Threading'] + ) @db = new DataBoard 'lastReadPosts', @sync @hr = $.el 'hr', id: 'unread-line' - @posts = new Set - @postsQuotingYou = new Set - @order = new RandomAccessList + @posts = new Set() + @postsQuotingYou = new Set() + @order = new RandomAccessList() @position = null Thread.callbacks.push diff --git a/src/Posting/Captcha.noscript.coffee b/src/Posting/Captcha.noscript.coffee index a7a10ac8d..f06cf4530 100644 --- a/src/Posting/Captcha.noscript.coffee +++ b/src/Posting/Captcha.noscript.coffee @@ -9,6 +9,7 @@ Captcha.noscript = container = $.el 'div', className: 'captcha-img' title: 'Reload reCAPTCHA' + input = $.el 'input', className: 'captcha-input field' title: 'Verification' @@ -103,7 +104,7 @@ Captcha.noscript = $.off input, 'focus click', @cb.focus if QR.nodes.el.getBoundingClientRect().bottom > doc.clientHeight - QR.nodes.el.style.top = null + QR.nodes.el.style.top = '' QR.nodes.el.style.bottom = '0px' destroy: -> diff --git a/src/Posting/QR.coffee b/src/Posting/QR.coffee index dd4800f10..61aea7900 100644 --- a/src/Posting/QR.coffee +++ b/src/Posting/QR.coffee @@ -13,6 +13,17 @@ QR = noscript = Conf['Force Noscript Captcha'] or !doc.dataset.jsEnabled @captcha = Captcha[if noscript then 'noscript' else 'v2'] + $.on d, '4chanXInitFinished', @initReady + + window.addEventListener 'focus', @focus, true + window.addEventListener 'blur', @focus, true + # We don't receive blur events from captcha iframe. + $.on d, 'click', @focus + + Post.callbacks.push + name: 'Quick Reply' + cb: @node + if Conf['QR Shortcut'] @shortcut = sc = $.el 'a', className: 'qr-shortcut fa fa-comment-o disabled' @@ -35,17 +46,6 @@ QR = # Prevent unnecessary loading of fallback iframe. $.onExists doc, '#postForm noscript', true, $.rm - $.on d, '4chanXInitFinished', @initReady - - window.addEventListener 'focus', @focus, true - window.addEventListener 'blur', @focus, true - # We don't receive blur events from captcha iframe. - $.on d, 'click', @focus - - Post.callbacks.push - name: 'Quick Reply' - cb: @node - initReady: -> $.off d, '4chanXInitFinished', @initReady QR.postingIsEnabled = !!$.id 'postForm' @@ -318,7 +318,7 @@ QR = QR.open() QR.handleFiles files $.addClass QR.nodes.el, 'dump' - + handleUrl: -> url = prompt 'Enter a URL:' return if url is null @@ -429,7 +429,6 @@ QR = if (e.ctrlKey or e.metaKey) and e.type is 'click' $.addClass QR.nodes.filename, 'edit' QR.nodes.filename.focus() - return $.on QR.nodes.filename, 'blur', -> $.rmClass QR.nodes.filename, 'edit' return if e.target.nodeName is 'INPUT' or (e.keyCode and e.keyCode not in [32, 13]) or e.ctrlKey e.preventDefault() QR.nodes.fileInput.click() @@ -437,11 +436,11 @@ QR = generatePostableThreadsList: -> return unless QR.nodes list = QR.nodes.thread - options = [list.firstChild] + options = [list.firstElementChild] for thread in g.BOARD.threads.keys options.push $.el 'option', value: thread - textContent: "Thread No.#{thread}" + textContent: "No.#{thread}" val = list.value $.rmAll list $.add list, options @@ -548,6 +547,7 @@ QR = $.on nodes.urlButton, 'click', QR.handleUrl $.on nodes.addPost, 'click', -> new QR.post true $.on nodes.form, 'submit', QR.submit + $.on nodes.filename, 'blur', -> $.rmClass @, 'edit' $.on nodes.fileRM, 'click', -> QR.selected.rmFile() $.on nodes.fileExtras, 'click', (e) -> e.stopPropagation() $.on nodes.spoiler, 'change', -> QR.selected.nodes.spoiler.click() @@ -665,7 +665,7 @@ QR = QR.status() QR.error $.el 'span', <%= html( - '${g.NAME} encountered an error while posting. ' + + meta.name + ' encountered an error while posting. ' + '[Banned?] ' + '[More info]' ) %>