Source: skins.minerva.scripts/page-issues/parser.js

( function ( M ) {
	/**
	 * @typedef PageIssue
	 * @prop {string} severity A SEVERITY_LEVEL key.
	 * @prop {boolean} grouped True if part of a group of multiple issues, false if singular.
	 * @prop {Icon} icon
	 */
	/**
	 * @typedef {Object} IssueSummary
	 * @prop {PageIssue} issue
	 * @prop {jQuery.Object} $el where the issue was extracted from
	 * @prop {string} iconString a string representation of icon.
	 *  This is kept for template compatibility (our views do not yet support composition).
	 * @prop {string} text HTML string.
	*/

	var Icon = M.require( 'mobile.startup' ).Icon,
		// Icons are matching the type selector below use a TYPE_* icon. When unmatched, the icon is
		// chosen by severity. Their color is always determined by severity, too.
		ICON_NAME = {
			// Generic severity icons.
			SEVERITY: {
				DEFAULT: 'issue-generic',
				LOW: 'issue-severity-low',
				MEDIUM: 'issue-severity-medium',
				HIGH: 'issue-generic'
			},

			// Icons customized by type.
			TYPE: {
				MOVE: 'issue-type-move',
				POINT_OF_VIEW: 'issue-type-point-of-view'
			}
		},
		ICON_COLOR = {
			DEFAULT: 'defaultColor',
			LOW: 'lowColor',
			MEDIUM: 'mediumColor',
			HIGH: 'highColor'
		},
		// How severities order and compare from least to greatest. For the multiple issues
		// template, severity should be considered the maximum of all its contained issues.
		SEVERITY_LEVEL = {
			DEFAULT: 0,
			LOW: 1,
			MEDIUM: 2,
			HIGH: 3
		},
		// Match the template's color CSS selector to a severity level concept. Derived via the
		// Ambox templates and sub-templates for the top five wikis and tested on page issues
		// inventory:
		// - https://people.wikimedia.org/~jdrewniak/page_issues_inventory
		// - https://en.wikipedia.org/wiki/Template:Ambox
		// - https://es.wikipedia.org/wiki/Plantilla:Metaplantilla_de_avisos
		// - https://ja.wikipedia.org/wiki/Template:Ambox
		// - https://ru.wikipedia.org/wiki/Шаблон:Ambox
		// - https://it.wikipedia.org/wiki/Template:Avviso
		// Severity is the class associated with the color. The ResourceLoader config mimics the
		// idea by using severity for color variants. Severity is determined independently of icons.
		// These selectors should be migrated to their templates.
		SEVERITY_REGEX = {
			// recommended (T206177), en, it
			LOW: /mobile-issue-severity-low|ambox-style|avviso-stile/,
			// recommended, en, it
			MEDIUM: /mobile-issue-severity-medium|ambox-content|avviso-contenuto/,
			// recommended, en, en, es / ru, it
			HIGH: /mobile-issue-severity-high|ambox-speedy|ambox-delete|ambox-serious|avviso-importante/
			// ..And everything else that doesn't match should be considered DEFAULT.
		},
		// As above but used to identify specific templates requiring icon customization.
		TYPE_REGEX = {
			// recommended (opt-in) / en, es / ru, it (long term only recommended should be used)
			MOVE: /mobile-issue-move|ambox-converted|ambox-move|ambox-merge|avviso-struttura/,
			POINT_OF_VIEW: new RegExp( [
				// recommended (opt-in)
				'mobile-issue-pov',
				// FIXME: en classes: plan to remove these provided can get adoption of recommended
				'ambox-Advert',
				'ambox-autobiography',
				'ambox-believerpov',
				'ambox-COI',
				'ambox-coverage',
				'ambox-criticism',
				'ambox-fanpov',
				'ambox-fringe-theories',
				'ambox-geographical-imbalance',
				'ambox-globalize',
				'ambox-npov-language',
				'ambox-POV',
				'ambox-pseudo',
				'ambox-systemic-bias',
				'ambox-unbalanced',
				'ambox-usgovtpov'
			].join( '|' ) )
			// ..And everything else that doesn't match is mapped to a "SEVERITY" type.
		},
		GROUPED_PARENT_REGEX = /mw-collapsible-content/,
		// Variants supported by specific types. The "severity icon" supports all severities but the
		// type icons only support one each by ResourceLoader.
		TYPE_SEVERITY = {
			MOVE: 'DEFAULT',
			POINT_OF_VIEW: 'MEDIUM'
		};

	/**
	 * @param {Element} box
	 * @return {string} An SEVERITY_SELECTOR key.
	 */
	function parseSeverity( box ) {
		var severity, identified;
		identified = Object.keys( SEVERITY_REGEX ).some( function ( key ) {
			var regex = SEVERITY_REGEX[ key ];
			severity = key;
			return regex.test( box.className );
		} );
		return identified ? severity : 'DEFAULT';
	}

	/**
	 * @param {Element} box
	 * @param {string} severity An SEVERITY_LEVEL key.
	 * @return {{name: string, severity: string}} An ICON_NAME.
	 */
	function parseType( box, severity ) {
		var identified, identifiedType;
		identified = Object.keys( TYPE_REGEX ).some( function ( type ) {
			var regex = TYPE_REGEX[ type ];
			identifiedType = type;
			return regex.test( box.className );
		} );
		return {
			name: identified ? ICON_NAME.TYPE[ identifiedType ] : ICON_NAME.SEVERITY[ severity ],
			severity: identified ? TYPE_SEVERITY[ identifiedType ] : severity
		};
	}

	/**
	 * @param {Element} box
	 * @return {boolean} True if part of a group of multiple issues, false if singular.
	 */
	function parseGroup( box ) {
		return !!box.parentNode && GROUPED_PARENT_REGEX.test( box.parentNode.className );
	}

	/**
	 * @param {Element} box
	 * @param {string} severity An SEVERITY_LEVEL key.
	 * @return {string} A severity or type ISSUE_ICON.
	 */
	function iconName( box, severity ) {
		var nameSeverity = parseType( box, severity );
		// The icon with color variant as expected by ResourceLoader,
		// {iconName}-{severityColorVariant}.
		return nameSeverity.name + '-' + ICON_COLOR[ nameSeverity.severity ];
	}

	/**
	 * @param {string[]} severityLevels an array of SEVERITY_KEY values.
	 * @return {string} The greatest SEVERITY_LEVEL key.
	 */
	function maxSeverity( severityLevels ) {
		return severityLevels.reduce( function ( max, severity ) {
			return SEVERITY_LEVEL[ max ] > SEVERITY_LEVEL[ severity ] ? max : severity;
		}, 'DEFAULT' );
	}

	/**
	 * @param {Element} box
	 * @return {PageIssue}
	 */
	function parse( box ) {
		var severity = parseSeverity( box );
		return {
			severity: severity,
			grouped: parseGroup( box ),
			icon: new Icon( {
				glyphPrefix: 'minerva',
				name: iconName( box, severity )
			} )
		};
	}

	/**
	 * Extract a summary message from a cleanup template generated element that is
	 * friendly for mobile display.
	 * @param {Object} $box element to extract the message from
	 * @return {IssueSummary}
	 */
	function extract( $box ) {
		var SELECTOR = '.mbox-text, .ambox-text',
			$container = $( '<div>' ),
			pageIssue;

		$box.find( SELECTOR ).each( function () {
			var contents,
				$this = $( this );
			// Clean up talk page boxes
			$this.find( 'table, .noprint' ).remove();
			contents = $this.html();

			if ( contents ) {
				$( '<p>' ).html( contents ).appendTo( $container );
			}
		} );

		pageIssue = parse( $box.get( 0 ) );

		return {
			issue: pageIssue,
			$el: $box,
			// For template compatibility with PageIssuesOverlay
			iconString: pageIssue.icon.toHtmlString(),
			text: $container.html()
		};
	}

	/**
	 * @module skins.minerva.scripts/utils
	 */
	module.exports = {
		extract: extract,
		parse: parse,
		maxSeverity: maxSeverity,
		iconName: iconName,
		test: {
			parseSeverity: parseSeverity,
			parseType: parseType,
			parseGroup: parseGroup
		}
	};

}( mw.mobileFrontend ) );