├── README.md ├── build ├── db.js ├── db.min.js └── dbjs.xpi ├── builder ├── JSBuilder.py ├── build.py └── jar │ ├── js.jar │ └── yuicompressor-2.4.6.jar ├── extension ├── README.md ├── data │ └── bridge.js ├── doc │ └── main.md ├── lib │ ├── db.js │ └── main.js └── package.json ├── src ├── functions.js ├── intro.js ├── outro.js ├── var.intro.js ├── var.outro.js ├── web │ ├── Database.js │ ├── functions.js │ ├── outro.js │ └── var.private.js └── xul │ ├── Database.js │ ├── functions.js │ └── var.private.js └── test └── db.html /README.md: -------------------------------------------------------------------------------- 1 | db.js :: Simplified Web SQL Database Management 2 | =============================================== 3 | 4 | db.js goal is to simplify common [Web SQL Database](http://www.w3.org/TR/webdatabase/) operations through an intuitive interface. 5 | 6 | Main db.js features are: 7 | 8 | * **CPU and memory safe** thanks to an interface that does not require up to 3 callbacks per each asynchronous operations 9 | * **tiny** library with **no** external **dependencies** 10 | * simplified **CREATE TABLE** with primary id autoincrement shortcut 11 | * simplified **INSERT** operations, accepting one or more arrays or objects automatically 12 | * simplified **TRUNCATE TABLE** operation, by default not possible in SQLite 13 | * **unified callback** behavior via custom **Event object** so that one callback could be potentially used with every operation 14 | * **NEW** db.js interface and Web SQL Database in Firefox via dedicated, non obtrusive, and safe [db.js add-on](https://addons.mozilla.org/en-US/firefox/addon/web-sql-database-is-back-db/) 15 | 16 | Firefox add-on now has a passes same [unit tests](http://www.3site.eu/_/test/db.html) created for browsers with native Web SQL Database support. 17 | 18 | The only known issue I am working on is the local file without a domain ... right now Firefox add-on does not work with empty documents or *file://* protocol, a locl webserver or an online webpage is necessary. 19 | 20 | 21 | Update 22 | ------ 23 | 24 | [The Firefox Add-on is out](https://addons.mozilla.org/en-US/firefox/addon/web-sql-database-is-back-db/), what's missing now is Internet Explorer. 25 | Some "*idiot*" like me who thinks SQLite should be used, created a fully managed C# port of SQLite which actually performs like hell. 26 | If you have any experience with Silverlight and JS bindings, please help me to finalize this project and bring SQLite in every browser out there, included mobile. 27 | 28 | 29 | API 30 | === 31 | 32 | following the list of methods and examples 33 | 34 | 35 | constructor 36 | ----------- 37 | 38 | Create a *db* instance following native JS behavior where *new* can or cannot be used. 39 | 40 | var db = new Database; 41 | /** 42 | * default options: 43 | * name "db" the database name 44 | * size (int)5Mb the initial size of the db 45 | * description "data" the database description 46 | * version "1.0" the db version 47 | */ 48 | 49 | var db2 = new Database({ 50 | name: "personal_data", 51 | description: "my business", 52 | size: 1 * 1024 * 1024 // 1Mb 53 | }); 54 | 55 | 56 | db.close() 57 | ---------- 58 | 59 | Simulate a *close database* action on native Web SQL Database implementations, close the opened file asynchronously in Firefox ad-on. 60 | Any active transaction will be completed/committed but it won't be possible to perform other operations with the current db object. 61 | 62 | var db = new Database; 63 | db 64 | .read("SELECT * FROM stuff", showStuff) 65 | .close() 66 | ; 67 | 68 | // to be sure we won't reuse the db further ... 69 | db = null; 70 | 71 | 72 | db.create(tableName, fields[, callback]) 73 | ---------------------------------------- 74 | 75 | Create a *tableName* only if does not exist already. *fields* is an Array of field where if the first value is undefined or null an autoincrement **id** will be created automatically. 76 | 77 | // contacts table creation example 78 | db.create("contacts", [ 79 | null, // id INTEGER PRIMARY KEY AUTOINCREMENT 80 | "name TEXT NOT NULL", // the second field of this table 81 | "cell TEXT NOT NUll" // the third field of this table 82 | ]); 83 | 84 | // table with no id 85 | db.create("events", [ 86 | "date INTEGER", // used to store 20110827 as example 87 | "description TEXT" // used to store the event description 88 | ]); 89 | 90 | // use a callback to be sure about the operation 91 | db.create(name, fields, function (evt) { 92 | // if "success" either it was created 93 | // or it was already there 94 | if (evt.type === "success") { 95 | // keep working with this table 96 | } else if (evt.type === "error") { 97 | // inform the user it was not possible to create the table 98 | } 99 | }); 100 | 101 | 102 | 103 | db.insert(tableName, data[, callback]) 104 | ---------------------------------------- 105 | 106 | Insert data into a table. 107 | 108 | db.insert("contacts", [ 109 | null, // id, incremented by default 110 | "WebReflection", // name 111 | "911" // phone number ( for development emergencies! ) 112 | ]); 113 | 114 | *data* can be an Array or a collection of Arrays 115 | 116 | db.insert("contacts", [[ 117 | null, "Mate", "01234" 118 | ], [ 119 | null, "Dude", "56789" 120 | ]]); 121 | 122 | `insert()`, as well as every other *db* method, can accept a callback as third argument. This will be invoked once when all operations have been completed. 123 | 124 | Please **note** that databases are basically always **homogenous collections** of data. 125 | If you have a list of *key/value* pairs, consider [JSONH](https://github.com/WebReflection/JSONH) as solution for its translation into a valid array for an `insert()`. 126 | 127 | // generic collection of data 128 | var myData = [{ 129 | id: 1, 130 | name: "Mate", 131 | cell: "01234" 132 | }, { 133 | id: 2, 134 | name: "Dude", 135 | cell: "56789" 136 | }]; 137 | 138 | // create packed version of the data (it's FAST!) 139 | var 140 | myDBData = JSONH.pack(myData), 141 | headers = myDBData[0] 142 | ; 143 | // remove JSONH headers info (number of headers plus first index) 144 | myDBData = myDBData.slice(headers + 1); 145 | 146 | // create an array of values per table row 147 | for (var dbData = [], i = 0; i < myDBData.length; i += headers) { 148 | dbData.push(myDBData.slice(i, i + headers)); 149 | } 150 | 151 | // insert into db all of them 152 | db.insert("contacts", dbData); 153 | 154 | 155 | db.drop(tableName[, callback]) 156 | ------------------------------ 157 | Drop/remove a table only if exists. 158 | 159 | db.drop("contacts", function (e) { 160 | if (e.type === "success") { 161 | alert("... feeling lonely ..."); 162 | } 163 | }); 164 | 165 | 166 | db.truncate(tableName[, callback]) 167 | ---------------------------------- 168 | 169 | Truncate is not natively supported by SQLite syntax but this method is clever enough and super fast: it saves the *creation table* statement first, and if `db.drop(tableName)` operation was successful, it creates the table again as it was before. 170 | 171 | db.truncate("contacts", function (e) { 172 | if (e.type === "success") { 173 | alert("Ready for a new life!"); 174 | } 175 | }); 176 | 177 | 178 | db.query(SQL[, arguments[, callback]]) 179 | ------------------------------------ 180 | 181 | Every `db.query()` call creates a new *transaction* and this method is able to accept one or more SQL statements per transactions. 182 | 183 | // single UPDATE example 184 | db.query( 185 | 'UPDATE TABLE contacts SET cell = ? WHERE name = ?', 186 | [ 187 | "76543", // the new cell to update 188 | "Mate" // the contact name which cell has to be updated 189 | ] 190 | ); 191 | 192 | // multiple UPDATE example. One query, many updates 193 | db.query( 194 | 'UPDATE TABLE contacts SET cell = ? WHERE name = ?', [ 195 | [ 196 | "76543", // the new cell to update 197 | "Mate" // the contact name which cell has to be updated 198 | ], [ 199 | "35468", // the new cell to update 200 | "Dude" // the contact name which cell has to be updated 201 | ] 202 | ]); 203 | 204 | // multiple query example. N queries, N updates 205 | db.query([ 206 | 'UPDATE TABLE contacts SET cell = ? WHERE name = ?', 207 | 'UPDATE TABLE contacts SET name = ? WHERE cell = ?' 208 | ], [ 209 | [ 210 | "76543", // the new cell to update 211 | "Mate" // the contact name which cell has to be updated 212 | ], [ 213 | "Dooooode", // the new cell to update 214 | "35468" // the contact name which cell has to be updated 215 | ] 216 | ]); 217 | 218 | Everything is still valid if the *arguments* is an object, rather than an array. 219 | 220 | db.query( 221 | 'UPDATE TABLE contacts SET cell = :cell WHERE name = :name', 222 | { 223 | cell: "76543", 224 | name: "Mate" 225 | } 226 | ); 227 | 228 | With `db.query()` *arguments* could be an object, an array, or a collection of both. 229 | 230 | 231 | db.read(SQL[, arguments[, callback]]) 232 | ----------------------------------- 233 | 234 | The `db.read()` method is similar to the `db.query()` one except it uses **readTransaction** rather than **transaction**. 235 | 236 | The difference between these two native methods, Firefox add-on a part, is that *transaction* operates in **read/write** mode while *readTransaction* operates in **read only** mode. 237 | 238 | I could not measure performances but if a query is about reading, rather than inserting, deleting, or updating, `db.read()` is the method you are looking for, assuming things are optimized and faster on SQLite level. 239 | 240 | 241 | Callback Event object 242 | ===================== 243 | 244 | Every callback will always receive the same kind of *Event objetct*. 245 | This object has these peculiarities: 246 | 247 | // if everything was fine ... 248 | { 249 | type: "success", 250 | result: SQLStatementCallback, 251 | item: function (i) { // shortcut 252 | return this.result.rows.item(i); 253 | }, 254 | length: result.rows.length // as shortcut, 255 | db: reference // the database object that performed the query 256 | } 257 | 258 | // if something went terribly wrong ... 259 | { 260 | type: "error", 261 | error: SQLStatementErrorCallback, 262 | db: reference // the database object that performed the query 263 | } 264 | 265 | In this way it is **always possible to recycle callbacks** rather than create a new function per each operation. 266 | 267 | It is also convenient to have length and item method in the object itself ... things are less "*boring*" here. 268 | 269 | db.read('SELECT * FROM contacts', function (e) { 270 | for (var i = 0; i < e.length; i++) { 271 | console.log(e.item(i)); 272 | } 273 | }); 274 | 275 | Bear in mind every time you access an item of a result set, a fresh new object may be created. 276 | Store the reference once rather than `e.item(i)` or `e.result.rows.item(i)` each time to gain performances. 277 | 278 | 279 | WHY 280 | === 281 | 282 | Regardless the fact **Web SQL Database** has been **deprecated** by **W3C** thanks to **Mozilla influence**, and probably somebody else as well, **SQLite is present by default everywhere**, starting from your mobile phone, tablet, Operating System ... etc etc. 283 | SQLite **is a de-facto standard** only W3C could drop so easily. 284 | 285 | I mean ... my [devpro.it](http://devpro.it/) site is based on SQLite since **ever** without a single problem and you can bet I have never updated a single piece of code related to the SQLite engine. 286 | 287 | Mozilla talked about [advantages of IndexedDB over Web SQL Database](http://hacks.mozilla.org/2010/06/comparing-indexeddb-and-webdatabase/) and **there isn't a single example where IndexedDb is faster, smaller, easier, or better, than SQLite**. 288 | I call it **epic-fail** due somebody with *pretending technicals knowledge* able to make such *political decision* and the proof is under everybody eyes. 289 | In [this explanation](http://hacks.mozilla.org/2010/06/beyond-html5-database-apis-and-the-road-to-indexeddb/) you can find the Mozilla reason here summarized: 290 | 291 | * despite the ubiquity that SQL enjoys, there isn’t a single normative SQL standard that defines the technology 292 | * We think SQLite is an extremely useful technology for applications, and **make it available for Firefox extensions and trusted code** 293 | * we don’t want changes to SQLite to affect the web later, and don’t think harnessing major browser releases (and a web standard) to SQLite is prudent 294 | * IndexedDB does not have this problem (*n.d. as well as SQL92 standard*) 295 | 296 | As we can read, **SQLite is integrated in Firefox since ever** because **it is a wonderful db engine** and this is why Mozilla developers and XUL components can **widely use it everywhere** but "*those stupid web developers should not*" ... personal comment out of these facts. 297 | 298 | As summary, **Web SQL Database is everywhere** except it's not exposed in Firefox and probably IE (... SURPRISEEEEE!!! ). 299 | **SQL syntax and purpose is not easy to replace** with whatever **IndexedDb based indeed on SQLite** behind the scene. 300 | 301 | **SQL is optimized on core level** which is **something Web Developers are missing** when it comes to performances. 302 | 303 | Moreover, **JavaScript itself changed** over these years **as well as HTML**, the argument *we cannot be stuck behind SQLite* is pointless out of a company that is updating the major version every few weeks. 304 | 305 | Accordingly, I don't care about these decisions, **all I care is about Web Development** and since nobody else complained that much about SQLite out there, I do believe even if W3C decided it's deprecated **it will last for very long time** in all Chrome, Opera, and Webkit/Safari browsers ... aka: mobile! 306 | -------------------------------------------------------------------------------- /build/db.js: -------------------------------------------------------------------------------- 1 | /*! 2 | (C) Andrea Giammarchi, @WebReflection - Mit Style License 3 | */ 4 | /**@license (C) Andrea Giammarchi, @WebReflection - Mit Style License 5 | */ 6 | var Database = (function (window, $Database) {"use strict"; 7 | 8 | if (window[$Database] && !window.opera) return window[$Database]; 9 | 10 | /** 11 | * Copyright (C) 2011 by Andrea Giammarchi, @WebReflection 12 | * 13 | * Permission is hereby granted, free of charge, to any person obtaining a copy 14 | * of this software and associated documentation files (the "Software"), to deal 15 | * in the Software without restriction, including without limitation the rights 16 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 | * copies of the Software, and to permit persons to whom the Software is 18 | * furnished to do so, subject to the following conditions: 19 | * 20 | * The above copyright notice and this permission notice shall be included in 21 | * all copies or substantial portions of the Software. 22 | * 23 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 29 | * THE SOFTWARE. 30 | */ 31 | 32 | 33 | function Database(options) { 34 | 35 | if (!(this instanceof Database)) 36 | return new Database(options) 37 | ; 38 | 39 | var self = this; 40 | 41 | options || (options = {}); 42 | 43 | // internal db invisible outside the closure 44 | defineProperty(self, expando, { 45 | enumerable: !1, 46 | writable: !0, 47 | configurable: !0, 48 | value: openDatabase( 49 | self.name = options.name || document.domain.replace(/\./g, "-") || "db", 50 | self.version = options.version || "1.0", 51 | self.description = options.description || "data", 52 | self.size = options.size || SIZE, 53 | empty 54 | ) 55 | }); 56 | 57 | // but internal db can reach self inside the closure 58 | self[expando][expando] = self; 59 | 60 | return self; 61 | } 62 | 63 | 64 | function close() { 65 | // hoping that Browsers will call asyncClose on their side 66 | // cannot actually remove references or transactions may fail 67 | // this[expando][expando] = null; 68 | // delete this[expando]; 69 | } 70 | 71 | function create(name, fields, fn) { 72 | fields[0] || (fields[0] = autoIncrement); 73 | this.query("CREATE" + TABLE + IF + " NOT" + EXISTS + name + " (" + fields.join(", ") + ")", fn); 74 | return this; 75 | } 76 | 77 | function createReadOrQuery(method) { 78 | method += "ransaction"; 79 | return function readOrWrite(SQL, A, fn) { 80 | var self = this; 81 | if (typeof A == "function") { 82 | fn = A; 83 | A = []; 84 | } 85 | self[expando][method](function (t) { 86 | for (var 87 | sql = arrayfy(SQL), 88 | a = toListOfParameters(A), 89 | i = 0, 90 | length = max(sql.length, a.length), 91 | tr = (t[expando] = {self:self, fn:fn, i:length}), 92 | tmp; 93 | i < length; ++i 94 | ) { 95 | t.executeSql(sql[i] || sql[0], a[i], success, error); 96 | } 97 | }); 98 | return self; 99 | }; 100 | } 101 | 102 | function drop(name, fn) { 103 | this.query(DROP + TABLE + IF + EXISTS + name, fn); 104 | return this; 105 | } 106 | 107 | function error(t, result) { 108 | if (t = t[expando]) { 109 | --t.i || (t.fn || empty)({ 110 | type: "error", 111 | error: result, 112 | db: t.self 113 | }); 114 | } 115 | } 116 | 117 | function insert(name, values, fn) { 118 | for (var 119 | self = this, 120 | sql = [], 121 | a = toListOfParameters(values), 122 | i = 0, length = a.length, 123 | many = Array(a[0].length + 1).join(", ?").slice(2); 124 | i < length; ++i 125 | ) { 126 | sql[i] = 'INSERT INTO ' + name + ' VALUES (' + many + ')'; 127 | } 128 | self.query(sql, a, fn); 129 | return self; 130 | } 131 | 132 | function success(t, result) { 133 | if (t = t[expando]) { 134 | --t.i || (t.fn || empty)({ 135 | type: "success", 136 | result: result, 137 | item: eventItem, 138 | length: result.rows.length, 139 | db: t.self 140 | }); 141 | } 142 | } 143 | 144 | function truncate(name, fn) { 145 | var 146 | self = this, 147 | rows, item 148 | ; 149 | self.query('SELECT * FROM sqlite_master WHERE name = ?', arrayfy(name), function (e) { 150 | if (e.type == "success") { 151 | item = e.length && e.result.rows.item(0); 152 | if (item && item.type == "table" && (item.tbl_name || item.name) == name) { 153 | // safer to perform double transaction here 154 | // due XUL native SQLite problems that actually "waried me" ... 155 | return self.query(DROP + TABLE + name, function (e) { 156 | self.query(item.sql, fn); 157 | }); 158 | } 159 | e.type = "error"; 160 | e.error = {message: "table " + name + " does not exists"}; 161 | delete e.result; 162 | } 163 | fn(e); 164 | }); 165 | return self; 166 | } 167 | 168 | 169 | function arrayfy(whatever) { 170 | return concat.call([], whatever === undefined ? [] : whatever); 171 | } 172 | 173 | function empty() {} 174 | 175 | function eventItem(i) { 176 | return this.result.rows.item(i); 177 | } 178 | 179 | function toListOfParameters(values) { 180 | return !isArray(values) || typeof values[0] != "object" || !values[0] ? [values] : values; 181 | } 182 | 183 | 184 | var 185 | undefined, 186 | SIZE = 5 * 1024 * 1024, 187 | TABLE = " TABLE ", 188 | DROP = "DROP", 189 | EXISTS = " EXISTS ", 190 | IF = "IF", 191 | autoIncrement = "id INTEGER PRIMARY KEY AUTOINCREMENT", 192 | Array = window.Array, 193 | Math = window.Math, 194 | max = Math.max, 195 | concat = [].concat, 196 | 197 | read = createReadOrQuery("readT"), 198 | query = createReadOrQuery("t"), 199 | document = window.document, 200 | openDatabase = window.openDatabase, 201 | expando = "_" + ("" + Math.random()).slice(2), 202 | isArray = Array.isArray || function (toString, a) { 203 | a = toString.call([]); 204 | return function isArray(o) { 205 | return a == toString.call(o); 206 | }; 207 | }({}.toString), 208 | Object = window.Object, 209 | defineProperty = Object.defineProperty || function (o, k, d) { 210 | o[k] = d.value; 211 | return o; 212 | }, 213 | DatabasePrototype = Database.prototype 214 | 215 | 216 | ; 217 | 218 | DatabasePrototype.close = close; 219 | DatabasePrototype.create = create; 220 | DatabasePrototype.drop = drop; 221 | DatabasePrototype.insert = insert; 222 | DatabasePrototype.read = read; 223 | DatabasePrototype.query = query; 224 | DatabasePrototype.truncate = truncate; 225 | 226 | 227 | return Database; 228 | 229 | }(this, "Database")); -------------------------------------------------------------------------------- /build/db.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | (C) Andrea Giammarchi, @WebReflection - Mit Style License 3 | */ 4 | var Database=(function(n,F){if(n[F]&&!n.opera){return n[F]}function v(K){if(!(this instanceof v)){return new v(K)}var J=this;K||(K={});e(J,i,{enumerable:!1,writable:!0,configurable:!0,value:j(J.name=K.name||y.domain.replace(/\./g,"-")||"db",J.version=K.version||"1.0",J.description=K.description||"data",J.size=K.size||I,q)});J[i][i]=J;return J}function u(){}function t(K,J,L){J[0]||(J[0]=r);this.query("CREATE"+p+l+" NOT"+A+K+" ("+J.join(", ")+")",L);return this}function B(K){K+="ransaction";return function J(O,L,N){var M=this;if(typeof L=="function"){N=L;L=[]}M[i][K](function(S){for(var V=x(O),P=H(L),R=0,T=E(V.length,P.length),U=(S[i]={self:M,fn:N,i:T}),Q;R "' + fullPath('../' + minName) + '"') 79 | 80 | # Google Closure Compiler [slowest, more greedy, smaller size] 81 | # os.system('java -jar "' + fullPath('jar/compiler.jar') + '" --compilation_level=SIMPLE_OPTIMIZATIONS --language_in ECMASCRIPT5_STRICT --js "' + fullPath('../' + fullName) + '" --js_output_file "' + fullPath('../' + minName) + '"') 82 | 83 | # create the gzip version 84 | tmp = gzip.open(fullPath('../' + minName + '.gz'), 'w') 85 | tmp.write(read('../' + minName)) 86 | tmp.close() 87 | 88 | # print out the result of all precedent operations 89 | print('Minified size: ' + getSize('../' + minName)) 90 | print('Min + Gzip size: ' + getSize('../' + minName + '.gz')) 91 | 92 | # remove the gzipped version 93 | os.remove(fullPath('../' + minName + '.gz')) 94 | 95 | # put back code that should have not been included in the minified version or replaced 96 | write('../' + fullName, content) 97 | 98 | # print out the result of all precedent operations 99 | print('Full size: ' + getSize('../' + fullName)) 100 | 101 | -------------------------------------------------------------------------------- /builder/build.py: -------------------------------------------------------------------------------- 1 | # JSBuilder http://code.google.com/p/javascript-builder/ 2 | 3 | copyright = '(C) Andrea Giammarchi, @WebReflection - Mit Style License' 4 | 5 | import JSBuilder 6 | 7 | # embedded DOM version for HTML tests 8 | print ("") 9 | print ("-----------------------") 10 | print ("| db.js DOM version |") 11 | print ("-----------------------") 12 | JSBuilder.compile( 13 | copyright, 14 | 'build/db.js', 15 | 'build/db.min.js', 16 | [ 17 | "intro.js", 18 | "web/Database.js", 19 | "web/functions.js", 20 | "functions.js", 21 | "var.intro.js", 22 | "web/var.private.js", 23 | "var.outro.js", 24 | "web/outro.js", 25 | "outro.js" 26 | ], [ 27 | "/*{use_strict}*/" 28 | ], [ 29 | '"use strict";' 30 | ] 31 | ) 32 | print ("----------------------") 33 | print ("") 34 | print ("") 35 | print ("-----------------------") 36 | print ("| db.js XUL version |") 37 | print ("-----------------------") 38 | 39 | JSBuilder.compile( 40 | copyright, 41 | 'extension/lib/db.js', 42 | '', 43 | [ 44 | "intro.js", 45 | "xul/Database.js", 46 | "xul/functions.js", 47 | "functions.js", 48 | "var.intro.js", 49 | "xul/var.private.js", 50 | "var.outro.js", 51 | "outro.js" 52 | ], [ 53 | "/*{use_strict}*/" 54 | ], [ 55 | 'exports.Database = Database;' 56 | ] 57 | ) 58 | print ("----------------------") 59 | print ("") 60 | 61 | # automate the xpi creation/build process 62 | # change this address if necessary to point to proper sdk folder 63 | relativeSDKPath = "../../Desktop/addon-sdk-1.0/bin/" 64 | try: 65 | import os 66 | current = JSBuilder.fullPath("../") 67 | sdk = JSBuilder.fullPath(relativeSDKPath) 68 | os.chdir(sdk) 69 | os.system("./cfx xpi --pkgdir=" + current + "/extension") 70 | f = open("dbjs.xpi", "r") 71 | content = f.read() 72 | f.close() 73 | f = open(current + "/build/dbjs.xpi", "w") 74 | f.write(content) 75 | f.close() 76 | os.remove("dbjs.xpi") 77 | except: 78 | print("SDK folder not found") 79 | 80 | # let me read the result ... 81 | import time 82 | time.sleep(2) -------------------------------------------------------------------------------- /builder/jar/js.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebReflection/Database/0749954a821353f27449b53e7cca30f7d8ed8c20/builder/jar/js.jar -------------------------------------------------------------------------------- /builder/jar/yuicompressor-2.4.6.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebReflection/Database/0749954a821353f27449b53e7cca30f7d8ed8c20/builder/jar/yuicompressor-2.4.6.jar -------------------------------------------------------------------------------- /extension/README.md: -------------------------------------------------------------------------------- 1 | Web SQL Database Is Back: db.js 2 | =============================== 3 | 4 | This add-on brings [db.js](https://github.com/WebReflection/Database) API available in Firefox and in every webpage. 5 | 6 | 7 | About 8 | ----- 9 | 10 | SQLite is a de-facto standard lightweight database engine present in almost every software you know, included DVD Players. 11 | Thanks to its performances, features, and portability, SQLite has been adopted by almost every browser, included Mozilla Family. 12 | Unfortunately Mozilla folks decided to do not expose SQLite through the Web SQL Database interface defined by W3C and adopted by any other browser with the lovely exception of Internet Explorer. 13 | 14 | With **db.js** the problem is solved: **SQLite Is Back** and with a nicely, simplified, and intuitive interface. 15 | 16 | Enjoy, [Andrea Giammarchi](http://webreflection.blogspot.com/) -------------------------------------------------------------------------------- /extension/data/bridge.js: -------------------------------------------------------------------------------- 1 | 2 | (function (port, domain, window, sbWindow) { 3 | 4 | /** 5 | * Copyright (C) 2011 by Andrea Giammarchi, @WebReflection 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | 26 | function eventItem(i) { 27 | return this.result.rows.item(i); 28 | } 29 | 30 | function item(i) { 31 | return this[i]; 32 | } 33 | 34 | function empty() {} 35 | 36 | var 37 | SIZE = 5 * 1024 * 1024, 38 | defineProperty = Object.defineProperty || function (o, k, v) { 39 | o[k] = v.value; 40 | return o; 41 | }, 42 | uid = "_" + domain + "_" + ("" + Math.random()).slice(2) + "_" + (+ new Date) + "_", 43 | drop_uid = "_drop" + uid, 44 | create_uid = "_create" + uid, 45 | insert_uid = "_insert" + uid, 46 | query_uid = "_query" + uid, 47 | truncate_uid = "_truncate" + uid, 48 | uids = [drop_uid, create_uid, insert_uid, query_uid, truncate_uid], 49 | instance = [], 50 | DatabasePrototype 51 | ; 52 | 53 | DatabasePrototype = (window.Database = function Database(options) { 54 | 55 | if (!(this instanceof Database)) 56 | return new Database(options) 57 | ; 58 | 59 | options || (options = {}); 60 | 61 | defineProperty(this, uid, { 62 | configurable: false, 63 | writable: false, 64 | enumerable: false, 65 | value: instance.push(this) - 1 66 | }); 67 | 68 | uids.forEach(function (uid) { 69 | defineProperty(this, uid, { 70 | configurable: false, 71 | writable: false, 72 | enumerable: false, 73 | value: [] 74 | }); 75 | }, this); 76 | 77 | port.emit("init", { 78 | domain: domain, 79 | options: options, 80 | i: this[uid] 81 | }); 82 | 83 | this.name = options.name || "db"; 84 | this.version = options.version || "1.0"; 85 | this.description = options.description || "data"; 86 | this.size = options.size || SIZE; 87 | 88 | }).prototype; 89 | 90 | DatabasePrototype.close = function close() { 91 | port.emit("dbclose", { 92 | i: this[uid] 93 | }); 94 | }; 95 | 96 | DatabasePrototype.create = function create(name, fields, fn) { 97 | port.emit("dbcreate", { 98 | uid: create_uid, 99 | id: this[create_uid].push(fn || empty) - 1, 100 | i: this[uid], 101 | result: [name, fields] 102 | }); 103 | return this; 104 | }; 105 | 106 | DatabasePrototype.drop = function drop(name, fn) { 107 | port.emit("dbdrop", { 108 | uid: drop_uid, 109 | id: this[drop_uid].push(fn || empty) - 1, 110 | i: this[uid], 111 | result: [name] 112 | }); 113 | return this; 114 | }; 115 | 116 | DatabasePrototype.insert = function insert(name, values, fn) { 117 | port.emit("dbinsert", { 118 | uid: insert_uid, 119 | id: this[insert_uid].push(fn || empty) - 1, 120 | i: this[uid], 121 | result: [name, values] 122 | }); 123 | return this; 124 | }; 125 | 126 | DatabasePrototype.read = 127 | DatabasePrototype.query = function query(SQL, A, fn) { 128 | var args = [SQL]; 129 | if (typeof A == "function") { 130 | fn = A; 131 | A = []; 132 | } else { 133 | args.push(A); 134 | } 135 | port.emit("dbquery", { 136 | uid: query_uid, 137 | id: this[query_uid].push(fn || empty) - 1, 138 | i: this[uid], 139 | result: args 140 | }); 141 | return this; 142 | }; 143 | 144 | DatabasePrototype.truncate = function truncate(name, fn) { 145 | port.emit("dbtruncate", { 146 | uid: truncate_uid, 147 | id: this[truncate_uid].push(fn || empty) - 1, 148 | i: this[uid], 149 | result: [name] 150 | }); 151 | return this; 152 | }; 153 | 154 | port.on("result", function (info) { 155 | var 156 | self = instance[info.i], 157 | queue = self[info.uid], 158 | result = info.result 159 | ; 160 | result.db = self; 161 | if ("result" in result) { 162 | result.item = eventItem; 163 | result.result.rows.item = item; 164 | } 165 | queue[info.id](result); 166 | delete queue[info.id]; 167 | }); 168 | 169 | port.emit("pagestart", { 170 | domain: domain, 171 | uid: uid 172 | }); 173 | 174 | sbWindow.addEventListener("unload", function () { 175 | instance.forEach(function (db) { 176 | db.close(); 177 | }); 178 | }, false); 179 | 180 | }( 181 | self.port, 182 | document.domain || "localhost", 183 | window.wrappedJSObject, 184 | window 185 | )); 186 | -------------------------------------------------------------------------------- /extension/doc/main.md: -------------------------------------------------------------------------------- 1 | Web SQL Database Is Back: db.js 2 | =============================== 3 | 4 | This add-on brings [db.js](https://github.com/WebReflection/Database) API available in Firefox and in every webpage. 5 | 6 | 7 | About 8 | ----- 9 | 10 | SQLite is a de-facto standard lightweight database engine present in almost every software you know, included DVD Players. 11 | Thanks to its performances, features, and portability, SQLite has been adopted by almost every browser, included Mozilla Family. 12 | Unfortunately Mozilla folks decided to do not expose SQLite through the Web SQL Database interface defined by W3C and adopted by any other browser with the lovely exception of Internet Explorer. 13 | 14 | With **db.js** the problem is solved: **SQLite Is Back** and with a nicely, simplified, and intuitive interface. 15 | 16 | Enjoy, [Andrea Giammarchi](http://webreflection.blogspot.com/) -------------------------------------------------------------------------------- /extension/lib/db.js: -------------------------------------------------------------------------------- 1 | /*! 2 | (C) Andrea Giammarchi, @WebReflection - Mit Style License 3 | */ 4 | /**@license (C) Andrea Giammarchi, @WebReflection - Mit Style License 5 | */ 6 | var Database = (function (window, $Database) {exports.Database = Database; 7 | 8 | if (window[$Database] && !window.opera) return window[$Database]; 9 | 10 | /** 11 | * Copyright (C) 2011 by Andrea Giammarchi, @WebReflection 12 | * 13 | * Permission is hereby granted, free of charge, to any person obtaining a copy 14 | * of this software and associated documentation files (the "Software"), to deal 15 | * in the Software without restriction, including without limitation the rights 16 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 | * copies of the Software, and to permit persons to whom the Software is 18 | * furnished to do so, subject to the following conditions: 19 | * 20 | * The above copyright notice and this permission notice shall be included in 21 | * all copies or substantial portions of the Software. 22 | * 23 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 29 | * THE SOFTWARE. 30 | */ 31 | 32 | 33 | 34 | function Database(options, domain) { 35 | 36 | if (!(this instanceof Database)) 37 | return new Database(options) 38 | ; 39 | 40 | var self = this, db; 41 | 42 | db = openDatabase(self.name = options.name || "db", domain); 43 | 44 | // fields assigned but ignored behind the scene in this version 45 | self.version = options.version || "1.0"; 46 | self.description = options.description || "data"; 47 | self.size = options.size || SIZE; 48 | 49 | self.close = function close() { 50 | db.asyncClose(); 51 | }; 52 | 53 | self.create = function create(name, fields, fn) { 54 | fields[0] || (fields[0] = autoIncrement); 55 | self.query("CREATE" + TABLE + IF + " NOT" + EXISTS + name + " (" + fields.join(", ") + ")", fn); 56 | return self; 57 | }; 58 | 59 | self.drop = function drop(name, fn) { 60 | self.query(DROP + TABLE + IF + EXISTS + name, fn); 61 | return self; 62 | }; 63 | 64 | self.insert = function insert(name, values, fn) { 65 | for (var 66 | sql = [], 67 | a = toListOfParameters(values), 68 | i = 0, length = a.length, 69 | many = Array(a[0].length + 1).join(", ?").slice(2); 70 | i < length; ++i 71 | ) { 72 | sql[i] = 'INSERT INTO ' + name + ' VALUES (' + many + ')'; 73 | } 74 | self.query(sql, a, fn); 75 | return self; 76 | }; 77 | 78 | self.read = self.query = function query(SQL, A, fn) { 79 | if (typeof A == "function") { 80 | fn = A; 81 | A = []; 82 | } 83 | db.beginTransaction(); 84 | for (var 85 | sql = arrayfy(SQL), 86 | a = toListOfParameters(A), 87 | i = 0, 88 | length = max(sql.length, a.length), 89 | tr = { 90 | i: length, 91 | self: self, 92 | fn: fn, 93 | db: db, 94 | handleResult: success, 95 | handleCompletion: complete, 96 | handleError: error 97 | }, 98 | statement, _; 99 | i < length; ++i 100 | ) { 101 | try { 102 | statement = db.createStatement(sql[i] || sql[0]); 103 | if (a[i]) { 104 | for (let param in statement.params) { 105 | statement.params[param] = a[i][param]; 106 | } 107 | } 108 | tr.statement = statement; 109 | statement.executeAsync(tr); 110 | } catch(e) { 111 | _ = e; 112 | break; 113 | } 114 | } 115 | if (_) { 116 | db.rollbackTransaction(); 117 | tr.handleError(_); 118 | } else { 119 | db.commitTransaction(); 120 | } 121 | }; 122 | 123 | self.truncate = function truncate(name, fn) { 124 | var rows, item; 125 | self.read('SELECT * FROM sqlite_master WHERE name = ?', arrayfy(name), function (e) { 126 | if (e.type == "success") { 127 | item = e.length && e.result.rows.item(0); 128 | if (item && item.type == "table" && (item.tbl_name || item.name) == name) { 129 | // safer to perform double transaction here 130 | // due XUL native SQLite problems that actually "waried me" ... 131 | return self.query(DROP + TABLE + name, function (e) { 132 | self.query(item.sql, fn); 133 | }); 134 | } 135 | e.type = "error"; 136 | e.error = {message: "table " + name + " does not exists"}; 137 | delete e.result; 138 | } 139 | fn(e); 140 | }); 141 | return self; 142 | }; 143 | 144 | } 145 | 146 | 147 | function complete(reason) { 148 | this.successful || --this.i || (this.fn || empty)({ 149 | type: "success", 150 | result: { 151 | insertId: this.db.lastInsertRowID, 152 | rowsAffected: 0, // TODO: is there a way to know this? 153 | rows: [] 154 | }, 155 | item: eventItem, 156 | length: 0, 157 | db: this.self 158 | }); 159 | } 160 | 161 | function error(e) { 162 | --this.i || (this.fn || empty)({ 163 | type: "error", 164 | error: e, 165 | db: this.self 166 | }); 167 | } 168 | 169 | function item(i) { 170 | return this[i]; 171 | } 172 | 173 | const {Cc, Ci} = require("chrome"); 174 | 175 | function openDatabase(name, domain) { 176 | var 177 | file = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties).get("UChrm", Ci.nsIFile), 178 | store = Cc["@mozilla.org/storage/service;1"].getService(Ci.mozIStorageService) 179 | ; 180 | file.append(domain); 181 | if (!file.exists() || !file.isDirectory()) { 182 | file.create(Ci.nsIFile.DIRECTORY_TYPE, 0777); 183 | } 184 | file.append(name.replace(/\W/g, "_") + ".sqlite"); 185 | if (!file.exists()) { 186 | file.create(Ci.nsIFile.FILE_TYPE, 0777); 187 | } 188 | return store.openDatabase(file); 189 | } 190 | 191 | function success(result) { 192 | if (--this.i) return; 193 | var 194 | statement = this.statement, 195 | columns = [], 196 | rows = [], 197 | i, length, row, tmp 198 | ; 199 | for (i = 0, length = statement.columnCount; i < length; ++i) { 200 | columns[i] = statement.getColumnName(i); 201 | } 202 | while (row = result.getNextRow()) { 203 | rows.push(tmp = {}); 204 | for (i = 0, length = row.numEntries; i < length; ++i) { 205 | tmp[columns[i]] = row.getResultByIndex(i); 206 | } 207 | } 208 | rows.item = item; 209 | this.successful = true; 210 | (this.fn || empty)({ 211 | type: "success", 212 | result: { 213 | insertId: this.db.lastInsertRowID, 214 | rowsAffected: 0, 215 | rows: rows 216 | }, 217 | item: eventItem, 218 | length: rows.length, 219 | db: this.self 220 | }); 221 | } 222 | 223 | 224 | function arrayfy(whatever) { 225 | return concat.call([], whatever === undefined ? [] : whatever); 226 | } 227 | 228 | function empty() {} 229 | 230 | function eventItem(i) { 231 | return this.result.rows.item(i); 232 | } 233 | 234 | function toListOfParameters(values) { 235 | return !isArray(values) || typeof values[0] != "object" || !values[0] ? [values] : values; 236 | } 237 | 238 | 239 | var 240 | undefined, 241 | SIZE = 5 * 1024 * 1024, 242 | TABLE = " TABLE ", 243 | DROP = "DROP", 244 | EXISTS = " EXISTS ", 245 | IF = "IF", 246 | autoIncrement = "id INTEGER PRIMARY KEY AUTOINCREMENT", 247 | Array = window.Array, 248 | Math = window.Math, 249 | max = Math.max, 250 | concat = [].concat, 251 | 252 | isArray = Array.isArray 253 | 254 | 255 | ; 256 | 257 | return Database; 258 | 259 | }(this, "Database")); -------------------------------------------------------------------------------- /extension/lib/main.js: -------------------------------------------------------------------------------- 1 | 2 | var 3 | Database = require("db").Database, 4 | instance = {} 5 | ; 6 | 7 | require("page-mod").PageMod({ 8 | 9 | /** 10 | * Copyright (C) 2011 by Andrea Giammarchi, @WebReflection 11 | * 12 | * Permission is hereby granted, free of charge, to any person obtaining a copy 13 | * of this software and associated documentation files (the "Software"), to deal 14 | * in the Software without restriction, including without limitation the rights 15 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 16 | * copies of the Software, and to permit persons to whom the Software is 17 | * furnished to do so, subject to the following conditions: 18 | * 19 | * The above copyright notice and this permission notice shall be included in 20 | * all copies or substantial portions of the Software. 21 | * 22 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 27 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 28 | * THE SOFTWARE. 29 | */ 30 | 31 | include: ["*", "file://*", "resource://*", "about:*"], 32 | contentScriptWhen: "start", 33 | contentScriptFile: require("self").data.url("bridge.js"), 34 | onAttach: function (worker) { 35 | 36 | function commonEvent(method) { 37 | port.on("db" + method, function (info) { 38 | var self = instance[uid][info.i]; 39 | info.result.push(function (result) { 40 | info.result = result; 41 | port.emit("result", info); 42 | }); 43 | self[method].apply(self, info.result); 44 | }); 45 | } 46 | 47 | var 48 | port = worker.port, 49 | domain, uid 50 | ; 51 | 52 | // global configuration 53 | port.on("pagestart", function (info) { 54 | domain = info.domain.replace(/\./g, "_"); 55 | uid = info.uid; 56 | instance[uid] || (instance[uid] = []); 57 | }); 58 | 59 | // constructor 60 | port.on("init", function (info) { 61 | (instance[uid][info.i] = new Database( 62 | info.options, 63 | info.domain 64 | ))[uid] = info.i; 65 | }); 66 | 67 | // methods 68 | port.on("dbclose", function (info) { 69 | var self = instance[uid][info.i]; 70 | if (self) { 71 | delete instance[uid][info.i]; 72 | self.close(); 73 | } 74 | }); 75 | 76 | // common 77 | commonEvent("create"); 78 | commonEvent("drop"); 79 | commonEvent("insert"); 80 | commonEvent("query"); 81 | commonEvent("truncate"); 82 | } 83 | }); 84 | -------------------------------------------------------------------------------- /extension/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dbjs", 3 | "license": "MIT", 4 | "author": "Andrea Giammarchi", 5 | "version": "0.1.2", 6 | "fullName": "Web SQL Database Is Back: db.js", 7 | "id": "jid1-RhjEBAZIyW3RQQ", 8 | "description": "Enables Web SQL Database through db.js Interface" 9 | } 10 | -------------------------------------------------------------------------------- /src/functions.js: -------------------------------------------------------------------------------- 1 | 2 | function arrayfy(whatever) { 3 | return concat.call([], whatever === undefined ? [] : whatever); 4 | } 5 | 6 | function empty() {} 7 | 8 | function eventItem(i) { 9 | return this.result.rows.item(i); 10 | } 11 | 12 | function toListOfParameters(values) { 13 | return !isArray(values) || typeof values[0] != "object" || !values[0] ? [values] : values; 14 | } 15 | -------------------------------------------------------------------------------- /src/intro.js: -------------------------------------------------------------------------------- 1 | 2 | var Database = (function (window, $Database) {/*{use_strict}*/ 3 | 4 | if (window[$Database] && !window.opera) return window[$Database]; 5 | 6 | /** 7 | * Copyright (C) 2011 by Andrea Giammarchi, @WebReflection 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | * THE SOFTWARE. 26 | */ 27 | -------------------------------------------------------------------------------- /src/outro.js: -------------------------------------------------------------------------------- 1 | 2 | return Database; 3 | 4 | }(this, "Database")); -------------------------------------------------------------------------------- /src/var.intro.js: -------------------------------------------------------------------------------- 1 | 2 | var 3 | undefined, 4 | SIZE = 5 * 1024 * 1024, 5 | TABLE = " TABLE ", 6 | DROP = "DROP", 7 | EXISTS = " EXISTS ", 8 | IF = "IF", 9 | autoIncrement = "id INTEGER PRIMARY KEY AUTOINCREMENT", 10 | Array = window.Array, 11 | Math = window.Math, 12 | max = Math.max, 13 | concat = [].concat, -------------------------------------------------------------------------------- /src/var.outro.js: -------------------------------------------------------------------------------- 1 | 2 | ; -------------------------------------------------------------------------------- /src/web/Database.js: -------------------------------------------------------------------------------- 1 | 2 | function Database(options) { 3 | 4 | if (!(this instanceof Database)) 5 | return new Database(options) 6 | ; 7 | 8 | var self = this; 9 | 10 | options || (options = {}); 11 | 12 | // internal db invisible outside the closure 13 | defineProperty(self, expando, { 14 | enumerable: !1, 15 | writable: !0, 16 | configurable: !0, 17 | value: openDatabase( 18 | self.name = options.name || document.domain.replace(/\./g, "-") || "db", 19 | self.version = options.version || "1.0", 20 | self.description = options.description || "data", 21 | self.size = options.size || SIZE, 22 | empty 23 | ) 24 | }); 25 | 26 | // but internal db can reach self inside the closure 27 | self[expando][expando] = self; 28 | 29 | return self; 30 | } 31 | -------------------------------------------------------------------------------- /src/web/functions.js: -------------------------------------------------------------------------------- 1 | 2 | function close() { 3 | // hoping that Browsers will call asyncClose on their side 4 | // cannot actually remove references or transactions may fail 5 | // this[expando][expando] = null; 6 | // delete this[expando]; 7 | } 8 | 9 | function create(name, fields, fn) { 10 | fields[0] || (fields[0] = autoIncrement); 11 | this.query("CREATE" + TABLE + IF + " NOT" + EXISTS + name + " (" + fields.join(", ") + ")", fn); 12 | return this; 13 | } 14 | 15 | function createReadOrQuery(method) { 16 | method += "ransaction"; 17 | return function readOrWrite(SQL, A, fn) { 18 | var self = this; 19 | if (typeof A == "function") { 20 | fn = A; 21 | A = []; 22 | } 23 | self[expando][method](function (t) { 24 | for (var 25 | sql = arrayfy(SQL), 26 | a = toListOfParameters(A), 27 | i = 0, 28 | length = max(sql.length, a.length), 29 | tr = (t[expando] = {self:self, fn:fn, i:length}), 30 | tmp; 31 | i < length; ++i 32 | ) { 33 | t.executeSql(sql[i] || sql[0], a[i], success, error); 34 | } 35 | }); 36 | return self; 37 | }; 38 | } 39 | 40 | function drop(name, fn) { 41 | this.query(DROP + TABLE + IF + EXISTS + name, fn); 42 | return this; 43 | } 44 | 45 | function error(t, result) { 46 | if (t = t[expando]) { 47 | --t.i || (t.fn || empty)({ 48 | type: "error", 49 | error: result, 50 | db: t.self 51 | }); 52 | } 53 | } 54 | 55 | function insert(name, values, fn) { 56 | for (var 57 | self = this, 58 | sql = [], 59 | a = toListOfParameters(values), 60 | i = 0, length = a.length, 61 | many = Array(a[0].length + 1).join(", ?").slice(2); 62 | i < length; ++i 63 | ) { 64 | sql[i] = 'INSERT INTO ' + name + ' VALUES (' + many + ')'; 65 | } 66 | self.query(sql, a, fn); 67 | return self; 68 | } 69 | 70 | function success(t, result) { 71 | if (t = t[expando]) { 72 | --t.i || (t.fn || empty)({ 73 | type: "success", 74 | result: result, 75 | item: eventItem, 76 | length: result.rows.length, 77 | db: t.self 78 | }); 79 | } 80 | } 81 | 82 | function truncate(name, fn) { 83 | var 84 | self = this, 85 | rows, item 86 | ; 87 | self.query('SELECT * FROM sqlite_master WHERE name = ?', arrayfy(name), function (e) { 88 | if (e.type == "success") { 89 | item = e.length && e.result.rows.item(0); 90 | if (item && item.type == "table" && (item.tbl_name || item.name) == name) { 91 | // safer to perform double transaction here 92 | // due XUL native SQLite problems that actually "waried me" ... 93 | return self.query(DROP + TABLE + name, function (e) { 94 | self.query(item.sql, fn); 95 | }); 96 | } 97 | e.type = "error"; 98 | e.error = {message: "table " + name + " does not exists"}; 99 | delete e.result; 100 | } 101 | fn(e); 102 | }); 103 | return self; 104 | } 105 | -------------------------------------------------------------------------------- /src/web/outro.js: -------------------------------------------------------------------------------- 1 | 2 | DatabasePrototype.close = close; 3 | DatabasePrototype.create = create; 4 | DatabasePrototype.drop = drop; 5 | DatabasePrototype.insert = insert; 6 | DatabasePrototype.read = read; 7 | DatabasePrototype.query = query; 8 | DatabasePrototype.truncate = truncate; 9 | -------------------------------------------------------------------------------- /src/web/var.private.js: -------------------------------------------------------------------------------- 1 | 2 | read = createReadOrQuery("readT"), 3 | query = createReadOrQuery("t"), 4 | document = window.document, 5 | openDatabase = window.openDatabase, 6 | expando = "_" + ("" + Math.random()).slice(2), 7 | isArray = Array.isArray || function (toString, a) { 8 | a = toString.call([]); 9 | return function isArray(o) { 10 | return a == toString.call(o); 11 | }; 12 | }({}.toString), 13 | Object = window.Object, 14 | defineProperty = Object.defineProperty || function (o, k, d) { 15 | o[k] = d.value; 16 | return o; 17 | }, 18 | DatabasePrototype = Database.prototype 19 | -------------------------------------------------------------------------------- /src/xul/Database.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | function Database(options, domain) { 4 | 5 | if (!(this instanceof Database)) 6 | return new Database(options) 7 | ; 8 | 9 | var self = this, db; 10 | 11 | db = openDatabase(self.name = options.name || "db", domain); 12 | 13 | // fields assigned but ignored behind the scene in this version 14 | self.version = options.version || "1.0"; 15 | self.description = options.description || "data"; 16 | self.size = options.size || SIZE; 17 | 18 | self.close = function close() { 19 | db.asyncClose(); 20 | }; 21 | 22 | self.create = function create(name, fields, fn) { 23 | fields[0] || (fields[0] = autoIncrement); 24 | self.query("CREATE" + TABLE + IF + " NOT" + EXISTS + name + " (" + fields.join(", ") + ")", fn); 25 | return self; 26 | }; 27 | 28 | self.drop = function drop(name, fn) { 29 | self.query(DROP + TABLE + IF + EXISTS + name, fn); 30 | return self; 31 | }; 32 | 33 | self.insert = function insert(name, values, fn) { 34 | for (var 35 | sql = [], 36 | a = toListOfParameters(values), 37 | i = 0, length = a.length, 38 | many = Array(a[0].length + 1).join(", ?").slice(2); 39 | i < length; ++i 40 | ) { 41 | sql[i] = 'INSERT INTO ' + name + ' VALUES (' + many + ')'; 42 | } 43 | self.query(sql, a, fn); 44 | return self; 45 | }; 46 | 47 | self.read = self.query = function query(SQL, A, fn) { 48 | if (typeof A == "function") { 49 | fn = A; 50 | A = []; 51 | } 52 | db.beginTransaction(); 53 | for (var 54 | sql = arrayfy(SQL), 55 | a = toListOfParameters(A), 56 | i = 0, 57 | length = max(sql.length, a.length), 58 | tr = { 59 | i: length, 60 | self: self, 61 | fn: fn, 62 | db: db, 63 | handleResult: success, 64 | handleCompletion: complete, 65 | handleError: error 66 | }, 67 | statement, _; 68 | i < length; ++i 69 | ) { 70 | try { 71 | statement = db.createStatement(sql[i] || sql[0]); 72 | if (a[i]) { 73 | for (let param in statement.params) { 74 | statement.params[param] = a[i][param]; 75 | } 76 | } 77 | tr.statement = statement; 78 | statement.executeAsync(tr); 79 | } catch(e) { 80 | _ = e; 81 | break; 82 | } 83 | } 84 | if (_) { 85 | db.rollbackTransaction(); 86 | tr.handleError(_); 87 | } else { 88 | db.commitTransaction(); 89 | } 90 | }; 91 | 92 | self.truncate = function truncate(name, fn) { 93 | var rows, item; 94 | self.read('SELECT * FROM sqlite_master WHERE name = ?', arrayfy(name), function (e) { 95 | if (e.type == "success") { 96 | item = e.length && e.result.rows.item(0); 97 | if (item && item.type == "table" && (item.tbl_name || item.name) == name) { 98 | // safer to perform double transaction here 99 | // due XUL native SQLite problems that actually "waried me" ... 100 | return self.query(DROP + TABLE + name, function (e) { 101 | self.query(item.sql, fn); 102 | }); 103 | } 104 | e.type = "error"; 105 | e.error = {message: "table " + name + " does not exists"}; 106 | delete e.result; 107 | } 108 | fn(e); 109 | }); 110 | return self; 111 | }; 112 | 113 | } 114 | -------------------------------------------------------------------------------- /src/xul/functions.js: -------------------------------------------------------------------------------- 1 | 2 | function complete(reason) { 3 | this.successful || --this.i || (this.fn || empty)({ 4 | type: "success", 5 | result: { 6 | insertId: this.db.lastInsertRowID, 7 | rowsAffected: 0, // TODO: is there a way to know this? 8 | rows: [] 9 | }, 10 | item: eventItem, 11 | length: 0, 12 | db: this.self 13 | }); 14 | } 15 | 16 | function error(e) { 17 | --this.i || (this.fn || empty)({ 18 | type: "error", 19 | error: e, 20 | db: this.self 21 | }); 22 | } 23 | 24 | function item(i) { 25 | return this[i]; 26 | } 27 | 28 | const {Cc, Ci} = require("chrome"); 29 | 30 | function openDatabase(name, domain) { 31 | var 32 | file = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties).get("UChrm", Ci.nsIFile), 33 | store = Cc["@mozilla.org/storage/service;1"].getService(Ci.mozIStorageService) 34 | ; 35 | file.append(domain); 36 | if (!file.exists() || !file.isDirectory()) { 37 | file.create(Ci.nsIFile.DIRECTORY_TYPE, 0777); 38 | } 39 | file.append(name.replace(/\W/g, "_") + ".sqlite"); 40 | if (!file.exists()) { 41 | file.create(Ci.nsIFile.FILE_TYPE, 0777); 42 | } 43 | return store.openDatabase(file); 44 | } 45 | 46 | function success(result) { 47 | if (--this.i) return; 48 | var 49 | statement = this.statement, 50 | columns = [], 51 | rows = [], 52 | i, length, row, tmp 53 | ; 54 | for (i = 0, length = statement.columnCount; i < length; ++i) { 55 | columns[i] = statement.getColumnName(i); 56 | } 57 | while (row = result.getNextRow()) { 58 | rows.push(tmp = {}); 59 | for (i = 0, length = row.numEntries; i < length; ++i) { 60 | tmp[columns[i]] = row.getResultByIndex(i); 61 | } 62 | } 63 | rows.item = item; 64 | this.successful = true; 65 | (this.fn || empty)({ 66 | type: "success", 67 | result: { 68 | insertId: this.db.lastInsertRowID, 69 | rowsAffected: 0, 70 | rows: rows 71 | }, 72 | item: eventItem, 73 | length: rows.length, 74 | db: this.self 75 | }); 76 | } 77 | -------------------------------------------------------------------------------- /src/xul/var.private.js: -------------------------------------------------------------------------------- 1 | 2 | isArray = Array.isArray 3 | -------------------------------------------------------------------------------- /test/db.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Database 5 | 206 | 207 | 208 | 209 | 210 | 242 | 243 | 244 |
245 | 251 | 252 | --------------------------------------------------------------------------------