Вход Регистрация
Файл: CloudBox-main/CloudBox/admin_assets/js/chart/jquery.sparkline.js
Строк: 3723
<?php
/**
 *
 * 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 (documentMathundefined) { // 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 = {},
            
getDefaultscreateClassSPFormatclipvalquartilenormalizeValuenormalizeValues,
            
removeisNumberallsumaddCSSensureArrayformatNumberRangeMap,
            
MouseHandlerTooltipbarHighlightMixin,
            
linebartristatediscretebulletpieboxdefaultStylesinitStyles,
            
VShapeVCanvas_baseVCanvas_canvasVCanvas_vmlpendingshapeCount 0;

        
/**
         * Default configuration settings
         */
        
getDefaults = function () {
            return {
                
// Settings common to most/all chart types
                
common: {
                    
type'line',
                    
lineColor'#00f',
                    
fillColor'#cdf',
                    
defaultPixelsPerValue3,
                    
width'auto',
                    
height'auto',
                    
compositefalse,
                    
tagValuesAttribute'values',
                    
tagOptionsPrefix'spark',
                    
enableTagOptionsfalse,
                    
enableHighlighttrue,
                    
highlightLighten1.4,
                    
tooltipSkipNulltrue,
                    
tooltipPrefix'',
                    
tooltipSuffix'',
                    
disableHiddenCheckfalse,
                    
numberFormatterfalse,
                    
numberDigitGroupCount3,
                    
numberDigitGroupSep',',
                    
numberDecimalMark'.',
                    
disableTooltipsfalse,
                    
disableInteractionfalse
                
},
                
// Defaults for line charts
                
line: {
                    
spotColor'#f80',
                    
highlightSpotColor'#5f5',
                    
highlightLineColor'#f22',
                    
spotRadius1.5,
                    
minSpotColor'#f80',
                    
maxSpotColor'#f80',
                    
lineWidth1,
                    
normalRangeMinundefined,
                    
normalRangeMaxundefined,
                    
normalRangeColor'#ccc',
                    
drawNormalOnTopfalse,
                    
chartRangeMinundefined,
                    
chartRangeMaxundefined,
                    
chartRangeMinXundefined,
                    
chartRangeMaxXundefined,
                    
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'],
                    
zeroColorundefined,
                    
nullColorundefined,
                    
zeroAxistrue,
                    
barWidth4,
                    
barSpacing1,
                    
chartRangeMaxundefined,
                    
chartRangeMinundefined,
                    
chartRangeClipfalse,
                    
colorMapundefined,
                    
tooltipFormat: new SPFormat('<span style="color: {{color}}">&#9679;</span> {{prefix}}{{value}}{{suffix}}')
                },
                
// Defaults for tristate charts
                
tristate: {
                    
barWidth4,
                    
barSpacing1,
                    
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',
                    
thresholdColorundefined,
                    
thresholdValue0,
                    
chartRangeMaxundefined,
                    
chartRangeMinundefined,
                    
chartRangeClipfalse,
                    
tooltipFormat: new SPFormat('{{prefix}}{{value}}{{suffix}}')
                },
                
// Defaults for bullet charts
                
bullet: {
                    
targetColor'#f33',
                    
targetWidth3// width of the target bar in pixels
                    
performanceColor'#33f',
                    
rangeColors: ['#d3dafe''#a8b6ff''#7f94ff'],
                    
baseundefined// 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: {
                    
offset0,
                    
sliceColors: ['#3366cc''#dc3912''#ff9900''#109618''#66aa00',
                        
'#dd4477''#0099c6''#990099'],
                    
borderWidth0,
                    
borderColor'#000',
                    
tooltipFormat: new SPFormat('<span style="color: {{color}}">&#9679;</span> {{value}} ({{percent.1}}%)')
                },
                
// Defaults for box plots
                
box: {
                    
rawfalse,
                    
boxLineColor'#000',
                    
boxFillColor'#cdf',
                    
whiskerColor'#000',
                    
outlierLineColor'#333',
                    
outlierFillColor'#fff',
                    
medianColor'#f00',
                    
showOutlierstrue,
                    
outlierIQR1.5,
                    
spotRadius1.5,
                    
targetundefined,
                    
targetColor'#4a2',
                    
chartRangeMaxundefined,
                    
chartRangeMinundefined,
                    
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;' +
            
'}' +
            
'.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(thisarguments);
            };
            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(arguments1, -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 (formatfclass) {
                
this.format format;
                
this.fclass fclass;
            },

            
render: function (fieldsetlookupsoptions) {
                var 
self this,
                    
fields fieldset,
                    
matchtokenlookupkeyfieldvalueprec;
                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(fieldvalueprec,
                                
options.get('numberDigitGroupCount'),
                                
options.get('numberDigitGroupSep'),
                                
options.get('numberDecimalMark'));
                        }
                    }
                    return 
fieldvalue;
                });
            }
        });

        
// convience method to avoid needing the new operator
        
$.spformat = function (formatfclass) {
            return new 
SPFormat(formatfclass);
        };

        
clipval = function (valminmax) {
            if (
val min) {
                return 
min;
            }
            if (
val max) {
                return 
max;
            }
            return 
val;
        };

        
quartile = function (valuesq) {
            var 
vl;
            if (
=== 2) {
                
vl Math.floor(values.length 2);
                return 
values.length values[vl] : (values[vl 1] + values[vl]) / 2;
            } else {
                if (
values.length 2) { // odd
                    
vl = (values.length q) / 4;
                    return 
vl ? (values[Math.floor(vl)] + values[Math.floor(vl) - 1]) / values[vl 1];
                } else { 
//even
                    
vl = (values.length 2) / 4;
                    return 
vl ? (values[Math.floor(vl)] + values[Math.floor(vl) - 1]) / 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 
iresult = [];
            for (
vals.lengthi--;) {
                
result[i] = normalizeValue(vals[i]);
            }
            return 
result;
        };

        
remove = function (valsfilter) {
            var 
ivlresult = [];
            for (
0vl vals.lengthvli++) {
                if (
vals[i] !== filter) {
                    
result.push(vals[i]);
                }
            }
            return 
result;
        };

        
isNumber = function (num) {
            return !
isNaN(parseFloat(num)) && isFinite(num);
        };

        
formatNumber = function (numprecgroupsizegroupsepdecsep) {
            var 
pi;
            
num = (prec === false parseFloat(num).toString() : num.toFixed(prec)).split('');
            
= (= $.inArray('.'num)) < num.length p;
            if (
num.length) {
                
num[p] = decsep;
            }
            for (
groupsize0-= groupsize) {
                
num.splice(i0groupsep);
            }
            return 
num.join('');
        };

        
// determine if all values of an array match a value
        // returns true if the array is empty
        
all = function (valarrignoreNull) {
            var 
i;
            for (
arr.lengthi--;) {
                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 0i;
            for (
vals.lengthi--;) {
                
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 (widthheightuseExistinginteract) {
            var 
targetmhandler;
            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 (widthheighttargetinteract) {
                        return new 
VCanvas_canvas(widthheighttargetinteract);
                    };
                } 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 (widthheighttargetinteract) {
                        return new 
VCanvas_vml(widthheighttarget);
                    };
                } 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(widthheightthisinteract);

            
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 
keyrangerangelist = [];
                for (
key in map) {
                    if (
map.hasOwnProperty(key) && typeof key === 'string' && key.indexOf(':') > -1) {
                        
range key.split(':');
                        
range[0] = range[0].length === ? -Infinity parseFloat(range[0]);
                        
range[1] = range[1].length === 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,
                    
irangeresult;
                if ((
result this.map[value]) !== undefined) {
                    return 
result;
                }
                if (
rangelist) {
                    for (
rangelist.lengthi--;) {
                        
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 (eloptions) {
                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.mouseenterthis));
                
$canvas.mouseleave($.proxy(this.mouseleavethis));
                
$canvas.click($.proxy(this.mouseclickthis));
            },

            
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.mousemovethis));
                
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.pageXe.pageY);
                }
                
this.updateDisplay();
            },

            
mouseleave: function () {
                $(
document.body).unbind('mousemove.jqs');
                var 
splist this.splist,
                    
spcount splist.length,
                    
needsRefresh false,
                    
spi;
                
this.over false;
                
this.currentEl null;

                if (
this.tooltip) {
                    
this.tooltip.remove();
                    
this.tooltip null;
                }

                for (
0spcounti++) {
                    
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.pageXe.pageY);
                }
                
this.updateDisplay();
            },

            
updateDisplay: function () {
                var 
splist this.splist,
                    
spcount splist.length,
                    
needsRefresh false,
                    
offset this.$canvas.offset(),
                    
localX this.currentPageX offset.left,
                    
localY this.currentPageY offset.top,
                    
tooltiphtmlspiresultchangeEvent;
                if (!
this.over) {
                    return;
                }
                for (
0spcounti++) {
                    
sp splist[i];
                    
result sp.setRegionHighlight(this.currentEllocalXlocalY);
                    if (
result) {
                        
needsRefresh true;
                    }
                }
                if (
needsRefresh) {
                    
changeEvent = $.Event('sparklineRegionChange');
                    
changeEvent.sparklines this.splist;
                    
this.$el.trigger(changeEvent);
                    if (
this.tooltip) {
                        
tooltiphtml '';
                        for (
0spcounti++) {
                            
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',
                    
stylesizetipStyle,
                    
'class'tooltipClassname
                
});
                
this.tooltip = $('<div/>', {
                    
id'jqstooltip',
                    
'class'tooltipClassname
                
}).appendTo(this.container);
                
// account for the container's location
                
offset this.tooltip.offset();
                
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.updateWindowDimsthis));
                
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 (xy) {
                if (
=== undefined) {
                    if (
this.mousex === undefined) {
                        return;
                    }
                    
this.mousex this.offsetLeft;
                    
this.mousey this.offsetTop;

                } else {
                    
this.mousex this.offsetLeft;
                    
this.mousey this.offsetTop;
                }
                if (!
this.height || !this.width || this.hidden) {
                    return;
                }

                
-= this.height this.tooltipOffsetY;
                
+= this.tooltipOffsetX;

                if (
this.scrollTop) {
                    
this.scrollTop;
                }
                if (
this.scrollLeft) {
                    
this.scrollLeft;
                } else if (
this.width this.scrollRight) {
                    
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 (userValuesuserOptions) {
            return 
this.each(function () {
                var 
options = new $.fn.sparkline.options(thisuserOptions),
                    
$this = $(this),
                    
renderi;
                
render = function () {
                    var 
valueswidthheighttmpmhandlerspvals;
                    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(thisoptions);
                            $.
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')](thisvaluesoptionswidthheight);

                    
sp.render();

                    if (
mhandler) {
                        
mhandler.registerSparkline(sp);
                    }
                };
                if (($(
this).html() && !options.get('disableHiddenCheck') && $(this).is(':hidden')) || !$(this).parents('body').length) {
                    if (!
options.get('composite') && $.data(this'_jqs_pending')) {
                        
// remove any existing references to the element
                        
for (pending.lengthii--) {
                            if (
pending[1][0] == this) {
                                
pending.splice(11);
                            }
                        }
                    }
                    
pending.push([thisrender]);
                    $.
data(this'_jqs_pending'true);
                } else {
                    
render.call(this);
                }
            });
        };

        $.
fn.sparkline.defaults getDefaults();


        $.
sparkline_display_visible = function () {
            var 
elipl;
            var 
done = [];
            for (
0pl pending.lengthpli++) {
                
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 (
done.lengthii--) {
                
pending.splice(done[1], 1);
            }
        };


        
/**
         * User option handler
         */
        
$.fn.sparkline.options createClass({
            
init: function (taguserOptions) {
                var 
extendedOptionsdefaultsbasetagOptionType;
                
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({}, baseextendedOptionsuserOptions);
            },


            
getTagSetting: function (key) {
                var 
prefix this.tagOptionsPrefix,
                    
valipairskeyval;
                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(01) === '[') {
                        
val val.substr(1val.length 2).split(',');
                        for (
val.lengthi--;) {
                            
val[i] = normalizeValue(val[i].replace(/(^s*)|(s*$)/g''));
                        }
                    } else if (
val.substr(01) === '{') {
                        
pairs val.substr(1val.length 2).split(',');
                        
val = {};
                        for (
pairs.lengthi--;) {
                            
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 (keydefaultval) {
                var 
tagOption this.getTagSetting(key),
                    
result;
                if (
tagOption !== UNSET_OPTION) {
                    return 
tagOption;
                }
                return (
result this.mergedOptions[key]) === undefined defaultval result;
            }
        });


        $.
fn.sparkline._base createClass({
            
disabledfalse,

            
init: function (elvaluesoptionswidthheight) {
                
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.widththis.heightthis.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 (xy) {
            },

            
/**
             * Highlight an item based on the moused-over x,y co-ordinate
             */
            
setRegionHighlight: function (elxy) {
                var 
currentRegion this.currentRegion,
                    
highlightEnabled = !this.options.get('disableHighlight'),
                    
newRegion;
                if (
this.canvasWidth || this.canvasHeight || || 0) {
                    return 
null;
                }
                
newRegion this.getRegion(elxy);
                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 = [],
                    
fieldsformatsformatlenfclasstexti,
                    
showFieldsshowFieldsKeynewFieldsfv,
                    
formatterformatfieldlenj;
                if (
this.currentRegion === undefined) {
                    return 
'';
                }
                
fields this.getCurrentRegionFields();
                
formatter options.get('tooltipFormatter');
                if (
formatter) {
                    return 
formatter(thisoptionsfields);
                }
                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 (
fields.lengthi--;) {
                        
fv fields[i][showFieldsKey];
                        if ((
= $.inArray(fvshowFields)) != -1) {
                            
newFields[j] = fields[i];
                        }
                    }
                    
fields newFields;
                }
                
formatlen formats.length;
                
fieldlen fields.length;
                for (
0formatleni++) {
                    
format formats[i];
                    if (
typeof format === 'string') {
                        
format = new SPFormat(format);
                    }
                    
fclass format.fclass || 'jqsfield';
                    for (
0fieldlenj++) {
                        if (!
fields[j].isNull || !options.get('tooltipSkipNull')) {
                            $.
extend(fields[j], {
                                
prefixoptions.get('tooltipPrefix'),
                                
suffixoptions.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 (coloroptions) {
                var 
highlightColor options.get('highlightColor'),
                    
lighten options.get('highlightLighten'),
                    
parsemultrgbnewi;
                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 === 16 1;
                        for (
03i++) {
                            
rgbnew[i] = clipval(Math.round(parseInt(parse[1], 16) * mult lighten), 0255);
                        }
                        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(currentRegionhighlight);
                    if ($.
isArray(newShapes) || $.isArray(shapeids)) {
                        
target.replaceWithShapes(shapeidsnewShapes);
                        
this.regionShapes[currentRegion] = $.map(newShapes, function (newShape) {
                            return 
newShape.id;
                        });
                    } else {
                        
target.replaceWithShape(shapeidsnewShapes);
                        
this.regionShapes[currentRegion] = newShapes.id;
                    }
                }
            },

            
render: function () {
                var 
values this.values,
                    
target this.target,
                    
regionShapes this.regionShapes,
                    
shapesidsij;

                if (!
this.cls._super.render.call(this)) {
                    return;
                }
                for (
values.lengthi--;) {
                    
shapes this.renderRegion(i);
                    if (
shapes) {
                        if ($.
isArray(shapes)) {
                            
ids = [];
                            for (
shapes.lengthj--;) {
                                
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 (elvaluesoptionswidthheight) {
                
line._super.init.call(thiselvaluesoptionswidthheight);
                
this.vertices = [];
                
this.regionMap = [];
                
this.xvalues = [];
                
this.yvalues = [];
                
this.yminmax = [];
                
this.hightlightSpotId null;
                
this.lastShapeId null;
                
this.initTarget();
            },

            
getRegion: function (elxy) {
                var 
i,
                    
regionMap this.regionMap// maps regions to value positions
                
for (regionMap.lengthi--;) {
                    if (
regionMap[i] !== null && >= regionMap[i][0] && <= regionMap[i][1]) {
                        return 
regionMap[i][2];
                    }
                }
                return 
undefined;
            },

            
getCurrentRegionFields: function () {
                var 
currentRegion this.currentRegion;
                return {
                    
isNullthis.yvalues[currentRegion] === null,
                    
xthis.xvalues[currentRegion],
                    
ythis.yvalues[currentRegion],
                    
colorthis.options.get('lineColor'),
                    
fillColorthis.options.get('fillColor'),
                    
offsetcurrentRegion
                
};
            },

            
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'),
                    
highlightSpothighlightLine;

                if (!
vertex) {
                    return;
                }
                if (
spotRadius && highlightSpotColor) {
                    
highlightSpot target.drawCircle(vertex[0], vertex[1],
                        
spotRadiusundefinedhighlightSpotColor);
                    
this.highlightSpotId highlightSpot.id;
                    
target.insertAfterShape(this.lastShapeIdhighlightSpot);
                }
                if (
highlightLineColor) {
                    
highlightLine target.drawLine(vertex[0], this.canvasTopvertex[0],
                        
this.canvasTop this.canvasHeighthighlightLineColor);
                    
this.highlightLineId highlightLine.id;
                    
target.insertAfterShape(this.lastShapeIdhighlightLine);
                }
            },

            
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,
                    
ivalisStrisArraysp;
                for (
0valcounti++) {
                    
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(Mathyminmax);
                
this.miny this.minyorg Math.min.apply(Mathyminmax);

                
this.maxx Math.max.apply(Mathxvalues);
                
this.minx Math.min.apply(Mathxvalues);

                
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 (canvasLeftcanvasTopcanvasHeightcanvasWidthrangey) {
                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(canvasLeftytopcanvasWidthheightundefinedthis.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,
                    
rangexrangeyyvallast,
                    
canvasTopcanvasLeft,
                    
vertexpathpathsxyxnextxposxposnext,
                    
lastnextyvalcountlineShapesfillShapesplen,
                    
valueSpotshlSpotsEnabledcolorxvaluesyvaluesi;

                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 === this.maxx this.minx;
                
rangey this.maxy this.miny === 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(canvasLeftcanvasTopcanvasHeightcanvasWidthrangey);
                }

                
path = [];
                
paths = [path];
                
last next null;
                
yvalcount yvalues.length;
                for (
0yvalcounti++) {
                    
xvalues[i];
                    
xnext xvalues[1];
                    
yvalues[i];
                    
xpos canvasLeft Math.round((this.minx) * (canvasWidth rangex));
                    
xposnext yvalcount canvasLeft Math.round((xnext this.minx) * (canvasWidth rangex)) : canvasWidth;
                    
next xpos + ((xposnext xpos) / 2);
                    
regionMap[i] = [last || 0nexti];
                    
last next;
                    if (
=== null) {
                        if (
i) {
                            if (
yvalues[1] !== null) {
                                
path = [];
                                
paths.push(path);
                            }
                            
vertices.push(null);
                        }
                    } else {
                        if (
this.miny) {
                            
this.miny;
                        }
                        if (
this.maxy) {
                            
this.maxy;
                        }
                        if (!
path.length) {
                            
// previous value was null
                            
path.push([xposcanvasTop canvasHeight]);
                        }
                        
vertex = [xposcanvasTop Math.round(canvasHeight - (canvasHeight * ((this.miny) / rangey)))];
                        
path.push(vertex);
                        
vertices.push(vertex);
                    }
                }

                
lineShapes = [];
                
fillShapes = [];
                
plen paths.length;
                for (
0pleni++) {
                    
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 (
0pleni++) {
                    
target.drawShape(fillShapes[i],
                        
options.get('fillColor'), options.get('fillColor')).append();
                }

                if (
options.get('normalRangeMin') !== undefined && options.get('drawNormalOnTop')) {
                    
this.drawNormalRange(canvasLeftcanvasTopcanvasHeightcanvasWidthrangey);
                }

                
plen lineShapes.length;
                for (
0pleni++) {
                    
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 (
0yvalcounti++) {
                        
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))),
                                
spotRadiusundefined,
                                
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))),
                        
spotRadiusundefined,
                        
options.get('spotColor')).append();
                }
                if (
this.maxy !== this.minyorg) {
                    if (
spotRadius && options.get('minSpotColor')) {
                        
xvalues[$.inArray(this.minyorgyvalues)];
                        
target.drawCircle(canvasLeft Math.round((this.minx) * (canvasWidth rangex)),
                            
canvasTop Math.round(canvasHeight - (canvasHeight * ((this.minyorg this.miny) / rangey))),
                            
spotRadiusundefined,
                            
options.get('minSpotColor')).append();
                    }
                    if (
spotRadius && options.get('maxSpotColor')) {
                        
xvalues[$.inArray(this.maxyorgyvalues)];
                        
target.drawCircle(canvasLeft Math.round((this.minx) * (canvasWidth rangex)),
                            
canvasTop Math.round(canvasHeight - (canvasHeight * ((this.maxyorg this.miny) / rangey))),
                            
spotRadiusundefined,
                            
options.get('maxSpotColor')).append();
                    }
                }

                
this.lastShapeId target.getLastShapeId();
                
this.canvasTop canvasTop;
                
target.render();
            }
        });

        
/**
         * Bar charts
         */
        
$.fn.sparkline.bar bar createClass($.fn.sparkline._basebarHighlightMixin, {
            
type'bar',

            
init: function (elvaluesoptionswidthheight) {
                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,
                    
isStackStringgroupMingroupMaxstackRanges,
                    
numValuesivlenrangezeroAxisxaxisOffsetminmaxclipMinclipMax,
                    
stackedvlistjslensvalsvalyoffsetyMaxCalccanvasHeightEf;
                
bar._super.init.call(thiselvaluesoptionswidthheight);

                
// scan values to determine whether to stack bars
                
for (0vlen values.lengthvleni++) {
                    
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(valnull); // min/max will treat null as zero
                        
groupMin Math.min.apply(Mathval);
                        
groupMax Math.max.apply(Mathval);
                        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 (
0vlen values.lengthvleni++) {
                    if (
stacked) {
                        
vlist values[i];
                        
values[i] = svals = [];
                        
stackTotals[i] = 0;
                        
stackRanges[i] = stackRangesNeg[i] = 0;
                        for (
0slen vlist.lengthslenj++) {
                            
val svals[j] = chartRangeClip clipval(vlist[j], clipMinclipMax) : vlist[j];
                            if (
val !== null) {
                                if (
val 0) {
                                    
stackTotals[i] += val;
                                }
                                if (
stackMin && stackMax 0) {
                                    if (
val 0) {
                                        
stackRangesNeg[i] += Math.abs(val);
                                    } else {
                                        
stackRanges[i] += val;
                                    }
                                } else {
                                    
stackRanges[i] += Math.abs(val - (val stackMax stackMin));
                                }
                                
numValues.push(val);
                            }
                        }
                    } else {
                        
val chartRangeClip clipval(values[i], clipMinclipMax) : values[i];
                        
val values[i] = normalizeValue(val);
                        if (
val !== null) {
                            
numValues.push(val);
                        }
                    }
                }
                
this.max max Math.max.apply(MathnumValues);
                
this.min min Math.min.apply(MathnumValues);
                
this.stackMax stackMax stacked Math.max.apply(MathstackTotals) : max;
                
this.stackMin stackMin stacked Math.min.apply(MathnumValues) : 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 <= && max >= && 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(MathstackRanges) + Math.max.apply(MathstackRangesNeg)) : 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 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 (elxy) {
                var 
result Math.floor(this.totalBarWidth);
                return (
result || result >= this.values.length) ? undefined result;
            },

            
getCurrentRegionFields: function () {
                var 
currentRegion this.currentRegion,
                    
values ensureArray(this.values[currentRegion]),
                    
result = [],
                    
valuei;
                for (
values.lengthi--;) {
                    
value values[i];
                    
result.push({
                        
isNullvalue === null,
                        
valuevalue,
                        
colorthis.calcColor(ivaluecurrentRegion),
                        
offsetcurrentRegion
                    
});
                }
                return 
result;
            },

            
calcColor: function (stacknumvaluevaluenum) {
                var 
colorMapByIndex this.colorMapByIndex,
                    
colorMapByValue this.colorMapByValue,
                    
options this.options,
                    
colornewColor;
                if (
this.stacked) {
                    
color options.get('stackedBarColor');
                } else {
                    
color = (value 0) ? options.get('negBarColor') : options.get('barColor');
                }
                if (
value === && 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 (valuenumhighlight) {
                var 
vals this.values[valuenum],
                    
options this.options,
                    
xaxisOffset this.xaxisOffset,
                    
result = [],
                    
range this.range,
                    
stacked this.stacked,
                    
target this.target,
                    
valuenum this.totalBarWidth,
                    
canvasHeightEf this.canvasHeightEf,
                    
yoffset this.yoffset,
                    
yheightcolorisNullyoffsetNegivalcountvalminPlottedallMin;

                
vals = $.isArray(vals) ? vals : [vals];
                
valcount vals.length;
                
val vals[0];
                
isNull all(nullvals);
                
allMin all(xaxisOffsetvalstrue);

                if (
isNull) {
                    if (
options.get('nullColor')) {
                        
color highlight options.get('nullColor') : this.calcHighlightColor(options.get('nullColor'), options);
                        
= (yoffset 0) ? yoffset yoffset;
                        return 
target.drawRect(xythis.barWidth 10colorcolor);
                    } else {
                        return 
undefined;
                    }
                }
                
yoffsetNeg yoffset;
                for (
0valcounti++) {
                    
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)) {
                        
yoffsetNeg;
                        
yoffsetNeg += height;
                    } else {
                        
yoffset height;
                        
yoffset -= height;
                    }
                    
color this.calcColor(ivalvaluenum);
                    if (
highlight) {
                        
color this.calcHighlightColor(coloroptions);
                    }
                    
result.push(target.drawRect(xythis.barWidth 1height 1colorcolor));
                }
                if (
result.length === 1) {
                    return 
result[0];
                }
                return 
result;
            }
        });

        
/**
         * Tristate charts
         */
        
$.fn.sparkline.tristate tristate createClass($.fn.sparkline._basebarHighlightMixin, {
            
type'tristate',

            
init: function (elvaluesoptionswidthheight) {
                var 
barWidth parseInt(options.get('barWidth'), 10),
                    
barSpacing parseInt(options.get('barSpacing'), 10);
                
tristate._super.init.call(thiselvaluesoptionswidthheight);

                
this.regionShapes = {};
                
this.barWidth barWidth;
                
this.barSpacing barSpacing;
                
this.totalBarWidth barWidth barSpacing;
                
this.values = $.map(valuesNumber);
                
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 (elxy) {
                return 
Math.floor(this.totalBarWidth);
            },

            
getCurrentRegionFields: function () {
                var 
currentRegion this.currentRegion;
                return {
                    
isNullthis.values[currentRegion] === undefined,
                    
valuethis.values[currentRegion],
                    
colorthis.calcColor(this.values[currentRegion], currentRegion),
                    
offsetcurrentRegion
                
};
            },

            
calcColor: function (valuevaluenum) {
                var 
values this.values,
                    
options this.options,
                    
colorMapByIndex this.colorMapByIndex,
                    
colorMapByValue this.colorMapByValue,
                    
colornewColor;

                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 (valuenumhighlight) {
                var 
values this.values,
                    
options this.options,
                    
target this.target,
                    
canvasHeightheighthalfHeight,
                    
xycolor;

                
canvasHeight target.pixelHeight;
                
halfHeight Math.round(canvasHeight 2);

                
valuenum this.totalBarWidth;
                if (
values[valuenum] < 0) {
                    
halfHeight;
                    
height halfHeight 1;
                } else if (
values[valuenum] > 0) {
                    
0;
                    
height halfHeight 1;
                } else {
                    
halfHeight 1;
                    
height 2;
                }
                
color this.calcColor(values[valuenum], valuenum);
                if (
color === null) {
                    return;
                }
                if (
highlight) {
                    
color this.calcHighlightColor(coloroptions);
                }
                return 
target.drawRect(xythis.barWidth 1height 1colorcolor);
            }
        });

        
/**
         * Discrete charts
         */
        
$.fn.sparkline.discrete discrete createClass($.fn.sparkline._basebarHighlightMixin, {
            
type'discrete',

            
init: function (elvaluesoptionswidthheight) {
                
discrete._super.init.call(thiselvaluesoptionswidthheight);

                
this.regionShapes = {};
                
this.values values = $.map(valuesNumber);
                
this.min Math.min.apply(Mathvalues);
                
this.max Math.max.apply(Mathvalues);
                
this.range this.max this.min;
                
this.width width options.get('width') === 'auto' values.length 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 (elxy) {
                return 
Math.floor(this.itemWidth);
            },

            
getCurrentRegionFields: function () {
                var 
currentRegion this.currentRegion;
                return {
                    
isNullthis.values[currentRegion] === undefined,
                    
valuethis.values[currentRegion],
                    
offsetcurrentRegion
                
};
            },

            
renderRegion: function (valuenumhighlight) {
                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,
                    
ytopvalcolorx;

                
val clipval(values[valuenum], minmax);
                
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(coloroptions);
                }
                return 
target.drawLine(xytopxytop lineHeightcolor);
            }
        });

        
/**
         * Bullet charts
         */
        
$.fn.sparkline.bullet bullet createClass($.fn.sparkline._base, {
            
type'bullet',

            
init: function (elvaluesoptionswidthheight) {
                var 
minmaxvals;
                
bullet._super.init.call(thiselvaluesoptionswidthheight);

                
// 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(Mathvalues);
                
max Math.max.apply(Mathvalues);
                if (
options.get('base') === undefined) {
                    
min min 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(widthheightoptions.get('composite'));
                if (!
values.length) {
                    
this.disabled true;
                }
                
this.initTarget();
            },

            
getRegion: function (elxy) {
                var 
shapeid this.target.getShapeAt(elxy);
                return (
shapeid !== undefined && this.shapes[shapeid] !== undefined) ? this.shapes[shapeid] : undefined;
            },

            
getCurrentRegionFields: function () {
                var 
currentRegion this.currentRegion;
                return {
                    
fieldkeycurrentRegion.substr(01),
                    
valuethis.values[currentRegion.substr(1)],
                    
regioncurrentRegion
                
};
            },

            
changeHighlight: function (highlight) {
                var 
currentRegion this.currentRegion,
                    
shapeid this.valueShapes[currentRegion],
                    
shape;
                
delete this.shapes[shapeid];
                switch (
currentRegion.substr(01)) {
                    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(shapeidshape);
            },

            
renderRange: function (rnhighlight) {
                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(colorthis.options);
                }
                return 
this.target.drawRect(00rangewidth 1this.canvasHeight 1colorcolor);
            },

            
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(colorthis.options);
                }
                return 
this.target.drawRect(0Math.round(this.canvasHeight 0.3), perfwidth 1,
                    
Math.round(this.canvasHeight 0.4) - 1colorcolor);
            },

            
renderTarget: function (highlight) {
                var 
targetval this.values[0],
                    
Math.round(this.canvasWidth * ((targetval this.min) / this.range) - (this.options.get('targetWidth') / 2)),
                    
targettop Math.round(this.canvasHeight 0.10),
                    
targetheight this.canvasHeight - (targettop 2),
                    
color this.options.get('targetColor');
                if (
highlight) {
                    
color this.calcHighlightColor(colorthis.options);
                }
                return 
this.target.drawRect(xtargettopthis.options.get('targetWidth') - 1targetheight 1colorcolor);
            },

            
render: function () {
                var 
vlen this.values.length,
                    
target this.target,
                    
ishape;
                if (!
bullet._super.render.call(this)) {
                    return;
                }
                for (
2vleni++) {
                    
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 (elvaluesoptionswidthheight) {
                var 
total 0i;

                
pie._super.init.call(thiselvaluesoptionswidthheight);

                
this.shapes = {}; // map shape ids to value offsets
                
this.valueShapes = {}; // maps value offsets to shape ids
                
this.values values = $.map(valuesNumber);

                if (
options.get('width') === 'auto') {
                    
this.width this.height;
                }

                if (
values.length 0) {
                    for (
values.lengthi--;) {
                        
total += values[i];
                    }
                }
                
this.total total;
                
this.initTarget();
                
this.radius Math.floor(Math.min(this.canvasWidththis.canvasHeight) / 2);
            },

            
getRegion: function (elxy) {
                var 
shapeid this.target.getShapeAt(elxy);
                return (
shapeid !== undefined && this.shapes[shapeid] !== undefined) ? this.shapes[shapeid] : undefined;
            },

            
getCurrentRegionFields: function () {
                var 
currentRegion this.currentRegion;
                return {
                    
isNullthis.values[currentRegion] === undefined,
                    
valuethis.values[currentRegion],
                    
percentthis.values[currentRegion] / this.total 100,
                    
colorthis.options.get('sliceColors')[currentRegion this.options.get('sliceColors').length],
                    
offsetcurrentRegion
                
};
            },

            
changeHighlight: function (highlight) {
                var 
currentRegion this.currentRegion,
                    
newslice this.renderSlice(currentRegionhighlight),
                    
shapeid this.valueShapes[currentRegion];
                
delete this.shapes[shapeid];
                
this.target.replaceWithShape(shapeidnewslice);
                
this.valueShapes[currentRegion] = newslice.id;
                
this.shapes[newslice.id] = currentRegion;
            },

            
renderSlice: function (valuenumhighlight) {
                var 
target this.target,
                    
options this.options,
                    
radius this.radius,
                    
borderWidth options.get('borderWidth'),
                    
offset options.get('offset'),
                    
circle Math.PI,
                    
values this.values,
                    
total this.total,
                    
next offset ? (Math.PI) * (offset 360) : 0,
                    
startendivlencolor;

                
vlen values.length;
                for (
0vleni++) {
                    
start next;
                    
end next;
                    if (
total 0) {  // avoid divide by zero
                        
end next + (circle * (values[i] / total));
                    }
                    if (
valuenum === i) {
                        
color options.get('sliceColors')[options.get('sliceColors').length];
                        if (
highlight) {
                            
color this.calcHighlightColor(coloroptions);
                        }

                        return 
target.drawPieSlice(radiusradiusradius borderWidthstartendundefinedcolor);
                    }
                    
next end;
                }
            },

            
render: function () {
                var 
target this.target,
                    
values this.values,
                    
options this.options,
                    
radius this.radius,
                    
borderWidth options.get('borderWidth'),
                    
shapei;

                if (!
pie._super.render.call(this)) {
                    return;
                }
                if (
borderWidth) {
                    
target.drawCircle(radiusradiusMath.floor(radius - (borderWidth 2)),
                        
options.get('borderColor'), undefinedborderWidth).append();
                }
                for (
values.lengthi--;) {
                    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 (elvaluesoptionswidthheight) {
                
box._super.init.call(thiselvaluesoptionswidthheight);
                
this.values = $.map(valuesNumber);
                
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'valuethis.quartiles[0] },
                    { 
field'med'valuethis.quartiles[1] },
                    { 
field'uq'valuethis.quartiles[2] }
                ];
                if (
this.loutlier !== undefined) {
                    
result.push({ field'lo'valuethis.loutlier});
                }
                if (
this.routlier !== undefined) {
                    
result.push({ field'ro'valuethis.routlier});
                }
                if (
this.lwhisker !== undefined) {
                    
result.push({ field'lw'valuethis.lwhisker});
                }
                if (
this.rwhisker !== undefined) {
                    
result.push({ field'rw'valuethis.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(Mathvalues) : options.get('chartRangeMin'),
                    
maxValue options.get('chartRangeMax') === undefined Math.max.apply(Mathvalues) : options.get('chartRangeMax'),
                    
canvasLeft 0,
                    
lwhiskerloutlieriqrq1q2q3rwhiskerroutlieri,
                    
sizeunitSize;

                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 (ab) {
                        return 
b;
                    });
                    
q1 quartile(values1);
                    
q2 quartile(values2);
                    
q3 quartile(values3);
                    
iqr q3 q1;
                    if (
options.get('showOutliers')) {
                        
lwhisker rwhisker undefined;
                        for (
0vleni++) {
                            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 = [q1q2q3];
                
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 -= 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 (targetidtypeargs) {
                
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 (widthheighttarget) {
                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 (x1y1x2y2lineColorlineWidth) {
                return 
this.drawShape([
                    [
x1y1],
                    [
x2y2]
                ], 
lineColorlineWidth);
            },

            
drawShape: function (pathlineColorfillColorlineWidth) {
                return 
this._genShape('Shape', [pathlineColorfillColorlineWidth]);
            },

            
drawCircle: function (xyradiuslineColorfillColorlineWidth) {
                return 
this._genShape('Circle', [xyradiuslineColorfillColorlineWidth]);
            },

            
drawPieSlice: function (xyradiusstartAngleendAnglelineColorfillColor) {
                return 
this._genShape('PieSlice', [xyradiusstartAngleendAnglelineColorfillColor]);
            },

            
drawRect: function (xywidthheightlineColorfillColor) {
                return 
this._genShape('Rect', [xywidthheightlineColorfillColor]);
            },

            
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 (eltarget) {
                $(
target).html(el);
            },

            
/**
             * Calculate the pixel dimensions of the canvas
             */
            
_calculatePixelDims: function (widthheightcanvas) {
                
// 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 (shapetypeshapeargs) {
                var 
id shapeCount++;
                
shapeargs.unshift(id);
                return new 
VShape(thisidshapetypeshapeargs);
            },

            
/**
             * Add a shape to the end of the render queue
             */
            
appendShape: function (shape) {
                
alert('appendShape not implemented');
            },

            
/**
             * Replace one shape with another
             */
            
replaceWithShape: function (shapeidshape) {
                
alert('replaceWithShape not implemented');
            },

            
/**
             * Insert one shape after another in the render queue
             */
            
insertAfterShape: function (shapeidshape) {
                
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 (elxy) {
                
alert('getShapeAt not implemented');
            },

            
/**
             * Render all queued shapes onto the canvas
             */
            
render: function () {
                
alert('render not implemented');
            }
        });

        
VCanvas_canvas createClass(VCanvas_base, {
            
init: function (widthheighttargetinteract) {
                
VCanvas_canvas._super.init.call(thiswidthheighttarget);
                
this.canvas document.createElement('canvas');
                if (
target[0]) {
                    
target target[0];
                }
                $.
data(target'_jqs_vcanvas'this);
                $(
this.canvas).css({ display'inline-block'widthwidthheightheightverticalAlign'top' });
                
this._insert(this.canvastarget);
                
this._calculatePixelDims(widthheightthis.canvas);
                
this.canvas.width this.pixelWidth;
                
this.canvas.height this.pixelHeight;
                
this.interact interact;
                
this.shapes = {};
                
this.shapeseq = [];
                
this.currentTargetShapeId undefined;
                $(
this.canvas).css({widththis.pixelWidthheightthis.pixelHeight});
            },

            
_getContext: function (lineColorfillColorlineWidth) {
                var 
context this.canvas.getContext('2d');
                if (
lineColor !== undefined) {
                    
context.strokeStyle lineColor;
                }
                
context.lineWidth lineWidth === undefined lineWidth;
                if (
fillColor !== undefined) {
                    
context.fillStyle fillColor;
                }
                return 
context;
            },

            
reset: function () {
                var 
context this._getContext();
                
context.clearRect(00this.pixelWidththis.pixelHeight);
                
this.shapes = {};
                
this.shapeseq = [];
                
this.currentTargetShapeId undefined;
            },

            
_drawShape: function (shapeidpathlineColorfillColorlineWidth) {
                var 
context this._getContext(lineColorfillColorlineWidth),
                    
iplen;
                
context.beginPath();
                
context.moveTo(path[0][0] + 0.5path[0][1] + 0.5);
                for (
1plen path.lengthpleni++) {
                    
context.lineTo(path[i][0] + 0.5path[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.targetXthis.targetY)) {
                    
this.currentTargetShapeId shapeid;
                }
            },

            
_drawCircle: function (shapeidxyradiuslineColorfillColorlineWidth) {
                var 
context this._getContext(lineColorfillColorlineWidth);
                
context.beginPath();
                
context.arc(xyradius0Math.PIfalse);
                if (
this.targetX !== undefined && this.targetY !== undefined &&
                    
context.isPointInPath(this.targetXthis.targetY)) {
                    
this.currentTargetShapeId shapeid;
                }
                if (
lineColor !== undefined) {
                    
context.stroke();
                }
                if (
fillColor !== undefined) {
                    
context.fill();
                }
            },

            
_drawPieSlice: function (shapeidxyradiusstartAngleendAnglelineColorfillColor) {
                var 
context this._getContext(lineColorfillColor);
                
context.beginPath();
                
context.moveTo(xy);
                
context.arc(xyradiusstartAngleendAnglefalse);
                
context.lineTo(xy);
                
context.closePath();
                if (
lineColor !== undefined) {
                    
context.stroke();
                }
                if (
fillColor) {
                    
context.fill();
                }
                if (
this.targetX !== undefined && this.targetY !== undefined &&
                    
context.isPointInPath(this.targetXthis.targetY)) {
                    
this.currentTargetShapeId shapeid;
                }
            },

            
_drawRect: function (shapeidxywidthheightlineColorfillColor) {
                return 
this._drawShape(shapeid, [
                    [
xy],
                    [
widthy],
                    [
widthheight],
                    [
xheight],
                    [
xy]
                ], 
lineColorfillColor);
            },

            
appendShape: function (shape) {
                
this.shapes[shape.id] = shape;
                
this.shapeseq.push(shape.id);
                
this.lastShapeId shape.id;
                return 
shape.id;
            },

            
replaceWithShape: function (shapeidshape) {
                var 
shapeseq this.shapeseq,
                    
i;
                
this.shapes[shape.id] = shape;
                for (
shapeseq.lengthi--;) {
                    if (
shapeseq[i] == shapeid) {
                        
shapeseq[i] = shape.id;
                    }
                }
                
delete this.shapes[shapeid];
            },

            
replaceWithShapes: function (shapeidsshapes) {
                var 
shapeseq this.shapeseq,
                    
shapemap = {},
                    
sidifirst;

                for (
shapeids.lengthi--;) {
                    
shapemap[shapeids[i]] = true;
                }
                for (
shapeseq.lengthi--;) {
                    
sid shapeseq[i];
                    if (
shapemap[sid]) {
                        
shapeseq.splice(i1);
                        
delete this.shapes[sid];
                        
first i;
                    }
                }
                for (
shapes.lengthi--;) {
                    
shapeseq.splice(first0shapes[i].id);
                    
this.shapes[shapes[i].id] = shapes[i];
                }

            },

            
insertAfterShape: function (shapeidshape) {
                var 
shapeseq this.shapeseq,
                    
i;
                for (
shapeseq.lengthi--;) {
                    if (
shapeseq[i] === shapeid) {
                        
shapeseq.splice(10shape.id);
                        
this.shapes[shape.id] = shape;
                        return;
                    }
                }
            },

            
removeShapeId: function (shapeid) {
                var 
shapeseq this.shapeseq,
                    
i;
                for (
shapeseq.lengthi--;) {
                    if (
shapeseq[i] === shapeid) {
                        
shapeseq.splice(i1);
                        break;
                    }
                }
                
delete this.shapes[shapeid];
            },

            
getShapeAt: function (elxy) {
                
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(),
                    
shapeidshapei;
                
context.clearRect(00this.pixelWidththis.pixelHeight);
                for (
0shapeCounti++) {
                    
shapeid shapeseq[i];
                    
shape shapes[shapeid];
                    
this['_draw' shape.type].apply(thisshape.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 (widthheighttarget) {
                var 
groupel;
                
VCanvas_vml._super.init.call(thiswidthheighttarget);
                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'widthwidthheightheightmargin'0px'padding'0px'verticalAlign'top'});
                
this._insert(this.canvastarget);
                
this._calculatePixelDims(widthheightthis.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 (shapeidpathlineColorfillColorlineWidth) {
                var 
vpath = [],
                    
initialstrokefillclosedvelpleni;
                for (
0plen path.lengthpleni++) {
                    
vpath[i] = '' + (path[i][0]) + ',' + (path[i][1]);
                }
                
initial vpath.splice(01);
                
lineWidth lineWidth === undefined 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 (shapeidxyradiuslineColorfillColorlineWidth) {
                var 
strokefillvel;
                
-= radius;
                
-= 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:' 'px; left:' 'px; width:' + (radius 2) + 'px; height:' + (radius 2) + 'px"></v:oval>';
                return 
vel;

            },

            
_drawPieSlice: function (shapeidxyradiusstartAngleendAnglelineColorfillColor) {
                var 
vpathstartxstartyendxendystrokefillvel;
                if (
startAngle === endAngle) {
                    return 
'';  // VML seems to have problem when start angle equals end angle.
                
}
                if ((
endAngle startAngle) === (Math.PI)) {
                    
startAngle 0.0;  // VML seems to have a problem when drawing a full circle that doesn't start 0
                    
endAngle = (Math.PI);
                }

                
startx Math.round(Math.cos(startAngle) * radius);
                
starty Math.round(Math.sin(startAngle) * radius);
                
endx Math.round(Math.cos(endAngle) * radius);
                
endy 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 radius;
                    
starty endy y;
                }

                if (
startx === endx && starty === endy && (endAngle startAngle) < Math.PI) {
                    return 
'';
                }

                
vpath = [radiusradiusradiusradiusstartxstartyendxendy];
                
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 ' ',' ' wa ' vpath.join(', ') + ' x e">' +
                    
' </v:shape>';
                return 
vel;
            },

            
_drawRect: function (shapeidxywidthheightlineColorfillColor) {
                return 
this._drawShape(shapeid, [
                    [
xy],
                    [
xheight],
                    [
widthheight],
                    [
widthy],
                    [
xy]
                ], 
lineColorfillColor);
            },

            
reset: function () {
                
this.group.innerHTML '';
            },

            
appendShape: function (shape) {
                var 
vel this['_draw' shape.type].apply(thisshape.args);
                if (
this.rendered) {
                    
this.group.insertAdjacentHTML('beforeEnd'vel);
                } else {
                    
this.prerender += vel;
                }
                
this.lastShapeId shape.id;
                return 
shape.id;
            },

            
replaceWithShape: function (shapeidshape) {
                var 
existing = $('#jqsshape' shapeid),
                    
vel this['_draw' shape.type].apply(thisshape.args);
                
existing[0].outerHTML vel;
            },

            
replaceWithShapes: function (shapeidsshapes) {
                
// 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 (
0sleni++) {
                    
replace += this['_draw' shapes[i].type].apply(thisshapes[i].args);
                }
                
existing[0].outerHTML replace;
                for (
1shapeids.lengthi++) {
                    $(
'#jqsshape' shapeids[i]).remove();
                }
            },

            
insertAfterShape: function (shapeidshape) {
                var 
existing = $('#jqsshape' shapeid),
                    
vel this['_draw' shape.type].apply(thisshape.args);
                
existing[0].insertAdjacentHTML('afterEnd'vel);
            },

            
removeShapeId: function (shapeid) {
                var 
existing = $('#jqsshape' shapeid);
                
this.group.removeChild(existing[0]);
            },

            
getShapeAt: function (elxy) {
                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;
                }
            }
        });

    }))
}(
documentMath));
?>
Онлайн: 4
Реклама