PageSourceSearch

https://crochtamaille.fr/wp-content/plugins/wpc-product-options/assets/js/frontend.js

js crochtamaille.fr collected 2026-10-06 05:34:33 UTC 23,976 bytes, 624 lines download raw bytes

1'use strict';
2
3(function ($) {
4    $(function () {
5        // ready
6        wpcpo_init();
7
8        // tooltips
9        if (wpcpo_vars.tooltip_library === 'tippy') {
10            tippy('.wpcpo-tippy-tooltip', {
11                allowHTML: true, interactive: true,
12            });
13        }
14    });
15
16    $(document).on('woosq_loaded', function () {
17        // quick view
18        wpcpo_init();
19    });
20
21    $(document).on('found_variation', function (e, t) {
22        if ($(e['target']).closest('.woosb-product').length || $(e['target']).closest('.wooco-product').length || $(e['target']).closest('.woobt-product').length || $(e['target']).closest('.woofs-product').length || $(e['target']).closest('.woosg-product').length) {
23            return;
24        }
25
26        let $total = $(e['target']).closest('form.cart').find('.wpcpo-total');
27
28        $total.attr('data-price', t['display_price']);
29
30        wpcpo_update_total($total);
31    });
32
33    $(document).on('reset_data', function (e) {
34        if ($(e['target']).closest('.woosb-product').length || $(e['target']).closest('.wooco-product').length || $(e['target']).closest('.woobt-product').length || $(e['target']).closest('.woofs-product').length || $(e['target']).closest('.woosg-product').length) {
35            return;
36        }
37
38        let $total = $(e['target']).closest('form.cart').find('.wpcpo-total');
39
40        $total.attr('data-price', $total.attr('data-o_price'));
41
42        wpcpo_update_total($total);
43    });
44
45    $(document).on('woovr_selected', function (e, selected, variations) {
46        if (variations.closest('.woosb-product').length || variations.closest('.wooco-product').length || variations.closest('.woobt-product').length || variations.closest('.woofs-product').length || variations.closest('.woosg-product').length) {
47            return;
48        }
49
50        let purchasable = selected.attr('data-purchasable'),
51            $total = variations.closest('form.cart').find('.wpcpo-total');
52
53        if (purchasable === 'yes') {
54            $total.attr('data-price', selected.attr('data-price'));
55        } else {
56            $total.attr('data-price', $total.attr('data-o_price'));
57        }
58
59        wpcpo_update_total($total);
60    });
61
62    /* WPC Product Bundles */
63    $(document).on('woosb_calc_price', function (e, total_sale, total, total_html, price_suffix, $wrap) {
64        let wid = $wrap.attr('data-id'), $total = $('.wpcpo-total[data-product-id="' + wid + '"]');
65
66        $total.attr('data-price', total_sale);
67
68        wpcpo_update_total($total);
69    });
70
71    /* WPC Composite Products */
72    $(document).on('wooco_calc_price', function (e, total, total_regular, total_html, $wrap) {
73        let wid = $wrap.attr('data-id'), $total = $('.wpcpo-total[data-product-id="' + wid + '"]');
74
75        $total.attr('data-price', total);
76
77        wpcpo_update_total($total);
78    });
79
80    $(document).on('change keyup', 'form.cart [name=quantity]', function () {
81        let $this = $(this);
82
83        if ($this.closest('.woosb-product').length || $this.closest('.wooco-product').length || $this.closest('.woobt-product').length || $this.closest('.woofs-product').length || $this.closest('.woosg-product').length) {
84            return;
85        }
86
87        wpcpo_update_total($this.closest('form.cart').find('.wpcpo-total'));
88    });
89
90    $(document).on('change input', '.wpcpo-option-field', function () {
91        let $this = $(this), $options = $this.closest('.wpcpo-options');
92
93        if ($this.hasClass('field-select')) {
94            let $wrapper = $this.closest('.form-row'), $option = $this.find(':selected'), label = $option.data('label'),
95                type = $option.data('price-type'), price = $option.data('price'),
96                custom_price = $option.data('price-custom');
97            $wrapper.find('input[name$="label\]"]').attr('value', label);
98            $wrapper.find('input[name$="price_type\]"]').attr('value', type);
99            $wrapper.find('input[name$="price\]"]').attr('value', price);
100            $wrapper.find('input[name$="custom_price\]"]').attr('value', custom_price);
101        } else if ($this.hasClass('field-radio')) {
102            let $wrapper = $this.closest('.form-row'), $option = $wrapper.find(':checked'),
103                label = $option.data('label'), type = $option.data('price-type'),
104                custom_price = $option.data('price-custom'), price = $option.data('price'),
105                image = $option.data('image');
106            $wrapper.find('input[name$="label\]"]').attr('value', label);
107            $wrapper.find('input[name$="image\]"]').attr('value', image);
108            $wrapper.find('input[name$="price_type\]"]').attr('value', t
108ype);
109            $wrapper.find('input[name$="price\]"]').attr('value', price);
110            $wrapper.find('input[name$="custom_price\]"]').attr('value', custom_price);
111        } else if ($this.hasClass('field-dimensions')) {
112            let $dimensions = $this.closest('.wpcpo-option-dimensions'), dimensions = [];
113
114            $dimensions.find('.wpcpo-dimension').each(function () {
115                if ($(this).find('input').val() !== '') {
116                    let dimension = $(this).find('label').text() + ': ' + $(this).find('input').val();
117
118                    dimensions.push(dimension);
119                }
120            });
121
122            if (dimensions.length) {
123                $this.val(dimensions.join('; '));
124            } else {
125                $this.val('');
126            }
127        } else if ($this.hasClass('field-uploads')) {
128            // Show selected file name on mobile/desktop
129            let $fileName = $this.closest('.wpcpo-option').find('.wpcpo-file-name');
130            if ($fileName.length) {
131                if (this.files && this.files.length > 0) {
132                    $fileName.text(this.files[0].name);
133                } else {
134                    $fileName.text('');
135                }
136            }
137        }
138
139        wpcpo_validate($options);
140        wpcpo_update_total($this.closest('form.cart').find('.wpcpo-total'));
141    });
142
143    $(document).on('blur', '.wpcpo-option-field', function () {
144        let $this = $(this), $options = $this.closest('.wpcpo-options');
145
146        wpcpo_validate($options);
147    });
148
149    $(document).on('change', '.wpcpo-option-field-dimension', function () {
150        let $this = $(this);
151
152        if (parseFloat($this.val()) < parseFloat($this.attr('min'))) {
153            $this.val($this.attr('min'));
154        }
155
156        $this.closest('.wpcpo-option-dimensions').find('.wpcpo-option-field').trigger('change');
157    });
158
159    $(document).on('keyup change', '.wpcpo-option-field', function () {
160        var $field = $(this);
161
162        if ($field.attr('maxlength') > 0) {
163            var value = $field.val();
164            var remaining = $field.attr('maxlength') - value.length;
165
166            $field.next('.wpcpo-chars-remaining').find('span').text(remaining);
167        }
168
169        if (wpcpo_vars.change_url) {
170            // update URL
171            var $options = $field.closest('.wpcpo-options');
172            var $form = $field.closest('form.cart');
173            var link = new URL($form.attr('action'));
174            var search = new URLSearchParams(link.search);
175            var new_link = '';
176
177            $options.find('.wpcpo-option').each(function () {
178                let $option = $(this), option_key = $option.data('key'), option_val = '';
179
180                if ($option.hasClass('wpcpo-option-checkbox') || $option.hasClass('wpcpo-option-image-checkbox') || $option.hasClass('wpcpo-option-color-checkbox')) {
181                    option_val = $.map($option.find(':checkbox:checked'), function (n, i) {
182                        return n.value;
183                    }).join(',');
184                }
185
186                if ($option.hasClass('wpcpo-option-select')) {
187                    option_val = $option.find('select').val();
188                }
189
190                if ($option.hasClass('wpcpo-option-radio') || $option.hasClass('wpcpo-option-image-radio') || $option.hasClass('wpcpo-option-color-radio')) {
191                    option_val = $option.find('input[type="radio"]:checked').length ? $option.find('input[type="radio"]:checked').val() : '';
192                }
193
194                if (option_val.length) {
195                    search.set(option_key, option_val);
196                } else {
197                    search.delete(option_key);
198                }
199            });
200
201            $('input[name="wpcpo_url"]').val(search.toString()).trigger('change');
202
203            if (search.toString() !== '') {
204                new_link = link + '?' + search.toString();
205            } else {
206                new_link = link;
207            }
208
209            window.history.replaceState(null, null, new_link);
210        }
211    });
212
213    $(document).on('click touch', '.wpcpo-clear-btn', function (e) {
214        e.preventDefault();
215
216        $('.wpcpo-option-field').each(function () {
217            $(this).val('').prop('checked', false).prop('selected', false).trigger('blur');
218        });
219
220        wpcpo_init();
221    });
222})(jQuery);
223
224function wpcpo_round(value, decimals) {
225    return Number(Math.round(value + 'e' + decimals) + 'e-' + decimals);
226}
227
228function wpcpo_format_money(number, places, symbol, thousand, decimal) {
229    number = number || 0;
230    places = !isNaN((places = Math.abs(places))) ? places : 2;
231    symbol = symbol !== undefined ? symbol : '$';
232    thousand = thousand || ',';
233    decimal = decimal || '.';
234
235    var negative = number < 0 ? '-' : '',
236        i = parseInt((number = wpcpo_round(Math.abs(+number || 0), places).toFixed(places)), 10) + '', j = 0;
237
238    if (i.length > 3) {
239        j = i.length % 3;
240    }
241
242    return (symbol + negative + (j ? i.substr(0, j) + thousand : '') + i.substr(j).replace(/(\d{3})(?=\d)/g, '$1' + thousand) + (places ? decimal + wpcpo_round(Math.abs(number - i), places).toFixed(places).slice(2) : ''));
243}
244
245function wpcpo_format_price(price) {
246    var price_html = '<span class="woocommerce-Price-amount amount">';
247    var price_formatted = wpcpo_format_money(price, wpcpo_vars.price_decimals, '', wpcpo_vars.price_thousand_separator, wpcpo_vars.price_decimal_separator);
248
249    switch (wpcpo_vars.price_format) {
250        case '%1$s%2$s':
251            //left
252            price_html += '<span class="woocommerce-Price-currencySymbol">' + wpcpo_vars.currency_symbol + '</span>' + price_formatted;
253            break;
254        case '%1$s %2$s':
255            //left with space
256            price_html += '<span class="woocommerce-Price-currencySymbol">' + wpcpo_vars.currency_symbol + '</span> ' + price_formatted;
257            break;
258        case '%2$s%1$s':
259            //right
260            price_html += price_formatted + '<span class="woocommerce-Price-currencySymbol">' + wpcpo_vars.currency_symbol + '</span>';
261            break;
262        case '%2$s %1$s':
263            //right with space
264            price_html += price_formatted + ' <span class="woocommerce-Price-currencySymbol">' + wpcpo_vars.currency_symbol + '</span>';
265            break;
266        default:
267            //default
268            price_html += '<span class="woocommerce-Price-currencySymbol">' + wpcpo_vars.currency_symbol + '</span> ' + price_formatted;
269    }
270
271    price_html += '</span>';
272
273    return price_html;
274}
275
276function wpcpo_get_custom_price(custom_price, quantity, product_price, value, total = 0) {
277    let length = value.length;
278    let words = wpcpo_words_count(value);
279
280    value = parseFloat(value.replace(/[^\d.]/g, ''));
281
282    custom_price = custom_price.toLowerCase().replace(/([vpqlws])+/gi, function (match, tag, char) {
283        switch (tag) {
284            case 'q':
285                return quantity;
286            case 'p':
287                return product_price;
288            case 'l':
289                return length;
290            case 'w':
291                return words;
292            case 'v':
293                return value === '' ? 1 : value;
294            case 's':
295                return total;
296        }
297    });
298
299    try {
300        custom_price = eval(custom_price.replace(/[^-()\d/*+.]/g, ''));
301    } catch (e) {
302        custom_price = 0;
303    }
304
305    if (isNaN(custom_price) || (custom_price === Infinity) || (custom_price === -Infinity)) {
306        custom_price = 0;
307    }
308
309    return custom_price;
310}
311
312function wpcpo_get_custom_dimensions_price($field, custom_price, quantity, product_price, value, total = 0) {
313    let $dimensions = $field.closest('.wpcpo-option-dimensions');
314
315    custom_price = custom_price.toLowerCase().replace(/([vpqlws])+/gi, function (match, tag, char) {
316        switch (tag) {
317            case 'q':
318                return quantity;
319            case 'p':
320                return product_price;
321            case 's':
322                return total;
323        }
324    });
325
326    custom_price = custom_price.toLowerCase().replace(/d(\d+)/gi, function (match, tag, char) {
327        if ($dimensions.find('.wpcpo-option-field-dimension-d' + tag).length && ($dimensions.find('.wpcpo-option-field-dimension-d' + tag).val() !== '')) {
328            return parseFloat($dimensions.find('.wpcpo-option-field-dimension-d' + tag).val());
329        } else {
330            return 0;
331        }
332    });
333
334    try {
335        custom_price = eval(custom_price.replace(/[^-()\d/*+.]/g, ''));
336    } catch (e) {
337        custom_price = 0;
338    }
339
340    if (isNaN(custom_price) || (custom_price === Infinity) || (custom_price === -Infinity)) {
341        custom_price = 0;
342    }
343
344    return custom_price;
345}
346
347function wpcpo_get_field_price($field, product_price, quantity = 1, total = 0) {
348    let field_price = 0, type = $field.data('price-type'), price = $field.data('price'),
349        custom_price = $field.data('price-custom'), value = $field.val();
350
351    if ($field.hasClass('field-select')) {
352        let $option = $field.find(':selected');
353
354        type = $option.data('price-type');
355        price = $option.data('price');
356        custom_price = $option.data('price-custom');
357    }
358
359    switch (type) {
360        case 'flat':
361            if (isNaN(price)) {
362                field_price = parseFloat(product_price) * parseFloat(price) / 100;
363            } else {
364                field_price = parseFloat(price);
365            }
366
367            break;
368        case 'custom':
369            if ($field.hasClass('field-dimensions')) {
370                field_price = wpcpo_get_custom_dimensions_price($field, custom_price, quantity, product_price, value, total);
371            } else {
372                field_price = wpcpo_get_custom_price(custom_price, quantity, product_price, value, total);
373            }
374
375            break;
376        default:
377            // qty
378            if (isNaN(price)) {
379                field_price = parseFloat(product_price) * parseFloat(price) / 100 * quantity;
380            } else {
381                field_price = parseFloat(price) * quantity;
382            }
383
384            break;
385    }
386
387    return field_price;
388}
389
390function wpcpo_update_total($total) {
391    let qty = parseFloat($total.closest('form.cart').find('[name=quantity]').val()),
392        price = parseFloat($total.attr('data-price')), type = $total.data('type'), name = $total.data('product-name'),
393        total = 0, fields = $total.closest('.wpcpo-wrapper').find('.wpcpo-option-field').get(), html = '',
394        qty_string = wpcpo_vars.is_rtl ? wpcpo_vars.quantity_symbol + qty : qty + wpcpo_vars.quantity_symbol,
395        selected = 0;
396
397    if ($total.attr('data-total') !== undefined) {
398        // Compatible with WPC Price by Quantity
399        total = parseFloat($total.attr('data-total'));
400    } else {
401        total = price * qty;
402    }
403
404    html += '<ul>';
405    html += `<li>
406<div class="wpcpo-col1"><span>${qty_string}</span> ${name}</div>
407<div class="wpcpo-col2"><strong><span class="amount">${wpcpo_format_price(total)}</span></strong></div>
408</li>`;
409
410    for (let field of fields) {
411        let $field = jQuery(field), enable_price = $field.data('enable-price'),
412            $price_label = $field.closest('.wpcpo-option').find('.label-price-' + $field.attr('id')),
413            value = $field.val();
414
415        if ($field.is(':checkbox') || $field.is(':radio')) {
416            if ($field.prop('checked')) {
417                value = $field.data('label');
418            } else {
419                value = '';
420            }
421        }
422
423        if ($field.is('select')) {
424            value = $field.find('option:selected').data('label');
425        }
426
427        $price_label.html(``);
428
429        if (value !== '' && value !== undefined) {
430            if (wpcpo_vars.summary_clear) {
431                selected++;
432            }
433
434            if (enable_price === 1 || wpcpo_vars.summary_free) {
435                let field_price = wpcpo_get_field_price($field, price, qty, total);
436
437                if (!isNaN(field_price)) {
438                    total += parseFloat(field_price);
439                }
440
441                if (!isNaN(field_price) || wpcpo_vars.summary_free) {
442                    if (isNaN(field_price)) {
443                        html += `<li class="wpcpo-free"><div class="wpcpo-col1"><span>${$field.data('title')}:</span> ${value}</div><div class="wpcpo-col2"><strong><span class="amount">${wpcpo_format_price(field_price)}</span></strong></div></li>`;
444                    } else {
445                        if (parseFloat(field_price) > 0) {
446                            $price_label.html(`(+${wpcpo_format_price(field_price)})`);
447                        } else {
448                            $price_label.html(`(${wpcpo_format_price(field_price)})`);
449                        }
450
451                        html += `<li><div class="wpcpo-col1"><span>${$field.data('title')}:</span> ${value}</div><div class="wpcpo-col2"><strong><span class="amount">${wpcpo_format_price(field_price)}</span></strong></div></li>`;
452                    }
453                }
454            }
455        }
456    }
457
458    html += `<li class="wpcpo-subtotal wpcpo-subtotal-${selected}"><span class="wpcpo-clear"><a class="wpcpo-clear-btn" href="javascript:void(0);">${wpcpo_vars.i18n_clear}</a></span><span class="wpcpo-subtotal-amount">${wpcpo_vars.i18n_subtotal}<span class="amount">${wpcpo_format_price(total)}</span></span></li>`;
459    html += '</ul>';
460
461    $total.html(html);
462
463    jQuery(document).trigger('wpcpo_update_total', [$total, total]);
464}
465
466function wpcpo_words_count(str) {
467    return str.split(' ').filter(function (n) {
468        return n != '';
469    }).length;
470}
471
472function wpcpo_validate($options) {
473    let valid = true, $btn = $options.closest('form.cart').find('.single_add_to_cart_button');
474
475    $options.find('.wpcpo-option-field').each(function () {
476        let $this = jQuery(this), $option = $this.closest('.wpcpo-option');
477
478        // Skip validation for options hidden by conditional logic
479        if ($option.data('wpcpo-hidden') === true) {
480            $option.removeClass('wpcpo-has-error');
481            $option.find('.wpcpo-option-field').removeAttr('required');
482            return;
483        }
484
485        // Limit Checkbox
486        if ($this.is(':checkbox')) {
487            let $inputsCheck = $option.find('input:checked'), countCheckbox = $inputsCheck.get();
488            if (parseInt($option.data('limit')) > 0) {
489                if (countCheckbox.length >= parseInt($option.data('limit'))) {
490                    $option.find('input:checkbox:not(:checked)').attr('disabled', true);
491                } else {
492                    $option.find('input:checkbox').attr('disabled', false);
493                }
494                if (countCheckbox.length > parseInt($option.data('limit'))) {
495                    $option.addClass('wpcpo-has-error');
496                    valid = false;
497                } else {
498                    $option.removeClass('wpcpo-has-error');
499                }
500            }
501        }
502
503        // Required Checkbox & Radio
504        // Note: do NOT use the `required` attribute on checkbox/radio inputs —
505        // image-checkbox and color-checkbox inputs are visually hidden via CSS
506        // and cannot be focused by browser native validation ("not focusable" error).
507        // Validation is handled entirely via wpcpo-has-error class + disabled button.
508        if ($this.is(':checkbox') || $this.is(':radio')) {
509            let $inputsCheck = $option.find('input:checked'), countCheckbox = $inputsCheck.get();
510            if ($option.hasClass('wpcpo-required')) {
511                if (countCheckbox.length > 0) {
512                    $option.removeClass('wpcpo-has-error');
513                } else {
514                    $option.addClass('wpcpo-has-error');
515                    valid = false;
516                }
517            }
518        }
519
520        // Required Input
521        if ($this.prop('required')) {
522            if ($this.val() === '') {
523                $option.addClass('wpcpo-has-error');
524                valid = false;
525            } else {
526                $option.removeClass('wpcpo-has-error');
527            }
528        }
529    });
530
531    if (!valid) {
532        $btn.addClass('wpcpo-disabled');
533    } else {
534        $btn.removeClass('wpcpo-disabled');
535    }
536
537    jQuery(document).trigger('wpcpo_validate', [$options, valid]);
538}
539
540function wpcpo_init() {
541    if (jQuery('.wpcpo-color-picker').length) {
542        jQuery('.wpcpo-color-picker').wpColorPicker({
543            change: function (e, ui) {
544                setTimeout(function () {
545                    wpcpo_update_total(jQuery(e['target']).closest('.wpcpo-wrapper').find('.wpcpo-total'));
546                }, 100);
547            },
548        });
549    }
550
551    jQuery('.wpcpo-date-picker').each(function () {
552        var format = jQuery(this).attr('data-format');
553        var block = jQuery(this).attr('data-block');
554        var dpk_data = {dateFormat: format};
555
556        if ((block !== undefined) && (block !== '')) {
557            var block_dates = block.split(',');
558
559            if (block_dates.includes('past')) {
560                dpk_data.minDate = new Date();
561            } else {
562                if (block_dates.includes('future')) {
563                    dpk_data.maxDate = new Date();
564                }
565            }
566
567            dpk_data.onRenderCell = function onRenderCell(date, cellType) {
568                if (cellType === 'day') {
569                    const weekday = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'];
570                    var day_w = weekday[date.getDay()];
571                    var day_f = (('0' + (date.getMonth() + 1)).slice(-2)) + '/' + (('0' + date.getDate()).slice(-2)) + '/' + date.getFullYear();
572                    var isDisabled = block_dates.includes(day_w) || block_dates.includes(day_f);
573
574                    return {
575                        disabled: isDisabled,
576                    };
577                }
578            };
579        }
580
581        jQuery(this).wpcdpk(dpk_data);
582    });
583
584    jQuery('.wpcpo-time-picker').each(function () {
585        var format = jQuery(this).attr('data-format');
586
587        jQuery(this).wpcdpk({
588            timeFormat: format, timepicker: true, onlyTimepicker: true, classes: 'only-time',
589        });
590    });
591
592    jQuery('.wpcpo-date-time-picker').each(function () {
593        var date_format = jQuery(this).attr('data-date_format');
594        var time_format = jQuery(this).attr('data-time_format');
595
596        jQuery(this).wpcdpk({
597            dateFormat: date_format, timeFormat: time_format, timepicker: true,
598        });
599    });
600
601    jQuery('.wpcpo-date-range-picker').each(function () {
602        var format = jQuery(this).attr('data-format');
603
604        jQuery(this).wpcdpk({range: true, dateFormat: format, multipleDatesSeparator: ' - '});
605    });
606
607    jQuery('.wpcpo-option-field').trigger('change');
608
609    jQuery('.wpcpo-option-field').each(function () {
610        if (jQuery(this).attr('maxlength') > 0) {
611            jQuery(this).after('<small class="wpcpo-chars-remaining"><span>' + jQuery(this).attr('maxlength') + '</span> ' + wpcpo_vars.i18n_remaining + '</small>');
612        }
613    });
614
615    jQuery('.wpcpo-options').each(function () {
616        wpcpo_validate(jQuery(this));
617    });
618
619    jQuery('.wpcpo-total').each(function () {
620        wpcpo_update_total(jQuery(this));
621    });
622
623    jQuery(document).trigger('wpcpo_init');
624}

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