/* * Polychart.js * Copyright (c) Polychart Inc * All Rights Reserved */ window.polyjs = (function(polyjs) { if (!polyjs) { var poly = {}; // Generated by CoffeeScript 1.6.2 /* Group an array of data items by the value of certain columns. Input: - `data`: an array of data items - `group`: an array of column keys, to group by Output: - an associate array of key: array of data, with the appropriate grouping the `key` is a string of format "columnKey:value;colunmKey2:value2;..." */ (function() { var __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; poly.groupBy = function(data, group) { return _.groupBy(data, poly.stringify(group)); }; poly.stringify = function(group) { return function(item) { var concat; concat = function(memo, g) { return "" + memo + g + ":" + item[g] + ";"; }; return _.reduce(group, concat, ""); }; }; poly.cross = function(keyVals, ignore) { var arrs, i, item, items, next, todo, val, _i, _j, _len, _len1, _ref; if (ignore == null) { ignore = []; } todo = _.difference(_.keys(keyVals), ignore); if (todo.length === 0) { return [{}]; } arrs = []; next = todo[0]; items = poly.cross(keyVals, ignore.concat(next)); _ref = keyVals[next]; for (_i = 0, _len = _ref.length; _i < _len; _i++) { val = _ref[_i]; for (_j = 0, _len1 = items.length; _j < _len1; _j++) { item = items[_j]; i = _.clone(item); i[next] = val; arrs.push(i); } } return arrs; }; poly.filter = function(statData, key, val) { var item, newData, _i, _len; newData = []; for (_i = 0, _len = statData.length; _i < _len; _i++) { item = statData[_i]; if (item[key] === val) { newData.push(item); } } return newData; }; /* Intersets values when filter key is common to both objects, add all values otherwise. TODO: handle the case when no intersection exist from a given common key */ poly.intersect = function(filter1, filter2) { var intersectIneq, intersectList, key, newFilter, val; intersectList = function(key) { var elem, newList, _i, _len, _ref; newList = []; _ref = filter1[key]["in"]; for (_i = 0, _len = _ref.length; _i < _len; _i++) { elem = _ref[_i]; if (__indexOf.call(filter2[key]["in"], elem) >= 0) { newList.push(elem); } } return { "in": newList }; }; intersectIneq = function(key) { var addbound, getLowerBound, getUpperBound, lowers, newIneq, type, uppers, val, _ref, _ref1; getUpperBound = function(filter) { if (filter[key].lt) { return { type: "lt", val: filter[key].lt }; } else if (filter[key].le) { return { type: "le", val: filter[key].le }; } else { return { type: null, val: null }; } }; getLowerBound = function(filter) { if (filter[key].gt) { return { type: "gt", val: filter[key].gt }; } else if (filter[key].ge) { return { type: "ge", val: filter[key].ge }; } else { return { type: null, val: null }; } }; addbound = function(bound) { return newIneq[bound.type] = bound.val; }; lowers = [getLowerBound(filter1), getLowerBound(filter2)]; uppers = [getUpperBound(filter1), getUpperBound(filter2)]; lowers.sort(function(a, b) { return b.val - a.val; }); uppers.sort(function(a, b) { return a.val - b.val; }); newIneq = {}; if (lowers[0].type && lowers[0].val) { _ref = lowers[0], type = _ref.type, val = _ref.val; if (lowers[0].val === lowers[1].val && lowers[0].type !== lowers[1].type) { type = "lt"; } newIneq[type] = val; } if (uppers[0].type && uppers[0].val) { _ref1 = uppers[0], type = _ref1.type, val = _ref1.val; if (uppers[0].val === uppers[1].val && uppers[0].type !== uppers[1].type) { type = "lt"; } newIneq[type] = val; } if (lowers[0].type && uppers[0].type) { if (lowers[0].val > uppers[0].val || (lowers[0].val === uppers[0].val && (lowers[0].key === "lt" || uppers[0].key === "gt"))) { throw "No intersection found!"; } } return newIneq; }; newFilter = {}; for (key in filter1) { val = filter1[key]; if (key in filter2) { if ("in" in filter1[key]) { newFilter[key] = intersectList(key); } else { newFilter[key] = intersectIneq(key); } } else { newFilter[key] = val; } } for (key in filter2) { val = filter2[key]; if (!(key in newFilter)) { newFilter[key] = val; } } return newFilter; }; /* Produces a linear function that passes through two points. Input: - `x1`: x coordinate of the first point - `y1`: y coordinate of the first point - `x2`: x coordinate of the second point - `y2`: y coordinate of the second point Output: - A function that, given the x-coord, returns the y-coord */ poly.linear = function(x1, y1, x2, y2) { if (_.isFinite(x1) && _.isFinite(y1) && _.isFinite(x2) && _.isFinite(y2)) { return function(x) { return (y2 - y1) / (x2 - x1) * (x - x1) + y1; }; } else { throw poly.error.input("Attempting to create linear function from infinity"); } }; /* given a sorted list and a midpoint calculate the median */ poly.median = function(values, sorted) { var mid; if (sorted == null) { sorted = false; } if (!sorted) { values = _.sortBy(values, function(x) { return x; }); } mid = values.length / 2; if (mid % 1 !== 0) { return values[Math.floor(mid)]; } return (values[mid - 1] + values[mid]) / 2; }; /* Produces a function that counts how many times it has been called */ poly.counter = function() { var i; i = 0; return function() { return i++; }; }; /* Sample an associate array (object) */ poly.sample = function(assoc, num) { return _.pick(assoc, _.shuffle(_.keys(assoc)).splice(0, num)); }; /* Given an OLD array and NEW array, split the points in (OLD union NEW) into three sets: - deleted - kept - added */ poly.compare = function(oldarr, newarr) { var added, deleted, kept, newElem, newIndex, oldElem, oldIndex, sortedNewarr, sortedOldarr; sortedOldarr = _.sortBy(oldarr, function(x) { return x; }); sortedNewarr = _.sortBy(newarr, function(x) { return x; }); deleted = []; kept = []; added = []; oldIndex = newIndex = 0; while (oldIndex < sortedOldarr.length || newIndex < sortedNewarr.length) { oldElem = sortedOldarr[oldIndex]; newElem = sortedNewarr[newIndex]; if (oldIndex >= sortedOldarr.length) { added.push(newElem); newIndex += 1; } else if (newIndex >= sortedNewarr.length) { deleted.push(oldElem); oldIndex += 1; } else if (oldElem < newElem) { deleted.push(oldElem); oldIndex += 1; } else if (oldElem > newElem) { added.push(newElem); newIndex += 1; } else if (oldElem === newElem) { kept.push(oldElem); oldIndex += 1; newIndex += 1; } else { throw DataError("Unknown data encounted"); } } return { deleted: deleted, kept: kept, added: added }; }; /* Given an aesthetic mapping in the "geom" object, flatten it and extract only the values from it. This is so that even if a compound object is encoded in an aestehtic, we have the correct set of values to calculate the min/max. */ poly.flatten = function(values) { var flat, k, v, _i, _len; flat = []; if (values != null) { if (_.isObject(values)) { if (values.t === 'scalefn') { if (values.f !== 'novalue') { flat.push(values.v); } } else { for (k in values) { v = values[k]; flat = flat.concat(poly.flatten(v)); } } } else if (_.isArray(values)) { for (_i = 0, _len = values.length; _i < _len; _i++) { v = values[_i]; flat = flat.concat(poly.flatten(v)); } } else { flat.push(values); } } return flat; }; /* GET LABEL TODO: move somewhere else and allow overwrite by user */ poly.getLabel = function(layers, aes) { return _.chain(layers).map(function(l) { return l.mapping[aes]; }).without(null, void 0).uniq().value().join(' | '); }; /* Estimate the number of pixels rendering this string would take...? */ poly.strSize = function(str) { var len; len = (str + "").length; if (len < 10) { return len * 6; } else { return (len - 10) * 5 + 60; } }; /* Sort Arrays: given a sorting function and some number of arrays, sort all the arrays by the function applied to the first array. This is used for sorting points for a line chart, i.e. poly.sortArrays(sortFn, [xs, ys]) This way, all the points are sorted by (sortFn(x) for x in xs) */ poly.sortArrays = function(fn, arrays) { var zipped; zipped = _.zip.apply(_, arrays); zipped.sort(function(a, b) { return fn(a[0], b[0]); }); return _.zip.apply(_, zipped); }; /* Determine if a value is not null and not undefined. */ poly.isDefined = function(x) { if (_.isObject(x)) { if (x.t === 'scalefn' && x.f !== 'novalue') { return poly.isDefined(x.v); } else { return true; } } else { return x !== void 0 && x !== null && !(_.isNumber(x) && _.isNaN(x)); } }; /* Determine if a String is a valid URI http://stackoverflow.com/questions/5717093/check-if-a-javascript-string-is-an-url */ poly.isURI = function(str) { var pattern; if (!_.isString(str)) { return false; } else { pattern = new RegExp('^(https?:\\/\\/)?' + '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + '((\\d{1,3}\\.){3}\\d{1,3}))' + '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' + '(\\?[;&a-z\\d%_.~+=-]*)?' + '(\\#[-a-z\\d_]*)?$', 'i'); return pattern.test(str); } }; }).call(this); // Generated by CoffeeScript 1.6.2 /* CONSTANTS --------- These are constants that are referred to throughout the coebase */ (function() { poly["const"] = { aes: ['x', 'y', 'color', 'size', 'opacity', 'shape', 'id', 'text'], pivot_aes: ['row', 'column', 'value'], noDomain: ['id', 'text', 'tooltip'], noLegend: ['x', 'y', 'id', 'text', 'tooltip'], trans: { 'bin': ['key', 'binwidth'], 'lag': ['key', 'lag'] }, stat: { 'count': ['key'], 'unique': ['key'], 'sum': ['key'], 'mean': ['key'], 'box': ['key'], 'median': ['key'] }, timerange: ['second', 'minute', 'hour', 'day', 'week', 'month', 'twomonth', 'quarter', 'sixmonth', 'year', 'twoyear', 'fiveyear', 'decade'], approxTimeInSeconds: { second: 1, minute: 60, hour: 60 * 60, day: 60 * 60 * 24, week: 60 * 60 * 24 * 7, month: 60 * 60 * 24 * 30, twomonth: 60 * 60 * 24 * 30 * 2, quarter: 60 * 60 * 24 * 30 * 4, sixmonth: 60 * 60 * 24 * 30 * 6, year: 60 * 60 * 24 * 365, twoyear: 60 * 60 * 24 * 365 * 2, fiveyear: 60 * 60 * 24 * 365 * 5 + 60 * 60 * 24 }, sort: { key: null, sort: null, limit: null, asc: false }, scaleFns: { novalue: function() { return { v: null, f: 'novalue', t: 'scalefn' }; }, max: function(v) { return { v: v, f: 'max', t: 'scalefn' }; }, min: function(v) { return { v: v, f: 'min', t: 'scalefn' }; }, upper: function(v, n, m) { return { v: v, n: n, m: m, f: 'upper', t: 'scalefn' }; }, lower: function(v, n, m) { return { v: v, n: n, m: m, f: 'lower', t: 'scalefn' }; }, middle: function(v) { return { v: v, f: 'middle', t: 'scalefn' }; }, jitter: function(v) { return { v: v, f: 'jitter', t: 'scalefn' }; }, identity: function(v) { return { v: v, f: 'identity', t: 'scalefn' }; } }, epsilon: Math.pow(10, -7), defaults: { 'x': { v: null, f: 'novalue', t: 'scalefn' }, 'y': { v: null, f: 'novalue', t: 'scalefn' }, 'color': 'steelblue', 'size': 2, 'opacity': 0.7 } }; }).call(this); // Generated by CoffeeScript 1.6.2 (function() { var DataError, DefinitionError, DependencyError, MissingData, ModeError, NotImplemented, ScaleError, Type, UnknownInput, __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; DefinitionError = (function(_super) { __extends(DefinitionError, _super); function DefinitionError(message) { this.message = message; this.name = "DefinitionError"; } return DefinitionError; })(Error); DependencyError = (function(_super) { __extends(DependencyError, _super); function DependencyError(message) { this.message = message; this.name = "DependencyError"; } return DependencyError; })(Error); ModeError = (function(_super) { __extends(ModeError, _super); function ModeError(message) { this.message = message; this.name = "ModeError"; } return ModeError; })(Error); DataError = (function(_super) { __extends(DataError, _super); function DataError(message) { this.message = message; this.name = "DataError"; } return DataError; })(Error); UnknownInput = (function(_super) { __extends(UnknownInput, _super); function UnknownInput(message) { this.message = message; this.name = "UnknownInput"; } return UnknownInput; })(Error); NotImplemented = (function(_super) { __extends(NotImplemented, _super); function NotImplemented(message) { this.message = message; this.name = "ModeError"; } return NotImplemented; })(Error); ScaleError = (function(_super) { __extends(ScaleError, _super); function ScaleError(message) { this.message = message; this.name = "ScaleError"; } return ScaleError; })(Error); MissingData = (function(_super) { __extends(MissingData, _super); function MissingData(message) { this.message = message; this.name = "MissingData"; } return MissingData; })(Error); Type = (function(_super) { __extends(Type, _super); function Type(message) { this.message = message; this.name = "Type"; } return Type; })(Error); poly.error = function(msg) { return new Error(msg); }; poly.error.data = function(msg) { return new DataError(msg); }; poly.error.depn = function(msg) { return new DependencyError(msg); }; poly.error.defn = function(msg) { return new DefinitionError(msg); }; poly.error.mode = function(msg) { return new ModeError(msg); }; poly.error.impl = function(msg) { return new NotImplemented(msg); }; poly.error.input = function(msg) { return new UnknownInput(msg); }; poly.error.scale = function(msg) { return new ScaleError(msg); }; poly.error.missing = function(msg) { return new MissingData(msg); }; poly.error.type = function(msg) { return new Type(msg); }; }).call(this); // Generated by CoffeeScript 1.6.2 /* Abstract Classes --------- Abstract classes, almost used like interfaces throughout the codebase */ (function() { var Geometry, Guide, GuideSet, Renderable, _ref, _ref1, __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; Renderable = (function() { function Renderable() {} Renderable.prototype.render = function() { return poly.error.impl(); }; Renderable.prototype.dispose = function() { return poly.error.impl(); }; return Renderable; })(); Guide = (function(_super) { __extends(Guide, _super); function Guide() { _ref = Guide.__super__.constructor.apply(this, arguments); return _ref; } Guide.prototype.getDimension = function() { throw poly.error.impl(); }; return Guide; })(Renderable); GuideSet = (function(_super) { __extends(GuideSet, _super); function GuideSet() { _ref1 = GuideSet.__super__.constructor.apply(this, arguments); return _ref1; } GuideSet.prototype.getDimension = function() { throw poly.error.impl(); }; GuideSet.prototype.make = function() { throw poly.error.impl(); }; return GuideSet; })(Renderable); /* This should probably be in its own class folder, and should technically be named "Renderable", but whatever. It manages what is currently on the screen, and what needs to be rendered. @geoms : a key-value pair of an identifier to a group of objects to be rendered. It should be of the following form: @geoms = { 'id' : { marks: { # an assoc array of renderable "marks", acceptable by # poly.render() function }, evtData: { # data bound to a click/mouseover/mouseout event # on the marks plotted }, tooltip: # tooltip text to show on mouseover } } @pts : a key-value pair of identfier to a group of objects rendered. the group of objects is also a key-value pair, corresponding to the key-value pair provided by `marks` as above. */ Geometry = (function(_super) { __extends(Geometry, _super); function Geometry(type) { this.type = type != null ? type : null; this.dispose = __bind(this.dispose, this); this.geoms = {}; this.pts = {}; } Geometry.prototype.set = function(geoms) { return this.geoms = geoms; }; Geometry.prototype.render = function(renderer) { var added, deleted, id, kept, newpts, _i, _j, _k, _len, _len1, _len2, _ref2; newpts = {}; _ref2 = poly.compare(_.keys(this.pts), _.keys(this.geoms)), deleted = _ref2.deleted, kept = _ref2.kept, added = _ref2.added; for (_i = 0, _len = deleted.length; _i < _len; _i++) { id = deleted[_i]; this._delete(renderer, this.pts[id]); } for (_j = 0, _len1 = added.length; _j < _len1; _j++) { id = added[_j]; newpts[id] = this._add(renderer, this.geoms[id]); } for (_k = 0, _len2 = kept.length; _k < _len2; _k++) { id = kept[_k]; newpts[id] = this._modify(renderer, this.pts[id], this.geoms[id]); } return this.pts = newpts; }; Geometry.prototype._delete = function(renderer, points) { var id2, pt, _results; _results = []; for (id2 in points) { pt = points[id2]; _results.push(renderer.remove(pt)); } return _results; }; Geometry.prototype._modify = function(renderer, points, geom) { var error, id2, mark, objs, _ref2; objs = {}; _ref2 = geom.marks; for (id2 in _ref2) { mark = _ref2[id2]; try { objs[id2] = points[id2] ? points[id2].data('m').type === mark.type ? renderer.animate(points[id2], mark, geom.evtData, geom.tooltip) : (renderer.remove(points[id2]), renderer.add(mark, geom.evtData, geom.tooltip, this.type)) : renderer.add(mark, geom.evtData, geom.tooltip, this.type); } catch (_error) { error = _error; if (error.name === 'MissingData') { console.log(error.message); } else { throw error; } } } return objs; }; Geometry.prototype._add = function(renderer, geom) { var error, id2, mark, objs, _ref2; objs = {}; _ref2 = geom.marks; for (id2 in _ref2) { mark = _ref2[id2]; try { objs[id2] = renderer.add(mark, geom.evtData, geom.tooltip, this.type); } catch (_error) { error = _error; if (error.name === 'MissingData') { console.log(error.message); } else { throw error; } } } return objs; }; Geometry.prototype.dispose = function(renderer) { var id, pt, _ref2; _ref2 = this.pts; for (id in _ref2) { pt = _ref2[id]; this._delete(renderer, pt); } return this.pts = {}; }; return Geometry; })(Renderable); poly.Renderable = Renderable; poly.Guide = Guide; poly.GuideSet = GuideSet; poly.Geometry = Geometry; }).call(this); // Generated by CoffeeScript 1.6.2 (function() { var PolyCanvas, PolyCanvasItem, __slice = [].slice, __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; PolyCanvas = (function() { function PolyCanvas(dom, w, h) { if (dom.getContext) { this.context = dom.getContext('2d'); } else { dom.polyGeom = this; } dom.width = w; dom.height = h; this.items = []; this._counter = 0; } PolyCanvas.prototype._makeItem = function(type, args) { var item; item = new PolyCanvasItem(type, this._newId(), this, args); this.items.unshift(item); return item; }; PolyCanvas.prototype._newId = function() { return this._counter += 1; }; PolyCanvas.prototype.rect = function() { var args; args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; return this._makeItem('rect', args); }; PolyCanvas.prototype.circle = function() { var args; args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; return this._makeItem('circle', args); }; PolyCanvas.prototype.path = function() { var args; args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; return this._makeItem('path', args); }; PolyCanvas.prototype.text = function() { var args; args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; return this._makeItem('text', args); }; PolyCanvas.prototype.remove = function(id) { var i, item, _i, _len, _ref; _ref = this.items; for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) { item = _ref[i]; if (item.id === id) { return this.items.splice(i, 1); } } }; PolyCanvas.prototype.toBack = function(id) { var bg, item; item = this.remove(id)[0]; bg = this.items.pop(); this.items.push(item); return this.items.push(bg); }; PolyCanvas.prototype.toFront = function(id) { var item; item = this.remove(id)[0]; return this.items.unshift(item); }; PolyCanvas.prototype._resetContext = function() { this.context.fillStyle = '#000000'; this.context.strokeStyle = '#000000'; this.context.globalAlpha = 1; return this.context.lineWidth = 0.5; }; PolyCanvas.prototype._stringToHex = function(colour) { switch (colour) { case 'black': return '#000000'; case 'white': return '#ffffff'; case 'steelblue': return '#4692B4'; default: return colour; } }; return PolyCanvas; })(); PolyCanvasItem = (function() { function PolyCanvasItem(type, id, canvas, args) { this.type = type; this.id = id; this.canvas = canvas; this._attr = {}; this._interact = {}; this.attr(args); } PolyCanvasItem.prototype.attr = function() { var args, key, params, val, _ref; args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; if (args.length > 0 && _.isArray(args[0])) { params = args[0]; switch (this.type) { case 'rect': this._attr = _.extend(this._attr, { x: params[0], y: params[1], width: params[2], height: params[3] }); break; case 'circle': this._attr = _.extend(this._attr, { x: params[0], y: params[1], r: params[2] }); break; case 'path': this._attr = _.extend(this._attr, { path: params[0] }); break; case 'text': this._attr = _.extend(this._attr, { x: params[0], y: params[1], text: params[2], 'font-size': '12pt', 'text-anchor': 'middle' }); break; default: throw poly.error.defn("Unknown geometry type!"); } } else if (args.length === 1 && _.isObject(args[0])) { _ref = args[0]; for (key in _ref) { val = _ref[key]; this._attr[key] = val; } } else if (args.length === 2 && (args[0] != null) && (args[1] != null)) { this._attr[args[0]] = args[1]; } return this; }; PolyCanvasItem.prototype.remove = function() { return this.canvas.remove(this.id); }; PolyCanvasItem.prototype.toBack = function() { return this.canvas.toBack(this.id); }; PolyCanvasItem.prototype.toFront = function() { return this.canvas.toFront(this.id); }; PolyCanvasItem.prototype.getBBox = function() { var character, fontSize, height, width, _i, _len, _ref, _ref1; if (this.type === 'text') { fontSize = (_ref = parseInt(this._attr['font-size'].slice(0, -2))) != null ? _ref : 12; width = 0; height = fontSize * 1.04; _ref1 = this._attr.text; for (_i = 0, _len = _ref1.length; _i < _len; _i++) { character = _ref1[_i]; if (__indexOf.call(",.1", character) >= 0) { width += fontSize / 4; } else { width += fontSize; } } return { height: height, width: width }; } else if (this.type === 'rect') { return { height: this._attr.height, width: this._attr.width }; } else if (this.type === 'circle') { return { height: this._attr.r, width: this._attr.r }; } }; PolyCanvasItem.prototype.transform = function(trans) { var scale; if (trans[0] === 's') { scale = trans.slice(1); if ('font-size' in this._attr) { this._attr['font-size'] = this._attr['font-size'].slice(0, -2) * scale + 'pt'; } if ('width' in this._attr) { this._attr['width'] *= scale; } if ('height' in this._attr) { return this._attr['height'] *= scale; } } }; PolyCanvasItem.prototype.animate = function() { var args; args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; this._attr.animate = args; return this; }; PolyCanvasItem.prototype.click = function(handler) { return this._interact.click = handler; }; PolyCanvasItem.prototype.drag = function(onmove, onstart, onend) { return this._interact.drag = { onmove: onmove, onstart: onstart, onend: onend }; }; PolyCanvasItem.prototype.hover = function(handler) { return this._interact.hover = handler; }; PolyCanvasItem.prototype.data = function(type, handler) { var _base, _ref; if ((_ref = (_base = this._interact).data) == null) { _base.data = {}; } return this._interact.data[type] = handler; }; PolyCanvasItem.prototype.touchstart = function(handler) { return this._interact.touchstart = handler; }; PolyCanvasItem.prototype.touchend = function(handler) { return this._interact.touchend = handler; }; PolyCanvasItem.prototype.touchmove = function(handler) { return this._interact.touchmove = handler; }; PolyCanvasItem.prototype.touchcancel = function(handler) { return this._interact.touchcancel = handler; }; return PolyCanvasItem; })(); poly.canvas = function(dom, w, h) { return new PolyCanvas(dom, w, h); }; }).call(this); // Generated by CoffeeScript 1.6.2 /* Get the offset of the element */ (function() { var touchInfo, _oldAlert, _this = this; poly.offset = function(elem) { var box, doc, docElem, win; box = { top: 0, left: 0 }; doc = elem && elem.ownerDocument; if (!doc) { return; } docElem = doc.documentElement; if (typeof elem.getBoundingClientRect !== "undefined") { box = elem.getBoundingClientRect(); } win = doc !== null && doc === doc.window ? doc : doc.nodeType === 9 && doc.defaultView; return { top: box.top + win.pageYOffset - docElem.clientTop, left: box.left + win.pageXOffset - docElem.clientLeft }; }; /* Get the raphael (x,y) position of a mouse event */ poly.getXY = function(offset, e) { var scrollX, scrollY, touch, x, y; if (e.type.indexOf('mouse') !== -1) { x = e.clientX; y = e.clientY; } else if (e.type.indexOf('touch') !== -1) { touch = e.changedTouches[0]; x = touch.clientX; y = touch.clientY; } scrollY = (document.documentElement && document.documentElement.scrollTop) || document.body.scrollTop; scrollX = (document.documentElement && document.documentElement.scrollLeft) || document.body.scrollLeft; return { x: x + scrollX - offset.left, y: y + scrollY - offset.top }; }; /* Transforms a TouchEvent to MouseEvent */ poly.touchToMouse = function(type, touchInfo, delay) { var event, evt, first; if (delay == null) { delay = false; } event = touchInfo.lastEvent; first = (event.touches.length > 0 && event.touches[0]) || (event.changedTouches.length > 0 && event.changedTouches[0]); evt = document.createEvent('MouseEvent'); evt.initMouseEvent(type, event.bubbles, event.cancelable, event.view, event.detail, first.screenX, first.screenY, first.clientX, first.clientY, event.ctrlKey, event.altKey, event.shiftKey, event.metaKey, 1, event.target); if (delay) { window.clearTimeout(touchInfo.pressTimer); return touchInfo.pressTimer = window.setTimeout((function() { return event.target.dispatchEvent(evt); }), delay); } else { return event.target.dispatchEvent(evt); } }; /* Touch Event Handling */ touchInfo = { lastStart: 0, lastTouch: 0, lastEvent: null, pressTimer: 0 }; _oldAlert = window.alert; poly.touch = function(type, obj, event, graph) { var elem, offset, touchPos; obj.tooltip = obj.data('t'); obj.evtData = obj.data('e'); touchInfo.lastEvent = event; event.preventDefault(); if (type === 'touchstart') { touchInfo.lastStart = event.timeStamp; poly.touchToMouse('mousedown', touchInfo); touchInfo.pressTimer = window.setTimeout((function() { return poly.touchToMouse('mouseover', touchInfo); }), 800); return window.alert = function() { var args; window.clearTimeout(touchInfo.pressTimer); args = arguments; return window.setTimeout((function() { _oldAlert.apply(window, args); return window.alert = _oldAlert; }), 100); }; } else if (type === 'touchmove') { elem = graph.paper.getById(event.target.raphaelid); offset = poly.offset(graph.dom); touchPos = poly.getXY(offset, event); if (event.timeStamp - touchInfo.lastStart > 600 && elem.isPointInside(touchPos.x, touchPos.y)) { return poly.touchToMouse('mouseover', touchInfo); } else { window.clearTimeout(touchInfo.pressTimer); return poly.touchToMouse('mouseout', touchInfo); } } else if (type === 'touchend') { window.clearTimeout(touchInfo.pressTimer); poly.touchToMouse('mouseup', touchInfo); poly.touchToMouse('mouseout', touchInfo, 400); if (event.timeStamp - touchInfo.lastStart < 800) { return poly.touchToMouse('click', touchInfo); } } else if (type === 'touchcancel') { window.clearTimeout(touchInfo.pressTimer); poly.touchToMouse('mouseout', touchInfo); return poly.touchToMouse('mouseup', touchInfo, 300); } }; }).call(this); // Generated by CoffeeScript 1.6.2 (function() { var POSTFIXES, formatNumber, postfix; poly.format = function(type, step) { switch (type) { case 'cat': return poly.format.identity; case 'num': return poly.format.number(step); case 'date': return poly.format.date(step); case 'none': return poly.format.identity; } }; poly.format.identity = function(x) { return x; }; POSTFIXES = { 0: '', 3: 'k', 6: 'm', 9: 'b', 12: 't' }; postfix = function(num, pow) { if (!_.isUndefined(POSTFIXES[pow])) { return num + POSTFIXES[pow]; } else { return num + 'e' + (pow > 0 ? '+' : '-') + Math.abs(pow); } }; formatNumber = function(n) { var abs, i, s, v; if (!isFinite(n)) { return n; } s = "" + n; abs = Math.abs(n); if (abs >= 10000) { v = ("" + abs).split(/\./); i = v[0].length % 3 || 3; v[0] = s.slice(0, i + (n < 0)) + v[0].slice(i).replace(/(\d{3})/g, ',$1'); s = v.join('.'); } return s; }; poly.format.getExp = function(num) { return Math.floor(Math.log(Math.abs(num === 0 ? 1 : num)) / Math.LN10); }; poly.format.number = function(exp_original) { return function(num) { var exp, exp_fixed, exp_precision, rounded; exp_fixed = 0; exp_precision = 0; exp = exp_original != null ? exp_original : poly.format.getExp(num); if ((exp_original != null) && (exp === 2 || exp === 5 || exp === 8 || exp === 11)) { exp_fixed = exp + 1; exp_precision = 1; } else if (exp === -1) { exp_fixed = 0; exp_precision = exp_original != null ? 1 : 2; } else if (exp === -2) { exp_fixed = 0; exp_precision = exp_original != null ? 2 : 3; } else if (exp === 1 || exp === 2) { exp_fixed = 0; } else if (exp > 3 && exp < 6) { exp_fixed = 3; } else if (exp > 6 && exp < 9) { exp_fixed = 6; } else if (exp > 9 && exp < 12) { exp_fixed = 9; } else if (exp > 12 && exp < 15) { exp_fixed = 12; } else { exp_fixed = exp; exp_precision = exp_original != null ? 0 : 1; } rounded = Math.round(num / Math.pow(10, exp_fixed - exp_precision)); rounded /= Math.pow(10, exp_precision); rounded = rounded.toFixed(exp_precision); return postfix(formatNumber(rounded), exp_fixed); }; }; poly.format.date = function(format) { var level; if (_.indexOf(poly["const"].timerange, format) !== -1) { level = format; if (level === 'second') { return function(date) { return moment.unix(date).format('h:mm:ss a'); }; } else if (level === 'minute') { return function(date) { return moment.unix(date).format('h:mm a'); }; } else if (level === 'hour') { return function(date) { return moment.unix(date).format('MMM D h a'); }; } else if (level === 'day' || level === 'week') { return function(date) { return moment.unix(date).format('MMM D'); }; } else if (level === 'month' || level === 'twomonth' || level === 'quarter' || level === 'sixmonth') { return function(date) { return moment.unix(date).format('YYYY/MM'); }; } else if (level === 'year' || level === 'twoyear' || level === 'fiveyear' || level === 'decade') { return function(date) { return moment.unix(date).format('YYYY'); }; } else { return function(date) { return moment.unix(date).format('YYYY'); }; } } else { return function(date) { return moment.unix(date).format(format); }; } }; poly.format._number_instance = poly.format.number(); poly.format.value = function(v) { if (_.isNumber(v)) { return poly.format._number_instance(v); } else { return v; } }; }).call(this); // Generated by CoffeeScript 1.6.2 /* Impute types from values */ (function() { var THRESHOLD, compareCat, compareNum; THRESHOLD = 0.95; poly.type = {}; poly.type.impute = function(values) { var date, length, m, num, value, _i, _len; date = 0; num = 0; length = 0; for (_i = 0, _len = values.length; _i < _len; _i++) { value = values[_i]; if (value == null) { continue; } length++; if (!isNaN(value) || !isNaN(value.replace(/\$|\,/g, ''))) { num++; } m = moment(value); if ((m != null) && m.isValid()) { date++; } } if (num > THRESHOLD * length) { return 'num'; } if (date > THRESHOLD * length) { return 'date'; } return 'cat'; }; /* Parse values into correct types */ poly.type.coerce = function(value, meta) { if (_.isUndefined(value) || _.isNull(value)) { return value; } else if (meta.type === 'cat') { return value; } else if (meta.type === 'num') { if (!isNaN(value)) { return +value; } else { return +(("" + value).replace(/\$|\,/g, '')); } } else if (meta.type === 'date') { if (!_.isNumber(value) && _.isEmpty(value)) { return null; } else if (meta.format) { if (meta.format === 'unix') { return moment.unix(value).unix(); } else { return moment(value, meta.format).unix(); } } else { if (isFinite(value) && value >= Math.pow(10, 9)) { return moment.unix(value).unix(); } else { return moment(value).unix(); } } } else { return void 0; } }; poly.type.compare = function(type) { switch (type) { case 'cat': return compareCat; default: return compareNum; } }; compareCat = function(a, b) { var al, bl; if (a === b) { return 0; } if (!_.isString(a)) { a = "" + a; } if (!_.isString(b)) { b = "" + b; } al = a.toLowerCase(); bl = b.toLowerCase(); if (al === bl) { if (a < b) { return -1; } else if (a > b) { return 1; } else { return 0; } } else { if (al < bl) { return -1; } else if (al > bl) { return 1; } else { return 0; } } }; compareNum = function(a, b) { if (a === b) { return 0; } else if (a === null) { return 1; } else if (b === null) { return -1; } else if (a < b) { return -1; } else if (a > b) { return 1; } else { return 0; } }; }).call(this); // Generated by CoffeeScript 1.6.2 /* Turns a 'non-strict' spec to a strict one. See the spec definition for more information. */ (function() { var LST, LayerSpecTranslator, NST, NumeralSpecTranslator, PST, PivotSpecTranslator, SpecTranslator, _ref, _ref1, _ref2, __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; poly.spec = {}; poly.spec.toStrictMode = function(spec) { var aes, facetvar, i, layer, v, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2; spec = _.clone(spec); if ((spec.layers == null) && spec.layer) { spec.layers = [spec.layer]; } if ((spec.guides == null) && spec.guide) { spec.guides = spec.guide; } if (spec.guides == null) { spec.guides = {}; } if (spec.layers) { _ref = spec.layers; for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) { layer = _ref[i]; _ref1 = poly["const"].aes; for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { aes = _ref1[_j]; if (layer[aes] && _.isString(layer[aes])) { layer[aes] = { "var": layer[aes] }; } } if (layer.sample == null) { layer.sample = 500; } } } if (spec.facet) { _ref2 = ['var', 'x', 'y']; for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) { v = _ref2[_k]; facetvar = spec.facet[v]; if (facetvar && _.isString(facetvar)) { spec.facet[v] = { "var": facetvar }; } } } else { spec.facet = { type: 'none' }; } if (!spec.coord) { spec.coord = { type: 'cartesian', flip: false }; } if (_.isString(spec.dom)) { spec.dom = document.getElementById(spec.dom); } return spec; }; poly.spec.check = function(spec) { var id, layer, _i, _len, _ref; if ((spec.layers == null) || spec.layers.length === 0) { throw poly.error.defn("No layers are defined in the specification."); } _ref = spec.layers; for (id = _i = 0, _len = _ref.length; _i < _len; id = ++_i) { layer = _ref[id]; if (layer.data == null) { throw poly.error.defn("Layer " + (id + 1) + " does not have data to plot!"); } if (!layer.data.isData) { throw poly.error.defn("Data must be a Polychart Data object."); } } if (!((spec.render != null) && spec.render === false) && !spec.dom) { throw poly.error.defn("No DOM element specified. Where to make plot?"); } return spec; }; SpecTranslator = (function() { function SpecTranslator() { this["return"] = __bind(this["return"], this); this.reset = __bind(this.reset, this); this.processGrouping = __bind(this.processGrouping, this); this.processMapping = __bind(this.processMapping, this); this.addSort = __bind(this.addSort, this); this.extractFilters = __bind(this.extractFilters, this); this.translate = __bind(this.translate, this); } SpecTranslator.prototype.translate = function(lspec, grouping) { if (grouping == null) { grouping = []; } }; SpecTranslator.prototype.extractFilters = function(input) { var expr, exprType, filterSpec, key, val, _ref, _results; if (input == null) { input = {}; } _results = []; for (key in input) { filterSpec = input[key]; val = _.clone(filterSpec); _ref = poly.parser.getExpression(key), exprType = _ref.exprType, expr = _ref.expr; val.expr = expr; if (exprType === 'stat') { throw poly.error.defn("Aggregate statistics in filters not allowed."); } if (exprType === 'trans') { this.trans.push(expr); } _results.push(this.filters.push(val)); } return _results; }; SpecTranslator.prototype.addSort = function(desc, expr) { var arg, args, fname, sdesc, sexpr, statinfo, _i, _len, _ref; sexpr = poly.parser.getExpression(desc.sort); statinfo = sexpr.statInfo(); if (statinfo) { fname = statinfo.fname, args = statinfo.args; } else { fname = null; args = []; } sdesc = { key: expr, sort: sexpr.expr, stat: fname, args: args, limit: desc.limit, asc: (_ref = desc.asc) != null ? _ref : false }; for (_i = 0, _len = args.length; _i < _len; _i++) { arg = args[_i]; if (arg.expr[0] !== 'ident') { this.trans.push(arg); } } return this.sort.push(sdesc); }; SpecTranslator.prototype.processMapping = function(desc) { var arg, args, expr, exprType, fname, statInfo, _i, _len, _ref, _ref1; _ref = poly.parser.getExpression(desc["var"]), exprType = _ref.exprType, expr = _ref.expr, statInfo = _ref.statInfo; desc["var"] = expr.name; this.select.push(expr); if (exprType === 'trans') { this.trans.push(expr); } if (exprType === 'stat') { _ref1 = statInfo(), fname = _ref1.fname, args = _ref1.args; for (_i = 0, _len = args.length; _i < _len; _i++) { arg = args[_i]; if (arg.expr[0] !== 'ident') { this.trans.push(arg); } } this.stat.push({ name: fname, args: args, expr: expr }); } else { this.groups.push(expr); } if ('sort' in desc) { return this.addSort(desc, expr); } }; SpecTranslator.prototype.processGrouping = function(grpvar) { var expr, exprType, statInfo, _ref; _ref = poly.parser.getExpression(grpvar["var"]), exprType = _ref.exprType, expr = _ref.expr, statInfo = _ref.statInfo; if (exprType === 'trans') { this.trans.push(expr); } else if (exprType === 'stat') { throw poly.error.defn("Facet variable should not contain statistics!"); } this.select.push(expr); return this.groups.push(expr); }; SpecTranslator.prototype.reset = function() { this.filters = []; this.trans = []; this.stat = []; this.select = []; this.groups = []; return this.sort = []; }; SpecTranslator.prototype["return"] = function() { var dedup, obj; dedup = function(expressions, key) { var dict, e, _i, _len; if (key == null) { key = function(x) { return x.name; }; } dict = {}; for (_i = 0, _len = expressions.length; _i < _len; _i++) { e = expressions[_i]; dict[key(e)] = e; } return _.values(dict); }; obj = { select: dedup(this.select), trans: dedup(this.trans), sort: this.sort, filter: this.filters, stats: { stats: dedup(this.stat, function(x) { return x.expr.name; }), groups: dedup(this.groups) } }; return obj; }; return SpecTranslator; })(); LayerSpecTranslator = (function(_super) { __extends(LayerSpecTranslator, _super); function LayerSpecTranslator() { this.pickAesthetics = __bind(this.pickAesthetics, this); this.translate = __bind(this.translate, this); _ref = LayerSpecTranslator.__super__.constructor.apply(this, arguments); return _ref; } LayerSpecTranslator.prototype.translate = function(lspec, grouping) { var aesthetics, desc, grpvar, key, _i, _len, _ref1; if (grouping == null) { grouping = []; } this.reset(); this.extractFilters((_ref1 = lspec.filter) != null ? _ref1 : {}); aesthetics = this.pickAesthetics(lspec, poly["const"].aes); for (key in aesthetics) { desc = aesthetics[key]; this.processMapping(desc); } for (_i = 0, _len = grouping.length; _i < _len; _i++) { grpvar = grouping[_i]; this.processGrouping(grpvar); } return this["return"](); }; LayerSpecTranslator.prototype.pickAesthetics = function(spec, aes) { var aesthetics, key; aesthetics = _.pick(spec, aes); for (key in aesthetics) { if (!('var' in aesthetics[key])) { delete aesthetics[key]; } } return aesthetics; }; return LayerSpecTranslator; })(SpecTranslator); PivotSpecTranslator = (function(_super) { __extends(PivotSpecTranslator, _super); function PivotSpecTranslator() { this.pickAesthetics = __bind(this.pickAesthetics, this); this.translate = __bind(this.translate, this); _ref1 = PivotSpecTranslator.__super__.constructor.apply(this, arguments); return _ref1; } PivotSpecTranslator.prototype.translate = function(lspec) { var aesthetics, desc, _i, _len, _ref2; this.reset(); this.extractFilters((_ref2 = lspec.filter) != null ? _ref2 : {}); aesthetics = this.pickAesthetics(lspec); for (_i = 0, _len = aesthetics.length; _i < _len; _i++) { desc = aesthetics[_i]; this.processMapping(desc); } return this["return"](); }; PivotSpecTranslator.prototype.pickAesthetics = function(lspec) { var aesthetics, aesthetics_list, item, key, list, _i, _len; aesthetics = _.pick(lspec, ['columns', 'rows', 'values']); aesthetics_list = []; for (key in aesthetics) { list = aesthetics[key]; for (_i = 0, _len = list.length; _i < _len; _i++) { item = list[_i]; if ('var' in item) { aesthetics_list.push(item); } } } return aesthetics_list; }; return PivotSpecTranslator; })(SpecTranslator); NumeralSpecTranslator = (function(_super) { __extends(NumeralSpecTranslator, _super); function NumeralSpecTranslator() { this.pickAesthetics = __bind(this.pickAesthetics, this); this.translate = __bind(this.translate, this); _ref2 = NumeralSpecTranslator.__super__.constructor.apply(this, arguments); return _ref2; } NumeralSpecTranslator.prototype.translate = function(lspec) { var aesthetics, desc, key, _ref3; this.reset(); this.extractFilters((_ref3 = lspec.filter) != null ? _ref3 : {}); aesthetics = this.pickAesthetics(lspec); for (key in aesthetics) { desc = aesthetics[key]; this.processMapping(desc); } return this["return"](); }; NumeralSpecTranslator.prototype.pickAesthetics = function(lspec) { var aesthetics, key; aesthetics = _.pick(lspec, ['value']); for (key in aesthetics) { if (!('var' in aesthetics[key])) { delete aesthetics[key]; } } return aesthetics; }; return NumeralSpecTranslator; })(SpecTranslator); LST = new LayerSpecTranslator(); PST = new PivotSpecTranslator(); NST = new NumeralSpecTranslator(); poly.spec.layerToData = LST.translate; poly.spec.pivotToData = PST.translate; poly.spec.numeralToData = NST.translate; }).call(this); // Generated by CoffeeScript 1.6.2 (function() { poly.xhr = function(url, mime, callback) { var req; req = new XMLHttpRequest; if (arguments.length < 3) { callback = mime; mime = null; } else if (mime && req.overrideMimeType) { req.overrideMimeType(mime); } req.open("GET", url, true); if (mime) { req.setRequestHeader("Accept", mime); } req.onreadystatechange = function() { var arg, s; if (req.readyState === 4) { s = req.status; arg = !s && req.response || s >= 200 && s < 300 || s === 304 ? req : null; return callback(arg); } }; return req.send(null); }; poly.text = function(url, mime, callback) { var ready; ready = function(req) { return callback(req && req.responseText); }; if (arguments.length < 3) { callback = mime; mime = null; } return poly.xhr(url, mime, ready); }; poly.json = function(url, callback) { return poly.text(url, "application/json", function(text) { return callback(text ? JSON.parse(text) : null); }); }; poly.dsv = function(delimiter, mimeType) { var delimiterCode, dsv, formatRow, formatValue, header, reFormat, reParse; reParse = new RegExp("\r\n|[" + delimiter + "\r\n]", "g"); reFormat = new RegExp("[\"" + delimiter + "\n]"); delimiterCode = delimiter.charCodeAt(0); formatRow = function(row) { return row.map(formatValue).join(delimiter); }; formatValue = function(text) { var _ref; return (_ref = reFormat.test(text)) != null ? _ref : "\"" + text.replace(/\"/g, "\"\"") + { "\"": text }; }; header = null; dsv = function(url, callback) { return poly.text(url, mimeType, function(text) { return callback(text && dsv.parse(text)); }); }; dsv.parse = function(text) { return dsv.parseRows(text, function(row, i) { var item, j, m, o; if (i) { o = {}; j = -1; m = header.length; while (++j < m) { item = row[j]; o[header[j]] = row[j]; } return o; } else { header = row; return null; } }); }; dsv.parseRows = function(text, f) { var EOF, EOL, a, eol, n, rows, t, token; EOL = {}; EOF = {}; rows = []; n = 0; t = null; eol = null; reParse.lastIndex = 0; token = function() { var c, i, j, m; if (reParse.lastIndex >= text.length) { return EOF; } if (eol) { eol = false; return EOL; } j = reParse.lastIndex; if (text.charCodeAt(j) === 34) { i = j; while (i++ < text.length) { if (text.charCodeAt(i) === 34) { if (text.charCodeAt(i + 1) !== 34) { break; } i++; } } reParse.lastIndex = i + 2; c = text.charCodeAt(i + 1); if (c === 13) { eol = true; if (text.charCodeAt(i + 2) === 10) { reParse.lastIndex++; } } else if (c === 10) { eol = true; } return text.substring(j + 1, i).replace(/""/g, "\""); } m = reParse.exec(text); if (m) { eol = m[0].charCodeAt(0) !== delimiterCode; return text.substring(j, m.index); } reParse.lastIndex = text.length; return text.substring(j); }; while ((t = token()) !== EOF) { a = []; while (t !== EOL && t !== EOF) { a.push(t); t = token(); } if (f && !(a = f(a, n++))) { continue; } rows.push(a); } return rows; }; dsv.format = function(rows) { return rows.map(formatRow).join("\n"); }; return dsv; }; poly.csv = poly.dsv(",", "text/csv"); }).call(this); // Generated by CoffeeScript 1.6.2 (function() { var BaseType, Call, Comma, Conditional, Const, DataType, DataTypeError, Expr, FuncType, Ident, InfixOp, InfixSymbol, Keyword, LParen, Literal, OpStack, Parser, RParen, Stream, Symbol, Token, UnknownType, assertIs, assertTagIs, bracket, createColTypeEnv, escape, exprJSON, exprType, extractOps, fname, getExpression, getName, getType, infixGTEQ, infixops, infixpat, infixpats, initialFuncTypeEnv, keywords, makeTypeEnv, matchToken, n, normalize, opname, pairNumToNum, parse, quote, s, showCall, showList, str, symbolOrKeyword, tag, tcat, tdate, testColTypeEnv, testExprJSON, testFuncTypeEnv, testTypeCheck, tnum, tokenize, tokenizers, typeCheck, unbracket, unescape, unquote, _i, _j, _k, _l, _len, _len1, _len2, _len3, _len4, _m, _ref, _ref1, _ref2, _ref3, _ref4, _ref5, _ref6, __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, __slice = [].slice, __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; escape = function(str) { return str.replace(/[\[\]\\]/g, function(match) { return '\\' + match; }); }; unescape = function(str) { return str.replace(/\\./g, function(match) { return match.slice(1); }); }; bracket = function(str) { return '[' + escape(str) + ']'; }; unbracket = function(str) { var n; n = str.length; if (str[0] === '[' && str[n - 1] === ']') { str = str.slice(1, +(n - 2) + 1 || 9e9); str = unescape(str); } return str; }; quote = function(str) { return '"' + str.replace(/["\\]/g, function(match) { return '\\' + match; }) + '"'; }; unquote = function(str) { var n, qu, _i, _len, _ref; n = str.length; _ref = ['"', "'"]; for (_i = 0, _len = _ref.length; _i < _len; _i++) { qu = _ref[_i]; if (str[0] === qu && str[n - 1] === qu) { str = str.slice(1, +(n - 2) + 1 || 9e9); str = unescape(str); break; } } return str; }; showCall = function(fname, args) { return "" + fname + "(" + args + ")"; }; showList = function(xs) { return "[" + xs + "]"; }; DataTypeError = (function() { function DataTypeError(message) { this.message = message; } return DataTypeError; })(); DataType = (function() { function DataType(name) { this.name = name; this._unify = __bind(this._unify, this); this._runify = __bind(this._runify, this); this._known_unify = __bind(this._known_unify, this); this.unify = __bind(this.unify, this); this.mismatch = __bind(this.mismatch, this); this.error = __bind(this.error, this); } DataType.prototype.error = function(context, msg) { var cmp, comparison, t0, t1; cmp = (function() { var _i, _len, _ref, _results; _results = []; for (_i = 0, _len = context.length; _i < _len; _i++) { _ref = context[_i], t0 = _ref[0], t1 = _ref[1]; _results.push("(" + t0 + " vs. " + t1 + ")"); } return _results; })(); cmp.reverse(); comparison = cmp.join(' in '); throw new DataTypeError(msg + ': ' + comparison); }; DataType.prototype.mismatch = function(context) { return this.error(context, 'Type mismatch'); }; DataType.prototype.unify = function(type) { return type._known_unify(this); }; DataType.prototype._known_unify = function(type) { this._runify([], type); return this; }; DataType.prototype._runify = function(context, type) { return this._unify(context.concat([[this.toString(), type.toString()]]), type); }; DataType.prototype._unify = function(context, type) { if (this.name !== type.name) { return this.mismatch(context); } }; return DataType; })(); UnknownType = (function(_super) { __extends(UnknownType, _super); function UnknownType() { this._unify = __bind(this._unify, this); this.unify = __bind(this.unify, this); this.toString = __bind(this.toString, this); UnknownType.__super__.constructor.call(this, '?'); this.found = null; } UnknownType.prototype.toString = function() { if (this.found === null) { return '?'; } else { return this.found.toString(); } }; UnknownType.prototype.unify = function(type) { return this._known_unify(type); }; UnknownType.prototype._unify = function(context, type) { if (this.found === null) { return this.found = type; } else { return this.found._unify(context, type); } }; return UnknownType; })(DataType); BaseType = (function(_super) { __extends(BaseType, _super); function BaseType() { this.toString = __bind(this.toString, this); _ref = BaseType.__super__.constructor.apply(this, arguments); return _ref; } BaseType.prototype.toString = function() { return "" + this.name; }; return BaseType; })(DataType); FuncType = (function(_super) { __extends(FuncType, _super); function FuncType(domains, range) { this.domains = domains; this.range = range; this._unify = __bind(this._unify, this); this.toString = __bind(this.toString, this); FuncType.__super__.constructor.call(this, '->'); } FuncType.prototype.toString = function() { var domain, domains; domains = ((function() { var _i, _len, _ref1, _results; _ref1 = this.domains; _results = []; for (_i = 0, _len = _ref1.length; _i < _len; _i++) { domain = _ref1[_i]; _results.push(domain.toString()); } return _results; }).call(this)).join(', '); return "([" + domains + "] -> " + this.range + ")"; }; FuncType.prototype._unify = function(context, type) { var d0, d1, _i, _len, _ref1, _ref2; FuncType.__super__._unify.call(this, context, type); if (this.domains.length !== type.domains.length) { this.error(context, 'function domains differ in length'); } _ref1 = _.zip(this.domains, type.domains); for (_i = 0, _len = _ref1.length; _i < _len; _i++) { _ref2 = _ref1[_i], d0 = _ref2[0], d1 = _ref2[1]; d0._runify(context, d1); } return this.range._runify(context, type.range); }; return FuncType; })(DataType); DataType.Base = _.object((function() { var _ref1, _results; _ref1 = { cat: 'cat', num: 'num', date: 'date', stat: 'stat' }; _results = []; for (n in _ref1) { s = _ref1[n]; _results.push([n, new BaseType(s)]); } return _results; })()); Stream = (function() { function Stream(src) { this.toString = __bind(this.toString, this); this.get = __bind(this.get, this); this.peek = __bind(this.peek, this); this.empty = __bind(this.empty, this); var val; this.buffer = ((function() { var _i, _len, _results; _results = []; for (_i = 0, _len = src.length; _i < _len; _i++) { val = src[_i]; _results.push(val); } return _results; })()).reverse(); } Stream.prototype.empty = function() { return this.buffer.length === 0; }; Stream.prototype.peek = function() { if (this.empty()) { return null; } else { return this.buffer[this.buffer.length - 1]; } }; Stream.prototype.get = function() { if (this.empty()) { return null; } else { return this.buffer.pop(); } }; Stream.prototype.toString = function() { return showCall('Stream', showList(__slice.call(this.buffer).reverse())); }; return Stream; })(); Token = (function() { Token.Tag = { symbol: 'symbol', literal: 'literal', infixsymbol: 'infixsymbol', keyword: 'keyword', lparen: '(', rparen: ')', comma: ',' }; function Token(tag) { this.tag = tag; this.contents = __bind(this.contents, this); this.toString = __bind(this.toString, this); } Token.prototype.toString = function() { return "<" + (this.contents().toString()) + ">"; }; Token.prototype.contents = function() { return [this.tag]; }; return Token; })(); Symbol = (function(_super) { __extends(Symbol, _super); function Symbol(name) { this.name = name; this.contents = __bind(this.contents, this); this.name = unbracket(this.name); Symbol.__super__.constructor.call(this, Token.Tag.symbol); } Symbol.prototype.contents = function() { return Symbol.__super__.contents.call(this).concat([this.name]); }; return Symbol; })(Token); Literal = (function(_super) { __extends(Literal, _super); function Literal(val, type) { this.val = val; this.type = type; this.contents = __bind(this.contents, this); Literal.__super__.constructor.call(this, Token.Tag.literal); if (this.type === DataType.Base.cat) { this.val = unquote(this.val); } } Literal.prototype.contents = function() { return Literal.__super__.contents.call(this).concat([this.val]); }; return Literal; })(Token); InfixSymbol = (function(_super) { __extends(InfixSymbol, _super); function InfixSymbol(op) { this.op = op; this.contents = __bind(this.contents, this); InfixSymbol.__super__.constructor.call(this, Token.Tag.infixsymbol); } InfixSymbol.prototype.contents = function() { return InfixSymbol.__super__.contents.call(this).concat([this.op]); }; return InfixSymbol; })(Token); Keyword = (function(_super) { __extends(Keyword, _super); function Keyword(name) { this.name = name; this.contents = __bind(this.contents, this); Keyword.__super__.constructor.call(this, Token.Tag.keyword); } Keyword.prototype.contents = function() { return Keyword.__super__.contents.call(this).concat([this.name]); }; return Keyword; })(Token); _ref1 = (function() { var _i, _len, _ref1, _results; _ref1 = [Token.Tag.lparen, Token.Tag.rparen, Token.Tag.comma]; _results = []; for (_i = 0, _len = _ref1.length; _i < _len; _i++) { tag = _ref1[_i]; _results.push(new Token(tag)); } return _results; })(), LParen = _ref1[0], RParen = _ref1[1], Comma = _ref1[2]; infixops = ['++', '*', '/', '%', '+', '-', '>=', '>', '<=', '<', '!=', '==']; infixGTEQ = function(lop, rop) { return infixops.indexOf(lop) <= infixops.indexOf(rop); }; infixpats = (function() { var _i, _len, _results; _results = []; for (_i = 0, _len = infixops.length; _i < _len; _i++) { str = infixops[_i]; _results.push(str.replace(/[+*]/g, function(m) { return '(\\' + m + ')'; })); } return _results; })(); infixpat = new RegExp('^(' + infixpats.join('|') + ')'); keywords = ['if', 'then', 'else']; symbolOrKeyword = function(name) { if (__indexOf.call(keywords, name) >= 0) { return new Keyword(name); } return new Symbol(name); }; tokenizers = [ [ /^\(/, function() { return LParen; } ], [ /^\)/, function() { return RParen; } ], [ /^,/, function() { return Comma; } ], [ /^[+-]?(0x[0-9a-fA-F]+|0?\.\d+|[1-9]\d*(\.\d+)?|0)([eE][+-]?\d+)?/, function(val) { return new Literal(val, DataType.Base.num); } ], [/^(([\w|\.]|[^\u0000-\u0080])+|\[((\\.)|[^\\\[\]])+\])/, symbolOrKeyword], [ /^('((\\.)|[^\\'])*'|"((\\.)|[^\\"])+")/, function(val) { return new Literal(val, DataType.Base.cat); } ], [ infixpat, function(op) { return new InfixSymbol(op); } ] ]; matchToken = function(str) { var match, op, pat, substr, _i, _len, _ref2; for (_i = 0, _len = tokenizers.length; _i < _len; _i++) { _ref2 = tokenizers[_i], pat = _ref2[0], op = _ref2[1]; match = pat.exec(str); if (match) { substr = match[0]; return [str.slice(substr.length), op(substr)]; } } throw poly.error.defn("There is an error in your specification at " + str); }; tokenize = function(str) { var tok, _ref2, _results; _results = []; while (true) { str = str.replace(/^\s+/, ''); if (!str) { break; } _ref2 = matchToken(str), str = _ref2[0], tok = _ref2[1]; _results.push(tok); } return _results; }; Expr = (function() { function Expr() {} Expr.prototype.toString = function() { return showCall(this.constructor.name, this.contents()); }; return Expr; })(); Ident = (function(_super) { __extends(Ident, _super); function Ident(name) { this.name = name; this.visit = __bind(this.visit, this); this.pretty = __bind(this.pretty, this); this.contents = __bind(this.contents, this); } Ident.prototype.contents = function() { return [this.name]; }; Ident.prototype.pretty = function() { return bracket(this.name); }; Ident.prototype.visit = function(visitor) { return visitor.ident(this, this.name); }; return Ident; })(Expr); Const = (function(_super) { __extends(Const, _super); function Const(val, vtype) { this.val = val; this.vtype = vtype; this.visit = __bind(this.visit, this); this.pretty = __bind(this.pretty, this); this.contents = __bind(this.contents, this); } Const.prototype.contents = function() { return [this.val]; }; Const.prototype.pretty = function() { if (this.vtype === DataType.Base.cat) { return quote(this.val); } else { return this.val; } }; Const.prototype.visit = function(visitor) { return visitor["const"](this, this.val, this.vtype); }; return Const; })(Expr); Call = (function(_super) { __extends(Call, _super); function Call(fname, args) { this.fname = fname; this.args = args; this.visit = __bind(this.visit, this); this.pretty = __bind(this.pretty, this); this.contents = __bind(this.contents, this); } Call.prototype.contents = function() { return [this.fname, showList(this.args)]; }; Call.prototype.pretty = function() { var arg; return showCall(this.fname, (function() { var _i, _len, _ref2, _results; _ref2 = this.args; _results = []; for (_i = 0, _len = _ref2.length; _i < _len; _i++) { arg = _ref2[_i]; _results.push(arg.pretty()); } return _results; }).call(this)); }; Call.prototype.visit = function(visitor) { var arg; return visitor.call(this, this.fname, (function() { var _i, _len, _ref2, _results; _ref2 = this.args; _results = []; for (_i = 0, _len = _ref2.length; _i < _len; _i++) { arg = _ref2[_i]; _results.push(arg.visit(visitor)); } return _results; }).call(this)); }; return Call; })(Expr); InfixOp = (function(_super) { __extends(InfixOp, _super); function InfixOp(opsym, lhs, rhs) { this.opsym = opsym; this.lhs = lhs; this.rhs = rhs; this.visit = __bind(this.visit, this); this.pretty = __bind(this.pretty, this); this.contents = __bind(this.contents, this); } InfixOp.prototype.contents = function() { return [this.lhs, this.opsym, this.rhs]; }; InfixOp.prototype.pretty = function() { return '(' + [this.lhs.pretty(), this.opsym, this.rhs.pretty()].join(' ') + ')'; }; InfixOp.prototype.visit = function(visitor) { return visitor.infixop(this, this.opsym, this.lhs.visit(visitor), this.rhs.visit(visitor)); }; return InfixOp; })(Expr); Conditional = (function(_super) { __extends(Conditional, _super); function Conditional(condition, consequent, alternative) { this.condition = condition; this.consequent = consequent; this.alternative = alternative; this.visit = __bind(this.visit, this); this.pretty = __bind(this.pretty, this); this.contents = __bind(this.contents, this); } Conditional.prototype.contents = function() { return [this.condition, this.consequent, this.alternative]; }; Conditional.prototype.pretty = function() { return ("(if " + (this.condition.pretty()) + " ") + ("then " + (this.consequent.pretty()) + " else " + (this.alternative.pretty()) + ")"); }; Conditional.prototype.visit = function(visitor) { return visitor.conditional(this, this.condition.visit(visitor), this.consequent.visit(visitor), this.alternative.visit(visitor)); }; return Conditional; })(Expr); OpStack = (function() { function OpStack() { this.finish = __bind(this.finish, this); this.push = __bind(this.push, this); this._reduce = __bind(this._reduce, this); this.ops = []; } OpStack.prototype._reduce = function(rhs, pred) { var lhs, lop, _ref2; while (this.ops.length !== 0) { _ref2 = this.ops.pop(), lhs = _ref2[0], lop = _ref2[1]; if (pred(lop)) { rhs = new InfixOp(lop, lhs, rhs); } else { this.ops.push([lhs, lop]); break; } } return rhs; }; OpStack.prototype.push = function(rhs, op) { var pred; pred = function(lop) { return infixGTEQ(lop, op); }; rhs = this._reduce(rhs, pred); return this.ops.push([rhs, op]); }; OpStack.prototype.finish = function(rhs) { var pred; pred = function(lop) { return true; }; return this._reduce(rhs, pred); }; return OpStack; })(); assertIs = function(received, expected) { if (received !== expected) { throw poly.error.defn("Expected " + expected + " but received " + received); } }; assertTagIs = function(received, tag) { return assertIs(received.tag, tag); }; Parser = (function() { function Parser(stream) { this.stream = stream; this.parseFinish = __bind(this.parseFinish, this); this.parseInfix = __bind(this.parseInfix, this); this.parseCallArgs = __bind(this.parseCallArgs, this); this.parseCall = __bind(this.parseCall, this); this.parseAtomCall = __bind(this.parseAtomCall, this); this.parseParenExpr = __bind(this.parseParenExpr, this); this.parseConditional = __bind(this.parseConditional, this); this.parseKeyword = __bind(this.parseKeyword, this); this.parseKeywordExpr = __bind(this.parseKeywordExpr, this); this.parseExpr = __bind(this.parseExpr, this); this.parseSubExpr = __bind(this.parseSubExpr, this); this.parseTopExpr = __bind(this.parseTopExpr, this); this.parseFail = __bind(this.parseFail, this); this.expect = __bind(this.expect, this); this.ops = new OpStack(); } Parser.prototype.expect = function(fail, alts) { var express, token, _i, _len, _ref2, _ref3; token = this.stream.peek(); if (token !== null) { for (_i = 0, _len = alts.length; _i < _len; _i++) { _ref2 = alts[_i], tag = _ref2[0], express = _ref2[1]; if (_ref3 = token.tag, __indexOf.call(tag, _ref3) >= 0) { return express(); } } } return fail(); }; Parser.prototype.parseFail = function() { throw poly.error.defn("There is an error in your specification at " + (this.stream.toString())); }; Parser.prototype.parseTopExpr = function() { var expr; expr = this.parseExpr(); if (this.stream.peek() !== null) { this.parseFail(); } return expr; }; Parser.prototype.parseSubExpr = function() { var parser; parser = new Parser(this.stream); return parser.parseExpr(); }; Parser.prototype.parseExpr = function() { var expr; expr = this.expect(this.parseFail, [[[Token.Tag.lparen], this.parseParenExpr], [[Token.Tag.keyword], this.parseKeywordExpr], [[Token.Tag.literal, Token.Tag.symbol], this.parseAtomCall]]); return this.expect(this.parseFinish(expr), [[[Token.Tag.infixsymbol], this.parseInfix(expr)]]); }; Parser.prototype.parseKeywordExpr = function() { var kw; kw = this.stream.peek(); assertTagIs(kw, Token.Tag.keyword); switch (kw.name) { case 'if': return this.parseConditional(); default: return this.parseFail(); } }; Parser.prototype.parseKeyword = function(expected) { var kw; kw = this.stream.get(); assertTagIs(kw, Token.Tag.keyword); return assertIs(kw.name, expected); }; Parser.prototype.parseConditional = function() { var altern, cond, conseq; this.parseKeyword('if'); cond = this.parseSubExpr(); this.parseKeyword('then'); conseq = this.parseSubExpr(); this.parseKeyword('else'); altern = this.parseSubExpr(); return new Conditional(cond, conseq, altern); }; Parser.prototype.parseParenExpr = function() { var expr; assertIs(this.stream.get(), LParen); expr = this.parseSubExpr(); assertIs(this.stream.get(), RParen); return expr; }; Parser.prototype.parseAtomCall = function() { var atom, tok; tok = this.stream.get(); atom = tok.tag === Token.Tag.literal ? new Const(tok.val, tok.type) : tok.tag === Token.Tag.symbol ? new Ident(tok.name) : assertIs(false, true); return this.expect((function() { return atom; }), [[[Token.Tag.lparen], this.parseCall(tok)]]); }; Parser.prototype.parseCall = function(tok) { var _this = this; return function(stream) { var args, name; assertTagIs(tok, Token.Tag.symbol); assertIs(_this.stream.get(), LParen); name = tok.name; args = _this.expect(_this.parseCallArgs([]), [ [ [Token.Tag.rparen], (function() { _this.stream.get(); return []; }) ] ]); return new Call(name, args); }; }; Parser.prototype.parseCallArgs = function(acc) { var _this = this; return function() { var arg, args; arg = _this.parseSubExpr(); args = acc.concat([arg]); return _this.expect(_this.parseFail, [ [ [Token.Tag.rparen], (function() { _this.stream.get(); return args; }) ], [ [Token.Tag.comma], (function() { _this.stream.get(); return _this.parseCallArgs(args)(); }) ] ]); }; }; Parser.prototype.parseInfix = function(rhs) { var _this = this; return function() { var op, optok; optok = _this.stream.get(); assertTagIs(optok, Token.Tag.infixsymbol); op = optok.op; _this.ops.push(rhs, op); return _this.parseExpr(); }; }; Parser.prototype.parseFinish = function(expr) { var _this = this; return function() { return _this.ops.finish(expr); }; }; return Parser; })(); parse = function(str) { var parser, stream; stream = new Stream(tokenize(str)); parser = new Parser(stream); return parser.parseTopExpr(); }; exprType = function(funcTypeEnv, colTypeEnv, expr) { var tapply, visitor; tapply = function(fname, targs) { var tfunc, tresult; if (!(fname in funcTypeEnv)) { throw poly.error.defn("Unknown function name: " + fname); } if (fname === '++' && targs.length === 2) { if (targs[1] === tnum && targs[0] === tcat) { fname = "++_num1"; } if (targs[0] === tnum && targs[1] === tcat) { fname = "++_num2"; } } if (fname === 'bin' && targs.length === 2 && targs[0] === tdate) { fname = 'bin_date'; } if ((fname === 'min' || fname === 'max') && targs.length === 1 && targs[0] === tdate) { fname = fname + '_date'; } if ((fname === 'count' || fname === 'unique' || fname === 'lag') && targs.length === 1) { if (targs[0] === tcat) { fname = fname + '_cat'; } else if (targs[0] === tdate) { fname = fname + '_date'; } } if (fname === 'parseDate' && targs.length === 1) { fname = 'parseDateDefault'; } tfunc = funcTypeEnv[fname]; tresult = new UnknownType; tfunc.unify(new FuncType(targs, tresult)); return tresult.found; }; visitor = { ident: function(expr, name) { if (name in colTypeEnv) { return colTypeEnv[name]; } else { throw poly.error.defn("Unknown column name: " + name); } }, "const": function(expr, val, type) { return type; }, call: function(expr, fname, targs) { return tapply(fname, targs); }, infixop: function(expr, opname, tlhs, trhs) { return tapply(opname, [tlhs, trhs]); }, conditional: function(expr, tcond, tconseq, taltern) { tcond.unify(DataType.Base.num); tconseq.unify(taltern); return tconseq; } }; return expr.visit(visitor); }; tcat = DataType.Base.cat; tnum = DataType.Base.num; tdate = DataType.Base.date; pairNumToNum = new FuncType([tnum, tnum], tnum); initialFuncTypeEnv = { '++': new FuncType([tcat, tcat], tcat), '++_num1': new FuncType([tcat, tnum], tcat), '++_num2': new FuncType([tnum, tcat], tcat) }; _ref2 = ['*', '/', '%', '+', '-', '>=', '>', '<=', '<', '!=', '==', '=']; for (_i = 0, _len = _ref2.length; _i < _len; _i++) { opname = _ref2[_i]; initialFuncTypeEnv[opname] = pairNumToNum; } _ref3 = ['sum', 'mean', 'box', 'median']; for (_j = 0, _len1 = _ref3.length; _j < _len1; _j++) { fname = _ref3[_j]; initialFuncTypeEnv[fname] = new FuncType([tnum], DataType.Base.stat); } _ref4 = ['min', 'max']; for (_k = 0, _len2 = _ref4.length; _k < _len2; _k++) { fname = _ref4[_k]; initialFuncTypeEnv[fname] = new FuncType([tnum], DataType.Base.stat); initialFuncTypeEnv[fname + '_date'] = new FuncType([tdate], DataType.Base.stat); } _ref5 = ['count', 'unique']; for (_l = 0, _len3 = _ref5.length; _l < _len3; _l++) { fname = _ref5[_l]; initialFuncTypeEnv[fname] = new FuncType([tnum], DataType.Base.stat); initialFuncTypeEnv[fname + '_cat'] = new FuncType([tcat], DataType.Base.stat); initialFuncTypeEnv[fname + '_date'] = new FuncType([tdate], DataType.Base.stat); } _ref6 = ['lag']; for (_m = 0, _len4 = _ref6.length; _m < _len4; _m++) { fname = _ref6[_m]; initialFuncTypeEnv[fname] = new FuncType([tnum, tnum], tnum); initialFuncTypeEnv[fname + '_cat'] = new FuncType([tcat, tnum], tcat); initialFuncTypeEnv[fname + '_date'] = new FuncType([tdate, tnum], tdate); } initialFuncTypeEnv.log = new FuncType([tnum], tnum); initialFuncTypeEnv.substr = new FuncType([tcat, tnum, tnum], tcat); initialFuncTypeEnv.length = new FuncType([tcat], tnum); initialFuncTypeEnv.upper = new FuncType([tcat], tcat); initialFuncTypeEnv.lower = new FuncType([tcat], tcat); initialFuncTypeEnv.indexOf = new FuncType([tcat, tcat], tnum); initialFuncTypeEnv.parseNum = new FuncType([tcat], tnum); initialFuncTypeEnv.parseDate = new FuncType([tcat, tcat], tdate); initialFuncTypeEnv.parseDateDefault = new FuncType([tcat], tdate); initialFuncTypeEnv.year = new FuncType([tdate], tnum); initialFuncTypeEnv.month = new FuncType([tdate], tnum); initialFuncTypeEnv.dayOfMonth = new FuncType([tdate], tnum); initialFuncTypeEnv.dayOfYear = new FuncType([tdate], tnum); initialFuncTypeEnv.dayOfWeek = new FuncType([tdate], tnum); initialFuncTypeEnv.hour = new FuncType([tdate], tnum); initialFuncTypeEnv.minute = new FuncType([tdate], tnum); initialFuncTypeEnv.second = new FuncType([tdate], tnum); initialFuncTypeEnv['bin'] = new FuncType([tnum, tnum], tnum); initialFuncTypeEnv['bin_date'] = new FuncType([tdate, tcat], tdate); exprJSON = function(expr) { var visitor; visitor = { ident: function(expr, name) { return [ 'ident', { name: name } ]; }, "const": function(expr, val, type) { return [ 'const', { value: val, type: type.name } ]; }, call: function(expr, fname, args) { return [ 'call', { fname: fname, args: args } ]; }, infixop: function(expr, opname, lhs, rhs) { return [ 'infixop', { opname: opname, lhs: lhs, rhs: rhs } ]; }, conditional: function(expr, cond, conseq, altern) { return [ 'conditional', { cond: cond, conseq: conseq, altern: altern } ]; } }; return expr.visit(visitor); }; extractOps = function(expr) { var extractor, results; results = { trans: [], stat: [] }; extractor = { ident: function(expr, name) { return expr; }, "const": function(expr, val, type) { return expr; }, call: function(expr, fname, args) { var opargs, optype, result; optype = fname in poly["const"].trans ? 'trans' : fname in poly["const"].stat ? 'stat' : 'none'; if (optype !== 'none') { opargs = poly["const"][optype][fname]; result = _.object(opargs, args); result.name = expr; result[optype] = fname; results[optype].push(result); return result.name; } else { throw poly.error.defn("The operation " + fname + " is not recognized. Please check your specifications."); } } }; expr.visit(extractor); return results; }; testTypeCheck = function() { var a0, a1, b0, b1, u0; b0 = DataType.Base.cat; b1 = DataType.Base.num; u0 = new UnknownType; a0 = new FuncType([b0, b1, u0, b1], b1); a1 = new FuncType([b0, b1, b0, u0], b1); return a0.unify(a1); }; testFuncTypeEnv = _.clone(initialFuncTypeEnv); testFuncTypeEnv.sum = new FuncType([tnum], DataType.Base.stat); testFuncTypeEnv.log = new FuncType([tnum], tnum); testFuncTypeEnv.nameCollision = new FuncType([tcat], tnum); testColTypeEnv = { x: tnum, nameCollision: tcat }; typeCheck = function(str) { var expr; expr = parse(str); return exprType(testFuncTypeEnv, testColTypeEnv, expr); }; testExprJSON = function(str) { var expr; expr = parse(str); return exprJSON(expr); }; createColTypeEnv = function(metas) { var colTypeEnv, key, meta; colTypeEnv = {}; for (key in metas) { meta = metas[key]; colTypeEnv[key] = DataType.Base[meta.type]; } return colTypeEnv; }; getType = function(str, typeEnv, combineStat) { var type; if (combineStat == null) { combineStat = true; } type = exprType(initialFuncTypeEnv, typeEnv, parse(str)); if (combineStat && type.name === 'stat') { return 'num'; } else { return type.name; } }; getExpression = function(str) { var etc, expr, exprObj, obj, rootType, statInfo, type, _ref7, _ref8; if (str === 'count(*)') { str = 'count(1)'; } expr = parse(str); exprObj = function(e) { return { name: e.pretty(), expr: exprJSON(e) }; }; statInfo = function() {}; obj = exprObj(expr); _ref7 = obj.expr, rootType = _ref7[0], etc = _ref7[1]; type = rootType === "ident" ? 'ident' : _.has(expr, 'fname') && ((_ref8 = expr.fname) === 'sum' || _ref8 === 'count' || _ref8 === 'unique' || _ref8 === 'mean' || _ref8 === 'box' || _ref8 === 'median' || _ref8 === 'min' || _ref8 === 'max') ? (statInfo = function() { var a; return { fname: expr.fname, args: (function() { var _len5, _n, _ref9, _results; _ref9 = expr.args; _results = []; for (_n = 0, _len5 = _ref9.length; _n < _len5; _n++) { a = _ref9[_n]; _results.push(exprObj(a)); } return _results; })() }; }, 'stat') : 'trans'; return { exprType: type, expr: obj, statInfo: statInfo }; }; makeTypeEnv = function(meta) {}; getName = function(str) { var e, expr; try { expr = parse(str); if ('name' in expr) { return expr.name; } } catch (_error) { e = _error; } return str; }; normalize = function(str) { return getName(parse(str).pretty()); }; poly.parser = { tj: testExprJSON, tc: typeCheck, ttc: testTypeCheck, createColTypeEnv: createColTypeEnv, getExpression: getExpression, getType: getType, tokenize: tokenize, parse: parse, bracket: bracket, unbracket: getName, normalize: normalize, escape: escape, unescape: unescape }; }).call(this); // Generated by CoffeeScript 1.6.2 /* Coordinates ----------- Defines what coordinate system is used to plot the graph. */ (function() { var Cartesian, Coordinate, Polar, _ref, _ref1, __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, _this = this; Coordinate = (function() { function Coordinate(spec) { var _ref, _ref1, _ref2; this.spec = spec; if ((_ref = this.spec) == null) { this.spec = {}; } this.flip = (_ref1 = this.spec.flip) != null ? _ref1 : false; this.scales = null; _ref2 = this.flip ? ['y', 'x'] : ['x', 'y'], this.x = _ref2[0], this.y = _ref2[1]; } Coordinate.prototype.make = function(dims) { return this.dims = dims; }; Coordinate.prototype.setScales = function(scales) { return this.scales = { x: scales.x.f, y: scales.y.f }; }; Coordinate.prototype.clipping = function(offset) { return [offset.x, offset.y, this.dims.eachWidth, this.dims.eachHeight]; }; Coordinate.prototype.getScale = function(aes) {}; Coordinate.prototype.ranges = function() {}; return Coordinate; })(); Cartesian = (function(_super) { __extends(Cartesian, _super); function Cartesian() { _ref = Cartesian.__super__.constructor.apply(this, arguments); return _ref; } Cartesian.prototype.type = 'cartesian'; Cartesian.prototype.getScale = function(aes) { if (aes === 'x' || aes === 'y') { return this.scales[this[aes]]; } else { throw poly.error.input("Coordinates only keep x & y scales"); } }; Cartesian.prototype.ranges = function() { var ranges; ranges = {}; ranges[this.x] = { min: 0, max: this.dims.eachWidth }; ranges[this.y] = { min: this.dims.eachHeight, max: 0 }; return ranges; }; Cartesian.prototype.axisType = function(aes) { return this[aes]; }; Cartesian.prototype.getXY = function(mayflip, mark) { var point, scalex, scaley; if (mayflip) { point = { x: _.isArray(mark.x) ? _.map(mark.x, this.scales.x) : this.scales.x(mark.x), y: _.isArray(mark.y) ? _.map(mark.y, this.scales.y) : this.scales.y(mark.y) }; return { x: point[this.x], y: point[this.y] }; } else { scalex = this.scales[this.x]; scaley = this.scales[this.y]; return { x: _.isArray(mark.x) ? _.map(mark.x, scalex) : scalex(mark.x), y: _.isArray(mark.y) ? _.map(mark.y, scaley) : scaley(mark.y) }; } }; Cartesian.prototype.getAes = function(pixel1, pixel2, reverse) { return { x: reverse.x(pixel1[this.x], pixel2[this.x]), y: reverse.y(pixel1[this.y], pixel2[this.y]) }; }; return Cartesian; })(Coordinate); Polar = (function(_super) { __extends(Polar, _super); function Polar() { this.getXY = __bind(this.getXY, this); _ref1 = Polar.__super__.constructor.apply(this, arguments); return _ref1; } Polar.prototype.type = 'polar'; Polar.prototype.make = function(dims) { this.dims = dims; this.cx = this.dims.eachWidth / 2; return this.cy = this.dims.eachHeight / 2; }; Polar.prototype.getScale = function(aes) { if (aes === 'r') { return this.scales[this.x]; } else if (aes === 't') { return this.scales[this.y]; } else { throw poly.error.input("Coordinates only keep r & t scales"); } }; Polar.prototype.ranges = function() { var r, ranges, t, _ref2; _ref2 = [this.x, this.y], r = _ref2[0], t = _ref2[1]; ranges = {}; ranges[t] = { min: 0, max: 2 * Math.PI }; ranges[r] = { min: 0, max: Math.min(this.dims.eachWidth, this.dims.eachHeight) / 2 - 10 }; return ranges; }; Polar.prototype.axisType = function(aes) { if (this[aes] === 'x') { return 'r'; } else { return 't'; } }; Polar.prototype.getXY = function(mayflip, mark) { var getpos, i, ident, points, r, radius, t, theta, x, xpos, y, ypos, _getx, _gety, _i, _j, _len, _len1, _ref2, _ref3, _ref4, _ref5, _this = this; _getx = function(radius, theta) { return _this.cx + radius * Math.cos(theta - Math.PI / 2); }; _gety = function(radius, theta) { return _this.cy + radius * Math.sin(theta - Math.PI / 2); }; _ref2 = [this.x, this.y], r = _ref2[0], t = _ref2[1]; if (mayflip) { if (_.isArray(mark[r])) { points = { x: [], y: [], r: [], t: [] }; _ref3 = mark[r]; for (i = _i = 0, _len = _ref3.length; _i < _len; i = ++_i) { radius = _ref3[i]; radius = this.scales[r](radius); theta = this.scales[t](mark[t][i]); points.x.push(_getx(radius, theta)); points.y.push(_gety(radius, theta)); points.r.push(radius); points.t.push(theta); } return points; } radius = this.scales[r](mark[r]); theta = this.scales[t](mark[t]); return { x: _getx(radius, theta), y: _gety(radius, theta), r: radius, t: theta }; } ident = function(obj) { return _.isObject(obj) && obj.t === 'scalefn' && obj.f === 'identity'; }; getpos = function(x, y) { var identx, identy; identx = ident(x); identy = ident(y); if (identx && !identy) { return { x: x.v, y: _gety(_this.scales[r](y), 0) }; } else if (identx && identy) { return { x: x.v, y: y.v }; } else if (!identx && identy) { return { y: y.v, x: _gety(_this.scales[t](x), 0) }; } else { radius = _this.scales[r](y); theta = _this.scales[t](x); return { x: _getx(radius, theta), y: _gety(radius, theta) }; } }; if (_.isArray(mark.x)) { points = { x: [], y: [] }; _ref4 = mark.x; for (i = _j = 0, _len1 = _ref4.length; _j < _len1; i = ++_j) { xpos = _ref4[i]; ypos = mark.y[i]; _ref5 = getpos(xpos, ypos), x = _ref5.x, y = _ref5.y; points.x.push(x); points.y.push(y); } return points; } return getpos(mark.x, mark.y); }; return Polar; })(Coordinate); poly.coord = { cartesian: function(spec) { return new Cartesian(spec); }, polar: function(spec) { return new Polar(spec); } }; poly.coord.make = function(spec) { if ((spec == null) || (spec.type == null)) { return poly.coord.cartesian(); } switch (spec.type) { case 'cartesian': return poly.coord.cartesian(spec); case 'polar': return poly.coord.polar(spec); default: throw poly.error.defn("No such coordinate type " + spec.type + "."); } }; }).call(this); // Generated by CoffeeScript 1.6.2 /* # CONSTANTS */ (function() { var CategoricalDomain, DateDomain, NumericDomain, aesthetics, domainMerge, flattenGeoms, makeDomain, makeDomainSet, mergeDomains, __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; aesthetics = poly["const"].aes; /* # GLOBALS */ poly.domain = {}; /* Produce a domain set for each layer based on both the information in each layer and the specification of the guides, then merge them into one domain set. */ poly.domain.make = function(geoms, metas, guideSpec, strictmode) { var domainSets, g, i; domainSets = []; for (i in geoms) { g = geoms[i]; domainSets.push(makeDomainSet(g.geoms, metas[i], guideSpec, strictmode)); } return poly.domain.merge(domainSets); }; poly.domain.compare = function(domain) { if (domain) { if (domain.type === 'cat') { return function(a, b) { a = _.indexOf(domain.levels, a); b = _.indexOf(domain.levels, b); if (a === -1) { return 1; } else if (b === -1) { return -1; } else if (a < b) { return -1; } else if (a > b) { return 1; } else { return 0; } }; } else { return poly.type.compare(domain.type); } } else { return function(x) { return x; }; } }; /* # CLASSES & HELPER */ /* Domain classes */ NumericDomain = (function() { function NumericDomain(params) { this.type = params.type, this.min = params.min, this.max = params.max, this.bw = params.bw; } return NumericDomain; })(); DateDomain = (function() { function DateDomain(params) { this.type = params.type, this.min = params.min, this.max = params.max, this.bw = params.bw; } return DateDomain; })(); CategoricalDomain = (function() { function CategoricalDomain(params) { this.type = params.type, this.levels = params.levels, this.sorted = params.sorted; } return CategoricalDomain; })(); /* Public-ish interface for making different domain types */ makeDomain = function(params) { if (params.type !== 'cat' && params.max === params.min) { if (params.bw) { params.max += params.bw; params.min -= params.bw; } else if (params.max === 0) { params.max += 1; } else { params.max *= 1.1; params.min /= 1.1; } } switch (params.type) { case 'num': return new NumericDomain(params); case 'date': return new DateDomain(params); case 'cat': return new CategoricalDomain(params); } }; /* Make a single domain out of some set of values */ poly.domain.single = function(values, meta, guide) { var bw, fromspec, max, min, _ref, _ref1, _ref10, _ref11, _ref12, _ref13, _ref14, _ref15, _ref2, _ref3, _ref4, _ref5, _ref6, _ref7, _ref8, _ref9; if (values.length === 0) { throw poly.error.input("Dataset is none?"); } fromspec = function(item) { if (guide != null) { return guide[item]; } else { return null; } }; switch (meta.type) { case 'num': bw = (_ref = fromspec('bw')) != null ? _ref : meta.bw; if (values.length > 1) { min = (_ref1 = fromspec('min')) != null ? _ref1 : _.min(values); max = (_ref2 = fromspec('max')) != null ? _ref2 : _.max(values) + (bw != null ? bw : 0); } else if (values.length === 1) { if (bw) { min = (_ref3 = fromspec('min')) != null ? _ref3 : values[0]; max = (_ref4 = fromspec('max')) != null ? _ref4 : values[0] + bw; } else { min = (_ref5 = fromspec('min')) != null ? _ref5 : values[0] - 1; max = (_ref6 = fromspec('max')) != null ? _ref6 : values[0] + 1; } } else { min = (_ref7 = fromspec('min')) != null ? _ref7 : 0; max = (_ref8 = (_ref9 = fromspec('max')) != null ? _ref9 : bw) != null ? _ref8 : 1; } return makeDomain({ type: 'num', min: min, max: max, bw: bw }); case 'date': bw = (_ref10 = fromspec('bw')) != null ? _ref10 : meta.bw; min = (_ref11 = fromspec('min')) != null ? _ref11 : _.min(values); max = fromspec('max'); if (max == null) { max = _.max(values); max = (function() { switch (bw) { case 'week': return moment.unix(max).add('days', 7).unix(); case 'twomonth': return moment.unix(max).add('months', 2).unix(); case 'quarter': return moment.unix(max).add('months', 4).unix(); case 'sixmonth': return moment.unix(max).add('months', 6).unix(); case 'twoyear': return moment.unix(max).add('years', 2).unix(); case 'fiveyear': return moment.unix(max).add('years', 5).unix(); case 'decade': return moment.unix(max).add('years', 10).unix(); default: return moment.unix(max).add(bw + 's', 1).unix(); } })(); } return makeDomain({ type: 'date', min: min, max: max, bw: bw }); case 'cat': return makeDomain({ type: 'cat', levels: (_ref12 = (_ref13 = fromspec('levels')) != null ? _ref13 : meta.levels) != null ? _ref12 : _.uniq(values), sorted: (_ref14 = (_ref15 = fromspec('levels')) != null ? _ref15 : meta.sorted) != null ? _ref14 : false }); } }; /* Make a domain set. A domain set is an associate array of domains, with the keys being aesthetics */ makeDomainSet = function(geoms, metas, guideSpec, strictmode) { var aes, domain, guide, meta, values; domain = {}; for (aes in metas) { meta = metas[aes]; guide = guideSpec[aes]; if (__indexOf.call(poly["const"].noDomain, aes) >= 0) { continue; } if (strictmode) { domain[aes] = makeDomain(guideSpec[aes]); } else { values = flattenGeoms(geoms, aes); domain[aes] = poly.domain.single(values, meta, guide); } } return domain; }; /* VERY preliminary flatten function. Need to optimize */ flattenGeoms = function(geoms, aes) { var geom, k, l, mark, v, values, _i, _len, _ref, _results; values = []; for (k in geoms) { geom = geoms[k]; _ref = geom.marks; for (l in _ref) { mark = _ref[l]; values = values.concat(poly.flatten(mark[aes])); } } _results = []; for (_i = 0, _len = values.length; _i < _len; _i++) { v = values[_i]; if (poly.isDefined(v)) { _results.push(v); } } return _results; }; /* Merge an array of domain sets: i.e. merge all the domains that shares the same aesthetics. */ poly.domain.merge = function(domainSets) { var aes, domains, merged, _i, _len; merged = {}; for (_i = 0, _len = aesthetics.length; _i < _len; _i++) { aes = aesthetics[_i]; domains = _.without(_.pluck(domainSets, aes), void 0); if (domains.length > 0) { merged[aes] = mergeDomains(domains); } } return merged; }; /* Helper for merging domains of the same type. Two domains of the same type can be merged if they share the same properties: - For numeric/date variables all domains must have the same binwidth parameter - For categorial variables, sorted domains must have any categories in common */ domainMerge = { 'num': function(domains) { var bw, max, min, _ref; bw = _.compact(_.uniq(_.map(domains, function(d) { return d.bw; }))); if (bw.length > 1) { throw poly.error.data("Not all layers have the same binwidth."); } bw = (_ref = bw[0]) != null ? _ref : void 0; min = _.min(_.map(domains, function(d) { return d.min; })); max = _.max(_.map(domains, function(d) { return d.max; })); return makeDomain({ type: 'num', min: min, max: max, bw: bw }); }, 'date': function(domains) { var bw, max, min, _ref; bw = _.compact(_.uniq(_.map(domains, function(d) { return d.bw; }))); if (bw.length > 1) { throw poly.error.data("Not all layers have the same binwidth."); } bw = (_ref = bw[0]) != null ? _ref : void 0; min = _.min(_.map(domains, function(d) { return d.min; })); max = _.max(_.map(domains, function(d) { return d.max; })); return makeDomain({ type: 'date', min: min, max: max, bw: bw }); }, 'cat': function(domains) { var add, d, l, levels, sortedLevels, unsortedLevels, _i, _j, _len, _len1; sortedLevels = []; for (_i = 0, _len = domains.length; _i < _len; _i++) { d = domains[_i]; if (d.sorted) { add = true; for (_j = 0, _len1 = sortedLevels.length; _j < _len1; _j++) { l = sortedLevels[_j]; if (_.isEqual(l, d.levels)) { add = false; } } if (add) { sortedLevels.push(d.levels); } } } unsortedLevels = _.chain(domains).filter(function(d) { return !d.sorted; }).map(function(d) { return d.levels; }).value(); if (sortedLevels.length > 1 && _.intersection.apply(this, sortedLevels)) { throw poly.error.data("You are trying to combine incompatible sorted domains in the same axis."); } sortedLevels = [_.flatten(sortedLevels, true)]; levels = _.union.apply(this, sortedLevels.concat(unsortedLevels)); if (sortedLevels[0].length === 0) { levels = levels.sort(); } return makeDomain({ type: 'cat', levels: levels, sorted: sortedLevels[0].length !== 0 }); } }; /* Merge an array of domains: Two domains can be merged if they are of the same type, and they share certain properties. */ mergeDomains = function(domains) { var types; types = _.uniq(_.map(domains, function(d) { return d.type; })); if (types.length > 1) { throw poly.error.data("You are trying to merge data of different types in the same axis or legend."); } return domainMerge[types[0]](domains); }; }).call(this); // Generated by CoffeeScript 1.6.2 /* Tick Generation --------------- Helper functions to legends & axes for generating ticks */ (function() { var Tick, getStep, tickFactory, tickValues; poly.tick = {}; /* Produce an associate array of aesthetics to tick objects. */ poly.tick.make = function(domain, guideSpec, type) { var formatter, i, next, numticks, prev, step, t, tickfn, tickobjs, ticks, tmpTick, _i, _ref, _ref1, _ref2, _this = this; step = null; formatter = function(x) { return x; }; if (guideSpec.ticks != null) { if (type === 'num') { ticks = _.filter(guideSpec.ticks, function(t) { return t >= domain.min && t <= domain.max; }); } else { ticks = guideSpec.ticks; } } else { numticks = (_ref = guideSpec.numticks) != null ? _ref : 5; _ref1 = tickValues[type](domain, numticks), ticks = _ref1.ticks, step = _ref1.step; } if (guideSpec.labels) { formatter = function(x) { var _ref2; return (_ref2 = guideSpec.labels[x]) != null ? _ref2 : x; }; } else if (guideSpec.formatter) { formatter = guideSpec.formatter; } else { formatter = poly.format(type.split('-')[0], step); } tickobjs = {}; tickfn = tickFactory(type, formatter); if (ticks) { for (i = _i = 0, _ref2 = ticks.length - 1; 0 <= _ref2 ? _i <= _ref2 : _i >= _ref2; i = 0 <= _ref2 ? ++_i : --_i) { prev = i === 0 ? null : ticks[i - 1]; next = i === ticks.length - 1 ? null : ticks[i + 1]; t = ticks[i]; tmpTick = tickfn(t, prev, next); tickobjs[tmpTick.value] = tmpTick; } } return { ticks: tickobjs, ticksFormatter: formatter }; }; /* # CLASSES & HELPERS */ /* Tick Object. */ Tick = (function() { function Tick(params) { this.location = params.location, this.value = params.value, this.index = params.index, this.evtData = params.evtData; } return Tick; })(); /* Helper function for creating a function that creates ticks */ tickFactory = function(type, formatter) { var i; i = 0; return function(value, prev, next) { var evtData; if (type === 'cat') { evtData = { "in": [value] }; } else { evtData = {}; if (prev != null) { evtData.ge = prev; } if (next != null) { evtData.le = next; } } return new Tick({ location: value, value: formatter(value), index: i++, evtData: evtData }); }; }; /* Helper function for determining the size of each "step" (distance between ticks) for numeric scales */ getStep = function(span, numticks) { var error, step; step = Math.pow(10, Math.floor(Math.log(span / numticks) / Math.LN10)); error = numticks / span * step; if (error < 0.15) { step *= 10; } else if (error <= 0.35) { step *= 5; } else if (error <= 0.75) { step *= 2; } return step; }; /* Function for calculating the location of ticks. */ tickValues = { 'none': function() { return {}; }, 'cat': function(domain, numticks) { var i, item, len, step, ticks, _i, _len, _ref; len = domain.levels.length; step = Math.max(1, Math.round(len / numticks)); ticks = []; _ref = domain.levels; for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) { item = _ref[i]; if (i % step === 0) { ticks.push(item); } } return { ticks: ticks }; }, 'num': function(domain, numticks) { var bw, max, min, step, ticks, tmp; min = domain.min, max = domain.max, bw = domain.bw; if (bw) { step = bw; while ((max - min) / step > numticks * 1.4) { step *= 2; } tmp = min; } else { step = getStep(max - min, numticks); tmp = Math.ceil(min / step) * step; } ticks = []; while (tmp <= max) { ticks.push(tmp); tmp += step; } return { ticks: ticks, step: Math.floor(Math.log(step) / Math.LN10) }; }, 'num-log': function(domain, numticks) { var exp, lg, lgmax, lgmin, max, min, num, step, ticks, tmp; ticks = []; min = domain.min, max = domain.max; lg = function(v) { return Math.log(v) / Math.LN10; }; exp = function(v) { return Math.exp(v * Math.LN10); }; lgmin = Math.max(lg(min), 0); lgmax = lg(max); step = getStep(lgmax - lgmin, numticks); tmp = Math.ceil(lgmin / step) * step; while (tmp < (lgmax + poly["const"].epsilon)) { if (tmp % 1 !== 0 && tmp % 1 <= 0.1) { tmp += step; continue; } else if (tmp % 1 > poly["const"].epsilon) { num = Math.floor(tmp) + lg(10 * (tmp % 1)); if (num % 1 === 0) { tmp += step; continue; } } else { num = tmp; } num = exp(num); if (num < min || num > max) { tmp += step; continue; } ticks.push(num); tmp += step; } return { ticks: ticks }; }, 'date': function(domain, numticks) { var bw, current, max, min, momentjsStep, secs, step, ticks, timeInSeconds, timeRange, _ref; min = domain.min, max = domain.max, bw = domain.bw; if (bw) { step = bw; while (step !== 'decade' && (max - min) / poly["const"].approxTimeInSeconds[step] > numticks * 1.4) { step = poly["const"].timerange[_.indexOf(poly["const"].timerange, step) + 1]; } } else { secs = (max - min) / numticks; step = 'decade'; _ref = poly["const"].approxTimeInSeconds; for (timeRange in _ref) { timeInSeconds = _ref[timeRange]; if (secs < timeInSeconds * 1.4) { step = timeRange; break; } } } ticks = []; current = moment.unix(min).startOf(step); momentjsStep = (function() { switch (step) { case 'twomonth': return ['months', 2]; case 'quarter': return ['months', 4]; case 'sixmonth': return ['months', 6]; case 'twoyear': return ['years', 2]; case 'fiveyear': return ['years', 5]; case 'decade': return ['years', 10]; default: return [step + 's', 1]; } })(); if (current.unix() < min) { current.add(momentjsStep[0], momentjsStep[1]); } while (current.unix() <= max) { ticks.push(current.unix()); current.add(momentjsStep[0], momentjsStep[1]); } return { ticks: ticks, step: step }; } }; }).call(this); // Generated by CoffeeScript 1.6.2 /* Title (Guide) --------- Classes related to the generation and management of titles. Titles are guides that is a single text: i.e. main titles and axis & facet labels. TODO: This is still the OLD version of Title that does not make use of Geometry/Renderable. This is okay for now since titles are so simple, but not scalable. */ (function() { var Title, TitleFacet, TitleH, TitleMain, TitleV, sf, _ref, _ref1, _ref2, _ref3, _ref4, __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; sf = poly["const"].scaleFns; Title = (function(_super) { __extends(Title, _super); function Title() { this.render = __bind(this.render, this); this.make = __bind(this.make, this); this.position = 'none'; this.titletext = null; this.title = null; } Title.prototype.make = function(params) { var guideSpec, option, position, title, _ref, _this = this; guideSpec = params.guideSpec, title = params.title, position = params.position, this.size = params.size, this.color = params.color; option = function(item, def) { var _ref; return (_ref = guideSpec[item]) != null ? _ref : def; }; this.titletext = option('title', title); this.position = (_ref = option('position', position)) != null ? _ref : this.defaultPosition; if (this.position === 'out') { return this.position = 'bottom'; } }; Title.prototype.render = function(renderer, dim, offset) { if (this.position !== 'none') { if (this.title != null) { renderer.remove(this.title); } return this.title = renderer.add(this._makeTitle(dim, offset), null, null, 'guide-' + this.titleType); } else if (this.title != null) { return renderer.remove(this.title); } }; Title.prototype.dispose = function(renderer) { renderer.remove(this.title); return this.title = null; }; Title.prototype._makeTitle = function() { throw poly.error.impl(); }; Title.prototype.getDimension = function() { var offset; offset = {}; if (this.position !== 'none') { offset[this.position] = 10; } return offset; }; return Title; })(poly.Guide); TitleH = (function(_super) { __extends(TitleH, _super); function TitleH() { _ref = TitleH.__super__.constructor.apply(this, arguments); return _ref; } TitleH.prototype.defaultPosition = 'bottom'; TitleH.prototype.titleType = 'titleH'; TitleH.prototype._makeTitle = function(dim, offset) { var x, y, _ref1, _ref2, _ref3, _ref4; y = this.position === 'top' ? dim.paddingTop + dim.guideTop - ((_ref1 = offset.top) != null ? _ref1 : 0) - 2 : dim.height - dim.paddingBottom - dim.guideBottom + ((_ref2 = offset.bottom) != null ? _ref2 : 0); x = dim.paddingLeft + dim.guideLeft + (dim.width - dim.paddingLeft - dim.guideLeft - dim.paddingRight - dim.guideRight) / 2; return { type: 'text', x: sf.identity(x), y: sf.identity(y), color: sf.identity((_ref3 = this.color) != null ? _ref3 : 'black'), size: sf.identity((_ref4 = this.size) != null ? _ref4 : 12), text: this.titletext, 'text-anchor': 'middle' }; }; return TitleH; })(Title); TitleV = (function(_super) { __extends(TitleV, _super); function TitleV() { _ref1 = TitleV.__super__.constructor.apply(this, arguments); return _ref1; } TitleV.prototype.defaultPosition = 'left'; TitleV.prototype.titleType = 'titleV'; TitleV.prototype._makeTitle = function(dim, offset) { var x, y, _ref2, _ref3, _ref4, _ref5; x = this.position === 'left' ? dim.paddingLeft + dim.guideLeft - ((_ref2 = offset.left) != null ? _ref2 : 0) - 7 : dim.width - dim.paddingRight - dim.guideRight + ((_ref3 = offset.right) != null ? _ref3 : 0); y = dim.paddingTop + dim.guideTop + (dim.height - dim.paddingTop - dim.guideTop - dim.paddingBottom - dim.guideBottom) / 2; return { type: 'text', x: sf.identity(x), y: sf.identity(y), color: sf.identity((_ref4 = this.color) != null ? _ref4 : 'black'), size: sf.identity((_ref5 = this.size) != null ? _ref5 : 12), text: this.titletext, 'text-anchor': 'middle', transform: 'r270' }; }; return TitleV; })(Title); TitleMain = (function(_super) { __extends(TitleMain, _super); function TitleMain() { _ref2 = TitleMain.__super__.constructor.apply(this, arguments); return _ref2; } TitleMain.prototype.titleType = 'title'; TitleMain.prototype._makeTitle = function(dim, offset) { var x, y, _ref3, _ref4; x = dim.width / 2; y = 20; return { type: 'text', x: sf.identity(x), y: sf.identity(y), color: sf.identity((_ref3 = this.color) != null ? _ref3 : 'black'), size: sf.identity((_ref4 = this.size) != null ? _ref4 : 12), text: this.titletext, 'font-size': '13px', 'font-weight': 'bold', 'text-anchor': 'middle' }; }; return TitleMain; })(Title); TitleFacet = (function(_super) { __extends(TitleFacet, _super); function TitleFacet() { this.render = __bind(this.render, this); this.make = __bind(this.make, this); _ref3 = TitleFacet.__super__.constructor.apply(this, arguments); return _ref3; } TitleFacet.prototype.make = function(params) { var title; title = params.title, this.size = params.size, this.color = params.color; return this.titletext = title; }; TitleFacet.prototype.render = function(renderer, dim, offset) { if (this.title != null) { return this.title = renderer.animate(this.title, this._makeTitle(dim, offset)); } else { return this.title = renderer.add(this._makeTitle(dim, offset), null, null, 'guide-facet-title'); } }; TitleFacet.prototype._makeTitle = function(dim, offset) { var _ref4, _ref5; return { type: 'text', x: sf.identity(offset.x + dim.eachWidth / 2), y: sf.identity(offset.y - 7), color: sf.identity((_ref4 = this.color) != null ? _ref4 : 'black'), size: sf.identity((_ref5 = this.size) != null ? _ref5 : 12), text: this.titletext, 'text-anchor': 'middle' }; }; return TitleFacet; })(Title); if ((_ref4 = poly.guide) == null) { poly.guide = {}; } poly.guide.title = function(type) { if (type === 'y' || type === 'r') { return new TitleV(); } else if (type === 'main') { return new TitleMain(); } else if (type === 'facet') { return new TitleFacet(); } else { return new TitleH(); } }; }).call(this); // Generated by CoffeeScript 1.6.2 /* Axis (Guide) --------- Classes related to the generation and management of axes. Like layers, Axis class (and classes that extends Guide) takes in required input about the data domain, scales, etc and produces abstract geometrical objects that can later be rendered using Geometry class. */ (function() { var Axes, Axis, RAxis, TAxis, XAxis, YAxis, axisColorMajor, axisColorMinor, sf, _ref, _ref1, _ref2, _ref3, _ref4, __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; sf = poly["const"].scaleFns; axisColorMajor = '#666'; axisColorMinor = '#EFEFEF'; /* Renders and manages multiple axes, plot over multiple facets. */ Axes = (function(_super) { __extends(Axes, _super); function Axes() { this.axesGeoms = {}; } Axes.prototype.make = function(params) { var _ref, _ref1, _ref2, _ref3; this.domains = params.domains, this.coord = params.coord, this.scales = params.scales, this.specs = params.specs, this.labels = params.labels; return this.axes = { x: poly.guide.axis(this.coord.axisType('x'), { domain: this.domains.x, type: this.scales.x.tickType(), guideSpec: (_ref = this.specs.x) != null ? _ref : {}, key: (_ref1 = this.labels.x) != null ? _ref1 : 'x' }), y: poly.guide.axis(this.coord.axisType('y'), { domain: this.domains.y, type: this.scales.y.tickType(), guideSpec: (_ref2 = this.specs.y) != null ? _ref2 : {}, key: (_ref3 = this.labels.y) != null ? _ref3 : 'y' }) }; }; Axes.prototype.getDimension = function(dims) { var axis, d, key, offset, _ref; offset = {}; _ref = this.axes; for (key in _ref) { axis = _ref[key]; d = axis.getDimension(); if (d.position === 'left') { offset.left = d.width; } else if (d.position === 'right') { offset.right = d.width; } else if (d.position === 'bottom') { offset.bottom = d.height; } else if (d.position === 'top') { offset.top = d.height; } } return offset; }; Axes.prototype.render = function(dims, renderer, facet) { var added, aes, axis, axisDim, deleted, drawx, drawy, indices, k, kept, key, offset, override, pts, r, type, xoverride, yoverride, _base, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2, _ref3, _ref4; indices = _.keys(facet.indices); _ref = poly.compare(_.keys(this.axesGeoms), indices), deleted = _ref.deleted, kept = _ref.kept, added = _ref.added; for (_i = 0, _len = deleted.length; _i < _len; _i++) { key = deleted[_i]; _ref1 = this.axesGeoms[key]; for (type in _ref1) { axis = _ref1[type]; axis.dispose(renderer()); } } axisDim = { top: 0, left: 0, right: dims.eachWidth, bottom: dims.eachHeight, width: dims.eachWidth, height: dims.eachHeight }; drawx = facet.edge(this.axes.x.position); drawy = facet.edge(this.axes.y.position); xoverride = { renderLabel: false, renderTick: false }; yoverride = { renderLabel: false, renderTick: false }; if (this.axes.x.type === 'r') { xoverride.renderLine = false; } if (this.axes.y.type === 'r') { yoverride.renderLine = false; } for (_j = 0, _len1 = indices.length; _j < _len1; _j++) { key = indices[_j]; offset = facet.getOffset(dims, key); if ((_ref2 = (_base = this.axesGeoms)[key]) == null) { _base[key] = { x: new poly.Geometry('guide'), y: new poly.Geometry('guide') }; } r = renderer(offset, false, false); override = drawx(key) ? {} : xoverride; this.axesGeoms[key].x.set(this.axes.x.calculate(axisDim, this.coord, override)); this.axesGeoms[key].x.render(r); override = drawy(key) ? {} : yoverride; this.axesGeoms[key].y.set(this.axes.y.calculate(axisDim, this.coord, override)); this.axesGeoms[key].y.render(r); _ref3 = ['x', 'y']; for (_k = 0, _len2 = _ref3.length; _k < _len2; _k++) { aes = _ref3[_k]; _ref4 = this.axesGeoms[key][aes].pts; for (k in _ref4) { pts = _ref4[k]; if (pts.grid) { pts.grid.toBack(); } } } } }; Axes.prototype.dispose = function(renderer) { var axes, key, _ref; _ref = this.axesGeoms; for (key in _ref) { axes = _ref[key]; axes.x.dispose(renderer); axes.y.dispose(renderer); } return this.axesGeoms = {}; }; return Axes; })(poly.GuideSet); /* Abstract class for a single axis. */ Axis = (function(_super) { __extends(Axis, _super); Axis.prototype.renderTickDefault = true; Axis.prototype.renderGridDefault = true; Axis.prototype.renderLabelDefault = true; Axis.prototype.renderLineDefault = true; function Axis(params) { this.calculate = __bind(this.calculate, this); var domain, guideSpec, key, option, type, _ref, _ref1, _this = this; domain = params.domain, type = params.type, guideSpec = params.guideSpec, key = params.key; option = function(item, def) { var _ref; return (_ref = guideSpec[item]) != null ? _ref : def; }; this.position = option('position', this.defaultPosition); if (_ref = this.position, __indexOf.call(this.validPositions, _ref) < 0) { throw poly.error.defn("X-axis position can't be " + this.position + "."); } this.titletext = option('title', key); this.renderTick = option('renderTick', this.renderTickDefault); this.renderGrid = option('renderGrid', this.renderGridDefault); this.renderLabel = option('renderLabel', this.renderLabelDefault); this.renderLine = option('renderLine', this.renderLineDefault); this.gridColor = option('gridColor', this.gridColor); _ref1 = poly.tick.make(domain, guideSpec, type), this.ticks = _ref1.ticks, this.ticksFormatter = _ref1.ticksFormatter; this.maxwidth = _.max(_.map(this.ticks, function(t) { return poly.strSize(t.value); })); this.maxwidth = Math.max(this.maxwidth, 0); } Axis.prototype.calculate = function(axisDim, coord, override) { var geoms, key, marks, tick, _ref, _ref1, _ref2, _ref3; this.coord = coord; if (this.position === "none") { return {}; } if (override == null) { override = {}; } axisDim.centerx = axisDim.left + axisDim.width / 2; axisDim.centery = axisDim.top + axisDim.height / 2; axisDim.radius = Math.min(axisDim.width, axisDim.height) / 2 - 10; geoms = {}; if (this.renderLine) { geoms['line'] = { marks: { 0: this._renderline(axisDim) } }; } _ref = this.ticks; for (key in _ref) { tick = _ref[key]; marks = {}; if (this.renderTick && ((_ref1 = override.renderTick) != null ? _ref1 : true)) { marks.tick = this._makeTick(axisDim, tick); } if (this.renderLabel && ((_ref2 = override.renderLabel) != null ? _ref2 : true)) { marks.text = this._makeLabel(axisDim, tick); } if (this.renderGrid && ((_ref3 = override.renderGrid) != null ? _ref3 : true)) { marks.grid = this._makeGrid(axisDim, tick); } geoms[key] = { marks: marks }; } return geoms; }; Axis.prototype._makeTick = function(obj) { if (!obj) { throw poly.error.impl(); } obj.type = 'path'; obj.stroke = sf.identity(axisColorMajor); obj.color = sf.identity(axisColorMajor); return obj; }; Axis.prototype._makeLabel = function(obj) { if (!obj) { throw poly.error.impl(); } obj.type = 'text'; obj.stroke = sf.identity(axisColorMajor); obj.color = sf.identity(axisColorMajor); return obj; }; Axis.prototype._makeGrid = function(obj) { if (!obj) { throw poly.error.impl(); } obj.stroke = this.gridColor != null ? this.gridColor : axisColorMinor; return obj; }; return Axis; })(poly.Guide); XAxis = (function(_super) { __extends(XAxis, _super); function XAxis() { _ref = XAxis.__super__.constructor.apply(this, arguments); return _ref; } XAxis.prototype.type = 'x'; XAxis.prototype.renderGridDefault = false; XAxis.prototype.defaultPosition = 'bottom'; XAxis.prototype.validPositions = ['top', 'bottom', 'none']; XAxis.prototype._renderline = function(axisDim) { var x1, x2, y; if (this.position === 'top') { y = sf.identity(axisDim.top); } else { y = sf.identity(axisDim.bottom); } x1 = sf.identity(axisDim.left); x2 = sf.identity(axisDim.left + axisDim.width); return { type: 'path', y: [y, y], x: [x1, x2], stroke: sf.identity(axisColorMajor) }; }; XAxis.prototype._makeTick = function(axisDim, tick) { var y1, y2; if (this.position === 'top') { y1 = sf.identity(axisDim.top); y2 = sf.identity(axisDim.top - 5); } else { y1 = sf.identity(axisDim.bottom); y2 = sf.identity(axisDim.bottom + 5); } return XAxis.__super__._makeTick.call(this, { x: [tick.location, tick.location], y: [y1, y2] }); }; XAxis.prototype._makeLabel = function(axisDim, tick) { var y; if (this.position === 'top') { y = sf.identity(axisDim.top - 15); } else { y = sf.identity(axisDim.bottom + 15); } return XAxis.__super__._makeLabel.call(this, { x: tick.location, y: y, text: tick.value, 'text-anchor': 'middle' }); }; XAxis.prototype._makeGrid = function(axisDim, tick) { var y1, y2; y1 = sf.identity(axisDim.top); y2 = sf.identity(axisDim.bottom); return XAxis.__super__._makeGrid.call(this, { type: 'path', x: [tick.location, tick.location], y: [y1, y2] }); }; XAxis.prototype.getDimension = function() { var _ref1; return { position: (_ref1 = this.position) != null ? _ref1 : 'bottom', height: 30, width: 'all' }; }; return XAxis; })(Axis); YAxis = (function(_super) { __extends(YAxis, _super); function YAxis() { _ref1 = YAxis.__super__.constructor.apply(this, arguments); return _ref1; } YAxis.prototype.type = 'y'; YAxis.prototype.renderLineDefault = false; YAxis.prototype.renderTickDefault = false; YAxis.prototype.defaultPosition = 'left'; YAxis.prototype.validPositions = ['left', 'right', 'none']; YAxis.prototype._renderline = function(axisDim) { var x, y1, y2; if (this.position === 'left') { x = sf.identity(axisDim.left); } else { x = sf.identity(axisDim.right); } y1 = sf.identity(axisDim.top); y2 = sf.identity(axisDim.top + axisDim.height); return { type: 'path', x: [x, x], y: [y1, y2], stroke: sf.identity(axisColorMajor) }; }; YAxis.prototype._makeTick = function(axisDim, tick) { var x1, x2; if (this.position === 'left') { x1 = sf.identity(axisDim.left); x2 = sf.identity(axisDim.left - 5); } else { x1 = sf.identity(axisDim.right); x2 = sf.identity(axisDim.right + 5); } return YAxis.__super__._makeTick.call(this, { x: [x1, x2], y: [tick.location, tick.location] }); }; YAxis.prototype._makeLabel = function(axisDim, tick) { var x; if (this.position === 'left') { x = sf.identity(axisDim.left - 7); } else { x = sf.identity(axisDim.right + 7); } return YAxis.__super__._makeLabel.call(this, { x: x, y: tick.location, text: tick.value, 'text-anchor': this.position === 'left' ? 'end' : 'start' }); }; YAxis.prototype._makeGrid = function(axisDim, tick) { var x1, x2; x1 = sf.identity(axisDim.left); x2 = sf.identity(axisDim.right); return YAxis.__super__._makeGrid.call(this, { type: 'path', y: [tick.location, tick.location], x: [x1, x2] }); }; YAxis.prototype.getDimension = function() { var _ref2; return { position: (_ref2 = this.position) != null ? _ref2 : 'right', height: 'all', width: 5 + this.maxwidth }; }; return YAxis; })(Axis); RAxis = (function(_super) { __extends(RAxis, _super); function RAxis() { _ref2 = RAxis.__super__.constructor.apply(this, arguments); return _ref2; } RAxis.prototype.type = 'r'; RAxis.prototype.defaultPosition = 'left'; RAxis.prototype.validPositions = ['left', 'right', 'none']; RAxis.prototype._renderline = function(axisDim) { var x, y1, y2; x = sf.identity(axisDim.left); y1 = sf.identity(axisDim.top); y2 = sf.identity(axisDim.top + axisDim.height / 2); return { type: 'path', x: [x, x], y: [y1, y2], stroke: sf.identity(axisColorMajor) }; }; RAxis.prototype._makeTick = function(axisDim, tick) { return RAxis.__super__._makeTick.call(this, { x: [sf.identity(axisDim.left), sf.identity(axisDim.left - 5)], y: [tick.location, tick.location] }); }; RAxis.prototype._makeLabel = function(axisDim, tick) { return RAxis.__super__._makeLabel.call(this, { x: sf.identity(axisDim.left - 7), y: tick.location, text: tick.value, 'text-anchor': 'end' }); }; RAxis.prototype._makeGrid = function(axisDim, tick) { return RAxis.__super__._makeGrid.call(this, { type: 'circle', x: sf.identity(axisDim.centerx), y: sf.identity(axisDim.centery), size: sf.identity(this.coord.getScale('r')(tick.location)), color: sf.identity('none'), 'fill-opacity': 0, 'stroke-width': 1 }); }; RAxis.prototype.getDimension = function() { return { position: 'left', height: 'all', width: 5 + this.maxwidth }; }; return RAxis; })(Axis); TAxis = (function(_super) { __extends(TAxis, _super); function TAxis() { _ref3 = TAxis.__super__.constructor.apply(this, arguments); return _ref3; } TAxis.prototype.type = 't'; TAxis.prototype.defaultPosition = 'out'; TAxis.prototype.validPositions = ['out', 'none']; TAxis.prototype._renderline = function(axisDim) { return { type: 'circle', x: sf.identity(axisDim.centerx), y: sf.identity(axisDim.centery), size: sf.identity(axisDim.radius), color: sf.identity('none'), stroke: sf.identity(axisColorMajor), 'stroke-width': 1 }; }; TAxis.prototype._makeTick = function(axisDim, tick) { return TAxis.__super__._makeTick.call(this, { x: [tick.location, tick.location], y: [sf.max(0), sf.max(3)] }); }; TAxis.prototype._makeLabel = function(axisDim, tick) { return TAxis.__super__._makeLabel.call(this, { x: tick.location, y: sf.max(12), text: tick.value, 'text-anchor': 'middle' }); }; TAxis.prototype._makeGrid = function(axisDim, tick) { var theta, x1, x2, y1, y2; x1 = sf.identity(axisDim.centerx); y1 = sf.identity(axisDim.centery); theta = this.coord.getScale('t')(tick.location) - Math.PI / 2; x2 = sf.identity(axisDim.centerx + axisDim.radius * Math.cos(theta)); y2 = sf.identity(axisDim.centery + axisDim.radius * Math.sin(theta)); return TAxis.__super__._makeGrid.call(this, { type: 'path', y: [y1, y2], x: [x1, x2] }); }; TAxis.prototype.getDimension = function() { return {}; }; return TAxis; })(Axis); if ((_ref4 = poly.guide) == null) { poly.guide = {}; } poly.guide.axis = function(type, params) { if (type === 'x') { return new XAxis(params); } else if (type === 'y') { return new YAxis(params); } else if (type === 'r') { return new RAxis(params); } else if (type === 't') { return new TAxis(params); } }; poly.guide.axes = function(params) { return new Axes(params); }; }).call(this); // Generated by CoffeeScript 1.6.2 /* Legends (Guide) --------- Classes related to the generation and management of legends. Legend object takes in required input and produces abstract geometrical objects that can be rendered using the Geometry class. Legends are less disposable compared to axes and layers, because legends themselves may be added, removed, or modified. Each legend assumes that it will render at coordinate (0,0). It is up to the Legends (GuideSet) object to determine the correct position of a legend. */ (function() { var HorizontalLegend, Legend, Legends, VerticalLegend, sf, _ref, _ref1, __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }, __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; sf = poly["const"].scaleFns; poly.guide.legends = function() { return new Legends(); }; poly.guide.legend = function(aes, position) { if (position === 'left' || position === 'right') { return new VerticalLegend(aes); } else { return new HorizontalLegend(aes); } }; Legends = (function(_super) { __extends(Legends, _super); function Legends() { this.legends = []; this.deletedLegends = []; } Legends.prototype.make = function(params) { var aes, aesGroups, dims, domains, guideSpec, i, idx, layerMapping, layers, legend, legenddeleted, scales, _i, _j, _len, _len1, _ref, _ref1, _ref2, _results; domains = params.domains, layers = params.layers, guideSpec = params.guideSpec, scales = params.scales, layerMapping = params.layerMapping, this.position = params.position, dims = params.dims; if ((_ref = this.postion) == null) { this.postion = 'right'; } if (this.position === 'none') { return; } aesGroups = this._mergeAes(domains, layers); idx = 0; while (idx < this.legends.length) { legend = this.legends[idx]; legenddeleted = true; i = 0; while (i < aesGroups.length) { aes = aesGroups[i]; if (_.isEqual(aes, legend.aes)) { aesGroups.splice(i, 1); legenddeleted = false; break; } i++; } if (legenddeleted) { this.deletedLegends.push(legend); this.legends.splice(idx, 1); } else { idx++; } } for (_i = 0, _len = aesGroups.length; _i < _len; _i++) { aes = aesGroups[_i]; this.legends.push(poly.guide.legend(aes, this.position)); } _ref1 = this.legends; _results = []; for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { legend = _ref1[_j]; aes = legend.aes[0]; _results.push(legend.make({ domain: domains[aes], position: this.position, guideSpec: (_ref2 = guideSpec[aes]) != null ? _ref2 : {}, type: scales[aes].tickType(), mapping: layerMapping, keys: poly.getLabel(layers, aes), dims: dims })); } return _results; }; Legends.prototype._mergeAes = function(domains, layers) { var aes, m, mapped, merged, merging, _i, _len; merging = []; for (aes in domains) { if (__indexOf.call(poly["const"].noLegend, aes) >= 0) { continue; } mapped = _.map(layers, function(layer) { return layer.mapping[aes]; }); if (!_.all(mapped, _.isUndefined)) { merged = false; for (_i = 0, _len = merging.length; _i < _len; _i++) { m = merging[_i]; if (_.isEqual(m.mapped, mapped)) { m.aes.push(aes); merged = true; break; } } if (!merged) { merging.push({ aes: [aes], mapped: mapped }); } } } return _.pluck(merging, 'aes'); }; Legends.prototype.getDimension = function(dims) { var retobj, _ref, _ref1; retobj = {}; if ((_ref = this.position) === 'left' || _ref === 'right') { retobj[this.position] = this._leftrightWidth(dims); } else if ((_ref1 = this.position) === 'top' || _ref1 === 'bottom') { retobj[this.position] = this._topbottomHeight(dims); } return retobj; }; Legends.prototype._leftrightWidth = function(dims) { var d, legend, maxheight, maxwidth, offset, _i, _len, _ref; maxheight = dims.chartHeight; maxwidth = 0; offset = { x: 10, y: 0 }; _ref = this.legends; for (_i = 0, _len = _ref.length; _i < _len; _i++) { legend = _ref[_i]; d = legend.getDimension(dims); if (d.height + offset.y > maxheight) { offset.x += maxwidth + 5; offset.y = 0; maxwidth = 0; } if (d.width > maxwidth) { maxwidth = d.width; } offset.y += d.height; } return offset.x + maxwidth; }; Legends.prototype._topbottomHeight = function(dims) { var d, height, legend, maxwidth, _i, _len, _ref; maxwidth = dims.chartWidth; height = 10; _ref = this.legends; for (_i = 0, _len = _ref.length; _i < _len; _i++) { legend = _ref[_i]; d = legend.getDimension(dims); height += d.height + 10; } return height; }; Legends.prototype.render = function(dims, renderer, offset) { var legend, r, _i, _len, _ref; r = renderer(); _ref = this.deletedLegends; for (_i = 0, _len = _ref.length; _i < _len; _i++) { legend = _ref[_i]; legend.dispose(r); } this.deletedLegends = []; if (this.position === 'left' || this.position === 'right') { return this._renderV(dims, renderer, offset); } else if (this.position === 'top' || this.position === 'bottom') { return this._renderH(dims, renderer, offset); } }; Legends.prototype._renderV = function(dims, renderer, offset) { var legend, legendDim, maxheight, maxwidth, newdim, offsetX, offsetY, realoffset, _i, _len, _ref, _results; legendDim = { top: dims.paddingTop + dims.guideTop, left: this.position === 'left' ? dims.paddingLeft : dims.width - dims.guideRight - dims.paddingRight }; maxwidth = 0; maxheight = dims.height - dims.guideTop - dims.paddingTop; offsetY = 10; offsetX = this.position === 'right' ? offset.right : 0; _ref = this.legends; _results = []; for (_i = 0, _len = _ref.length; _i < _len; _i++) { legend = _ref[_i]; newdim = legend.getDimension(dims); if (newdim.height + offset.y > maxheight) { offsetX += maxwidth + 5; offsetY = 0; maxwidth = 0; } if (newdim.width > maxwidth) { maxwidth = newdim.width; } realoffset = { x: offsetX + legendDim.left, y: offsetY + legendDim.top }; legend.render(renderer(realoffset, false, false), maxwidth); _results.push(offsetY += newdim.height); } return _results; }; Legends.prototype._renderH = function(dims, renderer, offset) { var legend, legendDim, newdim, realoffset, _i, _len, _ref, _results; legendDim = { left: dims.paddingLeft, top: this.position === 'top' ? dims.paddingTop : dims.height - dims.guideBottom - dims.paddingBottom }; realoffset = { x: legendDim.left, y: this.position === 'top' ? offset.top + legendDim.top : offset.bottom + legendDim.top + 10 }; _ref = this.legends; _results = []; for (_i = 0, _len = _ref.length; _i < _len; _i++) { legend = _ref[_i]; newdim = legend.getDimension(dims); legend.render(renderer(realoffset, false, false)); _results.push(realoffset.y += newdim.height + 10); } return _results; }; Legends.prototype.dispose = function(renderer) { var legend, _i, _len, _ref, _results; _ref = this.legends; _results = []; for (_i = 0, _len = _ref.length; _i < _len; _i++) { legend = _ref[_i]; _results.push(legend.dispose(renderer)); } return _results; }; return Legends; })(poly.GuideSet); Legend = (function(_super) { __extends(Legend, _super); Legend.prototype.TITLEHEIGHT = 15; Legend.prototype.TICKHEIGHT = 12; Legend.prototype.SPACING = 10; function Legend(aes) { this.aes = aes; this._makeEvtData = __bind(this._makeEvtData, this); this._makeTick = __bind(this._makeTick, this); this.geometry = new poly.Geometry('guide'); } Legend.prototype.make = function(params) { var domain, guideSpec, keys, type, _, _ref, _ref1; domain = params.domain, type = params.type, guideSpec = params.guideSpec, this.mapping = params.mapping, this.position = params.position, keys = params.keys; this.titletext = (_ref = guideSpec.title) != null ? _ref : keys; return _ref1 = poly.tick.make(domain, guideSpec, type), this.ticks = _ref1.ticks, _ = _ref1._, _ref1; }; Legend.prototype.calculate = function() { var evtData, geoms, key, marks, tick, _ref; geoms = {}; geoms['title'] = { marks: { 0: this._makeTitle(this.titletext) }, evtData: { aes: this.aes[0], value: 'legendTitle' } }; _ref = this.ticks; for (key in _ref) { tick = _ref[key]; marks = {}; marks.tick = this._makeTick(tick); marks.text = this._makeLabel(tick); evtData = this._makeEvtData(tick); geoms[key] = { marks: marks, evtData: evtData }; } return geoms; }; Legend.prototype.render = function(renderer) { this.geometry.set(this.calculate()); return this.geometry.render(renderer); }; Legend.prototype.dispose = function(renderer) { return this.geometry.dispose(renderer); }; Legend.prototype._makeTitle = function(text, offset) { if (offset == null) { offset = { x: 0, y: 0 }; } return { type: 'text', x: sf.identity(offset.x + 5), y: sf.identity(offset.y), color: sf.identity('black'), text: text, 'text-anchor': 'start' }; }; Legend.prototype._makeLabel = function(tick, offset) { if (!offset) { offset = { x: 0, y: 15 + tick.index * 12 }; } return { type: 'text', x: sf.identity(offset.x + 20), y: sf.identity(offset.y + 1), color: sf.identity('black'), text: tick.value, 'text-anchor': 'start' }; }; Legend.prototype._makeTick = function(tick, offset) { var aes, obj, value, _ref; if (!offset) { offset = { x: 0, y: 15 + tick.index * 12 }; } obj = { type: 'circle', x: sf.identity(offset.x + 10), y: sf.identity(offset.y), color: sf.identity('steelblue') }; _ref = this.mapping; for (aes in _ref) { value = _ref[aes]; if (__indexOf.call(poly["const"].noLegend, aes) >= 0) { continue; } value = value[0]; if (__indexOf.call(this.aes, aes) >= 0) { obj[aes] = tick.location; } else if ((value.type != null) && value.type === 'const') { obj[aes] = sf.identity(value.value); } else if (!_.isObject(value)) { obj[aes] = sf.identity(value); } else { obj[aes] = sf.identity(poly["const"].defaults[aes]); } } if (_.isObject(obj.size)) { obj.size = sf.identity(5); } return obj; }; Legend.prototype._makeEvtData = function(tick) { var aes, evtData, v, value, _i, _len, _ref; evtData = {}; _ref = this.mapping; for (aes in _ref) { value = _ref[aes]; for (_i = 0, _len = value.length; _i < _len; _i++) { v = value[_i]; if (__indexOf.call(this.aes, aes) >= 0 && v.type === 'map') { evtData[v.value] = _.extend(tick.evtData, { value: tick.location, aes: aes }); } } } return evtData; }; return Legend; })(poly.Guide); VerticalLegend = (function(_super) { __extends(VerticalLegend, _super); function VerticalLegend() { _ref = VerticalLegend.__super__.constructor.apply(this, arguments); return _ref; } VerticalLegend.prototype.make = function(params) { var tickWidth, titleWidth; VerticalLegend.__super__.make.call(this, params); this.height = this.TITLEHEIGHT + this.SPACING + this.TICKHEIGHT * _.size(this.ticks); titleWidth = poly.strSize(this.titletext); tickWidth = _.max(_.map(this.ticks, function(t) { return poly.strSize(t.value); })); return this.maxwidth = Math.max(titleWidth, tickWidth); }; VerticalLegend.prototype.getDimension = function() { return { position: this.position, height: this.height, width: 15 + this.maxwidth }; }; return VerticalLegend; })(Legend); HorizontalLegend = (function(_super) { __extends(HorizontalLegend, _super); function HorizontalLegend() { _ref1 = HorizontalLegend.__super__.constructor.apply(this, arguments); return _ref1; } HorizontalLegend.prototype.TICKSPACING = 25; HorizontalLegend.prototype.make = function(params) { var currWidth, t, width, _i, _len, _ref2; HorizontalLegend.__super__.make.call(this, params); this.maxwidth = params.dims.width; this.height = this.TITLEHEIGHT + this.SPACING; width = 0; this.height += this.TICKHEIGHT; _ref2 = this.ticks; for (_i = 0, _len = _ref2.length; _i < _len; _i++) { t = _ref2[_i]; currWidth = poly.strSize(t.value) + this.TICKSPACING; if ((width + currWidth) < this.maxwidth) { width += currWidth; } else { this.height += this.TICKHEIGHT; width = currWidth; } } return null; }; HorizontalLegend.prototype.calculate = function() { var currWidth, evtData, geoms, key, marks, offset, tick, _ref2; geoms = {}; geoms['title'] = { marks: { 0: this._makeTitle(this.titletext) } }; offset = { x: 0, y: this.TITLEHEIGHT }; _ref2 = this.ticks; for (key in _ref2) { tick = _ref2[key]; marks = {}; marks.tick = this._makeTick(tick, offset); marks.text = this._makeLabel(tick, offset); evtData = this._makeEvtData(tick, offset); geoms[key] = { marks: marks, evtData: evtData }; currWidth = poly.strSize(tick.value) + this.TICKSPACING; if ((offset.x + currWidth) < this.maxwidth) { offset.x += currWidth; } else { offset.x = 0; offset.y += this.TICKHEIGHT; } } return geoms; }; HorizontalLegend.prototype.getDimension = function() { return { position: this.position, height: this.height, width: 'all' }; }; return HorizontalLegend; })(Legend); }).call(this); // Generated by CoffeeScript 1.6.2 /* Scales ------ Scales are objects that can construct functions that takes a value from the data, and returns another value that is suitable for rendering an attribute of that value. */ (function() { var Area, CustomScale, Gradient, Gradient2, Identity, Linear, Log, Opacity, Palette, PositionScale, Scale, Shape, _ref, _ref1, _ref2, _ref3, _ref4, _ref5, _ref6, __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; Scale = (function() { function Scale(params) { this.f = null; } Scale.prototype.make = function(domain) { this.domain = domain; this.compare = poly.domain.compare(domain); if (!domain) { return this._makeNone(); } switch (domain.type) { case 'num': return this._makeNum(); case 'date': return this._makeDate(); case 'cat': return this._makeCat(); } }; Scale.prototype._makeNone = function() { throw poly.error.impl("You are using a scale that does not support null values"); }; Scale.prototype._makeNum = function() { throw poly.error.impl("You are using a scale that does not support numbers"); }; Scale.prototype._makeDate = function() { throw poly.error.impl("You are using a scale that does not support dates"); }; Scale.prototype._makeCat = function() { throw poly.error.impl("You are using a scale that does not support categories"); }; Scale.prototype.tickType = function() { if (!this.domain) { return this._tickNone(); } switch (this.domain.type) { case 'num': return this._tickNum(); case 'date': return this._tickDate(); case 'cat': return this._tickCat(); } }; Scale.prototype._tickNone = function() { return 'none'; }; Scale.prototype._tickNum = function() { return 'num'; }; Scale.prototype._tickDate = function() { return 'date'; }; Scale.prototype._tickCat = function() { return 'cat'; }; Scale.prototype._identityWrapper = function(y) { return function(x) { if (_.isObject(x) && x.t === 'scalefn') { if (x.f === 'identity') { return x.v; } } return y(x); }; }; return Scale; })(); /* Position Scales for the x- and y-axes */ PositionScale = (function(_super) { __extends(PositionScale, _super); function PositionScale(params) { this._catWrapper = __bind(this._catWrapper, this); this._dateWrapper = __bind(this._dateWrapper, this); this._numWrapper = __bind(this._numWrapper, this); this.f = null; this.finv = null; } PositionScale.prototype.make = function(domain, range, space) { this.range = range; this.space = space; if (!_.isNumber(this.space)) { this.space = 0.05; } return PositionScale.__super__.make.call(this, domain); }; PositionScale.prototype._makeNone = function() { var space, _this = this; space = (this.range.max - this.range.min) * this.space; this.f = this._NaNCheckWrap(function(value) { var width; if (_.isObject(value)) { width = function(m) { return (this.range.max - this.range.min - 2 * space) / m; }; switch (value.f) { case 'identity': return value.v; case 'middle': return _this.range.max / 2 + _this.range.min / 2; case 'max': return _this.range.max; case 'min': return _this.range.min; case 'novalue': return _this.range.max / 2 + _this.range.min / 2; case 'upper': if (!value.m) { return _this.range.max - space; } else { return (_this.range.min + space) + (value.n + 1) * width(value.m); } break; case 'lower': if (!value.m) { return _this.range.min + space; } else { return (_this.range.min + space) + value.n * width(value.m); } } } else { return _this.range.max / 2 + _this.range.min / 2; } }); return this.finv = function() { return {}; }; }; PositionScale.prototype._NaNCheckWrap = function(fn) { return function(value) { var out; if (!poly.isDefined(value)) { return void 0; } else { out = fn(value); if (isNaN(out) || out === Infinity || out === -Infinity) { throw poly.error.scale("Scale outputed a value that is not finite."); } return out; } }; }; PositionScale.prototype._numWrapper = function(domain, y) { var _this = this; return this._NaNCheckWrap(function(value) { var lower, space, upper, width; if (_.isObject(value)) { if (value.t === 'scalefn') { switch (value.f) { case 'identity': return value.v; case 'middle': return y(value.v + domain.bw / 2); case 'max': return _this.range.max + value.v; case 'min': return _this.range.min + value.v; case 'upper': case 'lower': upper = y(value.v + domain.bw); lower = y(value.v); space = (upper - lower) * _this.space; if (value.f === 'upper' && !value.m) { return upper - space; } if (value.f === 'lower' && !value.m) { return lower + space; } width = (upper - lower - 2 * space) / value.m; if (value.f === 'upper') { return (lower + space) + (value.n + 1) * width; } if (value.f === 'lower') { return (lower + space) + value.n * width; } } } throw poly.error.input("Unknown object " + value + " is passed to a scale"); } return y(value); }); }; PositionScale.prototype._dateWrapper = function(domain, y) { var _this = this; return this._NaNCheckWrap(function(value) { var lower, space, upper, width, _timeConversion; if (_.isObject(value)) { if (value.t === 'scalefn') { switch (value.f) { case 'identity': return value.v; case 'max': return _this.range.max + value.v; case 'min': return _this.range.min + value.v; case 'upper': case 'middle': case 'lower': _timeConversion = function(n, timerange, lower) { var m; if (lower == null) { lower = 0; } m = moment.unix(value.v).startOf(timerange); m[timerange](n * Math.floor(m[timerange]() / n) + n * lower); return m.unix(); }; upper = y((function() { switch (domain.bw) { case 'week': return moment.unix(value.v).day(7).unix(); case 'twomonth': return _timeConversion(2, 'month'); case 'quarter': return _timeConversion(4, 'month'); case 'sixmonth': return _timeConversion(6, 'month'); case 'twoyear': return _timeConversion(2, 'year'); case 'fiveyear': return _timeConversion(5, 'year'); case 'decade': return _timeConversion(10, 'year'); default: return moment.unix(value.v).endOf(domain.bw).unix(); } })()); lower = y((function() { switch (domain.bw) { case 'week': return moment.unix(value.v).day(0).unix(); case 'twomonth': return _timeConversion(2, 'month', 1); case 'quarter': return _timeConversion(4, 'month', 1); case 'sixmonth': return _timeConversion(6, 'month', 1); case 'twoyear': return _timeConversion(2, 'year', 1); case 'fiveyear': return _timeConversion(5, 'year', 1); case 'decade': return _timeConversion(10, 'year', 1); default: return moment.unix(value.v).startOf(domain.bw).unix(); } })()); space = (upper - lower) * _this.space; if (value.f === 'middle') { return upper / 2 + lower / 2; } if (value.f === 'upper' && !value.m) { return upper - space; } if (value.f === 'lower' && !value.m) { return lower + space; } width = (upper - lower - 2 * space) / value.m; if (value.f === 'upper') { return (lower + space) + (value.n + 1) * width; } if (value.f === 'lower') { return (lower + space) + value.n * width; } } } throw poly.error.input("Unknown object " + value + " is passed to a scale"); } return y(value); }); }; PositionScale.prototype._catWrapper = function(step, y) { var _this = this; return this._NaNCheckWrap(function(value) { var lower, space, upper, width; space = step * _this.space; if (_.isObject(value)) { if (value.t === 'scalefn') { switch (value.f) { case 'identity': return value.v; case 'max': return _this.range.max + value.v; case 'min': return _this.range.min + value.v; case 'upper': case 'middle': case 'lower': upper = y(value.v) + step; lower = y(value.v); if (value.f === 'middle') { return upper / 2 + lower / 2; } if (value.f === 'upper' && !value.m) { return upper - space; } if (value.f === 'lower' && !value.m) { return lower + space; } width = (upper - lower - 2 * space) / value.m; if (value.f === 'upper') { return (lower + space) + (value.n + 1) * width; } if (value.f === 'lower') { return (lower + space) + value.n * width; } } } throw poly.error.input("Unknown object " + value + " is passed to a scale"); } return y(value) + step / 2; }); }; return PositionScale; })(Scale); Linear = (function(_super) { __extends(Linear, _super); function Linear() { _ref = Linear.__super__.constructor.apply(this, arguments); return _ref; } Linear.prototype._makeNum = function() { var x, y; y = poly.linear(this.domain.min, this.range.min, this.domain.max, this.range.max); x = poly.linear(this.range.min, this.domain.min, this.range.max, this.domain.max); this.f = this._numWrapper(this.domain, y); return this.finv = function(y1, y2) { var xs; xs = [x(y1), x(y2)]; return { ge: _.min(xs), le: _.max(xs) }; }; }; Linear.prototype._makeDate = function() { var x, y; y = poly.linear(this.domain.min, this.range.min, this.domain.max, this.range.max); x = poly.linear(this.range.min, this.domain.min, this.range.max, this.domain.max); this.f = this._dateWrapper(this.domain, y); return this.finv = function(y1, y2) { var xs; xs = [x(y1), x(y2)]; return { ge: _.min(xs), le: _.max(xs) }; }; }; Linear.prototype._makeCat = function() { var step, x, y, _this = this; step = (this.range.max - this.range.min) / this.domain.levels.length; y = function(x) { var i; i = _.indexOf(_this.domain.levels, x); if (i === -1) { return null; } else { return _this.range.min + i * step; } }; x = function(y1, y2) { var i1, i2, _ref1; if (y2 < y1) { _ref1 = [y2, y1], y1 = _ref1[0], y2 = _ref1[1]; } i1 = Math.floor(y1 / step); i2 = Math.floor(y2 / step); return { "in": _this.domain.levels.slice(i1, +i2 + 1 || 9e9) }; }; this.f = this._catWrapper(step, y); return this.finv = x; }; return Linear; })(PositionScale); Log = (function(_super) { __extends(Log, _super); function Log() { _ref1 = Log.__super__.constructor.apply(this, arguments); return _ref1; } Log.prototype._makeNum = function() { var lg, x, ylin, ylininv; if (this.domain.min < 0) { throw poly.error.input("Log scale cannot handle zero or negative input."); } lg = Math.log; ylin = poly.linear(lg(this.domain.min), this.range.min, lg(this.domain.max), this.range.max); this.f = this._numWrapper(this.domain, function(x) { return ylin(lg(x)); }); ylininv = poly.linear(this.range.min, lg(this.domain.min), this.range.max, lg(this.domain.max)); x = function(y) { return Math.exp(ylininv(y)); }; return this.finv = function(y1, y2) { var xs; xs = [x(y1), x(y2)]; return { ge: _.min(xs), le: _.max(xs) }; }; }; Log.prototype._tickNum = function() { return 'num-log'; }; return Log; })(PositionScale); /* Other, legend-type scales for the x- and y-axes */ Area = (function(_super) { __extends(Area, _super); function Area() { this._makeDate = __bind(this._makeDate, this); this._makeNum = __bind(this._makeNum, this); _ref2 = Area.__super__.constructor.apply(this, arguments); return _ref2; } Area.prototype._makeNum = function() { var min, sqrt, ylin; min = this.domain.min === 0 ? 0 : 1; sqrt = Math.sqrt; ylin = poly.linear(sqrt(this.domain.min), min, sqrt(this.domain.max), 10); return this.f = this._identityWrapper(function(x) { return ylin(sqrt(x)); }); }; Area.prototype._makeDate = function() { return this._makeNum(); }; return Area; })(Scale); Opacity = (function(_super) { __extends(Opacity, _super); function Opacity() { this._makeDate = __bind(this._makeDate, this); this._makeNum = __bind(this._makeNum, this); _ref3 = Opacity.__super__.constructor.apply(this, arguments); return _ref3; } Opacity.prototype._makeNum = function() { var max, min; min = this.domain.min === 0 ? 0 : 0.1; max = 1; return this.f = this._identityWrapper(poly.linear(this.domain.min, min, this.domain.max, max)); }; Opacity.prototype._makeDate = function() { return this._makeNum(); }; return Opacity; })(Scale); Palette = (function(_super) { __extends(Palette, _super); function Palette() { this._makeCat = __bind(this._makeCat, this); _ref4 = Palette.__super__.constructor.apply(this, arguments); return _ref4; } Palette.prototype._makeCat = function() { var colors, h, n, _this = this; n = this.domain.levels.length; if (n <= 9) { colors = ["#E41A1C", "#377EB8", "#4DAF4A", "#984EA3", "#FF7F00", "#FFFF33", "#A65628", "#F781BF", "#999999"]; return this.f = function(value) { var i; i = _.indexOf(_this.domain.levels, value); return colors[i]; }; } else { h = function(v) { return _.indexOf(_this.domain.levels, v) / n + 1 / (2 * n); }; return this.f = function(value) { if (typeof Raphael !== "undefined" && Raphael !== null) { return Raphael.hsl(h(value), 0.5, 0.5); } else { return "hsl(" + (h(value)) + ",0.5,0.5)"; } }; } }; return Palette; })(Scale); Gradient = (function(_super) { __extends(Gradient, _super); function Gradient(params) { this._makeDate = __bind(this._makeDate, this); this._makeNum = __bind(this._makeNum, this); this.lower = params.lower, this.upper = params.upper; } Gradient.prototype._makeNum = function() { var b, g, lower, r, upper, _this = this; lower = typeof Raphael !== "undefined" && Raphael !== null ? Raphael.color(this.lower) : this.lower; upper = typeof Raphael !== "undefined" && Raphael !== null ? Raphael.color(this.upper) : this.upper; r = poly.linear(this.domain.min, lower.r, this.domain.max, upper.r); g = poly.linear(this.domain.min, lower.g, this.domain.max, upper.g); b = poly.linear(this.domain.min, lower.b, this.domain.max, upper.b); return this.f = this._identityWrapper(function(value) { if (typeof Raphael !== "undefined" && Raphael !== null) { return Raphael.rgb(r(value), g(value), b(value)); } else { return "rgb(" + (r(value)) + "," + (g(value)) + "," + (b(value)) + ")"; } }); }; Gradient.prototype._makeDate = function() { return this._makeNum(); }; return Gradient; })(Scale); Gradient2 = (function(_super) { __extends(Gradient2, _super); function Gradient2(params) { this._makeDate = __bind(this._makeDate, this); this._makeCat = __bind(this._makeCat, this); this._makeNum = __bind(this._makeNum, this); var _ref5; this.lower = params.lower, this.middle = params.middle, this.upper = params.upper, this.midpoint = params.midpoint; if ((_ref5 = this.midpoint) == null) { this.midpoint = 0; } } Gradient2.prototype._makeNum = function() { var b1, b2, g1, g2, lower, middle, r1, r2, upper, _this = this; lower = typeof Raphael !== "undefined" && Raphael !== null ? Raphael.color(this.lower) : this.lower; middle = typeof Raphael !== "undefined" && Raphael !== null ? Raphael.color(this.middle) : this.middle; upper = typeof Raphael !== "undefined" && Raphael !== null ? Raphael.color(this.upper) : this.upper; r1 = poly.linear(this.domain.min, lower.r, this.midpoint, middle.r); g1 = poly.linear(this.domain.min, lower.g, this.midpoint, middle.g); b1 = poly.linear(this.domain.min, lower.b, this.midpoint, middle.b); r2 = poly.linear(this.midpoint, middle.r, this.domain.max, upper.r); g2 = poly.linear(this.midpoint, middle.g, this.domain.max, upper.g); b2 = poly.linear(this.midpoint, middle.b, this.domain.max, upper.b); return this.f = this._identityWrapper(function(value) { if (value < _this.midpoint) { if (typeof Raphael !== "undefined" && Raphael !== null) { return Raphael.rgb(r1(value), g1(value), b1(value)); } else { return "rgb(" + (r1(value)) + "," + (g1(value)) + "," + (b1(value)) + ")"; } } else { if (typeof Raphael !== "undefined" && Raphael !== null) { return Raphael.rgb(r2(value), g2(value), b2(value)); } else { return "rgb(" + (r2(value)) + "," + (g2(value)) + "," + (b2(value)) + ")"; } } }); }; Gradient2.prototype._makeCat = function() {}; Gradient2.prototype._makeDate = function() { return this._makeNum(); }; return Gradient2; })(Scale); CustomScale = (function(_super) { __extends(CustomScale, _super); function CustomScale(params) { this["function"] = params["function"]; } CustomScale.prototype.make = function(domain) { this.domain = domain; this.compare = poly.domain.compare(domain); return this.f = this._identityWrapper(this["function"]); }; return CustomScale; })(Scale); Shape = (function(_super) { __extends(Shape, _super); function Shape() { _ref5 = Shape.__super__.constructor.apply(this, arguments); return _ref5; } Shape.prototype._makeCat = function() {}; return Shape; })(Scale); Identity = (function(_super) { __extends(Identity, _super); function Identity() { _ref6 = Identity.__super__.constructor.apply(this, arguments); return _ref6; } Identity.prototype.make = function(domain) { this.domain = domain; this.compare = function(a, b) { return 0; }; return this.f = this._identityWrapper(function(x) { return x; }); }; return Identity; })(Scale); /* Public interface to making different scales */ poly.scale = {}; poly.scale.Base = Scale; poly.scale.classes = { linear: Linear, log: Log, area: Area, palette: Palette, gradient: Gradient, gradient2: Gradient2, identity: Identity, opacity: Opacity, custom: CustomScale }; poly.scale.make = function(spec) { if (spec.type in poly.scale.classes) { return new poly.scale.classes[spec.type](spec); } else { throw poly.error.defn("No such scale " + spec.type + "."); } }; }).call(this); // Generated by CoffeeScript 1.6.2 (function() { var ScaleSet, typeError, __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; poly.scaleset = function(guideSpec, domains, ranges) { return new ScaleSet(guideSpec, domains, ranges); }; ScaleSet = (function() { function ScaleSet(tmpRanges, coord) { this.coord = coord; this.ranges = tmpRanges; this.axes = poly.guide.axes(); this.legends = poly.guide.legends(); } ScaleSet.prototype.make = function(guideSpec, domains, layers) { this.guideSpec = guideSpec; this.layers = layers; this.domains = domains; this.scales = this._makeScales(guideSpec, domains, this.ranges); this.reverse = { x: this.scales.x.finv, y: this.scales.y.finv }; return this.layerMapping = this._mapLayers(layers); }; ScaleSet.prototype.setRanges = function(ranges) { var aes, _i, _len, _ref, _results; this.ranges = ranges; _ref = ['x', 'y']; _results = []; for (_i = 0, _len = _ref.length; _i < _len; _i++) { aes = _ref[_i]; _results.push(this.scales[aes].make(this.domains[aes], this.ranges[aes], this.getSpec(aes).padding)); } return _results; }; ScaleSet.prototype._makeScales = function(guideSpec, domains, ranges) { var defaultSpec, scales, specScale, _ref, _ref1, _ref2, _ref3, _ref4, _ref5; specScale = function(a) { var possibleScales, _ref; if (guideSpec && (guideSpec[a] != null) && (guideSpec[a].scale != null)) { if (_.isFunction(guideSpec[a].scale)) { return { type: 'custom', "function": guideSpec[a].scale }; } else { possibleScales = (function() { switch (a) { case 'x': return ['linear', 'log']; case 'y': return ['linear', 'log']; case 'color': return ['palette', 'gradient', 'gradient2']; case 'size': return ['linear', 'log']; case 'opacity': return ['opacity']; case 'shape': return ['linear', 'log', 'area']; case 'id': return ['identity']; case 'text': return ['identity']; default: return []; } })(); if (_ref = guideSpec[a].scale.type, __indexOf.call(possibleScales, _ref) >= 0) { return guideSpec[a].scale; } else { throw poly.error.scale("Aesthetic " + a + " cannot have scale " + guideSpec[a].scale.type); } } } else { return null; } }; scales = {}; scales.x = poly.scale.make((_ref = specScale('x')) != null ? _ref : { type: 'linear' }); scales.x.make(domains.x, ranges.x, this.getSpec('x').padding); scales.y = poly.scale.make((_ref1 = specScale('y')) != null ? _ref1 : { type: 'linear' }); scales.y.make(domains.y, ranges.y, this.getSpec('y').padding); if (domains.color != null) { if (domains.color.type === 'cat') { scales.color = poly.scale.make((_ref2 = specScale('color')) != null ? _ref2 : { type: 'palette' }); } else { defaultSpec = { type: 'gradient', upper: 'steelblue', lower: 'red' }; scales.color = poly.scale.make((_ref3 = specScale('color')) != null ? _ref3 : defaultSpec); } scales.color.make(domains.color); } if (domains.size != null) { scales.size = poly.scale.make((_ref4 = specScale('size')) != null ? _ref4 : { type: 'area' }); scales.size.make(domains.size); } if (domains.opacity != null) { scales.opacity = poly.scale.make((_ref5 = specScale('opacity')) != null ? _ref5 : { type: 'opacity' }); scales.opacity.make(domains.opacity); } scales.text = poly.scale.make({ type: 'identity' }); scales.text.make(); return scales; }; ScaleSet.prototype.fromPixels = function(start, end) { var map, obj, x, y, _i, _j, _len, _len1, _ref, _ref1, _ref2; if ((start != null) && (end != null)) { _ref = this.coord.getAes(start, end, this.reverse), x = _ref.x, y = _ref.y; } obj = {}; _ref1 = this.layerMapping.x; for (_i = 0, _len = _ref1.length; _i < _len; _i++) { map = _ref1[_i]; if ((map.type != null) && map.type === 'map') { obj[map.value] = x != null ? x : null; } } _ref2 = this.layerMapping.y; for (_j = 0, _len1 = _ref2.length; _j < _len1; _j++) { map = _ref2[_j]; if ((map.type != null) && map.type === 'map') { obj[map.value] = y != null ? y : null; } } return obj; }; ScaleSet.prototype.getSpec = function(a) { if ((this.guideSpec != null) && (this.guideSpec[a] != null)) { return this.guideSpec[a]; } else { return {}; } }; ScaleSet.prototype.makeGuides = function(spec, dims) { var _ref, _ref1; this.makeAxes(); this.makeTitles((_ref = spec.title) != null ? _ref : ''); this.makeLegends((_ref1 = spec.legendPosition) != null ? _ref1 : 'right', dims); return { axes: this.axes, legends: this.legends, title: this.title }; }; ScaleSet.prototype.renderGuides = function(dims, renderer, facet) { this.axes.render(dims, renderer, facet); this.renderTitles(dims, renderer); return this.renderLegends(dims, renderer); }; ScaleSet.prototype.disposeGuides = function(renderer) { this.axes.dispose(renderer); this.legends.dispose(renderer); this.titles.x.dispose(renderer); this.titles.y.dispose(renderer); this.titles.main.dispose(renderer); return this.titles = {}; }; ScaleSet.prototype.makeTitles = function(maintitle) { var _ref; if ((_ref = this.titles) == null) { this.titles = { x: poly.guide.title(this.coord.axisType('x')), y: poly.guide.title(this.coord.axisType('y')), main: poly.guide.title('main') }; } this.titles.main.make({ title: maintitle, guideSpec: {}, position: "top" }); this.titles.x.make({ guideSpec: this.getSpec('x'), title: poly.getLabel(this.layers, 'x') }); this.titles.y.make({ guideSpec: this.getSpec('y'), title: poly.getLabel(this.layers, 'y') }); }; ScaleSet.prototype.titleOffset = function(dim) { var dir, key, o, offset, title, _i, _len, _ref, _ref1, _ref2; offset = {}; _ref = this.titles; for (key in _ref) { title = _ref[key]; o = title.getDimension(); _ref1 = ['left', 'right', 'top', ' bottom']; for (_i = 0, _len = _ref1.length; _i < _len; _i++) { dir = _ref1[_i]; if (o[dir]) { if ((_ref2 = offset[dir]) == null) { offset[dir] = 0; } offset[dir] += o[dir]; } } } return offset; }; ScaleSet.prototype.renderTitles = function(dims, renderer) { var o; renderer = renderer({}, false, false); o = this.axesOffset(dims); this.titles.x.render(renderer, dims, o); this.titles.y.render(renderer, dims, o); this.titles.main.render(renderer, dims, o); }; ScaleSet.prototype.makeAxes = function() { var _ref; return this.axes.make({ domains: { x: this.domains.x, y: this.domains.y }, coord: this.coord, scales: this.scales, specs: (_ref = this.guideSpec) != null ? _ref : {}, labels: { x: poly.getLabel(this.layers, 'x'), y: poly.getLabel(this.layers, 'y') } }); }; ScaleSet.prototype.axesOffset = function(dims) { return this.axes.getDimension(dims); }; ScaleSet.prototype._mapLayers = function(layers) { var aes, obj, _i, _len, _ref; obj = {}; _ref = poly["const"].aes; for (_i = 0, _len = _ref.length; _i < _len; _i++) { aes = _ref[_i]; obj[aes] = _.map(layers, function(layer) { if (layer.mapping[aes] != null) { return { type: 'map', value: layer.mapping[aes] }; } else if (layer.consts[aes] != null) { return { type: 'const', value: layer.consts[aes] }; } else { return layer.defaults[aes]; } }); } return obj; }; ScaleSet.prototype.makeLegends = function(position, dims) { if (position == null) { position = 'right'; } return this.legends.make({ domains: this.domains, layers: this.layers, guideSpec: this.guideSpec, scales: this.scales, layerMapping: this.layerMapping, position: position, dims: dims }); }; ScaleSet.prototype.legendOffset = function(dims) { return this.legends.getDimension(dims); }; ScaleSet.prototype.renderLegends = function(dims, renderer) { var axesOffset, dir, offset, titleOffset, _i, _len, _ref, _ref1, _ref2; offset = { left: 0, right: 0, top: 0, bottom: 0 }; axesOffset = this.axesOffset(dims); titleOffset = this.titleOffset(dims); _ref = ['left', 'right', 'top', 'bottom']; for (_i = 0, _len = _ref.length; _i < _len; _i++) { dir = _ref[_i]; offset[dir] += (_ref1 = axesOffset[dir]) != null ? _ref1 : 0; offset[dir] += (_ref2 = titleOffset[dir]) != null ? _ref2 : 0; } this.legends.render(dims, renderer, offset); }; return ScaleSet; })(); typeError = function(msg) { return msg; }; }).call(this); // Generated by CoffeeScript 1.6.2 /* Data Object --------- Polychart wrapper around a data set. This is contains the data structure required for poly.chart(). Data object that either contains JSON format of a dataset, or knows how to retrieve data from some source. */ (function() { var AbstractData, ApiData, BackendData, FrontendData, _getArray, _getArrayOfArrays, _getCSV, _getDataType, _getObject, __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }, __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; poly.data = function(blob) { var data, len, meta, type; type = void 0; data = void 0; meta = void 0; if (_.isObject(blob) && 'data' in blob && (((len = _.keys(blob).length) < 7 && 'meta' in blob) || len < 5)) { data = blob.data; meta = blob.meta; } else { data = blob; } switch (_getDataType(data)) { case 'json-object': case 'json-grid': case 'json-array': return poly.data.json(data, meta, type); case 'url': return poly.data.url(data, meta, type); case 'csv': return poly.data.csv(data, meta); case 'api': return poly.data.api(data); default: throw poly.error.data("Unknown data format."); } }; poly.data.json = function(data, meta, type) { return new FrontendData({ data: data, meta: meta, type: type }); }; poly.data.csv = function(data, meta) { return new FrontendData({ data: data, meta: meta, 'csv': 'csv' }); }; poly.data.url = function(url, computeBackend, limit) { return new BackendData({ url: url, computeBackend: computeBackend, limit: limit }); }; /* Data format which takes an API-facing function. Signature: poly.data.api = ((requestParams, (err, result) -> undefined) -> undefined) -> polyjsData */ poly.data.api = function(apiFun) { return new ApiData({ apiFun: apiFun }); }; /* Helper functions */ _getDataType = function(data) { if (_.isArray(data)) { if (_.isArray(data[0])) { return 'json-grid'; } else { return 'json-array'; } } else if (_.isObject(data)) { return 'json-object'; } else if (_.isString(data)) { if (poly.isURI(data)) { return 'url'; } else { return 'csv'; } } else if (_.isFunction(data)) { return 'api'; } else { throw poly.error.data("Unknown data format."); } }; _getArray = function(json, meta) { var first100, item, key, keys, raw, _i, _j, _k, _len, _len1, _len2, _ref; if (json.length > 0) { keys = _.union(_.keys(meta), _.keys(json[0])); first100 = json.slice(0, 100); for (_i = 0, _len = keys.length; _i < _len; _i++) { key = keys[_i]; if ((_ref = meta[key]) == null) { meta[key] = {}; } if (!meta[key].type) { meta[key].type = poly.type.impute(_.pluck(first100, key)); } } for (_j = 0, _len1 = json.length; _j < _len1; _j++) { item = json[_j]; for (_k = 0, _len2 = keys.length; _k < _len2; _k++) { key = keys[_k]; item[key] = poly.type.coerce(item[key], meta[key]); } } key = keys; raw = json; } else { key = _.keys(meta); raw = []; } return { key: key, raw: raw, meta: meta }; }; _getArrayOfArrays = function(json, meta) { var first100, i, item, key, keys, newitem, raw, retobj, value, _i, _j, _k, _len, _len1, _len2, _ref; retobj = []; if (json.length > 0) { keys = meta && _.isArray(meta) ? meta : meta && _.isObject(meta) ? _.keys(meta) : _.keys(json[0]); if (_.isArray(meta) || !_.isObject(meta)) { meta = {}; } first100 = json.slice(0, 100); for (i = _i = 0, _len = keys.length; _i < _len; i = ++_i) { key = keys[i]; if ((_ref = meta[key]) == null) { meta[key] = {}; } if (!meta[key].type) { meta[key].type = poly.type.impute(_.pluck(first100, i)); } } for (_j = 0, _len1 = json.length; _j < _len1; _j++) { item = json[_j]; newitem = {}; for (i = _k = 0, _len2 = item.length; _k < _len2; i = ++_k) { value = item[i]; key = keys[i]; newitem[key] = poly.type.coerce(value, meta[key]); } retobj.push(newitem); } key = keys; raw = retobj; } else { key = _.keys(meta); raw = []; } return { key: key, raw: raw, meta: meta }; }; _getObject = function(json, meta) { var i, k, key, keys, len, obj, raw, _i, _j, _k, _len, _len1, _ref, _ref1; keys = _.keys(json); raw = []; for (_i = 0, _len = keys.length; _i < _len; _i++) { key = keys[_i]; if ((_ref = meta[key]) == null) { meta[key] = {}; } if (!meta[key].type) { meta[key].type = poly.type.impute(json[key].slice(0, 100)); } } if (keys.length > 0) { len = json[keys[0]].length; if (len > 0) { for (i = _j = 0, _ref1 = len - 1; 0 <= _ref1 ? _j <= _ref1 : _j >= _ref1; i = 0 <= _ref1 ? ++_j : --_j) { obj = {}; for (_k = 0, _len1 = keys.length; _k < _len1; _k++) { k = keys[_k]; obj[k] = poly.type.coerce(json[k][i], meta[k]); } raw.push(obj); } } } key = keys; return { key: key, raw: raw, meta: meta }; }; _getCSV = function(str, meta) { return _getArray(poly.csv.parse(str), meta); }; /* Classes */ AbstractData = (function() { AbstractData.prototype.isData = true; function AbstractData() { this.raw = null; this.meta = {}; this.key = []; this.subscribed = []; this.computeBackend = false; } AbstractData.prototype.update = function() { var fn, _i, _len, _ref, _results; _ref = this.subscribed; _results = []; for (_i = 0, _len = _ref.length; _i < _len; _i++) { fn = _ref[_i]; _results.push(fn()); } return _results; }; AbstractData.prototype.subscribe = function(h) { if (_.indexOf(this.subscribed, h) === -1) { return this.subscribed.push(h); } }; AbstractData.prototype.unsubscribe = function(h) { return this.subscribed.splice(_.indexOf(this.subscribed, h), 1); }; AbstractData.prototype.keys = function() { return this.key; }; AbstractData.prototype.rename = function() { return false; }; AbstractData.prototype.renameMany = function() { return false; }; AbstractData.prototype.remove = function() { return false; }; AbstractData.prototype.filter = function() { return false; }; AbstractData.prototype.sort = function() { return false; }; AbstractData.prototype.derive = function() { return false; }; AbstractData.prototype.get = function(key) { if (this.raw) { return _.pluck(this.raw, key); } else { throw poly.error.data("Data has not been fetched or is undefined."); } }; AbstractData.prototype.len = function() { if (this.raw) { return this.raw.length; } else { throw poly.error.data("Data has not been fetched or is undefined."); } }; AbstractData.prototype.getObject = function(i) { if (this.raw) { return this.raw[i]; } else { throw poly.error.data("Data has not been fetched or is undefined."); } }; AbstractData.prototype.max = function(key) { return _.max(this.get(key)); }; AbstractData.prototype.min = function(key) { return _.min(this.get(key)); }; AbstractData.prototype.getMeta = function(key) { if (this.meta) { return this.meta[key]; } else { return void 0; } }; AbstractData.prototype.type = function(key) { var t; if (key in this.meta) { t = this.meta[key].type; if (t === 'num') { return 'number'; } else { return t; } } throw poly.error.defn("Data does not have column " + key + "."); }; return AbstractData; })(); FrontendData = (function(_super) { __extends(FrontendData, _super); function FrontendData(params) { FrontendData.__super__.constructor.call(this); this._setData(params); } FrontendData.prototype.getData = function(callback, dataSpec) { if (dataSpec == null) { callback(null, this); return; } return poly.data.frontendProcess(dataSpec, this, function(err, dataObj) { dataObj.raw = dataObj.data; return callback(err, dataObj); }); }; FrontendData.prototype.update = function(params) { this._setData(params); return FrontendData.__super__.update.call(this); }; FrontendData.prototype._setData = function(blob) { var data, meta, _ref, _ref1; if (_.isObject(blob) && _.keys(blob).length < 4 && 'data' in blob) { data = blob.data; meta = (_ref = blob.meta) != null ? _ref : {}; } else { data = blob; meta = {}; } _ref1 = (function() { var _ref1; switch ((_ref1 = blob.type) != null ? _ref1 : _getDataType(data)) { case 'json-object': return _getObject(data, meta); case 'json-grid': return _getArrayOfArrays(data, meta); case 'json-array': return _getArray(data, meta); case 'csv': return _getCSV(data, meta); default: throw poly.error.data("Unknown data format."); } })(), this.key = _ref1.key, this.raw = _ref1.raw, this.meta = _ref1.meta; return this.data = this.raw; }; FrontendData.prototype._checkRename = function(from, to) { if (to === '') { throw poly.error.defn("Column names cannot be an empty string"); } if (_.indexOf(this.key, from) === -1) { throw poly.error.defn("The key " + from + " doesn't exist!"); } if (_.indexOf(this.key, to) !== -1) { throw poly.error.defn("The key " + to + " already exists!"); } }; FrontendData.prototype.rename = function(from, to, checked) { var item, k, _i, _len, _ref; if (checked == null) { checked = false; } from = from.toString(); to = to.toString(); if (from === to) { return true; } if (!checked) { this._checkRename(from, to); } _ref = this.raw; for (_i = 0, _len = _ref.length; _i < _len; _i++) { item = _ref[_i]; item[to] = item[from]; delete item[from]; } k = _.indexOf(this.key, from); this.key[k] = to; this.meta[to] = this.meta[from]; delete this.meta[from]; return true; }; FrontendData.prototype.renameMany = function(map) { var from, to; for (from in map) { to = map[from]; if (from !== to) { this._checkRename(from, to); } } for (from in map) { to = map[from]; if (from !== to) { this.rename(from, to, true); } } return true; }; FrontendData.prototype.remove = function(key) { var index, item, _i, _len, _ref; index = _.indexOf(this.key, key); if (index === '-1') { return false; } this.key.splice(index, 1); delete this.meta[key]; _ref = this.raw; for (_i = 0, _len = _ref.length; _i < _len; _i++) { item = _ref[_i]; delete item[key]; } return true; }; FrontendData.prototype.filter = function(strfn) { var fn, item, newdata, newobj, _i, _len, _ref; fn = _.isFunction(strfn) ? strfn : _.isString(strfn) ? new Function('d', "with(d) { return " + strfn + ";}") : function() { return true; }; newdata = []; _ref = this.raw; for (_i = 0, _len = _ref.length; _i < _len; _i++) { item = _ref[_i]; if (fn(item)) { newdata.push(item); } } newobj = poly.data.json(newdata, this.meta); return newobj; }; FrontendData.prototype.sort = function(key, desc) { var newdata, newobj, sortfn, type; type = this.type(key); newdata = _.clone(this.raw); sortfn = poly.type.compare(type); newdata.sort(function(a, b) { return sortfn(a[key], b[key]); }); if (desc) { newdata.reverse(); } newobj = poly.data.json(newdata, this.meta); return newobj; }; FrontendData.prototype.derive = function(fnstr, key, opts) { var compute, context, dryrun, hasFnStr, item, value, _i, _len, _ref; if (opts == null) { opts = {}; } dryrun = opts.dryrun, context = opts.context; if (key == null) { key = _uniqueId("var_"); } if (context == null) { context = {}; } if (_.isFunction(fnstr)) { compute = fnstr; hasFnStr = false; } else { hasFnStr = true; compute = new Function('d', "with(this) { with(d) { return " + (fnstr || '""') + ";}}"); } _ref = this.raw; for (_i = 0, _len = _ref.length; _i < _len; _i++) { item = _ref[_i]; value = compute.call(context, item); if (_.isFunction(value)) { throw poly.error.defn("Derivation function returned another function."); } item[key] = value; } if (dryrun) { return { success: true, values: _.pluck(this.raw.slice(0, 11), key) }; } if (!(__indexOf.call(this.key, key) >= 0)) { this.key.push(key); } this.meta[key] = { type: poly.type.impute(_.pluck(this.raw.slice(0, 101), key)), derived: true }; if (hasFnStr) { this.meta[key].formula = fnstr; } return key; }; return FrontendData; })(AbstractData); BackendData = (function(_super) { __extends(BackendData, _super); function BackendData(params) { this.getData = __bind(this.getData, this); var _ref; BackendData.__super__.constructor.call(this); this.url = params.url, this.computeBackend = params.computeBackend, this.limit = params.limit; if ((_ref = this.computeBackend) == null) { this.computeBackend = false; } } BackendData.prototype.getData = function(callback, dataSpec) { var chr, url, _this = this; if ((this.raw != null) && (!this.computeBackend)) { if (dataSpec == null) { return callback(null, this); } poly.data.frontendProcess(dataSpec, this, function(err, dataObj) { dataObj.raw = dataObj.data; return callback(err, dataObj); }); return; } chr = _.indexOf(this.url, "?") === -1 ? '?' : '&'; url = this.url; if (this.limit) { url += "" + chr + "limit=" + this.limit; } if (dataSpec) { url += "&spec=" + (encodeURIComponent(JSON.stringify(dataSpec))); } return poly.text(url, function(blob) { var data, e, meta, _ref, _ref1; try { blob = JSON.parse(blob); } catch (_error) { e = _error; } if (_.isObject(blob) && _.keys(blob).length < 4 && 'data' in blob) { data = blob.data; meta = (_ref = blob.meta) != null ? _ref : {}; } else { data = blob; meta = {}; } _ref1 = (function() { switch (_getDataType(data)) { case 'json-object': return _getObject(data, meta); case 'json-grid': return _getArrayOfArrays(data, meta); case 'json-array': return _getArray(data, meta); case 'csv': return _getCSV(data, meta); default: throw poly.error.data("Unknown data format."); } })(), _this.key = _ref1.key, _this.raw = _ref1.raw, _this.meta = _ref1.meta; _this.data = _this.raw; return callback(null, _this); }); }; BackendData.prototype.update = function(params) { this.raw = null; return BackendData.__super__.update.call(this); }; BackendData.prototype.renameMany = function(obj) { return _.keys(obj).length === 0; }; return BackendData; })(AbstractData); ApiData = (function(_super) { __extends(ApiData, _super); function ApiData(params) { this.getData = __bind(this.getData, this); ApiData.__super__.constructor.call(this); this.apiFun = params.apiFun; this.computeBackend = true; } ApiData.prototype.getData = function(callback, dataSpec) { var _this = this; return this.apiFun(dataSpec, function(err, blob) { var data, e, error, meta, _ref, _ref1; if (err != null) { return callback(err, null); } if (_.isString(blob)) { try { blob = JSON.parse(blob); } catch (_error) { e = _error; } } error = null; try { data = blob.data; meta = (_ref = blob.meta) != null ? _ref : {}; _ref1 = (function() { switch (_getDataType(data)) { case 'json-object': return _getObject(data, meta); case 'json-grid': return _getArrayOfArrays(data, meta); case 'json-array': return _getArray(data, meta); case 'csv': return _getCSV(data, meta); default: throw poly.error.data("Unknown data format."); } })(), _this.key = _ref1.key, _this.raw = _ref1.raw, _this.meta = _ref1.meta; _this.data = _this.raw; } catch (_error) { e = _error; error = e; } return callback(error, _this); }); }; ApiData.prototype.update = function(params) { this.raw = null; return ApiData.__super__.update.call(this); }; ApiData.prototype.renameMany = function(obj) { return _.keys(obj).length === 0; }; return ApiData; })(AbstractData); }).call(this); // Generated by CoffeeScript 1.6.2 /* Wrapper around the data processing piece that keeps track of the kind of data processing to be done. */ (function() { var DataProcess, backendProcess, calculateMeta, calculateStats, createFunction, evaluate, filterFactory, filters, frontendProcess, interpretMeta, statistics, __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; DataProcess = (function() { function DataProcess(layerSpec, grouping, strictmode, parseMethod) { this.parseMethod = parseMethod != null ? parseMethod : poly.spec.layerToData; this._wrap = __bind(this._wrap, this); this.make = __bind(this.make, this); this.layerMeta = _.extend({}, layerSpec.meta, { _additionalInfo: layerSpec.additionalInfo }); this.dataObj = layerSpec.data; this.prevSpec = null; this.strictmode = strictmode; this.statData = null; this.metaData = {}; } DataProcess.prototype.make = function(spec, grouping, callback) { var dataSpec, wrappedCallback; wrappedCallback = this._wrap(callback); if (this.strictmode) { wrappedCallback({ data: this.dataObj.raw, meta: this.dataObj.meta }); } if (this.dataObj.computeBackend) { dataSpec = this.parseMethod(spec, grouping); if (this.layerMeta) { dataSpec.meta = this.layerMeta; } return backendProcess(dataSpec, this.dataObj, wrappedCallback); } else { dataSpec = this.parseMethod(spec, grouping); return this.dataObj.getData(function(err, data) { var obj, _i, _len, _ref; if (err != null) { return wrappedCallback(err, null); } if (__indexOf.call(dataSpec.select, 'count(*)') >= 0) { _ref = data.data; for (_i = 0, _len = _ref.length; _i < _len; _i++) { obj = _ref[_i]; obj['count(*)'] = 1; } data.meta['count(*)'] = {}; data.meta['count(*)']['type'] = 'num'; dataSpec.stats.stats.push({ key: 'count(*)', name: 'count(*)', stat: 'count' }); } return frontendProcess(dataSpec, data, wrappedCallback); }); } }; DataProcess.prototype._wrap = function(callback) { var _this = this; return function(err, params) { var data, meta; if (err != null) { return callback(err, null, null); } data = params.data, meta = params.meta; _this.statData = data; _this.metaData = meta; return callback(null, _this.statData, _this.metaData); }; }; return DataProcess; })(); poly.DataProcess = DataProcess; /* Temporary */ poly.data.process = function(dataObj, layerSpec, strictmode, callback) { var d; d = new DataProcess(layerSpec, strictmode); d.process(callback); return d; }; /* TRANSFORMS ---------- Functions to interpret the arithmetic and other expressions -- */ evaluate = { ident: function(name) { return function(row) { if (name in row) { return row[name]; } throw poly.error.defn("Referencing unknown column: " + name); }; }, "const": function(value) { return function() { return value; }; }, conditional: function(cond, conseq, altern) { return function(row) { if (cond(row)) { return conseq(row); } else { return altern(row); } }; }, infixop: { "+": function(lhs, rhs) { return function(row) { return lhs(row) + rhs(row); }; }, "-": function(lhs, rhs) { return function(row) { return lhs(row) - rhs(row); }; }, "*": function(lhs, rhs) { return function(row) { return lhs(row) * rhs(row); }; }, "/": function(lhs, rhs) { return function(row) { return lhs(row) / rhs(row); }; }, "%": function(lhs, rhs) { return function(row) { return lhs(row) % rhs(row); }; }, ">": function(lhs, rhs) { return function(row) { return lhs(row) > rhs(row); }; }, ">=": function(lhs, rhs) { return function(row) { return lhs(row) >= rhs(row); }; }, "<": function(lhs, rhs) { return function(row) { return lhs(row) < rhs(row); }; }, "<=": function(lhs, rhs) { return function(row) { return lhs(row) <= rhs(row); }; }, "!=": function(lhs, rhs) { return function(row) { return lhs(row) !== rhs(row); }; }, "==": function(lhs, rhs) { return function(row) { return lhs(row) === rhs(row); }; }, "=": function(lhs, rhs) { return function(row) { return lhs(row) === rhs(row); }; }, "++": function(lhs, rhs) { return function(row) { return lhs(row) + rhs(row); }; } }, trans: { "substr": function(args) { return function(row) { var end, start, str; str = args[0](row).toString(); start = args[1](row); end = args[2](row); return str.substr(start, end); }; }, "length": function(args) { return function(row) { var str; str = args[0](row).toString(); return _.size(str); }; }, "upper": function(args) { return function(row) { var str; str = args[0](row).toString(); return str.toUpperCase(); }; }, "lower": function(args) { return function(row) { var str; str = args[0](row).toString(); return str.toLowerCase(); }; }, "indexOf": function(args) { return function(row) { var haystack, needle; haystack = args[0](row).toString(); needle = args[1](row).toString(); return haystack.indexOf(needle); }; }, "parseNum": function(args) { return function(row) { var str; str = args[0](row).toString(); return +str; }; }, "parseDateDefault": function(args) { return function(row) { var str; str = args[0](row); return moment(str).unix(); }; }, "parseDate": function(args) { return function(row) { var format, str; str = args[0](row); format = args[1](row); return moment(str, format).unix(); }; }, "year": function(args) { return function(row) { var ts; ts = args[0](row); return moment.unix(ts).year(); }; }, "month": function(args) { return function(row) { var ts; ts = args[0](row); return moment.unix(ts).month() + 1; }; }, "dayOfMonth": function(args) { return function(row) { var ts; ts = args[0](row); return moment.unix(ts).date(); }; }, "dayOfYear": function(args) { return function(row) { var ts; ts = args[0](row); return moment.unix(ts).dayOfYear(); }; }, "dayOfWeek": function(args) { return function(row) { var ts; ts = args[0](row); return moment.unix(ts).day(); }; }, "hour": function(args) { return function(row) { var ts; ts = args[0](row); return moment.unix(ts).hour(); }; }, "minute": function(args) { return function(row) { var ts; ts = args[0](row); return moment.unix(ts).minute(); }; }, "second": function(args) { return function(row) { var ts; ts = args[0](row); return moment.unix(ts).second(); }; }, "log": function(args) { return function(row) { return Math.log(args[0](row)); }; }, "lag": function(args) { var lastn; lastn = []; return function(row) { var currentLag, i, lag, val; val = args[0](row); lag = args[1](row); currentLag = _.size(lastn); if (currentLag === 0) { lastn = (function() { var _i, _results; _results = []; for (i = _i = 1; 1 <= lag ? _i <= lag : _i >= lag; i = 1 <= lag ? ++_i : --_i) { _results.push(void 0); } return _results; })(); } else if (currentLag !== lag) { throw poly.error.defn("Lag period needs to be constant, but isn't!"); } lastn.push(val); return lastn.shift(); }; }, "bin": function(args) { return function(row) { var bw, val, _timeBinning, _this = this; val = args[0](row); bw = args[1](row); if (_.isNumber(bw)) { return Math.floor(val / bw) * bw; } _timeBinning = function(n, timerange) { var m; m = moment.unix(val).startOf(timerange); m[timerange](n * Math.floor(m[timerange]() / n)); return m.unix(); }; switch (bw) { case 'week': return moment.unix(val).day(0).unix(); case 'twomonth': return _timeBinning(2, 'month'); case 'quarter': return _timeBinning(4, 'month'); case 'sixmonth': return _timeBinning(6, 'month'); case 'twoyear': return _timeBinning(2, 'year'); case 'fiveyear': return _timeBinning(5, 'year'); case 'decade': return _timeBinning(10, 'year'); default: return moment.unix(val).startOf(bw).unix(); } }; } } }; createFunction = function(node) { var altern, arg, args, cond, conseq, fn, lhs, nodeType, payload, rhs, value; nodeType = node[0], payload = node[1]; fn = nodeType === 'ident' ? evaluate.ident(payload.name) : nodeType === 'const' ? (value = poly.type.coerce(payload.value, { type: payload.type }), evaluate["const"](value)) : nodeType === 'infixop' ? (lhs = createFunction(payload.lhs), rhs = createFunction(payload.rhs), evaluate.infixop[payload.opname](lhs, rhs)) : nodeType === 'conditional' ? (cond = createFunction(payload.cond), conseq = createFunction(payload.conseq), altern = createFunction(payload.altern), evaluate.conditional(cond, conseq, altern)) : nodeType === 'call' ? (args = (function() { var _i, _len, _ref, _results; _ref = payload.args; _results = []; for (_i = 0, _len = _ref.length; _i < _len; _i++) { arg = _ref[_i]; _results.push(createFunction(arg)); } return _results; })(), evaluate.trans[payload.fname](args)) : void 0; if (fn) { return fn; } throw poly.error.defn("Unknown operation of type: " + nodeType); }; /* FILTERS ---------- Key:value pair of available filtering operations to filtering function. The filtering function returns true iff the data item satisfies the filtering criteria. */ filters = { 'lt': function(x, value) { return x < value; }, 'le': function(x, value) { return x <= value; }, 'gt': function(x, value) { return x > value; }, 'ge': function(x, value) { return x >= value; }, 'in': function(x, value) { return __indexOf.call(value, x) >= 0; } }; /* Helper function to figures out which filter to create, then creates it */ filterFactory = function(filterSpec) { var filterFuncs; filterFuncs = []; _.each(filterSpec, function(filter) { var key, spec; key = poly.parser.unbracket(filter.expr.name); spec = _.pick(filter, 'lt', 'gt', 'le', 'ge', 'in'); return _.each(spec, function(value, predicate) { if (!(predicate in filters)) { return; } filter = function(item) { return filters[predicate](item[key], value); }; return filterFuncs.push(filter); }); }); return function(item) { var f, _i, _len; for (_i = 0, _len = filterFuncs.length; _i < _len; _i++) { f = filterFuncs[_i]; if (!f(item)) { return false; } } return true; }; }; /* STATISTICS ---------- Key:value pair of available statistics operations to a function that creates the appropriate statistical function given the spec. Each statistics function produces one atomic value for each group of data. */ statistics = { sum: function(values) { return _.reduce(_.without(values, void 0, null), (function(v, m) { return v + m; }), 0); }, mean: function(values) { values = _.without(values, void 0, null); return _.reduce(values, (function(v, m) { return v + m; }), 0) / values.length; }, count: function(values) { return _.without(values, void 0, null).length; }, unique: function(values) { return (_.uniq(_.without(values, void 0, null))).length; }, min: function(values) { return _.min(values); }, max: function(values) { return _.max(values); }, median: function(values) { return poly.median(values); }, box: function(values) { var iqr, len, lowerBound, mid, q2, q4, quarter, sortedValues, splitValues, upperBound, _ref; len = values.length; if (len > 5) { mid = len / 2; sortedValues = _.sortBy(values, function(x) { return x; }); quarter = Math.ceil(mid) / 2; if (quarter % 1 !== 0) { quarter = Math.floor(quarter); q2 = sortedValues[quarter]; q4 = sortedValues[(len - 1) - quarter]; } else { q2 = (sortedValues[quarter] + sortedValues[quarter - 1]) / 2; q4 = (sortedValues[len - quarter] + sortedValues[(len - quarter) - 1]) / 2; } iqr = q4 - q2; lowerBound = q2 - (1.5 * iqr); upperBound = q4 + (1.5 * iqr); splitValues = _.groupBy(sortedValues, function(v) { return v >= lowerBound && v <= upperBound; }); return { q1: _.min(splitValues["true"]), q2: q2, q3: poly.median(sortedValues, true), q4: q4, q5: _.max(splitValues["true"]), outliers: (_ref = splitValues["false"]) != null ? _ref : [] }; } return { outliers: values }; } }; /* Calculate statistics */ calculateStats = function(data, statSpecs) { var e, groupedData, statFuncs; statFuncs = {}; _.each(statSpecs.stats, function(statSpec) { var args, expr, fn, key, name; name = statSpec.name, expr = statSpec.expr, args = statSpec.args; fn = statistics[name]; key = poly.parser.unbracket(args[0].name); return statFuncs[expr.name] = function(data) { return fn(_.pluck(data, key)); }; }); groupedData = poly.groupBy(data, (function() { var _i, _len, _ref, _results; _ref = statSpecs.groups; _results = []; for (_i = 0, _len = _ref.length; _i < _len; _i++) { e = _ref[_i]; _results.push(poly.parser.unbracket(e.name)); } return _results; })()); return _.map(groupedData, function(data) { var name, rep, stats, _i, _len, _ref; rep = {}; _ref = statSpecs.groups; for (_i = 0, _len = _ref.length; _i < _len; _i++) { name = _ref[_i].name; name = poly.parser.unbracket(name); rep[name] = data[0][name]; } for (name in statFuncs) { stats = statFuncs[name]; rep[name] = stats(data); } return rep; }); }; /* META SORTING ------------ Calculations of meta properties including sorting and limiting based on the values of statistical calculations */ calculateMeta = function(metaSpec, data) { var args, asc, comparator, key, limit, multiplier, sort, sortKey, stat, statSpec, values; key = metaSpec.key, sort = metaSpec.sort, stat = metaSpec.stat, args = metaSpec.args, limit = metaSpec.limit, asc = metaSpec.asc; if (stat) { statSpec = { stats: [ { name: stat, expr: sort, args: args } ], groups: [key] }; data = calculateStats(data, statSpec); } multiplier = asc ? 1 : -1; sortKey = poly.parser.unbracket(sort.name); comparator = function(a, b) { if (a[sortKey] === b[sortKey]) { return 0; } if (a[sortKey] >= b[sortKey]) { return 1 * multiplier; } return -1 * multiplier; }; data.sort(comparator); if (limit) { data = data.slice(0, +(limit - 1) + 1 || 9e9); } values = _.uniq(_.pluck(data, poly.parser.unbracket(key.name))); return { meta: { levels: values, sorted: true }, filter: { expr: key, "in": values } }; }; /* GENERAL PROCESSING ------------------ Figure out what the metadata of a column should be based on what we know about other columns, and by the expression */ interpretMeta = function(metas) { var typeEnv; typeEnv = poly.parser.createColTypeEnv(metas); return function(expr) { var bw, innerPayload, innerType, payload, rootType, _ref, _ref1; _ref = expr.expr, rootType = _ref[0], payload = _ref[1]; bw = null; if (rootType === 'call' && payload.fname === 'bin') { _ref1 = payload.args[1], innerType = _ref1[0], innerPayload = _ref1[1]; if (innerType === 'const') { bw = poly.type.coerce(innerPayload.value, { type: innerPayload.type }); } } return { type: poly.parser.getType(expr.name, typeEnv), bw: bw }; }; }; /* GENERAL PROCESSING ------------------ Coordinating the actual work being done */ /* Perform the necessary computation in the front end */ frontendProcess = function(dataSpec, data, callback) { var addData, addMeta, additionalFilter, expr, filter, getMeta, key, meta, metaData, metaSpec, name, statSpec, _i, _j, _k, _l, _len, _len1, _len2, _len3, _ref, _ref1, _ref2, _ref3, _ref4, _ref5, _ref6; metaData = (_ref = _.clone(data.meta)) != null ? _ref : {}; getMeta = interpretMeta(metaData); addMeta = function(expr, meta) { var key, _ref1; if (meta == null) { meta = {}; } key = poly.parser.unbracket(expr.name); return metaData[key] = _.extend((_ref1 = metaData[key]) != null ? _ref1 : {}, getMeta(expr), meta); }; data = _.clone(data.raw); addData = function(key, fn) { var d, _i, _len, _results; _results = []; for (_i = 0, _len = data.length; _i < _len; _i++) { d = data[_i]; _results.push(d[key] = fn(d)); } return _results; }; if (dataSpec.trans) { _ref1 = dataSpec.trans; for (_i = 0, _len = _ref1.length; _i < _len; _i++) { expr = _ref1[_i]; addData(expr.name, createFunction(expr.expr)); addMeta(expr); } } if (dataSpec.filter) { data = _.filter(data, filterFactory(dataSpec.filter)); } if (dataSpec.sort) { additionalFilter = []; _ref2 = dataSpec.sort; for (_j = 0, _len1 = _ref2.length; _j < _len1; _j++) { metaSpec = _ref2[_j]; key = metaSpec.key; _ref3 = calculateMeta(metaSpec, data), meta = _ref3.meta, filter = _ref3.filter; additionalFilter.push(filter); addMeta(key, meta); } data = _.filter(data, filterFactory(additionalFilter)); } if (dataSpec.stats && dataSpec.stats.stats && dataSpec.stats.stats.length > 0) { data = calculateStats(data, dataSpec.stats); _ref4 = dataSpec.stats.stats; for (_k = 0, _len2 = _ref4.length; _k < _len2; _k++) { statSpec = _ref4[_k]; expr = statSpec.expr; addMeta(expr); } } _ref6 = (_ref5 = dataSpec.select) != null ? _ref5 : []; for (_l = 0, _len3 = _ref6.length; _l < _len3; _l++) { key = _ref6[_l]; name = poly.parser.unbracket(key.name); if ((metaData[name] == null) && name !== 'count(*)') { throw poly.error.defn("You referenced a data column " + name + " that doesn't exist."); } } return callback(null, { data: data, meta: metaData }); }; /* Perform the necessary computation in the backend */ backendProcess = function(dataSpec, dataObj, callback) { return dataObj.getData(callback, dataSpec); }; /* For debug purposes only */ poly.data.frontendProcess = frontendProcess; poly.data.createFunction = createFunction; }).call(this); // Generated by CoffeeScript 1.6.2 /* Layer ------------------------------------------ A "Layer" is a visual representation of some data. It is sometimes referred to as a glymph, geometry, or mark, and was (erronously) referred to as "chart type" in Polychart graph builder. Each layer needs to be initiated with a specification object. Once initiated, the layer's @calculate() function takes a dataset + metadata, and produces one or more objects representing geometric objects. These geometrical objects have the appropriate data mapped to each appropriate aesthetics, but the scale has not yet been applied. These geometrical objects are be rendered on the screen using the Geometry class found in abstract.coffee Layers can be reused: i.e. created once and applied to many versions of the same data set. It is also disposable, and does not contain state information -- only state that needs to be preserved for consistency is the geometry. */ /* Shared constants */ (function() { var Area, Bar, Box, Layer, Line, Path, Point, Spline, Text, Tile, aesthetics, defaults, sf, _ref, _ref1, _ref2, _ref3, _ref4, _ref5, _ref6, _ref7, _ref8, __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }, __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; aesthetics = poly["const"].aes; sf = poly["const"].scaleFns; defaults = { 'x': sf.novalue(), 'y': sf.novalue(), 'color': 'steelblue', 'size': 5, 'opacity': 0.9, 'shape': 1 }; /* Base class for all layers */ Layer = (function() { Layer.prototype.defaults = defaults; function Layer(spec, strictMode, guideSpec) { var aes, _i, _len; this.spec = spec; this.guideSpec = guideSpec; this.mapping = {}; this.consts = {}; for (_i = 0, _len = aesthetics.length; _i < _len; _i++) { aes = aesthetics[_i]; if (spec[aes]) { if (spec[aes]["var"]) { this.mapping[aes] = poly.parser.unbracket(spec[aes]["var"]); } if (spec[aes]["const"]) { this.consts[aes] = spec[aes]["const"]; } } } } Layer.prototype.calculate = function(statData, meta) { var aes, key, _ref; this.statData = statData; this.meta = meta; this._calcGeoms(); this.geoms = this._sample(this.geoms); meta = {}; _ref = this.mapping; for (aes in _ref) { key = _ref[aes]; meta[aes] = this.meta[key]; } return { geoms: this.geoms, meta: meta }; }; Layer.prototype._calcGeoms = function() { throw poly.error.impl(); }; Layer.prototype._tooltip = function(item) { var tooltip, _this = this; tooltip = null; if (typeof this.spec.tooltip === 'function') { return tooltip = function(scales) { return _this.spec.tooltip(item); }; } else if (this.spec.tooltip != null) { return tooltip = function(scales) { return _this.spec.tooltip; }; } else { return tooltip = function(scales) { var aes, formatter, key, seenKeys, text, _ref; text = ""; seenKeys = []; _ref = _this.mapping; for (aes in _ref) { key = _ref[aes]; if (seenKeys.indexOf(key) !== -1) { continue; } else { seenKeys.push(key); } if ((scales != null) && (scales[aes] != null)) { formatter = poly.format(scales[aes].domain.type, scales[aes].domain.bw); } else { formatter = function(x) { return x; }; } text += "\n" + key + ": " + (formatter(item[key])); } return text.substr(1); }; } }; Layer.prototype._sample = function(geoms) { if (this.spec.sample === false) { return geoms; } else if (_.isNumber(this.spec.sample)) { return poly.sample(geoms, this.spec.sample); } else { throw poly.error.defn("A layer's 'sample' definition should be an integer, not " + this.spec.sample); } }; Layer.prototype._getValue = function(item, aes) { if (this.mapping[aes]) { return item[this.mapping[aes]]; } else if (this.consts[aes]) { return sf.identity(this.consts[aes]); } else if (aes === 'x' || aes === 'y') { return this.defaults[aes]; } else { return sf.identity(this.defaults[aes]); } }; Layer.prototype._getIdFunc = function() { var _this = this; if (this.mapping['id'] != null) { return function(item) { return _this._getValue(item, 'id'); }; } else { return poly.counter(); } }; Layer.prototype._fillZeros = function(data, all_x) { var data_x, item, missing, x; data_x = (function() { var _i, _len, _results; _results = []; for (_i = 0, _len = data.length; _i < _len; _i++) { item = data[_i]; _results.push(this._getValue(item, 'x')); } return _results; }).call(this); missing = _.difference(all_x, data_x); return { x: data_x.concat(missing), y: ((function() { var _i, _len, _results; _results = []; for (_i = 0, _len = data.length; _i < _len; _i++) { item = data[_i]; _results.push(this._getValue(item, 'y')); } return _results; }).call(this)).concat((function() { var _i, _len, _results; _results = []; for (_i = 0, _len = missing.length; _i < _len; _i++) { x = missing[_i]; _results.push(void 0); } return _results; })()) }; }; Layer.prototype._stack = function(group) { var compare, data, datas, item, key, levels, sortfn, tmp, yval, _results, _this = this; datas = poly.groupBy(this.statData, group); _results = []; for (key in datas) { data = datas[key]; if (this.mapping.color) { levels = this.meta[this.mapping.color].levels; sortfn = levels != null ? function(a, b) { a = _.indexOf(levels, a[_this.mapping.color]); b = _.indexOf(levels, b[_this.mapping.color]); if (a < b) { return -1; } else if (a > b) { return 1; } else { return 0; } } : (compare = poly.type.compare(this.meta[this.mapping.color].type), function(a, b) { a = a[_this.mapping.color]; b = b[_this.mapping.color]; return compare(a, b); }); data.sort(sortfn); } tmp = 0; yval = this.mapping.y != null ? (function(item) { return item[_this.mapping.y]; }) : function(item) { return 0; }; _results.push((function() { var _i, _len, _results1; _results1 = []; for (_i = 0, _len = data.length; _i < _len; _i++) { item = data[_i]; item.$lower = tmp; tmp += parseFloat(yval(item)); _results1.push(item.$upper = tmp); } return _results1; })()); } return _results; }; Layer.prototype._dodge = function(group) { var aes, datas, groupAes, groupKey, item, key, numgroup, order, orderfn, values, yval, _i, _len, _ref, _results, _this = this; groupAes = _.without(_.keys(this.mapping), 'x', 'y', 'id'); groupKey = _.toArray(_.pick(this.mapping, groupAes)); yval = this.mapping.y != null ? (function(item) { return item[_this.mapping.y]; }) : function(item) { return 0; }; _ref = poly.groupBy(this.statData, group); _results = []; for (key in _ref) { datas = _ref[key]; order = {}; numgroup = 1; for (_i = 0, _len = groupAes.length; _i < _len; _i++) { aes = groupAes[_i]; values = _.uniq((function() { var _j, _len1, _results1; _results1 = []; for (_j = 0, _len1 = datas.length; _j < _len1; _j++) { item = datas[_j]; _results1.push(this._getValue(item, aes)); } return _results1; }).call(this)); numgroup *= values.length; values.sort(poly.type.compare(this.meta[this.mapping[aes]].type)); order[aes] = values; } orderfn = function(item) { var m, n, _j, _len1; m = numgroup; n = 0; for (_j = 0, _len1 = groupAes.length; _j < _len1; _j++) { aes = groupAes[_j]; m /= order[aes].length; n += m * _.indexOf(order[aes], _this._getValue(item, aes)); } return n; }; _results.push((function() { var _j, _len1, _results1; _results1 = []; for (_j = 0, _len1 = datas.length; _j < _len1; _j++) { item = datas[_j]; item.$n = orderfn(item); _results1.push(item.$m = numgroup); } return _results1; })()); } return _results; }; Layer.prototype._inLevels = function(item) { var aes, _i, _len, _ref, _ref1, _ref2; _ref = ['x', 'y']; for (_i = 0, _len = _ref.length; _i < _len; _i++) { aes = _ref[_i]; if ((((_ref1 = this.guideSpec[aes]) != null ? _ref1.levels : void 0) != null) && (_ref2 = item[this.spec[aes]["var"]], __indexOf.call(this.guideSpec[aes].levels, _ref2) < 0)) { return false; } } return true; }; return Layer; })(); Point = (function(_super) { __extends(Point, _super); function Point() { _ref = Point.__super__.constructor.apply(this, arguments); return _ref; } Point.prototype._calcGeoms = function() { var evtData, idfn, item, k, v, _i, _len, _ref1, _results; idfn = this._getIdFunc(); this.geoms = {}; _ref1 = this.statData; _results = []; for (_i = 0, _len = _ref1.length; _i < _len; _i++) { item = _ref1[_i]; evtData = {}; for (k in item) { v = item[k]; evtData[k] = { "in": [v] }; } _results.push(this.geoms[idfn(item)] = { marks: { 0: { type: 'circle', x: this._getValue(item, 'x'), y: this._getValue(item, 'y'), color: this._getValue(item, 'color'), size: this._getValue(item, 'size'), opacity: this._inLevels(item) ? this._getValue(item, 'opacity') : 0 } }, evtData: evtData, tooltip: this._tooltip(item) }); } return _results; }; return Point; })(Layer); Path = (function(_super) { __extends(Path, _super); function Path() { _ref1 = Path.__super__.constructor.apply(this, arguments); return _ref1; } Path.prototype._calcGeoms = function() { var data, datas, evtData, group, idfn, k, key, sample, _i, _len, _results, _this = this; group = (function() { var _i, _len, _ref2, _results; _ref2 = _.without(_.keys(this.mapping), 'x', 'y'); _results = []; for (_i = 0, _len = _ref2.length; _i < _len; _i++) { k = _ref2[_i]; _results.push(this.mapping[k]); } return _results; }).call(this); datas = poly.groupBy(this.statData, group); idfn = this._getIdFunc(); this.geoms = {}; _results = []; for (k in datas) { data = datas[k]; sample = data[0]; evtData = {}; for (_i = 0, _len = group.length; _i < _len; _i++) { key = group[_i]; evtData[key] = { "in": [sample[key]] }; } _results.push(this.geoms[idfn(sample)] = { marks: { 0: { type: 'path', x: _.map(data, function(item) { return _this._getValue(item, 'x'); }), y: _.map(data, function(item) { return _this._getValue(item, 'y'); }), color: this._getValue(sample, 'color'), opacity: this._getValue(sample, 'opacity'), size: this._getValue(sample, 'size') } }, evtData: evtData }); } return _results; }; return Path; })(Layer); Line = (function(_super) { __extends(Line, _super); function Line() { _ref2 = Line.__super__.constructor.apply(this, arguments); return _ref2; } Line.prototype.defaults = { 'x': sf.novalue(), 'y': sf.novalue(), 'color': 'steelblue', 'size': 1, 'opacity': 0.9, 'shape': 1 }; Line.prototype._calcGeoms = function() { var all_x, data, datas, evtData, group, i, idfn, item, k, key, pair, sample, segments, x, y, _i, _j, _len, _len1, _ref3, _results; all_x = _.uniq((function() { var _i, _len, _ref3, _results; _ref3 = this.statData; _results = []; for (_i = 0, _len = _ref3.length; _i < _len; _i++) { item = _ref3[_i]; _results.push(this._getValue(item, 'x')); } return _results; }).call(this)); group = (function() { var _i, _len, _ref3, _results; _ref3 = _.without(_.keys(this.mapping), 'x', 'y'); _results = []; for (_i = 0, _len = _ref3.length; _i < _len; _i++) { k = _ref3[_i]; _results.push(this.mapping[k]); } return _results; }).call(this); datas = _.pairs(poly.groupBy(this.statData, group)); idfn = this._getIdFunc(); this.geoms = {}; segments = (_.max(datas, function(pair) { return pair[1].length; }))[1].length === 1; _results = []; for (i = _i = 0, _len = datas.length; _i < _len; i = ++_i) { pair = datas[i]; key = pair[0], data = pair[1]; if (segments && i + 1 < datas.length) { data.push(datas[i + 1][1][0]); } sample = data[0]; evtData = {}; for (_j = 0, _len1 = group.length; _j < _len1; _j++) { key = group[_j]; evtData[key] = { "in": [sample[key]] }; } _ref3 = this._fillZeros(data, all_x), x = _ref3.x, y = _ref3.y; _results.push(this.geoms[idfn(sample)] = { marks: { 0: { type: 'line', x: x, y: y, color: this._getValue(sample, 'color'), opacity: this._getValue(sample, 'opacity'), size: this._getValue(sample, 'size') } }, evtData: evtData }); } return _results; }; return Line; })(Layer); Spline = (function(_super) { __extends(Spline, _super); function Spline() { _ref3 = Spline.__super__.constructor.apply(this, arguments); return _ref3; } Spline.prototype._calcGeoms = function() { var geom, key, key2, mark, _ref4, _results; Spline.__super__._calcGeoms.call(this); _ref4 = this.geoms; _results = []; for (key in _ref4) { geom = _ref4[key]; _results.push((function() { var _ref5, _results1; _ref5 = geom.marks; _results1 = []; for (key2 in _ref5) { mark = _ref5[key2]; _results1.push(mark.type = 'spline'); } return _results1; })()); } return _results; }; return Spline; })(Line); Bar = (function(_super) { __extends(Bar, _super); function Bar() { _ref4 = Bar.__super__.constructor.apply(this, arguments); return _ref4; } Bar.prototype._calcGeoms = function() { var m, _ref5; if (this.mapping.y && this.meta[this.mapping.y].type === 'cat') { throw poly.error.defn("The dependent variable of a bar chart cannot be categorical!"); } else { if (this.mapping.x) { m = this.meta[this.mapping.x]; if (m.type !== 'cat' && !m.bw && !m.binned) { if (m.type === 'num' && (this.guideSpec.x.bw == null)) { throw poly.error.type("Bar chart x-values need to be binned. Set binwidth or use the bin() transform!"); } } } this.position = (_ref5 = this.spec.position) != null ? _ref5 : 'stack'; if (this.position === 'stack') { return this._calcGeomsStack(); } else if (this.position === 'dodge') { return this._calcGeomsDodge(); } else { throw poly.error.defn("Bar chart position " + this.position + " is unknown."); } } }; Bar.prototype._calcGeomsDodge = function() { var evtData, group, idfn, item, k, lower, upper, v, _i, _len, _ref5, _results; group = this.mapping.x != null ? [this.mapping.x] : []; this._dodge(group); this._stack(group.concat("$n")); this.geoms = {}; idfn = this._getIdFunc(); _ref5 = this.statData; _results = []; for (_i = 0, _len = _ref5.length; _i < _len; _i++) { item = _ref5[_i]; evtData = {}; for (k in item) { v = item[k]; if (k !== 'y') { evtData[k] = { "in": [v] }; } } lower = sf.lower(this._getValue(item, 'x'), item.$n, item.$m); upper = sf.upper(this._getValue(item, 'x'), item.$n, item.$m); _results.push(this.geoms[idfn(item)] = { marks: { 0: { type: 'rect', x: [lower, upper], y: [item.$lower, item.$upper], color: this._getValue(item, 'color'), opacity: this._inLevels(item) ? this._getValue(item, 'opacity') : 0 } }, evtData: evtData, tooltip: this._tooltip(item) }); } return _results; }; Bar.prototype._calcGeomsStack = function() { var evtData, group, idfn, item, k, v, _i, _len, _ref5, _results; group = this.mapping.x != null ? [this.mapping.x] : []; this._stack(group); idfn = this._getIdFunc(); this.geoms = {}; _ref5 = this.statData; _results = []; for (_i = 0, _len = _ref5.length; _i < _len; _i++) { item = _ref5[_i]; evtData = {}; for (k in item) { v = item[k]; if (k !== 'y') { evtData[k] = { "in": [v] }; } } _results.push(this.geoms[idfn(item)] = { marks: { 0: { type: 'rect', x: [sf.lower(this._getValue(item, 'x')), sf.upper(this._getValue(item, 'x'))], y: [item.$lower, item.$upper], color: this._getValue(item, 'color'), opacity: this._inLevels(item) ? this._getValue(item, 'opacity') : 0 } }, evtData: evtData, tooltip: this._tooltip(item) }); } return _results; }; return Bar; })(Layer); Area = (function(_super) { __extends(Area, _super); function Area() { _ref5 = Area.__super__.constructor.apply(this, arguments); return _ref5; } Area.prototype._calcGeoms = function() { var all_x, counters, data, datas, evtData, group, idfn, item, k, key, sample, x, y, y_next, y_previous, _i, _j, _k, _len, _len1, _len2, _results; all_x = (function() { var _i, _len, _ref6, _results; _ref6 = this.statData; _results = []; for (_i = 0, _len = _ref6.length; _i < _len; _i++) { item = _ref6[_i]; if (poly.isDefined(this._getValue(item, 'y')) && poly.isDefined(x = this._getValue(item, 'x'))) { _results.push(x); } } return _results; }).call(this); all_x = _.uniq(all_x); counters = {}; for (_i = 0, _len = all_x.length; _i < _len; _i++) { key = all_x[_i]; counters[key] = 0; } group = (function() { var _j, _len1, _ref6, _results; _ref6 = _.without(_.keys(this.mapping), 'x', 'y'); _results = []; for (_j = 0, _len1 = _ref6.length; _j < _len1; _j++) { k = _ref6[_j]; _results.push(this.mapping[k]); } return _results; }).call(this); datas = poly.groupBy(this.statData, group); idfn = this._getIdFunc(); this.geoms = {}; _results = []; for (k in datas) { data = datas[k]; sample = data[0]; evtData = {}; for (_j = 0, _len1 = group.length; _j < _len1; _j++) { key = group[_j]; evtData[key] = { "in": [sample[key]] }; } y_previous = (function() { var _k, _len2, _results1; _results1 = []; for (_k = 0, _len2 = all_x.length; _k < _len2; _k++) { x = all_x[_k]; _results1.push(counters[x]); } return _results1; })(); for (_k = 0, _len2 = data.length; _k < _len2; _k++) { item = data[_k]; x = this._getValue(item, 'x'); y = this._getValue(item, 'y'); counters[x] += y; } y_next = (function() { var _l, _len3, _results1; _results1 = []; for (_l = 0, _len3 = all_x.length; _l < _len3; _l++) { x = all_x[_l]; _results1.push(counters[x]); } return _results1; })(); _results.push(this.geoms[idfn(sample)] = { marks: { 0: { type: 'area', x: all_x, y: { bottom: y_previous, top: y_next }, color: this._getValue(sample, 'color'), opacity: this._getValue(sample, 'opacity') } }, evtData: evtData }); } return _results; }; return Area; })(Layer); Text = (function(_super) { __extends(Text, _super); function Text() { _ref6 = Text.__super__.constructor.apply(this, arguments); return _ref6; } Text.prototype._calcGeoms = function() { var evtData, idfn, item, k, v, _i, _len, _ref7, _results; idfn = this._getIdFunc(); this.geoms = {}; _ref7 = this.statData; _results = []; for (_i = 0, _len = _ref7.length; _i < _len; _i++) { item = _ref7[_i]; evtData = {}; for (k in item) { v = item[k]; evtData[k] = { "in": [v] }; } _results.push(this.geoms[idfn(item)] = { marks: { 0: { type: 'text', x: this._getValue(item, 'x'), y: this._getValue(item, 'y'), text: this._getValue(item, 'text'), color: this._getValue(item, 'color'), size: this._getValue(item, 'size'), opacity: this._getValue(item, 'opacity'), 'text-anchor': 'center' } }, evtData: evtData }); } return _results; }; return Text; })(Layer); Tile = (function(_super) { __extends(Tile, _super); function Tile() { _ref7 = Tile.__super__.constructor.apply(this, arguments); return _ref7; } Tile.prototype._calcGeoms = function() { var evtData, idfn, item, k, v, x, y, _i, _len, _ref8, _results; idfn = this._getIdFunc(); this.geoms = {}; _ref8 = this.statData; _results = []; for (_i = 0, _len = _ref8.length; _i < _len; _i++) { item = _ref8[_i]; evtData = {}; x = this._getValue(item, 'x'); y = this._getValue(item, 'y'); for (k in item) { v = item[k]; if (k !== 'y' && k !== 'x') { evtData[k] = { "in": [v] }; } } _results.push(this.geoms[idfn(item)] = { marks: { 0: { type: 'rect', x: [sf.lower(this._getValue(item, 'x')), sf.upper(this._getValue(item, 'x'))], y: [sf.lower(this._getValue(item, 'y')), sf.upper(this._getValue(item, 'y'))], color: this._getValue(item, 'color'), size: this._getValue(item, 'size'), opacity: this._getValue(item, 'opacity') } }, evtData: evtData, tooltip: this._tooltip(item) }); } return _results; }; return Tile; })(Layer); Box = (function(_super) { __extends(Box, _super); function Box() { _ref8 = Box.__super__.constructor.apply(this, arguments); return _ref8; } Box.prototype._calcGeoms = function() { var color, evtData, geom, idfn, index, item, k, opacity, point, size, v, x, xl, xm, xu, y, _i, _j, _len, _len1, _ref10, _ref9, _results; idfn = this._getIdFunc(); this.geoms = {}; _ref9 = this.statData; _results = []; for (_i = 0, _len = _ref9.length; _i < _len; _i++) { item = _ref9[_i]; evtData = {}; for (k in item) { v = item[k]; if (k !== 'y') { evtData[k] = { "in": [v] }; } } x = this._getValue(item, 'x'); y = this._getValue(item, 'y'); color = this._getValue(item, 'color'); size = this._getValue(item, 'size'); opacity = this._inLevels(item) ? this._getValue(item, 'opacity') : 0; xl = sf.lower(x); xu = sf.upper(x); xm = sf.middle(x); geom = { marks: {}, evtData: evtData }; if (y.q1) { geom.marks = { iqr: { type: 'rect', x: [xl, xu], y: [y.q2, y.q4], stroke: color, color: sf.identity('white'), size: size, opacity: opacity, 'stroke-width': '1px' }, q1: { type: 'pline', x: [xl, xu], y: [y.q1, y.q1], color: color, size: size, opacity: opacity }, lower: { type: 'pline', x: [xm, xm], y: [y.q1, y.q2], color: color, size: size, opacity: opacity }, q5: { type: 'pline', x: [xl, xu], y: [y.q5, y.q5], color: color, size: size, opacity: opacity }, upper: { type: 'pline', x: [xm, xm], y: [y.q4, y.q5], color: color, size: size, opacity: opacity }, middle: { type: 'pline', x: [xl, xu], y: [y.q3, y.q3], color: color, size: size, opacity: opacity } }; } _ref10 = y.outliers; for (index = _j = 0, _len1 = _ref10.length; _j < _len1; index = ++_j) { point = _ref10[index]; geom.marks[index] = { type: 'circle', x: xm, y: point, color: color, size: sf.identity(3), opacity: opacity }; } _results.push(this.geoms[idfn(item)] = geom); } return _results; }; return Box; })(Layer); /* Public interface to making different layer types. TODO: this should be changed to make it easier to make other types of layers. */ poly.layer = {}; poly.layer.Base = Layer; poly.layer.classes = { 'point': Point, 'text': Text, 'line': Line, 'path': Path, 'area': Area, 'bar': Bar, 'tile': Tile, 'box': Box, 'spline': Spline }; poly.layer.make = function(layerSpec, strictMode, guideSpec) { var type; type = layerSpec.type; if (type in poly.layer.classes) { return new poly.layer.classes[type](layerSpec, strictMode, guideSpec); } throw poly.error.defn("No such layer " + layerSpec.type + "."); }; }).call(this); // Generated by CoffeeScript 1.6.2 (function() { var Pane, __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; poly.pane = {}; poly.pane.make = function(grp, title) { return new Pane(grp, title); }; Pane = (function(_super) { __extends(Pane, _super); function Pane(multiindex, titleObj) { this.titleObj = titleObj; this.index = multiindex; this.layers = null; this.title = null; } Pane.prototype.make = function(spec, data, layers) { var geoms, i, layer, meta, _i, _j, _len, _len1, _ref, _ref1, _ref2, _ref3; this.layers = layers; if (!this.geoms) { this.geoms = {}; _ref = this.layers; for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) { layer = _ref[i]; this.geoms[i] = new poly.Geometry(); } } this.metas = {}; _ref1 = this.layers; for (i = _j = 0, _len1 = _ref1.length; _j < _len1; i = ++_j) { layer = _ref1[i]; _ref2 = layer.calculate(data[i].statData, data[i].metaData), geoms = _ref2.geoms, meta = _ref2.meta; this.geoms[i].set(geoms); this.metas[i] = meta; } if ((_ref3 = this.title) == null) { this.title = this._makeTitle(spec); } this.title.make(this.titleObj); return this.domains = this._makeDomains(spec, this.geoms, this.metas); }; Pane.prototype._makeTitle = function() { return poly.guide.title('facet'); }; Pane.prototype._makeDomains = function(spec, geoms, metas) { return poly.domain.make(geoms, metas, spec.guides, spec.strict); }; Pane.prototype.render = function(renderer, offset, clipping, dims) { var geom, k, r, _ref, _results; this.title.render(renderer({}, false, false), dims, offset); r = renderer(offset, clipping, true); _ref = this.geoms; _results = []; for (k in _ref) { geom = _ref[k]; _results.push(geom.render(r)); } return _results; }; Pane.prototype.dispose = function(renderer) { var geom, k, _ref; _ref = this.geoms; for (k in _ref) { geom = _ref[k]; geom.dispose(renderer); } this.geoms = {}; return this.title.dispose(renderer); }; return Pane; })(poly.Renderable); }).call(this); // Generated by CoffeeScript 1.6.2 /* DIMENSIONS ---------- Calculate the pixel dimension and layout of a particular chart Dimension object has the following elements (all numeric in pixels): @width: the width of the entire chart, including paddings, guides, etc. @height : the height of the entire chart, including paddings, guides, etc. @paddingLeft: left padding, not including guides @paddingRight: right padding, not including guides @paddingTop: top padding, not including guides @paddingBottom: bottom padding, not including guides @guideLeft: space for guides (axes & legends) on the left side of chart @guideRight: space for guides (axes & legends) on the right side of chart @guideTop: space for guides (axes & legends) on the top of chart @guideBottom: space for guides (axes & legends) on the bottom of chart @chartHeight: height of area given for actual chart, includes all facets and the spaces between the facets @chartWidth: width of area given for actual chart, includes all facets and the spaces between the facets @eachHeight: the height of the chart area for each facet @eachWidth: the width of the chart area for each facet @horizontalSpacing: horizontal space between ajacent facets @verticalSpacing: horizontal space between ajacent facets */ (function() { poly.dim = {}; poly.dim.make = function(spec, scaleSet, facetGrid) { var bottom, dim, hMax, left, right, top, vMax, _ref, _ref1, _ref10, _ref2, _ref3, _ref4, _ref5, _ref6, _ref7, _ref8, _ref9; dim = { width: (_ref = spec.width) != null ? _ref : 400, height: (_ref1 = spec.height) != null ? _ref1 : 400, paddingLeft: (_ref2 = spec.paddingLeft) != null ? _ref2 : 10, paddingRight: (_ref3 = spec.paddingRight) != null ? _ref3 : 10, paddingTop: (_ref4 = spec.paddingTop) != null ? _ref4 : 10, paddingBottom: (_ref5 = spec.paddingBottom) != null ? _ref5 : 10, horizontalSpacing: (_ref6 = spec.horizontalSpacing) != null ? _ref6 : 10, verticalSpacing: (_ref7 = spec.verticalSpacing) != null ? _ref7 : 20, guideTop: 10, guideRight: 0, guideLeft: 5, guideBottom: 5 }; _ref8 = scaleSet.axesOffset(dim), left = _ref8.left, right = _ref8.right, top = _ref8.top, bottom = _ref8.bottom; dim.guideLeft += left != null ? left : 0; dim.guideRight += right != null ? right : 0; dim.guideBottom += bottom != null ? bottom : 0; dim.guideTop += top != null ? top : 0; _ref9 = scaleSet.titleOffset(dim), left = _ref9.left, right = _ref9.right, top = _ref9.top, bottom = _ref9.bottom; dim.guideLeft += left != null ? left : 0; dim.guideRight += right != null ? right : 0; dim.guideBottom += bottom != null ? bottom : 0; dim.guideTop += top != null ? top : 0; _ref10 = scaleSet.legendOffset(dim), left = _ref10.left, right = _ref10.right, top = _ref10.top, bottom = _ref10.bottom; dim.guideLeft += left != null ? left : 0; dim.guideRight += right != null ? right : 0; dim.guideBottom += bottom != null ? bottom : 0; dim.guideTop += top != null ? top : 0; hMax = dim.width * 0.40; vMax = dim.height * 0.40; if (dim.guideLeft > hMax) { dim.guideLeft = hMax; } if (dim.guideRight > hMax) { dim.guideRight = hMax; } if (dim.guideTop > vMax) { dim.guideTop = vMax; } if (dim.guideBottom > vMax) { dim.guideBottom = vMax; } dim.chartHeight = dim.height - dim.paddingTop - dim.paddingBottom - dim.guideTop - dim.guideBottom; dim.chartWidth = dim.width - dim.paddingLeft - dim.paddingRight - dim.guideLeft - dim.guideRight; if ((facetGrid.cols != null) && facetGrid.cols > 1) { dim.eachWidth = dim.chartWidth - dim.horizontalSpacing * facetGrid.cols; dim.eachWidth /= facetGrid.cols; } else { dim.eachWidth = dim.chartWidth; } if ((facetGrid.rows != null) && facetGrid.rows > 1) { dim.eachHeight = dim.chartHeight - dim.verticalSpacing * (facetGrid.rows + 1); dim.eachHeight /= facetGrid.rows; } else { dim.eachHeight = dim.chartHeight - dim.verticalSpacing; } return dim; }; poly.dim.guess = function(spec, facetGrid) { var dim, _ref, _ref1, _ref2, _ref3, _ref4, _ref5, _ref6, _ref7; dim = { width: (_ref = spec.width) != null ? _ref : 400, height: (_ref1 = spec.height) != null ? _ref1 : 400, paddingLeft: (_ref2 = spec.paddingLeft) != null ? _ref2 : 10, paddingRight: (_ref3 = spec.paddingRight) != null ? _ref3 : 10, paddingTop: (_ref4 = spec.paddingTop) != null ? _ref4 : 10, paddingBottom: (_ref5 = spec.paddingBottom) != null ? _ref5 : 10, guideLeft: 30, guideRight: 40, guideTop: 10, guideBottom: 30, horizontalSpacing: (_ref6 = spec.horizontalSpacing) != null ? _ref6 : 10, verticalSpacing: (_ref7 = spec.verticalSpacing) != null ? _ref7 : 10 }; dim.chartHeight = dim.height - dim.paddingTop - dim.paddingBottom - dim.guideTop - dim.guideBottom; dim.chartWidth = dim.width - dim.paddingLeft - dim.paddingRight - dim.guideLeft - dim.guideRight; if ((facetGrid.cols != null) && facetGrid.cols > 1) { dim.eachWidth = dim.chartWidth - dim.horizontalSpacing * (facetGrid.cols - 1); } else { dim.eachWidth = dim.chartWidth; } if ((facetGrid.rows != null) && facetGrid.rows > 1) { dim.eachHeight = dim.chartHeight - dim.verticalSpacing * (facetGrid.rows - 1); } else { dim.eachHeight = dim.chartHeight; } return dim; }; }).call(this); // Generated by CoffeeScript 1.6.2 /* # GLOBALS */ (function() { var Area, Circle, CircleRect, Line, Path, PathRenderer, PolarLine, Rect, Renderer, Spline, Text, renderer, _ref, _ref1, _ref2, _ref3, _ref4, _ref5, _ref6, _ref7, _ref8, _ref9, __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; poly.paper = function(dom, w, h, obj, showBox) { var bg, graph, numeral, paper; if (showBox == null) { showBox = true; } if (typeof Raphael !== "undefined" && Raphael !== null) { paper = Raphael(dom, w, h); } else { paper = poly.canvas(dom, w, h); } graph = obj.graph, numeral = obj.numeral; bg = paper.rect(0, 0, w, h).attr({ fill: 'white', opacity: 0, 'stroke-width': 0 }); if (graph != null) { bg.click(graph.handleEvent('reset')); poly.mouseEvents(graph, bg, showBox); poly.touchEvents(graph.handleEvent, bg, true); } else if (numeral != null) { bg.click(numeral.handleEvent('reset')); } return paper; }; /* Mouse Events */ poly.mouseEvents = function(graph, bg, showRect) { var end, endInfo, handler, onend, onmove, onstart, rect, start, startInfo; handler = graph.handleEvent('select'); if (showRect) { rect = null; } start = end = null; startInfo = endInfo = null; onstart = function() { start = null; return end = null; }; onmove = function(dx, dy, x, y) { var attr, offset; if ((startInfo != null) && (start != null)) { end = { x: start.x + dx, y: start.y + dy }; endInfo = graph.facet.getFacetInfo(graph.dims, end.x, end.y); if ((rect != null) && (endInfo != null) && endInfo.col === startInfo.col && endInfo.row === startInfo.row && showRect) { attr = { x: Math.min(start.x, end.x), y: Math.min(start.y, end.y), width: Math.abs(start.x - end.x), height: Math.abs(start.y - end.y) }; return rect = rect.attr(attr); } } else { offset = poly.offset(graph.dom); start = { x: x - offset.left, y: y - offset.top }; startInfo = graph.facet.getFacetInfo(graph.dims, start.x, start.y); if ((startInfo != null) && showRect) { rect = graph.paper.rect(start.x, start.y, 0, 0, 2); return rect = rect.attr({ fill: 'black', opacity: 0.2 }); } } }; onend = function() { if ((start != null) && (end != null)) { if ((rect != null) && showRect) { rect = rect.hide(); rect.remove(); } return handler({ start: start, end: end }); } }; return bg.drag(onmove, onstart, onend); }; poly.touchEvents = function(handleEvent, elem, enable) { if (enable == null) { enable = true; } if (enable) { elem.touchstart(handleEvent('touchstart')); elem.touchend(handleEvent('touchend')); elem.touchmove(handleEvent('touchmove')); return elem.touchcancel(handleEvent('touchcancel')); } }; /* Helper function for rendering all the geoms of an object */ poly.render = function(handleEvent, paper, scales, coord) { return function(offset, clipping, mayflip) { if (offset == null) { offset = {}; } if (clipping == null) { clipping = false; } if (mayflip == null) { mayflip = true; } if (coord.type == null) { throw poly.error.unknown("Coordinate don't have at type?"); } if (renderer[coord.type] == null) { throw poly.error.input("Unknown coordinate type " + coord.type); } return { add: function(mark, evtData, tooltip, type) { var pt; if (renderer[coord.type][mark.type] == null) { throw poly.error.input("Coord " + coord.type + " has no mark " + mark.type); } pt = renderer[coord.type][mark.type].render(paper, scales, coord, offset, mark, mayflip); pt.data('m', mark); if (evtData && _.keys(evtData).length > 0) { pt.data('e', evtData); } if (tooltip) { pt.data('t', tooltip); } if (clipping != null) { pt.attr('clip-rect', clipping); } if (type === 'guide') { pt.click(handleEvent('guide-click')); pt.hover(handleEvent('gover'), handleEvent('gout')); poly.touchEvents(handleEvent, pt, true); } else if (type === 'guide-title' || type === 'guide-titleH' || type === 'guide-titleV') { pt.click(handleEvent(type)); pt.hover(handleEvent('tover'), handleEvent('tout')); poly.touchEvents(handleEvent, pt, true); } else { pt.click(handleEvent('click')); pt.hover(handleEvent('mover'), handleEvent('mout')); poly.touchEvents(handleEvent, pt, true); } return pt; }, remove: function(pt) { return pt.remove(); }, animate: function(pt, mark, evtData, tooltip) { renderer[coord.type][mark.type].animate(pt, scales, coord, offset, mark, mayflip); if (clipping != null) { pt.attr('clip-rect', clipping); } if (evtData && _.keys(evtData).length > 0) { pt.data('e', evtData); } if (tooltip) { pt.data('t', tooltip); } pt.data('m', mark); return pt; } }; }; }; Renderer = (function() { function Renderer() {} Renderer.prototype.render = function(paper, scales, coord, offset, mark, mayflip) { var k, pt, v, _ref; pt = this._make(paper); _ref = this.attr(scales, coord, offset, mark, mayflip); for (k in _ref) { v = _ref[k]; pt.attr(k, v); } return pt; }; Renderer.prototype._make = function() { throw poly.error.impl(); }; Renderer.prototype.animate = function(pt, scales, coord, offset, mark, mayflip) { return pt.animate(this.attr(scales, coord, offset, mark, mayflip), 300); }; Renderer.prototype.attr = function(scales, coord, offset, mark, mayflip) { throw poly.error.impl(); }; Renderer.prototype._cantRender = function(aes) { throw poly.error.missingdata(); }; Renderer.prototype._makePath = function(xs, ys, type) { var path; if (type == null) { type = 'L '; } if (type === 'spline') { path = _.map(xs, function(x, i) { return (i === 0 ? "M " + x + " " + ys[i] + " R " : '') + ("" + x + " " + ys[i]); }); } else { path = _.map(xs, function(x, i) { return (i === 0 ? 'M ' : type) + x + ' ' + ys[i]; }); } return path.join(' '); }; Renderer.prototype._maybeApply = function(scales, mark, key) { var val; val = mark[key]; if (_.isObject(val) && val.f === 'identity') { return val.v; } else if (scales[key] != null) { return scales[key].f(val); } else { return val; } }; Renderer.prototype._applyOffset = function(x, y, offset) { var i, _ref, _ref1; if (!offset) { return { x: x, y: y }; } if ((_ref = offset.x) == null) { offset.x = 0; } if ((_ref1 = offset.y) == null) { offset.y = 0; } return { x: _.isArray(x) ? (function() { var _i, _len, _results; _results = []; for (_i = 0, _len = x.length; _i < _len; _i++) { i = x[_i]; _results.push(i + offset.x); } return _results; })() : x + offset.x, y: _.isArray(y) ? (function() { var _i, _len, _results; _results = []; for (_i = 0, _len = y.length; _i < _len; _i++) { i = y[_i]; _results.push(i + offset.y); } return _results; })() : y + offset.y }; }; Renderer.prototype._shared = function(scales, mark, attr) { var maybeAdd, _this = this; maybeAdd = function(aes) { if ((mark[aes] != null) && (attr[aes] == null)) { return attr[aes] = _this._maybeApply(scales, mark, aes); } }; maybeAdd('opacity'); maybeAdd('stroke-width'); maybeAdd('stroke-dasharray'); maybeAdd('stroke-dashoffset'); maybeAdd('transform'); maybeAdd('font-size'); maybeAdd('font-weight'); maybeAdd('font-family'); return attr; }; Renderer.prototype._checkPointUndefined = function(x, y, type) { if (type == null) { type = "Point"; } if (x === void 0 || y === void 0) { throw poly.error.missing("" + type + " cannot be plotted due to undefined data."); } }; Renderer.prototype._checkArrayUndefined = function(x, y, type) { var i; if (type == null) { type = "Line"; } if (_.all((function() { var _i, _ref, _results; _results = []; for (i = _i = 0, _ref = x.length - 1; 0 <= _ref ? _i <= _ref : _i >= _ref; i = 0 <= _ref ? ++_i : --_i) { _results.push(x[i] === void 0 || y[i] === void 0); } return _results; })())) { throw poly.error.missing("" + type + " cannot be plotted due to too many missing points."); } }; Renderer.prototype._checkArrayNaN = function(xs, ys) { var z, zs; zs = _.map(_.zip(xs, ys), function(z, i) { if (isNaN(z[0]) || isNaN(z[1])) { return void 0; } else { return z; } }); return { x: (function() { var _i, _len, _results; _results = []; for (_i = 0, _len = zs.length; _i < _len; _i++) { z = zs[_i]; if (z != null) { _results.push(z[0]); } } return _results; })(), y: (function() { var _i, _len, _results; _results = []; for (_i = 0, _len = zs.length; _i < _len; _i++) { z = zs[_i]; if (z != null) { _results.push(z[1]); } } return _results; })() }; }; return Renderer; })(); PathRenderer = (function(_super) { __extends(PathRenderer, _super); function PathRenderer() { _ref = PathRenderer.__super__.constructor.apply(this, arguments); return _ref; } PathRenderer.prototype.animate = function(pt, scales, coord, offset, mark, mayflip) { var newattr, oldmark, scaleattr, _this = this; oldmark = pt.data('m'); newattr = this.attr(scales, coord, offset, mark, mayflip); if (!_.isEqual(oldmark.x, mark.x)) { scaleattr = this.attr(scales, coord, offset, oldmark, mayflip); return pt.animate(scaleattr, 300, function() { return pt.attr(newattr); }); } else { return pt.animate(newattr, 300); } }; return PathRenderer; })(Renderer); Circle = (function(_super) { __extends(Circle, _super); function Circle() { _ref1 = Circle.__super__.constructor.apply(this, arguments); return _ref1; } Circle.prototype._make = function(paper) { return paper.circle(); }; Circle.prototype.attr = function(scales, coord, offset, mark, mayflip) { var attr, fill, stroke, x, y, _ref2, _ref3; _ref2 = coord.getXY(mayflip, mark), x = _ref2.x, y = _ref2.y; this._checkPointUndefined(x, y, "Circle"); _ref3 = this._applyOffset(x, y, offset), x = _ref3.x, y = _ref3.y; stroke = this._maybeApply(scales, mark, mark.stroke ? 'stroke' : 'color'); attr = { cx: x, cy: y, r: this._maybeApply(scales, mark, 'size'), stroke: stroke }; fill = this._maybeApply(scales, mark, 'color'); if (fill && fill !== 'none') { attr.fill = fill; } return this._shared(scales, mark, attr); }; return Circle; })(Renderer); Path = (function(_super) { __extends(Path, _super); function Path() { _ref2 = Path.__super__.constructor.apply(this, arguments); return _ref2; } Path.prototype._make = function(paper) { return paper.path(); }; Path.prototype.attr = function(scales, coord, offset, mark, mayflip) { var size, stroke, x, y, _ref3, _ref4; _ref3 = coord.getXY(mayflip, mark), x = _ref3.x, y = _ref3.y; this._checkArrayUndefined(x, y, "Path"); _ref4 = this._applyOffset(x, y, offset), x = _ref4.x, y = _ref4.y; stroke = this._maybeApply(scales, mark, mark.stroke ? 'stroke' : 'color'); size = this._maybeApply(scales, mark, mark.size ? 'size' : 'stroke-width'); return this._shared(scales, mark, { path: this._makePath(x, y), stroke: stroke, 'stroke-width': size }); }; return Path; })(Renderer); Line = (function(_super) { __extends(Line, _super); function Line() { _ref3 = Line.__super__.constructor.apply(this, arguments); return _ref3; } Line.prototype._make = function(paper) { return paper.path(); }; Line.prototype.attr = function(scales, coord, offset, mark, mayflip) { var i, size, stroke, x, xi, y, yi, _i, _len, _ref4, _ref5, _ref6, _ref7; _ref4 = poly.sortArrays(scales.x.compare, [mark.x, mark.y]), mark.x = _ref4[0], mark.y = _ref4[1]; _ref5 = coord.getXY(mayflip, mark), x = _ref5.x, y = _ref5.y; this._checkArrayUndefined(x, y, "Line"); for (i = _i = 0, _len = x.length; _i < _len; i = ++_i) { xi = x[i]; yi = y[i]; } _ref6 = this._applyOffset(x, y, offset), x = _ref6.x, y = _ref6.y; _ref7 = this._checkArrayNaN(x, y), x = _ref7.x, y = _ref7.y; stroke = this._maybeApply(scales, mark, mark.stroke ? 'stroke' : 'color'); size = this._maybeApply(scales, mark, mark.size ? 'size' : 'stroke-width'); return this._shared(scales, mark, { path: this._makePath(x, y), stroke: stroke, 'stroke-width': size }); }; return Line; })(PathRenderer); Spline = (function(_super) { __extends(Spline, _super); function Spline() { _ref4 = Spline.__super__.constructor.apply(this, arguments); return _ref4; } Spline.prototype.attr = function(scales, coord, offset, mark, mayflip) { var i, size, stroke, x, xi, y, yi, _i, _len, _ref5, _ref6, _ref7, _ref8; _ref5 = poly.sortArrays(scales.x.compare, [mark.x, mark.y]), mark.x = _ref5[0], mark.y = _ref5[1]; _ref6 = coord.getXY(mayflip, mark), x = _ref6.x, y = _ref6.y; this._checkArrayUndefined(x, y, "Spline"); for (i = _i = 0, _len = x.length; _i < _len; i = ++_i) { xi = x[i]; yi = y[i]; } _ref7 = this._applyOffset(x, y, offset), x = _ref7.x, y = _ref7.y; _ref8 = this._checkArrayNaN(x, y), x = _ref8.x, y = _ref8.y; stroke = this._maybeApply(scales, mark, mark.stroke ? 'stroke' : 'color'); size = this._maybeApply(scales, mark, mark.size ? 'size' : 'stroke-width'); return this._shared(scales, mark, { path: this._makePath(x, y, 'spline'), stroke: stroke, 'stroke-width': size }); }; return Spline; })(Line); PolarLine = (function(_super) { __extends(PolarLine, _super); function PolarLine() { _ref5 = PolarLine.__super__.constructor.apply(this, arguments); return _ref5; } PolarLine.prototype.attr = function(scales, coord, offset, mark, mayflip) { var dir, i, large, path, r, stroke, t, x, y, _ref6, _ref7; _ref6 = coord.getXY(mayflip, mark), x = _ref6.x, y = _ref6.y, r = _ref6.r, t = _ref6.t; this._checkArrayUndefined(x, y, "Line"); _ref7 = this._applyOffset(x, y, offset), x = _ref7.x, y = _ref7.y; path = (function() { var _i, _ref8; if (_.max(r) - _.min(r) < poly["const"].epsilon) { r = r[0]; path = "M " + x[0] + " " + y[0]; for (i = _i = 1, _ref8 = x.length - 1; 1 <= _ref8 ? _i <= _ref8 : _i >= _ref8; i = 1 <= _ref8 ? ++_i : --_i) { large = Math.abs(t[i] - t[i - 1]) > Math.PI ? 1 : 0; dir = t[i] - t[i - 1] > 0 ? 1 : 0; path += "A " + r + " " + r + " 0 " + large + " " + dir + " " + x[i] + " " + y[i]; } return path; } else { return this._makePath(x, y); } }).call(this); stroke = this._maybeApply(scales, mark, mark.stroke ? 'stroke' : 'color'); return this._shared(scales, mark, { path: path, stroke: stroke }); }; return PolarLine; })(Line); Area = (function(_super) { __extends(Area, _super); function Area() { _ref6 = Area.__super__.constructor.apply(this, arguments); return _ref6; } Area.prototype._make = function(paper) { return paper.path(); }; Area.prototype.attr = function(scales, coord, offset, mark, mayflip) { var bottom, top, x, y, _ref7, _ref8; _ref7 = poly.sortArrays(scales.x.compare, [mark.x, mark.y.top]), x = _ref7[0], y = _ref7[1]; top = coord.getXY(mayflip, { x: x, y: y }); top = this._applyOffset(top.x, top.y, offset); _ref8 = poly.sortArrays((function(a, b) { return -scales.x.compare(a, b); }), [mark.x, mark.y.bottom]), x = _ref8[0], y = _ref8[1]; bottom = coord.getXY(mayflip, { x: x, y: y }); bottom = this._applyOffset(bottom.x, bottom.y, offset); x = top.x.concat(bottom.x); y = top.y.concat(bottom.y); return this._shared(scales, mark, { path: this._makePath(x, y), stroke: this._maybeApply(scales, mark, 'color'), fill: this._maybeApply(scales, mark, 'color'), 'stroke-width': '0px' }); }; return Area; })(PathRenderer); Rect = (function(_super) { __extends(Rect, _super); function Rect() { _ref7 = Rect.__super__.constructor.apply(this, arguments); return _ref7; } Rect.prototype._make = function(paper) { return paper.rect(); }; Rect.prototype.attr = function(scales, coord, offset, mark, mayflip) { var stroke, x, y, _ref8, _ref9; _ref8 = coord.getXY(mayflip, mark), x = _ref8.x, y = _ref8.y; this._checkPointUndefined(x[0], y[0], "Bar"); this._checkPointUndefined(x[1], y[1], "Bar"); _ref9 = this._applyOffset(x, y, offset), x = _ref9.x, y = _ref9.y; stroke = this._maybeApply(scales, mark, mark.stroke ? 'stroke' : 'color'); return this._shared(scales, mark, { x: _.min(x), y: _.min(y), width: Math.abs(x[1] - x[0]), height: Math.abs(y[1] - y[0]), fill: this._maybeApply(scales, mark, 'color'), stroke: stroke, 'stroke-width': this._maybeApply(scales, mark, 'stroke-width' != null ? 'stroke-width' : '0px') }); }; return Rect; })(Renderer); CircleRect = (function(_super) { __extends(CircleRect, _super); function CircleRect() { _ref8 = CircleRect.__super__.constructor.apply(this, arguments); return _ref8; } CircleRect.prototype._make = function(paper) { return paper.path(); }; CircleRect.prototype.attr = function(scales, coord, offset, mark, mayflip) { var large, path, r, stroke, t, x, x0, x1, y, y0, y1, _ref10, _ref11, _ref12, _ref9; _ref9 = mark.x, x0 = _ref9[0], x1 = _ref9[1]; _ref10 = mark.y, y0 = _ref10[0], y1 = _ref10[1]; this._checkPointUndefined(x0, y0, "Bar"); this._checkPointUndefined(x1, y1, "Bar"); mark.x = [x0, x0, x1, x1]; mark.y = [y0, y1, y1, y0]; _ref11 = coord.getXY(mayflip, mark), x = _ref11.x, y = _ref11.y, r = _ref11.r, t = _ref11.t; _ref12 = this._applyOffset(x, y, offset), x = _ref12.x, y = _ref12.y; if (coord.flip) { x.push(x.splice(0, 1)[0]); y.push(y.splice(0, 1)[0]); r.push(r.splice(0, 1)[0]); t.push(t.splice(0, 1)[0]); } if (2 * Math.PI - Math.abs(t[1] - t[0]) < poly["const"].epsilon) { path = "M " + x[0] + " " + y[0] + " A " + r[0] + " " + r[0] + " 0 1 1 " + x[0] + " " + (y[0] + 2 * r[0]) + " A " + r[1] + " " + r[1] + " 0 1 1 " + x[1] + " " + y[1]; path += "M " + x[2] + " " + y[2] + " A " + r[2] + " " + r[2] + " 0 1 0 " + x[2] + " " + (y[2] + 2 * r[2]) + " A " + r[3] + " " + r[3] + " 0 1 0 " + x[3] + " " + y[3] + " Z"; } else { large = Math.abs(t[1] - t[0]) > Math.PI ? 1 : 0; path = "M " + x[0] + " " + y[0] + " A " + r[0] + " " + r[1] + " 0 " + large + " 1 " + x[1] + " " + y[1]; large = Math.abs(t[3] - t[2]) > Math.PI ? 1 : 0; path += "L " + x[2] + " " + y[2] + " A " + r[2] + " " + r[3] + " 0 " + large + " 0 " + x[3] + " " + y[3] + " Z"; } stroke = this._maybeApply(scales, mark, mark.stroke ? 'stroke' : 'color'); return this._shared(scales, mark, { path: path, fill: this._maybeApply(scales, mark, 'color'), stroke: stroke, 'stroke-width': this._maybeApply(scales, mark, 'stroke-width' != null ? 'stroke-width' : '0px') }); }; return CircleRect; })(Renderer); Text = (function(_super) { __extends(Text, _super); function Text() { _ref9 = Text.__super__.constructor.apply(this, arguments); return _ref9; } Text.prototype._make = function(paper) { return paper.text(); }; Text.prototype.attr = function(scales, coord, offset, mark, mayflip) { var x, y, _ref10, _ref11, _ref12; _ref10 = coord.getXY(mayflip, mark), x = _ref10.x, y = _ref10.y; this._checkPointUndefined(x, y, "Text"); _ref11 = this._applyOffset(x, y, offset), x = _ref11.x, y = _ref11.y; return this._shared(scales, mark, { x: x, y: y, r: 10, text: this._maybeApply(scales, mark, 'text'), 'font-size': this._maybeApply(scales, mark, 'size'), 'text-anchor': (_ref12 = mark['text-anchor']) != null ? _ref12 : 'left', fill: this._maybeApply(scales, mark, 'color') || 'black' }); }; return Text; })(Renderer); renderer = { cartesian: { circle: new Circle(), line: new Line(), pline: new Line(), area: new Area(), path: new Path(), text: new Text(), rect: new Rect(), spline: new Spline() }, polar: { circle: new Circle(), path: new Path(), line: new Line(), pline: new PolarLine(), area: new Area(), text: new Text(), rect: new CircleRect(), spline: new Spline() } }; }).call(this); // Generated by CoffeeScript 1.6.2 /* Interaction ----------- The functions here makes it easier to create common types of interactions. */ (function() { var __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; poly.handler = {}; /* Render a tooltip. This is actually included automatically for every graph. */ poly.handler.tooltip = function() { var maxHeight, maxWidth, minWidth, offset, paper, tooltip, _boxMargin, _boxPadding, _boxRadius, _positionTooltip, _update; tooltip = {}; offset = null; paper = null; _boxPadding = 10; _boxMargin = 20; _boxRadius = 10; maxHeight = null; maxWidth = null; minWidth = null; _update = function(e) { var mousePos; mousePos = poly.getXY(offset, e); if (tooltip.text != null) { return _positionTooltip(tooltip, mousePos); } }; _positionTooltip = function(tooltip, mousePos) { var box, delta, height, mx, my, text, width, x, y, _ref, _ref1; _ref = [mousePos.x, mousePos.y], mx = _ref[0], my = _ref[1]; if (tooltip.text != null) { height = tooltip.text.getBBox().height; text = { x: mx, y: my - height / 2 - _boxMargin }; tooltip.text.attr(text); _ref1 = tooltip.text.getBBox(), x = _ref1.x, y = _ref1.y, width = _ref1.width, height = _ref1.height; box = { x: x - _boxPadding / 2, y: y - _boxPadding / 2, width: width + _boxPadding, height: height + _boxPadding }; if (box.y < 0) { box.y = y + 3 * _boxPadding + height; text.y = my + height / 2 + 3 * _boxMargin / 4; } if (box.x + box.width > maxWidth) { delta = box.x + box.width - maxWidth; box.x -= delta / 2; text.x -= delta / 2; } if (box.x < minWidth) { text.x += minWidth - box.x; box.x = minWidth; } tooltip.text.attr(text); tooltip.box.attr(box); return tooltip; } }; return function(type, obj, event, graph) { var height, mousePos, width, x, x1, x2, y, y1, y2, _ref, _ref1, _ref2, _ref3; offset = poly.offset(graph.dom); paper = obj.paper; maxHeight = graph.dims.chartHeight; maxWidth = graph.dims.chartWidth + graph.dims.guideLeft + graph.dims.paddingLeft; minWidth = graph.dims.guideLeft + graph.dims.paddingLeft; if (type === 'mover' || type === 'mout') { if (tooltip.text != null) { tooltip.text.remove(); tooltip.box.remove(); } tooltip = {}; if (type === 'mover' && obj.tooltip) { mousePos = poly.getXY(offset, event); _ref = obj.getBBox(), x = _ref.x, y = _ref.y, x2 = _ref.x2, y2 = _ref.y2; _ref1 = [mousePos.x, mousePos.y], x1 = _ref1[0], y1 = _ref1[1]; tooltip.text = paper.text(x1, y1, obj.tooltip(graph.scaleSet.scales)).attr({ 'text-anchor': 'middle', fill: 'white' }); _ref2 = tooltip.text.getBBox(), x = _ref2.x, y = _ref2.y, width = _ref2.width, height = _ref2.height; tooltip.text.attr({ y: y1 - height / 2 - _boxMargin }); _ref3 = tooltip.text.getBBox(), x = _ref3.x, y = _ref3.y, width = _ref3.width, height = _ref3.height; tooltip.box = paper.rect(x - _boxPadding / 2, y - _boxPadding / 2, width + _boxPadding, height + _boxPadding, _boxRadius); tooltip.box.attr({ fill: '#213' }); tooltip.text.toFront(); tooltip = _positionTooltip(tooltip, mousePos); return obj.mousemove(_update); } } }; }; /* Drilldown. Suitable for bar charts over categorical data, mostly. This function does not handle the following: * drilldown for multiple aesthetics. does this even make sense? * breaks if an initial filter overlaps with one of the drilldown levels */ poly.handler.drilldown = function(aes, levels, initial_filter) { var current, filters; if (initial_filter == null) { initial_filter = {}; } if (!_.isArray(levels)) { throw poly.error.input("Parameter `levels` should be an array."); } if (__indexOf.call(poly["const"].aes, aes) < 0) { throw poly.error.input("Unknown aesthetic " + aes + "."); } current = 0; filters = [initial_filter]; return function(type, obj, event, graph) { var data, layer, newFilter, newFilterValue, spec, _i, _j, _len, _len1, _ref, _ref1; if (type === 'reset' && current > 0) { spec = graph.spec; filters.pop(); newFilter = filters.unshift(); current--; _ref = spec.layers; for (_i = 0, _len = _ref.length; _i < _len; _i++) { layer = _ref[_i]; layer.filter = newFilter; layer[aes] = levels[current]; layer.id = levels[current]; } return graph.make(graph.spec); } else if (type === 'click' && current < levels.length - 1) { data = obj.evtData; spec = graph.spec; newFilterValue = data[levels[current]]; if (!newFilterValue) { return; } newFilter = {}; newFilter[levels[current]] = newFilterValue; current++; newFilter = _.extend(_.clone(filters[filters.length - 1]), newFilter); _ref1 = spec.layers; for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { layer = _ref1[_j]; layer.filter = newFilter; layer[aes] = levels[current]; layer.id = levels[current]; } filters.push(newFilter); return graph.make(graph.spec); } }; }; /* Zooming and Resetting. Whenever click and drag on range, set to that range. * Reset event, that is, restoring to previous values, when click blank spot * TODO: Add a friendly interface to restrict zooms */ poly.handler.zoom = function(init_spec, zoomOptions) { var aes, initGuides, initHandlers, _ref, _ref1, _wrapHandlers; if (zoomOptions == null) { zoomOptions = { x: true, y: true }; } if (init_spec == null) { throw poly.error.input("Initial specification missing."); } initGuides = { x: _.clone((_ref = init_spec.guides) != null ? _ref.x : void 0), y: _.clone((_ref1 = init_spec.guides) != null ? _ref1.y : void 0) }; initHandlers = void 0; aes = ['x', 'y']; _wrapHandlers = function(h) { return function(type, obj, event, graph) { if (type === 'reset') { if (_.isFunction(h)) { return h('resetZoom', obj, event, graph); } else { return h.handle('resetZoom', obj, event, graph); } } else { if (_.isFunction(h)) { return h(type, obj, event, graph); } else { return h.handle(type, obj, event, graph); } } }; }; return function(type, obj, event, graph) { var aesVar, data, guides, layer, spec, v, _i, _j, _k, _len, _len1, _len2, _ref2, _ref3, _ref4, _ref5, _results; if (initHandlers == null) { initHandlers = _.clone(graph.handlers); } if (graph.coord.type === 'cartesian') { if (type === 'resetZoom') { spec = graph.spec; for (_i = 0, _len = aes.length; _i < _len; _i++) { v = aes[_i]; spec.guides[v] = _.clone(initGuides[v]); } graph.handlers = _.clone(initHandlers); graph.make(graph.spec); } if (type === 'select') { data = obj.evtData; guides = graph.spec.guides; _ref2 = graph.spec.layers; _results = []; for (_j = 0, _len1 = _ref2.length; _j < _len1; _j++) { layer = _ref2[_j]; for (_k = 0, _len2 = aes.length; _k < _len2; _k++) { v = aes[_k]; if (!(zoomOptions[v] && (layer[v] != null))) { continue; } aesVar = poly.parser.unbracket(layer[v]["var"]); if ((_ref3 = graph.axes.domains[v].type) === 'num' || _ref3 === 'date') { if (data[aesVar].le - data[aesVar].ge > poly["const"].epsilon) { if ((_ref4 = guides[v]) == null) { guides[v] = { min: null, max: null }; } guides[v].min = data[aesVar].ge; guides[v].max = data[aesVar].le; } } if (graph.axes.domains[v].type === 'cat') { if (data[aesVar]["in"].length !== 0) { if ((_ref5 = guides[v]) == null) { guides[v] = { levels: null }; } guides[v].levels = data[aesVar]["in"]; } } } graph.handlers = _.map(graph.handlers, _wrapHandlers); _results.push(graph.make(graph.spec)); } return _results; } } }; }; }).call(this); // Generated by CoffeeScript 1.6.2 (function() { var Facet, __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; poly.facet = {}; poly.facet.make = function() { return new Facet(); }; Facet = (function() { function Facet() { this.type = 'none'; this.mapping = {}; this.specgroups = []; this.groups = []; this.panes = {}; this.deletedPanes = []; } Facet.prototype.make = function(spec) { var aes, key, mapping, _ref; this.spec = spec; _ref = this._getMappings(this.spec.facet), this.type = _ref.type, mapping = _ref.mapping; this.mapping = mapping; this.groups = _.values(this.mapping); this.specgroups = {}; for (aes in mapping) { key = mapping[aes]; if (this.spec.facet[aes]) { key = poly.parser.normalize(key); this.specgroups[key] = this.spec.facet[aes]; } } if (this.spec.facet.formatter) { this.formatter = this.spec.facet.formatter; } this.style = {}; if (this.spec.facet.size) { this.style.size = this.spec.facet.size; } if (this.spec.facet.color) { return this.style.color = this.spec.facet.color; } }; Facet.prototype.calculate = function(datas, layers) { var added, deleted, kept, key, multiindex, name, numFacets, _i, _j, _len, _len1, _ref, _ref1, _ref2, _results; _ref = this._makeIndices(datas, this.specgroups), this.values = _ref.values, this.indices = _ref.indices; if (this.type === 'none') { this.rows = this.cols = 1; } else { this.cols = this.spec.facet.cols; this.rows = this.spec.facet.rows; if (this.type === 'wrap') { numFacets = this.values[this.mapping["var"]].length; if (!this.cols && !this.rows) { this.cols = Math.min(3, numFacets); } if (this.cols) { this.rows = Math.ceil(numFacets / this.cols); } else if (this.rows) { this.cols = Math.ceil(numFacets / this.rows); } } else { this.rows = this.mapping.y ? this.values[this.mapping.y].length : 1; this.cols = this.mapping.x ? this.values[this.mapping.x].length : 1; } } this.datas = this._groupData(datas, this.indices); _ref1 = poly.compare(_.keys(this.panes), _.keys(this.indices)), deleted = _ref1.deleted, kept = _ref1.kept, added = _ref1.added; for (_i = 0, _len = deleted.length; _i < _len; _i++) { key = deleted[_i]; this.deletedPanes.push(this.panes[key]); delete this.panes[key]; } for (_j = 0, _len1 = added.length; _j < _len1; _j++) { key = added[_j]; name = this.formatter ? this.formatter(this.indices[key]) : key; this.panes[key] = poly.pane.make(this.indices[key], _.extend({ title: name }, this.style)); } _ref2 = this.indices; _results = []; for (key in _ref2) { multiindex = _ref2[key]; _results.push(this.panes[key].make(this.spec, this.datas[key], layers)); } return _results; }; Facet.prototype.render = function(renderer, dims, coord) { var clipping, key, offset, pane, renderRemoval, _i, _len, _ref, _ref1, _results; if (this.deletedPanes.length > 0) { renderRemoval = renderer({}, false, false); _ref = this.deletedPanes; for (_i = 0, _len = _ref.length; _i < _len; _i++) { pane = _ref[_i]; pane.dispose(renderRemoval); } this.deletedPanes = []; } _ref1 = this.panes; _results = []; for (key in _ref1) { pane = _ref1[key]; offset = this.getOffset(dims, key); clipping = coord.clipping(offset); _results.push(pane.render(renderer, offset, clipping, dims)); } return _results; }; Facet.prototype.dispose = function(renderer) { var key, pane, _i, _len, _ref, _ref1; _ref = this.panes; for (key in _ref) { pane = _ref[key]; this.deletedPanes.push(pane); delete this.panes[key]; } if (renderer) { _ref1 = this.deletedPanes; for (_i = 0, _len = _ref1.length; _i < _len; _i++) { pane = _ref1[_i]; pane.dispose(renderer); } return this.deletedPanes = []; } else { } }; Facet.prototype.getGrid = function() { return { cols: this.cols, rows: this.rows }; }; Facet.prototype.getOffset = function(dims, id) { var col, row, _ref; _ref = this._getRowCol(id), col = _ref.col, row = _ref.row; return { x: dims.paddingLeft + dims.guideLeft + (dims.eachWidth + dims.horizontalSpacing) * col, y: dims.paddingTop + dims.guideTop + (dims.eachHeight + dims.verticalSpacing) * row + dims.verticalSpacing }; }; Facet.prototype.edge = function(dir) { var acc, col, edge, grp, key, m, n, optimize, row, _this = this; if (this.type === 'none') { return function() { return true; }; } if (this.type === 'grid') { row = function(id) { return _.indexOf(_this.values[_this.mapping.y], _this.indices[id][_this.mapping.y]); }; col = function(id) { return _.indexOf(_this.values[_this.mapping.x], _this.indices[id][_this.mapping.x]); }; } else { col = function(id) { return _.indexOf(_this.values[_this.mapping["var"]], _this.indices[id][_this.mapping["var"]]) % _this.cols; }; row = function(id) { return Math.floor(_.indexOf(_this.values[_this.mapping["var"]], _this.indices[id][_this.mapping["var"]]) / _this.cols); }; } if (dir === 'none') { return function() { return false; }; } if (dir === 'out') { return function() { return true; }; } grp = dir === 'top' || dir === 'bottom' ? col : row; optimize = dir === 'top' ? row : dir === 'bottom' ? function(k) { return -row(k); } : dir === 'left' ? col : dir === 'right' ? function(k) { return -col(k); } : void 0; acc = {}; for (key in this.indices) { n = grp(key); m = optimize(key); if (!acc[n] || m < acc[n].v) { acc[n] = { v: m, k: key }; } } edge = _.pluck(acc, 'k'); return function(identifier) { return __indexOf.call(edge, identifier) >= 0; }; }; Facet.prototype.getEvtData = function(col, row) { var aes, key, obj, _ref; obj = {}; _ref = this.mapping; for (aes in _ref) { key = _ref[aes]; if (aes === 'x' || aes === 'y') { obj[key] = { "in": this.values[key][col] }; } else { obj[key] = { "in": this.values[key][this.rows * row + col] }; } } return obj; }; Facet.prototype.getFacetInfo = function(dims, x, y, preset) { var adjusted, col, offset, row; if (preset) { if (!((preset.col != null) && (preset.row != null))) { throw poly.error.impl("Preset rows & columns are not present."); } col = preset.col; row = preset.row; } else { col = (x - dims.paddingLeft - dims.guideLeft) / (dims.eachWidth + dims.horizontalSpacing); col = Math.floor(col); row = (y - dims.paddingTop - dims.guideTop - dims.verticalSpacing) / (dims.eachHeight + dims.verticalSpacing); row = Math.floor(row); } if (col < 0 || col >= this.cols || row < 0 || row >= this.rows) { return null; } offset = { x: dims.paddingLeft + dims.guideLeft + (dims.eachWidth + dims.horizontalSpacing) * col, y: dims.paddingTop + dims.guideTop + (dims.eachHeight + dims.verticalSpacing) * row + dims.verticalSpacing }; adjusted = { x: x - offset.x, y: y - offset.y }; if (!preset && (adjusted.x > dims.eachWidth || adjusted.y > dims.eachHeight)) { return null; } adjusted.x = Math.max(Math.min(adjusted.x, dims.eachWidth), 0); adjusted.y = Math.max(Math.min(adjusted.y, dims.eachHeight), 0); return { row: row, col: col, offset: offset, adjusted: adjusted, evtData: this.getEvtData(col, row) }; }; /* Helper functions */ Facet.prototype._getMappings = function(spec) { var retobj; retobj = { type: 'none', mapping: {} }; if (_.isObject(spec)) { if (spec.type === 'wrap') { retobj.type = 'wrap'; if (!spec["var"]) { throw poly.error.defn("You didn't specify a variable to facet on."); } if (spec["var"]) { retobj.mapping["var"] = poly.parser.normalize(spec["var"]["var"]); } } else if (spec.type === 'grid') { retobj.type = 'grid'; if (!spec.x && spec.y) { throw poly.error.defn("You didn't specify a variable to facet on."); } if (spec.x) { retobj.mapping.x = poly.parser.normalize(spec.x["var"]); } if (spec.y) { retobj.mapping.y = poly.parser.normalize(spec.y["var"]); } } } return retobj; }; Facet.prototype._makeIndices = function(datas, groups) { var aes, data, grps, index, indexValues, indices, key, meta, name, sortfn, stringify, v, val, values, x, _i, _len, _ref; values = {}; for (aes in groups) { key = groups[aes]; name = poly.parser.normalize(key["var"]); if (key.levels) { values[name] = key.levels; } else { v = []; sortfn = null; for (index in datas) { data = datas[index]; if (meta = data.metaData[name]) { if (meta && ((_ref = meta.type) === 'num' || _ref === 'date')) { poly.type.compare(meta.type); } } v = _.uniq(_.union(v, _.pluck(data.statData, name))); } values[name] = sortfn != null ? v.sort(sortfn) : v; } } indexValues = poly.cross(values); indices = {}; grps = (function() { var _i, _len, _ref1, _results; _ref1 = _.pluck(groups, 'var'); _results = []; for (_i = 0, _len = _ref1.length; _i < _len; _i++) { x = _ref1[_i]; _results.push(poly.parser.normalize(x)); } return _results; })(); stringify = poly.stringify(grps); for (_i = 0, _len = indexValues.length; _i < _len; _i++) { val = indexValues[_i]; indices[stringify(val)] = val; } return { values: values, indices: indices }; }; Facet.prototype._groupData = function(unfaceted, indicies) { var datas, groupedData, id, mindex, pointer, value, _ref; groupedData = poly.groupProcessedData(unfaceted, this.groups); datas = {}; _ref = this.indices; for (id in _ref) { mindex = _ref[id]; pointer = groupedData; while (pointer.grouped === true) { value = mindex[pointer.key]; pointer = pointer.values[value]; } datas[id] = pointer; } return datas; }; Facet.prototype._getRowCol = function(id) { var retobj, value; retobj = { row: 0, col: 0 }; if (this.type === 'wrap') { value = this.indices[id][this.mapping["var"]]; id = _.indexOf(this.values[this.mapping["var"]], value); retobj.col = id % this.cols; retobj.row = Math.floor(id / this.cols); } else if (this.type === 'grid') { retobj.row = _.indexOf(this.values[this.mapping.y], this.indices[id][this.mapping.y]); retobj.col = _.indexOf(this.values[this.mapping.x], this.indices[id][this.mapping.x]); } return retobj; }; return Facet; })(); /* Take a processedData from the data processing step and group it for faceting purposes. Input is in the format: processData = { layer_id : { statData: [...], metaData: {...} } ... } Output should be in one of the two format: groupedData = { grouped: true key: group1 values: { value1: groupedData2 # note recursive def'n value2: groupedData3 ... } } OR groupedData = { layer_id : { statData: [...], metaData: {...} } ... } */ poly.groupProcessedData = function(processedData, groups) { var currGrp, data, index, newProcessedData, result, uniqueValues, value, _i, _len; if (groups.length === 0) { return processedData; } currGrp = groups.splice(0, 1)[0]; uniqueValues = []; for (index in processedData) { data = processedData[index]; if (currGrp in data.metaData) { uniqueValues = _.union(uniqueValues, _.uniq(_.pluck(data.statData, currGrp))); } } result = { grouped: true, key: currGrp, values: {} }; for (_i = 0, _len = uniqueValues.length; _i < _len; _i++) { value = uniqueValues[_i]; newProcessedData = {}; for (index in processedData) { data = processedData[index]; newProcessedData[index] = { metaData: data.metaData }; newProcessedData[index].statData = currGrp in data.metaData ? poly.filter(data.statData, currGrp, value) : _.clone(data.statData); } result.values[value] = poly.groupProcessedData(newProcessedData, _.clone(groups)); } return result; }; }).call(this); // Generated by CoffeeScript 1.6.2 (function() { var Pivot, PivotProcessedData, toStrictMode, __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; toStrictMode = function(spec) { var aes, i, key, mappedTo, val, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2, _ref3, _ref4, _ref5; _ref = ['row', 'column', 'value']; for (_i = 0, _len = _ref.length; _i < _len; _i++) { aes = _ref[_i]; if (!spec[aes + 's']) { spec[aes + 's'] = []; if (spec[aes] != null) { spec[aes + 's'].push(spec[aes]); } } } _ref1 = ['rows', 'columns', 'values']; for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { aes = _ref1[_j]; _ref2 = spec[aes]; for (i = _k = 0, _len2 = _ref2.length; _k < _len2; i = ++_k) { mappedTo = _ref2[i]; if (_.isString(mappedTo)) { spec[aes][i] = { "var": mappedTo }; } } } if ((_ref3 = spec.full) == null) { spec.full = false; } if ((_ref4 = spec.formatter) == null) { spec.formatter = {}; } _ref5 = spec.formatter; for (key in _ref5) { val = _ref5[key]; key = poly.parser.normalize(key); spec.formatter[key] = val; } return spec; }; PivotProcessedData = (function() { function PivotProcessedData(statData, ticks, spec) { var indexCols, indexRows, item, row, _i, _insertInto, _len, _ref; this.statData = statData; this.ticks = ticks; this.spec = spec; this.get = __bind(this.get, this); this.makeFormatters = __bind(this.makeFormatters, this); this.makeHeaders = __bind(this.makeHeaders, this); this.rows = (function() { var _i, _len, _ref, _results; _ref = this.spec.rows; _results = []; for (_i = 0, _len = _ref.length; _i < _len; _i++) { item = _ref[_i]; _results.push(poly.parser.unbracket(item["var"])); } return _results; }).call(this); this.columns = (function() { var _i, _len, _ref, _results; _ref = this.spec.columns; _results = []; for (_i = 0, _len = _ref.length; _i < _len; _i++) { item = _ref[_i]; _results.push(poly.parser.unbracket(item["var"])); } return _results; }).call(this); indexRows = this.rows.concat(this.columns); indexCols = this.columns; this.dataIndexByRows = {}; this.dataIndexByCols = {}; _insertInto = function(structure, keys, row) { var key, tmp, tmp_parent, _i, _len, _name, _ref; tmp = tmp_parent = structure; for (_i = 0, _len = keys.length; _i < _len; _i++) { key = keys[_i]; if ((_ref = tmp[_name = row[key]]) == null) { tmp[_name] = {}; } tmp_parent = tmp; tmp = tmp_parent[row[key]]; } return tmp_parent[row[key]] = row; }; _ref = this.statData; for (_i = 0, _len = _ref.length; _i < _len; _i++) { row = _ref[_i]; _insertInto(this.dataIndexByRows, indexRows, row); _insertInto(this.dataIndexByCols, indexCols, row); } } PivotProcessedData.prototype.makeHeaders = function() { var full, _recurse, _this = this; full = this.spec.full; _recurse = function(accumulator, indexValues, keys, item) { var key, restOfKeys, values; if (keys.length === 0) { return accumulator.push(indexValues); } else { key = keys[0]; restOfKeys = keys.slice(1); values = _.keys(item); return _.each(_this.ticks[key].ticks, function(tickValue, v) { var indexV; if (full || _.contains(values, "" + tickValue.location)) { indexV = _.clone(indexValues); indexV[key] = tickValue.value; return _recurse(accumulator, indexV, restOfKeys, item[tickValue.location]); } }); } }; this.rowHeaders = []; this.colHeaders = []; _recurse(this.rowHeaders, {}, this.rows, this.dataIndexByRows); _recurse(this.colHeaders, {}, this.columns, this.dataIndexByCols); return { rowHeaders: this.rowHeaders, colHeaders: this.colHeaders }; }; PivotProcessedData.prototype.makeFormatters = function() { var degree, exp, formatters, item, v, values, _i, _j, _len, _len1, _ref; values = (function() { var _i, _len, _ref, _results; _ref = this.spec.values; _results = []; for (_i = 0, _len = _ref.length; _i < _len; _i++) { item = _ref[_i]; _results.push(poly.parser.unbracket(item["var"])); } return _results; }).call(this); formatters = {}; for (_i = 0, _len = values.length; _i < _len; _i++) { v = values[_i]; formatters[v] = v in this.spec.formatter ? this.spec.formatter[v] : (exp = poly.format.getExp(_.min(_.pluck(this.statData, v))), degree = exp, poly.format.number(degree)); } _ref = this.columns.concat(this.rows); for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) { v = _ref[_j]; if (v in this.spec.formatter) { formatters[v] = this.spec.formatter[v]; } } return formatters; }; PivotProcessedData.prototype.get = function(rowMindex, colMindex, val) { var index, key, retvalue, _i, _j, _len, _len1, _ref, _ref1; retvalue = this.dataIndexByRows; _ref = this.rows; for (_i = 0, _len = _ref.length; _i < _len; _i++) { key = _ref[_i]; index = this.ticks[key].ticks[rowMindex[key]].location; if ((retvalue != null) && (retvalue[index] != null)) { retvalue = retvalue[index]; } } _ref1 = this.columns; for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { key = _ref1[_j]; index = this.ticks[key].ticks[colMindex[key]].location; if ((retvalue != null) && (retvalue[index] != null)) { retvalue = retvalue[index]; } } if ((retvalue != null) && (retvalue[val] != null)) { return retvalue[val]; } }; return PivotProcessedData; })(); Pivot = (function() { function Pivot(spec, callback, prepare) { this.callback = callback; this.prepare = prepare; this.render = __bind(this.render, this); this.generateTicks = __bind(this.generateTicks, this); if (spec == null) { throw poly.error.defn("No pivot table specification is passed in!"); } this.make(spec); } Pivot.prototype.make = function(spec) { var ps; this.spec = toStrictMode(spec); ps = new poly.DataProcess(this.spec, [], this.spec.strict, poly.spec.pivotToData); return ps.make(this.spec, [], this.render); }; Pivot.prototype.generateTicks = function(spec, statData, metaData) { var aes, bw, domain, guideSpec, item, key, meta, tick, ticks, values, _i, _j, _len, _len1, _ref, _ref1; ticks = {}; _ref = ['rows', 'columns']; for (_i = 0, _len = _ref.length; _i < _len; _i++) { aes = _ref[_i]; _ref1 = spec[aes]; for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { item = _ref1[_j]; key = poly.parser.unbracket(item["var"]); meta = metaData[key]; values = _.pluck(statData, key); domain = poly.domain.single(values, metaData[key], {}); guideSpec = meta.type === 'cat' ? { ticks: domain.levels } : meta.type === 'num' ? { numticks: (domain.max - domain.min) / meta.bw } : (bw = poly["const"].approxTimeInSeconds[meta.bw], { numticks: (domain.max - domain.min) / bw }); tick = poly.tick.make(domain, guideSpec, metaData[key].type); ticks[key] = tick; } } return ticks; }; Pivot.prototype.render = function(err, statData, metaData) { var cell, colHeaders, cols, cols_mindex, colspan, formatters, i, j, k, key, name, pivotData, pivotMeta, row, rowHeaders, rows, rows_mindex, rowspan, space, table, ticks, v, val, value, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2, _ref3; ticks = this.generateTicks(this.spec, statData, metaData); pivotData = new PivotProcessedData(statData, ticks, this.spec); _ref = pivotData.makeHeaders(), rowHeaders = _ref.rowHeaders, colHeaders = _ref.colHeaders; formatters = pivotData.makeFormatters(); pivotMeta = { ncol: this.spec.columns.length, nrow: this.spec.rows.length, nval: this.spec.values.length }; if (!$) { throw poly.error.depn("Pivot Tables require jQuery!"); } table = $('