@financial-times/custom-code-component 1.9.9 → 1.10.0

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,7 +1,7 @@
1
- function u(o) {
2
- this.listenerMap = [{}, {}], o && this.root(o), this.handle = u.prototype.handle.bind(this), this._removedListeners = [];
1
+ function d(o) {
2
+ this.listenerMap = [{}, {}], o && this.root(o), this.handle = d.prototype.handle.bind(this), this._removedListeners = [];
3
3
  }
4
- u.prototype.root = function(o) {
4
+ d.prototype.root = function(o) {
5
5
  const t = this.listenerMap;
6
6
  let e;
7
7
  if (this.rootElement) {
@@ -19,45 +19,45 @@ u.prototype.root = function(o) {
19
19
  t[0].hasOwnProperty(e) && this.rootElement.addEventListener(e, this.handle, !1);
20
20
  return this;
21
21
  };
22
- u.prototype.captureForType = function(o) {
22
+ d.prototype.captureForType = function(o) {
23
23
  return ["blur", "error", "focus", "load", "resize", "scroll"].indexOf(o) !== -1;
24
24
  };
25
- u.prototype.on = function(o, t, e, n) {
26
- let s, r, c, i;
25
+ d.prototype.on = function(o, t, e, n) {
26
+ let s, i, a, r;
27
27
  if (!o)
28
28
  throw new TypeError("Invalid event type: " + o);
29
29
  if (typeof t == "function" && (n = e, e = t, t = null), n === void 0 && (n = this.captureForType(o)), typeof e != "function")
30
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) ? (i = t, c = E) : /^#[a-z0-9\-_]+$/i.test(t) ? (i = t.slice(1), c = w) : (i = t, c = Element.prototype.matches) : (i = null, c = y.bind(this)), r[o].push({
31
+ return s = this.rootElement, i = this.listenerMap[n ? 1 : 0], i[o] || (s && s.addEventListener(o, this.handle, n), i[o] = []), t ? /^[a-z]+$/i.test(t) ? (r = t, a = y) : /^#[a-z0-9\-_]+$/i.test(t) ? (r = t.slice(1), a = v) : (r = t, a = Element.prototype.matches) : (r = null, a = w.bind(this)), i[o].push({
32
32
  selector: t,
33
33
  handler: e,
34
- matcher: c,
35
- matcherParam: i
34
+ matcher: a,
35
+ matcherParam: r
36
36
  }), this;
37
37
  };
38
- u.prototype.off = function(o, t, e, n) {
39
- let s, r, c, i, h;
38
+ d.prototype.off = function(o, t, e, n) {
39
+ let s, i, a, r, h;
40
40
  if (typeof t == "function" && (n = e, e = t, t = null), n === void 0)
41
41
  return this.off(o, t, e, !0), this.off(o, t, e, !1), this;
42
- if (c = this.listenerMap[n ? 1 : 0], !o) {
43
- for (h in c)
44
- c.hasOwnProperty(h) && this.off(h, t, e);
42
+ if (a = this.listenerMap[n ? 1 : 0], !o) {
43
+ for (h in a)
44
+ a.hasOwnProperty(h) && this.off(h, t, e);
45
45
  return this;
46
46
  }
47
- if (i = c[o], !i || !i.length)
47
+ if (r = a[o], !r || !r.length)
48
48
  return this;
49
- for (s = i.length - 1; s >= 0; s--)
50
- r = i[s], (!t || t === r.selector) && (!e || e === r.handler) && (this._removedListeners.push(r), i.splice(s, 1));
51
- return i.length || (delete c[o], this.rootElement && this.rootElement.removeEventListener(o, this.handle, n)), this;
49
+ for (s = r.length - 1; s >= 0; s--)
50
+ i = r[s], (!t || t === i.selector) && (!e || e === i.handler) && (this._removedListeners.push(i), r.splice(s, 1));
51
+ return r.length || (delete a[o], this.rootElement && this.rootElement.removeEventListener(o, this.handle, n)), this;
52
52
  };
53
- u.prototype.handle = function(o) {
53
+ d.prototype.handle = function(o) {
54
54
  let t, e;
55
55
  const n = o.type;
56
- let s, r, c, i, h = [], a;
57
- const m = "ftLabsDelegateIgnore";
58
- if (o[m] === !0)
56
+ let s, i, a, r, h = [], c;
57
+ const l = "ftLabsDelegateIgnore";
58
+ if (o[l] === !0)
59
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) {
60
+ switch (c = o.target, c.nodeType === 3 && (c = c.parentNode), c.correspondingUseElement && (c = c.correspondingUseElement), s = this.rootElement, i = o.eventPhase || (o.target !== o.currentTarget ? 3 : 2), i) {
61
61
  case 1:
62
62
  h = this.listenerMap[1][n];
63
63
  break;
@@ -68,28 +68,28 @@ u.prototype.handle = function(o) {
68
68
  h = this.listenerMap[0][n];
69
69
  break;
70
70
  }
71
- let l = [];
72
- for (e = h.length; a && e; ) {
73
- for (t = 0; t < e && (c = h[t], !!c); t++)
74
- a.tagName && ["button", "input", "select", "textarea"].indexOf(a.tagName.toLowerCase()) > -1 && a.hasAttribute("disabled") ? l = [] : c.matcher.call(a, c.matcherParam, a) && l.push([o, a, c]);
75
- if (a === s || (e = h.length, a = a.parentElement || a.parentNode, a instanceof HTMLDocument))
71
+ let u = [];
72
+ for (e = h.length; c && e; ) {
73
+ for (t = 0; t < e && (a = h[t], !!a); t++)
74
+ c.tagName && ["button", "input", "select", "textarea"].indexOf(c.tagName.toLowerCase()) > -1 && c.hasAttribute("disabled") ? u = [] : a.matcher.call(c, a.matcherParam, c) && u.push([o, c, a]);
75
+ if (c === s || (e = h.length, c = c.parentElement || c.parentNode, c instanceof HTMLDocument))
76
76
  break;
77
77
  }
78
- let d;
79
- for (t = 0; t < l.length; t++)
80
- if (!(this._removedListeners.indexOf(l[t][2]) > -1) && (i = this.fire.apply(this, l[t]), i === !1)) {
81
- l[t][0][m] = !0, l[t][0].preventDefault(), d = !1;
78
+ let m;
79
+ for (t = 0; t < u.length; t++)
80
+ if (!(this._removedListeners.indexOf(u[t][2]) > -1) && (r = this.fire.apply(this, u[t]), r === !1)) {
81
+ u[t][0][l] = !0, u[t][0].preventDefault(), m = !1;
82
82
  break;
83
83
  }
84
- return d;
84
+ return m;
85
85
  };
86
- u.prototype.fire = function(o, t, e) {
86
+ d.prototype.fire = function(o, t, e) {
87
87
  return e.handler.call(t, o, t);
88
88
  };
89
- function E(o, t) {
89
+ function y(o, t) {
90
90
  return o.toLowerCase() === t.tagName.toLowerCase();
91
91
  }
92
- function y(o, t) {
92
+ function w(o, t) {
93
93
  return this.rootElement === window ? (
94
94
  // Match the outer document (dispatched from document)
95
95
  t === document || // The <html> element (dispatched from document.body or document.documentElement)
@@ -97,96 +97,96 @@ function y(o, t) {
97
97
  t === window
98
98
  ) : this.rootElement === t;
99
99
  }
100
- function w(o, t) {
100
+ function v(o, t) {
101
101
  return o === t.id;
102
102
  }
103
- u.prototype.destroy = function() {
103
+ d.prototype.destroy = function() {
104
104
  this.off(), this.root();
105
105
  };
106
- function f(o) {
106
+ function g(o) {
107
107
  return typeof o == "string" ? o.trim() : o;
108
108
  }
109
- function g(o, t) {
109
+ function b(o, t) {
110
110
  for (const e in o)
111
111
  t[e] ? console.warn(`You can't set a custom property called ${e}`) : t[e] = o[e];
112
112
  }
113
- const v = (o, t, e) => {
114
- const n = Array.from(o.querySelectorAll(e)), s = n.findIndex((r) => r === t);
113
+ const k = (o, t, e) => {
114
+ const n = Array.from(o.querySelectorAll(e)), s = n.findIndex((i) => i === t);
115
115
  if (s !== -1)
116
116
  return {
117
117
  siblings: n.length,
118
118
  position: s
119
119
  };
120
- }, b = [
120
+ }, E = [
121
121
  "nodeName",
122
122
  "className",
123
123
  "id",
124
124
  "href",
125
125
  "text",
126
126
  "role"
127
- ], k = (o) => {
127
+ ], A = (o) => {
128
128
  const t = {};
129
- for (const e of b) {
129
+ for (const e of E) {
130
130
  const n = o[e] || o.getAttribute(e) || o.hasAttribute(e);
131
- n !== void 0 && (typeof n == "boolean" ? t[e] = n : t[e] = f(n));
131
+ n !== void 0 && (typeof n == "boolean" ? t[e] = n : t[e] = g(n));
132
132
  }
133
133
  return t;
134
- }, A = (o) => {
134
+ }, N = (o) => {
135
135
  try {
136
136
  const t = JSON.parse(o), e = Object.prototype.toString.call(t);
137
137
  return [e === "[object Object]" || e === "[object Array]", t];
138
138
  } catch {
139
139
  return [!1, null];
140
140
  }
141
- }, N = (o) => {
142
- const [t, e] = A(o);
141
+ }, P = (o) => {
142
+ const [t, e] = N(o);
143
143
  return t ? e : o;
144
- }, P = (o, t) => (o.filter(
144
+ }, $ = (o, t) => (o.filter(
145
145
  (e) => e.name.match(/^data-trackable|^data-o-|^aria-/i)
146
146
  ).forEach((e) => {
147
147
  t[e.name] = e.value;
148
- }), t), $ = (o, t, e) => {
148
+ }), t), x = (o, t, e) => {
149
149
  const n = {};
150
- return e && b.forEach((s) => {
150
+ return e && E.forEach((s) => {
151
151
  typeof t[s] < "u" && s !== "id" && (n[s] = t[s]);
152
152
  }), o.filter((s) => s.name.match(/^data-trackable-context-/i)).forEach((s) => {
153
- n[s.name.replace("data-trackable-context-", "")] = N(s.value);
153
+ n[s.name.replace("data-trackable-context-", "")] = P(s.value);
154
154
  }), n;
155
155
  };
156
- function x(o, t) {
157
- const e = o, n = e.getAttribute("data-trackable") ? `[data-trackable="${e.getAttribute("data-trackable")}"]` : e.nodeName, s = [], r = {};
156
+ function C(o, t) {
157
+ const e = o, n = e.getAttribute("data-trackable") ? `[data-trackable="${e.getAttribute("data-trackable")}"]` : e.nodeName, s = [], i = {};
158
158
  for (; o && o !== t; ) {
159
- const c = k(o), i = Array.from(o.attributes);
160
- let h = P(i, c);
159
+ const a = A(o), r = Array.from(o.attributes);
160
+ let h = $(r, a);
161
161
  h["data-trackable"] && (h = Object.assign(
162
162
  h,
163
- v(o, e, n)
163
+ k(o, e, n)
164
164
  )), s.push(h);
165
- const a = $(i, c, o === e);
166
- g(a, r), o = o.parentNode;
165
+ const c = x(r, a, o === e);
166
+ b(c, i), o = o.parentNode;
167
167
  }
168
- return { trace: s, customContext: r };
168
+ return { trace: s, customContext: i };
169
169
  }
170
- const C = ["ctrlKey", "altKey", "shiftKey", "metaKey"];
171
- class L {
170
+ const L = ["ctrlKey", "altKey", "shiftKey", "metaKey"];
171
+ class M {
172
172
  constructor({
173
173
  id: t = "00000000-0000-0000-0000-000000000000",
174
174
  name: e = "ccc-component",
175
175
  subtype: n = "interactive",
176
176
  teamName: s = "djd",
177
- shadowRoot: r = null,
178
- category: c = "cta",
179
- elements: i = 'a, button, input, [role="button"]'
177
+ shadowRoot: i = null,
178
+ category: a = "cta",
179
+ elements: r = 'a, button, input, [role="button"]'
180
180
  }) {
181
- this.cccId = t, this.cccName = e, this.subtype = n, this.teamName = s, this.shadowRoot = r, this.category = c, this.elements = i, this.isInitialised = !1;
181
+ this.cccId = t, this.cccName = e, this.subtype = n, this.teamName = s, this.shadowRoot = i, this.category = a, this.elements = r, this.isInitialised = !1;
182
182
  }
183
183
  // Get properties for the event (as opposed to properties of the clicked element)
184
184
  getEventProperties(t) {
185
185
  const e = {};
186
- for (const n of C)
186
+ for (const n of L)
187
187
  if (t[n])
188
188
  try {
189
- e[n] = f(t[n]);
189
+ e[n] = g(t[n]);
190
190
  } catch (s) {
191
191
  console.log(s);
192
192
  }
@@ -195,13 +195,13 @@ class L {
195
195
  // Controller for handling click events
196
196
  handleClickEvent(t, e) {
197
197
  return (n, s) => {
198
- const r = this.getEventProperties(n), { trace: c, customContext: i } = x(s, e);
199
- r.custom = s.dataset && s.dataset.custom ? JSON.parse(s.dataset.custom) : null, r.domPathTokens = c, r.component = {
198
+ const i = this.getEventProperties(n), { trace: a, customContext: r } = C(s, e);
199
+ i.custom = s.dataset && s.dataset.custom ? JSON.parse(s.dataset.custom) : null, i.domPathTokens = a, i.component = {
200
200
  id: this.cccId,
201
201
  name: this.cccName,
202
202
  type: "custom-code-component",
203
203
  subtype: this.subtype
204
- }, r.teamName = this.teamName, g(i, r), t.context = r, t.method = "ftCustomAnalytics", document.body.dispatchEvent(
204
+ }, i.teamName = this.teamName, b(r, i), t.context = i, t.method = "ftCustomAnalytics", document.body.dispatchEvent(
205
205
  new CustomEvent("oTracking.event", {
206
206
  detail: t,
207
207
  bubbles: !0,
@@ -243,7 +243,7 @@ class L {
243
243
  action: "click",
244
244
  category: this.category
245
245
  }, s = (e = this.shadowRoot) == null ? void 0 : e.querySelector("[data-component-root]");
246
- this.shadowRoot && new u(s).on(
246
+ this.shadowRoot && new d(s).on(
247
247
  "click",
248
248
  this.elements,
249
249
  this.handleClickEvent(n, s),
@@ -252,7 +252,7 @@ class L {
252
252
  }
253
253
  }
254
254
  }
255
- class M extends HTMLElement {
255
+ class O extends HTMLElement {
256
256
  constructor() {
257
257
  super(...arguments), this.RESERVED_ATTRS = /* @__PURE__ */ new Set([
258
258
  "iframe",
@@ -270,15 +270,15 @@ class M extends HTMLElement {
270
270
  if (!this.app)
271
271
  throw new Error("CCC mounted without App");
272
272
  const t = this.shadowRoot ?? this.attachShadow({ mode: this.mode }), e = this.app, n = JSON.parse(this.getAttribute("data-component-props")), s = Object.fromEntries(
273
- [...this.attributes].filter((i) => !this.RESERVED_ATTRS.has(i.name)).map((i) => [i.name, i.value])
273
+ [...this.attributes].filter((r) => !this.RESERVED_ATTRS.has(r.name)).map((r) => [r.name, r.value])
274
274
  );
275
- this.tracking = new L({
275
+ this.tracking = new M({
276
276
  name: `${this.getAttribute("path")}@${this.getAttribute("version")}`,
277
277
  subtype: "interactive",
278
278
  teamName: "djd",
279
279
  shadowRoot: this.shadowRoot
280
280
  });
281
- const { unmount: r, onmessage: c } = e(
281
+ const { unmount: i, onmessage: a } = e(
282
282
  t,
283
283
  {
284
284
  ...s,
@@ -288,74 +288,75 @@ class M extends HTMLElement {
288
288
  },
289
289
  ...this.children
290
290
  ) || {};
291
- r && (this.unmount = r), c && (this.onmessage = c);
291
+ i && (this.unmount = i), a && (this.onmessage = a);
292
292
  }
293
293
  async connectedCallback() {
294
- var h;
295
- const t = this.getAttribute("path"), e = this.getAttribute("version"), n = this.getAttribute("load-timeout") ?? 2e3, s = (h = this.getAttribute("env")) == null ? void 0 : h.toLowerCase().startsWith("d");
294
+ var c;
295
+ const t = this.getAttribute("path"), e = this.getAttribute("version"), n = this.getAttribute("load-timeout") ?? 2e3, s = (c = this.getAttribute("env")) == null ? void 0 : c.toLowerCase().startsWith("d");
296
296
  if (!t)
297
297
  throw new Error(
298
298
  "path attribute not specified in <custom-code-component>"
299
299
  );
300
- const [r, c, i] = t.split("/").reverse();
301
- if (!(!r || !c || !i)) {
302
- this.source = s ? `http://localhost:5173/src/${r}/index.jsx` : `https://www.ft.com/__component/${i}/${c}${e ? `@${e}` : "@latest"}/${r}/${r}.js`;
303
- try {
304
- this.app = await new Promise((a, m) => {
305
- const l = setTimeout(() => {
306
- this.dispatchEvent(
307
- new CustomEvent("ccc-timeout", {
308
- bubbles: !0,
309
- cancelable: !0,
310
- detail: {
311
- component: `${t}@${e}`,
312
- source: this.source
313
- }
314
- })
315
- ), this.dataset.cccError = "import-timeout", delete this.dataset.cccReady;
316
- }, Number(n));
317
- import(
318
- /* webpackIgnore: true */
319
- this.source
320
- /* @vite-ignore */
321
- ).then(({ default: d }) => {
322
- if (d)
323
- clearTimeout(l), a(d);
324
- else
325
- throw new p(
326
- "No component renderer default export found"
327
- );
328
- }).catch((d) => m(new p(d)));
329
- });
330
- } catch (a) {
331
- console.error(
332
- `<custom-code-component> error during import from ${t}@${e}`
333
- ), delete this.dataset.cccReady, this.dataset.cccError = "import-failure", this.dispatchEvent(new ErrorEvent("error", a)), console.error(a);
334
- return;
335
- }
336
- try {
337
- this.mode = this.getAttribute("shadow-open") == "false" ? "closed" : "open", await this.mount(), this.dispatchEvent(
338
- new CustomEvent("ccc-connected", {
339
- bubbles: !0,
340
- cancelable: !0,
341
- detail: {
342
- component: `${t}@${e}`,
343
- source: this.source
344
- }
345
- })
346
- ), this.dataset.cccReady = "true", delete this.dataset.cccError;
347
- } catch (a) {
348
- console.info(
349
- `<custom-code-component> uncaught error during mount from ${t}@${e}`
350
- ), console.error(a), this.dispatchEvent(new ErrorEvent("error", a)), this.dataset.cccError = "mount-error", delete this.dataset.cccReady;
351
- }
352
- try {
353
- this.tracking.init(this.id);
354
- } catch (a) {
355
- console.info(
356
- `Error initialising tracking on <custom-code-component> ${t}@${e}`
357
- ), console.error(a);
358
- }
300
+ const [i, a, r] = t.split("/").reverse();
301
+ if (!i || !a || !r)
302
+ return;
303
+ const h = this.getAttribute("id");
304
+ this.source = s ? `http://localhost:5173/src/${i}/index.jsx?id=${h}` : `https://www.ft.com/__component/${r}/${a}${e ? `@${e}` : "@latest"}/${i}/${i}.js?id=${h}`;
305
+ try {
306
+ this.app = await new Promise((l, u) => {
307
+ const m = setTimeout(() => {
308
+ this.dispatchEvent(
309
+ new CustomEvent("ccc-timeout", {
310
+ bubbles: !0,
311
+ cancelable: !0,
312
+ detail: {
313
+ component: `${t}@${e}`,
314
+ source: this.source
315
+ }
316
+ })
317
+ ), this.dataset.cccError = "import-timeout", delete this.dataset.cccReady;
318
+ }, Number(n));
319
+ import(
320
+ /* webpackIgnore: true */
321
+ this.source
322
+ /* @vite-ignore */
323
+ ).then(({ default: p }) => {
324
+ if (p)
325
+ clearTimeout(m), l(p);
326
+ else
327
+ throw new f(
328
+ "No component renderer default export found"
329
+ );
330
+ }).catch((p) => u(new f(p)));
331
+ });
332
+ } catch (l) {
333
+ console.error(
334
+ `<custom-code-component> error during import from ${t}@${e}`
335
+ ), delete this.dataset.cccReady, this.dataset.cccError = "import-failure", this.dispatchEvent(new ErrorEvent("error", l)), console.error(l);
336
+ return;
337
+ }
338
+ try {
339
+ this.mode = this.getAttribute("shadow-open") == "false" ? "closed" : "open", await this.mount(), this.dispatchEvent(
340
+ new CustomEvent("ccc-connected", {
341
+ bubbles: !0,
342
+ cancelable: !0,
343
+ detail: {
344
+ component: `${t}@${e}`,
345
+ source: this.source
346
+ }
347
+ })
348
+ ), this.dataset.cccReady = "true", delete this.dataset.cccError;
349
+ } catch (l) {
350
+ console.info(
351
+ `<custom-code-component> uncaught error during mount from ${t}@${e}`
352
+ ), console.error(l), this.dispatchEvent(new ErrorEvent("error", l)), this.dataset.cccError = "mount-error", delete this.dataset.cccReady;
353
+ }
354
+ try {
355
+ this.tracking.init(this.id);
356
+ } catch (l) {
357
+ console.info(
358
+ `Error initialising tracking on <custom-code-component> ${t}@${e}`
359
+ ), console.error(l);
359
360
  }
360
361
  }
361
362
  disconnectedCallback() {
@@ -369,13 +370,13 @@ class M extends HTMLElement {
369
370
  this.channel.port1.postMessage(t);
370
371
  }
371
372
  }
372
- const O = () => customElements.define("custom-code-component", M);
373
- customElements && !customElements.get("custom-code-component") && O();
374
- class p extends Error {
373
+ const R = () => customElements.define("custom-code-component", O);
374
+ customElements && !customElements.get("custom-code-component") && R();
375
+ class f extends Error {
375
376
  constructor(...t) {
376
- super(...t), Error.captureStackTrace && Error.captureStackTrace(this, p), this.name = "CCCImportError";
377
+ super(...t), Error.captureStackTrace && Error.captureStackTrace(this, f), this.name = "CCCImportError";
377
378
  }
378
379
  }
379
380
  export {
380
- O as init
381
+ R as init
381
382
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@financial-times/custom-code-component",
3
- "version": "1.9.9",
3
+ "version": "1.10.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -91,11 +91,15 @@ class FTCustomCodeComponent extends HTMLElement {
91
91
 
92
92
  if (!componentName || !componentRepo || !componentOrg) return;
93
93
 
94
+ const id = this.getAttribute('id');
95
+
96
+ // id querystring necessary to multiple allow components with the same source (same name and version number) to appear on the page correctly
94
97
  this.source = useLocalVersion
95
- ? `http://localhost:5173/src/${componentName}/index.jsx`
98
+ ? `http://localhost:5173/src/${componentName}/index.jsx?id=${id}`
96
99
  : `https://www.ft.com/__component/${componentOrg}/${componentRepo}${
97
100
  componentVersionRange ? `@${componentVersionRange}` : "@latest"
98
- }/${componentName}/${componentName}.js`;
101
+ }/${componentName}/${componentName}.js?id=${id}`;
102
+
99
103
 
100
104
  try {
101
105
  this.app = await new Promise((resolve, reject) => {