/* CodeSelect Gadget - JS Initialisation, Setup, Events. Activates all .code-select fields on the site. By clicking the icon the code is copied to clipboard and tooltip is changed See detailed documentation in Dev/mediawiki deferrable:NO -- Because other scripts are dependent on it */ (function () { // -------- // Settings var targetPadding = 5; // only relevant for use with target. Distance from target borders right and top var defaultLang = 'bash'; // other option: 'none'. Or languages which are supported for highlighting (see documentation) // Additional function names that should also be highlighted for bash (currently outsourced to MediaWiki:CodeSelectHighlightList) var highlightBashAdditionalFunctionNames = window.mwDev ? window.mwDev.data.app.codeSelectHighlightBashAdditionalFunctionNames : []; // ------ // Global var innerContent = $( ` Click = Copy Copied to clipboard! `); var cbuttonIcons = { normal: 'fa-regular fa-clone fa-rotate-90', copied: 'fa-solid fa-check' }; var highlightLangBashIsExtended = false; function extendHighlightLangBash() { if( ! highlightLangBashIsExtended ) { // Extend language bash with extra keywords let bashFnPattern = Prism.languages.bash.function.pattern; // We're using apt as a fixpoint, because it's very common and it's surrounded by pipes | let newBashFnPattern = new RegExp( bashFnPattern.source.replace( '|apt|', '|' + ['apt', ...highlightBashAdditionalFunctionNames].join('|') + '|' ), bashFnPattern.flags ); Prism.languages.bash = Prism.languages.extend( 'bash', { 'function': { lookbehind: true, pattern: newBashFnPattern }} ); highlightLangBashIsExtended = true; } } function markSelection( jqDom ) { let selection = window.getSelection(); selection.removeAllRanges(); let range = document.createRange(); range.selectNodeContents( jqDom[0] ); selection.addRange(range); } // ---------------- // jQuery Extension $.fn.codeSelect = function( action ) { // Only allow 'init' at the moment (extendable later) if( action != 'init' ) return; $(this).each( function() { // Prevent double initialization if( $(this).hasClass('js-fully-loaded') ) return; // Setup const codeSelect = $(this); const htmlMode = codeSelect.hasClass('insert-mode-html'); let content = codeSelect[ htmlMode ? 'html' : 'text' ](); codeSelect .empty() .append( innerContent.clone() ) .find('.code')[ htmlMode ? 'html' : 'text' ]( content ); let target = codeSelect.attr('data-target'); if( target && $(target).length ) { target = $(target).eq(0); // If we have a target and no content (only white space) was given, copy target's content if( ! content.match(/\S/) ) codeSelect.find('.code').text( target.text() ); let helper = $('
'); helper.append( codeSelect ); // Position over target codeSelect.css( { position: 'absolute', top: targetPadding - target.outerHeight() - ( parseInt( target.css('marginBottom') ) || 0 ), right: targetPadding, }); helper.insertAfter( target ); // Fixate styles from the target to prevent content shift let targetStyles = window.getComputedStyle( target[0] ); let getStyle = (name) => targetStyles.getPropertyValue(name); target.css( { margin: getStyle('margin'), lineHeight: getStyle('line-height'), padding: getStyle('padding'), fontSize: getStyle('font-size') } ); if( ! codeSelect.attr('data-button-image-src' ) ) codeSelect.attr('data-button-image-src',''); } else target = null; let imageButton = false; let imageSrc = codeSelect.attr('data-button-image-src'); if( imageSrc && imageSrc.match(/\S/) ) { $(``).insertAfter( codeSelect.find('.cbutton') ); codeSelect.find('i.cbutton').remove(); imageButton = true; } let cbutton = codeSelect.find('.cbutton'); let tooltip = codeSelect.find('.tooltip2'); // Highlighting const lang = codeSelect.attr('data-language') || defaultLang; if( htmlMode || lang == 'none' ) { codeSelect.find('.code').addClass( 'language-none' ); } else { let syntaxElement; // If CodeSelect is icon or imageButton no highlight for code box needed if( imageSrc == undefined ) { syntaxElement = codeSelect.find('.code')[0]; codeSelect.find('.code').addClass( 'language-' + lang ); } // If target then only highlight target if( target ) { syntaxElement = target[0]; target.addClass( 'language-' + lang ); } const highlight = function() { extendHighlightLangBash(); // Execute highlight if( syntaxElement ) Prism.highlightElement( syntaxElement ); }; // Execute if dependency is ready if not do it later on event basis if( Prism ) highlight(); else $('#highlight-code-script').on( 'load', highlight ); } // Events cbutton.on('click', function() { // copying markSelection( codeSelect.find('.code') ); document.execCommand("copy"); // feedback and closing codeSelect.addClass('copied'); if( ! imageButton ) cbutton.removeClass( cbuttonIcons.normal ).addClass( cbuttonIcons.copied ); setTimeout(f1, 3000); function f1() { tooltip.fadeOut( 600, f2 ); } function f2() { codeSelect.removeClass('copied'); if( ! imageButton ) cbutton.removeClass( cbuttonIcons.copied ).addClass( cbuttonIcons.normal ); setTimeout( f3, 200 ); } function f3() { tooltip.css( 'display', '' ); } }); codeSelect.find('.code').on('click', function() { markSelection( $(this).parent().find('.code') ); }); if( target ) { target.on( 'click', function() { markSelection( target ); }); } codeSelect.addClass('js-fully-loaded'); }); }; // ------------------------------------- // Enrich selection of standard elements $('.code-select').codeSelect('init'); })(); /* [[Category:MultiWiki]] */