Make $.get use the Chrome storage API. Close #503

Still need to fix $.sync, which I'll do tomorrow. Good night.
This commit is contained in:
Nicolas Stepien 2013-03-29 05:59:23 +01:00
parent b3b224a8df
commit 0a33b5a6d5
4 changed files with 250 additions and 156 deletions

View File

@ -200,15 +200,21 @@ $.extend $,
# Round to an integer otherwise.
Math.round size
"#{size} #{['B', 'KB', 'MB', 'GB'][unit]}"
item: (key, val) ->
item = {}
item[key] = val
item
<% if (type === 'crx') { %>
# https://developer.chrome.com/extensions/storage.html
delete: (keys) ->
chrome.storage.sync.remove keys
get: (key, defaultVal) ->
if val = localStorage.getItem g.NAMESPACE + key
JSON.parse val
get: (key, val, cb) ->
if arguments.length is 2
items = key
cb = val
else
defaultVal
items = $.item key, val
chrome.storage.sync.get items, cb
set: (key, val) ->
item = {}
item[key] = val
@ -231,11 +237,17 @@ do ->
localStorage.removeItem key
delete scriptStorage[key]
return
$.get = (key, defaultVal) ->
if val = scriptStorage[g.NAMESPACE + key]
JSON.parse val
$.get = (key, val, cb) ->
if arguments.length is 2
items = key
cb = val
else
defaultVal
items = $.item key, val
$.queueTask ->
for key of items
if val = scriptStorage[g.NAMESPACE + key]
items[key] = JSON.parse val
cb items
$.set = (key, val) ->
key = g.NAMESPACE + key
val = JSON.stringify val
@ -243,6 +255,7 @@ do ->
localStorage.setItem key, val
scriptStorage[key] = val
<% } else { %>
# http://wiki.greasespot.net/Main_Page
delete: (key) ->
unless keys instanceof Array
keys = [keys]
@ -251,11 +264,17 @@ do ->
localStorage.removeItem key
GM_deleteValue key
return
get: (key, defaultVal) ->
if val = GM_getValue g.NAMESPACE + key
JSON.parse val
get: (key, val, cb) ->
if arguments.length is 2
items = key
cb = val
else
defaultVal
items = $.item key, val
$.queueTask ->
for key of items
if val = GM_getValue g.NAMESPACE + key
items[key] = JSON.parse val
cb items
set: (key, val) ->
key = g.NAMESPACE + key
val = JSON.stringify val

View File

@ -210,10 +210,11 @@ Settings =
order: 110
open: -> Conf['Enable 4chan\'s Extension']
if (prevVersion = $.get 'previousversion', null) isnt g.VERSION
$.get 'previousversion', null, (item) ->
return if item['previousversion'] is g.VERSION
$.set 'lastupdate', Date.now()
$.set 'previousversion', g.VERSION
$.on d, '4chanXInitFinished', Settings.open unless prevVersion
$.on d, '4chanXInitFinished', Settings.open unless item['previousversion']
Settings.addSection 'Main', Settings.main
Settings.addSection 'Filter', Settings.filter
@ -309,37 +310,54 @@ Settings =
$.on $('.import', section), 'click', Settings.import
$.on $('input', section), 'change', Settings.onImport
items = {}
inputs = {}
for key, obj of Config.main
fs = $.el 'fieldset',
innerHTML: "<legend>#{key}</legend>"
for key, arr of obj
checked = if $.get(key, Conf[key]) then 'checked' else ''
description = arr[1]
div = $.el 'div',
innerHTML: "<label><input type=checkbox name=\"#{key}\" #{checked}>#{key}</label><span class=description>: #{description}</span>"
$.on $('input', div), 'change', $.cb.checked
innerHTML: "<label><input type=checkbox name=\"#{key}\">#{key}</label><span class=description>: #{description}</span>"
input = $ 'input', div
$.on input, 'change', $.cb.checked
items[key] = Conf[key]
inputs[key] = input
$.add fs, div
$.add section, fs
hiddenNum = 0
for ID, thread of ThreadHiding.getHiddenThreads().threads
hiddenNum++
for ID, thread of ReplyHiding.getHiddenPosts().threads
for ID, post of thread
hiddenNum++
$.get items, (items) ->
for key, val of items
inputs[key].checked = val
return
div = $.el 'div',
innerHTML: "<button>Hidden: #{hiddenNum}</button><span class=description>: Clear manually hidden threads and posts on /#{g.BOARD}/."
$.on $('button', div), 'click', ->
innerHTML: "<button></button><span class=description>: Clear manually hidden threads and posts on /#{g.BOARD}/."
button = $ 'button', div
hiddenNum = 0
ThreadHiding.getHiddenThreads (hiddenThreads) ->
for ID, thread of hiddenThreads.threads
hiddenNum++
button.textContent = "Hidden: #{hiddenNum}"
ReplyHiding.getHiddenPosts (hiddenPosts) ->
for ID, thread of hiddenPosts.threads
for ID, post of thread
hiddenNum++
button.textContent = "Hidden: #{hiddenNum}"
$.on button, 'click', ->
@textContent = 'Hidden: 0'
$.delete ["hiddenThreads.#{g.BOARD}", "hiddenPosts.#{g.BOARD}"]
$.after $('input[name="Stubs"]', section).parentNode.parentNode, div
export: ->
now = Date.now()
data =
version: g.VERSION
date: now
Conf: Conf
WatchedThreads: $.get('WatchedThreads', {})
export: (now, data) ->
unless typeof now is 'number'
now = Date.now()
data =
version: g.VERSION
date: now
Conf: Conf
$.get 'WatchedThreads', {}, (item) ->
data.WatchedThreads = item.WatchedThreads
Settings.export now, data
a = $.el 'a',
className: 'warning'
textContent: 'Save me!'
@ -478,8 +496,9 @@ Settings =
ta = $.el 'textarea',
name: name
className: 'field'
value: $.get name, Conf[name]
spellcheck: false
$.get name, Conf[name], (item) ->
ta.value = item[name]
$.on ta, 'change', $.cb.value
$.add div, ta
return
@ -529,7 +548,8 @@ Settings =
<textarea name=sauces class=field spellcheck=false></textarea>
"""
sauce = $ 'textarea', section
sauce.value = $.get 'sauces', Conf['sauces']
$.get 'sauces', Conf['sauces'], (item) ->
sauce.value = item['sauces']
$.on sauce, 'change', $.cb.value
rice: (section) ->
@ -592,17 +612,25 @@ Settings =
<textarea name=usercss class=field spellcheck=false #{if Conf['Custom CSS'] then '' else 'disabled'}></textarea>
</fieldset>
"""
items = {}
inputs = {}
for name in ['boardnav', 'time', 'backlink', 'fileInfo', 'favicon', 'usercss']
input = $ "[name=#{name}]", section
input.value = $.get name, Conf[name]
items[name] = Conf[name]
inputs[name] = input
event = if name in ['favicon', 'usercss']
'change'
else
'input'
$.on input, event, $.cb.value
unless name in ['usercss']
$.on input, event, Settings[name]
Settings[name].call input
$.get items, (items) ->
for key, val of items
input = inputs[key]
input.value = val
unless key in ['usercss']
$.on input, event, Settings[key]
Settings[key].call input
return
$.on $('input[name="Custom CSS"]', section), 'change', Settings.togglecss
$.on $.id('apply-css'), 'click', Settings.usercss
boardnav: ->
@ -652,17 +680,23 @@ Settings =
<tr><th>Actions</th><th>Keybinds</th></tr>
</tbody></table>
"""
tbody = $ 'tbody', section
tbody = $ 'tbody', section
items = {}
inputs = {}
for key, arr of Config.hotkeys
tr = $.el 'tr',
innerHTML: "<td>#{arr[1]}</td><td><input class=field></td>"
input = $ 'input', tr
input.name = key
input.value = $.get key, Conf[key]
input.name = key
input.spellcheck = false
items[key] = Conf[key]
inputs[key] = input
$.on input, 'keydown', Settings.keybind
$.add tbody, tr
return
$.get items, (items) ->
for key, val of items
inputs[key].value = val
return
keybind: (e) ->
return if e.keyCode is 9 # tab
e.preventDefault()
@ -990,13 +1024,14 @@ Filter =
re += ';op:yes'
# Add a new line before the regexp unless the text is empty.
save = $.get type, ''
save =
if save
"#{save}\n#{re}"
else
re
$.set type, save
$.get type, '', (item) ->
save = item[type]
save =
if save
"#{save}\n#{re}"
else
re
$.set type, save
# Open the settings and display & focus the relevant filter textarea.
Settings.open 'Filter'
@ -1026,8 +1061,10 @@ ThreadHiding =
return unless Conf['Thread Hiding']
$.prepend @OP.nodes.root, ThreadHiding.makeButton @, 'hide'
getHiddenThreads: ->
ThreadHiding.hiddenThreads = $.get "hiddenThreads.#{g.BOARD}", threads: {}
getHiddenThreads: (cb) ->
$.get "hiddenThreads.#{g.BOARD}", threads: {}, (item) ->
ThreadHiding.hiddenThreads = item["hiddenThreads.#{g.BOARD}"]
cb ThreadHiding.hiddenThreads if cb
syncFromCatalog: ->
# Sync hidden threads from the catalog into the index.
@ -1093,16 +1130,16 @@ ThreadHiding =
saveHiddenState: (thread, makeStub) ->
# Get fresh hidden threads.
hiddenThreads = ThreadHiding.getHiddenThreads()
hiddenThreadsCatalog = JSON.parse(localStorage.getItem "4chan-hide-t-#{g.BOARD}") or {}
if thread.isHidden
hiddenThreads.threads[thread] = {makeStub}
hiddenThreadsCatalog[thread] = true
else
delete hiddenThreads.threads[thread]
delete hiddenThreadsCatalog[thread]
$.set "hiddenThreads.#{g.BOARD}", hiddenThreads
localStorage.setItem "4chan-hide-t-#{g.BOARD}", JSON.stringify hiddenThreadsCatalog
ThreadHiding.getHiddenThreads (hiddenThreads) ->
hiddenThreadsCatalog = JSON.parse(localStorage.getItem "4chan-hide-t-#{g.BOARD}") or {}
if thread.isHidden
hiddenThreads.threads[thread] = {makeStub}
hiddenThreadsCatalog[thread] = true
else
delete hiddenThreads.threads[thread]
delete hiddenThreadsCatalog[thread]
$.set "hiddenThreads.#{g.BOARD}", hiddenThreads
localStorage.setItem "4chan-hide-t-#{g.BOARD}", JSON.stringify hiddenThreadsCatalog
toggle: (thread) ->
unless thread instanceof Thread
@ -1173,8 +1210,10 @@ ReplyHiding =
return unless Conf['Reply Hiding']
$.replace $('.sideArrows', @nodes.root), ReplyHiding.makeButton @, 'hide'
getHiddenPosts: ->
ReplyHiding.hiddenPosts = $.get "hiddenPosts.#{g.BOARD}", threads: {}
getHiddenPosts: (cb) ->
$.get "hiddenPosts.#{g.BOARD}", threads: {}, (item) ->
ReplyHiding.hiddenPosts = item["hiddenPosts.#{g.BOARD}"]
cb ReplyHiding.hiddenPosts if cb
menu:
init: ->
@ -1230,7 +1269,7 @@ ReplyHiding =
open: (post) ->
if !post.isReply or post.isClone
return false
thread = ReplyHiding.getHiddenPosts().threads[post.thread]
thread = ReplyHiding.hiddenPosts.threads[post.thread]
unless post.isHidden or data = thread?[post]
return false
ReplyHiding.menu.post = post
@ -1258,7 +1297,7 @@ ReplyHiding =
thisPost = $('input[name=thisPost]', parent).checked
replies = $('input[name=replies]', parent).checked
{post} = ReplyHiding.menu
thread = ReplyHiding.getHiddenPosts().threads[post.thread]
thread = ReplyHiding.hiddenPosts.threads[post.thread]
data = thread?[post]
if thisPost
ReplyHiding.show post, replies
@ -1281,20 +1320,20 @@ ReplyHiding =
saveHiddenState: (post, isHiding, thisPost, makeStub, hideRecursively) ->
# Get fresh hidden posts.
hiddenPosts = ReplyHiding.getHiddenPosts()
if isHiding
unless thread = hiddenPosts.threads[post.thread]
thread = hiddenPosts.threads[post.thread] = {}
thread[post] =
thisPost: thisPost isnt false # undefined -> true
makeStub: makeStub
hideRecursively: hideRecursively
else
thread = hiddenPosts.threads[post.thread]
delete thread[post]
unless Object.keys(thread).length
delete hiddenPosts.threads[post.thread]
$.set "hiddenPosts.#{g.BOARD}", hiddenPosts
ReplyHiding.getHiddenPosts (hiddenPosts) ->
if isHiding
unless thread = hiddenPosts.threads[post.thread]
thread = hiddenPosts.threads[post.thread] = {}
thread[post] =
thisPost: thisPost isnt false # undefined -> true
makeStub: makeStub
hideRecursively: hideRecursively
else
thread = hiddenPosts.threads[post.thread]
delete thread[post]
unless Object.keys(thread).length
delete hiddenPosts.threads[post.thread]
$.set "hiddenPosts.#{g.BOARD}", hiddenPosts
toggle: ->
post = Get.postFromNode @
@ -2461,8 +2500,13 @@ Get =
Get.insert post, root, context
Misc = # super semantic
clearThreads: (key) ->
return unless data = $.get key
clearThreads: (key, data) ->
unless data
$.get key, null, (item) ->
data = item[key]
return unless data
Misc.clearThreads key, data
return
unless Object.keys(data.threads).length
$.delete key
@ -3571,20 +3615,21 @@ Unread =
node: ->
Unread.thread = @
Unread.lastReadPost = $.get("lastReadPosts.#{@board}", threads: {}).threads[@] or 0
Unread.posts = []
Unread.postsQuotingYou = []
Unread.title = d.title
posts = []
for ID, post of @posts
posts.push post if post.isReply
Unread.addPosts posts
if Unread.posts.length
# Scroll to before the first unread post.
$.x('preceding-sibling::div[contains(@class,"postContainer")][1]', Unread.posts[0].nodes.root).scrollIntoView false
else if posts.length
# Scroll to the last read post.
posts[posts.length - 1].nodes.root.scrollIntoView()
$.get "lastReadPosts.#{@board}", threads: {}, (item) =>
Unread.lastReadPost = item["lastReadPosts.#{@board}"].threads[@] or 0
Unread.addPosts posts
if Unread.posts.length
# Scroll to before the first unread post.
$.x('preceding-sibling::div[contains(@class,"postContainer")][1]', Unread.posts[0].nodes.root).scrollIntoView false
else if posts.length
# Scroll to the last read post.
posts[posts.length - 1].nodes.root.scrollIntoView()
$.on d, 'ThreadUpdate', Unread.onUpdate
$.on d, 'scroll visibilitychange', Unread.read
$.on d, 'visibilitychange', Unread.setLine if Conf['Unread Line']
@ -3636,11 +3681,11 @@ Unread =
Unread.postsQuotingYou = Unread.postsQuotingYou[i..]
Unread.update() if e
saveLastReadPost: $.debounce($.SECOND, ->
lastReadPosts = $.get "lastReadPosts.#{Unread.thread.board}", threads: {}
lastReadPosts.threads[Unread.thread] = Unread.lastReadPost
$.set "lastReadPosts.#{Unread.thread.board}", lastReadPosts
)
saveLastReadPost: $.debounce $.SECOND, ->
$.get "lastReadPosts.#{Unread.thread.board}", threads: {}, (item) ->
lastReadPosts = item["lastReadPosts.#{Unread.thread.board}"]
lastReadPosts.threads[Unread.thread] = Unread.lastReadPost
$.set "lastReadPosts.#{Unread.thread.board}", lastReadPosts
setLine: (force) ->
return unless d.hidden or force is true
@ -4068,7 +4113,9 @@ ThreadWatcher =
className: 'favicon'
$.on favicon, 'click', ThreadWatcher.cb.toggle
$.before $('input', @OP.nodes.post), favicon
if g.VIEW is 'thread' and @ID is $.get 'AutoWatch', 0
return if g.VIEW isnt 'thread'
$.get 'AutoWatch', 0, (item) =>
return if item['AutoWatch'] isnt @ID
ThreadWatcher.watch @
$.delete 'AutoWatch'
@ -4078,7 +4125,10 @@ ThreadWatcher =
$.add d.body, ThreadWatcher.dialog
refresh: (watched) ->
watched or= $.get 'WatchedThreads', {}
unless watched
$.get 'WatchedThreads', {}, (item) ->
ThreadWatcher.refresh item['WatchedThreads']
return
nodes = [$('.move', ThreadWatcher.dialog)]
for board of watched
for id, props of watched[board]
@ -4126,17 +4176,19 @@ ThreadWatcher =
ThreadWatcher.unwatch thread.board, thread.ID
unwatch: (board, threadID) ->
watched = $.get 'WatchedThreads', {}
delete watched[board][threadID]
delete watched[board] unless Object.keys(watched[board]).length
ThreadWatcher.refresh watched
$.set 'WatchedThreads', watched
$.get 'WatchedThreads', {}, (item) ->
watched = item['WatchedThreads']
delete watched[board][threadID]
delete watched[board] unless Object.keys(watched[board]).length
ThreadWatcher.refresh watched
$.set 'WatchedThreads', watched
watch: (thread) ->
watched = $.get 'WatchedThreads', {}
watched[thread.board] or= {}
watched[thread.board][thread] =
href: "/#{thread.board}/res/#{thread}"
textContent: Get.threadExcerpt thread
ThreadWatcher.refresh watched
$.set 'WatchedThreads', watched
$.get 'WatchedThreads', {}, (item) ->
watched = item['WatchedThreads']
watched[thread.board] or= {}
watched[thread.board][thread] =
href: "/#{thread.board}/res/#{thread}"
textContent: Get.threadExcerpt thread
ThreadWatcher.refresh watched
$.set 'WatchedThreads', watched

View File

@ -281,21 +281,24 @@ class Clone extends Post
Main =
init: ->
# flatten Config into Conf
# and get saved or default values
flatten = (parent, obj) ->
if obj instanceof Array
Conf[parent] = obj[0]
else if typeof obj is 'object'
for key, val of obj
flatten key, val
else # string or number
Conf[parent] = obj
init: (items) ->
unless items
# flatten Config into Conf
# and get saved or default values
flatten = (parent, obj) ->
if obj instanceof Array
Conf[parent] = obj[0]
else if typeof obj is 'object'
for key, val of obj
flatten key, val
else # string or number
Conf[parent] = obj
return
flatten null, Config
$.get Conf, Main.init
return
flatten null, Config
for key, val of Conf
Conf[key] = $.get key, val
Conf = items
pathname = location.pathname.split '/'
g.BOARD = new Board pathname[1]
@ -499,20 +502,24 @@ Main =
# After that, check for updates every day if we still haven't updated.
now = Date.now()
freq = <% if (type === 'userjs') { %>6 * $.HOUR<% } else { %>7 * $.DAY<% } %>
if $.get('lastupdate', 0) > now - freq or $.get('lastchecked', 0) > now - $.DAY
return
$.ajax '<%= meta.page %><%= meta.buildsPath %>version', onload: ->
return unless @status is 200
version = @response
return unless /^\d\.\d+\.\d+$/.test version
if g.VERSION is version
# Don't check for updates too frequently if there wasn't one in a 'long' time.
$.set 'lastupdate', now
items =
lastupdate: 0
lastchecked: 0
$.get items, (items) ->
if items.lastupdate > now - freq or items.lastchecked > now - $.DAY
return
$.set 'lastchecked', now
el = $.el 'span',
innerHTML: "Update: <%= meta.name %> v#{version} is out, get it <a href=<%= meta.page %> target=_blank>here</a>."
new Notification 'info', el, 2 * $.MINUTE
$.ajax '<%= meta.page %><%= meta.buildsPath %>version', onload: ->
return unless @status is 200
version = @response
return unless /^\d\.\d+\.\d+$/.test version
if g.VERSION is version
# Don't check for updates too frequently if there wasn't one in a 'long' time.
$.set 'lastupdate', now
return
$.set 'lastchecked', now
el = $.el 'span',
innerHTML: "Update: <%= meta.name %> v#{version} is out, get it <a href=<%= meta.page %> target=_blank>here</a>."
new Notification 'info', el, 2 * $.MINUTE
handleErrors: (errors) ->
unless 'length' of errors

View File

@ -92,7 +92,8 @@ QR =
if yourPosts
QR.yourPosts = yourPosts
return
QR.yourPosts = $.get "yourPosts.#{g.BOARD}", threads: {}
$.get "yourPosts.#{g.BOARD}", threads: {}, (item) ->
QR.syncYourPosts item["yourPosts.#{g.BOARD}"]
$.sync "yourPosts.#{g.BOARD}", QR.syncYourPosts
error: (err) ->
@ -145,10 +146,11 @@ QR =
sage: if board is 'q' then 600 else 60
file: if board is 'q' then 300 else 30
post: if board is 'q' then 60 else 30
QR.cooldown.cooldowns = $.get "cooldown.#{board}", {}
QR.cooldown.upSpd = 0
QR.cooldown.upSpdAccuracy = .5
QR.cooldown.start()
$.get "cooldown.#{board}", {}, (item) ->
QR.cooldown.cooldowns = item["cooldown.#{board}"]
QR.cooldown.start()
$.sync "cooldown.#{board}", QR.cooldown.sync
start: ->
return if QR.cooldown.isCounting
@ -358,15 +360,6 @@ QR =
posts: []
post: class
constructor: ->
# set values, or null, to avoid 'undefined' values in inputs
prev = QR.posts[QR.posts.length - 1]
persona = $.get 'QR.persona', {}
@name = if prev then prev.name else persona.name or null
@email = if prev and !/^sage$/.test prev.email then prev.email else persona.email or null
@sub = if prev and Conf['Remember Subject'] then prev.sub else if Conf['Remember Subject'] then persona.sub else null
@spoiler = if prev and Conf['Remember Spoiler'] then prev.spoiler else false
@com = null
el = $.el 'a',
className: 'qr-preview'
draggable: true
@ -393,8 +386,29 @@ QR =
for event in ['dragStart', 'dragEnter', 'dragLeave', 'dragOver', 'dragEnd', 'drop']
$.on el, event.toLowerCase(), @[event]
@unlock()
QR.posts.push @
# set values, or null, to avoid 'undefined' values in inputs
@com = null
@spoiler = if prev and Conf['Remember Spoiler']
prev.spoiler
else
false
prev = QR.posts[QR.posts.length - 1]
$.get 'QR.persona', {}, (item) =>
persona = item['QR.persona']
@name = if prev
prev.name
else
persona.name or null
@email = if prev and !/^sage$/.test prev.email
prev.email
else
persona.email or null
@sub = if Conf['Remember Subject']
if prev then prev.sub else persona.sub
else
null
@unlock()
rm: ->
$.rm @nodes.el
index = QR.posts.indexOf @
@ -606,8 +620,9 @@ QR =
$.on imgContainer, 'click', @reload.bind @
$.on input, 'keydown', @keydown.bind @
$.get 'captchas', [], (item) =>
@sync item['captchas']
$.sync 'captchas', @sync
@sync $.get 'captchas', []
# start with an uncached captcha
@reload()
@ -960,12 +975,13 @@ QR =
QR.cleanNotifications()
QR.notifications.push new Notification 'success', h1.textContent, 5
persona = $.get 'QR.persona', {}
persona =
name: post.name
email: if /^sage$/.test post.email then persona.email else post.email
sub: if Conf['Remember Subject'] then post.sub else null
$.set 'QR.persona', persona
$.get 'QR.persona', {}, (item) ->
persona = item['QR.persona']
persona =
name: post.name
email: if /^sage$/.test post.email then persona.email else post.email
sub: if Conf['Remember Subject'] then post.sub else null
$.set 'QR.persona', persona
[_, threadID, postID] = h1.nextSibling.textContent.match /thread:(\d+),no:(\d+)/
postID = +postID