From: Steinar H. Gunderson Date: Sun, 21 Jun 2015 14:18:52 +0000 (+0200) Subject: Revert "Revert all the Sparkline optimizations; somehow they break something with... X-Git-Url: https://git.sesse.net/?p=remoteglot;a=commitdiff_plain;h=ce72ea0426221a7eed9b4091cf0ad72a557ac734;ds=sidebyside Revert "Revert all the Sparkline optimizations; somehow they break something with :hidden selectors." This reverts commit 1ced2c535eadfcde66aa95c6d33212ed31f861fe. The errors are about something else. --- diff --git a/www/js/jquery.sparkline.js b/www/js/jquery.sparkline.js index 721e03b..c59e8c9 100644 --- a/www/js/jquery.sparkline.js +++ b/www/js/jquery.sparkline.js @@ -59,19 +59,9 @@ *

Sparkline:

* $('.sparkline').sparkline(); * -* For line charts, x values can also be specified: -*

Sparkline: 1:1,2.7:4,3.4:6,5:6,6:8,8.7:5,9:3,10:5

-* $('#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}) -*

Sparkline: loading

-* 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 @@ -82,12 +72,7 @@ * 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, @@ -103,8 +88,6 @@ * 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) @@ -115,23 +98,7 @@ * 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 +* There is 1 type of sparkline, selected by supplying a "type" option of 'bar' (default), * * bar - Bar chart. Options: * barColor - Color of bars for postive values @@ -139,65 +106,17 @@ * 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 */ @@ -214,11 +133,11 @@ 'use strict'; var UNSET_OPTION = {}, - getDefaults, createClass, SPFormat, clipval, quartile, normalizeValue, normalizeValues, - remove, isNumber, all, sum, addCSS, ensureArray, formatNumber, RangeMap, + getDefaults, createClass, clipval, quartile, normalizeValue, normalizeValues, + remove, isNumber, all, sum, addCSS, ensureArray, formatNumber, MouseHandler, Tooltip, barHighlightMixin, - line, bar, tristate, discrete, bullet, pie, box, defaultStyles, initStyles, - VShape, VCanvas_base, VCanvas_canvas, VCanvas_vml, pending, shapeCount = 0; + bar, defaultStyles, initStyles, + VShape, VCanvas_base, VCanvas_canvas, pending, shapeCount = 0; /** * Default configuration settings @@ -227,16 +146,13 @@ return { // Settings common to most/all chart types common: { - type: 'line', + type: 'bar', lineColor: '#00f', fillColor: '#cdf', defaultPixelsPerValue: 3, width: 'auto', height: 'auto', - composite: false, tagValuesAttribute: 'values', - tagOptionsPrefix: 'spark', - enableTagOptions: false, enableHighlight: true, highlightLighten: 1.4, tooltipSkipNull: true, @@ -250,31 +166,10 @@ disableTooltips: false, disableInteraction: false }, - // Defaults for line charts - line: { - spotColor: '#f80', - highlightSpotColor: '#5f5', - highlightLineColor: '#f22', - spotRadius: 1.5, - minSpotColor: '#f80', - maxSpotColor: '#f80', - lineWidth: 1, - normalRangeMin: undefined, - normalRangeMax: undefined, - normalRangeColor: '#ccc', - drawNormalOnTop: false, - chartRangeMin: undefined, - chartRangeMax: undefined, - chartRangeMinX: undefined, - chartRangeMaxX: undefined, - tooltipFormat: new SPFormat(' {{prefix}}{{y}}{{suffix}}') - }, // Defaults for bar charts bar: { barColor: '#3366cc', negBarColor: '#f44', - stackedBarColor: ['#3366cc', '#dc3912', '#ff9900', '#109618', '#66aa00', - '#dd4477', '#0099c6', '#990099'], zeroColor: undefined, nullColor: undefined, zeroAxis: true, @@ -283,71 +178,8 @@ chartRangeMax: undefined, chartRangeMin: undefined, chartRangeClip: false, - colorMap: undefined, - tooltipFormat: new SPFormat(' {{prefix}}{{value}}{{suffix}}') - }, - // Defaults for tristate charts - tristate: { - barWidth: 4, - barSpacing: 1, - posBarColor: '#6f6', - negBarColor: '#f44', - zeroBarColor: '#999', - colorMap: {}, - tooltipFormat: new SPFormat(' {{value:map}}'), - tooltipValueLookups: { map: { '-1': 'Loss', '0': 'Draw', '1': 'Win' } } - }, - // Defaults for discrete charts - discrete: { - lineHeight: 'auto', - thresholdColor: undefined, - thresholdValue: 0, - chartRangeMax: undefined, - chartRangeMin: undefined, - chartRangeClip: false, - tooltipFormat: new SPFormat('{{prefix}}{{value}}{{suffix}}') - }, - // Defaults for bullet charts - bullet: { - targetColor: '#f33', - targetWidth: 3, // width of the target bar in pixels - performanceColor: '#33f', - rangeColors: ['#d3dafe', '#a8b6ff', '#7f94ff'], - base: undefined, // set this to a number to change the base start number - tooltipFormat: new SPFormat('{{fieldkey:fields}} - {{value}}'), - tooltipValueLookups: { fields: {r: 'Range', p: 'Performance', t: 'Target'} } + tooltipFormat: '', }, - // Defaults for pie charts - pie: { - offset: 0, - sliceColors: ['#3366cc', '#dc3912', '#ff9900', '#109618', '#66aa00', - '#dd4477', '#0099c6', '#990099'], - borderWidth: 0, - borderColor: '#000', - tooltipFormat: new SPFormat(' {{value}} ({{percent.1}}%)') - }, - // Defaults for box plots - box: { - raw: false, - boxLineColor: '#000', - boxFillColor: '#cdf', - whiskerColor: '#000', - outlierLineColor: '#333', - outlierFillColor: '#fff', - medianColor: '#f00', - showOutliers: true, - outlierIQR: 1.5, - spotRadius: 1.5, - target: undefined, - targetColor: '#4a2', - chartRangeMax: undefined, - chartRangeMin: undefined, - tooltipFormat: new SPFormat('{{field:fields}}: {{value}}'), - tooltipFormatFieldlistKey: 'field', - tooltipValueLookups: { fields: { lq: 'Lower Quartile', med: 'Median', - uq: 'Upper Quartile', lo: 'Left Outlier', ro: 'Right Outlier', - lw: 'Left Whisker', rw: 'Right Whisker'} } - } }; }; @@ -403,68 +235,6 @@ return Class; }; - /** - * Wraps a format string for tooltips - * {{x}} - * {{x.2} - * {{x:months}} - */ - $.SPFormatClass = SPFormat = createClass({ - fre: /\{\{([\w.]+?)(:(.+?))?\}\}/g, - precre: /(\w+)\.(\d+)/, - - init: function (format, fclass) { - this.format = format; - this.fclass = fclass; - }, - - render: function (fieldset, lookups, options) { - var self = this, - fields = fieldset, - match, token, lookupkey, fieldvalue, prec; - return this.format.replace(this.fre, function () { - var lookup; - token = arguments[1]; - lookupkey = arguments[3]; - match = self.precre.exec(token); - if (match) { - prec = match[2]; - token = match[1]; - } else { - prec = false; - } - fieldvalue = fields[token]; - if (fieldvalue === undefined) { - return ''; - } - if (lookupkey && lookups && lookups[lookupkey]) { - lookup = lookups[lookupkey]; - if (lookup.get) { // RangeMap - return lookups[lookupkey].get(fieldvalue) || fieldvalue; - } else { - return lookups[lookupkey][fieldvalue] || fieldvalue; - } - } - if (isNumber(fieldvalue)) { - if (options.get('numberFormatter')) { - fieldvalue = options.get('numberFormatter')(fieldvalue); - } else { - fieldvalue = formatNumber(fieldvalue, prec, - options.get('numberDigitGroupCount'), - options.get('numberDigitGroupSep'), - options.get('numberDecimalMark')); - } - } - return fieldvalue; - }); - } - }); - - // convience method to avoid needing the new operator - $.spformat = function(format, fclass) { - return new SPFormat(format, fclass); - }; - clipval = function (val, min, max) { if (val < min) { return min; @@ -610,12 +380,6 @@ $.fn.sparkline.canvas = function(width, height, target, interact) { return new VCanvas_canvas(width, height, target, interact); }; - } else if (document.namespaces && !document.namespaces.v) { - // VML is available - document.namespaces.add('v', 'urn:schemas-microsoft-com:vml', '#default#VML'); - $.fn.sparkline.canvas = function(width, height, target, interact) { - return new VCanvas_vml(width, height, target); - }; } else { // Neither Canvas nor VML are available $.fn.sparkline.canvas = false; @@ -646,45 +410,6 @@ } }; - $.RangeMapClass = RangeMap = createClass({ - init: function (map) { - var key, range, rangelist = []; - for (key in map) { - if (map.hasOwnProperty(key) && typeof key === 'string' && key.indexOf(':') > -1) { - range = key.split(':'); - range[0] = range[0].length === 0 ? -Infinity : parseFloat(range[0]); - range[1] = range[1].length === 0 ? Infinity : parseFloat(range[1]); - range[2] = map[key]; - rangelist.push(range); - } - } - this.map = map; - this.rangelist = rangelist || false; - }, - - get: function (value) { - var rangelist = this.rangelist, - i, range, result; - if ((result = this.map[value]) !== undefined) { - return result; - } - if (rangelist) { - for (i = rangelist.length; i--;) { - range = rangelist[i]; - if (range[0] <= value && range[1] >= value) { - return range[2]; - } - } - } - return undefined; - } - }); - - // Convenience function - $.range_map = function(map) { - return new RangeMap(map); - }; - MouseHandler = createClass({ init: function (el, options) { var $el = $(el); @@ -958,7 +683,6 @@ 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'; @@ -966,7 +690,6 @@ height = $(tmp).innerHeight() || $(tmp).height(); $(tmp).remove(); tmp = null; - } } else { height = options.get('height'); } @@ -976,21 +699,13 @@ if (!mhandler) { mhandler = new MouseHandler(this, options); $.data(this, '_jqs_mhandler', mhandler); - } else if (!options.get('composite')) { + } else { mhandler.reset(); } } else { mhandler = false; } - if (options.get('composite') && !$.data(this, '_jqs_vcanvas')) { - if (!$.data(this, '_jqs_errnotify')) { - alert('Attempted to attach a composite sparkline to an element with no existing sparkline'); - $.data(this, '_jqs_errnotify', true); - } - return; - } - sp = new $.fn.sparkline[options.get('type')](this, values, options, width, height); sp.render(); @@ -1000,7 +715,7 @@ } }; if (($(this).html() && !options.get('disableHiddenCheck') && $(this).is(':hidden')) || !$(this).parents('body').length) { - if (!options.get('composite') && $.data(this, '_jqs_pending')) { + if ($.data(this, '_jqs_pending')) { // remove any existing references to the element for (i = pending.length; i; i--) { if (pending[i - 1][0] == this) { @@ -1048,62 +763,20 @@ */ $.fn.sparkline.options = createClass({ init: function (tag, userOptions) { - var extendedOptions, defaults, base, tagOptionType; + var extendedOptions, defaults, base; 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]; - } + extendedOptions = defaults[userOptions.type || base.type]; this.mergedOptions = $.extend({}, base, extendedOptions, userOptions); }, - getTagSetting: function (key) { - var prefix = this.tagOptionsPrefix, - val, i, pairs, keyval; - if (prefix === false || prefix === undefined) { - return UNSET_OPTION; - } - if (this.tagValCache.hasOwnProperty(key)) { - val = this.tagValCache.key; - } else { - val = this.tag.getAttribute(prefix + key); - if (val === undefined || val === null) { - val = UNSET_OPTION; - } else if (val.substr(0, 1) === '[') { - val = val.substr(1, val.length - 2).split(','); - for (i = val.length; i--;) { - val[i] = normalizeValue(val[i].replace(/(^\s*)|(\s*$)/g, '')); - } - } else if (val.substr(0, 1) === '{') { - pairs = val.substr(1, val.length - 2).split(','); - val = {}; - for (i = pairs.length; i--;) { - keyval = pairs[i].split(':', 2); - val[keyval[0].replace(/(^\s*)|(\s*$)/g, '')] = normalizeValue(keyval[1].replace(/(^\s*)|(\s*$)/g, '')); - } - } else { - val = normalizeValue(val); - } - this.tagValCache.key = val; - } - return val; - }, - get: function (key, defaultval) { - var tagOption = this.getTagSetting(key), - result; - if (tagOption !== UNSET_OPTION) { - return tagOption; - } + var result; return (result = this.mergedOptions[key]) === undefined ? defaultval : result; } }); @@ -1127,7 +800,7 @@ */ initTarget: function () { var interactive = !this.options.get('disableInteraction'); - if (!(this.target = this.$el.simpledraw(this.width, this.height, this.options.get('composite'), interactive))) { + if (!(this.target = this.$el.simpledraw(this.width, this.height, false, interactive))) { this.disabled = true; } else { this.canvasWidth = this.target.pixelWidth; @@ -1246,9 +919,6 @@ fieldlen = fields.length; for (i = 0; i < formatlen; i++) { format = formats[i]; - if (typeof format === 'string') { - format = new SPFormat(format); - } fclass = format.fclass || 'jqsfield'; for (j = 0; j < fieldlen; j++) { if (!fields[j].isNull || !options.get('tooltipSkipNull')) { @@ -1347,356 +1017,6 @@ } }; - /** - * Line charts - */ - $.fn.sparkline.line = line = createClass($.fn.sparkline._base, { - type: 'line', - - init: function (el, values, options, width, height) { - line._super.init.call(this, el, values, options, width, height); - this.vertices = []; - this.regionMap = []; - this.xvalues = []; - this.yvalues = []; - this.yminmax = []; - this.hightlightSpotId = null; - this.lastShapeId = null; - this.initTarget(); - }, - - getRegion: function (el, x, y) { - var i, - regionMap = this.regionMap; // maps regions to value positions - for (i = regionMap.length; i--;) { - if (regionMap[i] !== null && x >= regionMap[i][0] && x <= regionMap[i][1]) { - return regionMap[i][2]; - } - } - return undefined; - }, - - getCurrentRegionFields: function () { - var currentRegion = this.currentRegion; - return { - isNull: this.yvalues[currentRegion] === null, - x: this.xvalues[currentRegion], - y: this.yvalues[currentRegion], - color: this.options.get('lineColor'), - fillColor: this.options.get('fillColor'), - offset: currentRegion - }; - }, - - renderHighlight: function () { - var currentRegion = this.currentRegion, - target = this.target, - vertex = this.vertices[currentRegion], - options = this.options, - spotRadius = options.get('spotRadius'), - highlightSpotColor = options.get('highlightSpotColor'), - highlightLineColor = options.get('highlightLineColor'), - highlightSpot, highlightLine; - - if (!vertex) { - return; - } - if (spotRadius && highlightSpotColor) { - highlightSpot = target.drawCircle(vertex[0], vertex[1], - spotRadius, undefined, highlightSpotColor); - this.highlightSpotId = highlightSpot.id; - target.insertAfterShape(this.lastShapeId, highlightSpot); - } - if (highlightLineColor) { - highlightLine = target.drawLine(vertex[0], this.canvasTop, vertex[0], - this.canvasTop + this.canvasHeight, highlightLineColor); - this.highlightLineId = highlightLine.id; - target.insertAfterShape(this.lastShapeId, highlightLine); - } - }, - - removeHighlight: function () { - var target = this.target; - if (this.highlightSpotId) { - target.removeShapeId(this.highlightSpotId); - this.highlightSpotId = null; - } - if (this.highlightLineId) { - target.removeShapeId(this.highlightLineId); - this.highlightLineId = null; - } - }, - - scanValues: function () { - var values = this.values, - valcount = values.length, - xvalues = this.xvalues, - yvalues = this.yvalues, - yminmax = this.yminmax, - i, val, isStr, isArray, sp; - for (i = 0; i < valcount; i++) { - val = values[i]; - isStr = typeof(values[i]) === 'string'; - isArray = typeof(values[i]) === 'object' && values[i] instanceof Array; - sp = isStr && values[i].split(':'); - if (isStr && sp.length === 2) { // x:y - xvalues.push(Number(sp[0])); - yvalues.push(Number(sp[1])); - yminmax.push(Number(sp[1])); - } else if (isArray) { - xvalues.push(val[0]); - yvalues.push(val[1]); - yminmax.push(val[1]); - } else { - xvalues.push(i); - if (values[i] === null || values[i] === 'null') { - yvalues.push(null); - } else { - yvalues.push(Number(val)); - yminmax.push(Number(val)); - } - } - } - if (this.options.get('xvalues')) { - xvalues = this.options.get('xvalues'); - } - - this.maxy = this.maxyorg = Math.max.apply(Math, yminmax); - this.miny = this.minyorg = Math.min.apply(Math, yminmax); - - this.maxx = Math.max.apply(Math, xvalues); - this.minx = Math.min.apply(Math, xvalues); - - this.xvalues = xvalues; - this.yvalues = yvalues; - this.yminmax = yminmax; - - }, - - processRangeOptions: function () { - var options = this.options, - normalRangeMin = options.get('normalRangeMin'), - normalRangeMax = options.get('normalRangeMax'); - - if (normalRangeMin !== undefined) { - if (normalRangeMin < this.miny) { - this.miny = normalRangeMin; - } - if (normalRangeMax > this.maxy) { - this.maxy = normalRangeMax; - } - } - if (options.get('chartRangeMin') !== undefined && (options.get('chartRangeClip') || options.get('chartRangeMin') < this.miny)) { - this.miny = options.get('chartRangeMin'); - } - if (options.get('chartRangeMax') !== undefined && (options.get('chartRangeClip') || options.get('chartRangeMax') > this.maxy)) { - this.maxy = options.get('chartRangeMax'); - } - if (options.get('chartRangeMinX') !== undefined && (options.get('chartRangeClipX') || options.get('chartRangeMinX') < this.minx)) { - this.minx = options.get('chartRangeMinX'); - } - if (options.get('chartRangeMaxX') !== undefined && (options.get('chartRangeClipX') || options.get('chartRangeMaxX') > this.maxx)) { - this.maxx = options.get('chartRangeMaxX'); - } - - }, - - drawNormalRange: function (canvasLeft, canvasTop, canvasHeight, canvasWidth, rangey) { - var normalRangeMin = this.options.get('normalRangeMin'), - normalRangeMax = this.options.get('normalRangeMax'), - ytop = canvasTop + Math.round(canvasHeight - (canvasHeight * ((normalRangeMax - this.miny) / rangey))), - height = Math.round((canvasHeight * (normalRangeMax - normalRangeMin)) / rangey); - this.target.drawRect(canvasLeft, ytop, canvasWidth, height, undefined, this.options.get('normalRangeColor')).append(); - }, - - render: function () { - var options = this.options, - target = this.target, - canvasWidth = this.canvasWidth, - canvasHeight = this.canvasHeight, - vertices = this.vertices, - spotRadius = options.get('spotRadius'), - regionMap = this.regionMap, - rangex, rangey, yvallast, - canvasTop, canvasLeft, - vertex, path, paths, x, y, xnext, xpos, xposnext, - last, next, yvalcount, lineShapes, fillShapes, plen, - valueSpots, hlSpotsEnabled, color, xvalues, yvalues, i; - - if (!line._super.render.call(this)) { - return; - } - - this.scanValues(); - this.processRangeOptions(); - - xvalues = this.xvalues; - yvalues = this.yvalues; - - if (!this.yminmax.length || this.yvalues.length < 2) { - // empty or all null valuess - return; - } - - canvasTop = canvasLeft = 0; - - rangex = this.maxx - this.minx === 0 ? 1 : this.maxx - this.minx; - rangey = this.maxy - this.miny === 0 ? 1 : this.maxy - this.miny; - yvallast = this.yvalues.length - 1; - - if (spotRadius && (canvasWidth < (spotRadius * 4) || canvasHeight < (spotRadius * 4))) { - spotRadius = 0; - } - if (spotRadius) { - // adjust the canvas size as required so that spots will fit - hlSpotsEnabled = options.get('highlightSpotColor') && !options.get('disableInteraction'); - if (hlSpotsEnabled || options.get('minSpotColor') || (options.get('spotColor') && yvalues[yvallast] === this.miny)) { - canvasHeight -= Math.ceil(spotRadius); - } - if (hlSpotsEnabled || options.get('maxSpotColor') || (options.get('spotColor') && yvalues[yvallast] === this.maxy)) { - canvasHeight -= Math.ceil(spotRadius); - canvasTop += Math.ceil(spotRadius); - } - if (hlSpotsEnabled || - ((options.get('minSpotColor') || options.get('maxSpotColor')) && (yvalues[0] === this.miny || yvalues[0] === this.maxy))) { - canvasLeft += Math.ceil(spotRadius); - canvasWidth -= Math.ceil(spotRadius); - } - if (hlSpotsEnabled || options.get('spotColor') || - (options.get('minSpotColor') || options.get('maxSpotColor') && - (yvalues[yvallast] === this.miny || yvalues[yvallast] === this.maxy))) { - canvasWidth -= Math.ceil(spotRadius); - } - } - - - canvasHeight--; - - if (options.get('normalRangeMin') !== undefined && !options.get('drawNormalOnTop')) { - this.drawNormalRange(canvasLeft, canvasTop, canvasHeight, canvasWidth, rangey); - } - - path = []; - paths = [path]; - last = next = null; - yvalcount = yvalues.length; - for (i = 0; i < yvalcount; i++) { - x = xvalues[i]; - xnext = xvalues[i + 1]; - y = yvalues[i]; - xpos = canvasLeft + Math.round((x - this.minx) * (canvasWidth / rangex)); - xposnext = i < yvalcount - 1 ? canvasLeft + Math.round((xnext - this.minx) * (canvasWidth / rangex)) : canvasWidth; - next = xpos + ((xposnext - xpos) / 2); - regionMap[i] = [last || 0, next, i]; - last = next; - if (y === null) { - if (i) { - if (yvalues[i - 1] !== null) { - path = []; - paths.push(path); - } - vertices.push(null); - } - } else { - if (y < this.miny) { - y = this.miny; - } - if (y > this.maxy) { - y = this.maxy; - } - if (!path.length) { - // previous value was null - path.push([xpos, canvasTop + canvasHeight]); - } - vertex = [xpos, canvasTop + Math.round(canvasHeight - (canvasHeight * ((y - this.miny) / rangey)))]; - path.push(vertex); - vertices.push(vertex); - } - } - - lineShapes = []; - fillShapes = []; - plen = paths.length; - for (i = 0; i < plen; i++) { - path = paths[i]; - if (path.length) { - if (options.get('fillColor')) { - path.push([path[path.length - 1][0], (canvasTop + canvasHeight)]); - fillShapes.push(path.slice(0)); - path.pop(); - } - // if there's only a single point in this path, then we want to display it - // as a vertical line which means we keep path[0] as is - if (path.length > 2) { - // else we want the first value - path[0] = [path[0][0], path[1][1]]; - } - lineShapes.push(path); - } - } - - // draw the fill first, then optionally the normal range, then the line on top of that - plen = fillShapes.length; - for (i = 0; i < plen; i++) { - target.drawShape(fillShapes[i], - options.get('fillColor'), options.get('fillColor')).append(); - } - - if (options.get('normalRangeMin') !== undefined && options.get('drawNormalOnTop')) { - this.drawNormalRange(canvasLeft, canvasTop, canvasHeight, canvasWidth, rangey); - } - - plen = lineShapes.length; - for (i = 0; i < plen; i++) { - target.drawShape(lineShapes[i], options.get('lineColor'), undefined, - options.get('lineWidth')).append(); - } - - if (spotRadius && options.get('valueSpots')) { - valueSpots = options.get('valueSpots'); - if (valueSpots.get === undefined) { - valueSpots = new RangeMap(valueSpots); - } - for (i = 0; i < yvalcount; i++) { - color = valueSpots.get(yvalues[i]); - if (color) { - target.drawCircle(canvasLeft + Math.round((xvalues[i] - this.minx) * (canvasWidth / rangex)), - canvasTop + Math.round(canvasHeight - (canvasHeight * ((yvalues[i] - this.miny) / rangey))), - spotRadius, undefined, - color).append(); - } - } - - } - if (spotRadius && options.get('spotColor') && yvalues[yvallast] !== null) { - target.drawCircle(canvasLeft + Math.round((xvalues[xvalues.length - 1] - this.minx) * (canvasWidth / rangex)), - canvasTop + Math.round(canvasHeight - (canvasHeight * ((yvalues[yvallast] - this.miny) / rangey))), - spotRadius, undefined, - options.get('spotColor')).append(); - } - if (this.maxy !== this.minyorg) { - if (spotRadius && options.get('minSpotColor')) { - x = xvalues[$.inArray(this.minyorg, yvalues)]; - target.drawCircle(canvasLeft + Math.round((x - this.minx) * (canvasWidth / rangex)), - canvasTop + Math.round(canvasHeight - (canvasHeight * ((this.minyorg - this.miny) / rangey))), - spotRadius, undefined, - options.get('minSpotColor')).append(); - } - if (spotRadius && options.get('maxSpotColor')) { - x = xvalues[$.inArray(this.maxyorg, yvalues)]; - target.drawCircle(canvasLeft + Math.round((x - this.minx) * (canvasWidth / rangex)), - canvasTop + Math.round(canvasHeight - (canvasHeight * ((this.maxyorg - this.miny) / rangey))), - spotRadius, undefined, - options.get('maxSpotColor')).append(); - } - } - - this.lastShapeId = target.getLastShapeId(); - this.canvasTop = canvasTop; - target.render(); - } - }); - /** * Bar charts */ @@ -1709,35 +1029,11 @@ chartRangeMin = options.get('chartRangeMin'), chartRangeMax = options.get('chartRangeMax'), chartRangeClip = options.get('chartRangeClip'), - stackMin = Infinity, - stackMax = -Infinity, - isStackString, groupMin, groupMax, stackRanges, + groupMin, groupMax, numValues, i, vlen, range, zeroAxis, xaxisOffset, min, max, clipMin, clipMax, - stacked, vlist, j, slen, svals, val, yoffset, yMaxCalc, canvasHeightEf; + vlist, j, slen, svals, val, yoffset, yMaxCalc, canvasHeightEf; bar._super.init.call(this, el, values, options, width, height); - // scan values to determine whether to stack bars - for (i = 0, vlen = values.length; i < vlen; i++) { - val = values[i]; - isStackString = typeof(val) === 'string' && val.indexOf(':') > -1; - if (isStackString || $.isArray(val)) { - stacked = true; - if (isStackString) { - val = values[i] = normalizeValues(val.split(':')); - } - val = remove(val, null); // min/max will treat null as zero - groupMin = Math.min.apply(Math, val); - groupMax = Math.max.apply(Math, val); - if (groupMin < stackMin) { - stackMin = groupMin; - } - if (groupMax > stackMax) { - stackMax = groupMax; - } - } - } - - this.stacked = stacked; this.regionShapes = {}; this.barWidth = barWidth; this.barSpacing = barSpacing; @@ -1752,45 +1048,15 @@ } numValues = []; - stackRanges = stacked ? [] : numValues; - var stackTotals = []; - var stackRangesNeg = []; for (i = 0, vlen = values.length; i < vlen; i++) { - if (stacked) { - vlist = values[i]; - values[i] = svals = []; - stackTotals[i] = 0; - stackRanges[i] = stackRangesNeg[i] = 0; - for (j = 0, slen = vlist.length; j < slen; j++) { - val = svals[j] = chartRangeClip ? clipval(vlist[j], clipMin, clipMax) : vlist[j]; - if (val !== null) { - if (val > 0) { - stackTotals[i] += val; - } - if (stackMin < 0 && stackMax > 0) { - if (val < 0) { - stackRangesNeg[i] += Math.abs(val); - } else { - stackRanges[i] += val; - } - } else { - stackRanges[i] += Math.abs(val - (val < 0 ? stackMax : stackMin)); - } - numValues.push(val); - } - } - } else { val = chartRangeClip ? clipval(values[i], clipMin, clipMax) : values[i]; val = values[i] = normalizeValue(val); if (val !== null) { numValues.push(val); } - } } this.max = max = Math.max.apply(Math, numValues); this.min = min = Math.min.apply(Math, numValues); - this.stackMax = stackMax = stacked ? Math.max.apply(Math, stackTotals) : max; - this.stackMin = stackMin = stacked ? Math.min.apply(Math, numValues) : min; if (options.get('chartRangeMin') !== undefined && (options.get('chartRangeClip') || options.get('chartRangeMin') < min)) { min = options.get('chartRangeMin'); @@ -1811,14 +1077,14 @@ } this.xaxisOffset = xaxisOffset; - range = stacked ? (Math.max.apply(Math, stackRanges) + Math.max.apply(Math, stackRangesNeg)) : max - min; + range = max - min; // as we plot zero/min values a single pixel line, we add a pixel to all other // values - Reduce the effective canvas size to suit this.canvasHeightEf = (zeroAxis && min < 0) ? this.canvasHeight - 2 : this.canvasHeight - 1; if (min < xaxisOffset) { - yMaxCalc = (stacked && max >= 0) ? stackMax : max; + yMaxCalc = max; yoffset = (yMaxCalc - xaxisOffset) / range * this.canvasHeight; if (yoffset !== Math.ceil(yoffset)) { this.canvasHeightEf -= 2; @@ -1829,17 +1095,6 @@ } 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; }, @@ -1866,24 +1121,13 @@ }, calcColor: function (stacknum, value, valuenum) { - var colorMapByIndex = this.colorMapByIndex, - colorMapByValue = this.colorMapByValue, - options = this.options, - color, newColor; - if (this.stacked) { - color = options.get('stackedBarColor'); - } else { + var color, newColor, + options = this.options; color = (value < 0) ? options.get('negBarColor') : options.get('barColor'); - } if (value === 0 && options.get('zeroColor') !== undefined) { color = options.get('zeroColor'); } - if (colorMapByValue && (newColor = colorMapByValue.get(value))) { - color = newColor; - } else if (colorMapByIndex && colorMapByIndex.length > valuenum) { - color = colorMapByIndex[valuenum]; - } - return $.isArray(color) ? color[stacknum % color.length] : color; + return color; }, /** @@ -1895,7 +1139,6 @@ xaxisOffset = this.xaxisOffset, result = [], range = this.range, - stacked = this.stacked, target = this.target, x = valuenum * this.totalBarWidth, canvasHeightEf = this.canvasHeightEf, @@ -1921,13 +1164,6 @@ for (i = 0; i < valcount; i++) { val = vals[i]; - if (stacked && val === xaxisOffset) { - if (!allMin || minPlotted) { - continue; - } - minPlotted = true; - } - if (range > 0) { height = Math.floor(canvasHeightEf * ((Math.abs(val - xaxisOffset) / range))) + 1; } else { @@ -1953,630 +1189,37 @@ } }); - /** - * Tristate charts - */ - $.fn.sparkline.tristate = tristate = createClass($.fn.sparkline._base, barHighlightMixin, { - type: 'tristate', + // Setup a very simple "virtual canvas" to make drawing the few shapes we need easier + // This is accessible as $(foo).simpledraw() - init: function (el, values, options, width, height) { - var barWidth = parseInt(options.get('barWidth'), 10), - barSpacing = parseInt(options.get('barSpacing'), 10); - tristate._super.init.call(this, el, values, options, width, height); + VShape = createClass({ + init: function (target, id, type, args) { + this.target = target; + this.id = id; + this.type = type; + this.args = args; + }, + append: function () { + this.target.appendShape(this); + return this; + } + }); - this.regionShapes = {}; - this.barWidth = barWidth; - this.barSpacing = barSpacing; - this.totalBarWidth = barWidth + barSpacing; - this.values = $.map(values, Number); - this.width = width = (values.length * barWidth) + ((values.length - 1) * barSpacing); + VCanvas_base = createClass({ + _pxregex: /(\d+)(px)?\s*$/i, - 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); - } + init: function (width, height, target) { + if (!width) { + return; } - this.initTarget(); - }, - - getRegion: function (el, x, y) { - return Math.floor(x / this.totalBarWidth); - }, - - getCurrentRegionFields: function () { - var currentRegion = this.currentRegion; - return { - isNull: this.values[currentRegion] === undefined, - value: this.values[currentRegion], - color: this.calcColor(this.values[currentRegion], currentRegion), - offset: currentRegion - }; - }, - - calcColor: function (value, valuenum) { - var values = this.values, - options = this.options, - colorMapByIndex = this.colorMapByIndex, - colorMapByValue = this.colorMapByValue, - color, newColor; - - if (colorMapByValue && (newColor = colorMapByValue.get(value))) { - color = newColor; - } else if (colorMapByIndex && colorMapByIndex.length > valuenum) { - color = colorMapByIndex[valuenum]; - } else if (values[valuenum] < 0) { - color = options.get('negBarColor'); - } else if (values[valuenum] > 0) { - color = options.get('posBarColor'); - } else { - color = options.get('zeroBarColor'); - } - return color; - }, - - renderRegion: function (valuenum, highlight) { - var values = this.values, - options = this.options, - target = this.target, - canvasHeight, height, halfHeight, - x, y, color; - - canvasHeight = target.pixelHeight; - halfHeight = Math.round(canvasHeight / 2); - - x = valuenum * this.totalBarWidth; - if (values[valuenum] < 0) { - y = halfHeight; - height = halfHeight - 1; - } else if (values[valuenum] > 0) { - y = 0; - height = halfHeight - 1; - } else { - y = halfHeight - 1; - height = 2; - } - color = this.calcColor(values[valuenum], valuenum); - if (color === null) { - return; - } - if (highlight) { - color = this.calcHighlightColor(color, options); - } - return target.drawRect(x, y, this.barWidth - 1, height - 1, color, color); - } - }); - - /** - * Discrete charts - */ - $.fn.sparkline.discrete = discrete = createClass($.fn.sparkline._base, barHighlightMixin, { - type: 'discrete', - - init: function (el, values, options, width, height) { - discrete._super.init.call(this, el, values, options, width, height); - - this.regionShapes = {}; - this.values = values = $.map(values, Number); - this.min = Math.min.apply(Math, values); - this.max = Math.max.apply(Math, values); - this.range = this.max - this.min; - this.width = width = options.get('width') === 'auto' ? values.length * 2 : this.width; - this.interval = Math.floor(width / values.length); - this.itemWidth = width / values.length; - if (options.get('chartRangeMin') !== undefined && (options.get('chartRangeClip') || options.get('chartRangeMin') < this.min)) { - this.min = options.get('chartRangeMin'); - } - if (options.get('chartRangeMax') !== undefined && (options.get('chartRangeClip') || options.get('chartRangeMax') > this.max)) { - this.max = options.get('chartRangeMax'); - } - this.initTarget(); - if (this.target) { - this.lineHeight = options.get('lineHeight') === 'auto' ? Math.round(this.canvasHeight * 0.3) : options.get('lineHeight'); - } - }, - - getRegion: function (el, x, y) { - return Math.floor(x / this.itemWidth); - }, - - getCurrentRegionFields: function () { - var currentRegion = this.currentRegion; - return { - isNull: this.values[currentRegion] === undefined, - value: this.values[currentRegion], - offset: currentRegion - }; - }, - - renderRegion: function (valuenum, highlight) { - var values = this.values, - options = this.options, - min = this.min, - max = this.max, - range = this.range, - interval = this.interval, - target = this.target, - canvasHeight = this.canvasHeight, - lineHeight = this.lineHeight, - pheight = canvasHeight - lineHeight, - ytop, val, color, x; - - val = clipval(values[valuenum], min, max); - x = valuenum * interval; - ytop = Math.round(pheight - pheight * ((val - min) / range)); - color = (options.get('thresholdColor') && val < options.get('thresholdValue')) ? options.get('thresholdColor') : options.get('lineColor'); - if (highlight) { - color = this.calcHighlightColor(color, options); - } - return target.drawLine(x, ytop, x, ytop + lineHeight, color); - } - }); - - /** - * Bullet charts - */ - $.fn.sparkline.bullet = bullet = createClass($.fn.sparkline._base, { - type: 'bullet', - - init: function (el, values, options, width, height) { - var min, max, vals; - bullet._super.init.call(this, el, values, options, width, height); - - // values: target, performance, range1, range2, range3 - this.values = values = normalizeValues(values); - // target or performance could be null - vals = values.slice(); - vals[0] = vals[0] === null ? vals[2] : vals[0]; - vals[1] = values[1] === null ? vals[2] : vals[1]; - min = Math.min.apply(Math, values); - max = Math.max.apply(Math, values); - if (options.get('base') === undefined) { - min = min < 0 ? min : 0; - } else { - min = options.get('base'); - } - this.min = min; - this.max = max; - this.range = max - min; - this.shapes = {}; - this.valueShapes = {}; - this.regiondata = {}; - this.width = width = options.get('width') === 'auto' ? '4.0em' : width; - this.target = this.$el.simpledraw(width, height, options.get('composite')); - if (!values.length) { - this.disabled = true; - } - this.initTarget(); - }, - - getRegion: function (el, x, y) { - var shapeid = this.target.getShapeAt(el, x, y); - return (shapeid !== undefined && this.shapes[shapeid] !== undefined) ? this.shapes[shapeid] : undefined; - }, - - getCurrentRegionFields: function () { - var currentRegion = this.currentRegion; - return { - fieldkey: currentRegion.substr(0, 1), - value: this.values[currentRegion.substr(1)], - region: currentRegion - }; - }, - - changeHighlight: function (highlight) { - var currentRegion = this.currentRegion, - shapeid = this.valueShapes[currentRegion], - shape; - delete this.shapes[shapeid]; - switch (currentRegion.substr(0, 1)) { - case 'r': - shape = this.renderRange(currentRegion.substr(1), highlight); - break; - case 'p': - shape = this.renderPerformance(highlight); - break; - case 't': - shape = this.renderTarget(highlight); - break; - } - this.valueShapes[currentRegion] = shape.id; - this.shapes[shape.id] = currentRegion; - this.target.replaceWithShape(shapeid, shape); - }, - - renderRange: function (rn, highlight) { - var rangeval = this.values[rn], - rangewidth = Math.round(this.canvasWidth * ((rangeval - this.min) / this.range)), - color = this.options.get('rangeColors')[rn - 2]; - if (highlight) { - color = this.calcHighlightColor(color, this.options); - } - return this.target.drawRect(0, 0, rangewidth - 1, this.canvasHeight - 1, color, color); - }, - - renderPerformance: function (highlight) { - var perfval = this.values[1], - perfwidth = Math.round(this.canvasWidth * ((perfval - this.min) / this.range)), - color = this.options.get('performanceColor'); - if (highlight) { - color = this.calcHighlightColor(color, this.options); - } - return this.target.drawRect(0, Math.round(this.canvasHeight * 0.3), perfwidth - 1, - Math.round(this.canvasHeight * 0.4) - 1, color, color); - }, - - renderTarget: function (highlight) { - var targetval = this.values[0], - x = Math.round(this.canvasWidth * ((targetval - this.min) / this.range) - (this.options.get('targetWidth') / 2)), - targettop = Math.round(this.canvasHeight * 0.10), - targetheight = this.canvasHeight - (targettop * 2), - color = this.options.get('targetColor'); - if (highlight) { - color = this.calcHighlightColor(color, this.options); - } - return this.target.drawRect(x, targettop, this.options.get('targetWidth') - 1, targetheight - 1, color, color); - }, - - render: function () { - var vlen = this.values.length, - target = this.target, - i, shape; - if (!bullet._super.render.call(this)) { - return; - } - for (i = 2; i < vlen; i++) { - shape = this.renderRange(i).append(); - this.shapes[shape.id] = 'r' + i; - this.valueShapes['r' + i] = shape.id; - } - if (this.values[1] !== null) { - shape = this.renderPerformance().append(); - this.shapes[shape.id] = 'p1'; - this.valueShapes.p1 = shape.id; - } - if (this.values[0] !== null) { - shape = this.renderTarget().append(); - this.shapes[shape.id] = 't0'; - this.valueShapes.t0 = shape.id; - } - target.render(); - } - }); - - /** - * Pie charts - */ - $.fn.sparkline.pie = pie = createClass($.fn.sparkline._base, { - type: 'pie', - - init: function (el, values, options, width, height) { - var total = 0, i; - - pie._super.init.call(this, el, values, options, width, height); - - this.shapes = {}; // map shape ids to value offsets - this.valueShapes = {}; // maps value offsets to shape ids - this.values = values = $.map(values, Number); - - if (options.get('width') === 'auto') { - this.width = this.height; - } - - if (values.length > 0) { - for (i = values.length; i--;) { - total += values[i]; - } - } - this.total = total; - this.initTarget(); - this.radius = Math.floor(Math.min(this.canvasWidth, this.canvasHeight) / 2); - }, - - getRegion: function (el, x, y) { - var shapeid = this.target.getShapeAt(el, x, y); - return (shapeid !== undefined && this.shapes[shapeid] !== undefined) ? this.shapes[shapeid] : undefined; - }, - - getCurrentRegionFields: function () { - var currentRegion = this.currentRegion; - return { - isNull: this.values[currentRegion] === undefined, - value: this.values[currentRegion], - percent: this.values[currentRegion] / this.total * 100, - color: this.options.get('sliceColors')[currentRegion % this.options.get('sliceColors').length], - offset: currentRegion - }; - }, - - changeHighlight: function (highlight) { - var currentRegion = this.currentRegion, - newslice = this.renderSlice(currentRegion, highlight), - shapeid = this.valueShapes[currentRegion]; - delete this.shapes[shapeid]; - this.target.replaceWithShape(shapeid, newslice); - this.valueShapes[currentRegion] = newslice.id; - this.shapes[newslice.id] = currentRegion; - }, - - renderSlice: function (valuenum, highlight) { - var target = this.target, - options = this.options, - radius = this.radius, - borderWidth = options.get('borderWidth'), - offset = options.get('offset'), - circle = 2 * Math.PI, - values = this.values, - total = this.total, - next = offset ? (2*Math.PI)*(offset/360) : 0, - start, end, i, vlen, color; - - vlen = values.length; - for (i = 0; i < vlen; i++) { - start = next; - end = next; - if (total > 0) { // avoid divide by zero - end = next + (circle * (values[i] / total)); - } - if (valuenum === i) { - color = options.get('sliceColors')[i % options.get('sliceColors').length]; - if (highlight) { - color = this.calcHighlightColor(color, options); - } - - return target.drawPieSlice(radius, radius, radius - borderWidth, start, end, undefined, color); - } - next = end; - } - }, - - render: function () { - var target = this.target, - values = this.values, - options = this.options, - radius = this.radius, - borderWidth = options.get('borderWidth'), - shape, i; - - if (!pie._super.render.call(this)) { - return; - } - if (borderWidth) { - target.drawCircle(radius, radius, Math.floor(radius - (borderWidth / 2)), - options.get('borderColor'), undefined, borderWidth).append(); - } - for (i = values.length; i--;) { - if (values[i]) { // don't render zero values - shape = this.renderSlice(i).append(); - this.valueShapes[i] = shape.id; // store just the shapeid - this.shapes[shape.id] = i; - } - } - target.render(); - } - }); - - /** - * Box plots - */ - $.fn.sparkline.box = box = createClass($.fn.sparkline._base, { - type: 'box', - - init: function (el, values, options, width, height) { - box._super.init.call(this, el, values, options, width, height); - this.values = $.map(values, Number); - this.width = options.get('width') === 'auto' ? '4.0em' : width; - this.initTarget(); - if (!this.values.length) { - this.disabled = 1; - } - }, - - /** - * Simulate a single region - */ - getRegion: function () { - return 1; - }, - - getCurrentRegionFields: function () { - var result = [ - { field: 'lq', value: this.quartiles[0] }, - { field: 'med', value: this.quartiles[1] }, - { field: 'uq', value: this.quartiles[2] } - ]; - if (this.loutlier !== undefined) { - result.push({ field: 'lo', value: this.loutlier}); - } - if (this.routlier !== undefined) { - result.push({ field: 'ro', value: this.routlier}); - } - if (this.lwhisker !== undefined) { - result.push({ field: 'lw', value: this.lwhisker}); - } - if (this.rwhisker !== undefined) { - result.push({ field: 'rw', value: this.rwhisker}); - } - return result; - }, - - render: function () { - var target = this.target, - values = this.values, - vlen = values.length, - options = this.options, - canvasWidth = this.canvasWidth, - canvasHeight = this.canvasHeight, - minValue = options.get('chartRangeMin') === undefined ? Math.min.apply(Math, values) : options.get('chartRangeMin'), - maxValue = options.get('chartRangeMax') === undefined ? Math.max.apply(Math, values) : options.get('chartRangeMax'), - canvasLeft = 0, - lwhisker, loutlier, iqr, q1, q2, q3, rwhisker, routlier, i, - size, unitSize; - - if (!box._super.render.call(this)) { - return; - } - - if (options.get('raw')) { - if (options.get('showOutliers') && values.length > 5) { - loutlier = values[0]; - lwhisker = values[1]; - q1 = values[2]; - q2 = values[3]; - q3 = values[4]; - rwhisker = values[5]; - routlier = values[6]; - } else { - lwhisker = values[0]; - q1 = values[1]; - q2 = values[2]; - q3 = values[3]; - rwhisker = values[4]; - } - } else { - values.sort(function (a, b) { return a - b; }); - q1 = quartile(values, 1); - q2 = quartile(values, 2); - q3 = quartile(values, 3); - iqr = q3 - q1; - if (options.get('showOutliers')) { - lwhisker = rwhisker = undefined; - for (i = 0; i < vlen; i++) { - if (lwhisker === undefined && values[i] > q1 - (iqr * options.get('outlierIQR'))) { - lwhisker = values[i]; - } - if (values[i] < q3 + (iqr * options.get('outlierIQR'))) { - rwhisker = values[i]; - } - } - loutlier = values[0]; - routlier = values[vlen - 1]; - } else { - lwhisker = values[0]; - rwhisker = values[vlen - 1]; - } - } - this.quartiles = [q1, q2, q3]; - this.lwhisker = lwhisker; - this.rwhisker = rwhisker; - this.loutlier = loutlier; - this.routlier = routlier; - - unitSize = canvasWidth / (maxValue - minValue + 1); - if (options.get('showOutliers')) { - canvasLeft = Math.ceil(options.get('spotRadius')); - canvasWidth -= 2 * Math.ceil(options.get('spotRadius')); - unitSize = canvasWidth / (maxValue - minValue + 1); - if (loutlier < lwhisker) { - target.drawCircle((loutlier - minValue) * unitSize + canvasLeft, - canvasHeight / 2, - options.get('spotRadius'), - options.get('outlierLineColor'), - options.get('outlierFillColor')).append(); - } - if (routlier > rwhisker) { - target.drawCircle((routlier - minValue) * unitSize + canvasLeft, - canvasHeight / 2, - options.get('spotRadius'), - options.get('outlierLineColor'), - options.get('outlierFillColor')).append(); - } - } - - // box - target.drawRect( - Math.round((q1 - minValue) * unitSize + canvasLeft), - Math.round(canvasHeight * 0.1), - Math.round((q3 - q1) * unitSize), - Math.round(canvasHeight * 0.8), - options.get('boxLineColor'), - options.get('boxFillColor')).append(); - // left whisker - target.drawLine( - Math.round((lwhisker - minValue) * unitSize + canvasLeft), - Math.round(canvasHeight / 2), - Math.round((q1 - minValue) * unitSize + canvasLeft), - Math.round(canvasHeight / 2), - options.get('lineColor')).append(); - target.drawLine( - Math.round((lwhisker - minValue) * unitSize + canvasLeft), - Math.round(canvasHeight / 4), - Math.round((lwhisker - minValue) * unitSize + canvasLeft), - Math.round(canvasHeight - canvasHeight / 4), - options.get('whiskerColor')).append(); - // right whisker - target.drawLine(Math.round((rwhisker - minValue) * unitSize + canvasLeft), - Math.round(canvasHeight / 2), - Math.round((q3 - minValue) * unitSize + canvasLeft), - Math.round(canvasHeight / 2), - options.get('lineColor')).append(); - target.drawLine( - Math.round((rwhisker - minValue) * unitSize + canvasLeft), - Math.round(canvasHeight / 4), - Math.round((rwhisker - minValue) * unitSize + canvasLeft), - Math.round(canvasHeight - canvasHeight / 4), - options.get('whiskerColor')).append(); - // median line - target.drawLine( - Math.round((q2 - minValue) * unitSize + canvasLeft), - Math.round(canvasHeight * 0.1), - Math.round((q2 - minValue) * unitSize + canvasLeft), - Math.round(canvasHeight * 0.9), - options.get('medianColor')).append(); - if (options.get('target')) { - size = Math.ceil(options.get('spotRadius')); - target.drawLine( - Math.round((options.get('target') - minValue) * unitSize + canvasLeft), - Math.round((canvasHeight / 2) - size), - Math.round((options.get('target') - minValue) * unitSize + canvasLeft), - Math.round((canvasHeight / 2) + size), - options.get('targetColor')).append(); - target.drawLine( - Math.round((options.get('target') - minValue) * unitSize + canvasLeft - size), - Math.round(canvasHeight / 2), - Math.round((options.get('target') - minValue) * unitSize + canvasLeft + size), - Math.round(canvasHeight / 2), - options.get('targetColor')).append(); - } - target.render(); - } - }); - - // Setup a very simple "virtual canvas" to make drawing the few shapes we need easier - // This is accessible as $(foo).simpledraw() - - VShape = createClass({ - init: function (target, id, type, args) { - this.target = target; - this.id = id; - this.type = type; - this.args = args; - }, - append: function () { - this.target.appendShape(this); - return this; - } - }); - - VCanvas_base = createClass({ - _pxregex: /(\d+)(px)?\s*$/i, - - init: function (width, height, target) { - if (!width) { - return; - } - this.width = width; - this.height = height; - this.target = target; - this.lastShapeId = null; - if (target[0]) { - target = target[0]; - } - $.data(target, '_jqs_vcanvas', this); + this.width = width; + this.height = height; + this.target = target; + this.lastShapeId = null; + if (target[0]) { + target = target[0]; + } + $.data(target, '_jqs_vcanvas', this); }, drawLine: function (x1, y1, x2, y2, lineColor, lineWidth) { @@ -2587,14 +1230,6 @@ return this._genShape('Shape', [path, lineColor, fillColor, lineWidth]); }, - drawCircle: function (x, y, radius, lineColor, fillColor, lineWidth) { - return this._genShape('Circle', [x, y, radius, lineColor, fillColor, lineWidth]); - }, - - drawPieSlice: function (x, y, radius, startAngle, endAngle, lineColor, fillColor) { - return this._genShape('PieSlice', [x, y, radius, startAngle, endAngle, lineColor, fillColor]); - }, - drawRect: function (x, y, width, height, lineColor, fillColor) { return this._genShape('Rect', [x, y, width, height, lineColor, fillColor]); }, @@ -2753,41 +1388,6 @@ } }, - _drawCircle: function (shapeid, x, y, radius, lineColor, fillColor, lineWidth) { - var context = this._getContext(lineColor, fillColor, lineWidth); - context.beginPath(); - context.arc(x, y, radius, 0, 2 * Math.PI, false); - if (this.targetX !== undefined && this.targetY !== undefined && - context.isPointInPath(this.targetX, this.targetY)) { - this.currentTargetShapeId = shapeid; - } - if (lineColor !== undefined) { - context.stroke(); - } - if (fillColor !== undefined) { - context.fill(); - } - }, - - _drawPieSlice: function (shapeid, x, y, radius, startAngle, endAngle, lineColor, fillColor) { - var context = this._getContext(lineColor, fillColor); - context.beginPath(); - context.moveTo(x, y); - context.arc(x, y, radius, startAngle, endAngle, false); - context.lineTo(x, y); - context.closePath(); - if (lineColor !== undefined) { - context.stroke(); - } - if (fillColor) { - context.fill(); - } - if (this.targetX !== undefined && this.targetY !== undefined && - context.isPointInPath(this.targetX, this.targetY)) { - this.currentTargetShapeId = shapeid; - } - }, - _drawRect: function (shapeid, x, y, width, height, lineColor, fillColor) { return this._drawShape(shapeid, [[x, y], [x + width, y], [x + width, y + height], [x, y + height], [x, y]], lineColor, fillColor); }, @@ -2885,170 +1485,4 @@ } }); - - VCanvas_vml = createClass(VCanvas_base, { - init: function (width, height, target) { - var groupel; - VCanvas_vml._super.init.call(this, width, height, target); - if (target[0]) { - target = target[0]; - } - $.data(target, '_jqs_vcanvas', this); - this.canvas = document.createElement('span'); - $(this.canvas).css({ display: 'inline-block', position: 'relative', overflow: 'hidden', width: width, height: height, margin: '0px', padding: '0px', verticalAlign: 'top'}); - this._insert(this.canvas, target); - this._calculatePixelDims(width, height, this.canvas); - this.canvas.width = this.pixelWidth; - this.canvas.height = this.pixelHeight; - groupel = ''; - this.canvas.insertAdjacentHTML('beforeEnd', groupel); - this.group = $(this.canvas).children()[0]; - this.rendered = false; - this.prerender = ''; - }, - - _drawShape: function (shapeid, path, lineColor, fillColor, lineWidth) { - var vpath = [], - initial, stroke, fill, closed, vel, plen, i; - for (i = 0, plen = path.length; i < plen; i++) { - vpath[i] = '' + (path[i][0]) + ',' + (path[i][1]); - } - initial = vpath.splice(0, 1); - lineWidth = lineWidth === undefined ? 1 : lineWidth; - stroke = lineColor === undefined ? ' stroked="false" ' : ' strokeWeight="' + lineWidth + 'px" strokeColor="' + lineColor + '" '; - fill = fillColor === undefined ? ' filled="false"' : ' fillColor="' + fillColor + '" filled="true" '; - closed = vpath[0] === vpath[vpath.length - 1] ? 'x ' : ''; - vel = '' + - ' '; - return vel; - }, - - _drawCircle: function (shapeid, x, y, radius, lineColor, fillColor, lineWidth) { - var stroke, fill, vel; - x -= radius; - y -= radius; - stroke = lineColor === undefined ? ' stroked="false" ' : ' strokeWeight="' + lineWidth + 'px" strokeColor="' + lineColor + '" '; - fill = fillColor === undefined ? ' filled="false"' : ' fillColor="' + fillColor + '" filled="true" '; - vel = ''; - return vel; - - }, - - _drawPieSlice: function (shapeid, x, y, radius, startAngle, endAngle, lineColor, fillColor) { - var vpath, startx, starty, endx, endy, stroke, fill, vel; - if (startAngle === endAngle) { - return ''; // VML seems to have problem when start angle equals end angle. - } - if ((endAngle - startAngle) === (2 * Math.PI)) { - startAngle = 0.0; // VML seems to have a problem when drawing a full circle that doesn't start 0 - endAngle = (2 * Math.PI); - } - - startx = x + Math.round(Math.cos(startAngle) * radius); - starty = y + Math.round(Math.sin(startAngle) * radius); - endx = x + Math.round(Math.cos(endAngle) * radius); - endy = y + Math.round(Math.sin(endAngle) * radius); - - if (startx === endx && starty === endy) { - if ((endAngle - startAngle) < Math.PI) { - // Prevent very small slices from being mistaken as a whole pie - return ''; - } - // essentially going to be the entire circle, so ignore startAngle - startx = endx = x + radius; - starty = endy = y; - } - - if (startx === endx && starty === endy && (endAngle - startAngle) < Math.PI) { - return ''; - } - - vpath = [x - radius, y - radius, x + radius, y + radius, startx, starty, endx, endy]; - stroke = lineColor === undefined ? ' stroked="false" ' : ' strokeWeight="1px" strokeColor="' + lineColor + '" '; - fill = fillColor === undefined ? ' filled="false"' : ' fillColor="' + fillColor + '" filled="true" '; - vel = '' + - ' '; - return vel; - }, - - _drawRect: function (shapeid, x, y, width, height, lineColor, fillColor) { - return this._drawShape(shapeid, [[x, y], [x, y + height], [x + width, y + height], [x + width, y], [x, y]], lineColor, fillColor); - }, - - reset: function () { - this.group.innerHTML = ''; - }, - - appendShape: function (shape) { - var vel = this['_draw' + shape.type].apply(this, shape.args); - if (this.rendered) { - this.group.insertAdjacentHTML('beforeEnd', vel); - } else { - this.prerender += vel; - } - this.lastShapeId = shape.id; - return shape.id; - }, - - replaceWithShape: function (shapeid, shape) { - var existing = $('#jqsshape' + shapeid), - vel = this['_draw' + shape.type].apply(this, shape.args); - existing[0].outerHTML = vel; - }, - - replaceWithShapes: function (shapeids, shapes) { - // replace the first shapeid with all the new shapes then toast the remaining old shapes - var existing = $('#jqsshape' + shapeids[0]), - replace = '', - slen = shapes.length, - i; - for (i = 0; i < slen; i++) { - replace += this['_draw' + shapes[i].type].apply(this, shapes[i].args); - } - existing[0].outerHTML = replace; - for (i = 1; i < shapeids.length; i++) { - $('#jqsshape' + shapeids[i]).remove(); - } - }, - - insertAfterShape: function (shapeid, shape) { - var existing = $('#jqsshape' + shapeid), - vel = this['_draw' + shape.type].apply(this, shape.args); - existing[0].insertAdjacentHTML('afterEnd', vel); - }, - - removeShapeId: function (shapeid) { - var existing = $('#jqsshape' + shapeid); - this.group.removeChild(existing[0]); - }, - - getShapeAt: function (el, x, y) { - var shapeid = el.id.substr(8); - return shapeid; - }, - - render: function () { - if (!this.rendered) { - // batch the intial render into a single repaint - this.group.innerHTML = this.prerender; - this.rendered = true; - } - } - }); - }))}(document, Math));