PageSourceSearch

https://www.canadiantire.ca/etc.clientlibs/ctcweb-core/clientlibs/…-8a26ce7bf0f9d90abd2588d35e599144-lc.min.js

js canadiantire.ca collected 2026-10-01 08:39:06 UTC 5,292 bytes, 192 lines download raw bytes

1/**
2 * Accordion FAQ Schema Injector
3 *
4 * Reads per-accordion Question[] payloads from `data-faq-schema`
5 * rendered server-side by AccordionModel (Sling Model), then builds one
6 * merged FAQPage schema as <script type="application/ld+json"> in <head>.
7 *
8 * This approach is preferred over client-side DOM parsing because:
9 *   - The Sling Model has direct access to JCR node structure
10 *   - HTML stripping & text normalization happens once on the server
11 *   - The JS is trivially simple and extremely fast (~1ms)
12 *   - Works even if CSS hides or lazy-loads accordion panels
13 *
14 * Schema spec: https://schema.org/FAQPage
15 */
16(function () {
17  'use strict';
18
19  var WRAPPER_SELECTOR = '.accordion-wrapper';
20  var SCHEMA_SELECTOR  = '[data-cmp-is="accordion"][data-faq-schema]';
21  var SCRIPT_TYPE      = 'application/ld+json';
22  var SCRIPT_ID        = 'accordion-faq-schema-jsonld';
23  var HOOK_ATTR        = 'data-cmp-hook-accordion';
24  var HOOK_VALUE       = 'schema';
25
26  function parseSchema(raw) {
27    if (!raw) {
28      return null;
29    }
30    try {
31      var parsed = JSON.parse(raw);
32      if (!Array.isArray(parsed)) {
33        if (typeof console !== 'undefined') {
34          console.warn('[AccordionSchema] Expected Question[] payload in data-faq-schema.');
35        }
36        return null;
37      }
38      return parsed;
39    } catch (e) {
40      if (typeof console !== 'undefined') {
41        console.error('[AccordionSchema] Invalid JSON in data-faq-schema:', e);
42      }
43      return null;
44    }
45  }
46
47  function getSchemaIdFromLocation() {
48    return window.location.origin + window.location.pathname + '#faq';
49  }
50
51  function getAccordions() {
52    var wrappers = document.querySelectorAll(WRAPPER_SELECTOR);
53    if (!wrappers.length) {
54      return document.querySelectorAll(SCHEMA_SELECTOR);
55    }
56
57    var all = [];
58    for (var i = 0; i < wrappers.length; i++) {
59      var inside = wrappers[i].querySelectorAll(SCHEMA_SELECTOR);
60      for (var j = 0; j < inside.length; j++) {
61        all.push(inside[j]);
62      }
63    }
64    return all;
65  }
66
67  function mergeSchemas(entityArrays) {
68    if (!entityArrays.length) {
69      return null;
70    }
71
72    var mergedMainEntity = [];
73    var schemaId = getSchemaIdFromLocation();
74
75    for (var i = 0; i < entityArrays.length; i++) {
76      var entities = entityArrays[i];
77      if (!entities || !Array.isArray(entities)) {
78        continue;
79      }
80
81      for (var j = 0; j < entities.length; j++) {
82        var entity = entities[j];
83        var name = entity && entity.name ? String(entity.name).trim() : '';
84        var answer = entity && entity.acceptedAnswer && entity.acceptedAnswer.text
85          ? String(entity.acceptedAnswer.text).trim()
86          : '';
87
88        if (!name) {
89          continue;
90        }
91
92        mergedMainEntity.push(entity);
93      }
94    }
95
96    if (!mergedMainEntity.length) {
97      return null;
98    }
99
100    return {
101      '@context': 'https://schema.org',
102      '@type': 'FAQPage',
103      '@id': schemaId,
104      mainEntity: mergedMainEntity
105    };
106  }
107
108  function safeSerialize(schemaObject) {
109    try {
110      var payload = JSON.stringify(schemaObject);
111      // Validate that the produced payload is well-formed JSON before writing to DOM.
112      JSON.parse(payload);
113      return payload;
114    } catch (e) {
115      if (typeof console !== 'undefined') {
116        console.error('[AccordionSchema] Failed to serialize merged FAQ schema:', e);
117      }
118      return null;
119    }
120  }
121
122  function upsertSchemaScript(schemaObject) {
123    var existing = document.getElementById(SCRIPT_ID);
124
125    if (!schemaObject) {
126      if (existing && existing.parentNode) {
127        existing.parentNode.removeChild(existing);
128      }
129      return;
130    }
131
132    var payload = safeSerialize(schemaObject);
133    if (!payload) {
134      if (existing && existing.parentNode) {
135        existing.parentNode.removeChild(existing);
136      }
137      return;
138    }
139
140    if (!existing) {
141      existing = document.createElement('script');
142      existing.type = SCRIPT_TYPE;
143      existing.id = SCRIPT_ID;
144      existing.setAttribute(HOOK_ATTR, HOOK_VALUE);
145      document.head.appendChild(existing);
146    }
147
148    existing.textContent = payload;
149  }
150
151  // Collect all accordion schemas and inject a single merged FAQPage script.
152  function injectAll() {
153    var accordions = getAccordions();
154    var entityArrays = [];
155
156    for (var i = 0; i < accordions.length; i++) {
157      var raw = accordions[i].getAttribute('data-faq-schema');
158      var parsed = parseSchema(raw);
159      if (parsed) {
160        entityArrays.push(parsed);
161      }
162    }
163
164    upsertSchemaScript(mergeSchemas(entityArrays));
165  }
166
167  window.AccordionSchemaInjector = {
168    /**
169     * Re-scans the page and injects any schemas not yet in <head>.
170     * Call this after dynamically adding accordion markup.
171     */
172    refresh: function () {
173      injectAll();
174    },
175
176    injectForElement: function (accordionElement) {
177      if (!accordionElement) {
178        return;
179      }
180      // Single element injection now merges with all others to keep one schema script.
181      injectAll();
182    }
183  };
184
185  // Run on DOMContentLoaded (or immediately if DOM is already ready)
186  if (document.readyState === 'loading') {
187    document.addEventListener('DOMContentLoaded', injectAll);
188  } else {
189    injectAll();
190  }
191
192}());

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.