MediaWiki:Common.js: Różnice pomiędzy wersjami

Z Dubbingpedia
Skocz do:nawigacja, szukaj
mNie podano opisu zmian
mNie podano opisu zmian
Linia 9: Linia 9:
         original: 'pdk-episodes__original'
         original: 'pdk-episodes__original'
     };
     };
    var sectionCounter = 0;


     function buildGrid( rows ) {
     function buildGrid( rows ) {
Linia 56: Linia 57:
         }
         }
         return groups;
         return groups;
    }
    function initializeSeasonToggles( table, rows ) {
        var seasons = rows.filter( function ( row ) {
            return row.classList.contains( 'pdk-episodes__season' );
        } );
        seasons.forEach( function ( season, seasonIndex ) {
            var start = rows.indexOf( season ) + 1;
            var nextSeason = seasons[ seasonIndex + 1 ];
            var end = nextSeason ? rows.indexOf( nextSeason ) : rows.length;
            var controlledRows = rows.slice( start, end ).filter( function ( row ) {
                return row.cells.length > 0;
            } );
            var cell = season.querySelector( 'th' );
            if ( !cell || cell.querySelector( '.pdk-episodes__season-toggle' ) ) {
                return;
            }
            var seasonName = cell.textContent.trim();
            var button = document.createElement( 'button' );
            var icon = document.createElement( 'span' );
            button.type = 'button';
            button.className = 'pdk-episodes__season-toggle';
            button.setAttribute( 'aria-expanded', 'true' );
            button.setAttribute( 'aria-label', 'Zwiń: ' + seasonName );
            button.title = 'Zwiń serię';
            icon.className = 'pdk-episodes__toggle-icon';
            icon.setAttribute( 'aria-hidden', 'true' );
            button.appendChild( icon );
            button.addEventListener( 'click', function () {
                var expanded = button.getAttribute( 'aria-expanded' ) === 'true';
                controlledRows.forEach( function ( row ) {
                    row.classList.toggle( 'pdk-episodes__row--series-hidden', expanded );
                } );
                button.setAttribute( 'aria-expanded', expanded ? 'false' : 'true' );
                button.setAttribute(
                    'aria-label',
                    ( expanded ? 'Rozwiń: ' : 'Zwiń: ' ) + seasonName
                );
                button.title = expanded ? 'Rozwiń serię' : 'Zwiń serię';
            } );
            cell.appendChild( button );
        } );
     }
     }


Linia 174: Linia 218:
         }
         }
         headerRow.classList.add( 'pdk-episodes__head' );
         headerRow.classList.add( 'pdk-episodes__head' );
        initializeSeasonToggles( table, rows );


         var headerIndex = rowIndexes.get( headerRow );
         var headerIndex = rowIndexes.get( headerRow );
Linia 335: Linia 380:


         mobileControls = createMobileControls( table, descriptors, applySort );
         mobileControls = createMobileControls( table, descriptors, applySort );
    }
    function headingLevel( node ) {
        if ( node.matches( 'h2' ) || node.classList.contains( 'mw-heading2' ) ) {
            return 2;
        }
        if ( node.matches( 'h3' ) || node.classList.contains( 'mw-heading3' ) ) {
            return 3;
        }
        return 0;
    }
    function initializeSectionContainer( container ) {
        if ( container.dataset.pdkSectionsReady === '1' ) {
            return;
        }
        container.dataset.pdkSectionsReady = '1';
        var headings = Array.prototype.slice.call( container.children ).map(
            function ( node ) {
                return {
                    node: node,
                    level: headingLevel( node )
                };
            }
        ).filter( function ( heading ) {
            return heading.level > 0;
        } );
        headings.slice().reverse().forEach( function ( heading ) {
            var content = document.createElement( 'div' );
            var cursor = heading.node.nextSibling;
            sectionCounter += 1;
            content.className = 'pdk-section-content';
            content.id = 'pdk-section-content-' + sectionCounter;
            heading.node.parentNode.insertBefore( content, cursor );
            while ( cursor ) {
                var next = cursor.nextSibling;
                var nextLevel = cursor.nodeType === 1 ? headingLevel( cursor ) : 0;
                if ( nextLevel && nextLevel <= heading.level ) {
                    break;
                }
                content.appendChild( cursor );
                cursor = next;
            }
            var titleElement = heading.node.matches( 'h2, h3' ) ?
                heading.node : heading.node.querySelector( 'h2, h3' );
            if ( !titleElement ) {
                return;
            }
            var title = titleElement.textContent.trim();
            var button = document.createElement( 'button' );
            var icon = document.createElement( 'span' );
            button.type = 'button';
            button.className = 'pdk-section-toggle';
            button.setAttribute( 'aria-expanded', 'true' );
            button.setAttribute( 'aria-controls', content.id );
            button.setAttribute( 'aria-label', 'Zwiń sekcję: ' + title );
            button.title = 'Zwiń sekcję';
            icon.className = 'pdk-section-toggle__icon';
            icon.setAttribute( 'aria-hidden', 'true' );
            button.appendChild( icon );
            button.addEventListener( 'click', function () {
                var expanded = button.getAttribute( 'aria-expanded' ) === 'true';
                content.hidden = expanded;
                heading.node.classList.toggle( 'pdk-section-heading--collapsed', expanded );
                button.setAttribute( 'aria-expanded', expanded ? 'false' : 'true' );
                button.setAttribute(
                    'aria-label',
                    ( expanded ? 'Rozwiń sekcję: ' : 'Zwiń sekcję: ' ) + title
                );
                button.title = expanded ? 'Rozwiń sekcję' : 'Zwiń sekcję';
            } );
            heading.node.classList.add( 'pdk-section-heading' );
            heading.node.appendChild( button );
        } );
    }
    function initializeSections( root ) {
        var containers = [];
        if ( root.matches && root.matches( '.mw-parser-output' ) ) {
            containers.push( root );
        }
        Array.prototype.forEach.call(
            root.querySelectorAll( '.mw-parser-output' ),
            function ( container ) {
                if ( containers.indexOf( container ) === -1 ) {
                    containers.push( container );
                }
            }
        );
        containers.forEach( initializeSectionContainer );
     }
     }


Linia 343: Linia 482:
             initializeTable
             initializeTable
         );
         );
        initializeSections( root );
     }
     }



Wersja z 16:14, 11 sie 2026

/* global mw */
( function () {
    'use strict';

    var COLUMN_CLASSES = {
        number: 'pdk-episodes__number',
        date: 'pdk-episodes__date',
        title: 'pdk-episodes__title',
        original: 'pdk-episodes__original'
    };
    var sectionCounter = 0;

    function buildGrid( rows ) {
        var grid = [];
        rows.forEach( function ( row, rowIndex ) {
            var column = 0;
            grid[ rowIndex ] = grid[ rowIndex ] || [];
            Array.prototype.forEach.call( row.cells, function ( cell ) {
                var rowOffset;
                var columnOffset;
                while ( grid[ rowIndex ][ column ] ) {
                    column += 1;
                }
                for ( rowOffset = 0; rowOffset < cell.rowSpan; rowOffset += 1 ) {
                    grid[ rowIndex + rowOffset ] = grid[ rowIndex + rowOffset ] || [];
                    for ( columnOffset = 0; columnOffset < cell.colSpan; columnOffset += 1 ) {
                        grid[ rowIndex + rowOffset ][ column + columnOffset ] = cell;
                    }
                }
                column += cell.colSpan;
            } );
        } );
        return grid;
    }

    function makeGroups( dataRows ) {
        var groups = [];
        var index = 0;
        while ( index < dataRows.length ) {
            var firstRow = dataRows[ index ];
            var span = 1;
            Array.prototype.forEach.call( firstRow.cells, function ( cell ) {
                span = Math.max( span, cell.rowSpan || 1 );
            } );
            var groupRows = dataRows.slice( index, index + span );
            groupRows.forEach( function ( row, rowOffset ) {
                row.classList.add( 'pdk-episodes__row' );
                if ( rowOffset > 0 ) {
                    row.classList.add( 'pdk-episodes__continuation' );
                }
            } );
            groups.push( {
                rows: groupRows,
                originalIndex: groups.length
            } );
            index += span;
        }
        return groups;
    }

    function initializeSeasonToggles( table, rows ) {
        var seasons = rows.filter( function ( row ) {
            return row.classList.contains( 'pdk-episodes__season' );
        } );
        seasons.forEach( function ( season, seasonIndex ) {
            var start = rows.indexOf( season ) + 1;
            var nextSeason = seasons[ seasonIndex + 1 ];
            var end = nextSeason ? rows.indexOf( nextSeason ) : rows.length;
            var controlledRows = rows.slice( start, end ).filter( function ( row ) {
                return row.cells.length > 0;
            } );
            var cell = season.querySelector( 'th' );
            if ( !cell || cell.querySelector( '.pdk-episodes__season-toggle' ) ) {
                return;
            }

            var seasonName = cell.textContent.trim();
            var button = document.createElement( 'button' );
            var icon = document.createElement( 'span' );
            button.type = 'button';
            button.className = 'pdk-episodes__season-toggle';
            button.setAttribute( 'aria-expanded', 'true' );
            button.setAttribute( 'aria-label', 'Zwiń: ' + seasonName );
            button.title = 'Zwiń serię';
            icon.className = 'pdk-episodes__toggle-icon';
            icon.setAttribute( 'aria-hidden', 'true' );
            button.appendChild( icon );
            button.addEventListener( 'click', function () {
                var expanded = button.getAttribute( 'aria-expanded' ) === 'true';
                controlledRows.forEach( function ( row ) {
                    row.classList.toggle( 'pdk-episodes__row--series-hidden', expanded );
                } );
                button.setAttribute( 'aria-expanded', expanded ? 'false' : 'true' );
                button.setAttribute(
                    'aria-label',
                    ( expanded ? 'Rozwiń: ' : 'Zwiń: ' ) + seasonName
                );
                button.title = expanded ? 'Rozwiń serię' : 'Zwiń serię';
            } );
            cell.appendChild( button );
        } );
    }

    function parseDate( value ) {
        var exact = value.match( /\b(\d{1,2})[.\/-](\d{1,2})[.\/-](\d{4})\b/ );
        if ( exact ) {
            return Number( exact[ 3 ] ) * 10000 + Number( exact[ 2 ] ) * 100 + Number( exact[ 1 ] );
        }
        var month = value.match( /\b(\d{1,2})[.\/-](\d{4})\b/ );
        if ( month ) {
            return Number( month[ 2 ] ) * 10000 + Number( month[ 1 ] ) * 100;
        }
        var year = value.match( /\b(\d{4})\b/ );
        return year ? Number( year[ 1 ] ) * 10000 : null;
    }

    function textForColumn( group, column, grid, rowIndexes ) {
        var value = '';
        group.rows.some( function ( row ) {
            var rowIndex = rowIndexes.get( row );
            var cell = grid[ rowIndex ] && grid[ rowIndex ][ column ];
            var candidate = cell ? cell.textContent.trim() : '';
            if ( candidate ) {
                value = candidate;
                return true;
            }
            return false;
        } );
        return value;
    }

    function createMobileControls( table, descriptors, applySort ) {
        var sortable = descriptors.filter( function ( descriptor ) {
            return descriptor.sortType;
        } );
        if ( !sortable.length ) {
            return null;
        }

        var labels = {};
        sortable.forEach( function ( descriptor ) {
            labels[ descriptor.label ] = ( labels[ descriptor.label ] || 0 ) + 1;
        } );
        var seen = {};
        var controls = document.createElement( 'div' );
        controls.className = 'pdk-episodes__mobile-sort';

        var select = document.createElement( 'select' );
        select.setAttribute( 'aria-label', 'Sortuj listę odcinków' );
        var original = document.createElement( 'option' );
        original.value = '';
        original.textContent = 'Kolejność według serii';
        select.appendChild( original );

        sortable.forEach( function ( descriptor ) {
            seen[ descriptor.label ] = ( seen[ descriptor.label ] || 0 ) + 1;
            var option = document.createElement( 'option' );
            option.value = String( descriptor.column );
            option.textContent = labels[ descriptor.label ] > 1 ?
                descriptor.label + ' (' + seen[ descriptor.label ] + ')' : descriptor.label;
            select.appendChild( option );
        } );

        var direction = document.createElement( 'button' );
        direction.type = 'button';
        direction.disabled = true;
        direction.textContent = '↑';
        direction.title = 'Zmień kierunek sortowania';
        direction.setAttribute( 'aria-label', 'Zmień kierunek sortowania' );

        select.addEventListener( 'change', function () {
            if ( select.value === '' ) {
                direction.disabled = true;
                direction.textContent = '↑';
                applySort( null, 'original' );
                return;
            }
            direction.disabled = false;
            direction.textContent = '↑';
            applySort( Number( select.value ), 'ascending' );
        } );

        direction.addEventListener( 'click', function () {
            var next = direction.textContent === '↑' ? 'descending' : 'ascending';
            direction.textContent = next === 'ascending' ? '↑' : '↓';
            applySort( Number( select.value ), next );
        } );

        controls.appendChild( select );
        controls.appendChild( direction );
        table.parentNode.insertBefore( controls, table );

        return {
            select: select,
            direction: direction
        };
    }

    function initializeTable( table ) {
        if ( table.dataset.pdkEpisodesReady === '1' ) {
            return;
        }
        table.dataset.pdkEpisodesReady = '1';

        var rows = Array.prototype.slice.call( table.rows );
        var grid = buildGrid( rows );
        var rowIndexes = new Map();
        rows.forEach( function ( row, index ) {
            rowIndexes.set( row, index );
        } );

        var headerRow = rows.find( function ( row ) {
            return !row.classList.contains( 'pdk-episodes__season' ) &&
                row.querySelector( 'th[data-pdk-sort], th[data-pdk-column]' );
        } );
        if ( !headerRow ) {
            return;
        }
        headerRow.classList.add( 'pdk-episodes__head' );
        initializeSeasonToggles( table, rows );

        var headerIndex = rowIndexes.get( headerRow );
        var descriptors = [];
        var usedHeaders = new Set();
        ( grid[ headerIndex ] || [] ).forEach( function ( cell, column ) {
            if ( !cell || usedHeaders.has( cell ) ) {
                return;
            }
            usedHeaders.add( cell );
            descriptors.push( {
                cell: cell,
                column: column,
                sortType: cell.dataset.pdkSort || '',
                columnType: cell.dataset.pdkColumn || '',
                label: cell.textContent.trim()
            } );
        } );

        var columnCounts = {};
        descriptors.forEach( function ( descriptor ) {
            if ( descriptor.columnType ) {
                columnCounts[ descriptor.columnType ] =
                    ( columnCounts[ descriptor.columnType ] || 0 ) + 1;
            }
        } );

        var dataRows = rows.filter( function ( row ) {
            return row !== headerRow &&
                !row.classList.contains( 'pdk-episodes__season' ) &&
                row.cells.length > 0 &&
                !Array.prototype.some.call( row.cells, function ( cell ) {
                    return cell.tagName === 'TH';
                } );
        } );

        descriptors.forEach( function ( descriptor ) {
            dataRows.forEach( function ( row ) {
                var rowIndex = rowIndexes.get( row );
                var cell = grid[ rowIndex ] && grid[ rowIndex ][ descriptor.column ];
                if ( !cell || cell.tagName !== 'TD' ) {
                    return;
                }
                if ( descriptor.columnType && COLUMN_CLASSES[ descriptor.columnType ] ) {
                    cell.classList.add( COLUMN_CLASSES[ descriptor.columnType ] );
                    if ( columnCounts[ descriptor.columnType ] > 1 ) {
                        cell.classList.add( 'pdk-episodes__column--labelled' );
                    }
                }
                if ( !cell.dataset.pdkLabel ) {
                    cell.dataset.pdkLabel = descriptor.label;
                }
            } );
        } );

        var groups = makeGroups( dataRows );
        var originalRows = rows.map( function ( row ) {
            return { row: row, parent: row.parentNode };
        } );
        var collator = new Intl.Collator( 'pl', { numeric: true, sensitivity: 'base' } );
        var currentColumn = null;
        var currentDirection = 'original';
        var mobileControls;

        function restoreOriginalOrder() {
            originalRows.forEach( function ( entry ) {
                entry.parent.appendChild( entry.row );
            } );
        }

        function updateIndicators( column, direction ) {
            descriptors.forEach( function ( descriptor ) {
                descriptor.cell.removeAttribute( 'aria-sort' );
            } );
            if ( column !== null && direction !== 'original' ) {
                var active = descriptors.find( function ( descriptor ) {
                    return descriptor.column === column;
                } );
                if ( active ) {
                    active.cell.setAttribute( 'aria-sort', direction );
                }
            }
            if ( mobileControls ) {
                mobileControls.select.value = column === null ? '' : String( column );
                mobileControls.direction.disabled = column === null;
                mobileControls.direction.textContent = direction === 'descending' ? '↓' : '↑';
            }
        }

        function applySort( column, direction ) {
            currentColumn = column;
            currentDirection = direction;
            if ( column === null || direction === 'original' ) {
                restoreOriginalOrder();
                table.classList.remove( 'pdk-episodes--sorted' );
                updateIndicators( null, 'original' );
                return;
            }

            var descriptor = descriptors.find( function ( item ) {
                return item.column === column;
            } );
            if ( !descriptor ) {
                return;
            }
            var sorted = groups.slice().sort( function ( left, right ) {
                var leftText = textForColumn( left, column, grid, rowIndexes );
                var rightText = textForColumn( right, column, grid, rowIndexes );
                var leftValue = descriptor.sortType === 'date' ? parseDate( leftText ) : leftText;
                var rightValue = descriptor.sortType === 'date' ? parseDate( rightText ) : rightText;
                var leftMissing = leftValue === null || leftValue === '';
                var rightMissing = rightValue === null || rightValue === '';
                if ( leftMissing !== rightMissing ) {
                    return leftMissing ? 1 : -1;
                }
                var comparison = descriptor.sortType === 'date' ?
                    leftValue - rightValue : collator.compare( leftValue, rightValue );
                if ( comparison === 0 ) {
                    return left.originalIndex - right.originalIndex;
                }
                return direction === 'descending' ? -comparison : comparison;
            } );

            var parent = groups.length && groups[ 0 ].rows.length ?
                groups[ 0 ].rows[ 0 ].parentNode : null;
            if ( parent ) {
                sorted.forEach( function ( group ) {
                    group.rows.forEach( function ( row ) {
                        parent.appendChild( row );
                    } );
                } );
            }
            table.classList.add( 'pdk-episodes--sorted' );
            updateIndicators( column, direction );
        }

        descriptors.filter( function ( descriptor ) {
            return descriptor.sortType;
        } ).forEach( function ( descriptor ) {
            var button = document.createElement( 'button' );
            var icon = document.createElement( 'span' );
            button.type = 'button';
            button.className = 'pdk-episodes__sort';
            button.title = 'Sortuj według: ' + descriptor.label;
            button.setAttribute( 'aria-label', 'Sortuj według: ' + descriptor.label );
            icon.className = 'pdk-episodes__sort-icon';
            icon.setAttribute( 'aria-hidden', 'true' );
            button.appendChild( icon );
            button.addEventListener( 'click', function () {
                var nextDirection = 'ascending';
                if ( currentColumn === descriptor.column && currentDirection === 'ascending' ) {
                    nextDirection = 'descending';
                } else if ( currentColumn === descriptor.column && currentDirection === 'descending' ) {
                    applySort( null, 'original' );
                    return;
                }
                applySort( descriptor.column, nextDirection );
            } );
            descriptor.cell.appendChild( button );
        } );

        mobileControls = createMobileControls( table, descriptors, applySort );
    }

    function headingLevel( node ) {
        if ( node.matches( 'h2' ) || node.classList.contains( 'mw-heading2' ) ) {
            return 2;
        }
        if ( node.matches( 'h3' ) || node.classList.contains( 'mw-heading3' ) ) {
            return 3;
        }
        return 0;
    }

    function initializeSectionContainer( container ) {
        if ( container.dataset.pdkSectionsReady === '1' ) {
            return;
        }
        container.dataset.pdkSectionsReady = '1';

        var headings = Array.prototype.slice.call( container.children ).map(
            function ( node ) {
                return {
                    node: node,
                    level: headingLevel( node )
                };
            }
        ).filter( function ( heading ) {
            return heading.level > 0;
        } );

        headings.slice().reverse().forEach( function ( heading ) {
            var content = document.createElement( 'div' );
            var cursor = heading.node.nextSibling;
            sectionCounter += 1;
            content.className = 'pdk-section-content';
            content.id = 'pdk-section-content-' + sectionCounter;
            heading.node.parentNode.insertBefore( content, cursor );

            while ( cursor ) {
                var next = cursor.nextSibling;
                var nextLevel = cursor.nodeType === 1 ? headingLevel( cursor ) : 0;
                if ( nextLevel && nextLevel <= heading.level ) {
                    break;
                }
                content.appendChild( cursor );
                cursor = next;
            }

            var titleElement = heading.node.matches( 'h2, h3' ) ?
                heading.node : heading.node.querySelector( 'h2, h3' );
            if ( !titleElement ) {
                return;
            }
            var title = titleElement.textContent.trim();
            var button = document.createElement( 'button' );
            var icon = document.createElement( 'span' );
            button.type = 'button';
            button.className = 'pdk-section-toggle';
            button.setAttribute( 'aria-expanded', 'true' );
            button.setAttribute( 'aria-controls', content.id );
            button.setAttribute( 'aria-label', 'Zwiń sekcję: ' + title );
            button.title = 'Zwiń sekcję';
            icon.className = 'pdk-section-toggle__icon';
            icon.setAttribute( 'aria-hidden', 'true' );
            button.appendChild( icon );
            button.addEventListener( 'click', function () {
                var expanded = button.getAttribute( 'aria-expanded' ) === 'true';
                content.hidden = expanded;
                heading.node.classList.toggle( 'pdk-section-heading--collapsed', expanded );
                button.setAttribute( 'aria-expanded', expanded ? 'false' : 'true' );
                button.setAttribute(
                    'aria-label',
                    ( expanded ? 'Rozwiń sekcję: ' : 'Zwiń sekcję: ' ) + title
                );
                button.title = expanded ? 'Rozwiń sekcję' : 'Zwiń sekcję';
            } );
            heading.node.classList.add( 'pdk-section-heading' );
            heading.node.appendChild( button );
        } );
    }

    function initializeSections( root ) {
        var containers = [];
        if ( root.matches && root.matches( '.mw-parser-output' ) ) {
            containers.push( root );
        }
        Array.prototype.forEach.call(
            root.querySelectorAll( '.mw-parser-output' ),
            function ( container ) {
                if ( containers.indexOf( container ) === -1 ) {
                    containers.push( container );
                }
            }
        );
        containers.forEach( initializeSectionContainer );
    }

    function initialize( content ) {
        var root = content && content[ 0 ] ? content[ 0 ] : content || document;
        Array.prototype.forEach.call(
            root.querySelectorAll( 'table.js-pdk-episodes' ),
            initializeTable
        );
        initializeSections( root );
    }

    if ( typeof mw !== 'undefined' && mw.hook ) {
        mw.hook( 'wikipage.content' ).add( initialize );
    } else if ( document.readyState === 'loading' ) {
        document.addEventListener( 'DOMContentLoaded', function () {
            initialize( document );
        } );
    } else {
        initialize( document );
    }
}() );