MediaWiki:Gadget-compare-core.js: Difference between revisions

From RuneRealm Wiki
Jump to navigation Jump to search
Content added Content deleted
 
No edit summary
 
(3 intermediate revisions by the same user not shown)
Line 18: Line 18:


var modalOpenedPrev = false;
var modalOpenedPrev = false;
var conf = mw.config.get(['stylepath', 'wgTitle']),
self = {
/**
* Inital loading method
*/
init: function init() {
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 del() {
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 loading() {
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 error() {
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 open(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 buildModal() {
var init = function init(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 conf = mw.config.get( [
var fieldset = new OO.ui.FieldsetLayout({
'stylepath',
label: 'Comparing ' + conf.wgTitle,
'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 submit(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
self = {
// otherwise price data won't be found
/**
item = item.charAt(0).toUpperCase() + item.slice(1);
* Inital loading method
var mwApiResult, excg, main, excgData;
*/
new mw.Api().get({
init: function () {
action: 'query',
self.buildModal();
prop: 'revisions',
titles: item + '|Module:Exchange/' + item,
var $compare = $( '.cioCompareLink' ),
rvprop: 'content',
$ibox = $( '.infobox-bonuses' );
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) {
$compare.each( function () {
self.done(main, res[excgData.itemId]);
var $this = $( this ),
}).fail(self.fail);
props = ( $this.attr( 'title' ) || '' ).split( '|' ),
} else {
text = props[0] !== '' ? props[0] : 'Compare items',
self.done(main, {});
items = props.length >= 2 ? props.slice( 1 ) : [conf.wgTitle],
}
$a = $( '<a>' )
}).fail(self.fail);
.attr( {
return false;
href: '#',
},
title: 'Compare this item with other items',
/**
'data-items': items.join( '|' )
* Success callback for `jQuery.ajax` promise
} )
*/
.text( text )
done: function done(main, apiRes) {
.on( 'click', self.open );
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()), '&nbsp;', $('<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 fail(_, error) {
self.showError('Error: ' + error);
},
/**
* Outputs error to the UI
*
* @param str {string} Error to display
*/
showError: function showError(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 format(str) {
var $td = $('<td>'),
first;


// set `null` or `undefined` to an empty string
$this
/*jshint eqnull:true */
.empty()
if (str == null) {
.append( $a )
/* jshint eqnull:false */
.parent()
str = '';
.show();
}
} );


// remove comments
$ibox.each( function () {
str = str.replace(/no|<!--.*?-->/gi, '').trim();
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'
} );


// cache first character of `str`
$this.after( button.$element
first = str.substring(0, 1);
.attr( {
if (!str) {
'data-items': conf.wgTitle
$td.addClass('cioEmpty').text('--');
})
} else if (/\d/.test(first)) {
.on( 'click', self.open )
$td.addClass('cioPos').text('+' + str);
);
} else if (first === '-') {
} );
$td.addClass('cioNeg').text(str);
} else {
$td.text(str);
}
return $td;
},
formatTotals: function formatTotals(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();
* 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'
} );
},


// cache first character of `str`
/**
first = str.substring(0, 1);
* 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: '...'
} );
},


// lower is better for speed and weight - reverse colors
/**
var lowerBetter = [14, 15].includes(index);
* Error image
if (!str) {
*
$td.addClass('cioEmpty').text('--');
* @return {jquery object}
} else if (parseFloat(str, 10) === 0) {
*/
$td.addClass('table-bg-yellow ').text(str);
error: function () {
} else if (/\d/.test(first)) {
return $( '<img>' )
$td.addClass(lowerBetter ? 'table-bg-red' : 'table-bg-green').text('+' + str);
.attr( {
} else if (first === '-') {
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",
$td.addClass(lowerBetter ? 'table-bg-green' : 'table-bg-red').text(str);
width: 16,
} else {
height: 16,
$td.text(str);
alt: 'Error'
}
} );
}
},


// context dependent whether higher or lower price is better - just don't color it
/**
if (index === 16) {
* Modal open method
$td.removeClass("table-bg-green table-bg-yellow table-bg-red");
*
}
* Callback to on click event
return $td;
*
},
* @param e {jquery.event}
*/
/**
* Calculate bonus totals
open: function ( e ) {
*/
e.preventDefault();
calcTotals: function calcTotals() {
window.OOUIWindowManager.openWindow( 'compare' );
// 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],
if (!modalOpenedPrev) { // avoid init-ing
$totals = $('#cioTotals');
modalOpenedPrev = true;
var items = $( this ).attr( 'data-items' ).split( '|' );
items.forEach( self.submit );
}
},


// don't show totals row when not comparing 2 or more items
/**
if ($('#cioItems tbody tr:not( #cioTotals )').length < 2) {
* Builds the compare modal
$totals.empty();
*
return;
* @return {jquery object}
}
*/
$('#cioItems tbody tr:not( #cioTotals ):first td').each(function (i) {
buildModal: function () {
var num = parseFloat($(this).text().replace(/,/g, ""));
var init = function (modal) {
totals[i] = isNaN(num) ? null : num;
modal.content = new OO.ui.PanelLayout( { padded: true, expanded: false } );
});

$('#cioItems tbody tr:not( #cioTotals ):not(:first)').each(function () {
var button1 = new OO.ui.ButtonWidget( {
$(this).children('td').each(function (i) {
flags: [ 'destructive' ],
if (totals[i] !== null) {
label: 'Cancel'
var num = parseFloat($(this).text().replace(/,/g, ""));
} );
if (isNaN(num)) {
var b1click = ('click', function(modal) {
totals[i] = null;
window.OOUIWindowManager.closeWindow(modal);
} else {
});
totals[i] -= num;
button1.on('click', b1click, [modal]);
}
}
var button2 = new OO.ui.ButtonWidget( {
});
label: 'Submit',
});
flags: [ 'progressive' ],
$totals.empty().append($('<th>').text('Diff'));
});
button2.on('click', function() {
totals.forEach(function (elem, index) {
$totals.append(self.formatTotals(
self.submit();
// don't total speed
});
// 14th index/column respectively
// [14].indexOf( index ) > -1 ? null : rs.addCommas( elem )
var input1 = new OO.ui.TextInputWidget({ id: 'cioItem' });
rs.addCommas(elem), index));
input1.on('enter', function(){
});
self.submit();
},
});
checkSign: function checkSign(value) {
return value === 0 ? true : value > 0 ? true : false;
// Create OOUI JS fieldset
}
var fieldset = new OO.ui.FieldsetLayout( {
};
label: 'Comparing ' + conf.wgTitle,
$(function () {
id: 'cioCompare'
mw.loader.using(['mediawiki.util', 'mediawiki.api', 'ext.gadget.rsw-util'], self.init);
} );
});
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() ),
'&nbsp;',
$( '<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 )});

Latest revision as of 12:06, 20 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 init() {
      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 del() {
        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 loading() {
        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 error() {
        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 open(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 buildModal() {
      var init = function init(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 submit(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 done(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()), '&nbsp;', $('<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 fail(_, error) {
      self.showError('Error: ' + error);
    },
    /**
     * Outputs error to the UI
     *
     * @param str {string} Error to display
     */
    showError: function showError(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 format(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 formatTotals(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 calcTotals() {
      // 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 checkSign(value) {
      return value === 0 ? true : value > 0 ? true : false;
    }
  };
$(function () {
  mw.loader.using(['mediawiki.util', 'mediawiki.api', 'ext.gadget.rsw-util'], self.init);
});