Basculer le menu
Changer de menu des préférences
Basculer le menu personnel
Non connecté(e)
Votre adresse IP sera visible au public si vous faites des modifications.

« MediaWiki:Common.js » : différence entre les versions

Page de l’interface de MediaWiki
Hiob (discussion | contributions)
minicard
Hiob (discussion | contributions)
Aucun résumé des modifications
 
(Une version intermédiaire par le même utilisateur non affichée)
Ligne 298 : Ligne 298 :
     }
     }
});
});
/* ============================================================
* Nefald Page Search — Recherche dans le contenu de la page
* ============================================================ */
/* --- Interception globale de Ctrl+F / Cmd+F --- */
$( document ).on( 'keydown.nefaldPageSearch', function ( e ) {
    if ( ( e.ctrlKey || e.metaKey ) && e.key.toLowerCase() === 'f' ) {
        var $searchInput = $( '.nefald-page-search__input' );
       
        if ( $searchInput.length > 0 ) {
            e.preventDefault();
           
            /* Scrolle vers la barre si elle n'est pas sticky ou visible */
            $searchInput[0].scrollIntoView( { behavior: 'smooth', block: 'center' } );
           
            /* Donne le focus et sélectionne le texte pour une frappe rapide */
            $searchInput.focus().select();
        }
    }
} );
mw.hook( 'wikipage.content' ).add( function ( $content ) {
    $content.find( '.nefald-page-search-container' ).each( function () {
        var $container = $( this );
        if ( $container.children().length > 0 ) { return; }
        /* Mise à jour du placeholder par défaut pour indiquer le raccourci */
        var placeholder = $container.data( 'placeholder' ) || 'Rechercher dans la page (Ctrl+F)…';
        /* --- Injection du HTML --- */
        var html =
            '<div class="nefald-page-search">' +
                '<span class="nefald-page-search__icon">&#x2315;</span>' +
                '<input class="nefald-page-search__input" type="search"' +
                    ' placeholder="' + placeholder + '"' +
                    ' aria-label="Rechercher dans la page"' +
                    ' autocomplete="off" spellcheck="false" />' +
                '<span class="nefald-page-search__counter"></span>' +
                '<button class="nefald-page-search__prev" title="Résultat précédent">&#x2191;</button>' +
                '<button class="nefald-page-search__next" title="Résultat suivant">&#x2193;</button>' +
                '<button class="nefald-page-search__clear" title="Effacer">&#x2715;</button>' +
            '</div>';
        $container.append( html );
        /* --- Références DOM --- */
        var $input  = $container.find( '.nefald-page-search__input' );
        var $counter = $container.find( '.nefald-page-search__counter' );
        var $prev    = $container.find( '.nefald-page-search__prev' );
        var $next    = $container.find( '.nefald-page-search__next' );
        var $clear  = $container.find( '.nefald-page-search__clear' );
        /* Zone dans laquelle on cherche : le contenu principal */
        var $scope = $( '#mw-content-text .mw-parser-output' );
        var matches = [];
        var currentIndex = -1;
        var timer = null;
        var HIGHLIGHT_CLASS = 'nefald-search-highlight';
        var ACTIVE_CLASS    = 'nefald-search-highlight--active';
        /* --- Fonctions utilitaires --- */
        /**
        * Parcourt récursivement les nœuds texte d'un élément
        * en ignorant les scripts, styles et le widget lui-même.
        */
        function getTextNodes( root ) {
            var nodes = [];
            var walker = document.createTreeWalker(
                root,
                NodeFilter.SHOW_TEXT,
                {
                    acceptNode: function ( node ) {
                        var parent = node.parentNode;
                        if ( !parent ) { return NodeFilter.FILTER_REJECT; }
                        var tag = parent.nodeName.toLowerCase();
                        if ( tag === 'script' || tag === 'style' || tag === 'textarea' || tag === 'noscript' ) {
                            return NodeFilter.FILTER_REJECT;
                        }
                        /* Ignorer le widget de recherche lui-même */
                        if ( $( parent ).closest( '.nefald-page-search-container' ).length ) {
                            return NodeFilter.FILTER_REJECT;
                        }
                        /* Ignorer les nœuds vides */
                        if ( node.nodeValue.trim() === '' ) {
                            return NodeFilter.FILTER_REJECT;
                        }
                        return NodeFilter.FILTER_ACCEPT;
                    }
                }
            );
            while ( walker.nextNode() ) {
                nodes.push( walker.currentNode );
            }
            return nodes;
        }
        /**
        * Supprime tous les surlignages existants en restaurant le texte d'origine.
        */
        function clearHighlights() {
            $scope.find( '.' + HIGHLIGHT_CLASS ).each( function () {
                var parent = this.parentNode;
                parent.replaceChild( document.createTextNode( this.textContent ), this );
                parent.normalize();
            } );
            matches = [];
            currentIndex = -1;
            $counter.text( '' );
        }
        /**
        * Cherche `query` dans tous les nœuds texte et entoure
        * chaque occurrence d'un <mark>.
        */
        function doSearch( query ) {
            clearHighlights();
            if ( !query || query.length < 2 ) { return; }
            /* Échapper les caractères spéciaux pour la RegExp */
            var escaped = query.replace( /[.*+?^${}()|[\]\\]/g, '\\$&' );
            var regex = new RegExp( '(' + escaped + ')', 'gi' );
            var textNodes = getTextNodes( $scope[ 0 ] );
            textNodes.forEach( function ( node ) {
                var text = node.nodeValue;
                if ( !regex.test( text ) ) { return; }
                regex.lastIndex = 0; /* reset après test */
                var frag = document.createDocumentFragment();
                var lastIndex = 0;
                var match;
                while ( ( match = regex.exec( text ) ) !== null ) {
                    /* Texte avant le match */
                    if ( match.index > lastIndex ) {
                        frag.appendChild( document.createTextNode( text.slice( lastIndex, match.index ) ) );
                    }
                    /* Le match lui-même dans un <mark> */
                    var mark = document.createElement( 'mark' );
                    mark.className = HIGHLIGHT_CLASS;
                    mark.textContent = match[ 1 ];
                    frag.appendChild( mark );
                    lastIndex = regex.lastIndex;
                }
                /* Texte restant après le dernier match */
                if ( lastIndex < text.length ) {
                    frag.appendChild( document.createTextNode( text.slice( lastIndex ) ) );
                }
                node.parentNode.replaceChild( frag, node );
            } );
            matches = $scope.find( '.' + HIGHLIGHT_CLASS ).toArray();
            if ( matches.length > 0 ) {
                currentIndex = 0;
                goTo( 0 );
            } else {
                $counter.text( '0 résultat' );
            }
        }
        /**
        * Navigue vers le match d'index donné.
        */
        function goTo( index ) {
            if ( matches.length === 0 ) { return; }
            /* Retirer la classe active du précédent */
            $( matches ).removeClass( ACTIVE_CLASS );
            /* Boucler */
            if ( index < 0 ) { index = matches.length - 1; }
            if ( index >= matches.length ) { index = 0; }
            currentIndex = index;
            var el = matches[ currentIndex ];
            $( el ).addClass( ACTIVE_CLASS );
            /* Scroller vers l'élément actif */
            el.scrollIntoView( { behavior: 'smooth', block: 'center' } );
            $counter.text( ( currentIndex + 1 ) + ' / ' + matches.length );
        }
        /* --- Événements --- */
        $input.on( 'input', function () {
            clearTimeout( timer );
            var query = $input.val();
            /* Debounce de 250 ms pour ne pas spammer sur chaque frappe */
            timer = setTimeout( function () {
                doSearch( query );
            }, 250 );
        } );
        $next.on( 'click', function () {
            goTo( currentIndex + 1 );
        } );
        $prev.on( 'click', function () {
            goTo( currentIndex - 1 );
        } );
        $clear.on( 'click', function () {
            $input.val( '' ).trigger( 'focus' );
            clearHighlights();
        } );
        /* Raccourcis clavier dans l'input */
        $input.on( 'keydown', function ( e ) {
            if ( e.key === 'Enter' ) {
                e.preventDefault();
                if ( e.shiftKey ) {
                    goTo( currentIndex - 1 );
                } else {
                    goTo( currentIndex + 1 );
                }
            }
            if ( e.key === 'Escape' ) {
                $input.val( '' );
                clearHighlights();
                $input.trigger( 'blur' );
            }
        } );
    } );
} );
Les témoins (''cookies'') nous aident à fournir nos services. En utilisant nos services, vous acceptez notre utilisation de témoins.