ページの作成:「ここにあるすべてのJavaScriptは、すべてのページ読み込みですべての利用者に対して読み込まれます: ======================================== * 屋台比較 * 最大4店舗を localStorage に保存 * ========================================: mw.loader.using( 'mediawiki.storage' ).then( function () { 'use strict'; const STORAGE_KEY = 'matsuriWikiCompareStalls'; const MAX_COMPARE = 4; /** * 比…」
 
編集の要約なし
1行目: 1行目:
/* ここにあるすべてのJavaScriptは、すべてのページ読み込みですべての利用者に対して読み込まれます */
/* ========================================
/* ========================================
  * 屋台比較
  * 屋台比較
  * 最大4店舗を localStorage に保存
  * 最大4店舗を localStorage に保存
* Template側にはbuttonを書かず、
* JavaScriptでbuttonを生成する
  * ======================================== */
  * ======================================== */


16行目: 17行目:
     */
     */
     function getCompareStalls() {
     function getCompareStalls() {
         const raw = mw.storage.get( STORAGE_KEY );
         const raw = mw.storage.get( STORAGE_KEY );


23行目: 25行目:


         try {
         try {
             const ids = JSON.parse( raw );
             const ids = JSON.parse( raw );


29行目: 32行目:
             }
             }


            /*
            * stall_id は文字列に統一。
            * 重複も除去する。
            */
             return [ ...new Set(
             return [ ...new Set(
                 ids
                 ids
42行目: 41行目:


         } catch ( e ) {
         } catch ( e ) {
             return [];
             return [];
         }
         }
     }
     }


     /**
     /**
50行目: 52行目:
     */
     */
     function saveCompareStalls( ids ) {
     function saveCompareStalls( ids ) {
         mw.storage.set(
         mw.storage.set(
             STORAGE_KEY,
             STORAGE_KEY,
             JSON.stringify( ids )
             JSON.stringify( ids )
         );
         );
     }
     }


     /**
     /**
60行目: 65行目:
     */
     */
     function showMessage( control, message, isError ) {
     function showMessage( control, message, isError ) {
         const messageElement =
         const messageElement =
             control.querySelector( '.stall-compare-message' );
             control.querySelector(
                '.stall-compare-message'
            );


         if ( !messageElement ) {
         if ( !messageElement ) {
73行目: 81行目:
             Boolean( isError )
             Boolean( isError )
         );
         );
     }
     }


     /**
     /**
     * ページ上の比較ボタン表示を更新
     * 比較ボタン生成
    */
    function createCompareButtons() {
 
        document
            .querySelectorAll(
                '.stall-compare-placeholder'
            )
            .forEach( function ( placeholder ) {
 
                /*
                * すでに生成済みなら何もしない
                */
                if (
                    placeholder.dataset.initialized === '1'
                ) {
                    return;
                }
 
                /*
                * id:
                * stall-compare-placeholder-1
                * ↓
                * stallId = "1"
                */
                const prefix =
                    'stall-compare-placeholder-';
 
                if (
                    !placeholder.id.startsWith( prefix )
                ) {
                    return;
                }
 
                const stallId =
                    placeholder.id.substring(
                        prefix.length
                    );
 
                if ( !stallId ) {
                    return;
                }
 
                /*
                * JavaScript側で本物のbuttonを作る
                */
                const button =
                    document.createElement( 'button' );
 
                button.type = 'button';
 
                button.className =
                    'stall-compare-button';
 
                button.dataset.stallId =
                    stallId;
 
                button.setAttribute(
                    'aria-pressed',
                    'false'
                );
 
                button.textContent =
                    '比較に追加';
 
                placeholder.appendChild( button );
 
                placeholder.dataset.initialized =
                    '1';
 
            } );
 
    }
 
 
    /**
    * ボタン状態更新
     */
     */
     function updateCompareButtons() {
     function updateCompareButtons() {
         const ids = getCompareStalls();
 
         const ids =
            getCompareStalls();


         document
         document
             .querySelectorAll( '.stall-compare-button' )
             .querySelectorAll(
                '.stall-compare-button'
            )
             .forEach( function ( button ) {
             .forEach( function ( button ) {


                 const stallId =
                 const stallId =
                     String( button.dataset.stallId || '' );
                     String(
                        button.dataset.stallId || ''
                    );


                 const selected =
                 const selected =
92行目: 184行目:


                 if ( selected ) {
                 if ( selected ) {
                     button.textContent = '比較から外す';
 
                     button.textContent =
                        '比較から外す';
 
                     button.classList.add(
                     button.classList.add(
                         'stall-compare-button-selected'
                         'stall-compare-button-selected'
                     );
                     );
                     button.setAttribute(
                     button.setAttribute(
                         'aria-pressed',
                         'aria-pressed',
                         'true'
                         'true'
                     );
                     );
                 } else {
                 } else {
                     button.textContent = '比較に追加';
 
                     button.textContent =
                        '比較に追加';
 
                     button.classList.remove(
                     button.classList.remove(
                         'stall-compare-button-selected'
                         'stall-compare-button-selected'
                     );
                     );
                     button.setAttribute(
                     button.setAttribute(
                         'aria-pressed',
                         'aria-pressed',
                         'false'
                         'false'
                     );
                     );
                 }
                 }
             } );
             } );
     }
     }


     /**
     /**
     * 比較ボタン
     * 比較ボタンクリック
     */
     */
     document.addEventListener( 'click', function ( event ) {
     document.addEventListener(
        'click',
        function ( event ) {
 
            const button =
                event.target.closest(
                    '.stall-compare-button'
                );
 
            if ( !button ) {
                return;
            }
 
            event.preventDefault();
 
            const stallId =
                String(
                    button.dataset.stallId || ''
                );
 
            const control =
                button.closest(
                    '.stall-compare-control'
                );
 
            if ( !stallId ) {
 
                if ( control ) {
 
                    showMessage(
                        control,
                        '屋台IDを取得できませんでした。',
                        true
                    );
 
                }
 
                return;
            }


        const button =
            let ids =
            event.target.closest( '.stall-compare-button' );
                getCompareStalls();


        if ( !button ) {
            const index =
            return;
                ids.indexOf( stallId );
        }


        event.preventDefault();


        const stallId =
            /*
             String( button.dataset.stallId || '' );
            * 登録済み
            * → 比較候補から外す
            */
             if ( index !== -1 ) {


        const control =
                ids.splice(
            button.closest( '.stall-compare-control' );
                    index,
                    1
                );


        if ( !stallId ) {
                 saveCompareStalls(
            if ( control ) {
                     ids
                 showMessage(
                     control,
                    '屋台IDを取得できませんでした。',
                    true
                 );
                 );
                updateCompareButtons();
                if ( control ) {
                    showMessage(
                        control,
                        '比較候補から外しました。',
                        false
                    );
                }
                return;
             }
             }


            return;
        }


        let ids = getCompareStalls();
            /*
            * 最大4店舗
            */
            if (
                ids.length >= MAX_COMPARE
            ) {
 
                if ( control ) {
 
                    showMessage(
                        control,
                        '比較できる屋台は最大4店舗です。',
                        true
                    );
 
                }
 
                return;
            }


        const index =
            ids.indexOf( stallId );


        /*
            /*
        * すでに登録済み
            * 比較候補へ追加
        * → 比較候補から削除
            */
        */
            ids.push(
        if ( index !== -1 ) {
                stallId
            );


             ids.splice( index, 1 );
             saveCompareStalls(
                ids
            );


            saveCompareStalls( ids );
             updateCompareButtons();
             updateCompareButtons();


             if ( control ) {
             if ( control ) {
                 showMessage(
                 showMessage(
                     control,
                     control,
                     '比較候補から外しました。',
                     '比較候補に追加しました(' +
                        ids.length +
                        '/4)。',
                     false
                     false
                 );
                 );
             }
             }


            return;
         }
         }
    );


        /*
        * 最大4件
        */
        if ( ids.length >= MAX_COMPARE ) {


            if ( control ) {
    /**
                showMessage(
    * 初期化
                    control,
    */
                    '比較できる屋台は最大4店舗です。',
    function initStallCompare() {
                    true
                );
            }
 
            return;
        }
 
        /*
        * 比較候補へ追加
        */
        ids.push( stallId );


         saveCompareStalls( ids );
         createCompareButtons();
         updateCompareButtons();
         updateCompareButtons();


        if ( control ) {
    }
            showMessage(
                control,
                '比較候補に追加しました(' +
                    ids.length +
                    '/4)。',
                false
            );
        }


    } );


     /*
     /*
     * 初期表示
     * 通常ページ
     */
     */
     updateCompareButtons();
     initStallCompare();
 


     /*
     /*
     * MediaWikiでページ本文が再描画された場合にも対応
     * MediaWiki本文再描画時
     */
     */
     mw.hook( 'wikipage.content' ).add( function () {
     mw.hook(
        updateCompareButtons();
        'wikipage.content'
     } );
    ).add(
        function () {
 
            initStallCompare();
 
        }
     );


} );
} );

2026年8月11日 (火) 02:26時点における版

/* ========================================
 * 屋台比較
 * 最大4店舗を localStorage に保存
 * Template側にはbuttonを書かず、
 * JavaScriptでbuttonを生成する
 * ======================================== */

mw.loader.using( 'mediawiki.storage' ).then( function () {

    'use strict';

    const STORAGE_KEY = 'matsuriWikiCompareStalls';
    const MAX_COMPARE = 4;

    /**
     * 比較候補を取得
     */
    function getCompareStalls() {

        const raw = mw.storage.get( STORAGE_KEY );

        if ( !raw ) {
            return [];
        }

        try {

            const ids = JSON.parse( raw );

            if ( !Array.isArray( ids ) ) {
                return [];
            }

            return [ ...new Set(
                ids
                    .map( String )
                    .filter( function ( id ) {
                        return id !== '';
                    } )
            ) ].slice( 0, MAX_COMPARE );

        } catch ( e ) {

            return [];

        }
    }


    /**
     * 比較候補を保存
     */
    function saveCompareStalls( ids ) {

        mw.storage.set(
            STORAGE_KEY,
            JSON.stringify( ids )
        );

    }


    /**
     * メッセージ表示
     */
    function showMessage( control, message, isError ) {

        const messageElement =
            control.querySelector(
                '.stall-compare-message'
            );

        if ( !messageElement ) {
            return;
        }

        messageElement.textContent = message;

        messageElement.classList.toggle(
            'stall-compare-message-error',
            Boolean( isError )
        );

    }


    /**
     * 比較ボタン生成
     */
    function createCompareButtons() {

        document
            .querySelectorAll(
                '.stall-compare-placeholder'
            )
            .forEach( function ( placeholder ) {

                /*
                 * すでに生成済みなら何もしない
                 */
                if (
                    placeholder.dataset.initialized === '1'
                ) {
                    return;
                }

                /*
                 * id:
                 * stall-compare-placeholder-1
                 * ↓
                 * stallId = "1"
                 */
                const prefix =
                    'stall-compare-placeholder-';

                if (
                    !placeholder.id.startsWith( prefix )
                ) {
                    return;
                }

                const stallId =
                    placeholder.id.substring(
                        prefix.length
                    );

                if ( !stallId ) {
                    return;
                }

                /*
                 * JavaScript側で本物のbuttonを作る
                 */
                const button =
                    document.createElement( 'button' );

                button.type = 'button';

                button.className =
                    'stall-compare-button';

                button.dataset.stallId =
                    stallId;

                button.setAttribute(
                    'aria-pressed',
                    'false'
                );

                button.textContent =
                    '比較に追加';

                placeholder.appendChild( button );

                placeholder.dataset.initialized =
                    '1';

            } );

    }


    /**
     * ボタン状態更新
     */
    function updateCompareButtons() {

        const ids =
            getCompareStalls();

        document
            .querySelectorAll(
                '.stall-compare-button'
            )
            .forEach( function ( button ) {

                const stallId =
                    String(
                        button.dataset.stallId || ''
                    );

                const selected =
                    ids.includes( stallId );

                if ( selected ) {

                    button.textContent =
                        '比較から外す';

                    button.classList.add(
                        'stall-compare-button-selected'
                    );

                    button.setAttribute(
                        'aria-pressed',
                        'true'
                    );

                } else {

                    button.textContent =
                        '比較に追加';

                    button.classList.remove(
                        'stall-compare-button-selected'
                    );

                    button.setAttribute(
                        'aria-pressed',
                        'false'
                    );

                }

            } );

    }


    /**
     * 比較ボタンクリック
     */
    document.addEventListener(
        'click',
        function ( event ) {

            const button =
                event.target.closest(
                    '.stall-compare-button'
                );

            if ( !button ) {
                return;
            }

            event.preventDefault();

            const stallId =
                String(
                    button.dataset.stallId || ''
                );

            const control =
                button.closest(
                    '.stall-compare-control'
                );

            if ( !stallId ) {

                if ( control ) {

                    showMessage(
                        control,
                        '屋台IDを取得できませんでした。',
                        true
                    );

                }

                return;
            }

            let ids =
                getCompareStalls();

            const index =
                ids.indexOf( stallId );


            /*
             * 登録済み
             * → 比較候補から外す
             */
            if ( index !== -1 ) {

                ids.splice(
                    index,
                    1
                );

                saveCompareStalls(
                    ids
                );

                updateCompareButtons();

                if ( control ) {

                    showMessage(
                        control,
                        '比較候補から外しました。',
                        false
                    );

                }

                return;
            }


            /*
             * 最大4店舗
             */
            if (
                ids.length >= MAX_COMPARE
            ) {

                if ( control ) {

                    showMessage(
                        control,
                        '比較できる屋台は最大4店舗です。',
                        true
                    );

                }

                return;
            }


            /*
             * 比較候補へ追加
             */
            ids.push(
                stallId
            );

            saveCompareStalls(
                ids
            );

            updateCompareButtons();

            if ( control ) {

                showMessage(
                    control,
                    '比較候補に追加しました(' +
                        ids.length +
                        '/4)。',
                    false
                );

            }

        }
    );


    /**
     * 初期化
     */
    function initStallCompare() {

        createCompareButtons();
        updateCompareButtons();

    }


    /*
     * 通常ページ
     */
    initStallCompare();


    /*
     * MediaWiki本文再描画時
     */
    mw.hook(
        'wikipage.content'
    ).add(
        function () {

            initStallCompare();

        }
    );

} );