Update: page issue icons
- Fix a bug where the all issues endpoint would incorrectly collect issues from all sections. - Update the page issue iconography. This increases the size of the delivered code and images by 1743 B minified uncompressed according to mw.inspect() (from 16.4 KiB to 18.1 KiB). - Add support for identifying page issue severity based on template CSS classes. - For multiple issues templates, show the highest priority icon. Bug: T191528 Change-Id: Ie0a4c83ec7cfb856ec581d058797109746e3cb99
|
@ -82,12 +82,12 @@ class MinervaHooks {
|
||||||
'targets' => [ 'mobile', 'desktop' ],
|
'targets' => [ 'mobile', 'desktop' ],
|
||||||
'scripts' => [
|
'scripts' => [
|
||||||
// additional scaffolding (minus initialisation scripts)
|
// additional scaffolding (minus initialisation scripts)
|
||||||
'resources/skins.minerva.scripts/utils.js',
|
'resources/skins.minerva.scripts/pageIssueParser.js',
|
||||||
'resources/skins.minerva.scripts/DownloadIcon.js',
|
'resources/skins.minerva.scripts/DownloadIcon.js',
|
||||||
'resources/skins.minerva.scripts/AB.js',
|
'resources/skins.minerva.scripts/AB.js',
|
||||||
// test files
|
// test files
|
||||||
'tests/qunit/skins.minerva.scripts/test_DownloadIcon.js',
|
'tests/qunit/skins.minerva.scripts/test_DownloadIcon.js',
|
||||||
'tests/qunit/skins.minerva.scripts/test_utils.js',
|
'tests/qunit/skins.minerva.scripts/test_pageIssueParser.js',
|
||||||
'tests/qunit/skins.minerva.scripts/test_AB.js',
|
'tests/qunit/skins.minerva.scripts/test_AB.js',
|
||||||
'tests/qunit/skins.minerva.notifications.badge/test_NotificationBadge.js'
|
'tests/qunit/skins.minerva.notifications.badge/test_NotificationBadge.js'
|
||||||
],
|
],
|
||||||
|
|
|
@ -84,6 +84,10 @@ table.ambox {
|
||||||
left: -8px;
|
left: -8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mw-ui-icon:before {
|
||||||
|
.background-size(75%, auto);
|
||||||
|
}
|
||||||
|
|
||||||
.ambox-learn-more {
|
.ambox-learn-more {
|
||||||
color: @linkColor;
|
color: @linkColor;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
|
Before Width: | Height: | Size: 335 B After Width: | Height: | Size: 335 B |
Before Width: | Height: | Size: 448 B After Width: | Height: | Size: 448 B |
|
@ -1 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?><svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><title>ambox-content</title><g id="ambox-content" fill="none" fill-rule="evenodd"><path d="M10 0C4.477 0 0 4.477 0 10s4.477 10 10 10 10-4.477 10-10S15.523 0 10 0zm1 16H9v-2h2v2zm0-4H9V4h2v8z" id="Page-1" fill="#FF5D01"/></g></svg>
|
|
Before Width: | Height: | Size: 328 B |
|
@ -1 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?><svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><title>ambox-default</title><g id="ambox-default" fill="none" fill-rule="evenodd"><path d="M19.638 17.36L11.528 3.3a1.85 1.85 0 0 0-1.53-1.09 1.85 1.85 0 0 0-1.52 1.09L.358 17.36C-.482 18.81.208 20 1.878 20h16.24c1.67 0 2.36-1.19 1.52-2.64zm-8.64-.36h-2v-2h2v2zm0-4h-2V7h2v6z" id="Page-1" fill="#54595D"/></g></svg>
|
|
Before Width: | Height: | Size: 413 B |
|
@ -1 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?><svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><title>ambox-speedy</title><g id="ambox-speedy" fill="none" fill-rule="evenodd"><path d="M19.638 17.36L11.528 3.3a1.85 1.85 0 0 0-1.53-1.09 1.85 1.85 0 0 0-1.52 1.09L.358 17.36C-.482 18.81.208 20 1.878 20h16.24c1.67 0 2.36-1.19 1.52-2.64zm-8.64-.36h-2v-2h2v2zm0-4h-2V7h2v6z" id="Page-1" fill="#D33"/></g></svg>
|
|
Before Width: | Height: | Size: 408 B |
|
@ -1 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?><svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><title>ambox-move</title><g id="ambox-move" fill="none" fill-rule="evenodd"><path id="left" fill="#36C" d="M18 13H8V9l-6 5 6 5v-4h10z"/><path id="left-copy" fill="#D33" transform="rotate(-180 10 6)" d="M18 5H8V1L2 6l6 5V7h10z"/></g></svg>
|
|
Before Width: | Height: | Size: 336 B |
|
@ -1 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?><svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><title>ambox-content.ambox-pov</title><g id="ambox-content.ambox-pov" fill="none" fill-rule="evenodd"><path d="M5 9L1 4h8L5 9zm-2.659 5.251l-.592-1.91 16.41-5.092.592 1.91-16.41 5.092zM15 12l4 5h-8l4-5z" id="Combined-Shape" fill="#54595D"/></g></svg>
|
|
Before Width: | Height: | Size: 348 B |
|
@ -1 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?><svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><title>ambox-protection</title><g id="ambox-protection" fill="none" fill-rule="evenodd"><path d="M16.07 8H15V5s0-5-5-5-5 5-5 5v3H3.93A1.93 1.93 0 0 0 2 9.93v8.15A1.93 1.93 0 0 0 3.93 20h12.14A1.93 1.93 0 0 0 18 18.07V9.93A1.93 1.93 0 0 0 16.07 8zM10 16a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm3-8H7V5.5C7 4 7 2 10 2s3 2 3 3.5V8z" id="Page-1" fill="#54595D"/></g></svg>
|
|
Before Width: | Height: | Size: 455 B |
|
@ -1 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?><svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><title>ambox-speedy</title><g id="ambox-speedy" fill="none" fill-rule="evenodd"><path d="M19.638 17.36L11.528 3.3a1.85 1.85 0 0 0-1.53-1.09 1.85 1.85 0 0 0-1.52 1.09L.358 17.36C-.482 18.81.208 20 1.878 20h16.24c1.67 0 2.36-1.19 1.52-2.64zm-8.64-.36h-2v-2h2v2zm0-4h-2V7h2v6z" id="Page-1" fill="#D33"/></g></svg>
|
|
Before Width: | Height: | Size: 408 B |
|
@ -1 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?><svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><title>ambox-style</title><g id="ambox-style" fill="none" fill-rule="evenodd"><g id="Group" transform="translate(2 1)"><path d="M16.09 10.44c-1.37-.326-3.422-2.752-4.825-4.612l-8.81 6.155 2.33 3.335c.816 1.169 2.28 1.81 4.02 1.66a23.12 23.12 0 0 1-1.583-1.988.357.357 0 0 1 .585-.409c.336.48 1.083 1.467 1.849 2.266.838-.189 1.722-.553 2.615-1.121-1.297-.822-2.184-2.354-2.23-2.436a.357.357 0 0 1 .619-.355c.011.02.991 1.709 2.271 2.347.931-.66 1.61-1.302 2.102-1.891-2.096-.398-3.142-1.86-3.194-1.934a.357.357 0 0 1 .584-.41c.009.012.353.49 1.023.936.63.42 1.326.673 2.075.759.586-.88.758-1.583.807-1.961a.31.31 0 0 0-.239-.341z" id="Combined-Shape" fill="#F5A623" fill-rule="nonzero"/><path d="M9.716 3.65a.928.928 0 0 0-1.293-.229L6.024 5.096 2.65.265a.62.62 0 0 0-.862-.153L.265 1.175a.619.619 0 0 0-.153.862l3.375 4.832-2.333 1.63a.928.928 0 0 0-.23 1.292l1.212 1.735 8.796-6.144A44.037 44.037 0 0 1 9.716 3.65z" id="Shape" fill="#774B20"/></g></g></svg>
|
|
Before Width: | Height: | Size: 1.0 KiB |
|
@ -0,0 +1 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"><title>alert</title><path d="M19.64 16.36L11.53 2.3A1.85 1.85 0 0 0 10 1.21 1.85 1.85 0 0 0 8.48 2.3L.36 16.36C-.48 17.81.21 19 1.88 19h16.24c1.67 0 2.36-1.19 1.52-2.64zM11 16H9v-2h2zm0-4H9V6h2z"/></svg>
|
After Width: | Height: | Size: 324 B |
|
@ -0,0 +1 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"><title>notice</title><path d="M10 0a10 10 0 1 0 10 10A10 10 0 0 0 10 0zm1 16H9v-2h2zm0-4H9V4h2z"/></svg>
|
After Width: | Height: | Size: 225 B |
|
@ -0,0 +1 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?><svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><g><path d="M6 8l2-3 2-3H2l2 3z"/><path d="M14 12l-2 3-2 3h8l-2-3z"/><path transform="rotate(-22.5 10 10)" d="M1.5 9h17v2h-17z"/></g></svg>
|
After Width: | Height: | Size: 250 B |
|
@ -0,0 +1 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?><svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 20 20"><style>.st0{clip-path:url(#SVGID_2_);fill:#f5a623}.st1{clip-path:url(#SVGID_4_);fill:#774b20}</style><g><g><defs><path id="SVGID_1_" d="M17.9 11.9c-1.4-.3-3.4-2.8-4.8-4.6l-8.8 6.2 2.3 3.3c.8 1.2 2.3 1.8 4 1.7-.8-.9-1.5-1.9-1.6-2-.1-.2 0-.4.1-.5.2-.1.4-.1.5.1.3.5 1.1 1.5 1.8 2.3.8-.2 1.7-.6 2.6-1.1-1.3-.8-2.2-2.4-2.2-2.4-.1-.2 0-.4.1-.5.2-.1.4 0 .5.1 0 0 1 1.7 2.3 2.3.9-.7 1.6-1.3 2.1-1.9-2.1-.4-3.1-1.9-3.2-1.9-.1-.2-.1-.4.1-.5.2-.1.4-.1.5.1a4.907 4.907 0 0 0 3.1 1.7c.6-.9.8-1.6.8-2 .1-.2 0-.3-.2-.4z"/></defs><clipPath id="SVGID_2_"><use xlink:href="#SVGID_1_" overflow="visible"/></clipPath><path class="st0" d="M-.7 2.3h23.9v21.2H-.7z"/></g><g><defs><path id="SVGID_3_" d="M11.6 5.2c-.3-.4-.9-.5-1.3-.2L7.9 6.6 4.5 1.8c-.2-.3-.6-.4-.9-.2L2.1 2.7c-.3.2-.3.6-.2.8l3.4 4.8L3 10c-.4.3-.5.9-.2 1.3L4 13l8.8-6.1c-.8-1-1.2-1.7-1.2-1.7z"/></defs><clipPath id="SVGID_4_"><use xlink:href="#SVGID_3_" overflow="visible"/></clipPath><path class="st1" d="M-3.2-3.5h20.9V18H-3.2z"/></g></g></svg>
|
After Width: | Height: | Size: 1.1 KiB |
|
@ -0,0 +1 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?><svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><style>.st0{fill:#36c}.st1{fill:#d33}</style><g><path class="st0" d="M6 8.7V12H0v4h6v3l3-2.6 3-2.6-3-2.5z"/><path class="st1" d="M14 4V.7l-3 2.6-3 2.5 3 2.6 3 2.6V8h6V4z"/></g></svg>
|
After Width: | Height: | Size: 293 B |
|
@ -15,8 +15,7 @@
|
||||||
|
|
||||||
( function () {
|
( function () {
|
||||||
var page = M.getCurrentPage(),
|
var page = M.getCurrentPage(),
|
||||||
getIconFromAmbox = M.require( 'skins.minerva.scripts/utils' )
|
pageIssueParser = M.require( 'skins.minerva.scripts/pageIssueParser' ),
|
||||||
.getIconFromAmbox,
|
|
||||||
overlayManager = M.require( 'skins.minerva.scripts/overlayManager' ),
|
overlayManager = M.require( 'skins.minerva.scripts/overlayManager' ),
|
||||||
CleanupOverlay = M.require( 'mobile.issues/CleanupOverlay' );
|
CleanupOverlay = M.require( 'mobile.issues/CleanupOverlay' );
|
||||||
|
|
||||||
|
@ -26,13 +25,15 @@
|
||||||
* @param {Object} $box element to extract the message from
|
* @param {Object} $box element to extract the message from
|
||||||
* @ignore
|
* @ignore
|
||||||
* @typedef {Object} IssueSummary
|
* @typedef {Object} IssueSummary
|
||||||
|
* @prop {PageIssue} pageIssue
|
||||||
* @prop {string} icon HTML string.
|
* @prop {string} icon HTML string.
|
||||||
* @prop {string} text HTML string.
|
* @prop {string} text HTML string.
|
||||||
* @return {IssueSummary}
|
* @return {IssueSummary}
|
||||||
*/
|
*/
|
||||||
function extractMessage( $box ) {
|
function extractMessage( $box ) {
|
||||||
var selector = '.mbox-text, .ambox-text',
|
var selector = '.mbox-text, .ambox-text',
|
||||||
$container = $( '<div>' );
|
$container = $( '<div>' ),
|
||||||
|
pageIssue;
|
||||||
|
|
||||||
$box.find( selector ).each( function () {
|
$box.find( selector ).each( function () {
|
||||||
var contents,
|
var contents,
|
||||||
|
@ -45,8 +46,11 @@
|
||||||
$( '<p>' ).html( contents ).appendTo( $container );
|
$( '<p>' ).html( contents ).appendTo( $container );
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
pageIssue = pageIssueParser.parse( $box.get( 0 ) );
|
||||||
return {
|
return {
|
||||||
icon: getIconFromAmbox( $box ).toHtmlString(),
|
pageIssue: pageIssue,
|
||||||
|
icon: pageIssue.icon.toHtmlString(),
|
||||||
text: $container.html()
|
text: $container.html()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -85,7 +89,8 @@
|
||||||
selector = 'table.ambox, table.tmbox, table.cmbox, table.fmbox',
|
selector = 'table.ambox, table.tmbox, table.cmbox, table.fmbox',
|
||||||
$metadata = $container.find( selector ),
|
$metadata = $container.find( selector ),
|
||||||
issues = [],
|
issues = [],
|
||||||
$link;
|
$link,
|
||||||
|
severity;
|
||||||
|
|
||||||
// clean it up a little
|
// clean it up a little
|
||||||
$metadata.find( '.NavFrame' ).remove();
|
$metadata.find( '.NavFrame' ).remove();
|
||||||
|
@ -107,10 +112,12 @@
|
||||||
allIssues[section] = issues;
|
allIssues[section] = issues;
|
||||||
|
|
||||||
if ( inline ) {
|
if ( inline ) {
|
||||||
|
severity = pageIssueParser.maxSeverity(
|
||||||
|
issues.map( function ( issue ) { return issue.pageIssue; } )
|
||||||
|
);
|
||||||
new Icon( {
|
new Icon( {
|
||||||
glyphPrefix: 'minerva',
|
glyphPrefix: 'minerva',
|
||||||
name: 'warning',
|
name: pageIssueParser.iconName( $metadata, severity )
|
||||||
isSmall: true
|
|
||||||
} ).prependTo( $metadata.find( '.mbox-text' ) );
|
} ).prependTo( $metadata.find( '.mbox-text' ) );
|
||||||
$learnMore = $( '<span>' )
|
$learnMore = $( '<span>' )
|
||||||
.addClass( 'ambox-learn-more' )
|
.addClass( 'ambox-learn-more' )
|
||||||
|
@ -148,7 +155,7 @@
|
||||||
// Note section.all may not exist, depending on the structure of the HTML page.
|
// Note section.all may not exist, depending on the structure of the HTML page.
|
||||||
// It will only exist when Minerva has been run in desktop mode.
|
// It will only exist when Minerva has been run in desktop mode.
|
||||||
// If it's absent, we'll reduce all the other lists into one.
|
// If it's absent, we'll reduce all the other lists into one.
|
||||||
return section.all || Object.keys( allIssues ).reduce(
|
return allIssues.all || Object.keys( allIssues ).reduce(
|
||||||
function ( all, key ) {
|
function ( all, key ) {
|
||||||
return all.concat( allIssues[key] );
|
return all.concat( allIssues[key] );
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,168 @@
|
||||||
|
( function ( M ) {
|
||||||
|
/**
|
||||||
|
* @typedef PageIssue
|
||||||
|
* @prop {string} severity A SEVERITY_LEVEL key.
|
||||||
|
* @prop {Icon} icon
|
||||||
|
*/
|
||||||
|
|
||||||
|
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 = {
|
||||||
|
LOW: /ambox-style|avviso-stile/, // en, it
|
||||||
|
MEDIUM: /ambox-content|avviso-contenuto/, // en, it
|
||||||
|
HIGH: /ambox-speedy|ambox-delete|ambox-serious|avviso-importante/ // en, en, es / ru, it
|
||||||
|
// ..And everything else that doesn't match should be considered DEFAULT.
|
||||||
|
},
|
||||||
|
// As above but used to identify specific templates requiring icon customization.
|
||||||
|
TYPE_REGEX = {
|
||||||
|
MOVE: /ambox-move|ambox-merge|avviso-struttura/, // en, es / ru, it
|
||||||
|
POINT_OF_VIEW: new RegExp( [ // en
|
||||||
|
'ambox-POV',
|
||||||
|
'ambox-Advert',
|
||||||
|
'ambox-autobiography',
|
||||||
|
'ambox-believerpov',
|
||||||
|
'ambox-COI',
|
||||||
|
'ambox-fanpov',
|
||||||
|
'ambox-globalize',
|
||||||
|
'ambox-npov-language',
|
||||||
|
'ambox-fringe-theories',
|
||||||
|
'ambox-systemic-bias'
|
||||||
|
].join( '|' ) )
|
||||||
|
// ..And everything else that doesn't match is mapped to a "SEVERITY" type.
|
||||||
|
},
|
||||||
|
// 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
|
||||||
|
* @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 {PageIssue[]} issues
|
||||||
|
* @return {string} The greatest SEVERITY_LEVEL key.
|
||||||
|
*/
|
||||||
|
function maxSeverity( issues ) {
|
||||||
|
return issues.reduce( function ( max, issue ) {
|
||||||
|
return SEVERITY_LEVEL[max] > SEVERITY_LEVEL[issue.severity] ? max : issue.severity;
|
||||||
|
}, 'DEFAULT' );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Element} box
|
||||||
|
* @return {PageIssue}
|
||||||
|
*/
|
||||||
|
function parse( box ) {
|
||||||
|
var severity = parseSeverity( box );
|
||||||
|
return {
|
||||||
|
severity: severity,
|
||||||
|
icon: new Icon( {
|
||||||
|
glyphPrefix: 'minerva',
|
||||||
|
name: iconName( box, severity )
|
||||||
|
} )
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @module skins.minerva.scripts/utils
|
||||||
|
*/
|
||||||
|
M.define( 'skins.minerva.scripts/pageIssueParser', {
|
||||||
|
/**
|
||||||
|
* Extract an icon for use with the issue.
|
||||||
|
* @param {JQuery.Object} $box element to extract the icon from
|
||||||
|
* @return {Icon} representing the icon
|
||||||
|
*/
|
||||||
|
parse: parse,
|
||||||
|
maxSeverity: maxSeverity,
|
||||||
|
iconName: iconName,
|
||||||
|
test: {
|
||||||
|
parseSeverity: parseSeverity,
|
||||||
|
parseType: parseType
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
|
}( mw.mobileFrontend ) );
|
|
@ -1,44 +0,0 @@
|
||||||
( function ( M ) {
|
|
||||||
|
|
||||||
var Icon = M.require( 'mobile.startup/Icon' );
|
|
||||||
|
|
||||||
function getIconFromAmbox( $box ) {
|
|
||||||
var glyph,
|
|
||||||
names = [
|
|
||||||
'speedy',
|
|
||||||
'delete',
|
|
||||||
'protection',
|
|
||||||
'pov',
|
|
||||||
'move',
|
|
||||||
'style',
|
|
||||||
'content'
|
|
||||||
];
|
|
||||||
|
|
||||||
// since objects have no concept of ordering we repeat ourselves here
|
|
||||||
names.forEach( function ( name ) {
|
|
||||||
if ( !glyph && $box.hasClass( 'ambox-' + name ) ) {
|
|
||||||
// with a match, exit
|
|
||||||
glyph = name;
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
|
|
||||||
glyph = glyph || 'default';
|
|
||||||
return new Icon( {
|
|
||||||
glyphPrefix: 'minerva',
|
|
||||||
name: 'issue-' + glyph
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @module skins.minerva.scripts/utils
|
|
||||||
*/
|
|
||||||
M.define( 'skins.minerva.scripts/utils', {
|
|
||||||
/**
|
|
||||||
* Extract an icon for use with the issue.
|
|
||||||
* @param {JQuery.Object} $box element to extract the icon from
|
|
||||||
* @return {Icon} representing the icon
|
|
||||||
*/
|
|
||||||
getIconFromAmbox: getIconFromAmbox
|
|
||||||
} );
|
|
||||||
|
|
||||||
}( mw.mobileFrontend ) );
|
|
58
skin.json
|
@ -242,19 +242,54 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"skins.minerva.icons.images.scripts": {
|
"skins.minerva.icons.images.scripts": {
|
||||||
|
"targets": [
|
||||||
|
"mobile",
|
||||||
|
"desktop"
|
||||||
|
],
|
||||||
|
"dependencies": [
|
||||||
|
"skins.minerva.icons.images.scripts.misc",
|
||||||
|
"skins.minerva.icons.page.issues.uncolored",
|
||||||
|
"skins.minerva.icons.page.issues.default.color",
|
||||||
|
"skins.minerva.icons.page.issues.medium.color"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"skins.minerva.icons.images.scripts.misc": {
|
||||||
"class": "ResourceLoaderImageModule",
|
"class": "ResourceLoaderImageModule",
|
||||||
"selector": ".mw-ui-icon-minerva-{name}:before",
|
"selector": ".mw-ui-icon-minerva-{name}:before",
|
||||||
"images": {
|
"images": {
|
||||||
"issue-speedy": "resources/skins.minerva.icons.images.scripts/issue-speedy.svg",
|
"download": "resources/skins.minerva.icons.images.scripts.misc/download.svg",
|
||||||
"issue-delete": "resources/skins.minerva.icons.images.scripts/issue-delete.svg",
|
"stop-hand": "resources/skins.minerva.icons.images.scripts.misc/stop-hand.svg"
|
||||||
"issue-protection": "resources/skins.minerva.icons.images.scripts/issue-protection.svg",
|
}
|
||||||
"issue-default": "resources/skins.minerva.icons.images.scripts/issue-default.svg",
|
},
|
||||||
"issue-pov": "resources/skins.minerva.icons.images.scripts/issue-pov.svg",
|
"skins.minerva.icons.page.issues.uncolored": {
|
||||||
"issue-style": "resources/skins.minerva.icons.images.scripts/issue-style.svg",
|
"class": "ResourceLoaderImageModule",
|
||||||
"issue-content": "resources/skins.minerva.icons.images.scripts/issue-content.svg",
|
"selector": ".mw-ui-icon-minerva-{name}:before",
|
||||||
"issue-move": "resources/skins.minerva.icons.images.scripts/issue-move.svg",
|
"images": {
|
||||||
"download": "resources/skins.minerva.icons.images.scripts/download.svg",
|
"issue-severity-low-lowColor": "resources/skins.minerva.icons.page.issues.uncolored/issue-severity-low.svg",
|
||||||
"stop-hand": "resources/skins.minerva.icons.images.scripts/stop-hand.svg"
|
"issue-type-move-defaultColor": "resources/skins.minerva.icons.page.issues.uncolored/issue-type-move.svg"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"skins.minerva.icons.page.issues.default.color": {
|
||||||
|
"class": "ResourceLoaderImageModule",
|
||||||
|
"selectorWithoutVariant": ".mw-ui-icon-minerva-{name}-defaultColor:before",
|
||||||
|
"selectorWithVariant": ".mw-ui-icon-minerva-{name}-{variant}:before",
|
||||||
|
"defaultColor": "#54595d",
|
||||||
|
"variants": {
|
||||||
|
"lowColor": { "color": "#fc3", "global": true },
|
||||||
|
"mediumColor": { "color": "#ff5d01", "global": true },
|
||||||
|
"highColor": { "color": "#d33", "global": true }
|
||||||
|
},
|
||||||
|
"images": {
|
||||||
|
"issue-generic": "resources/skins.minerva.icons.page.issues.default.color/issue-generic.svg"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"skins.minerva.icons.page.issues.medium.color": {
|
||||||
|
"class": "ResourceLoaderImageModule",
|
||||||
|
"selector": ".mw-ui-icon-minerva-{name}-mediumColor:before",
|
||||||
|
"defaultColor": "#ff5d01",
|
||||||
|
"images": {
|
||||||
|
"issue-severity-medium": "resources/skins.minerva.icons.page.issues.medium.color/issue-severity-medium.svg",
|
||||||
|
"issue-type-point-of-view": "resources/skins.minerva.icons.page.issues.medium.color/issue-type-point-of-view.svg"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"skins.minerva.mainPage.styles": {
|
"skins.minerva.mainPage.styles": {
|
||||||
|
@ -382,7 +417,7 @@
|
||||||
"resources/skins.minerva.scripts/mobileRedirect.js",
|
"resources/skins.minerva.scripts/mobileRedirect.js",
|
||||||
"resources/skins.minerva.scripts/search.js",
|
"resources/skins.minerva.scripts/search.js",
|
||||||
"resources/skins.minerva.scripts/references.js",
|
"resources/skins.minerva.scripts/references.js",
|
||||||
"resources/skins.minerva.scripts/utils.js",
|
"resources/skins.minerva.scripts/pageIssueParser.js",
|
||||||
"resources/skins.minerva.scripts/AB.js",
|
"resources/skins.minerva.scripts/AB.js",
|
||||||
"resources/skins.minerva.scripts/cleanuptemplates.js"
|
"resources/skins.minerva.scripts/cleanuptemplates.js"
|
||||||
]
|
]
|
||||||
|
@ -519,7 +554,6 @@
|
||||||
"resources/skins.minerva.editor/init.js"
|
"resources/skins.minerva.editor/init.js"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
||||||
"skins.minerva.backtotop": {
|
"skins.minerva.backtotop": {
|
||||||
"targets": [
|
"targets": [
|
||||||
"mobile",
|
"mobile",
|
||||||
|
|
|
@ -0,0 +1,124 @@
|
||||||
|
( function ( M ) {
|
||||||
|
var pageIssueParser = M.require( 'skins.minerva.scripts/pageIssueParser' );
|
||||||
|
|
||||||
|
QUnit.module( 'pageIssueParser' );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} className
|
||||||
|
* @return {Element}
|
||||||
|
*/
|
||||||
|
function newBox( className ) {
|
||||||
|
var box = document.createElement( 'div' );
|
||||||
|
box.className = className;
|
||||||
|
return box;
|
||||||
|
}
|
||||||
|
|
||||||
|
QUnit.test( 'parseSeverity', function ( assert ) {
|
||||||
|
var tests = [
|
||||||
|
[ '', 'DEFAULT', 'empty' ],
|
||||||
|
[ 'foo', 'DEFAULT', 'unknown' ],
|
||||||
|
[ 'ambox-style', 'LOW', 'style' ],
|
||||||
|
[ 'ambox-content', 'MEDIUM', 'content' ],
|
||||||
|
[ 'ambox-speedy', 'HIGH', 'speedy' ],
|
||||||
|
[ 'ambox-delete', 'HIGH', 'delete' ],
|
||||||
|
// Move has an "unknown" severity and falls into DEFAULT.
|
||||||
|
[ 'ambox-move', 'DEFAULT', 'move' ],
|
||||||
|
// Point of view uses ambox-content to identify correct severity.
|
||||||
|
[ 'ambox-content ambox-POV', 'MEDIUM', 'point of view' ]
|
||||||
|
// Mixed severities such as 'ambox-style ambox-content' are not prioritized.
|
||||||
|
];
|
||||||
|
tests.forEach( function ( params, i ) {
|
||||||
|
var
|
||||||
|
className = params[0],
|
||||||
|
expect = params[1],
|
||||||
|
test = params[2],
|
||||||
|
box = newBox( className );
|
||||||
|
assert.strictEqual(
|
||||||
|
pageIssueParser.test.parseSeverity( box ),
|
||||||
|
expect,
|
||||||
|
'Result should be the correct severity; case ' + i + ' failed: ' + test + '.'
|
||||||
|
);
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
|
||||||
|
QUnit.test( 'parseType', function ( assert ) {
|
||||||
|
var tests = [
|
||||||
|
[ '', 'DEFAULT', 'issue-generic', 'empty' ],
|
||||||
|
[ 'foo', 'DEFAULT', 'issue-generic', 'unknown' ],
|
||||||
|
[ 'ambox-move', 'DEFAULT', 'issue-type-move', 'move' ],
|
||||||
|
[ 'ambox-POV', 'MEDIUM', 'issue-type-point-of-view', 'point of view' ],
|
||||||
|
[ '', 'DEFAULT', 'issue-generic', 'Default severity' ],
|
||||||
|
[ '', 'LOW', 'issue-severity-low', 'Low severity' ],
|
||||||
|
[ '', 'MEDIUM', 'issue-severity-medium', 'Medium severity' ],
|
||||||
|
[ '', 'HIGH', 'issue-generic', 'HIGH severity' ]
|
||||||
|
];
|
||||||
|
tests.forEach( function ( params, i ) {
|
||||||
|
var
|
||||||
|
className = params[0],
|
||||||
|
severity = params[1],
|
||||||
|
expect = {
|
||||||
|
name: params[2],
|
||||||
|
severity: severity
|
||||||
|
},
|
||||||
|
test = params[3],
|
||||||
|
box = newBox( className );
|
||||||
|
assert.propEqual(
|
||||||
|
pageIssueParser.test.parseType( box, severity ),
|
||||||
|
expect,
|
||||||
|
'Result should be the correct icon type; case ' + i + ' failed: ' + test + '.'
|
||||||
|
);
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
|
||||||
|
QUnit.test( 'iconName', function ( assert ) {
|
||||||
|
var tests = [
|
||||||
|
[ '', 'DEFAULT', 'issue-generic-defaultColor' ],
|
||||||
|
[ '', 'LOW', 'issue-severity-low-lowColor' ],
|
||||||
|
[ '', 'MEDIUM', 'issue-severity-medium-mediumColor' ],
|
||||||
|
[ '', 'HIGH', 'issue-generic-highColor' ],
|
||||||
|
[ 'ambox-move', 'DEFAULT', 'issue-type-move-defaultColor' ],
|
||||||
|
[ 'ambox-POV', 'MEDIUM', 'issue-type-point-of-view-mediumColor' ],
|
||||||
|
// ResourceLoader only supplies color variants for the generic type. Ensure impossible
|
||||||
|
// combinations are forbidden.
|
||||||
|
[ 'ambox-style ambox-POV', 'LOW', 'issue-type-point-of-view-mediumColor' ],
|
||||||
|
[ 'ambox-content ambox-move', 'MEDIUM', 'issue-type-move-defaultColor' ]
|
||||||
|
];
|
||||||
|
tests.forEach( function ( params, i ) {
|
||||||
|
var
|
||||||
|
className = params[0],
|
||||||
|
severity = params[1],
|
||||||
|
expect = params[2],
|
||||||
|
box = newBox( className );
|
||||||
|
assert.strictEqual(
|
||||||
|
pageIssueParser.iconName( box, severity ),
|
||||||
|
expect,
|
||||||
|
'Result should be the correct ResourceLoader icon name; case ' + i + ' failed: ' + severity + '.'
|
||||||
|
);
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
|
||||||
|
QUnit.test( 'maxSeverity', function ( assert ) {
|
||||||
|
var tests = [
|
||||||
|
[ [], 'DEFAULT' ],
|
||||||
|
[ [ 'DEFAULT' ], 'DEFAULT' ],
|
||||||
|
[ [ 'DEFAULT', 'LOW' ], 'LOW' ],
|
||||||
|
[ [ 'DEFAULT', 'LOW', 'MEDIUM' ], 'MEDIUM' ],
|
||||||
|
[ [ 'DEFAULT', 'LOW', 'MEDIUM', 'HIGH' ], 'HIGH' ],
|
||||||
|
[ [ 'HIGH', 'DEFAULT', 'LOW', 'MEDIUM' ], 'HIGH' ],
|
||||||
|
[ [ 'DEFAULT', 'HIGH', 'LOW', 'MEDIUM' ], 'HIGH' ]
|
||||||
|
];
|
||||||
|
tests.forEach( function ( params, i ) {
|
||||||
|
var
|
||||||
|
severities = params[0],
|
||||||
|
expect = params[1];
|
||||||
|
assert.strictEqual(
|
||||||
|
pageIssueParser.maxSeverity( severities.map( function ( severity ) {
|
||||||
|
return { severity: severity };
|
||||||
|
} ) ),
|
||||||
|
expect,
|
||||||
|
'Result should be the highest severity in the array; case ' + i + ' failed.'
|
||||||
|
);
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
|
||||||
|
}( mw.mobileFrontend ) );
|
|
@ -1,47 +0,0 @@
|
||||||
( function ( M ) {
|
|
||||||
var utils = M.require( 'skins.minerva.scripts/utils' ),
|
|
||||||
getIconFromAmbox = utils.getIconFromAmbox;
|
|
||||||
|
|
||||||
QUnit.module( 'Minerva utils' );
|
|
||||||
QUnit.test( 'getIconFromAmbox', function ( assert ) {
|
|
||||||
var tests = [
|
|
||||||
[
|
|
||||||
'', 'issue-default'
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'ambox', 'issue-default'
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'ambox-content ambox-speedy', 'issue-speedy'
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'ambox-content ambox-delete', 'issue-delete'
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'ambox-content', 'issue-content'
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'ambox-content ambox-pov', 'issue-pov'
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'ambox-content ambox-style', 'issue-style'
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'ambox-content ambox-move', 'issue-move'
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'ambox-content ambox-protection', 'issue-protection'
|
|
||||||
]
|
|
||||||
];
|
|
||||||
tests.forEach( function ( test, i ) {
|
|
||||||
var $box = $( '<div>' );
|
|
||||||
$box.addClass( test[0] );
|
|
||||||
assert.strictEqual(
|
|
||||||
getIconFromAmbox( $box ).options.name,
|
|
||||||
test[1],
|
|
||||||
i
|
|
||||||
);
|
|
||||||
} );
|
|
||||||
} );
|
|
||||||
|
|
||||||
}( mw.mobileFrontend ) );
|
|