Skip to content

Commit

Permalink
refactor(localforage): create a constructor for the localforage object
Browse files Browse the repository at this point in the history
  • Loading branch information
thgreasi committed Sep 24, 2014
1 parent 84ee7be commit c318f37
Showing 1 changed file with 159 additions and 148 deletions.
307 changes: 159 additions & 148 deletions src/localforage.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
WEBSQL: 'webSQLStorage'
};

var DEFAULT_DRIVER_ORDER = [
var DefaultDriverOrder = [
DriverType.INDEXEDDB,
DriverType.WEBSQL,
DriverType.LOCALSTORAGE
Expand All @@ -33,6 +33,16 @@
WINDOW: 3
};

var DefaultConfig = {
description: '',
name: 'localforage',
// Default DB size is _JUST UNDER_ 5MB, as it's the highest size
// we can use without a prompt.
size: 4980736,
storeName: 'keyvaluepairs',
version: 1.0
};

// Attaching to window (i.e. no module loader) is the assumed,
// simple default.
var moduleType = ModuleType.WINDOW;
Expand Down Expand Up @@ -83,182 +93,183 @@
return result;
})(this);

// The actual localForage object that we expose as a module or via a
// global. It's extended by pulling in one of our other libraries.
var _this = this;
var localForage = {
INDEXEDDB: DriverType.INDEXEDDB,
LOCALSTORAGE: DriverType.LOCALSTORAGE,
WEBSQL: DriverType.WEBSQL,

_config: {
description: '',
name: 'localforage',
// Default DB size is _JUST UNDER_ 5MB, as it's the highest size
// we can use without a prompt.
size: 4980736,
storeName: 'keyvaluepairs',
version: 1.0
},
_driverSet: null,
_ready: false,

// Set any config values for localForage; can be called anytime before
// the first API call (e.g. `getItem`, `setItem`).
// We loop through options so we don't overwrite existing config
// values.
config: function(options) {
// If the options argument is an object, we use it to set values.
// Otherwise, we return either a specified config value or all
// config values.
if (typeof(options) === 'object') {
// If localforage is ready and fully initialized, we can't set
// any new configuration values. Instead, we return an error.
if (this._ready) {
return new Error("Can't call config() after localforage " +
"has been used.");
}

for (var i in options) {
this._config[i] = options[i];
function extend(/*...*/) {
for (var i = 1; i < arguments.length; i++) {
var arg = arguments[i];
if (arg) {
for (var key in arg) {
if (arg.hasOwnProperty(key)) {
arguments[0][key] = arg[key];
}
}

return true;
} else if (typeof(options) === 'string') {
return this._config[options];
} else {
return this._config;
}
},

driver: function() {
return this._driver || null;
},

ready: function(callback) {
var ready = new Promise(function(resolve, reject) {
localForage._driverSet.then(function() {
if (localForage._ready === null) {
localForage._ready = localForage._initStorage(
localForage._config);
}
}
return arguments[0];
}

localForage._ready.then(resolve, reject);
}).catch(reject);
function callWhenReady(localForageInstance, libraryMethod) {
localForageInstance[libraryMethod] = function() {
var _args = arguments;
return localForageInstance.ready().then(function() {
return localForageInstance[libraryMethod].apply(localForageInstance, _args);
});
};
}

ready.then(callback, callback);
// The actual localForage object that we expose as a module or via a
// global. It's extended by pulling in one of our other libraries.
var _this = this;

return ready;
},
function LocalForage(options) {
this._config = extend({}, DefaultConfig, options);
this._driverSet = null;
this._ready = false;

// Add a stub for each driver API method that delays the call to the
// corresponding driver method until localForage is ready. These stubs will
// be replaced by the driver methods as soon as the driver is loaded, so
// there is no performance impact.
for (var i = 0; i < LibraryMethods.length; i++) {
callWhenReady(this, LibraryMethods[i]);
}

setDriver: function(drivers, callback, errorCallback) {
var self = this;
this.setDriver(DefaultDriverOrder);
}

if (typeof drivers === 'string') {
drivers = [drivers];
LocalForage.prototype.INDEXEDDB = DriverType.INDEXEDDB;
LocalForage.prototype.LOCALSTORAGE = DriverType.LOCALSTORAGE;
LocalForage.prototype.WEBSQL = DriverType.WEBSQL;
// Set any config values for localForage; can be called anytime before
// the first API call (e.g. `getItem`, `setItem`).
// We loop through options so we don't overwrite existing config
// values.
LocalForage.prototype.config = function(options) {
// If the options argument is an object, we use it to set values.
// Otherwise, we return either a specified config value or all
// config values.
if (typeof(options) === 'object') {
// If localforage is ready and fully initialized, we can't set
// any new configuration values. Instead, we return an error.
if (this._ready) {
return new Error("Can't call config() after localforage " +
"has been used.");
}

this._driverSet = new Promise(function(resolve, reject) {
var driverName = self._getFirstSupportedDriver(drivers);

if (!driverName) {
var error = new Error('No available storage method found.');
self._driverSet = Promise.reject(error);

reject(error);
for (var i in options) {
this._config[i] = options[i];
}

return;
return true;
} else if (typeof(options) === 'string') {
return this._config[options];
} else {
return this._config;
}
};
LocalForage.prototype.driver = function() {
return this._driver || null;
};
LocalForage.prototype.ready = function(callback) {
var self = this;

var ready = new Promise(function(resolve, reject) {
self._driverSet.then(function() {
if (self._ready === null) {
self._ready = self._initStorage(
self._config);
}

self._ready = null;

// We allow localForage to be declared as a module or as a
// library available without AMD/require.js.
if (moduleType === ModuleType.DEFINE) {
require([driverName], function(lib) {
self._extend(lib);

resolve();
});

return;
} else if (moduleType === ModuleType.EXPORT) {
// Making it browserify friendly
var driver;
switch (driverName) {
case self.INDEXEDDB:
driver = require('./drivers/indexeddb');
break;
case self.LOCALSTORAGE:
driver = require('./drivers/localstorage');
break;
case self.WEBSQL:
driver = require('./drivers/websql');
}
self._ready.then(resolve, reject);
}).catch(reject);
});

self._extend(driver);
} else {
self._extend(_this[driverName]);
}
ready.then(callback, callback);
return ready;
};
LocalForage.prototype.setDriver = function(drivers, callback, errorCallback) {
var self = this;

resolve();
});
if (typeof drivers === 'string') {
drivers = [drivers];
}

this._driverSet = new Promise(function(resolve, reject) {
var driverName = self._getFirstSupportedDriver(drivers);

this._driverSet.then(callback, errorCallback);
if (!driverName) {
var error = new Error('No available storage method found.');
self._driverSet = Promise.reject(error);

return this._driverSet;
},
reject(error);

supports: function(driverName) {
return !!driverSupport[driverName];
},
return;
}

_extend: function(libraryMethodsAndProperties) {
for (var i in libraryMethodsAndProperties) {
if (libraryMethodsAndProperties.hasOwnProperty(i)) {
this[i] = libraryMethodsAndProperties[i];
self._ready = null;

// We allow localForage to be declared as a module or as a
// library available without AMD/require.js.
if (moduleType === ModuleType.DEFINE) {
require([driverName], function(lib) {
self._extend(lib);

resolve();
});

return;
} else if (moduleType === ModuleType.EXPORT) {
// Making it browserify friendly
var driver;
switch (driverName) {
case self.INDEXEDDB:
driver = require('./drivers/indexeddb');
break;
case self.LOCALSTORAGE:
driver = require('./drivers/localstorage');
break;
case self.WEBSQL:
driver = require('./drivers/websql');
}

self._extend(driver);
} else {
self._extend(_this[driverName]);
}
},

_getFirstSupportedDriver: function(drivers) {
var isArray = Array.isArray || function(arg) {
return Object.prototype.toString.call(arg) === '[object Array]';
};
resolve();
});

if (drivers && isArray(drivers)) {
for (var i = 0; i < drivers.length; i++) {
var driver = drivers[i];
this._driverSet.then(callback, errorCallback);
return this._driverSet;
};
LocalForage.prototype.supports = function(driverName) {
return !!driverSupport[driverName];
};
LocalForage.prototype._extend = function(libraryMethodsAndProperties) {
extend(this, libraryMethodsAndProperties);
};
LocalForage.prototype._getFirstSupportedDriver = function(drivers) {
var isArray = Array.isArray || function(arg) {
return Object.prototype.toString.call(arg) === '[object Array]';
};

if (this.supports(driver)) {
return driver;
}
if (drivers && isArray(drivers)) {
for (var i = 0; i < drivers.length; i++) {
var driver = drivers[i];

if (this.supports(driver)) {
return driver;
}
}

return null;
}
};

function callWhenReady(libraryMethod) {
localForage[libraryMethod] = function() {
var _args = arguments;
return localForage.ready().then(function() {
return localForage[libraryMethod].apply(localForage, _args);
});
};
}

// Add a stub for each driver API method that delays the call to the
// corresponding driver method until localForage is ready. These stubs will
// be replaced by the driver methods as soon as the driver is loaded, so
// there is no performance impact.
for (var i = 0; i < LibraryMethods.length; i++) {
callWhenReady(LibraryMethods[i]);
}
return null;
};
LocalForage.prototype.createInstance = function(options) {
return new LocalForage(options);
};

localForage.setDriver(DEFAULT_DRIVER_ORDER);
var localForage = new LocalForage();

// We allow localForage to be declared as a module or as a library
// available without AMD/require.js.
Expand Down

0 comments on commit c318f37

Please sign in to comment.