UNPKG

6.54 kBJavaScriptView Raw
1/* Copyright (c) 2010-2012 Marcus Westin
2 *
3 * Permission is hereby granted, free of charge, to any person obtaining a copy
4 * of this software and associated documentation files (the "Software"), to deal
5 * in the Software without restriction, including without limitation the rights
6 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 * copies of the Software, and to permit persons to whom the Software is
8 * furnished to do so, subject to the following conditions:
9 *
10 * The above copyright notice and this permission notice shall be included in
11 * all copies or substantial portions of the Software.
12 *
13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 * THE SOFTWARE.
20 */
21
22;(function(){
23 var store = {},
24 win = window,
25 doc = win.document,
26 localStorageName = 'localStorage',
27 namespace = '__storejs__',
28 storage
29
30 store.disabled = false
31 store.set = function(key, value) {}
32 store.get = function(key) {}
33 store.remove = function(key) {}
34 store.clear = function() {}
35 store.transact = function(key, defaultVal, transactionFn) {
36 var val = store.get(key)
37 if (transactionFn == null) {
38 transactionFn = defaultVal
39 defaultVal = null
40 }
41 if (typeof val == 'undefined') { val = defaultVal || {} }
42 transactionFn(val)
43 store.set(key, val)
44 }
45 store.getAll = function() {}
46
47 store.serialize = function(value) {
48 return JSON.stringify(value)
49 }
50 store.deserialize = function(value) {
51 if (typeof value != 'string') { return undefined }
52 try { return JSON.parse(value) }
53 catch(e) { return value || undefined }
54 }
55
56 // Functions to encapsulate questionable FireFox 3.6.13 behavior
57 // when about.config::dom.storage.enabled === false
58 // See https://github.com/marcuswestin/store.js/issues#issue/13
59 function isLocalStorageNameSupported() {
60 try { return (localStorageName in win && win[localStorageName]) }
61 catch(err) { return false }
62 }
63
64 if (isLocalStorageNameSupported()) {
65 storage = win[localStorageName]
66 store.set = function(key, val) {
67 if (val === undefined) { return store.remove(key) }
68 storage.setItem(key, store.serialize(val))
69 return val
70 }
71 store.get = function(key) { return store.deserialize(storage.getItem(key)) }
72 store.remove = function(key) { storage.removeItem(key) }
73 store.clear = function() { storage.clear() }
74 store.getAll = function() {
75 var ret = {}
76 for (var i=0; i<storage.length; ++i) {
77 var key = storage.key(i)
78 ret[key] = store.get(key)
79 }
80 return ret
81 }
82 } else if (doc.documentElement.addBehavior) {
83 var storageOwner,
84 storageContainer
85 // Since #userData storage applies only to specific paths, we need to
86 // somehow link our data to a specific path. We choose /favicon.ico
87 // as a pretty safe option, since all browsers already make a request to
88 // this URL anyway and being a 404 will not hurt us here. We wrap an
89 // iframe pointing to the favicon in an ActiveXObject(htmlfile) object
90 // (see: http://msdn.microsoft.com/en-us/library/aa752574(v=VS.85).aspx)
91 // since the iframe access rules appear to allow direct access and
92 // manipulation of the document element, even for a 404 page. This
93 // document can be used instead of the current document (which would
94 // have been limited to the current path) to perform #userData storage.
95 try {
96 storageContainer = new ActiveXObject('htmlfile')
97 storageContainer.open()
98 storageContainer.write('<s' + 'cript>document.w=window</s' + 'cript><iframe src="/favicon.ico"></frame>')
99 storageContainer.close()
100 storageOwner = storageContainer.w.frames[0].document
101 storage = storageOwner.createElement('div')
102 } catch(e) {
103 // somehow ActiveXObject instantiation failed (perhaps some special
104 // security settings or otherwse), fall back to per-path storage
105 storage = doc.createElement('div')
106 storageOwner = doc.body
107 }
108 function withIEStorage(storeFunction) {
109 return function() {
110 var args = Array.prototype.slice.call(arguments, 0)
111 args.unshift(storage)
112 // See http://msdn.microsoft.com/en-us/library/ms531081(v=VS.85).aspx
113 // and http://msdn.microsoft.com/en-us/library/ms531424(v=VS.85).aspx
114 storageOwner.appendChild(storage)
115 storage.addBehavior('#default#userData')
116 storage.load(localStorageName)
117 var result = storeFunction.apply(store, args)
118 storageOwner.removeChild(storage)
119 return result
120 }
121 }
122
123 // In IE7, keys may not contain special chars. See all of https://github.com/marcuswestin/store.js/issues/40
124 var forbiddenCharsRegex = new RegExp("[!\"#$%&'()*+,/\\\\:;<=>?@[\\]^`{|}~]", "g")
125 function ieKeyFix(key) {
126 return key.replace(forbiddenCharsRegex, '___')
127 }
128 store.set = withIEStorage(function(storage, key, val) {
129 key = ieKeyFix(key)
130 if (val === undefined) { return store.remove(key) }
131 storage.setAttribute(key, store.serialize(val))
132 storage.save(localStorageName)
133 return val
134 })
135 store.get = withIEStorage(function(storage, key) {
136 key = ieKeyFix(key)
137 return store.deserialize(storage.getAttribute(key))
138 })
139 store.remove = withIEStorage(function(storage, key) {
140 key = ieKeyFix(key)
141 storage.removeAttribute(key)
142 storage.save(localStorageName)
143 })
144 store.clear = withIEStorage(function(storage) {
145 var attributes = storage.XMLDocument.documentElement.attributes
146 storage.load(localStorageName)
147 for (var i=0, attr; attr=attributes[i]; i++) {
148 storage.removeAttribute(attr.name)
149 }
150 storage.save(localStorageName)
151 })
152 store.getAll = withIEStorage(function(storage) {
153 var attributes = storage.XMLDocument.documentElement.attributes
154 storage.load(localStorageName)
155 var ret = {}
156 for (var i=0, attr; attr=attributes[i]; ++i) {
157 ret[attr] = store.get(attr)
158 }
159 return ret
160 })
161 }
162
163 try {
164 store.set(namespace, namespace)
165 if (store.get(namespace) != namespace) { store.disabled = true }
166 store.remove(namespace)
167 } catch(e) {
168 store.disabled = true
169 }
170 store.enabled = !store.disabled
171
172 if (typeof module != 'undefined' && typeof module != 'function') { module.exports = store }
173 else if (typeof define === 'function' && define.amd) { define(store) }
174 else { this.store = store }
175})();