Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Replace Cube front-end with node-static.

The Cube front-end is now just a static file server (in addition to the /event
and /metric endpoints that power the collector and evaluator). This removes the
visualization component from Cube, so that it can focus on data collection and
metric computation. To make visualizations, you now simply write a bit of HTML
and JavaScript to fetch and display metrics. This commit will followup with some
examples that demonstrate the technique.

Also, add a 1-minute tier, and remove the week and year tiers. The highest tier
is now the 1-day tier. We could still use the old tiers, but there's a little
bit of overhead to invalidate every tier for every event; perhaps in the future,
we'll want to configure tiers on a per-event basis.
  • Loading branch information...
commit f848376e90bbfc9c27f333a21d080dc6b7e4ee73 1 parent e51cd37
@mbostock mbostock authored
Showing with 183 additions and 2,000 deletions.
  1. +2 −2 Makefile
  2. +0 −1  bin/evaluator.js
  3. +0 −12 lib/cube/client/board.css
  4. +0 −225 lib/cube/client/board.js
  5. +0 −87 lib/cube/client/body.css
  6. +0 −3  lib/cube/client/cube.js
  7. +0 −1  lib/cube/client/end.js
  8. +0 −58 lib/cube/client/header.js
  9. +0 −12 lib/cube/client/palette.css
  10. +0 −63 lib/cube/client/palette.js
  11. +0 −255 lib/cube/client/piece-area.js
  12. +0 −137 lib/cube/client/piece-sum.js
  13. +0 −65 lib/cube/client/piece-text.js
  14. +0 −151 lib/cube/client/piece.css
  15. +0 −270 lib/cube/client/piece.js
  16. +0 −1  lib/cube/client/semicolon.js
  17. +0 −45 lib/cube/client/squares.js
  18. +0 −1  lib/cube/client/start.js
  19. +0 −24 lib/cube/client/visualizer.html
  20. 0  lib/cube/{server → }/collectd.js
  21. +44 −0 lib/cube/collector.js
  22. 0  lib/cube/{server → }/emitter.js
  23. +8 −0 lib/cube/endpoint.js
  24. +10 −6 lib/cube/{server → }/evaluator.js
  25. 0  lib/cube/{server → }/event-expression.js
  26. 0  lib/cube/{server → }/event-expression.peg
  27. 0  lib/cube/{server → }/event.js
  28. +5 −7 lib/cube/index.js
  29. 0  lib/cube/{server → }/metric-expression.js
  30. 0  lib/cube/{server → }/metric-expression.peg
  31. 0  lib/cube/{server → }/metric.js
  32. 0  lib/cube/{server → }/reduces.js
  33. +12 −3 lib/cube/{server → }/server.js
  34. +0 −41 lib/cube/server/collector.js
  35. +0 −86 lib/cube/server/endpoint.js
  36. +0 −191 lib/cube/server/visualizer.js
  37. +8 −22 lib/cube/{server → }/tiers.js
  38. 0  lib/cube/{server → }/types.js
  39. +2 −2 package.json
  40. +4 −4 test/collector-test.js
  41. +0 −65 test/endpoint-test.js
  42. +1 −1  test/event-expression-test.js
  43. +1 −1  test/metric-expression-test.js
  44. +17 −28 test/metric-test.js
  45. +1 −1  test/reduces-test.js
  46. +67 −128 test/tiers-test.js
  47. +1 −1  test/types-test.js
View
4 Makefile
@@ -7,8 +7,8 @@ PEG_COMPILER = ./node_modules/pegjs/bin/pegjs
$(PEG_COMPILER) < $< > $@
all: \
- lib/cube/server/event-expression.js \
- lib/cube/server/metric-expression.js
+ lib/cube/event-expression.js \
+ lib/cube/metric-expression.js
test: all
@$(JS_TESTER)
View
1  bin/evaluator.js
@@ -4,7 +4,6 @@ var options = require("./evaluator-config"),
server.register = function(db, endpoints) {
cube.evaluator.register(db, endpoints);
- cube.visualizer.register(db, endpoints);
};
server.start();
View
12 lib/cube/client/board.css
@@ -1,12 +0,0 @@
-.squares rect {
- fill: none;
- stroke: #ddd;
-}
-
-.squares rect.black {
- fill: #fafafa;
-}
-
-.squares rect.shadow {
- fill: #e7e7e7;
-}
View
225 lib/cube/client/board.js
@@ -1,225 +0,0 @@
-cube.board = function(url, id) {
- var board = {id: cube_board_formatId(id)},
- socket,
- interval,
- pieceId = 0,
- palette,
- squares,
- pieces = [],
- size = [32, 18], // in number of squares
- squareSize = 40, // in pixels
- squareRadius = 4, // in pixels
- padding = 9.5; // half-pixel for crisp strokes
-
- var event = d3.dispatch(
- "size",
- "squareSize",
- "squareRadius",
- "padding",
- "view"
- );
-
- var svg = document.createElementNS(d3.ns.prefix.svg, "svg");
-
- d3.select(svg)
- .attr("class", "board");
-
- event.on("size.board", resize);
- event.on("squareSize.board", resize);
- event.on("squareRadius.board", resize);
- event.on("padding.board", resize);
-
- function message(message) {
- var e = JSON.parse(message.data);
- switch (e.type) {
- case "view": {
- event.view.call(board, e);
- break;
- }
- case "add": {
- var piece = board.add(cube.piece.type[e.piece.type])
- .fromJSON(e.piece)
- .on("move.board", move);
-
- d3.select(piece.node())
- .style("opacity", 1e-6)
- .transition()
- .duration(500)
- .style("opacity", 1);
-
- pieceId = Math.max(pieceId, piece.id = e.piece.id);
- break;
- }
- case "edit": {
- pieces.some(function(piece) {
- if (piece.id == e.piece.id) {
- piece
- .on("move.board", null)
- .transition(d3.transition().duration(500))
- .fromJSON(e.piece);
-
- // Renable events after transition starts.
- d3.timer(function() { piece.on("move.board", move); }, 250);
- return true;
- }
- });
- break;
- }
- case "move": {
- pieces.some(function(piece) {
- if (piece.id == e.piece.id) {
- piece
- .on("move.board", null)
- .transition(d3.transition().duration(500))
- .size(e.piece.size)
- .position(e.piece.position);
-
- // Bring to front.
- svg.parentNode.appendChild(piece.node());
-
- // Renable events after transition starts.
- d3.timer(function() { piece.on("move.board", move); }, 250);
- return true;
- }
- });
- break;
- }
- case "remove": {
- pieces.some(function(piece) {
- if (piece.id == e.piece.id) {
- board.remove(piece, true);
- return true;
- }
- });
- break;
- }
- }
- }
-
- function reopen() {
- if (socket) {
- pieces.slice().forEach(function(piece) { board.remove(piece, true); });
- socket.close();
- }
- socket = new WebSocket(url);
- socket.onopen = load;
- socket.onmessage = message;
- if (!interval) interval = setInterval(ping, 5000);
- }
-
- function load() {
- if (id && socket && socket.readyState == 1) {
- socket.send(JSON.stringify({type: "load", id: id}));
- }
- }
-
- function ping() {
- if (socket.readyState == 1) {
- socket.send(JSON.stringify({type: "ping", id: id}));
- } else if (socket.readyState > 1) {
- reopen();
- }
- }
-
- // A one-time listener to send an add event on mouseup.
- function add() {
- socket.send(JSON.stringify({type: "add", id: id, piece: this}));
- this.on("move.board", move);
- }
-
- function move() {
- socket.send(JSON.stringify({type: "move", id: id, piece: this}));
- }
-
- function edit() {
- socket.send(JSON.stringify({type: "edit", id: id, piece: this}));
- }
-
- function resize() {
- d3.select(svg)
- .attr("width", size[0] * squareSize + 2 * padding)
- .attr("height", (size[1] + 2) * squareSize + 2 * padding);
-
- d3.select(palette.node())
- .attr("transform", "translate(" + padding + "," + padding + ")");
-
- d3.select(squares.node())
- .attr("transform", "translate(" + padding + "," + (1.5 * squareSize + padding) + ")");
- }
-
- board.node = function() {
- return svg;
- };
-
- board.on = function(type, listener) {
- event.on(type, listener);
- return board;
- };
-
- board.size = function(x) {
- if (!arguments.length) return size;
- event.size.call(board, size = x);
- return board;
- };
-
- board.squareSize = function(x) {
- if (!arguments.length) return squareSize;
- event.squareSize.call(board, squareSize = x);
- return board;
- };
-
- board.squareRadius = function(x) {
- if (!arguments.length) return squareRadius;
- event.squareRadius.call(board, squareRadius = x);
- return board;
- };
-
- board.padding = function(x) {
- if (!arguments.length) return padding;
- event.padding.call(board, padding = x);
- return board;
- };
-
- board.add = function(type) {
- var piece = type(board);
- piece.id = ++pieceId;
- piece.on("move.board", add).on("edit.board", edit);
- svg.parentNode.appendChild(piece.node());
- pieces.push(piece);
- return piece;
- };
-
- board.remove = function(piece, silent) {
- piece.on("move.board", null).on("edit.board", null);
- if (silent) {
- d3.select(piece.node())
- .style("opacity", 1)
- .transition()
- .duration(500)
- .style("opacity", 1e-6)
- .remove();
- } else {
- socket.send(JSON.stringify({type: "remove", id: id, piece: {id: piece.id}}));
- svg.parentNode.removeChild(piece.node());
- }
- pieces.splice(pieces.indexOf(piece), 1);
- return piece;
- };
-
- board.toJSON = function() {
- return {id: id, size: size, pieces: pieces};
- };
-
- svg.appendChild((palette = cube.palette(board)).node());
- svg.appendChild((squares = cube.squares(board)).node());
- resize();
- reopen();
-
- return board;
-};
-
-function cube_board_formatId(id) {
- id = id.toString(36);
- if (id.length < 6) id = new Array(7 - id.length).join("0") + id;
- return id;
-}
View
87 lib/cube/client/body.css
@@ -1,87 +0,0 @@
-body {
- overflow-x: hidden;
- overflow-y: scroll;
- margin-top: 50px;
-}
-
-body, button {
- font: 14px "Helvetica Neue";
-}
-
-#header {
- display: block;
- position: fixed;
- top: 0;
- left: 0;
- width: 100%;
- height: 40px;
- background: #222;
- background: -webkit-gradient(linear,left top,left bottom,from(#333),to(#111));
- color: #fff;
- -webkit-box-shadow: 0 1px 2px rgba(0,0,0,0.5);
- z-index: 1;
-}
-
-.view #header {
- top: -60px;
-}
-
-.header {
- height: 40px;
- line-height: 40px;
- width: 1281px;
-}
-
-.header, #board {
- display: block;
- margin: auto;
- position: relative;
-}
-
-#board {
- width: 1299px;
-}
-
-.left {
- float: left;
-}
-
-.right {
- float: right;
-}
-
-.right button {
- margin: 0 0 0 4px;
-}
-
-svg {
- display: block;
-}
-
-button {
- background: #333;
- background: -webkit-gradient(linear,left top,left bottom,from(#444),to(#222));
- color: #fff;
- cursor: pointer;
- height: 30px;
- width: 113px;
- margin: 0 4px 0 0;
- padding: 0;
- border: solid 1px #000;
- border-radius: 4px;
-}
-
-.view {
- margin-top: -60px;
- overflow: hidden;
-}
-
-.view .squares {
- display: none;
-}
-
-@media screen and (min-width: 1920px) {
- .view {
- -webkit-transform: scale(1.4,1.4)translate(0px,110px);
- }
-}
View
3  lib/cube/client/cube.js
@@ -1,3 +0,0 @@
-cube = {version: "0.0.1"};
-
-var cube_time = d3.time.format.iso;
View
1  lib/cube/client/end.js
@@ -1 +0,0 @@
-})();
View
58 lib/cube/client/header.js
@@ -1,58 +0,0 @@
-cube.header = function(board) {
- var header = {};
-
- var div = document.createElement("div");
-
- var selection = d3.select(div)
- .attr("class", "header");
-
- var left = selection.append("div")
- .attr("class", "left");
-
- left.append("a")
- .attr("href", "/" + board.id)
- .append("button")
- .text("View");
-
- left.append("a")
- .attr("href", "/" + board.id + "/edit")
- .append("button")
- .text("Edit");
-
- var viewers = selection.append("div")
- .attr("class", "right");
-
- board.on("view", function(e) {
- viewers.text(e.count > 1 ? e.count - 1 + " other" + (e.count > 2 ? "s" : "") + " viewing" : null);
- });
-
- header.node = function() {
- return div;
- };
-
- if (mode == "view") {
- var shown = false;
-
- d3.select(window)
- .on("mouseout", mouseout)
- .on("mousemove", mousemove);
-
- function show(show) {
- if (show != shown) {
- d3.select(div.parentNode).transition()
- .style("top", ((shown = show) ? 0 : -60) + "px");
- }
- }
-
- function mouseout() {
- if (d3.event.relatedTarget == null) show(false);
- }
-
- function mousemove() {
- if (d3.event.pageY > 120) show(false);
- else if (d3.event.pageY < 60) show(true);
- }
- }
-
- return header;
-};
View
12 lib/cube/client/palette.css
@@ -1,12 +0,0 @@
-.palette rect {
- fill: #eee;
- stroke: #999;
-}
-
-.palette rect:hover {
- fill: #ddd;
-}
-
-.palette text {
- pointer-events: none;
-}
View
63 lib/cube/client/palette.js
@@ -1,63 +0,0 @@
-cube.palette = function(board) {
- var palette = {};
-
- var g = document.createElementNS(d3.ns.prefix.svg, "g");
-
- var type = d3.select(g)
- .attr("class", "palette")
- .selectAll(".piece-type")
- .data(d3.entries(cube.piece.type))
- .enter().append("svg:g")
- .attr("class", "piece-type")
- .on("mousedown", mousedown);
-
- type.append("svg:rect");
-
- type.append("svg:text")
- .attr("dy", ".35em")
- .attr("text-anchor", "middle")
- .text(function(d) { return d.key; });
-
- board
- .on("squareSize", resize)
- .on("squareRadius", resize);
-
- function resize() {
- var size = board.squareSize(),
- radius = board.squareRadius();
-
- type
- .attr("transform", function(d, i) { return "translate(" + (i * size + size / 2) + "," + (size / 2) + ")"; })
- .select("rect")
- .attr("x", -size / 2)
- .attr("y", -size / 2)
- .attr("width", size)
- .attr("height", size)
- .attr("rx", radius)
- .attr("ry", radius);
- }
-
- function mousedown(d) {
- var piece = board.add(d.value),
- pieceSize = piece.size(),
- squareSize = board.squareSize(),
- mouse = d3.svg.mouse(g);
-
- piece.position([
- mouse[0] / squareSize - pieceSize[0] / 2,
- mouse[1] / squareSize - pieceSize[1] / 2 - 1.5
- ]);
-
- // Simulate mousedown on the piece to start dragging.
- var div = d3.select(piece.node());
- div.each(div.on("mousedown.piece"));
- }
-
- palette.node = function() {
- return g;
- };
-
- resize();
-
- return palette;
-};
View
255 lib/cube/client/piece-area.js
@@ -1,255 +0,0 @@
-cube.piece.type.area = function(board) {
- var timeout,
- data = [],
- dt0;
-
- var area = cube.piece(board)
- .on("size", resize)
- .on("serialize", serialize)
- .on("deserialize", deserialize);
-
- var div = d3.select(area.node())
- .classed("area", true);
-
- if (mode == "edit") {
- div.append("h3")
- .attr("class", "title")
- .text("Area Chart");
-
- var query = div.append("textarea")
- .attr("class", "query")
- .attr("placeholder", "query expression…")
- .on("keyup.area", querychange)
- .on("focus.area", area.focus)
- .on("blur.area", area.blur);
-
- var time = div.append("div")
- .attr("class", "time")
- .text("Time Range:");
-
- time.append("input")
- .property("value", 1440);
-
- time.append("select").selectAll("option")
- .data([
- {description: "Seconds @ 10", value: 1e4},
- {description: "Minutes @ 5", value: 3e5},
- {description: "Hours", value: 36e5},
- {description: "Days", value: 864e5},
- {description: "Weeks", value: 6048e5},
- {description: "Months", value: 2592e6}
- ])
- .enter().append("option")
- .property("selected", function(d, i) { return i == 1; })
- .attr("value", cube_piece_areaValue)
- .text(function(d) { return d.description; });
-
- time.selectAll("input,select")
- .on("change.area", area.edit)
- .on("focus.area", area.focus)
- .on("blur.area", area.blur)
- } else {
- var m = [6, 40, 14, 10], // top, right, bottom, left margins
- socket;
-
- var svg = div.append("svg:svg");
-
- var x = d3.time.scale(),
- y = d3.scale.linear(),
- xAxis = d3.svg.axis().scale(x).orient("bottom").tickSubdivide(true),
- yAxis = d3.svg.axis().scale(y).orient("right");
-
- var a = d3.svg.area()
- .interpolate("step-after")
- .x(function(d) { return x(d.time); })
- .y0(function(d) { return y(0); })
- .y1(function(d) { return y(d.value); });
-
- var l = d3.svg.line()
- .interpolate("step-after")
- .x(function(d) { return x(d.time); })
- .y(function(d) { return y(d.value); });
-
- var g = svg.append("svg:g")
- .attr("transform", "translate(" + m[3] + "," + m[0] + ")");
-
- g.append("svg:g").attr("class", "y axis").call(yAxis);
- g.append("svg:path").attr("class", "area");
- g.append("svg:g").attr("class", "x axis").call(xAxis);
- g.append("svg:path").attr("class", "line");
- }
-
- function resize() {
- var transition = area.transition();
-
- if (mode == "edit") {
- var innerSize = area.innerSize();
-
- transition.select(".query")
- .style("width", innerSize[0] - 12 + "px")
- .style("height", innerSize[1] - 58 + "px");
-
- transition.select(".time select")
- .style("width", innerSize[0] - 174 + "px");
-
- } else {
- var z = board.squareSize(),
- w = area.size()[0] * z - m[1] - m[3],
- h = area.size()[1] * z - m[0] - m[2];
-
- x.range([0, w]);
- y.range([h, 0]);
-
- // Adjust the ticks based on the current chart dimensions.
- xAxis.ticks(w / 80).tickSize(-h, 0);
- yAxis.ticks(h / 25).tickSize(-w, 0);
-
- transition.select("svg")
- .attr("width", w + m[1] + m[3])
- .attr("height", h + m[0] + m[2]);
-
- transition.select(".area")
- .attr("d", a(data));
-
- transition.select(".x.axis")
- .attr("transform", "translate(0," + h + ")")
- .call(xAxis)
- .select("path")
- .attr("transform", "translate(0," + (y(0) - h) + ")");
-
- transition.select(".y.axis")
- .attr("transform", "translate(" + w + ",0)")
- .call(yAxis);
-
- transition.select(".line")
- .attr("d", l(data));
- }
- }
-
- function redraw() {
- if (data.length > 1) data[data.length - 1].value = data[data.length - 2].value;
-
- var z = board.squareSize(),
- h = area.size()[1] * z - m[0] - m[2],
- min = d3.min(data, cube_piece_areaValue),
- max = d3.max(data, cube_piece_areaValue);
-
- if ((min < 0) && (max < 0)) max = 0;
- else if ((min > 0) && (max > 0)) min = 0;
- y.domain([min, max]).nice();
-
- div.select(".area").attr("d", a(data));
- div.select(".y.axis").call(yAxis.tickFormat(cube_piece_format(y.domain())));
- div.select(".x.axis").call(xAxis).select("path").attr("transform", "translate(0," + (y(0) - h) + ")");
- div.select(".line").attr("d", l(data));
- return true;
- }
-
- function querychange() {
- if (timeout) clearTimeout(timeout);
- timeout = setTimeout(area.edit, 750);
- }
-
- function serialize(json) {
- var step = +time.select("select").property("value"),
- range = time.select("input").property("value") * cube_piece_areaMultipler(step);
- json.type = "area";
- json.query = query.property("value");
- json.time = {range: range, step: step};
- }
-
- function deserialize(json) {
- if (mode == "edit") {
- query.property("value", json.query);
- time.select("input").property("value", json.time.range / cube_piece_areaMultipler(json.time.step));
- time.select("select").property("value", json.time.step);
- } else {
- var dt1 = json.time.step,
- t1 = new Date(Math.floor(Date.now() / dt1) * dt1),
- t0 = new Date(t1 - json.time.range),
- d0 = x.domain(),
- d1 = [t0, t1];
-
- if (dt0 != dt1) {
- data = [];
- dt0 = dt1;
- }
-
- if (d0 != d1 + "") {
- x.domain(d1);
- resize();
- var times = data.map(cube_piece_areaTime);
- data = data.slice(d3.bisectLeft(times, t0), d3.bisectLeft(times, t1));
- data.push({time: t1, value: 0});
- }
-
- if (timeout) timeout = clearTimeout(timeout);
- if (socket) socket.close();
- socket = new WebSocket("ws://" + location.host + "/1.0/metric/get");
- socket.onopen = load;
- socket.onmessage = store;
-
- function load() {
- timeout = setTimeout(function() {
- socket.send(JSON.stringify({
- expression: json.query,
- start: cube_time(t0),
- stop: cube_time(t1),
- step: dt1
- }));
- timeout = setTimeout(function() {
- deserialize(json);
- }, t1 - Date.now() + dt1 + 4500 + 1000 * Math.random());
- }, 500);
- }
-
- // TODO use a skip list to insert more efficiently
- // TODO compute contiguous segments on the fly
- function store(message) {
- var d = JSON.parse(message.data);
- var i = d3.bisectLeft(data.map(cube_piece_areaTime), d.time = cube_time.parse(d.time));
- if (i < 0 || data[i].time - d.time) {
- if (d.value != null) {
- data.splice(i, 0, d);
- }
- } else if (d.value == null) {
- data.splice(i, 1);
- } else {
- data[i] = d;
- }
- d3.timer(redraw);
- }
- }
- }
-
- area.copy = function() {
- return board.add(cube.piece.type.area);
- };
-
- resize();
-
- return area;
-};
-
-function cube_piece_areaTime(d) {
- return d.time;
-}
-
-function cube_piece_areaValue(d) {
- return d.value;
-}
-
-var cube_piece_formatNumber = d3.format(".2r");
-
-function cube_piece_areaMultipler(step) {
- return step / (step === 1e4 ? 10
- : step === 3e5 ? 5
- : 1);
-}
-
-function cube_piece_format(domain) {
- var prefix = d3.formatPrefix(Math.max(-domain[0], domain[1]), 2);
- return function(value) {
- return cube_piece_formatNumber(value * prefix.scale) + prefix.symbol;
- };
-}
View
137 lib/cube/client/piece-sum.js
@@ -1,137 +0,0 @@
-cube.piece.type.sum = function(board) {
- var timeout,
- socket,
- data = 0,
- format = d3.format(".2s");
-
- var sum = cube.piece(board)
- .on("size", resize)
- .on("serialize", serialize)
- .on("deserialize", deserialize);
-
- var div = d3.select(sum.node())
- .classed("sum", true);
-
- if (mode == "edit") {
- div.append("h3")
- .attr("class", "title")
- .text("Rolling Sum");
-
- var query = div.append("textarea")
- .attr("class", "query")
- .attr("placeholder", "query expression…")
- .on("keyup.sum", querychange)
- .on("focus.sum", sum.focus)
- .on("blur.sum", sum.blur);
-
- var time = div.append("div")
- .attr("class", "time")
- .text("Time Range:");
-
- time.append("input")
- .property("value", 1440);
-
- time.append("select").selectAll("option")
- .data([
- {description: "Seconds @ 10", value: 1e4},
- {description: "Minutes @ 5", value: 3e5},
- {description: "Hours", value: 36e5},
- {description: "Days", value: 864e5},
- {description: "Weeks", value: 6048e5},
- {description: "Months", value: 2592e6}
- ])
- .enter().append("option")
- .property("selected", function(d, i) { return i == 1; })
- .attr("value", cube_piece_areaValue)
- .text(function(d) { return d.description; });
-
- time.selectAll("input,select")
- .on("change.sum", sum.edit)
- .on("focus.sum", sum.focus)
- .on("blur.sum", sum.blur)
- }
-
- function resize() {
- var innerSize = sum.innerSize(),
- transition = sum.transition();
-
- if (mode == "edit") {
- transition.select(".query")
- .style("width", innerSize[0] - 12 + "px")
- .style("height", innerSize[1] - 58 + "px");
-
- transition.select(".time select")
- .style("width", innerSize[0] - 174 + "px");
- } else {
- transition
- .style("font-size", innerSize[0] / 5 + "px")
- .style("line-height", innerSize[1] + "px")
- .text(format(data));
- }
- }
-
- function redraw() {
- div.text(format(data));
- return true;
- }
-
- function querychange() {
- if (timeout) clearTimeout(timeout);
- timeout = setTimeout(sum.edit, 750);
- }
-
- function serialize(json) {
- var step = +time.select("select").property("value"),
- range = time.select("input").property("value") * cube_piece_areaMultipler(step);
- json.type = "sum";
- json.query = query.property("value");
- json.time = {range: range, step: step};
- }
-
- function deserialize(json) {
- if (!json.time.range) json.time = {range: json.time, step: 3e5};
- if (mode == "edit") {
- query.property("value", json.query);
- time.select("input").property("value", json.time.range / cube_piece_areaMultipler(json.time.step));
- time.select("select").property("value", json.time.step);
- } else {
- var dt = json.time.step,
- t1 = new Date(Math.floor(Date.now() / dt) * dt),
- t0 = new Date(t1 - json.time.range);
-
- data = 0;
-
- if (timeout) timeout = clearTimeout(timeout);
- if (socket) socket.close();
- socket = new WebSocket("ws://" + location.host + "/1.0/metric/get");
- socket.onopen = load;
- socket.onmessage = store;
-
- function load() {
- socket.send(JSON.stringify({
- expression: json.query,
- start: cube_time(t0),
- stop: cube_time(t1),
- step: dt
- }));
- timeout = setTimeout(function() {
- deserialize(json);
- }, t1 - Date.now() + dt + 4500 + 1000 * Math.random());
- }
-
- function store(message) {
- var d = JSON.parse(message.data);
- if (!isNaN(d.value)) data += d.value;
- d3.timer(redraw);
- }
- }
- }
-
- sum.copy = function() {
- return board.add(cube.piece.type.sum);
- };
-
- resize();
-
- return sum;
-};
View
65 lib/cube/client/piece-text.js
@@ -1,65 +0,0 @@
-cube.piece.type.text = function(board) {
- var timeout;
-
- var text = cube.piece(board)
- .on("size", resize)
- .on("serialize", serialize)
- .on("deserialize", deserialize);
-
- var div = d3.select(text.node())
- .classed("text", true);
-
- if (mode == "edit") {
- div.append("h3")
- .attr("class", "title")
- .text("Text Label");
-
- var content = div.append("textarea")
- .attr("class", "content")
- .attr("placeholder", "text content…")
- .on("keyup.text", textchange)
- .on("focus.text", text.focus)
- .on("blur.text", text.blur);
- }
-
- function resize() {
- var transition = text.transition(),
- innerSize = text.innerSize();
-
- if (mode == "edit") {
- transition.select(".content")
- .style("width", innerSize[0] - 12 + "px")
- .style("height", innerSize[1] - 34 + "px");
- } else {
- transition
- .style("font-size", innerSize[0] / 12 + "px")
- .style("margin-top", innerSize[1] / 2 + innerSize[0] / 5 - innerSize[0] / 12 + "px");
- }
- }
-
- function textchange() {
- if (timeout) clearTimeout(timeout);
- timeout = setTimeout(text.edit, 750);
- }
-
- function serialize(json) {
- json.type = "text";
- json.content = content.property("value");
- }
-
- function deserialize(json) {
- if (mode == "edit") {
- content.property("value", json.content);
- } else {
- div.text(json.content);
- }
- }
-
- text.copy = function() {
- return board.add(cube.piece.type.text);
- };
-
- resize();
-
- return text;
-};
View
151 lib/cube/client/piece.css
@@ -1,151 +0,0 @@
-.piece {
- position: absolute;
- text-shadow: 0 1px 1px #fff;
- font-size: 12px;
-}
-
-.view .piece {
- font: 10px sans-serif;
- padding: 4px 4px 3px 3px;
-}
-
-.edit .piece {
- background: #eee;
- border: solid 2px #ccc;
- border-radius: 4px;
- margin: -2px 0 0 -2px;
-}
-
-.piece:focus, .piece.active {
- box-shadow: 0 4px 8px rgba(0,0,0,.3);
- outline-style: none;
- border-color: #999;
- background-color: #e7e7e7;
-}
-
-.piece h3 {
- pointer-events: none;
- font-size: 14px;
- margin: 4px;
-}
-
-.piece textarea, .piece input {
- margin-left: 4px;
- height: 20px;
- border-radius: 4px;
- border: solid 1px #ccc;
- resize: none;
-}
-
-.piece .time {
- margin-top: 1px;
- margin-left: 4px;
- line-height: 20px;
-}
-
-.piece input {
- height: 16px;
- width: 88px;
-}
-
-.piece select {
- margin-right: 4px;
- float: right;
- height: 20px;
- border-radius: 4px;
- border: solid 1px #ccc;
-}
-
-.view .piece.sum, .view .piece.text {
- text-align: right;
-}
-
-.resize {
- position: absolute;
- padding: 6px;
-}
-
-.resize.n {
- top: -8px;
- left: -8px;
- width: 100%;
- cursor: ns-resize;
-}
-
-.resize.e {
- top: -8px;
- right: -8px;
- height: 100%;
- cursor: ew-resize;
-}
-
-.resize.s {
- bottom: -8px;
- left: -8px;
- width: 100%;
- cursor: ns-resize;
-}
-
-.resize.w {
- top: -8px;
- left: -8px;
- height: 100%;
- cursor: ew-resize;
-}
-
-.resize.nw {
- top: -8px;
- left: -8px;
- cursor: nwse-resize;
-}
-
-.resize.ne {
- top: -8px;
- right: -8px;
- cursor: nesw-resize;
-}
-
-.resize.se {
- bottom: -8px;
- right: -8px;
- cursor: nwse-resize;
-}
-
-.resize.sw {
- bottom: -8px;
- left: -8px;
- cursor: nesw-resize;
-}
-
-.area path.area {
- fill: #e7e7e7;
-}
-
-.area path.line {
- fill: none;
- stroke: #666;
-}
-
-.axis {
- shape-rendering: crispEdges;
-}
-
-.axis .domain {
- fill: none;
-}
-
-.x.axis .domain {
- stroke: #000;
-}
-
-.x.axis line {
- stroke: #fff;
-}
-
-.x.axis .minor {
- stroke-opacity: .5;
-}
-
-.y.axis line {
- stroke: #eee;
-}
View
270 lib/cube/client/piece.js
@@ -1,270 +0,0 @@
-cube.piece = function(board) {
- var piece = {},
- size = [8, 3],
- position = [0, 0],
- padding = 4;
-
- var event = d3.dispatch(
- "position",
- "size",
- "move",
- "edit",
- "serialize",
- "deserialize"
- );
-
- var div = document.createElement("div"),
- selection = d3.select(div),
- transition = selection;
-
- var selection = d3.select(div)
- .attr("class", "piece");
-
- if (mode == "edit") {
- selection
- .attr("tabindex", 1)
- .on("keydown.piece", keydown)
- .on("mousedown.piece", mousedrag)
- .selectAll(".resize")
- .data(["n", "e", "s", "w", "nw", "ne", "se", "sw"])
- .enter().append("div")
- .attr("class", function(d) { return "resize " + d; })
- .on("mousedown.piece", mouseresize);
-
- d3.select(window)
- .on("keydown.piece", cube_piece_keydown)
- .on("mousemove.piece", cube_piece_mousemove)
- .on("mouseup.piece", cube_piece_mouseup);
- }
-
- board
- .on("padding.piece", resize)
- .on("squareSize.piece", resize);
-
- event.on("position.piece", resize);
- event.on("size.piece", resize);
- event.on("deserialize.piece", deserialize);
-
- function resize() {
- var squareSize = board.squareSize(),
- boardPadding = board.padding() | 0;
-
- piece.transition()
- .style("left", (padding + boardPadding + position[0] * squareSize) + "px")
- .style("top", (padding + boardPadding + (1.5 + position[1]) * squareSize) + "px")
- .style("width", size[0] * squareSize - 2 * padding + 1 + "px")
- .style("height", size[1] * squareSize - 2 * padding + 1 + "px");
- }
-
- function deserialize(x) {
- piece
- .size(x.size)
- .position(x.position);
- }
-
- function keydown() {
- if (d3.event.target !== this) return d3.event.stopPropagation();
- if (cube_piece_dragPiece) return;
- if (d3.event.keyCode === 8) {
- board.remove(piece);
- d3.event.preventDefault();
- }
- }
-
- piece.node = function() {
- return div;
- };
-
- piece.on = function(type, listener) {
- event.on(type, listener);
- return piece;
- };
-
- piece.size = function(x) {
- if (!arguments.length) return size;
- event.size.call(piece, size = x);
- return piece;
- };
-
- piece.innerSize = function() {
- var squareSize = board.squareSize();
- return [
- size[0] * squareSize - 2 * padding,
- size[1] * squareSize - 2 * padding
- ];
- };
-
- piece.position = function(x) {
- if (!arguments.length) return position;
- event.position.call(piece, position = x);
- return piece;
- };
-
- piece.toJSON = function() {
- var x = {id: piece.id, size: size, position: position};
- event.serialize.call(piece, x);
- return x;
- };
-
- piece.fromJSON = function(x) {
- event.deserialize.call(piece, x);
- return piece;
- };
-
- piece.edit = function() {
- event.edit.call(piece);
- return piece;
- };
-
- function mousedrag() {
- if (/^(INPUT|TEXTAREA|SELECT)$/.test(d3.event.target.tagName)) return;
- if (d3.event.target === this && d3.event.altKey) {
- cube_piece_dragPiece = piece.copy().fromJSON(piece.toJSON());
- cube_piece_dragPiece.node().focus();
- } else {
- d3.select(this).transition(); // cancel transition, if any
- this.parentNode.appendChild(this).focus();
- cube_piece_dragPiece = piece;
- }
- cube_piece_dragOrigin = [d3.event.pageX, d3.event.pageY];
- cube_piece_dragPosition = position.slice();
- cube_piece_dragSize = size.slice();
- cube_piece_dragBoard = board;
- cube_piece_mousemove();
- }
-
- function mouseresize(d) {
- cube_piece_dragResize = d;
- }
-
- piece.transition = function(x) {
- if (!arguments.length) return transition;
- if (x == null) {
- transition = selection;
- } else {
- transition = x.select(function() { return div; });
- d3.timer(function() {
- event.move.call(piece);
- return transition = selection;
- });
- }
- return piece;
- };
-
- piece.focus = function() {
- selection.classed("active", true);
- return piece;
- };
-
- piece.blur = function() {
- selection.classed("active", false);
- return piece;
- };
-
- resize();
-
- return piece;
-};
-
-cube.piece.type = {};
-
-var cube_piece_dragPiece,
- cube_piece_dragBoard,
- cube_piece_dragOrigin,
- cube_piece_dragPosition,
- cube_piece_dragSize,
- cube_piece_dragResize;
-
-function cube_piece_mousePosition() {
- var squareSize = cube_piece_dragBoard.squareSize();
- return cube_piece_dragResize ? [
- cube_piece_dragPosition[0] + /w$/.test(cube_piece_dragResize) * Math.min(cube_piece_dragSize[0] - 5, (d3.event.pageX - cube_piece_dragOrigin[0]) / squareSize),
- cube_piece_dragPosition[1] + /^n/.test(cube_piece_dragResize) * Math.min(cube_piece_dragSize[1] - 3, (d3.event.pageY - cube_piece_dragOrigin[1]) / squareSize)
- ] : [
- cube_piece_dragPosition[0] + (d3.event.pageX - cube_piece_dragOrigin[0]) / squareSize,
- cube_piece_dragPosition[1] + (d3.event.pageY - cube_piece_dragOrigin[1]) / squareSize
- ];
-}
-
-function cube_piece_mouseSize() {
- var squareSize = cube_piece_dragBoard.squareSize();
- return cube_piece_dragResize ? [
- Math.max(5, cube_piece_dragSize[0] + (/e$/.test(cube_piece_dragResize) ? 1 : /w$/.test(cube_piece_dragResize) ? -1 : 0) * (d3.event.pageX - cube_piece_dragOrigin[0]) / squareSize),
- Math.max(3, cube_piece_dragSize[1] + (/^s/.test(cube_piece_dragResize) ? 1 : /^n/.test(cube_piece_dragResize) ? -1 : 0) * (d3.event.pageY - cube_piece_dragOrigin[1]) / squareSize)
- ] : cube_piece_dragSize;
-}
-
-function cube_piece_clamp(position, size) {
- var boardSize = cube_piece_dragBoard.size();
- if (cube_piece_dragResize) {
- if (/e$/.test(cube_piece_dragResize)) {
- size[0] = Math.max(0, Math.min(boardSize[0] - position[0], Math.round(size[0])));
- } else if (/w$/.test(cube_piece_dragResize)) {
- size[0] = Math.round(size[0] + position[0] - (position[0] = Math.max(0, Math.min(boardSize[0], Math.round(position[0])))));
- }
- if (/^s/.test(cube_piece_dragResize)) {
- size[1] = Math.max(0, Math.min(boardSize[1] - position[1], Math.round(size[1])));
- } else if (/^n/.test(cube_piece_dragResize)) {
- size[1] = Math.round(size[1] + position[1] - (position[1] = Math.max(0, Math.min(boardSize[1], Math.round(position[1])))));
- }
- } else {
- position[0] = Math.max(0, Math.min(boardSize[0] - size[0], Math.round(position[0])));
- position[1] = Math.max(0, Math.min(boardSize[1] - size[1], Math.round(position[1])));
- }
-}
-
-function cube_piece_mousemove() {
- if (cube_piece_dragPiece) {
- var position = cube_piece_mousePosition(),
- size = cube_piece_mouseSize();
-
- cube_piece_dragPiece
- .position(position.slice())
- .size(size.slice());
-
- cube_piece_clamp(position, size);
-
- var x0 = position[0],
- y0 = position[1],
- x1 = x0 + size[0],
- y1 = y0 + size[1];
-
- d3.select(cube_piece_dragBoard.node()).selectAll(".squares rect")
- .classed("shadow", function(d, i) { return d.x >= x0 && d.x < x1 && d.y >= y0 && d.y < y1; });
-
- d3.event.preventDefault();
- }
-}
-
-function cube_piece_mouseup() {
- if (cube_piece_dragPiece) {
- var position = cube_piece_mousePosition(),
- size = cube_piece_mouseSize();
-
- cube_piece_clamp(position, size);
-
- d3.select(cube_piece_dragBoard.node()).selectAll(".squares rect")
- .classed("shadow", false);
-
- cube_piece_dragPiece
- .transition(d3.transition().ease("elastic").duration(500))
- .position(position)
- .size(size);
-
- cube_piece_dragPiece =
- cube_piece_dragBoard =
- cube_piece_dragEvent =
- cube_piece_dragOrigin =
- cube_piece_dragPosition =
- cube_piece_dragSize =
- cube_piece_dragResize = null;
- d3.event.preventDefault();
- }
-}
-
-// Disable delete as the back key, since we use it to delete pieces.
-function cube_piece_keydown() {
- if (d3.event.keyCode == 8) {
- d3.event.preventDefault();
- }
-}
View
1  lib/cube/client/semicolon.js
@@ -1 +0,0 @@
-;
View
45 lib/cube/client/squares.js
@@ -1,45 +0,0 @@
-cube.squares = function(board) {
- var squares = {};
-
- var g = document.createElementNS(d3.ns.prefix.svg, "g");
-
- d3.select(g)
- .attr("class", "squares");
-
- board
- .on("size", resize)
- .on("squareSize", resize)
- .on("squareRadius", resize);
-
- function resize() {
- var boardSize = board.size(),
- squareSize = board.squareSize(),
- squareRadius = board.squareRadius();
-
- var square = d3.select(g).selectAll(".square")
- .data(d3.range(boardSize[0] * boardSize[1])
- .map(function(d) {return { x: d % boardSize[0], y: d / boardSize[0] | 0}; }));
-
- square.enter().append("svg:rect")
- .attr("class", "square");
-
- square
- .attr("rx", squareRadius)
- .attr("ry", squareRadius)
- .attr("class", function(d, i) { return (i - d.y & 1 ? "black" : "white") + " square"; })
- .attr("x", function(d) { return d.x * squareSize; })
- .attr("y", function(d) { return d.y * squareSize; })
- .attr("width", squareSize)
- .attr("height", squareSize);
-
- square.exit().remove();
- }
-
- squares.node = function() {
- return g;
- };
-
- resize();
-
- return squares;
-};
View
1  lib/cube/client/start.js
@@ -1 +0,0 @@
-(function(){
View
24 lib/cube/client/visualizer.html
@@ -1,24 +0,0 @@
-<!DOCTYPE html>
-<html>
- <head>
- <title>Cube</title>
- <script type="text/javascript" src="/d3/d3.js"></script>
- <script type="text/javascript" src="/cube.js"></script>
- <link type="text/css" rel="stylesheet" href="/cube.css"/>
- </head>
- <body>
- <div id="header"></div>
- <div id="board"></div>
- <script type="text/javascript">
-
-var id = parseInt(location.pathname.substring(1), 36),
- mode = document.body.className = location.pathname.substring(8) || "view",
- board = cube.board("ws://" + location.host + "/board", id),
- header = cube.header(board);
-
-d3.select("#header").node().appendChild(header.node());
-d3.select("#board").node().appendChild(board.node());
-
- </script>
- </body>
-</html>
View
0  lib/cube/server/collectd.js → lib/cube/collectd.js
File renamed without changes
View
44 lib/cube/collector.js
@@ -0,0 +1,44 @@
+var endpoint = require("./endpoint");
+
+//
+var headers = {
+ "Content-Type": "application/json",
+ "Access-Control-Allow-Origin": "*"
+};
+
+exports.register = function(db, endpoints) {
+ var putter = require("./event").putter(db),
+ poster = post(putter);
+
+ //
+ endpoints.ws.push(
+ endpoint("/1.0/event/put", putter)
+ );
+
+ //
+ endpoints.http.push(
+ endpoint("POST", "/1.0/event", poster),
+ endpoint("POST", "/1.0/event/put", poster),
+ endpoint("POST", "/collectd", require("./collectd").putter(putter))
+ );
+};
+
+function post(putter) {
+ return function(request, response) {
+ var content = "";
+ request.on("data", function(chunk) {
+ content += chunk;
+ });
+ request.on("end", function() {
+ try {
+ JSON.parse(content).forEach(putter);
+ } catch (e) {
+ response.writeHead(400, headers);
+ response.end(JSON.stringify({error: e.toString()}));
+ return;
+ }
+ response.writeHead(200, headers);
+ response.end("{}");
+ });
+ };
+}
View
0  lib/cube/server/emitter.js → lib/cube/emitter.js
File renamed without changes
View
8 lib/cube/endpoint.js
@@ -0,0 +1,8 @@
+module.exports = function(method, path, dispatch) {
+ return {
+ match: arguments.length < 3
+ ? (dispatch = path, path = method, function(p) { return p == path; })
+ : function(p, m) { return m == method && p == path; },
+ dispatch: dispatch
+ };
+}
View
16 lib/cube/server/evaluator.js → lib/cube/evaluator.js
@@ -6,6 +6,7 @@ var endpoint = require("./endpoint"),
// results are returned.
var limitMax = 1e4;
+//
var headers = {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*"
@@ -15,15 +16,18 @@ exports.register = function(db, endpoints) {
var event = require("./event").getter(db),
metric = require("./metric").getter(db);
+ //
endpoints.ws.push(
- endpoint.exact("/1.0/event/get", event),
- endpoint.exact("/1.0/metric/get", metric)
+ endpoint("/1.0/event/get", event),
+ endpoint("/1.0/metric/get", metric)
);
+
+ //
endpoints.http.push(
- endpoint.exact("GET", "/1.0/event", eventGet),
- endpoint.exact("GET", "/1.0/event/get", eventGet),
- endpoint.exact("GET", "/1.0/metric", metricGet),
- endpoint.exact("GET", "/1.0/metric/get", metricGet)
+ endpoint("GET", "/1.0/event", eventGet),
+ endpoint("GET", "/1.0/event/get", eventGet),
+ endpoint("GET", "/1.0/metric", metricGet),
+ endpoint("GET", "/1.0/metric/get", metricGet)
);
function eventGet(request, response) {
View
0  lib/cube/server/event-expression.js → lib/cube/event-expression.js
File renamed without changes
View
0  lib/cube/server/event-expression.peg → lib/cube/event-expression.peg
File renamed without changes
View
0  lib/cube/server/event.js → lib/cube/event.js
File renamed without changes
View
12 lib/cube/index.js
@@ -1,7 +1,5 @@
-exports.version = "0.0.9";
-exports.emitter = require("./server/emitter");
-exports.server = require("./server/server");
-exports.collector = require("./server/collector");
-exports.evaluator = require("./server/evaluator");
-exports.visualizer = require("./server/visualizer");
-exports.endpoint = require("./server/endpoint");
+exports.emitter = require("./emitter");
+exports.server = require("./server");
+exports.collector = require("./collector");
+exports.evaluator = require("./evaluator");
+exports.endpoint = require("./endpoint");
View
0  lib/cube/server/metric-expression.js → lib/cube/metric-expression.js
File renamed without changes
View
0  lib/cube/server/metric-expression.peg → lib/cube/metric-expression.peg
File renamed without changes
View
0  lib/cube/server/metric.js → lib/cube/metric.js
File renamed without changes
View
0  lib/cube/server/reduces.js → lib/cube/reduces.js
File renamed without changes
View
15 lib/cube/server/server.js → lib/cube/server.js
@@ -3,6 +3,7 @@ var util = require("util"),
http = require("http"),
websocket = require("websocket"),
websprocket = require("websocket-server"),
+ static = require("node-static"),
mongodb = require("mongodb");
// Don't crash on errors.
@@ -12,7 +13,7 @@ process.on("uncaughtException", function(error) {
});
// And then this happened:
-websprocket.Connection = require("../../../node_modules/websocket-server/lib/ws/connection");
+websprocket.Connection = require("../../node_modules/websocket-server/lib/ws/connection");
// Configuration for WebSocket requests.
var wsOptions = {
@@ -35,6 +36,7 @@ module.exports = function(options) {
var server = {},
primary = http.createServer(),
secondary = websprocket.createServer(),
+ file = new static.Server("static"),
meta,
endpoints = {ws: [], http: []},
mongo = new mongodb.Server(options["mongo-host"], options["mongo-port"], server_options),
@@ -141,8 +143,15 @@ module.exports = function(options) {
}
}
- response.writeHead(404, {"Content-Type": "text/plain"});
- response.end("404 Not Found");
+ // If this request wasn't matched, see if there's a static file to serve.
+ request.on("end", function() {
+ file.serve(request, response, function(error) {
+ if (error && error.status == 404) {
+ response.writeHead(404, {"Content-Type": "text/plain"});
+ response.end("404 Not Found");
+ }
+ });
+ });
});
server.start = function() {
View
41 lib/cube/server/collector.js
@@ -1,41 +0,0 @@
-var endpoint = require("./endpoint"),
- util = require("util");
-
-exports.register = function(db, endpoints) {
- var putter = require("./event").putter(db),
- poster = post(putter);
- endpoints.ws.push(
- endpoint.exact("/1.0/event/put", putter)
- );
- endpoints.http.push(
- endpoint.exact("POST", "/1.0/event", poster),
- endpoint.exact("POST", "/1.0/event/put", poster),
- endpoint.exact("POST", "/collectd", require("./collectd").putter(putter))
- );
-};
-
-function post(putter) {
- return function(request, response) {
- var content = "";
- request.on("data", function(chunk) {
- content += chunk;
- });
- request.on("end", function() {
- try {
- JSON.parse(content).forEach(putter);
- } catch (e) {
- util.log(e);
- response.writeHead(400, {
- "Content-Type": "application/json",
- "Access-Control-Allow-Origin": "*"
- });
- return response.end("{\"status\":400}");
- }
- response.writeHead(200, {
- "Content-Type": "application/json",
- "Access-Control-Allow-Origin": "*"
- });
- response.end("{\"status\":200}");
- });
- };
-}
View
86 lib/cube/server/endpoint.js
@@ -1,86 +0,0 @@
-var fs = require("fs"),
- url = require("url"),
- path = require("path"),
- cube = require("../");
-
-exports.re = re;
-exports.exact = exact;
-exports.file = file;
-
-var types = {
- html: "text/html;charset=utf-8",
- css: "text/css;charset=utf-8",
- js: "text/javascript;charset=utf-8",
- json: "application/json;charset=utf-8",
- png: "image/png"
-};
-
-function exact(method, path, dispatch) {
- return {
- match: arguments.length < 3
- ? (dispatch = path, path = method, function(p) { return p == path; })
- : function(p, m) { return m == method && p == path; },
- dispatch: dispatch
- };
-}
-
-function re(re, dispatch) {
- return {
- match: function(p) { return re.test(p); },
- dispatch: dispatch
- };
-}
-
-function file() {
- var files = Array.prototype.slice.call(arguments),
- type = types[files[0].substring(files[0].lastIndexOf(".") + 1)];
- return function(request, response) {
- var modified = -Infinity,
- size = 0,
- n = files.length;
-
- files.forEach(function(file) {
- fs.stat(file, function(error, stats) {
- if (error) throw fiveohoh(request, response), error;
- size += stats.size;
- var time = new Date(stats.mtime);
- if (time > modified) modified = time;
- if (!--n) respond();
- });
- });
-
- function respond() {
- var status = modified <= new Date(request.headers["if-modified-since"]) ? 304 : 200,
- hasBody = status === 200 && request.method !== "HEAD";
-
- var headers = {
- "Content-Type": type,
- "Date": new Date().toUTCString(),
- "Last-Modified": modified.toUTCString()
- };
-
- if (hasBody) {
- headers["Content-Length"] = size;
- response.writeHead(status, headers);
- read(0);
- } else {
- response.writeHead(status, headers);
- response.end();
- }
- }
-
- function read(i) {
- fs.readFile(files[i], function(error, data) {
- if (error) throw fiveohoh(request, response), error;
- response.write(data);
- if (i < files.length - 1) read(i + 1);
- else response.end();
- });
- }
- };
-};
-
-function fiveohoh(request, response) {
- response.writeHead(500, {"Content-Type": "text/plain"});
- response.end("500 Server Error");
-}
View
191 lib/cube/server/visualizer.js
@@ -1,191 +0,0 @@
-var url = require("url"),
- path = require("path"),
- endpoint = require("./endpoint");
-
-exports.register = function(db, endpoints) {
- endpoints.ws.push(
- endpoint.exact("/board", viewBoard(db))
- );
- endpoints.http.push(
- endpoint.exact("/", createBoard(db)),
- endpoint.re(/^\/[0-9][0-9a-z]{5}(\/edit)?$/, loadBoard(db)),
- endpoint.exact("/cube.js", endpoint.file(
- resolve("start.js"),
- resolve("cube.js"),
- resolve("piece.js"),
- resolve("piece-area.js"),
- resolve("piece-sum.js"),
- resolve("piece-text.js"),
- resolve("palette.js"),
- resolve("squares.js"),
- resolve("board.js"),
- resolve("header.js"),
- resolve("end.js")
- )),
- endpoint.exact("/cube.css", endpoint.file(
- resolve("body.css"),
- resolve("palette.css"),
- resolve("board.css"),
- resolve("piece.css")
- )),
- endpoint.exact("/d3/d3.js", endpoint.file(
- resolve("../../../node_modules/d3/d3.min.js"),
- resolve("semicolon.js"),
- resolve("../../../node_modules/d3/d3.geo.min.js"),
- resolve("semicolon.js"),
- resolve("../../../node_modules/d3/d3.geom.min.js"),
- resolve("semicolon.js"),
- resolve("../../../node_modules/d3/d3.layout.min.js"),
- resolve("semicolon.js"),
- resolve("../../../node_modules/d3/d3.time.min.js")
- ))
- );
-};
-
-function createBoard(db) {
- var boards, max = parseInt("9zzzzy", 36);
-
- db.collection("boards", function(error, collection) {
- boards = collection;
- });
-
- return function random(request, response) {
- var id = (Math.random() * max | 0) + 1;
- boards.insert({_id: id}, {safe: true}, function(error) {
- if (error) {
- if (/^E11000/.test(error.message)) return random(request, response); // duplicate
- response.writeHead(500, {"Content-Type": "text/plain"});
- response.end("500 Server Error");
- } else {
- id = id.toString(36);
- if (id.length < 6) id = new Array(7 - id.length).join("0") + id;
- response.writeHead(302, {"Location": "http://" + request.headers["host"] + "/" + id + "/edit"});
- response.end();
- }
- });
- };
-}
-
-function loadBoard(db) {
- var boards,
- file = endpoint.file(resolve("visualizer.html"));
-
- db.collection("boards", function(error, collection) {
- boards = collection;
- });
-
- return function random(request, response) {
- var id = parseInt(url.parse(request.url).pathname.substring(1), "36");
- boards.findOne({_id: id}, function(error, object) {
- if (object == null) {
- response.writeHead(404, {"Content-Type": "text/plain"});
- response.end("404 Not Found");
- } else {
- file(request, response);
- }
- });
- };
-}
-
-function viewBoard(db) {
- var boards,
- boardsByCallback = {},
- callbacksByBoard = {};
-
- db.collection("boards", function(error, collection) {
- boards = collection;
- });
-
- function dispatch(request, callback) {
- switch (request.type) {
- case "load": load(request, callback); break;
- case "add": add(request, callback); break;
- case "edit": case "move": move(request, callback); break;
- case "remove": remove(request, callback); break;
- default: callback({type: "error", status: 400}); break;
- }
- }
-
- function add(request, callback) {
- var boardId = request.id,
- callbacks = callbacksByBoard[boardId].filter(function(c) { return c.id != callback.id; });
- boards.update({_id: boardId}, {$push: {pieces: request.piece}});
- if (callbacks.length) emit(callbacks, {type: "add", piece: request.piece});
- }
-
- function move(request, callback) {
- var boardId = request.id,
- callbacks = callbacksByBoard[boardId].filter(function(c) { return c.id != callback.id; });
- boards.update({_id: boardId, "pieces.id": request.piece.id}, {$set: {"pieces.$": request.piece}});
- if (callbacks.length) emit(callbacks, {type: request.type, piece: request.piece});
- }
-
- function remove(request, callback) {
- var boardId = request.id,
- callbacks = callbacksByBoard[boardId].filter(function(c) { return c.id != callback.id; });
- boards.update({_id: boardId}, {$pull: {pieces: {id: request.piece.id}}});
- if (callbacks.length) emit(callbacks, {type: "remove", piece: {id: request.piece.id}});
- }
-
- function load(request, callback) {
- var boardId = boardsByCallback[callback.id],
- callbacks;
-
- // If callback was previously viewing to a different board, remove it.
- if (boardId) {
- callbacks = callbacksByBoard[boardId];
- callbacks.splice(callbacks.indexOf(callback), 1);
- if (callbacks.length) emit(callbacks, {type: "view", count: callbacks.length});
- else delete callbacksByBoard[boardId];
- }
-
- // Register that we are now viewing the new board.
- boardsByCallback[callback.id] = boardId = request.id;
-
- // If this board has other viewers, notify them.
- if (boardId in callbacksByBoard) {
- callbacks = callbacksByBoard[boardId];
- callbacks.push(callback);
- emit(callbacks, {type: "view", count: callbacks.length});
- } else {
- callbacks = callbacksByBoard[boardId] = [callback];
- }
-
- // Asynchronously load the requested board.
- boards.findOne({_id: boardId}, function(error, board) {
- if (board != null) {
- if (board.pieces) board.pieces.forEach(function(piece) {
- callback({type: "add", piece: piece});
- });
- } else {
- callback({type: "error", status: 404});
- }
- });
- }
-
- dispatch.close = function(callback) {
- var boardId = boardsByCallback[callback.id],
- callbacks;
-
- // If callback was viewing, remove it.
- if (boardId) {
- callbacks = callbacksByBoard[boardId];
- callbacks.splice(callbacks.indexOf(callback), 1);
- if (callbacks.length) emit(callbacks, {type: "view", count: callbacks.length});
- else delete callbacksByBoard[boardId];
- delete boardsByCallback[callback.id];
- }
- };
-
- return dispatch;
-}
-
-function resolve(file) {
- return path.join(__dirname, "../client", file);
-}
-
-function emit(callbacks, event) {
- callbacks.forEach(function(callback) {
- callback(event);
- });
-}
View
30 lib/cube/server/tiers.js → lib/cube/tiers.js
@@ -5,10 +5,7 @@ var second = 1000,
minute = 60 * second,
minute5 = 5 * minute,
hour = 60 * minute,
- day = 24 * hour,
- week = 7 * day,
- month = 30 * day,
- year = 365 * day;
+ day = 24 * hour;
tiers[second10] = {
key: second10,
@@ -17,6 +14,13 @@ tiers[second10] = {
step: function(d) { return new Date(+d + second10); }
};
+tiers[minute] = {
+ key: minute,
+ floor: function(d) { return new Date(Math.floor(d / minute) * minute); },
+ ceil: tier_ceil,
+ step: function(d) { return new Date(+d + minute); }
+};
+
tiers[minute5] = {
key: minute5,
floor: function(d) { return new Date(Math.floor(d / minute5) * minute5); },
@@ -42,24 +46,6 @@ tiers[day] = {
size: function() { return 24; }
};
-tiers[week] = {
- key: week,
- floor: function(d) { (d = new Date(Math.floor(d / day) * day)).setUTCDate(d.getUTCDate() - d.getUTCDay()); return d; },
- ceil: tier_ceil,
- step: function(d) { return new Date(+d + week); },
- next: tiers[day],
- size: function() { return 7; }
-};
-
-tiers[month] = {
- key: month,
- floor: function(d) { return new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), 1)); },
- ceil: tier_ceil,
- step: function(d) { (d = new Date(d)).setUTCMonth(d.getUTCMonth() + 1); return d; },
- next: tiers[day],
- size: function(d) { return 32 - new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), 32)).getUTCDate(); }
-};
-
function tier_ceil(date) {
return this.step(this.floor(new Date(date - 1)));
}
View
0  lib/cube/server/types.js → lib/cube/types.js
File renamed without changes
View
4 package.json
@@ -1,6 +1,6 @@
{
"name": "cube",
- "version": "0.1.4",
+ "version": "0.2.0",
"description": "A system for time series visualization using MongoDB, Node and D3.",
"keywords": ["time series", "visualization"],
"homepage": "http://square.github.com/cube/",
@@ -8,7 +8,7 @@
"repository": {"type": "git", "url": "http://github.com/square/cube.git"},
"main": "./lib/cube",
"dependencies": {
- "d3": "2.7.2",
+ "d3": "2.9.0",
"mongodb": "0.9.9-7",
"pegjs": "0.6.2",
"vows": "0.5.11",
View
8 test/collector-test.js
@@ -28,7 +28,7 @@ suite.addBatch(test.batch({
topic: test.request({method: "POST", port: port, path: "/1.0/event/put"}, "This ain't JSON.\n"),
"responds with status 400": function(response) {
assert.equal(response.statusCode, 400);
- assert.deepEqual(JSON.parse(response.body), {status: 400});
+ assert.deepEqual(JSON.parse(response.body), {error: "SyntaxError: Unexpected token T"});
}
}
}));
@@ -38,7 +38,7 @@ suite.addBatch(test.batch({
topic: test.request({method: "POST", port: port, path: "/1.0/event/put"}, JSON.stringify(obj)),
"responds with status 400": function(response) {
assert.equal(response.statusCode, 400);
- assert.deepEqual(JSON.parse(response.body), {status: 400});
+ assert.deepEqual(JSON.parse(response.body), {error: "TypeError: Object #<Object> has no method 'forEach'"});
}
}
}));
@@ -48,7 +48,7 @@ suite.addBatch(test.batch({
topic: test.request({method: "POST", port: port, path: "/1.0/event/put"}, JSON.stringify(arr)),
"responds with status 200": function(response) {
assert.equal(response.statusCode, 200);
- assert.deepEqual(JSON.parse(response.body), {status: 200});
+ assert.deepEqual(JSON.parse(response.body), {});
}
}
}));
@@ -58,7 +58,7 @@ suite.addBatch(test.batch({
topic: test.request({method: "POST", port: port, path: "/1.0/event/put"}, JSON.stringify(num)),
"responds with status 400": function(response) {
assert.equal(response.statusCode, 400);
- assert.deepEqual(JSON.parse(response.body), {status: 400});
+ assert.deepEqual(JSON.parse(response.body), {error: "TypeError: Object 42 has no method 'forEach'"});
}
}
}));
View
65 test/endpoint-test.js
@@ -1,65 +0,0 @@
-var vows = require("vows"),
- assert = require("assert"),
- http = require("http"),
- test = require("./test"),
- endpoint = require("../lib/cube/server/endpoint");
-
-var suite = vows.describe("endpoint");
-
-var file = "lib/cube/client/semicolon.js",
- port = ++test.port,
- server = http.createServer(endpoint.file(file, file));
-
-server.listen(port, "127.0.0.1");
-
-suite.addBatch({
- "file": {
- "GET": {
- topic: test.request({method: "GET", port: port}),
- "the status should be 200": function(response) {
- assert.equal(response.statusCode, 200);
- },
- "the expected headers should be set": function(response) {
- assert.equal(response.headers["content-type"], "text/javascript;charset=utf-8");
- assert.equal(response.headers["content-length"], 2);
- assert.ok(new Date(response.headers["date"]) > Date.UTC(2011, 0, 1));
- assert.ok(new Date(response.headers["last-modified"]) > Date.UTC(2011, 0, 1));
- },
- "the expected content should be returned": function(response) {
- assert.equal(response.body, ";;");
- }
- },
- "GET If-Modified-Since": {
- topic: test.request({method: "GET", port: port, headers: {"if-modified-since": new Date(2101, 0, 1).toUTCString()}}),
- "the status should be 304": function(response) {
- assert.equal(response.statusCode, 304);
- },
- "the expected headers should be set": function(response) {
- assert.equal(response.headers["content-type"], "text/javascript;charset=utf-8");
- assert.ok(!("Content-Length" in response.headers));
- assert.ok(new Date(response.headers["date"]) > Date.UTC(2011, 0, 1));
- assert.ok(new Date(response.headers["last-modified"]) > Date.UTC(2011, 0, 1));
- },
- "no content should be returned": function(response) {
- assert.equal(response.body, "");
- }
- },
- "HEAD": {
- topic: test.request({method: "HEAD", port: port, headers: {"if-modified-since": new Date(2001, 0, 1).toUTCString()}}),
- "the status should be 200": function(response) {
- assert.equal(response.statusCode, 200);
- },
- "the expected headers should be set": function(response) {
- assert.equal(response.headers["content-type"], "text/javascript;charset=utf-8");
- assert.ok(!("Content-Length" in response.headers));
- assert.ok(new Date(response.headers["date"]) > Date.UTC(2011, 0, 1));
- assert.ok(new Date(response.headers["last-modified"]) > Date.UTC(2011, 0, 1));
- },
- "no content should be returned": function(response) {
- assert.equal(response.body, "");
- }
- }
- }
-});
-
-suite.export(module);
View
2  test/event-expression-test.js
@@ -1,6 +1,6 @@
var vows = require("vows"),
assert = require("assert"),
- parser = require("../lib/cube/server/event-expression");
+ parser = require("../lib/cube/event-expression");
var suite = vows.describe("event-expression");
View
2  test/metric-expression-test.js
@@ -1,6 +1,6 @@
var vows = require("vows"),
assert = require("assert"),
- parser = require("../lib/cube/server/metric-expression");
+ parser = require("../lib/cube/metric-expression");
var suite = vows.describe("metric-expression");
View
45 test/metric-test.js
@@ -1,24 +1,24 @@