MediaWiki:Gadget-compare-core.js: Difference between revisions
Jump to navigation
Jump to search
Content added Content deleted
No edit summary Tag: Reverted |
No edit summary Tag: Reverted |
||
Line 1: | Line 1: | ||
/** * Adds links for compare popups * * @author Quarenon * @author Ryan PM * @author Joeytje50 * @author Cqm * @author JaydenKieran * * @license GPLv3 <https://www.gnu.org/licenses/gpl-3.0.html> * * @todo try to find a standard img url domain to use * @todo re-center (vertical & horizontally) with new items added, or find a way to do it with pure CSS * might require overhaul to #overlay structure/styles */'use strict';var modalOpenedPrev = false;var conf = mw.config.get( [ 'stylepath', 'wgTitle' ] ), self = { /** * Inital loading method */ init: function () { self.buildModal(); var $compare = $( '.cioCompareLink' ), $ibox = $( '.infobox-bonuses' ); $compare.each( function () { var $this = $( this ), props = ( $this.attr( 'title' ) || '' ).split( '|' ), text = props[0] !== '' ? props[0] : 'Compare items', items = props.length >= 2 ? props.slice( 1 ) : [conf.wgTitle], $a = $( '<a>' ) .attr( { href: '#', title: 'Compare this item with other items', 'data-items': items.join( '|' ) } ) .text( text ) .on( 'click', self.open ); $this .empty() .append( $a ) .parent() .show(); } ); $ibox.each( function () { var $this = $( this ) // insert new row with compare link var button = new OO.ui.ButtonWidget( { label: 'Compare', title: 'Compare this item with other items', flags: 'primary' } ); $this.after( button.$element .attr( { 'data-items': conf.wgTitle }) .on( 'click', self.open ) ); } ); }, /** * Images * * These are functions to avoid us having to use .clone() * and to avoid potential memory leaks */ img: { /** * Delete image * * @return {jquery object} */ del: function () { return $( '<img>' ) .attr( { src: "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Cg fill='%23d33'%3E%3Cpath d='m4.3 2.9 12.8 12.8-1.4 1.4L2.9 4.3z'/%3E%3Cpath d='M17.1 4.3 4.3 17.1l-1.4-1.4L15.7 2.9z'/%3E%3C/g%3E%3C/svg%3E%0A", width: 16, height: 16, alt: 'Delete' } ); }, /** * Loading image * * @return {jquery object} */ loading: function () { return $( '<img>' ) .attr( { // .gif can't be converted to data: URI src: 'https://oldschool.runescape.wiki/images/2/23/Progress-wheel.gif?0a2fe', width: 16, height: 16, alt: '...' } ); }, /** * Error image * * @return {jquery object} */ error: function () { return $( '<img>' ) .attr( { src: "data:image/svg+xml,%3C%3Fxml version='1.0' encoding='UTF-8'%3F%3E%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Cg fill='%23d33'%3E%3Cpath d='M13.728 1H6.272L1 6.272v7.456L6.272 19h7.456L19 13.728V6.272zM11 15H9v-2h2zm0-4H9V5h2z'/%3E%3C/g%3E%3C/svg%3E%0A", width: 16, height: 16, alt: 'Error' } ); } }, /** * Modal open method * * Callback to on click event * * @param e {jquery.event} */ open: function ( e ) { e.preventDefault(); window.OOUIWindowManager.openWindow( 'compare' ); if (!modalOpenedPrev) { // avoid init-ing modalOpenedPrev = true; var items = $( this ).attr( 'data-items' ).split( '|' ); items.forEach( self.submit ); } }, /** * Builds the compare modal * * @return {jquery object} */ buildModal: function () { var init = function (modal) { modal.content = new OO.ui.PanelLayout( { padded: true, expanded: false } ); var button1 = new OO.ui.ButtonWidget( { flags: [ 'destructive' ], label: 'Cancel' } ); var b1click = ('click', function(modal) { window.OOUIWindowManager.closeWindow(modal); }); button1.on('click', b1click, [modal]); var button2 = new OO.ui.ButtonWidget( { label: 'Submit', flags: [ 'progressive' ], }); button2.on('click', function() { self.submit(); }); var input1 = new OO.ui.TextInputWidget({ id: 'cioItem' }); input1.on('enter', function(){ self.submit(); }); // Create OOUI JS fieldset var fieldset = new OO.ui.FieldsetLayout( { label: 'Comparing ' + conf.wgTitle, id: 'cioCompare' } ); fieldset.addItems( [ new OO.ui.ActionFieldLayout( input1, button2, { label: 'Compare with', align: 'inline', notices: [new OO.ui.HtmlSnippet('<div id="cioStatus"></div>')] } ) ] ); modal.content.$element.append($('<div>').append(fieldset.$element).append( $( '<table>' ) .addClass( 'wikitable' ) .attr( 'id', 'cioItems' ) .append( $( '<thead>' ) .append( $( '<tr>' ) .append( $( '<th>' ) .attr( 'rowspan', '2' ) .text( 'Name' ), $( '<th>' ) .attr( 'colspan', '5' ) .text( 'Attack bonuses' ), $( '<th>' ) .attr( 'colspan', '5' ) .text( 'Defence bonuses' ), $( '<th>' ) .attr( 'colspan', '4' ) .text( 'Other bonuses' ), $( '<th>' ) .attr( 'width', '30' ) .text( 'Speed' ), $( '<th>' ) .attr( 'width', '30' ) .text( 'Weight' ), $( '<th>' ) .attr( 'width', '30' ) .text( 'GE' ) ), $( '<tr>' ) .attr( 'height', '35' ) .append( $( '<th>' ) .attr( { class: 'cioIcon-stab', title: 'Stab bonus', width: '35' } ), $( '<th>' ) .attr( { class: 'cioIcon-slash', title: 'Slash bonus', width: '35' } ), $( '<th>' ) .attr( { class: 'cioIcon-crush', title: 'Crush bonus', width: '35' } ), $( '<th>' ) .attr( { class: 'cioIcon-magic', title: 'Magic bonus', width: '35' } ), $( '<th>' ) .attr( { class: 'cioIcon-ranged', title: 'Ranged bonus', width: '35' } ), $( '<th>' ) .attr( { class: 'cioIcon-stab', title: 'Stab bonus', width: '35' } ), $( '<th>' ) .attr( { class: 'cioIcon-slash', title: 'Slash bonus', width: '35' } ), $( '<th>' ) .attr( { class: 'cioIcon-crush', title: 'Crush bonus', width: '35' } ), $( '<th>' ) .attr( { class: 'cioIcon-magic', title: 'Magic bonus', width: '35' } ), $( '<th>' ) .attr( { class: 'cioIcon-ranged', title: 'Ranged bonus', width: '35' } ), $( '<th>' ) .attr( { class: 'cioIcon-strength', title: 'Strength bonus', width: '35' } ), $( '<th>' ) .attr( { class: 'cioIcon-rangedstrength', title: 'Ranged Strength bonus', width: '35' } ), $( '<th>' ) .attr( { class: 'cioIcon-magicdamage', title: 'Magic Damage bonus', width: '35' } ), $( '<th>' ) .attr( { class: 'cioIcon-prayer', title: 'Prayer bonus', width: '35' } ), $( '<th>' ) .attr( { class: 'cioIcon-speed', title: 'Speed', width: '35' } ), $( '<th>' ) .attr( { class: 'cioIcon-weight', title: 'Weight (kg)', width: '35' } ), $( '<th>' ) .attr( { class: 'cioIcon-price', title: 'Grand Exchange Price', width: '35' } ) ) ), $( '<tbody>' ) .append( $( '<tr>' ) .attr( 'id', 'cioTotals' ) // .addClass('table-bg-green') ) ), button1.$element )); modal.$body.append( modal.content.$element ); }; rs.createOOUIWindow('compare', 'Compare with other items', {size: 'larger', classes: ['rs-compare-modal', 'oo-ui-compare-width']}, init); }, /** * Initial callback for adding new items to the UI * * @param elem {string} (optional) */ submit: function ( elem ) { var item = elem || $( '#cioItem > input' ).val(); $( '#cioStatus' ) .empty() .attr( 'class', 'cioLoading' ) .append( self.img.loading(), ' Loading...' ); // make sure first letter of item is uppercase // otherwise price data won't be found item = item.charAt( 0 ).toUpperCase() + item.slice( 1 ); var mwApiResult, excg, main, excgData; ( new mw.Api() ) .get( { action: 'query', prop: 'revisions', titles: item + '|Module:Exchange/' + item, rvprop: 'content', redirects: '' } ) .then( function (data) { mwApiResult = data; for ( var x in mwApiResult.query.pages ) { if ( mwApiResult.query.pages.hasOwnProperty( x ) ) { if ( x < 0 ) { // the page does not exist mw.log( mwApiResult.query.pages[x] ); continue; } else if ( mwApiResult.query.pages[x].ns === 828 ) { excg = mwApiResult.query.pages[x]; } else if ( mwApiResult.query.pages[x].ns === 0 ) { main = mwApiResult.query.pages[x]; } } } if ( excg ) { excgData = rs.parseExchangeModule( excg.revisions[0]['*'] ); excgData.itemId = excgData.itemId || excgData.itemid; // make this more robust? $.getJSON("https://api.weirdgloop.org/exchange/history/osrs/latest?id=" + excgData.itemId) .done( function (res) { self.done(main, res[excgData.itemId]); } ) .fail( self.fail ); } else { self.done(main, {}); } } ) .fail( self.fail ); return false; }, /** * Success callback for `jQuery.ajax` promise */ done: function ( main, apiRes ) { var bonuses = [ 'astab', 'aslash', 'acrush', 'amagic', 'arange', 'dstab', 'dslash', 'dcrush', 'dmagic', 'drange', 'str', 'rstr', 'mdmg', 'prayer', 'speed' ], main, x, title, content, bonusData, itemData, $tr; mw.log( main, apiRes ); if ( !main ) { self.showError( 'Could not find that item.' ); return; } title = main.title; content = main.revisions[0]['*']; bonusData = rs.parseTemplate( 'infobox bonuses', content ); itemData = rs.parseTemplate( 'infobox item', content ); if ( $.isEmptyObject( bonusData ) ) { self.showError( 'No bonus data found for the item.' ); return; } $tr = $( '<tr>' ) .append( $( '<th>' ) .append( $( '<a>' ) .attr( { href: '#', title: 'Remove this row' } ) .on( 'click', function () { $( this ).closest( 'tr' ).fadeOut( 'slow', function () { $( this ).remove(); self.calcTotals(); window.OOUIWindowManager.getCurrentWindow().updateSize(); } ); return false; } ) .append( self.img.del() ), ' ', $( '<a>' ) .attr( { href: mw.util.getUrl( title ), title: title } ) .text( title ) ) ); bonuses.forEach( function ( el ) { // Use default version if defined, otherwise check if bonus has a version1 var defaultVersion = $.isEmptyObject( itemData ) || (itemData.defver === undefined) ? '1' : itemData.defver; var versionSpecificBonus = bonusData[el + defaultVersion]; $tr.append( self.format( versionSpecificBonus === undefined ? bonusData[el] : versionSpecificBonus ) ); } ); $tr.append( self.format( !$.isEmptyObject( itemData ) ? itemData.weight : null ) ); $tr.append( self.format( !$.isEmptyObject( apiRes ) ? rs.addCommas( apiRes.price ) : null ) ); $( '#cioTotals' ).before( $tr ); self.calcTotals(); $( '#cioStatus' ).empty(); $( '#cioItem > input' ).val( '' ); window.OOUIWindowManager.getCurrentWindow().updateSize(); }, /** * Error callback for `jQuery.ajax` promise */ fail: function ( _, error ) { self.showError( 'Error: ' + error ); }, /** * Outputs error to the UI * * @param str {string} Error to display */ showError: function ( str ) { $( '#cioStatus' ) .empty() .attr( 'class', 'cioError' ) .append( self.img.error(), ' ' + str ); }, /** * Formats each attribute's value and inserts it into a td cell * * @param str {string} Attribute value to format * * @return {jquery object} td cell to insert into the associated item's row */ format: function ( str ) { var $td = $( '<td>' ), first; // set `null` or `undefined` to an empty string /*jshint eqnull:true */ if ( str == null ) { /* jshint eqnull:false */ str = ''; } // remove comments str = str.replace( /no|<!--.*?-->/gi, '' ).trim(); // cache first character of `str` first = str.substring( 0, 1 ); if ( !str ) { $td .addClass( 'cioEmpty' ) .text( '--' ); } else if ( /\d/.test( first ) ) { $td .addClass( 'cioPos' ) .text( '+' + str ); } else if ( first === '-' ) { $td .addClass( 'cioNeg' ) .text( str ); } else { $td .text( str ); } return $td; }, formatTotals: function (str, index) { var $td = $( '<td>' ), first; // set `null` or `undefined` to an empty string /*jshint eqnull:true */ if ( str == null || str === "null" ) { /* jshint eqnull:false */ str = ''; } // remove comments str = str.replace( /no|<!--.*?-->/gi, '' ).trim(); // cache first character of `str` first = str.substring( 0, 1 ); // lower is better for speed and weight - reverse colors var lowerBetter = [14, 15].includes(index); if ( !str ) { $td .addClass( 'cioEmpty' ) .text( '--' ); } else if (parseFloat(str, 10) === 0) { $td .addClass( 'table-bg-yellow ') .text(str); } else if ( /\d/.test( first ) ) { $td .addClass( lowerBetter ? 'table-bg-red' : 'table-bg-green' ) .text( '+' + str ); } else if ( first === '-' ) { $td .addClass( lowerBetter ? 'table-bg-green' : 'table-bg-red' ) .text( str ); } else { $td.text( str ); } // context dependent whether higher or lower price is better - just don't color it if (index === 16) { $td.removeClass("table-bg-green table-bg-yellow table-bg-red"); } return $td; }, /** * Calculate bonus totals */ calcTotals: function () { // 19 0's, one for each attribute var totals = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], $totals = $( '#cioTotals' ); // don't show totals row when not comparing 2 or more items if ($( '#cioItems tbody tr:not( #cioTotals )' ).length < 2) { $totals.empty(); return; } $( '#cioItems tbody tr:not( #cioTotals ):first td' ).each(function(i) { var num = parseFloat($(this).text().replace(/,/g, "")); totals[i] = isNaN(num) ? null : num; }); $( '#cioItems tbody tr:not( #cioTotals ):not(:first)' ).each( function () { $( this ).children( 'td' ).each( function ( i ) { if (totals[i] !== null) { var num = parseFloat($(this).text().replace(/,/g, "")); if (isNaN(num)) { totals[i] = null; } else { totals[i] -= num; } } } ); } ); $totals .empty() .append( $( '<th>' ) .text( 'Diff' ) ); totals.forEach( function ( elem, index ) { $totals.append( self.formatTotals( // don't total speed // 14th index/column respectively // [14].indexOf( index ) > -1 ? null : rs.addCommas( elem ) rs.addCommas(elem), index ) ); } ); }, checkSign: function (value) { return value === 0 ? true : (value > 0 ? true : false); } };$(function(){mw.loader.using( ['mediawiki.util', 'mediawiki.api', 'ext.gadget.rsw-util'], self.init )}); |
|||
/** |
|||
* Adds links for compare popups |
|||
* |
|||
* @author Quarenon |
|||
* @author Ryan PM |
|||
* @author Joeytje50 |
|||
* @author Cqm |
|||
* @author JaydenKieran |
|||
* |
|||
* @license GPLv3 <https://www.gnu.org/licenses/gpl-3.0.html> |
|||
* |
|||
* @todo try to find a standard img url domain to use |
|||
* @todo re-center (vertical & horizontally) with new items added, or find a way to do it with pure CSS |
|||
* might require overhaul to #overlay structure/styles |
|||
*/ |
|||
'use strict'; |
|||
var modalOpenedPrev = false; |
|||
var conf = mw.config.get( [ |
|||
'stylepath', |
|||
'wgTitle' |
|||
] ), |
|||
self = { |
|||
/** |
|||
* Inital loading method |
|||
*/ |
|||
init: function () { |
|||
self.buildModal(); |
|||
var $compare = $( '.cioCompareLink' ), |
|||
$ibox = $( '.infobox-bonuses' ); |
|||
$compare.each( function () { |
|||
var $this = $( this ), |
|||
props = ( $this.attr( 'title' ) || '' ).split( '|' ), |
|||
text = props[0] !== '' ? props[0] : 'Compare items', |
|||
items = props.length >= 2 ? props.slice( 1 ) : [conf.wgTitle], |
|||
$a = $( '<a>' ) |
|||
.attr( { |
|||
href: '#', |
|||
title: 'Compare this item with other items', |
|||
'data-items': items.join( '|' ) |
|||
} ) |
|||
.text( text ) |
|||
.on( 'click', self.open ); |
|||
$this |
|||
.empty() |
|||
.append( $a ) |
|||
.parent() |
|||
.show(); |
|||
} ); |
|||
$ibox.each( function () { |
|||
var $this = $( this ) |
|||
// insert new row with compare link |
|||
var button = new OO.ui.ButtonWidget( { |
|||
label: 'Compare', |
|||
title: 'Compare this item with other items', |
|||
flags: 'primary' |
|||
} ); |
|||
$this.after( button.$element |
|||
.attr( { |
|||
'data-items': conf.wgTitle |
|||
}) |
|||
.on( 'click', self.open ) |
|||
); |
|||
} ); |
|||
}, |
|||
/** |
|||
* Images |
|||
* |
|||
* These are functions to avoid us having to use .clone() |
|||
* and to avoid potential memory leaks |
|||
*/ |
|||
img: { |
|||
/** |
|||
* Delete image |
|||
* |
|||
* @return {jquery object} |
|||
*/ |
|||
del: function () { |
|||
return $( '<img>' ) |
|||
.attr( { |
|||
src: "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Cg fill='%23d33'%3E%3Cpath d='m4.3 2.9 12.8 12.8-1.4 1.4L2.9 4.3z'/%3E%3Cpath d='M17.1 4.3 4.3 17.1l-1.4-1.4L15.7 2.9z'/%3E%3C/g%3E%3C/svg%3E%0A", |
|||
width: 16, |
|||
height: 16, |
|||
alt: 'Delete' |
|||
} ); |
|||
}, |
|||
/** |
|||
* Loading image |
|||
* |
|||
* @return {jquery object} |
|||
*/ |
|||
loading: function () { |
|||
return $( '<img>' ) |
|||
.attr( { |
|||
// .gif can't be converted to data: URI |
|||
src: 'https://oldschool.runescape.wiki/images/2/23/Progress-wheel.gif?0a2fe', |
|||
width: 16, |
|||
height: 16, |
|||
alt: '...' |
|||
} ); |
|||
}, |
|||
/** |
|||
* Error image |
|||
* |
|||
* @return {jquery object} |
|||
*/ |
|||
error: function () { |
|||
return $( '<img>' ) |
|||
.attr( { |
|||
src: "data:image/svg+xml,%3C%3Fxml version='1.0' encoding='UTF-8'%3F%3E%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Cg fill='%23d33'%3E%3Cpath d='M13.728 1H6.272L1 6.272v7.456L6.272 19h7.456L19 13.728V6.272zM11 15H9v-2h2zm0-4H9V5h2z'/%3E%3C/g%3E%3C/svg%3E%0A", |
|||
width: 16, |
|||
height: 16, |
|||
alt: 'Error' |
|||
} ); |
|||
} |
|||
}, |
|||
/** |
|||
* Modal open method |
|||
* |
|||
* Callback to on click event |
|||
* |
|||
* @param e {jquery.event} |
|||
*/ |
|||
open: function ( e ) { |
|||
e.preventDefault(); |
|||
window.OOUIWindowManager.openWindow( 'compare' ); |
|||
if (!modalOpenedPrev) { // avoid init-ing |
|||
modalOpenedPrev = true; |
|||
var items = $( this ).attr( 'data-items' ).split( '|' ); |
|||
items.forEach( self.submit ); |
|||
} |
|||
}, |
|||
/** |
|||
* Builds the compare modal |
|||
* |
|||
* @return {jquery object} |
|||
*/ |
|||
buildModal: function () { |
|||
var init = function (modal) { |
|||
modal.content = new OO.ui.PanelLayout( { padded: true, expanded: false } ); |
|||
var button1 = new OO.ui.ButtonWidget( { |
|||
flags: [ 'destructive' ], |
|||
label: 'Cancel' |
|||
} ); |
|||
var b1click = ('click', function(modal) { |
|||
window.OOUIWindowManager.closeWindow(modal); |
|||
}); |
|||
button1.on('click', b1click, [modal]); |
|||
var button2 = new OO.ui.ButtonWidget( { |
|||
label: 'Submit', |
|||
flags: [ 'progressive' ], |
|||
}); |
|||
button2.on('click', function() { |
|||
self.submit(); |
|||
}); |
|||
var input1 = new OO.ui.TextInputWidget({ id: 'cioItem' }); |
|||
input1.on('enter', function(){ |
|||
self.submit(); |
|||
}); |
|||
// Create OOUI JS fieldset |
|||
var fieldset = new OO.ui.FieldsetLayout( { |
|||
label: 'Comparing ' + conf.wgTitle, |
|||
id: 'cioCompare' |
|||
} ); |
|||
fieldset.addItems( [ |
|||
new OO.ui.ActionFieldLayout( |
|||
input1, |
|||
button2, |
|||
{ label: 'Compare with', align: 'inline', notices: [new OO.ui.HtmlSnippet('<div id="cioStatus"></div>')] } |
|||
) |
|||
] ); |
|||
modal.content.$element.append($('<div>').append(fieldset.$element).append( |
|||
$( '<table>' ) |
|||
.addClass( 'wikitable' ) |
|||
.attr( 'id', 'cioItems' ) |
|||
.append( |
|||
$( '<thead>' ) |
|||
.append( |
|||
$( '<tr>' ) |
|||
.append( |
|||
$( '<th>' ) |
|||
.attr( 'rowspan', '2' ) |
|||
.text( 'Name' ), |
|||
$( '<th>' ) |
|||
.attr( 'colspan', '5' ) |
|||
.text( 'Attack bonuses' ), |
|||
$( '<th>' ) |
|||
.attr( 'colspan', '5' ) |
|||
.text( 'Defence bonuses' ), |
|||
$( '<th>' ) |
|||
.attr( 'colspan', '4' ) |
|||
.text( 'Other bonuses' ), |
|||
$( '<th>' ) |
|||
.attr( 'width', '30' ) |
|||
.text( 'Speed' ), |
|||
$( '<th>' ) |
|||
.attr( 'width', '30' ) |
|||
.text( 'Weight' ), |
|||
$( '<th>' ) |
|||
.attr( 'width', '30' ) |
|||
.text( 'GE' ) |
|||
), |
|||
$( '<tr>' ) |
|||
.attr( 'height', '35' ) |
|||
.append( |
|||
$( '<th>' ) |
|||
.attr( { |
|||
class: 'cioIcon-stab', |
|||
title: 'Stab bonus', |
|||
width: '35' |
|||
} ), |
|||
$( '<th>' ) |
|||
.attr( { |
|||
class: 'cioIcon-slash', |
|||
title: 'Slash bonus', |
|||
width: '35' |
|||
} ), |
|||
$( '<th>' ) |
|||
.attr( { |
|||
class: 'cioIcon-crush', |
|||
title: 'Crush bonus', |
|||
width: '35' |
|||
} ), |
|||
$( '<th>' ) |
|||
.attr( { |
|||
class: 'cioIcon-magic', |
|||
title: 'Magic bonus', |
|||
width: '35' |
|||
} ), |
|||
$( '<th>' ) |
|||
.attr( { |
|||
class: 'cioIcon-ranged', |
|||
title: 'Ranged bonus', |
|||
width: '35' |
|||
} ), |
|||
$( '<th>' ) |
|||
.attr( { |
|||
class: 'cioIcon-stab', |
|||
title: 'Stab bonus', |
|||
width: '35' |
|||
} ), |
|||
$( '<th>' ) |
|||
.attr( { |
|||
class: 'cioIcon-slash', |
|||
title: 'Slash bonus', |
|||
width: '35' |
|||
} ), |
|||
$( '<th>' ) |
|||
.attr( { |
|||
class: 'cioIcon-crush', |
|||
title: 'Crush bonus', |
|||
width: '35' |
|||
} ), |
|||
$( '<th>' ) |
|||
.attr( { |
|||
class: 'cioIcon-magic', |
|||
title: 'Magic bonus', |
|||
width: '35' |
|||
} ), |
|||
$( '<th>' ) |
|||
.attr( { |
|||
class: 'cioIcon-ranged', |
|||
title: 'Ranged bonus', |
|||
width: '35' |
|||
} ), |
|||
$( '<th>' ) |
|||
.attr( { |
|||
class: 'cioIcon-strength', |
|||
title: 'Strength bonus', |
|||
width: '35' |
|||
} ), |
|||
$( '<th>' ) |
|||
.attr( { |
|||
class: 'cioIcon-rangedstrength', |
|||
title: 'Ranged Strength bonus', |
|||
width: '35' |
|||
} ), |
|||
$( '<th>' ) |
|||
.attr( { |
|||
class: 'cioIcon-magicdamage', |
|||
title: 'Magic Damage bonus', |
|||
width: '35' |
|||
} ), |
|||
$( '<th>' ) |
|||
.attr( { |
|||
class: 'cioIcon-prayer', |
|||
title: 'Prayer bonus', |
|||
width: '35' |
|||
} ), |
|||
$( '<th>' ) |
|||
.attr( { |
|||
class: 'cioIcon-speed', |
|||
title: 'Speed', |
|||
width: '35' |
|||
} ), |
|||
$( '<th>' ) |
|||
.attr( { |
|||
class: 'cioIcon-weight', |
|||
title: 'Weight (kg)', |
|||
width: '35' |
|||
} ), |
|||
$( '<th>' ) |
|||
.attr( { |
|||
class: 'cioIcon-price', |
|||
title: 'Grand Exchange Price', |
|||
width: '35' |
|||
} ) |
|||
) |
|||
), |
|||
$( '<tbody>' ) |
|||
.append( |
|||
$( '<tr>' ) |
|||
.attr( 'id', 'cioTotals' ) |
|||
// .addClass('table-bg-green') |
|||
) |
|||
), |
|||
button1.$element |
|||
)); |
|||
modal.$body.append( modal.content.$element ); |
|||
}; |
|||
rs.createOOUIWindow('compare', 'Compare with other items', {size: 'larger', classes: ['rs-compare-modal', 'oo-ui-compare-width']}, init); |
|||
}, |
|||
/** |
|||
* Initial callback for adding new items to the UI |
|||
* |
|||
* @param elem {string} (optional) |
|||
*/ |
|||
submit: function ( elem ) { |
|||
var item = elem || $( '#cioItem > input' ).val(); |
|||
$( '#cioStatus' ) |
|||
.empty() |
|||
.attr( 'class', 'cioLoading' ) |
|||
.append( |
|||
self.img.loading(), |
|||
' Loading...' |
|||
); |
|||
// make sure first letter of item is uppercase |
|||
// otherwise price data won't be found |
|||
item = item.charAt( 0 ).toUpperCase() + item.slice( 1 ); |
|||
var mwApiResult, excg, main, excgData; |
|||
( new mw.Api() ) |
|||
.get( { |
|||
action: 'query', |
|||
prop: 'revisions', |
|||
titles: item + '|Module:Exchange/' + item, |
|||
rvprop: 'content', |
|||
redirects: '' |
|||
} ) |
|||
.then( function (data) { |
|||
mwApiResult = data; |
|||
for ( var x in mwApiResult.query.mw.pages ) { |
|||
if ( mwApiResult.query.mw.pages.hasOwnProperty( x ) ) { |
|||
if ( x < 0 ) { |
|||
// the page does not exist |
|||
mw.log( mwApiResult.query.mw.pages[x] ); |
|||
continue; |
|||
} else if ( mwApiResult.query.mw.pages[x].ns === 828 ) { |
|||
excg = mwApiResult.query.mw.pages[x]; |
|||
} else if ( mwApiResult.query.mw.pages[x].ns === 0 ) { |
|||
main = mwApiResult.query.mw.pages[x]; |
|||
} |
|||
} |
|||
} |
|||
if ( excg ) { |
|||
excgData = rs.parseExchangeModule( excg.revisions[0]['*'] ); |
|||
excgData.itemId = excgData.itemId || excgData.itemid; // make this more robust? |
|||
$.getJSON("https://api.weirdgloop.org/exchange/history/osrs/latest?id=" + excgData.itemId) |
|||
.done( function (res) { |
|||
self.done(main, res[excgData.itemId]); |
|||
} ) |
|||
.fail( self.fail ); |
|||
} else { |
|||
self.done(main, {}); |
|||
} |
|||
} ) |
|||
.fail( self.fail ); |
|||
return false; |
|||
}, |
|||
/** |
|||
* Success callback for `jQuery.ajax` promise |
|||
*/ |
|||
done: function ( main, apiRes ) { |
|||
var bonuses = [ |
|||
'astab', |
|||
'aslash', |
|||
'acrush', |
|||
'amagic', |
|||
'arange', |
|||
'dstab', |
|||
'dslash', |
|||
'dcrush', |
|||
'dmagic', |
|||
'drange', |
|||
'str', |
|||
'rstr', |
|||
'mdmg', |
|||
'prayer', |
|||
'speed' |
|||
], |
|||
main, |
|||
x, |
|||
title, |
|||
content, |
|||
bonusData, |
|||
itemData, |
|||
$tr; |
|||
mw.log( main, apiRes ); |
|||
if ( !main ) { |
|||
self.showError( 'Could not find that item.' ); |
|||
return; |
|||
} |
|||
title = main.title; |
|||
content = main.revisions[0]['*']; |
|||
bonusData = rs.parseTemplate( 'infobox bonuses', content ); |
|||
itemData = rs.parseTemplate( 'infobox item', content ); |
|||
if ( $.isEmptyObject( bonusData ) ) { |
|||
self.showError( 'No bonus data found for the item.' ); |
|||
return; |
|||
} |
|||
$tr = $( '<tr>' ) |
|||
.append( |
|||
$( '<th>' ) |
|||
.append( |
|||
$( '<a>' ) |
|||
.attr( { |
|||
href: '#', |
|||
title: 'Remove this row' |
|||
} ) |
|||
.on( 'click', function () { |
|||
$( this ).closest( 'tr' ).fadeOut( 'slow', function () { |
|||
$( this ).remove(); |
|||
self.calcTotals(); |
|||
window.OOUIWindowManager.getCurrentWindow().updateSize(); |
|||
} ); |
|||
return false; |
|||
} ) |
|||
.append( self.img.del() ), |
|||
' ', |
|||
$( '<a>' ) |
|||
.attr( { |
|||
href: mw.mw.util.getUrl( title ), |
|||
title: title |
|||
} ) |
|||
.text( title ) |
|||
) |
|||
); |
|||
bonuses.forEach( function ( el ) { |
|||
// Use default version if defined, otherwise check if bonus has a version1 |
|||
var defaultVersion = $.isEmptyObject( itemData ) || (itemData.defver === undefined) ? '1' : itemData.defver; |
|||
var versionSpecificBonus = bonusData[el + defaultVersion]; |
|||
$tr.append( self.format( versionSpecificBonus === undefined ? bonusData[el] : versionSpecificBonus ) ); |
|||
} ); |
|||
$tr.append( self.format( !$.isEmptyObject( itemData ) ? itemData.weight : null ) ); |
|||
$tr.append( self.format( !$.isEmptyObject( apiRes ) ? rs.addCommas( apiRes.price ) : null ) ); |
|||
$( '#cioTotals' ).before( $tr ); |
|||
self.calcTotals(); |
|||
$( '#cioStatus' ).empty(); |
|||
$( '#cioItem > input' ).val( '' ); |
|||
window.OOUIWindowManager.getCurrentWindow().updateSize(); |
|||
}, |
|||
/** |
|||
* Error callback for `jQuery.ajax` promise |
|||
*/ |
|||
fail: function ( _, error ) { |
|||
self.showError( 'Error: ' + error ); |
|||
}, |
|||
/** |
|||
* Outputs error to the UI |
|||
* |
|||
* @param str {string} Error to display |
|||
*/ |
|||
showError: function ( str ) { |
|||
$( '#cioStatus' ) |
|||
.empty() |
|||
.attr( 'class', 'cioError' ) |
|||
.append( |
|||
self.img.error(), |
|||
' ' + str |
|||
); |
|||
}, |
|||
/** |
|||
* Formats each attribute's value and inserts it into a td cell |
|||
* |
|||
* @param str {string} Attribute value to format |
|||
* |
|||
* @return {jquery object} td cell to insert into the associated item's row |
|||
*/ |
|||
format: function ( str ) { |
|||
var $td = $( '<td>' ), |
|||
first; |
|||
// set `null` or `undefined` to an empty string |
|||
/*jshint eqnull:true */ |
|||
if ( str == null ) { |
|||
/* jshint eqnull:false */ |
|||
str = ''; |
|||
} |
|||
// remove comments |
|||
str = str.replace( /no|<!--.*?-->/gi, '' ).trim(); |
|||
// cache first character of `str` |
|||
first = str.substring( 0, 1 ); |
|||
if ( !str ) { |
|||
$td |
|||
.addClass( 'cioEmpty' ) |
|||
.text( '--' ); |
|||
} else if ( /\d/.test( first ) ) { |
|||
$td |
|||
.addClass( 'cioPos' ) |
|||
.text( '+' + str ); |
|||
} else if ( first === '-' ) { |
|||
$td |
|||
.addClass( 'cioNeg' ) |
|||
.text( str ); |
|||
} else { |
|||
$td |
|||
.text( str ); |
|||
} |
|||
return $td; |
|||
}, |
|||
formatTotals: function (str, index) { |
|||
var $td = $( '<td>' ), |
|||
first; |
|||
// set `null` or `undefined` to an empty string |
|||
/*jshint eqnull:true */ |
|||
if ( str == null || str === "null" ) { |
|||
/* jshint eqnull:false */ |
|||
str = ''; |
|||
} |
|||
// remove comments |
|||
str = str.replace( /no|<!--.*?-->/gi, '' ).trim(); |
|||
// cache first character of `str` |
|||
first = str.substring( 0, 1 ); |
|||
// lower is better for speed and weight - reverse colors |
|||
var lowerBetter = [14, 15].includes(index); |
|||
if ( !str ) { |
|||
$td |
|||
.addClass( 'cioEmpty' ) |
|||
.text( '--' ); |
|||
} else if (parseFloat(str, 10) === 0) { |
|||
$td |
|||
.addClass( 'table-bg-yellow ') |
|||
.text(str); |
|||
} else if ( /\d/.test( first ) ) { |
|||
$td |
|||
.addClass( lowerBetter ? 'table-bg-red' : 'table-bg-green' ) |
|||
.text( '+' + str ); |
|||
} else if ( first === '-' ) { |
|||
$td |
|||
.addClass( lowerBetter ? 'table-bg-green' : 'table-bg-red' ) |
|||
.text( str ); |
|||
} else { |
|||
$td.text( str ); |
|||
} |
|||
// context dependent whether higher or lower price is better - just don't color it |
|||
if (index === 16) { |
|||
$td.removeClass("table-bg-green table-bg-yellow table-bg-red"); |
|||
} |
|||
return $td; |
|||
}, |
|||
/** |
|||
* Calculate bonus totals |
|||
*/ |
|||
calcTotals: function () { |
|||
// 19 0's, one for each attribute |
|||
var totals = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], |
|||
$totals = $( '#cioTotals' ); |
|||
// don't show totals row when not comparing 2 or more items |
|||
if ($( '#cioItems tbody tr:not( #cioTotals )' ).length < 2) { |
|||
$totals.empty(); |
|||
return; |
|||
} |
|||
$( '#cioItems tbody tr:not( #cioTotals ):first td' ).each(function(i) { |
|||
var num = parseFloat($(this).text().replace(/,/g, "")); |
|||
totals[i] = isNaN(num) ? null : num; |
|||
}); |
|||
$( '#cioItems tbody tr:not( #cioTotals ):not(:first)' ).each( function () { |
|||
$( this ).children( 'td' ).each( function ( i ) { |
|||
if (totals[i] !== null) { |
|||
var num = parseFloat($(this).text().replace(/,/g, "")); |
|||
if (isNaN(num)) { |
|||
totals[i] = null; |
|||
} |
|||
else { |
|||
totals[i] -= num; |
|||
} |
|||
} |
|||
} ); |
|||
} ); |
|||
$totals |
|||
.empty() |
|||
.append( |
|||
$( '<th>' ) |
|||
.text( 'Diff' ) |
|||
); |
|||
totals.forEach( function ( elem, index ) { |
|||
$totals.append( |
|||
self.formatTotals( |
|||
// don't total speed |
|||
// 14th index/column respectively |
|||
// [14].indexOf( index ) > -1 ? null : rs.addCommas( elem ) |
|||
rs.addCommas(elem), index |
|||
) |
|||
); |
|||
} ); |
|||
}, |
|||
checkSign: function (value) { |
|||
return value === 0 ? true : (value > 0 ? true : false); |
|||
} |
|||
}; |
|||
$(function(){mw.loader.using( ['mediawiki.mw.util', 'mediawiki.api', 'ext.gadget.rsw-mw.util'], self.init )}); |
Revision as of 17:12, 17 October 2024
/** * Adds links for compare popups * * @author Quarenon * @author Ryan PM * @author Joeytje50 * @author Cqm * @author JaydenKieran * * @license GPLv3 <https://www.gnu.org/licenses/gpl-3.0.html> * * @todo try to find a standard img url domain to use * @todo re-center (vertical & horizontally) with new items added, or find a way to do it with pure CSS * might require overhaul to #overlay structure/styles */'use strict';var modalOpenedPrev = false;var conf = mw.config.get( [ 'stylepath', 'wgTitle' ] ), self = { /** * Inital loading method */ init: function () { self.buildModal(); var $compare = $( '.cioCompareLink' ), $ibox = $( '.infobox-bonuses' ); $compare.each( function () { var $this = $( this ), props = ( $this.attr( 'title' ) || '' ).split( '|' ), text = props[0] !== '' ? props[0] : 'Compare items', items = props.length >= 2 ? props.slice( 1 ) : [conf.wgTitle], $a = $( '<a>' ) .attr( { href: '#', title: 'Compare this item with other items', 'data-items': items.join( '|' ) } ) .text( text ) .on( 'click', self.open ); $this .empty() .append( $a ) .parent() .show(); } ); $ibox.each( function () { var $this = $( this ) // insert new row with compare link var button = new OO.ui.ButtonWidget( { label: 'Compare', title: 'Compare this item with other items', flags: 'primary' } ); $this.after( button.$element .attr( { 'data-items': conf.wgTitle }) .on( 'click', self.open ) ); } ); }, /** * Images * * These are functions to avoid us having to use .clone() * and to avoid potential memory leaks */ img: { /** * Delete image * * @return {jquery object} */ del: function () { return $( '<img>' ) .attr( { src: "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Cg fill='%23d33'%3E%3Cpath d='m4.3 2.9 12.8 12.8-1.4 1.4L2.9 4.3z'/%3E%3Cpath d='M17.1 4.3 4.3 17.1l-1.4-1.4L15.7 2.9z'/%3E%3C/g%3E%3C/svg%3E%0A", width: 16, height: 16, alt: 'Delete' } ); }, /** * Loading image * * @return {jquery object} */ loading: function () { return $( '<img>' ) .attr( { // .gif can't be converted to data: URI src: 'https://oldschool.runescape.wiki/images/2/23/Progress-wheel.gif?0a2fe', width: 16, height: 16, alt: '...' } ); }, /** * Error image * * @return {jquery object} */ error: function () { return $( '<img>' ) .attr( { src: "data:image/svg+xml,%3C%3Fxml version='1.0' encoding='UTF-8'%3F%3E%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Cg fill='%23d33'%3E%3Cpath d='M13.728 1H6.272L1 6.272v7.456L6.272 19h7.456L19 13.728V6.272zM11 15H9v-2h2zm0-4H9V5h2z'/%3E%3C/g%3E%3C/svg%3E%0A", width: 16, height: 16, alt: 'Error' } ); } }, /** * Modal open method * * Callback to on click event * * @param e {jquery.event} */ open: function ( e ) { e.preventDefault(); window.OOUIWindowManager.openWindow( 'compare' ); if (!modalOpenedPrev) { // avoid init-ing modalOpenedPrev = true; var items = $( this ).attr( 'data-items' ).split( '|' ); items.forEach( self.submit ); } }, /** * Builds the compare modal * * @return {jquery object} */ buildModal: function () { var init = function (modal) { modal.content = new OO.ui.PanelLayout( { padded: true, expanded: false } ); var button1 = new OO.ui.ButtonWidget( { flags: [ 'destructive' ], label: 'Cancel' } ); var b1click = ('click', function(modal) { window.OOUIWindowManager.closeWindow(modal); }); button1.on('click', b1click, [modal]); var button2 = new OO.ui.ButtonWidget( { label: 'Submit', flags: [ 'progressive' ], }); button2.on('click', function() { self.submit(); }); var input1 = new OO.ui.TextInputWidget({ id: 'cioItem' }); input1.on('enter', function(){ self.submit(); }); // Create OOUI JS fieldset var fieldset = new OO.ui.FieldsetLayout( { label: 'Comparing ' + conf.wgTitle, id: 'cioCompare' } ); fieldset.addItems( [ new OO.ui.ActionFieldLayout( input1, button2, { label: 'Compare with', align: 'inline', notices: [new OO.ui.HtmlSnippet('<div id="cioStatus"></div>')] } ) ] ); modal.content.$element.append($('<div>').append(fieldset.$element).append( $( '<table>' ) .addClass( 'wikitable' ) .attr( 'id', 'cioItems' ) .append( $( '<thead>' ) .append( $( '<tr>' ) .append( $( '<th>' ) .attr( 'rowspan', '2' ) .text( 'Name' ), $( '<th>' ) .attr( 'colspan', '5' ) .text( 'Attack bonuses' ), $( '<th>' ) .attr( 'colspan', '5' ) .text( 'Defence bonuses' ), $( '<th>' ) .attr( 'colspan', '4' ) .text( 'Other bonuses' ), $( '<th>' ) .attr( 'width', '30' ) .text( 'Speed' ), $( '<th>' ) .attr( 'width', '30' ) .text( 'Weight' ), $( '<th>' ) .attr( 'width', '30' ) .text( 'GE' ) ), $( '<tr>' ) .attr( 'height', '35' ) .append( $( '<th>' ) .attr( { class: 'cioIcon-stab', title: 'Stab bonus', width: '35' } ), $( '<th>' ) .attr( { class: 'cioIcon-slash', title: 'Slash bonus', width: '35' } ), $( '<th>' ) .attr( { class: 'cioIcon-crush', title: 'Crush bonus', width: '35' } ), $( '<th>' ) .attr( { class: 'cioIcon-magic', title: 'Magic bonus', width: '35' } ), $( '<th>' ) .attr( { class: 'cioIcon-ranged', title: 'Ranged bonus', width: '35' } ), $( '<th>' ) .attr( { class: 'cioIcon-stab', title: 'Stab bonus', width: '35' } ), $( '<th>' ) .attr( { class: 'cioIcon-slash', title: 'Slash bonus', width: '35' } ), $( '<th>' ) .attr( { class: 'cioIcon-crush', title: 'Crush bonus', width: '35' } ), $( '<th>' ) .attr( { class: 'cioIcon-magic', title: 'Magic bonus', width: '35' } ), $( '<th>' ) .attr( { class: 'cioIcon-ranged', title: 'Ranged bonus', width: '35' } ), $( '<th>' ) .attr( { class: 'cioIcon-strength', title: 'Strength bonus', width: '35' } ), $( '<th>' ) .attr( { class: 'cioIcon-rangedstrength', title: 'Ranged Strength bonus', width: '35' } ), $( '<th>' ) .attr( { class: 'cioIcon-magicdamage', title: 'Magic Damage bonus', width: '35' } ), $( '<th>' ) .attr( { class: 'cioIcon-prayer', title: 'Prayer bonus', width: '35' } ), $( '<th>' ) .attr( { class: 'cioIcon-speed', title: 'Speed', width: '35' } ), $( '<th>' ) .attr( { class: 'cioIcon-weight', title: 'Weight (kg)', width: '35' } ), $( '<th>' ) .attr( { class: 'cioIcon-price', title: 'Grand Exchange Price', width: '35' } ) ) ), $( '<tbody>' ) .append( $( '<tr>' ) .attr( 'id', 'cioTotals' ) // .addClass('table-bg-green') ) ), button1.$element )); modal.$body.append( modal.content.$element ); }; rs.createOOUIWindow('compare', 'Compare with other items', {size: 'larger', classes: ['rs-compare-modal', 'oo-ui-compare-width']}, init); }, /** * Initial callback for adding new items to the UI * * @param elem {string} (optional) */ submit: function ( elem ) { var item = elem || $( '#cioItem > input' ).val(); $( '#cioStatus' ) .empty() .attr( 'class', 'cioLoading' ) .append( self.img.loading(), ' Loading...' ); // make sure first letter of item is uppercase // otherwise price data won't be found item = item.charAt( 0 ).toUpperCase() + item.slice( 1 ); var mwApiResult, excg, main, excgData; ( new mw.Api() ) .get( { action: 'query', prop: 'revisions', titles: item + '|Module:Exchange/' + item, rvprop: 'content', redirects: '' } ) .then( function (data) { mwApiResult = data; for ( var x in mwApiResult.query.pages ) { if ( mwApiResult.query.pages.hasOwnProperty( x ) ) { if ( x < 0 ) { // the page does not exist mw.log( mwApiResult.query.pages[x] ); continue; } else if ( mwApiResult.query.pages[x].ns === 828 ) { excg = mwApiResult.query.pages[x]; } else if ( mwApiResult.query.pages[x].ns === 0 ) { main = mwApiResult.query.pages[x]; } } } if ( excg ) { excgData = rs.parseExchangeModule( excg.revisions[0]['*'] ); excgData.itemId = excgData.itemId || excgData.itemid; // make this more robust? $.getJSON("https://api.weirdgloop.org/exchange/history/osrs/latest?id=" + excgData.itemId) .done( function (res) { self.done(main, res[excgData.itemId]); } ) .fail( self.fail ); } else { self.done(main, {}); } } ) .fail( self.fail ); return false; }, /** * Success callback for `jQuery.ajax` promise */ done: function ( main, apiRes ) { var bonuses = [ 'astab', 'aslash', 'acrush', 'amagic', 'arange', 'dstab', 'dslash', 'dcrush', 'dmagic', 'drange', 'str', 'rstr', 'mdmg', 'prayer', 'speed' ], main, x, title, content, bonusData, itemData, $tr; mw.log( main, apiRes ); if ( !main ) { self.showError( 'Could not find that item.' ); return; } title = main.title; content = main.revisions[0]['*']; bonusData = rs.parseTemplate( 'infobox bonuses', content ); itemData = rs.parseTemplate( 'infobox item', content ); if ( $.isEmptyObject( bonusData ) ) { self.showError( 'No bonus data found for the item.' ); return; } $tr = $( '<tr>' ) .append( $( '<th>' ) .append( $( '<a>' ) .attr( { href: '#', title: 'Remove this row' } ) .on( 'click', function () { $( this ).closest( 'tr' ).fadeOut( 'slow', function () { $( this ).remove(); self.calcTotals(); window.OOUIWindowManager.getCurrentWindow().updateSize(); } ); return false; } ) .append( self.img.del() ), ' ', $( '<a>' ) .attr( { href: mw.util.getUrl( title ), title: title } ) .text( title ) ) ); bonuses.forEach( function ( el ) { // Use default version if defined, otherwise check if bonus has a version1 var defaultVersion = $.isEmptyObject( itemData ) || (itemData.defver === undefined) ? '1' : itemData.defver; var versionSpecificBonus = bonusData[el + defaultVersion]; $tr.append( self.format( versionSpecificBonus === undefined ? bonusData[el] : versionSpecificBonus ) ); } ); $tr.append( self.format( !$.isEmptyObject( itemData ) ? itemData.weight : null ) ); $tr.append( self.format( !$.isEmptyObject( apiRes ) ? rs.addCommas( apiRes.price ) : null ) ); $( '#cioTotals' ).before( $tr ); self.calcTotals(); $( '#cioStatus' ).empty(); $( '#cioItem > input' ).val( '' ); window.OOUIWindowManager.getCurrentWindow().updateSize(); }, /** * Error callback for `jQuery.ajax` promise */ fail: function ( _, error ) { self.showError( 'Error: ' + error ); }, /** * Outputs error to the UI * * @param str {string} Error to display */ showError: function ( str ) { $( '#cioStatus' ) .empty() .attr( 'class', 'cioError' ) .append( self.img.error(), ' ' + str ); }, /** * Formats each attribute's value and inserts it into a td cell * * @param str {string} Attribute value to format * * @return {jquery object} td cell to insert into the associated item's row */ format: function ( str ) { var $td = $( '<td>' ), first; // set `null` or `undefined` to an empty string /*jshint eqnull:true */ if ( str == null ) { /* jshint eqnull:false */ str = ''; } // remove comments str = str.replace( /no|<!--.*?-->/gi, '' ).trim(); // cache first character of `str` first = str.substring( 0, 1 ); if ( !str ) { $td .addClass( 'cioEmpty' ) .text( '--' ); } else if ( /\d/.test( first ) ) { $td .addClass( 'cioPos' ) .text( '+' + str ); } else if ( first === '-' ) { $td .addClass( 'cioNeg' ) .text( str ); } else { $td .text( str ); } return $td; }, formatTotals: function (str, index) { var $td = $( '<td>' ), first; // set `null` or `undefined` to an empty string /*jshint eqnull:true */ if ( str == null || str === "null" ) { /* jshint eqnull:false */ str = ''; } // remove comments str = str.replace( /no|<!--.*?-->/gi, '' ).trim(); // cache first character of `str` first = str.substring( 0, 1 ); // lower is better for speed and weight - reverse colors var lowerBetter = [14, 15].includes(index); if ( !str ) { $td .addClass( 'cioEmpty' ) .text( '--' ); } else if (parseFloat(str, 10) === 0) { $td .addClass( 'table-bg-yellow ') .text(str); } else if ( /\d/.test( first ) ) { $td .addClass( lowerBetter ? 'table-bg-red' : 'table-bg-green' ) .text( '+' + str ); } else if ( first === '-' ) { $td .addClass( lowerBetter ? 'table-bg-green' : 'table-bg-red' ) .text( str ); } else { $td.text( str ); } // context dependent whether higher or lower price is better - just don't color it if (index === 16) { $td.removeClass("table-bg-green table-bg-yellow table-bg-red"); } return $td; }, /** * Calculate bonus totals */ calcTotals: function () { // 19 0's, one for each attribute var totals = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], $totals = $( '#cioTotals' ); // don't show totals row when not comparing 2 or more items if ($( '#cioItems tbody tr:not( #cioTotals )' ).length < 2) { $totals.empty(); return; } $( '#cioItems tbody tr:not( #cioTotals ):first td' ).each(function(i) { var num = parseFloat($(this).text().replace(/,/g, "")); totals[i] = isNaN(num) ? null : num; }); $( '#cioItems tbody tr:not( #cioTotals ):not(:first)' ).each( function () { $( this ).children( 'td' ).each( function ( i ) { if (totals[i] !== null) { var num = parseFloat($(this).text().replace(/,/g, "")); if (isNaN(num)) { totals[i] = null; } else { totals[i] -= num; } } } ); } ); $totals .empty() .append( $( '<th>' ) .text( 'Diff' ) ); totals.forEach( function ( elem, index ) { $totals.append( self.formatTotals( // don't total speed // 14th index/column respectively // [14].indexOf( index ) > -1 ? null : rs.addCommas( elem ) rs.addCommas(elem), index ) ); } ); }, checkSign: function (value) { return value === 0 ? true : (value > 0 ? true : false); } };$(function(){mw.loader.using( ['mediawiki.util', 'mediawiki.api', 'ext.gadget.rsw-util'], self.init )});