MediaWiki:Gadget-compare-core.js
Jump to navigation
Jump to search
Note: After publishing, you may have to bypass your browser's cache to see the changes.
- Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
- Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
- Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
- Opera: Press Ctrl-F5.
1 /** <nowiki>
2 * Adds links for compare popups
3 *
4 * @author Quarenon
5 * @author Ryan PM
6 * @author Joeytje50
7 * @author Cqm
8 * @author JaydenKieran
9 *
10 * @license GPLv3 <https://www.gnu.org/licenses/gpl-3.0.html>
11 *
12 * @todo try to find a standard img url domain to use
13 * @todo re-center (vertical & horizontally) with new items added, or find a way to do it with pure CSS
14 * might require overhaul to #overlay structure/styles
15 */
16
17 'use strict';
18
19 var modalOpenedPrev = false;
20
21 var conf = mw.config.get( [
22 'stylepath',
23 'wgTitle'
24 ] ),
25
26 self = {
27 /**
28 * Inital loading method
29 */
30 init: function () {
31 self.buildModal();
32
33 var $compare = $( '.cioCompareLink' ),
34 $ibox = $( '.infobox-bonuses' );
35
36 $compare.each( function () {
37 var $this = $( this ),
38 props = ( $this.attr( 'title' ) || '' ).split( '|' ),
39 text = props[0] !== '' ? props[0] : 'Compare items',
40 items = props.length >= 2 ? props.slice( 1 ) : [conf.wgTitle],
41 $a = $( '<a>' )
42 .attr( {
43 href: '#',
44 title: 'Compare this item with other items',
45 'data-items': items.join( '|' )
46 } )
47 .text( text )
48 .on( 'click', self.open );
49
50 $this
51 .empty()
52 .append( $a )
53 .parent()
54 .show();
55 } );
56
57 $ibox.each( function () {
58 var $this = $( this )
59 // insert new row with compare link
60 var button = new OO.ui.ButtonWidget( {
61 label: 'Compare',
62 title: 'Compare this item with other items',
63 flags: 'primary'
64 } );
65
66 $this.after( button.$element
67 .css({'margin-left':'1em'})
68 .attr( {
69 'data-items': conf.wgTitle
70 })
71 .on( 'click', self.open )
72 );
73 } );
74
75 },
76
77 /**
78 * Images
79 *
80 * These are functions to avoid us having to use .clone()
81 * and to avoid potential memory leaks
82 */
83 img: {
84 /**
85 * Delete image
86 *
87 * @return {jquery object}
88 */
89 del: function () {
90 return $( '<img>' )
91 .attr( {
92 src: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAANCAYAAACZ3F9/AAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHWSURBVHjadFJBTxNREP7m7ba1kRAg2SZyUKFcCHAhXI134w/Qg+FCAt7ECwgXygESQMKlRDQxXjRKQq1COBEPeBBixB8gHGxSmrbJtuUA3V12x7e76WshcZI5vPnmm2/mywMzw89qsczmryNuvK+nefjzCiYgo1Yosr2ehrWygsr+d8a1qOx9Y2v1JcoLiwqjWv6UrXQabi4HCDknoiP2ZBSd9++RIn14D7gu4HnQentgpFKkt3ffopLj+PqApgGXLuyNVzD9fep1OO/eAjfiISbJ7Hqhoo+f5Qtcf/0G3skfUDQa3EByetAYiYCIwLYNrb8fxuwMKWIjilPTzPk8KBZDox6QpLJIJpGYT5G6sZXoR2nyGXuVGshfTQZLVWEYSCwvUWufaH2YW5+Zi+XQJNUhwKcFmDu7VxSUormVYTuTAeSNcj9p0mXYoesIjHMcRB89RtfDB6QUzY+bbGezTZK8yTdC3O0BLCusyQH25ieYmWygJKrHJ+xsf23uICdrIyMwXkxTYn6O9MGhoBasL512vmRR/Ztj0dGXpMj4OCgeB19cQB8ehvF8UhkRHRuDNiDJ5+dA203EJp6i485tav7FHwdcXl37718tLS1z5ei3wv8JMADpOhbE0Ou4cwAAAABJRU5ErkJggg==',
93 width: 14,
94 height: 13,
95 alt: '[X]'
96 } );
97 },
98
99 /**
100 * Loading image
101 *
102 * @return {jquery object}
103 */
104 loading: function () {
105 return $( '<img>' )
106 .attr( {
107 // .gif can't be converted to data: URI
108 src: 'https://oldschool.runescape.wiki/images/2/23/Progress-wheel.gif?0a2fe',
109 width: 16,
110 height: 16,
111 alt: '...'
112 } );
113 },
114
115 /**
116 * Error image
117 *
118 * @return {jquery object}
119 */
120 error: function () {
121 return $( '<img>' )
122 .attr( {
123 src: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAcpJREFUeNpi3FvPwoAHMEPpv+gSTg2/wTQTPs2MjMx/QBjJIAyAz4AKMS0/BjHtQDCbVAPkmNm4WxSdyhkUHYoYQGyQGCkG5MvbFDCwcfECMTeDvHUuSKyIWANs2Hkli6SNoxh+fzzK8PvDQQYZowgGTkGFfKCcKzEGZCo5VTMw/v/BcHxGGsPxWXkM//+9Y1ByLAPLETIgmV/WNEpMzY7h59uNcMGfr9cxCCsaMfDLmIBCNA2fAZnKTpUMf77fYPj/5xNC9O9Xhr/frjGouJQxAKN1JnK0IhtQLa7lZ8wjLMLw6+02BoY/H+ES//9+AbpiPQOXAD+DpEEYSKgRJscITYnSzGxcT0yS1jIw/TzJ8OfLJZAUSCsQ/WH4/x+Y6v79YmDmUmFg5g9kODUvnOHPj49KwNR4H+aCfDnzVAZWtr/AUD8EtPEzGJ9evY7h9JpNQC98Awbkd4Y/n88zMDF/Y5CzSAHrgbkAGG3ih03iFzD8fD4RaOEXoCgjkgv+Q7LCf0h2YGTiYuCUq2Y4sziZ4fv7Rz4s4GizLwTq+cbAIZnMQAz4/+8jg7JTGcOVtTmZIAOmX98CTupRDKSBrSC9jP/BTiQfAAQYAMjEmRhwR6odAAAAAElFTkSuQmCC',
124 width: 16,
125 height: 16,
126 alt: '!!'
127 } );
128 }
129 },
130
131 /**
132 * Modal open method
133 *
134 * Callback to on click event
135 *
136 * @param e {jquery.event}
137 */
138 open: function ( e ) {
139 e.preventDefault();
140 window.OOUIWindowManager.openWindow( 'compare' );
141
142 if (!modalOpenedPrev) { // avoid init-ing
143 modalOpenedPrev = true;
144 var items = $( this ).attr( 'data-items' ).split( '|' );
145 items.forEach( self.submit );
146 }
147 },
148
149 /**
150 * Builds the compare modal
151 *
152 * @return {jquery object}
153 */
154 buildModal: function () {
155 var init = function (modal) {
156 modal.content = new OO.ui.PanelLayout( { padded: true, expanded: false } );
157
158 var button1 = new OO.ui.ButtonWidget( {
159 flags: [ 'destructive' ],
160 label: 'Cancel'
161 } );
162 var b1click = ('click', function(modal) {
163 window.OOUIWindowManager.closeWindow(modal);
164 });
165 button1.on('click', b1click, [modal]);
166
167 var button2 = new OO.ui.ButtonWidget( {
168 label: 'Submit',
169 flags: [ 'progressive' ],
170 });
171 button2.on('click', function() {
172 self.submit();
173 });
174
175 var input1 = new OO.ui.TextInputWidget({ id: 'cioItem' });
176 input1.on('enter', function(){
177 self.submit();
178 });
179
180 // Create OOUI JS fieldset
181 var fieldset = new OO.ui.FieldsetLayout( {
182 label: 'Comparing ' + conf.wgTitle,
183 id: 'cioCompare'
184 } );
185
186 fieldset.addItems( [
187 new OO.ui.ActionFieldLayout(
188 input1,
189 button2,
190 { label: 'Compare with', align: 'inline', notices: [new OO.ui.HtmlSnippet('<div id="cioStatus"></div>')] }
191 )
192 ] );
193
194 modal.content.$element.append($('<div>').append(fieldset.$element).append(
195 $( '<table>' )
196 .addClass( 'wikitable' )
197 .attr( 'id', 'cioItems' )
198 .append(
199 $( '<thead>' )
200 .append(
201 $( '<tr>' )
202 .append(
203 $( '<th>' )
204 .attr( 'rowspan', '2' )
205 .text( 'Name' ),
206 $( '<th>' )
207 .attr( 'colspan', '5' )
208 .text( 'Attack bonuses' ),
209 $( '<th>' )
210 .attr( 'colspan', '5' )
211 .text( 'Defence bonuses' ),
212 $( '<th>' )
213 .attr( 'colspan', '4' )
214 .text( 'Other bonuses' ),
215 $( '<th>' )
216 .attr( 'width', '30' )
217 .text( 'Speed' ),
218 $( '<th>' )
219 .attr( 'width', '30' )
220 .text( 'Weight' ),
221 $( '<th>' )
222 .attr( 'width', '30' )
223 .text( 'GE' )
224 ),
225 $( '<tr>' )
226 .attr( 'height', '35' )
227 .append(
228 $( '<th>' )
229 .attr( {
230 class: 'cioIcon-stab',
231 title: 'Stab bonus',
232 width: '35'
233 } ),
234 $( '<th>' )
235 .attr( {
236 class: 'cioIcon-slash',
237 title: 'Slash bonus',
238 width: '35'
239 } ),
240 $( '<th>' )
241 .attr( {
242 class: 'cioIcon-crush',
243 title: 'Crush bonus',
244 width: '35'
245 } ),
246 $( '<th>' )
247 .attr( {
248 class: 'cioIcon-magic',
249 title: 'Magic bonus',
250 width: '35'
251 } ),
252 $( '<th>' )
253 .attr( {
254 class: 'cioIcon-ranged',
255 title: 'Ranged bonus',
256 width: '35'
257 } ),
258 $( '<th>' )
259 .attr( {
260 class: 'cioIcon-stab',
261 title: 'Stab bonus',
262 width: '35'
263 } ),
264 $( '<th>' )
265 .attr( {
266 class: 'cioIcon-slash',
267 title: 'Slash bonus',
268 width: '35'
269 } ),
270 $( '<th>' )
271 .attr( {
272 class: 'cioIcon-crush',
273 title: 'Crush bonus',
274 width: '35'
275 } ),
276 $( '<th>' )
277 .attr( {
278 class: 'cioIcon-magic',
279 title: 'Magic bonus',
280 width: '35'
281 } ),
282 $( '<th>' )
283 .attr( {
284 class: 'cioIcon-ranged',
285 title: 'Ranged bonus',
286 width: '35'
287 } ),
288 $( '<th>' )
289 .attr( {
290 class: 'cioIcon-strength',
291 title: 'Strength bonus',
292 width: '35'
293 } ),
294 $( '<th>' )
295 .attr( {
296 class: 'cioIcon-rangedstrength',
297 title: 'Ranged Strength bonus',
298 width: '35'
299 } ),
300 $( '<th>' )
301 .attr( {
302 class: 'cioIcon-magicdamage',
303 title: 'Magic Damage bonus',
304 width: '35'
305 } ),
306 $( '<th>' )
307 .attr( {
308 class: 'cioIcon-prayer',
309 title: 'Prayer bonus',
310 width: '35'
311 } ),
312 $( '<th>' )
313 .attr( {
314 class: 'cioIcon-speed',
315 title: 'Speed',
316 width: '35'
317 } ),
318 $( '<th>' )
319 .attr( {
320 class: 'cioIcon-weight',
321 title: 'Weight (kg)',
322 width: '35'
323 } ),
324 $( '<th>' )
325 .attr( {
326 class: 'cioIcon-price',
327 title: 'Grand Exchange Price',
328 width: '35'
329 } )
330 )
331 ),
332 $( '<tbody>' )
333 .append(
334 $( '<tr>' )
335 .attr( 'id', 'cioTotals' )
336 // .addClass('table-bg-green')
337 )
338 ),
339 button1.$element
340 ));
341 modal.$body.append( modal.content.$element );
342 };
343 rs.createOOUIWindow('compare', 'Compare with other items', {size: 'larger', classes: ['rs-compare-modal', 'oo-ui-compare-width']}, init);
344 },
345
346 /**
347 * Initial callback for adding new items to the UI
348 *
349 * @param elem {string} (optional)
350 */
351 submit: function ( elem ) {
352 var item = elem || $( '#cioItem > input' ).val();
353
354 $( '#cioStatus' )
355 .empty()
356 .attr( 'class', 'cioLoading' )
357 .append(
358 self.img.loading(),
359 ' Loading...'
360 );
361
362 // make sure first letter of item is uppercase
363 // otherwise price data won't be found
364 item = item.charAt( 0 ).toUpperCase() + item.slice( 1 );
365
366 var mwApiResult, excg, main, excgData;
367
368 ( new mw.Api() )
369 .get( {
370 action: 'query',
371 prop: 'revisions',
372 titles: item + '|Module:Exchange/' + item,
373 rvprop: 'content',
374 redirects: ''
375 } )
376 .then( function (data) {
377 mwApiResult = data;
378
379 for ( var x in mwApiResult.query.pages ) {
380 if ( mwApiResult.query.pages.hasOwnProperty( x ) ) {
381 if ( x < 0 ) {
382 // the page does not exist
383 mw.log( mwApiResult.query.pages[x] );
384 continue;
385 } else if ( mwApiResult.query.pages[x].ns === 828 ) {
386 excg = mwApiResult.query.pages[x];
387 } else if ( mwApiResult.query.pages[x].ns === 0 ) {
388 main = mwApiResult.query.pages[x];
389 }
390 }
391 }
392
393 if ( excg ) {
394 excgData = rs.parseExchangeModule( excg.revisions[0]['*'] );
395 excgData.itemId = excgData.itemId || excgData.itemid; // make this more robust?
396
397 $.getJSON("https://api.weirdgloop.org/exchange/history/osrs/latest?id=" + excgData.itemId)
398 .done( function (res) {
399 self.done(main, res[excgData.itemId]);
400 } )
401 .fail( self.fail );
402 } else {
403 self.done(main, {});
404 }
405 } )
406 .fail( self.fail );
407
408 return false;
409 },
410
411 /**
412 * Success callback for `jQuery.ajax` promise
413 */
414 done: function ( main, apiRes ) {
415 var bonuses = [
416 'astab',
417 'aslash',
418 'acrush',
419 'amagic',
420 'arange',
421 'dstab',
422 'dslash',
423 'dcrush',
424 'dmagic',
425 'drange',
426 'str',
427 'rstr',
428 'mdmg',
429 'prayer',
430 'speed'
431 ],
432 main,
433 x,
434 title,
435 content,
436 bonusData,
437 itemData,
438 $tr;
439
440 mw.log( main, apiRes );
441
442 if ( !main ) {
443 self.showError( 'Could not find that item.' );
444 return;
445 }
446
447 title = main.title;
448 content = main.revisions[0]['*'];
449 bonusData = rs.parseTemplate( 'infobox bonuses', content );
450 itemData = rs.parseTemplate( 'infobox item', content );
451
452 if ( $.isEmptyObject( bonusData ) ) {
453 self.showError( 'No bonus data found for the item.' );
454 return;
455 }
456
457 $tr = $( '<tr>' )
458 .append(
459 $( '<th>' )
460 .append(
461 $( '<a>' )
462 .attr( {
463 href: '#',
464 title: 'Remove this row'
465 } )
466 .on( 'click', function () {
467 $( this ).closest( 'tr' ).fadeOut( 'slow', function () {
468 $( this ).remove();
469 self.calcTotals();
470 } );
471
472 return false;
473 } )
474 .append( self.img.del() ),
475 ' ',
476 $( '<a>' )
477 .attr( {
478 href: mw.util.getUrl( title ),
479 title: title
480 } )
481 .text( title )
482 )
483 );
484
485 bonuses.forEach( function ( el ) {
486 // Use default version if defined, otherwise check if bonus has a version1
487 var defaultVersion = $.isEmptyObject( itemData ) || (itemData.defver === undefined) ? '1' : itemData.defver;
488 var versionSpecificBonus = bonusData[el + defaultVersion];
489 $tr.append( self.format( versionSpecificBonus === undefined ? bonusData[el] : versionSpecificBonus ) );
490 } );
491
492 $tr.append( self.format( !$.isEmptyObject( itemData ) ? itemData.weight : null ) );
493 $tr.append( self.format( !$.isEmptyObject( apiRes ) ? rs.addCommas( apiRes.price ) : null ) );
494
495 $( '#cioTotals' ).before( $tr );
496
497 self.calcTotals();
498 $( '#cioStatus' ).empty();
499 $( '#cioItem > input' ).val( '' );
500
501 window.OOUIWindowManager.getCurrentWindow().updateSize();
502 },
503
504 /**
505 * Error callback for `jQuery.ajax` promise
506 */
507 fail: function ( _, error ) {
508 self.showError( 'Error: ' + error );
509 },
510
511 /**
512 * Outputs error to the UI
513 *
514 * @param str {string} Error to display
515 */
516 showError: function ( str ) {
517 $( '#cioStatus' )
518 .empty()
519 .attr( 'class', 'cioError' )
520 .append(
521 self.img.error(),
522 ' ' + str
523 );
524 },
525
526 /**
527 * Formats each attribute's value and inserts it into a td cell
528 *
529 * @param str {string} Attribute value to format
530 *
531 * @return {jquery object} td cell to insert into the associated item's row
532 */
533 format: function ( str ) {
534 var $td = $( '<td>' ),
535 first;
536
537 // set `null` or `undefined` to an empty string
538 /*jshint eqnull:true */
539 if ( str == null ) {
540 /* jshint eqnull:false */
541 str = '';
542 }
543
544 // remove comments
545 str = str.replace( /no|<!--.*?-->/gi, '' ).trim();
546
547 // cache first character of `str`
548 first = str.substring( 0, 1 );
549
550 if ( !str ) {
551 $td
552 .addClass( 'cioEmpty' )
553 .text( '--' );
554 } else if ( /\d/.test( first ) ) {
555 $td
556 .addClass( 'cioPos' )
557 .text( '+' + str );
558 } else if ( first === '-' ) {
559 $td
560 .addClass( 'cioNeg' )
561 .text( str );
562 } else {
563 $td
564 .text( str );
565 }
566
567 return $td;
568 },
569
570 formatTotals: function (str) {
571 var $td = $( '<td>' ),
572 first;
573
574 // set `null` or `undefined` to an empty string
575 /*jshint eqnull:true */
576 if ( str == null ) {
577 /* jshint eqnull:false */
578 str = '';
579 }
580
581 // remove comments
582 str = str.replace( /no|<!--.*?-->/gi, '' ).trim();
583
584 // cache first character of `str`
585 first = str.substring( 0, 1 );
586
587 if ( !str ) {
588 $td
589 .addClass( 'cioEmpty' )
590 .text( '--' );
591 } else if ( /\d/.test( first ) ) {
592 $td
593 .addClass( 'table-bg-green' )
594 .text( '+' + str );
595 } else if ( first === '-' ) {
596 $td
597 .addClass( 'table-bg-red' )
598 .text( str );
599 } else {
600 $td
601 .addClass('table-bg-green')
602 .text( str );
603 }
604
605 return $td;
606 },
607
608 /**
609 * Calculate bonus totals
610 */
611 calcTotals: function () {
612 // 19 0's, one for each attribute
613 var totals = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
614 $totals = $( '#cioTotals' );
615
616 $( '#cioItems tbody tr:not( #cioTotals )' ).each( function () {
617 $( this ).children( 'td' ).each( function ( i ) {
618 var num = $( this ).text().replace( /,/g, '' );
619 if (!isNaN(num)) {
620 var totalsSign = self.checkSign(totals[i])
621 var numSign = self.checkSign(num)
622 if (!totalsSign && !numSign) {
623 // If both values are negative
624 totals[i] = totals[i] === num ? 0 : totals[i] > num ? parseFloat(-1 * (num - totals[i]), 10) : parseFloat(totals[i] - num, 10)
625 } else if (!totalsSign || !numSign) {
626 // If one of the values are negative
627 totals[i] = totals[i] === 0 ? num : totalsSign === false ? parseFloat(-1 * (num - totals[i]), 10) : parseFloat(totals[i] - num, 10)
628 } else {
629 // Nothing is negative, calculate it out.
630 totals[i] = totals[i] === 0 ? num : totals[i] === num ? 0 : totals[i] > num ? parseFloat(totals[i] - num, 10) : parseFloat(-1 * (num - totals[i]), 10)
631 }
632 } else {
633 totals[i] += 0
634 }
635 } );
636 } );
637
638 $totals
639 .empty()
640 .append(
641 $( '<th>' )
642 .text( 'Total' )
643 );
644
645 totals.forEach( function ( elem, index ) {
646 $totals.append(
647 self.formatTotals(
648 // don't total speed
649 // 14th index/column respectively
650 // [14].indexOf( index ) > -1 ? null : rs.addCommas( elem )
651 rs.addCommas(elem)
652 )
653 );
654 } );
655 },
656 checkSign: function (value) {
657 return value === 0 ? true : (value > 0 ? true : false);
658 }
659 };
660
661 $(function(){mw.loader.using( ['mediawiki.util', 'mediawiki.api', 'ext.gadget.rsw-util'], self.init )});