Widget:NVD3Test

From Granblue Fantasy Wiki
Jump to navigation Jump to search
<script type="application/javascript">
let processChartElement = function(element) {
     let series = [], errors = [];
     let formatDaysTime = (minutesPassed) => {
          var t = minutesPassed % 1440;
          var d = Math.floor(minutesPassed / 1440) + 1;
          var m = Math.floor(t%60)
          m = m < 10 ? "0" + m.toString() : m
          if (t == 0) { d = d-1; t = 1440; }

          return "Day " + d + ", " + Math.floor(t/60) + ":" + m;
     }
     let convertTeamRaidTime = (s) => {
          return formatDaysTime(1160 + (s/60));
     }
     for (let s of element.querySelectorAll('[data-series-label]')) {
          let sd = {
               key: s.dataset.seriesLabel || ('Unlabelled[' + series.length + ']'),
               values: [],
               color: s.dataset.seriesColor,
               area: s.dataset.seriesArea == "yes",
               grid: {
                    x: null,
                    y: null
               }
          };
          if (s.dataset.seriesX && s.dataset.seriesY) {
               let xv = s.dataset.seriesX.match(/\-?(?:\d+(?:\.\d+)?|\.\d+)/g);
               let yv = s.dataset.seriesY.match(/\-?(?:\d+(?:\.\d+)?|\.\d+)/g);
               if (xv && yv && xv.length == yv.length) {
                    for (let i = 0; i < xv.length; i++) {
                         sd.values.push({
                              x: Number.parseFloat(xv[i]),
                              y: Number.parseFloat(yv[i]),
                         });
                    }
               }
          } else if (s.dataset.seriesLinearInterpolation) {
               let prep = [];
               for (let x of s.dataset.seriesLinearInterpolation.match(/\-?(?:\d+(?:\.\d+)?|\.\d+)/g)) {
                    x = Number.parseFloat(x);
                    if (prep.length == 0 || prep[prep.length-1].length == 2)
                         prep.push([x]);
                    else
                         prep[prep.length-1].push(x)
               }
               if (prep.length && prep[prep.length-1].length != 2) prep.pop();
               prep.sort(function(a,b) { return a[0] == b[0] ? 0 : (a[0] < b[0] ? -1 : 1); });
               sd.values.push({x: prep[0][0], y: prep[0][1]});
               let stepSize = s.dataset.seriesInterpolationStep ? Number.parseFloat(s.dataset.seriesInterpolationStep) : 1;
               if (isNaN(stepSize) || stepSize < 0.5) stepSize = 1;
               for (let i = 1; i < prep.length; i++) {
                    let x0 = sd.values[sd.values.length-1].x, x1 = prep[i][0];
                    let y0 = sd.values[sd.values.length-1].y, y1 = prep[i][1];
                    let dy = (y1-y0)/(x1-x0);
                    for (let j=stepSize; (x0+j) < x1; j += stepSize) {
                         sd.values.push({x: x0+j, y: y0+j*dy});
                    }
                    sd.values.push({x: x1, y: y1});
               }
          }
          if (sd.values.length) {
               series.push(sd);
          } else {
               errors.push("Series contains invalid or incomplete data - " + sd.key);
          }
     }

     if (series.length) {
          let firstPoint = series[0].values[0];
          let yMin = firstPoint.y, yMax = firstPoint.y, xMin = firstPoint.x, xMax = firstPoint.x;
          for (let i = series.length; i-->0;) {
               for (let j = series[i].values.length; j-->0;) {
                    let y = series[i].values[j].y;
                    if (y > yMax)
                         yMax = y;
                    else if (yMin > y)
                         yMin = y;
               }
               if (xMin > series[i].values[0].x)
                    xMin = series[i].values[0].x;
               let lp = series[i].values[series[i].values.length-1];
               if (lp.x > xMax)
                    xMax = lp.x;
          }
          while (element.firstChild)
               element.removeChild(element.firstChild);
          let svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
          svg.style.width = element.dataset.chartWidth || (element.clientWidth + 'px');
          svg.style.height = element.dataset.chartHeight || (element.clientHeight + 'px');
          element.appendChild(svg);
          let yAxisWidth = element.dataset.yAxisWidth ? Number.parseInt(element.dataset.yAxisWidth) : 0;
          let yTickFormat = Math.max(yMax, Math.abs(yMin)) <= 200 ? ',.02f' : ',s';
          let xTickFormat = ',r';
          if (element.dataset.yNumberFormat) yTickFormat = element.dataset.yNumberFormat;
          if (element.dataset.xNumberFormat) xTickFormat = element.dataset.xNumberFormat;
          

          nv.addGraph(function() {
               var chart = nv.models.lineChart()
                    .useInteractiveGuideline(true)
                    .showYAxis(true)
                    .showXAxis(true)
               ;
               var xF, yF;
               switch (xTickFormat) {
                    case '': xF = d3.format(',r'); break;
                    case 'teamraid': xF = convertTeamRaidTime; break;
                    case 'daystime': xF = formatDaysTime; break;
                    default: xF = d3.format(xTickFormat); break;
               }
               switch (yTickFormat) {
                    case '': yF = d3.format(',.02f'); break;
                    case 'teamraid': yF = convertTeamRaidTime; break;
                    case 'daystime': yF = formatDaysTime; break;
                    default: yF = d3.format(yTickFormat); break;
               }

               chart.xAxis
                    .showMaxMin(true)
                    .tickFormat(xF)
                    // .tickValues([-69600, 103200, 214800,276000,362400,448800,535200])
               ;
               chart.yAxis
                    .showMaxMin(true)
                    .tickFormat(yF)
               ;

               // Gridlines at custom positions
               let g = [], gx = [], gy = [];
               if (element.dataset.xGrid) {
                    g = element.dataset.xGrid.match(/\-?(?:\d+(?:\.\d+)?|\.\d+)/g);
                    for (let i = 0; i < g.length; i++) {
                         gx.push(Number.parseFloat(g[i]));
                    }
                    if (gx.length > 0) {
                         chart.xAxis.tickValues(gx);
                    }
               }
               if (element.dataset.yGrid) {
                    g = element.dataset.yGrid.match(/\-?(?:\d+(?:\.\d+)?|\.\d+)/g);
                    for (let i = 0; i < g.length; i++) {
                         gy.push(Number.parseFloat(g[i]));
                    }
                    if (gy.length > 0) {
                         chart.yAxis.tickValues(gy);
                    }
               }
               if (yAxisWidth && yAxisWidth > 0)
                    chart.margin({left: yAxisWidth});
               if (yAxisWidth && yAxisWidth > 70)
                    chart.yAxis.axisLabelDistance(yAxisWidth-70);
               if (element.dataset.axisLabelX)
                    chart.xAxis.axisLabel(element.dataset.axisLabelX);
               if (element.dataset.axisLabelY)
                    chart.yAxis.axisLabel(element.dataset.axisLabelY);

               d3.select(svg)
                    .datum(series)
                    .transition().duration(500)
                    .call(chart)
               ;
               nv.utils.windowResize(chart.update);
               return chart;
          });
     } else {
          errors.push("Chart contains no usable series.");
     }

     if (errors.length) {
          let es = document.createElement('span');
          for (let i = 0; i < errors.length; i++) {
               if (i) es.appendChild(document.createElement('br'));
               es.appendChild(document.createTextNode(errors[i]));
          }
          es.className = "error-message";
          element.appendChild(es);
     }
};

if (document.querySelector('[data-nvd3-chart]'))
RLQ.push(['jquery', function() {
     mw.loader.using('ext.cargo.nvd3', function(require) {
          for (var e of document.querySelectorAll('[data-nvd3-chart]')) {
               processChartElement(e);
          }
     });
}]);
</script>