@hellotext/hellotext 2.1.4 → 2.1.5

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.
@@ -326,11 +326,7 @@ let _default = /*#__PURE__*/function (_Controller) {
326
326
  element.querySelector('[data-attachment-container]').appendChild(image);
327
327
  });
328
328
  }
329
-
330
- // Clear any typing indicator when message is received
331
- if (this.typingIndicatorVisible) {
332
- this.clearTypingIndicator();
333
- }
329
+ this.clearTypingIndicator();
334
330
  this.messagesContainerTarget.appendChild(element);
335
331
  _hellotext.default.eventEmitter.dispatch('webchat:message:received', {
336
332
  ...message,
@@ -422,8 +418,19 @@ let _default = /*#__PURE__*/function (_Controller) {
422
418
  this.attachmentContainerTarget.style.display = 'none';
423
419
  this.errorMessageContainerTarget.style.display = 'none';
424
420
  this.inputTarget.focus();
421
+
422
+ // Set up optimistic typing indicator BEFORE making the API call
423
+ // This prevents race conditions with server responses
424
+ if (!this.typingIndicatorVisible) {
425
+ clearTimeout(this.optimisticTypingTimeout);
426
+ this.optimisticTypingTimeout = setTimeout(() => {
427
+ this.showOptimisticTypingIndicator();
428
+ }, this.optimisticTypingIndicatorWaitValue);
429
+ }
425
430
  const response = await this.messagesAPI.create(formData);
426
431
  if (response.failed) {
432
+ // Clear the optimistic typing indicator on failure
433
+ clearTimeout(this.optimisticTypingTimeout);
427
434
  this.broadcastChannel.postMessage({
428
435
  type: 'message:failed',
429
436
  id: element.id
@@ -438,13 +445,10 @@ let _default = /*#__PURE__*/function (_Controller) {
438
445
  this.conversationIdValue = data.conversation;
439
446
  this.webChatChannel.updateSubscriptionWith(this.conversationIdValue);
440
447
  }
448
+
449
+ // If there's already a typing indicator visible, reset its timer
441
450
  if (this.typingIndicatorVisible) {
442
451
  this.resetTypingIndicatorTimer();
443
- } else {
444
- clearTimeout(this.optimisticTypingTimeout);
445
- this.optimisticTypingTimeout = setTimeout(() => {
446
- this.showOptimisticTypingIndicator();
447
- }, this.optimisticTypingIndicatorWaitValue);
448
452
  }
449
453
  this.attachmentContainerTarget.style.display = '';
450
454
  }
@@ -330,11 +330,7 @@ var _default = /*#__PURE__*/function (_Controller) {
330
330
  element.querySelector('[data-attachment-container]').appendChild(image);
331
331
  });
332
332
  }
333
-
334
- // Clear any typing indicator when message is received
335
- if (this.typingIndicatorVisible) {
336
- this.clearTypingIndicator();
337
- }
333
+ this.clearTypingIndicator();
338
334
  this.messagesContainerTarget.appendChild(element);
339
335
  Hellotext.eventEmitter.dispatch('webchat:message:received', _objectSpread(_objectSpread({}, message), {}, {
340
336
  body: element.querySelector('[data-body]').innerText
@@ -426,8 +422,19 @@ var _default = /*#__PURE__*/function (_Controller) {
426
422
  this.attachmentContainerTarget.style.display = 'none';
427
423
  this.errorMessageContainerTarget.style.display = 'none';
428
424
  this.inputTarget.focus();
425
+
426
+ // Set up optimistic typing indicator BEFORE making the API call
427
+ // This prevents race conditions with server responses
428
+ if (!this.typingIndicatorVisible) {
429
+ clearTimeout(this.optimisticTypingTimeout);
430
+ this.optimisticTypingTimeout = setTimeout(() => {
431
+ this.showOptimisticTypingIndicator();
432
+ }, this.optimisticTypingIndicatorWaitValue);
433
+ }
429
434
  var response = yield this.messagesAPI.create(formData);
430
435
  if (response.failed) {
436
+ // Clear the optimistic typing indicator on failure
437
+ clearTimeout(this.optimisticTypingTimeout);
431
438
  this.broadcastChannel.postMessage({
432
439
  type: 'message:failed',
433
440
  id: element.id
@@ -442,13 +449,10 @@ var _default = /*#__PURE__*/function (_Controller) {
442
449
  this.conversationIdValue = data.conversation;
443
450
  this.webChatChannel.updateSubscriptionWith(this.conversationIdValue);
444
451
  }
452
+
453
+ // If there's already a typing indicator visible, reset its timer
445
454
  if (this.typingIndicatorVisible) {
446
455
  this.resetTypingIndicatorTimer();
447
- } else {
448
- clearTimeout(this.optimisticTypingTimeout);
449
- this.optimisticTypingTimeout = setTimeout(() => {
450
- this.showOptimisticTypingIndicator();
451
- }, this.optimisticTypingIndicatorWaitValue);
452
456
  }
453
457
  this.attachmentContainerTarget.style.display = '';
454
458
  });
package/lib/hellotext.cjs CHANGED
@@ -35,6 +35,7 @@ let Hellotext = /*#__PURE__*/function () {
35
35
  async function initialize(business, config) {
36
36
  _core.Configuration.assign(config);
37
37
  _models.Session.initialize();
38
+ this.page = new _models.Page();
38
39
  this.business = new _models.Business(business);
39
40
  this.forms = new _models.FormCollection();
40
41
  _classPrivateFieldLooseBase(this, _query)[_query] = new _models.Query();
@@ -63,11 +64,12 @@ let Hellotext = /*#__PURE__*/function () {
63
64
  ...(params && params.headers || {}),
64
65
  ...this.headers
65
66
  };
67
+ const pageInstance = params && params.url ? new _models.Page(params.url) : this.page;
66
68
  const body = {
67
69
  session: this.session,
68
70
  action,
69
71
  ...params,
70
- url: params && params.url || window.location.href
72
+ ...pageInstance.trackingData
71
73
  };
72
74
  delete body.headers;
73
75
  return await _api.default.events.create({
package/lib/hellotext.js CHANGED
@@ -13,7 +13,7 @@ var id = 0;
13
13
  function _classPrivateFieldLooseKey(name) { return "__private_" + id++ + "_" + name; }
14
14
  import { Configuration, Event } from './core';
15
15
  import API, { Response } from './api';
16
- import { Business, FormCollection, Query, Session, Webchat } from './models';
16
+ import { Business, FormCollection, Page, Query, Session, Webchat } from './models';
17
17
  import { NotInitializedError } from './errors';
18
18
  var _session = /*#__PURE__*/_classPrivateFieldLooseKey("session");
19
19
  var _query = /*#__PURE__*/_classPrivateFieldLooseKey("query");
@@ -33,6 +33,7 @@ var Hellotext = /*#__PURE__*/function () {
33
33
  var _initialize = _asyncToGenerator(function* (business, config) {
34
34
  Configuration.assign(config);
35
35
  Session.initialize();
36
+ this.page = new Page();
36
37
  this.business = new Business(business);
37
38
  this.forms = new FormCollection();
38
39
  _classPrivateFieldLooseBase(this, _query)[_query] = new Query();
@@ -64,12 +65,11 @@ var Hellotext = /*#__PURE__*/function () {
64
65
  throw new NotInitializedError();
65
66
  }
66
67
  var headers = _objectSpread(_objectSpread({}, params && params.headers || {}), this.headers);
68
+ var pageInstance = params && params.url ? new Page(params.url) : this.page;
67
69
  var body = _objectSpread(_objectSpread({
68
70
  session: this.session,
69
71
  action
70
- }, params), {}, {
71
- url: params && params.url || window.location.href
72
- });
72
+ }, params), pageInstance.trackingData);
73
73
  delete body.headers;
74
74
  return yield API.events.create({
75
75
  headers,
@@ -27,6 +27,12 @@ Object.defineProperty(exports, "FormCollection", {
27
27
  return _form_collection.FormCollection;
28
28
  }
29
29
  });
30
+ Object.defineProperty(exports, "Page", {
31
+ enumerable: true,
32
+ get: function () {
33
+ return _page.Page;
34
+ }
35
+ });
30
36
  Object.defineProperty(exports, "Query", {
31
37
  enumerable: true,
32
38
  get: function () {
@@ -39,6 +45,12 @@ Object.defineProperty(exports, "Session", {
39
45
  return _session.Session;
40
46
  }
41
47
  });
48
+ Object.defineProperty(exports, "UTM", {
49
+ enumerable: true,
50
+ get: function () {
51
+ return _utm.UTM;
52
+ }
53
+ });
42
54
  Object.defineProperty(exports, "Webchat", {
43
55
  enumerable: true,
44
56
  get: function () {
@@ -49,6 +61,8 @@ var _business = require("./business");
49
61
  var _cookies = require("./cookies");
50
62
  var _form = require("./form");
51
63
  var _form_collection = require("./form_collection");
64
+ var _page = require("./page");
52
65
  var _query = require("./query");
53
66
  var _session = require("./session");
67
+ var _utm = require("./utm");
54
68
  var _webchat = require("./webchat");
@@ -2,6 +2,8 @@ export { Business } from './business';
2
2
  export { Cookies } from './cookies';
3
3
  export { Form } from './form';
4
4
  export { FormCollection } from './form_collection';
5
+ export { Page } from './page';
5
6
  export { Query } from './query';
6
7
  export { Session } from './session';
8
+ export { UTM } from './utm';
7
9
  export { Webchat } from './webchat';
@@ -0,0 +1,86 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.Page = void 0;
7
+ var _utm = require("./utm");
8
+ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
9
+ function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } }
10
+ function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
11
+ function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); }
12
+ function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
13
+ let Page = /*#__PURE__*/function () {
14
+ function Page(url = null) {
15
+ _classCallCheck(this, Page);
16
+ this.utm = new _utm.UTM();
17
+ this._url = url;
18
+ }
19
+
20
+ /**
21
+ * Get the current page URL
22
+ * @returns {string} The page URL
23
+ */
24
+ _createClass(Page, [{
25
+ key: "url",
26
+ get: function () {
27
+ return this._url || window.location.href;
28
+ }
29
+
30
+ /**
31
+ * Get the current page title
32
+ * @returns {string} The page title
33
+ */
34
+ }, {
35
+ key: "title",
36
+ get: function () {
37
+ return document.title;
38
+ }
39
+
40
+ /**
41
+ * Get the current page path
42
+ * @returns {string} The page path
43
+ */
44
+ }, {
45
+ key: "path",
46
+ get: function () {
47
+ if (this._url) {
48
+ try {
49
+ return new URL(this._url).pathname;
50
+ } catch (e) {
51
+ return '/';
52
+ }
53
+ }
54
+ return window.location.pathname;
55
+ }
56
+
57
+ /**
58
+ * Get current UTM parameters
59
+ * @returns {Object} The UTM parameters
60
+ */
61
+ }, {
62
+ key: "utmParams",
63
+ get: function () {
64
+ return this.utm.current;
65
+ }
66
+
67
+ /**
68
+ * Get page data for tracking
69
+ * @returns {Object} Object containing page object and utm_params
70
+ */
71
+ }, {
72
+ key: "trackingData",
73
+ get: function () {
74
+ return {
75
+ page: {
76
+ url: this.url,
77
+ title: this.title,
78
+ path: this.path
79
+ },
80
+ utm_params: this.utmParams
81
+ };
82
+ }
83
+ }]);
84
+ return Page;
85
+ }();
86
+ exports.Page = Page;
@@ -0,0 +1,81 @@
1
+ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
2
+ function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } }
3
+ function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
4
+ function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); }
5
+ function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
6
+ import { UTM } from './utm';
7
+ var Page = /*#__PURE__*/function () {
8
+ function Page() {
9
+ var url = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
10
+ _classCallCheck(this, Page);
11
+ this.utm = new UTM();
12
+ this._url = url;
13
+ }
14
+
15
+ /**
16
+ * Get the current page URL
17
+ * @returns {string} The page URL
18
+ */
19
+ _createClass(Page, [{
20
+ key: "url",
21
+ get: function get() {
22
+ return this._url || window.location.href;
23
+ }
24
+
25
+ /**
26
+ * Get the current page title
27
+ * @returns {string} The page title
28
+ */
29
+ }, {
30
+ key: "title",
31
+ get: function get() {
32
+ return document.title;
33
+ }
34
+
35
+ /**
36
+ * Get the current page path
37
+ * @returns {string} The page path
38
+ */
39
+ }, {
40
+ key: "path",
41
+ get: function get() {
42
+ if (this._url) {
43
+ try {
44
+ return new URL(this._url).pathname;
45
+ } catch (e) {
46
+ return '/';
47
+ }
48
+ }
49
+ return window.location.pathname;
50
+ }
51
+
52
+ /**
53
+ * Get current UTM parameters
54
+ * @returns {Object} The UTM parameters
55
+ */
56
+ }, {
57
+ key: "utmParams",
58
+ get: function get() {
59
+ return this.utm.current;
60
+ }
61
+
62
+ /**
63
+ * Get page data for tracking
64
+ * @returns {Object} Object containing page object and utm_params
65
+ */
66
+ }, {
67
+ key: "trackingData",
68
+ get: function get() {
69
+ return {
70
+ page: {
71
+ url: this.url,
72
+ title: this.title,
73
+ path: this.path
74
+ },
75
+ utm_params: this.utmParams
76
+ };
77
+ }
78
+ }]);
79
+ return Page;
80
+ }();
81
+ export { Page };
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.UTM = void 0;
7
+ var _cookies = require("./cookies");
8
+ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
9
+ function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } }
10
+ function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
11
+ function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); }
12
+ function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
13
+ let UTM = /*#__PURE__*/function () {
14
+ function UTM() {
15
+ _classCallCheck(this, UTM);
16
+ const urlSearchParams = new URLSearchParams(window.location.search);
17
+ const utmsFromUrl = {
18
+ source: urlSearchParams.get('utm_source'),
19
+ medium: urlSearchParams.get('utm_medium'),
20
+ campaign: urlSearchParams.get('utm_campaign'),
21
+ term: urlSearchParams.get('utm_term'),
22
+ content: urlSearchParams.get('utm_content')
23
+ };
24
+ if (utmsFromUrl.source && utmsFromUrl.medium) {
25
+ const cleanUtms = Object.fromEntries(Object.entries(utmsFromUrl).filter(([_, value]) => value));
26
+ _cookies.Cookies.set('hello_utm', JSON.stringify(cleanUtms));
27
+ }
28
+ }
29
+ _createClass(UTM, [{
30
+ key: "current",
31
+ get: function () {
32
+ try {
33
+ return JSON.parse(_cookies.Cookies.get('hello_utm')) || {};
34
+ } catch (e) {
35
+ return {};
36
+ }
37
+ }
38
+ }]);
39
+ return UTM;
40
+ }();
41
+ exports.UTM = UTM;
@@ -0,0 +1,38 @@
1
+ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
2
+ function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } }
3
+ function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
4
+ function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); }
5
+ function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
6
+ import { Cookies } from './cookies';
7
+ var UTM = /*#__PURE__*/function () {
8
+ function UTM() {
9
+ _classCallCheck(this, UTM);
10
+ var urlSearchParams = new URLSearchParams(window.location.search);
11
+ var utmsFromUrl = {
12
+ source: urlSearchParams.get('utm_source'),
13
+ medium: urlSearchParams.get('utm_medium'),
14
+ campaign: urlSearchParams.get('utm_campaign'),
15
+ term: urlSearchParams.get('utm_term'),
16
+ content: urlSearchParams.get('utm_content')
17
+ };
18
+ if (utmsFromUrl.source && utmsFromUrl.medium) {
19
+ var cleanUtms = Object.fromEntries(Object.entries(utmsFromUrl).filter(_ref => {
20
+ var [_, value] = _ref;
21
+ return value;
22
+ }));
23
+ Cookies.set('hello_utm', JSON.stringify(cleanUtms));
24
+ }
25
+ }
26
+ _createClass(UTM, [{
27
+ key: "current",
28
+ get: function get() {
29
+ try {
30
+ return JSON.parse(Cookies.get('hello_utm')) || {};
31
+ } catch (e) {
32
+ return {};
33
+ }
34
+ }
35
+ }]);
36
+ return UTM;
37
+ }();
38
+ export { UTM };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hellotext/hellotext",
3
- "version": "2.1.4",
3
+ "version": "2.1.5",
4
4
  "description": "Hellotext JavaScript Client",
5
5
  "source": "src/index.js",
6
6
  "main": "lib/index.cjs",
@@ -386,11 +386,7 @@ export default class extends Controller {
386
386
  })
387
387
  }
388
388
 
389
- // Clear any typing indicator when message is received
390
- if (this.typingIndicatorVisible) {
391
- this.clearTypingIndicator()
392
- }
393
-
389
+ this.clearTypingIndicator()
394
390
  this.messagesContainerTarget.appendChild(element)
395
391
 
396
392
  Hellotext.eventEmitter.dispatch('webchat:message:received', {
@@ -502,9 +498,21 @@ export default class extends Controller {
502
498
 
503
499
  this.inputTarget.focus()
504
500
 
501
+ // Set up optimistic typing indicator BEFORE making the API call
502
+ // This prevents race conditions with server responses
503
+ if (!this.typingIndicatorVisible) {
504
+ clearTimeout(this.optimisticTypingTimeout)
505
+ this.optimisticTypingTimeout = setTimeout(() => {
506
+ this.showOptimisticTypingIndicator()
507
+ }, this.optimisticTypingIndicatorWaitValue)
508
+ }
509
+
505
510
  const response = await this.messagesAPI.create(formData)
506
511
 
507
512
  if (response.failed) {
513
+ // Clear the optimistic typing indicator on failure
514
+ clearTimeout(this.optimisticTypingTimeout)
515
+
508
516
  this.broadcastChannel.postMessage({
509
517
  type: 'message:failed',
510
518
  id: element.id,
@@ -524,13 +532,9 @@ export default class extends Controller {
524
532
  this.webChatChannel.updateSubscriptionWith(this.conversationIdValue)
525
533
  }
526
534
 
535
+ // If there's already a typing indicator visible, reset its timer
527
536
  if (this.typingIndicatorVisible) {
528
537
  this.resetTypingIndicatorTimer()
529
- } else {
530
- clearTimeout(this.optimisticTypingTimeout)
531
- this.optimisticTypingTimeout = setTimeout(() => {
532
- this.showOptimisticTypingIndicator()
533
- }, this.optimisticTypingIndicatorWaitValue)
534
538
  }
535
539
 
536
540
  this.attachmentContainerTarget.style.display = ''
package/src/hellotext.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { Configuration, Event } from './core'
2
2
 
3
3
  import API, { Response } from './api'
4
- import { Business, FormCollection, Query, Session, Webchat } from './models'
4
+ import { Business, FormCollection, Page, Query, Session, Webchat } from './models'
5
5
 
6
6
  import { NotInitializedError } from './errors'
7
7
 
@@ -23,6 +23,7 @@ class Hellotext {
23
23
  Configuration.assign(config)
24
24
  Session.initialize()
25
25
 
26
+ this.page = new Page()
26
27
  this.business = new Business(business)
27
28
  this.forms = new FormCollection()
28
29
 
@@ -54,11 +55,13 @@ class Hellotext {
54
55
  ...this.headers,
55
56
  }
56
57
 
58
+ const pageInstance = params && params.url ? new Page(params.url) : this.page
59
+
57
60
  const body = {
58
61
  session: this.session,
59
62
  action,
60
63
  ...params,
61
- url: (params && params.url) || window.location.href,
64
+ ...pageInstance.trackingData,
62
65
  }
63
66
 
64
67
  delete body.headers
@@ -2,7 +2,7 @@ import Hellotext from '../hellotext'
2
2
 
3
3
  class Cookies {
4
4
  static set(name, value) {
5
- if(typeof document !== 'undefined') {
5
+ if (typeof document !== 'undefined') {
6
6
  document.cookie = `${name}=${value}; path=/;`
7
7
  }
8
8
 
@@ -12,7 +12,7 @@ class Cookies {
12
12
  }
13
13
 
14
14
  static get(name) {
15
- if(typeof document !== 'undefined') {
15
+ if (typeof document !== 'undefined') {
16
16
  return document.cookie.match('(^|;)\\s*' + name + '\\s*=\\s*([^;]+)')?.pop()
17
17
  } else {
18
18
  return undefined
@@ -2,6 +2,8 @@ export { Business } from './business'
2
2
  export { Cookies } from './cookies'
3
3
  export { Form } from './form'
4
4
  export { FormCollection } from './form_collection'
5
+ export { Page } from './page'
5
6
  export { Query } from './query'
6
7
  export { Session } from './session'
8
+ export { UTM } from './utm'
7
9
  export { Webchat } from './webchat'
@@ -0,0 +1,65 @@
1
+ import { UTM } from './utm'
2
+
3
+ class Page {
4
+ constructor(url = null) {
5
+ this.utm = new UTM()
6
+ this._url = url
7
+ }
8
+
9
+ /**
10
+ * Get the current page URL
11
+ * @returns {string} The page URL
12
+ */
13
+ get url() {
14
+ return this._url || window.location.href
15
+ }
16
+
17
+ /**
18
+ * Get the current page title
19
+ * @returns {string} The page title
20
+ */
21
+ get title() {
22
+ return document.title
23
+ }
24
+
25
+ /**
26
+ * Get the current page path
27
+ * @returns {string} The page path
28
+ */
29
+ get path() {
30
+ if (this._url) {
31
+ try {
32
+ return new URL(this._url).pathname
33
+ } catch (e) {
34
+ return '/'
35
+ }
36
+ }
37
+
38
+ return window.location.pathname
39
+ }
40
+
41
+ /**
42
+ * Get current UTM parameters
43
+ * @returns {Object} The UTM parameters
44
+ */
45
+ get utmParams() {
46
+ return this.utm.current
47
+ }
48
+
49
+ /**
50
+ * Get page data for tracking
51
+ * @returns {Object} Object containing page object and utm_params
52
+ */
53
+ get trackingData() {
54
+ return {
55
+ page: {
56
+ url: this.url,
57
+ title: this.title,
58
+ path: this.path,
59
+ },
60
+ utm_params: this.utmParams,
61
+ }
62
+ }
63
+ }
64
+
65
+ export { Page }
@@ -0,0 +1,33 @@
1
+ import { Cookies } from './cookies'
2
+
3
+ class UTM {
4
+ constructor() {
5
+ const urlSearchParams = new URLSearchParams(window.location.search)
6
+
7
+ const utmsFromUrl = {
8
+ source: urlSearchParams.get('utm_source'),
9
+ medium: urlSearchParams.get('utm_medium'),
10
+ campaign: urlSearchParams.get('utm_campaign'),
11
+ term: urlSearchParams.get('utm_term'),
12
+ content: urlSearchParams.get('utm_content'),
13
+ }
14
+
15
+ if (utmsFromUrl.source && utmsFromUrl.medium) {
16
+ const cleanUtms = Object.fromEntries(
17
+ Object.entries(utmsFromUrl).filter(([_, value]) => value),
18
+ )
19
+
20
+ Cookies.set('hello_utm', JSON.stringify(cleanUtms))
21
+ }
22
+ }
23
+
24
+ get current() {
25
+ try {
26
+ return JSON.parse(Cookies.get('hello_utm')) || {}
27
+ } catch (e) {
28
+ return {}
29
+ }
30
+ }
31
+ }
32
+
33
+ export { UTM }