/ajax/libs/cubism/1.1.0/cubism.v1.js
JavaScript | 995 lines | 774 code | 163 blank | 58 comment | 113 complexity | 3ac3c920d84136318f151cb94502095e MD5 | raw file
- (function(exports){
- var cubism = exports.cubism = {version: "1.1.0"};
- var cubism_id = 0;
- function cubism_identity(d) { return d; }
- cubism.option = function(name, defaultValue) {
- var values = cubism.options(name);
- return values.length ? values[0] : defaultValue;
- };
- cubism.options = function(name, defaultValues) {
- var options = location.search.substring(1).split("&"),
- values = [],
- i = -1,
- n = options.length,
- o;
- while (++i < n) {
- if ((o = options[i].split("="))[0] == name) {
- values.push(decodeURIComponent(o[1]));
- }
- }
- return values.length || arguments.length < 2 ? values : defaultValues;
- };
- cubism.context = function() {
- var context = new cubism_context,
- step = 1e4, // ten seconds, in milliseconds
- size = 1440, // four hours at ten seconds, in pixels
- start0, stop0, // the start and stop for the previous change event
- start1, stop1, // the start and stop for the next prepare event
- serverDelay = 5e3,
- clientDelay = 5e3,
- event = d3.dispatch("prepare", "beforechange", "change", "focus"),
- scale = context.scale = d3.time.scale().range([0, size]),
- timeout,
- focus;
- function update() {
- var now = Date.now();
- stop0 = new Date(Math.floor((now - serverDelay - clientDelay) / step) * step);
- start0 = new Date(stop0 - size * step);
- stop1 = new Date(Math.floor((now - serverDelay) / step) * step);
- start1 = new Date(stop1 - size * step);
- scale.domain([start0, stop0]);
- return context;
- }
- context.start = function() {
- if (timeout) clearTimeout(timeout);
- var delay = +stop1 + serverDelay - Date.now();
- // If we're too late for the first prepare event, skip it.
- if (delay < clientDelay) delay += step;
- timeout = setTimeout(function prepare() {
- stop1 = new Date(Math.floor((Date.now() - serverDelay) / step) * step);
- start1 = new Date(stop1 - size * step);
- event.prepare.call(context, start1, stop1);
- setTimeout(function() {
- scale.domain([start0 = start1, stop0 = stop1]);
- event.beforechange.call(context, start1, stop1);
- event.change.call(context, start1, stop1);
- event.focus.call(context, focus);
- }, clientDelay);
- timeout = setTimeout(prepare, step);
- }, delay);
- return context;
- };
- context.stop = function() {
- timeout = clearTimeout(timeout);
- return context;
- };
- timeout = setTimeout(context.start, 10);
- // Set or get the step interval in milliseconds.
- // Defaults to ten seconds.
- context.step = function(_) {
- if (!arguments.length) return step;
- step = +_;
- return update();
- };
- // Set or get the context size (the count of metric values).
- // Defaults to 1440 (four hours at ten seconds).
- context.size = function(_) {
- if (!arguments.length) return size;
- scale.range([0, size = +_]);
- return update();
- };
- // The server delay is the amount of time we wait for the server to compute a
- // metric. This delay may result from clock skew or from delays collecting
- // metrics from various hosts. Defaults to 4 seconds.
- context.serverDelay = function(_) {
- if (!arguments.length) return serverDelay;
- serverDelay = +_;
- return update();
- };
- // The client delay is the amount of additional time we wait to fetch those
- // metrics from the server. The client and server delay combined represent the
- // age of the most recent displayed metric. Defaults to 1 second.
- context.clientDelay = function(_) {
- if (!arguments.length) return clientDelay;
- clientDelay = +_;
- return update();
- };
- // Sets the focus to the specified index, and dispatches a "focus" event.
- context.focus = function(i) {
- event.focus.call(context, focus = i);
- return context;
- };
- // Add, remove or get listeners for events.
- context.on = function(type, listener) {
- if (arguments.length < 2) return event.on(type);
- event.on(type, listener);
- // Notify the listener of the current start and stop time, as appropriate.
- // This way, metrics can make requests for data immediately,
- // and likewise the axis can display itself synchronously.
- if (listener != null) {
- if (/^prepare(\.|$)/.test(type)) listener.call(context, start1, stop1);
- if (/^beforechange(\.|$)/.test(type)) listener.call(context, start0, stop0);
- if (/^change(\.|$)/.test(type)) listener.call(context, start0, stop0);
- if (/^focus(\.|$)/.test(type)) listener.call(context, focus);
- }
- return context;
- };
- d3.select(window).on("keydown.context-" + ++cubism_id, function() {
- switch (!d3.event.metaKey && d3.event.keyCode) {
- case 37: // left
- if (focus == null) focus = size - 1;
- if (focus > 0) context.focus(--focus);
- break;
- case 39: // right
- if (focus == null) focus = size - 2;
- if (focus < size - 1) context.focus(++focus);
- break;
- default: return;
- }
- d3.event.preventDefault();
- });
- return update();
- };
- function cubism_context() {}
- var cubism_contextPrototype = cubism_context.prototype;
- cubism_contextPrototype.constant = function(value) {
- return new cubism_metricConstant(this, +value);
- };
- cubism_contextPrototype.cube = function(host) {
- if (!arguments.length) host = "";
- var source = {},
- context = this;
- source.metric = function(expression) {
- return context.metric(function(start, stop, step, callback) {
- d3.json(host + "/1.0/metric"
- + "?expression=" + encodeURIComponent(expression)
- + "&start=" + cubism_cubeFormatDate(start)
- + "&stop=" + cubism_cubeFormatDate(stop)
- + "&step=" + step, function(data) {
- if (!data) return callback(new Error("unable to load data"));
- callback(null, data.map(function(d) { return d.value; }));
- });
- }, expression += "");
- };
- // Returns the Cube host.
- source.toString = function() {
- return host;
- };
- return source;
- };
- var cubism_cubeFormatDate = d3.time.format.iso;
- cubism_contextPrototype.graphite = function(host) {
- if (!arguments.length) host = "";
- var source = {},
- context = this;
- source.metric = function(expression) {
- var metric = context.metric(function(start, stop, step, callback) {
- d3.text(host + "/render?format=raw"
- + "&target=" + encodeURIComponent("alias(" + expression + ",'')")
- + "&from=" + cubism_graphiteFormatDate(start - 2 * step) // off-by-two?
- + "&until=" + cubism_graphiteFormatDate(stop - 1000), function(text) {
- if (!text) return callback(new Error("unable to load data"));
- callback(null, cubism_graphiteParse(text));
- });
- }, expression += "");
- metric.summarize = summarize;
- return metric;
- };
- source.find = function(pattern, callback) {
- d3.json(host + "/metrics/find?format=completer"
- + "&query=" + encodeURIComponent(pattern), function(result) {
- if (!result) return callback(new Error("unable to find metrics"));
- callback(null, result.metrics.map(function(d) { return d.path; }));
- });
- };
- // Returns the graphite host.
- source.toString = function() {
- return host;
- };
- function summarize(method) {
- var step = Math.round(context.step() / 1e3);
- if (step === 10) return this;
- step = !(step % 3600) ? step / 3600 + "hour" : !(step % 60) ? step / 60 + "min" : step + "sec";
- return source.metric("summarize(" + this + ",'" + step + "','" + method + "')");
- }
- return source;
- };
- // Graphite understands seconds since UNIX epoch.
- function cubism_graphiteFormatDate(time) {
- return Math.floor(time / 1000);
- }
- // Helper method for parsing graphite's raw format.
- function cubism_graphiteParse(text) {
- var i = text.indexOf("|"),
- meta = text.substring(0, i),
- c = meta.lastIndexOf(","),
- b = meta.lastIndexOf(",", c - 1),
- a = meta.lastIndexOf(",", b - 1),
- start = meta.substring(a + 1, b) * 1000,
- step = meta.substring(c + 1) * 1000;
- return text
- .substring(i + 1)
- .split(",")
- .slice(1) // the first value is always None?
- .map(function(d) { return +d; });
- }
- function cubism_metric(context) {
- if (!(context instanceof cubism_context)) throw new Error("invalid context");
- this.context = context;
- }
- var cubism_metricPrototype = cubism_metric.prototype;
- cubism_metricPrototype.valueAt = function() {
- return NaN;
- };
- cubism_metricPrototype.alias = function(name) {
- this.toString = function() { return name; };
- return this;
- };
- cubism_metricPrototype.extent = function() {
- var i = 0,
- n = this.context.size(),
- value,
- min = Infinity,
- max = -Infinity;
- while (++i < n) {
- value = this.valueAt(i);
- if (value < min) min = value;
- if (value > max) max = value;
- }
- return [min, max];
- };
- cubism_metricPrototype.on = function(type, listener) {
- return arguments.length < 2 ? null : this;
- };
- cubism_metricPrototype.shift = function() {
- return this;
- };
- cubism_metricPrototype.on = function() {
- return arguments.length < 2 ? null : this;
- };
- cubism_contextPrototype.metric = function(request, name) {
- var context = this,
- metric = new cubism_metric(context),
- id = ".metric-" + ++cubism_id,
- start = -Infinity,
- stop,
- step = context.step(),
- size = context.size(),
- values = [],
- event = d3.dispatch("change"),
- listening = 0,
- fetching;
- // Prefetch new data into a temporary array.
- function prepare(start1, stop) {
- var steps = Math.min(size, Math.round((start1 - start) / step));
- if (!steps || fetching) return; // already fetched, or fetching!
- fetching = true;
- steps = Math.min(size, steps + cubism_metricOverlap);
- var start0 = new Date(stop - steps * step);
- request(start0, stop, step, function(error, data) {
- fetching = false;
- if (error) return console.warn(error);
- var i = isFinite(start) ? Math.round((start0 - start) / step) : 0;
- for (var j = 0, m = data.length; j < m; ++j) values[j + i] = data[j];
- event.change.call(metric, start, stop);
- });
- }
- // When the context changes, switch to the new data, ready-or-not!
- function beforechange(start1, stop1) {
- if (!isFinite(start)) start = start1;
- values.splice(0, Math.max(0, Math.min(size, Math.round((start1 - start) / step))));
- start = start1;
- stop = stop1;
- }
- //
- metric.valueAt = function(i) {
- return values[i];
- };
- //
- metric.shift = function(offset) {
- return context.metric(cubism_metricShift(request, +offset));
- };
- //
- metric.on = function(type, listener) {
- if (!arguments.length) return event.on(type);
- // If there are no listeners, then stop listening to the context,
- // and avoid unnecessary fetches.
- if (listener == null) {
- if (event.on(type) != null && --listening == 0) {
- context.on("prepare" + id, null).on("beforechange" + id, null);
- }
- } else {
- if (event.on(type) == null && ++listening == 1) {
- context.on("prepare" + id, prepare).on("beforechange" + id, beforechange);
- }
- }
- event.on(type, listener);
- // Notify the listener of the current start and stop time, as appropriate.
- // This way, charts can display synchronous metrics immediately.
- if (listener != null) {
- if (/^change(\.|$)/.test(type)) listener.call(context, start, stop);
- }
- return metric;
- };
- //
- if (arguments.length > 1) metric.toString = function() {
- return name;
- };
- return metric;
- };
- // Number of metric to refetch each period, in case of lag.
- var cubism_metricOverlap = 6;
- // Wraps the specified request implementation, and shifts time by the given offset.
- function cubism_metricShift(request, offset) {
- return function(start, stop, step, callback) {
- request(new Date(+start + offset), new Date(+stop + offset), step, callback);
- };
- }
- function cubism_metricConstant(context, value) {
- cubism_metric.call(this, context);
- value = +value;
- var name = value + "";
- this.valueOf = function() { return value; };
- this.toString = function() { return name; };
- }
- var cubism_metricConstantPrototype = cubism_metricConstant.prototype = Object.create(cubism_metric.prototype);
- cubism_metricConstantPrototype.valueAt = function() {
- return +this;
- };
- cubism_metricConstantPrototype.extent = function() {
- return [+this, +this];
- };
- function cubism_metricOperator(name, operate) {
- function cubism_metricOperator(left, right) {
- if (!(right instanceof cubism_metric)) right = new cubism_metricConstant(left.context, right);
- else if (left.context !== right.context) throw new Error("mismatch context");
- cubism_metric.call(this, left.context);
- this.left = left;
- this.right = right;
- this.toString = function() { return left + " " + name + " " + right; };
- }
- var cubism_metricOperatorPrototype = cubism_metricOperator.prototype = Object.create(cubism_metric.prototype);
- cubism_metricOperatorPrototype.valueAt = function(i) {
- return operate(this.left.valueAt(i), this.right.valueAt(i));
- };
- cubism_metricOperatorPrototype.shift = function(offset) {
- return new cubism_metricOperator(this.left.shift(offset), this.right.shift(offset));
- };
- cubism_metricOperatorPrototype.on = function(type, listener) {
- if (arguments.length < 2) return this.left.on(type);
- this.left.on(type, listener);
- this.right.on(type, listener);
- return this;
- };
- return function(right) {
- return new cubism_metricOperator(this, right);
- };
- }
- cubism_metricPrototype.add = cubism_metricOperator("+", function(left, right) {
- return left + right;
- });
- cubism_metricPrototype.subtract = cubism_metricOperator("-", function(left, right) {
- return left - right;
- });
- cubism_metricPrototype.multiply = cubism_metricOperator("*", function(left, right) {
- return left * right;
- });
- cubism_metricPrototype.divide = cubism_metricOperator("/", function(left, right) {
- return left / right;
- });
- cubism_contextPrototype.horizon = function() {
- var context = this,
- mode = "offset",
- buffer = document.createElement("canvas"),
- width = buffer.width = context.size(),
- height = buffer.height = 30,
- scale = d3.scale.linear().interpolate(d3.interpolateRound),
- metric = cubism_identity,
- extent = null,
- title = cubism_identity,
- format = d3.format(".2s"),
- colors = ["#08519c","#3182bd","#6baed6","#bdd7e7","#bae4b3","#74c476","#31a354","#006d2c"];
- function horizon(selection) {
- selection
- .on("mousemove.horizon", function() { context.focus(d3.mouse(this)[0]); })
- .on("mouseout.horizon", function() { context.focus(null); });
- selection.append("canvas")
- .attr("width", width)
- .attr("height", height);
- selection.append("span")
- .attr("class", "title")
- .text(title);
- selection.append("span")
- .attr("class", "value");
- selection.each(function(d, i) {
- var that = this,
- id = ++cubism_id,
- metric_ = typeof metric === "function" ? metric.call(that, d, i) : metric,
- colors_ = typeof colors === "function" ? colors.call(that, d, i) : colors,
- extent_ = typeof extent === "function" ? extent.call(that, d, i) : extent,
- start = -Infinity,
- step = context.step(),
- canvas = d3.select(that).select("canvas"),
- span = d3.select(that).select(".value"),
- max_,
- m = colors_.length >> 1,
- ready;
- canvas.datum({id: id, metric: metric_});
- canvas = canvas.node().getContext("2d");
- function change(start1, stop) {
- canvas.save();
- // compute the new extent and ready flag
- var extent = metric_.extent();
- ready = extent.every(isFinite);
- if (extent_ != null) extent = extent_;
- // if this is an update (with no extent change), copy old values!
- var i0 = 0, max = Math.max(-extent[0], extent[1]);
- if (this === context) {
- if (max == max_) {
- i0 = width - cubism_metricOverlap;
- var dx = (start1 - start) / step;
- if (dx < width) {
- var canvas0 = buffer.getContext("2d");
- canvas0.clearRect(0, 0, width, height);
- canvas0.drawImage(canvas.canvas, dx, 0, width - dx, height, 0, 0, width - dx, height);
- canvas.clearRect(0, 0, width, height);
- canvas.drawImage(canvas0.canvas, 0, 0);
- }
- }
- start = start1;
- }
- // update the domain
- scale.domain([0, max_ = max]);
- // clear for the new data
- canvas.clearRect(i0, 0, width - i0, height);
- // record whether there are negative values to display
- var negative;
- // positive bands
- for (var j = 0; j < m; ++j) {
- canvas.fillStyle = colors_[m + j];
- // Adjust the range based on the current band index.
- var y0 = (j - m + 1) * height;
- scale.range([m * height + y0, y0]);
- y0 = scale(0);
- for (var i = i0, n = width, y1; i < n; ++i) {
- y1 = metric_.valueAt(i);
- if (y1 <= 0) { negative = true; continue; }
- canvas.fillRect(i, y1 = scale(y1), 1, y0 - y1);
- }
- }
- if (negative) {
- // enable offset mode
- if (mode === "offset") {
- canvas.translate(0, height);
- canvas.scale(1, -1);
- }
- // negative bands
- for (var j = 0; j < m; ++j) {
- canvas.fillStyle = colors_[m - 1 - j];
- // Adjust the range based on the current band index.
- var y0 = (j - m + 1) * height;
- scale.range([m * height + y0, y0]);
- y0 = scale(0);
- for (var i = i0, n = width, y1; i < n; ++i) {
- y1 = metric_.valueAt(i);
- if (y1 >= 0) continue;
- canvas.fillRect(i, scale(-y1), 1, y0 - scale(-y1));
- }
- }
- }
- canvas.restore();
- }
- function focus(i) {
- if (i == null) i = width - 1;
- var value = metric_.valueAt(i);
- span.datum(value).text(isNaN(value) ? null : format);
- }
- // Update the chart when the context changes.
- context.on("change.horizon-" + id, change);
- context.on("focus.horizon-" + id, focus);
- // Display the first metric change immediately,
- // but defer subsequent updates to the canvas change.
- // Note that someone still needs to listen to the metric,
- // so that it continues to update automatically.
- metric_.on("change.horizon-" + id, function(start, stop) {
- change(start, stop), focus();
- if (ready) metric_.on("change.horizon-" + id, cubism_identity);
- });
- });
- }
- horizon.remove = function(selection) {
- selection
- .on("mousemove.horizon", null)
- .on("mouseout.horizon", null);
- selection.selectAll("canvas")
- .each(remove)
- .remove();
- selection.selectAll(".title,.value")
- .remove();
- function remove(d) {
- d.metric.on("change.horizon-" + d.id, null);
- context.on("change.horizon-" + d.id, null);
- context.on("focus.horizon-" + d.id, null);
- }
- };
- horizon.mode = function(_) {
- if (!arguments.length) return mode;
- mode = _ + "";
- return horizon;
- };
- horizon.height = function(_) {
- if (!arguments.length) return height;
- buffer.height = height = +_;
- return horizon;
- };
- horizon.metric = function(_) {
- if (!arguments.length) return metric;
- metric = _;
- return horizon;
- };
- horizon.scale = function(_) {
- if (!arguments.length) return scale;
- scale = _;
- return horizon;
- };
- horizon.extent = function(_) {
- if (!arguments.length) return extent;
- extent = _;
- return horizon;
- };
- horizon.title = function(_) {
- if (!arguments.length) return title;
- title = _;
- return horizon;
- };
- horizon.format = function(_) {
- if (!arguments.length) return format;
- format = _;
- return horizon;
- };
- horizon.colors = function(_) {
- if (!arguments.length) return colors;
- colors = _;
- return horizon;
- };
- return horizon;
- };
- cubism_contextPrototype.comparison = function() {
- var context = this,
- width = context.size(),
- height = 120,
- scale = d3.scale.linear().interpolate(d3.interpolateRound),
- primary = function(d) { return d[0]; },
- secondary = function(d) { return d[1]; },
- extent = null,
- title = cubism_identity,
- formatPrimary = cubism_comparisonPrimaryFormat,
- formatChange = cubism_comparisonChangeFormat,
- colors = ["#9ecae1", "#225b84", "#a1d99b", "#22723a"],
- strokeWidth = 1.5;
- function comparison(selection) {
- selection
- .on("mousemove.comparison", function() { context.focus(d3.mouse(this)[0]); })
- .on("mouseout.comparison", function() { context.focus(null); });
- selection.append("canvas")
- .attr("width", width)
- .attr("height", height);
- selection.append("span")
- .attr("class", "title")
- .text(title);
- selection.append("span")
- .attr("class", "value primary");
- selection.append("span")
- .attr("class", "value change");
- selection.each(function(d, i) {
- var that = this,
- id = ++cubism_id,
- primary_ = typeof primary === "function" ? primary.call(that, d, i) : primary,
- secondary_ = typeof secondary === "function" ? secondary.call(that, d, i) : secondary,
- extent_ = typeof extent === "function" ? extent.call(that, d, i) : extent,
- div = d3.select(that),
- canvas = div.select("canvas"),
- spanPrimary = div.select(".value.primary"),
- spanChange = div.select(".value.change"),
- ready;
- canvas.datum({id: id, primary: primary_, secondary: secondary_});
- canvas = canvas.node().getContext("2d");
- function change(start, stop) {
- canvas.save();
- canvas.clearRect(0, 0, width, height);
- // update the scale
- var primaryExtent = primary_.extent(),
- secondaryExtent = secondary_.extent(),
- extent = extent_ == null ? primaryExtent : extent_;
- scale.domain(extent).range([height, 0]);
- ready = primaryExtent.concat(secondaryExtent).every(isFinite);
- // consistent overplotting
- var round = start / context.step() & 1
- ? cubism_comparisonRoundOdd
- : cubism_comparisonRoundEven;
- // positive changes
- canvas.fillStyle = colors[2];
- for (var i = 0, n = width; i < n; ++i) {
- var y0 = scale(primary_.valueAt(i)),
- y1 = scale(secondary_.valueAt(i));
- if (y0 < y1) canvas.fillRect(round(i), y0, 1, y1 - y0);
- }
- // negative changes
- canvas.fillStyle = colors[0];
- for (i = 0; i < n; ++i) {
- var y0 = scale(primary_.valueAt(i)),
- y1 = scale(secondary_.valueAt(i));
- if (y0 > y1) canvas.fillRect(round(i), y1, 1, y0 - y1);
- }
- // positive values
- canvas.fillStyle = colors[3];
- for (i = 0; i < n; ++i) {
- var y0 = scale(primary_.valueAt(i)),
- y1 = scale(secondary_.valueAt(i));
- if (y0 <= y1) canvas.fillRect(round(i), y0, 1, strokeWidth);
- }
- // negative values
- canvas.fillStyle = colors[1];
- for (i = 0; i < n; ++i) {
- var y0 = scale(primary_.valueAt(i)),
- y1 = scale(secondary_.valueAt(i));
- if (y0 > y1) canvas.fillRect(round(i), y0 - strokeWidth, 1, strokeWidth);
- }
- canvas.restore();
- }
- function focus(i) {
- if (i == null) i = width - 1;
- var valuePrimary = primary_.valueAt(i),
- valueSecondary = secondary_.valueAt(i),
- valueChange = (valuePrimary - valueSecondary) / valueSecondary;
- spanPrimary
- .datum(valuePrimary)
- .text(isNaN(valuePrimary) ? null : formatPrimary);
- spanChange
- .datum(valueChange)
- .text(isNaN(valueChange) ? null : formatChange)
- .attr("class", "value change " + (valueChange > 0 ? "positive" : valueChange < 0 ? "negative" : ""));
- }
- // Display the first primary change immediately,
- // but defer subsequent updates to the context change.
- // Note that someone still needs to listen to the metric,
- // so that it continues to update automatically.
- primary_.on("change.comparison-" + id, firstChange);
- secondary_.on("change.comparison-" + id, firstChange);
- function firstChange(start, stop) {
- change(start, stop), focus();
- if (ready) {
- primary_.on("change.comparison-" + id, cubism_identity);
- secondary_.on("change.comparison-" + id, cubism_identity);
- }
- }
- // Update the chart when the context changes.
- context.on("change.comparison-" + id, change);
- context.on("focus.comparison-" + id, focus);
- });
- }
- comparison.remove = function(selection) {
- selection
- .on("mousemove.comparison", null)
- .on("mouseout.comparison", null);
- selection.selectAll("canvas")
- .each(remove)
- .remove();
- selection.selectAll(".title,.value")
- .remove();
- function remove(d) {
- d.primary.on("change.comparison-" + d.id, null);
- d.secondary.on("change.comparison-" + d.id, null);
- context.on("change.comparison-" + d.id, null);
- context.on("focus.comparison-" + d.id, null);
- }
- };
- comparison.height = function(_) {
- if (!arguments.length) return height;
- height = +_;
- return comparison;
- };
- comparison.primary = function(_) {
- if (!arguments.length) return primary;
- primary = _;
- return comparison;
- };
- comparison.secondary = function(_) {
- if (!arguments.length) return secondary;
- secondary = _;
- return comparison;
- };
- comparison.scale = function(_) {
- if (!arguments.length) return scale;
- scale = _;
- return comparison;
- };
- comparison.extent = function(_) {
- if (!arguments.length) return extent;
- extent = _;
- return comparison;
- };
- comparison.title = function(_) {
- if (!arguments.length) return title;
- title = _;
- return comparison;
- };
- comparison.formatPrimary = function(_) {
- if (!arguments.length) return formatPrimary;
- formatPrimary = _;
- return comparison;
- };
- comparison.formatChange = function(_) {
- if (!arguments.length) return formatChange;
- formatChange = _;
- return comparison;
- };
- comparison.colors = function(_) {
- if (!arguments.length) return colors;
- colors = _;
- return comparison;
- };
- comparison.strokeWidth = function(_) {
- if (!arguments.length) return strokeWidth;
- strokeWidth = _;
- return comparison;
- };
- return comparison;
- };
- var cubism_comparisonPrimaryFormat = d3.format(".2s"),
- cubism_comparisonChangeFormat = d3.format("+.0%");
- function cubism_comparisonRoundEven(i) {
- return i & 0xfffffe;
- }
- function cubism_comparisonRoundOdd(i) {
- return ((i + 1) & 0xfffffe) - 1;
- }
- cubism_contextPrototype.axis = function() {
- var context = this,
- scale = context.scale,
- axis_ = d3.svg.axis().scale(scale);
- var format = context.step() < 6e4 ? cubism_axisFormatSeconds
- : context.step() < 864e5 ? cubism_axisFormatMinutes
- : cubism_axisFormatDays;
- function axis(selection) {
- var id = ++cubism_id,
- tick;
- var g = selection.append("svg")
- .datum({id: id})
- .attr("width", context.size())
- .attr("height", Math.max(28, -axis.tickSize()))
- .append("g")
- .attr("transform", "translate(0," + (axis_.orient() === "top" ? 27 : 4) + ")")
- .call(axis_);
- context.on("change.axis-" + id, function() {
- g.call(axis_);
- if (!tick) tick = d3.select(g.node().appendChild(g.selectAll("text").node().cloneNode(true)))
- .style("display", "none")
- .text(null);
- });
- context.on("focus.axis-" + id, function(i) {
- if (tick) {
- if (i == null) {
- tick.style("display", "none");
- g.selectAll("text").style("fill-opacity", null);
- } else {
- tick.style("display", null).attr("x", i).text(format(scale.invert(i)));
- var dx = tick.node().getComputedTextLength() + 6;
- g.selectAll("text").style("fill-opacity", function(d) { return Math.abs(scale(d) - i) < dx ? 0 : 1; });
- }
- }
- });
- }
- axis.remove = function(selection) {
- selection.selectAll("svg")
- .each(remove)
- .remove();
- function remove(d) {
- context.on("change.axis-" + d.id, null);
- context.on("focus.axis-" + d.id, null);
- }
- };
- return d3.rebind(axis, axis_,
- "orient",
- "ticks",
- "tickSubdivide",
- "tickSize",
- "tickPadding",
- "tickFormat");
- };
- var cubism_axisFormatSeconds = d3.time.format("%I:%M:%S %p"),
- cubism_axisFormatMinutes = d3.time.format("%I:%M %p"),
- cubism_axisFormatDays = d3.time.format("%B %d");
- cubism_contextPrototype.rule = function() {
- var context = this;
- function rule(selection) {
- var id = ++cubism_id;
- var line = selection.append("div")
- .datum({id: id})
- .attr("class", "line")
- .style("position", "fixed")
- .style("top", 0)
- .style("right", 0)
- .style("bottom", 0)
- .style("width", "1px")
- .style("pointer-events", "none");
- context.on("focus.rule-" + id, function(i) {
- line
- .style("display", i == null ? "none" : null)
- .style("left", function() { return this.parentNode.getBoundingClientRect().left + i + "px"; });
- });
- }
- rule.remove = function(selection) {
- selection.selectAll(".line")
- .each(remove)
- .remove();
- function remove(d) {
- context.on("focus.rule-" + d.id, null);
- }
- };
- return rule;
- };
- })(this);