diff --git a/src/Archive/Redirect.coffee b/src/Archive/Redirect.coffee index 86a097f92..5e601191d 100644 --- a/src/Archive/Redirect.coffee +++ b/src/Archive/Redirect.coffee @@ -15,11 +15,11 @@ Redirect = selectArchives: -> o = - thread: {} - post: {} - file: {} + thread: $.dict() + post: $.dict() + file: $.dict() - archives = {} + archives = $.dict() for data in Conf['archives'] for key in ['boards', 'files'] data[key] = [] unless data[key] instanceof Array @@ -32,7 +32,7 @@ Redirect = o.file[boardID] = data unless boardID of o.file or boardID not in files for boardID, record of Conf['selectedArchives'] - for type, id of record when (archive = archives[JSON.stringify id]) + for type, id of record when (archive = archives[JSON.stringify id]) and $.hasOwn(o, type) boards = if type is 'file' then archive.files else archive.boards o[type][boardID] = archive if boardID in boards @@ -76,14 +76,14 @@ Redirect = parse: (responses, cb) -> archives = [] - archiveUIDs = {} + archiveUIDs = $.dict() for response in responses for data in response uid = JSON.stringify(data.uid ? data.name) if uid of archiveUIDs $.extend archiveUIDs[uid], data else - archiveUIDs[uid] = data + archiveUIDs[uid] = $.dict.clone data archives.push data items = {archives, lastarchivecheck: Date.now()} $.set items @@ -98,7 +98,7 @@ Redirect = protocol: (archive) -> protocol = location.protocol - unless archive[protocol[0...-1]] + unless $.getOwn(archive, protocol[0...-1]) protocol = if protocol is 'https:' then 'http:' else 'https:' "#{protocol}//" @@ -146,10 +146,10 @@ Redirect = type if type is 'capcode' # https://github.com/pleebe/FoolFuuka/blob/bf4224eed04637a4d0bd4411c2bf5f9945dfec0b/src/Model/Search.php#L363 - value = { + value = $.getOwn({ 'Developer': 'dev' 'Verified': 'ver' - }[value] or value.toLowerCase() + }, value) or value.toLowerCase() else if type is 'image' value = value.replace /[+/=]/g, (c) -> ({'+': '-', '/': '_', '=': ''})[c] value = encodeURIComponent value diff --git a/src/Filtering/Filter.coffee b/src/Filtering/Filter.coffee index ba09d260f..e63c8ec53 100644 --- a/src/Filtering/Filter.coffee +++ b/src/Filtering/Filter.coffee @@ -1,6 +1,6 @@ Filter = - filters: {} - results: {} + filters: $.dict() + results: $.dict() init: -> return unless g.VIEW in ['index', 'thread', 'catalog'] and Conf['Filter'] return if g.VIEW is 'catalog' and not Conf['Filter in Native Catalog'] @@ -44,11 +44,11 @@ Filter = # Filter OPs along with their threads or replies only. op = filter.match(/(?:^|;)\s*op:(no|only)/)?[1] or '' - mask = {'no': 1, 'only': 2}[op] or 0 + mask = $.getOwn({'no': 1, 'only': 2}, op) or 0 # Filter only posts with/without files. file = filter.match(/(?:^|;)\s*file:(no|only)/)?[1] or '' - mask = mask | ({'no': 4, 'only': 8}[file] or 0) + mask = mask | ($.getOwn({'no': 4, 'only': 8}, file) or 0) # Overrule the `Show Stubs` setting. # Defaults to stub showing. @@ -102,7 +102,7 @@ Filter = parseBoards: (boardsRaw) -> return false unless boardsRaw return boards if (boards = Filter.parseBoardsMemo[boardsRaw]) - boards = {} + boards = $.dict() siteFilter = '' for boardID in boardsRaw.split(',') if ':' in boardID @@ -116,7 +116,7 @@ Filter = Filter.parseBoardsMemo[boardsRaw] = boards boards - parseBoardsMemo: {} + parseBoardsMemo: $.dict() test: (post, hideable=true) -> return post.filterResults if post.filterResults @@ -172,7 +172,7 @@ Filter = catalog: -> return unless (url = g.SITE.urls.catalogJSON?(g.BOARD)) - Filter.catalogData = {} + Filter.catalogData = $.dict() $.ajax url, onloadend: Filter.catalogParse Callbacks.CatalogThreadNative.push @@ -225,17 +225,18 @@ Filter = MD5: (post) -> post.files.map((f) -> f.MD5) values: (key, post) -> - if key of Filter.valueF + if $.hasOwn(Filter.valueF, key) Filter.valueF[key](post).filter((v) -> v?) else [key.split('+').map((k) -> - if (f=Filter.valueF[k]) + if (f = $.getOwn(Filter.valueF, k)) f(post).map((v) -> v or '').join('\n') else '' ).join('\n')] addFilter: (type, re, cb) -> + return unless $.hasOwn(Config.filter, type) $.get type, Conf[type], (item) -> save = item[type] # Add a new line before the regexp unless the text is empty. diff --git a/src/Filtering/Recursive.coffee b/src/Filtering/Recursive.coffee index b0cb145fa..d0d89ab03 100644 --- a/src/Filtering/Recursive.coffee +++ b/src/Filtering/Recursive.coffee @@ -1,5 +1,5 @@ Recursive = - recursives: {} + recursives: $.dict() init: -> return unless g.VIEW in ['index', 'thread'] Callbacks.Post.push diff --git a/src/Filtering/ThreadHiding.coffee b/src/Filtering/ThreadHiding.coffee index 4e250227a..81ea143cd 100644 --- a/src/Filtering/ThreadHiding.coffee +++ b/src/Filtering/ThreadHiding.coffee @@ -15,7 +15,7 @@ ThreadHiding = return unless $.hasStorage and g.SITE.software is 'yotsuba' hiddenThreads = ThreadHiding.db.get boardID: board.ID - defaultValue: {} + defaultValue: $.dict() hiddenThreads[threadID] = true for threadID of hiddenThreads localStorage.setItem "4chan-hide-t-#{board}", JSON.stringify hiddenThreads @@ -32,12 +32,12 @@ ThreadHiding = catalogSave: -> hiddenThreads2 = JSON.parse(localStorage.getItem "4chan-hide-t-#{g.BOARD}") or {} - for threadID of hiddenThreads2 when !(threadID of ThreadHiding.hiddenThreads) + for threadID of hiddenThreads2 when !$.hasOwn(ThreadHiding.hiddenThreads, threadID) ThreadHiding.db.set boardID: g.BOARD.ID threadID: threadID val: {makeStub: Conf['Stubs']} - for threadID of ThreadHiding.hiddenThreads when !(threadID of hiddenThreads2) + for threadID of ThreadHiding.hiddenThreads when !$.hasOwn(hiddenThreads2, threadID) ThreadHiding.db.delete boardID: g.BOARD.ID threadID: threadID @@ -176,7 +176,7 @@ ThreadHiding = toggle: (thread) -> unless thread instanceof Thread - thread = g.threads[@dataset.fullID] + thread = g.threads.get(@dataset.fullID) if thread.isHidden ThreadHiding.show thread else diff --git a/src/General/BoardConfig.coffee b/src/General/BoardConfig.coffee index 502b6bddb..4a9c79970 100644 --- a/src/General/BoardConfig.coffee +++ b/src/General/BoardConfig.coffee @@ -13,7 +13,7 @@ BoardConfig = load: -> if @status is 200 and @response and @response.boards - boards = {} + boards = $.dict() for board in @response.boards boards[board.board] = board {troll_flags} = @response diff --git a/src/General/Get.coffee b/src/General/Get.coffee index 4dae77b0f..159d350e1 100644 --- a/src/General/Get.coffee +++ b/src/General/Get.coffee @@ -13,14 +13,14 @@ Get = threadFromRoot: (root) -> return null unless root? {board} = root.dataset - g.threads["#{if board then encodeURIComponent(board) else g.BOARD.ID}.#{root.id.match(/\d*$/)[0]}"] + g.threads.get("#{if board then encodeURIComponent(board) else g.BOARD.ID}.#{root.id.match(/\d*$/)[0]}") threadFromNode: (node) -> Get.threadFromRoot $.x "ancestor-or-self::#{g.SITE.xpath.thread}", node postFromRoot: (root) -> return null unless root? - post = g.posts[root.dataset.fullID] + post = g.posts.get(root.dataset.fullID) index = root.dataset.clone - if index then post.clones[index] else post + if index then post.clones[+index] else post postFromNode: (root) -> Get.postFromRoot $.x "ancestor-or-self::#{g.SITE.xpath.postContainer}[1]", root postDataFromLink: (link) -> @@ -59,7 +59,7 @@ Get = # and their clones, # get all of their backlinks. if Conf['Quote Backlinks'] - handleQuotes qPost, 'backlinks' for quote in post.quotes when qPost = posts[quote] + handleQuotes qPost, 'backlinks' for quote in post.quotes when qPost = posts.get(quote) # Third: # Filter out irrelevant quotelinks. diff --git a/src/General/Index.coffee b/src/General/Index.coffee index 6cbd4fe54..8ccdf2a6f 100644 --- a/src/General/Index.coffee +++ b/src/General/Index.coffee @@ -52,7 +52,7 @@ Index = # Header "Index Navigation" submenu entries = [] - @inputs = inputs = {} + @inputs = inputs = $.dict() for name, arr of Config.Index when arr instanceof Array label = UI.checkbox name, "#{name[0]}#{name[1..].toLowerCase()}" label.title = arr[1] @@ -66,7 +66,7 @@ Index = $.on inputs['Anchor Hidden Threads'], 'change', @cb.resort watchSettings = (e) -> - if (input = inputs[e.target.name]) + if (input = $.getOwn(inputs, e.target.name)) input.checked = e.target.checked $.event 'change', null, input $.on d, 'OpenSettings', -> @@ -283,10 +283,10 @@ Index = Index.pageLoad false unless e?.detail?.deferred perBoardSort: -> - Conf['Index Sort'] = if @checked then {} else '' + Conf['Index Sort'] = if @checked then $.dict() else '' Index.saveSort() for i in [0...2] - Conf["Last Long Reply Thresholds #{i}"] = if @checked then {} else '' + Conf["Last Long Reply Thresholds #{i}"] = if @checked then $.dict() else '' Index.saveLastLongThresholds i return @@ -412,12 +412,12 @@ Index = commands = hash[1..].split '/' leftover = [] for command in commands - if (mode = Index.hashCommands.mode[command]) + if (mode = $.getOwn(Index.hashCommands.mode, command)) state.mode = mode else if command is 'index' state.mode = Conf['Previous Index Mode'] state.page = 1 - else if (sort = Index.hashCommands.sort[command.replace(/-rev$/, '')]) + else if (sort = $.getOwn(Index.hashCommands.sort, command.replace(/-rev$/, ''))) state.sort = sort state.sort += '-rev' if /-rev$/.test(command) else if /^s=/.test command @@ -659,10 +659,10 @@ Index = Index.threadsNumPerPage = pages[0]?.threads.length or 1 Index.liveThreadData = pages.reduce ((arr, next) -> arr.concat next.threads), [] Index.liveThreadIDs = Index.liveThreadData.map (data) -> data.no - Index.liveThreadDict = {} - Index.threadPosition = {} - Index.parsedThreads = {} - Index.replyData = {} + Index.liveThreadDict = $.dict() + Index.threadPosition = $.dict() + Index.parsedThreads = $.dict() + Index.replyData = $.dict() for data, i in Index.liveThreadData Index.liveThreadDict[data.no] = data Index.threadPosition[data.no] = i @@ -682,7 +682,7 @@ Index = return isHidden: (threadID) -> - if (thread = g.BOARD.threads[threadID]) and thread.OP and not thread.OP.isFetchedQuote + if (thread = g.BOARD.threads.get(threadID)) and thread.OP and not thread.OP.isFetchedQuote thread.isHidden else Index.parsedThreads[threadID].isHidden @@ -698,7 +698,7 @@ Index = try threadData = Index.liveThreadDict[ID] - if (thread = g.BOARD.threads[ID]) + if (thread = g.BOARD.threads.get(ID)) isStale = (thread.json isnt threadData) and (JSON.stringify(thread.json) isnt JSON.stringify(threadData)) if isStale thread.setCount 'post', threadData.replies + 1, threadData.bumplimit @@ -751,7 +751,7 @@ Index = continue if not (lastReplies = Index.liveThreadDict[thread.ID].last_replies) nodes = [] for data in lastReplies - if (post = thread.posts[data.no]) and not post.isFetchedQuote + if (post = thread.posts.get(data.no)) and not post.isFetchedQuote nodes.push post.nodes.root continue nodes.push node = g.SITE.Build.postFromObject data, thread.board.ID @@ -822,7 +822,7 @@ Index = if len >= Index.lastLongThresholds[+!!r.ext] return r if thread.omitted_posts then thread.last_replies[0] else thread - lastlongD = {} + lastlongD = $.dict() for thread in liveThreadData lastlongD[thread.no] = lastlong(thread).no [liveThreadData...].sort((a, b) -> diff --git a/src/General/Settings.coffee b/src/General/Settings.coffee index cccf7968b..45fb234ab 100644 --- a/src/General/Settings.coffee +++ b/src/General/Settings.coffee @@ -129,8 +129,8 @@ Settings = warning addWarning $.add section, warnings - items = {} - inputs = {} + items = $.dict() + inputs = $.dict() addCheckboxes = (root, obj) -> containers = [root] for key, arr of obj when arr instanceof Array @@ -177,7 +177,7 @@ Settings = div = $.el 'div', <%= html(': Clear manually-hidden threads and posts on all boards. Reload the page to apply.') %> button = $ 'button', div - $.get {hiddenThreads: {}, hiddenPosts: {}}, ({hiddenThreads, hiddenPosts}) -> + $.get {hiddenThreads: $.dict(), hiddenPosts: $.dict()}, ({hiddenThreads, hiddenPosts}) -> hiddenNum = 0 for ID, site of hiddenThreads when ID isnt 'boards' for ID, board of site.boards @@ -194,7 +194,7 @@ Settings = button.textContent = "Hidden: #{hiddenNum}" $.on button, 'click', -> @textContent = 'Hidden: 0' - $.get 'hiddenThreads', {}, ({hiddenThreads}) -> + $.get 'hiddenThreads', $.dict(), ({hiddenThreads}) -> if $.hasStorage and g.SITE.software is 'yotsuba' for boardID of hiddenThreads.boards localStorage.removeItem "4chan-hide-t-#{boardID}" @@ -203,7 +203,7 @@ Settings = export: -> # Make sure to export the most recent data, but don't overwrite existing `Conf` object. - Conf2 = {} + Conf2 = $.dict() $.extend Conf2, Conf $.get Conf2, (Conf2) -> # Don't export cached JSON data. @@ -235,7 +235,7 @@ Settings = reader = new FileReader() reader.onload = (e) -> try - Settings.loadSettings JSON.parse(e.target.result), (err) -> + Settings.loadSettings $.dict.json(e.target.result), (err) -> if err output.textContent = 'Import failed due to an error.' else if confirm 'Import successful. Reload now?' @@ -327,14 +327,14 @@ Settings = 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()}" if data.WatchedThreads - data.Conf['watchedThreads'] = boards: {} + data.Conf['watchedThreads'] = boards: $.dict() for boardID, threads of data.WatchedThreads for threadID, threadData of threads - (data.Conf['watchedThreads'].boards[boardID] or= {})[threadID] = excerpt: threadData.textContent + (data.Conf['watchedThreads'].boards[boardID] or= $.dict())[threadID] = excerpt: threadData.textContent data upgrade: (data, version) -> - changes = {} + changes = $.dict() set = (key, value) -> data[key] = changes[key] = value setD = (key, value) -> @@ -371,7 +371,7 @@ Settings = if data['selectedArchives']? uids = {"Moe":0,"4plebs Archive":3,"Nyafuu Archive":4,"Love is Over":5,"Rebecca Black Tech":8,"warosu":10,"fgts":15,"not4plebs":22,"DesuStorage":23,"fireden.net":24,"disabled":null} for boardID, record of data['selectedArchives'] - for type, name of record when name of uids + for type, name of record when $.hasOwn(uids, name) record[type] = uids[name] set 'selectedArchives', data['selectedArchives'] if compareString < '00001.00011.00016.00000' @@ -468,7 +468,7 @@ Settings = delete data[db].lastChecked set db, data[db] if data['siteSoftware']? and not data['siteProperties']? - siteProperties = {} + siteProperties = $.dict() for line in data['siteSoftware'].split('\n') [hostname, software] = line.split(' ') siteProperties[hostname] = {software} @@ -523,6 +523,7 @@ Settings = selectFilter: -> div = @nextElementSibling if (name = @value) isnt 'guide' + return unless $.hasOwn(Config.filter, name) $.rmAll div ta = $.el 'textarea', name: name @@ -551,7 +552,7 @@ Settings = $.extend section, <%= readHTML('Advanced.html') %> warning.hidden = Conf[warning.dataset.feature] for warning in $$ '.warning', section - inputs = {} + inputs = $.dict() for input in $$ '[name]', section inputs[input.name] = input @@ -560,7 +561,7 @@ Settings = Conf['lastarchivecheck'] = 0 $.id('lastarchivecheck').textContent = 'never' - items = {} + items = $.dict() for name, input of inputs when name not in ['captchaServiceKey', 'Interval', 'Custom CSS'] items[name] = Conf[name] event = if ( @@ -602,7 +603,7 @@ Settings = $.on customCSS, 'change', Settings.togglecss $.on applyCSS, 'click', -> CustomCSS.update() - itemsArchive = {} + itemsArchive = $.dict() itemsArchive[name] = Conf[name] for name in ['archives', 'selectedArchives', 'lastarchivecheck'] $.get itemsArchive, (itemsArchive) -> $.extend Conf, itemsArchive @@ -634,7 +635,7 @@ Settings = $.rmAll boardSelect $.rmAll tbody - archBoards = {} + archBoards = $.dict() for {uid, name, boards, files, software} in Conf['archives'] continue unless software in ['fuuka', 'foolfuuka'] for boardID in boards @@ -715,7 +716,7 @@ Settings = saveSelectedArchive: -> $.get 'selectedArchives', Conf['selectedArchives'], ({selectedArchives}) => - (selectedArchives[@dataset.boardid] or= {})[@dataset.type] = JSON.parse @value + (selectedArchives[@dataset.boardid] or= $.dict())[@dataset.type] = JSON.parse @value $.set 'selectedArchives', selectedArchives Conf['selectedArchives'] = selectedArchives Redirect.selectArchives() @@ -732,7 +733,7 @@ Settings = Conf['captchaServiceKey'][domain] = value $.get 'captchaServiceKey', Conf['captchaServiceKey'], ({captchaServiceKey}) -> captchaServiceKey[domain] = value - delete captchaServiceKey[domain] unless value or (domain of Config['captchaServiceKey'][0]) + delete captchaServiceKey[domain] unless value or $.hasOwn(Config['captchaServiceKey'][0], domain) Conf['captchaServiceKey'] = captchaServiceKey $.set 'captchaServiceKey', captchaServiceKey Settings.captchaServiceDomainList() @@ -794,8 +795,8 @@ Settings = $('.warning', section).hidden = Conf['Keybinds'] tbody = $ 'tbody', section - items = {} - inputs = {} + items = $.dict() + inputs = $.dict() for key, arr of Config.hotkeys tr = $.el 'tr', <%= html('${arr[1]}') %> diff --git a/src/General/Test.coffee b/src/General/Test.coffee index a60c14f3a..43f985eb4 100644 --- a/src/General/Test.coffee +++ b/src/General/Test.coffee @@ -127,7 +127,7 @@ Test = cb: testOne: -> - Test.testOne g.posts[@dataset.fullID] + Test.testOne g.posts.get(@dataset.fullID) Menu.menu.close() testAll: -> diff --git a/src/Images/FappeTyme.coffee b/src/Images/FappeTyme.coffee index d452287d5..1689bba80 100644 --- a/src/Images/FappeTyme.coffee +++ b/src/Images/FappeTyme.coffee @@ -25,7 +25,7 @@ FappeTyme = textContent: type[0] title: "#{type} Tyme active" $.on indicator, 'click', -> - check = FappeTyme.nodes[@parentNode.id.replace('shortcut-', '')] + check = $.getOwn(FappeTyme.nodes, @parentNode.id.replace('shortcut-', '')) check.checked = !check.checked $.event 'change', null, check Header.addShortcut lc, indicator, 410 diff --git a/src/Images/Gallery.coffee b/src/Images/Gallery.coffee index 99b254cef..9a411a70c 100644 --- a/src/Images/Gallery.coffee +++ b/src/Images/Gallery.coffee @@ -38,7 +38,7 @@ Gallery = Gallery.images = [] nodes = Gallery.nodes = {} - Gallery.fileIDs = {} + Gallery.fileIDs = $.dict() Gallery.slideshow = false nodes.el = dialog = $.el 'div', @@ -133,7 +133,7 @@ Gallery = load: (thumb, errorCB) -> ext = thumb.href.match /\w*$/ - elType = {'webm': 'video', 'mp4': 'video', 'pdf': 'iframe'}[ext] or 'img' + elType = $.getOwn({'webm': 'video', 'mp4': 'video', 'pdf': 'iframe'}, ext) or 'img' file = $.el elType $.extend file.dataset, thumb.dataset $.on file, 'error', errorCB @@ -185,7 +185,7 @@ Gallery = Gallery.cb.stop() # Scroll to post - if Conf['Scroll to Post'] and (post = g.posts[file.dataset.post]) + if Conf['Scroll to Post'] and (post = g.posts.get(file.dataset.post)) Header.scrollTo post.nodes.root # Preload next image @@ -196,11 +196,11 @@ Gallery = if @error?.code is MediaError.MEDIA_ERR_DECODE return new Notice 'error', 'Corrupt or unplayable video', 30 return if ImageCommon.isFromArchive @ - post = g.posts[@dataset.post] - file = post.files[@dataset.file] + post = g.posts.get(@dataset.post) + file = post.files[+@dataset.file] ImageCommon.error @, post, file, null, (url) => return unless url - Gallery.images[@dataset.id].href = url + Gallery.images[+@dataset.id].href = url (@src = url if Gallery.nodes.current is @) cacheError: -> @@ -341,7 +341,7 @@ Gallery = {current, frame} = Gallery.nodes {style} = current - if Conf['Stretch to Fit'] and (dim = g.posts[current.dataset.post]?.file.dimensions) + if Conf['Stretch to Fit'] and (dim = g.posts.get(current.dataset.post)?.file.dimensions) [width, height] = dim.split 'x' containerWidth = frame.clientWidth containerHeight = doc.clientHeight - 25 diff --git a/src/Images/Metadata.coffee b/src/Images/Metadata.coffee index d1e39feb8..3e7834310 100644 --- a/src/Images/Metadata.coffee +++ b/src/Images/Metadata.coffee @@ -24,7 +24,7 @@ Metadata = $.rmClass @parentNode, 'error' $.addClass @parentNode, 'loading' {index} = @parentNode.dataset - CrossOrigin.binary Get.postFromNode(@).files[index].url, (data) => + CrossOrigin.binary Get.postFromNode(@).files[+index].url, (data) => $.rmClass @parentNode, 'loading' if data? title = Metadata.parse data diff --git a/src/Images/Sauce.coffee b/src/Images/Sauce.coffee index b7717daa2..c8cd676e0 100644 --- a/src/Images/Sauce.coffee +++ b/src/Images/Sauce.coffee @@ -18,7 +18,7 @@ Sauce = parseLink: (link) -> return null if not (link = link.trim()) - parts = {} + parts = $.dict() for part, i in link.split /;(?=(?:text|boards|types|regexp|sandbox):?)/ if i is 0 parts['url'] = part @@ -47,7 +47,7 @@ Sauce = createSauceLink: (link, post, file) -> ext = file.url.match(/[^.]*$/)[0] - parts = {} + parts = $.dict() $.extend parts, link return null unless !parts['boards'] or parts['boards']["#{post.siteID}/#{post.boardID}"] or parts['boards']["#{post.siteID}/*"] diff --git a/src/Linkification/Embedding.coffee b/src/Linkification/Embedding.coffee index 659334dcd..eb87a47fb 100644 --- a/src/Linkification/Embedding.coffee +++ b/src/Linkification/Embedding.coffee @@ -1,7 +1,7 @@ Embedding = init: -> return unless g.VIEW in ['index', 'thread', 'archive'] and Conf['Linkify'] and (Conf['Embedding'] or Conf['Link Title'] or Conf['Cover Preview']) - @types = {} + @types = $.dict() @types[type.key] = type for type in @ordered_types if Conf['Embedding'] and g.VIEW isnt 'archive' diff --git a/src/Menu/DeleteLink.coffee b/src/Menu/DeleteLink.coffee index c16a52fa6..ca5280fb8 100644 --- a/src/Menu/DeleteLink.coffee +++ b/src/Menu/DeleteLink.coffee @@ -1,5 +1,5 @@ DeleteLink = - auto: [{}, {}] + auto: [$.dict(), $.dict()] init: -> return unless g.VIEW in ['index', 'thread'] and Conf['Menu'] and Conf['Delete Link'] @@ -77,7 +77,7 @@ DeleteLink = mode: 'usrdel' onlyimgdel: fileOnly pwd: QR.persona.getPassword() - form[post.ID] = 'delete' + form[+post.ID] = 'delete' $.ajax $.id('delform').action.replace("/#{g.BOARD}/", "/#{post.board}/"), responseType: 'document' @@ -110,7 +110,7 @@ DeleteLink = link.textContent = 'Deleted' if post.fullID is DeleteLink.post.fullID cooldown: - seconds: {} + seconds: $.dict() start: (post, seconds) -> # Already counting. diff --git a/src/Miscellaneous/Banner.coffee b/src/Miscellaneous/Banner.coffee index 87705d9bd..e3ff530e1 100644 --- a/src/Miscellaneous/Banner.coffee +++ b/src/Miscellaneous/Banner.coffee @@ -75,7 +75,7 @@ Banner = boardID: g.BOARD.ID threadID: @className - original: {} + original: $.dict() custom: (child) -> {className} = child diff --git a/src/Miscellaneous/CatalogLinks.coffee b/src/Miscellaneous/CatalogLinks.coffee index 27af6d5a6..1e2c8bc9f 100644 --- a/src/Miscellaneous/CatalogLinks.coffee +++ b/src/Miscellaneous/CatalogLinks.coffee @@ -81,12 +81,12 @@ CatalogLinks = return externalParse: -> - CatalogLinks.externalList = {} + CatalogLinks.externalList = $.dict() for line in Conf['externalCatalogURLs'].split '\n' continue if line[0] is '#' url = line.split(';')[0] boards = Filter.parseBoards(line.match(/;boards:([^;]+)/)?[1] or '*') - excludes = Filter.parseBoards(line.match(/;exclude:([^;]+)/)?[1]) or {} + excludes = Filter.parseBoards(line.match(/;exclude:([^;]+)/)?[1]) or $.dict() for board of boards unless excludes[board] or excludes[board.split('/')[0] + '/*'] CatalogLinks.externalList[board] = url diff --git a/src/Miscellaneous/ExpandThread.coffee b/src/Miscellaneous/ExpandThread.coffee index 6accc9719..672353b28 100644 --- a/src/Miscellaneous/ExpandThread.coffee +++ b/src/Miscellaneous/ExpandThread.coffee @@ -1,5 +1,5 @@ ExpandThread = - statuses: {} + statuses: $.dict() init: -> return if not (g.VIEW is 'index' and Conf['Thread Expansion']) if Conf['JSON Index'] @@ -96,7 +96,7 @@ ExpandThread = filesCount = 0 for postData in req.response.posts continue if postData.no is thread.ID - if (post = thread.posts[postData.no]) and not post.isFetchedQuote + if (post = thread.posts.get(postData.no)) and not post.isFetchedQuote filesCount++ if 'file' of post {root} = post.nodes postsRoot.push root diff --git a/src/Miscellaneous/FileInfo.coffee b/src/Miscellaneous/FileInfo.coffee index d62c6411b..e2364a247 100644 --- a/src/Miscellaneous/FileInfo.coffee +++ b/src/Miscellaneous/FileInfo.coffee @@ -26,7 +26,7 @@ FileInfo = format: (formatString, post, outputNode) -> output = [] formatString.replace /%(.)|[^%]+/g, (s, c) -> - output.push if c of FileInfo.formatters + output.push if $.hasOwn(FileInfo.formatters, c) FileInfo.formatters[c].call post else <%= html('${s}') %> diff --git a/src/Miscellaneous/Fourchan.coffee b/src/Miscellaneous/Fourchan.coffee index 186576ec4..7064f3c1b 100644 --- a/src/Miscellaneous/Fourchan.coffee +++ b/src/Miscellaneous/Fourchan.coffee @@ -7,8 +7,8 @@ Fourchan = initBoard: -> if g.BOARD.config.code_tags $.on window, 'prettyprint:cb', (e) -> - return if not (post = g.posts[e.detail.ID]) - return if not (pre = $$('.prettyprint', post.nodes.comment)[e.detail.i]) + return if not (post = g.posts.get(e.detail.ID)) + return if not (pre = $$('.prettyprint', post.nodes.comment)[+e.detail.i]) unless $.hasClass pre, 'prettyprinted' pre.innerHTML = e.detail.html $.addClass pre, 'prettyprinted' diff --git a/src/Miscellaneous/IDColor.coffee b/src/Miscellaneous/IDColor.coffee index 7a82491d9..d7155d18f 100644 --- a/src/Miscellaneous/IDColor.coffee +++ b/src/Miscellaneous/IDColor.coffee @@ -1,9 +1,8 @@ IDColor = init: -> return unless g.VIEW in ['index', 'thread'] and Conf['Color User IDs'] - @ids = { - Heaven: [0, 0, 0, '#fff'] - } + @ids = $.dict() + @ids['Heaven'] = [0, 0, 0, '#fff'] Callbacks.Post.push name: 'Color User IDs' diff --git a/src/Miscellaneous/ModContact.coffee b/src/Miscellaneous/ModContact.coffee index 8accd6d64..ae4454f4c 100644 --- a/src/Miscellaneous/ModContact.coffee +++ b/src/Miscellaneous/ModContact.coffee @@ -6,11 +6,11 @@ ModContact = cb: @node node: -> - return if @isClone or !ModContact.specific[@info.capcode] + return if @isClone or !$.hasOwn(ModContact.specific, @info.capcode) links = $.el 'span', className: 'contact-links brackets-wrap' $.extend links, ModContact.template(@info.capcode) $.after @nodes.capcode, links - if (moved = @info.comment.match /This thread was moved to >>>\/(\w+)\//) and ModContact.moveNote[moved[1]] + if (moved = @info.comment.match /This thread was moved to >>>\/(\w+)\//) and $.hasOwn(ModContact.moveNote, moved[1]) moveNote = $.el 'div', className: 'move-note' $.extend moveNote, ModContact.moveNote[moved[1]] $.add @nodes.post, moveNote diff --git a/src/Miscellaneous/Nav.coffee b/src/Miscellaneous/Nav.coffee index 8cfd93294..4db40266f 100644 --- a/src/Miscellaneous/Nav.coffee +++ b/src/Miscellaneous/Nav.coffee @@ -39,7 +39,7 @@ Nav = Nav.scroll +1 getThread: -> - return g.threads["#{g.BOARD}.#{g.THREADID}"].nodes.root if g.VIEW is 'thread' + return g.threads.get("#{g.BOARD}.#{g.THREADID}").nodes.root if g.VIEW is 'thread' return if $.hasClass doc, 'catalog-mode' for threadRoot in $$ g.SITE.selectors.thread thread = Get.threadFromRoot threadRoot diff --git a/src/Miscellaneous/RelativeDates.coffee b/src/Miscellaneous/RelativeDates.coffee index 42b0cfa61..69c2353dd 100644 --- a/src/Miscellaneous/RelativeDates.coffee +++ b/src/Miscellaneous/RelativeDates.coffee @@ -126,6 +126,6 @@ RelativeDates = markStale: (data) -> return if data in RelativeDates.stale # We can call RelativeDates.update() multiple times. - return if data instanceof Post and !g.posts[data.fullID] # collected post. + return if data instanceof Post and !g.posts.get(data.fullID) # collected post. return if data instanceof Element and !doc.contains(data) # removed catalog reply. RelativeDates.stale.push data diff --git a/src/Miscellaneous/Time.coffee b/src/Miscellaneous/Time.coffee index b8900636c..b6c844760 100644 --- a/src/Miscellaneous/Time.coffee +++ b/src/Miscellaneous/Time.coffee @@ -13,7 +13,7 @@ Time = format: (formatString, date) -> formatString.replace /%(.)/g, (s, c) -> - if c of Time.formatters + if $.hasOwn(Time.formatters, c) Time.formatters[c].call(date) else s diff --git a/src/Monitoring/Favicon.coffee b/src/Monitoring/Favicon.coffee index f3569bbce..d66400274 100644 --- a/src/Monitoring/Favicon.coffee +++ b/src/Monitoring/Favicon.coffee @@ -59,7 +59,8 @@ Favicon = '<%= readBase64('Metro.unreadNSFW.png') %>' '<%= readBase64('Metro.unreadNSFWY.png') %>' ] - }[Conf['favicon']] + } + items = $.getOwn(items, Conf['favicon']) f = Favicon t = 'data:image/png;base64,' diff --git a/src/Monitoring/MarkNewIPs.coffee b/src/Monitoring/MarkNewIPs.coffee index f867b1196..a9c397809 100644 --- a/src/Monitoring/MarkNewIPs.coffee +++ b/src/Monitoring/MarkNewIPs.coffee @@ -18,10 +18,10 @@ MarkNewIPs = when postCount - MarkNewIPs.postCount + deletedPosts.length i = MarkNewIPs.ipCount for fullID in newPosts - MarkNewIPs.markNew g.posts[fullID], ++i + MarkNewIPs.markNew g.posts.get(fullID), ++i when -deletedPosts.length for fullID in newPosts - MarkNewIPs.markOld g.posts[fullID] + MarkNewIPs.markOld g.posts.get(fullID) MarkNewIPs.ipCount = ipCount MarkNewIPs.postCount = postCount diff --git a/src/Monitoring/ReplyPruning.coffee b/src/Monitoring/ReplyPruning.coffee index b9a6b1d2e..396b0a333 100644 --- a/src/Monitoring/ReplyPruning.coffee +++ b/src/Monitoring/ReplyPruning.coffee @@ -88,7 +88,7 @@ ReplyPruning = return if e.detail[404] for fullID in e.detail.newPosts ReplyPruning.total++ - ReplyPruning.totalFiles++ if g.posts[fullID].file + ReplyPruning.totalFiles++ if g.posts.get(fullID).file return update: -> @@ -105,7 +105,7 @@ ReplyPruning = if ReplyPruning.hidden < hidden2 while ReplyPruning.hidden < hidden2 and ReplyPruning.position < posts.keys.length - post = posts[posts.keys[ReplyPruning.position++]] + post = posts.get(posts.keys[ReplyPruning.position++]) if post.isReply and not post.isFetchedQuote $.add ReplyPruning.container, node while (node = ReplyPruning.summary.nextSibling) and node isnt post.nodes.root $.add ReplyPruning.container, post.nodes.root @@ -115,7 +115,7 @@ ReplyPruning = else if ReplyPruning.hidden > hidden2 frag = $.frag() while ReplyPruning.hidden > hidden2 and ReplyPruning.position > 0 - post = posts[posts.keys[--ReplyPruning.position]] + post = posts.get(posts.keys[--ReplyPruning.position]) if post.isReply and not post.isFetchedQuote $.prepend frag, node while (node = ReplyPruning.container.lastChild) and node isnt post.nodes.root $.prepend frag, post.nodes.root diff --git a/src/Monitoring/ThreadStats.coffee b/src/Monitoring/ThreadStats.coffee index 120d1a215..251cbd0a0 100644 --- a/src/Monitoring/ThreadStats.coffee +++ b/src/Monitoring/ThreadStats.coffee @@ -55,7 +55,7 @@ ThreadStats = {posts} = ThreadStats.thread n = posts.keys.length for i in [ThreadStats.postIndex...n] by 1 - post = posts[posts.keys[i]] + post = posts.get(posts.keys[i]) unless post.isFetchedQuote ThreadStats.postCount++ ThreadStats.fileCount += post.files.length @@ -132,7 +132,7 @@ ThreadStats = ThreadStats.showPage and ThreadStats.pageCountEl.textContent isnt '1' and !g.SITE.threadModTimeIgnoresSage and - ThreadStats.thread.posts[ThreadStats.thread.lastPost].info.date > ThreadStats.lastPageUpdate + ThreadStats.thread.posts.get(ThreadStats.thread.lastPost).info.date > ThreadStats.lastPageUpdate ) clearTimeout ThreadStats.timeout ThreadStats.timeout = setTimeout ThreadStats.fetchPage, 5 * $.SECOND diff --git a/src/Monitoring/ThreadUpdater.coffee b/src/Monitoring/ThreadUpdater.coffee index de4a9f460..16882c8ec 100644 --- a/src/Monitoring/ThreadUpdater.coffee +++ b/src/Monitoring/ThreadUpdater.coffee @@ -266,7 +266,7 @@ ThreadUpdater = # XXX Reject updates that falsely delete the last post. return if postObjects[postObjects.length-1].no < lastPost and - new Date(req.getResponseHeader('Last-Modified')) - thread.posts[lastPost].info.date < 30 * $.SECOND + new Date(req.getResponseHeader('Last-Modified')) - thread.posts.get(lastPost).info.date < 30 * $.SECOND g.SITE.Build.spoilerRange[board] = OP.custom_spoiler thread.setStatus 'Archived', !!OP.archived @@ -291,7 +291,7 @@ ThreadUpdater = continue if ID <= lastPost # XXX Resurrect wrongly deleted posts. - if (post = thread.posts[ID]) and not post.isFetchedQuote + if (post = thread.posts.get(ID)) and not post.isFetchedQuote post.resurrect() continue @@ -304,14 +304,14 @@ ThreadUpdater = # Check for deleted posts. deletedPosts = [] for ID in ThreadUpdater.postIDs when ID not in index - thread.posts[ID].kill() + thread.posts.get(ID).kill() deletedPosts.push "#{board}.#{ID}" ThreadUpdater.postIDs = index # Check for deleted files. deletedFiles = [] for ID in ThreadUpdater.fileIDs when not (ID in files or "#{board}.#{ID}" in deletedPosts) - thread.posts[ID].kill true + thread.posts.get(ID).kill true deletedFiles.push "#{board}.#{ID}" ThreadUpdater.fileIDs = files diff --git a/src/Monitoring/ThreadWatcher.coffee b/src/Monitoring/ThreadWatcher.coffee index 8c4d6aec1..bb57513f8 100644 --- a/src/Monitoring/ThreadWatcher.coffee +++ b/src/Monitoring/ThreadWatcher.coffee @@ -150,7 +150,7 @@ ThreadWatcher = if Conf['Auto Watch'] ThreadWatcher.addRaw boardID, threadID, {}, cb else if Conf['Auto Watch Reply'] - ThreadWatcher.add (g.threads[boardID + '.' + threadID] or new Thread(threadID, g.boards[boardID] or new Board(boardID))), cb + ThreadWatcher.add (g.threads.get(boardID + '.' + threadID) or new Thread(threadID, g.boards[boardID] or new Board(boardID))), cb onIndexUpdate: (e) -> {db} = ThreadWatcher siteID = g.SITE.ID @@ -169,7 +169,7 @@ ThreadWatcher = nKilled++ ThreadWatcher.refresh() if nKilled onThreadRefresh: (e) -> - thread = g.threads[e.detail.threadID] + thread = g.threads.get(e.detail.threadID) return unless e.detail[404] and ThreadWatcher.isWatched thread # Update dead status. ThreadWatcher.add thread @@ -215,7 +215,7 @@ ThreadWatcher = ThreadWatcher.clearRequests() initLastModified: -> - lm = ($.lastModified['ThreadWatcher'] or= {}) + lm = ($.lastModified['ThreadWatcher'] or= $.dict()) for siteID, boards of ThreadWatcher.dbLM.data for boardID, data of boards.boards if ThreadWatcher.db.get {siteID, boardID} @@ -287,7 +287,7 @@ ThreadWatcher = {siteID, boardID} = board[0] lmDate = @getResponseHeader('Last-Modified') ThreadWatcher.dbLM.extend {siteID, boardID, val: $.item(url, lmDate)} - threads = {} + threads = $.dict() pageLength = 0 nThreads = 0 oldest = null @@ -449,7 +449,7 @@ ThreadWatcher = div setPrefixes: (threads) -> - prefixes = {} + prefixes = $.dict() for {siteID} in threads continue if siteID of prefixes len = 0 @@ -474,7 +474,7 @@ ThreadWatcher = ThreadWatcher.setPrefixes threads for {siteID, boardID, threadID, data} in threads # Add missing excerpt for threads added by Auto Watch - if not data.excerpt? and siteID is g.SITE.ID and (thread = g.threads["#{boardID}.#{threadID}"]) and thread.OP + if not data.excerpt? and siteID is g.SITE.ID and (thread = g.threads.get("#{boardID}.#{threadID}")) and thread.OP ThreadWatcher.db.extend {boardID, threadID, val: {excerpt: Get.threadExcerpt thread}} nodes.push ThreadWatcher.makeLine siteID, boardID, threadID, data {list} = ThreadWatcher @@ -554,7 +554,7 @@ ThreadWatcher = ThreadWatcher.addRaw boardID, threadID, data, cb addRaw: (boardID, threadID, data, cb) -> - oldData = ThreadWatcher.db.get {boardID, threadID, defaultValue: {}} + oldData = ThreadWatcher.db.get {boardID, threadID, defaultValue: $.dict()} delete oldData.last delete oldData.modified $.extend oldData, data @@ -594,7 +594,7 @@ ThreadWatcher = $.rmClass entryEl, rmClass entryEl.textContent = text true - $.on entryEl, 'click', -> ThreadWatcher.toggle g.threads["#{g.BOARD}.#{g.THREADID}"] + $.on entryEl, 'click', -> ThreadWatcher.toggle g.threads.get("#{g.BOARD}.#{g.THREADID}") addMenuEntries: -> entries = [] diff --git a/src/Monitoring/Unread.coffee b/src/Monitoring/Unread.coffee index b0f352a1c..48dabf83e 100644 --- a/src/Monitoring/Unread.coffee +++ b/src/Monitoring/Unread.coffee @@ -131,7 +131,7 @@ Unread = postIDs = Unread.thread.posts.keys for i in [Unread.readCount...postIDs.length] by 1 ID = +postIDs[i] - unless Unread.thread.posts[ID].isFetchedQuote + unless Unread.thread.posts.get(ID).isFetchedQuote break if ID > Unread.lastReadPost Unread.posts.delete ID Unread.postsQuotingYou.delete ID @@ -217,7 +217,7 @@ Unread = postIDs = Unread.thread.posts.keys for i in [Unread.readCount...postIDs.length] by 1 ID = +postIDs[i] - unless Unread.thread.posts[ID].isFetchedQuote + unless Unread.thread.posts.get(ID).isFetchedQuote break if Unread.posts.has ID Unread.lastReadPost = ID Unread.readCount++ diff --git a/src/Monitoring/UnreadIndex.coffee b/src/Monitoring/UnreadIndex.coffee index 681a163e3..b46b8dad0 100644 --- a/src/Monitoring/UnreadIndex.coffee +++ b/src/Monitoring/UnreadIndex.coffee @@ -1,7 +1,7 @@ UnreadIndex = - lastReadPost: {} - hr: {} - markReadLink: {} + lastReadPost: $.dict() + hr: $.dict() + markReadLink: $.dict() init: -> return unless g.VIEW is 'index' and Conf['Remember Last Read Post'] and Conf['Unread Line in Index'] @@ -27,7 +27,7 @@ UnreadIndex = onIndexRefresh: (e) -> return if e.detail.isCatalog for threadID in e.detail.threadIDs - thread = g.threads[threadID] + thread = g.threads.get(threadID) UnreadIndex.update thread onPostsInserted: (e) -> diff --git a/src/Posting/Captcha.fixes.coffee b/src/Posting/Captcha.fixes.coffee index e5640e9be..765d57fd3 100644 --- a/src/Posting/Captcha.fixes.coffee +++ b/src/Posting/Captcha.fixes.coffee @@ -154,7 +154,7 @@ Captcha.fixes = else if n isnt 9 and (i = @imageKeys16.indexOf key) >= 0 and i % 4 < w and (img = @images[n - (4 - i//4)*w + (i % 4)]) img.click() verify.focus() - else if dx = {'Up': n, 'Down': w, 'Left': last, 'Right': 1}[key] + else if dx = $.getOwn({'Up': n, 'Down': w, 'Left': last, 'Right': 1}, key) x = (x + dx) % (n + w) if n < x < last x = if dx is last then n else last diff --git a/src/Posting/QR.coffee b/src/Posting/QR.coffee index bc7a655db..8325c91a7 100644 --- a/src/Posting/QR.coffee +++ b/src/Posting/QR.coffee @@ -112,7 +112,7 @@ QR = statusCheck: -> return unless QR.nodes {thread} = QR.posts[0] - if thread isnt 'new' and g.threads["#{g.BOARD}.#{thread}"].isDead + if thread isnt 'new' and g.threads.get("#{g.BOARD}.#{thread}").isDead QR.abort() else QR.status() @@ -258,7 +258,7 @@ QR = status: -> return unless QR.nodes {thread} = QR.posts[0] - if thread isnt 'new' and g.threads["#{g.BOARD}.#{thread}"].isDead + if thread isnt 'new' and g.threads.get("#{g.BOARD}.#{thread}").isDead value = 'Dead' disabled = true QR.cooldown.auto = false @@ -402,7 +402,7 @@ QR = if file {type} = file blob = new Blob [file], {type} - blob.name = "#{Conf['pastedname']}.#{QR.extensionFromType[type] or 'jpg'}" + blob.name = "#{Conf['pastedname']}.#{$.getOwn(QR.extensionFromType, type) or 'jpg'}" QR.open() QR.handleFiles [blob] $.addClass QR.nodes.el, 'dump' @@ -651,7 +651,7 @@ QR = post = QR.posts[0] post.forceSave() threadID = post.thread - thread = g.BOARD.threads[threadID] + thread = g.BOARD.threads.get(threadID) if g.BOARD.ID is 'f' and threadID is 'new' filetag = QR.nodes.flashTag.value @@ -662,7 +662,7 @@ QR = err = 'New threads require a subject.' else unless !!g.BOARD.config.text_only or post.file err = 'No file selected.' - else if g.BOARD.threads[threadID].isClosed + else if g.BOARD.threads.get(threadID).isClosed err = 'You can\'t reply to this thread anymore.' else unless post.com or post.file err = 'No comment or file.' diff --git a/src/Posting/QR.cooldown.coffee b/src/Posting/QR.cooldown.coffee index 6ae895d8e..d53db866e 100644 --- a/src/Posting/QR.cooldown.coffee +++ b/src/Posting/QR.cooldown.coffee @@ -7,7 +7,7 @@ QR.cooldown = init: -> return unless Conf['Quick Reply'] @data = Conf['cooldowns'] - @changes = {} + @changes = $.dict() $.sync 'cooldowns', @sync # Called from QR @@ -35,7 +35,7 @@ QR.cooldown = QR.cooldown.count() sync: (data) -> - QR.cooldown.data = data or {} + QR.cooldown.data = data or $.dict() QR.cooldown.start() add: (threadID, postID) -> @@ -63,7 +63,7 @@ QR.cooldown = delete: (post) -> return unless QR.cooldown.data - cooldowns = (QR.cooldown.data[post.board.ID] or= {}) + cooldowns = (QR.cooldown.data[post.board.ID] or= $.dict()) for id, cooldown of cooldowns if !cooldown.delay? and cooldown.threadID is post.thread.ID and cooldown.postID is post.ID QR.cooldown.set post.board.ID, id, null @@ -71,7 +71,7 @@ QR.cooldown = secondsDeletion: (post) -> return 0 unless QR.cooldown.data and Conf['Cooldown'] - cooldowns = QR.cooldown.data[post.board.ID] or {} + cooldowns = QR.cooldown.data[post.board.ID] or $.dict() for start, cooldown of cooldowns if !cooldown.delay? and cooldown.threadID is post.thread.ID and cooldown.postID is post.ID seconds = QR.cooldown.delays.deletion - (Date.now() - start) // $.SECOND @@ -87,25 +87,25 @@ QR.cooldown = mergeChange: (data, scope, id, value) -> if value - (data[scope] or= {})[id] = value + (data[scope] or= $.dict())[id] = value else if scope of data delete data[scope][id] delete data[scope] if Object.keys(data[scope]).length is 0 set: (scope, id, value) -> QR.cooldown.mergeChange QR.cooldown.data, scope, id, value - (QR.cooldown.changes[scope] or= {})[id] = value + (QR.cooldown.changes[scope] or= $.dict())[id] = value save: -> {changes} = QR.cooldown return unless Object.keys(changes).length - $.get 'cooldowns', {}, ({cooldowns}) -> + $.get 'cooldowns', $.dict(), ({cooldowns}) -> for scope of QR.cooldown.changes for id, value of QR.cooldown.changes[scope] QR.cooldown.mergeChange cooldowns, scope, id, value QR.cooldown.data = cooldowns $.set 'cooldowns', cooldowns, -> - QR.cooldown.changes = {} + QR.cooldown.changes = $.dict() update: -> return unless QR.cooldown.isCounting @@ -117,7 +117,7 @@ QR.cooldown = seconds = 0 if Conf['Cooldown'] then for scope in [g.BOARD.ID, 'global'] - cooldowns = (QR.cooldown.data[scope] or= {}) + cooldowns = (QR.cooldown.data[scope] or= $.dict()) for start, cooldown of cooldowns start = +start diff --git a/src/Posting/QR.post.coffee b/src/Posting/QR.post.coffee index b18f3fdc1..f8d5f837c 100644 --- a/src/Posting/QR.post.coffee +++ b/src/Posting/QR.post.coffee @@ -122,6 +122,7 @@ QR.post = class @spoiler = input.checked return {name} = input.dataset + return unless name in ['thread', 'name', 'email', 'sub', 'com', 'filename', 'flag'] prev = @[name] @[name] = input.value or input.dataset.default or null switch name @@ -340,7 +341,7 @@ QR.post = class @file.newName = (@filename or '').replace /[/\\]/g, '-' unless QR.validExtension.test @filename # 4chan will truncate the filename if it has no extension. - @file.newName += ".#{QR.extensionFromType[@file.type] or 'jpg'}" + @file.newName += ".#{$.getOwn(QR.extensionFromType, @file.type) or 'jpg'}" updateFilename: -> long = "#{@filename} (#{@filesize})" diff --git a/src/Quotelinks/QuoteBacklink.coffee b/src/Quotelinks/QuoteBacklink.coffee index 4d3f8f856..d7be85ac1 100644 --- a/src/Quotelinks/QuoteBacklink.coffee +++ b/src/Quotelinks/QuoteBacklink.coffee @@ -10,7 +10,7 @@ QuoteBacklink = # Second callback adds relevant containers into posts. # This is is so that fetched posts can get their backlinks, # and that as much backlinks are appended in the background as possible. - containers: {} + containers: $.dict() init: -> return if g.VIEW not in ['index', 'thread'] or !Conf['Quote Backlinks'] @@ -35,7 +35,7 @@ QuoteBacklink = $.add a, QuoteYou.mark.cloneNode(true) if markYours for quote in @quotes containers = [QuoteBacklink.getContainer quote] - if (post = g.posts[quote]) and post.nodes.backlinkContainer + if (post = g.posts.get(quote)) and post.nodes.backlinkContainer # Don't add OP clones when OP Backlinks is disabled, # as the clones won't have the backlink containers. for clone in post.clones diff --git a/src/Quotelinks/QuoteInline.coffee b/src/Quotelinks/QuoteInline.coffee index 1cda988ad..b31bf3535 100644 --- a/src/Quotelinks/QuoteInline.coffee +++ b/src/Quotelinks/QuoteInline.coffee @@ -33,7 +33,7 @@ QuoteInline = return if $.modifiedClick e {boardID, threadID, postID} = Get.postDataFromLink @ - return if Conf['Inline Cross-thread Quotes Only'] and g.VIEW is 'thread' and g.posts["#{boardID}.#{postID}"]?.nodes.root.offsetParent # exists and not hidden + return if Conf['Inline Cross-thread Quotes Only'] and g.VIEW is 'thread' and g.posts.get("#{boardID}.#{postID}")?.nodes.root.offsetParent # exists and not hidden return if $.hasClass(doc, 'catalog-mode') e.preventDefault() @@ -66,7 +66,7 @@ QuoteInline = new Fetcher boardID, threadID, postID, inline, quoter return if not ( - (post = g.posts["#{boardID}.#{postID}"]) and + (post = g.posts.get("#{boardID}.#{postID}")) and context.thread is post.thread ) @@ -98,13 +98,13 @@ QuoteInline = return if not (el = root.firstElementChild) # Dereference clone. - post = g.posts["#{boardID}.#{postID}"] + post = g.posts.get("#{boardID}.#{postID}") post.rmClone el.dataset.clone # Decrease forward count and unhide. if Conf['Forward Hiding'] and isBacklink and - context.thread is g.threads["#{boardID}.#{threadID}"] and + context.thread is g.threads.get("#{boardID}.#{threadID}") and not --post.forwarded delete post.forwarded $.rmClass post.nodes.root, 'forwarded' diff --git a/src/Quotelinks/QuotePreview.coffee b/src/Quotelinks/QuotePreview.coffee index 90fc43bf0..3f0b544d9 100644 --- a/src/Quotelinks/QuotePreview.coffee +++ b/src/Quotelinks/QuotePreview.coffee @@ -40,7 +40,7 @@ QuotePreview = endEvents: 'mouseout click' cb: QuotePreview.mouseout - if Conf['Quote Highlighting'] and (origin = g.posts["#{boardID}.#{postID}"]) + if Conf['Quote Highlighting'] and (origin = g.posts.get("#{boardID}.#{postID}")) posts = [origin].concat origin.clones # Remove the clone that's in the qp from the array. posts.pop() diff --git a/src/Quotelinks/QuoteStrikeThrough.coffee b/src/Quotelinks/QuoteStrikeThrough.coffee index e566da9bf..77816c661 100644 --- a/src/Quotelinks/QuoteStrikeThrough.coffee +++ b/src/Quotelinks/QuoteStrikeThrough.coffee @@ -11,6 +11,6 @@ QuoteStrikeThrough = return if @isClone for quotelink in @nodes.quotelinks {boardID, postID} = Get.postDataFromLink quotelink - if g.posts["#{boardID}.#{postID}"]?.isHidden + if g.posts.get("#{boardID}.#{postID}")?.isHidden $.addClass quotelink, 'filtered' return diff --git a/src/Quotelinks/QuoteThreading.coffee b/src/Quotelinks/QuoteThreading.coffee index 91fbfc258..f8eedfe07 100644 --- a/src/Quotelinks/QuoteThreading.coffee +++ b/src/Quotelinks/QuoteThreading.coffee @@ -34,9 +34,9 @@ QuoteThreading = name: 'Quote Threading' cb: @node - parent: {} - children: {} - inserted: {} + parent: $.dict() + children: $.dict() + inserted: $.dict() toggleThreading: -> @setThreadingState !Conf['Thread Quotes'] @@ -65,7 +65,7 @@ QuoteThreading = parents = new Set() lastParent = null - for quote in @quotes when parent = g.posts[quote] + for quote in @quotes when parent = g.posts.get(quote) if not parent.isFetchedQuote and parent.isReply and parent.ID < @ID parents.add parent.ID lastParent = parent if not lastParent or parent.ID > lastParent.ID @@ -141,7 +141,7 @@ QuoteThreading = else nodes = [] Unread.order = new RandomAccessList() - QuoteThreading.inserted = {} + QuoteThreading.inserted = $.dict() posts.forEach (post) -> return if post.isFetchedQuote Unread.order.push post diff --git a/src/Quotelinks/Quotify.coffee b/src/Quotelinks/Quotify.coffee index a72fb15b0..8ea79956f 100644 --- a/src/Quotelinks/Quotify.coffee +++ b/src/Quotelinks/Quotify.coffee @@ -54,7 +54,7 @@ Quotify = @board.ID quoteID = "#{boardID}.#{postID}" - if post = g.posts[quoteID] + if post = g.posts.get(quoteID) unless post.isDead # Don't (Dead) when quotifying in an archived post, # and we know the post still exists. diff --git a/src/classes/CatalogThreadNative.coffee b/src/classes/CatalogThreadNative.coffee index f66bb1ab9..5a96dd726 100644 --- a/src/classes/CatalogThreadNative.coffee +++ b/src/classes/CatalogThreadNative.coffee @@ -9,4 +9,4 @@ class CatalogThreadNative @boardID = @nodes.thumb.parentNode.pathname.split(/\/+/)[1] @board = g.boards[@boardID] or new Board(@boardID) @ID = @threadID = +(root.dataset.id or root.id).match(/\d*$/)[0] - @thread = @board.threads[@ID] or new Thread(@ID, @board) + @thread = @board.threads.get(@ID) or new Thread(@ID, @board) diff --git a/src/classes/Connection.coffee b/src/classes/Connection.coffee index fc2c001be..843e6aebf 100644 --- a/src/classes/Connection.coffee +++ b/src/classes/Connection.coffee @@ -17,6 +17,6 @@ class Connection typeof e.data is 'string' and e.data[...g.NAMESPACE.length] is g.NAMESPACE data = JSON.parse e.data[g.NAMESPACE.length..] - for type, value of data - @cb[type]? value + for type, value of data when $.hasOwn(@cb, type) + @cb[type] value return diff --git a/src/classes/DataBoard.coffee b/src/classes/DataBoard.coffee index 3f245b369..2e76d1413 100644 --- a/src/classes/DataBoard.coffee +++ b/src/classes/DataBoard.coffee @@ -19,14 +19,14 @@ class DataBoard @data['4chan.org'] = {boards, lastChecked} delete @data.boards delete @data.lastChecked - @data[g.SITE.ID] or= boards: {} + @data[g.SITE.ID] or= boards: $.dict() changes: [] save: (change, cb) -> change() @changes.push change - $.get @key, {boards: {}}, (items) => + $.get @key, {boards: $.dict()}, (items) => return unless @changes.length needSync = ((items[@key].version or 0) > (@data.version or 0)) if needSync @@ -39,7 +39,7 @@ class DataBoard cb?() forceSync: (cb) -> - $.get @key, {boards: {}}, (items) => + $.get @key, {boards: $.dict()}, (items) => if (items[@key].version or 0) > (@data.version or 0) @initData items[@key] change() for change in @changes @@ -78,17 +78,17 @@ class DataBoard setUnsafe: ({siteID, boardID, threadID, postID, val}) -> siteID or= g.SITE.ID - @data[siteID] or= boards: {} + @data[siteID] or= boards: $.dict() if postID isnt undefined - ((@data[siteID].boards[boardID] or= {})[threadID] or= {})[postID] = val + ((@data[siteID].boards[boardID] or= $.dict())[threadID] or= $.dict())[postID] = val else if threadID isnt undefined - (@data[siteID].boards[boardID] or= {})[threadID] = val + (@data[siteID].boards[boardID] or= $.dict())[threadID] = val else @data[siteID].boards[boardID] = val extend: ({siteID, boardID, threadID, postID, val}, cb) -> @save => - oldVal = @get {siteID, boardID, threadID, postID, defaultValue: {}} + oldVal = @get {siteID, boardID, threadID, postID, defaultValue: $.dict()} for key, subVal of val if typeof subVal is 'undefined' delete oldVal[key] @@ -147,7 +147,7 @@ class DataBoard ajaxCleanParse: (boardID, response1, response2) -> siteID = g.SITE.ID return if not (board = @data[siteID].boards[boardID]) - threads = {} + threads = $.dict() if response1 for page in response1 for thread in page.threads diff --git a/src/classes/Fetcher.coffee b/src/classes/Fetcher.coffee index a267734f6..5795ef89d 100644 --- a/src/classes/Fetcher.coffee +++ b/src/classes/Fetcher.coffee @@ -1,11 +1,11 @@ class Fetcher constructor: (@boardID, @threadID, @postID, @root, @quoter) -> - if post = g.posts["#{@boardID}.#{@postID}"] + if post = g.posts.get("#{@boardID}.#{@postID}") @insert post return # 4chan X catalog data - if (post = Index.replyData?["#{@boardID}.#{@postID}"]) and (thread = g.threads["#{@boardID}.#{@threadID}"]) + if (post = Index.replyData?["#{@boardID}.#{@postID}"]) and (thread = g.threads.get("#{@boardID}.#{@threadID}")) board = g.boards[@boardID] post = new Post g.SITE.Build.postFromObject(post, @boardID), thread, board, {isFetchedQuote: true} Main.callbackNodes 'Post', [post] @@ -53,7 +53,7 @@ class Fetcher fetchedPost: (req, isCached) -> # In case of multiple callbacks for the same request, # don't parse the same original post more than once. - if post = g.posts["#{@boardID}.#{@postID}"] + if post = g.posts.get("#{@boardID}.#{@postID}") @insert post return @@ -96,7 +96,7 @@ class Fetcher board = g.boards[@boardID] or new Board @boardID - thread = g.threads["#{@boardID}.#{@threadID}"] or + thread = g.threads.get("#{@boardID}.#{@threadID}") or new Thread @threadID, board post = new Post g.SITE.Build.postFromObject(post, @boardID), thread, board, {isFetchedQuote: true} Main.callbackNodes 'Post', [post] @@ -115,7 +115,7 @@ class Fetcher for key of media when /_link$/.test key # Image/thumbnail URLs loaded over HTTP can be modified in transit. # Require them to be from an HTTP host so that no referrer is sent to them from an HTTPS page. - delete media[key] unless media[key]?.match /^http:\/\// + delete media[key] unless $.getOwn(media, key)?.match /^http:\/\// that.parseArchivedPost @response, url, archive return true return false @@ -123,7 +123,7 @@ class Fetcher parseArchivedPost: (data, url, archive) -> # In case of multiple callbacks for the same request, # don't parse the same original post more than once. - if post = g.posts["#{@boardID}.#{@postID}"] + if post = g.posts.get("#{@boardID}.#{@postID}") @insert post return @@ -210,7 +210,7 @@ class Fetcher board = g.boards[@boardID] or new Board @boardID - thread = g.threads["#{@boardID}.#{@threadID}"] or + thread = g.threads.get("#{@boardID}.#{@threadID}") or new Thread @threadID, board post = new Post g.SITE.Build.post(o), thread, board, {isFetchedQuote: true} post.kill() diff --git a/src/classes/Post.coffee b/src/classes/Post.coffee index 866415ecb..fdceb2d63 100644 --- a/src/classes/Post.coffee +++ b/src/classes/Post.coffee @@ -59,9 +59,9 @@ class Post <% if (readJSON('/.tests_enabled')) { %> return if @forBuildTest <% } %> - if g.posts[@fullID] + if g.posts.get(@fullID) @isRebuilt = true - @clones = g.posts[@fullID].clones + @clones = g.posts.get(@fullID).clones clone.origin = @ for clone in @clones @thread.lastPost = @ID if !@isFetchedQuote and @ID > @thread.lastPost diff --git a/src/classes/ShimSet.coffee b/src/classes/ShimSet.coffee index b48d6e160..eb37fe33e 100644 --- a/src/classes/ShimSet.coffee +++ b/src/classes/ShimSet.coffee @@ -1,6 +1,6 @@ class ShimSet constructor: -> - @elements = {} + @elements = $.dict() @size = 0 has: (value) -> value of @elements diff --git a/src/classes/SimpleDict.coffee b/src/classes/SimpleDict.coffee index 6c6b3edfd..455ac7d09 100644 --- a/src/classes/SimpleDict.coffee +++ b/src/classes/SimpleDict.coffee @@ -16,3 +16,9 @@ class SimpleDict forEach: (fn) -> fn @[key] for key in [@keys...] return + + get: (key) -> + if key is 'keys' + undefined + else + $.getOwn(@, key) diff --git a/src/classes/Thread.coffee b/src/classes/Thread.coffee index 659716ca4..90da7dbfc 100644 --- a/src/classes/Thread.coffee +++ b/src/classes/Thread.coffee @@ -1,7 +1,8 @@ class Thread toString: -> @ID - constructor: (@ID, @board) -> + constructor: (ID, @board) -> + @ID = +ID @threadID = @ID @boardID = @board.ID @siteID = g.SITE.ID diff --git a/src/css/CSS.js b/src/css/CSS.js index 261e765b5..8d106a089 100644 --- a/src/css/CSS.js +++ b/src/css/CSS.js @@ -27,7 +27,7 @@ sub: function(css) { var sel = variables; for (var i = 0; i < words.length; i++) { if (typeof sel !== 'object') return ':not(*)'; - sel = sel[words[i]]; + sel = $.getOwn(sel, words[i]); } if (typeof sel !== 'string') return ':not(*)'; return sel; diff --git a/src/globals/globals.js b/src/globals/globals.js index f8398488d..6b3f2b47f 100644 --- a/src/globals/globals.js +++ b/src/globals/globals.js @@ -1,6 +1,6 @@ var Conf, E, c, d, doc, docSet, g; -Conf = {}; +Conf = Object.create(null); c = console; d = document; doc = d.documentElement; @@ -13,8 +13,8 @@ docSet = function() { g = { VERSION: '<%= readJSON('/version.json').version %>', NAMESPACE: '<%= meta.name %>.', - sites: {}, - boards: {} + sites: Object.create(null), + boards: Object.create(null) }; E = (function() { diff --git a/src/main/Main.coffee b/src/main/Main.coffee index 7cdb7129e..7d513a3ad 100644 --- a/src/main/Main.coffee +++ b/src/main/Main.coffee @@ -33,7 +33,7 @@ Main = # Flatten default values from Config into Conf flatten = (parent, obj) -> if obj instanceof Array - Conf[parent] = obj[0] + Conf[parent] = $.dict.clone(obj[0]) else if typeof obj is 'object' for key, val of obj flatten key, val @@ -57,15 +57,15 @@ Main = flatten null, Config for db in DataBoard.keys - Conf[db] = {} - Conf['customTitles'] = {'4chan.org': {boards: {'qa': {'boardTitle': {orig: '/qa/ - Question & Answer', title: '/qa/ - 2D / Random'}}}}} - Conf['boardConfig'] = boards: {} + Conf[db] = $.dict() + Conf['customTitles'] = $.dict.clone {'4chan.org': {boards: {'qa': {'boardTitle': {orig: '/qa/ - Question & Answer', title: '/qa/ - 2D / Random'}}}}} + Conf['boardConfig'] = boards: $.dict() Conf['archives'] = Redirect.archives - Conf['selectedArchives'] = {} - Conf['cooldowns'] = {} - Conf['Index Sort'] = {} - Conf["Last Long Reply Thresholds #{i}"] = {} for i in [0...2] - Conf['siteProperties'] = {} + Conf['selectedArchives'] = $.dict() + Conf['cooldowns'] = $.dict() + Conf['Index Sort'] = $.dict() + Conf["Last Long Reply Thresholds #{i}"] = $.dict() for i in [0...2] + Conf['siteProperties'] = $.dict() # XXX old key names Conf['Except Archives from Encryption'] = false @@ -84,7 +84,7 @@ Main = $.addCSP "script-src #{jsWhitelist.replace(/^#.*$/mg, '').replace(/[\s;]+/g, ' ').trim()}" # Get saved values as items - items = {} + items = $.dict() items[key] = undefined for key of Conf items['previousversion'] = undefined ($.getSync or $.get) items, (items) -> @@ -363,7 +363,7 @@ Main = else g.BOARD threadID = +threadRoot.id.match(/\d*$/)[0] - return if !threadID or boardObj.threads[threadID]?.nodes.root + return if !threadID or boardObj.threads.get(threadID)?.nodes.root thread = new Thread threadID, boardObj thread.nodes.root = threadRoot threads.push thread diff --git a/src/platform/$.coffee b/src/platform/$.coffee index 4c309ee5b..bc195b1df 100644 --- a/src/platform/$.coffee +++ b/src/platform/$.coffee @@ -40,6 +40,32 @@ $.extend = (object, properties) -> object[key] = val return +$.dict = -> + Object.create(null) + +$.dict.clone = (obj) -> + if typeof obj isnt 'object' or obj is null + obj + else if obj instanceof Array + arr = [] + for i in [0...obj.length] by 1 + arr.push $.dict.clone(obj[i]) + arr + else + map = Object.create(null) + for key, val of obj + map[key] = $.dict.clone(val) + map + +$.dict.json = (str) -> + $.dict.clone(JSON.parse(str)) + +$.hasOwn = (obj, key) -> + Object::hasOwnProperty.call(obj, key) + +$.getOwn = (obj, key) -> + if Object::hasOwnProperty.call(obj, key) then obj[key] else undefined + $.ajax = do -> if window.wrappedJSObject and not XMLHttpRequest.wrappedJSObject pageXHR = XPCNativeWrapper window.wrappedJSObject.XMLHttpRequest @@ -85,7 +111,7 @@ $.ajax = do -> # XXX https://bugs.chromium.org/p/chromium/issues/detail?id=920638 do -> requestID = 0 - requests = {} + requests = $.dict() $.ajaxPageInit = -> if Conf['Chromium CORB Bug'] and g.SITE.software is 'yotsuba' @@ -97,7 +123,7 @@ do -> $.set 'Chromium CORB Bug', (Conf['Chromium CORB Bug'] = false) $.global -> - window.FCX.requests = {} + window.FCX.requests = Object.create(null) document.addEventListener '4chanXAjax', (e) -> {url, timeout, responseType, withCredentials, type, onprogress, form, headers, id} = e.detail @@ -144,7 +170,8 @@ do -> return unless (req = requests[e.detail.id]) delete requests[e.detail.id] if e.detail.status - $.extend req, e.detail + for key in ['status', 'statusText', 'response', 'responseHeaderString'] + req[key] = e.detail[key] if req.responseType is 'document' req.response = new DOMParser().parseFromString(e.detail.response, 'text/html') req.onloadend() @@ -165,7 +192,7 @@ do -> # Status Code 304: Not modified # With the `If-Modified-Since` header we only receive the HTTP headers and no body for 304 responses. # This saves a lot of bandwidth and CPU time for both the users and the servers. -$.lastModified = {} +$.lastModified = $.dict() $.whenModified = (url, bucket, cb, options={}) -> {timeout, ajax} = options params = [] @@ -174,12 +201,12 @@ $.whenModified = (url, bucket, cb, options={}) -> params.push "t=#{Date.now()}" if url.split('/')[2] is 'a.4cdn.org' url0 = url url += '?' + params.join('&') if params.length - headers = {} + headers = $.dict() if (t = $.lastModified[bucket]?[url0])? headers['If-Modified-Since'] = t r = (ajax or $.ajax) url, { onloadend: -> - ($.lastModified[bucket] or= {})[url0] = @getResponseHeader('Last-Modified') + ($.lastModified[bucket] or= $.dict())[url0] = @getResponseHeader('Last-Modified') cb.call @ timeout headers @@ -187,7 +214,7 @@ $.whenModified = (url, bucket, cb, options={}) -> r do -> - reqs = {} + reqs = $.dict() $.cache = (url, cb, options={}) -> {ajax} = options if (req = reqs[url]) @@ -212,11 +239,13 @@ do -> $.cb = checked: -> - $.set @name, @checked - Conf[@name] = @checked + if $.hasOwn(Conf, @name) + $.set @name, @checked + Conf[@name] = @checked value: -> - $.set @name, @value.trim() - Conf[@name] = @value + if $.hasOwn(Conf, @name) + $.set @name, @value.trim() + Conf[@name] = @value $.asap = (test, cb) -> if test() @@ -478,14 +507,14 @@ $.platform = '<%= type %>'; $.hasStorage = do -> try - return true if localStorage[g.NAMESPACE + 'hasStorage'] is 'true' - localStorage[g.NAMESPACE + 'hasStorage'] = 'true' - return localStorage[g.NAMESPACE + 'hasStorage'] is 'true' + return true if localStorage.getItem(g.NAMESPACE + 'hasStorage') is 'true' + localStorage.setItem(g.NAMESPACE + 'hasStorage', 'true') + return localStorage.getItem(g.NAMESPACE + 'hasStorage') is 'true' catch false $.item = (key, val) -> - item = {} + item = $.dict() item[key] = val item @@ -496,7 +525,7 @@ $.oneItemSugar = (fn) -> else fn key, val -$.syncing = {} +$.syncing = $.dict() $.securityCheck = (data) -> if location.protocol isnt 'https:' @@ -505,13 +534,13 @@ $.securityCheck = (data) -> <% if (type === 'crx') { %> # https://developer.chrome.com/extensions/storage.html $.oldValue = - local: {} - sync: {} + local: $.dict() + sync: $.dict() chrome.storage.onChanged.addListener (changes, area) -> for key of changes oldValue = $.oldValue.local[key] ? $.oldValue.sync[key] - $.oldValue[area][key] = changes[key].newValue + $.oldValue[area][key] = $.dict.clone(changes[key].newValue) newValue = $.oldValue.local[key] ? $.oldValue.sync[key] cb = $.syncing[key] if cb and JSON.stringify(newValue) isnt JSON.stringify(oldValue) @@ -542,11 +571,12 @@ $.get = $.oneItemSugar (data, cb) -> if $.engine is 'gecko' and area is 'sync' and keys.length > 3 keys = null chrome.storage[area].get keys, (result) -> + result = $.dict.clone(result) if chrome.runtime.lastError c.error chrome.runtime.lastError.message if keys is null - result2 = {} - result2[key] = val for key, val of result when key of data + result2 = $.dict() + result2[key] = val for key, val of result when $.hasOwn(data, key) result = result2 for key of data $.oldValue[area][key] = result[key] @@ -560,8 +590,8 @@ $.get = $.oneItemSugar (data, cb) -> do -> items = - local: {} - sync: {} + local: $.dict() + sync: $.dict() exceedsQuota = (key, value) -> # bytes in UTF-8 @@ -579,7 +609,7 @@ do -> timeout = {} setArea = (area, cb) -> - data = {} + data = $.dict() $.extend data, items[area] return if !Object.keys(data).length or timeout[area] > Date.now() chrome.storage[area].set data, -> @@ -609,8 +639,8 @@ do -> $.clear = (cb) -> return unless $.crxWorking() - items.local = {} - items.sync = {} + items.local = $.dict() + items.sync = $.dict() count = 2 err = null done = -> @@ -631,7 +661,7 @@ if GM?.deleteValue? and window.BroadcastChannel and not GM_addValueChangeListene $.on $.syncChannel, 'message', (e) -> for key, val of e.data when (cb = $.syncing[key]) - cb JSON.parse(JSON.stringify(val)), key + cb $.dict.json(JSON.stringify(val)), key $.sync = (key, cb) -> $.syncing[key] = cb @@ -642,7 +672,7 @@ if GM?.deleteValue? and window.BroadcastChannel and not GM_addValueChangeListene unless keys instanceof Array keys = [keys] Promise.all(GM.deleteValue(g.NAMESPACE + key) for key in keys).then -> - items = {} + items = $.dict() items[key] = undefined for key in keys $.syncChannel.postMessage items cb?() @@ -651,7 +681,7 @@ if GM?.deleteValue? and window.BroadcastChannel and not GM_addValueChangeListene keys = Object.keys items Promise.all(GM.getValue(g.NAMESPACE + key) for key in keys).then (values) -> for val, i in values when val - items[keys[i]] = JSON.parse val + items[keys[i]] = $.dict.json val cb items $.set = $.oneItemSugar (items, cb) -> @@ -675,7 +705,7 @@ else $.getValue = GM_getValue $.listValues = -> GM_listValues() # error when called if missing else if $.hasStorage - $.getValue = (key) -> localStorage[key] + $.getValue = (key) -> localStorage.getItem(key) $.listValues = -> key for key of localStorage when key[...g.NAMESPACE.length] is g.NAMESPACE else @@ -686,12 +716,12 @@ else $.setValue = GM_setValue $.deleteValue = GM_deleteValue else if GM_deleteValue? - $.oldValue = {} + $.oldValue = $.dict() $.setValue = (key, val) -> GM_setValue key, val if key of $.syncing $.oldValue[key] = val - localStorage[key] = val if $.hasStorage # for `storage` events + localStorage.setItem(key, val) if $.hasStorage # for `storage` events $.deleteValue = (key) -> GM_deleteValue key if key of $.syncing @@ -699,10 +729,10 @@ else localStorage.removeItem key if $.hasStorage # for `storage` events $.cantSync = true if !$.hasStorage else if $.hasStorage - $.oldValue = {} + $.oldValue = $.dict() $.setValue = (key, val) -> $.oldValue[key] = val if key of $.syncing - localStorage[key] = val + localStorage.setItem(key, val) $.deleteValue = (key) -> delete $.oldValue[key] if key of $.syncing localStorage.removeItem key @@ -715,7 +745,7 @@ else $.sync = (key, cb) -> $.syncing[key] = GM_addValueChangeListener g.NAMESPACE + key, (key2, oldValue, newValue, remote) -> if remote - newValue = JSON.parse newValue unless newValue is undefined + newValue = $.dict.json newValue unless newValue is undefined cb newValue, key $.forceSync = -> else if GM_deleteValue? or $.hasStorage @@ -730,7 +760,7 @@ else if newValue? return if newValue is $.oldValue[key] $.oldValue[key] = newValue - cb JSON.parse(newValue), key[g.NAMESPACE.length..] + cb $.dict.json(newValue), key[g.NAMESPACE.length..] else return unless $.oldValue[key]? delete $.oldValue[key] @@ -760,7 +790,7 @@ else $.getSync = (items, cb) -> for key of items when (val2 = $.getValue g.NAMESPACE + key) try - items[key] = JSON.parse val2 + items[key] = $.dict.json val2 catch err # XXX https://github.com/ccd0/4chan-x/issues/2218 unless /^(?:undefined)*$/.test(val2) diff --git a/src/platform/CrossOrigin.coffee b/src/platform/CrossOrigin.coffee index bc7968780..f0cd2738e 100644 --- a/src/platform/CrossOrigin.coffee +++ b/src/platform/CrossOrigin.coffee @@ -10,7 +10,7 @@ eventPageRequest = do -> <% } %> CrossOrigin = - binary: (url, cb, headers={}) -> + binary: (url, cb, headers=$.dict()) -> # XXX https://forums.lanik.us/viewtopic.php?f=64&t=24173&p=78310 url = url.replace /^((?:https?:)?\/\/(?:\w+\.)?(?:4chan|4channel|4cdn)\.org)\/adv\//, '$1//adv/' <% if (type === 'crx') { %> @@ -73,7 +73,7 @@ CrossOrigin = name = match.replace /\\"/g, '"' if /^text\/plain;\s*charset=x-user-defined$/i.test(mime) # In JS Blocker (Safari) content type comes back as 'text/plain; charset=x-user-defined'; guess from filename instead. - mime = QR.typeFromExtension[name.match(/[^.]*$/)[0].toLowerCase()] or 'application/octet-stream' + mime = $.getOwn(QR.typeFromExtension, name.match(/[^.]*$/)[0].toLowerCase()) or 'application/octet-stream' blob = new Blob([data], {type: mime}) blob.name = name cb blob @@ -85,13 +85,13 @@ CrossOrigin = responseHeaderString: null getResponseHeader: (headerName) -> if !@responseHeaders? and @responseHeaderString? - @responseHeaders = {} + @responseHeaders = $.dict() for header in @responseHeaderString.split('\r\n') if (i = header.indexOf(':')) >= 0 key = header[...i].trim().toLowerCase() val = header[i+1..].trim() @responseHeaders[key] = val - (@responseHeaders or {})[headerName.toLowerCase()] ? null + @responseHeaders?[headerName.toLowerCase()] ? null abort: -> onloadend: -> diff --git a/src/site/SW.tinyboard.coffee b/src/site/SW.tinyboard.coffee index b30327cc9..4651467bf 100644 --- a/src/site/SW.tinyboard.coffee +++ b/src/site/SW.tinyboard.coffee @@ -33,7 +33,7 @@ SW.tinyboard = detect: -> for script in $$ 'script:not([src])', d.head if (m = script.textContent.match(/\bvar configRoot=(".*?")/)) - properties = {} + properties = $.dict() try root = JSON.parse m[1] if root[0] is '/' diff --git a/src/site/SW.yotsuba.Build.coffee b/src/site/SW.yotsuba.Build.coffee index 2371d2930..4ae0b45c2 100644 --- a/src/site/SW.yotsuba.Build.coffee +++ b/src/site/SW.yotsuba.Build.coffee @@ -1,7 +1,7 @@ Build = staticPath: '//s.4cdn.org/image/' gifIcon: if window.devicePixelRatio >= 2 then '@2x.gif' else '.gif' - spoilerRange: {} + spoilerRange: $.dict() shortFilename: (filename) -> ext = filename.match(/\.?[^\.]*$/)[0] @@ -69,8 +69,9 @@ Build = o.file = SW.yotsuba.Build.parseJSONFile(data, {siteID, boardID}) o.files.push o.file # Temporary JSON properties for events such as April 1 / Halloween + o.extra = $.dict() for key of data when key[0] is 'x' - o[key] = data[key] + o.extra[key] = data[key] o parseJSONFile: (data, {siteID, boardID}) -> @@ -133,7 +134,7 @@ Build = capcodePlural = 'Verified Users' capcodeDescription = '' else - capcodeLong = {'Admin': 'Administrator', 'Mod': 'Moderator'}[capcode] or capcode + capcodeLong = $.getOwn({'Admin': 'Administrator', 'Mod': 'Moderator'}, capcode) or capcode capcodePlural = "#{capcodeLong}s" capcodeDescription = "a 4chan #{capcodeLong}" diff --git a/src/site/SW.yotsuba.Build/PostInfo.html b/src/site/SW.yotsuba.Build/PostInfo.html index fc1a8224a..1b159fd89 100644 --- a/src/site/SW.yotsuba.Build/PostInfo.html +++ b/src/site/SW.yotsuba.Build/PostInfo.html @@ -5,7 +5,7 @@ ?{email}{} ${name} ?{tripcode}{ ${tripcode}} - ?{o.xa19s}{ } + ?{o.extra.xa19s}{ } ?{pass}{ } ?{capcode}{ ## ${capcode}} ?{email}{} @@ -19,7 +19,7 @@ No. ${ID} - ?{o.xa19l && o.isReply}{ } + ?{o.extra.xa19l && o.isReply}{ } ?{o.isSticky}{ Sticky} ?{o.isClosed && !o.isArchived}{ Closed} ?{o.isArchived}{ Archived} diff --git a/src/site/Site.coffee b/src/site/Site.coffee index 317f8bed9..2865f02d7 100644 --- a/src/site/Site.coffee +++ b/src/site/Site.coffee @@ -7,14 +7,14 @@ Site = init: (cb) -> $.extend Conf['siteProperties'], Site.defaultProperties hostname = Site.resolve() - if hostname and Conf['siteProperties'][hostname].software of SW + if hostname and $.hasOwn(SW, Conf['siteProperties'][hostname].software) @set hostname cb() $.onExists doc, 'body', => for software of SW when (changes = SW[software].detect?()) changes.software = software hostname = location.hostname.replace(/^www\./, '') - properties = (Conf['siteProperties'][hostname] or= {}) + properties = (Conf['siteProperties'][hostname] or= $.dict()) changed = 0 for key of changes when properties[key] isnt changes[key] properties[key] = changes[key] @@ -29,7 +29,7 @@ Site = resolve: (url=location) -> {hostname} = url - while hostname and hostname not of Conf['siteProperties'] + while hostname and not $.hasOwn(Conf['siteProperties'], hostname) hostname = hostname.replace(/^[^.]*\.?/, '') if hostname hostname = canonical if (canonical = Conf['siteProperties'][hostname].canonical) @@ -43,7 +43,7 @@ Site = for ID, properties of Conf['siteProperties'] continue if properties.canonical software = properties.software - continue unless software and SW[software] + continue unless software and $.hasOwn(SW, software) g.sites[ID] = site = Object.create SW[software] $.extend site, {ID, siteID: ID, properties, software} g.SITE = g.sites[hostname]