├── .gitignore ├── .jshintrc ├── .project ├── .travis.yml ├── CONTRIBUTING.md ├── GruntFile.js ├── LICENSE ├── README.md ├── demo ├── demo.js ├── index.html └── trialtool.html ├── dist ├── jquery.indexeddb.js ├── jquery.indexeddb.min.js └── jquery.indexeddb.min.map ├── docs └── README.md ├── example ├── catalog.json ├── index.html └── style.css ├── index.html ├── indexeddb.jquery.json ├── lib ├── demoer │ ├── beautify.js │ ├── demoer.css │ ├── demoer.html │ ├── demoer.js │ └── jquery.min.js ├── firebug-lite.js ├── jquery.min.js └── queuedUnit.js ├── package.json ├── src └── jquery.indexeddb.js ├── style.css ├── test ├── api.txt ├── index.html ├── sample.html └── sampleData.js └── travis.sh /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | sauce_connect.log -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "camelcase": true, 3 | "nonew": true, 4 | "curly": true, 5 | "eqeqeq": true, 6 | "immed": true, 7 | "latedef": true, 8 | "newcap": true, 9 | "undef": true, 10 | "regexp": true, 11 | "evil": true, 12 | "eqnull": true, 13 | "expr": true, 14 | "browser": true, 15 | "globalstrict": true, 16 | "predef": ["DEBUG", 17 | "console", 18 | "require", 19 | "jQuery", 20 | "module", 21 | 22 | "_", 23 | "asyncTest", 24 | "DB", 25 | "dbVersion", 26 | "deepEqual", 27 | "equal", 28 | "expect", 29 | "fail", 30 | "module", 31 | "nextTest", 32 | "notEqual", 33 | "ok", 34 | "sample", 35 | "start", 36 | "stop", 37 | "queuedAsyncTest", 38 | "queuedModule", 39 | "unescapse", 40 | "process"] 41 | } -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | jquery-indexeddb 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | notifications: 3 | email: false 4 | after_failure: 5 | - ./travis.sh revert 6 | env: 7 | global: 8 | - secure: |- 9 | df2x3+OG30XluETYS3EM0bsZna6k4uu099FMzfnod7noFB5gBoIz4ygYyXR6 10 | NQS2iSwslZWsYIupEnDBH9cMqqRHlASExon8RxLGRYiChTLSsRSMlcd8SDMD 11 | ngGwSTq8hqtzCb0A0K6A93RppussTmoR1aRMUnBoxSeXwD0o4no= 12 | - secure: |- 13 | IBhbudcrXXb546jV6ANg2BLFdhM5sv/klEgdmRbH/kq44h4UcBZ6e07/GCoI 14 | RjiNBn8lavPVvC6l8wOpidbWbvg2n99Lhe0hdL0hZjVPj2afjHNBcOchnhJ5 15 | FJdsP3PlXuJhQetAfRktPzXyL4z8zhIp/0k9ptk4Vk5nWbOCCXI= 16 | after_success: 17 | - ./travis.sh merge 18 | node_js: 19 | - 0.8 20 | before_script: 21 | - ./travis.sh before 22 | - npm install -g grunt-cli 23 | language: node_js 24 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Please send all pull requests to the __incoming-pr__ branch. 2 | 3 | This repository uses [travis-ci](https://travis-ci.org/axemclion/jquery-indexeddb) for running the continuous integration (CI) tests. It uses [saucelabs](http://saucelabs.com) to run automated test cases on different browsers. The saucelabs server can be only accessed using a secure environment variable that is not accessible in pull requests. 4 | 5 | The Pull-Request lifecycle 6 | ------------------------ 7 | 8 | Thank you for submitting a patch to this project, we really appretiate it. Here is a quick overview of the process used to ensure that pull requests do not break existing functionality. You just have to do Step 1, all others are done by travis. 9 | 10 | 1. Send a pull request with your changes to `incoming-pr` branch 11 | 2. Travis runs only jslint on your pull request. 12 | * If the pull request tests fail, please correct the lint errors 13 | 3. Once the pull request passes the lint test, you pull request is merged into `incoming-pr` branch. 14 | 4. Travis runs *ALL* tests on `incoming-pr` branch. Since `incoming-pr` has access to the secure environment variables, it runs the saucelabs tests also. 15 | * If the saucelabs tests fail, `incoming-pr` is reverted to its original state. 16 | * Your changes are preserved in a separate branch. Please take a look at this branch and fix any failing tests 17 | 5. Once travis on the `incoming-pr` branch succeeds, your commits are automatically merged into `master`. -------------------------------------------------------------------------------- /GruntFile.js: -------------------------------------------------------------------------------- 1 | /* global module:false */ 2 | "use strict"; 3 | module.exports = function(grunt) { 4 | grunt.initConfig({ 5 | pkg: grunt.file.readJSON('package.json'), 6 | connect: { 7 | server: { 8 | options: { 9 | base: '.', 10 | port: 8080 11 | } 12 | } 13 | }, 14 | 15 | 'saucelabs-qunit': { 16 | all: { 17 | options: { 18 | username: 'indexeddbshim', 19 | key: process.env.SAUCE_ACCESS_KEY || '', 20 | tags: ['master'], 21 | urls: ['http://127.0.0.1:8080/test/index.html'], 22 | browsers: [{ 23 | browserName: 'chrome' 24 | }, { 25 | browserName: 'internet explorer', 26 | platform: 'Windows 2012', 27 | version: '10' 28 | } 29 | ] 30 | } 31 | } 32 | }, 33 | 34 | jshint: { 35 | all: { 36 | files: { 37 | src: ['Gruntfile.js', 'test/**/*.js'] 38 | }, 39 | options: { 40 | jshintrc: '.jshintrc' 41 | } 42 | } 43 | }, 44 | 45 | groundskeeper: { 46 | main: { 47 | files: { 48 | 'dist/jquery.indexeddb.js': ['src/jquery.indexeddb.js'] 49 | }, 50 | options: { 51 | 'console': false, 52 | 'debugger': false 53 | } 54 | } 55 | }, 56 | 57 | uglify: { 58 | options: { 59 | report: 'gzip', 60 | banner: '/*! <%= pkg.name %> v<%= pkg.version %> <%= grunt.template.today("yyyy-mm-dd") %> */\n', 61 | sourceMap: 'dist/<%= (pkg.name).replace(/-/g, ".") %>.min.map', 62 | sourceMapRoot: 'http://nparashuram.com/jquery-indexeddb/', 63 | sourceMappingURL: 'http://nparashuram.com/jquery-indexeddb/dist/<%=pkg.name%>.min.map' 64 | }, 65 | main: { 66 | files: { 67 | 'dist/<%= (pkg.name).replace(/-/g, ".")%>.min.js': ['dist/jquery.indexeddb.js'] 68 | } 69 | } 70 | }, 71 | watch: { 72 | all: { 73 | files: ['src/*.js'], 74 | tasks: ['uglify'] 75 | } 76 | }, 77 | clean: { 78 | dist: ['./dist'] 79 | } 80 | }); 81 | 82 | // Loading dependencies 83 | for (var key in grunt.file.readJSON('package.json').devDependencies) { 84 | if (key !== 'grunt' && key.indexOf('grunt') === 0) { 85 | grunt.loadNpmTasks(key); 86 | } 87 | } 88 | 89 | var testJobs = ["build", "connect"]; 90 | if (typeof process.env.SAUCE_ACCESS_KEY !== 'undefined') { 91 | testJobs.push("saucelabs-qunit"); 92 | } 93 | 94 | grunt.registerTask('build', ['jshint', 'groundskeeper', 'uglify']); 95 | grunt.registerTask('test', testJobs); 96 | grunt.registerTask('default', 'build'); 97 | grunt.registerTask('dev', ['build', 'connect', 'watch']); 98 | }; -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2012 Parashuram N and other contributors 2 | http://nparashuram.com 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining 5 | a copy of this software and associated documentation files (the 6 | "Software"), to deal in the Software without restriction, including 7 | without limitation the rights to use, copy, modify, merge, publish, 8 | distribute, sublicense, and/or sell copies of the Software, and to 9 | permit persons to whom the Software is furnished to do so, subject to 10 | the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 19 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 20 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Jquery Plugin for the IndexedDB API [![Build Status](https://secure.travis-ci.org/axemclion/jquery-indexeddb.png?branch=master)](https://travis-ci.org/axemclion/jquery-indexeddb) 2 | =============================================================================================================================================================================== 3 | 4 | IndexedDB is a database inside a browser to save and retrieve objects on the browser/client. The JQuery IndexedDB Plugin is a wrapper on the IndexedDB API for JQuery. 5 | 6 | Links 7 | ------ 8 | 9 | * Home page - http://nparashuram.com/jquery-indexeddb/index.html 10 | * Download the plugin - http://nparashuram.com/jquery-indexeddb/jquery.indexeddb.js 11 | * Sample application - http://nparashuram.com/jquery-indexeddb/example/index.html 12 | * API Documentation - https://github.com/axemclion/jquery-indexeddb/blob/gh-pages/docs/README.md 13 | 14 | Summary 15 | ------- 16 | The Jquery IndexedDB Plugin brings to goodness of Jquery to the browser's native IndexedDB API. It supports method chaining, promises and smart defaults, enabling you to get more done with less code. It also abstracts out differences in browser implementations. 17 | 18 | Code 19 | ---- 20 | The code written with the jQuery plugin is pretty simple. It looks something like 21 | 22 | ```javascript 23 | $.indexeddb("BookShop-1").objectStore("BookList").openCursor().each(write); 24 | ``` 25 | 26 | 27 | A typical operation using the IndexedDB API would involve using the request model, creating transactions, checking for existence of object store using error responses and exceptions and then finally getting to the part where the data is actually iterated over. 28 | 29 | ```javascript 30 | var request = window.indexedDB.open("BookShop-1"); 31 | request.onsuccess = function(event){ 32 | var db = request.result; 33 | var transaction = db.transaction(["BookList"], IDBTransaction.READ_WRITE); 34 | var objectStore = transaction.objectStore("BookList"); 35 | var request = DAO.objectStore.openCursor(); 36 | request.onsuccess = function(event){ 37 | var cursor = request.result; 38 | if (cursor) { 39 | write(cursor.key + "" + cursor.value); 40 | cursor["continue"](); 41 | } 42 | }; 43 | }; 44 | 45 | ``` 46 | 47 | Read more about the API syntax in the [documentation](https://github.com/axemclion/jquery-indexeddb/blob/master/docs/README.md). 48 | 49 | 50 | Building 51 | -------- 52 | 53 | Node is required to build this project. 54 | 55 | * `npm insall -g grunt-cli` # to install the grunt command line 56 | * `npm install` # to install all other dependencies from the package.json 57 | * Run one of the following grunt commands 58 | * `grunt` # to just minify, lint and build the source. Final file available in `dist/` folder 59 | * `grunt dev` # to start a web server. Navigate to `http://127.0.0.1:8080/test/` to run Qunit tests 60 | 61 | -------------------------------------------------------------------------------- /demo/demo.js: -------------------------------------------------------------------------------- 1 | function data(){ 2 | return { 3 | "bookName": "bookName-" + parseInt(Math.random() * 100), 4 | "price": parseInt(Math.random() * 1000), 5 | "checkedOut": new Date() 6 | } 7 | }; 8 | 9 | window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB; 10 | window.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange; 11 | window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction; 12 | 13 | var indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB; 14 | var dbDeleteRequest = indexedDB.deleteDatabase("BookShop1"); 15 | dbDeleteRequest.onsuccess = function(e){ 16 | }; 17 | 18 | $.indexedDB("BookShop1", { 19 | "schema": { 20 | 1: function(versionTransaction){ 21 | versionTransaction.createObjectStore("OldBookList", { 22 | "autoIncrement": true 23 | }); 24 | versionTransaction.createObjectStore("TempBookList"); 25 | } 26 | } 27 | }); 28 | 29 | var jqueryIndexedDB_Test = { 30 | "Create Object Store": { 31 | "code": function(){ 32 | $.indexedDB("BookShop1", { 33 | "schema": { 34 | 2: function(v){ 35 | var objectStore = v.createObjectStore("BookList", { 36 | "keyPath": "id", 37 | "autoIncrement": true 38 | }); 39 | objectStore.createIndex("price"); 40 | console.info("Created new object store"); 41 | } 42 | } 43 | }).then(console.info, console.error); 44 | }, 45 | 46 | "alternate": function(){ 47 | var request = window.indexedDB.open("BookShop1"); 48 | request.onsuccess = function(event){ 49 | var db = request.result; 50 | var req = db.setVersion((isNaN(parseInt(db.version, 10)) ? 0 : parseInt(db.version, 10) + 1)); 51 | req.onsuccess = function(){ 52 | var transaction = req.result; 53 | var objectStore = transaction.db.createObjectStore("BookList", { 54 | "keyPath": "id", 55 | "autoIncrement": true 56 | }); 57 | objectStore.createIndex("price"); 58 | console.info(objectStore); 59 | }; 60 | req.onerror = function(e){ 61 | console.error(e, req); 62 | }; 63 | }; 64 | request.onerror = function(e){ 65 | console.error(e, request); 66 | }; 67 | } 68 | }, 69 | 70 | "Delete Object Store": { 71 | "code": function(){ 72 | $.indexedDB("BookShop1", 3).then(console.info, console.error, function(v){ 73 | v.deleteObjectStore("TempBookList"); 74 | console.info("Object Store deleted"); 75 | }); 76 | }, 77 | "alternate": function(){ 78 | var request = window.indexedDB.open("BookShop1"); 79 | request.onsuccess = function(event){ 80 | var db = request.result; 81 | var req = db.setVersion((isNaN(parseInt(db.version, 10)) ? 0 : parseInt(db.version, 10) + 1)); 82 | req.onsuccess = function(){ 83 | var transaction = req.result; 84 | transaction.db.deleteObjectStore("TempBookList"); 85 | console.info(transaction.db); 86 | }; 87 | req.onerror = function(e){ 88 | console.error(e, req); 89 | }; 90 | }; 91 | request.onerror = function(e){ 92 | console.error(e, request); 93 | }; 94 | } 95 | }, 96 | 97 | "Transaction": { 98 | "code": function(){ 99 | var transaction = $.indexedDB("BookShop1").transaction(["OldBookList", "BookList"], $.indexedDB.IDBTransaction.READ_WRITE); 100 | transaction.then(console.info, console.error); 101 | transaction.progress(function(t){ 102 | t.objectStore("BookList").add(data()).then(console.info, console.error); 103 | t.objectStore("OldBookList").add(data(), new Date().getTime()).then(console.info, console.error); 104 | }); 105 | }, 106 | "alternate": function(){ 107 | var request = window.indexedDB.open("BookShop1"); 108 | request.onsuccess = function(event){ 109 | var db = request.result; 110 | var transaction = db.transaction([], IDBTransaction.READ_WRITE); 111 | console.info(transaction); 112 | var bookList = transaction.objectStore("BookList"); 113 | var oldBookList = transaction.objectStore("OldBookList"); 114 | var req1 = bookList.add(data()); 115 | var req2 = oldBookList.add(data(), new Date().getTime()); 116 | req1.onsuccess = function(){ 117 | console.info(req1.result); 118 | }; 119 | req1.onerror = function(e){ 120 | console.error(e, req1); 121 | }; 122 | req2.onsuccess = function(){ 123 | console.info(req2.result); 124 | }; 125 | req2.onerror = function(e){ 126 | console.error(e, req2); 127 | }; 128 | }; 129 | request.onerror = function(e){ 130 | console.error(e, request); 131 | }; 132 | } 133 | }, 134 | 135 | "Open Object Store, but dont create if does not exist": { 136 | "code": function(){ 137 | $.indexedDB("BookShop1").objectStore("BookList", false); 138 | }, 139 | "alternate": function(){ 140 | var request = window.indexedDB.open("BookShop1"); 141 | request.onsuccess = function(event){ 142 | var db = request.result; 143 | try { 144 | var transaction = db.transaction([], IDBTransaction.READ_WRITE); 145 | var objectStore = transaction.objectStore("BookList"); 146 | console.info(objectStore); 147 | } catch (e) { 148 | console.error(e, request); 149 | } 150 | }; 151 | request.onerror = function(e){ 152 | console.error(e, request); 153 | }; 154 | } 155 | }, 156 | 157 | "Open Object Store, or create if does not exist": { 158 | "code": function(){ 159 | $.indexedDB("BookShop1").objectStore("BookList", { 160 | "keyPath": "id", 161 | "autoIncrement": true 162 | }); 163 | }, 164 | "alternate": function(){ 165 | var request = window.indexedDB.open("BookShop1"); 166 | request.onsuccess = function(event){ 167 | var db = request.result; 168 | try { 169 | var transaction = db.transaction([], IDBTransaction.READ_WRITE); 170 | var objectStore = transaction.objectStore("BookList"); 171 | console.info(objectStore); 172 | } catch (e) { 173 | var req = db.setVersion((isNaN(parseInt(db.version, 10)) ? 0 : parseInt(db.version, 10) + 1)); 174 | req.onsuccess = function(){ 175 | var transaction = req.result; 176 | var objectStore = transaction.db.createObjectStore("BookList", { 177 | "autoIncrement": true 178 | }); 179 | console.info(objectStore); 180 | }; 181 | req.onerror = function(e){ 182 | console.error(e, req); 183 | }; 184 | } 185 | 186 | }; 187 | request.onerror = function(e){ 188 | console.error(e, request); 189 | }; 190 | } 191 | }, 192 | 193 | "Add Data to Object Store": { 194 | "code": function(){ 195 | window.book = data(); 196 | $.indexedDB("BookShop1").objectStore("BookList", true).add(book).then(function(val){ 197 | book.id = val; 198 | console.info(val); 199 | }, console.error); 200 | }, 201 | "alternate": function(){ 202 | window.book = data(); 203 | var request = window.indexedDB.open("BookShop1"); 204 | request.onsuccess = function(event){ 205 | var db = request.result; 206 | var transaction = db.transaction([], IDBTransaction.READ_WRITE); 207 | var bookList = transaction.objectStore("BookList"); 208 | var req = bookList.add(data()); 209 | req.onsuccess = function(){ 210 | book.id = req.result; 211 | console.info(req.result); 212 | }; 213 | req.onerror = function(e){ 214 | console.error(e, req); 215 | }; 216 | }; 217 | request.onerror = function(e){ 218 | console.error(e, request); 219 | }; 220 | } 221 | }, 222 | 223 | "Get data": { 224 | "code": function(){ 225 | $.indexedDB("BookShop1").objectStore("BookList").get(book.id).then(console.info, console.error); 226 | }, 227 | "alternate": function(){ 228 | var request = window.indexedDB.open("BookShop1"); 229 | request.onsuccess = function(event){ 230 | var db = request.result; 231 | var transaction = db.transaction([], IDBTransaction.READ_WRITE); 232 | var bookList = transaction.objectStore("BookList"); 233 | var req = bookList.get(book.id); 234 | req.onsuccess = function(){ 235 | console.info(req.result); 236 | }; 237 | req.onerror = function(e){ 238 | console.error(e, req); 239 | }; 240 | }; 241 | request.onerror = function(e){ 242 | console.error(e, request); 243 | }; 244 | } 245 | }, 246 | 247 | "Modify Data in Object Store": { 248 | "code": function(){ 249 | book["modified" + Math.random()] = true; 250 | $.indexedDB("BookShop1").objectStore("BookList").put(book, new Date().getTime()).then(console.info, console.error); 251 | }, 252 | "alternate": function(){ 253 | book["modified" + Math.random()] = true; 254 | var request = window.indexedDB.open("BookShop1"); 255 | request.onsuccess = function(event){ 256 | var db = request.result; 257 | var transaction = db.transaction([], IDBTransaction.READ_WRITE); 258 | var bookList = transaction.objectStore("BookList"); 259 | var req = bookList.put(data(), new Date().getTime()); 260 | req.onsuccess = function(){ 261 | console.info(req.result); 262 | }; 263 | req.onerror = function(e){ 264 | console.error(e, req); 265 | }; 266 | }; 267 | request.onerror = function(e){ 268 | console.error(e, request); 269 | }; 270 | } 271 | }, 272 | 273 | "Cursor and list all items in the object store": { 274 | "code": function(){ 275 | $.indexedDB("BookShop1").objectStore("BookList").each(console.info); 276 | }, 277 | "alternate": function(){ 278 | var request = window.indexedDB.open("BookShop1"); 279 | request.onsuccess = function(event){ 280 | var db = request.result; 281 | var transaction = db.transaction([], IDBTransaction.READ_WRITE); 282 | var bookList = transaction.objectStore("BookList"); 283 | var req = bookList.openCursor(); 284 | req.onsuccess = function(){ 285 | var cursor = req.result; 286 | if (cursor) { 287 | console.info(req.result.value); 288 | cursor["continue"](); 289 | } 290 | }; 291 | req.onerror = function(e){ 292 | console.error(e, req); 293 | }; 294 | }; 295 | request.onerror = function(e){ 296 | console.error(e, request); 297 | }; 298 | } 299 | }, 300 | 301 | "Cursor and delete items with price that is an odd number": { 302 | "code": function(){ 303 | $.indexedDB("BookShop1").objectStore("BookList").each(function(elem){ 304 | if (elem.value && elem.value.price % 2) { 305 | console.info("Deleting", elem.value); 306 | elem["delete"](); 307 | return true; 308 | } 309 | }); 310 | }, 311 | "alternate": function(){ 312 | var request = window.indexedDB.open("BookShop1"); 313 | request.onsuccess = function(event){ 314 | var db = request.result; 315 | var transaction = db.transaction([], IDBTransaction.READ_WRITE); 316 | var bookList = transaction.objectStore("BookList"); 317 | var req = bookList.openCursor(); 318 | req.onsuccess = function(){ 319 | var cursor = req.result; 320 | if (cursor) { 321 | if (cursor.value && cursor.value.price % 2) { 322 | console.info("Deleting", cursor.value); 323 | cursor["delete"](); 324 | } 325 | cursor["continue"](); 326 | } 327 | }; 328 | req.onerror = function(e){ 329 | console.error(e, req); 330 | }; 331 | }; 332 | request.onerror = function(e){ 333 | console.error(e, request); 334 | }; 335 | } 336 | }, 337 | 338 | "Cursor and update items with price that is an even number": { 339 | "code": function(){ 340 | $.indexedDB("BookShop1").objectStore("BookList").each(function(elem){ 341 | if (elem.value && elem.value.price % 2) { 342 | console.info("Updating", elem.value); 343 | elem.value["modifiedCursor-" + Math.random()] = true; 344 | elem.update(elem.value); 345 | } 346 | }); 347 | }, 348 | "alternate": function(){ 349 | var request = window.indexedDB.open("BookShop1"); 350 | request.onsuccess = function(event){ 351 | var db = request.result; 352 | var transaction = db.transaction([], IDBTransaction.READ_WRITE); 353 | var bookList = transaction.objectStore("BookList"); 354 | var req = bookList.openCursor(); 355 | req.onsuccess = function(){ 356 | var cursor = req.result; 357 | if (cursor) { 358 | if (cursor.value && cursor.value.price % 2) { 359 | cursor.value["modified-" + Math.random()] = true; 360 | console.info("Updating", cursor.value); 361 | cursor.update(cursor.value); 362 | } 363 | cursor["continue"](); 364 | } 365 | }; 366 | req.onerror = function(e){ 367 | console.error(e, req); 368 | }; 369 | }; 370 | request.onerror = function(e){ 371 | console.error(e, request); 372 | }; 373 | } 374 | }, 375 | "Open an Index and iterate over its objects": { 376 | "code": function(){ 377 | $.indexedDB("BookShop1").objectStore("BookList").index("price").each(console.info); 378 | }, 379 | "alternate": function(){ 380 | var request = window.indexedDB.open("BookShop1"); 381 | request.onsuccess = function(event){ 382 | var db = request.result; 383 | var transaction = db.transaction([], IDBTransaction.READ_WRITE); 384 | var bookList = transaction.objectStore("BookList"); 385 | // Assuming that index exists 386 | var index = bookList.index("price-index"); 387 | var req = index.openCursor(); 388 | req.onsuccess = function(){ 389 | var cursor = req.result; 390 | if (cursor) { 391 | console.info(cursor.value); 392 | cursor["continue"](); 393 | } 394 | }; 395 | req.onerror = function(e){ 396 | console.error(e, req); 397 | }; 398 | }; 399 | request.onerror = function(e){ 400 | console.error(e, request); 401 | }; 402 | } 403 | }, 404 | 405 | "Open a key cursor on an Index and iterate over its objects": { 406 | "code": function(){ 407 | $.indexedDB("BookShop1").objectStore("BookList").index("price").eachKey(console.info, [200, 500]); 408 | }, 409 | "alternate": function(){ 410 | var request = window.indexedDB.open("BookShop1"); 411 | request.onsuccess = function(event){ 412 | var db = request.result; 413 | var transaction = db.transaction([], IDBTransaction.READ_WRITE); 414 | var bookList = transaction.objectStore("BookList"); 415 | var index = bookList.index("price-index"); 416 | var range = new IDBKeyRange.bound(200, 500, true, true); 417 | var req = index.openKeyCursor(range); 418 | req.onsuccess = function(){ 419 | var cursor = req.result; 420 | if (cursor) { 421 | console.info(cursor.value, cursor.key); 422 | cursor["continue"](); 423 | } 424 | 425 | }; 426 | req.onerror = function(e){ 427 | console.error(e, req); 428 | }; 429 | }; 430 | request.onerror = function(e){ 431 | console.error(e, request); 432 | }; 433 | } 434 | } 435 | }; 436 | -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | IndexedDB Jquery Plugin 5 | 6 | 8 | 9 | 10 |
11 |
12 |

JQuery IndexedDB Plugin- API Demo

13 |
14 |
15 |
16 | 18 | 20 | 22 | 24 | 27 | 43 |
44 | 45 | 46 | -------------------------------------------------------------------------------- /demo/trialtool.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Jquery IndexedDB Plugin 4 | 13 | 15 | 16 | 17 |
18 |

Jquery IndexedDB Plugin

19 |

20 | The examples listed here illustrate the use of IndexedDB Jquery plugin. 21 |

22 |
23 | 327 | 328 | 329 | -------------------------------------------------------------------------------- /dist/jquery.indexeddb.js: -------------------------------------------------------------------------------- 1 | (function($, undefined) { 2 | 'use strict'; 3 | var indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB; 4 | var IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange; 5 | var IDBCursor = window.IDBCursor || window.webkitIDBCursor || {}; 6 | if (typeof IDBCursor.PREV === "undefined") { 7 | IDBCursor.PREV = "prev"; 8 | } 9 | if (typeof IDBCursor.NEXT === "undefined") { 10 | IDBCursor.NEXT = "next"; 11 | } 12 | 13 | /** 14 | * Best to use the constant IDBTransaction since older version support numeric types while the latest spec 15 | * supports strings 16 | */ 17 | var IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction; 18 | 19 | function getDefaultTransaction(mode) { 20 | var result = null; 21 | switch (mode) { 22 | case 0: 23 | case 1: 24 | case "readwrite": 25 | case "readonly": 26 | result = mode; 27 | break; 28 | default: 29 | result = IDBTransaction.READ_WRITE || "readwrite"; 30 | } 31 | return result; 32 | } 33 | 34 | $.extend({ 35 | /** 36 | * The IndexedDB object used to open databases 37 | * @param {Object} dbName - name of the database 38 | * @param {Object} config - version, onupgradeneeded, onversionchange, schema 39 | */ 40 | "indexedDB": function(dbName, config) { 41 | if (config) { 42 | // Parse the config argument 43 | if (typeof config === "number") config = { 44 | "version": config 45 | }; 46 | 47 | var version = config.version; 48 | if (config.schema && !version) { 49 | var max = -1; 50 | for (var key in config.schema) { 51 | max = max > key ? max : key; 52 | } 53 | version = config.version || max; 54 | } 55 | } 56 | 57 | 58 | var wrap = { 59 | "request": function(req, args) { 60 | return $.Deferred(function(dfd) { 61 | try { 62 | var idbRequest = typeof req === "function" ? req(args) : req; 63 | idbRequest.onsuccess = function(e) { 64 | 65 | dfd.resolveWith(idbRequest, [idbRequest.result, e]); 66 | }; 67 | idbRequest.onerror = function(e) { 68 | 69 | dfd.rejectWith(idbRequest, [idbRequest.error, e]); 70 | }; 71 | if (typeof idbRequest.onblocked !== "undefined" && idbRequest.onblocked === null) { 72 | idbRequest.onblocked = function(e) { 73 | 74 | var res; 75 | try { 76 | res = idbRequest.result; 77 | } catch (e) { 78 | res = null; // Required for Older Chrome versions, accessing result causes error 79 | } 80 | dfd.notifyWith(idbRequest, [res, e]); 81 | }; 82 | } 83 | if (typeof idbRequest.onupgradeneeded !== "undefined" && idbRequest.onupgradeneeded === null) { 84 | idbRequest.onupgradeneeded = function(e) { 85 | 86 | dfd.notifyWith(idbRequest, [idbRequest.result, e]); 87 | }; 88 | } 89 | } catch (e) { 90 | e.name = "exception"; 91 | dfd.rejectWith(idbRequest, ["exception", e]); 92 | } 93 | }); 94 | }, 95 | // Wraps the IDBTransaction to return promises, and other dependent methods 96 | "transaction": function(idbTransaction) { 97 | return { 98 | "objectStore": function(storeName) { 99 | try { 100 | return wrap.objectStore(idbTransaction.objectStore(storeName)); 101 | } catch (e) { 102 | idbTransaction.readyState !== idbTransaction.DONE && idbTransaction.abort(); 103 | return wrap.objectStore(null); 104 | } 105 | }, 106 | "createObjectStore": function(storeName, storeParams) { 107 | try { 108 | return wrap.objectStore(idbTransaction.db.createObjectStore(storeName, storeParams)); 109 | } catch (e) { 110 | idbTransaction.readyState !== idbTransaction.DONE && idbTransaction.abort(); 111 | } 112 | }, 113 | "deleteObjectStore": function(storeName) { 114 | try { 115 | idbTransaction.db.deleteObjectStore(storeName); 116 | } catch (e) { 117 | idbTransaction.readyState !== idbTransaction.DONE && idbTransaction.abort(); 118 | } 119 | }, 120 | "abort": function() { 121 | idbTransaction.abort(); 122 | } 123 | }; 124 | }, 125 | "objectStore": function(idbObjectStore) { 126 | var result = {}; 127 | // Define CRUD operations 128 | var crudOps = ["add", "put", "get", "delete", "clear", "count"]; 129 | for (var i = 0; i < crudOps.length; i++) { 130 | result[crudOps[i]] = (function(op) { 131 | return function() { 132 | return wrap.request(function(args) { 133 | return idbObjectStore[op].apply(idbObjectStore, args); 134 | }, arguments); 135 | }; 136 | })(crudOps[i]); 137 | } 138 | 139 | result.each = function(callback, range, direction) { 140 | return wrap.cursor(function() { 141 | if (direction) { 142 | return idbObjectStore.openCursor(wrap.range(range), direction); 143 | } else { 144 | return idbObjectStore.openCursor(wrap.range(range)); 145 | } 146 | }, callback); 147 | }; 148 | 149 | result.index = function(name) { 150 | return wrap.index(function() { 151 | return idbObjectStore.index(name); 152 | }); 153 | }; 154 | 155 | result.createIndex = function(prop, options, indexName) { 156 | if (arguments.length === 2 && typeof options === "string") { 157 | indexName = arguments[1]; 158 | options = null; 159 | } 160 | if (!indexName) { 161 | indexName = prop; 162 | } 163 | return wrap.index(function() { 164 | return idbObjectStore.createIndex(indexName, prop, options); 165 | }); 166 | }; 167 | 168 | result.deleteIndex = function(indexName) { 169 | return idbObjectStore.deleteIndex(indexName); 170 | }; 171 | 172 | return result; 173 | }, 174 | 175 | "range": function(r) { 176 | if ($.isArray(r)) { 177 | if (r.length === 1) { 178 | return IDBKeyRange.only(r[0]); 179 | } else { 180 | return IDBKeyRange.bound(r[0], r[1], (typeof r[2] === 'undefined') ? false : r[2], (typeof r[3] === 'undefined') ? false : r[3]); 181 | } 182 | } else if (typeof r === "undefined") { 183 | return null; 184 | } else { 185 | return r; 186 | } 187 | }, 188 | 189 | "cursor": function(idbCursor, callback) { 190 | return $.Deferred(function(dfd) { 191 | try { 192 | 193 | var cursorReq = typeof idbCursor === "function" ? idbCursor() : idbCursor; 194 | cursorReq.onsuccess = function(e) { 195 | 196 | if (!cursorReq.result) { 197 | dfd.resolveWith(cursorReq, [null, e]); 198 | return; 199 | } 200 | var elem = { 201 | // Delete, update do not move 202 | "delete": function() { 203 | return wrap.request(function() { 204 | return cursorReq.result["delete"](); 205 | }); 206 | }, 207 | "update": function(data) { 208 | return wrap.request(function() { 209 | return cursorReq.result["update"](data); 210 | }); 211 | }, 212 | "next": function(key) { 213 | this.data = key; 214 | }, 215 | "key": cursorReq.result.key, 216 | "value": cursorReq.result.value 217 | }; 218 | 219 | dfd.notifyWith(cursorReq, [elem, e]); 220 | var result = callback.apply(cursorReq, [elem]); 221 | 222 | try { 223 | if (result === false) { 224 | dfd.resolveWith(cursorReq, [null, e]); 225 | } else if (typeof result === "number") { 226 | cursorReq.result["advance"].apply(cursorReq.result, [result]); 227 | } else { 228 | if (elem.data) cursorReq.result["continue"].apply(cursorReq.result, [elem.data]); 229 | else cursorReq.result["continue"](); 230 | } 231 | } catch (e) { 232 | 233 | dfd.rejectWith(cursorReq, [cursorReq.result, e]); 234 | } 235 | }; 236 | cursorReq.onerror = function(e) { 237 | 238 | dfd.rejectWith(cursorReq, [cursorReq.result, e]); 239 | }; 240 | } catch (e) { 241 | 242 | e.type = "exception"; 243 | dfd.rejectWith(cursorReq, [null, e]); 244 | } 245 | }); 246 | }, 247 | 248 | "index": function(index) { 249 | try { 250 | var idbIndex = (typeof index === "function" ? index() : index); 251 | } catch (e) { 252 | idbIndex = null; 253 | } 254 | 255 | return { 256 | "each": function(callback, range, direction) { 257 | return wrap.cursor(function() { 258 | if (direction) { 259 | return idbIndex.openCursor(wrap.range(range), direction); 260 | } else { 261 | return idbIndex.openCursor(wrap.range(range)); 262 | } 263 | 264 | }, callback); 265 | }, 266 | "eachKey": function(callback, range, direction) { 267 | return wrap.cursor(function() { 268 | if (direction) { 269 | return idbIndex.openKeyCursor(wrap.range(range), direction); 270 | } else { 271 | return idbIndex.openKeyCursor(wrap.range(range)); 272 | } 273 | }, callback); 274 | }, 275 | "get": function(key) { 276 | if (typeof idbIndex.get === "function") { 277 | return wrap.request(idbIndex.get(key)); 278 | } else { 279 | return idbIndex.openCursor(wrap.range(key)); 280 | } 281 | }, 282 | "count": function() { 283 | if (typeof idbIndex.count === "function") { 284 | return wrap.request(idbIndex.count()); 285 | } else { 286 | throw "Count not implemented for cursors"; 287 | } 288 | }, 289 | "getKey": function(key) { 290 | if (typeof idbIndex.getKey === "function") { 291 | return wrap.request(idbIndex.getKey(key)); 292 | } else { 293 | return idbIndex.openKeyCursor(wrap.range(key)); 294 | } 295 | } 296 | }; 297 | } 298 | }; 299 | 300 | 301 | // Start with opening the database 302 | var dbPromise = wrap.request(function() { 303 | 304 | return version ? indexedDB.open(dbName, parseInt(version)) : indexedDB.open(dbName); 305 | }); 306 | dbPromise.then(function(db, e) { 307 | 308 | db.onversionchange = function() { 309 | // Try to automatically close the database if there is a version change request 310 | if (!(config && config.onversionchange && config.onversionchange() !== false)) { 311 | db.close(); 312 | } 313 | }; 314 | }, function(error, e) { 315 | 316 | // Nothing much to do if an error occurs 317 | }, function(db, e) { 318 | if (e && e.type === "upgradeneeded") { 319 | if (config && config.schema) { 320 | // Assuming that version is always an integer 321 | 322 | for (var i = e.oldVersion + 1; i <= e.newVersion; i++) { 323 | typeof config.schema[i] === "function" && config.schema[i].call(this, wrap.transaction(this.transaction)); 324 | } 325 | } 326 | if (config && typeof config.upgrade === "function") { 327 | config.upgrade.call(this, wrap.transaction(this.transaction)); 328 | } 329 | } 330 | }); 331 | 332 | return $.extend(dbPromise, { 333 | "cmp": function(key1, key2) { 334 | return indexedDB.cmp(key1, key2); 335 | }, 336 | "deleteDatabase": function() { 337 | // Kinda looks ugly coz DB is opened before it needs to be deleted. 338 | // Blame it on the API 339 | return $.Deferred(function(dfd) { 340 | dbPromise.then(function(db, e) { 341 | db.close(); 342 | wrap.request(function() { 343 | return indexedDB.deleteDatabase(dbName); 344 | }).then(function(result, e) { 345 | dfd.resolveWith(this, [result, e]); 346 | }, function(error, e) { 347 | dfd.rejectWith(this, [error, e]); 348 | }, function(db, e) { 349 | dfd.notifyWith(this, [db, e]); 350 | }); 351 | }, function(error, e) { 352 | dfd.rejectWith(this, [error, e]); 353 | }, function(db, e) { 354 | dfd.notifyWith(this, [db, e]); 355 | }); 356 | }); 357 | }, 358 | "transaction": function(storeNames, mode) { 359 | !$.isArray(storeNames) && (storeNames = [storeNames]); 360 | mode = getDefaultTransaction(mode); 361 | return $.Deferred(function(dfd) { 362 | dbPromise.then(function(db, e) { 363 | var idbTransaction; 364 | try { 365 | 366 | idbTransaction = db.transaction(storeNames, mode); 367 | 368 | idbTransaction.onabort = idbTransaction.onerror = function(e) { 369 | dfd.rejectWith(idbTransaction, [e]); 370 | }; 371 | idbTransaction.oncomplete = function(e) { 372 | dfd.resolveWith(idbTransaction, [e]); 373 | }; 374 | } catch (e) { 375 | 376 | e.type = "exception"; 377 | dfd.rejectWith(this, [e]); 378 | return; 379 | } 380 | try { 381 | dfd.notifyWith(idbTransaction, [wrap.transaction(idbTransaction)]); 382 | } catch (e) { 383 | e.type = "exception"; 384 | dfd.rejectWith(this, [e]); 385 | } 386 | }, function(err, e) { 387 | dfd.rejectWith(this, [e, err]); 388 | }, function(res, e) { 389 | 390 | //dfd.notifyWith(this, ["", e]); 391 | }); 392 | 393 | }); 394 | }, 395 | "objectStore": function(storeName, mode) { 396 | var me = this, 397 | result = {}; 398 | 399 | function op(callback) { 400 | return $.Deferred(function(dfd) { 401 | function onTransactionProgress(trans, callback) { 402 | try { 403 | 404 | callback(trans.objectStore(storeName)).then(function(result, e) { 405 | dfd.resolveWith(this, [result, e]); 406 | }, function(err, e) { 407 | dfd.rejectWith(this, [err, e]); 408 | }); 409 | } catch (e) { 410 | 411 | e.name = "exception"; 412 | dfd.rejectWith(trans, [e, e]); 413 | } 414 | } 415 | me.transaction(storeName, getDefaultTransaction(mode)).then(function() { 416 | 417 | // Nothing to do when transaction is complete 418 | }, function(err, e) { 419 | // If transaction fails, CrudOp fails 420 | if (err.code === err.NOT_FOUND_ERR && (mode === true || typeof mode === "object")) { 421 | 422 | var db = this.result; 423 | db.close(); 424 | dbPromise = wrap.request(function() { 425 | 426 | return indexedDB.open(dbName, (parseInt(db.version, 10) || 1) + 1); 427 | }); 428 | dbPromise.then(function(db, e) { 429 | 430 | db.onversionchange = function() { 431 | // Try to automatically close the database if there is a version change request 432 | if (!(config && config.onversionchange && config.onversionchange() !== false)) { 433 | db.close(); 434 | } 435 | }; 436 | me.transaction(storeName, getDefaultTransaction(mode)).then(function() { 437 | 438 | // Nothing much to do 439 | }, function(err, e) { 440 | dfd.rejectWith(this, [err, e]); 441 | }, function(trans, e) { 442 | 443 | onTransactionProgress(trans, callback); 444 | }); 445 | }, function(err, e) { 446 | dfd.rejectWith(this, [err, e]); 447 | }, function(db, e) { 448 | if (e.type === "upgradeneeded") { 449 | try { 450 | 451 | db.createObjectStore(storeName, mode === true ? { 452 | "autoIncrement": true 453 | } : mode); 454 | 455 | } catch (ex) { 456 | 457 | dfd.rejectWith(this, [ex, e]); 458 | } 459 | } 460 | }); 461 | } else { 462 | dfd.rejectWith(this, [err, e]); 463 | } 464 | }, function(trans) { 465 | 466 | onTransactionProgress(trans, callback); 467 | }); 468 | }); 469 | } 470 | 471 | function crudOp(opName, args) { 472 | return op(function(wrappedObjectStore) { 473 | return wrappedObjectStore[opName].apply(wrappedObjectStore, args); 474 | }); 475 | } 476 | 477 | function indexOp(opName, indexName, args) { 478 | return op(function(wrappedObjectStore) { 479 | var index = wrappedObjectStore.index(indexName); 480 | return index[opName].apply(index[opName], args); 481 | }); 482 | } 483 | 484 | var crud = ["add", "delete", "get", "put", "clear", "count", "each"]; 485 | for (var i = 0; i < crud.length; i++) { 486 | result[crud[i]] = (function(op) { 487 | return function() { 488 | return crudOp(op, arguments); 489 | }; 490 | })(crud[i]); 491 | } 492 | 493 | result.index = function(indexName) { 494 | return { 495 | "each": function(callback, range, direction) { 496 | return indexOp("each", indexName, [callback, range, direction]); 497 | }, 498 | "eachKey": function(callback, range, direction) { 499 | return indexOp("eachKey", indexName, [callback, range, direction]); 500 | }, 501 | "get": function(key) { 502 | return indexOp("get", indexName, [key]); 503 | }, 504 | "count": function() { 505 | return indexOp("count", indexName, []); 506 | }, 507 | "getKey": function(key) { 508 | return indexOp("getKey", indexName, [key]); 509 | } 510 | }; 511 | }; 512 | 513 | return result; 514 | } 515 | }); 516 | } 517 | }); 518 | 519 | $.indexedDB.IDBCursor = IDBCursor; 520 | $.indexedDB.IDBTransaction = IDBTransaction; 521 | $.idb = $.indexedDB; 522 | })(jQuery); -------------------------------------------------------------------------------- /dist/jquery.indexeddb.min.js: -------------------------------------------------------------------------------- 1 | /*! jquery-indexeddb v1.0.0 2013-10-30 */ 2 | (function(e){"use strict";function n(e){var n=null;switch(e){case 0:case 1:case"readwrite":case"readonly":n=e;break;default:n=u.READ_WRITE||"readwrite"}return n}var t=window.indexedDB||window.mozIndexedDB||window.webkitIndexedDB||window.msIndexedDB,r=window.IDBKeyRange||window.webkitIDBKeyRange,o=window.IDBCursor||window.webkitIDBCursor||{};o.PREV===undefined&&(o.PREV="prev"),o.NEXT===undefined&&(o.NEXT="next");var u=window.IDBTransaction||window.webkitIDBTransaction;e.extend({indexedDB:function(o,u){if(u){"number"==typeof u&&(u={version:u});var i=u.version;if(u.schema&&!i){var c=-1;for(var a in u.schema)c=c>a?c:a;i=u.version||c}}var f={request:function(n,t){return e.Deferred(function(e){try{var r="function"==typeof n?n(t):n;r.onsuccess=function(n){e.resolveWith(r,[r.result,n])},r.onerror=function(n){e.rejectWith(r,[r.error,n])},r.onblocked!==undefined&&null===r.onblocked&&(r.onblocked=function(n){var t;try{t=r.result}catch(n){t=null}e.notifyWith(r,[t,n])}),r.onupgradeneeded!==undefined&&null===r.onupgradeneeded&&(r.onupgradeneeded=function(n){e.notifyWith(r,[r.result,n])})}catch(o){o.name="exception",e.rejectWith(r,["exception",o])}})},transaction:function(e){return{objectStore:function(n){try{return f.objectStore(e.objectStore(n))}catch(t){return e.readyState!==e.DONE&&e.abort(),f.objectStore(null)}},createObjectStore:function(n,t){try{return f.objectStore(e.db.createObjectStore(n,t))}catch(r){e.readyState!==e.DONE&&e.abort()}},deleteObjectStore:function(n){try{e.db.deleteObjectStore(n)}catch(t){e.readyState!==e.DONE&&e.abort()}},abort:function(){e.abort()}}},objectStore:function(e){for(var n={},t=["add","put","get","delete","clear","count"],r=0;t.length>r;r++)n[t[r]]=function(n){return function(){return f.request(function(t){return e[n].apply(e,t)},arguments)}}(t[r]);return n.each=function(n,t,r){return f.cursor(function(){return r?e.openCursor(f.range(t),r):e.openCursor(f.range(t))},n)},n.index=function(n){return f.index(function(){return e.index(n)})},n.createIndex=function(n,t,r){return 2===arguments.length&&"string"==typeof t&&(r=arguments[1],t=null),r||(r=n),f.index(function(){return e.createIndex(r,n,t)})},n.deleteIndex=function(n){return e.deleteIndex(n)},n},range:function(n){return e.isArray(n)?1===n.length?r.only(n[0]):r.bound(n[0],n[1],n[2]===undefined?!1:n[2],n[3]===undefined?!1:n[3]):n===undefined?null:n},cursor:function(n,t){return e.Deferred(function(e){try{var r="function"==typeof n?n():n;r.onsuccess=function(n){if(!r.result)return e.resolveWith(r,[null,n]),undefined;var o={"delete":function(){return f.request(function(){return r.result["delete"]()})},update:function(e){return f.request(function(){return r.result.update(e)})},next:function(e){this.data=e},key:r.result.key,value:r.result.value};e.notifyWith(r,[o,n]);var u=t.apply(r,[o]);try{u===!1?e.resolveWith(r,[null,n]):"number"==typeof u?r.result.advance.apply(r.result,[u]):o.data?r.result["continue"].apply(r.result,[o.data]):r.result["continue"]()}catch(n){e.rejectWith(r,[r.result,n])}},r.onerror=function(n){e.rejectWith(r,[r.result,n])}}catch(o){o.type="exception",e.rejectWith(r,[null,o])}})},index:function(e){try{var n="function"==typeof e?e():e}catch(t){n=null}return{each:function(e,t,r){return f.cursor(function(){return r?n.openCursor(f.range(t),r):n.openCursor(f.range(t))},e)},eachKey:function(e,t,r){return f.cursor(function(){return r?n.openKeyCursor(f.range(t),r):n.openKeyCursor(f.range(t))},e)},get:function(e){return"function"==typeof n.get?f.request(n.get(e)):n.openCursor(f.range(e))},count:function(){if("function"==typeof n.count)return f.request(n.count());throw"Count not implemented for cursors"},getKey:function(e){return"function"==typeof n.getKey?f.request(n.getKey(e)):n.openKeyCursor(f.range(e))}}}},s=f.request(function(){return i?t.open(o,parseInt(i)):t.open(o)});return s.then(function(e){e.onversionchange=function(){u&&u.onversionchange&&u.onversionchange()!==!1||e.close()}},function(){},function(e,n){if(n&&"upgradeneeded"===n.type){if(u&&u.schema)for(var t=n.oldVersion+1;n.newVersion>=t;t++)"function"==typeof u.schema[t]&&u.schema[t].call(this,f.transaction(this.transaction));u&&"function"==typeof u.upgrade&&u.upgrade.call(this,f.transaction(this.transaction))}}),e.extend(s,{cmp:function(e,n){return t.cmp(e,n)},deleteDatabase:function(){return e.Deferred(function(e){s.then(function(n){n.close(),f.request(function(){return t.deleteDatabase(o)}).then(function(n,t){e.resolveWith(this,[n,t])},function(n,t){e.rejectWith(this,[n,t])},function(n,t){e.notifyWith(this,[n,t])})},function(n,t){e.rejectWith(this,[n,t])},function(n,t){e.notifyWith(this,[n,t])})})},transaction:function(t,r){return!e.isArray(t)&&(t=[t]),r=n(r),e.Deferred(function(e){s.then(function(n,o){var u;try{u=n.transaction(t,r),u.onabort=u.onerror=function(n){e.rejectWith(u,[n])},u.oncomplete=function(n){e.resolveWith(u,[n])}}catch(o){return o.type="exception",e.rejectWith(this,[o]),undefined}try{e.notifyWith(u,[f.transaction(u)])}catch(o){o.type="exception",e.rejectWith(this,[o])}},function(n,t){e.rejectWith(this,[t,n])},function(){})})},objectStore:function(r,i){function c(c){return e.Deferred(function(e){function a(n,t){try{t(n.objectStore(r)).then(function(n,t){e.resolveWith(this,[n,t])},function(n,t){e.rejectWith(this,[n,t])})}catch(o){o.name="exception",e.rejectWith(n,[o,o])}}h.transaction(r,n(i)).then(function(){},function(d,l){if(d.code!==d.NOT_FOUND_ERR||i!==!0&&"object"!=typeof i)e.rejectWith(this,[d,l]);else{var p=this.result;p.close(),s=f.request(function(){return t.open(o,(parseInt(p.version,10)||1)+1)}),s.then(function(t){t.onversionchange=function(){u&&u.onversionchange&&u.onversionchange()!==!1||t.close()},h.transaction(r,n(i)).then(function(){},function(n,t){e.rejectWith(this,[n,t])},function(e){a(e,c)})},function(n,t){e.rejectWith(this,[n,t])},function(n,t){if("upgradeneeded"===t.type)try{n.createObjectStore(r,i===!0?{autoIncrement:!0}:i)}catch(o){e.rejectWith(this,[o,t])}})}},function(e){a(e,c)})})}function a(e,n){return c(function(t){return t[e].apply(t,n)})}function d(e,n,t){return c(function(r){var o=r.index(n);return o[e].apply(o[e],t)})}for(var h=this,l={},p=["add","delete","get","put","clear","count","each"],y=0;p.length>y;y++)l[p[y]]=function(e){return function(){return a(e,arguments)}}(p[y]);return l.index=function(e){return{each:function(n,t,r){return d("each",e,[n,t,r])},eachKey:function(n,t,r){return d("eachKey",e,[n,t,r])},get:function(n){return d("get",e,[n])},count:function(){return d("count",e,[])},getKey:function(n){return d("getKey",e,[n])}}},l}})}}),e.indexedDB.IDBCursor=o,e.indexedDB.IDBTransaction=u,e.idb=e.indexedDB})(jQuery); 3 | //@ sourceMappingURL=http://nparashuram.com/jquery-indexeddb/dist/jquery-indexeddb.min.map -------------------------------------------------------------------------------- /dist/jquery.indexeddb.min.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"dist/jquery.indexeddb.min.js","sources":["dist/jquery.indexeddb.js"],"names":["$","getDefaultTransaction","mode","result","IDBTransaction","READ_WRITE","indexedDB","window","mozIndexedDB","webkitIndexedDB","msIndexedDB","IDBKeyRange","webkitIDBKeyRange","IDBCursor","webkitIDBCursor","PREV","NEXT","webkitIDBTransaction","extend","dbName","config","version","schema","max","key","wrap","request","req","args","Deferred","dfd","idbRequest","onsuccess","e","resolveWith","onerror","rejectWith","error","onblocked","res","notifyWith","onupgradeneeded","name","transaction","idbTransaction","objectStore","storeName","readyState","DONE","abort","createObjectStore","storeParams","db","deleteObjectStore","idbObjectStore","crudOps","i","length","op","apply","arguments","each","callback","range","direction","cursor","openCursor","index","createIndex","prop","options","indexName","deleteIndex","r","isArray","only","bound","idbCursor","cursorReq","elem","delete","update","data","next","this","value","type","idbIndex","eachKey","openKeyCursor","get","count","getKey","dbPromise","open","parseInt","then","onversionchange","close","oldVersion","newVersion","call","upgrade","cmp","key1","key2","deleteDatabase","storeNames","onabort","oncomplete","err","onTransactionProgress","trans","me","code","NOT_FOUND_ERR","autoIncrement","ex","crudOp","opName","wrappedObjectStore","indexOp","crud","idb","jQuery"],"mappings":"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","sourceRoot":"http://nparashuram.com/jquery-indexeddb/"} -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | Jquery-IndexedDB API Reference 2 | =============================== 3 | 4 | List of APIs - Quick Reference 5 | 6 | * [$.indexedDB()](#openDatabase) 7 | * [$.indexedDB(, schema)](#openDatabaseUpgrade) 8 | * [$.indexedDB().transaction()](#transactionSec) 9 | * [$.indexedDB().objectStore()](#objectStore) 10 | * [$.indexedDB().objectStore().add()](#crud) 11 | * [$.indexedDB().objectStore().each()](#cursorEach) 12 | * [$.indexedDB().objectStore().index()](#index) 13 | * [$.indexedDB().objectStore().deleteDatabase()](#deleteDatabase) 14 | 15 | IndexedDB - Quick Intro 16 | ------------------------- 17 | * [IndexedDB](http://www.w3.org/TR/IndexedDB/) is a database inside a browser to save and retrieve objects on the browser/client. 18 | * IndexedDB contains multiple `objectStores` (sort of tables) that contain data. 19 | * `objectStores` have data records in form of (key, javascript object) pairs. (key) uniquely identify records 20 | * Data can be written to, read from, or iterated over, inside and `object store` 21 | * (key, javascript object) pairs can also have indexes on a property in the (javascript object) 22 | * Data operations can be scoped into transactions. 23 | 24 | Open Database 25 | ---------------------------------------- 26 | Creates a new connection to the _Indexed Database_ specified as the name. 27 | 28 | ``` javascript 29 | var dbOpenPromise = $.indexedDB("database_name", { 30 | // The second parameter is optional 31 | "version" : 3, // Integer version that the DB should be opened with 32 | "upgrade" : function(transaction){ 33 | // Function called when DB is of lower version that specified in the version parameter 34 | // See transaction for details on the argument 35 | }, 36 | "schema" : { 37 | "1" : function(transaction){ 38 | // List of instructions to run when DB version is upgraded to 1. 39 | // See transaction for details on the argument 40 | }, 41 | "2" : function(transaction){ 42 | // Examples of using the transaction object 43 | var obj1 = transaction.objectStore("store1"); 44 | var obj2 = transaction.createObjectStore(store2); 45 | obj1.createIndex("index"); 46 | } 47 | } 48 | }); 49 | ``` 50 | 51 | 52 | 53 | When a `schema` parameter is specified, and if the DB is originally at version `1`, then only functions labeled `2` and above are executed. 54 | This provides a way to migrate database by creating object stores, indexes or even reading/writing data between database versions. 55 | For details on the `transaction` parameter, looks at the [Transaction](#trans) object returned during a transaction. 56 | 57 | Note that the `createObjectStore` 58 | and the `deleteObjectStore` are available only during the upgrade operations. 59 | Also, `objectStores` opened during progress can have `index` operations. See the [indexes](#index) section for more details 60 | 61 | If the optional parameter is not specified, it simply opens the database to the latest version available. 62 | 63 | It returns a [Jquery Promise]("http://api.jquery.com/category/deferred-object/") with the following 64 | [done](#dbOpenDone), [progress](#dbOpenProgress) and [fail](#dbOpenFail) event handlers. 65 | 66 | ### Handling Database open success 67 | 68 | ``` javascript 69 | dbOpenPromise.done(function(db, event){ 70 | // Called when the database is successfully opened. 71 | db; // the native IndexedDB Database object (aka result of the IDBRequest) , 72 | event; // the IndexedDB Event object. 73 | this; // Context inside the function is the native IDBRequest. 74 | }); 75 | ``` 76 | 77 | ### When the database open is in progress 78 | When that database is being upgraded or is blocked due to another transaction being in progress 79 | 80 | ``` javascript 81 | dbOpenPromise.progress(function(db, event){ 82 | // Called when there is the database open is in progress - when it is blocked or is being upgraded 83 | error; // is the error object and has more details on what the error is 84 | event; // is the IndexedDB Event object 85 | event.type; //indicates - blocked or upgrade 86 | this; // Context inside the function is the native IDBRequest 87 | }); 88 | ``` 89 | When a database open operation is blocked, you can choose to close the database. 90 | When the database is being upgraded, you can perform all actions permitted in the versionChange transaction. 91 | 92 | ### Handling database open error 93 | 94 | ``` javascript 95 | dbOpenPromise.fail(function(error, event){ 96 | // Called when there is an error opening the database. 97 | error; //is the error object and has more details on what the error is 98 | event; //is the IndexedDB Event object. event.type indicates the type - 'error' or 'exception' 99 | this; // Context inside the function is the native IDBRequest 100 | }); 101 | ``` 102 | Transactions 103 | -------------------------- 104 | Once a database is opened, read and write transactions can be performed within a transaction. 105 | 106 | ```javascript 107 | var transactionPromise = $.indexedDB("dbName").transaction(storeNames, mode); 108 | ``` 109 | 110 | `storeNames` is an array of object store names (or a single string object store name) that should be under this transaction. 111 | 112 | 113 | 114 | `mode` is 0 or 1 indication *READ_ONLY* or *READ_WRITE*, similar to [modes](http://www.w3.org/TR/IndexedDB/#dfn-mode) in the IndexedDB Specification. 115 | 116 | The returned transaction promise has the following event handlers during the lifetime of the transaction. 117 | 118 | ### Transaction in progress 119 | 120 | ``` javascript 121 | transactionPromise.progress(function(trans){ 122 | // Called when the transaction has started 123 | trans; // Use the methods on the trans object (see below) to perform operations 124 | }); 125 | ``` 126 | 127 | The transaction object passed to the callback when transaction is in progress has the following methods on it. 128 | 129 | ``` javascript 130 | var objectStore = trans.objectStore("objectStoreName"); 131 | ``` 132 | 133 | The following 2 methods are available only when the database is being upgraded. Look at the [Open Database Schema](#openDatabaseUpgrade) section for details. 134 | 135 | ``` javascript 136 | var objectStore = trans.createObjectStore("objectStoreName", { 137 | // Options to create object store. 138 | "autoIncrement" : true, // [detaults to true], indicating that the key for this object store should auto increment, 139 | "keyPath" : id // the path of key in the object, defaults to key that has to be specified separately 140 | }); 141 | 142 | trans.deleteObjectStore(objectStoreName); 143 | ``` 144 | See [object stores](#objectStore) for methods on the `objectStore` returned by the above calls. 145 | 146 | ### Transaction complete 147 | 148 | ``` javascript 149 | transactionPromise.done(function(event){ 150 | // Called when transaction is completed 151 | event; // Indicated the transaction complete event. 152 | }); 153 | ``` 154 | 155 | ### Transaction fails or is aborted 156 | 157 | ``` javascript 158 | transactionPromise.fail(function(event){ 159 | // Called when the transaction is aborted or fails 160 | event.type; // indicates the reason for failure being error, exception or abort 161 | }); 162 | ``` 163 | 164 | Object Stores 165 | ---------------------------------------- 166 | Once the database is opened, operations need to be performed on object stores. 167 | An object store can be opened inside a [transaction that is in progress](#transInProgress) or using a shorthand on the `$.indexedDB` object like. 168 | 169 | ``` javascript 170 | var objectStore = $.indexedDB("database_name").objectStore("objectStoreName", /* Optional */ mode ); 171 | ``` 172 | 173 | The `mode` parameter defaults to READ_WRITE and is similar to the `mode` parameter specified during a `transaction`. 174 | 175 | As a convenience method, if the `mode` is set to `true` (instead of 0 or 1), an object store is created if it does not exist. Internally, the database is closed and opened with a higher version to trigger the version transaction where the object store can be created. 176 | 177 | 178 | 179 | The above expression internally creates a transaction for this object store. The `mode` parameter is optional and similar to the [mode parameter](#transactionMode) in transactions. 180 | The CRUD methods on the object store are 181 | 182 | ```javascript 183 | var promise = objectStore.add(/*Javascript Object*/ value, /*Optional*/ key); // Adds data to the objectStore 184 | var promise = objectStore.get(key); Gets the object with the key 185 | var promise = objectStore.put(/*Javascript Object*/ value, key); // Updates the object for the specified key 186 | var promise = objectStore.delete(key); // Deletes the object with the specified key 187 | var promise = objectStore.count(); // Gets all the objects 188 | var promise = objectStore.clear(); // Removes all data from the object store; 189 | ``` 190 | 191 | The returned promise can be used to note the success or error of the operations 192 | 193 | ``` javascript 194 | promise.done(function(result, event){ 195 | result; // Result of the operation. Some operations like delete will return undefined 196 | event; // Success Event 197 | }); 198 | 199 | promise.fail(function(error, event){ 200 | error; // Type of error that has occured 201 | event; // Error event 202 | event.type; // indicates if there was an error or an exception 203 | }); 204 | ``` 205 | 206 | 207 | To iterate over the objects inside an object store, use 208 | 209 | ``` javascript 210 | var iterationPromise = objectStore.each(function(item){ 211 | // Called for each element during the iteration 212 | item.key, item.value; // The key and the value of current object 213 | item.delete(); // Deletes the current item 214 | item.update(newItem); // Updates the current item with newItem; 215 | 216 | return; 217 | // false - do not continue iteration 218 | // integer 'n' - n can be 1,2,... indicating skip next n objects and continue 219 | // object - continue to item with object as the next key 220 | // default - no return, or undefined return, continue iteration 221 | 222 | }, /*Optional*/ range, /*Optional*/ direction); 223 | 224 | iterationPromise.done(function(result, event){ 225 | // Iteration completed 226 | result ; // null, indicating that there are no more objects for iteration 227 | event ; // Success event 228 | }) 229 | 230 | iterationPromise.fail(function(error, event){ 231 | error; // Error during iteration 232 | event; // Error event, can be exception or event 233 | }); 234 | 235 | ``` 236 | `range` limits the results and can be an array like `[lower, upper, lowerOpen, upperOpen]`. 237 | If only one element is specified in the array, it becomes an equals clause. The parameters `lowerOpen` and `upperOpen` are optional. If no value is specified for either upper or for lower bounds, they are included. These arguments behave as described in the [specification](http://www.w3.org/TR/IndexedDB/#widl-IDBKeyRange-lowerOpen) 238 | 239 | In addition to the above CURD operation, `objectStore.index` methods also allow operations using indexes. See [indexes](#index) for details. 240 | 241 | Indexes 242 | --------------------------- 243 | Once a reference to an objectStore is obtained either using `transaction.objectStore()`, `transaction.createObjectStore()` or `$.indexedDB("").objectStore()`, 244 | the index object can be used. 245 | 246 | ``` javascript 247 | var index = objectStore.index("indexName"); 248 | index.each(function(item){ 249 | // Iterate over objects in index 250 | // Similar to iterating over objects in an ObjectStore 251 | item; // same as iterating over objects (see above) 252 | }, /*Optional*/ range, /*Optional */ direction); 253 | 254 | index.eachKey(function(item){ 255 | // Iterate over the keys of the object 256 | }); 257 | ``` 258 | 259 | While upgrading a database in the [version change transaction](openDatabaseUpgrade), indexes can also be created or deleted on an object store. 260 | 261 | ```javascript 262 | // trans is created when a database upgrade is in progress 263 | var objectStore = trans.objectStore("objectStoreName"); 264 | var index = objectStore.createIndex("object.property" , /*Optional*/ { 265 | "unique" : false, // Uniqueness of Index, defaults to false 266 | "multiEntry" : false // see explanation below 267 | }, /* Optional */ "indexName") 268 | objectStore.deleteIndex("indexName"); //returns nothing 269 | 270 | ``` 271 | If the second argument to the `createIndex` function is a string, it is considered to be the name of the index. If no indexName is specified in `createIndex`, the property name is used as indexName 272 | 273 | This multiEntry flag affects how the index behaves when the result of evaluating the index's key path yields an Array. 274 | 275 | * If the multiEntry flag is false, then a single record whose key is an Array is added to the index. 276 | * If the multiEntry flag is true, then the one record is added to the index for each item in the Array. The key for each record is the value of respective item in the Array. 277 | 278 | Delete Database 279 | ------------------------------- 280 | A database can be deleted using 281 | 282 | ``` javascript 283 | var deletePromise = $.indexedDB("database_name").deleteDatabase(); 284 | 285 | deletePromise.done(function(null, event){ 286 | /* Called when the delete is successful*/ 287 | event; // The success event 288 | }); 289 | deletePromise.fail(function(error, event){ 290 | /* Called when the delete is successful*/ 291 | error; // Reason for the error 292 | }); 293 | deletePromise.progress(function(db, event){ 294 | // Called when the deleting is blocked due to another transaction 295 | db; // Database that is opened 296 | event.type // Indicates it is blocked, etc. 297 | }); 298 | ``` 299 | 300 | Compare Keys 301 | --------------------- 302 | A convenience method to compare keys in a database. Can be used as `$.indexedDB("dbName").cmp(key1, key2)` and return 1,0 or -1 when key1 is greater than, equal to or less than key2 respectively. 303 | 304 | Links 305 | ------- 306 | Some useful links 307 | 308 | * [IndexedDB W3C Specification](http://www.w3.org/TR/IndexedDB/) 309 | * [IndexedDB API playground and examples](http://nparashuram.com/IndexedDB) 310 | * [My work on IndexedDB](http://blog.nparashuram.com/search/label/indexeddb) 311 | -------------------------------------------------------------------------------- /example/catalog.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "itemId": 1001, 3 | "name": "Desktop", 4 | "description": "A normal looking desktop computer", 5 | "price": 100, 6 | 7 | "rating": 2 8 | 9 | }, { 10 | "itemId": 1002, 11 | "name": "Laptop", 12 | "description": "A laptop computer", 13 | "price": 200, 14 | 15 | "rating": 4 16 | }, { 17 | "itemId": 1003, 18 | "name": "Tablet", 19 | "description": "A tablet computer", 20 | "price": 150, 21 | "stock": 4 22 | }, { 23 | "itemId": 1004, 24 | "name": "Embedded", 25 | "description": "Whatever", 26 | "stock": 4 27 | }, { 28 | "itemId": 1005, 29 | "name": "EInk Reader", 30 | "description": "Like a tablet, but different screen", 31 | "price": 150, 32 | "stock": 4 33 | }, { 34 | "itemId": 1006, 35 | "name": "Phone", 36 | "description": "No more a computer", 37 | "price": 250, 38 | "stock": 4 39 | }, { 40 | "itemId": 1007, 41 | "name": "Music players", 42 | "description": "the outer imits ? ", 43 | "price": 10, 44 | "stock": 4 45 | }] 46 | -------------------------------------------------------------------------------- /example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | A self contained example for the Jquery-IndexedDB plugin 5 | 7 | 9 | 11 | 12 | 13 | 14 | 15 |
16 |

My Web Store 17 | 21 |

22 |
23 |
24 |
25 |
26 |

Catalog

27 | Load from Local DB 28 | Sync to DB 29 |
30 | 31 | 32 | 35 | 38 | 45 | 46 |
33 | Id 34 | 36 | Actions 37 | 39 | Object 40 |
41 | Show all 42 | Sort by Price 43 |
44 |
47 |
48 |
49 |
50 |

Cart

51 | Load from Local DB 52 | Clear Table 53 |
54 | 55 | 56 | 59 | 62 | 69 | 70 |
57 | Id 58 | 60 | Actions 61 | 63 | Object 64 |
65 | Show all 66 | Sort by ItemId 67 |
68 |
71 |
72 |
73 |
74 |

wishlist

75 | Load from Local DB 76 | Clear Table 77 |
78 | 79 | 80 | 83 | 86 | 89 | 90 |
81 | Id 82 | 84 | Actions 85 | 87 | Object 88 |
91 |
92 |
93 |
94 |
95 | 96 | 227 | 228 | 229 | 287 | 304 | 305 | 306 | -------------------------------------------------------------------------------- /example/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-size: 14px; 3 | color: #333; 4 | font-family: Helvetica, Arial, sans-serif; 5 | background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAD0AAAA1CAIAAAB9U3TTAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYxIDY0LjE0MDk0OSwgMjAxMC8xMi8wNy0xMDo1NzowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NjIxQjBCQkNGMTcwMTFFMEFBREJENzE1MkFDM0ExNTUiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NjIxQjBCQkJGMTcwMTFFMEFBREJENzE1MkFDM0ExNTUiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNS4xIE1hY2ludG9zaCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjBDN0U3RDQ4RjE0NjExRTBBQURCRDcxNTJBQzNBMTU1IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjBDN0U3RDQ5RjE0NjExRTBBQURCRDcxNTJBQzNBMTU1Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+/qHfvQAACWBJREFUaN512mlW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") repeat scroll 0 0 transparent; 6 | } 7 | 8 | a { 9 | text-decoration: none; 10 | } 11 | 12 | h1, h2 { 13 | text-align: center; 14 | } 15 | 16 | .table-container { 17 | background: #ddd; 18 | -webkit-border-radius: 5px; 19 | -moz-border-radius: 5px; 20 | -ms-border-radius: 5px; 21 | -o-border-radius: 5px; 22 | border-radius: 5px; 23 | -webkit-box-shadow: inset 0 2px 2px #999; 24 | -moz-box-shadow: inset 0 2px 2px #999; 25 | box-shadow: inset 0 2px 2px #999; 26 | display: block; 27 | text-align: center; 28 | margin: 2% 1%; 29 | padding: 2%; 30 | } 31 | 32 | .controls { 33 | } 34 | 35 | .controls h2 { 36 | text-align: center; 37 | font-size: 1.1em; 38 | float: left 39 | } 40 | 41 | .controls a { 42 | display: block; 43 | float: right; 44 | margin: 2px; 45 | } 46 | 47 | table { 48 | clear: both; 49 | width: 100%; 50 | } 51 | 52 | table td, th { 53 | border: dotted 1px #777; 54 | padding: 3px; 55 | height: 2.5em; 56 | background-color: #F5F5F5; 57 | } 58 | 59 | table th { 60 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #eeeeee), color-stop(100%, #cccccc)); 61 | background-image: -webkit-linear-gradient(top, #eeeeee, #cccccc); 62 | background-image: -moz-linear-gradient(top, #eeeeee, #cccccc); 63 | background-image: -ms-linear-gradient(top, #eeeeee, #cccccc); 64 | background-image: -o-linear-gradient(top, #eeeeee, #cccccc); 65 | background-image: linear-gradient(top, #eeeeee, #cccccc); 66 | } 67 | 68 | table td.key, th.key { 69 | width: 10%; 70 | text-align: center; 71 | } 72 | 73 | table td.value { 74 | text-align: left; 75 | } 76 | 77 | table td.value div.keyval{ 78 | display: inline-block; 79 | padding : 5px; 80 | } 81 | 82 | @media screen and (max-width: 500px) { 83 | table td.value div.keyval{ 84 | display: block; 85 | padding : 1px; 86 | width: 100%; 87 | text-overflow : ellipsis; 88 | } 89 | } 90 | 91 | table td.value div.keyval:after{ 92 | content: ","; 93 | } 94 | 95 | table td.value div.keyval span.key{ 96 | color: #333; 97 | } 98 | 99 | table td.value div.keyval span.key:after{ 100 | content : ":"; 101 | } 102 | 103 | table td.value div.keyval span.value{ 104 | color: #777; 105 | } 106 | 107 | 108 | table td.action { 109 | width: 20%; 110 | min-width: 100px; 111 | } 112 | 113 | @media screen and (max-width: 1098px) { 114 | table td.action a{ 115 | display: block; 116 | width: 80px; 117 | margin: 5px auto; 118 | padding-top: 10px; 119 | padding-bottom: 10px; 120 | } 121 | } 122 | 123 | 124 | #console { 125 | clear: both; 126 | } 127 | .controls a{ 128 | background-color: #a5b8da; 129 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #a5b8da), color-stop(100%, #7089b3)); 130 | background-image: -webkit-linear-gradient(top, #a5b8da, #7089b3); 131 | background-image: -moz-linear-gradient(top, #a5b8da, #7089b3); 132 | background-image: -ms-linear-gradient(top, #a5b8da, #7089b3); 133 | background-image: -o-linear-gradient(top, #a5b8da, #7089b3); 134 | background-image: linear-gradient(top, #a5b8da, #7089b3); 135 | border-top: 1px solid #758fba; 136 | border-right: 1px solid #6c84ab; 137 | border-bottom: 1px solid #5c6f91; 138 | border-left: 1px solid #6c84ab; 139 | -webkit-box-shadow: inset 0 1px 0 0 #aec3e5; 140 | -moz-box-shadow: inset 0 1px 0 0 #aec3e5; 141 | box-shadow: inset 0 1px 0 0 #aec3e5; 142 | color: #fff; 143 | font: bold 11px "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", Geneva, Verdana, sans-serif; 144 | line-height: 1; 145 | padding: 8px 10px; 146 | text-align: center; 147 | text-shadow: 0 -1px 1px #64799e; 148 | text-transform: uppercase; 149 | width: 150px; 150 | } 151 | 152 | .controls a:hover { 153 | background-color: #9badcc; 154 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #9badcc), color-stop(100%, #687fa6)); 155 | background-image: -webkit-linear-gradient(top, #9badcc, #687fa6); 156 | background-image: -moz-linear-gradient(top, #9badcc, #687fa6); 157 | background-image: -ms-linear-gradient(top, #9badcc, #687fa6); 158 | background-image: -o-linear-gradient(top, #9badcc, #687fa6); 159 | background-image: linear-gradient(top, #9badcc, #687fa6); 160 | border-top: 1px solid #6d86ad; 161 | border-right: 1px solid #647a9e; 162 | border-bottom: 1px solid #546685; 163 | border-left: 1px solid #647a9e; 164 | -webkit-box-shadow: inset 0 1px 0 0 #a5b9d9; 165 | -moz-box-shadow: inset 0 1px 0 0 #a5b9d9; 166 | box-shadow: inset 0 1px 0 0 #a5b9d9; 167 | cursor: pointer; 168 | } 169 | .controls a:active { 170 | border: 1px solid #546685; 171 | -webkit-box-shadow: inset 0 0 8px 2px #7e8da6, 0 1px 0 0 #eeeeee; 172 | -moz-box-shadow: inset 0 0 8px 2px #7e8da6, 0 1px 0 0 #eeeeee; 173 | box-shadow: inset 0 0 8px 2px #7e8da6, 0 1px 0 0 #eeeeee; 174 | } 175 | 176 | .action a { 177 | vertical-align: middle; 178 | margin: 0 1%; 179 | line-height: 15px; 180 | } 181 | 182 | .action a{ 183 | background-color: #52a8e8; 184 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #52a8e8), color-stop(100%, #377ad0)); 185 | background-image: -webkit-linear-gradient(top, #52a8e8, #377ad0); 186 | background-image: -moz-linear-gradient(top, #52a8e8, #377ad0); 187 | background-image: -ms-linear-gradient(top, #52a8e8, #377ad0); 188 | background-image: -o-linear-gradient(top, #52a8e8, #377ad0); 189 | background-image: linear-gradient(top, #52a8e8, #377ad0); 190 | border-top: 1px solid #4081af; 191 | border-right: 1px solid #2e69a3; 192 | border-bottom: 1px solid #20559a; 193 | border-left: 1px solid #2e69a3; 194 | -webkit-border-radius: 16px; 195 | -moz-border-radius: 16px; 196 | -ms-border-radius: 16px; 197 | -o-border-radius: 16px; 198 | border-radius: 16px; 199 | -webkit-box-shadow: inset 0 1px 0 0 #72b9eb, 0 1px 2px 0 #b3b3b3; 200 | -moz-box-shadow: inset 0 1px 0 0 #72b9eb, 0 1px 2px 0 #b3b3b3; 201 | box-shadow: inset 0 1px 0 0 #72b9eb, 0 1px 2px 0 #b3b3b3; 202 | color: #fff; 203 | font: normal 11px "lucida grande", sans-serif; 204 | line-height: 1; 205 | padding: 3px 10px; 206 | text-align: center; 207 | text-shadow: 0 -1px 1px #3275bc; 208 | width: 112px; 209 | -webkit-background-clip: padding-box; 210 | } 211 | 212 | .action a.:hover { 213 | background-color: #3e9ee5; 214 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #3e9ee5), color-stop(100%, #206bcb)); 215 | background-image: -webkit-linear-gradient(top, #3e9ee5 0%, #206bcb 100%); 216 | background-image: -moz-linear-gradient(top, #3e9ee5 0%, #206bcb 100%); 217 | background-image: -ms-linear-gradient(top, #3e9ee5 0%, #206bcb 100%); 218 | background-image: -o-linear-gradient(top, #3e9ee5 0%, #206bcb 100%); 219 | background-image: linear-gradient(top, #3e9ee5 0%, #206bcb 100%); 220 | border-top: 1px solid #2a73a6; 221 | border-right: 1px solid #165899; 222 | border-bottom: 1px solid #07428f; 223 | border-left: 1px solid #165899; 224 | -webkit-box-shadow: inset 0 1px 0 0 #62b1e9; 225 | -moz-box-shadow: inset 0 1px 0 0 #62b1e9; 226 | box-shadow: inset 0 1px 0 0 #62b1e9; 227 | cursor: pointer; 228 | text-shadow: 0 -1px 1px #1d62ab; 229 | -webkit-background-clip: padding-box; 230 | } 231 | 232 | .action a:active { 233 | background: #3282d3; 234 | border: 1px solid #154c8c; 235 | border-bottom: 1px solid #0e408e; 236 | -webkit-box-shadow: inset 0 0 6px 3px #1657b5, 0 1px 0 0 white; 237 | -moz-box-shadow: inset 0 0 6px 3px #1657b5, 0 1px 0 0 white; 238 | box-shadow: inset 0 0 6px 3px #1657b5, 0 1px 0 0 white; 239 | text-shadow: 0 -1px 1px #2361a4; 240 | -webkit-background-clip: padding-box; 241 | } 242 | 243 | .action a::-moz-focus-inner { 244 | border: 0; 245 | padding: 0; 246 | } 247 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Jquery IndexedDB Plugin 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 |

JQuery IndexedDB Plugin

13 |
14 | 17 |

18 | IndexedDB 19 | is a database inside a browser to save and retrieve objects on the browser/client. 20 |

21 |

22 | The Jquery IndexedDB Plugin is a wrapper over IndexedDB, making it easier to use with Jquery. 23 |

32 |

33 |

34 | IndexedBD is not supported on 35 | all browsers 36 | . You can use the 37 | IndexedDB polyfill 38 | over WebSQL to get the plugin working on Opera, Safari, iPad, iPhone, etc. 39 |

40 |

Code

41 |

Typical code with the plugin looks something like this

42 | 43 | $.indexedDB("databaseName").objectStore("objectStoreName").add(data).then(); 44 | 45 |

46 | Note how transactions are automatic, object stores are implicitly created and the syntax is far less verbose. 47 |

48 |

Compare IndexedDB API and jQuery Plugin code

49 |

50 | Compare the code written using the IndexedDB API vs the jQuery Plugin. 51 |
52 | Check out the differences. 53 |

54 |
55 |
56 |
57 |

API Documentation

58 |

59 | Take a look at the API on 60 | Github Readme 61 | . 62 | You can also try out the entire API in the 63 | API Playground 64 | . 65 |

66 |
67 |

Example

68 | 76 |
77 | 78 |
79 |
80 |

Tests

81 |

82 | Try out the test suite on your browser to see the support 83 |
84 | Run the test suites now 85 |

86 |

Tested on Browsers

87 |
    88 |
  • Firefox 12.0 - 14.0a2 (2012-05-18)
  • 89 |
  • Chrome 18.0 - 21.0.1143.0 canary
  • 90 |
91 |
92 |
93 |
94 |
95 |

Try it Now!

96 |

 97 |                     
98 | 99 | 128 | 141 |
142 |

Other related links

143 | 154 |
155 |
156 | 172 | 173 | -------------------------------------------------------------------------------- /indexeddb.jquery.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "indexeddb", 3 | "title": "Jquery-IndexedDB", 4 | "description": "A Jquery Plugin for the IndexedDB API", 5 | "version": "0.1.0", 6 | "keywords": ["indexeddb", "offline", "storage"], 7 | "homepage": "http://nparashuram.com/jquery-indexeddb", 8 | "docs": "http://nparashuram.com/jquery-indexeddb", 9 | "demo": "http://nparashuram.com/jquery-indexeddb/example/index.html", 10 | "download": "http://nparashuram.com/jquery-indexeddb/dist/jquery.indexeddb.js", 11 | "bugs": "https://github.com/axemclion/jquery-indexeddb/issues", 12 | "author": { 13 | "name": "Parashuram", 14 | "email": "code@nparashuram.com", 15 | "url": "http://nparashuram.com/" 16 | }, 17 | "licenses": [{ 18 | "type": "GPLv2", 19 | "url": "http://www.example.com/licenses/gpl.html" 20 | }], 21 | "dependencies": { 22 | "jquery": ">=1.8.0" 23 | } 24 | } -------------------------------------------------------------------------------- /lib/demoer/demoer.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: Cambria, Times; 5 | } 6 | 7 | .sample { 8 | display: none; 9 | } 10 | 11 | .examples { 12 | margin: 0; 13 | padding: 1%; 14 | } 15 | 16 | .example-item { 17 | list-style: none; 18 | background-color: #FFFFFF; 19 | padding: 0.1em; 20 | margin: 0; 21 | cursor: pointer; 22 | } 23 | 24 | .example-item .snippet { 25 | display: none; 26 | width: 100%; 27 | border: 0; 28 | -webkit-transition: all 0.2s ease-in-out; 29 | -moz-transition: all 0.2s ease-in-out; 30 | -o-transition: all 0.2s ease-in-out; 31 | } 32 | 33 | .example-item .snippet .code { 34 | font-family: Monaco, Consolas, courier; 35 | font-size: 0.7em; 36 | background-color: #2A211C; 37 | color: #BDAE9D; 38 | width: 99%; 39 | height : 99%; 40 | padding : 0.5%; 41 | border: none; 42 | } 43 | 44 | .example-title { 45 | text-decoration: none; 46 | color: black; 47 | padding: 0.1em; 48 | display: block; 49 | height : 100%; 50 | line-height : 20px; 51 | } 52 | 53 | .example-item:hover { 54 | background-color: #FFFFCC; 55 | } 56 | 57 | .example-item:target{ 58 | margin-bottom : 1em; 59 | } 60 | 61 | .example-item:target .example-name { 62 | background-color: #DFECFF; 63 | height : 40px; 64 | vertical-align : middle; 65 | } 66 | 67 | .example-item:target .example-title { 68 | line-height : 40px; 69 | font-weight : bold; 70 | margin : 0 0em; 71 | } 72 | 73 | 74 | .example-item:target .code-linq, .example-item:target .code-full { 75 | height: 100px; 76 | display: block; 77 | padding: 0; 78 | margin: 0; 79 | } 80 | 81 | .example-item:target .code-full{ 82 | height : 200px; 83 | margin : 1em 0; 84 | } 85 | 86 | .example-more-code, .floating-button { 87 | display: none; 88 | font-size: 0.8em; 89 | padding: 0.2em 0.5em; 90 | margin: 0.6em 0.5em; 91 | float: right; 92 | vertical-align : middle; 93 | } 94 | 95 | .example-item:target .example-more-code, .example-item:target .floating-button { 96 | display: inline; 97 | } 98 | 99 | /* blue pill (inspired by iTunes) 100 | *******************************************************************************/ 101 | button.blue-pill { 102 | background-color: #a5b8da; 103 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #a5b8da), color-stop(100%, #7089b3)); 104 | background-image: -webkit-linear-gradient(top, #a5b8da, #7089b3); 105 | background-image: -moz-linear-gradient(top, #a5b8da, #7089b3); 106 | background-image: -ms-linear-gradient(top, #a5b8da, #7089b3); 107 | background-image: -o-linear-gradient(top, #a5b8da, #7089b3); 108 | background-image: linear-gradient(top, #a5b8da, #7089b3); 109 | border-top: 1px solid #758fba; 110 | border-right: 1px solid #6c84ab; 111 | border-bottom: 1px solid #5c6f91; 112 | border-left: 1px solid #6c84ab; 113 | -webkit-border-radius: 18px; 114 | -moz-border-radius: 18px; 115 | border-radius: 18px; 116 | -webkit-box-shadow: inset 0 1px 0 0 #aec3e5; 117 | -moz-box-shadow: inset 0 1px 0 0 #aec3e5; 118 | box-shadow: inset 0 1px 0 0 #aec3e5; 119 | color: #fff; 120 | font: bold 11px "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", Geneva, Verdana, sans-serif; 121 | line-height: 1; 122 | padding: 8px 0; 123 | text-align: center; 124 | text-shadow: 0 -1px 1px #64799e; 125 | text-transform: uppercase; 126 | width: 150px; } 127 | button.blue-pill:hover { 128 | background-color: #9badcc; 129 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #9badcc), color-stop(100%, #687fa6)); 130 | background-image: -webkit-linear-gradient(top, #9badcc, #687fa6); 131 | background-image: -moz-linear-gradient(top, #9badcc, #687fa6); 132 | background-image: -ms-linear-gradient(top, #9badcc, #687fa6); 133 | background-image: -o-linear-gradient(top, #9badcc, #687fa6); 134 | background-image: linear-gradient(top, #9badcc, #687fa6); 135 | border-top: 1px solid #6d86ad; 136 | border-right: 1px solid #647a9e; 137 | border-bottom: 1px solid #546685; 138 | border-left: 1px solid #647a9e; 139 | -webkit-box-shadow: inset 0 1px 0 0 #a5b9d9; 140 | -moz-box-shadow: inset 0 1px 0 0 #a5b9d9; 141 | box-shadow: inset 0 1px 0 0 #a5b9d9; 142 | cursor: pointer; } 143 | button.blue-pill:active { 144 | border: 1px solid #546685; 145 | -webkit-box-shadow: inset 0 0 8px 2px #7e8da6, 0 1px 0 0 #eeeeee; 146 | -moz-box-shadow: inset 0 0 8px 2px #7e8da6, 0 1px 0 0 #eeeeee; 147 | box-shadow: inset 0 0 8px 2px #7e8da6, 0 1px 0 0 #eeeeee; } -------------------------------------------------------------------------------- /lib/demoer/demoer.html: -------------------------------------------------------------------------------- 1 | 23 | -------------------------------------------------------------------------------- /lib/demoer/demoer.js: -------------------------------------------------------------------------------- 1 | var prettyCode = function(code){ 2 | if (typeof code === "function") { 3 | code = code.toString(); 4 | } 5 | code = code || ""; 6 | var start = "function (){"; 7 | code = code.substring(code.indexOf(start) + start.length + 2, code.length - 1).replace(/(^\s+|$\s+)/g, ""); 8 | return code; 9 | }; 10 | 11 | /** 12 | * Load all dependent URLs 13 | */ 14 | var getUrls = function(container, callback){ 15 | var base = $("script[src*=demoer\\.js]").attr("src"); 16 | base = base.substring(0, base.lastIndexOf("demoer.js")); 17 | 18 | // Loading Firebug 19 | var E = document.createElement('script'); 20 | E.setAttribute('id', "FirebugLite"); 21 | E.setAttribute('src', "https://getfirebug.com/firebug-lite.js#startOpened=true"); 22 | E.setAttribute("FirebugLite", "4"); 23 | document.getElementsByTagName('body')[0].appendChild(E); 24 | E.onload = function(){ 25 | Firebug.DOM.addListener("onHSplitterMouseMove", function(){ 26 | console.log("Resized"); 27 | }) 28 | } 29 | var count = 0; 30 | var timerHandle = window.setInterval(function(){ 31 | count++; 32 | if (count > 10) { 33 | window.clearInterval(timerHandle); 34 | } 35 | if (typeof firebug !== "undefined") { 36 | window.clearInterval(timerHandle); 37 | window.console = firebug; 38 | } 39 | }, 1000); 40 | 41 | // Loading beautify, and then the container 42 | $.getScript(base + "beautify.js", function(){ 43 | $(container).load(base + "demoer.html", function(){ 44 | callback(); 45 | }); 46 | }); 47 | 48 | $("head").append($("").attr({ 49 | "type": "text/css", 50 | "rel": "stylesheet", 51 | "href": base + "demoer.css" 52 | })); 53 | } 54 | 55 | // Start populating the samples 56 | var loadDemoes = function(container, exampleList){ 57 | getUrls(container, function(){ 58 | var exampleDiv = $(container).children("ul.examples:first"); 59 | var template = exampleDiv.children("li.sample:first"); 60 | var list = []; 61 | for (example in exampleList) { 62 | var exampleItem = template.clone(false).attr("id", example); 63 | var html = exampleItem.html().replace(/__EXAMPLE__/g, example); 64 | html = html.replace(/\s*__EXAMPLE_CODE__\s*/g, js_beautify(prettyCode(exampleList[example].code))); 65 | html = html.replace(/\s*__EXAMPLE_ALTERNATE__\s*/g, js_beautify(prettyCode(exampleList[example].alternate))); 66 | exampleItem.html(html); 67 | exampleDiv.append(exampleItem.removeClass("sample")); 68 | } 69 | }); 70 | 71 | var run = function(code, title){ 72 | console.group(title); 73 | eval(code); 74 | setTimeout(function(){ 75 | console.groupEnd(title); 76 | }, 1000); 77 | $(".more-code").hide(); 78 | } 79 | 80 | $(".example-more-code").live("click", function(e){ 81 | $(this).parent().nextAll(".code-full").toggle(); 82 | return false; 83 | }); 84 | 85 | $("button.code-run").live("click", function(e){ 86 | var root = $(this).parent().parent(); 87 | run(root.find(".code-linq").children(".code").text(), root.find("a.example-title").html()); 88 | }); 89 | 90 | $("#examples").css("overflow-y", "scroll"); 91 | function resize(){ 92 | var h = window.innerHeight - $("#FirebugUI").height() - $("h1").parent().height(); 93 | $("#examples").height(h); 94 | height = h; 95 | } 96 | }; 97 | -------------------------------------------------------------------------------- /lib/queuedUnit.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Ideally unit tests should be independent, but there are some cases where you really want those tests to be executed one after the other 3 | * Here is some code that does exactly that - a wrapper on top of QUnit. 4 | * 5 | * Usage 6 | * Instead of asyncTest, call queuedAsyncTest (same params) 7 | * Instead of module(), call queuedModule 8 | * After a test is over and the next test has to run, call nextTest() 9 | */ 10 | (function(window, console, undefined){ 11 | var testQueue = [], currentModule = null; 12 | 13 | // Filtering the tests based on the URLs 14 | var q = window.location.search.substring(1).split("&"); 15 | var filteredTests = []; 16 | for (var i = 0; i < q.length; i++) { 17 | var parts = q[i].split("="); 18 | if (parts[0] === "filter") { 19 | filteredTests.push(unescape(parts[1])); 20 | } 21 | } 22 | 23 | /** 24 | * Use this method instead of asyncTest. Once the test is finished, call nextTest(); 25 | * @param {Object} name 26 | * @param {Object} callback 27 | */ 28 | function queuedAsyncTest(name){ 29 | if (filteredTests.length === 0 || filteredTests.indexOf(currentModule + ": " + name) !== -1) { 30 | testQueue.push({ 31 | "name": name, 32 | "module": currentModule, 33 | "args": arguments 34 | }); 35 | } 36 | } 37 | 38 | /** 39 | * Use this in place of module(blah) 40 | * @param {Object} module 41 | */ 42 | function queuedModule(module){ 43 | currentModule = module; 44 | } 45 | 46 | if (typeof console.groupEnd !== "function"){ 47 | console.groupEnd = function(){ 48 | console.log("=================================="); 49 | } 50 | } 51 | 52 | if (typeof console.groupCollapsed !== "function"){ 53 | console.groupCollapsed = function(msg){ 54 | console.log.apply(console, arguments); 55 | } 56 | } 57 | 58 | /** 59 | * Once the current test is over, call nextTest() to start running the next test 60 | */ 61 | var timer = null; 62 | var testCount = 1; 63 | function nextTest(){ 64 | window.clearTimeout(timer); 65 | if (testQueue.length <= 0) { 66 | console.groupEnd(); 67 | console.log("All tests completed"); 68 | return; 69 | } 70 | var current = testQueue.splice(0, 1)[0]; 71 | console.groupEnd(); 72 | console.groupCollapsed("=========", testCount++, current.module, ":", current.name, "============"); 73 | module(current.module); 74 | // Expected asserts specified or not 75 | if (current.args.length === 2) { 76 | asyncTest(current.name, current.args[1]); 77 | } else if (current.args.length === 3) { 78 | asyncTest(current.name, current.args[1], current.args[2]); 79 | } 80 | } 81 | 82 | window.queuedAsyncTest = queuedAsyncTest; 83 | window.queuedModule = queuedModule; 84 | window.nextTest = nextTest; 85 | }(window, console)); 86 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery-indexeddb", 3 | "preferGlobal": "false", 4 | "version": "1.0.0", 5 | "author": "Parashuram ", 6 | "description": "A Jquery plugin for the IndexedDB API", 7 | "scripts": { 8 | "start": "grunt", 9 | "test": "grunt test" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git://github.com/axemclion/jquery-indexeddb.git" 14 | }, 15 | "bugs": { 16 | "url": "https://github.com/axemclion/jquery-indexeddb/issues" 17 | }, 18 | "main": "grunt.js", 19 | "keywords": [ 20 | "indexedDB", 21 | "database", 22 | "jquery"], 23 | "devDependencies": { 24 | "grunt": "~0.4.0", 25 | "grunt-saucelabs": "~4.0.0", 26 | "request": "~2.14.0", 27 | "grunt-contrib-uglify": "~0.1.1", 28 | "grunt-contrib-jshint": "~0.1.1", 29 | "grunt-contrib-watch": "~0.2.0", 30 | "grunt-contrib-connect": "~0.1.2", 31 | "grunt-contrib-concat": "~0.1.3", 32 | "grunt-contrib-copy": "~0.4.0", 33 | "grunt-groundskeeper": "~0.1.3", 34 | "grunt-contrib-clean": "~0.4.0" 35 | }, 36 | "bundleDependencies": [], 37 | "license": "MIT", 38 | "engines": { 39 | "node": ">=0.8" 40 | } 41 | } -------------------------------------------------------------------------------- /src/jquery.indexeddb.js: -------------------------------------------------------------------------------- 1 | (function($, undefined) { 2 | 'use strict'; 3 | var indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB; 4 | var IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange; 5 | var IDBCursor = window.IDBCursor || window.webkitIDBCursor || {}; 6 | if (typeof IDBCursor.PREV === "undefined") { 7 | IDBCursor.PREV = "prev"; 8 | } 9 | if (typeof IDBCursor.NEXT === "undefined") { 10 | IDBCursor.NEXT = "next"; 11 | } 12 | 13 | /** 14 | * Best to use the constant IDBTransaction since older version support numeric types while the latest spec 15 | * supports strings 16 | */ 17 | var IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction; 18 | 19 | function getDefaultTransaction(mode) { 20 | var result = null; 21 | switch (mode) { 22 | case 0: 23 | case 1: 24 | case "readwrite": 25 | case "readonly": 26 | result = mode; 27 | break; 28 | default: 29 | result = IDBTransaction.READ_WRITE || "readwrite"; 30 | } 31 | return result; 32 | } 33 | 34 | $.extend({ 35 | /** 36 | * The IndexedDB object used to open databases 37 | * @param {Object} dbName - name of the database 38 | * @param {Object} config - version, onupgradeneeded, onversionchange, schema 39 | */ 40 | "indexedDB": function(dbName, config) { 41 | if (config) { 42 | // Parse the config argument 43 | if (typeof config === "number") config = { 44 | "version": config 45 | }; 46 | 47 | var version = config.version; 48 | if (config.schema && !version) { 49 | var max = -1; 50 | for (var key in config.schema) { 51 | max = max > key ? max : key; 52 | } 53 | version = config.version || max; 54 | } 55 | } 56 | 57 | 58 | var wrap = { 59 | "request": function(req, args) { 60 | return $.Deferred(function(dfd) { 61 | try { 62 | var idbRequest = typeof req === "function" ? req(args) : req; 63 | idbRequest.onsuccess = function(e) { 64 | console.log("Success", idbRequest, e, this); 65 | dfd.resolveWith(idbRequest, [idbRequest.result, e]); 66 | }; 67 | idbRequest.onerror = function(e) { 68 | console.log("Error", idbRequest, e, this); 69 | dfd.rejectWith(idbRequest, [idbRequest.error, e]); 70 | }; 71 | if (typeof idbRequest.onblocked !== "undefined" && idbRequest.onblocked === null) { 72 | idbRequest.onblocked = function(e) { 73 | console.log("Blocked", idbRequest, e, this); 74 | var res; 75 | try { 76 | res = idbRequest.result; 77 | } catch (e) { 78 | res = null; // Required for Older Chrome versions, accessing result causes error 79 | } 80 | dfd.notifyWith(idbRequest, [res, e]); 81 | }; 82 | } 83 | if (typeof idbRequest.onupgradeneeded !== "undefined" && idbRequest.onupgradeneeded === null) { 84 | idbRequest.onupgradeneeded = function(e) { 85 | console.log("Upgrade", idbRequest, e, this); 86 | dfd.notifyWith(idbRequest, [idbRequest.result, e]); 87 | }; 88 | } 89 | } catch (e) { 90 | e.name = "exception"; 91 | dfd.rejectWith(idbRequest, ["exception", e]); 92 | } 93 | }); 94 | }, 95 | // Wraps the IDBTransaction to return promises, and other dependent methods 96 | "transaction": function(idbTransaction) { 97 | return { 98 | "objectStore": function(storeName) { 99 | try { 100 | return wrap.objectStore(idbTransaction.objectStore(storeName)); 101 | } catch (e) { 102 | idbTransaction.readyState !== idbTransaction.DONE && idbTransaction.abort(); 103 | return wrap.objectStore(null); 104 | } 105 | }, 106 | "createObjectStore": function(storeName, storeParams) { 107 | try { 108 | return wrap.objectStore(idbTransaction.db.createObjectStore(storeName, storeParams)); 109 | } catch (e) { 110 | idbTransaction.readyState !== idbTransaction.DONE && idbTransaction.abort(); 111 | } 112 | }, 113 | "deleteObjectStore": function(storeName) { 114 | try { 115 | idbTransaction.db.deleteObjectStore(storeName); 116 | } catch (e) { 117 | idbTransaction.readyState !== idbTransaction.DONE && idbTransaction.abort(); 118 | } 119 | }, 120 | "abort": function() { 121 | idbTransaction.abort(); 122 | } 123 | }; 124 | }, 125 | "objectStore": function(idbObjectStore) { 126 | var result = {}; 127 | // Define CRUD operations 128 | var crudOps = ["add", "put", "get", "delete", "clear", "count"]; 129 | for (var i = 0; i < crudOps.length; i++) { 130 | result[crudOps[i]] = (function(op) { 131 | return function() { 132 | return wrap.request(function(args) { 133 | return idbObjectStore[op].apply(idbObjectStore, args); 134 | }, arguments); 135 | }; 136 | })(crudOps[i]); 137 | } 138 | 139 | result.each = function(callback, range, direction) { 140 | return wrap.cursor(function() { 141 | if (direction) { 142 | return idbObjectStore.openCursor(wrap.range(range), direction); 143 | } else { 144 | return idbObjectStore.openCursor(wrap.range(range)); 145 | } 146 | }, callback); 147 | }; 148 | 149 | result.index = function(name) { 150 | return wrap.index(function() { 151 | return idbObjectStore.index(name); 152 | }); 153 | }; 154 | 155 | result.createIndex = function(prop, options, indexName) { 156 | if (arguments.length === 2 && typeof options === "string") { 157 | indexName = arguments[1]; 158 | options = null; 159 | } 160 | if (!indexName) { 161 | indexName = prop; 162 | } 163 | return wrap.index(function() { 164 | return idbObjectStore.createIndex(indexName, prop, options); 165 | }); 166 | }; 167 | 168 | result.deleteIndex = function(indexName) { 169 | return idbObjectStore.deleteIndex(indexName); 170 | }; 171 | 172 | return result; 173 | }, 174 | 175 | "range": function(r) { 176 | if ($.isArray(r)) { 177 | if (r.length === 1) { 178 | return IDBKeyRange.only(r[0]); 179 | } else { 180 | return IDBKeyRange.bound(r[0], r[1], (typeof r[2] === 'undefined') ? false : r[2], (typeof r[3] === 'undefined') ? false : r[3]); 181 | } 182 | } else if (typeof r === "undefined") { 183 | return null; 184 | } else { 185 | return r; 186 | } 187 | }, 188 | 189 | "cursor": function(idbCursor, callback) { 190 | return $.Deferred(function(dfd) { 191 | try { 192 | console.log("Cursor request created", idbCursor); 193 | var cursorReq = typeof idbCursor === "function" ? idbCursor() : idbCursor; 194 | cursorReq.onsuccess = function(e) { 195 | console.log("Cursor successful"); 196 | if (!cursorReq.result) { 197 | dfd.resolveWith(cursorReq, [null, e]); 198 | return; 199 | } 200 | var elem = { 201 | // Delete, update do not move 202 | "delete": function() { 203 | return wrap.request(function() { 204 | return cursorReq.result["delete"](); 205 | }); 206 | }, 207 | "update": function(data) { 208 | return wrap.request(function() { 209 | return cursorReq.result["update"](data); 210 | }); 211 | }, 212 | "next": function(key) { 213 | this.data = key; 214 | }, 215 | "key": cursorReq.result.key, 216 | "value": cursorReq.result.value 217 | }; 218 | console.log("Cursor in progress", elem, e); 219 | dfd.notifyWith(cursorReq, [elem, e]); 220 | var result = callback.apply(cursorReq, [elem]); 221 | console.log("Iteration function returned", result); 222 | try { 223 | if (result === false) { 224 | dfd.resolveWith(cursorReq, [null, e]); 225 | } else if (typeof result === "number") { 226 | cursorReq.result["advance"].apply(cursorReq.result, [result]); 227 | } else { 228 | if (elem.data) cursorReq.result["continue"].apply(cursorReq.result, [elem.data]); 229 | else cursorReq.result["continue"](); 230 | } 231 | } catch (e) { 232 | console.log("Exception when trying to advance cursor", cursorReq, e); 233 | dfd.rejectWith(cursorReq, [cursorReq.result, e]); 234 | } 235 | }; 236 | cursorReq.onerror = function(e) { 237 | console.log("Cursor request errored out", e); 238 | dfd.rejectWith(cursorReq, [cursorReq.result, e]); 239 | }; 240 | } catch (e) { 241 | console.log("An exception occured inside cursor", cursorReq, e); 242 | e.type = "exception"; 243 | dfd.rejectWith(cursorReq, [null, e]); 244 | } 245 | }); 246 | }, 247 | 248 | "index": function(index) { 249 | try { 250 | var idbIndex = (typeof index === "function" ? index() : index); 251 | } catch (e) { 252 | idbIndex = null; 253 | } 254 | console.log(idbIndex, index); 255 | return { 256 | "each": function(callback, range, direction) { 257 | return wrap.cursor(function() { 258 | if (direction) { 259 | return idbIndex.openCursor(wrap.range(range), direction); 260 | } else { 261 | return idbIndex.openCursor(wrap.range(range)); 262 | } 263 | 264 | }, callback); 265 | }, 266 | "eachKey": function(callback, range, direction) { 267 | return wrap.cursor(function() { 268 | if (direction) { 269 | return idbIndex.openKeyCursor(wrap.range(range), direction); 270 | } else { 271 | return idbIndex.openKeyCursor(wrap.range(range)); 272 | } 273 | }, callback); 274 | }, 275 | "get": function(key) { 276 | if (typeof idbIndex.get === "function") { 277 | return wrap.request(idbIndex.get(key)); 278 | } else { 279 | return idbIndex.openCursor(wrap.range(key)); 280 | } 281 | }, 282 | "count": function() { 283 | if (typeof idbIndex.count === "function") { 284 | return wrap.request(idbIndex.count()); 285 | } else { 286 | throw "Count not implemented for cursors"; 287 | } 288 | }, 289 | "getKey": function(key) { 290 | if (typeof idbIndex.getKey === "function") { 291 | return wrap.request(idbIndex.getKey(key)); 292 | } else { 293 | return idbIndex.openKeyCursor(wrap.range(key)); 294 | } 295 | } 296 | }; 297 | } 298 | }; 299 | 300 | 301 | // Start with opening the database 302 | var dbPromise = wrap.request(function() { 303 | console.log("Trying to open DB with", version); 304 | return version ? indexedDB.open(dbName, parseInt(version)) : indexedDB.open(dbName); 305 | }); 306 | dbPromise.then(function(db, e) { 307 | console.log("DB opened at", db.version); 308 | db.onversionchange = function() { 309 | // Try to automatically close the database if there is a version change request 310 | if (!(config && config.onversionchange && config.onversionchange() !== false)) { 311 | db.close(); 312 | } 313 | }; 314 | }, function(error, e) { 315 | console.log(error, e); 316 | // Nothing much to do if an error occurs 317 | }, function(db, e) { 318 | if (e && e.type === "upgradeneeded") { 319 | if (config && config.schema) { 320 | // Assuming that version is always an integer 321 | console.log("Upgrading DB to ", db.version); 322 | for (var i = e.oldVersion + 1; i <= e.newVersion; i++) { 323 | typeof config.schema[i] === "function" && config.schema[i].call(this, wrap.transaction(this.transaction)); 324 | } 325 | } 326 | if (config && typeof config.upgrade === "function") { 327 | config.upgrade.call(this, wrap.transaction(this.transaction)); 328 | } 329 | } 330 | }); 331 | 332 | return $.extend(dbPromise, { 333 | "cmp": function(key1, key2) { 334 | return indexedDB.cmp(key1, key2); 335 | }, 336 | "deleteDatabase": function() { 337 | // Kinda looks ugly coz DB is opened before it needs to be deleted. 338 | // Blame it on the API 339 | return $.Deferred(function(dfd) { 340 | dbPromise.then(function(db, e) { 341 | db.close(); 342 | wrap.request(function() { 343 | return indexedDB.deleteDatabase(dbName); 344 | }).then(function(result, e) { 345 | dfd.resolveWith(this, [result, e]); 346 | }, function(error, e) { 347 | dfd.rejectWith(this, [error, e]); 348 | }, function(db, e) { 349 | dfd.notifyWith(this, [db, e]); 350 | }); 351 | }, function(error, e) { 352 | dfd.rejectWith(this, [error, e]); 353 | }, function(db, e) { 354 | dfd.notifyWith(this, [db, e]); 355 | }); 356 | }); 357 | }, 358 | "transaction": function(storeNames, mode) { 359 | !$.isArray(storeNames) && (storeNames = [storeNames]); 360 | mode = getDefaultTransaction(mode); 361 | return $.Deferred(function(dfd) { 362 | dbPromise.then(function(db, e) { 363 | var idbTransaction; 364 | try { 365 | console.log("DB Opened, now trying to create a transaction", storeNames, mode); 366 | idbTransaction = db.transaction(storeNames, mode); 367 | console.log("Created a transaction", idbTransaction, mode, storeNames); 368 | idbTransaction.onabort = idbTransaction.onerror = function(e) { 369 | dfd.rejectWith(idbTransaction, [e]); 370 | }; 371 | idbTransaction.oncomplete = function(e) { 372 | dfd.resolveWith(idbTransaction, [e]); 373 | }; 374 | } catch (e) { 375 | console.log("Creating a traction failed", e, storeNames, mode, this); 376 | e.type = "exception"; 377 | dfd.rejectWith(this, [e]); 378 | return; 379 | } 380 | try { 381 | dfd.notifyWith(idbTransaction, [wrap.transaction(idbTransaction)]); 382 | } catch (e) { 383 | e.type = "exception"; 384 | dfd.rejectWith(this, [e]); 385 | } 386 | }, function(err, e) { 387 | dfd.rejectWith(this, [e, err]); 388 | }, function(res, e) { 389 | console.log("Database open is blocked or upgrade needed", res, e.type); 390 | //dfd.notifyWith(this, ["", e]); 391 | }); 392 | 393 | }); 394 | }, 395 | "objectStore": function(storeName, mode) { 396 | var me = this, 397 | result = {}; 398 | 399 | function op(callback) { 400 | return $.Deferred(function(dfd) { 401 | function onTransactionProgress(trans, callback) { 402 | try { 403 | console.log("Finally, returning the object store", trans); 404 | callback(trans.objectStore(storeName)).then(function(result, e) { 405 | dfd.resolveWith(this, [result, e]); 406 | }, function(err, e) { 407 | dfd.rejectWith(this, [err, e]); 408 | }); 409 | } catch (e) { 410 | console.log("Duh, an exception occured", e); 411 | e.name = "exception"; 412 | dfd.rejectWith(trans, [e, e]); 413 | } 414 | } 415 | me.transaction(storeName, getDefaultTransaction(mode)).then(function() { 416 | console.log("Transaction completed"); 417 | // Nothing to do when transaction is complete 418 | }, function(err, e) { 419 | // If transaction fails, CrudOp fails 420 | if (err.code === err.NOT_FOUND_ERR && (mode === true || typeof mode === "object")) { 421 | console.log("Object Not found, so will try to create one now"); 422 | var db = this.result; 423 | db.close(); 424 | dbPromise = wrap.request(function() { 425 | console.log("Now trying to open the database again", db.version); 426 | return indexedDB.open(dbName, (parseInt(db.version, 10) || 1) + 1); 427 | }); 428 | dbPromise.then(function(db, e) { 429 | console.log("Database opened, tto open transaction", db.version); 430 | db.onversionchange = function() { 431 | // Try to automatically close the database if there is a version change request 432 | if (!(config && config.onversionchange && config.onversionchange() !== false)) { 433 | db.close(); 434 | } 435 | }; 436 | me.transaction(storeName, getDefaultTransaction(mode)).then(function() { 437 | console.log("Transaction completed when trying to create object store"); 438 | // Nothing much to do 439 | }, function(err, e) { 440 | dfd.rejectWith(this, [err, e]); 441 | }, function(trans, e) { 442 | console.log("Transaction in progress, when object store was not found", this, trans, e); 443 | onTransactionProgress(trans, callback); 444 | }); 445 | }, function(err, e) { 446 | dfd.rejectWith(this, [err, e]); 447 | }, function(db, e) { 448 | if (e.type === "upgradeneeded") { 449 | try { 450 | console.log("Now trying to create an object store", e.type); 451 | db.createObjectStore(storeName, mode === true ? { 452 | "autoIncrement": true 453 | } : mode); 454 | console.log("Object store created", storeName, db); 455 | } catch (ex) { 456 | console.log("Exception when trying ot create a new object store", ex); 457 | dfd.rejectWith(this, [ex, e]); 458 | } 459 | } 460 | }); 461 | } else { 462 | dfd.rejectWith(this, [err, e]); 463 | } 464 | }, function(trans) { 465 | console.log("Transaction is in progress", trans); 466 | onTransactionProgress(trans, callback); 467 | }); 468 | }); 469 | } 470 | 471 | function crudOp(opName, args) { 472 | return op(function(wrappedObjectStore) { 473 | return wrappedObjectStore[opName].apply(wrappedObjectStore, args); 474 | }); 475 | } 476 | 477 | function indexOp(opName, indexName, args) { 478 | return op(function(wrappedObjectStore) { 479 | var index = wrappedObjectStore.index(indexName); 480 | return index[opName].apply(index[opName], args); 481 | }); 482 | } 483 | 484 | var crud = ["add", "delete", "get", "put", "clear", "count", "each"]; 485 | for (var i = 0; i < crud.length; i++) { 486 | result[crud[i]] = (function(op) { 487 | return function() { 488 | return crudOp(op, arguments); 489 | }; 490 | })(crud[i]); 491 | } 492 | 493 | result.index = function(indexName) { 494 | return { 495 | "each": function(callback, range, direction) { 496 | return indexOp("each", indexName, [callback, range, direction]); 497 | }, 498 | "eachKey": function(callback, range, direction) { 499 | return indexOp("eachKey", indexName, [callback, range, direction]); 500 | }, 501 | "get": function(key) { 502 | return indexOp("get", indexName, [key]); 503 | }, 504 | "count": function() { 505 | return indexOp("count", indexName, []); 506 | }, 507 | "getKey": function(key) { 508 | return indexOp("getKey", indexName, [key]); 509 | } 510 | }; 511 | }; 512 | 513 | return result; 514 | } 515 | }); 516 | } 517 | }); 518 | 519 | $.indexedDB.IDBCursor = IDBCursor; 520 | $.indexedDB.IDBTransaction = IDBTransaction; 521 | $.idb = $.indexedDB; 522 | })(jQuery); -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: Helvetica Neue, Verdana, Helvetica, Arial; 5 | font-size: 14px; 6 | color: #333; 7 | } 8 | 9 | body { 10 | overflow: auto; 11 | height: 100%; 12 | background: #6788AD; 13 | line-height : 18px; 14 | background: -moz-linear-gradient(top, #496687 0%, #6889AE 100%); 15 | background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #496687), color-stop(100%, #6889AE) ); 16 | filter: progid : DXImageTransform.Microsoft.gradient ( startColorstr = '#496687', endColorstr = '#6889AE',GradientType = 0 ); 17 | } 18 | 19 | li{ 20 | margin-bottom:3px; 21 | } 22 | 23 | #content-wrap{ 24 | width : 70%; 25 | margin : 0 auto; 26 | background-color : white; 27 | box-shadow : 0 0 20px #122F4B; 28 | } 29 | 30 | #download{ 31 | float : right; 32 | width : 200px; 33 | text-align : center; 34 | margin-right : 10px; 35 | } 36 | 37 | h1{ 38 | padding : 1%; 39 | background-color : #122F4B; 40 | color: white; 41 | font-family: Georgia, "Times New Roman", Times; 42 | font-weight : normal; 43 | text-align : center; 44 | padding : 20px 0; 45 | margin : 0; 46 | } 47 | 48 | #content{ 49 | padding : 2%; 50 | } 51 | 52 | pre{ 53 | border: SOLID 1px black; 54 | display : block; 55 | margin : 10px 0; 56 | padding : 10px; 57 | font-family : consolas, monaco, courier; 58 | font-size : 12px; 59 | } 60 | 61 | #log{ 62 | font-family : consolas, monaco, courier; 63 | font-size : 12px; 64 | border : SOLID 1px black; 65 | margin : 10px 0; 66 | padding : 10px; 67 | } 68 | 69 | button{ 70 | display : block; 71 | } 72 | 73 | h3{ 74 | } 75 | 76 | .left{ 77 | float : left; 78 | width : 48%; 79 | } 80 | 81 | .right{ 82 | border-left : 1px dotted #CCC; 83 | padding : 1%; 84 | float : right; 85 | width : 48%; 86 | } 87 | 88 | .clear{ 89 | clear:both; 90 | } 91 | 92 | 93 | /* punch 94 | *******************************************************************************/ 95 | .punch { 96 | display:block; 97 | text-decoration : none; 98 | background: #4162a8; 99 | border-top: 1px solid #38538c; 100 | border-right: 1px solid #1f2d4d; 101 | border-bottom: 1px solid #151e33; 102 | border-left: 1px solid #1f2d4d; 103 | -webkit-border-radius: 4px; 104 | -moz-border-radius: 4px; 105 | border-radius: 4px; 106 | -webkit-box-shadow: inset 0 1px 10px 1px #5c8bee, 0px 1px 0 #1d2c4d, 0 6px 0px #1f3053, 0 8px 4px 1px #111111; 107 | -moz-box-shadow: inset 0 1px 10px 1px #5c8bee, 0px 1px 0 #1d2c4d, 0 6px 0px #1f3053, 0 8px 4px 1px #111111; 108 | box-shadow: inset 0 1px 10px 1px #5c8bee, 0px 1px 0 #1d2c4d, 0 6px 0px #1f3053, 0 8px 4px 1px #111111; 109 | color: #fff; 110 | font: bold 20px "helvetica neue", helvetica, arial, sans-serif; 111 | line-height: 1; 112 | margin-bottom: 10px; 113 | padding: 10px 0 12px 0; 114 | text-align: center; 115 | text-shadow: 0px -1px 1px #1e2d4d; 116 | -webkit-background-clip: padding-box; } 117 | .punch:hover { 118 | -webkit-box-shadow: inset 0 0px 20px 1px #87adff, 0px 1px 0 #1d2c4d, 0 6px 0px #1f3053, 0 8px 4px 1px #111111; 119 | -moz-box-shadow: inset 0 0px 20px 1px #87adff, 0px 1px 0 #1d2c4d, 0 6px 0px #1f3053, 0 8px 4px 1px #111111; 120 | box-shadow: inset 0 0px 20px 1px #87adff, 0px 1px 0 #1d2c4d, 0 6px 0px #1f3053, 0 8px 4px 1px #111111; 121 | cursor: pointer; } 122 | .punch:active { 123 | -webkit-box-shadow: inset 0 1px 10px 1px #5c8bee, 0 1px 0 #1d2c4d, 0 2px 0 #1f3053, 0 4px 3px 0 #111111; 124 | -moz-box-shadow: inset 0 1px 10px 1px #5c8bee, 0 1px 0 #1d2c4d, 0 2px 0 #1f3053, 0 4px 3px 0 #111111; 125 | box-shadow: inset 0 1px 10px 1px #5c8bee, 0 1px 0 #1d2c4d, 0 2px 0 #1f3053, 0 4px 3px 0 #111111; 126 | } -------------------------------------------------------------------------------- /test/api.txt: -------------------------------------------------------------------------------- 1 | indexedDB(open) 2 | .done(function(dbRequest.result){this = dbRequest}) 3 | .fail(function(dbRequest.error) {this = dbRequest}) 4 | .progress(function(trans) {this = dbRequest; 5 | this = dbRequest; 6 | trans.createObjectStore 7 | trans.deleteObjectStore() 8 | trans.abort(); 9 | trans.done() 10 | trans.onerror() 11 | trans.progress() 12 | }) // called on upgrade and blocked 13 | 14 | .transaction(store, mode) 15 | .progress(function(trans){ 16 | this = transaction; 17 | trans.objectStore() 18 | trans.abort() 19 | }) 20 | .done(function(onCompleteEvent) {this = transaction}) // transaction is complete 21 | .fail(function(onErrorEvent ) {this = transaction}) // onabort, or onerror, or exception 22 | 23 | 24 | .objectStore(storeName, mode) 25 | .add(), .delete(), .put(), .clear() 26 | .forEach(elem){this.delete(), this.update(newElem)}, 27 | .index() 28 | .createIndex() 29 | .deleteIndex() 30 | 31 | .fail() 32 | 33 | 34 | .deleteDatabase() - returns a promise 35 | .cmp() - returns 1, 0, -1 -------------------------------------------------------------------------------- /test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 10 | 12 | 14 | 16 | 855 | 856 | 857 |

Jquery IndexedDB Tests

858 |

859 |
860 |
861 |

862 |
    863 |
864 |
865 | test markup, will be hidden 866 |
867 | 884 | 885 | 886 | -------------------------------------------------------------------------------- /test/sample.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | 11 | 13 | 36 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /test/sampleData.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var DB = { 3 | NAME : "dbName", 4 | OBJECT_STORE_1 : "objectStore1", 5 | OBJECT_STORE_2 : "objectStore2", 6 | OBJECT_STORE_3 : "objectStore3", 7 | OBJECT_STORE_4 : "objectStore4", 8 | INDEX1_ON_OBJECT_STORE_1 : "Index1_ObjectStore1", 9 | INDEX1_ON_OBJECT_STORE_2 : "Index1_ObjectStore2" 10 | }; 11 | 12 | var sample = (function() { 13 | 14 | var takenValues = {}; 15 | return { 16 | obj : function() { 17 | return { 18 | "String" : "Sample " + new Date(), 19 | "Int" : this.integer(), 20 | "Float" : Math.random(), 21 | "Boolean" : true 22 | }; 23 | }, 24 | integer : function(arg) { 25 | var res; 26 | do { 27 | res = parseInt(Math.random() * (arg || 1000), 10); 28 | } while (takenValues[res]); 29 | takenValues[res] = true; 30 | return res; 31 | } 32 | }; 33 | }()); 34 | -------------------------------------------------------------------------------- /travis.sh: -------------------------------------------------------------------------------- 1 | repo="https://${GH_TOKEN}@github.com/$TRAVIS_REPO_SLUG.git" 2 | 3 | # If this is a pull request, we dont want to continue 4 | if [ "$TRAVIS_PULL_REQUEST" != "false" ] ; then 5 | echo "Nothing to do on a pull request" 6 | exit 0 7 | fi 8 | 9 | # All Pull requests are to the branch called incoming-pr 10 | # If this is not that branch, the rest of the script is irrelevant 11 | if [ "$TRAVIS_BRANCH" != "incoming-pr" ] ; then 12 | echo "Nothing to do on the master branch" 13 | exit 0 14 | fi 15 | 16 | git fetch 17 | git branch --all 18 | # This part of the script is run before installing deps or tests 19 | if [ "$1" = "before" ] ; then 20 | # So this is incoming-pr branch. Need to check if this is exactly same as the master 21 | # If it is, this branch does not have anything new, so no need to try and build it 22 | incoming_commit=$(git rev-parse HEAD) 23 | git checkout --orphan master 24 | git pull origin master --depth=1 25 | master_commit=$(git rev-parse HEAD) 26 | if [ "$incoming_commit" = "$master_commit" ] ; then 27 | echo "Not required to build this as this is same as the master branch" 28 | exit 1 29 | else 30 | git checkout incoming-pr 31 | echo "Current branch is - $(git rev-parse HEAD)" 32 | exit 0 33 | fi 34 | fi 35 | 36 | git checkout incoming-pr 37 | if [ "$1" = "merge" ] ; then 38 | # If the build was successful, travis after-success will send merge 39 | echo "Merging incoming-pr to master" 40 | git checkout master 41 | git merge incoming-pr --log 42 | git push $repo master -q 2> /dev/null 43 | 44 | echo "Merging master into gh-pages" 45 | git checkout gh-pages 46 | git merge master --log 47 | git push $repo gh-pages -q 2> /dev/null 48 | else 49 | # If build failed, travis after-failure will send ./travis.sh revert 50 | echo "Reverting dev branch to what master was" 51 | # Save the current changes to a new branch 52 | echo "Creating a new branch for failed build - incoming-pr-fail-$TRAVIS_BUILD_ID" 53 | git checkout -b incoming-pr-fail-$TRAVIS_BUILD_ID 54 | git push $repo incoming-pr-fail-$TRAVIS_BUILD_ID -q 2> /dev/null 55 | git checkout master 56 | fi 57 | 58 | echo "Making incoming-pr same as master" 59 | # Merge or revert is done, so make incoming-pr branch at par with master 60 | # This is done to make it ready for accepting the next pull request 61 | git push $repo --delete incoming-pr -q 2> /dev/null 62 | git branch -D incoming-pr 63 | git branch incoming-pr 64 | git push $repo incoming-pr -q 2> /dev/null 65 | --------------------------------------------------------------------------------