@financial-times/custom-code-component 2.0.1-beta.1 → 2.0.1-beta.10

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.
@@ -1,579 +1,7 @@
1
- function l(o) {
2
- this.listenerMap = [{}, {}], o && this.root(o), this.handle = l.prototype.handle.bind(this), this._removedListeners = [];
3
- }
4
- l.prototype.root = function(o) {
5
- const t = this.listenerMap;
6
- let e;
7
- if (this.rootElement) {
8
- for (e in t[1])
9
- t[1].hasOwnProperty(e) && this.rootElement.removeEventListener(e, this.handle, !0);
10
- for (e in t[0])
11
- t[0].hasOwnProperty(e) && this.rootElement.removeEventListener(e, this.handle, !1);
12
- }
13
- if (!o || !o.addEventListener)
14
- return this.rootElement && delete this.rootElement, this;
15
- this.rootElement = o;
16
- for (e in t[1])
17
- t[1].hasOwnProperty(e) && this.rootElement.addEventListener(e, this.handle, !0);
18
- for (e in t[0])
19
- t[0].hasOwnProperty(e) && this.rootElement.addEventListener(e, this.handle, !1);
20
- return this;
21
- };
22
- l.prototype.captureForType = function(o) {
23
- return ["blur", "error", "focus", "load", "resize", "scroll"].indexOf(o) !== -1;
24
- };
25
- l.prototype.on = function(o, t, e, n) {
26
- let s, r, i, c;
27
- if (!o)
28
- throw new TypeError("Invalid event type: " + o);
29
- if (typeof t == "function" && (n = e, e = t, t = null), n === void 0 && (n = this.captureForType(o)), typeof e != "function")
30
- throw new TypeError("Handler must be a type of Function");
31
- return s = this.rootElement, r = this.listenerMap[n ? 1 : 0], r[o] || (s && s.addEventListener(o, this.handle, n), r[o] = []), t ? /^[a-z]+$/i.test(t) ? (c = t, i = $) : /^#[a-z0-9\-_]+$/i.test(t) ? (c = t.slice(1), i = L) : (c = t, i = Element.prototype.matches) : (c = null, i = T.bind(this)), r[o].push({
32
- selector: t,
33
- handler: e,
34
- matcher: i,
35
- matcherParam: c
36
- }), this;
37
- };
38
- l.prototype.off = function(o, t, e, n) {
39
- let s, r, i, c, h;
40
- if (typeof t == "function" && (n = e, e = t, t = null), n === void 0)
41
- return this.off(o, t, e, !0), this.off(o, t, e, !1), this;
42
- if (i = this.listenerMap[n ? 1 : 0], !o) {
43
- for (h in i)
44
- i.hasOwnProperty(h) && this.off(h, t, e);
45
- return this;
46
- }
47
- if (c = i[o], !c || !c.length)
48
- return this;
49
- for (s = c.length - 1; s >= 0; s--)
50
- r = c[s], (!t || t === r.selector) && (!e || e === r.handler) && (this._removedListeners.push(r), c.splice(s, 1));
51
- return c.length || (delete i[o], this.rootElement && this.rootElement.removeEventListener(o, this.handle, n)), this;
52
- };
53
- l.prototype.handle = function(o) {
54
- let t, e;
55
- const n = o.type;
56
- let s, r, i, c, h = [], a;
57
- const p = "ftLabsDelegateIgnore";
58
- if (o[p] === !0)
59
- return;
60
- switch (a = o.target, a.nodeType === 3 && (a = a.parentNode), a.correspondingUseElement && (a = a.correspondingUseElement), s = this.rootElement, r = o.eventPhase || (o.target !== o.currentTarget ? 3 : 2), r) {
61
- case 1:
62
- h = this.listenerMap[1][n];
63
- break;
64
- case 2:
65
- this.listenerMap[0] && this.listenerMap[0][n] && (h = h.concat(this.listenerMap[0][n])), this.listenerMap[1] && this.listenerMap[1][n] && (h = h.concat(this.listenerMap[1][n]));
66
- break;
67
- case 3:
68
- h = this.listenerMap[0][n];
69
- break;
70
- }
71
- let u = [];
72
- for (e = h.length; a && e; ) {
73
- for (t = 0; t < e && (i = h[t], !!i); t++)
74
- a.tagName && ["button", "input", "select", "textarea"].indexOf(a.tagName.toLowerCase()) > -1 && a.hasAttribute("disabled") ? u = [] : i.matcher.call(a, i.matcherParam, a) && u.push([o, a, i]);
75
- if (a === s || (e = h.length, a = a.parentElement || a.parentNode, a instanceof HTMLDocument))
76
- break;
77
- }
78
- let v;
79
- for (t = 0; t < u.length; t++)
80
- if (!(this._removedListeners.indexOf(u[t][2]) > -1) && (c = this.fire.apply(this, u[t]), c === !1)) {
81
- u[t][0][p] = !0, u[t][0].preventDefault(), v = !1;
82
- break;
83
- }
84
- return v;
85
- };
86
- l.prototype.fire = function(o, t, e) {
87
- return e.handler.call(t, o, t);
88
- };
89
- function $(o, t) {
90
- return o.toLowerCase() === t.tagName.toLowerCase();
91
- }
92
- function T(o, t) {
93
- return this.rootElement === window ? (
94
- // Match the outer document (dispatched from document)
95
- t === document || // The <html> element (dispatched from document.body or document.documentElement)
96
- t === document.documentElement || // Or the window itself (dispatched from window)
97
- t === window
98
- ) : this.rootElement === t;
99
- }
100
- function L(o, t) {
101
- return o === t.id;
102
- }
103
- l.prototype.destroy = function() {
104
- this.off(), this.root();
105
- };
106
- function R(o) {
107
- return typeof o == "string" ? o.trim() : o;
108
- }
109
- function A(o, t) {
110
- for (const e in o)
111
- t[e] ? console.warn(`You can't set a custom property called ${e}`) : t[e] = o[e];
112
- }
113
- const m = Object.freeze({
114
- DEBUG: 0,
115
- INFO: 1,
116
- WARN: 2,
117
- ERROR: 3,
118
- TEST: 4,
119
- DEFAULT: 2
120
- });
121
- function N(o) {
122
- const t = o == null ? void 0 : o.toLowerCase();
123
- return t === "debug" ? m.DEBUG : t === "info" ? m.INFO : t === "warn" ? m.WARN : t === "error" ? m.ERROR : t === "test" ? m.TEST : m.DEFAULT;
124
- }
125
- class k {
126
- constructor({ level: t = m.DEFAULT } = {
127
- level: m.DEFAULT
128
- }) {
129
- this.log = this.debug, this.level = t;
130
- }
131
- debug(...t) {
132
- this.level <= m.DEBUG && console.info(...t);
133
- }
134
- info(...t) {
135
- this.level <= m.INFO && console.info(...t);
136
- }
137
- warn(...t) {
138
- this.level <= m.WARN && console.warn(...t);
139
- }
140
- error(...t) {
141
- this.level <= m.ERROR && console.error(...t);
142
- }
143
- }
144
- const S = (o, t, e) => {
145
- const n = Array.from((o == null ? void 0 : o.querySelectorAll(e)) ?? []), s = n.findIndex((r) => r === t);
146
- if (s !== -1)
147
- return {
148
- siblings: n.length,
149
- position: s
150
- };
151
- }, C = [
152
- "nodeName",
153
- "className",
154
- "id",
155
- "href",
156
- "text",
157
- "role"
158
- ], O = (o) => {
159
- const t = {};
160
- for (const e of C) {
161
- const n = o[e] || o.getAttribute(e) || o.hasAttribute(e);
162
- n !== void 0 && (typeof n == "boolean" ? t[e] = n : t[e] = R(n));
163
- }
164
- return t;
165
- }, P = (o) => {
166
- try {
167
- const t = JSON.parse(o), e = Object.prototype.toString.call(t);
168
- return [e === "[object Object]" || e === "[object Array]", t];
169
- } catch {
170
- return [!1, null];
171
- }
172
- }, x = (o) => {
173
- const [t, e] = P(o);
174
- return t ? e : o;
175
- }, I = (o, t) => (o.filter(
176
- (e) => e.name.match(/^data-trackable|^data-o-|^aria-/i)
177
- ).forEach((e) => {
178
- t[e.name] = e.value;
179
- }), t), M = (o, t, e) => {
180
- const n = {};
181
- return e && C.forEach((s) => {
182
- typeof t[s] < "u" && s !== "id" && (n[s] = t[s]);
183
- }), o.filter((s) => s.name.match(/^data-trackable-context-/i)).forEach((s) => {
184
- n[s.name.replace("data-trackable-context-", "")] = x(s.value);
185
- }), n;
186
- };
187
- function D(o, t) {
188
- const e = o, n = e != null && e.getAttribute("data-trackable") ? `[data-trackable="${e.getAttribute("data-trackable")}"]` : e == null ? void 0 : e.nodeName, s = [], r = {};
189
- for (; o && o !== t; ) {
190
- const i = O(o), c = Array.from(o.attributes);
191
- let h = I(c, i);
192
- h["data-trackable"] && (h = Object.assign(
193
- h,
194
- S(o, e, n)
195
- )), s.push(h);
196
- const a = M(c, i, o === e);
197
- A(a, r), o = o.parentNode;
198
- }
199
- return { trace: s, customContext: r };
200
- }
201
- const U = ["ctrlKey", "altKey", "shiftKey", "metaKey"];
202
- class F {
203
- constructor({
204
- id: t = "00000000-0000-0000-0000-000000000000",
205
- name: e = "ccc-component",
206
- subtype: n = "interactive",
207
- teamName: s = "djd",
208
- shadowRoot: r = null,
209
- category: i = "cta",
210
- elements: c = 'a, button, input, [role="button"]',
211
- logger: h
212
- }) {
213
- this.cccId = t, this.cccName = e, this.subtype = n, this.teamName = s, this.shadowRoot = r, this.category = i, this.elements = c, this.isInitialised = !1, this.log = h ?? new k();
214
- }
215
- // Get properties for the event (as opposed to properties of the clicked element)
216
- getEventProperties(t) {
217
- const e = {};
218
- for (const n of U)
219
- if (t[n])
220
- try {
221
- e[n] = R(t[n]);
222
- } catch (s) {
223
- this.log.info(s);
224
- }
225
- return e;
226
- }
227
- // Controller for handling click events
228
- handleClickEvent(t, e) {
229
- return (n, s) => {
230
- const r = this.getEventProperties(n), { trace: i, customContext: c } = D(s, e);
231
- r.custom = s.dataset && s.dataset.custom ? JSON.parse(s.dataset.custom) : null, r.domPathTokens = i, r.component = {
232
- id: this.cccId,
233
- name: this.cccName,
234
- type: "custom-code-component",
235
- subtype: this.subtype
236
- }, r.teamName = this.teamName, r.url = document.URL, A(c, r), r.method = "ftCustomAnalytics", t = { ...t, ...r }, document.body.dispatchEvent(
237
- new CustomEvent("oTracking.event", {
238
- detail: t,
239
- bubbles: !0,
240
- composed: !0
241
- })
242
- );
243
- };
244
- }
245
- sendSpoorEvent(t, e) {
246
- const n = {
247
- category: "component",
248
- action: "act",
249
- component: {
250
- id: this.cccId,
251
- name: this.cccName,
252
- type: "custom-code-component",
253
- subtype: this.subtype
254
- },
255
- teamName: this.teamName,
256
- trigger_action: t,
257
- custom: e,
258
- method: "ftCustomAnalytics"
259
- };
260
- document.body.dispatchEvent(
261
- new CustomEvent("oTracking.event", {
262
- detail: n,
263
- bubbles: !0,
264
- composed: !0
265
- })
266
- );
267
- }
268
- init(t) {
269
- var e;
270
- if (!this.isInitialised) {
271
- this.isInitialised = !0, this.cccId = t || this.cccId;
272
- const n = {
273
- action: "click",
274
- category: this.category
275
- }, s = (e = this.shadowRoot) == null ? void 0 : e.querySelector("[data-component-root]");
276
- s && new l(s).on(
277
- "click",
278
- this.elements,
279
- this.handleClickEvent(n, s),
280
- !0
281
- );
282
- }
283
- }
284
- }
285
- class f {
286
- constructor(t) {
287
- this.org = "local", this.repo = "dev";
288
- const { org: e, repo: n, name: s, versionRange: r } = E(t) ? t : f.fromString(t);
289
- e && (this.org = e), n && (this.repo = n), this.name = s, this.versionRange = r;
290
- }
291
- set path(t) {
292
- const { org: e, repo: n, name: s, versionRange: r } = E(t) ? t : f.fromString(t);
293
- this.org = e, this.repo = n, this.name = s, this.versionRange = r;
294
- }
295
- get path() {
296
- return `${this.org}/${this.repo}@${this.versionRange}/${this.name}`;
297
- }
298
- toString() {
299
- return this.path;
300
- }
301
- static fromString(t, e) {
302
- var c;
303
- if (!t) throw new d("No path specified");
304
- const [n, s, r] = t.replace(/@[^\/]+/, "").split("/").reverse(), i = e ?? ((c = t.match(/@[^\/]+/)) == null ? void 0 : c.toString().replace("@", "")) ?? "unknown";
305
- if (s && !i) throw new d("No version specified");
306
- return new f({ org: r, repo: s, name: n, versionRange: i });
307
- }
308
- }
309
- function E(o) {
310
- return typeof o == "object" && o !== null ? "org" in o && "repo" in o && "name" in o : !1;
311
- }
312
- class d extends Error {
313
- constructor(t, e) {
314
- !e && t ? (super(t), this.component = null) : typeof (e == null ? void 0 : e.component) == "string" ? (super(
315
- t ?? `${e.cause ?? "Unknown error"} in ${e.component} imported from ${e.source ?? "an undefined source"}.`
316
- ), this.component = f.fromString(e.component)) : E(e == null ? void 0 : e.component) ? (super(
317
- t ?? `${e.cause ?? "Unknown error"} in ${e.component.org}/${e.component.repo}/${e.component.name}@${e.component.versionRange} imported from ${e.source ?? "an undefined source"}.`
318
- ), this.component = new f(e.component)) : (super(
319
- `${(e == null ? void 0 : e.cause) ?? "Unknown error"} in unknown component imported from ${(e == null ? void 0 : e.source) ?? "unknown source"}.`
320
- ), this.component = null), this.source = e == null ? void 0 : e.source, Error.captureStackTrace && Error.captureStackTrace(this, d), this.name = "CCCError";
321
- }
322
- }
323
- class g extends d {
324
- constructor(t, e) {
325
- super(t, { ...e, cause: "Import error" }), this.name = "CCCImportError";
326
- }
327
- }
328
- class j extends d {
329
- constructor(t) {
330
- super(null, { ...t, cause: "Timeout error" }), this.name = "CCCTimeoutError";
331
- }
332
- }
333
- const w = class w extends Event {
334
- constructor(t, e) {
335
- super(w.eventType, {
336
- bubbles: !0,
337
- cancelable: !1,
338
- composed: !0,
339
- ...e
340
- }), this.component = t.component, this.source = t.source;
341
- }
342
- };
343
- w.eventType = "ccc:ConnectedEvent";
344
- let b = w;
345
- const V = (o) => o.replace(
346
- /[A-Z]+(?![a-z])|[A-Z]/g,
347
- (t, e) => (e ? "-" : "") + t.toLowerCase()
348
- );
349
- function z(o) {
350
- return y([
351
- "localhost",
352
- "local.ft.com",
353
- /^.*\.apps\.in\.ft\.com$/
354
- ], o);
355
- }
356
- function J() {
357
- return y([
358
- "localhost",
359
- "local.ft.com",
360
- /^.*\.in\.ft\.com$/
361
- ], window.location.hostname);
362
- }
363
- function W() {
364
- return y([
365
- "spark.ft.com",
366
- "spark-staging.ft.com"
367
- ], window.location.host);
368
- }
369
- function y(o, t) {
370
- return t ? o.some((e) => typeof e == "string" ? e === t : e.test(t)) : !1;
371
- }
372
- function H(o) {
373
- if (o === null)
374
- return;
375
- let t;
376
- const e = new URL("http://localhost:5173");
377
- try {
378
- if (typeof o == "string") {
379
- if ((o === "" || o.toLowerCase() === "true") && J())
380
- t = e;
381
- else if (t = o.startsWith("http://") || o.startsWith("https://") ? new URL(o) : void 0, t && !z(t == null ? void 0 : t.hostname))
382
- throw new Error("Unsafe testing host override");
383
- } else W() && (t = e);
384
- } catch {
385
- return t;
386
- }
387
- return t;
388
- }
389
- async function q(o, t) {
390
- return t ? fetch(
391
- new URL(`src/${o}/config.yaml`, t),
392
- {
393
- method: "HEAD"
394
- }
395
- ).then(() => !0).catch(() => !1) : !1;
396
- }
397
- class K extends HTMLElement {
398
- constructor() {
399
- super(), this.mode = "open", this.RESERVED_ATTRS = /* @__PURE__ */ new Set([
400
- "iframe",
401
- "path",
402
- "version",
403
- "data-component-props",
404
- "data-asset-type",
405
- "shadow-open",
406
- "env",
407
- "load-timeout"
408
- ]), this.channel = new MessageChannel(), this.initTracking = async () => {
409
- var e;
410
- try {
411
- (e = this.tracking) == null || e.init(this.id);
412
- } catch (n) {
413
- const s = this.getAttribute("path"), r = this.getAttribute("version");
414
- this.log.info(
415
- `Error initialising tracking on <custom-code-component> ${s}@${r}`
416
- ), this.log.error(n);
417
- }
418
- }, this.log = new k({
419
- level: N(this.getAttribute("log"))
420
- });
421
- const t = HTMLElement.prototype.hasOwnProperty("attachInternals");
422
- try {
423
- const e = t && this.attachInternals();
424
- } catch (e) {
425
- this.log.error(e);
426
- }
427
- }
428
- async connectedCallback() {
429
- try {
430
- const t = this.getAttribute("path"), e = this.getAttribute("version");
431
- this.component = f.fromString(t, e), this.lightRoot = Array.from(this.childNodes), this.app = await this.load(), await this.mount(), await this.initTracking();
432
- } catch (t) {
433
- t instanceof Error && requestAnimationFrame(() => {
434
- this.emitError(t);
435
- }), this.unmount(t);
436
- }
437
- }
438
- emitError(t) {
439
- var n;
440
- let e;
441
- if (t instanceof d && ((n = t.name) != null && n.startsWith("CCC")) ? e = t.name.replace(/^CCC/, "ccc:") : e = `ccc:${(t == null ? void 0 : t.name) ?? "UnknownError"}`, !e)
442
- return this.log.debug(t);
443
- this.dispatchEvent(
444
- new ErrorEvent(e, {
445
- bubbles: !0,
446
- cancelable: !1,
447
- composed: !0,
448
- error: t,
449
- message: t.message
450
- })
451
- );
452
- }
453
- disconnectedCallback() {
454
- const t = this.getAttribute("path");
455
- this.log.info(`<custom-code-component:${t}> disconnected`), typeof this.onunmount == "function" && this.onunmount();
456
- }
457
- onmessage() {
458
- }
459
- onunmount(t) {
460
- }
461
- async onready(t) {
462
- try {
463
- await t;
464
- } catch (e) {
465
- e instanceof Error && requestAnimationFrame(() => {
466
- this.emitError(e);
467
- }), this.unmount(e);
468
- }
469
- }
470
- postMessage(t) {
471
- this.channel.port1.postMessage(t);
472
- }
473
- async mount(t) {
474
- var e, n;
475
- try {
476
- if (this.mode = this.getAttribute("shadow-open") == "false" ? "closed" : "open", this.component) {
477
- if (this.dispatchEvent(
478
- new b({
479
- component: this.component,
480
- source: this.source
481
- })
482
- ), this.dataset.cccReady = "true", delete this.dataset.cccError, !this.app)
483
- throw new Error("CCC mounted without App");
484
- const s = this.shadowRoot !== null, r = this.shadowRoot ?? this.attachShadow({ mode: this.mode }), i = JSON.parse(this.getAttribute("data-component-props")), c = Object.fromEntries(
485
- [...this.attributes].filter((u) => !this.RESERVED_ATTRS.has(u.name)).map((u) => [u.name, u.value])
486
- );
487
- this.tracking = new F({
488
- name: (e = this.component) == null ? void 0 : e.toString(),
489
- subtype: "interactive",
490
- teamName: "djd",
491
- shadowRoot: this.shadowRoot,
492
- logger: this.log
493
- });
494
- const { unmount: h, onmessage: a, ready: p } = this.app(
495
- r,
496
- {
497
- ...c,
498
- data: i,
499
- port: this.channel.port2,
500
- tracking: this.tracking,
501
- prerendered: !!t,
502
- children: this.children
503
- },
504
- s
505
- ) || {};
506
- h && (this.onunmount = h), a && (this.onmessage = a), p && this.onready(p);
507
- }
508
- } catch (s) {
509
- throw this.log.info(
510
- `<custom-code-component> uncaught error during mount from ${(n = this.component) == null ? void 0 : n.toString()}`
511
- ), this.log.error(s), s;
512
- }
513
- }
514
- // Called in top-level error handler
515
- // Replace shadow root with light DOM children on error
516
- unmount(t) {
517
- var e;
518
- this.lightRoot && (this.onunmount(), (e = this.shadowRoot) == null || e.replaceChildren(...this.lightRoot), this.dataset.cccError || (this.dataset.cccError = V(t.name.replace("CCC", ""))), delete this.dataset.cccReady);
519
- }
520
- async load() {
521
- if (!this.component)
522
- throw new Error("No path found");
523
- const t = this.getAttribute("path"), e = this.getAttribute("version"), n = Number(this.getAttribute("load-timeout") || 1e4), s = this.getAttribute("testEnv"), r = H(s), i = await q(this.component.name, r), c = this.getAttribute("id");
524
- this.source = i ? `${r == null ? void 0 : r.origin}/src/${this.component.name}/index.jsx?id=${c}` : `https://www.ft.com/__component/${this.component.org}/${this.component.repo}${e ? `@${e}` : "@latest"}/${this.component.name}/${this.component.name}.js?id=${c}`;
525
- try {
526
- return await new Promise(
527
- (h, a) => {
528
- const p = setTimeout(() => {
529
- this.log.error("CCC import timeout error"), a(
530
- new j({
531
- component: this.component,
532
- source: this.source
533
- })
534
- );
535
- }, Number(n));
536
- if (this.source)
537
- import(
538
- /* webpackIgnore: true */
539
- this.source
540
- /* @vite-ignore */
541
- ).then(({ default: u }) => {
542
- if (u)
543
- clearTimeout(p), h(u);
544
- else
545
- throw new g(
546
- "No component renderer default export found",
547
- {
548
- component: this.component,
549
- source: this.source
550
- }
551
- );
552
- }).catch((u) => {
553
- clearTimeout(p), this.log.error(u), u instanceof Error && !(u instanceof g) ? a(
554
- new g(u.message, {
555
- component: this.component,
556
- source: this.source
557
- })
558
- ) : a(u);
559
- });
560
- else
561
- throw clearTimeout(p), new g(`Unable to mount ${t}`, {
562
- component: this.component,
563
- source: this.source
564
- });
565
- }
566
- );
567
- } catch (h) {
568
- throw this.log.error(
569
- `<custom-code-component> error during import from ${t}@${e}`
570
- ), h;
571
- }
572
- }
573
- }
574
- const _ = () => customElements.define("custom-code-component", K);
575
- customElements && !customElements.get("custom-code-component") && _();
1
+ import { FTCustomCodeComponent as o } from "./custom-element.js";
2
+ const t = () => customElements.define("custom-code-component", o);
3
+ customElements && !customElements.get("custom-code-component") && t();
576
4
  export {
577
- _ as init
5
+ t as init
578
6
  };
579
7
  //# sourceMappingURL=CustomCodeComponent.js.map