Tip of the day
Want to find something particular about the game and don't mind spoilers? Check the Wiki!

MediaWiki:Gadget-WSWBHighlightTable.js

From Walkscape Walkthrough
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Note: After publishing, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
/* Gadget: WSWB Highlight Table
 *
 * Highlights rows in tables with class "wswb-highlight-table". A row's state
 * follows its data-wswb-object-id across every participating table and page.
 * Tables may set data-wswb-tracker when the same object needs an independent
 * checklist (for example, "obtained" versus "discovered").
 */
mw.loader.using( [ 'jquery', 'mediawiki.storage' ] ).then( function () {
    'use strict';

    var STORAGE_KEY = 'wswb:highlight-table:v1';
    var TABLE_SELECTOR = 'table.wswb-highlight-table';
    var FOOTER_CLASS = 'wswb-highlight-table-footer';
    var OBJECT_ID_ATTRIBUTE = 'data-wswb-object-id';
    var TRACKER_ATTRIBUTE = 'data-wswb-tracker';
    var DEFAULT_TRACKER = 'default';

    function canUseLocalStorage() {
        var testKey;

        try {
            testKey = STORAGE_KEY + ':test';
            localStorage.setItem( testKey, '1' );
            localStorage.removeItem( testKey );
            return true;
        } catch ( error ) {
            return false;
        }
    }

    var usesLocalStorage = canUseLocalStorage();
    var storage = usesLocalStorage ? {
        get: function ( key ) {
            return localStorage.getItem( key );
        },
        set: function ( key, value ) {
            localStorage.setItem( key, value );
        }
    } : {
        get: function ( key ) {
            return mw.storage.get( key );
        },
        set: function ( key, value ) {
            mw.storage.set( key, value );
        }
    };

    function readSelections() {
        var raw = storage.get( STORAGE_KEY );

        if ( !raw ) {
            return {};
        }

        try {
            var parsed = JSON.parse( raw );
            return parsed && typeof parsed === 'object' && !Array.isArray( parsed ) ? parsed : {};
        } catch ( error ) {
            return {};
        }
    }

    function writeSelections( selections ) {
        try {
            storage.set( STORAGE_KEY, JSON.stringify( selections ) );
        } catch ( error ) {
            mw.log.error( 'WSWB Highlight Table could not save its state:', error );
        }
    }

    function getTracker( $table ) {
        return $table.attr( TRACKER_ATTRIBUTE ) || DEFAULT_TRACKER;
    }

    function getObjectId( $row ) {
        return $row.attr( OBJECT_ID_ATTRIBUTE ) || '';
    }

    function getSelectionId( $table, $row ) {
        var objectId = getObjectId( $row );
        return objectId ? getTracker( $table ) + '/' + objectId : '';
    }

    function tableColumnCount( $table ) {
        var maximum = 1;

        $table.children( 'thead, tbody' ).children( 'tr' ).each( function () {
            var count = 0;

            $( this ).children( 'th, td' ).each( function () {
                var span = parseInt( $( this ).attr( 'colspan' ) || '1', 10 );
                count += isNaN( span ) ? 1 : span;
            } );

            maximum = Math.max( maximum, count );
        } );

        return maximum;
    }

    function selectableRows( $table ) {
        return $table.find( 'tbody tr[' + OBJECT_ID_ATTRIBUTE + ']' );
    }

    function updateFooter( $table ) {
        var $rows;
        var selectedIds = {};
        var allIds = {};
        var selectedCount;
        var totalCount;
        var $footer;
        var $cell;
        var $counter;

        if ( !$table.hasClass( FOOTER_CLASS ) ) {
            return;
        }

        $rows = selectableRows( $table );
        $rows.each( function () {
            var $row = $( this );
            var selectionId = getSelectionId( $table, $row );

            if ( selectionId ) {
                allIds[ selectionId ] = true;
                if ( $row.hasClass( 'wswb-highlight-on' ) ) {
                    selectedIds[ selectionId ] = true;
                }
            }
        } );

        selectedCount = Object.keys( selectedIds ).length;
        totalCount = Object.keys( allIds ).length;
        $footer = $table.children( 'tfoot' );

        if ( !$footer.length ) {
            $footer = $( '<tfoot>' ).appendTo( $table );
        }

        $cell = $footer.find( 'tr.wswb-highlight-table-footer-row > th' );
        if ( !$cell.length ) {
            $cell = $( '<th>' )
                .addClass( 'wswb-highlight-table-footer-cell' )
                .appendTo(
                    $( '<tr>' )
                        .addClass( 'wswb-highlight-table-footer-row' )
                        .appendTo( $footer )
                );
        }

        $cell.attr( 'colspan', tableColumnCount( $table ) );
        $counter = $cell.find( '.wswb-highlight-table-counter' );

        if ( !$counter.length ) {
            $counter = $( '<span>' )
                .addClass( 'wswb-highlight-table-counter' )
                .appendTo( $cell );

            $( '<button>' )
                .attr( 'type', 'button' )
                .addClass( 'wswb-highlight-table-clear-button' )
                .text( 'Clear All' )
                .appendTo( $cell );
        }

        $counter.text( 'Entries Completed: (' + selectedCount + ' / ' + totalCount + ')' );
    }

    function applySelections() {
        var selections = readSelections();

        $( TABLE_SELECTOR ).each( function () {
            var $table = $( this );

            selectableRows( $table ).each( function () {
                var $row = $( this );
                var selectionId = getSelectionId( $table, $row );
                var selected = selections[ selectionId ] === 1;

                $row
                    .toggleClass( 'wswb-highlight-on', selected )
                    .attr( 'aria-selected', selected ? 'true' : 'false' );

                if ( !$row.attr( 'tabindex' ) ) {
                    $row.attr( 'tabindex', '0' );
                }
            } );

            updateFooter( $table );
        } );
    }

    function toggleRow( $table, $row ) {
        var selectionId = getSelectionId( $table, $row );
        var selections;

        if ( !selectionId ) {
            return;
        }

        selections = readSelections();
        if ( selections[ selectionId ] === 1 ) {
            delete selections[ selectionId ];
        } else {
            selections[ selectionId ] = 1;
        }

        writeSelections( selections );
        applySelections();
    }

    function clearTable( $table ) {
        var selections = readSelections();

        selectableRows( $table ).each( function () {
            delete selections[ getSelectionId( $table, $( this ) ) ];
        } );

        writeSelections( selections );
        applySelections();
    }

    function bindTables( $root ) {
        $root.find( TABLE_SELECTOR ).addBack( TABLE_SELECTOR ).each( function () {
            var $table = $( this );

            $table.off( '.wswbHighlightTable' );
            $table.on( 'click.wswbHighlightTable', 'tbody tr[' + OBJECT_ID_ATTRIBUTE + ']', function ( event ) {
                if ( $( event.target ).closest( 'a, button, input, label, select, textarea' ).length ) {
                    return;
                }

                toggleRow( $table, $( this ) );
            } );
            $table.on( 'keydown.wswbHighlightTable', 'tbody tr[' + OBJECT_ID_ATTRIBUTE + ']', function ( event ) {
                if ( event.key === ' ' || event.key === 'Enter' ) {
                    event.preventDefault();
                    toggleRow( $table, $( this ) );
                }
            } );
            $table.on( 'click.wswbHighlightTable', '.wswb-highlight-table-clear-button', function () {
                clearTable( $table );
            } );
        } );
    }

    function initialize( $content ) {
        bindTables( $content );
        applySelections();
    }

    $( function () {
        initialize( $( document ) );
    } );

    mw.hook( 'wikipage.content' ).add( initialize );

    if ( usesLocalStorage ) {
        window.addEventListener( 'storage', function ( event ) {
            if ( event.key === STORAGE_KEY ) {
                applySelections();
            }
        } );
    }
} );