@financial-times/custom-code-component 2.0.1-beta.7 → 2.0.1-beta.9

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,597 +1,7 @@
1
- function p(o) {
2
- this.listenerMap = [{}, {}], o && this.root(o), this.handle = p.prototype.handle.bind(this), this._removedListeners = [];
3
- }
4
- p.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
- p.prototype.captureForType = function(o) {
23
- return ["blur", "error", "focus", "load", "resize", "scroll"].indexOf(o) !== -1;
24
- };
25
- p.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 = T) : /^#[a-z0-9\-_]+$/i.test(t) ? (c = t.slice(1), i = L) : (c = t, i = Element.prototype.matches) : (c = null, i = $.bind(this)), r[o].push({
32
- selector: t,
33
- handler: e,
34
- matcher: i,
35
- matcherParam: c
36
- }), this;
37
- };
38
- p.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
- p.prototype.handle = function(o) {
54
- let t, e;
55
- const n = o.type;
56
- let s, r, i, c, h = [], a;
57
- const l = "ftLabsDelegateIgnore";
58
- if (o[l] === !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][l] = !0, u[t][0].preventDefault(), v = !1;
82
- break;
83
- }
84
- return v;
85
- };
86
- p.prototype.fire = function(o, t, e) {
87
- return e.handler.call(t, o, t);
88
- };
89
- function T(o, t) {
90
- return o.toLowerCase() === t.tagName.toLowerCase();
91
- }
92
- function $(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
- p.prototype.destroy = function() {
104
- this.off(), this.root();
105
- };
106
- function C(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 R {
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
- }, k = [
152
- "nodeName",
153
- "className",
154
- "id",
155
- "href",
156
- "text",
157
- "role"
158
- ], P = (o) => {
159
- const t = {};
160
- for (const e of k) {
161
- const n = o[e] || o.getAttribute(e) || o.hasAttribute(e);
162
- n !== void 0 && (typeof n == "boolean" ? t[e] = n : t[e] = C(n));
163
- }
164
- return t;
165
- }, O = (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] = O(o);
174
- return t ? e : o;
175
- }, D = (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), I = (o, t, e) => {
180
- const n = {};
181
- return e && k.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 M(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 = P(o), c = Array.from(o.attributes);
191
- let h = D(c, i);
192
- h["data-trackable"] && (h = Object.assign(
193
- h,
194
- S(o, e, n)
195
- )), s.push(h);
196
- const a = I(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 R();
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] = C(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 } = M(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 p(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 } = b(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 } = b(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 b(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)) : b(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 E = w;
345
- const V = (o) => o.replace(
346
- /[A-Z]+(?![a-z])|[A-Z]/g,
347
- (t, e) => (e ? "-" : "") + t.toLowerCase()
348
- );
349
- function q(o) {
350
- return y([
351
- "localhost",
352
- "local.ft.com",
353
- /^.*\.apps\.in\.ft\.com$/
354
- ], o);
355
- }
356
- function z() {
357
- return y([
358
- "localhost",
359
- "local.ft.com",
360
- /^.*\.in\.ft\.com$/
361
- ], window.location.hostname);
362
- }
363
- function H() {
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 J(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") && z())
380
- t = e;
381
- else if (t = o.startsWith("http://") || o.startsWith("https://") ? new URL(o) : void 0, t && !q(t == null ? void 0 : t.hostname))
382
- throw new Error("Unsafe testing host override");
383
- } else H() && (t = e);
384
- } catch {
385
- return t;
386
- }
387
- return t;
388
- }
389
- async function W(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 R({
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.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 E({
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 });
485
- if (!s) {
486
- const u = document.createElement("template");
487
- u.innerHTML = "<div data-component-root><slot></slot></div>", this.appendChild(u), r.appendChild(u.content.cloneNode(!0));
488
- }
489
- const i = JSON.parse(this.getAttribute("data-component-props")), c = Object.fromEntries(
490
- [...this.attributes].filter((u) => !this.RESERVED_ATTRS.has(u.name)).map((u) => [u.name, u.value])
491
- );
492
- this.tracking = new F({
493
- name: (e = this.component) == null ? void 0 : e.toString(),
494
- subtype: "interactive",
495
- teamName: "djd",
496
- shadowRoot: this.shadowRoot,
497
- logger: this.log
498
- }), !i && c && (this.log.warn(
499
- `CCC ${this.component.toString()}: passing component settings as webcomponent attributes is %cDEPRECATED`,
500
- "font-weight: bold",
501
- " and will be removed in v3."
502
- ), this.log.warn(
503
- "Please use the %cdata-component-props",
504
- "text-decoration: underline;",
505
- " attribute instead."
506
- ));
507
- const { unmount: h, onmessage: a, ready: l } = this.app(
508
- r,
509
- {
510
- ...i ?? c,
511
- data: i ?? c,
512
- port: this.channel.port2,
513
- tracking: this.tracking,
514
- prerendered: !!t,
515
- children: this.children
516
- },
517
- s
518
- ) || {};
519
- h && (this.onunmount = h), a && (this.onmessage = a), l && this.onready(l);
520
- }
521
- } catch (s) {
522
- throw this.log.info(
523
- `<custom-code-component> uncaught error during mount from ${(n = this.component) == null ? void 0 : n.toString()}`
524
- ), this.log.error(s), s;
525
- }
526
- }
527
- // Called in top-level error handler
528
- // Replace shadow root with either <slot> or template[data-component-fallback]
529
- // slot on failure
530
- unmount(t) {
531
- var n;
532
- this.onunmount(t);
533
- const e = this.querySelector(
534
- "template[data-component-fallback]"
535
- ) ?? this.querySelector("template");
536
- e && ((n = this.shadowRoot) == null || n.replaceChildren(e.content.cloneNode(!0))), this.dataset.cccError || (this.dataset.cccError = V(t.name.replace("CCC", ""))), delete this.dataset.cccReady;
537
- }
538
- async load() {
539
- if (!this.component)
540
- throw new Error("No path found");
541
- const t = this.getAttribute("path"), e = this.getAttribute("version"), n = Number(this.getAttribute("load-timeout") || 1e4), s = this.getAttribute("test-env"), r = J(s), i = await W(this.component.name, r), c = this.getAttribute("id");
542
- 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}`;
543
- try {
544
- return await new Promise(
545
- (h, a) => {
546
- const l = setTimeout(() => {
547
- this.log.error("CCC import timeout error"), a(
548
- new j({
549
- component: this.component,
550
- source: this.source
551
- })
552
- );
553
- }, Number(n));
554
- if (this.source)
555
- import(
556
- /* webpackIgnore: true */
557
- this.source
558
- /* @vite-ignore */
559
- ).then(({ default: u }) => {
560
- if (u)
561
- clearTimeout(l), h(u);
562
- else
563
- throw new g(
564
- "No component renderer default export found",
565
- {
566
- component: this.component,
567
- source: this.source
568
- }
569
- );
570
- }).catch((u) => {
571
- clearTimeout(l), this.log.error(u), u instanceof Error && !(u instanceof g) ? a(
572
- new g(u.message, {
573
- component: this.component,
574
- source: this.source
575
- })
576
- ) : a(u);
577
- });
578
- else
579
- throw clearTimeout(l), new g(`Unable to mount ${t}`, {
580
- component: this.component,
581
- source: this.source
582
- });
583
- }
584
- );
585
- } catch (h) {
586
- throw this.log.error(
587
- `<custom-code-component> error during import from ${t}@${e}`
588
- ), h;
589
- }
590
- }
591
- }
592
- const _ = () => customElements.define("custom-code-component", K);
593
- 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();
594
4
  export {
595
- _ as init
5
+ t as init
596
6
  };
597
7
  //# sourceMappingURL=CustomCodeComponent.js.map