@zoho_apptics/apptics-js-sdk 1.0.2 → 2.0.0-beta.1
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.
- package/CHANGELOG.md +193 -0
- package/LICENSE +13 -0
- package/README.md +846 -11
- package/dist/apptics.esm.min.js +3 -0
- package/dist/apptics.esm.min.js.map +1 -0
- package/dist/apptics.min.js +3 -0
- package/dist/apptics.min.js.map +1 -0
- package/dist/chunks/lifecycle-VYr43KcB.min.js +3 -0
- package/dist/chunks/lifecycle-VYr43KcB.min.js.map +1 -0
- package/dist/cjs/auto-init.d.ts +8 -0
- package/dist/cjs/auto-init.js +32 -0
- package/dist/cjs/core/attempts.d.ts +28 -0
- package/dist/cjs/core/attempts.js +113 -0
- package/dist/cjs/core/batch.d.ts +32 -0
- package/dist/cjs/core/batch.js +116 -0
- package/dist/cjs/core/callsite.d.ts +34 -0
- package/dist/cjs/core/callsite.js +115 -0
- package/dist/cjs/core/consent.d.ts +27 -0
- package/dist/cjs/core/consent.js +132 -0
- package/dist/cjs/core/constants.d.ts +237 -0
- package/dist/cjs/core/constants.js +315 -0
- package/dist/cjs/core/db.d.ts +48 -0
- package/dist/cjs/core/db.js +467 -0
- package/dist/cjs/core/debug.d.ts +22 -0
- package/dist/cjs/core/debug.js +88 -0
- package/dist/cjs/core/env.d.ts +28 -0
- package/dist/cjs/core/env.js +267 -0
- package/dist/cjs/core/framework-id.d.ts +1 -0
- package/dist/cjs/core/framework-id.js +9 -0
- package/dist/cjs/core/guard.d.ts +11 -0
- package/dist/cjs/core/guard.js +86 -0
- package/dist/cjs/core/hooks.d.ts +39 -0
- package/dist/cjs/core/hooks.js +22 -0
- package/dist/cjs/core/locks.d.ts +9 -0
- package/dist/cjs/core/locks.js +22 -0
- package/dist/cjs/core/logger.d.ts +15 -0
- package/dist/cjs/core/logger.js +50 -0
- package/dist/cjs/core/state.d.ts +70 -0
- package/dist/cjs/core/state.js +78 -0
- package/dist/cjs/core/storage.d.ts +61 -0
- package/dist/cjs/core/storage.js +327 -0
- package/dist/cjs/core/stylesheet.d.ts +8 -0
- package/dist/cjs/core/stylesheet.js +29 -0
- package/dist/cjs/core/transport.d.ts +85 -0
- package/dist/cjs/core/transport.js +456 -0
- package/dist/cjs/core/utils.d.ts +26 -0
- package/dist/cjs/core/utils.js +175 -0
- package/dist/cjs/core/validate.d.ts +18 -0
- package/dist/cjs/core/validate.js +233 -0
- package/dist/cjs/device/appversion.d.ts +15 -0
- package/dist/cjs/device/appversion.js +170 -0
- package/dist/cjs/device/index.d.ts +34 -0
- package/dist/cjs/device/index.js +472 -0
- package/dist/cjs/engagement/api-rules.d.ts +49 -0
- package/dist/cjs/engagement/api-rules.js +206 -0
- package/dist/cjs/engagement/auto-api.d.ts +7 -0
- package/dist/cjs/engagement/auto-api.js +200 -0
- package/dist/cjs/engagement/auto-screen.d.ts +18 -0
- package/dist/cjs/engagement/auto-screen.js +305 -0
- package/dist/cjs/engagement/defined-events.d.ts +23 -0
- package/dist/cjs/engagement/defined-events.js +58 -0
- package/dist/cjs/engagement/send.d.ts +6 -0
- package/dist/cjs/engagement/send.js +104 -0
- package/dist/cjs/engagement/session.d.ts +51 -0
- package/dist/cjs/engagement/session.js +463 -0
- package/dist/cjs/engagement/track.d.ts +56 -0
- package/dist/cjs/engagement/track.js +375 -0
- package/dist/cjs/errors/send.d.ts +6 -0
- package/dist/cjs/errors/send.js +73 -0
- package/dist/cjs/errors/track.d.ts +4 -0
- package/dist/cjs/errors/track.js +167 -0
- package/dist/cjs/feedback/controls.d.ts +37 -0
- package/dist/cjs/feedback/controls.js +316 -0
- package/dist/cjs/feedback/index.d.ts +9 -0
- package/dist/cjs/feedback/index.js +56 -0
- package/dist/cjs/feedback/logs.d.ts +45 -0
- package/dist/cjs/feedback/logs.js +241 -0
- package/dist/cjs/feedback/quartz.d.ts +18 -0
- package/dist/cjs/feedback/quartz.js +105 -0
- package/dist/cjs/feedback/send.d.ts +28 -0
- package/dist/cjs/feedback/send.js +203 -0
- package/dist/cjs/feedback/styles.d.ts +10 -0
- package/dist/cjs/feedback/styles.js +8 -0
- package/dist/cjs/feedback/template.d.ts +2 -0
- package/dist/cjs/feedback/template.js +162 -0
- package/dist/cjs/feedback/ui.d.ts +63 -0
- package/dist/cjs/feedback/ui.js +709 -0
- package/dist/cjs/heatmap/send.d.ts +13 -0
- package/dist/cjs/heatmap/send.js +82 -0
- package/dist/cjs/heatmap/track.d.ts +4 -0
- package/dist/cjs/heatmap/track.js +448 -0
- package/dist/cjs/index.d.ts +22 -0
- package/dist/cjs/index.js +162 -0
- package/dist/cjs/lifecycle.d.ts +21 -0
- package/dist/cjs/lifecycle.js +734 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/remoteconfig/country.d.ts +4 -0
- package/dist/cjs/remoteconfig/country.js +73 -0
- package/dist/cjs/remoteconfig/custom.d.ts +10 -0
- package/dist/cjs/remoteconfig/custom.js +58 -0
- package/dist/cjs/remoteconfig/evaluate.d.ts +24 -0
- package/dist/cjs/remoteconfig/evaluate.js +184 -0
- package/dist/cjs/remoteconfig/fetch.d.ts +24 -0
- package/dist/cjs/remoteconfig/fetch.js +200 -0
- package/dist/cjs/remoteconfig/index.d.ts +15 -0
- package/dist/cjs/remoteconfig/index.js +116 -0
- package/dist/cjs/remoteconfig/store.d.ts +18 -0
- package/dist/cjs/remoteconfig/store.js +98 -0
- package/dist/cjs/remoteconfig/tz-countries.d.ts +8 -0
- package/dist/cjs/remoteconfig/tz-countries.js +13 -0
- package/dist/cjs/remotelog/gate.d.ts +2 -0
- package/dist/cjs/remotelog/gate.js +28 -0
- package/dist/cjs/remotelog/index.d.ts +8 -0
- package/dist/cjs/remotelog/logger.d.ts +22 -0
- package/dist/cjs/remotelog/logger.js +111 -0
- package/dist/cjs/remotelog/send.d.ts +13 -0
- package/dist/cjs/remotelog/send.js +153 -0
- package/dist/cjs/remotelogger/index.d.ts +12 -0
- package/dist/cjs/remotelogger/index.js +18 -0
- package/dist/cjs/scheduler.d.ts +8 -0
- package/dist/cjs/scheduler.js +78 -0
- package/dist/cjs/types.d.ts +385 -0
- package/dist/cjs/user/index.d.ts +33 -0
- package/dist/cjs/user/index.js +183 -0
- package/dist/esm/auto-init.d.ts +8 -0
- package/dist/esm/auto-init.js +29 -0
- package/dist/esm/core/attempts.d.ts +28 -0
- package/dist/esm/core/attempts.js +106 -0
- package/dist/esm/core/batch.d.ts +32 -0
- package/dist/esm/core/batch.js +109 -0
- package/dist/esm/core/callsite.d.ts +34 -0
- package/dist/esm/core/callsite.js +110 -0
- package/dist/esm/core/consent.d.ts +27 -0
- package/dist/esm/core/consent.js +122 -0
- package/dist/esm/core/constants.d.ts +237 -0
- package/dist/esm/core/constants.js +292 -0
- package/dist/esm/core/db.d.ts +48 -0
- package/dist/esm/core/db.js +453 -0
- package/dist/esm/core/debug.d.ts +22 -0
- package/dist/esm/core/debug.js +81 -0
- package/dist/esm/core/env.d.ts +28 -0
- package/dist/esm/core/env.js +251 -0
- package/dist/esm/core/framework-id.d.ts +1 -0
- package/dist/esm/core/framework-id.js +7 -0
- package/dist/esm/core/guard.d.ts +11 -0
- package/dist/esm/core/guard.js +83 -0
- package/dist/esm/core/hooks.d.ts +39 -0
- package/dist/esm/core/hooks.js +20 -0
- package/dist/esm/core/locks.d.ts +9 -0
- package/dist/esm/core/locks.js +20 -0
- package/dist/esm/core/logger.d.ts +15 -0
- package/dist/esm/core/logger.js +46 -0
- package/dist/esm/core/state.d.ts +70 -0
- package/dist/esm/core/state.js +73 -0
- package/dist/esm/core/storage.d.ts +61 -0
- package/dist/esm/core/storage.js +314 -0
- package/dist/esm/core/stylesheet.d.ts +8 -0
- package/dist/esm/core/stylesheet.js +27 -0
- package/dist/esm/core/transport.d.ts +85 -0
- package/dist/esm/core/transport.js +439 -0
- package/dist/esm/core/utils.d.ts +26 -0
- package/dist/esm/core/utils.js +161 -0
- package/dist/esm/core/validate.d.ts +18 -0
- package/dist/esm/core/validate.js +226 -0
- package/dist/esm/device/appversion.d.ts +15 -0
- package/dist/esm/device/appversion.js +164 -0
- package/dist/esm/device/index.d.ts +34 -0
- package/dist/esm/device/index.js +461 -0
- package/dist/esm/engagement/api-rules.d.ts +49 -0
- package/dist/esm/engagement/api-rules.js +199 -0
- package/dist/esm/engagement/auto-api.d.ts +7 -0
- package/dist/esm/engagement/auto-api.js +198 -0
- package/dist/esm/engagement/auto-screen.d.ts +18 -0
- package/dist/esm/engagement/auto-screen.js +301 -0
- package/dist/esm/engagement/defined-events.d.ts +23 -0
- package/dist/esm/engagement/defined-events.js +55 -0
- package/dist/esm/engagement/send.d.ts +6 -0
- package/dist/esm/engagement/send.js +101 -0
- package/dist/esm/engagement/session.d.ts +51 -0
- package/dist/esm/engagement/session.js +448 -0
- package/dist/esm/engagement/track.d.ts +56 -0
- package/dist/esm/engagement/track.js +365 -0
- package/dist/esm/errors/send.d.ts +6 -0
- package/dist/esm/errors/send.js +70 -0
- package/dist/esm/errors/track.d.ts +4 -0
- package/dist/esm/errors/track.js +163 -0
- package/dist/esm/feedback/controls.d.ts +37 -0
- package/dist/esm/feedback/controls.js +309 -0
- package/dist/esm/feedback/index.d.ts +9 -0
- package/dist/esm/feedback/index.js +51 -0
- package/dist/esm/feedback/logs.d.ts +45 -0
- package/dist/esm/feedback/logs.js +227 -0
- package/dist/esm/feedback/quartz.d.ts +18 -0
- package/dist/esm/feedback/quartz.js +99 -0
- package/dist/esm/feedback/send.d.ts +28 -0
- package/dist/esm/feedback/send.js +198 -0
- package/dist/esm/feedback/styles.d.ts +10 -0
- package/dist/esm/feedback/styles.js +4 -0
- package/dist/esm/feedback/template.d.ts +2 -0
- package/dist/esm/feedback/template.js +159 -0
- package/dist/esm/feedback/ui.d.ts +63 -0
- package/dist/esm/feedback/ui.js +703 -0
- package/dist/esm/heatmap/send.d.ts +13 -0
- package/dist/esm/heatmap/send.js +79 -0
- package/dist/esm/heatmap/track.d.ts +4 -0
- package/dist/esm/heatmap/track.js +445 -0
- package/dist/esm/index.d.ts +22 -0
- package/dist/esm/index.js +150 -0
- package/dist/esm/lifecycle.d.ts +21 -0
- package/dist/esm/lifecycle.js +729 -0
- package/dist/esm/package.json +3 -0
- package/dist/esm/remoteconfig/country.d.ts +4 -0
- package/dist/esm/remoteconfig/country.js +70 -0
- package/dist/esm/remoteconfig/custom.d.ts +10 -0
- package/dist/esm/remoteconfig/custom.js +54 -0
- package/dist/esm/remoteconfig/evaluate.d.ts +24 -0
- package/dist/esm/remoteconfig/evaluate.js +179 -0
- package/dist/esm/remoteconfig/fetch.d.ts +24 -0
- package/dist/esm/remoteconfig/fetch.js +196 -0
- package/dist/esm/remoteconfig/index.d.ts +15 -0
- package/dist/esm/remoteconfig/index.js +111 -0
- package/dist/esm/remoteconfig/store.d.ts +18 -0
- package/dist/esm/remoteconfig/store.js +91 -0
- package/dist/esm/remoteconfig/tz-countries.d.ts +8 -0
- package/dist/esm/remoteconfig/tz-countries.js +11 -0
- package/dist/esm/remotelog/gate.d.ts +2 -0
- package/dist/esm/remotelog/gate.js +26 -0
- package/dist/esm/remotelog/index.d.ts +8 -0
- package/dist/esm/remotelog/logger.d.ts +22 -0
- package/dist/esm/remotelog/logger.js +108 -0
- package/dist/esm/remotelog/send.d.ts +13 -0
- package/dist/esm/remotelog/send.js +149 -0
- package/dist/esm/remotelogger/index.d.ts +12 -0
- package/dist/esm/remotelogger/index.js +13 -0
- package/dist/esm/scheduler.d.ts +8 -0
- package/dist/esm/scheduler.js +74 -0
- package/dist/esm/types.d.ts +385 -0
- package/dist/esm/user/index.d.ts +33 -0
- package/dist/esm/user/index.js +176 -0
- package/dist/feedback.esm.min.js +3 -0
- package/dist/feedback.esm.min.js.map +1 -0
- package/package.json +122 -7
- package/src/appticssdk.js +0 -1
|
@@ -0,0 +1,703 @@
|
|
|
1
|
+
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.1 | (c) Zoho Corporation Private Limited | see LICENSE */
|
|
2
|
+
import { AP_TRACKING_STATUS } from '../core/constants.js';
|
|
3
|
+
import { _ZAP } from '../core/state.js';
|
|
4
|
+
import { Console, warnAlways } from '../core/logger.js';
|
|
5
|
+
import { isEmpty, isEmptyObj } from '../core/utils.js';
|
|
6
|
+
import { opt_in_status } from '../core/consent.js';
|
|
7
|
+
import { timerHtml, feedbackUIHtml } from './template.js';
|
|
8
|
+
import { sendFeedbackData } from './send.js';
|
|
9
|
+
import { chooseUserForSend, userChoice, wireDrag, wireUserPicker, wireLogControls, logChoice } from './controls.js';
|
|
10
|
+
import { injectStylesheet } from '../core/stylesheet.js';
|
|
11
|
+
import { recordingDomain, loadQuartz, quartzScriptSource } from './quartz.js';
|
|
12
|
+
import css from './styles.js';
|
|
13
|
+
|
|
14
|
+
// Quartz is an optional page-level SDK. Without it every call is a no-op (fix #5).
|
|
15
|
+
const QUARTZ_NOOP = {
|
|
16
|
+
startRecording() { },
|
|
17
|
+
previewVideo() { },
|
|
18
|
+
editVideo() { },
|
|
19
|
+
clearFeedback() { },
|
|
20
|
+
endRecording() { },
|
|
21
|
+
setVideoContent() { },
|
|
22
|
+
submitRecording() { return Promise.resolve({}); }
|
|
23
|
+
};
|
|
24
|
+
const QUARTZ_CONSTANTS_NOOP = { ErrorType: {}, FeedbackState: {} };
|
|
25
|
+
function hasQuartz() {
|
|
26
|
+
return typeof Quartz !== 'undefined' && !!Quartz; // no i18n
|
|
27
|
+
}
|
|
28
|
+
function quartz() {
|
|
29
|
+
return typeof Quartz !== 'undefined' && Quartz ? Quartz : QUARTZ_NOOP; // no i18n
|
|
30
|
+
}
|
|
31
|
+
function quartzConstants() {
|
|
32
|
+
return typeof QuartzConstants !== 'undefined' && QuartzConstants ? QuartzConstants : QUARTZ_CONSTANTS_NOOP; // no i18n
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Sends feedback and reflects the outcome in the modal (if open). Resolves with the outcome, or
|
|
36
|
+
* null when nothing was attempted (offline, or Apptics is disabled for this client).
|
|
37
|
+
*/
|
|
38
|
+
function submitFeedback(dataobj) {
|
|
39
|
+
return sendFeedbackData(dataobj).then(function (result) {
|
|
40
|
+
if (!result) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
feedback.handleStatusmsg(result.ok, result.message);
|
|
44
|
+
if (result.ok) {
|
|
45
|
+
setTimeout(() => {
|
|
46
|
+
feedback.close();
|
|
47
|
+
}, 2000);
|
|
48
|
+
}
|
|
49
|
+
return result;
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
/** Whether a value is an options object: a plain object, not a type number or a message string. */
|
|
53
|
+
function isOptions(value) {
|
|
54
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value); // no i18n
|
|
55
|
+
}
|
|
56
|
+
/** Said once: the old one-argument-after-another form of feedbackFormOpen. */
|
|
57
|
+
var saidOpenForm = false;
|
|
58
|
+
function sendFeedback(options) {
|
|
59
|
+
if (options !== undefined && !isOptions(options)) {
|
|
60
|
+
// the old form, sendFeedback(type, message, ...): sending it as it stands would send an
|
|
61
|
+
// empty report, so it is refused, and says what to pass instead
|
|
62
|
+
return Promise.reject(new Error('AppticsFeedback.sendFeedback: takes one options object, as sendFeedback({ type, message, attachments, logFiles, includeDiagnostics, includeLog, isAnonymous })')); // no i18n
|
|
63
|
+
}
|
|
64
|
+
const o = options || {};
|
|
65
|
+
const dataobj = {
|
|
66
|
+
type: !isEmpty(o.type) ? o.type : 1,
|
|
67
|
+
message: o.message || '', // no i18n
|
|
68
|
+
attachment: o.attachments || null,
|
|
69
|
+
logsFiles: o.logFiles || null,
|
|
70
|
+
// both flags stay as given: unset lets send.ts decide from what is actually stored
|
|
71
|
+
includeDyninfo: o.includeDiagnostics,
|
|
72
|
+
includeLog: o.includeLog,
|
|
73
|
+
quartzid: '' // no i18n
|
|
74
|
+
};
|
|
75
|
+
// from the active user, or from nobody with isAnonymous; the form's own choice is put back after
|
|
76
|
+
var restoreUser = chooseUserForSend(o.isAnonymous === true);
|
|
77
|
+
// an app awaiting this must be able to tell its user the feedback did not go: resolving on a
|
|
78
|
+
// failure would have it say "thanks, sent" for a report the server never received
|
|
79
|
+
return submitFeedback(dataobj).then(function (result) {
|
|
80
|
+
restoreUser();
|
|
81
|
+
return result;
|
|
82
|
+
}, function (err) {
|
|
83
|
+
restoreUser();
|
|
84
|
+
throw err;
|
|
85
|
+
}).then(function (result) {
|
|
86
|
+
if (!result) {
|
|
87
|
+
throw new Error('AppticsFeedback.sendFeedback: not sent, the browser is offline or Apptics is disabled for this client'); // no i18n
|
|
88
|
+
}
|
|
89
|
+
if (!result.ok) {
|
|
90
|
+
throw new Error('AppticsFeedback.sendFeedback: not sent, ' + result.message); // no i18n
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* The request both submit buttons send. A recording only adds its quartzid; everything else — the
|
|
96
|
+
* chosen type, the attachments, the log and diagnostics toggles — is read the same way, so one
|
|
97
|
+
* builder keeps the recording path from quietly falling behind the plain one.
|
|
98
|
+
*/
|
|
99
|
+
function buildFeedbackRequest(modal, message, quartzid) {
|
|
100
|
+
var seltype = modal.querySelector('.zasdk-selected').getAttribute('data-type'); // no i18n
|
|
101
|
+
var fileInput = modal.querySelector("#zasdkFileInput"); // no i18n
|
|
102
|
+
var files = fileInput.files;
|
|
103
|
+
return {
|
|
104
|
+
type: !isEmpty(seltype) ? feedback.typeObj[seltype] : 1,
|
|
105
|
+
message: message,
|
|
106
|
+
attachment: (files && files.length > 0) ? files : (feedback._opts.attachments || null),
|
|
107
|
+
logsFiles: feedback._opts.logFiles || null,
|
|
108
|
+
includeLog: logChoice.includeLog,
|
|
109
|
+
includeDyninfo: logChoice.includeDyninfo,
|
|
110
|
+
quartzid: quartzid
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Opens the form, optionally pre-filled. `isAnonymous` starts the user picker on Anonymous even
|
|
115
|
+
* when a user is registered; the include flags start their toggles on.
|
|
116
|
+
*/
|
|
117
|
+
/**
|
|
118
|
+
* A DataTransfer to hold the chosen attachments, or null where one cannot be made.
|
|
119
|
+
*
|
|
120
|
+
* It is the only way to build a FileList, which is what an <input type="file"> will accept back,
|
|
121
|
+
* so the attachment list is kept in one. The constructor is newer than the rest of the modal -
|
|
122
|
+
* Safari only gained it in 14.1 - and a plain `new DataTransfer()` throws on anything older,
|
|
123
|
+
* which took the whole form down with it on open. Feedback without attachments is worth far more
|
|
124
|
+
* than no feedback at all, so the form opens either way and the file input is left alone.
|
|
125
|
+
*/
|
|
126
|
+
function newTransfer() {
|
|
127
|
+
try {
|
|
128
|
+
return typeof DataTransfer === 'function' ? new DataTransfer() : null; // no i18n
|
|
129
|
+
}
|
|
130
|
+
catch (err) {
|
|
131
|
+
Console.warn('apptics.feedback: this browser cannot build a file list, attachments are off'); // no i18n
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
function feedbackFormOpen(options) {
|
|
136
|
+
if (options !== undefined && !isOptions(options) && !saidOpenForm) {
|
|
137
|
+
// the old form, feedbackFormOpen(type, message, ...): the form still opens, with its defaults
|
|
138
|
+
saidOpenForm = true;
|
|
139
|
+
warnAlways('AppticsFeedback.feedbackFormOpen: takes one options object, as feedbackFormOpen({ type, message, includeLog, includeDiagnostics, attachments, logFiles, isAnonymous }); opening it with its defaults'); // no i18n
|
|
140
|
+
}
|
|
141
|
+
const o = isOptions(options) ? options : {};
|
|
142
|
+
const opts = {
|
|
143
|
+
includeLog: o.includeLog === true,
|
|
144
|
+
includeDyninfo: o.includeDiagnostics === true,
|
|
145
|
+
isAnonymous: o.isAnonymous === true,
|
|
146
|
+
};
|
|
147
|
+
if (typeof o.type === 'number')
|
|
148
|
+
opts.type = o.type; // no i18n
|
|
149
|
+
if (typeof o.message === 'string' && o.message.length > 0)
|
|
150
|
+
opts.message = o.message; // no i18n
|
|
151
|
+
if (o.attachments && o.attachments.length > 0)
|
|
152
|
+
opts.attachments = o.attachments;
|
|
153
|
+
if (o.logFiles && o.logFiles.length > 0)
|
|
154
|
+
opts.logFiles = o.logFiles;
|
|
155
|
+
// the form brings its own styles, so an app imports or links no css
|
|
156
|
+
injectStylesheet(css);
|
|
157
|
+
feedback.init(opts);
|
|
158
|
+
}
|
|
159
|
+
/** The themes the form has: 'auto' follows the visitor's system setting. */
|
|
160
|
+
const FEEDBACK_THEMES = ['light', 'dark', 'auto']; // no i18n
|
|
161
|
+
/**
|
|
162
|
+
* Sets the form's theme, for a page with its own light and dark modes: 'auto' follows the
|
|
163
|
+
* operating system, which is not what such a page shows. Restyles a form or recording timer that
|
|
164
|
+
* is open now, and every one opened after; Apptics.init()'s feedback_theme only sets the first.
|
|
165
|
+
*/
|
|
166
|
+
function setFeedbackTheme(theme) {
|
|
167
|
+
if (FEEDBACK_THEMES.indexOf(theme) === -1) {
|
|
168
|
+
Console.warn('AppticsFeedback.setFeedbackTheme: takes \'light\', \'dark\' or \'auto\', not ' + JSON.stringify(theme)); // no i18n
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
_ZAP._config.feedback_theme = theme;
|
|
172
|
+
if (typeof document === 'undefined') { // no i18n
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
['zasdkFeedbackModal', 'zasdkrectimer'].forEach(function (id) {
|
|
176
|
+
var open = document.getElementById(id);
|
|
177
|
+
if (open) {
|
|
178
|
+
open.setAttribute('data-zasdk-theme', theme); // no i18n
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
function feedbackFormClose() {
|
|
183
|
+
feedback.close();
|
|
184
|
+
}
|
|
185
|
+
const feedback = {
|
|
186
|
+
feedbackUIHtml: feedbackUIHtml,
|
|
187
|
+
timerHtml: timerHtml,
|
|
188
|
+
uploadFileAry: [],
|
|
189
|
+
fileInput: null,
|
|
190
|
+
selectedFiles: null,
|
|
191
|
+
isScaled: false,
|
|
192
|
+
isHidden: false,
|
|
193
|
+
timerDisplay: null,
|
|
194
|
+
recordButton: null,
|
|
195
|
+
TOTAL_TIME: 5 * 60,
|
|
196
|
+
remainingTime: 5 * 60,
|
|
197
|
+
isRecording: false,
|
|
198
|
+
timerInterval: null,
|
|
199
|
+
uploadTotalSize: 0,
|
|
200
|
+
MaxFileSize: 10 * 1024 * 1024,
|
|
201
|
+
_opts: {},
|
|
202
|
+
typeObj: { 'bug': 0, 'feedback': 1, 'help': 2, 'report': 3 }, // no i18n
|
|
203
|
+
init: function (opts) {
|
|
204
|
+
feedback._opts = opts || {};
|
|
205
|
+
var modal = document.getElementById("zasdkFeedbackModal"); // no i18n
|
|
206
|
+
if (!isEmpty(modal)) {
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
const tempContainer = document.createElement('div'); // no i18n
|
|
210
|
+
feedback.uploadFileAry = [];
|
|
211
|
+
tempContainer.id = 'zasdkFeedbackModal'; // no i18n
|
|
212
|
+
tempContainer.className = 'zasdk-modal'; // no i18n
|
|
213
|
+
tempContainer.setAttribute('data-zasdk-theme', _ZAP._config.feedback_theme); // no i18n
|
|
214
|
+
tempContainer.innerHTML = feedback.feedbackUIHtml;
|
|
215
|
+
document.body.appendChild(tempContainer);
|
|
216
|
+
var modal = document.getElementById("zasdkFeedbackModal"); // no i18n
|
|
217
|
+
// screen recording as the console set it up for this app (getsdkconfig's quartz block)
|
|
218
|
+
var qz = _ZAP._quartz;
|
|
219
|
+
var qz_ws_key = qz.workspace;
|
|
220
|
+
var qz_dp_key = qz.department;
|
|
221
|
+
var qz_dc_key = recordingDomain(qz.domain);
|
|
222
|
+
var closeBtn = modal.querySelector("#appticsfeedbackclose"); // no i18n
|
|
223
|
+
var feedbackTypes = modal.querySelectorAll(".zasdk-feedback-type"); // no i18n
|
|
224
|
+
feedback.fileInput = modal.querySelector("#zasdkFileInput"); // no i18n
|
|
225
|
+
var quartzrecbtn = modal.querySelector("#zasdkqz_rec_button"); // no i18n
|
|
226
|
+
var submitbtn = modal.querySelector("#zasdk_submit_btn"); // no i18n
|
|
227
|
+
var submitbtncnt = modal.querySelector("#zasdk_submit_btnsubcnt"); // no i18n
|
|
228
|
+
const checkbox = modal.querySelector('#zasdkqz-consent'); // no i18n
|
|
229
|
+
const content = modal.querySelector('#zasdkqz-content'); // no i18n
|
|
230
|
+
const preivewButton = modal.querySelector('#preivewPlayButton'); // no i18n
|
|
231
|
+
const videoEditButton = modal.querySelector('#videoEditButton'); // no i18n
|
|
232
|
+
const deleteVideoButton = modal.querySelector('#videoDelButton'); // no i18n
|
|
233
|
+
const feedbackbox = modal.querySelector('#zasdkFeedbackconatiner'); // no i18n
|
|
234
|
+
const descriptionField = modal.querySelector('#zasdkDescription'); // no i18n
|
|
235
|
+
wireDrag(feedbackbox);
|
|
236
|
+
wireUserPicker(modal, feedback._opts.isAnonymous === true);
|
|
237
|
+
wireLogControls(modal, feedback._opts);
|
|
238
|
+
feedback.isScaled = false;
|
|
239
|
+
feedback.isHidden = false;
|
|
240
|
+
document.removeEventListener('quartz-feedback-state', feedback.bindQuartzEvent); // no i18n
|
|
241
|
+
document.addEventListener('quartz-feedback-state', feedback.bindQuartzEvent); // no i18n
|
|
242
|
+
document.removeEventListener('quartz-feedback-error', feedback.handleErrorQuartzEvent); // no i18n
|
|
243
|
+
document.addEventListener('quartz-feedback-error', feedback.handleErrorQuartzEvent); // no i18n
|
|
244
|
+
// the consent checkbox and its label only when a recording can actually be made: recording
|
|
245
|
+
// on for the app, its workspace, department and domain, and Quartz on the page. A checkbox
|
|
246
|
+
// that leads only to "unavailable" is not offered. Quartz is loaded here, the first time
|
|
247
|
+
// the form needs it, and the checkbox shows once it has arrived
|
|
248
|
+
var recordingConfigured = qz.enabled && !isEmpty(qz_dc_key) && !isEmpty(qz_ws_key) && !isEmpty(qz_dp_key);
|
|
249
|
+
var recordingBlock = modal.querySelector("#zasdkreccheckcnt"); // no i18n
|
|
250
|
+
recordingBlock.style.display = recordingConfigured && hasQuartz() ? "block" : "none"; // no i18n
|
|
251
|
+
if (recordingConfigured && !hasQuartz()) {
|
|
252
|
+
// quartz_cdn_url, or Zoho's CDN for this serviceUrl when the answer carries none
|
|
253
|
+
loadQuartz(quartzScriptSource(qz.cdnUrl, String(_ZAP._defaultoptions.serviceUrl || ''))).then(function (loaded) {
|
|
254
|
+
if (loaded && recordingBlock.isConnected) {
|
|
255
|
+
recordingBlock.style.display = "block"; // no i18n
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
feedbackbox.addEventListener('transitionend', function (event) {
|
|
260
|
+
if (event.propertyName === 'transform' && feedback.isScaled) { // no i18n
|
|
261
|
+
feedback.toggleWindowAnimation();
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
quartzrecbtn.onclick = function (e) {
|
|
265
|
+
e.stopImmediatePropagation();
|
|
266
|
+
let recordingDetails = {
|
|
267
|
+
workspaceLinkName: qz_ws_key,
|
|
268
|
+
departmentLinkName: qz_dp_key,
|
|
269
|
+
recordingDomain: qz_dc_key
|
|
270
|
+
};
|
|
271
|
+
feedback.handleStatusmsg();
|
|
272
|
+
// 1.x pressed on regardless and the button did nothing anyone could explain; the rest
|
|
273
|
+
// of the form still works, so say what is missing and leave it usable
|
|
274
|
+
if (!hasQuartz()) {
|
|
275
|
+
feedback.handleStatusmsg(false, 'Screen recording is unavailable on this page.'); // no i18n
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
if (isEmpty(qz_ws_key) || isEmpty(qz_dp_key) || isEmpty(qz_dc_key)) {
|
|
279
|
+
feedback.handleStatusmsg(false, 'Screen recording is not configured for this app.'); // no i18n
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
quartz().startRecording(recordingDetails);
|
|
283
|
+
};
|
|
284
|
+
closeBtn.onclick = function () {
|
|
285
|
+
feedback.close();
|
|
286
|
+
};
|
|
287
|
+
// keyboard: Enter or Space on the close control, and Escape anywhere, close the form
|
|
288
|
+
closeBtn.onkeydown = function (e) {
|
|
289
|
+
if (e.key === 'Enter' || e.key === ' ') { // no i18n
|
|
290
|
+
e.preventDefault();
|
|
291
|
+
feedback.close();
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
document.addEventListener('keydown', feedback.onEscape); // no i18n
|
|
295
|
+
preivewButton.addEventListener('click', function () {
|
|
296
|
+
quartz().previewVideo();
|
|
297
|
+
});
|
|
298
|
+
videoEditButton.addEventListener('click', function () {
|
|
299
|
+
quartz().editVideo({ quartzBranding: false });
|
|
300
|
+
});
|
|
301
|
+
deleteVideoButton.addEventListener('click', function () {
|
|
302
|
+
var modal = document.getElementById("zasdkFeedbackModal"); // no i18n
|
|
303
|
+
quartz().clearFeedback();
|
|
304
|
+
checkbox.checked = false;
|
|
305
|
+
content.classList.remove('zasdkqz-show'); // no i18n
|
|
306
|
+
modal.querySelector("#editRecordedVideo").style.display = "none"; // no i18n
|
|
307
|
+
modal.querySelector("#zasdkreccheckcnt").style.display = "block"; // no i18n
|
|
308
|
+
feedback.validateFeedbackField();
|
|
309
|
+
});
|
|
310
|
+
submitbtn.addEventListener('click', async () => {
|
|
311
|
+
// Disable button and show loader
|
|
312
|
+
feedback.validateFeedbackField();
|
|
313
|
+
submitbtn.disabled = true;
|
|
314
|
+
submitbtncnt.classList.add('zasdkbtndisabled'); // no i18n
|
|
315
|
+
submitbtn.value = '';
|
|
316
|
+
try {
|
|
317
|
+
let videoElement = modal.querySelector('#previewVideoElemId'), // no i18n
|
|
318
|
+
videoVisible = videoElement && videoElement.offsetParent !== null;
|
|
319
|
+
if (videoVisible) {
|
|
320
|
+
feedback.submitRecording();
|
|
321
|
+
}
|
|
322
|
+
else {
|
|
323
|
+
let message = modal.querySelector('#zasdkDescription').value.trim(); // no i18n
|
|
324
|
+
if (message.length > 0) {
|
|
325
|
+
submitFeedback(buildFeedbackRequest(modal, message, '')); // no i18n
|
|
326
|
+
}
|
|
327
|
+
else {
|
|
328
|
+
feedback.handleStatusmsg(false, 'Please provide more details for us to help you better.'); // no i18n
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
catch (error) {
|
|
333
|
+
Console.error("sendFeedbackData : " + error); // no i18n
|
|
334
|
+
}
|
|
335
|
+
});
|
|
336
|
+
checkbox.addEventListener('change', function () {
|
|
337
|
+
if (this.checked) {
|
|
338
|
+
content.classList.add('zasdkqz-show'); // no i18n
|
|
339
|
+
}
|
|
340
|
+
else {
|
|
341
|
+
content.classList.remove('zasdkqz-show'); // no i18n
|
|
342
|
+
}
|
|
343
|
+
});
|
|
344
|
+
descriptionField.addEventListener('input', feedback.validateFeedbackField); // no i18n
|
|
345
|
+
// keyboard and screen-reader users land in the form, not behind it
|
|
346
|
+
try {
|
|
347
|
+
descriptionField.focus({ preventScroll: true });
|
|
348
|
+
}
|
|
349
|
+
catch (err) { /* focus is a courtesy */ }
|
|
350
|
+
feedbackTypes.forEach(function (type) {
|
|
351
|
+
type.addEventListener("click", function () {
|
|
352
|
+
feedbackTypes.forEach(function (t) {
|
|
353
|
+
t.classList.remove("zasdk-selected"); // no i18n
|
|
354
|
+
});
|
|
355
|
+
this.classList.add("zasdk-selected"); // no i18n
|
|
356
|
+
});
|
|
357
|
+
});
|
|
358
|
+
// what feedbackFormOpen() was given: select that type, fill the description
|
|
359
|
+
if (typeof feedback._opts.type === 'number') { // no i18n
|
|
360
|
+
var wanted = Object.keys(feedback.typeObj).filter(function (name) {
|
|
361
|
+
return feedback.typeObj[name] === feedback._opts.type;
|
|
362
|
+
})[0];
|
|
363
|
+
var wantedTab = wanted ? modal.querySelector('.zasdk-feedback-type[data-type="' + wanted + '"]') : null; // no i18n
|
|
364
|
+
if (wantedTab) {
|
|
365
|
+
feedbackTypes.forEach(function (t) { t.classList.remove("zasdk-selected"); }); // no i18n
|
|
366
|
+
wantedTab.classList.add("zasdk-selected"); // no i18n
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
if (!isEmpty(feedback._opts.message)) {
|
|
370
|
+
descriptionField.value = String(feedback._opts.message);
|
|
371
|
+
feedback.updateLeftCount();
|
|
372
|
+
feedback.validateFeedbackField();
|
|
373
|
+
}
|
|
374
|
+
feedback.selectedFiles = newTransfer();
|
|
375
|
+
feedback.fileInput.onchange = function () {
|
|
376
|
+
const self = this;
|
|
377
|
+
var chosen = feedback.selectedFiles;
|
|
378
|
+
if (chosen === null) {
|
|
379
|
+
return; // no file list on this browser; see newTransfer()
|
|
380
|
+
}
|
|
381
|
+
if (chosen.files.length + self.files.length > 5) {
|
|
382
|
+
feedback.handleStatusmsg(false, 'Maximum 5 attachments is allowed'); // no i18n
|
|
383
|
+
feedback.fileInput.files = chosen.files;
|
|
384
|
+
self.files = chosen.files;
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
if (self.files.length > 0) {
|
|
388
|
+
let filesize = 0;
|
|
389
|
+
for (let file of self.files) {
|
|
390
|
+
filesize = filesize + file.size;
|
|
391
|
+
if (filesize > 10485760) {
|
|
392
|
+
feedback.handleStatusmsg(false, 'Maximum limit reached. The total file size cannot exceed 10MB.'); // no i18n
|
|
393
|
+
self.files = chosen.files;
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
else {
|
|
397
|
+
chosen.items.add(file); // Add new files
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
self.files = chosen.files; // Update input with all files
|
|
401
|
+
feedback.fileAttachment(); // Update UI
|
|
402
|
+
}
|
|
403
|
+
else if (chosen.files.length > 0) {
|
|
404
|
+
// Restore previous files if the user cancels
|
|
405
|
+
self.files = chosen.files;
|
|
406
|
+
}
|
|
407
|
+
};
|
|
408
|
+
feedback.handleStatusmsg();
|
|
409
|
+
feedback.updateLeftCount();
|
|
410
|
+
},
|
|
411
|
+
updateLeftCount: function () {
|
|
412
|
+
document.querySelectorAll("[reqname]").forEach(function (elem) {
|
|
413
|
+
let val = elem.value;
|
|
414
|
+
let maxLength = parseInt(elem.getAttribute("maxlength") || '', 10); // no i18n
|
|
415
|
+
let counterElem = elem.parentElement ? elem.parentElement.querySelector(".zaleftcharcnt") : null; // no i18n
|
|
416
|
+
if (counterElem) {
|
|
417
|
+
counterElem.textContent = String(maxLength - val.length); // Update remaining count
|
|
418
|
+
var unit = counterElem.parentElement ? counterElem.parentElement.querySelector(".zaleftchartxt") : null; // no i18n
|
|
419
|
+
if (unit) {
|
|
420
|
+
unit.textContent = maxLength - val.length === 1 ? 'character left' : 'characters left'; // no i18n
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
});
|
|
424
|
+
},
|
|
425
|
+
handleStatusmsg: function (isSuccess, stsmsg) {
|
|
426
|
+
var modal = document.getElementById("zasdkFeedbackModal"); // no i18n
|
|
427
|
+
if (isEmpty(modal)) {
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
var stscnt = modal.querySelector("#zasdksubmitstatus"); // no i18n
|
|
431
|
+
var submitbtn = modal.querySelector("#zasdk_submit_btn"); // no i18n
|
|
432
|
+
var submitbtncnt = modal.querySelector("#zasdk_submit_btnsubcnt"); // no i18n
|
|
433
|
+
var successmsgclass = ''; // no i18n
|
|
434
|
+
var successmsg = ''; // no i18n
|
|
435
|
+
stscnt.style.display = "none"; // no i18n
|
|
436
|
+
if (!isEmpty(isSuccess)) {
|
|
437
|
+
successmsgclass = isSuccess ? 'zasdkstssucs' : 'zasdkstserror'; // no i18n
|
|
438
|
+
successmsg = !isEmpty(stsmsg) ? stsmsg : ''; // no i18n
|
|
439
|
+
stscnt.textContent = successmsg;
|
|
440
|
+
stscnt.classList.add(successmsgclass);
|
|
441
|
+
stscnt.style.display = "block"; // no i18n
|
|
442
|
+
submitbtncnt.classList.remove('zasdkbtndisabled'); // no i18n
|
|
443
|
+
submitbtn.disabled = false;
|
|
444
|
+
submitbtn.value = 'Send'; // no i18n
|
|
445
|
+
}
|
|
446
|
+
},
|
|
447
|
+
handleErrorQuartzEvent: function (event) {
|
|
448
|
+
let errorType = event.detail.error.name;
|
|
449
|
+
let errorMessage = event.detail.error.message;
|
|
450
|
+
if (errorType == quartzConstants().ErrorType.RECORDING_INTERRUPTED || errorType == quartzConstants().ErrorType.FEEDBACK_TIMEOUT) {
|
|
451
|
+
feedback.stopRecording();
|
|
452
|
+
}
|
|
453
|
+
feedback.handleStatusmsg(false, errorMessage);
|
|
454
|
+
},
|
|
455
|
+
bindQuartzEvent: function (event) {
|
|
456
|
+
let feedbackState = event.detail.feedbackState;
|
|
457
|
+
if (feedbackState == quartzConstants().FeedbackState.RECORDING_IN_PROGRESS) {
|
|
458
|
+
var feedbacktimer = document.getElementById("zasdkrectimer"); // no i18n
|
|
459
|
+
if (!isEmpty(feedbacktimer)) {
|
|
460
|
+
feedbacktimer.remove();
|
|
461
|
+
}
|
|
462
|
+
const tempDiv = document.createElement("div"); // no i18n
|
|
463
|
+
tempDiv.innerHTML = feedback.timerHtml;
|
|
464
|
+
const node = tempDiv.firstElementChild;
|
|
465
|
+
node.setAttribute('data-zasdk-theme', _ZAP._config.feedback_theme); // no i18n
|
|
466
|
+
document.body.appendChild(node);
|
|
467
|
+
var feedbacktimer = document.getElementById("zasdkrectimer"); // no i18n
|
|
468
|
+
feedback.timerDisplay = feedbacktimer.querySelector('#zasdkTimerDisplay'); // no i18n
|
|
469
|
+
feedback.recordButton = feedbacktimer;
|
|
470
|
+
feedback.TOTAL_TIME = 5 * 60; // 5 minutes in seconds
|
|
471
|
+
feedback.remainingTime = feedback.TOTAL_TIME;
|
|
472
|
+
feedback.isRecording = true;
|
|
473
|
+
feedback.recordButton.addEventListener('click', feedback.toggleRecording); // no i18n
|
|
474
|
+
feedback.startRecording();
|
|
475
|
+
feedback.toggleWindowAnimation();
|
|
476
|
+
}
|
|
477
|
+
},
|
|
478
|
+
onEscape: function (e) {
|
|
479
|
+
if (e.key === 'Escape') { // no i18n
|
|
480
|
+
feedback.close();
|
|
481
|
+
}
|
|
482
|
+
},
|
|
483
|
+
close: function () {
|
|
484
|
+
var modal = document.getElementById("zasdkFeedbackModal"); // no i18n
|
|
485
|
+
document.removeEventListener('keydown', feedback.onEscape); // no i18n
|
|
486
|
+
document.removeEventListener('quartz-feedback-state', feedback.bindQuartzEvent); // no i18n
|
|
487
|
+
quartz().clearFeedback();
|
|
488
|
+
if (!isEmpty(modal)) {
|
|
489
|
+
modal.remove();
|
|
490
|
+
}
|
|
491
|
+
},
|
|
492
|
+
toggleWindowAnimation: function () {
|
|
493
|
+
var modal = document.getElementById("zasdkFeedbackModal"); // no i18n
|
|
494
|
+
const box = modal.querySelector('#zasdkFeedbackconatiner'); // no i18n
|
|
495
|
+
if (feedback.isHidden) {
|
|
496
|
+
modal.style.display = 'block'; // no i18n
|
|
497
|
+
setTimeout(() => {
|
|
498
|
+
box.style.transform = 'scale(1)'; // no i18n
|
|
499
|
+
feedback.validateFeedbackField();
|
|
500
|
+
}, 10);
|
|
501
|
+
feedback.isHidden = false;
|
|
502
|
+
feedback.isScaled = false;
|
|
503
|
+
}
|
|
504
|
+
else if (feedback.isScaled) {
|
|
505
|
+
modal.style.display = 'none'; // no i18n
|
|
506
|
+
feedback.isHidden = true;
|
|
507
|
+
}
|
|
508
|
+
else {
|
|
509
|
+
box.style.transform = 'scale(0)'; // no i18n
|
|
510
|
+
feedback.isScaled = true;
|
|
511
|
+
}
|
|
512
|
+
},
|
|
513
|
+
fileAttachment: function () {
|
|
514
|
+
var uploadedFiles = feedback.fileInput.files;
|
|
515
|
+
feedback.uploadTotalSize = 0;
|
|
516
|
+
feedback.MaxFileSize = 10 * 1024 * 1024; //10 MB
|
|
517
|
+
var modal = document.getElementById("zasdkFeedbackModal"); // no i18n
|
|
518
|
+
var sizeInfo = modal.querySelector("#zasdkfilesizeinfo"); // no i18n
|
|
519
|
+
var fileUl = modal.querySelector("#zasakfileList"); // no i18n
|
|
520
|
+
sizeInfo.textContent = ""; // no i18n
|
|
521
|
+
if ((uploadedFiles.length) > 5) {
|
|
522
|
+
feedback.fileLimitExceeded(false, true);
|
|
523
|
+
feedback.validateFeedbackField();
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
fileUl.innerHTML = ''; // no i18n
|
|
527
|
+
if (uploadedFiles.length > 0) {
|
|
528
|
+
var totalSize = 0;
|
|
529
|
+
for (var i = 0; i < uploadedFiles.length; i++) {
|
|
530
|
+
var fileName = uploadedFiles[i].name;
|
|
531
|
+
var fName = fileName.substring(0, fileName.lastIndexOf('.')); // no i18n
|
|
532
|
+
var extension = fileName.substring(fileName.lastIndexOf('.') + 1); // no i18n
|
|
533
|
+
var bytes = uploadedFiles[i].size;
|
|
534
|
+
totalSize += bytes;
|
|
535
|
+
var fileLi = feedback.elementCreator("li"); // no i18n
|
|
536
|
+
fileLi.title = fileName;
|
|
537
|
+
var span1 = feedback.elementCreator("span"); // no i18n
|
|
538
|
+
span1.className = "zaskFdBckFlName"; // no i18n
|
|
539
|
+
span1.innerText = (fName.length > 10) ? fName.slice(0, 3) + "..." + fName.slice(-3) : fName; // no i18n
|
|
540
|
+
var span2 = feedback.elementCreator("span"); // no i18n
|
|
541
|
+
span2.innerText = "." + extension; // no i18n
|
|
542
|
+
var span3 = feedback.elementCreator("span"); // no i18n
|
|
543
|
+
span3.className = "zasdkFdBckFlCls"; // no i18n
|
|
544
|
+
span3.setAttribute("data-name", fileName); // no i18n
|
|
545
|
+
span3.innerHTML = "×"; // no i18N
|
|
546
|
+
fileLi.appendChild(span1);
|
|
547
|
+
fileLi.appendChild(span2);
|
|
548
|
+
fileLi.appendChild(span3);
|
|
549
|
+
fileUl.appendChild(fileLi);
|
|
550
|
+
}
|
|
551
|
+
var limitReached = totalSize > feedback.MaxFileSize;
|
|
552
|
+
feedback.fileLimitExceeded(limitReached);
|
|
553
|
+
sizeInfo.textContent = `${feedback.formatFileSize(totalSize)} Used / 10 MB`; // no i18n
|
|
554
|
+
const clearFile = modal.querySelectorAll('.zasdkFdBckFlCls'); // no i18n
|
|
555
|
+
Array.from(clearFile).forEach(function (element) {
|
|
556
|
+
element.addEventListener('click', function () {
|
|
557
|
+
feedback.handleStatusmsg();
|
|
558
|
+
feedback.removeFileFromList(this);
|
|
559
|
+
feedback.fileAttachment();
|
|
560
|
+
});
|
|
561
|
+
});
|
|
562
|
+
}
|
|
563
|
+
feedback.validateFeedbackField();
|
|
564
|
+
},
|
|
565
|
+
elementCreator: function (elType) {
|
|
566
|
+
return document.createElement(elType);
|
|
567
|
+
},
|
|
568
|
+
fileLimitExceeded: function (limitReached, filesLengthReached) {
|
|
569
|
+
var infoMsg = "";
|
|
570
|
+
if (filesLengthReached || limitReached) {
|
|
571
|
+
infoMsg = filesLengthReached ? 'Maximum 5 attachments is allowed' : 'Maximum limit reached. The total file size cannot exceed 10MB.'; // no i18n
|
|
572
|
+
}
|
|
573
|
+
feedback.handleStatusmsg(false, infoMsg);
|
|
574
|
+
},
|
|
575
|
+
removeFileFromList: function (elem) {
|
|
576
|
+
elem.getAttribute('data-name'); // no i18n
|
|
577
|
+
const listItem = elem.parentElement; // the <li> holding this file
|
|
578
|
+
// Find the index of the element in the file preview list
|
|
579
|
+
const indexToRemove = Array.from(listItem.parentElement.children).indexOf(listItem);
|
|
580
|
+
listItem.remove(); // Remove the file preview
|
|
581
|
+
let newFiles = newTransfer();
|
|
582
|
+
feedback.selectedFiles = newTransfer();
|
|
583
|
+
if (newFiles === null || feedback.selectedFiles === null) {
|
|
584
|
+
return; // no file list to rebuild; the preview row is already gone
|
|
585
|
+
}
|
|
586
|
+
for (let i = 0; i < feedback.fileInput.files.length; i++) {
|
|
587
|
+
if (i !== indexToRemove) {
|
|
588
|
+
newFiles.items.add(feedback.fileInput.files[i]);
|
|
589
|
+
feedback.selectedFiles.items.add(feedback.fileInput.files[i]);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
feedback.fileInput.files = newFiles.files;
|
|
593
|
+
},
|
|
594
|
+
formatFileSize: function (bytes) {
|
|
595
|
+
if (bytes === 0)
|
|
596
|
+
return '0 KB'; // no i18n
|
|
597
|
+
const k = 1024;
|
|
598
|
+
const sizeInKB = bytes / k;
|
|
599
|
+
const sizeInMB = bytes / (k * k);
|
|
600
|
+
const sizeInGB = bytes / (k * k * k);
|
|
601
|
+
if (sizeInGB >= 1) {
|
|
602
|
+
return sizeInGB.toFixed(2) + ' GB'; // no i18n
|
|
603
|
+
}
|
|
604
|
+
else if (sizeInMB >= 1) {
|
|
605
|
+
return sizeInMB.toFixed(2) + ' MB'; // no i18n
|
|
606
|
+
}
|
|
607
|
+
else {
|
|
608
|
+
return sizeInKB.toFixed(2) + ' KB'; // no i18n
|
|
609
|
+
}
|
|
610
|
+
},
|
|
611
|
+
toggleRecording: function () {
|
|
612
|
+
feedback.isRecording = !feedback.isRecording;
|
|
613
|
+
if (feedback.isRecording) {
|
|
614
|
+
feedback.startRecording();
|
|
615
|
+
}
|
|
616
|
+
else {
|
|
617
|
+
feedback.stopRecording();
|
|
618
|
+
}
|
|
619
|
+
},
|
|
620
|
+
startRecording: function () {
|
|
621
|
+
if (!feedback.timerInterval) {
|
|
622
|
+
feedback.timerInterval = setInterval(feedback.updateTimer, 1000);
|
|
623
|
+
}
|
|
624
|
+
},
|
|
625
|
+
stopRecording: function () {
|
|
626
|
+
var modal = document.getElementById("zasdkFeedbackModal"); // no i18n
|
|
627
|
+
clearInterval(feedback.timerInterval || undefined);
|
|
628
|
+
feedback.timerInterval = null;
|
|
629
|
+
document.querySelector("#zasdkrectimer").remove(); // no i18n
|
|
630
|
+
feedback.toggleWindowAnimation();
|
|
631
|
+
modal.querySelector("#editRecordedVideo").style.display = "block"; // no i18n
|
|
632
|
+
modal.querySelector("#zasdkreccheckcnt").style.display = "none"; // no i18n
|
|
633
|
+
let videoElem = modal.querySelector('#previewVideoElemId'); // no i18n
|
|
634
|
+
quartz().endRecording();
|
|
635
|
+
quartz().setVideoContent(videoElem);
|
|
636
|
+
},
|
|
637
|
+
submitRecording: function () {
|
|
638
|
+
var modal = document.getElementById("zasdkFeedbackModal"); // no i18n
|
|
639
|
+
// the recording is filed for whoever the form's picker selected, falling back to the
|
|
640
|
+
// registered user; an anonymous report, a non-email id or a PII opt-out all send the
|
|
641
|
+
// placeholder instead, so Quartz never receives an address the user did not offer
|
|
642
|
+
let testEmail = 'user@example.com', // no i18n
|
|
643
|
+
testMessage = '-', picked = userChoice.anonymous ? '' : String(userChoice.userid || ''), // no i18n
|
|
644
|
+
active = !isEmpty(_ZAP._user_info.userid) ? String(_ZAP._user_info.userid) : '', // no i18n
|
|
645
|
+
userEmail = !isEmpty(picked) ? picked : active, subject = modal.querySelector('#zasdkDescription').value.trim() || testMessage, // no i18n
|
|
646
|
+
emailRegex = /^[\w]([\w\-\.\+\'=]*)@([a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,22}$/;
|
|
647
|
+
userEmail = (emailRegex.test(userEmail) && opt_in_status() == AP_TRACKING_STATUS.USAGE_TRACKING_WITH_PII) ? userEmail : testEmail;
|
|
648
|
+
// Submit the recording
|
|
649
|
+
quartz().submitRecording({
|
|
650
|
+
email: userEmail,
|
|
651
|
+
subject: subject,
|
|
652
|
+
})
|
|
653
|
+
.then((response) => {
|
|
654
|
+
var zqrecid = !isEmptyObj(response) && response && response.hasOwnProperty('feedbackLink') ? (response.feedbackLink || '') : ''; // no i18n
|
|
655
|
+
submitFeedback(buildFeedbackRequest(modal, subject, zqrecid));
|
|
656
|
+
})
|
|
657
|
+
.catch((error) => {
|
|
658
|
+
// the upload is Quartz's to do; if it fails, hand the form back rather than leaving
|
|
659
|
+
// the submit button spinning with nothing said
|
|
660
|
+
Console.error("submitRecording : " + error); // no i18n
|
|
661
|
+
feedback.handleStatusmsg(false, 'Could not upload the recording. Please try again.'); // no i18n
|
|
662
|
+
});
|
|
663
|
+
},
|
|
664
|
+
updateTimer: function () {
|
|
665
|
+
if (feedback.remainingTime > 0) {
|
|
666
|
+
feedback.remainingTime--;
|
|
667
|
+
feedback.updateDisplay();
|
|
668
|
+
}
|
|
669
|
+
else {
|
|
670
|
+
feedback.stopRecording();
|
|
671
|
+
}
|
|
672
|
+
},
|
|
673
|
+
updateDisplay: function () {
|
|
674
|
+
const minutes = Math.floor(feedback.remainingTime / 60);
|
|
675
|
+
const seconds = feedback.remainingTime % 60;
|
|
676
|
+
feedback.timerDisplay.textContent = `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`; // no i18n
|
|
677
|
+
},
|
|
678
|
+
validateFeedbackField: function () {
|
|
679
|
+
feedback.updateLeftCount();
|
|
680
|
+
var modal = document.getElementById("zasdkFeedbackModal"); // no i18n
|
|
681
|
+
const descriptionField = modal.querySelector('#zasdkDescription'); // no i18n
|
|
682
|
+
const attachmentField = modal.querySelector('#zasdkFileInput'); // no i18n
|
|
683
|
+
const videoElement = modal.querySelector('#previewVideoElemId'); // no i18n
|
|
684
|
+
const sendButton = modal.querySelector('#zasdk_submit_btn'); // no i18n
|
|
685
|
+
const descriptionFilled = descriptionField && descriptionField.value.trim() !== '' && descriptionField.value.trim().length > 0; // no i18n
|
|
686
|
+
attachmentField && attachmentField.files && attachmentField.files.length > 0;
|
|
687
|
+
videoElement && videoElement.offsetParent !== null; // Check if visible
|
|
688
|
+
const errorElements = modal.querySelectorAll('.zasdkmaxerror'); // no i18n
|
|
689
|
+
const hasErrors = feedback.getVisibleErrorCount(errorElements) > 0;
|
|
690
|
+
if (!hasErrors && descriptionFilled) {
|
|
691
|
+
sendButton.classList.remove('zasdkbtndisableclr'); // no i18n
|
|
692
|
+
feedback.handleStatusmsg();
|
|
693
|
+
}
|
|
694
|
+
else {
|
|
695
|
+
sendButton.classList.add('zasdkbtndisableclr'); // no i18n
|
|
696
|
+
}
|
|
697
|
+
},
|
|
698
|
+
getVisibleErrorCount: function (elements) {
|
|
699
|
+
return Array.from(elements).filter(element => element.offsetParent !== null).length;
|
|
700
|
+
}
|
|
701
|
+
};
|
|
702
|
+
|
|
703
|
+
export { feedback, feedbackFormClose, feedbackFormOpen, sendFeedback, setFeedbackTheme };
|