[go: up one dir, main page]

Jump to content

User:Andrybak/Scripts/copy-section-link.js

From Wikipedia, the free encyclopedia
Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.
// Fixed version of [[User:Enterprisey/copy-section-link.js]]
// <nowiki>
$.when(
    $.ready,
    mw.loader.using( [ "mediawiki.util", "oojs-ui-core", "oojs-ui-widgets" ] )
).then( function () {
	/*
	 * The convoluted function is needed, because there are different HTML
	 * layouts for "headings" in different skins.
	 * In Vector 2022, layout of ==Second level== versus ===Third level===
	 * headings is different even for a _single_ skin.
	 *
	 * The HTML layout is either
	 *    <div><h*></h*><edit section link /></div>
	 * or
	 *    <h*><edit section link /></h*>
	 *
	 * For details, see:
	 *   - https://www.mediawiki.org/w/index.php?title=Heading_HTML_changes&oldid=6538029
	 */
	// Returns a plain HTMLElement
	function findEditSectionForHeader(header) {
		// in Vector, the bracketed [edit] section link is a direct child element/node
		const maybeVectorEditSection = header.querySelector('.mw-editsection');
		if (maybeVectorEditSection) {
			return maybeVectorEditSection;
		}
		// in other skins, the bracketed [edit] section link is a sibling of <h2> <h3> etc
		if (header.parentElement.classList.contains('mw-heading')) {
			const maybeEditSection = header.parentElement.querySelector('.mw-editsection');
			if (maybeEditSection) {
				return maybeEditSection;
			}
		}
		return null;
	}

	/*
	 * See [[Module:Section link#L-11--L-15]]
	 */
	function escapeHash(hash) {
		return hash
			.replaceAll('{', '&#x7B;')
			.replaceAll('}', '&#x7D;');
	}
	
	/*
	 * The search for all section headings starts with
	 * finding all <h*> tags, which aren't for the table of contents.
	 * From the <h*> tags, we find the "[edit] section links".
	 */
	const allHeaders = $("#mw-content-text .mw-parser-output").find(":header").filter(':not(#mw-toc-heading)');

    allHeaders.each( function(i, header) {
        var popup = null;
        var editSection = findEditSectionForHeader(header);
        let target;
        if (editSection === null) {
        	target = $(header);
        	console.warn('[copy-section-link]', 'edit section link not found:', target);
        } else {
        	target = $(editSection);
        }
        target.append($( "<a>", { "class": "copy-section-link-pilcrow" } )
                .text( "¶" )
                .click( function () {
                    if( popup === null ) {
                        const hash = escapeHash(header.id ? header.id : header.querySelector('.mw-headline')?.id);
                        var oldid = mw.util.getParamValue( "oldid" );
                        var popupContent;
                        function makeContent( pageName, id ) {
                            var wikitext = (pageName + "#" + hash).replace( /_/g, " " );
                            if( !window.copySectionLinkNoSquareBrackets ) {
                               wikitext = '[[' + wikitext + ']]';
                            }
                            return $( '<p>', { "class": "copy-section-link-content" } ).append(
                                $( "<code>", { "id": "copy-section-wikilink" + id } ).text( wikitext ),
                                $( "<button>" )
                                    .text( "Copy" )
                                    .css( { "padding": "0.5em", "cursor": "pointer", "margin-left": "0.5em" } )
                                    .click( function () {
                                        var textField = $( this ).prev();
                                        try {
                                            navigator.clipboard.writeText( textField.text() );
                                        } catch( e ) {
                                            textField.select();
                                            document.execCommand( "copy" );
                                        }
                                    } ),
                                $( "<br>" ),
                                $( "<a>" )
                                	.attr( "href", mw.util.getUrl( pageName ) + "#" + encodeURIComponent( hash ) )
                                	.text( "external" )
                            );
                        }

                        var generalCss = { 'font-size': '0.9rem', 'font-family': 'sans-serif' };

                        var index;
                        if( oldid ) {
                            popupContent = makeContent( "Special:Permalink/" + oldid );
                            popupContent.css( generalCss );
                            popupContent.css( { 'padding-top': '0.5em', 'font-weight': 'normal' } );
                        } else {
                            var normalPanel = new OO.ui.TabPanelLayout( 'normal', {
                                label: 'Link',
                                $content: makeContent( mw.config.get( 'wgPageName' ), 'normal' )
                            } );
                            var permalinkPanel = new OO.ui.TabPanelLayout( 'permalink', {
                                label: 'Permalink',
                                $content: makeContent( 'Special:Permalink/' + mw.config.get( 'wgCurRevisionId' ), 'permalink' )
                            } );
                            index = new OO.ui.IndexLayout();
                            index.addTabPanels( [ normalPanel, permalinkPanel ] );
                            popupContent = index.$element;
                        }
                        popup = new OO.ui.PopupWidget( {
                            $content: popupContent,
                            $floatableContainer: $( this ),
                            padded: true,
                            width: 400,
                            height: 190,
                            align: 'forwards',
                        } );
                        $( this ).after( popup.$element );
                        if( index ) {
                            index.$menu.find( 'span.oo-ui-labelElement-label' ).css( generalCss );
                            index.$content.css( generalCss );
                        }

                        popup.toggle( true );
                    } else {
                        popup.toggle();
                    }
                } ) );
    } );
    mw.util.addCSS( ".copy-section-link-pilcrow" +
                        "{ display: none; margin-left: 1em; font-size: initial; }" +
                    ".mw-heading:hover .copy-section-link-pilcrow, " +
                    "h1:hover .copy-section-link-pilcrow, h2:hover .copy-section-link-pilcrow, h3:hover .copy-section-link-pilcrow," +
                    "h4:hover .copy-section-link-pilcrow, h5:hover .copy-section-link-pilcrow, h6:hover .copy-section-link-pilcrow" +
                        "{ display: inline; }" +
                    ".copy-section-link-pilcrow + .oo-ui-widget" +
                        "{ font-weight: normal; }" );
} );
// </nowiki>