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