PageSourceSearch

https://shop.lovecosmetic.jp/view/asset/system-1-1-3-892e5bc4bb86c7d95d2966bace73e78e.js

js lovecosmetic.jp collected 2026-09-24 18:34:03 UTC 98,922 bytes, 2,223 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});
1058
1059function initItemOption()
1060{
1061    var optionData = $('#makeshop-page-item-option-data');
1062    var purchaseTypeNormal = optionData.attr('data-purchase-type-normal');
1063    var purchaseTypeSubscription = optionData.attr('data-purchase-type-subscription');
1064    var optionIdPrefix = optionData.attr('data-id-option-prefix');
1065    var priceIdPrefix = optionData.attr('data-id-price-prefix');
1066    var priceExcludedTaxIdPrefix = optionData.attr('data-id-price-excluded-tax-prefix');
1067    var taxIdPrefix = optionData.attr('data-id-tax-prefix');
1068    var pointIdPrefix = optionData.attr('data-id-point-prefix');
1069
1070    $('[data-id^="' + optionIdPrefix + '"]').not('[data-id*="_custom_"]').on('change', function() {
1071    	var $this = $(this);
1072    	var dataId = $this.attr('data-id');
1073    	if (!dataId || dataId.indexOf('_custom_') !== -1) {
1074    	    return false;
1075    	}
1076    	var optionListIndex = $('[data-id="' + dataId + '"]').not('[data-id*="_custom_"]').index(this);
1077        var optionListSetCount = $('[data-id="' + optionIdPrefix + '1"]').not('[data-id*="_custom_"]').length;
1078
1079        var priceIdPrefixCount = $('[data-id^="' + priceIdPrefix + '"]').length;
1080        var priceExcludedTaxIdPrefixCount = $('[data-id^="' + priceExcludedTaxIdPrefix + '"]').length;
1081        var taxIdPrefixCount = $('[data-id^="' + taxIdPrefix + '"]').length;
1082        var pointIdPrefixCount = $('[data-id^="' + pointIdPrefix + '"]').length;
1083        
1084        var priceIdCount = $('[data-id="' + priceIdPrefix + purchaseTypeNormal + '"]').length;
1085        var priceExcludedTaxIdCount = $('[data-id="' + priceExcludedTaxIdPrefix + purchaseTypeNormal + '"]').length;
1086        var taxIdCount = $('[data-id="' + taxIdPrefix + purchaseTypeNormal + '"]').length;
1087        var pointIdCount = $('[data-id="' + pointIdPrefix + purchaseTypeNormal + '"]').length;
1088
1089        var subscriptionPriceIdCount = $('[data-id="' + priceIdPrefix + purchaseTypeSubscription + '"]').length;
1090        var subscriptionPriceExcludedTaxIdCount = $('[data-id="' + priceExcludedTaxIdPrefix + purchaseTypeSubscription + '"]').length;
1091        var subscriptionTaxIdCount = $('[data-id="' + taxIdPrefix + purchaseTypeSubscription + '"]').length;
1092        var subscriptionPointIdCount = $('[data-id="' + pointIdPrefix + purchaseTypeSubscription + '"]').length;
1093        
1094        var ajaxLoop = [];
1095        if (optionListSetCount === priceIdPrefixCount) {
1096        	var $priceElement = $('[data-id^="' + priceIdPrefix + '"]').eq(optionListIndex);
1097        	if ($priceElement.length && $priceElement.attr('data-id')) {
1098        		var purchaseType = $priceElement.attr('data-id').replace(priceIdPrefix, '');
1099        		ajaxLoop.push({ is_repeat_sale: (purchaseType === purchaseTypeSubscription) });
1100        	}
1101        } else if (optionListSetCount === priceExcludedTaxIdPrefixCount) {
1102        	var $priceElement = $('[data-id^="' + priceExcludedTaxIdPrefix + '"]').eq(optionListIndex);
1103        	if ($priceElement.length && $priceElement.attr('data-id')) {
1104        		var purchaseType = $priceElement.attr('data-id').replace(priceExcludedTaxIdPrefix, '');
1105        		ajaxLoop.push({ is_repeat_sale: (purchaseType === purchaseTypeSubscription) });
1106        	}
1107        } else {
1108        	if (0 < priceIdCount || 0 < priceExcludedTaxIdCount) {
1109        	    ajaxLoop.push({ is_repeat_sale: false });
1110        	}
1111        	if (0 < subscriptionPriceIdCount || 0 < subscriptionPriceExcludedTaxIdCount) {
1112        	    ajaxLoop.push({ is_repeat_sale: true });
1113        	}
1114        }
1115
1116        var optionList = [];
1117        if (optionListSetCount !== 0) {
1118            var optionMax = $('[data-id^="' + optionIdPrefix + '"]').not('[data-id*="_custom_"]').length / optionListSetCount;
1119            for (var i = 0; i < optionMax; i++) {
1120                optionList.push($('[data-id="' + optionIdPrefix + (i + 1) + '"]').not('[data-id*="_custom_"]').eq(optionListIndex).val());
1121            }
1122        }
1123
1124        // カスタムセレクトの値を取得(バリエーション変更時も加算価格が反映されるようにする)
1125        // 条件再評価を実行(initItemOption内でも条件を再評価)
1126        reEvaluateCustomSelectConditionsUntilStable(false);
1127        
1128        var customSelectList = getCustomSelectListFromDom();
1129
1130        if (ajaxLoop.length === 0) {
1131            return false;
1132        }
1133        
1134        var itemCode = optionData.attr('data-brandcode');
1135        if (!itemCode) {
1136            return false;
1137        }
1138        
1139        for (var ajaxIndex = 0; ajaxIndex < ajaxLoop.length; ajaxIndex++) {
1140	        $.ajax({
1141	            url: '/shop/item_info.html',
1142	            type: 'POST',
1143	            data: JSON.stringify({
1144	            	'element_index': optionListIndex,
1145	                'item_code': itemCode,
1146	                'option_list': optionList,
1147                    'custom_select_list': customSelectList,
1148	                'is_repeat_sale': ajaxLoop[ajaxIndex].is_repeat_sale
1149	            }),
1150	            contentType: 'application/json',
1151	            dataType: 'json'
1152	        }).done(function(data) {
1153	        	var purchaseType = purchaseTypeNormal;
1154	        	if (data.is_repeat_sale) {
1155	        		purchaseType = purchaseTypeSubscription;
1156	        	}
1157
1158	        	updateItemOptionPrice(data.element_index, data.final_price, optionListSetCount, priceIdPrefix, priceIdPrefixCount, priceIdCount, subscriptionPriceIdCount, purchaseType);
1159            	updateItemOptionPrice(data.element_index, data.final_price_excluded_tax, optionListSetCount, priceExcludedTaxIdPrefix, priceExcludedTaxIdPrefixCount, priceExcludedTaxIdCount, subscriptionPriceExcludedTaxIdCount, purchaseType);
1160	        	updateItemOptionPrice(data.element_index, data.final_tax, optionListSetCount, taxIdPrefix, taxIdPrefixCount, taxIdCount, subscriptionTaxIdCount, purchaseType);
1161	        	updateItemOptionPrice(data.element_index, data.final_point, optionListSetCount, pointIdPrefix, pointIdPrefixCount, pointIdCount, subscriptionPointIdCount, purchaseType);
1162
1163                // 数値入力カスタムセレクト用の基準価格を購入種別ごとに保持
1164                // 回数割引あり定期はカスタムセレクト追加金額を反映しない
1165                var isRepeatDiscount = !!data.is_repeat_discount;
1166                optionData.data(OPTION_DATA_KEY_REPEAT_DISCOUNT_PREFIX + purchaseType, isRepeatDiscount ? 1 : 0);
1167                var shouldApplyCustomSelectAdditional = !(data.is_repeat_sale && isRepeatDiscount);
1168                updateBasePriceFromFinalPrice(optionData, data.final_price, purchaseType, shouldApplyCustomSelectAdditional);
1169
1170	            // #8668: Determine whether all variation dropdowns have a valid selection (value !== '0').
1171	            var isOptionSelected = true;
1172	            if (optionListSetCount === 0) {
1173	                isOptionSelected = false;
1174	            } else {
1175	                for (var i = 0; i < optionList.length; i++) {
1176	                    if (optionList[i] === '0' || optionList[i] === 0) {
1177	                        isOptionSelected = false;
1178	                        break;
1179	                    }
1180	                }
1181	            }
1182	            // Update data-option-selected attribute and CSS classes for template use.
1183	            var $variationWrap = $('[data-option-selected]');
1184	            if ($variationWrap.length) {
1185	                $variationWrap.attr('data-option-selected', isOptionSelected ? '1' : '0');
1186	                if (isOptionSelected) {
1187	                    $variationWrap.removeClass('option-not-selected').addClass('option-selected');
1188	                } else {
1189	                    $variationWrap.removeClass('option-selected').addClass('option-not-selected');
1190	                }
1191	            }
1192	            // Update selected SKU stock quantity text element if present.
1193	            var $skuQtyEl = $('[data-id="item-selected-sku-stock-quantity"]');
1194	            if ($skuQtyEl.length) {
1195	                $skuQtyEl.text(data.is_selected_sku_stock_unlimited ? '' : data.selected_sku_stock_quantity);
1196	            }
1197
1198	            // カスタムセレクトの選択内容要約を表示
1199	            var $summaryEl = $('[data-id="item-custom-select-summary"]');
1200	            if ($summaryEl.length) {
1201	                var summaryText = (data.custom_select_summary != null && data.custom_select_summary !== '') ? data.custom_select_summary : '';
1202	                $summaryEl.text(summaryText).toggle(summaryText !== '');
1203	            }
1204
1205	            if (typeof MakeShop_afterItemOptionChange === 'function') {
1206	                var hookData = {
1207	                    elementIndex: data.element_index,
1208	                    originalCode: data.original_code,
1209	                    isSoldout: data.is_soldout,
1210	                    imageL: data.image_l,
1211	                    imageS: data.image_s,
1212	                    // #8668
1213	                    isOptionSelected: isOptionSelected,
1214	                    selectedSkuStockQuantity: data.selected_sku_stock_quantity,
1215	                    isSelectedSkuSoldout: data.is_selected_sku_soldout,
1216	                    isSelectedSkuStockUnlimited: data.is_selected_sku_stock_unlimited,
1217	                    isSelectedSkuSmallStock: data.is_selected_sku_small_stock,
1218	                    method: {
1219	                        modal: openModalMessage
1220	                    }
1221	                };
1222	                if (MakeShop_afterItemOptionChange(hookData) === false) {
1223	                    return;
1224	                }
1225	            }
1226	        });
1227        }
1228        
1229        return false;
1230    }).trigger('change');
1231}
1232
1233function updateItemOptionPrice(index, value, optionListSetCount, idPrefix, idPrefixCount, idCount, subscriptionIdCount, purchaseType)
1234{
1235    if (optionListSetCount === idPrefixCount) {
1236    	$('[data-id^="' + idPrefix + '"]').eq(index).html(value);
1237    } else if (optionListSetCount === idCount || optionListSetCount === subscriptionIdCount) {
1238    	$('[data-id="' + idPrefix + purchaseType + '"]').eq(index).html(value);
1239    } else {
1240    	$('[data-id="' + idPrefix + purchaseType + '"]').eq(0).html(value);
1241    }
1242}
1243
1244function initItemCustomSelect()
1245{
1246    var optionData = $('#makeshop-page-item-option-data');
1247    if (!optionData.length) {
1248        return;
1249    }
1250
1251    function getCustomSelectSyncPayload($wrap, inputType) {
1252        switch (inputType) {
1253            case 0:
1254                return {
1255                    kind: 'choice',
1256                    value: $wrap.find('select.makeshop-custom-select').val() || '0'
1257                };
1258            case 1:
1259                var $selectedButton = $wrap.find('button.makeshop-custom-select-button.selected').first();
1260                return {
1261                    kind: 'choice',
1262                    value: $selectedButton.length ? ($selectedButton.attr('data-value') || '0') : '0'
1263                };
1264            case 2:
1265                var $selectedImageButton = $wrap.find('button.makeshop-custom-select-image-button.selected').first();
1266                return {
1267                    kind: 'choice',
1268                    value: $selectedImageButton.length ? ($selectedImageButton.attr('data-value') || '0') : '0'
1269                };
1270            case 3:
1271                var $checkedRadio = $wrap.find('input.makeshop-custom-select-radio:checked').first();
1272                return {
1273                    kind: 'choice',
1274                    value: $checkedRadio.length ? ($checkedRadio.val() || '0') : '0'
1275                };
1276            case 4:
1277                var $textInput = $wrap.find('input.makeshop-custom-select-text').first();
1278                return {
1279                    kind: 'numeric',
1280                    value: $textInput.length ? ($textInput.val() || '') : ''
1281                };
1282            default:
1283                return null;
1284        }
1285    }
1286
1287    /**
1288     * 同期先のカスタムセレクト要素にペイロードの状態を反映する。
1289     * ※ changeイベントは意図的に発火させない(イベントハンドラ経由で再度syncが呼ばれ無限ループになるため)。
1290     */
1291    function applyCustomSelectSyncPayload($wrap, payload) {
1292        if (!payload) {
1293            return;
1294        }
1295        var inputType = parseInt($wrap.attr('data-input-type'), 10);
1296        if (payload.kind === 'choice') {
1297            var choiceValue = payload.value || '0';
1298            switch (inputType) {
1299                case 0:
1300                    $wrap.find('select.makeshop-custom-select').val(choiceValue);
1301                    break;
1302                case 1:
1303                    var $buttons = $wrap.find('button.makeshop-custom-select-button');
1304                    $buttons.removeClass('selected');
1305                    $buttons.filter('[data-value="' + choiceValue + '"]').addClass('selected');
1306                    break;
1307                case 2:
1308                    var $imageButtons = $wrap.find('button.makeshop-custom-select-image-button');
1309                    $imageButtons.removeClass('selected');
1310                    $imageButtons.filter('[data-value="' + choiceValue + '"]').addClass('selected');
1311                    break;
1312                case 3:
1313                    var $radios = $wrap.find('input.makeshop-custom-select-radio');
1314                    $radios.prop('checked', false);
1315                    $radios.filter('[value="' + choiceValue + '"]').prop('checked', true);
1316                    break;
1317            }
1318            return;
1319        }
1320        if (payload.kind === 'numeric' && inputType === 4) {
1321            var $targetTextInput = $wrap.find('input.makeshop-custom-select-text').first();
1322            if ($targetTextInput.length) {
1323                $targetTextInput.val(payload.value || '');
1324                $targetTextInput.data('prevRawValue', $targetTextInput.val() || '');
1325            }
1326        }
1327    }
1328
1329    function syncDuplicateCustomSelect($sourceWrap, inputType) {
1330        var customSelectId = $sourceWrap.attr('data-custom-select-id');
1331        if (!customSelectId) {
1332            return;
1333        }
1334        var payload = getCustomSelectSyncPayload($sourceWrap, inputType);
1335        if (!payload) {
1336            return;
1337        }
1338        $('.makeshop-custom-select-wrap').each(function() {
1339            var $targetWrap = $(this);
1340            if ($targetWrap.get(0) === $sourceWrap.get(0)) {
1341                return;
1342            }
1343            if ($targetWrap.attr('data-custom-select-id') !== customSelectId) {
1344                return;
1345            }
1346            applyCustomSelectSyncPayload($targetWrap, payload);
1347        });
1348    }
1349
1350    function normalizeRadioGroupNamesForDuplicateCustomSelect() {
1351        var radioWrapGroups = {};
1352        $('.makeshop-custom-select-wrap').each(function() {
1353            var $wrap = $(this);
1354            var inputType = parseInt($wrap.attr('data-input-type'), 10);
1355            if (inputType !== 3) {
1356                return;
1357            }
1358            var customSelectId = $wrap.attr('data-custom-select-id') || '';
1359            var $radios = $wrap.find('input.makeshop-custom-select-radio');
1360            if (!$radios.length) {
1361                return;
1362            }
1363            var originalName = $radios.first().attr('name') || '';
1364            if (!originalName) {
1365                return;
1366            }
1367            var groupKey = customSelectId + '::' + originalName;
1368            if (!radioWrapGroups[groupKey]) {
1369                radioWrapGroups[groupKey] = [];
1370            }
1371            radioWrapGroups[groupKey].push({
1372                wrap: $wrap,
1373                radios: $radios,
1374                originalName: originalName
1375            });
1376        });
1377
1378        $.each(radioWrapGroups, function(groupKey, groupItems) {
1379            if (!groupItems || groupItems.length <= 1) {
1380                return;
1381            }
1382            for (var index = 0; index < groupItems.length; index++) {
1383                var item = groupItems[index];
1384                var uniqueName = item.originalName + '_dup_' + index;
1385                item.radios.attr('name', uniqueName);
1386            }
1387        });
1388    }
1389
1390    normalizeRadioGroupNamesForDuplicateCustomSelect();
1391    
1392    var purchaseTypeNormal = optionData.attr('data-purchase-type-normal');
1393    var purchaseTypeSubscription = optionData.attr('data-purchase-type-subscription');
1394    var priceIdPrefix = optionData.attr('data-id-price-prefix');
1395    var priceExcludedTaxIdPrefix = optionData.attr('data-id-price-excluded-tax-prefix');
1396    var taxIdPrefix = optionData.attr('data-id-tax-prefix');
1397    var pointIdPrefix = optionData.attr('data-id-point-prefix');
1398
1399    // カスタムセレクトの変更イベントを監視
1400    $('.makeshop-custom-select-wrap').each(function() {
1401        var $wrap = $(this);
1402        var inputType = parseInt($wrap.attr('data-input-type'), 10);
1403        
1404        switch (inputType) {
1405            case 0: // dropdown
1406                $wrap.find('select.makeshop-custom-select').on('change', function() {
1407                    syncDuplicateCustomSelect($wrap, inputType);
1408                    updateCustomSelectPrice();
1409                });
1410                break;
1411                
1412            case 1: // button
1413                $wrap.find('button.makeshop-custom-select-button').on('click', function() {
1414                    var $button = $(this);
1415                    $wrap.find('button.makeshop-custom-select-button').removeClass('selected');
1416                    $button.addClass('selected');
1417                    syncDuplicateCustomSelect($wrap, inputType);
1418                    updateCustomSelectPrice();
1419                });
1420                break;
1421            case 2: // image button
1422                $wrap.find('button.makeshop-custom-select-image-button').on('click', function() {
1423                    var $button = $(this);
1424                    $wrap.find('button.makeshop-custom-select-image-button').removeClass('selected');
1425                    $button.addClass('selected');
1426                    syncDuplicateCustomSelect($wrap, inputType);
1427                    updateCustomSelectPrice();
1428                });
1429                break;
1430                
1431            case 3: // radio
1432                $wrap.find('input.makeshop-custom-select-radio').on('change', function() {
1433                    syncDuplicateCustomSelect($wrap, inputType);
1434                    updateCustomSelectPrice();
1435                });
1436                break;
1437           case 4: // text
1438                var $textInput = $wrap.find('input.makeshop-custom-select-text');
1439                $textInput.data('prevRawValue', $textInput.val() || '');
1440                $textInput.on('input change', function(e) {
1441                    // 入力中は商品価格を更新
1442                    reEvaluateCustomSelectConditionsUntilStable(true);
1443                    var $input = $(this);
1444                    var prevRaw = $input.data('prevRawValue') || '';
1445                    var rawVal = $input.val();
1446                    var value = parseFloat(rawVal);
1447                    var minVal = parseFloat($input.attr('min'));
1448                    var stepVal = parseFloat($input.attr('step'));
1449                    if (rawVal !== '' && !isNaN(value)) {
1450                        var originalEvent = e && e.originalEvent ? e.originalEvent : null;
1451                        var isDirectTextInput = e && e.type === 'input' && originalEvent && !!originalEvent.inputType;
1452                        var wasEmpty = prevRaw === '' || prevRaw === null;
1453                        var valueEqualsStep = !isNaN(stepVal) && Math.round(value * 100) === Math.round(stepVal * 100);
1454                        var minDiffersFromStep = isNaN(minVal) || Math.round(minVal * 100) !== Math.round(stepVal * 100);
1455                        var isFirstIncrementFromEmpty =
1456                            !isDirectTextInput &&
1457                            wasEmpty &&
1458                            valueEqualsStep &&
1459                            minDiffersFromStep;
1460                        if (isFirstIncrementFromEmpty) {
1461                            value = minVal;
1462                            $input.val(value);
1463                        }
1464                    } else {
1465                        value = value || 0;
1466                    }
1467                    $input.data('prevRawValue', $input.val());
1468                    syncDuplicateCustomSelect($wrap, inputType);
1469                    var unitPrice = parseFloat($input.attr('data-unit-price')) || 0;
1470                    var unitPriceTaxRate = parseFloat($input.attr('data-unit-price-tax-rate')) || 0;
1471                    var unitPriceTaxType = $input.attr('data-unit-price-tax-type') || 'round';
1472                    var $priceSpan = $wrap.find('.makeshop-custom-select-text-price');
1473                    var $textWrap = $wrap.find('.makeshop-custom-select-text-wrap');
1474                    var prefix = $textWrap.attr('data-price-format-prefix');
1475                    var suffix = $textWrap.attr('data-price-format-suffix');
1476                    var priceValue = getNumericInputPurchasableValue($input);
1477                    if (unitPrice != 0 && prefix && suffix) {
1478                        var totalPrice = formatCustomSelectNumericAddonForDisplay(Math.round(priceValue * unitPrice), unitPriceTaxRate, unitPriceTaxType);
1479                        var sign = (totalPrice >= 0) ? '+' : '';
1480                        var priceText = prefix + sign + numberFormat(totalPrice) + suffix;
1481                        if ($priceSpan.length) {
1482                            $priceSpan.text(priceText);
1483                        } else {
1484                            $wrap.append($('<span class="makeshop-custom-select-text-price"></span>').text(priceText));
1485                        }
1486                    }
1487                    var optionIdPrefix = optionData.attr('data-id-option-prefix');
1488                    var priceIdPrefix = optionData.attr('data-id-price-prefix');
1489                    var optionListSetCount = $('[data-id="' + optionIdPrefix + '1"]').not('[data-id*="_custom_"]').length;
1490                    var priceIdPrefixCount = $('[data-id^="' + priceIdPrefix + '"]').length;
1491                    var priceIdCount = $('[data-id="' + priceIdPrefix + purchaseTypeNormal + '"]').length;
1492                    var subscriptionPriceIdCount = $('[data-id="' + priceIdPrefix + purchaseTypeSubscription + '"]').length;
1493
1494                    var customSelectAdditional = 0;
1495                    $('.makeshop-custom-select-wrap:visible').each(function() {
1496                        if (parseInt($(this).attr('data-input-type'), 10) === 4) {
1497                            var $numInput = $(this).find('input.makeshop-custom-select-text');
1498                            if ($numInput.length) {
1499                                var v = getNumericInputPurchasableValue($numInput);
1500                                var u = parseFloat($numInput.attr('data-unit-price')) || 0;
1501                                var uTaxRate = parseFloat($numInput.attr('data-unit-price-tax-rate')) || 0;
1502                                var uTaxType = $numInput.attr('data-unit-price-tax-type') || 'round';
1503                                customSelectAdditional += applyTaxToExcludedPrice(Math.round(v * u), uTaxRate, uTaxType);
1504                            }
1505                        }
1506                    });
1507
1508                    var purchaseTypesToUpdate = [];
1509                    if (priceIdCount > 0) {
1510                        purchaseTypesToUpdate.push(purchaseTypeNormal);
1511                    }
1512                    if (subscriptionPriceIdCount > 0) {
1513                        purchaseTypesToUpdate.push(purchaseTypeSubscription);
1514                    }
1515                    if (purchaseTypesToUpdate.length === 0) {
1516                        // 通常・定期いずれの価格要素も見つからない場合は更新をスキップ
1517                        return;
1518                    }
1519
1520                    for (var ptIdx = 0; ptIdx < purchaseTypesToUpdate.length; ptIdx++) {
1521                        var targetPurchaseType = purchaseTypesToUpdate[ptIdx];
1522                        var isRepeatDiscount = parseInt(optionData.data(OPTION_DATA_KEY_REPEAT_DISCOUNT_PREFIX + targetPurchaseType), 10) === 1;
1523                        var additionalForTarget = customSelectAdditional; 
1524                        if (targetPurchaseType === purchaseTypeSubscription && isRepeatDiscount) {
1525                            additionalForTarget = 0;
1526                        }
1527                        var basePrice = parseFloat(optionData.data(OPTION_DATA_KEY_BASE_PRICE_PREFIX + targetPurchaseType));
1528                        if (isNaN(basePrice)) {
1529                            basePrice = parseFloat(optionData.data('basePrice'));
1530                        }
1531                        if (isNaN(basePrice)) {
1532                            continue;
1533                        }
1534                        var newPrice = Math.round(basePrice + additionalForTarget);
1535                        if (typeof newPrice !== 'number' || isNaN(newPrice)) {
1536                            newPrice = 0;
1537                        }
1538                        updateItemOptionPrice(0, numberFormat(newPrice), optionListSetCount, priceIdPrefix, priceIdPrefixCount, priceIdCount, subscriptionPriceIdCount, targetPurchaseType);
1539                    }
1540                });
1541                // フォーカスアウト時のみ価格反映のリクエストを送る
1542                $textInput.on('blur', function() {
1543                    updateCustomSelectPrice();
1544                });
1545                break;
1546        }
1547    });
1548    
1549    function updateCustomSelectPrice() {
1550        // 条件再評価を実行
1551        reEvaluateCustomSelectConditionsUntilStable(true);
1552        // カスタムセレクトの数値入力の変更でラベルを手動更新
1553        $('.makeshop-custom-select-wrap:visible').each(function() {
1554            if (parseInt($(this).attr('data-input-type'), 10) === 4) {
1555                var $wrap = $(this);
1556                var $input = $wrap.find('input.makeshop-custom-select-text');
1557                if ($input.length) {
1558                    var value = getNumericInputPurchasableValue($input);
1559                    var unitPrice = parseFloat($input.attr('data-unit-price')) || 0;
1560                    var unitPriceTaxRate = parseFloat($input.attr('data-unit-price-tax-rate')) || 0;
1561                    var unitPriceTaxType = $input.attr('data-unit-price-tax-type') || 'round';
1562                    var $priceSpan = $wrap.find('.makeshop-custom-select-text-price');
1563                    var $textWrap = $wrap.find('.makeshop-custom-select-text-wrap');
1564                    var prefix = $textWrap.attr('data-price-format-prefix');
1565                    var suffix = $textWrap.attr('data-price-format-suffix');
1566                    if (unitPrice != 0 && prefix && suffix) {
1567                        var totalPrice = formatCustomSelectNumericAddonForDisplay(Math.round(value * unitPrice), unitPriceTaxRate, unitPriceTaxType);
1568                        var sign = (totalPrice >= 0) ? '+' : '';
1569                        var priceText = prefix + sign + numberFormat(totalPrice) + suffix;
1570                        if ($priceSpan.length) {
1571                            $priceSpan.text(priceText);
1572                        } else {
1573                            $wrap.append($('<span class="makeshop-custom-select-text-price"></span>').text(priceText));
1574                        }
1575                    }
1576                }
1577            }
1578        });
1579        var optionData = $('#makeshop-page-item-option-data');
1580        if (!optionData.length) {
1581            return;
1582        }
1583        
1584        var purchaseTypeNormal = optionData.attr('data-purchase-type-normal');
1585        var purchaseTypeSubscription = optionData.attr('data-purchase-type-subscription');
1586        var priceIdPrefix = optionData.attr('data-id-price-prefix');
1587        var priceExcludedTaxIdPrefix = optionData.attr('data-id-price-excluded-tax-prefix');
1588        var taxIdPrefix = optionData.attr('data-id-tax-prefix');
1589        var pointIdPrefix = optionData.attr('data-id-point-prefix');
1590        var optionIdPrefix = optionData.attr('data-id-option-prefix');
1591        
1592        if (!purchaseTypeNormal || !purchaseTypeSubscription || !priceIdPrefix || !priceExcludedTaxIdPrefix || !taxIdPrefix || !pointIdPrefix || !optionIdPrefix) {
1593            return;
1594        }
1595        
1596        // 通常オプションの値を取得
1597        var optionList = [];
1598        var optionListSetCount = $('[data-id="' + optionIdPrefix + '1"]').not('[data-id*="_custom_"]').length;
1599        if (optionListSetCount !== 0) {
1600            var optionMax = $('[data-id^="' + optionIdPrefix + '"]').not('[data-id*="_custom_"]').length / optionListSetCount;
1601            for (var i = 0; i < optionMax; i++) {
1602                optionList.push($('[data-id="' + optionIdPrefix + (i + 1) + '"]').not('[data-id*="_custom_"]').eq(0).val() || 0);
1603            }
1604        }
1605        
1606        // カスタムセレクトの値を取得(価格計算用:無効値は0として送りベース価格を返させる)
1607        var customSelectList = getCustomSelectListFromDom({ forPriceCalc: true });
1608
1609        // 価格更新のためのAJAXリクエスト
1610        var priceIdPrefixCount = $('[data-id^="' + priceIdPrefix + '"]').length;
1611        var priceExcludedTaxIdPrefixCount = $('[data-id^="' + priceExcludedTaxIdPrefix + '"]').length;
1612        var taxIdPrefixCount = $('[data-id^="' + taxIdPrefix + '"]').length;
1613        var pointIdPrefixCount = $('[data-id^="' + pointIdPrefix + '"]').length;
1614        
1615        var priceIdCount = $('[data-id="' + priceIdPrefix + purchaseTypeNormal + '"]').length;
1616        var priceExcludedTaxIdCount = $('[data-id="' + priceExcludedTaxIdPrefix + purchaseTypeNormal + '"]').length;
1617        var taxIdCount = $('[data-id="' + taxIdPrefix + purchaseTypeNormal + '"]').length;
1618        var pointIdCount = $('[data-id="' + pointIdPrefix + purchaseTypeNormal + '"]').length;
1619
1620        var subscriptionPriceIdCount = $('[data-id="' + priceIdPrefix + purchaseTypeSubscription + '"]').length;
1621        var subscriptionPriceExcludedTaxIdCount = $('[data-id="' + priceExcludedTaxIdPrefix + purchaseTypeSubscription + '"]').length;
1622        var subscriptionTaxIdCount = $('[data-id="' + taxIdPrefix + purchaseTypeSubscription + '"]').length;
1623        var subscriptionPointIdCount = $('[data-id="' + pointIdPrefix + purchaseTypeSubscription + '"]').length;
1624        
1625        var ajaxLoop = [];
1626        if (optionListSetCount === priceIdPrefixCount) {
1627            var $priceElement = $('[data-id^="' + priceIdPrefix + '"]').eq(0);
1628            var priceDataId = $priceElement.attr('data-id');
1629            if (priceDataId) {
1630                var purchaseType = priceDataId.replace(priceIdPrefix, '');
1631                ajaxLoop.push({ is_repeat_sale: (purchaseType === purchaseTypeSubscription) });
1632            }
1633        } else if (optionListSetCount === priceExcludedTaxIdPrefixCount) {
1634            var $priceElement = $('[data-id^="' + priceExcludedTaxIdPrefix + '"]').eq(0);
1635            var priceDataId = $priceElement.attr('data-id');
1636            if (priceDataId) {
1637                var purchaseType = priceDataId.replace(priceExcludedTaxIdPrefix, '');
1638                ajaxLoop.push({ is_repeat_sale: (purchaseType === purchaseTypeSubscription) });
1639            }
1640        } else {
1641            if (0 < priceIdCount || 0 < priceExcludedTaxIdCount) {
1642                ajaxLoop.push({ is_repeat_sale: false });
1643            }
1644            if (0 < subscriptionPriceIdCount || 0 < subscriptionPriceExcludedTaxIdCount) {
1645                ajaxLoop.push({ is_repeat_sale: true });
1646            }
1647        }
1648        
1649        // カスタムセレクトのみの場合でも価格計算を実行
1650        if (ajaxLoop.length === 0 && customSelectList.length > 0) {
1651            if (0 < priceIdCount || 0 < priceExcludedTaxIdCount) {
1652                ajaxLoop.push({ is_repeat_sale: false });
1653            }
1654            if (0 < subscriptionPriceIdCount || 0 < subscriptionPriceExcludedTaxIdCount) {
1655                ajaxLoop.push({ is_repeat_sale: true });
1656            }
1657        }
1658        
1659        if (ajaxLoop.length === 0) {
1660            return;
1661        }
1662        
1663        var itemCode = optionData.attr('data-brandcode');
1664        if (!itemCode) {
1665            return;
1666        }
1667        
1668        for (var ajaxIndex = 0; ajaxIndex < ajaxLoop.length; ajaxIndex++) {
1669            var requestData = {
1670                'element_index': 0,
1671                'item_code': itemCode,
1672                'option_list': optionList,
1673                'custom_select_list': customSelectList,
1674                'is_repeat_sale': ajaxLoop[ajaxIndex].is_repeat_sale
1675            };
1676            
1677            $.ajax({
1678                url: '/shop/item_info.html',
1679                type: 'POST',
1680                data: JSON.stringify(requestData),
1681                contentType: 'application/json',
1682                dataType: 'json'
1683            }).done(function(data) {
1684                var purchaseType = purchaseTypeNormal;
1685                if (data.is_repeat_sale) {
1686                    purchaseType = purchaseTypeSubscription;
1687                }
1688
1689                updateItemOptionPrice(0, data.final_price, optionListSetCount, priceIdPrefix, priceIdPrefixCount, priceIdCount, subscriptionPriceIdCount, purchaseType);
1690                updateItemOptionPrice(0, data.final_price_excluded_tax, optionListSetCount, priceExcludedTaxIdPrefix, priceExcludedTaxIdPrefixCount, priceExcludedTaxIdCount, subscriptionPriceExcludedTaxIdCount, purchaseType);
1691                updateItemOptionPrice(0, data.final_tax, optionListSetCount, taxIdPrefix, taxIdPrefixCount, taxIdCount, subscriptionTaxIdCount, purchaseType);
1692                updateItemOptionPrice(0, data.final_point, optionListSetCount, pointIdPrefix, pointIdPrefixCount, pointIdCount, subscriptionPointIdCount, purchaseType);
1693
1694                // 数値入力カスタムセレクト用の基準価格を購入種別ごとに保持
1695                // 回数割引あり定期はカスタムセレクト追加金額を反映しない
1696                var isRepeatDiscount = !!data.is_repeat_discount;
1697                optionData.data(OPTION_DATA_KEY_REPEAT_DISCOUNT_PREFIX + purchaseType, isRepeatDiscount ? 1 : 0);
1698                var shouldApplyCustomSelectAdditional = !(data.is_repeat_sale && isRepeatDiscount);
1699                updateBasePriceFromFinalPrice(optionData, data.final_price, purchaseType, shouldApplyCustomSelectAdditional);
1700
1701                // カスタムセレクトの選択内容要約を表示
1702                var $summaryEl = $('[data-id="item-custom-select-summary"]');
1703                if ($summaryEl.length) {
1704                    var summaryText = (data.custom_select_summary != null && data.custom_select_summary !== '') ? data.custom_select_summary : '';
1705                    $summaryEl.text(summaryText).toggle(summaryText !== '');
1706                }
1707            }).fail(function(jqXHR, textStatus, errorThrown) {
1708                console.error('カスタムセレクト価格計算エラー:', textStatus, errorThrown);
1709            });
1710        }
1711    }
1712    
1713    // 数値入力カスタムセレクトがある場合、表示中の価格から算出
1714    $('.makeshop-custom-select-wrap').each(function() {
1715        if (parseInt($(this).attr('data-input-type'), 10) === 4) {
1716            var priceIdPrefix = optionData.attr('data-id-price-prefix');
1717            var $firstPriceEl = $('[data-id="' + priceIdPrefix + purchaseTypeNormal + '"]').eq(0);
1718            var initialPurchaseType = purchaseTypeNormal;
1719            if (!$firstPriceEl.length) {
1720                $firstPriceEl = $('[data-id="' + priceIdPrefix + purchaseTypeSubscription + '"]').eq(0);
1721                initialPurchaseType = purchaseTypeSubscription;
1722            }
1723            if ($firstPriceEl.length) {
1724                var currentPriceText = $firstPriceEl.text().replace(/[^0-9]/g, '');
1725                var currentPrice = parseInt(currentPriceText, 10) || 0;
1726                var customSelectAdditional = 0;
1727                $('.makeshop-custom-select-wrap:visible').each(function() {
1728                    if (parseInt($(this).attr('data-input-type'), 10) === 4) {
1729                        var $numInput = $(this).find('input.makeshop-custom-select-text');
1730                        if ($numInput.length) {
1731                            var v = getNumericInputPurchasableValue($numInput);
1732                            var u = parseFloat($numInput.attr('data-unit-price')) || 0;
1733                            var uTaxRate = parseFloat($numInput.attr('data-unit-price-tax-rate')) || 0;
1734                            var uTaxType = $numInput.attr('data-unit-price-tax-type') || 'round';
1735                            customSelectAdditional += applyTaxToExcludedPrice(Math.round(v * u), uTaxRate, uTaxType);
1736                        }
1737                    }
1738                });
1739                var initialBase = currentPrice - Math.round(customSelectAdditional);
1740                var safeInitialBase = (typeof initialBase === 'number' && !isNaN(initialBase)) ? initialBase : currentPrice;
1741                optionData.data('basePrice', safeInitialBase);
1742                optionData.data(OPTION_DATA_KEY_BASE_PRICE_PREFIX + initialPurchaseType, safeInitialBase);
1743            }
1744            return false;
1745        }
1746    });
1747
1748    // 初期表示時:数値入力の価格表示
1749    $('.makeshop-custom-select-wrap').each(function() {
1750        if (parseInt($(this).attr('data-input-type'), 10) === 4) {
1751            $(this).find('input.makeshop-custom-select-text').trigger('change');
1752        }
1753    });
1754    updateCustomSelectPrice();
1755}
1756
1757function initItemRestock()
1758{
1759    var restockData = $('#makeshop-page-item-restock-data');
1760    var restockUrl = restockData.attr('data-restock-url');
1761    var hasOption = restockData.attr('data-has-option');
1762    var optionIdPrefix = restockData.attr('data-id-option-prefix');
1763    
1764    $('[href="' + restockUrl + '"]').on('click', function() {
1765        window.open('about:blank', 'itemRestockWindow', 'width=448,height=450,scrollbars=yes,resizable=yes');
1766        
1767        var optionString = '';
1768        if (hasOption === 'Y') {
1769            var optionList = [];
1770            for (var i = 1; i <= 2; i++) {
1771                var $option = $('[data-id="' + optionIdPrefix + i + '"]');
1772                if ($option.length > 0) {
1773                    optionList.push($option.eq(0).val());
1774                }
1775            }
1776            if (optionList.length == 1) {
1777                optionList.push(0);
1778            }
1779            optionString = optionList.join('_');
1780        }
1781        
1782        $('#makeshop-form-item-restock [name=restock_option]').val(optionString);
1783        $('#makeshop-form-item-restock').trigger('submit');
1784        return false;
1785    });
1786}
1787
1788function initItemSkuRestock()
1789{
1790    var restockData = $('#makeshop-page-item-restock-data');
1791    var restockUrlPrefix = restockData.attr('data-sku-restock-url-prefix');
1792    
1793    $('[href^="' + restockUrlPrefix + '"]').on('click', function() {
1794        var params = $(this).attr('href').replace(restockUrlPrefix, '').split('-');
1795        var optionString = params[0] + '_' + params[1];
1796        
1797        window.open('about:blank', 'itemRestockWindow', 'width=448,height=450,scrollbars=yes,resizable=yes');
1798        $('#makeshop-form-item-restock [name=restock_option]').val(optionString);
1799        $('#makeshop-form-item-restock').trigger('submit');
1800        return false;
1801    });
1802}
1803
1804function initItemSkuInformation()
1805{
1806    var informationData = $('#makeshop-page-item-information-data');
1807    var informationUrlPrefix = informationData.attr('data-sku-information-url-prefix');
1808    
1809    $('[href^="' + informationUrlPrefix + '"]').on('click', function() {
1810        var source = $(this).attr('href');
1811        var urlIndex = $('[href="' + source + '"]').index(this);
1812        var params = source.replace(informationUrlPrefix, '').split('-');
1813        
1814        getItemInformation({
1815            'source': source,
1816            'element_index': urlIndex,
1817            'item_code': informationData.attr('data-brandcode'),
1818            'option_list': [params[0], params[1]]
1819        });
1820        
1821        return false;
1822    });
1823}
1824
1825function initItemFavorite()
1826{
1827    var favoriteData = $('#makeshop-page-item-favorite-data');
1828    var favoriteEntryUrlPrefix = favoriteData.attr('data-favorite-entry-url-prefix');
1829    var favoriteRemoveUrlPrefix = favoriteData.attr('data-favorite-remove-url-prefix');
1830    
1831    $('[href^="' + favoriteEntryUrlPrefix + '"]').on('click', function() {
1832        var source = $(this).attr('href');
1833        var favoriteEntryUrlIndex = $('[href="' + source + '"]').index(this);
1834        var params = source.replace(favoriteEntryUrlPrefix, '').split('-');
1835
1836        addItemFavorite({
1837            'source': source,
1838            'element_index': favoriteEntryUrlIndex,
1839            'item_code': favoriteData.attr('data-brandcode'),
1840            'option_list': [params[0], params[1]]
1841        });
1842        
1843        return false;
1844    });
1845    
1846    $('[href^="' + favoriteRemoveUrlPrefix + '"]').on('click', function() {
1847        var source = $(this).attr('href');
1848        var favoriteRemoveUrlIndex = $('[href="' + source + '"]').index(this);
1849        var params = source.replace(favoriteRemoveUrlPrefix, '').split('-');
1850
1851        removeItemFavorite({
1852        	'source': source,
1853            'element_index': favoriteRemoveUrlIndex,
1854            'item_code': favoriteData.attr('data-brandcode'),
1855            'option_list': [params[0], params[1]]
1856        });
1857        
1858        return false;
1859    });
1860}
1861
1862function initItemCart()
1863{
1864    var cartData = $('#makeshop-page-item-cart-data');
1865    var cartEntryUrlPrefix = cartData.attr('data-cart-entry-url-prefix');
1866    var purchaseTypeNormal = cartData.attr('data-purchase-type-normal');
1867    var purchaseTypeSubscription = cartData.attr('data-purchase-type-subscription');
1868    var quantityId = cartData.attr('data-id-quantity');
1869    var optionIdPrefix = cartData.attr('data-id-option-prefix');
1870    var namePrintIdPrefix = cartData.attr('data-id-name-print-prefix');
1871    
1872    $('[href^="' + cartEntryUrlPrefix + '"]').on('click', function() {
1873    	var source = $(this).attr('href');
1874        var cartEntryUrlPrefixIndex = $('[href^="' + cartEntryUrlPrefix + '"]').index(this);
1875        var cartEntryUrlIndex = $('[href="' + source + '"]').index(this);
1876
1877        var cartEntryUrlPrefixCount = $('[href^="' + cartEntryUrlPrefix + '"]').length;
1878        var cartEntryUrlCount = $('[href="' + source + '"]').length;
1879        
1880        var quantityIndex = 0;
1881        var quantityCount = $('[data-id="' + quantityId + '"]').length;
1882        if (quantityCount === cartEntryUrlPrefixCount) {
1883            quantityIndex = cartEntryUrlPrefixIndex;
1884        } else if (quantityCount === cartEntryUrlCount) {
1885            quantityIndex = cartEntryUrlIndex;
1886        }
1887        var quantity = getFormValue(quantityId, quantityIndex);
1888        quantity = (quantity === '') ? 1 : quantity;
1889        
1890        var optionListIndex = 0;
1891        var optionListSetCount = $('[data-id="' + optionIdPrefix + '1"]').length;
1892        if (optionListSetCount === cartEntryUrlPrefixCount) {
1893            optionListIndex = cartEntryUrlPrefixIndex;
1894        } else if (optionListSetCount === cartEntryUrlCount) {
1895            optionListIndex = cartEntryUrlIndex;
1896        }
1897        var optionList = [];
1898        if (optionListSetCount !== 0) {
1899            var optionMax = $('[data-id^="' + optionIdPrefix + '"]').length / optionListSetCount;
1900            for (var i = 0; i < optionMax; i++) {
1901                optionList.push($('[data-id="' + optionIdPrefix + (i + 1) + '"]').eq(optionListIndex).val());
1902            }
1903        }
1904
1905        var namePrintListCount = $('[data-id="' + namePrintIdPrefix + '1"]').length;
1906        var namePrintListIndex = getItemElementIndex(namePrintListCount, cartEntryUrlPrefixCount, cartEntryUrlPrefixIndex, cartEntryUrlCount, cartEntryUrlIndex);
1907        var namePrintList = [];
1908        if (namePrintListCount !== 0) {
1909            var namePrintMax = $('[data-id^="' + namePrintIdPrefix + '"]').length / namePrintListCount;
1910            for (var i = 0; i < namePrintMax; i++) {
1911                namePrintList.push($('[data-id="' + namePrintIdPrefix + (i + 1) + '"]').eq(namePrintListIndex).val());
1912            }
1913        }
1914        
1915        var purchaseType = source.replace(cartEntryUrlPrefix, '');
1916        
1917        // カスタムセレクトの値を取得(表示中のカスタムセレクトのみ送る。非表示は条件未達のため含めない)
1918        var customSelectList = getCustomSelectListFromDom();
1919
1920        addItemCart({
1921            'source': source,
1922            'element_index': cartEntryUrlIndex,
1923            'item_code': cartData.attr('data-brandcode'),
1924            'option_list': optionList,
1925            'quantity': quantity,
1926            'is_subscription': (purchaseType === purchaseTypeSubscription),
1927            'name_print': namePrintList,
1928            'custom_select_list': customSelectList,
1929            'end_url': cartData.attr('data-cart-url')
1930        });
1931
1932        return false;
1933    });
1934}
1935
1936function initItemSkuCart()
1937{
1938    var cartData = $('#makeshop-page-item-cart-data');
1939    var cartEntryUrlPrefix = cartData.attr('data-sku-cart-entry-url-prefix');
1940    var purchaseTypeNormal = cartData.attr('data-purchase-type-normal');
1941    var purchaseTypeSubscription = cartData.attr('data-purchase-type-subscription');
1942    var quantityId = cartData.attr('data-id-quantity');
1943    var namePrintIdPrefix = cartData.attr('data-id-name-print-prefix');
1944
1945    $('[href^="' + cartEntryUrlPrefix + '"]').on('click', function() {
1946    	var source = $(this).attr('href');
1947        var cartParams = source.replace(cartEntryUrlPrefix, '').split('-');
1948        var purchaseType = cartParams[0];
1949        var optionList = [];
1950        optionList.push(cartParams[1]);
1951        optionList.push(cartParams[2]);
1952        
1953        var cartEntryUrlIndex = $('[href="' + source + '"]').index(this);
1954        var cartEntryUrlCount = $('[href="' + source + '"]').length;
1955        
1956        var cartEntryUrlMatchIndex = $('[href^="' + cartEntryUrlPrefix + '"][href$="-' + cartParams[1] + '-' + cartParams[2] + '"]').index(this);
1957        var cartEntryUrlMatchCount = $('[href^="' + cartEntryUrlPrefix + '"][href$="-' + cartParams[1] + '-' + cartParams[2] + '"]').length;;
1958        
1959        var quantityIndex = 0;
1960        var quantityCount = $('[data-id="' + quantityId + '"]').length;
1961        if (quantityCount === cartEntryUrlCount) {
1962            quantityIndex = cartEntryUrlIndex;
1963        } else if (quantityCount === cartEntryUrlMatchCount) {
1964            quantityIndex = cartEntryUrlMatchIndex;
1965        }
1966        var quantity = getFormValue(quantityId, quantityIndex);
1967        quantity = (quantity === '') ? 1 : quantity;
1968        
1969        var namePrintListCount = $('[data-id="' + namePrintIdPrefix + '1"]').length;
1970        var namePrintListIndex = getItemElementIndex(namePrintListCount, cartEntryUrlCount, cartEntryUrlIndex, cartEntryUrlMatchCount, cartEntryUrlMatchIndex);
1971        var namePrintList = [];
1972        if (namePrintListCount !== 0) {
1973            var namePrintMax = $('[data-id^="' + namePrintIdPrefix + '"]').length / namePrintListCount;
1974            for (var i = 0; i < namePrintMax; i++) {
1975                namePrintList.push($('[data-id="' + namePrintIdPrefix + (i + 1) + '"]').eq(namePrintListIndex).val());
1976            }
1977        }
1978        
1979        // カスタムセレクトの値を取得(表示中のカスタムセレクトのみ送る。非表示は条件未達のため含めない)
1980        var customSelectList = getCustomSelectListFromDom();
1981
1982        addItemCart({
1983            'source': source,
1984            'element_index': cartEntryUrlIndex,
1985            'item_code': cartData.attr('data-brandcode'),
1986            'option_list': optionList,
1987            'quantity': quantity,
1988            'is_subscription': (purchaseType === purchaseTypeSubscription),
1989            'name_print': namePrintList,
1990            'custom_select_list': customSelectList,
1991            'end_url': cartData.attr('data-cart-url')
1992        });
1993
1994        return false;
1995    });
1996}
1997
1998function getItemElementIndex(count, cartEntryUrlPrefixCount, cartEntryUrlPrefixIndex, cartEntryUrlCount, cartEntryUrlIndex)
1999{
2000    var result = 0;
2001    
2002    if (count === cartEntryUrlPrefixCount) {
2003        result = cartEntryUrlPrefixIndex;
2004    } else if (count === cartEntryUrlCount) {
2005        result = cartEntryUrlIndex;
2006    }
2007    
2008    return result;
2009}
2010
2011function addItemCart(params)
2012{
2013    $.ajax({
2014        url: '/api/cart/',
2015        type: 'POST',
2016        data: JSON.stringify({
2017            'action': 'add',
2018            'source': params.source,
2019            'element_index': params.element_index,
2020            'item_code': params.item_code,
2021            'option_list': params.option_list,
2022            'quantity': params.quantity,
2023            'is_subscription': params.is_subscription,
2024            'name_print' : params.name_print,
2025            'custom_select_list': params.custom_select_list || []
2026        }),
2027        contentType: 'application/json',
2028        dataType: 'json'
2029    }).done(function(data) {
2030        if (data.result && data.conversion_list) {
2031            insertConversionHtml(data.conversion_list);
2032        }
2033        if (data.result && data.ga4.event) {
2034            makeshop_ga_gtag('event', data.ga4.event, data.ga4.parameters);
2035        }
2036
2037        if (typeof MakeShop_afterCartEntry === 'function') {
2038            var hookData = {
2039                result: data.result,
2040                source: data.source,
2041                elementIndex: data.element_index,
2042                systemCode: data.item_code,
2043                totalQuantity: data.total_quantity,
2044                error: data.error,
2045                method: {
2046                    modal: openModalMessage
2047                }
2048            };
2049            if (MakeShop_afterCartEntry(hookData) === false) {
2050                return;
2051            }
2052        }
2053        
2054        if (!data.result) {
2055            openModalMessage(data.error.message);
2056            return;
2057        }
2058
2059        location.href = params.end_url;
2060    });
2061}
2062
2063function getItemInformation(params)
2064{
2065    $.ajax({
2066        url: '/shop/item_info.html',
2067        type: 'POST',
2068        data: JSON.stringify({
2069            'source': params.source,
2070            'element_index': params.element_index,
2071            'item_code': params.item_code,
2072            'option_list': params.option_list
2073        }),
2074        contentType: 'application/json',
2075        dataType: 'json'
2076    }).done(function(data) {
2077        if (typeof MakeShop_afterItemOptionInformation === 'function') {
2078        	const optionRelatedImageList = [];
2079        	for (const image of data.option_related_image_list) {
2080        		optionRelatedImageList.push({ imageL: image.image_l, imageS: 
2080image.image_s });
2081        	}
2082            var hookData = {
2083                elementIndex: data.element_index,
2084                isSoldout: data.is_soldout,
2085                imageL: data.image_l,
2086                imageS: data.image_s,
2087                optionRelatedImageList: optionRelatedImageList,
2088                method: {
2089                    modal: openModalMessage
2090                }
2091            };
2092            if (MakeShop_afterItemOptionInformation(hookData) === false) {
2093                return;
2094            }
2095        }
2096    });
2097}
2098
2099function addItemFavorite(params)
2100{
2101    $.ajax({
2102        url: '/api/favorite/',
2103        type: 'POST',
2104        data: JSON.stringify({
2105            'action': 'add',
2106            'source': params.source,
2107            'element_index': params.element_index,
2108            'item_code': params.item_code,
2109            'option_list': params.option_list
2110        }),
2111        contentType: 'application/json',
2112        dataType: 'json'
2113    }).done(function(data) {
2114        if (typeof MakeShop_afterFavoriteEntry === 'function') {
2115            var hookData = {
2116                result: data.result,
2117                source: data.source,
2118                elementIndex: data.element_index,
2119                systemCode: data.item_code,
2120                error: data.error,
2121                method: {
2122                    modal: openModalMessage
2123                }
2124            };
2125            if (MakeShop_afterFavoriteEntry(hookData) === false) {
2126                return;
2127            }
2128        }
2129        
2130        if (!data.result) {
2131            openModalMessage(data.error.message);
2132            return;
2133        }
2134
2135        openModalMessage(data.message);
2136    });
2137}
2138
2139function removeItemFavorite(params)
2140{
2141    $.ajax({
2142        url: '/api/favorite/',
2143        type: 'POST',
2144        data: JSON.stringify({
2145            'action': 'remove',
2146            'source': params.source,
2147            'element_index': params.element_index,
2148            'item_code': params.item_code,
2149            'option1': params.option_list[0],
2150            'option2': params.option_list[1]
2151        }),
2152        contentType: 'application/json',
2153        dataType: 'json'
2154    }).done(function(data) {
2155        if (typeof MakeShop_afterFavoriteRemove === 'function') {
2156            var hookData = {
2157                result: data.result,
2158                source: data.source,
2159                elementIndex: data.element_index,
2160                systemCode: data.item_code,
2161                error: data.error,
2162                method: {
2163                    modal: openModalMessage
2164                }
2165            };
2166            if (MakeShop_afterFavoriteRemove(hookData) === false) {
2167                return;
2168            }
2169        }
2170        
2171        if (!data.result) {
2172            openModalMessage(data.error.message);
2173            return;
2174        }
2175
2176        openModalMessage(data.message);
2177    });
2178}
2179
2180function initItemOptionImage()
2181{
2182    var optionData = $('#makeshop-page-item-option-data');
2183    if (!optionData.length) return;
2184
2185    var optionIdPrefix = optionData.attr('data-id-option-prefix');
2186    var cartData = $('#makeshop-page-item-cart-data');
2187    var cartEntryUrlPrefix = cartData.attr('data-sku-cart-entry-url-prefix');
2188
2189    $('.option-image-option2-item').on('click', function(e) {
2190        if ($(e.target).closest('a').length) return;
2191
2192        var $cartLink = $(this).find('a[href^="' + cartEntryUrlPrefix + '"]').first();
2193        if (!$cartLink.length) return;
2194
2195        var parts = $cartLink.attr('href').replace(cartEntryUrlPrefix, '').split('-');
2196        var spcode1 = parts[1] || '0';
2197        var spcode2 = parts[2] || '0';
2198
2199        var $selects = $('[data-id^="' + optionIdPrefix + '"]').not('[data-id*="_custom_"]');
2200        $selects.eq(0).val(spcode1);
2201        if ($selects.length >= 2) {
2202            $selects.eq(1).val(spcode2);
2203        }
2204
2205        if ($selects.length) {
2206            $selects.first()[0].dispatchEvent(new Event('change', { bubbles: true }));
2207        }
2208    });
2209}
2210
2211function initItemContact()
2212{
2213    var contactData = $('#makeshop-page-item-contact-data');
2214    var contactUrl = contactData.attr('data-contact-url');
2215
2216    $('[href="' + contactUrl + '"]').on('click', function() {
2217        $('#makeshop-form-item-contact').trigger('submit');
2218        return false;
2219    });
2220}
2221
2222})($jQueryMakeShop);
2223$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.