@ohos-ports/cross-storage 1.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/client.js ADDED
@@ -0,0 +1,465 @@
1
+ /**
2
+ * cross-storage - Cross domain local storage
3
+ *
4
+ * @version 1.0.0
5
+ * @link https://github.com/zendesk/cross-storage
6
+ * @author Daniel St. Jules <danielst.jules@gmail.com>
7
+ * @copyright Zendesk
8
+ * @license Apache-2.0
9
+ */
10
+
11
+ ;(function(root) {
12
+ /**
13
+ * Constructs a new cross storage client given the url to a hub. By default,
14
+ * an iframe is created within the document body that points to the url. It
15
+ * also accepts an options object, which may include a timeout, frameId, and
16
+ * promise. The timeout, in milliseconds, is applied to each request and
17
+ * defaults to 5000ms. The options object may also include a frameId,
18
+ * identifying an existing frame on which to install its listeners. If the
19
+ * promise key is supplied the constructor for a Promise, that Promise library
20
+ * will be used instead of the default window.Promise.
21
+ *
22
+ * @example
23
+ * var storage = new CrossStorageClient('https://store.example.com/hub.html');
24
+ *
25
+ * @example
26
+ * var storage = new CrossStorageClient('https://store.example.com/hub.html', {
27
+ * timeout: 5000,
28
+ * frameId: 'storageFrame'
29
+ * });
30
+ *
31
+ * @constructor
32
+ *
33
+ * @param {string} url The url to a cross storage hub
34
+ * @param {object} [opts] An optional object containing additional options,
35
+ * including timeout, frameId, and promise
36
+ *
37
+ * @property {string} _id A UUID v4 id
38
+ * @property {function} _promise The Promise object to use
39
+ * @property {string} _frameId The id of the iFrame pointing to the hub url
40
+ * @property {string} _origin The hub's origin
41
+ * @property {object} _requests Mapping of request ids to callbacks
42
+ * @property {bool} _connected Whether or not it has connected
43
+ * @property {bool} _closed Whether or not the client has closed
44
+ * @property {int} _count Number of requests sent
45
+ * @property {function} _listener The listener added to the window
46
+ * @property {Window} _hub The hub window
47
+ */
48
+ function CrossStorageClient(url, opts) {
49
+ opts = opts || {};
50
+
51
+ this._id = CrossStorageClient._generateUUID();
52
+ this._promise = opts.promise || Promise;
53
+ this._frameId = opts.frameId || 'CrossStorageClient-' + this._id;
54
+ this._origin = CrossStorageClient._getOrigin(url);
55
+ this._requests = {};
56
+ this._connected = false;
57
+ this._closed = false;
58
+ this._count = 0;
59
+ this._timeout = opts.timeout || 5000;
60
+ this._listener = null;
61
+
62
+ this._installListener();
63
+
64
+ var frame;
65
+ if (opts.frameId) {
66
+ frame = document.getElementById(opts.frameId);
67
+ }
68
+
69
+ // If using a passed iframe, poll the hub for a ready message
70
+ if (frame) {
71
+ this._poll();
72
+ }
73
+
74
+ // Create the frame if not found or specified
75
+ frame = frame || this._createFrame(url);
76
+ this._hub = frame.contentWindow;
77
+ }
78
+
79
+ /**
80
+ * The styles to be applied to the generated iFrame. Defines a set of properties
81
+ * that hide the element by positioning it outside of the visible area, and
82
+ * by modifying its display.
83
+ *
84
+ * @member {Object}
85
+ */
86
+ CrossStorageClient.frameStyle = {
87
+ display: 'none',
88
+ position: 'absolute',
89
+ top: '-999px',
90
+ left: '-999px'
91
+ };
92
+
93
+ /**
94
+ * Returns the origin of an url, with cross browser support. Accommodates
95
+ * the lack of location.origin in IE, as well as the discrepancies in the
96
+ * inclusion of the port when using the default port for a protocol, e.g.
97
+ * 443 over https. Defaults to the origin of window.location if passed a
98
+ * relative path.
99
+ *
100
+ * @param {string} url The url to a cross storage hub
101
+ * @returns {string} The origin of the url
102
+ */
103
+ CrossStorageClient._getOrigin = function(url) {
104
+ var uri, protocol, origin;
105
+
106
+ uri = document.createElement('a');
107
+ uri.href = url;
108
+
109
+ if (!uri.host) {
110
+ uri = window.location;
111
+ }
112
+
113
+ if (!uri.protocol || uri.protocol === ':') {
114
+ protocol = window.location.protocol;
115
+ } else {
116
+ protocol = uri.protocol;
117
+ }
118
+
119
+ origin = protocol + '//' + uri.host;
120
+ origin = origin.replace(/:80$|:443$/, '');
121
+
122
+ return origin;
123
+ };
124
+
125
+ /**
126
+ * UUID v4 generation, taken from: http://stackoverflow.com/questions/
127
+ * 105034/how-to-create-a-guid-uuid-in-javascript/2117523#2117523
128
+ *
129
+ * @returns {string} A UUID v4 string
130
+ */
131
+ CrossStorageClient._generateUUID = function() {
132
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
133
+ var r = Math.random() * 16|0, v = c == 'x' ? r : (r&0x3|0x8);
134
+
135
+ return v.toString(16);
136
+ });
137
+ };
138
+
139
+ /**
140
+ * Returns a promise that is fulfilled when a connection has been established
141
+ * with the cross storage hub. Its use is required to avoid sending any
142
+ * requests prior to initialization being complete.
143
+ *
144
+ * @returns {Promise} A promise that is resolved on connect
145
+ */
146
+ CrossStorageClient.prototype.onConnect = function() {
147
+ var client = this;
148
+
149
+ if (this._connected) {
150
+ return this._promise.resolve();
151
+ } else if (this._closed) {
152
+ return this._promise.reject(new Error('CrossStorageClient has closed'));
153
+ }
154
+
155
+ // Queue connect requests for client re-use
156
+ if (!this._requests.connect) {
157
+ this._requests.connect = [];
158
+ }
159
+
160
+ return new this._promise(function(resolve, reject) {
161
+ var timeout = setTimeout(function() {
162
+ reject(new Error('CrossStorageClient could not connect'));
163
+ }, client._timeout);
164
+
165
+ client._requests.connect.push(function(err) {
166
+ clearTimeout(timeout);
167
+ if (err) return reject(err);
168
+
169
+ resolve();
170
+ });
171
+ });
172
+ };
173
+
174
+ /**
175
+ * Sets a key to the specified value. Returns a promise that is fulfilled on
176
+ * success, or rejected if any errors setting the key occurred, or the request
177
+ * timed out.
178
+ *
179
+ * @param {string} key The key to set
180
+ * @param {*} value The value to assign
181
+ * @returns {Promise} A promise that is settled on hub response or timeout
182
+ */
183
+ CrossStorageClient.prototype.set = function(key, value) {
184
+ return this._request('set', {
185
+ key: key,
186
+ value: value
187
+ });
188
+ };
189
+
190
+ /**
191
+ * Accepts one or more keys for which to retrieve their values. Returns a
192
+ * promise that is settled on hub response or timeout. On success, it is
193
+ * fulfilled with the value of the key if only passed a single argument.
194
+ * Otherwise it's resolved with an array of values. On failure, it is rejected
195
+ * with the corresponding error message.
196
+ *
197
+ * @param {...string} key The key to retrieve
198
+ * @returns {Promise} A promise that is settled on hub response or timeout
199
+ */
200
+ CrossStorageClient.prototype.get = function(key) {
201
+ var args = Array.prototype.slice.call(arguments);
202
+
203
+ return this._request('get', {keys: args});
204
+ };
205
+
206
+ /**
207
+ * Accepts one or more keys for deletion. Returns a promise that is settled on
208
+ * hub response or timeout.
209
+ *
210
+ * @param {...string} key The key to delete
211
+ * @returns {Promise} A promise that is settled on hub response or timeout
212
+ */
213
+ CrossStorageClient.prototype.del = function() {
214
+ var args = Array.prototype.slice.call(arguments);
215
+
216
+ return this._request('del', {keys: args});
217
+ };
218
+
219
+ /**
220
+ * Returns a promise that, when resolved, indicates that all localStorage
221
+ * data has been cleared.
222
+ *
223
+ * @returns {Promise} A promise that is settled on hub response or timeout
224
+ */
225
+ CrossStorageClient.prototype.clear = function() {
226
+ return this._request('clear');
227
+ };
228
+
229
+ /**
230
+ * Returns a promise that, when resolved, passes an array of all keys
231
+ * currently in storage.
232
+ *
233
+ * @returns {Promise} A promise that is settled on hub response or timeout
234
+ */
235
+ CrossStorageClient.prototype.getKeys = function() {
236
+ return this._request('getKeys');
237
+ };
238
+
239
+ /**
240
+ * Deletes the iframe and sets the connected state to false. The client can
241
+ * no longer be used after being invoked.
242
+ */
243
+ CrossStorageClient.prototype.close = function() {
244
+ var frame = document.getElementById(this._frameId);
245
+ if (frame) {
246
+ frame.parentNode.removeChild(frame);
247
+ }
248
+
249
+ // Support IE8 with detachEvent
250
+ if (window.removeEventListener) {
251
+ window.removeEventListener('message', this._listener, false);
252
+ } else {
253
+ window.detachEvent('onmessage', this._listener);
254
+ }
255
+
256
+ this._connected = false;
257
+ this._closed = true;
258
+ };
259
+
260
+ /**
261
+ * Installs the necessary listener for the window message event. When a message
262
+ * is received, the client's _connected status is changed to true, and the
263
+ * onConnect promise is fulfilled. Given a response message, the callback
264
+ * corresponding to its request is invoked. If response.error holds a truthy
265
+ * value, the promise associated with the original request is rejected with
266
+ * the error. Otherwise the promise is fulfilled and passed response.result.
267
+ *
268
+ * @private
269
+ */
270
+ CrossStorageClient.prototype._installListener = function() {
271
+ var client = this;
272
+
273
+ this._listener = function(message) {
274
+ var i, origin, error, response;
275
+
276
+ // Ignore invalid messages or those after the client has closed
277
+ if (client._closed || !message.data || typeof message.data !== 'string') {
278
+ return;
279
+ }
280
+
281
+ // postMessage returns the string "null" as the origin for "file://"
282
+ origin = (message.origin === 'null') ? 'file://' : message.origin;
283
+
284
+ // Ignore messages not from the correct origin
285
+ if (origin !== client._origin) return;
286
+
287
+ // LocalStorage isn't available in the hub
288
+ if (message.data === 'cross-storage:unavailable') {
289
+ if (!client._closed) client.close();
290
+ if (!client._requests.connect) return;
291
+
292
+ error = new Error('Closing client. Could not access localStorage in hub.');
293
+ for (i = 0; i < client._requests.connect.length; i++) {
294
+ client._requests.connect[i](error);
295
+ }
296
+
297
+ return;
298
+ }
299
+
300
+ // Handle initial connection
301
+ if (message.data.indexOf('cross-storage:') !== -1 && !client._connected) {
302
+ client._connected = true;
303
+ if (!client._requests.connect) return;
304
+
305
+ for (i = 0; i < client._requests.connect.length; i++) {
306
+ client._requests.connect[i](error);
307
+ }
308
+ delete client._requests.connect;
309
+ }
310
+
311
+ if (message.data === 'cross-storage:ready') return;
312
+
313
+ // All other messages
314
+ try {
315
+ response = JSON.parse(message.data);
316
+ } catch(e) {
317
+ return;
318
+ }
319
+
320
+ if (!response.id) return;
321
+
322
+ if (client._requests[response.id]) {
323
+ client._requests[response.id](response.error, response.result);
324
+ }
325
+ };
326
+
327
+ // Support IE8 with attachEvent
328
+ if (window.addEventListener) {
329
+ window.addEventListener('message', this._listener, false);
330
+ } else {
331
+ window.attachEvent('onmessage', this._listener);
332
+ }
333
+ };
334
+
335
+ /**
336
+ * Invoked when a frame id was passed to the client, rather than allowing
337
+ * the client to create its own iframe. Polls the hub for a ready event to
338
+ * establish a connected state.
339
+ */
340
+ CrossStorageClient.prototype._poll = function() {
341
+ var client, interval, targetOrigin;
342
+
343
+ client = this;
344
+
345
+ // postMessage requires that the target origin be set to "*" for "file://"
346
+ targetOrigin = (client._origin === 'file://') ? '*' : client._origin;
347
+
348
+ interval = setInterval(function() {
349
+ if (client._connected) return clearInterval(interval);
350
+ if (!client._hub) return;
351
+
352
+ client._hub.postMessage('cross-storage:poll', targetOrigin);
353
+ }, 1000);
354
+ };
355
+
356
+ /**
357
+ * Creates a new iFrame containing the hub. Applies the necessary styles to
358
+ * hide the element from view, prior to adding it to the document body.
359
+ * Returns the created element.
360
+ *
361
+ * @private
362
+ *
363
+ * @param {string} url The url to the hub
364
+ * returns {HTMLIFrameElement} The iFrame element itself
365
+ */
366
+ CrossStorageClient.prototype._createFrame = function(url) {
367
+ var frame, key;
368
+
369
+ frame = window.document.createElement('iframe');
370
+ frame.id = this._frameId;
371
+
372
+ // Style the iframe
373
+ for (key in CrossStorageClient.frameStyle) {
374
+ if (CrossStorageClient.frameStyle.hasOwnProperty(key)) {
375
+ frame.style[key] = CrossStorageClient.frameStyle[key];
376
+ }
377
+ }
378
+
379
+ window.document.body.appendChild(frame);
380
+ frame.src = url;
381
+
382
+ return frame;
383
+ };
384
+
385
+ /**
386
+ * Sends a message containing the given method and params to the hub. Stores
387
+ * a callback in the _requests object for later invocation on message, or
388
+ * deletion on timeout. Returns a promise that is settled in either instance.
389
+ *
390
+ * @private
391
+ *
392
+ * @param {string} method The method to invoke
393
+ * @param {*} params The arguments to pass
394
+ * @returns {Promise} A promise that is settled on hub response or timeout
395
+ */
396
+ CrossStorageClient.prototype._request = function(method, params) {
397
+ var req, client;
398
+
399
+ if (this._closed) {
400
+ return this._promise.reject(new Error('CrossStorageClient has closed'));
401
+ }
402
+
403
+ client = this;
404
+ client._count++;
405
+
406
+ req = {
407
+ id: this._id + ':' + client._count,
408
+ method: 'cross-storage:' + method,
409
+ params: params
410
+ };
411
+
412
+ return new this._promise(function(resolve, reject) {
413
+ var timeout, originalToJSON, targetOrigin;
414
+
415
+ // Timeout if a response isn't received after 4s
416
+ timeout = setTimeout(function() {
417
+ if (!client._requests[req.id]) return;
418
+
419
+ delete client._requests[req.id];
420
+ reject(new Error('Timeout: could not perform ' + req.method));
421
+ }, client._timeout);
422
+
423
+ // Add request callback
424
+ client._requests[req.id] = function(err, result) {
425
+ clearTimeout(timeout);
426
+ delete client._requests[req.id];
427
+ if (err) return reject(new Error(err));
428
+ resolve(result);
429
+ };
430
+
431
+ // In case we have a broken Array.prototype.toJSON, e.g. because of
432
+ // old versions of prototype
433
+ if (Array.prototype.toJSON) {
434
+ originalToJSON = Array.prototype.toJSON;
435
+ Array.prototype.toJSON = null;
436
+ }
437
+
438
+ // postMessage requires that the target origin be set to "*" for "file://"
439
+ targetOrigin = (client._origin === 'file://') ? '*' : client._origin;
440
+
441
+ // Send serialized message
442
+ client._hub.postMessage(JSON.stringify(req), targetOrigin);
443
+
444
+ // Restore original toJSON
445
+ if (originalToJSON) {
446
+ Array.prototype.toJSON = originalToJSON;
447
+ }
448
+ });
449
+ };
450
+
451
+ /**
452
+ * Export for various environments.
453
+ */
454
+ if (typeof module !== 'undefined' && module.exports) {
455
+ module.exports = CrossStorageClient;
456
+ } else if (typeof exports !== 'undefined') {
457
+ exports.CrossStorageClient = CrossStorageClient;
458
+ } else if (typeof define === 'function' && define.amd) {
459
+ define([], function() {
460
+ return CrossStorageClient;
461
+ });
462
+ } else {
463
+ root.CrossStorageClient = CrossStorageClient;
464
+ }
465
+ }(this));
@@ -0,0 +1,11 @@
1
+ /**
2
+ * cross-storage - Cross domain local storage
3
+ *
4
+ * @version 1.0.0
5
+ * @link https://github.com/zendesk/cross-storage
6
+ * @author Daniel St. Jules <danielst.jules@gmail.com>
7
+ * @copyright Zendesk
8
+ * @license Apache-2.0
9
+ */
10
+
11
+ !function(e){function t(e,r){r=r||{},this._id=t._generateUUID(),this._promise=r.promise||Promise,this._frameId=r.frameId||"CrossStorageClient-"+this._id,this._origin=t._getOrigin(e),this._requests={},this._connected=!1,this._closed=!1,this._count=0,this._timeout=r.timeout||5e3,this._listener=null,this._installListener();var o;r.frameId&&(o=document.getElementById(r.frameId)),o&&this._poll(),o=o||this._createFrame(e),this._hub=o.contentWindow}t.frameStyle={display:"none",position:"absolute",top:"-999px",left:"-999px"},t._getOrigin=function(e){var t,r,o;return t=document.createElement("a"),t.href=e,t.host||(t=window.location),r=t.protocol&&":"!==t.protocol?t.protocol:window.location.protocol,o=r+"//"+t.host,o=o.replace(/:80$|:443$/,"")},t._generateUUID=function(){return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(e){var t=16*Math.random()|0,r="x"==e?t:3&t|8;return r.toString(16)})},t.prototype.onConnect=function(){var e=this;return this._connected?this._promise.resolve():this._closed?this._promise.reject(new Error("CrossStorageClient has closed")):(this._requests.connect||(this._requests.connect=[]),new this._promise(function(t,r){var o=setTimeout(function(){r(new Error("CrossStorageClient could not connect"))},e._timeout);e._requests.connect.push(function(e){return clearTimeout(o),e?r(e):(t(),void 0)})}))},t.prototype.set=function(e,t){return this._request("set",{key:e,value:t})},t.prototype.get=function(){var e=Array.prototype.slice.call(arguments);return this._request("get",{keys:e})},t.prototype.del=function(){var e=Array.prototype.slice.call(arguments);return this._request("del",{keys:e})},t.prototype.clear=function(){return this._request("clear")},t.prototype.getKeys=function(){return this._request("getKeys")},t.prototype.close=function(){var e=document.getElementById(this._frameId);e&&e.parentNode.removeChild(e),window.removeEventListener?window.removeEventListener("message",this._listener,!1):window.detachEvent("onmessage",this._listener),this._connected=!1,this._closed=!0},t.prototype._installListener=function(){var e=this;this._listener=function(t){var r,o,n,s;if(!e._closed&&t.data&&"string"==typeof t.data&&(o="null"===t.origin?"file://":t.origin,o===e._origin))if("cross-storage:unavailable"!==t.data){if(-1!==t.data.indexOf("cross-storage:")&&!e._connected){if(e._connected=!0,!e._requests.connect)return;for(r=0;r<e._requests.connect.length;r++)e._requests.connect[r](n);delete e._requests.connect}if("cross-storage:ready"!==t.data){try{s=JSON.parse(t.data)}catch(i){return}s.id&&e._requests[s.id]&&e._requests[s.id](s.error,s.result)}}else{if(e._closed||e.close(),!e._requests.connect)return;for(n=new Error("Closing client. Could not access localStorage in hub."),r=0;r<e._requests.connect.length;r++)e._requests.connect[r](n)}},window.addEventListener?window.addEventListener("message",this._listener,!1):window.attachEvent("onmessage",this._listener)},t.prototype._poll=function(){var e,t,r;e=this,r="file://"===e._origin?"*":e._origin,t=setInterval(function(){return e._connected?clearInterval(t):(e._hub&&e._hub.postMessage("cross-storage:poll",r),void 0)},1e3)},t.prototype._createFrame=function(e){var r,o;r=window.document.createElement("iframe"),r.id=this._frameId;for(o in t.frameStyle)t.frameStyle.hasOwnProperty(o)&&(r.style[o]=t.frameStyle[o]);return window.document.body.appendChild(r),r.src=e,r},t.prototype._request=function(e,t){var r,o;return this._closed?this._promise.reject(new Error("CrossStorageClient has closed")):(o=this,o._count++,r={id:this._id+":"+o._count,method:"cross-storage:"+e,params:t},new this._promise(function(e,t){var n,s,i;n=setTimeout(function(){o._requests[r.id]&&(delete o._requests[r.id],t(new Error("Timeout: could not perform "+r.method)))},o._timeout),o._requests[r.id]=function(s,i){return clearTimeout(n),delete o._requests[r.id],s?t(new Error(s)):(e(i),void 0)},Array.prototype.toJSON&&(s=Array.prototype.toJSON,Array.prototype.toJSON=null),i="file://"===o._origin?"*":o._origin,o._hub.postMessage(JSON.stringify(r),i),s&&(Array.prototype.toJSON=s)}))},"undefined"!=typeof module&&module.exports?module.exports=t:"undefined"!=typeof exports?exports.CrossStorageClient=t:"function"==typeof define&&define.amd?define([],function(){return t}):e.CrossStorageClient=t}(this);