clarity-js 0.8.42 → 0.8.44

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 (116) hide show
  1. package/README.md +26 -26
  2. package/build/clarity.extended.js +1 -1
  3. package/build/clarity.insight.js +1 -1
  4. package/build/clarity.js +6101 -6071
  5. package/build/clarity.min.js +1 -1
  6. package/build/clarity.module.js +6101 -6071
  7. package/build/clarity.performance.js +1 -1
  8. package/package.json +70 -70
  9. package/rollup.config.ts +161 -161
  10. package/src/clarity.ts +65 -65
  11. package/src/core/api.ts +8 -8
  12. package/src/core/config.ts +29 -29
  13. package/src/core/copy.ts +3 -3
  14. package/src/core/dynamic.ts +13 -7
  15. package/src/core/event.ts +53 -53
  16. package/src/core/hash.ts +19 -19
  17. package/src/core/history.ts +71 -71
  18. package/src/core/index.ts +81 -81
  19. package/src/core/measure.ts +19 -19
  20. package/src/core/report.ts +28 -28
  21. package/src/core/scrub.ts +204 -202
  22. package/src/core/task.ts +181 -181
  23. package/src/core/throttle.ts +46 -46
  24. package/src/core/time.ts +26 -26
  25. package/src/core/timeout.ts +10 -10
  26. package/src/core/version.ts +2 -2
  27. package/src/data/baseline.ts +162 -162
  28. package/src/data/compress.ts +31 -31
  29. package/src/data/consent.ts +77 -77
  30. package/src/data/cookie.ts +90 -0
  31. package/src/data/custom.ts +23 -23
  32. package/src/data/dimension.ts +53 -53
  33. package/src/data/encode.ts +155 -155
  34. package/src/data/envelope.ts +53 -53
  35. package/src/data/extract.ts +211 -211
  36. package/src/data/index.ts +51 -50
  37. package/src/data/limit.ts +44 -44
  38. package/src/data/metadata.ts +321 -408
  39. package/src/data/metric.ts +51 -51
  40. package/src/data/ping.ts +36 -36
  41. package/src/data/signal.ts +30 -30
  42. package/src/data/summary.ts +34 -34
  43. package/src/data/token.ts +39 -39
  44. package/src/data/upgrade.ts +44 -44
  45. package/src/data/upload.ts +333 -333
  46. package/src/data/util.ts +18 -0
  47. package/src/data/variable.ts +83 -83
  48. package/src/diagnostic/encode.ts +40 -40
  49. package/src/diagnostic/fraud.ts +36 -36
  50. package/src/diagnostic/index.ts +13 -13
  51. package/src/diagnostic/internal.ts +28 -28
  52. package/src/diagnostic/script.ts +35 -35
  53. package/src/dynamic/agent/blank.ts +2 -2
  54. package/src/dynamic/agent/crisp.ts +40 -40
  55. package/src/dynamic/agent/encode.ts +25 -25
  56. package/src/dynamic/agent/index.ts +8 -8
  57. package/src/dynamic/agent/livechat.ts +58 -58
  58. package/src/dynamic/agent/tidio.ts +44 -44
  59. package/src/global.ts +6 -6
  60. package/src/index.ts +9 -9
  61. package/src/insight/blank.ts +14 -14
  62. package/src/insight/encode.ts +60 -60
  63. package/src/insight/snapshot.ts +114 -114
  64. package/src/interaction/change.ts +38 -38
  65. package/src/interaction/click.ts +173 -173
  66. package/src/interaction/clipboard.ts +32 -32
  67. package/src/interaction/encode.ts +210 -210
  68. package/src/interaction/index.ts +60 -60
  69. package/src/interaction/input.ts +57 -57
  70. package/src/interaction/pointer.ts +137 -137
  71. package/src/interaction/resize.ts +50 -50
  72. package/src/interaction/scroll.ts +129 -129
  73. package/src/interaction/selection.ts +66 -66
  74. package/src/interaction/submit.ts +30 -30
  75. package/src/interaction/timeline.ts +69 -69
  76. package/src/interaction/unload.ts +26 -26
  77. package/src/interaction/visibility.ts +27 -27
  78. package/src/layout/animation.ts +133 -133
  79. package/src/layout/custom.ts +42 -42
  80. package/src/layout/discover.ts +31 -31
  81. package/src/layout/document.ts +46 -46
  82. package/src/layout/dom.ts +439 -439
  83. package/src/layout/encode.ts +154 -154
  84. package/src/layout/index.ts +42 -42
  85. package/src/layout/mutation.ts +411 -411
  86. package/src/layout/node.ts +294 -294
  87. package/src/layout/offset.ts +19 -19
  88. package/src/layout/region.ts +151 -151
  89. package/src/layout/schema.ts +63 -63
  90. package/src/layout/selector.ts +82 -82
  91. package/src/layout/style.ts +159 -159
  92. package/src/layout/target.ts +32 -32
  93. package/src/layout/traverse.ts +27 -27
  94. package/src/performance/blank.ts +9 -9
  95. package/src/performance/encode.ts +31 -31
  96. package/src/performance/index.ts +12 -12
  97. package/src/performance/interaction.ts +125 -125
  98. package/src/performance/navigation.ts +31 -31
  99. package/src/performance/observer.ts +112 -112
  100. package/src/queue.ts +33 -33
  101. package/test/core.test.ts +139 -139
  102. package/test/helper.ts +162 -162
  103. package/test/html/core.html +27 -27
  104. package/test/stub.test.ts +7 -7
  105. package/test/tsconfig.test.json +5 -5
  106. package/tsconfig.json +21 -21
  107. package/tslint.json +32 -32
  108. package/types/agent.d.ts +39 -39
  109. package/types/core.d.ts +150 -150
  110. package/types/data.d.ts +572 -571
  111. package/types/diagnostic.d.ts +24 -24
  112. package/types/global.d.ts +30 -30
  113. package/types/index.d.ts +40 -40
  114. package/types/interaction.d.ts +177 -177
  115. package/types/layout.d.ts +276 -276
  116. package/types/performance.d.ts +31 -31
package/src/clarity.ts CHANGED
@@ -1,66 +1,66 @@
1
- import { Config, Module } from "@clarity-types/core";
2
- import { Constant } from "@clarity-types/data";
3
- import * as queue from "@src/queue";
4
- import * as core from "@src/core";
5
- import measure from "@src/core/measure";
6
- import * as task from "@src/core/task";
7
- import version from "@src/core/version";
8
- import * as data from "@src/data";
9
- import * as diagnostic from "@src/diagnostic";
10
- import * as interaction from "@src/interaction";
11
- import * as layout from "@src/layout";
12
- import * as performance from "@src/performance";
13
- import * as dynamic from "@src/core/dynamic";
14
- export { version };
15
- export { consent, consentv2, event, identify, set, upgrade, metadata, signal, maxMetric, dlog } from "@src/data";
16
- export { queue } from "@src/data/upload";
17
- export { register } from "@src/core/dynamic";
18
- export { schedule } from "@src/core/task";
19
- export { time } from "@src/core/time";
20
- export { hashText } from "@src/layout";
21
- export { measure };
22
- const modules: Module[] = [diagnostic, layout, interaction, performance, dynamic];
23
-
24
- export function start(config: Config = null): void {
25
- // Check that browser supports required APIs and we do not attempt to start Clarity multiple times
26
- if (core.check()) {
27
- core.config(config);
28
- core.start();
29
- data.start();
30
- modules.forEach(x => measure(x.start)());
31
-
32
- // If it's an internal call to start, without explicit configuration,
33
- // re-process any newly accumulated items in the queue
34
- if (config === null) { queue.process(); }
35
- }
36
- }
37
-
38
- // By default Clarity is asynchronous and will yield by looking for requestIdleCallback.
39
- // However, there can still be situations with single page apps where a user action can result
40
- // in the whole DOM being destroyed and reconstructed. While Clarity will perform favorably out of the box,
41
- // we do allow external clients to manually pause Clarity for that short burst of time and minimize
42
- // performance impact even further. For reference, we are talking single digit milliseconds optimization here, not seconds.
43
- export function pause(): void {
44
- if (core.active()) {
45
- data.event(Constant.Clarity, Constant.Pause);
46
- task.pause();
47
- }
48
- }
49
-
50
- // This is how external clients can get out of pause state, and resume Clarity to continue monitoring the page
51
- export function resume(): void {
52
- if (core.active()) {
53
- task.resume();
54
- data.event(Constant.Clarity, Constant.Resume);
55
- }
56
- }
57
-
58
- export function stop(): void {
59
- if (core.active()) {
60
- // Stop modules in the reverse order of their initialization and start queuing up items again
61
- modules.slice().reverse().forEach(x => measure(x.stop)());
62
- data.stop();
63
- core.stop();
64
- queue.setup();
65
- }
1
+ import { Config, Module } from "@clarity-types/core";
2
+ import { Constant } from "@clarity-types/data";
3
+ import * as queue from "@src/queue";
4
+ import * as core from "@src/core";
5
+ import measure from "@src/core/measure";
6
+ import * as task from "@src/core/task";
7
+ import version from "@src/core/version";
8
+ import * as data from "@src/data";
9
+ import * as diagnostic from "@src/diagnostic";
10
+ import * as interaction from "@src/interaction";
11
+ import * as layout from "@src/layout";
12
+ import * as performance from "@src/performance";
13
+ import * as dynamic from "@src/core/dynamic";
14
+ export { version };
15
+ export { consent, consentv2, event, identify, set, upgrade, metadata, signal, maxMetric, dlog } from "@src/data";
16
+ export { queue } from "@src/data/upload";
17
+ export { register } from "@src/core/dynamic";
18
+ export { schedule } from "@src/core/task";
19
+ export { time } from "@src/core/time";
20
+ export { hashText } from "@src/layout";
21
+ export { measure };
22
+ const modules: Module[] = [diagnostic, layout, interaction, performance, dynamic];
23
+
24
+ export function start(config: Config = null): void {
25
+ // Check that browser supports required APIs and we do not attempt to start Clarity multiple times
26
+ if (core.check()) {
27
+ core.config(config);
28
+ core.start();
29
+ data.start();
30
+ modules.forEach(x => measure(x.start)());
31
+
32
+ // If it's an internal call to start, without explicit configuration,
33
+ // re-process any newly accumulated items in the queue
34
+ if (config === null) { queue.process(); }
35
+ }
36
+ }
37
+
38
+ // By default Clarity is asynchronous and will yield by looking for requestIdleCallback.
39
+ // However, there can still be situations with single page apps where a user action can result
40
+ // in the whole DOM being destroyed and reconstructed. While Clarity will perform favorably out of the box,
41
+ // we do allow external clients to manually pause Clarity for that short burst of time and minimize
42
+ // performance impact even further. For reference, we are talking single digit milliseconds optimization here, not seconds.
43
+ export function pause(): void {
44
+ if (core.active()) {
45
+ data.event(Constant.Clarity, Constant.Pause);
46
+ task.pause();
47
+ }
48
+ }
49
+
50
+ // This is how external clients can get out of pause state, and resume Clarity to continue monitoring the page
51
+ export function resume(): void {
52
+ if (core.active()) {
53
+ task.resume();
54
+ data.event(Constant.Clarity, Constant.Resume);
55
+ }
56
+ }
57
+
58
+ export function stop(): void {
59
+ if (core.active()) {
60
+ // Stop modules in the reverse order of their initialization and start queuing up items again
61
+ modules.slice().reverse().forEach(x => measure(x.stop)());
62
+ data.stop();
63
+ core.stop();
64
+ queue.setup();
65
+ }
66
66
  }
package/src/core/api.ts CHANGED
@@ -1,8 +1,8 @@
1
- import { Constant } from "@clarity-types/core";
2
-
3
- export default function api(method: string): string {
4
- // Zone.js, a popular package for Angular, overrides native browser APIs which can lead to inconsistent state for single page applications.
5
- // Example issue: https://github.com/angular/angular/issues/31712
6
- // As a work around, we ensuring Clarity access APIs outside of Zone (and use native implementation instead)
7
- return window[Constant.Zone] && Constant.Symbol in window[Constant.Zone] ? window[Constant.Zone][Constant.Symbol](method) : method;
8
- }
1
+ import { Constant } from "@clarity-types/core";
2
+
3
+ export default function api(method: string): string {
4
+ // Zone.js, a popular package for Angular, overrides native browser APIs which can lead to inconsistent state for single page applications.
5
+ // Example issue: https://github.com/angular/angular/issues/31712
6
+ // As a work around, we ensuring Clarity access APIs outside of Zone (and use native implementation instead)
7
+ return window[Constant.Zone] && Constant.Symbol in window[Constant.Zone] ? window[Constant.Zone][Constant.Symbol](method) : method;
8
+ }
@@ -1,29 +1,29 @@
1
- import { Config, Time } from "@clarity-types/core";
2
-
3
- let config: Config = {
4
- projectId: null,
5
- delay: 1 * Time.Second,
6
- lean: false,
7
- lite: false,
8
- track: true,
9
- content: true,
10
- drop: [],
11
- mask: [],
12
- unmask: [],
13
- regions: [],
14
- cookies: [],
15
- fraud: true,
16
- checksum: [],
17
- report: null,
18
- upload: null,
19
- fallback: null,
20
- upgrade: null,
21
- action: null,
22
- dob: null,
23
- delayDom: false,
24
- throttleDom: true,
25
- conversions: false,
26
- includeSubdomains: true,
27
- };
28
-
29
- export default config;
1
+ import { Config, Time } from "@clarity-types/core";
2
+
3
+ let config: Config = {
4
+ projectId: null,
5
+ delay: 1 * Time.Second,
6
+ lean: false,
7
+ lite: false,
8
+ track: true,
9
+ content: true,
10
+ drop: [],
11
+ mask: [],
12
+ unmask: [],
13
+ regions: [],
14
+ cookies: [],
15
+ fraud: true,
16
+ checksum: [],
17
+ report: null,
18
+ upload: null,
19
+ fallback: null,
20
+ upgrade: null,
21
+ action: null,
22
+ dob: null,
23
+ delayDom: false,
24
+ throttleDom: true,
25
+ conversions: false,
26
+ includeSubdomains: true,
27
+ };
28
+
29
+ export default config;
package/src/core/copy.ts CHANGED
@@ -1,3 +1,3 @@
1
- export default function<T>(input: T): T {
2
- return JSON.parse(JSON.stringify(input));
3
- }
1
+ export default function<T>(input: T): T {
2
+ return JSON.parse(JSON.stringify(input));
3
+ }
@@ -1,5 +1,7 @@
1
1
  import { Constant } from "@clarity-types/layout";
2
+ import { Constant as DataConstant } from "@clarity-types/data";
2
3
  import * as baseline from "@src/data/baseline";
4
+ import { report } from "@src/core/report";
3
5
 
4
6
  let stopCallbacks: (() => void)[] = [];
5
7
  let active = false;
@@ -37,19 +39,23 @@ export function event(signal: string): void {
37
39
  return;
38
40
  }
39
41
 
40
- load(parts[0]);
41
-
42
- if (m) {
43
- modules.add(m);
44
- baseline.dynamic(modules);
45
- }
42
+ load(parts[0], m);
46
43
  }
47
44
 
48
- function load(url: string): void {
45
+ function load(url: string, mid: number | null): void {
49
46
  try {
50
47
  const script = document.createElement("script");
51
48
  script.src = url;
52
49
  script.async = true;
50
+ script.onload = () => {
51
+ if (mid) {
52
+ modules.add(mid);
53
+ baseline.dynamic(modules);
54
+ }
55
+ };
56
+ script.onerror = () => {
57
+ report(new Error(`${DataConstant.Module}: ${url}`));
58
+ };
53
59
  document.head.appendChild(script);
54
60
  } catch (error) {
55
61
  // Do nothing
package/src/core/event.ts CHANGED
@@ -1,53 +1,53 @@
1
- import { BrowserEvent, Constant } from "@clarity-types/core";
2
- import api from "./api";
3
- import measure from "./measure";
4
-
5
- let bindings: Map<EventTarget, BrowserEvent[]> = new Map();
6
-
7
- export function bind(target: EventTarget, event: string, listener: EventListener, capture: boolean = false, passive: boolean = true): void {
8
- listener = measure(listener) as EventListener;
9
- // Wrapping following lines inside try / catch to cover edge cases where we might try to access an inaccessible element.
10
- // E.g. Iframe may start off as same-origin but later turn into cross-origin, and the following lines will throw an exception.
11
- try {
12
- target[api(Constant.AddEventListener)](event, listener, { capture, passive });
13
- if (!has(target)) {
14
- bindings.set(target, []);
15
- }
16
-
17
- bindings.get(target).push({ event, listener, options: { capture, passive } });
18
- } catch {
19
- /* do nothing */
20
- }
21
- }
22
-
23
- export function reset(): void {
24
- // Walk through existing list of bindings and remove them all
25
- bindings.forEach((bindingsPerTarget: BrowserEvent[], target: EventTarget) => {
26
- resetByTarget(bindingsPerTarget, target);
27
- });
28
-
29
- bindings = new Map();
30
- }
31
-
32
- export function unbind(target: EventTarget) {
33
- if (!has(target)) {
34
- return;
35
- }
36
- resetByTarget(bindings.get(target), target);
37
- }
38
-
39
- export function has(target: EventTarget): boolean {
40
- return bindings.has(target);
41
- }
42
-
43
- function resetByTarget(bindingsPerTarget: BrowserEvent[], target: EventTarget): void {
44
- bindingsPerTarget.forEach((binding) => {
45
- // Wrapping inside try / catch to avoid situations where the element may be destroyed before we get a chance to unbind
46
- try {
47
- target[api(Constant.RemoveEventListener)](binding.event, binding.listener, { capture: binding.options.capture, passive: binding.options.passive });
48
- } catch {
49
- /* do nothing */
50
- }
51
- });
52
- bindings.delete(target);
53
- }
1
+ import { BrowserEvent, Constant } from "@clarity-types/core";
2
+ import api from "./api";
3
+ import measure from "./measure";
4
+
5
+ let bindings: Map<EventTarget, BrowserEvent[]> = new Map();
6
+
7
+ export function bind(target: EventTarget, event: string, listener: EventListener, capture: boolean = false, passive: boolean = true): void {
8
+ listener = measure(listener) as EventListener;
9
+ // Wrapping following lines inside try / catch to cover edge cases where we might try to access an inaccessible element.
10
+ // E.g. Iframe may start off as same-origin but later turn into cross-origin, and the following lines will throw an exception.
11
+ try {
12
+ target[api(Constant.AddEventListener)](event, listener, { capture, passive });
13
+ if (!has(target)) {
14
+ bindings.set(target, []);
15
+ }
16
+
17
+ bindings.get(target).push({ event, listener, options: { capture, passive } });
18
+ } catch {
19
+ /* do nothing */
20
+ }
21
+ }
22
+
23
+ export function reset(): void {
24
+ // Walk through existing list of bindings and remove them all
25
+ bindings.forEach((bindingsPerTarget: BrowserEvent[], target: EventTarget) => {
26
+ resetByTarget(bindingsPerTarget, target);
27
+ });
28
+
29
+ bindings = new Map();
30
+ }
31
+
32
+ export function unbind(target: EventTarget) {
33
+ if (!has(target)) {
34
+ return;
35
+ }
36
+ resetByTarget(bindings.get(target), target);
37
+ }
38
+
39
+ export function has(target: EventTarget): boolean {
40
+ return bindings.has(target);
41
+ }
42
+
43
+ function resetByTarget(bindingsPerTarget: BrowserEvent[], target: EventTarget): void {
44
+ bindingsPerTarget.forEach((binding) => {
45
+ // Wrapping inside try / catch to avoid situations where the element may be destroyed before we get a chance to unbind
46
+ try {
47
+ target[api(Constant.RemoveEventListener)](binding.event, binding.listener, { capture: binding.options.capture, passive: binding.options.passive });
48
+ } catch {
49
+ /* do nothing */
50
+ }
51
+ });
52
+ bindings.delete(target);
53
+ }
package/src/core/hash.ts CHANGED
@@ -1,19 +1,19 @@
1
- // tslint:disable: no-bitwise
2
- export default function(input: string, precision: number = null): string {
3
- // Code inspired from C# GetHashCode: https://github.com/Microsoft/referencesource/blob/master/mscorlib/system/string.cs
4
- let hash = 0;
5
- let hashOne = 5381;
6
- let hashTwo = hashOne;
7
- for (let i = 0; i < input.length; i += 2) {
8
- let charOne = input.charCodeAt(i);
9
- hashOne = ((hashOne << 5) + hashOne) ^ charOne;
10
- if (i + 1 < input.length) {
11
- let charTwo = input.charCodeAt(i + 1);
12
- hashTwo = ((hashTwo << 5) + hashTwo) ^ charTwo;
13
- }
14
- }
15
- // Replace the magic number from C# implementation (1566083941) with a smaller prime number (11579)
16
- // This ensures we don't hit integer overflow and prevent collisions
17
- hash = Math.abs(hashOne + (hashTwo * 11579));
18
- return (precision ? hash % Math.pow(2, precision) : hash).toString(36);
19
- }
1
+ // tslint:disable: no-bitwise
2
+ export default function(input: string, precision: number = null): string {
3
+ // Code inspired from C# GetHashCode: https://github.com/Microsoft/referencesource/blob/master/mscorlib/system/string.cs
4
+ let hash = 0;
5
+ let hashOne = 5381;
6
+ let hashTwo = hashOne;
7
+ for (let i = 0; i < input.length; i += 2) {
8
+ let charOne = input.charCodeAt(i);
9
+ hashOne = ((hashOne << 5) + hashOne) ^ charOne;
10
+ if (i + 1 < input.length) {
11
+ let charTwo = input.charCodeAt(i + 1);
12
+ hashTwo = ((hashTwo << 5) + hashTwo) ^ charTwo;
13
+ }
14
+ }
15
+ // Replace the magic number from C# implementation (1566083941) with a smaller prime number (11579)
16
+ // This ensures we don't hit integer overflow and prevent collisions
17
+ hash = Math.abs(hashOne + (hashTwo * 11579));
18
+ return (precision ? hash % Math.pow(2, precision) : hash).toString(36);
19
+ }
@@ -1,71 +1,71 @@
1
- import { BooleanFlag, Code, Constant, Metric, Setting, Severity } from "@clarity-types/data";
2
- import * as clarity from "@src/clarity";
3
- import * as core from "@src/core"
4
- import { bind } from "@src/core/event";
5
- import * as internal from "@src/diagnostic/internal";
6
- import * as metric from "@src/data/metric";
7
-
8
- let pushState = null;
9
- let replaceState = null;
10
- let url = null;
11
- let count = 0;
12
-
13
- export function start(): void {
14
- url = getCurrentUrl();
15
- count = 0;
16
- bind(window, "popstate", compute);
17
-
18
- // Add a proxy to history.pushState function
19
- if (pushState === null) {
20
- pushState = history.pushState;
21
- history.pushState = function(): void {
22
- pushState.apply(this, arguments);
23
- if (core.active() && check()) {
24
- compute();
25
- }
26
- };
27
- }
28
-
29
- // Add a proxy to history.replaceState function
30
- if (replaceState === null)
31
- {
32
- replaceState = history.replaceState;
33
- history.replaceState = function(): void {
34
- replaceState.apply(this, arguments);
35
- if (core.active() && check()) {
36
- compute();
37
- }
38
- };
39
- }
40
- }
41
-
42
- function check(): boolean {
43
- if (count++ > Setting.CallStackDepth) {
44
- internal.log(Code.CallStackDepth, Severity.Info);
45
- return false;
46
- }
47
- return true;
48
- }
49
-
50
- function compute(): void {
51
- count = 0; // Reset the counter
52
- if (url !== getCurrentUrl()) {
53
- // If the url changed, start tracking it as a new page
54
- clarity.stop();
55
- window.setTimeout(restart, Setting.RestartDelay);
56
- }
57
- }
58
-
59
- function restart(): void {
60
- clarity.start();
61
- metric.max(Metric.SinglePage, BooleanFlag.True);
62
- }
63
-
64
- function getCurrentUrl(): string {
65
- return location.href ? location.href.replace(location.hash, Constant.Empty) : location.href;
66
- }
67
-
68
- export function stop(): void {
69
- url = null;
70
- count = 0;
71
- }
1
+ import { BooleanFlag, Code, Constant, Metric, Setting, Severity } from "@clarity-types/data";
2
+ import * as clarity from "@src/clarity";
3
+ import * as core from "@src/core"
4
+ import { bind } from "@src/core/event";
5
+ import * as internal from "@src/diagnostic/internal";
6
+ import * as metric from "@src/data/metric";
7
+
8
+ let pushState = null;
9
+ let replaceState = null;
10
+ let url = null;
11
+ let count = 0;
12
+
13
+ export function start(): void {
14
+ url = getCurrentUrl();
15
+ count = 0;
16
+ bind(window, "popstate", compute);
17
+
18
+ // Add a proxy to history.pushState function
19
+ if (pushState === null) {
20
+ pushState = history.pushState;
21
+ history.pushState = function(): void {
22
+ pushState.apply(this, arguments);
23
+ if (core.active() && check()) {
24
+ compute();
25
+ }
26
+ };
27
+ }
28
+
29
+ // Add a proxy to history.replaceState function
30
+ if (replaceState === null)
31
+ {
32
+ replaceState = history.replaceState;
33
+ history.replaceState = function(): void {
34
+ replaceState.apply(this, arguments);
35
+ if (core.active() && check()) {
36
+ compute();
37
+ }
38
+ };
39
+ }
40
+ }
41
+
42
+ function check(): boolean {
43
+ if (count++ > Setting.CallStackDepth) {
44
+ internal.log(Code.CallStackDepth, Severity.Info);
45
+ return false;
46
+ }
47
+ return true;
48
+ }
49
+
50
+ function compute(): void {
51
+ count = 0; // Reset the counter
52
+ if (url !== getCurrentUrl()) {
53
+ // If the url changed, start tracking it as a new page
54
+ clarity.stop();
55
+ window.setTimeout(restart, Setting.RestartDelay);
56
+ }
57
+ }
58
+
59
+ function restart(): void {
60
+ clarity.start();
61
+ metric.max(Metric.SinglePage, BooleanFlag.True);
62
+ }
63
+
64
+ function getCurrentUrl(): string {
65
+ return location.href ? location.href.replace(location.hash, Constant.Empty) : location.href;
66
+ }
67
+
68
+ export function stop(): void {
69
+ url = null;
70
+ count = 0;
71
+ }