mirror of
https://github.com/haiwen/seahub.git
synced 2025-08-18 06:57:52 +00:00
299 lines
7.5 KiB
JavaScript
299 lines
7.5 KiB
JavaScript
|
define(['util/json2'],function(){
|
||
|
var amplify = {};
|
||
|
|
||
|
/*!
|
||
|
* Amplify Store - Persistent Client-Side Storage 1.1.0
|
||
|
*
|
||
|
* Copyright 2011 appendTo LLC. (http://appendto.com/team)
|
||
|
* Dual licensed under the MIT or GPL licenses.
|
||
|
* http://appendto.com/open-source-licenses
|
||
|
*
|
||
|
* http://amplifyjs.com
|
||
|
*/
|
||
|
(function( amplify, undefined ) {
|
||
|
|
||
|
var store = amplify.store = function( key, value, options, type ) {
|
||
|
var type = store.type;
|
||
|
if ( options && options.type && options.type in store.types ) {
|
||
|
type = options.type;
|
||
|
}
|
||
|
return store.types[ type ]( key, value, options || {} );
|
||
|
};
|
||
|
|
||
|
store.types = {};
|
||
|
store.type = null;
|
||
|
store.addType = function( type, storage ) {
|
||
|
if ( !store.type ) {
|
||
|
store.type = type;
|
||
|
}
|
||
|
|
||
|
store.types[ type ] = storage;
|
||
|
store[ type ] = function( key, value, options ) {
|
||
|
options = options || {};
|
||
|
options.type = type;
|
||
|
return store( key, value, options );
|
||
|
};
|
||
|
}
|
||
|
store.error = function() {
|
||
|
return "amplify.store quota exceeded";
|
||
|
};
|
||
|
|
||
|
var rprefix = /^__amplify__/;
|
||
|
function createFromStorageInterface( storageType, storage ) {
|
||
|
store.addType( storageType, function( key, value, options ) {
|
||
|
var storedValue, parsed, i, remove,
|
||
|
ret = value,
|
||
|
now = (new Date()).getTime();
|
||
|
|
||
|
if ( !key ) {
|
||
|
ret = {};
|
||
|
remove = [];
|
||
|
i = 0;
|
||
|
try {
|
||
|
// accessing the length property works around a localStorage bug
|
||
|
// in Firefox 4.0 where the keys don't update cross-page
|
||
|
// we assign to key just to avoid Closure Compiler from removing
|
||
|
// the access as "useless code"
|
||
|
// https://bugzilla.mozilla.org/show_bug.cgi?id=662511
|
||
|
key = storage.length;
|
||
|
|
||
|
while ( key = storage.key( i++ ) ) {
|
||
|
if ( rprefix.test( key ) ) {
|
||
|
parsed = JSON.parse( storage.getItem( key ) );
|
||
|
if ( parsed.expires && parsed.expires <= now ) {
|
||
|
remove.push( key );
|
||
|
} else {
|
||
|
ret[ key.replace( rprefix, "" ) ] = parsed.data;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
while ( key = remove.pop() ) {
|
||
|
storage.removeItem( key );
|
||
|
}
|
||
|
} catch ( error ) {}
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
// protect against name collisions with direct storage
|
||
|
key = "__amplify__" + key;
|
||
|
|
||
|
if ( value === undefined ) {
|
||
|
storedValue = storage.getItem( key );
|
||
|
parsed = storedValue ? JSON.parse( storedValue ) : { expires: -1 };
|
||
|
if ( parsed.expires && parsed.expires <= now ) {
|
||
|
storage.removeItem( key );
|
||
|
} else {
|
||
|
return parsed.data;
|
||
|
}
|
||
|
} else {
|
||
|
if ( value === null ) {
|
||
|
storage.removeItem( key );
|
||
|
} else {
|
||
|
parsed = JSON.stringify({
|
||
|
data: value,
|
||
|
expires: options.expires ? now + options.expires : null
|
||
|
});
|
||
|
try {
|
||
|
storage.setItem( key, parsed );
|
||
|
// quota exceeded
|
||
|
} catch( error ) {
|
||
|
// expire old data and try again
|
||
|
store[ storageType ]();
|
||
|
try {
|
||
|
storage.setItem( key, parsed );
|
||
|
} catch( error ) {
|
||
|
throw store.error();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
});
|
||
|
}
|
||
|
|
||
|
// localStorage + sessionStorage
|
||
|
// IE 8+, Firefox 3.5+, Safari 4+, Chrome 4+, Opera 10.5+, iPhone 2+, Android 2+
|
||
|
for ( var webStorageType in { localStorage: 1, sessionStorage: 1 } ) {
|
||
|
// try/catch for file protocol in Firefox
|
||
|
try {
|
||
|
if ( window[ webStorageType ].getItem ) {
|
||
|
createFromStorageInterface( webStorageType, window[ webStorageType ] );
|
||
|
}
|
||
|
} catch( e ) {}
|
||
|
}
|
||
|
|
||
|
// globalStorage
|
||
|
// non-standard: Firefox 2+
|
||
|
// https://developer.mozilla.org/en/dom/storage#globalStorage
|
||
|
if ( window.globalStorage ) {
|
||
|
// try/catch for file protocol in Firefox
|
||
|
try {
|
||
|
createFromStorageInterface( "globalStorage",
|
||
|
window.globalStorage[ window.location.hostname ] );
|
||
|
// Firefox 2.0 and 3.0 have sessionStorage and globalStorage
|
||
|
// make sure we default to globalStorage
|
||
|
// but don't default to globalStorage in 3.5+ which also has localStorage
|
||
|
if ( store.type === "sessionStorage" ) {
|
||
|
store.type = "globalStorage";
|
||
|
}
|
||
|
} catch( e ) {}
|
||
|
}
|
||
|
|
||
|
// userData
|
||
|
// non-standard: IE 5+
|
||
|
// http://msdn.microsoft.com/en-us/library/ms531424(v=vs.85).aspx
|
||
|
(function() {
|
||
|
// IE 9 has quirks in userData that are a huge pain
|
||
|
// rather than finding a way to detect these quirks
|
||
|
// we just don't register userData if we have localStorage
|
||
|
if ( store.types.localStorage ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// append to html instead of body so we can do this from the head
|
||
|
var div = document.createElement( "div" ),
|
||
|
attrKey = "amplify";
|
||
|
div.style.display = "none";
|
||
|
document.getElementsByTagName( "head" )[ 0 ].appendChild( div );
|
||
|
|
||
|
// we can't feature detect userData support
|
||
|
// so just try and see if it fails
|
||
|
// surprisingly, even just adding the behavior isn't enough for a failure
|
||
|
// so we need to load the data as well
|
||
|
try {
|
||
|
div.addBehavior( "#default#userdata" );
|
||
|
div.load( attrKey );
|
||
|
} catch( e ) {
|
||
|
div.parentNode.removeChild( div );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
store.addType( "userData", function( key, value, options ) {
|
||
|
div.load( attrKey );
|
||
|
var attr, parsed, prevValue, i, remove,
|
||
|
ret = value,
|
||
|
now = (new Date()).getTime();
|
||
|
|
||
|
if ( !key ) {
|
||
|
ret = {};
|
||
|
remove = [];
|
||
|
i = 0;
|
||
|
while ( attr = div.XMLDocument.documentElement.attributes[ i++ ] ) {
|
||
|
parsed = JSON.parse( attr.value );
|
||
|
if ( parsed.expires && parsed.expires <= now ) {
|
||
|
remove.push( attr.name );
|
||
|
} else {
|
||
|
ret[ attr.name ] = parsed.data;
|
||
|
}
|
||
|
}
|
||
|
while ( key = remove.pop() ) {
|
||
|
div.removeAttribute( key );
|
||
|
}
|
||
|
div.save( attrKey );
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
// convert invalid characters to dashes
|
||
|
// http://www.w3.org/TR/REC-xml/#NT-Name
|
||
|
// simplified to assume the starting character is valid
|
||
|
// also removed colon as it is invalid in HTML attribute names
|
||
|
key = key.replace( /[^-._0-9A-Za-z\xb7\xc0-\xd6\xd8-\xf6\xf8-\u037d\u37f-\u1fff\u200c-\u200d\u203f\u2040\u2070-\u218f]/g, "-" );
|
||
|
|
||
|
if ( value === undefined ) {
|
||
|
attr = div.getAttribute( key );
|
||
|
parsed = attr ? JSON.parse( attr ) : { expires: -1 };
|
||
|
if ( parsed.expires && parsed.expires <= now ) {
|
||
|
div.removeAttribute( key );
|
||
|
} else {
|
||
|
return parsed.data;
|
||
|
}
|
||
|
} else {
|
||
|
if ( value === null ) {
|
||
|
div.removeAttribute( key );
|
||
|
} else {
|
||
|
// we need to get the previous value in case we need to rollback
|
||
|
prevValue = div.getAttribute( key );
|
||
|
parsed = JSON.stringify({
|
||
|
data: value,
|
||
|
expires: (options.expires ? (now + options.expires) : null)
|
||
|
});
|
||
|
div.setAttribute( key, parsed );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
try {
|
||
|
div.save( attrKey );
|
||
|
// quota exceeded
|
||
|
} catch ( error ) {
|
||
|
// roll the value back to the previous value
|
||
|
if ( prevValue === null ) {
|
||
|
div.removeAttribute( key );
|
||
|
} else {
|
||
|
div.setAttribute( key, prevValue );
|
||
|
}
|
||
|
|
||
|
// expire old data and try again
|
||
|
store.userData();
|
||
|
try {
|
||
|
div.setAttribute( key, parsed );
|
||
|
div.save( attrKey );
|
||
|
} catch ( error ) {
|
||
|
// roll the value back to the previous value
|
||
|
if ( prevValue === null ) {
|
||
|
div.removeAttribute( key );
|
||
|
} else {
|
||
|
div.setAttribute( key, prevValue );
|
||
|
}
|
||
|
throw store.error();
|
||
|
}
|
||
|
}
|
||
|
return ret;
|
||
|
});
|
||
|
}() );
|
||
|
|
||
|
// in-memory storage
|
||
|
// fallback for all browsers to enable the API even if we can't persist data
|
||
|
(function() {
|
||
|
var memory = {},
|
||
|
timeout = {};
|
||
|
|
||
|
function copy( obj ) {
|
||
|
return obj === undefined ? undefined : JSON.parse( JSON.stringify( obj ) );
|
||
|
}
|
||
|
|
||
|
store.addType( "memory", function( key, value, options ) {
|
||
|
if ( !key ) {
|
||
|
return copy( memory );
|
||
|
}
|
||
|
|
||
|
if ( value === undefined ) {
|
||
|
return copy( memory[ key ] );
|
||
|
}
|
||
|
|
||
|
if ( timeout[ key ] ) {
|
||
|
clearTimeout( timeout[ key ] );
|
||
|
delete timeout[ key ];
|
||
|
}
|
||
|
|
||
|
if ( value === null ) {
|
||
|
delete memory[ key ];
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
memory[ key ] = value;
|
||
|
if ( options.expires ) {
|
||
|
timeout[ key ] = setTimeout(function() {
|
||
|
delete memory[ key ];
|
||
|
delete timeout[ key ];
|
||
|
}, options.expires );
|
||
|
}
|
||
|
|
||
|
return value;
|
||
|
});
|
||
|
}() );
|
||
|
|
||
|
}( amplify ) );
|
||
|
return amplify;
|
||
|
});
|