task \server 'Start dev server' ->
- invoke \setup
- say ''
- run 'src/server/server.co'
+ run './src/server/server.co'
task \build 'Build coco sources' ->
-### Setting Up
-
-This is a [node.js][nodejs] project, so you need node > 0.6.x and the node package manager, [npm][npm].
-
-Once that's done, I recommend you install Coco globally so you don't have to futz with your path: `npm install -g coco` -- if you choose not to, you should probably add `./node_modules/.bin` to `PATH` in your bashrc or something.
-
-Next, install your source checkout in dev mode by running `npm link` from the project root. This will also download and install all the package dependencies.
-
-Finally, start the `server` task using Coke (it's like Rake for Coco) with `coke server`. While this does what it says on the tin, it also seems to have a habit of randomly losing parts of stderr. For now, you can work around this by manually starting the server: `coke link && lib/server/server.co`
-
-You should now have a server running on 8081.
-
-### Project Layout
-- assets/ - static images
-- data/ - json, yaml, csv, etc. files that contain graph configuration and graph content data
-- data/datasources/ - graph content data
-- data/graphs/ - saved graph configurations.
-- lib/ - [Coco][coco] files. Application logic lives here.
-- lib/{chart,dashboard,dataset,graph}/ - Models and View Classes
-- lib/template/ - client side [Jade][jade] views. These are included and rendered by View classes.
-- lib/server/ - Server side [Coco][coco] files.
-- lib/server/server.co - [Express][expressjs] server setup. Routing is done here.
-- lib/server/controllers/ - Server side controllers. Routed to by [express-resource][].
-- www/ - (Mostly) static [Jade][jade] HTML templates and [Stylus][stylus] CSS templates. The [Jade][jade] templates are rendered by the server side controllers in lib/server/controllers/.
-- var/ - Compiled JavaScript and CSS files.
-
-### Deployment
-Coco needs to be compiled down to JavaScript in order for it to be executed. In development environments, this is done on the fly. In production environments, all Coco is compiled down into JavaScript files and placed in a dist/ directory. These JavaScript (and compiled Stylus CSS files) are served up directly to the browser upon request, rather than having to be compiled first.
-
-deploy.sh currently builds a distribution tmp/dist, and then rsyncs this over to reportcard.wmflabs.org. You will need an account with sudo permissions on reportcard2.pmtpa.wmflabs in order to deploy.
-
-
-### Notes
-
-- This project is written in [Coco][coco], a dialect of [CoffeeScript][coffee] -- they both compile
- down to JavaScript. The pair are very, very similar, [except][coco-improvements]
- for [a few][coco-incompatibilities] [things][coco-vs-coffee]. If you can read JavaScript and Ruby,
- you can understand Coco and CoffeeScript. (I refer to the [CoffeeScript docs][coffee-docs] for
- the syntax, and I find the [comparison page][coco-vs-coffee] to be the best reference for Coco.)
-
-- Coco require compilation before it'll run in the browser (though node can run it directly -- `#!/usr/bin/env coco` will work as a shebang as well). I've written [request middleware][connect-compiler] that recompiles stale files on demand, and it is pretty cool.
-
-
-
-[nodejs]: http://nodejs.org/
-[npm]: http://npmjs.org/
-[coco]: https://github.com/satyr/coco
-[coco-vs-coffee]: https://github.com/satyr/coco/wiki/side-by-side-comparison
-[coco-improvements]: https://github.com/satyr/coco/wiki/improvements
-[coco-incompatibilities]: https://github.com/satyr/coco/wiki/incompatibilities
-[coffee]: http://coffeescript.org/
-[coffee-docs]: http://coffeescript.org/#language
-[connect-compiler]: https://github.com/dsc/connect-compiler
-[jade]: https://github.com/visionmedia/jade
-[expressjs]: http://expressjs.com/guide.html
-[express-resource]: https://github.com/visionmedia/express-resource
-[stylus]: http://learnboost.github.com/stylus/
\ No newline at end of file
-var Backbone, op, BaseBackboneMixin, mixinBase, BaseModel, BaseList, _ref, _, __slice = [].slice;
+var Backbone, limn, op, BaseBackboneMixin, mixinBase, BaseModel, BaseList, _ref, _, __slice = [].slice;
Backbone = require('backbone');
+limn = require('../client');
_ref = require('../util'), _ = _ref._, op = _ref.op;
_ref = require('./base-mixin'), BaseBackboneMixin = _ref.BaseBackboneMixin, mixinBase = _ref.mixinBase;
/**
return BaseModel;
}()),
url: function(){
- return this.urlRoot + "/" + this.get('id') + ".json";
+ return limn.mount(this.urlRoot) + "/" + this.get('id') + ".json";
},
has: function(key){
return this.get(key) != null;
});
},
url: function(){
- var id;
+ var root, id;
+ root = limn.mount(this.urlRoot);
id = this.get('id') || this.get('slug');
if (id) {
- return this.urlRoot + "/" + id + ".json";
+ return root + "/" + id + ".json";
} else {
- return this.urlRoot + ".json";
+ return root + ".json";
}
},
load: function(){
require.define('/node_modules/limn/base/base-model.js', function(require, module, exports, __dirname, __filename, undefined){
-var Backbone, op, BaseBackboneMixin, mixinBase, BaseModel, BaseList, _ref, _, __slice = [].slice;
+var Backbone, limn, op, BaseBackboneMixin, mixinBase, BaseModel, BaseList, _ref, _, __slice = [].slice;
Backbone = require('backbone');
+limn = require('../client');
_ref = require('../util'), _ = _ref._, op = _ref.op;
_ref = require('./base-mixin'), BaseBackboneMixin = _ref.BaseBackboneMixin, mixinBase = _ref.mixinBase;
/**
return BaseModel;
}()),
url: function(){
- return this.urlRoot + "/" + this.get('id') + ".json";
+ return limn.mount(this.urlRoot) + "/" + this.get('id') + ".json";
},
has: function(key){
return this.get(key) != null;
});
},
url: function(){
- var id;
+ var root, id;
+ root = limn.mount(this.urlRoot);
id = this.get('id') || this.get('slug');
if (id) {
- return this.urlRoot + "/" + id + ".json";
+ return root + "/" + id + ".json";
} else {
- return this.urlRoot + ".json";
+ return root + ".json";
}
},
load: function(){
Base.displayName = 'Base';
var prototype = __extend(Base, superclass).prototype, constructor = Base;
function Base(){
+ var _ref;
this.__class__ = this.constructor;
this.__superclass__ = this.constructor.superclass;
this.__apply_bind__();
superclass.call(this);
- this.__class__.emit('new', this);
+ if (typeof (_ref = this.__class__).emit == 'function') {
+ _ref.emit('new', this);
+ }
}
/**
* A list of method-names to bind on `initialize`; set this on a subclass to override.
return this.getClassName() + "()";
};
Base.extended = function(Subclass){
- var k, v, _own = {}.hasOwnProperty;
- for (k in this) if (_own.call(this, k)) {
+ var k, v;
+ for (k in this) {
v = this[k];
- if (typeof v === 'function') {
+ if (typeof v === 'function' && !_.contains(['apply', 'call', 'constructor', 'toString'], k)) {
Subclass[k] = v;
}
}
Base.displayName = 'Base';
var prototype = __extend(Base, superclass).prototype, constructor = Base;
function Base(){
+ var _ref;
this.__class__ = this.constructor;
this.__superclass__ = this.constructor.superclass;
this.__apply_bind__();
superclass.call(this);
- this.__class__.emit('new', this);
+ if (typeof (_ref = this.__class__).emit == 'function') {
+ _ref.emit('new', this);
+ }
}
/**
* A list of method-names to bind on `initialize`; set this on a subclass to override.
return this.getClassName() + "()";
};
Base.extended = function(Subclass){
- var k, v, _own = {}.hasOwnProperty;
- for (k in this) if (_own.call(this, k)) {
+ var k, v;
+ for (k in this) {
v = this[k];
- if (typeof v === 'function') {
+ if (typeof v === 'function' && !_.contains(['apply', 'call', 'constructor', 'toString'], k)) {
Subclass[k] = v;
}
}
-var mixins, models, views, cache, cascading, data_binding;
+var mixins, models, views, cache, cascading, data_binding, parser_mixin;
exports.Base = require('./base');
mixins = require('./base-mixin');
models = require('./base-model');
cascading = require('./cascading-model');
data_binding = require('./data-binding');
__import(__import(__import(__import(__import(__import(exports, mixins), models), views), cache), cascading), data_binding);
+parser_mixin = require('./parser-mixin');
+__import(exports, parser_mixin);
function __import(obj, src){
var own = {}.hasOwnProperty;
for (var key in src) if (own.call(src, key)) obj[key] = src[key];
require.define('/node_modules/limn/base/index.js', function(require, module, exports, __dirname, __filename, undefined){
-var mixins, models, views, cache, cascading, data_binding;
+var mixins, models, views, cache, cascading, data_binding, parser_mixin;
exports.Base = require('./base');
mixins = require('./base-mixin');
models = require('./base-model');
cascading = require('./cascading-model');
data_binding = require('./data-binding');
__import(__import(__import(__import(__import(__import(exports, mixins), models), views), cache), cascading), data_binding);
+parser_mixin = require('./parser-mixin');
+__import(exports, parser_mixin);
function __import(obj, src){
var own = {}.hasOwnProperty;
for (var key in src) if (own.call(src, key)) obj[key] = src[key];
--- /dev/null
+var Parsers, BaseModel, BaseList, BaseView, Mixin, ParserMixin, ParsingModel, ParsingList, ParsingView, _ref;
+Parsers = require('../util/parser').Parsers;
+_ref = require('../base'), BaseModel = _ref.BaseModel, BaseList = _ref.BaseList, BaseView = _ref.BaseView, Mixin = _ref.Mixin;
+exports.Parsers = Parsers;
+/**
+ * @class Methods for a class to select parsers by type reflection.
+ * @mixin
+ */
+exports.ParserMixin = ParserMixin = (function(superclass){
+ ParserMixin.displayName = 'ParserMixin';
+ var prototype = __extend(ParserMixin, superclass).prototype, constructor = ParserMixin;
+ __import(ParserMixin.prototype, Parsers);
+ function ParserMixin(target){
+ return Mixin.call(ParserMixin, target);
+ }
+ prototype.parseValue = function(v, type){
+ return this.getParser(type)(v);
+ };
+ prototype.getParser = function(type){
+ var fn, t, _i, _ref, _len;
+ type == null && (type = 'String');
+ fn = this["parse" + type];
+ if (typeof fn === 'function') {
+ return fn;
+ }
+ type = _(String(type).toLowerCase());
+ for (_i = 0, _len = (_ref = ['Integer', 'Float', 'Number', 'Boolean', 'Object', 'Array', 'Function']).length; _i < _len; ++_i) {
+ t = _ref[_i];
+ if (type.startsWith(t.toLowerCase())) {
+ return this["parse" + t];
+ }
+ }
+ return this.defaultParser || this.parseString;
+ };
+ prototype.getParserFromExample = function(v){
+ var type;
+ if (v == null) {
+ return null;
+ }
+ type = typeof v;
+ if (type !== 'object') {
+ return this.getParser(type);
+ } else if (_.isArray(v)) {
+ return this.getParser('Array');
+ } else {
+ return this.getParser('Object');
+ }
+ };
+ return ParserMixin;
+}(Mixin));
+/**
+ * @class Basic model which mixes in the ParserMixin.
+ * @extends BaseModel
+ * @borrows ParserMixin
+ */
+ParsingModel = exports.ParsingModel = BaseModel.extend(ParserMixin.mix({
+ constructor: (function(){
+ function ParsingModel(){
+ return BaseModel.apply(this, arguments);
+ }
+ return ParsingModel;
+ }())
+}));
+/**
+ * @class Basic collection which mixes in the ParserMixin.
+ * @extends BaseList
+ * @borrows ParserMixin
+ */
+ParsingList = exports.ParsingList = BaseList.extend(ParserMixin.mix({
+ constructor: (function(){
+ function ParsingList(){
+ return BaseList.apply(this, arguments);
+ }
+ return ParsingList;
+ }())
+}));
+/**
+ * @class Basic view which mixes in the ParserMixin.
+ * @extends BaseView
+ * @borrows ParserMixin
+ */
+ParsingView = exports.ParsingView = BaseView.extend(ParserMixin.mix({
+ constructor: (function(){
+ function ParsingView(){
+ return BaseView.apply(this, arguments);
+ }
+ return ParsingView;
+ }())
+}));
+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
+require.define('/node_modules/limn/base/parser-mixin.js', function(require, module, exports, __dirname, __filename, undefined){
+
+var Parsers, BaseModel, BaseList, BaseView, Mixin, ParserMixin, ParsingModel, ParsingList, ParsingView, _ref;
+Parsers = require('../util/parser').Parsers;
+_ref = require('../base'), BaseModel = _ref.BaseModel, BaseList = _ref.BaseList, BaseView = _ref.BaseView, Mixin = _ref.Mixin;
+exports.Parsers = Parsers;
+/**
+ * @class Methods for a class to select parsers by type reflection.
+ * @mixin
+ */
+exports.ParserMixin = ParserMixin = (function(superclass){
+ ParserMixin.displayName = 'ParserMixin';
+ var prototype = __extend(ParserMixin, superclass).prototype, constructor = ParserMixin;
+ __import(ParserMixin.prototype, Parsers);
+ function ParserMixin(target){
+ return Mixin.call(ParserMixin, target);
+ }
+ prototype.parseValue = function(v, type){
+ return this.getParser(type)(v);
+ };
+ prototype.getParser = function(type){
+ var fn, t, _i, _ref, _len;
+ type == null && (type = 'String');
+ fn = this["parse" + type];
+ if (typeof fn === 'function') {
+ return fn;
+ }
+ type = _(String(type).toLowerCase());
+ for (_i = 0, _len = (_ref = ['Integer', 'Float', 'Number', 'Boolean', 'Object', 'Array', 'Function']).length; _i < _len; ++_i) {
+ t = _ref[_i];
+ if (type.startsWith(t.toLowerCase())) {
+ return this["parse" + t];
+ }
+ }
+ return this.defaultParser || this.parseString;
+ };
+ prototype.getParserFromExample = function(v){
+ var type;
+ if (v == null) {
+ return null;
+ }
+ type = typeof v;
+ if (type !== 'object') {
+ return this.getParser(type);
+ } else if (_.isArray(v)) {
+ return this.getParser('Array');
+ } else {
+ return this.getParser('Object');
+ }
+ };
+ return ParserMixin;
+}(Mixin));
+/**
+ * @class Basic model which mixes in the ParserMixin.
+ * @extends BaseModel
+ * @borrows ParserMixin
+ */
+ParsingModel = exports.ParsingModel = BaseModel.extend(ParserMixin.mix({
+ constructor: (function(){
+ function ParsingModel(){
+ return BaseModel.apply(this, arguments);
+ }
+ return ParsingModel;
+ }())
+}));
+/**
+ * @class Basic collection which mixes in the ParserMixin.
+ * @extends BaseList
+ * @borrows ParserMixin
+ */
+ParsingList = exports.ParsingList = BaseList.extend(ParserMixin.mix({
+ constructor: (function(){
+ function ParsingList(){
+ return BaseList.apply(this, arguments);
+ }
+ return ParsingList;
+ }())
+}));
+/**
+ * @class Basic view which mixes in the ParserMixin.
+ * @extends BaseView
+ * @borrows ParserMixin
+ */
+ParsingView = exports.ParsingView = BaseView.extend(ParserMixin.mix({
+ constructor: (function(){
+ function ParsingView(){
+ return BaseView.apply(this, arguments);
+ }
+ return ParsingView;
+ }())
+}));
+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;
+}
+
+});
-var moment, Backbone, op, ReadyEmitter, Parsers, ParserMixin, KNOWN_CHART_TYPES, ChartType, _ref, _, __slice = [].slice;
+var moment, Backbone, limn, op, ReadyEmitter, Parsers, ParserMixin, KNOWN_CHART_TYPES, ChartType, _ref, _, __slice = [].slice;
moment = require('moment');
Backbone = require('backbone');
+limn = require('../client');
_ref = require('../util'), _ = _ref._, op = _ref.op;
ReadyEmitter = require('../util/event').ReadyEmitter;
-_ref = require('../util/parser'), Parsers = _ref.Parsers, ParserMixin = _ref.ParserMixin;
+_ref = require('../base'), Parsers = _ref.Parsers, ParserMixin = _ref.ParserMixin;
/**
* Map of known libraries by name.
* @type Object
* info about valid options, along with their types and defaults.
*/
prototype.loadSpec = function(){
- var proto, _this = this;
+ var proto, url, _this = this;
if (this.ready) {
return this;
}
proto = this.constructor.prototype;
+ url = (limn.config.mount !== '/' ? limn.config.mount : '') + this.SPEC_URL;
jQuery.ajax({
- url: this.SPEC_URL,
+ url: url,
dataType: 'json',
success: function(spec){
proto.spec = spec;
require.define('/node_modules/limn/chart/chart-type.js', function(require, module, exports, __dirname, __filename, undefined){
-var moment, Backbone, op, ReadyEmitter, Parsers, ParserMixin, KNOWN_CHART_TYPES, ChartType, _ref, _, __slice = [].slice;
+var moment, Backbone, limn, op, ReadyEmitter, Parsers, ParserMixin, KNOWN_CHART_TYPES, ChartType, _ref, _, __slice = [].slice;
moment = require('moment');
Backbone = require('backbone');
+limn = require('../client');
_ref = require('../util'), _ = _ref._, op = _ref.op;
ReadyEmitter = require('../util/event').ReadyEmitter;
-_ref = require('../util/parser'), Parsers = _ref.Parsers, ParserMixin = _ref.ParserMixin;
+_ref = require('../base'), Parsers = _ref.Parsers, ParserMixin = _ref.ParserMixin;
/**
* Map of known libraries by name.
* @type Object
* info about valid options, along with their types and defaults.
*/
prototype.loadSpec = function(){
- var proto, _this = this;
+ var proto, url, _this = this;
if (this.ready) {
return this;
}
proto = this.constructor.prototype;
+ url = (limn.config.mount !== '/' ? limn.config.mount : '') + this.SPEC_URL;
jQuery.ajax({
- url: this.SPEC_URL,
+ url: url,
dataType: 'json',
success: function(spec){
proto.spec = spec;
-var op, Parsers, ParserMixin, ParsingModel, ParsingView, BaseModel, BaseList, TagSet, KNOWN_TAGS, ChartOption, ChartOptionList, _ref, _, __slice = [].slice;
+var op, BaseModel, BaseList, Parsers, ParserMixin, ParsingModel, ParsingView, TagSet, KNOWN_TAGS, ChartOption, ChartOptionList, _ref, _, __slice = [].slice;
_ref = require('../../util'), _ = _ref._, op = _ref.op;
-_ref = require('../../util/parser'), Parsers = _ref.Parsers, ParserMixin = _ref.ParserMixin, ParsingModel = _ref.ParsingModel, ParsingView = _ref.ParsingView;
-_ref = require('../../base'), BaseModel = _ref.BaseModel, BaseList = _ref.BaseList;
+_ref = require('../../base'), BaseModel = _ref.BaseModel, BaseList = _ref.BaseList, Parsers = _ref.Parsers, ParserMixin = _ref.ParserMixin, ParsingModel = _ref.ParsingModel, ParsingView = _ref.ParsingView;
/**
* @class A set of tags.
*/
require.define('/node_modules/limn/chart/option/chart-option-model.js', function(require, module, exports, __dirname, __filename, undefined){
-var op, Parsers, ParserMixin, ParsingModel, ParsingView, BaseModel, BaseList, TagSet, KNOWN_TAGS, ChartOption, ChartOptionList, _ref, _, __slice = [].slice;
+var op, BaseModel, BaseList, Parsers, ParserMixin, ParsingModel, ParsingView, TagSet, KNOWN_TAGS, ChartOption, ChartOptionList, _ref, _, __slice = [].slice;
_ref = require('../../util'), _ = _ref._, op = _ref.op;
-_ref = require('../../util/parser'), Parsers = _ref.Parsers, ParserMixin = _ref.ParserMixin, ParsingModel = _ref.ParsingModel, ParsingView = _ref.ParsingView;
-_ref = require('../../base'), BaseModel = _ref.BaseModel, BaseList = _ref.BaseList;
+_ref = require('../../base'), BaseModel = _ref.BaseModel, BaseList = _ref.BaseList, Parsers = _ref.Parsers, ParserMixin = _ref.ParserMixin, ParsingModel = _ref.ParsingModel, ParsingView = _ref.ParsingView;
/**
* @class A set of tags.
*/
var d3, ColorBrewer, op, ChartType, D3ChartElement, root, D3ChartType, _ref, _;
d3 = require('d3');
ColorBrewer = require('colorbrewer');
-_ref = require('../../../util'), _ = _ref._, op = _ref.op;
-ChartType = require('../../chart-type').ChartType;
-D3ChartElement = require('./d3-chart-element').D3ChartElement;
+_ref = require('../../util'), _ = _ref._, op = _ref.op;
+ChartType = require('../chart-type').ChartType;
+D3ChartElement = require('./d3/d3-chart-element').D3ChartElement;
root = function(){
return this;
}();
var d3, ColorBrewer, op, ChartType, D3ChartElement, root, D3ChartType, _ref, _;
d3 = require('d3');
ColorBrewer = require('colorbrewer');
-_ref = require('../../../util'), _ = _ref._, op = _ref.op;
-ChartType = require('../../chart-type').ChartType;
-D3ChartElement = require('./d3-chart-element').D3ChartElement;
+_ref = require('../../util'), _ = _ref._, op = _ref.op;
+ChartType = require('../chart-type').ChartType;
+D3ChartElement = require('./d3/d3-chart-element').D3ChartElement;
root = function(){
return this;
}();
var ChartType, DygraphsChartType, _;
-_ = require('../../../util/underscore');
-ChartType = require('../../chart-type').ChartType;
+_ = require('../../util/underscore');
+ChartType = require('../chart-type').ChartType;
exports.DygraphsChartType = DygraphsChartType = (function(superclass){
DygraphsChartType.displayName = 'DygraphsChartType';
var prototype = __extend(DygraphsChartType, superclass).prototype, constructor = DygraphsChartType;
require.define('/node_modules/limn/chart/type/dygraphs.js', function(require, module, exports, __dirname, __filename, undefined){
var ChartType, DygraphsChartType, _;
-_ = require('../../../util/underscore');
-ChartType = require('../../chart-type').ChartType;
+_ = require('../../util/underscore');
+ChartType = require('../chart-type').ChartType;
exports.DygraphsChartType = DygraphsChartType = (function(superclass){
DygraphsChartType.displayName = 'DygraphsChartType';
var prototype = __extend(DygraphsChartType, superclass).prototype, constructor = DygraphsChartType;
--- /dev/null
+var EventEmitter, op, event, root, limn, emitter, k, Backbone, BaseView, BaseModel, BaseList, ChartType, DygraphsChartType, Graph, GraphList, GraphDisplayView, GraphEditView, GraphListView, DashboardView, Dashboard, LimnApp, _ref, _, _i, _len;
+EventEmitter = require('events').EventEmitter;
+_ref = require('./util'), _ = _ref._, op = _ref.op, event = _ref.event, root = _ref.root;
+limn = exports;
+emitter = limn.__emitter__ = new event.ReadyEmitter();
+for (_i = 0, _len = (_ref = ['on', 'addListener', 'off', 'removeListener', 'emit', 'trigger', 'once', 'removeAllListeners']).length; _i < _len; ++_i) {
+ k = _ref[_i];
+ limn[k] = emitter[k].bind(emitter);
+}
+limn.mount = function(path){
+ var mnt, _ref;
+ mnt = ((_ref = limn.config) != null ? _ref.mount : void 8) || '/';
+ return (mnt !== '/' ? mnt : '') + path;
+};
+Backbone = require('backbone');
+_ref = limn.base = require('./base'), BaseView = _ref.BaseView, BaseModel = _ref.BaseModel, BaseList = _ref.BaseList;
+_ref = limn.chart = require('./chart'), ChartType = _ref.ChartType, DygraphsChartType = _ref.DygraphsChartType;
+_ref = limn.graph = require('./graph'), Graph = _ref.Graph, GraphList = _ref.GraphList, GraphDisplayView = _ref.GraphDisplayView, GraphEditView = _ref.GraphEditView, GraphListView = _ref.GraphListView;
+_ref = limn.dashboard = require('./dashboard'), DashboardView = _ref.DashboardView, Dashboard = _ref.Dashboard;
+/**
+ * @class Sets up root application, automatically attaching to an existing element
+ * found at `appSelector` and delegating to the appropriate view.
+ * @extends Backbone.Router
+ */
+LimnApp = limn.LimnApp = Backbone.Router.extend({
+ appSelector: '#content .inner',
+ routes: {
+ 'graphs/(new|edit)': 'newGraph',
+ 'graphs/:graphId/edit': 'editGraph',
+ 'graphs/:graphId': 'showGraph',
+ 'graphs': 'listGraphs',
+ 'dashboards/(new|edit)': 'newDashboard',
+ 'dashboards/:dashId/edit': 'editDashboard',
+ 'dashboards/:dashId': 'showDashboard',
+ 'dashboards': 'listDashboards'
+ }
+ /**
+ * @constructor
+ */,
+ constructor: (function(){
+ function LimnApp(config){
+ var that;
+ this.config = config != null
+ ? config
+ : {};
+ if (that = config.appSelector) {
+ this.appSelector = that;
+ }
+ this.el = config.el || (config.el = jQuery(this.appSelector)[0]);
+ this.$el = jQuery(this.el);
+ Backbone.Router.call(this, config);
+ return this;
+ }
+ return LimnApp;
+ }()),
+ initialize: function(){
+ var _this = this;
+ jQuery(function(){
+ return _this.setup();
+ });
+ return this;
+ },
+ setup: function(){
+ this.route(/^(?:[\?].*)?$/, 'home');
+ return Backbone.history.start({
+ pushState: true,
+ root: this.config.mount
+ });
+ },
+ processData: function(id, data){
+ data == null && (data = {});
+ if (!(id && _(['edit', 'new']).contains(id))) {
+ data.id = data.slug = id;
+ }
+ return data;
+ }
+ /* * * * Routes * * * */,
+ home: function(){
+ return this.showDashboard('reportcard');
+ },
+ createGraphModel: function(id){
+ var data, graph;
+ data = this.processData(id);
+ return graph = new Graph(data, {
+ parse: true
+ });
+ },
+ newGraph: function(){
+ return this.editGraph();
+ },
+ editGraph: function(id){
+ this.model = this.createGraphModel(id);
+ return this.view = new GraphEditView({
+ model: this.model
+ }).attach(this.el);
+ },
+ showGraph: function(id){
+ this.model = this.createGraphModel(id);
+ return this.view = new GraphDisplayView({
+ model: this.model
+ }).attach(this.el);
+ },
+ listGraphs: function(){
+ this.collection = new GraphList();
+ return this.view = new GraphListView({
+ collection: this.collection
+ }).attach(this.el);
+ },
+ createDashboardModel: function(id){
+ var data, dashboard;
+ data = this.processData(id);
+ return dashboard = new Dashboard(data, {
+ parse: true
+ });
+ },
+ newDashboard: function(){
+ return console.error('newDashboard!?');
+ },
+ editDashboard: function(id){
+ return console.error('editDashboard!?');
+ },
+ showDashboard: function(id){
+ this.model = this.createDashboardModel(id);
+ return this.view = new DashboardView({
+ model: this.model
+ }).attach(this.el);
+ },
+ listDashboards: function(){
+ return console.error('listDashboards!?');
+ },
+ getClassName: function(){
+ return (this.constructor.name || this.constructor.displayName) + "";
+ },
+ toString: function(){
+ return this.getClassName() + "()";
+ }
+});
+__import(LimnApp, {
+ findConfig: function(){
+ var config;
+ config = root.limn_config || {};
+ config.mount || (config.mount = "/");
+ return config;
+ },
+ main: (function(){
+ function limnMain(){
+ var config;
+ config = limn.config || (limn.config = LimnApp.findConfig());
+ if (!config.libOnly) {
+ limn.app || (limn.app = new LimnApp(config));
+ }
+ return limn.emit('main', limn.app);
+ }
+ return limnMain;
+ }())
+});
+jQuery(LimnApp.main);
+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
+require.define('/node_modules/limn/client.js', function(require, module, exports, __dirname, __filename, undefined){
+
+var EventEmitter, op, event, root, limn, emitter, k, Backbone, BaseView, BaseModel, BaseList, ChartType, DygraphsChartType, Graph, GraphList, GraphDisplayView, GraphEditView, GraphListView, DashboardView, Dashboard, LimnApp, _ref, _, _i, _len;
+EventEmitter = require('events').EventEmitter;
+_ref = require('./util'), _ = _ref._, op = _ref.op, event = _ref.event, root = _ref.root;
+limn = exports;
+emitter = limn.__emitter__ = new event.ReadyEmitter();
+for (_i = 0, _len = (_ref = ['on', 'addListener', 'off', 'removeListener', 'emit', 'trigger', 'once', 'removeAllListeners']).length; _i < _len; ++_i) {
+ k = _ref[_i];
+ limn[k] = emitter[k].bind(emitter);
+}
+limn.mount = function(path){
+ var mnt, _ref;
+ mnt = ((_ref = limn.config) != null ? _ref.mount : void 8) || '/';
+ return (mnt !== '/' ? mnt : '') + path;
+};
+Backbone = require('backbone');
+_ref = limn.base = require('./base'), BaseView = _ref.BaseView, BaseModel = _ref.BaseModel, BaseList = _ref.BaseList;
+_ref = limn.chart = require('./chart'), ChartType = _ref.ChartType, DygraphsChartType = _ref.DygraphsChartType;
+_ref = limn.graph = require('./graph'), Graph = _ref.Graph, GraphList = _ref.GraphList, GraphDisplayView = _ref.GraphDisplayView, GraphEditView = _ref.GraphEditView, GraphListView = _ref.GraphListView;
+_ref = limn.dashboard = require('./dashboard'), DashboardView = _ref.DashboardView, Dashboard = _ref.Dashboard;
+/**
+ * @class Sets up root application, automatically attaching to an existing element
+ * found at `appSelector` and delegating to the appropriate view.
+ * @extends Backbone.Router
+ */
+LimnApp = limn.LimnApp = Backbone.Router.extend({
+ appSelector: '#content .inner',
+ routes: {
+ 'graphs/(new|edit)': 'newGraph',
+ 'graphs/:graphId/edit': 'editGraph',
+ 'graphs/:graphId': 'showGraph',
+ 'graphs': 'listGraphs',
+ 'dashboards/(new|edit)': 'newDashboard',
+ 'dashboards/:dashId/edit': 'editDashboard',
+ 'dashboards/:dashId': 'showDashboard',
+ 'dashboards': 'listDashboards'
+ }
+ /**
+ * @constructor
+ */,
+ constructor: (function(){
+ function LimnApp(config){
+ var that;
+ this.config = config != null
+ ? config
+ : {};
+ if (that = config.appSelector) {
+ this.appSelector = that;
+ }
+ this.el = config.el || (config.el = jQuery(this.appSelector)[0]);
+ this.$el = jQuery(this.el);
+ Backbone.Router.call(this, config);
+ return this;
+ }
+ return LimnApp;
+ }()),
+ initialize: function(){
+ var _this = this;
+ jQuery(function(){
+ return _this.setup();
+ });
+ return this;
+ },
+ setup: function(){
+ this.route(/^(?:[\?].*)?$/, 'home');
+ return Backbone.history.start({
+ pushState: true,
+ root: this.config.mount
+ });
+ },
+ processData: function(id, data){
+ data == null && (data = {});
+ if (!(id && _(['edit', 'new']).contains(id))) {
+ data.id = data.slug = id;
+ }
+ return data;
+ }
+ /* * * * Routes * * * */,
+ home: function(){
+ return this.showDashboard('reportcard');
+ },
+ createGraphModel: function(id){
+ var data, graph;
+ data = this.processData(id);
+ return graph = new Graph(data, {
+ parse: true
+ });
+ },
+ newGraph: function(){
+ return this.editGraph();
+ },
+ editGraph: function(id){
+ this.model = this.createGraphModel(id);
+ return this.view = new GraphEditView({
+ model: this.model
+ }).attach(this.el);
+ },
+ showGraph: function(id){
+ this.model = this.createGraphModel(id);
+ return this.view = new GraphDisplayView({
+ model: this.model
+ }).attach(this.el);
+ },
+ listGraphs: function(){
+ this.collection = new GraphList();
+ return this.view = new GraphListView({
+ collection: this.collection
+ }).attach(this.el);
+ },
+ createDashboardModel: function(id){
+ var data, dashboard;
+ data = this.processData(id);
+ return dashboard = new Dashboard(data, {
+ parse: true
+ });
+ },
+ newDashboard: function(){
+ return console.error('newDashboard!?');
+ },
+ editDashboard: function(id){
+ return console.error('editDashboard!?');
+ },
+ showDashboard: function(id){
+ this.model = this.createDashboardModel(id);
+ return this.view = new DashboardView({
+ model: this.model
+ }).attach(this.el);
+ },
+ listDashboards: function(){
+ return console.error('listDashboards!?');
+ },
+ getClassName: function(){
+ return (this.constructor.name || this.constructor.displayName) + "";
+ },
+ toString: function(){
+ return this.getClassName() + "()";
+ }
+});
+__import(LimnApp, {
+ findConfig: function(){
+ var config;
+ config = root.limn_config || {};
+ config.mount || (config.mount = "/");
+ return config;
+ },
+ main: (function(){
+ function limnMain(){
+ var config;
+ config = limn.config || (limn.config = LimnApp.findConfig());
+ if (!config.libOnly) {
+ limn.app || (limn.app = new LimnApp(config));
+ }
+ return limn.emit('main', limn.app);
+ }
+ return limnMain;
+ }())
+});
+jQuery(LimnApp.main);
+function __import(obj, src){
+ var own = {}.hasOwnProperty;
+ for (var key in src) if (own.call(src, key)) obj[key] = src[key];
+ return obj;
+}
+
+});
-var op, TimeSeriesData, CSVData, BaseModel, BaseList, ModelCache, Metric, MetricList, DataSource, DataSourceList, ALL_SOURCES, sourceCache, _ref, _;
+var limn, op, TimeSeriesData, CSVData, BaseModel, BaseList, ModelCache, Metric, MetricList, DataSource, DataSourceList, ALL_SOURCES, sourceCache, _ref, _;
+limn = require('../client');
_ref = require('../util'), _ = _ref._, op = _ref.op;
_ref = require('../util/timeseries'), TimeSeriesData = _ref.TimeSeriesData, CSVData = _ref.CSVData;
_ref = require('../base'), BaseModel = _ref.BaseModel, BaseList = _ref.BaseList, ModelCache = _ref.ModelCache;
ready: false,
cache: ALL_SOURCES
});
-$.getJSON('/datasources/all', function(data){
- ALL_SOURCES.reset(_.map(data, op.I));
- return sourceCache.triggerReady();
+limn.on('main', function(){
+ return $.getJSON(limn.mount('/datasources/all'), function(data){
+ ALL_SOURCES.reset(_.map(data, op.I));
+ return sourceCache.triggerReady();
+ });
});
DataSource.getAllSources = function(){
return ALL_SOURCES;
require.define('/node_modules/limn/data/datasource-model.js', function(require, module, exports, __dirname, __filename, undefined){
-var op, TimeSeriesData, CSVData, BaseModel, BaseList, ModelCache, Metric, MetricList, DataSource, DataSourceList, ALL_SOURCES, sourceCache, _ref, _;
+var limn, op, TimeSeriesData, CSVData, BaseModel, BaseList, ModelCache, Metric, MetricList, DataSource, DataSourceList, ALL_SOURCES, sourceCache, _ref, _;
+limn = require('../client');
_ref = require('../util'), _ = _ref._, op = _ref.op;
_ref = require('../util/timeseries'), TimeSeriesData = _ref.TimeSeriesData, CSVData = _ref.CSVData;
_ref = require('../base'), BaseModel = _ref.BaseModel, BaseList = _ref.BaseList, ModelCache = _ref.ModelCache;
ready: false,
cache: ALL_SOURCES
});
-$.getJSON('/datasources/all', function(data){
- ALL_SOURCES.reset(_.map(data, op.I));
- return sourceCache.triggerReady();
+limn.on('main', function(){
+ return $.getJSON(limn.mount('/datasources/all'), function(data){
+ ALL_SOURCES.reset(_.map(data, op.I));
+ return sourceCache.triggerReady();
+ });
});
DataSource.getAllSources = function(){
return ALL_SOURCES;
-var Seq, Cascade, BaseModel, BaseList, ModelCache, ChartType, DataSet, root, Graph, GraphList, _ref, _;
+var Seq, limn, Cascade, BaseModel, BaseList, ModelCache, ChartType, DataSet, root, Graph, GraphList, _ref, _;
Seq = require('seq');
+limn = require('../client');
_ref = require('../util'), _ = _ref._, Cascade = _ref.Cascade;
_ref = require('../base'), BaseModel = _ref.BaseModel, BaseList = _ref.BaseList, ModelCache = _ref.ModelCache;
ChartType = require('../chart').ChartType;
};
},
url: function(){
- return this.urlRoot + "/" + this.get('slug') + ".json";
+ return limn.mount(this.urlRoot) + "/" + this.get('slug') + ".json";
},
constructor: (function(){
function Graph(attributes, opts){
toURL: function(action){
var slug, path;
slug = this.get('slug');
- path = _.compact([this.urlRoot, slug, action]).join('/');
+ path = limn.mount(_.compact([this.urlRoot, slug, action])).join('/');
return path + "?" + this.toKV({
keepSlug: !!slug
});
* @returns {String} Path portion of slug URL, e.g. /graphs/:slug
*/,
toLink: function(){
- return this.urlRoot + "/" + this.get('slug');
+ return limn.mount(this.urlRoot) + "/" + this.get('slug');
}
/**
* @returns {String} Permalinked URI, e.g. http://reportcard.wmflabs.org/:slug
require.define('/node_modules/limn/graph/graph-model.js', function(require, module, exports, __dirname, __filename, undefined){
-var Seq, Cascade, BaseModel, BaseList, ModelCache, ChartType, DataSet, root, Graph, GraphList, _ref, _;
+var Seq, limn, Cascade, BaseModel, BaseList, ModelCache, ChartType, DataSet, root, Graph, GraphList, _ref, _;
Seq = require('seq');
+limn = require('../client');
_ref = require('../util'), _ = _ref._, Cascade = _ref.Cascade;
_ref = require('../base'), BaseModel = _ref.BaseModel, BaseList = _ref.BaseList, ModelCache = _ref.ModelCache;
ChartType = require('../chart').ChartType;
};
},
url: function(){
- return this.urlRoot + "/" + this.get('slug') + ".json";
+ return limn.mount(this.urlRoot) + "/" + this.get('slug') + ".json";
},
constructor: (function(){
function Graph(attributes, opts){
toURL: function(action){
var slug, path;
slug = this.get('slug');
- path = _.compact([this.urlRoot, slug, action]).join('/');
+ path = limn.mount(_.compact([this.urlRoot, slug, action])).join('/');
return path + "?" + this.toKV({
keepSlug: !!slug
});
* @returns {String} Path portion of slug URL, e.g. /graphs/:slug
*/,
toLink: function(){
- return this.urlRoot + "/" + this.get('slug');
+ return limn.mount(this.urlRoot) + "/" + this.get('slug');
}
/**
* @returns {String} Permalinked URI, e.g. http://reportcard.wmflabs.org/:slug
-var limn, Backbone, op, root, BaseView, BaseModel, BaseList, ChartType, DygraphsChartType, Graph, GraphList, GraphDisplayView, GraphEditView, GraphListView, DashboardView, Dashboard, LimnApp, _ref, _;
+var EventEmitter, op, event, root, limn, emitter, k, Backbone, BaseView, BaseModel, BaseList, ChartType, DygraphsChartType, Graph, GraphList, GraphDisplayView, GraphEditView, GraphListView, DashboardView, Dashboard, LimnApp, _ref, _, _i, _len;
+EventEmitter = require('events').EventEmitter;
+_ref = require('./util'), _ = _ref._, op = _ref.op, event = _ref.event, root = _ref.root;
limn = exports;
+emitter = limn.__emitter__ = new event.ReadyEmitter();
+for (_i = 0, _len = (_ref = ['on', 'addListener', 'off', 'removeListener', 'emit', 'trigger', 'once', 'removeAllListeners']).length; _i < _len; ++_i) {
+ k = _ref[_i];
+ limn[k] = emitter[k].bind(emitter);
+}
+limn.mount = function(path){
+ var mnt, _ref;
+ mnt = ((_ref = limn.config) != null ? _ref.mount : void 8) || '/';
+ return (mnt !== '/' ? mnt : '') + path;
+};
Backbone = require('backbone');
-_ref = limn.util = require('./util'), _ = _ref._, op = _ref.op, root = _ref.root;
_ref = limn.base = require('./base'), BaseView = _ref.BaseView, BaseModel = _ref.BaseModel, BaseList = _ref.BaseList;
_ref = limn.chart = require('./chart'), ChartType = _ref.ChartType, DygraphsChartType = _ref.DygraphsChartType;
_ref = limn.graph = require('./graph'), Graph = _ref.Graph, GraphList = _ref.GraphList, GraphDisplayView = _ref.GraphDisplayView, GraphEditView = _ref.GraphEditView, GraphListView = _ref.GraphListView;
var config;
config = limn.config || (limn.config = LimnApp.findConfig());
if (!config.libOnly) {
- return limn.app || (limn.app = new LimnApp(config));
+ limn.app || (limn.app = new LimnApp(config));
}
+ return limn.emit('main', limn.app);
}
return limnMain;
}())
require.define('/node_modules/limn/limn.js', function(require, module, exports, __dirname, __filename, undefined){
-var limn, Backbone, op, root, BaseView, BaseModel, BaseList, ChartType, DygraphsChartType, Graph, GraphList, GraphDisplayView, GraphEditView, GraphListView, DashboardView, Dashboard, LimnApp, _ref, _;
+var EventEmitter, op, event, root, limn, emitter, k, Backbone, BaseView, BaseModel, BaseList, ChartType, DygraphsChartType, Graph, GraphList, GraphDisplayView, GraphEditView, GraphListView, DashboardView, Dashboard, LimnApp, _ref, _, _i, _len;
+EventEmitter = require('events').EventEmitter;
+_ref = require('./util'), _ = _ref._, op = _ref.op, event = _ref.event, root = _ref.root;
limn = exports;
+emitter = limn.__emitter__ = new event.ReadyEmitter();
+for (_i = 0, _len = (_ref = ['on', 'addListener', 'off', 'removeListener', 'emit', 'trigger', 'once', 'removeAllListeners']).length; _i < _len; ++_i) {
+ k = _ref[_i];
+ limn[k] = emitter[k].bind(emitter);
+}
+limn.mount = function(path){
+ var mnt, _ref;
+ mnt = ((_ref = limn.config) != null ? _ref.mount : void 8) || '/';
+ return (mnt !== '/' ? mnt : '') + path;
+};
Backbone = require('backbone');
-_ref = limn.util = require('./util'), _ = _ref._, op = _ref.op, root = _ref.root;
_ref = limn.base = require('./base'), BaseView = _ref.BaseView, BaseModel = _ref.BaseModel, BaseList = _ref.BaseList;
_ref = limn.chart = require('./chart'), ChartType = _ref.ChartType, DygraphsChartType = _ref.DygraphsChartType;
_ref = limn.graph = require('./graph'), Graph = _ref.Graph, GraphList = _ref.GraphList, GraphDisplayView = _ref.GraphDisplayView, GraphEditView = _ref.GraphEditView, GraphListView = _ref.GraphListView;
var config;
config = limn.config || (limn.config = LimnApp.findConfig());
if (!config.libOnly) {
- return limn.app || (limn.app = new LimnApp(config));
+ limn.app || (limn.app = new LimnApp(config));
}
+ return limn.emit('main', limn.app);
}
return limnMain;
}())
this.set('limn options', opts);
mkdirp(opts.dataDir);
this.configure(function(){
+ var view_opts;
this.set('views', WWW);
this.set('view engine', 'jade');
- this.set('view options', __import({
+ view_opts = __import({
layout: false,
+ config: this.set('limn options'),
version: REV,
IS_DEV: IS_DEV,
- IS_PROD: IS_PROD
- }, require('./view-helpers')));
+ IS_PROD: IS_PROD,
+ REV: REV
+ }, require('./view-helpers'));
+ view_opts.__defineGetter__('mount', function(){
+ return app.route || '/';
+ });
+ this.set('view options', view_opts);
this.use(require('./reqinfo')({}));
this.use(express.bodyParser());
this.use(express.methodOverride());
/**
* Load Limn middleware
*/
-app.use(limn = app.limn = LimnMiddleware({
+limn = app.limn = LimnMiddleware({
varDir: './var',
dataDir: './var/data',
proxy: {
enabled: true,
whitelist: /.*/
}
-}));
+});
+app.use(limn);
app.use(express.errorHandler({
dumpExceptions: true,
showStack: true
-var op, root, backbone, parser, Cascade, _, _ref, __slice = [].slice;
+var op, root, event, backbone, parser, Cascade, _, _ref, __slice = [].slice;
_ = exports._ = require('./underscore');
op = exports.op = require('./op');
root = exports.root = function(){
return _results;
};
}
-__import(exports, require('./event'));
+event = exports.event = require('./event');
+__import(exports, event);
backbone = exports.backbone = require('./backbone');
parser = exports.parser = require('./parser');
Cascade = exports.Cascade = require('./cascade');
require.define('/node_modules/limn/util/index.js', function(require, module, exports, __dirname, __filename, undefined){
-var op, root, backbone, parser, Cascade, _, _ref, __slice = [].slice;
+var op, root, event, backbone, parser, Cascade, _, _ref, __slice = [].slice;
_ = exports._ = require('./underscore');
op = exports.op = require('./op');
root = exports.root = function(){
return _results;
};
}
-__import(exports, require('./event'));
+event = exports.event = require('./event');
+__import(exports, event);
backbone = exports.backbone = require('./backbone');
parser = exports.parser = require('./parser');
Cascade = exports.Cascade = require('./cascade');
-var op, BaseModel, BaseList, BaseView, Mixin, Parsers, ParserMixin, ParsingModel, ParsingList, ParsingView, _, _ref;
+var op, Parsers, _;
_ = require('./underscore');
op = require('./op');
-_ref = require('../base'), BaseModel = _ref.BaseModel, BaseList = _ref.BaseList, BaseView = _ref.BaseView, Mixin = _ref.Mixin;
/**
* @namespace Parsers by type.
*/
}
}
};
-Parsers.parseNumber = Parsers.parseFloat;
-/**
- * @class Methods for a class to select parsers by type reflection.
- * @mixin
- */
-exports.ParserMixin = ParserMixin = (function(superclass){
- ParserMixin.displayName = 'ParserMixin';
- var prototype = __extend(ParserMixin, superclass).prototype, constructor = ParserMixin;
- __import(ParserMixin.prototype, Parsers);
- function ParserMixin(target){
- return Mixin.call(ParserMixin, target);
- }
- prototype.parseValue = function(v, type){
- return this.getParser(type)(v);
- };
- prototype.getParser = function(type){
- var fn, t, _i, _ref, _len;
- type == null && (type = 'String');
- fn = this["parse" + type];
- if (typeof fn === 'function') {
- return fn;
- }
- type = _(String(type).toLowerCase());
- for (_i = 0, _len = (_ref = ['Integer', 'Float', 'Number', 'Boolean', 'Object', 'Array', 'Function']).length; _i < _len; ++_i) {
- t = _ref[_i];
- if (type.startsWith(t.toLowerCase())) {
- return this["parse" + t];
- }
- }
- return this.defaultParser || this.parseString;
- };
- prototype.getParserFromExample = function(v){
- var type;
- if (v == null) {
- return null;
- }
- type = typeof v;
- if (type !== 'object') {
- return this.getParser(type);
- } else if (_.isArray(v)) {
- return this.getParser('Array');
- } else {
- return this.getParser('Object');
- }
- };
- return ParserMixin;
-}(Mixin));
-/**
- * @class Basic model which mixes in the ParserMixin.
- * @extends BaseModel
- * @borrows ParserMixin
- */
-ParsingModel = exports.ParsingModel = BaseModel.extend(ParserMixin.mix({
- constructor: (function(){
- function ParsingModel(){
- return BaseModel.apply(this, arguments);
- }
- return ParsingModel;
- }())
-}));
-/**
- * @class Basic collection which mixes in the ParserMixin.
- * @extends BaseList
- * @borrows ParserMixin
- */
-ParsingList = exports.ParsingList = BaseList.extend(ParserMixin.mix({
- constructor: (function(){
- function ParsingList(){
- return BaseList.apply(this, arguments);
- }
- return ParsingList;
- }())
-}));
-/**
- * @class Basic view which mixes in the ParserMixin.
- * @extends BaseView
- * @borrows ParserMixin
- */
-ParsingView = exports.ParsingView = BaseView.extend(ParserMixin.mix({
- constructor: (function(){
- function ParsingView(){
- return BaseView.apply(this, arguments);
- }
- return ParsingView;
- }())
-}));
-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
+Parsers.parseNumber = Parsers.parseFloat;
\ No newline at end of file
require.define('/node_modules/limn/util/parser.js', function(require, module, exports, __dirname, __filename, undefined){
-var op, BaseModel, BaseList, BaseView, Mixin, Parsers, ParserMixin, ParsingModel, ParsingList, ParsingView, _, _ref;
+var op, Parsers, _;
_ = require('./underscore');
op = require('./op');
-_ref = require('../base'), BaseModel = _ref.BaseModel, BaseList = _ref.BaseList, BaseView = _ref.BaseView, Mixin = _ref.Mixin;
/**
* @namespace Parsers by type.
*/
}
};
Parsers.parseNumber = Parsers.parseFloat;
-/**
- * @class Methods for a class to select parsers by type reflection.
- * @mixin
- */
-exports.ParserMixin = ParserMixin = (function(superclass){
- ParserMixin.displayName = 'ParserMixin';
- var prototype = __extend(ParserMixin, superclass).prototype, constructor = ParserMixin;
- __import(ParserMixin.prototype, Parsers);
- function ParserMixin(target){
- return Mixin.call(ParserMixin, target);
- }
- prototype.parseValue = function(v, type){
- return this.getParser(type)(v);
- };
- prototype.getParser = function(type){
- var fn, t, _i, _ref, _len;
- type == null && (type = 'String');
- fn = this["parse" + type];
- if (typeof fn === 'function') {
- return fn;
- }
- type = _(String(type).toLowerCase());
- for (_i = 0, _len = (_ref = ['Integer', 'Float', 'Number', 'Boolean', 'Object', 'Array', 'Function']).length; _i < _len; ++_i) {
- t = _ref[_i];
- if (type.startsWith(t.toLowerCase())) {
- return this["parse" + t];
- }
- }
- return this.defaultParser || this.parseString;
- };
- prototype.getParserFromExample = function(v){
- var type;
- if (v == null) {
- return null;
- }
- type = typeof v;
- if (type !== 'object') {
- return this.getParser(type);
- } else if (_.isArray(v)) {
- return this.getParser('Array');
- } else {
- return this.getParser('Object');
- }
- };
- return ParserMixin;
-}(Mixin));
-/**
- * @class Basic model which mixes in the ParserMixin.
- * @extends BaseModel
- * @borrows ParserMixin
- */
-ParsingModel = exports.ParsingModel = BaseModel.extend(ParserMixin.mix({
- constructor: (function(){
- function ParsingModel(){
- return BaseModel.apply(this, arguments);
- }
- return ParsingModel;
- }())
-}));
-/**
- * @class Basic collection which mixes in the ParserMixin.
- * @extends BaseList
- * @borrows ParserMixin
- */
-ParsingList = exports.ParsingList = BaseList.extend(ParserMixin.mix({
- constructor: (function(){
- function ParsingList(){
- return BaseList.apply(this, arguments);
- }
- return ParsingList;
- }())
-}));
-/**
- * @class Basic view which mixes in the ParserMixin.
- * @extends BaseView
- * @borrows ParserMixin
- */
-ParsingView = exports.ParsingView = BaseView.extend(ParserMixin.mix({
- constructor: (function(){
- function ParsingView(){
- return BaseView.apply(this, arguments);
- }
- return ParsingView;
- }())
-}));
-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;
-}
});
Backbone = require 'backbone'
+limn = require '../client'
{ _, op,
} = require '../util'
{ BaseBackboneMixin, mixinBase,
### Accessors
url: ->
- "#{@urlRoot}/#{@get('id')}.json"
+ "#{limn.mount @urlRoot}/#{@get('id')}.json"
has: (key) ->
@get(key)?
@models.map -> it.id or it.get('id') or it.cid
url: ->
+ root = limn.mount @urlRoot
id = @get('id') or @get('slug')
if id
- "#{@urlRoot}/#id.json"
+ "#root/#id.json"
else
- "#{@urlRoot}.json"
+ "#root.json"
load: ->
@__superclass__ = @..superclass
@__apply_bind__()
super()
- @__class__.emit 'new', this
+ @__class__.emit? 'new', this
### Auto-Bound methods
_.bindAll this, ...names if names.length
+ ### Misc
getClassName: ->
"#{@..name or @..displayName}"
@extended = (Subclass) ->
# copy over all class methods, including this
- for own k, v in this
- Subclass[k] = v if typeof v is 'function'
+ for k, v in this
+ Subclass[k] = v if typeof v is 'function' and not _.contains <[ apply call constructor toString ]>, k
Subclass.__super__ = @::
Subclass
data_binding = require './data-binding'
exports import mixins import models import views \
import cache import cascading import data_binding
+
+parser_mixin = require './parser-mixin'
+exports import parser_mixin
--- /dev/null
+{ Parsers,
+} = require '../util/parser'
+{ BaseModel, BaseList, BaseView, Mixin,
+} = require '../base'
+
+exports.Parsers = Parsers
+
+/**
+ * @class Methods for a class to select parsers by type reflection.
+ * @mixin
+ */
+class exports.ParserMixin extends Mixin
+ this:: import Parsers
+
+ (target) ->
+ return Mixin.call ParserMixin, target
+
+
+ # XXX: So I'm meh about mixing in the Parsers dictionary.
+ #
+ # - Pros: mixing in `parseXXX()` methods makes it easy to
+ # override in the target class.
+ # - Cons: `parse()` is a Backbone method, which bit me once
+ # already (hence `parseValue()`), so conflicts aren't unlikely.
+ #
+ # Other ideas:
+ # - Parsers live at `@__parsers__`, and each instance gets its own clone
+ # -> Parser lookup uses a Cascade from the object. (Why not just use prototype, tho?)
+
+ parseValue: (v, type) ->
+ @getParser(type)(v)
+
+ getParser: (type='String') ->
+ # If this is a known type and we have a parser for it, return that
+ fn = @["parse#type"]
+ return fn if typeof fn is 'function'
+
+ # Handle compound/optional types
+ # XXX: handle 'or' by returning an array of parsers?
+ type = _ String(type).toLowerCase()
+ for t of <[ Integer Float Number Boolean Object Array Function ]>
+ if type.startsWith t.toLowerCase()
+ return @["parse#t"]
+ @defaultParser or @parseString
+
+ getParserFromExample: (v) ->
+ return null unless v?
+ type = typeof v
+
+ if type is not 'object'
+ @getParser type
+ else if _.isArray v
+ @getParser 'Array'
+ else
+ @getParser 'Object'
+
+
+
+
+/**
+ * @class Basic model which mixes in the ParserMixin.
+ * @extends BaseModel
+ * @borrows ParserMixin
+ */
+ParsingModel = exports.ParsingModel = BaseModel.extend ParserMixin.mix do
+ constructor: function ParsingModel then BaseModel ...
+
+
+/**
+ * @class Basic collection which mixes in the ParserMixin.
+ * @extends BaseList
+ * @borrows ParserMixin
+ */
+ParsingList = exports.ParsingList = BaseList.extend ParserMixin.mix do
+ constructor: function ParsingList then BaseList ...
+
+
+/**
+ * @class Basic view which mixes in the ParserMixin.
+ * @extends BaseView
+ * @borrows ParserMixin
+ */
+ParsingView = exports.ParsingView = BaseView.extend ParserMixin.mix do
+ constructor: function ParsingView then BaseView ...
+
+
moment = require 'moment'
Backbone = require 'backbone'
+limn = require '../client'
{ _, op,
} = require '../util'
{ ReadyEmitter,
} = require '../util/event'
{ Parsers, ParserMixin,
-} = require '../util/parser'
+} = require '../base'
*/
loadSpec: ->
return this if @ready
- proto = @constructor::
+ proto = @constructor::
+ url = (if limn.config.mount is not '/' then limn.config.mount else '') + @SPEC_URL
jQuery.ajax do
- url : @SPEC_URL
+ url : url
dataType : 'json'
- success : (spec) ~>
+ success : (spec) ~>
proto.spec = spec
proto.options_ordered = spec
proto.options = _.synthesize spec, -> [it.name, it]
{ _, op,
} = require '../../util'
-{ Parsers, ParserMixin, ParsingModel, ParsingView,
-} = require '../../util/parser'
-{ BaseModel, BaseList,
+{
+ BaseModel, BaseList,
+ Parsers, ParserMixin, ParsingModel, ParsingView,
} = require '../../base'
ColorBrewer = require 'colorbrewer'
{ _, op,
-} = require '../../../util'
+} = require '../../util'
{ ChartType,
-} = require '../../chart-type'
+} = require '../chart-type'
{ D3ChartElement,
-} = require './d3-chart-element'
+} = require './d3/d3-chart-element'
root = do -> this
-_ = require '../../../util/underscore'
+_ = require '../../util/underscore'
{ ChartType,
-} = require '../../chart-type'
+} = require '../chart-type'
class exports.DygraphsChartType extends ChartType
+{EventEmitter} = require 'events'
+
+{ _, op, event, root,
+} = require './util'
+
+# Decorate root limn namespace object with EventEmitter methods
limn = exports
+emitter = limn.__emitter__ = new event.ReadyEmitter()
+for k of <[ on addListener off removeListener emit trigger once removeAllListeners ]>
+ limn[k] = emitter[k].bind emitter
+
+limn.mount = (path) ->
+ mnt = limn.config?.mount or '/'
+ (if mnt is not '/' then mnt else '') + path
+
Backbone = require 'backbone'
-{ _, op, root,
-} = limn.util = require './util'
{ BaseView, BaseModel, BaseList,
} = limn.base = require './base'
{ ChartType, DygraphsChartType,
main : function limnMain
config = limn.config or= LimnApp.findConfig()
limn.app or= new LimnApp config unless config.libOnly
+ limn.emit 'main', limn.app
jQuery LimnApp.main
-
+limn = require '../client'
{ _, op,
} = require '../util'
{ TimeSeriesData, CSVData,
sourceCache = new ModelCache DataSource, {-ready, cache:ALL_SOURCES}
# Fetch all DataSources
-$.getJSON '/datasources/all', (data) ->
- ALL_SOURCES.reset _.map data, op.I
- sourceCache.triggerReady()
+limn.on 'main', ->
+ $.getJSON limn.mount('/datasources/all'), (data) ->
+ ALL_SOURCES.reset _.map data, op.I
+ sourceCache.triggerReady()
DataSource.getAllSources = ->
ALL_SOURCES
Seq = require 'seq'
+limn = require '../client'
{ _, Cascade,
} = require '../util'
{ BaseModel, BaseList, ModelCache,
options : {}
url: ->
- "#{@urlRoot}/#{@get('slug')}.json"
+ "#{limn.mount @urlRoot}/#{@get('slug')}.json"
*/
toURL: (action) ->
slug = @get 'slug'
- path = _.compact [ @urlRoot, slug, action ] .join '/'
+ path = limn.mount _.compact [ @urlRoot, slug, action ] .join '/'
"#path?#{@toKV { keepSlug: !!slug }}"
/**
* @returns {String} Path portion of slug URL, e.g. /graphs/:slug
*/
toLink: ->
- "#{@urlRoot}/#{@get('slug')}"
+ "#{limn.mount @urlRoot}/#{@get('slug')}"
/**
* @returns {String} Permalinked URI, e.g. http://reportcard.wmflabs.org/:slug
#!/usr/bin/env node
var coco = require('coco')
-, coffee = require('coffee-script')
, server = require('./server')
;
@configure ->
@set 'views', WWW
@set 'view engine', 'jade'
- @set 'view options', {
+
+ view_opts = {
layout : false
+ config : @set('limn options')
version : REV
- IS_DEV, IS_PROD
+ IS_DEV, IS_PROD, REV
} import require './view-helpers'
- # @helpers require './view-helpers'
+ view_opts.__defineGetter__ 'mount', -> app.route or '/'
+ @set 'view options', view_opts
# Parse URL, fiddle
@use require('./reqinfo')({})
/**
* Load Limn middleware
*/
-app.use limn = app.limn = LimnMiddleware do
+limn = app.limn = LimnMiddleware do
varDir : './var'
dataDir : './var/data'
proxy :
enabled : true
whitelist : /.*/
+app.use limn
+# app.use '/vis', limn
+
# show exceptions, pretty stack traces ### FIXME
app.use express.errorHandler { +dumpExceptions, +showStack }
jQuery(el)[method] ...args
-exports import require './event'
+event = exports.event = require './event'
+exports import event
backbone = exports.backbone = require './backbone'
parser = exports.parser = require './parser'
_ = require './underscore'
op = require './op'
-{ BaseModel, BaseList, BaseView, Mixin,
-} = require '../base'
/**
# Aliases
Parsers.parseNumber = Parsers.parseFloat
-
-/**
- * @class Methods for a class to select parsers by type reflection.
- * @mixin
- */
-class exports.ParserMixin extends Mixin
- this:: import Parsers
-
- (target) ->
- return Mixin.call ParserMixin, target
-
-
- # XXX: So I'm meh about mixing in the Parsers dictionary.
- #
- # - Pros: mixing in `parseXXX()` methods makes it easy to
- # override in the target class.
- # - Cons: `parse()` is a Backbone method, which bit me once
- # already (hence `parseValue()`), so conflicts aren't unlikely.
- #
- # Other ideas:
- # - Parsers live at `@__parsers__`, and each instance gets its own clone
- # -> Parser lookup uses a Cascade from the object. (Why not just use prototype, tho?)
-
- parseValue: (v, type) ->
- @getParser(type)(v)
-
- getParser: (type='String') ->
- # If this is a known type and we have a parser for it, return that
- fn = @["parse#type"]
- return fn if typeof fn is 'function'
-
- # Handle compound/optional types
- # XXX: handle 'or' by returning an array of parsers?
- type = _ String(type).toLowerCase()
- for t of <[ Integer Float Number Boolean Object Array Function ]>
- if type.startsWith t.toLowerCase()
- return @["parse#t"]
- @defaultParser or @parseString
-
- getParserFromExample: (v) ->
- return null unless v?
- type = typeof v
-
- if type is not 'object'
- @getParser type
- else if _.isArray v
- @getParser 'Array'
- else
- @getParser 'Object'
-
-
-
-
-/**
- * @class Basic model which mixes in the ParserMixin.
- * @extends BaseModel
- * @borrows ParserMixin
- */
-ParsingModel = exports.ParsingModel = BaseModel.extend ParserMixin.mix do
- constructor: function ParsingModel then BaseModel ...
-
-
-/**
- * @class Basic collection which mixes in the ParserMixin.
- * @extends BaseList
- * @borrows ParserMixin
- */
-ParsingList = exports.ParsingList = BaseList.extend ParserMixin.mix do
- constructor: function ParsingList then BaseList ...
-
-
-/**
- * @class Basic view which mixes in the ParserMixin.
- * @extends BaseView
- * @borrows ParserMixin
- */
-ParsingView = exports.ParsingView = BaseView.extend ParserMixin.mix do
- constructor: function ParsingView then BaseView ...
-
-
-module.exports = exports = '9c55933';
+module.exports = exports = '6e6fd02';
display: inline-block;
vertical-align: text-top;
background-repeat: no-repeat;
- background-image: url("/img/hicons/hicons-sprite-black.png");
+ background-image: url("../img/hicons/hicons-sprite-black.png");
width: 32px;
height: 32px;
line-height: 32px;
}
[class^="hicon-"][class~="icon-white"],
[class*=" hicon-"][class~="icon-white"] {
- background-image: url("/img/hicons/hicons-sprite-white.png");
+ background-image: url("../img/hicons/hicons-sprite-white.png");
}
[class^="hicon-"][class~="icon-sm"],
[class*=" hicon-"][class~="icon-sm"] {
display inline-block
vertical-align text-top
background-repeat no-repeat
- background-image url("/img/hicons/hicons-sprite-black.png")
+ background-image url("../img/hicons/hicons-sprite-black.png")
&[class~="icon-white"]
- background-image url("/img/hicons/hicons-sprite-white.png")
+ background-image url("../img/hicons/hicons-sprite-white.png")
// Large-size (32px) icons have no suffix
width 32px
height: 17px;
line-height: 16px;
background: transparent no-repeat 0 0;
- background-image: url("/img/wmf_logo/wmf_logo-white-16x16.png") !important;
+ background-image: url("../img/wmf_logo/wmf_logo-white-16x16.png") !important;
}
.image {
display: block;
width: 45px;
height: 45px;
line-height: 45px;
- background-image: url("/img/wmf_logo/wmf_logo-black-45x45-a100.png");
+ background-image: url("../img/wmf_logo/wmf_logo-black-45x45-a100.png");
opacity: 0.1;
filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=10);
}
width: 31px;
height: 32px;
line-height: 32px;
- background-image: url("/img/public_domain/public_domain-31x32-a100.png");
+ background-image: url("../img/public_domain/public_domain-31x32-a100.png");
opacity: 0.12;
filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=12);
}
height 17px
line-height 16px
background transparent no-repeat 0 0
- background-image url("/img/wmf_logo/wmf_logo-white-16x16.png") !important
+ background-image url("../img/wmf_logo/wmf_logo-white-16x16.png") !important
.image
width 45px
height 45px
line-height 45px
- background-image url("/img/wmf_logo/wmf_logo-black-45x45-a100.png")
+ background-image url("../img/wmf_logo/wmf_logo-black-45x45-a100.png")
opacity 0.10
&:hover
opacity 0.25
width 31px
height 32px
line-height 32px
- background-image url("/img/public_domain/public_domain-31x32-a100.png")
+ background-image url("../img/public_domain/public_domain-31x32-a100.png")
opacity 0.12
&:hover
opacity 0.25
--- /dev/null
+.info-row.row-fluid
+ .site-map.col.span4
+ h3
+ i.hicon-chart-curve.icon-white.icon-sm
+ a(href="/") Limn
+ ul.site-level
+ li
+ i.icon-home.icon-white
+ a(href="/") Home
+ //- FIXME: put link to /graphs only if dev env, for now.
+ if IS_DEV
+ li
+ i.icon-th.icon-white
+ a(href='/graphs') Browse
+ li
+ i.icon-glass.icon-white
+ a(href="/about") About
+ ul
+ li: a(href="/contact") Contact
+ li: a(href="http://mediawiki.org/wiki/Analytics", target="_blank") Other Projects
+ li
+ i.icon-bookmark.icon-white
+ a(href="/project") Project
+ //-
+ ul
+ li: a(href="mailto:dsc@wikimedia.org?subject=Reportcard/Limn Feedback") Feedback!
+ li: a(href="/blog") Blog
+ li: a(href="/docs/roadmap") Roadmap
+ li: a(href="https://github.com/wikimedia/limn", target="_blank") Source Repository
+ li: a(href="https://github.com/wikimedia/limn/issues", target="_blank") Issue Tracker
+ li: a(href="https://lists.wikimedia.org/mailman/listinfo/analytics", target="_blank") Mailing List
+ //-
+ li
+ i.icon-question-sign.icon-white
+ a(href="/help") Help
+
+ .get-involved.col.span4
+ h3
+ i.icon-comment.icon-white
+ a(href="/project") Get Involved!
+ p
+ a(href="/project") Limn
+ | is
+ a(href="https://github.com/wikimedia/limn", target="_blank") open-source software
+ | , made with love by the
+ a(href="/about") Wikimedia Analytics Team
+ | .
+ p
+ | Find a bug or have a suggestion?
+ a(href="mailto:dsc@wikimedia.org?subject=Limn Feedback") We'd love to hear from you!
+
+ .about-wmf.col.span4
+ h3
+ i.icon-wmf.icon-white
+ a(href="http://wikimedia.org", target="_blank") The Wikimedia Movement
+ p
+ a(href="http://wikimediafoundation.org/wiki/Wikimedia:About", target="_blank") The Wikimedia Foundation
+ | is the non-profit organization that operates
+ a(href="http://wikipedia.org", target="_blank") Wikipedia
+ | and
+ a(href="http://wikimedia.org", target="_blank") other free knowledge projects
+ | .
+ p
+ | If you're excited about community analytics, check out some of the
+ a(href="http://stats.wikimedia.org", target="_blank") other stuff we're working on
+ | .
+
+.copyright-row.row-fluid
+ .copyright.inner(xmlns:dct="http://purl.org/dc/terms/")
+ a.public-domain.image(href="http://creativecommons.org/publicdomain/zero/1.0/", rel="license", title="Public Domain", target="_blank")
+ | To the extent possible under law,
+ a(href="http://www.wikimediafoundation.org", rel="dct:publisher", title="The Wikimedia Foundation", target="_blank")
+ span(property="dct:title") The Wikimedia Foundation
+ | has waived all copyright and related or neighboring rights to the
+ span(property="dct:title") Monthly Report Card data and charts
+ | .
+
+.tos-row.row-fluid
+ a(href="http://wikimediafoundation.org/wiki/Terms_of_use", target="_blank") Terms of Use
+ span.separator •
+ a(href="http://wikimediafoundation.org/wiki/Wikimedia:General_disclaimer", target="_blank") Disclaimers
+ span.separator •
+ a(href="http://wikimediafoundation.org/wiki/Wikimedia:Privacy_policy", target="_blank") Privacy Policy
+
+a.wmf-logo.image(href="http://mediawiki.org/wiki/Analytics", target="_blank",
+ title="A product of Team Analytics at the Wikimedia Foundation")
+ | A product of Team Analytics at the Wikimedia Foundation
mixin css('geo-display.css')
block main-scripts
- script(src="/js/limn/main-geo.js?"+version)
+ script(src=mount+"/js/limn/main-geo.js?"+version)
block content
section.geo
footer
block footer
- .info-row.row-fluid
- .site-map.col.span4
- h3
- i.hicon-chart-curve.icon-white.icon-sm
- a(href="/") Limn
- ul.site-level
- li
- i.icon-home.icon-white
- a(href="/") Home
- //- FIXME: put link to /graphs only if dev env, for now.
- if IS_DEV
- li
- i.icon-th.icon-white
- a(href='/graphs') Browse
- li
- i.icon-glass.icon-white
- a(href="/about") About
- ul
- li: a(href="/contact") Contact
- li: a(href="http://mediawiki.org/wiki/Analytics", target="_blank") Other Projects
- li
- i.icon-bookmark.icon-white
- a(href="/project") Project
- //-
- ul
- li: a(href="mailto:dsc@wikimedia.org?subject=Reportcard/Limn Feedback") Feedback!
- li: a(href="/blog") Blog
- li: a(href="/docs/roadmap") Roadmap
- li: a(href="https://github.com/wikimedia/limn", target="_blank") Source Repository
- li: a(href="https://github.com/wikimedia/limn/issues", target="_blank") Issue Tracker
- li: a(href="https://lists.wikimedia.org/mailman/listinfo/analytics", target="_blank") Mailing List
- //-
- li
- i.icon-question-sign.icon-white
- a(href="/help") Help
-
- .get-involved.col.span4
- h3
- i.icon-comment.icon-white
- a(href="/project") Get Involved!
- p
- a(href="/project") Limn
- | is
- a(href="https://github.com/wikimedia/limn", target="_blank") open-source software
- | , made with love by the
- a(href="/about") Wikimedia Analytics Team
- | .
- p
- | Find a bug or have a suggestion?
- a(href="mailto:dsc@wikimedia.org?subject=Limn Feedback") We'd love to hear from you!
-
- .about-wmf.col.span4
- h3
- i.icon-wmf.icon-white
- a(href="http://wikimedia.org", target="_blank") The Wikimedia Movement
- p
- a(href="http://wikimediafoundation.org/wiki/Wikimedia:About", target="_blank") The Wikimedia Foundation
- | is the non-profit organization that operates
- a(href="http://wikipedia.org", target="_blank") Wikipedia
- | and
- a(href="http://wikimedia.org", target="_blank") other free knowledge projects
- | .
- p
- | If you're excited about community analytics, check out some of the
- a(href="http://stats.wikimedia.org", target="_blank") other stuff we're working on
- | .
-
- .copyright-row.row-fluid
- .copyright.inner(xmlns:dct="http://purl.org/dc/terms/")
- a.public-domain.image(href="http://creativecommons.org/publicdomain/zero/1.0/", rel="license", title="Public Domain", target="_blank")
- | To the extent possible under law,
- a(href="http://www.wikimediafoundation.org", rel="dct:publisher", title="The Wikimedia Foundation", target="_blank")
- span(property="dct:title") The Wikimedia Foundation
- | has waived all copyright and related or neighboring rights to the
- span(property="dct:title") Monthly Report Card data and charts
- | .
-
- .tos-row.row-fluid
- a(href="http://wikimediafoundation.org/wiki/Terms_of_use", target="_blank") Terms of Use
- span.separator •
- a(href="http://wikimediafoundation.org/wiki/Wikimedia:General_disclaimer", target="_blank") Disclaimers
- span.separator •
- a(href="http://wikimediafoundation.org/wiki/Wikimedia:Privacy_policy", target="_blank") Privacy Policy
-
- a.wmf-logo.image(href="http://mediawiki.org/wiki/Analytics", target="_blank",
- title="A product of Team Analytics at the Wikimedia Foundation")
- | A product of Team Analytics at the Wikimedia Foundation
+ include footer
.scripts
block scripts
+ - var limn_config = { 'mount':mount, 'rev':version, 'env':NODE_ENV };
script
var VERSION = !{ JSON.stringify(version) };
var ENV = !{ JSON.stringify(NODE_ENV) };
var IS_DEV = ENV === 'development', IS_PROD = ENV === 'production';
+ var limn_config = !{ JSON.stringify(limn_config) };
block lib-scripts
for src in sources(WWW+'/modules.yaml')
- script(src=src+"?"+version)
+ script(src=path.join(mount, src)+"?"+version)
block page-scripts
script
- var limn = require('limn/limn');
+ var limn = require('limn/client');
block main-scripts
block addenda
-- root = (function(){ return this; })();
-- if (typeof version == 'undefined') root.version = 'HEAD';
-- if (version == null || !version || version === 'HEAD') { version = String(new Date().getTime()); }
+- if (!locals.version) locals.version = 'HEAD';
+- if (version == null || !version || version === 'HEAD') { locals.version = String(new Date().getTime()); }
mixin css(href, media, path_root)
- media = media || 'screen'
- - path_root = path_root || '/css'
- - var ver = ((typeof version != 'undefined' && version) ? '?'+version : '')
- - href = ((path_root && href.charAt(0) !== '/') ? path_root+'/' : '') + href + ver
+ - path_root = path_root || (href.charAt(0) === '/' ? locals.mount : locals.path.join(locals.mount, 'css'))
+ - var ver = (locals.version ? '?'+locals.version : '')
+ - href = locals.path.join(path_root, href) + ver
link(type='text/css', rel='stylesheet', media=media, href=href)
- data-binding
- model-cache
- cascading-model
+ - parser-mixin
- index
- graph:
- graph-model
- dashboard-model
- dashboard-view
- index
- - limn
+ - client
# - suffix: .js
# paths: