@subiz/widget-v4 4.1.167 → 4.1.169

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.
Files changed (43) hide show
  1. package/app.js +2 -0
  2. package/compiled_src/lib.js +4 -0
  3. package/compiled_src/plugin.js +1 -0
  4. package/compiled_src/store/api.js +0 -1
  5. package/compiled_src/store/api_fake.js +0 -1
  6. package/compiled_src/util/common.js +24 -0
  7. package/dist/198a92fcacc.app.js +1 -0
  8. package/dist/{198a3614cdc.chat.html → 198a92fcacc.chat.html} +1 -1
  9. package/dist/198a92fcacc.chat.js +1 -0
  10. package/dist/{198a3614cdc.form.html → 198a92fcacc.form.html} +1 -1
  11. package/dist/198a92fcacc.form.js +1 -0
  12. package/dist/{198a3614cdc.index.html → 198a92fcacc.index.html} +3 -2
  13. package/dist/{198a3614cdc.library.html → 198a92fcacc.library.html} +3 -2
  14. package/dist/198a92fcacc.library.js +1 -0
  15. package/dist/{198a3614cdc.preview.html → 198a92fcacc.preview.html} +1 -1
  16. package/dist/198a92fcacc.preview.js +1 -0
  17. package/dist/198a92fcacc.webview.js +1 -0
  18. package/dist/HASH +1 -1
  19. package/dist/screenshoot.js +55 -35
  20. package/index.html +1 -0
  21. package/package.json +6 -6
  22. package/src/lib.js +6 -0
  23. package/src/plugin.js +1 -0
  24. package/src/store/api.js +0 -1
  25. package/src/store/api_fake.js +0 -1
  26. package/src/util/common.js +28 -0
  27. package/tracking.js +114 -36
  28. package/dist/198a3614cdc.app.js +0 -1
  29. package/dist/198a3614cdc.chat.js +0 -1
  30. package/dist/198a3614cdc.form.js +0 -1
  31. package/dist/198a3614cdc.library.js +0 -1
  32. package/dist/198a3614cdc.preview.js +0 -1
  33. package/dist/198a3614cdc.webview.js +0 -1
  34. /package/dist/{198a3614cdc.127.js → 198a92fcacc.127.js} +0 -0
  35. /package/dist/{198a3614cdc.132.js → 198a92fcacc.132.js} +0 -0
  36. /package/dist/{198a3614cdc.185.js → 198a92fcacc.185.js} +0 -0
  37. /package/dist/{198a3614cdc.27.js → 198a92fcacc.27.js} +0 -0
  38. /package/dist/{198a3614cdc.278.js → 198a92fcacc.278.js} +0 -0
  39. /package/dist/{198a3614cdc.339.js → 198a92fcacc.339.js} +0 -0
  40. /package/dist/{198a3614cdc.459.js → 198a92fcacc.459.js} +0 -0
  41. /package/dist/{198a3614cdc.490.js → 198a92fcacc.490.js} +0 -0
  42. /package/dist/{198a3614cdc.74.js → 198a92fcacc.74.js} +0 -0
  43. /package/dist/{198a3614cdc.997.js → 198a92fcacc.997.js} +0 -0
package/dist/HASH CHANGED
@@ -1 +1 @@
1
- 198a3614cdc
1
+ 198a92fcacc
@@ -2262,6 +2262,7 @@ __webpack_require__.r(__webpack_exports__);
2262
2262
  /* harmony export */ usersOfConvo: () => (/* binding */ usersOfConvo)
2263
2263
  /* harmony export */ });
2264
2264
  var op = __webpack_require__(3259);
2265
+ const ajax = __webpack_require__(318);
2265
2266
  function rgbToHsl(r, g, b) {
2266
2267
  ;
2267
2268
  r /= 255, g /= 255, b /= 255;
@@ -2451,6 +2452,29 @@ var setInterval = f.contentWindow.setInterval.bind(f.contentWindow);
2451
2452
  var clearInterval = f.contentWindow.clearInterval.bind(f.contentWindow);
2452
2453
  var clearTimeout = f.contentWindow.clearTimeout.bind(f.contentWindow);
2453
2454
  var XMLHttpRequest = f.contentWindow.XMLHttpRequest;
2455
+ const _originalOpen = XMLHttpRequest.prototype.open;
2456
+ const _originalSend = XMLHttpRequest.prototype.send;
2457
+ XMLHttpRequest.prototype.open = function (method, url, async, user, password) {
2458
+ this._url = url;
2459
+ return _originalOpen.apply(this, arguments);
2460
+ };
2461
+ XMLHttpRequest.prototype.send = function (body) {
2462
+ let old = this.onreadystatechange;
2463
+ this.onreadystatechange = function (a, b, c) {
2464
+ if (this.readyState != 4) return;
2465
+ if (this.status >= 400 || this.status == 0) {
2466
+ if (typeof sbz_logs == 'object') sbz_logs.push({
2467
+ type: 'xhrfailed',
2468
+ url: this._url,
2469
+ status: this.status,
2470
+ response: this.responseText
2471
+ });
2472
+ }
2473
+ old(a, b, c);
2474
+ };
2475
+ return _originalSend.apply(this, arguments);
2476
+ };
2477
+ ajax.env.XMLHttpRequest = XMLHttpRequest;
2454
2478
  function convoOfEv(ev) {
2455
2479
  if (!ev) return '';
2456
2480
  return op.get(ev, 'data.conversation.id', op.get(ev, 'data.message.conversation_id'));
@@ -3737,7 +3761,6 @@ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol"
3737
3761
  function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
3738
3762
 
3739
3763
  const ajax = __webpack_require__(318);
3740
- ajax.env.XMLHttpRequest = util/* default */.A.XMLHttpRequest;
3741
3764
  function jsonifyTextPlain(param, done) {
3742
3765
  let req = param.request;
3743
3766
  if (req.content_type === 'text/plain' && req.body !== undefined) req.body = util/* default */.A.stringify(req.body);
@@ -3873,7 +3896,10 @@ function jsonifyTextPlain(param, done) {
3873
3896
  _defineProperty(this, "time", _ => ajax.setBaseUrl(this.ApiURL).withCredentials(true).setParser('json').get('/time'));
3874
3897
  _defineProperty(this, "updateUser", data => this.apireq.post(`/accounts/${this.accid}/users/${this.uid}`, data));
3875
3898
  _defineProperty(this, "getUser", _ => this.apireq.get(`/accounts/${this.accid}/users/${this.uid}`));
3876
- _defineProperty(this, "getAccountSetting", () => this.apireq.get(`/accounts/${this.accid}/settings`));
3899
+ _defineProperty(this, "getAccountSetting", () => {
3900
+ let href = util/* default */.A.get(document, 'location.href');
3901
+ return this.apireq.addQuery('url', href).get(`/accounts/${this.accid}/settings`);
3902
+ });
3877
3903
  _defineProperty(this, "sendImpression", impression => this.apireq.post(`/accounts/${this.accid}/campaigns/${impression.campaign_id}/impressions`, impression));
3878
3904
  _defineProperty(this, "sendConversion", conversion => this.apireq.post(`/accounts/${this.accid}/campaigns/${conversion.campaign_id}/conversions`, conversion));
3879
3905
  _defineProperty(this, "lookupOrder", key => ajax.setBaseUrl(this.ApiURL).setContentType('text/plain').beforeHook(jsonifyTextPlain).setParser('json').addQuery('x-signed-key', key).get(`/orders/123`));
@@ -4428,7 +4454,6 @@ function api_fake_toPropertyKey(t) { var i = api_fake_toPrimitive(t, "string");
4428
4454
  function api_fake_toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
4429
4455
 
4430
4456
  const api_fake_ajax = __webpack_require__(318);
4431
- api_fake_ajax.env.XMLHttpRequest = window.XMLHttpRequest;
4432
4457
  function api_fake_jsonifyTextPlain(param, done) {
4433
4458
  let req = param.request;
4434
4459
  if (req.content_type === 'text/plain' && req.body !== undefined) req.body = util/* default */.A.stringify(req.body);
@@ -5090,6 +5115,7 @@ class Store {
5090
5115
  });
5091
5116
  }
5092
5117
  }, 2000);
5118
+ this.loopCheckConnectionStatus();
5093
5119
  }
5094
5120
  async loopCheckConnectionStatus() {
5095
5121
  while (true) {
@@ -5248,8 +5274,6 @@ class Store {
5248
5274
  if (!this.isInsync('account')) this.reconnect();
5249
5275
  }
5250
5276
  async init(accid, mask, userRef, options) {
5251
- this.readyGate.close();
5252
- accid = (accid || '').trim();
5253
5277
  this.g_sync = {};
5254
5278
  if (!options || !options.use_subdomain_cookie) {
5255
5279
  if (window && window.location && window.location.hostname) {
@@ -5284,69 +5308,58 @@ class Store {
5284
5308
  cleanMyLocalStorage();
5285
5309
  this.clearBrowserData();
5286
5310
  }
5287
- this.lightConvos = {};
5288
- this.cache = new kv(new lsdb('sbz2023', true), 'kv');
5289
5311
  this.api.setAuth({
5290
5312
  accid: accid
5291
5313
  });
5292
5314
  if (userRef) this.cookie.set('__sbref', userRef);
5293
- let user = await this.api.setAuth({
5315
+ return this._initResource(accid, {
5294
5316
  accid: accid,
5295
5317
  mask: mask,
5296
5318
  ref: userRef
5319
+ }, {
5320
+ account_id: accid,
5321
+ user_ref: userRef,
5322
+ user_mask: mask
5297
5323
  });
5324
+ }
5325
+ async _initResource(accid, auth, realtimep) {
5326
+ this.g_sync = {};
5327
+ let user = await this.api.setAuth(auth);
5298
5328
  if (user.error) {
5299
5329
  console.error(user);
5300
5330
  return user;
5301
5331
  }
5332
+ this.readyGate.close();
5333
+ accid = (accid || '').trim();
5334
+ this.lightConvos = this.kv.match('light_convos') || {};
5335
+ this.cache = new kv(new lsdb('sbz2023', true), 'kv');
5302
5336
  if (this.realtime) this.realtime.stop();
5303
- if (this.fake) this.realtime = new realtime_fake();else this.realtime = new Realtime(this.RealtimeURL, {
5304
- account_id: accid,
5305
- user_ref: userRef,
5306
- user_mask: mask
5307
- }, xhrsend);
5337
+ if (this.fake) this.realtime = new realtime_fake();else this.realtime = new Realtime(this.RealtimeURL, realtimep, xhrsend);
5308
5338
  this.realtime.onEvent(this.onWs.bind(this));
5309
5339
  this.realtime.onInterrupted(async () => {
5310
5340
  await this.reconnectWs(accid, user.id);
5311
5341
  this.reconnect();
5312
5342
  });
5313
- this.lightConvos = this.kv.match('light_convos') || {};
5314
5343
  this.seenLoop();
5315
5344
  await this.reconnect({
5316
5345
  user: user
5317
5346
  });
5318
5347
  this.readyGate.open();
5319
5348
  this.reconnectWs(accid, user.id);
5320
- this.loopCheckConnectionStatus();
5321
5349
  return user;
5322
5350
  }
5323
5351
  async login(accid, user_id, tokenGenerator) {
5324
5352
  if (this._die) return dieErr;
5325
- let user = await this.api.setAuth({
5353
+ cleanMyLocalStorage();
5354
+ this.clearBrowserData();
5355
+ this.new_visitor = false;
5356
+ return this._initResource(accid, {
5326
5357
  accid: accid,
5327
5358
  profile_id: user_id,
5328
5359
  tokenGenerator: tokenGenerator
5329
- });
5330
- if (user.error) return user;
5331
- this.clearBrowserData();
5332
- this.readyGate.close();
5333
- accid = (accid || '').trim();
5334
- this.g_sync = {};
5335
- this.new_visitor = false;
5336
- if (this.realtime) this.realtime.stop();
5337
- if (this.fake) this.realtime = new realtime_fake();else this.realtime = new Realtime(this.RealtimeURL, {
5360
+ }, {
5338
5361
  getAccessToken: () => this.api.token_generator()
5339
5362
  });
5340
- this.realtime.onEvent(this.onWs.bind(this));
5341
- this.realtime.onInterrupted(async () => {
5342
- await this.reconnectWs(accid, user.id);
5343
- this.reconnect();
5344
- });
5345
- this.seenLoop();
5346
- await this.reconnect();
5347
- this.readyGate.open();
5348
- this.reconnectWs(accid, user.id);
5349
- return user;
5350
5363
  }
5351
5364
  logout(userRef) {
5352
5365
  return this.api.setAuth({
@@ -9100,6 +9113,8 @@ class Event extends common/* Com */.Mw {
9100
9113
  renderAIDevNote() {
9101
9114
  if (!this.props.isLast) return null;
9102
9115
  if (!this.props.store.isAIDevMode()) return null;
9116
+ let cid = util/* default */.A.get(this.props.event.ev, 'data.message.conversation_id', '');
9117
+ if (cid.startsWith('cslight')) return null;
9103
9118
  let ev = util/* default */.A.get(this.props.event, 'ev') || {};
9104
9119
  let pongs = util/* default */.A.get(ev, 'data.message.pongs') || [];
9105
9120
  let hasLike = this.hasLike();
@@ -11327,6 +11342,7 @@ function Plugin(plugin, store, $container, lib) {
11327
11342
  if (!$div) return;
11328
11343
  if (!this.t) return;
11329
11344
  if (plugin.type === 'chatbox') {
11345
+ if (typeof sbz_info != 'undefined') sbz_info.render_chatbox = util/* default */.A.Date.now();
11330
11346
  (0,preact_module/* render */.XX)((0,preact_module.h)(Main, {
11331
11347
  destroy: _destroy,
11332
11348
  plugin: plugin,
@@ -11670,10 +11686,13 @@ class lib_Widget {
11670
11686
  return outmask;
11671
11687
  }
11672
11688
  async setAccount(id, $container, mask, cb) {
11689
+ if (typeof sbz_info != 'undefined') sbz_info.set_account = util/* default */.A.Date.now();
11673
11690
  if (!id) return {
11674
11691
  error: 'empty_account_id'
11675
11692
  };
11693
+ if (typeof sbz_info != 'undefined') sbz_info.set_account_1 = util/* default */.A.Date.now();
11676
11694
  let outmask = await this.store.init(id, mask);
11695
+ if (typeof sbz_info != 'undefined') sbz_info.set_account_2 = util/* default */.A.Date.now();
11677
11696
  util/* default */.A.setTimeout(() => window.postMessage({
11678
11697
  type: 'widget_loaded',
11679
11698
  account_id: id
@@ -11686,6 +11705,7 @@ class lib_Widget {
11686
11705
  util/* default */.A.clearInterval(clean);
11687
11706
  }, 1000);
11688
11707
  this.domGate.open();
11708
+ if (typeof sbz_info != 'undefined') sbz_info.set_account_done = util/* default */.A.Date.now();
11689
11709
  cb && cb(outmask);
11690
11710
  return outmask;
11691
11711
  }
package/index.html CHANGED
@@ -15,6 +15,7 @@
15
15
  <h1>Subiz widget testing</h1>
16
16
  <button id="test1">Test 1</button>
17
17
  <script>
18
+ let sbz_info = {}
18
19
  function getUrlParameter(name) {
19
20
  name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]')
20
21
  var regex = new RegExp('[\\?&]' + name + '=([^&#]*)')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@subiz/widget-v4",
3
- "version": "4.1.167",
3
+ "version": "4.1.169",
4
4
  "main": "./compiled_src/lib.js",
5
5
  "scripts": {
6
6
  "dev": "npx webpack serve --config webpack.dev.js",
@@ -21,15 +21,15 @@
21
21
  "preact": "10.19.3"
22
22
  },
23
23
  "devDependencies": {
24
- "@babel/cli": "^7.28.0",
25
- "@babel/core": "^7.28.0",
24
+ "@babel/cli": "^7.28.3",
25
+ "@babel/core": "^7.28.3",
26
26
  "@babel/plugin-proposal-class-properties": "^7.18.6",
27
27
  "@babel/plugin-transform-react-jsx": "^7.27.1",
28
- "@babel/preset-env": "^7.28.0",
28
+ "@babel/preset-env": "^7.28.3",
29
29
  "babel-loader": "^10.0.0",
30
30
  "babel-plugin-syntax-jsx": "^6.18.0",
31
- "html-webpack-plugin": "^5.6.3",
32
- "puppeteer": "^24.16.1",
31
+ "html-webpack-plugin": "^5.6.4",
32
+ "puppeteer": "^24.16.2",
33
33
  "webpack": "^5.101.1",
34
34
  "webpack-bundle-analyzer": "^4.10.2",
35
35
  "webpack-cli": "^6.0.1",
package/src/lib.js CHANGED
@@ -117,8 +117,12 @@ class Widget {
117
117
  }
118
118
 
119
119
  async setAccount(id, $container, mask, cb) {
120
+ if (typeof sbz_info != 'undefined') sbz_info.set_account = sb.Date.now()
121
+
120
122
  if (!id) return {error: 'empty_account_id'} // do nothing
123
+ if (typeof sbz_info != 'undefined') sbz_info.set_account_1 = sb.Date.now()
121
124
  let outmask = await this.store.init(id, mask)
125
+ if (typeof sbz_info != 'undefined') sbz_info.set_account_2 = sb.Date.now()
122
126
  sb.setTimeout(() => window.postMessage({type: 'widget_loaded', account_id: id}, '*'), 0) // send to chrome extension
123
127
  // add container to client site
124
128
  if (!$container) $container = document.body || document
@@ -131,6 +135,8 @@ class Widget {
131
135
  sb.clearInterval(clean)
132
136
  }, 1000)
133
137
  this.domGate.open()
138
+ if (typeof sbz_info != 'undefined') sbz_info.set_account_done = sb.Date.now()
139
+
134
140
  cb && cb(outmask)
135
141
  return outmask
136
142
  }
package/src/plugin.js CHANGED
@@ -73,6 +73,7 @@ function Plugin(plugin, store, $container, lib) {
73
73
 
74
74
  if (plugin.type === 'chatbox') {
75
75
  // if (!Chatbox) Chatbox = (await LoadChatbox()).default
76
+ if (typeof sbz_info != 'undefined' ) sbz_info.render_chatbox = sb.Date.now()
76
77
  render(<Chatbox destroy={_destroy} plugin={plugin} store={store} ref={_ref} t={this.t} />, $div)
77
78
  }
78
79
 
package/src/store/api.js CHANGED
@@ -1,6 +1,5 @@
1
1
  import sb from '../util/index.js'
2
2
  const ajax = require('@subiz/ajax/src/ajax.js')
3
- ajax.env.XMLHttpRequest = sb.XMLHttpRequest
4
3
 
5
4
  function jsonifyTextPlain(param, done) {
6
5
  let req = param.request
@@ -1,7 +1,6 @@
1
1
  import sb from '../util/index.js'
2
2
 
3
3
  const ajax = require('@subiz/ajax/src/ajax.js')
4
- ajax.env.XMLHttpRequest = window.XMLHttpRequest
5
4
 
6
5
  function jsonifyTextPlain(param, done) {
7
6
  let req = param.request
@@ -1,4 +1,5 @@
1
1
  var op = require('./op.js')
2
+ const ajax = require('@subiz/ajax/src/ajax.js')
2
3
 
3
4
  /**
4
5
  * Converts an RGB color value to HSL. Conversion formula
@@ -231,6 +232,33 @@ export var clearInterval = f.contentWindow.clearInterval.bind(f.contentWindow)
231
232
  export var clearTimeout = f.contentWindow.clearTimeout.bind(f.contentWindow)
232
233
  export var XMLHttpRequest = f.contentWindow.XMLHttpRequest
233
234
 
235
+ const _originalOpen = XMLHttpRequest.prototype.open
236
+ const _originalSend = XMLHttpRequest.prototype.send
237
+ XMLHttpRequest.prototype.open = function (method, url, async, user, password) {
238
+ this._url = url // store URL for logging
239
+ return _originalOpen.apply(this, arguments)
240
+ }
241
+
242
+ XMLHttpRequest.prototype.send = function (body) {
243
+ let old = this.onreadystatechange
244
+ this.onreadystatechange = function (a, b, c) {
245
+ if (this.readyState != 4) return
246
+ // request completed
247
+ if (this.status >= 400 || this.status == 0) {
248
+ if (typeof sbz_logs == 'object')
249
+ sbz_logs.push({
250
+ type: 'xhrfailed',
251
+ url: this._url,
252
+ status: this.status,
253
+ response: this.responseText,
254
+ })
255
+ }
256
+ old(a, b, c)
257
+ }
258
+ return _originalSend.apply(this, arguments)
259
+ }
260
+ ajax.env.XMLHttpRequest = XMLHttpRequest
261
+
234
262
  // convoOfEv extracts the ID of the conversation
235
263
  // that event is sent to
236
264
  export function convoOfEv(ev) {
package/tracking.js CHANGED
@@ -1,45 +1,123 @@
1
1
  // send signal to server when the chatbox is not visible
2
2
 
3
- setTimeout(() => {
4
- var $button = document.querySelector('div.chat-button')
5
-
6
- let status = ''
7
- if ($button) {
8
- var rect = $button.getBoundingClientRect()
9
- var visible = rect.width > 0 && rect.height > 0
10
- if (visible) {
11
- // the chatbox is visible
12
- return
13
- } else {
14
- // the chatbox is not visible
15
- status = 'hidden'
16
- }
17
- } else {
18
- // the chatbox is not found in DOM
19
- status = 'not-found'
3
+ if (typeof sbz_info == 'undefined') {
4
+ var sbz_logs = []
5
+ var sbz_info = {}
6
+ // Catch synchronous runtime errors (ReferenceError, TypeError, etc.)
7
+ window.onerror = function (message, source, lineno, colno, error) {
8
+ sbz_logs.push({
9
+ created: Date.now(),
10
+ type: 'error',
11
+ message: message,
12
+ source: source,
13
+ line: lineno,
14
+ column: colno,
15
+ error: error,
16
+ })
20
17
  }
21
18
 
22
- // Collect device and browser info
23
- var info = {
24
- status: status,
25
- href: window.location.href,
26
- userAgent: navigator.userAgent,
27
- platform: navigator.platform,
28
- language: navigator.language,
29
- timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
19
+ // Catch unhandled Promise rejections
20
+ window.addEventListener('unhandledrejection', function (event) {
21
+ sbz_logs.push({
22
+ created: Date.now(),
23
+ type: 'rejection',
24
+ reason: event.reason + '',
25
+ })
26
+ })
27
+
28
+ const _originalOpen = XMLHttpRequest.prototype.open
29
+ const _originalSend = XMLHttpRequest.prototype.send
30
+
31
+ XMLHttpRequest.prototype.open = function (method, url, async, user, password) {
32
+ this._url = url // store URL for logging
33
+ return _originalOpen.apply(this, arguments)
30
34
  }
31
35
 
32
- // Send via XHR
33
- var xhr = new XMLHttpRequest()
34
- xhr.open('POST', 'https://api.subiz.com.vn/4.1/chatbox-failed', true)
35
- xhr.withCredentials = false
36
- xhr.setRequestHeader('Content-Type', 'text/plain')
36
+ XMLHttpRequest.prototype.send = function (body) {
37
+ this.addEventListener('load', function () {
38
+ if (this.status >= 400) {
39
+ sbz_logs.push({
40
+ type: 'xhrfailed',
41
+ url: this._url,
42
+ status: this.status,
43
+ response: this.responseText,
44
+ })
45
+ }
46
+ })
47
+ this.addEventListener('error', function () {
48
+ console.error('XHR network error:', {url: this._url})
49
+ })
50
+ return _originalSend.apply(this, arguments)
51
+ }
37
52
 
38
- xhr.onreadystatechange = function () {
39
- if (xhr.readyState === XMLHttpRequest.DONE) {
40
- console.log('Server responded with:', xhr.status, xhr.responseText)
41
- }
53
+ // ===== 4. Catch fetch errors =====
54
+ const originalFetch = window.fetch
55
+ window.fetch = function (...args) {
56
+ return originalFetch(...args)
57
+ .then((response) => {
58
+ if (!response.ok) {
59
+ sbz_logs.push({
60
+ type: 'fetchfailed',
61
+ url: args[0],
62
+ status: response.status,
63
+ statusText: response.statusText,
64
+ })
65
+ }
66
+ return response
67
+ })
68
+ .catch((err) => {
69
+ sbz_logs.push({
70
+ type: 'fetchfailed2',
71
+ url: args[0],
72
+ error: err + '',
73
+ })
74
+ throw err // re-throw if needed
75
+ })
42
76
  }
43
77
 
44
- xhr.send(JSON.stringify(info))
45
- }, 15000)
78
+ setTimeout(() => {
79
+ var $button = document.querySelector('div.chat-button')
80
+
81
+ let status = ''
82
+ if ($button) {
83
+ var rect = $button.getBoundingClientRect()
84
+ var visible = rect.width > 0 && rect.height > 0
85
+ if (visible) {
86
+ // the chatbox is visible
87
+ return
88
+ } else {
89
+ // the chatbox is not visible
90
+ status = 'hidden'
91
+ }
92
+ } else {
93
+ // the chatbox is not found in DOM
94
+ status = 'not-found'
95
+ }
96
+
97
+ // Collect device and browser info
98
+ var info = {
99
+ status: status,
100
+ href: window.location.href,
101
+ userAgent: navigator.userAgent,
102
+ platform: navigator.platform,
103
+ language: navigator.language,
104
+ timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
105
+ logs: sbz_logs,
106
+ info: sbz_info,
107
+ }
108
+
109
+ // Send via XHR
110
+ var xhr = new XMLHttpRequest()
111
+ xhr.open('POST', 'https://api.subiz.com.vn/4.1/chatbox-failed', true)
112
+ xhr.withCredentials = false
113
+ xhr.setRequestHeader('Content-Type', 'text/plain')
114
+
115
+ xhr.onreadystatechange = function () {
116
+ if (xhr.readyState === XMLHttpRequest.DONE) {
117
+ console.log('Server responded with:', xhr.status, xhr.responseText)
118
+ }
119
+ }
120
+
121
+ xhr.send(JSON.stringify(info))
122
+ }, 15000)
123
+ }