"""
@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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
+ */
+ prototype.mapping = null;
+ /**
+ * @constructor
+ */;
+ function Controller(app, name){
+ var actions, k, fn, _ref;
+ this.app = app;
+ this.routes || (this.routes = {});
+ actions = {};
+ for (k in this) {
+ fn = this[k];
+ if (!(typeof fn === 'function' && k !== 'constructor')) {
+ continue;
+ }
+ actions[k] = this[k] = fn.bind(this);
+ }
+ delete actions.load;
+ if (typeof actions.autoload === 'function') {
+ actions.load = actions.autoload;
+ }
+ ((_ref = this.app).resources || (_ref.resources = {}))[this.name] = this;
+ this.applyControllerMapping();
+ superclass.call(this, name || this.name, actions, this.app);
+ }
+ /**
+ * Apply the contents of a mapping hash.
+ * @private
+ */
+ prototype.applyControllerMapping = function(mapping){
+ var subroute, methods, verb, method;
+ mapping == null && (mapping = this.mapping);
+ for (subroute in mapping) {
+ methods = mapping[subroute];
+ if (typeof methods === 'string') {
+ methods = {
+ all: methods
+ };
+ }
+ for (verb in methods) {
+ method = methods[verb];
+ this.map(verb, subroute, this[method]);
+ }
+ }
+ return this;
+ };
+ prototype.toString = function(){
+ return this.constructor.name + "('" + this.name + "', base='" + this.base + "', app=" + this.app + ")";
+ };
+ return Controller;
+}(Resource));
+express.HTTPServer.prototype.controller = express.HTTPSServer.prototype.controller = function(name, ControllerClass, opts){
+ var _ref;
+ if (typeof name === 'function') {
+ _ref = [ControllerClass, name, null], opts = _ref[0], ControllerClass = _ref[1], name = _ref[2];
+ }
+ return new ControllerClass(this, name);
+};
+module.exports = exports = Controller;
+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
--- /dev/null
+var fs, path, exists, Seq, yaml, mkdirp, mkdirpAsync, readJSONFilesAsync, FileBackedController, DashboardController, exports, _, _ref;
+fs = require('fs');
+path = require('path');
+exists = path.existsSync;
+_ = require('underscore');
+Seq = require('seq');
+yaml = require('js-yaml');
+_ref = require('../mkdirp'), mkdirp = _ref.mkdirp, mkdirpAsync = _ref.mkdirpAsync;
+readJSONFilesAsync = require('../files').readJSONFilesAsync;
+FileBackedController = require('../file-controller');
+/**
+ * @class Resource controller for dashboard requests.
+ */
+DashboardController = (function(superclass){
+ DashboardController.displayName = 'DashboardController';
+ var prototype = __extend(DashboardController, superclass).prototype, constructor = DashboardController;
+ prototype.PROTECTED_IDS = ['main', 'reportcard'];
+ prototype.PROTECT = true;
+ prototype.name = 'dashboards';
+ prototype.dataDir = 'data/dashboards';
+ function DashboardController(){
+ superclass.apply(this, arguments);
+ }
+ prototype.index = function(req, res){
+ var pattern;
+ switch (req.format) {
+ case 'json':
+ pattern = this.dataDir + "/*.json";
+ return Seq().seq(function(){
+ return readJSONFilesAsync(pattern, this);
+ }).seq(function(files){
+ return res.send(_.values(files));
+ });
+ default:
+ return res.render(this.id + "/index");
+ }
+ };
+ prototype.show = function(req, res){
+ if (req.format === 'json') {
+ return res.send(req.dashboard);
+ } else {
+ return res.render(this.id + "/view");
+ }
+ };
+ prototype.edit = function(req, res){
+ if (req.format === 'json') {
+ return res.send(req.dashboard);
+ } else {
+ return res.render(this.id + "/edit");
+ }
+ };
+ prototype['new'] = function(req, res){
+ return res.render(this.id + "/edit");
+ };
+ prototype.create = function(req, res){
+ var data, file;
+ if (!(data = this.processBody(req, res))) {
+ return;
+ }
+ file = this.toFile(data.id);
+ if (exists(file)) {
+ return res.send({
+ result: "error",
+ message: "Dashboard '" + data.id + "' already exists!"
+ });
+ } else {
+ return fs.writeFile(file, JSON.stringify(data), "utf8", this.errorHandler(res, "Error writing Dashboard!"));
+ }
+ };
+ prototype.update = function(req, res){
+ var data;
+ if (!(data = this.processBody(req, res))) {
+ return;
+ }
+ if (this.PROTECT && _(this.PROTECTED_IDS).contains(data.id)) {
+ return res.send({
+ result: "error",
+ message: "Dashboard '" + data.id + "' is read-only."
+ }, 403);
+ }
+ return fs.writeFile(this.toFile(data.id), JSON.stringify(data), "utf8", this.errorHandler(res, "Error writing Dashboard!"));
+ };
+ prototype.destroy = function(req, res){
+ var id;
+ id = req.param.dashboard;
+ if (this.PROTECT && _(this.PROTECTED_IDS).contains(id)) {
+ return res.send({
+ result: "error",
+ message: "Dashboard '" + id + "' is read-only."
+ }, 403);
+ }
+ return fs.unlink(this.toFile(id), this.errorHandler(res, "Dashboard '" + id + "' does not exist!"));
+ };
+ return DashboardController;
+}(FileBackedController));
+module.exports = exports = DashboardController;
+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
--- /dev/null
+var fs, path, exists, yaml, findit, Seq, Controller, EXT_PAT, YAML_EXT_PAT, YAML_OR_JSON_PAT, DataSourceController, exports, _;
+fs = require('fs');
+path = require('path');
+exists = path.existsSync;
+_ = require('underscore');
+yaml = require('js-yaml');
+findit = require('findit');
+Seq = require('seq');
+Controller = require('../controller');
+EXT_PAT = /\.[^\.]*$/i;
+YAML_EXT_PAT = /\.ya?ml$/i;
+YAML_OR_JSON_PAT = /\.(json|ya?ml)$/i;
+/**
+ * @class Resource controller for graph requests.
+ */
+DataSourceController = (function(superclass){
+ DataSourceController.displayName = 'DataSourceController';
+ var prototype = __extend(DataSourceController, superclass).prototype, constructor = DataSourceController;
+ prototype.name = 'datasources';
+ prototype.dataDir = 'data/datasources';
+ prototype.mapping = {
+ all: 'allData'
+ };
+ function DataSourceController(){
+ superclass.apply(this, arguments);
+ }
+ prototype.toFile = function(id){
+ return this.dataDir + "/" + id + ".json";
+ };
+ /**
+ * Auto-load :id for related requests.
+ */
+ prototype.autoload = function(id, cb){
+ var files, pattern, file, parser;
+ files = findit.sync(this.dataDir);
+ pattern = new RegExp(id + ".(json|ya?ml)$", "i");
+ file = _.find(files, function(it){
+ return pattern.test(it);
+ });
+ if (!file) {
+ console.error("Unable to find DataSource for '" + id + "'!");
+ return cb(new Error("Unable to find DataSource for '" + id + "'!"));
+ }
+ if (_.endsWith(file, id + ".json")) {
+ parser = JSON.parse;
+ }
+ if (_.endsWith(file, id + ".yaml")) {
+ parser = yaml.load;
+ }
+ return fs.readFile(file, 'utf8', function(err, data){
+ if ('ENOENT' === (err != null ? err.code : void 8)) {
+ console.error("Unable to find DataSource for '" + id + "'!");
+ return cb(new Error("Unable to find DataSource for '" + id + "'!"));
+ }
+ if (err) {
+ console.error("DataSourceController.autoload(" + id + ", " + typeof cb + ") -->\n", err);
+ return cb(err);
+ }
+ try {
+ return cb(null, parser(data));
+ } catch (err) {
+ console.error("DataSourceController.autoload(" + id + ", " + typeof cb + ") -->\n", err);
+ return cb(err);
+ }
+ });
+ };
+ /**
+ * GET /datasources
+ * @returns {Object} JSON listing of the datasource metadata files.
+ */
+ prototype.index = function(req, res, next){
+ var files;
+ files = findit.sync(this.dataDir);
+ return res.send(files.filter(function(it){
+ return YAML_OR_JSON_PAT.test(it);
+ }).map(function(it){
+ return (it + "").replace(YAML_EXT_PAT, '.json');
+ }));
+ };
+ /**
+ * GET /datasources/:datasource
+ */
+ prototype.show = function(req, res){
+ return res.send(req.datasource);
+ };
+ /**
+ * Returns the aggregated JSON content of the datasource metadata files.
+ */
+ prototype.allData = function(req, res, next){
+ var data, files, _this = this;
+ data = {};
+ files = [];
+ return Seq(findit.sync(this.dataDir)).filter(function(it){
+ return YAML_OR_JSON_PAT.test(it);
+ }).seq(function(){
+ files = this.stack.slice();
+ return this.ok(files);
+ }).flatten().parMap_(function(next, f){
+ return fs.readFile(f, 'utf8', next);
+ }).parMap(function(text, i){
+ var f, k, v, that;
+ f = files[i];
+ k = f.replace(YAML_EXT_PAT, '.json');
+ v = data[k] = {};
+ try {
+ if (YAML_EXT_PAT.test(f)) {
+ v = data[k] = yaml.load(text);
+ } else {
+ v = data[k] = JSON.parse(text);
+ }
+ return this.ok(v);
+ } catch (err) {
+ console.error("[/datasources] catch! " + err);
+ console.error(err);
+ if (that = err.stack) {
+ console.error(that);
+ }
+ return res.send({
+ error: String(err),
+ partial_data: data
+ });
+ }
+ }).seq(function(){
+ return res.send(data);
+ })['catch'](function(err){
+ var that;
+ console.error('[/datasources] catch!');
+ console.error(err);
+ if (that = err.stack) {
+ console.error(that);
+ }
+ return res.send({
+ error: String(err),
+ partial_data: data
+ });
+ });
+ };
+ return DataSourceController;
+}(Controller));
+module.exports = exports = DataSourceController;
+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
--- /dev/null
+var fs, path, exists, Seq, yaml, mkdirp, mkdirpAsync, readJSONFilesAsync, Controller, GraphController, exports, _, _ref;
+fs = require('fs');
+path = require('path');
+exists = path.existsSync;
+_ = require('underscore');
+Seq = require('seq');
+yaml = require('js-yaml');
+_ref = require('../mkdirp'), mkdirp = _ref.mkdirp, mkdirpAsync = _ref.mkdirpAsync;
+readJSONFilesAsync = require('../files').readJSONFilesAsync;
+Controller = require('../controller');
+/**
+ * @class Resource controller for graph requests.
+ */
+GraphController = (function(superclass){
+ GraphController.displayName = 'GraphController';
+ var prototype = __extend(GraphController, superclass).prototype, constructor = GraphController;
+ prototype.PROTECTED_GRAPH_IDS = ['unique_visitors', 'pageviews', 'pageviews_mobile', 'reach', 'commons', 'articles', 'articles_per_day', 'edits', 'new_editors', 'active_editors', 'active_editors_target', 'very_active_editors'];
+ prototype.PROTECT_GRAPHS = true;
+ prototype.name = 'graphs';
+ prototype.dataDir = 'data/graphs';
+ function GraphController(){
+ superclass.apply(this, arguments);
+ }
+ prototype.toFile = function(id){
+ return this.dataDir + "/" + id + ".json";
+ };
+ /**
+ * Auto-load :id for related requests.
+ */
+ prototype.autoload = function(id, cb){
+ var file, parser, yamlFile;
+ file = this.toFile(id);
+ parser = JSON.parse;
+ yamlFile = file.replace(/\.json$/i, '.yaml');
+ if (exists(yamlFile)) {
+ file = yamlFile;
+ parser = yaml.load;
+ }
+ return fs.readFile(file, 'utf8', function(err, data){
+ if ('ENOENT' === (err != null ? err.code : void 8)) {
+ return cb(null, {});
+ }
+ if (err) {
+ console.error("GraphController.autoload(" + id + ", " + typeof cb + ") -->\nerr");
+ return cb(err);
+ }
+ try {
+ return cb(null, parser(data));
+ } catch (err) {
+ console.error("GraphController.autoload(" + id + ", " + typeof cb + ") -->\nerr");
+ return cb(err);
+ }
+ });
+ };
+ prototype.index = function(req, res){
+ var pattern;
+ switch (req.format) {
+ case 'json':
+ pattern = this.dataDir + "/*.json";
+ return Seq().seq(function(){
+ return readJSONFilesAsync(pattern, this);
+ }).seq(function(graphs){
+ return res.send(_.values(graphs));
+ });
+ default:
+ return res.render('graph/index');
+ }
+ };
+ prototype.show = function(req, res){
+ if (req.format === 'json') {
+ return res.send(req.graph);
+ } else {
+ return res.render('graph/view');
+ }
+ };
+ prototype.edit = function(req, res){
+ if (req.format === 'json') {
+ return res.send(req.graph);
+ } else {
+ return res.render('graph/edit');
+ }
+ };
+ prototype['new'] = function(req, res){
+ return res.render('graph/edit');
+ };
+ prototype.create = function(req, res){
+ var data, file;
+ if (!(data = this.processBody(req, res))) {
+ return;
+ }
+ file = this.toFile(data.id);
+ if (exists(file)) {
+ return res.send({
+ result: "error",
+ message: "Graph '" + data.id + "' already exists!"
+ });
+ } else {
+ return fs.writeFile(file, JSON.stringify(data), "utf8", this.errorHandler(res, "Error writing graph!"));
+ }
+ };
+ prototype.update = function(req, res){
+ var data;
+ if (!(data = this.processBody(req, res))) {
+ return;
+ }
+ if (this.PROTECT_GRAPHS && _(this.PROTECTED_GRAPH_IDS).contains(data.id)) {
+ return res.send({
+ result: "error",
+ message: "Graph '" + data.id + "' is read-only."
+ }, 403);
+ }
+ return fs.writeFile(this.toFile(data.id), JSON.stringify(data), "utf8", this.errorHandler(res, "Error writing graph!"));
+ };
+ prototype.destroy = function(req, res){
+ var id;
+ id = req.param.graph;
+ if (this.PROTECT_GRAPHS && _(this.PROTECTED_GRAPH_IDS).contains(id)) {
+ return res.send({
+ result: "error",
+ message: "Graph '" + id + "' is read-only."
+ }, 403);
+ }
+ return fs.unlink(this.toFile(id), this.errorHandler(res, "Graph '" + id + "' does not exist!"));
+ };
+ prototype.processBody = function(req, res){
+ var data;
+ if (!req.body) {
+ res.send({
+ result: "error",
+ message: "Data required!"
+ }, 501);
+ return false;
+ }
+ data = req.body;
+ data.slug || (data.slug = data.id);
+ data.id || (data.id = data.slug);
+ if (!data.slug) {
+ res.send({
+ result: "error",
+ message: "Slug required!"
+ }, 501);
+ return false;
+ }
+ if (!exists(this.dataDir)) {
+ mkdirp(this.dataDir);
+ }
+ return data;
+ };
+ prototype.errorHandler = function(res, msg){
+ return function(err){
+ var msg;
+ if (err) {
+ msg || (msg = err.message || String(err));
+ console.error(msg);
+ return res.send({
+ result: "error",
+ message: msg
+ }, 501);
+ } else {
+ return res.send({
+ result: "ok"
+ });
+ }
+ };
+ };
+ return GraphController;
+}(Controller));
+module.exports = exports = GraphController;
+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
--- /dev/null
+var fs, path, exists, Seq, yaml, mkdirp, mkdirpAsync, readJSONFilesAsync, Controller, FileBackedController, exports, _, _ref;
+fs = require('fs');
+path = require('path');
+exists = path.existsSync;
+_ = require('underscore');
+Seq = require('seq');
+yaml = require('js-yaml');
+_ref = require('./mkdirp'), mkdirp = _ref.mkdirp, mkdirpAsync = _ref.mkdirpAsync;
+readJSONFilesAsync = require('./files').readJSONFilesAsync;
+Controller = require('./controller');
+/**
+ * @class Resource controller backed by flat json or yaml files.
+ */
+FileBackedController = (function(superclass){
+ FileBackedController.displayName = 'FileBackedController';
+ var prototype = __extend(FileBackedController, superclass).prototype, constructor = FileBackedController;
+ prototype.name = null;
+ prototype.dataDir = null;
+ function FileBackedController(){
+ this.dataDir || (this.dataDir = "data/" + this.name);
+ superclass.apply(this, arguments);
+ }
+ /**
+ * Override to customize lookup of files by ID.
+ *
+ * @param {String} id ID of this resource.
+ * @returns {String} Path to file for this resource.
+ */
+ prototype.toFile = function(id){
+ return this.dataDir + "/" + id + ".json";
+ };
+ /**
+ * Auto-load :id for related requests.
+ *
+ * @param {String} id ID of the resource.
+ * @param {Function} cb Callback to invoke with the loaded object.
+ */
+ prototype.autoload = function(id, cb){
+ var file, parser, yamlFile;
+ file = this.toFile(id);
+ parser = JSON.parse;
+ yamlFile = file.replace(/\.json$/i, '.yaml');
+ if (exists(yamlFile)) {
+ file = yamlFile;
+ parser = yaml.load;
+ }
+ return fs.readFile(file, 'utf8', function(err, data){
+ if ('ENOENT' === (err != null ? err.code : void 8)) {
+ return cb(null, {});
+ }
+ if (err) {
+ console.error(this + ".autoload(" + id + ", " + typeof cb + ") -->\nerr");
+ return cb(err);
+ }
+ try {
+ return cb(null, parser(data));
+ } catch (err) {
+ console.error(this + ".autoload(" + id + ", " + typeof cb + ") -->\nerr");
+ return cb(err);
+ }
+ });
+ };
+ prototype.processBody = function(req, res){
+ var data;
+ if (!req.body) {
+ res.send({
+ result: "error",
+ message: "Data required!"
+ }, 501);
+ return false;
+ }
+ data = req.body;
+ data.slug || (data.slug = data.id);
+ data.id || (data.id = data.slug);
+ if (!data.slug) {
+ res.send({
+ result: "error",
+ message: "Slug required!"
+ }, 501);
+ return false;
+ }
+ if (!exists(this.dataDir)) {
+ mkdirp(this.dataDir);
+ }
+ return data;
+ };
+ prototype.errorHandler = function(res, msg){
+ return function(err){
+ var msg;
+ if (err) {
+ msg || (msg = err.message || String(err));
+ console.error(msg);
+ return res.send({
+ result: "error",
+ message: msg
+ }, 501);
+ } else {
+ return res.send({
+ result: "ok"
+ });
+ }
+ };
+ };
+ return FileBackedController;
+}(Controller));
+module.exports = exports = FileBackedController;
+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
--- /dev/null
+/**
+ * @fileOverview Filesystem utilities.
+ */
+var fs, path, Seq, glob, readFilesAsync, readJSONFilesAsync, logErrorsAnd, files, u, paths, _, __slice = [].slice;
+fs = require('fs');
+path = require('path');
+_ = require('underscore');
+Seq = require('seq');
+glob = require('glob');
+/**
+ * Asynchronously reads the text for each filepath produced by the
+ * globs supplied, returning a map from filepath to contents.
+ *
+ * @param {String|Array<String>} patterns List of file-paths and/or glob-patterns to read.
+ * @param {Function} cb Callback taking `(error, data)` where `data` is a map
+ * from filepath to contents. As always, `error` will be null on success.
+ * @returns {Seq} The Seq object representing the async operation chain. (You
+ * can usually ignore this.)
+ */
+readFilesAsync = exports.readFilesAsync = function(patterns, cb){
+ var files, data;
+ if (typeof patterns === 'string') {
+ patterns = [patterns];
+ }
+ files = [];
+ data = {};
+ return Seq(patterns).parMap(function(pat){
+ return glob(pat, {
+ nocase: true,
+ nosort: true
+ }, this);
+ }).flatten().parMap(function(f){
+ files.push(f);
+ return fs.readFile(f, 'utf8', this);
+ }).parEach(function(text, i){
+ var f;
+ f = files[i];
+ data[f] = text;
+ return this.ok();
+ }).seq(function(){
+ return cb(null, data);
+ })['catch'](function(err){
+ console.error(err.file, err);
+ return cb(err);
+ });
+};
+/**
+ * Asynchronously reads text and parses JSON for each filepath produced by the
+ * globs supplied, returning a map from filepath to contents.
+ *
+ * @param {String|Array<String>} patterns List of filepaths and/or glob-patterns to read.
+ * @param {Function} cb Callback taking `(error, data)` where `data` is a map
+ * from filepath to contents. As always, `error` will be null on success.
+ * @returns {Seq} The Seq object representing the async operation chain. (You
+ * can usually ignore this.)
+ */
+readJSONFilesAsync = exports.readJSONFilesAsync = function(patterns, cb){
+ var data;
+ data = {};
+ return Seq().seq(function(){
+ return readFilesAsync(patterns, this);
+ }).seq(function(data){
+ return this.ok(_.map(data, function(text, f){
+ return [f, text];
+ }));
+ }).flatten(false).parMap(function(_arg){
+ var f, text;
+ f = _arg[0], text = _arg[1];
+ try {
+ data[f] = JSON.parse(text);
+ return this.ok();
+ } catch (err) {
+ err.file = f;
+ console.error(f, err);
+ return cb(err);
+ }
+ }).seq(function(){
+ return cb(null, data);
+ })['catch'](function(err){
+ console.error(err.file, err);
+ return cb(err);
+ });
+};
+logErrorsAnd = exports.logErrorsAnd = function(cb){
+ return function(err){
+ var args;
+ args = __slice.call(arguments, 1);
+ global.args = arguments;
+ if (err) {
+ return console.error(err);
+ } else {
+ if (cb) {
+ return cb.apply(null, args);
+ }
+ }
+ };
+};
+if (require.main === module) {
+ files = exports;
+ u = require('kraken/util/underscore');
+ paths = ['package.*', 'deploy.sh'];
+ files.readFilesAsync(paths, function(err, data){
+ if (err) {
+ return console.error(err);
+ } else {
+ return console.log('\n\n', global.data = u.map(data, function(txt, f){
+ return f + ": " + txt.length;
+ }));
+ }
+ });
+}
\ No newline at end of file
--- /dev/null
+var exists, fs, dirname, path, exec, spawn, subproc, glob, yaml, Seq, express, op, mkdirp, mkdirpAsync, readFilesAsync, Controller, BASE, DATA, LIB, SRC, STATIC, VAR, WWW, NODE_ENV, IS_DEV, IS_PROD, LOG_LEVEL, REV, DEFAULT_OPTIONS, exports, application, _ref, _;
+fs = (_ref = require('fs'), exists = _ref.existsSync, _ref);
+path = (_ref = require('path'), dirname = _ref.dirname, _ref);
+subproc = (_ref = require('child_process'), exec = _ref.exec, spawn = _ref.spawn, _ref);
+glob = require('glob');
+yaml = require('js-yaml');
+Seq = require('seq');
+express = require('express');
+_ref = require('../util'), _ = _ref._, op = _ref.op;
+_ref = require('./mkdirp'), mkdirp = _ref.mkdirp, mkdirpAsync = _ref.mkdirpAsync;
+readFilesAsync = require('./files').readFilesAsync;
+Controller = require('./controller');
+/**
+ * Limn project-internals
+ */
+BASE = dirname(dirname(__dirname));
+DATA = BASE + "/data";
+LIB = BASE + "/llb";
+SRC = BASE + "/src";
+STATIC = BASE + "/static";
+VAR = BASE + "/var";
+WWW = BASE + "/www";
+NODE_ENV = process.env.NODE_ENV || 'development';
+IS_DEV = NODE_ENV === 'development';
+IS_PROD = NODE_ENV === 'production';
+LOG_LEVEL = process.env.KRAKEN_LOG_LEVEL;
+LOG_LEVEL || (LOG_LEVEL = IS_DEV ? 'INFO' : 'WARN');
+LOG_LEVEL = LOG_LEVEL.toUpperCase();
+REV = process.env.KRAKEN_REV || 'HEAD';
+try {
+ REV = require('../version');
+} catch (e) {}
+DEFAULT_OPTIONS = {
+ dataDir: './data',
+ proxy: {
+ enabled: false,
+ whitelist: null,
+ blacklist: null
+ }
+};
+exports = module.exports = limn;
+/**
+ * Create a new instance of the Limn middleware.
+ * @param {Object} [options={}] Options:
+ */
+function limn(options){
+ var app;
+ app = express.createServer();
+ app = _.extend(app, application);
+ app.init();
+ return app;
+}
+application = limn.application = {
+ /**
+ * @constructor
+ */
+ init: function(opts){
+ var opx, YAML_EXT_PAT, proxy;
+ opts == null && (opts = {});
+ this.REV = REV;
+ this.BASE = BASE;
+ opts = _.merge({}, DEFAULT_OPTIONS, opts);
+ opx = opts.proxy;
+ if (opx.enabled === false && (opx.whitelist || opx.blacklist)) {
+ opx.enabled = true;
+ }
+ if (opx.enabled) {
+ opx.whitelist || (opx.whitelist = [/.*/]);
+ opx.blacklist || (opx.blacklist = []);
+ }
+ this.set('limn options', opts);
+ this.configure(function(){
+ this.set('views', WWW);
+ this.set('view engine', 'jade');
+ this.set('view options', __import({
+ layout: false,
+ version: REV,
+ IS_DEV: IS_DEV,
+ IS_PROD: IS_PROD
+ }, require('./view-helpers')));
+ this.use(require('./reqinfo')({}));
+ this.use(express.bodyParser());
+ this.use(express.methodOverride());
+ this.use(this.router);
+ return this.use(require('browserify')({
+ mount: '/vendor/browserify.js',
+ require: ['seq', 'd3', 'events'],
+ cache: BASE + "/.cache/browserify/cache.json"
+ }));
+ });
+ this.configure('production', function(){
+ this.use(express.logger());
+ this.set('static max age', 108000000);
+ return this.use(express.errorHandler());
+ });
+ this.configure('development', function(){
+ var compiler;
+ this.use(express.errorHandler({
+ dumpExceptions: true,
+ showStack: true
+ }));
+ this.set('view options').pretty = true;
+ compiler = require('connect-compiler-extras');
+ this.use('/js/kraken', compiler({
+ enabled: 'coco',
+ src: SRC,
+ dest: VAR + "/js/kraken",
+ log_level: LOG_LEVEL
+ }));
+ this.use(compiler({
+ enabled: ['jade-browser', 'stylus', 'yaml'],
+ src: WWW,
+ dest: VAR,
+ options: {
+ stylus: {
+ nib: true,
+ include: WWW + "/css"
+ }
+ },
+ log_level: LOG_LEVEL
+ }));
+ this.use(compiler({
+ enabled: 'yaml',
+ src: DATA,
+ dest: VAR + "/data",
+ log_level: LOG_LEVEL
+ }));
+ this.use(compiler({
+ enabled: 'commonjs_define',
+ src: [STATIC],
+ dest: VAR,
+ options: {
+ commonjs: {
+ drop_path_parts: 1,
+ drop_full_ext: false
+ },
+ commonjs_define: {
+ drop_path_parts: 1,
+ drop_full_ext: false
+ }
+ },
+ log_level: LOG_LEVEL
+ }));
+ return this.use(compiler({
+ enabled: 'commonjs_define',
+ src: [VAR, WWW],
+ dest: VAR,
+ options: {
+ commonjs: {
+ drop_path_parts: 1,
+ drop_full_ext: true
+ },
+ commonjs_define: {
+ drop_path_parts: 1,
+ drop_full_ext: true
+ }
+ },
+ log_level: LOG_LEVEL
+ }));
+ });
+ this.configure(function(){
+ var opts;
+ opts = this.set('static file options') || {};
+ this.use(express['static'](VAR, __clone(opts)));
+ this.use(express['static'](WWW, __clone(opts)));
+ return this.use(express['static'](STATIC, __clone(opts)));
+ });
+ this.controller(require('./controllers/graph'));
+ this.controller(require('./controllers/dashboard'));
+ YAML_EXT_PAT = /\.ya?ml$/i;
+ this.get('/datasources/all', function(req, res, next){
+ var data;
+ data = {};
+ return Seq().seq(glob, 'data/datasources/**/*.@(yaml|json)', {
+ nocase: true,
+ nosort: true
+ }, Seq).seq(function(paths){
+ return readFilesAsync(paths, this);
+ }).seq(function(txts){
+ return this.ok(_.items(txts));
+ }).flatten(false).parMap(function(_arg){
+ var f, text, k, v, that;
+ f = _arg[0], text = _arg[1];
+ k = f.replace(YAML_EXT_PAT, '.json');
+ v = data[k] = {};
+ try {
+ if (YAML_EXT_PAT.test(f)) {
+ v = data[k] = yaml.load(text);
+ } else {
+ v = data[k] = JSON.parse(text);
+ }
+ return this.ok(v);
+ } catch (err) {
+ console.error("[/data/all] catch! " + err);
+ console.error(err);
+ if (that = err.stack) {
+ console.error(that);
+ }
+ return res.send({
+ error: String(err),
+ partial_data: data
+ });
+ }
+ }).seq(function(){
+ return res.send(data);
+ })['catch'](function(err){
+ var that;
+ console.error('[/data/all] catch!');
+ console.error(err);
+ if (that = err.stack) {
+ console.error(that);
+ }
+ return res.send({
+ error: String(err),
+ partial_data: data
+ });
+ });
+ });
+ this.controller(require('./controllers/datasource'));
+ if (opts.proxy.enabled) {
+ proxy = require('./proxy')({
+ blacklist: opts.proxy.blacklist,
+ whitelist: opts.proxy.whitelist
+ });
+ this.get('/x', proxy);
+ this.get('/x/*', proxy);
+ }
+ this.get('/', function(req, res){
+ return res.render('dashboard/view');
+ });
+ this.get('/geo', function(req, res){
+ return res.render('geo');
+ });
+ this.get('/:type/:action/?', function(req, res, next){
+ var type, action, _ref;
+ _ref = req.params, type = _ref.type, action = _ref.action;
+ if (exists(WWW + "/" + type + "/" + action + ".jade")) {
+ return res.render(type + "/" + action);
+ } else {
+ return next();
+ }
+ });
+ this.get('/:type/?', function(req, res, next){
+ var type;
+ type = req.params.type;
+ if (exists(WWW + "/" + type + ".jade")) {
+ return res.render(type + "");
+ } else {
+ return next();
+ }
+ });
+ return this;
+ }
+};
+function __import(obj, src){
+ var own = {}.hasOwnProperty;
+ for (var key in src) if (own.call(src, key)) obj[key] = src[key];
+ return obj;
+}
+function __clone(it){
+ function fun(){} fun.prototype = it;
+ return new fun;
+}
\ No newline at end of file
--- /dev/null
+var fs, path, expand, mkdirpAsync, mkdirpSync, mkdirp, __slice = [].slice;
+fs = require('fs');
+path = require('path');
+expand = exports.expand = function(){
+ var parts, p, home;
+ parts = __slice.call(arguments);
+ p = path.normalize(path.join.apply(path, parts));
+ if (p.indexOf('~') === 0) {
+ home = process.env.HOME || process.env.HOMEPATH;
+ p = path.join(home, p.slice(1));
+ }
+ return path.resolve(p);
+};
+mkdirpAsync = exports.mkdirpAsync = (function(){
+ function mkdirpAsync(p, mode, cb){
+ var _ref;
+ mode == null && (mode = 493);
+ if (typeof mode === 'function') {
+ _ref = [mode, 493], cb = _ref[0], mode = _ref[1];
+ }
+ cb || (cb = function(){});
+ p = expand(p);
+ return path.exists(p, function(exists){
+ var ps, _p;
+ if (exists) {
+ return cb(null);
+ }
+ ps = p.split('/');
+ _p = ps.slice(0, -1).join('/');
+ return mkdirpAsync(_p, mode, function(err){
+ if ((err != null ? err.code : void 8) === 'EEXIST') {
+ return cb(null);
+ }
+ if (err) {
+ return cb(err);
+ }
+ return fs.mkdir(_p, function(err){
+ if ((err != null ? err.code : void 8) === 'EEXIST') {
+ return cb(null);
+ }
+ if (err) {
+ return cb(err);
+ }
+ });
+ });
+ });
+ }
+ return mkdirpAsync;
+}());
+mkdirp = exports.mkdirp = mkdirpSync = exports.mkdirpSync = function(p, mode){
+ var made_any, part, _p, _i, _ref, _len;
+ mode == null && (mode = 493);
+ made_any = false;
+ _p = '';
+ for (_i = 0, _len = (_ref = expand(p).slice(1).split('/')).length; _i < _len; ++_i) {
+ part = _ref[_i];
+ _p += '/' + part;
+ if (path.existsSync(_p)) {
+ continue;
+ }
+ made_any = true;
+ fs.mkdirSync(_p, mode);
+ }
+ return made_any;
+};
\ No newline at end of file
--- /dev/null
+var url, minimatch, request, matchesList, ProxyMiddleware, exports, _;
+_ = require('underscore');
+url = require('url');
+minimatch = require('minimatch');
+request = require('request');
+matchesList = function(list, value){
+ var pat, _i, _len;
+ for (_i = 0, _len = list.length; _i < _len; ++_i) {
+ pat = list[_i];
+ if (pat(value)) {
+ return true;
+ }
+ }
+ return false;
+};
+ProxyMiddleware = function(options){
+ var whitelist, blacklist, _ref;
+ options == null && (options = {});
+ _ref = options = (__import({
+ whitelist: [],
+ blacklist: []
+ }, options)), whitelist = _ref.whitelist, blacklist = _ref.blacklist;
+ whitelist = whitelist.map(function(it){
+ return minimatch.filter(it, {
+ nocase: true
+ });
+ });
+ blacklist = blacklist.map(function(it){
+ return minimatch.filter(it, {
+ nocase: true
+ });
+ });
+ return function(req, res){
+ var targetUrl, target;
+ targetUrl = (req.params.url || url.parse(req.url).pathname.slice(3)).trim();
+ if (!targetUrl) {
+ return res.send({
+ error: 'URL required'
+ }, 400);
+ }
+ if (!/^https?:\/\//.test(targetUrl)) {
+ targetUrl = "http://" + targetUrl;
+ }
+ target = url.parse(targetUrl, true, true);
+ if (matchesList(blacklist, target.hostname)) {
+ return res.send({
+ error: 'Domain is blacklisted'
+ }, 403);
+ }
+ if (!matchesList(whitelist, target.hostname)) {
+ return res.send({
+ error: 'Domain is not whitelisted'
+ }, 403);
+ }
+ res.header('X-Accel-Buffering', 'no');
+ console.log("[Proxy] " + targetUrl);
+ return request.get(targetUrl).pipe(res);
+ };
+};
+module.exports = exports = ProxyMiddleware;
+exports.ProxyMiddleware = ProxyMiddleware;
+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
--- /dev/null
+var url, exports, ReqInfoMiddleware, _;
+_ = require('underscore');
+url = require('url');
+module.exports = exports = function(options){
+ var mw;
+ mw = new ReqInfoMiddleware(options);
+ return mw.respond;
+};
+exports.ReqInfoMiddleware = ReqInfoMiddleware = (function(){
+ ReqInfoMiddleware.displayName = 'ReqInfoMiddleware';
+ var prototype = ReqInfoMiddleware.prototype, constructor = ReqInfoMiddleware;
+ function ReqInfoMiddleware(options){
+ this.options = options != null
+ ? options
+ : {};
+ _.bindAll(this, 'respond');
+ }
+ prototype.parse = url.parse;
+ prototype.respond = (function(){
+ function reqinfo(req, res, next){
+ req.info = this.parse(req.url);
+ return next();
+ }
+ return reqinfo;
+ }());
+ return ReqInfoMiddleware;
+}());
\ No newline at end of file
--- /dev/null
+var fs, path, exec, spawn, exists, express, LimnMiddleware, exports, app, limn, mainfile, PORT, that, NODE_ENV, REV, _ref;
+fs = require('fs');
+path = require('path');
+_ref = require('child_process'), exec = _ref.exec, spawn = _ref.spawn;
+exists = fs.existsSync || path.existsSync;
+express = require('express');
+LimnMiddleware = require('./middleware');
+app = exports = module.exports = express.createServer();
+/**
+ * Handle webhook notification to pull from origin.
+ */
+app.all('/webhook/post-update', function(req, res){
+ var cmd, child;
+ cmd = 'git pull origin master';
+ console.log("[/webhook/post-update] $ " + cmd);
+ return child = exec(cmd, function(err, stdout, stderr){
+ res.contentType('.txt');
+ console.log('[/webhook/post-update] ', stdout);
+ console.log('[/webhook/post-update] ', stderr);
+ if (err) {
+ console.error('[/webhook/post-update] ERROR!', err);
+ return res.send("$ " + cmd + "\n\n" + stdout + "\n\n" + stderr + "\n\nERROR! " + err, 503);
+ } else {
+ return res.send("$ " + cmd + "\n\n" + stdout + "\n\n" + stderr, 200);
+ }
+ });
+});
+/**
+ * Load Limn middleware
+ */
+app.use(limn = app.limn = LimnMiddleware());
+mainfile = path.basename((_ref = require.main) != null ? _ref.filename : void 8);
+if (require.main === module || 'Cokefile' === mainfile) {
+ PORT = 8081;
+ if (that = process.env.KRAKEN_PORT) {
+ PORT = parseInt(that, 10);
+ }
+ NODE_ENV = process.env.NODE_ENV || 'development';
+ REV = process.env.KRAKEN_REV || 'HEAD';
+ try {
+ REV = require('../version');
+ } catch (e) {}
+ exec('git rev-parse --short HEAD', {
+ cwd: process.cwd(),
+ env: process.env
+ }, function(err, stdout, stderr){
+ var REV, s;
+ if (err) {
+ throw err;
+ }
+ if (!REV) {
+ REV = stdout.trim();
+ }
+ console.log(s = "Kraken Server (port=" + PORT + ", env=" + NODE_ENV + ", rev=" + REV + ", base_dir=" + limn.BASE + ")");
+ console.log(__repeatString('=', s.length));
+ return app.listen(PORT);
+ });
+}
+function __repeatString(str, n){
+ for (var r = ''; n > 0; (n >>= 1) && (str += str)) if (n & 1) r += str;
+ return r;
+}
\ No newline at end of file
--- /dev/null
+var CWD, WWW, VAR, STATIC, VERSION, fs, path, yaml, jade, NODE_ENV, IS_PROD, IS_TEST, IS_DEV, SOURCES_ENV, sources, joinTree, _;
+CWD = exports.CWD = process.cwd();
+WWW = exports.WWW = CWD + "/www";
+VAR = exports.VAR = CWD + "/var";
+STATIC = exports.STATIC = CWD + "/static";
+VERSION = 'HEAD';
+try {
+ VERSION = require('../version');
+} catch (e) {}
+exports.VERSION = exports.version = VERSION;
+fs = exports.fs = require('fs');
+path = exports.path = require('path');
+_ = exports._ = require('underscore');
+_.str = require('underscore.string');
+_.mixin(_.str.exports());
+yaml = exports.yaml = require('js-yaml');
+jade = exports.jade = require('jade');
+exports.env = process.env;
+NODE_ENV = exports.NODE_ENV = (process.env.NODE_ENV || 'development').toLowerCase();
+IS_PROD = exports.IS_PROD = NODE_ENV === 'production';
+IS_TEST = exports.IS_TEST = NODE_ENV === 'test';
+IS_DEV = exports.IS_DEV = !(IS_PROD || IS_TEST);
+SOURCES_ENV = process.env.KRAKEN_FORCE_BUNDLES ? 'production' : NODE_ENV;
+/**
+ * Reify a modules.yaml file
+ */
+sources = exports.sources = function(modulesFile, node_env){
+ var mods, modlist;
+ node_env == null && (node_env = SOURCES_ENV);
+ mods = yaml.load(fs.readFileSync(modulesFile, 'utf8'));
+ modlist = (mods.all || []).concat(mods[node_env] || []);
+ return _.flatten(modlist.map(function(_arg){
+ var suffix, paths;
+ suffix = _arg.suffix || '', paths = _arg.paths;
+ return joinTree('', paths).map(function(it){
+ return it + suffix;
+ });
+ }));
+};
+joinTree = exports.joinTree = (function(){
+ function joinTree(root, tree){
+ if (typeof tree === 'string') {
+ return [root + "/" + tree];
+ }
+ return _(tree).reduce(function(acc, branch){
+ if (typeof branch === 'string') {
+ acc.push(root + "/" + branch);
+ } else {
+ _.each(branch, function(v, k){
+ return acc.push.apply(acc, joinTree(root + "/" + k, v));
+ });
+ }
+ return acc;
+ }, []);
+ }
+ return joinTree;
+}());
\ No newline at end of file
--- /dev/null
+var AliasDict, exports, _, __slice = [].slice;
+_ = require('kraken/util/underscore');
+/**
+ * @class A mapping of key-value pairs supporting key-aliases.
+ */
+AliasDict = (function(){
+ AliasDict.displayName = 'AliasDict';
+ var prototype = AliasDict.prototype, constructor = AliasDict;
+ /**
+ * Data store.
+ * @type Object
+ * @private
+ */
+ prototype._data = null;
+ /**
+ * Mapping from keys to an array of [potentially nested] alias-keys.
+ * @type Object<String, Array<String>>
+ * @private
+ */
+ prototype._aliases = null;
+ /**
+ * @constructor
+ */;
+ function AliasDict(){
+ this._data = {};
+ this._aliases = {};
+ this.extend.apply(this, arguments);
+ }
+ /**
+ * @returns {Number} Number of real keys in the Dict.
+ */
+ prototype.size = function(){
+ return _.keys(this._data).length;
+ };
+ /**
+ * @returns {AliasDict} A copy of the AliasDict, including aliases as well as data.
+ */
+ prototype.clone = function(){
+ var d;
+ d = new AliasDict(this._data);
+ _.each(this._aliases, function(v, k){
+ return d.setAlias(k, v.slice());
+ });
+ return d;
+ };
+ /**
+ * @returns {Boolean} Whether there is a value at the given key.
+ */
+ prototype.has = function(key){
+ return this.get(key) != null;
+ };
+ /**
+ * @returns {*} Ignores aliases, returning the value at key or `undefined`.
+ */
+ prototype.getValue = function(key){
+ var prop;
+ prop = _.getNested(this._data, key);
+ if (prop != null) {
+ return prop.value;
+ }
+ };
+ prototype.get = function(key, def){
+ var aliases, val;
+ aliases = this._aliases[key] || [key];
+ val = aliases.reduce(function(val, alias){
+ var prop;
+ if ((val != null) !== undefined) {
+ return val;
+ }
+ prop = _.getNested(this._data, alias);
+ if (prop != null) {
+ return prop.value;
+ }
+ }, undefined);
+ if (val !== undefined) {
+ return val;
+ } else {
+ return def;
+ }
+ };
+ prototype.set = function(key, val){
+ _.setNested(this._data, key, val, {
+ ensure: true
+ });
+ return val;
+ };
+ prototype.del = function(key){
+ var prop;
+ prop = _.getNestedMeta(key);
+ if (prop) {
+ delete prop.obj[prop.key];
+ return prop.value;
+ }
+ };
+ prototype.hasAlias = function(key){
+ return this._aliases[key] != null;
+ };
+ prototype.getAlias = function(key, def){
+ def == null && (def = []);
+ return this._aliases[key] || def;
+ };
+ prototype.setAlias = function(key, aliases){
+ this._aliases[key] = _.isArray(aliases)
+ ? aliases
+ : [aliases];
+ return this;
+ };
+ prototype.addAlias = function(key){
+ var aliases;
+ aliases = __slice.call(arguments, 1);
+ this._aliases[key] = _.flatten(this.getAlias(key, [key]).concat(aliases));
+ return this;
+ };
+ prototype.delAlias = function(key){
+ var _ref, _ref2;
+ return _ref2 = (_ref = this._aliases)[key], delete _ref[key], _ref2;
+ };
+ prototype.toObject = function(){
+ return _.clone(this._data);
+ };
+ prototype.keys = function(){
+ return _.keys(this._data);
+ };
+ prototype.values = function(){
+ return _.values(this._data);
+ };
+ prototype.extend = function(){
+ var args, o, k, v, _i, _len;
+ args = __slice.call(arguments);
+ for (_i = 0, _len = args.length; _i < _len; ++_i) {
+ o = args[_i];
+ for (k in o) {
+ v = o[k];
+ this.set(k, v);
+ }
+ }
+ return this;
+ };
+ prototype.reduce = function(fn, acc, context){
+ context == null && (context = this);
+ return _.reduce(this._data, fn, acc, context);
+ };
+ prototype.map = function(fn, context){
+ context == null && (context = this);
+ return _.map(this._data, fn, context);
+ };
+ prototype.filter = function(fn, context){
+ context == null && (context = this);
+ return _.filter(this._data, fn, context);
+ };
+ prototype.each = function(fn, context){
+ context == null && (context = this);
+ _.each(this._data, fn, context);
+ return this;
+ };
+ prototype.invoke = function(name){
+ var args;
+ args = __slice.call(arguments, 1);
+ return _.invoke.apply(_, [this._data, name].concat(__slice.call(args)));
+ };
+ prototype.pluck = function(attr){
+ return _.pluck(this._data, attr);
+ };
+ prototype.find = function(fn, context){
+ context == null && (context = this);
+ return _.find(this._data, fn, context);
+ };
+ prototype.toString = function(){
+ var Cls;
+ Cls = this.constructor;
+ return (Cls.displayName || Cls.name) + "()";
+ };
+ return AliasDict;
+}());
+module.exports = exports = AliasDict;
\ No newline at end of file
--- /dev/null
+var SEEK_ABSOLUTE, SEEK_RELATIVE, SEEK_FROM_EOF, bin, binlen, mask, chr, ord, BitString, exports;
+SEEK_ABSOLUTE = 0;
+SEEK_RELATIVE = 1;
+SEEK_FROM_EOF = 2;
+bin = function(n){
+ var s;
+ do {
+ s = (n % 2 ? '1' : '0') + (s || '');
+ n >>= 1;
+ } while (n);
+ return s;
+};
+binlen = function(n){
+ return bin(Math.abs(n)).length;
+};
+mask = function(n){
+ return (1 << n) - 1;
+};
+chr = function(it){
+ return String.fromCharCode(it);
+};
+ord = function(it){
+ return String(it).charCodeAt(0);
+};
+/**
+ * File-like object for reading/writing bits.
+ * @class
+ */
+BitString = (function(){
+ BitString.displayName = 'BitString';
+ var prototype = BitString.prototype, constructor = BitString;
+ prototype.buf = null;
+ prototype._pos = -1;
+ prototype._spill = 0;
+ prototype._spillen = 0;
+ prototype._peek = 0;
+ prototype._peeklen = 0;
+ function BitString(source, buf){
+ var i, _to;
+ source == null && (source = '');
+ buf == null && (buf = []);
+ this.buf = buf.slice();
+ for (i = 0, _to = source.length; i < _to; ++i) {
+ this._bufwrite(source.charCodeAt(i));
+ }
+ }
+ prototype.size = function(){
+ return this.buf.length + (this._spillen ? 1 : 0);
+ };
+ prototype.bitsize = function(){
+ return this.buf.length * 8 + this._spillen;
+ };
+ prototype._bufwrite = function(b){
+ if (this._pos === -1) {
+ this.buf.push(b);
+ } else {
+ this.buf[this._pos] = b;
+ if (++this._pos >= this.buf.length) {
+ this._pos = -1;
+ }
+ }
+ return this;
+ };
+ prototype.writebits = function(n, size){
+ var bits, b;
+ size = size || binlen(n);
+ bits = this._spill << size | n;
+ size += this._spillen;
+ while (size >= 8) {
+ size -= 8;
+ b = bits >> size;
+ bits &= mask(size);
+ this._bufwrite(b);
+ }
+ this._spill = bits;
+ this._spillen = size;
+ return this;
+ };
+ prototype.flush = function(){
+ var b;
+ b = this._spill;
+ if (this._spillen) {
+ b <<= 8 - this._spillen;
+ this._bufwrite(b);
+ }
+ this._spill = 0;
+ this._spillen = 0;
+ return this;
+ };
+ prototype.truncate = function(){
+ this.buf = [];
+ this._pos = -1;
+&n