cubism-rails 1.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source :rubygems
2
+
3
+ # Specify your gem's dependencies in gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Vlad Gorodetsky
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,25 @@
1
+ # cubism-rails
2
+
3
+ Cubism.js is a [D3](http://mbostock.github.com/d3/) plugin for visualizing time series. Use Cubism to construct better realtime dashboards, pulling data from [Graphite](/square/cubism/wiki/Graphite), [Cube](/square/cubism/wiki/Cube) and other sources. Cubism is available under the [Apache License](/square/cubism/blob/master/LICENSE).
4
+
5
+ ## Installation
6
+
7
+ Add the following to your gemfile:
8
+
9
+ gem 'cubism-rails'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Add the following directive to your JavaScript manifest file (application.js):
16
+
17
+ //= require cubism
18
+
19
+ ## Contributing
20
+
21
+ 1. Fork it
22
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
23
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
24
+ 4. Push to the branch (`git push origin my-new-feature`)
25
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'cubism-rails/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "cubism-rails"
8
+ gem.version = Cubism::Rails::VERSION
9
+ gem.authors = ["Vlad Gorodetsky"]
10
+ gem.email = ["v@gor.io"]
11
+ gem.description = %q{Cubism.js: A JavaScript library for time series visualization.}
12
+ gem.summary = %q{Gemified cubism.js asset for Rails}
13
+ gem.homepage = "http://github.com/bai/cubism-rails"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.add_dependency "railties", ">= 3.0", "< 5.0"
21
+ end
@@ -0,0 +1,5 @@
1
+ module Cubism
2
+ module Rails
3
+ VERSION = "1.2.2"
4
+ end
5
+ end
@@ -0,0 +1,8 @@
1
+ require "cubism-rails/version"
2
+
3
+ module Cubism
4
+ module Rails
5
+ class Engine < ::Rails::Engine
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,1045 @@
1
+ (function(exports){
2
+ var cubism = exports.cubism = {version: "1.2.2"};
3
+ var cubism_id = 0;
4
+ function cubism_identity(d) { return d; }
5
+ cubism.option = function(name, defaultValue) {
6
+ var values = cubism.options(name);
7
+ return values.length ? values[0] : defaultValue;
8
+ };
9
+
10
+ cubism.options = function(name, defaultValues) {
11
+ var options = location.search.substring(1).split("&"),
12
+ values = [],
13
+ i = -1,
14
+ n = options.length,
15
+ o;
16
+ while (++i < n) {
17
+ if ((o = options[i].split("="))[0] == name) {
18
+ values.push(decodeURIComponent(o[1]));
19
+ }
20
+ }
21
+ return values.length || arguments.length < 2 ? values : defaultValues;
22
+ };
23
+ cubism.context = function() {
24
+ var context = new cubism_context,
25
+ step = 1e4, // ten seconds, in milliseconds
26
+ size = 1440, // four hours at ten seconds, in pixels
27
+ start0, stop0, // the start and stop for the previous change event
28
+ start1, stop1, // the start and stop for the next prepare event
29
+ serverDelay = 5e3,
30
+ clientDelay = 5e3,
31
+ event = d3.dispatch("prepare", "beforechange", "change", "focus"),
32
+ scale = context.scale = d3.time.scale().range([0, size]),
33
+ timeout,
34
+ focus;
35
+
36
+ function update() {
37
+ var now = Date.now();
38
+ stop0 = new Date(Math.floor((now - serverDelay - clientDelay) / step) * step);
39
+ start0 = new Date(stop0 - size * step);
40
+ stop1 = new Date(Math.floor((now - serverDelay) / step) * step);
41
+ start1 = new Date(stop1 - size * step);
42
+ scale.domain([start0, stop0]);
43
+ return context;
44
+ }
45
+
46
+ context.start = function() {
47
+ if (timeout) clearTimeout(timeout);
48
+ var delay = +stop1 + serverDelay - Date.now();
49
+
50
+ // If we're too late for the first prepare event, skip it.
51
+ if (delay < clientDelay) delay += step;
52
+
53
+ timeout = setTimeout(function prepare() {
54
+ stop1 = new Date(Math.floor((Date.now() - serverDelay) / step) * step);
55
+ start1 = new Date(stop1 - size * step);
56
+ event.prepare.call(context, start1, stop1);
57
+
58
+ setTimeout(function() {
59
+ scale.domain([start0 = start1, stop0 = stop1]);
60
+ event.beforechange.call(context, start1, stop1);
61
+ event.change.call(context, start1, stop1);
62
+ event.focus.call(context, focus);
63
+ }, clientDelay);
64
+
65
+ timeout = setTimeout(prepare, step);
66
+ }, delay);
67
+ return context;
68
+ };
69
+
70
+ context.stop = function() {
71
+ timeout = clearTimeout(timeout);
72
+ return context;
73
+ };
74
+
75
+ timeout = setTimeout(context.start, 10);
76
+
77
+ // Set or get the step interval in milliseconds.
78
+ // Defaults to ten seconds.
79
+ context.step = function(_) {
80
+ if (!arguments.length) return step;
81
+ step = +_;
82
+ return update();
83
+ };
84
+
85
+ // Set or get the context size (the count of metric values).
86
+ // Defaults to 1440 (four hours at ten seconds).
87
+ context.size = function(_) {
88
+ if (!arguments.length) return size;
89
+ scale.range([0, size = +_]);
90
+ return update();
91
+ };
92
+
93
+ // The server delay is the amount of time we wait for the server to compute a
94
+ // metric. This delay may result from clock skew or from delays collecting
95
+ // metrics from various hosts. Defaults to 4 seconds.
96
+ context.serverDelay = function(_) {
97
+ if (!arguments.length) return serverDelay;
98
+ serverDelay = +_;
99
+ return update();
100
+ };
101
+
102
+ // The client delay is the amount of additional time we wait to fetch those
103
+ // metrics from the server. The client and server delay combined represent the
104
+ // age of the most recent displayed metric. Defaults to 1 second.
105
+ context.clientDelay = function(_) {
106
+ if (!arguments.length) return clientDelay;
107
+ clientDelay = +_;
108
+ return update();
109
+ };
110
+
111
+ // Sets the focus to the specified index, and dispatches a "focus" event.
112
+ context.focus = function(i) {
113
+ event.focus.call(context, focus = i);
114
+ return context;
115
+ };
116
+
117
+ // Add, remove or get listeners for events.
118
+ context.on = function(type, listener) {
119
+ if (arguments.length < 2) return event.on(type);
120
+
121
+ event.on(type, listener);
122
+
123
+ // Notify the listener of the current start and stop time, as appropriate.
124
+ // This way, metrics can make requests for data immediately,
125
+ // and likewise the axis can display itself synchronously.
126
+ if (listener != null) {
127
+ if (/^prepare(\.|$)/.test(type)) listener.call(context, start1, stop1);
128
+ if (/^beforechange(\.|$)/.test(type)) listener.call(context, start0, stop0);
129
+ if (/^change(\.|$)/.test(type)) listener.call(context, start0, stop0);
130
+ if (/^focus(\.|$)/.test(type)) listener.call(context, focus);
131
+ }
132
+
133
+ return context;
134
+ };
135
+
136
+ d3.select(window).on("keydown.context-" + ++cubism_id, function() {
137
+ switch (!d3.event.metaKey && d3.event.keyCode) {
138
+ case 37: // left
139
+ if (focus == null) focus = size - 1;
140
+ if (focus > 0) context.focus(--focus);
141
+ break;
142
+ case 39: // right
143
+ if (focus == null) focus = size - 2;
144
+ if (focus < size - 1) context.focus(++focus);
145
+ break;
146
+ default: return;
147
+ }
148
+ d3.event.preventDefault();
149
+ });
150
+
151
+ return update();
152
+ };
153
+
154
+ function cubism_context() {}
155
+
156
+ var cubism_contextPrototype = cubism.context.prototype = cubism_context.prototype;
157
+
158
+ cubism_contextPrototype.constant = function(value) {
159
+ return new cubism_metricConstant(this, +value);
160
+ };
161
+ cubism_contextPrototype.cube = function(host) {
162
+ if (!arguments.length) host = "";
163
+ var source = {},
164
+ context = this;
165
+
166
+ source.metric = function(expression) {
167
+ return context.metric(function(start, stop, step, callback) {
168
+ d3.json(host + "/1.0/metric"
169
+ + "?expression=" + encodeURIComponent(expression)
170
+ + "&start=" + cubism_cubeFormatDate(start)
171
+ + "&stop=" + cubism_cubeFormatDate(stop)
172
+ + "&step=" + step, function(data) {
173
+ if (!data) return callback(new Error("unable to load data"));
174
+ callback(null, data.map(function(d) { return d.value; }));
175
+ });
176
+ }, expression += "");
177
+ };
178
+
179
+ // Returns the Cube host.
180
+ source.toString = function() {
181
+ return host;
182
+ };
183
+
184
+ return source;
185
+ };
186
+
187
+ var cubism_cubeFormatDate = d3.time.format.iso;
188
+ cubism_contextPrototype.graphite = function(host) {
189
+ if (!arguments.length) host = "";
190
+ var source = {},
191
+ context = this;
192
+
193
+ source.metric = function(expression) {
194
+ var sum = "sum";
195
+
196
+ var metric = context.metric(function(start, stop, step, callback) {
197
+ var target = expression;
198
+
199
+ // Apply the summarize, if necessary.
200
+ if (step !== 1e4) target = "summarize(" + target + ",'"
201
+ + (!(step % 36e5) ? step / 36e5 + "hour" : !(step % 6e4) ? step / 6e4 + "min" : step / 1e3 + "sec")
202
+ + "','" + sum + "')";
203
+
204
+ d3.text(host + "/render?format=raw"
205
+ + "&target=" + encodeURIComponent("alias(" + target + ",'')")
206
+ + "&from=" + cubism_graphiteFormatDate(start - 2 * step) // off-by-two?
207
+ + "&until=" + cubism_graphiteFormatDate(stop - 1000), function(text) {
208
+ if (!text) return callback(new Error("unable to load data"));
209
+ callback(null, cubism_graphiteParse(text));
210
+ });
211
+ }, expression += "");
212
+
213
+ metric.summarize = function(_) {
214
+ sum = _;
215
+ return metric;
216
+ };
217
+
218
+ return metric;
219
+ };
220
+
221
+ source.find = function(pattern, callback) {
222
+ d3.json(host + "/metrics/find?format=completer"
223
+ + "&query=" + encodeURIComponent(pattern), function(result) {
224
+ if (!result) return callback(new Error("unable to find metrics"));
225
+ callback(null, result.metrics.map(function(d) { return d.path; }));
226
+ });
227
+ };
228
+
229
+ // Returns the graphite host.
230
+ source.toString = function() {
231
+ return host;
232
+ };
233
+
234
+ return source;
235
+ };
236
+
237
+ // Graphite understands seconds since UNIX epoch.
238
+ function cubism_graphiteFormatDate(time) {
239
+ return Math.floor(time / 1000);
240
+ }
241
+
242
+ // Helper method for parsing graphite's raw format.
243
+ function cubism_graphiteParse(text) {
244
+ var i = text.indexOf("|"),
245
+ meta = text.substring(0, i),
246
+ c = meta.lastIndexOf(","),
247
+ b = meta.lastIndexOf(",", c - 1),
248
+ a = meta.lastIndexOf(",", b - 1),
249
+ start = meta.substring(a + 1, b) * 1000,
250
+ step = meta.substring(c + 1) * 1000;
251
+ return text
252
+ .substring(i + 1)
253
+ .split(",")
254
+ .slice(1) // the first value is always None?
255
+ .map(function(d) { return +d; });
256
+ }
257
+ function cubism_metric(context) {
258
+ if (!(context instanceof cubism_context)) throw new Error("invalid context");
259
+ this.context = context;
260
+ }
261
+
262
+ var cubism_metricPrototype = cubism_metric.prototype;
263
+
264
+ cubism.metric = cubism_metric;
265
+
266
+ cubism_metricPrototype.valueAt = function() {
267
+ return NaN;
268
+ };
269
+
270
+ cubism_metricPrototype.alias = function(name) {
271
+ this.toString = function() { return name; };
272
+ return this;
273
+ };
274
+
275
+ cubism_metricPrototype.extent = function() {
276
+ var i = 0,
277
+ n = this.context.size(),
278
+ value,
279
+ min = Infinity,
280
+ max = -Infinity;
281
+ while (++i < n) {
282
+ value = this.valueAt(i);
283
+ if (value < min) min = value;
284
+ if (value > max) max = value;
285
+ }
286
+ return [min, max];
287
+ };
288
+
289
+ cubism_metricPrototype.on = function(type, listener) {
290
+ return arguments.length < 2 ? null : this;
291
+ };
292
+
293
+ cubism_metricPrototype.shift = function() {
294
+ return this;
295
+ };
296
+
297
+ cubism_metricPrototype.on = function() {
298
+ return arguments.length < 2 ? null : this;
299
+ };
300
+
301
+ cubism_contextPrototype.metric = function(request, name) {
302
+ var context = this,
303
+ metric = new cubism_metric(context),
304
+ id = ".metric-" + ++cubism_id,
305
+ start = -Infinity,
306
+ stop,
307
+ step = context.step(),
308
+ size = context.size(),
309
+ values = [],
310
+ event = d3.dispatch("change"),
311
+ listening = 0,
312
+ fetching;
313
+
314
+ // Prefetch new data into a temporary array.
315
+ function prepare(start1, stop) {
316
+ var steps = Math.min(size, Math.round((start1 - start) / step));
317
+ if (!steps || fetching) return; // already fetched, or fetching!
318
+ fetching = true;
319
+ steps = Math.min(size, steps + cubism_metricOverlap);
320
+ var start0 = new Date(stop - steps * step);
321
+ request(start0, stop, step, function(error, data) {
322
+ fetching = false;
323
+ if (error) return console.warn(error);
324
+ var i = isFinite(start) ? Math.round((start0 - start) / step) : 0;
325
+ for (var j = 0, m = data.length; j < m; ++j) values[j + i] = data[j];
326
+ event.change.call(metric, start, stop);
327
+ });
328
+ }
329
+
330
+ // When the context changes, switch to the new data, ready-or-not!
331
+ function beforechange(start1, stop1) {
332
+ if (!isFinite(start)) start = start1;
333
+ values.splice(0, Math.max(0, Math.min(size, Math.round((start1 - start) / step))));
334
+ start = start1;
335
+ stop = stop1;
336
+ }
337
+
338
+ //
339
+ metric.valueAt = function(i) {
340
+ return values[i];
341
+ };
342
+
343
+ //
344
+ metric.shift = function(offset) {
345
+ return context.metric(cubism_metricShift(request, +offset));
346
+ };
347
+
348
+ //
349
+ metric.on = function(type, listener) {
350
+ if (!arguments.length) return event.on(type);
351
+
352
+ // If there are no listeners, then stop listening to the context,
353
+ // and avoid unnecessary fetches.
354
+ if (listener == null) {
355
+ if (event.on(type) != null && --listening == 0) {
356
+ context.on("prepare" + id, null).on("beforechange" + id, null);
357
+ }
358
+ } else {
359
+ if (event.on(type) == null && ++listening == 1) {
360
+ context.on("prepare" + id, prepare).on("beforechange" + id, beforechange);
361
+ }
362
+ }
363
+
364
+ event.on(type, listener);
365
+
366
+ // Notify the listener of the current start and stop time, as appropriate.
367
+ // This way, charts can display synchronous metrics immediately.
368
+ if (listener != null) {
369
+ if (/^change(\.|$)/.test(type)) listener.call(context, start, stop);
370
+ }
371
+
372
+ return metric;
373
+ };
374
+
375
+ //
376
+ if (arguments.length > 1) metric.toString = function() {
377
+ return name;
378
+ };
379
+
380
+ return metric;
381
+ };
382
+
383
+ // Number of metric to refetch each period, in case of lag.
384
+ var cubism_metricOverlap = 6;
385
+
386
+ // Wraps the specified request implementation, and shifts time by the given offset.
387
+ function cubism_metricShift(request, offset) {
388
+ return function(start, stop, step, callback) {
389
+ request(new Date(+start + offset), new Date(+stop + offset), step, callback);
390
+ };
391
+ }
392
+ function cubism_metricConstant(context, value) {
393
+ cubism_metric.call(this, context);
394
+ value = +value;
395
+ var name = value + "";
396
+ this.valueOf = function() { return value; };
397
+ this.toString = function() { return name; };
398
+ }
399
+
400
+ var cubism_metricConstantPrototype = cubism_metricConstant.prototype = Object.create(cubism_metric.prototype);
401
+
402
+ cubism_metricConstantPrototype.valueAt = function() {
403
+ return +this;
404
+ };
405
+
406
+ cubism_metricConstantPrototype.extent = function() {
407
+ return [+this, +this];
408
+ };
409
+ function cubism_metricOperator(name, operate) {
410
+
411
+ function cubism_metricOperator(left, right) {
412
+ if (!(right instanceof cubism_metric)) right = new cubism_metricConstant(left.context, right);
413
+ else if (left.context !== right.context) throw new Error("mismatch context");
414
+ cubism_metric.call(this, left.context);
415
+ this.left = left;
416
+ this.right = right;
417
+ this.toString = function() { return left + " " + name + " " + right; };
418
+ }
419
+
420
+ var cubism_metricOperatorPrototype = cubism_metricOperator.prototype = Object.create(cubism_metric.prototype);
421
+
422
+ cubism_metricOperatorPrototype.valueAt = function(i) {
423
+ return operate(this.left.valueAt(i), this.right.valueAt(i));
424
+ };
425
+
426
+ cubism_metricOperatorPrototype.shift = function(offset) {
427
+ return new cubism_metricOperator(this.left.shift(offset), this.right.shift(offset));
428
+ };
429
+
430
+ cubism_metricOperatorPrototype.on = function(type, listener) {
431
+ if (arguments.length < 2) return this.left.on(type);
432
+ this.left.on(type, listener);
433
+ this.right.on(type, listener);
434
+ return this;
435
+ };
436
+
437
+ return function(right) {
438
+ return new cubism_metricOperator(this, right);
439
+ };
440
+ }
441
+
442
+ cubism_metricPrototype.add = cubism_metricOperator("+", function(left, right) {
443
+ return left + right;
444
+ });
445
+
446
+ cubism_metricPrototype.subtract = cubism_metricOperator("-", function(left, right) {
447
+ return left - right;
448
+ });
449
+
450
+ cubism_metricPrototype.multiply = cubism_metricOperator("*", function(left, right) {
451
+ return left * right;
452
+ });
453
+
454
+ cubism_metricPrototype.divide = cubism_metricOperator("/", function(left, right) {
455
+ return left / right;
456
+ });
457
+ cubism_contextPrototype.horizon = function() {
458
+ var context = this,
459
+ mode = "offset",
460
+ buffer = document.createElement("canvas"),
461
+ width = buffer.width = context.size(),
462
+ height = buffer.height = 30,
463
+ scale = d3.scale.linear().interpolate(d3.interpolateRound),
464
+ metric = cubism_identity,
465
+ extent = null,
466
+ title = cubism_identity,
467
+ format = d3.format(".2s"),
468
+ colors = ["#08519c","#3182bd","#6baed6","#bdd7e7","#bae4b3","#74c476","#31a354","#006d2c"];
469
+
470
+ function horizon(selection) {
471
+
472
+ selection
473
+ .on("mousemove.horizon", function() { context.focus(Math.round(d3.mouse(this)[0])); })
474
+ .on("mouseout.horizon", function() { context.focus(null); });
475
+
476
+ selection.append("canvas")
477
+ .attr("width", width)
478
+ .attr("height", height);
479
+
480
+ selection.append("span")
481
+ .attr("class", "title")
482
+ .text(title);
483
+
484
+ selection.append("span")
485
+ .attr("class", "value");
486
+
487
+ selection.each(function(d, i) {
488
+ var that = this,
489
+ id = ++cubism_id,
490
+ metric_ = typeof metric === "function" ? metric.call(that, d, i) : metric,
491
+ colors_ = typeof colors === "function" ? colors.call(that, d, i) : colors,
492
+ extent_ = typeof extent === "function" ? extent.call(that, d, i) : extent,
493
+ start = -Infinity,
494
+ step = context.step(),
495
+ canvas = d3.select(that).select("canvas"),
496
+ span = d3.select(that).select(".value"),
497
+ max_,
498
+ m = colors_.length >> 1,
499
+ ready;
500
+
501
+ canvas.datum({id: id, metric: metric_});
502
+ canvas = canvas.node().getContext("2d");
503
+
504
+ function change(start1, stop) {
505
+ canvas.save();
506
+
507
+ // compute the new extent and ready flag
508
+ var extent = metric_.extent();
509
+ ready = extent.every(isFinite);
510
+ if (extent_ != null) extent = extent_;
511
+
512
+ // if this is an update (with no extent change), copy old values!
513
+ var i0 = 0, max = Math.max(-extent[0], extent[1]);
514
+ if (this === context) {
515
+ if (max == max_) {
516
+ i0 = width - cubism_metricOverlap;
517
+ var dx = (start1 - start) / step;
518
+ if (dx < width) {
519
+ var canvas0 = buffer.getContext("2d");
520
+ canvas0.clearRect(0, 0, width, height);
521
+ canvas0.drawImage(canvas.canvas, dx, 0, width - dx, height, 0, 0, width - dx, height);
522
+ canvas.clearRect(0, 0, width, height);
523
+ canvas.drawImage(canvas0.canvas, 0, 0);
524
+ }
525
+ }
526
+ start = start1;
527
+ }
528
+
529
+ // update the domain
530
+ scale.domain([0, max_ = max]);
531
+
532
+ // clear for the new data
533
+ canvas.clearRect(i0, 0, width - i0, height);
534
+
535
+ // record whether there are negative values to display
536
+ var negative;
537
+
538
+ // positive bands
539
+ for (var j = 0; j < m; ++j) {
540
+ canvas.fillStyle = colors_[m + j];
541
+
542
+ // Adjust the range based on the current band index.
543
+ var y0 = (j - m + 1) * height;
544
+ scale.range([m * height + y0, y0]);
545
+ y0 = scale(0);
546
+
547
+ for (var i = i0, n = width, y1; i < n; ++i) {
548
+ y1 = metric_.valueAt(i);
549
+ if (y1 <= 0) { negative = true; continue; }
550
+ canvas.fillRect(i, y1 = scale(y1), 1, y0 - y1);
551
+ }
552
+ }
553
+
554
+ if (negative) {
555
+ // enable offset mode
556
+ if (mode === "offset") {
557
+ canvas.translate(0, height);
558
+ canvas.scale(1, -1);
559
+ }
560
+
561
+ // negative bands
562
+ for (var j = 0; j < m; ++j) {
563
+ canvas.fillStyle = colors_[m - 1 - j];
564
+
565
+ // Adjust the range based on the current band index.
566
+ var y0 = (j - m + 1) * height;
567
+ scale.range([m * height + y0, y0]);
568
+ y0 = scale(0);
569
+
570
+ for (var i = i0, n = width, y1; i < n; ++i) {
571
+ y1 = metric_.valueAt(i);
572
+ if (y1 >= 0) continue;
573
+ canvas.fillRect(i, scale(-y1), 1, y0 - scale(-y1));
574
+ }
575
+ }
576
+ }
577
+
578
+ canvas.restore();
579
+ }
580
+
581
+ function focus(i) {
582
+ if (i == null) i = width - 1;
583
+ var value = metric_.valueAt(i);
584
+ span.datum(value).text(isNaN(value) ? null : format);
585
+ }
586
+
587
+ // Update the chart when the context changes.
588
+ context.on("change.horizon-" + id, change);
589
+ context.on("focus.horizon-" + id, focus);
590
+
591
+ // Display the first metric change immediately,
592
+ // but defer subsequent updates to the canvas change.
593
+ // Note that someone still needs to listen to the metric,
594
+ // so that it continues to update automatically.
595
+ metric_.on("change.horizon-" + id, function(start, stop) {
596
+ change(start, stop), focus();
597
+ if (ready) metric_.on("change.horizon-" + id, cubism_identity);
598
+ });
599
+ });
600
+ }
601
+
602
+ horizon.remove = function(selection) {
603
+
604
+ selection
605
+ .on("mousemove.horizon", null)
606
+ .on("mouseout.horizon", null);
607
+
608
+ selection.selectAll("canvas")
609
+ .each(remove)
610
+ .remove();
611
+
612
+ selection.selectAll(".title,.value")
613
+ .remove();
614
+
615
+ function remove(d) {
616
+ d.metric.on("change.horizon-" + d.id, null);
617
+ context.on("change.horizon-" + d.id, null);
618
+ context.on("focus.horizon-" + d.id, null);
619
+ }
620
+ };
621
+
622
+ horizon.mode = function(_) {
623
+ if (!arguments.length) return mode;
624
+ mode = _ + "";
625
+ return horizon;
626
+ };
627
+
628
+ horizon.height = function(_) {
629
+ if (!arguments.length) return height;
630
+ buffer.height = height = +_;
631
+ return horizon;
632
+ };
633
+
634
+ horizon.metric = function(_) {
635
+ if (!arguments.length) return metric;
636
+ metric = _;
637
+ return horizon;
638
+ };
639
+
640
+ horizon.scale = function(_) {
641
+ if (!arguments.length) return scale;
642
+ scale = _;
643
+ return horizon;
644
+ };
645
+
646
+ horizon.extent = function(_) {
647
+ if (!arguments.length) return extent;
648
+ extent = _;
649
+ return horizon;
650
+ };
651
+
652
+ horizon.title = function(_) {
653
+ if (!arguments.length) return title;
654
+ title = _;
655
+ return horizon;
656
+ };
657
+
658
+ horizon.format = function(_) {
659
+ if (!arguments.length) return format;
660
+ format = _;
661
+ return horizon;
662
+ };
663
+
664
+ horizon.colors = function(_) {
665
+ if (!arguments.length) return colors;
666
+ colors = _;
667
+ return horizon;
668
+ };
669
+
670
+ return horizon;
671
+ };
672
+ cubism_contextPrototype.comparison = function() {
673
+ var context = this,
674
+ width = context.size(),
675
+ height = 120,
676
+ scale = d3.scale.linear().interpolate(d3.interpolateRound),
677
+ primary = function(d) { return d[0]; },
678
+ secondary = function(d) { return d[1]; },
679
+ extent = null,
680
+ title = cubism_identity,
681
+ formatPrimary = cubism_comparisonPrimaryFormat,
682
+ formatChange = cubism_comparisonChangeFormat,
683
+ colors = ["#9ecae1", "#225b84", "#a1d99b", "#22723a"],
684
+ strokeWidth = 1.5;
685
+
686
+ function comparison(selection) {
687
+
688
+ selection
689
+ .on("mousemove.comparison", function() { context.focus(Math.round(d3.mouse(this)[0])); })
690
+ .on("mouseout.comparison", function() { context.focus(null); });
691
+
692
+ selection.append("canvas")
693
+ .attr("width", width)
694
+ .attr("height", height);
695
+
696
+ selection.append("span")
697
+ .attr("class", "title")
698
+ .text(title);
699
+
700
+ selection.append("span")
701
+ .attr("class", "value primary");
702
+
703
+ selection.append("span")
704
+ .attr("class", "value change");
705
+
706
+ selection.each(function(d, i) {
707
+ var that = this,
708
+ id = ++cubism_id,
709
+ primary_ = typeof primary === "function" ? primary.call(that, d, i) : primary,
710
+ secondary_ = typeof secondary === "function" ? secondary.call(that, d, i) : secondary,
711
+ extent_ = typeof extent === "function" ? extent.call(that, d, i) : extent,
712
+ div = d3.select(that),
713
+ canvas = div.select("canvas"),
714
+ spanPrimary = div.select(".value.primary"),
715
+ spanChange = div.select(".value.change"),
716
+ ready;
717
+
718
+ canvas.datum({id: id, primary: primary_, secondary: secondary_});
719
+ canvas = canvas.node().getContext("2d");
720
+
721
+ function change(start, stop) {
722
+ canvas.save();
723
+ canvas.clearRect(0, 0, width, height);
724
+
725
+ // update the scale
726
+ var primaryExtent = primary_.extent(),
727
+ secondaryExtent = secondary_.extent(),
728
+ extent = extent_ == null ? primaryExtent : extent_;
729
+ scale.domain(extent).range([height, 0]);
730
+ ready = primaryExtent.concat(secondaryExtent).every(isFinite);
731
+
732
+ // consistent overplotting
733
+ var round = start / context.step() & 1
734
+ ? cubism_comparisonRoundOdd
735
+ : cubism_comparisonRoundEven;
736
+
737
+ // positive changes
738
+ canvas.fillStyle = colors[2];
739
+ for (var i = 0, n = width; i < n; ++i) {
740
+ var y0 = scale(primary_.valueAt(i)),
741
+ y1 = scale(secondary_.valueAt(i));
742
+ if (y0 < y1) canvas.fillRect(round(i), y0, 1, y1 - y0);
743
+ }
744
+
745
+ // negative changes
746
+ canvas.fillStyle = colors[0];
747
+ for (i = 0; i < n; ++i) {
748
+ var y0 = scale(primary_.valueAt(i)),
749
+ y1 = scale(secondary_.valueAt(i));
750
+ if (y0 > y1) canvas.fillRect(round(i), y1, 1, y0 - y1);
751
+ }
752
+
753
+ // positive values
754
+ canvas.fillStyle = colors[3];
755
+ for (i = 0; i < n; ++i) {
756
+ var y0 = scale(primary_.valueAt(i)),
757
+ y1 = scale(secondary_.valueAt(i));
758
+ if (y0 <= y1) canvas.fillRect(round(i), y0, 1, strokeWidth);
759
+ }
760
+
761
+ // negative values
762
+ canvas.fillStyle = colors[1];
763
+ for (i = 0; i < n; ++i) {
764
+ var y0 = scale(primary_.valueAt(i)),
765
+ y1 = scale(secondary_.valueAt(i));
766
+ if (y0 > y1) canvas.fillRect(round(i), y0 - strokeWidth, 1, strokeWidth);
767
+ }
768
+
769
+ canvas.restore();
770
+ }
771
+
772
+ function focus(i) {
773
+ if (i == null) i = width - 1;
774
+ var valuePrimary = primary_.valueAt(i),
775
+ valueSecondary = secondary_.valueAt(i),
776
+ valueChange = (valuePrimary - valueSecondary) / valueSecondary;
777
+
778
+ spanPrimary
779
+ .datum(valuePrimary)
780
+ .text(isNaN(valuePrimary) ? null : formatPrimary);
781
+
782
+ spanChange
783
+ .datum(valueChange)
784
+ .text(isNaN(valueChange) ? null : formatChange)
785
+ .attr("class", "value change " + (valueChange > 0 ? "positive" : valueChange < 0 ? "negative" : ""));
786
+ }
787
+
788
+ // Display the first primary change immediately,
789
+ // but defer subsequent updates to the context change.
790
+ // Note that someone still needs to listen to the metric,
791
+ // so that it continues to update automatically.
792
+ primary_.on("change.comparison-" + id, firstChange);
793
+ secondary_.on("change.comparison-" + id, firstChange);
794
+ function firstChange(start, stop) {
795
+ change(start, stop), focus();
796
+ if (ready) {
797
+ primary_.on("change.comparison-" + id, cubism_identity);
798
+ secondary_.on("change.comparison-" + id, cubism_identity);
799
+ }
800
+ }
801
+
802
+ // Update the chart when the context changes.
803
+ context.on("change.comparison-" + id, change);
804
+ context.on("focus.comparison-" + id, focus);
805
+ });
806
+ }
807
+
808
+ comparison.remove = function(selection) {
809
+
810
+ selection
811
+ .on("mousemove.comparison", null)
812
+ .on("mouseout.comparison", null);
813
+
814
+ selection.selectAll("canvas")
815
+ .each(remove)
816
+ .remove();
817
+
818
+ selection.selectAll(".title,.value")
819
+ .remove();
820
+
821
+ function remove(d) {
822
+ d.primary.on("change.comparison-" + d.id, null);
823
+ d.secondary.on("change.comparison-" + d.id, null);
824
+ context.on("change.comparison-" + d.id, null);
825
+ context.on("focus.comparison-" + d.id, null);
826
+ }
827
+ };
828
+
829
+ comparison.height = function(_) {
830
+ if (!arguments.length) return height;
831
+ height = +_;
832
+ return comparison;
833
+ };
834
+
835
+ comparison.primary = function(_) {
836
+ if (!arguments.length) return primary;
837
+ primary = _;
838
+ return comparison;
839
+ };
840
+
841
+ comparison.secondary = function(_) {
842
+ if (!arguments.length) return secondary;
843
+ secondary = _;
844
+ return comparison;
845
+ };
846
+
847
+ comparison.scale = function(_) {
848
+ if (!arguments.length) return scale;
849
+ scale = _;
850
+ return comparison;
851
+ };
852
+
853
+ comparison.extent = function(_) {
854
+ if (!arguments.length) return extent;
855
+ extent = _;
856
+ return comparison;
857
+ };
858
+
859
+ comparison.title = function(_) {
860
+ if (!arguments.length) return title;
861
+ title = _;
862
+ return comparison;
863
+ };
864
+
865
+ comparison.formatPrimary = function(_) {
866
+ if (!arguments.length) return formatPrimary;
867
+ formatPrimary = _;
868
+ return comparison;
869
+ };
870
+
871
+ comparison.formatChange = function(_) {
872
+ if (!arguments.length) return formatChange;
873
+ formatChange = _;
874
+ return comparison;
875
+ };
876
+
877
+ comparison.colors = function(_) {
878
+ if (!arguments.length) return colors;
879
+ colors = _;
880
+ return comparison;
881
+ };
882
+
883
+ comparison.strokeWidth = function(_) {
884
+ if (!arguments.length) return strokeWidth;
885
+ strokeWidth = _;
886
+ return comparison;
887
+ };
888
+
889
+ return comparison;
890
+ };
891
+
892
+ var cubism_comparisonPrimaryFormat = d3.format(".2s"),
893
+ cubism_comparisonChangeFormat = d3.format("+.0%");
894
+
895
+ function cubism_comparisonRoundEven(i) {
896
+ return i & 0xfffffe;
897
+ }
898
+
899
+ function cubism_comparisonRoundOdd(i) {
900
+ return ((i + 1) & 0xfffffe) - 1;
901
+ }
902
+ cubism_contextPrototype.axis = function() {
903
+ var context = this,
904
+ scale = context.scale,
905
+ axis_ = d3.svg.axis().scale(scale);
906
+
907
+ var format = context.step() < 6e4 ? cubism_axisFormatSeconds
908
+ : context.step() < 864e5 ? cubism_axisFormatMinutes
909
+ : cubism_axisFormatDays;
910
+
911
+ function axis(selection) {
912
+ var id = ++cubism_id,
913
+ tick;
914
+
915
+ var g = selection.append("svg")
916
+ .datum({id: id})
917
+ .attr("width", context.size())
918
+ .attr("height", Math.max(28, -axis.tickSize()))
919
+ .append("g")
920
+ .attr("transform", "translate(0," + (axis_.orient() === "top" ? 27 : 4) + ")")
921
+ .call(axis_);
922
+
923
+ context.on("change.axis-" + id, function() {
924
+ g.call(axis_);
925
+ if (!tick) tick = d3.select(g.node().appendChild(g.selectAll("text").node().cloneNode(true)))
926
+ .style("display", "none")
927
+ .text(null);
928
+ });
929
+
930
+ context.on("focus.axis-" + id, function(i) {
931
+ if (tick) {
932
+ if (i == null) {
933
+ tick.style("display", "none");
934
+ g.selectAll("text").style("fill-opacity", null);
935
+ } else {
936
+ tick.style("display", null).attr("x", i).text(format(scale.invert(i)));
937
+ var dx = tick.node().getComputedTextLength() + 6;
938
+ g.selectAll("text").style("fill-opacity", function(d) { return Math.abs(scale(d) - i) < dx ? 0 : 1; });
939
+ }
940
+ }
941
+ });
942
+ }
943
+
944
+ axis.remove = function(selection) {
945
+
946
+ selection.selectAll("svg")
947
+ .each(remove)
948
+ .remove();
949
+
950
+ function remove(d) {
951
+ context.on("change.axis-" + d.id, null);
952
+ context.on("focus.axis-" + d.id, null);
953
+ }
954
+ };
955
+
956
+ return d3.rebind(axis, axis_,
957
+ "orient",
958
+ "ticks",
959
+ "tickSubdivide",
960
+ "tickSize",
961
+ "tickPadding",
962
+ "tickFormat");
963
+ };
964
+
965
+ var cubism_axisFormatSeconds = d3.time.format("%I:%M:%S %p"),
966
+ cubism_axisFormatMinutes = d3.time.format("%I:%M %p"),
967
+ cubism_axisFormatDays = d3.time.format("%B %d");
968
+ cubism_contextPrototype.rule = function() {
969
+ var context = this,
970
+ metric = cubism_identity;
971
+
972
+ function rule(selection) {
973
+ var id = ++cubism_id;
974
+
975
+ var line = selection.append("div")
976
+ .datum({id: id})
977
+ .attr("class", "line")
978
+ .call(cubism_ruleStyle);
979
+
980
+ selection.each(function(d, i) {
981
+ var that = this,
982
+ id = ++cubism_id,
983
+ metric_ = typeof metric === "function" ? metric.call(that, d, i) : metric;
984
+
985
+ if (!metric_) return;
986
+
987
+ function change(start, stop) {
988
+ var values = [];
989
+
990
+ for (var i = 0, n = context.size(); i < n; ++i) {
991
+ if (metric_.valueAt(i)) {
992
+ values.push(i);
993
+ }
994
+ }
995
+
996
+ var lines = selection.selectAll(".metric").data(values);
997
+ lines.exit().remove();
998
+ lines.enter().append("div").attr("class", "metric line").call(cubism_ruleStyle);
999
+ lines.style("left", cubism_ruleLeft);
1000
+ }
1001
+
1002
+ context.on("change.rule-" + id, change);
1003
+ metric_.on("change.rule-" + id, change);
1004
+ });
1005
+
1006
+ context.on("focus.rule-" + id, function(i) {
1007
+ line.datum(i)
1008
+ .style("display", i == null ? "none" : null)
1009
+ .style("left", i == null ? null : cubism_ruleLeft);
1010
+ });
1011
+ }
1012
+
1013
+ rule.remove = function(selection) {
1014
+
1015
+ selection.selectAll(".line")
1016
+ .each(remove)
1017
+ .remove();
1018
+
1019
+ function remove(d) {
1020
+ context.on("focus.rule-" + d.id, null);
1021
+ }
1022
+ };
1023
+
1024
+ rule.metric = function(_) {
1025
+ if (!arguments.length) return metric;
1026
+ metric = _;
1027
+ return rule;
1028
+ };
1029
+
1030
+ return rule;
1031
+ };
1032
+
1033
+ function cubism_ruleStyle(line) {
1034
+ line
1035
+ .style("position", "absolute")
1036
+ .style("top", 0)
1037
+ .style("bottom", 0)
1038
+ .style("width", "1px")
1039
+ .style("pointer-events", "none");
1040
+ }
1041
+
1042
+ function cubism_ruleLeft(i) {
1043
+ return i + "px";
1044
+ }
1045
+ })(this);
@@ -0,0 +1 @@
1
+ (function(a){function d(a){return a}function e(){}function h(a){return Math.floor(a/1e3)}function i(a){var b=a.indexOf("|"),c=a.substring(0,b),d=c.lastIndexOf(","),e=c.lastIndexOf(",",d-1),f=c.lastIndexOf(",",e-1),g=c.substring(f+1,e)*1e3,h=c.substring(d+1)*1e3;return a.substring(b+1).split(",").slice(1).map(function(a){return+a})}function j(a){if(!(a instanceof e))throw new Error("invalid context");this.context=a}function m(a,b){return function(c,d,e,f){a(new Date(+c+b),new Date(+d+b),e,f)}}function n(a,b){j.call(this,a),b=+b;var c=b+"";this.valueOf=function(){return b},this.toString=function(){return c}}function p(a,b){function c(b,c){if(c instanceof j){if(b.context!==c.context)throw new Error("mismatch context")}else c=new n(b.context,c);j.call(this,b.context),this.left=b,this.right=c,this.toString=function(){return b+" "+a+" "+c}}var d=c.prototype=Object.create(j.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 s(a){return a&16777214}function t(a){return(a+1&16777214)-1}function x(a){a.style("position","absolute").style("top",0).style("bottom",0).style("width","1px").style("pointer-events","none")}function y(a){return a+"px"}var b=a.cubism={version:"1.2.2"},c=0;b.option=function(a,c){var d=b.options(a);return d.length?d[0]:c},b.options=function(a,b){var c=location.search.substring(1).split("&"),d=[],e=-1,f=c.length,g;while(++e<f)(g=c[e].split("="))[0]==a&&d.push(decodeURIComponent(g[1]));return d.length||arguments.length<2?d:b},b.context=function(){function p(){var c=Date.now();return g=new Date(Math.floor((c-j-k)/b)*b),f=new Date(g-d*b),i=new Date(Math.floor((c-j)/b)*b),h=new Date(i-d*b),m.domain([f,g]),a}var a=new e,b=1e4,d=1440,f,g,h,i,j=5e3,k=5e3,l=d3.dispatch("prepare","beforechange","change","focus"),m=a.scale=d3.time.scale().range([0,d]),n,o;return a.start=function(){n&&clearTimeout(n);var c=+i+j-Date.now();return c<k&&(c+=b),n=setTimeout(function e(){i=new Date(Math.floor((Date.now()-j)/b)*b),h=new Date(i-d*b),l.prepare.call(a,h,i),setTimeout(function(){m.domain([f=h,g=i]),l.beforechange.call(a,h,i),l.change.call(a,h,i),l.focus.call(a,o)},k),n=setTimeout(e,b)},c),a},a.stop=function(){return n=clearTimeout(n),a},n=setTimeout(a.start,10),a.step=function(a){return arguments.length?(b=+a,p()):b},a.size=function(a){return arguments.length?(m.range([0,d=+a]),p()):d},a.serverDelay=function(a){return arguments.length?(j=+a,p()):j},a.clientDelay=function(a){return arguments.length?(k=+a,p()):k},a.focus=function(b){return l.focus.call(a,o=b),a},a.on=function(b,c){return arguments.length<2?l.on(b):(l.on(b,c),c!=null&&(/^prepare(\.|$)/.test(b)&&c.call(a,h,i),/^beforechange(\.|$)/.test(b)&&c.call(a,f,g),/^change(\.|$)/.test(b)&&c.call(a,f,g),/^focus(\.|$)/.test(b)&&c.call(a,o)),a)},d3.select(window).on("keydown.context-"+ ++c,function(){switch(!d3.event.metaKey&&d3.event.keyCode){case 37:o==null&&(o=d-1),o>0&&a.focus(--o);break;case 39:o==null&&(o=d-2),o<d-1&&a.focus(++o);break;default:return}d3.event.preventDefault()}),p()};var f=b.context.prototype=e.prototype;f.constant=function(a){return new n(this,+a)},f.cube=function(a){arguments.length||(a="");var b={},c=this;return b.metric=function(b){return c.metric(function(c,d,e,f){d3.json(a+"/1.0/metric"+"?expression="+encodeURIComponent(b)+"&start="+g(c)+"&stop="+g(d)+"&step="+e,function(a){if(!a)return f(new Error("unable to load data"));f(null,a.map(function(a){return a.value}))})},b+="")},b.toString=function(){return a},b};var g=d3.time.format.iso;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 j=b;f!==1e4&&(j="summarize("+j+",'"+(f%36e5?f%6e4?f/1e3+"sec":f/6e4+"min":f/36e5+"hour")+"','"+d+"')"),d3.text(a+"/render?format=raw"+"&target="+encodeURIComponent("alias("+j+",'')")+"&from="+h(c-2*f)+"&until="+h(e-1e3),function(a){if(!a)return g(new Error("unable to load data"));g(null,i(a))})},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){if(!a)return c(new Error("unable to find metrics"));c(null,a.metrics.map(function(a){return a.path}))})},b.toString=function(){return a},b};var k=j.prototype;b.metric=j,k.valueAt=function(){return NaN},k.alias=function(a){return this.toString=function(){return a},this},k.extent=function(){var a=0,b=this.context.size(),c,d=Infinity,e=-Infinity;while(++a<b)c=this.valueAt(a),c<d&&(d=c),c>e&&(e=c);return[d,e]},k.on=function(a,b){return arguments.length<2?null:this},k.shift=function(){return this},k.on=function(){return arguments.length<2?null:this},f.metric=function(a,b){function r(b,c){var d=Math.min(k,Math.round((b-g)/i));if(!d||q)return;q=!0,d=Math.min(k,d+l);var f=new Date(c-d*i);a(f,c,i,function(a,b){q=!1;if(a)return console.warn(a);var d=isFinite(g)?Math.round((f-g)/i):0;for(var h=0,j=b.length;h<j;++h)n[h+d]=b[h];o.change.call(e,g,c)})}function s(a,b){isFinite(g)||(g=a),n.splice(0,Math.max(0,Math.min(k,Math.round((a-g)/i)))),g=a,h=b}var d=this,e=new j(d),f=".metric-"+ ++c,g=-Infinity,h,i=d.step(),k=d.size(),n=[],o=d3.dispatch("change"),p=0,q;return e.valueAt=function(a){return n[a]},e.shift=function(b){return d.metric(m(a,+b))},e.on=function(a,b){return arguments.length?(b==null?o.on(a)!=null&&--p==0&&d.on("prepare"+f,null).on("beforechange"+f,null):o.on(a)==null&&++p==1&&d.on("prepare"+f,r).on("beforechange"+f,s),o.on(a,b),b!=null&&/^change(\.|$)/.test(a)&&b.call(d,g,h),e):o.on(a)},arguments.length>1&&(e.toString=function(){return b}),e};var l=6,o=n.prototype=Object.create(j.prototype);o.valueAt=function(){return+this},o.extent=function(){return[+this,+this]},k.add=p("+",function(a,b){return a+b}),k.subtract=p("-",function(a,b){return a-b}),k.multiply=p("*",function(a,b){return a*b}),k.divide=p("/",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),t!=null&&(i=t);var j=0,k=Math.max(-i[0],i[1]);if(this===a){if(k==y){j=f-l;var m=(c-u)/v;if(m<f){var n=e.getContext("2d");n.clearRect(0,0,f,g),n.drawImage(w.canvas,m,0,f-m,g,0,0,f-m,g),w.clearRect(0,0,f,g),w.drawImage(n.canvas,0,0)}}u=c}h.domain([0,y=k]),w.clearRect(j,0,f-j,g);var o;for(var p=0;p<z;++p){w.fillStyle=s[z+p];var q=(p-z+1)*g;h.range([z*g+q,q]),q=h(0);for(var x=j,B=f,C;x<B;++x){C=r.valueAt(x);if(C<=0){o=!0;continue}w.fillRect(x,C=h(C),1,q-C)}}if(o){b==="offset"&&(w.translate(0,g),w.scale(1,-1));for(var p=0;p<z;++p){w.fillStyle=s[z-1-p];var q=(p-z+1)*g;h.range([z*g+q,q]),q=h(0);for(var x=j,B=f,C;x<B;++x){C=r.valueAt(x);if(C>=0)continue;w.fillRect(x,h(-C),1,q-h(-C))}}}w.restore()}function C(a){a==null&&(a=f-1);var b=r.valueAt(a);x.datum(b).text(isNaN(b)?null:m)}var p=this,q=++c,r=typeof i=="function"?i.call(p,k,o):i,s=typeof n=="function"?n.call(p,k,o):n,t=typeof j=="function"?j.call(p,k,o):j,u=-Infinity,v=a.step(),w=d3.select(p).select("canvas"),x=d3.select(p).select(".value"),y,z=s.length>>1,A;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,m=d3.format(".2s"),n=["#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?(m=a,o):m},o.colors=function(a){return arguments.length?(n=a,o):n},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=u.extent(),i=v==null?g:v;f.domain(i).range([e,0]),A=g.concat(h).every(isFinite);var j=c/a.step()&1?t:s;x.fillStyle=m[2];for(var k=0,l=b;k<l;++k){var o=f(r.valueAt(k)),p=f(u.valueAt(k));o<p&&x.fillRect(j(k),o,1,p-o)}x.fillStyle=m[0];for(k=0;k<l;++k){var o=f(r.valueAt(k)),p=f(u.valueAt(k));o>p&&x.fillRect(j(k),p,1,o-p)}x.fillStyle=m[3];for(k=0;k<l;++k){var o=f(r.valueAt(k)),p=f(u.valueAt(k));o<=p&&x.fillRect(j(k),o,1,n)}x.fillStyle=m[1];for(k=0;k<l;++k){var o=f(r.valueAt(k)),p=f(u.valueAt(k));o>p&&x.fillRect(j(k),o-n,1,n)}x.restore()}function C(a){a==null&&(a=b-1);var c=r.valueAt(a),d=u.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),u.on("change.comparison-"+q,d))}var p=this,q=++c,r=typeof g=="function"?g.call(p,j,o):g,u=typeof h=="function"?h.call(p,j,o):h,v=typeof i=="function"?i.call(p,j,o):i,w=d3.select(p),x=w.select("canvas"),y=w.select(".value.primary"),z=w.select(".value.change"),A;x.datum({id:q,primary:r,secondary:u}),x=x.node().getContext("2d"),r.on("change.comparison-"+q,D),u.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=q,l=r,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 q=d3.format(".2s"),r=d3.format("+.0%");f.axis=function(){function f(g){var h=++c,i,j=g.append("svg").datum({id:h}).attr("width",a.size()).attr("height",Math.max(28,-f.tickSize())).append("g").attr("transform","translate(0,"+(d.orient()==="top"?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(a==null)i.style("display","none"),j.selectAll("text").style("fill-opacity",null);else{i.style("display",null).attr("x",a).text(e(b.invert(a)));var c=i.node().getComputedTextLength()+6;j.selectAll("text").style("fill-opacity",function(d){return Math.abs(b(d)-a)<c?0:1})}})}var a=this,b=a.scale,d=d3.svg.axis().scale(b),e=a.step()<6e4?u:a.step()<864e5?v:w;return f.remove=function(b){function c(b){a.on("change.axis-"+b.id,null),a.on("focus.axis-"+b.id,null)}b.selectAll("svg").each(c).remove()},d3.rebind(f,d,"orient","ticks","tickSubdivide","tickSize","tickPadding","tickFormat")};var u=d3.time.format("%I:%M:%S %p"),v=d3.time.format("%I:%M %p"),w=d3.time.format("%B %d");f.rule=function(){function e(d){var e=++c,f=d.append("div").datum({id:e}).attr("class","line").call(x);d.each(function(e,f){function j(b,c){var e=[];for(var f=0,g=a.size();f<g;++f)i.valueAt(f)&&e.push(f);var h=d.selectAll(".metric").data(e);h.exit().remove(),h.enter().append("div").attr("class","metric line").call(x),h.style("left",y)}var g=this,h=++c,i=typeof b=="function"?b.call(g,e,f):b;if(!i)return;a.on("change.rule-"+h,j),i.on("change.rule-"+h,j)}),a.on("focus.rule-"+e,function(a){f.datum(a).style("display",a==null?"none":null).style("left",a==null?null:y)})}var a=this,b=d;return e.remove=function(b){function c(b){a.on("focus.rule-"+b.id,null)}b.selectAll(".line").each(c).remove()},e.metric=function(a){return arguments.length?(b=a,e):b},e}})(this);
metadata ADDED
@@ -0,0 +1,77 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cubism-rails
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.2.2
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Vlad Gorodetsky
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-01-06 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: railties
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '3.0'
22
+ - - <
23
+ - !ruby/object:Gem::Version
24
+ version: '5.0'
25
+ type: :runtime
26
+ prerelease: false
27
+ version_requirements: !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '3.0'
33
+ - - <
34
+ - !ruby/object:Gem::Version
35
+ version: '5.0'
36
+ description: ! 'Cubism.js: A JavaScript library for time series visualization.'
37
+ email:
38
+ - v@gor.io
39
+ executables: []
40
+ extensions: []
41
+ extra_rdoc_files: []
42
+ files:
43
+ - .gitignore
44
+ - Gemfile
45
+ - LICENSE
46
+ - README.md
47
+ - Rakefile
48
+ - cubism-rails.gemspec
49
+ - lib/cubism-rails.rb
50
+ - lib/cubism-rails/version.rb
51
+ - vendor/assets/javascripts/cubism.js
52
+ - vendor/assets/javascripts/cubism.min.js
53
+ homepage: http://github.com/bai/cubism-rails
54
+ licenses: []
55
+ post_install_message:
56
+ rdoc_options: []
57
+ require_paths:
58
+ - lib
59
+ required_ruby_version: !ruby/object:Gem::Requirement
60
+ none: false
61
+ requirements:
62
+ - - ! '>='
63
+ - !ruby/object:Gem::Version
64
+ version: '0'
65
+ required_rubygems_version: !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ! '>='
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ requirements: []
72
+ rubyforge_project:
73
+ rubygems_version: 1.8.24
74
+ signing_key:
75
+ specification_version: 3
76
+ summary: Gemified cubism.js asset for Rails
77
+ test_files: []