function HistogramWidget(elmt, options) { this._elmt = elmt; this._options = options; this._range = null; this._binMatrix = null; this._highlight = null; this._initializeUI(); } HistogramWidget.prototype.highlight = function(from, to) { this._highlight = { from: from, to: to }; this._update(); }; HistogramWidget.prototype.update = function(min, max, step, binMatrix, from, to) { if (typeof min == "undefined" || typeof binMatrix == "undefined" || binMatrix.length === 0 || binMatrix[0].length === 0) { this._range = null; this._binMatrix = null; this._highlight = null; this._elmt.hide(); } else { this._range = { min: min, max: max, step: step }; this._binMatrix = binMatrix; this._peak = 0; for (var r = 0; r < binMatrix.length; r++) { var row = binMatrix[r]; for (var c = 0; c < row.length; c++) { this._peak = Math.max(this._peak, row[c]); } } if (typeof from != "undefined" && typeof to != "undefined") { this._highlight = { from: from, to: to }; } this._update(); } }; HistogramWidget.prototype._update = function() { if (this._binMatrix != null) { if (this._highlight != null) { this._highlight.from = Math.max(this._highlight.from, this._range.min); this._highlight.to = Math.min(this._highlight.to, this._range.max); } this._elmt.show(); this._resize(); this._render(); } }; HistogramWidget.prototype._initializeUI = function() { this._elmt .empty() .hide() .addClass("histogram-widget") .html( '' ); this._elmts = DOM.bind(this._elmt); }; HistogramWidget.prototype._resize = function() { this._elmts.canvas.attr("height", "height" in this._options ? this._options.height : 50); this._elmts.canvas.attr("width", this._elmts.canvas.width()); }; HistogramWidget.prototype._render = function() { var self = this; var options = this._options; var canvas = this._elmts.canvas[0]; var ctx = canvas.getContext('2d'); ctx.fillStyle = "white"; ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.save(); ctx.translate(0, canvas.height); ctx.scale(1, -1); var stepPixels = canvas.width / this._binMatrix[0].length; var stepScale = stepPixels / this._range.step; /* * Draw axis */ ctx.save(); ctx.strokeStyle = "emptyBinColor" in options ? options.emptyBinColor : "#faa"; ctx.lineWidth = 1; ctx.moveTo(0, 0); ctx.lineTo(canvas.width, 0); ctx.stroke(); ctx.restore(); /* * Draw bins */ var makeColor = function(i) { var n = Math.floor(15 * (self._binMatrix.length - i) / self._binMatrix.length); var h = n.toString(16); return "#" + h + h + h; }; var renderRow = function(row, color) { ctx.save(); ctx.lineWidth = 0; ctx.fillStyle = color; for (var c = 0; c < row.length; c++) { var x = self._range.min + c * self._range.step; var y = row[c]; if (y > 0) { var left = c * stepPixels; var width = Math.ceil(stepPixels); var height = Math.ceil(y * canvas.height / self._peak); ctx.fillRect(left, 0, width, height); } } ctx.restore(); }; for (var r = 0; r < this._binMatrix.length; r++) { renderRow( this._binMatrix[r], "binColors" in options && r < options.binColors.length ? options.binColors[r] : makeColor(r) ); } /* * Draw highlight */ if (this._highlight != null) { ctx.fillStyle = "rgba(192,192,192, 0.5)"; ctx.globalCompositeOperation = "source-over"; if (this._highlight.from > this._range.min) { ctx.fillRect( 0, 0, (this._highlight.from - this._range.min) * stepScale, canvas.height ); } if (this._highlight.to < this._range.max) { ctx.fillRect( (this._highlight.to - this._range.min) * stepScale, 0, canvas.width - (this._highlight.to - this._range.min) * stepScale, canvas.height ); } } ctx.restore(); };