From bf8b9e4215097969c539d4de0225b00ba7362f95 Mon Sep 17 00:00:00 2001 From: Sean Date: Wed, 10 Jun 2015 19:21:01 +0100 Subject: [PATCH] 2.0.0 release --- .gitdown/_usage.md | 44 ++++++++++++++++++++++++++++++---- README.md | 44 ++++++++++++++++++++++++++++++---- bower.json | 2 +- changes.md | 9 ++++++- dist/angular-locker.min.js | 2 +- dist/angular-locker.min.js.map | 2 +- package.json | 2 +- 7 files changed, 90 insertions(+), 15 deletions(-) diff --git a/.gitdown/_usage.md b/.gitdown/_usage.md index 5d21127..a157412 100644 --- a/.gitdown/_usage.md +++ b/.gitdown/_usage.md @@ -12,14 +12,17 @@ Configure via `lockerProvider` (*optional*) ```js .config(['lockerProvider', function config(lockerProvider) { - lockerProvider.setDefaultDriver('session') - .setDefaultNamespace('myAppName') - .setSeparator('.') - .setEventsEnabled(false); + lockerProvider.defaults({ + driver: 'session', + namespace: 'myApp', + separator: '.', + eventsEnabled: true, + extend: {} + }); }]); ``` -*Note*: You can also pass `false` into `setDefaultNamespace()` if you prefer to not have a namespace in your keys. +*Note*: You can also pass `false` to `namespace` if you prefer to not have a namespace in your keys. inject `locker` into your controller/service/directive etc @@ -29,6 +32,28 @@ inject `locker` into your controller/service/directive etc }]); ``` +#### Extending Locker + +You can pass in an implementation of the [Storage Interface](https://developer.mozilla.org/en-US/docs/Web/API/Storage) to the `lockerProvider` as described above. e.g. + +```js +lockerProvider.defaults({ + extend: { + myCustomStore: function () { + // getItem + // setItem + // removeItem + // etc + } + } +}); + +// then use as normal +locker.driver('myCustomStore').put('foo', 'bar'); +``` + +See my [storageMock](https://github.com/tymondesigns/angular-locker/blob/master/test/mock/storageMock.js) for an example on how to define a custom implementation. + ---------------------------- ### Switching storage drivers @@ -96,6 +121,15 @@ locker.put('someKey', function(current) { locker.get('someKey') // = ['foo', 'bar', 'baz'] ``` +If the current value is not already set then you can pass a third parameter as a default that will be returned instead. e.g. + +```js +// given locker.get('foo') is not defined +locker.put('foo', function (current) { + // current will equal 'bar' +}, 'bar'); +``` + #### adding multiple items at once by passing a single object This will add each key/value pair as a **separate** item in storage diff --git a/README.md b/README.md index 99e1dfd..564bece 100644 --- a/README.md +++ b/README.md @@ -65,14 +65,17 @@ Configure via `lockerProvider` (*optional*) ```js .config(['lockerProvider', function config(lockerProvider) { - lockerProvider.setDefaultDriver('session') - .setDefaultNamespace('myAppName') - .setSeparator('.') - .setEventsEnabled(false); + lockerProvider.defaults({ + driver: 'session', + namespace: 'myApp', + separator: '.', + eventsEnabled: true, + extend: {} + }); }]); ``` -*Note*: You can also pass `false` into `setDefaultNamespace()` if you prefer to not have a namespace in your keys. +*Note*: You can also pass `false` to `namespace` if you prefer to not have a namespace in your keys. inject `locker` into your controller/service/directive etc @@ -82,6 +85,28 @@ inject `locker` into your controller/service/directive etc }]); ``` +

Extending Locker

+ +You can pass in an implementation of the [Storage Interface](https://developer.mozilla.org/en-US/docs/Web/API/Storage) to the `lockerProvider` as described above. e.g. + +```js +lockerProvider.defaults({ + extend: { + myCustomStore: function () { + // getItem + // setItem + // removeItem + // etc + } + } +}); + +// then use as normal +locker.driver('myCustomStore').put('foo', 'bar'); +``` + +See my [storageMock](https://github.com/tymondesigns/angular-locker/blob/master/test/mock/storageMock.js) for an example on how to define a custom implementation. + ----------------------------

Switching storage drivers

@@ -149,6 +174,15 @@ locker.put('someKey', function(current) { locker.get('someKey') // = ['foo', 'bar', 'baz'] ``` +If the current value is not already set then you can pass a third parameter as a default that will be returned instead. e.g. + +```js +// given locker.get('foo') is not defined +locker.put('foo', function (current) { + // current will equal 'bar' +}, 'bar'); +``` +

adding multiple items at once by passing a single object

This will add each key/value pair as a **separate** item in storage diff --git a/bower.json b/bower.json index d1b7bb1..0efc92a 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "angular-locker", - "version": "1.2.1", + "version": "2.0.0", "homepage": "https://github.com/tymondesigns/angular-locker", "authors": [ "Sean Tymon " diff --git a/changes.md b/changes.md index 4856df1..d7e2301 100644 --- a/changes.md +++ b/changes.md @@ -15,4 +15,11 @@ lockerProvider.defaults({ ##### General -- Reduced size of minified file by removing *now* unnecessary functions. +- Added ability to extend locker at the config stage +- Added `keys()` method to return an array of keys that exist within the current driver/namespace +- Reduced size of minified file by removing *now* unnecessary functions +- Adding third default parameter to `put()` method +- Hugely refactored and simplified Gulp build process +- Added [jscs](http://jscs.info/) to enforce coding style +- Namespaces can now contain the separator without any issues +- Lots of micro optimisations \ No newline at end of file diff --git a/dist/angular-locker.min.js b/dist/angular-locker.min.js index 6086897..9835ec0 100644 --- a/dist/angular-locker.min.js +++ b/dist/angular-locker.min.js @@ -1,3 +1,3 @@ -/*! angular-locker v1.2.1 | (c) 2015 @tymondesigns | https://github.com/tymondesigns/angular-locker */ +/*! angular-locker v2.0.0 | (c) 2015 @tymondesigns | https://github.com/tymondesigns/angular-locker */ !function(t,e){"function"==typeof define&&define.amd?define(function(){return e(t.angular)}):"object"==typeof exports?module.exports=e(t.angular||window&&window.angular):e(t.angular)}(this,function(t){"use strict";t.module("angular-locker",[]).provider("locker",function(){var e=function(e,r){return t.isFunction(e)?e(r):e},r=function(e){return t.isDefined(e)&&null!==e},i=function(t){throw new Error("[angular-locker] "+t)},s={driver:"local",namespace:"locker",eventsEnabled:!0,separator:".",extend:{}};return{defaults:function(e){return r(e)?void t.forEach(e,function(t,e){s.hasOwnProperty(e)&&(s[e]=t)}):s},$get:["$window","$rootScope","$parse",function(n,o,h){function a(s){this._options=s,this._registeredDrivers=t.extend({local:n.localStorage,session:n.sessionStorage},s.extend),this._resolveDriver=function(t){return this._registeredDrivers.hasOwnProperty(t)||i('The driver "'+t+'" was not found.'),this._registeredDrivers[t]},this._driver=this._resolveDriver(s.driver),this._namespace=s.namespace,this._separator=s.separator,this._watchers={},this._checkSupport=function(t){if(!r(this._supported)){var e="l";try{this._resolveDriver(t||"local").setItem(e,e),this._resolveDriver(t||"local").removeItem(e),this._supported=!0}catch(i){this._supported=!1}}return this._supported},this._getPrefix=function(t){return this._namespace?this._namespace+this._separator+t:t},this._serialize=function(e){try{return t.toJson(e)}catch(r){return e}},this._unserialize=function(e){try{return t.fromJson(e)}catch(r){return e}},this._event=function(e,r){this._options.eventsEnabled&&o.$emit(e,t.extend(r,{driver:this._options.driver,namespace:this._namespace}))},this._setItem=function(e,r){this._checkSupport()||i("The browser does not support localStorage");try{var s=this._getItem(e);this._driver.setItem(this._getPrefix(e),this._serialize(r)),this._exists(e)&&!t.equals(s,r)?this._event("locker.item.updated",{key:e,oldValue:s,newValue:r}):this._event("locker.item.added",{key:e,value:r})}catch(n){i(-1!==["QUOTA_EXCEEDED_ERR","NS_ERROR_DOM_QUOTA_REACHED","QuotaExceededError"].indexOf(n.name)?"The browser storage quota has been exceeded":'Could not add item with key "'+e+'"')}},this._getItem=function(t){return this._checkSupport()||i("The browser does not support localStorage"),this._unserialize(this._driver.getItem(this._getPrefix(t)))},this._exists=function(t){return this._checkSupport()||i("The browser does not support localStorage"),this._driver.hasOwnProperty(this._getPrefix(e(t)))},this._removeItem=function(t){return this._checkSupport()||i("The browser does not support localStorage"),this._exists(t)?(this._driver.removeItem(this._getPrefix(t)),this._event("locker.item.forgotten",{key:t}),!0):!1}}return a.prototype={put:function(i,s,n){if(!r(i))return!1;if(i=e(i),t.isObject(i))t.forEach(i,function(t,e){this._setItem(e,r(t)?t:n)},this);else{if(!r(s))return!1;var o=this._getItem(i);this._setItem(i,e(s,r(o)?o:n))}return this},add:function(t,e,r){return this.has(t)?!1:(this.put(t,e,r),!0)},get:function(e,r){if(t.isArray(e)){var i={};return t.forEach(e,function(t){this.has(t)&&(i[t]=this._getItem(t))},this),i}return this.has(e)?this._getItem(e):2===arguments.length?r:void 0},has:function(t){return this._exists(t)},forget:function(r){return r=e(r),t.isArray(r)?r.map(this._removeItem,this):this._removeItem(r),this},pull:function(t,e){var r=this.get(t,e);return this.forget(t),r},all:function(){var e={};return t.forEach(this._driver,function(t,r){if(this._namespace){var i=this._namespace+this._separator;0===r.indexOf(i)&&(r=r.substring(i.length))}this.has(r)&&(e[r]=this.get(r))},this),e},keys:function(){return Object.keys(this.all())},clean:function(){return this.forget(this.keys())},empty:function(){return this._driver.clear(),this},count:function(){return this.keys().length},bind:function(e,i,s){r(e.$eval(i))||(h(i).assign(e,this.get(i,s)),this.add(i,s));var n=this;return this._watchers[i+e.$id]=e.$watch(i,function(t){n.put(i,t)},t.isObject(e[i])),this},unbind:function(t,e){h(e).assign(t,void 0),this.forget(e);var r=e+t.$id;return this._watchers[r]&&(this._watchers[r](),delete this._watchers[r]),this},driver:function(e){return e===this._options.driver?this:this.instance(t.extend(this._options,{driver:e}))},getDriver:function(){return this._driver},namespace:function(e){return e===this._namespace?this:this.instance(t.extend(this._options,{namespace:e}))},getNamespace:function(){return this._namespace},supported:function(t){return this._checkSupport(t)},instance:function(t){return new a(t)}},new a(s)}]}})}); //# sourceMappingURL=angular-locker.min.js.map \ No newline at end of file diff --git a/dist/angular-locker.min.js.map b/dist/angular-locker.min.js.map index 36b9d29..500499b 100644 --- a/dist/angular-locker.min.js.map +++ b/dist/angular-locker.min.js.map @@ -1,2 +1,2 @@ -/*! angular-locker v1.2.1 | (c) 2015 @tymondesigns | https://github.com/tymondesigns/angular-locker */ +/*! angular-locker v2.0.0 | (c) 2015 @tymondesigns | https://github.com/tymondesigns/angular-locker */ {"version":3,"sources":["angular-locker.min.js"],"names":["root","factory","define","amd","angular","exports","module","window","this","provider","_value","value","param","isFunction","_defined","isDefined","_error","msg","Error","defaults","driver","namespace","eventsEnabled","separator","extend","forEach","val","key","hasOwnProperty","$get","$window","$rootScope","$parse","Locker","options","_options","_registeredDrivers","local","localStorage","session","sessionStorage","_resolveDriver","_driver","_namespace","_separator","_watchers","_checkSupport","_supported","l","setItem","removeItem","e","_getPrefix","_serialize","toJson","_unserialize","fromJson","_event","name","payload","$emit","_setItem","oldVal","_getItem","_exists","equals","oldValue","newValue","indexOf","getItem","_removeItem","prototype","put","def","isObject","add","has","get","isArray","items","k","arguments","length","forget","map","pull","all","prefix","substring","keys","Object","clean","empty","clear","count","bind","$scope","$eval","assign","self","$id","$watch","newVal","unbind","watchId","instance","getDriver","getNamespace","supported"],"mappings":"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","file":"angular-locker.min.js","sourcesContent":["/**\n * angular-locker\n *\n * A simple & configurable abstraction for local/session storage in angular projects.\n *\n * @link https://github.com/tymondesigns/angular-locker\n * @author Sean Tymon @tymondesigns\n * @license MIT License, http://www.opensource.org/licenses/MIT\n */\n\n(function (root, factory) {\n if (typeof define === 'function' && define.amd) {\n define(function () {\n return factory(root.angular);\n });\n } else if (typeof exports === 'object') {\n module.exports = factory(root.angular || (window && window.angular));\n } else {\n factory(root.angular);\n }\n})(this, function (angular) {\n\n 'use strict';\n\n angular.module('angular-locker', [])\n\n .provider('locker', function () {\n\n /**\n * If value is a function then execute, otherwise return\n *\n * @param {Mixed} value\n * @param {Mixed} param\n *\n * @return {Mixed}\n */\n var _value = function (value, param) {\n return angular.isFunction(value) ? value(param) : value;\n };\n\n /**\n * Determine whether a value is defined and not null\n *\n * @param {Mixed} value\n *\n * @return {Boolean}\n */\n var _defined = function (value) {\n return angular.isDefined(value) && value !== null;\n };\n\n /**\n * Trigger an error\n *\n * @param {String} msg\n */\n var _error = function (msg) {\n throw new Error('[angular-locker] ' + msg);\n };\n\n /**\n * Set the defaults\n *\n * @type {Object}\n */\n var defaults = {\n driver: 'local',\n namespace: 'locker',\n eventsEnabled: true,\n separator: '.',\n extend: {}\n };\n\n return {\n\n /**\n * Allow the defaults to be specified via the `lockerProvider`\n *\n * @param {Object} value\n */\n defaults: function (value) {\n if (! _defined(value)) return defaults;\n\n angular.forEach(value, function (val, key) {\n if (defaults.hasOwnProperty(key)) defaults[key] = val;\n });\n },\n\n /**\n * The locker service\n */\n $get: ['$window', '$rootScope', '$parse', function ($window, $rootScope, $parse) {\n\n /**\n * Define the Locker class\n *\n * @param {Object} options\n */\n function Locker (options) {\n\n /**\n * The config options\n *\n * @type {Object}\n */\n this._options = options;\n\n /**\n * Out of the box drivers\n *\n * @type {Object}\n */\n this._registeredDrivers = angular.extend({\n local: $window.localStorage,\n session: $window.sessionStorage\n }, options.extend);\n\n /**\n * Get the Storage instance from the key\n *\n * @param {String} driver\n *\n * @return {Storage}\n */\n this._resolveDriver = function (driver) {\n if (! this._registeredDrivers.hasOwnProperty(driver)) {\n _error('The driver \"' + driver + '\" was not found.');\n }\n\n return this._registeredDrivers[driver];\n };\n\n /**\n * The driver instance\n *\n * @type {Storage}\n */\n this._driver = this._resolveDriver(options.driver);\n\n /**\n * The namespace value\n *\n * @type {String}\n */\n this._namespace = options.namespace;\n\n /**\n * Separates the namespace from the keys\n *\n * @type {String}\n */\n this._separator = options.separator;\n\n /**\n * Store the watchers here so we can un-register them later\n *\n * @type {Object}\n */\n this._watchers = {};\n\n /**\n * Check browser support\n *\n * @see github.com/Modernizr/Modernizr/blob/master/feature-detects/storage/localstorage.js#L38-L47\n * @param {String} driver\n *\n * @return {Boolean}\n */\n this._checkSupport = function (driver) {\n if (! _defined(this._supported)) {\n var l = 'l';\n try {\n this._resolveDriver(driver || 'local').setItem(l, l);\n this._resolveDriver(driver || 'local').removeItem(l);\n this._supported = true;\n } catch (e) {\n this._supported = false;\n }\n }\n\n return this._supported;\n };\n\n /**\n * Build the storage key from the namspace\n *\n * @param {String} key\n *\n * @return {String}\n */\n this._getPrefix = function (key) {\n if (! this._namespace) return key;\n\n return this._namespace + this._separator + key;\n };\n\n /**\n * Try to encode value as json, or just return the value upon failure\n *\n * @param {Mixed} value\n *\n * @return {Mixed}\n */\n this._serialize = function (value) {\n try {\n return angular.toJson(value);\n } catch (e) {\n return value;\n }\n };\n\n /**\n * Try to parse value as json, if it fails then it probably isn't json\n * so just return it\n *\n * @param {String} value\n *\n * @return {Mixed}\n */\n this._unserialize = function (value) {\n try {\n return angular.fromJson(value);\n } catch (e) {\n return value;\n }\n };\n\n /**\n * Trigger an event\n *\n * @param {String} name\n * @param {Object} payload\n */\n this._event = function (name, payload) {\n if (this._options.eventsEnabled) {\n $rootScope.$emit(name, angular.extend(payload, {\n driver: this._options.driver,\n namespace: this._namespace,\n }));\n }\n };\n\n /**\n * Add to storage\n *\n * @param {String} key\n *\n * @param {Mixed} value\n */\n this._setItem = function (key, value) {\n if (! this._checkSupport()) _error('The browser does not support localStorage');\n\n try {\n var oldVal = this._getItem(key);\n this._driver.setItem(this._getPrefix(key), this._serialize(value));\n if (this._exists(key) && ! angular.equals(oldVal, value)) {\n this._event('locker.item.updated', { key: key, oldValue: oldVal, newValue: value });\n } else {\n this._event('locker.item.added', { key: key, value: value });\n }\n } catch (e) {\n if (['QUOTA_EXCEEDED_ERR',\n 'NS_ERROR_DOM_QUOTA_REACHED',\n 'QuotaExceededError'].indexOf(e.name) !== -1) {\n _error('The browser storage quota has been exceeded');\n } else {\n _error('Could not add item with key \"' + key + '\"');\n }\n }\n };\n\n /**\n * Get from storage\n *\n * @param {String} key\n *\n * @return {Mixed}\n */\n this._getItem = function (key) {\n if (! this._checkSupport()) _error('The browser does not support localStorage');\n\n return this._unserialize(this._driver.getItem(this._getPrefix(key)));\n };\n\n /**\n * Exists in storage\n *\n * @param {String} key\n *\n * @return {Boolean}\n */\n this._exists = function (key) {\n if (! this._checkSupport()) _error('The browser does not support localStorage');\n\n return this._driver.hasOwnProperty(this._getPrefix(_value(key)));\n };\n\n /**\n * Remove from storage\n *\n * @param {String} key\n *\n * @return {Boolean}\n */\n this._removeItem = function (key) {\n if (! this._checkSupport()) _error('The browser does not support localStorage');\n\n if (! this._exists(key)) return false;\n\n this._driver.removeItem(this._getPrefix(key));\n this._event('locker.item.forgotten', { key: key });\n\n return true;\n };\n }\n\n /**\n * Define the public api\n *\n * @type {Object}\n */\n Locker.prototype = {\n\n /**\n * Add a new item to storage (even if it already exists)\n *\n * @param {Mixed} key\n * @param {Mixed} value\n * @param {Mixed} def\n *\n * @return {Locker|Boolean}\n */\n put: function (key, value, def) {\n if (! _defined(key)) return false;\n key = _value(key);\n\n if (angular.isObject(key)) {\n angular.forEach(key, function (value, key) {\n this._setItem(key, _defined(value) ? value : def);\n }, this);\n } else {\n if (! _defined(value)) return false;\n var val = this._getItem(key);\n this._setItem(key, _value(value, _defined(val) ? val : def));\n }\n\n return this;\n },\n\n /**\n * Add an item to storage if it doesn't already exist\n *\n * @param {Mixed} key\n * @param {Mixed} value\n * @param {Mixed} def\n *\n * @return {Boolean}\n */\n add: function (key, value, def) {\n if (! this.has(key)) {\n this.put(key, value, def);\n return true;\n }\n\n return false;\n },\n\n /**\n * Retrieve the specified item from storage\n *\n * @param {String|Array} key\n * @param {Mixed} def\n *\n * @return {Mixed}\n */\n get: function (key, def) {\n if (angular.isArray(key)) {\n var items = {};\n angular.forEach(key, function (k) {\n if (this.has(k)) items[k] = this._getItem(k);\n }, this);\n\n return items;\n }\n\n if (! this.has(key)) return arguments.length === 2 ? def : void 0;\n\n return this._getItem(key);\n },\n\n /**\n * Determine whether the item exists in storage\n *\n * @param {String|Function} key\n *\n * @return {Boolean}\n */\n has: function (key) {\n return this._exists(key);\n },\n\n /**\n * Remove specified item(s) from storage\n *\n * @param {Mixed} key\n *\n * @return {Object}\n */\n forget: function (key) {\n key = _value(key);\n\n if (angular.isArray(key)) {\n key.map(this._removeItem, this);\n } else {\n this._removeItem(key);\n }\n\n return this;\n },\n\n /**\n * Retrieve the specified item from storage and then remove it\n *\n * @param {String|Array} key\n * @param {Mixed} def\n *\n * @return {Mixed}\n */\n pull: function (key, def) {\n var value = this.get(key, def);\n this.forget(key);\n\n return value;\n },\n\n /**\n * Return all items in storage within the current namespace/driver\n *\n * @return {Object}\n */\n all: function () {\n var items = {};\n angular.forEach(this._driver, function (value, key) {\n if (this._namespace) {\n var prefix = this._namespace + this._separator;\n if (key.indexOf(prefix) === 0) key = key.substring(prefix.length);\n }\n if (this.has(key)) items[key] = this.get(key);\n }, this);\n\n return items;\n },\n\n /**\n * Get the storage keys as an array\n *\n * @return {Array}\n */\n keys: function () {\n return Object.keys(this.all());\n },\n\n /**\n * Remove all items set within the current namespace/driver\n *\n * @return {self}\n */\n clean: function () {\n return this.forget(this.keys());\n },\n\n /**\n * Empty the current storage driver completely. careful now.\n *\n * @return {Locker}\n */\n empty: function () {\n this._driver.clear();\n\n return this;\n },\n\n /**\n * Get the total number of items within the current namespace\n *\n * @return {Integer}\n */\n count: function () {\n return this.keys().length;\n },\n\n /**\n * Bind a storage key to a $scope property\n *\n * @param {Object} $scope\n * @param {String} key\n * @param {Mixed} def\n *\n * @return {Locker}\n */\n bind: function ($scope, key, def) {\n if (! _defined( $scope.$eval(key) )) {\n $parse(key).assign($scope, this.get(key, def));\n this.add(key, def);\n }\n\n var self = this;\n this._watchers[key + $scope.$id] = $scope.$watch(key, function (newVal) {\n self.put(key, newVal);\n }, angular.isObject($scope[key]));\n\n return this;\n },\n\n /**\n * Unbind a storage key from a $scope property\n *\n * @param {Object} $scope\n * @param {String} key\n *\n * @return {Locker}\n */\n unbind: function ($scope, key) {\n $parse(key).assign($scope, void 0);\n this.forget(key);\n\n var watchId = key + $scope.$id;\n\n if (this._watchers[watchId]) {\n // execute the de-registration function\n this._watchers[watchId]();\n delete this._watchers[watchId];\n }\n\n return this;\n },\n\n /**\n * Set the storage driver on a new instance to enable overriding defaults\n *\n * @param {String} driver\n *\n * @return {Locker}\n */\n driver: function (driver) {\n // no need to create a new instance if the driver is the same\n if (driver === this._options.driver) return this;\n\n return this.instance(angular.extend(this._options, { driver: driver }));\n },\n\n /**\n * Get the currently set driver\n *\n * @return {Storage}\n */\n getDriver: function () {\n return this._driver;\n },\n\n /**\n * Set the namespace on a new instance to enable overriding defaults\n *\n * @param {String} namespace\n *\n * @return {Locker}\n */\n namespace: function (namespace) {\n // no need to create a new instance if the namespace is the same\n if (namespace === this._namespace) return this;\n\n return this.instance(angular.extend(this._options, { namespace: namespace }));\n },\n\n /**\n * Get the currently set namespace\n *\n * @return {String}\n */\n getNamespace: function () {\n return this._namespace;\n },\n\n /**\n * Check browser support\n *\n * @see github.com/Modernizr/Modernizr/blob/master/feature-detects/storage/localstorage.js#L38-L47\n * @param {String} driver\n * @return {Boolean}\n */\n supported: function (driver) {\n return this._checkSupport(driver);\n },\n\n /**\n * Get a new instance of Locker\n *\n * @param {Object} options\n *\n * @return {Locker}\n */\n instance: function (options) {\n return new Locker(options);\n }\n };\n\n // return the default instance\n return new Locker(defaults);\n }]\n };\n\n });\n\n});\n"],"sourceRoot":"/source/"} \ No newline at end of file diff --git a/package.json b/package.json index abfa629..1e9acd0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "angular-locker", - "version": "1.2.1", + "version": "2.0.0", "description": "A simple & configurable abstraction for local/session storage in angular projects", "author": "@tymondesigns", "license": "MIT",