├── README.md ├── db ├── has.js └── SQL.js ├── LocalDB.js ├── tests ├── runTests.html ├── LocalStorage.js └── module.js └── LocalStorage.js /README.md: -------------------------------------------------------------------------------- 1 | Unfinished! -------------------------------------------------------------------------------- /db/has.js: -------------------------------------------------------------------------------- 1 | define(["dojo/has"], function(has){ 2 | has.add("indexed", !!window.IndexedDB); 3 | return has; 4 | }); -------------------------------------------------------------------------------- /LocalDB.js: -------------------------------------------------------------------------------- 1 | define(["./db/has!indexedb?./db/IndexedDB:sql?./db/SQL:./LocalStorage"], function(LocalDB){ 2 | // module: 3 | // dojo/store/LocalDB 4 | // summary: 5 | // The module defines an object store based on local database access 6 | return LocalDB; 7 | }); 8 | -------------------------------------------------------------------------------- /tests/runTests.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Dojo CORE and BASE D.O.H. Unit Test Runner 5 | 6 | 7 | Redirecting to D.O.H runner. 8 | 9 | 10 | -------------------------------------------------------------------------------- /tests/LocalStorage.js: -------------------------------------------------------------------------------- 1 | define([ 2 | "doh/main", 3 | "../LocalStorage" 4 | ], function(doh, LocalStorage){ 5 | var store = new LocalStorage({storeId: "test"}); 6 | doh.register("localdb/tests/LocalStorage", { 7 | "io": { 8 | runTest: function(t){ 9 | store.put({ 10 | id: 1, 11 | name: "One", 12 | odd: true 13 | }); 14 | doh.is(store.get(1).name, "One"); 15 | doh.is(store.get(1).odd, true); 16 | doh.is(store.getIdentity(store.get(1)), 1); 17 | store.add({ 18 | id: 2, 19 | name: "Two", 20 | odd: false 21 | }); 22 | doh.is(store.get(2).name, "Two"); 23 | doh.is(store.get(2).odd, false); 24 | doh.is(store.get(1).name, "One"); 25 | var overwriteAllowed; 26 | try{ 27 | store.put({ 28 | id: 2, 29 | name: "Three", 30 | odd: false 31 | }, {overwrite: false}); 32 | overwriteAllowed = true; 33 | }catch(e){ 34 | } 35 | doh.f(overwriteAllowed); 36 | doh.is(store.get(2).name, "Two"); 37 | doh.is(store.query().length, 2); 38 | var length = 0; 39 | store.query({odd:true}).forEach(function(object){ 40 | doh.is(object.odd, true); 41 | length++; 42 | }); 43 | doh.is(length, 1); 44 | store.remove(1); 45 | doh.is(store.get(1), undefined); 46 | store.remove(2); 47 | doh.is(store.get(2), undefined); 48 | doh.is(store.query().length, 0); 49 | store.query({odd:true}).forEach(function(object){ 50 | doh.error("should be empty"); 51 | }); 52 | } 53 | } 54 | }, function(){}, function(){ 55 | store.remove(1); 56 | store.remove(2); 57 | }); 58 | }); 59 | -------------------------------------------------------------------------------- /tests/module.js: -------------------------------------------------------------------------------- 1 | define([ 2 | "doh/main", 3 | "../LocalStorage", 4 | "../db/SQL" 5 | ], function(doh, LocalStorage, SQL){ 6 | function testStore(StoreClass){ 7 | var store = new StoreClass({storeId: "test"}); 8 | doh.register("localdb/tests/LocalStorage", 9 | { 10 | "io": { 11 | runTest: function(t){ 12 | store.put({ 13 | id: 1, 14 | name: "One", 15 | odd: true 16 | }); 17 | doh.is(store.get(1).name, "One"); 18 | doh.is(store.get(1).odd, true); 19 | doh.is(store.getIdentity(store.get(1)), 1); 20 | store.add({ 21 | id: 2, 22 | name: "Two", 23 | odd: false 24 | }); 25 | doh.is(store.get(2).name, "Two"); 26 | doh.is(store.get(2).odd, false); 27 | doh.is(store.get(1).name, "One"); 28 | var overwriteAllowed; 29 | try{ 30 | store.put({ 31 | id: 2, 32 | name: "Three", 33 | odd: false 34 | }, {overwrite: false}); 35 | overwriteAllowed = true; 36 | }catch(e){ 37 | } 38 | doh.f(overwriteAllowed); 39 | doh.is(store.get(2).name, "Two"); 40 | doh.is(store.query().length, 2); 41 | var length = 0; 42 | store.query({odd:true}).forEach(function(object){ 43 | doh.is(object.odd, true); 44 | length++; 45 | }); 46 | doh.is(length, 1); 47 | store.remove(1); 48 | doh.is(store.get(1), undefined); 49 | store.remove(2); 50 | doh.is(store.get(2), undefined); 51 | doh.is(store.query().length, 0); 52 | store.query({odd:true}).forEach(function(object){ 53 | doh.error("should be empty"); 54 | }); 55 | } 56 | } 57 | }, function(){}, function(){ 58 | store.remove(1); 59 | store.remove(2); 60 | }); 61 | } 62 | testStore(SQL); 63 | testStore(LocalStorage); 64 | }); 65 | -------------------------------------------------------------------------------- /db/SQL.js: -------------------------------------------------------------------------------- 1 | define(["dojo/_base/declare"], function(declare) { 2 | // module: 3 | // dojo/store/LocalStorage 4 | // summary: 5 | // The module defines an object store based on local storage 6 | 7 | 8 | return declare([], { 9 | constructor: function(config){ 10 | this.database = openDatabase(config.name || "db", true, true, true);// TODO: what are these arguments supposed to be? 11 | this.table = config.table; 12 | this.idColumn = config.idColumn || "id"; 13 | this.indexPrefix = config.indexPrefix || "idx_"; 14 | }, 15 | idProperty: "id", 16 | selectColumns: ["*"], 17 | get: function(id){ 18 | return when(this.executeSql("SELECT " + this.selectColumns.join(",") + " FROM " + this.table + " WHERE " + idColumn + "=?", [id]), function(result){ 19 | return first(result.rows); 20 | }); 21 | }, 22 | getId: function(object){ 23 | return object[idColumn]; 24 | }, 25 | "delete": function(id){ 26 | return store.executeSql("DELETE FROM " + config.table + " WHERE " + idColumn + "=?", [id]); // Promise 27 | }, 28 | identifyGeneratedKey: true, 29 | add: function(object, directives){ 30 | var params = [], vals = [], cols = []; 31 | for(var i in object){ 32 | if(object.hasOwnProperty(i)){ 33 | cols.push(i); 34 | vals.push('?'); 35 | params.push(object[i]); 36 | } 37 | } 38 | if(store.identifyGeneratedKey){ 39 | params.idColumn = config.idColumn; 40 | } 41 | var sql = "INSERT INTO " + config.table + " (" + cols.join(',') + ") VALUES (" + vals.join(',') + ")"; 42 | return when(store.executeSql(sql, params), function(results) { 43 | var id = results.insertId; 44 | object[idColumn] = id; 45 | return id; 46 | }); 47 | }, 48 | put: function(object, directives){ 49 | directives = directives || {}; 50 | var id = directives.id || object[this.idProperty]; 51 | var overwrite = directives.overwrite; 52 | if(overwrite === undefined){ 53 | overwrite = this.get(id); 54 | } 55 | 56 | if(!overwrite){ 57 | store.add(object, directives); 58 | } 59 | var sql = "UPDATE " + config.table + " SET "; 60 | var first = true; 61 | var params = []; 62 | for(var i in object){ 63 | if(object.hasOwnProperty(i)){ 64 | if(first) first = false; 65 | else sql += ","; 66 | sql += i + "=?"; 67 | params.push(object[i]); 68 | } 69 | } 70 | sql += " WHERE " + idColumn + "=?"; 71 | params.push(object[idColumn]); 72 | 73 | return when(store.executeSql(sql, params), function(result){ 74 | return id; 75 | }); 76 | }, 77 | query: function(query, options){ 78 | 79 | }, 80 | executeSql: function(sql, parameters){ 81 | var deferred = defer(); 82 | var result, error; 83 | database.executeSql(sql, parameters, function(value){ 84 | deferred.resolve(result = value); 85 | }, function(e){ 86 | deferred.reject(error = e); 87 | }); 88 | // return synchronously if the data is already available. 89 | if(result){ 90 | return result; 91 | } 92 | if(error){ 93 | throw error; 94 | } 95 | return deferred.promise; 96 | } 97 | 98 | }); 99 | }); 100 | -------------------------------------------------------------------------------- /LocalStorage.js: -------------------------------------------------------------------------------- 1 | define(["dojo/_base/declare", "dojo/store/Memory", "dojo/json"], function(declare, Memory, JSON) { 2 | // module: 3 | // dojo/store/LocalStorage 4 | // summary: 5 | // The module defines an object store based on local storage 6 | 7 | 8 | return declare([Memory], { 9 | // summary: 10 | // This is a local storage object store. It implements dojo.store.api.Store. 11 | // storeId: String 12 | // An identifier for the local store, allows you to have distinct local stores by setting unique ids on each 13 | storeId: "default-store", 14 | constructor: function(args){ 15 | this.idPrefix = args.storeId + "-"; 16 | }, 17 | get: function(id){ 18 | // summary: 19 | // Retrieves an object by its identity 20 | // id: Number 21 | // The identity to use to lookup the object 22 | // returns: Object 23 | // The object in the store that matches the given id. 24 | return JSON.parse(localStorage.getItem(this.idPrefix + id)); 25 | }, 26 | put: function(object, options){ 27 | // summary: 28 | // Stores an object 29 | // object: Object 30 | // The object to store. 31 | // options: dojo.store.api.Store.PutDirectives?? 32 | // Additional metadata for storing the data. Includes an "id" 33 | // property if a specific id is to be used. 34 | // returns: Number 35 | var data = this.data, 36 | index = this.index, 37 | options = options || {}, 38 | idProperty = this.idProperty; 39 | var id = this.idPrefix + (object[idProperty] = (options && "id" in options) ? options.id : idProperty in object ? object[idProperty] : Math.random()); 40 | if(typeof options.overwrite == "boolean"){ 41 | if((localStorage.getItem(id) == null) == options.overwrite){ 42 | throw new Error("Overwrite " + (options.overwrite ? "required" : "not allowed")); 43 | } 44 | } 45 | localStorage.setItem(id, JSON.stringify(object)); 46 | return this.inherited(arguments); 47 | }, 48 | remove: function(id){ 49 | // summary: 50 | // Deletes an object by its identity 51 | // id: Number 52 | // The identity to use to delete the object 53 | // returns: Boolean 54 | // Returns true if an object was removed, falsy (undefined) if no object matched the id 55 | localStorage.removeItem(this.idPrefix + id); 56 | return this.inherited(arguments); 57 | }, 58 | query: function(query, options){ 59 | // summary: 60 | // Queries the store for objects. 61 | // query: Object 62 | // The query to use for retrieving objects from the store. 63 | // options: dojo.store.api.Store.QueryOptions? 64 | // The optional arguments to apply to the resultset. 65 | // returns: dojo.store.api.Store.QueryResults 66 | // The results of the query, extended with iterative methods. 67 | // 68 | // example: 69 | // Given the following store: 70 | // 71 | // | var store = new dojo.store.Memory({ 72 | // | data: [ 73 | // | {id: 1, name: "one", prime: false }, 74 | // | {id: 2, name: "two", even: true, prime: true}, 75 | // | {id: 3, name: "three", prime: true}, 76 | // | {id: 4, name: "four", even: true, prime: false}, 77 | // | {id: 5, name: "five", prime: true} 78 | // | ] 79 | // | }); 80 | // 81 | // ...find all items where "prime" is true: 82 | // 83 | // | var results = store.query({ prime: true }); 84 | // 85 | // ...or find all items where "even" is true: 86 | // 87 | // | var results = store.query({ even: true }); 88 | if(!this._loaded){ 89 | // load the data from local storage 90 | this._loaded = true; 91 | var index = this.index = {}; 92 | var data = this.data = []; 93 | var idPrefix = this.idPrefix; 94 | var idPrefixLength = idPrefix.length; 95 | for(var i = 0, l = localStorage.length; i < l; i++){ 96 | // iterate through each key 97 | var key = localStorage.key(i); 98 | 99 | if(key.slice(0, idPrefixLength) == idPrefix){ 100 | var object = JSON.parse(localStorage.getItem(key)); 101 | index[object[this.idProperty]] = data.push(object) - 1; 102 | } 103 | } 104 | } 105 | return this.inherited(arguments); 106 | } 107 | }); 108 | 109 | }); 110 | --------------------------------------------------------------------------------