From 8b5cabe29c59184ae8337031eae6562ec44d9994 Mon Sep 17 00:00:00 2001 From: iver56 Date: Sun, 25 Sep 2016 00:30:17 +0200 Subject: [PATCH] Minify cubism --- web_client/index.html | 2 +- web_client/lib/cubism/cubism.v1.js | 1331 ------------------------ web_client/lib/cubism/cubism.v1.min.js | 1 + 3 files changed, 2 insertions(+), 1332 deletions(-) delete mode 100644 web_client/lib/cubism/cubism.v1.js create mode 100644 web_client/lib/cubism/cubism.v1.min.js diff --git a/web_client/index.html b/web_client/index.html index b8ab481..733f575 100644 --- a/web_client/index.html +++ b/web_client/index.html @@ -19,7 +19,7 @@ - + diff --git a/web_client/lib/cubism/cubism.v1.js b/web_client/lib/cubism/cubism.v1.js deleted file mode 100644 index 01161aa..0000000 --- a/web_client/lib/cubism/cubism.v1.js +++ /dev/null @@ -1,1331 +0,0 @@ -(function(exports){ -var cubism = exports.cubism = {version: "1.6.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_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; -/* librato (http://dev.librato.com/v1/post/metrics) source - * If you want to see an example of how to use this source, check: https://gist.github.com/drio/5792680 - */ -cubism_contextPrototype.librato = function(user, token) { - var source = {}, - context = this; - auth_string = "Basic " + btoa(user + ":" + token); - avail_rsts = [ 1, 60, 900, 3600 ]; - - /* Given a step, find the best librato resolution to use. - * - * Example: - * - * (s) : cubism step - * - * avail_rsts 1 --------------- 60 --------------- 900 ---------------- 3600 - * | (s) | - * | | - * [low_res top_res] - * - * return: low_res (60) - */ - function find_ideal_librato_resolution(step) { - var highest_res = avail_rsts[0], - lowest_res = avail_rsts[avail_rsts.length]; // high and lowest available resolution from librato - - /* If step is outside the highest or lowest librato resolution, pick them and we are done */ - if (step >= lowest_res) - return lowest_res; - - if (step <= highest_res) - return highest_res; - - /* If not, find in what resolution interval the step lands. */ - var iof, top_res, i; - for (i=step; i<=lowest_res; i++) { - iof = avail_rsts.indexOf(i); - if (iof > -1) { - top_res = avail_rsts[iof]; - break; - } - } - - var low_res; - for (i=step; i>=highest_res; i--) { - iof = avail_rsts.indexOf(i); - if (iof > -1) { - low_res = avail_rsts[iof]; - break; - } - } - - /* What's the closest librato resolution given the step ? */ - return ((top_res-step) < (step-low_res)) ? top_res : low_res; - } - - function find_librato_resolution(sdate, edate, step) { - var i_size = edate - sdate, // interval size - month = 2419200, - week = 604800, - two_days = 172800, - ideal_res; - - if (i_size > month) - return 3600; - - ideal_res = find_ideal_librato_resolution(step); - - /* - * Now we have the ideal resolution, but due to the retention policies at librato, maybe we have - * to use a higher resolution. - * http://support.metrics.librato.com/knowledgebase/articles/66838-understanding-metrics-roll-ups-retention-and-grap - */ - if (i_size > week && ideal_res < 900) - return 900; - else if (i_size > two_days && ideal_res < 60) - return 60; - else - return ideal_res; - } - - /* All the logic to query the librato API is here */ - var librato_request = function(composite) { - var url_prefix = "https://metrics-api.librato.com/v1/metrics"; - - function make_url(sdate, edate, step) { - var params = "compose=" + composite + - "&start_time=" + sdate + - "&end_time=" + edate + - "&resolution=" + find_librato_resolution(sdate, edate, step); - return url_prefix + "?" + params; - } - - /* - * We are most likely not going to get the same number of measurements - * cubism expects for a particular context: We have to perform down/up - * sampling - */ - function down_up_sampling(isdate, iedate, step, librato_mm) { - var av = []; - - for (i=isdate; i<=iedate; i+=step) { - var int_mes = []; - while (librato_mm.length && librato_mm[0].measure_time <= i) { - int_mes.push(librato_mm.shift().value); - } - - var v; - if (int_mes.length) { /* Compute the average */ - v = int_mes.reduce(function(a, b) { return a + b }) / int_mes.length; - } else { /* No librato values on interval */ - v = (av.length) ? av[av.length-1] : 0; - } - av.push(v); - } - - return av; - } - - request = {}; - - request.fire = function(isdate, iedate, step, callback_done) { - var a_values = []; /* Store partial values from librato */ - - /* - * Librato has a limit in the number of measurements we get back in a request (100). - * We recursively perform requests to the API to ensure we have all the data points - * for the interval we are working on. - */ - function actual_request(full_url) { - d3.json(full_url) - .header("X-Requested-With", "XMLHttpRequest") - .header("Authorization", auth_string) - .header("Librato-User-Agent", 'cubism/' + cubism.version) - .get(function (error, data) { /* Callback; data available */ - if (!error) { - if (data.measurements.length === 0) { - return - } - data.measurements[0].series.forEach(function(o) { a_values.push(o); }); - - var still_more_values = 'query' in data && 'next_time' in data.query; - if (still_more_values) { - actual_request(make_url(data.query.next_time, iedate, step)); - } else { - var a_adjusted = down_up_sampling(isdate, iedate, step, a_values); - callback_done(a_adjusted); - } - } - }); - } - - actual_request(make_url(isdate, iedate, step)); - }; - - return request; - }; - - /* - * The user will use this method to create a cubism source (librato in this case) - * and call .metric() as necessary to create metrics. - */ - source.metric = function(m_composite) { - return context.metric(function(start, stop, step, callback) { - /* All the librato logic is here; .fire() retrieves the metrics' data */ - librato_request(m_composite) - .fire(cubism_libratoFormatDate(start), - cubism_libratoFormatDate(stop), - cubism_libratoFormatDate(step), - function(a_values) { callback(null, a_values); }); - - }, m_composite += ""); - }; - - /* This is not used when the source is librato */ - source.toString = function() { - return "librato"; - }; - - return source; -}; - -var cubism_libratoFormatDate = function(time) { - return Math.floor(time / 1000); -}; -cubism_contextPrototype.graphite = function(host) { - if (!arguments.length) host = ""; - var source = {}, - context = this; - - source.metric = function(expression) { - var sum = "sum"; - - var metric = context.metric(function(start, stop, step, callback) { - var target = expression; - - // Apply the summarize, if necessary. - if (step !== 1e4) target = "summarize(" + target + ",'" - + (!(step % 36e5) ? step / 36e5 + "hour" : !(step % 6e4) ? step / 6e4 + "min" : step / 1e3 + "sec") - + "','" + sum + "')"; - - d3.text(host + "/render?format=raw" - + "&target=" + encodeURIComponent("alias(" + target + ",'')") - + "&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 = function(_) { - sum = _; - return metric; - }; - - 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; - }; - - 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; }); -} -cubism_contextPrototype.gangliaWeb = function(config) { - var host = '', - uriPathPrefix = '/ganglia2/'; - - if (arguments.length) { - if (config.host) { - host = config.host; - } - - if (config.uriPathPrefix) { - uriPathPrefix = config.uriPathPrefix; - - /* Add leading and trailing slashes, as appropriate. */ - if( uriPathPrefix[0] != '/' ) { - uriPathPrefix = '/' + uriPathPrefix; - } - - if( uriPathPrefix[uriPathPrefix.length - 1] != '/' ) { - uriPathPrefix += '/'; - } - } - } - - var source = {}, - context = this; - - source.metric = function(metricInfo) { - - /* Store the members from metricInfo into local variables. */ - var clusterName = metricInfo.clusterName, - metricName = metricInfo.metricName, - hostName = metricInfo.hostName, - isReport = metricInfo.isReport || false, - titleGenerator = metricInfo.titleGenerator || - /* Reasonable (not necessarily pretty) default for titleGenerator. */ - function(unusedMetricInfo) { - /* unusedMetricInfo is, well, unused in this default case. */ - return ('clusterName:' + clusterName + - ' metricName:' + metricName + - (hostName ? ' hostName:' + hostName : '')); - }, - onChangeCallback = metricInfo.onChangeCallback; - - /* Default to plain, simple metrics. */ - var metricKeyName = isReport ? 'g' : 'm'; - - var gangliaWebMetric = context.metric(function(start, stop, step, callback) { - - function constructGangliaWebRequestQueryParams() { - return ('c=' + clusterName + - '&' + metricKeyName + '=' + metricName + - (hostName ? '&h=' + hostName : '') + - '&cs=' + start/1000 + '&ce=' + stop/1000 + '&step=' + step/1000 + '&graphlot=1'); - } - - d3.json(host + uriPathPrefix + 'graph.php?' + constructGangliaWebRequestQueryParams(), - function(result) { - if( !result ) { - return callback(new Error("Unable to fetch GangliaWeb data")); - } - - callback(null, result[0].data); - }); - - }, titleGenerator(metricInfo)); - - gangliaWebMetric.toString = function() { - return titleGenerator(metricInfo); - }; - - /* Allow users to run their custom code each time a gangliaWebMetric changes. - * - * TODO Consider abstracting away the naked Cubism call, and instead exposing - * a callback that takes in the values array (maybe alongwith the original - * start and stop 'naked' parameters), since it's handy to have the entire - * dataset at your disposal (and users will likely implement onChangeCallback - * primarily to get at this dataset). - */ - if (onChangeCallback) { - gangliaWebMetric.on('change', onChangeCallback); - } - - return gangliaWebMetric; - }; - - // Returns the gangliaWeb host + uriPathPrefix. - source.toString = function() { - return host + uriPathPrefix; - }; - - return source; -}; - -function cubism_metric(context) { - if (!(context instanceof cubism_context)) throw new Error("invalid context"); - this.context = context; -} - -var cubism_metricPrototype = cubism_metric.prototype; - -cubism.metric = cubism_metric; - -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(Math.round(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; } - if (y1 === undefined) 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(Math.round(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 formatDefault = context.step() < 6e4 ? cubism_axisFormatSeconds - : context.step() < 864e5 ? cubism_axisFormatMinutes - : cubism_axisFormatDays; - var format = formatDefault; - - 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); - } - }; - - axis.focusFormat = function(_) { - if (!arguments.length) return format == formatDefault ? null : _; - format = _ == null ? formatDefault : _; - return axis; - }; - - 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, - metric = cubism_identity; - - function rule(selection) { - var id = ++cubism_id; - - var line = selection.append("div") - .datum({id: id}) - .attr("class", "line") - .call(cubism_ruleStyle); - - selection.each(function(d, i) { - var that = this, - id = ++cubism_id, - metric_ = typeof metric === "function" ? metric.call(that, d, i) : metric; - - if (!metric_) return; - - function change(start, stop) { - var values = []; - - for (var i = 0, n = context.size(); i < n; ++i) { - if (metric_.valueAt(i)) { - values.push(i); - } - } - - var lines = selection.selectAll(".metric").data(values); - lines.exit().remove(); - lines.enter().append("div").attr("class", "metric line").call(cubism_ruleStyle); - lines.style("left", cubism_ruleLeft); - } - - context.on("change.rule-" + id, change); - metric_.on("change.rule-" + id, change); - }); - - context.on("focus.rule-" + id, function(i) { - line.datum(i) - .style("display", i == null ? "none" : null) - .style("left", i == null ? null : cubism_ruleLeft); - }); - } - - rule.remove = function(selection) { - - selection.selectAll(".line") - .each(remove) - .remove(); - - function remove(d) { - context.on("focus.rule-" + d.id, null); - } - }; - - rule.metric = function(_) { - if (!arguments.length) return metric; - metric = _; - return rule; - }; - - return rule; -}; - -function cubism_ruleStyle(line) { - line - .style("position", "absolute") - .style("top", 0) - .style("bottom", 0) - .style("width", "1px") - .style("pointer-events", "none"); -} - -function cubism_ruleLeft(i) { - return i + "px"; -} -})(this); diff --git a/web_client/lib/cubism/cubism.v1.min.js b/web_client/lib/cubism/cubism.v1.min.js new file mode 100644 index 0000000..7deb765 --- /dev/null +++ b/web_client/lib/cubism/cubism.v1.min.js @@ -0,0 +1 @@ +!function(a){function d(a){return a}function e(){}function j(a){return Math.floor(a/1e3)}function k(a){var b=a.indexOf("|"),c=a.substring(0,b),d=c.lastIndexOf(","),e=c.lastIndexOf(",",d-1),f=c.lastIndexOf(",",e-1);1e3*c.substring(f+1,e),1e3*c.substring(d+1);return a.substring(b+1).split(",").slice(1).map(function(a){return+a})}function l(a){if(!(a instanceof e))throw new Error("invalid context");this.context=a}function o(a,b){return function(c,d,e,f){a(new Date(+c+b),new Date(+d+b),e,f)}}function p(a,b){l.call(this,a),b=+b;var c=b+"";this.valueOf=function(){return b},this.toString=function(){return c}}function r(a,b){function c(b,c){if(c instanceof l){if(b.context!==c.context)throw new Error("mismatch context")}else c=new p(b.context,c);l.call(this,b.context),this.left=b,this.right=c,this.toString=function(){return b+" "+a+" "+c}}var d=c.prototype=Object.create(l.prototype);return d.valueAt=function(a){return b(this.left.valueAt(a),this.right.valueAt(a))},d.shift=function(a){return new c(this.left.shift(a),this.right.shift(a))},d.on=function(a,b){return arguments.length<2?this.left.on(a):(this.left.on(a,b),this.right.on(a,b),this)},function(a){return new c(this,a)}}function u(a){return 16777214&a}function v(a){return(a+1&16777214)-1}function z(a){a.style("position","absolute").style("top",0).style("bottom",0).style("width","1px").style("pointer-events","none")}function A(a){return a+"px"}var b=a.cubism={version:"1.6.0"},c=0;b.option=function(a,c){var d=b.options(a);return d.length?d[0]:c},b.options=function(a,b){for(var g,c=location.search.substring(1).split("&"),d=[],e=-1,f=c.length;++e0&&a.focus(--o);break;case 39:null==o&&(o=d-2),o=c)return c;if(a<=b)return b;var d,e,f;for(f=a;f<=c;f++)if(d=avail_rsts.indexOf(f),d>-1){e=avail_rsts[d];break}var g;for(f=a;f>=b;f--)if(d=avail_rsts.indexOf(f),d>-1){g=avail_rsts[d];break}return e-ae?3600:(i=f(c),d>g&&i<900?900:d>h&&i<60?60:i)}var d={},e=this;auth_string="Basic "+btoa(a+":"+c),avail_rsts=[1,60,900,3600];var j=function(a){function d(b,d,e){var f="compose="+a+"&start_time="+b+"&end_time="+d+"&resolution="+g(b,d,e);return c+"?"+f}function e(a,b,c,d){var e=[];for(i=a;i<=b;i+=c){for(var f=[];d.length&&d[0].measure_time<=i;)f.push(d.shift().value);var g;g=f.length?f.reduce(function(a,b){return a+b})/f.length:e.length?e[e.length-1]:0,e.push(g)}return e}var c="https://metrics-api.librato.com/v1/metrics";return request={},request.fire=function(a,c,f,g){function i(j){d3.json(j).header("X-Requested-With","XMLHttpRequest").header("Authorization",auth_string).header("Librato-User-Agent","cubism/"+b.version).get(function(b,j){if(!b){if(0===j.measurements.length)return;j.measurements[0].series.forEach(function(a){h.push(a)});var k="query"in j&&"next_time"in j.query;if(k)i(d(j.query.next_time,c,f));else{var l=e(a,c,f,h);g(l)}}})}var h=[];i(d(a,c,f))},request};return d.metric=function(a){return e.metric(function(b,c,d,e){j(a).fire(h(b),h(c),h(d),function(a){e(null,a)})},a+="")},d.toString=function(){return"librato"},d};var h=function(a){return Math.floor(a/1e3)};f.graphite=function(a){arguments.length||(a="");var b={},c=this;return b.metric=function(b){var d="sum",e=c.metric(function(c,e,f,g){var h=b;1e4!==f&&(h="summarize("+h+",'"+(f%36e5?f%6e4?f/1e3+"sec":f/6e4+"min":f/36e5+"hour")+"','"+d+"')"),d3.text(a+"/render?format=raw&target="+encodeURIComponent("alias("+h+",'')")+"&from="+j(c-2*f)+"&until="+j(e-1e3),function(a){return a?void g(null,k(a)):g(new Error("unable to load data"))})},b+="");return e.summarize=function(a){return d=a,e},e},b.find=function(b,c){d3.json(a+"/metrics/find?format=completer&query="+encodeURIComponent(b),function(a){return a?void c(null,a.metrics.map(function(a){return a.path})):c(new Error("unable to find metrics"))})},b.toString=function(){return a},b},f.gangliaWeb=function(a){var b="",c="/ganglia2/";arguments.length&&(a.host&&(b=a.host),a.uriPathPrefix&&(c=a.uriPathPrefix,"/"!=c[0]&&(c="/"+c),"/"!=c[c.length-1]&&(c+="/")));var d={},e=this;return d.metric=function(a){var d=a.clusterName,f=a.metricName,g=a.hostName,h=a.isReport||!1,i=a.titleGenerator||function(a){return"clusterName:"+d+" metricName:"+f+(g?" hostName:"+g:"")},j=a.onChangeCallback,k=h?"g":"m",l=e.metric(function(a,e,h,i){function j(){return"c="+d+"&"+k+"="+f+(g?"&h="+g:"")+"&cs="+a/1e3+"&ce="+e/1e3+"&step="+h/1e3+"&graphlot=1"}d3.json(b+c+"graph.php?"+j(),function(a){return a?void i(null,a[0].data):i(new Error("Unable to fetch GangliaWeb data"))})},i(a));return l.toString=function(){return i(a)},j&&l.on("change",j),l},d.toString=function(){return b+c},d};var m=l.prototype;b.metric=l,m.valueAt=function(){return NaN},m.alias=function(a){return this.toString=function(){return a},this},m.extent=function(){for(var c,a=0,b=this.context.size(),d=1/0,e=-(1/0);++ae&&(e=c);return[d,e]},m.on=function(a,b){return arguments.length<2?null:this},m.shift=function(){return this},m.on=function(){return arguments.length<2?null:this},f.metric=function(a,b){function r(b,c){var d=Math.min(j,Math.round((b-g)/i));if(d&&!q){q=!0,d=Math.min(j,d+n);var f=new Date(c-d*i);a(f,c,i,function(a,b){if(q=!1,a)return console.warn(a);for(var d=isFinite(g)?Math.round((f-g)/i):0,h=0,j=b.length;h1&&(e.toString=function(){return b}),e};var n=6,q=p.prototype=Object.create(l.prototype);q.valueAt=function(){return+this},q.extent=function(){return[+this,+this]},m.add=r("+",function(a,b){return a+b}),m.subtract=r("-",function(a,b){return a-b}),m.multiply=r("*",function(a,b){return a*b}),m.divide=r("/",function(a,b){return a/b}),f.horizon=function(){function o(o){o.on("mousemove.horizon",function(){a.focus(Math.round(d3.mouse(this)[0]))}).on("mouseout.horizon",function(){a.focus(null)}),o.append("canvas").attr("width",f).attr("height",g),o.append("span").attr("class","title").text(k),o.append("span").attr("class","value"),o.each(function(k,o){function B(c,d){w.save();var i=r.extent();A=i.every(isFinite),null!=t&&(i=t);var j=0,k=Math.max(-i[0],i[1]);if(this===a){if(k==y){j=f-n;var l=(c-u)/v;if(l=0||w.fillRect(x,h(-C),1,q-h(-C))}}w.restore()}function C(a){null==a&&(a=f-1);var b=r.valueAt(a);x.datum(b).text(isNaN(b)?null:l)}var y,A,p=this,q=++c,r="function"==typeof i?i.call(p,k,o):i,s="function"==typeof m?m.call(p,k,o):m,t="function"==typeof j?j.call(p,k,o):j,u=-(1/0),v=a.step(),w=d3.select(p).select("canvas"),x=d3.select(p).select(".value"),z=s.length>>1;w.datum({id:q,metric:r}),w=w.node().getContext("2d"),a.on("change.horizon-"+q,B),a.on("focus.horizon-"+q,C),r.on("change.horizon-"+q,function(a,b){B(a,b),C(),A&&r.on("change.horizon-"+q,d)})})}var a=this,b="offset",e=document.createElement("canvas"),f=e.width=a.size(),g=e.height=30,h=d3.scale.linear().interpolate(d3.interpolateRound),i=d,j=null,k=d,l=d3.format(".2s"),m=["#08519c","#3182bd","#6baed6","#bdd7e7","#bae4b3","#74c476","#31a354","#006d2c"];return o.remove=function(b){function c(b){b.metric.on("change.horizon-"+b.id,null),a.on("change.horizon-"+b.id,null),a.on("focus.horizon-"+b.id,null)}b.on("mousemove.horizon",null).on("mouseout.horizon",null),b.selectAll("canvas").each(c).remove(),b.selectAll(".title,.value").remove()},o.mode=function(a){return arguments.length?(b=a+"",o):b},o.height=function(a){return arguments.length?(e.height=g=+a,o):g},o.metric=function(a){return arguments.length?(i=a,o):i},o.scale=function(a){return arguments.length?(h=a,o):h},o.extent=function(a){return arguments.length?(j=a,o):j},o.title=function(a){return arguments.length?(k=a,o):k},o.format=function(a){return arguments.length?(l=a,o):l},o.colors=function(a){return arguments.length?(m=a,o):m},o},f.comparison=function(){function o(o){o.on("mousemove.comparison",function(){a.focus(Math.round(d3.mouse(this)[0]))}).on("mouseout.comparison",function(){a.focus(null)}),o.append("canvas").attr("width",b).attr("height",e),o.append("span").attr("class","title").text(j),o.append("span").attr("class","value primary"),o.append("span").attr("class","value change"),o.each(function(j,o){function B(c,d){x.save(),x.clearRect(0,0,b,e);var g=r.extent(),h=s.extent(),i=null==t?g:t;f.domain(i).range([e,0]),A=g.concat(h).every(isFinite);var j=c/a.step()&1?v:u;x.fillStyle=m[2];for(var k=0,l=b;kp&&x.fillRect(j(k),p,1,o-p)}for(x.fillStyle=m[3],k=0;kp&&x.fillRect(j(k),o-n,1,n)}x.restore()}function C(a){null==a&&(a=b-1);var c=r.valueAt(a),d=s.valueAt(a),e=(c-d)/d;y.datum(c).text(isNaN(c)?null:k),z.datum(e).text(isNaN(e)?null:l).attr("class","value change "+(e>0?"positive":e<0?"negative":""))}function D(a,b){B(a,b),C(),A&&(r.on("change.comparison-"+q,d),s.on("change.comparison-"+q,d))}var A,p=this,q=++c,r="function"==typeof g?g.call(p,j,o):g,s="function"==typeof h?h.call(p,j,o):h,t="function"==typeof i?i.call(p,j,o):i,w=d3.select(p),x=w.select("canvas"),y=w.select(".value.primary"),z=w.select(".value.change");x.datum({id:q,primary:r,secondary:s}),x=x.node().getContext("2d"),r.on("change.comparison-"+q,D),s.on("change.comparison-"+q,D),a.on("change.comparison-"+q,B),a.on("focus.comparison-"+q,C)})}var a=this,b=a.size(),e=120,f=d3.scale.linear().interpolate(d3.interpolateRound),g=function(a){return a[0]},h=function(a){return a[1]},i=null,j=d,k=s,l=t,m=["#9ecae1","#225b84","#a1d99b","#22723a"],n=1.5;return o.remove=function(b){function c(b){b.primary.on("change.comparison-"+b.id,null),b.secondary.on("change.comparison-"+b.id,null),a.on("change.comparison-"+b.id,null),a.on("focus.comparison-"+b.id,null)}b.on("mousemove.comparison",null).on("mouseout.comparison",null),b.selectAll("canvas").each(c).remove(),b.selectAll(".title,.value").remove()},o.height=function(a){return arguments.length?(e=+a,o):e},o.primary=function(a){return arguments.length?(g=a,o):g},o.secondary=function(a){return arguments.length?(h=a,o):h},o.scale=function(a){return arguments.length?(f=a,o):f},o.extent=function(a){return arguments.length?(i=a,o):i},o.title=function(a){return arguments.length?(j=a,o):j},o.formatPrimary=function(a){return arguments.length?(k=a,o):k},o.formatChange=function(a){return arguments.length?(l=a,o):l},o.colors=function(a){return arguments.length?(m=a,o):m},o.strokeWidth=function(a){return arguments.length?(n=a,o):n},o};var s=d3.format(".2s"),t=d3.format("+.0%");f.axis=function(){function g(e){var i,h=++c,j=e.append("svg").datum({id:h}).attr("width",a.size()).attr("height",Math.max(28,-g.tickSize())).append("g").attr("transform","translate(0,"+("top"===d.orient()?27:4)+")").call(d);a.on("change.axis-"+h,function(){j.call(d),i||(i=d3.select(j.node().appendChild(j.selectAll("text").node().cloneNode(!0))).style("display","none").text(null))}),a.on("focus.axis-"+h,function(a){if(i)if(null==a)i.style("display","none"),j.selectAll("text").style("fill-opacity",null);else{i.style("display",null).attr("x",a).text(f(b.invert(a)));var c=i.node().getComputedTextLength()+6;j.selectAll("text").style("fill-opacity",function(d){return Math.abs(b(d)-a)