1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
|
import Cookie from './cookie.js';
/**
* Stud.IP: Caching in JavaScript
*
* Uses local storage for persistent storage across browser sessions
* for items with a given expiry or as a tab spanning session storage
* when no expiry is given.
*
* Example:
*
* var cache = STUDIP.Cache.getInstance(),
* foo = cache.get('foo');
* if (typeof foo === undefined) {
* foo = 'bar';
* cache.set('foo', foo);
* }
*
* Pass set() an expiry duration in seconds to allow persistent storage
* across browser sessions.
*
* Example:
*
* var cache = STUDIP.Cache.getInstance(),
* tmp;
* cache.set('foo', 'bar', 5);
* tmp = cache.get('foo');
* setTimeout(function () {
* console.log([tmp, cache.get('foo')]);
* }, 6000);
* // Will result in ['bar', undefined] after 6 seconds have passed
*
* You may pass get() a creator function as an optional second parameter
* so the value will be generated on the fly if not found in cache.
*
* Example:
*
* var cache = STUDIP.Cache.getInstance(),
* creator = function (index) { return 'Hello ' + index; };
* cache.remove('World');
* console.log(cache.get('World', creator));
* // Will result in 'Hello World' both on the console and in cache
*
* Cache instances may use prefixes to avoid conflicts with other js
* functions (this is the single reason why the lib was designed to use a
* getInstance() method).
*
* Example:
*
* var cache0 = STUDIP.Cache.getInstance(''),
* cache1 = STUDIP.Cache.getInstance('foo');
* cache0.set('foobar', 'baz');
* console.log([cache0.get('bar'), cache1.get('bar')]);
* // Will result in [undefined, 'baz']
*
* If the browser does not support any of the storage types, a dummy polyfill
* will be used that doesn't actually store data.
*
* Internally, all items are prefixed with a 'studip.' in order to avoid
* clashes.
*
* This implementation does not use sessionStorage due to the fact that the
* cache should work across tabs and windows. A session is indicated by a
* session cookie that this implementation will use.
*
* @author Jan-Hendrik Willms <tleilax+studip@gmail.com>
* @license GPL2 or any later version
* @copyright Stud.IP core group
* @since Stud.IP 3.2
*/
// Use localstorage or dummy
var cache;
try {
let test_key = '__storageTest123';
window.localStorage.setItem(test_key, 'foo');
window.localStorage.removeItem(test_key);
cache = window.localStorage;
} catch {
cache = new class {
constructor() { this.length = 0; }
clear() {}
getItem() { return undefined; }
key() { return undefined; }
removeItem() {}
setItem() {}
}();
}
class Cache {
/**
* @param prefix Optional prefix for the cache
* @param session_id
*/
constructor(prefix, session_id) {
this.prefix = 'studip.' + (prefix || '');
this.session_id = session_id;
}
/**
* Locates an item in the caches.
*
* @param index Key of the item to look up
* @return mixed false if item is not found, item's value otherwise
*/
locate(index) {
index = this.prefix + index;
if (cache[index] !== undefined) {
const now = new Date().getTime();
let item = JSON.parse(cache.getItem(index));
if (!item.expires || item.expires > now) {
return item.value;
}
cache.removeItem(index);
}
return undefined;
}
/**
* Returns whether the cache has an item stored for the given key.
*
* @param index Key used to store the item
* @return bool
*/
has(index) {
return this.locate(index) !== undefined;
}
/**
* Retrieves an object from the cache for the given key.
* You may provide an additional creator function if the
* value was not found to immediately create and set it.
* The function will be passed the index as it's only argument.
*
* @param index Key used to store the item
* @param setter Optional creator function for the value
* @param expires Optional storage duration in seconds
* @return mixed Value of the item or undefined if not found.
*/
get(index, setter, expires) {
var result = this.locate(index);
if (result === undefined && setter && typeof setter === 'function') {
result = setter(index);
this.set(index, result, expires);
}
return result;
}
/**
* Store an item in the cache.
*
* @param index Key used to store the item
* @param value Value of the item
* @param expires Optional storage duration in seconds
*/
set(index, value, expires) {
index = this.prefix + index;
cache.setItem(index, JSON.stringify({
value: value,
expires: expires ? new Date().getTime() + expires * 1000 : false,
session: this.session_id
}));
}
/**
* Removes an item from the cache.
*
* @param index Key used to store the item
*/
remove(index) {
if (this.has(index)) {
index = this.prefix + index;
cache.removeItem(index);
}
}
/**
* Clears the cache completely. Respects the prefix, so only
* the prefixed items will be removed.
*/
prune() {
if (this.prefix) {
for (let key in cache) {
if (cache[key] !== undefined && key.indexOf(this.prefix) === 0) {
cache.removeItem(key);
}
}
} else {
cache.clear();
}
}
}
/**
* Expose the Cache object with it's getInstance method to the global
* STUDIP object.
*/
const CacheFacade = {
getInstance: function (prefix) {
// Initialized browser session?
const now = new Date().getTime();
var session_id = Cookie.get('cache_session');
if (session_id === undefined) {
session_id = new Date().getTime().toString();
Cookie.set('cache_session', session_id);
for (let key in cache) {
if (cache[key] === undefined || key.indexOf('studip.') !== 0) {
continue;
}
var item = JSON.parse(cache.getItem(key));
if (item.expires < now || (item.expires === false && item.session !== session_id)) {
cache.removeItem(key);
}
}
}
return new Cache(prefix, session_id);
}
};
export default CacheFacade;
|