From f213484245c37a53122e593b90d18e2475a2382a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Saparelli?= Date: Tue, 8 Nov 2011 18:33:55 +1300 Subject: [PATCH] Add DOM Storage setters --- README.markdown | 13 +++-- dynworker.coffee | 104 +++++++++++++++++++++++++++++++----- dynworker.js | 133 ++++++++++++++++++++++++++++++++++++++++------- index.html | 2 +- 4 files changed, 213 insertions(+), 39 deletions(-) diff --git a/README.markdown b/README.markdown index 572f46b..0d5ce71 100644 --- a/README.markdown +++ b/README.markdown @@ -11,7 +11,7 @@ extra file you will ever have to load is `dynworker.js`. DynWorker contains a few utilities to augment your worker. You can easily inject functions into the worker, run arbitrary code, pass messages containing mixed -data (everything is JSON-encoded), and access DOM storage. +data (everything is JSON-encoded), and access DOM storage (setters only atm). Future developments may include being able to clone workers, access the DOM asynchronously, make full XHR requests (including XML parsing), access the indexedDB directly from @@ -46,9 +46,10 @@ worker.inject("funcName", function(arg1, arg2) { return result; }); -// The callback gets back the raw event -worker.receive(function(e) { - e.data; // Display and strike awe +// The callback gets back the raw event and the +// parsed data +worker.receive(function(e, data) { + data; // Display and strike awe }); @@ -73,7 +74,9 @@ Inside the worker: ```javascript DynWorker.localStorage.setItem("key", "data"); -var data = DynWorker.localStorage.getItem("key"); +DynWorker.localStorage.getItem("key", function(data) { + // Getters are not implemented yet +}); ``` diff --git a/dynworker.coffee b/dynworker.coffee index bed971b..1fd004c 100644 --- a/dynworker.coffee +++ b/dynworker.coffee @@ -1,26 +1,33 @@ ### -DynWorker is Copyright Félix "passcod" Saparelli +DynWorker is copyright Félix "passcod" Saparelli and licensed under MIT: http://mit.passcod.net ### "use strict"; DynWorker = (path = DynWorker.libpath) -> + # The internal worker worker = new Worker(path) + # Adds a listener to the "onmessage" event. The `e` object + # cannot be touched, so we also pass a second parameter that + # contains the unserialized data. receive = (func) -> callback = (e) -> - e.data = JSON.parse e.data - func e + func e, JSON.parse e.data worker.addEventListener "message", callback, false + # A helper method for devs! log = -> - receive (e) -> - console.log e.data + receive (e, data) -> + console.log data + # Send a message to the worker. The message is JSON-ified, so + # you can pass in whatever will get properly serialized. send = (msg) -> - worker.postMessage(JSON.strinigfy msg) + worker.postMessage(JSON.stringify msg) + # Send a command to the worker. Args optional cmd = (action, args...) -> msg = {DynWorkerAction: action, args: 0} objAdd = (arg) -> @@ -29,20 +36,54 @@ DynWorker = (path = DynWorker.libpath) -> objAdd arg for arg in args send msg - + # Eval code in the worker weval = (code) -> cmd 'eval', code + # Inject a function in the worker. The function can be anonymous or + # named, but the name will get stripped anyway, so you have to be + # careful when writing recursive functions to use the internal name. inject = (name, func) -> + # There's two kinds of functions: named and anonymous. We want them + # all to be anonymous, so we can easily store them. sfunc = func.toString() unless /^function \(/.test(sfunc) sfunc.replace(/^function [^\(]+\(/, 'function (') + # Namespace the function inside the worker weval "DynWorker.ns['#{name}']=#{sfunc};" + # Run a function in the worker and pass it args run = (name, args...) -> + # Send up what the function returns weval "DynWorker.send(DynWorker.ns['#{name}'].apply(null, #{JSON.stringify args}));" + # Use DOM Storage + store = (type, action, key, data) -> + switch action + when 'set' + if type is 'local' + window.localStorage.setItem key, data + else + window.sessionStorage.setItem key, data + + when 'remove' + if type is 'local' + window.localStorage.removeItem key + else + window.sessionStorage.removeItem key + + when 'clear' + if type is 'local' + window.localStorage.clear() + else + window.sessionStorage.clear() + + # Respond to commands from worker + receive (e, data) -> + switch data['DynWorkerAction'] + when 'domstorage' then store data.arg1, data.arg2, data.arg3, data.arg4 + return { receive: receive log: log @@ -53,21 +94,31 @@ DynWorker = (path = DynWorker.libpath) -> run: run } -DynWorker.ns = {} - +# Default path and method to set it differently DynWorker.libpath = "dynworker.js" DynWorker.path = (path) -> if path then DynWorker.libpath = path + +# The following functions make up the in-worker API. They probably shouldn't +# be defined here, but rather added when needed. For now it's OK, though. + +# Namespace for injected functions +DynWorker.ns = {} + +# Receive a message from the parent (main) thread DynWorker.receive = (func) -> callback = (e) -> - e.data = JSON.parse e.data - func e + # Can't touch `e`, so use a second parameter + func e, JSON.parse e.data self.addEventListener "message", callback, false +# Send a message "up" (to the parent/main thread) DynWorker.send = (msg) -> self.postMessage(JSON.stringify msg) +# Send a command to the parent (main) thread. This works +# the same as worker.cmd does in the main thread. DynWorker.cmd = (action, args...) -> msg = {DynWorkerAction: action, args: 0} objAdd = (arg) -> @@ -76,12 +127,37 @@ DynWorker.cmd = (action, args...) -> objAdd arg for arg in args DynWorker.send msg +# Use DOM Storage from the worker. Everything is async, +# and the getters take a callback method instead of returning. +DynWorker.localStorage = { + setItem: (key, data) -> + DynWorker.cmd 'domstorage', 'local', 'set', key, data + + removeItem: (key) -> + DynWorker.cmd 'domstorage', 'local', 'remove', key + + clear: -> + DynWorker.cmd 'domstorage', 'local', 'clear' +} +DynWorker.sessionStorage = { + setItem: (key, data) -> + DynWorker.cmd 'domstorage', 'session', 'set', key, data + + removeItem: (key) -> + DynWorker.cmd 'domstorage', 'session', 'remove', key + + clear: -> + DynWorker.cmd 'domstorage', 'session', 'clear' +} + if typeof window == "undefined" + # This is a worker self.DynWorker = DynWorker - DynWorker.receive (e) -> - switch e.data['DynWorkerAction'] - when 'eval' then eval e.data['arg1'] + DynWorker.receive (e, data) -> + switch data['DynWorkerAction'] + when 'eval' then eval data['arg1'] else + # This is the main thread window.DynWorker = DynWorker diff --git a/dynworker.js b/dynworker.js index 885a3fd..2b34332 100644 --- a/dynworker.js +++ b/dynworker.js @@ -1,39 +1,51 @@ (function() { /* - DynWorker is Copyright 2011- Félix "passcod" Saparelli - and licensed under MIT: http://passcod.mit-license.org + DynWorker is copyright Félix "passcod" Saparelli + and licensed under MIT: http://mit.passcod.net */ "use strict"; var DynWorker; var __slice = Array.prototype.slice; - self.onmessage = function(e) { - switch (e.data['DynWorkerAction']) { - case 'eval': - return eval(e.data['code']); - } - }; DynWorker = function(path) { - var inject, log, receive, run, send, weval, worker; + var cmd, inject, log, receive, run, send, store, weval, worker; if (path == null) { path = DynWorker.libpath; } worker = new Worker(path); - receive = function(callback) { + receive = function(func) { + var callback; + callback = function(e) { + return func(e, JSON.parse(e.data)); + }; return worker.addEventListener("message", callback, false); }; log = function() { - return receive(function(e) { - return console.log(e.data); + return receive(function(e, data) { + return console.log(data); }); }; send = function(msg) { - return worker.postMessage(msg); + return worker.postMessage(JSON.stringify(msg)); + }; + cmd = function() { + var action, arg, args, msg, objAdd, _i, _len; + action = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : []; + msg = { + DynWorkerAction: action, + args: 0 + }; + objAdd = function(arg) { + msg.args++; + return msg["arg" + msg.args] = arg; + }; + for (_i = 0, _len = args.length; _i < _len; _i++) { + arg = args[_i]; + objAdd(arg); + } + return send(msg); }; weval = function(code) { - return send({ - DynWorkerAction: 'eval', - code: code - }); + return cmd('eval', code); }; inject = function(name, func) { var sfunc; @@ -48,27 +60,110 @@ name = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : []; return weval("DynWorker.send(DynWorker.ns['" + name + "'].apply(null, " + (JSON.stringify(args)) + "));"); }; + store = function(type, action, key, data) { + switch (action) { + case 'set': + if (type === 'local') { + return window.localStorage.setItem(key, data); + } else { + return window.sessionStorage.setItem(key, data); + } + break; + case 'remove': + if (type === 'local') { + return window.localStorage.removeItem(key); + } else { + return window.sessionStorage.removeItem(key); + } + break; + case 'clear': + if (type === 'local') { + return window.localStorage.clear(); + } else { + return window.sessionStorage.clear(); + } + } + }; + receive(function(e, data) { + switch (data['DynWorkerAction']) { + case 'domstorage': + return store(data.arg1, data.arg2, data.arg3, data.arg4); + } + }); return { receive: receive, log: log, send: send, + cmd: cmd, eval: weval, inject: inject, run: run }; }; - DynWorker.ns = {}; DynWorker.libpath = "dynworker.js"; DynWorker.path = function(path) { if (path) { return DynWorker.libpath = path; } }; + DynWorker.ns = {}; + DynWorker.receive = function(func) { + var callback; + callback = function(e) { + return func(e, JSON.parse(e.data)); + }; + return self.addEventListener("message", callback, false); + }; DynWorker.send = function(msg) { - return self.postMessage(msg); + return self.postMessage(JSON.stringify(msg)); + }; + DynWorker.cmd = function() { + var action, arg, args, msg, objAdd, _i, _len; + action = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : []; + msg = { + DynWorkerAction: action, + args: 0 + }; + objAdd = function(arg) { + msg.args++; + return msg["arg" + msg.args] = arg; + }; + for (_i = 0, _len = args.length; _i < _len; _i++) { + arg = args[_i]; + objAdd(arg); + } + return DynWorker.send(msg); + }; + DynWorker.localStorage = { + setItem: function(key, data) { + return DynWorker.cmd('domstorage', 'local', 'set', key, data); + }, + removeItem: function(key) { + return DynWorker.cmd('domstorage', 'local', 'remove', key); + }, + clear: function() { + return DynWorker.cmd('domstorage', 'local', 'clear'); + } + }; + DynWorker.sessionStorage = { + setItem: function(key, data) { + return DynWorker.cmd('domstorage', 'session', 'set', key, data); + }, + removeItem: function(key) { + return DynWorker.cmd('domstorage', 'session', 'remove', key); + }, + clear: function() { + return DynWorker.cmd('domstorage', 'session', 'clear'); + } }; if (typeof window === "undefined") { self.DynWorker = DynWorker; + DynWorker.receive(function(e, data) { + switch (data['DynWorkerAction']) { + case 'eval': + return eval(data['arg1']); + } + }); } else { window.DynWorker = DynWorker; } diff --git a/index.html b/index.html index 54523a1..295afb4 100644 --- a/index.html +++ b/index.html @@ -4,7 +4,7 @@ DynWorker - Dynamic Web Worker - +