Add simplified storage API
Provide a standard mechanism for accessing localStorage. It may seems simplistic right now, but to give an idea of the why: * We already have jquery.jStorage.js which is a much more heavyweight approach to storing non-essential values. * We are repeating ourselves a lot in extensions by having to do localStorage detection and then deal with full localStorage. In MobileFrontend we have a settings module. This is one of the reasons Gather depends on MobileFrontend and I'm keen to remove that dependency. * We might want to move to indexdb in future. Having a single API makes moving this easier - we don't have to update everywhere that uses localStorage * Saving non-string support would be useful. The API could be adjusted to take a mixed second parameter that stringifys JSON objects. * Cookie fallbacks are a possible alternative when localStorage is not supported. This allows us to be agnostic of the storage mechanism going forward. Note: This doesn't reuse the handling in mediawiki.js as at this point I am not sure there is value. mw.loader.store.enabled is false when $wgResourceLoaderStorageEnabled is not true and this should work even without that case. We can review this at a latter point. See: Id5c32bb7a662dda8d153490f7c47e972cabc1efd I3fd44b0ae6633a7053aee247bc3c4704ba987bc8 Bug: T96155 Change-Id: Idb37352acecd745beb53aa8d77ea050851448e0d
This commit is contained in:
parent
6147de1182
commit
7daab75414
5 changed files with 129 additions and 0 deletions
|
|
@ -27,6 +27,7 @@
|
|||
"mw.messagePoster.*",
|
||||
"mw.notification",
|
||||
"mw.Notification_",
|
||||
"mw.storage",
|
||||
"mw.user",
|
||||
"mw.util",
|
||||
"mw.plugin.*",
|
||||
|
|
|
|||
|
|
@ -1092,6 +1092,10 @@ return array(
|
|||
'styles' => 'resources/src/mediawiki/mediawiki.sectionAnchor.css',
|
||||
'targets' => array( 'desktop', 'mobile' ),
|
||||
),
|
||||
'mediawiki.storage' => array(
|
||||
'scripts' => 'resources/src/mediawiki/mediawiki.storage.js',
|
||||
'targets' => array( 'desktop', 'mobile' ),
|
||||
),
|
||||
'mediawiki.Title' => array(
|
||||
'scripts' => 'resources/src/mediawiki/mediawiki.Title.js',
|
||||
'dependencies' => array(
|
||||
|
|
|
|||
69
resources/src/mediawiki/mediawiki.storage.js
Normal file
69
resources/src/mediawiki/mediawiki.storage.js
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
( function ( mw ) {
|
||||
'use strict';
|
||||
var storage;
|
||||
|
||||
/**
|
||||
* Library for storing device specific information. It should be used for storing simple
|
||||
* strings and is not suitable for storing large chunks of data.
|
||||
* @class mw.storage
|
||||
* @singleton
|
||||
*/
|
||||
storage = {
|
||||
isLocalStorageSupported: false,
|
||||
/**
|
||||
* Retrieve value from device storage.
|
||||
*
|
||||
* @param {String} key of item to retrieve
|
||||
* @returns {String|Boolean} false when localStorage not available, otherwise string
|
||||
*/
|
||||
get: function ( key ) {
|
||||
if ( this.isLocalStorageSupported ) {
|
||||
return localStorage.getItem( key );
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Set a value in device storage.
|
||||
*
|
||||
* @param {String} key key name to store under.
|
||||
* @param {String} value to be stored.
|
||||
* @returns {Boolean} whether the save succeeded or not.
|
||||
*/
|
||||
set: function ( key, value ) {
|
||||
try {
|
||||
localStorage.setItem( key, value );
|
||||
return true;
|
||||
} catch ( e ) {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove a value from device storage.
|
||||
*
|
||||
* @param {String} key of item to remove.
|
||||
* @returns {Boolean} whether the save succeeded or not.
|
||||
*/
|
||||
remove: function ( key ) {
|
||||
if ( this.isLocalStorageSupported ) {
|
||||
localStorage.removeItem( key );
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
mw.storage = storage;
|
||||
// See if local storage is supported
|
||||
try {
|
||||
localStorage.setItem( 'localStorageTest', 'localStorageTest' );
|
||||
localStorage.removeItem( 'localStorageTest' );
|
||||
storage.isLocalStorageSupported = true;
|
||||
} catch ( e ) {
|
||||
// Already set. No body needed.
|
||||
}
|
||||
|
||||
}( mediaWiki ) );
|
||||
|
|
@ -68,6 +68,7 @@ return array(
|
|||
'tests/qunit/suites/resources/mediawiki/mediawiki.jscompat.test.js',
|
||||
'tests/qunit/suites/resources/mediawiki/mediawiki.messagePoster.factory.test.js',
|
||||
'tests/qunit/suites/resources/mediawiki/mediawiki.RegExp.test.js',
|
||||
'tests/qunit/suites/resources/mediawiki/mediawiki.storage.test.js',
|
||||
'tests/qunit/suites/resources/mediawiki/mediawiki.template.test.js',
|
||||
'tests/qunit/suites/resources/mediawiki/mediawiki.test.js',
|
||||
'tests/qunit/suites/resources/mediawiki/mediawiki.Title.test.js',
|
||||
|
|
@ -113,6 +114,7 @@ return array(
|
|||
'mediawiki.jqueryMsg',
|
||||
'mediawiki.messagePoster',
|
||||
'mediawiki.RegExp',
|
||||
'mediawiki.storage',
|
||||
'mediawiki.Title',
|
||||
'mediawiki.toc',
|
||||
'mediawiki.Uri',
|
||||
|
|
|
|||
|
|
@ -0,0 +1,53 @@
|
|||
( function ( mw ) {
|
||||
QUnit.module( 'mediawiki.storage: normal case.', {
|
||||
setup: function () {
|
||||
this.sandbox.stub( mw.storage, 'isLocalStorageSupported', true );
|
||||
this.spy = this.sandbox.spy( localStorage, 'setItem' );
|
||||
this.sandbox.stub( localStorage, 'getItem' )
|
||||
.withArgs( 'foo' ).returns( 'test' )
|
||||
.withArgs( 'bar' ).returns( null );
|
||||
}
|
||||
} );
|
||||
|
||||
QUnit.test( 'set/get with localStorage', 4, function ( assert ) {
|
||||
mw.storage.set( 'foo', 'test' );
|
||||
assert.strictEqual( this.spy.calledOnce, true, 'Check localStorage called.' );
|
||||
assert.strictEqual( this.spy.calledWith( 'foo', 'test' ), true,
|
||||
'Check prefixed.' );
|
||||
assert.strictEqual( mw.storage.get( 'foo' ), 'test', 'Check value gets stored.' );
|
||||
assert.strictEqual( mw.storage.get( 'bar' ), null, 'Unset values are null.' );
|
||||
} );
|
||||
|
||||
QUnit.module( 'mediawiki.storage: localStorage does not exist', {
|
||||
setup: function () {
|
||||
this.sandbox.stub( mw.storage, 'isLocalStorageSupported', false );
|
||||
this.sandbox.stub( localStorage, 'setItem' ).throws();
|
||||
}
|
||||
} );
|
||||
|
||||
QUnit.test( 'set/get without localStorage', 3, function ( assert ) {
|
||||
assert.strictEqual( mw.storage.set( 'foo', 'test' ), false,
|
||||
'When localStorage not available save fails.' );
|
||||
|
||||
assert.strictEqual( mw.storage.remove( 'foo', 'test' ), false,
|
||||
'When localStorage not available remove fails.' );
|
||||
|
||||
assert.strictEqual( mw.storage.get( 'foo' ), false,
|
||||
'Inability to retrieve values return false to differentiate from null (not set).' );
|
||||
} );
|
||||
|
||||
QUnit.module( 'mediawiki.storage: localStorage exhausted', {
|
||||
setup: function () {
|
||||
this.sandbox.stub( mw.storage, 'isLocalStorageSupported', true );
|
||||
this.sandbox.stub( localStorage, 'setItem' ).throws();
|
||||
this.sandbox.stub( localStorage, 'getItem' ).returns( null );
|
||||
}
|
||||
} );
|
||||
|
||||
QUnit.test( 'set/get without localStorage', 2, function ( assert ) {
|
||||
assert.strictEqual( mw.storage.set( 'foo', 'test' ), false,
|
||||
'When localStorage not available inform user with false.' );
|
||||
assert.strictEqual( mw.storage.get( 'foo' ), null, 'No value registered.' );
|
||||
} );
|
||||
|
||||
}( mediaWiki ) );
|
||||
Loading…
Reference in a new issue