hina-cloud-js-sdk 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,425 @@
1
+ import { Log, SDKDebug, _ } from "./utils";
2
+
3
+ function xhr() {
4
+ if (
5
+ typeof window.XMLHttpRequest !== "undefined" &&
6
+ "withCredentials" in new XMLHttpRequest()
7
+ ) {
8
+ return new XMLHttpRequest();
9
+ } else if (typeof window.XDomainRequest !== "undefined") {
10
+ let XDomainRequest = window.XDomainRequest;
11
+ return new XDomainRequest();
12
+ } else {
13
+ return null;
14
+ }
15
+ }
16
+
17
+ function ajax(para) {
18
+ SDKDebug.checkAjax(para.url);
19
+
20
+ para.timeout = para.timeout || 20000;
21
+
22
+ let r = xhr();
23
+
24
+ if (!r) {
25
+ return false;
26
+ }
27
+
28
+ para = _.extend(
29
+ {
30
+ success: function () {},
31
+ error: function () {},
32
+ },
33
+ para
34
+ );
35
+
36
+ let oldSuccess = para.success;
37
+ let oldError = para.error;
38
+ let errorTimer;
39
+
40
+ let clearTimer = (isReset = false) => {
41
+ if (errorTimer) {
42
+ clearTimeout(errorTimer);
43
+ errorTimer = null;
44
+ if (isReset) {
45
+ r.onreadystatechange = null;
46
+ r.onload = null;
47
+ r.onerror = null;
48
+ }
49
+ }
50
+ };
51
+
52
+ para.success = function (data) {
53
+ oldSuccess(data);
54
+ clearTimer();
55
+ };
56
+
57
+ para.error = function (err) {
58
+ oldError(err);
59
+ clearTimer();
60
+ };
61
+
62
+ errorTimer = setTimeout(() => {
63
+ try {
64
+ if (r && _.check.isObject(r) && r.abort) {
65
+ r.abort();
66
+ }
67
+ } catch (error) {
68
+ Log.log(error);
69
+ }
70
+
71
+ clearTimer(true);
72
+ }, para.timeout);
73
+
74
+ let getSafeJSON = (d) => {
75
+ if (!d) {
76
+ return "";
77
+ }
78
+ return _.safeJSONParse(d);
79
+ };
80
+
81
+ if (typeof XDomainRequest !== "undefined" && r instanceof window.XDomainRequest) {
82
+ r.onload = function () {
83
+ para.success && para.success(getSafeJSON(r.responseText));
84
+ r.onreadystatechange = null;
85
+ r.onload = null;
86
+ r.onerror = null;
87
+ };
88
+ r.onerror = function () {
89
+ para.error && para.error(getSafeJSON(r.responseText), r.status);
90
+ r.onreadystatechange = null;
91
+ r.onerror = null;
92
+ r.onload = null;
93
+ };
94
+ }
95
+
96
+ r.open("post", para.url, true);
97
+
98
+ if (para.credentials) {
99
+ r.withCredentials = true;
100
+ }
101
+
102
+ if (r.setRequestHeader) {
103
+ r.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
104
+ }
105
+
106
+ r.onreadystatechange = function () {
107
+ try {
108
+ if (r.readyState === 4) {
109
+ if ((r.status >= 200 && r.status < 300) || r.status === 304) {
110
+ para.success(getSafeJSON(r.responseText));
111
+ } else {
112
+ para.error(getSafeJSON(r.responseText), r.status);
113
+ }
114
+ r.onreadystatechange = null;
115
+ r.onload = null;
116
+ }
117
+ } catch (e) {
118
+ r.onreadystatechange = null;
119
+ r.onload = null;
120
+ }
121
+ };
122
+
123
+ r.send(para.data || null);
124
+ }
125
+
126
+ class AjaxSend {
127
+ constructor(para) {
128
+ this.callback = para.callback;
129
+ this.serverUrl = para.serverUrl;
130
+ this.data = para.data;
131
+ this.dataSendTimeout = para.dataSendTimeout;
132
+ }
133
+
134
+ run() {
135
+ ajax({
136
+ url: this.serverUrl,
137
+ data: this.data,
138
+ timeout: this.dataSendTimeout,
139
+ success: this.cb,
140
+ error: this.cb,
141
+ });
142
+ }
143
+
144
+ cb() {
145
+ if (this.callback) {
146
+ if (!_.check.isFunction(this.callback)) {
147
+ Log.log("sdk callback is not a function");
148
+ return;
149
+ }
150
+ this.callback();
151
+ }
152
+ }
153
+ }
154
+
155
+ class ImageSend {
156
+ constructor(para) {
157
+ this.callback = para.callback;
158
+ this.serverUrl = para.serverUrl;
159
+ this.data = para.data;
160
+ this.dataSendTimeout = para.dataSendTimeout;
161
+
162
+ this.img = document.createElement("img");
163
+ this.img.width = 1;
164
+ this.img.height = 1;
165
+
166
+ if (para.imgUseCrossOrigin) {
167
+ this.img.crossOrigin = "anonymous";
168
+ }
169
+ }
170
+
171
+ run() {
172
+ let callAndDelete = () => {
173
+ if (this.img && !this.img.complete) {
174
+ this.img.complete = true;
175
+ this.cb && this.cb();
176
+ }
177
+ };
178
+
179
+ this.img.onload = function () {
180
+ this.onload = null;
181
+ callAndDelete();
182
+ };
183
+
184
+ this.img.onerror = function () {
185
+ this.onerror = null;
186
+ callAndDelete();
187
+ };
188
+
189
+ this.img.onabort = function () {
190
+ this.onabort = null;
191
+ callAndDelete();
192
+ };
193
+
194
+ if (this.serverUrl.indexOf("?") !== -1) {
195
+ this.img.src = this.serverUrl + "&" + this.data;
196
+ } else {
197
+ this.img.src = this.serverUrl + "?" + this.data;
198
+ }
199
+ }
200
+
201
+ cb() {
202
+ if (this.callback) {
203
+ if (!_.check.isFunction(this.callback)) {
204
+ Log.log("sdk callback is not a function");
205
+ return;
206
+ }
207
+ this.callback();
208
+ }
209
+
210
+ setTimeout(() => {
211
+ let sys = _.info.browser().type;
212
+ if (sys === "ie") {
213
+ this.img.src = "about:blank";
214
+ } else {
215
+ this.img.src = "";
216
+ }
217
+ }, this.dataSendTimeout);
218
+ }
219
+ }
220
+
221
+ class BeaconSend {
222
+ constructor(para) {
223
+ this.callback = para.callback;
224
+ this.serverUrl = para.serverUrl;
225
+ this.data = para.data;
226
+ }
227
+
228
+ run() {
229
+ if (
230
+ _.check.isObject(navigator) &&
231
+ _.check.isFunction(navigator.sendBeacon)
232
+ ) {
233
+ let formData = new FormData();
234
+ formData.append("data", _.base64Encode(this.data));
235
+ navigator.sendBeacon(this.serverUrl, formData);
236
+ }
237
+
238
+ setTimeout(() => {
239
+ this.cb();
240
+ });
241
+ }
242
+
243
+ cb() {
244
+ if (this.callback) {
245
+ if (!_.check.isFunction(this.callback)) {
246
+ Log.log("sdk callback is not a function");
247
+ return;
248
+ }
249
+ this.callback();
250
+ }
251
+ }
252
+ }
253
+
254
+ let dataStoragePrefix = "hinasdk_data_";
255
+ let tabStoragePrefix = "hinasdk_tab";
256
+
257
+ /**
258
+ * 批量发送
259
+ * 分为两个cache, 第一个cache存储tabkey和dataKey、expireTime
260
+ * {
261
+ * k: tabkey,
262
+ * v: [{dataKey, expireTime}]
263
+ * }
264
+ * 第二个cache存储引用dataKey和data组成真实数据kv
265
+ * {
266
+ * k: dataKey,
267
+ * v: data
268
+ * }
269
+ * data不需要加密
270
+ */
271
+
272
+ class BatchSend {
273
+ constructor(config) {
274
+ this.timer = null;
275
+ this.sendTimeStamp = 0;
276
+ this.batchConfig = _.extend(
277
+ {
278
+ dataSendTimeout: 6000,
279
+ sendInterval: 6000,
280
+ storageLimit: 200,
281
+ },
282
+ config["batchSend"]
283
+ );
284
+ this.tabKey = tabStoragePrefix;
285
+ this.config = config;
286
+ }
287
+
288
+ batchInterval() {
289
+ if (!SDKDebug.checkServerUrl(this.config["serverUrl"])) return;
290
+
291
+ this.timer = setTimeout(() => {
292
+ this.recycle();
293
+ this.send();
294
+ clearTimeout(this.timer);
295
+ this.batchInterval();
296
+ }, this.batchConfig["sendInterval"]);
297
+ }
298
+
299
+ request(data, dataKeys) {
300
+ ajax({
301
+ url: this.config["serverUrl"],
302
+ data:
303
+ "data_list=" + encodeURIComponent(_.base64Encode(JSON.stringify(data))),
304
+ timeout: this.batchConfig["dataSendTimeout"],
305
+ success: () => {
306
+ this.remove(dataKeys);
307
+ this.sendTimeStamp = 0;
308
+ },
309
+ error: () => {
310
+ this.sendTimeStamp = 0;
311
+ },
312
+ });
313
+ }
314
+
315
+ send() {
316
+ if (
317
+ this.sendTimeStamp &&
318
+ _.now() - this.sendTimeStamp < this.batchConfig["dataSendTimeout"]
319
+ ) {
320
+ return;
321
+ }
322
+ let tabStorage = _.localStorage.get(this.tabKey);
323
+ if (tabStorage) {
324
+ this.sendTimeStamp = _.now();
325
+ let tabStorageKeys = _.safeJSONParse(tabStorage) || [];
326
+ if (tabStorageKeys.length) {
327
+ let data = [];
328
+ let tabList = [];
329
+ let len =
330
+ tabStorageKeys.length < this.batchConfig["storageLimit"]
331
+ ? tabStorageKeys.length
332
+ : this.batchConfig["storageLimit"];
333
+ for (let i = 0; i < len; i++) {
334
+ let d = _.readObjectVal(tabStorageKeys[i].dataKey);
335
+ tabList.push(tabStorageKeys[i].dataKey);
336
+ data.push(d);
337
+ }
338
+ this.request(data, tabList);
339
+ }
340
+ }
341
+ }
342
+
343
+ remove(dataKeys) {
344
+ let tabStorage = _.localStorage.get(this.tabKey);
345
+ if (tabStorage) {
346
+ let tabStorageKeys = _.safeJSONParse(tabStorage) || [];
347
+ tabStorageKeys = tabStorageKeys?.map((item) => item.dataKey);
348
+ for (let i = 0; i < dataKeys.length; i++) {
349
+ let idx = _.indexOf(tabStorageKeys, dataKeys[i]);
350
+ if (idx > -1) {
351
+ tabStorage.splice(idx, 1);
352
+ }
353
+ _.localStorage.remove(dataKeys[i]);
354
+ }
355
+ _.localStorage.set(this.tabKey, JSON.stringify(tabStorage));
356
+ }
357
+ }
358
+
359
+ add(data) {
360
+ let dataKey = dataStoragePrefix + _.getRandom();
361
+ let tabStorage = _.localStorage.get(this.tabKey);
362
+ if (tabStorage == null) {
363
+ tabStorage = [];
364
+ } else {
365
+ tabStorage = _.safeJSONParse(tabStorage) || [];
366
+ }
367
+
368
+ if(_.check.isString(tabStorage)){
369
+ tabStorage = _.safeJSONParse(tabStorage) || [];
370
+ }
371
+
372
+ tabStorage.push({
373
+ dataKey,
374
+ expireTime: _.now() + this.batchConfig["sendInterval"] * 2,
375
+ });
376
+ _.localStorage.set(this.tabKey, JSON.stringify(tabStorage));
377
+ _.saveObjectVal(dataKey, data);
378
+
379
+ if (tabStorage.length > this.batchConfig["storageLimit"]) {
380
+ let spliceData = tabStorage.slice(0, 20);
381
+ let dataList = [];
382
+ for (let i = 0; i < spliceData.length; i++) {
383
+ let item = _.readObjectVal(spliceData[i].dataKey);
384
+ dataList.push(item);
385
+ }
386
+ this.request(dataList, spliceData);
387
+ }
388
+ // 无加密data
389
+ if (data.type === "track_signup" || data.event === "H_pageview") {
390
+ this.send();
391
+ }
392
+ }
393
+
394
+ //找出过期的删除
395
+ recycle() {
396
+ let tabStorageKeys = _.localStorage.get(this.tabKey);
397
+ if (tabStorageKeys) {
398
+ let tabStorage = _.safeJSONParse(tabStorageKeys) || [];
399
+ if (tabStorage.length) {
400
+ let delList = [];
401
+ for (let i = 0; i < tabStorage.length; i++) {
402
+ if (_.now() > tabStorage[i].expireTime) {
403
+ delList.push(tabStorage[i].dataKey);
404
+ }
405
+ }
406
+ this.remove(delList);
407
+ } else {
408
+ for (let i = 0; i < localStorage.length; i++) {
409
+ let key = _.localStorage.key(i);
410
+ if (key.indexOf(dataStoragePrefix) === 0) {
411
+ tabStorage.push({
412
+ dataKey: key,
413
+ expireTime: _.now() + this.batchConfig["sendInterval"] * 2,
414
+ });
415
+ }
416
+ }
417
+ if (tabStorage.length > 0) {
418
+ _.localStorage.set(this.tabKey, JSON.stringify(tabStorage));
419
+ }
420
+ }
421
+ }
422
+ }
423
+ }
424
+
425
+ export { ajax, AjaxSend, ImageSend, BeaconSend, BatchSend };