PageSourceSearch

https://cuore-amore.jp/view/asset/system-17-1-3-f5970b24939c8ae0c21a7388c7d5b152.js

js cuore-amore.jp collected 2026-10-09 23:37:09 UTC 102,746 bytes, 2,310 lines download raw bytes

1var $jQueryMakeShop = $.noConflict(true);
2(function($) {
3$(function() {
4    initCommonLogin();
5    initCommonCart();
6    initCommonOrderForm();
7    initCommonFavorite();
8    initCommonSearchForm();
9    initCommonContactForm();
10    initCommonSlideshow();
11});
12
13// Deletion Candidate
14function initCommonLogin()
15{
16    var loginUrlPrefix = '#makeshop-common-login-url:';
17    $('[href^="' + loginUrlPrefix + '"]').on('click', function() {
18        var key = $(this).attr('href').replace(loginUrlPrefix, '');
19        switch (key) {
20            case 'login':    location.replace('/view/member/login'); break;
21            case 'mypage':   location.replace('/view/member/mypage'); break;
22            case 'confirm':  location.replace('/view/member/order-history'); break;
23            case 'reserve':  location.replace('/view/member/point'); break;
24            case 'favorite': location.replace('/view/member/favorite'); break;
25            case 'member':   location.replace('/view/member/member-edit'); break;
26            default:         break;
27        }
28        
29        return false;
30    });
31}
32
33function initCommonCart()
34{
35    var pageData = $('#makeshop-page-common-cart-data');
36    var cartUrl = pageData.attr('data-cart-url');
37    var entryUrlPrefix = pageData.attr('data-cart-entry-url-prefix');
38    var removeUrlPrefix = pageData.attr('data-cart-remove-url-prefix');
39    var quantityUrlPrefix = pageData.attr('data-cart-quantity-url-prefix');
40    var quantityIdPrefix = pageData.attr('data-id-quantity-prefix');
41
42    $('[href^="' + entryUrlPrefix + '"]').on('click', function() {
43        var source = $(this).attr('href');
44        var index = $('[href="' + source + '"]').index(this);
45        var itemCode = source.replace(entryUrlPrefix, '');
46        
47        $.ajax({
48            url: '/api/cart/',
49            type: 'POST',
50            data: JSON.stringify({
51                'action': 'add',
52                'source': source,
53                'element_index': index,
54                'item_code': itemCode,
55                'option_list': [],
56                'quantity': 1,
57                'is_subscription': false,
58                'name_print' : []
59            }),
60            contentType: 'application/json',
61            dataType: 'json'
62        }).done(function(data) {
63            if (data.result && data.conversion_list) {
64                insertConversionHtml(data.conversion_list);
65            }
66            if (data.result && data.ga4.event) {
67                makeshop_ga_gtag('event', data.ga4.event, data.ga4.parameters);
68            }
69
70            if (typeof MakeShop_afterListCartEntry === 'function') {
71                var hookData = {
72                    result: data.result,
73                    source: data.source,
74                    elementIndex: data.element_index,
75                    systemCode: data.item_code,
76                    totalQuantity: data.total_quantity,
77                    error: data.error,
78                    method: {
79                        modal: openModalMessage
80                    }
81                };
82                if (MakeShop_afterListCartEntry(hookData) === false) {
83                    return;
84                }
85            }
86            
87            if (!data.result) {
88                openModalMessage(data.error.message);
89                return;
90            }
91
92            location.href = cartUrl;
93        });
94        
95        return false;
96    });
97
98    $('[href^="' + removeUrlPrefix + '"]').on('click', function() {
99        var source = $(this).attr('href');
100        var index = $('[href="' + source + '"]').index(this);
101        var key = source.replace(removeUrlPrefix, '');
102        var item = key.split('-');
103        var customSelectBasketId = item.length > 5 ? parseInt(item[5], 10) : 0;
104        
105        $.ajax({
106            url: '/api/cart/',
107            type: 'POST',
108            data: JSON.stringify({
109                'action': 'remove',
110                'source': source,
111                'element_index': index,
112                'item_code': item[0],
113                'option1': item[1],
114                'option2': item[2],
115                'option_group': item[3],
116                'name_print_number': item[4],
117                'custom_select_basket_id': customSelectBasketId
118            }),
119            contentType: 'application/json',
120            dataType: 'json'
121        }).done(function(data) {
122            if (typeof MakeShop_afterCartRemove === 'function') {
123                var hookData = {
124                    result: data.result,
125                    source: data.source,
126                    elementIndex: data.element_index,
127                    systemCode: data.item_code,
128                    totalQuantity: data.total_quantity,
129                    error: data.error,
130                    method: {
131                        modal: openModalMessage
132                    }
133                };
134                if (MakeShop_afterCartRemove(hookData) === false) {
135                    return;
136                }
137            }
138            
139            if (!data.result) {
140                openModalMessage(data.error.message);
141                return;
142            }
143            location.href = cartUrl;
144        });
145        
146        return false;
147    });
148
149    $('[href^="' + quantityUrlPrefix + '"]').on('click', function() {
150    	var source = $(this).attr('href');
151        var index = $('[href="' + source + '"]').index(this);
152        var key = source.replace(quantityUrlPrefix, '');
153        var item = key.split('-');
154        var customSelectBasketId = item.length > 5 ? parseInt(item[5], 10) : 0;
155
156        var quantity = getFormValue(quantityIdPrefix + key, index);
157        
158        $.ajax({
159            url: '/api/cart/',
160            type: 'POST',
161            data: JSON.stringify({
162                'action': 'quantity',
163                'source': source,
164                'element_index': index,
165                'item_code': item[0],
166                'option1': item[1],
167                'option2': item[2],
168                'option_group': item[3],
169                'name_print_number': item[4],
170                'quantity': quantity,
171                'custom_select_basket_id': customSelectBasketId
172            }),
173            contentType: 'application/json',
174            dataType: 'json'
175        }).done(function(data) {
176            if (typeof MakeShop_afterCartQuantity === 'function') {
177                var hookData = {
178                    result: data.result,
179                    source: data.source,
180                    elementIndex: data.element_index,
181                    systemCode: data.item_code,
182                    totalQuantity: data.total_quantity,
183                    error: data.error,
184                    method: {
185                        modal: openModalMessage
186                    }
187                };
188                if (MakeShop_afterCartQuantity(hookData) === false) {
189                    return;
190                }
191            }
192            
193            if (!data.result) {
194                openModalMessage(data.error.message);
195                return;
196            }
197            location.href = cartUrl;
198        });
199        
200        return false;
201    });
202}
203
204function initCommonOrderForm()
205{
206    var pageData = $('#makeshop-page-common-order-data');
207    var orderUrl = pageData.attr('data-order-url');
208
209    $('[href="' + orderUrl + '"]').on('click', function() {
210        $.ajax({
211            url: '/api/cart/',
212            type: 'POST',
213            data: JSON.stringify({
214                'service_type': 'makeshop',
215                'action': 'order'
216            }),
217            contentType: 'application/json',
218            dataType: 'json'
219        }).done(function(data) {
220            if (!data.result) {
221                openModalMessage(data.error.message);
222                return;
223            }
224
225            if (data.action === 'login') {
226                var opt = '&ssl_tempid=' + $('#makeshop-form-common-order input[name=ssl_tempid]').val();
227                ssl_login(data.login_type, opt);
228                return;
229            }
230            
231            $('#makeshop-form-common-order').attr('action', data.order_url);
232
233            $('#makeshop-form-common-order').trigger('submit');
234        });
235        
236        return false;
237    });
238}
239
240function initCommonFavorite()
241{
242    var pageData = $('#makeshop-page-common-favorite-data');
243    var entryUrlPrefix = pageData.attr('data-favorite-entry-url-prefix');
244    var removeUrlPrefix = pageData.attr('data-favorite-remove-url-prefix');
245
246    $('[href^="' + entryUrlPrefix + '"]').on('click', function() {
247    	var source = $(this).attr('href');
248        var index = $('[href="' + source + '"]').index(this);
249        var itemCode = source.replace(entryUrlPrefix, '');
250        
251        $.ajax({
252            url: '/api/favorite/',
253            type: 'POST',
254            data: JSON.stringify({
255                'action': 'add',
256                'source': source,
257                'element_index': index,
258                'item_code': itemCode
259            }),
260            contentType: 'application/json',
261            dataType: 'json'
262        }).done(function(data) {
263            if (typeof MakeShop_afterListFavoriteEntry === 'function') {
264                var hookData = {
265                    result: data.result,
266                    source: data.source,
267                    elementIndex: data.element_index,
268                    systemCode: data.item_code,
269                    error: data.error,
270                    method: {
271                        modal: openModalMessage
272                    }
273                };
274                if (MakeShop_afterListFavoriteEntry(hookData) === false) {
275                    return;
276                }
277            }
278        	
279            if (!data.result) {
280                openModalMessage(data.error.message);
281                return;
282            }
283
284            openModalMessage(data.message);
285        });
286        
287        return false;
288    });
289
290    $('[href^="' + removeUrlPrefix + '"]').on('click', function() {
291    	var source = $(this).attr('href');
292        var index = $('[href="' + source + '"]').index(this);
293        var itemCode = source.replace(removeUrlPrefix, '');
294        
295        $.ajax({
296            url: '/api/favorite/',
297            type: 'POST',
298            data: JSON.stringify({
299                'action': 'remove',
300                'source': source,
301                'element_index': index,
302                'item_code': itemCode
303            }),
304            contentType: 'application/json',
305            dataType: 'json'
306        }).done(function(data) {
307            if (typeof MakeShop_afterListFavoriteRemove === 'function') {
308                var hookData = {
309                    result: data.result,
310                    source: data.source,
311                    elementIndex: data.element_index,
312                    systemCode: data.item_code,
313                    error: data.error,
314                    method: {
315                        modal: openModalMessage
316                    }
317                };
318                if (MakeShop_afterListFavoriteRemove(hookData) === false) {
319                    return;
320                }
321            }
322        	
323            if (!data.result) {
324                openModalMessage(data.error.message);
325                return;
326            }
327
328            openModalMessage(data.message);
329        });
330        
331        return false;
332    });
333}
334
335function initCommonSearchForm()
336{
337    var pageData = $('#makeshop-page-common-search-data');
338    var searchUrl = pageData.attr('data-search-url');
339    var keywordId = pageData.attr('data-id-keyword');
340    var nameId = pageData.attr('data-id-name');
341    var priceLowId = pageData.attr('data-id-price-low');
342    var priceHighId = pageData.attr('data-id-price-high');
343    var categoryId = pageData.attr('data-id-category');
344    var originalCodeId = pageData.attr('data-id-original-code');
345    
346    $('[href="' + searchUrl + '"]').on('click', function() {
347        var index = $('[href="' + searchUrl + '"]').index(this);
348        var count = $('[href="' + searchUrl + '"]').length;
349
350        var keyword = getFormValue(keywordId, count === $('[data-id="' + keywordId + '"]').length ? index : 0);
351        var name = getFormValue(nameId, count === $('[data-id="' + nameId + '"]').length ? index : 0);
352        var priceLow = getFormValue(priceLowId, count === $('[data-id="' + priceLowId + '"]').length ? index : 0);
353        var priceHigh = getFormValue(priceHighId, count === $('[data-id="' + priceHighId + '"]').length ? index : 0);
354        var category = getFormValue(categoryId, count === $('[data-id="' + categoryId + '"]').length ? index : 0);
355        var originalCode = getFormValue(originalCodeId, count === $('[data-id="' + originalCodeId + '"]').length ? index : 0);
356        
357        $.ajax({
358            url: '/shop/shopsearch_url.html',
359            type: 'POST',
360            data: JSON.stringify({
361                'keyword': keyword,
362                'name': name,
363                'price_low': priceLow,
364                'price_high': priceHigh,
365                'category': category,
366                'original_code': originalCode
367            }),
368            contentType: 'application/json',
369            dataType: 'json'
370        }).done(function(data) {
371            if (!data.result) {
372                openModalMessage(data.error.message);
373                return;
374            }
375            location.href = data.url;
376        });
377
378        return false;
379    });
380}
381
382function initCommonContactForm()
383{
384    var pageData = $('#makeshop-page-common-contact-data');
385    var contactUrl = pageData.attr('data-contact-url');
386    
387    $('[href="' + contactUrl + '"]').on('click', function() {
388        $('#makeshop-form-common-contact').trigger('submit');
389        return false;
390    });
391}
392
393function initCommonSlideshow()
394{
395    var pageData = $('#makeshop-page-common-slideshow-data');
396    
397    if (pageData.attr('data-enabled') !== 'Y') {
398        return;
399    }
400
401    $('#M_slider .M_sliderFirstImage').remove();
402    $('#M_slider li').show();
403    $('#M_slider').bxSlider({
404        pause: pageData.attr('data-pause'),
405        speed: pageData.attr('data-speed'),
406        controls: pageData.attr('data-controls') === 'Y',
407        captions: pageData.attr('data-captions') === 'Y',
408        mode: pageData.attr('data-mode'),
409        auto: true
410    });
411}
412
413function openModalMessage(message, callback)
414{
415    $('[data-remodal-id=makeshop-common-modal] .error-text').html(message);
416    var modal = $('[data-remodal-id=makeshop-common-modal]').remodal({
417        hashTracking: false,
418        closeOnOutsideClick: false,
419        modifier: 'makeshop-modal'
420    });
421    if (callback !== undefined) {
422        $(document).on('closed', '.makeshop-modal .remodal', function (e) {
423        	callback();
424        });
425    }
426    modal.open();
427}
428window.openAmazonBuyNowModalMessage = function(message, callback) {
429    $('[data-remodal-id=makeshop-common-modal] .error-text').html(message);
430    var modal = $('[data-remodal-id=makeshop-common-modal]').remodal({
431        hashTracking: false,
432        closeOnOutsideClick: false,
433        modifier: 'makeshop-modal'
434    });
435    
436    if (callback !== undefined) {
437        // 名前空間化されたイベントを使用して、既存のハンドラを解除してから登録
438        $(document).off('closed.amazonBuyNow', '.makeshop-modal .remodal');
439        $(document).on('closed.amazonBuyNow', '.makeshop-modal .remodal', function (e) {
440            callback();
441            // 実行後にハンドラを解除(メモリリーク防止)
442            $(document).off('closed.amazonBuyNow', '.makeshop-modal .remodal');
443        });
444    }
445    
446    modal.open();
447};
448
449function getFormValue(dataId, index)
450{
451    var result = $('[data-id="' + dataId + '"]').eq(index).val();
452    return (result === undefined) ? '' : result;
453}
454
455function setFormValue(dataId, index, value)
456{
457    $('[data-id="' + dataId + '"]').eq(index).val(value);
458}
459
460function insertConversionHtml(conversionList)
461{
462    for (const conversion of conversionList) {
463        const div = $('<div>').css('display', 'none');
464
465        if (conversion.position === 'head') {
466            $('head').append(div);
467        } else if (conversion.position === 'body_top') {
468            $('body').prepend(div);
469        } else if (conversion.position === 'body_bottom') {
470            $('body').append(div);
471        }
472
473        div.html(conversion.html);
474    }
475}
476
477// カスタムセレクトの現在の値を取得(グローバル関数)
478function getCustomSelectValues() {
479    var values = {};
480    $('.makeshop-custom-select-wrap:visible').each(function() {
481        var $wrap = $(this);
482        var customSelectId = $wrap.attr('data-custom-select-id');
483        var inputType = parseInt($wrap.attr('data-input-type'), 10);
484        var value = null;
485        
486        switch (inputType) {
487            case 0: // dropdown
488                value = $wrap.find('select.makeshop-custom-select').val() || '0';
489                if (value !== '0') {
490                    values[customSelectId] = value;
491                }
492                break;
493            case 1: // button
494                var $selected = $wrap.find('button.makeshop-custom-select-button.selected');
495                if ($selected.length) {
496                    value = $selected.attr('data-value') || '0';
497                    if (value !== '0') {
498                        values[customSelectId] = value;
499                    }
500                }
501                break;
502            case 2: // image button
503                var $selectedImage = $wrap.find('button.makeshop-custom-select-image-button.selected');
504                if ($selectedImage.length) {
505                    value = $selectedImage.attr('data-value') || '0';
506                    if (value !== '0') {
507                        values[customSelectId] = value;
508                    }
509                }
510                break;
511            case 3: // radio
512                var $checked = $wrap.find('input.makeshop-custom-select-radio:checked');
513                if ($checked.length) {
514                    value = $checked.val() || '0';
515                    if (value !== '0') {
516                        values[customSelectId] = value;
517                    }
518                }
519                break;
520            case 4: // text
521                var $text = $wrap.find('input.makeshop-custom-select-text');
522                if ($text.length) {
523                    var rawValue = $text.val();
524                    var minValue = parseFloat($text.attr('min'));
525                    var maxValue = parseFloat($text.attr('max'));
526                    // 条件表示の判定では、入力中の値を使う(step一致は要求しない)
527                    // stepは購入可否・価格計算側でのみ考慮する
528                    var parsedValue = parseFloat(rawValue);
529                    if (rawValue !== '' && rawValue !== null && rawValue !== undefined && !isNaN(parsedValue)) {
530                        value = parsedValue;
531                        var isUnderMin = !isNaN(minValue) && value < minValue;
532                        var isOverMax = !isNaN(maxValue) && value > maxValue;
533                        if (!isUnderMin && !isOverMax) {
534                            values[customSelectId] = value;
535                        }
536                    }
537                }
538                break;
539        }
540    });
541    return values;
542}
543
544// 数値入力が購入可能な値か(min?max内かつstepに合致)を判定
545function isPurchasableNumericValue(value, minVal, maxVal, stepVal) {
546    if (value === '' || value === null || value === undefined || isNaN(parseFloat(value))) {
547        return false;
548    }
549    var v = parseFloat(value);
550    var min = isNaN(minVal) ? 0 : parseFloat(minVal);
551    var max = isNaN(maxVal) ? Infinity : parseFloat(maxVal);
552    var step = isNaN(stepVal) || stepVal <= 0 ? 1 : parseFloat(stepVal);
553    if (v < min || v > max) {
554        return false;
555    }
556    var vInt = Math.round(v * 100);
557    var minInt = Math.round(min * 100);
558    var stepInt = Math.round(step * 100);
559    if (stepInt <= 0) {
560        return true;
561    }
562    return Math.abs((vInt - minInt) % stepInt) === 0;
563}
564
565function getNumericInputPurchasableValue($numInput) {
566    var raw = $numInput.val();
567    var min = parseFloat($numInput.attr('min'));
568    var max = parseFloat($numInput.attr('max'));
569    var step = parseFloat($numInput.attr('step'));
570    return isPurchasableNumericValue(raw, min, max, step) ? (parseFloat(raw) || 0) : 0;
571}
572
573// 数値を3桁区切りの文字列にフォーマット(グローバル関数)
574function numberFormat(num) {
575    var n = parseFloat(num);
576    if (typeof n !== 'number' || isNaN(n)) {
577        return '0';
578    }
579    return n.toString().replace(/(\d)(?=(\d{3})+$)/g, '$1,');
580}
581
582var OPTION_DATA_KEY_BASE_PRICE_PREFIX = 'basePrice-';
583var OPTION_DATA_KEY_REPEAT_DISCOUNT_PREFIX = 'isRepeatDiscount-';
584
585function applyTaxToExcludedPrice(excludedAmount, taxRate, taxType) {
586    if (!taxRate || taxRate <= 0) {
587        return excludedAmount;
588    }
589    var r = taxRate / 100;
590    var tax;
591    switch (taxType) {
592        case 'floor':
593            tax = Math.floor(excludedAmount * r);
594            break;
595        case 'ceil':
596            tax = Math.ceil(excludedAmount * r);
597            break;
598        default:
599            tax = Math.round(excludedAmount * r);
600            break;
601    }
602    return excludedAmount + tax;
603}
604
605function getItemOptionIsExcludingTaxCalc() {
606    var $opt = $('#makeshop-page-item-option-data');
607    if (!$opt.length) {
608        return false;
609    }
610    // 外税(tax を別立て計算するか)
611    var flag = ($opt.attr('data-is-excluding-tax-calc') || '');
612    return flag === 'Y';
613}
614
615// カスタムセレクト数値入力の横に出す金額(外税ショップは税抜表示、それ以外は税込)
616function formatCustomSelectNumericAddonForDisplay(excludedAmount, taxRate, taxType) {
617    if (getItemOptionIsExcludingTaxCalc()) {
618        return excludedAmount;
619    }
620    return applyTaxToExcludedPrice(excludedAmount, taxRate, taxType);
621}
622
623// カスタムセレクトの値を配列形式で取得(API/価格計算用。表示中のもののみ)
624// options.forPriceCalc: true のときは価格計算用(無効値は0として送る)。カート追加時は false で実際の値を送りサーバーで「入力値に誤りがあります」を返させる
625function getCustomSelectListFromDom(options) {
626    options = options || {};
627    var forPriceCalc = options.forPriceCalc === true;
628    var customSelectList = [];
629    var seenCustomSelectIds = {};
630    $('.makeshop-custom-select-wrap:visible').each(function() {
631        var $wrap = $(this);
632        var customSelectId = $wrap.attr('data-custom-select-id');
633        if (seenCustomSelectIds[customSelectId]) {
634            return;
635        }
636        seenCustomSelectIds[customSelectId] = true;
637
638        var inputType = parseInt($wrap.attr('data-input-type'), 10);
639        var selectedItemId = '';
640        var numericValue = 0;
641        var shouldSendNumericValue = false;
642
643        switch (inputType) {
644            case 0: // dropdown
645                selectedItemId = $wrap.find('select.makeshop-custom-select').val() || '0';
646                break;
647            case 1: // button
648                var $selected = $wrap.find('button.makeshop-custom-select-button.selected');
649                selectedItemId = $selected.length ? $selected.attr('data-value') || '0' : '0';
650                break;
651            case 2: // image button
652                var $selectedImage = $wrap.find('button.makeshop-custom-select-image-button.selected');
653                selectedItemId = $selectedImage.length ? $selectedImage.attr('data-value') || '0' : '0';
654                break;
655            case 3: // radio
656                var $checked = $wrap.find('input.makeshop-custom-select-radio:checked');
657                selectedItemId = $checked.length ? $checked.val() || '0' : '0';
658                break;
659            case 4: // text
660                var $text = $wrap.find('input.makeshop-custom-select-text');
661                var rawVal = $text.val();
662                var textMin = parseFloat($text.attr('min'));
663                var textMax = parseFloat($text.attr('max'));
664                var textStep = parseFloat($text.attr('step'));
665                var isPurchasable = isPurchasableNumericValue(rawVal, textMin, textMax, textStep);
666                if (rawVal === '') {
667                    selectedItemId = '0';
668                    // カート追加時は未入力を「0」と区別するため numeric_value を送らない
669                    // (価格計算時は従来どおり 0 を送る)
670                    if (forPriceCalc) {
671                        numericValue = 0;
672                        shouldSendNumericValue = true;
673                    }
674                } else if (forPriceCalc && !isPurchasable) {
675                    selectedItemId = '0';
676                    numericValue = 0;
677                    shouldSendNumericValue = true;
678                } else {
679                    numericValue = parseFloat(rawVal) || 0;
680                    selectedItemId = $text.attr('data-selected-item-id') || '1';
681                    shouldSendNumericValue = true;
682                }
683                break;
684        }
685
686        if (selectedItemId !== '') {
687            var customSelectData = {
688                custom_select_id: customSelectId,
689                selected_item_id: selectedItemId
690            };
691            // 数値入力型の場合、未入力時(カート追加時)は numeric_value を送らない
692            if (inputType === 4 && shouldSendNumericValue) {
693                customSelectData.numeric_value = numericValue;
694            }
695            customSelectList.push(customSelectData);
696        }
697    });
698    return customSelectList;
699}
700
701// 数値入力カスタムセレクト用:final_priceからbasePriceを算出してoptionDataに保存(SKU価格+加算の表示用)
702function updateBasePriceFromFinalPrice(optionData, finalPrice, purchaseType, isCustomSelectAdditionalEnabled) {
703    if (!optionData || !optionData.length) {
704        return;
705    }
706    var customSelectAdditional = 0;
707    $('.makeshop-custom-select-wrap:visible').each(function() {
708        if (parseInt($(this).attr('data-input-type'), 10) === 4) {
709            var $numInput = $(this).find('input.makeshop-custom-select-text');
710            if ($numInput.length) {
711                var val = getNumericInputPurchasableValue($numInput);
712                var up = parseFloat($numInput.attr('data-unit-price')) || 0;
713                var upTaxRate = parseFloat($numInput.attr('data-unit-price-tax-rate')) || 0;
714                var upTaxType = $numInput.attr('data-unit-price-tax-type') || 'round';
715                customSelectAdditional += applyTaxToExcludedPrice(Math.round(val * up), upTaxRate, upTaxType);
716            }
717        }
718    });
719    var finalPriceNum = parseFloat(String(finalPrice).replace(/[^0-9.-]/g, '')) || 0;
720    var additionalForBasePrice = (isCustomSelectAdditionalEnabled === false) ? 0 : customSelectAdditional;
721    var basePriceValue = finalPriceNum - Math.round(additionalForBasePrice);
722    var safeBasePrice = (typeof basePriceValue === 'number' && !isNaN(basePriceValue)) ? basePriceValue : finalPriceNum;
723    optionData.data('basePrice', safeBasePrice);
724    if (purchaseType) {
725        optionData.data(OPTION_DATA_KEY_BASE_PRICE_PREFIX + purchaseType, safeBasePrice);
726    }
727}
728
729// 条件を評価する(グローバル関数)
730// conditionsは配列(GroupMappingConditions)
731// 配列内の各要素はTriggerConditionで、OR条件(どれか1つを満たせばOK)
732function evaluateCustomSelectCondition(conditions, values) {
733    if (!conditions || !Array.isArray(conditions) || conditions.length === 0) {
734        return true;
735    }
736    
737    // 配列内の各TriggerConditionを評価(OR条件)
738    for (var i = 0; i < conditions.length; i++) {
739        var triggerCondition = conditions[i];
740        
741        if (!triggerCondition || !triggerCondition.trigger_custom_select_id || !triggerCondition.rules) {
742            continue;
743        }
744        
745        var triggerCustomSelectId = triggerCondition.trigger_custom_select_id;
746        var rules = triggerCondition.rules;
747        
748        if (!Array.isArray(rules) || rules.length === 0) {
749            continue;
750        }
751        
752        // デフォルト値が設定されていない場合はこのtrigger条件を満たさない
753        if (!values.hasOwnProperty(triggerCustomSelectId)) {
754            continue;
755        }
756        
757        var actualValue = values[triggerCustomSelectId];
758        
759        // rules内の各要素はConditionRuleで、AND条件(すべてを満たす必要がある)
760        var allRulesMet = true;
761        for (var j = 0; j < rules.length; j++) {
762            var rule = rules[j];
763            
764            if (!rule || !rule.operator) {
765                allRulesMet = false;
766                break;
767            }
768            
769            var operator = rule.operator;
770            var ruleMet = false;
771            
772            if (operator === 'eq') {
773                // item_ids内はOR条件(どれか1つに一致すればOK)
774                if (rule.item_ids && Array.isArray(rule.item_ids)) {
775                    var actualValueInt = parseInt(actualValue, 10);
776                    for (var k = 0; k < rule.item_ids.length; k++) {
777                        if (actualValueInt === parseInt(rule.item_ids[k], 10)) {
778                            ruleMet = true;
779                            break;
780                        }
781                    }
782                }
783            } else if (operator === 'gte') {
784                // numeric_value以上
785                if (rule.numeric_value !== undefined) {
786                    ruleMet = (parseFloat(actualValue) >= parseFloat(rule.numeric_value));
787                }
788            } else if (operator === 'lte') {
789                // numeric_value以下
790                if (rule.numeric_value !== undefined) {
791                    ruleMet = (parseFloat(actualValue) <= parseFloat(rule.numeric_value));
792                }
793            }
794            
795            if (!ruleMet) {
796                allRulesMet = false;
797                break;
798            }
799        }
800        
801        // このtrigger条件を満たした場合はtrueを返す(OR条件)
802        if (allRulesMet) {
803            return true;
804        }
805    }
806    
807    // すべてのtrigger条件を満たさなかった場合
808    return false;
809}
810
811// 条件再評価を実行してDOMを更新
812function reEvaluateCustomSelectConditions(skipNumericTrigger) {
813    var values = getCustomSelectValues();
814    
815    $('.makeshop-custom-select-wrap').each(function() {
816        var $wrap = $(this);
817        var hasCondition = $wrap.attr('data-has-condition') === '1';
818        
819        if (!hasCondition) {
820            // 条件がない場合は常に表示
821            $wrap.show();
822            $wrap.attr('data-condition-met', '1');
823            return;
824        }
825        
826        var conditionData = $wrap.attr('data-condition');
827        if (!conditionData) {
828            $wrap.show();
829            $wrap.attr('data-condition-met', '1');
830            return;
831        }
832        
833        try {
834            var conditions = JSON.parse(conditionData);
835            // conditionsは配列(GroupMappingConditions)
836            if (!Array.isArray(conditions)) {
837                conditions = [];
838            }
839            
840            var conditionMet = evaluateCustomSelectCondition(conditions, values);
841            
842            // 前回の状態を確認(data-condition-metを更新する前に)
843            var previousConditionMet = $wrap.attr('data-condition-met') === '1';
844            
845            if (conditionMet) {
846                $wrap.show();
847                $wrap.attr('data-condition-met', '1');
848                
849                // 非表示から表示になった場合のみ初期値を適用
850                if (!previousConditionMet) {
851                    var inputType = parseInt($wrap.attr('data-input-type'), 10);
852                    var defaultItemId = $wrap.attr('data-default-item-id');
853                    
854                    switch (inputType) {
855                        case 0: // dropdown
856                            var $select = $wrap.find('select.makeshop-custom-select');
857                            if ($select.length) {
858                                var currentVal = $select.val();
859                                // 未選択状態の場合のみ初期値を適用
860                                if ((currentVal === '0' || currentVal === null || currentVal === '' || currentVal === undefined) && defaultItemId) {
861                                    var $defaultOption = $select.find('option[value="' + defaultItemId + '"]');
862                                    if ($defaultOption.length && $defaultOption.val() !== '0') {
863                                        $select.val($defaultOption.val());
864                                        $select.trigger('change');
865                                    }
866                                }
867                            }
868                            break;
869                        case 1: // button
870                            var $buttons = $wrap.find('button.makeshop-custom-select-button');
871                            if ($buttons.length && !$buttons.filter('.selected').length && defaultItemId) {
872                                var $defaultButton = $buttons.filter('[data-value="' + defaultItemId + '"]');
873                                if ($defaultButton.length) {
874                                    // 表示用トリガーバーのラベルを更新
875                                    var csIdBtn = $wrap.attr('data-custom-select-id');
876                                    var $barBtnRoot = $('.ms-cs-trigger-bar[data-target-id="' + csIdBtn + '"]');
877                                    if ($barBtnRoot.length) {
878                                        var $barBtn = $barBtnRoot.find('.ms-cs-value-text');
879                                        var labelBtn = $defaultButton.find('.makeshop-custom-select-button-text').text();
880                                        if ($barBtn.length && labelBtn) {
881                                            $barBtn.text(labelBtn);
882                                        }
883                                    }
884
885                                    // 既存のclickハンドラに任せて選択状態・価格更新を実行
886                                    $defaultButton.trigger('click');
887                                }
888                            }
889                            break;
890                        case 2: // image button
891                            var $imageButtons = $wrap.find('button.makeshop-custom-select-image-button');
892                            if ($imageButtons.length && !$imageButtons.filter('.selected').length && defaultItemId) {
893                                var $defaultImageButton = $imageButtons.filter('[data-value="' + defaultItemId + '"]');
894                                if ($defaultImageButton.length) {
895                                    // 表示用トリガーバーのラベル+画像を更新
896                                    var csIdImg = $wrap.attr('data-custom-select-id');
897                                    var $barImgRoot = $('.ms-cs-trigger-bar[data-target-id="' + csIdImg + '"]');
898                                    if ($barImgRoot.length) {
899                                        var $barImgText = $barImgRoot.find('.ms-cs-value-text');
900                                        var $barImgWrap = $barImgRoot.find('.ms-cs-selected-image-wrap');
901                                        var labelImg = $defaultImageButton.find('.makeshop-custom-select-button-text').text();
902                                        if ($barImgText.length && labelImg) {
903                                            $barImgText.text(labelImg);
904                                        }
905                                        // ボタン内の画像を反映
906                                        var $srcImg = $defaultImageButton.find('.makeshop-custom-select-image').first();
907                                        if ($barImgWrap.length && $srcImg.length) {
908                                            $barImgWrap.empty().append($srcImg.clone());
909                                            $barImgWrap.show();
910                                        }
911                                    }
912
913                                    // 既存のclickハンドラに任せて選択状態・価格更新を実行
914                                    $defaultImageButton.trigger('click');
915                                }
916                            }
917                            break;
918                        case 3: // radio
919                            var $radios = $wrap.find('input.makeshop-custom-select-radio');
920                            if ($radios.length && !$radios.filter(':checked').length && defaultItemId) {
921                                var $defaultRadio = $radios.filter('[value="' + defaultItemId + '"]');
922                                if ($defaultRadio.length) {
923                                    $radios.prop('checked', false);
924                                    $defaultRadio.prop('checked', true);
925                                    $defaultRadio.trigger('change');
926                                }
927                            }
928                            break;
929                        case 4: // text
930                            var $text = $wrap.find('input.makeshop-custom-select-text');
931                            if ($text.length) {
932                                var currentTextVal = parseFloat($text.val()) || 0;
933                                var minValue = parseFloat($text.attr('min')) || 0;
934                                // 値が最小値以下の場合(未設定状態)のみ初期値を適用
935                                if (currentTextVal <= minValue) {
936                                    var rawDefault = $text.attr('value');
937                                    var defaultValue = (rawDefault !== undefined && rawDefault !== null && rawDefault !== '') ? parseFloat(rawDefault) : NaN;
938
939                                    // デフォルト値が明示的に設定されている場合のみ反映する
940                                    if (!isNaN(defaultValue)) {
941                                        $text.val(defaultValue);
942                                        // 価格表示を更新
943                                        var unitPrice = parseFloat($text.attr('data-unit-price')) || 0;
944                                        var $priceSpan = $wrap.find('.makeshop-custom-select-text-price');
945                                        var $textWrap = $wrap.find('.makeshop-custom-select-text-wrap');
946                                        var prefix = $textWrap.attr('data-price-format-prefix');
947                                        var suffix = $textWrap.attr('data-price-format-suffix');
948                                        var unitPriceTaxRate = parseFloat($text.attr('data-unit-price-tax-rate')) || 0;
949                                        var unitPriceTaxType = $text.attr('data-unit-price-tax-type') || 'round';
950                                        if (unitPrice != 0 && prefix && suffix) {
951                                            var totalPrice = formatCustomSelectNumericAddonForDisplay(Math.round(defaultValue * unitPrice), unitPriceTaxRate, unitPriceTaxType);
952                                            var sign = (totalPrice >= 0) ? '+' : '';
953                                            var priceText = prefix + sign + numberFormat(totalPrice) + suffix;
954                                            if ($priceSpan.length) {
955                                                $priceSpan.text(priceText);
956                                            }
957                                        }
958                                        $text.trigger('change');
959                                    }
960                                }
961                            }
962                            break;
963                    }
964                }
965            } else {
966                $wrap.hide();
967                $wrap.attr('data-condition-met', '0');
968                
969                // 非表示になったカスタムセレクトの値をリセット
970                var inputType = parseInt($wrap.attr('data-input-type'), 10);
971                switch (inputType) {
972                    case 0: // dropdown
973                        $wrap.find('select.makeshop-custom-select').val('0');
974                        break;
975                    case 1: // button
976                        $wrap.find('button.makeshop-custom-select-button').removeClass('selected');
977                        break;
978                    case 2: // image button
979                        $wrap.find('button.makeshop-custom-select-image-button').removeClass('selected');
980                        break;
981                    case 3: // radio
982                        $wrap.find('input.makeshop-custom-select-radio').prop('checked', false);
983                        break;
984                    case 4: // text
985                        var $text = $wrap.find('input.makeshop-custom-select-text');
986                        // 非表示時は数値をクリアし「未入力」状態に戻す
987                        $text.val('');
988                        var $priceSpan = $wrap.find('.makeshop-custom-select-text-price');
989                        if ($priceSpan.length) {
990                            $priceSpan.text('');
991                        }
992                        break;
993                }
994            }
995        } catch (e) {
996            console.error('条件評価エラー:', e);
997            $wrap.show();
998            $wrap.attr('data-condition-met', '1');
999        }
1000    });
1001    // 条件に合致して初めて表示された数値入力の価格表示を更新
1002    if (!skipNumericTrigger) {
1003        $('.makeshop-custom-select-wrap:visible').each(function() {
1004            if (parseInt($(this).attr('data-input-type'), 10) === 4) {
1005                $(this).find('input.makeshop-custom-select-text').trigger('change');
1006            }
1007        });
1008    }
1009}
1010
1011/**
1012 * 全カスタムセレクト要素の現在の状態(ID・表示/非表示・条件充足)のスナップショットを文字列として取得する。
1013 * reEvaluateCustomSelectConditionsUntilStable() で収束判定に使用される。
1014 * @returns {string} 各要素の状態を '|' 区切りで連結した文字列
1015 */
1016function getCustomSelectConditionStateSnapshot() {
1017    var snapshot = [];
1018    $('.makeshop-custom-select-wrap').each(function() {
1019        var $wrap = $(this);
1020        var id = $wrap.attr('data-custom-select-id') || '';
1021        var visible = $wrap.is(':visible') ? '1' : '0';
1022        var met = $wrap.attr('data-condition-met') === '1' ? '1' : '0';
1023        snapshot.push(id + ':' + visible + ':' + met);
1024    });
1025    return snapshot.join('|');
1026}
1027
1028/**
1029 * 条件評価を安定するまで繰り返し実行する。
1030 * 多段依存(A→B→C→D…)の連鎖評価を1回の呼び出しで収束させる。
1031 * @param {boolean} skipNumericTrigger - 数値入力のchangeイベントトリガーをスキップするか
1032 */
1033function reEvaluateCustomSelectConditionsUntilStable(skipNumericTrigger) {
1034    var maxIterations = Math.max(1, $('.makeshop-custom-select-wrap').length);
1035    var i;
1036    for (i = 0; i < maxIterations; i++) {
1037        var before = getCustomSelectConditionStateSnapshot();
1038        reEvaluateCustomSelectConditions(skipNumericTrigger);
1039        var after = getCustomSelectConditionStateSnapshot();
1040        if (before === after) {
1041            break;
1042        }
1043    }
1044}
1045
1046$(function() {
1047    initItemOption();
1048    initItemCustomSelect();
1049    initItemRestock();
1050    initItemSkuRestock();
1051    initItemSkuInformation();
1052    initItemFavorite();
1053    initItemCart();
1054    initItemSkuCart();
1055    initItemContact();
1056    initItemOptionImage();
1057    initItemMetaField();
1058});
1059
1060/**
1061 * 商品属性のバリエーション切替(#10964)
1062 *
1063 * 商品ページ表示時にバリエーションごとの値をページ内に保持しておき、
1064 * バリエーションが選択されたら data-id 付きの要素の中身を差し替える。
1065 * 上書きの無いバリエーションでは商品の値に戻す。
1066 */
1067function initItemMetaField()
1068{
1069    var metaFieldData = $('#makeshop-page-item-metafield-data');
1070    if (!metaFieldData.length) {
1071        return;
1072    }
1073
1074    var optionIdPrefix = metaFieldData.attr('data-id-option-prefix');
1075    var metaFieldIdPrefix = metaFieldData.attr('data-id-metafield-prefix');
1076    if (!optionIdPrefix || !metaFieldIdPrefix) {
1077        return;
1078    }
1079
1080    var skuValues = metaFieldData.attr('data-sku-values');
1081    if (!skuValues) {
1082        // SKU単位の上書きが1件も無い商品は切り替えるものが無い
1083        return;
1084    }
1085
1086    var data = null;
1087    try {
1088        data = JSON.parse(skuValues);
1089    } catch (e) {
1090        return;
1091    }
1092    if (!data || !data.sku) {
1093        return;
1094    }
1095
1096    var $options = $('[data-id^="' + optionIdPrefix + '"]').not('[data-id*="_custom_"]');
1097    if (!$options.length) {
1098        return;
1099    }
1100
1101    // 同じ要素に価格取得の change ハンドラが bind されているため、名前空間を付けて初期化時に巻き込まないようにする
1102    $options.on('change.metafield', function() {
1103        var $this = $(this);
1104        var dataId = $this.attr('data-id');
1105        if (!dataId || dataId.indexOf('_custom_') !== -1) {
1106            return;
1107        }
1108
1109        // 同じバリエーションの組をテンプレートに複数配置できるため、変更された組の選択値だけを見る
1110        var optionListIndex = $('[data-id="' + dataId + '"]').not('[data-id*="_custom_"]').index(this);
1111        var optionListSetCount = $('[data-id="' + optionIdPrefix + '1"]').not('[data-id*="_custom_"]').length;
1112        if (optionListSetCount === 0) {
1113            return;
1114        }
1115
1116        var optionMax = $options.length / optionListSetCount;
1117        var optionList = [];
1118        for (var i = 0; i < optionMax; i++) {
1119            var value = $('[data-id="' + optionIdPrefix + (i + 1) + '"]').not('[data-id*="_custom_"]').eq(optionListIndex).val();
1120            optionList.push(parseInt(value, 10) || 0);
1121        }
1122
1123        var skuKey = (optionList[0] || 0) + '-' + (optionList[1] || 0);
1124        var values = $.extend({}, data['default'] || {}, data.sku[skuKey] || {});
1125        $.each(values, function(key, html) {
1126            var $target = $('[data-id="' + metaFieldIdPrefix + key + '"]');
1127            if (!$target.length) {
1128                return;
1129            }
1130            // 値が無くなるバリエーションでは要素ごと隠す。
1131            // 一括出力(list_html)は値だけ消すと属性名の空行が残るため、行ごと隠す
1132            //
1133            // 属性を1件ずつ書いているテンプレートでは行ラッパがショップ自作なので、
1134            // data-makeshop-item-metafield-row を付けてもらったらそれも同じ対象にする
1135            // (makeshop-jp/popeye#37716)。closest() はショップ自作のDOMも遡るため、
1136            // 既存クラスと同じ仕組みでそのまま出し入れできる。
1137            // 既存クラス .makeshop-item-metafield-item を案内しないのは、
1138            // 既定テーマのCSSが体裁を当てており、ショップ独自レイアウトに乗ってしまうため
1139            var hasValue = (html !== '');
1140            $target.html(html).toggle(hasValue);
1141            $target.closest('.makeshop-item-metafield-item, [data-makeshop-item-metafield-row
1141]').toggle(hasValue);
1142        });
1143    }).trigger('change.metafield');
1144}
1145
1146function initItemOption()
1147{
1148    var optionData = $('#makeshop-page-item-option-data');
1149    var purchaseTypeNormal = optionData.attr('data-purchase-type-normal');
1150    var purchaseTypeSubscription = optionData.attr('data-purchase-type-subscription');
1151    var optionIdPrefix = optionData.attr('data-id-option-prefix');
1152    var priceIdPrefix = optionData.attr('data-id-price-prefix');
1153    var priceExcludedTaxIdPrefix = optionData.attr('data-id-price-excluded-tax-prefix');
1154    var taxIdPrefix = optionData.attr('data-id-tax-prefix');
1155    var pointIdPrefix = optionData.attr('data-id-point-prefix');
1156
1157    $('[data-id^="' + optionIdPrefix + '"]').not('[data-id*="_custom_"]').on('change', function() {
1158    	var $this = $(this);
1159    	var dataId = $this.attr('data-id');
1160    	if (!dataId || dataId.indexOf('_custom_') !== -1) {
1161    	    return false;
1162    	}
1163    	var optionListIndex = $('[data-id="' + dataId + '"]').not('[data-id*="_custom_"]').index(this);
1164        var optionListSetCount = $('[data-id="' + optionIdPrefix + '1"]').not('[data-id*="_custom_"]').length;
1165
1166        var priceIdPrefixCount = $('[data-id^="' + priceIdPrefix + '"]').length;
1167        var priceExcludedTaxIdPrefixCount = $('[data-id^="' + priceExcludedTaxIdPrefix + '"]').length;
1168        var taxIdPrefixCount = $('[data-id^="' + taxIdPrefix + '"]').length;
1169        var pointIdPrefixCount = $('[data-id^="' + pointIdPrefix + '"]').length;
1170        
1171        var priceIdCount = $('[data-id="' + priceIdPrefix + purchaseTypeNormal + '"]').length;
1172        var priceExcludedTaxIdCount = $('[data-id="' + priceExcludedTaxIdPrefix + purchaseTypeNormal + '"]').length;
1173        var taxIdCount = $('[data-id="' + taxIdPrefix + purchaseTypeNormal + '"]').length;
1174        var pointIdCount = $('[data-id="' + pointIdPrefix + purchaseTypeNormal + '"]').length;
1175
1176        var subscriptionPriceIdCount = $('[data-id="' + priceIdPrefix + purchaseTypeSubscription + '"]').length;
1177        var subscriptionPriceExcludedTaxIdCount = $('[data-id="' + priceExcludedTaxIdPrefix + purchaseTypeSubscription + '"]').length;
1178        var subscriptionTaxIdCount = $('[data-id="' + taxIdPrefix + purchaseTypeSubscription + '"]').length;
1179        var subscriptionPointIdCount = $('[data-id="' + pointIdPrefix + purchaseTypeSubscription + '"]').length;
1180        
1181        var ajaxLoop = [];
1182        if (optionListSetCount === priceIdPrefixCount) {
1183        	var $priceElement = $('[data-id^="' + priceIdPrefix + '"]').eq(optionListIndex);
1184        	if ($priceElement.length && $priceElement.attr('data-id')) {
1185        		var purchaseType = $priceElement.attr('data-id').replace(priceIdPrefix, '');
1186        		ajaxLoop.push({ is_repeat_sale: (purchaseType === purchaseTypeSubscription) });
1187        	}
1188        } else if (optionListSetCount === priceExcludedTaxIdPrefixCount) {
1189        	var $priceElement = $('[data-id^="' + priceExcludedTaxIdPrefix + '"]').eq(optionListIndex);
1190        	if ($priceElement.length && $priceElement.attr('data-id')) {
1191        		var purchaseType = $priceElement.attr('data-id').replace(priceExcludedTaxIdPrefix, '');
1192        		ajaxLoop.push({ is_repeat_sale: (purchaseType === purchaseTypeSubscription) });
1193        	}
1194        } else {
1195        	if (0 < priceIdCount || 0 < priceExcludedTaxIdCount) {
1196        	    ajaxLoop.push({ is_repeat_sale: false });
1197        	}
1198        	if (0 < subscriptionPriceIdCount || 0 < subscriptionPriceExcludedTaxIdCount) {
1199        	    ajaxLoop.push({ is_repeat_sale: true });
1200        	}
1201        }
1202
1203        var optionList = [];
1204        if (optionListSetCount !== 0) {
1205            var optionMax = $('[data-id^="' + optionIdPrefix + '"]').not('[data-id*="_custom_"]').length / optionListSetCount;
1206            for (var i = 0; i < optionMax; i++) {
1207                optionList.push($('[data-id="' + optionIdPrefix + (i + 1) + '"]').not('[data-id*="_custom_"]').eq(optionListIndex).val());
1208            }
1209        }
1210
1211        // カスタムセレクトの値を取得(バリエーション変更時も加算価格が反映されるようにする)
1212        // 条件再評価を実行(initItemOption内でも条件を再評価)
1213        reEvaluateCustomSelectConditionsUntilStable(false);
1214        
1215        var customSelectList = getCustomSelectListFromDom();
1216
1217        if (ajaxLoop.length === 0) {
1218            return false;
1219        }
1220        
1221        var itemCode = optionData.attr('data-brandcode');
1222        if (!itemCode) {
1223            return false;
1224        }
1225        
1226        for (var ajaxIndex = 0; ajaxIndex < ajaxLoop.length; ajaxIndex++) {
1227	        $.ajax({
1228	            url: '/shop/item_info.html',
1229	            type: 'POST',
1230	            data: JSON.stringify({
1231	            	'element_index': optionListIndex,
1232	                'item_code': itemCode,
1233	                'option_list': optionList,
1234                    'custom_select_list': customSelectList,
1235	                'is_repeat_sale': ajaxLoop[ajaxIndex].is_repeat_sale
1236	            }),
1237	            contentType: 'application/json',
1238	            dataType: 'json'
1239	        }).done(function(data) {
1240	        	var purchaseType = purchaseTypeNormal;
1241	        	if (data.is_repeat_sale) {
1242	        		purchaseType = purchaseTypeSubscription;
1243	        	}
1244
1245	        	updateItemOptionPrice(data.element_index, data.final_price, optionListSetCount, priceIdPrefix, priceIdPrefixCount, priceIdCount, subscriptionPriceIdCount, purchaseType);
1246            	updateItemOptionPrice(data.element_index, data.final_price_excluded_tax, optionListSetCount, priceExcludedTaxIdPrefix, priceExcludedTaxIdPrefixCount, priceExcludedTaxIdCount, subscriptionPriceExcludedTaxIdCount, purchaseType);
1247	        	updateItemOptionPrice(data.element_index, data.final_tax, optionListSetCount, taxIdPrefix, taxIdPrefixCount, taxIdCount, subscriptionTaxIdCount, purchaseType);
1248	        	updateItemOptionPrice(data.element_index, data.final_point, optionListSetCount, pointIdPrefix, pointIdPrefixCount, pointIdCount, subscriptionPointIdCount, purchaseType);
1249
1250                // 数値入力カスタムセレクト用の基準価格を購入種別ごとに保持
1251                // 回数割引あり定期はカスタムセレクト追加金額を反映しない
1252                var isRepeatDiscount = !!data.is_repeat_discount;
1253                optionData.data(OPTION_DATA_KEY_REPEAT_DISCOUNT_PREFIX + purchaseType, isRepeatDiscount ? 1 : 0);
1254                var shouldApplyCustomSelectAdditional = !(data.is_repeat_sale && isRepeatDiscount);
1255                updateBasePriceFromFinalPrice(optionData, data.final_price, purchaseType, shouldApplyCustomSelectAdditional);
1256
1257	            // #8668: Determine whether all variation dropdowns have a valid selection (value !== '0').
1258	            var isOptionSelected = true;
1259	            if (optionListSetCount === 0) {
1260	                isOptionSelected = false;
1261	            } else {
1262	                for (var i = 0; i < optionList.length; i++) {
1263	                    if (optionList[i] === '0' || optionList[i] === 0) {
1264	                        isOptionSelected = false;
1265	                        break;
1266	                    }
1267	                }
1268	            }
1269	            // Update data-option-selected attribute and CSS classes for template use.
1270	            var $variationWrap = $('[data-option-selected]');
1271	            if ($variationWrap.length) {
1272	                $variationWrap.attr('data-option-selected', isOptionSelected ? '1' : '0');
1273	                if (isOptionSelected) {
1274	                    $variationWrap.removeClass('option-not-selected').addClass('option-selected');
1275	                } else {
1276	                    $variationWrap.removeClass('option-selected').addClass('option-not-selected');
1277	                }
1278	            }
1279	            // Update selected SKU stock quantity text element if present.
1280	            var $skuQtyEl = $('[data-id="item-selected-sku-stock-quantity"]');
1281	            if ($skuQtyEl.length) {
1282	                $skuQtyEl.text(data.is_selected_sku_stock_unlimited ? '' : data.selected_sku_stock_quantity);
1283	            }
1284
1285	            // カスタムセレクトの選択内容要約を表示
1286	            var $summaryEl = $('[data-id="item-custom-select-summary"]');
1287	            if ($summaryEl.length) {
1288	                var summaryText = (data.custom_select_summary != null && data.custom_select_summary !== '') ? data.custom_select_summary : '';
1289	                $summaryEl.text(summaryText).toggle(summaryText !== '');
1290	            }
1291
1292	            if (typeof MakeShop_afterItemOptionChange === 'function') {
1293	                var hookData = {
1294	                    elementIndex: data.element_index,
1295	                    originalCode: data.original_code,
1296	                    isSoldout: data.is_soldout,
1297	                    imageL: data.image_l,
1298	                    imageS: data.image_s,
1299	                    // #8668
1300	                    isOptionSelected: isOptionSelected,
1301	                    selectedSkuStockQuantity: data.selected_sku_stock_quantity,
1302	                    isSelectedSkuSoldout: data.is_selected_sku_soldout,
1303	                    isSelectedSkuStockUnlimited: data.is_selected_sku_stock_unlimited,
1304	                    isSelectedSkuSmallStock: data.is_selected_sku_small_stock,
1305	                    method: {
1306	                        modal: openModalMessage
1307	                    }
1308	                };
1309	                if (MakeShop_afterItemOptionChange(hookData) === false) {
1310	                    return;
1311	                }
1312	            }
1313	        });
1314        }
1315        
1316        return false;
1317    }).trigger('change');
1318}
1319
1320function updateItemOptionPrice(index, value, optionListSetCount, idPrefix, idPrefixCount, idCount, subscriptionIdCount, purchaseType)
1321{
1322    if (optionListSetCount === idPrefixCount) {
1323    	$('[data-id^="' + idPrefix + '"]').eq(index).html(value);
1324    } else if (optionListSetCount === idCount || optionListSetCount === subscriptionIdCount) {
1325    	$('[data-id="' + idPrefix + purchaseType + '"]').eq(index).html(value);
1326    } else {
1327    	$('[data-id="' + idPrefix + purchaseType + '"]').eq(0).html(value);
1328    }
1329}
1330
1331function initItemCustomSelect()
1332{
1333    var optionData = $('#makeshop-page-item-option-data');
1334    if (!optionData.length) {
1335        return;
1336    }
1337
1338    function getCustomSelectSyncPayload($wrap, inputType) {
1339        switch (inputType) {
1340            case 0:
1341                return {
1342                    kind: 'choice',
1343                    value: $wrap.find('select.makeshop-custom-select').val() || '0'
1344                };
1345            case 1:
1346                var $selectedButton = $wrap.find('button.makeshop-custom-select-button.selected').first();
1347                return {
1348                    kind: 'choice',
1349                    value: $selectedButton.length ? ($selectedButton.attr('data-value') || '0') : '0'
1350                };
1351            case 2:
1352                var $selectedImageButton = $wrap.find('button.makeshop-custom-select-image-button.selected').first();
1353                return {
1354                    kind: 'choice',
1355                    value: $selectedImageButton.length ? ($selectedImageButton.attr('data-value') || '0') : '0'
1356                };
1357            case 3:
1358                var $checkedRadio = $wrap.find('input.makeshop-custom-select-radio:checked').first();
1359                return {
1360                    kind: 'choice',
1361                    value: $checkedRadio.length ? ($checkedRadio.val() || '0') : '0'
1362                };
1363            case 4:
1364                var $textInput = $wrap.find('input.makeshop-custom-select-text').first();
1365                return {
1366                    kind: 'numeric',
1367                    value: $textInput.length ? ($textInput.val() || '') : ''
1368                };
1369            default:
1370                return null;
1371        }
1372    }
1373
1374    /**
1375     * 同期先のカスタムセレクト要素にペイロードの状態を反映する。
1376     * ※ changeイベントは意図的に発火させない(イベントハンドラ経由で再度syncが呼ばれ無限ループになるため)。
1377     */
1378    function applyCustomSelectSyncPayload($wrap, payload) {
1379        if (!payload) {
1380            return;
1381        }
1382        var inputType = parseInt($wrap.attr('data-input-type'), 10);
1383        if (payload.kind === 'choice') {
1384            var choiceValue = payload.value || '0';
1385            switch (inputType) {
1386                case 0:
1387                    $wrap.find('select.makeshop-custom-select').val(choiceValue);
1388                    break;
1389                case 1:
1390                    var $buttons = $wrap.find('button.makeshop-custom-select-button');
1391                    $buttons.removeClass('selected');
1392                    $buttons.filter('[data-value="' + choiceValue + '"]').addClass('selected');
1393                    break;
1394                case 2:
1395                    var $imageButtons = $wrap.find('button.makeshop-custom-select-image-button');
1396                    $imageButtons.removeClass('selected');
1397                    $imageButtons.filter('[data-value="' + choiceValue + '"]').addClass('selected');
1398                    break;
1399                case 3:
1400                    var $radios = $wrap.find('input.makeshop-custom-select-radio');
1401                    $radios.prop('checked', false);
1402                    $radios.filter('[value="' + choiceValue + '"]').prop('checked', true);
1403                    break;
1404            }
1405            return;
1406        }
1407        if (payload.kind === 'numeric' && inputType === 4) {
1408            var $targetTextInput = $wrap.find('input.makeshop-custom-select-text').first();
1409            if ($targetTextInput.length) {
1410                $targetTextInput.val(payload.value || '');
1411                $targetTextInput.data('prevRawValue', $targetTextInput.val() || '');
1412            }
1413        }
1414    }
1415
1416    function syncDuplicateCustomSelect($sourceWrap, inputType) {
1417        var customSelectId = $sourceWrap.attr('data-custom-select-id');
1418        if (!customSelectId) {
1419            return;
1420        }
1421        var payload = getCustomSelectSyncPayload($sourceWrap, inputType);
1422        if (!payload) {
1423            return;
1424        }
1425        $('.makeshop-custom-select-wrap').each(function() {
1426            var $targetWrap = $(this);
1427            if ($targetWrap.get(0) === $sourceWrap.get(0)) {
1428                return;
1429            }
1430            if ($targetWrap.attr('data-custom-select-id') !== customSelectId) {
1431                return;
1432            }
1433            applyCustomSelectSyncPayload($targetWrap, payload);
1434        });
1435    }
1436
1437    function normalizeRadioGroupNamesForDuplicateCustomSelect() {
1438        var radioWrapGroups = {};
1439        $('.makeshop-custom-select-wrap').each(function() {
1440            var $wrap = $(this);
1441            var inputType = parseInt($wrap.attr('data-input-type'), 10);
1442            if (inputType !== 3) {
1443                return;
1444            }
1445            var customSelectId = $wrap.attr('data-custom-select-id') || '';
1446            var $radios = $wrap.find('input.makeshop-custom-select-radio');
1447            if (!$radios.length) {
1448                return;
1449            }
1450            var originalName = $radios.first().attr('name') || '';
1451            if (!originalName) {
1452                return;
1453            }
1454            var groupKey = customSelectId + '::' + originalName;
1455            if (!radioWrapGroups[groupKey]) {
1456                radioWrapGroups[groupKey] = [];
1457            }
1458            radioWrapGroups[groupKey].push({
1459                wrap: $wrap,
1460                radios: $radios,
1461                originalName: originalName
1462            });
1463        });
1464
1465        $.each(radioWrapGroups, function(groupKey, groupItems) {
1466            if (!groupItems || groupItems.length <= 1) {
1467                return;
1468            }
1469            for (var index = 0; index < groupItems.length; index++) {
1470                var item = groupItems[index];
1471                var uniqueName = item.originalName + '_dup_' + index;
1472                item.radios.attr('name', uniqueName);
1473            }
1474        });
1475    }
1476
1477    normalizeRadioGroupNamesForDuplicateCustomSelect();
1478    
1479    var purchaseTypeNormal = optionData.attr('data-purchase-type-normal');
1480    var purchaseTypeSubscription = optionData.attr('data-purchase-type-subscription');
1481    var priceIdPrefix = optionData.attr('data-id-price-prefix');
1482    var priceExcludedTaxIdPrefix = optionData.attr('data-id-price-excluded-tax-prefix');
1483    var taxIdPrefix = optionData.attr('data-id-tax-prefix');
1484    var pointIdPrefix = optionData.attr('data-id-point-prefix');
1485
1486    // カスタムセレクトの変更イベントを監視
1487    $('.makeshop-custom-select-wrap').each(function() {
1488        var $wrap = $(this);
1489        var inputType = parseInt($wrap.attr('data-input-type'), 10);
1490        
1491        switch (inputType) {
1492            case 0: // dropdown
1493                $wrap.find('select.makeshop-custom-select').on('change', function() {
1494                    syncDuplicateCustomSelect($wrap, inputType);
1495                    updateCustomSelectPrice();
1496                });
1497                break;
1498                
1499            case 1: // button
1500                $wrap.find('button.makeshop-custom-select-button').on('click', function() {
1501                    var $button = $(this);
1502                    $wrap.find('button.makeshop-custom-select-button').removeClass('selected');
1503                    $button.addClass('selected');
1504                    syncDuplicateCustomSelect($wrap, inputType);
1505                    updateCustomSelectPrice();
1506                });
1507                break;
1508            case 2: // image button
1509                $wrap.find('button.makeshop-custom-select-image-button').on('click', function() {
1510                    var $button = $(this);
1511                    $wrap.find('button.makeshop-custom-select-image-button').removeClass('selected');
1512                    $button.addClass('selected');
1513                    syncDuplicateCustomSelect($wrap, inputType);
1514                    updateCustomSelectPrice();
1515                });
1516                break;
1517                
1518            case 3: // radio
1519                $wrap.find('input.makeshop-custom-select-radio').on('change', function() {
1520                    syncDuplicateCustomSelect($wrap, inputType);
1521                    updateCustomSelectPrice();
1522                });
1523                break;
1524           case 4: // text
1525                var $textInput = $wrap.find('input.makeshop-custom-select-text');
1526                $textInput.data('prevRawValue', $textInput.val() || '');
1527                $textInput.on('input change', function(e) {
1528                    // 入力中は商品価格を更新
1529                    reEvaluateCustomSelectConditionsUntilStable(true);
1530                    var $input = $(this);
1531                    var prevRaw = $input.data('prevRawValue') || '';
1532                    var rawVal = $input.val();
1533                    var value = parseFloat(rawVal);
1534                    var minVal = parseFloat($input.attr('min'));
1535                    var stepVal = parseFloat($input.attr('step'));
1536                    if (rawVal !== '' && !isNaN(value)) {
1537                        var originalEvent = e && e.originalEvent ? e.originalEvent : null;
1538                        var isDirectTextInput = e && e.type === 'input' && originalEvent && !!originalEvent.inputType;
1539                        var wasEmpty = prevRaw === '' || prevRaw === null;
1540                        var valueEqualsStep = !isNaN(stepVal) && Math.round(value * 100) === Math.round(stepVal * 100);
1541                        var minDiffersFromStep = isNaN(minVal) || Math.round(minVal * 100) !== Math.round(stepVal * 100);
1542                        var isFirstIncrementFromEmpty =
1543                            !isDirectTextInput &&
1544                            wasEmpty &&
1545                            valueEqualsStep &&
1546                            minDiffersFromStep;
1547                        if (isFirstIncrementFromEmpty) {
1548                            value = minVal;
1549                            $input.val(value);
1550                        }
1551                    } else {
1552                        value = value || 0;
1553                    }
1554                    $input.data('prevRawValue', $input.val());
1555                    syncDuplicateCustomSelect($wrap, inputType);
1556                    var unitPrice = parseFloat($input.attr('data-unit-price')) || 0;
1557                    var unitPriceTaxRate = parseFloat($input.attr('data-unit-price-tax-rate')) || 0;
1558                    var unitPriceTaxType = $input.attr('data-unit-price-tax-type') || 'round';
1559                    var $priceSpan = $wrap.find('.makeshop-custom-select-text-price');
1560                    var $textWrap = $wrap.find('.makeshop-custom-select-text-wrap');
1561                    var prefix = $textWrap.attr('data-price-format-prefix');
1562                    var suffix = $textWrap.attr('data-price-format-suffix');
1563                    var priceValue = getNumericInputPurchasableValue($input);
1564                    if (unitPrice != 0 && prefix && suffix) {
1565                        var totalPrice = formatCustomSelectNumericAddonForDisplay(Math.round(priceValue * unitPrice), unitPriceTaxRate, unitPriceTaxType);
1566                        var sign = (totalPrice >= 0) ? '+' : '';
1567                        var priceText = prefix + sign + numberFormat(totalPrice) + suffix;
1568                        if ($priceSpan.length) {
1569                            $priceSpan.text(priceText);
1570                        } else {
1571                            $wrap.append($('<span class="makeshop-custom-select-text-price"></span>').text(priceText));
1572                        }
1573                    }
1574                    var optionIdPrefix = optionData.attr('data-id-option-prefix');
1575                    var priceIdPrefix = optionData.attr('data-id-price-prefix');
1576                    var optionListSetCount = $('[data-id="' + optionIdPrefix + '1"]').not('[data-id*="_custom_"]').length;
1577                    var priceIdPrefixCount = $('[data-id^="' + priceIdPrefix + '"]').length;
1578                    var priceIdCount = $('[data-id="' + priceIdPrefix + purchaseTypeNormal + '"]').length;
1579                    var subscriptionPriceIdCount = $('[data-id="' + priceIdPrefix + purchaseTypeSubscription + '"]').length;
1580
1581                    var customSelectAdditional = 0;
1582                    $('.makeshop-custom-select-wrap:visible').each(function() {
1583                        if (parseInt($(this).attr('data-input-type'), 10) === 4) {
1584                            var $numInput = $(this).find('input.makeshop-custom-select-text');
1585                            if ($numInput.length) {
1586                                var v = getNumericInputPurchasableValue($numInput);
1587                                var u = parseFloat($numInput.attr('data-unit-price')) || 0;
1588                                var uTaxRate = parseFloat($numInput.attr('data-unit-price-tax-rate')) || 0;
1589                                var uTaxType = $numInput.attr('data-unit-price-tax-type') || 'round';
1590                                customSelectAdditional += applyTaxToExcludedPrice(Math.round(v * u), uTaxRate, uTaxType);
1591                            }
1592                        }
1593                    });
1594
1595                    var purchaseTypesToUpdate = [];
1596                    if (priceIdCount > 0) {
1597                        purchaseTypesToUpdate.push(purchaseTypeNormal);
1598                    }
1599                    if (subscriptionPriceIdCount > 0) {
1600                        purchaseTypesToUpdate.push(purchaseTypeSubscription);
1601                    }
1602                    if (purchaseTypesToUpdate.length === 0) {
1603                        // 通常・定期いずれの価格要素も見つからない場合は更新をスキップ
1604                        return;
1605                    }
1606
1607                    for (var ptIdx = 0; ptIdx < purchaseTypesToUpdate.length; ptIdx++) {
1608                        var targetPurchaseType = purchaseTypesToUpdate[ptIdx];
1609                        var isRepeatDiscount = parseInt(optionData.data(OPTION_DATA_KEY_REPEAT_DISCOUNT_PREFIX + targetPurchaseType), 10) === 1;
1610                        var additionalForTarget = customSelectAdditional; 
1611                        if (targetPurchaseType === purchaseTypeSubscription && isRepeatDiscount) {
1612                            additionalForTarget = 0;
1613                        }
1614                        var basePrice = parseFloat(optionData.data(OPTION_DATA_KEY_BASE_PRICE_PREFIX + targetPurchaseType));
1615                        if (isNaN(basePrice)) {
1616                            basePrice = parseFloat(optionData.data('basePrice'));
1617                        }
1618                        if (isNaN(basePrice)) {
1619                            continue;
1620                        }
1621                        var newPrice = Math.round(basePrice + additionalForTarget);
1622                        if (typeof newPrice !== 'number' || isNaN(newPrice)) {
1623                            newPrice = 0;
1624                        }
1625                        updateItemOptionPrice(0, numberFormat(newPrice), optionListSetCount, priceIdPrefix, priceIdPrefixCount, priceIdCount, subscriptionPriceIdCount, targetPurchaseType);
1626                    }
1627                });
1628                // フォーカスアウト時のみ価格反映のリクエストを送る
1629                $textInput.on('blur', function() {
1630                    updateCustomSelectPrice();
1631                });
1632                break;
1633        }
1634    });
1635    
1636    function updateCustomSelectPrice() {
1637        // 条件再評価を実行
1638        reEvaluateCustomSelectConditionsUntilStable(true);
1639        // カスタムセレクトの数値入力の変更でラベルを手動更新
1640        $('.makeshop-custom-select-wrap:visible').each(function() {
1641            if (parseInt($(this).attr('data-input-type'), 10) === 4) {
1642                var $wrap = $(this);
1643                var $input = $wrap.find('input.makeshop-custom-select-text');
1644                if ($input.length) {
1645                    var value = getNumericInputPurchasableValue($input);
1646                    var unitPrice = parseFloat($input.attr('data-unit-price')) || 0;
1647                    var unitPriceTaxRate = parseFloat($input.attr('data-unit-price-tax-rate')) || 0;
1648                    var unitPriceTaxType = $input.attr('data-unit-price-tax-type') || 'round';
1649                    var $priceSpan = $wrap.find('.makeshop-custom-select-text-price');
1650                    var $textWrap = $wrap.find('.makeshop-custom-select-text-wrap');
1651                    var prefix = $textWrap.attr('data-price-format-prefix');
1652                    var suffix = $textWrap.attr('data-price-format-suffix');
1653                    if (unitPrice != 0 && prefix && suffix) {
1654                        var totalPrice = formatCustomSelectNumericAddonForDisplay(Math.round(value * unitPrice), unitPriceTaxRate, unitPriceTaxType);
1655                        var sign = (totalPrice >= 0) ? '+' : '';
1656                        var priceText = prefix + sign + numberFormat(totalPrice) + suffix;
1657                        if ($priceSpan.length) {
1658                            $priceSpan.text(priceText);
1659                        } else {
1660                            $wrap.append($('<span class="makeshop-custom-select-text-price"></span>').text(priceText));
1661                        }
1662                    }
1663                }
1664            }
1665        });
1666        var optionData = $('#makeshop-page-item-option-data');
1667        if (!optionData.length) {
1668            return;
1669        }
1670        
1671        var purchaseTypeNormal = optionData.attr('data-purchase-type-normal');
1672        var purchaseTypeSubscription = optionData.attr('data-purchase-type-subscription');
1673        var priceIdPrefix = optionData.attr('data-id-price-prefix');
1674        var priceExcludedTaxIdPrefix = optionData.attr('data-id-price-excluded-tax-prefix');
1675        var taxIdPrefix = optionData.attr('data-id-tax-prefix');
1676        var pointIdPrefix = optionData.attr('data-id-point-prefix');
1677        var optionIdPrefix = optionData.attr('data-id-option-prefix');
1678        
1679        if (!purchaseTypeNormal || !purchaseTypeSubscription || !priceIdPrefix || !priceExcludedTaxIdPrefix || !taxIdPrefix || !pointIdPrefix || !optionIdPrefix) {
1680            return;
1681        }
1682        
1683        // 通常オプションの値を取得
1684        var optionList = [];
1685        var optionListSetCount = $('[data-id="' + optionIdPrefix + '1"]').not('[data-id*="_custom_"]').length;
1686        if (optionListSetCount !== 0) {
1687            var optionMax = $('[data-id^="' + optionIdPrefix + '"]').not('[data-id*="_custom_"]').length / optionListSetCount;
1688            for (var i = 0; i < optionMax; i++) {
1689                optionList.push($('[data-id="' + optionIdPrefix + (i + 1) + '"]').not('[data-id*="_custom_"]').eq(0).val() || 0);
1690            }
1691        }
1692        
1693        // カスタムセレクトの値を取得(価格計算用:無効値は0として送りベース価格を返させる)
1694        var customSelectList = getCustomSelectListFromDom({ forPriceCalc: true });
1695
1696        // 価格更新のためのAJAXリクエスト
1697        var priceIdPrefixCount = $('[data-id^="' + priceIdPrefix + '"]').length;
1698        var priceExcludedTaxIdPrefixCount = $('[data-id^="' + priceExcludedTaxIdPrefix + '"]').length;
1699        var taxIdPrefixCount = $('[data-id^="' + taxIdPrefix + '"]').length;
1700        var pointIdPrefixCount = $('[data-id^="' + pointIdPrefix + '"]').length;
1701        
1702        var priceIdCount = $('[data-id="' + priceIdPrefix + purchaseTypeNormal + '"]').length;
1703        var priceExcludedTaxIdCount = $('[data-id="' + priceExcludedTaxIdPrefix + purchaseTypeNormal + '"]').length;
1704        var taxIdCount = $('[data-id="' + taxIdPrefix + purchaseTypeNormal + '"]').length;
1705        var pointIdCount = $('[data-id="' + pointIdPrefix + purchaseTypeNormal + '"]').length;
1706
1707        var subscriptionPriceIdCount = $('[data-id="' + priceIdPrefix + purchaseTypeSubscription + '"]').length;
1708        var subscriptionPriceExcludedTaxIdCount = $('[data-id="' + priceExcludedTaxIdPrefix + purchaseTypeSubscription + '"]').length;
1709        var subscriptionTaxIdCount = $('[data-id="' + taxIdPrefix + purchaseTypeSubscription + '"]').length;
1710        var subscriptionPointIdCount = $('[data-id="' + pointIdPrefix + purchaseTypeSubscription + '"]').length;
1711        
1712        var ajaxLoop = [];
1713        if (optionListSetCount === priceIdPrefixCount) {
1714            var $priceElement = $('[data-id^="' + priceIdPrefix + '"]').eq(0);
1715            var priceDataId = $priceElement.attr('data-id');
1716            if (priceDataId) {
1717                var purchaseType = priceDataId.replace(priceIdPrefix, '');
1718                ajaxLoop.push({ is_repeat_sale: (purchaseType === purchaseTypeSubscription) });
1719            }
1720        } else if (optionListSetCount === priceExcludedTaxIdPrefixCount) {
1721            var $priceElement = $('[data-id^="' + priceExcludedTaxIdPrefix + '"]').eq(0);
1722            var priceDataId = $priceElement.attr('data-id');
1723            if (priceDataId) {
1724                var purchaseType = priceDataId.replace(priceExcludedTaxIdPrefix, '');
1725                ajaxLoop.push({ is_repeat_sale: (purchaseType === purchaseTypeSubscription) });
1726            }
1727        } else {
1728            if (0 < priceIdCount || 0 < priceExcludedTaxIdCount) {
1729                ajaxLoop.push({ is_repeat_sale: false });
1730            }
1731            if (0 < subscriptionPriceIdCount || 0 < subscriptionPriceExcludedTaxIdCount) {
1732                ajaxLoop.push({ is_repeat_sale: true });
1733            }
1734        }
1735        
1736        // カスタムセレクトのみの場合でも価格計算を実行
1737        if (ajaxLoop.length === 0 && customSelectList.length > 0) {
1738            if (0 < priceIdCount || 0 < priceExcludedTaxIdCount) {
1739                ajaxLoop.push({ is_repeat_sale: false });
1740            }
1741            if (0 < subscriptionPriceIdCount || 0 < subscriptionPriceExcludedTaxIdCount) {
1742                ajaxLoop.push({ is_repeat_sale: true });
1743            }
1744        }
1745        
1746        if (ajaxLoop.length === 0) {
1747            return;
1748        }
1749        
1750        var itemCode = optionData.attr('data-brandcode');
1751        if (!itemCode) {
1752            return;
1753        }
1754        
1755        for (var ajaxIndex = 0; ajaxIndex < ajaxLoop.length; ajaxIndex++) {
1756            var requestData = {
1757                'element_index': 0,
1758                'item_code': itemCode,
1759                'option_list': optionList,
1760                'custom_select_list': customSelectList,
1761                'is_repeat_sale': ajaxLoop[ajaxIndex].is_repeat_sale
1762            };
1763            
1764            $.ajax({
1765                url: '/shop/item_info.html',
1766                type: 'POST',
1767                data: JSON.stringify(requestData),
1768                contentType: 'application/json',
1769                dataType: 'json'
1770            }).done(function(data) {
1771                var purchaseType = purchaseTypeNormal;
1772                if (data.is_repeat_sale) {
1773                    purchaseType = purchaseTypeSubscription;
1774                }
1775
1776                updateItemOptionPrice(0, data.final_price, optionListSetCount, priceIdPrefix, priceIdPrefixCount, priceIdCount, subscriptionPriceIdCount, purchaseType);
1777                updateItemOptionPrice(0, data.final_price_excluded_tax, optionListSetCount, priceExcludedTaxIdPrefix, priceExcludedTaxIdPrefixCount, priceExcludedTaxIdCount, subscriptionPriceExcludedTaxIdCount, purchaseType);
1778                updateItemOptionPrice(0, data.final_tax, optionListSetCount, taxIdPrefix, taxIdPrefixCount, taxIdCount, subscriptionTaxIdCount, purchaseType);
1779                updateItemOptionPrice(0, data.final_point, optionListSetCount, pointIdPrefix, pointIdPrefixCount, pointIdCount, subscriptionPointIdCount, purchaseType);
1780
1781                // 数値入力カスタムセレクト用の基準価格を購入種別ごとに保持
1782                // 回数割引あり定期はカスタムセレクト追加金額を反映しない
1783                var isRepeatDiscount = !!data.is_repeat_discount;
1784                optionData.data(OPTION_DATA_KEY_REPEAT_DISCOUNT_PREFIX + purchaseType, isRepeatDiscount ? 1 : 0);
1785                var shouldApplyCustomSelectAdditional = !(data.is_repeat_sale && isRepeatDiscount);
1786                updateBasePriceFromFinalPrice(optionData, data.final_price, purchaseType, shouldApplyCustomSelectAdditional);
1787
1788                // カスタムセレクトの選択内容要約を表示
1789                var $summaryEl = $('[data-id="item-custom-select-summary"]');
1790                if ($summaryEl.length) {
1791                    var summaryText = (data.custom_select_summary != null && data.custom_select_summary !== '') ? data.custom_select_summary : '';
1792                    $summaryEl.text(summaryText).toggle(summaryText !== '');
1793                }
1794            }).fail(function(jqXHR, textStatus, errorThrown) {
1795                console.error('カスタムセレクト価格計算エラー:', textStatus, errorThrown);
1796            });
1797        }
1798    }
1799    
1800    // 数値入力カスタムセレクトがある場合、表示中の価格から算出
1801    $('.makeshop-custom-select-wrap').each(function() {
1802        if (parseInt($(this).attr('data-input-type'), 10) === 4) {
1803            var priceIdPrefix = optionData.attr('data-id-price-prefix');
1804            var $firstPriceEl = $('[data-id="' + priceIdPrefix + purchaseTypeNormal + '"]').eq(0);
1805            var initialPurchaseType = purchaseTypeNormal;
1806            if (!$firstPriceEl.length) {
1807                $firstPriceEl = $('[data-id="' + priceIdPrefix + purchaseTypeSubscription + '"]').eq(0);
1808                initialPurchaseType = purchaseTypeSubscription;
1809            }
1810            if ($firstPriceEl.length) {
1811                var currentPriceText = $firstPriceEl.text().replace(/[^0-9]/g, '');
1812                var currentPrice = parseInt(currentPriceText, 10) || 0;
1813                var customSelectAdditional = 0;
1814                $('.makeshop-custom-select-wrap:visible').each(function() {
1815                    if (parseInt($(this).attr('data-input-type'), 10) === 4) {
1816                        var $numInput = $(this).find('input.makeshop-custom-select-text');
1817                        if ($numInput.length) {
1818                            var v = getNumericInputPurchasableValue($numInput);
1819                            var u = parseFloat($numInput.attr('data-unit-price')) || 0;
1820                            var uTaxRate = parseFloat($numInput.attr('data-unit-price-tax-rate')) || 0;
1821                            var uTaxType = $numInput.attr('data-unit-price-tax-type') || 'round';
1822                            customSelectAdditional += applyTaxToExcludedPrice(Math.round(v * u), uTaxRate, uTaxType);
1823                        }
1824                    }
1825                });
1826                var initialBase = currentPrice - Math.round(customSelectAdditional);
1827                var safeInitialBase = (typeof initialBase === 'number' && !isNaN(initialBase)) ? initialBase : currentPrice;
1828                optionData.data('basePrice', safeInitialBase);
1829                optionData.data(OPTION_DATA_KEY_BASE_PRICE_PREFIX + initialPurchaseType, safeInitialBase);
1830            }
1831            return false;
1832        }
1833    });
1834
1835    // 初期表示時:数値入力の価格表示
1836    $('.makeshop-custom-select-wrap').each(function() {
1837        if (parseInt($(this).attr('data-input-type'), 10) === 4) {
1838            $(this).find('input.makeshop-custom-select-text').trigger('change');
1839        }
1840    });
1841    updateCustomSelectPrice();
1842}
1843
1844function initItemRestock()
1845{
1846    var restockData = $('#makeshop-page-item-restock-data');
1847    var restockUrl = restockData.attr('data-restock-url');
1848    var hasOption = restockData.attr('data-has-option');
1849    var optionIdPrefix = restockData.attr('data-id-option-prefix');
1850    
1851    $('[href="' + restockUrl + '"]').on('click', function() {
1852        window.open('about:blank', 'itemRestockWindow', 'width=448,height=450,scrollbars=yes,resizable=yes');
1853        
1854        var optionString = '';
1855        if (hasOption === 'Y') {
1856            var optionList = [];
1857            for (var i = 1; i <= 2; i++) {
1858                var $option = $('[data-id="' + optionIdPrefix + i + '"]');
1859                if ($option.length > 0) {
1860                    optionList.push($option.eq(0).val());
1861                }
1862            }
1863            if (optionList.length == 1) {
1864                optionList.push(0);
1865            }
1866            optionString = optionList.join('_');
1867        }
1868        
1869        $('#makeshop-form-item-restock [name=restock_option]').val(optionString);
1870        $('#makeshop-form-item-restock').trigger('submit');
1871        return false;
1872    });
1873}
1874
1875function initItemSkuRestock()
1876{
1877    var restockData = $('#makeshop-page-item-restock-data');
1878    var restockUrlPrefix = restockData.attr('data-sku-restock-url-prefix');
1879    
1880    $('[href^="' + restockUrlPrefix + '"]').on('click', function() {
1881        var params = $(this).attr('href').replace(restockUrlPrefix, '').split('-');
1882        var optionString = params[0] + '_' + params[1];
1883        
1884        window.open('about:blank', 'itemRestockWindow', 'width=448,height=450,scrollbars=yes,resizable=yes');
1885        $('#makeshop-form-item-restock [name=restock_option]').val(optionString);
1886        $('#makeshop-form-item-restock').trigger('submit');
1887        return false;
1888    });
1889}
1890
1891function initItemSkuInformation()
1892{
1893    var informationData = $('#makeshop-page-item-information-data');
1894    var informationUrlPrefix = informationData.attr('data-sku-information-url-prefix');
1895    
1896    $('[href^="' + informationUrlPrefix + '"]').on('click', function() {
1897        var source = $(this).attr('href');
1898        var urlIndex = $('[href="' + source + '"]').index(this);
1899        var params = source.replace(informationUrlPrefix, '').split('-');
1900        
1901        getItemInformation({
1902            'source': source,
1903            'element_index': urlIndex,
1904            'item_code': informationData.attr('data-brandcode'),
1905            'option_list': [params[0], params[1]]
1906        });
1907        
1908        return false;
1909    });
1910}
1911
1912function initItemFavorite()
1913{
1914    var favoriteData = $('#makeshop-page-item-favorite-data');
1915    var favoriteEntryUrlPrefix = favoriteData.attr('data-favorite-entry-url-prefix');
1916    var favoriteRemoveUrlPrefix = favoriteData.attr('data-favorite-remove-url-prefix');
1917    
1918    $('[href^="' + favoriteEntryUrlPrefix + '"]').on('click', function() {
1919        var source = $(this).attr('href');
1920        var favoriteEntryUrlIndex = $('[href="' + source + '"]').index(this);
1921        var params = source.replace(favoriteEntryUrlPrefix, '').split('-');
1922
1923        addItemFavorite({
1924            'source': source,
1925            'element_index': favoriteEntryUrlIndex,
1926            'item_code': favoriteData.attr('data-brandcode'),
1927            'option_list': [params[0], params[1]]
1928        });
1929        
1930        return false;
1931    });
1932    
1933    $('[href^="' + favoriteRemoveUrlPrefix + '"]').on('click', function() {
1934        var source = $(this).attr('href');
1935        var favoriteRemoveUrlIndex = $('[href="' + source + '"]').index(this);
1936        var params = source.replace(favoriteRemoveUrlPrefix, '').split('-');
1937
1938        removeItemFavorite({
1939        	'source': source,
1940            'element_index': favoriteRemoveUrlIndex,
1941            'item_code': favoriteData.attr('data-brandcode'),
1942            'option_list': [params[0], params[1]]
1943        });
1944        
1945        return false;
1946    });
1947}
1948
1949function initItemCart()
1950{
1951    var cartData = $('#makeshop-page-item-cart-data');
1952    var cartEntryUrlPrefix = cartData.attr('data-cart-entry-url-prefix');
1953    var purchaseTypeNormal = cartData.attr('data-purchase-type-normal');
1954    var purchaseTypeSubscription = cartData.attr('data-purchase-type-subscription');
1955    var quantityId = cartData.attr('data-id-quantity');
1956    var optionIdPrefix = cartData.attr('data-id-option-prefix');
1957    var namePrintIdPrefix = cartData.attr('data-id-name-print-prefix');
1958    
1959    $('[href^="' + cartEntryUrlPrefix + '"]').on('click', function() {
1960    	var source = $(this).attr('href');
1961        var cartEntryUrlPrefixIndex = $('[href^="' + cartEntryUrlPrefix + '"]').index(this);
1962        var cartEntryUrlIndex = $('[href="' + source + '"]').index(this);
1963
1964        var cartEntryUrlPrefixCount = $('[href^="' + cartEntryUrlPrefix + '"]').length;
1965        var cartEntryUrlCount = $('[href="' + source + '"]').length;
1966        
1967        var quantityIndex = 0;
1968        var quantityCount = $('[data-id="' + quantityId + '"]').length;
1969        if (quantityCount === cartEntryUrlPrefixCount) {
1970            quantityIndex = cartEntryUrlPrefixIndex;
1971        } else if (quantityCount === cartEntryUrlCount) {
1972            quantityIndex = cartEntryUrlIndex;
1973        }
1974        var quantity = getFormValue(quantityId, quantityIndex);
1975        quantity = (quantity === '') ? 1 : quantity;
1976        
1977        var optionListIndex = 0;
1978        var optionListSetCount = $('[data-id="' + optionIdPrefix + '1"]').length;
1979        if (optionListSetCount === cartEntryUrlPrefixCount) {
1980            optionListIndex = cartEntryUrlPrefixIndex;
1981        } else if (optionListSetCount === cartEntryUrlCount) {
1982            optionListIndex = cartEntryUrlIndex;
1983        }
1984        var optionList = [];
1985        if (optionListSetCount !== 0) {
1986            var optionMax = $('[data-id^="' + optionIdPrefix + '"]').length / optionListSetCount;
1987            for (var i = 0; i < optionMax; i++) {
1988                optionList.push($('[data-id="' + optionIdPrefix + (i + 1) + '"]').eq(optionListIndex).val());
1989            }
1990        }
1991
1992        var namePrintListCount = $('[data-id="' + namePrintIdPrefix + '1"]').length;
1993        var namePrintListIndex = getItemElementIndex(namePrintListCount, cartEntryUrlPrefixCount, cartEntryUrlPrefixIndex, cartEntryUrlCount, cartEntryUrlIndex);
1994        var namePrintList = [];
1995        if (namePrintListCount !== 0) {
1996            var namePrintMax = $('[data-id^="' + namePrintIdPrefix + '"]').length / namePrintListCount;
1997            for (var i = 0; i < namePrintMax; i++) {
1998                namePrintList.push($('[data-id="' + namePrintIdPrefix + (i + 1) + '"]').eq(namePrintListIndex).val());
1999            }
2000        }
2001        
2002        var purchaseType = source.replace(cartEntryUrlPrefix, '');
2003        
2004        // カスタムセレクトの値を取得(表示中のカスタムセレクトのみ送る。非表示は条件未達のため含めない)
2005        var customSelectList = getCustomSelectListFromDom();
2006
2007        addItemCart({
2008            'source': source,
2009            'element_index': cartEntryUrlIndex,
2010            'item_code': cartData.attr('data-brandcode'),
2011            'option_list': optionList,
2012            'quantity': quantity,
2013            'is_subscription': (purchaseType === purchaseTypeSubscription),
2014            'name_print': namePrintList,
2015            'custom_select_list': customSelectList,
2016            'end_url': cartData.attr('data-cart-url')
2017        });
2018
2019        return false;
2020    });
2021}
2022
2023function initItemSkuCart()
2024{
2025    var cartData = $('#makeshop-page-item-cart-data');
2026    var cartEntryUrlPrefix = cartData.attr('data-sku-cart-entry-url-prefix');
2027    var purchaseTypeNormal = cartData.attr('data-purchase-type-normal');
2028    var purchaseTypeSubscription = cartData.attr('data-purchase-type-subscription');
2029    var quantityId = cartData.attr('data-id-quantity');
2030    var namePrintIdPrefix = cartData.attr('data-id-name-print-prefix');
2031
2032    $('[href^="' + cartEntryUrlPrefix + '"]').on('click', function() {
2033    	var source = $(this).attr('href');
2034        var cartParams = source.replace(cartEntryUrlPrefix, '').split('-');
2035        var purchaseType = cartParams[0];
2036        var optionList = [];
2037        optionList.push(cartParams[1]);
2038        optionList.push(cartParams[2]);
2039        
2040        var cartEntryUrlIndex = $('[href="' + source + '"]').index(this);
2041        var cartEntryUrlCount = $('[href="' + source + '"]').length;
2042        
2043        var cartEntryUrlMatchIndex = $('[href^="' + cartEntryUrlPrefix + '"][href$="-' + cartParams[1] + '-' + cartParams[2] + '"]').index(this);
2044        var cartEntryUrlMatchCount = $('[href^="' + cartEntryUrlPrefix + '"][href$="-' + cartParams[1] + '-' + cartParams[2] + '"]').length;;
2045        
2046        var quantityIndex = 0;
2047        var quantityCount = $('[data-id="' + quantityId + '"]').length;
2048        if (quantityCount === cartEntryUrlCount) {
2049            quantityIndex = cartEntryUrlIndex;
2050        } else if (quantityCount === cartEntryUrlMatchCount) {
2051            quantityIndex = cartEntryUrlMatchIndex;
2052        }
2053        var quantity = getFormValue(quantityId, quantityIndex);
2054        quantity = (quantity === '') ? 1 : quantity;
2055        
2056        var namePrintListCount = $('[data-id="' + namePrintIdPrefix + '1"]').length;
2057        var namePrintListIndex = getItemElementIndex(namePrintListCount, cartEntryUrlCount, cartEntryUrlIndex, cartEntryUrlMatchCount, cartEntryUrlMatchIndex);
2058        var namePrintList = [];
2059        if (namePrintListCount !== 0) {
2060            var namePrintMax = $('[data-id^="' + namePrintIdPrefix + '"]').length / namePrintListCount;
2061            for (var i = 0; i < namePrintMax; i++) {
2062                namePrintList.push($('[data-id="' + namePrintIdPrefix + (i + 1) + '"]').eq(namePrintListIndex).val());
2063            }
2064        }
2065        
2066        // カスタムセレクトの値を取得(表示中のカスタムセレクトのみ送る。非表示は条件未達のため含めない)
2067        var customSelectList = getCustomSelectListFromDom();
2068
2069        addItemCart({
2070            'source': source,
2071            'element_index': cartEntryUrlIndex,
2072            'item_code': cartData.attr('data-brandcode'),
2073            'option_list': optionList,
2074            'quantity': quantity,
2075            'is_subscription': (purchaseType === purchaseTypeSubscription),
2076            'name_print': namePrintList,
2077            'custom_select_list': customSelectList,
2078            'end_url': cartData.attr('data-cart-url')
2079        });
2080
2081        return false;
2082    });
2083}
2084
2085function getItemElementIndex(count, cartEntryUrlPrefixCount, cartEntryUrlPrefixIndex, cartEntryUrlCount, cartEntryUrlIndex)
2086{
2087    var result = 0;
2088    
2089    if (count === cartEntryUrlPrefixCount) {
2090        result = cartEntryUrlPrefixIndex;
2091    } else if (count === cartEntryUrlCount) {
2092        result = cartEntryUrlIndex;
2093    }
2094    
2095    return result;
2096}
2097
2098function addItemCart(params)
2099{
2100    $.ajax({
2101        url: '/api/cart/',
2102        type: 'POST',
2103        data: JSON.stringify({
2104            'action': 'add',
2105            'source': params.source,
2106            'element_index': params.element_index,
2107            'item_code': params.item_code,
2108            'option_list': params.option_list,
2109            'quantity': params.quantity,
2110            'is_subscription': params.is_subscription,
2111            'name_print' : params.name_print,
2112            'custom_select_list': params.custom_select_list || []
2113        }),
2114        contentType: 'application/json',
2115        dataType: 'json'
2116    }).done(function(data) {
2117        if (data.result && data.conversion_list) {
2118            insertConversionHtml(data.conversion_list);
2119        }
2120        if (data.result && data.ga4.event) {
2121            makeshop_ga_gtag('event', data.ga4.event, data.ga4.parameters);
2122        }
2123
2124        if (typeof MakeShop_afterCartEntry === 'function') {
2125            var hookData = {
2126                result: data.result,
2127                source: data.source,
2128                elementIndex: data.element_index,
2129                systemCode: data.item_code,
2130                totalQuantity: data.total_quantity,
2131                error: data.error,
2132                method: {
2133                    modal: openModalMessage
2134                }
2135            };
2136            if (MakeShop_afterCartEntry(hookData) === false) {
2137                return;
2138            }
2139        }
2140        
2141        if (!data.result) {
2142            openModalMessage(data.error.message);
2143            return;
2144        }
2145
2146        location.href = params.end_url;
2147    });
2148}
2149
2150function getItemInformation(params)
2151{
2152    $.ajax({
2153        url: '/shop/item_info.html',
2154        type: 'POST',
2155        data: JSON.stringify({
2156            'source': params.source,
2157            'element_index': params.element_index,
2158            'item_code': params.item_code,
2159            'option_list': params.option_list
2160        }),
2161        contentType: 'application/json',
2162        dataType: 'json'
2163    }).done(function(data) {
2164        if (typeof MakeShop_afterItemOptionInformation === 'function') {
2165        	const optionRelatedImageList = [];
2166        	for (const image of data.option_related_image_list) {
2167        		optionRelatedImageList.push({ imageL: image.image_l, imageS: 
2167image.image_s });
2168        	}
2169            var hookData = {
2170                elementIndex: data.element_index,
2171                isSoldout: data.is_soldout,
2172                imageL: data.image_l,
2173                imageS: data.image_s,
2174                optionRelatedImageList: optionRelatedImageList,
2175                method: {
2176                    modal: openModalMessage
2177                }
2178            };
2179            if (MakeShop_afterItemOptionInformation(hookData) === false) {
2180                return;
2181            }
2182        }
2183    });
2184}
2185
2186function addItemFavorite(params)
2187{
2188    $.ajax({
2189        url: '/api/favorite/',
2190        type: 'POST',
2191        data: JSON.stringify({
2192            'action': 'add',
2193            'source': params.source,
2194            'element_index': params.element_index,
2195            'item_code': params.item_code,
2196            'option_list': params.option_list
2197        }),
2198        contentType: 'application/json',
2199        dataType: 'json'
2200    }).done(function(data) {
2201        if (typeof MakeShop_afterFavoriteEntry === 'function') {
2202            var hookData = {
2203                result: data.result,
2204                source: data.source,
2205                elementIndex: data.element_index,
2206                systemCode: data.item_code,
2207                error: data.error,
2208                method: {
2209                    modal: openModalMessage
2210                }
2211            };
2212            if (MakeShop_afterFavoriteEntry(hookData) === false) {
2213                return;
2214            }
2215        }
2216        
2217        if (!data.result) {
2218            openModalMessage(data.error.message);
2219            return;
2220        }
2221
2222        openModalMessage(data.message);
2223    });
2224}
2225
2226function removeItemFavorite(params)
2227{
2228    $.ajax({
2229        url: '/api/favorite/',
2230        type: 'POST',
2231        data: JSON.stringify({
2232            'action': 'remove',
2233            'source': params.source,
2234            'element_index': params.element_index,
2235            'item_code': params.item_code,
2236            'option1': params.option_list[0],
2237            'option2': params.option_list[1]
2238        }),
2239        contentType: 'application/json',
2240        dataType: 'json'
2241    }).done(function(data) {
2242        if (typeof MakeShop_afterFavoriteRemove === 'function') {
2243            var hookData = {
2244                result: data.result,
2245                source: data.source,
2246                elementIndex: data.element_index,
2247                systemCode: data.item_code,
2248                error: data.error,
2249                method: {
2250                    modal: openModalMessage
2251                }
2252            };
2253            if (MakeShop_afterFavoriteRemove(hookData) === false) {
2254                return;
2255            }
2256        }
2257        
2258        if (!data.result) {
2259            openModalMessage(data.error.message);
2260            return;
2261        }
2262
2263        openModalMessage(data.message);
2264    });
2265}
2266
2267function initItemOptionImage()
2268{
2269    var optionData = $('#makeshop-page-item-option-data');
2270    if (!optionData.length) return;
2271
2272    var optionIdPrefix = optionData.attr('data-id-option-prefix');
2273    var cartData = $('#makeshop-page-item-cart-data');
2274    var cartEntryUrlPrefix = cartData.attr('data-sku-cart-entry-url-prefix');
2275
2276    $('.option-image-option2-item').on('click', function(e) {
2277        if ($(e.target).closest('a').length) return;
2278
2279        var $cartLink = $(this).find('a[href^="' + cartEntryUrlPrefix + '"]').first();
2280        if (!$cartLink.length) return;
2281
2282        var parts = $cartLink.attr('href').replace(cartEntryUrlPrefix, '').split('-');
2283        var spcode1 = parts[1] || '0';
2284        var spcode2 = parts[2] || '0';
2285
2286        var $selects = $('[data-id^="' + optionIdPrefix + '"]').not('[data-id*="_custom_"]');
2287        $selects.eq(0).val(spcode1);
2288        if ($selects.length >= 2) {
2289            $selects.eq(1).val(spcode2);
2290        }
2291
2292        if ($selects.length) {
2293            $selects.first()[0].dispatchEvent(new Event('change', { bubbles: true }));
2294        }
2295    });
2296}
2297
2298function initItemContact()
2299{
2300    var contactData = $('#makeshop-page-item-contact-data');
2301    var contactUrl = contactData.attr('data-contact-url');
2302
2303    $('[href="' + contactUrl + '"]').on('click', function() {
2304        $('#makeshop-form-item-contact').trigger('submit');
2305        return false;
2306    });
2307}
2308
2309})($jQueryMakeShop);
2310$jQueryMakeShop = null;

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.