/*
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 = $( `
		<i class="fa-regular fa-clone fa-rotate-90 cbutton"></i>
		<span class="tooltip2">
			<span class="hover">Click = Copy</span>
			<span class="copied">
				<i class="fa-solid fa-check"></i>
				Copied to clipboard!
			</span>
		</span>
		<span class="code"></span>
	`);
	
	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 = $('<div class="code-select-target-helper"></div>');
				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/) ) {
				$(`<img class="cbutton" ${ codeSelect.hasClass('image-eager') ? '' : 'decoding="async" loading="lazy"' } src="${imageSrc}" />`).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]]
*/