重量と送料設定

重量欄と送料設定の初期設定を変更。送料設定を優先順に並び替え。重量欄で計算可能に。

// ==UserScript==
// @name         重量と送料設定
// @namespace    http://tampermonkey.net/
// @version      1.10.1
// @description  重量欄と送料設定の初期設定を変更。送料設定を優先順に並び替え。重量欄で計算可能に。
// @license      MIT
// @match        *://plus-nao.com/forests/*/mainedit/*
// @match        *://plus-nao.com/forests/*/registered_mainedit/*
// @grant        none
// @run-at       document-body
// ==/UserScript==

(function () {
    'use strict';

    const DB_NAME = 'ShippingDB';
    const STORE_NAME = 'ShippingSettings';
    let db;

    function initialize() {
        const shippingSelect = document.getElementById("TbMainproduct送料設定");
        const weightInput = document.getElementById("TbMainproductWeight");
        const saveButton = document.getElementById("saveAndSkuStock");

        if (shippingSelect) {
            customizeDropDown(shippingSelect);
        }

        if (weightInput && saveButton) {
            initializeMainEditPage(shippingSelect, weightInput, saveButton);
        }
    }

    function initializeMainEditPage(shippingSelect, weightInput, saveButton) {
        const productId = window.location.pathname.split('/').pop();

        const emptyOption = document.createElement('option');
        emptyOption.value = "";
        emptyOption.text = "送料を選択";
        shippingSelect.insertBefore(emptyOption, shippingSelect.firstChild);
        shippingSelect.value = "";

        managePlaceholder(weightInput, '重量を入力');

        function updateButtonStyle() {
            if (shippingSelect.value === "") {
                saveButton.disabled = true;
                saveButton.style.cursor = 'not-allowed';
                saveButton.value = "送料を選択してください";
            } else {
                saveButton.disabled = false;
                saveButton.style.cursor = '';
                saveButton.value = "保存してSKU在庫の設定";
            }
        }

        function loadSavedShipping() {
            getShippingSetting(productId).then(savedShipping => {
                if (savedShipping) {
                    shippingSelect.value = savedShipping;
                }

                updateButtonStyle();

                if (shippingSelect.value === "") {
                    setTimeout(loadSavedShipping, 200);
                }
            });
        }

        loadSavedShipping();

        shippingSelect.addEventListener('change', function () {
            updateButtonStyle();
            saveShippingSetting(productId, shippingSelect.value);
        });
    }

    function managePlaceholder(inputElement, placeholder) {
        if (!inputElement) return;

        inputElement.placeholder = placeholder;

        if (inputElement.value === '0') {
            inputElement.value = '';
        }

        inputElement.addEventListener('blur', function () {
            let value = inputElement.value.trim();

            if (value.endsWith('kg')) {
                value = value.slice(0, -2).trim();
                let numberValue = parseFloat(value) * 1000;
                numberValue = Math.ceil(numberValue);
                inputElement.value = numberValue.toString();
            } else if (value.endsWith('g')) {
                value = value.slice(0, -1).trim();
                inputElement.value = value;
            } else {
                const evaluatedValue = evaluateExpression(value);
                if (!isNaN(evaluatedValue)) {
                    inputElement.value = evaluatedValue.toString();
                }
            }
        });
    }

    function evaluateExpression(expr) {
        let result = NaN;

        if (expr.trim() === '') {
            return result;
        }

        expr = expr.replace(/[0-9]/g, (c) => String.fromCharCode(c.charCodeAt(0) - 0xFEE0));
        expr = expr.replace(/+/g, '+')
            .replace(/-/g, '-')
            .replace(/×/g, '*')
            .replace(/÷/g, '/')
            .replace(/./g, '.');

        if (!/^[\d+\-*/().]+$/.test(expr)) {
            return result;
        }

        try {
            const maxDecimalPlaces = (expr.match(/\.\d+/g) || []).reduce((max, num) => {
                return Math.max(max, num.length - 1);
            }, 0);

            const scalingFactor = Math.pow(10, maxDecimalPlaces);
            const scaledExpr = `(${expr}) * ${scalingFactor}`;
            result = new Function('return ' + scaledExpr)() / scalingFactor;

            result = Math.round(result * 100) / 100;

        } catch (error) {
            console.error('無効な式です:', error);
        }
        return result;
    }

    function openDatabase() {
        return new Promise((resolve, reject) => {
            const request = indexedDB.open(DB_NAME, 1);

            request.onupgradeneeded = function (event) {
                db = event.target.result;
                db.createObjectStore(STORE_NAME, { keyPath: 'productId' });
            };

            request.onsuccess = function (event) {
                db = event.target.result;
                resolve(db);
            };

            request.onerror = function (event) {
                reject('Database error: ' + event.target.errorCode);
            };
        });
    }

    function saveShippingSetting(productId, shippingValue) {
        return new Promise((resolve, reject) => {
            const transaction = db.transaction([STORE_NAME], 'readwrite');
            const store = transaction.objectStore(STORE_NAME);
            const request = store.put({ productId: productId, shippingValue: shippingValue });

            request.onsuccess = function () {
                resolve();
            };

            request.onerror = function (event) {
                reject('Save error: ' + event.target.errorCode);
            };
        });
    }

    function getShippingSetting(productId) {
        return new Promise((resolve, reject) => {
            const transaction = db.transaction([STORE_NAME], 'readonly');
            const store = transaction.objectStore(STORE_NAME);
            const request = store.get(productId);

            request.onsuccess = function (event) {
                resolve(event.target.result ? event.target.result.shippingValue : null);
            };

            request.onerror = function (event) {
                reject('Fetch error: ' + event.target.errorCode);
            };
        });
    }

    function customizeDropDown(shippingSelect) {
        const order = [
            "24", "25", "26", "5", "27", "4", "29", "10",
            "11", "12", "13", "22", "14", "15", "16", "17",
            "18", "19", "20", "21", "23", "8", "28", "9"
        ];
        const unusedOptions = ["23", "8", "28", "9"];
        const tooltips = {
            "24": "12cm×23.5cm以内、厚さ1cm以内\n重さ50g以内",
            "25": "3辺の合計が60cm以内、1辺の最長は34cm以内、厚さ2cm以内\n重さ1kg以内",
            "26": "34×25cm以内、厚さ3cm以内\n重さ50g以内",
            "5": "3辺の合計が60cm以内、1辺の最長は34cm以内、厚さ3cm以内\n重さ1kg以内",
            "27": "3辺の合計が90cm以内、1辺の最長は60cm以内\n重さ50g以内",
            "4": "3辺の合計が90cm以内、1辺の最長は60cm以内\n重さ100g以内",
            "29": "3辺の合計が90cm以内、1辺の最長は60cm以内\n重さ150g以内",
            "10": "3辺の合計が90cm以内、1辺の最長は60cm以内\n重さ250g以内",
            "11": "3辺の合計が60cm以内\n重さ20kg以内",
            "12": "3辺の合計が80cm以内\n重さ20kg以内",
            "13": "3辺の合計が100cm以内\n重さ20kg以内",
            "22": "3辺の合計が120cm以内\n重さ20kg以内",
            "14": "3辺の合計が140cm以内\n重さ20kg以内",
            "15": "3辺の合計が160cm以内\n重さ20kg以内",
            "16": "3辺の合計が170cm以内\n重さ20kg以内",
            "17": "3辺の合計が180cm以内\n重さ20kg以内",
            "18": "3辺の合計が200cm以内\n重さ20kg以内",
            "19": "3辺の合計が220cm以内\n重さ20kg以内",
            "20": "3辺の合計が240cm以内\n重さ20kg以内",
            "21": "3辺の合計が260cm以内\n重さ20kg以内"
        };

        const options = Array.from(shippingSelect.options);
        const orderedOptions = order.map(value => options.find(option => option.value === value)).filter(Boolean);

        shippingSelect.innerHTML = '';
        for (let option of orderedOptions) {
            shippingSelect.add(option);
        }

        for (let option of shippingSelect.options) {
            if (tooltips[option.value]) {
                option.title = tooltips[option.value];
            }

            if (unusedOptions.includes(option.value)) {
                option.style.color = "#8B0000";
                option.title = `${tooltips[option.value] || ""} 現在使われていません`.trim();
            }
        }
    }

    window.addEventListener('load', function () {
        openDatabase().then(() => {
            initialize();
        }).catch(error => {
            console.error(error);
        });
    });
})();