text: addEventListener('kupCardEvent', (e) => {
      console.log(e);
  });
  
  const cards = document.querySelectorAll('kup-card');
  
  for (let index = 0; index < cards.length; index++) {
      cards[index].data = {
          button: [
              {
                  icon: 'book',
              },
              {
                  icon: 'table-column-plus-after',
                  title: 'Add column',
              },
              {
                  icon: 'label',
                  title: 'Add code/description column',
              },
          ],
          chart: [
              {
                  data: {
                      columns: [
                          {
                              name: 'Col1',
                              title: 'Person',
                              size: '10',
                          },
                          {
                              name: 'Col2',
                              title: 'Value1',
                              size: '10',
                          },
                          {
                              name: 'Col3',
                              title: 'Value2',
                              size: '10',
                          },
                          {
                              name: 'Col4',
                              title: 'Value3',
                              size: '10',
                          },
                      ],
                      rows: [
                          {
                              cells: {
                                  Col1: {
                                      obj: {
                                          t: 'CN',
                                          p: 'COL',
                                          k: 'CASFRA',
                                      },
                                      value: 'CASFRA',
                                  },
                                  Col2: {
                                      obj: {
                                          t: 'NR',
                                          p: '',
                                          k: '10',
                                      },
                                      value: '10',
                                  },
                                  Col3: {
                                      obj: {
                                          t: 'NR',
                                          p: '',
                                          k: '100.60',
                                      },
                                      value: '100.60',
                                  },
                                  Col4: {
                                      obj: {
                                          t: 'NR',
                                          p: '',
                                          k: '55',
                                      },
                                      value: '55',
                                  },
                              },
                              id: '0',
                          },
                          {
                              cells: {
                                  Col1: {
                                      obj: {
                                          t: 'CN',
                                          p: 'COL',
                                          k: 'DELGIO',
                                      },
                                      value: 'DELGIO',
                                  },
                                  Col2: {
                                      obj: {
                                          t: 'NR',
                                          p: '',
                                          k: '6',
                                      },
                                      value: '6',
                                  },
                                  Col3: {
                                      obj: {
                                          t: 'NR',
                                          p: '',
                                          k: '67.8',
                                      },
                                      value: '67.8',
                                  },
                                  Col4: {
                                      obj: {
                                          t: 'NR',
                                          p: '',
                                          k: '33',
                                      },
                                      value: '33',
                                  },
                              },
                              id: '1',
                          },
                          {
                              cells: {
                                  Col1: {
                                      obj: {
                                          t: 'CN',
                                          p: 'COL',
                                          k: 'PARFRA',
                                      },
                                      value: 'PARFRA',
                                  },
                                  Col2: {
                                      obj: {
                                          t: 'NR',
                                          p: '',
                                          k: '5',
                                      },
                                      value: '5',
                                  },
                                  Col3: {
                                      obj: {
                                          t: 'NR',
                                          p: '',
                                          k: '120.06',
                                      },
                                      value: '120.06',
                                  },
                                  Col4: {
                                      obj: {
                                          t: 'NR',
                                          p: '',
                                          k: '77',
                                      },
                                      value: '77',
                                  },
                              },
                              id: '2',
                          },
                      ],
                  },
                  series: ['Col2', 'Col3', 'Col4'],
                  axis: 'Col1',
                  offlineMode: {
                      value: '1;7;-5;6;10;-5;3;4',
                      shape: 'line',
                  },
              },
          ],
          checkbox: [
              {
                  label: 'Checkbox1',
              },
              {
                  label: 'Checkbox2',
              },
              {
                  label: 'Checkbox3',
              },
              {
                  label: 'Checkbox4',
              },
          ],
          chip: [
              {
                  data: [
                      {
                          value: '1_chip',
                          icon: 'filter_1',
                          label: 'First chip',
                          checked: false,
                      },
                      {
                          value: '2_chip',
                          icon: 'filter_2',
                          label: 'Second chip',
                          checked: true,
                      },
                      {
                          value: '3_chip',
                          icon: 'filter_3',
                          label: 'Third chip',
                          checked: false,
                      },
                      {
                          value: '4_chip',
                          icon: 'filter_4',
                          label: 'Fourth chip',
                          checked: false,
                      },
                      {
                          value: '5_chip',
                          icon: 'filter_5',
                          label: 'Fifth chip',
                          checked: false,
                      },
                      {
                          value: '6_chip',
                          icon: 'filter_6',
                          label: 'Sixth chip',
                          checked: false,
                      },
                      {
                          value: '7_chip',
                          icon: 'filter_7',
                          label: 'Seventh chip',
                          checked: false,
                      },
                      {
                          value: '8_chip',
                          icon: 'filter_8',
                          label: 'Eighth chip',
                          checked: false,
                      },
                      {
                          value: '9_chip',
                          icon: 'filter_9',
                          label: 'Ninth chip',
                          checked: false,
                      },
                  ],
              },
          ],
          image: [
              {
                  resource: 'omega',
              },
              {
                  resource: 'radioactive',
              },
          ],
          progressbar: [
              {
                  value: 75,
                  centeredLabel: false,
              },
          ],
          text: [
              '#1 L U L',
              '#2 LUL',
              '#3 omegalul',
              '#4 L U L',
              '#5 LUL',
              '#6 omegalul',
              '#7 L U L',
              '#8 LUL',
              '#9 omegalul',
          ],
          textfield: [
              {
                  fullWidth: true,
                  icon: 'magnify',
                  isClearable: true,
                  label: 'Search...',
              },
          ],
      };
  }
  
  // SPARKLINE BELOW
  /**
   *
   * jquery.sparkline.js
   *
   * v2.1.2
   * (c) Splunk, Inc
   * Contact: Gareth Watts (gareth@splunk.com)
   * http://omnipotent.net/jquery.sparkline/
   *
   * Generates inline sparkline charts from data supplied either to the method
   * or inline in HTML
   *
   * Compatible with Internet Explorer 6.0+ and modern browsers equipped with the canvas tag
   * (Firefox 2.0+, Safari, Opera, etc)
   *
   * License: New BSD License
   *
   * Copyright (c) 2012, Splunk Inc.
   * All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without modification,
   * are permitted provided that the following conditions are met:
   *
   *     * Redistributions of source code must retain the above copyright notice,
   *       this list of conditions and the following disclaimer.
   *     * Redistributions in binary form must reproduce the above copyright notice,
   *       this list of conditions and the following disclaimer in the documentation
   *       and/or other materials provided with the distribution.
   *     * Neither the name of Splunk Inc nor the names of its contributors may
   *       be used to endorse or promote products derived from this software without
   *       specific prior written permission.
   *
   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
   * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
   * SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
   * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
   * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   *
   *
   * Usage:
   *  $(selector).sparkline(values, options)
   *
   * If values is undefined or set to 'html' then the data values are read from the specified tag:
   *   <p>Sparkline: <span class="sparkline">1,4,6,6,8,5,3,5</span></p>
   *   $('.sparkline').sparkline();
   * There must be no spaces in the enclosed data set
   *
   * Otherwise values must be an array of numbers or null values
   *    <p>Sparkline: <span id="sparkline1">This text replaced if the browser is compatible</span></p>
   *    $('#sparkline1').sparkline([1,4,6,6,8,5,3,5])
   *    $('#sparkline2').sparkline([1,4,6,null,null,5,3,5])
   *
   * Values can also be specified in an HTML comment, or as a values attribute:
   *    <p>Sparkline: <span class="sparkline"><!--1,4,6,6,8,5,3,5 --></span></p>
   *    <p>Sparkline: <span class="sparkline" values="1,4,6,6,8,5,3,5"></span></p>
   *    $('.sparkline').sparkline();
   *
   * For line charts, x values can also be specified:
   *   <p>Sparkline: <span class="sparkline">1:1,2.7:4,3.4:6,5:6,6:8,8.7:5,9:3,10:5</span></p>
   *    $('#sparkline1').sparkline([ [1,1], [2.7,4], [3.4,6], [5,6], [6,8], [8.7,5], [9,3], [10,5] ])
   *
   * By default, options should be passed in as teh second argument to the sparkline function:
   *   $('.sparkline').sparkline([1,2,3,4], {type: 'bar'})
   *
   * Options can also be set by passing them on the tag itself.  This feature is disabled by default though
   * as there's a slight performance overhead:
   *   $('.sparkline').sparkline([1,2,3,4], {enableTagOptions: true})
   *   <p>Sparkline: <span class="sparkline" sparkType="bar" sparkBarColor="red">loading</span></p>
   * Prefix all options supplied as tag attribute with "spark" (configurable by setting tagOptionPrefix)
   *
   * Supported options:
   *   lineColor - Color of the line used for the chart
   *   fillColor - Color used to fill in the chart - Set to '' or false for a transparent chart
   *   width - Width of the chart - Defaults to 3 times the number of values in pixels
   *   height - Height of the chart - Defaults to the height of the containing element
   *   chartRangeMin - Specify the minimum value to use for the Y range of the chart - Defaults to the minimum value supplied
   *   chartRangeMax - Specify the maximum value to use for the Y range of the chart - Defaults to the maximum value supplied
   *   chartRangeClip - Clip out of range values to the max/min specified by chartRangeMin and chartRangeMax
   *   chartRangeMinX - Specify the minimum value to use for the X range of the chart - Defaults to the minimum value supplied
   *   chartRangeMaxX - Specify the maximum value to use for the X range of the chart - Defaults to the maximum value supplied
   *   composite - If true then don't erase any existing chart attached to the tag, but draw
   *           another chart over the top - Note that width and height are ignored if an
   *           existing chart is detected.
   *   tagValuesAttribute - Name of tag attribute to check for data values - Defaults to 'values'
   *   enableTagOptions - Whether to check tags for sparkline options
   *   tagOptionPrefix - Prefix used for options supplied as tag attributes - Defaults to 'spark'
   *   disableHiddenCheck - If set to true, then the plugin will assume that charts will never be drawn into a
   *           hidden dom element, avoding a browser reflow
   *   disableInteraction - If set to true then all mouseover/click interaction behaviour will be disabled,
   *       making the plugin perform much like it did in 1.x
   *   disableTooltips - If set to true then tooltips will be disabled - Defaults to false (tooltips enabled)
   *   disableHighlight - If set to true then highlighting of selected chart elements on mouseover will be disabled
   *       defaults to false (highlights enabled)
   *   highlightLighten - Factor to lighten/darken highlighted chart values by - Defaults to 1.4 for a 40% increase
   *   tooltipContainer - Specify which DOM element the tooltip should be rendered into - defaults to document.body
   *   tooltipClassname - Optional CSS classname to apply to tooltips - If not specified then a default style will be applied
   *   tooltipOffsetX - How many pixels away from the mouse pointer to render the tooltip on the X axis
   *   tooltipOffsetY - How many pixels away from the mouse pointer to render the tooltip on the r axis
   *   tooltipFormatter  - Optional callback that allows you to override the HTML displayed in the tooltip
   *       callback is given arguments of (sparkline, options, fields)
   *   tooltipChartTitle - If specified then the tooltip uses the string specified by this setting as a title
   *   tooltipFormat - A format string or SPFormat object  (or an array thereof for multiple entries)
   *       to control the format of the tooltip
   *   tooltipPrefix - A string to prepend to each field displayed in a tooltip
   *   tooltipSuffix - A string to append to each field displayed in a tooltip
   *   tooltipSkipNull - If true then null values will not have a tooltip displayed (defaults to true)
   *   tooltipValueLookups - An object or range map to map field values to tooltip strings
   *       (eg. to map -1 to "Lost", 0 to "Draw", and 1 to "Win")
   *   numberFormatter - Optional callback for formatting numbers in tooltips
   *   numberDigitGroupSep - Character to use for group separator in numbers "1,234" - Defaults to ","
   *   numberDecimalMark - Character to use for the decimal point when formatting numbers - Defaults to "."
   *   numberDigitGroupCount - Number of digits between group separator - Defaults to 3
   *
   * There are 7 types of sparkline, selected by supplying a "type" option of 'line' (default),
   * 'bar', 'tristate', 'bullet', 'discrete', 'pie' or 'box'
   *    line - Line chart.  Options:
   *       spotColor - Set to '' to not end each line in a circular spot
   *       minSpotColor - If set, color of spot at minimum value
   *       maxSpotColor - If set, color of spot at maximum value
   *       spotRadius - Radius in pixels
   *       lineWidth - Width of line in pixels
   *       normalRangeMin
   *       normalRangeMax - If set draws a filled horizontal bar between these two values marking the "normal"
   *                      or expected range of values
   *       normalRangeColor - Color to use for the above bar
   *       drawNormalOnTop - Draw the normal range above the chart fill color if true
   *       defaultPixelsPerValue - Defaults to 3 pixels of width for each value in the chart
   *       highlightSpotColor - The color to use for drawing a highlight spot on mouseover - Set to null to disable
   *       highlightLineColor - The color to use for drawing a highlight line on mouseover - Set to null to disable
   *       valueSpots - Specify which points to draw spots on, and in which color.  Accepts a range map
   *
   *   bar - Bar chart.  Options:
   *       barColor - Color of bars for postive values
   *       negBarColor - Color of bars for negative values
   *       zeroColor - Color of bars with zero values
   *       nullColor - Color of bars with null values - Defaults to omitting the bar entirely
   *       barWidth - Width of bars in pixels
   *       colorMap - Optional mappnig of values to colors to override the *BarColor values above
   *                  can be an Array of values to control the color of individual bars or a range map
   *                  to specify colors for individual ranges of values
   *       barSpacing - Gap between bars in pixels
   *       zeroAxis - Centers the y-axis around zero if true
   *
   *   tristate - Charts values of win (>0), lose (<0) or draw (=0)
   *       posBarColor - Color of win values
   *       negBarColor - Color of lose values
   *       zeroBarColor - Color of draw values
   *       barWidth - Width of bars in pixels
   *       barSpacing - Gap between bars in pixels
   *       colorMap - Optional mappnig of values to colors to override the *BarColor values above
   *                  can be an Array of values to control the color of individual bars or a range map
   *                  to specify colors for individual ranges of values
   *
   *   discrete - Options:
   *       lineHeight - Height of each line in pixels - Defaults to 30% of the graph height
   *       thesholdValue - Values less than this value will be drawn using thresholdColor instead of lineColor
   *       thresholdColor
   *
   *   bullet - Values for bullet graphs msut be in the order: target, performance, range1, range2, range3, ...
   *       options:
   *       targetColor - The color of the vertical target marker
   *       targetWidth - The width of the target marker in pixels
   *       performanceColor - The color of the performance measure horizontal bar
   *       rangeColors - Colors to use for each qualitative range background color
   *
   *   pie - Pie chart. Options:
   *       sliceColors - An array of colors to use for pie slices
   *       offset - Angle in degrees to offset the first slice - Try -90 or +90
   *       borderWidth - Width of border to draw around the pie chart, in pixels - Defaults to 0 (no border)
   *       borderColor - Color to use for the pie chart border - Defaults to #000
   *
   *   box - Box plot. Options:
   *       raw - Set to true to supply pre-computed plot points as values
   *             values should be: low_outlier, low_whisker, q1, median, q3, high_whisker, high_outlier
   *             When set to false you can supply any number of values and the box plot will
   *             be computed for you.  Default is false.
   *       showOutliers - Set to true (default) to display outliers as circles
   *       outlierIQR - Interquartile range used to determine outliers.  Default 1.5
   *       boxLineColor - Outline color of the box
   *       boxFillColor - Fill color for the box
   *       whiskerColor - Line color used for whiskers
   *       outlierLineColor - Outline color of outlier circles
   *       outlierFillColor - Fill color of the outlier circles
   *       spotRadius - Radius of outlier circles
   *       medianColor - Line color of the median line
   *       target - Draw a target cross hair at the supplied value (default undefined)
   *
   *
   *
   *   Examples:
   *   $('#sparkline1').sparkline(myvalues, { lineColor: '#f00', fillColor: false });
   *   $('.barsparks').sparkline('html', { type:'bar', height:'40px', barWidth:5 });
   *   $('#tristate').sparkline([1,1,-1,1,0,0,-1], { type:'tristate' }):
   *   $('#discrete').sparkline([1,3,4,5,5,3,4,5], { type:'discrete' });
   *   $('#bullet').sparkline([10,12,12,9,7], { type:'bullet' });
   *   $('#pie').sparkline([1,1,2], { type:'pie' });
   */
  
  /*jslint regexp: true, browser: true, jquery: true, white: true, nomen: false, plusplus: false, maxerr: 500, indent: 4 */
  
  (function (document, Math, undefined) {
      // performance/minified-size optimization
      (function (factory) {
          if (typeof define === 'function' && define.amd) {
              define(['jquery'], factory);
          } else if (jQuery && !jQuery.fn.sparkline) {
              factory(jQuery);
          }
      })(function ($) {
          'use strict';
  
          var UNSET_OPTION = {},
              getDefaults,
              createClass,
              SPFormat,
              clipval,
              quartile,
              normalizeValue,
              normalizeValues,
              remove,
              isNumber,
              all,
              sum,
              addCSS,
              ensureArray,
              formatNumber,
              RangeMap,
              MouseHandler,
              Tooltip,
              barHighlightMixin,
              line,
              bar,
              tristate,
              discrete,
              bullet,
              pie,
              box,
              defaultStyles,
              initStyles,
              VShape,
              VCanvas_base,
              VCanvas_canvas,
              VCanvas_vml,
              pending,
              shapeCount = 0;
  
          /**
           * Default configuration settings
           */
          getDefaults = function () {
              return {
                  // Settings common to most/all chart types
                  common: {
                      type: 'line',
                      lineColor: '#00f',
                      fillColor: '#cdf',
                      defaultPixelsPerValue: 3,
                      width: 'auto',
                      height: 'auto',
                      composite: false,
                      tagValuesAttribute: 'values',
                      tagOptionsPrefix: 'spark',
                      enableTagOptions: false,
                      enableHighlight: true,
                      highlightLighten: 1.4,
                      tooltipSkipNull: true,
                      tooltipPrefix: '',
                      tooltipSuffix: '',
                      disableHiddenCheck: false,
                      numberFormatter: false,
                      numberDigitGroupCount: 3,
                      numberDigitGroupSep: ',',
                      numberDecimalMark: '.',
                      disableTooltips: false,
                      disableInteraction: false,
                  },
                  // Defaults for line charts
                  line: {
                      spotColor: '#f80',
                      highlightSpotColor: '#5f5',
                      highlightLineColor: '#f22',
                      spotRadius: 1.5,
                      minSpotColor: '#f80',
                      maxSpotColor: '#f80',
                      lineWidth: 1,
                      normalRangeMin: undefined,
                      normalRangeMax: undefined,
                      normalRangeColor: '#ccc',
                      drawNormalOnTop: false,
                      chartRangeMin: undefined,
                      chartRangeMax: undefined,
                      chartRangeMinX: undefined,
                      chartRangeMaxX: undefined,
                      tooltipFormat: new SPFormat(
                          '<span style="color: {{color}}">&#9679;</span> {{prefix}}{{y}}{{suffix}}'
                      ),
                  },
                  // Defaults for bar charts
                  bar: {
                      barColor: '#3366cc',
                      negBarColor: '#f44',
                      stackedBarColor: [
                          '#3366cc',
                          '#dc3912',
                          '#ff9900',
                          '#109618',
                          '#66aa00',
                          '#dd4477',
                          '#0099c6',
                          '#990099',
                      ],
                      zeroColor: undefined,
                      nullColor: undefined,
                      zeroAxis: true,
                      barWidth: 4,
                      barSpacing: 1,
                      chartRangeMax: undefined,
                      chartRangeMin: undefined,
                      chartRangeClip: false,
                      colorMap: undefined,
                      tooltipFormat: new SPFormat(
                          '<span style="color: {{color}}">&#9679;</span> {{prefix}}{{value}}{{suffix}}'
                      ),
                  },
                  // Defaults for tristate charts
                  tristate: {
                      barWidth: 4,
                      barSpacing: 1,
                      posBarColor: '#6f6',
                      negBarColor: '#f44',
                      zeroBarColor: '#999',
                      colorMap: {},
                      tooltipFormat: new SPFormat(
                          '<span style="color: {{color}}">&#9679;</span> {{value:map}}'
                      ),
                      tooltipValueLookups: {
                          map: { '-1': 'Loss', 0: 'Draw', 1: 'Win' },
                      },
                  },
                  // Defaults for discrete charts
                  discrete: {
                      lineHeight: 'auto',
                      thresholdColor: undefined,
                      thresholdValue: 0,
                      chartRangeMax: undefined,
                      chartRangeMin: undefined,
                      chartRangeClip: false,
                      tooltipFormat: new SPFormat(
                          '{{prefix}}{{value}}{{suffix}}'
                      ),
                  },
                  // Defaults for bullet charts
                  bullet: {
                      targetColor: '#f33',
                      targetWidth: 3, // width of the target bar in pixels
                      performanceColor: '#33f',
                      rangeColors: ['#d3dafe', '#a8b6ff', '#7f94ff'],
                      base: undefined, // set this to a number to change the base start number
                      tooltipFormat: new SPFormat(
                          '{{fieldkey:fields}} - {{value}}'
                      ),
                      tooltipValueLookups: {
                          fields: { r: 'Range', p: 'Performance', t: 'Target' },
                      },
                  },
                  // Defaults for pie charts
                  pie: {
                      offset: 0,
                      sliceColors: [
                          '#3366cc',
                          '#dc3912',
                          '#ff9900',
                          '#109618',
                          '#66aa00',
                          '#dd4477',
                          '#0099c6',
                          '#990099',
                      ],
                      borderWidth: 0,
                      borderColor: '#000',
                      tooltipFormat: new SPFormat(
                          '<span style="color: {{color}}">&#9679;</span> {{value}} ({{percent.1}}%)'
                      ),
                  },
                  // Defaults for box plots
                  box: {
                      raw: false,
                      boxLineColor: '#000',
                      boxFillColor: '#cdf',
                      whiskerColor: '#000',
                      outlierLineColor: '#333',
                      outlierFillColor: '#fff',
                      medianColor: '#f00',
                      showOutliers: true,
                      outlierIQR: 1.5,
                      spotRadius: 1.5,
                      target: undefined,
                      targetColor: '#4a2',
                      chartRangeMax: undefined,
                      chartRangeMin: undefined,
                      tooltipFormat: new SPFormat('{{field:fields}}: {{value}}'),
                      tooltipFormatFieldlistKey: 'field',
                      tooltipValueLookups: {
                          fields: {
                              lq: 'Lower Quartile',
                              med: 'Median',
                              uq: 'Upper Quartile',
                              lo: 'Left Outlier',
                              ro: 'Right Outlier',
                              lw: 'Left Whisker',
                              rw: 'Right Whisker',
                          },
                      },
                  },
              };
          };
  
          // You can have tooltips use a css class other than jqstooltip by specifying tooltipClassname
          defaultStyles =
              '.jqstooltip { ' +
              'position: absolute;' +
              'left: 0px;' +
              'top: 0px;' +
              'visibility: hidden;' +
              'background: rgb(0, 0, 0) transparent;' +
              'background-color: rgba(0,0,0,0.6);' +
              'filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000, endColorstr=#99000000);' +
              '-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000, endColorstr=#99000000)";' +
              'color: white;' +
              'font: 10px arial, san serif;' +
              'text-align: left;' +
              'white-space: nowrap;' +
              'padding: 5px;' +
              'border: 1px solid white;' +
              'z-index: 10000;' +
              'width: auto !important;' +
              'height: auto !important;' +
              '}' +
              '.jqsfield { ' +
              'color: white;' +
              'font: 10px arial, san serif;' +
              'text-align: left;' +
              '}';
  
          /**
           * Utilities
           */
  
          createClass = function (/* [baseclass, [mixin, ...]], definition */) {
              var Class, args;
              Class = function () {
                  this.init.apply(this, arguments);
              };
              if (arguments.length > 1) {
                  if (arguments[0]) {
                      Class.prototype = $.extend(
                          new arguments[0](),
                          arguments[arguments.length - 1]
                      );
                      Class._super = arguments[0].prototype;
                  } else {
                      Class.prototype = arguments[arguments.length - 1];
                  }
                  if (arguments.length > 2) {
                      args = Array.prototype.slice.call(arguments, 1, -1);
                      args.unshift(Class.prototype);
                      $.extend.apply($, args);
                  }
              } else {
                  Class.prototype = arguments[0];
              }
              Class.prototype.cls = Class;
              return Class;
          };
  
          /**
           * Wraps a format string for tooltips
           * {{x}}
           * {{x.2}
           * {{x:months}}
           */
          $.SPFormatClass = SPFormat = createClass({
              fre: /\{\{([\w.]+?)(:(.+?))?\}\}/g,
              precre: /(\w+)\.(\d+)/,
  
              init: function (format, fclass) {
                  this.format = format;
                  this.fclass = fclass;
              },
  
              render: function (fieldset, lookups, options) {
                  var self = this,
                      fields = fieldset,
                      match,
                      token,
                      lookupkey,
                      fieldvalue,
                      prec;
                  return this.format.replace(this.fre, function () {
                      var lookup;
                      token = arguments[1];
                      lookupkey = arguments[3];
                      match = self.precre.exec(token);
                      if (match) {
                          prec = match[2];
                          token = match[1];
                      } else {
                          prec = false;
                      }
                      fieldvalue = fields[token];
                      if (fieldvalue === undefined) {
                          return '';
                      }
                      if (lookupkey && lookups && lookups[lookupkey]) {
                          lookup = lookups[lookupkey];
                          if (lookup.get) {
                              // RangeMap
                              return (
                                  lookups[lookupkey].get(fieldvalue) || fieldvalue
                              );
                          } else {
                              return lookups[lookupkey][fieldvalue] || fieldvalue;
                          }
                      }
                      if (isNumber(fieldvalue)) {
                          if (options.get('numberFormatter')) {
                              fieldvalue = options.get('numberFormatter')(
                                  fieldvalue
                              );
                          } else {
                              fieldvalue = formatNumber(
                                  fieldvalue,
                                  prec,
                                  options.get('numberDigitGroupCount'),
                                  options.get('numberDigitGroupSep'),
                                  options.get('numberDecimalMark')
                              );
                          }
                      }
                      return fieldvalue;
                  });
              },
          });
  
          // convience method to avoid needing the new operator
          $.spformat = function (format, fclass) {
              return new SPFormat(format, fclass);
          };
  
          clipval = function (val, min, max) {
              if (val < min) {
                  return min;
              }
              if (val > max) {
                  return max;
              }
              return val;
          };
  
          quartile = function (values, q) {
              var vl;
              if (q === 2) {
                  vl = Math.floor(values.length / 2);
                  return values.length % 2
                      ? values[vl]
                      : (values[vl - 1] + values[vl]) / 2;
              } else {
                  if (values.length % 2) {
                      // odd
                      vl = (values.length * q + q) / 4;
                      return vl % 1
                          ? (values[Math.floor(vl)] +
                                values[Math.floor(vl) - 1]) /
                                2
                          : values[vl - 1];
                  } else {
                      //even
                      vl = (values.length * q + 2) / 4;
                      return vl % 1
                          ? (values[Math.floor(vl)] +
                                values[Math.floor(vl) - 1]) /
                                2
                          : values[vl - 1];
                  }
              }
          };
  
          normalizeValue = function (val) {
              var nf;
              switch (val) {
                  case 'undefined':
                      val = undefined;
                      break;
                  case 'null':
                      val = null;
                      break;
                  case 'true':
                      val = true;
                      break;
                  case 'false':
                      val = false;
                      break;
                  default:
                      nf = parseFloat(val);
                      if (val == nf) {
                          val = nf;
                      }
              }
              return val;
          };
  
          normalizeValues = function (vals) {
              var i,
                  result = [];
              for (i = vals.length; i--; ) {
                  result[i] = normalizeValue(vals[i]);
              }
              return result;
          };
  
          remove = function (vals, filter) {
              var i,
                  vl,
                  result = [];
              for (i = 0, vl = vals.length; i < vl; i++) {
                  if (vals[i] !== filter) {
                      result.push(vals[i]);
                  }
              }
              return result;
          };
  
          isNumber = function (num) {
              return !isNaN(parseFloat(num)) && isFinite(num);
          };
  
          formatNumber = function (num, prec, groupsize, groupsep, decsep) {
              var p, i;
              num = (prec === false
                  ? parseFloat(num).toString()
                  : num.toFixed(prec)
              ).split('');
              p = (p = $.inArray('.', num)) < 0 ? num.length : p;
              if (p < num.length) {
                  num[p] = decsep;
              }
              for (i = p - groupsize; i > 0; i -= groupsize) {
                  num.splice(i, 0, groupsep);
              }
              return num.join('');
          };
  
          // determine if all values of an array match a value
          // returns true if the array is empty
          all = function (val, arr, ignoreNull) {
              var i;
              for (i = arr.length; i--; ) {
                  if (ignoreNull && arr[i] === null) continue;
                  if (arr[i] !== val) {
                      return false;
                  }
              }
              return true;
          };
  
          // sums the numeric values in an array, ignoring other values
          sum = function (vals) {
              var total = 0,
                  i;
              for (i = vals.length; i--; ) {
                  total += typeof vals[i] === 'number' ? vals[i] : 0;
              }
              return total;
          };
  
          ensureArray = function (val) {
              return $.isArray(val) ? val : [val];
          };
  
          // http://paulirish.com/2008/bookmarklet-inject-new-css-rules/
          addCSS = function (css) {
              var tag;
              //if ('\v' == 'v') /* ie only */ {
              if (document.createStyleSheet) {
                  document.createStyleSheet().cssText = css;
              } else {
                  tag = document.createElement('style');
                  tag.type = 'text/css';
                  document.getElementsByTagName('head')[0].appendChild(tag);
                  tag[
                      typeof document.body.style.WebkitAppearance ==
                      'string' /* webkit only */
                          ? 'innerText'
                          : 'innerHTML'
                  ] = css;
              }
          };
  
          // Provide a cross-browser interface to a few simple drawing primitives
          $.fn.simpledraw = function (width, height, useExisting, interact) {
              var target, mhandler;
              if (useExisting && (target = this.data('_jqs_vcanvas'))) {
                  return target;
              }
  
              if ($.fn.sparkline.canvas === false) {
                  // We've already determined that neither Canvas nor VML are available
                  return false;
              } else if ($.fn.sparkline.canvas === undefined) {
                  // No function defined yet -- need to see if we support Canvas or VML
                  var el = document.createElement('canvas');
                  if (el.getContext && el.getContext('2d')) {
                      // Canvas is available
                      $.fn.sparkline.canvas = function (
                          width,
                          height,
                          target,
                          interact
                      ) {
                          return new VCanvas_canvas(
                              width,
                              height,
                              target,
                              interact
                          );
                      };
                  } else if (document.namespaces && !document.namespaces.v) {
                      // VML is available
                      document.namespaces.add(
                          'v',
                          'urn:schemas-microsoft-com:vml',
                          '#default#VML'
                      );
                      $.fn.sparkline.canvas = function (
                          width,
                          height,
                          target,
                          interact
                      ) {
                          return new VCanvas_vml(width, height, target);
                      };
                  } else {
                      // Neither Canvas nor VML are available
                      $.fn.sparkline.canvas = false;
                      return false;
                  }
              }
  
              if (width === undefined) {
                  width = $(this).innerWidth();
              }
              if (height === undefined) {
                  height = $(this).innerHeight();
              }
  
              target = $.fn.sparkline.canvas(width, height, this, interact);
  
              mhandler = $(this).data('_jqs_mhandler');
              if (mhandler) {
                  mhandler.registerCanvas(target);
              }
              return target;
          };
  
          $.fn.cleardraw = function () {
              var target = this.data('_jqs_vcanvas');
              if (target) {
                  target.reset();
              }
          };
  
          $.RangeMapClass = RangeMap = createClass({
              init: function (map) {
                  var key,
                      range,
                      rangelist = [];
                  for (key in map) {
                      if (
                          map.hasOwnProperty(key) &&
                          typeof key === 'string' &&
                          key.indexOf(':') > -1
                      ) {
                          range = key.split(':');
                          range[0] =
                              range[0].length === 0
                                  ? -Infinity
                                  : parseFloat(range[0]);
                          range[1] =
                              range[1].length === 0
                                  ? Infinity
                                  : parseFloat(range[1]);
                          range[2] = map[key];
                          rangelist.push(range);
                      }
                  }
                  this.map = map;
                  this.rangelist = rangelist || false;
              },
  
              get: function (value) {
                  var rangelist = this.rangelist,
                      i,
                      range,
                      result;
                  if ((result = this.map[value]) !== undefined) {
                      return result;
                  }
                  if (rangelist) {
                      for (i = rangelist.length; i--; ) {
                          range = rangelist[i];
                          if (range[0] <= value && range[1] >= value) {
                              return range[2];
                          }
                      }
                  }
                  return undefined;
              },
          });
  
          // Convenience function
          $.range_map = function (map) {
              return new RangeMap(map);
          };
  
          MouseHandler = createClass({
              init: function (el, options) {
                  var $el = $(el);
                  this.$el = $el;
                  this.options = options;
                  this.currentPageX = 0;
                  this.currentPageY = 0;
                  this.el = el;
                  this.splist = [];
                  this.tooltip = null;
                  this.over = false;
                  this.displayTooltips = !options.get('disableTooltips');
                  this.highlightEnabled = !options.get('disableHighlight');
              },
  
              registerSparkline: function (sp) {
                  this.splist.push(sp);
                  if (this.over) {
                      this.updateDisplay();
                  }
              },
  
              registerCanvas: function (canvas) {
                  var $canvas = $(canvas.canvas);
                  this.canvas = canvas;
                  this.$canvas = $canvas;
                  $canvas.mouseenter($.proxy(this.mouseenter, this));
                  $canvas.mouseleave($.proxy(this.mouseleave, this));
                  $canvas.click($.proxy(this.mouseclick, this));
              },
  
              reset: function (removeTooltip) {
                  this.splist = [];
                  if (this.tooltip && removeTooltip) {
                      this.tooltip.remove();
                      this.tooltip = undefined;
                  }
              },
  
              mouseclick: function (e) {
                  var clickEvent = $.Event('sparklineClick');
                  clickEvent.originalEvent = e;
                  clickEvent.sparklines = this.splist;
                  this.$el.trigger(clickEvent);
              },
  
              mouseenter: function (e) {
                  $(document.body).unbind('mousemove.jqs');
                  $(document.body).bind(
                      'mousemove.jqs',
                      $.proxy(this.mousemove, this)
                  );
                  this.over = true;
                  this.currentPageX = e.pageX;
                  this.currentPageY = e.pageY;
                  this.currentEl = e.target;
                  if (!this.tooltip && this.displayTooltips) {
                      this.tooltip = new Tooltip(this.options);
                      this.tooltip.updatePosition(e.pageX, e.pageY);
                  }
                  this.updateDisplay();
              },
  
              mouseleave: function () {
                  $(document.body).unbind('mousemove.jqs');
                  var splist = this.splist,
                      spcount = splist.length,
                      needsRefresh = false,
                      sp,
                      i;
                  this.over = false;
                  this.currentEl = null;
  
                  if (this.tooltip) {
                      this.tooltip.remove();
                      this.tooltip = null;
                  }
  
                  for (i = 0; i < spcount; i++) {
                      sp = splist[i];
                      if (sp.clearRegionHighlight()) {
                          needsRefresh = true;
                      }
                  }
  
                  if (needsRefresh) {
                      this.canvas.render();
                  }
              },
  
              mousemove: function (e) {
                  this.currentPageX = e.pageX;
                  this.currentPageY = e.pageY;
                  this.currentEl = e.target;
                  if (this.tooltip) {
                      this.tooltip.updatePosition(e.pageX, e.pageY);
                  }
                  this.updateDisplay();
              },
  
              updateDisplay: function () {
                  var splist = this.splist,
                      spcount = splist.length,
                      needsRefresh = false,
                      offset = jQuery3_4_1_Offset(this.$canvas),
                      localX = this.currentPageX - offset.left,
                      localY = this.currentPageY - offset.top,
                      tooltiphtml,
                      sp,
                      i,
                      result,
                      changeEvent;
                  if (!this.over) {
                      return;
                  }
                  for (i = 0; i < spcount; i++) {
                      sp = splist[i];
                      result = sp.setRegionHighlight(
                          this.currentEl,
                          localX,
                          localY
                      );
                      if (result) {
                          needsRefresh = true;
                      }
                  }
                  if (needsRefresh) {
                      changeEvent = $.Event('sparklineRegionChange');
                      changeEvent.sparklines = this.splist;
                      this.$el.trigger(changeEvent);
                      if (this.tooltip) {
                          tooltiphtml = '';
                          for (i = 0; i < spcount; i++) {
                              sp = splist[i];
                              tooltiphtml += sp.getCurrentRegionTooltip();
                          }
                          this.tooltip.setContent(tooltiphtml);
                      }
                      if (!this.disableHighlight) {
                          this.canvas.render();
                      }
                  }
                  if (result === null) {
                      this.mouseleave();
                  }
              },
          });
  
          Tooltip = createClass({
              sizeStyle:
                  'position: static !important;' +
                  'display: block !important;' +
                  'visibility: hidden !important;' +
                  'float: left !important;',
  
              init: function (options) {
                  var tooltipClassname = options.get(
                          'tooltipClassname',
                          'jqstooltip'
                      ),
                      sizetipStyle = this.sizeStyle,
                      offset;
                  this.container =
                      options.get('tooltipContainer') || document.body;
                  this.tooltipOffsetX = options.get('tooltipOffsetX', 10);
                  this.tooltipOffsetY = options.get('tooltipOffsetY', 12);
                  // remove any previous lingering tooltip
                  $('#jqssizetip').remove();
                  $('#jqstooltip').remove();
                  this.sizetip = $('<div/>', {
                      id: 'jqssizetip',
                      style: sizetipStyle,
                      class: tooltipClassname,
                  });
                  this.tooltip = $('<div/>', {
                      id: 'jqstooltip',
                      class: tooltipClassname,
                  }).appendTo(this.container);
                  // account for the container's location
                  offset = jQuery3_4_1_Offset(this.tooltip);
                  this.offsetLeft = offset.left;
                  this.offsetTop = offset.top;
                  this.hidden = true;
                  $(window).unbind('resize.jqs scroll.jqs');
                  $(window).bind(
                      'resize.jqs scroll.jqs',
                      $.proxy(this.updateWindowDims, this)
                  );
                  this.updateWindowDims();
              },
  
              updateWindowDims: function () {
                  this.scrollTop = $(window).scrollTop();
                  this.scrollLeft = $(window).scrollLeft();
                  this.scrollRight = this.scrollLeft + $(window).width();
                  this.updatePosition();
              },
  
              getSize: function (content) {
                  this.sizetip.html(content).appendTo(this.container);
                  this.width = this.sizetip.width() + 1;
                  this.height = this.sizetip.height();
                  this.sizetip.remove();
              },
  
              setContent: function (content) {
                  if (!content) {
                      this.tooltip.css('visibility', 'hidden');
                      this.hidden = true;
                      return;
                  }
                  this.getSize(content);
                  this.tooltip.html(content).css({
                      width: this.width,
                      height: this.height,
                      visibility: 'visible',
                  });
                  if (this.hidden) {
                      this.hidden = false;
                      this.updatePosition();
                  }
              },
  
              updatePosition: function (x, y) {
                  if (x === undefined) {
                      if (this.mousex === undefined) {
                          return;
                      }
                      x = this.mousex - this.offsetLeft;
                      y = this.mousey - this.offsetTop;
                  } else {
                      this.mousex = x = x - this.offsetLeft;
                      this.mousey = y = y - this.offsetTop;
                  }
                  if (!this.height || !this.width || this.hidden) {
                      return;
                  }
  
                  y -= this.height + this.tooltipOffsetY;
                  x += this.tooltipOffsetX;
  
                  if (y < this.scrollTop) {
                      y = this.scrollTop;
                  }
                  if (x < this.scrollLeft) {
                      x = this.scrollLeft;
                  } else if (x + this.width > this.scrollRight) {
                      x = this.scrollRight - this.width;
                  }
  
                  this.tooltip.css({
                      left: x,
                      top: y,
                  });
              },
  
              remove: function () {
                  this.tooltip.remove();
                  this.sizetip.remove();
                  this.sizetip = this.tooltip = undefined;
                  $(window).unbind('resize.jqs scroll.jqs');
              },
          });
  
          initStyles = function () {
              addCSS(defaultStyles);
          };
  
          $(initStyles);
  
          pending = [];
          $.fn.sparkline = function (userValues, userOptions) {
              return this.each(function () {
                  var options = new $.fn.sparkline.options(this, userOptions),
                      $this = $(this),
                      render,
                      i;
                  render = function () {
                      var values, width, height, tmp, mhandler, sp, vals;
                      if (userValues === 'html' || userValues === undefined) {
                          vals = this.getAttribute(
                              options.get('tagValuesAttribute')
                          );
                          if (vals === undefined || vals === null) {
                              vals = $this.html();
                          }
                          values = vals
                              .replace(/(^\s*<!--)|(-->\s*$)|\s+/g, '')
                              .split(',');
                      } else {
                          values = userValues;
                      }
  
                      width =
                          options.get('width') === 'auto'
                              ? values.length *
                                options.get('defaultPixelsPerValue')
                              : options.get('width');
                      if (options.get('height') === 'auto') {
                          if (
                              !options.get('composite') ||
                              !$.data(this, '_jqs_vcanvas')
                          ) {
                              // must be a better way to get the line height
                              tmp = document.createElement('span');
                              tmp.innerHTML = 'a';
                              $this.html(tmp);
                              height = $(tmp).innerHeight() || $(tmp).height();
                              $(tmp).remove();
                              tmp = null;
                          }
                      } else {
                          height = options.get('height');
                      }
  
                      if (!options.get('disableInteraction')) {
                          mhandler = $.data(this, '_jqs_mhandler');
                          if (!mhandler) {
                              mhandler = new MouseHandler(this, options);
                              $.data(this, '_jqs_mhandler', mhandler);
                          } else if (!options.get('composite')) {
                              mhandler.reset();
                          }
                      } else {
                          mhandler = false;
                      }
  
                      if (
                          options.get('composite') &&
                          !$.data(this, '_jqs_vcanvas')
                      ) {
                          if (!$.data(this, '_jqs_errnotify')) {
                              alert(
                                  'Attempted to attach a composite sparkline to an element with no existing sparkline'
                              );
                              $.data(this, '_jqs_errnotify', true);
                          }
                          return;
                      }
  
                      sp = new $.fn.sparkline[options.get('type')](
                          this,
                          values,
                          options,
                          width,
                          height
                      );
  
                      sp.render();
  
                      if (mhandler) {
                          mhandler.registerSparkline(sp);
                      }
                  };
                  // modificato rispetto alla release ufficiale, eliminata ultimo test, il body non è accessibile da un shadow-root
                  //if (($(this).html() && !options.get('disableHiddenCheck') && $(this).is(':hidden')) || !$(this).parents('body').length) {
                  if (
                      $(this).html() &&
                      !options.get('disableHiddenCheck') &&
                      $(this).is(':hidden')
                  ) {
                      if (
                          !options.get('composite') &&
                          $.data(this, '_jqs_pending')
                      ) {
                          // remove any existing references to the element
                          for (i = pending.length; i; i--) {
                              if (pending[i - 1][0] == this) {
                                  pending.splice(i - 1, 1);
                              }
                          }
                      }
                      pending.push([this, render]);
                      $.data(this, '_jqs_pending', true);
                  } else {
                      render.call(this);
                  }
              });
          };
  
          $.fn.sparkline.defaults = getDefaults();
  
          $.sparkline_display_visible = function () {
              var el, i, pl;
              var done = [];
              for (i = 0, pl = pending.length; i < pl; i++) {
                  el = pending[i][0];
                  if ($(el).is(':visible') && !$(el).parents().is(':hidden')) {
                      pending[i][1].call(el);
                      $.data(pending[i][0], '_jqs_pending', false);
                      done.push(i);
                  } else if (
                      !$(el).closest('html').length &&
                      !$.data(el, '_jqs_pending')
                  ) {
                      // element has been inserted and removed from the DOM
                      // If it was not yet inserted into the dom then the .data request
                      // will return true.
                      // removing from the dom causes the data to be removed.
                      $.data(pending[i][0], '_jqs_pending', false);
                      done.push(i);
                  }
              }
              for (i = done.length; i; i--) {
                  pending.splice(done[i - 1], 1);
              }
          };
  
          /**
           * User option handler
           */
          $.fn.sparkline.options = createClass({
              init: function (tag, userOptions) {
                  var extendedOptions, defaults, base, tagOptionType;
                  this.userOptions = userOptions = userOptions || {};
                  this.tag = tag;
                  this.tagValCache = {};
                  defaults = $.fn.sparkline.defaults;
                  base = defaults.common;
                  this.tagOptionsPrefix =
                      userOptions.enableTagOptions &&
                      (userOptions.tagOptionsPrefix || base.tagOptionsPrefix);
  
                  tagOptionType = this.getTagSetting('type');
                  if (tagOptionType === UNSET_OPTION) {
                      extendedOptions = defaults[userOptions.type || base.type];
                  } else {
                      extendedOptions = defaults[tagOptionType];
                  }
                  this.mergedOptions = $.extend(
                      {},
                      base,
                      extendedOptions,
                      userOptions
                  );
              },
  
              getTagSetting: function (key) {
                  var prefix = this.tagOptionsPrefix,
                      val,
                      i,
                      pairs,
                      keyval;
                  if (prefix === false || prefix === undefined) {
                      return UNSET_OPTION;
                  }
                  if (this.tagValCache.hasOwnProperty(key)) {
                      val = this.tagValCache.key;
                  } else {
                      val = this.tag.getAttribute(prefix + key);
                      if (val === undefined || val === null) {
                          val = UNSET_OPTION;
                      } else if (val.substr(0, 1) === '[') {
                          val = val.substr(1, val.length - 2).split(',');
                          for (i = val.length; i--; ) {
                              val[i] = normalizeValue(
                                  val[i].replace(/(^\s*)|(\s*$)/g, '')
                              );
                          }
                      } else if (val.substr(0, 1) === '{') {
                          pairs = val.substr(1, val.length - 2).split(',');
                          val = {};
                          for (i = pairs.length; i--; ) {
                              keyval = pairs[i].split(':', 2);
                              val[
                                  keyval[0].replace(/(^\s*)|(\s*$)/g, '')
                              ] = normalizeValue(
                                  keyval[1].replace(/(^\s*)|(\s*$)/g, '')
                              );
                          }
                      } else {
                          val = normalizeValue(val);
                      }
                      this.tagValCache.key = val;
                  }
                  return val;
              },
  
              get: function (key, defaultval) {
                  var tagOption = this.getTagSetting(key),
                      result;
                  if (tagOption !== UNSET_OPTION) {
                      return tagOption;
                  }
                  return (result = this.mergedOptions[key]) === undefined
                      ? defaultval
                      : result;
              },
          });
  
          $.fn.sparkline._base = createClass({
              disabled: false,
  
              init: function (el, values, options, width, height) {
                  this.el = el;
                  this.$el = $(el);
                  this.values = values;
                  this.options = options;
                  this.width = width;
                  this.height = height;
                  this.currentRegion = undefined;
              },
  
              /**
               * Setup the canvas
               */
              initTarget: function () {
                  var interactive = !this.options.get('disableInteraction');
                  if (
                      !(this.target = this.$el.simpledraw(
                          this.width,
                          this.height,
                          this.options.get('composite'),
                          interactive
                      ))
                  ) {
                      this.disabled = true;
                  } else {
                      this.canvasWidth = this.target.pixelWidth;
                      this.canvasHeight = this.target.pixelHeight;
                  }
              },
  
              /**
               * Actually render the chart to the canvas
               */
              render: function () {
                  if (this.disabled) {
                      this.el.innerHTML = '';
                      return false;
                  }
                  return true;
              },
  
              /**
               * Return a region id for a given x/y co-ordinate
               */
              getRegion: function (x, y) {},
  
              /**
               * Highlight an item based on the moused-over x,y co-ordinate
               */
              setRegionHighlight: function (el, x, y) {
                  var currentRegion = this.currentRegion,
                      highlightEnabled = !this.options.get('disableHighlight'),
                      newRegion;
                  if (
                      x > this.canvasWidth ||
                      y > this.canvasHeight ||
                      x < 0 ||
                      y < 0
                  ) {
                      return null;
                  }
                  newRegion = this.getRegion(el, x, y);
                  if (currentRegion !== newRegion) {
                      if (currentRegion !== undefined && highlightEnabled) {
                          this.removeHighlight();
                      }
                      this.currentRegion = newRegion;
                      if (newRegion !== undefined && highlightEnabled) {
                          this.renderHighlight();
                      }
                      return true;
                  }
                  return false;
              },
  
              /**
               * Reset any currently highlighted item
               */
              clearRegionHighlight: function () {
                  if (this.currentRegion !== undefined) {
                      this.removeHighlight();
                      this.currentRegion = undefined;
                      return true;
                  }
                  return false;
              },
  
              renderHighlight: function () {
                  this.changeHighlight(true);
              },
  
              removeHighlight: function () {
                  this.changeHighlight(false);
              },
  
              changeHighlight: function (highlight) {},
  
              /**
               * Fetch the HTML to display as a tooltip
               */
              getCurrentRegionTooltip: function () {
                  var options = this.options,
                      header = '',
                      entries = [],
                      fields,
                      formats,
                      formatlen,
                      fclass,
                      text,
                      i,
                      showFields,
                      showFieldsKey,
                      newFields,
                      fv,
                      formatter,
                      format,
                      fieldlen,
                      j;
                  if (this.currentRegion === undefined) {
                      return '';
                  }
                  fields = this.getCurrentRegionFields();
                  formatter = options.get('tooltipFormatter');
                  if (formatter) {
                      return formatter(this, options, fields);
                  }
                  if (options.get('tooltipChartTitle')) {
                      header +=
                          '<div class="jqs jqstitle">' +
                          options.get('tooltipChartTitle') +
                          '</div>\n';
                  }
                  formats = this.options.get('tooltipFormat');
                  if (!formats) {
                      return '';
                  }
                  if (!$.isArray(formats)) {
                      formats = [formats];
                  }
                  if (!$.isArray(fields)) {
                      fields = [fields];
                  }
                  showFields = this.options.get('tooltipFormatFieldlist');
                  showFieldsKey = this.options.get('tooltipFormatFieldlistKey');
                  if (showFields && showFieldsKey) {
                      // user-selected ordering of fields
                      newFields = [];
                      for (i = fields.length; i--; ) {
                          fv = fields[i][showFieldsKey];
                          if ((j = $.inArray(fv, showFields)) != -1) {
                              newFields[j] = fields[i];
                          }
                      }
                      fields = newFields;
                  }
                  formatlen = formats.length;
                  fieldlen = fields.length;
                  for (i = 0; i < formatlen; i++) {
                      format = formats[i];
                      if (typeof format === 'string') {
                          format = new SPFormat(format);
                      }
                      fclass = format.fclass || 'jqsfield';
                      for (j = 0; j < fieldlen; j++) {
                          if (
                              !fields[j].isNull ||
                              !options.get('tooltipSkipNull')
                          ) {
                              $.extend(fields[j], {
                                  prefix: options.get('tooltipPrefix'),
                                  suffix: options.get('tooltipSuffix'),
                              });
                              text = format.render(
                                  fields[j],
                                  options.get('tooltipValueLookups'),
                                  options
                              );
                              entries.push(
                                  '<div class="' + fclass + '">' + text + '</div>'
                              );
                          }
                      }
                  }
                  if (entries.length) {
                      return header + entries.join('\n');
                  }
                  return '';
              },
  
              getCurrentRegionFields: function () {},
  
              calcHighlightColor: function (color, options) {
                  var highlightColor = options.get('highlightColor'),
                      lighten = options.get('highlightLighten'),
                      parse,
                      mult,
                      rgbnew,
                      i;
                  if (highlightColor) {
                      return highlightColor;
                  }
                  if (lighten) {
                      // extract RGB values
                      parse =
                          /^#([0-9a-f])([0-9a-f])([0-9a-f])$/i.exec(color) ||
                          /^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i.exec(
                              color
                          );
                      if (parse) {
                          rgbnew = [];
                          mult = color.length === 4 ? 16 : 1;
                          for (i = 0; i < 3; i++) {
                              rgbnew[i] = clipval(
                                  Math.round(
                                      parseInt(parse[i + 1], 16) * mult * lighten
                                  ),
                                  0,
                                  255
                              );
                          }
                          return 'rgb(' + rgbnew.join(',') + ')';
                      }
                  }
                  return color;
              },
          });
  
          barHighlightMixin = {
              changeHighlight: function (highlight) {
                  var currentRegion = this.currentRegion,
                      target = this.target,
                      shapeids = this.regionShapes[currentRegion],
                      newShapes;
                  // will be null if the region value was null
                  if (shapeids) {
                      newShapes = this.renderRegion(currentRegion, highlight);
                      if ($.isArray(newShapes) || $.isArray(shapeids)) {
                          target.replaceWithShapes(shapeids, newShapes);
                          this.regionShapes[currentRegion] = $.map(
                              newShapes,
                              function (newShape) {
                                  return newShape.id;
                              }
                          );
                      } else {
                          target.replaceWithShape(shapeids, newShapes);
                          this.regionShapes[currentRegion] = newShapes.id;
                      }
                  }
              },
  
              render: function () {
                  var values = this.values,
                      target = this.target,
                      regionShapes = this.regionShapes,
                      shapes,
                      ids,
                      i,
                      j;
  
                  if (!this.cls._super.render.call(this)) {
                      return;
                  }
                  for (i = values.length; i--; ) {
                      shapes = this.renderRegion(i);
                      if (shapes) {
                          if ($.isArray(shapes)) {
                              ids = [];
                              for (j = shapes.length; j--; ) {
                                  shapes[j].append();
                                  ids.push(shapes[j].id);
                              }
                              regionShapes[i] = ids;
                          } else {
                              shapes.append();
                              regionShapes[i] = shapes.id; // store just the shapeid
                          }
                      } else {
                          // null value
                          regionShapes[i] = null;
                      }
                  }
                  target.render();
              },
          };
  
          /**
           * Line charts
           */
          $.fn.sparkline.line = line = createClass($.fn.sparkline._base, {
              type: 'line',
  
              init: function (el, values, options, width, height) {
                  line._super.init.call(this, el, values, options, width, height);
                  this.vertices = [];
                  this.regionMap = [];
                  this.xvalues = [];
                  this.yvalues = [];
                  this.yminmax = [];
                  this.hightlightSpotId = null;
                  this.lastShapeId = null;
                  this.initTarget();
              },
  
              getRegion: function (el, x, y) {
                  var i,
                      regionMap = this.regionMap; // maps regions to value positions
                  for (i = regionMap.length; i--; ) {
                      if (
                          regionMap[i] !== null &&
                          x >= regionMap[i][0] &&
                          x <= regionMap[i][1]
                      ) {
                          return regionMap[i][2];
                      }
                  }
                  return undefined;
              },
  
              getCurrentRegionFields: function () {
                  var currentRegion = this.currentRegion;
                  return {
                      isNull: this.yvalues[currentRegion] === null,
                      x: this.xvalues[currentRegion],
                      y: this.yvalues[currentRegion],
                      color: this.options.get('lineColor'),
                      fillColor: this.options.get('fillColor'),
                      offset: currentRegion,
                  };
              },
  
              renderHighlight: function () {
                  var currentRegion = this.currentRegion,
                      target = this.target,
                      vertex = this.vertices[currentRegion],
                      options = this.options,
                      spotRadius = options.get('spotRadius'),
                      highlightSpotColor = options.get('highlightSpotColor'),
                      highlightLineColor = options.get('highlightLineColor'),
                      highlightSpot,
                      highlightLine;
  
                  if (!vertex) {
                      return;
                  }
                  if (spotRadius && highlightSpotColor) {
                      highlightSpot = target.drawCircle(
                          vertex[0],
                          vertex[1],
                          spotRadius,
                          undefined,
                          highlightSpotColor
                      );
                      this.highlightSpotId = highlightSpot.id;
                      target.insertAfterShape(this.lastShapeId, highlightSpot);
                  }
                  if (highlightLineColor) {
                      highlightLine = target.drawLine(
                          vertex[0],
                          this.canvasTop,
                          vertex[0],
                          this.canvasTop + this.canvasHeight,
                          highlightLineColor
                      );
                      this.highlightLineId = highlightLine.id;
                      target.insertAfterShape(this.lastShapeId, highlightLine);
                  }
              },
  
              removeHighlight: function () {
                  var target = this.target;
                  if (this.highlightSpotId) {
                      target.removeShapeId(this.highlightSpotId);
                      this.highlightSpotId = null;
                  }
                  if (this.highlightLineId) {
                      target.removeShapeId(this.highlightLineId);
                      this.highlightLineId = null;
                  }
              },
  
              scanValues: function () {
                  var values = this.values,
                      valcount = values.length,
                      xvalues = this.xvalues,
                      yvalues = this.yvalues,
                      yminmax = this.yminmax,
                      i,
                      val,
                      isStr,
                      isArray,
                      sp;
                  for (i = 0; i < valcount; i++) {
                      val = values[i];
                      isStr = typeof values[i] === 'string';
                      isArray =
                          typeof values[i] === 'object' &&
                          values[i] instanceof Array;
                      sp = isStr && values[i].split(':');
                      if (isStr && sp.length === 2) {
                          // x:y
                          xvalues.push(Number(sp[0]));
                          yvalues.push(Number(sp[1]));
                          yminmax.push(Number(sp[1]));
                      } else if (isArray) {
                          xvalues.push(val[0]);
                          yvalues.push(val[1]);
                          yminmax.push(val[1]);
                      } else {
                          xvalues.push(i);
                          if (values[i] === null || values[i] === 'null') {
                              yvalues.push(null);
                          } else {
                              yvalues.push(Number(val));
                              yminmax.push(Number(val));
                          }
                      }
                  }
                  if (this.options.get('xvalues')) {
                      xvalues = this.options.get('xvalues');
                  }
  
                  this.maxy = this.maxyorg = Math.max.apply(Math, yminmax);
                  this.miny = this.minyorg = Math.min.apply(Math, yminmax);
  
                  this.maxx = Math.max.apply(Math, xvalues);
                  this.minx = Math.min.apply(Math, xvalues);
  
                  this.xvalues = xvalues;
                  this.yvalues = yvalues;
                  this.yminmax = yminmax;
              },
  
              processRangeOptions: function () {
                  var options = this.options,
                      normalRangeMin = options.get('normalRangeMin'),
                      normalRangeMax = options.get('normalRangeMax');
  
                  if (normalRangeMin !== undefined) {
                      if (normalRangeMin < this.miny) {
                          this.miny = normalRangeMin;
                      }
                      if (normalRangeMax > this.maxy) {
                          this.maxy = normalRangeMax;
                      }
                  }
                  if (
                      options.get('chartRangeMin') !== undefined &&
                      (options.get('chartRangeClip') ||
                          options.get('chartRangeMin') < this.miny)
                  ) {
                      this.miny = options.get('chartRangeMin');
                  }
                  if (
                      options.get('chartRangeMax') !== undefined &&
                      (options.get('chartRangeClip') ||
                          options.get('chartRangeMax') > this.maxy)
                  ) {
                      this.maxy = options.get('chartRangeMax');
                  }
                  if (
                      options.get('chartRangeMinX') !== undefined &&
                      (options.get('chartRangeClipX') ||
                          options.get('chartRangeMinX') < this.minx)
                  ) {
                      this.minx = options.get('chartRangeMinX');
                  }
                  if (
                      options.get('chartRangeMaxX') !== undefined &&
                      (options.get('chartRangeClipX') ||
                          options.get('chartRangeMaxX') > this.maxx)
                  ) {
                      this.maxx = options.get('chartRangeMaxX');
                  }
              },
  
              drawNormalRange: function (
                  canvasLeft,
                  canvasTop,
                  canvasHeight,
                  canvasWidth,
                  rangey
              ) {
                  var normalRangeMin = this.options.get('normalRangeMin'),
                      normalRangeMax = this.options.get('normalRangeMax'),
                      ytop =
                          canvasTop +
                          Math.round(
                              canvasHeight -
                                  canvasHeight *
                                      ((normalRangeMax - this.miny) / rangey)
                          ),
                      height = Math.round(
                          (canvasHeight * (normalRangeMax - normalRangeMin)) /
                              rangey
                      );
                  this.target
                      .drawRect(
                          canvasLeft,
                          ytop,
                          canvasWidth,
                          height,
                          undefined,
                          this.options.get('normalRangeColor')
                      )
                      .append();
              },
  
              render: function () {
                  var options = this.options,
                      target = this.target,
                      canvasWidth = this.canvasWidth,
                      canvasHeight = this.canvasHeight,
                      vertices = this.vertices,
                      spotRadius = options.get('spotRadius'),
                      regionMap = this.regionMap,
                      rangex,
                      rangey,
                      yvallast,
                      canvasTop,
                      canvasLeft,
                      vertex,
                      path,
                      paths,
                      x,
                      y,
                      xnext,
                      xpos,
                      xposnext,
                      last,
                      next,
                      yvalcount,
                      lineShapes,
                      fillShapes,
                      plen,
                      valueSpots,
                      hlSpotsEnabled,
                      color,
                      xvalues,
                      yvalues,
                      i;
  
                  if (!line._super.render.call(this)) {
                      return;
                  }
  
                  this.scanValues();
                  this.processRangeOptions();
  
                  xvalues = this.xvalues;
                  yvalues = this.yvalues;
  
                  if (!this.yminmax.length || this.yvalues.length < 2) {
                      // empty or all null valuess
                      return;
                  }
  
                  canvasTop = canvasLeft = 0;
  
                  rangex =
                      this.maxx - this.minx === 0 ? 1 : this.maxx - this.minx;
                  rangey =
                      this.maxy - this.miny === 0 ? 1 : this.maxy - this.miny;
                  yvallast = this.yvalues.length - 1;
  
                  if (
                      spotRadius &&
                      (canvasWidth < spotRadius * 4 ||
                          canvasHeight < spotRadius * 4)
                  ) {
                      spotRadius = 0;
                  }
                  if (spotRadius) {
                      // adjust the canvas size as required so that spots will fit
                      hlSpotsEnabled =
                          options.get('highlightSpotColor') &&
                          !options.get('disableInteraction');
                      if (
                          hlSpotsEnabled ||
                          options.get('minSpotColor') ||
                          (options.get('spotColor') &&
                              yvalues[yvallast] === this.miny)
                      ) {
                          canvasHeight -= Math.ceil(spotRadius);
                      }
                      if (
                          hlSpotsEnabled ||
                          options.get('maxSpotColor') ||
                          (options.get('spotColor') &&
                              yvalues[yvallast] === this.maxy)
                      ) {
                          canvasHeight -= Math.ceil(spotRadius);
                          canvasTop += Math.ceil(spotRadius);
                      }
                      if (
                          hlSpotsEnabled ||
                          ((options.get('minSpotColor') ||
                              options.get('maxSpotColor')) &&
                              (yvalues[0] === this.miny ||
                                  yvalues[0] === this.maxy))
                      ) {
                          canvasLeft += Math.ceil(spotRadius);
                          canvasWidth -= Math.ceil(spotRadius);
                      }
                      if (
                          hlSpotsEnabled ||
                          options.get('spotColor') ||
                          options.get('minSpotColor') ||
                          (options.get('maxSpotColor') &&
                              (yvalues[yvallast] === this.miny ||
                                  yvalues[yvallast] === this.maxy))
                      ) {
                          canvasWidth -= Math.ceil(spotRadius);
                      }
                  }
  
                  canvasHeight--;
  
                  if (
                      options.get('normalRangeMin') !== undefined &&
                      !options.get('drawNormalOnTop')
                  ) {
                      this.drawNormalRange(
                          canvasLeft,
                          canvasTop,
                          canvasHeight,
                          canvasWidth,
                          rangey
                      );
                  }
  
                  path = [];
                  paths = [path];
                  last = next = null;
                  yvalcount = yvalues.length;
                  for (i = 0; i < yvalcount; i++) {
                      x = xvalues[i];
                      xnext = xvalues[i + 1];
                      y = yvalues[i];
                      xpos =
                          canvasLeft +
                          Math.round((x - this.minx) * (canvasWidth / rangex));
                      xposnext =
                          i < yvalcount - 1
                              ? canvasLeft +
                                Math.round(
                                    (xnext - this.minx) * (canvasWidth / rangex)
                                )
                              : canvasWidth;
                      next = xpos + (xposnext - xpos) / 2;
                      regionMap[i] = [last || 0, next, i];
                      last = next;
                      if (y === null) {
                          if (i) {
                              if (yvalues[i - 1] !== null) {
                                  path = [];
                                  paths.push(path);
                              }
                              vertices.push(null);
                          }
                      } else {
                          if (y < this.miny) {
                              y = this.miny;
                          }
                          if (y > this.maxy) {
                              y = this.maxy;
                          }
                          if (!path.length) {
                              // previous value was null
                              path.push([xpos, canvasTop + canvasHeight]);
                          }
                          vertex = [
                              xpos,
                              canvasTop +
                                  Math.round(
                                      canvasHeight -
                                          canvasHeight *
                                              ((y - this.miny) / rangey)
                                  ),
                          ];
                          path.push(vertex);
                          vertices.push(vertex);
                      }
                  }
  
                  lineShapes = [];
                  fillShapes = [];
                  plen = paths.length;
                  for (i = 0; i < plen; i++) {
                      path = paths[i];
                      if (path.length) {
                          if (options.get('fillColor')) {
                              path.push([
                                  path[path.length - 1][0],
                                  canvasTop + canvasHeight,
                              ]);
                              fillShapes.push(path.slice(0));
                              path.pop();
                          }
                          // if there's only a single point in this path, then we want to display it
                          // as a vertical line which means we keep path[0]  as is
                          if (path.length > 2) {
                              // else we want the first value
                              path[0] = [path[0][0], path[1][1]];
                          }
                          lineShapes.push(path);
                      }
                  }
  
                  // draw the fill first, then optionally the normal range, then the line on top of that
                  plen = fillShapes.length;
                  for (i = 0; i < plen; i++) {
                      target
                          .drawShape(
                              fillShapes[i],
                              options.get('fillColor'),
                              options.get('fillColor')
                          )
                          .append();
                  }
  
                  if (
                      options.get('normalRangeMin') !== undefined &&
                      options.get('drawNormalOnTop')
                  ) {
                      this.drawNormalRange(
                          canvasLeft,
                          canvasTop,
                          canvasHeight,
                          canvasWidth,
                          rangey
                      );
                  }
  
                  plen = lineShapes.length;
                  for (i = 0; i < plen; i++) {
                      target
                          .drawShape(
                              lineShapes[i],
                              options.get('lineColor'),
                              undefined,
                              options.get('lineWidth')
                          )
                          .append();
                  }
  
                  if (spotRadius && options.get('valueSpots')) {
                      valueSpots = options.get('valueSpots');
                      if (valueSpots.get === undefined) {
                          valueSpots = new RangeMap(valueSpots);
                      }
                      for (i = 0; i < yvalcount; i++) {
                          color = valueSpots.get(yvalues[i]);
                          if (color) {
                              target
                                  .drawCircle(
                                      canvasLeft +
                                          Math.round(
                                              (xvalues[i] - this.minx) *
                                                  (canvasWidth / rangex)
                                          ),
                                      canvasTop +
                                          Math.round(
                                              canvasHeight -
                                                  canvasHeight *
                                                      ((yvalues[i] - this.miny) /
                                                          rangey)
                                          ),
                                      spotRadius,
                                      undefined,
                                      color
                                  )
                                  .append();
                          }
                      }
                  }
                  if (
                      spotRadius &&
                      options.get('spotColor') &&
                      yvalues[yvallast] !== null
                  ) {
                      target
                          .drawCircle(
                              canvasLeft +
                                  Math.round(
                                      (xvalues[xvalues.length - 1] - this.minx) *
                                          (canvasWidth / rangex)
                                  ),
                              canvasTop +
                                  Math.round(
                                      canvasHeight -
                                          canvasHeight *
                                              ((yvalues[yvallast] - this.miny) /
                                                  rangey)
                                  ),
                              spotRadius,
                              undefined,
                              options.get('spotColor')
                          )
                          .append();
                  }
                  if (this.maxy !== this.minyorg) {
                      if (spotRadius && options.get('minSpotColor')) {
                          x = xvalues[$.inArray(this.minyorg, yvalues)];
                          target
                              .drawCircle(
                                  canvasLeft +
                                      Math.round(
                                          (x - this.minx) * (canvasWidth / rangex)
                                      ),
                                  canvasTop +
                                      Math.round(
                                          canvasHeight -
                                              canvasHeight *
                                                  ((this.minyorg - this.miny) /
                                                      rangey)
                                      ),
                                  spotRadius,
                                  undefined,
                                  options.get('minSpotColor')
                              )
                              .append();
                      }
                      if (spotRadius && options.get('maxSpotColor')) {
                          x = xvalues[$.inArray(this.maxyorg, yvalues)];
                          target
                              .drawCircle(
                                  canvasLeft +
                                      Math.round(
                                          (x - this.minx) * (canvasWidth / rangex)
                                      ),
                                  canvasTop +
                                      Math.round(
                                          canvasHeight -
                                              canvasHeight *
                                                  ((this.maxyorg - this.miny) /
                                                      rangey)
                                      ),
                                  spotRadius,
                                  undefined,
                                  options.get('maxSpotColor')
                              )
                              .append();
                      }
                  }
  
                  this.lastShapeId = target.getLastShapeId();
                  this.canvasTop = canvasTop;
                  target.render();
              },
          });
  
          /**
           * Bar charts
           */
          $.fn.sparkline.bar = bar = createClass(
              $.fn.sparkline._base,
              barHighlightMixin,
              {
                  type: 'bar',
  
                  init: function (el, values, options, width, height) {
                      var barWidth = parseInt(options.get('barWidth'), 10),
                          barSpacing = parseInt(options.get('barSpacing'), 10),
                          chartRangeMin = options.get('chartRangeMin'),
                          chartRangeMax = options.get('chartRangeMax'),
                          chartRangeClip = options.get('chartRangeClip'),
                          stackMin = Infinity,
                          stackMax = -Infinity,
                          isStackString,
                          groupMin,
                          groupMax,
                          stackRanges,
                          numValues,
                          i,
                          vlen,
                          range,
                          zeroAxis,
                          xaxisOffset,
                          min,
                          max,
                          clipMin,
                          clipMax,
                          stacked,
                          vlist,
                          j,
                          slen,
                          svals,
                          val,
                          yoffset,
                          yMaxCalc,
                          canvasHeightEf;
                      bar._super.init.call(
                          this,
                          el,
                          values,
                          options,
                          width,
                          height
                      );
  
                      // scan values to determine whether to stack bars
                      for (i = 0, vlen = values.length; i < vlen; i++) {
                          val = values[i];
                          isStackString =
                              typeof val === 'string' && val.indexOf(':') > -1;
                          if (isStackString || $.isArray(val)) {
                              stacked = true;
                              if (isStackString) {
                                  val = values[i] = normalizeValues(
                                      val.split(':')
                                  );
                              }
                              val = remove(val, null); // min/max will treat null as zero
                              groupMin = Math.min.apply(Math, val);
                              groupMax = Math.max.apply(Math, val);
                              if (groupMin < stackMin) {
                                  stackMin = groupMin;
                              }
                              if (groupMax > stackMax) {
                                  stackMax = groupMax;
                              }
                          }
                      }
  
                      this.stacked = stacked;
                      this.regionShapes = {};
                      this.barWidth = barWidth;
                      this.barSpacing = barSpacing;
                      this.totalBarWidth = barWidth + barSpacing;
                      this.width = width =
                          values.length * barWidth +
                          (values.length - 1) * barSpacing;
  
                      this.initTarget();
  
                      if (chartRangeClip) {
                          clipMin =
                              chartRangeMin === undefined
                                  ? -Infinity
                                  : chartRangeMin;
                          clipMax =
                              chartRangeMax === undefined
                                  ? Infinity
                                  : chartRangeMax;
                      }
  
                      numValues = [];
                      stackRanges = stacked ? [] : numValues;
                      var stackTotals = [];
                      var stackRangesNeg = [];
                      for (i = 0, vlen = values.length; i < vlen; i++) {
                          if (stacked) {
                              vlist = values[i];
                              values[i] = svals = [];
                              stackTotals[i] = 0;
                              stackRanges[i] = stackRangesNeg[i] = 0;
                              for (j = 0, slen = vlist.length; j < slen; j++) {
                                  val = svals[j] = chartRangeClip
                                      ? clipval(vlist[j], clipMin, clipMax)
                                      : vlist[j];
                                  if (val !== null) {
                                      if (val > 0) {
                                          stackTotals[i] += val;
                                      }
                                      if (stackMin < 0 && stackMax > 0) {
                                          if (val < 0) {
                                              stackRangesNeg[i] += Math.abs(val);
                                          } else {
                                              stackRanges[i] += val;
                                          }
                                      } else {
                                          stackRanges[i] += Math.abs(
                                              val -
                                                  (val < 0 ? stackMax : stackMin)
                                          );
                                      }
                                      numValues.push(val);
                                  }
                              }
                          } else {
                              val = chartRangeClip
                                  ? clipval(values[i], clipMin, clipMax)
                                  : values[i];
                              val = values[i] = normalizeValue(val);
                              if (val !== null) {
                                  numValues.push(val);
                              }
                          }
                      }
                      this.max = max = Math.max.apply(Math, numValues);
                      this.min = min = Math.min.apply(Math, numValues);
                      this.stackMax = stackMax = stacked
                          ? Math.max.apply(Math, stackTotals)
                          : max;
                      this.stackMin = stackMin = stacked
                          ? Math.min.apply(Math, numValues)
                          : min;
  
                      if (
                          options.get('chartRangeMin') !== undefined &&
                          (options.get('chartRangeClip') ||
                              options.get('chartRangeMin') < min)
                      ) {
                          min = options.get('chartRangeMin');
                      }
                      if (
                          options.get('chartRangeMax') !== undefined &&
                          (options.get('chartRangeClip') ||
                              options.get('chartRangeMax') > max)
                      ) {
                          max = options.get('chartRangeMax');
                      }
  
                      this.zeroAxis = zeroAxis = options.get('zeroAxis', true);
                      if (min <= 0 && max >= 0 && zeroAxis) {
                          xaxisOffset = 0;
                      } else if (zeroAxis == false) {
                          xaxisOffset = min;
                      } else if (min > 0) {
                          xaxisOffset = min;
                      } else {
                          xaxisOffset = max;
                      }
                      this.xaxisOffset = xaxisOffset;
  
                      range = stacked
                          ? Math.max.apply(Math, stackRanges) +
                            Math.max.apply(Math, stackRangesNeg)
                          : max - min;
  
                      // as we plot zero/min values a single pixel line, we add a pixel to all other
                      // values - Reduce the effective canvas size to suit
                      this.canvasHeightEf =
                          zeroAxis && min < 0
                              ? this.canvasHeight - 2
                              : this.canvasHeight - 1;
  
                      if (min < xaxisOffset) {
                          yMaxCalc = stacked && max >= 0 ? stackMax : max;
                          yoffset =
                              ((yMaxCalc - xaxisOffset) / range) *
                              this.canvasHeight;
                          if (yoffset !== Math.ceil(yoffset)) {
                              this.canvasHeightEf -= 2;
                              yoffset = Math.ceil(yoffset);
                          }
                      } else {
                          yoffset = this.canvasHeight;
                      }
                      this.yoffset = yoffset;
  
                      if ($.isArray(options.get('colorMap'))) {
                          this.colorMapByIndex = options.get('colorMap');
                          this.colorMapByValue = null;
                      } else {
                          this.colorMapByIndex = null;
                          this.colorMapByValue = options.get('colorMap');
                          if (
                              this.colorMapByValue &&
                              this.colorMapByValue.get === undefined
                          ) {
                              this.colorMapByValue = new RangeMap(
                                  this.colorMapByValue
                              );
                          }
                      }
  
                      this.range = range;
                  },
  
                  getRegion: function (el, x, y) {
                      var result = Math.floor(x / this.totalBarWidth);
                      return result < 0 || result >= this.values.length
                          ? undefined
                          : result;
                  },
  
                  getCurrentRegionFields: function () {
                      var currentRegion = this.currentRegion,
                          values = ensureArray(this.values[currentRegion]),
                          result = [],
                          value,
                          i;
                      for (i = values.length; i--; ) {
                          value = values[i];
                          result.push({
                              isNull: value === null,
                              value: value,
                              color: this.calcColor(i, value, currentRegion),
                              offset: currentRegion,
                          });
                      }
                      return result;
                  },
  
                  calcColor: function (stacknum, value, valuenum) {
                      var colorMapByIndex = this.colorMapByIndex,
                          colorMapByValue = this.colorMapByValue,
                          options = this.options,
                          color,
                          newColor;
                      if (this.stacked) {
                          color = options.get('stackedBarColor');
                      } else {
                          color =
                              value < 0
                                  ? options.get('negBarColor')
                                  : options.get('barColor');
                      }
                      if (value === 0 && options.get('zeroColor') !== undefined) {
                          color = options.get('zeroColor');
                      }
                      if (
                          colorMapByValue &&
                          (newColor = colorMapByValue.get(value))
                      ) {
                          color = newColor;
                      } else if (
                          colorMapByIndex &&
                          colorMapByIndex.length > valuenum
                      ) {
                          color = colorMapByIndex[valuenum];
                      }
                      return $.isArray(color)
                          ? color[stacknum % color.length]
                          : color;
                  },
  
                  /**
                   * Render bar(s) for a region
                   */
                  renderRegion: function (valuenum, highlight) {
                      var vals = this.values[valuenum],
                          options = this.options,
                          xaxisOffset = this.xaxisOffset,
                          result = [],
                          range = this.range,
                          stacked = this.stacked,
                          target = this.target,
                          x = valuenum * this.totalBarWidth,
                          canvasHeightEf = this.canvasHeightEf,
                          yoffset = this.yoffset,
                          y,
                          height,
                          color,
                          isNull,
                          yoffsetNeg,
                          i,
                          valcount,
                          val,
                          minPlotted,
                          allMin;
  
                      vals = $.isArray(vals) ? vals : [vals];
                      valcount = vals.length;
                      val = vals[0];
                      isNull = all(null, vals);
                      allMin = all(xaxisOffset, vals, true);
  
                      if (isNull) {
                          if (options.get('nullColor')) {
                              color = highlight
                                  ? options.get('nullColor')
                                  : this.calcHighlightColor(
                                        options.get('nullColor'),
                                        options
                                    );
                              y = yoffset > 0 ? yoffset - 1 : yoffset;
                              return target.drawRect(
                                  x,
                                  y,
                                  this.barWidth - 1,
                                  0,
                                  color,
                                  color
                              );
                          } else {
                              return undefined;
                          }
                      }
                      yoffsetNeg = yoffset;
                      for (i = 0; i < valcount; i++) {
                          val = vals[i];
  
                          if (stacked && val === xaxisOffset) {
                              if (!allMin || minPlotted) {
                                  continue;
                              }
                              minPlotted = true;
                          }
  
                          if (range > 0) {
                              height =
                                  Math.floor(
                                      canvasHeightEf *
                                          (Math.abs(val - xaxisOffset) / range)
                                  ) + 1;
                          } else {
                              height = 1;
                          }
                          if (
                              val < xaxisOffset ||
                              (val === xaxisOffset && yoffset === 0)
                          ) {
                              y = yoffsetNeg;
                              yoffsetNeg += height;
                          } else {
                              y = yoffset - height;
                              yoffset -= height;
                          }
                          color = this.calcColor(i, val, valuenum);
                          if (highlight) {
                              color = this.calcHighlightColor(color, options);
                          }
                          result.push(
                              target.drawRect(
                                  x,
                                  y,
                                  this.barWidth - 1,
                                  height - 1,
                                  color,
                                  color
                              )
                          );
                      }
                      if (result.length === 1) {
                          return result[0];
                      }
                      return result;
                  },
              }
          );
  
          /**
           * Tristate charts
           */
          $.fn.sparkline.tristate = tristate = createClass(
              $.fn.sparkline._base,
              barHighlightMixin,
              {
                  type: 'tristate',
  
                  init: function (el, values, options, width, height) {
                      var barWidth = parseInt(options.get('barWidth'), 10),
                          barSpacing = parseInt(options.get('barSpacing'), 10);
                      tristate._super.init.call(
                          this,
                          el,
                          values,
                          options,
                          width,
                          height
                      );
  
                      this.regionShapes = {};
                      this.barWidth = barWidth;
                      this.barSpacing = barSpacing;
                      this.totalBarWidth = barWidth + barSpacing;
                      this.values = $.map(values, Number);
                      this.width = width =
                          values.length * barWidth +
                          (values.length - 1) * barSpacing;
  
                      if ($.isArray(options.get('colorMap'))) {
                          this.colorMapByIndex = options.get('colorMap');
                          this.colorMapByValue = null;
                      } else {
                          this.colorMapByIndex = null;
                          this.colorMapByValue = options.get('colorMap');
                          if (
                              this.colorMapByValue &&
                              this.colorMapByValue.get === undefined
                          ) {
                              this.colorMapByValue = new RangeMap(
                                  this.colorMapByValue
                              );
                          }
                      }
                      this.initTarget();
                  },
  
                  getRegion: function (el, x, y) {
                      return Math.floor(x / this.totalBarWidth);
                  },
  
                  getCurrentRegionFields: function () {
                      var currentRegion = this.currentRegion;
                      return {
                          isNull: this.values[currentRegion] === undefined,
                          value: this.values[currentRegion],
                          color: this.calcColor(
                              this.values[currentRegion],
                              currentRegion
                          ),
                          offset: currentRegion,
                      };
                  },
  
                  calcColor: function (value, valuenum) {
                      var values = this.values,
                          options = this.options,
                          colorMapByIndex = this.colorMapByIndex,
                          colorMapByValue = this.colorMapByValue,
                          color,
                          newColor;
  
                      if (
                          colorMapByValue &&
                          (newColor = colorMapByValue.get(value))
                      ) {
                          color = newColor;
                      } else if (
                          colorMapByIndex &&
                          colorMapByIndex.length > valuenum
                      ) {
                          color = colorMapByIndex[valuenum];
                      } else if (values[valuenum] < 0) {
                          color = options.get('negBarColor');
                      } else if (values[valuenum] > 0) {
                          color = options.get('posBarColor');
                      } else {
                          color = options.get('zeroBarColor');
                      }
                      return color;
                  },
  
                  renderRegion: function (valuenum, highlight) {
                      var values = this.values,
                          options = this.options,
                          target = this.target,
                          canvasHeight,
                          height,
                          halfHeight,
                          x,
                          y,
                          color;
  
                      canvasHeight = target.pixelHeight;
                      halfHeight = Math.round(canvasHeight / 2);
  
                      x = valuenum * this.totalBarWidth;
                      if (values[valuenum] < 0) {
                          y = halfHeight;
                          height = halfHeight - 1;
                      } else if (values[valuenum] > 0) {
                          y = 0;
                          height = halfHeight - 1;
                      } else {
                          y = halfHeight - 1;
                          height = 2;
                      }
                      color = this.calcColor(values[valuenum], valuenum);
                      if (color === null) {
                          return;
                      }
                      if (highlight) {
                          color = this.calcHighlightColor(color, options);
                      }
                      return target.drawRect(
                          x,
                          y,
                          this.barWidth - 1,
                          height - 1,
                          color,
                          color
                      );
                  },
              }
          );
  
          /**
           * Discrete charts
           */
          $.fn.sparkline.discrete = discrete = createClass(
              $.fn.sparkline._base,
              barHighlightMixin,
              {
                  type: 'discrete',
  
                  init: function (el, values, options, width, height) {
                      discrete._super.init.call(
                          this,
                          el,
                          values,
                          options,
                          width,
                          height
                      );
  
                      this.regionShapes = {};
                      this.values = values = $.map(values, Number);
                      this.min = Math.min.apply(Math, values);
                      this.max = Math.max.apply(Math, values);
                      this.range = this.max - this.min;
                      this.width = width =
                          options.get('width') === 'auto'
                              ? values.length * 2
                              : this.width;
                      this.interval = Math.floor(width / values.length);
                      this.itemWidth = width / values.length;
                      if (
                          options.get('chartRangeMin') !== undefined &&
                          (options.get('chartRangeClip') ||
                              options.get('chartRangeMin') < this.min)
                      ) {
                          this.min = options.get('chartRangeMin');
                      }
                      if (
                          options.get('chartRangeMax') !== undefined &&
                          (options.get('chartRangeClip') ||
                              options.get('chartRangeMax') > this.max)
                      ) {
                          this.max = options.get('chartRangeMax');
                      }
                      this.initTarget();
                      if (this.target) {
                          this.lineHeight =
                              options.get('lineHeight') === 'auto'
                                  ? Math.round(this.canvasHeight * 0.3)
                                  : options.get('lineHeight');
                      }
                  },
  
                  getRegion: function (el, x, y) {
                      return Math.floor(x / this.itemWidth);
                  },
  
                  getCurrentRegionFields: function () {
                      var currentRegion = this.currentRegion;
                      return {
                          isNull: this.values[currentRegion] === undefined,
                          value: this.values[currentRegion],
                          offset: currentRegion,
                      };
                  },
  
                  renderRegion: function (valuenum, highlight) {
                      var values = this.values,
                          options = this.options,
                          min = this.min,
                          max = this.max,
                          range = this.range,
                          interval = this.interval,
                          target = this.target,
                          canvasHeight = this.canvasHeight,
                          lineHeight = this.lineHeight,
                          pheight = canvasHeight - lineHeight,
                          ytop,
                          val,
                          color,
                          x;
  
                      val = clipval(values[valuenum], min, max);
                      x = valuenum * interval;
                      ytop = Math.round(
                          pheight - pheight * ((val - min) / range)
                      );
                      color =
                          options.get('thresholdColor') &&
                          val < options.get('thresholdValue')
                              ? options.get('thresholdColor')
                              : options.get('lineColor');
                      if (highlight) {
                          color = this.calcHighlightColor(color, options);
                      }
                      return target.drawLine(
                          x,
                          ytop,
                          x,
                          ytop + lineHeight,
                          color
                      );
                  },
              }
          );
  
          /**
           * Bullet charts
           */
          $.fn.sparkline.bullet = bullet = createClass($.fn.sparkline._base, {
              type: 'bullet',
  
              init: function (el, values, options, width, height) {
                  var min, max, vals;
                  bullet._super.init.call(
                      this,
                      el,
                      values,
                      options,
                      width,
                      height
                  );
  
                  // values: target, performance, range1, range2, range3
                  this.values = values = normalizeValues(values);
                  // target or performance could be null
                  vals = values.slice();
                  vals[0] = vals[0] === null ? vals[2] : vals[0];
                  vals[1] = values[1] === null ? vals[2] : vals[1];
                  min = Math.min.apply(Math, values);
                  max = Math.max.apply(Math, values);
                  if (options.get('base') === undefined) {
                      min = min < 0 ? min : 0;
                  } else {
                      min = options.get('base');
                  }
                  this.min = min;
                  this.max = max;
                  this.range = max - min;
                  this.shapes = {};
                  this.valueShapes = {};
                  this.regiondata = {};
                  this.width = width =
                      options.get('width') === 'auto' ? '4.0em' : width;
                  this.target = this.$el.simpledraw(
                      width,
                      height,
                      options.get('composite')
                  );
                  if (!values.length) {
                      this.disabled = true;
                  }
                  this.initTarget();
              },
  
              getRegion: function (el, x, y) {
                  var shapeid = this.target.getShapeAt(el, x, y);
                  return shapeid !== undefined &&
                      this.shapes[shapeid] !== undefined
                      ? this.shapes[shapeid]
                      : undefined;
              },
  
              getCurrentRegionFields: function () {
                  var currentRegion = this.currentRegion;
                  return {
                      fieldkey: currentRegion.substr(0, 1),
                      value: this.values[currentRegion.substr(1)],
                      region: currentRegion,
                  };
              },
  
              changeHighlight: function (highlight) {
                  var currentRegion = this.currentRegion,
                      shapeid = this.valueShapes[currentRegion],
                      shape;
                  delete this.shapes[shapeid];
                  switch (currentRegion.substr(0, 1)) {
                      case 'r':
                          shape = this.renderRange(
                              currentRegion.substr(1),
                              highlight
                          );
                          break;
                      case 'p':
                          shape = this.renderPerformance(highlight);
                          break;
                      case 't':
                          shape = this.renderTarget(highlight);
                          break;
                  }
                  this.valueShapes[currentRegion] = shape.id;
                  this.shapes[shape.id] = currentRegion;
                  this.target.replaceWithShape(shapeid, shape);
              },
  
              renderRange: function (rn, highlight) {
                  var rangeval = this.values[rn],
                      rangewidth = Math.round(
                          this.canvasWidth * ((rangeval - this.min) / this.range)
                      ),
                      color = this.options.get('rangeColors')[rn - 2];
                  if (highlight) {
                      color = this.calcHighlightColor(color, this.options);
                  }
                  return this.target.drawRect(
                      0,
                      0,
                      rangewidth - 1,
                      this.canvasHeight - 1,
                      color,
                      color
                  );
              },
  
              renderPerformance: function (highlight) {
                  var perfval = this.values[1],
                      perfwidth = Math.round(
                          this.canvasWidth * ((perfval - this.min) / this.range)
                      ),
                      color = this.options.get('performanceColor');
                  if (highlight) {
                      color = this.calcHighlightColor(color, this.options);
                  }
                  return this.target.drawRect(
                      0,
                      Math.round(this.canvasHeight * 0.3),
                      perfwidth - 1,
                      Math.round(this.canvasHeight * 0.4) - 1,
                      color,
                      color
                  );
              },
  
              renderTarget: function (highlight) {
                  var targetval = this.values[0],
                      x = Math.round(
                          this.canvasWidth *
                              ((targetval - this.min) / this.range) -
                              this.options.get('targetWidth') / 2
                      ),
                      targettop = Math.round(this.canvasHeight * 0.1),
                      targetheight = this.canvasHeight - targettop * 2,
                      color = this.options.get('targetColor');
                  if (highlight) {
                      color = this.calcHighlightColor(color, this.options);
                  }
                  return this.target.drawRect(
                      x,
                      targettop,
                      this.options.get('targetWidth') - 1,
                      targetheight - 1,
                      color,
                      color
                  );
              },
  
              render: function () {
                  var vlen = this.values.length,
                      target = this.target,
                      i,
                      shape;
                  if (!bullet._super.render.call(this)) {
                      return;
                  }
                  for (i = 2; i < vlen; i++) {
                      shape = this.renderRange(i).append();
                      this.shapes[shape.id] = 'r' + i;
                      this.valueShapes['r' + i] = shape.id;
                  }
                  if (this.values[1] !== null) {
                      shape = this.renderPerformance().append();
                      this.shapes[shape.id] = 'p1';
                      this.valueShapes.p1 = shape.id;
                  }
                  if (this.values[0] !== null) {
                      shape = this.renderTarget().append();
                      this.shapes[shape.id] = 't0';
                      this.valueShapes.t0 = shape.id;
                  }
                  target.render();
              },
          });
  
          /**
           * Pie charts
           */
          $.fn.sparkline.pie = pie = createClass($.fn.sparkline._base, {
              type: 'pie',
  
              init: function (el, values, options, width, height) {
                  var total = 0,
                      i;
  
                  pie._super.init.call(this, el, values, options, width, height);
  
                  this.shapes = {}; // map shape ids to value offsets
                  this.valueShapes = {}; // maps value offsets to shape ids
                  this.values = values = $.map(values, Number);
  
                  if (options.get('width') === 'auto') {
                      this.width = this.height;
                  }
  
                  if (values.length > 0) {
                      for (i = values.length; i--; ) {
                          total += values[i];
                      }
                  }
                  this.total = total;
                  this.initTarget();
                  this.radius = Math.floor(
                      Math.min(this.canvasWidth, this.canvasHeight) / 2
                  );
              },
  
              getRegion: function (el, x, y) {
                  var shapeid = this.target.getShapeAt(el, x, y);
                  return shapeid !== undefined &&
                      this.shapes[shapeid] !== undefined
                      ? this.shapes[shapeid]
                      : undefined;
              },
  
              getCurrentRegionFields: function () {
                  var currentRegion = this.currentRegion;
                  return {
                      isNull: this.values[currentRegion] === undefined,
                      value: this.values[currentRegion],
                      percent: (this.values[currentRegion] / this.total) * 100,
                      color: this.options.get('sliceColors')[
                          currentRegion % this.options.get('sliceColors').length
                      ],
                      offset: currentRegion,
                  };
              },
  
              changeHighlight: function (highlight) {
                  var currentRegion = this.currentRegion,
                      newslice = this.renderSlice(currentRegion, highlight),
                      shapeid = this.valueShapes[currentRegion];
                  delete this.shapes[shapeid];
                  this.target.replaceWithShape(shapeid, newslice);
                  this.valueShapes[currentRegion] = newslice.id;
                  this.shapes[newslice.id] = currentRegion;
              },
  
              renderSlice: function (valuenum, highlight) {
                  var target = this.target,
                      options = this.options,
                      radius = this.radius,
                      borderWidth = options.get('borderWidth'),
                      offset = options.get('offset'),
                      circle = 2 * Math.PI,
                      values = this.values,
                      total = this.total,
                      next = offset ? 2 * Math.PI * (offset / 360) : 0,
                      start,
                      end,
                      i,
                      vlen,
                      color;
  
                  vlen = values.length;
                  for (i = 0; i < vlen; i++) {
                      start = next;
                      end = next;
                      if (total > 0) {
                          // avoid divide by zero
                          end = next + circle * (values[i] / total);
                      }
                      if (valuenum === i) {
                          color = options.get('sliceColors')[
                              i % options.get('sliceColors').length
                          ];
                          if (highlight) {
                              color = this.calcHighlightColor(color, options);
                          }
  
                          return target.drawPieSlice(
                              radius,
                              radius,
                              radius - borderWidth,
                              start,
                              end,
                              undefined,
                              color
                          );
                      }
                      next = end;
                  }
              },
  
              render: function () {
                  var target = this.target,
                      values = this.values,
                      options = this.options,
                      radius = this.radius,
                      borderWidth = options.get('borderWidth'),
                      shape,
                      i;
  
                  if (!pie._super.render.call(this)) {
                      return;
                  }
                  if (borderWidth) {
                      target
                          .drawCircle(
                              radius,
                              radius,
                              Math.floor(radius - borderWidth / 2),
                              options.get('borderColor'),
                              undefined,
                              borderWidth
                          )
                          .append();
                  }
                  for (i = values.length; i--; ) {
                      if (values[i]) {
                          // don't render zero values
                          shape = this.renderSlice(i).append();
                          this.valueShapes[i] = shape.id; // store just the shapeid
                          this.shapes[shape.id] = i;
                      }
                  }
                  target.render();
              },
          });
  
          /**
           * Box plots
           */
          $.fn.sparkline.box = box = createClass($.fn.sparkline._base, {
              type: 'box',
  
              init: function (el, values, options, width, height) {
                  box._super.init.call(this, el, values, options, width, height);
                  this.values = $.map(values, Number);
                  this.width = options.get('width') === 'auto' ? '4.0em' : width;
                  this.initTarget();
                  if (!this.values.length) {
                      this.disabled = 1;
                  }
              },
  
              /**
               * Simulate a single region
               */
              getRegion: function () {
                  return 1;
              },
  
              getCurrentRegionFields: function () {
                  var result = [
                      { field: 'lq', value: this.quartiles[0] },
                      { field: 'med', value: this.quartiles[1] },
                      { field: 'uq', value: this.quartiles[2] },
                  ];
                  if (this.loutlier !== undefined) {
                      result.push({ field: 'lo', value: this.loutlier });
                  }
                  if (this.routlier !== undefined) {
                      result.push({ field: 'ro', value: this.routlier });
                  }
                  if (this.lwhisker !== undefined) {
                      result.push({ field: 'lw', value: this.lwhisker });
                  }
                  if (this.rwhisker !== undefined) {
                      result.push({ field: 'rw', value: this.rwhisker });
                  }
                  return result;
              },
  
              render: function () {
                  var target = this.target,
                      values = this.values,
                      vlen = values.length,
                      options = this.options,
                      canvasWidth = this.canvasWidth,
                      canvasHeight = this.canvasHeight,
                      minValue =
                          options.get('chartRangeMin') === undefined
                              ? Math.min.apply(Math, values)
                              : options.get('chartRangeMin'),
                      maxValue =
                          options.get('chartRangeMax') === undefined
                              ? Math.max.apply(Math, values)
                              : options.get('chartRangeMax'),
                      canvasLeft = 0,
                      lwhisker,
                      loutlier,
                      iqr,
                      q1,
                      q2,
                      q3,
                      rwhisker,
                      routlier,
                      i,
                      size,
                      unitSize;
  
                  if (!box._super.render.call(this)) {
                      return;
                  }
  
                  if (options.get('raw')) {
                      if (options.get('showOutliers') && values.length > 5) {
                          loutlier = values[0];
                          lwhisker = values[1];
                          q1 = values[2];
                          q2 = values[3];
                          q3 = values[4];
                          rwhisker = values[5];
                          routlier = values[6];
                      } else {
                          lwhisker = values[0];
                          q1 = values[1];
                          q2 = values[2];
                          q3 = values[3];
                          rwhisker = values[4];
                      }
                  } else {
                      values.sort(function (a, b) {
                          return a - b;
                      });
                      q1 = quartile(values, 1);
                      q2 = quartile(values, 2);
                      q3 = quartile(values, 3);
                      iqr = q3 - q1;
                      if (options.get('showOutliers')) {
                          lwhisker = rwhisker = undefined;
                          for (i = 0; i < vlen; i++) {
                              if (
                                  lwhisker === undefined &&
                                  values[i] > q1 - iqr * options.get('outlierIQR')
                              ) {
                                  lwhisker = values[i];
                              }
                              if (
                                  values[i] <
                                  q3 + iqr * options.get('outlierIQR')
                              ) {
                                  rwhisker = values[i];
                              }
                          }
                          loutlier = values[0];
                          routlier = values[vlen - 1];
                      } else {
                          lwhisker = values[0];
                          rwhisker = values[vlen - 1];
                      }
                  }
                  this.quartiles = [q1, q2, q3];
                  this.lwhisker = lwhisker;
                  this.rwhisker = rwhisker;
                  this.loutlier = loutlier;
                  this.routlier = routlier;
  
                  unitSize = canvasWidth / (maxValue - minValue + 1);
                  if (options.get('showOutliers')) {
                      canvasLeft = Math.ceil(options.get('spotRadius'));
                      canvasWidth -= 2 * Math.ceil(options.get('spotRadius'));
                      unitSize = canvasWidth / (maxValue - minValue + 1);
                      if (loutlier < lwhisker) {
                          target
                              .drawCircle(
                                  (loutlier - minValue) * unitSize + canvasLeft,
                                  canvasHeight / 2,
                                  options.get('spotRadius'),
                                  options.get('outlierLineColor'),
                                  options.get('outlierFillColor')
                              )
                              .append();
                      }
                      if (routlier > rwhisker) {
                          target
                              .drawCircle(
                                  (routlier - minValue) * unitSize + canvasLeft,
                                  canvasHeight / 2,
                                  options.get('spotRadius'),
                                  options.get('outlierLineColor'),
                                  options.get('outlierFillColor')
                              )
                              .append();
                      }
                  }
  
                  // box
                  target
                      .drawRect(
                          Math.round((q1 - minValue) * unitSize + canvasLeft),
                          Math.round(canvasHeight * 0.1),
                          Math.round((q3 - q1) * unitSize),
                          Math.round(canvasHeight * 0.8),
                          options.get('boxLineColor'),
                          options.get('boxFillColor')
                      )
                      .append();
                  // left whisker
                  target
                      .drawLine(
                          Math.round(
                              (lwhisker - minValue) * unitSize + canvasLeft
                          ),
                          Math.round(canvasHeight / 2),
                          Math.round((q1 - minValue) * unitSize + canvasLeft),
                          Math.round(canvasHeight / 2),
                          options.get('lineColor')
                      )
                      .append();
                  target
                      .drawLine(
                          Math.round(
                              (lwhisker - minValue) * unitSize + canvasLeft
                          ),
                          Math.round(canvasHeight / 4),
                          Math.round(
                              (lwhisker - minValue) * unitSize + canvasLeft
                          ),
                          Math.round(canvasHeight - canvasHeight / 4),
                          options.get('whiskerColor')
                      )
                      .append();
                  // right whisker
                  target
                      .drawLine(
                          Math.round(
                              (rwhisker - minValue) * unitSize + canvasLeft
                          ),
                          Math.round(canvasHeight / 2),
                          Math.round((q3 - minValue) * unitSize + canvasLeft),
                          Math.round(canvasHeight / 2),
                          options.get('lineColor')
                      )
                      .append();
                  target
                      .drawLine(
                          Math.round(
                              (rwhisker - minValue) * unitSize + canvasLeft
                          ),
                          Math.round(canvasHeight / 4),
                          Math.round(
                              (rwhisker - minValue) * unitSize + canvasLeft
                          ),
                          Math.round(canvasHeight - canvasHeight / 4),
                          options.get('whiskerColor')
                      )
                      .append();
                  // median line
                  target
                      .drawLine(
                          Math.round((q2 - minValue) * unitSize + canvasLeft),
                          Math.round(canvasHeight * 0.1),
                          Math.round((q2 - minValue) * unitSize + canvasLeft),
                          Math.round(canvasHeight * 0.9),
                          options.get('medianColor')
                      )
                      .append();
                  if (options.get('target')) {
                      size = Math.ceil(options.get('spotRadius'));
                      target
                          .drawLine(
                              Math.round(
                                  (options.get('target') - minValue) * unitSize +
                                      canvasLeft
                              ),
                              Math.round(canvasHeight / 2 - size),
                              Math.round(
                                  (options.get('target') - minValue) * unitSize +
                                      canvasLeft
                              ),
                              Math.round(canvasHeight / 2 + size),
                              options.get('targetColor')
                          )
                          .append();
                      target
                          .drawLine(
                              Math.round(
                                  (options.get('target') - minValue) * unitSize +
                                      canvasLeft -
                                      size
                              ),
                              Math.round(canvasHeight / 2),
                              Math.round(
                                  (options.get('target') - minValue) * unitSize +
                                      canvasLeft +
                                      size
                              ),
                              Math.round(canvasHeight / 2),
                              options.get('targetColor')
                          )
                          .append();
                  }
                  target.render();
              },
          });
  
          // Setup a very simple "virtual canvas" to make drawing the few shapes we need easier
          // This is accessible as $(foo).simpledraw()
  
          VShape = createClass({
              init: function (target, id, type, args) {
                  this.target = target;
                  this.id = id;
                  this.type = type;
                  this.args = args;
              },
              append: function () {
                  this.target.appendShape(this);
                  return this;
              },
          });
  
          VCanvas_base = createClass({
              _pxregex: /(\d+)(px)?\s*$/i,
  
              init: function (width, height, target) {
                  if (!width) {
                      return;
                  }
                  this.width = width;
                  this.height = height;
                  this.target = target;
                  this.lastShapeId = null;
                  if (target[0]) {
                      target = target[0];
                  }
                  $.data(target, '_jqs_vcanvas', this);
              },
  
              drawLine: function (x1, y1, x2, y2, lineColor, lineWidth) {
                  return this.drawShape(
                      [
                          [x1, y1],
                          [x2, y2],
                      ],
                      lineColor,
                      lineWidth
                  );
              },
  
              drawShape: function (path, lineColor, fillColor, lineWidth) {
                  return this._genShape('Shape', [
                      path,
                      lineColor,
                      fillColor,
                      lineWidth,
                  ]);
              },
  
              drawCircle: function (
                  x,
                  y,
                  radius,
                  lineColor,
                  fillColor,
                  lineWidth
              ) {
                  return this._genShape('Circle', [
                      x,
                      y,
                      radius,
                      lineColor,
                      fillColor,
                      lineWidth,
                  ]);
              },
  
              drawPieSlice: function (
                  x,
                  y,
                  radius,
                  startAngle,
                  endAngle,
                  lineColor,
                  fillColor
              ) {
                  return this._genShape('PieSlice', [
                      x,
                      y,
                      radius,
                      startAngle,
                      endAngle,
                      lineColor,
                      fillColor,
                  ]);
              },
  
              drawRect: function (x, y, width, height, lineColor, fillColor) {
                  return this._genShape('Rect', [
                      x,
                      y,
                      width,
                      height,
                      lineColor,
                      fillColor,
                  ]);
              },
  
              getElement: function () {
                  return this.canvas;
              },
  
              /**
               * Return the most recently inserted shape id
               */
              getLastShapeId: function () {
                  return this.lastShapeId;
              },
  
              /**
               * Clear and reset the canvas
               */
              reset: function () {
                  alert('reset not implemented');
              },
  
              _insert: function (el, target) {
                  $(target).html(el);
              },
  
              /**
               * Calculate the pixel dimensions of the canvas
               */
              _calculatePixelDims: function (width, height, canvas) {
                  // XXX This should probably be a configurable option
                  var match;
                  match = this._pxregex.exec(height);
                  if (match) {
                      this.pixelHeight = match[1];
                  } else {
                      this.pixelHeight = $(canvas).height();
                  }
                  match = this._pxregex.exec(width);
                  if (match) {
                      this.pixelWidth = match[1];
                  } else {
                      this.pixelWidth = $(canvas).width();
                  }
              },
  
              /**
               * Generate a shape object and id for later rendering
               */
              _genShape: function (shapetype, shapeargs) {
                  var id = shapeCount++;
                  shapeargs.unshift(id);
                  return new VShape(this, id, shapetype, shapeargs);
              },
  
              /**
               * Add a shape to the end of the render queue
               */
              appendShape: function (shape) {
                  alert('appendShape not implemented');
              },
  
              /**
               * Replace one shape with another
               */
              replaceWithShape: function (shapeid, shape) {
                  alert('replaceWithShape not implemented');
              },
  
              /**
               * Insert one shape after another in the render queue
               */
              insertAfterShape: function (shapeid, shape) {
                  alert('insertAfterShape not implemented');
              },
  
              /**
               * Remove a shape from the queue
               */
              removeShapeId: function (shapeid) {
                  alert('removeShapeId not implemented');
              },
  
              /**
               * Find a shape at the specified x/y co-ordinates
               */
              getShapeAt: function (el, x, y) {
                  alert('getShapeAt not implemented');
              },
  
              /**
               * Render all queued shapes onto the canvas
               */
              render: function () {
                  alert('render not implemented');
              },
          });
  
          VCanvas_canvas = createClass(VCanvas_base, {
              init: function (width, height, target, interact) {
                  VCanvas_canvas._super.init.call(this, width, height, target);
                  this.canvas = document.createElement('canvas');
                  if (target[0]) {
                      target = target[0];
                  }
                  $.data(target, '_jqs_vcanvas', this);
                  $(this.canvas).css({
                      display: 'inline-block',
                      width: width,
                      height: height,
                      verticalAlign: 'top',
                  });
                  this._insert(this.canvas, target);
                  this._calculatePixelDims(width, height, this.canvas);
                  this.canvas.width = this.pixelWidth;
                  this.canvas.height = this.pixelHeight;
                  this.interact = interact;
                  this.shapes = {};
                  this.shapeseq = [];
                  this.currentTargetShapeId = undefined;
                  $(this.canvas).css({
                      width: this.pixelWidth,
                      height: this.pixelHeight,
                  });
              },
  
              _getContext: function (lineColor, fillColor, lineWidth) {
                  var context = this.canvas.getContext('2d');
                  if (lineColor !== undefined) {
                      context.strokeStyle = lineColor;
                  }
                  context.lineWidth = lineWidth === undefined ? 1 : lineWidth;
                  if (fillColor !== undefined) {
                      context.fillStyle = fillColor;
                  }
                  return context;
              },
  
              reset: function () {
                  var context = this._getContext();
                  context.clearRect(0, 0, this.pixelWidth, this.pixelHeight);
                  this.shapes = {};
                  this.shapeseq = [];
                  this.currentTargetShapeId = undefined;
              },
  
              _drawShape: function (
                  shapeid,
                  path,
                  lineColor,
                  fillColor,
                  lineWidth
              ) {
                  var context = this._getContext(lineColor, fillColor, lineWidth),
                      i,
                      plen;
                  context.beginPath();
                  context.moveTo(path[0][0] + 0.5, path[0][1] + 0.5);
                  for (i = 1, plen = path.length; i < plen; i++) {
                      context.lineTo(path[i][0] + 0.5, path[i][1] + 0.5); // the 0.5 offset gives us crisp pixel-width lines
                  }
                  if (lineColor !== undefined) {
                      context.stroke();
                  }
                  if (fillColor !== undefined) {
                      context.fill();
                  }
                  if (
                      this.targetX !== undefined &&
                      this.targetY !== undefined &&
                      context.isPointInPath(this.targetX, this.targetY)
                  ) {
                      this.currentTargetShapeId = shapeid;
                  }
              },
  
              _drawCircle: function (
                  shapeid,
                  x,
                  y,
                  radius,
                  lineColor,
                  fillColor,
                  lineWidth
              ) {
                  var context = this._getContext(lineColor, fillColor, lineWidth);
                  context.beginPath();
                  context.arc(x, y, radius, 0, 2 * Math.PI, false);
                  if (
                      this.targetX !== undefined &&
                      this.targetY !== undefined &&
                      context.isPointInPath(this.targetX, this.targetY)
                  ) {
                      this.currentTargetShapeId = shapeid;
                  }
                  if (lineColor !== undefined) {
                      context.stroke();
                  }
                  if (fillColor !== undefined) {
                      context.fill();
                  }
              },
  
              _drawPieSlice: function (
                  shapeid,
                  x,
                  y,
                  radius,
                  startAngle,
                  endAngle,
                  lineColor,
                  fillColor
              ) {
                  var context = this._getContext(lineColor, fillColor);
                  context.beginPath();
                  context.moveTo(x, y);
                  context.arc(x, y, radius, startAngle, endAngle, false);
                  context.lineTo(x, y);
                  context.closePath();
                  if (lineColor !== undefined) {
                      context.stroke();
                  }
                  if (fillColor) {
                      context.fill();
                  }
                  if (
                      this.targetX !== undefined &&
                      this.targetY !== undefined &&
                      context.isPointInPath(this.targetX, this.targetY)
                  ) {
                      this.currentTargetShapeId = shapeid;
                  }
              },
  
              _drawRect: function (
                  shapeid,
                  x,
                  y,
                  width,
                  height,
                  lineColor,
                  fillColor
              ) {
                  return this._drawShape(
                      shapeid,
                      [
                          [x, y],
                          [x + width, y],
                          [x + width, y + height],
                          [x, y + height],
                          [x, y],
                      ],
                      lineColor,
                      fillColor
                  );
              },
  
              appendShape: function (shape) {
                  this.shapes[shape.id] = shape;
                  this.shapeseq.push(shape.id);
                  this.lastShapeId = shape.id;
                  return shape.id;
              },
  
              replaceWithShape: function (shapeid, shape) {
                  var shapeseq = this.shapeseq,
                      i;
                  this.shapes[shape.id] = shape;
                  for (i = shapeseq.length; i--; ) {
                      if (shapeseq[i] == shapeid) {
                          shapeseq[i] = shape.id;
                      }
                  }
                  delete this.shapes[shapeid];
              },
  
              replaceWithShapes: function (shapeids, shapes) {
                  var shapeseq = this.shapeseq,
                      shapemap = {},
                      sid,
                      i,
                      first;
  
                  for (i = shapeids.length; i--; ) {
                      shapemap[shapeids[i]] = true;
                  }
                  for (i = shapeseq.length; i--; ) {
                      sid = shapeseq[i];
                      if (shapemap[sid]) {
                          shapeseq.splice(i, 1);
                          delete this.shapes[sid];
                          first = i;
                      }
                  }
                  for (i = shapes.length; i--; ) {
                      shapeseq.splice(first, 0, shapes[i].id);
                      this.shapes[shapes[i].id] = shapes[i];
                  }
              },
  
              insertAfterShape: function (shapeid, shape) {
                  var shapeseq = this.shapeseq,
                      i;
                  for (i = shapeseq.length; i--; ) {
                      if (shapeseq[i] === shapeid) {
                          shapeseq.splice(i + 1, 0, shape.id);
                          this.shapes[shape.id] = shape;
                          return;
                      }
                  }
              },
  
              removeShapeId: function (shapeid) {
                  var shapeseq = this.shapeseq,
                      i;
                  for (i = shapeseq.length; i--; ) {
                      if (shapeseq[i] === shapeid) {
                          shapeseq.splice(i, 1);
                          break;
                      }
                  }
                  delete this.shapes[shapeid];
              },
  
              getShapeAt: function (el, x, y) {
                  this.targetX = x;
                  this.targetY = y;
                  this.render();
                  return this.currentTargetShapeId;
              },
  
              render: function () {
                  var shapeseq = this.shapeseq,
                      shapes = this.shapes,
                      shapeCount = shapeseq.length,
                      context = this._getContext(),
                      shapeid,
                      shape,
                      i;
                  context.clearRect(0, 0, this.pixelWidth, this.pixelHeight);
                  for (i = 0; i < shapeCount; i++) {
                      shapeid = shapeseq[i];
                      shape = shapes[shapeid];
                      this['_draw' + shape.type].apply(this, shape.args);
                  }
                  if (!this.interact) {
                      // not interactive so no need to keep the shapes array
                      this.shapes = {};
                      this.shapeseq = [];
                  }
              },
          });
  
          VCanvas_vml = createClass(VCanvas_base, {
              init: function (width, height, target) {
                  var groupel;
                  VCanvas_vml._super.init.call(this, width, height, target);
                  if (target[0]) {
                      target = target[0];
                  }
                  $.data(target, '_jqs_vcanvas', this);
                  this.canvas = document.createElement('span');
                  $(this.canvas).css({
                      display: 'inline-block',
                      position: 'relative',
                      overflow: 'hidden',
                      width: width,
                      height: height,
                      margin: '0px',
                      padding: '0px',
                      verticalAlign: 'top',
                  });
                  this._insert(this.canvas, target);
                  this._calculatePixelDims(width, height, this.canvas);
                  this.canvas.width = this.pixelWidth;
                  this.canvas.height = this.pixelHeight;
                  groupel =
                      '<v:group coordorigin="0 0" coordsize="' +
                      this.pixelWidth +
                      ' ' +
                      this.pixelHeight +
                      '"' +
                      ' style="position:absolute;top:0;left:0;width:' +
                      this.pixelWidth +
                      'px;height=' +
                      this.pixelHeight +
                      'px;"></v:group>';
                  this.canvas.insertAdjacentHTML('beforeEnd', groupel);
                  this.group = $(this.canvas).children()[0];
                  this.rendered = false;
                  this.prerender = '';
              },
  
              _drawShape: function (
                  shapeid,
                  path,
                  lineColor,
                  fillColor,
                  lineWidth
              ) {
                  var vpath = [],
                      initial,
                      stroke,
                      fill,
                      closed,
                      vel,
                      plen,
                      i;
                  for (i = 0, plen = path.length; i < plen; i++) {
                      vpath[i] = '' + path[i][0] + ',' + path[i][1];
                  }
                  initial = vpath.splice(0, 1);
                  lineWidth = lineWidth === undefined ? 1 : lineWidth;
                  stroke =
                      lineColor === undefined
                          ? ' stroked="false" '
                          : ' strokeWeight="' +
                            lineWidth +
                            'px" strokeColor="' +
                            lineColor +
                            '" ';
                  fill =
                      fillColor === undefined
                          ? ' filled="false"'
                          : ' fillColor="' + fillColor + '" filled="true" ';
                  closed = vpath[0] === vpath[vpath.length - 1] ? 'x ' : '';
                  vel =
                      '<v:shape coordorigin="0 0" coordsize="' +
                      this.pixelWidth +
                      ' ' +
                      this.pixelHeight +
                      '" ' +
                      ' id="jqsshape' +
                      shapeid +
                      '" ' +
                      stroke +
                      fill +
                      ' style="position:absolute;left:0px;top:0px;height:' +
                      this.pixelHeight +
                      'px;width:' +
                      this.pixelWidth +
                      'px;padding:0px;margin:0px;" ' +
                      ' path="m ' +
                      initial +
                      ' l ' +
                      vpath.join(', ') +
                      ' ' +
                      closed +
                      'e">' +
                      ' </v:shape>';
                  return vel;
              },
  
              _drawCircle: function (
                  shapeid,
                  x,
                  y,
                  radius,
                  lineColor,
                  fillColor,
                  lineWidth
              ) {
                  var stroke, fill, vel;
                  x -= radius;
                  y -= radius;
                  stroke =
                      lineColor === undefined
                          ? ' stroked="false" '
                          : ' strokeWeight="' +
                            lineWidth +
                            'px" strokeColor="' +
                            lineColor +
                            '" ';
                  fill =
                      fillColor === undefined
                          ? ' filled="false"'
                          : ' fillColor="' + fillColor + '" filled="true" ';
                  vel =
                      '<v:oval ' +
                      ' id="jqsshape' +
                      shapeid +
                      '" ' +
                      stroke +
                      fill +
                      ' style="position:absolute;top:' +
                      y +
                      'px; left:' +
                      x +
                      'px; width:' +
                      radius * 2 +
                      'px; height:' +
                      radius * 2 +
                      'px"></v:oval>';
                  return vel;
              },
  
              _drawPieSlice: function (
                  shapeid,
                  x,
                  y,
                  radius,
                  startAngle,
                  endAngle,
                  lineColor,
                  fillColor
              ) {
                  var vpath, startx, starty, endx, endy, stroke, fill, vel;
                  if (startAngle === endAngle) {
                      return ''; // VML seems to have problem when start angle equals end angle.
                  }
                  if (endAngle - startAngle === 2 * Math.PI) {
                      startAngle = 0.0; // VML seems to have a problem when drawing a full circle that doesn't start 0
                      endAngle = 2 * Math.PI;
                  }
  
                  startx = x + Math.round(Math.cos(startAngle) * radius);
                  starty = y + Math.round(Math.sin(startAngle) * radius);
                  endx = x + Math.round(Math.cos(endAngle) * radius);
                  endy = y + Math.round(Math.sin(endAngle) * radius);
  
                  if (startx === endx && starty === endy) {
                      if (endAngle - startAngle < Math.PI) {
                          // Prevent very small slices from being mistaken as a whole pie
                          return '';
                      }
                      // essentially going to be the entire circle, so ignore startAngle
                      startx = endx = x + radius;
                      starty = endy = y;
                  }
  
                  if (
                      startx === endx &&
                      starty === endy &&
                      endAngle - startAngle < Math.PI
                  ) {
                      return '';
                  }
  
                  vpath = [
                      x - radius,
                      y - radius,
                      x + radius,
                      y + radius,
                      startx,
                      starty,
                      endx,
                      endy,
                  ];
                  stroke =
                      lineColor === undefined
                          ? ' stroked="false" '
                          : ' strokeWeight="1px" strokeColor="' +
                            lineColor +
                            '" ';
                  fill =
                      fillColor === undefined
                          ? ' filled="false"'
                          : ' fillColor="' + fillColor + '" filled="true" ';
                  vel =
                      '<v:shape coordorigin="0 0" coordsize="' +
                      this.pixelWidth +
                      ' ' +
                      this.pixelHeight +
                      '" ' +
                      ' id="jqsshape' +
                      shapeid +
                      '" ' +
                      stroke +
                      fill +
                      ' style="position:absolute;left:0px;top:0px;height:' +
                      this.pixelHeight +
                      'px;width:' +
                      this.pixelWidth +
                      'px;padding:0px;margin:0px;" ' +
                      ' path="m ' +
                      x +
                      ',' +
                      y +
                      ' wa ' +
                      vpath.join(', ') +
                      ' x e">' +
                      ' </v:shape>';
                  return vel;
              },
  
              _drawRect: function (
                  shapeid,
                  x,
                  y,
                  width,
                  height,
                  lineColor,
                  fillColor
              ) {
                  return this._drawShape(
                      shapeid,
                      [
                          [x, y],
                          [x, y + height],
                          [x + width, y + height],
                          [x + width, y],
                          [x, y],
                      ],
                      lineColor,
                      fillColor
                  );
              },
  
              reset: function () {
                  this.group.innerHTML = '';
              },
  
              appendShape: function (shape) {
                  var vel = this['_draw' + shape.type].apply(this, shape.args);
                  if (this.rendered) {
                      this.group.insertAdjacentHTML('beforeEnd', vel);
                  } else {
                      this.prerender += vel;
                  }
                  this.lastShapeId = shape.id;
                  return shape.id;
              },
  
              replaceWithShape: function (shapeid, shape) {
                  var existing = $('#jqsshape' + shapeid),
                      vel = this['_draw' + shape.type].apply(this, shape.args);
                  existing[0].outerHTML = vel;
              },
  
              replaceWithShapes: function (shapeids, shapes) {
                  // replace the first shapeid with all the new shapes then toast the remaining old shapes
                  var existing = $('#jqsshape' + shapeids[0]),
                      replace = '',
                      slen = shapes.length,
                      i;
                  for (i = 0; i < slen; i++) {
                      replace += this['_draw' + shapes[i].type].apply(
                          this,
                          shapes[i].args
                      );
                  }
                  existing[0].outerHTML = replace;
                  for (i = 1; i < shapeids.length; i++) {
                      $('#jqsshape' + shapeids[i]).remove();
                  }
              },
  
              insertAfterShape: function (shapeid, shape) {
                  var existing = $('#jqsshape' + shapeid),
                      vel = this['_draw' + shape.type].apply(this, shape.args);
                  existing[0].insertAdjacentHTML('afterEnd', vel);
              },
  
              removeShapeId: function (shapeid) {
                  var existing = $('#jqsshape' + shapeid);
                  this.group.removeChild(existing[0]);
              },
  
              getShapeAt: function (el, x, y) {
                  var shapeid = el.id.substr(8);
                  return shapeid;
              },
  
              render: function () {
                  if (!this.rendered) {
                      // batch the intial render into a single repaint
                      this.group.innerHTML = this.prerender;
                      this.rendered = true;
                  }
              },
          });
      });
  })(document, Math);
  
  /**************************************************************************************** */
  function jQuery3_4_1_Offset(elem) {
      var rect, win;
  
      if (!elem) {
          return { top: 0, left: 0 };
      }
      elem = elem.get(0);
  
      if (!elem) {
          return { top: 0, left: 0 };
      }
  
      // Return zeros for disconnected and hidden (display: none) elements (gh-2310)
      // Support: IE <=11 only
      // Running getBoundingClientRect on a
      // disconnected node in IE throws an error
      if (!elem.getClientRects().length) {
          return { top: 0, left: 0 };
      }
  
      // Get document-relative position by adding viewport scroll to viewport-relative gBCR
      rect = elem.getBoundingClientRect();
      win = elem.ownerDocument.defaultView;
      return {
          top: rect.top + win.pageYOffset,
          left: rect.left + win.pageXOffset,
      };
  }
  