From 2f6306dae337b0836b0e3c9396d3ae05e09d8c65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20L=C3=B3pez?= Date: Thu, 16 Feb 2017 18:18:10 +0100 Subject: [PATCH] Release 2.16.2 --- NEWS | 2 +- test/suite-bundle.js | 25921 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 25922 insertions(+), 1 deletion(-) create mode 100644 test/suite-bundle.js diff --git a/NEWS b/NEWS index ecd71a9..279a0d9 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,5 @@ 2.16.2 - - + - Re-publish version 2.16.1 - Remove width trick to use clear rect #280 diff --git a/test/suite-bundle.js b/test/suite-bundle.js new file mode 100644 index 0000000..97437ef --- /dev/null +++ b/test/suite-bundle.js @@ -0,0 +1,25921 @@ +(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o _) { + this.time(0); + } + this.rescale(); + return this; + }, + + steps: function(_) { + this.options.steps = _; + return this.rescale(); + }, + + step: function(s) { + if(arguments.length === 0) return this.range(this.domain(this._time)); + this._time = this.domainInv(this.rangeInv(s)); + }, + + pause: function() { + this.running = false; + cancelAnimationFrame(this._tick); + this.options.onPause && this.options.onPause(); + }, + + _tick: function() { + var t1 = +new Date(); + var delta = (t1 - this._t0)*0.001; + // if delta is really big means the tab lost the focus + // at some point, so limit delta change + delta = Math.min(this.options.maxDelta, delta); + this._t0 = t1; + this._time += delta; + if(this.step() >= this.options.steps) { + if(!this.options.loop){ + // set time to max time + this.time(this.options.animationDuration); + this.pause(); + } else { + this._time = 0; + } + } + if(this.running) { + this.time(this._time); + requestAnimationFrame(this._tick); + } + } + + }; + +module.exports = Animator; + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"./":10}],2:[function(require,module,exports){ +var _torque_reference_latest = { + "version": "1.0.0", + "style": { + "comp-op": { + "css": "comp-op", + "default-value": "src-over", + "default-meaning": "add the current layer on top of other layers", + "doc": "Composite operation. This defines how this layer should behave relative to layers atop or below it.", + "type": [ + "src", // + "src-over", // + "dst-over", // + "src-in", // + "dst-in", // + "src-out", // + "dst-out", // + "src-atop", // + "dst-atop", // + "xor", // + "darken", // + "lighten" // + ] + } + }, + "layer" : { + "buffer-size": { + "default-value": "0", + "type":"float", + "default-meaning": "No buffer will be used", + "doc": "Extra tolerance around the Layer extent (in pixels) used to when querying and (potentially) clipping the layer data during rendering" + }, + "-torque-clear-color": { + "css": "-torque-clear-color", + "type": "color", + "default-value": "rgba(255, 255, 255, 0)", + "default-meaning": "full clear", + "doc": "color used to clear canvas on each frame" + }, + "-torque-frame-count": { + "css": "-torque-frame-count", + "default-value": "128", + "type":"number", + "default-meaning": "the data is broken into 128 time frames", + "doc": "Number of animation steps/frames used in the animation. If the data contains a fewere number of total frames, the lesser value will be used." + }, + "-torque-resolution": { + "css": "-torque-resolution", + "default-value": "2", + "type":"number", + "default-meaning": "", + "doc": "Spatial resolution in pixels. A resolution of 1 means no spatial aggregation of the data. Any other resolution of N results in spatial aggregation into cells of NxN pixels. The value N must be power of 2" + }, + "-torque-animation-duration": { + "css": "-torque-animation-duration", + "default-value": "30", + "type":"number", + "default-meaning": "the animation lasts 30 seconds", + "doc": "Animation duration in seconds" + }, + "-torque-aggregation-function": { + "css": "-torque-aggregation-function", + "default-value": "count(cartodb_id)", + "type": "string", + "default-meaning": "the value for each cell is the count of points in that cell", + "doc": "A function used to calculate a value from the aggregate data for each cell. See -torque-resolution" + }, + "-torque-time-attribute": { + "css": "-torque-time-attribute", + "default-value": "time", + "type": "string", + "default-meaning": "the data column in your table that is of a time based type", + "doc": "The table column that contains the time information used create the animation" + }, + "-torque-data-aggregation": { + "css": "-torque-data-aggregation", + "default-value": "linear", + "type": [ + "cumulative" + ], + "default-meaning": "previous values are discarded", + "doc": "A linear animation will discard previous values while a cumulative animation will accumulate them until it restarts" + } + }, + "symbolizers" : { + "*": { + "comp-op": { + "css": "comp-op", + "default-value": "src-over", + "default-meaning": "add the current layer on top of other layers", + "doc": "Composite operation. This defines how this layer should behave relative to layers atop or below it.", + "type": [ + "src", // + "src-over", // + "dst-over", // + "src-in", // + "dst-in", // + "src-out", // + "dst-out", // + "src-atop", // + "dst-atop", // + "xor", // + "darken", // + "lighten" // + ] + }, + "opacity": { + "css": "opacity", + "type": "float", + "doc": "An alpha value for the style (which means an alpha applied to all features in separate buffer and then composited back to main buffer)", + "default-value": 1, + "default-meaning": "no separate buffer will be used and no alpha will be applied to the style after rendering" + } + }, + "trail": { + "steps": { + "css": "trail-steps", + "type": "float", + "default-value": 1, + "default-meaning": "no trail steps", + "doc": "How many steps of trails are going to be rendered" + } + }, + "polygon": { + "fill": { + "css": "polygon-fill", + "type": "color", + "default-value": "rgba(128,128,128,1)", + "default-meaning": "gray and fully opaque (alpha = 1), same as rgb(128,128,128)", + "doc": "Fill color to assign to a polygon" + }, + "fill-opacity": { + "css": "polygon-opacity", + "type": "float", + "doc": "The opacity of the polygon", + "default-value": 1, + "default-meaning": "opaque" + } + }, + "line": { + "stroke": { + "css": "line-color", + "default-value": "rgba(0,0,0,1)", + "type": "color", + "default-meaning": "black and fully opaque (alpha = 1), same as rgb(0,0,0)", + "doc": "The color of a drawn line" + }, + "stroke-width": { + "css": "line-width", + "default-value": 1, + "type": "float", + "doc": "The width of a line in pixels" + }, + "stroke-opacity": { + "css": "line-opacity", + "default-value": 1, + "type": "float", + "default-meaning": "opaque", + "doc": "The opacity of a line" + }, + "stroke-linejoin": { + "css": "line-join", + "default-value": "miter", + "type": [ + "miter", + "round", + "bevel" + ], + "doc": "The behavior of lines when joining" + }, + "stroke-linecap": { + "css": "line-cap", + "default-value": "butt", + "type": [ + "butt", + "round", + "square" + ], + "doc": "The display of line endings" + } + }, + "markers": { + "file": { + "css": "marker-file", + "doc": "An SVG file that this marker shows at each placement. If no file is given, the marker will show an ellipse.", + "default-value": "", + "default-meaning": "An ellipse or circle, if width equals height", + "type": "uri" + }, + "opacity": { + "css": "marker-opacity", + "doc": "The overall opacity of the marker, if set, overrides both the opacity of both the fill and stroke", + "default-value": 1, + "default-meaning": "The stroke-opacity and fill-opacity will be used", + "type": "float" + }, + "fill-opacity": { + "css": "marker-fill-opacity", + "doc": "The fill opacity of the marker", + "default-value": 1, + "default-meaning": "opaque", + "type": "float" + }, + "stroke": { + "css": "marker-line-color", + "doc": "The color of the stroke around a marker shape.", + "default-value": "black", + "type": "color" + }, + "stroke-width": { + "css": "marker-line-width", + "doc": "The width of the stroke around a marker shape, in pixels. This is positioned on the boundary, so high values can cover the area itself.", + "type": "float" + }, + "stroke-opacity": { + "css": "marker-line-opacity", + "default-value": 1, + "default-meaning": "opaque", + "doc": "The opacity of a line", + "type": "float" + }, + "fill": { + "css": "marker-fill", + "default-value": "blue", + "doc": "The color of the area of the marker.", + "type": "color" + }, + "marker-type": { + "css": "marker-type", + "type": [ + "rectangle", + "ellipse" + ], + "default-value": "ellipse", + "doc": "The default marker-type. If a SVG file is not given as the marker-file parameter, the renderer provides either an rectangle or an ellipse (a circle if height is equal to width)" + }, + "width": { + "css": "marker-width", + "default-value": 10, + "doc": "The width of the marker, if using one of the default types.", + "type": "float" + } + }, + "point": { + "file": { + "css": "point-file", + "type": "uri", + "required": false, + "default-value": "none", + "doc": "Image file to represent a point" + }, + "opacity": { + "css": "point-opacity", + "type": "float", + "default-value": 1.0, + "default-meaning": "Fully opaque", + "doc": "A value from 0 to 1 to control the opacity of the point" + } + } + }, + "colors": { + "aliceblue": [240, 248, 255], + "antiquewhite": [250, 235, 215], + "aqua": [0, 255, 255], + "aquamarine": [127, 255, 212], + "azure": [240, 255, 255], + "beige": [245, 245, 220], + "bisque": [255, 228, 196], + "black": [0, 0, 0], + "blanchedalmond": [255,235,205], + "blue": [0, 0, 255], + "blueviolet": [138, 43, 226], + "brown": [165, 42, 42], + "burlywood": [222, 184, 135], + "cadetblue": [95, 158, 160], + "chartreuse": [127, 255, 0], + "chocolate": [210, 105, 30], + "coral": [255, 127, 80], + "cornflowerblue": [100, 149, 237], + "cornsilk": [255, 248, 220], + "crimson": [220, 20, 60], + "cyan": [0, 255, 255], + "darkblue": [0, 0, 139], + "darkcyan": [0, 139, 139], + "darkgoldenrod": [184, 134, 11], + "darkgray": [169, 169, 169], + "darkgreen": [0, 100, 0], + "darkgrey": [169, 169, 169], + "darkkhaki": [189, 183, 107], + "darkmagenta": [139, 0, 139], + "darkolivegreen": [85, 107, 47], + "darkorange": [255, 140, 0], + "darkorchid": [153, 50, 204], + "darkred": [139, 0, 0], + "darksalmon": [233, 150, 122], + "darkseagreen": [143, 188, 143], + "darkslateblue": [72, 61, 139], + "darkslategrey": [47, 79, 79], + "darkturquoise": [0, 206, 209], + "darkviolet": [148, 0, 211], + "deeppink": [255, 20, 147], + "deepskyblue": [0, 191, 255], + "dimgray": [105, 105, 105], + "dimgrey": [105, 105, 105], + "dodgerblue": [30, 144, 255], + "firebrick": [178, 34, 34], + "floralwhite": [255, 250, 240], + "forestgreen": [34, 139, 34], + "fuchsia": [255, 0, 255], + "gainsboro": [220, 220, 220], + "ghostwhite": [248, 248, 255], + "gold": [255, 215, 0], + "goldenrod": [218, 165, 32], + "gray": [128, 128, 128], + "grey": [128, 128, 128], + "green": [0, 128, 0], + "greenyellow": [173, 255, 47], + "honeydew": [240, 255, 240], + "hotpink": [255, 105, 180], + "indianred": [205, 92, 92], + "indigo": [75, 0, 130], + "ivory": [255, 255, 240], + "khaki": [240, 230, 140], + "lavender": [230, 230, 250], + "lavenderblush": [255, 240, 245], + "lawngreen": [124, 252, 0], + "lemonchiffon": [255, 250, 205], + "lightblue": [173, 216, 230], + "lightcoral": [240, 128, 128], + "lightcyan": [224, 255, 255], + "lightgoldenrodyellow": [250, 250, 210], + "lightgray": [211, 211, 211], + "lightgreen": [144, 238, 144], + "lightgrey": [211, 211, 211], + "lightpink": [255, 182, 193], + "lightsalmon": [255, 160, 122], + "lightseagreen": [32, 178, 170], + "lightskyblue": [135, 206, 250], + "lightslategray": [119, 136, 153], + "lightslategrey": [119, 136, 153], + "lightsteelblue": [176, 196, 222], + "lightyellow": [255, 255, 224], + "lime": [0, 255, 0], + "limegreen": [50, 205, 50], + "linen": [250, 240, 230], + "magenta": [255, 0, 255], + "maroon": [128, 0, 0], + "mediumaquamarine": [102, 205, 170], + "mediumblue": [0, 0, 205], + "mediumorchid": [186, 85, 211], + "mediumpurple": [147, 112, 219], + "mediumseagreen": [60, 179, 113], + "mediumslateblue": [123, 104, 238], + "mediumspringgreen": [0, 250, 154], + "mediumturquoise": [72, 209, 204], + "mediumvioletred": [199, 21, 133], + "midnightblue": [25, 25, 112], + "mintcream": [245, 255, 250], + "mistyrose": [255, 228, 225], + "moccasin": [255, 228, 181], + "navajowhite": [255, 222, 173], + "navy": [0, 0, 128], + "oldlace": [253, 245, 230], + "olive": [128, 128, 0], + "olivedrab": [107, 142, 35], + "orange": [255, 165, 0], + "orangered": [255, 69, 0], + "orchid": [218, 112, 214], + "palegoldenrod": [238, 232, 170], + "palegreen": [152, 251, 152], + "paleturquoise": [175, 238, 238], + "palevioletred": [219, 112, 147], + "papayawhip": [255, 239, 213], + "peachpuff": [255, 218, 185], + "peru": [205, 133, 63], + "pink": [255, 192, 203], + "plum": [221, 160, 221], + "powderblue": [176, 224, 230], + "purple": [128, 0, 128], + "red": [255, 0, 0], + "rosybrown": [188, 143, 143], + "royalblue": [65, 105, 225], + "saddlebrown": [139, 69, 19], + "salmon": [250, 128, 114], + "sandybrown": [244, 164, 96], + "seagreen": [46, 139, 87], + "seashell": [255, 245, 238], + "sienna": [160, 82, 45], + "silver": [192, 192, 192], + "skyblue": [135, 206, 235], + "slateblue": [106, 90, 205], + "slategray": [112, 128, 144], + "slategrey": [112, 128, 144], + "snow": [255, 250, 250], + "springgreen": [0, 255, 127], + "steelblue": [70, 130, 180], + "tan": [210, 180, 140], + "teal": [0, 128, 128], + "thistle": [216, 191, 216], + "tomato": [255, 99, 71], + "turquoise": [64, 224, 208], + "violet": [238, 130, 238], + "wheat": [245, 222, 179], + "white": [255, 255, 255], + "whitesmoke": [245, 245, 245], + "yellow": [255, 255, 0], + "yellowgreen": [154, 205, 50], + "transparent": [0, 0, 0, 0] + } +}; + +module.exports = { + version: { + latest: _torque_reference_latest, + '1.0.0': _torque_reference_latest + } +}; + +},{}],3:[function(require,module,exports){ +(function (global){ +// +// common functionallity for torque layers +// +var carto = global.carto || require('carto'); + +function TorqueLayer() {} + +TorqueLayer.prototype = { +}; + +TorqueLayer.optionsFromLayer = function(mapConfig) { + var opts = {}; + if (!mapConfig) return opts; + var attrs = { + 'buffer-size': 'buffer-size', + '-torque-frame-count': 'steps', + '-torque-resolution': 'resolution', + '-torque-animation-duration': 'animationDuration', + '-torque-aggregation-function': 'countby', + '-torque-time-attribute': 'column', + '-torque-data-aggregation': 'data_aggregation' + }; + for (var i in attrs) { + var v = mapConfig.eval(i); + if (v !== undefined) { + var a = attrs[i]; + opts[a] = v; + } + } + return opts; +}; + +TorqueLayer.optionsFromCartoCSS = function(cartocss) { + var shader = new carto.RendererJS().render(cartocss); + var mapConfig = shader.findLayer({ name: 'Map' }); + return TorqueLayer.optionsFromLayer(mapConfig); +}; + +module.exports.TorqueLayer = TorqueLayer; + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"carto":36}],4:[function(require,module,exports){ +(function (global){ + var Event = {}; + Event.on = function(evt, callback) { + var cb = this._evt_callbacks = this._evt_callbacks || {}; + var l = cb[evt] || (cb[evt] = []); + l.push(callback); + return this; + }; + + Event.trigger = function(evt) { + var c = this._evt_callbacks && this._evt_callbacks[evt]; + for(var i = 0; c && i < c.length; ++i) { + c[i].apply(this, Array.prototype.slice.call(arguments, 1)); + } + return this; + }; + + Event.fire = Event.trigger; + + Event.off = function (evt, callback) { + var c = this._evt_callbacks && this._evt_callbacks[evt]; + if (c && !callback) { + delete this._evt_callbacks[evt]; + return this; + } + var remove = []; + for(var i = 0; c && i < c.length; ++i) { + if(c[i] === callback) remove.push(i); + } + while((i = remove.pop()) !== undefined) c.splice(i, 1); + return this; + }; + + Event.callbacks = function(evt) { + return (this._evt_callbacks && this._evt_callbacks[evt]) || []; + }; + + function extend() { + var objs = arguments; + var a = objs[0]; + for (var i = 1; i < objs.length; ++i) { + var b = objs[i]; + for (var k in b) { + a[k] = b[k]; + } + } + return a; + } + + function clone(a) { + return extend({}, a); + } + + function isFunction(f) { + return typeof f == 'function' || false; + } + + function isArray(value) { + return value && typeof value == 'object' && Object.prototype.toString.call(value) == '[object Array]'; + } + + // types + var types = { + Uint8Array: typeof(global['Uint8Array']) !== 'undefined' ? global.Uint8Array : Array, + Uint8ClampedArray: typeof(global['Uint8ClampedArray']) !== 'undefined' ? global.Uint8ClampedArray: Array, + Uint32Array: typeof(global['Uint32Array']) !== 'undefined' ? global.Uint32Array : Array, + Int16Array: typeof(global['Int16Array']) !== 'undefined' ? global.Int16Array : Array, + Int32Array: typeof(global['Int32Array']) !== 'undefined' ? global.Int32Array: Array + }; + + function isBrowserSupported() { + return !!document.createElement('canvas'); + } + + function userAgent() { + return typeof navigator !== 'undefined' ? navigator.userAgent : ''; + } + + var flags = { + sprites_to_images: userAgent().indexOf('Safari') === -1 && userAgent().indexOf('Firefox') === -1 + }; + +module.exports = { + Event: Event, + extend: extend, + clone: clone, + isFunction: isFunction, + isArray: isArray, + types: types, + isBrowserSupported: isBrowserSupported, + flags: flags +}; + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{}],5:[function(require,module,exports){ +/** + * @license + * Copyright 2013 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @fileoverview Extends OverlayView to provide a canvas "Layer". + * @author Brendan Kenny + */ + +/** + * A map layer that provides a canvas over the slippy map and a callback + * system for efficient animation. Requires canvas and CSS 2D transform + * support. + * @constructor + * @extends google.maps.OverlayView + * @param {CanvasLayerOptions=} opt_options Options to set in this CanvasLayer. + */ + +function CanvasLayer(opt_options) { + /** + * If true, canvas is in a map pane and the OverlayView is fully functional. + * See google.maps.OverlayView.onAdd for more information. + * @type {boolean} + * @private + */ + this.isAdded_ = false; + + /** + * If true, each update will immediately schedule the next. + * @type {boolean} + * @private + */ + this.isAnimated_ = false; + + /** + * The name of the MapPane in which this layer will be displayed. + * @type {string} + * @private + */ + this.paneName_ = CanvasLayer.DEFAULT_PANE_NAME_; + + /** + * A user-supplied function called whenever an update is required. Null or + * undefined if a callback is not provided. + * @type {?function=} + * @private + */ + this.updateHandler_ = null; + + /** + * A user-supplied function called whenever an update is required and the + * map has been resized since the last update. Null or undefined if a + * callback is not provided. + * @type {?function} + * @private + */ + this.resizeHandler_ = null; + + /** + * The LatLng coordinate of the top left of the current view of the map. Will + * be null when this.isAdded_ is false. + * @type {google.maps.LatLng} + * @private + */ + this.topLeft_ = null; + + /** + * The map-pan event listener. Will be null when this.isAdded_ is false. Will + * be null when this.isAdded_ is false. + * @type {?function} + * @private + */ + this.centerListener_ = null; + + /** + * The map-resize event listener. Will be null when this.isAdded_ is false. + * @type {?function} + * @private + */ + this.resizeListener_ = null; + + /** + * If true, the map size has changed and this.resizeHandler_ must be called + * on the next update. + * @type {boolean} + * @private + */ + this.needsResize_ = true; + + /** + * A browser-defined id for the currently requested callback. Null when no + * callback is queued. + * @type {?number} + * @private + */ + this.requestAnimationFrameId_ = null; + + var canvas = document.createElement('canvas'); + canvas.style.position = 'absolute'; + canvas.style.top = 0; + canvas.style.left = 0; + canvas.style.pointerEvents = 'none'; + + /** + * The canvas element. + * @type {!HTMLCanvasElement} + */ + this.canvas = canvas; + + /** + * Simple bind for functions with no args for bind-less browsers (Safari). + * @param {Object} thisArg The this value used for the target function. + * @param {function} func The function to be bound. + */ + function simpleBindShim(thisArg, func) { + return function() { func.apply(thisArg); }; + } + + /** + * A reference to this.repositionCanvas_ with this bound as its this value. + * @type {function} + * @private + */ + this.repositionFunction_ = simpleBindShim(this, this.repositionCanvas_); + + /** + * A reference to this.resize_ with this bound as its this value. + * @type {function} + * @private + */ + this.resizeFunction_ = simpleBindShim(this, this.resize_); + + /** + * A reference to this.update_ with this bound as its this value. + * @type {function} + * @private + */ + this.requestUpdateFunction_ = simpleBindShim(this, this.update_); + + // set provided options, if any + if (opt_options) { + this.setOptions(opt_options); + } +} + +CanvasLayer.prototype = new google.maps.OverlayView(); + +/** + * The default MapPane to contain the canvas. + * @type {string} + * @const + * @private + */ +CanvasLayer.DEFAULT_PANE_NAME_ = 'overlayLayer'; + +/** + * Transform CSS property name, with vendor prefix if required. If browser + * does not support transforms, property will be ignored. + * @type {string} + * @const + * @private + */ +CanvasLayer.CSS_TRANSFORM_ = (function() { + var div = document.createElement('div'); + var transformProps = [ + 'transform', + 'WebkitTransform', + 'MozTransform', + 'OTransform', + 'msTransform' + ]; + for (var i = 0; i < transformProps.length; i++) { + var prop = transformProps[i]; + if (div.style[prop] !== undefined) { + return prop; + } + } + + // return unprefixed version by default + return transformProps[0]; +})(); + +/** + * The requestAnimationFrame function, with vendor-prefixed or setTimeout-based + * fallbacks. MUST be called with window as thisArg. + * @type {function} + * @param {function} callback The function to add to the frame request queue. + * @return {number} The browser-defined id for the requested callback. + * @private + */ +CanvasLayer.prototype.requestAnimFrame_ = + window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || + window.msRequestAnimationFrame || + function(callback) { + return window.setTimeout(callback, 1000 / 60); + }; + +/** + * The cancelAnimationFrame function, with vendor-prefixed fallback. Does not + * fall back to clearTimeout as some platforms implement requestAnimationFrame + * but not cancelAnimationFrame, and the cost is an extra frame on onRemove. + * MUST be called with window as thisArg. + * @type {function} + * @param {number=} requestId The id of the frame request to cancel. + * @private + */ +CanvasLayer.prototype.cancelAnimFrame_ = + window.cancelAnimationFrame || + window.webkitCancelAnimationFrame || + window.mozCancelAnimationFrame || + window.oCancelAnimationFrame || + window.msCancelAnimationFrame || + function(requestId) {}; + +/** + * Sets any options provided. See CanvasLayerOptions for more information. + * @param {CanvasLayerOptions} options The options to set. + */ +CanvasLayer.prototype.setOptions = function(options) { + if (options.animate !== undefined) { + this.setAnimate(options.animate); + } + + if (options.paneName !== undefined) { + this.setPane(options.paneName); + } + + if (options.updateHandler !== undefined) { + this.setUpdateHandler(options.updateHandler); + } + + if (options.resizeHandler !== undefined) { + this.setResizeHandler(options.resizeHandler); + } + + if(options.readyHandler) { + this.readyHandler = options.readyHandler; + } + +}; + +/** + * Set the animated state of the layer. If true, updateHandler will be called + * repeatedly, once per frame. If false, updateHandler will only be called when + * a map property changes that could require the canvas content to be redrawn. + * @param {boolean} animate Whether the canvas is animated. + */ +CanvasLayer.prototype.setAnimate = function(animate) { + this.isAnimated_ = !!animate; + + if (this.isAnimated_) { + this.scheduleUpdate(); + } +}; + +/** + * @return {boolean} Whether the canvas is animated. + */ +CanvasLayer.prototype.isAnimated = function() { + return this.isAnimated_; +}; + +/** + * Set the MapPane in which this layer will be displayed, by name. See + * {@code google.maps.MapPanes} for the panes available. + * @param {string} paneName The name of the desired MapPane. + */ +CanvasLayer.prototype.setPaneName = function(paneName) { + this.paneName_ = paneName; + + this.setPane_(); +}; + +/** + * Set the opacity for the canvas. + * + * @param {number} opacity The opacity of the canvas + */ +CanvasLayer.prototype.setOpacity = function (opacity) { + this.canvas.style.opacity = opacity; +}; + +/** + * Get the canvases opacity. + * + * @return {number} The opacity of the canvas + */ +CanvasLayer.prototype.getOpacity = function () { + return this.canvas.style.opacity; +}; + +/** + * @return {string} The name of the current container pane. + */ +CanvasLayer.prototype.getPaneName = function() { + return this.paneName_; +}; + +/** + * Adds the canvas to the specified container pane. Since this is guaranteed to + * execute only after onAdd is called, this is when paneName's existence is + * checked (and an error is thrown if it doesn't exist). + * @private + */ +CanvasLayer.prototype.setPane_ = function() { + if (!this.isAdded_) { + return; + } + + // onAdd has been called, so panes can be used + var panes = this.getPanes(); + if (!panes[this.paneName_]) { + throw new Error('"' + this.paneName_ + '" is not a valid MapPane name.'); + } + + panes[this.paneName_].appendChild(this.canvas); +}; + +/** + * Set a function that will be called whenever the parent map and the overlay's + * canvas have been resized. If opt_resizeHandler is null or unspecified, any + * existing callback is removed. + * @param {?function=} opt_resizeHandler The resize callback function. + */ +CanvasLayer.prototype.setResizeHandler = function(opt_resizeHandler) { + this.resizeHandler_ = opt_resizeHandler; +}; + +/** + * Set a function that will be called when a repaint of the canvas is required. + * If opt_updateHandler is null or unspecified, any existing callback is + * removed. + * @param {?function=} opt_updateHandler The update callback function. + */ +CanvasLayer.prototype.setUpdateHandler = function(opt_updateHandler) { + this.updateHandler_ = opt_updateHandler; +}; + +/** + * @inheritDoc + */ +CanvasLayer.prototype.onAdd = function() { + if (this.isAdded_) { + return; + } + + this.isAdded_ = true; + this.setPane_(); + + this.resizeListener_ = google.maps.event.addListener(this.getMap(), + 'resize', this.resizeFunction_); + this.centerListener_ = google.maps.event.addListener(this.getMap(), + 'center_changed', this.repositionFunction_); + + this.resize_(); + this.repositionCanvas_(); + this.readyHandler && this.readyHandler(); +}; + +/** + * @inheritDoc + */ +CanvasLayer.prototype.onRemove = function() { + if (!this.isAdded_) { + return; + } + + this.isAdded_ = false; + this.topLeft_ = null; + + // remove canvas and listeners for pan and resize from map + this.canvas.parentElement.removeChild(this.canvas); + if (this.centerListener_) { + google.maps.event.removeListener(this.centerListener_); + this.centerListener_ = null; + } + if (this.resizeListener_) { + google.maps.event.removeListener(this.resizeListener_); + this.resizeListener_ = null; + } + + // cease canvas update callbacks + if (this.requestAnimationFrameId_) { + this.cancelAnimFrame_.call(window, this.requestAnimationFrameId_); + this.requestAnimationFrameId_ = null; + } +}; + +/** + * The internal callback for resize events that resizes the canvas to keep the + * map properly covered. + * @private + */ +CanvasLayer.prototype.resize_ = function() { + // TODO(bckenny): it's common to use a smaller canvas but use CSS to scale + // what is drawn by the browser to save on fill rate. Add an option to do + // this. + + if (!this.isAdded_) { + return; + } + + var map = this.getMap(); + var width = map.getDiv().offsetWidth; + var height = map.getDiv().offsetHeight; + var oldWidth = this.canvas.width; + var oldHeight = this.canvas.height; + + // resizing may allocate a new back buffer, so do so conservatively + if (oldWidth !== width || oldHeight !== height) { + this.canvas.width = width; + this.canvas.height = height; + this.canvas.style.width = width + 'px'; + this.canvas.style.height = height + 'px'; + + this.needsResize_ = true; + this.scheduleUpdate(); + } +}; + +/** + * @inheritDoc + */ +CanvasLayer.prototype.draw = function() { + this.repositionCanvas_(); +}; + +/** + * Internal callback for map view changes. Since the Maps API moves the overlay + * along with the map, this function calculates the opposite translation to + * keep the canvas in place. + * @private + */ +CanvasLayer.prototype.repositionCanvas_ = function() { + // TODO(bckenny): *should* only be executed on RAF, but in current browsers + // this causes noticeable hitches in map and overlay relative + // positioning. + + var bounds = this.getMap().getBounds(); + this.topLeft_ = new google.maps.LatLng(bounds.getNorthEast().lat(), + bounds.getSouthWest().lng()); + + // canvas position relative to draggable map's conatainer depends on + // overlayView's projection, not the map's + var projection = this.getProjection(); + var divTopLeft = projection.fromLatLngToDivPixel(this.topLeft_); + + // when the zoom level is low, more than one map can be shown in the screen + // so the canvas should be attach to the map with more are in the screen + var mapSize = (1 << this.getMap().getZoom())*256; + if (Math.abs(divTopLeft.x) > mapSize) { + divTopLeft.x -= mapSize; + } + this.canvas.style[CanvasLayer.CSS_TRANSFORM_] = 'translate(' + + Math.round(divTopLeft.x) + 'px,' + Math.round(divTopLeft.y) + 'px)'; + + this.scheduleUpdate(); +}; + +/** + * Internal callback that serves as main animation scheduler via + * requestAnimationFrame. Calls resize and update callbacks if set, and + * schedules the next frame if overlay is animated. + * @private + */ +CanvasLayer.prototype.update_ = function() { + this.requestAnimationFrameId_ = null; + + if (!this.isAdded_) { + return; + } + + if (this.isAnimated_) { + this.scheduleUpdate(); + } + + if (this.needsResize_ && this.resizeHandler_) { + this.needsResize_ = false; + this.resizeHandler_(); + } + + if (this.updateHandler_) { + this.updateHandler_(); + } +}; + +/** + * A convenience method to get the current LatLng coordinate of the top left of + * the current view of the map. + * @return {google.maps.LatLng} The top left coordinate. + */ +CanvasLayer.prototype.getTopLeft = function() { + return this.topLeft_; +}; + +/** + * Schedule a requestAnimationFrame callback to updateHandler. If one is + * already scheduled, there is no effect. + */ +CanvasLayer.prototype.scheduleUpdate = function() { + if (this.isAdded_ && !this.requestAnimationFrameId_) { + this.requestAnimationFrameId_ = + this.requestAnimFrame_.call(window, this.requestUpdateFunction_); + } +}; + +module.exports = CanvasLayer; + +},{}],6:[function(require,module,exports){ +/* + ==================== + canvas setup for drawing tiles + ==================== + */ + +function CanvasTileLayer(canvas_setup, render) { + this.tileSize = new google.maps.Size(256, 256); + this.maxZoom = 19; + this.name = "Tile #s"; + this.alt = "Canvas tile layer"; + this.tiles = {}; + this.canvas_setup = canvas_setup; + this.render = render; + if (!render) { + this.render = canvas_setup; + } +} + + +// create a tile with a canvas element +CanvasTileLayer.prototype.create_tile_canvas = function (coord, zoom, ownerDocument) { + + // create canvas and reset style + var canvas = ownerDocument.createElement('canvas'); + var hit_canvas = ownerDocument.createElement('canvas'); + canvas.style.border = hit_canvas.style.border = "none"; + canvas.style.margin = hit_canvas.style.margin = "0"; + canvas.style.padding = hit_canvas.style.padding = "0"; + + // prepare canvas and context sizes + var ctx = canvas.getContext('2d'); + ctx.width = canvas.width = this.tileSize.width; + ctx.height = canvas.height = this.tileSize.height; + + var hit_ctx = hit_canvas.getContext('2d'); + hit_canvas.width = hit_ctx.width = this.tileSize.width; + hit_canvas.height = hit_ctx.height = this.tileSize.height; + + //set unique id + var tile_id = coord.x + '_' + coord.y + '_' + zoom; + + canvas.setAttribute('id', tile_id); + hit_canvas.setAttribute('id', tile_id); + + if (tile_id in this.tiles) + delete this.tiles[tile_id]; + + this.tiles[tile_id] = {canvas:canvas, ctx:ctx, hit_canvas:hit_canvas, hit_ctx:hit_ctx, coord:coord, zoom:zoom, primitives:null}; + + // custom setup + //if (tile_id == '19295_24654_16'){ + if (this.canvas_setup) + this.canvas_setup(this.tiles[tile_id], coord, zoom); + //} + return canvas; + +} + + +CanvasTileLayer.prototype.each = function (callback) { + for (var t in this.tiles) { + var tile = this.tiles[t]; + callback(tile); + } +} + +CanvasTileLayer.prototype.recreate = function () { + for (var t in this.tiles) { + var tile = this.tiles[t]; + this.canvas_setup(tile, tile.coord, tile.zoom); + } +}; + +CanvasTileLayer.prototype.redraw_tile = function (tile) { + this.render(tile, tile.coord, tile.zoom); +}; + +CanvasTileLayer.prototype.redraw = function () { + for (var t in this.tiles) { + var tile = this.tiles[t]; + this.render(tile, tile.coord, tile.zoom); + } +}; + +// could be called directly... +CanvasTileLayer.prototype.getTile = function (coord, zoom, ownerDocument) { + return this.create_tile_canvas(coord, zoom, ownerDocument); +}; + +CanvasTileLayer.prototype.releaseTile = function (tile) { + var id = tile.getAttribute('id'); + delete this.tiles[id]; +}; + +module.exports = CanvasTileLayer; + +},{}],7:[function(require,module,exports){ +function GMapsTileLoader() { +} + + +GMapsTileLoader.prototype = { + + _initTileLoader: function(map, projection) { + this._map = map; + this._projection = projection; + this._tiles = {}; + this._tilesLoading = {}; + this._tilesToLoad = 0; + this._updateTiles = this._updateTiles.bind(this); + this._listeners = []; + this._listeners.push( + google.maps.event.addListener(this._map, 'dragend', this._updateTiles), + google.maps.event.addListener(this._map, 'zoom_changed', this._updateTiles) + ); + this.tileSize = 256; + this._updateTiles(); + }, + + _removeTileLoader: function() { + this._listeners.forEach(function (listener) { + google.maps.event.removeListener(listener); + }); + + this._removeTiles(); + }, + + _removeTiles: function () { + for (var key in this._tiles) { + this._removeTile(key); + } + }, + + _reloadTiles: function() { + this._removeTiles(); + this._updateTiles(); + }, + + _updateTiles: function () { + + if (!this._map) { return; } + + var bounds = this._map.getBounds(); + var zoom = this._map.getZoom(); + var tileSize = this.tileSize; + var mzoom = (1 << zoom); + + var topLeft = new google.maps.LatLng( + bounds.getNorthEast().lat(), + bounds.getSouthWest().lng() + ); + + var bottomRigth = new google.maps.LatLng( + bounds.getSouthWest().lat(), + bounds.getNorthEast().lng() + ); + + + this._projection = this._map.getProjection(); + var divTopLeft = this._projection.fromLatLngToPoint(topLeft); + var divBottomRight = this._projection.fromLatLngToPoint(bottomRigth); + + + var nwTilePoint = new google.maps.Point( + Math.floor(divTopLeft.x*mzoom / tileSize), + Math.floor(divTopLeft.y*mzoom / tileSize)), + seTilePoint = new google.maps.Point( + Math.floor(divBottomRight.x*mzoom / tileSize), + Math.floor(divBottomRight.y*mzoom / tileSize)); + + + this._addTilesFromCenterOut(nwTilePoint, seTilePoint); + this._removeOtherTiles(nwTilePoint, seTilePoint); + }, + + _removeOtherTiles: function (nwTilePoint, seTilePoint) { + var kArr, x, y, key; + + var zoom = this._map.getZoom(); + for (key in this._tiles) { + if (this._tiles.hasOwnProperty(key)) { + kArr = key.split(':'); + x = parseInt(kArr[0], 10); + y = parseInt(kArr[1], 10); + z = parseInt(kArr[2], 10); + + // remove tile if it's out of bounds + if (z !== zoom || x < nwTilePoint.x || x > seTilePoint.x || y < nwTilePoint.y || y > seTilePoint.y) { + this._removeTile(key); + } + } + } + }, + + _removeTile: function (key) { + this.onTileRemoved && this.onTileRemoved(this._tiles[key]); + delete this._tiles[key]; + delete this._tilesLoading[key]; + }, + + _tileKey: function(tilePoint) { + return tilePoint.x + ':' + tilePoint.y + ':' + tilePoint.zoom; + }, + + _tileShouldBeLoaded: function (tilePoint) { + var k = this._tileKey(tilePoint); + return !(k in this._tiles) && !(k in this._tilesLoading); + }, + + _tileLoaded: function(tilePoint, tileData) { + this._tilesToLoad--; + var k = tilePoint.x + ':' + tilePoint.y + ':' + tilePoint.zoom + this._tiles[k] = tileData; + delete this._tilesLoading[k]; + if(this._tilesToLoad === 0) { + this.onTilesLoaded && this.onTilesLoaded(); + } + }, + + getTilePos: function (tilePoint) { + var limit = (1 << this._map.getZoom()); + // wrap tile + tilePoint = { + x: ((tilePoint.x % limit) + limit) % limit, + y: tilePoint.y + }; + + tilePoint = new google.maps.Point( + tilePoint.x * this.tileSize, + tilePoint.y * this.tileSize + ); + + var bounds = this._map.getBounds(); + var topLeft = new google.maps.LatLng( + bounds.getNorthEast().lat(), + bounds.getSouthWest().lng() + ); + + var divTopLeft = this._map.getProjection().fromLatLngToPoint(topLeft); + zoom = (1 << this._map.getZoom()); + divTopLeft.x = divTopLeft.x * zoom; + divTopLeft.y = divTopLeft.y * zoom; + + return new google.maps.Point( + tilePoint.x - divTopLeft.x, + tilePoint.y - divTopLeft.y + ); + }, + + _addTilesFromCenterOut: function (nwTilePoint, seTilePoint) { + var queue = [], + center = new google.maps.Point( + (nwTilePoint.x + seTilePoint.x) * 0.5, + (nwTilePoint.y + seTilePoint.y) * 0.5 + ), + zoom = this._map.getZoom(); + + var j, i, point; + + for (j = nwTilePoint.y; j <= seTilePoint.y; j++) { + for (i = nwTilePoint.x; i <= seTilePoint.x; i++) { + point = new google.maps.Point (i, j); + point.zoom = zoom; + + if (this._tileShouldBeLoaded(point)) { + queue.push(point); + } + } + } + + var tilesToLoad = queue.length; + + if (tilesToLoad === 0) { return; } + + function distanceToCenterSq(point) { + var dx = point.x - center.x; + var dy = point.y - center.y; + return dx * dx + dy * dy; + } + + // load tiles in order of their distance to center + queue.sort(function (a, b) { + return distanceToCenterSq(a) - distanceToCenterSq(b); + }); + + this._tilesToLoad += tilesToLoad; + + for (i = 0; i < tilesToLoad; i++) { + var t = queue[i]; + var k = this._tileKey(t); + this._tilesLoading[k] = t; + // events + if (this.onTileAdded) { + this.onTileAdded(t); + } + } + + this.onTilesLoading && this.onTilesLoading(); + } + +} + +module.exports = GMapsTileLoader; + +},{}],8:[function(require,module,exports){ +var gmaps = {}; +if (typeof google !== 'undefined' && typeof google.maps !== 'undefined') { + gmaps = require('./torque'); + gmaps.GMapsTileLoader = require('./gmaps_tileloader_mixin'); +} +module.exports = gmaps; + +},{"./gmaps_tileloader_mixin":7,"./torque":9}],9:[function(require,module,exports){ +(function (global){ +var carto = global.carto || require('carto'); +var torque = require('../'); +var CanvasLayer = require('./CanvasLayer'); +var CanvasTileLayer = require('./canvas_tile_layer'); +var GMapsTileLoader = require('./gmaps_tileloader_mixin'); + +function GMapsTorqueLayer(options) { + var self = this; + if (!torque.isBrowserSupported()) { + throw new Error("browser is not supported by torque"); + } + this.key = 0; + this.shader = null; + this.ready = false; + this.options = torque.extend({}, options); + this.options = torque.extend({ + provider: 'windshaft', + renderer: 'point', + resolution: 2, + steps: 100, + visible: true + }, this.options); + if (options.cartocss) { + torque.extend(this.options, + torque.common.TorqueLayer.optionsFromCartoCSS(options.cartocss)); + } + + this.hidden = !this.options.visible; + + this.animator = new torque.Animator(function(time) { + var k = time | 0; + if(self.key !== k) { + self.setKey(k); + } + }, torque.clone(this.options)); + + this.play = this.animator.start.bind(this.animator); + this.stop = this.animator.stop.bind(this.animator); + this.pause = this.animator.pause.bind(this.animator); + this.toggle = this.animator.toggle.bind(this.animator); + this.setDuration = this.animator.duration.bind(this.animator); + this.isRunning = this.animator.isRunning.bind(this.animator); + + + CanvasLayer.call(this, { + map: this.options.map, + //resizeHandler: this.redraw, + animate: false, + updateHandler: this.render, + readyHandler: this.initialize + }); + +} + +/** + * torque layer + */ +GMapsTorqueLayer.prototype = torque.extend({}, + CanvasLayer.prototype, + GMapsTileLoader.prototype, + torque.Event, + { + + providers: { + 'sql_api': torque.providers.json, + 'url_template': torque.providers.JsonArray, + 'windshaft': torque.providers.windshaft + }, + + renderers: { + 'point': torque.renderer.Point, + 'pixel': torque.renderer.Rectangle + }, + + initialize: function() { + var self = this; + + this.onTileAdded = this.onTileAdded.bind(this); + + this.options.ready = function() { + self.fire("change:bounds", { + bounds: self.provider.getBounds() + }); + self.animator.steps(self.provider.getSteps()); + self.animator.rescale(); + self.fire('change:steps', { + steps: self.provider.getSteps() + }); + self.setKey(self.key); + }; + + this.provider = new this.providers[this.options.provider](this.options); + this.renderer = new this.renderers[this.options.renderer](this.getCanvas(), this.options); + this.renderer.options.errorCallback = this.options.errorCallback; + + // this listener should be before tile loader + this._cacheListener = google.maps.event.addListener(this.options.map, 'zoom_changed', function() { + self.renderer && self.renderer.clearSpriteCache(); + }); + + this._initTileLoader(this.options.map, this.getProjection()); + + if (this.shader) { + this.renderer.setShader(this.shader); + } + + }, + + hide: function() { + if(this.hidden) return this; + this.pause(); + this.clear(); + this.hidden = true; + return this; + }, + + show: function() { + if(!this.hidden) return this; + this.hidden = false; + this.play(); + if (this.options.steps === 1){ + this.redraw(); + } + return this; + }, + + setSQL: function(sql) { + if (this.provider.options.named_map) throw new Error("SQL queries on named maps are read-only"); + if (!this.provider || !this.provider.setSQL) { + throw new Error("this provider does not support SQL"); + } + this.provider.setSQL(sql); + this._reloadTiles(); + return this; + }, + + setBlendMode: function(_) { + this.renderer && this.renderer.setBlendMode(_); + this.redraw(); + }, + + setSteps: function(steps) { + this.provider && this.provider.setSteps(steps); + this.animator && this.animator.steps(steps); + this._reloadTiles(); + }, + + setColumn: function(column, isTime) { + this.provider && this.provider.setColumn(column, isTime); + this._reloadTiles(); + }, + + getTimeBounds: function() { + return this.provider && this.provider.getKeySpan(); + }, + + getCanvas: function() { + return this.canvas; + }, + + // for each tile shown on the map request the data + onTileAdded: function(t) { + var self = this; + this.provider.getTileData(t, t.zoom, function(tileData) { + // don't load tiles that are not being shown + if (t.zoom !== self.options.map.getZoom()) return; + self._tileLoaded(t, tileData); + if (tileData) { + self.redraw(); + } + }); + }, + + clear: function() { + var canvas = this.canvas; + canvas.width = canvas.width; + }, + + /** + * render the selectef key + * don't call this function directly, it's called by + * requestAnimationFrame. Use redraw to refresh it + */ + render: function() { + if(this.hidden) return; + var t, tile, pos; + var canvas = this.canvas; + this.renderer.clearCanvas(); + var ctx = canvas.getContext('2d'); + + // renders only a "frame" + for(t in this._tiles) { + tile = this._tiles[t]; + if (tile) { + pos = this.getTilePos(tile.coord); + ctx.setTransform(1, 0, 0, 1, pos.x, pos.y); + this.renderer.renderTile(tile, this.key); + } + } + this.renderer.applyFilters(); + }, + + getActivePointsBBox: function(step) { + var positions = []; + var tileMax = this.options.resolution * (256/this.options.resolution - 1); + for(var t in this._tiles) { + var tile = this._tiles[t]; + positions = positions.concat(this.renderer.getActivePointsBBox(tile, step)); + } + return positions; + }, + + /** + * set key to be shown. If it's a single value + * it renders directly, if it's an array it renders + * accumulated + */ + setKey: function(key) { + this.key = key; + this.animator.step(key); + this.redraw(); + this.fire('change:time', { time: this.getTime(), step: this.key }); + }, + + /** + * helper function, does the same than ``setKey`` but only + * accepts scalars. + */ + setStep: function(time) { + if(time === undefined || time.length !== undefined) { + throw new Error("setTime only accept scalars"); + } + this.setKey(time); + }, + + /** + * transform from animation step to Date object + * that contains the animation time + * + * ``step`` should be between 0 and ``steps - 1`` + */ + stepToTime: function(step) { + if (!this.provider) return 0; + var times = this.provider.getKeySpan(); + var time = times.start + (times.end - times.start)*(step/this.provider.getSteps()); + return new Date(time); + }, + + timeToStep: function(timestamp) { + if (typeof timestamp === "Date") timestamp = timestamp.getTime(); + if (!this.provider) return 0; + var times = this.provider.getKeySpan(); + var step = (this.provider.getSteps() * (timestamp - times.start)) / (times.end - times.start); + return step; + }, + + getStep: function() { + return this.key; + }, + + /** + * returns the animation time defined by the data + * in the defined column. Date object + */ + getTime: function() { + return this.stepToTime(this.key); + }, + + /** + * set the cartocss for the current renderer + */ + setCartoCSS: function(cartocss) { + if (this.provider && this.provider.options.named_map) throw new Error("CartoCSS style on named maps is read-only"); + var shader = new carto.RendererJS().render(cartocss); + this.shader = shader; + if (this.renderer) { + this.renderer.setShader(shader); + } + + // provider options + var options = torque.common.TorqueLayer.optionsFromLayer(shader.findLayer({ name: 'Map' })); + this.provider && this.provider.setCartoCSS && this.provider.setCartoCSS(cartocss); + if(this.provider && this.provider.setOptions(options)) { + this._reloadTiles(); + } + torque.extend(this.options, options); + + // animator options + if (options.animationDuration) { + this.animator.duration(options.animationDuration); + } + + this.redraw(); + return this; + }, + + redraw: function() { + this.scheduleUpdate(); + }, + + onRemove: function() { + this.fire('remove'); + CanvasLayer.prototype.onRemove.call(this); + this.animator.stop(); + this._removeTileLoader(); + google.maps.event.removeListener(this._cacheListener); + }, + + getValueForPos: function(x, y, step) { + step = step === undefined ? this.key: step; + var t, tile, pos, value = null, xx, yy; + for(t in this._tiles) { + tile = this._tiles[t]; + pos = this.getTilePos(tile.coord); + xx = x - pos.x; + yy = y - pos.y; + if (xx >= 0 && yy >= 0 && xx < this.renderer.TILE_SIZE && yy <= this.renderer.TILE_SIZE) { + value = this.renderer.getValueFor(tile, step, xx, yy); + } + if (value !== null) { + return value; + } + } + return null; + }, + getValueForBBox: function(x, y, w, h) { + var xf = x + w, yf = y + h; + var sum = 0; + for(_y = y; y= 0) { + this.cancelAnimationFrame.call(window, this.currentAnimationFrame); + } + this.currentAnimationFrame = this.requestAnimationFrame.call(window, this.render); + }, + + // use direct: true if you are inside an animation frame call + redraw: function(direct) { + var domPosition = L.DomUtil.getPosition(this._map.getPanes().mapPane); + if (domPosition) { + L.DomUtil.setPosition(this._canvas, { x: -domPosition.x, y: -domPosition.y }); + } + if (direct) { + this.render(); + } else { + this._render(); + } + }, + + onResize: function() { + }, + + render: function() { + throw new Error('render function should be implemented'); + } + +}); + +},{"./leaflet_tileloader_mixin":13}],12:[function(require,module,exports){ +if (typeof L !== 'undefined') { + require('./torque'); +} + +},{"./torque":14}],13:[function(require,module,exports){ +L.Mixin.TileLoader = { + + _initTileLoader: function() { + this._tiles = {} + this._tilesLoading = {}; + this._tilesToLoad = 0; + this._map.on({ + 'moveend': this._updateTiles + }, this); + this._updateTiles(); + }, + + _removeTileLoader: function() { + this._map.off({ + 'moveend': this._updateTiles + }, this); + this._removeTiles(); + }, + + _updateTiles: function () { + + if (!this._map) { return; } + + var bounds = this._map.getPixelBounds(), + zoom = this._map.getZoom(), + tileSize = this.options.tileSize; + + if (zoom > this.options.maxZoom || zoom < this.options.minZoom) { + return; + } + + var nwTilePoint = new L.Point( + Math.floor(bounds.min.x / tileSize), + Math.floor(bounds.min.y / tileSize)), + + seTilePoint = new L.Point( + Math.floor(bounds.max.x / tileSize), + Math.floor(bounds.max.y / tileSize)), + + tileBounds = new L.Bounds(nwTilePoint, seTilePoint); + + this._addTilesFromCenterOut(tileBounds); + this._removeOtherTiles(tileBounds); + }, + + _removeTiles: function (bounds) { + for (var key in this._tiles) { + this._removeTile(key); + } + }, + + _reloadTiles: function() { + this._removeTiles(); + this._updateTiles(); + }, + + _removeOtherTiles: function (bounds) { + var kArr, x, y, z, key; + var zoom = this._map.getZoom(); + + for (key in this._tiles) { + if (this._tiles.hasOwnProperty(key)) { + kArr = key.split(':'); + x = parseInt(kArr[0], 10); + y = parseInt(kArr[1], 10); + z = parseInt(kArr[2], 10); + + // remove tile if it's out of bounds + if (zoom !== z || x < bounds.min.x || x > bounds.max.x || y < bounds.min.y || y > bounds.max.y) { + this._removeTile(key); + } + } + } + }, + + _removeTile: function (key) { + this.fire('tileRemoved', this._tiles[key]); + delete this._tiles[key]; + delete this._tilesLoading[key]; + }, + + _tileKey: function(tilePoint) { + return tilePoint.x + ':' + tilePoint.y + ':' + tilePoint.zoom; + }, + + _tileShouldBeLoaded: function (tilePoint) { + var k = this._tileKey(tilePoint); + return !(k in this._tiles) && !(k in this._tilesLoading); + }, + + _tileLoaded: function(tilePoint, tileData) { + this._tilesToLoad--; + var k = tilePoint.x + ':' + tilePoint.y + ':' + tilePoint.zoom + this._tiles[k] = tileData; + delete this._tilesLoading[k]; + if(this._tilesToLoad === 0) { + this.fire("tilesLoaded"); + } + }, + + getTilePos: function (tilePoint) { + tilePoint = new L.Point(tilePoint.x, tilePoint.y); + var origin = this._map._getNewTopLeftPoint(this._map.getCenter()), + tileSize = this.options.tileSize; + + return tilePoint.multiplyBy(tileSize).subtract(origin); + }, + + _addTilesFromCenterOut: function (bounds) { + var queue = [], + center = bounds.getCenter(), + zoom = this._map.getZoom(); + + var j, i, point; + + for (j = bounds.min.y; j <= bounds.max.y; j++) { + for (i = bounds.min.x; i <= bounds.max.x; i++) { + point = new L.Point(i, j); + point.zoom = zoom; + + if (this._tileShouldBeLoaded(point)) { + queue.push(point); + } + } + } + + var tilesToLoad = queue.length; + + if (tilesToLoad === 0) { return; } + + // load tiles in order of their distance to center + queue.sort(function (a, b) { + return a.distanceTo(center) - b.distanceTo(center); + }); + + this._tilesToLoad += tilesToLoad; + + for (i = 0; i < tilesToLoad; i++) { + var t = queue[i]; + var k = this._tileKey(t); + this._tilesLoading[k] = t; + this.fire('tileAdded', t); + } + this.fire("tilesLoading"); + + } + +} + +},{}],14:[function(require,module,exports){ +(function (global){ +var carto = global.carto || require('carto'); +var torque = require('../'); + +require('./canvas_layer'); + +/** + * torque layer + */ +L.TorqueLayer = L.CanvasLayer.extend({ + + providers: { + 'sql_api': torque.providers.json, + 'url_template': torque.providers.JsonArray, + 'windshaft': torque.providers.windshaft + }, + + renderers: { + 'point': torque.renderer.Point, + 'pixel': torque.renderer.Rectangle + }, + + initialize: function(options) { + var self = this; + if (!torque.isBrowserSupported()) { + throw new Error("browser is not supported by torque"); + } + options.tileLoader = true; + this.key = 0; + this.prevRenderedKey = 0; + if (options.cartocss) { + torque.extend(options, torque.common.TorqueLayer.optionsFromCartoCSS(options.cartocss)); + } + + options.resolution = options.resolution || 2; + options.steps = options.steps || 100; + options.visible = options.visible === undefined ? true: options.visible; + this.hidden = !options.visible; + + this.animator = new torque.Animator(function(time) { + var k = time | 0; + if(self.key !== k) { + self.setKey(k, { direct: true }); + } + }, torque.extend(torque.clone(options), { + onPause: function() { + self.fire('pause'); + }, + onStop: function() { + self.fire('stop'); + }, + onStart: function() { + self.fire('play'); + } + })); + + this.play = this.animator.start.bind(this.animator); + this.stop = this.animator.stop.bind(this.animator); + this.pause = this.animator.pause.bind(this.animator); + this.toggle = this.animator.toggle.bind(this.animator); + this.setDuration = this.animator.duration.bind(this.animator); + this.isRunning = this.animator.isRunning.bind(this.animator); + + + L.CanvasLayer.prototype.initialize.call(this, options); + + this.options.renderer = this.options.renderer || 'point'; + this.options.provider = this.options.provider || 'windshaft'; + + this.provider = new this.providers[this.options.provider](options); + this.renderer = new this.renderers[this.options.renderer](this.getCanvas(), options); + + options.ready = function() { + self.fire("change:bounds", { + bounds: self.provider.getBounds() + }); + self.animator.steps(self.provider.getSteps()); + self.animator.rescale(); + self.fire('change:steps', { + steps: self.provider.getSteps() + }); + self.setKey(self.key); + }; + + this.renderer.on("allIconsLoaded", this.render.bind(this)); + + + // for each tile shown on the map request the data + this.on('tileAdded', function(t) { + var tileData = this.provider.getTileData(t, t.zoom, function(tileData) { + // don't load tiles that are not being shown + if (t.zoom !== self._map.getZoom()) return; + self._tileLoaded(t, tileData); + self._clearTileCaches(); + if (tileData) { + self.redraw(); + } + }); + }, this); + + }, + + _clearTileCaches: function() { + var t, tile; + for(t in this._tiles) { + tile = this._tiles[t]; + if (tile && tile._tileCache) { + tile._tileCache = null; + } + } + }, + + _clearCaches: function() { + this.renderer && this.renderer.clearSpriteCache(); + this._clearTileCaches(); + }, + + onAdd: function (map) { + map.on({ + 'zoomend': this._clearCaches, + 'zoomstart': this._pauseOnZoom, + }, this); + + map.on({ + 'zoomend': this._resumeOnZoom + }, this); + L.CanvasLayer.prototype.onAdd.call(this, map); + }, + + onRemove: function(map) { + this.fire('remove'); + this._removeTileLoader(); + map.off({ + 'zoomend': this._clearCaches, + 'zoomstart': this._pauseOnZoom, + }, this); + map.off({ + 'zoomend': this._resumeOnZoom + }, this); + L.CanvasLayer.prototype.onRemove.call(this, map); + }, + + _pauseOnZoom: function() { + this.wasRunning = this.isRunning(); + if (this.wasRunning) { + this.pause(); + } + }, + + _resumeOnZoom: function() { + if (this.wasRunning) { + this.play(); + } + }, + + hide: function() { + if(this.hidden) return this; + this.pause(); + this.clear(); + this.hidden = true; + return this; + }, + + show: function() { + if(!this.hidden) return this; + this.hidden = false; + this.play(); + if (this.options.steps === 1){ + this.redraw(); + } + return this; + }, + + setSQL: function(sql) { + if (this.provider.options.named_map) throw new Error("SQL queries on named maps are read-only"); + if (!this.provider || !this.provider.setSQL) { + throw new Error("this provider does not support SQL"); + } + this.provider.setSQL(sql); + this._reloadTiles(); + return this; + }, + + setBlendMode: function(_) { + this.renderer.setBlendMode(_); + this.redraw(); + }, + + setSteps: function(steps) { + this.provider.setSteps(steps); + this._reloadTiles(); + }, + + setColumn: function(column, isTime) { + this.provider.setColumn(column, isTime); + this._reloadTiles(); + }, + + getTimeBounds: function() { + return this.provider && this.provider.getKeySpan(); + }, + + clear: function() { + var canvas = this.getCanvas(); + canvas.width = canvas.width; + }, + + /** + * render the selectef key + * don't call this function directly, it's called by + * requestAnimationFrame. Use redraw to refresh it + */ + render: function() { + if(this.hidden) return; + var t, tile, pos; + var canvas = this.getCanvas(); + this.renderer.clearCanvas(); + var ctx = canvas.getContext('2d'); + + for(t in this._tiles) { + tile = this._tiles[t]; + if (tile) { + // clear cache + if (this.animator.isRunning()) { + tile._tileCache = null; + } + + pos = this.getTilePos(tile.coord); + ctx.setTransform(1, 0, 0, 1, pos.x, pos.y); + + if (tile._tileCache) { + // when the tile has a cached image just render it and avoid to render + // all the points + this.renderer._ctx.drawImage(tile._tileCache, 0, 0); + } else { + this.renderer.renderTile(tile, this.key); + } + } + } + this.renderer.applyFilters(); + + // prepare caches if the animation is not running + // don't cache if the key has just changed, this avoids to cache + // when the user is dragging, it only cache when the map is still + if (!this.animator.isRunning() && this.key === this.prevRenderedKey) { + var tile_size = this.renderer.TILE_SIZE; + for(t in this._tiles) { + tile = this._tiles[t]; + if (tile && !tile._tileCache) { + var c = tile._tileCache = document.createElement('canvas'); + c.width = c.height = tile_size; + pos = this.getTilePos(tile.coord); + // clip bounds, firefox raise an exception when try to get data from outside canvas + var x = Math.max(0, pos.x) + var y = Math.max(0, pos.y) + var w = Math.min(tile_size, this.getCanvas().width - x); + var h = Math.min(tile_size, this.getCanvas().height - y); + if (w > 0 && h > 0) { + c.getContext('2d').drawImage(this.getCanvas(), x, y, w, h, x - pos.x, y - pos.y, w, h); + } + } + } + } + + this.prevRenderedKey = this.key; + + }, + + /** + * set key to be shown. If it's a single value + * it renders directly, if it's an array it renders + * accumulated + */ + setKey: function(key, options) { + this.key = key; + this.animator.step(key); + this._clearTileCaches(); + this.redraw(options && options.direct); + this.fire('change:time', { time: this.getTime(), step: this.key }); + }, + + /** + * helper function, does the same than ``setKey`` but only + * accepts scalars. + */ + setStep: function(time) { + if(time === undefined || time.length !== undefined) { + throw new Error("setTime only accept scalars"); + } + this.setKey(time); + }, + + /** + * transform from animation step to Date object + * that contains the animation time + * + * ``step`` should be between 0 and ``steps - 1`` + */ + stepToTime: function(step) { + var times = this.provider.getKeySpan(); + var time = times.start + (times.end - times.start)*(step/this.provider.getSteps()); + return new Date(time); + }, + + timeToStep: function(timestamp) { + if (typeof timestamp === "Date") timestamp = timestamp.getTime(); + if (!this.provider) return 0; + var times = this.provider.getKeySpan(); + var step = (this.provider.getSteps() * (timestamp - times.start)) / (times.end - times.start); + return step; + }, + + getStep: function() { + return this.key; + }, + + /** + * returns the animation time defined by the data + * in the defined column. Date object + */ + getTime: function() { + return this.stepToTime(this.key); + }, + + /** + * returns an object with the start and end times + */ + getTimeSpan: function() { + return this.provider.getKeySpan(); + }, + + /** + * set the cartocss for the current renderer + */ + setCartoCSS: function(cartocss) { + if (this.provider.options.named_map) throw new Error("CartoCSS style on named maps is read-only"); + if (!this.renderer) throw new Error('renderer is not valid'); + var shader = new carto.RendererJS().render(cartocss); + this.renderer.setShader(shader); + + // provider options + var options = torque.common.TorqueLayer.optionsFromLayer(shader.findLayer({ name: 'Map' })); + this.provider.setCartoCSS && this.provider.setCartoCSS(cartocss); + if(this.provider.setOptions(options)) { + this._reloadTiles(); + } + + torque.extend(this.options, options); + + // animator options + if (options.animationDuration) { + this.animator.duration(options.animationDuration); + } + this._clearCaches(); + this.redraw(); + return this; + }, + + /** + * get active points for a step in active zoom + * returns a list of bounding boxes [[] , [], []] + * empty list if there is no active pixels + */ + getActivePointsBBox: function(step) { + var positions = []; + for(var t in this._tiles) { + var tile = this._tiles[t]; + positions = positions.concat(this.renderer.getActivePointsBBox(tile, step)); + } + return positions; + }, + + /** + * return the value for position relative to map coordinates. null for no value + */ + getValueForPos: function(x, y, step) { + step = step === undefined ? this.key: step; + var t, tile, pos, value = null, xx, yy; + for(t in this._tiles) { + tile = this._tiles[t]; + pos = this.getTilePos(tile.coord); + xx = x - pos.x; + yy = y - pos.y; + if (xx >= 0 && yy >= 0 && xx < this.renderer.TILE_SIZE && yy <= this.renderer.TILE_SIZE) { + value = this.renderer.getValueFor(tile, step, xx, yy); + } + if (value !== null) { + return value; + } + } + return null; + }, + + getValueForBBox: function(x, y, w, h) { + var xf = x + w, yf = y + h, _x=x; + var sum = 0; + for(_y = y; _y 1) { + Profiler.new_value(this.name, this.count); + this.count = 0; + this.start(); + } + } +}; + +Profiler.metric = function(name) { + return new Metric(name); +}; + +module.exports = Profiler; + +},{}],18:[function(require,module,exports){ +module.exports = { + json: require('./json'), + JsonArray: require('./jsonarray'), + windshaft: require('./windshaft') +}; + +},{"./json":19,"./jsonarray":20,"./windshaft":21}],19:[function(require,module,exports){ +var torque = require('../'); +var Profiler = require('../profiler'); + + var Uint8Array = torque.types.Uint8Array; + var Int32Array = torque.types.Int32Array; + var Uint32Array = torque.types.Uint32Array; + + // format('hello, {0}', 'rambo') -> "hello, rambo" + function format(str) { + for(var i = 1; i < arguments.length; ++i) { + var attrs = arguments[i]; + for(var attr in attrs) { + str = str.replace(RegExp('\\{' + attr + '\\}', 'g'), attrs[attr]); + } + } + return str; + } + + var json = function (options) { + this._ready = false; + this._tileQueue = []; + this.options = options; + + this.options.is_time = this.options.is_time === undefined ? true: this.options.is_time; + this.options.tiler_protocol = options.tiler_protocol || 'http'; + this.options.tiler_domain = options.tiler_domain || 'cartodb.com'; + this.options.tiler_port = options.tiler_port || 80; + + if (this.options.data_aggregation) { + this.options.cumulative = this.options.data_aggregation === 'cumulative'; + } + + // check options + if (options.resolution === undefined ) throw new Error("resolution should be provided"); + if (options.steps === undefined ) throw new Error("steps should be provided"); + if(options.start === undefined) { + this._fetchKeySpan(); + } else { + this._setReady(true); + } + }; + + json.prototype = { + + /** + * return the torque tile encoded in an efficient javascript + * structure: + * { + * x:Uint8Array x coordinates in tile reference system, normally from 0-255 + * y:Uint8Array y coordinates in tile reference system + * Index: Array index to the properties + * } + */ + proccessTile: function(rows, coord, zoom) { + var r; + var x = new Uint8Array(rows.length); + var y = new Uint8Array(rows.length); + + var prof_mem = Profiler.metric('ProviderJSON:mem'); + var prof_point_count = Profiler.metric('ProviderJSON:point_count'); + var prof_process_time = Profiler.metric('ProviderJSON:process_time').start() + + // count number of dates + var dates = 0; + var maxDateSlots = -1; + for (r = 0; r < rows.length; ++r) { + var row = rows[r]; + dates += row.dates__uint16.length; + for(var d = 0; d < row.dates__uint16.length; ++d) { + maxDateSlots = Math.max(maxDateSlots, row.dates__uint16[d]); + } + } + + if(this.options.cumulative) { + dates = (1 + maxDateSlots) * rows.length; + } + + var type = this.options.cumulative ? Uint32Array: Uint8Array; + + // reserve memory for all the dates + var timeIndex = new Int32Array(maxDateSlots + 1); //index-size + var timeCount = new Int32Array(maxDateSlots + 1); + var renderData = new (this.options.valueDataType || type)(dates); + var renderDataPos = new Uint32Array(dates); + + prof_mem.inc( + 4 * maxDateSlots + // timeIndex + 4 * maxDateSlots + // timeCount + dates + //renderData + dates * 4 + ); //renderDataPos + + prof_point_count.inc(rows.length); + + var rowsPerSlot = {}; + + // precache pixel positions + for (var r = 0; r < rows.length; ++r) { + var row = rows[r]; + x[r] = row.x__uint8 * this.options.resolution; + // fix value when it's in the tile EDGE + // TODO: this should be fixed in SQL query + if (row.y__uint8 === -1) { + y[r] = 0; + } else { + y[r] = row.y__uint8 * this.options.resolution; + } + + var dates = row.dates__uint16; + var vals = row.vals__uint8; + if (!this.options.cumulative) { + for (var j = 0, len = dates.length; j < len; ++j) { + var rr = rowsPerSlot[dates[j]] || (rowsPerSlot[dates[j]] = []); + if(this.options.cumulative) { + vals[j] += prev_val; + } + prev_val = vals[j]; + rr.push([r, vals[j]]); + } + } else { + var valByDate = {} + for (var j = 0, len = dates.length; j < len; ++j) { + valByDate[dates[j]] = vals[j]; + } + var accum = 0; + + // extend the latest to the end + for (var j = dates[0]; j <= maxDateSlots; ++j) { + var rr = rowsPerSlot[j] || (rowsPerSlot[j] = []); + var v = valByDate[j]; + if (v) { + accum += v; + } + rr.push([r, accum]); + } + + /*var lastDateSlot = dates[dates.length - 1]; + for (var j = lastDateSlot + 1; j <= maxDateSlots; ++j) { + var rr = rowsPerSlot[j] || (rowsPerSlot[j] = []); + rr.push([r, prev_val]); + } + */ + } + + } + + // for each timeslot search active buckets + var renderDataIndex = 0; + var timeSlotIndex = 0; + var i = 0; + for(var i = 0; i <= maxDateSlots; ++i) { + var c = 0; + var slotRows = rowsPerSlot[i] + if(slotRows) { + for (var r = 0; r < slotRows.length; ++r) { + var rr = slotRows[r]; + ++c; + renderDataPos[renderDataIndex] = rr[0] + renderData[renderDataIndex] = rr[1]; + ++renderDataIndex; + } + } + timeIndex[i] = timeSlotIndex; + timeCount[i] = c; + timeSlotIndex += c; + } + + prof_process_time.end(); + + return { + x: x, + y: y, + z: zoom, + coord: { + x: coord.x, + y: coord.y, + z: zoom + }, + timeCount: timeCount, + timeIndex: timeIndex, + renderDataPos: renderDataPos, + renderData: renderData, + maxDate: maxDateSlots + }; + }, + + _host: function() { + var opts = this.options; + var port = opts.sql_api_port; + var domain = ((opts.user_name || opts.user) + '.' + (opts.sql_api_domain || 'cartodb.com')) + (port ? ':' + port: ''); + var protocol = opts.sql_api_protocol || 'http'; + return this.options.url || protocol + '://' + domain + '/api/v2/sql'; + }, + + url: function(subhost) { + var opts = this.options; + var protocol = opts.sql_api_protocol || 'http'; + if (!this.options.cdn_url) { + return this._host(); + } + var h = protocol+ "://"; + if (subhost) { + h += subhost + "."; + } + var cdn_host = opts.cdn_url; + if(!cdn_host.http && !cdn_host.https) { + throw new Error("cdn_host should contain http and/or https entries"); + } + h += cdn_host[protocol] + "/" + (opts.user_name || opts.user) + '/api/v2/sql'; + return h; + }, + + _hash: function(str) { + var hash = 0; + if (!str || str.length == 0) return hash; + for (var i = 0, l = str.length; i < l; ++i) { + hash = (( (hash << 5 ) - hash ) + str.charCodeAt(i)) | 0; + } + return hash; + }, + + _extraParams: function() { + if (this.options.extra_params) { + var p = []; + for(var k in this.options.extra_params) { + var v = this.options.extra_params[k]; + if (v) { + p.push(k + "=" + encodeURIComponent(v)); + } + } + return p.join('&'); + } + return null; + }, + + isHttps: function() { + return this.options.sql_api_protocol && this.options.sql_api_protocol === 'https'; + }, + + // execute actual query + sql: function(sql, callback, options) { + options = options || {}; + var subdomains = this.options.subdomains || '0123'; + if(this.isHttps()) { + subdomains = [null]; // no subdomain + } + + + var url; + if (options.no_cdn) { + url = this._host(); + } else { + url = this.url(subdomains[Math.abs(this._hash(sql))%subdomains.length]); + } + var extra = this._extraParams(); + torque.net.get( url + "?q=" + encodeURIComponent(sql) + (extra ? "&" + extra: ''), function (data) { + if(options.parseJSON) { + data = JSON.parse(data && data.responseText); + } + callback && callback(data); + }); + }, + + getTileData: function(coord, zoom, callback) { + if(!this._ready) { + this._tileQueue.push([coord, zoom, callback]); + } else { + this._getTileData(coord, zoom, callback); + } + }, + + _setReady: function(ready) { + this._ready = true; + this._processQueue(); + this.options.ready && this.options.ready(); + }, + + _processQueue: function() { + var item; + while (item = this._tileQueue.pop()) { + this._getTileData.apply(this, item); + } + }, + + /** + * `coord` object like {x : tilex, y: tiley } + * `zoom` quadtree zoom level + */ + _getTileData: function(coord, zoom, callback) { + var prof_fetch_time = Profiler.metric('ProviderJSON:tile_fetch_time').start() + this.table = this.options.table; + var numTiles = 1 << zoom; + + var column_conv = this.options.column; + + if(this.options.is_time) { + column_conv = format("date_part('epoch', {column})", this.options); + } + + var sql = "" + + "WITH " + + "par AS (" + + " SELECT CDB_XYZ_Resolution({zoom})*{resolution} as res" + + ", 256/{resolution} as tile_size" + + ", CDB_XYZ_Extent({x}, {y}, {zoom}) as ext " + + ")," + + "cte AS ( "+ + " SELECT ST_SnapToGrid(i.the_geom_webmercator, p.res) g" + + ", {countby} c" + + ", floor(({column_conv} - {start})/{step}) d" + + " FROM ({_sql}) i, par p " + + " WHERE i.the_geom_webmercator && p.ext " + + " GROUP BY g, d" + + ") " + + "" + + "SELECT (st_x(g)-st_xmin(p.ext))/p.res x__uint8, " + + " (st_y(g)-st_ymin(p.ext))/p.res y__uint8," + + " array_agg(c) vals__uint8," + + " array_agg(d) dates__uint16" + + // the tile_size where are needed because the overlaps query in cte subquery includes the points + // in the left and bottom borders of the tile + " FROM cte, par p where (st_y(g)-st_ymin(p.ext))/p.res < tile_size and (st_x(g)-st_xmin(p.ext))/p.res < tile_size GROUP BY x__uint8, y__uint8"; + + + var query = format(sql, this.options, { + zoom: zoom, + x: coord.x, + y: coord.y, + column_conv: column_conv, + _sql: this.getSQL() + }); + + var self = this; + this.sql(query, function (data) { + if (data) { + var rows = JSON.parse(data.responseText).rows; + callback(self.proccessTile(rows, coord, zoom)); + } else { + callback(null); + } + prof_fetch_time.end(); + }); + }, + + getKeySpan: function() { + return { + start: this.options.start * 1000, + end: this.options.end * 1000, + step: this.options.step, + steps: this.options.steps, + columnType: this.options.is_time ? 'date': 'number' + }; + }, + + setColumn: function(column, isTime) { + this.options.column = column; + this.options.is_time = isTime === undefined ? true: false; + this.reload(); + }, + + setResolution: function(res) { + this.options.resolution = res; + }, + + // return true if tiles has been changed + setOptions: function(opt) { + var refresh = false; + + if(opt.resolution !== undefined && opt.resolution !== this.options.resolution) { + this.options.resolution = opt.resolution; + refresh = true; + } + + if(opt.steps !== undefined && opt.steps !== this.options.steps) { + this.setSteps(opt.steps, { silent: true }); + refresh = true; + } + + if(opt.column !== undefined && opt.column !== this.options.column) { + this.options.column = opt.column; + refresh = true; + } + + if(opt.countby !== undefined && opt.countby !== this.options.countby) { + this.options.countby = opt.countby; + refresh = true; + } + + if(opt.data_aggregation !== undefined) { + var c = opt.data_aggregation === 'cumulative'; + if (this.options.cumulative !== c) { + this.options.cumulative = c; + refresh = true; + } + } + + if (refresh) this.reload(); + return refresh; + + }, + + reload: function() { + this._ready = false; + this._fetchKeySpan(); + }, + + setSQL: function(sql) { + if (this.options.sql != sql) { + this.options.sql = sql; + this.reload(); + } + }, + + getSteps: function() { + return Math.min(this.options.steps, this.options.data_steps); + }, + + setSteps: function(steps, opt) { + opt = opt || {}; + if (this.options.steps !== steps) { + this.options.steps = steps; + this.options.step = (this.options.end - this.options.start)/this.getSteps(); + this.options.step = this.options.step || 1; + if (!opt.silent) this.reload(); + } + }, + + getBounds: function() { + return this.options.bounds; + }, + + getSQL: function() { + return this.options.sql || "select * from " + this.options.table; + }, + + _tilerHost: function() { + var opts = this.options; + var user = (opts.user_name || opts.user); + return opts.tiler_protocol + + "://" + (user ? user + "." : "") + + opts.tiler_domain + + ((opts.tiler_port != "") ? (":" + opts.tiler_port) : ""); + }, + + _fetchUpdateAt: function(callback) { + var self = this; + var layergroup = { + "version": "1.0.1", + "stat_tag": this.options.stat_tag || 'torque', + "layers": [{ + "type": "cartodb", + "options": { + "cartocss_version": "2.1.1", + "cartocss": "#layer {}", + "sql": this.getSQL() + } + }] + }; + var url = this._tilerHost() + "/tiles/layergroup"; + var extra = this._extraParams(); + + // tiler needs map_key instead of api_key + // so replace it + if (extra) { + extra = extra.replace('api_key=', 'map_key='); + } + + url = url + + "?config=" + encodeURIComponent(JSON.stringify(layergroup)) + + "&callback=?" + (extra ? "&" + extra: ''); + + torque.net.jsonp(url, function (data) { + var query = format("select * from ({sql}) __torque_wrap_sql limit 0", { sql: self.getSQL() }); + self.sql(query, function (queryData) { + if (data && queryData) { + callback({ + updated_at: data.last_updated, + fields: queryData.fields + }); + } + }, { parseJSON: true }); + }); + }, + + // + // the data range could be set by the user though ``start`` + // option. It can be fecthed from the table when the start + // is not specified. + // + _fetchKeySpan: function() { + var self = this; + var max_col, min_col, max_tmpl, min_tmpl; + + this._fetchUpdateAt(function(data) { + if (!data) return; + self.options.extra_params = self.options.extra_params || {}; + self.options.extra_params.last_updated = data.updated_at || 0; + self.options.extra_params.cache_policy = 'persist'; + self.options.is_time = data.fields[self.options.column].type === 'date'; + + var column_conv = self.options.column; + if (self.options.is_time){ + max_tmpl = "date_part('epoch', max({column}))"; + min_tmpl = "date_part('epoch', min({column}))"; + column_conv = format("date_part('epoch', {column})", self.options); + } else { + max_tmpl = "max({column})"; + min_tmpl = "min({column})"; + } + + max_col = format(max_tmpl, { column: self.options.column }); + min_col = format(min_tmpl, { column: self.options.column }); + + /*var sql_stats = "" + + "WITH summary_groups as ( " + + "WITH summary as ( " + + "select (row_number() over (order by __time_col asc nulls last)+1)/2 as rownum, __time_col " + + "from (select *, {column} as __time_col from ({sql}) __s) __torque_wrap_sql " + + "order by __time_col asc " + + ") " + + "SELECT " + + "max(__time_col) OVER(PARTITION BY rownum) - " + + "min(__time_col) OVER(PARTITION BY rownum) diff " + + "FROM summary " + + "), subq as ( " + + " SELECT " + + "st_xmax(st_envelope(st_collect(the_geom))) xmax, " + + "st_ymax(st_envelope(st_collect(the_geom))) ymax, " + + "st_xmin(st_envelope(st_collect(the_geom))) xmin, " + + "st_ymin(st_envelope(st_collect(the_geom))) ymin, " + + "{max_col} max, " + + "{min_col} min FROM ({sql}) __torque_wrap_sql " + + ")" + + "SELECT " + + "xmax, xmin, ymax, ymin, a.max as max_date, a.min as min_date, " + + "avg(diff) as diffavg," + + "(a.max - a.min)/avg(diff) as num_steps " + + "FROM summary_groups, subq a " + + "WHERE diff > 0 group by xmax, xmin, ymax, ymin, max_date, min_date"; + */ + var sql_stats = " SELECT " + + "st_xmax(st_envelope(st_collect(the_geom))) xmax, " + + "st_ymax(st_envelope(st_collect(the_geom))) ymax, " + + "st_xmin(st_envelope(st_collect(the_geom))) xmin, " + + "st_ymin(st_envelope(st_collect(the_geom))) ymin, " + + "count(*) as num_steps, " + + "{max_col} max_date, " + + "{min_col} min_date FROM ({sql}) __torque_wrap_sql "; + + var sql = format(sql_stats, { + max_col: max_col, + min_col: min_col, + column: column_conv, + sql: self.getSQL() + }); + + self.sql(sql, function(data) { + //TODO: manage bounds + data = data.rows[0]; + self.options.start = data.min_date; + self.options.end = data.max_date; + self.options.step = (data.max_date - data.min_date)/Math.min(self.options.steps, data.num_steps>>0); + self.options.data_steps = data.num_steps >> 0; + // step can't be 0 + self.options.step = self.options.step || 1; + self.options.bounds = [ + [data.ymin, data.xmin], + [data.ymax, data.xmax] + ]; + self._setReady(true); + }, { parseJSON: true, no_cdn: true }); + }, { parseJSON: true, no_cdn: true}) + } + + }; + +module.exports = json; + +},{"../":10,"../profiler":17}],20:[function(require,module,exports){ +var torque = require('../'); +var Profiler = require('../profiler'); + + var Uint8Array = torque.types.Uint8Array; + var Int32Array = torque.types.Int32Array; + var Uint32Array = torque.types.Uint32Array; + + // format('hello, {0}', 'rambo') -> "hello, rambo" + function format(str, attrs) { + for(var i = 1; i < arguments.length; ++i) { + var attrs = arguments[i]; + for(var attr in attrs) { + str = str.replace(RegExp('\\{' + attr + '\\}', 'g'), attrs[attr]); + } + } + return str; + } + + var json = function (options) { + // check options + this.options = options; + }; + + + json.prototype = { + + // + // return the data aggregated by key: + // { + // key0: 12, + // key1: 32 + // key2: 25 + // } + // + aggregateByKey: function(rows) { + function getKeys(row) { + var HEADER_SIZE = 3; + var valuesCount = row.data[2]; + var keys = {}; + for (var s = 0; s < valuesCount; ++s) { + keys[row.data[HEADER_SIZE + s]] = row.data[HEADER_SIZE + valuesCount + s]; + } + return keys; + } + var keys = {}; + for (r = 0; r < rows.length; ++r) { + var rowKeys = getKeys(rows[r]); + for(var k in rowKeys) { + keys[k] = keys[k] || 0; + keys[k] += rowKeys[k]; + } + } + return keys; + }, + + + + + /** + * + */ + proccessTile: function(rows, coord, zoom) { + var r; + var x = new Uint8Array(rows.length); + var y = new Uint8Array(rows.length); + var self = this; + + // decode into a javascript strcuture the array + function decode_row(row) { + var HEADER_SIZE = 3; + var o = { + x: row.data[0] * self.options.resolution, + y: row.data[1] * self.options.resolution, + valuesCount: row.data[2], + times: [], + values: [] + }; + for (var s = 0; s < o.valuesCount; ++s) { + o.times.push(row.data[HEADER_SIZE + s]); + o.values.push(row.data[HEADER_SIZE + o.valuesCount + s]); + } + if(self.options.cumulative) { + for (var s = 1; s < o.valuesCount; ++s) { + o.values[s] += o.values[s - 1]; + } + } + return o + } + + // decode all the rows + for (r = 0; r < rows.length; ++r) { + rows[r] = decode_row(rows[r]); + } + + // count number of dates + var dates = 0; + var maxDateSlots = 0; + for (r = 0; r < rows.length; ++r) { + var row = rows[r]; + dates += row.times.length; + for(var d = 0; d < row.times.length; ++d) { + maxDateSlots = Math.max(maxDateSlots, row.times[d]); + } + } + + // reserve memory for all the dates + var timeIndex = new Int32Array(maxDateSlots + 1); //index-size + var timeCount = new Int32Array(maxDateSlots + 1); + var renderData = new (this.options.valueDataType || Uint8Array)(dates); + var renderDataPos = new Uint32Array(dates); + + var rowsPerSlot = {}; + + // precache pixel positions + for (var r = 0; r < rows.length; ++r) { + var row = rows[r]; + x[r] = row.x; + y[r] = row.y; + + var dates = row.times; + var vals = row.values; + for (var j = 0, len = dates.length; j < len; ++j) { + var rr = rowsPerSlot[dates[j]] || (rowsPerSlot[dates[j]] = []); + rr.push([r, vals[j]]); + } + } + + // for each timeslot search active buckets + var renderDataIndex = 0; + var timeSlotIndex = 0; + var i = 0; + for(var i = 0; i <= maxDateSlots; ++i) { + var c = 0; + var slotRows = rowsPerSlot[i] + if(slotRows) { + for (var r = 0; r < slotRows.length; ++r) { + var rr = slotRows[r]; + ++c; + renderDataPos[renderDataIndex] = rr[0] + renderData[renderDataIndex] = rr[1]; + ++renderDataIndex; + } + } + timeIndex[i] = timeSlotIndex; + timeCount[i] = c; + timeSlotIndex += c; + } + + return { + x: x, + y: y, + coord: { + x: coord.x, + y: coord.y, + z: zoom + }, + timeCount: timeCount, + timeIndex: timeIndex, + renderDataPos: renderDataPos, + renderData: renderData + }; + }, + + url: function() { + return this.options.url; + }, + + + tileUrl: function(coord, zoom) { + var template = this.url(); + var s = (this.options.subdomains || 'abcd')[(coord.x + coord.y + zoom) % 4]; + return template + .replace('{x}', coord.x) + .replace('{y}', coord.y) + .replace('{z}', zoom) + .replace('{s}', s); + }, + + getTile: function(coord, zoom, callback) { + var template = this.tileUrl(coord, zoom); + + var self = this; + var fetchTime = Profiler.metric('jsonarray:fetch time'); + fetchTime.start(); + torque.net.get(template, function (data) { + fetchTime.end(); + if(data) { + data = JSON.parse(data.responseText); + } + callback(data); + }); + }, + + /** + * `coord` object like {x : tilex, y: tiley } + * `zoom` quadtree zoom level + */ + getTileData: function(coord, zoom, callback) { + var template = this.tileUrl(coord, zoom); + + var self = this; + var fetchTime = Profiler.metric('jsonarray:fetch time'); + fetchTime.start(); + torque.net.get(template, function (data) { + fetchTime.end(); + var processed = null; + + var processingTime = Profiler.metric('jsonarray:processing time'); + var parsingTime = Profiler.metric('jsonarray:parsing time'); + try { + processingTime.start(); + parsingTime.start(); + var rows = JSON.parse(data.responseText || data.response).rows; + parsingTime.end(); + processed = self.proccessTile(rows, coord, zoom); + processingTime.end(); + } catch(e) { + console.error("problem parsing JSON on ", coord, zoom); + } + + callback(processed); + + }); + } + + }; + + module.exports = json; + +},{"../":10,"../profiler":17}],21:[function(require,module,exports){ + var torque = require('../'); + var Profiler = require('../profiler'); + + var Uint8Array = torque.types.Uint8Array; + var Int32Array = torque.types.Int32Array; + var Uint32Array = torque.types.Uint32Array; + var Uint8ClampedArray = torque.types.Uint8ClampedArray; + + // format('hello, {0}', 'rambo') -> "hello, rambo" + function format(str) { + for(var i = 1; i < arguments.length; ++i) { + var attrs = arguments[i]; + for(var attr in attrs) { + str = str.replace(RegExp('\\{' + attr + '\\}', 'g'), attrs[attr]); + } + } + return str; + } + + var windshaft = function (options) { + this._ready = false; + this._tileQueue = []; + this.options = options; + + this.options.is_time = this.options.is_time === undefined ? true: this.options.is_time; + this.options.tiler_protocol = options.tiler_protocol || 'http'; + this.options.tiler_domain = options.tiler_domain || 'cartodb.com'; + this.options.tiler_port = options.tiler_port || 80; + + // backwards compatible + if (!options.maps_api_template) { + this._buildMapsApiTemplate(this.options); + } else { + this.options.maps_api_template = options.maps_api_template; + } + + this.options.coordinates_data_type = this.options.coordinates_data_type || Uint8Array; + + if (this.options.data_aggregation) { + this.options.cumulative = this.options.data_aggregation === 'cumulative'; + } + if (this.options.auth_token) { + var e = this.options.extra_params || (this.options.extra_params = {}); + e.auth_token = this.options.auth_token; + } + if (!this.options.no_fetch_map) { + this._fetchMap(); + } + }; + + windshaft.prototype = { + + /** + * return the torque tile encoded in an efficient javascript + * structure: + * { + * x:Uint8Array x coordinates in tile reference system, normally from 0-255 + * y:Uint8Array y coordinates in tile reference system + * Index: Array index to the properties + * } + */ + proccessTile: function(rows, coord, zoom) { + var r; + var x = new this.options.coordinates_data_type(rows.length); + var y = new this.options.coordinates_data_type(rows.length); + + var prof_mem = Profiler.metric('torque.provider.windshaft.mem'); + var prof_point_count = Profiler.metric('torque.provider.windshaft.points'); + var prof_process_time = Profiler.metric('torque.provider.windshaft.process_time').start(); + + // count number of dates + var dates = 0; + var maxDateSlots = -1; + for (r = 0; r < rows.length; ++r) { + var row = rows[r]; + dates += row.dates__uint16.length; + for(var d = 0; d < row.dates__uint16.length; ++d) { + maxDateSlots = Math.max(maxDateSlots, row.dates__uint16[d]); + } + } + + if(this.options.cumulative) { + dates = (1 + maxDateSlots) * rows.length; + } + + var type = this.options.cumulative ? Uint32Array: Uint8ClampedArray; + + // reserve memory for all the dates + var timeIndex = new Int32Array(maxDateSlots + 1); //index-size + var timeCount = new Int32Array(maxDateSlots + 1); + var renderData = new (this.options.valueDataType || type)(dates); + var renderDataPos = new Uint32Array(dates); + + prof_mem.inc( + 4 * maxDateSlots + // timeIndex + 4 * maxDateSlots + // timeCount + dates + //renderData + dates * 4 + ); //renderDataPos + + prof_point_count.inc(rows.length); + + var rowsPerSlot = {}; + + // precache pixel positions + for (var r = 0; r < rows.length; ++r) { + var row = rows[r]; + x[r] = row.x__uint8 * this.options.resolution; + y[r] = row.y__uint8 * this.options.resolution; + + var dates = row.dates__uint16; + var vals = row.vals__uint8; + if (!this.options.cumulative) { + for (var j = 0, len = dates.length; j < len; ++j) { + var rr = rowsPerSlot[dates[j]] || (rowsPerSlot[dates[j]] = []); + if(this.options.cumulative) { + vals[j] += prev_val; + } + prev_val = vals[j]; + rr.push([r, vals[j]]); + } + } else { + var valByDate = {} + for (var j = 0, len = dates.length; j < len; ++j) { + valByDate[dates[j]] = vals[j]; + } + var accum = 0; + + // extend the latest to the end + for (var j = dates[0]; j <= maxDateSlots; ++j) { + var rr = rowsPerSlot[j] || (rowsPerSlot[j] = []); + var v = valByDate[j]; + if (v) { + accum += v; + } + rr.push([r, accum]); + } + + /*var lastDateSlot = dates[dates.length - 1]; + for (var j = lastDateSlot + 1; j <= maxDateSlots; ++j) { + var rr = rowsPerSlot[j] || (rowsPerSlot[j] = []); + rr.push([r, prev_val]); + } + */ + } + + } + + // for each timeslot search active buckets + var renderDataIndex = 0; + var timeSlotIndex = 0; + var i = 0; + for(var i = 0; i <= maxDateSlots; ++i) { + var c = 0; + var slotRows = rowsPerSlot[i] + if(slotRows) { + for (var r = 0; r < slotRows.length; ++r) { + var rr = slotRows[r]; + ++c; + renderDataPos[renderDataIndex] = rr[0] + renderData[renderDataIndex] = rr[1]; + ++renderDataIndex; + } + } + timeIndex[i] = timeSlotIndex; + timeCount[i] = c; + timeSlotIndex += c; + } + + prof_process_time.end(); + + return { + x: x, + y: y, + z: zoom, + coord: { + x: coord.x, + y: coord.y, + z: zoom + }, + timeCount: timeCount, + timeIndex: timeIndex, + renderDataPos: renderDataPos, + renderData: renderData, + maxDate: maxDateSlots + }; + }, + + /*setCartoCSS: function(c) { + this.options.cartocss = c; + },*/ + + setSteps: function(steps, opt) { + opt = opt || {}; + if (this.options.steps !== steps) { + this.options.steps = steps; + this.options.step = (this.options.end - this.options.start)/this.getSteps(); + this.options.step = this.options.step || 1; + if (!opt.silent) this.reload(); + } + }, + + setOptions: function(opt) { + var refresh = false; + + if(opt.resolution !== undefined && opt.resolution !== this.options.resolution) { + this.options.resolution = opt.resolution; + refresh = true; + } + + if(opt.steps !== undefined && opt.steps !== this.options.steps) { + this.setSteps(opt.steps, { silent: true }); + refresh = true; + } + + if(opt.column !== undefined && opt.column !== this.options.column) { + this.options.column = opt.column; + refresh = true; + } + + if(opt.countby !== undefined && opt.countby !== this.options.countby) { + this.options.countby = opt.countby; + refresh = true; + } + + if(opt.data_aggregation !== undefined) { + var c = opt.data_aggregation === 'cumulative'; + if (this.options.cumulative !== c) { + this.options.cumulative = c; + refresh = true; + } + } + + if (refresh) this.reload(); + return refresh; + }, + + _extraParams: function(e) { + e = torque.extend(torque.extend({}, e), this.options.extra_params); + if (e) { + var p = []; + for(var k in e) { + var v = e[k]; + if (v) { + if (torque.isArray(v)) { + for (var i = 0, len = v.length; i < len; i++) { + p.push(k + "[]=" + encodeURIComponent(v[i])); + } + } else { + p.push(k + "=" + encodeURIComponent(v)); + } + } + } + return p.join('&'); + } + return null; + }, + + getTileData: function(coord, zoom, callback) { + if(!this._ready) { + this._tileQueue.push([coord, zoom, callback]); + } else { + this._getTileData(coord, zoom, callback); + } + }, + + _setReady: function(ready) { + this._ready = true; + this._processQueue(); + this.options.ready && this.options.ready(); + }, + + _processQueue: function() { + var item; + while (item = this._tileQueue.pop()) { + this._getTileData.apply(this, item); + } + }, + + /** + * `coord` object like {x : tilex, y: tiley } + * `zoom` quadtree zoom level + */ + _getTileData: function(coord, zoom, callback) { + var self = this; + var prof_fetch_time = Profiler.metric('torque.provider.windshaft.tile.fetch').start(); + var subdomains = this.options.subdomains || '0123'; + var limit_x = Math.pow(2, zoom); + var corrected_x = ((coord.x % limit_x) + limit_x) % limit_x; + var index = Math.abs(corrected_x + coord.y) % subdomains.length; + var url = this.templateUrl + .replace('{x}', corrected_x) + .replace('{y}', coord.y) + .replace('{z}', zoom) + .replace('{s}', subdomains[index]) + + var extra = this._extraParams(); + torque.net.get( url + (extra ? "?" + extra: ''), function (data) { + prof_fetch_time.end(); + if (data && data.responseText) { + var rows = JSON.parse(data.responseText); + callback(self.proccessTile(rows, coord, zoom)); + } else { + Profiler.metric('torque.provider.windshaft.tile.error').inc(); + callback(null); + } + }); + }, + + getKeySpan: function() { + return { + start: this.options.start, + end: this.options.end, + step: this.options.step, + steps: this.options.steps, + columnType: this.options.column_type + }; + }, + + setColumn: function(column, isTime) { + this.options.column = column; + this.options.is_time = isTime === undefined ? true: false; + this.reload(); + }, + + reload: function() { + this._ready = false; + this._fetchMap(); + }, + + getSteps: function() { + return Math.min(this.options.steps, this.options.data_steps); + }, + + getBounds: function() { + return this.options.bounds; + }, + + getSQL: function() { + return this.options.sql || "select * from " + this.options.table; + }, + + setSQL: function(sql) { + if (this.options.sql != sql) { + this.options.sql = sql; + this.reload(); + } + }, + + _buildMapsApiTemplate: function(opts) { + var user = opts.user_name || opts.user; + opts.maps_api_template = opts.tiler_protocol + + "://" + ((user) ? "{user}.":"") + + opts.tiler_domain + + ((opts.tiler_port != "") ? (":" + opts.tiler_port) : ""); + }, + + _tilerHost: function() { + var opts = this.options; + var user = opts.user_name || opts.user; + return opts.maps_api_template.replace('{user}', user); + }, + + url: function () { + var opts = this.options; + var cdn_host = opts.cdn_url; + var has_empty_cdn = !cdn_host || (cdn_host && (!cdn_host.http && !cdn_host.https)); + + if (opts.no_cdn || has_empty_cdn) { + return this._tilerHost(); + } else { + var protocol = this.isHttps() ? 'https': 'http'; + var h = protocol + "://"; + if (!this.isHttps()) { + h += "{s}."; + } + var cdn_url = cdn_host[protocol]; + // build default template url if the cdn url is not templatized + // this is for backwards compatiblity, ideally we should use the url + // that tiler sends to us right away + if (!this._isUserTemplateUrl(cdn_url)) { + cdn_url = cdn_url + "/{user}"; + } + var user = opts.user_name || opts.user; + h += cdn_url.replace('{user}', user) + return h; + } + + }, + + _isUserTemplateUrl: function(t) { + return t && t.indexOf('{user}') !== -1; + }, + + isHttps: function() { + return this.options.maps_api_template.indexOf('https') === 0; + }, + + _generateCartoCSS: function() { + var attr = { + '-torque-frame-count': this.options.steps, + '-torque-resolution': this.options.resolution, + '-torque-aggregation-function': "'" + this.options.countby + "'", + '-torque-time-attribute': "'" + this.options.column + "'", + '-torque-data-aggregation': this.options.cumulative ? 'cumulative': 'linear', + }; + var st = 'Map{'; + for (var k in attr) { + st += k + ":" + attr[k] + ";"; + } + return st + "}"; + }, + + _fetchMap: function(callback) { + var self = this; + var layergroup = {}; + var host = this.options.dynamic_cdn ? this.url().replace('{s}', '0'): this._tilerHost(); + var url = host + "/api/v1/map"; + var named = this.options.named_map; + var allParams = {}; + + if(named) { + //tiles/template + url = host + "/api/v1/map/named/" + named.name + "/jsonp"; + if(typeof named.params !== "undefined"){ + layergroup = named.params; + } + } else { + layergroup = { + "version": "1.0.1", + "stat_tag": this.options.stat_tag || 'torque', + "layers": [{ + "type": "torque", + "options": { + "cartocss_version": "1.0.0", + "cartocss": this._generateCartoCSS(), + "sql": this.getSQL() + } + }] + }; + } + + if(this.options.stat_tag){ + allParams["stat_tag"] = this.options.stat_tag; + } + + extra = this._extraParams(allParams); + + // tiler needs map_key instead of api_key + // so replace it + if (extra) { + extra = extra.replace('api_key=', 'map_key='); + } + + url = url + + "?config=" + encodeURIComponent(JSON.stringify(layergroup)) + + "&callback=?" + (extra ? "&" + extra: ''); + + var map_instance_time = Profiler.metric('torque.provider.windshaft.layergroup.time').start(); + torque.net.jsonp(url, function (data) { + map_instance_time.end(); + if (data) { + if (data.errors){ + self.options.errorCallback && self.options.errorCallback(data.errors); + return; + } + var torque_key = Object.keys(data.metadata.torque)[0] + var opt = data.metadata.torque[torque_key]; + for(var k in opt) { + self.options[k] = opt[k]; + } + // use cdn_url if present + if (data.cdn_url) { + var c = self.options.cdn_url = self.options.cdn_url || {}; + c.http = data.cdn_url.http || c.http; + c.https = data.cdn_url.https || c.https; + } + self.templateUrl = self.url() + "/api/v1/map/" + data.layergroupid + "/" + torque_key + "/{z}/{x}/{y}.json.torque"; + self._setReady(true); + } else { + Profiler.metric('torque.provider.windshaft.layergroup.error').inc(); + } + }, { callbackName: self.options.instanciateCallback }); + } + + }; + + module.exports = windshaft; + +},{"../":10,"../profiler":17}],22:[function(require,module,exports){ + var TAU = Math.PI*2; + // min value to render a line. + // it does not make sense to render a line of a width is not even visible + var LINEWIDTH_MIN_VALUE = 0.05; + var MAX_SPRITE_RADIUS = 255; + + function renderPoint(ctx, st) { + ctx.fillStyle = st['marker-fill']; + var pixel_size = st['marker-width']; + + // render a circle + // TODO: fill and stroke order should depend on the order of the properties + // in the cartocss. + + // fill + ctx.beginPath(); + ctx.arc(0, 0, pixel_size, 0, TAU, true, true); + ctx.closePath(); + + if (st['marker-opacity'] !== undefined ) st['marker-fill-opacity'] = st['marker-line-opacity'] = st['marker-opacity']; + + if (st['marker-fill']) { + ctx.globalAlpha = st['marker-fill-opacity'] >= 0? st['marker-fill-opacity']: 1; + + if (ctx.globalAlpha > 0) { + ctx.fill(); + } + } + + // stroke + if (st['marker-line-color'] && st['marker-line-width'] && st['marker-line-width'] > LINEWIDTH_MIN_VALUE) { + ctx.globalAlpha = st['marker-line-opacity'] >= 0? st['marker-line-opacity']: 1; + if (st['marker-line-width'] !== undefined) { + ctx.lineWidth = st['marker-line-width']; + } + ctx.strokeStyle = st['marker-line-color']; + + // do not render for alpha = 0 + if (ctx.globalAlpha > 0) { + ctx.stroke(); + } + } + } + + function renderRectangle(ctx, st) { + ctx.fillStyle = st['marker-fill']; + var pixel_size = st['marker-width']; + var w = pixel_size * 2; + + // fill + if (st['marker-fill']) { + if (st['marker-fill-opacity'] !== undefined || st['marker-opacity'] !== undefined) { + ctx.globalAlpha = st['marker-fill-opacity'] || st['marker-opacity']; + } + ctx.fillRect(-pixel_size, -pixel_size, w, w) + } + + // stroke + ctx.globalAlpha = 1.0; + if (st['marker-line-color'] && st['marker-line-width']) { + if (st['marker-line-opacity']) { + ctx.globalAlpha = st['marker-line-opacity']; + } + if (st['marker-line-width']) { + ctx.lineWidth = st['marker-line-width']; + } + ctx.strokeStyle = st['marker-line-color']; + + // do not render for alpha = 0 + if (ctx.globalAlpha > 0) { + ctx.strokeRect(-pixel_size, -pixel_size, w, w) + } + } + } + + function renderSprite(ctx, img, st) { + + if(img.complete){ + if (st['marker-fill-opacity'] !== undefined || st['marker-opacity'] !== undefined) { + ctx.globalAlpha = st['marker-fill-opacity'] || st['marker-opacity']; + } + ctx.drawImage(img, 0, 0, Math.min(img.width, MAX_SPRITE_RADIUS), Math.min(img.height, MAX_SPRITE_RADIUS)); + } + } + +module.exports = { + renderPoint: renderPoint, + renderSprite: renderSprite, + renderRectangle: renderRectangle, + MAX_SPRITE_RADIUS: MAX_SPRITE_RADIUS +}; + +},{}],23:[function(require,module,exports){ +module.exports = { + cartocss: require('./cartocss_render'), + Point: require('./point'), + Rectangle: require('./rectangle') +}; +},{"./cartocss_render":22,"./point":24,"./rectangle":25}],24:[function(require,module,exports){ +(function (global){ +var torque = require('../'); +var cartocss = require('./cartocss_render'); +var Profiler = require('../profiler'); +var carto = global.carto || require('carto'); +var Filters = require('./torque_filters'); + + var TAU = Math.PI * 2; + var DEFAULT_CARTOCSS = [ + '#layer {', + ' marker-fill: #662506;', + ' marker-width: 4;', + ' [value > 1] { marker-fill: #FEE391; }', + ' [value > 2] { marker-fill: #FEC44F; }', + ' [value > 3] { marker-fill: #FE9929; }', + ' [value > 4] { marker-fill: #EC7014; }', + ' [value > 5] { marker-fill: #CC4C02; }', + ' [value > 6] { marker-fill: #993404; }', + ' [value > 7] { marker-fill: #662506; }', + '}' + ].join('\n'); + + var COMP_OP_TO_CANVAS = { + "src": 'source-over', + "src-over": 'source-over', + "dst-over": 'destination-over', + "src-in": 'source-in', + "dst-in": 'destination-in', + "src-out": 'source-out', + "dst-out": 'destination-out', + "src-atop": 'source-atop', + "dst-atop": 'destination-atop', + "xor": 'xor', + "darken": 'darken', + "lighten": 'lighten' + } + + function compop2canvas(compop) { + return COMP_OP_TO_CANVAS[compop] || compop; + } + + // + // this renderer just render points depending of the value + // + function PointRenderer(canvas, options) { + if (!canvas) { + throw new Error("canvas can't be undefined"); + } + this.options = options; + this._canvas = canvas; + this._ctx = canvas.getContext('2d'); + this._sprites = []; // sprites per layer + this._shader = null; + this._icons = {}; + this._iconsToLoad = 0; + this._filters = new Filters(this._canvas, {canvasClass: options.canvasClass}); + this.setCartoCSS(this.options.cartocss || DEFAULT_CARTOCSS); + this.TILE_SIZE = 256; + this._style = null; + this._gradients = {}; + + this._forcePoints = false; + } + + torque.extend(PointRenderer.prototype, torque.Event, { + + clearCanvas: function() { + var canvas = this._canvas; + var color = this._Map['-torque-clear-color'] + // shortcut for the default value + var ctx = this._ctx; + if (color === "rgba(255, 255, 255, 0)" || !color) { + ctx.save(); + ctx.setTransform(1, 0, 0, 1, 0, 0); + ctx.clearRect(0, 0, canvas.width, canvas.height); + ctx.restore(); + } else { + ctx.setTransform(1, 0, 0, 1, 0, 0); + var compop = this._Map['comp-op'] + ctx.globalCompositeOperation = compop2canvas(compop); + ctx.fillStyle = color; + ctx.fillRect(0, 0, canvas.width, canvas.height); + } + }, + + setCanvas: function(canvas) { + this._canvas = canvas; + this._ctx = canvas.getContext('2d'); + }, + + // + // sets the cartocss style to render stuff + // + setCartoCSS: function(cartocss) { + // clean sprites + this.setShader(new carto.RendererJS().render(cartocss)); + }, + + setShader: function(shader) { + // clean sprites + this._sprites = []; + this._shader = shader; + this._Map = this._shader.getDefault().getStyle({}, { zoom: 0 }); + var img_names = this._shader.getImageURLs(); + this._preloadIcons(img_names); + }, + + clearSpriteCache: function() { + this._sprites = []; + }, + + + // + // generate sprite based on cartocss style + // + generateSprite: function(shader, value, shaderVars) { + var self = this; + var prof = Profiler.metric('torque.renderer.point.generateSprite').start(); + var st = shader.getStyle({ + value: value + }, shaderVars); + if(this._style === null || this._style !== st){ + this._style = st; + } + + var pointSize = st['marker-width']; + if (!pointSize) { + return null; + } + + if (st['marker-opacity'] === 0 && !st['marker-line-opacity']) { + return null; + } + + var canvas = this._createCanvas(); + var ctx = canvas.getContext('2d'); + + var markerFile = st["marker-file"] || st["point-file"]; + var qualifiedUrl = markerFile && this._qualifyURL(markerFile); + + if (qualifiedUrl && this._iconsToLoad <= 0 && this._icons[qualifiedUrl]) { + var img = this._icons[qualifiedUrl]; + + var dWidth = Math.min(st['marker-width'] * 2 || img.width, cartocss.MAX_SPRITE_RADIUS * 2); + var dHeight = Math.min((st['marker-height'] || dWidth) * (img.width / img.height), cartocss.MAX_SPRITE_RADIUS * 2); + + canvas.width = ctx.width = dWidth; + canvas.height = ctx.height = dHeight; + + ctx.scale(dWidth/img.width, dHeight/img.height); + + cartocss.renderSprite(ctx, img, st); + } else { + // take into account the exterior ring to calculate the size + var canvasSize = (st['marker-line-width'] || 0) + pointSize*2; + var w = ctx.width = canvas.width = ctx.height = canvas.height = Math.ceil(canvasSize); + ctx.translate(w/2, w/2); + + var mt = st['marker-type']; + if (mt && mt === 'rectangle') { + cartocss.renderRectangle(ctx, st); + } else { + cartocss.renderPoint(ctx, st); + } + } + prof.end(true); + if (torque.flags.sprites_to_images) { + var i = this._createImage(); + i.src = canvas.toDataURL(); + return i; + } + + return canvas; + }, + + // + // renders all the layers (and frames for each layer) from cartocss + // + renderTile: function(tile, key, callback) { + if (this._iconsToLoad > 0) { + this.on('allIconsLoaded', function() { + this.renderTile.apply(this, [tile, key, callback]); + }); + return false; + } + var prof = Profiler.metric('torque.renderer.point.renderLayers').start(); + var layers = this._shader.getLayers(); + for(var i = 0, n = layers.length; i < n; ++i ) { + var layer = layers[i]; + if (layer.name() !== "Map") { + var sprites = this._sprites[i] || (this._sprites[i] = {}); + // frames for each layer + for(var fr = 0; fr < layer.frames().length; ++fr) { + var frame = layer.frames()[fr]; + var fr_sprites = sprites[frame] || (sprites[frame] = []); + this._renderTile(tile, key - frame, frame, fr_sprites, layer); + } + } + } + + prof.end(true); + + return callback && callback(null); + }, + + _createCanvas: function() { + return this.options.canvasClass + ? new this.options.canvasClass() + : document.createElement('canvas'); + }, + + _createImage: function() { + return this.options.imageClass + ? new this.options.imageClass() + : new Image(); + }, + + _setImageSrc: function(img, url, callback) { + if (this.options.setImageSrc) { + this.options.setImageSrc(img, url, callback); + } else { + img.onload = function(){ + callback(null); + }; + img.onerror = function(){ + callback(new Error('Could not load image')); + }; + img.src = url; + } + }, + + _qualifyURL: function(url) { + if (typeof this.options.qualifyURL !== "undefined"){ + return this.options.qualifyURL(url); + } + else{ + var a = document.createElement('a'); + a.href = url; + return a.href; + } + }, + + // + // renders a tile in the canvas for key defined in + // the torque tile + // + _renderTile: function(tile, key, frame_offset, sprites, shader, shaderVars) { + if (!this._canvas) return; + + var prof = Profiler.metric('torque.renderer.point.renderTile').start(); + var ctx = this._ctx; + var blendMode = compop2canvas(shader.eval('comp-op')) || this.options.blendmode; + if (blendMode) { + ctx.globalCompositeOperation = blendMode; + } + if (this.options.cumulative && key > tile.maxDate) { + //TODO: precache because this tile is not going to change + key = tile.maxDate; + } + var tileMax = this.options.resolution * (this.TILE_SIZE/this.options.resolution - 1) + var activePixels = tile.timeCount[key]; + var anchor = this.options.resolution/2; + if (activePixels) { + var pixelIndex = tile.timeIndex[key]; + for(var p = 0; p < activePixels; ++p) { + var posIdx = tile.renderDataPos[pixelIndex + p]; + var c = tile.renderData[pixelIndex + p]; + if (c) { + var sp = sprites[c]; + if (sp === undefined) { + sp = sprites[c] = this.generateSprite(shader, c, torque.extend({ zoom: tile.z, 'frame-offset': frame_offset }, shaderVars)); + } + if (sp) { + var x = tile.x[posIdx]- (sp.width >> 1) + anchor; + var y = tileMax - tile.y[posIdx] + anchor; // flip mercator + ctx.drawImage(sp, x, y - (sp.height >> 1)); + } + } + } + } + + + prof.end(true); + }, + + setBlendMode: function(b) { + this.options.blendmode = b; + }, + + /** + * get active points for a step in active zoom + * returns a list of bounding boxes [[sw, ne] , [], []] where ne is a {lat: .., lon: ...} obj + * empty list if there is no active pixels + */ + getActivePointsBBox: function(tile, step) { + var positions = []; + var mercator = new torque.Mercator(); + + var tileMax = this.options.resolution * (this.TILE_SIZE/this.options.resolution - 1); + //this.renderer.renderTile(tile, this.key, pos.x, pos.y); + var activePixels = tile.timeCount[step]; + var pixelIndex = tile.timeIndex[step]; + for(var p = 0; p < activePixels; ++p) { + var posIdx = tile.renderDataPos[pixelIndex + p]; + var c = tile.renderData[pixelIndex + p]; + if (c) { + var x = tile.x[posIdx]; + var y = tileMax - tile.y[posIdx]; // flip mercator + positions.push(mercator.tilePixelBBox( + tile.coord.x, + tile.coord.y, + tile.coord.z, + x, y + )); + } + } + return positions; + }, + + // return the value for x, y (tile coordinates) + // null for no value + getValueFor: function(tile, step, px, py) { + var mercator = new torque.Mercator(); + var res = this.options.resolution; + var res2 = res >> 1; + + var tileMax = this.options.resolution * (this.TILE_SIZE/this.options.resolution - 1); + //this.renderer.renderTile(tile, this.key, pos.x, pos.y); + var activePixels = tile.timeCount[step]; + var pixelIndex = tile.timeIndex[step]; + for(var p = 0; p < activePixels; ++p) { + var posIdx = tile.renderDataPos[pixelIndex + p]; + var c = tile.renderData[pixelIndex + p]; + if (c) { + var x = tile.x[posIdx]; + var y = tileMax - tile.y[posIdx]; + var dx = px + res2 - x; + var dy = py + res2 - y; + if (dx >= 0 && dx < res && dy >= 0 && dy < res) { + return { + value: c, + bbox: mercator.tilePixelBBox( + tile.coord.x, + tile.coord.y, + tile.coord.z, + x - res2, y - res2, res + ) + } + } + } + } + return null; + }, + + _preloadIcons: function(img_names) { + var self = this; + + if (img_names.length > 0 && !this._forcePoints) { + + var qualifiedImageUrlSet = Object.keys(img_names.reduce(function(imgNamesMap, imgName) { + var qualifiedUrl = self._qualifyURL(imgName); + if (!self._icons[qualifiedUrl]) { + imgNamesMap[qualifiedUrl] = true; + } + return imgNamesMap; + }, {})); + + var filtered = self._shader.getLayers().some(function(layer) { + return typeof layer.shader["image-filters"] !== "undefined"; + }); + + this._iconsToLoad += qualifiedImageUrlSet.length; + + qualifiedImageUrlSet.forEach(function(qualifiedImageUrl) { + self._icons[qualifiedImageUrl] = null; + + var img = self._createImage(); + + if (filtered) { + img.crossOrigin = 'Anonymous'; + } + + self._setImageSrc(img, qualifiedImageUrl, function(err) { + if (err) { + self._forcePoints = true; + self.clearSpriteCache(); + self._iconsToLoad = 0; + self.fire("allIconsLoaded"); + if(filtered) { + console.info("Only CORS-enabled, or same domain image-files can be used in combination with image-filters"); + } + console.error("Couldn't get marker-file " + qualifiedImageUrl); + } else { + self._icons[qualifiedImageUrl] = img; + self._iconsToLoad--; + + if (self._iconsToLoad <= 0){ + self.clearSpriteCache(); + self.fire("allIconsLoaded"); + } + } + }); + }); + } else { + this.fire("allIconsLoaded"); + } + }, + + applyFilters: function(){ + if(this._style){ + if(this._style['image-filters']){ + function gradientKey(imf){ + var hash = "" + for(var i = 0; i < imf.args.length; i++){ + var rgb = imf.args[i].rgb; + hash += rgb[0] + ":" + rgb[1] + ":" + rgb[2]; + } + return hash; + } + var gradient = this._gradients[gradientKey(this._style['image-filters'])]; + if(!gradient){ + function componentToHex(c) { + var hex = c.toString(16); + return hex.length == 1 ? "0" + hex : hex; + } + + function rgbToHex(r, g, b) { + return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b); + } + gradient = {}; + var colorize = this._style['image-filters'].args; + + var increment = 1/colorize.length; + for (var i = 0; i < colorize.length; i++){ + var key = increment * i + increment; + var rgb = colorize[i].rgb; + var formattedColor = rgbToHex(rgb[0], rgb[1], rgb[2]); + gradient[key] = formattedColor; + } + this._gradients[gradientKey(this._style['image-filters'])] = gradient; + } + this._filters.gradient(gradient); + this._filters.draw(); + } + } + } +}); + + + // exports public api +module.exports = PointRenderer; + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"../":10,"../profiler":17,"./cartocss_render":22,"./torque_filters":26,"carto":36}],25:[function(require,module,exports){ +(function (global){ +var carto = global.carto || require('carto'); + + var DEFAULT_CARTOCSS = [ + '#layer {', + ' polygon-fill: #FFFF00;', + ' [value > 10] { polygon-fill: #FFFF00; }', + ' [value > 100] { polygon-fill: #FFCC00; }', + ' [value > 1000] { polygon-fill: #FE9929; }', + ' [value > 10000] { polygon-fill: #FF6600; }', + ' [value > 100000] { polygon-fill: #FF3300; }', + '}' + ].join('\n'); + + var TAU = Math.PI * 2; + + // + // this renderer just render points depending of the value + // + function RectanbleRenderer(canvas, options) { + this.options = options; + carto.tree.Reference.set(torque['torque-reference']); + this.setCanvas(canvas); + this.setCartoCSS(this.options.cartocss || DEFAULT_CARTOCSS); + } + + RectanbleRenderer.prototype = { + + // + // sets the cartocss style to render stuff + // + setCartoCSS: function(cartocss) { + this._cartoCssStyle = new carto.RendererJS().render(cartocss); + if(this._cartoCssStyle.getLayers().length > 1) { + throw new Error("only one CartoCSS layer is supported"); + } + this._shader = this._cartoCssStyle.getLayers()[0].shader; + }, + + setCanvas: function(canvas) { + if(!canvas) return; + this._canvas = canvas; + this._ctx = canvas.getContext('2d'); + }, + + accumulate: function(tile, keys) { + var prof = Profiler.metric('RectangleRender:accumulate').start(); + var x, y, posIdx, p, k, key, activePixels, pixelIndex; + var res = this.options.resolution; + var s = 256/res; + var accum = new Float32Array(s*s); + + if(typeof(keys) !== 'object') { + keys = [keys]; + } + + for(k = 0; k < keys.length; ++k) { + key = keys[k]; + activePixels = tile.timeCount[key]; + if(activePixels) { + pixelIndex = tile.timeIndex[key]; + for(p = 0; p < activePixels; ++p) { + posIdx = tile.renderDataPos[pixelIndex + p]; + x = tile.x[posIdx]/res; + y = tile.y[posIdx]/res; + accum[x*s + y] += tile.renderData[pixelIndex + p]; + } + } + } + + prof.end(); + return accum; + }, + + renderTileAccum: function(accum, px, py) { + var prof = Profiler.metric('RectangleRender:renderTileAccum').start(); + var color, x, y, alpha; + var res = this.options.resolution; + var ctx = this._ctx; + var s = (256/res) | 0; + var s2 = s*s; + var colors = this._colors; + if(this.options.blendmode) { + ctx.globalCompositeOperation = this.options.blendmode; + } + var polygon_alpha = this._shader['polygon-opacity'] || function() { return 1.0; }; + for(var i = 0; i < s2; ++i) { + var xy = i; + var value = accum[i]; + if(value) { + x = (xy/s) | 0; + y = xy % s; + // by-pass the style generation for improving performance + color = this._shader['polygon-fill']({ value: value }, { zoom: 0 }); + ctx.fillStyle = color; + //TODO: each function should have a default value for each + //property defined in the cartocss + alpha = polygon_alpha({ value: value }, { zoom: 0 }); + if(alpha === null) { + alpha = 1.0; + } + ctx.globalAlpha = alpha; + ctx.fillRect(x * res, 256 - res - y * res, res, res); + } + } + prof.end(); + }, + + // + // renders a tile in the canvas for key defined in + // the torque tile + // + renderTile: function(tile, key, callback) { + if(!this._canvas) return; + + var res = this.options.resolution; + + //var prof = Profiler.get('render').start(); + var ctx = this._ctx; + var colors = this._colors; + var activepixels = tile.timeCount[key]; + if(activepixels) { + var w = this._canvas.width; + var h = this._canvas.height; + //var imageData = ctx.getImageData(0, 0, w, h); + //var pixels = imageData.data; + var pixelIndex = tile.timeIndex[key]; + for(var p = 0; p < activePixels; ++p) { + var posIdx = tile.renderDataPos[pixelIndex + p]; + var c = tile.renderData[pixelIndex + p]; + if(c) { + var color = colors[Math.min(c, colors.length - 1)]; + var x = tile.x[posIdx];// + px; + var y = tile.y[posIdx]; //+ py; + + ctx.fillStyle = color; + ctx.fillRect(x, y, res, res); + /* + + for(var xx = 0; xx < res; ++xx) { + for(var yy = 0; yy < res; ++yy) { + var idx = 4*((x+xx) + w*(y + yy)); + pixels[idx + 0] = color[0]; + pixels[idx + 1] = color[1]; + pixels[idx + 2] = color[2]; + pixels[idx + 3] = color[3]; + } + } + */ + } + } + //ctx.putImageData(imageData, 0, 0); + } + //prof.end(); + return callback && callback(null); + } + }; + + + // exports public api +module.exports = RectanbleRenderer; + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"carto":36}],26:[function(require,module,exports){ +/* + Based on simpleheat, a tiny JavaScript library for drawing heatmaps with Canvas, + by Vladimir Agafonkin + https://github.com/mourner/simpleheat +*/ + +'use strict'; + +function torque_filters(canvas, options) { + // jshint newcap: false, validthis: true + if (!(this instanceof torque_filters)) { return new torque_filters(canvas, options); } + + options = options || {}; + + this._canvas = canvas = typeof canvas === 'string' ? document.getElementById(canvas) : canvas; + + this._ctx = canvas.getContext('2d'); + this._width = canvas.width; + this._height = canvas.height; + + this._max = 1; + this._data = []; + + this.canvasClass = options.canvasClass; +} + +torque_filters.prototype = { + + defaultGradient: { + 0.4: 'blue', + 0.6: 'cyan', + 0.7: 'lime', + 0.8: 'yellow', + 1.0: 'red' + }, + + gradient: function (grad) { + // create a 256x1 gradient that we'll use to turn a grayscale heatmap into a colored one + var canvas = this._createCanvas(), + ctx = canvas.getContext('2d'), + gradient = ctx.createLinearGradient(0, 0, 0, 256); + + canvas.width = 1; + canvas.height = 256; + + for (var i in grad) { + gradient.addColorStop(+i, grad[i]); + } + + ctx.fillStyle = gradient; + ctx.fillRect(0, 0, 1, 256); + + this._grad = ctx.getImageData(0, 0, 1, 256).data; + + return this; + }, + + draw: function () { + if (!this._grad) { + this.gradient(this.defaultGradient); + } + + var ctx = this._ctx; + var colored = ctx.getImageData(0, 0, this._canvas.width, this._canvas.height); + this._colorize(colored.data, this._grad); + ctx.putImageData(colored, 0, 0); + + return this; + }, + + _colorize: function (pixels, gradient) { + for (var i = 3, len = pixels.length, j; i < len; i += 4) { + j = pixels[i] * 4; // get gradient color from opacity value + + if (j) { + pixels[i - 3] = gradient[j]; + pixels[i - 2] = gradient[j + 1]; + pixels[i - 1] = gradient[j + 2]; + } + } + }, + + _createCanvas: function() { + return this.canvasClass + ? new this.canvasClass() + : document.createElement('canvas'); + } +}; + +module.exports = torque_filters; + +},{}],27:[function(require,module,exports){ +(function (global){ +var torque = require('./core'); + + var lastCall = null; + + function jsonp(url, callback, options) { + options = options || {}; + options.timeout = options.timeout === undefined ? 10000: options.timeout; + var head = document.getElementsByTagName('head')[0]; + var script = document.createElement('script'); + + // function name + var fnName = options.callbackName || 'torque_' + Date.now(); + + if (torque.isFunction(fnName)) { + fnName = fnName(); + } + + function clean() { + head.removeChild(script); + clearTimeout(timeoutTimer); + delete window[fnName]; + } + + window[fnName] = function() { + clean(); + callback.apply(window, arguments); + }; + + // timeout for errors + var timeoutTimer = setTimeout(function() { + clean(); + callback.call(window, null); + }, options.timeout); + + // setup url + url = url.replace('callback=\?', 'callback=' + fnName); + script.type = 'text/javascript'; + script.src = url; + script.async = true; + // defer the loading because IE9 loads in the same frame the script + // so Loader._script is null + setTimeout(function() { head.appendChild(script); }, 0); + } + + function get(url, callback, options) { + options = options || { + method: 'GET', + data: null, + responseType: 'text' + }; + lastCall = { url: url, callback: callback }; + var request = XMLHttpRequest; + // from d3.js + if (global.XDomainRequest + && !("withCredentials" in request) + && /^(http(s)?:)?\/\//.test(url)) request = XDomainRequest; + + var req = new request(); + req.open(options.method, url, true); + + + function respond() { + var status = req.status, result; + var r = options.responseType === 'arraybuffer' ? req.response: req.responseText; + if (!status && r || status >= 200 && status < 300 || status === 304) { + callback(req); + } else { + callback(null); + } + } + + "onload" in req + ? req.onload = req.onerror = respond + : req.onreadystatechange = function() { req.readyState > 3 && respond(); }; + + req.onprogress = function() {}; + + req.responseType = options.responseType; //'arraybuffer'; + if (options.data) { + req.setRequestHeader("Content-type", "application/json"); + //req.setRequestHeader("Content-type", "application/x-www-form-urlencoded") + req.setRequestHeader("Accept", "*"); + } + req.send(options.data); + return req; + } + + function post(url, data, callback) { + return get(url, callback, { + data: data, + method: "POST" + }); + } + +module.exports = { + get: get, + post: post, + jsonp: jsonp, + lastCall: function() { return lastCall; } +}; + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"./core":4}],28:[function(require,module,exports){ + +},{}],29:[function(require,module,exports){ +// http://wiki.commonjs.org/wiki/Unit_Testing/1.0 +// +// THIS IS NOT TESTED NOR LIKELY TO WORK OUTSIDE V8! +// +// Originally from narwhal.js (http://narwhaljs.org) +// Copyright (c) 2009 Thomas Robinson <280north.com> +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the 'Software'), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +// when used in node, this will actually load the util module we depend on +// versus loading the builtin util module as happens otherwise +// this is a bug in node module loading as far as I am concerned +var util = require('util/'); + +var pSlice = Array.prototype.slice; +var hasOwn = Object.prototype.hasOwnProperty; + +// 1. The assert module provides functions that throw +// AssertionError's when particular conditions are not met. The +// assert module must conform to the following interface. + +var assert = module.exports = ok; + +// 2. The AssertionError is defined in assert. +// new assert.AssertionError({ message: message, +// actual: actual, +// expected: expected }) + +assert.AssertionError = function AssertionError(options) { + this.name = 'AssertionError'; + this.actual = options.actual; + this.expected = options.expected; + this.operator = options.operator; + if (options.message) { + this.message = options.message; + this.generatedMessage = false; + } else { + this.message = getMessage(this); + this.generatedMessage = true; + } + var stackStartFunction = options.stackStartFunction || fail; + + if (Error.captureStackTrace) { + Error.captureStackTrace(this, stackStartFunction); + } + else { + // non v8 browsers so we can have a stacktrace + var err = new Error(); + if (err.stack) { + var out = err.stack; + + // try to strip useless frames + var fn_name = stackStartFunction.name; + var idx = out.indexOf('\n' + fn_name); + if (idx >= 0) { + // once we have located the function frame + // we need to strip out everything before it (and its line) + var next_line = out.indexOf('\n', idx + 1); + out = out.substring(next_line + 1); + } + + this.stack = out; + } + } +}; + +// assert.AssertionError instanceof Error +util.inherits(assert.AssertionError, Error); + +function replacer(key, value) { + if (util.isUndefined(value)) { + return '' + value; + } + if (util.isNumber(value) && (isNaN(value) || !isFinite(value))) { + return value.toString(); + } + if (util.isFunction(value) || util.isRegExp(value)) { + return value.toString(); + } + return value; +} + +function truncate(s, n) { + if (util.isString(s)) { + return s.length < n ? s : s.slice(0, n); + } else { + return s; + } +} + +function getMessage(self) { + return truncate(JSON.stringify(self.actual, replacer), 128) + ' ' + + self.operator + ' ' + + truncate(JSON.stringify(self.expected, replacer), 128); +} + +// At present only the three keys mentioned above are used and +// understood by the spec. Implementations or sub modules can pass +// other keys to the AssertionError's constructor - they will be +// ignored. + +// 3. All of the following functions must throw an AssertionError +// when a corresponding condition is not met, with a message that +// may be undefined if not provided. All assertion methods provide +// both the actual and expected values to the assertion error for +// display purposes. + +function fail(actual, expected, message, operator, stackStartFunction) { + throw new assert.AssertionError({ + message: message, + actual: actual, + expected: expected, + operator: operator, + stackStartFunction: stackStartFunction + }); +} + +// EXTENSION! allows for well behaved errors defined elsewhere. +assert.fail = fail; + +// 4. Pure assertion tests whether a value is truthy, as determined +// by !!guard. +// assert.ok(guard, message_opt); +// This statement is equivalent to assert.equal(true, !!guard, +// message_opt);. To test strictly for the value true, use +// assert.strictEqual(true, guard, message_opt);. + +function ok(value, message) { + if (!value) fail(value, true, message, '==', assert.ok); +} +assert.ok = ok; + +// 5. The equality assertion tests shallow, coercive equality with +// ==. +// assert.equal(actual, expected, message_opt); + +assert.equal = function equal(actual, expected, message) { + if (actual != expected) fail(actual, expected, message, '==', assert.equal); +}; + +// 6. The non-equality assertion tests for whether two objects are not equal +// with != assert.notEqual(actual, expected, message_opt); + +assert.notEqual = function notEqual(actual, expected, message) { + if (actual == expected) { + fail(actual, expected, message, '!=', assert.notEqual); + } +}; + +// 7. The equivalence assertion tests a deep equality relation. +// assert.deepEqual(actual, expected, message_opt); + +assert.deepEqual = function deepEqual(actual, expected, message) { + if (!_deepEqual(actual, expected)) { + fail(actual, expected, message, 'deepEqual', assert.deepEqual); + } +}; + +function _deepEqual(actual, expected) { + // 7.1. All identical values are equivalent, as determined by ===. + if (actual === expected) { + return true; + + } else if (util.isBuffer(actual) && util.isBuffer(expected)) { + if (actual.length != expected.length) return false; + + for (var i = 0; i < actual.length; i++) { + if (actual[i] !== expected[i]) return false; + } + + return true; + + // 7.2. If the expected value is a Date object, the actual value is + // equivalent if it is also a Date object that refers to the same time. + } else if (util.isDate(actual) && util.isDate(expected)) { + return actual.getTime() === expected.getTime(); + + // 7.3 If the expected value is a RegExp object, the actual value is + // equivalent if it is also a RegExp object with the same source and + // properties (`global`, `multiline`, `lastIndex`, `ignoreCase`). + } else if (util.isRegExp(actual) && util.isRegExp(expected)) { + return actual.source === expected.source && + actual.global === expected.global && + actual.multiline === expected.multiline && + actual.lastIndex === expected.lastIndex && + actual.ignoreCase === expected.ignoreCase; + + // 7.4. Other pairs that do not both pass typeof value == 'object', + // equivalence is determined by ==. + } else if (!util.isObject(actual) && !util.isObject(expected)) { + return actual == expected; + + // 7.5 For all other Object pairs, including Array objects, equivalence is + // determined by having the same number of owned properties (as verified + // with Object.prototype.hasOwnProperty.call), the same set of keys + // (although not necessarily the same order), equivalent values for every + // corresponding key, and an identical 'prototype' property. Note: this + // accounts for both named and indexed properties on Arrays. + } else { + return objEquiv(actual, expected); + } +} + +function isArguments(object) { + return Object.prototype.toString.call(object) == '[object Arguments]'; +} + +function objEquiv(a, b) { + if (util.isNullOrUndefined(a) || util.isNullOrUndefined(b)) + return false; + // an identical 'prototype' property. + if (a.prototype !== b.prototype) return false; + //~~~I've managed to break Object.keys through screwy arguments passing. + // Converting to array solves the problem. + if (isArguments(a)) { + if (!isArguments(b)) { + return false; + } + a = pSlice.call(a); + b = pSlice.call(b); + return _deepEqual(a, b); + } + try { + var ka = objectKeys(a), + kb = objectKeys(b), + key, i; + } catch (e) {//happens when one is a string literal and the other isn't + return false; + } + // having the same number of owned properties (keys incorporates + // hasOwnProperty) + if (ka.length != kb.length) + return false; + //the same set of keys (although not necessarily the same order), + ka.sort(); + kb.sort(); + //~~~cheap key test + for (i = ka.length - 1; i >= 0; i--) { + if (ka[i] != kb[i]) + return false; + } + //equivalent values for every corresponding key, and + //~~~possibly expensive deep test + for (i = ka.length - 1; i >= 0; i--) { + key = ka[i]; + if (!_deepEqual(a[key], b[key])) return false; + } + return true; +} + +// 8. The non-equivalence assertion tests for any deep inequality. +// assert.notDeepEqual(actual, expected, message_opt); + +assert.notDeepEqual = function notDeepEqual(actual, expected, message) { + if (_deepEqual(actual, expected)) { + fail(actual, expected, message, 'notDeepEqual', assert.notDeepEqual); + } +}; + +// 9. The strict equality assertion tests strict equality, as determined by ===. +// assert.strictEqual(actual, expected, message_opt); + +assert.strictEqual = function strictEqual(actual, expected, message) { + if (actual !== expected) { + fail(actual, expected, message, '===', assert.strictEqual); + } +}; + +// 10. The strict non-equality assertion tests for strict inequality, as +// determined by !==. assert.notStrictEqual(actual, expected, message_opt); + +assert.notStrictEqual = function notStrictEqual(actual, expected, message) { + if (actual === expected) { + fail(actual, expected, message, '!==', assert.notStrictEqual); + } +}; + +function expectedException(actual, expected) { + if (!actual || !expected) { + return false; + } + + if (Object.prototype.toString.call(expected) == '[object RegExp]') { + return expected.test(actual); + } else if (actual instanceof expected) { + return true; + } else if (expected.call({}, actual) === true) { + return true; + } + + return false; +} + +function _throws(shouldThrow, block, expected, message) { + var actual; + + if (util.isString(expected)) { + message = expected; + expected = null; + } + + try { + block(); + } catch (e) { + actual = e; + } + + message = (expected && expected.name ? ' (' + expected.name + ').' : '.') + + (message ? ' ' + message : '.'); + + if (shouldThrow && !actual) { + fail(actual, expected, 'Missing expected exception' + message); + } + + if (!shouldThrow && expectedException(actual, expected)) { + fail(actual, expected, 'Got unwanted exception' + message); + } + + if ((shouldThrow && actual && expected && + !expectedException(actual, expected)) || (!shouldThrow && actual)) { + throw actual; + } +} + +// 11. Expected to throw an error: +// assert.throws(block, Error_opt, message_opt); + +assert.throws = function(block, /*optional*/error, /*optional*/message) { + _throws.apply(this, [true].concat(pSlice.call(arguments))); +}; + +// EXTENSION! This is annoying to write outside this module. +assert.doesNotThrow = function(block, /*optional*/message) { + _throws.apply(this, [false].concat(pSlice.call(arguments))); +}; + +assert.ifError = function(err) { if (err) {throw err;}}; + +var objectKeys = Object.keys || function (obj) { + var keys = []; + for (var key in obj) { + if (hasOwn.call(obj, key)) keys.push(key); + } + return keys; +}; + +},{"util/":34}],30:[function(require,module,exports){ +(function (process){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// resolves . and .. elements in a path array with directory names there +// must be no slashes, empty elements, or device names (c:\) in the array +// (so also no leading and trailing slashes - it does not distinguish +// relative and absolute paths) +function normalizeArray(parts, allowAboveRoot) { + // if the path tries to go above the root, `up` ends up > 0 + var up = 0; + for (var i = parts.length - 1; i >= 0; i--) { + var last = parts[i]; + if (last === '.') { + parts.splice(i, 1); + } else if (last === '..') { + parts.splice(i, 1); + up++; + } else if (up) { + parts.splice(i, 1); + up--; + } + } + + // if the path is allowed to go above the root, restore leading ..s + if (allowAboveRoot) { + for (; up--; up) { + parts.unshift('..'); + } + } + + return parts; +} + +// Split a filename into [root, dir, basename, ext], unix version +// 'root' is just a slash, or nothing. +var splitPathRe = + /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/; +var splitPath = function(filename) { + return splitPathRe.exec(filename).slice(1); +}; + +// path.resolve([from ...], to) +// posix version +exports.resolve = function() { + var resolvedPath = '', + resolvedAbsolute = false; + + for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) { + var path = (i >= 0) ? arguments[i] : process.cwd(); + + // Skip empty and invalid entries + if (typeof path !== 'string') { + throw new TypeError('Arguments to path.resolve must be strings'); + } else if (!path) { + continue; + } + + resolvedPath = path + '/' + resolvedPath; + resolvedAbsolute = path.charAt(0) === '/'; + } + + // At this point the path should be resolved to a full absolute path, but + // handle relative paths to be safe (might happen when process.cwd() fails) + + // Normalize the path + resolvedPath = normalizeArray(filter(resolvedPath.split('/'), function(p) { + return !!p; + }), !resolvedAbsolute).join('/'); + + return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.'; +}; + +// path.normalize(path) +// posix version +exports.normalize = function(path) { + var isAbsolute = exports.isAbsolute(path), + trailingSlash = substr(path, -1) === '/'; + + // Normalize the path + path = normalizeArray(filter(path.split('/'), function(p) { + return !!p; + }), !isAbsolute).join('/'); + + if (!path && !isAbsolute) { + path = '.'; + } + if (path && trailingSlash) { + path += '/'; + } + + return (isAbsolute ? '/' : '') + path; +}; + +// posix version +exports.isAbsolute = function(path) { + return path.charAt(0) === '/'; +}; + +// posix version +exports.join = function() { + var paths = Array.prototype.slice.call(arguments, 0); + return exports.normalize(filter(paths, function(p, index) { + if (typeof p !== 'string') { + throw new TypeError('Arguments to path.join must be strings'); + } + return p; + }).join('/')); +}; + + +// path.relative(from, to) +// posix version +exports.relative = function(from, to) { + from = exports.resolve(from).substr(1); + to = exports.resolve(to).substr(1); + + function trim(arr) { + var start = 0; + for (; start < arr.length; start++) { + if (arr[start] !== '') break; + } + + var end = arr.length - 1; + for (; end >= 0; end--) { + if (arr[end] !== '') break; + } + + if (start > end) return []; + return arr.slice(start, end - start + 1); + } + + var fromParts = trim(from.split('/')); + var toParts = trim(to.split('/')); + + var length = Math.min(fromParts.length, toParts.length); + var samePartsLength = length; + for (var i = 0; i < length; i++) { + if (fromParts[i] !== toParts[i]) { + samePartsLength = i; + break; + } + } + + var outputParts = []; + for (var i = samePartsLength; i < fromParts.length; i++) { + outputParts.push('..'); + } + + outputParts = outputParts.concat(toParts.slice(samePartsLength)); + + return outputParts.join('/'); +}; + +exports.sep = '/'; +exports.delimiter = ':'; + +exports.dirname = function(path) { + var result = splitPath(path), + root = result[0], + dir = result[1]; + + if (!root && !dir) { + // No dirname whatsoever + return '.'; + } + + if (dir) { + // It has a dirname, strip trailing slash + dir = dir.substr(0, dir.length - 1); + } + + return root + dir; +}; + + +exports.basename = function(path, ext) { + var f = splitPath(path)[2]; + // TODO: make this comparison case-insensitive on windows? + if (ext && f.substr(-1 * ext.length) === ext) { + f = f.substr(0, f.length - ext.length); + } + return f; +}; + + +exports.extname = function(path) { + return splitPath(path)[3]; +}; + +function filter (xs, f) { + if (xs.filter) return xs.filter(f); + var res = []; + for (var i = 0; i < xs.length; i++) { + if (f(xs[i], i, xs)) res.push(xs[i]); + } + return res; +} + +// String.prototype.substr - negative index don't work in IE8 +var substr = 'ab'.substr(-1) === 'b' + ? function (str, start, len) { return str.substr(start, len) } + : function (str, start, len) { + if (start < 0) start = str.length + start; + return str.substr(start, len); + } +; + +}).call(this,require('_process')) +},{"_process":31}],31:[function(require,module,exports){ +// shim for using process in browser + +var process = module.exports = {}; + +process.nextTick = (function () { + var canSetImmediate = typeof window !== 'undefined' + && window.setImmediate; + var canMutationObserver = typeof window !== 'undefined' + && window.MutationObserver; + var canPost = typeof window !== 'undefined' + && window.postMessage && window.addEventListener + ; + + if (canSetImmediate) { + return function (f) { return window.setImmediate(f) }; + } + + var queue = []; + + if (canMutationObserver) { + var hiddenDiv = document.createElement("div"); + var observer = new MutationObserver(function () { + var queueList = queue.slice(); + queue.length = 0; + queueList.forEach(function (fn) { + fn(); + }); + }); + + observer.observe(hiddenDiv, { attributes: true }); + + return function nextTick(fn) { + if (!queue.length) { + hiddenDiv.setAttribute('yes', 'no'); + } + queue.push(fn); + }; + } + + if (canPost) { + window.addEventListener('message', function (ev) { + var source = ev.source; + if ((source === window || source === null) && ev.data === 'process-tick') { + ev.stopPropagation(); + if (queue.length > 0) { + var fn = queue.shift(); + fn(); + } + } + }, true); + + return function nextTick(fn) { + queue.push(fn); + window.postMessage('process-tick', '*'); + }; + } + + return function nextTick(fn) { + setTimeout(fn, 0); + }; +})(); + +process.title = 'browser'; +process.browser = true; +process.env = {}; +process.argv = []; + +function noop() {} + +process.on = noop; +process.addListener = noop; +process.once = noop; +process.off = noop; +process.removeListener = noop; +process.removeAllListeners = noop; +process.emit = noop; + +process.binding = function (name) { + throw new Error('process.binding is not supported'); +}; + +// TODO(shtylman) +process.cwd = function () { return '/' }; +process.chdir = function (dir) { + throw new Error('process.chdir is not supported'); +}; + +},{}],32:[function(require,module,exports){ +if (typeof Object.create === 'function') { + // implementation from standard node.js 'util' module + module.exports = function inherits(ctor, superCtor) { + ctor.super_ = superCtor + ctor.prototype = Object.create(superCtor.prototype, { + constructor: { + value: ctor, + enumerable: false, + writable: true, + configurable: true + } + }); + }; +} else { + // old school shim for old browsers + module.exports = function inherits(ctor, superCtor) { + ctor.super_ = superCtor + var TempCtor = function () {} + TempCtor.prototype = superCtor.prototype + ctor.prototype = new TempCtor() + ctor.prototype.constructor = ctor + } +} + +},{}],33:[function(require,module,exports){ +module.exports = function isBuffer(arg) { + return arg && typeof arg === 'object' + && typeof arg.copy === 'function' + && typeof arg.fill === 'function' + && typeof arg.readUInt8 === 'function'; +} +},{}],34:[function(require,module,exports){ +(function (process,global){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var formatRegExp = /%[sdj%]/g; +exports.format = function(f) { + if (!isString(f)) { + var objects = []; + for (var i = 0; i < arguments.length; i++) { + objects.push(inspect(arguments[i])); + } + return objects.join(' '); + } + + var i = 1; + var args = arguments; + var len = args.length; + var str = String(f).replace(formatRegExp, function(x) { + if (x === '%%') return '%'; + if (i >= len) return x; + switch (x) { + case '%s': return String(args[i++]); + case '%d': return Number(args[i++]); + case '%j': + try { + return JSON.stringify(args[i++]); + } catch (_) { + return '[Circular]'; + } + default: + return x; + } + }); + for (var x = args[i]; i < len; x = args[++i]) { + if (isNull(x) || !isObject(x)) { + str += ' ' + x; + } else { + str += ' ' + inspect(x); + } + } + return str; +}; + + +// Mark that a method should not be used. +// Returns a modified function which warns once by default. +// If --no-deprecation is set, then it is a no-op. +exports.deprecate = function(fn, msg) { + // Allow for deprecating things in the process of starting up. + if (isUndefined(global.process)) { + return function() { + return exports.deprecate(fn, msg).apply(this, arguments); + }; + } + + if (process.noDeprecation === true) { + return fn; + } + + var warned = false; + function deprecated() { + if (!warned) { + if (process.throwDeprecation) { + throw new Error(msg); + } else if (process.traceDeprecation) { + console.trace(msg); + } else { + console.error(msg); + } + warned = true; + } + return fn.apply(this, arguments); + } + + return deprecated; +}; + + +var debugs = {}; +var debugEnviron; +exports.debuglog = function(set) { + if (isUndefined(debugEnviron)) + debugEnviron = process.env.NODE_DEBUG || ''; + set = set.toUpperCase(); + if (!debugs[set]) { + if (new RegExp('\\b' + set + '\\b', 'i').test(debugEnviron)) { + var pid = process.pid; + debugs[set] = function() { + var msg = exports.format.apply(exports, arguments); + console.error('%s %d: %s', set, pid, msg); + }; + } else { + debugs[set] = function() {}; + } + } + return debugs[set]; +}; + + +/** + * Echos the value of a value. Trys to print the value out + * in the best way possible given the different types. + * + * @param {Object} obj The object to print out. + * @param {Object} opts Optional options object that alters the output. + */ +/* legacy: obj, showHidden, depth, colors*/ +function inspect(obj, opts) { + // default options + var ctx = { + seen: [], + stylize: stylizeNoColor + }; + // legacy... + if (arguments.length >= 3) ctx.depth = arguments[2]; + if (arguments.length >= 4) ctx.colors = arguments[3]; + if (isBoolean(opts)) { + // legacy... + ctx.showHidden = opts; + } else if (opts) { + // got an "options" object + exports._extend(ctx, opts); + } + // set default options + if (isUndefined(ctx.showHidden)) ctx.showHidden = false; + if (isUndefined(ctx.depth)) ctx.depth = 2; + if (isUndefined(ctx.colors)) ctx.colors = false; + if (isUndefined(ctx.customInspect)) ctx.customInspect = true; + if (ctx.colors) ctx.stylize = stylizeWithColor; + return formatValue(ctx, obj, ctx.depth); +} +exports.inspect = inspect; + + +// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics +inspect.colors = { + 'bold' : [1, 22], + 'italic' : [3, 23], + 'underline' : [4, 24], + 'inverse' : [7, 27], + 'white' : [37, 39], + 'grey' : [90, 39], + 'black' : [30, 39], + 'blue' : [34, 39], + 'cyan' : [36, 39], + 'green' : [32, 39], + 'magenta' : [35, 39], + 'red' : [31, 39], + 'yellow' : [33, 39] +}; + +// Don't use 'blue' not visible on cmd.exe +inspect.styles = { + 'special': 'cyan', + 'number': 'yellow', + 'boolean': 'yellow', + 'undefined': 'grey', + 'null': 'bold', + 'string': 'green', + 'date': 'magenta', + // "name": intentionally not styling + 'regexp': 'red' +}; + + +function stylizeWithColor(str, styleType) { + var style = inspect.styles[styleType]; + + if (style) { + return '\u001b[' + inspect.colors[style][0] + 'm' + str + + '\u001b[' + inspect.colors[style][1] + 'm'; + } else { + return str; + } +} + + +function stylizeNoColor(str, styleType) { + return str; +} + + +function arrayToHash(array) { + var hash = {}; + + array.forEach(function(val, idx) { + hash[val] = true; + }); + + return hash; +} + + +function formatValue(ctx, value, recurseTimes) { + // Provide a hook for user-specified inspect functions. + // Check that value is an object with an inspect function on it + if (ctx.customInspect && + value && + isFunction(value.inspect) && + // Filter out the util module, it's inspect function is special + value.inspect !== exports.inspect && + // Also filter out any prototype objects using the circular check. + !(value.constructor && value.constructor.prototype === value)) { + var ret = value.inspect(recurseTimes, ctx); + if (!isString(ret)) { + ret = formatValue(ctx, ret, recurseTimes); + } + return ret; + } + + // Primitive types cannot have properties + var primitive = formatPrimitive(ctx, value); + if (primitive) { + return primitive; + } + + // Look up the keys of the object. + var keys = Object.keys(value); + var visibleKeys = arrayToHash(keys); + + if (ctx.showHidden) { + keys = Object.getOwnPropertyNames(value); + } + + // IE doesn't make error fields non-enumerable + // http://msdn.microsoft.com/en-us/library/ie/dww52sbt(v=vs.94).aspx + if (isError(value) + && (keys.indexOf('message') >= 0 || keys.indexOf('description') >= 0)) { + return formatError(value); + } + + // Some type of object without properties can be shortcutted. + if (keys.length === 0) { + if (isFunction(value)) { + var name = value.name ? ': ' + value.name : ''; + return ctx.stylize('[Function' + name + ']', 'special'); + } + if (isRegExp(value)) { + return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); + } + if (isDate(value)) { + return ctx.stylize(Date.prototype.toString.call(value), 'date'); + } + if (isError(value)) { + return formatError(value); + } + } + + var base = '', array = false, braces = ['{', '}']; + + // Make Array say that they are Array + if (isArray(value)) { + array = true; + braces = ['[', ']']; + } + + // Make functions say that they are functions + if (isFunction(value)) { + var n = value.name ? ': ' + value.name : ''; + base = ' [Function' + n + ']'; + } + + // Make RegExps say that they are RegExps + if (isRegExp(value)) { + base = ' ' + RegExp.prototype.toString.call(value); + } + + // Make dates with properties first say the date + if (isDate(value)) { + base = ' ' + Date.prototype.toUTCString.call(value); + } + + // Make error with message first say the error + if (isError(value)) { + base = ' ' + formatError(value); + } + + if (keys.length === 0 && (!array || value.length == 0)) { + return braces[0] + base + braces[1]; + } + + if (recurseTimes < 0) { + if (isRegExp(value)) { + return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); + } else { + return ctx.stylize('[Object]', 'special'); + } + } + + ctx.seen.push(value); + + var output; + if (array) { + output = formatArray(ctx, value, recurseTimes, visibleKeys, keys); + } else { + output = keys.map(function(key) { + return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array); + }); + } + + ctx.seen.pop(); + + return reduceToSingleString(output, base, braces); +} + + +function formatPrimitive(ctx, value) { + if (isUndefined(value)) + return ctx.stylize('undefined', 'undefined'); + if (isString(value)) { + var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '') + .replace(/'/g, "\\'") + .replace(/\\"/g, '"') + '\''; + return ctx.stylize(simple, 'string'); + } + if (isNumber(value)) + return ctx.stylize('' + value, 'number'); + if (isBoolean(value)) + return ctx.stylize('' + value, 'boolean'); + // For some reason typeof null is "object", so special case here. + if (isNull(value)) + return ctx.stylize('null', 'null'); +} + + +function formatError(value) { + return '[' + Error.prototype.toString.call(value) + ']'; +} + + +function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { + var output = []; + for (var i = 0, l = value.length; i < l; ++i) { + if (hasOwnProperty(value, String(i))) { + output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, + String(i), true)); + } else { + output.push(''); + } + } + keys.forEach(function(key) { + if (!key.match(/^\d+$/)) { + output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, + key, true)); + } + }); + return output; +} + + +function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) { + var name, str, desc; + desc = Object.getOwnPropertyDescriptor(value, key) || { value: value[key] }; + if (desc.get) { + if (desc.set) { + str = ctx.stylize('[Getter/Setter]', 'special'); + } else { + str = ctx.stylize('[Getter]', 'special'); + } + } else { + if (desc.set) { + str = ctx.stylize('[Setter]', 'special'); + } + } + if (!hasOwnProperty(visibleKeys, key)) { + name = '[' + key + ']'; + } + if (!str) { + if (ctx.seen.indexOf(desc.value) < 0) { + if (isNull(recurseTimes)) { + str = formatValue(ctx, desc.value, null); + } else { + str = formatValue(ctx, desc.value, recurseTimes - 1); + } + if (str.indexOf('\n') > -1) { + if (array) { + str = str.split('\n').map(function(line) { + return ' ' + line; + }).join('\n').substr(2); + } else { + str = '\n' + str.split('\n').map(function(line) { + return ' ' + line; + }).join('\n'); + } + } + } else { + str = ctx.stylize('[Circular]', 'special'); + } + } + if (isUndefined(name)) { + if (array && key.match(/^\d+$/)) { + return str; + } + name = JSON.stringify('' + key); + if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) { + name = name.substr(1, name.length - 2); + name = ctx.stylize(name, 'name'); + } else { + name = name.replace(/'/g, "\\'") + .replace(/\\"/g, '"') + .replace(/(^"|"$)/g, "'"); + name = ctx.stylize(name, 'string'); + } + } + + return name + ': ' + str; +} + + +function reduceToSingleString(output, base, braces) { + var numLinesEst = 0; + var length = output.reduce(function(prev, cur) { + numLinesEst++; + if (cur.indexOf('\n') >= 0) numLinesEst++; + return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1; + }, 0); + + if (length > 60) { + return braces[0] + + (base === '' ? '' : base + '\n ') + + ' ' + + output.join(',\n ') + + ' ' + + braces[1]; + } + + return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1]; +} + + +// NOTE: These type checking functions intentionally don't use `instanceof` +// because it is fragile and can be easily faked with `Object.create()`. +function isArray(ar) { + return Array.isArray(ar); +} +exports.isArray = isArray; + +function isBoolean(arg) { + return typeof arg === 'boolean'; +} +exports.isBoolean = isBoolean; + +function isNull(arg) { + return arg === null; +} +exports.isNull = isNull; + +function isNullOrUndefined(arg) { + return arg == null; +} +exports.isNullOrUndefined = isNullOrUndefined; + +function isNumber(arg) { + return typeof arg === 'number'; +} +exports.isNumber = isNumber; + +function isString(arg) { + return typeof arg === 'string'; +} +exports.isString = isString; + +function isSymbol(arg) { + return typeof arg === 'symbol'; +} +exports.isSymbol = isSymbol; + +function isUndefined(arg) { + return arg === void 0; +} +exports.isUndefined = isUndefined; + +function isRegExp(re) { + return isObject(re) && objectToString(re) === '[object RegExp]'; +} +exports.isRegExp = isRegExp; + +function isObject(arg) { + return typeof arg === 'object' && arg !== null; +} +exports.isObject = isObject; + +function isDate(d) { + return isObject(d) && objectToString(d) === '[object Date]'; +} +exports.isDate = isDate; + +function isError(e) { + return isObject(e) && + (objectToString(e) === '[object Error]' || e instanceof Error); +} +exports.isError = isError; + +function isFunction(arg) { + return typeof arg === 'function'; +} +exports.isFunction = isFunction; + +function isPrimitive(arg) { + return arg === null || + typeof arg === 'boolean' || + typeof arg === 'number' || + typeof arg === 'string' || + typeof arg === 'symbol' || // ES6 symbol + typeof arg === 'undefined'; +} +exports.isPrimitive = isPrimitive; + +exports.isBuffer = require('./support/isBuffer'); + +function objectToString(o) { + return Object.prototype.toString.call(o); +} + + +function pad(n) { + return n < 10 ? '0' + n.toString(10) : n.toString(10); +} + + +var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', + 'Oct', 'Nov', 'Dec']; + +// 26 Feb 16:19:34 +function timestamp() { + var d = new Date(); + var time = [pad(d.getHours()), + pad(d.getMinutes()), + pad(d.getSeconds())].join(':'); + return [d.getDate(), months[d.getMonth()], time].join(' '); +} + + +// log is just a thin wrapper to console.log that prepends a timestamp +exports.log = function() { + console.log('%s - %s', timestamp(), exports.format.apply(exports, arguments)); +}; + + +/** + * Inherit the prototype methods from one constructor into another. + * + * The Function.prototype.inherits from lang.js rewritten as a standalone + * function (not on Function.prototype). NOTE: If this file is to be loaded + * during bootstrapping this function needs to be rewritten using some native + * functions as prototype setup using normal JavaScript does not work as + * expected during bootstrapping (see mirror.js in r114903). + * + * @param {function} ctor Constructor function which needs to inherit the + * prototype. + * @param {function} superCtor Constructor function to inherit prototype from. + */ +exports.inherits = require('inherits'); + +exports._extend = function(origin, add) { + // Don't do anything if add isn't an object + if (!add || !isObject(add)) return origin; + + var keys = Object.keys(add); + var i = keys.length; + while (i--) { + origin[keys[i]] = add[keys[i]]; + } + return origin; +}; + +function hasOwnProperty(obj, prop) { + return Object.prototype.hasOwnProperty.call(obj, prop); +} + +}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"./support/isBuffer":33,"_process":31,"inherits":32}],35:[function(require,module,exports){ +(function (tree) { + +tree.functions = { + rgb: function (r, g, b) { + return this.rgba(r, g, b, 1.0); + }, + rgba: function (r, g, b, a) { + var rgb = [r, g, b].map(function (c) { return number(c); }); + a = number(a); + if (rgb.some(isNaN) || isNaN(a)) return null; + return new tree.Color(rgb, a); + }, + // Only require val + stop: function (val) { + var color, mode; + if (arguments.length > 1) color = arguments[1]; + if (arguments.length > 2) mode = arguments[2]; + + return { + is: 'tag', + val: val, + color: color, + mode: mode, + toString: function(env) { + return '\n\t'; + } + }; + }, + hsl: function (h, s, l) { + return this.hsla(h, s, l, 1.0); + }, + hsla: function (h, s, l, a) { + h = (number(h) % 360) / 360; + s = number(s); l = number(l); a = number(a); + if ([h, s, l, a].some(isNaN)) return null; + + var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s, + m1 = l * 2 - m2; + + return this.rgba(hue(h + 1/3) * 255, + hue(h) * 255, + hue(h - 1/3) * 255, + a); + + function hue(h) { + h = h < 0 ? h + 1 : (h > 1 ? h - 1 : h); + if (h * 6 < 1) return m1 + (m2 - m1) * h * 6; + else if (h * 2 < 1) return m2; + else if (h * 3 < 2) return m1 + (m2 - m1) * (2/3 - h) * 6; + else return m1; + } + }, + hue: function (color) { + if (!('toHSL' in color)) return null; + return new tree.Dimension(Math.round(color.toHSL().h)); + }, + saturation: function (color) { + if (!('toHSL' in color)) return null; + return new tree.Dimension(Math.round(color.toHSL().s * 100), '%'); + }, + lightness: function (color) { + if (!('toHSL' in color)) return null; + return new tree.Dimension(Math.round(color.toHSL().l * 100), '%'); + }, + alpha: function (color) { + if (!('toHSL' in color)) return null; + return new tree.Dimension(color.toHSL().a); + }, + saturate: function (color, amount) { + if (!('toHSL' in color)) return null; + var hsl = color.toHSL(); + + hsl.s += amount.value / 100; + hsl.s = clamp(hsl.s); + return hsla(hsl); + }, + desaturate: function (color, amount) { + if (!('toHSL' in color)) return null; + var hsl = color.toHSL(); + + hsl.s -= amount.value / 100; + hsl.s = clamp(hsl.s); + return hsla(hsl); + }, + lighten: function (color, amount) { + if (!('toHSL' in color)) return null; + var hsl = color.toHSL(); + + hsl.l += amount.value / 100; + hsl.l = clamp(hsl.l); + return hsla(hsl); + }, + darken: function (color, amount) { + if (!('toHSL' in color)) return null; + var hsl = color.toHSL(); + + hsl.l -= amount.value / 100; + hsl.l = clamp(hsl.l); + return hsla(hsl); + }, + fadein: function (color, amount) { + if (!('toHSL' in color)) return null; + var hsl = color.toHSL(); + + hsl.a += amount.value / 100; + hsl.a = clamp(hsl.a); + return hsla(hsl); + }, + fadeout: function (color, amount) { + if (!('toHSL' in color)) return null; + var hsl = color.toHSL(); + + hsl.a -= amount.value / 100; + hsl.a = clamp(hsl.a); + return hsla(hsl); + }, + spin: function (color, amount) { + if (!('toHSL' in color)) return null; + var hsl = color.toHSL(); + var hue = (hsl.h + amount.value) % 360; + + hsl.h = hue < 0 ? 360 + hue : hue; + + return hsla(hsl); + }, + replace: function (entity, a, b) { + if (entity.is === 'field') { + return entity.toString + '.replace(' + a.toString() + ', ' + b.toString() + ')'; + } else { + return entity.replace(a, b); + } + }, + // + // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein + // http://sass-lang.com + // + mix: function (color1, color2, weight) { + var p = weight.value / 100.0; + var w = p * 2 - 1; + var a = color1.toHSL().a - color2.toHSL().a; + + var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; + var w2 = 1 - w1; + + var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2, + color1.rgb[1] * w1 + color2.rgb[1] * w2, + color1.rgb[2] * w1 + color2.rgb[2] * w2]; + + var alpha = color1.alpha * p + color2.alpha * (1 - p); + + return new tree.Color(rgb, alpha); + }, + greyscale: function (color) { + return this.desaturate(color, new tree.Dimension(100)); + }, + '%': function (quoted /* arg, arg, ...*/) { + var args = Array.prototype.slice.call(arguments, 1), + str = quoted.value; + + for (var i = 0; i < args.length; i++) { + str = str.replace(/%s/, args[i].value) + .replace(/%[da]/, args[i].toString()); + } + str = str.replace(/%%/g, '%'); + return new tree.Quoted(str); + } +}; + +var image_filter_functors = [ + 'emboss', 'blur', 'gray', 'sobel', 'edge-detect', + 'x-gradient', 'y-gradient', 'sharpen']; + +for (var i = 0; i < image_filter_functors.length; i++) { + var f = image_filter_functors[i]; + tree.functions[f] = (function(f) { + return function() { + return new tree.ImageFilter(f); + }; + })(f); +} + +tree.functions['agg-stack-blur'] = function(x, y) { + return new tree.ImageFilter('agg-stack-blur', [x, y]); +}; + +tree.functions['scale-hsla'] = function(h0,h1,s0,s1,l0,l1,a0,a1) { + return new tree.ImageFilter('scale-hsla', [h0,h1,s0,s1,l0,l1,a0,a1]); +}; + +function hsla(h) { + return tree.functions.hsla(h.h, h.s, h.l, h.a); +} + +function number(n) { + if (n instanceof tree.Dimension) { + return parseFloat(n.unit == '%' ? n.value / 100 : n.value); + } else if (typeof(n) === 'number') { + return n; + } else { + return NaN; + } +} + +function clamp(val) { + return Math.min(1, Math.max(0, val)); +} + +})(require('./tree')); + +},{"./tree":41}],36:[function(require,module,exports){ +(function (process,__dirname){ +var util = require('util'), + fs = require('fs'), + path = require('path'); + + +function getVersion() { + if (process.browser) { + return require('../../package.json').version.split('.'); + } else if (parseInt(process.version.split('.')[1], 10) > 4) { + return require('../../package.json').version.split('.'); + } else { + // older node + var package_json = JSON.parse(fs.readFileSync(path.join(__dirname,'../../package.json'))); + return package_json.version.split('.'); + } +} + +var carto = { + version: getVersion(), + Parser: require('./parser').Parser, + Renderer: require('./renderer').Renderer, + tree: require('./tree'), + RendererJS: require('./renderer_js'), + default_reference: require('./torque-reference'), + + // @TODO + writeError: function(ctx, options) { + var message = ''; + var extract = ctx.extract; + var error = []; + + options = options || {}; + + if (options.silent) { return; } + + options.indent = options.indent || ''; + + if (!('index' in ctx) || !extract) { + return util.error(options.indent + (ctx.stack || ctx.message)); + } + + if (typeof(extract[0]) === 'string') { + error.push(stylize((ctx.line - 1) + ' ' + extract[0], 'grey')); + } + + if (extract[1] === '' && typeof extract[2] === 'undefined') { + extract[1] = '¶'; + } + error.push(ctx.line + ' ' + extract[1].slice(0, ctx.column) + + stylize(stylize(extract[1][ctx.column], 'bold') + + extract[1].slice(ctx.column + 1), 'yellow')); + + if (typeof(extract[2]) === 'string') { + error.push(stylize((ctx.line + 1) + ' ' + extract[2], 'grey')); + } + error = options.indent + error.join('\n' + options.indent) + '\033[0m\n'; + + message = options.indent + message + stylize(ctx.message, 'red'); + if (ctx.filename) (message += stylize(' in ', 'red') + ctx.filename); + + util.error(message, error); + + if (ctx.callLine) { + util.error(stylize('from ', 'red') + (ctx.filename || '')); + util.error(stylize(ctx.callLine, 'grey') + ' ' + ctx.callExtract); + } + if (ctx.stack) { util.error(stylize(ctx.stack, 'red')); } + } +}; + +require('./tree/call'); +require('./tree/color'); +require('./tree/comment'); +require('./tree/definition'); +require('./tree/dimension'); +require('./tree/element'); +require('./tree/expression'); +require('./tree/filterset'); +require('./tree/filter'); +require('./tree/field'); +require('./tree/keyword'); +require('./tree/layer'); +require('./tree/literal'); +require('./tree/operation'); +require('./tree/quoted'); +require('./tree/imagefilter'); +require('./tree/reference'); +require('./tree/rule'); +require('./tree/ruleset'); +require('./tree/selector'); +require('./tree/style'); +require('./tree/url'); +require('./tree/value'); +require('./tree/variable'); +require('./tree/zoom'); +require('./tree/invalid'); +require('./tree/fontset'); +require('./tree/frame_offset'); +require('./functions'); + +for (var k in carto) { exports[k] = carto[k]; } + +// Stylize a string +function stylize(str, style) { + var styles = { + 'bold' : [1, 22], + 'inverse' : [7, 27], + 'underline' : [4, 24], + 'yellow' : [33, 39], + 'green' : [32, 39], + 'red' : [31, 39], + 'grey' : [90, 39] + }; + return '\033[' + styles[style][0] + 'm' + str + + '\033[' + styles[style][1] + 'm'; +} + +}).call(this,require('_process'),"/node_modules/carto/lib/carto") +},{"../../package.json":71,"./functions":35,"./parser":37,"./renderer":38,"./renderer_js":39,"./torque-reference":40,"./tree":41,"./tree/call":42,"./tree/color":43,"./tree/comment":44,"./tree/definition":45,"./tree/dimension":46,"./tree/element":47,"./tree/expression":48,"./tree/field":49,"./tree/filter":50,"./tree/filterset":51,"./tree/fontset":52,"./tree/frame_offset":53,"./tree/imagefilter":54,"./tree/invalid":55,"./tree/keyword":56,"./tree/layer":57,"./tree/literal":58,"./tree/operation":59,"./tree/quoted":60,"./tree/reference":61,"./tree/rule":62,"./tree/ruleset":63,"./tree/selector":64,"./tree/style":65,"./tree/url":66,"./tree/value":67,"./tree/variable":68,"./tree/zoom":69,"_process":31,"fs":28,"path":30,"util":34}],37:[function(require,module,exports){ +(function (global){ +var carto = exports, + tree = require('./tree'), + _ = global._ || require('underscore'); + +// Token matching is done with the `$` function, which either takes +// a terminal string or regexp, or a non-terminal function to call. +// It also takes care of moving all the indices forwards. +carto.Parser = function Parser(env) { + var input, // LeSS input string + i, // current index in `input` + j, // current chunk + temp, // temporarily holds a chunk's state, for backtracking + memo, // temporarily holds `i`, when backtracking + furthest, // furthest index the parser has gone to + chunks, // chunkified input + current, // index of current chunk, in `input` + parser; + + var that = this; + + // This function is called after all files + // have been imported through `@import`. + var finish = function() {}; + + function save() { + temp = chunks[j]; + memo = i; + current = i; + } + function restore() { + chunks[j] = temp; + i = memo; + current = i; + } + + function sync() { + if (i > current) { + chunks[j] = chunks[j].slice(i - current); + current = i; + } + } + // + // Parse from a token, regexp or string, and move forward if match + // + function $(tok) { + var match, args, length, c, index, endIndex, k; + + // Non-terminal + if (tok instanceof Function) { + return tok.call(parser.parsers); + // Terminal + // Either match a single character in the input, + // or match a regexp in the current chunk (chunk[j]). + } else if (typeof(tok) === 'string') { + match = input.charAt(i) === tok ? tok : null; + length = 1; + sync(); + } else { + sync(); + + match = tok.exec(chunks[j]); + if (match) { + length = match[0].length; + } else { + return null; + } + } + + // The match is confirmed, add the match length to `i`, + // and consume any extra white-space characters (' ' || '\n') + // which come after that. The reason for this is that LeSS's + // grammar is mostly white-space insensitive. + if (match) { + var mem = i += length; + endIndex = i + chunks[j].length - length; + + while (i < endIndex) { + c = input.charCodeAt(i); + if (! (c === 32 || c === 10 || c === 9)) { break; } + i++; + } + chunks[j] = chunks[j].slice(length + (i - mem)); + current = i; + + if (chunks[j].length === 0 && j < chunks.length - 1) { j++; } + + if (typeof(match) === 'string') { + return match; + } else { + return match.length === 1 ? match[0] : match; + } + } + } + + // Same as $(), but don't change the state of the parser, + // just return the match. + function peek(tok) { + if (typeof(tok) === 'string') { + return input.charAt(i) === tok; + } else { + return !!tok.test(chunks[j]); + } + } + + function extractErrorLine(style, errorIndex) { + return (style.slice(0, errorIndex).match(/\n/g) || '').length + 1; + } + + + // Make an error object from a passed set of properties. + // Accepted properties: + // - `message`: Text of the error message. + // - `filename`: Filename where the error occurred. + // - `index`: Char. index where the error occurred. + function makeError(err) { + var einput; + + _(err).defaults({ + index: furthest, + filename: env.filename, + message: 'Parse error.', + line: 0, + column: -1 + }); + + if (err.filename && that.env.inputs && that.env.inputs[err.filename]) { + einput = that.env.inputs[err.filename]; + } else { + einput = input; + } + + err.line = extractErrorLine(einput, err.index); + for (var n = err.index; n >= 0 && einput.charAt(n) !== '\n'; n--) { + err.column++; + } + + return new Error(_('<%=filename%>:<%=line%>:<%=column%> <%=message%>').template(err)); + } + + this.env = env = env || {}; + this.env.filename = this.env.filename || null; + this.env.inputs = this.env.inputs || {}; + + // The Parser + parser = { + + extractErrorLine: extractErrorLine, + // + // Parse an input string into an abstract syntax tree. + // Throws an error on parse errors. + parse: function(str) { + var root, start, end, zone, line, lines, buff = [], c, error = null; + + i = j = current = furthest = 0; + chunks = []; + input = str.replace(/\r\n/g, '\n'); + if (env.filename) { + that.env.inputs[env.filename] = input; + } + + var early_exit = false; + + // Split the input into chunks. + chunks = (function (chunks) { + var j = 0, + skip = /(?:@\{[\w-]+\}|[^"'`\{\}\/\(\)\\])+/g, + comment = /\/\*(?:[^*]|\*+[^\/*])*\*+\/|\/\/.*/g, + string = /"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'|`((?:[^`]|\\.)*)`/g, + level = 0, + match, + chunk = chunks[0], + inParam; + + for (var i = 0, c, cc; i < input.length;) { + skip.lastIndex = i; + if (match = skip.exec(input)) { + if (match.index === i) { + i += match[0].length; + chunk.push(match[0]); + } + } + c = input.charAt(i); + comment.lastIndex = string.lastIndex = i; + + if (match = string.exec(input)) { + if (match.index === i) { + i += match[0].length; + chunk.push(match[0]); + continue; + } + } + + if (!inParam && c === '/') { + cc = input.charAt(i + 1); + if (cc === '/' || cc === '*') { + if (match = comment.exec(input)) { + if (match.index === i) { + i += match[0].length; + chunk.push(match[0]); + continue; + } + } + } + } + + switch (c) { + case '{': if (! inParam) { level ++; chunk.push(c); break; } + case '}': if (! inParam) { level --; chunk.push(c); chunks[++j] = chunk = []; break; } + case '(': if (! inParam) { inParam = true; chunk.push(c); break; } + case ')': if ( inParam) { inParam = false; chunk.push(c); break; } + default: chunk.push(c); + } + + i++; + } + if (level !== 0) { + error = { + index: i - 1, + type: 'Parse', + message: (level > 0) ? "missing closing `}`" : "missing opening `{`" + }; + } + + return chunks.map(function (c) { return c.join(''); }); + })([[]]); + + if (error) { + throw makeError(error); + } + + // Start with the primary rule. + // The whole syntax tree is held under a Ruleset node, + // with the `root` property set to true, so no `{}` are + // output. + root = new tree.Ruleset([], $(this.parsers.primary)); + root.root = true; + + // Get an array of Ruleset objects, flattened + // and sorted according to specificitySort + root.toList = (function() { + var line, lines, column; + return function(env) { + env.error = function(e) { + if (!env.errors) env.errors = new Error(''); + if (env.errors.message) { + env.errors.message += '\n' + makeError(e).message; + } else { + env.errors.message = makeError(e).message; + } + }; + env.frames = env.frames || []; + + + // call populates Invalid-caused errors + var definitions = this.flatten([], [], env); + definitions.sort(specificitySort); + return definitions; + }; + })(); + + // Sort rules by specificity: this function expects selectors to be + // split already. + // + // Written to be used as a .sort(Function); + // argument. + // + // [1, 0, 0, 467] > [0, 0, 1, 520] + var specificitySort = function(a, b) { + var as = a.specificity; + var bs = b.specificity; + + if (as[0] != bs[0]) return bs[0] - as[0]; + if (as[1] != bs[1]) return bs[1] - as[1]; + if (as[2] != bs[2]) return bs[2] - as[2]; + return bs[3] - as[3]; + }; + + return root; + }, + + // Here in, the parsing rules/functions + // + // The basic structure of the syntax tree generated is as follows: + // + // Ruleset -> Rule -> Value -> Expression -> Entity + // + // In general, most rules will try to parse a token with the `$()` function, and if the return + // value is truly, will return a new node, of the relevant type. Sometimes, we need to check + // first, before parsing, that's when we use `peek()`. + parsers: { + // The `primary` rule is the *entry* and *exit* point of the parser. + // The rules here can appear at any level of the parse tree. + // + // The recursive nature of the grammar is an interplay between the `block` + // rule, which represents `{ ... }`, the `ruleset` rule, and this `primary` rule, + // as represented by this simplified grammar: + // + // primary → (ruleset | rule)+ + // ruleset → selector+ block + // block → '{' primary '}' + // + // Only at one point is the primary rule not called from the + // block rule: at the root level. + primary: function() { + var node, root = []; + + while ((node = $(this.rule) || $(this.ruleset) || + $(this.comment)) || + $(/^[\s\n]+/) || (node = $(this.invalid))) { + if (node) root.push(node); + } + return root; + }, + + invalid: function () { + var chunk = $(/^[^;\n]*[;\n]/); + + // To fail gracefully, match everything until a semicolon or linebreak. + if (chunk) { + return new tree.Invalid(chunk, memo); + } + }, + + // We create a Comment node for CSS comments `/* */`, + // but keep the LeSS comments `//` silent, by just skipping + // over them. + comment: function() { + var comment; + + if (input.charAt(i) !== '/') return; + + if (input.charAt(i + 1) === '/') { + return new tree.Comment($(/^\/\/.*/), true); + } else if (comment = $(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/)) { + return new tree.Comment(comment); + } + }, + + // Entities are tokens which can be found inside an Expression + entities: { + + // A string, which supports escaping " and ' "milky way" 'he\'s the one!' + quoted: function() { + if (input.charAt(i) !== '"' && input.charAt(i) !== "'") return; + var str = $(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/); + if (str) { + return new tree.Quoted(str[1] || str[2]); + } + }, + + // A reference to a Mapnik field, like [NAME] + // Behind the scenes, this has the same representation, but Carto + // needs to be careful to warn when unsupported operations are used. + field: function() { + if (! $('[')) return; + var field_name = $(/(^[^\]]+)/); + if (! $(']')) return; + if (field_name) return new tree.Field(field_name[1]); + }, + + // This is a comparison operator + comparison: function() { + var str = $(/^=~|=|!=|<=|>=|<|>/); + if (str) { + return str; + } + }, + + // A catch-all word, such as: hard-light + // These can start with either a letter or a dash (-), + // and then contain numbers, underscores, and letters. + keyword: function() { + var k = $(/^[A-Za-z-]+[A-Za-z-0-9_]*/); + if (k) { return new tree.Keyword(k); } + }, + + // A function call like rgb(255, 0, 255) + // The arguments are parsed with the `entities.arguments` parser. + call: function() { + var name, args; + + if (!(name = /^([\w\-]+|%)\(/.exec(chunks[j]))) return; + + name = name[1]; + + if (name === 'url') { + // url() is handled by the url parser instead + return null; + } else { + i += name.length; + } + + $('('); // Parse the '(' and consume whitespace. + + args = $(this.entities['arguments']); + + if (!$(')')) return; + + if (name) { + return new tree.Call(name, args, i); + } + }, + // Arguments are comma-separated expressions + 'arguments': function() { + var args = [], arg; + + while (arg = $(this.expression)) { + args.push(arg); + if (! $(',')) { break; } + } + + return args; + }, + literal: function() { + return $(this.entities.dimension) || + $(this.entities.keywordcolor) || + $(this.entities.hexcolor) || + $(this.entities.quoted); + }, + + // Parse url() tokens + // + // We use a specific rule for urls, because they don't really behave like + // standard function calls. The difference is that the argument doesn't have + // to be enclosed within a string, so it can't be parsed as an Expression. + url: function() { + var value; + + if (input.charAt(i) !== 'u' || !$(/^url\(/)) return; + value = $(this.entities.quoted) || $(this.entities.variable) || + $(/^[\-\w%@$\/.&=:;#+?~]+/) || ''; + if (! $(')')) { + return new tree.Invalid(value, memo, 'Missing closing ) in URL.'); + } else { + return new tree.URL((typeof value.value !== 'undefined' || + value instanceof tree.Variable) ? + value : new tree.Quoted(value)); + } + }, + + // A Variable entity, such as `@fink`, in + // + // width: @fink + 2px + // + // We use a different parser for variable definitions, + // see `parsers.variable`. + variable: function() { + var name, index = i; + + if (input.charAt(i) === '@' && (name = $(/^@[\w-]+/))) { + return new tree.Variable(name, index, env.filename); + } + }, + + hexcolor: function() { + var rgb; + if (input.charAt(i) === '#' && (rgb = $(/^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})/))) { + return new tree.Color(rgb[1]); + } + }, + + keywordcolor: function() { + var rgb = chunks[j].match(/^[a-z]+/); + if (rgb && rgb[0] in tree.Reference.data.colors) { + return new tree.Color(tree.Reference.data.colors[$(/^[a-z]+/)]); + } + }, + + // A Dimension, that is, a number and a unit. The only + // unit that has an effect is % + dimension: function() { + var c = input.charCodeAt(i); + if ((c > 57 || c < 45) || c === 47) return; + var value = $(/^(-?\d*\.?\d+(?:[eE][-+]?\d+)?)(\%|\w+)?/); + if (value) { + return new tree.Dimension(value[1], value[2], memo); + } + } + }, + + // The variable part of a variable definition. + // Used in the `rule` parser. Like @fink: + variable: function() { + var name; + + if (input.charAt(i) === '@' && (name = $(/^(@[\w-]+)\s*:/))) { + return name[1]; + } + }, + + // Entities are the smallest recognized token, + // and can be found inside a rule's value. + entity: function() { + return $(this.entities.call) || + $(this.entities.literal) || + $(this.entities.field) || + $(this.entities.variable) || + $(this.entities.url) || + $(this.entities.keyword); + }, + + // A Rule terminator. Note that we use `peek()` to check for '}', + // because the `block` rule will be expecting it, but we still need to make sure + // it's there, if ';' was ommitted. + end: function() { + return $(';') || peek('}'); + }, + + // Elements are the building blocks for Selectors. They consist of + // an element name, such as a tag a class, or `*`. + element: function() { + var e = $(/^(?:[.#][\w\-]+|\*|Map)/); + if (e) return new tree.Element(e); + }, + + // Attachments allow adding multiple lines, polygons etc. to an + // object. There can only be one attachment per selector. + attachment: function() { + var s = $(/^::([\w\-]+(?:\/[\w\-]+)*)/); + if (s) return s[1]; + }, + + // Selectors are made out of one or more Elements, see above. + selector: function() { + var a, attachment, + e, elements = [], + f, filters = new tree.Filterset(), + z, zooms = [], + frame_offset = tree.FrameOffset.none; + segments = 0, conditions = 0; + + while ( + (e = $(this.element)) || + (z = $(this.zoom)) || + (fo = $(this.frame_offset)) || + (f = $(this.filter)) || + (a = $(this.attachment)) + ) { + segments++; + if (e) { + elements.push(e); + } else if (z) { + zooms.push(z); + conditions++; + } else if (fo) { + frame_offset = fo; + conditions++; + } else if (f) { + var err = filters.add(f); + if (err) { + throw makeError({ + message: err, + index: i - 1 + }); + } + conditions++; + } else if (attachment) { + throw makeError({ + message: 'Encountered second attachment name.', + index: i - 1 + }); + } else { + attachment = a; + } + + var c = input.charAt(i); + if (c === '{' || c === '}' || c === ';' || c === ',') { break; } + } + + if (segments) { + return new tree.Selector(filters, zooms, frame_offset, elements, attachment, conditions, memo); + } + }, + + filter: function() { + save(); + var key, op, val; + if (! $('[')) return; + if (key = $(/^[a-zA-Z0-9\-_]+/) || + $(this.entities.quoted) || + $(this.entities.variable) || + $(this.entities.keyword) || + $(this.entities.field)) { + // TODO: remove at 1.0.0 + if (key instanceof tree.Quoted) { + key = new tree.Field(key.toString()); + } + if ((op = $(this.entities.comparison)) && + (val = $(this.entities.quoted) || + $(this.entities.variable) || + $(this.entities.dimension) || + $(this.entities.keyword) || + $(this.entities.field))) { + if (! $(']')) { + throw makeError({ + message: 'Missing closing ] of filter.', + index: memo - 1 + }); + } + if (!key.is) key = new tree.Field(key); + return new tree.Filter(key, op, val, memo, env.filename); + } + } + }, + + frame_offset: function() { + save(); + var op, val; + if ($(/^\[\s*frame-offset/g) && + (op = $(this.entities.comparison)) && + (val = $(/^\d+/)) && + $(']')) { + return tree.FrameOffset(op, val, memo); + } + }, + + zoom: function() { + save(); + var op, val; + if ($(/^\[\s*zoom/g) && + (op = $(this.entities.comparison)) && + (val = $(this.entities.variable) || $(this.entities.dimension)) && $(']')) { + return new tree.Zoom(op, val, memo); + } else { + // backtrack + restore(); + } + }, + + // The `block` rule is used by `ruleset` + // It's a wrapper around the `primary` rule, with added `{}`. + block: function() { + var content; + + if ($('{') && (content = $(this.primary)) && $('}')) { + return content; + } + }, + + // div, .class, body > p {...} + ruleset: function() { + var selectors = [], s, f, l, rules, filters = []; + save(); + + while (s = $(this.selector)) { + selectors.push(s); + while ($(this.comment)) {} + if (! $(',')) { break; } + while ($(this.comment)) {} + } + if (s) { + while ($(this.comment)) {} + } + + if (selectors.length > 0 && (rules = $(this.block))) { + if (selectors.length === 1 && + selectors[0].elements.length && + selectors[0].elements[0].value === 'Map') { + var rs = new tree.Ruleset(selectors, rules); + rs.isMap = true; + return rs; + } + return new tree.Ruleset(selectors, rules); + } else { + // Backtrack + restore(); + } + }, + + rule: function() { + var name, value, c = input.charAt(i); + save(); + + if (c === '.' || c === '#') { return; } + + if (name = $(this.variable) || $(this.property)) { + value = $(this.value); + + if (value && $(this.end)) { + return new tree.Rule(name, value, memo, env.filename); + } else { + furthest = i; + restore(); + } + } + }, + + font: function() { + var value = [], expression = [], weight, font, e; + + while (e = $(this.entity)) { + expression.push(e); + } + + value.push(new tree.Expression(expression)); + + if ($(',')) { + while (e = $(this.expression)) { + value.push(e); + if (! $(',')) { break; } + } + } + return new tree.Value(value); + }, + + // A Value is a comma-delimited list of Expressions + // In a Rule, a Value represents everything after the `:`, + // and before the `;`. + value: function() { + var e, expressions = []; + + while (e = $(this.expression)) { + expressions.push(e); + if (! $(',')) { break; } + } + + if (expressions.length > 1) { + return new tree.Value(expressions.map(function(e) { + return e.value[0]; + })); + } else if (expressions.length === 1) { + return new tree.Value(expressions); + } + }, + // A sub-expression, contained by parenthensis + sub: function() { + var e; + + if ($('(') && (e = $(this.expression)) && $(')')) { + return e; + } + }, + // This is a misnomer because it actually handles multiplication + // and division. + multiplication: function() { + var m, a, op, operation; + if (m = $(this.operand)) { + while ((op = ($('/') || $('*') || $('%'))) && (a = $(this.operand))) { + operation = new tree.Operation(op, [operation || m, a], memo); + } + return operation || m; + } + }, + addition: function() { + var m, a, op, operation; + if (m = $(this.multiplication)) { + while ((op = $(/^[-+]\s+/) || (input.charAt(i - 1) != ' ' && ($('+') || $('-')))) && + (a = $(this.multiplication))) { + operation = new tree.Operation(op, [operation || m, a], memo); + } + return operation || m; + } + }, + + // An operand is anything that can be part of an operation, + // such as a Color, or a Variable + operand: function() { + return $(this.sub) || $(this.entity); + }, + + // Expressions either represent mathematical operations, + // or white-space delimited Entities. @var * 2 + expression: function() { + var e, delim, entities = [], d; + + while (e = $(this.addition) || $(this.entity)) { + entities.push(e); + } + + if (entities.length > 0) { + return new tree.Expression(entities); + } + }, + property: function() { + var name = $(/^(([a-z][-a-z_0-9]*\/)?\*?-?[-a-z_0-9]+)\s*:/); + if (name) return name[1]; + } + } + }; + return parser; +}; + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"./tree":41,"underscore":102}],38:[function(require,module,exports){ +(function (global){ +var _ = global._ || require('underscore'); +var carto = require('./index'); + +carto.Renderer = function Renderer(env, options) { + this.env = env || {}; + this.options = options || {}; + this.options.mapnik_version = this.options.mapnik_version || '3.0.0'; +}; + +/** + * Prepare a MSS document (given as an string) into a + * XML Style fragment (mostly useful for debugging) + * + * @param {String} data the mss contents as a string. + */ +carto.Renderer.prototype.renderMSS = function render(data) { + // effects is a container for side-effects, which currently + // are limited to FontSets. + var env = _(this.env).defaults({ + benchmark: false, + validation_data: false, + effects: [] + }); + + if (!carto.tree.Reference.setVersion(this.options.mapnik_version)) { + throw new Error("Could not set mapnik version to " + this.options.mapnik_version); + } + + var output = []; + var styles = []; + + if (env.benchmark) console.time('Parsing MSS'); + var parser = (carto.Parser(env)).parse(data); + if (env.benchmark) console.timeEnd('Parsing MSS'); + + if (env.benchmark) console.time('Rule generation'); + var rule_list = parser.toList(env); + if (env.benchmark) console.timeEnd('Rule generation'); + + if (env.benchmark) console.time('Rule inheritance'); + var rules = inheritDefinitions(rule_list, env); + if (env.benchmark) console.timeEnd('Rule inheritance'); + + if (env.benchmark) console.time('Style sort'); + var sorted = sortStyles(rules,env); + if (env.benchmark) console.timeEnd('Style sort'); + + if (env.benchmark) console.time('Total Style generation'); + for (var k = 0, rule, style_name; k < sorted.length; k++) { + rule = sorted[k]; + style_name = 'style' + (rule.attachment !== '__default__' ? '-' + rule.attachment : ''); + styles.push(style_name); + var bench_name = '\tStyle "'+style_name+'" (#'+k+') toXML'; + if (env.benchmark) console.time(bench_name); + // env.effects can be modified by this call + output.push(carto.tree.StyleXML(style_name, rule.attachment, rule, env)); + if (env.benchmark) console.timeEnd(bench_name); + } + if (env.benchmark) console.timeEnd('Total Style generation'); + if (env.errors) throw env.errors; + return output.join('\n'); +}; + +/** + * Prepare a MML document (given as an object) into a + * fully-localized XML file ready for Mapnik2 consumption + * + * @param {String} m - the JSON file as a string. + */ +carto.Renderer.prototype.render = function render(m) { + // effects is a container for side-effects, which currently + // are limited to FontSets. + var env = _(this.env).defaults({ + benchmark: false, + validation_data: false, + effects: [], + ppi: 90.714 + }); + + if (!carto.tree.Reference.setVersion(this.options.mapnik_version)) { + throw new Error("Could not set mapnik version to " + this.options.mapnik_version); + } + + var output = []; + + // Transform stylesheets into definitions. + var definitions = _(m.Stylesheet).chain() + .map(function(s) { + if (typeof s == 'string') { + throw new Error("Stylesheet object is expected not a string: '" + s + "'"); + } + // Passing the environment from stylesheet to stylesheet, + // allows frames and effects to be maintained. + env = _(env).extend({filename:s.id}); + + var time = +new Date(), + root = (carto.Parser(env)).parse(s.data); + if (env.benchmark) + console.warn('Parsing time: ' + (new Date() - time) + 'ms'); + return root.toList(env); + }) + .flatten() + .value(); + + function appliesTo(name, classIndex) { + return function(definition) { + return definition.appliesTo(l.name, classIndex); + }; + } + + // Iterate through layers and create styles custom-built + // for each of them, and apply those styles to the layers. + var styles, l, classIndex, rules, sorted, matching; + for (var i = 0; i < m.Layer.length; i++) { + l = m.Layer[i]; + styles = []; + classIndex = {}; + + if (env.benchmark) console.warn('processing layer: ' + l.id); + // Classes are given as space-separated alphanumeric strings. + var classes = (l['class'] || '').split(/\s+/g); + for (var j = 0; j < classes.length; j++) { + classIndex[classes[j]] = true; + } + matching = definitions.filter(appliesTo(l.name, classIndex)); + rules = inheritDefinitions(matching, env); + sorted = sortStyles(rules, env); + + for (var k = 0, rule, style_name; k < sorted.length; k++) { + rule = sorted[k]; + style_name = l.name + (rule.attachment !== '__default__' ? '-' + rule.attachment : ''); + + // env.effects can be modified by this call + var styleXML = carto.tree.StyleXML(style_name, rule.attachment, rule, env); + + if (styleXML) { + output.push(styleXML); + styles.push(style_name); + } + } + + output.push(carto.tree.LayerXML(l, styles)); + } + + output.unshift(env.effects.map(function(e) { + return e.toXML(env); + }).join('\n')); + + var map_properties = getMapProperties(m, definitions, env); + + // Exit on errors. + if (env.errors) throw env.errors; + + // Pass TileJSON and other custom parameters through to Mapnik XML. + var parameters = _(m).reduce(function(memo, v, k) { + if (!v && v !== 0) return memo; + + switch (k) { + // Known skippable properties. + case 'srs': + case 'Layer': + case 'Stylesheet': + break; + // Non URL-bound TileJSON properties. + case 'bounds': + case 'center': + case 'minzoom': + case 'maxzoom': + case 'version': + memo.push(' ' + v + ''); + break; + // Properties that require CDATA. + case 'name': + case 'description': + case 'legend': + case 'attribution': + case 'template': + memo.push(' '); + break; + // Mapnik image format. + case 'format': + memo.push(' ' + v + ''); + break; + // Mapnik interactivity settings. + case 'interactivity': + memo.push(' ' + v.layer + ''); + memo.push(' ' + v.fields + ''); + break; + // Support any additional scalar properties. + default: + if ('string' === typeof v) { + memo.push(' '); + } else if ('number' === typeof v) { + memo.push(' ' + v + ''); + } else if ('boolean' === typeof v) { + memo.push(' ' + v + ''); + } + break; + } + return memo; + }, []); + if (parameters.length) output.unshift( + '\n' + + parameters.join('\n') + + '\n\n' + ); + + var properties = _(map_properties).map(function(v) { return ' ' + v; }).join(''); + + output.unshift( + '\n' + + '\n' + + '\n'); + output.push(''); + return output.join('\n'); +}; + +/** + * This function currently modifies 'current' + * @param {Array} current current list of rules + * @param {Object} definition a Definition object to add to the rules + * @param {Object} byFilter an object/dictionary of existing filters. This is + * actually keyed `attachment->filter` + * @param {Object} env the current environment +*/ +function addRules(current, definition, byFilter, env) { + var newFilters = definition.filters, + newRules = definition.rules, + updatedFilters, clone, previous; + + // The current definition might have been split up into + // multiple definitions already. + for (var k = 0; k < current.length; k++) { + updatedFilters = current[k].filters.cloneWith(newFilters); + if (updatedFilters) { + previous = byFilter[updatedFilters]; + if (previous) { + // There's already a definition with those exact + // filters. Add the current definitions' rules + // and stop processing it as the existing rule + // has already gone down the inheritance chain. + previous.addRules(newRules); + } else { + clone = current[k].clone(updatedFilters); + // Make sure that we're only maintaining the clone + // when we did actually add rules. If not, there's + // no need to keep the clone around. + if (clone.addRules(newRules)) { + // We inserted an element before this one, so we need + // to make sure that in the next loop iteration, we're + // not performing the same task for this element again, + // hence the k++. + byFilter[updatedFilters] = clone; + current.splice(k, 0, clone); + k++; + } + } + } else if (updatedFilters === null) { + // if updatedFilters is null, then adding the filters doesn't + // invalidate or split the selector, so we addRules to the + // combined selector + + // Filters can be added, but they don't change the + // filters. This means we don't have to split the + // definition. + // + // this is cloned here because of shared classes, see + // sharedclass.mss + current[k] = current[k].clone(); + current[k].addRules(newRules); + } + // if updatedFeatures is false, then the filters split the rule, + // so they aren't the same inheritance chain + } + return current; +} + +/** + * Apply inherited styles from their ancestors to them. + * + * called either once per render (in the case of mss) or per layer + * (for mml) + * + * @param {Object} definitions - a list of definitions objects + * that contain .rules + * @param {Object} env - the environment + * @return {Array} an array of arrays is returned, + * in which each array refers to a specific attachment + */ +function inheritDefinitions(definitions, env) { + var inheritTime = +new Date(); + // definitions are ordered by specificity, + // high (index 0) to low + var byAttachment = {}, + byFilter = {}; + var result = []; + var current, previous, attachment; + + // Evaluate the filters specified by each definition with the given + // environment to correctly resolve variable references + definitions.forEach(function(d) { + d.filters.ev(env); + }); + + for (var i = 0; i < definitions.length; i++) { + + attachment = definitions[i].attachment; + current = [definitions[i]]; + + if (!byAttachment[attachment]) { + byAttachment[attachment] = []; + byAttachment[attachment].attachment = attachment; + byFilter[attachment] = {}; + result.push(byAttachment[attachment]); + } + + // Iterate over all subsequent rules. + for (var j = i + 1; j < definitions.length; j++) { + if (definitions[j].attachment === attachment) { + // Only inherit rules from the same attachment. + current = addRules(current, definitions[j], byFilter[attachment], env); + } + } + + for (var k = 0; k < current.length; k++) { + byFilter[attachment][current[k].filters] = current[k]; + byAttachment[attachment].push(current[k]); + } + } + + if (env.benchmark) console.warn('Inheritance time: ' + ((new Date() - inheritTime)) + 'ms'); + + return result; + +} + +// Sort styles by the minimum index of their rules. +// This sorts a slice of the styles, so it returns a sorted +// array but does not change the input. +function sortStylesIndex(a, b) { return b.index - a.index; } +function sortStyles(styles, env) { + for (var i = 0; i < styles.length; i++) { + var style = styles[i]; + style.index = Infinity; + for (var b = 0; b < style.length; b++) { + var rules = style[b].rules; + for (var r = 0; r < rules.length; r++) { + var rule = rules[r]; + if (rule.index < style.index) { + style.index = rule.index; + } + } + } + } + + var result = styles.slice(); + result.sort(sortStylesIndex); + return result; +} + +/** + * Find a rule like Map { background-color: #fff; }, + * if any, and return a list of properties to be inserted + * into the = minzoom - 1e-6 and scale < maxzoom + 1e-6" + }, + "maxzoom": { + "default-value": "1.79769e+308", + "type":"float", + "default-meaning": "The layer will be visible at the maximum possible scale", + "doc": "The maximum scale denominator that this layer will be visible at. The default is the numeric limit of the C++ double type, which may vary slightly by system, but is likely a massive number like 1.79769e+308 and ensures that this layer will always be visible unless the value is reduced. A layer's visibility is determined by whether its status is true and if the Map scale >= minzoom - 1e-6 and scale < maxzoom + 1e-6" + }, + "queryable": { + "default-value": false, + "type":"boolean", + "default-meaning": "The layer will not be available for the direct querying of data values", + "doc": "This property was added for GetFeatureInfo/WMS compatibility and is rarely used. It is off by default meaning that in a WMS context the layer will not be able to be queried unless the property is explicitly set to true" + }, + "clear-label-cache": { + "default-value": false, + "type":"boolean", + "default-meaning": "The renderer's collision detector cache (used for avoiding duplicate labels and overlapping markers) will not be cleared immediately before processing this layer", + "doc": "This property, by default off, can be enabled to allow a user to clear the collision detector cache before a given layer is processed. This may be desirable to ensure that a given layers data shows up on the map even if it normally would not because of collisions with previously rendered labels or markers" + }, + "group-by": { + "default-value": "", + "type":"string", + "default-meaning": "No special layer grouping will be used during rendering", + "doc": "https://github.com/mapnik/mapnik/wiki/Grouped-rendering" + }, + "buffer-size": { + "default-value": "0", + "type":"float", + "default-meaning": "No buffer will be used", + "doc": "Extra tolerance around the Layer extent (in pixels) used to when querying and (potentially) clipping the layer data during rendering" + }, + "maximum-extent": { + "default-value": "none", + "type":"bbox", + "default-meaning": "No clipping extent will be used", + "doc": "An extent to be used to limit the bounds used to query this specific layer data during rendering. Should be minx, miny, maxx, maxy in the coordinates of the Layer." + } + }, + "symbolizers" : { + "*": { + "image-filters": { + "css": "image-filters", + "default-value": "none", + "default-meaning": "no filters", + "type": "functions", + "functions": [ + ["agg-stack-blur", 2], + ["emboss", 0], + ["blur", 0], + ["gray", 0], + ["sobel", 0], + ["edge-detect", 0], + ["x-gradient", 0], + ["y-gradient", 0], + ["invert", 0], + ["sharpen", 0], + ["colorize-alpha", -1], + ["color-to-alpha", 1], + ["scale-hsla", 8] + ], + "doc": "A list of image filters." + }, + "comp-op": { + "css": "comp-op", + "default-value": "src-over", + "default-meaning": "add the current layer on top of other layers", + "doc": "Composite operation. This defines how this layer should behave relative to layers atop or below it.", + "type": ["clear", + "src", + "dst", + "src-over", + "source-over", // added for torque + "dst-over", + "src-in", + "dst-in", + "src-out", + "dst-out", + "src-atop", + "dst-atop", + "xor", + "plus", + "minus", + "multiply", + "screen", + "overlay", + "darken", + "lighten", + "lighter", // added for torque + "color-dodge", + "color-burn", + "hard-light", + "soft-light", + "difference", + "exclusion", + "contrast", + "invert", + "invert-rgb", + "grain-merge", + "grain-extract", + "hue", + "saturation", + "color", + "value" + ] + }, + "opacity": { + "css": "opacity", + "type": "float", + "doc": "An alpha value for the style (which means an alpha applied to all features in separate buffer and then composited back to main buffer)", + "default-value": 1, + "default-meaning": "no separate buffer will be used and no alpha will be applied to the style after rendering" + } + }, + "map": { + "background-color": { + "css": "background-color", + "default-value": "none", + "default-meaning": "transparent", + "type": "color", + "doc": "Map Background color" + }, + "background-image": { + "css": "background-image", + "type": "uri", + "default-value": "", + "default-meaning": "transparent", + "doc": "An image that is repeated below all features on a map as a background.", + "description": "Map Background image" + }, + "srs": { + "css": "srs", + "type": "string", + "default-value": "+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs", + "default-meaning": "The proj4 literal of EPSG:4326 is assumed to be the Map's spatial reference and all data from layers within this map will be plotted using this coordinate system. If any layers do not declare an srs value then they will be assumed to be in the same srs as the Map and not transformations will be needed to plot them in the Map's coordinate space", + "doc": "Map spatial reference (proj4 string)" + }, + "buffer-size": { + "css": "buffer-size", + "default-value": "0", + "type":"float", + "default-meaning": "No buffer will be used", + "doc": "Extra tolerance around the map (in pixels) used to ensure labels crossing tile boundaries are equally rendered in each tile (e.g. cut in each tile). Not intended to be used in combination with \"avoid-edges\"." + }, + "maximum-extent": { + "css": "", + "default-value": "none", + "type":"bbox", + "default-meaning": "No clipping extent will be used", + "doc": "An extent to be used to limit the bounds used to query all layers during rendering. Should be minx, miny, maxx, maxy in the coordinates of the Map." + }, + "base": { + "css": "base", + "default-value": "", + "default-meaning": "This base path defaults to an empty string meaning that any relative paths to files referenced in styles or layers will be interpreted relative to the application process.", + "type": "string", + "doc": "Any relative paths used to reference files will be understood as relative to this directory path if the map is loaded from an in memory object rather than from the filesystem. If the map is loaded from the filesystem and this option is not provided it will be set to the directory of the stylesheet." + }, + "paths-from-xml": { + "css": "", + "default-value": true, + "default-meaning": "Paths read from XML will be interpreted from the location of the XML", + "type": "boolean", + "doc": "value to control whether paths in the XML will be interpreted from the location of the XML or from the working directory of the program that calls load_map()" + }, + "minimum-version": { + "css": "", + "default-value": "none", + "default-meaning": "Mapnik version will not be detected and no error will be thrown about compatibility", + "type": "string", + "doc": "The minumum Mapnik version (e.g. 0.7.2) needed to use certain functionality in the stylesheet" + }, + "font-directory": { + "css": "font-directory", + "type": "uri", + "default-value": "none", + "default-meaning": "No map-specific fonts will be registered", + "doc": "Path to a directory which holds fonts which should be registered when the Map is loaded (in addition to any fonts that may be automatically registered)." + } + }, + "polygon": { + "fill": { + "css": "polygon-fill", + "type": "color", + "default-value": "rgba(128,128,128,1)", + "default-meaning": "gray and fully opaque (alpha = 1), same as rgb(128,128,128)", + "doc": "Fill color to assign to a polygon" + }, + "fill-opacity": { + "css": "polygon-opacity", + "type": "float", + "doc": "The opacity of the polygon", + "default-value": 1, + "default-meaning": "opaque" + }, + "gamma": { + "css": "polygon-gamma", + "type": "float", + "default-value": 1, + "default-meaning": "fully antialiased", + "range": "0-1", + "doc": "Level of antialiasing of polygon edges" + }, + "gamma-method": { + "css": "polygon-gamma-method", + "type": [ + "power", + "linear", + "none", + "threshold", + "multiply" + ], + "default-value": "power", + "default-meaning": "pow(x,gamma) is used to calculate pixel gamma, which produces slightly smoother line and polygon antialiasing than the 'linear' method, while other methods are usually only used to disable AA", + "doc": "An Antigrain Geometry specific rendering hint to control the quality of antialiasing. Under the hood in Mapnik this method is used in combination with the 'gamma' value (which defaults to 1). The methods are in the AGG source at https://github.com/mapnik/mapnik/blob/master/deps/agg/include/agg_gamma_functions.h" + }, + "clip": { + "css": "polygon-clip", + "type": "boolean", + "default-value": true, + "default-meaning": "geometry will be clipped to map bounds before rendering", + "doc": "geometries are clipped to map bounds by default for best rendering performance. In some cases users may wish to disable this to avoid rendering artifacts." + }, + "smooth": { + "css": "polygon-smooth", + "type": "float", + "default-value": 0, + "default-meaning": "no smoothing", + "range": "0-1", + "doc": "Smooths out geometry angles. 0 is no smoothing, 1 is fully smoothed. Values greater than 1 will produce wild, looping geometries." + }, + "geometry-transform": { + "css": "polygon-geometry-transform", + "type": "functions", + "default-value": "none", + "default-meaning": "geometry will not be transformed", + "doc": "Allows transformation functions to be applied to the geometry.", + "functions": [ + ["matrix", 6], + ["translate", 2], + ["scale", 2], + ["rotate", 3], + ["skewX", 1], + ["skewY", 1] + ] + }, + "comp-op": { + "css": "polygon-comp-op", + "default-value": "src-over", + "default-meaning": "add the current symbolizer on top of other symbolizer", + "doc": "Composite operation. This defines how this symbolizer should behave relative to symbolizers atop or below it.", + "type": ["clear", + "src", + "dst", + "src-over", + "dst-over", + "src-in", + "dst-in", + "src-out", + "dst-out", + "src-atop", + "dst-atop", + "xor", + "plus", + "minus", + "multiply", + "screen", + "overlay", + "darken", + "lighten", + "color-dodge", + "color-burn", + "hard-light", + "soft-light", + "difference", + "exclusion", + "contrast", + "invert", + "invert-rgb", + "grain-merge", + "grain-extract", + "hue", + "saturation", + "color", + "value" + ] + } + }, + "line": { + "stroke": { + "css": "line-color", + "default-value": "rgba(0,0,0,1)", + "type": "color", + "default-meaning": "black and fully opaque (alpha = 1), same as rgb(0,0,0)", + "doc": "The color of a drawn line" + }, + "stroke-width": { + "css": "line-width", + "default-value": 1, + "type": "float", + "doc": "The width of a line in pixels" + }, + "stroke-opacity": { + "css": "line-opacity", + "default-value": 1, + "type": "float", + "default-meaning": "opaque", + "doc": "The opacity of a line" + }, + "stroke-linejoin": { + "css": "line-join", + "default-value": "miter", + "type": [ + "miter", + "round", + "bevel" + ], + "doc": "The behavior of lines when joining" + }, + "stroke-linecap": { + "css": "line-cap", + "default-value": "butt", + "type": [ + "butt", + "round", + "square" + ], + "doc": "The display of line endings" + }, + "stroke-gamma": { + "css": "line-gamma", + "type": "float", + "default-value": 1, + "default-meaning": "fully antialiased", + "range": "0-1", + "doc": "Level of antialiasing of stroke line" + }, + "stroke-gamma-method": { + "css": "line-gamma-method", + "type": [ + "power", + "linear", + "none", + "threshold", + "multiply" + ], + "default-value": "power", + "default-meaning": "pow(x,gamma) is used to calculate pixel gamma, which produces slightly smoother line and polygon antialiasing than the 'linear' method, while other methods are usually only used to disable AA", + "doc": "An Antigrain Geometry specific rendering hint to control the quality of antialiasing. Under the hood in Mapnik this method is used in combination with the 'gamma' value (which defaults to 1). The methods are in the AGG source at https://github.com/mapnik/mapnik/blob/master/deps/agg/include/agg_gamma_functions.h" + }, + "stroke-dasharray": { + "css": "line-dasharray", + "type": "numbers", + "doc": "A pair of length values [a,b], where (a) is the dash length and (b) is the gap length respectively. More than two values are supported for more complex patterns.", + "default-value": "none", + "default-meaning": "solid line" + }, + "stroke-dashoffset": { + "css": "line-dash-offset", + "type": "numbers", + "doc": "valid parameter but not currently used in renderers (only exists for experimental svg support in Mapnik which is not yet enabled)", + "default-value": "none", + "default-meaning": "solid line" + }, + "stroke-miterlimit": { + "css": "line-miterlimit", + "type": "float", + "doc": "The limit on the ratio of the miter length to the stroke-width. Used to automatically convert miter joins to bevel joins for sharp angles to avoid the miter extending beyond the thickness of the stroking path. Normally will not need to be set, but a larger value can sometimes help avoid jaggy artifacts.", + "default-value": 4.0, + "default-meaning": "Will auto-convert miters to bevel line joins when theta is less than 29 degrees as per the SVG spec: 'miterLength / stroke-width = 1 / sin ( theta / 2 )'" + }, + "clip": { + "css": "line-clip", + "type": "boolean", + "default-value": true, + "default-meaning": "geometry will be clipped to map bounds before rendering", + "doc": "geometries are clipped to map bounds by default for best rendering performance. In some cases users may wish to disable this to avoid rendering artifacts." + }, + "smooth": { + "css": "line-smooth", + "type": "float", + "default-value": 0, + "default-meaning": "no smoothing", + "range": "0-1", + "doc": "Smooths out geometry angles. 0 is no smoothing, 1 is fully smoothed. Values greater than 1 will produce wild, looping geometries." + }, + "offset": { + "css": "line-offset", + "type": "float", + "default-value": 0, + "default-meaning": "no offset", + "doc": "Offsets a line a number of pixels parallel to its actual path. Postive values move the line left, negative values move it right (relative to the directionality of the line)." + }, + "rasterizer": { + "css": "line-rasterizer", + "type": [ + "full", + "fast" + ], + "default-value": "full", + "doc": "Exposes an alternate AGG rendering method that sacrifices some accuracy for speed." + }, + "geometry-transform": { + "css": "line-geometry-transform", + "type": "functions", + "default-value": "none", + "default-meaning": "geometry will not be transformed", + "doc": "Allows transformation functions to be applied to the geometry.", + "functions": [ + ["matrix", 6], + ["translate", 2], + ["scale", 2], + ["rotate", 3], + ["skewX", 1], + ["skewY", 1] + ] + }, + "comp-op": { + "css": "line-comp-op", + "default-value": "src-over", + "default-meaning": "add the current symbolizer on top of other symbolizer", + "doc": "Composite operation. This defines how this symbolizer should behave relative to symbolizers atop or below it.", + "type": ["clear", + "src", + "dst", + "src-over", + "dst-over", + "src-in", + "dst-in", + "src-out", + "dst-out", + "src-atop", + "dst-atop", + "xor", + "plus", + "minus", + "multiply", + "screen", + "overlay", + "darken", + "lighten", + "color-dodge", + "color-burn", + "hard-light", + "soft-light", + "difference", + "exclusion", + "contrast", + "invert", + "invert-rgb", + "grain-merge", + "grain-extract", + "hue", + "saturation", + "color", + "value" + ] + } + }, + "markers": { + "file": { + "css": "marker-file", + "doc": "An SVG file that this marker shows at each placement. If no file is given, the marker will show an ellipse.", + "default-value": "", + "default-meaning": "An ellipse or circle, if width equals height", + "type": "uri" + }, + "opacity": { + "css": "marker-opacity", + "doc": "The overall opacity of the marker, if set, overrides both the opacity of both the fill and stroke", + "default-value": 1, + "default-meaning": "The stroke-opacity and fill-opacity will be used", + "type": "float" + }, + "fill-opacity": { + "css": "marker-fill-opacity", + "doc": "The fill opacity of the marker", + "default-value": 1, + "default-meaning": "opaque", + "type": "float" + }, + "stroke": { + "css": "marker-line-color", + "doc": "The color of the stroke around a marker shape.", + "default-value": "black", + "type": "color" + }, + "stroke-width": { + "css": "marker-line-width", + "doc": "The width of the stroke around a marker shape, in pixels. This is positioned on the boundary, so high values can cover the area itself.", + "type": "float" + }, + "stroke-opacity": { + "css": "marker-line-opacity", + "default-value": 1, + "default-meaning": "opaque", + "doc": "The opacity of a line", + "type": "float" + }, + "placement": { + "css": "marker-placement", + "type": [ + "point", + "line", + "interior" + ], + "default-value": "point", + "default-meaning": "Place markers at the center point (centroid) of the geometry", + "doc": "Attempt to place markers on a point, in the center of a polygon, or if markers-placement:line, then multiple times along a line. 'interior' placement can be used to ensure that points placed on polygons are forced to be inside the polygon interior" + }, + "multi-policy": { + "css": "marker-multi-policy", + "type": [ + "each", + "whole", + "largest" + ], + "default-value": "each", + "default-meaning": "If a feature contains multiple geometries and the placement type is either point or interior then a marker will be rendered for each", + "doc": "A special setting to allow the user to control rendering behavior for 'multi-geometries' (when a feature contains multiple geometries). This setting does not apply to markers placed along lines. The 'each' policy is default and means all geometries will get a marker. The 'whole' policy means that the aggregate centroid between all geometries will be used. The 'largest' policy means that only the largest (by bounding box areas) feature will get a rendered marker (this is how text labeling behaves by default)." + }, + "marker-type": { + "css": "marker-type", + "type": [ + "arrow", + "ellipse", + "rectangle" + ], + "default-value": "ellipse", + "doc": "The default marker-type. If a SVG file is not given as the marker-file parameter, the renderer provides either an arrow or an ellipse (a circle if height is equal to width)" + }, + "width": { + "css": "marker-width", + "default-value": 10, + "doc": "The width of the marker, if using one of the default types.", + "type": "float", + "expression": true + }, + "height": { + "css": "marker-height", + "default-value": 10, + "doc": "The height of the marker, if using one of the default types.", + "type": "float", + "expression": true + }, + "fill": { + "css": "marker-fill", + "default-value": "blue", + "doc": "The color of the area of the marker.", + "type": "color" + }, + "allow-overlap": { + "css": "marker-allow-overlap", + "type": "boolean", + "default-value": false, + "doc": "Control whether overlapping markers are shown or hidden.", + "default-meaning": "Do not allow makers to overlap with each other - overlapping markers will not be shown." + }, + "ignore-placement": { + "css": "marker-ignore-placement", + "type": "boolean", + "default-value": false, + "default-meaning": "do not store the bbox of this geometry in the collision detector cache", + "doc": "value to control whether the placement of the feature will prevent the placement of other features" + }, + "spacing": { + "css": "marker-spacing", + "doc": "Space between repeated labels", + "default-value": 100, + "type": "float" + }, + "max-error": { + "css": "marker-max-error", + "type": "float", + "default-value": 0.2, + "doc": "The maximum difference between actual marker placement and the marker-spacing parameter. Setting a high value can allow the renderer to try to resolve placement conflicts with other symbolizers." + }, + "transform": { + "css": "marker-transform", + "type": "functions", + "functions": [ + ["matrix", 6], + ["translate", 2], + ["scale", 2], + ["rotate", 3], + ["skewX", 1], + ["skewY", 1] + ], + "default-value": "", + "default-meaning": "No transformation", + "doc": "SVG transformation definition" + }, + "clip": { + "css": "marker-clip", + "type": "boolean", + "default-value": true, + "default-meaning": "geometry will be clipped to map bounds before rendering", + "doc": "geometries are clipped to map bounds by default for best rendering performance. In some cases users may wish to disable this to avoid rendering artifacts." + }, + "smooth": { + "css": "marker-smooth", + "type": "float", + "default-value": 0, + "default-meaning": "no smoothing", + "range": "0-1", + "doc": "Smooths out geometry angles. 0 is no smoothing, 1 is fully smoothed. Values greater than 1 will produce wild, looping geometries." + }, + "geometry-transform": { + "css": "marker-geometry-transform", + "type": "functions", + "default-value": "none", + "default-meaning": "geometry will not be transformed", + "doc": "Allows transformation functions to be applied to the geometry.", + "functions": [ + ["matrix", 6], + ["translate", 2], + ["scale", 2], + ["rotate", 3], + ["skewX", 1], + ["skewY", 1] + ] + }, + "comp-op": { + "css": "marker-comp-op", + "default-value": "src-over", + "default-meaning": "add the current symbolizer on top of other symbolizer", + "doc": "Composite operation. This defines how this symbolizer should behave relative to symbolizers atop or below it.", + "type": ["clear", + "src", + "dst", + "src-over", + "dst-over", + "src-in", + "dst-in", + "src-out", + "dst-out", + "src-atop", + "dst-atop", + "xor", + "plus", + "minus", + "multiply", + "screen", + "overlay", + "darken", + "lighten", + "color-dodge", + "color-burn", + "hard-light", + "soft-light", + "difference", + "exclusion", + "contrast", + "invert", + "invert-rgb", + "grain-merge", + "grain-extract", + "hue", + "saturation", + "color", + "value" + ] + } + }, + "shield": { + "name": { + "css": "shield-name", + "type": "string", + "expression": true, + "serialization": "content", + "doc": "Value to use for a shield\"s text label. Data columns are specified using brackets like [column_name]" + }, + "file": { + "css": "shield-file", + "required": true, + "type": "uri", + "default-value": "none", + "doc": "Image file to render behind the shield text" + }, + "face-name": { + "css": "shield-face-name", + "type": "string", + "validate": "font", + "doc": "Font name and style to use for the shield text", + "default-value": "", + "required": true + }, + "unlock-image": { + "css": "shield-unlock-image", + "type": "boolean", + "doc": "This parameter should be set to true if you are trying to position text beside rather than on top of the shield image", + "default-value": false, + "default-meaning": "text alignment relative to the shield image uses the center of the image as the anchor for text positioning." + }, + "size": { + "css": "shield-size", + "type": "float", + "doc": "The size of the shield text in pixels" + }, + "fill": { + "css": "shield-fill", + "type": "color", + "doc": "The color of the shield text" + }, + "placement": { + "css": "shield-placement", + "type": [ + "point", + "line", + "vertex", + "interior" + ], + "default-value": "point", + "doc": "How this shield should be placed. Point placement attempts to place it on top of points, line places along lines multiple times per feature, vertex places on the vertexes of polygons, and interior attempts to place inside of polygons." + }, + "avoid-edges": { + "css": "shield-avoid-edges", + "doc": "Tell positioning algorithm to avoid labeling near intersection edges.", + "type": "boolean", + "default-value": false + }, + "allow-overlap": { + "css": "shield-allow-overlap", + "type": "boolean", + "default-value": false, + "doc": "Control whether overlapping shields are shown or hidden.", + "default-meaning": "Do not allow shields to overlap with other map elements already placed." + }, + "minimum-distance": { + "css": "shield-min-distance", + "type": "float", + "default-value": 0, + "doc": "Minimum distance to the next shield symbol, not necessarily the same shield." + }, + "spacing": { + "css": "shield-spacing", + "type": "float", + "default-value": 0, + "doc": "The spacing between repeated occurrences of the same shield on a line" + }, + "minimum-padding": { + "css": "shield-min-padding", + "default-value": 0, + "doc": "Determines the minimum amount of padding that a shield gets relative to other shields", + "type": "float" + }, + "wrap-width": { + "css": "shield-wrap-width", + "type": "unsigned", + "default-value": 0, + "doc": "Length of a chunk of text in characters before wrapping text" + }, + "wrap-before": { + "css": "shield-wrap-before", + "type": "boolean", + "default-value": false, + "doc": "Wrap text before wrap-width is reached. If false, wrapped lines will be a bit longer than wrap-width." + }, + "wrap-character": { + "css": "shield-wrap-character", + "type": "string", + "default-value": " ", + "doc": "Use this character instead of a space to wrap long names." + }, + "halo-fill": { + "css": "shield-halo-fill", + "type": "color", + "default-value": "#FFFFFF", + "default-meaning": "white", + "doc": "Specifies the color of the halo around the text." + }, + "halo-radius": { + "css": "shield-halo-radius", + "doc": "Specify the radius of the halo in pixels", + "default-value": 0, + "default-meaning": "no halo", + "type": "float" + }, + "character-spacing": { + "css": "shield-character-spacing", + "type": "unsigned", + "default-value": 0, + "doc": "Horizontal spacing between characters (in pixels). Currently works for point placement only, not line placement." + }, + "line-spacing": { + "css": "shield-line-spacing", + "doc": "Vertical spacing between lines of multiline labels (in pixels)", + "type": "unsigned" + }, + "dx": { + "css": "shield-text-dx", + "type": "float", + "doc": "Displace text within shield by fixed amount, in pixels, +/- along the X axis. A positive value will shift the text right", + "default-value": 0 + }, + "dy": { + "css": "shield-text-dy", + "type": "float", + "doc": "Displace text within shield by fixed amount, in pixels, +/- along the Y axis. A positive value will shift the text down", + "default-value": 0 + }, + "shield-dx": { + "css": "shield-dx", + "type": "float", + "doc": "Displace shield by fixed amount, in pixels, +/- along the X axis. A positive value will shift the text right", + "default-value": 0 + }, + "shield-dy": { + "css": "shield-dy", + "type": "float", + "doc": "Displace shield by fixed amount, in pixels, +/- along the Y axis. A positive value will shift the text down", + "default-value": 0 + }, + "opacity": { + "css": "shield-opacity", + "type": "float", + "doc": "(Default 1.0) - opacity of the image used for the shield", + "default-value": 1 + }, + "text-opacity": { + "css": "shield-text-opacity", + "type": "float", + "doc": "(Default 1.0) - opacity of the text placed on top of the shield", + "default-value": 1 + }, + "horizontal-alignment": { + "css": "shield-horizontal-alignment", + "type": [ + "left", + "middle", + "right", + "auto" + ], + "doc": "The shield's horizontal alignment from its centerpoint", + "default-value": "auto" + }, + "vertical-alignment": { + "css": "shield-vertical-alignment", + "type": [ + "top", + "middle", + "bottom", + "auto" + ], + "doc": "The shield's vertical alignment from its centerpoint", + "default-value": "middle" + }, + "text-transform": { + "css": "shield-text-transform", + "type": [ + "none", + "uppercase", + "lowercase", + "capitalize" + ], + "doc": "Transform the case of the characters", + "default-value": "none" + }, + "justify-alignment": { + "css": "shield-justify-alignment", + "type": [ + "left", + "center", + "right", + "auto" + ], + "doc": "Define how text in a shield's label is justified", + "default-value": "auto" + }, + "clip": { + "css": "shield-clip", + "type": "boolean", + "default-value": true, + "default-meaning": "geometry will be clipped to map bounds before rendering", + "doc": "geometries are clipped to map bounds by default for best rendering performance. In some cases users may wish to disable this to avoid rendering artifacts." + }, + "comp-op": { + "css": "shield-comp-op", + "default-value": "src-over", + "default-meaning": "add the current symbolizer on top of other symbolizer", + "doc": "Composite operation. This defines how this symbolizer should behave relative to symbolizers atop or below it.", + "type": ["clear", + "src", + "dst", + "src-over", + "dst-over", + "src-in", + "dst-in", + "src-out", + "dst-out", + "src-atop", + "dst-atop", + "xor", + "plus", + "minus", + "multiply", + "screen", + "overlay", + "darken", + "lighten", + "color-dodge", + "color-burn", + "hard-light", + "soft-light", + "difference", + "exclusion", + "contrast", + "invert", + "invert-rgb", + "grain-merge", + "grain-extract", + "hue", + "saturation", + "color", + "value" + ] + } + }, + "line-pattern": { + "file": { + "css": "line-pattern-file", + "type": "uri", + "default-value": "none", + "required": true, + "doc": "An image file to be repeated and warped along a line" + }, + "clip": { + "css": "line-pattern-clip", + "type": "boolean", + "default-value": true, + "default-meaning": "geometry will be clipped to map bounds before rendering", + "doc": "geometries are clipped to map bounds by default for best rendering performance. In some cases users may wish to disable this to avoid rendering artifacts." + }, + "smooth": { + "css": "line-pattern-smooth", + "type": "float", + "default-value": 0, + "default-meaning": "no smoothing", + "range": "0-1", + "doc": "Smooths out geometry angles. 0 is no smoothing, 1 is fully smoothed. Values greater than 1 will produce wild, looping geometries." + }, + "geometry-transform": { + "css": "line-pattern-geometry-transform", + "type": "functions", + "default-value": "none", + "default-meaning": "geometry will not be transformed", + "doc": "Allows transformation functions to be applied to the geometry.", + "functions": [ + ["matrix", 6], + ["translate", 2], + ["scale", 2], + ["rotate", 3], + ["skewX", 1], + ["skewY", 1] + ] + }, + "comp-op": { + "css": "line-pattern-comp-op", + "default-value": "src-over", + "default-meaning": "add the current symbolizer on top of other symbolizer", + "doc": "Composite operation. This defines how this symbolizer should behave relative to symbolizers atop or below it.", + "type": ["clear", + "src", + "dst", + "src-over", + "dst-over", + "src-in", + "dst-in", + "src-out", + "dst-out", + "src-atop", + "dst-atop", + "xor", + "plus", + "minus", + "multiply", + "screen", + "overlay", + "darken", + "lighten", + "color-dodge", + "color-burn", + "hard-light", + "soft-light", + "difference", + "exclusion", + "contrast", + "invert", + "invert-rgb", + "grain-merge", + "grain-extract", + "hue", + "saturation", + "color", + "value" + ] + } + }, + "polygon-pattern": { + "file": { + "css": "polygon-pattern-file", + "type": "uri", + "default-value": "none", + "required": true, + "doc": "Image to use as a repeated pattern fill within a polygon" + }, + "alignment": { + "css": "polygon-pattern-alignment", + "type": [ + "local", + "global" + ], + "default-value": "local", + "doc": "Specify whether to align pattern fills to the layer or to the map." + }, + "gamma": { + "css": "polygon-pattern-gamma", + "type": "float", + "default-value": 1, + "default-meaning": "fully antialiased", + "range": "0-1", + "doc": "Level of antialiasing of polygon pattern edges" + }, + "opacity": { + "css": "polygon-pattern-opacity", + "type": "float", + "doc": "(Default 1.0) - Apply an opacity level to the image used for the pattern", + "default-value": 1, + "default-meaning": "The image is rendered without modifications" + }, + "clip": { + "css": "polygon-pattern-clip", + "type": "boolean", + "default-value": true, + "default-meaning": "geometry will be clipped to map bounds before rendering", + "doc": "geometries are clipped to map bounds by default for best rendering performance. In some cases users may wish to disable this to avoid rendering artifacts." + }, + "smooth": { + "css": "polygon-pattern-smooth", + "type": "float", + "default-value": 0, + "default-meaning": "no smoothing", + "range": "0-1", + "doc": "Smooths out geometry angles. 0 is no smoothing, 1 is fully smoothed. Values greater than 1 will produce wild, looping geometries." + }, + "geometry-transform": { + "css": "polygon-pattern-geometry-transform", + "type": "functions", + "default-value": "none", + "default-meaning": "geometry will not be transformed", + "doc": "Allows transformation functions to be applied to the geometry.", + "functions": [ + ["matrix", 6], + ["translate", 2], + ["scale", 2], + ["rotate", 3], + ["skewX", 1], + ["skewY", 1] + ] + }, + "comp-op": { + "css": "polygon-pattern-comp-op", + "default-value": "src-over", + "default-meaning": "add the current symbolizer on top of other symbolizer", + "doc": "Composite operation. This defines how this symbolizer should behave relative to symbolizers atop or below it.", + "type": ["clear", + "src", + "dst", + "src-over", + "dst-over", + "src-in", + "dst-in", + "src-out", + "dst-out", + "src-atop", + "dst-atop", + "xor", + "plus", + "minus", + "multiply", + "screen", + "overlay", + "darken", + "lighten", + "color-dodge", + "color-burn", + "hard-light", + "soft-light", + "difference", + "exclusion", + "contrast", + "invert", + "invert-rgb", + "grain-merge", + "grain-extract", + "hue", + "saturation", + "color", + "value" + ] + } + }, + "raster": { + "opacity": { + "css": "raster-opacity", + "default-value": 1, + "default-meaning": "opaque", + "type": "float", + "doc": "The opacity of the raster symbolizer on top of other symbolizers." + }, + "filter-factor": { + "css": "raster-filter-factor", + "default-value": -1, + "default-meaning": "Allow the datasource to choose appropriate downscaling.", + "type": "float", + "doc": "This is used by the Raster or Gdal datasources to pre-downscale images using overviews. Higher numbers can sometimes cause much better scaled image output, at the cost of speed." + }, + "scaling": { + "css": "raster-scaling", + "type": [ + "near", + "fast", + "bilinear", + "bilinear8", + "bicubic", + "spline16", + "spline36", + "hanning", + "hamming", + "hermite", + "kaiser", + "quadric", + "catrom", + "gaussian", + "bessel", + "mitchell", + "sinc", + "lanczos", + "blackman" + ], + "default-value": "near", + "doc": "The scaling algorithm used to making different resolution versions of this raster layer. Bilinear is a good compromise between speed and accuracy, while lanczos gives the highest quality." + }, + "mesh-size": { + "css": "raster-mesh-size", + "default-value": 16, + "default-meaning": "Reprojection mesh will be 1/16 of the resolution of the source image", + "type": "unsigned", + "doc": "A reduced resolution mesh is used for raster reprojection, and the total image size is divided by the mesh-size to determine the quality of that mesh. Values for mesh-size larger than the default will result in faster reprojection but might lead to distortion." + }, + "comp-op": { + "css": "raster-comp-op", + "default-value": "src-over", + "default-meaning": "add the current symbolizer on top of other symbolizer", + "doc": "Composite operation. This defines how this symbolizer should behave relative to symbolizers atop or below it.", + "type": ["clear", + "src", + "dst", + "src-over", + "dst-over", + "src-in", + "dst-in", + "src-out", + "dst-out", + "src-atop", + "dst-atop", + "xor", + "plus", + "minus", + "multiply", + "screen", + "overlay", + "darken", + "lighten", + "color-dodge", + "color-burn", + "hard-light", + "soft-light", + "difference", + "exclusion", + "contrast", + "invert", + "invert-rgb", + "grain-merge", + "grain-extract", + "hue", + "saturation", + "color", + "value" + ] + } + }, + "point": { + "file": { + "css": "point-file", + "type": "uri", + "required": false, + "default-value": "none", + "doc": "Image file to represent a point" + }, + "allow-overlap": { + "css": "point-allow-overlap", + "type": "boolean", + "default-value": false, + "doc": "Control whether overlapping points are shown or hidden.", + "default-meaning": "Do not allow points to overlap with each other - overlapping markers will not be shown." + }, + "ignore-placement": { + "css": "point-ignore-placement", + "type": "boolean", + "default-value": false, + "default-meaning": "do not store the bbox of this geometry in the collision detector cache", + "doc": "value to control whether the placement of the feature will prevent the placement of other features" + }, + "opacity": { + "css": "point-opacity", + "type": "float", + "default-value": 1.0, + "default-meaning": "Fully opaque", + "doc": "A value from 0 to 1 to control the opacity of the point" + }, + "placement": { + "css": "point-placement", + "type": [ + "centroid", + "interior" + ], + "doc": "How this point should be placed. Centroid calculates the geometric center of a polygon, which can be outside of it, while interior always places inside of a polygon.", + "default-value": "centroid" + }, + "transform": { + "css": "point-transform", + "type": "functions", + "functions": [ + ["matrix", 6], + ["translate", 2], + ["scale", 2], + ["rotate", 3], + ["skewX", 1], + ["skewY", 1] + ], + "default-value": "", + "default-meaning": "No transformation", + "doc": "SVG transformation definition" + }, + "comp-op": { + "css": "point-comp-op", + "default-value": "src-over", + "default-meaning": "add the current symbolizer on top of other symbolizer", + "doc": "Composite operation. This defines how this symbolizer should behave relative to symbolizers atop or below it.", + "type": ["clear", + "src", + "dst", + "src-over", + "dst-over", + "src-in", + "dst-in", + "src-out", + "dst-out", + "src-atop", + "dst-atop", + "xor", + "plus", + "minus", + "multiply", + "screen", + "overlay", + "darken", + "lighten", + "color-dodge", + "color-burn", + "hard-light", + "soft-light", + "difference", + "exclusion", + "contrast", + "invert", + "invert-rgb", + "grain-merge", + "grain-extract", + "hue", + "saturation", + "color", + "value" + ] + } + }, + "text": { + "name": { + "css": "text-name", + "type": "string", + "expression": true, + "required": true, + "default-value": "", + "serialization": "content", + "doc": "Value to use for a text label. Data columns are specified using brackets like [column_name]" + }, + "face-name": { + "css": "text-face-name", + "type": "string", + "validate": "font", + "doc": "Font name and style to render a label in", + "required": true + }, + "size": { + "css": "text-size", + "type": "float", + "default-value": 10, + "doc": "Text size in pixels" + }, + "text-ratio": { + "css": "text-ratio", + "doc": "Define the amount of text (of the total) present on successive lines when wrapping occurs", + "default-value": 0, + "type": "unsigned" + }, + "wrap-width": { + "css": "text-wrap-width", + "doc": "Length of a chunk of text in characters before wrapping text", + "default-value": 0, + "type": "unsigned" + }, + "wrap-before": { + "css": "text-wrap-before", + "type": "boolean", + "default-value": false, + "doc": "Wrap text before wrap-width is reached. If false, wrapped lines will be a bit longer than wrap-width." + }, + "wrap-character": { + "css": "text-wrap-character", + "type": "string", + "default-value": " ", + "doc": "Use this character instead of a space to wrap long text." + }, + "spacing": { + "css": "text-spacing", + "type": "unsigned", + "doc": "Distance between repeated text labels on a line (aka. label-spacing)" + }, + "character-spacing": { + "css": "text-character-spacing", + "type": "float", + "default-value": 0, + "doc": "Horizontal spacing adjustment between characters in pixels" + }, + "line-spacing": { + "css": "text-line-spacing", + "default-value": 0, + "type": "unsigned", + "doc": "Vertical spacing adjustment between lines in pixels" + }, + "label-position-tolerance": { + "css": "text-label-position-tolerance", + "default-value": 0, + "type": "unsigned", + "doc": "Allows the label to be displaced from its ideal position by a number of pixels (only works with placement:line)" + }, + "max-char-angle-delta": { + "css": "text-max-char-angle-delta", + "type": "float", + "default-value": "22.5", + "doc": "The maximum angle change, in degrees, allowed between adjacent characters in a label. This value internally is converted to radians to the default is 22.5*math.pi/180.0. The higher the value the fewer labels will be placed around around sharp corners." + }, + "fill": { + "css": "text-fill", + "doc": "Specifies the color for the text", + "default-value": "#000000", + "type": "color" + }, + "opacity": { + "css": "text-opacity", + "doc": "A number from 0 to 1 specifying the opacity for the text", + "default-value": 1.0, + "default-meaning": "Fully opaque", + "type": "float" + }, + "halo-fill": { + "css": "text-halo-fill", + "type": "color", + "default-value": "#FFFFFF", + "default-meaning": "white", + "doc": "Specifies the color of the halo around the text." + }, + "halo-radius": { + "css": "text-halo-radius", + "doc": "Specify the radius of the halo in pixels", + "default-value": 0, + "default-meaning": "no halo", + "type": "float" + }, + "dx": { + "css": "text-dx", + "type": "float", + "doc": "Displace text by fixed amount, in pixels, +/- along the X axis. A positive value will shift the text right", + "default-value": 0 + }, + "dy": { + "css": "text-dy", + "type": "float", + "doc": "Displace text by fixed amount, in pixels, +/- along the Y axis. A positive value will shift the text down", + "default-value": 0 + }, + "vertical-alignment": { + "css": "text-vertical-alignment", + "type": [ + "top", + "middle", + "bottom", + "auto" + ], + "doc": "Position of label relative to point position.", + "default-value": "auto", + "default-meaning": "Default affected by value of dy; \"bottom\" for dy>0, \"top\" for dy<0." + }, + "avoid-edges": { + "css": "text-avoid-edges", + "doc": "Tell positioning algorithm to avoid labeling near intersection edges.", + "default-value": false, + "type": "boolean" + }, + "minimum-distance": { + "css": "text-min-distance", + "doc": "Minimum permitted distance to the next text symbolizer.", + "type": "float" + }, + "minimum-padding": { + "css": "text-min-padding", + "doc": "Determines the minimum amount of padding that a text symbolizer gets relative to other text", + "type": "float" + }, + "minimum-path-length": { + "css": "text-min-path-length", + "type": "float", + "default-value": 0, + "default-meaning": "place labels on all paths", + "doc": "Place labels only on paths longer than this value." + }, + "allow-overlap": { + "css": "text-allow-overlap", + "type": "boolean", + "default-value": false, + "doc": "Control whether overlapping text is shown or hidden.", + "default-meaning": "Do not allow text to overlap with other text - overlapping markers will not be shown." + }, + "orientation": { + "css": "text-orientation", + "type": "float", + "expression": true, + "doc": "Rotate the text." + }, + "placement": { + "css": "text-placement", + "type": [ + "point", + "line", + "vertex", + "interior" + ], + "default-value": "point", + "doc": "Control the style of placement of a point versus the geometry it is attached to." + }, + "placement-type": { + "css": "text-placement-type", + "doc": "Re-position and/or re-size text to avoid overlaps. \"simple\" for basic algorithm (using text-placements string,) \"dummy\" to turn this feature off.", + "type": [ + "dummy", + "simple" + ], + "default-value": "dummy" + }, + "placements": { + "css": "text-placements", + "type": "string", + "default-value": "", + "doc": "If \"placement-type\" is set to \"simple\", use this \"POSITIONS,[SIZES]\" string. An example is `text-placements: \"E,NE,SE,W,NW,SW\";` " + }, + "text-transform": { + "css": "text-transform", + "type": [ + "none", + "uppercase", + "lowercase", + "capitalize" + ], + "doc": "Transform the case of the characters", + "default-value": "none" + }, + "horizontal-alignment": { + "css": "text-horizontal-alignment", + "type": [ + "left", + "middle", + "right", + "auto" + ], + "doc": "The text's horizontal alignment from its centerpoint", + "default-value": "auto" + }, + "justify-alignment": { + "css": "text-align", + "type": [ + "left", + "right", + "center", + "auto" + ], + "doc": "Define how text is justified", + "default-value": "auto", + "default-meaning": "Auto alignment means that text will be centered by default except when using the `placement-type` parameter - in that case either right or left justification will be used automatically depending on where the text could be fit given the `text-placements` directives" + }, + "clip": { + "css": "text-clip", + "type": "boolean", + "default-value": true, + "default-meaning": "geometry will be clipped to map bounds before rendering", + "doc": "geometries are clipped to map bounds by default for best rendering performance. In some cases users may wish to disable this to avoid rendering artifacts." + }, + "comp-op": { + "css": "text-comp-op", + "default-value": "src-over", + "default-meaning": "add the current symbolizer on top of other symbolizer", + "doc": "Composite operation. This defines how this symbolizer should behave relative to symbolizers atop or below it.", + "type": ["clear", + "src", + "dst", + "src-over", + "dst-over", + "src-in", + "dst-in", + "src-out", + "dst-out", + "src-atop", + "dst-atop", + "xor", + "plus", + "minus", + "multiply", + "screen", + "overlay", + "darken", + "lighten", + "color-dodge", + "color-burn", + "hard-light", + "soft-light", + "difference", + "exclusion", + "contrast", + "invert", + "invert-rgb", + "grain-merge", + "grain-extract", + "hue", + "saturation", + "color", + "value" + ] + } + }, + "building": { + "fill": { + "css": "building-fill", + "default-value": "#FFFFFF", + "doc": "The color of the buildings walls.", + "type": "color" + }, + "fill-opacity": { + "css": "building-fill-opacity", + "type": "float", + "doc": "The opacity of the building as a whole, including all walls.", + "default-value": 1 + }, + "height": { + "css": "building-height", + "doc": "The height of the building in pixels.", + "type": "float", + "expression": true, + "default-value": "0" + } + }, + "torque": { + "-torque-clear-color": { + "css": "-torque-clear-color", + "type": "color", + "default-value": "rgba(255, 255, 255, 0)", + "default-meaning": "full clear", + "doc": "color used to clear canvas on each frame" + }, + "-torque-frame-count": { + "css": "-torque-frame-count", + "default-value": "128", + "type":"float", + "default-meaning": "the data is broken into 128 time frames", + "doc": "Number of animation steps/frames used in the animation. If the data contains a fewere number of total frames, the lesser value will be used." + }, + "-torque-resolution": { + "css": "-torque-resolution", + "default-value": "2", + "type":"float", + "default-meaning": "", + "doc": "Spatial resolution in pixels. A resolution of 1 means no spatial aggregation of the data. Any other resolution of N results in spatial aggregation into cells of NxN pixels. The value N must be power of 2" + }, + "-torque-animation-duration": { + "css": "-torque-animation-duration", + "default-value": "30", + "type":"float", + "default-meaning": "the animation lasts 30 seconds", + "doc": "Animation duration in seconds" + }, + "-torque-aggregation-function": { + "css": "-torque-aggregation-function", + "default-value": "count(cartodb_id)", + "type": "string", + "default-meaning": "the value for each cell is the count of points in that cell", + "doc": "A function used to calculate a value from the aggregate data for each cell. See -torque-resolution" + }, + "-torque-time-attribute": { + "css": "-torque-time-attribute", + "default-value": "time", + "type": "string", + "default-meaning": "the data column in your table that is of a time based type", + "doc": "The table column that contains the time information used create the animation" + }, + "-torque-data-aggregation": { + "css": "-torque-data-aggregation", + "default-value": "linear", + "type": [ + "linear", + "cumulative" + ], + "default-meaning": "previous values are discarded", + "doc": "A linear animation will discard previous values while a cumulative animation will accumulate them until it restarts" + } + } + }, + "colors": { + "aliceblue": [240, 248, 255], + "antiquewhite": [250, 235, 215], + "aqua": [0, 255, 255], + "aquamarine": [127, 255, 212], + "azure": [240, 255, 255], + "beige": [245, 245, 220], + "bisque": [255, 228, 196], + "black": [0, 0, 0], + "blanchedalmond": [255,235,205], + "blue": [0, 0, 255], + "blueviolet": [138, 43, 226], + "brown": [165, 42, 42], + "burlywood": [222, 184, 135], + "cadetblue": [95, 158, 160], + "chartreuse": [127, 255, 0], + "chocolate": [210, 105, 30], + "coral": [255, 127, 80], + "cornflowerblue": [100, 149, 237], + "cornsilk": [255, 248, 220], + "crimson": [220, 20, 60], + "cyan": [0, 255, 255], + "darkblue": [0, 0, 139], + "darkcyan": [0, 139, 139], + "darkgoldenrod": [184, 134, 11], + "darkgray": [169, 169, 169], + "darkgreen": [0, 100, 0], + "darkgrey": [169, 169, 169], + "darkkhaki": [189, 183, 107], + "darkmagenta": [139, 0, 139], + "darkolivegreen": [85, 107, 47], + "darkorange": [255, 140, 0], + "darkorchid": [153, 50, 204], + "darkred": [139, 0, 0], + "darksalmon": [233, 150, 122], + "darkseagreen": [143, 188, 143], + "darkslateblue": [72, 61, 139], + "darkslategrey": [47, 79, 79], + "darkturquoise": [0, 206, 209], + "darkviolet": [148, 0, 211], + "deeppink": [255, 20, 147], + "deepskyblue": [0, 191, 255], + "dimgray": [105, 105, 105], + "dimgrey": [105, 105, 105], + "dodgerblue": [30, 144, 255], + "firebrick": [178, 34, 34], + "floralwhite": [255, 250, 240], + "forestgreen": [34, 139, 34], + "fuchsia": [255, 0, 255], + "gainsboro": [220, 220, 220], + "ghostwhite": [248, 248, 255], + "gold": [255, 215, 0], + "goldenrod": [218, 165, 32], + "gray": [128, 128, 128], + "grey": [128, 128, 128], + "green": [0, 128, 0], + "greenyellow": [173, 255, 47], + "honeydew": [240, 255, 240], + "hotpink": [255, 105, 180], + "indianred": [205, 92, 92], + "indigo": [75, 0, 130], + "ivory": [255, 255, 240], + "khaki": [240, 230, 140], + "lavender": [230, 230, 250], + "lavenderblush": [255, 240, 245], + "lawngreen": [124, 252, 0], + "lemonchiffon": [255, 250, 205], + "lightblue": [173, 216, 230], + "lightcoral": [240, 128, 128], + "lightcyan": [224, 255, 255], + "lightgoldenrodyellow": [250, 250, 210], + "lightgray": [211, 211, 211], + "lightgreen": [144, 238, 144], + "lightgrey": [211, 211, 211], + "lightpink": [255, 182, 193], + "lightsalmon": [255, 160, 122], + "lightseagreen": [32, 178, 170], + "lightskyblue": [135, 206, 250], + "lightslategray": [119, 136, 153], + "lightslategrey": [119, 136, 153], + "lightsteelblue": [176, 196, 222], + "lightyellow": [255, 255, 224], + "lime": [0, 255, 0], + "limegreen": [50, 205, 50], + "linen": [250, 240, 230], + "magenta": [255, 0, 255], + "maroon": [128, 0, 0], + "mediumaquamarine": [102, 205, 170], + "mediumblue": [0, 0, 205], + "mediumorchid": [186, 85, 211], + "mediumpurple": [147, 112, 219], + "mediumseagreen": [60, 179, 113], + "mediumslateblue": [123, 104, 238], + "mediumspringgreen": [0, 250, 154], + "mediumturquoise": [72, 209, 204], + "mediumvioletred": [199, 21, 133], + "midnightblue": [25, 25, 112], + "mintcream": [245, 255, 250], + "mistyrose": [255, 228, 225], + "moccasin": [255, 228, 181], + "navajowhite": [255, 222, 173], + "navy": [0, 0, 128], + "oldlace": [253, 245, 230], + "olive": [128, 128, 0], + "olivedrab": [107, 142, 35], + "orange": [255, 165, 0], + "orangered": [255, 69, 0], + "orchid": [218, 112, 214], + "palegoldenrod": [238, 232, 170], + "palegreen": [152, 251, 152], + "paleturquoise": [175, 238, 238], + "palevioletred": [219, 112, 147], + "papayawhip": [255, 239, 213], + "peachpuff": [255, 218, 185], + "peru": [205, 133, 63], + "pink": [255, 192, 203], + "plum": [221, 160, 221], + "powderblue": [176, 224, 230], + "purple": [128, 0, 128], + "red": [255, 0, 0], + "rosybrown": [188, 143, 143], + "royalblue": [65, 105, 225], + "saddlebrown": [139, 69, 19], + "salmon": [250, 128, 114], + "sandybrown": [244, 164, 96], + "seagreen": [46, 139, 87], + "seashell": [255, 245, 238], + "sienna": [160, 82, 45], + "silver": [192, 192, 192], + "skyblue": [135, 206, 235], + "slateblue": [106, 90, 205], + "slategray": [112, 128, 144], + "slategrey": [112, 128, 144], + "snow": [255, 250, 250], + "springgreen": [0, 255, 127], + "steelblue": [70, 130, 180], + "tan": [210, 180, 140], + "teal": [0, 128, 128], + "thistle": [216, 191, 216], + "tomato": [255, 99, 71], + "turquoise": [64, 224, 208], + "violet": [238, 130, 238], + "wheat": [245, 222, 179], + "white": [255, 255, 255], + "whitesmoke": [245, 245, 245], + "yellow": [255, 255, 0], + "yellowgreen": [154, 205, 50], + "transparent": [0, 0, 0, 0] + }, + "filter": { + "value": [ + "true", + "false", + "null", + "point", + "linestring", + "polygon", + "collection" + ] + } +} + +module.exports = { + version: { + latest: _mapnik_reference_latest, + '2.1.1': _mapnik_reference_latest + } +}; + +},{}],41:[function(require,module,exports){ +/** + * TODO: document this. What does this do? + */ +if(typeof(module) !== "undefined") { + module.exports.find = function (obj, fun) { + for (var i = 0, r; i < obj.length; i++) { + if (r = fun.call(obj, obj[i])) { return r; } + } + return null; + }; +} + +},{}],42:[function(require,module,exports){ +(function (global){ +(function(tree) { +var _ = global._ || require('underscore'); +tree.Call = function Call(name, args, index) { + this.name = name; + this.args = args; + this.index = index; +}; + +tree.Call.prototype = { + is: 'call', + // When evuating a function call, + // we either find the function in `tree.functions` [1], + // in which case we call it, passing the evaluated arguments, + // or we simply print it out as it appeared originally [2]. + // The *functions.js* file contains the built-in functions. + // The reason why we evaluate the arguments, is in the case where + // we try to pass a variable to a function, like: `saturate(@color)`. + // The function should receive the value, not the variable. + 'ev': function(env) { + var args = this.args.map(function(a) { return a.ev(env); }); + + for (var i = 0; i < args.length; i++) { + if (args[i].is === 'undefined') { + return { + is: 'undefined', + value: 'undefined' + }; + } + } + + if (this.name in tree.functions) { + if (tree.functions[this.name].length <= args.length) { + var val = tree.functions[this.name].apply(tree.functions, args); + if (val === null) { + env.error({ + message: 'incorrect arguments given to ' + this.name + '()', + index: this.index, + type: 'runtime', + filename: this.filename + }); + return { is: 'undefined', value: 'undefined' }; + } + return val; + } else { + env.error({ + message: 'incorrect number of arguments for ' + this.name + + '(). ' + tree.functions[this.name].length + ' expected.', + index: this.index, + type: 'runtime', + filename: this.filename + }); + return { + is: 'undefined', + value: 'undefined' + }; + } + } else { + var fn = tree.Reference.mapnikFunctions[this.name]; + if (fn === undefined) { + var functions = _.pairs(tree.Reference.mapnikFunctions); + // cheap closest, needs improvement. + var name = this.name; + var mean = functions.map(function(f) { + return [f[0], tree.Reference.editDistance(name, f[0]), f[1]]; + }).sort(function(a, b) { + return a[1] - b[1]; + }); + env.error({ + message: 'unknown function ' + this.name + '(), did you mean ' + + mean[0][0] + '(' + mean[0][2] + ')', + index: this.index, + type: 'runtime', + filename: this.filename + }); + return { + is: 'undefined', + value: 'undefined' + }; + } + if (fn !== args.length && + !(Array.isArray(fn) && _.include(fn, args.length)) && + // support variable-arg functions like `colorize-alpha` + fn !== -1) { + env.error({ + message: 'function ' + this.name + '() takes ' + + fn + ' arguments and was given ' + args.length, + index: this.index, + type: 'runtime', + filename: this.filename + }); + return { + is: 'undefined', + value: 'undefined' + }; + } else { + // Save the evaluated versions of arguments + this.args = args; + return this; + } + } + }, + + toString: function(env, format) { + if (this.args.length) { + return this.name + '(' + this.args.join(',') + ')'; + } else { + return this.name; + } + } +}; + +})(require('../tree')); + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"../tree":41,"underscore":102}],43:[function(require,module,exports){ +(function(tree) { +// RGB Colors - #ff0014, #eee +// can be initialized with a 3 or 6 char string or a 3 or 4 element +// numerical array +tree.Color = function Color(rgb, a) { + // The end goal here, is to parse the arguments + // into an integer triplet, such as `128, 255, 0` + // + // This facilitates operations and conversions. + if (Array.isArray(rgb)) { + this.rgb = rgb.slice(0, 3); + } else if (rgb.length == 6) { + this.rgb = rgb.match(/.{2}/g).map(function(c) { + return parseInt(c, 16); + }); + } else { + this.rgb = rgb.split('').map(function(c) { + return parseInt(c + c, 16); + }); + } + + if (typeof(a) === 'number') { + this.alpha = a; + } else if (rgb.length === 4) { + this.alpha = rgb[3]; + } else { + this.alpha = 1; + } +}; + +tree.Color.prototype = { + is: 'color', + 'ev': function() { return this; }, + + // If we have some transparency, the only way to represent it + // is via `rgba`. Otherwise, we use the hex representation, + // which has better compatibility with older browsers. + // Values are capped between `0` and `255`, rounded and zero-padded. + toString: function() { + if (this.alpha < 1.0) { + return 'rgba(' + this.rgb.map(function(c) { + return Math.round(c); + }).concat(this.alpha).join(', ') + ')'; + } else { + return '#' + this.rgb.map(function(i) { + i = Math.round(i); + i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16); + return i.length === 1 ? '0' + i : i; + }).join(''); + } + }, + + // Operations have to be done per-channel, if not, + // channels will spill onto each other. Once we have + // our result, in the form of an integer triplet, + // we create a new Color node to hold the result. + operate: function(env, op, other) { + var result = []; + + if (! (other instanceof tree.Color)) { + other = other.toColor(); + } + + for (var c = 0; c < 3; c++) { + result[c] = tree.operate(op, this.rgb[c], other.rgb[c]); + } + return new tree.Color(result); + }, + + toHSL: function() { + var r = this.rgb[0] / 255, + g = this.rgb[1] / 255, + b = this.rgb[2] / 255, + a = this.alpha; + + var max = Math.max(r, g, b), min = Math.min(r, g, b); + var h, s, l = (max + min) / 2, d = max - min; + + if (max === min) { + h = s = 0; + } else { + s = l > 0.5 ? d / (2 - max - min) : d / (max + min); + + switch (max) { + case r: h = (g - b) / d + (g < b ? 6 : 0); break; + case g: h = (b - r) / d + 2; break; + case b: h = (r - g) / d + 4; break; + } + h /= 6; + } + return { h: h * 360, s: s, l: l, a: a }; + } +}; + +})(require('../tree')); + +},{"../tree":41}],44:[function(require,module,exports){ +(function(tree) { + +tree.Comment = function Comment(value, silent) { + this.value = value; + this.silent = !!silent; +}; + +tree.Comment.prototype = { + toString: function(env) { + return ''; + }, + 'ev': function() { return this; } +}; + +})(require('../tree')); + +},{"../tree":41}],45:[function(require,module,exports){ +(function (global){ +(function(tree) { +var assert = require('assert'), + _ = global._ || require('underscore'); + +// A definition is the combination of a selector and rules, like +// #foo { +// polygon-opacity:1.0; +// } +// +// The selector can have filters +tree.Definition = function Definition(selector, rules) { + this.elements = selector.elements; + assert.ok(selector.filters instanceof tree.Filterset); + this.rules = rules; + this.ruleIndex = {}; + for (var i = 0; i < this.rules.length; i++) { + if ('zoom' in this.rules[i]) this.rules[i] = this.rules[i].clone(); + this.rules[i].zoom = selector.zoom; + this.ruleIndex[this.rules[i].updateID()] = true; + } + this.filters = selector.filters; + this.zoom = selector.zoom; + this.frame_offset = selector.frame_offset; + this.attachment = selector.attachment || '__default__'; + this.specificity = selector.specificity(); +}; + +tree.Definition.prototype.toString = function() { + var str = this.filters.toString(); + for (var i = 0; i < this.rules.length; i++) { + str += '\n ' + this.rules[i]; + } + return str; +}; + +tree.Definition.prototype.clone = function(filters) { + if (filters) assert.ok(filters instanceof tree.Filterset); + var clone = Object.create(tree.Definition.prototype); + clone.rules = this.rules.slice(); + clone.ruleIndex = _.clone(this.ruleIndex); + clone.filters = filters ? filters : this.filters.clone(); + clone.attachment = this.attachment; + return clone; +}; + +tree.Definition.prototype.addRules = function(rules) { + var added = 0; + + // Add only unique rules. + for (var i = 0; i < rules.length; i++) { + if (!this.ruleIndex[rules[i].id]) { + this.rules.push(rules[i]); + this.ruleIndex[rules[i].id] = true; + added++; + } + } + + return added; +}; + +// Determine whether this selector matches a given id +// and array of classes, by determining whether +// all elements it contains match. +tree.Definition.prototype.appliesTo = function(id, classes) { + for (var i = 0, l = this.elements.length; i < l; i++) { + var elem = this.elements[i]; + if (!(elem.wildcard || + (elem.type === 'class' && classes[elem.clean]) || + (elem.type === 'id' && id === elem.clean))) return false; + } + return true; +}; + +function symbolizerName(symbolizer) { + function capitalize(str) { return str[1].toUpperCase(); } + return symbolizer.charAt(0).toUpperCase() + + symbolizer.slice(1).replace(/\-./, capitalize) + 'Symbolizer'; +} + +// Get a simple list of the symbolizers, in order +function symbolizerList(sym_order) { + return sym_order.sort(function(a, b) { return a[1] - b[1]; }) + .map(function(v) { return v[0]; }); +} + +tree.Definition.prototype.symbolizersToXML = function(env, symbolizers, zoom) { + var xml = zoom.toXML(env).join('') + this.filters.toXML(env); + + // Sort symbolizers by the index of their first property definition + var sym_order = [], indexes = []; + for (var key in symbolizers) { + indexes = []; + for (var prop in symbolizers[key]) { + indexes.push(symbolizers[key][prop].index); + } + var min_idx = Math.min.apply(Math, indexes); + sym_order.push([key, min_idx]); + } + + sym_order = symbolizerList(sym_order); + var sym_count = 0; + + for (var i = 0; i < sym_order.length; i++) { + var attributes = symbolizers[sym_order[i]]; + var symbolizer = sym_order[i].split('/').pop(); + + // Skip the magical * symbolizer which is used for universal properties + // which are bubbled up to Style elements intead of Symbolizer elements. + if (symbolizer === '*') continue; + sym_count++; + + var fail = tree.Reference.requiredProperties(symbolizer, attributes); + if (fail) { + var rule = attributes[Object.keys(attributes).shift()]; + env.error({ + message: fail, + index: rule.index, + filename: rule.filename + }); + } + + var name = symbolizerName(symbolizer); + + var selfclosing = true, tagcontent; + xml += ' <' + name + ' '; + for (var j in attributes) { + if (symbolizer === 'map') env.error({ + message: 'Map properties are not permitted in other rules', + index: attributes[j].index, + filename: attributes[j].filename + }); + var x = tree.Reference.selector(attributes[j].name); + if (x && x.serialization && x.serialization === 'content') { + selfclosing = false; + tagcontent = attributes[j].ev(env).toXML(env, true); + } else if (x && x.serialization && x.serialization === 'tag') { + selfclosing = false; + tagcontent = attributes[j].ev(env).toXML(env, true); + } else { + xml += attributes[j].ev(env).toXML(env) + ' '; + } + } + if (selfclosing) { + xml += '/>\n'; + } else if (typeof tagcontent !== "undefined") { + if (tagcontent.indexOf('<') != -1) { + xml += '>' + tagcontent + '\n'; + } else { + xml += '>\n'; + } + } + } + if (!sym_count || !xml) return ''; + return ' \n' + xml + ' \n'; +}; + +// Take a zoom range of zooms and 'i', the index of a rule in this.rules, +// and finds all applicable symbolizers +tree.Definition.prototype.collectSymbolizers = function(zooms, i) { + var symbolizers = {}, child; + + for (var j = i; j < this.rules.length; j++) { + child = this.rules[j]; + var key = child.instance + '/' + child.symbolizer; + if (zooms.current & child.zoom && + (!(key in symbolizers) || + (!(child.name in symbolizers[key])))) { + zooms.current &= child.zoom; + if (!(key in symbolizers)) { + symbolizers[key] = {}; + } + symbolizers[key][child.name] = child; + } + } + + if (Object.keys(symbolizers).length) { + zooms.rule &= (zooms.available &= ~zooms.current); + return symbolizers; + } +}; + +// The tree.Zoom.toString function ignores the holes in zoom ranges and outputs +// scaledenominators that cover the whole range from the first to last bit set. +// This algorithm can produces zoom ranges that may have holes. However, +// when using the filter-mode="first", more specific zoom filters will always +// end up before broader ranges. The filter-mode will pick those first before +// resorting to the zoom range with the hole and stop processing further rules. +tree.Definition.prototype.toXML = function(env, existing) { + var filter = this.filters.toString(); + if (!(filter in existing)) existing[filter] = tree.Zoom.all; + + var available = tree.Zoom.all, xml = '', zoom, symbolizers, + zooms = { available: tree.Zoom.all }; + for (var i = 0; i < this.rules.length && available; i++) { + zooms.rule = this.rules[i].zoom; + if (!(existing[filter] & zooms.rule)) continue; + + while (zooms.current = zooms.rule & available) { + if (symbolizers = this.collectSymbolizers(zooms, i)) { + if (!(existing[filter] & zooms.current)) continue; + xml += this.symbolizersToXML(env, symbolizers, + (new tree.Zoom()).setZoom(existing[filter] & zooms.current)); + existing[filter] &= ~zooms.current; + } + } + } + + return xml; +}; + +tree.Definition.prototype.toJS = function(env) { + var shaderAttrs = {}; + + // merge conditions from filters with zoom condition of the + // definition + var zoom = "(" + this.zoom + " & (1 << ctx.zoom))"; + var frame_offset = this.frame_offset; + var _if = this.filters.toJS(env); + var filters = [zoom]; + if(_if) filters.push(_if); + if(frame_offset) filters.push('ctx["frame-offset"] === ' + frame_offset); + _if = filters.join(" && "); + _.each(this.rules, function(rule) { + if(rule instanceof tree.Rule) { + shaderAttrs[rule.name] = shaderAttrs[rule.name] || []; + + var r = { + index: rule.index, + symbolizer: rule.symbolizer + }; + + if (_if) { + r.js = "if(" + _if + "){" + rule.value.toJS(env) + "}" + } else { + r.js = rule.value.toJS(env); + } + + r.constant = rule.value.ev(env).is !== 'field'; + r.filtered = !!_if; + + shaderAttrs[rule.name].push(r); + } else { + throw new Error("Ruleset not supported"); + //if (rule instanceof tree.Ruleset) { + //var sh = rule.toJS(env); + //for(var v in sh) { + //shaderAttrs[v] = shaderAttrs[v] || []; + //for(var attr in sh[v]) { + //shaderAttrs[v].push(sh[v][attr]); + //} + //} + //} + } + }); + return shaderAttrs; +}; + + +})(require('../tree')); + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"../tree":41,"assert":29,"underscore":102}],46:[function(require,module,exports){ +(function (global){ +(function(tree) { +var _ = global._ || require('underscore'); +// +// A number with a unit +// +tree.Dimension = function Dimension(value, unit, index) { + this.value = parseFloat(value); + this.unit = unit || null; + this.index = index; +}; + +tree.Dimension.prototype = { + is: 'float', + physical_units: ['m', 'cm', 'in', 'mm', 'pt', 'pc'], + screen_units: ['px', '%'], + all_units: ['m', 'cm', 'in', 'mm', 'pt', 'pc', 'px', '%'], + densities: { + m: 0.0254, + mm: 25.4, + cm: 2.54, + pt: 72, + pc: 6 + }, + ev: function (env) { + if (this.unit && !_.contains(this.all_units, this.unit)) { + env.error({ + message: "Invalid unit: '" + this.unit + "'", + index: this.index + }); + return { is: 'undefined', value: 'undefined' }; + } + + // normalize units which are not px or % + if (this.unit && _.contains(this.physical_units, this.unit)) { + if (!env.ppi) { + env.error({ + message: "ppi is not set, so metric units can't be used", + index: this.index + }); + return { is: 'undefined', value: 'undefined' }; + } + // convert all units to inch + // convert inch to px using ppi + this.value = (this.value / this.densities[this.unit]) * env.ppi; + this.unit = 'px'; + } + + return this; + }, + round: function() { + this.value = Math.round(this.value); + return this; + }, + toColor: function() { + return new tree.Color([this.value, this.value, this.value]); + }, + round: function() { + this.value = Math.round(this.value); + return this; + }, + toString: function() { + return this.value.toString(); + }, + operate: function(env, op, other) { + if (this.unit === '%' && other.unit !== '%') { + env.error({ + message: 'If two operands differ, the first must not be %', + index: this.index + }); + return { + is: 'undefined', + value: 'undefined' + }; + } + + if (this.unit !== '%' && other.unit === '%') { + if (op === '*' || op === '/' || op === '%') { + env.error({ + message: 'Percent values can only be added or subtracted from other values', + index: this.index + }); + return { + is: 'undefined', + value: 'undefined' + }; + } + + return new tree.Dimension(tree.operate(op, + this.value, this.value * other.value * 0.01), + this.unit); + } + + //here the operands are either the same (% or undefined or px), or one is undefined and the other is px + return new tree.Dimension(tree.operate(op, this.value, other.value), + this.unit || other.unit); + } +}; + +})(require('../tree')); + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"../tree":41,"underscore":102}],47:[function(require,module,exports){ +(function(tree) { + +// An element is an id or class selector +tree.Element = function Element(value) { + this.value = value.trim(); + if (this.value[0] === '#') { + this.type = 'id'; + this.clean = this.value.replace(/^#/, ''); + } + if (this.value[0] === '.') { + this.type = 'class'; + this.clean = this.value.replace(/^\./, ''); + } + if (this.value.indexOf('*') !== -1) { + this.type = 'wildcard'; + } +}; + +// Determine the 'specificity matrix' of this +// specific selector +tree.Element.prototype.specificity = function() { + return [ + (this.type === 'id') ? 1 : 0, // a + (this.type === 'class') ? 1 : 0 // b + ]; +}; + +tree.Element.prototype.toString = function() { return this.value; }; + +})(require('../tree')); + +},{"../tree":41}],48:[function(require,module,exports){ +(function(tree) { + +tree.Expression = function Expression(value) { + this.value = value; +}; + +tree.Expression.prototype = { + is: 'expression', + ev: function(env) { + if (this.value.length > 1) { + return new tree.Expression(this.value.map(function(e) { + return e.ev(env); + })); + } else { + return this.value[0].ev(env); + } + }, + + toString: function(env) { + return this.value.map(function(e) { + return e.toString(env); + }).join(' '); + } +}; + +})(require('../tree')); + +},{"../tree":41}],49:[function(require,module,exports){ +(function(tree) { + +tree.Field = function Field(content) { + this.value = content || ''; +}; + +tree.Field.prototype = { + is: 'field', + toString: function() { + return '[' + this.value + ']'; + }, + 'ev': function() { + return this; + } +}; + +})(require('../tree')); + +},{"../tree":41}],50:[function(require,module,exports){ +(function(tree) { + +tree.Filter = function Filter(key, op, val, index, filename) { + this.key = key; + this.op = op; + this.val = val; + this.index = index; + this.filename = filename; + + this.id = this.key + this.op + this.val; +}; + +// xmlsafe, numeric, suffix +var ops = { + '<': [' < ', 'numeric'], + '>': [' > ', 'numeric'], + '=': [' = ', 'both'], + '!=': [' != ', 'both'], + '<=': [' <= ', 'numeric'], + '>=': [' >= ', 'numeric'], + '=~': ['.match(', 'string', ')'] +}; + +tree.Filter.prototype.ev = function(env) { + this.key = this.key.ev(env); + this.val = this.val.ev(env); + return this; +}; + +tree.Filter.prototype.toXML = function(env) { + if (tree.Reference.data.filter) { + if (this.key.is === 'keyword' && -1 === tree.Reference.data.filter.value.indexOf(this.key.toString())) { + env.error({ + message: this.key.toString() + ' is not a valid keyword in a filter expression', + index: this.index, + filename: this.filename + }); + } + if (this.val.is === 'keyword' && -1 === tree.Reference.data.filter.value.indexOf(this.val.toString())) { + env.error({ + message: this.val.toString() + ' is not a valid keyword in a filter expression', + index: this.index, + filename: this.filename + }); + } + } + var key = this.key.toString(false); + var val = this.val.toString(this.val.is == 'string'); + + if ( + (ops[this.op][1] == 'numeric' && isNaN(val) && this.val.is !== 'field') || + (ops[this.op][1] == 'string' && (val)[0] != "'") + ) { + env.error({ + message: 'Cannot use operator "' + this.op + '" with value ' + this.val, + index: this.index, + filename: this.filename + }); + } + + return key + ops[this.op][0] + val + (ops[this.op][2] || ''); +}; + +tree.Filter.prototype.toString = function() { + return '[' + this.id + ']'; +}; + +})(require('../tree')); + +},{"../tree":41}],51:[function(require,module,exports){ +(function (global){ +var tree = require('../tree'); +var _ = global._ || require('underscore'); + +tree.Filterset = function Filterset() { + this.filters = {}; +}; + +tree.Filterset.prototype.toXML = function(env) { + var filters = []; + for (var id in this.filters) { + filters.push('(' + this.filters[id].toXML(env).trim() + ')'); + } + if (filters.length) { + return ' ' + filters.join(' and ') + '\n'; + } else { + return ''; + } +}; + +tree.Filterset.prototype.toString = function() { + var arr = []; + for (var id in this.filters) arr.push(this.filters[id].id); + return arr.sort().join('\t'); +}; + +tree.Filterset.prototype.ev = function(env) { + for (var i in this.filters) { + this.filters[i].ev(env); + } + return this; +}; + +tree.Filterset.prototype.clone = function() { + var clone = new tree.Filterset(); + for (var id in this.filters) { + clone.filters[id] = this.filters[id]; + } + return clone; +}; + +// Note: other has to be a tree.Filterset. +tree.Filterset.prototype.cloneWith = function(other) { + var additions = []; + for (var id in other.filters) { + var status = this.addable(other.filters[id]); + // status is true, false or null. if it's null we don't fail this + // clone nor do we add the filter. + if (status === false) { + return false; + } + if (status === true) { + // Adding the filter will override another value. + additions.push(other.filters[id]); + } + } + + // Adding the other filters doesn't make this filterset invalid, but it + // doesn't add anything to it either. + if (!additions.length) { + return null; + } + + // We can successfully add all filters. Now clone the filterset and add the + // new rules. + var clone = new tree.Filterset(); + + // We can add the rules that are already present without going through the + // add function as a Filterset is always in it's simplest canonical form. + for (id in this.filters) { + clone.filters[id] = this.filters[id]; + } + + // Only add new filters that actually change the filter. + while (id = additions.shift()) { + clone.add(id); + } + + return clone; +}; + +tree.Filterset.prototype.toJS = function(env) { + var opMap = { + '=': '===' + }; + return _.map(this.filters, function(filter) { + var op = filter.op; + if(op in opMap) { + op = opMap[op]; + } + var val = filter.val; + if(filter._val !== undefined) { + val = filter._val.toString(true); + } + var attrs = "data"; + return attrs + "." + filter.key.value + " " + op + " " + (val.is === 'string' ? "'"+ val +"'" : val); + }).join(' && '); +}; + +// Returns true when the new filter can be added, false otherwise. +// It can also return null, and on the other side we test for === true or +// false +tree.Filterset.prototype.addable = function(filter) { + var key = filter.key.toString(), + value = filter.val.toString(); + + if (value.match(/^[0-9]+(\.[0-9]*)?$/)) value = parseFloat(value); + + switch (filter.op) { + case '=': + // if there is already foo= and we're adding foo= + if (this.filters[key + '='] !== undefined) { + if (this.filters[key + '='].val.toString() != value) { + return false; + } else { + return null; + } + } + if (this.filters[key + '!=' + value] !== undefined) return false; + if (this.filters[key + '>'] !== undefined && this.filters[key + '>'].val >= value) return false; + if (this.filters[key + '<'] !== undefined && this.filters[key + '<'].val <= value) return false; + if (this.filters[key + '>='] !== undefined && this.filters[key + '>='].val > value) return false; + if (this.filters[key + '<='] !== undefined && this.filters[key + '<='].val < value) return false; + return true; + + case '=~': + return true; + + case '!=': + if (this.filters[key + '='] !== undefined) return (this.filters[key + '='].val == value) ? false : null; + if (this.filters[key + '!=' + value] !== undefined) return null; + if (this.filters[key + '>'] !== undefined && this.filters[key + '>'].val >= value) return null; + if (this.filters[key + '<'] !== undefined && this.filters[key + '<'].val <= value) return null; + if (this.filters[key + '>='] !== undefined && this.filters[key + '>='].val > value) return null; + if (this.filters[key + '<='] !== undefined && this.filters[key + '<='].val < value) return null; + return true; + + case '>': + if (key + '=' in this.filters) { + if (this.filters[key + '='].val <= value) { + return false; + } else { + return null; + } + } + if (this.filters[key + '<'] !== undefined && this.filters[key + '<'].val <= value) return false; + if (this.filters[key + '<='] !== undefined && this.filters[key + '<='].val <= value) return false; + if (this.filters[key + '>'] !== undefined && this.filters[key + '>'].val >= value) return null; + if (this.filters[key + '>='] !== undefined && this.filters[key + '>='].val > value) return null; + return true; + + case '>=': + if (this.filters[key + '=' ] !== undefined) return (this.filters[key + '='].val < value) ? false : null; + if (this.filters[key + '<' ] !== undefined && this.filters[key + '<'].val <= value) return false; + if (this.filters[key + '<='] !== undefined && this.filters[key + '<='].val < value) return false; + if (this.filters[key + '>' ] !== undefined && this.filters[key + '>'].val >= value) return null; + if (this.filters[key + '>='] !== undefined && this.filters[key + '>='].val >= value) return null; + return true; + + case '<': + if (this.filters[key + '=' ] !== undefined) return (this.filters[key + '='].val >= value) ? false : null; + if (this.filters[key + '>' ] !== undefined && this.filters[key + '>'].val >= value) return false; + if (this.filters[key + '>='] !== undefined && this.filters[key + '>='].val >= value) return false; + if (this.filters[key + '<' ] !== undefined && this.filters[key + '<'].val <= value) return null; + if (this.filters[key + '<='] !== undefined && this.filters[key + '<='].val < value) return null; + return true; + + case '<=': + if (this.filters[key + '=' ] !== undefined) return (this.filters[key + '='].val > value) ? false : null; + if (this.filters[key + '>' ] !== undefined && this.filters[key + '>'].val >= value) return false; + if (this.filters[key + '>='] !== undefined && this.filters[key + '>='].val > value) return false; + if (this.filters[key + '<' ] !== undefined && this.filters[key + '<'].val <= value) return null; + if (this.filters[key + '<='] !== undefined && this.filters[key + '<='].val <= value) return null; + return true; + } +}; + +// Does the new filter constitute a conflict? +tree.Filterset.prototype.conflict = function(filter) { + var key = filter.key.toString(), + value = filter.val.toString(); + + if (!isNaN(parseFloat(value))) value = parseFloat(value); + + // if (a=b) && (a=c) + // if (a=b) && (a!=b) + // or (a!=b) && (a=b) + if ((filter.op === '=' && this.filters[key + '='] !== undefined && + value != this.filters[key + '='].val.toString()) || + (filter.op === '!=' && this.filters[key + '='] !== undefined && + value == this.filters[key + '='].val.toString()) || + (filter.op === '=' && this.filters[key + '!='] !== undefined && + value == this.filters[key + '!='].val.toString())) { + return filter.toString() + ' added to ' + this.toString() + ' produces an invalid filter'; + } + + return false; +}; + +// Only call this function for filters that have been cleared by .addable(). +tree.Filterset.prototype.add = function(filter, env) { + var key = filter.key.toString(), + id, + op = filter.op, + conflict = this.conflict(filter), + numval; + + if (conflict) return conflict; + + if (op === '=') { + for (var i in this.filters) { + if (this.filters[i].key == key) delete this.filters[i]; + } + this.filters[key + '='] = filter; + } else if (op === '!=') { + this.filters[key + '!=' + filter.val] = filter; + } else if (op === '=~') { + this.filters[key + '=~' + filter.val] = filter; + } else if (op === '>') { + // If there are other filters that are also > + // but are less than this one, they don't matter, so + // remove them. + for (var j in this.filters) { + if (this.filters[j].key == key && this.filters[j].val <= filter.val) { + delete this.filters[j]; + } + } + this.filters[key + '>'] = filter; + } else if (op === '>=') { + for (var k in this.filters) { + numval = (+this.filters[k].val.toString()); + if (this.filters[k].key == key && numval < filter.val) { + delete this.filters[k]; + } + } + if (this.filters[key + '!=' + filter.val] !== undefined) { + delete this.filters[key + '!=' + filter.val]; + filter.op = '>'; + this.filters[key + '>'] = filter; + } + else { + this.filters[key + '>='] = filter; + } + } else if (op === '<') { + for (var l in this.filters) { + numval = (+this.filters[l].val.toString()); + if (this.filters[l].key == key && numval >= filter.val) { + delete this.filters[l]; + } + } + this.filters[key + '<'] = filter; + } else if (op === '<=') { + for (var m in this.filters) { + numval = (+this.filters[m].val.toString()); + if (this.filters[m].key == key && numval > filter.val) { + delete this.filters[m]; + } + } + if (this.filters[key + '!=' + filter.val] !== undefined) { + delete this.filters[key + '!=' + filter.val]; + filter.op = '<'; + this.filters[key + '<'] = filter; + } + else { + this.filters[key + '<='] = filter; + } + } +}; + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"../tree":41,"underscore":102}],52:[function(require,module,exports){ +(function(tree) { + +tree._getFontSet = function(env, fonts) { + var fontKey = fonts.join(''); + if (env._fontMap && env._fontMap[fontKey]) { + return env._fontMap[fontKey]; + } + + var new_fontset = new tree.FontSet(env, fonts); + env.effects.push(new_fontset); + if (!env._fontMap) env._fontMap = {}; + env._fontMap[fontKey] = new_fontset; + return new_fontset; +}; + +tree.FontSet = function FontSet(env, fonts) { + this.fonts = fonts; + this.name = 'fontset-' + env.effects.length; +}; + +tree.FontSet.prototype.toXML = function(env) { + return '\n' + + this.fonts.map(function(f) { + return ' '; + }).join('\n') + + '\n'; +}; + +})(require('../tree')); + +},{"../tree":41}],53:[function(require,module,exports){ +var tree = require('../tree'); + +// Storage for Frame offset value +// and stores them as bit-sequences so that they can be combined, +// inverted, and compared quickly. +tree.FrameOffset = function(op, value, index) { + value = parseInt(value, 10); + if (value > tree.FrameOffset.max || value <= 0) { + throw { + message: 'Only frame-offset levels between 1 and ' + + tree.FrameOffset.max + ' supported.', + index: index + }; + } + + if (op !== '=') { + throw { + message: 'only = operator is supported for frame-offset', + index: index + }; + } + return value; +}; + +tree.FrameOffset.max = 32; +tree.FrameOffset.none = 0; + + +},{"../tree":41}],54:[function(require,module,exports){ +(function(tree) { + +tree.ImageFilter = function ImageFilter(filter, args) { + this.filter = filter; + this.args = args || null; +}; + +tree.ImageFilter.prototype = { + is: 'imagefilter', + ev: function() { return this; }, + + toString: function() { + if (this.args) { + return this.filter + '(' + this.args.join(',') + ')'; + } else { + return this.filter; + } + } +}; + + +})(require('../tree')); + +},{"../tree":41}],55:[function(require,module,exports){ +(function (tree) { +tree.Invalid = function Invalid(chunk, index, message) { + this.chunk = chunk; + this.index = index; + this.type = 'syntax'; + this.message = message || "Invalid code: " + this.chunk; +}; + +tree.Invalid.prototype.is = 'invalid'; + +tree.Invalid.prototype.ev = function(env) { + env.error({ + chunk: this.chunk, + index: this.index, + type: 'syntax', + message: this.message || "Invalid code: " + this.chunk + }); + return { + is: 'undefined' + }; +}; +})(require('../tree')); + +},{"../tree":41}],56:[function(require,module,exports){ +(function(tree) { + +tree.Keyword = function Keyword(value) { + this.value = value; + var special = { + 'transparent': 'color', + 'true': 'boolean', + 'false': 'boolean' + }; + this.is = special[value] ? special[value] : 'keyword'; +}; +tree.Keyword.prototype = { + ev: function() { return this; }, + toString: function() { return this.value; } +}; + +})(require('../tree')); + +},{"../tree":41}],57:[function(require,module,exports){ +(function(tree) { + +tree.LayerXML = function(obj, styles) { + var dsoptions = []; + for (var i in obj.Datasource) { + dsoptions.push(''); + } + + var prop_string = ''; + for (var prop in obj.properties) { + if (prop === 'minzoom') { + prop_string += ' maxzoom="' + tree.Zoom.ranges[obj.properties[prop]] + '"\n'; + } else if (prop === 'maxzoom') { + prop_string += ' minzoom="' + tree.Zoom.ranges[obj.properties[prop]+1] + '"\n'; + } else { + prop_string += ' ' + prop + '="' + obj.properties[prop] + '"\n'; + } + } + + return '\n ' + + styles.reverse().map(function(s) { + return '' + s + ''; + }).join('\n ') + + (dsoptions.length ? + '\n \n ' + + dsoptions.join('\n ') + + '\n \n' + : '') + + ' \n'; +}; + +})(require('../tree')); + +},{"../tree":41}],58:[function(require,module,exports){ +// A literal is a literal string for Mapnik - the +// result of the combination of a `tree.Field` with any +// other type. +(function(tree) { + +tree.Literal = function Field(content) { + this.value = content || ''; + this.is = 'field'; +}; + +tree.Literal.prototype = { + toString: function() { + return this.value; + }, + 'ev': function() { + return this; + } +}; + +})(require('../tree')); + +},{"../tree":41}],59:[function(require,module,exports){ +// An operation is an expression with an op in between two operands, +// like 2 + 1. +(function(tree) { + +tree.Operation = function Operation(op, operands, index) { + this.op = op.trim(); + this.operands = operands; + this.index = index; +}; + +tree.Operation.prototype.is = 'operation'; + +tree.Operation.prototype.ev = function(env) { + var a = this.operands[0].ev(env), + b = this.operands[1].ev(env), + temp; + + if (a.is === 'undefined' || b.is === 'undefined') { + return { + is: 'undefined', + value: 'undefined' + }; + } + + if (a instanceof tree.Dimension && b instanceof tree.Color) { + if (this.op === '*' || this.op === '+') { + temp = b, b = a, a = temp; + } else { + env.error({ + name: "OperationError", + message: "Can't substract or divide a color from a number", + index: this.index + }); + } + } + + // Only concatenate plain strings, because this is easily + // pre-processed + if (a instanceof tree.Quoted && b instanceof tree.Quoted && this.op !== '+') { + env.error({ + message: "Can't subtract, divide, or multiply strings.", + index: this.index, + type: 'runtime', + filename: this.filename + }); + return { + is: 'undefined', + value: 'undefined' + }; + } + + // Fields, literals, dimensions, and quoted strings can be combined. + if (a instanceof tree.Field || b instanceof tree.Field || + a instanceof tree.Literal || b instanceof tree.Literal) { + if (a.is === 'color' || b.is === 'color') { + env.error({ + message: "Can't subtract, divide, or multiply colors in expressions.", + index: this.index, + type: 'runtime', + filename: this.filename + }); + return { + is: 'undefined', + value: 'undefined' + }; + } else { + return new tree.Literal(a.ev(env).toString(true) + this.op + b.ev(env).toString(true)); + } + } + + if (a.operate === undefined) { + env.error({ + message: 'Cannot do math with type ' + a.is + '.', + index: this.index, + type: 'runtime', + filename: this.filename + }); + return { + is: 'undefined', + value: 'undefined' + }; + } + + return a.operate(env, this.op, b); +}; + +tree.operate = function(op, a, b) { + switch (op) { + case '+': return a + b; + case '-': return a - b; + case '*': return a * b; + case '%': return a % b; + case '/': return a / b; + } +}; + +})(require('../tree')); + +},{"../tree":41}],60:[function(require,module,exports){ +(function(tree) { + +tree.Quoted = function Quoted(content) { + this.value = content || ''; +}; + +tree.Quoted.prototype = { + is: 'string', + + toString: function(quotes) { + var escapedValue = this.value + .replace(/&/g, '&') + var xmlvalue = escapedValue + .replace(/\'/g, '\\\'') + .replace(/\"/g, '"') + .replace(//g, '>'); + return (quotes === true) ? "'" + xmlvalue + "'" : escapedValue; + }, + + 'ev': function() { + return this; + }, + + operate: function(env, op, other) { + return new tree.Quoted(tree.operate(op, this.toString(), other.toString(this.contains_field))); + } +}; + +})(require('../tree')); + +},{"../tree":41}],61:[function(require,module,exports){ +(function (global){ +// Carto pulls in a reference from the `mapnik-reference` +// module. This file builds indexes from that file for its various +// options, and provides validation methods for property: value +// combinations. +(function(tree) { + +var _ = global._ || require('underscore'), + ref = {}; + +ref.setData = function(data) { + ref.data = data; + ref.selector_cache = generateSelectorCache(data); + ref.mapnikFunctions = generateMapnikFunctions(data); + + ref.mapnikFunctions.matrix = [6]; + ref.mapnikFunctions.translate = [1, 2]; + ref.mapnikFunctions.scale = [1, 2]; + ref.mapnikFunctions.rotate = [1, 3]; + ref.mapnikFunctions.skewX = [1]; + ref.mapnikFunctions.skewY = [1]; + + ref.required_cache = generateRequiredProperties(data); +}; + +ref.setVersion = function(version) { + var mapnik_reference = require('mapnik-reference'); + if (mapnik_reference.version.hasOwnProperty(version)) { + ref.setData(mapnik_reference.version[version]); + return true; + } else { + return false; + } +}; + +ref.selectorData = function(selector, i) { + if (ref.selector_cache[selector]) return ref.selector_cache[selector][i]; +}; + +ref.validSelector = function(selector) { return !!ref.selector_cache[selector]; }; +ref.selectorName = function(selector) { return ref.selectorData(selector, 2); }; +ref.selector = function(selector) { return ref.selectorData(selector, 0); }; +ref.symbolizer = function(selector) { return ref.selectorData(selector, 1); }; + +function generateSelectorCache(data) { + var index = {}; + for (var i in data.symbolizers) { + for (var j in data.symbolizers[i]) { + if (data.symbolizers[i][j].hasOwnProperty('css')) { + index[data.symbolizers[i][j].css] = [data.symbolizers[i][j], i, j]; + } + } + } + return index; +} + +function generateMapnikFunctions(data) { + var functions = {}; + for (var i in data.symbolizers) { + for (var j in data.symbolizers[i]) { + if (data.symbolizers[i][j].type === 'functions') { + for (var k = 0; k < data.symbolizers[i][j].functions.length; k++) { + var fn = data.symbolizers[i][j].functions[k]; + functions[fn[0]] = fn[1]; + } + } + } + } + return functions; +} + +function generateRequiredProperties(data) { + var cache = {}; + for (var symbolizer_name in data.symbolizers) { + cache[symbolizer_name] = []; + for (var j in data.symbolizers[symbolizer_name]) { + if (data.symbolizers[symbolizer_name][j].required) { + cache[symbolizer_name].push(data.symbolizers[symbolizer_name][j].css); + } + } + } + return cache; +} + +ref.requiredProperties = function(symbolizer_name, rules) { + var req = ref.required_cache[symbolizer_name]; + for (var i in req) { + if (!(req[i] in rules)) { + return 'Property ' + req[i] + ' required for defining ' + + symbolizer_name + ' styles.'; + } + } +}; + +// TODO: finish implementation - this is dead code +ref._validateValue = { + 'font': function(env, value) { + if (env.validation_data && env.validation_data.fonts) { + return env.validation_data.fonts.indexOf(value) != -1; + } else { + return true; + } + } +}; + +ref.isFont = function(selector) { + return ref.selector(selector).validate == 'font'; +}; + +// https://gist.github.com/982927 +ref.editDistance = function(a, b){ + if (a.length === 0) return b.length; + if (b.length === 0) return a.length; + var matrix = []; + for (var i = 0; i <= b.length; i++) { matrix[i] = [i]; } + for (var j = 0; j <= a.length; j++) { matrix[0][j] = j; } + for (i = 1; i <= b.length; i++) { + for (j = 1; j <= a.length; j++) { + if (b.charAt(i-1) == a.charAt(j-1)) { + matrix[i][j] = matrix[i-1][j-1]; + } else { + matrix[i][j] = Math.min(matrix[i-1][j-1] + 1, // substitution + Math.min(matrix[i][j-1] + 1, // insertion + matrix[i-1][j] + 1)); // deletion + } + } + } + return matrix[b.length][a.length]; +}; + +function validateFunctions(value, selector) { + if (value.value[0].is === 'string') return true; + for (var i in value.value) { + for (var j in value.value[i].value) { + if (value.value[i].value[j].is !== 'call') return false; + var f = _.find(ref + .selector(selector).functions, function(x) { + return x[0] == value.value[i].value[j].name; + }); + if (!(f && f[1] == -1)) { + // This filter is unknown or given an incorrect number of arguments + if (!f || f[1] !== value.value[i].value[j].args.length) return false; + } + } + } + return true; +} + +function validateKeyword(value, selector) { + if (typeof ref.selector(selector).type === 'object') { + return ref.selector(selector).type + .indexOf(value.value[0].value) !== -1; + } else { + // allow unquoted keywords as strings + return ref.selector(selector).type === 'string'; + } +} + +ref.validValue = function(env, selector, value) { + var i, j; + // TODO: handle in reusable way + if (!ref.selector(selector)) { + return false; + } else if (value.value[0].is == 'keyword') { + return validateKeyword(value, selector); + } else if (value.value[0].is == 'undefined') { + // caught earlier in the chain - ignore here so that + // error is not overridden + return true; + } else if (ref.selector(selector).type == 'numbers') { + for (i in value.value) { + if (value.value[i].is !== 'float') { + return false; + } + } + return true; + } else if (ref.selector(selector).type == 'tags') { + if (!value.value) return false; + if (!value.value[0].value) { + return value.value[0].is === 'tag'; + } + for (i = 0; i < value.value[0].value.length; i++) { + if (value.value[0].value[i].is !== 'tag') return false; + } + return true; + } else if (ref.selector(selector).type == 'functions') { + // For backwards compatibility, you can specify a string for `functions`-compatible + // values, though they will not be validated. + return validateFunctions(value, selector); + } else if (ref.selector(selector).type === 'unsigned') { + if (value.value[0].is === 'float') { + value.value[0].round(); + return true; + } else { + return false; + } + } else if ((ref.selector(selector).expression)) { + return true; + } else { + if (ref.selector(selector).validate) { + var valid = false; + for (i = 0; i < value.value.length; i++) { + if (ref.selector(selector).type == value.value[i].is && + ref + ._validateValue + [ref.selector(selector).validate] + (env, value.value[i].value)) { + return true; + } + } + return valid; + } else { + return ref.selector(selector).type == value.value[0].is; + } + } +}; + +tree.Reference = ref; + +})(require('../tree')); + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"../tree":41,"mapnik-reference":70,"underscore":102}],62:[function(require,module,exports){ +(function(tree) { +// a rule is a single property and value combination, or variable +// name and value combination, like +// polygon-opacity: 1.0; or @opacity: 1.0; +tree.Rule = function Rule(name, value, index, filename) { + var parts = name.split('/'); + this.name = parts.pop(); + this.instance = parts.length ? parts[0] : '__default__'; + this.value = (value instanceof tree.Value) ? + value : new tree.Value([value]); + this.index = index; + this.symbolizer = tree.Reference.symbolizer(this.name); + this.filename = filename; + this.variable = (name.charAt(0) === '@'); +}; + +tree.Rule.prototype.is = 'rule'; + +tree.Rule.prototype.clone = function() { + var clone = Object.create(tree.Rule.prototype); + clone.name = this.name; + clone.value = this.value; + clone.index = this.index; + clone.instance = this.instance; + clone.symbolizer = this.symbolizer; + clone.filename = this.filename; + clone.variable = this.variable; + return clone; +}; + +tree.Rule.prototype.updateID = function() { + return this.id = this.zoom + '#' + this.instance + '#' + this.name; +}; + +tree.Rule.prototype.toString = function() { + return '[' + tree.Zoom.toString(this.zoom) + '] ' + this.name + ': ' + this.value; +}; + +function getMean(name) { + return Object.keys(tree.Reference.selector_cache).map(function(f) { + return [f, tree.Reference.editDistance(name, f)]; + }).sort(function(a, b) { return a[1] - b[1]; }); +} + +// second argument, if true, outputs the value of this +// rule without the usual attribute="content" wrapping. Right +// now this is just for the TextSymbolizer, but applies to other +// properties in reference.json which specify serialization=content +tree.Rule.prototype.toXML = function(env, content, sep, format) { + if (!tree.Reference.validSelector(this.name)) { + var mean = getMean(this.name); + var mean_message = ''; + if (mean[0][1] < 3) { + mean_message = '. Did you mean ' + mean[0][0] + '?'; + } + return env.error({ + message: "Unrecognized rule: " + this.name + mean_message, + index: this.index, + type: 'syntax', + filename: this.filename + }); + } + + if ((this.value instanceof tree.Value) && + !tree.Reference.validValue(env, this.name, this.value)) { + if (!tree.Reference.selector(this.name)) { + return env.error({ + message: 'Unrecognized property: ' + + this.name, + index: this.index, + type: 'syntax', + filename: this.filename + }); + } else { + var typename; + if (tree.Reference.selector(this.name).validate) { + typename = tree.Reference.selector(this.name).validate; + } else if (typeof tree.Reference.selector(this.name).type === 'object') { + typename = 'keyword (options: ' + tree.Reference.selector(this.name).type.join(', ') + ')'; + } else { + typename = tree.Reference.selector(this.name).type; + } + return env.error({ + message: 'Invalid value for ' + + this.name + + ', the type ' + typename + + ' is expected. ' + this.value + + ' (of type ' + this.value.value[0].is + ') ' + + ' was given.', + index: this.index, + type: 'syntax', + filename: this.filename + }); + } + } + + if (this.variable) { + return ''; + } else if (tree.Reference.isFont(this.name) && this.value.value.length > 1) { + var f = tree._getFontSet(env, this.value.value); + return 'fontset-name="' + f.name + '"'; + } else if (content) { + return this.value.toString(env, this.name, sep); + } else { + return tree.Reference.selectorName(this.name) + + '="' + + this.value.toString(env, this.name) + + '"'; + } +}; + +// TODO: Rule ev chain should add fontsets to env.frames +tree.Rule.prototype.ev = function(context) { + return new tree.Rule(this.name, + this.value.ev(context), + this.index, + this.filename); +}; + +})(require('../tree')); + +},{"../tree":41}],63:[function(require,module,exports){ +(function(tree) { + +tree.Ruleset = function Ruleset(selectors, rules) { + this.selectors = selectors; + this.rules = rules; + // static cache of find() function + this._lookups = {}; +}; +tree.Ruleset.prototype = { + is: 'ruleset', + 'ev': function(env) { + var i, + ruleset = new tree.Ruleset(this.selectors, this.rules.slice(0)); + ruleset.root = this.root; + + // push the current ruleset to the frames stack + env.frames.unshift(ruleset); + + // Evaluate everything else + for (i = 0, rule; i < ruleset.rules.length; i++) { + rule = ruleset.rules[i]; + ruleset.rules[i] = rule.ev ? rule.ev(env) : rule; + } + + // Pop the stack + env.frames.shift(); + + return ruleset; + }, + match: function(args) { + return !args || args.length === 0; + }, + variables: function() { + if (this._variables) { return this._variables; } + else { + return this._variables = this.rules.reduce(function(hash, r) { + if (r instanceof tree.Rule && r.variable === true) { + hash[r.name] = r; + } + return hash; + }, {}); + } + }, + variable: function(name) { + return this.variables()[name]; + }, + rulesets: function() { + if (this._rulesets) { return this._rulesets; } + else { + return this._rulesets = this.rules.filter(function(r) { + return (r instanceof tree.Ruleset); + }); + } + }, + find: function(selector, self) { + self = self || this; + var rules = [], rule, match, + key = selector.toString(); + + if (key in this._lookups) { return this._lookups[key]; } + + this.rulesets().forEach(function(rule) { + if (rule !== self) { + for (var j = 0; j < rule.selectors.length; j++) { + match = selector.match(rule.selectors[j]); + if (match) { + if (selector.elements.length > 1) { + Array.prototype.push.apply(rules, rule.find( + new tree.Selector(null, null, null, selector.elements.slice(1)), self)); + } else { + rules.push(rule); + } + break; + } + } + } + }); + return this._lookups[key] = rules; + }, + // Zooms can use variables. This replaces tree.Zoom objects on selectors + // with simple bit-arrays that we can compare easily. + evZooms: function(env) { + for (var i = 0; i < this.selectors.length; i++) { + var zval = tree.Zoom.all; + for (var z = 0; z < this.selectors[i].zoom.length; z++) { + zval = zval & this.selectors[i].zoom[z].ev(env).zoom; + } + this.selectors[i].zoom = zval; + } + }, + flatten: function(result, parents, env) { + var selectors = [], i, j; + if (this.selectors.length === 0) { + env.frames = env.frames.concat(this.rules); + } + // evaluate zoom variables on this object. + this.evZooms(env); + for (i = 0; i < this.selectors.length; i++) { + var child = this.selectors[i]; + + if (!child.filters) { + // TODO: is this internal inconsistency? + // This is an invalid filterset. + continue; + } + + if (parents.length) { + for (j = 0; j < parents.length; j++) { + var parent = parents[j]; + + var mergedFilters = parent.filters.cloneWith(child.filters); + if (mergedFilters === null) { + // Filters could be added, but they didn't change the + // filters. This means that we only have to clone when + // the zoom levels or the attachment is different too. + if (parent.zoom === (parent.zoom & child.zoom) && + parent.frame_offset === child.frame_offset && + parent.attachment === child.attachment && + parent.elements.join() === child.elements.join()) { + selectors.push(parent); + continue; + } else { + mergedFilters = parent.filters; + } + } else if (!mergedFilters) { + // The merged filters are invalid, that means we don't + // have to clone. + continue; + } + + var clone = Object.create(tree.Selector.prototype); + clone.filters = mergedFilters; + clone.zoom = parent.zoom & child.zoom; + clone.frame_offset = child.frame_offset; + clone.elements = parent.elements.concat(child.elements); + if (parent.attachment && child.attachment) { + clone.attachment = parent.attachment + '/' + child.attachment; + } + else clone.attachment = child.attachment || parent.attachment; + clone.conditions = parent.conditions + child.conditions; + clone.index = child.index; + selectors.push(clone); + } + } else { + selectors.push(child); + } + } + + var rules = []; + for (i = 0; i < this.rules.length; i++) { + var rule = this.rules[i]; + + // Recursively flatten any nested rulesets + if (rule instanceof tree.Ruleset) { + rule.flatten(result, selectors, env); + } else if (rule instanceof tree.Rule) { + rules.push(rule); + } else if (rule instanceof tree.Invalid) { + env.error(rule); + } + } + + var index = rules.length ? rules[0].index : false; + for (i = 0; i < selectors.length; i++) { + // For specificity sort, use the position of the first rule to allow + // defining attachments that are under current element as a descendant + // selector. + if (index !== false) { + selectors[i].index = index; + } + result.push(new tree.Definition(selectors[i], rules.slice())); + } + + return result; + } +}; +})(require('../tree')); + +},{"../tree":41}],64:[function(require,module,exports){ +(function(tree) { + +tree.Selector = function Selector(filters, zoom, frame_offset, elements, attachment, conditions, index) { + this.elements = elements || []; + this.attachment = attachment; + this.filters = filters || {}; + this.frame_offset = frame_offset; + this.zoom = typeof zoom !== 'undefined' ? zoom : tree.Zoom.all; + this.conditions = conditions; + this.index = index; +}; + +// Determine the specificity of this selector +// based on the specificity of its elements - calling +// Element.specificity() in order to do so +// +// [ID, Class, Filters, Position in document] +tree.Selector.prototype.specificity = function() { + return this.elements.reduce(function(memo, e) { + var spec = e.specificity(); + memo[0] += spec[0]; + memo[1] += spec[1]; + return memo; + }, [0, 0, this.conditions, this.index]); +}; + +})(require('../tree')); + +},{"../tree":41}],65:[function(require,module,exports){ +(function (global){ +(function(tree) { +var _ = global._ || require('underscore'); + +// Given a style's name, attachment, definitions, and an environment object, +// return a stringified style for Mapnik +tree.StyleXML = function(name, attachment, definitions, env) { + var existing = {}; + var image_filters = [], image_filters_inflate = [], direct_image_filters = [], comp_op = [], opacity = []; + + for (var i = 0; i < definitions.length; i++) { + for (var j = 0; j < definitions[i].rules.length; j++) { + if (definitions[i].rules[j].name === 'image-filters') { + image_filters.push(definitions[i].rules[j]); + } + if (definitions[i].rules[j].name === 'image-filters-inflate') { + image_filters_inflate.push(definitions[i].rules[j]); + } + if (definitions[i].rules[j].name === 'direct-image-filters') { + direct_image_filters.push(definitions[i].rules[j]); + } + if (definitions[i].rules[j].name === 'comp-op') { + comp_op.push(definitions[i].rules[j]); + } + if (definitions[i].rules[j].name === 'opacity') { + opacity.push(definitions[i].rules[j]); + } + } + } + + var rules = definitions.map(function(definition) { + return definition.toXML(env, existing); + }); + + var attrs_xml = ''; + + if (image_filters.length) { + attrs_xml += ' image-filters="' + _.chain(image_filters) + // prevent identical filters from being duplicated in the style + .uniq(function(i) { return i.id; }).map(function(f) { + return f.ev(env).toXML(env, true, ',', 'image-filter'); + }).value().join(',') + '"'; + } + + if (image_filters_inflate.length) { + attrs_xml += ' image-filters-inflate="' + image_filters_inflate[0].value.ev(env).toString() + '"'; + } + + if (direct_image_filters.length) { + attrs_xml += ' direct-image-filters="' + _.chain(direct_image_filters) + // prevent identical filters from being duplicated in the style + .uniq(function(i) { return i.id; }).map(function(f) { + return f.ev(env).toXML(env, true, ',', 'direct-image-filter'); + }).value().join(',') + '"'; + } + + if (comp_op.length && comp_op[0].value.ev(env).value != 'src-over') { + attrs_xml += ' comp-op="' + comp_op[0].value.ev(env).toString() + '"'; + } + + if (opacity.length && opacity[0].value.ev(env).value != 1) { + attrs_xml += ' opacity="' + opacity[0].value.ev(env).toString() + '"'; + } + var rule_string = rules.join(''); + if (!attrs_xml && !rule_string) return ''; + return ''; +}; + +})(require('../tree')); + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"../tree":41,"underscore":102}],66:[function(require,module,exports){ +(function(tree) { + +tree.URL = function URL(val, paths) { + this.value = val; + this.paths = paths; +}; + +tree.URL.prototype = { + is: 'uri', + toString: function() { + return this.value.toString(); + }, + ev: function(ctx) { + return new tree.URL(this.value.ev(ctx), this.paths); + } +}; + +})(require('../tree')); + +},{"../tree":41}],67:[function(require,module,exports){ +(function(tree) { + +tree.Value = function Value(value) { + this.value = value; +}; + +tree.Value.prototype = { + is: 'value', + ev: function(env) { + if (this.value.length === 1) { + return this.value[0].ev(env); + } else { + return new tree.Value(this.value.map(function(v) { + return v.ev(env); + })); + } + }, + toString: function(env, selector, sep, format) { + return this.value.map(function(e) { + return e.toString(env, format); + }).join(sep || ', '); + }, + clone: function() { + var obj = Object.create(tree.Value.prototype); + if (Array.isArray(obj)) obj.value = this.value.slice(); + else obj.value = this.value; + obj.is = this.is; + return obj; + }, + + toJS: function(env) { + //var v = this.value[0].value[0]; + var val = this.ev(env); + var v = val.toString(); + if(val.is === "color" || val.is === 'uri' || val.is === 'string' || val.is === 'keyword') { + v = "'" + v + "'"; + } else if (val.is === 'field') { + // replace [variable] by ctx['variable'] + v = v.replace(/\[(.*)\]/g, "data['$1']"); + }else if (val.is === 'call') { + v = JSON.stringify({ + name: val.name, + args: val.args + }) + } + return "_value = " + v + ";"; + } + +}; + +})(require('../tree')); + +},{"../tree":41}],68:[function(require,module,exports){ +(function(tree) { + +tree.Variable = function Variable(name, index, filename) { + this.name = name; + this.index = index; + this.filename = filename; +}; + +tree.Variable.prototype = { + is: 'variable', + toString: function() { + return this.name; + }, + ev: function(env) { + var variable, + v, + name = this.name; + + if (this._css) return this._css; + + var thisframe = env.frames.filter(function(f) { + return f.name == this.name; + }.bind(this)); + if (thisframe.length) { + return thisframe[0].value.ev(env); + } else { + env.error({ + message: 'variable ' + this.name + ' is undefined', + index: this.index, + type: 'runtime', + filename: this.filename + }); + return { + is: 'undefined', + value: 'undefined' + }; + } + } +}; + +})(require('../tree')); + +},{"../tree":41}],69:[function(require,module,exports){ +var tree = require('../tree'); + +// Storage for zoom ranges. Only supports continuous ranges, +// and stores them as bit-sequences so that they can be combined, +// inverted, and compared quickly. +tree.Zoom = function(op, value, index) { + this.op = op; + this.value = value; + this.index = index; +}; + +tree.Zoom.prototype.setZoom = function(zoom) { + this.zoom = zoom; + return this; +}; + +tree.Zoom.prototype.ev = function(env) { + var start = 0, + end = Infinity, + value = parseInt(this.value.ev(env).toString(), 10), + zoom = 0; + + if (value > tree.Zoom.maxZoom || value < 0) { + env.error({ + message: 'Only zoom levels between 0 and ' + + tree.Zoom.maxZoom + ' supported.', + index: this.index + }); + } + + switch (this.op) { + case '=': + this.zoom = 1 << value; + return this; + case '>': + start = value + 1; + break; + case '>=': + start = value; + break; + case '<': + end = value - 1; + break; + case '<=': + end = value; + break; + } + for (var i = 0; i <= tree.Zoom.maxZoom; i++) { + if (i >= start && i <= end) { + zoom |= (1 << i); + } + } + this.zoom = zoom; + return this; +}; + +tree.Zoom.prototype.toString = function() { + return this.zoom; +}; + +// Covers all zoomlevels from 0 to 22 +tree.Zoom.all = 0x7FFFFF; + +tree.Zoom.maxZoom = 22; + +tree.Zoom.ranges = { + 0: 1000000000, + 1: 500000000, + 2: 200000000, + 3: 100000000, + 4: 50000000, + 5: 25000000, + 6: 12500000, + 7: 6500000, + 8: 3000000, + 9: 1500000, + 10: 750000, + 11: 400000, + 12: 200000, + 13: 100000, + 14: 50000, + 15: 25000, + 16: 12500, + 17: 5000, + 18: 2500, + 19: 1500, + 20: 750, + 21: 500, + 22: 250, + 23: 100 +}; + +// Only works for single range zooms. `[XXX....XXXXX.........]` is invalid. +tree.Zoom.prototype.toXML = function() { + var conditions = []; + if (this.zoom != tree.Zoom.all) { + var start = null, end = null; + for (var i = 0; i <= tree.Zoom.maxZoom; i++) { + if (this.zoom & (1 << i)) { + if (start === null) start = i; + end = i; + } + } + if (start > 0) conditions.push(' ' + + tree.Zoom.ranges[start] + '\n'); + if (end < 22) conditions.push(' ' + + tree.Zoom.ranges[end + 1] + '\n'); + } + return conditions; +}; + +tree.Zoom.prototype.toString = function() { + var str = ''; + for (var i = 0; i <= tree.Zoom.maxZoom; i++) { + str += (this.zoom & (1 << i)) ? 'X' : '.'; + } + return str; +}; + +},{"../tree":41}],70:[function(require,module,exports){ +(function (__dirname){ +var fs = require('fs'), + path = require('path'), + existsSync = require('fs').existsSync || require('path').existsSync; + +// Load all stated versions into the module exports +module.exports.version = {}; + +var refs = [ + '2.0.0', + '2.0.1', + '2.0.2', + '2.1.0', + '2.1.1', + '2.2.0', + '2.3.0', + '3.0.0' +]; + +refs.map(function(version) { + module.exports.version[version] = require(path.join(__dirname, version, 'reference.json')); + var ds_path = path.join(__dirname, version, 'datasources.json'); + if (existsSync(ds_path)) { + module.exports.version[version].datasources = require(ds_path).datasources; + } +}); + +}).call(this,"/node_modules/carto/node_modules/mapnik-reference") +},{"fs":28,"path":30}],71:[function(require,module,exports){ +module.exports={ + "name": "carto", + "version": "0.15.1-cdb1", + "description": "CartoCSS Stylesheet Compiler", + "url": "https://github.com/cartodb/carto", + "repository": { + "type": "git", + "url": "git+ssh://git@github.com/cartodb/carto.git" + }, + "author": { + "name": "CartoDB", + "url": "http://cartodb.com/" + }, + "keywords": [ + "maps", + "css", + "stylesheets" + ], + "contributors": [ + { + "name": "Tom MacWright", + "email": "macwright@gmail.com" + }, + { + "name": "Konstantin Käfer" + }, + { + "name": "Alexis Sellier", + "email": "self@cloudhead.net" + }, + { + "name": "Raul Ochoa", + "email": "rochoa@cartodb.com" + }, + { + "name": "Javi Santana", + "email": "jsantana@cartodb.com" + } + ], + "licenses": [ + { + "type": "Apache" + } + ], + "bin": { + "carto": "./bin/carto" + }, + "man": [ + "./man/carto.1" + ], + "main": "./lib/carto/index", + "engines": { + "node": ">=0.4.x" + }, + "dependencies": { + "underscore": "~1.6.0", + "mapnik-reference": "~6.0.2", + "optimist": "~0.6.0" + }, + "devDependencies": { + "mocha": "1.12.x", + "jshint": "0.2.x", + "sax": "0.1.x", + "istanbul": "~0.2.14", + "coveralls": "~2.10.1", + "browserify": "~7.0.0", + "uglify-js": "1.3.3" + }, + "scripts": { + "pretest": "npm install", + "test": "mocha -R spec", + "coverage": "istanbul cover ./node_modules/.bin/_mocha && coveralls < ./coverage/lcov.info" + }, + "gitHead": "8050ec843f1f32a6469e5d1cf49602773015d398", + "readme": "# CartoCSS\n\n[![Build Status](https://secure.travis-ci.org/mapbox/carto.png)](http://travis-ci.org/mapbox/carto)\n\nIs as stylesheet renderer for javascript, It's an evolution of the Mapnik renderer from Mapbox.\nPlease, see original [Mapbox repo](http://github.com/mapbox/carto) for more information and credits\n\n## Quick Start\n\n```javascript\n// shader is a CartoCSS object\n\nvar cartocss = [\n '#layer {',\n ' marker-width: [property]',\n ' marker-fill: red',\n '}'\n].join('')\nvar shader = new carto.RendererJS().render(cartocss);\nvar layers = shader.getLayers()\nfor (var i = 0; i < layers.length; ++i) {\n var layer = layers[i];\n console.log(\"layer name: \", layer.fullName())\n console.log(\"- frames: \", layer.frames())\n console.log(\"- attachment: \", layer.attachment())\n\n var layerShader = layer.getStyle({ property: 1 }, { zoom: 10 })\n console.log(layerShader['marker-width']) // 1\n console.log(layerShader['marker-fill']) // #FF0000\n}\n\n```\n\n# API\n\n## RendererJS\n\n### render(cartocss)\n\n## CartoCSS\n\ncompiled cartocss object\n\n### getLayers\n\nreturn the layers, an array of ``CartoCSS.Layer`` object\n\n### getDefault\n\nreturn the default layer (``CartoCSS.Layer``), usually the Map layer\n\n\n### findLayer(where)\n\nfind a layer using where object.\n\n```\nshader.findLayer({ name: 'test' })\n```\n\n## CartoCSS.Layer\n\n### getStyle(props, context)\n\nreturn the evaluated style:\n - props: object containing properties needed to render the style. If the cartocss style uses\n some variables they should be passed in this object\n - context: rendering context variables like ``zoom`` or animation ``frame``\n\n\n\n\n\n\n\n\n\n\n## Reference Documentation\n\n* [mapbox.com/carto](http://mapbox.com/carto/)\n\n\n", + "readmeFilename": "README.md", + "bugs": { + "url": "https://github.com/cartodb/carto/issues" + }, + "homepage": "https://github.com/cartodb/carto#readme", + "_id": "carto@0.15.1-cdb1", + "_shasum": "9bd71a967caad86493561b8a335a38137aec3098", + "_from": "cartodb/carto#0.15.1-cdb1", + "_resolved": "git://github.com/cartodb/carto.git#8050ec843f1f32a6469e5d1cf49602773015d398" +} + +},{}],72:[function(require,module,exports){ +if (typeof Function.prototype.bind != 'function') { + Function.prototype.bind = function bind(obj) { + var args = Array.prototype.slice.call(arguments, 1), + self = this, + nop = function() { + }, + bound = function() { + return self.apply( + this instanceof nop ? this : (obj || {}), args.concat( + Array.prototype.slice.call(arguments) + ) + ); + }; + nop.prototype = this.prototype || {}; + bound.prototype = new nop(); + return bound; + }; +} + +},{}],73:[function(require,module,exports){ +/*! + * QUnit 1.23.1 + * https://qunitjs.com/ + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license + * https://jquery.org/license + * + * Date: 2016-04-12T17:29Z + */ + +( function( global ) { + +var QUnit = {}; + +var Date = global.Date; +var now = Date.now || function() { + return new Date().getTime(); +}; + +var setTimeout = global.setTimeout; +var clearTimeout = global.clearTimeout; + +// Store a local window from the global to allow direct references. +var window = global.window; + +var defined = { + document: window && window.document !== undefined, + setTimeout: setTimeout !== undefined, + sessionStorage: ( function() { + var x = "qunit-test-string"; + try { + sessionStorage.setItem( x, x ); + sessionStorage.removeItem( x ); + return true; + } catch ( e ) { + return false; + } + }() ) +}; + +var fileName = ( sourceFromStacktrace( 0 ) || "" ).replace( /(:\d+)+\)?/, "" ).replace( /.+\//, "" ); +var globalStartCalled = false; +var runStarted = false; + +var toString = Object.prototype.toString, + hasOwn = Object.prototype.hasOwnProperty; + +// Returns a new Array with the elements that are in a but not in b +function diff( a, b ) { + var i, j, + result = a.slice(); + + for ( i = 0; i < result.length; i++ ) { + for ( j = 0; j < b.length; j++ ) { + if ( result[ i ] === b[ j ] ) { + result.splice( i, 1 ); + i--; + break; + } + } + } + return result; +} + +// From jquery.js +function inArray( elem, array ) { + if ( array.indexOf ) { + return array.indexOf( elem ); + } + + for ( var i = 0, length = array.length; i < length; i++ ) { + if ( array[ i ] === elem ) { + return i; + } + } + + return -1; +} + +/** + * Makes a clone of an object using only Array or Object as base, + * and copies over the own enumerable properties. + * + * @param {Object} obj + * @return {Object} New object with only the own properties (recursively). + */ +function objectValues ( obj ) { + var key, val, + vals = QUnit.is( "array", obj ) ? [] : {}; + for ( key in obj ) { + if ( hasOwn.call( obj, key ) ) { + val = obj[ key ]; + vals[ key ] = val === Object( val ) ? objectValues( val ) : val; + } + } + return vals; +} + +function extend( a, b, undefOnly ) { + for ( var prop in b ) { + if ( hasOwn.call( b, prop ) ) { + + // Avoid "Member not found" error in IE8 caused by messing with window.constructor + // This block runs on every environment, so `global` is being used instead of `window` + // to avoid errors on node. + if ( prop !== "constructor" || a !== global ) { + if ( b[ prop ] === undefined ) { + delete a[ prop ]; + } else if ( !( undefOnly && typeof a[ prop ] !== "undefined" ) ) { + a[ prop ] = b[ prop ]; + } + } + } + } + + return a; +} + +function objectType( obj ) { + if ( typeof obj === "undefined" ) { + return "undefined"; + } + + // Consider: typeof null === object + if ( obj === null ) { + return "null"; + } + + var match = toString.call( obj ).match( /^\[object\s(.*)\]$/ ), + type = match && match[ 1 ]; + + switch ( type ) { + case "Number": + if ( isNaN( obj ) ) { + return "nan"; + } + return "number"; + case "String": + case "Boolean": + case "Array": + case "Set": + case "Map": + case "Date": + case "RegExp": + case "Function": + case "Symbol": + return type.toLowerCase(); + } + if ( typeof obj === "object" ) { + return "object"; + } +} + +// Safe object type checking +function is( type, obj ) { + return QUnit.objectType( obj ) === type; +} + +// Doesn't support IE6 to IE9, it will return undefined on these browsers +// See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack +function extractStacktrace( e, offset ) { + offset = offset === undefined ? 4 : offset; + + var stack, include, i; + + if ( e.stack ) { + stack = e.stack.split( "\n" ); + if ( /^error$/i.test( stack[ 0 ] ) ) { + stack.shift(); + } + if ( fileName ) { + include = []; + for ( i = offset; i < stack.length; i++ ) { + if ( stack[ i ].indexOf( fileName ) !== -1 ) { + break; + } + include.push( stack[ i ] ); + } + if ( include.length ) { + return include.join( "\n" ); + } + } + return stack[ offset ]; + + // Support: Safari <=6 only + } else if ( e.sourceURL ) { + + // Exclude useless self-reference for generated Error objects + if ( /qunit.js$/.test( e.sourceURL ) ) { + return; + } + + // For actual exceptions, this is useful + return e.sourceURL + ":" + e.line; + } +} + +function sourceFromStacktrace( offset ) { + var error = new Error(); + + // Support: Safari <=7 only, IE <=10 - 11 only + // Not all browsers generate the `stack` property for `new Error()`, see also #636 + if ( !error.stack ) { + try { + throw error; + } catch ( err ) { + error = err; + } + } + + return extractStacktrace( error, offset ); +} + +/** + * Config object: Maintain internal state + * Later exposed as QUnit.config + * `config` initialized at top of scope + */ +var config = { + + // The queue of tests to run + queue: [], + + // Block until document ready + blocking: true, + + // By default, run previously failed tests first + // very useful in combination with "Hide passed tests" checked + reorder: true, + + // By default, modify document.title when suite is done + altertitle: true, + + // HTML Reporter: collapse every test except the first failing test + // If false, all failing tests will be expanded + collapse: true, + + // By default, scroll to top of the page when suite is done + scrolltop: true, + + // Depth up-to which object will be dumped + maxDepth: 5, + + // When enabled, all tests must call expect() + requireExpects: false, + + // Placeholder for user-configurable form-exposed URL parameters + urlConfig: [], + + // Set of all modules. + modules: [], + + // Stack of nested modules + moduleStack: [], + + // The first unnamed module + currentModule: { + name: "", + tests: [] + }, + + callbacks: {} +}; + +// Push a loose unnamed module to the modules collection +config.modules.push( config.currentModule ); + +var loggingCallbacks = {}; + +// Register logging callbacks +function registerLoggingCallbacks( obj ) { + var i, l, key, + callbackNames = [ "begin", "done", "log", "testStart", "testDone", + "moduleStart", "moduleDone" ]; + + function registerLoggingCallback( key ) { + var loggingCallback = function( callback ) { + if ( objectType( callback ) !== "function" ) { + throw new Error( + "QUnit logging methods require a callback function as their first parameters." + ); + } + + config.callbacks[ key ].push( callback ); + }; + + // DEPRECATED: This will be removed on QUnit 2.0.0+ + // Stores the registered functions allowing restoring + // at verifyLoggingCallbacks() if modified + loggingCallbacks[ key ] = loggingCallback; + + return loggingCallback; + } + + for ( i = 0, l = callbackNames.length; i < l; i++ ) { + key = callbackNames[ i ]; + + // Initialize key collection of logging callback + if ( objectType( config.callbacks[ key ] ) === "undefined" ) { + config.callbacks[ key ] = []; + } + + obj[ key ] = registerLoggingCallback( key ); + } +} + +function runLoggingCallbacks( key, args ) { + var i, l, callbacks; + + callbacks = config.callbacks[ key ]; + for ( i = 0, l = callbacks.length; i < l; i++ ) { + callbacks[ i ]( args ); + } +} + +// DEPRECATED: This will be removed on 2.0.0+ +// This function verifies if the loggingCallbacks were modified by the user +// If so, it will restore it, assign the given callback and print a console warning +function verifyLoggingCallbacks() { + var loggingCallback, userCallback; + + for ( loggingCallback in loggingCallbacks ) { + if ( QUnit[ loggingCallback ] !== loggingCallbacks[ loggingCallback ] ) { + + userCallback = QUnit[ loggingCallback ]; + + // Restore the callback function + QUnit[ loggingCallback ] = loggingCallbacks[ loggingCallback ]; + + // Assign the deprecated given callback + QUnit[ loggingCallback ]( userCallback ); + + if ( global.console && global.console.warn ) { + global.console.warn( + "QUnit." + loggingCallback + " was replaced with a new value.\n" + + "Please, check out the documentation on how to apply logging callbacks.\n" + + "Reference: https://api.qunitjs.com/category/callbacks/" + ); + } + } + } +} + +( function() { + if ( !defined.document ) { + return; + } + + // `onErrorFnPrev` initialized at top of scope + // Preserve other handlers + var onErrorFnPrev = window.onerror; + + // Cover uncaught exceptions + // Returning true will suppress the default browser handler, + // returning false will let it run. + window.onerror = function( error, filePath, linerNr ) { + var ret = false; + if ( onErrorFnPrev ) { + ret = onErrorFnPrev( error, filePath, linerNr ); + } + + // Treat return value as window.onerror itself does, + // Only do our handling if not suppressed. + if ( ret !== true ) { + if ( QUnit.config.current ) { + if ( QUnit.config.current.ignoreGlobalErrors ) { + return true; + } + QUnit.pushFailure( error, filePath + ":" + linerNr ); + } else { + QUnit.test( "global failure", extend( function() { + QUnit.pushFailure( error, filePath + ":" + linerNr ); + }, { validTest: true } ) ); + } + return false; + } + + return ret; + }; +}() ); + +// Figure out if we're running the tests from a server or not +QUnit.isLocal = !( defined.document && window.location.protocol !== "file:" ); + +// Expose the current QUnit version +QUnit.version = "1.23.1"; + +extend( QUnit, { + + // Call on start of module test to prepend name to all tests + module: function( name, testEnvironment, executeNow ) { + var module, moduleFns; + var currentModule = config.currentModule; + + if ( arguments.length === 2 ) { + if ( objectType( testEnvironment ) === "function" ) { + executeNow = testEnvironment; + testEnvironment = undefined; + } + } + + // DEPRECATED: handles setup/teardown functions, + // beforeEach and afterEach should be used instead + if ( testEnvironment && testEnvironment.setup ) { + testEnvironment.beforeEach = testEnvironment.setup; + delete testEnvironment.setup; + } + if ( testEnvironment && testEnvironment.teardown ) { + testEnvironment.afterEach = testEnvironment.teardown; + delete testEnvironment.teardown; + } + + module = createModule(); + + moduleFns = { + beforeEach: setHook( module, "beforeEach" ), + afterEach: setHook( module, "afterEach" ) + }; + + if ( objectType( executeNow ) === "function" ) { + config.moduleStack.push( module ); + setCurrentModule( module ); + executeNow.call( module.testEnvironment, moduleFns ); + config.moduleStack.pop(); + module = module.parentModule || currentModule; + } + + setCurrentModule( module ); + + function createModule() { + var parentModule = config.moduleStack.length ? + config.moduleStack.slice( -1 )[ 0 ] : null; + var moduleName = parentModule !== null ? + [ parentModule.name, name ].join( " > " ) : name; + var module = { + name: moduleName, + parentModule: parentModule, + tests: [], + moduleId: generateHash( moduleName ) + }; + + var env = {}; + if ( parentModule ) { + extend( env, parentModule.testEnvironment ); + delete env.beforeEach; + delete env.afterEach; + } + extend( env, testEnvironment ); + module.testEnvironment = env; + + config.modules.push( module ); + return module; + } + + function setCurrentModule( module ) { + config.currentModule = module; + } + + }, + + // DEPRECATED: QUnit.asyncTest() will be removed in QUnit 2.0. + asyncTest: asyncTest, + + test: test, + + skip: skip, + + only: only, + + // DEPRECATED: The functionality of QUnit.start() will be altered in QUnit 2.0. + // In QUnit 2.0, invoking it will ONLY affect the `QUnit.config.autostart` blocking behavior. + start: function( count ) { + var globalStartAlreadyCalled = globalStartCalled; + + if ( !config.current ) { + globalStartCalled = true; + + if ( runStarted ) { + throw new Error( "Called start() outside of a test context while already started" ); + } else if ( globalStartAlreadyCalled || count > 1 ) { + throw new Error( "Called start() outside of a test context too many times" ); + } else if ( config.autostart ) { + throw new Error( "Called start() outside of a test context when " + + "QUnit.config.autostart was true" ); + } else if ( !config.pageLoaded ) { + + // The page isn't completely loaded yet, so bail out and let `QUnit.load` handle it + config.autostart = true; + return; + } + } else { + + // If a test is running, adjust its semaphore + config.current.semaphore -= count || 1; + + // If semaphore is non-numeric, throw error + if ( isNaN( config.current.semaphore ) ) { + config.current.semaphore = 0; + + QUnit.pushFailure( + "Called start() with a non-numeric decrement.", + sourceFromStacktrace( 2 ) + ); + return; + } + + // Don't start until equal number of stop-calls + if ( config.current.semaphore > 0 ) { + return; + } + + // Throw an Error if start is called more often than stop + if ( config.current.semaphore < 0 ) { + config.current.semaphore = 0; + + QUnit.pushFailure( + "Called start() while already started (test's semaphore was 0 already)", + sourceFromStacktrace( 2 ) + ); + return; + } + } + + resumeProcessing(); + }, + + // DEPRECATED: QUnit.stop() will be removed in QUnit 2.0. + stop: function( count ) { + + // If there isn't a test running, don't allow QUnit.stop() to be called + if ( !config.current ) { + throw new Error( "Called stop() outside of a test context" ); + } + + // If a test is running, adjust its semaphore + config.current.semaphore += count || 1; + + pauseProcessing(); + }, + + config: config, + + is: is, + + objectType: objectType, + + extend: extend, + + load: function() { + config.pageLoaded = true; + + // Initialize the configuration options + extend( config, { + stats: { all: 0, bad: 0 }, + moduleStats: { all: 0, bad: 0 }, + started: 0, + updateRate: 1000, + autostart: true, + filter: "" + }, true ); + + config.blocking = false; + + if ( config.autostart ) { + resumeProcessing(); + } + }, + + stack: function( offset ) { + offset = ( offset || 0 ) + 2; + return sourceFromStacktrace( offset ); + } +} ); + +registerLoggingCallbacks( QUnit ); + +function begin() { + var i, l, + modulesLog = []; + + // If the test run hasn't officially begun yet + if ( !config.started ) { + + // Record the time of the test run's beginning + config.started = now(); + + verifyLoggingCallbacks(); + + // Delete the loose unnamed module if unused. + if ( config.modules[ 0 ].name === "" && config.modules[ 0 ].tests.length === 0 ) { + config.modules.shift(); + } + + // Avoid unnecessary information by not logging modules' test environments + for ( i = 0, l = config.modules.length; i < l; i++ ) { + modulesLog.push( { + name: config.modules[ i ].name, + tests: config.modules[ i ].tests + } ); + } + + // The test run is officially beginning now + runLoggingCallbacks( "begin", { + totalTests: Test.count, + modules: modulesLog + } ); + } + + config.blocking = false; + process( true ); +} + +function process( last ) { + function next() { + process( last ); + } + var start = now(); + config.depth = ( config.depth || 0 ) + 1; + + while ( config.queue.length && !config.blocking ) { + if ( !defined.setTimeout || config.updateRate <= 0 || + ( ( now() - start ) < config.updateRate ) ) { + if ( config.current ) { + + // Reset async tracking for each phase of the Test lifecycle + config.current.usedAsync = false; + } + config.queue.shift()(); + } else { + setTimeout( next, 13 ); + break; + } + } + config.depth--; + if ( last && !config.blocking && !config.queue.length && config.depth === 0 ) { + done(); + } +} + +function pauseProcessing() { + config.blocking = true; + + if ( config.testTimeout && defined.setTimeout ) { + clearTimeout( config.timeout ); + config.timeout = setTimeout( function() { + if ( config.current ) { + config.current.semaphore = 0; + QUnit.pushFailure( "Test timed out", sourceFromStacktrace( 2 ) ); + } else { + throw new Error( "Test timed out" ); + } + resumeProcessing(); + }, config.testTimeout ); + } +} + +function resumeProcessing() { + runStarted = true; + + // A slight delay to allow this iteration of the event loop to finish (more assertions, etc.) + if ( defined.setTimeout ) { + setTimeout( function() { + if ( config.current && config.current.semaphore > 0 ) { + return; + } + if ( config.timeout ) { + clearTimeout( config.timeout ); + } + + begin(); + }, 13 ); + } else { + begin(); + } +} + +function done() { + var runtime, passed; + + config.autorun = true; + + // Log the last module results + if ( config.previousModule ) { + runLoggingCallbacks( "moduleDone", { + name: config.previousModule.name, + tests: config.previousModule.tests, + failed: config.moduleStats.bad, + passed: config.moduleStats.all - config.moduleStats.bad, + total: config.moduleStats.all, + runtime: now() - config.moduleStats.started + } ); + } + delete config.previousModule; + + runtime = now() - config.started; + passed = config.stats.all - config.stats.bad; + + runLoggingCallbacks( "done", { + failed: config.stats.bad, + passed: passed, + total: config.stats.all, + runtime: runtime + } ); +} + +function setHook( module, hookName ) { + if ( module.testEnvironment === undefined ) { + module.testEnvironment = {}; + } + + return function( callback ) { + module.testEnvironment[ hookName ] = callback; + }; +} + +var focused = false; +var priorityCount = 0; +var unitSampler; + +function Test( settings ) { + var i, l; + + ++Test.count; + + extend( this, settings ); + this.assertions = []; + this.semaphore = 0; + this.usedAsync = false; + this.module = config.currentModule; + this.stack = sourceFromStacktrace( 3 ); + + // Register unique strings + for ( i = 0, l = this.module.tests; i < l.length; i++ ) { + if ( this.module.tests[ i ].name === this.testName ) { + this.testName += " "; + } + } + + this.testId = generateHash( this.module.name, this.testName ); + + this.module.tests.push( { + name: this.testName, + testId: this.testId + } ); + + if ( settings.skip ) { + + // Skipped tests will fully ignore any sent callback + this.callback = function() {}; + this.async = false; + this.expected = 0; + } else { + this.assert = new Assert( this ); + } +} + +Test.count = 0; + +Test.prototype = { + before: function() { + if ( + + // Emit moduleStart when we're switching from one module to another + this.module !== config.previousModule || + + // They could be equal (both undefined) but if the previousModule property doesn't + // yet exist it means this is the first test in a suite that isn't wrapped in a + // module, in which case we'll just emit a moduleStart event for 'undefined'. + // Without this, reporters can get testStart before moduleStart which is a problem. + !hasOwn.call( config, "previousModule" ) + ) { + if ( hasOwn.call( config, "previousModule" ) ) { + runLoggingCallbacks( "moduleDone", { + name: config.previousModule.name, + tests: config.previousModule.tests, + failed: config.moduleStats.bad, + passed: config.moduleStats.all - config.moduleStats.bad, + total: config.moduleStats.all, + runtime: now() - config.moduleStats.started + } ); + } + config.previousModule = this.module; + config.moduleStats = { all: 0, bad: 0, started: now() }; + runLoggingCallbacks( "moduleStart", { + name: this.module.name, + tests: this.module.tests + } ); + } + + config.current = this; + + if ( this.module.testEnvironment ) { + delete this.module.testEnvironment.beforeEach; + delete this.module.testEnvironment.afterEach; + } + this.testEnvironment = extend( {}, this.module.testEnvironment ); + + this.started = now(); + runLoggingCallbacks( "testStart", { + name: this.testName, + module: this.module.name, + testId: this.testId + } ); + + if ( !config.pollution ) { + saveGlobal(); + } + }, + + run: function() { + var promise; + + config.current = this; + + if ( this.async ) { + QUnit.stop(); + } + + this.callbackStarted = now(); + + if ( config.notrycatch ) { + runTest( this ); + return; + } + + try { + runTest( this ); + } catch ( e ) { + this.pushFailure( "Died on test #" + ( this.assertions.length + 1 ) + " " + + this.stack + ": " + ( e.message || e ), extractStacktrace( e, 0 ) ); + + // Else next test will carry the responsibility + saveGlobal(); + + // Restart the tests if they're blocking + if ( config.blocking ) { + QUnit.start(); + } + } + + function runTest( test ) { + promise = test.callback.call( test.testEnvironment, test.assert ); + test.resolvePromise( promise ); + } + }, + + after: function() { + checkPollution(); + }, + + queueHook: function( hook, hookName ) { + var promise, + test = this; + return function runHook() { + config.current = test; + if ( config.notrycatch ) { + callHook(); + return; + } + try { + callHook(); + } catch ( error ) { + test.pushFailure( hookName + " failed on " + test.testName + ": " + + ( error.message || error ), extractStacktrace( error, 0 ) ); + } + + function callHook() { + promise = hook.call( test.testEnvironment, test.assert ); + test.resolvePromise( promise, hookName ); + } + }; + }, + + // Currently only used for module level hooks, can be used to add global level ones + hooks: function( handler ) { + var hooks = []; + + function processHooks( test, module ) { + if ( module.parentModule ) { + processHooks( test, module.parentModule ); + } + if ( module.testEnvironment && + QUnit.objectType( module.testEnvironment[ handler ] ) === "function" ) { + hooks.push( test.queueHook( module.testEnvironment[ handler ], handler ) ); + } + } + + // Hooks are ignored on skipped tests + if ( !this.skip ) { + processHooks( this, this.module ); + } + return hooks; + }, + + finish: function() { + config.current = this; + if ( config.requireExpects && this.expected === null ) { + this.pushFailure( "Expected number of assertions to be defined, but expect() was " + + "not called.", this.stack ); + } else if ( this.expected !== null && this.expected !== this.assertions.length ) { + this.pushFailure( "Expected " + this.expected + " assertions, but " + + this.assertions.length + " were run", this.stack ); + } else if ( this.expected === null && !this.assertions.length ) { + this.pushFailure( "Expected at least one assertion, but none were run - call " + + "expect(0) to accept zero assertions.", this.stack ); + } + + var i, + bad = 0; + + this.runtime = now() - this.started; + config.stats.all += this.assertions.length; + config.moduleStats.all += this.assertions.length; + + for ( i = 0; i < this.assertions.length; i++ ) { + if ( !this.assertions[ i ].result ) { + bad++; + config.stats.bad++; + config.moduleStats.bad++; + } + } + + runLoggingCallbacks( "testDone", { + name: this.testName, + module: this.module.name, + skipped: !!this.skip, + failed: bad, + passed: this.assertions.length - bad, + total: this.assertions.length, + runtime: this.runtime, + + // HTML Reporter use + assertions: this.assertions, + testId: this.testId, + + // Source of Test + source: this.stack, + + // DEPRECATED: this property will be removed in 2.0.0, use runtime instead + duration: this.runtime + } ); + + // QUnit.reset() is deprecated and will be replaced for a new + // fixture reset function on QUnit 2.0/2.1. + // It's still called here for backwards compatibility handling + QUnit.reset(); + + config.current = undefined; + }, + + queue: function() { + var priority, + test = this; + + if ( !this.valid() ) { + return; + } + + function run() { + + // Each of these can by async + synchronize( [ + function() { + test.before(); + }, + + test.hooks( "beforeEach" ), + function() { + test.run(); + }, + + test.hooks( "afterEach" ).reverse(), + + function() { + test.after(); + }, + function() { + test.finish(); + } + ] ); + } + + // Prioritize previously failed tests, detected from sessionStorage + priority = QUnit.config.reorder && defined.sessionStorage && + +sessionStorage.getItem( "qunit-test-" + this.module.name + "-" + this.testName ); + + return synchronize( run, priority, config.seed ); + }, + + pushResult: function( resultInfo ) { + + // Destructure of resultInfo = { result, actual, expected, message, negative } + var source, + details = { + module: this.module.name, + name: this.testName, + result: resultInfo.result, + message: resultInfo.message, + actual: resultInfo.actual, + expected: resultInfo.expected, + testId: this.testId, + negative: resultInfo.negative || false, + runtime: now() - this.started + }; + + if ( !resultInfo.result ) { + source = sourceFromStacktrace(); + + if ( source ) { + details.source = source; + } + } + + runLoggingCallbacks( "log", details ); + + this.assertions.push( { + result: !!resultInfo.result, + message: resultInfo.message + } ); + }, + + pushFailure: function( message, source, actual ) { + if ( !( this instanceof Test ) ) { + throw new Error( "pushFailure() assertion outside test context, was " + + sourceFromStacktrace( 2 ) ); + } + + var details = { + module: this.module.name, + name: this.testName, + result: false, + message: message || "error", + actual: actual || null, + testId: this.testId, + runtime: now() - this.started + }; + + if ( source ) { + details.source = source; + } + + runLoggingCallbacks( "log", details ); + + this.assertions.push( { + result: false, + message: message + } ); + }, + + resolvePromise: function( promise, phase ) { + var then, message, + test = this; + if ( promise != null ) { + then = promise.then; + if ( QUnit.objectType( then ) === "function" ) { + QUnit.stop(); + then.call( + promise, + function() { QUnit.start(); }, + function( error ) { + message = "Promise rejected " + + ( !phase ? "during" : phase.replace( /Each$/, "" ) ) + + " " + test.testName + ": " + ( error.message || error ); + test.pushFailure( message, extractStacktrace( error, 0 ) ); + + // Else next test will carry the responsibility + saveGlobal(); + + // Unblock + QUnit.start(); + } + ); + } + } + }, + + valid: function() { + var filter = config.filter, + regexFilter = /^(!?)\/([\w\W]*)\/(i?$)/.exec( filter ), + module = config.module && config.module.toLowerCase(), + fullName = ( this.module.name + ": " + this.testName ); + + function moduleChainNameMatch( testModule ) { + var testModuleName = testModule.name ? testModule.name.toLowerCase() : null; + if ( testModuleName === module ) { + return true; + } else if ( testModule.parentModule ) { + return moduleChainNameMatch( testModule.parentModule ); + } else { + return false; + } + } + + function moduleChainIdMatch( testModule ) { + return inArray( testModule.moduleId, config.moduleId ) > -1 || + testModule.parentModule && moduleChainIdMatch( testModule.parentModule ); + } + + // Internally-generated tests are always valid + if ( this.callback && this.callback.validTest ) { + return true; + } + + if ( config.moduleId && config.moduleId.length > 0 && + !moduleChainIdMatch( this.module ) ) { + + return false; + } + + if ( config.testId && config.testId.length > 0 && + inArray( this.testId, config.testId ) < 0 ) { + + return false; + } + + if ( module && !moduleChainNameMatch( this.module ) ) { + return false; + } + + if ( !filter ) { + return true; + } + + return regexFilter ? + this.regexFilter( !!regexFilter[ 1 ], regexFilter[ 2 ], regexFilter[ 3 ], fullName ) : + this.stringFilter( filter, fullName ); + }, + + regexFilter: function( exclude, pattern, flags, fullName ) { + var regex = new RegExp( pattern, flags ); + var match = regex.test( fullName ); + + return match !== exclude; + }, + + stringFilter: function( filter, fullName ) { + filter = filter.toLowerCase(); + fullName = fullName.toLowerCase(); + + var include = filter.charAt( 0 ) !== "!"; + if ( !include ) { + filter = filter.slice( 1 ); + } + + // If the filter matches, we need to honour include + if ( fullName.indexOf( filter ) !== -1 ) { + return include; + } + + // Otherwise, do the opposite + return !include; + } +}; + +// Resets the test setup. Useful for tests that modify the DOM. +/* +DEPRECATED: Use multiple tests instead of resetting inside a test. +Use testStart or testDone for custom cleanup. +This method will throw an error in 2.0, and will be removed in 2.1 +*/ +QUnit.reset = function() { + + // Return on non-browser environments + // This is necessary to not break on node tests + if ( !defined.document ) { + return; + } + + var fixture = defined.document && document.getElementById && + document.getElementById( "qunit-fixture" ); + + if ( fixture ) { + fixture.innerHTML = config.fixture; + } +}; + +QUnit.pushFailure = function() { + if ( !QUnit.config.current ) { + throw new Error( "pushFailure() assertion outside test context, in " + + sourceFromStacktrace( 2 ) ); + } + + // Gets current test obj + var currentTest = QUnit.config.current; + + return currentTest.pushFailure.apply( currentTest, arguments ); +}; + +// Based on Java's String.hashCode, a simple but not +// rigorously collision resistant hashing function +function generateHash( module, testName ) { + var hex, + i = 0, + hash = 0, + str = module + "\x1C" + testName, + len = str.length; + + for ( ; i < len; i++ ) { + hash = ( ( hash << 5 ) - hash ) + str.charCodeAt( i ); + hash |= 0; + } + + // Convert the possibly negative integer hash code into an 8 character hex string, which isn't + // strictly necessary but increases user understanding that the id is a SHA-like hash + hex = ( 0x100000000 + hash ).toString( 16 ); + if ( hex.length < 8 ) { + hex = "0000000" + hex; + } + + return hex.slice( -8 ); +} + +function synchronize( callback, priority, seed ) { + var last = !priority, + index; + + if ( QUnit.objectType( callback ) === "array" ) { + while ( callback.length ) { + synchronize( callback.shift() ); + } + return; + } + + if ( priority ) { + config.queue.splice( priorityCount++, 0, callback ); + } else if ( seed ) { + if ( !unitSampler ) { + unitSampler = unitSamplerGenerator( seed ); + } + + // Insert into a random position after all priority items + index = Math.floor( unitSampler() * ( config.queue.length - priorityCount + 1 ) ); + config.queue.splice( priorityCount + index, 0, callback ); + } else { + config.queue.push( callback ); + } + + if ( config.autorun && !config.blocking ) { + process( last ); + } +} + +function unitSamplerGenerator( seed ) { + + // 32-bit xorshift, requires only a nonzero seed + // http://excamera.com/sphinx/article-xorshift.html + var sample = parseInt( generateHash( seed ), 16 ) || -1; + return function() { + sample ^= sample << 13; + sample ^= sample >>> 17; + sample ^= sample << 5; + + // ECMAScript has no unsigned number type + if ( sample < 0 ) { + sample += 0x100000000; + } + + return sample / 0x100000000; + }; +} + +function saveGlobal() { + config.pollution = []; + + if ( config.noglobals ) { + for ( var key in global ) { + if ( hasOwn.call( global, key ) ) { + + // In Opera sometimes DOM element ids show up here, ignore them + if ( /^qunit-test-output/.test( key ) ) { + continue; + } + config.pollution.push( key ); + } + } + } +} + +function checkPollution() { + var newGlobals, + deletedGlobals, + old = config.pollution; + + saveGlobal(); + + newGlobals = diff( config.pollution, old ); + if ( newGlobals.length > 0 ) { + QUnit.pushFailure( "Introduced global variable(s): " + newGlobals.join( ", " ) ); + } + + deletedGlobals = diff( old, config.pollution ); + if ( deletedGlobals.length > 0 ) { + QUnit.pushFailure( "Deleted global variable(s): " + deletedGlobals.join( ", " ) ); + } +} + +// Will be exposed as QUnit.asyncTest +function asyncTest( testName, expected, callback ) { + if ( arguments.length === 2 ) { + callback = expected; + expected = null; + } + + QUnit.test( testName, expected, callback, true ); +} + +// Will be exposed as QUnit.test +function test( testName, expected, callback, async ) { + if ( focused ) { return; } + + var newTest; + + if ( arguments.length === 2 ) { + callback = expected; + expected = null; + } + + newTest = new Test( { + testName: testName, + expected: expected, + async: async, + callback: callback + } ); + + newTest.queue(); +} + +// Will be exposed as QUnit.skip +function skip( testName ) { + if ( focused ) { return; } + + var test = new Test( { + testName: testName, + skip: true + } ); + + test.queue(); +} + +// Will be exposed as QUnit.only +function only( testName, expected, callback, async ) { + var newTest; + + if ( focused ) { return; } + + QUnit.config.queue.length = 0; + focused = true; + + if ( arguments.length === 2 ) { + callback = expected; + expected = null; + } + + newTest = new Test( { + testName: testName, + expected: expected, + async: async, + callback: callback + } ); + + newTest.queue(); +} + +function Assert( testContext ) { + this.test = testContext; +} + +// Assert helpers +QUnit.assert = Assert.prototype = { + + // Specify the number of expected assertions to guarantee that failed test + // (no assertions are run at all) don't slip through. + expect: function( asserts ) { + if ( arguments.length === 1 ) { + this.test.expected = asserts; + } else { + return this.test.expected; + } + }, + + // Increment this Test's semaphore counter, then return a function that + // decrements that counter a maximum of once. + async: function( count ) { + var test = this.test, + popped = false, + acceptCallCount = count; + + if ( typeof acceptCallCount === "undefined" ) { + acceptCallCount = 1; + } + + test.semaphore += 1; + test.usedAsync = true; + pauseProcessing(); + + return function done() { + + if ( popped ) { + test.pushFailure( "Too many calls to the `assert.async` callback", + sourceFromStacktrace( 2 ) ); + return; + } + acceptCallCount -= 1; + if ( acceptCallCount > 0 ) { + return; + } + + test.semaphore -= 1; + popped = true; + resumeProcessing(); + }; + }, + + // Exports test.push() to the user API + // Alias of pushResult. + push: function( result, actual, expected, message, negative ) { + var currentAssert = this instanceof Assert ? this : QUnit.config.current.assert; + return currentAssert.pushResult( { + result: result, + actual: actual, + expected: expected, + message: message, + negative: negative + } ); + }, + + pushResult: function( resultInfo ) { + + // Destructure of resultInfo = { result, actual, expected, message, negative } + var assert = this, + currentTest = ( assert instanceof Assert && assert.test ) || QUnit.config.current; + + // Backwards compatibility fix. + // Allows the direct use of global exported assertions and QUnit.assert.* + // Although, it's use is not recommended as it can leak assertions + // to other tests from async tests, because we only get a reference to the current test, + // not exactly the test where assertion were intended to be called. + if ( !currentTest ) { + throw new Error( "assertion outside test context, in " + sourceFromStacktrace( 2 ) ); + } + + if ( currentTest.usedAsync === true && currentTest.semaphore === 0 ) { + currentTest.pushFailure( "Assertion after the final `assert.async` was resolved", + sourceFromStacktrace( 2 ) ); + + // Allow this assertion to continue running anyway... + } + + if ( !( assert instanceof Assert ) ) { + assert = currentTest.assert; + } + + return assert.test.pushResult( resultInfo ); + }, + + ok: function( result, message ) { + message = message || ( result ? "okay" : "failed, expected argument to be truthy, was: " + + QUnit.dump.parse( result ) ); + this.pushResult( { + result: !!result, + actual: result, + expected: true, + message: message + } ); + }, + + notOk: function( result, message ) { + message = message || ( !result ? "okay" : "failed, expected argument to be falsy, was: " + + QUnit.dump.parse( result ) ); + this.pushResult( { + result: !result, + actual: result, + expected: false, + message: message + } ); + }, + + equal: function( actual, expected, message ) { + /*jshint eqeqeq:false */ + this.pushResult( { + result: expected == actual, + actual: actual, + expected: expected, + message: message + } ); + }, + + notEqual: function( actual, expected, message ) { + /*jshint eqeqeq:false */ + this.pushResult( { + result: expected != actual, + actual: actual, + expected: expected, + message: message, + negative: true + } ); + }, + + propEqual: function( actual, expected, message ) { + actual = objectValues( actual ); + expected = objectValues( expected ); + this.pushResult( { + result: QUnit.equiv( actual, expected ), + actual: actual, + expected: expected, + message: message + } ); + }, + + notPropEqual: function( actual, expected, message ) { + actual = objectValues( actual ); + expected = objectValues( expected ); + this.pushResult( { + result: !QUnit.equiv( actual, expected ), + actual: actual, + expected: expected, + message: message, + negative: true + } ); + }, + + deepEqual: function( actual, expected, message ) { + this.pushResult( { + result: QUnit.equiv( actual, expected ), + actual: actual, + expected: expected, + message: message + } ); + }, + + notDeepEqual: function( actual, expected, message ) { + this.pushResult( { + result: !QUnit.equiv( actual, expected ), + actual: actual, + expected: expected, + message: message, + negative: true + } ); + }, + + strictEqual: function( actual, expected, message ) { + this.pushResult( { + result: expected === actual, + actual: actual, + expected: expected, + message: message + } ); + }, + + notStrictEqual: function( actual, expected, message ) { + this.pushResult( { + result: expected !== actual, + actual: actual, + expected: expected, + message: message, + negative: true + } ); + }, + + "throws": function( block, expected, message ) { + var actual, expectedType, + expectedOutput = expected, + ok = false, + currentTest = ( this instanceof Assert && this.test ) || QUnit.config.current; + + // 'expected' is optional unless doing string comparison + if ( message == null && typeof expected === "string" ) { + message = expected; + expected = null; + } + + currentTest.ignoreGlobalErrors = true; + try { + block.call( currentTest.testEnvironment ); + } catch ( e ) { + actual = e; + } + currentTest.ignoreGlobalErrors = false; + + if ( actual ) { + expectedType = QUnit.objectType( expected ); + + // We don't want to validate thrown error + if ( !expected ) { + ok = true; + expectedOutput = null; + + // Expected is a regexp + } else if ( expectedType === "regexp" ) { + ok = expected.test( errorString( actual ) ); + + // Expected is a string + } else if ( expectedType === "string" ) { + ok = expected === errorString( actual ); + + // Expected is a constructor, maybe an Error constructor + } else if ( expectedType === "function" && actual instanceof expected ) { + ok = true; + + // Expected is an Error object + } else if ( expectedType === "object" ) { + ok = actual instanceof expected.constructor && + actual.name === expected.name && + actual.message === expected.message; + + // Expected is a validation function which returns true if validation passed + } else if ( expectedType === "function" && expected.call( {}, actual ) === true ) { + expectedOutput = null; + ok = true; + } + } + + currentTest.assert.pushResult( { + result: ok, + actual: actual, + expected: expectedOutput, + message: message + } ); + } +}; + +// Provide an alternative to assert.throws(), for environments that consider throws a reserved word +// Known to us are: Closure Compiler, Narwhal +( function() { + /*jshint sub:true */ + Assert.prototype.raises = Assert.prototype [ "throws" ]; //jscs:ignore requireDotNotation +}() ); + +function errorString( error ) { + var name, message, + resultErrorString = error.toString(); + if ( resultErrorString.substring( 0, 7 ) === "[object" ) { + name = error.name ? error.name.toString() : "Error"; + message = error.message ? error.message.toString() : ""; + if ( name && message ) { + return name + ": " + message; + } else if ( name ) { + return name; + } else if ( message ) { + return message; + } else { + return "Error"; + } + } else { + return resultErrorString; + } +} + +// Test for equality any JavaScript type. +// Author: Philippe Rathé +QUnit.equiv = ( function() { + + // Stack to decide between skip/abort functions + var callers = []; + + // Stack to avoiding loops from circular referencing + var parents = []; + var parentsB = []; + + var getProto = Object.getPrototypeOf || function( obj ) { + + /*jshint proto: true */ + return obj.__proto__; + }; + + function useStrictEquality( b, a ) { + + // To catch short annotation VS 'new' annotation of a declaration. e.g.: + // `var i = 1;` + // `var j = new Number(1);` + if ( typeof a === "object" ) { + a = a.valueOf(); + } + if ( typeof b === "object" ) { + b = b.valueOf(); + } + + return a === b; + } + + function compareConstructors( a, b ) { + var protoA = getProto( a ); + var protoB = getProto( b ); + + // Comparing constructors is more strict than using `instanceof` + if ( a.constructor === b.constructor ) { + return true; + } + + // Ref #851 + // If the obj prototype descends from a null constructor, treat it + // as a null prototype. + if ( protoA && protoA.constructor === null ) { + protoA = null; + } + if ( protoB && protoB.constructor === null ) { + protoB = null; + } + + // Allow objects with no prototype to be equivalent to + // objects with Object as their constructor. + if ( ( protoA === null && protoB === Object.prototype ) || + ( protoB === null && protoA === Object.prototype ) ) { + return true; + } + + return false; + } + + function getRegExpFlags( regexp ) { + return "flags" in regexp ? regexp.flags : regexp.toString().match( /[gimuy]*$/ )[ 0 ]; + } + + var callbacks = { + "string": useStrictEquality, + "boolean": useStrictEquality, + "number": useStrictEquality, + "null": useStrictEquality, + "undefined": useStrictEquality, + "symbol": useStrictEquality, + "date": useStrictEquality, + + "nan": function() { + return true; + }, + + "regexp": function( b, a ) { + return a.source === b.source && + + // Include flags in the comparison + getRegExpFlags( a ) === getRegExpFlags( b ); + }, + + // - skip when the property is a method of an instance (OOP) + // - abort otherwise, + // initial === would have catch identical references anyway + "function": function() { + var caller = callers[ callers.length - 1 ]; + return caller !== Object && typeof caller !== "undefined"; + }, + + "array": function( b, a ) { + var i, j, len, loop, aCircular, bCircular; + + len = a.length; + if ( len !== b.length ) { + + // Safe and faster + return false; + } + + // Track reference to avoid circular references + parents.push( a ); + parentsB.push( b ); + for ( i = 0; i < len; i++ ) { + loop = false; + for ( j = 0; j < parents.length; j++ ) { + aCircular = parents[ j ] === a[ i ]; + bCircular = parentsB[ j ] === b[ i ]; + if ( aCircular || bCircular ) { + if ( a[ i ] === b[ i ] || aCircular && bCircular ) { + loop = true; + } else { + parents.pop(); + parentsB.pop(); + return false; + } + } + } + if ( !loop && !innerEquiv( a[ i ], b[ i ] ) ) { + parents.pop(); + parentsB.pop(); + return false; + } + } + parents.pop(); + parentsB.pop(); + return true; + }, + + "set": function( b, a ) { + var innerEq, + outerEq = true; + + if ( a.size !== b.size ) { + return false; + } + + a.forEach( function( aVal ) { + innerEq = false; + + b.forEach( function( bVal ) { + if ( innerEquiv( bVal, aVal ) ) { + innerEq = true; + } + } ); + + if ( !innerEq ) { + outerEq = false; + } + } ); + + return outerEq; + }, + + "map": function( b, a ) { + var innerEq, + outerEq = true; + + if ( a.size !== b.size ) { + return false; + } + + a.forEach( function( aVal, aKey ) { + innerEq = false; + + b.forEach( function( bVal, bKey ) { + if ( innerEquiv( [ bVal, bKey ], [ aVal, aKey ] ) ) { + innerEq = true; + } + } ); + + if ( !innerEq ) { + outerEq = false; + } + } ); + + return outerEq; + }, + + "object": function( b, a ) { + var i, j, loop, aCircular, bCircular; + + // Default to true + var eq = true; + var aProperties = []; + var bProperties = []; + + if ( compareConstructors( a, b ) === false ) { + return false; + } + + // Stack constructor before traversing properties + callers.push( a.constructor ); + + // Track reference to avoid circular references + parents.push( a ); + parentsB.push( b ); + + // Be strict: don't ensure hasOwnProperty and go deep + for ( i in a ) { + loop = false; + for ( j = 0; j < parents.length; j++ ) { + aCircular = parents[ j ] === a[ i ]; + bCircular = parentsB[ j ] === b[ i ]; + if ( aCircular || bCircular ) { + if ( a[ i ] === b[ i ] || aCircular && bCircular ) { + loop = true; + } else { + eq = false; + break; + } + } + } + aProperties.push( i ); + if ( !loop && !innerEquiv( a[ i ], b[ i ] ) ) { + eq = false; + break; + } + } + + parents.pop(); + parentsB.pop(); + + // Unstack, we are done + callers.pop(); + + for ( i in b ) { + + // Collect b's properties + bProperties.push( i ); + } + + // Ensures identical properties name + return eq && innerEquiv( aProperties.sort(), bProperties.sort() ); + } + }; + + function typeEquiv( a, b ) { + var type = QUnit.objectType( a ); + return QUnit.objectType( b ) === type && callbacks[ type ]( b, a ); + } + + // The real equiv function + function innerEquiv( a, b ) { + + // We're done when there's nothing more to compare + if ( arguments.length < 2 ) { + return true; + } + + // Require type-specific equality + return ( a === b || typeEquiv( a, b ) ) && + + // ...across all consecutive argument pairs + ( arguments.length === 2 || innerEquiv.apply( this, [].slice.call( arguments, 1 ) ) ); + } + + return innerEquiv; +}() ); + +// Based on jsDump by Ariel Flesler +// http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html +QUnit.dump = ( function() { + function quote( str ) { + return "\"" + str.toString().replace( /\\/g, "\\\\" ).replace( /"/g, "\\\"" ) + "\""; + } + function literal( o ) { + return o + ""; + } + function join( pre, arr, post ) { + var s = dump.separator(), + base = dump.indent(), + inner = dump.indent( 1 ); + if ( arr.join ) { + arr = arr.join( "," + s + inner ); + } + if ( !arr ) { + return pre + post; + } + return [ pre, inner + arr, base + post ].join( s ); + } + function array( arr, stack ) { + var i = arr.length, + ret = new Array( i ); + + if ( dump.maxDepth && dump.depth > dump.maxDepth ) { + return "[object Array]"; + } + + this.up(); + while ( i-- ) { + ret[ i ] = this.parse( arr[ i ], undefined, stack ); + } + this.down(); + return join( "[", ret, "]" ); + } + + var reName = /^function (\w+)/, + dump = { + + // The objType is used mostly internally, you can fix a (custom) type in advance + parse: function( obj, objType, stack ) { + stack = stack || []; + var res, parser, parserType, + inStack = inArray( obj, stack ); + + if ( inStack !== -1 ) { + return "recursion(" + ( inStack - stack.length ) + ")"; + } + + objType = objType || this.typeOf( obj ); + parser = this.parsers[ objType ]; + parserType = typeof parser; + + if ( parserType === "function" ) { + stack.push( obj ); + res = parser.call( this, obj, stack ); + stack.pop(); + return res; + } + return ( parserType === "string" ) ? parser : this.parsers.error; + }, + typeOf: function( obj ) { + var type; + if ( obj === null ) { + type = "null"; + } else if ( typeof obj === "undefined" ) { + type = "undefined"; + } else if ( QUnit.is( "regexp", obj ) ) { + type = "regexp"; + } else if ( QUnit.is( "date", obj ) ) { + type = "date"; + } else if ( QUnit.is( "function", obj ) ) { + type = "function"; + } else if ( obj.setInterval !== undefined && + obj.document !== undefined && + obj.nodeType === undefined ) { + type = "window"; + } else if ( obj.nodeType === 9 ) { + type = "document"; + } else if ( obj.nodeType ) { + type = "node"; + } else if ( + + // Native arrays + toString.call( obj ) === "[object Array]" || + + // NodeList objects + ( typeof obj.length === "number" && obj.item !== undefined && + ( obj.length ? obj.item( 0 ) === obj[ 0 ] : ( obj.item( 0 ) === null && + obj[ 0 ] === undefined ) ) ) + ) { + type = "array"; + } else if ( obj.constructor === Error.prototype.constructor ) { + type = "error"; + } else { + type = typeof obj; + } + return type; + }, + + separator: function() { + return this.multiline ? this.HTML ? "
" : "\n" : this.HTML ? " " : " "; + }, + + // Extra can be a number, shortcut for increasing-calling-decreasing + indent: function( extra ) { + if ( !this.multiline ) { + return ""; + } + var chr = this.indentChar; + if ( this.HTML ) { + chr = chr.replace( /\t/g, " " ).replace( / /g, " " ); + } + return new Array( this.depth + ( extra || 0 ) ).join( chr ); + }, + up: function( a ) { + this.depth += a || 1; + }, + down: function( a ) { + this.depth -= a || 1; + }, + setParser: function( name, parser ) { + this.parsers[ name ] = parser; + }, + + // The next 3 are exposed so you can use them + quote: quote, + literal: literal, + join: join, + depth: 1, + maxDepth: QUnit.config.maxDepth, + + // This is the list of parsers, to modify them, use dump.setParser + parsers: { + window: "[Window]", + document: "[Document]", + error: function( error ) { + return "Error(\"" + error.message + "\")"; + }, + unknown: "[Unknown]", + "null": "null", + "undefined": "undefined", + "function": function( fn ) { + var ret = "function", + + // Functions never have name in IE + name = "name" in fn ? fn.name : ( reName.exec( fn ) || [] )[ 1 ]; + + if ( name ) { + ret += " " + name; + } + ret += "("; + + ret = [ ret, dump.parse( fn, "functionArgs" ), "){" ].join( "" ); + return join( ret, dump.parse( fn, "functionCode" ), "}" ); + }, + array: array, + nodelist: array, + "arguments": array, + object: function( map, stack ) { + var keys, key, val, i, nonEnumerableProperties, + ret = []; + + if ( dump.maxDepth && dump.depth > dump.maxDepth ) { + return "[object Object]"; + } + + dump.up(); + keys = []; + for ( key in map ) { + keys.push( key ); + } + + // Some properties are not always enumerable on Error objects. + nonEnumerableProperties = [ "message", "name" ]; + for ( i in nonEnumerableProperties ) { + key = nonEnumerableProperties[ i ]; + if ( key in map && inArray( key, keys ) < 0 ) { + keys.push( key ); + } + } + keys.sort(); + for ( i = 0; i < keys.length; i++ ) { + key = keys[ i ]; + val = map[ key ]; + ret.push( dump.parse( key, "key" ) + ": " + + dump.parse( val, undefined, stack ) ); + } + dump.down(); + return join( "{", ret, "}" ); + }, + node: function( node ) { + var len, i, val, + open = dump.HTML ? "<" : "<", + close = dump.HTML ? ">" : ">", + tag = node.nodeName.toLowerCase(), + ret = open + tag, + attrs = node.attributes; + + if ( attrs ) { + for ( i = 0, len = attrs.length; i < len; i++ ) { + val = attrs[ i ].nodeValue; + + // IE6 includes all attributes in .attributes, even ones not explicitly + // set. Those have values like undefined, null, 0, false, "" or + // "inherit". + if ( val && val !== "inherit" ) { + ret += " " + attrs[ i ].nodeName + "=" + + dump.parse( val, "attribute" ); + } + } + } + ret += close; + + // Show content of TextNode or CDATASection + if ( node.nodeType === 3 || node.nodeType === 4 ) { + ret += node.nodeValue; + } + + return ret + open + "/" + tag + close; + }, + + // Function calls it internally, it's the arguments part of the function + functionArgs: function( fn ) { + var args, + l = fn.length; + + if ( !l ) { + return ""; + } + + args = new Array( l ); + while ( l-- ) { + + // 97 is 'a' + args[ l ] = String.fromCharCode( 97 + l ); + } + return " " + args.join( ", " ) + " "; + }, + + // Object calls it internally, the key part of an item in a map + key: quote, + + // Function calls it internally, it's the content of the function + functionCode: "[code]", + + // Node calls it internally, it's a html attribute value + attribute: quote, + string: quote, + date: quote, + regexp: literal, + number: literal, + "boolean": literal + }, + + // If true, entities are escaped ( <, >, \t, space and \n ) + HTML: false, + + // Indentation unit + indentChar: " ", + + // If true, items in a collection, are separated by a \n, else just a space. + multiline: true + }; + + return dump; +}() ); + +// Back compat +QUnit.jsDump = QUnit.dump; + +// Deprecated +// Extend assert methods to QUnit for Backwards compatibility +( function() { + var i, + assertions = Assert.prototype; + + function applyCurrent( current ) { + return function() { + var assert = new Assert( QUnit.config.current ); + current.apply( assert, arguments ); + }; + } + + for ( i in assertions ) { + QUnit[ i ] = applyCurrent( assertions[ i ] ); + } +}() ); + +// For browser, export only select globals +if ( defined.document ) { + + ( function() { + var i, l, + keys = [ + "test", + "module", + "expect", + "asyncTest", + "start", + "stop", + "ok", + "notOk", + "equal", + "notEqual", + "propEqual", + "notPropEqual", + "deepEqual", + "notDeepEqual", + "strictEqual", + "notStrictEqual", + "throws", + "raises" + ]; + + for ( i = 0, l = keys.length; i < l; i++ ) { + window[ keys[ i ] ] = QUnit[ keys[ i ] ]; + } + }() ); + + window.QUnit = QUnit; +} + +// For nodejs +if ( typeof module !== "undefined" && module && module.exports ) { + module.exports = QUnit; + + // For consistency with CommonJS environments' exports + module.exports.QUnit = QUnit; +} + +// For CommonJS with exports, but without module.exports, like Rhino +if ( typeof exports !== "undefined" && exports ) { + exports.QUnit = QUnit; +} + +if ( typeof define === "function" && define.amd ) { + define( function() { + return QUnit; + } ); + QUnit.config.autostart = false; +} + +// Get a reference to the global object, like window in browsers +}( ( function() { + return this; +}() ) ) ); + +( function() { + +// Only interact with URLs via window.location +var location = typeof window !== "undefined" && window.location; +if ( !location ) { + return; +} + +var urlParams = getUrlParams(); + +QUnit.urlParams = urlParams; + +// Match module/test by inclusion in an array +QUnit.config.moduleId = [].concat( urlParams.moduleId || [] ); +QUnit.config.testId = [].concat( urlParams.testId || [] ); + +// Exact case-insensitive match of the module name +QUnit.config.module = urlParams.module; + +// Regular expression or case-insenstive substring match against "moduleName: testName" +QUnit.config.filter = urlParams.filter; + +// Test order randomization +if ( urlParams.seed === true ) { + + // Generate a random seed if the option is specified without a value + QUnit.config.seed = Math.random().toString( 36 ).slice( 2 ); +} else if ( urlParams.seed ) { + QUnit.config.seed = urlParams.seed; +} + +// Add URL-parameter-mapped config values with UI form rendering data +QUnit.config.urlConfig.push( + { + id: "hidepassed", + label: "Hide passed tests", + tooltip: "Only show tests and assertions that fail. Stored as query-strings." + }, + { + id: "noglobals", + label: "Check for Globals", + tooltip: "Enabling this will test if any test introduces new properties on the " + + "global object (`window` in Browsers). Stored as query-strings." + }, + { + id: "notrycatch", + label: "No try-catch", + tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging " + + "exceptions in IE reasonable. Stored as query-strings." + } +); + +QUnit.begin( function() { + var i, option, + urlConfig = QUnit.config.urlConfig; + + for ( i = 0; i < urlConfig.length; i++ ) { + + // Options can be either strings or objects with nonempty "id" properties + option = QUnit.config.urlConfig[ i ]; + if ( typeof option !== "string" ) { + option = option.id; + } + + if ( QUnit.config[ option ] === undefined ) { + QUnit.config[ option ] = urlParams[ option ]; + } + } +} ); + +function getUrlParams() { + var i, param, name, value; + var urlParams = {}; + var params = location.search.slice( 1 ).split( "&" ); + var length = params.length; + + for ( i = 0; i < length; i++ ) { + if ( params[ i ] ) { + param = params[ i ].split( "=" ); + name = decodeURIComponent( param[ 0 ] ); + + // Allow just a key to turn on a flag, e.g., test.html?noglobals + value = param.length === 1 || + decodeURIComponent( param.slice( 1 ).join( "=" ) ) ; + if ( urlParams[ name ] ) { + urlParams[ name ] = [].concat( urlParams[ name ], value ); + } else { + urlParams[ name ] = value; + } + } + } + + return urlParams; +} + +// Don't load the HTML Reporter on non-browser environments +if ( typeof window === "undefined" || !window.document ) { + return; +} + +// Deprecated QUnit.init - Ref #530 +// Re-initialize the configuration options +QUnit.init = function() { + var config = QUnit.config; + + config.stats = { all: 0, bad: 0 }; + config.moduleStats = { all: 0, bad: 0 }; + config.started = 0; + config.updateRate = 1000; + config.blocking = false; + config.autostart = true; + config.autorun = false; + config.filter = ""; + config.queue = []; + + appendInterface(); +}; + +var config = QUnit.config, + document = window.document, + collapseNext = false, + hasOwn = Object.prototype.hasOwnProperty, + unfilteredUrl = setUrl( { filter: undefined, module: undefined, + moduleId: undefined, testId: undefined } ), + defined = { + sessionStorage: ( function() { + var x = "qunit-test-string"; + try { + sessionStorage.setItem( x, x ); + sessionStorage.removeItem( x ); + return true; + } catch ( e ) { + return false; + } + }() ) + }, + modulesList = []; + +/** +* Escape text for attribute or text content. +*/ +function escapeText( s ) { + if ( !s ) { + return ""; + } + s = s + ""; + + // Both single quotes and double quotes (for attributes) + return s.replace( /['"<>&]/g, function( s ) { + switch ( s ) { + case "'": + return "'"; + case "\"": + return """; + case "<": + return "<"; + case ">": + return ">"; + case "&": + return "&"; + } + } ); +} + +/** + * @param {HTMLElement} elem + * @param {string} type + * @param {Function} fn + */ +function addEvent( elem, type, fn ) { + if ( elem.addEventListener ) { + + // Standards-based browsers + elem.addEventListener( type, fn, false ); + } else if ( elem.attachEvent ) { + + // Support: IE <9 + elem.attachEvent( "on" + type, function() { + var event = window.event; + if ( !event.target ) { + event.target = event.srcElement || document; + } + + fn.call( elem, event ); + } ); + } +} + +/** + * @param {Array|NodeList} elems + * @param {string} type + * @param {Function} fn + */ +function addEvents( elems, type, fn ) { + var i = elems.length; + while ( i-- ) { + addEvent( elems[ i ], type, fn ); + } +} + +function hasClass( elem, name ) { + return ( " " + elem.className + " " ).indexOf( " " + name + " " ) >= 0; +} + +function addClass( elem, name ) { + if ( !hasClass( elem, name ) ) { + elem.className += ( elem.className ? " " : "" ) + name; + } +} + +function toggleClass( elem, name, force ) { + if ( force || typeof force === "undefined" && !hasClass( elem, name ) ) { + addClass( elem, name ); + } else { + removeClass( elem, name ); + } +} + +function removeClass( elem, name ) { + var set = " " + elem.className + " "; + + // Class name may appear multiple times + while ( set.indexOf( " " + name + " " ) >= 0 ) { + set = set.replace( " " + name + " ", " " ); + } + + // Trim for prettiness + elem.className = typeof set.trim === "function" ? set.trim() : set.replace( /^\s+|\s+$/g, "" ); +} + +function id( name ) { + return document.getElementById && document.getElementById( name ); +} + +function getUrlConfigHtml() { + var i, j, val, + escaped, escapedTooltip, + selection = false, + urlConfig = config.urlConfig, + urlConfigHtml = ""; + + for ( i = 0; i < urlConfig.length; i++ ) { + + // Options can be either strings or objects with nonempty "id" properties + val = config.urlConfig[ i ]; + if ( typeof val === "string" ) { + val = { + id: val, + label: val + }; + } + + escaped = escapeText( val.id ); + escapedTooltip = escapeText( val.tooltip ); + + if ( !val.value || typeof val.value === "string" ) { + urlConfigHtml += ""; + } else { + urlConfigHtml += ""; + } + } + + return urlConfigHtml; +} + +// Handle "click" events on toolbar checkboxes and "change" for select menus. +// Updates the URL with the new state of `config.urlConfig` values. +function toolbarChanged() { + var updatedUrl, value, tests, + field = this, + params = {}; + + // Detect if field is a select menu or a checkbox + if ( "selectedIndex" in field ) { + value = field.options[ field.selectedIndex ].value || undefined; + } else { + value = field.checked ? ( field.defaultValue || true ) : undefined; + } + + params[ field.name ] = value; + updatedUrl = setUrl( params ); + + // Check if we can apply the change without a page refresh + if ( "hidepassed" === field.name && "replaceState" in window.history ) { + QUnit.urlParams[ field.name ] = value; + config[ field.name ] = value || false; + tests = id( "qunit-tests" ); + if ( tests ) { + toggleClass( tests, "hidepass", value || false ); + } + window.history.replaceState( null, "", updatedUrl ); + } else { + window.location = updatedUrl; + } +} + +function setUrl( params ) { + var key, arrValue, i, + querystring = "?", + location = window.location; + + params = QUnit.extend( QUnit.extend( {}, QUnit.urlParams ), params ); + + for ( key in params ) { + + // Skip inherited or undefined properties + if ( hasOwn.call( params, key ) && params[ key ] !== undefined ) { + + // Output a parameter for each value of this key (but usually just one) + arrValue = [].concat( params[ key ] ); + for ( i = 0; i < arrValue.length; i++ ) { + querystring += encodeURIComponent( key ); + if ( arrValue[ i ] !== true ) { + querystring += "=" + encodeURIComponent( arrValue[ i ] ); + } + querystring += "&"; + } + } + } + return location.protocol + "//" + location.host + + location.pathname + querystring.slice( 0, -1 ); +} + +function applyUrlParams() { + var selectedModule, + modulesList = id( "qunit-modulefilter" ), + filter = id( "qunit-filter-input" ).value; + + selectedModule = modulesList ? + decodeURIComponent( modulesList.options[ modulesList.selectedIndex ].value ) : + undefined; + + window.location = setUrl( { + module: ( selectedModule === "" ) ? undefined : selectedModule, + filter: ( filter === "" ) ? undefined : filter, + + // Remove moduleId and testId filters + moduleId: undefined, + testId: undefined + } ); +} + +function toolbarUrlConfigContainer() { + var urlConfigContainer = document.createElement( "span" ); + + urlConfigContainer.innerHTML = getUrlConfigHtml(); + addClass( urlConfigContainer, "qunit-url-config" ); + + // For oldIE support: + // * Add handlers to the individual elements instead of the container + // * Use "click" instead of "change" for checkboxes + addEvents( urlConfigContainer.getElementsByTagName( "input" ), "click", toolbarChanged ); + addEvents( urlConfigContainer.getElementsByTagName( "select" ), "change", toolbarChanged ); + + return urlConfigContainer; +} + +function toolbarLooseFilter() { + var filter = document.createElement( "form" ), + label = document.createElement( "label" ), + input = document.createElement( "input" ), + button = document.createElement( "button" ); + + addClass( filter, "qunit-filter" ); + + label.innerHTML = "Filter: "; + + input.type = "text"; + input.value = config.filter || ""; + input.name = "filter"; + input.id = "qunit-filter-input"; + + button.innerHTML = "Go"; + + label.appendChild( input ); + + filter.appendChild( label ); + filter.appendChild( button ); + addEvent( filter, "submit", function( ev ) { + applyUrlParams(); + + if ( ev && ev.preventDefault ) { + ev.preventDefault(); + } + + return false; + } ); + + return filter; +} + +function toolbarModuleFilterHtml() { + var i, + moduleFilterHtml = ""; + + if ( !modulesList.length ) { + return false; + } + + moduleFilterHtml += "" + + ""; + + return moduleFilterHtml; +} + +function toolbarModuleFilter() { + var toolbar = id( "qunit-testrunner-toolbar" ), + moduleFilter = document.createElement( "span" ), + moduleFilterHtml = toolbarModuleFilterHtml(); + + if ( !toolbar || !moduleFilterHtml ) { + return false; + } + + moduleFilter.setAttribute( "id", "qunit-modulefilter-container" ); + moduleFilter.innerHTML = moduleFilterHtml; + + addEvent( moduleFilter.lastChild, "change", applyUrlParams ); + + toolbar.appendChild( moduleFilter ); +} + +function appendToolbar() { + var toolbar = id( "qunit-testrunner-toolbar" ); + + if ( toolbar ) { + toolbar.appendChild( toolbarUrlConfigContainer() ); + toolbar.appendChild( toolbarLooseFilter() ); + toolbarModuleFilter(); + } +} + +function appendHeader() { + var header = id( "qunit-header" ); + + if ( header ) { + header.innerHTML = "" + header.innerHTML + + " "; + } +} + +function appendBanner() { + var banner = id( "qunit-banner" ); + + if ( banner ) { + banner.className = ""; + } +} + +function appendTestResults() { + var tests = id( "qunit-tests" ), + result = id( "qunit-testresult" ); + + if ( result ) { + result.parentNode.removeChild( result ); + } + + if ( tests ) { + tests.innerHTML = ""; + result = document.createElement( "p" ); + result.id = "qunit-testresult"; + result.className = "result"; + tests.parentNode.insertBefore( result, tests ); + result.innerHTML = "Running...
 "; + } +} + +function storeFixture() { + var fixture = id( "qunit-fixture" ); + if ( fixture ) { + config.fixture = fixture.innerHTML; + } +} + +function appendFilteredTest() { + var testId = QUnit.config.testId; + if ( !testId || testId.length <= 0 ) { + return ""; + } + return "
Rerunning selected tests: " + + escapeText( testId.join( ", " ) ) + + " Run all tests
"; +} + +function appendUserAgent() { + var userAgent = id( "qunit-userAgent" ); + + if ( userAgent ) { + userAgent.innerHTML = ""; + userAgent.appendChild( + document.createTextNode( + "QUnit " + QUnit.version + "; " + navigator.userAgent + ) + ); + } +} + +function appendInterface() { + var qunit = id( "qunit" ); + + if ( qunit ) { + qunit.innerHTML = + "

" + escapeText( document.title ) + "

" + + "

" + + "
" + + appendFilteredTest() + + "

" + + "
    "; + } + + appendHeader(); + appendBanner(); + appendTestResults(); + appendUserAgent(); + appendToolbar(); +} + +function appendTestsList( modules ) { + var i, l, x, z, test, moduleObj; + + for ( i = 0, l = modules.length; i < l; i++ ) { + moduleObj = modules[ i ]; + + for ( x = 0, z = moduleObj.tests.length; x < z; x++ ) { + test = moduleObj.tests[ x ]; + + appendTest( test.name, test.testId, moduleObj.name ); + } + } +} + +function appendTest( name, testId, moduleName ) { + var title, rerunTrigger, testBlock, assertList, + tests = id( "qunit-tests" ); + + if ( !tests ) { + return; + } + + title = document.createElement( "strong" ); + title.innerHTML = getNameHtml( name, moduleName ); + + rerunTrigger = document.createElement( "a" ); + rerunTrigger.innerHTML = "Rerun"; + rerunTrigger.href = setUrl( { testId: testId } ); + + testBlock = document.createElement( "li" ); + testBlock.appendChild( title ); + testBlock.appendChild( rerunTrigger ); + testBlock.id = "qunit-test-output-" + testId; + + assertList = document.createElement( "ol" ); + assertList.className = "qunit-assert-list"; + + testBlock.appendChild( assertList ); + + tests.appendChild( testBlock ); +} + +// HTML Reporter initialization and load +QUnit.begin( function( details ) { + var i, moduleObj, tests; + + // Sort modules by name for the picker + for ( i = 0; i < details.modules.length; i++ ) { + moduleObj = details.modules[ i ]; + if ( moduleObj.name ) { + modulesList.push( moduleObj.name ); + } + } + modulesList.sort( function( a, b ) { + return a.localeCompare( b ); + } ); + + // Capture fixture HTML from the page + storeFixture(); + + // Initialize QUnit elements + appendInterface(); + appendTestsList( details.modules ); + tests = id( "qunit-tests" ); + if ( tests && config.hidepassed ) { + addClass( tests, "hidepass" ); + } +} ); + +QUnit.done( function( details ) { + var i, key, + banner = id( "qunit-banner" ), + tests = id( "qunit-tests" ), + html = [ + "Tests completed in ", + details.runtime, + " milliseconds.
    ", + "", + details.passed, + " assertions of ", + details.total, + " passed, ", + details.failed, + " failed." + ].join( "" ); + + if ( banner ) { + banner.className = details.failed ? "qunit-fail" : "qunit-pass"; + } + + if ( tests ) { + id( "qunit-testresult" ).innerHTML = html; + } + + if ( config.altertitle && document.title ) { + + // Show ✖ for good, ✔ for bad suite result in title + // use escape sequences in case file gets loaded with non-utf-8-charset + document.title = [ + ( details.failed ? "\u2716" : "\u2714" ), + document.title.replace( /^[\u2714\u2716] /i, "" ) + ].join( " " ); + } + + // Clear own sessionStorage items if all tests passed + if ( config.reorder && defined.sessionStorage && details.failed === 0 ) { + for ( i = 0; i < sessionStorage.length; i++ ) { + key = sessionStorage.key( i++ ); + if ( key.indexOf( "qunit-test-" ) === 0 ) { + sessionStorage.removeItem( key ); + } + } + } + + // Scroll back to top to show results + if ( config.scrolltop && window.scrollTo ) { + window.scrollTo( 0, 0 ); + } +} ); + +function getNameHtml( name, module ) { + var nameHtml = ""; + + if ( module ) { + nameHtml = "" + escapeText( module ) + ": "; + } + + nameHtml += "" + escapeText( name ) + ""; + + return nameHtml; +} + +QUnit.testStart( function( details ) { + var running, testBlock, bad; + + testBlock = id( "qunit-test-output-" + details.testId ); + if ( testBlock ) { + testBlock.className = "running"; + } else { + + // Report later registered tests + appendTest( details.name, details.testId, details.module ); + } + + running = id( "qunit-testresult" ); + if ( running ) { + bad = QUnit.config.reorder && defined.sessionStorage && + +sessionStorage.getItem( "qunit-test-" + details.module + "-" + details.name ); + + running.innerHTML = ( bad ? + "Rerunning previously failed test:
    " : + "Running:
    " ) + + getNameHtml( details.name, details.module ); + } + +} ); + +function stripHtml( string ) { + + // Strip tags, html entity and whitespaces + return string.replace( /<\/?[^>]+(>|$)/g, "" ).replace( /\"/g, "" ).replace( /\s+/g, "" ); +} + +QUnit.log( function( details ) { + var assertList, assertLi, + message, expected, actual, diff, + showDiff = false, + testItem = id( "qunit-test-output-" + details.testId ); + + if ( !testItem ) { + return; + } + + message = escapeText( details.message ) || ( details.result ? "okay" : "failed" ); + message = "" + message + ""; + message += "@ " + details.runtime + " ms"; + + // The pushFailure doesn't provide details.expected + // when it calls, it's implicit to also not show expected and diff stuff + // Also, we need to check details.expected existence, as it can exist and be undefined + if ( !details.result && hasOwn.call( details, "expected" ) ) { + if ( details.negative ) { + expected = "NOT " + QUnit.dump.parse( details.expected ); + } else { + expected = QUnit.dump.parse( details.expected ); + } + + actual = QUnit.dump.parse( details.actual ); + message += ""; + + if ( actual !== expected ) { + + message += ""; + + // Don't show diff if actual or expected are booleans + if ( !( /^(true|false)$/.test( actual ) ) && + !( /^(true|false)$/.test( expected ) ) ) { + diff = QUnit.diff( expected, actual ); + showDiff = stripHtml( diff ).length !== + stripHtml( expected ).length + + stripHtml( actual ).length; + } + + // Don't show diff if expected and actual are totally different + if ( showDiff ) { + message += ""; + } + } else if ( expected.indexOf( "[object Array]" ) !== -1 || + expected.indexOf( "[object Object]" ) !== -1 ) { + message += ""; + } else { + message += ""; + } + + if ( details.source ) { + message += ""; + } + + message += "
    Expected:
    " +
    +			escapeText( expected ) +
    +			"
    Result:
    " +
    +				escapeText( actual ) + "
    Diff:
    " +
    +					diff + "
    Message: " + + "Diff suppressed as the depth of object is more than current max depth (" + + QUnit.config.maxDepth + ").

    Hint: Use QUnit.dump.maxDepth to " + + " run with a higher max depth or " + + "Rerun without max depth.

    Message: " + + "Diff suppressed as the expected and actual results have an equivalent" + + " serialization
    Source:
    " +
    +				escapeText( details.source ) + "
    "; + + // This occurs when pushFailure is set and we have an extracted stack trace + } else if ( !details.result && details.source ) { + message += "" + + "" + + "
    Source:
    " +
    +			escapeText( details.source ) + "
    "; + } + + assertList = testItem.getElementsByTagName( "ol" )[ 0 ]; + + assertLi = document.createElement( "li" ); + assertLi.className = details.result ? "pass" : "fail"; + assertLi.innerHTML = message; + assertList.appendChild( assertLi ); +} ); + +QUnit.testDone( function( details ) { + var testTitle, time, testItem, assertList, + good, bad, testCounts, skipped, sourceName, + tests = id( "qunit-tests" ); + + if ( !tests ) { + return; + } + + testItem = id( "qunit-test-output-" + details.testId ); + + assertList = testItem.getElementsByTagName( "ol" )[ 0 ]; + + good = details.passed; + bad = details.failed; + + // Store result when possible + if ( config.reorder && defined.sessionStorage ) { + if ( bad ) { + sessionStorage.setItem( "qunit-test-" + details.module + "-" + details.name, bad ); + } else { + sessionStorage.removeItem( "qunit-test-" + details.module + "-" + details.name ); + } + } + + if ( bad === 0 ) { + + // Collapse the passing tests + addClass( assertList, "qunit-collapsed" ); + } else if ( bad && config.collapse && !collapseNext ) { + + // Skip collapsing the first failing test + collapseNext = true; + } else { + + // Collapse remaining tests + addClass( assertList, "qunit-collapsed" ); + } + + // The testItem.firstChild is the test name + testTitle = testItem.firstChild; + + testCounts = bad ? + "" + bad + ", " + "" + good + ", " : + ""; + + testTitle.innerHTML += " (" + testCounts + + details.assertions.length + ")"; + + if ( details.skipped ) { + testItem.className = "skipped"; + skipped = document.createElement( "em" ); + skipped.className = "qunit-skipped-label"; + skipped.innerHTML = "skipped"; + testItem.insertBefore( skipped, testTitle ); + } else { + addEvent( testTitle, "click", function() { + toggleClass( assertList, "qunit-collapsed" ); + } ); + + testItem.className = bad ? "fail" : "pass"; + + time = document.createElement( "span" ); + time.className = "runtime"; + time.innerHTML = details.runtime + " ms"; + testItem.insertBefore( time, assertList ); + } + + // Show the source of the test when showing assertions + if ( details.source ) { + sourceName = document.createElement( "p" ); + sourceName.innerHTML = "Source: " + details.source; + addClass( sourceName, "qunit-source" ); + if ( bad === 0 ) { + addClass( sourceName, "qunit-collapsed" ); + } + addEvent( testTitle, "click", function() { + toggleClass( sourceName, "qunit-collapsed" ); + } ); + testItem.appendChild( sourceName ); + } +} ); + +// Avoid readyState issue with phantomjs +// Ref: #818 +var notPhantom = ( function( p ) { + return !( p && p.version && p.version.major > 0 ); +} )( window.phantom ); + +if ( notPhantom && document.readyState === "complete" ) { + QUnit.load(); +} else { + addEvent( window, "load", QUnit.load ); +} + +/* + * This file is a modified version of google-diff-match-patch's JavaScript implementation + * (https://code.google.com/p/google-diff-match-patch/source/browse/trunk/javascript/diff_match_patch_uncompressed.js), + * modifications are licensed as more fully set forth in LICENSE.txt. + * + * The original source of google-diff-match-patch is attributable and licensed as follows: + * + * Copyright 2006 Google Inc. + * https://code.google.com/p/google-diff-match-patch/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * More Info: + * https://code.google.com/p/google-diff-match-patch/ + * + * Usage: QUnit.diff(expected, actual) + * + */ +QUnit.diff = ( function() { + function DiffMatchPatch() { + } + + // DIFF FUNCTIONS + + /** + * The data structure representing a diff is an array of tuples: + * [[DIFF_DELETE, 'Hello'], [DIFF_INSERT, 'Goodbye'], [DIFF_EQUAL, ' world.']] + * which means: delete 'Hello', add 'Goodbye' and keep ' world.' + */ + var DIFF_DELETE = -1, + DIFF_INSERT = 1, + DIFF_EQUAL = 0; + + /** + * Find the differences between two texts. Simplifies the problem by stripping + * any common prefix or suffix off the texts before diffing. + * @param {string} text1 Old string to be diffed. + * @param {string} text2 New string to be diffed. + * @param {boolean=} optChecklines Optional speedup flag. If present and false, + * then don't run a line-level diff first to identify the changed areas. + * Defaults to true, which does a faster, slightly less optimal diff. + * @return {!Array.} Array of diff tuples. + */ + DiffMatchPatch.prototype.DiffMain = function( text1, text2, optChecklines ) { + var deadline, checklines, commonlength, + commonprefix, commonsuffix, diffs; + + // The diff must be complete in up to 1 second. + deadline = ( new Date() ).getTime() + 1000; + + // Check for null inputs. + if ( text1 === null || text2 === null ) { + throw new Error( "Null input. (DiffMain)" ); + } + + // Check for equality (speedup). + if ( text1 === text2 ) { + if ( text1 ) { + return [ + [ DIFF_EQUAL, text1 ] + ]; + } + return []; + } + + if ( typeof optChecklines === "undefined" ) { + optChecklines = true; + } + + checklines = optChecklines; + + // Trim off common prefix (speedup). + commonlength = this.diffCommonPrefix( text1, text2 ); + commonprefix = text1.substring( 0, commonlength ); + text1 = text1.substring( commonlength ); + text2 = text2.substring( commonlength ); + + // Trim off common suffix (speedup). + commonlength = this.diffCommonSuffix( text1, text2 ); + commonsuffix = text1.substring( text1.length - commonlength ); + text1 = text1.substring( 0, text1.length - commonlength ); + text2 = text2.substring( 0, text2.length - commonlength ); + + // Compute the diff on the middle block. + diffs = this.diffCompute( text1, text2, checklines, deadline ); + + // Restore the prefix and suffix. + if ( commonprefix ) { + diffs.unshift( [ DIFF_EQUAL, commonprefix ] ); + } + if ( commonsuffix ) { + diffs.push( [ DIFF_EQUAL, commonsuffix ] ); + } + this.diffCleanupMerge( diffs ); + return diffs; + }; + + /** + * Reduce the number of edits by eliminating operationally trivial equalities. + * @param {!Array.} diffs Array of diff tuples. + */ + DiffMatchPatch.prototype.diffCleanupEfficiency = function( diffs ) { + var changes, equalities, equalitiesLength, lastequality, + pointer, preIns, preDel, postIns, postDel; + changes = false; + equalities = []; // Stack of indices where equalities are found. + equalitiesLength = 0; // Keeping our own length var is faster in JS. + /** @type {?string} */ + lastequality = null; + + // Always equal to diffs[equalities[equalitiesLength - 1]][1] + pointer = 0; // Index of current position. + + // Is there an insertion operation before the last equality. + preIns = false; + + // Is there a deletion operation before the last equality. + preDel = false; + + // Is there an insertion operation after the last equality. + postIns = false; + + // Is there a deletion operation after the last equality. + postDel = false; + while ( pointer < diffs.length ) { + + // Equality found. + if ( diffs[ pointer ][ 0 ] === DIFF_EQUAL ) { + if ( diffs[ pointer ][ 1 ].length < 4 && ( postIns || postDel ) ) { + + // Candidate found. + equalities[ equalitiesLength++ ] = pointer; + preIns = postIns; + preDel = postDel; + lastequality = diffs[ pointer ][ 1 ]; + } else { + + // Not a candidate, and can never become one. + equalitiesLength = 0; + lastequality = null; + } + postIns = postDel = false; + + // An insertion or deletion. + } else { + + if ( diffs[ pointer ][ 0 ] === DIFF_DELETE ) { + postDel = true; + } else { + postIns = true; + } + + /* + * Five types to be split: + * ABXYCD + * AXCD + * ABXC + * AXCD + * ABXC + */ + if ( lastequality && ( ( preIns && preDel && postIns && postDel ) || + ( ( lastequality.length < 2 ) && + ( preIns + preDel + postIns + postDel ) === 3 ) ) ) { + + // Duplicate record. + diffs.splice( + equalities[ equalitiesLength - 1 ], + 0, + [ DIFF_DELETE, lastequality ] + ); + + // Change second copy to insert. + diffs[ equalities[ equalitiesLength - 1 ] + 1 ][ 0 ] = DIFF_INSERT; + equalitiesLength--; // Throw away the equality we just deleted; + lastequality = null; + if ( preIns && preDel ) { + + // No changes made which could affect previous entry, keep going. + postIns = postDel = true; + equalitiesLength = 0; + } else { + equalitiesLength--; // Throw away the previous equality. + pointer = equalitiesLength > 0 ? equalities[ equalitiesLength - 1 ] : -1; + postIns = postDel = false; + } + changes = true; + } + } + pointer++; + } + + if ( changes ) { + this.diffCleanupMerge( diffs ); + } + }; + + /** + * Convert a diff array into a pretty HTML report. + * @param {!Array.} diffs Array of diff tuples. + * @param {integer} string to be beautified. + * @return {string} HTML representation. + */ + DiffMatchPatch.prototype.diffPrettyHtml = function( diffs ) { + var op, data, x, + html = []; + for ( x = 0; x < diffs.length; x++ ) { + op = diffs[ x ][ 0 ]; // Operation (insert, delete, equal) + data = diffs[ x ][ 1 ]; // Text of change. + switch ( op ) { + case DIFF_INSERT: + html[ x ] = "" + escapeText( data ) + ""; + break; + case DIFF_DELETE: + html[ x ] = "" + escapeText( data ) + ""; + break; + case DIFF_EQUAL: + html[ x ] = "" + escapeText( data ) + ""; + break; + } + } + return html.join( "" ); + }; + + /** + * Determine the common prefix of two strings. + * @param {string} text1 First string. + * @param {string} text2 Second string. + * @return {number} The number of characters common to the start of each + * string. + */ + DiffMatchPatch.prototype.diffCommonPrefix = function( text1, text2 ) { + var pointermid, pointermax, pointermin, pointerstart; + + // Quick check for common null cases. + if ( !text1 || !text2 || text1.charAt( 0 ) !== text2.charAt( 0 ) ) { + return 0; + } + + // Binary search. + // Performance analysis: https://neil.fraser.name/news/2007/10/09/ + pointermin = 0; + pointermax = Math.min( text1.length, text2.length ); + pointermid = pointermax; + pointerstart = 0; + while ( pointermin < pointermid ) { + if ( text1.substring( pointerstart, pointermid ) === + text2.substring( pointerstart, pointermid ) ) { + pointermin = pointermid; + pointerstart = pointermin; + } else { + pointermax = pointermid; + } + pointermid = Math.floor( ( pointermax - pointermin ) / 2 + pointermin ); + } + return pointermid; + }; + + /** + * Determine the common suffix of two strings. + * @param {string} text1 First string. + * @param {string} text2 Second string. + * @return {number} The number of characters common to the end of each string. + */ + DiffMatchPatch.prototype.diffCommonSuffix = function( text1, text2 ) { + var pointermid, pointermax, pointermin, pointerend; + + // Quick check for common null cases. + if ( !text1 || + !text2 || + text1.charAt( text1.length - 1 ) !== text2.charAt( text2.length - 1 ) ) { + return 0; + } + + // Binary search. + // Performance analysis: https://neil.fraser.name/news/2007/10/09/ + pointermin = 0; + pointermax = Math.min( text1.length, text2.length ); + pointermid = pointermax; + pointerend = 0; + while ( pointermin < pointermid ) { + if ( text1.substring( text1.length - pointermid, text1.length - pointerend ) === + text2.substring( text2.length - pointermid, text2.length - pointerend ) ) { + pointermin = pointermid; + pointerend = pointermin; + } else { + pointermax = pointermid; + } + pointermid = Math.floor( ( pointermax - pointermin ) / 2 + pointermin ); + } + return pointermid; + }; + + /** + * Find the differences between two texts. Assumes that the texts do not + * have any common prefix or suffix. + * @param {string} text1 Old string to be diffed. + * @param {string} text2 New string to be diffed. + * @param {boolean} checklines Speedup flag. If false, then don't run a + * line-level diff first to identify the changed areas. + * If true, then run a faster, slightly less optimal diff. + * @param {number} deadline Time when the diff should be complete by. + * @return {!Array.} Array of diff tuples. + * @private + */ + DiffMatchPatch.prototype.diffCompute = function( text1, text2, checklines, deadline ) { + var diffs, longtext, shorttext, i, hm, + text1A, text2A, text1B, text2B, + midCommon, diffsA, diffsB; + + if ( !text1 ) { + + // Just add some text (speedup). + return [ + [ DIFF_INSERT, text2 ] + ]; + } + + if ( !text2 ) { + + // Just delete some text (speedup). + return [ + [ DIFF_DELETE, text1 ] + ]; + } + + longtext = text1.length > text2.length ? text1 : text2; + shorttext = text1.length > text2.length ? text2 : text1; + i = longtext.indexOf( shorttext ); + if ( i !== -1 ) { + + // Shorter text is inside the longer text (speedup). + diffs = [ + [ DIFF_INSERT, longtext.substring( 0, i ) ], + [ DIFF_EQUAL, shorttext ], + [ DIFF_INSERT, longtext.substring( i + shorttext.length ) ] + ]; + + // Swap insertions for deletions if diff is reversed. + if ( text1.length > text2.length ) { + diffs[ 0 ][ 0 ] = diffs[ 2 ][ 0 ] = DIFF_DELETE; + } + return diffs; + } + + if ( shorttext.length === 1 ) { + + // Single character string. + // After the previous speedup, the character can't be an equality. + return [ + [ DIFF_DELETE, text1 ], + [ DIFF_INSERT, text2 ] + ]; + } + + // Check to see if the problem can be split in two. + hm = this.diffHalfMatch( text1, text2 ); + if ( hm ) { + + // A half-match was found, sort out the return data. + text1A = hm[ 0 ]; + text1B = hm[ 1 ]; + text2A = hm[ 2 ]; + text2B = hm[ 3 ]; + midCommon = hm[ 4 ]; + + // Send both pairs off for separate processing. + diffsA = this.DiffMain( text1A, text2A, checklines, deadline ); + diffsB = this.DiffMain( text1B, text2B, checklines, deadline ); + + // Merge the results. + return diffsA.concat( [ + [ DIFF_EQUAL, midCommon ] + ], diffsB ); + } + + if ( checklines && text1.length > 100 && text2.length > 100 ) { + return this.diffLineMode( text1, text2, deadline ); + } + + return this.diffBisect( text1, text2, deadline ); + }; + + /** + * Do the two texts share a substring which is at least half the length of the + * longer text? + * This speedup can produce non-minimal diffs. + * @param {string} text1 First string. + * @param {string} text2 Second string. + * @return {Array.} Five element Array, containing the prefix of + * text1, the suffix of text1, the prefix of text2, the suffix of + * text2 and the common middle. Or null if there was no match. + * @private + */ + DiffMatchPatch.prototype.diffHalfMatch = function( text1, text2 ) { + var longtext, shorttext, dmp, + text1A, text2B, text2A, text1B, midCommon, + hm1, hm2, hm; + + longtext = text1.length > text2.length ? text1 : text2; + shorttext = text1.length > text2.length ? text2 : text1; + if ( longtext.length < 4 || shorttext.length * 2 < longtext.length ) { + return null; // Pointless. + } + dmp = this; // 'this' becomes 'window' in a closure. + + /** + * Does a substring of shorttext exist within longtext such that the substring + * is at least half the length of longtext? + * Closure, but does not reference any external variables. + * @param {string} longtext Longer string. + * @param {string} shorttext Shorter string. + * @param {number} i Start index of quarter length substring within longtext. + * @return {Array.} Five element Array, containing the prefix of + * longtext, the suffix of longtext, the prefix of shorttext, the suffix + * of shorttext and the common middle. Or null if there was no match. + * @private + */ + function diffHalfMatchI( longtext, shorttext, i ) { + var seed, j, bestCommon, prefixLength, suffixLength, + bestLongtextA, bestLongtextB, bestShorttextA, bestShorttextB; + + // Start with a 1/4 length substring at position i as a seed. + seed = longtext.substring( i, i + Math.floor( longtext.length / 4 ) ); + j = -1; + bestCommon = ""; + while ( ( j = shorttext.indexOf( seed, j + 1 ) ) !== -1 ) { + prefixLength = dmp.diffCommonPrefix( longtext.substring( i ), + shorttext.substring( j ) ); + suffixLength = dmp.diffCommonSuffix( longtext.substring( 0, i ), + shorttext.substring( 0, j ) ); + if ( bestCommon.length < suffixLength + prefixLength ) { + bestCommon = shorttext.substring( j - suffixLength, j ) + + shorttext.substring( j, j + prefixLength ); + bestLongtextA = longtext.substring( 0, i - suffixLength ); + bestLongtextB = longtext.substring( i + prefixLength ); + bestShorttextA = shorttext.substring( 0, j - suffixLength ); + bestShorttextB = shorttext.substring( j + prefixLength ); + } + } + if ( bestCommon.length * 2 >= longtext.length ) { + return [ bestLongtextA, bestLongtextB, + bestShorttextA, bestShorttextB, bestCommon + ]; + } else { + return null; + } + } + + // First check if the second quarter is the seed for a half-match. + hm1 = diffHalfMatchI( longtext, shorttext, + Math.ceil( longtext.length / 4 ) ); + + // Check again based on the third quarter. + hm2 = diffHalfMatchI( longtext, shorttext, + Math.ceil( longtext.length / 2 ) ); + if ( !hm1 && !hm2 ) { + return null; + } else if ( !hm2 ) { + hm = hm1; + } else if ( !hm1 ) { + hm = hm2; + } else { + + // Both matched. Select the longest. + hm = hm1[ 4 ].length > hm2[ 4 ].length ? hm1 : hm2; + } + + // A half-match was found, sort out the return data. + text1A, text1B, text2A, text2B; + if ( text1.length > text2.length ) { + text1A = hm[ 0 ]; + text1B = hm[ 1 ]; + text2A = hm[ 2 ]; + text2B = hm[ 3 ]; + } else { + text2A = hm[ 0 ]; + text2B = hm[ 1 ]; + text1A = hm[ 2 ]; + text1B = hm[ 3 ]; + } + midCommon = hm[ 4 ]; + return [ text1A, text1B, text2A, text2B, midCommon ]; + }; + + /** + * Do a quick line-level diff on both strings, then rediff the parts for + * greater accuracy. + * This speedup can produce non-minimal diffs. + * @param {string} text1 Old string to be diffed. + * @param {string} text2 New string to be diffed. + * @param {number} deadline Time when the diff should be complete by. + * @return {!Array.} Array of diff tuples. + * @private + */ + DiffMatchPatch.prototype.diffLineMode = function( text1, text2, deadline ) { + var a, diffs, linearray, pointer, countInsert, + countDelete, textInsert, textDelete, j; + + // Scan the text on a line-by-line basis first. + a = this.diffLinesToChars( text1, text2 ); + text1 = a.chars1; + text2 = a.chars2; + linearray = a.lineArray; + + diffs = this.DiffMain( text1, text2, false, deadline ); + + // Convert the diff back to original text. + this.diffCharsToLines( diffs, linearray ); + + // Eliminate freak matches (e.g. blank lines) + this.diffCleanupSemantic( diffs ); + + // Rediff any replacement blocks, this time character-by-character. + // Add a dummy entry at the end. + diffs.push( [ DIFF_EQUAL, "" ] ); + pointer = 0; + countDelete = 0; + countInsert = 0; + textDelete = ""; + textInsert = ""; + while ( pointer < diffs.length ) { + switch ( diffs[ pointer ][ 0 ] ) { + case DIFF_INSERT: + countInsert++; + textInsert += diffs[ pointer ][ 1 ]; + break; + case DIFF_DELETE: + countDelete++; + textDelete += diffs[ pointer ][ 1 ]; + break; + case DIFF_EQUAL: + + // Upon reaching an equality, check for prior redundancies. + if ( countDelete >= 1 && countInsert >= 1 ) { + + // Delete the offending records and add the merged ones. + diffs.splice( pointer - countDelete - countInsert, + countDelete + countInsert ); + pointer = pointer - countDelete - countInsert; + a = this.DiffMain( textDelete, textInsert, false, deadline ); + for ( j = a.length - 1; j >= 0; j-- ) { + diffs.splice( pointer, 0, a[ j ] ); + } + pointer = pointer + a.length; + } + countInsert = 0; + countDelete = 0; + textDelete = ""; + textInsert = ""; + break; + } + pointer++; + } + diffs.pop(); // Remove the dummy entry at the end. + + return diffs; + }; + + /** + * Find the 'middle snake' of a diff, split the problem in two + * and return the recursively constructed diff. + * See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations. + * @param {string} text1 Old string to be diffed. + * @param {string} text2 New string to be diffed. + * @param {number} deadline Time at which to bail if not yet complete. + * @return {!Array.} Array of diff tuples. + * @private + */ + DiffMatchPatch.prototype.diffBisect = function( text1, text2, deadline ) { + var text1Length, text2Length, maxD, vOffset, vLength, + v1, v2, x, delta, front, k1start, k1end, k2start, + k2end, k2Offset, k1Offset, x1, x2, y1, y2, d, k1, k2; + + // Cache the text lengths to prevent multiple calls. + text1Length = text1.length; + text2Length = text2.length; + maxD = Math.ceil( ( text1Length + text2Length ) / 2 ); + vOffset = maxD; + vLength = 2 * maxD; + v1 = new Array( vLength ); + v2 = new Array( vLength ); + + // Setting all elements to -1 is faster in Chrome & Firefox than mixing + // integers and undefined. + for ( x = 0; x < vLength; x++ ) { + v1[ x ] = -1; + v2[ x ] = -1; + } + v1[ vOffset + 1 ] = 0; + v2[ vOffset + 1 ] = 0; + delta = text1Length - text2Length; + + // If the total number of characters is odd, then the front path will collide + // with the reverse path. + front = ( delta % 2 !== 0 ); + + // Offsets for start and end of k loop. + // Prevents mapping of space beyond the grid. + k1start = 0; + k1end = 0; + k2start = 0; + k2end = 0; + for ( d = 0; d < maxD; d++ ) { + + // Bail out if deadline is reached. + if ( ( new Date() ).getTime() > deadline ) { + break; + } + + // Walk the front path one step. + for ( k1 = -d + k1start; k1 <= d - k1end; k1 += 2 ) { + k1Offset = vOffset + k1; + if ( k1 === -d || ( k1 !== d && v1[ k1Offset - 1 ] < v1[ k1Offset + 1 ] ) ) { + x1 = v1[ k1Offset + 1 ]; + } else { + x1 = v1[ k1Offset - 1 ] + 1; + } + y1 = x1 - k1; + while ( x1 < text1Length && y1 < text2Length && + text1.charAt( x1 ) === text2.charAt( y1 ) ) { + x1++; + y1++; + } + v1[ k1Offset ] = x1; + if ( x1 > text1Length ) { + + // Ran off the right of the graph. + k1end += 2; + } else if ( y1 > text2Length ) { + + // Ran off the bottom of the graph. + k1start += 2; + } else if ( front ) { + k2Offset = vOffset + delta - k1; + if ( k2Offset >= 0 && k2Offset < vLength && v2[ k2Offset ] !== -1 ) { + + // Mirror x2 onto top-left coordinate system. + x2 = text1Length - v2[ k2Offset ]; + if ( x1 >= x2 ) { + + // Overlap detected. + return this.diffBisectSplit( text1, text2, x1, y1, deadline ); + } + } + } + } + + // Walk the reverse path one step. + for ( k2 = -d + k2start; k2 <= d - k2end; k2 += 2 ) { + k2Offset = vOffset + k2; + if ( k2 === -d || ( k2 !== d && v2[ k2Offset - 1 ] < v2[ k2Offset + 1 ] ) ) { + x2 = v2[ k2Offset + 1 ]; + } else { + x2 = v2[ k2Offset - 1 ] + 1; + } + y2 = x2 - k2; + while ( x2 < text1Length && y2 < text2Length && + text1.charAt( text1Length - x2 - 1 ) === + text2.charAt( text2Length - y2 - 1 ) ) { + x2++; + y2++; + } + v2[ k2Offset ] = x2; + if ( x2 > text1Length ) { + + // Ran off the left of the graph. + k2end += 2; + } else if ( y2 > text2Length ) { + + // Ran off the top of the graph. + k2start += 2; + } else if ( !front ) { + k1Offset = vOffset + delta - k2; + if ( k1Offset >= 0 && k1Offset < vLength && v1[ k1Offset ] !== -1 ) { + x1 = v1[ k1Offset ]; + y1 = vOffset + x1 - k1Offset; + + // Mirror x2 onto top-left coordinate system. + x2 = text1Length - x2; + if ( x1 >= x2 ) { + + // Overlap detected. + return this.diffBisectSplit( text1, text2, x1, y1, deadline ); + } + } + } + } + } + + // Diff took too long and hit the deadline or + // number of diffs equals number of characters, no commonality at all. + return [ + [ DIFF_DELETE, text1 ], + [ DIFF_INSERT, text2 ] + ]; + }; + + /** + * Given the location of the 'middle snake', split the diff in two parts + * and recurse. + * @param {string} text1 Old string to be diffed. + * @param {string} text2 New string to be diffed. + * @param {number} x Index of split point in text1. + * @param {number} y Index of split point in text2. + * @param {number} deadline Time at which to bail if not yet complete. + * @return {!Array.} Array of diff tuples. + * @private + */ + DiffMatchPatch.prototype.diffBisectSplit = function( text1, text2, x, y, deadline ) { + var text1a, text1b, text2a, text2b, diffs, diffsb; + text1a = text1.substring( 0, x ); + text2a = text2.substring( 0, y ); + text1b = text1.substring( x ); + text2b = text2.substring( y ); + + // Compute both diffs serially. + diffs = this.DiffMain( text1a, text2a, false, deadline ); + diffsb = this.DiffMain( text1b, text2b, false, deadline ); + + return diffs.concat( diffsb ); + }; + + /** + * Reduce the number of edits by eliminating semantically trivial equalities. + * @param {!Array.} diffs Array of diff tuples. + */ + DiffMatchPatch.prototype.diffCleanupSemantic = function( diffs ) { + var changes, equalities, equalitiesLength, lastequality, + pointer, lengthInsertions2, lengthDeletions2, lengthInsertions1, + lengthDeletions1, deletion, insertion, overlapLength1, overlapLength2; + changes = false; + equalities = []; // Stack of indices where equalities are found. + equalitiesLength = 0; // Keeping our own length var is faster in JS. + /** @type {?string} */ + lastequality = null; + + // Always equal to diffs[equalities[equalitiesLength - 1]][1] + pointer = 0; // Index of current position. + + // Number of characters that changed prior to the equality. + lengthInsertions1 = 0; + lengthDeletions1 = 0; + + // Number of characters that changed after the equality. + lengthInsertions2 = 0; + lengthDeletions2 = 0; + while ( pointer < diffs.length ) { + if ( diffs[ pointer ][ 0 ] === DIFF_EQUAL ) { // Equality found. + equalities[ equalitiesLength++ ] = pointer; + lengthInsertions1 = lengthInsertions2; + lengthDeletions1 = lengthDeletions2; + lengthInsertions2 = 0; + lengthDeletions2 = 0; + lastequality = diffs[ pointer ][ 1 ]; + } else { // An insertion or deletion. + if ( diffs[ pointer ][ 0 ] === DIFF_INSERT ) { + lengthInsertions2 += diffs[ pointer ][ 1 ].length; + } else { + lengthDeletions2 += diffs[ pointer ][ 1 ].length; + } + + // Eliminate an equality that is smaller or equal to the edits on both + // sides of it. + if ( lastequality && ( lastequality.length <= + Math.max( lengthInsertions1, lengthDeletions1 ) ) && + ( lastequality.length <= Math.max( lengthInsertions2, + lengthDeletions2 ) ) ) { + + // Duplicate record. + diffs.splice( + equalities[ equalitiesLength - 1 ], + 0, + [ DIFF_DELETE, lastequality ] + ); + + // Change second copy to insert. + diffs[ equalities[ equalitiesLength - 1 ] + 1 ][ 0 ] = DIFF_INSERT; + + // Throw away the equality we just deleted. + equalitiesLength--; + + // Throw away the previous equality (it needs to be reevaluated). + equalitiesLength--; + pointer = equalitiesLength > 0 ? equalities[ equalitiesLength - 1 ] : -1; + + // Reset the counters. + lengthInsertions1 = 0; + lengthDeletions1 = 0; + lengthInsertions2 = 0; + lengthDeletions2 = 0; + lastequality = null; + changes = true; + } + } + pointer++; + } + + // Normalize the diff. + if ( changes ) { + this.diffCleanupMerge( diffs ); + } + + // Find any overlaps between deletions and insertions. + // e.g: abcxxxxxxdef + // -> abcxxxdef + // e.g: xxxabcdefxxx + // -> defxxxabc + // Only extract an overlap if it is as big as the edit ahead or behind it. + pointer = 1; + while ( pointer < diffs.length ) { + if ( diffs[ pointer - 1 ][ 0 ] === DIFF_DELETE && + diffs[ pointer ][ 0 ] === DIFF_INSERT ) { + deletion = diffs[ pointer - 1 ][ 1 ]; + insertion = diffs[ pointer ][ 1 ]; + overlapLength1 = this.diffCommonOverlap( deletion, insertion ); + overlapLength2 = this.diffCommonOverlap( insertion, deletion ); + if ( overlapLength1 >= overlapLength2 ) { + if ( overlapLength1 >= deletion.length / 2 || + overlapLength1 >= insertion.length / 2 ) { + + // Overlap found. Insert an equality and trim the surrounding edits. + diffs.splice( + pointer, + 0, + [ DIFF_EQUAL, insertion.substring( 0, overlapLength1 ) ] + ); + diffs[ pointer - 1 ][ 1 ] = + deletion.substring( 0, deletion.length - overlapLength1 ); + diffs[ pointer + 1 ][ 1 ] = insertion.substring( overlapLength1 ); + pointer++; + } + } else { + if ( overlapLength2 >= deletion.length / 2 || + overlapLength2 >= insertion.length / 2 ) { + + // Reverse overlap found. + // Insert an equality and swap and trim the surrounding edits. + diffs.splice( + pointer, + 0, + [ DIFF_EQUAL, deletion.substring( 0, overlapLength2 ) ] + ); + + diffs[ pointer - 1 ][ 0 ] = DIFF_INSERT; + diffs[ pointer - 1 ][ 1 ] = + insertion.substring( 0, insertion.length - overlapLength2 ); + diffs[ pointer + 1 ][ 0 ] = DIFF_DELETE; + diffs[ pointer + 1 ][ 1 ] = + deletion.substring( overlapLength2 ); + pointer++; + } + } + pointer++; + } + pointer++; + } + }; + + /** + * Determine if the suffix of one string is the prefix of another. + * @param {string} text1 First string. + * @param {string} text2 Second string. + * @return {number} The number of characters common to the end of the first + * string and the start of the second string. + * @private + */ + DiffMatchPatch.prototype.diffCommonOverlap = function( text1, text2 ) { + var text1Length, text2Length, textLength, + best, length, pattern, found; + + // Cache the text lengths to prevent multiple calls. + text1Length = text1.length; + text2Length = text2.length; + + // Eliminate the null case. + if ( text1Length === 0 || text2Length === 0 ) { + return 0; + } + + // Truncate the longer string. + if ( text1Length > text2Length ) { + text1 = text1.substring( text1Length - text2Length ); + } else if ( text1Length < text2Length ) { + text2 = text2.substring( 0, text1Length ); + } + textLength = Math.min( text1Length, text2Length ); + + // Quick check for the worst case. + if ( text1 === text2 ) { + return textLength; + } + + // Start by looking for a single character match + // and increase length until no match is found. + // Performance analysis: https://neil.fraser.name/news/2010/11/04/ + best = 0; + length = 1; + while ( true ) { + pattern = text1.substring( textLength - length ); + found = text2.indexOf( pattern ); + if ( found === -1 ) { + return best; + } + length += found; + if ( found === 0 || text1.substring( textLength - length ) === + text2.substring( 0, length ) ) { + best = length; + length++; + } + } + }; + + /** + * Split two texts into an array of strings. Reduce the texts to a string of + * hashes where each Unicode character represents one line. + * @param {string} text1 First string. + * @param {string} text2 Second string. + * @return {{chars1: string, chars2: string, lineArray: !Array.}} + * An object containing the encoded text1, the encoded text2 and + * the array of unique strings. + * The zeroth element of the array of unique strings is intentionally blank. + * @private + */ + DiffMatchPatch.prototype.diffLinesToChars = function( text1, text2 ) { + var lineArray, lineHash, chars1, chars2; + lineArray = []; // E.g. lineArray[4] === 'Hello\n' + lineHash = {}; // E.g. lineHash['Hello\n'] === 4 + + // '\x00' is a valid character, but various debuggers don't like it. + // So we'll insert a junk entry to avoid generating a null character. + lineArray[ 0 ] = ""; + + /** + * Split a text into an array of strings. Reduce the texts to a string of + * hashes where each Unicode character represents one line. + * Modifies linearray and linehash through being a closure. + * @param {string} text String to encode. + * @return {string} Encoded string. + * @private + */ + function diffLinesToCharsMunge( text ) { + var chars, lineStart, lineEnd, lineArrayLength, line; + chars = ""; + + // Walk the text, pulling out a substring for each line. + // text.split('\n') would would temporarily double our memory footprint. + // Modifying text would create many large strings to garbage collect. + lineStart = 0; + lineEnd = -1; + + // Keeping our own length variable is faster than looking it up. + lineArrayLength = lineArray.length; + while ( lineEnd < text.length - 1 ) { + lineEnd = text.indexOf( "\n", lineStart ); + if ( lineEnd === -1 ) { + lineEnd = text.length - 1; + } + line = text.substring( lineStart, lineEnd + 1 ); + lineStart = lineEnd + 1; + + if ( lineHash.hasOwnProperty ? lineHash.hasOwnProperty( line ) : + ( lineHash[ line ] !== undefined ) ) { + chars += String.fromCharCode( lineHash[ line ] ); + } else { + chars += String.fromCharCode( lineArrayLength ); + lineHash[ line ] = lineArrayLength; + lineArray[ lineArrayLength++ ] = line; + } + } + return chars; + } + + chars1 = diffLinesToCharsMunge( text1 ); + chars2 = diffLinesToCharsMunge( text2 ); + return { + chars1: chars1, + chars2: chars2, + lineArray: lineArray + }; + }; + + /** + * Rehydrate the text in a diff from a string of line hashes to real lines of + * text. + * @param {!Array.} diffs Array of diff tuples. + * @param {!Array.} lineArray Array of unique strings. + * @private + */ + DiffMatchPatch.prototype.diffCharsToLines = function( diffs, lineArray ) { + var x, chars, text, y; + for ( x = 0; x < diffs.length; x++ ) { + chars = diffs[ x ][ 1 ]; + text = []; + for ( y = 0; y < chars.length; y++ ) { + text[ y ] = lineArray[ chars.charCodeAt( y ) ]; + } + diffs[ x ][ 1 ] = text.join( "" ); + } + }; + + /** + * Reorder and merge like edit sections. Merge equalities. + * Any edit section can move as long as it doesn't cross an equality. + * @param {!Array.} diffs Array of diff tuples. + */ + DiffMatchPatch.prototype.diffCleanupMerge = function( diffs ) { + var pointer, countDelete, countInsert, textInsert, textDelete, + commonlength, changes, diffPointer, position; + diffs.push( [ DIFF_EQUAL, "" ] ); // Add a dummy entry at the end. + pointer = 0; + countDelete = 0; + countInsert = 0; + textDelete = ""; + textInsert = ""; + commonlength; + while ( pointer < diffs.length ) { + switch ( diffs[ pointer ][ 0 ] ) { + case DIFF_INSERT: + countInsert++; + textInsert += diffs[ pointer ][ 1 ]; + pointer++; + break; + case DIFF_DELETE: + countDelete++; + textDelete += diffs[ pointer ][ 1 ]; + pointer++; + break; + case DIFF_EQUAL: + + // Upon reaching an equality, check for prior redundancies. + if ( countDelete + countInsert > 1 ) { + if ( countDelete !== 0 && countInsert !== 0 ) { + + // Factor out any common prefixes. + commonlength = this.diffCommonPrefix( textInsert, textDelete ); + if ( commonlength !== 0 ) { + if ( ( pointer - countDelete - countInsert ) > 0 && + diffs[ pointer - countDelete - countInsert - 1 ][ 0 ] === + DIFF_EQUAL ) { + diffs[ pointer - countDelete - countInsert - 1 ][ 1 ] += + textInsert.substring( 0, commonlength ); + } else { + diffs.splice( 0, 0, [ DIFF_EQUAL, + textInsert.substring( 0, commonlength ) + ] ); + pointer++; + } + textInsert = textInsert.substring( commonlength ); + textDelete = textDelete.substring( commonlength ); + } + + // Factor out any common suffixies. + commonlength = this.diffCommonSuffix( textInsert, textDelete ); + if ( commonlength !== 0 ) { + diffs[ pointer ][ 1 ] = textInsert.substring( textInsert.length - + commonlength ) + diffs[ pointer ][ 1 ]; + textInsert = textInsert.substring( 0, textInsert.length - + commonlength ); + textDelete = textDelete.substring( 0, textDelete.length - + commonlength ); + } + } + + // Delete the offending records and add the merged ones. + if ( countDelete === 0 ) { + diffs.splice( pointer - countInsert, + countDelete + countInsert, [ DIFF_INSERT, textInsert ] ); + } else if ( countInsert === 0 ) { + diffs.splice( pointer - countDelete, + countDelete + countInsert, [ DIFF_DELETE, textDelete ] ); + } else { + diffs.splice( + pointer - countDelete - countInsert, + countDelete + countInsert, + [ DIFF_DELETE, textDelete ], [ DIFF_INSERT, textInsert ] + ); + } + pointer = pointer - countDelete - countInsert + + ( countDelete ? 1 : 0 ) + ( countInsert ? 1 : 0 ) + 1; + } else if ( pointer !== 0 && diffs[ pointer - 1 ][ 0 ] === DIFF_EQUAL ) { + + // Merge this equality with the previous one. + diffs[ pointer - 1 ][ 1 ] += diffs[ pointer ][ 1 ]; + diffs.splice( pointer, 1 ); + } else { + pointer++; + } + countInsert = 0; + countDelete = 0; + textDelete = ""; + textInsert = ""; + break; + } + } + if ( diffs[ diffs.length - 1 ][ 1 ] === "" ) { + diffs.pop(); // Remove the dummy entry at the end. + } + + // Second pass: look for single edits surrounded on both sides by equalities + // which can be shifted sideways to eliminate an equality. + // e.g: ABAC -> ABAC + changes = false; + pointer = 1; + + // Intentionally ignore the first and last element (don't need checking). + while ( pointer < diffs.length - 1 ) { + if ( diffs[ pointer - 1 ][ 0 ] === DIFF_EQUAL && + diffs[ pointer + 1 ][ 0 ] === DIFF_EQUAL ) { + + diffPointer = diffs[ pointer ][ 1 ]; + position = diffPointer.substring( + diffPointer.length - diffs[ pointer - 1 ][ 1 ].length + ); + + // This is a single edit surrounded by equalities. + if ( position === diffs[ pointer - 1 ][ 1 ] ) { + + // Shift the edit over the previous equality. + diffs[ pointer ][ 1 ] = diffs[ pointer - 1 ][ 1 ] + + diffs[ pointer ][ 1 ].substring( 0, diffs[ pointer ][ 1 ].length - + diffs[ pointer - 1 ][ 1 ].length ); + diffs[ pointer + 1 ][ 1 ] = + diffs[ pointer - 1 ][ 1 ] + diffs[ pointer + 1 ][ 1 ]; + diffs.splice( pointer - 1, 1 ); + changes = true; + } else if ( diffPointer.substring( 0, diffs[ pointer + 1 ][ 1 ].length ) === + diffs[ pointer + 1 ][ 1 ] ) { + + // Shift the edit over the next equality. + diffs[ pointer - 1 ][ 1 ] += diffs[ pointer + 1 ][ 1 ]; + diffs[ pointer ][ 1 ] = + diffs[ pointer ][ 1 ].substring( diffs[ pointer + 1 ][ 1 ].length ) + + diffs[ pointer + 1 ][ 1 ]; + diffs.splice( pointer + 1, 1 ); + changes = true; + } + } + pointer++; + } + + // If shifts were made, the diff needs reordering and another shift sweep. + if ( changes ) { + this.diffCleanupMerge( diffs ); + } + }; + + return function( o, n ) { + var diff, output, text; + diff = new DiffMatchPatch(); + output = diff.DiffMain( o, n ); + diff.diffCleanupEfficiency( output ); + text = diff.diffPrettyHtml( output ); + + return text; + }; +}() ); + +}() ); + +},{}],74:[function(require,module,exports){ +/** + * Sinon core utilities. For internal use only. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +var sinon = (function () { // eslint-disable-line no-unused-vars + "use strict"; + + var sinonModule; + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + sinonModule = module.exports = require("./sinon/util/core"); + require("./sinon/extend"); + require("./sinon/walk"); + require("./sinon/typeOf"); + require("./sinon/times_in_words"); + require("./sinon/spy"); + require("./sinon/call"); + require("./sinon/behavior"); + require("./sinon/stub"); + require("./sinon/mock"); + require("./sinon/collection"); + require("./sinon/assert"); + require("./sinon/sandbox"); + require("./sinon/test"); + require("./sinon/test_case"); + require("./sinon/match"); + require("./sinon/format"); + require("./sinon/log_error"); + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require, module.exports, module); + sinonModule = module.exports; + } else { + sinonModule = {}; + } + + return sinonModule; +}()); + +},{"./sinon/assert":75,"./sinon/behavior":76,"./sinon/call":77,"./sinon/collection":78,"./sinon/extend":79,"./sinon/format":80,"./sinon/log_error":81,"./sinon/match":82,"./sinon/mock":83,"./sinon/sandbox":84,"./sinon/spy":85,"./sinon/stub":86,"./sinon/test":87,"./sinon/test_case":88,"./sinon/times_in_words":89,"./sinon/typeOf":90,"./sinon/util/core":91,"./sinon/walk":98}],75:[function(require,module,exports){ +(function (global){ +/** + * @depend times_in_words.js + * @depend util/core.js + * @depend match.js + * @depend format.js + */ +/** + * Assertions matching the test spy retrieval interface. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function (sinonGlobal, global) { + "use strict"; + + var slice = Array.prototype.slice; + + function makeApi(sinon) { + var assert; + + function verifyIsStub() { + var method; + + for (var i = 0, l = arguments.length; i < l; ++i) { + method = arguments[i]; + + if (!method) { + assert.fail("fake is not a spy"); + } + + if (method.proxy && method.proxy.isSinonProxy) { + verifyIsStub(method.proxy); + } else { + if (typeof method !== "function") { + assert.fail(method + " is not a function"); + } + + if (typeof method.getCall !== "function") { + assert.fail(method + " is not stubbed"); + } + } + + } + } + + function verifyIsValidAssertion(assertionMethod, assertionArgs) { + switch (assertionMethod) { + case "notCalled": + case "called": + case "calledOnce": + case "calledTwice": + case "calledThrice": + if (assertionArgs.length !== 0) { + assert.fail(assertionMethod + + " takes 1 argument but was called with " + + (assertionArgs.length + 1) + " arguments"); + } + break; + default: + break; + } + } + + function failAssertion(object, msg) { + object = object || global; + var failMethod = object.fail || assert.fail; + failMethod.call(object, msg); + } + + function mirrorPropAsAssertion(name, method, message) { + if (arguments.length === 2) { + message = method; + method = name; + } + + assert[name] = function (fake) { + verifyIsStub(fake); + + var args = slice.call(arguments, 1); + verifyIsValidAssertion(name, args); + + var failed = false; + + if (typeof method === "function") { + failed = !method(fake); + } else { + failed = typeof fake[method] === "function" ? + !fake[method].apply(fake, args) : !fake[method]; + } + + if (failed) { + failAssertion(this, (fake.printf || fake.proxy.printf).apply(fake, [message].concat(args))); + } else { + assert.pass(name); + } + }; + } + + function exposedName(prefix, prop) { + return !prefix || /^fail/.test(prop) ? prop : + prefix + prop.slice(0, 1).toUpperCase() + prop.slice(1); + } + + assert = { + failException: "AssertError", + + fail: function fail(message) { + var error = new Error(message); + error.name = this.failException || assert.failException; + + throw error; + }, + + pass: function pass() {}, + + callOrder: function assertCallOrder() { + verifyIsStub.apply(null, arguments); + var expected = ""; + var actual = ""; + + if (!sinon.calledInOrder(arguments)) { + try { + expected = [].join.call(arguments, ", "); + var calls = slice.call(arguments); + var i = calls.length; + while (i) { + if (!calls[--i].called) { + calls.splice(i, 1); + } + } + actual = sinon.orderByFirstCall(calls).join(", "); + } catch (e) { + // If this fails, we'll just fall back to the blank string + } + + failAssertion(this, "expected " + expected + " to be " + + "called in order but were called as " + actual); + } else { + assert.pass("callOrder"); + } + }, + + callCount: function assertCallCount(method, count) { + verifyIsStub(method); + + if (method.callCount !== count) { + var msg = "expected %n to be called " + sinon.timesInWords(count) + + " but was called %c%C"; + failAssertion(this, method.printf(msg)); + } else { + assert.pass("callCount"); + } + }, + + expose: function expose(target, options) { + if (!target) { + throw new TypeError("target is null or undefined"); + } + + var o = options || {}; + var prefix = typeof o.prefix === "undefined" && "assert" || o.prefix; + var includeFail = typeof o.includeFail === "undefined" || !!o.includeFail; + + for (var method in this) { + if (method !== "expose" && (includeFail || !/^(fail)/.test(method))) { + target[exposedName(prefix, method)] = this[method]; + } + } + + return target; + }, + + match: function match(actual, expectation) { + var matcher = sinon.match(expectation); + if (matcher.test(actual)) { + assert.pass("match"); + } else { + var formatted = [ + "expected value to match", + " expected = " + sinon.format(expectation), + " actual = " + sinon.format(actual) + ]; + + failAssertion(this, formatted.join("\n")); + } + } + }; + + mirrorPropAsAssertion("called", "expected %n to have been called at least once but was never called"); + mirrorPropAsAssertion("notCalled", function (spy) { + return !spy.called; + }, "expected %n to not have been called but was called %c%C"); + mirrorPropAsAssertion("calledOnce", "expected %n to be called once but was called %c%C"); + mirrorPropAsAssertion("calledTwice", "expected %n to be called twice but was called %c%C"); + mirrorPropAsAssertion("calledThrice", "expected %n to be called thrice but was called %c%C"); + mirrorPropAsAssertion("calledOn", "expected %n to be called with %1 as this but was called with %t"); + mirrorPropAsAssertion( + "alwaysCalledOn", + "expected %n to always be called with %1 as this but was called with %t" + ); + mirrorPropAsAssertion("calledWithNew", "expected %n to be called with new"); + mirrorPropAsAssertion("alwaysCalledWithNew", "expected %n to always be called with new"); + mirrorPropAsAssertion("calledWith", "expected %n to be called with arguments %*%C"); + mirrorPropAsAssertion("calledWithMatch", "expected %n to be called with match %*%C"); + mirrorPropAsAssertion("alwaysCalledWith", "expected %n to always be called with arguments %*%C"); + mirrorPropAsAssertion("alwaysCalledWithMatch", "expected %n to always be called with match %*%C"); + mirrorPropAsAssertion("calledWithExactly", "expected %n to be called with exact arguments %*%C"); + mirrorPropAsAssertion("alwaysCalledWithExactly", "expected %n to always be called with exact arguments %*%C"); + mirrorPropAsAssertion("neverCalledWith", "expected %n to never be called with arguments %*%C"); + mirrorPropAsAssertion("neverCalledWithMatch", "expected %n to never be called with match %*%C"); + mirrorPropAsAssertion("threw", "%n did not throw exception%C"); + mirrorPropAsAssertion("alwaysThrew", "%n did not always throw exception%C"); + + sinon.assert = assert; + return assert; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + require("./match"); + require("./format"); + module.exports = makeApi(sinon); + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon, // eslint-disable-line no-undef + typeof global !== "undefined" ? global : self +)); + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"./format":80,"./match":82,"./util/core":91}],76:[function(require,module,exports){ +(function (process){ +/** + * @depend util/core.js + * @depend extend.js + */ +/** + * Stub behavior + * + * @author Christian Johansen (christian@cjohansen.no) + * @author Tim Fischbach (mail@timfischbach.de) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function (sinonGlobal) { + "use strict"; + + var slice = Array.prototype.slice; + var join = Array.prototype.join; + var useLeftMostCallback = -1; + var useRightMostCallback = -2; + + var nextTick = (function () { + if (typeof process === "object" && typeof process.nextTick === "function") { + return process.nextTick; + } + + if (typeof setImmediate === "function") { + return setImmediate; + } + + return function (callback) { + setTimeout(callback, 0); + }; + })(); + + function throwsException(error, message) { + if (typeof error === "string") { + this.exception = new Error(message || ""); + this.exception.name = error; + } else if (!error) { + this.exception = new Error("Error"); + } else { + this.exception = error; + } + + return this; + } + + function getCallback(behavior, args) { + var callArgAt = behavior.callArgAt; + + if (callArgAt >= 0) { + return args[callArgAt]; + } + + var argumentList; + + if (callArgAt === useLeftMostCallback) { + argumentList = args; + } + + if (callArgAt === useRightMostCallback) { + argumentList = slice.call(args).reverse(); + } + + var callArgProp = behavior.callArgProp; + + for (var i = 0, l = argumentList.length; i < l; ++i) { + if (!callArgProp && typeof argumentList[i] === "function") { + return argumentList[i]; + } + + if (callArgProp && argumentList[i] && + typeof argumentList[i][callArgProp] === "function") { + return argumentList[i][callArgProp]; + } + } + + return null; + } + + function makeApi(sinon) { + function getCallbackError(behavior, func, args) { + if (behavior.callArgAt < 0) { + var msg; + + if (behavior.callArgProp) { + msg = sinon.functionName(behavior.stub) + + " expected to yield to '" + behavior.callArgProp + + "', but no object with such a property was passed."; + } else { + msg = sinon.functionName(behavior.stub) + + " expected to yield, but no callback was passed."; + } + + if (args.length > 0) { + msg += " Received [" + join.call(args, ", ") + "]"; + } + + return msg; + } + + return "argument at index " + behavior.callArgAt + " is not a function: " + func; + } + + function callCallback(behavior, args) { + if (typeof behavior.callArgAt === "number") { + var func = getCallback(behavior, args); + + if (typeof func !== "function") { + throw new TypeError(getCallbackError(behavior, func, args)); + } + + if (behavior.callbackAsync) { + nextTick(function () { + func.apply(behavior.callbackContext, behavior.callbackArguments); + }); + } else { + func.apply(behavior.callbackContext, behavior.callbackArguments); + } + } + } + + var proto = { + create: function create(stub) { + var behavior = sinon.extend({}, sinon.behavior); + delete behavior.create; + behavior.stub = stub; + + return behavior; + }, + + isPresent: function isPresent() { + return (typeof this.callArgAt === "number" || + this.exception || + typeof this.returnArgAt === "number" || + this.returnThis || + this.returnValueDefined); + }, + + invoke: function invoke(context, args) { + callCallback(this, args); + + if (this.exception) { + throw this.exception; + } else if (typeof this.returnArgAt === "number") { + return args[this.returnArgAt]; + } else if (this.returnThis) { + return context; + } + + return this.returnValue; + }, + + onCall: function onCall(index) { + return this.stub.onCall(index); + }, + + onFirstCall: function onFirstCall() { + return this.stub.onFirstCall(); + }, + + onSecondCall: function onSecondCall() { + return this.stub.onSecondCall(); + }, + + onThirdCall: function onThirdCall() { + return this.stub.onThirdCall(); + }, + + withArgs: function withArgs(/* arguments */) { + throw new Error( + "Defining a stub by invoking \"stub.onCall(...).withArgs(...)\" " + + "is not supported. Use \"stub.withArgs(...).onCall(...)\" " + + "to define sequential behavior for calls with certain arguments." + ); + }, + + callsArg: function callsArg(pos) { + if (typeof pos !== "number") { + throw new TypeError("argument index is not number"); + } + + this.callArgAt = pos; + this.callbackArguments = []; + this.callbackContext = undefined; + this.callArgProp = undefined; + this.callbackAsync = false; + + return this; + }, + + callsArgOn: function callsArgOn(pos, context) { + if (typeof pos !== "number") { + throw new TypeError("argument index is not number"); + } + if (typeof context !== "object") { + throw new TypeError("argument context is not an object"); + } + + this.callArgAt = pos; + this.callbackArguments = []; + this.callbackContext = context; + this.callArgProp = undefined; + this.callbackAsync = false; + + return this; + }, + + callsArgWith: function callsArgWith(pos) { + if (typeof pos !== "number") { + throw new TypeError("argument index is not number"); + } + + this.callArgAt = pos; + this.callbackArguments = slice.call(arguments, 1); + this.callbackContext = undefined; + this.callArgProp = undefined; + this.callbackAsync = false; + + return this; + }, + + callsArgOnWith: function callsArgWith(pos, context) { + if (typeof pos !== "number") { + throw new TypeError("argument index is not number"); + } + if (typeof context !== "object") { + throw new TypeError("argument context is not an object"); + } + + this.callArgAt = pos; + this.callbackArguments = slice.call(arguments, 2); + this.callbackContext = context; + this.callArgProp = undefined; + this.callbackAsync = false; + + return this; + }, + + yields: function () { + this.callArgAt = useLeftMostCallback; + this.callbackArguments = slice.call(arguments, 0); + this.callbackContext = undefined; + this.callArgProp = undefined; + this.callbackAsync = false; + + return this; + }, + + yieldsRight: function () { + this.callArgAt = useRightMostCallback; + this.callbackArguments = slice.call(arguments, 0); + this.callbackContext = undefined; + this.callArgProp = undefined; + this.callbackAsync = false; + + return this; + }, + + yieldsOn: function (context) { + if (typeof context !== "object") { + throw new TypeError("argument context is not an object"); + } + + this.callArgAt = useLeftMostCallback; + this.callbackArguments = slice.call(arguments, 1); + this.callbackContext = context; + this.callArgProp = undefined; + this.callbackAsync = false; + + return this; + }, + + yieldsTo: function (prop) { + this.callArgAt = useLeftMostCallback; + this.callbackArguments = slice.call(arguments, 1); + this.callbackContext = undefined; + this.callArgProp = prop; + this.callbackAsync = false; + + return this; + }, + + yieldsToOn: function (prop, context) { + if (typeof context !== "object") { + throw new TypeError("argument context is not an object"); + } + + this.callArgAt = useLeftMostCallback; + this.callbackArguments = slice.call(arguments, 2); + this.callbackContext = context; + this.callArgProp = prop; + this.callbackAsync = false; + + return this; + }, + + throws: throwsException, + throwsException: throwsException, + + returns: function returns(value) { + this.returnValue = value; + this.returnValueDefined = true; + this.exception = undefined; + + return this; + }, + + returnsArg: function returnsArg(pos) { + if (typeof pos !== "number") { + throw new TypeError("argument index is not number"); + } + + this.returnArgAt = pos; + + return this; + }, + + returnsThis: function returnsThis() { + this.returnThis = true; + + return this; + } + }; + + function createAsyncVersion(syncFnName) { + return function () { + var result = this[syncFnName].apply(this, arguments); + this.callbackAsync = true; + return result; + }; + } + + // create asynchronous versions of callsArg* and yields* methods + for (var method in proto) { + // need to avoid creating anotherasync versions of the newly added async methods + if (proto.hasOwnProperty(method) && method.match(/^(callsArg|yields)/) && !method.match(/Async/)) { + proto[method + "Async"] = createAsyncVersion(method); + } + } + + sinon.behavior = proto; + return proto; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + require("./extend"); + module.exports = makeApi(sinon); + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +}).call(this,require('_process')) +},{"./extend":79,"./util/core":91,"_process":31}],77:[function(require,module,exports){ +/** + * @depend util/core.js + * @depend match.js + * @depend format.js + */ +/** + * Spy calls + * + * @author Christian Johansen (christian@cjohansen.no) + * @author Maximilian Antoni (mail@maxantoni.de) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + * Copyright (c) 2013 Maximilian Antoni + */ +(function (sinonGlobal) { + "use strict"; + + var slice = Array.prototype.slice; + + function makeApi(sinon) { + function throwYieldError(proxy, text, args) { + var msg = sinon.functionName(proxy) + text; + if (args.length) { + msg += " Received [" + slice.call(args).join(", ") + "]"; + } + throw new Error(msg); + } + + var callProto = { + calledOn: function calledOn(thisValue) { + if (sinon.match && sinon.match.isMatcher(thisValue)) { + return thisValue.test(this.thisValue); + } + return this.thisValue === thisValue; + }, + + calledWith: function calledWith() { + var l = arguments.length; + if (l > this.args.length) { + return false; + } + for (var i = 0; i < l; i += 1) { + if (!sinon.deepEqual(arguments[i], this.args[i])) { + return false; + } + } + + return true; + }, + + calledWithMatch: function calledWithMatch() { + var l = arguments.length; + if (l > this.args.length) { + return false; + } + for (var i = 0; i < l; i += 1) { + var actual = this.args[i]; + var expectation = arguments[i]; + if (!sinon.match || !sinon.match(expectation).test(actual)) { + return false; + } + } + return true; + }, + + calledWithExactly: function calledWithExactly() { + return arguments.length === this.args.length && + this.calledWith.apply(this, arguments); + }, + + notCalledWith: function notCalledWith() { + return !this.calledWith.apply(this, arguments); + }, + + notCalledWithMatch: function notCalledWithMatch() { + return !this.calledWithMatch.apply(this, arguments); + }, + + returned: function returned(value) { + return sinon.deepEqual(value, this.returnValue); + }, + + threw: function threw(error) { + if (typeof error === "undefined" || !this.exception) { + return !!this.exception; + } + + return this.exception === error || this.exception.name === error; + }, + + calledWithNew: function calledWithNew() { + return this.proxy.prototype && this.thisValue instanceof this.proxy; + }, + + calledBefore: function (other) { + return this.callId < other.callId; + }, + + calledAfter: function (other) { + return this.callId > other.callId; + }, + + callArg: function (pos) { + this.args[pos](); + }, + + callArgOn: function (pos, thisValue) { + this.args[pos].apply(thisValue); + }, + + callArgWith: function (pos) { + this.callArgOnWith.apply(this, [pos, null].concat(slice.call(arguments, 1))); + }, + + callArgOnWith: function (pos, thisValue) { + var args = slice.call(arguments, 2); + this.args[pos].apply(thisValue, args); + }, + + "yield": function () { + this.yieldOn.apply(this, [null].concat(slice.call(arguments, 0))); + }, + + yieldOn: function (thisValue) { + var args = this.args; + for (var i = 0, l = args.length; i < l; ++i) { + if (typeof args[i] === "function") { + args[i].apply(thisValue, slice.call(arguments, 1)); + return; + } + } + throwYieldError(this.proxy, " cannot yield since no callback was passed.", args); + }, + + yieldTo: function (prop) { + this.yieldToOn.apply(this, [prop, null].concat(slice.call(arguments, 1))); + }, + + yieldToOn: function (prop, thisValue) { + var args = this.args; + for (var i = 0, l = args.length; i < l; ++i) { + if (args[i] && typeof args[i][prop] === "function") { + args[i][prop].apply(thisValue, slice.call(arguments, 2)); + return; + } + } + throwYieldError(this.proxy, " cannot yield to '" + prop + + "' since no callback was passed.", args); + }, + + getStackFrames: function () { + // Omit the error message and the two top stack frames in sinon itself: + return this.stack && this.stack.split("\n").slice(3); + }, + + toString: function () { + var callStr = this.proxy ? this.proxy.toString() + "(" : ""; + var args = []; + + if (!this.args) { + return ":("; + } + + for (var i = 0, l = this.args.length; i < l; ++i) { + args.push(sinon.format(this.args[i])); + } + + callStr = callStr + args.join(", ") + ")"; + + if (typeof this.returnValue !== "undefined") { + callStr += " => " + sinon.format(this.returnValue); + } + + if (this.exception) { + callStr += " !" + this.exception.name; + + if (this.exception.message) { + callStr += "(" + this.exception.message + ")"; + } + } + if (this.stack) { + callStr += this.getStackFrames()[0].replace(/^\s*(?:at\s+|@)?/, " at "); + + } + + return callStr; + } + }; + + callProto.invokeCallback = callProto.yield; + + function createSpyCall(spy, thisValue, args, returnValue, exception, id, stack) { + if (typeof id !== "number") { + throw new TypeError("Call id is not a number"); + } + var proxyCall = sinon.create(callProto); + proxyCall.proxy = spy; + proxyCall.thisValue = thisValue; + proxyCall.args = args; + proxyCall.returnValue = returnValue; + proxyCall.exception = exception; + proxyCall.callId = id; + proxyCall.stack = stack; + + return proxyCall; + } + createSpyCall.toString = callProto.toString; // used by mocks + + sinon.spyCall = createSpyCall; + return createSpyCall; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + require("./match"); + require("./format"); + module.exports = makeApi(sinon); + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +},{"./format":80,"./match":82,"./util/core":91}],78:[function(require,module,exports){ +/** + * @depend util/core.js + * @depend spy.js + * @depend stub.js + * @depend mock.js + */ +/** + * Collections of stubs, spies and mocks. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function (sinonGlobal) { + "use strict"; + + var push = [].push; + var hasOwnProperty = Object.prototype.hasOwnProperty; + + function getFakes(fakeCollection) { + if (!fakeCollection.fakes) { + fakeCollection.fakes = []; + } + + return fakeCollection.fakes; + } + + function each(fakeCollection, method) { + var fakes = getFakes(fakeCollection); + + for (var i = 0, l = fakes.length; i < l; i += 1) { + if (typeof fakes[i][method] === "function") { + fakes[i][method](); + } + } + } + + function compact(fakeCollection) { + var fakes = getFakes(fakeCollection); + var i = 0; + while (i < fakes.length) { + fakes.splice(i, 1); + } + } + + function makeApi(sinon) { + var collection = { + verify: function resolve() { + each(this, "verify"); + }, + + restore: function restore() { + each(this, "restore"); + compact(this); + }, + + reset: function restore() { + each(this, "reset"); + }, + + verifyAndRestore: function verifyAndRestore() { + var exception; + + try { + this.verify(); + } catch (e) { + exception = e; + } + + this.restore(); + + if (exception) { + throw exception; + } + }, + + add: function add(fake) { + push.call(getFakes(this), fake); + return fake; + }, + + spy: function spy() { + return this.add(sinon.spy.apply(sinon, arguments)); + }, + + stub: function stub(object, property, value) { + if (property) { + var original = object[property]; + + if (typeof original !== "function") { + if (!hasOwnProperty.call(object, property)) { + throw new TypeError("Cannot stub non-existent own property " + property); + } + + object[property] = value; + + return this.add({ + restore: function () { + object[property] = original; + } + }); + } + } + if (!property && !!object && typeof object === "object") { + var stubbedObj = sinon.stub.apply(sinon, arguments); + + for (var prop in stubbedObj) { + if (typeof stubbedObj[prop] === "function") { + this.add(stubbedObj[prop]); + } + } + + return stubbedObj; + } + + return this.add(sinon.stub.apply(sinon, arguments)); + }, + + mock: function mock() { + return this.add(sinon.mock.apply(sinon, arguments)); + }, + + inject: function inject(obj) { + var col = this; + + obj.spy = function () { + return col.spy.apply(col, arguments); + }; + + obj.stub = function () { + return col.stub.apply(col, arguments); + }; + + obj.mock = function () { + return col.mock.apply(col, arguments); + }; + + return obj; + } + }; + + sinon.collection = collection; + return collection; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + require("./mock"); + require("./spy"); + require("./stub"); + module.exports = makeApi(sinon); + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +},{"./mock":83,"./spy":85,"./stub":86,"./util/core":91}],79:[function(require,module,exports){ +/** + * @depend util/core.js + */ +(function (sinonGlobal) { + "use strict"; + + function makeApi(sinon) { + + // Adapted from https://developer.mozilla.org/en/docs/ECMAScript_DontEnum_attribute#JScript_DontEnum_Bug + var hasDontEnumBug = (function () { + var obj = { + constructor: function () { + return "0"; + }, + toString: function () { + return "1"; + }, + valueOf: function () { + return "2"; + }, + toLocaleString: function () { + return "3"; + }, + prototype: function () { + return "4"; + }, + isPrototypeOf: function () { + return "5"; + }, + propertyIsEnumerable: function () { + return "6"; + }, + hasOwnProperty: function () { + return "7"; + }, + length: function () { + return "8"; + }, + unique: function () { + return "9"; + } + }; + + var result = []; + for (var prop in obj) { + if (obj.hasOwnProperty(prop)) { + result.push(obj[prop]()); + } + } + return result.join("") !== "0123456789"; + })(); + + /* Public: Extend target in place with all (own) properties from sources in-order. Thus, last source will + * override properties in previous sources. + * + * target - The Object to extend + * sources - Objects to copy properties from. + * + * Returns the extended target + */ + function extend(target /*, sources */) { + var sources = Array.prototype.slice.call(arguments, 1); + var source, i, prop; + + for (i = 0; i < sources.length; i++) { + source = sources[i]; + + for (prop in source) { + if (source.hasOwnProperty(prop)) { + target[prop] = source[prop]; + } + } + + // Make sure we copy (own) toString method even when in JScript with DontEnum bug + // See https://developer.mozilla.org/en/docs/ECMAScript_DontEnum_attribute#JScript_DontEnum_Bug + if (hasDontEnumBug && source.hasOwnProperty("toString") && source.toString !== target.toString) { + target.toString = source.toString; + } + } + + return target; + } + + sinon.extend = extend; + return sinon.extend; + } + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + module.exports = makeApi(sinon); + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +},{"./util/core":91}],80:[function(require,module,exports){ +/** + * @depend util/core.js + */ +/** + * Format functions + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2014 Christian Johansen + */ +(function (sinonGlobal, formatio) { + "use strict"; + + function makeApi(sinon) { + function valueFormatter(value) { + return "" + value; + } + + function getFormatioFormatter() { + var formatter = formatio.configure({ + quoteStrings: false, + limitChildrenCount: 250 + }); + + function format() { + return formatter.ascii.apply(formatter, arguments); + } + + return format; + } + + function getNodeFormatter() { + try { + var util = require("util"); + } catch (e) { + /* Node, but no util module - would be very old, but better safe than sorry */ + } + + function format(v) { + var isObjectWithNativeToString = typeof v === "object" && v.toString === Object.prototype.toString; + return isObjectWithNativeToString ? util.inspect(v) : v; + } + + return util ? format : valueFormatter; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var formatter; + + if (isNode) { + try { + formatio = require("formatio"); + } + catch (e) {} // eslint-disable-line no-empty + } + + if (formatio) { + formatter = getFormatioFormatter(); + } else if (isNode) { + formatter = getNodeFormatter(); + } else { + formatter = valueFormatter; + } + + sinon.format = formatter; + return sinon.format; + } + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + module.exports = makeApi(sinon); + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon, // eslint-disable-line no-undef + typeof formatio === "object" && formatio // eslint-disable-line no-undef +)); + +},{"./util/core":91,"formatio":99,"util":34}],81:[function(require,module,exports){ +/** + * @depend util/core.js + */ +/** + * Logs errors + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2014 Christian Johansen + */ +(function (sinonGlobal) { + "use strict"; + + // cache a reference to setTimeout, so that our reference won't be stubbed out + // when using fake timers and errors will still get logged + // https://github.com/cjohansen/Sinon.JS/issues/381 + var realSetTimeout = setTimeout; + + function makeApi(sinon) { + + function log() {} + + function logError(label, err) { + var msg = label + " threw exception: "; + + function throwLoggedError() { + err.message = msg + err.message; + throw err; + } + + sinon.log(msg + "[" + err.name + "] " + err.message); + + if (err.stack) { + sinon.log(err.stack); + } + + if (logError.useImmediateExceptions) { + throwLoggedError(); + } else { + logError.setTimeout(throwLoggedError, 0); + } + } + + // When set to true, any errors logged will be thrown immediately; + // If set to false, the errors will be thrown in separate execution frame. + logError.useImmediateExceptions = false; + + // wrap realSetTimeout with something we can stub in tests + logError.setTimeout = function (func, timeout) { + realSetTimeout(func, timeout); + }; + + var exports = {}; + exports.log = sinon.log = log; + exports.logError = sinon.logError = logError; + + return exports; + } + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + module.exports = makeApi(sinon); + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +},{"./util/core":91}],82:[function(require,module,exports){ +/** + * @depend util/core.js + * @depend typeOf.js + */ +/*jslint eqeqeq: false, onevar: false, plusplus: false*/ +/*global module, require, sinon*/ +/** + * Match functions + * + * @author Maximilian Antoni (mail@maxantoni.de) + * @license BSD + * + * Copyright (c) 2012 Maximilian Antoni + */ +(function (sinonGlobal) { + "use strict"; + + function makeApi(sinon) { + function assertType(value, type, name) { + var actual = sinon.typeOf(value); + if (actual !== type) { + throw new TypeError("Expected type of " + name + " to be " + + type + ", but was " + actual); + } + } + + var matcher = { + toString: function () { + return this.message; + } + }; + + function isMatcher(object) { + return matcher.isPrototypeOf(object); + } + + function matchObject(expectation, actual) { + if (actual === null || actual === undefined) { + return false; + } + for (var key in expectation) { + if (expectation.hasOwnProperty(key)) { + var exp = expectation[key]; + var act = actual[key]; + if (isMatcher(exp)) { + if (!exp.test(act)) { + return false; + } + } else if (sinon.typeOf(exp) === "object") { + if (!matchObject(exp, act)) { + return false; + } + } else if (!sinon.deepEqual(exp, act)) { + return false; + } + } + } + return true; + } + + function match(expectation, message) { + var m = sinon.create(matcher); + var type = sinon.typeOf(expectation); + switch (type) { + case "object": + if (typeof expectation.test === "function") { + m.test = function (actual) { + return expectation.test(actual) === true; + }; + m.message = "match(" + sinon.functionName(expectation.test) + ")"; + return m; + } + var str = []; + for (var key in expectation) { + if (expectation.hasOwnProperty(key)) { + str.push(key + ": " + expectation[key]); + } + } + m.test = function (actual) { + return matchObject(expectation, actual); + }; + m.message = "match(" + str.join(", ") + ")"; + break; + case "number": + m.test = function (actual) { + // we need type coercion here + return expectation == actual; // eslint-disable-line eqeqeq + }; + break; + case "string": + m.test = function (actual) { + if (typeof actual !== "string") { + return false; + } + return actual.indexOf(expectation) !== -1; + }; + m.message = "match(\"" + expectation + "\")"; + break; + case "regexp": + m.test = function (actual) { + if (typeof actual !== "string") { + return false; + } + return expectation.test(actual); + }; + break; + case "function": + m.test = expectation; + if (message) { + m.message = message; + } else { + m.message = "match(" + sinon.functionName(expectation) + ")"; + } + break; + default: + m.test = function (actual) { + return sinon.deepEqual(expectation, actual); + }; + } + if (!m.message) { + m.message = "match(" + expectation + ")"; + } + return m; + } + + matcher.or = function (m2) { + if (!arguments.length) { + throw new TypeError("Matcher expected"); + } else if (!isMatcher(m2)) { + m2 = match(m2); + } + var m1 = this; + var or = sinon.create(matcher); + or.test = function (actual) { + return m1.test(actual) || m2.test(actual); + }; + or.message = m1.message + ".or(" + m2.message + ")"; + return or; + }; + + matcher.and = function (m2) { + if (!arguments.length) { + throw new TypeError("Matcher expected"); + } else if (!isMatcher(m2)) { + m2 = match(m2); + } + var m1 = this; + var and = sinon.create(matcher); + and.test = function (actual) { + return m1.test(actual) && m2.test(actual); + }; + and.message = m1.message + ".and(" + m2.message + ")"; + return and; + }; + + match.isMatcher = isMatcher; + + match.any = match(function () { + return true; + }, "any"); + + match.defined = match(function (actual) { + return actual !== null && actual !== undefined; + }, "defined"); + + match.truthy = match(function (actual) { + return !!actual; + }, "truthy"); + + match.falsy = match(function (actual) { + return !actual; + }, "falsy"); + + match.same = function (expectation) { + return match(function (actual) { + return expectation === actual; + }, "same(" + expectation + ")"); + }; + + match.typeOf = function (type) { + assertType(type, "string", "type"); + return match(function (actual) { + return sinon.typeOf(actual) === type; + }, "typeOf(\"" + type + "\")"); + }; + + match.instanceOf = function (type) { + assertType(type, "function", "type"); + return match(function (actual) { + return actual instanceof type; + }, "instanceOf(" + sinon.functionName(type) + ")"); + }; + + function createPropertyMatcher(propertyTest, messagePrefix) { + return function (property, value) { + assertType(property, "string", "property"); + var onlyProperty = arguments.length === 1; + var message = messagePrefix + "(\"" + property + "\""; + if (!onlyProperty) { + message += ", " + value; + } + message += ")"; + return match(function (actual) { + if (actual === undefined || actual === null || + !propertyTest(actual, property)) { + return false; + } + return onlyProperty || sinon.deepEqual(value, actual[property]); + }, message); + }; + } + + match.has = createPropertyMatcher(function (actual, property) { + if (typeof actual === "object") { + return property in actual; + } + return actual[property] !== undefined; + }, "has"); + + match.hasOwn = createPropertyMatcher(function (actual, property) { + return actual.hasOwnProperty(property); + }, "hasOwn"); + + match.bool = match.typeOf("boolean"); + match.number = match.typeOf("number"); + match.string = match.typeOf("string"); + match.object = match.typeOf("object"); + match.func = match.typeOf("function"); + match.array = match.typeOf("array"); + match.regexp = match.typeOf("regexp"); + match.date = match.typeOf("date"); + + sinon.match = match; + return match; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + require("./typeOf"); + module.exports = makeApi(sinon); + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +},{"./typeOf":90,"./util/core":91}],83:[function(require,module,exports){ +/** + * @depend times_in_words.js + * @depend util/core.js + * @depend call.js + * @depend extend.js + * @depend match.js + * @depend spy.js + * @depend stub.js + * @depend format.js + */ +/** + * Mock functions. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function (sinonGlobal) { + "use strict"; + + function makeApi(sinon) { + var push = [].push; + var match = sinon.match; + + function mock(object) { + // if (typeof console !== undefined && console.warn) { + // console.warn("mock will be removed from Sinon.JS v2.0"); + // } + + if (!object) { + return sinon.expectation.create("Anonymous mock"); + } + + return mock.create(object); + } + + function each(collection, callback) { + if (!collection) { + return; + } + + for (var i = 0, l = collection.length; i < l; i += 1) { + callback(collection[i]); + } + } + + function arrayEquals(arr1, arr2, compareLength) { + if (compareLength && (arr1.length !== arr2.length)) { + return false; + } + + for (var i = 0, l = arr1.length; i < l; i++) { + if (!sinon.deepEqual(arr1[i], arr2[i])) { + return false; + } + } + return true; + } + + sinon.extend(mock, { + create: function create(object) { + if (!object) { + throw new TypeError("object is null"); + } + + var mockObject = sinon.extend({}, mock); + mockObject.object = object; + delete mockObject.create; + + return mockObject; + }, + + expects: function expects(method) { + if (!method) { + throw new TypeError("method is falsy"); + } + + if (!this.expectations) { + this.expectations = {}; + this.proxies = []; + } + + if (!this.expectations[method]) { + this.expectations[method] = []; + var mockObject = this; + + sinon.wrapMethod(this.object, method, function () { + return mockObject.invokeMethod(method, this, arguments); + }); + + push.call(this.proxies, method); + } + + var expectation = sinon.expectation.create(method); + push.call(this.expectations[method], expectation); + + return expectation; + }, + + restore: function restore() { + var object = this.object; + + each(this.proxies, function (proxy) { + if (typeof object[proxy].restore === "function") { + object[proxy].restore(); + } + }); + }, + + verify: function verify() { + var expectations = this.expectations || {}; + var messages = []; + var met = []; + + each(this.proxies, function (proxy) { + each(expectations[proxy], function (expectation) { + if (!expectation.met()) { + push.call(messages, expectation.toString()); + } else { + push.call(met, expectation.toString()); + } + }); + }); + + this.restore(); + + if (messages.length > 0) { + sinon.expectation.fail(messages.concat(met).join("\n")); + } else if (met.length > 0) { + sinon.expectation.pass(messages.concat(met).join("\n")); + } + + return true; + }, + + invokeMethod: function invokeMethod(method, thisValue, args) { + var expectations = this.expectations && this.expectations[method] ? this.expectations[method] : []; + var expectationsWithMatchingArgs = []; + var currentArgs = args || []; + var i, available; + + for (i = 0; i < expectations.length; i += 1) { + var expectedArgs = expectations[i].expectedArguments || []; + if (arrayEquals(expectedArgs, currentArgs, expectations[i].expectsExactArgCount)) { + expectationsWithMatchingArgs.push(expectations[i]); + } + } + + for (i = 0; i < expectationsWithMatchingArgs.length; i += 1) { + if (!expectationsWithMatchingArgs[i].met() && + expectationsWithMatchingArgs[i].allowsCall(thisValue, args)) { + return expectationsWithMatchingArgs[i].apply(thisValue, args); + } + } + + var messages = []; + var exhausted = 0; + + for (i = 0; i < expectationsWithMatchingArgs.length; i += 1) { + if (expectationsWithMatchingArgs[i].allowsCall(thisValue, args)) { + available = available || expectationsWithMatchingArgs[i]; + } else { + exhausted += 1; + } + } + + if (available && exhausted === 0) { + return available.apply(thisValue, args); + } + + for (i = 0; i < expectations.length; i += 1) { + push.call(messages, " " + expectations[i].toString()); + } + + messages.unshift("Unexpected call: " + sinon.spyCall.toString.call({ + proxy: method, + args: args + })); + + sinon.expectation.fail(messages.join("\n")); + } + }); + + var times = sinon.timesInWords; + var slice = Array.prototype.slice; + + function callCountInWords(callCount) { + if (callCount === 0) { + return "never called"; + } + + return "called " + times(callCount); + } + + function expectedCallCountInWords(expectation) { + var min = expectation.minCalls; + var max = expectation.maxCalls; + + if (typeof min === "number" && typeof max === "number") { + var str = times(min); + + if (min !== max) { + str = "at least " + str + " and at most " + times(max); + } + + return str; + } + + if (typeof min === "number") { + return "at least " + times(min); + } + + return "at most " + times(max); + } + + function receivedMinCalls(expectation) { + var hasMinLimit = typeof expectation.minCalls === "number"; + return !hasMinLimit || expectation.callCount >= expectation.minCalls; + } + + function receivedMaxCalls(expectation) { + if (typeof expectation.maxCalls !== "number") { + return false; + } + + return expectation.callCount === expectation.maxCalls; + } + + function verifyMatcher(possibleMatcher, arg) { + var isMatcher = match && match.isMatcher(possibleMatcher); + + return isMatcher && possibleMatcher.test(arg) || true; + } + + sinon.expectation = { + minCalls: 1, + maxCalls: 1, + + create: function create(methodName) { + var expectation = sinon.extend(sinon.stub.create(), sinon.expectation); + delete expectation.create; + expectation.method = methodName; + + return expectation; + }, + + invoke: function invoke(func, thisValue, args) { + this.verifyCallAllowed(thisValue, args); + + return sinon.spy.invoke.apply(this, arguments); + }, + + atLeast: function atLeast(num) { + if (typeof num !== "number") { + throw new TypeError("'" + num + "' is not number"); + } + + if (!this.limitsSet) { + this.maxCalls = null; + this.limitsSet = true; + } + + this.minCalls = num; + + return this; + }, + + atMost: function atMost(num) { + if (typeof num !== "number") { + throw new TypeError("'" + num + "' is not number"); + } + + if (!this.limitsSet) { + this.minCalls = null; + this.limitsSet = true; + } + + this.maxCalls = num; + + return this; + }, + + never: function never() { + return this.exactly(0); + }, + + once: function once() { + return this.exactly(1); + }, + + twice: function twice() { + return this.exactly(2); + }, + + thrice: function thrice() { + return this.exactly(3); + }, + + exactly: function exactly(num) { + if (typeof num !== "number") { + throw new TypeError("'" + num + "' is not a number"); + } + + this.atLeast(num); + return this.atMost(num); + }, + + met: function met() { + return !this.failed && receivedMinCalls(this); + }, + + verifyCallAllowed: function verifyCallAllowed(thisValue, args) { + if (receivedMaxCalls(this)) { + this.failed = true; + sinon.expectation.fail(this.method + " already called " + times(this.maxCalls)); + } + + if ("expectedThis" in this && this.expectedThis !== thisValue) { + sinon.expectation.fail(this.method + " called with " + thisValue + " as thisValue, expected " + + this.expectedThis); + } + + if (!("expectedArguments" in this)) { + return; + } + + if (!args) { + sinon.expectation.fail(this.method + " received no arguments, expected " + + sinon.format(this.expectedArguments)); + } + + if (args.length < this.expectedArguments.length) { + sinon.expectation.fail(this.method + " received too few arguments (" + sinon.format(args) + + "), expected " + sinon.format(this.expectedArguments)); + } + + if (this.expectsExactArgCount && + args.length !== this.expectedArguments.length) { + sinon.expectation.fail(this.method + " received too many arguments (" + sinon.format(args) + + "), expected " + sinon.format(this.expectedArguments)); + } + + for (var i = 0, l = this.expectedArguments.length; i < l; i += 1) { + + if (!verifyMatcher(this.expectedArguments[i], args[i])) { + sinon.expectation.fail(this.method + " received wrong arguments " + sinon.format(args) + + ", didn't match " + this.expectedArguments.toString()); + } + + if (!sinon.deepEqual(this.expectedArguments[i], args[i])) { + sinon.expectation.fail(this.method + " received wrong arguments " + sinon.format(args) + + ", expected " + sinon.format(this.expectedArguments)); + } + } + }, + + allowsCall: function allowsCall(thisValue, args) { + if (this.met() && receivedMaxCalls(this)) { + return false; + } + + if ("expectedThis" in this && this.expectedThis !== thisValue) { + return false; + } + + if (!("expectedArguments" in this)) { + return true; + } + + args = args || []; + + if (args.length < this.expectedArguments.length) { + return false; + } + + if (this.expectsExactArgCount && + args.length !== this.expectedArguments.length) { + return false; + } + + for (var i = 0, l = this.expectedArguments.length; i < l; i += 1) { + if (!verifyMatcher(this.expectedArguments[i], args[i])) { + return false; + } + + if (!sinon.deepEqual(this.expectedArguments[i], args[i])) { + return false; + } + } + + return true; + }, + + withArgs: function withArgs() { + this.expectedArguments = slice.call(arguments); + return this; + }, + + withExactArgs: function withExactArgs() { + this.withArgs.apply(this, arguments); + this.expectsExactArgCount = true; + return this; + }, + + on: function on(thisValue) { + this.expectedThis = thisValue; + return this; + }, + + toString: function () { + var args = (this.expectedArguments || []).slice(); + + if (!this.expectsExactArgCount) { + push.call(args, "[...]"); + } + + var callStr = sinon.spyCall.toString.call({ + proxy: this.method || "anonymous mock expectation", + args: args + }); + + var message = callStr.replace(", [...", "[, ...") + " " + + expectedCallCountInWords(this); + + if (this.met()) { + return "Expectation met: " + message; + } + + return "Expected " + message + " (" + + callCountInWords(this.callCount) + ")"; + }, + + verify: function verify() { + if (!this.met()) { + sinon.expectation.fail(this.toString()); + } else { + sinon.expectation.pass(this.toString()); + } + + return true; + }, + + pass: function pass(message) { + sinon.assert.pass(message); + }, + + fail: function fail(message) { + var exception = new Error(message); + exception.name = "ExpectationError"; + + throw exception; + } + }; + + sinon.mock = mock; + return mock; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + require("./times_in_words"); + require("./call"); + require("./extend"); + require("./match"); + require("./spy"); + require("./stub"); + require("./format"); + + module.exports = makeApi(sinon); + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +},{"./call":77,"./extend":79,"./format":80,"./match":82,"./spy":85,"./stub":86,"./times_in_words":89,"./util/core":91}],84:[function(require,module,exports){ +/** + * @depend util/core.js + * @depend extend.js + * @depend collection.js + * @depend util/fake_timers.js + * @depend util/fake_server_with_clock.js + */ +/** + * Manages fake collections as well as fake utilities such as Sinon's + * timers and fake XHR implementation in one convenient object. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function (sinonGlobal) { + "use strict"; + + function makeApi(sinon) { + var push = [].push; + + function exposeValue(sandbox, config, key, value) { + if (!value) { + return; + } + + if (config.injectInto && !(key in config.injectInto)) { + config.injectInto[key] = value; + sandbox.injectedKeys.push(key); + } else { + push.call(sandbox.args, value); + } + } + + function prepareSandboxFromConfig(config) { + var sandbox = sinon.create(sinon.sandbox); + + if (config.useFakeServer) { + if (typeof config.useFakeServer === "object") { + sandbox.serverPrototype = config.useFakeServer; + } + + sandbox.useFakeServer(); + } + + if (config.useFakeTimers) { + if (typeof config.useFakeTimers === "object") { + sandbox.useFakeTimers.apply(sandbox, config.useFakeTimers); + } else { + sandbox.useFakeTimers(); + } + } + + return sandbox; + } + + sinon.sandbox = sinon.extend(sinon.create(sinon.collection), { + useFakeTimers: function useFakeTimers() { + this.clock = sinon.useFakeTimers.apply(sinon, arguments); + + return this.add(this.clock); + }, + + serverPrototype: sinon.fakeServer, + + useFakeServer: function useFakeServer() { + var proto = this.serverPrototype || sinon.fakeServer; + + if (!proto || !proto.create) { + return null; + } + + this.server = proto.create(); + return this.add(this.server); + }, + + inject: function (obj) { + sinon.collection.inject.call(this, obj); + + if (this.clock) { + obj.clock = this.clock; + } + + if (this.server) { + obj.server = this.server; + obj.requests = this.server.requests; + } + + obj.match = sinon.match; + + return obj; + }, + + restore: function () { + if (arguments.length) { + throw new Error("sandbox.restore() does not take any parameters. Perhaps you meant stub.restore()"); + } + + sinon.collection.restore.apply(this, arguments); + this.restoreContext(); + }, + + restoreContext: function () { + if (this.injectedKeys) { + for (var i = 0, j = this.injectedKeys.length; i < j; i++) { + delete this.injectInto[this.injectedKeys[i]]; + } + this.injectedKeys = []; + } + }, + + create: function (config) { + if (!config) { + return sinon.create(sinon.sandbox); + } + + var sandbox = prepareSandboxFromConfig(config); + sandbox.args = sandbox.args || []; + sandbox.injectedKeys = []; + sandbox.injectInto = config.injectInto; + var prop, + value; + var exposed = sandbox.inject({}); + + if (config.properties) { + for (var i = 0, l = config.properties.length; i < l; i++) { + prop = config.properties[i]; + value = exposed[prop] || prop === "sandbox" && sandbox; + exposeValue(sandbox, config, prop, value); + } + } else { + exposeValue(sandbox, config, "sandbox", value); + } + + return sandbox; + }, + + match: sinon.match + }); + + sinon.sandbox.useFakeXMLHttpRequest = sinon.sandbox.useFakeServer; + + return sinon.sandbox; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + require("./extend"); + require("./util/fake_server_with_clock"); + require("./util/fake_timers"); + require("./collection"); + module.exports = makeApi(sinon); + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +},{"./collection":78,"./extend":79,"./util/core":91,"./util/fake_server_with_clock":94,"./util/fake_timers":95}],85:[function(require,module,exports){ +/** + * @depend times_in_words.js + * @depend util/core.js + * @depend extend.js + * @depend call.js + * @depend format.js + */ +/** + * Spy functions + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function (sinonGlobal) { + "use strict"; + + function makeApi(sinon) { + var push = Array.prototype.push; + var slice = Array.prototype.slice; + var callId = 0; + + function spy(object, property, types) { + if (!property && typeof object === "function") { + return spy.create(object); + } + + if (!object && !property) { + return spy.create(function () { }); + } + + if (types) { + // A new descriptor is needed here because we can only wrap functions + // By passing the original descriptor we would end up trying to spy non-function properties + var descriptor = {}; + var methodDesc = sinon.getPropertyDescriptor(object, property); + + for (var i = 0; i < types.length; i++) { + descriptor[types[i]] = spy.create(methodDesc[types[i]]); + } + return sinon.wrapMethod(object, property, descriptor); + } + + return sinon.wrapMethod(object, property, spy.create(object[property])); + } + + function matchingFake(fakes, args, strict) { + if (!fakes) { + return undefined; + } + + for (var i = 0, l = fakes.length; i < l; i++) { + if (fakes[i].matches(args, strict)) { + return fakes[i]; + } + } + } + + function incrementCallCount() { + this.called = true; + this.callCount += 1; + this.notCalled = false; + this.calledOnce = this.callCount === 1; + this.calledTwice = this.callCount === 2; + this.calledThrice = this.callCount === 3; + } + + function createCallProperties() { + this.firstCall = this.getCall(0); + this.secondCall = this.getCall(1); + this.thirdCall = this.getCall(2); + this.lastCall = this.getCall(this.callCount - 1); + } + + var vars = "a,b,c,d,e,f,g,h,i,j,k,l"; + function createProxy(func, proxyLength) { + // Retain the function length: + var p; + if (proxyLength) { + eval("p = (function proxy(" + vars.substring(0, proxyLength * 2 - 1) + // eslint-disable-line no-eval + ") { return p.invoke(func, this, slice.call(arguments)); });"); + } else { + p = function proxy() { + return p.invoke(func, this, slice.call(arguments)); + }; + } + p.isSinonProxy = true; + return p; + } + + var uuid = 0; + + // Public API + var spyApi = { + reset: function () { + if (this.invoking) { + var err = new Error("Cannot reset Sinon function while invoking it. " + + "Move the call to .reset outside of the callback."); + err.name = "InvalidResetException"; + throw err; + } + + this.called = false; + this.notCalled = true; + this.calledOnce = false; + this.calledTwice = false; + this.calledThrice = false; + this.callCount = 0; + this.firstCall = null; + this.secondCall = null; + this.thirdCall = null; + this.lastCall = null; + this.args = []; + this.returnValues = []; + this.thisValues = []; + this.exceptions = []; + this.callIds = []; + this.stacks = []; + if (this.fakes) { + for (var i = 0; i < this.fakes.length; i++) { + this.fakes[i].reset(); + } + } + + return this; + }, + + create: function create(func, spyLength) { + var name; + + if (typeof func !== "function") { + func = function () { }; + } else { + name = sinon.functionName(func); + } + + if (!spyLength) { + spyLength = func.length; + } + + var proxy = createProxy(func, spyLength); + + sinon.extend(proxy, spy); + delete proxy.create; + sinon.extend(proxy, func); + + proxy.reset(); + proxy.prototype = func.prototype; + proxy.displayName = name || "spy"; + proxy.toString = sinon.functionToString; + proxy.instantiateFake = sinon.spy.create; + proxy.id = "spy#" + uuid++; + + return proxy; + }, + + invoke: function invoke(func, thisValue, args) { + var matching = matchingFake(this.fakes, args); + var exception, returnValue; + + incrementCallCount.call(this); + push.call(this.thisValues, thisValue); + push.call(this.args, args); + push.call(this.callIds, callId++); + + // Make call properties available from within the spied function: + createCallProperties.call(this); + + try { + this.invoking = true; + + if (matching) { + returnValue = matching.invoke(func, thisValue, args); + } else { + returnValue = (this.func || func).apply(thisValue, args); + } + + var thisCall = this.getCall(this.callCount - 1); + if (thisCall.calledWithNew() && typeof returnValue !== "object") { + returnValue = thisValue; + } + } catch (e) { + exception = e; + } finally { + delete this.invoking; + } + + push.call(this.exceptions, exception); + push.call(this.returnValues, returnValue); + push.call(this.stacks, new Error().stack); + + // Make return value and exception available in the calls: + createCallProperties.call(this); + + if (exception !== undefined) { + throw exception; + } + + return returnValue; + }, + + named: function named(name) { + this.displayName = name; + return this; + }, + + getCall: function getCall(i) { + if (i < 0 || i >= this.callCount) { + return null; + } + + return sinon.spyCall(this, this.thisValues[i], this.args[i], + this.returnValues[i], this.exceptions[i], + this.callIds[i], this.stacks[i]); + }, + + getCalls: function () { + var calls = []; + var i; + + for (i = 0; i < this.callCount; i++) { + calls.push(this.getCall(i)); + } + + return calls; + }, + + calledBefore: function calledBefore(spyFn) { + if (!this.called) { + return false; + } + + if (!spyFn.called) { + return true; + } + + return this.callIds[0] < spyFn.callIds[spyFn.callIds.length - 1]; + }, + + calledAfter: function calledAfter(spyFn) { + if (!this.called || !spyFn.called) { + return false; + } + + return this.callIds[this.callCount - 1] > spyFn.callIds[spyFn.callCount - 1]; + }, + + withArgs: function () { + var args = slice.call(arguments); + + if (this.fakes) { + var match = matchingFake(this.fakes, args, true); + + if (match) { + return match; + } + } else { + this.fakes = []; + } + + var original = this; + var fake = this.instantiateFake(); + fake.matchingAguments = args; + fake.parent = this; + push.call(this.fakes, fake); + + fake.withArgs = function () { + return original.withArgs.apply(original, arguments); + }; + + for (var i = 0; i < this.args.length; i++) { + if (fake.matches(this.args[i])) { + incrementCallCount.call(fake); + push.call(fake.thisValues, this.thisValues[i]); + push.call(fake.args, this.args[i]); + push.call(fake.returnValues, this.returnValues[i]); + push.call(fake.exceptions, this.exceptions[i]); + push.call(fake.callIds, this.callIds[i]); + } + } + createCallProperties.call(fake); + + return fake; + }, + + matches: function (args, strict) { + var margs = this.matchingAguments; + + if (margs.length <= args.length && + sinon.deepEqual(margs, args.slice(0, margs.length))) { + return !strict || margs.length === args.length; + } + }, + + printf: function (format) { + var spyInstance = this; + var args = slice.call(arguments, 1); + var formatter; + + return (format || "").replace(/%(.)/g, function (match, specifyer) { + formatter = spyApi.formatters[specifyer]; + + if (typeof formatter === "function") { + return formatter.call(null, spyInstance, args); + } else if (!isNaN(parseInt(specifyer, 10))) { + return sinon.format(args[specifyer - 1]); + } + + return "%" + specifyer; + }); + } + }; + + function delegateToCalls(method, matchAny, actual, notCalled) { + spyApi[method] = function () { + if (!this.called) { + if (notCalled) { + return notCalled.apply(this, arguments); + } + return false; + } + + var currentCall; + var matches = 0; + + for (var i = 0, l = this.callCount; i < l; i += 1) { + currentCall = this.getCall(i); + + if (currentCall[actual || method].apply(currentCall, arguments)) { + matches += 1; + + if (matchAny) { + return true; + } + } + } + + return matches === this.callCount; + }; + } + + delegateToCalls("calledOn", true); + delegateToCalls("alwaysCalledOn", false, "calledOn"); + delegateToCalls("calledWith", true); + delegateToCalls("calledWithMatch", true); + delegateToCalls("alwaysCalledWith", false, "calledWith"); + delegateToCalls("alwaysCalledWithMatch", false, "calledWithMatch"); + delegateToCalls("calledWithExactly", true); + delegateToCalls("alwaysCalledWithExactly", false, "calledWithExactly"); + delegateToCalls("neverCalledWith", false, "notCalledWith", function () { + return true; + }); + delegateToCalls("neverCalledWithMatch", false, "notCalledWithMatch", function () { + return true; + }); + delegateToCalls("threw", true); + delegateToCalls("alwaysThrew", false, "threw"); + delegateToCalls("returned", true); + delegateToCalls("alwaysReturned", false, "returned"); + delegateToCalls("calledWithNew", true); + delegateToCalls("alwaysCalledWithNew", false, "calledWithNew"); + delegateToCalls("callArg", false, "callArgWith", function () { + throw new Error(this.toString() + " cannot call arg since it was not yet invoked."); + }); + spyApi.callArgWith = spyApi.callArg; + delegateToCalls("callArgOn", false, "callArgOnWith", function () { + throw new Error(this.toString() + " cannot call arg since it was not yet invoked."); + }); + spyApi.callArgOnWith = spyApi.callArgOn; + delegateToCalls("yield", false, "yield", function () { + throw new Error(this.toString() + " cannot yield since it was not yet invoked."); + }); + // "invokeCallback" is an alias for "yield" since "yield" is invalid in strict mode. + spyApi.invokeCallback = spyApi.yield; + delegateToCalls("yieldOn", false, "yieldOn", function () { + throw new Error(this.toString() + " cannot yield since it was not yet invoked."); + }); + delegateToCalls("yieldTo", false, "yieldTo", function (property) { + throw new Error(this.toString() + " cannot yield to '" + property + + "' since it was not yet invoked."); + }); + delegateToCalls("yieldToOn", false, "yieldToOn", function (property) { + throw new Error(this.toString() + " cannot yield to '" + property + + "' since it was not yet invoked."); + }); + + spyApi.formatters = { + c: function (spyInstance) { + return sinon.timesInWords(spyInstance.callCount); + }, + + n: function (spyInstance) { + return spyInstance.toString(); + }, + + C: function (spyInstance) { + var calls = []; + + for (var i = 0, l = spyInstance.callCount; i < l; ++i) { + var stringifiedCall = " " + spyInstance.getCall(i).toString(); + if (/\n/.test(calls[i - 1])) { + stringifiedCall = "\n" + stringifiedCall; + } + push.call(calls, stringifiedCall); + } + + return calls.length > 0 ? "\n" + calls.join("\n") : ""; + }, + + t: function (spyInstance) { + var objects = []; + + for (var i = 0, l = spyInstance.callCount; i < l; ++i) { + push.call(objects, sinon.format(spyInstance.thisValues[i])); + } + + return objects.join(", "); + }, + + "*": function (spyInstance, args) { + var formatted = []; + + for (var i = 0, l = args.length; i < l; ++i) { + push.call(formatted, sinon.format(args[i])); + } + + return formatted.join(", "); + } + }; + + sinon.extend(spy, spyApi); + + spy.spyCall = sinon.spyCall; + sinon.spy = spy; + + return spy; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var core = require("./util/core"); + require("./call"); + require("./extend"); + require("./times_in_words"); + require("./format"); + module.exports = makeApi(core); + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +},{"./call":77,"./extend":79,"./format":80,"./times_in_words":89,"./util/core":91}],86:[function(require,module,exports){ +/** + * @depend util/core.js + * @depend extend.js + * @depend spy.js + * @depend behavior.js + * @depend walk.js + */ +/** + * Stub functions + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function (sinonGlobal) { + "use strict"; + + function makeApi(sinon) { + function stub(object, property, func) { + if (!!func && typeof func !== "function" && typeof func !== "object") { + throw new TypeError("Custom stub should be a function or a property descriptor"); + } + + var wrapper; + + if (func) { + if (typeof func === "function") { + wrapper = sinon.spy && sinon.spy.create ? sinon.spy.create(func) : func; + } else { + wrapper = func; + if (sinon.spy && sinon.spy.create) { + var types = sinon.objectKeys(wrapper); + for (var i = 0; i < types.length; i++) { + wrapper[types[i]] = sinon.spy.create(wrapper[types[i]]); + } + } + } + } else { + var stubLength = 0; + if (typeof object === "object" && typeof object[property] === "function") { + stubLength = object[property].length; + } + wrapper = stub.create(stubLength); + } + + if (!object && typeof property === "undefined") { + return sinon.stub.create(); + } + + if (typeof property === "undefined" && typeof object === "object") { + sinon.walk(object || {}, function (value, prop, propOwner) { + // we don't want to stub things like toString(), valueOf(), etc. so we only stub if the object + // is not Object.prototype + if ( + propOwner !== Object.prototype && + prop !== "constructor" && + typeof sinon.getPropertyDescriptor(propOwner, prop).value === "function" + ) { + stub(object, prop); + } + }); + + return object; + } + + return sinon.wrapMethod(object, property, wrapper); + } + + + /*eslint-disable no-use-before-define*/ + function getParentBehaviour(stubInstance) { + return (stubInstance.parent && getCurrentBehavior(stubInstance.parent)); + } + + function getDefaultBehavior(stubInstance) { + return stubInstance.defaultBehavior || + getParentBehaviour(stubInstance) || + sinon.behavior.create(stubInstance); + } + + function getCurrentBehavior(stubInstance) { + var behavior = stubInstance.behaviors[stubInstance.callCount - 1]; + return behavior && behavior.isPresent() ? behavior : getDefaultBehavior(stubInstance); + } + /*eslint-enable no-use-before-define*/ + + var uuid = 0; + + var proto = { + create: function create(stubLength) { + var functionStub = function () { + return getCurrentBehavior(functionStub).invoke(this, arguments); + }; + + functionStub.id = "stub#" + uuid++; + var orig = functionStub; + functionStub = sinon.spy.create(functionStub, stubLength); + functionStub.func = orig; + + sinon.extend(functionStub, stub); + functionStub.instantiateFake = sinon.stub.create; + functionStub.displayName = "stub"; + functionStub.toString = sinon.functionToString; + + functionStub.defaultBehavior = null; + functionStub.behaviors = []; + + return functionStub; + }, + + resetBehavior: function () { + var i; + + this.defaultBehavior = null; + this.behaviors = []; + + delete this.returnValue; + delete this.returnArgAt; + this.returnThis = false; + + if (this.fakes) { + for (i = 0; i < this.fakes.length; i++) { + this.fakes[i].resetBehavior(); + } + } + }, + + onCall: function onCall(index) { + if (!this.behaviors[index]) { + this.behaviors[index] = sinon.behavior.create(this); + } + + return this.behaviors[index]; + }, + + onFirstCall: function onFirstCall() { + return this.onCall(0); + }, + + onSecondCall: function onSecondCall() { + return this.onCall(1); + }, + + onThirdCall: function onThirdCall() { + return this.onCall(2); + } + }; + + function createBehavior(behaviorMethod) { + return function () { + this.defaultBehavior = this.defaultBehavior || sinon.behavior.create(this); + this.defaultBehavior[behaviorMethod].apply(this.defaultBehavior, arguments); + return this; + }; + } + + for (var method in sinon.behavior) { + if (sinon.behavior.hasOwnProperty(method) && + !proto.hasOwnProperty(method) && + method !== "create" && + method !== "withArgs" && + method !== "invoke") { + proto[method] = createBehavior(method); + } + } + + sinon.extend(stub, proto); + sinon.stub = stub; + + return stub; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var core = require("./util/core"); + require("./behavior"); + require("./spy"); + require("./extend"); + module.exports = makeApi(core); + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +},{"./behavior":76,"./extend":79,"./spy":85,"./util/core":91}],87:[function(require,module,exports){ +/** + * @depend util/core.js + * @depend sandbox.js + */ +/** + * Test function, sandboxes fakes + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function (sinonGlobal) { + "use strict"; + + function makeApi(sinon) { + var slice = Array.prototype.slice; + + function test(callback) { + var type = typeof callback; + + if (type !== "function") { + throw new TypeError("sinon.test needs to wrap a test function, got " + type); + } + + function sinonSandboxedTest() { + var config = sinon.getConfig(sinon.config); + config.injectInto = config.injectIntoThis && this || config.injectInto; + var sandbox = sinon.sandbox.create(config); + var args = slice.call(arguments); + var oldDone = args.length && args[args.length - 1]; + var exception, result; + + if (typeof oldDone === "function") { + args[args.length - 1] = function sinonDone(res) { + if (res) { + sandbox.restore(); + } else { + sandbox.verifyAndRestore(); + } + oldDone(res); + }; + } + + try { + result = callback.apply(this, args.concat(sandbox.args)); + } catch (e) { + exception = e; + } + + if (typeof exception !== "undefined") { + sandbox.restore(); + throw exception; + } else if (typeof oldDone !== "function") { + sandbox.verifyAndRestore(); + } + + return result; + } + + if (callback.length) { + return function sinonAsyncSandboxedTest(done) { // eslint-disable-line no-unused-vars + return sinonSandboxedTest.apply(this, arguments); + }; + } + + return sinonSandboxedTest; + } + + test.config = { + injectIntoThis: true, + injectInto: null, + properties: ["spy", "stub", "mock", "clock", "server", "requests"], + useFakeTimers: true, + useFakeServer: true + }; + + sinon.test = test; + return test; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var core = require("./util/core"); + require("./sandbox"); + module.exports = makeApi(core); + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require, module.exports, module); + } else if (sinonGlobal) { + makeApi(sinonGlobal); + } +}(typeof sinon === "object" && sinon || null)); // eslint-disable-line no-undef + +},{"./sandbox":84,"./util/core":91}],88:[function(require,module,exports){ +/** + * @depend util/core.js + * @depend test.js + */ +/** + * Test case, sandboxes all test functions + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function (sinonGlobal) { + "use strict"; + + function createTest(property, setUp, tearDown) { + return function () { + if (setUp) { + setUp.apply(this, arguments); + } + + var exception, result; + + try { + result = property.apply(this, arguments); + } catch (e) { + exception = e; + } + + if (tearDown) { + tearDown.apply(this, arguments); + } + + if (exception) { + throw exception; + } + + return result; + }; + } + + function makeApi(sinon) { + function testCase(tests, prefix) { + if (!tests || typeof tests !== "object") { + throw new TypeError("sinon.testCase needs an object with test functions"); + } + + prefix = prefix || "test"; + var rPrefix = new RegExp("^" + prefix); + var methods = {}; + var setUp = tests.setUp; + var tearDown = tests.tearDown; + var testName, + property, + method; + + for (testName in tests) { + if (tests.hasOwnProperty(testName) && !/^(setUp|tearDown)$/.test(testName)) { + property = tests[testName]; + + if (typeof property === "function" && rPrefix.test(testName)) { + method = property; + + if (setUp || tearDown) { + method = createTest(property, setUp, tearDown); + } + + methods[testName] = sinon.test(method); + } else { + methods[testName] = tests[testName]; + } + } + } + + return methods; + } + + sinon.testCase = testCase; + return testCase; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var core = require("./util/core"); + require("./test"); + module.exports = makeApi(core); + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +},{"./test":87,"./util/core":91}],89:[function(require,module,exports){ +/** + * @depend util/core.js + */ +(function (sinonGlobal) { + "use strict"; + + function makeApi(sinon) { + + function timesInWords(count) { + switch (count) { + case 1: + return "once"; + case 2: + return "twice"; + case 3: + return "thrice"; + default: + return (count || 0) + " times"; + } + } + + sinon.timesInWords = timesInWords; + return sinon.timesInWords; + } + + function loadDependencies(require, exports, module) { + var core = require("./util/core"); + module.exports = makeApi(core); + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +},{"./util/core":91}],90:[function(require,module,exports){ +/** + * @depend util/core.js + */ +/** + * Format functions + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2014 Christian Johansen + */ +(function (sinonGlobal) { + "use strict"; + + function makeApi(sinon) { + function typeOf(value) { + if (value === null) { + return "null"; + } else if (value === undefined) { + return "undefined"; + } + var string = Object.prototype.toString.call(value); + return string.substring(8, string.length - 1).toLowerCase(); + } + + sinon.typeOf = typeOf; + return sinon.typeOf; + } + + function loadDependencies(require, exports, module) { + var core = require("./util/core"); + module.exports = makeApi(core); + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +},{"./util/core":91}],91:[function(require,module,exports){ +/** + * @depend ../../sinon.js + */ +/** + * Sinon core utilities. For internal use only. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function (sinonGlobal) { + "use strict"; + + var div = typeof document !== "undefined" && document.createElement("div"); + var hasOwn = Object.prototype.hasOwnProperty; + + function isDOMNode(obj) { + var success = false; + + try { + obj.appendChild(div); + success = div.parentNode === obj; + } catch (e) { + return false; + } finally { + try { + obj.removeChild(div); + } catch (e) { + // Remove failed, not much we can do about that + } + } + + return success; + } + + function isElement(obj) { + return div && obj && obj.nodeType === 1 && isDOMNode(obj); + } + + function isFunction(obj) { + return typeof obj === "function" || !!(obj && obj.constructor && obj.call && obj.apply); + } + + function isReallyNaN(val) { + return typeof val === "number" && isNaN(val); + } + + function mirrorProperties(target, source) { + for (var prop in source) { + if (!hasOwn.call(target, prop)) { + target[prop] = source[prop]; + } + } + } + + function isRestorable(obj) { + return typeof obj === "function" && typeof obj.restore === "function" && obj.restore.sinon; + } + + // Cheap way to detect if we have ES5 support. + var hasES5Support = "keys" in Object; + + function makeApi(sinon) { + sinon.wrapMethod = function wrapMethod(object, property, method) { + if (!object) { + throw new TypeError("Should wrap property of object"); + } + + if (typeof method !== "function" && typeof method !== "object") { + throw new TypeError("Method wrapper should be a function or a property descriptor"); + } + + function checkWrappedMethod(wrappedMethod) { + var error; + + if (!isFunction(wrappedMethod)) { + error = new TypeError("Attempted to wrap " + (typeof wrappedMethod) + " property " + + property + " as function"); + } else if (wrappedMethod.restore && wrappedMethod.restore.sinon) { + error = new TypeError("Attempted to wrap " + property + " which is already wrapped"); + } else if (wrappedMethod.calledBefore) { + var verb = wrappedMethod.returns ? "stubbed" : "spied on"; + error = new TypeError("Attempted to wrap " + property + " which is already " + verb); + } + + if (error) { + if (wrappedMethod && wrappedMethod.stackTrace) { + error.stack += "\n--------------\n" + wrappedMethod.stackTrace; + } + throw error; + } + } + + var error, wrappedMethod, i; + + function simplePropertyAssignment() { + wrappedMethod = object[property]; + checkWrappedMethod(wrappedMethod); + object[property] = method; + method.displayName = property; + } + + // IE 8 does not support hasOwnProperty on the window object and Firefox has a problem + // when using hasOwn.call on objects from other frames. + var owned = (object.hasOwnProperty && object.hasOwnProperty === hasOwn) ? + object.hasOwnProperty(property) : hasOwn.call(object, property); + + if (hasES5Support) { + var methodDesc = (typeof method === "function") ? {value: method} : method; + var wrappedMethodDesc = sinon.getPropertyDescriptor(object, property); + + if (!wrappedMethodDesc) { + error = new TypeError("Attempted to wrap " + (typeof wrappedMethod) + " property " + + property + " as function"); + } else if (wrappedMethodDesc.restore && wrappedMethodDesc.restore.sinon) { + error = new TypeError("Attempted to wrap " + property + " which is already wrapped"); + } + if (error) { + if (wrappedMethodDesc && wrappedMethodDesc.stackTrace) { + error.stack += "\n--------------\n" + wrappedMethodDesc.stackTrace; + } + throw error; + } + + var types = sinon.objectKeys(methodDesc); + for (i = 0; i < types.length; i++) { + wrappedMethod = wrappedMethodDesc[types[i]]; + checkWrappedMethod(wrappedMethod); + } + + mirrorProperties(methodDesc, wrappedMethodDesc); + for (i = 0; i < types.length; i++) { + mirrorProperties(methodDesc[types[i]], wrappedMethodDesc[types[i]]); + } + Object.defineProperty(object, property, methodDesc); + + // catch failing assignment + // this is the converse of the check in `.restore` below + if ( typeof method === "function" && object[property] !== method ) { + // correct any wrongdoings caused by the defineProperty call above, + // such as adding new items (if object was a Storage object) + delete object[property]; + simplePropertyAssignment(); + } + } else { + simplePropertyAssignment(); + } + + method.displayName = property; + + // Set up a stack trace which can be used later to find what line of + // code the original method was created on. + method.stackTrace = (new Error("Stack Trace for original")).stack; + + method.restore = function () { + // For prototype properties try to reset by delete first. + // If this fails (ex: localStorage on mobile safari) then force a reset + // via direct assignment. + if (!owned) { + // In some cases `delete` may throw an error + try { + delete object[property]; + } catch (e) {} // eslint-disable-line no-empty + // For native code functions `delete` fails without throwing an error + // on Chrome < 43, PhantomJS, etc. + } else if (hasES5Support) { + Object.defineProperty(object, property, wrappedMethodDesc); + } + + // this only supports ES5 getter/setter, for ES3.1 and lower + // __lookupSetter__ / __lookupGetter__ should be integrated + if (hasES5Support) { + var checkDesc = sinon.getPropertyDescriptor(object, property); + if (checkDesc.value === method) { + object[property] = wrappedMethod; + } + + // Use strict equality comparison to check failures then force a reset + // via direct assignment. + } else if (object[property] === method) { + object[property] = wrappedMethod; + } + }; + + method.restore.sinon = true; + + if (!hasES5Support) { + mirrorProperties(method, wrappedMethod); + } + + return method; + }; + + sinon.create = function create(proto) { + var F = function () {}; + F.prototype = proto; + return new F(); + }; + + sinon.deepEqual = function deepEqual(a, b) { + if (sinon.match && sinon.match.isMatcher(a)) { + return a.test(b); + } + + if (typeof a !== "object" || typeof b !== "object") { + return isReallyNaN(a) && isReallyNaN(b) || a === b; + } + + if (isElement(a) || isElement(b)) { + return a === b; + } + + if (a === b) { + return true; + } + + if ((a === null && b !== null) || (a !== null && b === null)) { + return false; + } + + if (a instanceof RegExp && b instanceof RegExp) { + return (a.source === b.source) && (a.global === b.global) && + (a.ignoreCase === b.ignoreCase) && (a.multiline === b.multiline); + } + + var aString = Object.prototype.toString.call(a); + if (aString !== Object.prototype.toString.call(b)) { + return false; + } + + if (aString === "[object Date]") { + return a.valueOf() === b.valueOf(); + } + + var prop; + var aLength = 0; + var bLength = 0; + + if (aString === "[object Array]" && a.length !== b.length) { + return false; + } + + for (prop in a) { + if (hasOwn.call(a, prop)) { + aLength += 1; + + if (!(prop in b)) { + return false; + } + + if (!deepEqual(a[prop], b[prop])) { + return false; + } + } + } + + for (prop in b) { + if (hasOwn.call(b, prop)) { + bLength += 1; + } + } + + return aLength === bLength; + }; + + sinon.functionName = function functionName(func) { + var name = func.displayName || func.name; + + // Use function decomposition as a last resort to get function + // name. Does not rely on function decomposition to work - if it + // doesn't debugging will be slightly less informative + // (i.e. toString will say 'spy' rather than 'myFunc'). + if (!name) { + var matches = func.toString().match(/function ([^\s\(]+)/); + name = matches && matches[1]; + } + + return name; + }; + + sinon.functionToString = function toString() { + if (this.getCall && this.callCount) { + var thisValue, + prop; + var i = this.callCount; + + while (i--) { + thisValue = this.getCall(i).thisValue; + + for (prop in thisValue) { + if (thisValue[prop] === this) { + return prop; + } + } + } + } + + return this.displayName || "sinon fake"; + }; + + sinon.objectKeys = function objectKeys(obj) { + if (obj !== Object(obj)) { + throw new TypeError("sinon.objectKeys called on a non-object"); + } + + var keys = []; + var key; + for (key in obj) { + if (hasOwn.call(obj, key)) { + keys.push(key); + } + } + + return keys; + }; + + sinon.getPropertyDescriptor = function getPropertyDescriptor(object, property) { + var proto = object; + var descriptor; + + while (proto && !(descriptor = Object.getOwnPropertyDescriptor(proto, property))) { + proto = Object.getPrototypeOf(proto); + } + return descriptor; + }; + + sinon.getConfig = function (custom) { + var config = {}; + custom = custom || {}; + var defaults = sinon.defaultConfig; + + for (var prop in defaults) { + if (defaults.hasOwnProperty(prop)) { + config[prop] = custom.hasOwnProperty(prop) ? custom[prop] : defaults[prop]; + } + } + + return config; + }; + + sinon.defaultConfig = { + injectIntoThis: true, + injectInto: null, + properties: ["spy", "stub", "mock", "clock", "server", "requests"], + useFakeTimers: true, + useFakeServer: true + }; + + sinon.timesInWords = function timesInWords(count) { + return count === 1 && "once" || + count === 2 && "twice" || + count === 3 && "thrice" || + (count || 0) + " times"; + }; + + sinon.calledInOrder = function (spies) { + for (var i = 1, l = spies.length; i < l; i++) { + if (!spies[i - 1].calledBefore(spies[i]) || !spies[i].called) { + return false; + } + } + + return true; + }; + + sinon.orderByFirstCall = function (spies) { + return spies.sort(function (a, b) { + // uuid, won't ever be equal + var aCall = a.getCall(0); + var bCall = b.getCall(0); + var aId = aCall && aCall.callId || -1; + var bId = bCall && bCall.callId || -1; + + return aId < bId ? -1 : 1; + }); + }; + + sinon.createStubInstance = function (constructor) { + if (typeof constructor !== "function") { + throw new TypeError("The constructor should be a function."); + } + return sinon.stub(sinon.create(constructor.prototype)); + }; + + sinon.restore = function (object) { + if (object !== null && typeof object === "object") { + for (var prop in object) { + if (isRestorable(object[prop])) { + object[prop].restore(); + } + } + } else if (isRestorable(object)) { + object.restore(); + } + }; + + return sinon; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports) { + makeApi(exports); + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +},{}],92:[function(require,module,exports){ +/** + * Minimal Event interface implementation + * + * Original implementation by Sven Fuchs: https://gist.github.com/995028 + * Modifications and tests by Christian Johansen. + * + * @author Sven Fuchs (svenfuchs@artweb-design.de) + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2011 Sven Fuchs, Christian Johansen + */ +if (typeof sinon === "undefined") { + this.sinon = {}; +} + +(function () { + "use strict"; + + var push = [].push; + + function makeApi(sinon) { + sinon.Event = function Event(type, bubbles, cancelable, target) { + this.initEvent(type, bubbles, cancelable, target); + }; + + sinon.Event.prototype = { + initEvent: function (type, bubbles, cancelable, target) { + this.type = type; + this.bubbles = bubbles; + this.cancelable = cancelable; + this.target = target; + }, + + stopPropagation: function () {}, + + preventDefault: function () { + this.defaultPrevented = true; + } + }; + + sinon.ProgressEvent = function ProgressEvent(type, progressEventRaw, target) { + this.initEvent(type, false, false, target); + this.loaded = typeof progressEventRaw.loaded === "number" ? progressEventRaw.loaded : null; + this.total = typeof progressEventRaw.total === "number" ? progressEventRaw.total : null; + this.lengthComputable = !!progressEventRaw.total; + }; + + sinon.ProgressEvent.prototype = new sinon.Event(); + + sinon.ProgressEvent.prototype.constructor = sinon.ProgressEvent; + + sinon.CustomEvent = function CustomEvent(type, customData, target) { + this.initEvent(type, false, false, target); + this.detail = customData.detail || null; + }; + + sinon.CustomEvent.prototype = new sinon.Event(); + + sinon.CustomEvent.prototype.constructor = sinon.CustomEvent; + + sinon.EventTarget = { + addEventListener: function addEventListener(event, listener) { + this.eventListeners = this.eventListeners || {}; + this.eventListeners[event] = this.eventListeners[event] || []; + push.call(this.eventListeners[event], listener); + }, + + removeEventListener: function removeEventListener(event, listener) { + var listeners = this.eventListeners && this.eventListeners[event] || []; + + for (var i = 0, l = listeners.length; i < l; ++i) { + if (listeners[i] === listener) { + return listeners.splice(i, 1); + } + } + }, + + dispatchEvent: function dispatchEvent(event) { + var type = event.type; + var listeners = this.eventListeners && this.eventListeners[type] || []; + + for (var i = 0; i < listeners.length; i++) { + if (typeof listeners[i] === "function") { + listeners[i].call(this, event); + } else { + listeners[i].handleEvent(event); + } + } + + return !!event.defaultPrevented; + } + }; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require) { + var sinon = require("./core"); + makeApi(sinon); + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require); + } else { + makeApi(sinon); // eslint-disable-line no-undef + } +}()); + +},{"./core":91}],93:[function(require,module,exports){ +/** + * @depend fake_xdomain_request.js + * @depend fake_xml_http_request.js + * @depend ../format.js + * @depend ../log_error.js + */ +/** + * The Sinon "server" mimics a web server that receives requests from + * sinon.FakeXMLHttpRequest and provides an API to respond to those requests, + * both synchronously and asynchronously. To respond synchronuously, canned + * answers have to be provided upfront. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function () { + "use strict"; + + var push = [].push; + + function responseArray(handler) { + var response = handler; + + if (Object.prototype.toString.call(handler) !== "[object Array]") { + response = [200, {}, handler]; + } + + if (typeof response[2] !== "string") { + throw new TypeError("Fake server response body should be string, but was " + + typeof response[2]); + } + + return response; + } + + var wloc = typeof window !== "undefined" ? window.location : {}; + var rCurrLoc = new RegExp("^" + wloc.protocol + "//" + wloc.host); + + function matchOne(response, reqMethod, reqUrl) { + var rmeth = response.method; + var matchMethod = !rmeth || rmeth.toLowerCase() === reqMethod.toLowerCase(); + var url = response.url; + var matchUrl = !url || url === reqUrl || (typeof url.test === "function" && url.test(reqUrl)); + + return matchMethod && matchUrl; + } + + function match(response, request) { + var requestUrl = request.url; + + if (!/^https?:\/\//.test(requestUrl) || rCurrLoc.test(requestUrl)) { + requestUrl = requestUrl.replace(rCurrLoc, ""); + } + + if (matchOne(response, this.getHTTPMethod(request), requestUrl)) { + if (typeof response.response === "function") { + var ru = response.url; + var args = [request].concat(ru && typeof ru.exec === "function" ? ru.exec(requestUrl).slice(1) : []); + return response.response.apply(response, args); + } + + return true; + } + + return false; + } + + function makeApi(sinon) { + sinon.fakeServer = { + create: function (config) { + var server = sinon.create(this); + server.configure(config); + if (!sinon.xhr.supportsCORS) { + this.xhr = sinon.useFakeXDomainRequest(); + } else { + this.xhr = sinon.useFakeXMLHttpRequest(); + } + server.requests = []; + + this.xhr.onCreate = function (xhrObj) { + server.addRequest(xhrObj); + }; + + return server; + }, + configure: function (config) { + var whitelist = { + "autoRespond": true, + "autoRespondAfter": true, + "respondImmediately": true, + "fakeHTTPMethods": true + }; + var setting; + + config = config || {}; + for (setting in config) { + if (whitelist.hasOwnProperty(setting) && config.hasOwnProperty(setting)) { + this[setting] = config[setting]; + } + } + }, + addRequest: function addRequest(xhrObj) { + var server = this; + push.call(this.requests, xhrObj); + + xhrObj.onSend = function () { + server.handleRequest(this); + + if (server.respondImmediately) { + server.respond(); + } else if (server.autoRespond && !server.responding) { + setTimeout(function () { + server.responding = false; + server.respond(); + }, server.autoRespondAfter || 10); + + server.responding = true; + } + }; + }, + + getHTTPMethod: function getHTTPMethod(request) { + if (this.fakeHTTPMethods && /post/i.test(request.method)) { + var matches = (request.requestBody || "").match(/_method=([^\b;]+)/); + return matches ? matches[1] : request.method; + } + + return request.method; + }, + + handleRequest: function handleRequest(xhr) { + if (xhr.async) { + if (!this.queue) { + this.queue = []; + } + + push.call(this.queue, xhr); + } else { + this.processRequest(xhr); + } + }, + + log: function log(response, request) { + var str; + + str = "Request:\n" + sinon.format(request) + "\n\n"; + str += "Response:\n" + sinon.format(response) + "\n\n"; + + sinon.log(str); + }, + + respondWith: function respondWith(method, url, body) { + if (arguments.length === 1 && typeof method !== "function") { + this.response = responseArray(method); + return; + } + + if (!this.responses) { + this.responses = []; + } + + if (arguments.length === 1) { + body = method; + url = method = null; + } + + if (arguments.length === 2) { + body = url; + url = method; + method = null; + } + + push.call(this.responses, { + method: method, + url: url, + response: typeof body === "function" ? body : responseArray(body) + }); + }, + + respond: function respond() { + if (arguments.length > 0) { + this.respondWith.apply(this, arguments); + } + + var queue = this.queue || []; + var requests = queue.splice(0, queue.length); + + for (var i = 0; i < requests.length; i++) { + this.processRequest(requests[i]); + } + }, + + processRequest: function processRequest(request) { + try { + if (request.aborted) { + return; + } + + var response = this.response || [404, {}, ""]; + + if (this.responses) { + for (var l = this.responses.length, i = l - 1; i >= 0; i--) { + if (match.call(this, this.responses[i], request)) { + response = this.responses[i].response; + break; + } + } + } + + if (request.readyState !== 4) { + this.log(response, request); + + request.respond(response[0], response[1], response[2]); + } + } catch (e) { + sinon.logError("Fake server request processing", e); + } + }, + + restore: function restore() { + return this.xhr.restore && this.xhr.restore.apply(this.xhr, arguments); + } + }; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./core"); + require("./fake_xdomain_request"); + require("./fake_xml_http_request"); + require("../format"); + makeApi(sinon); + module.exports = sinon; + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require, module.exports, module); + } else { + makeApi(sinon); // eslint-disable-line no-undef + } +}()); + +},{"../format":80,"./core":91,"./fake_xdomain_request":96,"./fake_xml_http_request":97}],94:[function(require,module,exports){ +/** + * @depend fake_server.js + * @depend fake_timers.js + */ +/** + * Add-on for sinon.fakeServer that automatically handles a fake timer along with + * the FakeXMLHttpRequest. The direct inspiration for this add-on is jQuery + * 1.3.x, which does not use xhr object's onreadystatehandler at all - instead, + * it polls the object for completion with setInterval. Dispite the direct + * motivation, there is nothing jQuery-specific in this file, so it can be used + * in any environment where the ajax implementation depends on setInterval or + * setTimeout. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function () { + "use strict"; + + function makeApi(sinon) { + function Server() {} + Server.prototype = sinon.fakeServer; + + sinon.fakeServerWithClock = new Server(); + + sinon.fakeServerWithClock.addRequest = function addRequest(xhr) { + if (xhr.async) { + if (typeof setTimeout.clock === "object") { + this.clock = setTimeout.clock; + } else { + this.clock = sinon.useFakeTimers(); + this.resetClock = true; + } + + if (!this.longestTimeout) { + var clockSetTimeout = this.clock.setTimeout; + var clockSetInterval = this.clock.setInterval; + var server = this; + + this.clock.setTimeout = function (fn, timeout) { + server.longestTimeout = Math.max(timeout, server.longestTimeout || 0); + + return clockSetTimeout.apply(this, arguments); + }; + + this.clock.setInterval = function (fn, timeout) { + server.longestTimeout = Math.max(timeout, server.longestTimeout || 0); + + return clockSetInterval.apply(this, arguments); + }; + } + } + + return sinon.fakeServer.addRequest.call(this, xhr); + }; + + sinon.fakeServerWithClock.respond = function respond() { + var returnVal = sinon.fakeServer.respond.apply(this, arguments); + + if (this.clock) { + this.clock.tick(this.longestTimeout || 0); + this.longestTimeout = 0; + + if (this.resetClock) { + this.clock.restore(); + this.resetClock = false; + } + } + + return returnVal; + }; + + sinon.fakeServerWithClock.restore = function restore() { + if (this.clock) { + this.clock.restore(); + } + + return sinon.fakeServer.restore.apply(this, arguments); + }; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require) { + var sinon = require("./core"); + require("./fake_server"); + require("./fake_timers"); + makeApi(sinon); + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require); + } else { + makeApi(sinon); // eslint-disable-line no-undef + } +}()); + +},{"./core":91,"./fake_server":93,"./fake_timers":95}],95:[function(require,module,exports){ +/** + * Fake timer API + * setTimeout + * setInterval + * clearTimeout + * clearInterval + * tick + * reset + * Date + * + * Inspired by jsUnitMockTimeOut from JsUnit + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function () { + "use strict"; + + function makeApi(s, lol) { + /*global lolex */ + var llx = typeof lolex !== "undefined" ? lolex : lol; + + s.useFakeTimers = function () { + var now; + var methods = Array.prototype.slice.call(arguments); + + if (typeof methods[0] === "string") { + now = 0; + } else { + now = methods.shift(); + } + + var clock = llx.install(now || 0, methods); + clock.restore = clock.uninstall; + return clock; + }; + + s.clock = { + create: function (now) { + return llx.createClock(now); + } + }; + + s.timers = { + setTimeout: setTimeout, + clearTimeout: clearTimeout, + setImmediate: (typeof setImmediate !== "undefined" ? setImmediate : undefined), + clearImmediate: (typeof clearImmediate !== "undefined" ? clearImmediate : undefined), + setInterval: setInterval, + clearInterval: clearInterval, + Date: Date + }; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, epxorts, module, lolex) { + var core = require("./core"); + makeApi(core, lolex); + module.exports = core; + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require, module.exports, module, require("lolex")); + } else { + makeApi(sinon); // eslint-disable-line no-undef + } +}()); + +},{"./core":91,"lolex":100}],96:[function(require,module,exports){ +(function (global){ +/** + * @depend core.js + * @depend ../extend.js + * @depend event.js + * @depend ../log_error.js + */ +/** + * Fake XDomainRequest object + */ + +/** + * Returns the global to prevent assigning values to 'this' when this is undefined. + * This can occur when files are interpreted by node in strict mode. + * @private + */ +function getGlobal() { + "use strict"; + + return typeof window !== "undefined" ? window : global; +} + +if (typeof sinon === "undefined") { + if (typeof this === "undefined") { + getGlobal().sinon = {}; + } else { + this.sinon = {}; + } +} + +// wrapper for global +(function (global) { + "use strict"; + + var xdr = { XDomainRequest: global.XDomainRequest }; + xdr.GlobalXDomainRequest = global.XDomainRequest; + xdr.supportsXDR = typeof xdr.GlobalXDomainRequest !== "undefined"; + xdr.workingXDR = xdr.supportsXDR ? xdr.GlobalXDomainRequest : false; + + function makeApi(sinon) { + sinon.xdr = xdr; + + function FakeXDomainRequest() { + this.readyState = FakeXDomainRequest.UNSENT; + this.requestBody = null; + this.requestHeaders = {}; + this.status = 0; + this.timeout = null; + + if (typeof FakeXDomainRequest.onCreate === "function") { + FakeXDomainRequest.onCreate(this); + } + } + + function verifyState(x) { + if (x.readyState !== FakeXDomainRequest.OPENED) { + throw new Error("INVALID_STATE_ERR"); + } + + if (x.sendFlag) { + throw new Error("INVALID_STATE_ERR"); + } + } + + function verifyRequestSent(x) { + if (x.readyState === FakeXDomainRequest.UNSENT) { + throw new Error("Request not sent"); + } + if (x.readyState === FakeXDomainRequest.DONE) { + throw new Error("Request done"); + } + } + + function verifyResponseBodyType(body) { + if (typeof body !== "string") { + var error = new Error("Attempted to respond to fake XDomainRequest with " + + body + ", which is not a string."); + error.name = "InvalidBodyException"; + throw error; + } + } + + sinon.extend(FakeXDomainRequest.prototype, sinon.EventTarget, { + open: function open(method, url) { + this.method = method; + this.url = url; + + this.responseText = null; + this.sendFlag = false; + + this.readyStateChange(FakeXDomainRequest.OPENED); + }, + + readyStateChange: function readyStateChange(state) { + this.readyState = state; + var eventName = ""; + switch (this.readyState) { + case FakeXDomainRequest.UNSENT: + break; + case FakeXDomainRequest.OPENED: + break; + case FakeXDomainRequest.LOADING: + if (this.sendFlag) { + //raise the progress event + eventName = "onprogress"; + } + break; + case FakeXDomainRequest.DONE: + if (this.isTimeout) { + eventName = "ontimeout"; + } else if (this.errorFlag || (this.status < 200 || this.status > 299)) { + eventName = "onerror"; + } else { + eventName = "onload"; + } + break; + } + + // raising event (if defined) + if (eventName) { + if (typeof this[eventName] === "function") { + try { + this[eventName](); + } catch (e) { + sinon.logError("Fake XHR " + eventName + " handler", e); + } + } + } + }, + + send: function send(data) { + verifyState(this); + + if (!/^(get|head)$/i.test(this.method)) { + this.requestBody = data; + } + this.requestHeaders["Content-Type"] = "text/plain;charset=utf-8"; + + this.errorFlag = false; + this.sendFlag = true; + this.readyStateChange(FakeXDomainRequest.OPENED); + + if (typeof this.onSend === "function") { + this.onSend(this); + } + }, + + abort: function abort() { + this.aborted = true; + this.responseText = null; + this.errorFlag = true; + + if (this.readyState > sinon.FakeXDomainRequest.UNSENT && this.sendFlag) { + this.readyStateChange(sinon.FakeXDomainRequest.DONE); + this.sendFlag = false; + } + }, + + setResponseBody: function setResponseBody(body) { + verifyRequestSent(this); + verifyResponseBodyType(body); + + var chunkSize = this.chunkSize || 10; + var index = 0; + this.responseText = ""; + + do { + this.readyStateChange(FakeXDomainRequest.LOADING); + this.responseText += body.substring(index, index + chunkSize); + index += chunkSize; + } while (index < body.length); + + this.readyStateChange(FakeXDomainRequest.DONE); + }, + + respond: function respond(status, contentType, body) { + // content-type ignored, since XDomainRequest does not carry this + // we keep the same syntax for respond(...) as for FakeXMLHttpRequest to ease + // test integration across browsers + this.status = typeof status === "number" ? status : 200; + this.setResponseBody(body || ""); + }, + + simulatetimeout: function simulatetimeout() { + this.status = 0; + this.isTimeout = true; + // Access to this should actually throw an error + this.responseText = undefined; + this.readyStateChange(FakeXDomainRequest.DONE); + } + }); + + sinon.extend(FakeXDomainRequest, { + UNSENT: 0, + OPENED: 1, + LOADING: 3, + DONE: 4 + }); + + sinon.useFakeXDomainRequest = function useFakeXDomainRequest() { + sinon.FakeXDomainRequest.restore = function restore(keepOnCreate) { + if (xdr.supportsXDR) { + global.XDomainRequest = xdr.GlobalXDomainRequest; + } + + delete sinon.FakeXDomainRequest.restore; + + if (keepOnCreate !== true) { + delete sinon.FakeXDomainRequest.onCreate; + } + }; + if (xdr.supportsXDR) { + global.XDomainRequest = sinon.FakeXDomainRequest; + } + return sinon.FakeXDomainRequest; + }; + + sinon.FakeXDomainRequest = FakeXDomainRequest; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./core"); + require("../extend"); + require("./event"); + require("../log_error"); + makeApi(sinon); + module.exports = sinon; + } + + if (isAMD) { + define(loadDependencies); + } else if (isNode) { + loadDependencies(require, module.exports, module); + } else { + makeApi(sinon); // eslint-disable-line no-undef + } +})(typeof global !== "undefined" ? global : self); + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"../extend":79,"../log_error":81,"./core":91,"./event":92}],97:[function(require,module,exports){ +(function (global){ +/** + * @depend core.js + * @depend ../extend.js + * @depend event.js + * @depend ../log_error.js + */ +/** + * Fake XMLHttpRequest object + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2013 Christian Johansen + */ +(function (sinonGlobal, global) { + "use strict"; + + function getWorkingXHR(globalScope) { + var supportsXHR = typeof globalScope.XMLHttpRequest !== "undefined"; + if (supportsXHR) { + return globalScope.XMLHttpRequest; + } + + var supportsActiveX = typeof globalScope.ActiveXObject !== "undefined"; + if (supportsActiveX) { + return function () { + return new globalScope.ActiveXObject("MSXML2.XMLHTTP.3.0"); + }; + } + + return false; + } + + var supportsProgress = typeof ProgressEvent !== "undefined"; + var supportsCustomEvent = typeof CustomEvent !== "undefined"; + var supportsFormData = typeof FormData !== "undefined"; + var supportsArrayBuffer = typeof ArrayBuffer !== "undefined"; + var supportsBlob = (function () { + try { + return !!new Blob(); + } catch (e) { + return false; + } + })(); + var sinonXhr = { XMLHttpRequest: global.XMLHttpRequest }; + sinonXhr.GlobalXMLHttpRequest = global.XMLHttpRequest; + sinonXhr.GlobalActiveXObject = global.ActiveXObject; + sinonXhr.supportsActiveX = typeof sinonXhr.GlobalActiveXObject !== "undefined"; + sinonXhr.supportsXHR = typeof sinonXhr.GlobalXMLHttpRequest !== "undefined"; + sinonXhr.workingXHR = getWorkingXHR(global); + sinonXhr.supportsCORS = sinonXhr.supportsXHR && "withCredentials" in (new sinonXhr.GlobalXMLHttpRequest()); + + var unsafeHeaders = { + "Accept-Charset": true, + "Accept-Encoding": true, + Connection: true, + "Content-Length": true, + Cookie: true, + Cookie2: true, + "Content-Transfer-Encoding": true, + Date: true, + Expect: true, + Host: true, + "Keep-Alive": true, + Referer: true, + TE: true, + Trailer: true, + "Transfer-Encoding": true, + Upgrade: true, + "User-Agent": true, + Via: true + }; + + // An upload object is created for each + // FakeXMLHttpRequest and allows upload + // events to be simulated using uploadProgress + // and uploadError. + function UploadProgress() { + this.eventListeners = { + abort: [], + error: [], + load: [], + loadend: [], + progress: [] + }; + } + + UploadProgress.prototype.addEventListener = function addEventListener(event, listener) { + this.eventListeners[event].push(listener); + }; + + UploadProgress.prototype.removeEventListener = function removeEventListener(event, listener) { + var listeners = this.eventListeners[event] || []; + + for (var i = 0, l = listeners.length; i < l; ++i) { + if (listeners[i] === listener) { + return listeners.splice(i, 1); + } + } + }; + + UploadProgress.prototype.dispatchEvent = function dispatchEvent(event) { + var listeners = this.eventListeners[event.type] || []; + + for (var i = 0, listener; (listener = listeners[i]) != null; i++) { + listener(event); + } + }; + + // Note that for FakeXMLHttpRequest to work pre ES5 + // we lose some of the alignment with the spec. + // To ensure as close a match as possible, + // set responseType before calling open, send or respond; + function FakeXMLHttpRequest() { + this.readyState = FakeXMLHttpRequest.UNSENT; + this.requestHeaders = {}; + this.requestBody = null; + this.status = 0; + this.statusText = ""; + this.upload = new UploadProgress(); + this.responseType = ""; + this.response = ""; + if (sinonXhr.supportsCORS) { + this.withCredentials = false; + } + + var xhr = this; + var events = ["loadstart", "load", "abort", "error", "loadend"]; + + function addEventListener(eventName) { + xhr.addEventListener(eventName, function (event) { + var listener = xhr["on" + eventName]; + + if (listener && typeof listener === "function") { + listener.call(this, event); + } + }); + } + + for (var i = events.length - 1; i >= 0; i--) { + addEventListener(events[i]); + } + + if (typeof FakeXMLHttpRequest.onCreate === "function") { + FakeXMLHttpRequest.onCreate(this); + } + } + + function verifyState(xhr) { + if (xhr.readyState !== FakeXMLHttpRequest.OPENED) { + throw new Error("INVALID_STATE_ERR"); + } + + if (xhr.sendFlag) { + throw new Error("INVALID_STATE_ERR"); + } + } + + function getHeader(headers, header) { + header = header.toLowerCase(); + + for (var h in headers) { + if (h.toLowerCase() === header) { + return h; + } + } + + return null; + } + + // filtering to enable a white-list version of Sinon FakeXhr, + // where whitelisted requests are passed through to real XHR + function each(collection, callback) { + if (!collection) { + return; + } + + for (var i = 0, l = collection.length; i < l; i += 1) { + callback(collection[i]); + } + } + function some(collection, callback) { + for (var index = 0; index < collection.length; index++) { + if (callback(collection[index]) === true) { + return true; + } + } + return false; + } + // largest arity in XHR is 5 - XHR#open + var apply = function (obj, method, args) { + switch (args.length) { + case 0: return obj[method](); + case 1: return obj[method](args[0]); + case 2: return obj[method](args[0], args[1]); + case 3: return obj[method](args[0], args[1], args[2]); + case 4: return obj[method](args[0], args[1], args[2], args[3]); + case 5: return obj[method](args[0], args[1], args[2], args[3], args[4]); + } + }; + + FakeXMLHttpRequest.filters = []; + FakeXMLHttpRequest.addFilter = function addFilter(fn) { + this.filters.push(fn); + }; + var IE6Re = /MSIE 6/; + FakeXMLHttpRequest.defake = function defake(fakeXhr, xhrArgs) { + var xhr = new sinonXhr.workingXHR(); // eslint-disable-line new-cap + + each([ + "open", + "setRequestHeader", + "send", + "abort", + "getResponseHeader", + "getAllResponseHeaders", + "addEventListener", + "overrideMimeType", + "removeEventListener" + ], function (method) { + fakeXhr[method] = function () { + return apply(xhr, method, arguments); + }; + }); + + var copyAttrs = function (args) { + each(args, function (attr) { + try { + fakeXhr[attr] = xhr[attr]; + } catch (e) { + if (!IE6Re.test(navigator.userAgent)) { + throw e; + } + } + }); + }; + + var stateChange = function stateChange() { + fakeXhr.readyState = xhr.readyState; + if (xhr.readyState >= FakeXMLHttpRequest.HEADERS_RECEIVED) { + copyAttrs(["status", "statusText"]); + } + if (xhr.readyState >= FakeXMLHttpRequest.LOADING) { + copyAttrs(["responseText", "response"]); + } + if (xhr.readyState === FakeXMLHttpRequest.DONE) { + copyAttrs(["responseXML"]); + } + if (fakeXhr.onreadystatechange) { + fakeXhr.onreadystatechange.call(fakeXhr, { target: fakeXhr }); + } + }; + + if (xhr.addEventListener) { + for (var event in fakeXhr.eventListeners) { + if (fakeXhr.eventListeners.hasOwnProperty(event)) { + + /*eslint-disable no-loop-func*/ + each(fakeXhr.eventListeners[event], function (handler) { + xhr.addEventListener(event, handler); + }); + /*eslint-enable no-loop-func*/ + } + } + xhr.addEventListener("readystatechange", stateChange); + } else { + xhr.onreadystatechange = stateChange; + } + apply(xhr, "open", xhrArgs); + }; + FakeXMLHttpRequest.useFilters = false; + + function verifyRequestOpened(xhr) { + if (xhr.readyState !== FakeXMLHttpRequest.OPENED) { + throw new Error("INVALID_STATE_ERR - " + xhr.readyState); + } + } + + function verifyRequestSent(xhr) { + if (xhr.readyState === FakeXMLHttpRequest.DONE) { + throw new Error("Request done"); + } + } + + function verifyHeadersReceived(xhr) { + if (xhr.async && xhr.readyState !== FakeXMLHttpRequest.HEADERS_RECEIVED) { + throw new Error("No headers received"); + } + } + + function verifyResponseBodyType(body) { + if (typeof body !== "string") { + var error = new Error("Attempted to respond to fake XMLHttpRequest with " + + body + ", which is not a string."); + error.name = "InvalidBodyException"; + throw error; + } + } + + function convertToArrayBuffer(body) { + var buffer = new ArrayBuffer(body.length); + var view = new Uint8Array(buffer); + for (var i = 0; i < body.length; i++) { + var charCode = body.charCodeAt(i); + if (charCode >= 256) { + throw new TypeError("arraybuffer or blob responseTypes require binary string, " + + "invalid character " + body[i] + " found."); + } + view[i] = charCode; + } + return buffer; + } + + function isXmlContentType(contentType) { + return !contentType || /(text\/xml)|(application\/xml)|(\+xml)/.test(contentType); + } + + function convertResponseBody(responseType, contentType, body) { + if (responseType === "" || responseType === "text") { + return body; + } else if (supportsArrayBuffer && responseType === "arraybuffer") { + return convertToArrayBuffer(body); + } else if (responseType === "json") { + try { + return JSON.parse(body); + } catch (e) { + // Return parsing failure as null + return null; + } + } else if (supportsBlob && responseType === "blob") { + var blobOptions = {}; + if (contentType) { + blobOptions.type = contentType; + } + return new Blob([convertToArrayBuffer(body)], blobOptions); + } else if (responseType === "document") { + if (isXmlContentType(contentType)) { + return FakeXMLHttpRequest.parseXML(body); + } + return null; + } + throw new Error("Invalid responseType " + responseType); + } + + function clearResponse(xhr) { + if (xhr.responseType === "" || xhr.responseType === "text") { + xhr.response = xhr.responseText = ""; + } else { + xhr.response = xhr.responseText = null; + } + xhr.responseXML = null; + } + + FakeXMLHttpRequest.parseXML = function parseXML(text) { + // Treat empty string as parsing failure + if (text !== "") { + try { + if (typeof DOMParser !== "undefined") { + var parser = new DOMParser(); + return parser.parseFromString(text, "text/xml"); + } + var xmlDoc = new window.ActiveXObject("Microsoft.XMLDOM"); + xmlDoc.async = "false"; + xmlDoc.loadXML(text); + return xmlDoc; + } catch (e) { + // Unable to parse XML - no biggie + } + } + + return null; + }; + + FakeXMLHttpRequest.statusCodes = { + 100: "Continue", + 101: "Switching Protocols", + 200: "OK", + 201: "Created", + 202: "Accepted", + 203: "Non-Authoritative Information", + 204: "No Content", + 205: "Reset Content", + 206: "Partial Content", + 207: "Multi-Status", + 300: "Multiple Choice", + 301: "Moved Permanently", + 302: "Found", + 303: "See Other", + 304: "Not Modified", + 305: "Use Proxy", + 307: "Temporary Redirect", + 400: "Bad Request", + 401: "Unauthorized", + 402: "Payment Required", + 403: "Forbidden", + 404: "Not Found", + 405: "Method Not Allowed", + 406: "Not Acceptable", + 407: "Proxy Authentication Required", + 408: "Request Timeout", + 409: "Conflict", + 410: "Gone", + 411: "Length Required", + 412: "Precondition Failed", + 413: "Request Entity Too Large", + 414: "Request-URI Too Long", + 415: "Unsupported Media Type", + 416: "Requested Range Not Satisfiable", + 417: "Expectation Failed", + 422: "Unprocessable Entity", + 500: "Internal Server Error", + 501: "Not Implemented", + 502: "Bad Gateway", + 503: "Service Unavailable", + 504: "Gateway Timeout", + 505: "HTTP Version Not Supported" + }; + + function makeApi(sinon) { + sinon.xhr = sinonXhr; + + sinon.extend(FakeXMLHttpRequest.prototype, sinon.EventTarget, { + async: true, + + open: function open(method, url, async, username, password) { + this.method = method; + this.url = url; + this.async = typeof async === "boolean" ? async : true; + this.username = username; + this.password = password; + clearResponse(this); + this.requestHeaders = {}; + this.sendFlag = false; + + if (FakeXMLHttpRequest.useFilters === true) { + var xhrArgs = arguments; + var defake = some(FakeXMLHttpRequest.filters, function (filter) { + return filter.apply(this, xhrArgs); + }); + if (defake) { + return FakeXMLHttpRequest.defake(this, arguments); + } + } + this.readyStateChange(FakeXMLHttpRequest.OPENED); + }, + + readyStateChange: function readyStateChange(state) { + this.readyState = state; + + var readyStateChangeEvent = new sinon.Event("readystatechange", false, false, this); + var event, progress; + + if (typeof this.onreadystatechange === "function") { + try { + this.onreadystatechange(readyStateChangeEvent); + } catch (e) { + sinon.logError("Fake XHR onreadystatechange handler", e); + } + } + + if (this.readyState === FakeXMLHttpRequest.DONE) { + // ensure loaded and total are numbers + progress = { + loaded: this.progress || 0, + total: this.progress || 0 + }; + + if (this.status === 0) { + event = this.aborted ? "abort" : "error"; + } + else { + event = "load"; + } + + if (supportsProgress) { + this.upload.dispatchEvent(new sinon.ProgressEvent("progress", progress, this)); + this.upload.dispatchEvent(new sinon.ProgressEvent(event, progress, this)); + this.upload.dispatchEvent(new sinon.ProgressEvent("loadend", progress, this)); + } + + this.dispatchEvent(new sinon.ProgressEvent("progress", progress, this)); + this.dispatchEvent(new sinon.ProgressEvent(event, progress, this)); + this.dispatchEvent(new sinon.ProgressEvent("loadend", progress, this)); + } + + this.dispatchEvent(readyStateChangeEvent); + }, + + setRequestHeader: function setRequestHeader(header, value) { + verifyState(this); + + if (unsafeHeaders[header] || /^(Sec-|Proxy-)/.test(header)) { + throw new Error("Refused to set unsafe header \"" + header + "\""); + } + + if (this.requestHeaders[header]) { + this.requestHeaders[header] += "," + value; + } else { + this.requestHeaders[header] = value; + } + }, + + // Helps testing + setResponseHeaders: function setResponseHeaders(headers) { + verifyRequestOpened(this); + this.responseHeaders = {}; + + for (var header in headers) { + if (headers.hasOwnProperty(header)) { + this.responseHeaders[header] = headers[header]; + } + } + + if (this.async) { + this.readyStateChange(FakeXMLHttpRequest.HEADERS_RECEIVED); + } else { + this.readyState = FakeXMLHttpRequest.HEADERS_RECEIVED; + } + }, + + // Currently treats ALL data as a DOMString (i.e. no Document) + send: function send(data) { + verifyState(this); + + if (!/^(get|head)$/i.test(this.method)) { + var contentType = getHeader(this.requestHeaders, "Content-Type"); + if (this.requestHeaders[contentType]) { + var value = this.requestHeaders[contentType].split(";"); + this.requestHeaders[contentType] = value[0] + ";charset=utf-8"; + } else if (supportsFormData && !(data instanceof FormData)) { + this.requestHeaders["Content-Type"] = "text/plain;charset=utf-8"; + } + + this.requestBody = data; + } + + this.errorFlag = false; + this.sendFlag = this.async; + clearResponse(this); + this.readyStateChange(FakeXMLHttpRequest.OPENED); + + if (typeof this.onSend === "function") { + this.onSend(this); + } + + this.dispatchEvent(new sinon.Event("loadstart", false, false, this)); + }, + + abort: function abort() { + this.aborted = true; + clearResponse(this); + this.errorFlag = true; + this.requestHeaders = {}; + this.responseHeaders = {}; + + if (this.readyState > FakeXMLHttpRequest.UNSENT && this.sendFlag) { + this.readyStateChange(FakeXMLHttpRequest.DONE); + this.sendFlag = false; + } + + this.readyState = FakeXMLHttpRequest.UNSENT; + }, + + error: function error() { + clearResponse(this); + this.errorFlag = true; + this.requestHeaders = {}; + this.responseHeaders = {}; + + this.readyStateChange(FakeXMLHttpRequest.DONE); + }, + + getResponseHeader: function getResponseHeader(header) { + if (this.readyState < FakeXMLHttpRequest.HEADERS_RECEIVED) { + return null; + } + + if (/^Set-Cookie2?$/i.test(header)) { + return null; + } + + header = getHeader(this.responseHeaders, header); + + return this.responseHeaders[header] || null; + }, + + getAllResponseHeaders: function getAllResponseHeaders() { + if (this.readyState < FakeXMLHttpRequest.HEADERS_RECEIVED) { + return ""; + } + + var headers = ""; + + for (var header in this.responseHeaders) { + if (this.responseHeaders.hasOwnProperty(header) && + !/^Set-Cookie2?$/i.test(header)) { + headers += header + ": " + this.responseHeaders[header] + "\r\n"; + } + } + + return headers; + }, + + setResponseBody: function setResponseBody(body) { + verifyRequestSent(this); + verifyHeadersReceived(this); + verifyResponseBodyType(body); + var contentType = this.getResponseHeader("Content-Type"); + + var isTextResponse = this.responseType === "" || this.responseType === "text"; + clearResponse(this); + if (this.async) { + var chunkSize = this.chunkSize || 10; + var index = 0; + + do { + this.readyStateChange(FakeXMLHttpRequest.LOADING); + + if (isTextResponse) { + this.responseText = this.response += body.substring(index, index + chunkSize); + } + index += chunkSize; + } while (index < body.length); + } + + this.response = convertResponseBody(this.responseType, contentType, body); + if (isTextResponse) { + this.responseText = this.response; + } + + if (this.responseType === "document") { + this.responseXML = this.response; + } else if (this.responseType === "" && isXmlContentType(contentType)) { + this.responseXML = FakeXMLHttpRequest.parseXML(this.responseText); + } + this.progress = body.length; + this.readyStateChange(FakeXMLHttpRequest.DONE); + }, + + respond: function respond(status, headers, body) { + this.status = typeof status === "number" ? status : 200; + this.statusText = FakeXMLHttpRequest.statusCodes[this.status]; + this.setResponseHeaders(headers || {}); + this.setResponseBody(body || ""); + }, + + uploadProgress: function uploadProgress(progressEventRaw) { + if (supportsProgress) { + this.upload.dispatchEvent(new sinon.ProgressEvent("progress", progressEventRaw)); + } + }, + + downloadProgress: function downloadProgress(progressEventRaw) { + if (supportsProgress) { + this.dispatchEvent(new sinon.ProgressEvent("progress", progressEventRaw)); + } + }, + + uploadError: function uploadError(error) { + if (supportsCustomEvent) { + this.upload.dispatchEvent(new sinon.CustomEvent("error", {detail: error})); + } + } + }); + + sinon.extend(FakeXMLHttpRequest, { + UNSENT: 0, + OPENED: 1, + HEADERS_RECEIVED: 2, + LOADING: 3, + DONE: 4 + }); + + sinon.useFakeXMLHttpRequest = function () { + FakeXMLHttpRequest.restore = function restore(keepOnCreate) { + if (sinonXhr.supportsXHR) { + global.XMLHttpRequest = sinonXhr.GlobalXMLHttpRequest; + } + + if (sinonXhr.supportsActiveX) { + global.ActiveXObject = sinonXhr.GlobalActiveXObject; + } + + delete FakeXMLHttpRequest.restore; + + if (keepOnCreate !== true) { + delete FakeXMLHttpRequest.onCreate; + } + }; + if (sinonXhr.supportsXHR) { + global.XMLHttpRequest = FakeXMLHttpRequest; + } + + if (sinonXhr.supportsActiveX) { + global.ActiveXObject = function ActiveXObject(objId) { + if (objId === "Microsoft.XMLHTTP" || /^Msxml2\.XMLHTTP/i.test(objId)) { + + return new FakeXMLHttpRequest(); + } + + return new sinonXhr.GlobalActiveXObject(objId); + }; + } + + return FakeXMLHttpRequest; + }; + + sinon.FakeXMLHttpRequest = FakeXMLHttpRequest; + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + function loadDependencies(require, exports, module) { + var sinon = require("./core"); + require("../extend"); + require("./event"); + require("../log_error"); + makeApi(sinon); + module.exports = sinon; + } + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon, // eslint-disable-line no-undef + typeof global !== "undefined" ? global : self +)); + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"../extend":79,"../log_error":81,"./core":91,"./event":92}],98:[function(require,module,exports){ +/** + * @depend util/core.js + */ +(function (sinonGlobal) { + "use strict"; + + function makeApi(sinon) { + function walkInternal(obj, iterator, context, originalObj, seen) { + var proto, prop; + + if (typeof Object.getOwnPropertyNames !== "function") { + // We explicitly want to enumerate through all of the prototype's properties + // in this case, therefore we deliberately leave out an own property check. + /* eslint-disable guard-for-in */ + for (prop in obj) { + iterator.call(context, obj[prop], prop, obj); + } + /* eslint-enable guard-for-in */ + + return; + } + + Object.getOwnPropertyNames(obj).forEach(function (k) { + if (seen[k] !== true) { + seen[k] = true; + var target = typeof Object.getOwnPropertyDescriptor(obj, k).get === "function" ? + originalObj : obj; + iterator.call(context, target[k], k, target); + } + }); + + proto = Object.getPrototypeOf(obj); + if (proto) { + walkInternal(proto, iterator, context, originalObj, seen); + } + } + + /* Public: walks the prototype chain of an object and iterates over every own property + * name encountered. The iterator is called in the same fashion that Array.prototype.forEach + * works, where it is passed the value, key, and own object as the 1st, 2nd, and 3rd positional + * argument, respectively. In cases where Object.getOwnPropertyNames is not available, walk will + * default to using a simple for..in loop. + * + * obj - The object to walk the prototype chain for. + * iterator - The function to be called on each pass of the walk. + * context - (Optional) When given, the iterator will be called with this object as the receiver. + */ + function walk(obj, iterator, context) { + return walkInternal(obj, iterator, context, obj, {}); + } + + sinon.walk = walk; + return sinon.walk; + } + + function loadDependencies(require, exports, module) { + var sinon = require("./util/core"); + module.exports = makeApi(sinon); + } + + var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; + var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; + + if (isAMD) { + define(loadDependencies); + return; + } + + if (isNode) { + loadDependencies(require, module.exports, module); + return; + } + + if (sinonGlobal) { + makeApi(sinonGlobal); + } +}( + typeof sinon === "object" && sinon // eslint-disable-line no-undef +)); + +},{"./util/core":91}],99:[function(require,module,exports){ +(function (global){ +((typeof define === "function" && define.amd && function (m) { + define("formatio", ["samsam"], m); +}) || (typeof module === "object" && function (m) { + module.exports = m(require("samsam")); +}) || function (m) { this.formatio = m(this.samsam); } +)(function (samsam) { + "use strict"; + + var formatio = { + excludeConstructors: ["Object", /^.$/], + quoteStrings: true, + limitChildrenCount: 0 + }; + + var hasOwn = Object.prototype.hasOwnProperty; + + var specialObjects = []; + if (typeof global !== "undefined") { + specialObjects.push({ object: global, value: "[object global]" }); + } + if (typeof document !== "undefined") { + specialObjects.push({ + object: document, + value: "[object HTMLDocument]" + }); + } + if (typeof window !== "undefined") { + specialObjects.push({ object: window, value: "[object Window]" }); + } + + function functionName(func) { + if (!func) { return ""; } + if (func.displayName) { return func.displayName; } + if (func.name) { return func.name; } + var matches = func.toString().match(/function\s+([^\(]+)/m); + return (matches && matches[1]) || ""; + } + + function constructorName(f, object) { + var name = functionName(object && object.constructor); + var excludes = f.excludeConstructors || + formatio.excludeConstructors || []; + + var i, l; + for (i = 0, l = excludes.length; i < l; ++i) { + if (typeof excludes[i] === "string" && excludes[i] === name) { + return ""; + } else if (excludes[i].test && excludes[i].test(name)) { + return ""; + } + } + + return name; + } + + function isCircular(object, objects) { + if (typeof object !== "object") { return false; } + var i, l; + for (i = 0, l = objects.length; i < l; ++i) { + if (objects[i] === object) { return true; } + } + return false; + } + + function ascii(f, object, processed, indent) { + if (typeof object === "string") { + var qs = f.quoteStrings; + var quote = typeof qs !== "boolean" || qs; + return processed || quote ? '"' + object + '"' : object; + } + + if (typeof object === "function" && !(object instanceof RegExp)) { + return ascii.func(object); + } + + processed = processed || []; + + if (isCircular(object, processed)) { return "[Circular]"; } + + if (Object.prototype.toString.call(object) === "[object Array]") { + return ascii.array.call(f, object, processed); + } + + if (!object) { return String((1/object) === -Infinity ? "-0" : object); } + if (samsam.isElement(object)) { return ascii.element(object); } + + if (typeof object.toString === "function" && + object.toString !== Object.prototype.toString) { + return object.toString(); + } + + var i, l; + for (i = 0, l = specialObjects.length; i < l; i++) { + if (object === specialObjects[i].object) { + return specialObjects[i].value; + } + } + + return ascii.object.call(f, object, processed, indent); + } + + ascii.func = function (func) { + return "function " + functionName(func) + "() {}"; + }; + + ascii.array = function (array, processed) { + processed = processed || []; + processed.push(array); + var pieces = []; + var i, l; + l = (this.limitChildrenCount > 0) ? + Math.min(this.limitChildrenCount, array.length) : array.length; + + for (i = 0; i < l; ++i) { + pieces.push(ascii(this, array[i], processed)); + } + + if(l < array.length) + pieces.push("[... " + (array.length - l) + " more elements]"); + + return "[" + pieces.join(", ") + "]"; + }; + + ascii.object = function (object, processed, indent) { + processed = processed || []; + processed.push(object); + indent = indent || 0; + var pieces = [], properties = samsam.keys(object).sort(); + var length = 3; + var prop, str, obj, i, k, l; + l = (this.limitChildrenCount > 0) ? + Math.min(this.limitChildrenCount, properties.length) : properties.length; + + for (i = 0; i < l; ++i) { + prop = properties[i]; + obj = object[prop]; + + if (isCircular(obj, processed)) { + str = "[Circular]"; + } else { + str = ascii(this, obj, processed, indent + 2); + } + + str = (/\s/.test(prop) ? '"' + prop + '"' : prop) + ": " + str; + length += str.length; + pieces.push(str); + } + + var cons = constructorName(this, object); + var prefix = cons ? "[" + cons + "] " : ""; + var is = ""; + for (i = 0, k = indent; i < k; ++i) { is += " "; } + + if(l < properties.length) + pieces.push("[... " + (properties.length - l) + " more elements]"); + + if (length + indent > 80) { + return prefix + "{\n " + is + pieces.join(",\n " + is) + "\n" + + is + "}"; + } + return prefix + "{ " + pieces.join(", ") + " }"; + }; + + ascii.element = function (element) { + var tagName = element.tagName.toLowerCase(); + var attrs = element.attributes, attr, pairs = [], attrName, i, l, val; + + for (i = 0, l = attrs.length; i < l; ++i) { + attr = attrs.item(i); + attrName = attr.nodeName.toLowerCase().replace("html:", ""); + val = attr.nodeValue; + if (attrName !== "contenteditable" || val !== "inherit") { + if (!!val) { pairs.push(attrName + "=\"" + val + "\""); } + } + } + + var formatted = "<" + tagName + (pairs.length > 0 ? " " : ""); + var content = element.innerHTML; + + if (content.length > 20) { + content = content.substr(0, 20) + "[...]"; + } + + var res = formatted + pairs.join(" ") + ">" + content + + ""; + + return res.replace(/ contentEditable="inherit"/, ""); + }; + + function Formatio(options) { + for (var opt in options) { + this[opt] = options[opt]; + } + } + + Formatio.prototype = { + functionName: functionName, + + configure: function (options) { + return new Formatio(options); + }, + + constructorName: function (object) { + return constructorName(this, object); + }, + + ascii: function (object, processed, indent) { + return ascii(this, object, processed, indent); + } + }; + + return Formatio.prototype; +}); + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"samsam":101}],100:[function(require,module,exports){ +(function (global){ +/*global global, window*/ +/** + * @author Christian Johansen (christian@cjohansen.no) and contributors + * @license BSD + * + * Copyright (c) 2010-2014 Christian Johansen + */ + +(function (global) { + "use strict"; + + // Make properties writable in IE, as per + // http://www.adequatelygood.com/Replacing-setTimeout-Globally.html + // JSLint being anal + var glbl = global; + + global.setTimeout = glbl.setTimeout; + global.clearTimeout = glbl.clearTimeout; + global.setInterval = glbl.setInterval; + global.clearInterval = glbl.clearInterval; + global.Date = glbl.Date; + + // setImmediate is not a standard function + // avoid adding the prop to the window object if not present + if('setImmediate' in global) { + global.setImmediate = glbl.setImmediate; + global.clearImmediate = glbl.clearImmediate; + } + + // node expects setTimeout/setInterval to return a fn object w/ .ref()/.unref() + // browsers, a number. + // see https://github.com/cjohansen/Sinon.JS/pull/436 + + var NOOP = function () { return undefined; }; + var timeoutResult = setTimeout(NOOP, 0); + var addTimerReturnsObject = typeof timeoutResult === "object"; + clearTimeout(timeoutResult); + + var NativeDate = Date; + var uniqueTimerId = 1; + + /** + * Parse strings like "01:10:00" (meaning 1 hour, 10 minutes, 0 seconds) into + * number of milliseconds. This is used to support human-readable strings passed + * to clock.tick() + */ + function parseTime(str) { + if (!str) { + return 0; + } + + var strings = str.split(":"); + var l = strings.length, i = l; + var ms = 0, parsed; + + if (l > 3 || !/^(\d\d:){0,2}\d\d?$/.test(str)) { + throw new Error("tick only understands numbers and 'h:m:s'"); + } + + while (i--) { + parsed = parseInt(strings[i], 10); + + if (parsed >= 60) { + throw new Error("Invalid time " + str); + } + + ms += parsed * Math.pow(60, (l - i - 1)); + } + + return ms * 1000; + } + + /** + * Used to grok the `now` parameter to createClock. + */ + function getEpoch(epoch) { + if (!epoch) { return 0; } + if (typeof epoch.getTime === "function") { return epoch.getTime(); } + if (typeof epoch === "number") { return epoch; } + throw new TypeError("now should be milliseconds since UNIX epoch"); + } + + function inRange(from, to, timer) { + return timer && timer.callAt >= from && timer.callAt <= to; + } + + function mirrorDateProperties(target, source) { + var prop; + for (prop in source) { + if (source.hasOwnProperty(prop)) { + target[prop] = source[prop]; + } + } + + // set special now implementation + if (source.now) { + target.now = function now() { + return target.clock.now; + }; + } else { + delete target.now; + } + + // set special toSource implementation + if (source.toSource) { + target.toSource = function toSource() { + return source.toSource(); + }; + } else { + delete target.toSource; + } + + // set special toString implementation + target.toString = function toString() { + return source.toString(); + }; + + target.prototype = source.prototype; + target.parse = source.parse; + target.UTC = source.UTC; + target.prototype.toUTCString = source.prototype.toUTCString; + + return target; + } + + function createDate() { + function ClockDate(year, month, date, hour, minute, second, ms) { + // Defensive and verbose to avoid potential harm in passing + // explicit undefined when user does not pass argument + switch (arguments.length) { + case 0: + return new NativeDate(ClockDate.clock.now); + case 1: + return new NativeDate(year); + case 2: + return new NativeDate(year, month); + case 3: + return new NativeDate(year, month, date); + case 4: + return new NativeDate(year, month, date, hour); + case 5: + return new NativeDate(year, month, date, hour, minute); + case 6: + return new NativeDate(year, month, date, hour, minute, second); + default: + return new NativeDate(year, month, date, hour, minute, second, ms); + } + } + + return mirrorDateProperties(ClockDate, NativeDate); + } + + function addTimer(clock, timer) { + if (timer.func === undefined) { + throw new Error("Callback must be provided to timer calls"); + } + + if (!clock.timers) { + clock.timers = {}; + } + + timer.id = uniqueTimerId++; + timer.createdAt = clock.now; + timer.callAt = clock.now + (timer.delay || (clock.duringTick ? 1 : 0)); + + clock.timers[timer.id] = timer; + + if (addTimerReturnsObject) { + return { + id: timer.id, + ref: NOOP, + unref: NOOP + }; + } + + return timer.id; + } + + + function compareTimers(a, b) { + // Sort first by absolute timing + if (a.callAt < b.callAt) { + return -1; + } + if (a.callAt > b.callAt) { + return 1; + } + + // Sort next by immediate, immediate timers take precedence + if (a.immediate && !b.immediate) { + return -1; + } + if (!a.immediate && b.immediate) { + return 1; + } + + // Sort next by creation time, earlier-created timers take precedence + if (a.createdAt < b.createdAt) { + return -1; + } + if (a.createdAt > b.createdAt) { + return 1; + } + + // Sort next by id, lower-id timers take precedence + if (a.id < b.id) { + return -1; + } + if (a.id > b.id) { + return 1; + } + + // As timer ids are unique, no fallback `0` is necessary + } + + function firstTimerInRange(clock, from, to) { + var timers = clock.timers, + timer = null, + id, + isInRange; + + for (id in timers) { + if (timers.hasOwnProperty(id)) { + isInRange = inRange(from, to, timers[id]); + + if (isInRange && (!timer || compareTimers(timer, timers[id]) === 1)) { + timer = timers[id]; + } + } + } + + return timer; + } + + function callTimer(clock, timer) { + var exception; + + if (typeof timer.interval === "number") { + clock.timers[timer.id].callAt += timer.interval; + } else { + delete clock.timers[timer.id]; + } + + try { + if (typeof timer.func === "function") { + timer.func.apply(null, timer.args); + } else { + eval(timer.func); + } + } catch (e) { + exception = e; + } + + if (!clock.timers[timer.id]) { + if (exception) { + throw exception; + } + return; + } + + if (exception) { + throw exception; + } + } + + function timerType(timer) { + if (timer.immediate) { + return "Immediate"; + } else if (typeof timer.interval !== "undefined") { + return "Interval"; + } else { + return "Timeout"; + } + } + + function clearTimer(clock, timerId, ttype) { + if (!timerId) { + // null appears to be allowed in most browsers, and appears to be + // relied upon by some libraries, like Bootstrap carousel + return; + } + + if (!clock.timers) { + clock.timers = []; + } + + // in Node, timerId is an object with .ref()/.unref(), and + // its .id field is the actual timer id. + if (typeof timerId === "object") { + timerId = timerId.id; + } + + if (clock.timers.hasOwnProperty(timerId)) { + // check that the ID matches a timer of the correct type + var timer = clock.timers[timerId]; + if (timerType(timer) === ttype) { + delete clock.timers[timerId]; + } else { + throw new Error("Cannot clear timer: timer created with set" + ttype + "() but cleared with clear" + timerType(timer) + "()"); + } + } + } + + function uninstall(clock, target) { + var method, + i, + l; + + for (i = 0, l = clock.methods.length; i < l; i++) { + method = clock.methods[i]; + + if (target[method].hadOwnProperty) { + target[method] = clock["_" + method]; + } else { + try { + delete target[method]; + } catch (ignore) {} + } + } + + // Prevent multiple executions which will completely remove these props + clock.methods = []; + } + + function hijackMethod(target, method, clock) { + var prop; + + clock[method].hadOwnProperty = Object.prototype.hasOwnProperty.call(target, method); + clock["_" + method] = target[method]; + + if (method === "Date") { + var date = mirrorDateProperties(clock[method], target[method]); + target[method] = date; + } else { + target[method] = function () { + return clock[method].apply(clock, arguments); + }; + + for (prop in clock[method]) { + if (clock[method].hasOwnProperty(prop)) { + target[method][prop] = clock[method][prop]; + } + } + } + + target[method].clock = clock; + } + + var timers = { + setTimeout: setTimeout, + clearTimeout: clearTimeout, + setImmediate: global.setImmediate, + clearImmediate: global.clearImmediate, + setInterval: setInterval, + clearInterval: clearInterval, + Date: Date + }; + + var keys = Object.keys || function (obj) { + var ks = [], + key; + + for (key in obj) { + if (obj.hasOwnProperty(key)) { + ks.push(key); + } + } + + return ks; + }; + + exports.timers = timers; + + function createClock(now) { + var clock = { + now: getEpoch(now), + timeouts: {}, + Date: createDate() + }; + + clock.Date.clock = clock; + + clock.setTimeout = function setTimeout(func, timeout) { + return addTimer(clock, { + func: func, + args: Array.prototype.slice.call(arguments, 2), + delay: timeout + }); + }; + + clock.clearTimeout = function clearTimeout(timerId) { + return clearTimer(clock, timerId, "Timeout"); + }; + + clock.setInterval = function setInterval(func, timeout) { + return addTimer(clock, { + func: func, + args: Array.prototype.slice.call(arguments, 2), + delay: timeout, + interval: timeout + }); + }; + + clock.clearInterval = function clearInterval(timerId) { + return clearTimer(clock, timerId, "Interval"); + }; + + clock.setImmediate = function setImmediate(func) { + return addTimer(clock, { + func: func, + args: Array.prototype.slice.call(arguments, 1), + immediate: true + }); + }; + + clock.clearImmediate = function clearImmediate(timerId) { + return clearTimer(clock, timerId, "Immediate"); + }; + + clock.tick = function tick(ms) { + ms = typeof ms === "number" ? ms : parseTime(ms); + var tickFrom = clock.now, tickTo = clock.now + ms, previous = clock.now; + var timer = firstTimerInRange(clock, tickFrom, tickTo); + var oldNow; + + clock.duringTick = true; + + var firstException; + while (timer && tickFrom <= tickTo) { + if (clock.timers[timer.id]) { + tickFrom = clock.now = timer.callAt; + try { + oldNow = clock.now; + callTimer(clock, timer); + // compensate for any setSystemTime() call during timer callback + if (oldNow !== clock.now) { + tickFrom += clock.now - oldNow; + tickTo += clock.now - oldNow; + previous += clock.now - oldNow; + } + } catch (e) { + firstException = firstException || e; + } + } + + timer = firstTimerInRange(clock, previous, tickTo); + previous = tickFrom; + } + + clock.duringTick = false; + clock.now = tickTo; + + if (firstException) { + throw firstException; + } + + return clock.now; + }; + + clock.reset = function reset() { + clock.timers = {}; + }; + + clock.setSystemTime = function setSystemTime(now) { + // determine time difference + var newNow = getEpoch(now); + var difference = newNow - clock.now; + + // update 'system clock' + clock.now = newNow; + + // update timers and intervals to keep them stable + for (var id in clock.timers) { + if (clock.timers.hasOwnProperty(id)) { + var timer = clock.timers[id]; + timer.createdAt += difference; + timer.callAt += difference; + } + } + }; + + return clock; + } + exports.createClock = createClock; + + exports.install = function install(target, now, toFake) { + var i, + l; + + if (typeof target === "number") { + toFake = now; + now = target; + target = null; + } + + if (!target) { + target = global; + } + + var clock = createClock(now); + + clock.uninstall = function () { + uninstall(clock, target); + }; + + clock.methods = toFake || []; + + if (clock.methods.length === 0) { + clock.methods = keys(timers); + } + + for (i = 0, l = clock.methods.length; i < l; i++) { + hijackMethod(target, clock.methods[i], clock); + } + + return clock; + }; + +}(global || this)); + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{}],101:[function(require,module,exports){ +((typeof define === "function" && define.amd && function (m) { define("samsam", m); }) || + (typeof module === "object" && + function (m) { module.exports = m(); }) || // Node + function (m) { this.samsam = m(); } // Browser globals +)(function () { + var o = Object.prototype; + var div = typeof document !== "undefined" && document.createElement("div"); + + function isNaN(value) { + // Unlike global isNaN, this avoids type coercion + // typeof check avoids IE host object issues, hat tip to + // lodash + var val = value; // JsLint thinks value !== value is "weird" + return typeof value === "number" && value !== val; + } + + function getClass(value) { + // Returns the internal [[Class]] by calling Object.prototype.toString + // with the provided value as this. Return value is a string, naming the + // internal class, e.g. "Array" + return o.toString.call(value).split(/[ \]]/)[1]; + } + + /** + * @name samsam.isArguments + * @param Object object + * + * Returns ``true`` if ``object`` is an ``arguments`` object, + * ``false`` otherwise. + */ + function isArguments(object) { + if (getClass(object) === 'Arguments') { return true; } + if (typeof object !== "object" || typeof object.length !== "number" || + getClass(object) === "Array") { + return false; + } + if (typeof object.callee == "function") { return true; } + try { + object[object.length] = 6; + delete object[object.length]; + } catch (e) { + return true; + } + return false; + } + + /** + * @name samsam.isElement + * @param Object object + * + * Returns ``true`` if ``object`` is a DOM element node. Unlike + * Underscore.js/lodash, this function will return ``false`` if ``object`` + * is an *element-like* object, i.e. a regular object with a ``nodeType`` + * property that holds the value ``1``. + */ + function isElement(object) { + if (!object || object.nodeType !== 1 || !div) { return false; } + try { + object.appendChild(div); + object.removeChild(div); + } catch (e) { + return false; + } + return true; + } + + /** + * @name samsam.keys + * @param Object object + * + * Return an array of own property names. + */ + function keys(object) { + var ks = [], prop; + for (prop in object) { + if (o.hasOwnProperty.call(object, prop)) { ks.push(prop); } + } + return ks; + } + + /** + * @name samsam.isDate + * @param Object value + * + * Returns true if the object is a ``Date``, or *date-like*. Duck typing + * of date objects work by checking that the object has a ``getTime`` + * function whose return value equals the return value from the object's + * ``valueOf``. + */ + function isDate(value) { + return typeof value.getTime == "function" && + value.getTime() == value.valueOf(); + } + + /** + * @name samsam.isNegZero + * @param Object value + * + * Returns ``true`` if ``value`` is ``-0``. + */ + function isNegZero(value) { + return value === 0 && 1 / value === -Infinity; + } + + /** + * @name samsam.equal + * @param Object obj1 + * @param Object obj2 + * + * Returns ``true`` if two objects are strictly equal. Compared to + * ``===`` there are two exceptions: + * + * - NaN is considered equal to NaN + * - -0 and +0 are not considered equal + */ + function identical(obj1, obj2) { + if (obj1 === obj2 || (isNaN(obj1) && isNaN(obj2))) { + return obj1 !== 0 || isNegZero(obj1) === isNegZero(obj2); + } + } + + + /** + * @name samsam.deepEqual + * @param Object obj1 + * @param Object obj2 + * + * Deep equal comparison. Two values are "deep equal" if: + * + * - They are equal, according to samsam.identical + * - They are both date objects representing the same time + * - They are both arrays containing elements that are all deepEqual + * - They are objects with the same set of properties, and each property + * in ``obj1`` is deepEqual to the corresponding property in ``obj2`` + * + * Supports cyclic objects. + */ + function deepEqualCyclic(obj1, obj2) { + + // used for cyclic comparison + // contain already visited objects + var objects1 = [], + objects2 = [], + // contain pathes (position in the object structure) + // of the already visited objects + // indexes same as in objects arrays + paths1 = [], + paths2 = [], + // contains combinations of already compared objects + // in the manner: { "$1['ref']$2['ref']": true } + compared = {}; + + /** + * used to check, if the value of a property is an object + * (cyclic logic is only needed for objects) + * only needed for cyclic logic + */ + function isObject(value) { + + if (typeof value === 'object' && value !== null && + !(value instanceof Boolean) && + !(value instanceof Date) && + !(value instanceof Number) && + !(value instanceof RegExp) && + !(value instanceof String)) { + + return true; + } + + return false; + } + + /** + * returns the index of the given object in the + * given objects array, -1 if not contained + * only needed for cyclic logic + */ + function getIndex(objects, obj) { + + var i; + for (i = 0; i < objects.length; i++) { + if (objects[i] === obj) { + return i; + } + } + + return -1; + } + + // does the recursion for the deep equal check + return (function deepEqual(obj1, obj2, path1, path2) { + var type1 = typeof obj1; + var type2 = typeof obj2; + + // == null also matches undefined + if (obj1 === obj2 || + isNaN(obj1) || isNaN(obj2) || + obj1 == null || obj2 == null || + type1 !== "object" || type2 !== "object") { + + return identical(obj1, obj2); + } + + // Elements are only equal if identical(expected, actual) + if (isElement(obj1) || isElement(obj2)) { return false; } + + var isDate1 = isDate(obj1), isDate2 = isDate(obj2); + if (isDate1 || isDate2) { + if (!isDate1 || !isDate2 || obj1.getTime() !== obj2.getTime()) { + return false; + } + } + + if (obj1 instanceof RegExp && obj2 instanceof RegExp) { + if (obj1.toString() !== obj2.toString()) { return false; } + } + + var class1 = getClass(obj1); + var class2 = getClass(obj2); + var keys1 = keys(obj1); + var keys2 = keys(obj2); + + if (isArguments(obj1) || isArguments(obj2)) { + if (obj1.length !== obj2.length) { return false; } + } else { + if (type1 !== type2 || class1 !== class2 || + keys1.length !== keys2.length) { + return false; + } + } + + var key, i, l, + // following vars are used for the cyclic logic + value1, value2, + isObject1, isObject2, + index1, index2, + newPath1, newPath2; + + for (i = 0, l = keys1.length; i < l; i++) { + key = keys1[i]; + if (!o.hasOwnProperty.call(obj2, key)) { + return false; + } + + // Start of the cyclic logic + + value1 = obj1[key]; + value2 = obj2[key]; + + isObject1 = isObject(value1); + isObject2 = isObject(value2); + + // determine, if the objects were already visited + // (it's faster to check for isObject first, than to + // get -1 from getIndex for non objects) + index1 = isObject1 ? getIndex(objects1, value1) : -1; + index2 = isObject2 ? getIndex(objects2, value2) : -1; + + // determine the new pathes of the objects + // - for non cyclic objects the current path will be extended + // by current property name + // - for cyclic objects the stored path is taken + newPath1 = index1 !== -1 + ? paths1[index1] + : path1 + '[' + JSON.stringify(key) + ']'; + newPath2 = index2 !== -1 + ? paths2[index2] + : path2 + '[' + JSON.stringify(key) + ']'; + + // stop recursion if current objects are already compared + if (compared[newPath1 + newPath2]) { + return true; + } + + // remember the current objects and their pathes + if (index1 === -1 && isObject1) { + objects1.push(value1); + paths1.push(newPath1); + } + if (index2 === -1 && isObject2) { + objects2.push(value2); + paths2.push(newPath2); + } + + // remember that the current objects are already compared + if (isObject1 && isObject2) { + compared[newPath1 + newPath2] = true; + } + + // End of cyclic logic + + // neither value1 nor value2 is a cycle + // continue with next level + if (!deepEqual(value1, value2, newPath1, newPath2)) { + return false; + } + } + + return true; + + }(obj1, obj2, '$1', '$2')); + } + + var match; + + function arrayContains(array, subset) { + if (subset.length === 0) { return true; } + var i, l, j, k; + for (i = 0, l = array.length; i < l; ++i) { + if (match(array[i], subset[0])) { + for (j = 0, k = subset.length; j < k; ++j) { + if (!match(array[i + j], subset[j])) { return false; } + } + return true; + } + } + return false; + } + + /** + * @name samsam.match + * @param Object object + * @param Object matcher + * + * Compare arbitrary value ``object`` with matcher. + */ + match = function match(object, matcher) { + if (matcher && typeof matcher.test === "function") { + return matcher.test(object); + } + + if (typeof matcher === "function") { + return matcher(object) === true; + } + + if (typeof matcher === "string") { + matcher = matcher.toLowerCase(); + var notNull = typeof object === "string" || !!object; + return notNull && + (String(object)).toLowerCase().indexOf(matcher) >= 0; + } + + if (typeof matcher === "number") { + return matcher === object; + } + + if (typeof matcher === "boolean") { + return matcher === object; + } + + if (typeof(matcher) === "undefined") { + return typeof(object) === "undefined"; + } + + if (matcher === null) { + return object === null; + } + + if (getClass(object) === "Array" && getClass(matcher) === "Array") { + return arrayContains(object, matcher); + } + + if (matcher && typeof matcher === "object") { + if (matcher === object) { + return true; + } + var prop; + for (prop in matcher) { + var value = object[prop]; + if (typeof value === "undefined" && + typeof object.getAttribute === "function") { + value = object.getAttribute(prop); + } + if (matcher[prop] === null || typeof matcher[prop] === 'undefined') { + if (value !== matcher[prop]) { + return false; + } + } else if (typeof value === "undefined" || !match(value, matcher[prop])) { + return false; + } + } + return true; + } + + throw new Error("Matcher was not a string, a number, a " + + "function, a boolean or an object"); + }; + + return { + isArguments: isArguments, + isElement: isElement, + isDate: isDate, + isNegZero: isNegZero, + identical: identical, + deepEqual: deepEqualCyclic, + match: match, + keys: keys + }; +}); + +},{}],102:[function(require,module,exports){ +// Underscore.js 1.6.0 +// http://underscorejs.org +// (c) 2009-2014 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors +// Underscore may be freely distributed under the MIT license. + +(function() { + + // Baseline setup + // -------------- + + // Establish the root object, `window` in the browser, or `exports` on the server. + var root = this; + + // Save the previous value of the `_` variable. + var previousUnderscore = root._; + + // Establish the object that gets returned to break out of a loop iteration. + var breaker = {}; + + // Save bytes in the minified (but not gzipped) version: + var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype; + + // Create quick reference variables for speed access to core prototypes. + var + push = ArrayProto.push, + slice = ArrayProto.slice, + concat = ArrayProto.concat, + toString = ObjProto.toString, + hasOwnProperty = ObjProto.hasOwnProperty; + + // All **ECMAScript 5** native function implementations that we hope to use + // are declared here. + var + nativeForEach = ArrayProto.forEach, + nativeMap = ArrayProto.map, + nativeReduce = ArrayProto.reduce, + nativeReduceRight = ArrayProto.reduceRight, + nativeFilter = ArrayProto.filter, + nativeEvery = ArrayProto.every, + nativeSome = ArrayProto.some, + nativeIndexOf = ArrayProto.indexOf, + nativeLastIndexOf = ArrayProto.lastIndexOf, + nativeIsArray = Array.isArray, + nativeKeys = Object.keys, + nativeBind = FuncProto.bind; + + // Create a safe reference to the Underscore object for use below. + var _ = function(obj) { + if (obj instanceof _) return obj; + if (!(this instanceof _)) return new _(obj); + this._wrapped = obj; + }; + + // Export the Underscore object for **Node.js**, with + // backwards-compatibility for the old `require()` API. If we're in + // the browser, add `_` as a global object via a string identifier, + // for Closure Compiler "advanced" mode. + if (typeof exports !== 'undefined') { + if (typeof module !== 'undefined' && module.exports) { + exports = module.exports = _; + } + exports._ = _; + } else { + root._ = _; + } + + // Current version. + _.VERSION = '1.6.0'; + + // Collection Functions + // -------------------- + + // The cornerstone, an `each` implementation, aka `forEach`. + // Handles objects with the built-in `forEach`, arrays, and raw objects. + // Delegates to **ECMAScript 5**'s native `forEach` if available. + var each = _.each = _.forEach = function(obj, iterator, context) { + if (obj == null) return obj; + if (nativeForEach && obj.forEach === nativeForEach) { + obj.forEach(iterator, context); + } else if (obj.length === +obj.length) { + for (var i = 0, length = obj.length; i < length; i++) { + if (iterator.call(context, obj[i], i, obj) === breaker) return; + } + } else { + var keys = _.keys(obj); + for (var i = 0, length = keys.length; i < length; i++) { + if (iterator.call(context, obj[keys[i]], keys[i], obj) === breaker) return; + } + } + return obj; + }; + + // Return the results of applying the iterator to each element. + // Delegates to **ECMAScript 5**'s native `map` if available. + _.map = _.collect = function(obj, iterator, context) { + var results = []; + if (obj == null) return results; + if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context); + each(obj, function(value, index, list) { + results.push(iterator.call(context, value, index, list)); + }); + return results; + }; + + var reduceError = 'Reduce of empty array with no initial value'; + + // **Reduce** builds up a single result from a list of values, aka `inject`, + // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available. + _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) { + var initial = arguments.length > 2; + if (obj == null) obj = []; + if (nativeReduce && obj.reduce === nativeReduce) { + if (context) iterator = _.bind(iterator, context); + return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator); + } + each(obj, function(value, index, list) { + if (!initial) { + memo = value; + initial = true; + } else { + memo = iterator.call(context, memo, value, index, list); + } + }); + if (!initial) throw new TypeError(reduceError); + return memo; + }; + + // The right-associative version of reduce, also known as `foldr`. + // Delegates to **ECMAScript 5**'s native `reduceRight` if available. + _.reduceRight = _.foldr = function(obj, iterator, memo, context) { + var initial = arguments.length > 2; + if (obj == null) obj = []; + if (nativeReduceRight && obj.reduceRight === nativeReduceRight) { + if (context) iterator = _.bind(iterator, context); + return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator); + } + var length = obj.length; + if (length !== +length) { + var keys = _.keys(obj); + length = keys.length; + } + each(obj, function(value, index, list) { + index = keys ? keys[--length] : --length; + if (!initial) { + memo = obj[index]; + initial = true; + } else { + memo = iterator.call(context, memo, obj[index], index, list); + } + }); + if (!initial) throw new TypeError(reduceError); + return memo; + }; + + // Return the first value which passes a truth test. Aliased as `detect`. + _.find = _.detect = function(obj, predicate, context) { + var result; + any(obj, function(value, index, list) { + if (predicate.call(context, value, index, list)) { + result = value; + return true; + } + }); + return result; + }; + + // Return all the elements that pass a truth test. + // Delegates to **ECMAScript 5**'s native `filter` if available. + // Aliased as `select`. + _.filter = _.select = function(obj, predicate, context) { + var results = []; + if (obj == null) return results; + if (nativeFilter && obj.filter === nativeFilter) return obj.filter(predicate, context); + each(obj, function(value, index, list) { + if (predicate.call(context, value, index, list)) results.push(value); + }); + return results; + }; + + // Return all the elements for which a truth test fails. + _.reject = function(obj, predicate, context) { + return _.filter(obj, function(value, index, list) { + return !predicate.call(context, value, index, list); + }, context); + }; + + // Determine whether all of the elements match a truth test. + // Delegates to **ECMAScript 5**'s native `every` if available. + // Aliased as `all`. + _.every = _.all = function(obj, predicate, context) { + predicate || (predicate = _.identity); + var result = true; + if (obj == null) return result; + if (nativeEvery && obj.every === nativeEvery) return obj.every(predicate, context); + each(obj, function(value, index, list) { + if (!(result = result && predicate.call(context, value, index, list))) return breaker; + }); + return !!result; + }; + + // Determine if at least one element in the object matches a truth test. + // Delegates to **ECMAScript 5**'s native `some` if available. + // Aliased as `any`. + var any = _.some = _.any = function(obj, predicate, context) { + predicate || (predicate = _.identity); + var result = false; + if (obj == null) return result; + if (nativeSome && obj.some === nativeSome) return obj.some(predicate, context); + each(obj, function(value, index, list) { + if (result || (result = predicate.call(context, value, index, list))) return breaker; + }); + return !!result; + }; + + // Determine if the array or object contains a given value (using `===`). + // Aliased as `include`. + _.contains = _.include = function(obj, target) { + if (obj == null) return false; + if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1; + return any(obj, function(value) { + return value === target; + }); + }; + + // Invoke a method (with arguments) on every item in a collection. + _.invoke = function(obj, method) { + var args = slice.call(arguments, 2); + var isFunc = _.isFunction(method); + return _.map(obj, function(value) { + return (isFunc ? method : value[method]).apply(value, args); + }); + }; + + // Convenience version of a common use case of `map`: fetching a property. + _.pluck = function(obj, key) { + return _.map(obj, _.property(key)); + }; + + // Convenience version of a common use case of `filter`: selecting only objects + // containing specific `key:value` pairs. + _.where = function(obj, attrs) { + return _.filter(obj, _.matches(attrs)); + }; + + // Convenience version of a common use case of `find`: getting the first object + // containing specific `key:value` pairs. + _.findWhere = function(obj, attrs) { + return _.find(obj, _.matches(attrs)); + }; + + // Return the maximum element or (element-based computation). + // Can't optimize arrays of integers longer than 65,535 elements. + // See [WebKit Bug 80797](https://bugs.webkit.org/show_bug.cgi?id=80797) + _.max = function(obj, iterator, context) { + if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) { + return Math.max.apply(Math, obj); + } + var result = -Infinity, lastComputed = -Infinity; + each(obj, function(value, index, list) { + var computed = iterator ? iterator.call(context, value, index, list) : value; + if (computed > lastComputed) { + result = value; + lastComputed = computed; + } + }); + return result; + }; + + // Return the minimum element (or element-based computation). + _.min = function(obj, iterator, context) { + if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) { + return Math.min.apply(Math, obj); + } + var result = Infinity, lastComputed = Infinity; + each(obj, function(value, index, list) { + var computed = iterator ? iterator.call(context, value, index, list) : value; + if (computed < lastComputed) { + result = value; + lastComputed = computed; + } + }); + return result; + }; + + // Shuffle an array, using the modern version of the + // [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle). + _.shuffle = function(obj) { + var rand; + var index = 0; + var shuffled = []; + each(obj, function(value) { + rand = _.random(index++); + shuffled[index - 1] = shuffled[rand]; + shuffled[rand] = value; + }); + return shuffled; + }; + + // Sample **n** random values from a collection. + // If **n** is not specified, returns a single random element. + // The internal `guard` argument allows it to work with `map`. + _.sample = function(obj, n, guard) { + if (n == null || guard) { + if (obj.length !== +obj.length) obj = _.values(obj); + return obj[_.random(obj.length - 1)]; + } + return _.shuffle(obj).slice(0, Math.max(0, n)); + }; + + // An internal function to generate lookup iterators. + var lookupIterator = function(value) { + if (value == null) return _.identity; + if (_.isFunction(value)) return value; + return _.property(value); + }; + + // Sort the object's values by a criterion produced by an iterator. + _.sortBy = function(obj, iterator, context) { + iterator = lookupIterator(iterator); + return _.pluck(_.map(obj, function(value, index, list) { + return { + value: value, + index: index, + criteria: iterator.call(context, value, index, list) + }; + }).sort(function(left, right) { + var a = left.criteria; + var b = right.criteria; + if (a !== b) { + if (a > b || a === void 0) return 1; + if (a < b || b === void 0) return -1; + } + return left.index - right.index; + }), 'value'); + }; + + // An internal function used for aggregate "group by" operations. + var group = function(behavior) { + return function(obj, iterator, context) { + var result = {}; + iterator = lookupIterator(iterator); + each(obj, function(value, index) { + var key = iterator.call(context, value, index, obj); + behavior(result, key, value); + }); + return result; + }; + }; + + // Groups the object's values by a criterion. Pass either a string attribute + // to group by, or a function that returns the criterion. + _.groupBy = group(function(result, key, value) { + _.has(result, key) ? result[key].push(value) : result[key] = [value]; + }); + + // Indexes the object's values by a criterion, similar to `groupBy`, but for + // when you know that your index values will be unique. + _.indexBy = group(function(result, key, value) { + result[key] = value; + }); + + // Counts instances of an object that group by a certain criterion. Pass + // either a string attribute to count by, or a function that returns the + // criterion. + _.countBy = group(function(result, key) { + _.has(result, key) ? result[key]++ : result[key] = 1; + }); + + // Use a comparator function to figure out the smallest index at which + // an object should be inserted so as to maintain order. Uses binary search. + _.sortedIndex = function(array, obj, iterator, context) { + iterator = lookupIterator(iterator); + var value = iterator.call(context, obj); + var low = 0, high = array.length; + while (low < high) { + var mid = (low + high) >>> 1; + iterator.call(context, array[mid]) < value ? low = mid + 1 : high = mid; + } + return low; + }; + + // Safely create a real, live array from anything iterable. + _.toArray = function(obj) { + if (!obj) return []; + if (_.isArray(obj)) return slice.call(obj); + if (obj.length === +obj.length) return _.map(obj, _.identity); + return _.values(obj); + }; + + // Return the number of elements in an object. + _.size = function(obj) { + if (obj == null) return 0; + return (obj.length === +obj.length) ? obj.length : _.keys(obj).length; + }; + + // Array Functions + // --------------- + + // Get the first element of an array. Passing **n** will return the first N + // values in the array. Aliased as `head` and `take`. The **guard** check + // allows it to work with `_.map`. + _.first = _.head = _.take = function(array, n, guard) { + if (array == null) return void 0; + if ((n == null) || guard) return array[0]; + if (n < 0) return []; + return slice.call(array, 0, n); + }; + + // Returns everything but the last entry of the array. Especially useful on + // the arguments object. Passing **n** will return all the values in + // the array, excluding the last N. The **guard** check allows it to work with + // `_.map`. + _.initial = function(array, n, guard) { + return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n)); + }; + + // Get the last element of an array. Passing **n** will return the last N + // values in the array. The **guard** check allows it to work with `_.map`. + _.last = function(array, n, guard) { + if (array == null) return void 0; + if ((n == null) || guard) return array[array.length - 1]; + return slice.call(array, Math.max(array.length - n, 0)); + }; + + // Returns everything but the first entry of the array. Aliased as `tail` and `drop`. + // Especially useful on the arguments object. Passing an **n** will return + // the rest N values in the array. The **guard** + // check allows it to work with `_.map`. + _.rest = _.tail = _.drop = function(array, n, guard) { + return slice.call(array, (n == null) || guard ? 1 : n); + }; + + // Trim out all falsy values from an array. + _.compact = function(array) { + return _.filter(array, _.identity); + }; + + // Internal implementation of a recursive `flatten` function. + var flatten = function(input, shallow, output) { + if (shallow && _.every(input, _.isArray)) { + return concat.apply(output, input); + } + each(input, function(value) { + if (_.isArray(value) || _.isArguments(value)) { + shallow ? push.apply(output, value) : flatten(value, shallow, output); + } else { + output.push(value); + } + }); + return output; + }; + + // Flatten out an array, either recursively (by default), or just one level. + _.flatten = function(array, shallow) { + return flatten(array, shallow, []); + }; + + // Return a version of the array that does not contain the specified value(s). + _.without = function(array) { + return _.difference(array, slice.call(arguments, 1)); + }; + + // Split an array into two arrays: one whose elements all satisfy the given + // predicate, and one whose elements all do not satisfy the predicate. + _.partition = function(array, predicate) { + var pass = [], fail = []; + each(array, function(elem) { + (predicate(elem) ? pass : fail).push(elem); + }); + return [pass, fail]; + }; + + // Produce a duplicate-free version of the array. If the array has already + // been sorted, you have the option of using a faster algorithm. + // Aliased as `unique`. + _.uniq = _.unique = function(array, isSorted, iterator, context) { + if (_.isFunction(isSorted)) { + context = iterator; + iterator = isSorted; + isSorted = false; + } + var initial = iterator ? _.map(array, iterator, context) : array; + var results = []; + var seen = []; + each(initial, function(value, index) { + if (isSorted ? (!index || seen[seen.length - 1] !== value) : !_.contains(seen, value)) { + seen.push(value); + results.push(array[index]); + } + }); + return results; + }; + + // Produce an array that contains the union: each distinct element from all of + // the passed-in arrays. + _.union = function() { + return _.uniq(_.flatten(arguments, true)); + }; + + // Produce an array that contains every item shared between all the + // passed-in arrays. + _.intersection = function(array) { + var rest = slice.call(arguments, 1); + return _.filter(_.uniq(array), function(item) { + return _.every(rest, function(other) { + return _.contains(other, item); + }); + }); + }; + + // Take the difference between one array and a number of other arrays. + // Only the elements present in just the first array will remain. + _.difference = function(array) { + var rest = concat.apply(ArrayProto, slice.call(arguments, 1)); + return _.filter(array, function(value){ return !_.contains(rest, value); }); + }; + + // Zip together multiple lists into a single array -- elements that share + // an index go together. + _.zip = function() { + var length = _.max(_.pluck(arguments, 'length').concat(0)); + var results = new Array(length); + for (var i = 0; i < length; i++) { + results[i] = _.pluck(arguments, '' + i); + } + return results; + }; + + // Converts lists into objects. Pass either a single array of `[key, value]` + // pairs, or two parallel arrays of the same length -- one of keys, and one of + // the corresponding values. + _.object = function(list, values) { + if (list == null) return {}; + var result = {}; + for (var i = 0, length = list.length; i < length; i++) { + if (values) { + result[list[i]] = values[i]; + } else { + result[list[i][0]] = list[i][1]; + } + } + return result; + }; + + // If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**), + // we need this function. Return the position of the first occurrence of an + // item in an array, or -1 if the item is not included in the array. + // Delegates to **ECMAScript 5**'s native `indexOf` if available. + // If the array is large and already in sort order, pass `true` + // for **isSorted** to use binary search. + _.indexOf = function(array, item, isSorted) { + if (array == null) return -1; + var i = 0, length = array.length; + if (isSorted) { + if (typeof isSorted == 'number') { + i = (isSorted < 0 ? Math.max(0, length + isSorted) : isSorted); + } else { + i = _.sortedIndex(array, item); + return array[i] === item ? i : -1; + } + } + if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted); + for (; i < length; i++) if (array[i] === item) return i; + return -1; + }; + + // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available. + _.lastIndexOf = function(array, item, from) { + if (array == null) return -1; + var hasIndex = from != null; + if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) { + return hasIndex ? array.lastIndexOf(item, from) : array.lastIndexOf(item); + } + var i = (hasIndex ? from : array.length); + while (i--) if (array[i] === item) return i; + return -1; + }; + + // Generate an integer Array containing an arithmetic progression. A port of + // the native Python `range()` function. See + // [the Python documentation](http://docs.python.org/library/functions.html#range). + _.range = function(start, stop, step) { + if (arguments.length <= 1) { + stop = start || 0; + start = 0; + } + step = arguments[2] || 1; + + var length = Math.max(Math.ceil((stop - start) / step), 0); + var idx = 0; + var range = new Array(length); + + while(idx < length) { + range[idx++] = start; + start += step; + } + + return range; + }; + + // Function (ahem) Functions + // ------------------ + + // Reusable constructor function for prototype setting. + var ctor = function(){}; + + // Create a function bound to a given object (assigning `this`, and arguments, + // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if + // available. + _.bind = function(func, context) { + var args, bound; + if (nativeBind && func.bind === nativeBind) return nativeBind.apply(func, slice.call(arguments, 1)); + if (!_.isFunction(func)) throw new TypeError; + args = slice.call(arguments, 2); + return bound = function() { + if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments))); + ctor.prototype = func.prototype; + var self = new ctor; + ctor.prototype = null; + var result = func.apply(self, args.concat(slice.call(arguments))); + if (Object(result) === result) return result; + return self; + }; + }; + + // Partially apply a function by creating a version that has had some of its + // arguments pre-filled, without changing its dynamic `this` context. _ acts + // as a placeholder, allowing any combination of arguments to be pre-filled. + _.partial = function(func) { + var boundArgs = slice.call(arguments, 1); + return function() { + var position = 0; + var args = boundArgs.slice(); + for (var i = 0, length = args.length; i < length; i++) { + if (args[i] === _) args[i] = arguments[position++]; + } + while (position < arguments.length) args.push(arguments[position++]); + return func.apply(this, args); + }; + }; + + // Bind a number of an object's methods to that object. Remaining arguments + // are the method names to be bound. Useful for ensuring that all callbacks + // defined on an object belong to it. + _.bindAll = function(obj) { + var funcs = slice.call(arguments, 1); + if (funcs.length === 0) throw new Error('bindAll must be passed function names'); + each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); }); + return obj; + }; + + // Memoize an expensive function by storing its results. + _.memoize = function(func, hasher) { + var memo = {}; + hasher || (hasher = _.identity); + return function() { + var key = hasher.apply(this, arguments); + return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments)); + }; + }; + + // Delays a function for the given number of milliseconds, and then calls + // it with the arguments supplied. + _.delay = function(func, wait) { + var args = slice.call(arguments, 2); + return setTimeout(function(){ return func.apply(null, args); }, wait); + }; + + // Defers a function, scheduling it to run after the current call stack has + // cleared. + _.defer = function(func) { + return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1))); + }; + + // Returns a function, that, when invoked, will only be triggered at most once + // during a given window of time. Normally, the throttled function will run + // as much as it can, without ever going more than once per `wait` duration; + // but if you'd like to disable the execution on the leading edge, pass + // `{leading: false}`. To disable execution on the trailing edge, ditto. + _.throttle = function(func, wait, options) { + var context, args, result; + var timeout = null; + var previous = 0; + options || (options = {}); + var later = function() { + previous = options.leading === false ? 0 : _.now(); + timeout = null; + result = func.apply(context, args); + context = args = null; + }; + return function() { + var now = _.now(); + if (!previous && options.leading === false) previous = now; + var remaining = wait - (now - previous); + context = this; + args = arguments; + if (remaining <= 0) { + clearTimeout(timeout); + timeout = null; + previous = now; + result = func.apply(context, args); + context = args = null; + } else if (!timeout && options.trailing !== false) { + timeout = setTimeout(later, remaining); + } + return result; + }; + }; + + // Returns a function, that, as long as it continues to be invoked, will not + // be triggered. The function will be called after it stops being called for + // N milliseconds. If `immediate` is passed, trigger the function on the + // leading edge, instead of the trailing. + _.debounce = function(func, wait, immediate) { + var timeout, args, context, timestamp, result; + + var later = function() { + var last = _.now() - timestamp; + if (last < wait) { + timeout = setTimeout(later, wait - last); + } else { + timeout = null; + if (!immediate) { + result = func.apply(context, args); + context = args = null; + } + } + }; + + return function() { + context = this; + args = arguments; + timestamp = _.now(); + var callNow = immediate && !timeout; + if (!timeout) { + timeout = setTimeout(later, wait); + } + if (callNow) { + result = func.apply(context, args); + context = args = null; + } + + return result; + }; + }; + + // Returns a function that will be executed at most one time, no matter how + // often you call it. Useful for lazy initialization. + _.once = function(func) { + var ran = false, memo; + return function() { + if (ran) return memo; + ran = true; + memo = func.apply(this, arguments); + func = null; + return memo; + }; + }; + + // Returns the first function passed as an argument to the second, + // allowing you to adjust arguments, run code before and after, and + // conditionally execute the original function. + _.wrap = function(func, wrapper) { + return _.partial(wrapper, func); + }; + + // Returns a function that is the composition of a list of functions, each + // consuming the return value of the function that follows. + _.compose = function() { + var funcs = arguments; + return function() { + var args = arguments; + for (var i = funcs.length - 1; i >= 0; i--) { + args = [funcs[i].apply(this, args)]; + } + return args[0]; + }; + }; + + // Returns a function that will only be executed after being called N times. + _.after = function(times, func) { + return function() { + if (--times < 1) { + return func.apply(this, arguments); + } + }; + }; + + // Object Functions + // ---------------- + + // Retrieve the names of an object's properties. + // Delegates to **ECMAScript 5**'s native `Object.keys` + _.keys = function(obj) { + if (!_.isObject(obj)) return []; + if (nativeKeys) return nativeKeys(obj); + var keys = []; + for (var key in obj) if (_.has(obj, key)) keys.push(key); + return keys; + }; + + // Retrieve the values of an object's properties. + _.values = function(obj) { + var keys = _.keys(obj); + var length = keys.length; + var values = new Array(length); + for (var i = 0; i < length; i++) { + values[i] = obj[keys[i]]; + } + return values; + }; + + // Convert an object into a list of `[key, value]` pairs. + _.pairs = function(obj) { + var keys = _.keys(obj); + var length = keys.length; + var pairs = new Array(length); + for (var i = 0; i < length; i++) { + pairs[i] = [keys[i], obj[keys[i]]]; + } + return pairs; + }; + + // Invert the keys and values of an object. The values must be serializable. + _.invert = function(obj) { + var result = {}; + var keys = _.keys(obj); + for (var i = 0, length = keys.length; i < length; i++) { + result[obj[keys[i]]] = keys[i]; + } + return result; + }; + + // Return a sorted list of the function names available on the object. + // Aliased as `methods` + _.functions = _.methods = function(obj) { + var names = []; + for (var key in obj) { + if (_.isFunction(obj[key])) names.push(key); + } + return names.sort(); + }; + + // Extend a given object with all the properties in passed-in object(s). + _.extend = function(obj) { + each(slice.call(arguments, 1), function(source) { + if (source) { + for (var prop in source) { + obj[prop] = source[prop]; + } + } + }); + return obj; + }; + + // Return a copy of the object only containing the whitelisted properties. + _.pick = function(obj) { + var copy = {}; + var keys = concat.apply(ArrayProto, slice.call(arguments, 1)); + each(keys, function(key) { + if (key in obj) copy[key] = obj[key]; + }); + return copy; + }; + + // Return a copy of the object without the blacklisted properties. + _.omit = function(obj) { + var copy = {}; + var keys = concat.apply(ArrayProto, slice.call(arguments, 1)); + for (var key in obj) { + if (!_.contains(keys, key)) copy[key] = obj[key]; + } + return copy; + }; + + // Fill in a given object with default properties. + _.defaults = function(obj) { + each(slice.call(arguments, 1), function(source) { + if (source) { + for (var prop in source) { + if (obj[prop] === void 0) obj[prop] = source[prop]; + } + } + }); + return obj; + }; + + // Create a (shallow-cloned) duplicate of an object. + _.clone = function(obj) { + if (!_.isObject(obj)) return obj; + return _.isArray(obj) ? obj.slice() : _.extend({}, obj); + }; + + // Invokes interceptor with the obj, and then returns obj. + // The primary purpose of this method is to "tap into" a method chain, in + // order to perform operations on intermediate results within the chain. + _.tap = function(obj, interceptor) { + interceptor(obj); + return obj; + }; + + // Internal recursive comparison function for `isEqual`. + var eq = function(a, b, aStack, bStack) { + // Identical objects are equal. `0 === -0`, but they aren't identical. + // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal). + if (a === b) return a !== 0 || 1 / a == 1 / b; + // A strict comparison is necessary because `null == undefined`. + if (a == null || b == null) return a === b; + // Unwrap any wrapped objects. + if (a instanceof _) a = a._wrapped; + if (b instanceof _) b = b._wrapped; + // Compare `[[Class]]` names. + var className = toString.call(a); + if (className != toString.call(b)) return false; + switch (className) { + // Strings, numbers, dates, and booleans are compared by value. + case '[object String]': + // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is + // equivalent to `new String("5")`. + return a == String(b); + case '[object Number]': + // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for + // other numeric values. + return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b); + case '[object Date]': + case '[object Boolean]': + // Coerce dates and booleans to numeric primitive values. Dates are compared by their + // millisecond representations. Note that invalid dates with millisecond representations + // of `NaN` are not equivalent. + return +a == +b; + // RegExps are compared by their source patterns and flags. + case '[object RegExp]': + return a.source == b.source && + a.global == b.global && + a.multiline == b.multiline && + a.ignoreCase == b.ignoreCase; + } + if (typeof a != 'object' || typeof b != 'object') return false; + // Assume equality for cyclic structures. The algorithm for detecting cyclic + // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. + var length = aStack.length; + while (length--) { + // Linear search. Performance is inversely proportional to the number of + // unique nested structures. + if (aStack[length] == a) return bStack[length] == b; + } + // Objects with different constructors are not equivalent, but `Object`s + // from different frames are. + var aCtor = a.constructor, bCtor = b.constructor; + if (aCtor !== bCtor && !(_.isFunction(aCtor) && (aCtor instanceof aCtor) && + _.isFunction(bCtor) && (bCtor instanceof bCtor)) + && ('constructor' in a && 'constructor' in b)) { + return false; + } + // Add the first object to the stack of traversed objects. + aStack.push(a); + bStack.push(b); + var size = 0, result = true; + // Recursively compare objects and arrays. + if (className == '[object Array]') { + // Compare array lengths to determine if a deep comparison is necessary. + size = a.length; + result = size == b.length; + if (result) { + // Deep compare the contents, ignoring non-numeric properties. + while (size--) { + if (!(result = eq(a[size], b[size], aStack, bStack))) break; + } + } + } else { + // Deep compare objects. + for (var key in a) { + if (_.has(a, key)) { + // Count the expected number of properties. + size++; + // Deep compare each member. + if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break; + } + } + // Ensure that both objects contain the same number of properties. + if (result) { + for (key in b) { + if (_.has(b, key) && !(size--)) break; + } + result = !size; + } + } + // Remove the first object from the stack of traversed objects. + aStack.pop(); + bStack.pop(); + return result; + }; + + // Perform a deep comparison to check if two objects are equal. + _.isEqual = function(a, b) { + return eq(a, b, [], []); + }; + + // Is a given array, string, or object empty? + // An "empty" object has no enumerable own-properties. + _.isEmpty = function(obj) { + if (obj == null) return true; + if (_.isArray(obj) || _.isString(obj)) return obj.length === 0; + for (var key in obj) if (_.has(obj, key)) return false; + return true; + }; + + // Is a given value a DOM element? + _.isElement = function(obj) { + return !!(obj && obj.nodeType === 1); + }; + + // Is a given value an array? + // Delegates to ECMA5's native Array.isArray + _.isArray = nativeIsArray || function(obj) { + return toString.call(obj) == '[object Array]'; + }; + + // Is a given variable an object? + _.isObject = function(obj) { + return obj === Object(obj); + }; + + // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp. + each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) { + _['is' + name] = function(obj) { + return toString.call(obj) == '[object ' + name + ']'; + }; + }); + + // Define a fallback version of the method in browsers (ahem, IE), where + // there isn't any inspectable "Arguments" type. + if (!_.isArguments(arguments)) { + _.isArguments = function(obj) { + return !!(obj && _.has(obj, 'callee')); + }; + } + + // Optimize `isFunction` if appropriate. + if (typeof (/./) !== 'function') { + _.isFunction = function(obj) { + return typeof obj === 'function'; + }; + } + + // Is a given object a finite number? + _.isFinite = function(obj) { + return isFinite(obj) && !isNaN(parseFloat(obj)); + }; + + // Is the given value `NaN`? (NaN is the only number which does not equal itself). + _.isNaN = function(obj) { + return _.isNumber(obj) && obj != +obj; + }; + + // Is a given value a boolean? + _.isBoolean = function(obj) { + return obj === true || obj === false || toString.call(obj) == '[object Boolean]'; + }; + + // Is a given value equal to null? + _.isNull = function(obj) { + return obj === null; + }; + + // Is a given variable undefined? + _.isUndefined = function(obj) { + return obj === void 0; + }; + + // Shortcut function for checking if an object has a given property directly + // on itself (in other words, not on a prototype). + _.has = function(obj, key) { + return hasOwnProperty.call(obj, key); + }; + + // Utility Functions + // ----------------- + + // Run Underscore.js in *noConflict* mode, returning the `_` variable to its + // previous owner. Returns a reference to the Underscore object. + _.noConflict = function() { + root._ = previousUnderscore; + return this; + }; + + // Keep the identity function around for default iterators. + _.identity = function(value) { + return value; + }; + + _.constant = function(value) { + return function () { + return value; + }; + }; + + _.property = function(key) { + return function(obj) { + return obj[key]; + }; + }; + + // Returns a predicate for checking whether an object has a given set of `key:value` pairs. + _.matches = function(attrs) { + return function(obj) { + if (obj === attrs) return true; //avoid comparing an object to itself. + for (var key in attrs) { + if (attrs[key] !== obj[key]) + return false; + } + return true; + } + }; + + // Run a function **n** times. + _.times = function(n, iterator, context) { + var accum = Array(Math.max(0, n)); + for (var i = 0; i < n; i++) accum[i] = iterator.call(context, i); + return accum; + }; + + // Return a random integer between min and max (inclusive). + _.random = function(min, max) { + if (max == null) { + max = min; + min = 0; + } + return min + Math.floor(Math.random() * (max - min + 1)); + }; + + // A (possibly faster) way to get the current timestamp as an integer. + _.now = Date.now || function() { return new Date().getTime(); }; + + // List of HTML entities for escaping. + var entityMap = { + escape: { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''' + } + }; + entityMap.unescape = _.invert(entityMap.escape); + + // Regexes containing the keys and values listed immediately above. + var entityRegexes = { + escape: new RegExp('[' + _.keys(entityMap.escape).join('') + ']', 'g'), + unescape: new RegExp('(' + _.keys(entityMap.unescape).join('|') + ')', 'g') + }; + + // Functions for escaping and unescaping strings to/from HTML interpolation. + _.each(['escape', 'unescape'], function(method) { + _[method] = function(string) { + if (string == null) return ''; + return ('' + string).replace(entityRegexes[method], function(match) { + return entityMap[method][match]; + }); + }; + }); + + // If the value of the named `property` is a function then invoke it with the + // `object` as context; otherwise, return it. + _.result = function(object, property) { + if (object == null) return void 0; + var value = object[property]; + return _.isFunction(value) ? value.call(object) : value; + }; + + // Add your own custom functions to the Underscore object. + _.mixin = function(obj) { + each(_.functions(obj), function(name) { + var func = _[name] = obj[name]; + _.prototype[name] = function() { + var args = [this._wrapped]; + push.apply(args, arguments); + return result.call(this, func.apply(_, args)); + }; + }); + }; + + // Generate a unique integer id (unique within the entire client session). + // Useful for temporary DOM ids. + var idCounter = 0; + _.uniqueId = function(prefix) { + var id = ++idCounter + ''; + return prefix ? prefix + id : id; + }; + + // By default, Underscore uses ERB-style template delimiters, change the + // following template settings to use alternative delimiters. + _.templateSettings = { + evaluate : /<%([\s\S]+?)%>/g, + interpolate : /<%=([\s\S]+?)%>/g, + escape : /<%-([\s\S]+?)%>/g + }; + + // When customizing `templateSettings`, if you don't want to define an + // interpolation, evaluation or escaping regex, we need one that is + // guaranteed not to match. + var noMatch = /(.)^/; + + // Certain characters need to be escaped so that they can be put into a + // string literal. + var escapes = { + "'": "'", + '\\': '\\', + '\r': 'r', + '\n': 'n', + '\t': 't', + '\u2028': 'u2028', + '\u2029': 'u2029' + }; + + var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g; + + // JavaScript micro-templating, similar to John Resig's implementation. + // Underscore templating handles arbitrary delimiters, preserves whitespace, + // and correctly escapes quotes within interpolated code. + _.template = function(text, data, settings) { + var render; + settings = _.defaults({}, settings, _.templateSettings); + + // Combine delimiters into one regular expression via alternation. + var matcher = new RegExp([ + (settings.escape || noMatch).source, + (settings.interpolate || noMatch).source, + (settings.evaluate || noMatch).source + ].join('|') + '|$', 'g'); + + // Compile the template source, escaping string literals appropriately. + var index = 0; + var source = "__p+='"; + text.replace(matcher, function(match, escape, interpolate, evaluate, offset) { + source += text.slice(index, offset) + .replace(escaper, function(match) { return '\\' + escapes[match]; }); + + if (escape) { + source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'"; + } + if (interpolate) { + source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'"; + } + if (evaluate) { + source += "';\n" + evaluate + "\n__p+='"; + } + index = offset + match.length; + return match; + }); + source += "';\n"; + + // If a variable is not specified, place data values in local scope. + if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n'; + + source = "var __t,__p='',__j=Array.prototype.join," + + "print=function(){__p+=__j.call(arguments,'');};\n" + + source + "return __p;\n"; + + try { + render = new Function(settings.variable || 'obj', '_', source); + } catch (e) { + e.source = source; + throw e; + } + + if (data) return render(data, _); + var template = function(data) { + return render.call(this, data, _); + }; + + // Provide the compiled function source as a convenience for precompilation. + template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}'; + + return template; + }; + + // Add a "chain" function, which will delegate to the wrapper. + _.chain = function(obj) { + return _(obj).chain(); + }; + + // OOP + // --------------- + // If Underscore is called as a function, it returns a wrapped object that + // can be used OO-style. This wrapper holds altered versions of all the + // underscore functions. Wrapped objects may be chained. + + // Helper function to continue chaining intermediate results. + var result = function(obj) { + return this._chain ? _(obj).chain() : obj; + }; + + // Add all of the Underscore functions to the wrapper object. + _.mixin(_); + + // Add all mutator Array functions to the wrapper. + each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) { + var method = ArrayProto[name]; + _.prototype[name] = function() { + var obj = this._wrapped; + method.apply(obj, arguments); + if ((name == 'shift' || name == 'splice') && obj.length === 0) delete obj[0]; + return result.call(this, obj); + }; + }); + + // Add all accessor Array functions to the wrapper. + each(['concat', 'join', 'slice'], function(name) { + var method = ArrayProto[name]; + _.prototype[name] = function() { + return result.call(this, method.apply(this._wrapped, arguments)); + }; + }); + + _.extend(_.prototype, { + + // Start chaining a wrapped Underscore object. + chain: function() { + this._chain = true; + return this; + }, + + // Extracts the result from a wrapped and chained object. + value: function() { + return this._wrapped; + } + + }); + + // AMD registration happens at the end for compatibility with AMD loaders + // that may not enforce next-turn semantics on modules. Even though general + // practice for AMD registration is to be anonymous, underscore registers + // as a named module because, like jQuery, it is a base library that is + // popular enough to be bundled in a third party lib, but not be part of + // an AMD load request. Those cases could generate an error when an + // anonymous define() is called outside of a loader request. + if (typeof define === 'function' && define.amd) { + define('underscore', [], function() { + return _; + }); + } +}).call(this); + +},{}],103:[function(require,module,exports){ +var torque = require('../lib/torque'); +var sinon = require('sinon'); +require('phantomjs-polyfill'); + +QUnit.module('animator'); + +test('time moves', function(assert) { + var done = assert.async(); + var animatora = new torque.Animator(function(){}, {steps: 500, animationDuration: 2}); + animatora.start(); + setTimeout(function(){ + assert.notEqual(animatora._time, 0); + done(); + }, 20) + animatora.pause(); +}); + +test("rescale should resume animation if previously playing", function(assert){ + var animator = new torque.Animator(function(){}, {steps: 500, animationDuration: 2}); + animator.toggle(); + animator.rescale(); + assert.ok(animator.running); + animator.pause() +}); + +test("rescale shouldn't resume animation if previously paused", function(assert){ + var animator = new torque.Animator(function(){}, {steps: 500, animationDuration: 2}); + animator.pause(); + animator.rescale(); + assert.notOk(animator.running); +}); + +test("onStart runs properly", function(assert){ + var animator = new torque.Animator(function(){}, {steps: 500, animationDuration: 2}); + animator.options.onStop = function(){ + assert.ok(true); + animator.pause(); + }; + animator.stop(); +}); + +test("stop should take the pointer to position zero", function(assert){ + var animator = new torque.Animator(function(){}, {steps: 500, animationDuration: 2}); + animator.stop() + assert.equal(animator._time, 0); +}); + +test("stop should call onStop", function(assert){ + var animator = new torque.Animator(function(){}, {steps: 500, animationDuration: 2}); + animator.options.onStop = function(){ + assert.ok(true); + animator.pause(); + }; + animator.stop(); +}); + +test("altering steps should rescale", function(assert){ + var animator = new torque.Animator(function(){}, {steps: 500, animationDuration: 2}); + sinon.spy(animator, "rescale"); + animator.steps(600); + assert.ok(animator.rescale.calledOnce); +}); + +test("tick should set time to zero if steps are bigger than range", function(assert){ + var done = assert.async(); + var animatorb = new torque.Animator(function(){}, {steps: 500, animationDuration: 2}); + animatorb.start(); + animatorb.step(800); + setTimeout(function(){ + console.log(animatorb.step()); + assert.ok(animatorb.step() < 800); + done(); + }, 20); + animatorb.pause(); +}); + +test("tick should pause animation on end if loop is disabled", function(assert){ + var animator = new torque.Animator(function(){}, {steps: 500, animationDuration: 2}); + animator.options.loop = false; + animator.toggle(); + animator.step(600); + assert.equal(animator._time,animator.options.animationDuration); + +}); + +},{"../lib/torque":10,"phantomjs-polyfill":72,"sinon":74}],104:[function(require,module,exports){ +var torque = require('../lib/torque'); + +QUnit.module('torque.core.Event'); + + function TestObj(){} + TestObj.prototype = torque.Event; + + test("on", function() { + var called = false; + var o = new TestObj(); + o.on('test', function() { called = true}); + o.trigger('test'); + equal(called, true); + }); + + test("off", function() { + var called = false; + var o = new TestObj(); + function fn() { called = true; } + o.on('test', fn); + equal(o.callbacks('test').length, 1); + o.off('test', fn); + equal(o.callbacks('test').length, 0); + o.trigger('test'); + equal(called, false); + o.on('test', fn); + o.off('test'); + equal(o.callbacks('test').length, 0); + }); + +},{"../lib/torque":10}],105:[function(require,module,exports){ +module.exports=module.exports = { + "pixel_size": 4, + "total_rows": 451, + "dimension_mapping": [ + {"SP_NO_YEAR": 0}, + {"SP_PRE_1900": 1}, + {"SP_1900_1910": 2}, + {"SP_1910_1920": 3}, + {"SP_1920_1930": 4}, + {"SP_1930_1940": 5}, + {"SP_1940_1950": 6}, + {"SP_1950_1960": 7}, + {"SP_1960_1970": 8}, + {"SP_1970_1980": 9}, + {"SP_1980_1990": 10}, + {"SP_1990_2000": 11}, + {"SP_2000_2010": 12}, + {"SP_2010_2020": 13}, + {"OBS_NO_YEAR": 14}, + {"OBS_PRE_1900": 15}, + {"OBS_1900_1910": 16}, + {"OBS_1910_1920": 17}, + {"OBS_1920_1930": 18}, + {"OBS_1930_1940": 19}, + {"OBS_1940_1950": 20}, + {"OBS_1950_1960": 21}, + {"OBS_1960_1970": 22}, + {"OBS_1970_1980": 23}, + {"OBS_1980_1990": 24}, + {"OBS_1990_2000": 25}, + {"OBS_2000_2010": 26}, + {"OBS_2010_2020": 27}, + {"LIVING": 28}, + {"FOSSIL": 29}, + {"OTH_NO_YEAR": 30}, + {"OTH_PRE_1900": 31}, + {"OTH_1900_1910": 32}, + {"OTH_1910_1920": 33}, + {"OTH_1920_1930": 34}, + {"OTH_1930_1940": 35}, + {"OTH_1940_1950": 36}, + {"OTH_1950_1960": 37}, + {"OTH_1960_1970": 38}, + {"OTH_1970_1980": 39}, + {"OTH_1980_1990": 40}, + {"OTH_1990_2000": 41}, + {"OTH_2000_2010": 42}, + {"OTH_2010_2020": 43} + ], + "rows": [ + {"data": [24, 34, 2, 27, 26, 2, 6]}, + {"data": [25, 34, 2, 26, 25, 1, 1]}, + {"data": [10, 17, 4, 41, 26, 25, 42, 29, 86, 1, 4]}, + {"data": [11, 17, 5, 41, 40, 26, 25, 42, 15, 6, 29, 3, 3]}, + {"data": [18, 34, 5, 27, 26, 25, 23, 24, 193, 68, 4, 1, 8]}, + {"data": [19, 34, 4, 27, 26, 25, 24, 23, 15, 6, 2]}, + {"data": [20, 34, 3, 27, 26, 24, 1, 3, 4]}, + {"data": [1, 17, 1, 11, 2]}, + {"data": [21, 34, 2, 26, 25, 6, 2]}, + {"data": [22, 34, 3, 27, 26, 25, 4, 10, 2]}, + {"data": [23, 34, 4, 27, 26, 25, 24, 1, 5, 2, 1]}, + {"data": [29, 17, 1, 27, 6]}, + {"data": [30, 17, 1, 27, 1]}, + {"data": [56, 34, 2, 43, 30, 1, 1]}, + {"data": [57, 34, 11, 27, 41, 40, 26, 25, 23, 24, 43, 42, 30, 39, 50, 15, 57, 19, 11, 2, 6, 8, 49, 3, 52]}, + {"data": [42, 17, 1, 27, 1]}, + {"data": [41, 17, 1, 27, 2]}, + {"data": [36, 17, 8, 27, 26, 21, 25, 23, 24, 43, 22, 2035, 2655, 120, 2140, 2304, 2198, 1, 1091]}, + {"data": [37, 17, 7, 27, 26, 21, 25, 23, 24, 22, 332, 439, 35, 512, 510, 645, 176]}, + {"data": [34, 17, 13, 27, 41, 38, 40, 26, 21, 25, 23, 24, 43, 37, 42, 39, 233, 64, 6, 46, 647, 2, 37, 2, 26, 1616, 44, 1538, 22]}, + {"data": [35, 17, 16, 38, 27, 21, 25, 23, 14, 22, 41, 40, 26, 24, 43, 37, 15, 42, 39, 13, 748, 59, 812, 986, 1, 320, 126, 63, 762, 1047, 1769, 1, 1, 2476, 94]}, + {"data": [55, 34, 5, 40, 10, 23, 24, 43, 2, 1, 1, 1, 1]}, + {"data": [32, 17, 4, 27, 26, 43, 42, 9, 15, 51, 26]}, + {"data": [33, 17, 15, 38, 27, 10, 25, 23, 22, 0, 41, 40, 26, 24, 9, 43, 42, 39, 8, 1205, 1, 171, 143, 7, 1, 486, 177, 3551, 44, 1, 5945, 4139, 164]}, + {"data": [40, 34, 1, 26, 2]}, + {"data": [38, 34, 2, 27, 26, 3, 223]}, + {"data": [37, 34, 4, 27, 10, 26, 23, 4, 2, 18, 2]}, + {"data": [36, 34, 3, 27, 25, 9, 2, 4, 3]}, + {"data": [24, 35, 2, 26, 25, 11, 3]}, + {"data": [20, 35, 4, 27, 26, 25, 24, 56, 60, 5, 5]}, + {"data": [21, 35, 2, 27, 26, 3, 13]}, + {"data": [2, 16, 1, 26, 4]}, + {"data": [22, 35, 3, 27, 26, 25, 8, 9, 3]}, + {"data": [23, 35, 3, 27, 26, 24, 2, 11, 1]}, + {"data": [19, 35, 4, 27, 26, 25, 24, 102, 83, 41, 2]}, + {"data": [61, 35, 2, 27, 26, 5, 10]}, + {"data": [58, 35, 14, 38, 27, 21, 25, 23, 34, 30, 41, 40, 26, 24, 43, 42, 39, 1, 72, 1, 21, 8, 1, 36, 146, 187, 58, 5, 121, 575, 181]}, + {"data": [56, 35, 13, 27, 41, 38, 40, 26, 25, 23, 24, 43, 37, 42, 30, 39, 16, 27, 2, 39, 5, 7, 4, 5, 17, 1, 106, 1, 28]}, + {"data": [57, 35, 14, 38, 27, 23, 25, 14, 30, 41, 40, 26, 24, 9, 43, 42, 39, 1, 58, 1, 33, 1, 17, 126, 200, 106, 11, 1, 163, 659, 196]}, + {"data": [34, 16, 11, 27, 41, 38, 40, 26, 25, 24, 43, 42, 22, 39, 264, 52, 7, 23, 960, 16, 4, 1191, 1186, 7, 22]}, + {"data": [35, 16, 13, 27, 41, 38, 40, 26, 25, 20, 24, 43, 37, 42, 22, 39, 123, 207, 1, 48, 23, 14, 3, 2, 2397, 1, 4131, 1, 79]}, + {"data": [55, 35, 8, 27, 41, 26, 25, 23, 43, 42, 39, 1, 5, 2, 1, 1, 22, 16, 1]}, + {"data": [33, 16, 8, 27, 41, 40, 26, 25, 24, 43, 42, 19, 10, 12, 60, 8, 2, 57, 62]}, + {"data": [36, 16, 14, 38, 27, 21, 25, 23, 14, 22, 41, 40, 26, 24, 43, 42, 39, 1, 1409, 92, 656, 773, 1, 323, 18, 2, 2050, 940, 263, 325, 3]}, + {"data": [37, 16, 10, 27, 41, 40, 26, 21, 25, 23, 24, 15, 22, 356, 3, 4, 388, 8, 283, 240, 315, 1, 49]}, + {"data": [42, 35, 3, 27, 26, 8, 4, 3, 15]}, + {"data": [41, 35, 1, 26, 1]}, + {"data": [40, 35, 1, 26, 1]}, + {"data": [38, 35, 1, 27, 1]}, + {"data": [37, 35, 7, 27, 26, 25, 23, 24, 9, 0, 3, 1017, 3, 2, 1, 19, 18]}, + {"data": [36, 35, 3, 27, 26, 24, 4, 24, 1]}, + {"data": [35, 35, 1, 26, 2]}, + {"data": [34, 35, 2, 27, 26, 2, 13]}, + {"data": [17, 32, 5, 27, 26, 25, 24, 22, 11, 24, 13, 2, 1]}, + {"data": [19, 32, 1, 2, 1]}, + {"data": [18, 32, 2, 27, 26, 10, 9]}, + {"data": [21, 32, 2, 27, 26, 1, 2]}, + {"data": [23, 32, 1, 25, 1]}, + {"data": [22, 32, 1, 26, 6]}, + {"data": [25, 32, 1, 26, 2]}, + {"data": [24, 32, 1, 25, 1]}, + {"data": [12, 19, 3, 27, 26, 25, 3, 49, 6]}, + {"data": [15, 19, 4, 27, 26, 25, 24, 75, 133, 20, 13]}, + {"data": [14, 19, 1, 26, 2]}, + {"data": [8, 19, 2, 27, 26, 10, 2]}, + {"data": [11, 19, 4, 27, 40, 26, 25, 114, 76, 104, 1]}, + {"data": [10, 19, 4, 27, 26, 25, 24, 3, 58, 47, 5]}, + {"data": [16, 19, 2, 6, 0, 1, 1]}, + {"data": [30, 19, 7, 27, 41, 26, 25, 24, 42, 30, 19, 1, 13, 13, 5, 67, 8]}, + {"data": [31, 19, 11, 6, 27, 41, 40, 26, 25, 23, 24, 43, 42, 30, 1, 68, 1911, 2, 59, 14, 2, 12, 3, 58, 27]}, + {"data": [14, 32, 1, 30, 1]}, + {"data": [38, 19, 4, 27, 26, 25, 11, 1, 10, 1, 7]}, + {"data": [36, 19, 6, 27, 1, 26, 24, 14, 0, 3, 1, 286, 1, 170, 1]}, + {"data": [35, 19, 14, 27, 41, 38, 40, 26, 21, 25, 23, 24, 43, 37, 14, 42, 39, 55, 219, 49, 271, 28, 1, 71, 39, 238, 1512, 1, 3, 1624, 136]}, + {"data": [34, 19, 16, 38, 27, 36, 21, 25, 23, 22, 41, 40, 26, 20, 24, 43, 37, 42, 39, 636, 1131, 2, 17, 2263, 147, 154, 1384, 1175, 2901, 3, 1427, 15287, 48, 20324, 1274]}, + {"data": [33, 19, 15, 27, 38, 25, 23, 8, 22, 0, 41, 40, 12, 26, 24, 43, 42, 39, 1863, 3, 1231, 15, 2, 7, 2, 727, 341, 13, 3216, 200, 7235, 2604, 35]}, + {"data": [32, 19, 12, 27, 41, 38, 40, 26, 25, 23, 24, 43, 42, 22, 39, 393, 45, 4, 4, 306, 41, 4, 6, 948, 286, 2, 6]}, + {"data": [47, 19, 1, 26, 1]}, + {"data": [41, 19, 1, 26, 1]}, + {"data": [40, 19, 2, 12, 11, 2, 1]}, + {"data": [38, 32, 3, 27, 26, 25, 82, 45, 4]}, + {"data": [39, 32, 2, 26, 25, 6, 1]}, + {"data": [37, 32, 1, 27, 1]}, + {"data": [20, 33, 2, 26, 24, 3, 1]}, + {"data": [22, 33, 2, 26, 25, 2, 4]}, + {"data": [5, 18, 2, 26, 25, 2, 1]}, + {"data": [17, 33, 3, 27, 26, 25, 13, 6, 1]}, + {"data": [7, 18, 2, 27, 26, 24, 5]}, + {"data": [18, 33, 2, 27, 26, 28, 14]}, + {"data": [11, 18, 4, 41, 40, 26, 39, 8, 2, 6, 2]}, + {"data": [10, 18, 1, 26, 4]}, + {"data": [25, 33, 2, 26, 24, 6, 1]}, + {"data": [12, 18, 2, 4, 26, 1, 1]}, + {"data": [24, 33, 1, 26, 5]}, + {"data": [14, 18, 1, 27, 3]}, + {"data": [31, 18, 5, 27, 26, 25, 24, 30, 20, 8, 12, 6, 3]}, + {"data": [35, 18, 17, 27, 38, 21, 25, 23, 31, 22, 41, 40, 26, 19, 20, 24, 43, 37, 42, 39, 2770, 268, 132, 1238, 1236, 3, 852, 1602, 932, 1947, 2, 2, 1312, 12245, 148, 13268, 713]}, + {"data": [34, 18, 16, 38, 27, 21, 25, 23, 22, 41, 40, 12, 26, 20, 24, 43, 37, 42, 39, 349, 315, 25, 38, 21, 39, 511, 620, 1, 214, 1, 28, 5081, 122, 6714, 440]}, + {"data": [33, 18, 14, 38, 27, 25, 23, 11, 22, 41, 40, 12, 26, 24, 43, 42, 39, 89, 2062, 801, 1298, 8, 87, 2567, 2420, 8, 4316, 2413, 9928, 6125, 1291]}, + {"data": [32, 18, 13, 27, 41, 38, 40, 26, 25, 23, 24, 43, 37, 42, 22, 39, 1077, 801, 4, 169, 3263, 560, 8, 89, 5363, 1, 3582, 1, 29]}, + {"data": [37, 18, 10, 27, 1, 4, 26, 21, 25, 23, 24, 14, 22, 197, 7, 1, 260, 30, 190, 265, 150, 1, 152]}, + {"data": [36, 18, 11, 27, 26, 21, 25, 23, 20, 24, 43, 14, 42, 22, 5188, 5457, 335, 4168, 3430, 7, 4572, 13, 78, 6, 2256]}, + {"data": [58, 33, 2, 27, 26, 4, 1]}, + {"data": [57, 33, 9, 27, 41, 40, 26, 25, 43, 14, 42, 39, 2, 3, 1, 11, 3, 1, 1, 48, 7]}, + {"data": [38, 33, 3, 27, 26, 25, 12, 12, 1]}, + {"data": [39, 33, 2, 27, 26, 1, 2]}, + {"data": [36, 33, 1, 25, 1]}, + {"data": [41, 33, 1, 26, 1]}, + {"data": [31, 21, 24, 6, 27, 38, 25, 23, 2, 14, 22, 0, 30, 41, 1, 4, 40, 3, 26, 24, 9, 43, 37, 32, 33, 42, 39, 1, 649, 4, 860, 29, 4, 296, 2, 129, 18, 1079, 4, 52, 155, 15, 580, 228, 102, 208, 2, 2, 2, 12119, 461]}, + {"data": [30, 21, 8, 27, 26, 12, 25, 11, 24, 13, 0, 21, 60, 3, 40, 1, 65, 29, 1513]}, + {"data": [22, 21, 5, 6, 27, 26, 25, 24, 3, 17, 60, 28, 9]}, + {"data": [21, 21, 4, 27, 26, 25, 24, 17, 178, 157, 19]}, + {"data": [20, 21, 2, 24, 22, 1, 2]}, + {"data": [18, 21, 1, 26, 4]}, + {"data": [17, 21, 11, 6, 27, 4, 10, 26, 25, 5, 24, 9, 8, 0, 1, 8, 2, 74, 60, 34, 2, 8, 195, 36, 310]}, + {"data": [16, 21, 6, 4, 26, 25, 5, 24, 0, 2, 18, 12, 2, 1, 4]}, + {"data": [14, 21, 10, 27, 4, 26, 25, 23, 34, 5, 11, 24, 0, 2524, 4, 5488, 1502, 31, 1, 3, 28, 358, 31]}, + {"data": [15, 21, 8, 6, 27, 26, 25, 23, 24, 9, 0, 2, 1, 30, 1, 1, 10, 5, 7]}, + {"data": [12, 21, 11, 27, 4, 40, 26, 25, 23, 24, 9, 37, 7, 0, 192, 1, 1, 656, 152, 4, 26, 1, 1, 1, 2]}, + {"data": [13, 21, 13, 27, 4, 26, 25, 23, 34, 5, 24, 32, 8, 22, 0, 35, 1221, 1, 3685, 1042, 3, 1, 3, 120, 1, 1, 5, 2, 1]}, + {"data": [10, 21, 22, 6, 27, 38, 36, 10, 25, 23, 5, 34, 8, 22, 0, 35, 4, 40, 3, 26, 20, 24, 9, 37, 7, 11, 4866, 11, 3, 2, 2043, 403, 14, 2, 4, 269, 3, 5, 5, 1, 2, 8997, 1, 461, 4, 2, 22]}, + {"data": [11, 21, 14, 38, 27, 25, 23, 22, 0, 41, 4, 40, 3, 26, 24, 7, 39, 3, 999, 1132, 5, 20, 1, 20, 2, 280, 1, 4304, 273, 3, 13]}, + {"data": [9, 21, 5, 27, 26, 25, 24, 22, 256, 720, 90, 14, 1]}, + {"data": [19, 38, 8, 6, 27, 26, 25, 5, 24, 8, 0, 10, 809, 357, 17, 1, 6, 88, 20]}, + {"data": [17, 38, 1, 26, 3]}, + {"data": [22, 38, 3, 27, 26, 25, 7, 17, 2]}, + {"data": [20, 38, 2, 26, 0, 1, 58]}, + {"data": [21, 38, 7, 27, 10, 26, 25, 24, 9, 0, 98, 12, 108, 18, 5, 6, 19]}, + {"data": [52, 21, 1, 26, 1]}, + {"data": [35, 38, 7, 27, 26, 12, 25, 23, 24, 22, 46, 3064, 7, 1114, 104, 7272, 74]}, + {"data": [37, 38, 3, 27, 25, 24, 1, 15, 36]}, + {"data": [36, 38, 5, 27, 26, 25, 24, 14, 3, 440, 700, 1551, 1]}, + {"data": [50, 21, 3, 27, 26, 11, 1, 3, 1]}, + {"data": [56, 38, 36, 38, 36, 34, 5, 11, 8, 16, 31, 22, 0, 41, 1, 40, 3, 12, 26, 19, 24, 43, 32, 33, 42, 7, 39, 27, 10, 21, 25, 23, 14, 35, 30, 4, 18, 9, 37, 92, 15, 116, 8, 15, 3, 2, 5, 12, 6, 2482, 1, 1711, 4, 12, 978, 2, 4983, 1984, 3, 14, 10880, 3, 2339, 371, 20, 34, 841, 67, 35, 33, 280, 25, 1, 6, 49]}, + {"data": [44, 21, 1, 26, 4]}, + {"data": [57, 38, 30, 38, 36, 34, 5, 11, 8, 31, 22, 41, 1, 40, 12, 26, 19, 20, 24, 43, 33, 42, 7, 39, 27, 6, 10, 21, 25, 23, 35, 30, 37, 48, 6, 1, 1, 3, 3, 1, 21, 778, 2, 1298, 13, 874, 1, 5, 86, 558, 3, 2801, 3, 1609, 1074, 2, 8, 51, 243, 77, 5, 188, 29]}, + {"data": [58, 38, 39, 38, 36, 34, 5, 11, 13, 8, 31, 22, 0, 41, 1, 40, 3, 12, 26, 19, 20, 24, 43, 32, 15, 33, 7, 42, 39, 6, 27, 10, 21, 25, 23, 2, 14, 35, 30, 18, 9, 37, 37, 14, 14, 1, 4, 1, 50, 3, 99, 26, 1311, 1, 2065, 4, 61, 1211, 2, 2, 1183, 2592, 5, 1, 2, 6, 4015, 2848, 3, 854, 30, 36, 1627, 126, 3, 50043, 11, 505, 1, 28, 27]}, + {"data": [59, 38, 13, 27, 36, 23, 25, 35, 30, 41, 40, 26, 24, 43, 42, 39, 9, 2, 3, 7, 1, 1, 21, 47, 11, 2, 53, 174, 37]}, + {"data": [40, 21, 1, 26, 1]}, + {"data": [62, 38, 7, 27, 10, 26, 25, 23, 9, 0, 243, 1, 229, 2, 1, 72, 74]}, + {"data": [63, 38, 2, 27, 26, 66, 82]}, + {"data": [36, 21, 4, 27, 26, 25, 24, 3, 3, 3, 1]}, + {"data": [37, 21, 2, 27, 26, 2, 9]}, + {"data": [38, 21, 3, 27, 26, 11, 2, 2, 6]}, + {"data": [32, 21, 19, 27, 6, 21, 25, 23, 2, 14, 0, 30, 41, 4, 40, 12, 26, 24, 9, 43, 42, 39, 524, 2, 1, 317, 25, 2, 1977, 30, 5, 809, 1, 523, 5, 377, 348, 14, 170, 122, 78]}, + {"data": [52, 38, 7, 36, 40, 34, 37, 31, 42, 35, 1, 3, 1, 1, 1, 1, 2]}, + {"data": [33, 21, 15, 27, 6, 10, 25, 23, 8, 14, 22, 0, 3, 12, 26, 24, 9, 7, 209, 1, 1, 49, 2, 1, 290, 1, 1, 9, 1, 332, 18, 6, 4]}, + {"data": [34, 21, 6, 27, 26, 12, 21, 25, 23, 12, 112, 1, 1, 5, 1]}, + {"data": [55, 38, 18, 38, 27, 25, 23, 34, 14, 35, 30, 41, 40, 12, 26, 24, 43, 37, 32, 42, 39, 7, 5, 4, 6, 2, 1, 1, 3, 63, 105, 1, 54, 3, 22, 1, 1, 148, 74]}, + {"data": [35, 21, 5, 27, 26, 25, 24, 14, 12, 23, 28, 29, 7]}, + {"data": [31, 20, 24, 27, 38, 36, 21, 25, 23, 34, 2, 0, 35, 30, 41, 4, 40, 3, 26, 24, 9, 43, 37, 32, 33, 42, 39, 203, 69, 6, 1, 45, 5, 7, 4, 471, 4, 314, 7580, 2, 1397, 3, 155, 120, 468, 1761, 23, 7, 3, 15216, 574]}, + {"data": [30, 20, 9, 27, 26, 12, 25, 11, 24, 13, 42, 0, 49, 47, 3, 1, 68, 5, 53, 16, 3718]}, + {"data": [17, 20, 1, 26, 1]}, + {"data": [21, 20, 2, 26, 25, 1, 1]}, + {"data": [20, 20, 2, 26, 0, 1, 1]}, + {"data": [10, 20, 5, 27, 4, 26, 25, 24, 30, 1, 392, 135, 1]}, + {"data": [11, 20, 19, 38, 27, 36, 10, 25, 23, 5, 22, 0, 30, 4, 40, 26, 24, 9, 37, 33, 7, 39, 6, 488, 1, 2, 854, 3, 1, 1, 3, 1, 5, 9, 2428, 42, 1, 8, 1, 2, 12]}, + {"data": [8, 20, 3, 27, 26, 25, 8, 35, 2]}, + {"data": [9, 20, 5, 27, 26, 25, 24, 39, 17, 195, 168, 3, 1]}, + {"data": [14, 20, 3, 26, 25, 24, 28, 8, 1]}, + {"data": [15, 20, 3, 26, 5, 0, 79, 2, 2]}, + {"data": [12, 20, 5, 27, 26, 25, 24, 39, 29, 748, 297, 17, 2]}, + {"data": [13, 20, 4, 27, 26, 25, 24, 7, 494, 66, 4]}, + {"data": [20, 39, 3, 27, 25, 0, 2, 3, 3]}, + {"data": [21, 39, 1, 27, 2]}, + {"data": [18, 39, 3, 27, 26, 24, 89, 25, 1]}, + {"data": [19, 39, 4, 27, 26, 5, 24, 127, 72, 4, 34]}, + {"data": [60, 20, 1, 26, 7]}, + {"data": [49, 20, 1, 26, 1]}, + {"data": [50, 20, 2, 26, 12, 2, 1]}, + {"data": [62, 39, 7, 27, 26, 25, 24, 9, 22, 0, 12, 7, 1, 1, 58, 1, 58]}, + {"data": [63, 39, 8, 27, 10, 26, 12, 25, 24, 9, 0, 400, 218, 355, 46, 75, 9, 734, 954]}, + {"data": [56, 39, 15, 38, 27, 25, 23, 34, 22, 30, 41, 40, 26, 24, 43, 37, 42, 39, 2, 4, 13, 4, 8, 2, 12, 44, 81, 23, 11, 44, 3, 184, 109]}, + {"data": [44, 20, 1, 26, 1]}, + {"data": [57, 39, 37, 38, 36, 34, 5, 11, 8, 31, 22, 0, 41, 1, 40, 3, 12, 26, 19, 20, 24, 43, 32, 33, 42, 7, 39, 6, 27, 10, 21, 25, 23, 2, 14, 35, 30, 4, 9, 37, 137, 8, 6, 2, 4, 7, 13, 58, 13, 3456, 2, 2050, 2, 4, 2550, 4, 19, 258, 3646, 4, 6, 14550, 19, 3579, 2, 3677, 4, 259, 1393, 133, 1, 2, 7, 731, 1, 5, 88]}, + {"data": [58, 39, 21, 27, 38, 36, 21, 25, 23, 31, 22, 35, 30, 41, 40, 3, 26, 20, 24, 43, 37, 7, 42, 39, 117, 20, 6, 13, 20, 17, 3, 4, 1, 89, 321, 509, 4, 106, 2, 29, 145, 8, 3, 743, 562]}, + {"data": [32, 20, 12, 27, 41, 40, 26, 25, 23, 24, 9, 43, 42, 0, 30, 99, 21, 31, 116, 69, 5, 127, 176, 15, 24, 176, 1]}, + {"data": [33, 20, 8, 6, 27, 4, 26, 25, 23, 24, 22, 2, 62, 4, 1548, 719, 123, 681, 61]}, + {"data": [34, 20, 17, 38, 27, 10, 21, 25, 23, 22, 0, 35, 41, 40, 26, 20, 24, 43, 42, 39, 112, 316, 14, 1, 633, 443, 48, 14, 1, 189, 186, 2579, 1, 232, 1902, 2358, 113]}, + {"data": [35, 20, 5, 27, 26, 25, 23, 24, 4, 12, 1, 14, 6]}, + {"data": [36, 20, 4, 27, 26, 25, 42, 2, 2, 3, 1]}, + {"data": [38, 20, 3, 27, 1, 26, 3, 1, 4]}, + {"data": [39, 20, 1, 27, 3]}, + {"data": [20, 23, 7, 27, 26, 25, 23, 2, 24, 22, 233, 1371, 714, 8, 2, 127, 21]}, + {"data": [18, 23, 23, 6, 27, 10, 21, 25, 23, 11, 5, 2, 13, 8, 22, 0, 1, 4, 3, 12, 26, 19, 20, 24, 9, 7, 113, 24587, 23, 2492, 25268, 229, 28, 18, 12, 1, 13, 3396, 261, 30, 16, 11, 15, 74262, 158, 18, 2811, 239, 27]}, + {"data": [19, 23, 21, 6, 27, 21, 25, 23, 11, 5, 2, 13, 8, 16, 22, 0, 1, 4, 3, 12, 26, 24, 9, 7, 3, 32508, 36, 16510, 376, 1, 5, 3, 1, 4, 1, 38, 32, 25, 4, 29, 4, 59194, 1944, 29, 11]}, + {"data": [16, 23, 22, 6, 27, 10, 21, 25, 23, 11, 5, 2, 13, 22, 0, 1, 4, 3, 12, 26, 19, 20, 18, 24, 9, 31, 27248, 12, 209, 22634, 2044, 18, 3, 99, 3, 609, 3, 75, 12, 8, 53, 81810, 6, 86, 1, 5307, 4]}, + {"data": [17, 23, 24, 6, 27, 10, 21, 25, 23, 11, 5, 2, 13, 8, 22, 0, 35, 1, 4, 3, 12, 26, 19, 20, 24, 9, 7, 225, 25403, 343, 17, 34351, 701, 50, 278, 85, 9, 350, 508, 1571, 2, 76, 48, 87, 33, 86770, 93, 79, 4832, 912, 116]}, + {"data": [30, 23, 4, 27, 4, 26, 24, 11, 1, 596, 1]}, + {"data": [31, 23, 8, 27, 10, 26, 25, 23, 24, 9, 14, 149, 3, 1223, 13, 1, 15, 2, 1068]}, + {"data": [19, 36, 4, 27, 26, 25, 24, 78, 19, 3, 3]}, + {"data": [23, 36, 4, 27, 26, 25, 24, 72, 68, 8, 3]}, + {"data": [22, 36, 4, 27, 26, 25, 24, 28, 36, 2, 3]}, + {"data": [21, 36, 4, 27, 26, 25, 9, 21, 25, 6, 36]}, + {"data": [20, 36, 2, 27, 26, 2, 48]}, + {"data": [15, 23, 15, 27, 6, 21, 25, 23, 5, 2, 22, 0, 1, 3, 26, 24, 9, 7, 7870, 34, 1, 8836, 74, 1, 2, 190, 4, 17, 20, 29217, 1252, 6, 1]}, + {"data": [14, 23, 13, 27, 1, 4, 3, 10, 26, 25, 23, 5, 24, 9, 7, 22, 2854, 3, 6, 3, 2, 8605, 2090, 60, 2, 356, 5, 2, 1]}, + {"data": [13, 23, 12, 6, 27, 3, 10, 26, 25, 23, 24, 9, 8, 7, 22, 28, 1368, 1, 2, 4106, 542, 2, 89, 1, 6, 1, 2]}, + {"data": [12, 23, 10, 6, 27, 26, 25, 23, 5, 24, 7, 22, 0, 1, 1300, 4227, 1152, 8, 145, 101, 1, 2, 1]}, + {"data": [24, 36, 5, 6, 27, 26, 25, 24, 2, 32, 52, 12, 1]}, + {"data": [11, 23, 9, 6, 27, 4, 3, 26, 25, 23, 5, 24, 4, 1619, 18, 7, 4573, 337, 6, 80, 56]}, + {"data": [10, 23, 13, 6, 27, 4, 10, 26, 12, 25, 23, 5, 24, 9, 22, 0, 3, 2037, 28, 6, 7129, 1, 1313, 93, 6, 128, 2, 18, 3]}, + {"data": [9, 23, 6, 27, 26, 25, 23, 24, 22, 580, 880, 133, 1, 25, 3]}, + {"data": [34, 36, 4, 27, 26, 25, 24, 5, 223, 1, 1]}, + {"data": [35, 36, 5, 27, 26, 25, 24, 14, 3, 946, 59, 79, 1]}, + {"data": [36, 36, 12, 27, 10, 26, 21, 25, 23, 24, 9, 17, 16, 22, 0, 4, 8, 2651, 7, 1861, 108, 4863, 3, 1, 1, 20, 8]}, + {"data": [37, 36, 11, 27, 26, 21, 25, 23, 24, 9, 14, 16, 22, 0, 51, 1493, 6, 3399, 158, 9092, 1, 2, 1, 14, 1]}, + {"data": [38, 36, 1, 27, 1]}, + {"data": [37, 23, 3, 27, 26, 25, 90, 32, 5]}, + {"data": [36, 23, 5, 27, 26, 12, 13, 8, 29, 17, 9, 1, 2]}, + {"data": [39, 23, 2, 27, 12, 1, 12]}, + {"data": [38, 23, 2, 12, 11, 1, 4]}, + {"data": [33, 23, 10, 27, 1, 3, 26, 25, 23, 24, 14, 22, 0, 53, 1, 2, 62, 162, 1, 4, 816, 6, 13]}, + {"data": [32, 23, 11, 27, 1, 10, 26, 12, 25, 23, 24, 14, 22, 0, 382, 1, 1, 471, 2, 15, 7, 32, 2400, 1, 10]}, + {"data": [35, 23, 4, 27, 26, 8, 22, 30, 25, 2, 1]}, + {"data": [55, 36, 5, 40, 26, 25, 24, 43, 1, 1, 1, 2, 1]}, + {"data": [34, 23, 11, 27, 1, 4, 3, 26, 25, 23, 24, 31, 22, 0, 57, 1, 1, 2, 93, 9, 1, 3, 1, 1, 5]}, + {"data": [57, 36, 15, 38, 27, 21, 25, 23, 22, 30, 41, 40, 26, 24, 43, 37, 42, 39, 1, 20, 1, 5, 2, 1, 2, 30, 74, 23, 2, 34, 1, 180, 64]}, + {"data": [45, 23, 3, 27, 26, 11, 2, 14, 2]}, + {"data": [44, 23, 1, 27, 1]}, + {"data": [56, 36, 12, 27, 41, 36, 40, 26, 12, 25, 24, 43, 42, 30, 39, 2, 11, 1, 29, 6, 1, 5, 2, 16, 97, 2, 23]}, + {"data": [59, 36, 17, 38, 27, 21, 25, 23, 14, 22, 30, 41, 40, 26, 24, 43, 37, 33, 42, 39, 13, 30, 9, 50, 6, 2, 4, 22, 176, 152, 44, 89, 181, 11, 1, 542, 156]}, + {"data": [47, 23, 2, 27, 26, 4, 1]}, + {"data": [58, 36, 14, 38, 27, 21, 25, 22, 30, 41, 40, 26, 24, 43, 37, 42, 39, 10, 16, 22, 14, 2, 25, 90, 244, 26, 14, 90, 12, 299, 299]}, + {"data": [46, 23, 1, 26, 1]}, + {"data": [61, 36, 4, 27, 26, 25, 24, 10, 14, 3, 1]}, + {"data": [18, 22, 19, 27, 6, 10, 21, 25, 23, 5, 2, 8, 22, 0, 1, 4, 3, 12, 26, 24, 9, 7, 2273, 5, 21, 1, 5166, 16, 5, 3, 8, 9, 206, 5, 17, 7, 7, 10479, 578, 218, 5]}, + {"data": [19, 22, 12, 6, 27, 4, 3, 26, 25, 23, 24, 8, 7, 22, 0, 85, 413, 8, 1, 2204, 946, 159, 470, 3, 4, 3, 1]}, + {"data": [16, 22, 14, 27, 6, 10, 25, 23, 11, 5, 8, 22, 0, 3, 26, 24, 9, 350, 1, 6, 267, 9, 3, 18, 47, 3, 244, 3, 1742, 57, 196]}, + {"data": [17, 22, 11, 27, 4, 26, 25, 23, 5, 24, 9, 8, 7, 0, 50, 1, 239, 180, 18, 9, 88, 335, 152, 1, 497]}, + {"data": [22, 22, 5, 27, 26, 25, 23, 24, 60, 114, 274, 9, 370]}, + {"data": [20, 22, 17, 38, 27, 25, 23, 34, 22, 0, 30, 40, 3, 26, 24, 9, 37, 32, 42, 39, 7, 52, 561, 33, 1, 20, 1, 1, 8, 2, 742, 114, 1, 1, 2, 1, 1]}, + {"data": [21, 22, 6, 27, 26, 25, 23, 24, 30, 20, 336, 67, 6, 13, 1]}, + {"data": [12, 37, 2, 27, 26, 1, 23]}, + {"data": [31, 22, 6, 27, 26, 25, 24, 14, 0, 105, 6, 10, 1, 2791, 20]}, + {"data": [23, 37, 2, 27, 26, 12, 4]}, + {"data": [22, 37, 3, 27, 26, 25, 3, 9, 1]}, + {"data": [21, 37, 4, 27, 26, 24, 0, 8, 8, 6, 2]}, + {"data": [20, 37, 4, 27, 10, 26, 0, 5, 1, 24, 1]}, + {"data": [19, 37, 3, 27, 26, 25, 51, 44, 1]}, + {"data": [11, 22, 16, 27, 6, 10, 25, 23, 11, 5, 2, 13, 8, 4, 12, 26, 24, 9, 7, 1267, 7, 75, 2086, 2, 83, 70, 1, 1, 4, 8, 8, 5732, 120, 45, 17]}, + {"data": [10, 22, 23, 6, 27, 38, 10, 25, 23, 11, 5, 34, 2, 8, 22, 0, 1, 4, 40, 3, 12, 26, 24, 9, 33, 7, 37, 9104, 1, 58, 6926, 26, 49, 61, 2, 11, 6, 31, 1, 1, 28, 3, 27, 40, 26068, 853, 87, 5, 26]}, + {"data": [9, 22, 9, 6, 27, 40, 26, 25, 23, 11, 24, 22, 6, 343, 1, 601, 59, 1, 2, 13, 4]}, + {"data": [15, 22, 10, 27, 1, 4, 3, 26, 25, 23, 24, 22, 0, 2359, 2, 25, 28, 8372, 1679, 30, 296, 3, 1]}, + {"data": [14, 22, 10, 6, 27, 3, 26, 12, 25, 23, 5, 24, 22, 137, 904, 5, 3091, 4, 1172, 3, 1, 190, 2]}, + {"data": [13, 22, 7, 6, 27, 4, 26, 25, 23, 24, 1, 477, 1, 641, 208, 2, 30]}, + {"data": [12, 22, 6, 27, 26, 25, 23, 24, 22, 1059, 2512, 881, 6, 71, 1]}, + {"data": [36, 37, 9, 27, 26, 12, 25, 23, 24, 9, 14, 22, 10, 1583, 5, 1578, 10, 3837, 11, 2, 2]}, + {"data": [37, 37, 10, 27, 26, 12, 21, 25, 23, 24, 8, 14, 16, 31, 1676, 2, 4, 2314, 381, 7511, 18, 1, 1]}, + {"data": [51, 22, 2, 27, 26, 1, 10]}, + {"data": [34, 37, 1, 26, 1]}, + {"data": [35, 37, 6, 27, 26, 25, 23, 24, 22, 2, 98, 416, 3, 728, 2]}, + {"data": [57, 22, 1, 26, 1]}, + {"data": [33, 22, 10, 27, 10, 26, 25, 23, 24, 14, 7, 22, 0, 282, 22, 493, 39, 31, 9, 2377, 6, 1, 26]}, + {"data": [32, 22, 8, 27, 26, 25, 23, 24, 14, 22, 0, 139, 45, 17, 7, 14, 4519, 1, 75]}, + {"data": [52, 37, 3, 38, 40, 31, 1, 1, 1]}, + {"data": [35, 22, 6, 27, 26, 25, 23, 5, 24, 43, 41, 11, 1, 3, 2]}, + {"data": [55, 37, 15, 38, 27, 36, 25, 34, 35, 41, 40, 26, 24, 43, 37, 33, 42, 39, 3, 4, 1, 6, 2, 1, 9, 22, 6, 2, 2, 1, 4, 37, 24]}, + {"data": [54, 37, 1, 33, 4]}, + {"data": [34, 22, 14, 27, 36, 10, 23, 25, 34, 35, 30, 41, 40, 26, 24, 42, 39, 61, 2, 1, 14, 22, 13, 1, 245, 4074, 769, 59, 10, 985, 27]}, + {"data": [37, 22, 3, 26, 12, 25, 8, 1, 4]}, + {"data": [36, 22, 2, 27, 26, 5, 8]}, + {"data": [39, 22, 1, 27, 16]}, + {"data": [38, 22, 1, 11, 1]}, + {"data": [41, 22, 1, 27, 1]}, + {"data": [61, 37, 4, 27, 41, 43, 42, 5, 8, 8, 25]}, + {"data": [40, 22, 1, 27, 1]}, + {"data": [57, 37, 16, 38, 27, 21, 25, 23, 34, 22, 30, 41, 40, 26, 24, 43, 37, 42, 39, 8, 42, 2, 33, 4, 2, 1, 9, 47, 120, 70, 10, 59, 1, 217, 139]}, + {"data": [56, 37, 21, 27, 38, 25, 23, 11, 34, 14, 31, 35, 30, 41, 4, 40, 3, 26, 24, 43, 37, 33, 42, 39, 26, 23, 19, 3, 4, 7, 2, 1, 5, 18, 82, 1, 182, 1, 57, 1, 72, 1, 2, 358, 224]}, + {"data": [59, 37, 22, 27, 38, 36, 21, 25, 23, 5, 34, 22, 0, 35, 30, 41, 40, 26, 24, 43, 37, 32, 33, 42, 39, 431, 46, 11, 58, 174, 61, 1, 3, 5, 1, 3, 84, 650, 617, 220, 281, 303, 37, 2, 1, 2003, 928]}, + {"data": [58, 37, 21, 27, 38, 36, 21, 25, 23, 31, 22, 35, 30, 41, 40, 12, 26, 24, 43, 37, 32, 33, 42, 39, 76, 8, 11, 10, 93, 3, 1, 2, 2, 36, 230, 511, 1, 129, 46, 320, 14, 1, 4, 1158, 684]}, + {"data": [40, 25, 2, 6, 27, 54, 3]}, + {"data": [61, 42, 1, 27, 1]}, + {"data": [41, 25, 3, 6, 12, 23, 33, 4, 2]}, + {"data": [44, 25, 2, 5, 11, 2, 10]}, + {"data": [45, 25, 6, 27, 26, 25, 23, 5, 24, 2, 2, 4, 2, 60, 1]}, + {"data": [34, 25, 2, 27, 0, 1, 2]}, + {"data": [33, 25, 4, 26, 25, 5, 7, 4, 1, 1, 4]}, + {"data": [38, 25, 7, 27, 26, 25, 24, 14, 7, 0, 49, 28, 10, 7, 996, 1, 1]}, + {"data": [39, 25, 3, 27, 26, 7, 37, 9, 11]}, + {"data": [36, 25, 6, 27, 26, 12, 25, 8, 7, 5, 41, 2, 10, 2, 8]}, + {"data": [37, 25, 3, 27, 26, 25, 5, 20, 3]}, + {"data": [56, 25, 4, 27, 26, 24, 22, 1, 4, 1, 1]}, + {"data": [50, 25, 1, 26, 1]}, + {"data": [49, 25, 1, 25, 2]}, + {"data": [10, 25, 18, 27, 6, 10, 25, 23, 11, 5, 2, 8, 22, 0, 4, 3, 12, 26, 24, 9, 7, 6572, 5, 40, 2550, 122, 51, 17, 2, 21, 4, 4, 23, 12, 77, 12943, 381, 39, 3]}, + {"data": [11, 25, 20, 6, 27, 10, 25, 23, 11, 5, 2, 8, 22, 0, 1, 4, 40, 3, 12, 26, 24, 9, 7, 23, 9144, 15, 2769, 316, 48, 61, 1, 60, 22, 2, 7, 144, 2, 50, 10, 15890, 628, 17, 2]}, + {"data": [12, 25, 16, 27, 6, 25, 23, 11, 2, 8, 22, 0, 1, 4, 3, 26, 24, 9, 7, 8583, 14, 3120, 35, 31, 1, 6, 7, 150, 1, 25, 3, 15796, 542, 169, 2]}, + {"data": [13, 25, 16, 27, 6, 10, 25, 23, 11, 5, 13, 8, 22, 1, 3, 26, 24, 9, 7, 6645, 105, 32, 2141, 20, 90, 14, 8, 14, 1, 4, 1, 13210, 184, 23, 3]}, + {"data": [14, 25, 17, 27, 6, 10, 21, 25, 23, 13, 8, 22, 0, 1, 3, 26, 20, 24, 9, 7, 7674, 16, 2, 1, 6907, 12, 1, 1, 2, 1, 3, 1, 22462, 4, 1239, 1, 1]}, + {"data": [15, 25, 12, 6, 27, 1, 4, 3, 10, 26, 19, 25, 23, 24, 22, 74, 3607, 1, 2, 15, 1, 10665, 2, 1699, 73, 338, 5]}, + {"data": [21, 42, 3, 27, 26, 25, 7, 2, 1]}, + {"data": [18, 42, 1, 26, 1]}, + {"data": [19, 42, 7, 27, 26, 25, 24, 9, 8, 0, 19, 29, 4, 1, 7, 1, 7]}, + {"data": [31, 25, 7, 27, 41, 40, 26, 25, 23, 42, 21, 2, 1, 54, 5, 7, 2]}, + {"data": [30, 25, 6, 27, 41, 4, 26, 25, 23, 11, 4, 1, 48, 8, 9]}, + {"data": [17, 25, 13, 27, 6, 23, 25, 11, 5, 22, 1, 4, 3, 12, 26, 24, 3133, 63, 148, 2599, 3, 4, 6, 4, 2, 1, 1, 11419, 647]}, + {"data": [16, 25, 7, 6, 27, 26, 25, 23, 24, 22, 32, 2620, 10946, 2429, 103, 616, 4]}, + {"data": [18, 25, 9, 27, 1, 26, 21, 25, 23, 20, 24, 22, 825, 7, 2749, 20, 500, 12, 2, 67, 6]}, + {"data": [20, 25, 4, 27, 26, 25, 24, 38, 229, 10, 3]}, + {"data": [47, 24, 1, 27, 5]}, + {"data": [45, 24, 1, 27, 1]}, + {"data": [43, 24, 3, 27, 26, 24, 1, 1, 1]}, + {"data": [40, 24, 1, 6, 9]}, + {"data": [38, 24, 5, 27, 26, 25, 24, 8, 11, 7, 2, 15, 2]}, + {"data": [39, 24, 4, 27, 26, 22, 0, 18, 4, 1, 1]}, + {"data": [36, 24, 7, 27, 26, 12, 25, 24, 8, 7, 37, 103, 26, 10, 2, 2, 14]}, + {"data": [37, 24, 4, 27, 26, 25, 8, 9, 44, 4, 4]}, + {"data": [34, 24, 6, 27, 1, 4, 26, 25, 0, 11, 2, 3, 25, 1, 2]}, + {"data": [35, 24, 5, 27, 26, 5, 24, 0, 7, 29, 1, 1, 1]}, + {"data": [32, 24, 8, 27, 26, 29, 25, 23, 24, 14, 0, 26, 192, 1, 2, 4, 16, 36, 1]}, + {"data": [52, 24, 2, 27, 25, 1, 2]}, + {"data": [12, 24, 11, 27, 1, 3, 26, 25, 23, 2, 24, 9, 7, 22, 3319, 4, 5, 6700, 1831, 19, 1, 147, 1, 1, 1]}, + {"data": [13, 24, 20, 6, 27, 10, 21, 25, 23, 11, 5, 2, 8, 22, 0, 1, 4, 40, 3, 12, 26, 24, 9, 20, 7781, 40, 1, 5150, 351, 28, 38, 21, 6, 95, 14, 5, 5, 1, 25, 154, 17145, 917, 28]}, + {"data": [14, 24, 19, 27, 6, 10, 21, 25, 23, 11, 5, 2, 8, 22, 0, 1, 4, 3, 12, 26, 24, 9, 3173, 65, 1, 1, 3449, 66, 2, 7, 3, 5, 3, 1, 2, 5, 11, 3, 9435, 295, 1]}, + {"data": [15, 24, 19, 27, 6, 10, 21, 25, 23, 11, 5, 17, 8, 16, 22, 0, 1, 4, 12, 26, 24, 9, 7143, 53, 761, 1, 7676, 19, 11, 22, 2, 25, 1, 15, 961, 8, 10, 177, 25852, 844, 164]}, + {"data": [9, 24, 8, 27, 4, 26, 25, 23, 2, 24, 22, 1096, 12, 1626, 106, 36, 2, 31, 6]}, + {"data": [10, 24, 21, 6, 27, 10, 21, 25, 23, 11, 5, 2, 8, 14, 22, 0, 1, 4, 3, 12, 26, 24, 9, 7, 19, 13436, 4, 2, 6521, 188, 36, 29, 51, 6, 62, 24, 1, 63, 122, 784, 58, 23877, 883, 20, 7]}, + {"data": [11, 24, 7, 27, 4, 26, 25, 23, 5, 24, 638, 8, 1329, 301, 104, 6, 64]}, + {"data": [19, 43, 4, 27, 26, 25, 24, 40, 65, 10, 4]}, + {"data": [20, 43, 3, 27, 26, 25, 1, 1, 1]}, + {"data": [31, 24, 9, 27, 10, 26, 25, 23, 24, 9, 8, 14, 86, 10, 2229, 16, 5, 17, 5, 27, 295]}, + {"data": [30, 24, 7, 27, 10, 26, 25, 23, 24, 0, 597, 65, 859, 23, 4, 3, 64]}, + {"data": [27, 24, 3, 27, 10, 0, 7, 336, 336]}, + {"data": [26, 24, 2, 10, 0, 38, 38]}, + {"data": [17, 24, 17, 27, 6, 10, 21, 25, 23, 11, 5, 22, 0, 30, 1, 3, 26, 20, 24, 7, 9947, 173, 1, 26, 16045, 317, 2, 9, 28, 3, 1, 15, 16, 47395, 6, 3338, 1]}, + {"data": [16, 24, 19, 27, 6, 10, 21, 25, 23, 11, 5, 2, 8, 22, 0, 30, 1, 12, 26, 20, 24, 9, 10616, 52, 2, 27, 11782, 292, 7, 6, 4, 6, 14, 3, 3, 24, 8, 37869, 17, 1669, 11]}, + {"data": [19, 24, 8, 27, 10, 26, 21, 25, 23, 24, 22, 479, 6, 2421, 32, 1096, 47, 109, 82]}, + {"data": [18, 24, 20, 6, 27, 10, 21, 25, 23, 11, 5, 2, 22, 1, 4, 3, 12, 26, 19, 20, 24, 9, 7, 116, 57342, 39, 88, 39757, 1080, 61, 4, 8, 453, 22, 41, 13, 3, 163175, 2, 1, 4497, 29, 9]}, + {"data": [38, 27, 1, 25, 2]}, + {"data": [37, 27, 8, 27, 1, 10, 26, 9, 8, 7, 0, 5, 1, 15, 61, 10, 29, 6, 10]}, + {"data": [63, 40, 5, 27, 26, 25, 9, 0, 194, 75, 5, 124, 124]}, + {"data": [43, 27, 2, 26, 7, 1, 9]}, + {"data": [42, 27, 5, 27, 26, 25, 7, 0, 20, 105, 3, 2, 1]}, + {"data": [62, 40, 9, 27, 4, 26, 25, 23, 24, 9, 22, 0, 485, 50, 511, 18, 18, 9, 166, 1, 166]}, + {"data": [41, 27, 4, 27, 26, 25, 23, 674, 1775, 183, 1]}, + {"data": [61, 40, 3, 27, 26, 25, 25, 10, 4]}, + {"data": [40, 27, 3, 27, 26, 25, 8, 1, 12]}, + {"data": [47, 27, 4, 27, 26, 25, 24, 1, 2, 2, 2]}, + {"data": [46, 27, 6, 6, 27, 10, 26, 25, 0, 26, 2, 1, 3, 1, 1]}, + {"data": [58, 40, 27, 38, 36, 34, 8, 31, 41, 40, 26, 20, 24, 43, 33, 42, 7, 39, 27, 6, 10, 21, 23, 25, 30, 35, 4, 18, 9, 37, 30, 5, 1, 3, 4, 752, 726, 190, 1, 1, 513, 1, 2936, 3, 1006, 281, 1, 1, 22, 3, 54, 220, 4, 2, 1, 1, 14]}, + {"data": [57, 40, 16, 27, 21, 25, 23, 11, 34, 35, 30, 41, 40, 26, 43, 37, 32, 42, 39, 23, 3, 5, 1, 1, 1, 1, 6, 110, 92, 32, 92, 2, 1, 583, 131]}, + {"data": [45, 27, 3, 27, 26, 25, 8, 50, 2]}, + {"data": [44, 27, 3, 27, 26, 5, 6, 16, 57]}, + {"data": [50, 27, 1, 27, 1]}, + {"data": [48, 27, 5, 6, 27, 26, 25, 7, 36, 12, 10, 1, 64]}, + {"data": [20, 40, 5, 27, 26, 25, 9, 0, 8, 19, 6, 1, 1]}, + {"data": [3, 27, 4, 27, 26, 25, 24, 125, 93, 17, 3]}, + {"data": [19, 40, 7, 27, 26, 12, 25, 24, 7, 0, 20, 18, 9, 3, 9, 7, 12]}, + {"data": [18, 40, 3, 27, 26, 25, 42, 39, 1]}, + {"data": [11, 27, 1, 26, 1]}, + {"data": [13, 27, 10, 6, 27, 41, 26, 25, 23, 5, 24, 9, 39, 4, 228, 38, 189, 10, 1, 2, 7, 5, 1]}, + {"data": [24, 40, 1, 0, 7]}, + {"data": [12, 27, 7, 27, 26, 12, 25, 24, 37, 42, 187, 320, 5, 31, 12, 1, 1]}, + {"data": [14, 27, 18, 27, 36, 25, 23, 5, 2, 14, 35, 41, 1, 4, 40, 3, 26, 24, 9, 42, 39, 4085, 1, 202, 27, 12, 6, 4, 1, 5, 1, 2, 2, 9, 4189, 34, 3, 12, 3]}, + {"data": [17, 27, 6, 27, 26, 25, 23, 24, 22, 1352, 1635, 367, 27, 202, 20]}, + {"data": [18, 27, 4, 27, 26, 25, 24, 125, 98, 45, 2]}, + {"data": [0, 40, 4, 27, 26, 9, 0, 16, 7, 9, 2]}, + {"data": [38, 26, 9, 27, 4, 26, 12, 25, 23, 24, 14, 7, 84, 5, 52, 5, 7, 1, 11, 248, 2]}, + {"data": [37, 26, 5, 27, 10, 26, 25, 8, 15, 45, 15, 3, 1]}, + {"data": [32, 26, 1, 7, 3]}, + {"data": [47, 26, 6, 6, 27, 26, 25, 23, 24, 3, 22, 9, 6, 1, 10]}, + {"data": [46, 26, 5, 6, 27, 26, 25, 24, 9, 10, 41, 17, 2]}, + {"data": [45, 26, 6, 27, 26, 25, 5, 24, 7, 8, 46, 25, 16, 3, 4]}, + {"data": [44, 26, 2, 27, 26, 2, 4]}, + {"data": [43, 26, 1, 27, 1]}, + {"data": [62, 41, 5, 27, 26, 25, 9, 0, 347, 297, 10, 101, 102]}, + {"data": [42, 26, 2, 6, 11, 9, 6]}, + {"data": [61, 41, 5, 27, 26, 25, 9, 0, 27, 52, 12, 166, 166]}, + {"data": [41, 26, 4, 6, 27, 23, 5, 12, 2, 3, 11]}, + {"data": [40, 26, 2, 27, 26, 274, 2]}, + {"data": [53, 26, 2, 27, 26, 1, 1]}, + {"data": [50, 26, 1, 26, 1]}, + {"data": [19, 41, 2, 27, 26, 11, 1]}, + {"data": [13, 26, 13, 27, 10, 23, 25, 5, 8, 22, 0, 4, 40, 26, 24, 7, 843, 4, 21, 429, 7, 2, 7, 5, 3, 11, 1313, 22, 2]}, + {"data": [12, 26, 10, 27, 4, 3, 26, 25, 23, 5, 24, 8, 22, 2229, 1, 1, 3971, 789, 21, 1, 206, 2, 1]}, + {"data": [15, 26, 9, 6, 27, 26, 21, 25, 23, 5, 24, 22, 43, 9216, 15050, 1, 2520, 35, 3, 473, 20]}, + {"data": [14, 26, 9, 6, 27, 26, 25, 23, 5, 24, 9, 0, 30, 13905, 22539, 3222, 33, 6, 480, 4, 2]}, + {"data": [11, 26, 8, 27, 4, 26, 25, 23, 24, 9, 39, 341, 16, 525, 39, 5, 3, 1, 1]}, + {"data": [16, 26, 9, 6, 27, 26, 21, 25, 23, 5, 24, 22, 4, 1523, 3387, 1, 604, 50, 2, 168, 2]}, + {"data": [17, 26, 11, 27, 38, 40, 26, 21, 25, 23, 24, 9, 8, 22, 2726, 1, 1, 5173, 2, 1255, 17, 383, 1, 2, 2]}, + {"data": [28, 26, 1, 27, 1]}, + {"data": [29, 26, 2, 27, 26, 1, 2]}, + {"data": [30, 26, 4, 27, 26, 25, 23, 6, 31, 13, 7]}, + {"data": [31, 26, 2, 26, 25, 3, 3]}, + {"data": [49, 29, 3, 27, 26, 25, 9, 16, 4]}, + {"data": [51, 29, 1, 27, 2]}, + {"data": [50, 29, 2, 27, 26, 13, 7]}, + {"data": [40, 29, 1, 22, 1]}, + {"data": [45, 29, 4, 27, 26, 25, 5, 3, 6, 2, 3]}, + {"data": [46, 29, 2, 27, 26, 2, 1]}, + {"data": [37, 29, 4, 27, 4, 26, 0, 4, 2, 2, 1]}, + {"data": [38, 29, 1, 26, 4]}, + {"data": [29, 29, 5, 27, 41, 26, 25, 24, 8, 1, 5, 1, 3]}, + {"data": [28, 29, 2, 27, 26, 3, 4]}, + {"data": [19, 29, 3, 27, 26, 25, 24, 25, 1]}, + {"data": [17, 29, 1, 26, 1]}, + {"data": [16, 29, 3, 27, 26, 25, 165, 60, 20]}, + {"data": [21, 29, 1, 26, 2]}, + {"data": [14, 29, 2, 27, 26, 7, 23]}, + {"data": [15, 29, 5, 27, 26, 25, 24, 14, 235, 447, 10, 7, 1]}, + {"data": [49, 28, 5, 27, 3, 26, 12, 25, 7, 2, 4, 5, 3]}, + {"data": [48, 28, 1, 27, 2]}, + {"data": [50, 28, 1, 27, 4]}, + {"data": [44, 28, 2, 27, 26, 13, 3]}, + {"data": [45, 28, 3, 27, 26, 25, 2, 1, 6]}, + {"data": [46, 28, 1, 27, 3]}, + {"data": [36, 13, 9, 27, 41, 40, 26, 25, 24, 43, 42, 39, 3, 5, 2, 8, 3, 2, 35, 9, 5]}, + {"data": [37, 13, 8, 27, 41, 40, 26, 25, 24, 43, 42, 5, 1, 1, 18, 1, 1, 40, 20]}, + {"data": [41, 28, 1, 26, 5]}, + {"data": [42, 28, 1, 26, 2]}, + {"data": [37, 28, 2, 26, 0, 4, 1]}, + {"data": [38, 28, 1, 27, 1]}, + {"data": [39, 28, 1, 27, 1]}, + {"data": [21, 28, 1, 27, 1]}, + {"data": [20, 28, 5, 27, 26, 25, 23, 24, 400, 508, 27, 1, 1]}, + {"data": [19, 28, 3, 27, 26, 25, 99, 122, 7]}, + {"data": [18, 28, 4, 27, 26, 25, 24, 23, 67, 1, 1]}, + {"data": [17, 28, 2, 27, 26, 31, 2]}, + {"data": [16, 28, 2, 27, 26, 2, 13]}, + {"data": [14, 28, 16, 6, 27, 36, 25, 5, 8, 31, 30, 41, 40, 26, 24, 37, 42, 7, 39, 46, 657, 1, 59, 4, 2, 1, 3, 5, 131, 883, 14, 1, 10, 3, 60]}, + {"data": [15, 28, 9, 27, 41, 40, 26, 25, 23, 24, 14, 42, 40, 2, 15, 176, 4, 1, 5, 3, 1]}, + {"data": [12, 28, 1, 27, 1]}, + {"data": [13, 28, 9, 27, 40, 26, 25, 24, 37, 8, 7, 35, 330, 20, 447, 18, 15, 1, 1, 3, 1]}, + {"data": [4, 28, 8, 27, 10, 26, 25, 23, 11, 24, 0, 538, 8, 446, 36, 3, 4, 5, 1]}, + {"data": [3, 28, 14, 27, 10, 25, 23, 11, 2, 8, 22, 0, 3, 12, 26, 24, 7, 289, 1, 40, 1, 1, 2, 2, 3, 6, 52, 3, 277, 17, 1]}, + {"data": [49, 31, 1, 7, 6]}, + {"data": [50, 31, 1, 27, 1]}, + {"data": [63, 14, 1, 27, 1]}, + {"data": [54, 31, 1, 26, 1]}, + {"data": [37, 31, 1, 27, 2]}, + {"data": [38, 31, 1, 26, 10]}, + {"data": [37, 14, 10, 27, 41, 40, 26, 12, 25, 23, 24, 43, 42, 100, 34, 33, 180, 1, 25, 3, 19, 448, 258]}, + {"data": [36, 14, 10, 27, 41, 40, 26, 25, 23, 24, 43, 42, 39, 111, 13, 4, 162, 59, 3, 22, 197, 86, 15]}, + {"data": [35, 14, 8, 27, 41, 40, 26, 25, 24, 43, 42, 167, 37, 8, 191, 25, 4, 519, 311]}, + {"data": [34, 14, 11, 27, 41, 38, 40, 26, 25, 24, 43, 42, 22, 39, 10, 1, 1, 1, 19, 1, 1, 57, 31, 1, 1]}, + {"data": [18, 31, 1, 27, 1]}, + {"data": [17, 31, 2, 27, 26, 6, 19]}, + {"data": [48, 30, 1, 30, 1]}, + {"data": [49, 30, 1, 26, 2]}, + {"data": [45, 30, 2, 27, 26, 1, 3]}, + {"data": [35, 15, 15, 38, 27, 21, 25, 23, 22, 41, 40, 26, 20, 24, 43, 37, 42, 39, 8, 28, 1, 9, 7, 2, 75, 17, 21, 1, 8, 739, 2, 1675, 24]}, + {"data": [46, 30, 6, 27, 26, 25, 23, 24, 7, 4, 12, 1, 12, 2, 3]}, + {"data": [34, 15, 11, 27, 41, 38, 40, 26, 25, 24, 43, 42, 22, 39, 159, 276, 4, 279, 371, 270, 275, 739, 415, 1, 6]}, + {"data": [37, 15, 5, 27, 26, 21, 23, 24, 6, 34, 5, 3, 4]}, + {"data": [36, 15, 11, 27, 41, 26, 21, 25, 23, 24, 43, 42, 22, 39, 321, 4, 535, 13, 788, 74, 226, 58, 64, 18, 1]}, + {"data": [20, 30, 1, 26, 3]}, + {"data": [18, 30, 1, 25, 1]}, + {"data": [16, 30, 4, 27, 26, 25, 24, 80, 101, 18, 2]}, + {"data": [17, 30, 5, 27, 26, 25, 23, 24, 174, 202, 44, 1, 4]}, + {"data": [31, 30, 1, 26, 1] + } +]}; + +},{}],106:[function(require,module,exports){ +var torque = require('../lib/torque'); +var providers = torque.providers; + +var json, url; +QUnit.module('provider.json'); +QUnit.testStart(function() { + json = new providers.json({ + table: 'test', + user: "rambo", + resolution: 1, + steps: 10, + extra_params: { + testing: 'abcd%' + } + }); +}); + + test("url", function() { + equal("http://rambo.cartodb.com/api/v2/sql", json.url()); + }); + + test("extra_params", function() { + var url = "http://rambo.cartodb.com/api/v2/sql?q=1&testing=abcd%25"; + json.sql('1'); + equal(torque.net.lastCall().url, url); + }); + + test("no_cdn", function() { + var url = "http://rambo.cartodb.com/api/v2/sql?q=1&testing=abcd%25"; + json.options.cdn_url = { http: 'test.com' }; + json.sql('1', null, { no_cdn: true }); + equal(torque.net.lastCall().url, url); + }); + + test("getSQL", function() { + var s; + equal(json.getSQL(), "select * from test"); + json.setSQL(s='select * from test limit 10'); + equal(json.getSQL(), s); + json.setSQL(null); + equal(json.getSQL(), "select * from test"); + }); + + test("cdn_url", function() { + json.options.cdn_url = { http: 'test.com' }; + equal(json.url('a'), 'http://a.test.com/rambo/api/v2/sql'); + }); + + + + +},{"../lib/torque":10}],107:[function(require,module,exports){ +var torque = require('../lib/torque/core'); +var jsonarray_fixture = require('./data/torque.array.json'); + +QUnit.module('provider.jsonarray'); + +test("processTile", function() { + var json = new torque.providers.JsonArray({ + url: "http://test.com/{z}/{x}/{y}.json" + }); + var tile = json.proccessTile(jsonarray_fixture.rows, { + x: 0, + y: 0, + z: 0 + }); + ok(tile); + equal(jsonarray_fixture.rows.length, tile.x.length); + equal(jsonarray_fixture.rows.length, tile.y.length); + +}); + +},{"../lib/torque/core":4,"./data/torque.array.json":105}],108:[function(require,module,exports){ +var torque = require('../lib/torque/core'); + +var windshaft, url; +var lastCall; +var old_net; +QUnit.module('provider.windshaft', { + setup: function() { + old_net = torque.net.jsonp; + old_get = torque.net.get; + torque.net.jsonp = function(url, callback) { + lastCall = url; + callback({ layergroupid: 'testlg', metadata: { torque: { 0: { data_steps:10 }} } }); + }; + torque.net.get = function(url, callback) { + lastCall = url; + callback(null); + }; + windshaft = new torque.providers.windshaft({ + table: 'test', + user: "rambo", + cartocss: '#test{}', + sql: 'test', + resolution: 1, + steps: 10, + extra_params: { + testing: 'abcd%' + } + }); + }, + teardown: function() { + torque.net.jsonp = old_net; + torque.net.get = old_get; + } +}); + + test("tiler request", function() { + var layergroup = { + "version": "1.0.1", + "stat_tag": 'torque', + "layers": [{ + "type": "torque", + "options": { + "cartocss_version": "1.0.0", + "cartocss": 'Map{-torque-frame-count:10;-torque-resolution:1;-torque-aggregation-function:\'undefined\';-torque-time-attribute:\'undefined\';-torque-data-aggregation:linear;}', + "sql": 'test' + } + }] + }; + + var url = "http://rambo.cartodb.com:80/api/v1/map?config=" + encodeURIComponent(JSON.stringify(layergroup)) + "&callback=" + equal(lastCall.indexOf(url), 0); + equal(windshaft.options.data_steps, 10); + + }); + + test("url", function() { + equal(windshaft.url(), "http://rambo.cartodb.com:80"); + }); + + test("url cdn", function() { + windshaft.options.cdn_url = { http: 'cartocdn.com' }; + equal(windshaft.url(), "http://{s}.cartocdn.com/rambo"); + }); + + test("url cdn https", function() { + windshaft.options.tiler_protocol = 'https'; + windshaft._buildMapsApiTemplate(windshaft.options); + windshaft.options.cdn_url = { https: 'cartocdn.com' }; + equal(windshaft.url(), "https://cartocdn.com/rambo"); + }); + + test("named map", function() { + windshaft_named = new torque.providers.windshaft({ + table: 'test', + user: "rambo", + named_map: { + name: 'test_named' + } + }); + var url = "http://rambo.cartodb.com:80/api/v1/map/named/test_named/jsonp?config"; + equal(lastCall.indexOf(url), 0); + }); + + test("fetch tile", function() { + windshaft._ready = true; + windshaft.getTileData({x: 0, y: 1, corrected: {x: 0, y: 1}}, 2, function() {}); + equal(lastCall,"http://rambo.cartodb.com:80/api/v1/map/testlg/0/2/0/1.json.torque?testing=abcd%25"); + }); + + test("include auth_token", function() { + windshaft_named = new torque.providers.windshaft({ + table: 'test', + user: "rambo", + auth_token: 'test_auth_token', + named_map: { + name: 'test_named' + } + }); + ok(lastCall.indexOf("auth_token=test_auth_token") !== -1); + }) + + test("include stat_tag", function() { + windshaft_named = new torque.providers.windshaft({ + table: 'test', + user: "rambo", + stat_tag: 'test', + named_map: { + name: 'test_named' + } + }); + ok(lastCall.indexOf("stat_tag=test") !== -1); + }) + + test("include extra params in named maps", function() { + windshaft_named = new torque.providers.windshaft({ + table: 'test', + user: "rambo", + stat_tag: 'test', + named_map: { + name: 'test_named', + params: { + "wololo": "wololo" + } + } + }); + ok(lastCall.indexOf("wololo%22%3A%22wololo") !== -1); + }) + +test("auth_token as array param", function() { + windshaft_named = new torque.providers.windshaft({ + table: 'test', + user: "rambo", + auth_token: ['test_auth_token'], + named_map: { + name: 'test_named' + } + }); + ok(lastCall.indexOf("auth_token[]=test_auth_token") !== -1); +}); + +test("auth_token with several params as array param and present in url", function() { + windshaft_named = new torque.providers.windshaft({ + table: 'test', + user: "rambo", + auth_token: ['token1', 'token2'], + named_map: { + name: 'test_named' + } + }); + ok(lastCall.indexOf("auth_token[]=token1") !== -1); + ok(lastCall.indexOf("auth_token[]=token2") !== -1); +}); + +[ + { shouldInvokeFetchMap: true, desc: 'undefined no_fetch_map option provided should invoke _fetchMap' }, + { no_fetch_map: false, shouldInvokeFetchMap: true, desc: 'no_fetch_map=false should invoke _fetchMap' }, + { no_fetch_map: true, shouldInvokeFetchMap: false, desc: 'no_fetch_map=true should NOT invoke _fetchMap' } +].forEach(function(fetchMapCase) { + + test("no_fetch_map option: " + fetchMapCase.desc, function() { + var fetchMapFn = torque.providers.windshaft.prototype._fetchMap; + + var _fetchMapInvoked = false; + torque.providers.windshaft.prototype._fetchMap = function() { + _fetchMapInvoked = true; + }; + + new torque.providers.windshaft({ + table: 'test', + user: "rambo", + no_fetch_map: fetchMapCase.no_fetch_map + }); + + equal(_fetchMapInvoked, fetchMapCase.shouldInvokeFetchMap); + + torque.providers.windshaft.prototype._fetchMap = fetchMapFn; + }); + +}); + + +},{"../lib/torque/core":4}],109:[function(require,module,exports){ +var torque = require('../../lib/torque'); + +QUnit.module('renderer/point'); + +var DEFAULT_CARTOCSS = [ + 'Map {', + ' -torque-resolution: 1;', + '}', + '#layer {', + ' marker-fill: #662506;', + ' marker-width: 4;', + ' [value > 1] { marker-fill: #FEE391; }', + ' [value > 2] { marker-fill: #FEC44F; }', + ' [value > 3] { marker-fill: #FE9929; }', + ' [value > 4] { marker-fill: #EC7014; }', + ' [value > 5] { marker-fill: #CC4C02; }', + ' [value > 6] { marker-fill: #993404; }', + ' [value > 7] { marker-fill: #662506; }', + '}' +].join('\n'); + +var renderer = null; +QUnit.testStart(function() { + var canvas = document.createElement('canvas'); + renderer = new torque.renderer.Point(canvas, {}); +}); + +test('render shader layers', function() { + renderer.setCartoCSS(DEFAULT_CARTOCSS) + var count = 0; + renderer._renderTile = function() { ++count }; + renderer.renderTile(null, 0); + equal(count, 1); +}); + +test('render conditional point layers', function() { + var css = [ + '#test {', + 'marker-width: 10;', + '[zoom = 18] {', + 'marker-width: 20;', + '}}'].join('\n'); + + renderer.setCartoCSS(css) + + var layer = renderer._shader.getLayers()[0]; + var st = layer.getStyle({}, { zoom: 10, 'frame-offset': 0 }); + equal(st['marker-width'], 10); + st = layer.getStyle({}, { zoom: 18, 'frame-offset': 0 }); + equal(st['marker-width'], 20); +}); + +test('should generate sprite when maker-fill > 0', function() { + var css = [ + '#test {', + ' marker-width: 10;', + '}'].join('\n'); + + renderer.setCartoCSS(css) + var layer = renderer._shader.getLayers()[0]; + var sprite = renderer.generateSprite(layer, 0, { zoom: 0 }) + notEqual(sprite, null); +}); + + +test('should not generate sprite when maker-fill: 0', function() { + var css = [ + '#test {', + ' marker-width: 0;', + '}'].join('\n'); + + renderer.setCartoCSS(css) + var layer = renderer._shader.getLayers()[0]; + var sprite = renderer.generateSprite(layer, 0, { zoom: 0 }) + equal(sprite, null); +}); + +test('should not generate sprite when maker-opacity: 0', function() { + var css = [ + '#test {', + ' marker-width: 10;', + ' marker-opacity: 0;', + '}'].join('\n'); + + renderer.setCartoCSS(css) + var layer = renderer._shader.getLayers()[0]; + var sprite = renderer.generateSprite(layer, 0, { zoom: 0 }) + equal(sprite, null); +}); + +test('get value for position', function() { + var mercator = new torque.Mercator(); + tile = { + timeCount: [1], + timeIndex: [0], + renderDataPos: [0], + renderData: [5], + x: [100], + y: [3], + coord: { x: 0, y: 0, z: 0 } + }; + renderer.options = { + resolution: 1 + }; + var v = renderer.getValueFor(tile, 0, 100, 255 - 3); + var bbox = mercator.tilePixelBBox(0, 0, 0, 100, 255 - 3, 1); + equal(v.bbox[0].lat, bbox[0].lat); + equal(v.bbox[1].lat, bbox[1].lat); + equal(v.bbox[0].lon, bbox[0].lon); + equal(v.bbox[1].lon, bbox[1].lon); + equal(v.value, 5); + + v = renderer.getValueFor(tile, 0, 100, 255 - 4); + equal(v, null); + v = renderer.getValueFor(tile, 0, 99, 255 - 3); + equal(v, null); +}); + +},{"../../lib/torque":10}],110:[function(require,module,exports){ +var torque = require('../lib/torque/core'); + +QUnit.module('request'); + +asyncTest("json", 6, function(assert) { + var called = null; + torque.net.jsonp('./data/foobar.jsonp.js?callback=?', function(test) { + called = arguments; + }); + + setTimeout(function() { + var scripts = document.getElementsByTagName('script'); + var found = null; + for (var i = 0 ; !found && i < scripts.length; ++i) { + var s = scripts[i]; + if (s.getAttribute('src').indexOf('foobar.jsonp.js') !== -1) { + found = s; + } + } + var src = found.getAttribute('src'); + var fnName = src.match(/torque_.*/); + window[fnName]('test1', 2, null); + assert.equal(src.indexOf('./data/foobar.jsonp.js?callback=torque_'), 0); + assert.equal(called[0], 'test1'); + assert.equal(called[1], 2); + assert.equal(called[2], null); + assert.equal(found.parent, null); + assert.equal(window[fnName], undefined); + QUnit.start(); + }, 5); + +}); + +},{"../lib/torque/core":4}],111:[function(require,module,exports){ +require('qunitjs'); + +require('./core'); +require('./torque'); +require('./renderer/point'); +require('./provider.jsonarray'); +require('./provider.windshaft.test'); +require('./provider.json'); +require('./request'); +require('./animator'); + +},{"./animator":103,"./core":104,"./provider.json":106,"./provider.jsonarray":107,"./provider.windshaft.test":108,"./renderer/point":109,"./request":110,"./torque":112,"qunitjs":73}],112:[function(require,module,exports){ +var torque = require('../lib/torque/core'); + +QUnit.module('torque'); + + +test('gets metadata from cartocss', function() { + var STYLE = [ + "Map { ", + " buffer-size: 1;", + " -torque-frame-count: 1;", + " -torque-resolution: 2;", + " -torque-animation-duration: 31;", + " -torque-aggregation-function: 'avg(cartodb_id)';", + " -torque-time-attribute: 'time';", + " -torque-data-aggregation: 'cumulative';", + "}", + "#layer { ", + " polygon-fill: #FFF;", + "}" + ].join('\n'); + var opts = torque.common.TorqueLayer.optionsFromCartoCSS(STYLE); + equal(opts['buffer-size'], 1); + equal(opts['steps'], 1); + equal(opts['resolution'], 2); + equal(opts['animationDuration'], 31); + equal(opts['countby'], 'avg(cartodb_id)'); + equal(opts['column'],'time'); + equal(opts['data_aggregation'], 'cumulative'); +}) + +},{"../lib/torque/core":4}]},{},[111]);