1 | import deepEqual from 'deep-equal';
|
2 |
|
3 | const DEFAULT_CHECK_DELAY = 3000;
|
4 | const COOKIE_EXPIRES = 365;
|
5 | const QUOTA = 4093;
|
6 |
|
7 | const SECONDS_IN_DAY = 24 * 60 * 60 * 1000;
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 | export default class FallbackStorage {
|
19 | static DEFAULT_COOKIE_NAME = 'localStorage';
|
20 | static DEFAULT_SESSION_COOKIE_NAME = 'sessionStorage';
|
21 | static DEFAULT_CHECK_DELAY = DEFAULT_CHECK_DELAY;
|
22 | static COOKIE_EXPIRES = COOKIE_EXPIRES;
|
23 |
|
24 | |
25 |
|
26 |
|
27 |
|
28 |
|
29 | static QUOTA = QUOTA;
|
30 |
|
31 | |
32 |
|
33 |
|
34 |
|
35 |
|
36 |
|
37 | static _createCookie(name, value, days) {
|
38 | let date;
|
39 | let expires;
|
40 |
|
41 | if (days) {
|
42 | date = new Date();
|
43 | date.setTime(date.getTime() + (days * SECONDS_IN_DAY));
|
44 | expires = `; expires=${date.toGMTString()}`;
|
45 | } else {
|
46 | expires = ';';
|
47 | }
|
48 |
|
49 | document.cookie = `${name}=${value}${expires}; path=/`;
|
50 | }
|
51 |
|
52 | |
53 |
|
54 |
|
55 |
|
56 |
|
57 |
|
58 | static _readCookie(name) {
|
59 | const nameEQ = `${name}=`;
|
60 | const cookies = document.cookie.split(';');
|
61 |
|
62 | let cookie;
|
63 | for (let i = 0; i < cookies.length; i++) {
|
64 | cookie = cookies[i];
|
65 | while (cookie.charAt(0) === ' ') {
|
66 | cookie = cookie.substring(1, cookie.length);
|
67 | }
|
68 |
|
69 | if (cookie.indexOf(nameEQ) === 0) {
|
70 | return cookie.substring(nameEQ.length, cookie.length);
|
71 | }
|
72 | }
|
73 |
|
74 | return undefined;
|
75 | }
|
76 |
|
77 | constructor(config = {}) {
|
78 | const session = config.type === 'session';
|
79 |
|
80 | this.cookieName = config.cookieName ||
|
81 | (
|
82 | session
|
83 | ? this.constructor.DEFAULT_SESSION_COOKIE_NAME
|
84 | : this.constructor.DEFAULT_COOKIE_NAME
|
85 | );
|
86 | this.checkDelay = config.checkDelay || this.constructor.DEFAULT_CHECK_DELAY;
|
87 | this.expires = session ? this.constructor.COOKIE_EXPIRES : null;
|
88 | }
|
89 |
|
90 | |
91 |
|
92 |
|
93 |
|
94 | _read() {
|
95 | return new Promise(resolve => {
|
96 | const rawData = FallbackStorage._readCookie(this.cookieName);
|
97 | resolve(JSON.parse(decodeURIComponent(rawData)));
|
98 | }).catch(() => ({}));
|
99 | }
|
100 |
|
101 | |
102 |
|
103 |
|
104 |
|
105 |
|
106 | _write(data) {
|
107 | return new Promise(resolve => {
|
108 | const stringData = encodeURIComponent(JSON.stringify(data));
|
109 | FallbackStorage.
|
110 | _createCookie(this.cookieName, stringData === '{}' ? '' : stringData, this.expires);
|
111 | return resolve(data);
|
112 | });
|
113 | }
|
114 |
|
115 | |
116 |
|
117 |
|
118 |
|
119 | get(key) {
|
120 | return this._read().then(data => data[key] || null);
|
121 | }
|
122 |
|
123 | |
124 |
|
125 |
|
126 |
|
127 |
|
128 | set(key, value) {
|
129 | return this._read().then(data => {
|
130 | if (key) {
|
131 | if (value != null) {
|
132 | data[key] = value;
|
133 | } else {
|
134 | Reflect.deleteProperty(data, key);
|
135 | }
|
136 | }
|
137 |
|
138 | return this._write(data);
|
139 | });
|
140 | }
|
141 |
|
142 | |
143 |
|
144 |
|
145 |
|
146 | remove(key) {
|
147 | return this.set(key, null);
|
148 | }
|
149 |
|
150 | |
151 |
|
152 |
|
153 |
|
154 |
|
155 | each(callback) {
|
156 | if (typeof callback !== 'function') {
|
157 | return Promise.reject(new Error('Callback is not a function'));
|
158 | }
|
159 |
|
160 | return this._read().then(data => {
|
161 | const promises = [];
|
162 | for (const key in data) {
|
163 | if (data.hasOwnProperty(key)) {
|
164 | promises.push(callback(key, data[key]));
|
165 | }
|
166 | }
|
167 | return Promise.all(promises);
|
168 | });
|
169 | }
|
170 |
|
171 | |
172 |
|
173 |
|
174 |
|
175 |
|
176 | on(key, callback) {
|
177 | let stop = false;
|
178 |
|
179 | const checkForChange = oldValue => {
|
180 | this.get(key).then(newValue => {
|
181 | if (stop) {
|
182 | return;
|
183 | }
|
184 |
|
185 | if (!deepEqual(oldValue, newValue)) {
|
186 | callback(newValue);
|
187 | }
|
188 |
|
189 | window.setTimeout(() => checkForChange(oldValue), this.checkDelay);
|
190 | });
|
191 | };
|
192 |
|
193 | this.get(key).then(checkForChange);
|
194 |
|
195 | return () => {
|
196 | stop = true;
|
197 | };
|
198 | }
|
199 | }
|