@zoho_apptics/apptics-js-sdk 2.0.0-beta → 2.0.0-beta.2

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 (236) hide show
  1. package/CHANGELOG.md +191 -21
  2. package/README.md +519 -333
  3. package/dist/apptics.esm.min.js +2 -2
  4. package/dist/apptics.esm.min.js.map +1 -1
  5. package/dist/apptics.min.js +2 -2
  6. package/dist/apptics.min.js.map +1 -1
  7. package/dist/chunks/lifecycle-C6m1W7jo.min.js +3 -0
  8. package/dist/chunks/lifecycle-C6m1W7jo.min.js.map +1 -0
  9. package/dist/chunks/send-CgUO82ne.min.js +3 -0
  10. package/dist/chunks/send-CgUO82ne.min.js.map +1 -0
  11. package/dist/cjs/core/attempts.js +1 -1
  12. package/dist/cjs/core/batch.js +2 -2
  13. package/dist/cjs/core/consent.d.ts +18 -4
  14. package/dist/cjs/core/consent.js +31 -15
  15. package/dist/cjs/core/constants.d.ts +57 -27
  16. package/dist/cjs/core/constants.js +52 -33
  17. package/dist/cjs/core/db.d.ts +12 -4
  18. package/dist/cjs/core/db.js +167 -34
  19. package/dist/cjs/core/debug.js +1 -1
  20. package/dist/cjs/core/env.d.ts +0 -3
  21. package/dist/cjs/core/env.js +1 -9
  22. package/dist/cjs/core/framework-id.d.ts +1 -1
  23. package/dist/cjs/core/framework-id.js +3 -3
  24. package/dist/cjs/core/guard.d.ts +9 -0
  25. package/dist/cjs/core/guard.js +27 -13
  26. package/dist/cjs/core/hooks.d.ts +7 -4
  27. package/dist/cjs/core/hooks.js +3 -3
  28. package/dist/cjs/core/locks.d.ts +9 -0
  29. package/dist/cjs/core/locks.js +22 -0
  30. package/dist/cjs/core/logger.d.ts +3 -4
  31. package/dist/cjs/core/logger.js +4 -40
  32. package/dist/cjs/core/state.d.ts +9 -6
  33. package/dist/cjs/core/state.js +3 -5
  34. package/dist/cjs/core/storage.d.ts +20 -5
  35. package/dist/cjs/core/storage.js +115 -20
  36. package/dist/cjs/core/stylesheet.d.ts +8 -0
  37. package/dist/cjs/core/stylesheet.js +29 -0
  38. package/dist/cjs/core/transport.d.ts +2 -0
  39. package/dist/cjs/core/transport.js +30 -18
  40. package/dist/cjs/core/utils.js +1 -1
  41. package/dist/cjs/core/validate.js +2 -2
  42. package/dist/cjs/device/appversion.d.ts +10 -6
  43. package/dist/cjs/device/appversion.js +38 -3
  44. package/dist/cjs/device/index.d.ts +28 -2
  45. package/dist/cjs/device/index.js +312 -44
  46. package/dist/cjs/engagement/api-rules.d.ts +32 -67
  47. package/dist/cjs/engagement/api-rules.js +144 -157
  48. package/dist/cjs/engagement/auto-api.js +2 -2
  49. package/dist/cjs/engagement/auto-screen.d.ts +2 -0
  50. package/dist/cjs/engagement/auto-screen.js +67 -3
  51. package/dist/cjs/engagement/defined-events.js +1 -1
  52. package/dist/cjs/engagement/send.js +12 -3
  53. package/dist/cjs/engagement/session.d.ts +29 -3
  54. package/dist/cjs/engagement/session.js +253 -26
  55. package/dist/cjs/engagement/track.d.ts +4 -4
  56. package/dist/cjs/engagement/track.js +58 -30
  57. package/dist/cjs/errors/send.js +2 -2
  58. package/dist/cjs/errors/track.d.ts +2 -1
  59. package/dist/cjs/errors/track.js +49 -7
  60. package/dist/cjs/feedback/controls.d.ts +12 -0
  61. package/dist/cjs/feedback/controls.js +104 -25
  62. package/dist/cjs/feedback/index.d.ts +8 -7
  63. package/dist/cjs/feedback/index.js +32 -13
  64. package/dist/cjs/feedback/logs.d.ts +13 -11
  65. package/dist/cjs/feedback/logs.js +57 -28
  66. package/dist/cjs/feedback/quartz.d.ts +18 -0
  67. package/dist/cjs/feedback/quartz.js +105 -0
  68. package/dist/cjs/feedback/send.d.ts +5 -2
  69. package/dist/cjs/feedback/send.js +30 -28
  70. package/dist/cjs/feedback/styles.d.ts +10 -0
  71. package/dist/cjs/feedback/styles.js +8 -0
  72. package/dist/cjs/feedback/template.d.ts +1 -1
  73. package/dist/cjs/feedback/template.js +20 -12
  74. package/dist/cjs/feedback/ui.d.ts +21 -6
  75. package/dist/cjs/feedback/ui.js +283 -70
  76. package/dist/cjs/heatmap/send.d.ts +2 -0
  77. package/dist/cjs/heatmap/send.js +3 -5
  78. package/dist/cjs/heatmap/track.d.ts +1 -1
  79. package/dist/cjs/heatmap/track.js +87 -16
  80. package/dist/cjs/index.d.ts +16 -63
  81. package/dist/cjs/index.js +144 -95
  82. package/dist/cjs/lifecycle.d.ts +21 -3
  83. package/dist/cjs/lifecycle.js +353 -89
  84. package/dist/cjs/remoteconfig/country.d.ts +4 -0
  85. package/dist/cjs/remoteconfig/country.js +73 -0
  86. package/dist/cjs/remoteconfig/custom.d.ts +7 -7
  87. package/dist/cjs/remoteconfig/custom.js +31 -51
  88. package/dist/cjs/remoteconfig/evaluate.d.ts +3 -3
  89. package/dist/cjs/remoteconfig/evaluate.js +56 -43
  90. package/dist/cjs/remoteconfig/fetch.d.ts +16 -9
  91. package/dist/cjs/remoteconfig/fetch.js +120 -39
  92. package/dist/cjs/remoteconfig/index.d.ts +19 -8
  93. package/dist/cjs/remoteconfig/index.js +81 -39
  94. package/dist/cjs/remoteconfig/store.d.ts +15 -7
  95. package/dist/cjs/remoteconfig/store.js +68 -35
  96. package/dist/cjs/remoteconfig/tz-countries.d.ts +8 -0
  97. package/dist/cjs/remoteconfig/tz-countries.js +13 -0
  98. package/dist/{esm/core → cjs/remotelog}/callsite.d.ts +11 -4
  99. package/dist/cjs/{core → remotelog}/callsite.js +30 -7
  100. package/dist/cjs/remotelog/gate.d.ts +2 -11
  101. package/dist/cjs/remotelog/gate.js +9 -35
  102. package/dist/cjs/remotelog/index.d.ts +4 -21
  103. package/dist/cjs/remotelog/logger.d.ts +11 -17
  104. package/dist/cjs/remotelog/logger.js +31 -59
  105. package/dist/cjs/remotelog/send.js +11 -11
  106. package/dist/cjs/remotelogger/index.d.ts +12 -0
  107. package/dist/cjs/remotelogger/index.js +18 -0
  108. package/dist/cjs/scheduler.d.ts +3 -1
  109. package/dist/cjs/scheduler.js +25 -22
  110. package/dist/cjs/types.d.ts +208 -90
  111. package/dist/cjs/user/index.js +16 -7
  112. package/dist/esm/core/attempts.js +1 -1
  113. package/dist/esm/core/batch.js +2 -2
  114. package/dist/esm/core/consent.d.ts +18 -4
  115. package/dist/esm/core/consent.js +32 -17
  116. package/dist/esm/core/constants.d.ts +57 -27
  117. package/dist/esm/core/constants.js +51 -33
  118. package/dist/esm/core/db.d.ts +12 -4
  119. package/dist/esm/core/db.js +164 -35
  120. package/dist/esm/core/debug.js +1 -1
  121. package/dist/esm/core/env.d.ts +0 -3
  122. package/dist/esm/core/env.js +2 -9
  123. package/dist/esm/core/framework-id.d.ts +1 -1
  124. package/dist/esm/core/framework-id.js +3 -3
  125. package/dist/esm/core/guard.d.ts +9 -0
  126. package/dist/esm/core/guard.js +27 -14
  127. package/dist/esm/core/hooks.d.ts +7 -4
  128. package/dist/esm/core/hooks.js +3 -3
  129. package/dist/esm/core/locks.d.ts +9 -0
  130. package/dist/esm/core/locks.js +20 -0
  131. package/dist/esm/core/logger.d.ts +3 -4
  132. package/dist/esm/core/logger.js +4 -40
  133. package/dist/esm/core/state.d.ts +9 -6
  134. package/dist/esm/core/state.js +3 -5
  135. package/dist/esm/core/storage.d.ts +20 -5
  136. package/dist/esm/core/storage.js +113 -21
  137. package/dist/esm/core/stylesheet.d.ts +8 -0
  138. package/dist/esm/core/stylesheet.js +27 -0
  139. package/dist/esm/core/transport.d.ts +2 -0
  140. package/dist/esm/core/transport.js +30 -19
  141. package/dist/esm/core/utils.js +1 -1
  142. package/dist/esm/core/validate.js +2 -2
  143. package/dist/esm/device/appversion.d.ts +10 -6
  144. package/dist/esm/device/appversion.js +37 -4
  145. package/dist/esm/device/index.d.ts +28 -2
  146. package/dist/esm/device/index.js +313 -49
  147. package/dist/esm/engagement/api-rules.d.ts +32 -67
  148. package/dist/esm/engagement/api-rules.js +143 -155
  149. package/dist/esm/engagement/auto-api.js +3 -3
  150. package/dist/esm/engagement/auto-screen.d.ts +2 -0
  151. package/dist/esm/engagement/auto-screen.js +67 -4
  152. package/dist/esm/engagement/defined-events.js +1 -1
  153. package/dist/esm/engagement/send.js +12 -3
  154. package/dist/esm/engagement/session.d.ts +29 -3
  155. package/dist/esm/engagement/session.js +251 -30
  156. package/dist/esm/engagement/track.d.ts +4 -4
  157. package/dist/esm/engagement/track.js +65 -37
  158. package/dist/esm/errors/send.js +2 -2
  159. package/dist/esm/errors/track.d.ts +2 -1
  160. package/dist/esm/errors/track.js +49 -7
  161. package/dist/esm/feedback/controls.d.ts +12 -0
  162. package/dist/esm/feedback/controls.js +103 -26
  163. package/dist/esm/feedback/index.d.ts +8 -7
  164. package/dist/esm/feedback/index.js +34 -13
  165. package/dist/esm/feedback/logs.d.ts +13 -11
  166. package/dist/esm/feedback/logs.js +59 -30
  167. package/dist/esm/feedback/quartz.d.ts +18 -0
  168. package/dist/esm/feedback/quartz.js +99 -0
  169. package/dist/esm/feedback/send.d.ts +5 -2
  170. package/dist/esm/feedback/send.js +31 -31
  171. package/dist/esm/feedback/styles.d.ts +10 -0
  172. package/dist/esm/feedback/styles.js +4 -0
  173. package/dist/esm/feedback/template.d.ts +1 -1
  174. package/dist/esm/feedback/template.js +20 -12
  175. package/dist/esm/feedback/ui.d.ts +21 -6
  176. package/dist/esm/feedback/ui.js +283 -71
  177. package/dist/esm/heatmap/send.d.ts +2 -0
  178. package/dist/esm/heatmap/send.js +3 -5
  179. package/dist/esm/heatmap/track.d.ts +1 -1
  180. package/dist/esm/heatmap/track.js +87 -16
  181. package/dist/esm/index.d.ts +16 -63
  182. package/dist/esm/index.js +148 -83
  183. package/dist/esm/lifecycle.d.ts +21 -3
  184. package/dist/esm/lifecycle.js +359 -98
  185. package/dist/esm/remoteconfig/country.d.ts +4 -0
  186. package/dist/esm/remoteconfig/country.js +70 -0
  187. package/dist/esm/remoteconfig/custom.d.ts +7 -7
  188. package/dist/esm/remoteconfig/custom.js +32 -52
  189. package/dist/esm/remoteconfig/evaluate.d.ts +3 -3
  190. package/dist/esm/remoteconfig/evaluate.js +57 -44
  191. package/dist/esm/remoteconfig/fetch.d.ts +16 -9
  192. package/dist/esm/remoteconfig/fetch.js +120 -39
  193. package/dist/esm/remoteconfig/index.d.ts +19 -8
  194. package/dist/esm/remoteconfig/index.js +82 -40
  195. package/dist/esm/remoteconfig/store.d.ts +15 -7
  196. package/dist/esm/remoteconfig/store.js +68 -36
  197. package/dist/esm/remoteconfig/tz-countries.d.ts +8 -0
  198. package/dist/esm/remoteconfig/tz-countries.js +11 -0
  199. package/dist/{cjs/core → esm/remotelog}/callsite.d.ts +11 -4
  200. package/dist/esm/{core → remotelog}/callsite.js +30 -8
  201. package/dist/esm/remotelog/gate.d.ts +2 -11
  202. package/dist/esm/remotelog/gate.js +10 -32
  203. package/dist/esm/remotelog/index.d.ts +4 -21
  204. package/dist/esm/remotelog/logger.d.ts +11 -17
  205. package/dist/esm/remotelog/logger.js +33 -61
  206. package/dist/esm/remotelog/send.js +13 -13
  207. package/dist/esm/remotelogger/index.d.ts +12 -0
  208. package/dist/esm/remotelogger/index.js +13 -0
  209. package/dist/esm/scheduler.d.ts +3 -1
  210. package/dist/esm/scheduler.js +26 -24
  211. package/dist/esm/types.d.ts +208 -90
  212. package/dist/esm/user/index.js +18 -8
  213. package/dist/feedback.esm.min.js +2 -2
  214. package/dist/feedback.esm.min.js.map +1 -1
  215. package/dist/remoteconfig.esm.min.js +3 -0
  216. package/dist/remoteconfig.esm.min.js.map +1 -0
  217. package/dist/remotelogger.esm.min.js +3 -0
  218. package/dist/remotelogger.esm.min.js.map +1 -0
  219. package/package.json +16 -17
  220. package/dist/chunks/batch-BU_mTpm1.min.js +0 -3
  221. package/dist/chunks/batch-BU_mTpm1.min.js.map +0 -1
  222. package/dist/chunks/logs-Bea5zUW_.min.js +0 -3
  223. package/dist/chunks/logs-Bea5zUW_.min.js.map +0 -1
  224. package/dist/cjs/auto-init.d.ts +0 -8
  225. package/dist/cjs/auto-init.js +0 -32
  226. package/dist/cjs/heatmap/index.d.ts +0 -16
  227. package/dist/cjs/heatmap/index.js +0 -22
  228. package/dist/cjs/remotelog/index.js +0 -55
  229. package/dist/esm/auto-init.d.ts +0 -8
  230. package/dist/esm/auto-init.js +0 -29
  231. package/dist/esm/heatmap/index.d.ts +0 -16
  232. package/dist/esm/heatmap/index.js +0 -17
  233. package/dist/esm/remotelog/index.js +0 -51
  234. package/dist/heatmap.esm.min.js +0 -3
  235. package/dist/heatmap.esm.min.js.map +0 -1
  236. package/dist/style.css +0 -1
@@ -1,4 +1,4 @@
1
- /*! @zoho_apptics/apptics-js-sdk v2.0.0-beta | (c) Zoho Corporation Private Limited | see LICENSE */
1
+ /*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.2 | (c) Zoho Corporation Private Limited | see LICENSE */
2
2
  'use strict';
3
3
 
4
4
  var constants = require('./core/constants.js');
@@ -9,7 +9,6 @@ var storage = require('./core/storage.js');
9
9
  var hooks = require('./core/hooks.js');
10
10
  var debug = require('./core/debug.js');
11
11
  var env = require('./core/env.js');
12
- var consent = require('./core/consent.js');
13
12
  var index = require('./device/index.js');
14
13
  var appversion = require('./device/appversion.js');
15
14
  var session = require('./engagement/session.js');
@@ -18,10 +17,10 @@ var autoScreen = require('./engagement/auto-screen.js');
18
17
  var track = require('./engagement/track.js');
19
18
  var track$1 = require('./errors/track.js');
20
19
  var scheduler = require('./scheduler.js');
21
- require('./remotelog/index.js');
20
+ var send = require('./remotelog/send.js');
22
21
  var index$1 = require('./user/index.js');
23
22
  var attempts = require('./core/attempts.js');
24
- var send = require('./remotelog/send.js');
23
+ var frameworkId = require('./core/framework-id.js');
25
24
 
26
25
  let listenersAttached = false;
27
26
  /** The config init() was last called with; reset() starts again from it. */
@@ -40,16 +39,29 @@ function isBrowser() {
40
39
  }
41
40
  }
42
41
  /**
43
- * Canonical config: serviceUrl, zak, identifier, appversion.
44
- * The console snippet uses older names (base_domain, token), which are accepted here.
45
- * serviceUrl loses any trailing slash, because request paths add their own.
42
+ * The config as the SDK keeps it: base_url, auth_key and app_domain under their inside names
43
+ * (serviceUrl, zak, identifier; see CONFIG_KEYS), and appversion as text. An earlier name of a
44
+ * key is still read when the key itself is not given, with a warning naming the key to use.
45
+ * The base url loses any trailing slash, because request paths add their own.
46
46
  */
47
47
  function normaliseConfig(config) {
48
48
  var settings = Object.assign({}, config);
49
- Object.keys(constants.CONFIG_ALIASES).forEach(function (key) {
50
- if (utils.isEmpty(settings[key]) && !utils.isEmpty(settings[constants.CONFIG_ALIASES[key]])) {
51
- settings[key] = settings[constants.CONFIG_ALIASES[key]];
49
+ Object.keys(constants.CONFIG_KEYS).forEach(function (key) {
50
+ var inside = constants.CONFIG_KEYS[key];
51
+ if (!utils.isEmpty(config[key])) {
52
+ settings[inside] = config[key];
53
+ return;
52
54
  }
55
+ delete settings[inside];
56
+ Object.keys(constants.RETIRED_CONFIG_NAMES).forEach(function (old) {
57
+ if (constants.RETIRED_CONFIG_NAMES[old] !== key || utils.isEmpty(config[old])) {
58
+ return;
59
+ }
60
+ logger.warnAlways('Apptics.init: ' + old + ' is now ' + key + '; rename it in your config'); // no i18n
61
+ if (utils.isEmpty(settings[inside])) {
62
+ settings[inside] = config[old];
63
+ }
64
+ });
53
65
  });
54
66
  if (!utils.isEmpty(settings.serviceUrl)) {
55
67
  settings.serviceUrl = String(settings.serviceUrl).replace(/\/+$/, ''); // no i18n
@@ -75,21 +87,62 @@ const APPVERSION_PLACEHOLDERS = ['YOUR_APP_VERSION', 'YOUR_BUILD_OR_APP_VERSION'
75
87
  function isAppVersionPlaceholder(version) {
76
88
  return APPVERSION_PLACEHOLDERS.indexOf(version.toUpperCase()) !== -1;
77
89
  }
90
+ /**
91
+ * The framework id this SDK reports to serviceUrl. The id built into the package is the one
92
+ * sdk-apptics.zoho.com issued at publish time, and localzoho servers don't know it: any
93
+ * localzoho serviceUrl reports 5970 instead, the id apptics.localzoho.com issued for 2.0.0-beta
94
+ * (Browser), so the same package also runs there.
95
+ */
96
+ const LOCALZOHO_FRAMEWORK_ID = 5970;
97
+ function frameworkIdFor(serviceUrl) {
98
+ var host = serviceUrl;
99
+ try {
100
+ host = new URL(serviceUrl).hostname;
101
+ }
102
+ catch (err) {
103
+ // not a url: judge the text as it is
104
+ }
105
+ return host.indexOf('localzoho') !== -1 ? LOCALZOHO_FRAMEWORK_ID : frameworkId.FRAMEWORK_ID; // no i18n
106
+ }
78
107
  /** Which app this browser's stored data belongs to, before app/addversion has named it. */
79
108
  function appKey(settings) {
80
109
  return settings.serviceUrl + '|' + settings.identifier; // no i18n
81
110
  }
111
+ /**
112
+ * The app version stored data belongs to, read before app/addversion replaces it: what tells a
113
+ * new release of the app from a reload. '' on a first visit.
114
+ */
115
+ var previousAppVersion = ''; // no i18n
82
116
  /**
83
117
  * Data stored for another app on this origin is discarded before anything else reads it,
84
118
  * including the stored app/addversion answer that would otherwise hand out that app's ids.
119
+ *
120
+ * Another app is a different base_url, app_domain or auth_key from the ones the stored data was
121
+ * registered with. Everything goes, the queues included, and start-up runs as on a first visit:
122
+ * app/addversion, device/add and the rest. Resolves once the store is empty.
85
123
  */
86
124
  function discardOtherAppData(settings) {
87
- var stored = storage.user_defaults.item('appkey'); // no i18n
88
- if (!utils.isEmpty(stored) && stored !== appKey(settings)) {
89
- logger.Console.log('init: stored data belongs to another app, resetting'); // no i18n
90
- clearStorage();
125
+ // which app it is, and which version, are in app_version_info (serviceUrl, identifier, a
126
+ // fingerprint of zak, and appversion): no keys of their own. 2.0.0-beta's appkey and
127
+ // appversion are read this once
128
+ var info = storage.user_defaults.item('app_version_info') || {}; // no i18n
129
+ var stored = !utils.isEmpty(info.serviceUrl) && !utils.isEmpty(info.identifier)
130
+ ? appKey(info)
131
+ : storage.user_defaults.item('appkey'); // no i18n
132
+ previousAppVersion = String((!utils.isEmpty(info.appversion) ? info.appversion : storage.user_defaults.item('appversion')) || ''); // no i18n
133
+ storage.user_defaults.add('appkey', undefined); // no i18n
134
+ storage.user_defaults.add('appversion', undefined); // no i18n
135
+ var otherApp = !utils.isEmpty(stored) && stored !== appKey(settings);
136
+ // a build that kept no fingerprint cannot tell: hasAppVersion() registers again, and records one
137
+ var otherKey = !utils.isEmpty(info.zakhash) && info.zakhash !== appversion.keyFingerprint(settings.zak);
138
+ if (!otherApp && !otherKey) {
139
+ return Promise.resolve();
91
140
  }
92
- storage.user_defaults.add('appkey', appKey(settings)); // no i18n
141
+ logger.Console.log('init: stored data was registered with another ' + (otherApp ? 'base_url or app_domain' : 'auth_key') + ', clearing it and starting as a first visit'); // no i18n
142
+ previousAppVersion = ''; // no i18n
143
+ // the remote config values read from the store, which is about to go
144
+ hooks.hooks.resetRemoteConfig();
145
+ return clearStorage();
93
146
  }
94
147
  /**
95
148
  * Timing an app may tune in init(). Each has a range: a value outside it is ignored with a
@@ -133,27 +186,32 @@ function applyIntervals(config) {
133
186
  }
134
187
  }
135
188
  }
189
+ /** Everything the SDK stores: settings, ids and every queue. Resolves once it is all gone. */
136
190
  function clearStorage() {
137
191
  attempts.clearAttempts();
138
192
  storage.user_defaults.clear();
139
- storage.engagement_transactions.clear();
140
- storage.error_transactions.clear();
193
+ return Promise.all([
194
+ storage.engagement_transactions, storage.error_transactions, storage.heatmap_transactions, storage.remotelog_transactions, storage.feedbacklog_transactions,
195
+ ].map(function (queue) {
196
+ return queue.clear();
197
+ })).then(function () {
198
+ return undefined;
199
+ });
141
200
  }
142
201
  /**
143
- * Starts the SDK. The config object is required: pass it directly, or let the console snippet
144
- * supply it (auto-init reads window.appticssettings._defaultoptions and calls init with it).
145
- * Returns true when the config has serviceUrl, zak, identifier and appversion and start-up has
202
+ * Starts the SDK. The config object is required: the SDK starts only when the app calls this.
203
+ * Returns true when the config has base_url, auth_key, app_domain and appversion and start-up has
146
204
  * begun (see ready()), false when it was already running, the config is missing or incomplete, or
147
205
  * there is no browser. Start-up calls app/addversion with those four, which answers with aaid, the
148
206
  * portal and project ids and the rest; then device/add registers the browser, as in 1.x.
149
207
  */
150
208
  function init(config) {
151
209
  if (!config || typeof config !== 'object') { // no i18n
152
- logger.warnAlways('Apptics.init: a config object is required, e.g. apptics.init({ serviceUrl, zak, identifier, appversion })'); // no i18n
210
+ logger.warnAlways('Apptics.init: a config object is required, e.g. Apptics.init({ base_url, auth_key, app_domain, appversion })'); // no i18n
153
211
  return false;
154
212
  }
155
213
  if (state._ZAP._initialised) {
156
- logger.Console.warn('Apptics.init: already initialised; call apptics.reset() to start again'); // no i18n
214
+ logger.Console.warn('Apptics.init: already initialised; call Apptics.reset() to start again'); // no i18n
157
215
  return false;
158
216
  }
159
217
  if (!isBrowser()) {
@@ -162,7 +220,7 @@ function init(config) {
162
220
  }
163
221
  var settings = normaliseConfig(config);
164
222
  var missing = constants.REQUIRED_CONFIG.filter(function (key) {
165
- return utils.isEmpty(settings[key]);
223
+ return utils.isEmpty(settings[constants.CONFIG_KEYS[key] || key]);
166
224
  });
167
225
  if (missing.length > 0) {
168
226
  logger.warnAlways('Apptics.init: missing required config: ' + missing.join(', ')); // no i18n
@@ -176,16 +234,14 @@ function init(config) {
176
234
  state._ZAP._defaultoptions = settings;
177
235
  // every request goes to serviceUrl; there is no other host to learn about
178
236
  state._ZAP._defaultoptions.base_domain = settings.serviceUrl;
237
+ // decided per init(), so a reset() or a page on another host never keeps the last one
238
+ state._ZAP._frameworkID = frameworkIdFor(settings.serviceUrl);
179
239
  // marks the SDK as started for the checks that need it; app/addversion replaces it with aaid
180
240
  state._ZAP._token = settings.identifier;
181
241
  // console_log: the SDK's own activity in the browser console
182
242
  if (!utils.isEmpty(config.console_log)) {
183
243
  state._ZAP._config.debug = !!config.console_log;
184
244
  }
185
- // track_session: false records no sessions, as Android's automaticSessionsTrackingStatus
186
- state._ZAP._config.track_session = typeof config.track_session === 'boolean' ? config.track_session : constants.AP_CONFIG.track_session; // no i18n
187
- // show_logs: the app's own logger lines in the browser console, as Android's showLogs
188
- state._ZAP._config.show_logs = typeof config.show_logs === 'boolean' ? config.show_logs : constants.AP_CONFIG.show_logs; // no i18n
189
245
  if (typeof config.ignore_do_not_track === 'boolean') { // no i18n
190
246
  state._ZAP._config.ignore_do_not_track = config.ignore_do_not_track;
191
247
  }
@@ -198,10 +254,14 @@ function init(config) {
198
254
  debug.applyDebugConfig(state._ZAP._config.debug_api);
199
255
  // what to call this device in the debug stream; see devicename in core/transport.ts
200
256
  state._ZAP._config.device_name = typeof config.device_name === 'string' ? config.device_name : constants.AP_CONFIG.device_name; // no i18n
201
- // remote logging: the SDK's own diagnostics, sent to console/addlog
202
- state._ZAP._config.remote_log = constants.AP_CONFIG.remote_log;
203
- if (typeof config.remote_log === 'boolean') { // no i18n
204
- state._ZAP._config.remote_log = config.remote_log;
257
+ // the SDK no longer reports its own failures to the Remote Logger page: only the lines an app
258
+ // logs with AppticsLogger go there
259
+ if (config.remote_log !== undefined) {
260
+ logger.warnAlways('Apptics.init: remote_log is no longer read; the Remote Logger page shows the lines your app logs with AppticsLogger'); // no i18n
261
+ }
262
+ // AppticsLogger lines go to the Remote Logger page only, never to the browser console
263
+ if (config.show_logs !== undefined) {
264
+ logger.warnAlways('Apptics.init: show_logs is no longer read; AppticsLogger lines are not printed to the browser console'); // no i18n
205
265
  }
206
266
  // automatic api tracking: the app's own requests, wrapped. Which hosts it records is
207
267
  // configureApiTracking's to say; the two keys that once did it are refused out loud rather
@@ -209,22 +269,42 @@ function init(config) {
209
269
  state._ZAP._config.track_api = constants.AP_CONFIG.track_api;
210
270
  ['track_api_hosts', 'track_api_exclude_hosts'].forEach(function (key) {
211
271
  if (config[key] !== undefined) {
212
- logger.warnAlways('Apptics.init: ' + key + ' is no longer supported; use configureApiTracking(new ApiTrackingBuilder().' // no i18n
272
+ logger.warnAlways('Apptics.init: ' + key + ' is no longer supported; use AppticsApi.configure(new AppticsApi.ApiTrackingBuilder().' // no i18n
213
273
  + (key === 'track_api_hosts' ? 'allowOnlyDomains' : 'ignoreDomains') + '(...).build())'); // no i18n
214
274
  }
215
275
  });
276
+ // screen recording is set up in the console now, and arrives with getsdkconfig
277
+ ['DC', 'quartz_workspace_key', 'quartz_department_key'].forEach(function (key) {
278
+ if (config[key] !== undefined) {
279
+ logger.warnAlways('Apptics.init: ' + key + ' is no longer read; screen recording in the feedback form is set up from the Apptics console'); // no i18n
280
+ }
281
+ });
282
+ // sessions are what everything else is recorded in; an app that wants nothing tracked uses
283
+ // AppticsSettings.setTrackingStatus instead
284
+ if (config.track_session !== undefined) {
285
+ logger.warnAlways('Apptics.init: track_session is no longer read; sessions are always recorded. To stop tracking, use AppticsSettings.setTrackingStatus(AppticsSettings.AP_TRACKING_STATUS.NO_TRACKING)'); // no i18n
286
+ }
216
287
  if (typeof config.track_api === 'boolean') { // no i18n
217
288
  state._ZAP._config.track_api = config.track_api;
218
289
  }
219
290
  autoApi.enableApiTracking(state._ZAP._config.track_api);
220
- // heatmap capture: a no-op unless the app imported the heatmap entry, which sets the hook
291
+ // heatmap capture: switched through the hook heatmap/track.ts sets
221
292
  state._ZAP._config.track_heatmap = typeof config.track_heatmap === 'boolean' // no i18n
222
293
  ? config.track_heatmap
223
294
  : constants.AP_CONFIG.track_heatmap;
224
- hooks.hooks.enableHeatmap(state._ZAP._config.track_heatmap);
295
+ hooks.hooks.trackHeatmap(state._ZAP._config.track_heatmap);
225
296
  // automatic screen tracking, on unless the config says false: switched on by start(), once a
226
297
  // screen view can be stored
227
298
  state._ZAP._config.track_screen = typeof config.track_screen === 'boolean' ? config.track_screen : constants.AP_CONFIG.track_screen; // no i18n
299
+ state._ZAP._config.screen_routes = screenRouteRules(config.screen_routes);
300
+ // uncaught errors and tagged clicks, on unless the config says false: the listeners check
301
+ // these as each error or click happens
302
+ state._ZAP._config.track_error = typeof config.track_error === 'boolean' ? config.track_error : constants.AP_CONFIG.track_error; // no i18n
303
+ state._ZAP._config.track_click = typeof config.track_click === 'boolean' ? config.track_click : constants.AP_CONFIG.track_click; // no i18n
304
+ // a theme in the config wins; without one, what AppticsFeedback.setFeedbackTheme set stays
305
+ if (config.feedback_theme === 'light' || config.feedback_theme === 'dark' || config.feedback_theme === 'auto') { // no i18n
306
+ state._ZAP._config.feedback_theme = config.feedback_theme;
307
+ }
228
308
  applyIntervals(config);
229
309
  lastConfig = config;
230
310
  state._ZAP._initialised = true;
@@ -242,22 +322,11 @@ function init(config) {
242
322
  state._ZAP._pending = [];
243
323
  return false;
244
324
  }
245
- discardOtherAppData(state._ZAP._defaultoptions);
246
- // app/addversion is the first call and everything waits on it: it is what turns the four
247
- // values init() was given into aaid, the portal and project ids every url carries, and
248
- // appreleaseversionid, which metaWebSDKV2 refuses a payload without
249
- return appversion.registerAppVersion().then(function (registered) {
325
+ return discardOtherAppData(state._ZAP._defaultoptions).then(function () {
250
326
  if (myGeneration !== generation) {
251
327
  return false;
252
328
  }
253
- if (!registered) {
254
- logger.warnAlways('Apptics.init: app/addversion did not return the app ids, not starting'); // no i18n
255
- state._ZAP._initialised = false;
256
- state._ZAP._token = ''; // no i18n
257
- state._ZAP._pending = [];
258
- return false;
259
- }
260
- return start(config, myGeneration);
329
+ return startUp(config, myGeneration);
261
330
  });
262
331
  }).catch(function (err) {
263
332
  logger.Console.error('Apptics.init: start-up failed', err); // no i18n
@@ -265,42 +334,177 @@ function init(config) {
265
334
  });
266
335
  return true;
267
336
  }
337
+ /** Start-up from app/addversion on, once the store holds only this app's data. */
338
+ function startUp(config, myGeneration) {
339
+ // app/addversion is the first call and everything waits on it: it is what turns the four
340
+ // values init() was given into aaid, the portal and project ids every url carries, and
341
+ // appreleaseversionid, which metaWebSDKV2 refuses a payload without
342
+ return appversion.registerAppVersion().then(function (registered) {
343
+ if (myGeneration !== generation) {
344
+ return false;
345
+ }
346
+ if (!registered && appversion.appVersionFailedForNetwork()) {
347
+ // a first visit with no network: start once it is back, keeping what the app
348
+ // tracks until then queued, rather than giving up for the rest of the page
349
+ logger.warnAlways('Apptics.init: offline, so app/addversion is tried again when the browser is back online; calls made until then are kept'); // no i18n
350
+ return registerWhenOnline(myGeneration).then(function (ok) {
351
+ if (myGeneration !== generation) {
352
+ return false;
353
+ }
354
+ if (!ok) {
355
+ state._ZAP._initialised = false;
356
+ state._ZAP._token = ''; // no i18n
357
+ state._ZAP._pending = [];
358
+ return false;
359
+ }
360
+ return start(config, myGeneration);
361
+ });
362
+ }
363
+ if (!registered) {
364
+ logger.warnAlways('Apptics.init: app/addversion did not return the app ids, not starting'); // no i18n
365
+ state._ZAP._initialised = false;
366
+ state._ZAP._token = ''; // no i18n
367
+ state._ZAP._pending = [];
368
+ return false;
369
+ }
370
+ return start(config, myGeneration);
371
+ });
372
+ }
373
+ /** How often a start-up waiting on the network tries app/addversion again, besides on `online`. */
374
+ const OFFLINE_RETRY_MS = 60 * 1000;
375
+ /**
376
+ * app/addversion again once the network is back: on the `online` event, and every minute for a
377
+ * network that came back without one. Resolves true once registered, false when the server
378
+ * refuses it or a reset()/new init() took over.
379
+ */
380
+ function registerWhenOnline(myGeneration) {
381
+ return new Promise(function (resolve) {
382
+ var timer = null;
383
+ var trying = false;
384
+ function done(ok) {
385
+ window.removeEventListener('online', attempt); // no i18n
386
+ if (timer !== null)
387
+ clearInterval(timer);
388
+ resolve(ok);
389
+ }
390
+ function attempt() {
391
+ if (myGeneration !== generation) {
392
+ done(false);
393
+ return;
394
+ }
395
+ if (trying || navigator.onLine === false) {
396
+ return;
397
+ }
398
+ trying = true;
399
+ appversion.registerAppVersion().then(function (registered) {
400
+ trying = false;
401
+ if (registered) {
402
+ done(true);
403
+ }
404
+ else if (!appversion.appVersionFailedForNetwork()) {
405
+ logger.warnAlways('Apptics.init: app/addversion did not return the app ids, not starting'); // no i18n
406
+ done(false);
407
+ }
408
+ });
409
+ }
410
+ window.addEventListener('online', attempt); // no i18n
411
+ timer = setInterval(attempt, OFFLINE_RETRY_MS);
412
+ });
413
+ }
414
+ /**
415
+ * screen_routes as init() keeps it: each entry checked, an exact path cleaned up, a regex given as
416
+ * a string compiled. An entry that cannot be used is left out, and said, rather than matching
417
+ * something its author did not mean.
418
+ */
419
+ function screenRouteRules(routes) {
420
+ if (!Array.isArray(routes)) {
421
+ return [];
422
+ }
423
+ var out = [];
424
+ routes.forEach(function (route) {
425
+ if (typeof route === 'string') { // no i18n
426
+ if (route.trim() !== '') { // no i18n
427
+ out.push({ kind: 'pattern', pattern: route.trim() }); // no i18n
428
+ }
429
+ return;
430
+ }
431
+ var rule = route && typeof route === 'object' ? route : null; // no i18n
432
+ var name = rule && typeof rule.name === 'string' ? rule.name.trim() : ''; // no i18n
433
+ if (rule && name !== '' && typeof rule.equals === 'string' && rule.equals.trim() !== '') { // no i18n
434
+ out.push({ kind: 'equals', path: autoScreen.routePath(rule.equals.trim()), name: name }); // no i18n
435
+ return;
436
+ }
437
+ if (rule && name !== '' && rule.regex !== undefined) {
438
+ var regex = null;
439
+ if (rule.regex instanceof RegExp) {
440
+ regex = rule.regex;
441
+ }
442
+ else if (typeof rule.regex === 'string' && rule.regex !== '') { // no i18n
443
+ try {
444
+ regex = new RegExp(rule.regex);
445
+ }
446
+ catch (err) {
447
+ regex = null;
448
+ }
449
+ }
450
+ if (regex) {
451
+ out.push({ kind: 'regex', regex: regex, name: name }); // no i18n
452
+ return;
453
+ }
454
+ }
455
+ logger.warnAlways('Apptics.init: screen_routes entry ' + safeJson(route) + ' is left out: give a route pattern, { equals, name } or { regex, name }'); // no i18n
456
+ });
457
+ return out;
458
+ }
459
+ /** An entry as the warning shows it: a RegExp as its source, which JSON.stringify would hide. */
460
+ function safeJson(value) {
461
+ try {
462
+ return JSON.stringify(value, function (_key, one) { return one instanceof RegExp ? String(one) : one; }) || String(value);
463
+ }
464
+ catch (err) {
465
+ return String(value);
466
+ }
467
+ }
268
468
  /** Resolves true once start-up has finished, false if init() refused or storage is unusable. */
269
469
  function ready() {
270
470
  return state._ZAP._readyPromise || Promise.resolve(false);
271
471
  }
272
472
  function start(config, myGeneration) {
473
+ // this tab's lock, before any screen view: it is how other tabs tell this one is still open
474
+ session.holdTabLock();
273
475
  // app/addversion named the app; data stored for another one was discarded before it ran
274
476
  state._ZAP._token = String(state._ZAP._defaultoptions.aaid);
275
- storage.user_defaults.add('token', state._ZAP._token); // no i18n
276
477
  // last known server settings (getupdates) apply before anything is tracked
277
478
  index.restoreServerSettings();
278
- // required by init(), so _defaultoptions.appversion is always the app's own version, as the
279
- // text normaliseConfig made of it: there is no default to fall back to, and the stored value
280
- // below is kept only to notice when the version changes
281
- var uuid = storage.user_defaults.item('uuid'); // no i18n
282
- if (utils.isEmpty(uuid)) {
283
- uuid = utils.getNewUUID();
284
- storage.user_defaults.add('uuid', uuid); // no i18n
285
- }
286
- state._ZAP._uuid = uuid;
287
- var stored_appversion = storage.user_defaults.item('appversion'); // no i18n
288
- if (utils.isEmpty(stored_appversion)) {
289
- storage.user_defaults.add('appversion', state._ZAP._defaultoptions.appversion); // no i18n
290
- stored_appversion = state._ZAP._defaultoptions.appversion;
291
- }
292
- var appVersionChanged = stored_appversion != state._ZAP._defaultoptions.appversion;
479
+ // the user this browser was tracking as, before getsdkconfig is asked: it carries their ids
480
+ index$1.getUserInfo();
481
+ // a new release of the app, against the version the stored data was for (see
482
+ // discardOtherAppData): a first visit has none, and is no change
483
+ var appVersionChanged = previousAppVersion !== '' && previousAppVersion != state._ZAP._defaultoptions.appversion; // no i18n
293
484
  state._ZAP._ignoreDnt = state._ZAP._config.ignore_do_not_track;
294
- storage.tracking_preference.update_status(consent.getTrackingStatus());
295
- index.checkAndUpdateDeviceInfo();
296
- // returning visit: ask the server for its settings now instead of waiting 2 minutes
297
- // (first visit: device/add triggers it once the ids exist)
298
- index.refreshServerSettings();
485
+ // the visitor's own stored choice, not getTrackingStatus(): that answers NO_TRACKING while Do
486
+ // Not Track is on, and storing it made one such visit switch tracking off for good
487
+ storage.tracking_preference.update_status(storage.tracking_preference.opt_in_status());
488
+ // the uuid and device info, and a registration on a first visit, under a lock shared with the
489
+ // site's other tabs (see prepareDevice)
490
+ return index.prepareDevice().then(function () {
491
+ if (myGeneration !== generation) {
492
+ return false;
493
+ }
494
+ return startTracking(appVersionChanged, myGeneration);
495
+ });
496
+ }
497
+ /** The rest of start-up, once the device is set up. */
498
+ function startTracking(appVersionChanged, myGeneration) {
499
+ // getsdkconfig, once per page load and then at each answer's next_check_after. A first visit
500
+ // asked as soon as device/add succeeded (registerDeviceWithApptics) and this does nothing; a
501
+ // returning visit, whose device is registered, asks now, after app/addversion. Until it
502
+ // answers, the settings saved by the last visit apply (restoreServerSettings, in start())
503
+ index.startServerSettingChecks();
299
504
  if (appVersionChanged) {
300
505
  state._ZAP._device_info.appversionid = ''; // no i18n
301
506
  index.updateDeviceWithApptics(state._ZAP._device_info).then(function (result) {
302
507
  if (utils.isSuccess(result)) {
303
- storage.user_defaults.add('appversion', state._ZAP._defaultoptions.appversion); // no i18n
304
508
  logger.Console.log('init: appversion updated to ' + state._ZAP._defaultoptions.appversion); // no i18n
305
509
  }
306
510
  }).catch(function (err) {
@@ -311,7 +515,6 @@ function start(config, myGeneration) {
311
515
  state._ZAP._device_info.connectionType = env.getConnectionType();
312
516
  attachListeners();
313
517
  state._ZAP._properties.orientation = env.getOrientation();
314
- index$1.getUserInfo();
315
518
  return session.checkAndUpdateSession().then(function () {
316
519
  if (myGeneration !== generation) {
317
520
  return false;
@@ -346,12 +549,13 @@ function start(config, myGeneration) {
346
549
  function reset() {
347
550
  generation++;
348
551
  scheduler.stopTimers();
552
+ session.resetInactivity();
349
553
  // the wrappers come off with everything else; the init() below puts them back if the config
350
554
  // asked for them, so a reset that has no config to restore leaves XHR and fetch untouched
351
555
  autoApi.enableApiTracking(false);
352
556
  autoScreen.enableScreenTracking(false);
353
557
  appversion.clearAppVersion();
354
- hooks.hooks.enableHeatmap(false);
558
+ hooks.hooks.trackHeatmap(false);
355
559
  hooks.hooks.resetHeatmap();
356
560
  hooks.hooks.resetRemoteConfig();
357
561
  clearStorage();
@@ -367,7 +571,7 @@ function reset() {
367
571
  state._ZAP._apid = ''; // no i18n
368
572
  state._ZAP._mapid = ''; // no i18n
369
573
  state._ZAP._sending = false;
370
- logger.Console.log('apptics.reset()'); // no i18n
574
+ logger.Console.log('Apptics.reset()'); // no i18n
371
575
  // start again with the config init() was called with
372
576
  if (lastConfig) {
373
577
  init(lastConfig);
@@ -376,9 +580,42 @@ function reset() {
376
580
  function isActive() {
377
581
  return state._ZAP._ready;
378
582
  }
379
- // Declarative click tracking: <el apeventaction="name" apeventprops='{"k":"v"}'> inside <el apgroupaction="group">
583
+ /**
584
+ * AppticsEvent.setClickTracking: click tracking from markup on or off, as the `track_click`
585
+ * config sets it. The listener stays; it checks the switch on every click.
586
+ */
587
+ function setClickTracking(enabled) {
588
+ state._ZAP._config.track_click = typeof enabled === 'boolean' ? enabled : true; // no i18n
589
+ logger.Console.log('click tracking: ' + (state._ZAP._config.track_click ? 'on' : 'off')); // no i18n
590
+ }
591
+ /**
592
+ * AppticsScreen.setScreenTracking: automatic screen tracking on or off, as the `track_screen`
593
+ * config sets it. Applied now once the SDK is running; before that it is only noted, and start()
594
+ * switches tracking to it. Like the other switches, the next init() (a reset() too) goes back to
595
+ * what its config says.
596
+ */
597
+ function setScreenTracking(enabled) {
598
+ state._ZAP._config.track_screen = typeof enabled === 'boolean' ? enabled : true; // no i18n
599
+ if (isActive()) {
600
+ autoScreen.enableScreenTracking(state._ZAP._config.track_screen);
601
+ }
602
+ }
603
+ /**
604
+ * A click-tracking attribute, in either spelling: `apeventaction`, or `data-apeventaction`, which
605
+ * is valid HTML and what React and TypeScript accept without declaring the attribute.
606
+ */
607
+ function trackingAttribute(el, name) {
608
+ var value = el.getAttribute(name);
609
+ return value !== null && value !== '' ? value : el.getAttribute('data-' + name); // no i18n
610
+ }
611
+ /** `[name]` and `[data-name]`, empty values left out, for closest(). */
612
+ function trackingSelector(name) {
613
+ return '[' + name + ']:not([' + name + '=""]),[data-' + name + ']:not([data-' + name + '=""])'; // no i18n
614
+ }
615
+ // Declarative click tracking: <el apeventaction="name" apeventprops='{"k":"v"}'> inside <el apgroupaction="group">,
616
+ // or the same attributes with a data- prefix
380
617
  function onDocumentClick(event) {
381
- if (!window.appticsReadyStatus || !isActive()) {
618
+ if (!window.appticsReadyStatus || !isActive() || !state._ZAP._config.track_click) {
382
619
  return;
383
620
  }
384
621
  var start = event.target;
@@ -388,15 +625,16 @@ function onDocumentClick(event) {
388
625
  if (!start) {
389
626
  return;
390
627
  }
391
- var targetElem = start.closest('[apeventaction]:not([apeventaction=""])'); // no i18n
628
+ var targetElem = start.closest(trackingSelector('apeventaction')); // no i18n
392
629
  if (!targetElem) {
393
630
  return;
394
631
  }
395
- var eventName = targetElem.getAttribute('apeventaction') || ''; // no i18n
396
- var groupElem = targetElem.closest('[apgroupaction]'); // no i18n
397
- var groupName = (groupElem && groupElem.getAttribute('apgroupaction')) || constants.AP_CONFIG.default_group_name; // no i18n
632
+ var eventName = trackingAttribute(targetElem, 'apeventaction') || ''; // no i18n
633
+ var groupElem = targetElem.closest(trackingSelector('apgroupaction')); // no i18n
634
+ // no apgroupaction around it: trackEvent refuses it and says the group is missing
635
+ var groupName = (groupElem && trackingAttribute(groupElem, 'apgroupaction')) || undefined; // no i18n
398
636
  var eventProps = {};
399
- var rawProps = targetElem.getAttribute('apeventprops'); // no i18n
637
+ var rawProps = trackingAttribute(targetElem, 'apeventprops'); // no i18n
400
638
  if (!utils.isEmpty(rawProps)) {
401
639
  try {
402
640
  eventProps = JSON.parse(rawProps);
@@ -418,10 +656,18 @@ function onDocumentClick(event) {
418
656
  function onPageHidden() {
419
657
  if (!isActive())
420
658
  return;
659
+ // the moment the user left: the session ends at it if they are not back within inactiveduration
660
+ session.markTabInactive(sendEndedSession);
421
661
  session.updateScreenWithEndtime();
422
662
  scheduler.flush(true);
423
663
  logger.Console.log('page hidden: closing the open screen and flushing'); // no i18n
424
664
  }
665
+ /** A session that inactivity ended while the tab was away: sent now, not when the user returns. */
666
+ function sendEndedSession() {
667
+ if (!isActive())
668
+ return;
669
+ scheduler.flush();
670
+ }
425
671
  /**
426
672
  * The tab is closing, or the page is being replaced or frozen into the back/forward cache.
427
673
  * The session ends here, and everything queued goes out by keepalive, since a normal
@@ -453,14 +699,18 @@ function attachListeners() {
453
699
  window.addEventListener('blur', function () {
454
700
  if (!isActive())
455
701
  return;
702
+ // focus moving into an iframe on the page blurs the window too, and the user is still here
703
+ if (document.activeElement && document.activeElement.tagName === 'IFRAME')
704
+ return; // no i18n
456
705
  logger.Console.log('Window lost focus, user switched to another window or application'); // no i18n
457
- session.updateLastActiveTimeOfSession();
706
+ session.markTabInactive(sendEndedSession);
458
707
  });
459
708
  // Track when the window gains focus (user switches back to the window)
460
709
  window.addEventListener('focus', function () {
461
710
  if (!isActive())
462
711
  return;
463
712
  logger.Console.log('Window regained focus, user switched back to the window'); // no i18n
713
+ session.markTabActive();
464
714
  session.checkAndUpdateSession();
465
715
  scheduler.startTimers();
466
716
  });
@@ -471,6 +721,7 @@ function attachListeners() {
471
721
  if (!isActive() || !event.persisted)
472
722
  return;
473
723
  logger.Console.log('restored from the back/forward cache: starting a new session'); // no i18n
724
+ session.markTabActive();
474
725
  // the screen the page was left on starts a new view, in the new session
475
726
  session.checkAndUpdateSession().then(track.resumeScreen);
476
727
  scheduler.startTimers();
@@ -482,12 +733,12 @@ function attachListeners() {
482
733
  onPageHidden();
483
734
  return;
484
735
  }
485
- // back in view: the server's tracking flags may have changed while we were away
486
- logger.Console.log('page visible: refreshing the server settings'); // no i18n
736
+ // back in view. getsdkconfig is not asked here: only at its next_check_after, on a tab's
737
+ // first page load, and when a user is registered or getupdates reports a change
738
+ session.markTabActive();
487
739
  // hiding ended the screen view; the screen the user is back on starts a new one, after the
488
740
  // session is settled so the view belongs to the right one
489
741
  session.checkAndUpdateSession().then(track.resumeScreen);
490
- index.refreshServerSettings();
491
742
  });
492
743
  // if we are running in cordova use ondeviceready otherwise onload
493
744
  window.addEventListener(window.cordova ? 'deviceready' : 'load', function () {
@@ -502,8 +753,8 @@ function attachListeners() {
502
753
  // until it has them: register before flushing, and flush once that has landed
503
754
  if (index.needsDevice()) {
504
755
  logger.Console.log('back online: registering this device before sending'); // no i18n
505
- index.checkAndUpdateDeviceInfo();
506
- index.deviceReady().then(function () {
756
+ // the same locked path as start-up, so two tabs coming back online register it once
757
+ index.prepareDevice().then(index.deviceReady).then(function () {
507
758
  if (!isActive())
508
759
  return;
509
760
  scheduler.flush();
@@ -521,18 +772,31 @@ function attachListeners() {
521
772
  });
522
773
  }
523
774
  window.addEventListener('unhandledrejection', function (event) {
524
- if (isActive() && state._ZAP._isGlobalErrorTracking) {
775
+ if (isActive() && state._ZAP._config.track_error) {
525
776
  track$1.trackError(event);
526
777
  }
527
778
  });
779
+ // in the capture phase: a <script>, <img> or <link> that fails to load fires `error` on the
780
+ // element, and it does not bubble, so a bubbling listener never hears of it. One listener
781
+ // hears both kinds, the window's own errors included, so none is counted twice
528
782
  window.addEventListener('error', function (event) {
529
- if (isActive() && state._ZAP._isGlobalErrorTracking) {
530
- track$1.trackError(event);
783
+ if (!isActive() || !state._ZAP._config.track_error) {
784
+ return;
531
785
  }
532
- });
786
+ var target = event.target;
787
+ if (target && target !== window && typeof target.tagName === 'string') { // no i18n
788
+ // named by describeTarget: Event "error" on <script src="...">
789
+ track$1.trackError({ type: 'error', reason: event }); // no i18n
790
+ return;
791
+ }
792
+ track$1.trackError(event);
793
+ }, true);
533
794
  window.addEventListener('click', onDocumentClick, true); // no i18n
534
795
  }
535
796
 
536
797
  exports.init = init;
537
798
  exports.ready = ready;
538
799
  exports.reset = reset;
800
+ exports.screenRouteRules = screenRouteRules;
801
+ exports.setClickTracking = setClickTracking;
802
+ exports.setScreenTracking = setScreenTracking;