// oldIE-userDataStorage provides storage for Internet Explorer // versions 6 and 7, where no localStorage, sessionStorage, etc // is available. var util = require('../src/util') var Global = util.Global module.exports = { name: 'oldIE-userDataStorage', write: write, read: read, each: each, remove: remove, clearAll: clearAll, } var storageName = 'storejs' var doc = Global.document var _withStorageEl = _makeIEStorageElFunction() var disable = (Global.navigator ? Global.navigator.userAgent : '').match(/ (MSIE 8|MSIE 9|MSIE 10)\./) // MSIE 9.x, MSIE 10.x function write(unfixedKey, data) { if (disable) { return } var fixedKey = fixKey(unfixedKey) _withStorageEl(function(storageEl) { storageEl.setAttribute(fixedKey, data) storageEl.save(storageName) }) } function read(unfixedKey) { if (disable) { return } var fixedKey = fixKey(unfixedKey) var res = null _withStorageEl(function(storageEl) { res = storageEl.getAttribute(fixedKey) }) return res } function each(callback) { _withStorageEl(function(storageEl) { var attributes = storageEl.XMLDocument.documentElement.attributes for (var i=attributes.length-1; i>=0; i--) { var attr = attributes[i] callback(storageEl.getAttribute(attr.name), attr.name) } }) } function remove(unfixedKey) { var fixedKey = fixKey(unfixedKey) _withStorageEl(function(storageEl) { storageEl.removeAttribute(fixedKey) storageEl.save(storageName) }) } function clearAll() { _withStorageEl(function(storageEl) { var attributes = storageEl.XMLDocument.documentElement.attributes storageEl.load(storageName) for (var i=attributes.length-1; i>=0; i--) { storageEl.removeAttribute(attributes[i].name) } storageEl.save(storageName) }) } // Helpers ////////// // In IE7, keys cannot start with a digit or contain certain chars. // See https://github.com/marcuswestin/store.js/issues/40 // See https://github.com/marcuswestin/store.js/issues/83 var forbiddenCharsRegex = new RegExp("[!\"#$%&'()*+,/\\\\:;<=>?@[\\]^`{|}~]", "g") function fixKey(key) { return key.replace(/^\d/, '___$&').replace(forbiddenCharsRegex, '___') } function _makeIEStorageElFunction() { if (!doc || !doc.documentElement || !doc.documentElement.addBehavior) { return null } var scriptTag = 'script', storageOwner, storageContainer, storageEl // Since #userData storage applies only to specific paths, we need to // somehow link our data to a specific path. We choose /favicon.ico // as a pretty safe option, since all browsers already make a request to // this URL anyway and being a 404 will not hurt us here. We wrap an // iframe pointing to the favicon in an ActiveXObject(htmlfile) object // (see: http://msdn.microsoft.com/en-us/library/aa752574(v=VS.85).aspx) // since the iframe access rules appear to allow direct access and // manipulation of the document element, even for a 404 page. This // document can be used instead of the current document (which would // have been limited to the current path) to perform #userData storage. try { /* global ActiveXObject */ storageContainer = new ActiveXObject('htmlfile') storageContainer.open() storageContainer.write('<'+scriptTag+'>document.w=window') storageContainer.close() storageOwner = storageContainer.w.frames[0].document storageEl = storageOwner.createElement('div') } catch(e) { // somehow ActiveXObject instantiation failed (perhaps some special // security settings or otherwse), fall back to per-path storage storageEl = doc.createElement('div') storageOwner = doc.body } return function(storeFunction) { var args = [].slice.call(arguments, 0) args.unshift(storageEl) // See http://msdn.microsoft.com/en-us/library/ms531081(v=VS.85).aspx // and http://msdn.microsoft.com/en-us/library/ms531424(v=VS.85).aspx storageOwner.appendChild(storageEl) storageEl.addBehavior('#default#userData') storageEl.load(storageName) storeFunction.apply(this, args) storageOwner.removeChild(storageEl) return } }