@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');
@@ -38,19 +38,59 @@ function stackOf(errorEvent) {
38
38
  }
39
39
  /** Said in place of a stack when there is none, so `message` is never sent empty. */
40
40
  const NO_STACK = 'no stack available'; // no i18n
41
+ /** The element an event came from, as `<script src="…">`, so a failed load says what failed. */
42
+ function describeTarget(target) {
43
+ var el = target;
44
+ if (!el || typeof el.tagName !== 'string') { // no i18n
45
+ return ''; // no i18n
46
+ }
47
+ var tag = el.tagName.toLowerCase();
48
+ var url = el.currentSrc || el.src || el.href || ''; // no i18n
49
+ return ' on <' + tag + (url ? (el.href && !el.src ? ' href="' : ' src="') + url + '"' : '') + '>'; // no i18n
50
+ }
51
+ /**
52
+ * What was thrown or rejected, as text an issue can be named by. An Error reads as itself. A
53
+ * promise rejected with an Event - what a failed script or image load hands over - used to be
54
+ * recorded as "[object Event]"; it is named by its type and where it came from instead. Anything
55
+ * else that is an object is shown as its JSON, cut short.
56
+ */
57
+ function describeThrown(thrown) {
58
+ if (thrown === null || thrown === undefined) {
59
+ return ''; // no i18n
60
+ }
61
+ if (typeof thrown !== 'object') { // no i18n
62
+ return String(thrown);
63
+ }
64
+ if (thrown instanceof Error || typeof thrown.message === 'string' && typeof thrown.name === 'string') { // no i18n
65
+ return String(thrown);
66
+ }
67
+ if (typeof Event !== 'undefined' && thrown instanceof Event) { // no i18n
68
+ return 'Event "' + thrown.type + '"' + describeTarget(thrown.target); // no i18n
69
+ }
70
+ var text = String(thrown);
71
+ if (text !== '[object Object]') { // no i18n
72
+ return text;
73
+ }
74
+ try {
75
+ return JSON.stringify(thrown).slice(0, 300);
76
+ }
77
+ catch (err) {
78
+ return text;
79
+ }
80
+ }
41
81
  function trackError(errorEvent, jsonObj) {
42
82
  if (!consent.canTrackErrors('trackError')) { // no i18n
43
83
  return;
44
84
  }
45
85
  if (utils.isEmpty(errorEvent)) {
46
- logger.Console.warn('AppticsTrackError: Invalid error'); // no i18n
86
+ logger.Console.warn('AppticsError.trackError: Invalid error'); // no i18n
47
87
  return;
48
88
  }
49
89
  var _jsonObj = validate.validateJSON(jsonObj);
50
90
  var stack = stackOf(errorEvent);
51
91
  var methodName = extractMethodNameFromStackTrace(stack) || "";
52
92
  var issuetype = errorEvent.type || "";
53
- var thrown = errorEvent.reason || errorEvent.error || errorEvent.message || "";
93
+ var thrown = describeThrown(errorEvent.reason || errorEvent.error || errorEvent.message || "");
54
94
  // "Error: x at handler", or just "Error: x" when the stack names no method. issuename is
55
95
  // required too, so something that carried no message at all is still named rather than blank
56
96
  var described = !utils.isEmpty(String(thrown)) ? String(thrown) : (!utils.isEmpty(issuetype) ? String(issuetype) : 'Unknown error'); // no i18n
@@ -76,11 +116,13 @@ function trackError(errorEvent, jsonObj) {
76
116
  session.commitPendingSession();
77
117
  debug.noteDebugActivity();
78
118
  storage.error_transactions.add(errorData);
79
- logger.Console.log('AppticsTrackError(\'' + issuetype + '\',\'' + message + '\',' + JSON.stringify(_jsonObj || {}) + ')'); // no i18n
119
+ logger.Console.log('AppticsError.trackError(\'' + issuetype + '\',\'' + message + '\',' + JSON.stringify(_jsonObj || {}) + ')'); // no i18n
80
120
  logger.Console.dir(errorData);
81
121
  }
82
- function enableGlobalErrorHandler(status) {
83
- state._ZAP._isGlobalErrorTracking = typeof status === "boolean" ? status : true; // no i18n
122
+ /** AppticsError.setErrorTracking: uncaught errors on or off, as the `track_error` config sets it. */
123
+ function setErrorTracking(status) {
124
+ state._ZAP._config.track_error = typeof status === "boolean" ? status : true; // no i18n
125
+ logger.Console.log('automatic error tracking: ' + (state._ZAP._config.track_error ? 'on' : 'off')); // no i18n
84
126
  }
85
127
  function extractMethodNameFromStackTrace(stackTrace) {
86
128
  let methodName = ''; // no i18n
@@ -122,6 +164,6 @@ function extractMethodNameFromStackTrace(stackTrace) {
122
164
  return methodName;
123
165
  }
124
166
 
125
- exports.enableGlobalErrorHandler = enableGlobalErrorHandler;
126
167
  exports.extractMethodNameFromStackTrace = extractMethodNameFromStackTrace;
168
+ exports.setErrorTracking = setErrorTracking;
127
169
  exports.trackError = trackError;
@@ -12,6 +12,12 @@ export declare const logChoice: {
12
12
  includeLog?: boolean;
13
13
  includeDyninfo?: boolean;
14
14
  };
15
+ /**
16
+ * Who a sendFeedback() report, made without the form, is attributed to: nobody when `anonymous`,
17
+ * else the user AppticsUser.setUser set (or the active registered one, status 1). Returns a function
18
+ * that puts back the choice that was there, so a form left open keeps its own.
19
+ */
20
+ export declare function chooseUserForSend(anonymous: boolean): () => void;
15
21
  /**
16
22
  * The "User" dropdown above the description. It lists Anonymous plus every registered user, and
17
23
  * starts on the active one (status 1) unless the caller asked for anonymous.
@@ -29,3 +35,9 @@ export declare function wireLogControls(modal: HTMLElement, opts: Dict): void;
29
35
  * kept inside the viewport.
30
36
  */
31
37
  export declare function wireDrag(box: HTMLElement): void;
38
+ /**
39
+ * Lets the recording timer be dragged anywhere on the page. A press that moves it is a drag, and
40
+ * does not also stop the recording, as a plain click on it does. Call before the click listener
41
+ * is added, so a drag's click is stopped ahead of it.
42
+ */
43
+ export declare function wireTimerDrag(timer: HTMLElement): void;
@@ -1,10 +1,11 @@
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 logger = require('../core/logger.js');
5
5
  var hooks = require('../core/hooks.js');
6
6
  var storage = require('../core/storage.js');
7
7
  var utils = require('../core/utils.js');
8
+ var state = require('../core/state.js');
8
9
  var logs = require('./logs.js');
9
10
 
10
11
  /**
@@ -25,6 +26,34 @@ function resetUserChoice() {
25
26
  userChoice.userid = ''; // no i18n
26
27
  userChoice.groupid = ''; // no i18n
27
28
  }
29
+ /**
30
+ * Who a sendFeedback() report, made without the form, is attributed to: nobody when `anonymous`,
31
+ * else the user AppticsUser.setUser set (or the active registered one, status 1). Returns a function
32
+ * that puts back the choice that was there, so a form left open keeps its own.
33
+ */
34
+ function chooseUserForSend(anonymous) {
35
+ var before = { anonymous: userChoice.anonymous, userid: userChoice.userid, groupid: userChoice.groupid };
36
+ resetUserChoice();
37
+ if (!anonymous) {
38
+ // the user setUser set on this page, else the active one of those registered before
39
+ var active = !utils.isEmpty(state._ZAP._user_info.userid) ? state._ZAP._user_info : null;
40
+ storedUsers().forEach(function (user) {
41
+ if (!active && user && user.status == 1 && !utils.isEmpty(user.userid)) {
42
+ active = user;
43
+ }
44
+ });
45
+ if (active) {
46
+ userChoice.anonymous = false;
47
+ userChoice.userid = String(active.userid);
48
+ userChoice.groupid = String(active.groupid || ''); // no i18n
49
+ }
50
+ }
51
+ return function restore() {
52
+ userChoice.anonymous = before.anonymous;
53
+ userChoice.userid = before.userid;
54
+ userChoice.groupid = before.groupid;
55
+ };
56
+ }
28
57
  /** Every distinct registered user, by the address the picker shows. */
29
58
  function storedUsers() {
30
59
  var users = storage.user_defaults.item('users'); // no i18n
@@ -238,49 +267,99 @@ function wireDrag(box) {
238
267
  if (!header) {
239
268
  return;
240
269
  }
241
- var startX = 0, startY = 0, boxLeft = 0, boxTop = 0, dragging = false;
242
- header.addEventListener('mousedown', function (event) {
243
- if (event.target.closest('.zasdk-close')) { // no i18n
244
- return; // the close button is not a drag handle
245
- }
246
- if (!box.style.left || box.style.left === 'auto') { // no i18n
247
- var rect = box.getBoundingClientRect();
248
- box.style.right = 'auto'; // no i18n
249
- box.style.bottom = 'auto'; // no i18n
250
- box.style.left = rect.left + 'px'; // no i18n
251
- box.style.top = rect.top + 'px'; // no i18n
270
+ makeDraggable(box, header, function (target) {
271
+ // the close button is not a drag handle
272
+ return !!target.closest('.zasdk-close'); // no i18n
273
+ });
274
+ }
275
+ /** Where the recording timer was last dragged to, so the next recording's timer starts there. */
276
+ var timerSpot = null;
277
+ /**
278
+ * Lets the recording timer be dragged anywhere on the page. A press that moves it is a drag, and
279
+ * does not also stop the recording, as a plain click on it does. Call before the click listener
280
+ * is added, so a drag's click is stopped ahead of it.
281
+ */
282
+ function wireTimerDrag(timer) {
283
+ if (timerSpot) {
284
+ placeAt(timer, timerSpot.left, timerSpot.top);
285
+ }
286
+ makeDraggable(timer, timer, undefined, function (left, top) {
287
+ timerSpot = { left: left, top: top };
288
+ });
289
+ }
290
+ /** Puts a fixed box at left/top, inside the viewport. */
291
+ function placeAt(box, left, top) {
292
+ box.style.right = 'auto'; // no i18n
293
+ box.style.bottom = 'auto'; // no i18n
294
+ box.style.left = Math.max(0, Math.min(left, window.innerWidth - box.offsetWidth)) + 'px'; // no i18n
295
+ box.style.top = Math.max(0, Math.min(top, window.innerHeight - box.offsetHeight)) + 'px'; // no i18n
296
+ }
297
+ /**
298
+ * Moves `box` with the mouse or a finger pressed on `handle`. `skip` says which presses are not a
299
+ * drag; `moved` hears where the box ended up. A click that ends a drag goes no further.
300
+ */
301
+ function makeDraggable(box, handle, skip, moved) {
302
+ var startX = 0, startY = 0, boxLeft = 0, boxTop = 0, dragging = false, dragged = false;
303
+ handle.addEventListener('pointerdown', function (event) {
304
+ if (event.button !== 0 || (skip && skip(event.target))) {
305
+ return;
252
306
  }
307
+ var rect = box.getBoundingClientRect();
253
308
  dragging = true;
309
+ dragged = false;
254
310
  startX = event.clientX;
255
311
  startY = event.clientY;
256
- boxLeft = parseInt(box.style.left, 10);
257
- boxTop = parseInt(box.style.top, 10);
258
- header.style.cursor = 'grabbing'; // no i18n
312
+ boxLeft = rect.left;
313
+ boxTop = rect.top;
314
+ handle.style.cursor = 'move'; // no i18n
315
+ // the moves and the release come to the handle however far the pointer goes, so nothing
316
+ // is left listening on the document once the box is gone
317
+ try {
318
+ handle.setPointerCapture(event.pointerId);
319
+ }
320
+ catch (err) { /* a pointer already released */ }
259
321
  event.preventDefault();
260
322
  });
261
- document.addEventListener('mousemove', function (event) {
323
+ handle.addEventListener('pointermove', function (event) {
262
324
  if (!dragging) {
263
325
  return;
264
326
  }
265
- var left = boxLeft + (event.clientX - startX);
266
- var top = boxTop + (event.clientY - startY);
267
- box.style.left = Math.max(0, Math.min(left, window.innerWidth - box.offsetWidth)) + 'px'; // no i18n
268
- box.style.top = Math.max(0, Math.min(top, window.innerHeight - box.offsetHeight)) + 'px'; // no i18n
269
- });
270
- document.addEventListener('mouseup', function () {
271
- if (dragging) {
272
- dragging = false;
273
- header.style.cursor = 'grab'; // no i18n
327
+ var dx = event.clientX - startX, dy = event.clientY - startY;
328
+ if (!dragged && Math.abs(dx) < 4 && Math.abs(dy) < 4) {
329
+ return; // a press that wobbles is still a click
274
330
  }
331
+ dragged = true;
332
+ placeAt(box, boxLeft + dx, boxTop + dy);
275
333
  });
334
+ function stop() {
335
+ if (!dragging) {
336
+ return;
337
+ }
338
+ dragging = false;
339
+ handle.style.cursor = ''; // no i18n
340
+ if (dragged && moved) {
341
+ moved(parseInt(box.style.left, 10), parseInt(box.style.top, 10));
342
+ }
343
+ }
344
+ handle.addEventListener('pointerup', stop); // no i18n
345
+ handle.addEventListener('pointercancel', stop); // no i18n
346
+ box.addEventListener('click', function (event) {
347
+ if (dragged) {
348
+ dragged = false;
349
+ event.stopImmediatePropagation();
350
+ event.preventDefault();
351
+ }
352
+ }, true);
276
353
  }
277
354
  // core/transport asks for the picked user when it builds the feedback request's parameters
278
355
  hooks.hooks.feedbackUser = function () {
279
356
  return userChoice.anonymous || utils.isEmpty(userChoice.userid) ? null : userChoice;
280
357
  };
281
358
 
359
+ exports.chooseUserForSend = chooseUserForSend;
282
360
  exports.logChoice = logChoice;
283
361
  exports.userChoice = userChoice;
284
362
  exports.wireDrag = wireDrag;
285
363
  exports.wireLogControls = wireLogControls;
364
+ exports.wireTimerDrag = wireTimerDrag;
286
365
  exports.wireUserPicker = wireUserPicker;
@@ -1,8 +1,9 @@
1
- import { feedbackClose as feedbackCloseImpl } from './ui.js';
2
1
  import type { AppticsFeedbackApi } from '../types.js';
3
- export declare const feedbackOpen: AppticsFeedbackApi["feedbackOpen"];
4
- export declare const feedbackClose: typeof feedbackCloseImpl;
5
- /** Resolves once the feedback is sent; rejects when it wasn't (SDK not started, offline, refused). */
6
- export declare const sendFeedback: AppticsFeedbackApi["sendFeedback"];
7
- declare const feedback: AppticsFeedbackApi;
8
- export default feedback;
2
+ export type { FeedbackOpenOptions, SendFeedbackOptions } from '../types.js';
3
+ /**
4
+ * Android's AppticsFeedback: the form, sending feedback without it, and the context recorded for
5
+ * the next one. Every feedback call, addFeedbackLog and addDiagnosticsInfo included, is here and
6
+ * only here: the main entry has no AppticsFeedback.
7
+ */
8
+ export declare const AppticsFeedback: AppticsFeedbackApi;
9
+ export default AppticsFeedback;
@@ -1,37 +1,56 @@
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
  Object.defineProperty(exports, '__esModule', { value: true });
5
5
 
6
6
  var guard = require('../core/guard.js');
7
+ var constants = require('../core/constants.js');
7
8
  var logger = require('../core/logger.js');
8
9
  var lifecycle = require('../lifecycle.js');
9
10
  var ui = require('./ui.js');
11
+ var logs = require('./logs.js');
10
12
 
11
13
  /**
12
14
  * Optional feedback modal, shipped as its own entry point:
13
- * import { feedbackOpen } from '@zoho_apptics/apptics-js-sdk/feedback';
14
- * import '@zoho_apptics/apptics-js-sdk/style.css';
15
+ * import { AppticsFeedback } from '@zoho_apptics/apptics-js-sdk/feedback';
16
+ * AppticsFeedback.feedbackFormOpen();
15
17
  * Apps that never import it don't pay for the modal or its markup.
16
18
  * The <script> build includes it, so snippet users are unaffected.
17
19
  */
18
- const feedbackOpen = guard.guard('feedbackOpen', ui.feedbackOpen); // no i18n
19
- const feedbackClose = guard.guard('feedbackClose', ui.feedbackClose); // no i18n
20
+ const feedbackFormOpenGuarded = guard.guard('AppticsFeedback.feedbackFormOpen', ui.feedbackFormOpen); // no i18n
21
+ const feedbackFormCloseGuarded = guard.guard('AppticsFeedback.feedbackFormClose', ui.feedbackFormClose); // no i18n
22
+ const addFeedbackLogGuarded = guard.guard('AppticsFeedback.addFeedbackLog', logs.addFeedbackLog); // no i18n
23
+ const addDiagnosticsInfoGuarded = guard.guard('AppticsFeedback.addDiagnosticsInfo', logs.addDiagnosticsInfo); // no i18n
24
+ const setFeedbackThemeGuarded = guard.guard('AppticsFeedback.setFeedbackTheme', ui.setFeedbackTheme); // no i18n
20
25
  /** Resolves once the feedback is sent; rejects when it wasn't (SDK not started, offline, refused). */
21
- const sendFeedback = guard.guard('sendFeedback', function (...args) {
26
+ const sendFeedbackGuarded = guard.guard('AppticsFeedback.sendFeedback', function (...args) {
22
27
  return lifecycle.ready().then(function (started) {
23
28
  if (!started) {
24
- throw new Error('apptics.sendFeedback: not sent, the SDK has not started; call init() first'); // no i18n
29
+ throw new Error('AppticsFeedback.sendFeedback: not sent, the SDK has not started; call Apptics.init() first'); // no i18n
25
30
  }
26
31
  return ui.sendFeedback(...args);
27
32
  }).catch(function (err) {
28
- logger.Console.error('apptics.sendFeedback failed:', err); // no i18n
33
+ logger.Console.error('AppticsFeedback.sendFeedback failed:', err); // no i18n
29
34
  throw err;
30
35
  });
31
36
  });
32
- const feedback = { feedbackOpen, feedbackClose, sendFeedback };
37
+ /* each call a function of its own, with the parameter names the console shows (see forward) */
38
+ /**
39
+ * Android's AppticsFeedback: the form, sending feedback without it, and the context recorded for
40
+ * the next one. Every feedback call, addFeedbackLog and addDiagnosticsInfo included, is here and
41
+ * only here: the main entry has no AppticsFeedback.
42
+ */
43
+ const AppticsFeedback = {
44
+ /** The feedback types: BUG, FEEDBACK, HELP, REPORT. */
45
+ AP_FEEDBACK_TYPE: constants.AP_FEEDBACK_TYPE,
46
+ feedbackFormOpen: function feedbackFormOpen(options) { return guard.forward(feedbackFormOpenGuarded, arguments); },
47
+ feedbackFormClose: function feedbackFormClose() { return guard.forward(feedbackFormCloseGuarded, arguments); },
48
+ sendFeedback: function sendFeedback(options) { return guard.forward(sendFeedbackGuarded, arguments); },
49
+ addFeedbackLog: function addFeedbackLog(message) { return guard.forward(addFeedbackLogGuarded, arguments); },
50
+ addDiagnosticsInfo: function addDiagnosticsInfo(heading, key, value) { return guard.forward(addDiagnosticsInfoGuarded, arguments); },
51
+ /** The form's theme from now on, and of a form open now: 'light', 'dark' or 'auto'. */
52
+ setFeedbackTheme: function setFeedbackTheme(theme) { return guard.forward(setFeedbackThemeGuarded, arguments); },
53
+ };
33
54
 
34
- exports.default = feedback;
35
- exports.feedbackClose = feedbackClose;
36
- exports.feedbackOpen = feedbackOpen;
37
- exports.sendFeedback = sendFeedback;
55
+ exports.AppticsFeedback = AppticsFeedback;
56
+ exports.default = AppticsFeedback;
@@ -15,26 +15,28 @@ export declare function trimStoredLogs(): Promise<void>;
15
15
  */
16
16
  export declare function addFeedbackLog(message: string): void;
17
17
  /**
18
- * Records a diagnostics key/value under a heading, sent inside dyninfo.txt.
19
- * Entries sharing a heading are grouped together in the file.
18
+ * Records a diagnostics entry, `value` under `key` in the `heading` block of dyninfo.txt. All three
19
+ * are required: an entry without a value says nothing, so it is refused and the console says why.
20
+ * A value of 0 or false is a value.
20
21
  */
21
- export declare function addDiagnosticsInfo(heading: string, key: string, value?: unknown): void;
22
+ export declare function addDiagnosticsInfo(heading: string, key: string, value: unknown): void;
22
23
  /** Removes every stored entry. */
23
24
  export declare function clearFeedbackLogs(): Promise<void>;
24
25
  /** Removes the entries a submission carried, by the ids the rendered files reported. */
25
- export declare function log_transactions_remove(sentIds: string[]): Promise<void>;
26
+ export declare function feedbacklog_transactions_remove(sentIds: string[]): Promise<void>;
26
27
  /** feedbacklog.txt from the stored log lines, or null when there are none. */
27
28
  export declare function toLogFile(): Promise<RenderedFile | null>;
28
- /** The log lines as the file would carry them, for showing in the modal before sending. */
29
+ /**
30
+ * The log lines for showing in the modal before sending, each time in the visitor's local zone.
31
+ * The file that is sent (toLogFile) keeps the ISO 8601 UTC stamps, so the server and anything
32
+ * that parses it are unaffected.
33
+ */
29
34
  export declare function logText(): Promise<string>;
30
- /** The diagnostics entries as dyninfo.txt would carry them, without the device info block. */
35
+ /** The diagnostics entries as dyninfo.txt carries them. */
31
36
  export declare function diagnosticsText(): Promise<string>;
32
37
  /** True when any log line is waiting, so the modal knows whether to offer the toggle. */
33
38
  export declare function hasLogLines(): Promise<boolean>;
34
- /**
35
- * dyninfo.txt: the stored diagnostics entries followed by a [Device Info] block holding
36
- * `deviceInfo`. Returns null only when there is nothing at all to put in it.
37
- */
38
- export declare function toDyninfoFile(deviceInfo: string | null): Promise<RenderedFile | null>;
39
+ /** dyninfo.txt: the stored diagnostics entries. Returns null when there are none. */
40
+ export declare function toDyninfoFile(): Promise<RenderedFile | null>;
39
41
  /** True when any diagnostics entry is waiting, so a submission knows to send dyninfo unasked. */
40
42
  export declare function hasDiagnostics(): Promise<boolean>;
@@ -1,7 +1,8 @@
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 logger = require('../core/logger.js');
5
+ var db = require('../core/db.js');
5
6
  var storage = require('../core/storage.js');
6
7
  var state = require('../core/state.js');
7
8
  var utils = require('../core/utils.js');
@@ -26,18 +27,19 @@ const MAX_LOG_FILE_SIZE = 2 * 1024 * 1024;
26
27
  const TRIM_DELAY_MS = 2000;
27
28
  const LOG_FILE_NAME = 'feedbacklog.txt'; // no i18n
28
29
  const DYNINFO_FILE_NAME = 'dyninfo.txt'; // no i18n
29
- const LOG = 'log'; // no i18n
30
- const DIAGNOSTICS = 'diagnostics'; // no i18n
30
+ // the types the entries are stored under (2.0.0-beta and 1.x said log and diagnostics)
31
+ const LOG = db.FEEDBACK_LOG;
32
+ const DIAGNOSTICS = db.FEEDBACK_DIAGNOSTICS;
31
33
  let trimScheduled = false;
32
34
  /** Drops the oldest entries once the store is over MAX_ENTRIES. */
33
35
  function trimStoredLogs() {
34
- return storage.log_transactions.all().then(function (items) {
36
+ return storage.feedbacklog_transactions.all().then(function (items) {
35
37
  const excess = items.length - MAX_ENTRIES;
36
38
  if (excess <= 0) {
37
39
  return undefined;
38
40
  }
39
41
  logger.Console.warn('apptics: dropping ' + excess + ' oldest feedback log entries (cap is ' + MAX_ENTRIES + ')'); // no i18n
40
- return storage.log_transactions.remove(items.slice(0, excess));
42
+ return storage.feedbacklog_transactions.remove(items.slice(0, excess));
41
43
  });
42
44
  }
43
45
  /** One trim per burst of adds, so a chatty app pays for one pass over the store, not one per line. */
@@ -52,7 +54,7 @@ function scheduleTrim() {
52
54
  }, TRIM_DELAY_MS);
53
55
  }
54
56
  function store(item) {
55
- storage.log_transactions.add(item);
57
+ storage.feedbacklog_transactions.add(item);
56
58
  scheduleTrim();
57
59
  }
58
60
  function truncate(text) {
@@ -72,11 +74,30 @@ function addFeedbackLog(message) {
72
74
  * Records a diagnostics key/value under a heading, sent inside dyninfo.txt.
73
75
  * Entries sharing a heading are grouped together in the file.
74
76
  */
77
+ /** Messages already printed, so a call repeated in a loop says so once. */
78
+ const refusedSaid = {};
79
+ function refuse(message) {
80
+ if (!refusedSaid[message]) {
81
+ refusedSaid[message] = true;
82
+ logger.warnAlways('AppticsFeedback.addDiagnosticsInfo: ' + message + ', so the entry is not recorded'); // no i18n
83
+ }
84
+ }
85
+ /**
86
+ * Records a diagnostics entry, `value` under `key` in the `heading` block of dyninfo.txt. All three
87
+ * are required: an entry without a value says nothing, so it is refused and the console says why.
88
+ * A value of 0 or false is a value.
89
+ */
75
90
  function addDiagnosticsInfo(heading, key, value) {
76
91
  if (typeof heading !== 'string' || heading.length === 0) { // no i18n
92
+ refuse('a heading is required');
77
93
  return;
78
94
  }
79
95
  if (typeof key !== 'string' || key.length === 0) { // no i18n
96
+ refuse('a key is required');
97
+ return;
98
+ }
99
+ if (value === undefined || value === null || value === '') { // no i18n
100
+ refuse('a value is required for "' + heading + '" / "' + key + '"');
80
101
  return;
81
102
  }
82
103
  store({
@@ -84,16 +105,16 @@ function addDiagnosticsInfo(heading, key, value) {
84
105
  happenedat: utils.nowMs(),
85
106
  heading: heading,
86
107
  key: key,
87
- value: truncate(String(value === undefined ? '' : value)), // no i18n
108
+ value: truncate(String(value)),
88
109
  });
89
110
  }
90
111
  /** Removes the entries a submission carried, by the ids the rendered files reported. */
91
- function log_transactions_remove(sentIds) {
92
- return storage.log_transactions.remove(sentIds.map(function (id) { return { id: id }; }));
112
+ function feedbacklog_transactions_remove(sentIds) {
113
+ return storage.feedbacklog_transactions.remove(sentIds.map(function (id) { return { id: id }; }));
93
114
  }
94
115
  /** Entries of one type, oldest first, skipping any that have passed batch_request_timeout. */
95
116
  function entriesOfType(type) {
96
- return storage.log_transactions.all().then(function (items) {
117
+ return storage.feedbacklog_transactions.all().then(function (items) {
97
118
  return items.filter(function (item) {
98
119
  return item.type === type && !state.isExpired(item.happenedat);
99
120
  });
@@ -147,11 +168,29 @@ function toLogFile() {
147
168
  return asFile(LOG_FILE_NAME, renderLogLines(items), items);
148
169
  });
149
170
  }
150
- /** The log lines as the file would carry them, for showing in the modal before sending. */
171
+ /** `16 Oct 2026, 11:53:48 PM`: a log line's time in the visitor's own zone, for the viewer. */
172
+ function readableStamp(item) {
173
+ var iso = stamp(item);
174
+ var d = new Date(iso);
175
+ if (isNaN(d.getTime())) {
176
+ return iso;
177
+ }
178
+ return d.toLocaleDateString(undefined, { day: '2-digit', month: 'short', year: 'numeric' }) + ', ' + // no i18n
179
+ d.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit', second: '2-digit' }); // no i18n
180
+ }
181
+ /**
182
+ * The log lines for showing in the modal before sending, each time in the visitor's local zone.
183
+ * The file that is sent (toLogFile) keeps the ISO 8601 UTC stamps, so the server and anything
184
+ * that parses it are unaffected.
185
+ */
151
186
  function logText() {
152
- return entriesOfType(LOG).then(renderLogLines);
187
+ return entriesOfType(LOG).then(function (items) {
188
+ return items.map(function (item) {
189
+ return '[' + readableStamp(item) + '] ' + item.message; // no i18n
190
+ }).join('\n'); // no i18n
191
+ });
153
192
  }
154
- /** The diagnostics entries as dyninfo.txt would carry them, without the device info block. */
193
+ /** The diagnostics entries as dyninfo.txt carries them. */
155
194
  function diagnosticsText() {
156
195
  return entriesOfType(DIAGNOSTICS).then(groupDiagnostics);
157
196
  }
@@ -161,23 +200,13 @@ function hasLogLines() {
161
200
  return items.length > 0;
162
201
  });
163
202
  }
164
- /**
165
- * dyninfo.txt: the stored diagnostics entries followed by a [Device Info] block holding
166
- * `deviceInfo`. Returns null only when there is nothing at all to put in it.
167
- */
168
- function toDyninfoFile(deviceInfo) {
203
+ /** dyninfo.txt: the stored diagnostics entries. Returns null when there are none. */
204
+ function toDyninfoFile() {
169
205
  return entriesOfType(DIAGNOSTICS).then(function (items) {
170
- const blocks = [];
171
- if (items.length > 0) {
172
- blocks.push(groupDiagnostics(items));
173
- }
174
- if (deviceInfo) {
175
- blocks.push('[Device Info]\n' + deviceInfo); // no i18n
176
- }
177
- if (blocks.length === 0) {
206
+ if (items.length === 0) {
178
207
  return null;
179
208
  }
180
- return asFile(DYNINFO_FILE_NAME, blocks.join('\n\n'), items); // no i18n
209
+ return asFile(DYNINFO_FILE_NAME, groupDiagnostics(items), items);
181
210
  });
182
211
  }
183
212
  /** True when any diagnostics entry is waiting, so a submission knows to send dyninfo unasked. */
@@ -193,10 +222,10 @@ exports.MAX_LOG_FILE_SIZE = MAX_LOG_FILE_SIZE;
193
222
  exports.addDiagnosticsInfo = addDiagnosticsInfo;
194
223
  exports.addFeedbackLog = addFeedbackLog;
195
224
  exports.diagnosticsText = diagnosticsText;
225
+ exports.feedbacklog_transactions_remove = feedbacklog_transactions_remove;
196
226
  exports.hasDiagnostics = hasDiagnostics;
197
227
  exports.hasLogLines = hasLogLines;
198
228
  exports.logText = logText;
199
- exports.log_transactions_remove = log_transactions_remove;
200
229
  exports.toDyninfoFile = toDyninfoFile;
201
230
  exports.toLogFile = toLogFile;
202
231
  exports.trimStoredLogs = trimStoredLogs;
@@ -0,0 +1,18 @@
1
+ /** quartz_domain as a recording host: zoho.com → https://quartz.zoho.com. '' when it isn't a domain. */
2
+ export declare function recordingDomain(domain: string): string;
3
+ /**
4
+ * The Quartz script for an app whose getsdkconfig answer carries no quartz_cdn_url, chosen by
5
+ * the serviceUrl: a localzoho server's apps load it from Zoho's local CDN, every other app from
6
+ * the public one.
7
+ */
8
+ export declare function defaultQuartzScriptUrl(serviceUrl: string): string;
9
+ /** The script to load: quartz_cdn_url when getsdkconfig gave one, else the CDN the serviceUrl points to. */
10
+ export declare function quartzScriptSource(cdnUrl: string, serviceUrl: string): string;
11
+ /** The url to load, or '' when it isn't an https url on a Zoho host. */
12
+ export declare function quartzScriptUrl(url: string): string;
13
+ /**
14
+ * Makes Quartz available: resolves true once it is on the page, false when it can't be (no url,
15
+ * a url that isn't allowed, or a script that failed to load). Adds the script once; every caller
16
+ * waits on the same load, and a failed one may be tried again on the next open.
17
+ */
18
+ export declare function loadQuartz(url: string): Promise<boolean>;