MediaWiki:Gadget-compare-core.js
After saving, you may need to bypass your browser's cache to see the changes. For further information, see Wikipedia:Bypass your cache.
- In most Windows and Linux browsers: Hold down Ctrl and press F5.
- In Safari: Hold down ⇧ Shift and click the Reload button.
- In Chrome and Firefox for Mac: Hold down both ⌘ Cmd+⇧ Shift and press R.
/**
* 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()), ' ', $('<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);
});