Applicaton now obeys a mount-point set by middleware.
authorDavid Schoonover <dsc@wikimedia.org>
Tue, 17 Jul 2012 23:08:24 +0000 (16:08 -0700)
committerDavid Schoonover <dsc@wikimedia.org>
Tue, 17 Jul 2012 23:40:43 +0000 (16:40 -0700)
58 files changed:
Cokefile
README.md
lib/base/base-model.js
lib/base/base-model.mod.js
lib/base/base.js
lib/base/base.mod.js
lib/base/index.js
lib/base/index.mod.js
lib/base/parser-mixin.js [new file with mode: 0644]
lib/base/parser-mixin.mod.js [new file with mode: 0644]
lib/chart/chart-type.js
lib/chart/chart-type.mod.js
lib/chart/option/chart-option-model.js
lib/chart/option/chart-option-model.mod.js
lib/chart/type/d3-chart.js
lib/chart/type/d3-chart.mod.js
lib/chart/type/dygraphs.js
lib/chart/type/dygraphs.mod.js
lib/client.js [new file with mode: 0644]
lib/client.mod.js [new file with mode: 0644]
lib/data/datasource-model.js
lib/data/datasource-model.mod.js
lib/graph/graph-model.js
lib/graph/graph-model.mod.js
lib/limn.js
lib/limn.mod.js
lib/server/middleware.js
lib/server/server.js
lib/util/index.js
lib/util/index.mod.js
lib/util/parser.js
lib/util/parser.mod.js
src/base/base-model.co
src/base/base.co
src/base/index.co
src/base/parser-mixin.co [new file with mode: 0644]
src/chart/chart-type.co
src/chart/option/chart-option-model.co
src/chart/type/d3-chart.co
src/chart/type/dygraphs.co
src/client.co [moved from src/limn.co with 89% similarity]
src/data/datasource-model.co
src/graph/graph-model.co
src/server/index.js
src/server/middleware.co
src/server/server.co
src/util/index.co
src/util/parser.co
src/version.js
www/css/hicons.css
www/css/hicons.styl
www/css/layout.css
www/css/layout.styl
www/footer.jade [new file with mode: 0644]
www/geo.jade
www/layout.jade
www/mixins/helpers.jade
www/modules.yaml

index c19c035..4f39c17 100644 (file)
--- a/Cokefile
+++ b/Cokefile
@@ -35,9 +35,7 @@ task \setup 'Ensure project is set up for development.' ->
 
 
 task \server 'Start dev server' ->
-    invoke \setup
-    say ''
-    run 'src/server/server.co'
+    run './src/server/server.co'
 
 
 task \build 'Build coco sources' ->
index befe80d..2bfa087 100644 (file)
--- a/README.md
+++ b/README.md
@@ -2,60 +2,3 @@
 
 
 
-### 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
index 2e3394c..7fe3c06 100644 (file)
@@ -1,5 +1,6 @@
-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;
 /**
@@ -17,7 +18,7 @@ BaseModel = exports.BaseModel = Backbone.Model.extend(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;
@@ -221,12 +222,13 @@ BaseList = exports.BaseList = Backbone.Collection.extend(mixinBase({
     });
   },
   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(){
index 9d7f227..421525a 100644 (file)
@@ -1,7 +1,8 @@
 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;
 /**
@@ -19,7 +20,7 @@ BaseModel = exports.BaseModel = Backbone.Model.extend(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;
@@ -223,12 +224,13 @@ BaseList = exports.BaseList = Backbone.Collection.extend(mixinBase({
     });
   },
   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(){
index eac2083..701c9cc 100644 (file)
@@ -18,11 +18,14 @@ Base = (function(superclass){
   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.
@@ -46,10 +49,10 @@ Base = (function(superclass){
     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;
       }
     }
index d8daec5..77c5599 100644 (file)
@@ -20,11 +20,14 @@ Base = (function(superclass){
   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.
@@ -48,10 +51,10 @@ Base = (function(superclass){
     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;
       }
     }
index 5caef36..4334860 100644 (file)
@@ -1,4 +1,4 @@
-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');
@@ -7,6 +7,8 @@ cache = require('./model-cache');
 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];
index d23b254..f7867c9 100644 (file)
@@ -1,6 +1,6 @@
 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');
@@ -9,6 +9,8 @@ cache = require('./model-cache');
 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];
diff --git a/lib/base/parser-mixin.js b/lib/base/parser-mixin.js
new file mode 100644 (file)
index 0000000..8a67ce4
--- /dev/null
@@ -0,0 +1,100 @@
+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
diff --git a/lib/base/parser-mixin.mod.js b/lib/base/parser-mixin.mod.js
new file mode 100644 (file)
index 0000000..510e807
--- /dev/null
@@ -0,0 +1,104 @@
+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;
+}
+
+});
index 721046d..c7aad90 100644 (file)
@@ -1,9 +1,10 @@
-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
@@ -140,13 +141,14 @@ exports.ChartType = ChartType = (function(superclass){
    * 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;
index e62af90..6362824 100644 (file)
@@ -1,11 +1,12 @@
 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
@@ -142,13 +143,14 @@ exports.ChartType = ChartType = (function(superclass){
    * 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;