Created
September 21, 2013 00:00
-
-
Save JawsomeJason/6645493 to your computer and use it in GitHub Desktop.
Stores inlined JS/CSS into localStorage, and then helps you re-inject it on page reload
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @fileOverview Stores inline JS/CSS into localStorage | |
* @author "Jason T. Featheringham" <[email protected]> | |
* @requires this.localStorage, | |
* this.document, | |
* this.JSON, | |
* this.document.querySelectorAll, | |
* | |
* How It Works: | |
* On your server, when you want some content stored locally instead of retrieved from the server. An example using a script file (script.js), but any DOM element will work. | |
* | |
* 1. Check cookies[:codecache]. | |
* * If the cookie doesn't exist, inline the content: | |
* <script data-codecache="script.js"> alert('hello world'); </script> | |
* * If the cookie === 'false', the browser does not support the right features. Use an old-school script[src]: | |
* <script src="script.js"></script> | |
* * Otherwise, the cookie will be an array of stored [data-codecache] ids. If this script's ID exists, simply create a placeholder: | |
* <script data-codecache="script.js"></script> | |
* 2. It's smarter if your inlined content is in the <head> | |
* 3. Inline this code afterwards | |
* 4. call `new CodeCache();` | |
*/ | |
// the server should not include this code if cookies[:codecache] === 'false' | |
( function( window, undefined ) { | |
// Feature detection | |
var storage = window.localStorage && window.localStorage.getItem && window.localStorage.setItem && window.localStorage, | |
JSON = window.JSON && window.JSON.parse && window.JSON.stringify && window.JSON, | |
document = window.document, | |
query = document && document.querySelectorAll, | |
supported = storage && query && JSON; | |
if( !supported ) { | |
// browser can't support codecache; let the server know so they can use script[src]; | |
document.cookie = 'codecache=false'; | |
} | |
/** | |
* Stores element data in localStorage and injects into page when requested. | |
* @returns {CodeCache} | |
* @constructor | |
* @description | |
* Value of attribute is identifier | |
* If element is empty, populate with stored data. | |
* Otherwise, store current element's data | |
*/ | |
var CodeCache = function() { | |
var codecache = 'codecache', | |
// find existing cookie values representing keys stored items, if anything | |
match = document.cookie.match( new RegExp( '(^|;) ?' + codecache + '=([^;]*)(;|$)' ) ), | |
// find existing localstorage items, if anything | |
store = JSON.parse( storage.getItem( codecache ) ) || {}; | |
this._store = store; | |
return this.scan(); | |
}; | |
/** | |
* Gets keys from the store | |
* @returns {Array} | |
*/ | |
CodeCache.prototype.keys = function() { | |
var keys = [], | |
key; | |
if( 'keys' in Object ) { | |
return Object.keys( this._store ); | |
} | |
else { | |
for( key in this._store ) { | |
this._store.hasOwnProperty( key ) && keys.push( key ); | |
} | |
return keys; | |
} | |
}; | |
/** | |
* Gets and sets cached code | |
* @param key | |
* @param value | |
* @returns {Object|String|CodeCache} | |
*/ | |
CodeCache.prototype.store = function( key, value ) { | |
var _this = this, | |
update = function() { | |
window.localStorage.setItem( 'codecache', JSON.stringify( _this._store ) ); | |
document.cookie = 'codecache=' + JSON.stringify( _this.keys() ); | |
}; | |
// get all keys | |
if( key === undefined ) { | |
return this._store; | |
} | |
// get specific key value | |
else if( value === undefined ) { | |
return this._store.hasOwnProperty( key ) && this._store[ key ] || undefined; | |
} | |
// delete a key | |
else if( value === null && this._store.hasOwnProperty( key ) ) { | |
delete this._store[ key ]; | |
update(); | |
return this; | |
} | |
// set a key | |
else { | |
this._store[ key ] = value; | |
update(); | |
return this; | |
} | |
}; | |
/** determines property name for the inner content of a node */ | |
CodeCache.prototype._textProp = document && document.head && 'textContent' in document.head && 'textContent' || 'innerText'; | |
CodeCache.prototype.scan = function() { | |
// get all items that are set to be stored | |
var scripts = document.querySelectorAll( '[data-codecache]'), | |
i, max, script, | |
isEmpty = function( text ) { return text.replace(/^\s\s*/, '').replace(/\s\s*$/, '') === ''; }; | |
for( i = 0, max = scripts.length; i < max; i++ ) { | |
script = scripts[i]; | |
if( isEmpty( script[this._textProp] ) ) { | |
// needs us to fill it in | |
this.inject( script ); | |
} | |
else { | |
// we need to store it in localStorage | |
this.add( script ); | |
} | |
} | |
return this; | |
}; | |
/** | |
* Adds a DOM element to cache | |
* @param {Node} node | |
* @returns {CodeCache} | |
*/ | |
CodeCache.prototype.add = function( node ) { | |
var cachekey = node.attributes.getNamedItem('data-codecache').value, | |
content = node[ this._textProp ]; | |
this.store( cachekey, content ); | |
return this; | |
}; | |
/** | |
* Inject a cached element into the DOM | |
* @param {Node} node | |
* @returns {CodeCache} | |
*/ | |
CodeCache.prototype.inject = function( node ) { | |
var parent = node.parentNode, | |
newNode = node.cloneNode( true ), | |
cachekey = node.attributes.getNamedItem('data-codecache').value; | |
newNode[ this._textProp ] = this.store( cachekey ); | |
parent.insertBefore( newNode, node ); | |
parent.removeChild( node ); | |
return this; | |
} | |
// expose to global | |
window.CodeCache = CodeCache; | |
} )( this ); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment