├── DBi.js ├── index.html ├── readme.md ├── schema_1stRun └── schema.json ├── schema_v2 └── schema.json └── schema_v3 └── schema.json /DBi.js: -------------------------------------------------------------------------------- 1 | /* 2 | DBi.js 3 | 4 | Provides an interface to HTML 5 database objects 5 | 6 | This software is released under the MIT License. 7 | 8 | Copyright (c) 2011 Daniel J. Pinter, http://DataZombies.com/ 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | */ 17 | /*jslint devel: true, bitwise: true, regexp: true, sloppy: true, white: true, nomen: true, plusplus: true, maxerr: 50, indent: 2 */ 18 | (function () { 19 | // Constructor 20 | var DBi = function (settings) { 21 | var i, that = this; 22 | 23 | that.db = {}; 24 | that.error = null; 25 | that.isInitialized = false; 26 | that.longName = null; 27 | that.maxSize = null; 28 | that.schema = {}; 29 | that.schemaReady = false; 30 | that.shortName = null; 31 | that.version = null; 32 | that.settings = { 33 | debug: false, 34 | jsonAsynchronous: false, 35 | reset: false, 36 | schemaFile: null, 37 | schemaObject: null 38 | }; 39 | 40 | // Apply user's settings 41 | for (i in settings) { 42 | if (settings.hasOwnProperty(i)){ 43 | that.settings[i] = settings[i]; 44 | } 45 | } 46 | that.initialization(); 47 | }, 48 | regexParseSQL = /(ALTER|CREATE|UPDATE)\s+(?:(?:UNIQUE\s+)?(INDEX|TABLE|TRIGGER|VIEW)\s+(?:IF\s+NOT\s+EXISTS\s+)?)?(?:(?:OR\s+(?:ABORT|FAIL|IGNORE|REPLACE|ROLLBACK)\s+))?(\w+)\s*(?:\((.*)\))?/i, 49 | regexTableConstraints = /,\s?((?:CHECK|FOREIGN\s+KEY|PRIMARY\s+KEY|UNIQUE).*)/i; 50 | 51 | // Prototype 52 | DBi.prototype = { 53 | // ***************************************************************************** 54 | // _debug () 55 | // If debug is true output a string to the console. 56 | // ***************************************************************************** 57 | _debug: function (a) { 58 | if (this.settings.debug) { 59 | console.log(a); 60 | } 61 | }, 62 | 63 | //**************************************************************************** 64 | // _errorHandler () 65 | // Sends error message to console regardless of value in debug. 66 | //**************************************************************************** 67 | _errorHandler: function (a, b, c) { 68 | if (a !== null && typeof (b) === 'undefined') { 69 | if (-1 !== a.message.indexOf('callback did not return false')) { 70 | return true; 71 | } 72 | c = 'Transaction Error ' + a.code + ' - ' + a.message; 73 | } else if (b !== null) { 74 | c = 'SQL Error ' + b.code + ' - ' + b.message; 75 | } 76 | this.error = c; 77 | console.log('**********\n* ' + c + '\n**********'); 78 | return true; 79 | }, 80 | 81 | 82 | //**************************************************************************** 83 | // initialization () 84 | // Initialize, create & populate and/or upgrade a database in one transaction 85 | // with a JSON file or object. 86 | //**************************************************************************** 87 | initialization: function () { 88 | var jDB, json, jStorage, that = this, 89 | 90 | // insert records 91 | populateTable = function (t, name, tbl) { 92 | var arrVal, i = tbl.records.length - 1, j, strCol, strVal; 93 | for (i; i >= 0; --i) { 94 | arrVal = []; 95 | strCol = ''; 96 | strVal = ''; 97 | for (j in tbl.records[i]) { 98 | if (tbl.records[i].hasOwnProperty(j)) { 99 | arrVal.push(tbl.records[i][j]); 100 | strCol += j + ', '; 101 | strVal += '?, '; 102 | } 103 | } 104 | t.executeSql( 105 | 'INSERT INTO ' + name + ' (' + strCol.substr(0, strCol.lastIndexOf(', ')) + ') VALUES (' + strVal.substr(0, strVal.lastIndexOf(', ')) + ');', 106 | arrVal, 107 | null, 108 | that._errorHandler 109 | ); 110 | } 111 | that._debug('Table ' + that.shortName + '.' + name + ' populated.'); 112 | }, 113 | 114 | // create indices, tables, triggers and views 115 | executeDDL = function (t, type, name, obj) { 116 | var action, i, sql; 117 | if (typeof (obj.sql) !== 'undefined') { 118 | sql = obj.sql; 119 | } else { 120 | sql = 'CREATE ' + type + ' IF NOT EXISTS ' + obj.name + ' ('; 121 | for (i in obj.columns) { 122 | if (obj.columns.hasOwnProperty(i)) { 123 | sql += i + ' ' + obj.columns[i] + ', '; 124 | } 125 | } 126 | sql = sql.substr(0, sql.lastIndexOf(', ')) + (typeof (obj.tableConstraint) !== 'undefined' ? ', ' + obj.tableConstraint : '') + ');'; 127 | } 128 | action = sql.match(regexParseSQL)[1]; 129 | t.executeSql( 130 | sql, 131 | [], 132 | function (t, r) { 133 | that._debug(action + ' ' + type + ' ' + that.shortName + '.' + name + '.'); 134 | if (typeof (obj.records) !== 'undefined' && obj.records.length > 0) { 135 | populateTable(t, name, obj); 136 | } 137 | }, 138 | that._errorHandler 139 | ); 140 | }, 141 | 142 | // remove objects from the database 143 | dropObject = function (t, type, name, tbl) { 144 | t.executeSql( 145 | 'DROP ' + type + ' IF EXISTS ' + name + ';', 146 | [], 147 | function (t, r) { 148 | that._debug('DROP ' + type + ' ' + that.shortName + '.' + name + '.'); 149 | if (typeof (tbl) !== 'undefined' && tbl !== null) { 150 | executeDDL(t, type, name, tbl); 151 | } 152 | }, 153 | that._errorHandler 154 | ); 155 | }, 156 | 157 | // create localStorage and sessionStorage objects if they don't exist 158 | createStorages = function (dbSN, jStorage) { 159 | var newS = function (m, s, j) { 160 | var i, n; 161 | for (i in j) { 162 | if (j.hasOwnProperty(i)) { 163 | n = m + '_' + i; 164 | if (!(n in s)) { 165 | s[n] = j[i]; 166 | that._debug('Storage ' + n + ' added with value "' + j[i] + '".'); 167 | } 168 | } 169 | } 170 | }; 171 | if (typeof (jStorage) !== 'undefined') { 172 | newS(dbSN, localStorage, jStorage.local); 173 | newS(dbSN, sessionStorage, jStorage.session); 174 | } 175 | }, 176 | 177 | // delete localStorage and sessionStorage objects 178 | deleteStorage = function (dbSN, jStorage) { 179 | var delS = function (n, s) { 180 | var i; 181 | for (i in s) { 182 | if (s.hasOwnProperty(i)) { 183 | if (i.substr(0, n.length + 1).toLowerCase() === n.toLowerCase() + '_') { 184 | delete s[i]; 185 | that._debug('Storage ' + i + ' deleted.'); 186 | } 187 | } 188 | } 189 | }; 190 | delS(dbSN, localStorage); 191 | delS(dbSN, sessionStorage); 192 | }, 193 | 194 | // open a database 195 | openDB = function (SN, V, LN, MS) { 196 | if (SN === null || SN === '' || LN === null || LN === '' || MS === null || MS === '') { 197 | that._errorHandler(null, null, 'Error - Database attributes error\n shortName: ' + SN + '\n longName: ' + LN + '\n maxSize: ' + MS + '.'); 198 | return false; 199 | } 200 | try { 201 | that.db = openDatabase(SN, '', LN, MS); 202 | } catch (err) { 203 | that._errorHandler(null, null, 'Error - Database failed to open. Unknown error ' + err + '.'); 204 | return false; 205 | } 206 | if (typeof (that.db) !== 'undefined') { 207 | that.shortName = SN; 208 | that.version = that.db.version; 209 | that.longName = LN; 210 | that.maxSize = MS; 211 | that._debug('Database ' + that.shortName + ' opened.\n version: ' + that.version + '\n longName: ' + that.longName + '\n maxSize: ' + that.maxSize + ' bytes (' + ~~ ((that.maxSize / 10488576 * 1000) + 0.5) / 1000 + ' MB)'); 212 | return true; 213 | } else { 214 | that._errorHandler(null, null, 'Error - Failed to open the database.'); 215 | return false; 216 | } 217 | }, 218 | 219 | // called on the app's 1st run or when reset = true 220 | resetOrNewSchema = function (jDB, jStorage) { 221 | var exclusions = '', i, j, type = ''; 222 | that._debug('reset or new.'); 223 | deleteStorage(jDB.shortName, jStorage); 224 | createStorages(jDB.shortName, jStorage); 225 | for (i in jDB) { 226 | if (jDB.hasOwnProperty(i) && i !== 'upgrades') { 227 | type = (i === 'indices' ? 'index' : i.substr(0, i.length - 1)); 228 | for (j in jDB[i]) { 229 | if (jDB[i].hasOwnProperty(j)) { 230 | exclusions += ',"' + type + jDB[i][j].name + '"'; 231 | } 232 | } 233 | } 234 | } 235 | that.db.transaction( 236 | function (t) { 237 | t.executeSql( 238 | 'SELECT upper(type) type, name FROM sqlite_master WHERE type||name Not In("table__WebKitDatabaseInfoTable__"' + exclusions + ') AND name NOT LIKE "sqlite_%"', 239 | [], 240 | function (t, r) { 241 | var i = r.rows.length - 1; 242 | if (r.rows.length !== -1) { 243 | for (i; i >= 0; --i) { 244 | dropObject(t, r.rows.item(i).type, r.rows.item(i).name, null); 245 | } 246 | } 247 | }, 248 | that._errorHandler 249 | ); 250 | }, 251 | that._errorHandler, 252 | function () { 253 | that.db.transaction( 254 | function (t) { 255 | var i, getName = function (obj) { 256 | var name, sql; 257 | if (typeof (obj.sql) !== 'undefined') { 258 | sql = obj.sql.replace(/\s{2,}/g, ' '); 259 | name = obj.sql.match(regexParseSQL)[3]; 260 | } else { 261 | name = obj.name; 262 | } 263 | return name; 264 | }; 265 | for (i in jDB.tables) { 266 | if (jDB.tables.hasOwnProperty(i)) { 267 | dropObject(t, 'Table', getName(jDB.tables[i]), jDB.tables[i]); 268 | } 269 | } 270 | for (i in jDB.indices) { 271 | if (jDB.indices.hasOwnProperty(i)) { 272 | dropObject(t, 'Index', getName(jDB.indices[i]), jDB.indices[i]); 273 | } 274 | } 275 | for (i in jDB.triggers) { 276 | if (jDB.triggers.hasOwnProperty(i)) { 277 | dropObject(t, 'Trigger', getName(jDB.triggers[i]), jDB.triggers[i]); 278 | } 279 | } 280 | for (i in jDB.views) { 281 | if (jDB.views.hasOwnProperty(i)) { 282 | dropObject(t, 'View', getName(jDB.views[i]), jDB.views[i]); 283 | } 284 | } 285 | }, 286 | that._errorHandler, 287 | function () { 288 | that.db.changeVersion( 289 | that.db.version, 290 | jDB.version, 291 | function (t) {}, // callback 292 | function (err) {}, // errorCallback 293 | function () { // successCallback 294 | that.version = that.db.version; 295 | that.isInitialized = true; 296 | that._debug(that.shortName + ', version ' + that.version +', is initialized.'); 297 | } 298 | ); 299 | } 300 | ); 301 | } 302 | ); 303 | }, 304 | 305 | // performs upgrades to database schema and records when the upgrades object is present in the JSON file 306 | upgradeSchema = function (jDB, jStorage) { 307 | // under construction 308 | }, 309 | 310 | // load a JSON object or a JSON data file asynchronously/non-asynchronously depending on the value in jsonAsynchronous 311 | AE_35 = function (jsonObj, jsonURL) { 312 | var jsonData = new XMLHttpRequest(), 313 | jsonStatus = false, 314 | processJSONdata = function () { 315 | if (jsonStatus) { 316 | if ('openDatabase' in window) { 317 | jDB = jsonData.database; 318 | jStorage = jsonData.storage; 319 | if (openDB(jDB.shortName, jDB.version, jDB.longName, jDB.maxSize)) { 320 | if (that.settings.reset || that.version === '') { 321 | that.settings.reset = true; 322 | resetOrNewSchema(jDB, jStorage); 323 | } else { 324 | if (jDB.version > that.version && that.version !== '') { 325 | //upgradeSchema(jDB, jStorage); 326 | // under construction 327 | } else { 328 | that.isInitialized = true; 329 | that._debug('same version, no reset - just open db.'); 330 | that._debug(that.shortName + ', version ' + that.version +', is initialized.'); 331 | } 332 | } 333 | } 334 | } else { 335 | that._errorHandler(null, null, 'Error - Databases are not supported on this platform.'); 336 | } 337 | } 338 | }, 339 | handleJSONresponse = function () { 340 | if (!jsonData || jsonData === null || jsonData.status !== 200) { 341 | that._errorHandler(null, null, 'JSON Error ' + jsonData.status + ' - file "' + jsonURL + '" ' + jsonData.statusText + '.'); 342 | } else { 343 | that._debug('JSON file "' + jsonURL + '" loaded.'); 344 | try { 345 | jsonData = JSON.parse(jsonData.responseText); 346 | jsonStatus = true; 347 | } catch (err) { 348 | that._errorHandler(null, null, err); 349 | jsonStatus = false; 350 | } 351 | } 352 | }; 353 | 354 | if(typeof (jsonURL) !== 'undefined' && jsonURL !== null) { 355 | jsonData.open("GET", jsonURL, that.settings.jsonAsynchronous); 356 | try { 357 | jsonData.send(null); 358 | } catch (err) { 359 | that._errorHandler(null, null, err); 360 | jsonData = null; 361 | } 362 | if (that.settings.jsonAsynchronous) { 363 | jsonData.onreadystatechange = function () { 364 | if (jsonData.readyState === 4) { 365 | handleJSONresponse(); 366 | processJSONdata(); 367 | } 368 | }; 369 | } else { 370 | handleJSONresponse(); 371 | processJSONdata(); 372 | } 373 | } else { 374 | jsonData = jsonObj; 375 | jsonStatus = true; 376 | processJSONdata(); 377 | } 378 | }; 379 | 380 | if((typeof (that.settings.schemaFile) !== 'undefined' && that.settings.schemaFile !== null) || 381 | (typeof (that.settings.schemaObject) !== 'undefined' && that.settings.schemaObject !== null)) { 382 | AE_35(that.settings.schemaObject, that.settings.schemaFile); 383 | } else { 384 | that._errorHandler(null, null, 'Error - schemaFile or schemaObject is not defined.'); 385 | } 386 | }, 387 | 388 | //**************************************************************************** 389 | // outputSchema () 390 | // Dump the localstorage, sessionStorage & database. 391 | // Based on "Abusing HTML 5 Structured Client-side Storage" by Alberto Trivero 392 | // (http://trivero.secdiscover.com/html5whitepaper.pdf) 393 | //**************************************************************************** 394 | outputSchema: function () { 395 | var indices = [], 396 | json = {}, 397 | ls, 398 | ss, 399 | tables = [], 400 | that = this, 401 | triggers = [], 402 | views = [], 403 | 404 | // gets localStorage and sessionStorage data 405 | getStorage = function (s) { 406 | var dbSN = that.shortName.length !== 0 ? that.shortName.toLowerCase() + '_' : '', 407 | i, 408 | storage = {isEmpty: true}; 409 | for (i in s) { 410 | if (s.hasOwnProperty(i)) { 411 | if (i.substr(0, dbSN.length).toLowerCase() === dbSN) { 412 | storage[i.substr(dbSN.length, i.length)] = s.getItem(i); 413 | storage.isEmpty = false; 414 | } 415 | } 416 | } 417 | return storage; 418 | }, 419 | 420 | // saves the database schema to schema 421 | outputJSON_Schema = function (json) { 422 | that.schema = json; 423 | that.schemaReady = true; 424 | }; 425 | 426 | that.schemaReady = false; 427 | json.generationTimeStamp = new Date(); 428 | json.userAgent = navigator.userAgent; 429 | 430 | // get sessionStorage and localStorage 431 | ls = getStorage(localStorage); 432 | ss = getStorage(sessionStorage); 433 | if (ls.isEmpty === false || ss.isEmpty === false) { 434 | json.storage = {}; 435 | if (ls.isEmpty === false) { 436 | delete ls.isEmpty; 437 | json.storage.local = ls; 438 | } 439 | if (ss.isEmpty === false) { 440 | delete ss.isEmpty; 441 | json.storage.session = ss; 442 | } 443 | } 444 | // start pulling the schema if the database is open 445 | if ('openDatabase' in window) { 446 | if (that.maxSize !== 0 && that.db.version !== '') { 447 | json.database = { 448 | shortName: that.shortName, 449 | version: that.version, 450 | longName: that.longName, 451 | maxSize: that.maxSize 452 | }; 453 | that.db.transaction( 454 | function (t) { 455 | t.executeSql( 456 | 'SELECT sql FROM sqlite_master WHERE name!="__WebKitDatabaseInfoTable__" AND name NOT LIKE "sqlite_%" ORDER BY 1 DESC;', 457 | [], 458 | function (t, r) { 459 | var i, sql, table = {}, 460 | dumpTable = function (t, table) { 461 | // get all data from the selected table 462 | t.executeSql( 463 | 'SELECT * FROM ' + table.name + ' ORDER BY 1 DESC;', 464 | [], 465 | function (t, r) { 466 | var a, b, record = [], 467 | records = []; 468 | if (r.rows.length !== 0) { 469 | for (a = r.rows.length - 1; a >= 0; --a) { 470 | record = {}; 471 | for (b in r.rows.item(a)) { 472 | if (r.rows.item(a).hasOwnProperty(b)) { 473 | record[b] = r.rows.item(a)[b]; 474 | } 475 | } 476 | records.push(record); 477 | } 478 | table.records = records; 479 | } 480 | }, 481 | that._errorHandler); 482 | }, 483 | getColumns = function (sql) { 484 | // extract columns from the sql 485 | var columns = {}, 486 | i, temp = []; 487 | for (i in sql) { 488 | if (sql.hasOwnProperty(i)) { 489 | temp = sql[i].match(/(\w+)\s+(.+)/); 490 | columns[temp[1]] = temp[2]; 491 | } 492 | } 493 | return columns; 494 | }; 495 | 496 | for (i = r.rows.length - 1; i >= 0; --i) { 497 | sql = r.rows.item(i).sql.replace(/\s{2,}/g, ' ') + ';'; 498 | sql = sql.match(regexParseSQL); 499 | sql[0] = r.rows.item(i).sql.replace(/\s{2,}/g, ' ') + ';'; 500 | if (typeof (sql[4]) !== 'undefined') { 501 | if (sql[4].match(regexTableConstraints) !== null) { 502 | sql[5] = sql[4].match(regexTableConstraints)[1]; 503 | sql[4] = sql[4].replace(sql[4].match(regexTableConstraints)[0], ''); 504 | } 505 | sql[4] = sql[4].split(/,/); 506 | } 507 | switch (sql[2].toLowerCase()) { 508 | case 'index': 509 | indices.push({ 510 | name: sql[3], 511 | sql: sql[0] 512 | }); 513 | break; 514 | case 'table': 515 | table = { 516 | name: sql[3], 517 | sql: sql[0], 518 | columns: '' 519 | }; 520 | if (typeof (sql[5]) !== 'undefined' && typeof (sql[5]) !== 'null') { 521 | table.tableConstraint = sql[5]; 522 | } 523 | table.columns = getColumns(sql[4]); 524 | dumpTable(t, table); 525 | tables.push(table); 526 | break; 527 | case 'trigger': 528 | triggers.push({ 529 | name: sql[3], 530 | sql: sql[0] 531 | }); 532 | break; 533 | case 'view': 534 | views.push({ 535 | name: sql[3], 536 | sql: sql[0] 537 | }); 538 | break; 539 | } 540 | } 541 | }, 542 | that._errorHandler 543 | ); 544 | }, 545 | that._errorHandler, 546 | function () { 547 | if (tables.length !== 0) { 548 | json.database.tables = tables; 549 | } 550 | if (indices.length !== 0) { 551 | json.database.indices = indices; 552 | } 553 | if (triggers.length !== 0) { 554 | json.database.triggers = triggers; 555 | } 556 | if (views.length !== 0) { 557 | json.database.views = views; 558 | } 559 | outputJSON_Schema(json); 560 | } 561 | ); 562 | } else { 563 | outputJSON_Schema(json); 564 | } 565 | } else { 566 | outputJSON_Schema(json); 567 | } 568 | } 569 | }; 570 | window.DBi = DBi; 571 | })(); 572 | /* 573 | 574 | Database openDatabase( 575 | DOMString name, 576 | DOMString version, 577 | DOMString displayName, 578 | unsigned long estimatedSize, 579 | function (Database database) { // optional DatabaseCallback creationCallback 580 | } 581 | ); 582 | 583 | database.transaction || database.readTransaction( 584 | function (SQLTransaction transaction) { // SQLTransactionCallback callback 585 | }, 586 | function (SQLError error) { // optional SQLTransactionErrorCallback errorCallback 587 | }, 588 | function () { // optional SQLVoidCallback successCallback 589 | } 590 | ); 591 | 592 | database.changeVersion( 593 | DOMString oldVersion, 594 | DOMString newVersion, 595 | function (SQLTransaction transaction) { // optional SQLTransactionCallback callback 596 | }, 597 | function (SQLError error) { // optional SQLTransactionErrorCallback errorCallback 598 | }, 599 | function () { // optional SQLVoidCallback successCallback 600 | } 601 | ); 602 | 603 | transaction.executeSql( 604 | DOMString sqlStatement, 605 | optional ObjectArray arguments, 606 | function (SQLTransaction transaction, SQLResultSet resultSet) { // optional SQLStatementCallback callback 607 | }, 608 | function (SQLTransaction transaction, SQLError error) { // optional SQLStatementErrorCallback errorCallback 609 | } 610 | ); 611 | 612 | */ -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | db tests 5 | 6 | 7 | 98 | 99 | 100 |

DBi Control Panel

101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 125 | 126 |
120 |
121 |
122 |
123 | 124 |
127 | 128 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # DataZombie's DBi v0.2 2 | (pronounced Debi) 3 | 4 | __DBi handles all the work of creating an iOS or Safari WebSQL database and manages local/session storages.__ 5 | 6 | DBi creates, tables, indices, triggers & views and populates the tables all from a single JSON file that's outputted from DBi. 7 | 8 | [Google Group](https://groups.google.com/group/datazombies-dbi) 9 | 10 | If you find that DBi has been a benefit to you project please donate to the cause via PayPal (http://tinyurl.com/2fpmx27). Any amount is appreciated. 11 | 12 | ## Step 1 - Include the Script Tag in Your HTML5 Markup 13 | 14 | ``` html 15 | 16 | ``` 17 | 18 | ## Step 2 - Define the Database Schema 19 | All DBi JSON schema files has one required object, database, one optional object, storage, and two optional properties, generationTimeStamp and userAgent. generationTimeStamp and userAgent are generated during schema output. 20 | 21 | ``` js 22 | {"generationTimeStamp":"", 23 | "userAgent":"", 24 | "storage": {}, 25 | "database": {} 26 | } 27 | ``` 28 | 29 | The database object is subdivided into five required attributes that define the database: shortName, version, longName, maxSize. The only required array is tables. The optional arrays define the database's indices, triggers, views and upgrades. Upgrades will be implemented in a future release. 30 | 31 | ``` js 32 | "database": 33 | {"shortName":"", 34 | "version":"", 35 | "longName":"", 36 | "maxSize":"", 37 | "tables":[], 38 | "indices":[], 39 | "triggers":[], 40 | "views":[], 41 | "upgrades":[] 42 | } 43 | ``` 44 | 45 | The minimum you need to get started is the required database information. All other development and modifications can be done in the web browser's database interface (Safari Web Inspector) and outputted for implementation. More on output later. 46 | 47 | The minimum JSON schema would look like this: 48 | 49 | ``` js 50 | {"database": 51 | {"shortName":"dbCT", 52 | "version":"1.0", 53 | "longName":"Chess Tournament Database", 54 | "maxSize":"1048576"}} 55 | ``` 56 | Database information with one table: 57 | 58 | ``` js 59 | {"database": 60 | {"shortName":"dbCT", 61 | "version":"1.0", 62 | "longName":"Chess Tournament Database", 63 | "maxSize":"1048576"}, 64 | "tables":[ 65 | {"name":"Games", 66 | "sql":"CREATE TABLE Games (id INTEGER PRIMARY KEY AUTOINCREMENT,gamename TEXT,weight REAL DEFAULT .10 CHECK (weight<=1));"}, 67 | {"name":"Players", 68 | "columns": 69 | {"id":"INTEGER PRIMARY KEY AUTOINCREMENT", 70 | "fname":"TEXT NOT NULL", 71 | "lname":"TEXT NOT NULL"}, 72 | "records":[ 73 | {"id":1, "fname":"Bobby", "lname":"Fisher"}, 74 | {"id":2, "fname":"Bart", "lname":"Simpsen"}, 75 | {"id":3, "fname":"Garry", "lname":"Kasparov"}]}] 76 | ``` 77 | 78 | You can see that tables can be created by either putting the DDL in the sql attribute (the Games table) or define each column (the Players table). See the schema files in this repo for more examples. 79 | 80 | ## Step 3 - Creating the Database 81 | In your JavaScript code initialize your database variable. 82 | 83 | ``` js 84 | dbWebApp = new DBi({ 85 | debug: true, 86 | jsonAsynchronous: false, 87 | reset: false, 88 | schemaFile: './schema_1stRun/schema.json' 89 | }); 90 | ``` 91 | 92 | __debug__: verbose and helpful messages sent to the console. _Optional. Default false_ 93 | 94 | __jsonAsynchronous__: load the JSON schema file asynchronously. _Optional. Default false_ 95 | 96 | __reset__: delete all storages and dump all database objects; a fresh start. _Optional. Default false_ 97 | 98 | __schemaFile__: The path to the JSON schema file. 99 | 100 | __schemaObject__: A JSON object. 101 | 102 | The database initlization must have a __schemaFile__ or a __schemaObject__. 103 | 104 | From here you can use all the transaction and executeSQL for your development. 105 | 106 | ### Step 3a - Outputting the Database Schema 107 | In the console type... 108 | 109 | ``` js 110 | dbWebApp.outputSchema(); 111 | ``` 112 | 113 | Your database schema will be stored in dbWebApp.schema. See index.html for functions that output the schema to the browser window. 114 | 115 | 116 | DBi is provided with the MIT license. Please submit an issue if you find any bugs that need to be squished or features you would like to see. Feel free to fork this repo. Please submit pull requests for any fixes, enhancements or new features. 117 | 118 | ### Change Log 119 | 2011-11-24 v0.2 Add the ability to pass a JSON object to DBi instead of a JSON file URL; improved index.html's JSON output function; 120 | 121 | 2011-11-02 v0.1 Initial (alpha-betaish) release. -------------------------------------------------------------------------------- /schema_1stRun/schema.json: -------------------------------------------------------------------------------- 1 | {"generationTimeStamp":"2011-01-06T16:48:17.814Z", 2 | "userAgent":"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_6; en-us) AppleWebKit/533.19.4 (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4", 3 | "storage": 4 | {"local": 5 | {"reference1":"localstorage, sessionStorage & database dump based on \"Abusing HTML 5 Structured Client-side Storage\" by Alberto Trivero (http://trivero.secdiscover.com/html5whitepaper.pdf)", 6 | "reference2":"Chess Tournament Database from http://mobile.tutsplus.com/tutorials/android/android-sqlite/"}, 7 | "session": 8 | {"Nexus6":"Roy Batty: \"I've seen things you people wouldn't believe. Attack ships on fire off the shoulder of Orion. I watched C-beams glitter in the dark near the Tannhauser gate. All those moments will be lost in time... like tears in rain... Time to die.\""}}, 9 | "database": 10 | {"shortName":"dbCT", 11 | "version":"1.0", 12 | "longName":"Chess Tournament Database", 13 | "maxSize":"1048576", 14 | "tables":[ 15 | {"name":"Games", 16 | "sql":"CREATE TABLE Games (id INTEGER PRIMARY KEY AUTOINCREMENT,gamename TEXT,weight REAL DEFAULT .10 CHECK (weight<=1));"}, 17 | {"name":"Players", 18 | "columns": 19 | {"id":"INTEGER PRIMARY KEY AUTOINCREMENT", 20 | "fname":"TEXT NOT NULL", 21 | "lname":"TEXT NOT NULL"}, 22 | "records":[ 23 | {"id":1, "fname":"Bobby", "lname":"Fisher"}, 24 | {"id":2, "fname":"Bart", "lname":"Simpsen"}, 25 | {"id":3, "fname":"Garry", "lname":"Kasparov"}]}], 26 | "indices":[ 27 | {"name":"Index1", 28 | "sql":"CREATE UNIQUE INDEX Index1 on Players (id ASC);"}, 29 | {"name":"Index2", 30 | "sql":"CREATE UNIQUE INDEX Index2 on Games (id ASC);"}], 31 | "triggers":[ 32 | {"name":"Trigger1", 33 | "sql":"CREATE TRIGGER Trigger1 BEFORE INSERT ON Games FOR EACH ROW BEGIN SELECT * FROM Games; END;"}], 34 | "views":[ 35 | {"name":"Report_Players", 36 | "sql":"CREATE VIEW Report_Players AS SELECT Players.fname||' '|| Players.lname AS PlayerName FROM Players;"}]}} -------------------------------------------------------------------------------- /schema_v2/schema.json: -------------------------------------------------------------------------------- 1 | {"generationTimeStamp":"2011-01-15T10:06:12.814Z", 2 | "userAgent":"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_6; en-us) AppleWebKit/533.19.4 (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4", 3 | "storage": 4 | {"local": 5 | {"reference1":"localstorage, sessionStorage & database dump based on \"Abusing HTML 5 Structured Client-side Storage\" by Alberto Trivero (http://trivero.secdiscover.com/html5whitepaper.pdf)", 6 | "reference2":"Chess Tournament Database from http://mobile.tutsplus.com/tutorials/android/android-sqlite", 7 | "reference3":"Bart's real name from http://en.wikipedia.org/wiki/Bart_Simpson", 8 | "reference4":"Bobby Fischer's mname and correct spelling of his lname from http://www.nndb.com/people/455/000024383"}, 9 | "session": 10 | {"Nexus6":"Roy Batty: \"I've seen things you people wouldn't believe. Attack ships on fire off the shoulder of Orion. I watched C-beams glitter in the dark near the Tannhauser gate. All those moments will be lost in time... like tears in rain... Time to die.\""}}, 11 | "database": 12 | {"shortName":"dbCT", 13 | "version":"2.0", 14 | "longName":"Chess Tournament Database", 15 | "maxSize":"1048576", 16 | "tables":[ 17 | {"name":"Games", 18 | "sql":"CREATE TABLE Games (id INTEGER PRIMARY KEY AUTOINCREMENT,gamename TEXT,weight REAL DEFAULT .10 CHECK (weight<=1));", 19 | "records":[ 20 | {"id":1, "gamename":"Semi-Final", "weight":0.25}, 21 | {"id":2, "gamename":"Warm-up Heat 1", "weight":0.1}, 22 | {"id":3, "gamename":"Warm-up Heat 2", "weight":0.1}, 23 | {"id":4, "gamename":"Warm-up Heat 3", "weight":0.1}, 24 | {"id":5, "gamename":"Warm-up Heat 4", "weight":0.1}, 25 | {"id":6, "gamename":"Final", "weight":0.35}]}, 26 | {"name":"Players", 27 | "columns": 28 | {"id":"INTEGER PRIMARY KEY AUTOINCREMENT", 29 | "fname":"TEXT NOT NULL", 30 | "mname":"TEXT DEFAULT '' NOT NULL", 31 | "lname":"TEXT NOT NULL"}, 32 | "records":[ 33 | {"id":1, "fname":"Bobby", "mname":"James", "lname":"Fischer"}, 34 | {"id":2, "fname":"Bartholomew", "mname":"JoJo", "lname":"Simpson"}, 35 | {"id":3, "fname":"Garry", "mname":"", "lname":"Kasparov"}]}], 36 | "indices":[ 37 | {"name":"Index1", 38 | "sql":"CREATE UNIQUE INDEX Index1 on Players (id ASC);"}, 39 | {"name":"Index2", 40 | "sql":"CREATE UNIQUE INDEX Index2 on Games (id ASC);"}], 41 | "triggers":[ 42 | {"name":"Trigger1", 43 | "sql":"CREATE TRIGGER Trigger1 BEFORE INSERT ON Games FOR EACH ROW BEGIN SELECT * FROM Games; END"}], 44 | "views":[ 45 | {"name":"Report_Players", 46 | "sql":"CREATE VIEW Report_Players AS SELECT Players.fname||' '|| Players.lname AS PlayerName FROM Players;"}], 47 | "upgrades":[ 48 | {"version":"2.0", 49 | "step_1":[ 50 | {"sql":"ALTER TABLE Players ADD mname TEXT DEFAULT '' NOT NULL;"}, 51 | {"sql":"UPDATE Players SET mname=?, lname=? WHERE id=?;", "values":["James", "Fischer", 1]}, 52 | {"sql":"UPDATE Players SET mname=?, lname=? WHERE id=?;", "values":["JoJo", "Simpson", 2]}], 53 | "step_2":[ 54 | {"table":"Games", 55 | "addRecords":true}]}]}} -------------------------------------------------------------------------------- /schema_v3/schema.json: -------------------------------------------------------------------------------- 1 | {"generationTimeStamp":"2011-02-10T09:13:01.024Z", 2 | "userAgent":"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_6; en-us) AppleWebKit/533.19.4 (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4", 3 | "storage": 4 | {"local": 5 | {"reference1":"localstorage, sessionStorage & database dump based on \"Abusing HTML 5 Structured Client-side Storage\" by Alberto Trivero (http://trivero.secdiscover.com/html5whitepaper.pdf)", 6 | "reference2":"Chess Tournament Database from http://mobile.tutsplus.com/tutorials/android/android-sqlite", 7 | "reference3":"Bart's real name from http://en.wikipedia.org/wiki/Bart_Simpson", 8 | "reference4":"Bobby Fischer's mname and correct spelling of his lname from http://www.nndb.com/people/455/000024383"}, 9 | "session": 10 | {"Nexus6":"Roy Batty: \"I've seen things you people wouldn't believe. Attack ships on fire off the shoulder of Orion. I watched C-beams glitter in the dark near the Tannhauser gate. All those moments will be lost in time... like tears in rain... Time to die.\""}}, 11 | "database": 12 | {"shortName":"dbCT", 13 | "version":"3.0", 14 | "longName":"Chess Tournament Database", 15 | "maxSize":"1048576", 16 | "tables":[ 17 | {"name":"Players", 18 | "columns": 19 | {"id":"INTEGER PRIMARY KEY AUTOINCREMENT", 20 | "fname":"TEXT NOT NULL", 21 | "mname":"TEXT DEFAULT '' NOT NULL", 22 | "lname":"TEXT NOT NULL"}, 23 | "records":[ 24 | {"id":1, "fname":"Bobby", "mname":"James", "lname":"Fischer"}, 25 | {"id":2, "fname":"Bartholomew", "mname":"JoJo", "lname":"Simpson"}, 26 | {"id":3, "fname":"Garry", "mname":"", "lname":"Kasparov"}]}, 27 | {"name":"GameResults", 28 | "sql":"CREATE TABLE GameResults (playerid INTEGER REFERENCES Players(id),gameid INTEGER REFERENCES Games(id),score INTEGER CHECK (score<=100 AND score>=0),PRIMARY KEY (playerid, gameid));", 29 | "columns": 30 | {"playerid":"INTEGER REFERENCES Players(id)", 31 | "gameid":"INTEGER REFERENCES Games(id)", 32 | "score":"INTEGER CHECK (score<=100 AND score>=0)"}, 33 | "tableConstraint":"PRIMARY KEY (playerid, gameid)", 34 | "records":[ 35 | {"playerid":1, "gameid":1, "score":82}, 36 | {"playerid":1, "gameid":2, "score":88}, 37 | {"playerid":1, "gameid":3, "score":78}, 38 | {"playerid":1, "gameid":4, "score":90}, 39 | {"playerid":1, "gameid":5, "score":85}, 40 | {"playerid":1, "gameid":6, "score":94}, 41 | {"playerid":2, "gameid":1, "score":10}, 42 | {"playerid":2, "gameid":2, "score":60}, 43 | {"playerid":2, "gameid":3, "score":50}, 44 | {"playerid":2, "gameid":4, "score":55}, 45 | {"playerid":2, "gameid":5, "score":45}, 46 | {"playerid":2, "gameid":6, "score":65}, 47 | {"playerid":3, "gameid":1, "score":100}, 48 | {"playerid":3, "gameid":2, "score":100}, 49 | {"playerid":3, "gameid":3, "score":100}, 50 | {"playerid":3, "gameid":4, "score":100}, 51 | {"playerid":3, "gameid":5, "score":100}, 52 | {"playerid":3, "gameid":6, "score":100}]}, 53 | {"name":"Games", 54 | "sql":"CREATE TABLE Games (id INTEGER PRIMARY KEY AUTOINCREMENT,gamename TEXT,weight REAL DEFAULT .10 CHECK (weight<=1));", 55 | "records":[ 56 | {"id":1, "gamename":"Semi-Final", "weight":0.25}, 57 | {"id":2, "gamename":"Warm-up Heat 1", "weight":0.1}, 58 | {"id":3, "gamename":"Warm-up Heat 2", "weight":0.1}, 59 | {"id":4, "gamename":"Warm-up Heat 3", "weight":0.1}, 60 | {"id":5, "gamename":"Warm-up Heat 4", "weight":0.1}, 61 | {"id":6, "gamename":"Final", "weight":0.35}]}], 62 | "indices":[ 63 | {"name":"Index1", 64 | "sql":"CREATE UNIQUE INDEX Index1 on Players (id ASC);"}, 65 | {"name":"Index2", 66 | "sql":"CREATE UNIQUE INDEX Index2 on Games (id ASC);"}, 67 | {"name":"Index3", 68 | "sql":"CREATE UNIQUE INDEX Index3 on GameResults (playerid ASC, gameid ASC);"}], 69 | "triggers":[ 70 | {"name":"Trigger1", 71 | "sql":"CREATE TRIGGER Trigger1 BEFORE INSERT ON Games FOR EACH ROW BEGIN SELECT * FROM Games; END"}], 72 | "views":[ 73 | {"name":"Report_Players", 74 | "sql":"CREATE VIEW Report_Players AS SELECT Players.fname||' '|| Players.lname AS PlayerName FROM Players;"}, 75 | {"name":"Report_Results", 76 | "sql":"CREATE VIEW Report_Results AS SELECT Players.lname||', '|| Players.fname||' '|| Players.mname AS PlayerName, SUM(Games.weight*GameResults.score) AS TotalWeightedScore FROM GameResults JOIN Players ON (GameResults.playerid=Players.id) JOIN Games ON (GameResults.gameid=Games.id) GROUP BY GameResults.playerid ORDER BY TotalWeightedScore DESC;"}], 77 | "upgrades":[ 78 | {"version":"2.0", 79 | "step_1":[ 80 | {"sql":"ALTER TABLE Players ADD mname TEXT DEFAULT '' NOT NULL;"}, 81 | {"sql":"UPDATE Players SET mname=?, lname=? WHERE id=?;", "values":["James", "Fischer", 1]}, 82 | {"sql":"UPDATE Players SET mname=?, lname=? WHERE id=?;", "values":["JoJo", "Simpson", 2]}], 83 | "step_2":[ 84 | {"table":"Games", 85 | "addRecords":true}]}, 86 | {"version":"3.0", 87 | "step_1":[ 88 | {"table":"GameResults", 89 | "newTable":true, 90 | "addRecords":true}], 91 | "step_2":[ 92 | {"sql":"CREATE VIEW Report_Results AS SELECT Players.lname||', '|| Players.fname||' '|| Players.mname AS PlayerName, SUM(Games.weight*GameResults.score) AS TotalWeightedScore FROM GameResults JOIN Players ON (GameResults.playerid=Players.id) JOIN Games ON (GameResults.gameid=Games.id) GROUP BY GameResults.playerid ORDER BY TotalWeightedScore DESC;"}, 93 | {"sql":"CREATE UNIQUE INDEX Index3 on GameResults (playerid ASC, gameid ASC);"}]}]}} --------------------------------------------------------------------------------