PageSourceSearch

https://networkenglb.com/js/sanity-live.js?v=eef781f3

js networkenglb.com collected 2026-10-08 01:13:11 UTC 5,951 bytes, 147 lines download raw bytes

1/* ============================================================================
2   NETWORK ENGINEERING — live content refresh
3
4   data/products.js and data/news.js still ship and still render first, so the
5   catalogue is in the page on first paint (fast, works offline, visible to
6   crawlers) and the site keeps working if Sanity is unreachable.
7
8   On top of that this asks Sanity for the current content and — only if
9   something actually changed — swaps the globals and asks app.js to re-render.
10   Publishing in the Studio therefore shows up on the site immediately, with no
11   export and no upload.
12
13   Two CDN queries per page view, on the free plan's 1,000,000/month.
14
15   Load it AFTER app.js:
16     <script src="data/products.js"></script>
17     <script src="js/app.js"></script>
18     <script src="js/sanity-live.js"></script>
19
20   CORS is the gate. Sanity rejects a browser request from any origin not on
21   the project allowlist, and www.networkenglb.com does NOT redirect to the
22   apex — it is a separate origin. Both hostnames must be allowed or the site
23   silently stops updating on one of them. file:// can never work: its origin
24   is null. That is exactly why the shipped globals stay as the fallback.
25   ========================================================================== */
26(function () {
27  'use strict';
28
29  var PROJECT = 'rbyd9cc3';
30  var DATASET = 'production';
31  var IMG = '?w=1200&auto=format&fit=max';
32
33  if (typeof fetch !== 'function' || location.protocol === 'file:') return;
34
35  var Q_CONTENT =
36    '{"products": *[_type=="product" && published==true]|order(order asc, name asc){' +
37      'name, summary, description, specs, featured,' +
38      '"slug": slug.current,' +
39      '"brand": brand->slug.current,' +
40      '"brandName": brand->name,' +
41      '"categories": categories[]->key.current,' +
42      '"series": series->slug.current,' +
43      '"images": images[].asset->url,' +
44      '"datasheets": datasheets[]{title, url, "file": file.asset->url}},' +
45    '"categories": *[_type=="category"]|order(order asc, label asc){' +
46      'label, blurb, featured, "key": key.current},' +
47    '"brands": *[_type=="brand"]|order(order asc, name asc){' +
48      'name, tagline, blurb, representation, website,' +
49      '"key": slug.current, "logo": logo.asset->url},' +
50    '"news": *[_type=="newsPost" && published==true]|order(date desc, order asc){' +
51      'title, summary, body, date, sourceUrl,' +
52      '"slug": slug.current, "image": image.asset->url},' +
53    '"settings": *[_id=="siteSettings"][0]{' +
54      'title, tagline, founded, intro, phones, email, address, mapQuery,' +
55      '"productOverview": productOverview.asset->url}}';
56
57  function url(q) {
58    return 'https://' + PROJECT + '.apicdn.sanity.io/v2024-01-01/data/query/' +
59           DATASET + '?query=' + encodeURIComponent(q);
60  }
61
62  /* Same reshaping as sanity/export-content.mjs. The two must agree, or a page
63     would look different before and after this fetch lands. */
64  function shape(res) {
65    var products = (res.products || []).map(function (p) {
66      var docs = (p.datasheets || []).map(function (d) {
67        return {title: d.title, url: d.file || d.url};
68      }).filter(function (d) { return d.url; });
69      var out = {
70        slug: p.slug,
71        name: p.name,
72        summary: p.summary || '',
73        body: p.description || '',
74        specs: (p.specs || []).map(function (s) {
75          return {label: s.label, value: s.value};
76        }),
77        categories: p.categories || [],
78        brand: p.brand || '',
79        brandName: p.brandName || '',
80        images: (p.images || []).map(function (u) { return u + IMG; }),
81        docs: docs,
82        pdfs: docs.map(function (d) { return d.url; }),
83        models: []
84      };
85      if (p.series) out.parent = p.series;
86      if (p.featured) out.featured = true;
87      return out;
88    });
89
90    // build the series -> models backlinks the site reads
91    var bySlug = {};
92    products.forEach(function (p) { bySlug[p.slug] = p; });
93    products.forEach(function (p) {
94      if (p.parent && bySlug[p.parent]) {
95        bySlug[p.parent].models.push({slug: p.slug, name: p.name});
96      }
97    });
98
99    var categories = (res.categories || []).map(function (c) {
100      return {
101        key: c.key,
102        label: c.label,
103        blurb: c.blurb || '',
104        featured: c.featured !== false,
105        count: products.filter(function (p) {
106          return !p.parent && p.categories.indexOf(c.key) !== -1;
107        }).length
108      };
109    });
110
111    return {
112      products: products,
113      categories: categories,
114      brands: res.brands || [],
115      news: res.news || [],
116      settings: res.settings || {}
117    };
118  }
119
120  fetch(url(Q_CONTENT))
121    .then(function (r) {
122      // 403 here almost always means this origin is missing from the project's
123      // CORS allowlist rather than anything being wrong with the query.
124      if (!r.ok) throw new Error('Sanity ' + r.status);
125      return r.json();
126    })
127    .then(function (json) {
128      var next = shape(json.result || {});
129      if (!next.products.length) return;          // never blank the page
130
131      var changed = JSON.stringify(next.products) !== JSON.stringify(window.NE_PRODUCTS) ||
132                    JSON.stringify(next.categories) !== JSON.stringify(window.NE_CATEGORIES) ||
133                    JSON.stringify(next.news) !== JSON.stringify(window.NE_NEWS);
134
135      window.NE_PRODUCTS = next.products;
136      window.NE_CATEGORIES = next.categories;
137      window.NE_BRANDS = next.brands;
138      window.NE_NEWS = next.news;
139      window.NE_SITE = next.settings;
140
141      // the header and footer read NE_SITE / NE_BRANDS, so a phone number or a
142      // principal edited in the Studio lands on all seven pages
143      if (window.NE_CHROME) window.NE_CHROME.refresh();
144      if (changed && typeof window.NE_RENDER === 'function') window.NE_RENDER();
145    })
146    .catch(function () { /* shipped data stays on screen */ });
147})();

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.