MinervaNeue/resources/skins.minerva.scripts/page-issues/parser.js
jdlrobson 72df451bd3 Embrace packageFiles
Help with readability by using module.exports and require rather than the MobileFrontend
provided mw.mobileFrontend module manager (and avoid adopting webpack at this time)

Replace usages of mw.mobileFrontend.require with local require and module.exports
(compatible with RL or Node implementation)

Changes:
* Notifications modules are merged into skins.minerva.scripts and initialised
via a client side check.
* new file overlayManager for exporting an overlayManager singleton
rather than being hidden inside resources/skins.minerva.scripts/init.js
* All M.define/M.requires swapped out for require where possible
The `define` method is now forbidden in the repo.

Bug: T212944
Change-Id: I44790dd3fc6fe42bb502d79c39c4081c223bf2b1
2019-07-16 18:04:10 +00:00

233 lines
7.0 KiB
JavaScript

( 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 ) );