Compile all Coco files, otherwise we don't have Middleware :P middleware
authorDavid Schoonover <dsc@wikimedia.org>
Tue, 10 Jul 2012 13:33:27 +0000 (06:33 -0700)
committerDavid Schoonover <dsc@wikimedia.org>
Tue, 10 Jul 2012 13:33:33 +0000 (06:33 -0700)
23 files changed:
Cokefile
lib/base/asset-manager.js [new file with mode: 0644]
lib/chart/type/d3/d3-bar-chart-type.js [new file with mode: 0644]
lib/chart/type/d3/d3-geo-element.js [new file with mode: 0644]
lib/server/controller.js [new file with mode: 0644]
lib/server/controllers/dashboard.js [new file with mode: 0644]
lib/server/controllers/datasource.js [new file with mode: 0644]
lib/server/controllers/graph.js [new file with mode: 0644]
lib/server/controllers/index.js [new file with mode: 0644]
lib/server/file-controller.js [new file with mode: 0644]
lib/server/files.js [new file with mode: 0644]
lib/server/middleware.js [new file with mode: 0644]
lib/server/mkdirp.js [new file with mode: 0644]
lib/server/proxy.js [new file with mode: 0644]
lib/server/reqinfo.js [new file with mode: 0644]
lib/server/server.js [new file with mode: 0644]
lib/server/view-helpers.js [new file with mode: 0644]
lib/util/aliasdict.js [new file with mode: 0644]
lib/util/bitstring.js [new file with mode: 0644]
lib/util/crc.js [new file with mode: 0644]
lib/util/hashset.js [new file with mode: 0644]
src/util/aliasdict.co
src/util/underscore/_functions.co [deleted file]

index 1b23cd0..4e13b86 100644 (file)
--- a/Cokefile
+++ b/Cokefile
@@ -74,18 +74,13 @@ task \build 'Build coco sources' ->
             """
             @ok()
         
-        .set glob('src/main-*.co', {+sync}).concat sources("www/modules.yaml", 'development').map -> it.slice 1
-        .seqEach (srcfile) ->
-            infile = srcfile.replace /^js\/kraken/, 'src' .replace matchExt, '.co'
+        .set glob 'src/**/*.co', {+sync}
+        .seqEach (infile) ->
             return @ok() unless exists infile
-            # unless exists infile
-            #     console.log "  Skipping Coco compile:\t (#srcfile)\t #infile does not exist"
-            #     return @ok()
-            
-            outfile = srcfile.replace /^(js\/kraken|src)/, 'lib' .replace matchExt, '.js' .replace /\.co$/, '.js'
+            outfile = infile.replace /^src/, 'lib' .replace /\.co$/, '.js'
             console.log "  Compiling Coco to JS:\t #infile \t-->\t #outfile"
             mkdirp dirname outfile
-            write outfile, Coco.compile read(infile), {+bare}
+            write outfile, Coco.compile read(infile), {+bare, filename:infile}
             @ok()
         
         .set sources("www/modules.yaml", 'development').map -> it.slice 1
diff --git a/lib/base/asset-manager.js b/lib/base/asset-manager.js
new file mode 100644 (file)
index 0000000..d65ef1e
--- /dev/null
@@ -0,0 +1,49 @@
+var op, ReadyEmitter, AssetManager, _ref, _;
+_ref = require('kraken/util'), _ = _ref._, op = _ref.op;
+ReadyEmitter = require('kraken/util/event').ReadyEmitter;
+AssetManager = (function(superclass){
+  AssetManager.displayName = 'AssetManager';
+  var prototype = __extend(AssetManager, superclass).prototype, constructor = AssetManager;
+  prototype.assets = null;
+  /**
+   * @constructor
+   */;
+  function AssetManager(){
+    superclass.apply(this, arguments);
+    this.assets = {};
+  }
+  /**
+   * Load the corresponding chart specification, which includes
+   * info about valid options, along with their types and defaults.
+   */
+  prototype.load = function(){
+    var proto, _this = this;
+    if (this.ready) {
+      return this;
+    }
+    proto = this.constructor.prototype;
+    jQuery.ajax({
+      url: this.SPEC_URL,
+      success: function(spec){
+        proto.spec = spec;
+        proto.options_ordered = spec;
+        proto.options = _.synthesize(spec, function(it){
+          return [it.name, it];
+        });
+        proto.ready = true;
+        return _this.emit('ready', _this);
+      },
+      error: function(it){
+        return console.error("Error loading " + _this.typeName + " spec! " + it);
+      }
+    });
+    return this;
+  };
+  return AssetManager;
+}(ReadyEmitter));
+function __extend(sub, sup){
+  function fun(){} fun.prototype = (sub.superclass = sup).prototype;
+  (sub.prototype = new fun).constructor = sub;
+  if (typeof sup.extended == 'function') sup.extended(sub);
+  return sub;
+}
\ No newline at end of file
diff --git a/lib/chart/type/d3/d3-bar-chart-type.js b/lib/chart/type/d3/d3-bar-chart-type.js
new file mode 100644 (file)
index 0000000..fa0746a
--- /dev/null
@@ -0,0 +1,138 @@
+var d3, op, ChartType, root, BarChartType, _ref, _;
+d3 = require('d3');
+_ref = require('kraken/util'), _ = _ref._, op = _ref.op;
+ChartType = require('kraken/chart/chart-type').ChartType;
+root = function(){
+  return this;
+}();
+exports.BarChartType = BarChartType = (function(superclass){
+  BarChartType.displayName = 'BarChartType';
+  var prototype = __extend(BarChartType, superclass).prototype, constructor = BarChartType;
+  prototype.__bind__ = ['determineSize'];
+  prototype.SPEC_URL = '/schema/d3/d3-bar.json';
+  prototype.typeName = 'd3-bar';
+  ChartType.register(BarChartType);
+  /**
+   * Hash of role-names to the selector which, when applied to the view,
+   * returns the correct element.
+   * @type Object
+   */
+  prototype.roles = {
+    viewport: '.viewport',
+    legend: '.graph-legend'
+  };
+  function BarChartType(){
+    superclass.apply(this, arguments);
+  }
+  prototype.getData = function(){
+    return this.model.dataset.getColumns();
+  };
+  prototype.transform = function(){
+    var dataset, options;
+    dataset = this.model.dataset;
+    options = __import(this.model.getOptions(), this.determineSize());
+    __import(options, {
+      colors: dataset.getColors(),
+      labels: dataset.getLabels()
+    });
+    return options;
+  };
+  prototype.renderChartType = function(metric, svgEl, xScale, yScale){
+    var X, Y, metricBars, data, barWidth, barHeight, chT;
+    X = function(d, i){
+      return xScale(d[0]);
+    };
+    Y = function(d, i){
+      return yScale(d[1]);
+    };
+    metricBars = root.metricBars = svgEl.append("g").attr("class", "metric bars " + metric.get('label'));
+    data = d3.zip(metric.getDateColumn(), metric.getData());
+    barWidth = svgEl.attr('width') / data.length;
+    barHeight = function(d){
+      return svgEl.attr('height') - Y(d);
+    };
+    metricBars.selectAll("bar").data(data).enter().append("rect").attr("class", function(d, i){
+      return "metric bar " + i;
+    }).attr("x", X).attr("y", Y).attr("height", barHeight).attr("width", function(){
+      return barWidth;
+    }).attr("fill", metric.get('color')).attr("stroke", "white").style("opacity", "0.4").style("z-index", -10);
+    chT = this;
+    metricBars.selectAll(".metric.bar").on("mouseover", function(d, i){
+      return svgEl.append("text").attr("class", "mf").attr("dx", 50).attr("dy", 100).style("font-size", "0px").transition().duration(800).text("Uh boy, the target would be:  " + chT.numberFormatter(d[1]).toString()).style("font-size", "25px");
+    }).on("mouseout", function(d, i){
+      return svgEl.selectAll(".mf").transition().duration(300).text("BUMMER!!!").style("font-size", "0px").remove();
+    });
+    return svgEl;
+  };
+  prototype.renderChart = function(data, viewport, options, lastChart){
+    var margin, width, height, xScale, yScale, dates, cols, allValues, svg, enterFrame, frame, xAxis, X, Y, barWidth, barHeight, bars, lens, gLens, gInner, mf;
+    margin = {
+      top: 20,
+      right: 20,
+      bottom: 20,
+      left: 20
+    };
+    width = 760;
+    height = 320;
+    xScale = d3.time.scale();
+    yScale = d3.scale.linear();
+    dates = data[0];
+    cols = data.slice(1);
+    allValues = d3.merge(cols);
+    xScale.domain(d3.extent(dates)).range([0, width - margin.left - margin.right]);
+    yScale.domain(d3.extent(allValues)).range([height - margin.top - margin.bottom, 0]);
+    svg = d3.select(viewport[0]).selectAll("svg").data([cols]);
+    enterFrame = svg.enter().append("svg").append("g").attr("class", "frame");
+    enterFrame.append("g").attr("class", "x axis time");
+    svg.attr("width", width).attr("height", height);
+    frame = svg.select("g.frame").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
+    xAxis = d3.svg.axis().scale(xScale).orient("bottom").tickSize(6, 0);
+    frame.select(".x.axis.time").attr("transform", "translate(0," + yScale.range()[0] + ")").call(xAxis);
+    X = function(d, i){
+      return xScale(d[0]);
+    };
+    Y = function(d, i){
+      return yScale(d[1]);
+    };
+    barWidth = svg.attr('width') / dates.length;
+    barHeight = function(d){
+      return svg.attr('height') - Y(d);
+    };
+    bars = frame.selectAll("g.bars").data(cols.map(function(it){
+      return d3.zip(dates, it);
+    }));
+    bars.enter().append("g").attr("class", function(col, i){
+      return "metric bars " + i;
+    });
+    bars.exit().remove();
+    bars.selectAll(".bar").data(op.first).enter().append("rect").attr("class", "bar").attr("x", X).attr("y", Y).attr("height", barHeight).attr("width", function(){
+      return barWidth;
+    }).attr("fill", "red").attr("stroke", "white");
+    lens = root.lens = frame.selectAll("g.lens").data([[]]);
+    gLens = lens.enter().append("g").attr("class", "lens").style("z-index", 1e9);
+    gInner = gLens.append("g").attr("transform", "translate(1.5em,0)");
+    gInner.append("circle").attr("r", "1.5em").style("fill", "rgba(255, 255, 255, 0.4)").style("stroke", "white").style("stroke-width", "3px");
+    gInner.append("text").attr("y", "0.5em").attr("text-anchor", "middle").style("fill", "white").style("font", "12px Helvetica").style("font-weight", "bold");
+    mf = frame.selectAll("g.mf").data(["mf"]).enter().append("g").attr("class", "mf").append("text").attr("class", "yoyo").attr("dx", 50).attr("dy", 100);
+    bars.selectAll(".bar").on("mouseover", function(d, i){
+      var el;
+      el = root.el = el;
+      return mf.transition().duration(300).ease("exp").text("Uh boy, the target would be:" + d[1]).style("font-size", "25px");
+    }).on("mouseout", function(d, i){
+      return mf.transition().duration(1000).text("BUMMER!!!").style("font-size", "0px");
+    });
+    return svg;
+  };
+  return BarChartType;
+}(ChartType));
+function __extend(sub, sup){
+  function fun(){} fun.prototype = (sub.superclass = sup).prototype;
+  (sub.prototype = new fun).constructor = sub;
+  if (typeof sup.extended == 'function') sup.extended(sub);
+  return sub;
+}
+function __import(obj, src){
+  var own = {}.hasOwnProperty;
+  for (var key in src) if (own.call(src, key)) obj[key] = src[key];
+  return obj;
+}
\ No newline at end of file
diff --git a/lib/chart/type/d3/d3-geo-element.js b/lib/chart/type/d3/d3-geo-element.js
new file mode 100644 (file)
index 0000000..64ddb6c
--- /dev/null
@@ -0,0 +1,131 @@
+var ColorBrewer, op, ChartType, GeoWorldChartType, data, main, _ref, _;
+ColorBrewer = require('colorbrewer');
+_ref = require('kraken/util'), _ = _ref._, op = _ref.op;
+ChartType = require('kraken/chart/chart-type').ChartType;
+exports.GeoWorldChartType = GeoWorldChartType = (function(superclass){
+  GeoWorldChartType.displayName = 'GeoWorldChartType';
+  var prototype = __extend(GeoWorldChartType, superclass).prototype, constructor = GeoWorldChartType;
+  prototype.__bind__ = ['dygNumberFormatter', 'dygNumberFormatterHTML'];
+  prototype.SPEC_URL = '/schema/d3/d3-geo-world.json';
+  prototype.typeName = 'd3-geo-world';
+  ChartType.register(GeoWorldChartType);
+  /**
+   * Hash of role-names to the selector which, when applied to the view,
+   * returns the correct element.
+   * @type Object
+   */
+  prototype.roles = {
+    viewport: '.viewport',
+    legend: '.graph-legend'
+  };
+  function GeoWorldChartType(){
+    superclass.apply(this, arguments);
+  }
+  prototype.transform = function(){
+    var options;
+    options = __import(this.model.getOptions(), this.determineSize());
+    if (options.colors.scaleDomain != null) {
+      options.colors.scaleDomain = d3.extent;
+    }
+    return options;
+  };
+  prototype.getProjection = function(type){
+    switch (type) {
+    case 'mercator':
+    case 'albers':
+    case 'albersUsa':
+      return d3.geo[type]();
+    case 'azimuthalOrtho':
+      return d3.geo.azimuthal().mode('orthographic');
+    case 'azimuthalStereo':
+      return d3.geo.azimuthal().mode('stereographic');
+    default:
+      throw new Error("Invalid map projection type '" + type + "'!");
+    }
+  };
+  prototype.renderChart = function(data, viewport, options, lastChart){
+    var width, height, fill, quantize, projection, path, feature, infobox, move, zoom, chart, setInfoBox, worldmap;
+    width = options.width, height = options.height;
+    fill = this.fill = function(data, options){
+      return d3.scale[options.colors.scale]().domain(options.colors.scaleDomain).range(options.colors.palette);
+    };
+    quantize = this.quantize = function(data, options){
+      return function(d){
+        if (data[d.properties.name] != null) {
+          return fill(data[d.properties.name].editors);
+        } else {
+          return fill("rgb(0,0,0)");
+        }
+      };
+    };
+    projection = this.projection = this.getProjection(options.map.projection).scale(width).translate([width / 2, height / 2]);
+    path = d3.geo.path().projection(projection);
+    feature = map.selectAll(".feature");
+    infobox = d3.select('.infobox');
+    move = function(){
+      projection.translate(d3.event.translate).scale(d3.event.scale);
+      return feature.attr("d", path);
+    };
+    zoom = d3.behavior.zoom().translate(projection.translate()).scale(projection.scale()).scaleExtent([height, height * 8]).on("zoom", move);
+    chart = d3.select(viewport[0]).append("svg:svg").attr("width", width).attr("height", height).append("svg:g").attr("transform", "translate(0,0)").call(zoom);
+    map.append("svg:rect").attr("class", "frame").attr("width", width).attr("height", height);
+    infobox.select('#ball').append("svg:svg").attr("width", "100%").attr("height", "20px").append("svg:rect").attr("width", "60%").attr("height", "20px").attr("fill", '#f40500');
+    setInfoBox = function(d){
+      var name, ae, e5, e100, xy;
+      name = d.properties.name;
+      ae = 0;
+      e5 = 0;
+      e100 = 0;
+      if (data[name] != null) {
+        ae = parseInt(data[name].editors);
+        e5 = parseInt(data[name].editors5);
+        e100 = parseInt(data[name].editors100);
+      }
+      infobox.select('#country').text(name);
+      infobox.select('#ae').text(ae);
+      infobox.select('#e5').text(e5 + " (" + (100.0 * e5 / ae).toPrecision(3) + "%)");
+      infobox.select('#e100').text(e100 + " (" + (100.0 * e100 / ae).toPrecision(3) + "%)");
+      xy = d3.svg.mouse(this);
+      infobox.style("left", xy[0] + 'px');
+      infobox.style("top", xy[1] + 'px');
+      return infobox.style("display", "block");
+    };
+    return worldmap = function(){
+      return d3.json("/data/geo/maps/world-countries.json", function(json){
+        return feature = feature.data(json.features).enter().append("svg:path").attr("class", "feature").attr("d", path).attr("fill", quantize).attr("id", function(d){
+          return d.properties.name;
+        }).on("mouseover", setInfoBox).on("mouseout", function(){
+          return infobox.style("display", "none");
+        });
+      });
+    };
+  };
+  return GeoWorldChartType;
+}(ChartType));
+data = null;
+main = function(){
+  return jQuery.ajax({
+    url: "/data/geo/data/en_geo_editors.json",
+    dataType: 'json',
+    success: function(res){
+      data = res;
+      jQuery('.geo-spinner').spin(false).hide();
+      worldmap();
+      return console.log('Loaded geo coding map!');
+    },
+    error: function(err){
+      return console.error(err);
+    }
+  });
+};
+function __extend(sub, sup){
+  function fun(){} fun.prototype = (sub.superclass = sup).prototype;
+  (sub.prototype = new fun).constructor = sub;
+  if (typeof sup.extended == 'function') sup.extended(sub);
+  return sub;
+}
+function __import(obj, src){
+  var own = {}.hasOwnProperty;
+  for (var key in src) if (own.call(src, key)) obj[key] = src[key];
+  return obj;
+}
\ No newline at end of file
diff --git a/lib/server/controller.js b/lib/server/controller.js
new file mode 100644 (file)
index 0000000..2c0f556
--- /dev/null
@@ -0,0 +1,117 @@
+var express, Resource, hasOwn, Controller, exports;
+express = require('express');
+Resource = require('express-resource');
+hasOwn = Object.prototype.hasOwnProperty;
+/**
+ * @class Resource controller for easily subclassing an express Resource.
+ */
+Controller = (function(superclass){
+  Controller.displayName = 'Controller';
+  var prototype = __extend(Controller, superclass).prototype, constructor = Controller;
+  /**
+   * Singular, lowercase resource-noun.
+   * @optional
+   * @type String
+   * @example "user"
+   */
+  prototype.id = null;
+  /**
+   * Plural, lowercase resource-noun.
+   * @required
+   * @type String
+   * @example "users"
+   */
+  prototype.name = null;
+  /**
+   * Resource routing prefix.
+   * @optional
+   * @type String
+   */
+  prototype.base = '/';
+  /**
+   * Default format.
+   * @type String
+   */
+  prototype.format = null;
+  /**
+   * Hash of sub-routes. Keys are routes, and values are either:
+   *  - String: the name of a method to be used for used for all HTTP-methods
+   *    for this sub-route.
+   *  - Object: Hash of HTTP-method (get, put, post, del, or all) to the name
+   *    of a method on this Controller.
+   * 
+   * Example:
+   *      { '/foo' => 'foo',
+   *        '/bar' => {'get' => 'get_bar', 'del' => 'delete_bar' },
+   *      }
+   *      If this mapping is in a controller with name 'nonya', then
+   *          GET     '/nonya/foo' -> NonyaController.foo(),
+   *          GET     '/nonya/bar' -> NonyaController.get_bar()
+   *          DELETE  '/nonya/bar' -> NonyaController.delete_bar()
+   * 
+   * @type Object
+