notify-me-wl 3.4.0 → 3.5.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.
package/README.md CHANGED
@@ -20,6 +20,7 @@ related guides.
20
20
  - Closes on the × button, clicking the backdrop, or pressing `Esc`; locks background scroll while open and respects `prefers-reduced-motion`.
21
21
  - Optional email/SMS marketing opt-ins.
22
22
  - Two display modes: a centred modal (default) or a right-hand slide-in panel — see [Display modes](#display-modes).
23
+ - Opens from any element marked `data-twc-notify-me` — PLP size buttons, "Size out of stock?" links — for the product it belongs to, with the right size pre-selected. See [Opening from product listing pages](#opening-from-product-listing-pages).
23
24
  - Two auth modes: Shopify App Proxy (default) or a merchant-supplied token — see [Configuration](#configuration).
24
25
  - Optional "Shop similar styles" section under the form, showing personalised product recommendations for the customer.
25
26
 
@@ -40,6 +41,7 @@ npm install # install dev dependencies
40
41
  npm run build # emit build/notify-me-wl.min.js
41
42
  npm run dev # rebuild on change (watch mode)
42
43
  npm run typecheck # type-check without emitting
44
+ npm run selftest # build, then run the harness pages in headless Chrome
43
45
  ```
44
46
 
45
47
  > **v2.0.0 path change:** the distributed bundle moved from `index.js` to
@@ -47,15 +49,22 @@ npm run typecheck # type-check without emitting
47
49
 
48
50
  ### Usage
49
51
 
50
- Add a button to your HTML where you want the widget to appear. The button should have an `id` of `popup-open`, a `data-fields` attribute with the fields you want to include in the form (as a JSON string), and a `data-type` attribute to specify the widget type (`notify-me` or `coming-soon`).
52
+ Add `<div id="notification-widget"></div>` once per page and put the configuration attributes below on it (or, as older installs do, on a `<button id="popup-open">` — its attributes win where both carry one). Anything marked `data-twc-notify-me` opens the widget; the legacy `#popup-open` button does too. `data-fields` is a JSON string of the fields to include, and `data-type` is `notify-me` or `coming-soon`.
51
53
 
52
54
  ```html
53
55
  <div id="notification-widget"></div>
54
56
  <button id="popup-open" data-fields='["mobile", "firstName", "lastName"]' data-type="notify-me">Open Popup</button>
55
57
  ```
56
58
 
59
+ `window.currentProduct` supplies the product on a product page. Its `variants` may
60
+ include Shopify's `available` flag — the widget then lists only out-of-stock sizes
61
+ itself — or be pre-filtered in Liquid as older installs do.
62
+
57
63
  ### Configuration
58
64
 
65
+ Every attribute below can be set on `#notification-widget` or on `#popup-open`. When
66
+ both carry the same attribute, `#popup-open` wins. `#popup-open` is optional.
67
+
59
68
  - `data-fields`: A JSON array specifying the optional fields to include in the form. Possible values are `"email"`, `"mobile"`, `"firstName"`, `"lastName"`, `"countryCode"`, and `"provinceCode"`. Defaults to `["email"]`. (A required Size selector is always shown and populated from the product's variants.)
60
69
  - `data-type`: Specifies the type of the widget. Possible values are `"notify-me"` and `"coming-soon"`.
61
70
  - `data-display`: How the widget presents itself. `"modal"` (default) is a centred dialog; `"panel"` slides in from the right edge at full height. Any other value falls back to `"modal"`. See [Display modes](#display-modes).
@@ -91,7 +100,7 @@ continue." message; submission is blocked until a token is available.
91
100
 
92
101
  #### `"token"`
93
102
 
94
- Sends a token you supply, from `data-access-token` on the open button or
103
+ Sends a token you supply, from `data-access-token` on `#notification-widget` (or `#popup-open`) or
95
104
  `window.TWC_ACCESS_TOKEN`. The attribute wins if both are present, and the `Bearer `
96
105
  prefix is added if you omit it.
97
106
 
@@ -141,7 +150,7 @@ sliding.
141
150
 
142
151
  Market and country are captured on every submission so interest can be reported per
143
152
  market. The values come from Liquid, which renders server-side, so the theme passes
144
- them to the widget as attributes on the open button:
153
+ them to the widget as attributes on `#notification-widget` (or the legacy `#popup-open` button):
145
154
 
146
155
  ```html
147
156
  <button id="popup-open"
@@ -167,6 +176,83 @@ When `data-fields` includes `"countryCode"`, the country the shopper picks in th
167
176
  takes precedence over `data-country-code`. The dropdown is pre-selected to the market's
168
177
  country, so leaving it untouched sends the market country either way.
169
178
 
179
+ ### Opening from product listing pages
180
+
181
+ Three attributes connect a theme to the widget. No theme JavaScript is needed.
182
+
183
+ | Attribute | Where | Meaning |
184
+ | --- | --- | --- |
185
+ | `data-twc-product` | A product container, e.g. the card | JSON `{ id, title, variants: [{ id, title, available }] }` |
186
+ | `data-twc-notify-me` | Any clickable element | Clicking it opens the widget |
187
+ | `data-variant-id` | Optional, on the same element | Variant to pre-select |
188
+
189
+ The widget finds the product from the clicked element's closest `data-twc-product`,
190
+ falling back to `window.currentProduct`. It listens at page level in the capture phase,
191
+ so cards added later (filters, infinite scroll) work, and a theme handler that stops
192
+ propagation does not block it. It calls `preventDefault()` on the click — `href="#"`
193
+ does not jump — but lets the theme's own handlers run.
194
+
195
+ Build the product JSON with a compact capture rather than `product | json`, which is
196
+ too heavy to repeat on every card, and escape it into a double-quoted attribute so a
197
+ title with a quote or apostrophe cannot break the markup:
198
+
199
+ ```liquid
200
+ {%- capture twc_product -%}
201
+ {"id":{{ product.id | json }},"title":{{ product.title | json }},"variants":[
202
+ {%- for v in product.variants -%}
203
+ {"id":{{ v.id | json }},"title":{{ v.title | json }},"available":{{ v.available | json }}}
204
+ {%- unless forloop.last -%},{%- endunless -%}
205
+ {%- endfor -%}]}
206
+ {%- endcapture -%}
207
+ <div class="product-card" data-twc-product="{{ twc_product | escape }}">
208
+ {%- for variant in product.variants -%}
209
+ <button type="button" data-variant-id="{{ variant.id }}"
210
+ {% unless variant.available %}data-twc-notify-me{% endunless %}>{{ variant.title }}</button>
211
+ {%- endfor -%}
212
+ <a href="#" data-twc-notify-me>Size out of stock?</a>
213
+ </div>
214
+ ```
215
+
216
+ Out-of-stock size buttons must not carry the `disabled` attribute: browsers do not
217
+ dispatch clicks on disabled buttons. Style them with a class or `aria-disabled="true"`
218
+ instead.
219
+
220
+ Render `#notification-widget` with the configuration once per page (e.g. in
221
+ `theme.liquid`), and load the script with `defer` or no attribute:
222
+
223
+ ```liquid
224
+ <div id="notification-widget"
225
+ data-tenant="your-tenant"
226
+ data-type="notify-me"
227
+ data-fields='["email"]'
228
+ data-currency="{{ cart.currency.iso_code }}"
229
+ data-market-id="{{ localization.market.id }}"
230
+ data-market-handle="{{ localization.market.handle }}"
231
+ data-country-code="{{ localization.country.iso_code }}"></div>
232
+ <script src="https://cdn.jsdelivr.net/npm/notify-me-wl@3.5.0/build/notify-me-wl.min.js" defer></script>
233
+ ```
234
+
235
+ A page must have exactly one `#notification-widget`. A second, empty one earlier in
236
+ the page would be the one the widget reads its configuration from.
237
+
238
+ ### Size pre-selection
239
+
240
+ The size list shows only out-of-stock sizes. On open, the first rule that yields one of
241
+ them wins:
242
+
243
+ 1. `data-variant-id` on the clicked element.
244
+ 2. Product page only (the product came from `window.currentProduct`): `?variant=` in
245
+ the URL, then the value of the first `select[name="id"]` inside a
246
+ `form[action*="/cart/add"]`. Hidden `input[name="id"]` fields are ignored — some
247
+ themes keep a stale one in an installments form. Never applied to a card, so page
248
+ state cannot leak into a listing.
249
+ 3. Exactly one out-of-stock size — that size.
250
+ 4. Otherwise, "Select a size".
251
+
252
+ A product with no out-of-stock sizes shows "All sizes are currently in stock." instead
253
+ of the form. A missing or malformed product shows "Product information is unavailable.
254
+ Please refresh the page."
255
+
170
256
  ### Shop similar styles
171
257
 
172
258
  When enabled, the widget shows a horizontally scrolling row of recommended products
@@ -181,7 +267,7 @@ customer-interest call, and the tenant doubles as the retailer id in the path.
181
267
 
182
268
  The API needs a customer email, resolved in this order:
183
269
 
184
- 1. `data-customer-email` on the open button (testing).
270
+ 1. `data-customer-email` on `#notification-widget` (or `#popup-open`) (testing).
185
271
  2. `window.customer.email` — a logged-in Shopify customer. The section then appears
186
272
  as soon as the modal opens.
187
273
  3. `window.customerEmail` — the same address exposed as a bare global, which is how
@@ -248,7 +334,7 @@ The recommendation profile tag is resolved in this order, first match winning:
248
334
 
249
335
  1. `profileTag` on `websiteRecommendations`.
250
336
  2. `tags` on `websiteRecommendations`.
251
- 3. `data-recommendations-profile` on the open button.
337
+ 3. `data-recommendations-profile` on `#notification-widget` (or `#popup-open`).
252
338
  4. The bundled default, `similar`.
253
339
 
254
340
  The tenant config outranks the attribute deliberately — it is the intended home for this
@@ -308,11 +394,12 @@ simply not rendered. It never blocks or alters the notify-me submission.
308
394
 
309
395
  ### How It Works
310
396
 
311
- 1. **Initialization**: The script listens for the `DOMContentLoaded` event to initialize the widget.
397
+ 1. **Initialization**: The script initializes on `DOMContentLoaded` (or immediately, if the page has already loaded), reading configuration from `#notification-widget` and `#popup-open`.
312
398
  2. **Styles Injection**: It injects the modal/form styles, all namespaced under `#twc-nm-overlay` so they don't collide with the host theme.
313
399
  3. **Modal Creation**: Creates the modal structure and appends it to the `notification-widget` div.
314
400
  4. **Dynamic Content**: Based on the `data-type`, it sets the appropriate copy and button text, and builds the fields listed in `data-fields` plus the always-present Size selector.
315
- 5. **Form Submission**: On submit, the button enters a "Sending…" state, the configured auth token is resolved, and the form data is POSTed to the API. The result is shown inline (success message then auto-close, or an error message); the popup is never closed on failure.
401
+ 5. **Opening**: A click on any `data-twc-notify-me` element (or `#popup-open`) resolves the product, rebuilds the Size selector with that product's out-of-stock sizes, and pre-selects one per [Size pre-selection](#size-pre-selection).
402
+ 6. **Form Submission**: On submit, the button enters a "Sending…" state, the configured auth token is resolved, and the form data is POSTed to the API. The result is shown inline (success message then auto-close, or an error message); the popup is never closed on failure.
316
403
 
317
404
  ### API Request Example
318
405
 
@@ -1 +1 @@
1
- !function(){"use strict";const e=[{code:"AF",name:"Afghanistan"},{code:"AX",name:"Åland Islands"},{code:"AL",name:"Albania"},{code:"DZ",name:"Algeria"},{code:"AS",name:"American Samoa"},{code:"AD",name:"Andorra"},{code:"AO",name:"Angola"},{code:"AI",name:"Anguilla"},{code:"AG",name:"Antigua and Barbuda"},{code:"AR",name:"Argentina"},{code:"AM",name:"Armenia"},{code:"AW",name:"Aruba"},{code:"AU",name:"Australia"},{code:"AT",name:"Austria"},{code:"AZ",name:"Azerbaijan"},{code:"BS",name:"Bahamas"},{code:"BH",name:"Bahrain"},{code:"BD",name:"Bangladesh"},{code:"BB",name:"Barbados"},{code:"BY",name:"Belarus"},{code:"BE",name:"Belgium"},{code:"BZ",name:"Belize"},{code:"BJ",name:"Benin"},{code:"BM",name:"Bermuda"},{code:"BT",name:"Bhutan"},{code:"BO",name:"Bolivia"},{code:"BA",name:"Bosnia and Herzegovina"},{code:"BW",name:"Botswana"},{code:"BR",name:"Brazil"},{code:"BN",name:"Brunei"},{code:"BG",name:"Bulgaria"},{code:"BF",name:"Burkina Faso"},{code:"BI",name:"Burundi"},{code:"CV",name:"Cabo Verde"},{code:"KH",name:"Cambodia"},{code:"CM",name:"Cameroon"},{code:"CA",name:"Canada"},{code:"KY",name:"Cayman Islands"},{code:"CF",name:"Central African Republic"},{code:"TD",name:"Chad"},{code:"CL",name:"Chile"},{code:"CN",name:"China"},{code:"CO",name:"Colombia"},{code:"KM",name:"Comoros"},{code:"CG",name:"Congo"},{code:"CD",name:"Congo, DR"},{code:"CK",name:"Cook Islands"},{code:"CR",name:"Costa Rica"},{code:"HR",name:"Croatia"},{code:"CU",name:"Cuba"},{code:"CW",name:"Curaçao"},{code:"CY",name:"Cyprus"},{code:"CZ",name:"Czech Republic"},{code:"DK",name:"Denmark"},{code:"DJ",name:"Djibouti"},{code:"DM",name:"Dominica"},{code:"DO",name:"Dominican Republic"},{code:"EC",name:"Ecuador"},{code:"EG",name:"Egypt"},{code:"SV",name:"El Salvador"},{code:"GQ",name:"Equatorial Guinea"},{code:"ER",name:"Eritrea"},{code:"EE",name:"Estonia"},{code:"SZ",name:"Eswatini"},{code:"ET",name:"Ethiopia"},{code:"FO",name:"Faroe Islands"},{code:"FJ",name:"Fiji"},{code:"FI",name:"Finland"},{code:"FR",name:"France"},{code:"GF",name:"French Guiana"},{code:"PF",name:"French Polynesia"},{code:"GA",name:"Gabon"},{code:"GM",name:"Gambia"},{code:"GE",name:"Georgia"},{code:"DE",name:"Germany"},{code:"GH",name:"Ghana"},{code:"GI",name:"Gibraltar"},{code:"GR",name:"Greece"},{code:"GL",name:"Greenland"},{code:"GD",name:"Grenada"},{code:"GP",name:"Guadeloupe"},{code:"GU",name:"Guam"},{code:"GT",name:"Guatemala"},{code:"GN",name:"Guinea"},{code:"GW",name:"Guinea-Bissau"},{code:"GY",name:"Guyana"},{code:"HT",name:"Haiti"},{code:"HN",name:"Honduras"},{code:"HK",name:"Hong Kong"},{code:"HU",name:"Hungary"},{code:"IS",name:"Iceland"},{code:"IN",name:"India"},{code:"ID",name:"Indonesia"},{code:"IR",name:"Iran"},{code:"IQ",name:"Iraq"},{code:"IE",name:"Ireland"},{code:"IM",name:"Isle of Man"},{code:"IL",name:"Israel"},{code:"IT",name:"Italy"},{code:"JM",name:"Jamaica"},{code:"JP",name:"Japan"},{code:"JE",name:"Jersey"},{code:"JO",name:"Jordan"},{code:"KZ",name:"Kazakhstan"},{code:"KE",name:"Kenya"},{code:"KI",name:"Kiribati"},{code:"KR",name:"Korea, South"},{code:"XK",name:"Kosovo"},{code:"KW",name:"Kuwait"},{code:"KG",name:"Kyrgyzstan"},{code:"LA",name:"Laos"},{code:"LV",name:"Latvia"},{code:"LB",name:"Lebanon"},{code:"LS",name:"Lesotho"},{code:"LR",name:"Liberia"},{code:"LY",name:"Libya"},{code:"LI",name:"Liechtenstein"},{code:"LT",name:"Lithuania"},{code:"LU",name:"Luxembourg"},{code:"MO",name:"Macao"},{code:"MG",name:"Madagascar"},{code:"MW",name:"Malawi"},{code:"MY",name:"Malaysia"},{code:"MV",name:"Maldives"},{code:"ML",name:"Mali"},{code:"MT",name:"Malta"},{code:"MH",name:"Marshall Islands"},{code:"MQ",name:"Martinique"},{code:"MR",name:"Mauritania"},{code:"MU",name:"Mauritius"},{code:"MX",name:"Mexico"},{code:"FM",name:"Micronesia"},{code:"MD",name:"Moldova"},{code:"MC",name:"Monaco"},{code:"MN",name:"Mongolia"},{code:"ME",name:"Montenegro"},{code:"MS",name:"Montserrat"},{code:"MA",name:"Morocco"},{code:"MZ",name:"Mozambique"},{code:"MM",name:"Myanmar"},{code:"NA",name:"Namibia"},{code:"NR",name:"Nauru"},{code:"NP",name:"Nepal"},{code:"NL",name:"Netherlands"},{code:"NC",name:"New Caledonia"},{code:"NZ",name:"New Zealand"},{code:"NI",name:"Nicaragua"},{code:"NE",name:"Niger"},{code:"NG",name:"Nigeria"},{code:"NU",name:"Niue"},{code:"MK",name:"North Macedonia"},{code:"NO",name:"Norway"},{code:"OM",name:"Oman"},{code:"PK",name:"Pakistan"},{code:"PW",name:"Palau"},{code:"PS",name:"Palestine"},{code:"PA",name:"Panama"},{code:"PG",name:"Papua New Guinea"},{code:"PY",name:"Paraguay"},{code:"PE",name:"Peru"},{code:"PH",name:"Philippines"},{code:"PL",name:"Poland"},{code:"PT",name:"Portugal"},{code:"PR",name:"Puerto Rico"},{code:"QA",name:"Qatar"},{code:"RE",name:"Réunion"},{code:"RO",name:"Romania"},{code:"RW",name:"Rwanda"},{code:"KN",name:"Saint Kitts and Nevis"},{code:"LC",name:"Saint Lucia"},{code:"VC",name:"Saint Vincent and the Grenadines"},{code:"WS",name:"Samoa"},{code:"SM",name:"San Marino"},{code:"SA",name:"Saudi Arabia"},{code:"SN",name:"Senegal"},{code:"RS",name:"Serbia"},{code:"SC",name:"Seychelles"},{code:"SL",name:"Sierra Leone"},{code:"SG",name:"Singapore"},{code:"SK",name:"Slovakia"},{code:"SI",name:"Slovenia"},{code:"SB",name:"Solomon Islands"},{code:"SO",name:"Somalia"},{code:"ZA",name:"South Africa"},{code:"SS",name:"South Sudan"},{code:"ES",name:"Spain"},{code:"LK",name:"Sri Lanka"},{code:"SD",name:"Sudan"},{code:"SR",name:"Suriname"},{code:"SE",name:"Sweden"},{code:"CH",name:"Switzerland"},{code:"SY",name:"Syria"},{code:"TW",name:"Taiwan"},{code:"TJ",name:"Tajikistan"},{code:"TZ",name:"Tanzania"},{code:"TH",name:"Thailand"},{code:"TL",name:"Timor-Leste"},{code:"TG",name:"Togo"},{code:"TO",name:"Tonga"},{code:"TT",name:"Trinidad and Tobago"},{code:"TN",name:"Tunisia"},{code:"TR",name:"Turkey"},{code:"TM",name:"Turkmenistan"},{code:"TC",name:"Turks and Caicos Islands"},{code:"TV",name:"Tuvalu"},{code:"UG",name:"Uganda"},{code:"UA",name:"Ukraine"},{code:"AE",name:"United Arab Emirates"},{code:"GB",name:"United Kingdom"},{code:"US",name:"United States"},{code:"UY",name:"Uruguay"},{code:"UZ",name:"Uzbekistan"},{code:"VU",name:"Vanuatu"},{code:"VE",name:"Venezuela"},{code:"VN",name:"Vietnam"},{code:"VG",name:"Virgin Islands, British"},{code:"VI",name:"Virgin Islands, U.S."},{code:"YE",name:"Yemen"},{code:"ZM",name:"Zambia"},{code:"ZW",name:"Zimbabwe"}];let n=null;async function t(e){if("proxy"===e.mode){const t=await async function(e){if(n)return n;try{const t=await fetch(`/apps/${e}/auth/token`,{method:"GET",headers:{Accept:"application/json"}}),o=await t.json().catch(()=>null);return t.ok&&o&&o.success&&o.data&&o.data.access_token?(n=o.data.access_token,n):null}catch(e){return console.error("Error fetching proxy access token:",e),null}}(e.proxyApp);return t?`Bearer ${t}`:null}return e.accessToken?/^Bearer /i.test(e.accessToken)?e.accessToken:`Bearer ${e.accessToken}`:(console.error('notify-me-wl: data-auth="token" requires an access token. Set data-access-token on the open button or window.TWC_ACCESS_TOKEN, or use data-auth="proxy" to obtain one via the Shopify App Proxy.'),null)}const o=new Map;function a(e){return"string"==typeof e?e.trim():""}async function c(e,n,c){const r=`${e}|${c||""}`,i=o.get(r);if(void 0!==i)return i;const d=await t(n);if(!d)return null;try{const n=await fetch("https://api.au-aws.thewishlist.io/services/eventcollector/api/v1/custom/configs/public",{method:"GET",headers:{Accept:"application/json",Authorization:d,"X-Twc-Tenant":e}});if(!n.ok)return console.error("Tenant config request failed:",n.status),null;const t=function(e,n=null){if(!e||"object"!=typeof e)return null;const t=e.websiteRecommendations;if(!t||"object"!=typeof t)return null;const o=a(t.engine).toUpperCase();if("TWC"===o)return{engine:"TWC"};if("ATHOS"===o){const e=a(t.siteIdentifier);return e?{engine:"ATHOS",siteIdentifier:e,profileTag:a(t.profileTag)||a(t.tags)||a(n)||"similar"}:null}return null}(await n.json().catch(()=>null),c);return o.set(r,t),t}catch(e){return console.error("Error fetching tenant config:",e),null}}function r(e){return"string"==typeof e?e.trim():""}function i(e){if("number"==typeof e&&Number.isFinite(e))return e;if("string"==typeof e){const n=parseFloat(e);if(Number.isFinite(n))return n}return null}function d(e){return Boolean(e.name&&e.imageUrl&&e.productUrl)}function s(e,n){const t=new Map;return e.forEach(e=>{const n=t.get(e.product.ref);n&&n.score>=e.score||t.set(e.product.ref,e)}),Array.from(t.values()).sort((e,n)=>n.score-e.score).slice(0,n).map(e=>e.product)}async function m(e){const{email:n,tenant:o,auth:a,count:c}=e,m=await t(a);if(!m)return null;const l=Math.min(3*c,20),u=`https://api.au-aws.thewishlist.io/services/recommendations/api/v1/recommendations/${encodeURIComponent(o)}/${encodeURIComponent(n)}?n=${l}`;try{const e=await fetch(u,{method:"GET",headers:{Accept:"application/json",Authorization:m,"X-Twc-Tenant":o}});if(!e.ok)return console.error("Recommendations request failed:",e.status),null;return function(e,n){const t=e&&e.recommendations;if(!Array.isArray(t))return[];const o=[];return t.forEach(e=>{var n;const t=e&&e.product;if(!t)return;const a=r(t.name),c=r(t.image_url),s=r(t.product_url);if(!d({name:a,imageUrl:c,productUrl:s}))return;const m=r(t.product_ref)||r(t.product_id)||s;o.push({score:"number"==typeof e.score?e.score:0,product:{id:r(t.product_id)||m,ref:m,name:a,imageUrl:c,productUrl:s,price:null!==(n=i(t.price))&&void 0!==n?n:0,originalPrice:i(t.original_price)}})}),s(o,n)}(await e.json().catch(()=>null),c)}catch(e){return console.error("Error fetching recommendations:",e),null}}async function l(e){const{engine:n,email:t,count:o,productId:a}=e,c=encodeURIComponent(n.siteIdentifier),m=[`tags=${encodeURIComponent(n.profileTag)}`,`limits=${o}`];a&&m.push(`products=${encodeURIComponent(a)}`),t&&m.push(`shopper=${encodeURIComponent(t)}`);const l=`https://${c}.a.searchspring.io/boost/${c}/recommend?`+m.join("&");try{const e=await fetch(l,{method:"GET",headers:{Accept:"application/json"}});if(!e.ok)return console.error("Athos recommendations request failed:",e.status),null;return function(e,n){const t=Array.isArray(e)?e[0]:e,o=t&&t.results;if(!Array.isArray(o))return[];const a=[];return o.forEach((e,n)=>{var t;const c=e&&e.mappings&&e.mappings.core;if(!c)return;const s=r(c.name),m=r(c.imageUrl)||r(c.thumbnailImageUrl),l=r(c.url);if(!d({name:s,imageUrl:m,productUrl:l}))return;const u=r(c.uid)||r(c.sku)||l,p=null!==(t=i(c.price))&&void 0!==t?t:0,w=i(c.msrp);a.push({score:o.length-n,product:{id:r(c.uid)||u,ref:u,name:s,imageUrl:m,productUrl:l,price:p,originalPrice:null!==w&&w>p?w:null}})}),s(a,n)}(await e.json().catch(()=>null),o)}catch(e){return console.error("Error fetching Athos recommendations:",e),null}}const u=new Map;const p="twc-nm-recs",w="twc-nm-recs-title";function f(e,n){if(n)try{return new Intl.NumberFormat(void 0,{style:"currency",currency:n}).format(e)}catch(e){}return new Intl.NumberFormat(void 0,{minimumFractionDigits:0,maximumFractionDigits:2}).format(e)}function h(){const e=document.createElement("section");e.id=p,e.className="twc-nm-recs",e.setAttribute("aria-labelledby",w);const n=document.createElement("h3");n.id=w,n.className="twc-nm-recs-title",n.textContent="Shop similar styles",e.appendChild(n);const t=document.createElement("div");return t.className="twc-nm-recs-row",function(e){let n=0,t=0,o=!1,a=!1,c=!1;e.addEventListener("pointerdown",r=>{"mouse"===r.pointerType&&0===r.button&&(o=!0,a=!1,c=!1,n=r.clientX,t=e.scrollLeft,e.classList.add("twc-nm-recs-row--dragging"))}),e.addEventListener("pointermove",c=>{if(!o)return;const r=c.clientX-n;!a&&Math.abs(r)<5||(a=!0,e.scrollLeft=t-r,c.preventDefault())});const r=()=>{o&&(o=!1,c=a,e.classList.remove("twc-nm-recs-row--dragging"))};e.addEventListener("pointerup",r),e.addEventListener("pointercancel",r),e.addEventListener("pointerleave",r),e.addEventListener("click",e=>{c&&(c=!1,e.preventDefault(),e.stopPropagation())},!0),e.addEventListener("dragstart",e=>{o&&a&&e.preventDefault()})}(t),e.appendChild(t),{section:e,row:t}}function g(){const e=document.createElement("div");e.className="twc-nm-rec twc-nm-rec--skeleton",e.setAttribute("aria-hidden","true");const n=document.createElement("div");n.className="twc-nm-rec-imgbox twc-nm-skel",e.appendChild(n);const t=document.createElement("span");t.className="twc-nm-skel twc-nm-skel-line",e.appendChild(t);const o=document.createElement("span");return o.className="twc-nm-skel twc-nm-skel-line twc-nm-skel-line--short",e.appendChild(o),e}function y(e,n,t){const o=e.querySelector(".twc-nm-recs-row");if(!o)return;const a=(c=t,window.Shopify&&window.Shopify.currency&&window.Shopify.currency.active||c||null);var c;const r=document.createDocumentFragment();n.forEach(e=>{r.appendChild(function(e,n){const t=document.createElement("a");t.className="twc-nm-rec",t.href=e.productUrl,t.target="_blank",t.rel="noopener noreferrer";const o=document.createElement("div");o.className="twc-nm-rec-imgbox";const a=document.createElement("img");a.className="twc-nm-rec-img",a.src=e.imageUrl,a.alt=e.name,a.loading="lazy",a.decoding="async",o.appendChild(a),t.appendChild(o);const c=document.createElement("span");c.className="twc-nm-rec-name",c.textContent=e.name,t.appendChild(c);const r=document.createElement("span");if(r.className="twc-nm-rec-price",null!==e.originalPrice&&e.originalPrice>e.price){const t=document.createElement("s");t.className="twc-nm-rec-was",t.textContent=f(e.originalPrice,n),r.appendChild(t),r.appendChild(document.createTextNode(" "))}return r.appendChild(document.createTextNode(f(e.price,n))),t.appendChild(r),t}(e,a))}),o.textContent="",o.appendChild(r),e.classList.remove("twc-nm-recs--loading"),e.removeAttribute("aria-busy")}function b(){const e=document.getElementById(p);e&&e.parentNode&&e.parentNode.removeChild(e)}const v={"notify-me":{text:"Register to receive a notification as soon as this item is back in stock",buttonText:"Notify Me"},"coming-soon":{text:"Register your interest to hear more about this item",buttonText:"Register Interest"}};function x(n){const{wrapper:o,openButton:a,config:r,productData:i,localization:d}=n,{fields:s,type:w,display:f,auth:x,tenant:C,recommendationsEnabled:k,recommendationsCount:E,recommendationsProfile:S,currency:A,customerEmail:T}=r,M=document.createElement("div");M.innerHTML='\n <div id="twc-nm-overlay" role="dialog" aria-modal="true" aria-labelledby="popup-title">\n <div class="twc-nm-card">\n <button type="button" id="popup-close" class="twc-nm-close" aria-label="Close"><svg viewBox="0 0 16 16" width="16" height="16" aria-hidden="true" focusable="false"><path d="M3 3l10 10M13 3L3 13" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" fill="none"/></svg></button>\n <h2 id="popup-title" class="twc-nm-title"></h2>\n <p id="popup-text" class="twc-nm-text"></p>\n <form id="popup-form" class="twc-nm-form">\n <div class="twc-nm-field">\n <label class="twc-nm-label" for="twc-nm-size">Size</label>\n <select class="twc-nm-select" id="twc-nm-size" name="select-size" required>\n <option value="" disabled selected>Select a size</option>\n </select>\n </div>\n </form>\n </div>\n </div>\n ',o.appendChild(M);const N=document.getElementById("twc-nm-overlay"),I=document.getElementById("popup-close"),L=document.getElementById("popup-title"),B=document.getElementById("popup-text"),G=document.querySelector("select[name='select-size']"),P=document.getElementById("popup-form");if(!(N&&I&&L&&B&&G&&P))return;"panel"===f&&N.classList.add("twc-nm--panel"),B.innerText=v[w].text;const U=e.map(e=>`<option value="${e.code}"${e.code===d.countryCode?" selected":""}>${e.name}</option>`).join(""),z=(e,n,t)=>`<div class="twc-nm-field"><label class="twc-nm-label" for="${e}">${n}</label>${t}</div>`,R={email:z("twc-nm-email","Email",'<input class="twc-nm-input" id="twc-nm-email" name="email" type="email" placeholder="you@example.com" required />'),mobile:z("twc-nm-mobile","Mobile",'<input class="twc-nm-input" id="twc-nm-mobile" name="mobile" type="tel" placeholder="Mobile number" required />'),firstName:z("twc-nm-firstname","First name",'<input class="twc-nm-input" id="twc-nm-firstname" name="firstName" type="text" placeholder="First name" required />'),lastName:z("twc-nm-lastname","Last name",'<input class="twc-nm-input" id="twc-nm-lastname" name="lastName" type="text" placeholder="Last name" required />'),countryCode:z("twc-nm-country","Country",`<select class="twc-nm-select" id="twc-nm-country" name="countryCode"><option value="">Select country</option>${U}</select>`),provinceCode:z("twc-nm-province","State / Province",`<input class="twc-nm-input" id="twc-nm-province" name="provinceCode" type="text" placeholder="State / province code" value="${d.provinceCode||""}" />`)};if(s.forEach(e=>{R[e]&&P.insertAdjacentHTML("beforeend",R[e])}),P.insertAdjacentHTML("beforeend",'<div class="twc-nm-checks">\n <label class="twc-nm-check"><input type="checkbox" name="mailList" /> Email me store updates</label>\n <label class="twc-nm-check"><input type="checkbox" name="mailListSms" /> Text me store updates</label>\n </div>'),P.insertAdjacentHTML("beforeend",'<div id="popup-status" class="twc-nm-status" role="status" aria-live="polite" hidden></div>'),P.insertAdjacentHTML("beforeend",`<button type="submit" class="twc-nm-submit">${v[w].buttonText}</button>`),T){const e=P.querySelector("input[name='email']");e&&(e.value=T)}const $=document.getElementById("popup-status"),D=P.querySelector(".twc-nm-submit");if(!$||!D)return;function K(e,n){$.textContent=n,$.className=`twc-nm-status twc-nm-status--${e}`,$.hidden=!1}function H(){$.hidden=!0,$.textContent=""}const F=e=>{const n=P.querySelector(e);return n?n.value:""},j=e=>{const n=P.querySelector(e);return!!n&&n.checked},O=N.querySelector(".twc-nm-card");let q=null;async function V(e){const n=await function(e,n,t){return c(e,n,t)}(C,x,S);if(!n)return!1;if(!N.classList.contains("is-open"))return!1;const t=function(e){const{section:n,row:t}=h();n.classList.add("twc-nm-recs--loading"),n.setAttribute("aria-busy","true");for(let n=0;n<e;n++)t.appendChild(g());return n}(E);O.appendChild(t);const o=await async function(e,n){const{email:t,tenant:o,auth:a,count:c,productId:r}=n,i=`${e.engine}|${o}|${t}|${c}|${r||""}`,d=u.get(i);if(d)return d;let s=null;return s="ATHOS"===e.engine?await l({engine:e,email:t,count:c,productId:r}):await m({email:t,tenant:o,auth:a,count:c}),null===s?[]:(u.set(i,s),s)}(n,{email:e,tenant:C,auth:x,count:E,productId:i?String(i.id):null});return!!t.isConnected&&(o.length?(y(t,o,A),!0):(b(),!1))}function _(e){if(!k||!O)return Promise.resolve(!1);if(q)return q;if(document.getElementById(p))return Promise.resolve(!0);const n=V(e);return q=n,n.then(e=>(q===n&&(q=null),e),e=>(q===n&&(q=null),console.error("Error rendering recommendations:",e),!1))}let W=!1,Z=null;function J(e){"Escape"===e.key&&Y()}function Y(){N.classList.remove("is-open"),document.body.style.overflow="",document.removeEventListener("keydown",J),H(),b(),q=null,Z&&"function"==typeof Z.focus&&Z.focus()}a.addEventListener("click",function(){if(Z=document.activeElement,N.classList.add("is-open"),document.body.style.overflow="hidden",document.addEventListener("keydown",J),!i)return P.hidden=!0,void K("error","Product information is unavailable. Please refresh the page.");L.textContent=i.title,W||((i.variants||[]).forEach(e=>{const n=document.createElement("option");n.value=e.title,n.textContent=e.title,G.appendChild(n)}),W=!0),T&&_(T);const e=P.querySelector("select, input");e&&e.focus()}),I.addEventListener("click",Y),N.addEventListener("click",e=>{e.target===N&&Y()}),P.addEventListener("submit",async e=>{e.preventDefault();const n=((null==i?void 0:i.variants)||[]).find(e=>e.title===G.value);if(!n)return void K("error","Please select a size.");const o={variantRef:n.id,email:F("input[name='email']"),subscribe:j("input[name='mailList']"),subscribeSms:j("input[name='mailListSms']")};if("coming-soon"===w?o.comingSoon=!0:o.notifyMe=!0,s.includes("firstName")&&(o.firstName=F("input[name='firstName']")),s.includes("lastName")&&(o.lastName=F("input[name='lastName']")),s.includes("mobile")&&(o.mobile=F("input[name='mobile']")),s.includes("countryCode")){const e=F("select[name='countryCode']");e&&(o.countryCode=e)}else d.countryCode&&(o.countryCode=d.countryCode);if(s.includes("provinceCode")){const e=F("input[name='provinceCode']");e&&(o.provinceCode=e)}else d.provinceCode&&(o.provinceCode=d.provinceCode);d.marketId&&(o.marketId=d.marketId),d.marketHandle&&(o.marketHandle=d.marketHandle),H();const a=D.textContent||"";D.disabled=!0,D.textContent="Sending…";let c={ok:!1,reason:"error"};try{c=await async function(e,n,o){const a=await t(n);if(!a)return{ok:!1,reason:"auth"};try{return(await fetch("https://api.au-aws.thewishlist.io/services/wsservice/api/wishlist/items/customerInterest",{method:"POST",headers:{"Content-Type":"application/json",Authorization:a,"X-Twc-Tenant":o},body:JSON.stringify(e)})).ok?{ok:!0}:{ok:!1,reason:"error"}}catch(e){return console.error("Error submitting form:",e),{ok:!1,reason:"error"}}}(o,x,C)}finally{D.disabled=!1,D.textContent=a}if(c.ok){K("success","You're on the list. We'll be in touch.");const e=T||F("input[name='email']");!!e&&await _(e)||setTimeout(Y,1500)}else"auth"===c.reason?K("error","proxy"===x.mode?"Please log in to your account to continue.":"Something went wrong. Please try again."):K("error","Something went wrong. Please try again.")})}function C(e){var n;const t=(null!=e?e:"").trim();if(!t)return null;const o=null!==(n=t.split("?")[0].split("/").pop())&&void 0!==n?n:"";return/^\d+$/.test(o)?o:null}function k(e){const n=(null!=e?e:"").trim();return/^[a-z0-9_-]+$/i.test(n)?n:null}function E(e){const n=(null!=e?e:"").trim();return/^[a-z]{2}$/i.test(n)?n.toUpperCase():null}function S(e){const n=e.getAttribute("data-customer-email");if(n&&n.trim())return n.trim();const t=window.customer&&window.customer.email;if(t&&t.trim())return t.trim();const o=window.customerEmail;return"string"==typeof o&&o.trim()?o.trim():null}document.addEventListener("DOMContentLoaded",()=>{const e=document.getElementById("notification-widget");if(!e)return;const n=document.getElementById("popup-open");if(!n)return;!function(){const e=document.createElement("style");e.type="text/css",e.innerText="\n .notification-btn {\n display: inline-block;\n width: 100%;\n padding: 12px 24px;\n background: #fff;\n border: 1px solid #111827;\n border-radius: 8px;\n color: #111827;\n font: inherit;\n text-align: center;\n text-transform: uppercase;\n cursor: pointer;\n }\n .notification-btn:hover {\n background: #111827;\n color: #fff;\n }\n #twc-nm-overlay {\n position: fixed;\n inset: 0;\n z-index: 99999;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 16px;\n background: rgba(17, 17, 17, 0.55);\n opacity: 0;\n visibility: hidden;\n transition: opacity 200ms ease;\n }\n #twc-nm-overlay.is-open {\n opacity: 1;\n visibility: visible;\n }\n #twc-nm-overlay * {\n box-sizing: border-box;\n }\n #twc-nm-overlay .twc-nm-card {\n position: relative;\n width: 100%;\n max-width: 400px;\n max-height: calc(100vh - 32px);\n overflow-y: auto;\n padding: 24px;\n background: #fff;\n color: #111827;\n border-radius: 12px;\n box-shadow: 0 10px 40px rgba(0, 0, 0, 0.18);\n font: inherit;\n transform: translateY(8px);\n transition: transform 200ms ease;\n }\n #twc-nm-overlay.is-open .twc-nm-card {\n transform: translateY(0);\n }\n /* Panel mode: same blocking backdrop, docked to the right edge and\n full height instead of centred. Declared after the modal rules so\n the shared properties are overridden. */\n #twc-nm-overlay.twc-nm--panel {\n align-items: stretch;\n justify-content: flex-end;\n padding: 0;\n }\n #twc-nm-overlay.twc-nm--panel .twc-nm-card {\n width: 420px;\n max-width: 100%;\n max-height: none;\n border-radius: 0;\n box-shadow: -10px 0 40px rgba(0, 0, 0, 0.18);\n transform: translateX(100%);\n }\n /* Needs the extra class to beat both the .is-open rule above and the\n panel's own resting transform. */\n #twc-nm-overlay.twc-nm--panel.is-open .twc-nm-card {\n transform: translateX(0);\n }\n @media (max-width: 480px) {\n #twc-nm-overlay.twc-nm--panel .twc-nm-card {\n width: 100%;\n }\n }\n #twc-nm-overlay .twc-nm-close {\n position: absolute;\n top: 12px;\n right: 12px;\n width: 32px;\n height: 32px;\n min-width: 0;\n min-height: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n margin: 0;\n padding: 0;\n border: none;\n background: transparent;\n box-shadow: none;\n border-radius: 6px;\n text-indent: 0;\n line-height: 1;\n color: #6b7280;\n opacity: 1;\n visibility: visible;\n cursor: pointer;\n }\n /* The icon is an inline SVG rather than a text glyph so host-theme\n button resets (font-size: 0, icon fonts, text-indent) can't hide it. */\n #twc-nm-overlay .twc-nm-close svg {\n display: block;\n width: 16px;\n height: 16px;\n flex: none;\n pointer-events: none;\n }\n #twc-nm-overlay .twc-nm-close::before,\n #twc-nm-overlay .twc-nm-close::after {\n content: none;\n }\n #twc-nm-overlay .twc-nm-close:hover {\n background: #f3f4f6;\n color: #111827;\n }\n #twc-nm-overlay .twc-nm-title {\n margin: 0 32px 4px 0;\n font-size: 18px;\n font-weight: 600;\n }\n #twc-nm-overlay .twc-nm-text {\n margin: 0 0 16px;\n font-size: 14px;\n line-height: 1.5;\n color: #6b7280;\n }\n #twc-nm-overlay .twc-nm-form {\n display: flex;\n flex-direction: column;\n gap: 14px;\n }\n #twc-nm-overlay .twc-nm-field {\n display: flex;\n flex-direction: column;\n gap: 6px;\n }\n #twc-nm-overlay .twc-nm-label {\n font-size: 13px;\n font-weight: 500;\n color: #374151;\n }\n #twc-nm-overlay .twc-nm-input,\n #twc-nm-overlay .twc-nm-select {\n width: 100%;\n padding: 10px 12px;\n font-size: 14px;\n font-family: inherit;\n color: #111827;\n background: #fff;\n border: 1px solid #e5e7eb;\n border-radius: 8px;\n }\n #twc-nm-overlay .twc-nm-input:focus,\n #twc-nm-overlay .twc-nm-select:focus {\n outline: none;\n border-color: #111827;\n box-shadow: 0 0 0 3px rgba(17, 24, 39, 0.08);\n }\n #twc-nm-overlay .twc-nm-select {\n appearance: none;\n -webkit-appearance: none;\n padding-right: 36px;\n cursor: pointer;\n background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath d='M2 4l4 4 4-4' stroke='%236b7280' stroke-width='1.5' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E\");\n background-repeat: no-repeat;\n background-position: right 12px center;\n }\n #twc-nm-overlay .twc-nm-checks {\n display: flex;\n flex-direction: column;\n gap: 10px;\n }\n #twc-nm-overlay .twc-nm-check {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 13px;\n color: #374151;\n cursor: pointer;\n }\n #twc-nm-overlay .twc-nm-check input {\n width: 16px;\n height: 16px;\n margin: 0;\n accent-color: #111827;\n }\n #twc-nm-overlay .twc-nm-status {\n font-size: 13px;\n line-height: 1.5;\n padding: 10px 12px;\n border-radius: 8px;\n }\n #twc-nm-overlay .twc-nm-status--error {\n color: #b91c1c;\n background: #fef2f2;\n }\n #twc-nm-overlay .twc-nm-status--success {\n color: #15803d;\n background: #f0fdf4;\n }\n #twc-nm-overlay .twc-nm-submit {\n width: 100%;\n padding: 12px 16px;\n font-size: 14px;\n font-weight: 600;\n font-family: inherit;\n color: #fff;\n background: #111827;\n border: none;\n border-radius: 8px;\n cursor: pointer;\n transition: background 150ms ease;\n }\n #twc-nm-overlay .twc-nm-submit:hover {\n background: #374151;\n }\n #twc-nm-overlay .twc-nm-submit:disabled {\n opacity: 0.6;\n cursor: default;\n }\n #twc-nm-overlay .twc-nm-recs {\n margin-top: 20px;\n padding-top: 20px;\n border-top: 1px solid #e5e7eb;\n }\n #twc-nm-overlay .twc-nm-recs-title {\n margin: 0 0 12px;\n font-size: 14px;\n font-weight: 600;\n color: #111827;\n }\n #twc-nm-overlay .twc-nm-recs-row {\n display: flex;\n gap: 12px;\n overflow-x: auto;\n scroll-snap-type: x mandatory;\n -webkit-overflow-scrolling: touch;\n padding-bottom: 8px;\n scrollbar-width: thin;\n }\n /* Only where a real pointer exists — the affordance is meaningless\n on touch, which pans the row natively. */\n @media (hover: hover) {\n #twc-nm-overlay .twc-nm-recs-row {\n cursor: grab;\n }\n }\n #twc-nm-overlay .twc-nm-recs-row--dragging,\n #twc-nm-overlay .twc-nm-recs-row--dragging .twc-nm-rec {\n cursor: grabbing;\n }\n #twc-nm-overlay .twc-nm-recs-row--dragging {\n /* Snapping fights a manually driven scrollLeft and feels sticky. */\n scroll-snap-type: none;\n user-select: none;\n -webkit-user-select: none;\n }\n #twc-nm-overlay .twc-nm-rec {\n flex: 0 0 132px;\n scroll-snap-align: start;\n display: flex;\n flex-direction: column;\n gap: 6px;\n color: inherit;\n text-decoration: none;\n }\n #twc-nm-overlay .twc-nm-rec:focus-visible {\n outline: 2px solid #111827;\n outline-offset: 2px;\n border-radius: 8px;\n }\n #twc-nm-overlay .twc-nm-rec-imgbox {\n aspect-ratio: 3 / 4;\n overflow: hidden;\n border-radius: 8px;\n background: #f3f4f6;\n }\n #twc-nm-overlay .twc-nm-rec-img {\n display: block;\n width: 100%;\n height: 100%;\n object-fit: cover;\n transition: opacity 150ms ease;\n }\n #twc-nm-overlay .twc-nm-rec:hover .twc-nm-rec-img {\n opacity: 0.85;\n }\n #twc-nm-overlay .twc-nm-rec-name {\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n font-size: 13px;\n line-height: 1.35;\n color: #111827;\n }\n #twc-nm-overlay .twc-nm-rec-price {\n font-size: 13px;\n font-weight: 600;\n color: #111827;\n }\n #twc-nm-overlay .twc-nm-rec-was {\n margin-right: 4px;\n font-weight: 400;\n color: #9ca3af;\n }\n #twc-nm-overlay .twc-nm-rec--skeleton {\n pointer-events: none;\n }\n #twc-nm-overlay .twc-nm-skel {\n background-color: #f3f4f6;\n background-image: linear-gradient(\n 90deg,\n #f3f4f6 0%,\n #e8eaee 50%,\n #f3f4f6 100%\n );\n background-size: 200% 100%;\n border-radius: 4px;\n animation: twc-nm-shimmer 1.3s ease-in-out infinite;\n }\n #twc-nm-overlay .twc-nm-rec-imgbox.twc-nm-skel {\n border-radius: 8px;\n }\n #twc-nm-overlay .twc-nm-skel-line {\n height: 12px;\n width: 100%;\n }\n #twc-nm-overlay .twc-nm-skel-line--short {\n width: 45%;\n }\n @keyframes twc-nm-shimmer {\n from {\n background-position: 200% 0;\n }\n to {\n background-position: -200% 0;\n }\n }\n @media (prefers-reduced-motion: reduce) {\n #twc-nm-overlay,\n #twc-nm-overlay .twc-nm-card,\n #twc-nm-overlay .twc-nm-rec-img {\n transition: none;\n }\n #twc-nm-overlay .twc-nm-card,\n #twc-nm-overlay.twc-nm--panel .twc-nm-card {\n transform: none;\n }\n #twc-nm-overlay .twc-nm-skel {\n animation: none;\n }\n }\n ",document.head.appendChild(e)}();const t=JSON.parse(n.getAttribute("data-fields")||'["email"]'),o=n.getAttribute("data-type")||"notify-me",a="panel"===n.getAttribute("data-display")?"panel":"modal",c=n.getAttribute("data-auth")||"proxy",r=n.getAttribute("data-tenant")||"victoria-woods",i=n.getAttribute("data-proxy-app")||"twc-sdk",d=n.getAttribute("data-access-token")||window.TWC_ACCESS_TOKEN||null,s="false"!==n.getAttribute("data-recommendations"),m=parseInt(n.getAttribute("data-recommendations-count")||"",10),l=Number.isFinite(m)&&m>0?m:8,u=(n.getAttribute("data-recommendations-profile")||"").trim()||null,p=n.getAttribute("data-currency")||null,w=function(e){var n,t,o;const a={countryCode:E(e.getAttribute("data-country-code")),provinceCode:null,marketId:C(e.getAttribute("data-market-id")),marketHandle:k(e.getAttribute("data-market-handle"))},c=window.Shopify;if(c){const e=null===(n=c.markets)||void 0===n?void 0:n.currentMarket;if(e&&(!a.countryCode&&e.countryCode&&(a.countryCode=e.countryCode),a.marketId||(a.marketId=C(String(null!==(t=e.id)&&void 0!==t?t:"")))),a.countryCode||"string"!=typeof c.country||(a.countryCode=c.country),!a.countryCode&&"string"==typeof c.locale){const e=c.locale.split("-");e.length>1&&(a.countryCode=e[e.length-1].toUpperCase())}}if(!a.countryCode||!a.provinceCode){const e=null===(o=window.customer)||void 0===o?void 0:o.default_address;e&&(!a.countryCode&&e.country_code&&(a.countryCode=e.country_code),!a.provinceCode&&e.province_code&&(a.provinceCode=e.province_code))}if(!a.countryCode){const e=(navigator.language||"").split("-");e.length>1&&(a.countryCode=e[e.length-1].toUpperCase())}return a}(n);x({wrapper:e,openButton:n,config:{fields:t,type:o,display:a,auth:{mode:c,proxyApp:i,accessToken:d},tenant:r,recommendationsEnabled:s,recommendationsCount:l,recommendationsProfile:u,currency:p,customerEmail:S(n)},productData:window.currentProduct,localization:w})})}();
1
+ !function(){"use strict";const e="data-twc-product";function n(e){if(!e||"object"!=typeof e)return!1;const n=e;return("number"==typeof n.id||"string"==typeof n.id)&&"string"==typeof n.title&&(void 0===n.variants||Array.isArray(n.variants))}function t(){const e=new URLSearchParams(window.location.search).get("variant"),n=document.querySelector('form[action*="/cart/add"] select[name="id"]');return[e,n?n.value:null]}const o=[{code:"AF",name:"Afghanistan"},{code:"AX",name:"Åland Islands"},{code:"AL",name:"Albania"},{code:"DZ",name:"Algeria"},{code:"AS",name:"American Samoa"},{code:"AD",name:"Andorra"},{code:"AO",name:"Angola"},{code:"AI",name:"Anguilla"},{code:"AG",name:"Antigua and Barbuda"},{code:"AR",name:"Argentina"},{code:"AM",name:"Armenia"},{code:"AW",name:"Aruba"},{code:"AU",name:"Australia"},{code:"AT",name:"Austria"},{code:"AZ",name:"Azerbaijan"},{code:"BS",name:"Bahamas"},{code:"BH",name:"Bahrain"},{code:"BD",name:"Bangladesh"},{code:"BB",name:"Barbados"},{code:"BY",name:"Belarus"},{code:"BE",name:"Belgium"},{code:"BZ",name:"Belize"},{code:"BJ",name:"Benin"},{code:"BM",name:"Bermuda"},{code:"BT",name:"Bhutan"},{code:"BO",name:"Bolivia"},{code:"BA",name:"Bosnia and Herzegovina"},{code:"BW",name:"Botswana"},{code:"BR",name:"Brazil"},{code:"BN",name:"Brunei"},{code:"BG",name:"Bulgaria"},{code:"BF",name:"Burkina Faso"},{code:"BI",name:"Burundi"},{code:"CV",name:"Cabo Verde"},{code:"KH",name:"Cambodia"},{code:"CM",name:"Cameroon"},{code:"CA",name:"Canada"},{code:"KY",name:"Cayman Islands"},{code:"CF",name:"Central African Republic"},{code:"TD",name:"Chad"},{code:"CL",name:"Chile"},{code:"CN",name:"China"},{code:"CO",name:"Colombia"},{code:"KM",name:"Comoros"},{code:"CG",name:"Congo"},{code:"CD",name:"Congo, DR"},{code:"CK",name:"Cook Islands"},{code:"CR",name:"Costa Rica"},{code:"HR",name:"Croatia"},{code:"CU",name:"Cuba"},{code:"CW",name:"Curaçao"},{code:"CY",name:"Cyprus"},{code:"CZ",name:"Czech Republic"},{code:"DK",name:"Denmark"},{code:"DJ",name:"Djibouti"},{code:"DM",name:"Dominica"},{code:"DO",name:"Dominican Republic"},{code:"EC",name:"Ecuador"},{code:"EG",name:"Egypt"},{code:"SV",name:"El Salvador"},{code:"GQ",name:"Equatorial Guinea"},{code:"ER",name:"Eritrea"},{code:"EE",name:"Estonia"},{code:"SZ",name:"Eswatini"},{code:"ET",name:"Ethiopia"},{code:"FO",name:"Faroe Islands"},{code:"FJ",name:"Fiji"},{code:"FI",name:"Finland"},{code:"FR",name:"France"},{code:"GF",name:"French Guiana"},{code:"PF",name:"French Polynesia"},{code:"GA",name:"Gabon"},{code:"GM",name:"Gambia"},{code:"GE",name:"Georgia"},{code:"DE",name:"Germany"},{code:"GH",name:"Ghana"},{code:"GI",name:"Gibraltar"},{code:"GR",name:"Greece"},{code:"GL",name:"Greenland"},{code:"GD",name:"Grenada"},{code:"GP",name:"Guadeloupe"},{code:"GU",name:"Guam"},{code:"GT",name:"Guatemala"},{code:"GN",name:"Guinea"},{code:"GW",name:"Guinea-Bissau"},{code:"GY",name:"Guyana"},{code:"HT",name:"Haiti"},{code:"HN",name:"Honduras"},{code:"HK",name:"Hong Kong"},{code:"HU",name:"Hungary"},{code:"IS",name:"Iceland"},{code:"IN",name:"India"},{code:"ID",name:"Indonesia"},{code:"IR",name:"Iran"},{code:"IQ",name:"Iraq"},{code:"IE",name:"Ireland"},{code:"IM",name:"Isle of Man"},{code:"IL",name:"Israel"},{code:"IT",name:"Italy"},{code:"JM",name:"Jamaica"},{code:"JP",name:"Japan"},{code:"JE",name:"Jersey"},{code:"JO",name:"Jordan"},{code:"KZ",name:"Kazakhstan"},{code:"KE",name:"Kenya"},{code:"KI",name:"Kiribati"},{code:"KR",name:"Korea, South"},{code:"XK",name:"Kosovo"},{code:"KW",name:"Kuwait"},{code:"KG",name:"Kyrgyzstan"},{code:"LA",name:"Laos"},{code:"LV",name:"Latvia"},{code:"LB",name:"Lebanon"},{code:"LS",name:"Lesotho"},{code:"LR",name:"Liberia"},{code:"LY",name:"Libya"},{code:"LI",name:"Liechtenstein"},{code:"LT",name:"Lithuania"},{code:"LU",name:"Luxembourg"},{code:"MO",name:"Macao"},{code:"MG",name:"Madagascar"},{code:"MW",name:"Malawi"},{code:"MY",name:"Malaysia"},{code:"MV",name:"Maldives"},{code:"ML",name:"Mali"},{code:"MT",name:"Malta"},{code:"MH",name:"Marshall Islands"},{code:"MQ",name:"Martinique"},{code:"MR",name:"Mauritania"},{code:"MU",name:"Mauritius"},{code:"MX",name:"Mexico"},{code:"FM",name:"Micronesia"},{code:"MD",name:"Moldova"},{code:"MC",name:"Monaco"},{code:"MN",name:"Mongolia"},{code:"ME",name:"Montenegro"},{code:"MS",name:"Montserrat"},{code:"MA",name:"Morocco"},{code:"MZ",name:"Mozambique"},{code:"MM",name:"Myanmar"},{code:"NA",name:"Namibia"},{code:"NR",name:"Nauru"},{code:"NP",name:"Nepal"},{code:"NL",name:"Netherlands"},{code:"NC",name:"New Caledonia"},{code:"NZ",name:"New Zealand"},{code:"NI",name:"Nicaragua"},{code:"NE",name:"Niger"},{code:"NG",name:"Nigeria"},{code:"NU",name:"Niue"},{code:"MK",name:"North Macedonia"},{code:"NO",name:"Norway"},{code:"OM",name:"Oman"},{code:"PK",name:"Pakistan"},{code:"PW",name:"Palau"},{code:"PS",name:"Palestine"},{code:"PA",name:"Panama"},{code:"PG",name:"Papua New Guinea"},{code:"PY",name:"Paraguay"},{code:"PE",name:"Peru"},{code:"PH",name:"Philippines"},{code:"PL",name:"Poland"},{code:"PT",name:"Portugal"},{code:"PR",name:"Puerto Rico"},{code:"QA",name:"Qatar"},{code:"RE",name:"Réunion"},{code:"RO",name:"Romania"},{code:"RW",name:"Rwanda"},{code:"KN",name:"Saint Kitts and Nevis"},{code:"LC",name:"Saint Lucia"},{code:"VC",name:"Saint Vincent and the Grenadines"},{code:"WS",name:"Samoa"},{code:"SM",name:"San Marino"},{code:"SA",name:"Saudi Arabia"},{code:"SN",name:"Senegal"},{code:"RS",name:"Serbia"},{code:"SC",name:"Seychelles"},{code:"SL",name:"Sierra Leone"},{code:"SG",name:"Singapore"},{code:"SK",name:"Slovakia"},{code:"SI",name:"Slovenia"},{code:"SB",name:"Solomon Islands"},{code:"SO",name:"Somalia"},{code:"ZA",name:"South Africa"},{code:"SS",name:"South Sudan"},{code:"ES",name:"Spain"},{code:"LK",name:"Sri Lanka"},{code:"SD",name:"Sudan"},{code:"SR",name:"Suriname"},{code:"SE",name:"Sweden"},{code:"CH",name:"Switzerland"},{code:"SY",name:"Syria"},{code:"TW",name:"Taiwan"},{code:"TJ",name:"Tajikistan"},{code:"TZ",name:"Tanzania"},{code:"TH",name:"Thailand"},{code:"TL",name:"Timor-Leste"},{code:"TG",name:"Togo"},{code:"TO",name:"Tonga"},{code:"TT",name:"Trinidad and Tobago"},{code:"TN",name:"Tunisia"},{code:"TR",name:"Turkey"},{code:"TM",name:"Turkmenistan"},{code:"TC",name:"Turks and Caicos Islands"},{code:"TV",name:"Tuvalu"},{code:"UG",name:"Uganda"},{code:"UA",name:"Ukraine"},{code:"AE",name:"United Arab Emirates"},{code:"GB",name:"United Kingdom"},{code:"US",name:"United States"},{code:"UY",name:"Uruguay"},{code:"UZ",name:"Uzbekistan"},{code:"VU",name:"Vanuatu"},{code:"VE",name:"Venezuela"},{code:"VN",name:"Vietnam"},{code:"VG",name:"Virgin Islands, British"},{code:"VI",name:"Virgin Islands, U.S."},{code:"YE",name:"Yemen"},{code:"ZM",name:"Zambia"},{code:"ZW",name:"Zimbabwe"}];let a=null;async function c(e){if("proxy"===e.mode){const n=await async function(e){if(a)return a;try{const n=await fetch(`/apps/${e}/auth/token`,{method:"GET",headers:{Accept:"application/json"}}),t=await n.json().catch(()=>null);return n.ok&&t&&t.success&&t.data&&t.data.access_token?(a=t.data.access_token,a):null}catch(e){return console.error("Error fetching proxy access token:",e),null}}(e.proxyApp);return n?`Bearer ${n}`:null}return e.accessToken?/^Bearer /i.test(e.accessToken)?e.accessToken:`Bearer ${e.accessToken}`:(console.error('notify-me-wl: data-auth="token" requires an access token. Set data-access-token on the open button or window.TWC_ACCESS_TOKEN, or use data-auth="proxy" to obtain one via the Shopify App Proxy.'),null)}const r=new Map;function i(e){return"string"==typeof e?e.trim():""}async function d(e,n,t){const o=`${e}|${t||""}`,a=r.get(o);if(void 0!==a)return a;const d=await c(n);if(!d)return null;try{const n=await fetch("https://api.au-aws.thewishlist.io/services/eventcollector/api/v1/custom/configs/public",{method:"GET",headers:{Accept:"application/json",Authorization:d,"X-Twc-Tenant":e}});if(!n.ok)return console.error("Tenant config request failed:",n.status),null;const a=function(e,n=null){if(!e||"object"!=typeof e)return null;const t=e.websiteRecommendations;if(!t||"object"!=typeof t)return null;const o=i(t.engine).toUpperCase();if("TWC"===o)return{engine:"TWC"};if("ATHOS"===o){const e=i(t.siteIdentifier);return e?{engine:"ATHOS",siteIdentifier:e,profileTag:i(t.profileTag)||i(t.tags)||i(n)||"similar"}:null}return null}(await n.json().catch(()=>null),t);return r.set(o,a),a}catch(e){return console.error("Error fetching tenant config:",e),null}}function s(e){return"string"==typeof e?e.trim():""}function m(e){if("number"==typeof e&&Number.isFinite(e))return e;if("string"==typeof e){const n=parseFloat(e);if(Number.isFinite(n))return n}return null}function l(e){return Boolean(e.name&&e.imageUrl&&e.productUrl)}function u(e,n){const t=new Map;return e.forEach(e=>{const n=t.get(e.product.ref);n&&n.score>=e.score||t.set(e.product.ref,e)}),Array.from(t.values()).sort((e,n)=>n.score-e.score).slice(0,n).map(e=>e.product)}async function p(e){const{email:n,tenant:t,auth:o,count:a}=e,r=await c(o);if(!r)return null;const i=Math.min(3*a,20),d=`https://api.au-aws.thewishlist.io/services/recommendations/api/v1/recommendations/${encodeURIComponent(t)}/${encodeURIComponent(n)}?n=${i}`;try{const e=await fetch(d,{method:"GET",headers:{Accept:"application/json",Authorization:r,"X-Twc-Tenant":t}});if(!e.ok)return console.error("Recommendations request failed:",e.status),null;return function(e,n){const t=e&&e.recommendations;if(!Array.isArray(t))return[];const o=[];return t.forEach(e=>{var n;const t=e&&e.product;if(!t)return;const a=s(t.name),c=s(t.image_url),r=s(t.product_url);if(!l({name:a,imageUrl:c,productUrl:r}))return;const i=s(t.product_ref)||s(t.product_id)||r;o.push({score:"number"==typeof e.score?e.score:0,product:{id:s(t.product_id)||i,ref:i,name:a,imageUrl:c,productUrl:r,price:null!==(n=m(t.price))&&void 0!==n?n:0,originalPrice:m(t.original_price)}})}),u(o,n)}(await e.json().catch(()=>null),a)}catch(e){return console.error("Error fetching recommendations:",e),null}}async function f(e){const{engine:n,email:t,count:o,productId:a}=e,c=encodeURIComponent(n.siteIdentifier),r=[`tags=${encodeURIComponent(n.profileTag)}`,`limits=${o}`];a&&r.push(`products=${encodeURIComponent(a)}`),t&&r.push(`shopper=${encodeURIComponent(t)}`);const i=`https://${c}.a.searchspring.io/boost/${c}/recommend?`+r.join("&");try{const e=await fetch(i,{method:"GET",headers:{Accept:"application/json"}});if(!e.ok)return console.error("Athos recommendations request failed:",e.status),null;return function(e,n){const t=Array.isArray(e)?e[0]:e,o=t&&t.results;if(!Array.isArray(o))return[];const a=[];return o.forEach((e,n)=>{var t;const c=e&&e.mappings&&e.mappings.core;if(!c)return;const r=s(c.name),i=s(c.imageUrl)||s(c.thumbnailImageUrl),d=s(c.url);if(!l({name:r,imageUrl:i,productUrl:d}))return;const u=s(c.uid)||s(c.sku)||d,p=null!==(t=m(c.price))&&void 0!==t?t:0,f=m(c.msrp);a.push({score:o.length-n,product:{id:s(c.uid)||u,ref:u,name:r,imageUrl:i,productUrl:d,price:p,originalPrice:null!==f&&f>p?f:null}})}),u(a,n)}(await e.json().catch(()=>null),o)}catch(e){return console.error("Error fetching Athos recommendations:",e),null}}const w=new Map;const h="twc-nm-recs",y="twc-nm-recs-title";function g(e,n){if(n)try{return new Intl.NumberFormat(void 0,{style:"currency",currency:n}).format(e)}catch(e){}return new Intl.NumberFormat(void 0,{minimumFractionDigits:0,maximumFractionDigits:2}).format(e)}function v(){const e=document.createElement("section");e.id=h,e.className="twc-nm-recs",e.setAttribute("aria-labelledby",y);const n=document.createElement("h3");n.id=y,n.className="twc-nm-recs-title",n.textContent="Shop similar styles",e.appendChild(n);const t=document.createElement("div");return t.className="twc-nm-recs-row",function(e){let n=0,t=0,o=!1,a=!1,c=!1;e.addEventListener("pointerdown",r=>{"mouse"===r.pointerType&&0===r.button&&(o=!0,a=!1,c=!1,n=r.clientX,t=e.scrollLeft,e.classList.add("twc-nm-recs-row--dragging"))}),e.addEventListener("pointermove",c=>{if(!o)return;const r=c.clientX-n;!a&&Math.abs(r)<5||(a=!0,e.scrollLeft=t-r,c.preventDefault())});const r=()=>{o&&(o=!1,c=a,e.classList.remove("twc-nm-recs-row--dragging"))};e.addEventListener("pointerup",r),e.addEventListener("pointercancel",r),e.addEventListener("pointerleave",r),e.addEventListener("click",e=>{c&&(c=!1,e.preventDefault(),e.stopPropagation())},!0),e.addEventListener("dragstart",e=>{o&&a&&e.preventDefault()})}(t),e.appendChild(t),{section:e,row:t}}function b(){const e=document.createElement("div");e.className="twc-nm-rec twc-nm-rec--skeleton",e.setAttribute("aria-hidden","true");const n=document.createElement("div");n.className="twc-nm-rec-imgbox twc-nm-skel",e.appendChild(n);const t=document.createElement("span");t.className="twc-nm-skel twc-nm-skel-line",e.appendChild(t);const o=document.createElement("span");return o.className="twc-nm-skel twc-nm-skel-line twc-nm-skel-line--short",e.appendChild(o),e}function x(e,n,t){const o=e.querySelector(".twc-nm-recs-row");if(!o)return;const a=(c=t,window.Shopify&&window.Shopify.currency&&window.Shopify.currency.active||c||null);var c;const r=document.createDocumentFragment();n.forEach(e=>{r.appendChild(function(e,n){const t=document.createElement("a");t.className="twc-nm-rec",t.href=e.productUrl,t.target="_blank",t.rel="noopener noreferrer";const o=document.createElement("div");o.className="twc-nm-rec-imgbox";const a=document.createElement("img");a.className="twc-nm-rec-img",a.src=e.imageUrl,a.alt=e.name,a.loading="lazy",a.decoding="async",o.appendChild(a),t.appendChild(o);const c=document.createElement("span");c.className="twc-nm-rec-name",c.textContent=e.name,t.appendChild(c);const r=document.createElement("span");if(r.className="twc-nm-rec-price",null!==e.originalPrice&&e.originalPrice>e.price){const t=document.createElement("s");t.className="twc-nm-rec-was",t.textContent=g(e.originalPrice,n),r.appendChild(t),r.appendChild(document.createTextNode(" "))}return r.appendChild(document.createTextNode(g(e.price,n))),t.appendChild(r),t}(e,a))}),o.textContent="",o.appendChild(r),e.classList.remove("twc-nm-recs--loading"),e.removeAttribute("aria-busy")}function C(){const e=document.getElementById(h);e&&e.parentNode&&e.parentNode.removeChild(e)}const k={"notify-me":{text:"Register to receive a notification as soon as this item is back in stock",buttonText:"Notify Me"},"coming-soon":{text:"Register your interest to hear more about this item",buttonText:"Register Interest"}};function S(a){const{wrapper:r,config:i,localization:s}=a,{fields:m,type:l,display:u,auth:y,tenant:g,recommendationsEnabled:S,recommendationsCount:E,recommendationsProfile:T,currency:A,customerEmail:M}=i,N=document.createElement("div");N.innerHTML='\n <div id="twc-nm-overlay" role="dialog" aria-modal="true" aria-labelledby="popup-title">\n <div class="twc-nm-card">\n <button type="button" id="popup-close" class="twc-nm-close" aria-label="Close"><svg viewBox="0 0 16 16" width="16" height="16" aria-hidden="true" focusable="false"><path d="M3 3l10 10M13 3L3 13" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" fill="none"/></svg></button>\n <h2 id="popup-title" class="twc-nm-title"></h2>\n <p id="popup-text" class="twc-nm-text"></p>\n <div id="popup-notice" class="twc-nm-status" role="status" aria-live="polite" hidden></div>\n <form id="popup-form" class="twc-nm-form">\n <div class="twc-nm-field">\n <label class="twc-nm-label" for="twc-nm-size">Size</label>\n <select class="twc-nm-select" id="twc-nm-size" name="select-size" required>\n <option value="" disabled selected>Select a size</option>\n </select>\n </div>\n </form>\n </div>\n </div>\n ',r.appendChild(N);const L=document.getElementById("twc-nm-overlay"),I=document.getElementById("popup-close"),B=document.getElementById("popup-title"),G=document.getElementById("popup-text"),P=document.querySelector("select[name='select-size']"),U=document.getElementById("popup-form"),z=document.getElementById("popup-notice");if(!(L&&I&&B&&G&&P&&U&&z))return null;"panel"===u&&L.classList.add("twc-nm--panel"),G.innerText=k[l].text;const R=o.map(e=>`<option value="${e.code}"${e.code===s.countryCode?" selected":""}>${e.name}</option>`).join(""),$=(e,n,t)=>`<div class="twc-nm-field"><label class="twc-nm-label" for="${e}">${n}</label>${t}</div>`,H={email:$("twc-nm-email","Email",'<input class="twc-nm-input" id="twc-nm-email" name="email" type="email" placeholder="you@example.com" required />'),mobile:$("twc-nm-mobile","Mobile",'<input class="twc-nm-input" id="twc-nm-mobile" name="mobile" type="tel" placeholder="Mobile number" required />'),firstName:$("twc-nm-firstname","First name",'<input class="twc-nm-input" id="twc-nm-firstname" name="firstName" type="text" placeholder="First name" required />'),lastName:$("twc-nm-lastname","Last name",'<input class="twc-nm-input" id="twc-nm-lastname" name="lastName" type="text" placeholder="Last name" required />'),countryCode:$("twc-nm-country","Country",`<select class="twc-nm-select" id="twc-nm-country" name="countryCode"><option value="">Select country</option>${R}</select>`),provinceCode:$("twc-nm-province","State / Province",`<input class="twc-nm-input" id="twc-nm-province" name="provinceCode" type="text" placeholder="State / province code" value="${s.provinceCode||""}" />`)};if(m.forEach(e=>{H[e]&&U.insertAdjacentHTML("beforeend",H[e])}),U.insertAdjacentHTML("beforeend",'<div class="twc-nm-checks">\n <label class="twc-nm-check"><input type="checkbox" name="mailList" /> Email me store updates</label>\n <label class="twc-nm-check"><input type="checkbox" name="mailListSms" /> Text me store updates</label>\n </div>'),U.insertAdjacentHTML("beforeend",'<div id="popup-status" class="twc-nm-status" role="status" aria-live="polite" hidden></div>'),U.insertAdjacentHTML("beforeend",`<button type="submit" class="twc-nm-submit">${k[l].buttonText}</button>`),M){const e=U.querySelector("input[name='email']");e&&(e.value=M)}const K=document.getElementById("popup-status"),D=U.querySelector(".twc-nm-submit");if(!K||!D)return null;function F(e,n){K.textContent=n,K.className=`twc-nm-status twc-nm-status--${e}`,K.hidden=!1}function j(){K.hidden=!0,K.textContent=""}function O(e,n){U.hidden=!0,z.textContent=n,z.className=`twc-nm-status twc-nm-status--${e}`,z.hidden=!1}const q=e=>{const n=U.querySelector(e);return n?n.value:""},V=e=>{const n=U.querySelector(e);return!!n&&n.checked},_=L.querySelector(".twc-nm-card");let J=null,W=[],Z=null;async function Y(e){const n=await function(e,n,t){return d(e,n,t)}(g,y,T);if(!n)return!1;if(!L.classList.contains("is-open"))return!1;const t=function(e){const{section:n,row:t}=v();n.classList.add("twc-nm-recs--loading"),n.setAttribute("aria-busy","true");for(let n=0;n<e;n++)t.appendChild(b());return n}(E);_.appendChild(t);const o=await async function(e,n){const{email:t,tenant:o,auth:a,count:c,productId:r}=n,i=`${e.engine}|${o}|${t}|${c}|${r||""}`,d=w.get(i);if(d)return d;let s=null;return s="ATHOS"===e.engine?await f({engine:e,email:t,count:c,productId:r}):await p({email:t,tenant:o,auth:a,count:c}),null===s?[]:(w.set(i,s),s)}(n,{email:e,tenant:g,auth:y,count:E,productId:J?String(J.id):null});return!!t.isConnected&&(o.length?(x(t,o,A),!0):(C(),!1))}function X(e){if(!S||!_)return Promise.resolve(!1);if(Z)return Z;if(document.getElementById(h))return Promise.resolve(!0);const n=Y(e);return Z=n,n.then(e=>(Z===n&&(Z=null),e),e=>(Z===n&&(Z=null),console.error("Error rendering recommendations:",e),!1))}let Q=null,ee=0;function ne(e){"Escape"===e.key&&te()}function te(){ee++,L.classList.remove("is-open"),document.body.style.overflow="",document.removeEventListener("keydown",ne),j(),C(),Z=null,Q&&"function"==typeof Q.focus&&Q.focus()}return I.addEventListener("click",te),L.addEventListener("click",e=>{e.target===L&&te()}),U.addEventListener("submit",async e=>{e.preventDefault();const n=W.find(e=>String(e.id)===P.value);if(!n)return void F("error","Please select a size.");const t={variantRef:n.id,email:q("input[name='email']"),subscribe:V("input[name='mailList']"),subscribeSms:V("input[name='mailListSms']")};if("coming-soon"===l?t.comingSoon=!0:t.notifyMe=!0,m.includes("firstName")&&(t.firstName=q("input[name='firstName']")),m.includes("lastName")&&(t.lastName=q("input[name='lastName']")),m.includes("mobile")&&(t.mobile=q("input[name='mobile']")),m.includes("countryCode")){const e=q("select[name='countryCode']");e&&(t.countryCode=e)}else s.countryCode&&(t.countryCode=s.countryCode);if(m.includes("provinceCode")){const e=q("input[name='provinceCode']");e&&(t.provinceCode=e)}else s.provinceCode&&(t.provinceCode=s.provinceCode);s.marketId&&(t.marketId=s.marketId),s.marketHandle&&(t.marketHandle=s.marketHandle),j();const o=ee,a=D.textContent||"";D.disabled=!0,D.textContent="Sending…";let r={ok:!1,reason:"error"};try{r=await async function(e,n,t){const o=await c(n);if(!o)return{ok:!1,reason:"auth"};try{return(await fetch("https://api.au-aws.thewishlist.io/services/wsservice/api/wishlist/items/customerInterest",{method:"POST",headers:{"Content-Type":"application/json",Authorization:o,"X-Twc-Tenant":t},body:JSON.stringify(e)})).ok?{ok:!0}:{ok:!1,reason:"error"}}catch(e){return console.error("Error submitting form:",e),{ok:!1,reason:"error"}}}(t,y,g)}finally{o===ee&&(D.disabled=!1,D.textContent=a)}if(o===ee)if(r.ok){F("success","You're on the list. We'll be in touch.");const e=M||q("input[name='email']");!!e&&await X(e)||o!==ee||setTimeout(()=>{o===ee&&te()},1500)}else"auth"===r.reason?F("error","proxy"===y.mode?"Please log in to your account to continue.":"Something went wrong. Please try again."):F("error","Something went wrong. Please try again.")}),{open:function(o){ee++,D.disabled=!1,D.textContent=k[l].buttonText,j(),U.hidden=!1,z.hidden=!0,z.textContent="",C(),Z=null,L.classList.contains("is-open")||(Q=document.activeElement),L.classList.add("is-open"),document.body.style.overflow="hidden",document.addEventListener("keydown",ne);const a=function(t){const o=t?t.closest(`[${e}]`):null;if(o){try{const t=JSON.parse(o.getAttribute(e)||"");if(n(t))return{ok:!0,product:t,source:"card"}}catch(e){}return console.error(`notify-me-wl: invalid ${e} JSON on`,o),{ok:!1}}const a=window.currentProduct;return n(a)?{ok:!0,product:a,source:"page"}:{ok:!1}}(o);if(!a.ok)return J=null,W=[],B.textContent="",void O("error","Product information is unavailable. Please refresh the page.");if(J=a.product,W=(J.variants||[]).filter(e=>!!e&&("number"==typeof e.id||"string"==typeof e.id)&&"string"==typeof e.title&&!0!==e.available),B.textContent=J.title,!W.length)return void O("info","All sizes are currently in stock.");const c=o?o.getAttribute("data-variant-id"):null,r="page"===a.source?[c,...t()]:[c];var i,d;i=W,d=function(e,n){const t=e.map(e=>String(e.id));for(const e of n){const n=e?e.trim():"";if(n&&t.includes(n))return n}return 1===t.length?t[0]:null}(W,r),P.innerHTML='<option value="" disabled selected>Select a size</option>',i.forEach(e=>{const n=document.createElement("option");n.value=String(e.id),n.textContent=e.title,P.appendChild(n)}),d&&(P.value=d),M&&X(M);const s=U.querySelector("select, input");s&&s.focus()}}}function E(e){var n;const t=(null!=e?e:"").trim();if(!t)return null;const o=null!==(n=t.split("?")[0].split("/").pop())&&void 0!==n?n:"";return/^\d+$/.test(o)?o:null}function T(e){const n=(null!=e?e:"").trim();return/^[a-z0-9_-]+$/i.test(n)?n:null}function A(e){const n=(null!=e?e:"").trim();return/^[a-z]{2}$/i.test(n)?n.toUpperCase():null}function M(e){var n,t,o;const a={countryCode:A(e("data-country-code")),provinceCode:null,marketId:E(e("data-market-id")),marketHandle:T(e("data-market-handle"))},c=window.Shopify;if(c){const e=null===(n=c.markets)||void 0===n?void 0:n.currentMarket;if(e&&(!a.countryCode&&e.countryCode&&(a.countryCode=e.countryCode),a.marketId||(a.marketId=E(String(null!==(t=e.id)&&void 0!==t?t:"")))),a.countryCode||"string"!=typeof c.country||(a.countryCode=c.country),!a.countryCode&&"string"==typeof c.locale){const e=c.locale.split("-");e.length>1&&(a.countryCode=e[e.length-1].toUpperCase())}}if(!a.countryCode||!a.provinceCode){const e=null===(o=window.customer)||void 0===o?void 0:o.default_address;e&&(!a.countryCode&&e.country_code&&(a.countryCode=e.country_code),!a.provinceCode&&e.province_code&&(a.provinceCode=e.province_code))}if(!a.countryCode){const e=(navigator.language||"").split("-");e.length>1&&(a.countryCode=e[e.length-1].toUpperCase())}return a}function N(e){const n=e("data-customer-email");if(n&&n.trim())return n.trim();const t=window.customer&&window.customer.email;if(t&&t.trim())return t.trim();const o=window.customerEmail;return"string"==typeof o&&o.trim()?o.trim():null}const L="[data-twc-notify-me]";function I(){const e=document.getElementById("notification-widget");if(!e)return;const n=document.getElementById("popup-open"),t=(o=n,a=e,e=>o&&o.hasAttribute(e)?o.getAttribute(e):a.getAttribute(e));var o,a;!function(){const e=document.createElement("style");e.type="text/css",e.innerText="\n .notification-btn {\n display: inline-block;\n width: 100%;\n padding: 12px 24px;\n background: #fff;\n border: 1px solid #111827;\n border-radius: 8px;\n color: #111827;\n font: inherit;\n text-align: center;\n text-transform: uppercase;\n cursor: pointer;\n }\n .notification-btn:hover {\n background: #111827;\n color: #fff;\n }\n #twc-nm-overlay {\n position: fixed;\n inset: 0;\n z-index: 99999;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 16px;\n background: rgba(17, 17, 17, 0.55);\n opacity: 0;\n visibility: hidden;\n transition: opacity 200ms ease;\n }\n #twc-nm-overlay.is-open {\n opacity: 1;\n visibility: visible;\n }\n #twc-nm-overlay * {\n box-sizing: border-box;\n }\n #twc-nm-overlay .twc-nm-card {\n position: relative;\n width: 100%;\n max-width: 400px;\n max-height: calc(100vh - 32px);\n overflow-y: auto;\n padding: 24px;\n background: #fff;\n color: #111827;\n border-radius: 12px;\n box-shadow: 0 10px 40px rgba(0, 0, 0, 0.18);\n font: inherit;\n transform: translateY(8px);\n transition: transform 200ms ease;\n }\n #twc-nm-overlay.is-open .twc-nm-card {\n transform: translateY(0);\n }\n /* Panel mode: same blocking backdrop, docked to the right edge and\n full height instead of centred. Declared after the modal rules so\n the shared properties are overridden. */\n #twc-nm-overlay.twc-nm--panel {\n align-items: stretch;\n justify-content: flex-end;\n padding: 0;\n }\n #twc-nm-overlay.twc-nm--panel .twc-nm-card {\n width: 420px;\n max-width: 100%;\n max-height: none;\n border-radius: 0;\n box-shadow: -10px 0 40px rgba(0, 0, 0, 0.18);\n transform: translateX(100%);\n }\n /* Needs the extra class to beat both the .is-open rule above and the\n panel's own resting transform. */\n #twc-nm-overlay.twc-nm--panel.is-open .twc-nm-card {\n transform: translateX(0);\n }\n @media (max-width: 480px) {\n #twc-nm-overlay.twc-nm--panel .twc-nm-card {\n width: 100%;\n }\n }\n #twc-nm-overlay .twc-nm-close {\n position: absolute;\n top: 12px;\n right: 12px;\n width: 32px;\n height: 32px;\n min-width: 0;\n min-height: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n margin: 0;\n padding: 0;\n border: none;\n background: transparent;\n box-shadow: none;\n border-radius: 6px;\n text-indent: 0;\n line-height: 1;\n color: #6b7280;\n opacity: 1;\n visibility: visible;\n cursor: pointer;\n }\n /* The icon is an inline SVG rather than a text glyph so host-theme\n button resets (font-size: 0, icon fonts, text-indent) can't hide it. */\n #twc-nm-overlay .twc-nm-close svg {\n display: block;\n width: 16px;\n height: 16px;\n flex: none;\n pointer-events: none;\n }\n #twc-nm-overlay .twc-nm-close::before,\n #twc-nm-overlay .twc-nm-close::after {\n content: none;\n }\n #twc-nm-overlay .twc-nm-close:hover {\n background: #f3f4f6;\n color: #111827;\n }\n #twc-nm-overlay .twc-nm-title {\n margin: 0 32px 4px 0;\n font-size: 18px;\n font-weight: 600;\n }\n #twc-nm-overlay .twc-nm-text {\n margin: 0 0 16px;\n font-size: 14px;\n line-height: 1.5;\n color: #6b7280;\n }\n #twc-nm-overlay .twc-nm-form {\n display: flex;\n flex-direction: column;\n gap: 14px;\n }\n #twc-nm-overlay .twc-nm-field {\n display: flex;\n flex-direction: column;\n gap: 6px;\n }\n #twc-nm-overlay .twc-nm-label {\n font-size: 13px;\n font-weight: 500;\n color: #374151;\n }\n #twc-nm-overlay .twc-nm-input,\n #twc-nm-overlay .twc-nm-select {\n width: 100%;\n padding: 10px 12px;\n font-size: 14px;\n font-family: inherit;\n color: #111827;\n background: #fff;\n border: 1px solid #e5e7eb;\n border-radius: 8px;\n }\n #twc-nm-overlay .twc-nm-input:focus,\n #twc-nm-overlay .twc-nm-select:focus {\n outline: none;\n border-color: #111827;\n box-shadow: 0 0 0 3px rgba(17, 24, 39, 0.08);\n }\n #twc-nm-overlay .twc-nm-select {\n appearance: none;\n -webkit-appearance: none;\n padding-right: 36px;\n cursor: pointer;\n background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath d='M2 4l4 4 4-4' stroke='%236b7280' stroke-width='1.5' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E\");\n background-repeat: no-repeat;\n background-position: right 12px center;\n }\n #twc-nm-overlay .twc-nm-checks {\n display: flex;\n flex-direction: column;\n gap: 10px;\n }\n #twc-nm-overlay .twc-nm-check {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 13px;\n color: #374151;\n cursor: pointer;\n }\n #twc-nm-overlay .twc-nm-check input {\n width: 16px;\n height: 16px;\n margin: 0;\n accent-color: #111827;\n }\n #twc-nm-overlay .twc-nm-status {\n font-size: 13px;\n line-height: 1.5;\n padding: 10px 12px;\n border-radius: 8px;\n }\n #twc-nm-overlay .twc-nm-status--error {\n color: #b91c1c;\n background: #fef2f2;\n }\n #twc-nm-overlay .twc-nm-status--success {\n color: #15803d;\n background: #f0fdf4;\n }\n #twc-nm-overlay .twc-nm-status--info {\n color: #374151;\n background: #f3f4f6;\n }\n #twc-nm-overlay .twc-nm-submit {\n width: 100%;\n padding: 12px 16px;\n font-size: 14px;\n font-weight: 600;\n font-family: inherit;\n color: #fff;\n background: #111827;\n border: none;\n border-radius: 8px;\n cursor: pointer;\n transition: background 150ms ease;\n }\n #twc-nm-overlay .twc-nm-submit:hover {\n background: #374151;\n }\n #twc-nm-overlay .twc-nm-submit:disabled {\n opacity: 0.6;\n cursor: default;\n }\n #twc-nm-overlay .twc-nm-recs {\n margin-top: 20px;\n padding-top: 20px;\n border-top: 1px solid #e5e7eb;\n }\n #twc-nm-overlay .twc-nm-recs-title {\n margin: 0 0 12px;\n font-size: 14px;\n font-weight: 600;\n color: #111827;\n }\n #twc-nm-overlay .twc-nm-recs-row {\n display: flex;\n gap: 12px;\n overflow-x: auto;\n scroll-snap-type: x mandatory;\n -webkit-overflow-scrolling: touch;\n padding-bottom: 8px;\n scrollbar-width: thin;\n }\n /* Only where a real pointer exists — the affordance is meaningless\n on touch, which pans the row natively. */\n @media (hover: hover) {\n #twc-nm-overlay .twc-nm-recs-row {\n cursor: grab;\n }\n }\n #twc-nm-overlay .twc-nm-recs-row--dragging,\n #twc-nm-overlay .twc-nm-recs-row--dragging .twc-nm-rec {\n cursor: grabbing;\n }\n #twc-nm-overlay .twc-nm-recs-row--dragging {\n /* Snapping fights a manually driven scrollLeft and feels sticky. */\n scroll-snap-type: none;\n user-select: none;\n -webkit-user-select: none;\n }\n #twc-nm-overlay .twc-nm-rec {\n flex: 0 0 132px;\n scroll-snap-align: start;\n display: flex;\n flex-direction: column;\n gap: 6px;\n color: inherit;\n text-decoration: none;\n }\n #twc-nm-overlay .twc-nm-rec:focus-visible {\n outline: 2px solid #111827;\n outline-offset: 2px;\n border-radius: 8px;\n }\n #twc-nm-overlay .twc-nm-rec-imgbox {\n aspect-ratio: 3 / 4;\n overflow: hidden;\n border-radius: 8px;\n background: #f3f4f6;\n }\n #twc-nm-overlay .twc-nm-rec-img {\n display: block;\n width: 100%;\n height: 100%;\n object-fit: cover;\n transition: opacity 150ms ease;\n }\n #twc-nm-overlay .twc-nm-rec:hover .twc-nm-rec-img {\n opacity: 0.85;\n }\n #twc-nm-overlay .twc-nm-rec-name {\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n font-size: 13px;\n line-height: 1.35;\n color: #111827;\n }\n #twc-nm-overlay .twc-nm-rec-price {\n font-size: 13px;\n font-weight: 600;\n color: #111827;\n }\n #twc-nm-overlay .twc-nm-rec-was {\n margin-right: 4px;\n font-weight: 400;\n color: #9ca3af;\n }\n #twc-nm-overlay .twc-nm-rec--skeleton {\n pointer-events: none;\n }\n #twc-nm-overlay .twc-nm-skel {\n background-color: #f3f4f6;\n background-image: linear-gradient(\n 90deg,\n #f3f4f6 0%,\n #e8eaee 50%,\n #f3f4f6 100%\n );\n background-size: 200% 100%;\n border-radius: 4px;\n animation: twc-nm-shimmer 1.3s ease-in-out infinite;\n }\n #twc-nm-overlay .twc-nm-rec-imgbox.twc-nm-skel {\n border-radius: 8px;\n }\n #twc-nm-overlay .twc-nm-skel-line {\n height: 12px;\n width: 100%;\n }\n #twc-nm-overlay .twc-nm-skel-line--short {\n width: 45%;\n }\n @keyframes twc-nm-shimmer {\n from {\n background-position: 200% 0;\n }\n to {\n background-position: -200% 0;\n }\n }\n @media (prefers-reduced-motion: reduce) {\n #twc-nm-overlay,\n #twc-nm-overlay .twc-nm-card,\n #twc-nm-overlay .twc-nm-rec-img {\n transition: none;\n }\n #twc-nm-overlay .twc-nm-card,\n #twc-nm-overlay.twc-nm--panel .twc-nm-card {\n transform: none;\n }\n #twc-nm-overlay .twc-nm-skel {\n animation: none;\n }\n }\n ",document.head.appendChild(e)}();const c=JSON.parse(t("data-fields")||'["email"]'),r=t("data-type")||"notify-me",i="panel"===t("data-display")?"panel":"modal",d=t("data-auth")||"proxy",s=t("data-tenant")||"victoria-woods",m=t("data-proxy-app")||"twc-sdk",l=t("data-access-token")||window.TWC_ACCESS_TOKEN||null,u="false"!==t("data-recommendations"),p=parseInt(t("data-recommendations-count")||"",10),f=S({wrapper:e,config:{fields:c,type:r,display:i,auth:{mode:d,proxyApp:m,accessToken:l},tenant:s,recommendationsEnabled:u,recommendationsCount:Number.isFinite(p)&&p>0?p:8,recommendationsProfile:(t("data-recommendations-profile")||"").trim()||null,currency:t("data-currency")||null,customerEmail:N(t)},localization:M(t)});f&&(document.addEventListener("click",e=>{const n=e.target,t=n instanceof Element?n.closest(L):null;t&&(e.preventDefault(),f.open(t))},!0),n&&!n.matches(L)&&n.addEventListener("click",()=>f.open(n)))}"loading"===document.readyState?document.addEventListener("DOMContentLoaded",I):I()}();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "notify-me-wl",
3
- "version": "3.4.0",
3
+ "version": "3.5.0",
4
4
  "description": "Coming Soon / Notify Me wishlist widget for retailer product pages.",
5
5
  "main": "build/notify-me-wl.min.js",
6
6
  "files": [
@@ -11,6 +11,7 @@
11
11
  "build": "rollup -c",
12
12
  "dev": "rollup -c -w",
13
13
  "typecheck": "tsc --noEmit",
14
+ "selftest": "npm run build && sh harness/run-selftest.sh",
14
15
  "test": "echo \"Error: no test specified\" && exit 1"
15
16
  },
16
17
  "author": "",