Advertisement
gaz_lloyd

Untitled

May 9th, 2015
355
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // ==UserScript==
  2. // @name         gecharttest
  3. // @namespace    http://your.homepage/
  4. // @version      0.1
  5. // @description  enter something useful
  6. // @author       You
  7. // @match        http://runescape.wikia.com/wiki/User:Sum1_0_o/54nd80x?oldid=13527298&debug=1&usesitejs=0
  8. // @match        http://runescape.wikia.com/wiki/Exchange:Drygore_mace?usesitejs=0&debug=1
  9. // @match        http://runescape.wikia.com/wiki/Drygore_mace?usesitejs=0&debug=1
  10. // @require       //ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js
  11. // @grant        none
  12. // ==/UserScript==
  13.  
  14. /* MediaWiki:Common.js/GECharts.js */
  15. /** <nowiki>
  16.  * Grand Exchange Charts
  17.  * Displays price data of item(s) in a chart
  18.  *
  19.  * Highstock docs <http://api.highcharts.com/highstock>
  20.  * Highstock change log <http://www.highcharts.com/documentation/changelog#highstock>
  21.  *
  22.  * @author Joeytje50
  23.  * @author Azliq7
  24.  * @author Cqm
  25.  *
  26.  * @todo use a consistent variable for the chart id
  27.  *       currently it's one of c, i or id
  28.  * @todo remove script URLs (javascript:func) in favour of onclick events
  29.  *       may require attaching the events after the some parts have loaded
  30.  * @todo fix averages
  31.  */
  32.  
  33. /*global jQuery, mediaWiki, rswiki, Highcharts, wgPageName, wgTitle, wgNamespaceNumber */
  34.  
  35. ;( function ( $, mw, rs ) {
  36.  
  37.         /**
  38.          * Cache mw.config variables
  39.          */
  40.     var conf = mw.config.get( [
  41.             'wgNamespaceNumber',
  42.             'wgPageName',
  43.             'wgTitle'
  44.         ] ),
  45.  
  46.         /**
  47.          * <doc>
  48.          *
  49.          * @todo replace `_GEC` wih this
  50.          */
  51.         gec = {},
  52.  
  53.         // @todo document each of these
  54.         _GEC = {
  55.             AIQueue: [],
  56.             AILoaded: [],
  57.             AIData: [],
  58.             addedData: [],
  59.             average: parseInt( ( location.hash.match( /#a=([^#]*)/ ) || [] )[1], 10 ) || ''
  60.         },
  61.  
  62.         /**
  63.          * Startup methods
  64.          */
  65.         self = {
  66.             /**
  67.              * Loads and implements any required dependencies
  68.              */
  69.             deps: function () {
  70.                 if ( !mw.loader.getState( 'rs.highcharts' ) ) {
  71.                     mw.loader.implement(
  72.                         'rs.highcharts',
  73.                         ['http://code.highcharts.com/stock/highstock.js'],
  74.                         {}, {}
  75.                     );
  76.                 }
  77.  
  78.                 mw.loader.using( ['mediawiki.util', 'mediawiki.api', 'rs.highcharts'], self.init );
  79.             },
  80.  
  81.             /**
  82.              * Initial loading function
  83.              */
  84.             init: function () {
  85.                 ( function() {
  86.                     var newhash = location.hash
  87.                         .replace( /\.([0-9a-f]{2})/gi, function( _, first ) {
  88.                             return String.fromCharCode( parseInt( first, 16 ) );
  89.                         } )
  90.                         .replace( / /g, '_' );
  91.                     if ( newhash && newhash.match( /#[aiz]=/ ) ) {
  92.                         location.hash = newhash;
  93.                     }
  94.                 }() );
  95.  
  96.                 $( '.GEdatachart' ).attr( 'id', function ( c ) {
  97.                     return 'GEdatachart' + c;
  98.                 } );
  99.                 $( '.GEdataprices' ).attr( 'id', function ( c ) {
  100.                     return 'GEdataprices' + c;
  101.                 } );
  102.                 $( '.GEChartBox' ).each( function ( c ) {
  103.                     $( this ).find( '.GEChartItems' ).attr( 'id', 'GEChartItems' + c );
  104.                 } );
  105.  
  106.                 Highcharts.setOptions( {
  107.                     lang: {
  108.                         // @todo can this be done with CSS?
  109.                         resetZoom: null,
  110.                         numericSymbols: ['K', 'M', 'B', 'T', 'Qd', 'Qt'],
  111.                     }
  112.                 } );
  113.  
  114.                 // globals to maintain javascript hrefs
  115.                 window._GEC = _GEC;
  116.                 window.popupChart = popupChart;
  117.                 window.addItem = chart.addItem;
  118.                 window.removeGraphItem = chart.removeItem;
  119.  
  120.                 self.buildPopup();
  121.                 self.setupCharts();
  122.             },
  123.  
  124.             /**
  125.              * <doc>
  126.              */
  127.             buildPopup: function () {
  128.                 $( 'body' ).append(
  129.                     $( '<div>' )
  130.                         .attr( 'id', 'GEchartpopup' )
  131.                         .css( 'display', 'none' )
  132.                         .append(
  133.                             $( '<div>' )
  134.                                 .attr( 'id', 'closepopup' )
  135.                                 .append(
  136.                                     $( '<a>' )
  137.                                         // @todo kill script url
  138.                                         .attr( 'href', 'javascript:popupChart(false)' )
  139.                                         // @todo find something better than this
  140.                                         //       CSS?
  141.                                         .text( 'Close popup [X]' )
  142.                                 ),
  143.                             $( '<div>' )
  144.                                 .attr( 'id', 'itemstats' ),
  145.                             $( '<form>' )
  146.                                 .attr( {
  147.                                     // @todo kill script url
  148.                                     action: 'javascript:addItem("popup")',
  149.                                     id: 'chartPropertiespopup'
  150.                                 } )
  151.                                 .append(
  152.                                     $( '<label>' )
  153.                                         .append(
  154.                                             'Average: ',
  155.                                             $( '<input>' )
  156.                                                 .attr( {
  157.                                                     type: 'number',
  158.                                                     min: '1',
  159.                                                     id: 'averagepopup'
  160.                                                 } ),
  161.                                             ' days'
  162.                                         ),
  163.                                     $( '<div>' )
  164.                                         .addClass( 'moreitems' )
  165.                                         .append(
  166.                                             'Add more item(s): ',
  167.                                             $( '<input>' )
  168.                                                 .attr( {
  169.                                                     type: 'text',
  170.                                                     id: 'extraItempopup'
  171.                                                 } ),
  172.                                             '&nbsp;',
  173.                                             $( '<b>' )
  174.                                                 .text( 'Note:' ),
  175.                                             ' If you add more items, the averages will not be shown.'
  176.                                         ),
  177.                                     $( '<button>' )
  178.                                         .text( 'Submit' ),
  179.                                     $( '<button>' )
  180.                                         .attr( 'type', 'reset' )
  181.                                         .append(
  182.                                             'Reset',
  183.                                             $( '<span>' )
  184.                                                 .addClass( 'resetallfields' )
  185.                                                 .text( ' all fields' )
  186.                                         )
  187.                                 ),
  188.                             $( '<a>' )
  189.                                 .addClass( 'GEPermLink' )
  190.                                 .attr( {
  191.                                     id: 'GEPermLinkpopup',
  192.                                     title: 'Permanent link to the current chart settings and items. Right click to copy the url.',
  193.                                     // @todo kill script url
  194.                                     href: 'javascript:void(0)'
  195.                                 } )
  196.                                 .text( 'Permanent link' ),
  197.                             $( '<div>' )
  198.                                 .attr( 'id', 'addedItemspopup' ),
  199.                             $( '<div>' )
  200.                                 .attr( 'id', 'GEpopupchart' )
  201.                     )
  202.                 );
  203.             },
  204.  
  205.             /**
  206.              * <doc>
  207.              */
  208.             setupCharts: function () {
  209.  
  210.                 $( 'div.GEdatachart' ).each( function ( c ) {
  211.  
  212.                     var $dataPrices = $( '#GEdataprices' + c ),
  213.                         $dataChart = $( '#GEdatachart' + c ),
  214.                         dataItem = $dataPrices.attr( 'data-item' ),
  215.                         isSmall = $dataChart.hasClass( 'smallChart' ),
  216.                         isIndexChart = /index/i.test( dataItem ),
  217.                         isInfobox = $dataPrices.is( '.infobox *, .infobar *' ),
  218.                         itemName = dataItem || wgTitle.split( '/' )[0],
  219.                         dataList = getData( c, isSmall ),
  220.                         yAxis = dataList[1],
  221.                         zoom;
  222.  
  223.                     if ( !$dataPrices.length ) {
  224.                         return;
  225.                     }
  226.  
  227.                     dataList = getData( c, isSmall );
  228.                     yAxis = dataList[1];
  229.                     // @todo rename this to xAxis
  230.                     dataList = dataList[0];
  231.  
  232.                     // setting up the form and chart elements
  233.  
  234.                     if ( !isSmall ) {
  235.                         $dataChart.before(
  236.                             '<form action="javascript:addItem('+c+')" id="chartProperties'+c+'"><label>Average: <input type="number" min="1" id="average'+c+'" value="'+_GEC.average+'"> days</label>'+(isIndexChart?'':'<br/>Add more item(s): <input type="text" id="extraItem'+c+'">&nbsp;<b>Note:</b> If you add more items, the averages will not be shown.')+'<br/><button>Submit</button><input type="reset" value="Reset'+(isIndexChart?'':' all fields')+'"/></form>'+(wgNamespaceNumber == 112 && wgTitle.split('/')[1] == 'Data'||wgPageName == 'Grand_Exchange_Market_Watch/Chart' ? '<a class="GEPermLink" id="GEPermLink'+c+'" title="Permanent link to the current chart settings and items. Right click to copy the url." href="javascript:void(0)">Permanent link</a>':'')+'<div id="addedItems'+c+'"></div>'
  237.                         );
  238.                     }
  239.  
  240.                     if ( itemName.toLowerCase() !== 'blank' ) {
  241.                         zoom = parseInt( ( location.hash.match( /#z=([^#]*)/ ) || [] )[1] );
  242.                         zoom = zoom && zoom <= 6 && zoom >= 0 ?
  243.                             zoom - 1 :
  244.                                 ( zoom === 0 ?
  245.                                     0 :
  246.                                         2 );
  247.                     }
  248.  
  249.                     var enlarge = '<a href="javascript:popupChart(' + c + ');" style="text-decoration:underline;color:inherit;font-size:inherit;">Enlarge chart</a>';
  250.                     // @todo this doesn't do anything on small charts
  251.                     //       is it supposed to?
  252.                     var zoomOut = '<a href="javascript:_GEC.chart' + c + '.zoomOut();" style="text-decoration:underline;color:inherit;font-size:inherit;">Zoom out</a>';
  253.  
  254.                     //generating the chart
  255.                     _GEC['chart' + c] = new Highcharts.StockChart( {
  256.                         chart: {
  257.                             renderTo: 'GEdatachart' + c,
  258.                             backgroundColor: 'white',
  259.                             plotBackgroundColor: 'white',
  260.                             zoomType: isSmall ? 'x' : '',
  261.                             //height: isSmall?210:null,
  262.                             events: {
  263.                                 redraw: function() {
  264.                                     _GEC.thisid = this.renderTo.id.replace('GEdatachart','').replace('GEpopupchart','popup');
  265.                                     setTimeout(function() {setChartExtremes(_GEC.thisid);},0);
  266.                                 }
  267.                             },
  268.                         },
  269.                         legend: {
  270.                             enabled: !isSmall,
  271.                             backgroundColor: 'white',
  272.                             align: 'right',
  273.                             layout: 'vertical',
  274.                             verticalAlign: 'top',
  275.                             y: 85
  276.                         },
  277.                         title: {
  278.                             text: isSmall?(isInfobox?enlarge:itemName):'Grand Exchange Market Watch',
  279.                             useHTML: true,
  280.                             style: {
  281.                                 color: 'black',
  282.                                 fontSize: isSmall?(enlarge?'13px':'15px'):'18px',
  283.                             },
  284.                         },
  285.                         subtitle: {
  286.                             text: isSmall?(isInfobox?zoomOut:enlarge+' | '+zoomOut):(itemName.toLowerCase()=='blank'?'Historical chart':itemName),
  287.                             useHTML: true,
  288.                             y: 35,
  289.                             style: {
  290.                                 color: '#666',
  291.                                 fontSize: isSmall?'13px':'15px',
  292.                             },
  293.                         },
  294.                         rangeSelector: {
  295.                             enabled: !isSmall,
  296.                             selected: zoom,
  297.                             inputBoxStyle: {
  298.                                 right: '15px',
  299.                                 display: isSmall?'none':'block'
  300.                             },
  301.                             inputStyle: {
  302.                                 width: '100px',
  303.                             },
  304.                             inputDateFormat: "%e-%b-%Y",
  305.                             buttonTheme: {
  306.                                 class: 'zoomButton',
  307.                             },
  308.                             buttons: [{
  309.                                 type: 'month',
  310.                                 count: 1,
  311.                                 text: '1m'
  312.                             }, {
  313.                                 type: 'month',
  314.                                 count: 2,
  315.                                 text: '2m'
  316.                             }, {
  317.                                 type: 'month',
  318.                                 count: 3,
  319.                                 text: '3m'
  320.                             }, {
  321.                                 type: 'month',
  322.                                 count: 6,
  323.                                 text: '6m'
  324.                             }, {
  325.                                 type: 'year',
  326.                                 count: 1,
  327.                                 text: '1y'
  328.                             }, {
  329.                                 type: 'all',
  330.                                 text: 'All'
  331.                             }]
  332.                         },
  333.                         plotOptions: {
  334.                             series: {
  335.                                 enableMouseTracking: !isSmall,
  336.                                 dataGrouping: {
  337.                                     dateTimeLabelFormats: {
  338.                                         day: ['%A, %e %B %Y', '%A, %e %B', '-%A, %e %B %Y'],
  339.                                         week: ['Week from %A, %e %B %Y', '%A, %e %B', '-%A, %e %B %Y'],
  340.                                         month: ['%B %Y', '%B', '-%B %Y'],
  341.                                         year: ['%Y', '%Y', '-%Y']
  342.                                     }
  343.                                 }
  344.                             }
  345.                         },
  346.                         tooltip: {
  347.                             enabled: !isSmall,
  348.                             valueDecimals: isIndexChart?2:0,
  349.                             headerFormat: '<span style="font-size: 12px">{point.key}</span><br/>',
  350.                             xDateFormat: "%A, %e %B %Y",
  351.                         },
  352.                         navigator: {
  353.                             xAxis: {
  354.                                 dateTimeLabelFormats: {
  355.                                     day: "%e-%b",
  356.                                     week: "%e-%b",
  357.                                     month: "%b-%Y",
  358.                                     year: "%Y",
  359.                                 },
  360.                                 minTickInterval: 24 * 3600 * 1000, //1 day
  361.                             },
  362.                             maskFill: 'none',
  363.                             enabled: !isSmall
  364.                         },
  365.                         credits: {
  366.                             enabled: false,
  367.                         },
  368.                         xAxis: [{
  369.                             lineColor: '#666',
  370.                             tickColor: '#666',
  371.                             dateTimeLabelFormats: {
  372.                                 day: "%e-%b",
  373.                                 week: "%e-%b",
  374.                                 month: "%b-%Y",
  375.                                 year: "%Y",
  376.                             },
  377.                             minTickInterval: 24 * 3600 * 1000, //1 day
  378.                         }],
  379.                         yAxis: yAxis,
  380.                         series: dataList,
  381.                         colors: window.GEMWChartColors||['#4572A7','#AA4643','#89A54E','#80699B','#3D96AE','#DB843D','#92A8CD','#A47D7C','#B5CA92']
  382.                     } );
  383.  
  384.                     var items = ($('#GEChartItems'+c).html()||'').split(',');
  385.                     var noAdd = [];
  386.                     var i;
  387.  
  388.                     for (i=0;i<items.length;i++) {
  389.                         items[i] = items[i].trim();
  390.  
  391.                         if (items[i]) { addItem(c, items[i]); console.log("found extra item in "+c+": "+items[i]);}
  392.                         else { noAdd.push(1); }
  393.                     }
  394.                     if (items.length==noAdd.length&&_GEC['chart'+c].series[0].name.toLowerCase()!='blank') setChartRange(c);
  395.  
  396.                     //adjusting the axes extremes (initial load)
  397.                     setChartExtremes(c);
  398.  
  399.                     //loading the chart and additional price info when the page is ready
  400.                     if ((wgNamespaceNumber == 112 && wgTitle.split('/')[1] == 'Data'||wgPageName == 'Grand_Exchange_Market_Watch/Chart') && location.hash.match('#i=')) {
  401.                         var hash = location.hash;
  402.                         items = decodeURIComponent((hash.match(/#i=([^#]*)/)||[])[1]||'').replace(/_/g,' ').split(',');
  403.                         for (i=0;i<items.length;i++) {
  404.                             if (!items[i].match(/^\s*$/)) addItem(0,items[i]);
  405.                         }
  406.                     }
  407.  
  408.                 } );
  409.  
  410.             }
  411.         },
  412.  
  413.         /**
  414.          * General helper methods
  415.          */
  416.         util = {
  417.             /**
  418.              * <doc>
  419.              *
  420.              * @todo replace with $.extend
  421.              *
  422.              * @param a {object}
  423.              * @param b {object} (optional)
  424.              *
  425.              * @return {object}
  426.              */
  427.             cloneObj: function ( a, b ) {
  428.                 if ( typeof a !== 'object' ) {
  429.                     return '';
  430.                 }
  431.  
  432.                 if ( typeof b !== 'object' ) {
  433.                     b = {};
  434.                 }
  435.  
  436.                 for ( var key in a ) {
  437.                     if ( a.hasOwnProperty( key ) ) {
  438.                         b[key] = a[key];
  439.                     }
  440.                 }
  441.  
  442.                 return b;
  443.             },
  444.  
  445.             /**
  446.              * Averages prices across a specified time interval
  447.              *
  448.              * @param arr {array} Array of arrays, where each member of `arr`
  449.              *                    is in the format [time, price]
  450.              *                    Which is how we store the price data
  451.              *                    @example [x-coord, y-coord]
  452.              * @param amt {number} Interval to average across in days
  453.              * @param round {number} (optional) Number of decimal places to round to
  454.              *                       Defaults to 0
  455.              *
  456.              * @return {array} Array of arrays, where each member of the return array
  457.              *                 is in the format [time, price] (as above)
  458.              *                 and
  459.              */
  460.             avg: function ( arr, amt, round ) {
  461.                 amt = amt || arr.length;
  462.                 // convert `round` into a number we can use for rounding
  463.                 round = Math.pow( 10, round || 0 );
  464.  
  465.                 var avgs = [],
  466.                     list = [],
  467.                     i;
  468.  
  469.                 // adds each price to `list`
  470.                 // when `amt` is reached, average the contents of `list`
  471.                 //
  472.                 // each iteration after `amt` is reached averages the contents of `list`
  473.                 // which is continuously being updated as each iteration
  474.                 // after `amt` is reached replaces a member of `list`
  475.                 // @example when `i` is 31 the current price replaces `list[1]`
  476.                 //          when `i` is 35 the current price replaces `list[5]`
  477.                 for ( i = 0; i < arr.length; i++ ) {
  478.                     list[i % amt] = arr[i][1];
  479.  
  480.                     if ( i >= amt ) {
  481.                         avgs.push( [
  482.                             // don't modify the time (y-coord)
  483.                             arr[i][0],
  484.                             Math.round( ( util.sum( list ) / list.length ) * round ) / round
  485.                         ] );
  486.                     }
  487.                 }
  488.  
  489.                 return avgs;
  490.             },
  491.  
  492.             /**
  493.              * Finds the sum of numbers in an array
  494.              * Only called by `util.avg`
  495.              *
  496.              * @param arr {array} Array of number to find the sum of
  497.              *
  498.              * @return {number} Sum of the numbers in `arr`
  499.              */
  500.             sum: function ( arr ) {
  501.                 var total = 0,
  502.                     i;
  503.  
  504.                 for ( i = 0; i < arr.length; i++ ) {
  505.                     total += parseFloat( arr[i], 10 );
  506.                 }
  507.  
  508.                 return total;
  509.             },
  510.  
  511.             /**
  512.              * Rounds and formats numbers
  513.              *
  514.              * @example 12345        -> 12.3K
  515.              * @example 1234567      -> 1.2M
  516.              * @example 123456789012 -> 123.4M
  517.              *
  518.              * @param num {number|string} Number to format
  519.              *
  520.              * @return {string} Formatted number
  521.              */
  522.             toKMB: function ( num ) {
  523.                 // strip commas from number string
  524.                 // as `parseInt` will interpret them as a decimal separator
  525.                 // pass numbers and string to `parseInt` to convert floats too
  526.                 num = parseInt( ( typeof num === 'string' ? num.replace( /,/g, '' ) : num ), 10 );
  527.                 var neg = num < 0 ? '-' : '';
  528.                
  529.                 num = Math.abs( num );
  530.  
  531.                 // `1eX` is shorthand for `Math.pow( 10, X )`
  532.                 if ( num >= 1e10 ) {
  533.                     num = Math.round( num / 1e8 ) / 10;
  534.                     num += 'B';
  535.                 } else if ( num >= 1e7 ) {
  536.                     num = Math.round( num / 1e5 ) / 10;
  537.                     num += 'M';
  538.                 } else if ( num >= 1e4 ) {
  539.                     num = Math.round( num / 100 ) / 10;
  540.                     num += 'K';
  541.                 }
  542.  
  543.                 return rs.addCommas( neg + num );
  544.             },
  545.  
  546.             /**
  547.              * Capitalises first character of a string
  548.              *
  549.              * @source <http://stackoverflow.com/a/1026087>
  550.              *
  551.              * @param str {string}
  552.              *
  553.              * @return {string}
  554.              */
  555.             ucFirst: function ( str ) {
  556.                 return str.charAt( 0 ).toUpperCase() + str.slice( 1 );
  557.             }
  558.         },
  559.  
  560.         /**
  561.          * Chart methods
  562.          */
  563.         chart = {
  564.             /**
  565.              * <doc>
  566.              *
  567.              * @param id {string|number}
  568.              * @param match {string} is normally the 'line' that isn't an item's price data
  569.              *                       such as average or volume
  570.              *
  571.              * @return {number}
  572.              */
  573.             getSeriesIndex: function ( id, match ) {
  574.                 var chart = _GEC['chart' + id],
  575.                     series = chart.series,
  576.                     i;
  577.  
  578.                 if ( chart ) {
  579.                     for ( i = 0; i < series.length; i++ ) {
  580.                         if (series[i].name.match(match)) {
  581.                             return i;
  582.                         }
  583.                     }
  584.  
  585.                     return -1;
  586.                 }
  587.  
  588.                 // @todo what happens if !chart
  589.             },
  590.  
  591.             /**
  592.              * Creates a URL with preset options
  593.              *
  594.              * @todo change to url params
  595.              * @todo document the individual params/options
  596.              *
  597.              * @param id {number|string}
  598.              *
  599.              * @return {string}
  600.              */
  601.             permLinkUrl: function ( id ) {
  602.                 var chart = _GEC['chart' + id],
  603.                     xt = chart.xAxis[0].getExtremes(),
  604.                     series = chart.series,
  605.                     minDate = ( new Date( xt.min ) )
  606.                         .toDateString()
  607.                         .split( ' ' )
  608.                         .slice( 1 )
  609.                         .join( '_' ),
  610.                     maxDate = ( new Date( xt.max ) )
  611.                         .toDateString()
  612.                         .split( ' ' )
  613.                         .slice( 1 )
  614.                         .join( '_' ),
  615.                     inputAvg = parseInt( $( '#average' + id ).val(), 10 ),
  616.                     urlHash = '#t=' + minDate + ',' + maxDate,
  617.                     items = '',
  618.                     i;
  619.  
  620.                 if ( !isNaN( inputAvg ) ) {
  621.                     urlHash += '#a=' + inputAvg;
  622.                 }
  623.  
  624.                 for ( i = 0; i < series.length; i++ ) {
  625.                     if ( series[i].name == 'Navigator' || series[i].name.match( 'average' ) ) {
  626.                         continue;
  627.                     }
  628.  
  629.                     // separate items with commas
  630.                     if ( items ) {
  631.                         items += ',';
  632.                     }
  633.  
  634.                     // @todo url encode this?
  635.                     items += series[i].name.replace( / /g, '_' );
  636.                 }
  637.  
  638.                 urlHash += '#i=' + items;
  639.  
  640.                 // @todo hide the redirect h2
  641.                 return 'http://rs.wikia.com/wiki/GEMW/C' + urlHash;
  642.             },
  643.  
  644.             /**
  645.              * Add a new item to the chart
  646.              *
  647.              * @param i
  648.              * @param it {string} (optional)
  649.              */
  650.             addItem: function ( i, it ) {
  651.                 _GEC.chartid = i;
  652.                 console.log("adding item to "+i+": "+it);
  653.                 var $extraItem = $( '#extraItem' + i ),
  654.                     item = ( it || '' ).trim() || $extraItem.val(),
  655.                     dataItems = [
  656.                         '#addedItems' + i + ' [data-item]',
  657.                         '#GEdataprices' + i + '[data-item]'
  658.                     ],
  659.                     $dataItems = $( dataItems.join( ',' ) ).map( function () {
  660.                         return $( this ).attr( 'data-item' ).toLowerCase();
  661.                     } ),
  662.                     $addedItems = $( '#addedItems' + i ),
  663.                     id,
  664.                     data,
  665.                     series,
  666.                     seriesIndex,
  667.                     gecchartid = i;
  668.  
  669.                 if ( item && item.length ) {
  670.  
  671.                     if (
  672.                         // @todo should a number passed to .get()
  673.                         $dataItems.get().indexOf( item.toLowerCase() ) !== -1 ||
  674.                         _GEC.AIQueue.indexOf( item.toLowerCase() ) !== -1
  675.                     ) {
  676.                         if (!it) {
  677.                             alert( item + ' is already in the graph.' );
  678.                         }
  679.  
  680.                         $extraItem.val( '' );
  681.  
  682.                         return false;
  683.                     }
  684.  
  685.                     $extraItem.prop( 'disabled', true );
  686.  
  687.                     $.get(
  688.                         '/api.php',
  689.                         {
  690.                             action: 'query',
  691.                             prop: 'revisions',
  692.                             rvprop: 'content',
  693.                             format: 'json',
  694.                             titles: 'Module:Exchange/' + util.ucFirst( item ) + '/Data'
  695.                         },
  696.                         // @todo can this be split out into a separate method?
  697.                         function ( resp ) {
  698.                             console.log("got response for "+it+", addding to "+gecchartid);
  699.                             var $extraItem  = $( '#extraItem' + gecchartid ),
  700.                                 item = mw.util.getParamValue( 'titles', this.url )
  701.                                     .replace( 'Exchange:', '' )
  702.                                     .replace( 'Module:Exchange/', '' )
  703.                                     .replace( '/Data', '' ),
  704.                                 prices,
  705.                                 data = [],
  706.                                 pages = resp.query.pages;
  707.  
  708.                             // action=query doesn't throw many errors
  709.                             // all of which are down to the supplied params (stuff we do here)
  710.                             if ( resp.error ) {
  711.                                 alert( 'An error occured while loading ' + item );
  712.                                 console.log( resp );
  713.                             }
  714.  
  715.                             // page not found
  716.                             if ( pages[-1] ) {
  717.                                 if ( $( '#extraItem' + gecchartid ).val().length ) {
  718.                                     alert( 'The item ' + item + ' doesn\'t exist on our Grand Exchange database.' );
  719.                                     $extraItem.prop( 'disabled', false ).val( '' );
  720.                                     return false;
  721.                                 }
  722.  
  723.                                 _GEC.AILoaded.push( false );
  724.  
  725.                                 if (
  726.                                     _GEC.AIData.length &&
  727.                                     _GEC.AIQueue.length == _GEC.AILoaded.length
  728.                                 ) {
  729.                                     loadChartsQueueComplete( gecchartid );
  730.                                 } else if ( !_GEC.AIData.length ) {
  731.                                     setChartRange( gecchartid );
  732.                                 }
  733.  
  734.                                 $extraItem.prop( 'disabled', false ).val( '' );
  735.  
  736.                                 return false;
  737.                             }
  738.  
  739.                             // @todo what is this for
  740.                             //       should it be resp.parse.links?
  741.                             /*
  742.                             if (resp.links) {
  743.                                 $.get('/api.php?action=parse&format=json&page='+encodeURIComponent(resp.links[0]['*']), this.success);
  744.                                 return false;
  745.                             }
  746.                             */
  747.  
  748.                             _GEC.AILoaded.push( item );
  749.  
  750.                             prices = resp.query.pages[Object.keys( resp.query.pages )[0]]
  751.                                 .revisions[0]['*'];
  752.  
  753.                             prices = prices
  754.                                 .replace( /return\s*\{/, '' )
  755.                                 .replace( /\}/, '' )
  756.                                 .split( /,/ );
  757.  
  758.                             data = [];
  759.  
  760.                             prices.forEach( function ( elem ) {
  761.                                 // point as in data point
  762.                                 // time:price:volume
  763.                                 var point = elem
  764.                                         .trim()
  765.                                         .replace( /'/g, '' );
  766.  
  767.                                 if ( point ) {
  768.                                     point = point.split( ':' );
  769.                                     data.push( [
  770.                                         // time
  771.                                         parseInt( point[0], 10 ) * 1000,
  772.                                         // price
  773.                                         parseInt( point[1], 10 )
  774.                                     ] );
  775.                                 }
  776.                             } );
  777.  
  778.                             _GEC.AIData.push( {
  779.                                 name: item,
  780.                                 data: data,
  781.                                 id: item,
  782.                                 gecchartid: gecchartid,
  783.                                 lineWidth: 2
  784.                             } );
  785.  
  786.                             if ( getSeriesIndex( gecchartid, 'average' ) !== -1 ) {
  787.                                 _GEC['chart' + gecchartid]
  788.                                     .series[getSeriesIndex( gecchartid, 'average' )]
  789.                                     .remove();
  790.                             }
  791.  
  792.                             if (_GEC.AIQueue.length == _GEC.AILoaded.length) {
  793.                                 // This is always true when only 1 item is being loaded.
  794.                                 loadChartsQueueComplete( i );
  795.                             }
  796.                         }
  797.                     );
  798.  
  799.                     _GEC.AIQueue.push( item.toLowerCase() );
  800.  
  801.                 // @todo when does this happen
  802.                     /* This happens when there are no further items added to the charts, i.e. when the original item is the only one.
  803.                        This is indeed a flawed test, since it won't work on GEMW/C, where there is no original item in the chart.
  804.                        This should be replaced with another test that also works on GEMW/C.
  805.                      */
  806.                 } else if (
  807.                     $addedItems.html().match( /^\s*$/ ) ||
  808.                     (
  809.                         conf.wgPageName == 'Grand_Exchange_Market_Watch/Chart' &&
  810.                         $addedItems.find( 'a' ).length === 1
  811.                     )
  812.                 ) {
  813.                     id = (i === 'popup' ? $( '#GEchartpopup' ).attr( 'data-chartid' ) : i);
  814.                     console.log("getting data for id "+id+"  i "+i);
  815.                     data = getData( id, false, i );
  816.                     series = _GEC['chart' + i].series;
  817.                     seriesIndex = getSeriesIndex( i, 'average' );
  818.  
  819.                     //remove an average line if it already exists
  820.                     if ( seriesIndex !== -1 ) {
  821.                         series[seriesIndex].remove();
  822.                     }
  823.  
  824.                     //add average line when there is only 1 item in the chart
  825.                     _GEC['chart' + i].addSeries( data[0][1] );
  826.                 }
  827.                 console.log("added "+it+" to "+gecchartid);
  828.             },
  829.  
  830.             /**
  831.              * <doc>
  832.              *
  833.              * @param c {number|string}
  834.              */
  835.             loadQueueComplete: function ( cin ) {
  836.                 var cnum = typeof cin === 'number',
  837.                     c = cnum ? _GEC.AIQueue.length : cin,
  838.                     id,
  839.                     chartdata,
  840.                     isSmall = [],
  841.                     data = [_GEC.AIQueue.length],
  842.                     i,
  843.                     index,
  844.                     itemhash,
  845.                     $addedItems,
  846.                     iname,
  847.                     hadBlank;
  848.  
  849.                 if (cnum) {
  850.                     for (i = 0; i < c; i++) {
  851.                         isSmall[i] = $( '#GEdatachart' + i ).hasClass( 'smallChart' );
  852.                     }
  853.                 } else {
  854.                         isSmall[c] = $( '#GEdatachart' + c ).hasClass( 'smallChart' );
  855.                 }
  856.                
  857.                 if (cnum) {
  858.                     for (i = 0; i < c; i++) {
  859.                         if ( getSeriesIndex( i, '7-day volume' ) !== -1 ) {
  860.                             id = i === 'popup' ? $( '#GEchartpopup' ).attr( 'data-chartid' ) : i;
  861.                             chartdata = getData( id, true );
  862.                             chartdata[1].title.text = 'Price history';
  863.  
  864.                             reloadChart( i, {
  865.                                 series: chartdata[0],
  866.                                 yAxis: chartdata[1]
  867.                             } );
  868.                         }
  869.                     }
  870.                 } else {
  871.                     if ( getSeriesIndex( c, '7-day volume' ) !== -1 ) {
  872.                             id = c === 'popup' ? $( '#GEchartpopup' ).attr( 'data-chartid' ) : c;
  873.                             chartdata = getData( id, true );
  874.                             chartdata[1].title.text = 'Price history';
  875.  
  876.                             reloadChart( c, {
  877.                                 series: chartdata[0],
  878.                                 yAxis: chartdata[1]
  879.                             } );
  880.                         }
  881.                 }
  882.  
  883.                 for ( i = 0; i < _GEC.AIData.length; i++ ) {
  884.                     index = _GEC.AIQueue.indexOf( ( _GEC.AIData[i] || {name: ''} ).name.toLowerCase() );
  885.                     data[index !== -1 ? index : data.length] = _GEC.AIData[i];
  886.                 }
  887.  
  888.                 // @todo should this be `Array.isArray`
  889.                 //       or should it default to `{}`
  890.                 // @todo test if isSmall is needed in the conditional
  891.                 if (cnum) {
  892.                     for (i = 0; i < c; i++) {
  893.                         if ( isSmall[i] && typeof _GEC.addedData[data[i].gecchartid] !== 'object' ) {
  894.                             _GEC.addedData[data[i].gecchartid] = [];
  895.                         }
  896.                     }
  897.                 } else {
  898.                     if ( isSmall[c] && typeof _GEC.addedData[data[c].gecchartid] !== 'object' ) {
  899.                         _GEC.addedData[data[c].gecchartid] = [];
  900.                     }
  901.                    
  902.                 }
  903.  
  904.                 for ( i = 0; i < data.length; i++ ) {
  905.                     if ( data[i] ) {
  906.                         _GEC['chart' + data[i].gecchartid].addSeries( data[i] );
  907.                     }
  908.  
  909.                     if ( cnum && isSmall ) {
  910.                         _GEC.addedData[data[i].gecchartid][i] = data[i];
  911.                     }
  912.                 }
  913.  
  914.                 if (cnum) {
  915.                     for (i = 0; i < c; i++) {
  916.                         setChartExtremes( i );
  917.                         $( '#extraItem' + i ).prop( 'disabled', false ).val( '' );
  918.                     }
  919.                 } else {
  920.                     setChartExtremes( c );
  921.                     $( '#extraItem' + c ).prop( 'disabled', false ).val( '' );
  922.                 }
  923.                 $( '#itemstats' ).hide();
  924.                 itemhash = ( location.hash.match( /#i=[^#]*/ ) || [] )[0] || location.hash + '#i=';
  925.                 $addedItems = $( '#addedItems' + c );
  926.  
  927.                 for ( i = 0; i < data.length; i++ ) {
  928.                     if ( !data[i] ) {
  929.                         continue;
  930.                     }
  931.  
  932.                     iname = data[i].name;
  933.  
  934.                     if ( !$addedItems.text().trim() ) {
  935.                         $addedItems.append(
  936.                             'Remove items from graph: ',
  937.                             $( '<a>' )
  938.                                 .attr( {
  939.                                     href: 'javascript:removeGraphItem("' + iname + '","' + c + '")',
  940.                                     'data-item': iname
  941.                                 } )
  942.                                 .text( iname )
  943.                         );
  944.                         itemhash = '#i=' + iname;
  945.                     } else {
  946.                         $addedItems.append(
  947.                             ', ',
  948.                             $( '<a>' )
  949.                                 .attr( {
  950.                                     href: 'javascript:removeGraphItem("' + iname + '","' + c + '")',
  951.                                     'data-item': iname
  952.                                 } )
  953.                                 .text( iname )
  954.                         );
  955.                         itemhash += ',' + iname;
  956.                     }
  957.                 }
  958.  
  959.                 if ( location.hash.match( /#i=/ ) ) {
  960.                     itemhash = location.hash
  961.                         .replace( /#i=[^#]*/, itemhash )
  962.                         .replace( / /g, '_' );
  963.                 } else {
  964.                     itemhash = location.hash + itemhash;
  965.                 }
  966.  
  967.                 if (
  968.                     (
  969.                         wgNamespaceNumber == 112 && wgTitle.split( '/' )[1] == 'Data' ||
  970.                         wgPageName == 'Grand_Exchange_Market_Watch/Chart'
  971.                     ) &&
  972.                     itemhash.replace( '#i=', '' ).length
  973.                 ) {
  974.                     location.hash = itemhash;
  975.                 }
  976.  
  977.                 _GEC.AIQueue = [];
  978.                 _GEC.AILoaded = [];
  979.                 _GEC.AIData = [];
  980.  
  981.                 if (cnum) {
  982.                     for (i = 0; i < c; i++) {
  983.                         hadBlank = removeGraphItem( 'Blank', i );
  984.  
  985.                         if ( hadBlank ) {
  986.                             setChartRange( i );
  987.                         }
  988.                     }
  989.                 } else {
  990.                     hadBlank = removeGraphItem( 'Blank', c );
  991.  
  992.                     if ( hadBlank ) {
  993.                         setChartRange( c );
  994.                     }
  995.                 }
  996.             },
  997.  
  998.             /**
  999.              * <doc>
  1000.              *
  1001.              * @param c {number|string}
  1002.              *
  1003.              * @return {boolean}
  1004.              */
  1005.             setRange: function ( c ) {
  1006.                 var zoom = parseInt( ( location.hash.match( /#z=([^#]*)/ ) || [] )[1], 10 );
  1007.                 zoom = zoom && zoom <= 6 && zoom >= 0 ? zoom - 1 : ( zoom === 0 ? 0 : 2 );
  1008.                 var hash = location.hash;
  1009.                 var hasT = conf.wgNamespaceNumber === 112 && conf.wgTitle.split( '/' )[1] === 'Data' || conf.wgPageName === 'Grand_Exchange_Market_Watch/Chart';
  1010.                
  1011.                 if ( typeof c === 'number' && ( hasT && !hash.match( '#t=' ) || !hasT ) ) {
  1012.                     $( '#GEdatachart' + c + ' .zoomButton' ).eq( zoom ).click();
  1013.                     return true;
  1014.                 }
  1015.  
  1016.                 var timespan = decodeURIComponent( ( hash.match( /#t=([^#]*)/ ) || [] )[1] || '' )
  1017.                     .replace( /_/g, ' ' )
  1018.                     .split( ',' );
  1019.                 var dates = [new Date( timespan[0] ), new Date( timespan[1] )];
  1020.                 var d = new Date( timespan[0] );
  1021.                 var extremes = _GEC['chart' + c].xAxis[0].getExtremes();
  1022.                
  1023.                 if ( dates[0] !== 'Invalid Date' && dates[1] === 'Invalid Date' && typeof zoom === 'number' ) {
  1024.                     var button = _GEC['chart' + c].rangeSelector.buttonOptions[zoom];
  1025.  
  1026.                     if ( button.type === 'month' ) {
  1027.                         d.setMonth( d.getMonth() + button.count );
  1028.                     } else if ( button.type === 'year' ) {
  1029.                         d.setYear( d.getFullYear() + button.count );
  1030.                     } else if ( button.type === 'all' ) {
  1031.                         d = new Date( extremes.dataMax );
  1032.                     }
  1033.  
  1034.                     dates[1] = d;
  1035.                 }
  1036.  
  1037.                 if ( dates[0] !== 'Invalid Date' && dates[1] !== 'Invalid Date') {
  1038.                     _GEC['chart' + c].xAxis[0].setExtremes( dates[0].getTime(), dates[1].getTime() );
  1039.                     return true;
  1040.                 }
  1041.  
  1042.                 return false;
  1043.             },
  1044.  
  1045.             /**
  1046.              * <doc>
  1047.              *
  1048.              * @param c {number|string}
  1049.              * @param change {object}
  1050.              */
  1051.             reload: function ( c, change ) {
  1052.                 var options = _GEC['chart' + c].options;
  1053.  
  1054.                 if ( !options ) {
  1055.                     // @todo do we need to return `false` here
  1056.                     // @todo when does this happen
  1057.                     return false;
  1058.                 }
  1059.  
  1060.                 $.extend( options, change );
  1061.  
  1062.                 _GEC['chart' + c] = new Highcharts.StockChart( options );
  1063.             },
  1064.  
  1065.             /**
  1066.              * <doc>
  1067.              *
  1068.              * @param item {string}
  1069.              * @param c {number|string}
  1070.              *
  1071.              * @return {boolean}
  1072.              */
  1073.             removeItem: function ( item, c ) {
  1074.                 var series = _GEC['chart' + c].series,
  1075.                     id,
  1076.                     i,
  1077.                     newhash,
  1078.                     data;
  1079.  
  1080.                 // find the item we want to remove
  1081.                 for ( i = 0; i < series.length; i++ ) {
  1082.                     if ( series[i].name.match( item ) ) {
  1083.                         id = i;
  1084.                     }
  1085.                 }
  1086.  
  1087.                 // @todo when does this happen
  1088.                 //       when we can't find the item?
  1089.                 if ( typeof id !== 'number' ) {
  1090.                     return false;
  1091.                 }
  1092.  
  1093.                 // remove item from url hash
  1094.                 newhash = location.hash
  1095.                     .replace( /_/g, ' ' )
  1096.                     .replace( new RegExp( '(#i=[^#]*),?' + item, 'i' ), '$1' )
  1097.                     .replace( /,,/g, ',' )
  1098.                     .replace( /,#/g, '#' )
  1099.                     .replace( /#i=,/g, '#i=' )
  1100.                     .replace( /#i=($|#)/, '$1' )
  1101.                     .replace( / /g, '_' );
  1102.  
  1103.                 if ( newhash.replace( '#i=', '' ).length ) {
  1104.                     location.hash = newhash;
  1105.                 } else if ( location.hash.length ) {
  1106.                     location.hash = '';
  1107.                 }
  1108.  
  1109.                 // remove the item from the chart
  1110.                 series[id].remove();
  1111.                 // reset extremes?
  1112.                 setChartExtremes( c );
  1113.  
  1114.                 // @todo can we cache #addedItems somehow
  1115.                 // remove item from list at top of graph
  1116.                 $( '#addedItems' + c + ' [data-item="' + item + '"]' ).remove();
  1117.                 // cleanup list
  1118.                 $( '#addedItems' + c ).html(
  1119.                     $( '#addedItems' + c )
  1120.                         .html()
  1121.                         .replace( /, , /g, ', ' )
  1122.                         .replace( /, $/, '' )
  1123.                         .replace( ': , ', ': ' )
  1124.                 );
  1125.  
  1126.                 // if the list is empty show average, 7-day vol and item stats again
  1127.                 if ( !$('#addedItems' + c + ' [data-item]' ).length ) {
  1128.                     $( '#addedItems' + c ).empty();
  1129.                     id = c == 'popup' ? $( '#GEchartpopup' ).attr( 'data-chartid' ) : c;
  1130.                     data = getData( id, false );
  1131.  
  1132.                     reloadChart( c, {
  1133.                         series: data[0],
  1134.                         yAxis: data[1]
  1135.                     } );
  1136.  
  1137.                     $( '#itemstats' ).show();
  1138.                 }
  1139.  
  1140.                 return true;
  1141.             },
  1142.  
  1143.             /**
  1144.              * <doc>
  1145.              *
  1146.              * @param i {number|string}
  1147.              */
  1148.             popup: function () {},
  1149.  
  1150.             /**
  1151.              * <doc>
  1152.              *
  1153.              * @param i
  1154.              */
  1155.             setExtremes: function ( i ) {
  1156.                 var ch = _GEC['chart' + i],
  1157.                     exts = _GEC['chart' + i].yAxis[0].getExtremes();
  1158.  
  1159.                 if (
  1160.                     exts.dataMin * 0.95 !== exts.userMin ||
  1161.                     exts.dataMax * 1.05 !== exts.userMax
  1162.                 ) {
  1163.                     ch.yAxis[0].setExtremes( exts.dataMin * 0.95, exts.dataMax * 1.05 );
  1164.  
  1165.                     if ( ch.yAxis[2] ) {
  1166.                         exts = ch.yAxis[1].getExtremes();
  1167.                         ch.yAxis[1].setExtremes( 0, exts.dataMax * 1.05 );
  1168.                     }
  1169.                 }
  1170.  
  1171.                 if ( i === 'popup' ) {
  1172.                     // @todo use onclick event
  1173.                     $( '#GEPermLink' + i ).get( 0 ).href = chartPermLinkUrl( i );
  1174.                 }
  1175.             },
  1176.  
  1177.             /**
  1178.              * <doc>
  1179.              *
  1180.              * @param c {number|string}
  1181.              */
  1182.             addPopupPriceInfo: function () {},
  1183.  
  1184.             /**
  1185.              * <doc>
  1186.              *
  1187.              * @param c {number|string}
  1188.              * @param isSmall {boolean}
  1189.              * @param avginput {number|string} (optional)
  1190.              *        number component of input element used for altering the average interval
  1191.              *        when the interval is in days
  1192.              *        when is this different to `c`?
  1193.              *
  1194.              * @return {array} 2 item array containing X and Y respectively
  1195.              *                 @todo expand on what X and Y are
  1196.              */
  1197.             getData: function () {}
  1198.         },
  1199.  
  1200.         // map old functions to new locations until uses are fixed
  1201.         getSeriesIndex = chart.getSeriesIndex,
  1202.         chartPermLinkUrl = chart.permLinkUrl,
  1203.         addItem = chart.addItem,
  1204.         removeGraphItem = chart.removeItem,
  1205.         reloadChart = chart.reload,
  1206.         setChartRange = chart.setRange,
  1207.         setChartExtremes = chart.setExtremes,
  1208.         loadChartsQueueComplete = chart.loadQueueComplete;
  1209.         // popupChart = chart.popup;
  1210.         // addPopupPriceInfo = chart.addPopupPriceInfo;
  1211.         // getData = chart.getData;
  1212.  
  1213.     // chart-related general functions
  1214.  
  1215.     function popupChart( i ) {
  1216.         var $popup = $( '#GEchartpopup' ),
  1217.             $overlay = $( '#overlay' ),
  1218.             options,
  1219.             data,
  1220.             n;
  1221.  
  1222.         if ( !$popup.length ) {
  1223.             return false;
  1224.         }
  1225.  
  1226.         if ( $overlay.length ) {
  1227.             $overlay.toggle();
  1228.         } else {
  1229.             $popup.before(
  1230.                 $( '<div>' )
  1231.                     .attr( 'id', 'overlay' )
  1232.                     .css( 'display', 'block' )
  1233.             );
  1234.             $overlay = $( '#overlay' );
  1235.         }
  1236.  
  1237.         $overlay.on( 'click', function () {
  1238.             popupChart( false );
  1239.         } );
  1240.  
  1241.         if ( typeof i === 'number' ) {
  1242.             $( document ).keydown( function ( e ) {
  1243.                 // Esc
  1244.                 if ( e.which === 27 ) {
  1245.                     popupChart( false );
  1246.                 }
  1247.             } );
  1248.         } else {
  1249.             // @todo only remove our event
  1250.             $( document ).off( 'keydown' );
  1251.         }
  1252.  
  1253.         if (typeof i === 'boolean' && !i) {
  1254.             $popup.hide();
  1255.             $('#addedItemspopup').html('');
  1256.         } else {
  1257.             $popup.toggle();
  1258.         }
  1259.  
  1260.         if ( typeof i === 'number' && $popup.attr( 'data-chartid' ) !== i ) {
  1261.             $( '#averagepopup' ).val( _GEC.average );
  1262.             addPopupPriceInfo( i );
  1263.             $popup.attr( 'data-chartid', i );
  1264.  
  1265.             options = {};
  1266.             data = getData( i, false );
  1267.  
  1268.             // @todo can this be replaced with $.extend?
  1269.             // @todo what is this supposed to do?
  1270.             util.cloneObj( _GEC['chart' + i].options, options );
  1271.  
  1272.             options.chart.renderTo = 'GEpopupchart';
  1273.             options.legend.enabled = true;
  1274.             options.title.text = 'Grand Exchange Market Watch';
  1275.             options.title.style.fontSize = '18px';
  1276.             options.subtitle.text = options.series[0].name;
  1277.             options.subtitle.style.fontSize = '15px;';
  1278.             options.chart.zoomType = '';
  1279.             options.rangeSelector.enabled = true;
  1280.             options.rangeSelector.inputBoxStyle.display = 'block';
  1281.             options.plotOptions.series.enableMouseTracking = true;
  1282.             options.tooltip.enabled = true;
  1283.             options.navigator.enabled = true;
  1284.             options.credits.enabled = false;
  1285.             options.series = [{}];
  1286.             options.series = _GEC.addedData[i] ? [data[0][0]] : data[0];
  1287.             options.yAxis = data[1];
  1288.  
  1289.             _GEC.chartpopup = new Highcharts.StockChart( options );
  1290.  
  1291.             if ( _GEC.addedData[i] ) {
  1292.                 for ( n = 0; n < _GEC.addedData[i].length; n++ ) {
  1293.                     _GEC.chartpopup.addSeries( _GEC.addedData[i][n] );
  1294.                 }
  1295.             }
  1296.  
  1297.             setChartExtremes( 'popup' );
  1298.             _GEC.chartpopup.redraw();
  1299.         }
  1300.  
  1301.         $( '#GEPermLinkpopup' ).first().attr( 'href', chartPermLinkUrl('popup') );
  1302.     }
  1303.  
  1304.     function rg( num ) {
  1305.         var colour = 'red';
  1306.  
  1307.         if ( num > 0 ) {
  1308.             colour = 'green';
  1309.         } else if ( num === 0 ) {
  1310.             colour = 'blue';
  1311.         }
  1312.  
  1313.         return colour;
  1314.     }
  1315.  
  1316. function addPopupPriceInfo( c ) {
  1317.     if (($('#GEdataprices'+c).attr('data-item')||'').match(/index/i)) return false;
  1318.     var prices = $('#GEdataprices'+c).html().split(/,\s/);
  1319.     var data = [];
  1320.     var curprice, i;
  1321.     for (i=0;i<prices.length;i++) {
  1322.         if (prices[i].replace(/\s/g,'').length) {
  1323.             curprice = prices[i].split(':');
  1324.             data.push(parseInt(curprice[1]));
  1325.         }
  1326.     }
  1327.     var datal = data.length;
  1328.     curprice = data[datal-1];
  1329.     var priceDiffs = [], percentDiffs = [];
  1330.     var dayDiffs = [1,7,30,90,180];
  1331.     for (i=0;i<dayDiffs.length;i++) {
  1332.         var pthen = data[datal-dayDiffs[i]-1];
  1333.         var pdiff = curprice - pthen;
  1334.         if (!isNaN(pdiff)) {
  1335.             priceDiffs.push(pdiff);
  1336.             percentDiffs.push(Math.round(pdiff / pthen * 1000)/10);
  1337.         } else {
  1338.             dayDiffs.slice(0,i+1);
  1339.         }
  1340.     }
  1341.  
  1342.     var itemStats = '<table class="wikitable">'+
  1343.     '<caption onclick="$(this).next(\'tbody\').toggle();$(this).children().toggle()"><span style="display:none;white-space:nowrap">Price Info</span><span>Close</span></caption><tbody>'+
  1344.     '<tr><th>Price</th><td>' + util.toKMB( curprice ) + '</td></tr>';
  1345.     for (i=0;i<priceDiffs.length;i++) {
  1346.         itemStats += '<tr><th>'+dayDiffs[i]+'-day</th><td style="color:'+rg(priceDiffs[i])+'">'+(priceDiffs[i]>0?'+':'') + util.toKMB( priceDiffs[i] ) + ' ('+percentDiffs[i]+'%)</td></tr>';
  1347.     }
  1348.     itemStats += $('#GEdataprices'+c).attr('data-value').length?'<tr><th><a href="/wiki/Value" target="_blank" title="Value">Value</a></th><td>'+util.toKMB($('#GEdataprices'+c).attr('data-value'))+'</td></tr>':'';
  1349.     itemStats += $('#GEdataprices'+c).attr('data-limit').length?'<tr><th><a href="/wiki/Grand_Exchange#Trade_Restrictions" target="_blank" title="Grand Exchange buying limit">Buy limit</a></th><td>'+util.toKMB($('#GEdataprices'+c).attr('data-limit'))+'</td></tr>':'';
  1350.     itemStats += '<tr><th colspan="2"><a href="/wiki/Exchange:'+encodeURI($('#GEdataprices'+c).attr('data-item'))+'" target="_blank" title="'+$('#GEdataprices'+c).attr('data-item')+'">More price info</a></th></tr>';
  1351.     itemStats += '</tbody></table>';
  1352.     $('#itemstats').html(itemStats);
  1353. }
  1354.  
  1355.     function getData( cin, isSmall, avginput ) {
  1356.         console.log('got getData inputs: cin: '+cin+' isSmall: '+isSmall+' avginput: '+avginput);
  1357.         var c = cin === 'popup' ? $( '#GEchartpopup' ).attr( 'data-chartid' ) : cin,
  1358.             $dataPrices = $( '#GEdataprices' + c ),
  1359.             dataItem = $dataPrices.attr( 'data-item' ),
  1360.             isIndexChart = /index/i.test( dataItem ),
  1361.             itemName = dataItem || wgTitle.split( '/' )[0],
  1362.             ch = _GEC['chart' + c],
  1363.             chartLoaded = !!( ch && ch.series && ch.series.length ),
  1364.             prices = [],
  1365.             i,
  1366.             data = [],
  1367.             thisprice,
  1368.             volumes = [],
  1369.             dataList,
  1370.             inputAvg,
  1371.             newhash,
  1372.             yAxis,
  1373.             chartPageData;
  1374.            
  1375.  
  1376.         // happens when the first chart isSmall
  1377.         // and the average input id is actually the popup chart
  1378.         // the chart's id is popup, but the input's id is 0
  1379.         avginput = avginput || c;
  1380.  
  1381.         if ( chartLoaded && itemName.toLowerCase() === 'blank' ) {
  1382.             chartPageData = _GEC['chart' + c].series[
  1383.                 getSeriesIndex( c, $( '#addedItems' + c ).find( 'a' ).data( 'item' ) )
  1384.             ];
  1385.            
  1386.             for ( i = 0; i < chartPageData.xData.length; i++ ) {
  1387.                 prices.push( chartPageData.xData[i] + ':' + chartPageData.yData[i] );
  1388.             }
  1389.         } else {
  1390.             // @todo convert this to looking is a data-data or data-itemdata attribute
  1391.             //       which allows us to ditch some CSS hacks in wikiamobile
  1392.             //       store the price data in this attribute instead
  1393.             //       see <http://stackoverflow.com/a/1496150> for practical limitations
  1394.             if ( $( '#GEdataprices' + c ).attr( 'data-data' ) ) {
  1395.                 prices = $( '#GEdataprices' + c ).attr( 'data-data' ).split( '|' );
  1396.             } else {
  1397.                 prices = $( '#GEdataprices' + c ).html().split( /,\s/ );
  1398.             }
  1399.         }
  1400.  
  1401.         for ( i = 0; i < prices.length; i++ ) {
  1402.             if ( prices[i].trim() ) {
  1403.                 thisprice = prices[i].split( ':' );
  1404.  
  1405.                 data.push( [
  1406.                     // time
  1407.                     parseInt( thisprice[0], 10 ) * 1000,
  1408.                     // @todo should this be parseInt?
  1409.                     // price
  1410.                     parseFloat( thisprice[1], 10 )
  1411.                 ] );
  1412.  
  1413.                 if ( thisprice[2] && !isSmall ) {
  1414.                     volumes.push( [
  1415.                         // time
  1416.                         parseInt( thisprice[0], 10 ) * 1000,
  1417.                         // volume
  1418.                         // volumes are in millions
  1419.                         parseFloat( thisprice[2], 10 ) * 1000000
  1420.                     ] );
  1421.                 }
  1422.             }
  1423.         }
  1424.  
  1425.         // datalist's elements are essentially each line on the chart
  1426.         // so price, 30-day-average and volume
  1427.         dataList = [{
  1428.             name: itemName,
  1429.             data: data,
  1430.             lineWidth: isSmall ? 2 : 3
  1431.         }];
  1432.  
  1433.         if ( itemName.toLowerCase() === 'blank' && !chartLoaded ) {
  1434.             dataList[0].color = '#000000';
  1435.         }
  1436.  
  1437.         if ( !isSmall && ( itemName.toLowerCase() !== 'blank' || chartLoaded ) ) {
  1438.             inputAvg = parseInt( $( 'input#average' + avginput ).val(), 10 );
  1439.  
  1440.             // @todo should this be isNaN?
  1441.             if ( inputAvg ) {
  1442.                 newhash = location.hash
  1443.                     .replace( /#a=[^#]*|$/, '#a=' + inputAvg )
  1444.                     .replace( / /g, '_' );
  1445.  
  1446.                 if ( newhash.length ) {
  1447.                     location.hash = newhash;
  1448.                 }
  1449.             }
  1450.  
  1451.             inputAvg = inputAvg || 30;
  1452.             dataList.push( {
  1453.                 name: inputAvg + '-day average',
  1454.                 data: util.avg( data, inputAvg, isIndexChart ? 2 : 0 ),
  1455.                 lineWidth: 2,
  1456.                 dashStyle: 'shortdash',
  1457.             } );
  1458.  
  1459.             if ( volumes.length >= 10 ) {
  1460.                 dataList.push( {
  1461.                     name: '7-day volume',
  1462.                     data: volumes,
  1463.                     type: 'area',
  1464.                     color: '#cc8400',
  1465.                     fillColor: {
  1466.                         linearGradient: {
  1467.                             x1: 0,
  1468.                             y1: 0,
  1469.                             x2: 0,
  1470.                             y2: 1
  1471.                         },
  1472.                         stops: [
  1473.                             [0, '#ffa500'],
  1474.                             [1, 'white']
  1475.                         ],
  1476.                     },
  1477.                     // display on separate y-axis
  1478.                     yAxis: 1,
  1479.                 } );
  1480.             }
  1481.         }
  1482.  
  1483.         // create y-axis for price data
  1484.         yAxis = {
  1485.             title: {
  1486.                 text: isSmall ? null : ( isIndexChart ? 'Index history' : 'Price history' ),
  1487.                 offset: 60,
  1488.                 rotation: 270,
  1489.                 style: {
  1490.                     color: 'black',
  1491.                     fontSize: '12px',
  1492.                 },
  1493.             },
  1494.             opposite: false,
  1495.             labels: {
  1496.                 align: 'right',
  1497.                 x: -8,
  1498.                 y: 4,
  1499.             },
  1500.             allowDecimals: false,
  1501.             // 1 coin
  1502.             minTickInterval: 1,
  1503.             showLastLabel: 1,
  1504.             lineWidth: 1,
  1505.             lineColor: '#E0E0E0'
  1506.         };
  1507.    
  1508.         // volume data is plotted on a seperate y-axis
  1509.         if ( volumes.length >= 10 && !isSmall ) {
  1510.             // set height to allow room for second y-axis
  1511.             yAxis.height = 200;
  1512.  
  1513.             // convert to array and add volume data
  1514.             yAxis = [yAxis, {
  1515.                 title: {
  1516.                     text: '7-day volume',
  1517.                     offset: 60,
  1518.                     rotation: 270,
  1519.                     style: {
  1520.                         color: 'black',
  1521.                         fontSize: '12px'
  1522.                     }
  1523.                 },
  1524.                 opposite: false,
  1525.                 labels: {
  1526.                     align: 'right',
  1527.                     x: -8,
  1528.                     y: 4,
  1529.                 },
  1530.                 showEmpty: 0,
  1531.                 showLastLabel: 1,
  1532.                 offset: 0,
  1533.                 lineWidth: 1,
  1534.                 lineColor: '#E0E0E0',
  1535.                 height: 50,
  1536.                 top: 325,
  1537.                 min: 0
  1538.             }];
  1539.         }
  1540.    
  1541.         return [dataList, yAxis];
  1542.     }
  1543.  
  1544.     $( self.deps );
  1545.  
  1546. }( jQuery, mediaWiki, {addCommas: function ( num ) {
  1547.             num += '';
  1548.  
  1549.             var x = num.split( '.' ),
  1550.                 x1 = x[0],
  1551.                 x2 = x.length > 1 ? '.' + x[1] : '',
  1552.                 rgx = /(\d+)(\d{3})/;
  1553.  
  1554.             while ( rgx.test( x1 ) ) {
  1555.                 x1 = x1.replace( rgx, '$1,$2' );
  1556.             }
  1557.  
  1558.             return x1 + x2;
  1559.         }} ) );
  1560. mw.loader.state({"wikia.fake.articles.3fd05734fe2a4319522c9e2dc9d4a64f":"ready"});
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement