diff --git a/.gitignore b/.gitignore index a4e260d..86b8f9d 100644 --- a/.gitignore +++ b/.gitignore @@ -8,7 +8,6 @@ tmp/* *.swp .fhclocal/ dist/ -libs/generated/lawnchair.js test/sync/testServer src/modules-cov/* src-cov/* diff --git a/Gruntfile.js b/Gruntfile.js index 3cf0b30..4aad51f 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -20,30 +20,12 @@ module.exports = function(grunt) { } }, concat: { - lawnchair: { - src: [ - "libs/lawnchair/lawnchair.js", - "libs/lawnchair/lawnchairWindowNameStorageAdapter.js", - "libs/lawnchair/lawnchairLocalStorageAdapter.js", - "libs/lawnchair/lawnchairWebkitSqlAdapter.js", - "libs/lawnchair/lawnchairIndexDbAdapter.js", - "libs/lawnchair/lawnchairHtml5FileSystem.js", - "libs/lawnchair/lawnchairMemoryAdapter.js" - ], - dest: "libs/generated/lawnchair.js" - }, crypto: { src:[ "libs/cryptojs/cryptojs-core.js", - "libs/cryptojs/cryptojs-enc-base64.js", "libs/cryptojs/cryptojs-cipher-core.js", - "libs/cryptojs/cryptojs-aes.js", - "libs/cryptojs/cryptojs-md5.js", "libs/cryptojs/cryptojs-sha1.js", - "libs/cryptojs/cryptojs-x64-core.js", - "libs/cryptojs/cryptojs-sha256.js", - "libs/cryptojs/cryptojs-sha512.js", - "libs/cryptojs/cryptojs-sha3.js" + "libs/cryptojs/cryptojs-x64-core.js" ], dest: "libs/generated/crypto.js" } @@ -144,7 +126,7 @@ module.exports = function(grunt) { //run tests in phatomjs grunt.registerTask('test', ['jshint:all', 'browserify:dist', 'browserify:require', 'browserify:test', 'connect:server', 'mocha_phantomjs:test']); - grunt.registerTask('concat-core-sdk', ['jshint', 'concat:lawnchair', 'concat:crypto', 'browserify:dist']); + grunt.registerTask('concat-core-sdk', ['jshint', 'concat:crypto', 'browserify:dist']); grunt.registerTask('build', ['concat-core-sdk', 'uglify:dist']); diff --git a/libs/generated/lawnchair.js b/libs/generated/lawnchair.js new file mode 100644 index 0000000..d5368a2 --- /dev/null +++ b/libs/generated/lawnchair.js @@ -0,0 +1,1354 @@ +/** + * Lawnchair! + * --- + * clientside json store + * + */ +var Lawnchair = function (options, callback) { + // ensure Lawnchair was called as a constructor + if (!(this instanceof Lawnchair)) return new Lawnchair(options, callback); + + // lawnchair requires json + if (!JSON) throw 'JSON unavailable! Include http://www.json.org/json2.js to fix.' + // options are optional; callback is not + if (arguments.length <= 2 && arguments.length > 0) { + callback = (typeof arguments[0] === 'function') ? arguments[0] : arguments[1]; + options = (typeof arguments[0] === 'function') ? {} : arguments[0]; + } else { + throw 'Incorrect # of ctor args!' + } + // TODO perhaps allow for pub/sub instead? + if (typeof callback !== 'function') throw 'No callback was provided'; + + // default configuration + this.record = options.record || 'record' // default for records + this.name = options.name || 'records' // default name for underlying store + + // mixin first valid adapter + var adapter + // if the adapter is passed in we try to load that only + if (options.adapter) { + + // the argument passed should be an array of prefered adapters + // if it is not, we convert it + if(typeof(options.adapter) === 'string'){ + options.adapter = [options.adapter]; + } + + // iterates over the array of passed adapters + for(var j = 0, k = options.adapter.length; j < k; j++){ + + // itirates over the array of available adapters + for (var i = Lawnchair.adapters.length-1; i >= 0; i--) { + if (Lawnchair.adapters[i].adapter === options.adapter[j]) { + adapter = Lawnchair.adapters[i].valid() ? Lawnchair.adapters[i] : undefined; + if (adapter) break + } + } + if (adapter) break + } + + // otherwise find the first valid adapter for this env + } + else { + for (var i = 0, l = Lawnchair.adapters.length; i < l; i++) { + adapter = Lawnchair.adapters[i].valid() ? Lawnchair.adapters[i] : undefined + if (adapter) break + } + } + + // we have failed + if (!adapter) throw 'No valid adapter.' + + // yay! mixin the adapter + for (var j in adapter) + this[j] = adapter[j] + + // call init for each mixed in plugin + for (var i = 0, l = Lawnchair.plugins.length; i < l; i++) + Lawnchair.plugins[i].call(this) + + // init the adapter + this.init(options, callback) +} + +Lawnchair.adapters = [] + +/** + * queues an adapter for mixin + * === + * - ensures an adapter conforms to a specific interface + * + */ +Lawnchair.adapter = function (id, obj) { + // add the adapter id to the adapter obj + // ugly here for a cleaner dsl for implementing adapters + obj['adapter'] = id + // methods required to implement a lawnchair adapter + var implementing = 'adapter valid init keys save batch get exists all remove nuke'.split(' ') + , indexOf = this.prototype.indexOf + // mix in the adapter + for (var i in obj) { + if (indexOf(implementing, i) === -1) throw 'Invalid adapter! Nonstandard method: ' + i + } + // if we made it this far the adapter interface is valid + // insert the new adapter as the preferred adapter + Lawnchair.adapters.splice(0,0,obj) +} + +Lawnchair.plugins = [] + +/** + * generic shallow extension for plugins + * === + * - if an init method is found it registers it to be called when the lawnchair is inited + * - yes we could use hasOwnProp but nobody here is an asshole + */ +Lawnchair.plugin = function (obj) { + for (var i in obj) + i === 'init' ? Lawnchair.plugins.push(obj[i]) : this.prototype[i] = obj[i] +} + +/** + * helpers + * + */ +Lawnchair.prototype = { + + isArray: Array.isArray || function(o) { return Object.prototype.toString.call(o) === '[object Array]' }, + + /** + * this code exists for ie8... for more background see: + * http://www.flickr.com/photos/westcoastlogic/5955365742/in/photostream + */ + indexOf: function(ary, item, i, l) { + if (ary.indexOf) return ary.indexOf(item) + for (i = 0, l = ary.length; i < l; i++) if (ary[i] === item) return i + return -1 + }, + + // awesome shorthand callbacks as strings. this is shameless theft from dojo. + lambda: function (callback) { + return this.fn(this.record, callback) + }, + + // first stab at named parameters for terse callbacks; dojo: first != best // ;D + fn: function (name, callback) { + return typeof callback == 'string' ? new Function(name, callback) : callback + }, + + // returns a unique identifier (by way of Backbone.localStorage.js) + // TODO investigate smaller UUIDs to cut on storage cost + uuid: function () { + var S4 = function () { + return (((1+Math.random())*0x10000)|0).toString(16).substring(1); + } + return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4()); + }, + + // a classic iterator + each: function (callback) { + var cb = this.lambda(callback) + // iterate from chain + if (this.__results) { + for (var i = 0, l = this.__results.length; i < l; i++) cb.call(this, this.__results[i], i) + } + // otherwise iterate the entire collection + else { + this.all(function(r) { + for (var i = 0, l = r.length; i < l; i++) cb.call(this, r[i], i) + }) + } + return this + } +// -- +}; +// window.name code courtesy Remy Sharp: http://24ways.org/2009/breaking-out-the-edges-of-the-browser +Lawnchair.adapter('window-name', (function() { + if (typeof window==='undefined') { + window = { top: { } }; // node/optimizer compatibility + } + + // edited from the original here by elsigh + // Some sites store JSON data in window.top.name, but some folks (twitter on iPad) + // put simple strings in there - we should make sure not to cause a SyntaxError. + var data = {} + try { + data = JSON.parse(window.top.name) + } catch (e) {} + + + return { + + valid: function () { + return typeof window.top.name != 'undefined' + }, + + init: function (options, callback) { + data[this.name] = data[this.name] || {index:[],store:{}} + this.index = data[this.name].index + this.store = data[this.name].store + this.fn(this.name, callback).call(this, this) + return this + }, + + keys: function (callback) { + this.fn('keys', callback).call(this, this.index) + return this + }, + + save: function (obj, cb) { + // data[key] = value + ''; // force to string + // window.top.name = JSON.stringify(data); + var key = obj.key || this.uuid() + this.exists(key, function(exists) { + if (!exists) { + if (obj.key) delete obj.key + this.index.push(key) + } + this.store[key] = obj + + try { + window.top.name = JSON.stringify(data) // TODO wow, this is the only diff from the memory adapter + } catch(e) { + // restore index/store to previous value before JSON exception + if (!exists) { + this.index.pop(); + delete this.store[key]; + } + throw e; + } + + if (cb) { + obj.key = key + this.lambda(cb).call(this, obj) + } + }) + return this + }, + + batch: function (objs, cb) { + var r = [] + for (var i = 0, l = objs.length; i < l; i++) { + this.save(objs[i], function(record) { + r.push(record) + }) + } + if (cb) this.lambda(cb).call(this, r) + return this + }, + + get: function (keyOrArray, cb) { + var r; + if (this.isArray(keyOrArray)) { + r = [] + for (var i = 0, l = keyOrArray.length; i < l; i++) { + r.push(this.store[keyOrArray[i]]) + } + } else { + r = this.store[keyOrArray] + if (r) r.key = keyOrArray + } + if (cb) this.lambda(cb).call(this, r) + return this + }, + + exists: function (key, cb) { + this.lambda(cb).call(this, !!(this.store[key])) + return this + }, + + all: function (cb) { + var r = [] + for (var i = 0, l = this.index.length; i < l; i++) { + var obj = this.store[this.index[i]] + obj.key = this.index[i] + r.push(obj) + } + this.fn(this.name, cb).call(this, r) + return this + }, + + remove: function (keyOrArray, cb) { + var del = this.isArray(keyOrArray) ? keyOrArray : [keyOrArray] + for (var i = 0, l = del.length; i < l; i++) { + var key = del[i].key ? del[i].key : del[i] + var where = this.indexOf(this.index, key) + if (where < 0) continue /* key not present */ + delete this.store[key] + this.index.splice(where, 1) + } + window.top.name = JSON.stringify(data) + if (cb) this.lambda(cb).call(this) + return this + }, + + nuke: function (cb) { + this.store = data[this.name].store = {} + this.index = data[this.name].index = [] + window.top.name = JSON.stringify(data) + if (cb) this.lambda(cb).call(this) + return this + } + } +///// +})()) +/** + * dom storage adapter + * === + * - originally authored by Joseph Pecoraro + * + */ +// +// TODO does it make sense to be chainable all over the place? +// chainable: nuke, remove, all, get, save, all +// not chainable: valid, keys +// +Lawnchair.adapter('dom', (function() { + var storage = null; + try{ + storage = window.localStorage; + }catch(e){ + + } + // the indexer is an encapsulation of the helpers needed to keep an ordered index of the keys + var indexer = function(name) { + return { + // the key + key: name + '._index_', + // returns the index + all: function() { + var a = storage.getItem(this.key) + if (a) { + a = JSON.parse(a) + } + if (a === null) storage.setItem(this.key, JSON.stringify([])) // lazy init + return JSON.parse(storage.getItem(this.key)) + }, + // adds a key to the index + add: function (key) { + var a = this.all() + a.push(key) + storage.setItem(this.key, JSON.stringify(a)) + }, + // deletes a key from the index + del: function (key) { + var a = this.all(), r = [] + // FIXME this is crazy inefficient but I'm in a strata meeting and half concentrating + for (var i = 0, l = a.length; i < l; i++) { + if (a[i] != key) r.push(a[i]) + } + storage.setItem(this.key, JSON.stringify(r)) + }, + // returns index for a key + find: function (key) { + var a = this.all() + for (var i = 0, l = a.length; i < l; i++) { + if (key === a[i]) return i + } + return false + } + } + } + + // adapter api + return { + + // ensure we are in an env with localStorage + valid: function () { + return !!storage && function() { + // in mobile safari if safe browsing is enabled, window.storage + // is defined but setItem calls throw exceptions. + var success = true + var value = Math.random() + try { + storage.setItem(value, value) + } catch (e) { + success = false + } + storage.removeItem(value) + return success + }() + }, + + init: function (options, callback) { + this.indexer = indexer(this.name) + if (callback) this.fn(this.name, callback).call(this, this) + }, + + save: function (obj, callback) { + var key = obj.key ? this.name + '.' + obj.key : this.name + '.' + this.uuid() + // now we kil the key and use it in the store colleciton + delete obj.key; + storage.setItem(key, JSON.stringify(obj)) + // if the key is not in the index push it on + if (this.indexer.find(key) === false) this.indexer.add(key) + obj.key = key.slice(this.name.length + 1) + if (callback) { + this.lambda(callback).call(this, obj) + } + return this + }, + + batch: function (ary, callback) { + var saved = [] + // not particularily efficient but this is more for sqlite situations + for (var i = 0, l = ary.length; i < l; i++) { + this.save(ary[i], function(r){ + saved.push(r) + }) + } + if (callback) this.lambda(callback).call(this, saved) + return this + }, + + // accepts [options], callback + keys: function(callback) { + if (callback) { + var name = this.name + var indices = this.indexer.all(); + var keys = []; + //Checking for the support of map. + if(Array.prototype.map) { + keys = indices.map(function(r){ return r.replace(name + '.', '') }) + } else { + for (var key in indices) { + keys.push(key.replace(name + '.', '')); + } + } + this.fn('keys', callback).call(this, keys) + } + return this // TODO options for limit/offset, return promise + }, + + get: function (key, callback) { + if (this.isArray(key)) { + var r = [] + for (var i = 0, l = key.length; i < l; i++) { + var k = this.name + '.' + key[i] + var obj = storage.getItem(k) + if (obj) { + obj = JSON.parse(obj) + obj.key = key[i] + } + r.push(obj) + } + if (callback) this.lambda(callback).call(this, r) + } else { + var k = this.name + '.' + key + var obj = storage.getItem(k) + if (obj) { + obj = JSON.parse(obj) + obj.key = key + } + if (callback) this.lambda(callback).call(this, obj) + } + return this + }, + + exists: function (key, cb) { + var exists = this.indexer.find(this.name+'.'+key) === false ? false : true ; + this.lambda(cb).call(this, exists); + return this; + }, + // NOTE adapters cannot set this.__results but plugins do + // this probably should be reviewed + all: function (callback) { + var idx = this.indexer.all() + , r = [] + , o + , k + for (var i = 0, l = idx.length; i < l; i++) { + k = idx[i] //v + o = JSON.parse(storage.getItem(k)) + o.key = k.replace(this.name + '.', '') + r.push(o) + } + if (callback) this.fn(this.name, callback).call(this, r) + return this + }, + + remove: function (keyOrArray, callback) { + var self = this; + if (this.isArray(keyOrArray)) { + // batch remove + var i, done = keyOrArray.length; + var removeOne = function(i) { + self.remove(keyOrArray[i], function() { + if ((--done) > 0) { return; } + if (callback) { + self.lambda(callback).call(self); + } + }); + }; + for (i=0; i < keyOrArray.length; i++) + removeOne(i); + return this; + } + var key = this.name + '.' + + ((keyOrArray.key) ? keyOrArray.key : keyOrArray) + this.indexer.del(key) + storage.removeItem(key) + if (callback) this.lambda(callback).call(this) + return this + }, + + nuke: function (callback) { + this.all(function(r) { + for (var i = 0, l = r.length; i < l; i++) { + this.remove(r[i]); + } + if (callback) this.lambda(callback).call(this) + }) + return this + } + }})()); +Lawnchair.adapter('webkit-sqlite', (function() { + // private methods + var fail = function(e, i) { + if (console) { + console.log('error in sqlite adaptor!', e, i) + } + }, now = function() { + return new Date() + } // FIXME need to use better date fn + // not entirely sure if this is needed... + + // public methods + return { + + valid: function() { + return !!(window.openDatabase) + }, + + init: function(options, callback) { + var that = this, + cb = that.fn(that.name, callback), + create = "CREATE TABLE IF NOT EXISTS " + this.record + " (id NVARCHAR(32) UNIQUE PRIMARY KEY, value TEXT, timestamp REAL)", + win = function() { + return cb.call(that, that); + } + // open a connection and create the db if it doesn't exist + //FEEDHENRY CHANGE TO ALLOW ERROR CALLBACK + if (options && 'function' === typeof options.fail) fail = options.fail + //END CHANGE + this.db = openDatabase(this.name, '1.0.0', this.name, 65536) + this.db.transaction(function(t) { + t.executeSql(create, [], win, fail) + }) + }, + + keys: function(callback) { + var cb = this.lambda(callback), + that = this, + keys = "SELECT id FROM " + this.record + " ORDER BY timestamp DESC" + + this.db.readTransaction(function(t) { + var win = function(xxx, results) { + if (results.rows.length == 0) { + cb.call(that, []) + } else { + var r = []; + for (var i = 0, l = results.rows.length; i < l; i++) { + r.push(results.rows.item(i).id); + } + cb.call(that, r) + } + } + t.executeSql(keys, [], win, fail) + }) + return this + }, + // you think thats air you're breathing now? + save: function(obj, callback, error) { + var that = this + objs = (this.isArray(obj) ? obj : [obj]).map(function(o) { + if (!o.key) { + o.key = that.uuid() + } + return o + }), + ins = "INSERT OR REPLACE INTO " + this.record + " (value, timestamp, id) VALUES (?,?,?)", + win = function() { + if (callback) { + that.lambda(callback).call(that, that.isArray(obj) ? objs : objs[0]) + } + }, error = error || function() {}, insvals = [], + ts = now() + + try { + for (var i = 0, l = objs.length; i < l; i++) { + insvals[i] = [JSON.stringify(objs[i]), ts, objs[i].key]; + } + } catch (e) { + fail(e) + throw e; + } + + that.db.transaction(function(t) { + for (var i = 0, l = objs.length; i < l; i++) + t.executeSql(ins, insvals[i]) + }, function(e, i) { + fail(e, i) + }, win) + + return this + }, + + + batch: function(objs, callback) { + return this.save(objs, callback) + }, + + get: function(keyOrArray, cb) { + var that = this, + sql = '', + args = this.isArray(keyOrArray) ? keyOrArray : [keyOrArray]; + // batch selects support + sql = 'SELECT id, value FROM ' + this.record + " WHERE id IN (" + + args.map(function() { + return '?' + }).join(",") + ")" + // FIXME + // will always loop the results but cleans it up if not a batch return at the end.. + // in other words, this could be faster + var win = function(xxx, results) { + var o, r, lookup = {} + // map from results to keys + for (var i = 0, l = results.rows.length; i < l; i++) { + o = JSON.parse(results.rows.item(i).value) + o.key = results.rows.item(i).id + lookup[o.key] = o; + } + r = args.map(function(key) { + return lookup[key]; + }); + if (!that.isArray(keyOrArray)) r = r.length ? r[0] : null + if (cb) that.lambda(cb).call(that, r) + } + this.db.readTransaction(function(t) { + t.executeSql(sql, args, win, fail) + }) + return this + }, + + exists: function(key, cb) { + var is = "SELECT * FROM " + this.record + " WHERE id = ?", + that = this, + win = function(xxx, results) { + if (cb) that.fn('exists', cb).call(that, (results.rows.length > 0)) + } + this.db.readTransaction(function(t) { + t.executeSql(is, [key], win, fail) + }) + return this + }, + + all: function(callback) { + var that = this, + all = "SELECT * FROM " + this.record, + r = [], + cb = this.fn(this.name, callback) || undefined, + win = function(xxx, results) { + if (results.rows.length != 0) { + for (var i = 0, l = results.rows.length; i < l; i++) { + var obj = JSON.parse(results.rows.item(i).value) + obj.key = results.rows.item(i).id + r.push(obj) + } + } + if (cb) cb.call(that, r) + } + + this.db.readTransaction(function(t) { + t.executeSql(all, [], win, fail) + }) + return this + }, + + remove: function(keyOrArray, cb) { + var that = this, + args, sql = "DELETE FROM " + this.record + " WHERE id ", + win = function() { + if (cb) that.lambda(cb).call(that) + } + if (!this.isArray(keyOrArray)) { + sql += '= ?'; + args = [keyOrArray]; + } else { + args = keyOrArray; + sql += "IN (" + + args.map(function() { + return '?' + }).join(',') + + ")"; + } + args = args.map(function(obj) { + return obj.key ? obj.key : obj; + }); + + this.db.transaction(function(t) { + t.executeSql(sql, args, win, fail); + }); + + return this; + }, + + nuke: function(cb) { + var nuke = "DELETE FROM " + this.record, + that = this, + win = cb ? function() { + that.lambda(cb).call(that) + } : function() {} + this.db.transaction(function(t) { + t.executeSql(nuke, [], win, fail) + }) + return this + } + } +})()); +Lawnchair.adapter('indexed-db', (function(){ + + function fail(e, i) { + if(console) { console.log('error in indexed-db adapter!' + e.message, e, i); debugger;} + } ; + + function getIDB(){ + return window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.oIndexedDB || window.msIndexedDB; + }; + + + + return { + + valid: function() { return !!getIDB(); }, + + init:function(options, callback) { + this.idb = getIDB(); + this.waiting = []; + var request = this.idb.open(this.name, 2); + var self = this; + var cb = self.fn(self.name, callback); + var win = function(){ return cb.call(self, self); } + //FEEDHENRY CHANGE TO ALLOW ERROR CALLBACK + if(options && 'function' === typeof options.fail) fail = options.fail + //END CHANGE + request.onupgradeneeded = function(event){ + self.store = request.result.createObjectStore("teststore", { autoIncrement: true} ); + for (var i = 0; i < self.waiting.length; i++) { + self.waiting[i].call(self); + } + self.waiting = []; + win(); + } + + request.onsuccess = function(event) { + self.db = request.result; + + + if(self.db.version != "2.0") { + if(typeof self.db.setVersion == 'function'){ + + var setVrequest = self.db.setVersion("2.0"); + // onsuccess is the only place we can create Object Stores + setVrequest.onsuccess = function(e) { + self.store = self.db.createObjectStore("teststore", { autoIncrement: true} ); + for (var i = 0; i < self.waiting.length; i++) { + self.waiting[i].call(self); + } + self.waiting = []; + win(); + }; + setVrequest.onerror = function(e) { + // console.log("Failed to create objectstore " + e); + fail(e); + } + + } + } else { + self.store = {}; + for (var i = 0; i < self.waiting.length; i++) { + self.waiting[i].call(self); + } + self.waiting = []; + win(); + } + } + request.onerror = fail; + }, + + save:function(obj, callback) { + if(!this.store) { + this.waiting.push(function() { + this.save(obj, callback); + }); + return; + } + + var self = this; + var win = function (e) { if (callback) { obj.key = e.target.result; self.lambda(callback).call(self, obj) }}; + var accessType = "readwrite"; + var trans = this.db.transaction(["teststore"],accessType); + var store = trans.objectStore("teststore"); + var request = obj.key ? store.put(obj, obj.key) : store.put(obj); + + request.onsuccess = win; + request.onerror = fail; + + return this; + }, + + // FIXME this should be a batch insert / just getting the test to pass... + batch: function (objs, cb) { + + var results = [] + , done = false + , self = this + + var updateProgress = function(obj) { + results.push(obj) + done = results.length === objs.length + } + + var checkProgress = setInterval(function() { + if (done) { + if (cb) self.lambda(cb).call(self, results) + clearInterval(checkProgress) + } + }, 200) + + for (var i = 0, l = objs.length; i < l; i++) + this.save(objs[i], updateProgress) + + return this + }, + + + get:function(key, callback) { + if(!this.store || !this.db) { + this.waiting.push(function() { + this.get(key, callback); + }); + return; + } + + + var self = this; + var win = function (e) { if (callback) { self.lambda(callback).call(self, e.target.result) }}; + + + if (!this.isArray(key)){ + var req = this.db.transaction("teststore").objectStore("teststore").get(key); + + req.onsuccess = win; + req.onerror = function(event) { + //console.log("Failed to find " + key); + fail(event); + }; + + // FIXME: again the setInterval solution to async callbacks.. + } else { + + // note: these are hosted. + var results = [] + , done = false + , keys = key + + var updateProgress = function(obj) { + results.push(obj) + done = results.length === keys.length + } + + var checkProgress = setInterval(function() { + if (done) { + if (callback) self.lambda(callback).call(self, results) + clearInterval(checkProgress) + } + }, 200) + + for (var i = 0, l = keys.length; i < l; i++) + this.get(keys[i], updateProgress) + + } + + return this; + }, + + all:function(callback) { + if(!this.store) { + this.waiting.push(function() { + this.all(callback); + }); + return; + } + var cb = this.fn(this.name, callback) || undefined; + var self = this; + var objectStore = this.db.transaction("teststore").objectStore("teststore"); + var toReturn = []; + objectStore.openCursor().onsuccess = function(event) { + var cursor = event.target.result; + if (cursor) { + toReturn.push(cursor.value); + cursor.continue(); + } + else { + if (cb) cb.call(self, toReturn); + } + }; + return this; + }, + + remove:function(keyOrObj, callback) { + if(!this.store) { + this.waiting.push(function() { + this.remove(keyOrObj, callback); + }); + return; + } + if (typeof keyOrObj == "object") { + keyOrObj = keyOrObj.key; + } + var self = this; + var win = function () { if (callback) self.lambda(callback).call(self) }; + + var request = this.db.transaction(["teststore"], "readwrite").objectStore("teststore").delete(keyOrObj); + request.onsuccess = win; + request.onerror = fail; + return this; + }, + + nuke:function(callback) { + if(!this.store) { + this.waiting.push(function() { + this.nuke(callback); + }); + return; + } + + var self = this + , win = callback ? function() { self.lambda(callback).call(self) } : function(){}; + + try { + this.db + .transaction(["teststore"], "readwrite") + .objectStore("teststore").clear().onsuccess = win; + + } catch(e) { + fail(); + } + return this; + } + + }; + +})()); +Lawnchair.adapter('html5-filesystem', (function(global){ + + var fail = function( e ) { + if ( console ) console.error(e, e.name); + }; + + var ls = function( reader, callback, entries ) { + var result = entries || []; + reader.readEntries(function( results ) { + if ( !results.length ) { + if ( callback ) callback( result.map(function(entry) { return entry.name; }) ); + } else { + ls( reader, callback, result.concat( Array.prototype.slice.call( results ) ) ); + } + }, fail ); + }; + + var filesystems = {}; + + var root = function( store, callback ) { + var directory = filesystems[store.name]; + if ( directory ) { + callback( directory ); + } else { + setTimeout(function() { + root( store, callback ); + }, 10 ); + } + }; + + var isPhoneGap = function() { + //http://stackoverflow.com/questions/10347539/detect-between-a-mobile-browser-or-a-phonegap-application + //may break. + var app = document.URL.indexOf('http://') === -1 && document.URL.indexOf('https://') === -1; + if (app) { + return true; + } else { + return false; + } + } + + var createBlobOrString = function(contentstr) { + var retVal; + if (isPhoneGap()) { // phonegap filewriter works with strings, later versions also work with binary arrays, and if passed a blob will just convert to binary array anyway + retVal = contentstr; + } else { + var targetContentType = 'application/json'; + try { + retVal = new Blob( [contentstr], { type: targetContentType }); // Blob doesn't exist on all androids + } + catch (e){ + // TypeError old chrome and FF + var blobBuilder = window.BlobBuilder || + window.WebKitBlobBuilder || + window.MozBlobBuilder || + window.MSBlobBuilder; + if (e.name == 'TypeError' && blobBuilder) { + var bb = new blobBuilder(); + bb.append([contentstr.buffer]); + retVal = bb.getBlob(targetContentType); + } else { + // We can't make a Blob, so just return the stringified content + retVal = contentstr; + } + } + } + return retVal; + } + + return { + // boolean; true if the adapter is valid for the current environment + valid: function() { + var fs = global.requestFileSystem || global.webkitRequestFileSystem || global.moz_requestFileSystem; + return !!fs; + }, + + // constructor call and callback. 'name' is the most common option + init: function( options, callback ) { + var me = this; + var error = function(e) { fail(e); if ( callback ) me.fn( me.name, callback ).call( me, me ); }; + var size = options.size || 100*1024*1024; + var name = this.name; + //disable file backup to icloud + me.backup = false; + if(typeof options.backup !== 'undefined'){ + me.backup = options.backup; + } + + function requestFileSystem(amount) { +// console.log('in requestFileSystem'); + var fs = global.requestFileSystem || global.webkitRequestFileSystem || global.moz_requestFileSystem; + var mode = window.PERSISTENT; + if(typeof LocalFileSystem !== "undefined" && typeof LocalFileSystem.PERSISTENT !== "undefined"){ + mode = LocalFileSystem.PERSISTENT; + } + fs(mode, amount, function(fs) { +// console.log('got FS ', fs); + fs.root.getDirectory( name, {create:true}, function( directory ) { +// console.log('got DIR ', directory); + filesystems[name] = directory; + if ( callback ) me.fn( me.name, callback ).call( me, me ); + }, function( e ) { +// console.log('error getting dir :: ', e); + error(e); + }); + }, function( e ) { +// console.log('error getting FS :: ', e); + error(e); + }); + }; + + // When in the browser we need to use the html5 file system rather than + // the one cordova supplies, but it needs to request a quota first. + if (typeof navigator.webkitPersistentStorage !== 'undefined') { + navigator.webkitPersistentStorage.requestQuota(size, requestFileSystem, function() { + logger.warn('User declined file storage'); + error('User declined file storage'); + }); + } else { + // Amount is 0 because we pretty much have free reign over the + // amount of storage we use on an android device. + requestFileSystem(0); + } + }, + + // returns all the keys in the store + keys: function( callback ) { + var me = this; + root( this, function( store ) { + ls( store.createReader(), function( entries ) { + if ( callback ) me.fn( 'keys', callback ).call( me, entries ); + }); + }); + return this; + }, + + // save an object + save: function( obj, callback ) { + var me = this; + var key = obj.key || this.uuid(); + obj.key = key; + var error = function(e) { fail(e); if ( callback ) me.lambda( callback ).call( me ); }; + root( this, function( store ) { + var writeContent = function(file, error){ + file.createWriter(function( writer ) { + writer.onerror = error; + writer.onwriteend = function() { + // Clear the onWriteEnd handler so the truncate does not call it and cause an infinite loop + this.onwriteend = null; + // Truncate the file at the end of the written contents. This ensures that if we are updating + // a file which was previously longer, we will not be left with old contents beyond the end of + // the current buffer. + this.truncate(this.position); + if ( callback ) me.lambda( callback ).call( me, obj ); + }; + var contentStr = JSON.stringify(obj); + + var writerContent = createBlobOrString(contentStr); + writer.write(writerContent); + }, error ); + } + store.getFile( key, {create:true}, function( file ) { + if(typeof file.setMetadata === 'function' && (me.backup === false || me.backup === 'false')){ + //set meta data on the file to make sure it won't be backed up by icloud + file.setMetadata(function(){ + writeContent(file, error); + }, function(){ + writeContent(file, error); + }, {'com.apple.MobileBackup': 1}); + } else { + writeContent(file, error); + } + }, error ); + }); + return this; + }, + + // batch save array of objs + batch: function( objs, callback ) { + var me = this; + var saved = []; + for ( var i = 0, il = objs.length; i < il; i++ ) { + me.save( objs[i], function( obj ) { + saved.push( obj ); + if ( saved.length === il && callback ) { + me.lambda( callback ).call( me, saved ); + } + }); + } + return this; + }, + + // retrieve obj (or array of objs) and apply callback to each + get: function( key /* or array */, callback ) { + var me = this; + if ( this.isArray( key ) ) { + var values = []; + for ( var i = 0, il = key.length; i < il; i++ ) { + me.get( key[i], function( result ) { + if ( result ) values.push( result ); + if ( values.length === il && callback ) { + me.lambda( callback ).call( me, values ); + } + }); + } + } else { + var error = function(e) { + fail( e ); + if ( callback ) { + me.lambda( callback ).call( me ); + } + }; + root( this, function( store ) { + store.getFile( key, {create:false}, function( entry ) { + entry.file(function( file ) { + var reader = new FileReader(); + + reader.onerror = error; + + reader.onload = function(e) { + var res = {}; + try { + res = JSON.parse( e.target.result); + res.key = key; + } catch (e) { + res = {key:key}; + } + if ( callback ) me.lambda( callback ).call( me, res ); + }; + + reader.readAsText( file ); + }, error ); + }, error ); + }); + } + return this; + }, + + // check if an obj exists in the collection + exists: function( key, callback ) { + var me = this; + root( this, function( store ) { + store.getFile( key, {create:false}, function() { + if ( callback ) me.lambda( callback ).call( me, true ); + }, function() { + if ( callback ) me.lambda( callback ).call( me, false ); + }); + }); + return this; + }, + + // returns all the objs to the callback as an array + all: function( callback ) { + var me = this; + if ( callback ) { + this.keys(function( keys ) { + if ( !keys.length ) { + me.fn( me.name, callback ).call( me, [] ); + } else { + me.get( keys, function( values ) { + me.fn( me.name, callback ).call( me, values ); + }); + } + }); + } + return this; + }, + + // remove a doc or collection of em + remove: function( key /* or object */, callback ) { + var me = this; + var error = function(e) { fail( e ); if ( callback ) me.lambda( callback ).call( me ); }; + root( this, function( store ) { + store.getFile( (typeof key === 'string' ? key : key.key ), {create:false}, function( file ) { + file.remove(function() { + if ( callback ) me.lambda( callback ).call( me ); + }, error ); + }, error ); + }); + return this; + }, + + // destroy everything + nuke: function( callback ) { + var me = this; + var count = 0; + this.keys(function( keys ) { + if ( !keys.length ) { + if ( callback ) me.lambda( callback ).call( me ); + } else { + for ( var i = 0, il = keys.length; i < il; i++ ) { + me.remove( keys[i], function() { + count++; + if ( count === il && callback ) { + me.lambda( callback ).call( me ); + } + }); + } + } + }); + return this; + } + }; +}(this))); + +Lawnchair.adapter('memory', (function(){ + + var data = {} + + return { + valid: function() { return true }, + + init: function (options, callback) { + data[this.name] = data[this.name] || {index:[],store:{}} + this.index = data[this.name].index + this.store = data[this.name].store + var cb = this.fn(this.name, callback) + if (cb) cb.call(this, this) + return this + }, + + keys: function (callback) { + this.fn('keys', callback).call(this, this.index) + return this + }, + + save: function(obj, cb) { + var key = obj.key || this.uuid() + + this.exists(key, function(exists) { + if (!exists) { + if (obj.key) delete obj.key + this.index.push(key) + } + + this.store[key] = obj + + if (cb) { + obj.key = key + this.lambda(cb).call(this, obj) + } + }) + + return this + }, + + batch: function (objs, cb) { + var r = [] + for (var i = 0, l = objs.length; i < l; i++) { + this.save(objs[i], function(record) { + r.push(record) + }) + } + if (cb) this.lambda(cb).call(this, r) + return this + }, + + get: function (keyOrArray, cb) { + var r; + if (this.isArray(keyOrArray)) { + r = [] + for (var i = 0, l = keyOrArray.length; i < l; i++) { + r.push(this.store[keyOrArray[i]]) + } + } else { + r = this.store[keyOrArray] + if (r) r.key = keyOrArray + } + if (cb) this.lambda(cb).call(this, r) + return this + }, + + exists: function (key, cb) { + this.lambda(cb).call(this, !!(this.store[key])) + return this + }, + + all: function (cb) { + var r = [] + for (var i = 0, l = this.index.length; i < l; i++) { + var obj = this.store[this.index[i]] + obj.key = this.index[i] + r.push(obj) + } + this.fn(this.name, cb).call(this, r) + return this + }, + + remove: function (keyOrArray, cb) { + var del = this.isArray(keyOrArray) ? keyOrArray : [keyOrArray] + for (var i = 0, l = del.length; i < l; i++) { + var key = del[i].key ? del[i].key : del[i] + var where = this.indexOf(this.index, key) + if (where < 0) continue /* key not present */ + delete this.store[key] + this.index.splice(where, 1) + } + if (cb) this.lambda(cb).call(this) + return this + }, + + nuke: function (cb) { + this.store = data[this.name].store = {} + this.index = data[this.name].index = [] + if (cb) this.lambda(cb).call(this) + return this + } + } +///// +})()); \ No newline at end of file diff --git a/libs/lawnchair/lawnchair.js b/libs/lawnchair/lawnchair.js index fa6f06c..8705c34 100644 --- a/libs/lawnchair/lawnchair.js +++ b/libs/lawnchair/lawnchair.js @@ -4,7 +4,7 @@ * clientside json store * */ -var Lawnchair = function (options, callback) { +var Lawnchair = module.exports = function (options, callback) { // ensure Lawnchair was called as a constructor if (!(this instanceof Lawnchair)) return new Lawnchair(options, callback); @@ -162,4 +162,12 @@ Lawnchair.prototype = { return this } // -- -}; \ No newline at end of file +}; + +require('./lawnchairHtml5FileSystem')(Lawnchair) +require('./lawnchairIndexDbAdapter')(Lawnchair) +require('./lawnchairLocalStorageAdapter')(Lawnchair) +require('./lawnchairMemoryAdapter')(Lawnchair) +require('./lawnchairTitanium')(Lawnchair) +require('./lawnchairWebkitSqlAdapter')(Lawnchair) +require('./lawnchairWindowNameStorageAdapter')(Lawnchair) \ No newline at end of file diff --git a/libs/lawnchair/lawnchairHtml5FileSystem.js b/libs/lawnchair/lawnchairHtml5FileSystem.js index d2ded4f..cc57d2a 100644 --- a/libs/lawnchair/lawnchairHtml5FileSystem.js +++ b/libs/lawnchair/lawnchairHtml5FileSystem.js @@ -1,304 +1,307 @@ -Lawnchair.adapter('html5-filesystem', (function(global){ - - var fail = function( e ) { - if ( console ) console.error(e, e.name); - }; - var ls = function( reader, callback, entries ) { - var result = entries || []; - reader.readEntries(function( results ) { - if ( !results.length ) { - if ( callback ) callback( result.map(function(entry) { return entry.name; }) ); - } else { - ls( reader, callback, result.concat( Array.prototype.slice.call( results ) ) ); - } - }, fail ); - }; +module.exports = function (Lawnchair) { + Lawnchair.adapter('html5-filesystem', (function(global){ + + var fail = function( e ) { + if ( console ) console.error(e, e.name); + }; - var filesystems = {}; + var ls = function( reader, callback, entries ) { + var result = entries || []; + reader.readEntries(function( results ) { + if ( !results.length ) { + if ( callback ) callback( result.map(function(entry) { return entry.name; }) ); + } else { + ls( reader, callback, result.concat( Array.prototype.slice.call( results ) ) ); + } + }, fail ); + }; - var root = function( store, callback ) { - var directory = filesystems[store.name]; - if ( directory ) { - callback( directory ); - } else { - setTimeout(function() { - root( store, callback ); - }, 10 ); - } - }; + var filesystems = {}; - var isPhoneGap = function() { - //http://stackoverflow.com/questions/10347539/detect-between-a-mobile-browser-or-a-phonegap-application - //may break. - var app = document.URL.indexOf('http://') === -1 && document.URL.indexOf('https://') === -1; - if (app) { - return true; - } else { - return false; - } - } + var root = function( store, callback ) { + var directory = filesystems[store.name]; + if ( directory ) { + callback( directory ); + } else { + setTimeout(function() { + root( store, callback ); + }, 10 ); + } + }; - var createBlobOrString = function(contentstr) { - var retVal; - if (isPhoneGap()) { // phonegap filewriter works with strings, later versions also work with binary arrays, and if passed a blob will just convert to binary array anyway - retVal = contentstr; - } else { - var targetContentType = 'application/json'; - try { - retVal = new Blob( [contentstr], { type: targetContentType }); // Blob doesn't exist on all androids + var isPhoneGap = function() { + //http://stackoverflow.com/questions/10347539/detect-between-a-mobile-browser-or-a-phonegap-application + //may break. + var app = document.URL.indexOf('http://') === -1 && document.URL.indexOf('https://') === -1; + if (app) { + return true; + } else { + return false; } - catch (e){ - // TypeError old chrome and FF - var blobBuilder = window.BlobBuilder || - window.WebKitBlobBuilder || - window.MozBlobBuilder || - window.MSBlobBuilder; - if (e.name == 'TypeError' && blobBuilder) { - var bb = new blobBuilder(); - bb.append([contentstr.buffer]); - retVal = bb.getBlob(targetContentType); - } else { - // We can't make a Blob, so just return the stringified content - retVal = contentstr; + } + + var createBlobOrString = function(contentstr) { + var retVal; + if (isPhoneGap()) { // phonegap filewriter works with strings, later versions also work with binary arrays, and if passed a blob will just convert to binary array anyway + retVal = contentstr; + } else { + var targetContentType = 'application/json'; + try { + retVal = new Blob( [contentstr], { type: targetContentType }); // Blob doesn't exist on all androids + } + catch (e){ + // TypeError old chrome and FF + var blobBuilder = window.BlobBuilder || + window.WebKitBlobBuilder || + window.MozBlobBuilder || + window.MSBlobBuilder; + if (e.name == 'TypeError' && blobBuilder) { + var bb = new blobBuilder(); + bb.append([contentstr.buffer]); + retVal = bb.getBlob(targetContentType); + } else { + // We can't make a Blob, so just return the stringified content + retVal = contentstr; + } } } + return retVal; } - return retVal; - } - return { - // boolean; true if the adapter is valid for the current environment - valid: function() { - var fs = global.requestFileSystem || global.webkitRequestFileSystem || global.moz_requestFileSystem; - return !!fs; - }, + return { + // boolean; true if the adapter is valid for the current environment + valid: function() { + var fs = global.requestFileSystem || global.webkitRequestFileSystem || global.moz_requestFileSystem; + return !!fs; + }, - // constructor call and callback. 'name' is the most common option - init: function( options, callback ) { - var me = this; - var error = function(e) { fail(e); if ( callback ) me.fn( me.name, callback ).call( me, me ); }; - var size = options.size || 100*1024*1024; - var name = this.name; - //disable file backup to icloud - me.backup = false; - if(typeof options.backup !== 'undefined'){ - me.backup = options.backup; - } + // constructor call and callback. 'name' is the most common option + init: function( options, callback ) { + var me = this; + var error = function(e) { fail(e); if ( callback ) me.fn( me.name, callback ).call( me, me ); }; + var size = options.size || 100*1024*1024; + var name = this.name; + //disable file backup to icloud + me.backup = false; + if(typeof options.backup !== 'undefined'){ + me.backup = options.backup; + } - function requestFileSystem(amount) { -// console.log('in requestFileSystem'); - var fs = global.requestFileSystem || global.webkitRequestFileSystem || global.moz_requestFileSystem; - var mode = window.PERSISTENT; - if(typeof LocalFileSystem !== "undefined" && typeof LocalFileSystem.PERSISTENT !== "undefined"){ - mode = LocalFileSystem.PERSISTENT; - } - fs(mode, amount, function(fs) { -// console.log('got FS ', fs); - fs.root.getDirectory( name, {create:true}, function( directory ) { -// console.log('got DIR ', directory); - filesystems[name] = directory; - if ( callback ) me.fn( me.name, callback ).call( me, me ); + function requestFileSystem(amount) { + // console.log('in requestFileSystem'); + var fs = global.requestFileSystem || global.webkitRequestFileSystem || global.moz_requestFileSystem; + var mode = window.PERSISTENT; + if(typeof LocalFileSystem !== "undefined" && typeof LocalFileSystem.PERSISTENT !== "undefined"){ + mode = LocalFileSystem.PERSISTENT; + } + fs(mode, amount, function(fs) { + // console.log('got FS ', fs); + fs.root.getDirectory( name, {create:true}, function( directory ) { + // console.log('got DIR ', directory); + filesystems[name] = directory; + if ( callback ) me.fn( me.name, callback ).call( me, me ); + }, function( e ) { + // console.log('error getting dir :: ', e); + error(e); + }); }, function( e ) { -// console.log('error getting dir :: ', e); + // console.log('error getting FS :: ', e); error(e); }); - }, function( e ) { -// console.log('error getting FS :: ', e); - error(e); - }); - }; + }; - // When in the browser we need to use the html5 file system rather than - // the one cordova supplies, but it needs to request a quota first. - if (typeof navigator.webkitPersistentStorage !== 'undefined') { - navigator.webkitPersistentStorage.requestQuota(size, requestFileSystem, function() { - logger.warn('User declined file storage'); - error('User declined file storage'); - }); - } else { - // Amount is 0 because we pretty much have free reign over the - // amount of storage we use on an android device. - requestFileSystem(0); - } - }, + // When in the browser we need to use the html5 file system rather than + // the one cordova supplies, but it needs to request a quota first. + if (typeof navigator.webkitPersistentStorage !== 'undefined') { + navigator.webkitPersistentStorage.requestQuota(size, requestFileSystem, function() { + logger.warn('User declined file storage'); + error('User declined file storage'); + }); + } else { + // Amount is 0 because we pretty much have free reign over the + // amount of storage we use on an android device. + requestFileSystem(0); + } + }, - // returns all the keys in the store - keys: function( callback ) { - var me = this; - root( this, function( store ) { - ls( store.createReader(), function( entries ) { - if ( callback ) me.fn( 'keys', callback ).call( me, entries ); + // returns all the keys in the store + keys: function( callback ) { + var me = this; + root( this, function( store ) { + ls( store.createReader(), function( entries ) { + if ( callback ) me.fn( 'keys', callback ).call( me, entries ); + }); }); - }); - return this; - }, - - // save an object - save: function( obj, callback ) { - var me = this; - var key = obj.key || this.uuid(); - obj.key = key; - var error = function(e) { fail(e); if ( callback ) me.lambda( callback ).call( me ); }; - root( this, function( store ) { - var writeContent = function(file, error){ - file.createWriter(function( writer ) { - writer.onerror = error; - writer.onwriteend = function() { - // Clear the onWriteEnd handler so the truncate does not call it and cause an infinite loop - this.onwriteend = null; - // Truncate the file at the end of the written contents. This ensures that if we are updating - // a file which was previously longer, we will not be left with old contents beyond the end of - // the current buffer. - this.truncate(this.position); - if ( callback ) me.lambda( callback ).call( me, obj ); - }; - var contentStr = JSON.stringify(obj); + return this; + }, - var writerContent = createBlobOrString(contentStr); - writer.write(writerContent); - }, error ); - } - store.getFile( key, {create:true}, function( file ) { - if(typeof file.setMetadata === 'function' && (me.backup === false || me.backup === 'false')){ - //set meta data on the file to make sure it won't be backed up by icloud - file.setMetadata(function(){ - writeContent(file, error); - }, function(){ - writeContent(file, error); - }, {'com.apple.MobileBackup': 1}); - } else { - writeContent(file, error); - } - }, error ); - }); - return this; - }, + // save an object + save: function( obj, callback ) { + var me = this; + var key = obj.key || this.uuid(); + obj.key = key; + var error = function(e) { fail(e); if ( callback ) me.lambda( callback ).call( me ); }; + root( this, function( store ) { + var writeContent = function(file, error){ + file.createWriter(function( writer ) { + writer.onerror = error; + writer.onwriteend = function() { + // Clear the onWriteEnd handler so the truncate does not call it and cause an infinite loop + this.onwriteend = null; + // Truncate the file at the end of the written contents. This ensures that if we are updating + // a file which was previously longer, we will not be left with old contents beyond the end of + // the current buffer. + this.truncate(this.position); + if ( callback ) me.lambda( callback ).call( me, obj ); + }; + var contentStr = JSON.stringify(obj); - // batch save array of objs - batch: function( objs, callback ) { - var me = this; - var saved = []; - for ( var i = 0, il = objs.length; i < il; i++ ) { - me.save( objs[i], function( obj ) { - saved.push( obj ); - if ( saved.length === il && callback ) { - me.lambda( callback ).call( me, saved ); + var writerContent = createBlobOrString(contentStr); + writer.write(writerContent); + }, error ); } + store.getFile( key, {create:true}, function( file ) { + if(typeof file.setMetadata === 'function' && (me.backup === false || me.backup === 'false')){ + //set meta data on the file to make sure it won't be backed up by icloud + file.setMetadata(function(){ + writeContent(file, error); + }, function(){ + writeContent(file, error); + }, {'com.apple.MobileBackup': 1}); + } else { + writeContent(file, error); + } + }, error ); }); - } - return this; - }, + return this; + }, - // retrieve obj (or array of objs) and apply callback to each - get: function( key /* or array */, callback ) { - var me = this; - if ( this.isArray( key ) ) { - var values = []; - for ( var i = 0, il = key.length; i < il; i++ ) { - me.get( key[i], function( result ) { - if ( result ) values.push( result ); - if ( values.length === il && callback ) { - me.lambda( callback ).call( me, values ); + // batch save array of objs + batch: function( objs, callback ) { + var me = this; + var saved = []; + for ( var i = 0, il = objs.length; i < il; i++ ) { + me.save( objs[i], function( obj ) { + saved.push( obj ); + if ( saved.length === il && callback ) { + me.lambda( callback ).call( me, saved ); } }); } - } else { - var error = function(e) { - fail( e ); - if ( callback ) { - me.lambda( callback ).call( me ); + return this; + }, + + // retrieve obj (or array of objs) and apply callback to each + get: function( key /* or array */, callback ) { + var me = this; + if ( this.isArray( key ) ) { + var values = []; + for ( var i = 0, il = key.length; i < il; i++ ) { + me.get( key[i], function( result ) { + if ( result ) values.push( result ); + if ( values.length === il && callback ) { + me.lambda( callback ).call( me, values ); + } + }); } - }; - root( this, function( store ) { - store.getFile( key, {create:false}, function( entry ) { - entry.file(function( file ) { - var reader = new FileReader(); + } else { + var error = function(e) { + fail( e ); + if ( callback ) { + me.lambda( callback ).call( me ); + } + }; + root( this, function( store ) { + store.getFile( key, {create:false}, function( entry ) { + entry.file(function( file ) { + var reader = new FileReader(); - reader.onerror = error; + reader.onerror = error; - reader.onload = function(e) { - var res = {}; - try { - res = JSON.parse( e.target.result); - res.key = key; - } catch (e) { - res = {key:key}; - } - if ( callback ) me.lambda( callback ).call( me, res ); - }; + reader.onload = function(e) { + var res = {}; + try { + res = JSON.parse( e.target.result); + res.key = key; + } catch (e) { + res = {key:key}; + } + if ( callback ) me.lambda( callback ).call( me, res ); + }; - reader.readAsText( file ); + reader.readAsText( file ); + }, error ); }, error ); - }, error ); + }); + } + return this; + }, + + // check if an obj exists in the collection + exists: function( key, callback ) { + var me = this; + root( this, function( store ) { + store.getFile( key, {create:false}, function() { + if ( callback ) me.lambda( callback ).call( me, true ); + }, function() { + if ( callback ) me.lambda( callback ).call( me, false ); + }); }); - } - return this; - }, + return this; + }, - // check if an obj exists in the collection - exists: function( key, callback ) { - var me = this; - root( this, function( store ) { - store.getFile( key, {create:false}, function() { - if ( callback ) me.lambda( callback ).call( me, true ); - }, function() { - if ( callback ) me.lambda( callback ).call( me, false ); + // returns all the objs to the callback as an array + all: function( callback ) { + var me = this; + if ( callback ) { + this.keys(function( keys ) { + if ( !keys.length ) { + me.fn( me.name, callback ).call( me, [] ); + } else { + me.get( keys, function( values ) { + me.fn( me.name, callback ).call( me, values ); + }); + } + }); + } + return this; + }, + + // remove a doc or collection of em + remove: function( key /* or object */, callback ) { + var me = this; + var error = function(e) { fail( e ); if ( callback ) me.lambda( callback ).call( me ); }; + root( this, function( store ) { + store.getFile( (typeof key === 'string' ? key : key.key ), {create:false}, function( file ) { + file.remove(function() { + if ( callback ) me.lambda( callback ).call( me ); + }, error ); + }, error ); }); - }); - return this; - }, + return this; + }, - // returns all the objs to the callback as an array - all: function( callback ) { - var me = this; - if ( callback ) { + // destroy everything + nuke: function( callback ) { + var me = this; + var count = 0; this.keys(function( keys ) { if ( !keys.length ) { - me.fn( me.name, callback ).call( me, [] ); + if ( callback ) me.lambda( callback ).call( me ); } else { - me.get( keys, function( values ) { - me.fn( me.name, callback ).call( me, values ); - }); + for ( var i = 0, il = keys.length; i < il; i++ ) { + me.remove( keys[i], function() { + count++; + if ( count === il && callback ) { + me.lambda( callback ).call( me ); + } + }); + } } }); + return this; } - return this; - }, - - // remove a doc or collection of em - remove: function( key /* or object */, callback ) { - var me = this; - var error = function(e) { fail( e ); if ( callback ) me.lambda( callback ).call( me ); }; - root( this, function( store ) { - store.getFile( (typeof key === 'string' ? key : key.key ), {create:false}, function( file ) { - file.remove(function() { - if ( callback ) me.lambda( callback ).call( me ); - }, error ); - }, error ); - }); - return this; - }, - - // destroy everything - nuke: function( callback ) { - var me = this; - var count = 0; - this.keys(function( keys ) { - if ( !keys.length ) { - if ( callback ) me.lambda( callback ).call( me ); - } else { - for ( var i = 0, il = keys.length; i < il; i++ ) { - me.remove( keys[i], function() { - count++; - if ( count === il && callback ) { - me.lambda( callback ).call( me ); - } - }); - } - } - }); - return this; - } - }; -}(this))); + }; + }(this))); +} diff --git a/libs/lawnchair/lawnchairIndexDbAdapter.js b/libs/lawnchair/lawnchairIndexDbAdapter.js index a3aa9a7..64e2ace 100644 --- a/libs/lawnchair/lawnchairIndexDbAdapter.js +++ b/libs/lawnchair/lawnchairIndexDbAdapter.js @@ -1,235 +1,237 @@ -Lawnchair.adapter('indexed-db', (function(){ +module.exports = function (Lawnchair) { + Lawnchair.adapter('indexed-db', (function(){ - function fail(e, i) { - if(console) { console.log('error in indexed-db adapter!' + e.message, e, i); debugger;} - } ; + function fail(e, i) { + if(console) { console.log('error in indexed-db adapter!' + e.message, e, i); debugger;} + } ; - function getIDB(){ - return window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.oIndexedDB || window.msIndexedDB; - }; + function getIDB(){ + return window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.oIndexedDB || window.msIndexedDB; + }; - return { + return { - valid: function() { return !!getIDB(); }, + valid: function() { return !!getIDB(); }, - init:function(options, callback) { - this.idb = getIDB(); - this.waiting = []; - var request = this.idb.open(this.name, 2); - var self = this; - var cb = self.fn(self.name, callback); - var win = function(){ return cb.call(self, self); } - //FEEDHENRY CHANGE TO ALLOW ERROR CALLBACK - if(options && 'function' === typeof options.fail) fail = options.fail - //END CHANGE - request.onupgradeneeded = function(event){ - self.store = request.result.createObjectStore("teststore", { autoIncrement: true} ); - for (var i = 0; i < self.waiting.length; i++) { - self.waiting[i].call(self); - } - self.waiting = []; - win(); - } - - request.onsuccess = function(event) { - self.db = request.result; - - - if(self.db.version != "2.0") { - if(typeof self.db.setVersion == 'function'){ - - var setVrequest = self.db.setVersion("2.0"); - // onsuccess is the only place we can create Object Stores - setVrequest.onsuccess = function(e) { - self.store = self.db.createObjectStore("teststore", { autoIncrement: true} ); - for (var i = 0; i < self.waiting.length; i++) { - self.waiting[i].call(self); - } - self.waiting = []; - win(); - }; - setVrequest.onerror = function(e) { - // console.log("Failed to create objectstore " + e); - fail(e); - } - - } - } else { - self.store = {}; + init:function(options, callback) { + this.idb = getIDB(); + this.waiting = []; + var request = this.idb.open(this.name, 2); + var self = this; + var cb = self.fn(self.name, callback); + var win = function(){ return cb.call(self, self); } + //FEEDHENRY CHANGE TO ALLOW ERROR CALLBACK + if(options && 'function' === typeof options.fail) fail = options.fail + //END CHANGE + request.onupgradeneeded = function(event){ + self.store = request.result.createObjectStore("teststore", { autoIncrement: true} ); for (var i = 0; i < self.waiting.length; i++) { self.waiting[i].call(self); } self.waiting = []; win(); } - } - request.onerror = fail; - }, - - save:function(obj, callback) { - if(!this.store) { - this.waiting.push(function() { - this.save(obj, callback); - }); - return; - } - var self = this; - var win = function (e) { if (callback) { obj.key = e.target.result; self.lambda(callback).call(self, obj) }}; - var accessType = "readwrite"; - var trans = this.db.transaction(["teststore"],accessType); - var store = trans.objectStore("teststore"); - var request = obj.key ? store.put(obj, obj.key) : store.put(obj); + request.onsuccess = function(event) { + self.db = request.result; + + + if(self.db.version != "2.0") { + if(typeof self.db.setVersion == 'function'){ + + var setVrequest = self.db.setVersion("2.0"); + // onsuccess is the only place we can create Object Stores + setVrequest.onsuccess = function(e) { + self.store = self.db.createObjectStore("teststore", { autoIncrement: true} ); + for (var i = 0; i < self.waiting.length; i++) { + self.waiting[i].call(self); + } + self.waiting = []; + win(); + }; + setVrequest.onerror = function(e) { + // console.log("Failed to create objectstore " + e); + fail(e); + } - request.onsuccess = win; - request.onerror = fail; + } + } else { + self.store = {}; + for (var i = 0; i < self.waiting.length; i++) { + self.waiting[i].call(self); + } + self.waiting = []; + win(); + } + } + request.onerror = fail; + }, + + save:function(obj, callback) { + if(!this.store) { + this.waiting.push(function() { + this.save(obj, callback); + }); + return; + } - return this; - }, + var self = this; + var win = function (e) { if (callback) { obj.key = e.target.result; self.lambda(callback).call(self, obj) }}; + var accessType = "readwrite"; + var trans = this.db.transaction(["teststore"],accessType); + var store = trans.objectStore("teststore"); + var request = obj.key ? store.put(obj, obj.key) : store.put(obj); - // FIXME this should be a batch insert / just getting the test to pass... - batch: function (objs, cb) { + request.onsuccess = win; + request.onerror = fail; - var results = [] - , done = false - , self = this + return this; + }, - var updateProgress = function(obj) { - results.push(obj) - done = results.length === objs.length - } + // FIXME this should be a batch insert / just getting the test to pass... + batch: function (objs, cb) { + + var results = [] + , done = false + , self = this - var checkProgress = setInterval(function() { - if (done) { - if (cb) self.lambda(cb).call(self, results) - clearInterval(checkProgress) + var updateProgress = function(obj) { + results.push(obj) + done = results.length === objs.length } - }, 200) - for (var i = 0, l = objs.length; i < l; i++) - this.save(objs[i], updateProgress) + var checkProgress = setInterval(function() { + if (done) { + if (cb) self.lambda(cb).call(self, results) + clearInterval(checkProgress) + } + }, 200) - return this - }, + for (var i = 0, l = objs.length; i < l; i++) + this.save(objs[i], updateProgress) + return this + }, - get:function(key, callback) { - if(!this.store || !this.db) { - this.waiting.push(function() { - this.get(key, callback); - }); - return; - } + get:function(key, callback) { + if(!this.store || !this.db) { + this.waiting.push(function() { + this.get(key, callback); + }); + return; + } - var self = this; - var win = function (e) { if (callback) { self.lambda(callback).call(self, e.target.result) }}; + var self = this; + var win = function (e) { if (callback) { self.lambda(callback).call(self, e.target.result) }}; - if (!this.isArray(key)){ - var req = this.db.transaction("teststore").objectStore("teststore").get(key); - req.onsuccess = win; - req.onerror = function(event) { - //console.log("Failed to find " + key); - fail(event); - }; + if (!this.isArray(key)){ + var req = this.db.transaction("teststore").objectStore("teststore").get(key); - // FIXME: again the setInterval solution to async callbacks.. - } else { + req.onsuccess = win; + req.onerror = function(event) { + //console.log("Failed to find " + key); + fail(event); + }; - // note: these are hosted. - var results = [] - , done = false - , keys = key + // FIXME: again the setInterval solution to async callbacks.. + } else { - var updateProgress = function(obj) { - results.push(obj) - done = results.length === keys.length - } + // note: these are hosted. + var results = [] + , done = false + , keys = key - var checkProgress = setInterval(function() { - if (done) { - if (callback) self.lambda(callback).call(self, results) - clearInterval(checkProgress) + var updateProgress = function(obj) { + results.push(obj) + done = results.length === keys.length } - }, 200) - for (var i = 0, l = keys.length; i < l; i++) - this.get(keys[i], updateProgress) + var checkProgress = setInterval(function() { + if (done) { + if (callback) self.lambda(callback).call(self, results) + clearInterval(checkProgress) + } + }, 200) - } + for (var i = 0, l = keys.length; i < l; i++) + this.get(keys[i], updateProgress) - return this; - }, + } - all:function(callback) { - if(!this.store) { - this.waiting.push(function() { - this.all(callback); - }); - return; - } - var cb = this.fn(this.name, callback) || undefined; - var self = this; - var objectStore = this.db.transaction("teststore").objectStore("teststore"); - var toReturn = []; - objectStore.openCursor().onsuccess = function(event) { - var cursor = event.target.result; - if (cursor) { - toReturn.push(cursor.value); - cursor.continue(); + return this; + }, + + all:function(callback) { + if(!this.store) { + this.waiting.push(function() { + this.all(callback); + }); + return; + } + var cb = this.fn(this.name, callback) || undefined; + var self = this; + var objectStore = this.db.transaction("teststore").objectStore("teststore"); + var toReturn = []; + objectStore.openCursor().onsuccess = function(event) { + var cursor = event.target.result; + if (cursor) { + toReturn.push(cursor.value); + cursor.continue(); + } + else { + if (cb) cb.call(self, toReturn); + } + }; + return this; + }, + + remove:function(keyOrObj, callback) { + if(!this.store) { + this.waiting.push(function() { + this.remove(keyOrObj, callback); + }); + return; } - else { - if (cb) cb.call(self, toReturn); + if (typeof keyOrObj == "object") { + keyOrObj = keyOrObj.key; + } + var self = this; + var win = function () { if (callback) self.lambda(callback).call(self) }; + + var request = this.db.transaction(["teststore"], "readwrite").objectStore("teststore").delete(keyOrObj); + request.onsuccess = win; + request.onerror = fail; + return this; + }, + + nuke:function(callback) { + if(!this.store) { + this.waiting.push(function() { + this.nuke(callback); + }); + return; } - }; - return this; - }, - - remove:function(keyOrObj, callback) { - if(!this.store) { - this.waiting.push(function() { - this.remove(keyOrObj, callback); - }); - return; - } - if (typeof keyOrObj == "object") { - keyOrObj = keyOrObj.key; - } - var self = this; - var win = function () { if (callback) self.lambda(callback).call(self) }; - - var request = this.db.transaction(["teststore"], "readwrite").objectStore("teststore").delete(keyOrObj); - request.onsuccess = win; - request.onerror = fail; - return this; - }, - - nuke:function(callback) { - if(!this.store) { - this.waiting.push(function() { - this.nuke(callback); - }); - return; - } - var self = this - , win = callback ? function() { self.lambda(callback).call(self) } : function(){}; + var self = this + , win = callback ? function() { self.lambda(callback).call(self) } : function(){}; - try { - this.db - .transaction(["teststore"], "readwrite") - .objectStore("teststore").clear().onsuccess = win; + try { + this.db + .transaction(["teststore"], "readwrite") + .objectStore("teststore").clear().onsuccess = win; - } catch(e) { - fail(); + } catch(e) { + fail(); + } + return this; } - return this; - } - }; + }; -})()); \ No newline at end of file + })()); +} \ No newline at end of file diff --git a/libs/lawnchair/lawnchairLocalStorageAdapter.js b/libs/lawnchair/lawnchairLocalStorageAdapter.js index 5f72f58..aef8ac6 100644 --- a/libs/lawnchair/lawnchairLocalStorageAdapter.js +++ b/libs/lawnchair/lawnchairLocalStorageAdapter.js @@ -1,210 +1,212 @@ -/** - * dom storage adapter - * === - * - originally authored by Joseph Pecoraro - * - */ -// -// TODO does it make sense to be chainable all over the place? -// chainable: nuke, remove, all, get, save, all -// not chainable: valid, keys -// -Lawnchair.adapter('dom', (function() { - var storage = null; - try{ - storage = window.localStorage; - }catch(e){ +module.exports = function (Lawnchair) { + /** + * dom storage adapter + * === + * - originally authored by Joseph Pecoraro + * + */ + // + // TODO does it make sense to be chainable all over the place? + // chainable: nuke, remove, all, get, save, all + // not chainable: valid, keys + // + Lawnchair.adapter('dom', (function() { + var storage = null; + try{ + storage = window.localStorage; + }catch(e){ - } - // the indexer is an encapsulation of the helpers needed to keep an ordered index of the keys - var indexer = function(name) { - return { - // the key - key: name + '._index_', - // returns the index - all: function() { - var a = storage.getItem(this.key) - if (a) { - a = JSON.parse(a) - } - if (a === null) storage.setItem(this.key, JSON.stringify([])) // lazy init - return JSON.parse(storage.getItem(this.key)) - }, - // adds a key to the index - add: function (key) { - var a = this.all() - a.push(key) - storage.setItem(this.key, JSON.stringify(a)) - }, - // deletes a key from the index - del: function (key) { - var a = this.all(), r = [] - // FIXME this is crazy inefficient but I'm in a strata meeting and half concentrating - for (var i = 0, l = a.length; i < l; i++) { - if (a[i] != key) r.push(a[i]) - } - storage.setItem(this.key, JSON.stringify(r)) - }, - // returns index for a key - find: function (key) { - var a = this.all() - for (var i = 0, l = a.length; i < l; i++) { - if (key === a[i]) return i + } + // the indexer is an encapsulation of the helpers needed to keep an ordered index of the keys + var indexer = function(name) { + return { + // the key + key: name + '._index_', + // returns the index + all: function() { + var a = storage.getItem(this.key) + if (a) { + a = JSON.parse(a) + } + if (a === null) storage.setItem(this.key, JSON.stringify([])) // lazy init + return JSON.parse(storage.getItem(this.key)) + }, + // adds a key to the index + add: function (key) { + var a = this.all() + a.push(key) + storage.setItem(this.key, JSON.stringify(a)) + }, + // deletes a key from the index + del: function (key) { + var a = this.all(), r = [] + // FIXME this is crazy inefficient but I'm in a strata meeting and half concentrating + for (var i = 0, l = a.length; i < l; i++) { + if (a[i] != key) r.push(a[i]) + } + storage.setItem(this.key, JSON.stringify(r)) + }, + // returns index for a key + find: function (key) { + var a = this.all() + for (var i = 0, l = a.length; i < l; i++) { + if (key === a[i]) return i + } + return false } - return false } } - } - // adapter api - return { + // adapter api + return { - // ensure we are in an env with localStorage - valid: function () { - return !!storage && function() { - // in mobile safari if safe browsing is enabled, window.storage - // is defined but setItem calls throw exceptions. - var success = true - var value = Math.random() - try { - storage.setItem(value, value) - } catch (e) { - success = false - } - storage.removeItem(value) - return success - }() - }, + // ensure we are in an env with localStorage + valid: function () { + return !!storage && function() { + // in mobile safari if safe browsing is enabled, window.storage + // is defined but setItem calls throw exceptions. + var success = true + var value = Math.random() + try { + storage.setItem(value, value) + } catch (e) { + success = false + } + storage.removeItem(value) + return success + }() + }, - init: function (options, callback) { - this.indexer = indexer(this.name) - if (callback) this.fn(this.name, callback).call(this, this) - }, + init: function (options, callback) { + this.indexer = indexer(this.name) + if (callback) this.fn(this.name, callback).call(this, this) + }, - save: function (obj, callback) { - var key = obj.key ? this.name + '.' + obj.key : this.name + '.' + this.uuid() - // now we kil the key and use it in the store colleciton - delete obj.key; - storage.setItem(key, JSON.stringify(obj)) - // if the key is not in the index push it on - if (this.indexer.find(key) === false) this.indexer.add(key) - obj.key = key.slice(this.name.length + 1) - if (callback) { - this.lambda(callback).call(this, obj) - } - return this - }, + save: function (obj, callback) { + var key = obj.key ? this.name + '.' + obj.key : this.name + '.' + this.uuid() + // now we kil the key and use it in the store colleciton + delete obj.key; + storage.setItem(key, JSON.stringify(obj)) + // if the key is not in the index push it on + if (this.indexer.find(key) === false) this.indexer.add(key) + obj.key = key.slice(this.name.length + 1) + if (callback) { + this.lambda(callback).call(this, obj) + } + return this + }, - batch: function (ary, callback) { - var saved = [] - // not particularily efficient but this is more for sqlite situations - for (var i = 0, l = ary.length; i < l; i++) { - this.save(ary[i], function(r){ - saved.push(r) - }) - } - if (callback) this.lambda(callback).call(this, saved) - return this - }, + batch: function (ary, callback) { + var saved = [] + // not particularily efficient but this is more for sqlite situations + for (var i = 0, l = ary.length; i < l; i++) { + this.save(ary[i], function(r){ + saved.push(r) + }) + } + if (callback) this.lambda(callback).call(this, saved) + return this + }, - // accepts [options], callback - keys: function(callback) { - if (callback) { - var name = this.name - var indices = this.indexer.all(); - var keys = []; - //Checking for the support of map. - if(Array.prototype.map) { - keys = indices.map(function(r){ return r.replace(name + '.', '') }) - } else { - for (var key in indices) { - keys.push(key.replace(name + '.', '')); + // accepts [options], callback + keys: function(callback) { + if (callback) { + var name = this.name + var indices = this.indexer.all(); + var keys = []; + //Checking for the support of map. + if(Array.prototype.map) { + keys = indices.map(function(r){ return r.replace(name + '.', '') }) + } else { + for (var key in indices) { + keys.push(key.replace(name + '.', '')); + } } + this.fn('keys', callback).call(this, keys) } - this.fn('keys', callback).call(this, keys) - } - return this // TODO options for limit/offset, return promise - }, + return this // TODO options for limit/offset, return promise + }, - get: function (key, callback) { - if (this.isArray(key)) { - var r = [] - for (var i = 0, l = key.length; i < l; i++) { - var k = this.name + '.' + key[i] - var obj = storage.getItem(k) + get: function (key, callback) { + if (this.isArray(key)) { + var r = [] + for (var i = 0, l = key.length; i < l; i++) { + var k = this.name + '.' + key[i] + var obj = storage.getItem(k) + if (obj) { + obj = JSON.parse(obj) + obj.key = key[i] + } + r.push(obj) + } + if (callback) this.lambda(callback).call(this, r) + } else { + var k = this.name + '.' + key + var obj = storage.getItem(k) if (obj) { obj = JSON.parse(obj) - obj.key = key[i] + obj.key = key } - r.push(obj) + if (callback) this.lambda(callback).call(this, obj) } - if (callback) this.lambda(callback).call(this, r) - } else { - var k = this.name + '.' + key - var obj = storage.getItem(k) - if (obj) { - obj = JSON.parse(obj) - obj.key = key - } - if (callback) this.lambda(callback).call(this, obj) - } - return this - }, - - exists: function (key, cb) { - var exists = this.indexer.find(this.name+'.'+key) === false ? false : true ; - this.lambda(cb).call(this, exists); - return this; - }, - // NOTE adapters cannot set this.__results but plugins do - // this probably should be reviewed - all: function (callback) { - var idx = this.indexer.all() - , r = [] - , o - , k - for (var i = 0, l = idx.length; i < l; i++) { - k = idx[i] //v - o = JSON.parse(storage.getItem(k)) - o.key = k.replace(this.name + '.', '') - r.push(o) - } - if (callback) this.fn(this.name, callback).call(this, r) - return this - }, + return this + }, - remove: function (keyOrArray, callback) { - var self = this; - if (this.isArray(keyOrArray)) { - // batch remove - var i, done = keyOrArray.length; - var removeOne = function(i) { - self.remove(keyOrArray[i], function() { - if ((--done) > 0) { return; } - if (callback) { - self.lambda(callback).call(self); - } - }); - }; - for (i=0; i < keyOrArray.length; i++) - removeOne(i); + exists: function (key, cb) { + var exists = this.indexer.find(this.name+'.'+key) === false ? false : true ; + this.lambda(cb).call(this, exists); return this; - } - var key = this.name + '.' + - ((keyOrArray.key) ? keyOrArray.key : keyOrArray) - this.indexer.del(key) - storage.removeItem(key) - if (callback) this.lambda(callback).call(this) - return this - }, + }, + // NOTE adapters cannot set this.__results but plugins do + // this probably should be reviewed + all: function (callback) { + var idx = this.indexer.all() + , r = [] + , o + , k + for (var i = 0, l = idx.length; i < l; i++) { + k = idx[i] //v + o = JSON.parse(storage.getItem(k)) + o.key = k.replace(this.name + '.', '') + r.push(o) + } + if (callback) this.fn(this.name, callback).call(this, r) + return this + }, - nuke: function (callback) { - this.all(function(r) { - for (var i = 0, l = r.length; i < l; i++) { - this.remove(r[i]); + remove: function (keyOrArray, callback) { + var self = this; + if (this.isArray(keyOrArray)) { + // batch remove + var i, done = keyOrArray.length; + var removeOne = function(i) { + self.remove(keyOrArray[i], function() { + if ((--done) > 0) { return; } + if (callback) { + self.lambda(callback).call(self); + } + }); + }; + for (i=0; i < keyOrArray.length; i++) + removeOne(i); + return this; } + var key = this.name + '.' + + ((keyOrArray.key) ? keyOrArray.key : keyOrArray) + this.indexer.del(key) + storage.removeItem(key) if (callback) this.lambda(callback).call(this) - }) - return this - } - }})()); \ No newline at end of file + return this + }, + + nuke: function (callback) { + this.all(function(r) { + for (var i = 0, l = r.length; i < l; i++) { + this.remove(r[i]); + } + if (callback) this.lambda(callback).call(this) + }) + return this + } + }})()); +} \ No newline at end of file diff --git a/libs/lawnchair/lawnchairMemoryAdapter.js b/libs/lawnchair/lawnchairMemoryAdapter.js index ed73287..2cade72 100644 --- a/libs/lawnchair/lawnchairMemoryAdapter.js +++ b/libs/lawnchair/lawnchairMemoryAdapter.js @@ -1,105 +1,107 @@ -Lawnchair.adapter('memory', (function(){ +module.exports = function (Lawnchair) { + Lawnchair.adapter('memory', (function(){ - var data = {} + var data = {} - return { - valid: function() { return true }, + return { + valid: function() { return true }, - init: function (options, callback) { - data[this.name] = data[this.name] || {index:[],store:{}} - this.index = data[this.name].index - this.store = data[this.name].store - var cb = this.fn(this.name, callback) - if (cb) cb.call(this, this) - return this - }, + init: function (options, callback) { + data[this.name] = data[this.name] || {index:[],store:{}} + this.index = data[this.name].index + this.store = data[this.name].store + var cb = this.fn(this.name, callback) + if (cb) cb.call(this, this) + return this + }, - keys: function (callback) { - this.fn('keys', callback).call(this, this.index) - return this - }, + keys: function (callback) { + this.fn('keys', callback).call(this, this.index) + return this + }, - save: function(obj, cb) { - var key = obj.key || this.uuid() - - this.exists(key, function(exists) { - if (!exists) { - if (obj.key) delete obj.key - this.index.push(key) - } - - this.store[key] = obj + save: function(obj, cb) { + var key = obj.key || this.uuid() - if (cb) { - obj.key = key - this.lambda(cb).call(this, obj) - } - }) - - return this - }, + this.exists(key, function(exists) { + if (!exists) { + if (obj.key) delete obj.key + this.index.push(key) + } - batch: function (objs, cb) { - var r = [] - for (var i = 0, l = objs.length; i < l; i++) { - this.save(objs[i], function(record) { - r.push(record) + this.store[key] = obj + + if (cb) { + obj.key = key + this.lambda(cb).call(this, obj) + } }) - } - if (cb) this.lambda(cb).call(this, r) - return this - }, - get: function (keyOrArray, cb) { - var r; - if (this.isArray(keyOrArray)) { - r = [] - for (var i = 0, l = keyOrArray.length; i < l; i++) { - r.push(this.store[keyOrArray[i]]) + return this + }, + + batch: function (objs, cb) { + var r = [] + for (var i = 0, l = objs.length; i < l; i++) { + this.save(objs[i], function(record) { + r.push(record) + }) } - } else { - r = this.store[keyOrArray] - if (r) r.key = keyOrArray - } - if (cb) this.lambda(cb).call(this, r) - return this - }, + if (cb) this.lambda(cb).call(this, r) + return this + }, - exists: function (key, cb) { - this.lambda(cb).call(this, !!(this.store[key])) - return this - }, + get: function (keyOrArray, cb) { + var r; + if (this.isArray(keyOrArray)) { + r = [] + for (var i = 0, l = keyOrArray.length; i < l; i++) { + r.push(this.store[keyOrArray[i]]) + } + } else { + r = this.store[keyOrArray] + if (r) r.key = keyOrArray + } + if (cb) this.lambda(cb).call(this, r) + return this + }, - all: function (cb) { - var r = [] - for (var i = 0, l = this.index.length; i < l; i++) { - var obj = this.store[this.index[i]] - obj.key = this.index[i] - r.push(obj) - } - this.fn(this.name, cb).call(this, r) - return this - }, + exists: function (key, cb) { + this.lambda(cb).call(this, !!(this.store[key])) + return this + }, - remove: function (keyOrArray, cb) { - var del = this.isArray(keyOrArray) ? keyOrArray : [keyOrArray] - for (var i = 0, l = del.length; i < l; i++) { - var key = del[i].key ? del[i].key : del[i] - var where = this.indexOf(this.index, key) - if (where < 0) continue /* key not present */ - delete this.store[key] - this.index.splice(where, 1) - } - if (cb) this.lambda(cb).call(this) - return this - }, + all: function (cb) { + var r = [] + for (var i = 0, l = this.index.length; i < l; i++) { + var obj = this.store[this.index[i]] + obj.key = this.index[i] + r.push(obj) + } + this.fn(this.name, cb).call(this, r) + return this + }, - nuke: function (cb) { - this.store = data[this.name].store = {} - this.index = data[this.name].index = [] - if (cb) this.lambda(cb).call(this) - return this + remove: function (keyOrArray, cb) { + var del = this.isArray(keyOrArray) ? keyOrArray : [keyOrArray] + for (var i = 0, l = del.length; i < l; i++) { + var key = del[i].key ? del[i].key : del[i] + var where = this.indexOf(this.index, key) + if (where < 0) continue /* key not present */ + delete this.store[key] + this.index.splice(where, 1) + } + if (cb) this.lambda(cb).call(this) + return this + }, + + nuke: function (cb) { + this.store = data[this.name].store = {} + this.index = data[this.name].index = [] + if (cb) this.lambda(cb).call(this) + return this + } } - } -///// -})()); \ No newline at end of file + ///// + })()); +} \ No newline at end of file diff --git a/libs/lawnchair/lawnchairTitanium.js b/libs/lawnchair/lawnchairTitanium.js index 8e89cb8..f8a52f9 100644 --- a/libs/lawnchair/lawnchairTitanium.js +++ b/libs/lawnchair/lawnchairTitanium.js @@ -1,112 +1,114 @@ -Lawnchair.adapter('titanium', (function(global){ +module.exports = function (Lawnchair) { + Lawnchair.adapter('titanium', (function(global){ - return { - // boolean; true if the adapter is valid for the current environment - valid: function() { - return typeof Titanium !== 'undefined'; - }, + return { + // boolean; true if the adapter is valid for the current environment + valid: function() { + return typeof Titanium !== 'undefined'; + }, - // constructor call and callback. 'name' is the most common option - init: function( options, callback ) { - if (callback){ - return this.fn('init', callback).call(this) - } - }, - - // returns all the keys in the store - keys: function( callback ) { - if (callback) { - return this.fn('keys', callback).call(this, Titanium.App.Properties.listProperties()); - } - return this; - }, + // constructor call and callback. 'name' is the most common option + init: function( options, callback ) { + if (callback){ + return this.fn('init', callback).call(this) + } + }, - // save an object - save: function( obj, callback ) { - var saveRes = Titanium.App.Properties.setObject(obj.key, obj); + // returns all the keys in the store + keys: function( callback ) { if (callback) { - return this.fn('save', callback).call(this, saveRes); + return this.fn('keys', callback).call(this, Titanium.App.Properties.listProperties()); } return this; - }, + }, - // batch save array of objs - batch: function( objs, callback ) { - var me = this; - var saved = []; - for ( var i = 0, il = objs.length; i < il; i++ ) { - me.save( objs[i], function( obj ) { - saved.push( obj ); - if ( saved.length === il && callback ) { - me.lambda( callback ).call( me, saved ); - } - }); - } - return this; - }, + // save an object + save: function( obj, callback ) { + var saveRes = Titanium.App.Properties.setObject(obj.key, obj); + if (callback) { + return this.fn('save', callback).call(this, saveRes); + } + return this; + }, - // retrieve obj (or array of objs) and apply callback to each - get: function( key /* or array */, callback ) { - var me = this; - if ( this.isArray( key ) ) { - var values = []; - for ( var i = 0, il = key.length; i < il; i++ ) { - me.get( key[i], function( result ) { - if ( result ) values.push( result ); - if ( values.length === il && callback ) { - me.lambda( callback ).call( me, values ); + // batch save array of objs + batch: function( objs, callback ) { + var me = this; + var saved = []; + for ( var i = 0, il = objs.length; i < il; i++ ) { + me.save( objs[i], function( obj ) { + saved.push( obj ); + if ( saved.length === il && callback ) { + me.lambda( callback ).call( me, saved ); } }); } - } else { - return this.fn('init', callback).call(this, Titanium.App.Properties.getObject(key)); - } - return this; - }, - - // check if an obj exists in the collection - exists: function( key, callback ) { - if (callback){ - if (Titanium.App.Properties.getObject(key)){ - return callback(this, true); - }else{ - return callback(this, false); - } - } + return this; + }, - return this; - }, - - // returns all the objs to the callback as an array - all: function( callback ) { - var me = this; - if ( callback ) { - this.keys(function( keys ) { - if ( !keys.length ) { - me.fn( me.name, callback ).call( me, [] ); - } else { - me.get( keys, function( values ) { - me.fn( me.name, callback ).call( me, values ); + // retrieve obj (or array of objs) and apply callback to each + get: function( key /* or array */, callback ) { + var me = this; + if ( this.isArray( key ) ) { + var values = []; + for ( var i = 0, il = key.length; i < il; i++ ) { + me.get( key[i], function( result ) { + if ( result ) values.push( result ); + if ( values.length === il && callback ) { + me.lambda( callback ).call( me, values ); + } }); } - }); - } - return this; - }, + } else { + return this.fn('init', callback).call(this, Titanium.App.Properties.getObject(key)); + } + return this; + }, - // remove a doc or collection of em - remove: function( key /* or object */, callback ) { - var me = this; - Titanium.App.Properties.removeProperty(key); - if (callback) { - return this.fn('remove', callback).call(this); - } - return this; - }, + // check if an obj exists in the collection + exists: function( key, callback ) { + if (callback){ + if (Titanium.App.Properties.getObject(key)){ + return callback(this, true); + }else{ + return callback(this, false); + } + } + + return this; + }, - // destroy everything - nuke: function( callback ) { - // nah, lets not do that - } - }; -}(this))); + // returns all the objs to the callback as an array + all: function( callback ) { + var me = this; + if ( callback ) { + this.keys(function( keys ) { + if ( !keys.length ) { + me.fn( me.name, callback ).call( me, [] ); + } else { + me.get( keys, function( values ) { + me.fn( me.name, callback ).call( me, values ); + }); + } + }); + } + return this; + }, + + // remove a doc or collection of em + remove: function( key /* or object */, callback ) { + var me = this; + Titanium.App.Properties.removeProperty(key); + if (callback) { + return this.fn('remove', callback).call(this); + } + return this; + }, + + // destroy everything + nuke: function( callback ) { + // nah, lets not do that + } + }; + }(this))); +} \ No newline at end of file diff --git a/libs/lawnchair/lawnchairWebkitSqlAdapter.js b/libs/lawnchair/lawnchairWebkitSqlAdapter.js index 2772744..8be499b 100644 --- a/libs/lawnchair/lawnchairWebkitSqlAdapter.js +++ b/libs/lawnchair/lawnchairWebkitSqlAdapter.js @@ -1,204 +1,206 @@ -Lawnchair.adapter('webkit-sqlite', (function() { - // private methods - var fail = function(e, i) { - if (console) { - console.log('error in sqlite adaptor!', e, i) - } - }, now = function() { - return new Date() - } // FIXME need to use better date fn - // not entirely sure if this is needed... - - // public methods - return { - - valid: function() { - return !!(window.openDatabase) - }, - - init: function(options, callback) { - var that = this, - cb = that.fn(that.name, callback), - create = "CREATE TABLE IF NOT EXISTS " + this.record + " (id NVARCHAR(32) UNIQUE PRIMARY KEY, value TEXT, timestamp REAL)", - win = function() { - return cb.call(that, that); - } - // open a connection and create the db if it doesn't exist - //FEEDHENRY CHANGE TO ALLOW ERROR CALLBACK - if (options && 'function' === typeof options.fail) fail = options.fail - //END CHANGE - this.db = openDatabase(this.name, '1.0.0', this.name, 65536) - this.db.transaction(function(t) { - t.executeSql(create, [], win, fail) - }) - }, - - keys: function(callback) { - var cb = this.lambda(callback), - that = this, - keys = "SELECT id FROM " + this.record + " ORDER BY timestamp DESC" - - this.db.readTransaction(function(t) { - var win = function(xxx, results) { - if (results.rows.length == 0) { - cb.call(that, []) - } else { - var r = []; - for (var i = 0, l = results.rows.length; i < l; i++) { - r.push(results.rows.item(i).id); +module.exports = function (Lawnchair) { + Lawnchair.adapter('webkit-sqlite', (function() { + // private methods + var fail = function(e, i) { + if (console) { + console.log('error in sqlite adaptor!', e, i) + } + }, now = function() { + return new Date() + } // FIXME need to use better date fn + // not entirely sure if this is needed... + + // public methods + return { + + valid: function() { + return !!(window.openDatabase) + }, + + init: function(options, callback) { + var that = this, + cb = that.fn(that.name, callback), + create = "CREATE TABLE IF NOT EXISTS " + this.record + " (id NVARCHAR(32) UNIQUE PRIMARY KEY, value TEXT, timestamp REAL)", + win = function() { + return cb.call(that, that); + } + // open a connection and create the db if it doesn't exist + //FEEDHENRY CHANGE TO ALLOW ERROR CALLBACK + if (options && 'function' === typeof options.fail) fail = options.fail + //END CHANGE + this.db = openDatabase(this.name, '1.0.0', this.name, 65536) + this.db.transaction(function(t) { + t.executeSql(create, [], win, fail) + }) + }, + + keys: function(callback) { + var cb = this.lambda(callback), + that = this, + keys = "SELECT id FROM " + this.record + " ORDER BY timestamp DESC" + + this.db.readTransaction(function(t) { + var win = function(xxx, results) { + if (results.rows.length == 0) { + cb.call(that, []) + } else { + var r = []; + for (var i = 0, l = results.rows.length; i < l; i++) { + r.push(results.rows.item(i).id); + } + cb.call(that, r) } - cb.call(that, r) } - } - t.executeSql(keys, [], win, fail) - }) - return this - }, - // you think thats air you're breathing now? - save: function(obj, callback, error) { - var that = this - objs = (this.isArray(obj) ? obj : [obj]).map(function(o) { - if (!o.key) { - o.key = that.uuid() - } - return o - }), - ins = "INSERT OR REPLACE INTO " + this.record + " (value, timestamp, id) VALUES (?,?,?)", - win = function() { - if (callback) { - that.lambda(callback).call(that, that.isArray(obj) ? objs : objs[0]) + t.executeSql(keys, [], win, fail) + }) + return this + }, + // you think thats air you're breathing now? + save: function(obj, callback, error) { + var that = this + objs = (this.isArray(obj) ? obj : [obj]).map(function(o) { + if (!o.key) { + o.key = that.uuid() } - }, error = error || function() {}, insvals = [], - ts = now() + return o + }), + ins = "INSERT OR REPLACE INTO " + this.record + " (value, timestamp, id) VALUES (?,?,?)", + win = function() { + if (callback) { + that.lambda(callback).call(that, that.isArray(obj) ? objs : objs[0]) + } + }, error = error || function() {}, insvals = [], + ts = now() - try { - for (var i = 0, l = objs.length; i < l; i++) { - insvals[i] = [JSON.stringify(objs[i]), ts, objs[i].key]; + try { + for (var i = 0, l = objs.length; i < l; i++) { + insvals[i] = [JSON.stringify(objs[i]), ts, objs[i].key]; + } + } catch (e) { + fail(e) + throw e; } - } catch (e) { - fail(e) - throw e; - } - that.db.transaction(function(t) { - for (var i = 0, l = objs.length; i < l; i++) - t.executeSql(ins, insvals[i]) - }, function(e, i) { - fail(e, i) - }, win) - - return this - }, - - - batch: function(objs, callback) { - return this.save(objs, callback) - }, - - get: function(keyOrArray, cb) { - var that = this, - sql = '', - args = this.isArray(keyOrArray) ? keyOrArray : [keyOrArray]; - // batch selects support - sql = 'SELECT id, value FROM ' + this.record + " WHERE id IN (" + - args.map(function() { - return '?' - }).join(",") + ")" - // FIXME - // will always loop the results but cleans it up if not a batch return at the end.. - // in other words, this could be faster - var win = function(xxx, results) { - var o, r, lookup = {} - // map from results to keys - for (var i = 0, l = results.rows.length; i < l; i++) { - o = JSON.parse(results.rows.item(i).value) - o.key = results.rows.item(i).id - lookup[o.key] = o; - } - r = args.map(function(key) { - return lookup[key]; - }); - if (!that.isArray(keyOrArray)) r = r.length ? r[0] : null - if (cb) that.lambda(cb).call(that, r) - } - this.db.readTransaction(function(t) { - t.executeSql(sql, args, win, fail) - }) - return this - }, - - exists: function(key, cb) { - var is = "SELECT * FROM " + this.record + " WHERE id = ?", - that = this, - win = function(xxx, results) { - if (cb) that.fn('exists', cb).call(that, (results.rows.length > 0)) + that.db.transaction(function(t) { + for (var i = 0, l = objs.length; i < l; i++) + t.executeSql(ins, insvals[i]) + }, function(e, i) { + fail(e, i) + }, win) + + return this + }, + + + batch: function(objs, callback) { + return this.save(objs, callback) + }, + + get: function(keyOrArray, cb) { + var that = this, + sql = '', + args = this.isArray(keyOrArray) ? keyOrArray : [keyOrArray]; + // batch selects support + sql = 'SELECT id, value FROM ' + this.record + " WHERE id IN (" + + args.map(function() { + return '?' + }).join(",") + ")" + // FIXME + // will always loop the results but cleans it up if not a batch return at the end.. + // in other words, this could be faster + var win = function(xxx, results) { + var o, r, lookup = {} + // map from results to keys + for (var i = 0, l = results.rows.length; i < l; i++) { + o = JSON.parse(results.rows.item(i).value) + o.key = results.rows.item(i).id + lookup[o.key] = o; + } + r = args.map(function(key) { + return lookup[key]; + }); + if (!that.isArray(keyOrArray)) r = r.length ? r[0] : null + if (cb) that.lambda(cb).call(that, r) } - this.db.readTransaction(function(t) { - t.executeSql(is, [key], win, fail) - }) - return this - }, - - all: function(callback) { - var that = this, - all = "SELECT * FROM " + this.record, - r = [], - cb = this.fn(this.name, callback) || undefined, - win = function(xxx, results) { - if (results.rows.length != 0) { - for (var i = 0, l = results.rows.length; i < l; i++) { - var obj = JSON.parse(results.rows.item(i).value) - obj.key = results.rows.item(i).id - r.push(obj) + this.db.readTransaction(function(t) { + t.executeSql(sql, args, win, fail) + }) + return this + }, + + exists: function(key, cb) { + var is = "SELECT * FROM " + this.record + " WHERE id = ?", + that = this, + win = function(xxx, results) { + if (cb) that.fn('exists', cb).call(that, (results.rows.length > 0)) + } + this.db.readTransaction(function(t) { + t.executeSql(is, [key], win, fail) + }) + return this + }, + + all: function(callback) { + var that = this, + all = "SELECT * FROM " + this.record, + r = [], + cb = this.fn(this.name, callback) || undefined, + win = function(xxx, results) { + if (results.rows.length != 0) { + for (var i = 0, l = results.rows.length; i < l; i++) { + var obj = JSON.parse(results.rows.item(i).value) + obj.key = results.rows.item(i).id + r.push(obj) + } } + if (cb) cb.call(that, r) } - if (cb) cb.call(that, r) - } - this.db.readTransaction(function(t) { - t.executeSql(all, [], win, fail) - }) - return this - }, - - remove: function(keyOrArray, cb) { - var that = this, - args, sql = "DELETE FROM " + this.record + " WHERE id ", - win = function() { - if (cb) that.lambda(cb).call(that) + this.db.readTransaction(function(t) { + t.executeSql(all, [], win, fail) + }) + return this + }, + + remove: function(keyOrArray, cb) { + var that = this, + args, sql = "DELETE FROM " + this.record + " WHERE id ", + win = function() { + if (cb) that.lambda(cb).call(that) + } + if (!this.isArray(keyOrArray)) { + sql += '= ?'; + args = [keyOrArray]; + } else { + args = keyOrArray; + sql += "IN (" + + args.map(function() { + return '?' + }).join(',') + + ")"; } - if (!this.isArray(keyOrArray)) { - sql += '= ?'; - args = [keyOrArray]; - } else { - args = keyOrArray; - sql += "IN (" + - args.map(function() { - return '?' - }).join(',') + - ")"; + args = args.map(function(obj) { + return obj.key ? obj.key : obj; + }); + + this.db.transaction(function(t) { + t.executeSql(sql, args, win, fail); + }); + + return this; + }, + + nuke: function(cb) { + var nuke = "DELETE FROM " + this.record, + that = this, + win = cb ? function() { + that.lambda(cb).call(that) + } : function() {} + this.db.transaction(function(t) { + t.executeSql(nuke, [], win, fail) + }) + return this } - args = args.map(function(obj) { - return obj.key ? obj.key : obj; - }); - - this.db.transaction(function(t) { - t.executeSql(sql, args, win, fail); - }); - - return this; - }, - - nuke: function(cb) { - var nuke = "DELETE FROM " + this.record, - that = this, - win = cb ? function() { - that.lambda(cb).call(that) - } : function() {} - this.db.transaction(function(t) { - t.executeSql(nuke, [], win, fail) - }) - return this } - } -})()); \ No newline at end of file + })()); +} \ No newline at end of file diff --git a/libs/lawnchair/lawnchairWindowNameStorageAdapter.js b/libs/lawnchair/lawnchairWindowNameStorageAdapter.js index cd3bc64..5b2bd2c 100644 --- a/libs/lawnchair/lawnchairWindowNameStorageAdapter.js +++ b/libs/lawnchair/lawnchairWindowNameStorageAdapter.js @@ -1,130 +1,132 @@ -// window.name code courtesy Remy Sharp: http://24ways.org/2009/breaking-out-the-edges-of-the-browser -Lawnchair.adapter('window-name', (function() { - if (typeof window==='undefined') { - window = { top: { } }; // node/optimizer compatibility - } - - // edited from the original here by elsigh - // Some sites store JSON data in window.top.name, but some folks (twitter on iPad) - // put simple strings in there - we should make sure not to cause a SyntaxError. - var data = {} - try { - data = JSON.parse(window.top.name) - } catch (e) {} +module.exports = function (Lawnchair) { + // window.name code courtesy Remy Sharp: http://24ways.org/2009/breaking-out-the-edges-of-the-browser + Lawnchair.adapter('window-name', (function() { + if (typeof window==='undefined') { + window = { top: { } }; // node/optimizer compatibility + } + // edited from the original here by elsigh + // Some sites store JSON data in window.top.name, but some folks (twitter on iPad) + // put simple strings in there - we should make sure not to cause a SyntaxError. + var data = {} + try { + data = JSON.parse(window.top.name) + } catch (e) {} - return { - valid: function () { - return typeof window.top.name != 'undefined' - }, + return { - init: function (options, callback) { - data[this.name] = data[this.name] || {index:[],store:{}} - this.index = data[this.name].index - this.store = data[this.name].store - this.fn(this.name, callback).call(this, this) - return this - }, + valid: function () { + return typeof window.top.name != 'undefined' + }, - keys: function (callback) { - this.fn('keys', callback).call(this, this.index) - return this - }, + init: function (options, callback) { + data[this.name] = data[this.name] || {index:[],store:{}} + this.index = data[this.name].index + this.store = data[this.name].store + this.fn(this.name, callback).call(this, this) + return this + }, - save: function (obj, cb) { - // data[key] = value + ''; // force to string - // window.top.name = JSON.stringify(data); - var key = obj.key || this.uuid() - this.exists(key, function(exists) { - if (!exists) { - if (obj.key) delete obj.key - this.index.push(key) - } - this.store[key] = obj + keys: function (callback) { + this.fn('keys', callback).call(this, this.index) + return this + }, - try { - window.top.name = JSON.stringify(data) // TODO wow, this is the only diff from the memory adapter - } catch(e) { - // restore index/store to previous value before JSON exception + save: function (obj, cb) { + // data[key] = value + ''; // force to string + // window.top.name = JSON.stringify(data); + var key = obj.key || this.uuid() + this.exists(key, function(exists) { if (!exists) { - this.index.pop(); - delete this.store[key]; + if (obj.key) delete obj.key + this.index.push(key) } - throw e; - } + this.store[key] = obj - if (cb) { - obj.key = key - this.lambda(cb).call(this, obj) - } - }) - return this - }, + try { + window.top.name = JSON.stringify(data) // TODO wow, this is the only diff from the memory adapter + } catch(e) { + // restore index/store to previous value before JSON exception + if (!exists) { + this.index.pop(); + delete this.store[key]; + } + throw e; + } - batch: function (objs, cb) { - var r = [] - for (var i = 0, l = objs.length; i < l; i++) { - this.save(objs[i], function(record) { - r.push(record) + if (cb) { + obj.key = key + this.lambda(cb).call(this, obj) + } }) - } - if (cb) this.lambda(cb).call(this, r) - return this - }, + return this + }, - get: function (keyOrArray, cb) { - var r; - if (this.isArray(keyOrArray)) { - r = [] - for (var i = 0, l = keyOrArray.length; i < l; i++) { - r.push(this.store[keyOrArray[i]]) + batch: function (objs, cb) { + var r = [] + for (var i = 0, l = objs.length; i < l; i++) { + this.save(objs[i], function(record) { + r.push(record) + }) } - } else { - r = this.store[keyOrArray] - if (r) r.key = keyOrArray - } - if (cb) this.lambda(cb).call(this, r) - return this - }, + if (cb) this.lambda(cb).call(this, r) + return this + }, - exists: function (key, cb) { - this.lambda(cb).call(this, !!(this.store[key])) - return this - }, + get: function (keyOrArray, cb) { + var r; + if (this.isArray(keyOrArray)) { + r = [] + for (var i = 0, l = keyOrArray.length; i < l; i++) { + r.push(this.store[keyOrArray[i]]) + } + } else { + r = this.store[keyOrArray] + if (r) r.key = keyOrArray + } + if (cb) this.lambda(cb).call(this, r) + return this + }, - all: function (cb) { - var r = [] - for (var i = 0, l = this.index.length; i < l; i++) { - var obj = this.store[this.index[i]] - obj.key = this.index[i] - r.push(obj) - } - this.fn(this.name, cb).call(this, r) - return this - }, + exists: function (key, cb) { + this.lambda(cb).call(this, !!(this.store[key])) + return this + }, - remove: function (keyOrArray, cb) { - var del = this.isArray(keyOrArray) ? keyOrArray : [keyOrArray] - for (var i = 0, l = del.length; i < l; i++) { - var key = del[i].key ? del[i].key : del[i] - var where = this.indexOf(this.index, key) - if (where < 0) continue /* key not present */ - delete this.store[key] - this.index.splice(where, 1) - } - window.top.name = JSON.stringify(data) - if (cb) this.lambda(cb).call(this) - return this - }, + all: function (cb) { + var r = [] + for (var i = 0, l = this.index.length; i < l; i++) { + var obj = this.store[this.index[i]] + obj.key = this.index[i] + r.push(obj) + } + this.fn(this.name, cb).call(this, r) + return this + }, + + remove: function (keyOrArray, cb) { + var del = this.isArray(keyOrArray) ? keyOrArray : [keyOrArray] + for (var i = 0, l = del.length; i < l; i++) { + var key = del[i].key ? del[i].key : del[i] + var where = this.indexOf(this.index, key) + if (where < 0) continue /* key not present */ + delete this.store[key] + this.index.splice(where, 1) + } + window.top.name = JSON.stringify(data) + if (cb) this.lambda(cb).call(this) + return this + }, - nuke: function (cb) { - this.store = data[this.name].store = {} - this.index = data[this.name].index = [] - window.top.name = JSON.stringify(data) - if (cb) this.lambda(cb).call(this) - return this + nuke: function (cb) { + this.store = data[this.name].store = {} + this.index = data[this.name].index = [] + window.top.name = JSON.stringify(data) + if (cb) this.lambda(cb).call(this) + return this + } } - } -///// -})()) \ No newline at end of file + ///// + })()) +} \ No newline at end of file diff --git a/src/sync-client.js b/src/sync-client.js index 04dc0c3..e5580cc 100755 --- a/src/sync-client.js +++ b/src/sync-client.js @@ -1,5 +1,5 @@ var CryptoJS = require("../libs/generated/crypto"); -var Lawnchair = require('../libs/generated/lawnchair'); +var Lawnchair = require('../libs/lawnchair/lawnchair'); var defaultCloudHandler = require('./cloudHandler'); var cidProvider = require('./clientIdProvider'); @@ -10,7 +10,7 @@ module.exports = newClient; function newClient(id) { var clientId = (id || '') + cidProvider.getClientId(); - + var self = { // CONFIG