PageSourceSearch

https://www.zavzpret.com/lib/blocks/find-pharmacy/find-pharmacy.js

js zavzpret.com collected 2026-09-28 09:13:32 UTC 4,341 bytes, 133 lines download raw bytes

1import { FranklinBlock, getAvailableChildrenRow } from '../../scripts/lib-franklin.js';
2import { getFormData, getFormsEnv, FORMS_API_ENDPOINT_PROD, FORMS_API_ENDPOINT_UAT } from '../../scripts/lib-franklin/forms-service.js';
3
4const CONFIG_TOKEN_PROD_DEFAULT = 'paxlovid-pharma-finder-generic-prod';
5const CONFIG_TOKEN_UAT_DEFAULT = 'paxlovid-pharma-finder-generic';
6
7const DEFAULT_REQUESTED_SERVICE = 'PharmacyFinder';
8
9const isUat = () => getFormsEnv() !== 'production';
10
11const requestPharmacyFinder = async (token, requestedService) => {
12  const { csrfToken } = await getFormData(token);
13
14  const endpoint = isUat() ? FORMS_API_ENDPOINT_UAT : FORMS_API_ENDPOINT_PROD;
15
16  const resp = await fetch(endpoint, {
17    method: 'POST',
18    cache: 'no-cache',
19    headers: {
20      'Content-Type': 'application/json',
21      'x-config-token': token,
22    },
23    body: JSON.stringify({
24      requestedServices: [requestedService],
25      csrfToken,
26    }),
27  });
28
29  if (resp.status >= 400) {
30    throw new Error(`Error loading pharmacy finder: ${token}`);
31  }
32
33  return resp.json();
34};
35
36export default class FindPharmacy extends FranklinBlock {
37  constructor(blockName, block) {
38    super(blockName, block);
39    this.config = {};
40  }
41
42  async beforeBlockDataRead() {
43    const read = (name) => {
44      const row = getAvailableChildrenRow(this.block, name);
45      return row ? row.textContent.trim() : null;
46    };
47
48    this.config = {
49      heading: read('heading'),
50      requestedService: read('requested_service') || DEFAULT_REQUESTED_SERVICE,
51      configTokenProd: read('config_token_prod') || CONFIG_TOKEN_PROD_DEFAULT,
52      configTokenUat: read('config_token_uat') || CONFIG_TOKEN_UAT_DEFAULT,
53      formEndpointProd: read('form_endpoint_prod') || FORMS_API_ENDPOINT_PROD,
54      formEndpointUat: read('form_endpoint_uat') || FORMS_API_ENDPOINT_UAT,
55      esVariant: read('es_variant') === 'true',
56    };
57  }
58
59  resolveToken() {
60    return isUat() ? this.config.configTokenUat : this.config.configTokenProd;
61  }
62
63  renderIframe(iframeSrc) {
64    const wrapper = document.createElement('div');
65    wrapper.classList.add('core-find-pharmacy-iframe-wrapper');
66
67    const iframe = document.createElement('iframe');
68    iframe.classList.add('core-find-pharmacy-iframe');
69    iframe.setAttribute('title', this.config.heading || 'Find a Pharmacy');
70    iframe.setAttribute('loading', 'lazy');
71    iframe.src = iframeSrc;
72
73    wrapper.append(iframe);
74    return { wrapper, iframe };
75  }
76
77  // The embedded tool reports its own height; mirror it onto the iframe so there is no inner scrollbar.
78  static listenForContentHeight(iframe, iframeSrc) {
79    const iframeOrigin = (() => {
80      try {
81        return new URL(iframeSrc, window.location.href).origin;
82      } catch {
83        return null;
84      }
85    })();
86
87    window.addEventListener('message', (event) => {
88      if (iframeOrigin && event.origin !== iframeOrigin) return;
89      const { messageType, height } = event.data || {};
90      if (messageType === 'CONTENT_HEIGHT_UPDATED' && height) {
91        iframe.style.height = `${height}px`;
92      }
93    });
94  }
95
96  async afterBlockRender() {
97    this.block.textContent = '';
98
99    if (this.config.esVariant) {
100      this.block.classList.add('es');
101    }
102
103    if (this.config.heading) {
104      const heading = document.createElement('h2');
105      heading.classList.add('core-find-pharmacy-heading');
106      heading.textContent = this.config.heading;
107      this.block.append(heading);
108    }
109
110    try {
111      const token = this.resolveToken();
112      const response = await requestPharmacyFinder(token, this.config.requestedService);
113      const iframeSrc = response?.data?.[0]?.details?.url;
114
115      if (!iframeSrc) {
116        throw new Error('Pharmacy finder response did not include an iframe source');
117      }
118
119      const { wrapper, iframe } = this.renderIframe(iframeSrc);
120      this.block.append(wrapper);
121      FindPharmacy.listenForContentHeight(iframe, iframeSrc);
122    } catch (error) {
123      console.error('Error loading pharmacy finder', error);
124      this.block.classList.add('find-pharmacy-error');
125      const message = document.createElement('p');
126      message.textContent = error.message;
127      this.block.append(message);
128    }
129
130    this.block.classList.add('block-padding-standard');
131    this.block.classList.add('block-focus-visible');
132  }
133}

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.