@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/LICENSE +176 -0
- package/README.md +325 -0
- package/dist/client.js +465 -0
- package/dist/client.min.js +11 -0
- package/dist/hub.js +279 -0
- package/dist/hub.min.js +11 -0
- package/lib/client.js +629 -0
- package/lib/hub.js +299 -0
- package/lib/index.js +4 -0
- package/package.json +41 -0
package/lib/client.js
ADDED
|
@@ -0,0 +1,629 @@
|
|
|
1
|
+
;(function(root) {
|
|
2
|
+
/**
|
|
3
|
+
* Constructs a new cross storage client given the url to a hub. By default,
|
|
4
|
+
* an iframe is created within the document body that points to the url. It
|
|
5
|
+
* also accepts an options object, which may include a timeout, frameId, and
|
|
6
|
+
* promise. The timeout, in milliseconds, is applied to each request and
|
|
7
|
+
* defaults to 5000ms. The options object may also include a frameId,
|
|
8
|
+
* identifying an existing frame on which to install its listeners. If the
|
|
9
|
+
* promise key is supplied the constructor for a Promise, that Promise library
|
|
10
|
+
* will be used instead of the default window.Promise.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* var storage = new CrossStorageClient('https://store.example.com/hub.html');
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* var storage = new CrossStorageClient('https://store.example.com/hub.html', {
|
|
17
|
+
* timeout: 5000,
|
|
18
|
+
* frameId: 'storageFrame'
|
|
19
|
+
* });
|
|
20
|
+
*
|
|
21
|
+
* @constructor
|
|
22
|
+
*
|
|
23
|
+
* @param {string} url The url to a cross storage hub
|
|
24
|
+
* @param {object} [opts] An optional object containing additional options,
|
|
25
|
+
* including timeout, frameId, and promise
|
|
26
|
+
*
|
|
27
|
+
* @property {string} _id A UUID v4 id
|
|
28
|
+
* @property {function} _promise The Promise object to use
|
|
29
|
+
* @property {string} _frameId The id of the iFrame pointing to the hub url
|
|
30
|
+
* @property {string} _origin The hub's origin
|
|
31
|
+
* @property {object} _requests Mapping of request ids to callbacks
|
|
32
|
+
* @property {bool} _connected Whether or not it has connected
|
|
33
|
+
* @property {bool} _closed Whether or not the client has closed
|
|
34
|
+
* @property {int} _count Number of requests sent
|
|
35
|
+
* @property {function} _listener The listener added to the window
|
|
36
|
+
* @property {Window} _hub The hub window
|
|
37
|
+
*/
|
|
38
|
+
function CrossStorageClient(url, opts) {
|
|
39
|
+
opts = opts || {};
|
|
40
|
+
|
|
41
|
+
this._id = CrossStorageClient._generateUUID();
|
|
42
|
+
this._promise = opts.promise || Promise;
|
|
43
|
+
this._frameId = opts.frameId || 'CrossStorageClient-' + this._id;
|
|
44
|
+
this._origin = CrossStorageClient._getOrigin(url);
|
|
45
|
+
this._requests = {};
|
|
46
|
+
this._connected = false;
|
|
47
|
+
this._closed = false;
|
|
48
|
+
this._count = 0;
|
|
49
|
+
this._timeout = opts.timeout || 5000;
|
|
50
|
+
this._listener = null;
|
|
51
|
+
|
|
52
|
+
// OHOS/Node.js in-process mode: skip iframe, use hub directly
|
|
53
|
+
if (CrossStorageClient._isNodeEnvironment()) {
|
|
54
|
+
this._initInProcessHub(url, opts);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
this._installListener();
|
|
59
|
+
|
|
60
|
+
var frame;
|
|
61
|
+
if (opts.frameId) {
|
|
62
|
+
frame = document.getElementById(opts.frameId);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// If using a passed iframe, poll the hub for a ready message
|
|
66
|
+
if (frame) {
|
|
67
|
+
this._poll();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Create the frame if not found or specified
|
|
71
|
+
frame = frame || this._createFrame(url);
|
|
72
|
+
this._hub = frame.contentWindow;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* The styles to be applied to the generated iFrame. Defines a set of properties
|
|
77
|
+
* that hide the element by positioning it outside of the visible area, and
|
|
78
|
+
* by modifying its display.
|
|
79
|
+
*
|
|
80
|
+
* @member {Object}
|
|
81
|
+
*/
|
|
82
|
+
CrossStorageClient.frameStyle = {
|
|
83
|
+
display: 'none',
|
|
84
|
+
position: 'absolute',
|
|
85
|
+
top: '-999px',
|
|
86
|
+
left: '-999px'
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Detects whether we are running in a Node.js/OHOS environment (no browser).
|
|
91
|
+
*
|
|
92
|
+
* @returns {boolean} True if running in Node.js/OHOS
|
|
93
|
+
* @private
|
|
94
|
+
*/
|
|
95
|
+
CrossStorageClient._isNodeEnvironment = function() {
|
|
96
|
+
return typeof window === 'undefined' || typeof document === 'undefined';
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* In-process localStorage implementation for Node.js/OHOS environments.
|
|
101
|
+
* Uses a simple Map to store key-value pairs.
|
|
102
|
+
*
|
|
103
|
+
* @returns {object} A localStorage-compatible object
|
|
104
|
+
* @private
|
|
105
|
+
*/
|
|
106
|
+
CrossStorageClient._createLocalStorage = function() {
|
|
107
|
+
var store = {};
|
|
108
|
+
return {
|
|
109
|
+
getItem: function(key) {
|
|
110
|
+
return Object.prototype.hasOwnProperty.call(store, key) ? store[key] : null;
|
|
111
|
+
},
|
|
112
|
+
setItem: function(key, value) {
|
|
113
|
+
store[key] = String(value);
|
|
114
|
+
},
|
|
115
|
+
removeItem: function(key) {
|
|
116
|
+
delete store[key];
|
|
117
|
+
},
|
|
118
|
+
clear: function() {
|
|
119
|
+
store = {};
|
|
120
|
+
},
|
|
121
|
+
key: function(index) {
|
|
122
|
+
var keys = Object.keys(store);
|
|
123
|
+
return index < keys.length ? keys[index] : null;
|
|
124
|
+
},
|
|
125
|
+
get length() {
|
|
126
|
+
return Object.keys(store).length;
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Initializes an in-process hub for Node.js/OHOS environments.
|
|
133
|
+
* Instead of creating an iframe and using postMessage, this directly
|
|
134
|
+
* instantiates the CrossStorageHub with a mock localStorage and
|
|
135
|
+
* wires the client to call hub methods directly.
|
|
136
|
+
*
|
|
137
|
+
* @param {string} url The url to a cross storage hub (used for origin)
|
|
138
|
+
* @param {object} opts Options object
|
|
139
|
+
* @private
|
|
140
|
+
*/
|
|
141
|
+
CrossStorageClient.prototype._initInProcessHub = function(url, opts) {
|
|
142
|
+
var client = this;
|
|
143
|
+
|
|
144
|
+
// Create in-process localStorage
|
|
145
|
+
var localStorage = CrossStorageClient._createLocalStorage();
|
|
146
|
+
|
|
147
|
+
// Try to load CrossStorageHub
|
|
148
|
+
var CrossStorageHub;
|
|
149
|
+
try {
|
|
150
|
+
CrossStorageHub = require('./hub.js');
|
|
151
|
+
} catch (e) {
|
|
152
|
+
// If hub.js can't be loaded, create a minimal in-process hub
|
|
153
|
+
CrossStorageHub = null;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (CrossStorageHub) {
|
|
157
|
+
// Initialize the hub with permissive permissions and our localStorage
|
|
158
|
+
// We patch the hub's localStorage reference
|
|
159
|
+
CrossStorageHub._storage = localStorage;
|
|
160
|
+
CrossStorageHub.init([
|
|
161
|
+
{origin: /.*/, allow: ['get', 'set', 'del', 'clear', 'getKeys']}
|
|
162
|
+
]);
|
|
163
|
+
this._inProcessHub = CrossStorageHub;
|
|
164
|
+
} else {
|
|
165
|
+
// Create a minimal in-process hub implementation
|
|
166
|
+
this._inProcessHub = {
|
|
167
|
+
_storage: localStorage,
|
|
168
|
+
_set: function(params) {
|
|
169
|
+
localStorage.setItem(params.key, params.value);
|
|
170
|
+
},
|
|
171
|
+
_get: function(params) {
|
|
172
|
+
var result = [];
|
|
173
|
+
for (var i = 0; i < params.keys.length; i++) {
|
|
174
|
+
result.push(localStorage.getItem(params.keys[i]));
|
|
175
|
+
}
|
|
176
|
+
return result.length > 1 ? result : result[0];
|
|
177
|
+
},
|
|
178
|
+
_del: function(params) {
|
|
179
|
+
for (var i = 0; i < params.keys.length; i++) {
|
|
180
|
+
localStorage.removeItem(params.keys[i]);
|
|
181
|
+
}
|
|
182
|
+
},
|
|
183
|
+
_clear: function() {
|
|
184
|
+
localStorage.clear();
|
|
185
|
+
},
|
|
186
|
+
_getKeys: function() {
|
|
187
|
+
var keys = [];
|
|
188
|
+
for (var i = 0; i < localStorage.length; i++) {
|
|
189
|
+
keys.push(localStorage.key(i));
|
|
190
|
+
}
|
|
191
|
+
return keys;
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Create a mock hub window for compatibility
|
|
197
|
+
this._hub = {
|
|
198
|
+
postMessage: function() {} // no-op in in-process mode
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
// Mark as connected immediately since hub is in-process
|
|
202
|
+
this._connected = true;
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Returns the origin of an url, with cross browser support. Accommodates
|
|
207
|
+
* the lack of location.origin in IE, as well as the discrepancies in the
|
|
208
|
+
* inclusion of the port when using the default port for a protocol, e.g.
|
|
209
|
+
* 443 over https. Defaults to the origin of window.location if passed a
|
|
210
|
+
* relative path.
|
|
211
|
+
*
|
|
212
|
+
* @param {string} url The url to a cross storage hub
|
|
213
|
+
* @returns {string} The origin of the url
|
|
214
|
+
*/
|
|
215
|
+
CrossStorageClient._getOrigin = function(url) {
|
|
216
|
+
var uri, protocol, origin;
|
|
217
|
+
|
|
218
|
+
// Node.js/OHOS path: use URL parsing without document.createElement
|
|
219
|
+
if (CrossStorageClient._isNodeEnvironment()) {
|
|
220
|
+
try {
|
|
221
|
+
var parsed = new URL(url, 'http://localhost');
|
|
222
|
+
origin = parsed.protocol + '//' + parsed.host;
|
|
223
|
+
origin = origin.replace(/:80$|:443$/, '');
|
|
224
|
+
return origin;
|
|
225
|
+
} catch (e) {
|
|
226
|
+
return 'http://localhost';
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
uri = document.createElement('a');
|
|
231
|
+
uri.href = url;
|
|
232
|
+
|
|
233
|
+
if (!uri.host) {
|
|
234
|
+
uri = window.location;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (!uri.protocol || uri.protocol === ':') {
|
|
238
|
+
protocol = window.location.protocol;
|
|
239
|
+
} else {
|
|
240
|
+
protocol = uri.protocol;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
origin = protocol + '//' + uri.host;
|
|
244
|
+
origin = origin.replace(/:80$|:443$/, '');
|
|
245
|
+
|
|
246
|
+
return origin;
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* UUID v4 generation, taken from: http://stackoverflow.com/questions/
|
|
251
|
+
* 105034/how-to-create-a-guid-uuid-in-javascript/2117523#2117523
|
|
252
|
+
*
|
|
253
|
+
* @returns {string} A UUID v4 string
|
|
254
|
+
*/
|
|
255
|
+
CrossStorageClient._generateUUID = function() {
|
|
256
|
+
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
|
257
|
+
var r = Math.random() * 16|0, v = c == 'x' ? r : (r&0x3|0x8);
|
|
258
|
+
|
|
259
|
+
return v.toString(16);
|
|
260
|
+
});
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Returns a promise that is fulfilled when a connection has been established
|
|
265
|
+
* with the cross storage hub. Its use is required to avoid sending any
|
|
266
|
+
* requests prior to initialization being complete.
|
|
267
|
+
*
|
|
268
|
+
* @returns {Promise} A promise that is resolved on connect
|
|
269
|
+
*/
|
|
270
|
+
CrossStorageClient.prototype.onConnect = function() {
|
|
271
|
+
var client = this;
|
|
272
|
+
|
|
273
|
+
// In-process mode: already connected
|
|
274
|
+
if (this._inProcessHub) {
|
|
275
|
+
if (this._connected) {
|
|
276
|
+
return this._promise.resolve();
|
|
277
|
+
} else if (this._closed) {
|
|
278
|
+
return this._promise.reject(new Error('CrossStorageClient has closed'));
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
if (this._connected) {
|
|
283
|
+
return this._promise.resolve();
|
|
284
|
+
} else if (this._closed) {
|
|
285
|
+
return this._promise.reject(new Error('CrossStorageClient has closed'));
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Queue connect requests for client re-use
|
|
289
|
+
if (!this._requests.connect) {
|
|
290
|
+
this._requests.connect = [];
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
return new this._promise(function(resolve, reject) {
|
|
294
|
+
var timeout = setTimeout(function() {
|
|
295
|
+
reject(new Error('CrossStorageClient could not connect'));
|
|
296
|
+
}, client._timeout);
|
|
297
|
+
|
|
298
|
+
client._requests.connect.push(function(err) {
|
|
299
|
+
clearTimeout(timeout);
|
|
300
|
+
if (err) return reject(err);
|
|
301
|
+
|
|
302
|
+
resolve();
|
|
303
|
+
});
|
|
304
|
+
});
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Sets a key to the specified value. Returns a promise that is fulfilled on
|
|
309
|
+
* success, or rejected if any errors setting the key occurred, or the request
|
|
310
|
+
* timed out.
|
|
311
|
+
*
|
|
312
|
+
* @param {string} key The key to set
|
|
313
|
+
* @param {*} value The value to assign
|
|
314
|
+
* @returns {Promise} A promise that is settled on hub response or timeout
|
|
315
|
+
*/
|
|
316
|
+
CrossStorageClient.prototype.set = function(key, value) {
|
|
317
|
+
return this._request('set', {
|
|
318
|
+
key: key,
|
|
319
|
+
value: value
|
|
320
|
+
});
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Accepts one or more keys for which to retrieve their values. Returns a
|
|
325
|
+
* promise that is settled on hub response or timeout. On success, it is
|
|
326
|
+
* fulfilled with the value of the key if only passed a single argument.
|
|
327
|
+
* Otherwise it's resolved with an array of values. On failure, it is rejected
|
|
328
|
+
* with the corresponding error message.
|
|
329
|
+
*
|
|
330
|
+
* @param {...string} key The key to retrieve
|
|
331
|
+
* @returns {Promise} A promise that is settled on hub response or timeout
|
|
332
|
+
*/
|
|
333
|
+
CrossStorageClient.prototype.get = function(key) {
|
|
334
|
+
var args = Array.prototype.slice.call(arguments);
|
|
335
|
+
|
|
336
|
+
return this._request('get', {keys: args});
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Accepts one or more keys for deletion. Returns a promise that is settled on
|
|
341
|
+
* hub response or timeout.
|
|
342
|
+
*
|
|
343
|
+
* @param {...string} key The key to delete
|
|
344
|
+
* @returns {Promise} A promise that is settled on hub response or timeout
|
|
345
|
+
*/
|
|
346
|
+
CrossStorageClient.prototype.del = function() {
|
|
347
|
+
var args = Array.prototype.slice.call(arguments);
|
|
348
|
+
|
|
349
|
+
return this._request('del', {keys: args});
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Returns a promise that, when resolved, indicates that all localStorage
|
|
354
|
+
* data has been cleared.
|
|
355
|
+
*
|
|
356
|
+
* @returns {Promise} A promise that is settled on hub response or timeout
|
|
357
|
+
*/
|
|
358
|
+
CrossStorageClient.prototype.clear = function() {
|
|
359
|
+
return this._request('clear');
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Returns a promise that, when resolved, passes an array of all keys
|
|
364
|
+
* currently in storage.
|
|
365
|
+
*
|
|
366
|
+
* @returns {Promise} A promise that is settled on hub response or timeout
|
|
367
|
+
*/
|
|
368
|
+
CrossStorageClient.prototype.getKeys = function() {
|
|
369
|
+
return this._request('getKeys');
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Deletes the iframe and sets the connected state to false. The client can
|
|
374
|
+
* no longer be used after being invoked.
|
|
375
|
+
*/
|
|
376
|
+
CrossStorageClient.prototype.close = function() {
|
|
377
|
+
// In-process mode: no iframe to remove
|
|
378
|
+
if (this._inProcessHub) {
|
|
379
|
+
this._connected = false;
|
|
380
|
+
this._closed = true;
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
var frame = document.getElementById(this._frameId);
|
|
385
|
+
if (frame) {
|
|
386
|
+
frame.parentNode.removeChild(frame);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// Support IE8 with detachEvent
|
|
390
|
+
if (window.removeEventListener) {
|
|
391
|
+
window.removeEventListener('message', this._listener, false);
|
|
392
|
+
} else {
|
|
393
|
+
window.detachEvent('onmessage', this._listener);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
this._connected = false;
|
|
397
|
+
this._closed = true;
|
|
398
|
+
};
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* Installs the necessary listener for the window message event. When a message
|
|
402
|
+
* is received, the client's _connected status is changed to true, and the
|
|
403
|
+
* onConnect promise is fulfilled. Given a response message, the callback
|
|
404
|
+
* corresponding to its request is invoked. If response.error holds a truthy
|
|
405
|
+
* value, the promise associated with the original request is rejected with
|
|
406
|
+
* the error. Otherwise the promise is fulfilled and passed response.result.
|
|
407
|
+
*
|
|
408
|
+
* @private
|
|
409
|
+
*/
|
|
410
|
+
CrossStorageClient.prototype._installListener = function() {
|
|
411
|
+
// In-process mode: no message listener needed
|
|
412
|
+
if (this._inProcessHub) {
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
var client = this;
|
|
417
|
+
|
|
418
|
+
this._listener = function(message) {
|
|
419
|
+
var i, origin, error, response;
|
|
420
|
+
|
|
421
|
+
// Ignore invalid messages or those after the client has closed
|
|
422
|
+
if (client._closed || !message.data || typeof message.data !== 'string') {
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// postMessage returns the string "null" as the origin for "file://"
|
|
427
|
+
origin = (message.origin === 'null') ? 'file://' : message.origin;
|
|
428
|
+
|
|
429
|
+
// Ignore messages not from the correct origin
|
|
430
|
+
if (origin !== client._origin) return;
|
|
431
|
+
|
|
432
|
+
// LocalStorage isn't available in the hub
|
|
433
|
+
if (message.data === 'cross-storage:unavailable') {
|
|
434
|
+
if (!client._closed) client.close();
|
|
435
|
+
if (!client._requests.connect) return;
|
|
436
|
+
|
|
437
|
+
error = new Error('Closing client. Could not access localStorage in hub.');
|
|
438
|
+
for (i = 0; i < client._requests.connect.length; i++) {
|
|
439
|
+
client._requests.connect[i](error);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// Handle initial connection
|
|
446
|
+
if (message.data.indexOf('cross-storage:') !== -1 && !client._connected) {
|
|
447
|
+
client._connected = true;
|
|
448
|
+
if (!client._requests.connect) return;
|
|
449
|
+
|
|
450
|
+
for (i = 0; i < client._requests.connect.length; i++) {
|
|
451
|
+
client._requests.connect[i](error);
|
|
452
|
+
}
|
|
453
|
+
delete client._requests.connect;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
if (message.data === 'cross-storage:ready') return;
|
|
457
|
+
|
|
458
|
+
// All other messages
|
|
459
|
+
try {
|
|
460
|
+
response = JSON.parse(message.data);
|
|
461
|
+
} catch(e) {
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
if (!response.id) return;
|
|
466
|
+
|
|
467
|
+
if (client._requests[response.id]) {
|
|
468
|
+
client._requests[response.id](response.error, response.result);
|
|
469
|
+
}
|
|
470
|
+
};
|
|
471
|
+
|
|
472
|
+
// Support IE8 with attachEvent
|
|
473
|
+
if (window.addEventListener) {
|
|
474
|
+
window.addEventListener('message', this._listener, false);
|
|
475
|
+
} else {
|
|
476
|
+
window.attachEvent('onmessage', this._listener);
|
|
477
|
+
}
|
|
478
|
+
};
|
|
479
|
+
|
|
480
|
+
/**
|
|
481
|
+
* Invoked when a frame id was passed to the client, rather than allowing
|
|
482
|
+
* the client to create its own iframe. Polls the hub for a ready event to
|
|
483
|
+
* establish a connected state.
|
|
484
|
+
*/
|
|
485
|
+
CrossStorageClient.prototype._poll = function() {
|
|
486
|
+
var client, interval, targetOrigin;
|
|
487
|
+
|
|
488
|
+
client = this;
|
|
489
|
+
|
|
490
|
+
// postMessage requires that the target origin be set to "*" for "file://"
|
|
491
|
+
targetOrigin = (client._origin === 'file://') ? '*' : client._origin;
|
|
492
|
+
|
|
493
|
+
interval = setInterval(function() {
|
|
494
|
+
if (client._connected) return clearInterval(interval);
|
|
495
|
+
if (!client._hub) return;
|
|
496
|
+
|
|
497
|
+
client._hub.postMessage('cross-storage:poll', targetOrigin);
|
|
498
|
+
}, 1000);
|
|
499
|
+
};
|
|
500
|
+
|
|
501
|
+
/**
|
|
502
|
+
* Creates a new iFrame containing the hub. Applies the necessary styles to
|
|
503
|
+
* hide the element from view, prior to adding it to the document body.
|
|
504
|
+
* Returns the created element.
|
|
505
|
+
*
|
|
506
|
+
* @private
|
|
507
|
+
*
|
|
508
|
+
* @param {string} url The url to the hub
|
|
509
|
+
* returns {HTMLIFrameElement} The iFrame element itself
|
|
510
|
+
*/
|
|
511
|
+
CrossStorageClient.prototype._createFrame = function(url) {
|
|
512
|
+
var frame, key;
|
|
513
|
+
|
|
514
|
+
frame = window.document.createElement('iframe');
|
|
515
|
+
frame.id = this._frameId;
|
|
516
|
+
|
|
517
|
+
// Style the iframe
|
|
518
|
+
for (key in CrossStorageClient.frameStyle) {
|
|
519
|
+
if (CrossStorageClient.frameStyle.hasOwnProperty(key)) {
|
|
520
|
+
frame.style[key] = CrossStorageClient.frameStyle[key];
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
window.document.body.appendChild(frame);
|
|
525
|
+
frame.src = url;
|
|
526
|
+
|
|
527
|
+
return frame;
|
|
528
|
+
};
|
|
529
|
+
|
|
530
|
+
/**
|
|
531
|
+
* Sends a message containing the given method and params to the hub. Stores
|
|
532
|
+
* a callback in the _requests object for later invocation on message, or
|
|
533
|
+
* deletion on timeout. Returns a promise that is settled in either instance.
|
|
534
|
+
*
|
|
535
|
+
* In in-process mode (Node.js/OHOS), this directly calls the hub methods
|
|
536
|
+
* instead of using postMessage.
|
|
537
|
+
*
|
|
538
|
+
* @private
|
|
539
|
+
*
|
|
540
|
+
* @param {string} method The method to invoke
|
|
541
|
+
* @param {*} params The arguments to pass
|
|
542
|
+
* @returns {Promise} A promise that is settled on hub response or timeout
|
|
543
|
+
*/
|
|
544
|
+
CrossStorageClient.prototype._request = function(method, params) {
|
|
545
|
+
var req, client;
|
|
546
|
+
|
|
547
|
+
if (this._closed) {
|
|
548
|
+
return this._promise.reject(new Error('CrossStorageClient has closed'));
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
// In-process mode: directly call hub methods
|
|
552
|
+
if (this._inProcessHub) {
|
|
553
|
+
client = this;
|
|
554
|
+
var hubMethod = method;
|
|
555
|
+
var hubFn = this._inProcessHub['_' + hubMethod];
|
|
556
|
+
|
|
557
|
+
return new this._promise(function(resolve, reject) {
|
|
558
|
+
try {
|
|
559
|
+
var result = hubFn.call(client._inProcessHub, params || {});
|
|
560
|
+
resolve(result);
|
|
561
|
+
} catch (err) {
|
|
562
|
+
reject(new Error(err.message));
|
|
563
|
+
}
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
client = this;
|
|
568
|
+
client._count++;
|
|
569
|
+
|
|
570
|
+
req = {
|
|
571
|
+
id: this._id + ':' + client._count,
|
|
572
|
+
method: 'cross-storage:' + method,
|
|
573
|
+
params: params
|
|
574
|
+
};
|
|
575
|
+
|
|
576
|
+
return new this._promise(function(resolve, reject) {
|
|
577
|
+
var timeout, originalToJSON, targetOrigin;
|
|
578
|
+
|
|
579
|
+
// Timeout if a response isn't received after 4s
|
|
580
|
+
timeout = setTimeout(function() {
|
|
581
|
+
if (!client._requests[req.id]) return;
|
|
582
|
+
|
|
583
|
+
delete client._requests[req.id];
|
|
584
|
+
reject(new Error('Timeout: could not perform ' + req.method));
|
|
585
|
+
}, client._timeout);
|
|
586
|
+
|
|
587
|
+
// Add request callback
|
|
588
|
+
client._requests[req.id] = function(err, result) {
|
|
589
|
+
clearTimeout(timeout);
|
|
590
|
+
delete client._requests[req.id];
|
|
591
|
+
if (err) return reject(new Error(err));
|
|
592
|
+
resolve(result);
|
|
593
|
+
};
|
|
594
|
+
|
|
595
|
+
// In case we have a broken Array.prototype.toJSON, e.g. because of
|
|
596
|
+
// old versions of prototype
|
|
597
|
+
if (Array.prototype.toJSON) {
|
|
598
|
+
originalToJSON = Array.prototype.toJSON;
|
|
599
|
+
Array.prototype.toJSON = null;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
// postMessage requires that the target origin be set to "*" for "file://"
|
|
603
|
+
targetOrigin = (client._origin === 'file://') ? '*' : client._origin;
|
|
604
|
+
|
|
605
|
+
// Send serialized message
|
|
606
|
+
client._hub.postMessage(JSON.stringify(req), targetOrigin);
|
|
607
|
+
|
|
608
|
+
// Restore original toJSON
|
|
609
|
+
if (originalToJSON) {
|
|
610
|
+
Array.prototype.toJSON = originalToJSON;
|
|
611
|
+
}
|
|
612
|
+
});
|
|
613
|
+
};
|
|
614
|
+
|
|
615
|
+
/**
|
|
616
|
+
* Export for various environments.
|
|
617
|
+
*/
|
|
618
|
+
if (typeof module !== 'undefined' && module.exports) {
|
|
619
|
+
module.exports = CrossStorageClient;
|
|
620
|
+
} else if (typeof exports !== 'undefined') {
|
|
621
|
+
exports.CrossStorageClient = CrossStorageClient;
|
|
622
|
+
} else if (typeof define === 'function' && define.amd) {
|
|
623
|
+
define([], function() {
|
|
624
|
+
return CrossStorageClient;
|
|
625
|
+
});
|
|
626
|
+
} else {
|
|
627
|
+
root.CrossStorageClient = CrossStorageClient;
|
|
628
|
+
}
|
|
629
|
+
}(typeof global !== 'undefined' ? global : this));
|