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.xa19s}}
+ ?{o.extra.xa19s}{ ${o.extra.xa19s}}
?{pass}{ }
?{capcode}{ ## ${capcode}}
?{email}{}
@@ -19,7 +19,7 @@
No.
${ID}
- ?{o.xa19l && o.isReply}{ Like! ×${o.xa19l}}
+ ?{o.extra.xa19l && o.isReply}{ Like! ×${o.extra.xa19l}}
?{o.isSticky}{
}
?{o.isClosed && !o.isArchived}{
}
?{o.isArchived}{
}
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]