import Store from "../store"; //sparkline设置 let createClass = function (/* [baseclass, [mixin, ...]], definition */) { let Class, args; Class = function () { this.init.apply(this, arguments); }; if (arguments.length > 1) { if (arguments[0]) { Class.prototype = $.extend(new arguments[0](), arguments[arguments.length - 1]); Class._super = arguments[0].prototype; } else { Class.prototype = arguments[arguments.length - 1]; } if (arguments.length > 2) { args = Array.prototype.slice.call(arguments, 1, -1); args.unshift(Class.prototype); $.extend.apply($, args); } } else { Class.prototype = arguments[0]; } Class.prototype.cls = Class; return Class; }; /** * Wraps a format string for tooltips * {{x}} * {{x.2} * {{x:months}} */ let SPFormat = createClass({ fre: /\{\{([\w.]+?)(:(.+?))?\}\}/g, precre: /(\w+)\.(\d+)/, init: function (format, fclass) { this.format = format; this.fclass = fclass; }, render: function (fieldset, lookups, options) { let self = this, fields = fieldset, match, token, lookupkey, fieldvalue, prec; return this.format.replace(this.fre, function () { let 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); }; let clipval = function (val, min, max) { if (val < min) { return min; } if (val > max) { return max; } return val; }; let quartile = function (values, q) { let vl; if (q === 2) { vl = Math.floor(values.length / 2); return values.length % 2 ? values[vl] : (values[vl-1] + values[vl]) / 2; } else { if (values.length % 2 ) { // odd vl = (values.length * q + q) / 4; return vl % 1 ? (values[Math.floor(vl)] + values[Math.floor(vl) - 1]) / 2 : values[vl-1]; } else { //even vl = (values.length * q + 2) / 4; return vl % 1 ? (values[Math.floor(vl)] + values[Math.floor(vl) - 1]) / 2 : values[vl-1]; } } }; let normalizeValue = function (val) { let 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; }; let normalizeValues = function (vals) { let i, result = []; for (i = vals.length; i--;) { result[i] = normalizeValue(vals[i]); } return result; }; let all = function (val, arr, ignoreNull) { let i; for (i = arr.length; i--; ) { if (ignoreNull && arr[i] === null) continue; if (arr[i] !== val) { return false; } } return true; }; // sums the numeric values in an array, ignoring other values let sum = function (vals) { let total = 0, i; for (i = vals.length; i--;) { total += typeof vals[i] === 'number' ? vals[i] : 0; } return total; }; let remove = function (vals, filter) { let i, vl, result = []; for (i = 0, vl = vals.length; i < vl; i++) { if (vals[i] !== filter) { result.push(vals[i]); } } return result; }; let isNumber = function (num) { return !isNaN(parseFloat(num)) && isFinite(num); }; let formatNumber = function (num, prec, groupsize, groupsep, decsep) { let p, i; num = (prec === false ? parseFloat(num).toString() : num.toFixed(prec)).split(''); p = (p = $.inArray('.', num)) < 0 ? num.length : p; if (p < num.length) { num[p] = decsep; } for (i = p - groupsize; i > 0; i -= groupsize) { num.splice(i, 0, groupsep); } return num.join(''); }; let RangeMap = createClass({ init: function (map) { let 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) { let 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); }; const luckysheetSparkline = { defaultOption:{ // Settings common to most/all chart types common: { type: 'line', lineColor: '#2ec7c9', fillColor: '#CCF3F4', defaultPixelsPerValue: 3, width: 'auto', height: 'auto', composite: false, tagValuesAttribute: 'values', tagOptionsPrefix: 'spark', enableTagOptions: false, enableHighlight: true, highlightLighten: 1.4, tooltipSkipNull: true, tooltipPrefix: '', tooltipSuffix: '', disableHiddenCheck: false, numberFormatter: false, numberDigitGroupCount: 3, numberDigitGroupSep: ',', numberDecimalMark: '.', disableTooltips: true, disableInteraction: true, offsetX:0, offsetY:0 }, // Defaults for line charts //=LINESPL line: { spotColor: 0, highlightSpotColor: '#5f5', highlightLineColor: '#f22', spotRadius: 1.5, minSpotColor: 0, maxSpotColor: 0, lineWidth: 1, normalRangeMin: undefined, normalRangeMax: undefined, normalRangeColor: '#ccc', drawNormalOnTop: true, chartRangeMin: undefined, chartRangeMax: undefined, chartRangeMinX: undefined, chartRangeMaxX: undefined, // tooltipFormat: new SPFormat('<span style="color: {{color}}">●</span> {{prefix}}{{y}}{{suffix}}') }, // Defaults for bar charts bar: { barColor: '#fc5c5c', negBarColor: '#97b552', stackedBarColor: ["#2ec7c9", "#fc5c5c", "#5ab1ef", "#ffb980", "#d87a80", "#8d98b3", "#e5cf0d", "#97b552", "#95706d","#dc69aa","#07a2a4","#9a7fd1","#588dd5","#f5994e","#c05050","#59678c","#c9ab00","#7eb00a","#6f5553","#c14089"], zeroColor: undefined, nullColor: undefined, zeroAxis: true, barWidth: 4, barSpacing: 1, chartRangeMax: undefined, chartRangeMin: undefined, chartRangeClip: false, colorMap: undefined, // tooltipFormat: new SPFormat('<span style="color: {{color}}">●</span> {{prefix}}{{value}}{{suffix}}') }, column: { barColor: '#fc5c5c', negBarColor: '#97b552', stackedBarColor: ["#2ec7c9", "#fc5c5c", "#5ab1ef", "#ffb980", "#d87a80", "#8d98b3", "#e5cf0d", "#97b552", "#95706d","#dc69aa","#07a2a4","#9a7fd1","#588dd5","#f5994e","#c05050","#59678c","#c9ab00","#7eb00a","#6f5553","#c14089"], zeroColor: undefined, nullColor: undefined, zeroAxis: true, barWidth: 4, barSpacing: 1, chartRangeMax: undefined, chartRangeMin: undefined, chartRangeClip: false, colorMap: undefined, // tooltipFormat: new SPFormat('<span style="color: {{color}}">●</span> {{prefix}}{{value}}{{suffix}}') }, // Defaults for tristate charts tristate: { barWidth: 4, barSpacing: 1, posBarColor: '#fc5c5c', negBarColor: '#97b552', zeroBarColor: '#999', colorMap: {}, // tooltipFormat: new SPFormat('<span style="color: {{color}}">●</span> {{value:map}}'), // tooltipValueLookups: { map: { '-1': 'Loss', '0': 'Draw', '1': 'Win' } } }, // Defaults for discrete charts discrete: { lineHeight: 'auto', thresholdColor: "#fc5c5c", 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','#6D87FF','#5876FF','#4465FF','#2F54FF','#1A43FF','#0532FF'], base: undefined, // set this to a number to change the base start number // tooltipFormat: new SPFormat('{{fieldkey:fields}} - {{value}}'), // tooltipValueLookups: { fields: {r: 'Range', p: 'Performance', t: 'Target'} } }, // Defaults for pie charts pie: { offset: 0, sliceColors: ["#2ec7c9", "#fc5c5c", "#5ab1ef", "#ffb980", "#d87a80", "#8d98b3", "#e5cf0d", "#97b552", "#95706d","#dc69aa","#07a2a4","#9a7fd1","#588dd5","#f5994e","#c05050","#59678c","#c9ab00","#7eb00a","#6f5553","#c14089"], borderWidth: 0, borderColor: '#000', // tooltipFormat: new SPFormat('<span style="color: {{color}}">●</span> {{value}} ({{percent.1}}%)') }, // Defaults for box plots box: { raw: false, boxLineColor: '#000', boxFillColor: '#cdf', whiskerColor: '#000', outlierLineColor: '#5E5E5E', 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'} } } }, line:{ 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) { let 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 () { let 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 () { let 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 () { let 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 () { let 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 () { let 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) { let 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); //(x1, y1, x2, y2, lineColor, lineWidth) if(height==0 && normalRangeMin==normalRangeMax){ height=1; } this.target.drawRect(canvasLeft, ytop, canvasWidth, height, undefined, this.options.get('normalRangeColor')).append(); }, render: function (el,userValues) { this.vertices = []; this.regionMap = []; this.xvalues = []; this.yvalues = []; this.yminmax = []; this.hightlightSpotId = null; this.lastShapeId = null; this.values = userValues; let options = this.options, target = this.target, canvasWidth = el.mergedOptions.width, canvasHeight = el.mergedOptions.height, 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(); } plen = lineShapes.length; for (i = 0; i < plen; i++) { target.drawShape(lineShapes[i], options.get('lineColor'), undefined, options.get('lineWidth')).append(); } if (options.get('normalRangeMin') !== undefined && options.get('drawNormalOnTop')) { this.drawNormalRange(canvasLeft, canvasTop, canvasHeight, canvasWidth, rangey); } 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:{ type: 'bar', init: function (el, values) { let options = this.options; let width = el.mergedOptions.height; let height = el.mergedOptions.width; this.canvasWidth = el.mergedOptions.height; this.canvasHeight = el.mergedOptions.width; let barWidth = parseInt(options.get('barWidth'), 10), barSpacing = parseInt(options.get('barSpacing'), 10), chartRangeMin = options.get('chartRangeMin'), chartRangeMax = options.get('chartRangeMax'), chartRangeClip = options.get('chartRangeClip'), stackMin = Infinity, stackMax = -Infinity, isStackString, groupMin, groupMax, stackRanges, numValues, i, vlen, range, zeroAxis, xaxisOffset, min, max, clipMin, clipMax, stacked, vlist, j, slen, svals, val, yoffset, yMaxCalc, canvasHeightEf; //bar._super.init.call(this, el, values, options, width, height); this.values = values; // 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 = Math.floor(width/values.length)-barSpacing; this.barSpacing = barSpacing; this.totalBarWidth = this.barWidth + barSpacing; //this.width = width = (values.length * barWidth) + ((values.length - 1) * barSpacing); this.width = width; //this.initTarget(); if (chartRangeClip) { clipMin = chartRangeMin === undefined ? -Infinity : chartRangeMin; clipMax = chartRangeMax === undefined ? Infinity : chartRangeMax; } numValues = []; stackRanges = stacked ? [] : numValues; let stackTotals = []; let 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); // stackRanges[i] += Math.abs(val - (val < 0 ? stackMax : stackMin)); } numValues.push(val); } } } else { val = chartRangeClip ? clipval(values[i], clipMin, clipMax) : values[i]; val = values[i] = normalizeValue(val); if (val !== null) { numValues.push(val); } } } this.max = max = Math.max.apply(Math, numValues); this.min = min = Math.min.apply(Math, numValues); this.stackMax = stackMax = stacked ? Math.max.apply(Math, stackTotals) : max; this.stackMin = stackMin = stacked ? Math.min.apply(Math, numValues) : min; if (options.get('chartRangeMin') !== undefined && (options.get('chartRangeClip') || options.get('chartRangeMin') < min)) { min = options.get('chartRangeMin'); } if (options.get('chartRangeMax') !== undefined && (options.get('chartRangeClip') || options.get('chartRangeMax') > max)) { max = options.get('chartRangeMax'); } this.zeroAxis = zeroAxis = options.get('zeroAxis', true); if (min <= 0 && max >= 0 && zeroAxis) { xaxisOffset = 0; } else if (zeroAxis == false) { xaxisOffset = min; } else if (min > 0) { xaxisOffset = 0; } else { xaxisOffset = max; } this.xaxisOffset = xaxisOffset; range = stacked ? (Math.max.apply(Math, stackRanges) + Math.max.apply(Math, stackRangesNeg)) : max - xaxisOffset; // 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; this.isNeg = false; if (min < xaxisOffset) { // yMaxCalc = (stacked && max >= 0) ? stackMax : max; // yoffset = (yMaxCalc - xaxisOffset) / range * this.canvasHeight; yoffset = Math.floor(this.canvasHeight/2); this.isNeg = true; if (yoffset !== Math.ceil(yoffset)) { this.canvasHeightEf -= 2; yoffset = Math.ceil(yoffset); } } else { yoffset = 0; } this.yoffset = yoffset; if ($.isArray(options.get('colorMap'))) { this.colorMapByIndex = options.get('colorMap'); this.colorMapByValue = null; } else { this.colorMapByIndex = null; this.colorMapByValue = options.get('colorMap'); if (this.colorMapByValue && this.colorMapByValue.get === undefined) { this.colorMapByValue = new RangeMap(this.colorMapByValue); } } this.range = range; }, getRegion: function (el, x, y) { let result = Math.floor(x / this.totalBarWidth); return (result < 0 || result >= this.values.length) ? undefined : result; }, getCurrentRegionFields: function () { let currentRegion = this.currentRegion, values = ensureArray(this.values[currentRegion]), result = [], value, i; for (i = values.length; i--;) { value = values[i]; result.push({ isNull: value === null, value: value, color: this.calcColor(i, value, currentRegion), offset: currentRegion }); } return result; }, calcColor: function (stacknum, value, valuenum) { let colorMapByIndex = this.colorMapByIndex, colorMapByValue = this.colorMapByValue, options = this.options, color, newColor; if (this.stacked) { color = options.get('stackedBarColor'); } else { color = (value < 0) ? options.get('negBarColor') : options.get('barColor'); } if (value === 0 && options.get('zeroColor') !== undefined) { color = options.get('zeroColor'); } if (colorMapByValue && (newColor = colorMapByValue.get(value))) { color = newColor; } else if (colorMapByIndex && colorMapByIndex.length > valuenum) { color = colorMapByIndex[valuenum]; } return $.isArray(color) ? color[stacknum % color.length] : color; }, /** * Render bar(s) for a region */ renderRegion: function (valuenum, highlight) { let vals = this.values[valuenum], options = this.options, xaxisOffset = this.xaxisOffset, result = [], range = this.range, stacked = this.stacked, target = this.target, x = valuenum * this.totalBarWidth, canvasHeightEf = this.canvasHeightEf, yoffset = this.yoffset, y, height, color, isNull, yoffsetNeg, i, valcount, val, minPlotted, allMin; vals = $.isArray(vals) ? vals : [vals]; valcount = vals.length; val = vals[0]; isNull = all(null, vals); allMin = all(xaxisOffset, vals, true); if (isNull) { if (options.get('nullColor')) { color = highlight ? options.get('nullColor') : this.calcHighlightColor(options.get('nullColor'), options); y = (yoffset > 0) ? yoffset - 1 : yoffset; return target.drawRect(y, x, 0, this.barWidth - 1, color, color); } else { return undefined; } } yoffsetNeg = yoffset; if(this.isNeg){ canvasHeightEf = Math.floor(canvasHeightEf/2); } 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))); } else { height = canvasHeightEf; } if (val < xaxisOffset || (val === xaxisOffset && yoffset === 0)) { y = yoffsetNeg - height; yoffsetNeg += height; } else { if(stacked){ y = yoffset; yoffset += height; } else{ y = yoffset; yoffset -= height; } } color = this.calcColor(i, val, valuenum); if (highlight) { color = this.calcHighlightColor(color, options); } result.push(target.drawRect(y, x, height - 1, this.barWidth - 1,color, color)); } if (result.length === 1) { return result[0]; } return result; } }, column:{ type: 'column', init: function (el, values) { let options = this.options; let width = el.mergedOptions.width; let height = el.mergedOptions.height; this.canvasWidth = el.mergedOptions.width; this.canvasHeight = el.mergedOptions.height; let barWidth = parseInt(options.get('barWidth'), 10), barSpacing = parseInt(options.get('barSpacing'), 10), chartRangeMin = options.get('chartRangeMin'), chartRangeMax = options.get('chartRangeMax'), chartRangeClip = options.get('chartRangeClip'), stackMin = Infinity, stackMax = -Infinity, isStackString, groupMin, groupMax, stackRanges, numValues, i, vlen, range, zeroAxis, xaxisOffset, min, max, clipMin, clipMax, stacked, vlist, j, slen, svals, val, yoffset, yMaxCalc, canvasHeightEf; //bar._super.init.call(this, el, values, options, width, height); this.values = values; // 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 = Math.floor(width/values.length)-barSpacing; this.barSpacing = barSpacing; this.totalBarWidth = this.barWidth + barSpacing; //this.width = width = (values.length * barWidth) + ((values.length - 1) * barSpacing); this.width = width; //this.initTarget(); if (chartRangeClip) { clipMin = chartRangeMin === undefined ? -Infinity : chartRangeMin; clipMax = chartRangeMax === undefined ? Infinity : chartRangeMax; } numValues = []; stackRanges = stacked ? [] : numValues; let stackTotals = []; let 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); // stackRanges[i] += Math.abs(val - (val < 0 ? stackMax : stackMin)); } numValues.push(val); } } } else { val = chartRangeClip ? clipval(values[i], clipMin, clipMax) : values[i]; val = values[i] = normalizeValue(val); if (val !== null) { numValues.push(val); } } } this.max = max = Math.max.apply(Math, numValues); this.min = min = Math.min.apply(Math, numValues); this.stackMax = stackMax = stacked ? Math.max.apply(Math, stackTotals) : max; this.stackMin = stackMin = stacked ? Math.min.apply(Math, numValues) : min; if (options.get('chartRangeMin') !== undefined && (options.get('chartRangeClip') || options.get('chartRangeMin') < min)) { min = options.get('chartRangeMin'); } if (options.get('chartRangeMax') !== undefined && (options.get('chartRangeClip') || options.get('chartRangeMax') > max)) { max = options.get('chartRangeMax'); } this.zeroAxis = zeroAxis = options.get('zeroAxis', true); if (min <= 0 && max >= 0 && zeroAxis) { xaxisOffset = 0; } else if (zeroAxis == false) { xaxisOffset = min; } else if (min > 0) { xaxisOffset = 0; } else { xaxisOffset = max; } this.xaxisOffset = xaxisOffset; range = stacked ? (Math.max.apply(Math, stackRanges) + Math.max.apply(Math, stackRangesNeg)) : max - xaxisOffset; // 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; this.isNeg = false; if (min < xaxisOffset) { // yMaxCalc = (stacked && max >= 0) ? stackMax : max; // yoffset = (yMaxCalc - xaxisOffset) / range * this.canvasHeight; yoffset = Math.floor(this.canvasHeight/2); this.isNeg = true; if (yoffset !== Math.ceil(yoffset)) { this.canvasHeightEf -= 2; yoffset = Math.ceil(yoffset); } } else { yoffset = this.canvasHeight; } this.yoffset = yoffset; if ($.isArray(options.get('colorMap'))) { this.colorMapByIndex = options.get('colorMap'); this.colorMapByValue = null; } else { this.colorMapByIndex = null; this.colorMapByValue = options.get('colorMap'); if (this.colorMapByValue && this.colorMapByValue.get === undefined) { this.colorMapByValue = new RangeMap(this.colorMapByValue); } } this.range = range; }, getRegion: function (el, x, y) { let result = Math.floor(x / this.totalBarWidth); return (result < 0 || result >= this.values.length) ? undefined : result; }, getCurrentRegionFields: function () { let currentRegion = this.currentRegion, values = ensureArray(this.values[currentRegion]), result = [], value, i; for (i = values.length; i--;) { value = values[i]; result.push({ isNull: value === null, value: value, color: this.calcColor(i, value, currentRegion), offset: currentRegion }); } return result; }, calcColor: function (stacknum, value, valuenum) { let colorMapByIndex = this.colorMapByIndex, colorMapByValue = this.colorMapByValue, options = this.options, color, newColor; if (this.stacked) { color = options.get('stackedBarColor'); } else { color = (value < 0) ? options.get('negBarColor') : options.get('barColor'); } if (value === 0 && options.get('zeroColor') !== undefined) { color = options.get('zeroColor'); } if (colorMapByValue && (newColor = colorMapByValue.get(value))) { color = newColor; } else if (colorMapByIndex && colorMapByIndex.length > valuenum) { color = colorMapByIndex[valuenum]; } return $.isArray(color) ? color[stacknum % color.length] : color; }, /** * Render bar(s) for a region */ renderRegion: function (valuenum, highlight) { let vals = this.values[valuenum], options = this.options, xaxisOffset = this.xaxisOffset, result = [], range = this.range, stacked = this.stacked, target = this.target, x = valuenum * this.totalBarWidth, canvasHeightEf = this.canvasHeightEf, yoffset = this.yoffset, y, height, color, isNull, yoffsetNeg, i, valcount, val, minPlotted, allMin; vals = $.isArray(vals) ? vals : [vals]; valcount = vals.length; val = vals[0]; isNull = all(null, vals); allMin = all(xaxisOffset, vals, true); if (isNull) { if (options.get('nullColor')) { color = highlight ? options.get('nullColor') : this.calcHighlightColor(options.get('nullColor'), options); y = (yoffset > 0) ? yoffset - 1 : yoffset; return target.drawRect(x, y, this.barWidth - 1, 0, color, color); } else { return undefined; } } yoffsetNeg = yoffset; if(this.isNeg){ canvasHeightEf = Math.floor(canvasHeightEf/2); } 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))); } else { height = canvasHeightEf; } if (val < xaxisOffset || (val === xaxisOffset && yoffset === 0)) { y = yoffsetNeg; yoffsetNeg += height; } else { y = yoffset - height; yoffset -= height; } color = this.calcColor(i, val, valuenum); if (highlight) { color = this.calcHighlightColor(color, options); } result.push(target.drawRect(x, y, this.barWidth - 1, height - 1, color, color)); } if (result.length === 1) { return result[0]; } return result; } }, tristate:{ type: 'tristate', init: function(el, values) { let options = this.options; let width = el.mergedOptions.width; let height = el.mergedOptions.height; this.canvasWidth = el.mergedOptions.width; this.canvasHeight = el.mergedOptions.height; let barWidth = parseInt(options.get('barWidth'), 10), barSpacing = parseInt(options.get('barSpacing'), 10); //tristate._super.init.call(this, el, values, options, width, height); this.regionShapes = {}; this.barWidth = barWidth; this.barSpacing = barSpacing; this.totalBarWidth = barWidth + barSpacing; this.values = $.map(values, Number); this.width = width = (values.length * barWidth) + ((values.length - 1) * barSpacing); if ($.isArray(options.get('colorMap'))) { this.colorMapByIndex = options.get('colorMap'); this.colorMapByValue = null; } else { this.colorMapByIndex = null; this.colorMapByValue = options.get('colorMap'); if (this.colorMapByValue && this.colorMapByValue.get === undefined) { this.colorMapByValue = new RangeMap(this.colorMapByValue); } } //this.initTarget(); }, getRegion: function (el, x, y) { return Math.floor(x / this.totalBarWidth); }, getCurrentRegionFields: function () { let 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) { let 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) { let values = this.values, options = this.options, target = this.target, canvasHeight, height, halfHeight, x, y, color; canvasHeight = this.canvasHeight; 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:{ type: 'discrete', init: function(el, values) { let options = this.options; let width = el.mergedOptions.width; let height = el.mergedOptions.height; this.canvasWidth = el.mergedOptions.width; this.canvasHeight = el.mergedOptions.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; 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 () { let currentRegion = this.currentRegion; return { isNull: this.values[currentRegion] === undefined, value: this.values[currentRegion], offset: currentRegion }; }, renderRegion: function (valuenum, highlight) { let 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); return this.target.drawRect(x, ytop, interval<=2?1:interval-2, lineHeight, color, color); } }, bullet:{ type: 'bullet', init: function(el, values) { let options = this.options; let width = el.mergedOptions.width; let height = el.mergedOptions.height; this.canvasWidth = el.mergedOptions.width; this.canvasHeight = el.mergedOptions.height; let 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; //this.target = this.$el.simpledraw(width, height, options.get('composite')); if (!values.length) { this.disabled = true; } //this.initTarget(); }, getRegion: function (el, x, y) { let shapeid = this.target.getShapeAt(el, x, y); return (shapeid !== undefined && this.shapes[shapeid] !== undefined) ? this.shapes[shapeid] : undefined; }, getCurrentRegionFields: function () { let currentRegion = this.currentRegion; return { fieldkey: currentRegion.substr(0, 1), value: this.values[currentRegion.substr(1)], region: currentRegion }; }, changeHighlight: function (highlight) { let 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) { let 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) { let 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) { let 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 (el,userValues) { this.init(el,userValues); let 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:{ type: 'pie', init: function(el, values) { let options = this.options; let width = el.mergedOptions.width; let height = el.mergedOptions.height; this.canvasWidth = el.mergedOptions.width; this.canvasHeight = el.mergedOptions.height; let 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) { let shapeid = this.target.getShapeAt(el, x, y); return (shapeid !== undefined && this.shapes[shapeid] !== undefined) ? this.shapes[shapeid] : undefined; }, getCurrentRegionFields: function () { let 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) { let 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) { let 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 (el,userValues) { this.init(el,userValues); let 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:{ type: 'box', init: function(el, values) { let options = this.options; let width = el.mergedOptions.width; let height = el.mergedOptions.height; this.canvasWidth = el.mergedOptions.width; this.canvasHeight = el.mergedOptions.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 () { let 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 (el,userValues) { this.init(el,userValues); let 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(); } }, shapeCount:0, shapes:{}, shapeseq:[], lastShapeId:null, mergedOptions:null, init:function(userValues, userOptions){ let extendedOptions, defaults, base; userOptions = userOptions || {}; let _this = this; defaults = this.defaultOption; base = defaults.common; extendedOptions = defaults[userOptions.type || base.type]; _this.shapeCount = 0; _this.shapes = {}; _this.shapeseq = []; _this.lastShapeId = null; _this.mergedOptions = $.extend({}, base, extendedOptions, userOptions); _this.mergedOptions.width = _this.mergedOptions.width; _this.mergedOptions.height = _this.mergedOptions.height; _this[_this.mergedOptions.type].render(_this, userValues); return { shapes:_this.shapes, shapeseq:_this.shapeseq, offsetX:_this.mergedOptions.offsetX, offsetY:_this.mergedOptions.offsetY, pixelWidth:_this.mergedOptions.width, pixelHeight:_this.mergedOptions.height}; }, _getContext: function (lineColor, fillColor, lineWidth) { let context; if(this.ctx != null){ context = this.ctx; } else{ context = $("#" + this._canvasID ).get(0).getContext('2d'); } if (lineColor !== undefined) { context.strokeStyle = lineColor; } context.lineWidth = lineWidth === undefined ? 1 : lineWidth; if (fillColor !== undefined) { context.fillStyle = fillColor; } return context; }, reset: function () { let context = this._getContext(); context.clearRect(0, 0, this.pixelWidth, this.pixelHeight); this.shapes = {}; this.shapeseq = []; this.currentTargetShapeId = undefined; }, _drawShape: function (shapeid, path, lineColor, fillColor, lineWidth) { let context = this._getContext(lineColor, fillColor, lineWidth), i, plen; context.beginPath(); context.moveTo(path[0][0] + 0.5 + this.offsetX, path[0][1] + 0.5 + this.offsetY); for (i = 1, plen = path.length; i < plen; i++) { context.lineTo(path[i][0] + 0.5 + this.offsetX, path[i][1] + 0.5 + this.offsetY); // the 0.5 offset gives us crisp pixel-width lines } if (lineColor !== undefined) { context.stroke(); } if (fillColor !== undefined) { context.fill(); } if (this.targetX !== undefined && this.targetY !== undefined && context.isPointInPath(this.targetX + this.offsetX, this.targetY + this.offsetY)) { this.currentTargetShapeId = shapeid; } }, _drawCircle: function (shapeid, x, y, radius, lineColor, fillColor, lineWidth) { let context = this._getContext(lineColor, fillColor, lineWidth); context.beginPath(); x+=this.offsetX; y+=this.offsetY; context.arc(x, y, radius, 0, 2 * Math.PI, false); if (this.targetX !== undefined && this.targetY !== undefined && context.isPointInPath(this.targetX+this.offsetX, this.targetY+this.offsetY)) { this.currentTargetShapeId = shapeid; } if (lineColor !== undefined) { context.stroke(); } if (fillColor !== undefined) { context.fill(); } }, _drawPieSlice: function (shapeid, x, y, radius, startAngle, endAngle, lineColor, fillColor) { let context = this._getContext(lineColor, fillColor); x+=this.offsetX; y+=this.offsetY; 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.offsetX, this.targetY+this.offsetY)) { this.currentTargetShapeId = shapeid; } }, _drawRect: function (shapeid, x, y, width, height, lineColor, fillColor) { // x+=this.offsetX; // y+=this.offsetY; return this._drawShape(shapeid, [[x, y], [x + width, y], [x + width, y + height], [x, y + height], [x, y]], lineColor, fillColor); }, appendShape: function (shape) { this.shapes[shape.id] = shape; this.shapeseq.push(shape.id); this.lastShapeId = shape.id; return shape.id; }, replaceWithShape: function (shapeid, shape) { let shapeseq = this.shapeseq, i; this.shapes[shape.id] = shape; for (i = shapeseq.length; i--;) { if (shapeseq[i] == shapeid) { shapeseq[i] = shape.id; } } delete this.shapes[shapeid]; }, replaceWithShapes: function (shapeids, shapes) { let shapeseq = this.shapeseq, shapemap = {}, sid, i, first; for (i = shapeids.length; i--;) { shapemap[shapeids[i]] = true; } for (i = shapeseq.length; i--;) { sid = shapeseq[i]; if (shapemap[sid]) { shapeseq.splice(i, 1); delete this.shapes[sid]; first = i; } } for (i = shapes.length; i--;) { shapeseq.splice(first, 0, shapes[i].id); this.shapes[shapes[i].id] = shapes[i]; } }, insertAfterShape: function (shapeid, shape) { let shapeseq = this.shapeseq, i; for (i = shapeseq.length; i--;) { if (shapeseq[i] === shapeid) { shapeseq.splice(i + 1, 0, shape.id); this.shapes[shape.id] = shape; return; } } }, removeShapeId: function (shapeid) { let shapeseq = this.shapeseq, i; for (i = shapeseq.length; i--;) { if (shapeseq[i] === shapeid) { shapeseq.splice(i, 1); break; } } delete this.shapes[shapeid]; }, getShapeAt: function (el, x, y) { this.targetX = x; this.targetY = y; this.render(); return this.currentTargetShapeId; }, _canvasID:"luckysheetTableContent", render: function (shapeseq, shapes, offsetX, offsetY, pixelWidth, pixelHeight,canvasid,ctx) { if(canvasid==null){ canvasid = "luckysheetTableContent"; } this._canvasID = canvasid; if(ctx != null){ this.ctx = ctx; } let shapeCount = shapeseq.length, context = this._getContext(), shapeid, shape, i; this.offsetX = offsetX; this.offsetY = offsetY; this.pixelWidth = pixelWidth; this.pixelHeight = pixelHeight; //context.clearRect(this.offsetX, this.offsetY, this.pixelWidth, this.pixelHeight); // qkSparkSetting.currentSparkVlaue = { // shapeseq : shapeseq, // shapes:shapes, // shapeCount:shapeCount, // el:this // } for (i = 0; i < shapeCount; i++) { shapeid = shapeseq[i]; shape = shapes[shapeid]; this['_draw' + shape.type].apply(this, shape.args); } // if (!this.interact) { // // not interactive so no need to keep the shapes array // this.shapes = {}; // this.shapeseq = []; // } }, drawLine: function (x1, y1, x2, y2, lineColor, lineWidth) { return this.drawShape([[x1, y1], [x2, y2]], lineColor, lineWidth); }, drawShape: function (path, lineColor, fillColor, lineWidth) { return this._genShape('Shape', [path, lineColor, fillColor, lineWidth]); }, drawCircle: function (x, y, radius, lineColor, fillColor, lineWidth) { return this._genShape('Circle', [x, y, radius, lineColor, fillColor, lineWidth]); }, drawPieSlice: function (x, y, radius, startAngle, endAngle, lineColor, fillColor) { return this._genShape('PieSlice', [x, y, radius, startAngle, endAngle, lineColor, fillColor]); }, drawRect: function (x, y, width, height, lineColor, fillColor) { return this._genShape('Rect', [x, y, width, height, lineColor, fillColor]); }, _genShape: function (shapetype, shapeargs) { let id = this.shapeCount++; shapeargs.unshift(id); // return new VShape(this, id, shapetype, shapeargs); // this.target = target; // this.id = id; // this.type = type; // this.args = args; let shape = { id:id, type:shapetype, args:shapeargs }; this.shapes[id] = shape; this.shapeseq.push(id); this.lastShapeId = id; return { append:function(){ return shape; }, get:function(){ return id; } }; } } let barHighlightMixin = { changeHighlight: function (highlight) { let currentRegion = this.currentRegion, target = this.target, shapeids = this.regionShapes[currentRegion], newShapes; // will be null if the region value was null if (shapeids) { newShapes = this.renderRegion(currentRegion, highlight); if ($.isArray(newShapes) || $.isArray(shapeids)) { target.replaceWithShapes(shapeids, newShapes); this.regionShapes[currentRegion] = $.map(newShapes, function (newShape) { return newShape.id; }); } else { target.replaceWithShape(shapeids, newShapes); this.regionShapes[currentRegion] = newShapes.id; } } }, render: function (el,userValues) { this.init(el, userValues); let values = this.values, target = this.target, regionShapes = this.regionShapes, shapes, ids, i, j; // if (!this.cls._super.render.call(this)) { // return; // } for (i = values.length; i--;) { shapes = this.renderRegion(i); if (shapes) { if ($.isArray(shapes)) { ids = []; for (j = shapes.length; j--;) { shapes[j].append(); ids.push(shapes[j].id); } regionShapes[i] = ids; } else { shapes.append(); regionShapes[i] = shapes.id; // store just the shapeid } } else { // null value regionShapes[i] = null; } } //target.render(); } }; let _luckysheetSparkLineOptions = { get:function(type){ return luckysheetSparkline.mergedOptions[type]; } } let _luckysheetSparkLineTarget = { drawLine:function(x1, y1, x2, y2, lineColor, lineWidth){ return luckysheetSparkline.drawLine(x1, y1, x2, y2, lineColor, lineWidth); }, drawShape:function(path, lineColor, fillColor, lineWidth){ return luckysheetSparkline.drawShape(path, lineColor, fillColor, lineWidth); }, drawCircle:function(x, y, radius, lineColor, fillColor, lineWidth){ return luckysheetSparkline.drawCircle(x, y, radius, lineColor, fillColor, lineWidth); }, drawPieSlice:function(x, y, radius, startAngle, endAngle, lineColor, fillColor){ return luckysheetSparkline.drawPieSlice(x, y, radius, startAngle, endAngle, lineColor, fillColor); }, drawRect:function(x, y, width, height, lineColor, fillColor){ return luckysheetSparkline.drawRect(x, y, width, height, lineColor, fillColor); } } for(let item in luckysheetSparkline){ if(item in {"line":null, "bar":null, "column":null, "tristate":null, "discrete":null, "bullet":null, "pie":null, "box":null}){ luckysheetSparkline[item].options = _luckysheetSparkLineOptions; luckysheetSparkline[item].target = _luckysheetSparkLineTarget; } if(item in {"bar":null, "column":null, "tristate":null, "discrete":null}){ luckysheetSparkline[item].changeHighlight = barHighlightMixin.changeHighlight; luckysheetSparkline[item].render = barHighlightMixin.render; } } export default luckysheetSparkline;