Jump to content

MediaWiki:Gadget-ChartJs.js

From Creopedia

Note: After publishing, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
/**
 * Chart.js gadget for Module:ChartJs
 *
 * This script finds elements with the class 'site-chartjs-target',
 * loads Chart.js if it's not already available, and then renders a chart
 * based on the data attributes provided in the HTML.
 */

const CHART_TARGET_CLASS = 'site-chartjs-target';
const CHART_INITIALIZED_CLASS = 'site-chartjs-initialized';
const CHARTJS_URL = 'https://cdn.jsdelivr.net/npm/[email protected]/dist/chart.umd.min.js';

function init() {
	const elementsToAttach = document.querySelectorAll( `.${CHART_TARGET_CLASS}:not(.${CHART_INITIALIZED_CLASS})` );

	if ( elementsToAttach.length === 0 ) {
		return;
	}

	// Load the Chart.js library and then render the charts.
	loadChartJs().then( () => {
		elementsToAttach.forEach( renderChart );
	} );
}

/**
 * @returns {Promise} Resolves when the ChartJs library is loaded.
 */
function loadChartJs() {
	if ( typeof window.Chart !== 'undefined' ) {
		return Promise.resolve();
	}
	return mw.loader.getScript( CHARTJS_URL );
}

/**
 * @param {HTMLElement} element The target element for the chart.
 */
function renderChart( element ) {
	const { chartjsType, chartjsData, chartjsOptions } = element.dataset;

	if ( typeof chartjsType !== 'string' ) {
		mw.log.error( '[Gadget-ChartJs] No chart type specified for element:', element );
		return;
	}

	if ( typeof chartjsData !== 'string' ) {
		mw.log.error( '[Gadget-ChartJs] No chart data specified for element:', element );
		return;
	}

	let data, options;

	try {
		data = JSON.parse( decodeJsonString( chartjsData ) );
		options = chartjsOptions ? JSON.parse( decodeJsonString( chartjsOptions ) ) : {};
	} catch ( e ) {
		mw.log.error( '[Gadget-ChartJs] Invalid JSON in data attributes for element:', element, e );
		// Mark as initialized to avoid trying again.
		element.classList.add( CHART_INITIALIZED_CLASS );
		return;
	}

	const canvas = document.createElement( 'canvas' );
	const config = {
		type: chartjsType,
		data: data,
		options: options
	};

	new window.Chart( canvas, config );

	element.innerHTML = '';
	element.appendChild( canvas );

	element.removeAttribute( 'data-chartjs-type' );
	element.removeAttribute( 'data-chartjs-data' );
	element.removeAttribute( 'data-chartjs-options' );

	element.classList.add( CHART_INITIALIZED_CLASS );
}

/**
 * See Module:ChartJs for details.
 *
 * @return string
 */
function decodeJsonString( str ) {
	return str.replace( /😩/g, '{' ).replace( /😒/g, '}' );
}

// Initialize on initial page load.
mw.hook( 'wikipage.content' ).add( init );

// Re-initialize after a VisualEditor edit.
mw.loader.using( 'ext.visualEditor.desktopArticleTarget.init' ).then( () => {
	mw.hook( 'postEdit' ).add( init );
} );