"""
@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