Skip to content

Instantly share code, notes, and snippets.

@axemclion
Created May 11, 2012 00:29
Show Gist options
  • Save axemclion/2656808 to your computer and use it in GitHub Desktop.
Save axemclion/2656808 to your computer and use it in GitHub Desktop.
Shim that converts Chrome's SetVersion to the standard IndexedDB onupgradeneeded events
////////////////////////////////////////////////////////////////////////////////////////////////////
var openReqShim = function(dbName, version){
var me = this;
var IDBRequest = function(){
this.onsuccess = this.onerror = this.onblocked = this.onupgradeneeded = null;
};
function copyReq(req){
req = req || dbOpenReq;
for (var key in req) {
if (typeof result[key] === "undefined") {
result[key] = req[key];
}
}
}
function callback(fn, context, argArray, func){
//window.setTimeout(function(){
(typeof context[fn] === "function") && context[fn].apply(context, argArray);
(typeof func === "function") && func();
//}, 1);
}
var indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
var dbOpenReq = version ? indexedDB.open(dbName, version) : indexedDB.open(dbName);
var result = new IDBRequest();
dbOpenReq.onsuccess = function(e){
copyReq();
var db = dbOpenReq.result;
if (typeof db.setVersion === "function") {
var oldVersion = parseInt(db.version || 1, 10);
var newVersion = typeof version === "undefined" ? oldVersion : parseInt(version, 10);
if (oldVersion < newVersion) {
var versionReq = db.setVersion(version);
versionReq.onsuccess = function(upgradeEvent){
result.transaction = versionReq.result;
var event = new Event("upgradeneeded");
event.oldVersion = oldVersion;
event.newVersion = newVersion;
for (key in upgradeEvent) {
if (key !== "type") {
event[key] = upgradeEvent[key];
}
}
callback("onupgradeneeded", result, [event]);
// Version transaction is now complete, to open ordinary transaction
versionReq.result.db.close();
//console.log("Database closed, and will try to open again, with same version");
var newDbOpenReq = indexedDB.open(dbName);
delete result.transaction;
delete result.result;
newDbOpenReq.onsuccess = function(e){
//console.log("DB Opened without version change", newDbOpenReq.result);
copyReq(newDbOpenReq);
callback("onsuccess", result, [e], function(){
newDbOpenReq.result.close();
});
newDbOpenReq.result.close();
};
newDbOpenReq.onerror = function(e){
copyReq(newDbOpenReq);
callback("onerror", result, [e], function(){
//console.log("Closed database in newRequest on error", newDbOpenReq);
newDbOpenReq.result.close();
});
};
newDbOpenReq.onblocked = function(e){
//console.log("DB Blocked without version change", newDbOpenReq.result);
copyReq(newDbOpenReq);
callback("onblocked", result, [e], function(){
//console.log("Closed database in newRequest on blocked", newDbOpenReq);
newDbOpenReq.result.close();
});
};
};
versionReq.onerror = function(){
callback("onerror", result, [e]);
versionReq.result.close();
};
versionReq.onblocked = function(e){
// This always gets called, resulting the blocking the DB upgrade
//console.log("Version transaction blocked, so calling the on blocked method");
callback("onblocked", result, [e]);
};
} else if (oldVersion === newVersion) {
callback("onsuccess", result, [e]);
db.close();
} else {
callback("onerror", result, [e]);
db.close();
}
} else {
callback("onsuccess", result, [e]);
}
};
dbOpenReq.onerror = function(e){
copyReq();
//console.log("Error", dbOpenReq);
callback("onerror", result, [e]);
};
dbOpenReq.onblocked = function(e){
copyReq();
callback("onblocked", result, [e]);
};
dbOpenReq.onupgradeneeded = function(e){
copyReq();
if (typeof result["onupgradeneeded"] === "function") {
result["onupgradeneeded"](e);
}
};
return result;
}
@jepp
Copy link

jepp commented Jul 15, 2012

I think there's something broken with this shim under Chrome 20. I'm not sure if it's just 20 or earlier as well, though. I have this test case. You can flip useShim to true or false. It should wipe the old database, create the new one, create an object store, then add data to it:

var DB = 'db';
var OS = 'test';
var ver = 10;
var db;
var os;

var useShim = true;


window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
for (var f in window) {
  if (window.hasOwnProperty(f)) {
    if (f.substr(0, 'webkitIDB'.length) === 'webkitIDB') {
      window[f.slice('webkit'.length, f.length)] = window[f];
    }
  }
}

var req = window.indexedDB.getDatabaseNames();

req.onsuccess = function (e) {
  if (e.target.result.length !== 0) {
    console.log('deleting db');
    var req = window.indexedDB.deleteDatabase(DB);
    req.onsuccess = function () {
      console.log('db deleted - running test');
      setTimeout(runTest, 100);
    }
  }
  else {
    console.log('db not found - running test');
    setTimeout(runTest, 100);
  }
};


function runTest() {
  var openReq = useShim ? openReqShim(DB, ver) : window.indexedDB.open(DB, ver);

  openReq.onupgradeneeded = function (e) {
    console.log('onupgradeneeded');

    db = useShim ? this.result : e.currentTarget.source;

    if (db.objectStoreNames.length === 0) {
      console.log('creating object store');

      db.createObjectStore(OS, {
        "keyPath":"akey"
      });
    }

    console.log('createObjectStore success');

    setTimeout(tryAdd, 1000);
  };

  openReq.onsuccess = function (e) {
    db = e.currentTarget.result;

    console.log("Database Opened successfully");

    if (!useShim) {
      var request = db.setVersion(ver);

      request.onsuccess = function () {
        console.log("setversion success");

        //if (ver != db.version) {
        if (db.objectStoreNames.length === 0) {
          openReq.onupgradeneeded.apply(this, arguments);
        }
      };
    }

  };

  openReq.onerror = function (e) {
    console.log("Database NOT Opened successfully");
  };
  openReq.onblocked = function (e) {
    console.log("Database blocked called");
  };
}

function tryAdd() {
  console.log('Getting transaction');
  var trans = db.transaction([OS], window.IDBTransaction.READ_WRITE);

  trans.oncomplete = function () {
    console.log('oncomplete', arguments);
  };

  trans.onerror = function () {
    console.log('onerror', arguments);
  };

  trans.onabort = function () {
    console.log('onabort', arguments);
  };

  console.log('Getting objectstore');
  os = trans.objectStore(OS);

  console.log('got store:', os);

  console.log('Adding key');
  os.add({"akey":(new Date()).getTime()});

  console.log('Done');
}

If you set useShim=false, it works fine. With useShim=true, I get this error:
Uncaught Error: NOT_ALLOWED_ERR: DOM IDBDatabase Exception 6
when trying to get a transaction in tryAdd.

It seems like maybe it's leaving it in a bad state. Any ideas?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment