@atlassian/atlassian-connect-js 5.3.190

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 (190) hide show
  1. package/.depcheckrc.json +16 -0
  2. package/.editorconfig +9 -0
  3. package/.envrc +5 -0
  4. package/.eslintignore +12 -0
  5. package/.eslintrc +31 -0
  6. package/.husky/pre-commit +11 -0
  7. package/.lintstagedrc.js +6 -0
  8. package/.netrc +1 -0
  9. package/.npmrc-public +4 -0
  10. package/.nvmrc +1 -0
  11. package/LICENSE +3 -0
  12. package/README.md +281 -0
  13. package/bitbucket-pipelines.yml +33 -0
  14. package/build/bin/bin-helper.js +29 -0
  15. package/build/bin/npm-postinstall.js +9 -0
  16. package/build/configs/append-sourcemapping.js +13 -0
  17. package/build/configs/availabletasks.js +15 -0
  18. package/build/configs/clean.js +12 -0
  19. package/build/configs/concat.js +44 -0
  20. package/build/configs/copy.js +32 -0
  21. package/build/configs/index.js +19 -0
  22. package/build/configs/jshint.js +9 -0
  23. package/build/configs/karma.js +11 -0
  24. package/build/configs/replace.js +10 -0
  25. package/build/configs/requirejs.js +187 -0
  26. package/build/configs/saucelabs-launchers.js +30 -0
  27. package/build/configs/shell.js +24 -0
  28. package/build/configs/uglify.js +15 -0
  29. package/build/configs/watch.js +45 -0
  30. package/build/end.frag +13 -0
  31. package/build/start.frag +18 -0
  32. package/bundlesize.config.json +14 -0
  33. package/dist/connect-host.css +225 -0
  34. package/dist/connect-host.js +13908 -0
  35. package/dist/connect-host.min.css +1 -0
  36. package/dist/iframe-fedramp.js +13663 -0
  37. package/dist/iframe.js +13663 -0
  38. package/dist/legacy-text-colors.css +245 -0
  39. package/dist/surfaces.css +11 -0
  40. package/dist/themes/atlaskit-tokens_dark-future.css +6 -0
  41. package/dist/themes/atlaskit-tokens_dark.css +394 -0
  42. package/dist/themes/atlaskit-tokens_legacy-dark.css +394 -0
  43. package/dist/themes/atlaskit-tokens_legacy-light.css +394 -0
  44. package/dist/themes/atlaskit-tokens_light-future.css +6 -0
  45. package/dist/themes/atlaskit-tokens_light.css +394 -0
  46. package/dist/themes/atlaskit-tokens_shape.css +14 -0
  47. package/dist/themes/atlaskit-tokens_spacing.css +26 -0
  48. package/dist/themes/atlaskit-tokens_typography-adg3.css +25 -0
  49. package/dist/themes/atlaskit-tokens_typography-modernized.css +25 -0
  50. package/dist/themes/atlaskit-tokens_typography-refreshed.css +25 -0
  51. package/gulpfile.js +204 -0
  52. package/memleak-check/app.html +10 -0
  53. package/memleak-check/index.html +31 -0
  54. package/memleak-check/index.js +33 -0
  55. package/package.json +102 -0
  56. package/renovate.json +11 -0
  57. package/security-assistant.yml +2 -0
  58. package/spec/.eslintrc +10 -0
  59. package/spec/config/karma.base.conf.js +200 -0
  60. package/spec/config/karma.conf.js +20 -0
  61. package/spec/fixtures/base_dialog_component_tests.js +180 -0
  62. package/spec/mocks/mock_feature_flag.js +23 -0
  63. package/spec/tests/amd_spec.js +122 -0
  64. package/spec/tests/analytics_dispatcher_spec.js +429 -0
  65. package/spec/tests/analytics_performance_spec.js +41 -0
  66. package/spec/tests/button_spec.js +143 -0
  67. package/spec/tests/dialog_extension_spec.js +59 -0
  68. package/spec/tests/dialog_module_provider_spec.js +499 -0
  69. package/spec/tests/dialog_module_spec.js +395 -0
  70. package/spec/tests/dialog_spec.js +296 -0
  71. package/spec/tests/dialog_webitem_spec.js +183 -0
  72. package/spec/tests/dropdown_module_spec.js +77 -0
  73. package/spec/tests/env_module_spec.js +153 -0
  74. package/spec/tests/events_spec.js +77 -0
  75. package/spec/tests/extension_configuration_options_store_spec.js +25 -0
  76. package/spec/tests/flag_module_provider_spec.js +79 -0
  77. package/spec/tests/flag_module_spec.js +118 -0
  78. package/spec/tests/flag_spec.js +243 -0
  79. package/spec/tests/host-api_spec.js +220 -0
  80. package/spec/tests/iframe-create_spec.js +43 -0
  81. package/spec/tests/iframe_container_spec.js +19 -0
  82. package/spec/tests/iframe_spec.js +265 -0
  83. package/spec/tests/inline_dialog_spec.js +41 -0
  84. package/spec/tests/inline_dialog_webitem_spec.js +331 -0
  85. package/spec/tests/loading_indicator_spec.js +48 -0
  86. package/spec/tests/messages_module_spec.js +108 -0
  87. package/spec/tests/meta_spec.js +71 -0
  88. package/spec/tests/module_args_spec.js +52 -0
  89. package/spec/tests/observe_spec.js +73 -0
  90. package/spec/tests/public_events.spec.js +102 -0
  91. package/spec/tests/scroll_position_spec.js +109 -0
  92. package/spec/tests/theming_spec.js +594 -0
  93. package/spec/tests/util_spec.js +55 -0
  94. package/spec/tests/utils/base64_spec.js +20 -0
  95. package/spec/tests/utils/button_spec.js +11 -0
  96. package/spec/tests/utils/cookie_spec.js +19 -0
  97. package/spec/tests/utils/deprecate_spec.js +22 -0
  98. package/spec/tests/utils/dialog_spec.js +297 -0
  99. package/spec/tests/utils/host_util_spec.js +45 -0
  100. package/spec/tests/utils/iframe_spec.js +41 -0
  101. package/spec/tests/utils/jwt_spec.js +135 -0
  102. package/spec/tests/utils/url_spec.js +37 -0
  103. package/spec/tests/utils/waitUntilReadyNextTick.js +5 -0
  104. package/spec/tests/utils/webitem.js +219 -0
  105. package/spec/tests/webitem_spec.js +131 -0
  106. package/src/css/host/dialog.css +144 -0
  107. package/src/css/host/flags.css +5 -0
  108. package/src/css/host/host.css +64 -0
  109. package/src/css/host/messages.css +12 -0
  110. package/src/css/plugin/legacy-text-colors.less +55 -0
  111. package/src/css/plugin/surfaces.css +11 -0
  112. package/src/host/ACJSFrameworkAdaptor.js +44 -0
  113. package/src/host/actions/analytics_action.js +35 -0
  114. package/src/host/actions/button_actions.js +21 -0
  115. package/src/host/actions/dialog_actions.js +27 -0
  116. package/src/host/actions/dialog_extension_actions.js +25 -0
  117. package/src/host/actions/dom_event_actions.js +32 -0
  118. package/src/host/actions/dropdown_actions.js +12 -0
  119. package/src/host/actions/env_actions.js +57 -0
  120. package/src/host/actions/event_actions.js +33 -0
  121. package/src/host/actions/flag_actions.js +22 -0
  122. package/src/host/actions/iframe_actions.js +27 -0
  123. package/src/host/actions/inline_dialog_actions.js +26 -0
  124. package/src/host/actions/inline_dialog_webitem_actions.js +10 -0
  125. package/src/host/actions/jwt_actions.js +51 -0
  126. package/src/host/actions/loading_indicator_actions.js +10 -0
  127. package/src/host/actions/module_actions.js +14 -0
  128. package/src/host/actions/webitem_actions.js +29 -0
  129. package/src/host/components/button.js +112 -0
  130. package/src/host/components/dialog.js +447 -0
  131. package/src/host/components/dialog_extension.js +106 -0
  132. package/src/host/components/dialog_webitem.js +69 -0
  133. package/src/host/components/flag.js +102 -0
  134. package/src/host/components/iframe.js +130 -0
  135. package/src/host/components/iframe_container.js +38 -0
  136. package/src/host/components/inline_dialog.js +108 -0
  137. package/src/host/components/inline_dialog_webitem.js +157 -0
  138. package/src/host/components/loading_indicator.js +110 -0
  139. package/src/host/components/webitem.js +227 -0
  140. package/src/host/deprecate.js +20 -0
  141. package/src/host/dispatchers/analytics_dispatcher.js +512 -0
  142. package/src/host/dispatchers/event_dispatcher.js +46 -0
  143. package/src/host/dollar.js +8 -0
  144. package/src/host/host-api.js +325 -0
  145. package/src/host/iframe-create.js +7 -0
  146. package/src/host/index.js +108 -0
  147. package/src/host/module-providers.js +13 -0
  148. package/src/host/modules/_featureFlag.js +42 -0
  149. package/src/host/modules/_performance.js +54 -0
  150. package/src/host/modules/analytics.js +17 -0
  151. package/src/host/modules/dialog.js +496 -0
  152. package/src/host/modules/dropdown.js +255 -0
  153. package/src/host/modules/env.js +156 -0
  154. package/src/host/modules/events.js +28 -0
  155. package/src/host/modules/flag.js +170 -0
  156. package/src/host/modules/host.js +19 -0
  157. package/src/host/modules/inline-dialog.js +33 -0
  158. package/src/host/modules/messages.js +309 -0
  159. package/src/host/modules/page.js +29 -0
  160. package/src/host/modules/scroll-position.js +102 -0
  161. package/src/host/modules/theming.js +114 -0
  162. package/src/host/stores/extension_configuration_options_store.js +24 -0
  163. package/src/host/util.js +98 -0
  164. package/src/host/utils/access-narrowing-context.js +18 -0
  165. package/src/host/utils/base64.js +22 -0
  166. package/src/host/utils/button.js +10 -0
  167. package/src/host/utils/cookie.js +14 -0
  168. package/src/host/utils/dialog.js +239 -0
  169. package/src/host/utils/feature-flag.js +27 -0
  170. package/src/host/utils/iframe.js +23 -0
  171. package/src/host/utils/jwt.js +60 -0
  172. package/src/host/utils/observe.js +34 -0
  173. package/src/host/utils/removal-observer.js +30 -0
  174. package/src/host/utils/simplexdm.js +58 -0
  175. package/src/host/utils/url.js +23 -0
  176. package/src/host/utils/webitem.js +214 -0
  177. package/src/plugin/amd.js +115 -0
  178. package/src/plugin/analytics.js +84 -0
  179. package/src/plugin/deprecate.js +16 -0
  180. package/src/plugin/dialog.js +197 -0
  181. package/src/plugin/dollar.js +77 -0
  182. package/src/plugin/events-instance.js +2 -0
  183. package/src/plugin/events.js +246 -0
  184. package/src/plugin/extension_configuration_options_store.js +27 -0
  185. package/src/plugin/featureFlag.js +8 -0
  186. package/src/plugin/index.js +118 -0
  187. package/src/plugin/meta.js +14 -0
  188. package/src/plugin/public-events.js +73 -0
  189. package/src/plugin/theming.js +211 -0
  190. package/src/plugin/util.js +104 -0
@@ -0,0 +1,512 @@
1
+ import EventDispatcher from './event_dispatcher';
2
+ import $ from '../dollar';
3
+ import observe from '../utils/observe';
4
+
5
+ /**
6
+ * Timings beyond 20 seconds (connect's load timeout) will be clipped to an X.
7
+ * @const
8
+ * @type {int}
9
+ */
10
+ const LOADING_TIME_THRESHOLD = 20000;
11
+
12
+ /**
13
+ * Trim extra zeros from the load time.
14
+ * @const
15
+ * @type {int}
16
+ */
17
+ const LOADING_TIME_TRIMP_PRECISION = 100;
18
+
19
+ /**
20
+ * @param {number} value
21
+ * @returns {number}
22
+ */
23
+ function bucketLoadingTime(value) {
24
+ const bucket = value > LOADING_TIME_THRESHOLD ? LOADING_TIME_THRESHOLD : value;
25
+ return (Math.ceil(bucket / LOADING_TIME_TRIMP_PRECISION) * LOADING_TIME_TRIMP_PRECISION)
26
+ }
27
+
28
+ // Only send 0.1% of content resolver events
29
+ const CONTENT_RESOLVER_SAMPLE_RATE = 0.1;
30
+
31
+ // Only send 0.1% of invoke method events
32
+ const INVOKE_METHOD_SAMPLE_RATE = 0.1;
33
+
34
+
35
+ const ALLOWLISTED_METHODS = [
36
+ 'saveMacro',
37
+ ]
38
+
39
+
40
+ class AnalyticsDispatcher {
41
+
42
+ constructor() {
43
+ this._addons = {};
44
+ this._sampleLoadEvent = new Map();
45
+ }
46
+
47
+ /**
48
+ * Determines whether a sampled event should be sent based on given rate.
49
+ * @param {int} rate Rate as an integer percentage.
50
+ * @returns {boolean}
51
+ */
52
+ _shouldSampleEvent(rate) {
53
+ return Math.random() * 100 <= rate
54
+ }
55
+
56
+ /**
57
+ * We want to keep sampling consistent across the various events that are sent for a single iframe load.
58
+ * @param extension
59
+ * @returns {boolean}
60
+ */
61
+ _shouldSampleLoadEvent(extension) {
62
+ // Only send 1% of iframe load events
63
+ const IFRAME_LOAD_SAMPLE_RATE = 1;
64
+ if (this._sampleLoadEvent.has(extension.id)) {
65
+ return this._sampleLoadEvent.get(extension.id);
66
+ }
67
+ const shouldSample = this._shouldSampleEvent(IFRAME_LOAD_SAMPLE_RATE);
68
+ this._sampleLoadEvent.set(extension.id, shouldSample);
69
+ return shouldSample;
70
+ }
71
+
72
+ /**
73
+ * Track an event via GasV3.
74
+ *
75
+ * Warning! Confluence does not use ac/analytics, we have to manually forward it
76
+ * The legacy way of doing this was using individual hooks for each event. Many existing events have dedicated hooks.
77
+ * If you are creating a new event, you can use sendExternal=true to avoid the need for a dedicated hook.
78
+ *
79
+ * https://stash.atlassian.com/projects/ATLASSIAN/repos/atlassian-frontend-monorepo/browse/confluence/packages/confluence-connect-support/src/ConnectAnalyticsPublishSupport.js
80
+ * @param eventType {string}
81
+ * @param event {object}
82
+ * @param sendExternal {boolean}
83
+ * @private
84
+ */
85
+ _trackGasV3(eventType, event, sendExternal) {
86
+ if (event.attributes) {
87
+ event.attributes.apVersion = (window._AP && window._AP.version) ? window._AP.version : undefined;
88
+ }
89
+ if (sendExternal) {
90
+ this._forwardGasV3AnalyticsEvent(eventType, event);
91
+ }
92
+
93
+ try {
94
+ const analyticsCrossProduct = window.require('ac/analytics');
95
+ analyticsCrossProduct.emitGasV3(eventType, event);
96
+ } catch (e) {
97
+ if (!window.Confluence && !window.__karma__) {
98
+ // this is not serious. It usually means the product is doing analytics using another mechanism
99
+ // Confluence no longer uses ac/analytics so we should stop reporting it as an error
100
+ console.info('Connect GasV3 catch', e);
101
+ }
102
+ }
103
+ }
104
+
105
+ _trackAndForwardGasV3(eventType, event) {
106
+ this._trackGasV3(eventType, event, true);
107
+ }
108
+
109
+ _forwardGasV3AnalyticsEvent(eventType, event) {
110
+ EventDispatcher.dispatch('analytics-forward-event', eventType, event)
111
+ }
112
+
113
+ _time() {
114
+ return window.performance && window.performance.now ? window.performance.now() : new Date().getTime();
115
+ }
116
+
117
+ trackLoadingStarted(extension) {
118
+ if (this._addons && extension && extension.id) {
119
+ extension.startLoading = this._time();
120
+ this._addons[extension.id] = extension;
121
+ } else {
122
+ console.error('ACJS: cannot track loading analytics', this._addons, extension);
123
+ }
124
+ }
125
+
126
+ trackGasV3UseOfDeprecatedMethod(methodUsed, extension) {
127
+ this._trackAndForwardGasV3('operational', {
128
+ action: 'deprecatedMethod',
129
+ actionSubject: 'connectAddon',
130
+ actionSubjectId: extension.addon_key,
131
+ attributes: {
132
+ moduleKey: extension.key,
133
+ methodUsed: methodUsed
134
+ },
135
+ source: extension.addon_key
136
+ })
137
+ }
138
+
139
+ trackGasV3MultipleDialogOpening(dialogType, extension) {
140
+ this._trackAndForwardGasV3('operational', {
141
+ action: 'multipleDialogOpened',
142
+ actionSubject: 'connectAddon',
143
+ actionSubjectId: extension.addon_key,
144
+ attributes: {
145
+ moduleKey: extension.key,
146
+ dialogType: dialogType
147
+ },
148
+ source: extension.addon_key
149
+ });
150
+ }
151
+
152
+ trackIframePerformance(metrics, extension) {
153
+ if (!this._shouldSampleLoadEvent(extension)) {
154
+ return;
155
+ }
156
+ this._trackGasV3('operational', {
157
+ source: extension.addon_key,
158
+ action: 'iframeRendered',
159
+ actionSubject: 'connectAddon',
160
+ actionSubjectId: extension.addon_key,
161
+ attributes: {
162
+ key: extension['key'],
163
+ pearApp: this._getPearApp(extension),
164
+ moduleType: this._getModuleType(extension),
165
+ iframeIsCacheable: this._isCacheable(extension),
166
+ moduleLocation: this._getModuleLocation(extension),
167
+ domainLookupTime: metrics.domainLookupTime,
168
+ connectionTime: metrics.connectionTime,
169
+ decodedBodySize: metrics.decodedBodySize,
170
+ domContentLoadedTime: metrics.domContentLoadedTime,
171
+ fetchTime: metrics.fetchTime
172
+ }
173
+ });
174
+ }
175
+
176
+ trackGasV3WebVitals(metrics, extension) {
177
+ if (!this._shouldSampleLoadEvent(extension)) {
178
+ return;
179
+ }
180
+ this._trackAndForwardGasV3('operational', {
181
+ source: extension.addon_key,
182
+ action: 'webVitals',
183
+ actionSubject: 'connectAddon',
184
+ actionSubjectId: extension.addon_key,
185
+ attributes: Object.assign({}, metrics, {
186
+ key: extension['key'],
187
+ pearApp: this._getPearApp(extension),
188
+ moduleType: this._getModuleType(extension),
189
+ iframeIsCacheable: this._isCacheable(extension),
190
+ moduleLocation: this._getModuleLocation(extension),
191
+ })
192
+ })
193
+ }
194
+
195
+ dispatch(name, data) {
196
+ this._trackGasV3External(name, data);
197
+ }
198
+
199
+ trackExternal(name, data) {
200
+ this._trackGasV3External(name, data);
201
+ }
202
+
203
+ _trackGasV3External(name, data) {
204
+ this._trackAndForwardGasV3('operational', {
205
+ action: 'externalEvent',
206
+ actionSubject: 'connectAddon',
207
+ actionSubjectId: name,
208
+ attributes: {
209
+ eventName: name,
210
+ ...(typeof data === 'object' ? data : { values: data })
211
+ },
212
+ source: 'connectAddon'
213
+ });
214
+ }
215
+
216
+ /**
217
+ * method called when an iframe's loading metrics gets corrupted
218
+ * to destroy the analytics as they cannot be reliable
219
+ * this should be called when:
220
+ * 1. the product calls iframe creation multiple times for the same connect addon
221
+ * 2. the iframe is moved / repainted causing a window.reload event
222
+ * 3. user right clicks iframe and reloads it
223
+ */
224
+ _resetAnalyticsDueToUnreliable(extensionId) {
225
+ if(!extensionId) {
226
+ throw new Error('Cannot reset analytics due to no extension id');
227
+ }
228
+ if(this._addons[extensionId]) {
229
+ clearTimeout(this._addons[extensionId]);
230
+ delete this._addons[extensionId];
231
+ } else {
232
+ console.info('Cannot clear analytics, cache does not contain extension id');
233
+ }
234
+ }
235
+
236
+ _isCacheable(extension) {
237
+ const href = extension.url;
238
+ return href !== undefined && href.indexOf('xdm_e=') === -1;
239
+ }
240
+
241
+ _getModuleType(extension) {
242
+ return extension.options ? extension.options.moduleType : undefined;
243
+ }
244
+
245
+ _getModuleLocation(extension) {
246
+ return extension.options ? extension.options.moduleLocation : undefined;
247
+ }
248
+
249
+ _getPearApp(extension) {
250
+ return extension.options && extension.options.pearApp === 'true';
251
+ }
252
+
253
+ trackGasV3Visible (extension) {
254
+ if (!this._shouldSampleLoadEvent(extension)) {
255
+ return;
256
+ }
257
+ this._trackGasV3('operational', {
258
+ action: 'iframeViewed',
259
+ actionSubject: 'connectAddon',
260
+ actionSubjectId: extension['addon_key'],
261
+ attributes: {
262
+ moduleType: this._getModuleType(extension),
263
+ iframeIsCacheable: this._isCacheable(extension),
264
+ moduleKey: extension.key,
265
+ moduleLocation: this._getModuleLocation(extension),
266
+ pearApp: this._getPearApp(extension)
267
+ },
268
+ source: extension.addon_key
269
+ });
270
+ }
271
+
272
+ trackGasV3InvokeMethod(extension, module, method, methodArgs) {
273
+ if (!this._shouldSampleEvent(INVOKE_METHOD_SAMPLE_RATE) && !ALLOWLISTED_METHODS.includes(method)) {
274
+ return;
275
+ }
276
+
277
+ const args = Array.isArray(methodArgs) ? [...methodArgs] : [];
278
+ const isContextFn = (arg) => typeof arg === 'function' && Boolean(arg._context);
279
+
280
+ // Remove undefined values, or the callback context function only at the very end of the array
281
+ while (args.length > 0 && (args[args.length - 1] === undefined || isContextFn(args[args.length - 1]))) {
282
+ args.pop();
283
+ }
284
+
285
+ const argTypes = args.map(arg => {
286
+ if (Array.isArray(arg)) {
287
+ return 'array'
288
+ } else if (arg === null) {
289
+ return 'null'
290
+ }
291
+ return typeof arg;
292
+ })
293
+
294
+ this._trackAndForwardGasV3('operational', {
295
+ action: 'invokeMethod',
296
+ actionSubject: 'connectAddon',
297
+ actionSubjectId: extension['addon_key'],
298
+ attributes: {
299
+ argCount: args.length,
300
+ argTypes,
301
+ module: module,
302
+ method: method,
303
+ moduleType: this._getModuleType(extension),
304
+ moduleKey: extension.key,
305
+ moduleLocation: this._getModuleLocation(extension),
306
+ pearApp: this._getPearApp(extension)
307
+ },
308
+ source: extension.addon_key
309
+ });
310
+ }
311
+
312
+ trackGasV3ContentResolver(isSuccess, extra) {
313
+ if (!this._shouldSampleEvent(CONTENT_RESOLVER_SAMPLE_RATE)) {
314
+ return;
315
+ }
316
+ const extension = extra.extension;
317
+ const attrs = extra.attrs;
318
+ const action = isSuccess ? 'contentResolverSucceeded' : 'contentResolverFailed';
319
+ this._trackGasV3('operational', {
320
+ action: action,
321
+ actionSubject: 'connectAddon',
322
+ actionSubjectId: extension['addon_key'],
323
+ attributes: Object.assign({}, attrs, {
324
+ moduleType: this._getModuleType(extension),
325
+ moduleKey: extension.key,
326
+ moduleLocation: this._getModuleLocation(extension),
327
+ pearApp: this._getPearApp(extension)
328
+ }),
329
+ source: extension.addon_key
330
+ });
331
+ }
332
+
333
+ trackGasV3LoadingEnded (extension) {
334
+ if (!this._shouldSampleLoadEvent(extension)) {
335
+ return;
336
+ }
337
+ var iframeLoadMillis = this._time() - this._addons[extension.id].startLoading;
338
+ this._trackGasV3('operational', {
339
+ action: 'iframeLoaded',
340
+ actionSubject: 'connectAddon',
341
+ actionSubjectId: extension['addon_key'],
342
+ attributes: {
343
+ moduleType: this._getModuleType(extension),
344
+ iframeIsCacheable: this._isCacheable(extension),
345
+ iframeLoadMillis: iframeLoadMillis,
346
+ iframeLoadBucket: bucketLoadingTime(iframeLoadMillis),
347
+ moduleKey: extension.key,
348
+ moduleLocation: this._getModuleLocation(extension),
349
+ pearApp: this._getPearApp(extension)
350
+ },
351
+ source: extension.addon_key
352
+ });
353
+ }
354
+
355
+ trackGasV3LoadingTimeout (extension) {
356
+ if (!this._shouldSampleLoadEvent(extension)) {
357
+ return;
358
+ }
359
+ this._trackGasV3('operational', {
360
+ action: 'iframeTimeout',
361
+ actionSubject: 'connectAddon',
362
+ actionSubjectId: extension['addon_key'],
363
+ attributes: {
364
+ moduleType: this._getModuleType(extension),
365
+ iframeIsCacheable: this._isCacheable(extension),
366
+ moduleKey: extension.key,
367
+ moduleLocation: this._getModuleLocation(extension),
368
+ pearApp: this._getPearApp(extension),
369
+ connectedStatus: typeof window.navigator.onLine === 'boolean' ? window.navigator.onLine.toString() : 'not-supported'
370
+ },
371
+ source: extension.addon_key
372
+ });
373
+ }
374
+
375
+ /**
376
+ * @param {WebItemInvokedEventData} data
377
+ */
378
+ trackGasV3InlineDialogOpened(data) {
379
+ const extension = data.extension;
380
+ this._trackAndForwardGasV3('operational', {
381
+ action: 'inlineDialogOpened',
382
+ actionSubject: 'connectAddon',
383
+ actionSubjectId: extension['addon_key'],
384
+ attributes: {
385
+ moduleKey: extension.key,
386
+ moduleLocation: this._getModuleLocation(extension),
387
+ eventType: data.eventType,
388
+ onHoverEnabled: data.onHover
389
+ },
390
+ source: extension.addon_key
391
+ });
392
+ }
393
+
394
+ /**
395
+ * Track a dialog opened via the legacy event-handler (non-imperative) API
396
+ */
397
+ trackGasV3LegacyDialogOpened(data) {
398
+ const extension = data.extension;
399
+ this._trackAndForwardGasV3('operational', {
400
+ action: 'legacyDialogOpened',
401
+ actionSubject: 'connectAddon',
402
+ actionSubjectId: extension['addon_key'],
403
+ attributes: {
404
+ moduleKey: extension.key,
405
+ moduleLocation: this._getModuleLocation(extension),
406
+ parentIds: data.parentIds
407
+ },
408
+ source: extension.addon_key
409
+ });
410
+ }
411
+
412
+ /**
413
+ * Tracks when the Statsig client is not initialised before the Statsig feature flag is fetched
414
+ * Jira - https://data-portal.internal.atlassian.com/analytics/registry/67270
415
+ * Confluence - https://data-portal.internal.atlassian.com/analytics/registry/67271
416
+ */
417
+ trackGasV3FeatureFlagDefaultFalse(featureFlag) {
418
+ this._trackAndForwardGasV3('operational', {
419
+ action: 'returned',
420
+ actionSubject: 'defaultFalseFeatureFlag',
421
+ actionSubjectId: featureFlag,
422
+ source: 'host',
423
+ attributes: {
424
+ featureFlag
425
+ },
426
+ source: 'Host'
427
+ });
428
+ }
429
+
430
+ /**
431
+ * Tracks when a Connect iframe is clicked
432
+ */
433
+ trackGasV3IframeClicked(data) {
434
+ this._trackAndForwardGasV3('ui', {
435
+ action: 'iframeClicked',
436
+ actionSubject: 'connectAddon',
437
+ source: 'host',
438
+ attributes: {
439
+ appId: data.addonKey,
440
+ moduleType: data.moduleType,
441
+ moduleLocation: data.moduleLocation,
442
+ baseUrl: data.baseUrl,
443
+ }
444
+ })
445
+ }
446
+ }
447
+
448
+ var analytics = new AnalyticsDispatcher();
449
+ if ($.fn) {
450
+ EventDispatcher.register('iframe-create', function (data) {
451
+ analytics.trackLoadingStarted(data.extension);
452
+ });
453
+ }
454
+
455
+ EventDispatcher.register('method-invoked', function (data) {
456
+ analytics.trackGasV3InvokeMethod(data.extension, data.module, data.fn, data.args);
457
+ });
458
+
459
+ EventDispatcher.register('iframe-bridge-start', function (data) {
460
+ analytics.trackLoadingStarted(data.extension);
461
+ });
462
+ EventDispatcher.register('iframe-bridge-established', function (data) {
463
+ analytics.trackGasV3LoadingEnded(data.extension);
464
+ observe(document.getElementById(data.extension.id), () => {
465
+ EventDispatcher.dispatch('iframe-visible', data.extension);
466
+ analytics.trackGasV3Visible(data.extension);
467
+ });
468
+ });
469
+
470
+ EventDispatcher.register('iframe-bridge-timeout', function (data) {
471
+ analytics.trackGasV3LoadingTimeout(data.extension);
472
+ });
473
+
474
+ EventDispatcher.register('analytics-deprecated-method-used', function (data) {
475
+ analytics.trackGasV3UseOfDeprecatedMethod(data.methodUsed, data.extension);
476
+ });
477
+ EventDispatcher.register('analytics-iframe-performance', function (data) {
478
+ analytics.trackIframePerformance(data.metrics, data.extension);
479
+ });
480
+ EventDispatcher.register('analytics-web-vitals', function (data) {
481
+ analytics.trackGasV3WebVitals(data.metrics, data.extension);
482
+ });
483
+
484
+ EventDispatcher.register('iframe-destroyed', function (data) {
485
+ analytics._resetAnalyticsDueToUnreliable(data.extension.extension_id);
486
+ });
487
+
488
+ EventDispatcher.register('analytics-external-event-track', function (data) {
489
+ analytics.trackExternal(data.eventName, data.values);
490
+ });
491
+
492
+ EventDispatcher.register('analytics-content-resolver-track', function (data) {
493
+ const success = data.success;
494
+ const extra = data.extra;
495
+ analytics.trackGasV3ContentResolver(success, extra);
496
+ });
497
+
498
+ EventDispatcher.register('analytics-inline-dialog-opened', function (data) {
499
+ analytics.trackGasV3InlineDialogOpened(data);
500
+ });
501
+
502
+ EventDispatcher.register('analytics-legacy-dialog-opened', function (data) {
503
+ analytics.trackGasV3LegacyDialogOpened(data);
504
+ });
505
+
506
+ EventDispatcher.register('analytics-track-iframe-clicked', function (data) {
507
+ analytics.trackGasV3IframeClicked(data);
508
+ })
509
+
510
+
511
+
512
+ export default analytics;
@@ -0,0 +1,46 @@
1
+ /**
2
+ * pub/sub for extension state (created, destroyed, initialized)
3
+ * taken from hipchat webcore
4
+ **/
5
+
6
+ import EventEmitter from 'events';
7
+
8
+ class EventDispatcher extends EventEmitter {
9
+
10
+ constructor() {
11
+ super();
12
+ this.setMaxListeners(20);
13
+ }
14
+
15
+ dispatch(action, ...args) {
16
+ this.emit.apply(this, ['before:' + action].concat(args));
17
+ this.emit.apply(this, arguments);
18
+ this.emit.apply(this, ['after:' + action].concat(args));
19
+ }
20
+
21
+ registerOnce(action, callback) {
22
+ if (typeof action === 'string') {
23
+ this.once(action, callback);
24
+ } else {
25
+ throw 'ACJS: event name must be string';
26
+ }
27
+ }
28
+
29
+ register(action, callback) {
30
+ if (typeof action === 'string') {
31
+ this.on(action, callback);
32
+ } else {
33
+ throw 'ACJS: event name must be string';
34
+ }
35
+ }
36
+
37
+ unregister(action, callback) {
38
+ if (typeof action === 'string') {
39
+ this.removeListener(action, callback);
40
+ } else {
41
+ throw 'ACJS: event name must be string';
42
+ }
43
+ }
44
+ }
45
+
46
+ export default new EventDispatcher();
@@ -0,0 +1,8 @@
1
+ /**
2
+ * The iframe-side code exposes a jquery-like implementation via _dollar.
3
+ * This runs on the product side to provide AJS.$ under a _dollar module to provide a consistent interface
4
+ * to code that runs on host and iframe.
5
+ */
6
+ export default (window.AJS && window.AJS.$) || function() {
7
+ console.error('[ACJS] jQuery was not loaded before Connect. If in product frontend, make sure to load via a wrapper, eg withConnectHost()')
8
+ };