@schukai/monster 4.64.0 → 4.65.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/CHANGELOG.md +11 -0
- package/package.json +1 -1
- package/source/components/form/buy-box.mjs +2516 -0
- package/source/components/form/cart-control.mjs +710 -0
- package/source/components/form/style/buy-box.pcss +124 -0
- package/source/components/form/style/cart-control.pcss +35 -0
- package/source/components/form/stylesheet/buy-box.mjs +38 -0
- package/source/components/form/stylesheet/cart-control.mjs +38 -0
- package/source/components/form/stylesheet/variant-select.mjs +13 -6
- package/test/cases/components/form/buy-box.mjs +181 -0
|
@@ -0,0 +1,2516 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright © Volker Schukai and all contributing authors, {{copyRightYear}}. All rights reserved.
|
|
3
|
+
* Node module: @schukai/monster
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the GNU Affero General Public License version 3 (AGPLv3).
|
|
6
|
+
* The full text of the license can be found at: https://www.gnu.org/licenses/agpl-3.0.en.html
|
|
7
|
+
*
|
|
8
|
+
* For those who do not wish to adhere to the AGPLv3, a commercial license is available.
|
|
9
|
+
* Acquiring a commercial license allows you to use this software without complying with the AGPLv3 terms.
|
|
10
|
+
* For more information about purchasing a commercial license, please contact Volker Schukai.
|
|
11
|
+
*
|
|
12
|
+
* SPDX-License-Identifier: AGPL-3.0
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { instanceSymbol } from "../../constants.mjs";
|
|
16
|
+
import { addAttributeToken } from "../../dom/attributes.mjs";
|
|
17
|
+
import { ATTRIBUTE_ERRORMESSAGE } from "../../dom/constants.mjs";
|
|
18
|
+
import { CustomControl } from "../../dom/customcontrol.mjs";
|
|
19
|
+
import {
|
|
20
|
+
assembleMethodSymbol,
|
|
21
|
+
registerCustomElement,
|
|
22
|
+
} from "../../dom/customelement.mjs";
|
|
23
|
+
import { fireCustomEvent } from "../../dom/events.mjs";
|
|
24
|
+
import { getLocaleOfDocument } from "../../dom/locale.mjs";
|
|
25
|
+
import { getDocument } from "../../dom/util.mjs";
|
|
26
|
+
import { Pathfinder } from "../../data/pathfinder.mjs";
|
|
27
|
+
import { Formatter } from "../../text/formatter.mjs";
|
|
28
|
+
import { isFunction, isObject, isString } from "../../types/is.mjs";
|
|
29
|
+
import { CommonStyleSheet } from "../stylesheet/common.mjs";
|
|
30
|
+
import { FormStyleSheet } from "../stylesheet/form.mjs";
|
|
31
|
+
import { VariantSelectStyleSheet } from "./stylesheet/variant-select.mjs";
|
|
32
|
+
import { BuyBoxStyleSheet } from "./stylesheet/buy-box.mjs";
|
|
33
|
+
import "./variant-select.mjs";
|
|
34
|
+
import "./quantity.mjs";
|
|
35
|
+
import "./message-state-button.mjs";
|
|
36
|
+
import "../datatable/datasource/dom.mjs";
|
|
37
|
+
import "../datatable/datasource/rest.mjs";
|
|
38
|
+
|
|
39
|
+
export { BuyBox };
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* @private
|
|
43
|
+
* @type {symbol}
|
|
44
|
+
*/
|
|
45
|
+
const controlElementSymbol = Symbol("controlElement");
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* @private
|
|
49
|
+
* @type {symbol}
|
|
50
|
+
*/
|
|
51
|
+
const variantElementSymbol = Symbol("variantElement");
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* @private
|
|
55
|
+
* @type {symbol}
|
|
56
|
+
*/
|
|
57
|
+
const quantityElementSymbol = Symbol("quantityElement");
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* @private
|
|
61
|
+
* @type {symbol}
|
|
62
|
+
*/
|
|
63
|
+
const quantityInputElementSymbol = Symbol("quantityInputElement");
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* @private
|
|
67
|
+
* @type {symbol}
|
|
68
|
+
*/
|
|
69
|
+
const quantityUnitElementSymbol = Symbol("quantityUnitElement");
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* @private
|
|
73
|
+
* @type {symbol}
|
|
74
|
+
*/
|
|
75
|
+
const submitElementSymbol = Symbol("submitElement");
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* @private
|
|
79
|
+
* @type {symbol}
|
|
80
|
+
*/
|
|
81
|
+
const submitLabelElementSymbol = Symbol("submitLabelElement");
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* @private
|
|
85
|
+
* @type {symbol}
|
|
86
|
+
*/
|
|
87
|
+
const messageElementSymbol = Symbol("messageElement");
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* @private
|
|
91
|
+
* @type {symbol}
|
|
92
|
+
*/
|
|
93
|
+
const priceElementSymbol = Symbol("priceElement");
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* @private
|
|
97
|
+
* @type {symbol}
|
|
98
|
+
*/
|
|
99
|
+
const sumElementSymbol = Symbol("sumElement");
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* @private
|
|
103
|
+
* @type {symbol}
|
|
104
|
+
*/
|
|
105
|
+
const listPriceElementSymbol = Symbol("listPriceElement");
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* @private
|
|
109
|
+
* @type {symbol}
|
|
110
|
+
*/
|
|
111
|
+
const basePriceElementSymbol = Symbol("basePriceElement");
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* @private
|
|
115
|
+
* @type {symbol}
|
|
116
|
+
*/
|
|
117
|
+
const taxElementSymbol = Symbol("taxElement");
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* @private
|
|
121
|
+
* @type {symbol}
|
|
122
|
+
*/
|
|
123
|
+
const totalElementSymbol = Symbol("totalElement");
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* @private
|
|
127
|
+
* @type {symbol}
|
|
128
|
+
*/
|
|
129
|
+
const deliveryElementSymbol = Symbol("deliveryElement");
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* @private
|
|
133
|
+
* @type {symbol}
|
|
134
|
+
*/
|
|
135
|
+
const quantityStaticElementSymbol = Symbol("quantityStaticElement");
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* @private
|
|
139
|
+
* @type {symbol}
|
|
140
|
+
*/
|
|
141
|
+
const datasourceElementSymbol = Symbol("datasourceElement");
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* @private
|
|
145
|
+
* @type {symbol}
|
|
146
|
+
*/
|
|
147
|
+
const productDataSymbol = Symbol("productData");
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* @private
|
|
151
|
+
* @type {symbol}
|
|
152
|
+
*/
|
|
153
|
+
const cartDataSymbol = Symbol("cartData");
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* @private
|
|
157
|
+
* @type {symbol}
|
|
158
|
+
*/
|
|
159
|
+
const stockRequestSymbol = Symbol("stockRequest");
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* @private
|
|
163
|
+
* @type {symbol}
|
|
164
|
+
*/
|
|
165
|
+
const cartControlSymbol = Symbol("cartControl");
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* @private
|
|
169
|
+
* @type {symbol}
|
|
170
|
+
*/
|
|
171
|
+
const pendingCartPayloadSymbol = Symbol("pendingCartPayload");
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* @private
|
|
175
|
+
* @type {symbol}
|
|
176
|
+
*/
|
|
177
|
+
const pricingRequestSymbol = Symbol("pricingRequest");
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* @private
|
|
181
|
+
* @type {symbol}
|
|
182
|
+
*/
|
|
183
|
+
const pricingCacheSymbol = Symbol("pricingCache");
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* BuyBox
|
|
187
|
+
*
|
|
188
|
+
* @summary Product buy box control with variants, quantity, pricing, and add-to-cart.
|
|
189
|
+
* @fires monster-buy-box-change
|
|
190
|
+
* @fires monster-buy-box-valid
|
|
191
|
+
* @fires monster-buy-box-invalid
|
|
192
|
+
* @fires monster-buy-box-submit
|
|
193
|
+
* @fires monster-buy-box-success
|
|
194
|
+
* @fires monster-buy-box-error
|
|
195
|
+
* @fires monster-buy-box-loaded
|
|
196
|
+
*/
|
|
197
|
+
class BuyBox extends CustomControl {
|
|
198
|
+
/**
|
|
199
|
+
* This method is called by the `instanceof` operator.
|
|
200
|
+
* @return {symbol}
|
|
201
|
+
*/
|
|
202
|
+
static get [instanceSymbol]() {
|
|
203
|
+
return Symbol.for("@schukai/monster/components/form/buy-box@@instance");
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* To set the options via the HTML tag, the attribute `data-monster-options` must be used.
|
|
208
|
+
* @see {@link https://monsterjs.org/en/doc/#configurate-a-monster-control}
|
|
209
|
+
*
|
|
210
|
+
* @property {Object} templates Template definitions
|
|
211
|
+
* @property {string} templates.main Main template
|
|
212
|
+
* @property {Object} labels Labels
|
|
213
|
+
* @property {Object} features Feature toggles
|
|
214
|
+
* @property {boolean} features.messages Show status messages
|
|
215
|
+
* @property {boolean} features.rowSelects Allow row selects in variant select
|
|
216
|
+
* @property {boolean} features.combinedSelect Allow combined variant select
|
|
217
|
+
* @property {boolean} features.allowDecimal Allow decimal quantities
|
|
218
|
+
* @property {Object} product Static product config
|
|
219
|
+
* @property {Object} pricing Static pricing config
|
|
220
|
+
* @property {Object} variants Variant configuration (passed to monster-variant-select)
|
|
221
|
+
* @property {Object} quantity Quantity configuration
|
|
222
|
+
* @property {Object} cart Cart configuration (endpoint + mapping or cart control selector)
|
|
223
|
+
* @property {Object} stock Stock configuration (endpoint + mapping)
|
|
224
|
+
* @property {Object} datasource Datasource config (selector or rest)
|
|
225
|
+
* @property {Object} mapping Mapping from datasource data to product values
|
|
226
|
+
* @property {Object} actions Callback actions
|
|
227
|
+
*/
|
|
228
|
+
get defaults() {
|
|
229
|
+
return Object.assign({}, super.defaults, {
|
|
230
|
+
templates: {
|
|
231
|
+
main: getTemplate(),
|
|
232
|
+
},
|
|
233
|
+
labels: getTranslations(),
|
|
234
|
+
features: {
|
|
235
|
+
messages: true,
|
|
236
|
+
rowSelects: false,
|
|
237
|
+
combinedSelect: false,
|
|
238
|
+
allowDecimal: false,
|
|
239
|
+
},
|
|
240
|
+
product: {
|
|
241
|
+
sku: null,
|
|
242
|
+
name: null,
|
|
243
|
+
stock: null,
|
|
244
|
+
delivery: null,
|
|
245
|
+
unitLabel: null,
|
|
246
|
+
currency: "EUR",
|
|
247
|
+
taxIncluded: true,
|
|
248
|
+
taxRate: null,
|
|
249
|
+
},
|
|
250
|
+
pricing: {
|
|
251
|
+
price: null,
|
|
252
|
+
listPrice: null,
|
|
253
|
+
basePrice: null,
|
|
254
|
+
basePriceUnit: null,
|
|
255
|
+
tiers: [],
|
|
256
|
+
api: {
|
|
257
|
+
url: null,
|
|
258
|
+
features: {
|
|
259
|
+
alwaysFetch: false,
|
|
260
|
+
},
|
|
261
|
+
fetch: {
|
|
262
|
+
method: "GET",
|
|
263
|
+
headers: {
|
|
264
|
+
accept: "application/json",
|
|
265
|
+
},
|
|
266
|
+
},
|
|
267
|
+
mapping: {
|
|
268
|
+
selector: "*",
|
|
269
|
+
priceTemplate: null,
|
|
270
|
+
listPriceTemplate: null,
|
|
271
|
+
basePriceTemplate: null,
|
|
272
|
+
basePriceUnitTemplate: null,
|
|
273
|
+
tiersPath: null,
|
|
274
|
+
taxRateTemplate: null,
|
|
275
|
+
taxIncludedTemplate: null,
|
|
276
|
+
},
|
|
277
|
+
},
|
|
278
|
+
},
|
|
279
|
+
variants: {
|
|
280
|
+
dimensions: [],
|
|
281
|
+
data: [],
|
|
282
|
+
features: {},
|
|
283
|
+
pricing: {
|
|
284
|
+
priceKey: "price",
|
|
285
|
+
listPriceKey: "listPrice",
|
|
286
|
+
basePriceKey: "basePrice",
|
|
287
|
+
basePriceUnitKey: "basePriceUnit",
|
|
288
|
+
currencyKey: "currency",
|
|
289
|
+
taxRateKey: "taxRate",
|
|
290
|
+
taxIncludedKey: "taxIncluded",
|
|
291
|
+
tiersKey: "tiers",
|
|
292
|
+
},
|
|
293
|
+
},
|
|
294
|
+
quantity: {
|
|
295
|
+
value: 1,
|
|
296
|
+
min: 1,
|
|
297
|
+
max: Number.POSITIVE_INFINITY,
|
|
298
|
+
step: 1,
|
|
299
|
+
precision: null,
|
|
300
|
+
allowed: null,
|
|
301
|
+
rounding: "half-up",
|
|
302
|
+
},
|
|
303
|
+
datasource: {
|
|
304
|
+
selector: null,
|
|
305
|
+
rest: null,
|
|
306
|
+
},
|
|
307
|
+
mapping: {
|
|
308
|
+
selector: "*",
|
|
309
|
+
skuTemplate: null,
|
|
310
|
+
nameTemplate: null,
|
|
311
|
+
stockTemplate: null,
|
|
312
|
+
deliveryTemplate: null,
|
|
313
|
+
unitLabelTemplate: null,
|
|
314
|
+
priceTemplate: null,
|
|
315
|
+
listPriceTemplate: null,
|
|
316
|
+
basePriceTemplate: null,
|
|
317
|
+
basePriceUnitTemplate: null,
|
|
318
|
+
currencyTemplate: null,
|
|
319
|
+
taxIncludedTemplate: null,
|
|
320
|
+
taxRateTemplate: null,
|
|
321
|
+
variantsPath: null,
|
|
322
|
+
cartPath: null,
|
|
323
|
+
},
|
|
324
|
+
stock: {
|
|
325
|
+
url: null,
|
|
326
|
+
fetch: {
|
|
327
|
+
method: "GET",
|
|
328
|
+
headers: {
|
|
329
|
+
accept: "application/json",
|
|
330
|
+
},
|
|
331
|
+
},
|
|
332
|
+
mapping: {
|
|
333
|
+
selector: "*",
|
|
334
|
+
stockTemplate: null,
|
|
335
|
+
},
|
|
336
|
+
features: {
|
|
337
|
+
autoCheck: true,
|
|
338
|
+
},
|
|
339
|
+
},
|
|
340
|
+
cart: {
|
|
341
|
+
url: null,
|
|
342
|
+
method: "POST",
|
|
343
|
+
headers: {
|
|
344
|
+
"Content-Type": "application/json",
|
|
345
|
+
},
|
|
346
|
+
controlSelector: null,
|
|
347
|
+
bodyTemplate: null,
|
|
348
|
+
mapping: {
|
|
349
|
+
selector: "*",
|
|
350
|
+
skuTemplate: null,
|
|
351
|
+
qtyTemplate: null,
|
|
352
|
+
},
|
|
353
|
+
},
|
|
354
|
+
actions: {
|
|
355
|
+
onchange: null,
|
|
356
|
+
onvalid: null,
|
|
357
|
+
oninvalid: null,
|
|
358
|
+
onsubmit: null,
|
|
359
|
+
onsuccess: null,
|
|
360
|
+
onerror: null,
|
|
361
|
+
onload: null,
|
|
362
|
+
},
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* @return {BuyBox}
|
|
368
|
+
*/
|
|
369
|
+
[assembleMethodSymbol]() {
|
|
370
|
+
super[assembleMethodSymbol]();
|
|
371
|
+
this[pricingCacheSymbol] = new Map();
|
|
372
|
+
initControlReferences.call(this);
|
|
373
|
+
initDatasource.call(this);
|
|
374
|
+
initEventHandler.call(this);
|
|
375
|
+
updateSubmitLabel.call(this);
|
|
376
|
+
applyData.call(this);
|
|
377
|
+
return this;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* Re-apply options and mapped data.
|
|
382
|
+
* @return {BuyBox}
|
|
383
|
+
*/
|
|
384
|
+
refresh() {
|
|
385
|
+
initDatasource.call(this);
|
|
386
|
+
applyData.call(this);
|
|
387
|
+
return this;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* @return {CSSStyleSheet[]}
|
|
392
|
+
*/
|
|
393
|
+
static getCSSStyleSheet() {
|
|
394
|
+
return [
|
|
395
|
+
CommonStyleSheet,
|
|
396
|
+
FormStyleSheet,
|
|
397
|
+
VariantSelectStyleSheet,
|
|
398
|
+
BuyBoxStyleSheet,
|
|
399
|
+
];
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* @return {string}
|
|
404
|
+
*/
|
|
405
|
+
static getTag() {
|
|
406
|
+
return "monster-buy-box";
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* @private
|
|
412
|
+
*/
|
|
413
|
+
function initControlReferences() {
|
|
414
|
+
this[controlElementSymbol] = this.shadowRoot.querySelector(
|
|
415
|
+
"[data-monster-role=control]",
|
|
416
|
+
);
|
|
417
|
+
this[variantElementSymbol] = this.shadowRoot.querySelector(
|
|
418
|
+
"[data-monster-role=variants]",
|
|
419
|
+
);
|
|
420
|
+
this[quantityElementSymbol] = this.shadowRoot.querySelector(
|
|
421
|
+
"[data-monster-role=quantity]",
|
|
422
|
+
);
|
|
423
|
+
this[quantityInputElementSymbol] = this.shadowRoot.querySelector(
|
|
424
|
+
"[data-monster-role=quantity-input]",
|
|
425
|
+
);
|
|
426
|
+
this[quantityUnitElementSymbol] = this.shadowRoot.querySelector(
|
|
427
|
+
"[data-monster-role=quantity-unit]",
|
|
428
|
+
);
|
|
429
|
+
this[submitElementSymbol] = this.shadowRoot.querySelector(
|
|
430
|
+
"[data-monster-role=submit]",
|
|
431
|
+
);
|
|
432
|
+
this[submitLabelElementSymbol] = this.shadowRoot.querySelector(
|
|
433
|
+
"[data-monster-role=submit-label]",
|
|
434
|
+
);
|
|
435
|
+
this[messageElementSymbol] = this.shadowRoot.querySelector(
|
|
436
|
+
"[data-monster-role=message]",
|
|
437
|
+
);
|
|
438
|
+
this[quantityStaticElementSymbol] = this.shadowRoot.querySelector(
|
|
439
|
+
"[data-monster-role=quantity-static]",
|
|
440
|
+
);
|
|
441
|
+
this[priceElementSymbol] = this.shadowRoot.querySelector(
|
|
442
|
+
"[data-monster-role=price]",
|
|
443
|
+
);
|
|
444
|
+
this[sumElementSymbol] = this.shadowRoot.querySelector(
|
|
445
|
+
"[data-monster-role=sum]",
|
|
446
|
+
);
|
|
447
|
+
this[listPriceElementSymbol] = this.shadowRoot.querySelector(
|
|
448
|
+
"[data-monster-role=list-price]",
|
|
449
|
+
);
|
|
450
|
+
this[basePriceElementSymbol] = this.shadowRoot.querySelector(
|
|
451
|
+
"[data-monster-role=base-price]",
|
|
452
|
+
);
|
|
453
|
+
this[taxElementSymbol] = this.shadowRoot.querySelector(
|
|
454
|
+
"[data-monster-role=tax]",
|
|
455
|
+
);
|
|
456
|
+
this[totalElementSymbol] = this.shadowRoot.querySelector(
|
|
457
|
+
"[data-monster-role=total]",
|
|
458
|
+
);
|
|
459
|
+
this[deliveryElementSymbol] = this.shadowRoot.querySelector(
|
|
460
|
+
"[data-monster-role=delivery]",
|
|
461
|
+
);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/**
|
|
465
|
+
* @private
|
|
466
|
+
* @return {HTMLElement|null}
|
|
467
|
+
*/
|
|
468
|
+
function getCartControl() {
|
|
469
|
+
const selector = this.getOption("cart.controlSelector");
|
|
470
|
+
if (!isString(selector) || selector === "") {
|
|
471
|
+
return null;
|
|
472
|
+
}
|
|
473
|
+
if (this[cartControlSymbol] && this[cartControlSymbol].isConnected) {
|
|
474
|
+
return this[cartControlSymbol];
|
|
475
|
+
}
|
|
476
|
+
const element = getDocument().querySelector(selector);
|
|
477
|
+
if (element) {
|
|
478
|
+
this[cartControlSymbol] = element;
|
|
479
|
+
}
|
|
480
|
+
return element || null;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* @private
|
|
485
|
+
*/
|
|
486
|
+
function initDatasource() {
|
|
487
|
+
if (this[datasourceElementSymbol]) {
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
const selector = this.getOption("datasource.selector");
|
|
491
|
+
if (isString(selector) && selector !== "") {
|
|
492
|
+
this[datasourceElementSymbol] = getDocument().querySelector(selector);
|
|
493
|
+
} else if (isObject(this.getOption("datasource.rest"))) {
|
|
494
|
+
const rest = getDocument().createElement("monster-datasource-rest");
|
|
495
|
+
rest.setOption("read", this.getOption("datasource.rest.read", {}));
|
|
496
|
+
rest.setOption("features.autoInit", true);
|
|
497
|
+
rest.setOption("autoInit.oneTime", true);
|
|
498
|
+
this.appendChild(rest);
|
|
499
|
+
this[datasourceElementSymbol] = rest;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
if (this[datasourceElementSymbol]) {
|
|
503
|
+
this[datasourceElementSymbol].addEventListener(
|
|
504
|
+
"monster-datasource-fetched",
|
|
505
|
+
(event) => {
|
|
506
|
+
const data = event?.detail?.data || this[datasourceElementSymbol]?.data;
|
|
507
|
+
if (data) {
|
|
508
|
+
this[productDataSymbol] = data;
|
|
509
|
+
applyData.call(this);
|
|
510
|
+
}
|
|
511
|
+
},
|
|
512
|
+
);
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
/**
|
|
517
|
+
* @private
|
|
518
|
+
*/
|
|
519
|
+
function initEventHandler() {
|
|
520
|
+
const variants = this.shadowRoot.querySelector("monster-variant-select");
|
|
521
|
+
const quantity = this.shadowRoot.querySelector("monster-quantity");
|
|
522
|
+
const submit = this[submitElementSymbol];
|
|
523
|
+
|
|
524
|
+
if (variants) {
|
|
525
|
+
variants.addEventListener("monster-variant-select-change", () => {
|
|
526
|
+
updateState.call(this);
|
|
527
|
+
checkStockIfNeeded.call(this);
|
|
528
|
+
checkPricingIfNeeded.call(this);
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
if (quantity) {
|
|
533
|
+
quantity.addEventListener("monster-quantity-change", () => {
|
|
534
|
+
updateState.call(this);
|
|
535
|
+
checkStockIfNeeded.call(this);
|
|
536
|
+
checkPricingIfNeeded.call(this);
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
if (this[quantityInputElementSymbol]) {
|
|
540
|
+
this[quantityInputElementSymbol].addEventListener("input", () => {
|
|
541
|
+
updateState.call(this);
|
|
542
|
+
checkStockIfNeeded.call(this);
|
|
543
|
+
checkPricingIfNeeded.call(this);
|
|
544
|
+
});
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
if (submit) {
|
|
548
|
+
submit.setOption("actions.click", () => {
|
|
549
|
+
handleSubmit.call(this);
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
/**
|
|
555
|
+
* @private
|
|
556
|
+
*/
|
|
557
|
+
function applyData() {
|
|
558
|
+
const data = this[productDataSymbol] || this.getOption("product");
|
|
559
|
+
if (!data) {
|
|
560
|
+
return;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
const mapped = mapProductData.call(this, data);
|
|
564
|
+
this[productDataSymbol] = mapped;
|
|
565
|
+
this[cartDataSymbol] = mapped.cart;
|
|
566
|
+
|
|
567
|
+
applyVariants.call(this, mapped);
|
|
568
|
+
applyQuantity.call(this);
|
|
569
|
+
updateState.call(this, true);
|
|
570
|
+
checkStockIfNeeded.call(this);
|
|
571
|
+
checkPricingIfNeeded.call(this);
|
|
572
|
+
updateSubmitLabel.call(this);
|
|
573
|
+
fireCustomEvent(this, "monster-buy-box-loaded", { product: mapped });
|
|
574
|
+
const loadAction = this.getOption("actions.onload");
|
|
575
|
+
if (isFunction(loadAction)) {
|
|
576
|
+
loadAction.call(this, mapped);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
/**
|
|
581
|
+
* @private
|
|
582
|
+
* @param {Object} data
|
|
583
|
+
* @return {Object}
|
|
584
|
+
*/
|
|
585
|
+
function mapProductData(data) {
|
|
586
|
+
const mapping = this.getOption("mapping", {});
|
|
587
|
+
let source = data;
|
|
588
|
+
if (
|
|
589
|
+
isString(mapping?.selector) &&
|
|
590
|
+
mapping.selector !== "*" &&
|
|
591
|
+
mapping.selector
|
|
592
|
+
) {
|
|
593
|
+
try {
|
|
594
|
+
source = new Pathfinder(data).getVia(mapping.selector);
|
|
595
|
+
} catch (_e) {
|
|
596
|
+
source = data;
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
const value = (key) => mapValueFromSource(source, mapping[key]);
|
|
601
|
+
|
|
602
|
+
const product = Object.assign({}, this.getOption("product"), {
|
|
603
|
+
sku: value("skuTemplate") ?? this.getOption("product.sku"),
|
|
604
|
+
name: value("nameTemplate") ?? this.getOption("product.name"),
|
|
605
|
+
stock: parseNumber(value("stockTemplate"), this.getOption("product.stock")),
|
|
606
|
+
delivery: value("deliveryTemplate") ?? this.getOption("product.delivery"),
|
|
607
|
+
unitLabel:
|
|
608
|
+
value("unitLabelTemplate") ?? this.getOption("product.unitLabel"),
|
|
609
|
+
currency: value("currencyTemplate") ?? this.getOption("product.currency"),
|
|
610
|
+
taxIncluded: parseBoolean(
|
|
611
|
+
value("taxIncludedTemplate"),
|
|
612
|
+
this.getOption("product.taxIncluded"),
|
|
613
|
+
),
|
|
614
|
+
taxRate: parseNumber(
|
|
615
|
+
value("taxRateTemplate"),
|
|
616
|
+
this.getOption("product.taxRate"),
|
|
617
|
+
),
|
|
618
|
+
});
|
|
619
|
+
|
|
620
|
+
const pricing = Object.assign({}, this.getOption("pricing"), {
|
|
621
|
+
price: parseNumber(value("priceTemplate"), this.getOption("pricing.price")),
|
|
622
|
+
listPrice: parseNumber(
|
|
623
|
+
value("listPriceTemplate"),
|
|
624
|
+
this.getOption("pricing.listPrice"),
|
|
625
|
+
),
|
|
626
|
+
basePrice: parseNumber(
|
|
627
|
+
value("basePriceTemplate"),
|
|
628
|
+
this.getOption("pricing.basePrice"),
|
|
629
|
+
),
|
|
630
|
+
basePriceUnit:
|
|
631
|
+
value("basePriceUnitTemplate") ?? this.getOption("pricing.basePriceUnit"),
|
|
632
|
+
});
|
|
633
|
+
|
|
634
|
+
const variantsPath = mapping?.variantsPath;
|
|
635
|
+
const variantData = variantsPath
|
|
636
|
+
? new Pathfinder(source).getVia(variantsPath)
|
|
637
|
+
: this.getOption("variants.data");
|
|
638
|
+
|
|
639
|
+
const cartPath = mapping?.cartPath;
|
|
640
|
+
const cartData = cartPath ? new Pathfinder(source).getVia(cartPath) : null;
|
|
641
|
+
|
|
642
|
+
return { product, pricing, variants: variantData, cart: cartData, source };
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
/**
|
|
646
|
+
* @private
|
|
647
|
+
* @param {Object} source
|
|
648
|
+
* @param {string|null} template
|
|
649
|
+
* @return {string|null}
|
|
650
|
+
*/
|
|
651
|
+
function mapValueFromSource(source, template) {
|
|
652
|
+
if (!isString(template) || template === "") {
|
|
653
|
+
return null;
|
|
654
|
+
}
|
|
655
|
+
try {
|
|
656
|
+
if (template.includes("${")) {
|
|
657
|
+
return new Formatter(source).format(template);
|
|
658
|
+
}
|
|
659
|
+
return new Pathfinder(source).getVia(template);
|
|
660
|
+
} catch (_e) {
|
|
661
|
+
return null;
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
/**
|
|
666
|
+
* @private
|
|
667
|
+
* @param {Object} mapped
|
|
668
|
+
*/
|
|
669
|
+
function applyVariants(mapped) {
|
|
670
|
+
const variants = this.shadowRoot.querySelector("monster-variant-select");
|
|
671
|
+
if (!variants) {
|
|
672
|
+
return;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
variants.setAttribute(
|
|
676
|
+
"aria-label",
|
|
677
|
+
this.getOption("labels.variantsAria") || "Variant selection",
|
|
678
|
+
);
|
|
679
|
+
const variantOptions = Object.assign({}, this.getOption("variants"));
|
|
680
|
+
if (Array.isArray(mapped?.variants)) {
|
|
681
|
+
variantOptions.data = mapped.variants;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
variantOptions.features = Object.assign({}, variantOptions.features, {
|
|
685
|
+
rowSelects: this.getOption("features.rowSelects"),
|
|
686
|
+
combinedSelect: this.getOption("features.combinedSelect"),
|
|
687
|
+
});
|
|
688
|
+
|
|
689
|
+
const hasDimensions =
|
|
690
|
+
Array.isArray(variantOptions.dimensions) &&
|
|
691
|
+
variantOptions.dimensions.length > 0;
|
|
692
|
+
const hasData =
|
|
693
|
+
Array.isArray(variantOptions.data) && variantOptions.data.length > 0;
|
|
694
|
+
const hasUrl = isString(variantOptions.url) && variantOptions.url !== "";
|
|
695
|
+
if (this[variantElementSymbol]) {
|
|
696
|
+
this[variantElementSymbol].hidden = !(hasDimensions && (hasData || hasUrl));
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
variants.setOption("dimensions", variantOptions.dimensions || []);
|
|
700
|
+
variants.setOption("data", variantOptions.data || []);
|
|
701
|
+
variants.setOption("url", variantOptions.url ?? null);
|
|
702
|
+
variants.setOption("fetch", variantOptions.fetch || {});
|
|
703
|
+
variants.setOption("mapping", variantOptions.mapping || {});
|
|
704
|
+
variants.setOption("layout", variantOptions.layout || {});
|
|
705
|
+
variants.setOption("features.rowSelects", variantOptions.features.rowSelects);
|
|
706
|
+
variants.setOption(
|
|
707
|
+
"features.combinedSelect",
|
|
708
|
+
variantOptions.features.combinedSelect,
|
|
709
|
+
);
|
|
710
|
+
if (typeof variants.refresh === "function") {
|
|
711
|
+
variants.refresh();
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
/**
|
|
716
|
+
* @private
|
|
717
|
+
*/
|
|
718
|
+
function applyQuantity() {
|
|
719
|
+
const quantity = this.shadowRoot.querySelector("monster-quantity");
|
|
720
|
+
const quantityInput = this[quantityInputElementSymbol];
|
|
721
|
+
const quantityStatic = this[quantityStaticElementSymbol];
|
|
722
|
+
const quantityLabel = this.shadowRoot.querySelector(
|
|
723
|
+
"[data-monster-role=quantity-label]",
|
|
724
|
+
);
|
|
725
|
+
if (!quantity) {
|
|
726
|
+
return;
|
|
727
|
+
}
|
|
728
|
+
if (!quantityInput) {
|
|
729
|
+
return;
|
|
730
|
+
}
|
|
731
|
+
quantity.setAttribute(
|
|
732
|
+
"aria-label",
|
|
733
|
+
this.getOption("labels.quantityAria") || "Quantity",
|
|
734
|
+
);
|
|
735
|
+
quantityInput.setAttribute(
|
|
736
|
+
"aria-label",
|
|
737
|
+
this.getOption("labels.quantityAria") || "Quantity",
|
|
738
|
+
);
|
|
739
|
+
const options = this.getOption("quantity", {});
|
|
740
|
+
const precision =
|
|
741
|
+
this.getOption("features.allowDecimal") === true
|
|
742
|
+
? Number(options.precision ?? 2)
|
|
743
|
+
: 0;
|
|
744
|
+
const useDecimalInput = this.getOption("features.allowDecimal") === true;
|
|
745
|
+
const hasFixedQuantity = isFixedQuantity(options);
|
|
746
|
+
quantity.setOption("min", options.min);
|
|
747
|
+
quantity.setOption("max", options.max);
|
|
748
|
+
quantity.setOption("step", options.step);
|
|
749
|
+
quantity.setOption("precision", precision);
|
|
750
|
+
quantity.setOption("inputmode", useDecimalInput ? "decimal" : "numeric");
|
|
751
|
+
quantity.value = options.value ?? options.min ?? 1;
|
|
752
|
+
|
|
753
|
+
quantity.hidden = useDecimalInput || hasFixedQuantity;
|
|
754
|
+
quantityInput.hidden = !useDecimalInput || hasFixedQuantity;
|
|
755
|
+
quantityInput.step = options.step ?? 1;
|
|
756
|
+
quantityInput.min = Number.isFinite(options.min) ? options.min : "";
|
|
757
|
+
quantityInput.max = Number.isFinite(options.max) ? options.max : "";
|
|
758
|
+
quantityInput.value = formatQuantityForLocale(
|
|
759
|
+
Number.isFinite(options.value) ? options.value : (options.min ?? 1),
|
|
760
|
+
precision,
|
|
761
|
+
);
|
|
762
|
+
if (quantityStatic) {
|
|
763
|
+
quantityStatic.hidden = !hasFixedQuantity;
|
|
764
|
+
if (hasFixedQuantity) {
|
|
765
|
+
const label = this.getOption("labels.quantity") || "Quantity";
|
|
766
|
+
setElementText(
|
|
767
|
+
quantityStatic,
|
|
768
|
+
`${label}: ${formatQuantityForLocale(options.min ?? 1, precision)}`,
|
|
769
|
+
);
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
if (quantityLabel) {
|
|
773
|
+
quantityLabel.hidden = hasFixedQuantity;
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
if (this[quantityUnitElementSymbol]) {
|
|
777
|
+
const unit = this[productDataSymbol]?.product?.unitLabel;
|
|
778
|
+
this[quantityUnitElementSymbol].textContent =
|
|
779
|
+
unit && unit !== "" ? `(${unit})` : "";
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
/**
|
|
784
|
+
* @private
|
|
785
|
+
* @param {boolean} initial
|
|
786
|
+
*/
|
|
787
|
+
function updateState(initial = false) {
|
|
788
|
+
const variants = this.shadowRoot.querySelector("monster-variant-select");
|
|
789
|
+
const quantity = this.shadowRoot.querySelector("monster-quantity");
|
|
790
|
+
const selection = variants?.getOption?.("selection") || {};
|
|
791
|
+
const hasVariants = hasVariantOptions.call(this, variants);
|
|
792
|
+
const sku = hasVariants ? variants?.value : this.getOption("product.sku");
|
|
793
|
+
const qty = getQuantityValue.call(this, quantity);
|
|
794
|
+
|
|
795
|
+
const productBase =
|
|
796
|
+
this[productDataSymbol]?.product || this.getOption("product");
|
|
797
|
+
const pricingBase =
|
|
798
|
+
this[productDataSymbol]?.pricing || this.getOption("pricing");
|
|
799
|
+
const variantData = findSelectedVariantData.call(this, variants, selection);
|
|
800
|
+
const variantPricingActive = hasVariantPricing.call(this, variants);
|
|
801
|
+
const resolved = resolvePricing.call(
|
|
802
|
+
this,
|
|
803
|
+
pricingBase,
|
|
804
|
+
productBase,
|
|
805
|
+
variantData,
|
|
806
|
+
qty,
|
|
807
|
+
variantPricingActive,
|
|
808
|
+
);
|
|
809
|
+
const product = resolved.product;
|
|
810
|
+
const pricing = resolved.pricing;
|
|
811
|
+
const isPriceOk = resolved.priceAvailable !== false;
|
|
812
|
+
const stock = product?.stock;
|
|
813
|
+
const cartItem = findCartItem.call(this, sku);
|
|
814
|
+
const hasSku = isString(sku) && sku !== "";
|
|
815
|
+
|
|
816
|
+
const isValidQty = isQuantityAllowed.call(this, qty);
|
|
817
|
+
const isStockOk = hasSku
|
|
818
|
+
? Number.isFinite(stock)
|
|
819
|
+
? qty <= stock
|
|
820
|
+
: true
|
|
821
|
+
: true;
|
|
822
|
+
const isVariantOk = hasSku;
|
|
823
|
+
|
|
824
|
+
updatePriceDisplay.call(this, pricing, qty, product);
|
|
825
|
+
updateMessage.call(this, {
|
|
826
|
+
isValidQty,
|
|
827
|
+
isStockOk,
|
|
828
|
+
isVariantOk,
|
|
829
|
+
isPriceOk,
|
|
830
|
+
hasVariants,
|
|
831
|
+
});
|
|
832
|
+
|
|
833
|
+
const detail = {
|
|
834
|
+
sku,
|
|
835
|
+
selection,
|
|
836
|
+
qty,
|
|
837
|
+
product,
|
|
838
|
+
pricing,
|
|
839
|
+
cartItem,
|
|
840
|
+
priceAvailable: isPriceOk,
|
|
841
|
+
};
|
|
842
|
+
fireCustomEvent(this, "monster-buy-box-change", detail);
|
|
843
|
+
const changeAction = this.getOption("actions.onchange");
|
|
844
|
+
if (isFunction(changeAction)) {
|
|
845
|
+
changeAction.call(this, detail);
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
const valid = isValidQty && isStockOk && isVariantOk && isPriceOk;
|
|
849
|
+
if (!initial) {
|
|
850
|
+
if (valid) {
|
|
851
|
+
fireCustomEvent(this, "monster-buy-box-valid", detail);
|
|
852
|
+
const action = this.getOption("actions.onvalid");
|
|
853
|
+
if (isFunction(action)) {
|
|
854
|
+
action.call(this, detail);
|
|
855
|
+
}
|
|
856
|
+
} else {
|
|
857
|
+
fireCustomEvent(this, "monster-buy-box-invalid", detail);
|
|
858
|
+
const action = this.getOption("actions.oninvalid");
|
|
859
|
+
if (isFunction(action)) {
|
|
860
|
+
action.call(this, detail);
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
setSubmitEnabled.call(this, valid);
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
/**
|
|
869
|
+
* @private
|
|
870
|
+
* @param {Object} pricing
|
|
871
|
+
* @param {number} qty
|
|
872
|
+
* @param {Object} product
|
|
873
|
+
*/
|
|
874
|
+
function updatePriceDisplay(pricing, qty, product) {
|
|
875
|
+
const currency = product?.currency || "EUR";
|
|
876
|
+
const price = pricing?.price;
|
|
877
|
+
const listPrice = pricing?.listPrice;
|
|
878
|
+
const basePrice = pricing?.basePrice;
|
|
879
|
+
const baseUnit = pricing?.basePriceUnit;
|
|
880
|
+
const taxIncluded = product?.taxIncluded;
|
|
881
|
+
const taxRate = product?.taxRate;
|
|
882
|
+
const subtotal =
|
|
883
|
+
price !== null && price !== undefined && Number.isFinite(qty)
|
|
884
|
+
? price * qty
|
|
885
|
+
: null;
|
|
886
|
+
|
|
887
|
+
if (this[priceElementSymbol]) {
|
|
888
|
+
setElementText(
|
|
889
|
+
this[priceElementSymbol],
|
|
890
|
+
price !== null && price !== undefined ? formatMoney(price, currency) : "",
|
|
891
|
+
);
|
|
892
|
+
}
|
|
893
|
+
if (this[sumElementSymbol]) {
|
|
894
|
+
setElementText(
|
|
895
|
+
this[sumElementSymbol],
|
|
896
|
+
subtotal !== null ? formatMoney(subtotal, currency) : "",
|
|
897
|
+
);
|
|
898
|
+
}
|
|
899
|
+
if (this[listPriceElementSymbol]) {
|
|
900
|
+
setElementText(
|
|
901
|
+
this[listPriceElementSymbol],
|
|
902
|
+
listPrice !== null && listPrice !== undefined
|
|
903
|
+
? formatMoney(listPrice, currency)
|
|
904
|
+
: "",
|
|
905
|
+
);
|
|
906
|
+
}
|
|
907
|
+
if (this[basePriceElementSymbol]) {
|
|
908
|
+
if (basePrice !== null && basePrice !== undefined) {
|
|
909
|
+
const base = formatMoney(basePrice, currency);
|
|
910
|
+
setElementText(
|
|
911
|
+
this[basePriceElementSymbol],
|
|
912
|
+
baseUnit ? `${base} / ${baseUnit}` : base,
|
|
913
|
+
);
|
|
914
|
+
} else {
|
|
915
|
+
setElementText(this[basePriceElementSymbol], "");
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
if (this[taxElementSymbol]) {
|
|
919
|
+
const label = taxIncluded
|
|
920
|
+
? this.getOption("labels.taxIncluded")
|
|
921
|
+
: this.getOption("labels.taxExcluded");
|
|
922
|
+
if (Number.isFinite(taxRate)) {
|
|
923
|
+
setElementText(this[taxElementSymbol], `${label} (${taxRate}%)`);
|
|
924
|
+
} else {
|
|
925
|
+
setElementText(this[taxElementSymbol], label);
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
if (this[totalElementSymbol]) {
|
|
929
|
+
const total = subtotal;
|
|
930
|
+
setElementText(
|
|
931
|
+
this[totalElementSymbol],
|
|
932
|
+
total !== null ? formatMoney(total, currency) : "",
|
|
933
|
+
);
|
|
934
|
+
}
|
|
935
|
+
if (this[deliveryElementSymbol]) {
|
|
936
|
+
setElementText(
|
|
937
|
+
this[deliveryElementSymbol],
|
|
938
|
+
product?.delivery ?? this.getOption("labels.deliveryUnknown"),
|
|
939
|
+
);
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
/**
|
|
944
|
+
* @private
|
|
945
|
+
* @param {Object} state
|
|
946
|
+
*/
|
|
947
|
+
function updateMessage(state) {
|
|
948
|
+
if (this.getOption("features.messages") === false) {
|
|
949
|
+
if (this[messageElementSymbol]) {
|
|
950
|
+
this[messageElementSymbol].textContent = "";
|
|
951
|
+
}
|
|
952
|
+
return;
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
if (!this[messageElementSymbol]) {
|
|
956
|
+
return;
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
let msg = "";
|
|
960
|
+
msg = getInvalidMessage.call(this, state);
|
|
961
|
+
|
|
962
|
+
this[messageElementSymbol].textContent = msg;
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
/**
|
|
966
|
+
* @private
|
|
967
|
+
* @param {Object} state
|
|
968
|
+
* @return {string}
|
|
969
|
+
*/
|
|
970
|
+
function getInvalidMessage(state) {
|
|
971
|
+
if (!state) {
|
|
972
|
+
return "";
|
|
973
|
+
}
|
|
974
|
+
if (!state.isVariantOk && state.hasVariants) {
|
|
975
|
+
return this.getOption("labels.selectVariant");
|
|
976
|
+
}
|
|
977
|
+
if (state.isPriceOk === false) {
|
|
978
|
+
return this.getOption("labels.priceUnavailable");
|
|
979
|
+
}
|
|
980
|
+
if (!state.isValidQty) {
|
|
981
|
+
return this.getOption("labels.selectQuantity");
|
|
982
|
+
}
|
|
983
|
+
if (!state.isStockOk) {
|
|
984
|
+
return this.getOption("labels.outOfStock");
|
|
985
|
+
}
|
|
986
|
+
return "";
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
/**
|
|
990
|
+
* @private
|
|
991
|
+
* @param {boolean} enabled
|
|
992
|
+
*/
|
|
993
|
+
function setSubmitEnabled(enabled) {
|
|
994
|
+
if (!this[submitElementSymbol]) {
|
|
995
|
+
return;
|
|
996
|
+
}
|
|
997
|
+
this[submitElementSymbol].setOption("features.disableButton", false);
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
/**
|
|
1001
|
+
* @private
|
|
1002
|
+
*/
|
|
1003
|
+
function handleSubmit() {
|
|
1004
|
+
const variants = this.shadowRoot.querySelector("monster-variant-select");
|
|
1005
|
+
const quantity = this.shadowRoot.querySelector("monster-quantity");
|
|
1006
|
+
const hasVariants = hasVariantOptions.call(this, variants);
|
|
1007
|
+
const sku = hasVariants ? variants?.value : this.getOption("product.sku");
|
|
1008
|
+
const qty = getQuantityValue.call(this, quantity);
|
|
1009
|
+
const selection = variants?.getOption?.("selection") || {};
|
|
1010
|
+
const productBase =
|
|
1011
|
+
this[productDataSymbol]?.product || this.getOption("product");
|
|
1012
|
+
const pricingBase =
|
|
1013
|
+
this[productDataSymbol]?.pricing || this.getOption("pricing");
|
|
1014
|
+
const variantData = findSelectedVariantData.call(this, variants, selection);
|
|
1015
|
+
const variantPricingActive = hasVariantPricing.call(this, variants);
|
|
1016
|
+
const resolved = resolvePricing.call(
|
|
1017
|
+
this,
|
|
1018
|
+
pricingBase,
|
|
1019
|
+
productBase,
|
|
1020
|
+
variantData,
|
|
1021
|
+
qty,
|
|
1022
|
+
variantPricingActive,
|
|
1023
|
+
);
|
|
1024
|
+
const product = resolved.product;
|
|
1025
|
+
const pricing = resolved.pricing;
|
|
1026
|
+
const isPriceOk = resolved.priceAvailable !== false;
|
|
1027
|
+
|
|
1028
|
+
const hasSku = isString(sku) && sku !== "";
|
|
1029
|
+
const stock = product?.stock;
|
|
1030
|
+
const isValidQty = isQuantityAllowed.call(this, qty);
|
|
1031
|
+
const isStockOk = hasSku
|
|
1032
|
+
? Number.isFinite(stock)
|
|
1033
|
+
? qty <= stock
|
|
1034
|
+
: true
|
|
1035
|
+
: true;
|
|
1036
|
+
const isVariantOk = hasSku;
|
|
1037
|
+
const valid = isValidQty && isStockOk && isVariantOk && isPriceOk;
|
|
1038
|
+
if (!valid) {
|
|
1039
|
+
updateState.call(this);
|
|
1040
|
+
const button = this[submitElementSymbol];
|
|
1041
|
+
const message = getInvalidMessage.call(this, {
|
|
1042
|
+
isValidQty,
|
|
1043
|
+
isStockOk,
|
|
1044
|
+
isVariantOk,
|
|
1045
|
+
isPriceOk,
|
|
1046
|
+
hasVariants,
|
|
1047
|
+
});
|
|
1048
|
+
button?.setState("failed", 3000);
|
|
1049
|
+
button?.setMessage(message);
|
|
1050
|
+
button?.showMessage?.(3000);
|
|
1051
|
+
return;
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
const detail = { sku, qty, selection, product, pricing };
|
|
1055
|
+
fireCustomEvent(this, "monster-buy-box-submit", detail);
|
|
1056
|
+
const action = this.getOption("actions.onsubmit");
|
|
1057
|
+
if (isFunction(action)) {
|
|
1058
|
+
action.call(this, detail);
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
const button = this[submitElementSymbol];
|
|
1062
|
+
const cartControl = getCartControl.call(this);
|
|
1063
|
+
if (cartControl && isFunction(cartControl.addOrChange)) {
|
|
1064
|
+
this[pendingCartPayloadSymbol] = detail;
|
|
1065
|
+
button?.setState("activity");
|
|
1066
|
+
const onVerified = (event) => {
|
|
1067
|
+
if (!matchesCartPayload.call(this, event?.detail)) {
|
|
1068
|
+
return;
|
|
1069
|
+
}
|
|
1070
|
+
this[pendingCartPayloadSymbol] = null;
|
|
1071
|
+
cartControl.removeEventListener(
|
|
1072
|
+
"monster-cart-control-verified",
|
|
1073
|
+
onVerified,
|
|
1074
|
+
);
|
|
1075
|
+
cartControl.removeEventListener("monster-cart-control-error", onError);
|
|
1076
|
+
const responseData = event?.detail?.data;
|
|
1077
|
+
const items = Array.isArray(responseData)
|
|
1078
|
+
? responseData
|
|
1079
|
+
: Array.isArray(responseData?.items)
|
|
1080
|
+
? responseData.items
|
|
1081
|
+
: null;
|
|
1082
|
+
if (items) {
|
|
1083
|
+
this[cartDataSymbol] = items;
|
|
1084
|
+
updateState.call(this);
|
|
1085
|
+
}
|
|
1086
|
+
button?.setState("successful", 2000);
|
|
1087
|
+
fireCustomEvent(this, "monster-buy-box-success", {
|
|
1088
|
+
sku,
|
|
1089
|
+
qty,
|
|
1090
|
+
payload: event?.detail?.data,
|
|
1091
|
+
});
|
|
1092
|
+
const okAction = this.getOption("actions.onsuccess");
|
|
1093
|
+
if (isFunction(okAction)) {
|
|
1094
|
+
okAction.call(this, { sku, qty, payload: event?.detail?.data });
|
|
1095
|
+
}
|
|
1096
|
+
};
|
|
1097
|
+
const onError = (event) => {
|
|
1098
|
+
if (!matchesCartPayload.call(this, event?.detail)) {
|
|
1099
|
+
return;
|
|
1100
|
+
}
|
|
1101
|
+
this[pendingCartPayloadSymbol] = null;
|
|
1102
|
+
cartControl.removeEventListener(
|
|
1103
|
+
"monster-cart-control-verified",
|
|
1104
|
+
onVerified,
|
|
1105
|
+
);
|
|
1106
|
+
cartControl.removeEventListener("monster-cart-control-error", onError);
|
|
1107
|
+
button?.setState("failed", 3000);
|
|
1108
|
+
button?.setMessage(this.getOption("labels.addToCartError"));
|
|
1109
|
+
button?.showMessage?.(3000);
|
|
1110
|
+
fireCustomEvent(this, "monster-buy-box-error", {
|
|
1111
|
+
sku,
|
|
1112
|
+
qty,
|
|
1113
|
+
error: event?.detail?.error,
|
|
1114
|
+
});
|
|
1115
|
+
const errAction = this.getOption("actions.onerror");
|
|
1116
|
+
if (isFunction(errAction)) {
|
|
1117
|
+
errAction.call(this, event?.detail?.error);
|
|
1118
|
+
}
|
|
1119
|
+
};
|
|
1120
|
+
cartControl.addEventListener("monster-cart-control-verified", onVerified);
|
|
1121
|
+
cartControl.addEventListener("monster-cart-control-error", onError);
|
|
1122
|
+
cartControl.addOrChange(detail);
|
|
1123
|
+
return;
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
const url = this.getOption("cart.url");
|
|
1127
|
+
if (!isString(url) || url === "") {
|
|
1128
|
+
const notConfigured = this.getOption("labels.addToCartNotConfigured");
|
|
1129
|
+
button?.setState("failed", 3000);
|
|
1130
|
+
button?.setMessage(notConfigured);
|
|
1131
|
+
button?.showMessage?.(3000);
|
|
1132
|
+
fireCustomEvent(this, "monster-buy-box-error", {
|
|
1133
|
+
sku,
|
|
1134
|
+
qty,
|
|
1135
|
+
error: new Error("cart url not configured"),
|
|
1136
|
+
});
|
|
1137
|
+
const errAction = this.getOption("actions.onerror");
|
|
1138
|
+
if (isFunction(errAction)) {
|
|
1139
|
+
errAction.call(this, new Error("cart url not configured"));
|
|
1140
|
+
}
|
|
1141
|
+
return;
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
button?.setState("activity");
|
|
1145
|
+
|
|
1146
|
+
fetch(url, buildCartRequest.call(this, detail))
|
|
1147
|
+
.then(async (response) => {
|
|
1148
|
+
if (!response.ok) {
|
|
1149
|
+
throw new Error("add to cart failed");
|
|
1150
|
+
}
|
|
1151
|
+
const payload = await response.json();
|
|
1152
|
+
button?.setState("successful", 2000);
|
|
1153
|
+
fireCustomEvent(this, "monster-buy-box-success", { sku, qty, payload });
|
|
1154
|
+
const okAction = this.getOption("actions.onsuccess");
|
|
1155
|
+
if (isFunction(okAction)) {
|
|
1156
|
+
okAction.call(this, { sku, qty, payload });
|
|
1157
|
+
}
|
|
1158
|
+
})
|
|
1159
|
+
.catch((e) => {
|
|
1160
|
+
button?.setState("failed", 3000);
|
|
1161
|
+
button?.setMessage(this.getOption("labels.addToCartError"));
|
|
1162
|
+
button?.showMessage?.(3000);
|
|
1163
|
+
fireCustomEvent(this, "monster-buy-box-error", { sku, qty, error: e });
|
|
1164
|
+
const errAction = this.getOption("actions.onerror");
|
|
1165
|
+
if (isFunction(errAction)) {
|
|
1166
|
+
errAction.call(this, e);
|
|
1167
|
+
}
|
|
1168
|
+
});
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
/**
|
|
1172
|
+
* @private
|
|
1173
|
+
* @param {Object} detail
|
|
1174
|
+
* @return {boolean}
|
|
1175
|
+
*/
|
|
1176
|
+
function matchesCartPayload(detail) {
|
|
1177
|
+
const pending = this[pendingCartPayloadSymbol];
|
|
1178
|
+
if (!pending) {
|
|
1179
|
+
return false;
|
|
1180
|
+
}
|
|
1181
|
+
const source = detail?.source || detail?.payload;
|
|
1182
|
+
if (!source || typeof source !== "object") {
|
|
1183
|
+
return true;
|
|
1184
|
+
}
|
|
1185
|
+
if (isString(source.sku) && source.sku !== pending.sku) {
|
|
1186
|
+
return false;
|
|
1187
|
+
}
|
|
1188
|
+
if (source.qty !== undefined && Number(source.qty) !== Number(pending.qty)) {
|
|
1189
|
+
return false;
|
|
1190
|
+
}
|
|
1191
|
+
return true;
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
/**
|
|
1195
|
+
* @private
|
|
1196
|
+
* @param {Object} data
|
|
1197
|
+
* @return {Object}
|
|
1198
|
+
*/
|
|
1199
|
+
function buildCartRequest(data) {
|
|
1200
|
+
const headers = Object.assign({}, this.getOption("cart.headers", {}));
|
|
1201
|
+
const method = this.getOption("cart.method") || "POST";
|
|
1202
|
+
const bodyTemplate = this.getOption("cart.bodyTemplate");
|
|
1203
|
+
let body = null;
|
|
1204
|
+
|
|
1205
|
+
if (isString(bodyTemplate) && bodyTemplate.includes("${")) {
|
|
1206
|
+
body = new Formatter(stringifyForTemplate(data)).format(bodyTemplate);
|
|
1207
|
+
} else {
|
|
1208
|
+
body = JSON.stringify(data);
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
return { method, headers, body };
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
/**
|
|
1215
|
+
* @private
|
|
1216
|
+
* @param {Object|Array|string|number|boolean|null} value
|
|
1217
|
+
* @return {Object|Array|string}
|
|
1218
|
+
*/
|
|
1219
|
+
function stringifyForTemplate(value) {
|
|
1220
|
+
if (value === null || value === undefined) {
|
|
1221
|
+
return "";
|
|
1222
|
+
}
|
|
1223
|
+
if (Array.isArray(value)) {
|
|
1224
|
+
return value.map((entry) => stringifyForTemplate(entry));
|
|
1225
|
+
}
|
|
1226
|
+
if (typeof value === "object") {
|
|
1227
|
+
const result = {};
|
|
1228
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
1229
|
+
result[key] = stringifyForTemplate(entry);
|
|
1230
|
+
}
|
|
1231
|
+
return result;
|
|
1232
|
+
}
|
|
1233
|
+
return `${value}`;
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1236
|
+
/**
|
|
1237
|
+
* @private
|
|
1238
|
+
* @param {number} value
|
|
1239
|
+
* @return {boolean}
|
|
1240
|
+
*/
|
|
1241
|
+
function isQuantityAllowed(value) {
|
|
1242
|
+
if (!Number.isFinite(value)) {
|
|
1243
|
+
return false;
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1246
|
+
const options = this.getOption("quantity", {});
|
|
1247
|
+
if (this.getOption("features.allowDecimal") !== true) {
|
|
1248
|
+
if (!Number.isInteger(value)) {
|
|
1249
|
+
return false;
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
const min = Number(options.min);
|
|
1253
|
+
const max = Number(options.max);
|
|
1254
|
+
if (Number.isFinite(min) && value < min) {
|
|
1255
|
+
return false;
|
|
1256
|
+
}
|
|
1257
|
+
if (Number.isFinite(max) && value > max) {
|
|
1258
|
+
return false;
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
const allowed = options.allowed;
|
|
1262
|
+
if (!allowed) {
|
|
1263
|
+
return true;
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
const { list, ranges } = parseAllowed(allowed);
|
|
1267
|
+
if (list.length === 0 && ranges.length === 0) {
|
|
1268
|
+
return true;
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
const allowDecimal = this.getOption("features.allowDecimal") === true;
|
|
1272
|
+
const precision = Number.isFinite(options.precision)
|
|
1273
|
+
? options.precision
|
|
1274
|
+
: allowDecimal
|
|
1275
|
+
? 2
|
|
1276
|
+
: 0;
|
|
1277
|
+
const rounded = roundQuantity(value, precision, options.rounding);
|
|
1278
|
+
if (list.includes(rounded)) {
|
|
1279
|
+
return true;
|
|
1280
|
+
}
|
|
1281
|
+
for (const range of ranges) {
|
|
1282
|
+
if (rounded >= range.min && rounded <= range.max) {
|
|
1283
|
+
return true;
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
return false;
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
/**
|
|
1290
|
+
* @private
|
|
1291
|
+
* @param {string|Array} allowed
|
|
1292
|
+
* @return {Object}
|
|
1293
|
+
*/
|
|
1294
|
+
function parseAllowed(allowed) {
|
|
1295
|
+
if (Array.isArray(allowed)) {
|
|
1296
|
+
return {
|
|
1297
|
+
list: allowed.filter((v) => Number.isFinite(v)),
|
|
1298
|
+
ranges: [],
|
|
1299
|
+
};
|
|
1300
|
+
}
|
|
1301
|
+
if (!isString(allowed)) {
|
|
1302
|
+
return { list: [], ranges: [] };
|
|
1303
|
+
}
|
|
1304
|
+
const list = [];
|
|
1305
|
+
const ranges = [];
|
|
1306
|
+
const parts = allowed.split(",");
|
|
1307
|
+
for (const part of parts) {
|
|
1308
|
+
const trimmed = part.trim();
|
|
1309
|
+
if (trimmed === "") {
|
|
1310
|
+
continue;
|
|
1311
|
+
}
|
|
1312
|
+
if (trimmed.includes("-")) {
|
|
1313
|
+
const [minRaw, maxRaw] = trimmed.split("-");
|
|
1314
|
+
const min = minRaw === "" ? Number.NEGATIVE_INFINITY : Number(minRaw);
|
|
1315
|
+
const max = maxRaw === "" ? Number.POSITIVE_INFINITY : Number(maxRaw);
|
|
1316
|
+
if (Number.isFinite(min) || Number.isFinite(max)) {
|
|
1317
|
+
ranges.push({ min, max });
|
|
1318
|
+
}
|
|
1319
|
+
continue;
|
|
1320
|
+
}
|
|
1321
|
+
const value = Number(trimmed);
|
|
1322
|
+
if (Number.isFinite(value)) {
|
|
1323
|
+
list.push(value);
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
return { list, ranges };
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
/**
|
|
1330
|
+
* @private
|
|
1331
|
+
* @param {HTMLElement|null} variants
|
|
1332
|
+
* @param {Object} selection
|
|
1333
|
+
* @return {Object|null}
|
|
1334
|
+
*/
|
|
1335
|
+
function findSelectedVariantData(variants, selection) {
|
|
1336
|
+
if (!(variants instanceof HTMLElement)) {
|
|
1337
|
+
return null;
|
|
1338
|
+
}
|
|
1339
|
+
const data = variants.getOption?.("data") || [];
|
|
1340
|
+
const dimensions = variants.getOption?.("dimensions") || [];
|
|
1341
|
+
if (!Array.isArray(data) || data.length === 0) {
|
|
1342
|
+
return null;
|
|
1343
|
+
}
|
|
1344
|
+
if (!selection || Object.keys(selection).length === 0) {
|
|
1345
|
+
return null;
|
|
1346
|
+
}
|
|
1347
|
+
for (const item of data) {
|
|
1348
|
+
let matches = true;
|
|
1349
|
+
for (const def of dimensions) {
|
|
1350
|
+
const key = def?.key;
|
|
1351
|
+
if (!isString(key) || key === "") {
|
|
1352
|
+
continue;
|
|
1353
|
+
}
|
|
1354
|
+
const expected = selection?.[key];
|
|
1355
|
+
if (!isString(expected) || expected === "") {
|
|
1356
|
+
matches = false;
|
|
1357
|
+
break;
|
|
1358
|
+
}
|
|
1359
|
+
const value = getDimensionValueFromItem(item, def);
|
|
1360
|
+
if (String(value) !== String(expected)) {
|
|
1361
|
+
matches = false;
|
|
1362
|
+
break;
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
if (matches) {
|
|
1366
|
+
return item;
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
return null;
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
/**
|
|
1373
|
+
* @private
|
|
1374
|
+
* @param {HTMLElement|null} variants
|
|
1375
|
+
* @return {boolean}
|
|
1376
|
+
*/
|
|
1377
|
+
function hasVariantPricing(variants) {
|
|
1378
|
+
if (!(variants instanceof HTMLElement)) {
|
|
1379
|
+
return false;
|
|
1380
|
+
}
|
|
1381
|
+
const data = variants.getOption?.("data") || [];
|
|
1382
|
+
if (!Array.isArray(data) || data.length === 0) {
|
|
1383
|
+
return false;
|
|
1384
|
+
}
|
|
1385
|
+
const pricingKeys = getVariantPricingKeys.call(this);
|
|
1386
|
+
const priceKey = pricingKeys.priceKey;
|
|
1387
|
+
const tiersKey = pricingKeys.tiersKey;
|
|
1388
|
+
for (const item of data) {
|
|
1389
|
+
if (isString(priceKey) && priceKey !== "") {
|
|
1390
|
+
const value = item?.[priceKey];
|
|
1391
|
+
if (value !== null && value !== undefined && value !== "") {
|
|
1392
|
+
return true;
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
if (isString(tiersKey) && tiersKey !== "") {
|
|
1396
|
+
const tiers = item?.[tiersKey];
|
|
1397
|
+
if (Array.isArray(tiers) && tiers.length > 0) {
|
|
1398
|
+
return true;
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
return false;
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
/**
|
|
1406
|
+
* @private
|
|
1407
|
+
* @param {Object} item
|
|
1408
|
+
* @param {Object} def
|
|
1409
|
+
* @return {string|null}
|
|
1410
|
+
*/
|
|
1411
|
+
function getDimensionValueFromItem(item, def) {
|
|
1412
|
+
const template = def?.valueTemplate;
|
|
1413
|
+
if (isString(template) && template !== "") {
|
|
1414
|
+
return new Formatter(item).format(template);
|
|
1415
|
+
}
|
|
1416
|
+
const key = def?.key;
|
|
1417
|
+
if (!isString(key) || key === "") {
|
|
1418
|
+
return null;
|
|
1419
|
+
}
|
|
1420
|
+
try {
|
|
1421
|
+
return new Pathfinder(item).getVia(key);
|
|
1422
|
+
} catch (_e) {
|
|
1423
|
+
return item?.[key];
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1427
|
+
/**
|
|
1428
|
+
* @private
|
|
1429
|
+
* @param {Object} pricing
|
|
1430
|
+
* @param {Object} product
|
|
1431
|
+
* @param {Object|null} variantData
|
|
1432
|
+
* @param {number} qty
|
|
1433
|
+
* @return {{pricing: Object, product: Object}}
|
|
1434
|
+
*/
|
|
1435
|
+
function resolvePricing(
|
|
1436
|
+
pricing,
|
|
1437
|
+
product,
|
|
1438
|
+
variantData,
|
|
1439
|
+
qty,
|
|
1440
|
+
variantPricingActive = false,
|
|
1441
|
+
) {
|
|
1442
|
+
let nextPricing = Object.assign({}, pricing);
|
|
1443
|
+
let nextProduct = Object.assign({}, product);
|
|
1444
|
+
let priceAvailable = true;
|
|
1445
|
+
|
|
1446
|
+
if (variantPricingActive) {
|
|
1447
|
+
nextPricing = Object.assign({}, nextPricing, {
|
|
1448
|
+
price: null,
|
|
1449
|
+
listPrice: null,
|
|
1450
|
+
basePrice: null,
|
|
1451
|
+
basePriceUnit: null,
|
|
1452
|
+
tiers: [],
|
|
1453
|
+
});
|
|
1454
|
+
if (!variantData) {
|
|
1455
|
+
priceAvailable = false;
|
|
1456
|
+
}
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1459
|
+
if (variantData) {
|
|
1460
|
+
const pricingKeys = getVariantPricingKeys.call(this);
|
|
1461
|
+
const priceKey = pricingKeys.priceKey;
|
|
1462
|
+
const listPriceKey = pricingKeys.listPriceKey;
|
|
1463
|
+
const basePriceKey = pricingKeys.basePriceKey;
|
|
1464
|
+
const basePriceUnitKey = pricingKeys.basePriceUnitKey;
|
|
1465
|
+
const currencyKey = pricingKeys.currencyKey;
|
|
1466
|
+
const taxRateKey = pricingKeys.taxRateKey;
|
|
1467
|
+
const taxIncludedKey = pricingKeys.taxIncludedKey;
|
|
1468
|
+
const tiersKey = pricingKeys.tiersKey;
|
|
1469
|
+
|
|
1470
|
+
const variantPrice =
|
|
1471
|
+
isString(priceKey) && priceKey !== "" ? variantData?.[priceKey] : null;
|
|
1472
|
+
const variantListPrice =
|
|
1473
|
+
isString(listPriceKey) && listPriceKey !== ""
|
|
1474
|
+
? variantData?.[listPriceKey]
|
|
1475
|
+
: null;
|
|
1476
|
+
const variantBasePrice =
|
|
1477
|
+
isString(basePriceKey) && basePriceKey !== ""
|
|
1478
|
+
? variantData?.[basePriceKey]
|
|
1479
|
+
: null;
|
|
1480
|
+
const variantBaseUnit =
|
|
1481
|
+
isString(basePriceUnitKey) && basePriceUnitKey !== ""
|
|
1482
|
+
? variantData?.[basePriceUnitKey]
|
|
1483
|
+
: null;
|
|
1484
|
+
const variantCurrency =
|
|
1485
|
+
isString(currencyKey) && currencyKey !== ""
|
|
1486
|
+
? variantData?.[currencyKey]
|
|
1487
|
+
: null;
|
|
1488
|
+
const variantTaxRate =
|
|
1489
|
+
isString(taxRateKey) && taxRateKey !== ""
|
|
1490
|
+
? variantData?.[taxRateKey]
|
|
1491
|
+
: null;
|
|
1492
|
+
const variantTaxIncluded =
|
|
1493
|
+
isString(taxIncludedKey) && taxIncludedKey !== ""
|
|
1494
|
+
? variantData?.[taxIncludedKey]
|
|
1495
|
+
: null;
|
|
1496
|
+
const variantTiers =
|
|
1497
|
+
isString(tiersKey) && tiersKey !== "" ? variantData?.[tiersKey] : null;
|
|
1498
|
+
|
|
1499
|
+
if (variantPrice !== null && variantPrice !== undefined) {
|
|
1500
|
+
nextPricing.price = parseNumber(variantPrice, nextPricing.price);
|
|
1501
|
+
}
|
|
1502
|
+
if (variantListPrice !== null && variantListPrice !== undefined) {
|
|
1503
|
+
nextPricing.listPrice = parseNumber(
|
|
1504
|
+
variantListPrice,
|
|
1505
|
+
nextPricing.listPrice,
|
|
1506
|
+
);
|
|
1507
|
+
}
|
|
1508
|
+
if (variantBasePrice !== null && variantBasePrice !== undefined) {
|
|
1509
|
+
nextPricing.basePrice = parseNumber(
|
|
1510
|
+
variantBasePrice,
|
|
1511
|
+
nextPricing.basePrice,
|
|
1512
|
+
);
|
|
1513
|
+
}
|
|
1514
|
+
if (variantBaseUnit !== null && variantBaseUnit !== undefined) {
|
|
1515
|
+
nextPricing.basePriceUnit = variantBaseUnit;
|
|
1516
|
+
}
|
|
1517
|
+
if (variantCurrency !== null && variantCurrency !== undefined) {
|
|
1518
|
+
nextProduct.currency = variantCurrency;
|
|
1519
|
+
}
|
|
1520
|
+
if (variantTaxRate !== null && variantTaxRate !== undefined) {
|
|
1521
|
+
nextProduct.taxRate = parseNumber(variantTaxRate, nextProduct.taxRate);
|
|
1522
|
+
}
|
|
1523
|
+
if (variantTaxIncluded !== null && variantTaxIncluded !== undefined) {
|
|
1524
|
+
nextProduct.taxIncluded = parseBoolean(
|
|
1525
|
+
variantTaxIncluded,
|
|
1526
|
+
nextProduct.taxIncluded,
|
|
1527
|
+
);
|
|
1528
|
+
}
|
|
1529
|
+
if (Array.isArray(variantTiers)) {
|
|
1530
|
+
nextPricing.tiers = variantTiers;
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
const tiers = Array.isArray(nextPricing.tiers)
|
|
1535
|
+
? nextPricing.tiers
|
|
1536
|
+
: Array.isArray(this.getOption("pricing.tiers"))
|
|
1537
|
+
? this.getOption("pricing.tiers")
|
|
1538
|
+
: [];
|
|
1539
|
+
if (tiers.length > 0 && Number.isFinite(qty)) {
|
|
1540
|
+
const tier = findTierForQuantity(tiers, qty);
|
|
1541
|
+
if (tier) {
|
|
1542
|
+
const tierResult = applyTier(tier, nextPricing);
|
|
1543
|
+
nextPricing = Object.assign({}, nextPricing, tierResult.pricing);
|
|
1544
|
+
if (tierResult.currency) {
|
|
1545
|
+
nextProduct.currency = tierResult.currency;
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
|
|
1550
|
+
if (variantPricingActive) {
|
|
1551
|
+
priceAvailable = Number.isFinite(nextPricing.price);
|
|
1552
|
+
}
|
|
1553
|
+
|
|
1554
|
+
return { pricing: nextPricing, product: nextProduct, priceAvailable };
|
|
1555
|
+
}
|
|
1556
|
+
|
|
1557
|
+
/**
|
|
1558
|
+
* @private
|
|
1559
|
+
* @return {Object}
|
|
1560
|
+
*/
|
|
1561
|
+
function getVariantPricingKeys() {
|
|
1562
|
+
const defaults = {
|
|
1563
|
+
priceKey: "price",
|
|
1564
|
+
listPriceKey: "listPrice",
|
|
1565
|
+
basePriceKey: "basePrice",
|
|
1566
|
+
basePriceUnitKey: "basePriceUnit",
|
|
1567
|
+
currencyKey: "currency",
|
|
1568
|
+
taxRateKey: "taxRate",
|
|
1569
|
+
taxIncludedKey: "taxIncluded",
|
|
1570
|
+
tiersKey: "tiers",
|
|
1571
|
+
};
|
|
1572
|
+
const configured = this.getOption("variants.pricing");
|
|
1573
|
+
if (!isObject(configured)) {
|
|
1574
|
+
return defaults;
|
|
1575
|
+
}
|
|
1576
|
+
return Object.assign({}, defaults, configured);
|
|
1577
|
+
}
|
|
1578
|
+
|
|
1579
|
+
/**
|
|
1580
|
+
* @private
|
|
1581
|
+
* @param {Array} tiers
|
|
1582
|
+
* @param {number} qty
|
|
1583
|
+
* @return {Object|null}
|
|
1584
|
+
*/
|
|
1585
|
+
function findTierForQuantity(tiers, qty) {
|
|
1586
|
+
let match = null;
|
|
1587
|
+
for (const tier of tiers) {
|
|
1588
|
+
const minRaw = tier?.min ?? tier?.from ?? tier?.qty ?? null;
|
|
1589
|
+
const maxRaw = tier?.max ?? tier?.to ?? null;
|
|
1590
|
+
const min = minRaw === null ? 1 : Number(minRaw);
|
|
1591
|
+
const max = maxRaw === null || maxRaw === undefined ? null : Number(maxRaw);
|
|
1592
|
+
if (!Number.isFinite(min)) {
|
|
1593
|
+
continue;
|
|
1594
|
+
}
|
|
1595
|
+
if (qty < min) {
|
|
1596
|
+
continue;
|
|
1597
|
+
}
|
|
1598
|
+
if (max !== null && Number.isFinite(max) && qty > max) {
|
|
1599
|
+
continue;
|
|
1600
|
+
}
|
|
1601
|
+
if (!match || min > Number(match.min ?? 0)) {
|
|
1602
|
+
match = Object.assign({}, tier, { min, max });
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
return match;
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1608
|
+
/**
|
|
1609
|
+
* @private
|
|
1610
|
+
* @param {Object} tier
|
|
1611
|
+
* @param {Object} fallback
|
|
1612
|
+
* @return {Object}
|
|
1613
|
+
*/
|
|
1614
|
+
function applyTier(tier, fallback) {
|
|
1615
|
+
const next = Object.assign({}, fallback);
|
|
1616
|
+
let currency = null;
|
|
1617
|
+
if (tier.price !== undefined) {
|
|
1618
|
+
next.price = parseNumber(tier.price, next.price);
|
|
1619
|
+
}
|
|
1620
|
+
if (tier.listPrice !== undefined) {
|
|
1621
|
+
next.listPrice = parseNumber(tier.listPrice, next.listPrice);
|
|
1622
|
+
}
|
|
1623
|
+
if (tier.basePrice !== undefined) {
|
|
1624
|
+
next.basePrice = parseNumber(tier.basePrice, next.basePrice);
|
|
1625
|
+
}
|
|
1626
|
+
if (tier.basePriceUnit !== undefined) {
|
|
1627
|
+
next.basePriceUnit = tier.basePriceUnit;
|
|
1628
|
+
}
|
|
1629
|
+
if (tier.currency !== undefined) {
|
|
1630
|
+
currency = tier.currency;
|
|
1631
|
+
}
|
|
1632
|
+
return { pricing: next, currency };
|
|
1633
|
+
}
|
|
1634
|
+
|
|
1635
|
+
/**
|
|
1636
|
+
* @private
|
|
1637
|
+
* @param {string|null} sku
|
|
1638
|
+
* @return {string}
|
|
1639
|
+
*/
|
|
1640
|
+
function getPricingCacheKey(sku) {
|
|
1641
|
+
if (isString(sku) && sku !== "") {
|
|
1642
|
+
return sku;
|
|
1643
|
+
}
|
|
1644
|
+
return "__base__";
|
|
1645
|
+
}
|
|
1646
|
+
|
|
1647
|
+
/**
|
|
1648
|
+
* @private
|
|
1649
|
+
* @param {{product: Object, pricing: Object}|null} cached
|
|
1650
|
+
*/
|
|
1651
|
+
function applyPricingCache(cached) {
|
|
1652
|
+
if (!cached) {
|
|
1653
|
+
return;
|
|
1654
|
+
}
|
|
1655
|
+
this[productDataSymbol] = Object.assign({}, this[productDataSymbol], {
|
|
1656
|
+
product: cached.product,
|
|
1657
|
+
pricing: cached.pricing,
|
|
1658
|
+
});
|
|
1659
|
+
}
|
|
1660
|
+
|
|
1661
|
+
/**
|
|
1662
|
+
* @private
|
|
1663
|
+
* @param {Object} options
|
|
1664
|
+
* @return {boolean}
|
|
1665
|
+
*/
|
|
1666
|
+
function isFixedQuantity(options) {
|
|
1667
|
+
const min = Number(options.min);
|
|
1668
|
+
const max = Number(options.max);
|
|
1669
|
+
if (!Number.isFinite(min) || !Number.isFinite(max)) {
|
|
1670
|
+
return false;
|
|
1671
|
+
}
|
|
1672
|
+
return min === 1 && max === 1;
|
|
1673
|
+
}
|
|
1674
|
+
|
|
1675
|
+
/**
|
|
1676
|
+
* @private
|
|
1677
|
+
* @param {HTMLElement|null} variants
|
|
1678
|
+
* @return {boolean}
|
|
1679
|
+
*/
|
|
1680
|
+
function hasVariantOptions(variants) {
|
|
1681
|
+
if (!(variants instanceof HTMLElement)) {
|
|
1682
|
+
return false;
|
|
1683
|
+
}
|
|
1684
|
+
const data = variants.getOption?.("data") || [];
|
|
1685
|
+
const url = variants.getOption?.("url");
|
|
1686
|
+
return (
|
|
1687
|
+
(Array.isArray(data) && data.length > 0) || (isString(url) && url !== "")
|
|
1688
|
+
);
|
|
1689
|
+
}
|
|
1690
|
+
|
|
1691
|
+
/**
|
|
1692
|
+
* @private
|
|
1693
|
+
* @param {HTMLElement|null} quantityControl
|
|
1694
|
+
* @return {number|null}
|
|
1695
|
+
*/
|
|
1696
|
+
function getQuantityValue(quantityControl) {
|
|
1697
|
+
const useDecimalInput = this.getOption("features.allowDecimal") === true;
|
|
1698
|
+
if (useDecimalInput) {
|
|
1699
|
+
const raw = this[quantityInputElementSymbol]?.value ?? "";
|
|
1700
|
+
const parsed = parseLocaleNumber(raw);
|
|
1701
|
+
if (Number.isFinite(parsed)) {
|
|
1702
|
+
return parsed;
|
|
1703
|
+
}
|
|
1704
|
+
}
|
|
1705
|
+
const fallback =
|
|
1706
|
+
this.getOption("quantity.value") ?? this.getOption("quantity.min") ?? 1;
|
|
1707
|
+
if (useDecimalInput) {
|
|
1708
|
+
return Number(fallback);
|
|
1709
|
+
}
|
|
1710
|
+
const value = quantityControl?.value;
|
|
1711
|
+
if (Number.isFinite(value)) {
|
|
1712
|
+
return value;
|
|
1713
|
+
}
|
|
1714
|
+
return Number(fallback);
|
|
1715
|
+
}
|
|
1716
|
+
|
|
1717
|
+
/**
|
|
1718
|
+
* @private
|
|
1719
|
+
* @param {number} value
|
|
1720
|
+
* @param {number|null} precision
|
|
1721
|
+
* @param {string} rounding
|
|
1722
|
+
* @return {number}
|
|
1723
|
+
*/
|
|
1724
|
+
function roundQuantity(value, precision, rounding) {
|
|
1725
|
+
if (!Number.isFinite(value)) {
|
|
1726
|
+
return value;
|
|
1727
|
+
}
|
|
1728
|
+
if (!Number.isFinite(precision)) {
|
|
1729
|
+
return value;
|
|
1730
|
+
}
|
|
1731
|
+
const factor = 10 ** precision;
|
|
1732
|
+
const scaled = value * factor;
|
|
1733
|
+
if (rounding === "half-up") {
|
|
1734
|
+
return Math.round(scaled) / factor;
|
|
1735
|
+
}
|
|
1736
|
+
if (rounding === "floor") {
|
|
1737
|
+
return Math.floor(scaled) / factor;
|
|
1738
|
+
}
|
|
1739
|
+
if (rounding === "ceil") {
|
|
1740
|
+
return Math.ceil(scaled) / factor;
|
|
1741
|
+
}
|
|
1742
|
+
return Math.round(scaled) / factor;
|
|
1743
|
+
}
|
|
1744
|
+
|
|
1745
|
+
/**
|
|
1746
|
+
* @private
|
|
1747
|
+
* @param {string} value
|
|
1748
|
+
* @param {string} currency
|
|
1749
|
+
* @return {string}
|
|
1750
|
+
*/
|
|
1751
|
+
function formatMoney(value, currency) {
|
|
1752
|
+
try {
|
|
1753
|
+
const locale = getLocaleOfDocument();
|
|
1754
|
+
return new Intl.NumberFormat(locale.locale, {
|
|
1755
|
+
style: "currency",
|
|
1756
|
+
currency,
|
|
1757
|
+
}).format(value);
|
|
1758
|
+
} catch (_e) {
|
|
1759
|
+
return `${value} ${currency}`;
|
|
1760
|
+
}
|
|
1761
|
+
}
|
|
1762
|
+
|
|
1763
|
+
/**
|
|
1764
|
+
* @private
|
|
1765
|
+
* @param {number} value
|
|
1766
|
+
* @param {number} precision
|
|
1767
|
+
* @return {string}
|
|
1768
|
+
*/
|
|
1769
|
+
function formatQuantityForLocale(value, precision) {
|
|
1770
|
+
try {
|
|
1771
|
+
const locale = getLocaleOfDocument();
|
|
1772
|
+
return new Intl.NumberFormat(locale.locale, {
|
|
1773
|
+
minimumFractionDigits: precision,
|
|
1774
|
+
maximumFractionDigits: precision,
|
|
1775
|
+
}).format(value);
|
|
1776
|
+
} catch (_e) {
|
|
1777
|
+
return `${value}`;
|
|
1778
|
+
}
|
|
1779
|
+
}
|
|
1780
|
+
|
|
1781
|
+
/**
|
|
1782
|
+
* @private
|
|
1783
|
+
* @param {string} value
|
|
1784
|
+
* @return {number}
|
|
1785
|
+
*/
|
|
1786
|
+
function parseLocaleNumber(value) {
|
|
1787
|
+
if (!isString(value)) {
|
|
1788
|
+
return Number.NaN;
|
|
1789
|
+
}
|
|
1790
|
+
const trimmed = value.trim();
|
|
1791
|
+
if (trimmed === "") {
|
|
1792
|
+
return Number.NaN;
|
|
1793
|
+
}
|
|
1794
|
+
const locale = getLocaleOfDocument();
|
|
1795
|
+
const decimal = getDecimalSeparator(locale.locale);
|
|
1796
|
+
const group = decimal === "," ? "." : ",";
|
|
1797
|
+
const normalized = trimmed
|
|
1798
|
+
.replace(new RegExp(`\\${group}`, "g"), "")
|
|
1799
|
+
.replace(new RegExp(`\\${decimal}`), ".");
|
|
1800
|
+
return Number(normalized);
|
|
1801
|
+
}
|
|
1802
|
+
|
|
1803
|
+
/**
|
|
1804
|
+
* @private
|
|
1805
|
+
* @param {string} locale
|
|
1806
|
+
* @return {string}
|
|
1807
|
+
*/
|
|
1808
|
+
function getDecimalSeparator(locale) {
|
|
1809
|
+
try {
|
|
1810
|
+
const parts = new Intl.NumberFormat(locale).formatToParts(1.1);
|
|
1811
|
+
const separator = parts.find((part) => part.type === "decimal");
|
|
1812
|
+
return separator?.value || ".";
|
|
1813
|
+
} catch (_e) {
|
|
1814
|
+
return ".";
|
|
1815
|
+
}
|
|
1816
|
+
}
|
|
1817
|
+
|
|
1818
|
+
/**
|
|
1819
|
+
* @private
|
|
1820
|
+
*/
|
|
1821
|
+
function updateSubmitLabel() {
|
|
1822
|
+
if (!this[submitLabelElementSymbol]) {
|
|
1823
|
+
return;
|
|
1824
|
+
}
|
|
1825
|
+
const label = this.getOption("labels.addToCart") || "Add to cart";
|
|
1826
|
+
this[submitLabelElementSymbol].textContent = label;
|
|
1827
|
+
}
|
|
1828
|
+
|
|
1829
|
+
/**
|
|
1830
|
+
* @private
|
|
1831
|
+
* @param {HTMLElement} element
|
|
1832
|
+
* @param {string} text
|
|
1833
|
+
*/
|
|
1834
|
+
function setElementText(element, text) {
|
|
1835
|
+
element.textContent = text;
|
|
1836
|
+
element.hidden = text === "";
|
|
1837
|
+
}
|
|
1838
|
+
|
|
1839
|
+
/**
|
|
1840
|
+
* @private
|
|
1841
|
+
* @param {string|null} sku
|
|
1842
|
+
* @return {Object|null}
|
|
1843
|
+
*/
|
|
1844
|
+
function findCartItem(sku) {
|
|
1845
|
+
if (!isString(sku) || sku === "") {
|
|
1846
|
+
return null;
|
|
1847
|
+
}
|
|
1848
|
+
const cart = this[cartDataSymbol];
|
|
1849
|
+
if (!Array.isArray(cart)) {
|
|
1850
|
+
return null;
|
|
1851
|
+
}
|
|
1852
|
+
|
|
1853
|
+
const skuTemplate = this.getOption("cart.mapping.skuTemplate");
|
|
1854
|
+
const qtyTemplate = this.getOption("cart.mapping.qtyTemplate");
|
|
1855
|
+
|
|
1856
|
+
for (const item of cart) {
|
|
1857
|
+
const itemSku =
|
|
1858
|
+
mapValueFromSource(item, skuTemplate) ?? item?.sku ?? item?.id ?? null;
|
|
1859
|
+
if (itemSku === sku) {
|
|
1860
|
+
const qty = parseNumber(
|
|
1861
|
+
mapValueFromSource(item, qtyTemplate),
|
|
1862
|
+
item?.qty ?? item?.quantity ?? null,
|
|
1863
|
+
);
|
|
1864
|
+
return { sku: itemSku, qty, source: item };
|
|
1865
|
+
}
|
|
1866
|
+
}
|
|
1867
|
+
return null;
|
|
1868
|
+
}
|
|
1869
|
+
|
|
1870
|
+
/**
|
|
1871
|
+
* @private
|
|
1872
|
+
*/
|
|
1873
|
+
function checkStockIfNeeded() {
|
|
1874
|
+
const options = this.getOption("stock", {});
|
|
1875
|
+
const url = options?.url;
|
|
1876
|
+
if (!isString(url) || url === "") {
|
|
1877
|
+
return;
|
|
1878
|
+
}
|
|
1879
|
+
if (options?.features?.autoCheck === false) {
|
|
1880
|
+
return;
|
|
1881
|
+
}
|
|
1882
|
+
|
|
1883
|
+
if (this[stockRequestSymbol]) {
|
|
1884
|
+
this[stockRequestSymbol].abort?.();
|
|
1885
|
+
this[stockRequestSymbol] = null;
|
|
1886
|
+
}
|
|
1887
|
+
|
|
1888
|
+
const controller = new AbortController();
|
|
1889
|
+
this[stockRequestSymbol] = controller;
|
|
1890
|
+
const sku = this.shadowRoot.querySelector("monster-variant-select")?.value;
|
|
1891
|
+
if (!isString(sku) || sku === "") {
|
|
1892
|
+
return;
|
|
1893
|
+
}
|
|
1894
|
+
const query = sku ? `?sku=${encodeURIComponent(sku)}` : "";
|
|
1895
|
+
|
|
1896
|
+
fetch(url + query, {
|
|
1897
|
+
...options.fetch,
|
|
1898
|
+
signal: controller.signal,
|
|
1899
|
+
})
|
|
1900
|
+
.then((response) => {
|
|
1901
|
+
if (!response.ok) {
|
|
1902
|
+
throw new Error("stock check failed");
|
|
1903
|
+
}
|
|
1904
|
+
return response.json();
|
|
1905
|
+
})
|
|
1906
|
+
.then((json) => {
|
|
1907
|
+
const selector = options?.mapping?.selector || "*";
|
|
1908
|
+
let data = json;
|
|
1909
|
+
if (selector !== "*" && selector !== "") {
|
|
1910
|
+
data = new Pathfinder(json).getVia(selector);
|
|
1911
|
+
}
|
|
1912
|
+
const stock = mapValueFromSource(data, options?.mapping?.stockTemplate);
|
|
1913
|
+
const product = this[productDataSymbol]?.product || {};
|
|
1914
|
+
this[productDataSymbol] = Object.assign({}, this[productDataSymbol], {
|
|
1915
|
+
product: Object.assign({}, product, {
|
|
1916
|
+
stock: parseNumber(stock, product.stock),
|
|
1917
|
+
}),
|
|
1918
|
+
});
|
|
1919
|
+
updateState.call(this);
|
|
1920
|
+
})
|
|
1921
|
+
.catch((e) => {
|
|
1922
|
+
if (e.name === "AbortError") {
|
|
1923
|
+
return;
|
|
1924
|
+
}
|
|
1925
|
+
addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, `${e}`);
|
|
1926
|
+
});
|
|
1927
|
+
}
|
|
1928
|
+
|
|
1929
|
+
/**
|
|
1930
|
+
* @private
|
|
1931
|
+
*/
|
|
1932
|
+
function checkPricingIfNeeded() {
|
|
1933
|
+
const api = this.getOption("pricing.api", {});
|
|
1934
|
+
const url = api?.url;
|
|
1935
|
+
if (!isString(url) || url === "") {
|
|
1936
|
+
return;
|
|
1937
|
+
}
|
|
1938
|
+
if (this[pricingRequestSymbol]) {
|
|
1939
|
+
this[pricingRequestSymbol].abort?.();
|
|
1940
|
+
this[pricingRequestSymbol] = null;
|
|
1941
|
+
}
|
|
1942
|
+
const variants = this.shadowRoot.querySelector("monster-variant-select");
|
|
1943
|
+
const quantity = this.shadowRoot.querySelector("monster-quantity");
|
|
1944
|
+
const hasVariants = hasVariantOptions.call(this, variants);
|
|
1945
|
+
const sku = hasVariants ? variants?.value : this.getOption("product.sku");
|
|
1946
|
+
const qty = getQuantityValue.call(this, quantity);
|
|
1947
|
+
const selection = variants?.getOption?.("selection") || {};
|
|
1948
|
+
if (hasVariants && (!isString(sku) || sku === "")) {
|
|
1949
|
+
return;
|
|
1950
|
+
}
|
|
1951
|
+
const alwaysFetch = api?.features?.alwaysFetch === true;
|
|
1952
|
+
const cacheKey = getPricingCacheKey.call(this, sku);
|
|
1953
|
+
if (!alwaysFetch && cacheKey && this[pricingCacheSymbol]?.has(cacheKey)) {
|
|
1954
|
+
const cached = this[pricingCacheSymbol].get(cacheKey);
|
|
1955
|
+
applyPricingCache.call(this, cached);
|
|
1956
|
+
updateState.call(this);
|
|
1957
|
+
return;
|
|
1958
|
+
}
|
|
1959
|
+
const requestData = stringifyForTemplate({
|
|
1960
|
+
sku,
|
|
1961
|
+
qty: Number.isFinite(qty) ? qty : "",
|
|
1962
|
+
selection,
|
|
1963
|
+
});
|
|
1964
|
+
const requestUrl = url.includes("${")
|
|
1965
|
+
? new Formatter(requestData).format(url)
|
|
1966
|
+
: url;
|
|
1967
|
+
const controller = new AbortController();
|
|
1968
|
+
this[pricingRequestSymbol] = controller;
|
|
1969
|
+
|
|
1970
|
+
fetch(requestUrl, { ...(api?.fetch || {}), signal: controller.signal })
|
|
1971
|
+
.then((response) => {
|
|
1972
|
+
if (!response.ok) {
|
|
1973
|
+
throw new Error("pricing fetch failed");
|
|
1974
|
+
}
|
|
1975
|
+
return response.json();
|
|
1976
|
+
})
|
|
1977
|
+
.then((json) => {
|
|
1978
|
+
const selector = api?.mapping?.selector || "*";
|
|
1979
|
+
let data = json;
|
|
1980
|
+
if (selector !== "*" && selector !== "") {
|
|
1981
|
+
data = new Pathfinder(json).getVia(selector);
|
|
1982
|
+
}
|
|
1983
|
+
const price = mapValueFromSource(data, api?.mapping?.priceTemplate);
|
|
1984
|
+
const listPrice = mapValueFromSource(
|
|
1985
|
+
data,
|
|
1986
|
+
api?.mapping?.listPriceTemplate,
|
|
1987
|
+
);
|
|
1988
|
+
const basePrice = mapValueFromSource(
|
|
1989
|
+
data,
|
|
1990
|
+
api?.mapping?.basePriceTemplate,
|
|
1991
|
+
);
|
|
1992
|
+
const baseUnit = mapValueFromSource(
|
|
1993
|
+
data,
|
|
1994
|
+
api?.mapping?.basePriceUnitTemplate,
|
|
1995
|
+
);
|
|
1996
|
+
const tiersPath = api?.mapping?.tiersPath;
|
|
1997
|
+
let tiers = null;
|
|
1998
|
+
if (tiersPath) {
|
|
1999
|
+
try {
|
|
2000
|
+
tiers = new Pathfinder(data).getVia(tiersPath);
|
|
2001
|
+
} catch (_e) {
|
|
2002
|
+
tiers = null;
|
|
2003
|
+
}
|
|
2004
|
+
}
|
|
2005
|
+
const taxRate = mapValueFromSource(data, api?.mapping?.taxRateTemplate);
|
|
2006
|
+
const taxIncluded = mapValueFromSource(
|
|
2007
|
+
data,
|
|
2008
|
+
api?.mapping?.taxIncludedTemplate,
|
|
2009
|
+
);
|
|
2010
|
+
|
|
2011
|
+
const current = this[productDataSymbol]?.product || {};
|
|
2012
|
+
const pricing = Object.assign({}, this[productDataSymbol]?.pricing, {
|
|
2013
|
+
price: parseNumber(price, this.getOption("pricing.price")),
|
|
2014
|
+
listPrice: parseNumber(listPrice, this.getOption("pricing.listPrice")),
|
|
2015
|
+
basePrice: parseNumber(basePrice, this.getOption("pricing.basePrice")),
|
|
2016
|
+
basePriceUnit: baseUnit ?? this.getOption("pricing.basePriceUnit"),
|
|
2017
|
+
tiers: Array.isArray(tiers) ? tiers : this.getOption("pricing.tiers"),
|
|
2018
|
+
});
|
|
2019
|
+
|
|
2020
|
+
const product = Object.assign({}, current, {
|
|
2021
|
+
taxRate: parseNumber(taxRate, current.taxRate),
|
|
2022
|
+
taxIncluded: parseBoolean(taxIncluded, current.taxIncluded),
|
|
2023
|
+
});
|
|
2024
|
+
|
|
2025
|
+
const cacheEntry = { product, pricing };
|
|
2026
|
+
if (cacheKey) {
|
|
2027
|
+
this[pricingCacheSymbol]?.set(cacheKey, cacheEntry);
|
|
2028
|
+
}
|
|
2029
|
+
applyPricingCache.call(this, cacheEntry);
|
|
2030
|
+
updateState.call(this);
|
|
2031
|
+
})
|
|
2032
|
+
.catch((e) => {
|
|
2033
|
+
if (e.name === "AbortError") {
|
|
2034
|
+
return;
|
|
2035
|
+
}
|
|
2036
|
+
addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, `${e}`);
|
|
2037
|
+
});
|
|
2038
|
+
}
|
|
2039
|
+
|
|
2040
|
+
/**
|
|
2041
|
+
* @private
|
|
2042
|
+
* @param {string|null} value
|
|
2043
|
+
* @param {boolean} fallback
|
|
2044
|
+
* @return {boolean}
|
|
2045
|
+
*/
|
|
2046
|
+
function parseBoolean(value, fallback) {
|
|
2047
|
+
if (value === null || value === undefined) {
|
|
2048
|
+
return fallback;
|
|
2049
|
+
}
|
|
2050
|
+
if (typeof value === "boolean") {
|
|
2051
|
+
return value;
|
|
2052
|
+
}
|
|
2053
|
+
if (isString(value)) {
|
|
2054
|
+
return value.toLowerCase() === "true";
|
|
2055
|
+
}
|
|
2056
|
+
return fallback;
|
|
2057
|
+
}
|
|
2058
|
+
|
|
2059
|
+
/**
|
|
2060
|
+
* @private
|
|
2061
|
+
* @param {string|null} value
|
|
2062
|
+
* @param {number|null} fallback
|
|
2063
|
+
* @return {number|null}
|
|
2064
|
+
*/
|
|
2065
|
+
function parseNumber(value, fallback) {
|
|
2066
|
+
if (value === null || value === undefined) {
|
|
2067
|
+
return fallback;
|
|
2068
|
+
}
|
|
2069
|
+
const num = Number(value);
|
|
2070
|
+
return Number.isFinite(num) ? num : fallback;
|
|
2071
|
+
}
|
|
2072
|
+
|
|
2073
|
+
/**
|
|
2074
|
+
* @private
|
|
2075
|
+
* @return {string}
|
|
2076
|
+
*/
|
|
2077
|
+
function getTemplate() {
|
|
2078
|
+
// language=HTML
|
|
2079
|
+
return `
|
|
2080
|
+
<div data-monster-role="control" part="control">
|
|
2081
|
+
<div data-monster-role="header" part="header">
|
|
2082
|
+
<slot name="title"></slot>
|
|
2083
|
+
</div>
|
|
2084
|
+
<div data-monster-role="body" part="body">
|
|
2085
|
+
<div data-monster-role="variants" part="variants">
|
|
2086
|
+
<monster-variant-select part="variants-control"></monster-variant-select>
|
|
2087
|
+
</div>
|
|
2088
|
+
<div data-monster-role="quantity" part="quantity">
|
|
2089
|
+
<div data-monster-role="quantity-label" part="quantity-label">
|
|
2090
|
+
<span data-monster-role="quantity-label-text" part="quantity-label-text"
|
|
2091
|
+
data-monster-replace="path:labels.quantity"></span>
|
|
2092
|
+
<span data-monster-role="quantity-unit" part="quantity-unit"></span>
|
|
2093
|
+
</div>
|
|
2094
|
+
<monster-quantity part="quantity-control"></monster-quantity>
|
|
2095
|
+
<input data-monster-role="quantity-input" part="quantity-input"
|
|
2096
|
+
type="text" inputmode="decimal" />
|
|
2097
|
+
<div data-monster-role="quantity-static" part="quantity-static"></div>
|
|
2098
|
+
</div>
|
|
2099
|
+
<div data-monster-role="prices" part="prices">
|
|
2100
|
+
<div data-monster-role="list-price" part="list-price"></div>
|
|
2101
|
+
<div data-monster-role="price" part="price"></div>
|
|
2102
|
+
<div data-monster-role="sum" part="sum"></div>
|
|
2103
|
+
<div data-monster-role="base-price" part="base-price"></div>
|
|
2104
|
+
<div data-monster-role="tax" part="tax"></div>
|
|
2105
|
+
<div data-monster-role="total-label" part="total-label"
|
|
2106
|
+
data-monster-replace="path:labels.total"></div>
|
|
2107
|
+
<div data-monster-role="total" part="total"></div>
|
|
2108
|
+
</div>
|
|
2109
|
+
<div data-monster-role="delivery" part="delivery"></div>
|
|
2110
|
+
<div data-monster-role="message" part="message"></div>
|
|
2111
|
+
<div data-monster-role="actions" part="actions">
|
|
2112
|
+
<monster-message-state-button data-monster-role="submit" part="submit">
|
|
2113
|
+
<span data-monster-role="submit-label" part="submit-label"></span>
|
|
2114
|
+
</monster-message-state-button>
|
|
2115
|
+
</div>
|
|
2116
|
+
</div>
|
|
2117
|
+
<div data-monster-role="slots" part="slots">
|
|
2118
|
+
<slot name="info"></slot>
|
|
2119
|
+
<slot name="shipping"></slot>
|
|
2120
|
+
<slot name="footer"></slot>
|
|
2121
|
+
</div>
|
|
2122
|
+
</div>
|
|
2123
|
+
`;
|
|
2124
|
+
}
|
|
2125
|
+
|
|
2126
|
+
/**
|
|
2127
|
+
* @private
|
|
2128
|
+
* @returns {object}
|
|
2129
|
+
*/
|
|
2130
|
+
function getTranslations() {
|
|
2131
|
+
const locale = getLocaleOfDocument();
|
|
2132
|
+
switch (locale.language) {
|
|
2133
|
+
case "de":
|
|
2134
|
+
return {
|
|
2135
|
+
addToCart: "In den Warenkorb",
|
|
2136
|
+
addToCartAria: "In den Warenkorb",
|
|
2137
|
+
quantity: "Menge",
|
|
2138
|
+
total: "Summe",
|
|
2139
|
+
variantsAria: "Varianten",
|
|
2140
|
+
quantityAria: "Menge",
|
|
2141
|
+
selectVariant: "Bitte eine Variante waehlen.",
|
|
2142
|
+
selectQuantity: "Bitte Menge waehlen.",
|
|
2143
|
+
priceUnavailable: "Preis nicht verfuegbar.",
|
|
2144
|
+
invalidQuantity: "Menge ist nicht erlaubt.",
|
|
2145
|
+
outOfStock: "Nicht genuegend Lagerbestand.",
|
|
2146
|
+
addToCartError: "Hinzufuegen fehlgeschlagen.",
|
|
2147
|
+
addToCartNotConfigured: "Warenkorb ist nicht konfiguriert.",
|
|
2148
|
+
taxIncluded: "inkl. MwSt.",
|
|
2149
|
+
taxExcluded: "zzgl. MwSt.",
|
|
2150
|
+
deliveryUnknown: "Lieferzeit unbekannt",
|
|
2151
|
+
};
|
|
2152
|
+
case "es":
|
|
2153
|
+
return {
|
|
2154
|
+
addToCart: "Anadir al carrito",
|
|
2155
|
+
addToCartAria: "Anadir al carrito",
|
|
2156
|
+
quantity: "Cantidad",
|
|
2157
|
+
total: "Total",
|
|
2158
|
+
variantsAria: "Variantes",
|
|
2159
|
+
quantityAria: "Cantidad",
|
|
2160
|
+
selectVariant: "Seleccione una variante.",
|
|
2161
|
+
selectQuantity: "Seleccione una cantidad.",
|
|
2162
|
+
priceUnavailable: "Precio no disponible.",
|
|
2163
|
+
invalidQuantity: "Cantidad no permitida.",
|
|
2164
|
+
outOfStock: "Stock insuficiente.",
|
|
2165
|
+
addToCartError: "Error al anadir.",
|
|
2166
|
+
addToCartNotConfigured: "Carrito no configurado.",
|
|
2167
|
+
taxIncluded: "incl. impuestos",
|
|
2168
|
+
taxExcluded: "excl. impuestos",
|
|
2169
|
+
deliveryUnknown: "Plazo desconocido",
|
|
2170
|
+
};
|
|
2171
|
+
case "zh":
|
|
2172
|
+
return {
|
|
2173
|
+
addToCart: "加入购物车",
|
|
2174
|
+
addToCartAria: "加入购物车",
|
|
2175
|
+
quantity: "数量",
|
|
2176
|
+
total: "总计",
|
|
2177
|
+
variantsAria: "变体选择",
|
|
2178
|
+
quantityAria: "数量",
|
|
2179
|
+
selectVariant: "请选择一个变体。",
|
|
2180
|
+
selectQuantity: "请选择数量。",
|
|
2181
|
+
priceUnavailable: "价格不可用。",
|
|
2182
|
+
invalidQuantity: "数量无效。",
|
|
2183
|
+
outOfStock: "库存不足。",
|
|
2184
|
+
addToCartError: "添加失败。",
|
|
2185
|
+
addToCartNotConfigured: "购物车未配置。",
|
|
2186
|
+
taxIncluded: "含税",
|
|
2187
|
+
taxExcluded: "不含税",
|
|
2188
|
+
deliveryUnknown: "交付时间未知",
|
|
2189
|
+
};
|
|
2190
|
+
case "hi":
|
|
2191
|
+
return {
|
|
2192
|
+
addToCart: "कार्ट में जोड़ें",
|
|
2193
|
+
addToCartAria: "कार्ट में जोड़ें",
|
|
2194
|
+
quantity: "मात्रा",
|
|
2195
|
+
total: "कुल",
|
|
2196
|
+
variantsAria: "विकल्प",
|
|
2197
|
+
quantityAria: "मात्रा",
|
|
2198
|
+
selectVariant: "कृपया एक विकल्प चुनें।",
|
|
2199
|
+
selectQuantity: "कृपया मात्रा चुनें।",
|
|
2200
|
+
priceUnavailable: "मूल्य उपलब्ध नहीं।",
|
|
2201
|
+
invalidQuantity: "मात्रा अनुमत नहीं है।",
|
|
2202
|
+
outOfStock: "स्टॉक पर्याप्त नहीं है।",
|
|
2203
|
+
addToCartError: "जोड़ने में त्रुटि।",
|
|
2204
|
+
addToCartNotConfigured: "कार्ट कॉन्फ़िगर नहीं है।",
|
|
2205
|
+
taxIncluded: "कर सहित",
|
|
2206
|
+
taxExcluded: "कर रहित",
|
|
2207
|
+
deliveryUnknown: "डिलीवरी समय अज्ञात",
|
|
2208
|
+
};
|
|
2209
|
+
case "bn":
|
|
2210
|
+
return {
|
|
2211
|
+
addToCart: "কার্টে যোগ করুন",
|
|
2212
|
+
addToCartAria: "কার্টে যোগ করুন",
|
|
2213
|
+
quantity: "পরিমাণ",
|
|
2214
|
+
total: "মোট",
|
|
2215
|
+
variantsAria: "ভ্যারিয়েন্ট",
|
|
2216
|
+
quantityAria: "পরিমাণ",
|
|
2217
|
+
selectVariant: "দয়া করে একটি ভ্যারিয়েন্ট বেছে নিন।",
|
|
2218
|
+
selectQuantity: "দয়া করে পরিমাণ বেছে নিন।",
|
|
2219
|
+
priceUnavailable: "মূল্য পাওয়া যাচ্ছে না।",
|
|
2220
|
+
invalidQuantity: "পরিমাণ অনুমোদিত নয়।",
|
|
2221
|
+
outOfStock: "স্টক পর্যাপ্ত নয়।",
|
|
2222
|
+
addToCartError: "যোগ করতে ব্যর্থ।",
|
|
2223
|
+
addToCartNotConfigured: "কার্ট কনফিগার করা হয়নি।",
|
|
2224
|
+
taxIncluded: "করসহ",
|
|
2225
|
+
taxExcluded: "কর ছাড়া",
|
|
2226
|
+
deliveryUnknown: "ডেলিভারি সময় অজানা",
|
|
2227
|
+
};
|
|
2228
|
+
case "pt":
|
|
2229
|
+
return {
|
|
2230
|
+
addToCart: "Adicionar ao carrinho",
|
|
2231
|
+
addToCartAria: "Adicionar ao carrinho",
|
|
2232
|
+
quantity: "Quantidade",
|
|
2233
|
+
total: "Total",
|
|
2234
|
+
variantsAria: "Variantes",
|
|
2235
|
+
quantityAria: "Quantidade",
|
|
2236
|
+
selectVariant: "Escolha uma variante.",
|
|
2237
|
+
selectQuantity: "Escolha uma quantidade.",
|
|
2238
|
+
priceUnavailable: "Preco indisponivel.",
|
|
2239
|
+
invalidQuantity: "Quantidade nao permitida.",
|
|
2240
|
+
outOfStock: "Estoque insuficiente.",
|
|
2241
|
+
addToCartError: "Falha ao adicionar.",
|
|
2242
|
+
addToCartNotConfigured: "Carrinho nao configurado.",
|
|
2243
|
+
taxIncluded: "incl. imposto",
|
|
2244
|
+
taxExcluded: "excl. imposto",
|
|
2245
|
+
deliveryUnknown: "Prazo desconhecido",
|
|
2246
|
+
};
|
|
2247
|
+
case "ru":
|
|
2248
|
+
return {
|
|
2249
|
+
addToCart: "V korzinu",
|
|
2250
|
+
addToCartAria: "V korzinu",
|
|
2251
|
+
quantity: "Kolichestvo",
|
|
2252
|
+
total: "Itogo",
|
|
2253
|
+
variantsAria: "Varianty",
|
|
2254
|
+
quantityAria: "Kolichestvo",
|
|
2255
|
+
selectVariant: "Vyberite variant.",
|
|
2256
|
+
selectQuantity: "Vyberite kolichestvo.",
|
|
2257
|
+
priceUnavailable: "Tsena nedostupna.",
|
|
2258
|
+
invalidQuantity: "Nedopustimoe kol-vo.",
|
|
2259
|
+
outOfStock: "Nedostatochno na sklade.",
|
|
2260
|
+
addToCartError: "Ne udalos dobavit.",
|
|
2261
|
+
addToCartNotConfigured: "Korzina ne nastroena.",
|
|
2262
|
+
taxIncluded: "s NDS",
|
|
2263
|
+
taxExcluded: "bez NDS",
|
|
2264
|
+
deliveryUnknown: "Srok neizvesten",
|
|
2265
|
+
};
|
|
2266
|
+
case "ja":
|
|
2267
|
+
return {
|
|
2268
|
+
addToCart: "カートに追加",
|
|
2269
|
+
addToCartAria: "カートに追加",
|
|
2270
|
+
quantity: "数量",
|
|
2271
|
+
total: "合計",
|
|
2272
|
+
variantsAria: "バリエーション",
|
|
2273
|
+
quantityAria: "数量",
|
|
2274
|
+
selectVariant: "バリエーションを選択してください。",
|
|
2275
|
+
selectQuantity: "数量を選択してください。",
|
|
2276
|
+
priceUnavailable: "価格が利用できません。",
|
|
2277
|
+
invalidQuantity: "数量が無効です。",
|
|
2278
|
+
outOfStock: "在庫不足。",
|
|
2279
|
+
addToCartError: "追加に失敗。",
|
|
2280
|
+
addToCartNotConfigured: "カートが未設定です。",
|
|
2281
|
+
taxIncluded: "税込",
|
|
2282
|
+
taxExcluded: "税別",
|
|
2283
|
+
deliveryUnknown: "配送情報なし",
|
|
2284
|
+
};
|
|
2285
|
+
case "pa":
|
|
2286
|
+
return {
|
|
2287
|
+
addToCart: "ਕਾਰਟ ਵਿੱਚ ਸ਼ਾਮਲ ਕਰੋ",
|
|
2288
|
+
addToCartAria: "ਕਾਰਟ ਵਿੱਚ ਸ਼ਾਮਲ ਕਰੋ",
|
|
2289
|
+
quantity: "ਮਾਤਰਾ",
|
|
2290
|
+
total: "ਕੁੱਲ",
|
|
2291
|
+
variantsAria: "ਵੈਰੀਐਂਟ",
|
|
2292
|
+
quantityAria: "ਮਾਤਰਾ",
|
|
2293
|
+
selectVariant: "ਕਿਰਪਾ ਕਰਕੇ ਇਕ ਵੈਰੀਐਂਟ ਚੁਣੋ।",
|
|
2294
|
+
selectQuantity: "ਕਿਰਪਾ ਕਰਕੇ ਮਾਤਰਾ ਚੁਣੋ।",
|
|
2295
|
+
priceUnavailable: "ਕੀਮਤ ਉਪਲਬਧ ਨਹੀਂ।",
|
|
2296
|
+
invalidQuantity: "ਮਾਤਰਾ ਮਨਜ਼ੂਰ ਨਹੀਂ।",
|
|
2297
|
+
outOfStock: "ਸਟਾਕ ਘੱਟ ਹੈ।",
|
|
2298
|
+
addToCartError: "ਜੋੜਨਾ ਅਸਫਲ।",
|
|
2299
|
+
addToCartNotConfigured: "ਕਾਰਟ ਕਨਫਿਗਰ ਨਹੀਂ ਹੈ।",
|
|
2300
|
+
taxIncluded: "ਟੈਕਸ ਸਮੇਤ",
|
|
2301
|
+
taxExcluded: "ਟੈਕਸ ਤੋਂ ਬਿਨਾਂ",
|
|
2302
|
+
deliveryUnknown: "ਡਿਲਿਵਰੀ ਸਮਾਂ ਅਣਜਾਣ",
|
|
2303
|
+
};
|
|
2304
|
+
case "mr":
|
|
2305
|
+
return {
|
|
2306
|
+
addToCart: "कार्टमध्ये जोडा",
|
|
2307
|
+
addToCartAria: "कार्टमध्ये जोडा",
|
|
2308
|
+
quantity: "प्रमाण",
|
|
2309
|
+
total: "एकूण",
|
|
2310
|
+
variantsAria: "पर्याय",
|
|
2311
|
+
quantityAria: "प्रमाण",
|
|
2312
|
+
selectVariant: "कृपया एक पर्याय निवडा.",
|
|
2313
|
+
selectQuantity: "कृपया प्रमाण निवडा.",
|
|
2314
|
+
priceUnavailable: "किंमत उपलब्ध नाही.",
|
|
2315
|
+
invalidQuantity: "प्रमाण अनुमती नाही.",
|
|
2316
|
+
outOfStock: "साठा अपुरा आहे.",
|
|
2317
|
+
addToCartError: "जोडण्यात त्रुटी.",
|
|
2318
|
+
addToCartNotConfigured: "कार्ट संरचित नाही.",
|
|
2319
|
+
taxIncluded: "करासह",
|
|
2320
|
+
taxExcluded: "कराशिवाय",
|
|
2321
|
+
deliveryUnknown: "डिलिव्हरी वेळ अज्ञात",
|
|
2322
|
+
};
|
|
2323
|
+
case "fr":
|
|
2324
|
+
return {
|
|
2325
|
+
addToCart: "Ajouter au panier",
|
|
2326
|
+
addToCartAria: "Ajouter au panier",
|
|
2327
|
+
quantity: "Quantite",
|
|
2328
|
+
total: "Total",
|
|
2329
|
+
variantsAria: "Variantes",
|
|
2330
|
+
quantityAria: "Quantite",
|
|
2331
|
+
selectVariant: "Veuillez choisir une variante.",
|
|
2332
|
+
selectQuantity: "Veuillez choisir une quantite.",
|
|
2333
|
+
priceUnavailable: "Prix indisponible.",
|
|
2334
|
+
invalidQuantity: "Quantite non autorisee.",
|
|
2335
|
+
outOfStock: "Stock insuffisant.",
|
|
2336
|
+
addToCartError: "Ajout echoue.",
|
|
2337
|
+
addToCartNotConfigured: "Panier non configure.",
|
|
2338
|
+
taxIncluded: "TTC",
|
|
2339
|
+
taxExcluded: "HT",
|
|
2340
|
+
deliveryUnknown: "Delai inconnu",
|
|
2341
|
+
};
|
|
2342
|
+
case "it":
|
|
2343
|
+
return {
|
|
2344
|
+
addToCart: "Aggiungi al carrello",
|
|
2345
|
+
addToCartAria: "Aggiungi al carrello",
|
|
2346
|
+
quantity: "Quantita",
|
|
2347
|
+
total: "Totale",
|
|
2348
|
+
variantsAria: "Varianti",
|
|
2349
|
+
quantityAria: "Quantita",
|
|
2350
|
+
selectVariant: "Seleziona una variante.",
|
|
2351
|
+
selectQuantity: "Seleziona una quantita.",
|
|
2352
|
+
priceUnavailable: "Prezzo non disponibile.",
|
|
2353
|
+
invalidQuantity: "Quantita non consentita.",
|
|
2354
|
+
outOfStock: "Scorte insufficienti.",
|
|
2355
|
+
addToCartError: "Aggiunta fallita.",
|
|
2356
|
+
addToCartNotConfigured: "Carrello non configurato.",
|
|
2357
|
+
taxIncluded: "IVA inclusa",
|
|
2358
|
+
taxExcluded: "IVA esclusa",
|
|
2359
|
+
deliveryUnknown: "Tempi sconosciuti",
|
|
2360
|
+
};
|
|
2361
|
+
case "nl":
|
|
2362
|
+
return {
|
|
2363
|
+
addToCart: "In winkelwagen",
|
|
2364
|
+
addToCartAria: "In winkelwagen",
|
|
2365
|
+
quantity: "Aantal",
|
|
2366
|
+
total: "Totaal",
|
|
2367
|
+
variantsAria: "Varianten",
|
|
2368
|
+
quantityAria: "Aantal",
|
|
2369
|
+
selectVariant: "Kies een variant.",
|
|
2370
|
+
selectQuantity: "Kies een hoeveelheid.",
|
|
2371
|
+
priceUnavailable: "Prijs niet beschikbaar.",
|
|
2372
|
+
invalidQuantity: "Hoeveelheid niet toegestaan.",
|
|
2373
|
+
outOfStock: "Onvoldoende voorraad.",
|
|
2374
|
+
addToCartError: "Toevoegen mislukt.",
|
|
2375
|
+
addToCartNotConfigured: "Winkelwagen niet geconfigureerd.",
|
|
2376
|
+
taxIncluded: "incl. btw",
|
|
2377
|
+
taxExcluded: "excl. btw",
|
|
2378
|
+
deliveryUnknown: "Levertijd onbekend",
|
|
2379
|
+
};
|
|
2380
|
+
case "sv":
|
|
2381
|
+
return {
|
|
2382
|
+
addToCart: "Lag i kundvagn",
|
|
2383
|
+
addToCartAria: "Lag i kundvagn",
|
|
2384
|
+
quantity: "Antal",
|
|
2385
|
+
total: "Summa",
|
|
2386
|
+
variantsAria: "Varianter",
|
|
2387
|
+
quantityAria: "Antal",
|
|
2388
|
+
selectVariant: "Valj en variant.",
|
|
2389
|
+
selectQuantity: "Valj antal.",
|
|
2390
|
+
priceUnavailable: "Pris ej tillgangligt.",
|
|
2391
|
+
invalidQuantity: "Antal ej tillatet.",
|
|
2392
|
+
outOfStock: "Otillrackligt lager.",
|
|
2393
|
+
addToCartError: "Kunde inte lagga till.",
|
|
2394
|
+
addToCartNotConfigured: "Kundvagn ej konfigurerad.",
|
|
2395
|
+
taxIncluded: "inkl. moms",
|
|
2396
|
+
taxExcluded: "exkl. moms",
|
|
2397
|
+
deliveryUnknown: "Leverans okand",
|
|
2398
|
+
};
|
|
2399
|
+
case "pl":
|
|
2400
|
+
return {
|
|
2401
|
+
addToCart: "Dodaj do koszyka",
|
|
2402
|
+
addToCartAria: "Dodaj do koszyka",
|
|
2403
|
+
quantity: "Ilosc",
|
|
2404
|
+
total: "Suma",
|
|
2405
|
+
variantsAria: "Warianty",
|
|
2406
|
+
quantityAria: "Ilosc",
|
|
2407
|
+
selectVariant: "Wybierz wariant.",
|
|
2408
|
+
selectQuantity: "Wybierz ilosc.",
|
|
2409
|
+
priceUnavailable: "Cena niedostepna.",
|
|
2410
|
+
invalidQuantity: "Niepoprawna ilosc.",
|
|
2411
|
+
outOfStock: "Brak na stanie.",
|
|
2412
|
+
addToCartError: "Nie udalo sie dodac.",
|
|
2413
|
+
addToCartNotConfigured: "Koszyk nie skonfigurowany.",
|
|
2414
|
+
taxIncluded: "z VAT",
|
|
2415
|
+
taxExcluded: "bez VAT",
|
|
2416
|
+
deliveryUnknown: "Czas dostawy nieznany",
|
|
2417
|
+
};
|
|
2418
|
+
case "da":
|
|
2419
|
+
return {
|
|
2420
|
+
addToCart: "Laeg i kurv",
|
|
2421
|
+
addToCartAria: "Laeg i kurv",
|
|
2422
|
+
quantity: "Maengde",
|
|
2423
|
+
total: "Total",
|
|
2424
|
+
variantsAria: "Varianter",
|
|
2425
|
+
quantityAria: "Maengde",
|
|
2426
|
+
selectVariant: "Vaelg en variant.",
|
|
2427
|
+
selectQuantity: "Vaelg maengde.",
|
|
2428
|
+
priceUnavailable: "Pris ikke tilgaengelig.",
|
|
2429
|
+
invalidQuantity: "Maengde ikke tilladt.",
|
|
2430
|
+
outOfStock: "Ikke nok lager.",
|
|
2431
|
+
addToCartError: "Kunne ikke tilfoeje.",
|
|
2432
|
+
addToCartNotConfigured: "Kurv ikke konfigureret.",
|
|
2433
|
+
taxIncluded: "inkl. moms",
|
|
2434
|
+
taxExcluded: "ekskl. moms",
|
|
2435
|
+
deliveryUnknown: "Leveringstid ukendt",
|
|
2436
|
+
};
|
|
2437
|
+
case "fi":
|
|
2438
|
+
return {
|
|
2439
|
+
addToCart: "Lisaa koriin",
|
|
2440
|
+
addToCartAria: "Lisaa koriin",
|
|
2441
|
+
quantity: "Maara",
|
|
2442
|
+
total: "Yhteensa",
|
|
2443
|
+
variantsAria: "Vaihtoehdot",
|
|
2444
|
+
quantityAria: "Maara",
|
|
2445
|
+
selectVariant: "Valitse vaihtoehto.",
|
|
2446
|
+
selectQuantity: "Valitse maara.",
|
|
2447
|
+
priceUnavailable: "Hinta ei saatavilla.",
|
|
2448
|
+
invalidQuantity: "Maara ei sallittu.",
|
|
2449
|
+
outOfStock: "Varasto ei riita.",
|
|
2450
|
+
addToCartError: "Lisaaminen epaonnistui.",
|
|
2451
|
+
addToCartNotConfigured: "Ostoskori ei ole konfiguroitu.",
|
|
2452
|
+
taxIncluded: "sis. alv",
|
|
2453
|
+
taxExcluded: "ei sis. alv",
|
|
2454
|
+
deliveryUnknown: "Toimitusaika tuntematon",
|
|
2455
|
+
};
|
|
2456
|
+
case "no":
|
|
2457
|
+
return {
|
|
2458
|
+
addToCart: "Legg i handlekurv",
|
|
2459
|
+
addToCartAria: "Legg i handlekurv",
|
|
2460
|
+
quantity: "Antall",
|
|
2461
|
+
total: "Sum",
|
|
2462
|
+
variantsAria: "Varianter",
|
|
2463
|
+
quantityAria: "Antall",
|
|
2464
|
+
selectVariant: "Velg en variant.",
|
|
2465
|
+
selectQuantity: "Velg antall.",
|
|
2466
|
+
priceUnavailable: "Pris ikke tilgjengelig.",
|
|
2467
|
+
invalidQuantity: "Mengde ikke tillatt.",
|
|
2468
|
+
outOfStock: "Ikke nok lager.",
|
|
2469
|
+
addToCartError: "Kunne ikke legge til.",
|
|
2470
|
+
addToCartNotConfigured: "Handlekurv ikke konfigurert.",
|
|
2471
|
+
taxIncluded: "inkl. mva",
|
|
2472
|
+
taxExcluded: "ekskl. mva",
|
|
2473
|
+
deliveryUnknown: "Leveringstid ukjent",
|
|
2474
|
+
};
|
|
2475
|
+
case "cs":
|
|
2476
|
+
return {
|
|
2477
|
+
addToCart: "Pridat do kosiku",
|
|
2478
|
+
addToCartAria: "Pridat do kosiku",
|
|
2479
|
+
quantity: "Mnozstvi",
|
|
2480
|
+
total: "Celkem",
|
|
2481
|
+
variantsAria: "Varianty",
|
|
2482
|
+
quantityAria: "Mnozstvi",
|
|
2483
|
+
selectVariant: "Vyberte variantu.",
|
|
2484
|
+
selectQuantity: "Vyberte mnozstvi.",
|
|
2485
|
+
priceUnavailable: "Cena neni dostupna.",
|
|
2486
|
+
invalidQuantity: "Neplatne mnozstvi.",
|
|
2487
|
+
outOfStock: "Nedostatek na sklade.",
|
|
2488
|
+
addToCartError: "Nelze pridat.",
|
|
2489
|
+
addToCartNotConfigured: "Kosik neni nakonfigurovan.",
|
|
2490
|
+
taxIncluded: "vc. DPH",
|
|
2491
|
+
taxExcluded: "bez DPH",
|
|
2492
|
+
deliveryUnknown: "Dodaci lhuta neznama",
|
|
2493
|
+
};
|
|
2494
|
+
default:
|
|
2495
|
+
return {
|
|
2496
|
+
addToCart: "Add to cart",
|
|
2497
|
+
addToCartAria: "Add to cart",
|
|
2498
|
+
quantity: "Quantity",
|
|
2499
|
+
total: "Total",
|
|
2500
|
+
variantsAria: "Variants",
|
|
2501
|
+
quantityAria: "Quantity",
|
|
2502
|
+
selectVariant: "Please select a variant.",
|
|
2503
|
+
selectQuantity: "Please choose a quantity.",
|
|
2504
|
+
priceUnavailable: "Price unavailable.",
|
|
2505
|
+
invalidQuantity: "Quantity is not allowed.",
|
|
2506
|
+
outOfStock: "Not enough stock.",
|
|
2507
|
+
addToCartError: "Failed to add to cart.",
|
|
2508
|
+
addToCartNotConfigured: "Cart is not configured.",
|
|
2509
|
+
taxIncluded: "incl. tax",
|
|
2510
|
+
taxExcluded: "excl. tax",
|
|
2511
|
+
deliveryUnknown: "Delivery unknown",
|
|
2512
|
+
};
|
|
2513
|
+
}
|
|
2514
|
+
}
|
|
2515
|
+
|
|
2516
|
+
registerCustomElement(BuyBox);
|