├── .gitignore
├── LICENSE
├── Makefile
├── README
├── VERSION
├── js
├── controllers
│ ├── controller.js
│ └── realtime.js
├── datasources
│ └── datasource.js
├── end.js
├── inheritance.js
├── layers
│ ├── bubble.js
│ └── layer.js
├── license.js
├── maps
│ ├── canvas.js
│ ├── gmap.js
│ ├── gmap2.js
│ ├── map.js
│ └── raphael.js
├── marks
│ ├── gmap
│ │ └── mark.js
│ ├── gmap2
│ │ └── mark.js
│ ├── gmap2_shape.js
│ ├── raphael
│ │ └── mark.js
│ └── start.js
├── options.js
├── raphael_1.5.2.js
├── start.js
└── util
│ ├── colors.js
│ ├── gmap_styles.js
│ └── styles.js
└── release
└── rotarymaps_min.0.2.js
/.gitignore:
--------------------------------------------------------------------------------
1 | build
2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2014 Thumbtack
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without modification, are permitted
5 | provided that the following conditions are met:
6 |
7 | 1) Redistributions of source code must retain the above copyright notice, this list of conditions
8 | and the following disclaimer.
9 |
10 | 2) Redistributions in binary form must reproduce the above copyright notice, this list of conditions
11 | and the following disclaimer in the documentation and/or other materials provided with the
12 | distribution.
13 |
14 | 3) Neither the name of the copyright holder nor the names of its contributors may be used to
15 | endorse or promote products derived from this software without specific prior written permission.
16 |
17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
18 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
19 | FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
20 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
23 | IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
24 | THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | # RotaryMaps Makefile
2 | #
3 | # Requires YUI compressor and RaphaelJS
4 |
5 | VERSION = $(shell head -n 1 VERSION)
6 |
7 | JS_RM_FILES = \
8 | js/raphael_1.5.2.js \
9 | js/start.js \
10 | js/inheritance.js \
11 | js/options.js \
12 | js/util/colors.js \
13 | js/util/styles.js \
14 | js/util/gmap_styles.js \
15 | js/controllers/controller.js \
16 | js/controllers/realtime.js \
17 | js/datasources/datasource.js \
18 | js/maps/map.js \
19 | js/maps/canvas.js \
20 | js/maps/raphael.js \
21 | js/maps/gmap.js \
22 | js/maps/gmap2.js \
23 | js/marks/start.js \
24 | js/marks/raphael/mark.js \
25 | js/marks/gmap/mark.js \
26 | js/marks/gmap2/mark.js \
27 | js/layers/layer.js \
28 | js/layers/bubble.js \
29 | js/end.js
30 |
31 | JS_FILES = \
32 | js/license.js \
33 | $(JS_RM_FILES)
34 |
35 | all: build/rotarymaps.js
36 |
37 | min: build/rotarymaps_min.js
38 |
39 | release: release/rotarymaps_min.$(VERSION).js
40 |
41 | build/rotarymaps.js: $(JS_FILES) Makefile
42 | mkdir -p build
43 | cat $(JS_FILES) > $@
44 |
45 | build/rotarymaps_min.js: build/rotarymaps.js
46 | rm -f $@
47 | cat js/license.js >> $@
48 | curl -s --data-urlencode 'js_code@build/rotarymaps.js' --data-urlencode 'output_format=text' \
49 | --data-urlencode 'output_info=compiled_code' http://closure-compiler.appspot.com/compile \
50 | >> $@
51 |
52 | release/rotarymaps_min.$(VERSION).js: build/rotarymaps_min.js
53 | mkdir -p release
54 | cp build/rotarymaps_min.js $@
55 |
56 | clean:
57 | rm -rf build/*
58 |
59 |
--------------------------------------------------------------------------------
/README:
--------------------------------------------------------------------------------
1 | RotaryMaps
2 | ----------------
3 | Integrating RaphaelJS with Google Maps.
4 |
5 | Created by Chris Mueller at Thumbtack (www.thumbtack.com)
6 |
7 | See example at:
8 | http://thumbtack.github.com/rotarymaps/
9 |
10 | Sample code also at:
11 | https://gist.github.com/987822
12 |
--------------------------------------------------------------------------------
/VERSION:
--------------------------------------------------------------------------------
1 | 0.2
2 |
--------------------------------------------------------------------------------
/js/controllers/controller.js:
--------------------------------------------------------------------------------
1 | cg.Controller = cg.Class.extend({
2 | init: function(map, datasource, layer, options) {
3 | this.options = cg.extend(OPTIONS.CONTROLLER, options);
4 | this.map = map;
5 | this.datasource = datasource;
6 | this.layer = layer;
7 | this.datasource.addController(this);
8 | this.layer.setMap(this.map).setController(this);
9 | },
10 | refresh: function() {
11 | // Total, naive refresh
12 | var layer = this.layer;
13 | cg.each(this.datasource.getDataItems(), function(data_item) {
14 | layer.addDataItem(data_item);
15 | });
16 | return this;
17 | }
18 | });
19 |
20 |
--------------------------------------------------------------------------------
/js/controllers/realtime.js:
--------------------------------------------------------------------------------
1 | cg.Realtime = cg.Controller.extend({
2 | init: function(map, datasource, layer, options) {
3 | this._super(map, datasource, layer, cg.extend(OPTIONS.REALTIME, options));
4 | setInterval(this.deleteExpired, 5000);
5 | },
6 | updateOne: function(new_data_item) {
7 | var that = this,
8 | time = this.convertTimeIntoFutureMs(new_data_item.timestamp);
9 | window.setTimeout(function() {
10 | that.layer.addDataItem(new_data_item);
11 | }, time);
12 | return this;
13 | },
14 | updateMany: function(new_data_items) {
15 | var that = this;
16 | cg.each(new_data_items, function(new_data_item) {
17 | that.updateOne(new_data_item);
18 | });
19 | return this;
20 | },
21 | convertTimeIntoFutureMs: function(desired_time) {
22 | var now = new Date().getTime();
23 | return Math.max(0, (desired_time + this.options.expected_delay) - now);
24 | },
25 | deleteExpired: function() {
26 | // TODO
27 | }
28 | });
29 |
30 |
--------------------------------------------------------------------------------
/js/datasources/datasource.js:
--------------------------------------------------------------------------------
1 | cg.Datasource = cg.Class.extend({
2 | init: function(raw_data) {
3 | this.data = raw_data && this.formatData(raw_data) || [];
4 | this.controllers = [];
5 | this.dirty = true;
6 |
7 | this.stats = {};
8 | this.analyze();
9 | },
10 | addController: function(controller) {
11 | this.controllers.push(controller);
12 | },
13 | load: function() {
14 | // Total refresh of the data
15 | cg.each(this.controllers, function(controller) {
16 | controller.refresh();
17 | });
18 | },
19 | push: function(raw_data_item) {
20 | this.data.push(this.formatDataItem(raw_data_item));
21 | cg.each(this.controllers, function(controller) {
22 | controller.updateOne(raw_data_item);
23 | });
24 | return this;
25 | },
26 | pushMany: function(raw_data_items) {
27 | var formatted = this.formatData(raw_data_items)
28 | i = 0
29 | ii = formatted.length;
30 | for (; i < ii; i++) {
31 | this.data.push(formatted[i]);
32 | }
33 | cg.each(this.controllers, function(controller) {
34 | controller.updateMany(formatted);
35 | });
36 | return this;
37 | },
38 | formatData: function(raw_data) {
39 | var results = [],
40 | that = this;
41 |
42 | cg.each(raw_data, function(raw_data_item) {
43 | that.formatDataItem(raw_data_item);
44 | results.push(raw_data_item);
45 | });
46 |
47 | return results;
48 | },
49 | formatDataItem: function(raw_data_item) {
50 | raw_data_item.__id = cg.uniqueDataId();
51 | raw_data_item.__store = this;
52 | raw_data_item.__relative_value = function() {
53 | cg.convert(that.min(), that.max(), 0, 1, data_item.val);
54 | };
55 | },
56 | getDataItems: function() {
57 | return this.data;
58 | },
59 | analyze: function(force) {
60 | if (this.dirty || force) {
61 | this.stats.max = this.reduce(function(item, current) {
62 | return (current === null || item.val > current) ? item.val : current;
63 | });
64 |
65 | this.stats.min = this.reduce(function(item, current) {
66 | return (current === null || item.val < current) ? item.val : current;
67 | });
68 |
69 | this.dirty = false;
70 | }
71 | return this;
72 | },
73 | reduce: function(callback) {
74 | var result = null, key;
75 | for (key in this.data) {
76 | result = callback(this.data[key], result);
77 | }
78 | return result;
79 | }
80 | });
81 |
--------------------------------------------------------------------------------
/js/end.js:
--------------------------------------------------------------------------------
1 | return cg;
2 | })(Raphael.ninja());
3 |
--------------------------------------------------------------------------------
/js/inheritance.js:
--------------------------------------------------------------------------------
1 | // Simple JavaScript Inheritance
2 | // By John Resig http://ejohn.org/
3 | // MIT Licensed.
4 | //
5 | // Inspired by base2 and Prototype
6 | (function() {
7 | var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
8 | // The base Class implementation (does nothing)
9 | cg.Class = function() {};
10 |
11 | // Create a new Class that inherits from this class
12 | cg.Class.extend = function(properties) {
13 | var _super = this.prototype;
14 |
15 | // Instantiate a base class (but only create the instance,
16 | // don't run the init constructor)
17 | initializing = true;
18 | var prototype = new this();
19 | initializing = false;
20 |
21 | // Copy the properties over onto the new prototype
22 | for (var name in properties) {
23 | // Check if we're overwriting an existing function
24 | prototype[name] = typeof properties[name] == "function" &&
25 | typeof _super[name] == "function" && fnTest.test(properties[name]) ?
26 | (function(name, fn) {
27 | return function() {
28 | var tmp = this._super;
29 |
30 | // Add a new ._super() method that is the same method
31 | // but on the super-class
32 | this._super = _super[name];
33 |
34 | // The method only need to be bound temporarily, so we
35 | // remove it when we're done executing
36 | var ret = fn.apply(this, arguments);
37 | this._super = tmp;
38 |
39 | return ret;
40 | };
41 | })(name, properties[name]) :
42 | properties[name];
43 | }
44 |
45 | // The dummy class constructor
46 | function Class() {
47 | // All construction is actually done in the init method
48 | if (!initializing && this.init) {
49 | this.init.apply(this, arguments);
50 | }
51 | }
52 |
53 | // Populate our constructed prototype object
54 | Class.prototype = prototype;
55 |
56 | // Enforce the constructor to be what we expect
57 | Class.constructor = Class;
58 |
59 | // And make this class extendable
60 | Class.extend = arguments.callee;
61 |
62 | return Class;
63 | };
64 | })();
65 |
66 |
--------------------------------------------------------------------------------
/js/layers/bubble.js:
--------------------------------------------------------------------------------
1 | cg.Bubble = cg.Layer.extend({
2 | init: function(options) {
3 | this._super(cg.extend(OPTIONS.BUBBLE, options));
4 | this.markers = {};
5 | },
6 | addDataItem: function(data_item) {
7 | var fadeout_after = 0,
8 | fadeout_duration = 0,
9 | fadeout_callback = null,
10 | style = cg.extend(this.options.circle),
11 | opacity = style.opacity,
12 | radius = cg.callable(this.options.radius) ?
13 | this.options.radius(data_item) : this.options.radius,
14 | marker = this.map.circle(data_item.lat, data_item.lng, radius);
15 |
16 | style.opacity = this.options.fade_in ? 0 : style.opacity;
17 |
18 | marker.applyStyle(style);
19 | this.markers[data_item.__id] = marker;
20 |
21 | if (this.options.fade_in) {
22 | marker.node.animate({opacity: opacity}, this.options.fade_in);
23 | }
24 |
25 | // Is there a callback for when the marker was created
26 | if (this.options.marker && cg.callable(this.options.marker)) {
27 | this.options.marker(this, marker, data_item);
28 | }
29 |
30 | // Should we fade out the marker after it was created?
31 | if (this.options.fade_out) {
32 | if (this.options.fade_out instanceof Object) {
33 | fadeout_after = this.options.fade_out.after ? this.options.fade_out.after : 0;
34 | fadeout_duration = this.options.fade_out.duration ?
35 | this.options.fade_out.duration : 0;
36 | } else {
37 | fadeout_duration = parseInt(this.options.fade_out);
38 | }
39 |
40 | fadeout_callback = function() {
41 | marker.node.animate({opacity: 0}, fadeout_duration, function() {
42 | marker.remove();
43 | });
44 | };
45 |
46 | if (fadeout_after) {
47 | setTimeout(fadeout_callback, fadeout_after);
48 | } else if (fadeout_duration) {
49 | fadeout_callback();
50 | }
51 | }
52 | return this;
53 | },
54 | removeDataItem: function(id) {
55 | this._super(id);
56 | this.map.removeMarker(this.markers[id]);
57 | delete this.markers[id];
58 | return this;
59 | }
60 | });
61 |
--------------------------------------------------------------------------------
/js/layers/layer.js:
--------------------------------------------------------------------------------
1 | cg.Layer = cg.Class.extend({
2 | init: function(options) {
3 | this.options = options;
4 | this.map = null;
5 | this.controller = null;
6 | this.listeners = ["moveEnd", "zoomEnd"];
7 | },
8 | getListeners: function() {
9 | return this.listeners;
10 | },
11 | setController: function(controller) {
12 | this.controller = controller;
13 | },
14 | setMap: function(map) {
15 | var that = this;
16 | this.map = map;
17 | cg.each(this.listeners, function(listener) {
18 | map.bind(listener, that[listener]);
19 | });
20 | return this;
21 | },
22 | addDataItem: function(data_item) { },
23 | removeDataItem: function(id) { },
24 | moveEnd: function() { },
25 | zoomEnd: function(old_zoom, new_zoom) { }
26 | });
27 |
28 |
--------------------------------------------------------------------------------
/js/license.js:
--------------------------------------------------------------------------------
1 | /*
2 | * RotaryMaps.js
3 | *
4 | * Distributed under the MIT License
5 | * http://www.opensource.org/licenses/mit-license.php
6 | *
7 | * Created at thumbtack.com
8 | *
9 | */
10 |
11 |
--------------------------------------------------------------------------------
/js/maps/canvas.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thumbtack/rotarymaps/b3bf60d275489b657bf22a2d74749a0bded96bd3/js/maps/canvas.js
--------------------------------------------------------------------------------
/js/maps/gmap.js:
--------------------------------------------------------------------------------
1 | cg.GMap = cg.Map.extend({
2 | init: function(container) {
3 | this._super(container);
4 | },
5 | addMarker: function(marker) {
6 | // no-op in this case
7 | return marker;
8 | },
9 | circle: function(lat, lng, radius, options) {
10 | return this.addMarker(new cg.marks.gmap.Circle(this, lat, lng, radius));
11 | },
12 | removeMarker: function(marker) {
13 | marker.gmap_marker.setMap(null);
14 | marker.gmap_marker = null;
15 | marker = null;
16 | return this;
17 | }
18 | });
19 |
20 |
--------------------------------------------------------------------------------
/js/maps/gmap2.js:
--------------------------------------------------------------------------------
1 | cg.GMap2 = cg.Map.extend({
2 | init: function(container) {
3 | var that = this;
4 | this._super(container);
5 | GEvent.addListener(container, "zoomend", function(old_level, new_level) {
6 | that.trigger("zoomEnd", old_level, new_level);
7 | });
8 | GEvent.addListener(container, "moveend", function() {
9 | that.trigger("moveEnd");
10 | });
11 | GEvent.addListener(container, "maptypechanged", function() {
12 | that.trigger("mapTypeChanged");
13 | });
14 | },
15 | colorize: function(color, opacity) {
16 | var rects = [];
17 | rects.push([
18 | new GLatLng(-85,0),
19 | new GLatLng(85,0),
20 | new GLatLng(85,90),
21 | new GLatLng(-85,90)]);
22 | rects.push([
23 | new GLatLng(-85,90),
24 | new GLatLng(85,90),
25 | new GLatLng(85,180),
26 | new GLatLng(-85,180)]);
27 | rects.push([
28 | new GLatLng(-85,180.000001),
29 | new GLatLng(85,180.000001),
30 | new GLatLng(85,270),
31 | new GLatLng(-85,270)]);
32 | rects.push([
33 | new GLatLng(-85,270),
34 | new GLatLng(85,270),
35 | new GLatLng(85,360),
36 | new GLatLng(-85,360)]);
37 | for (var i in rects) {
38 | this.container.addOverlay(new GPolygon(rects[i], null, 0, 0, color, opacity));
39 | }
40 | },
41 | addMarker: function(marker) {
42 | this.container.addOverlay(marker.getGMapMarker());
43 | return marker;
44 | },
45 | circle: function(lat, lng, radius, options) {
46 | return this.addMarker(new cg.marks.gmap2.Circle(this, lat, lng, radius));
47 | },
48 | removeMarker: function(marker) {
49 | marker.gmap_marker.remove();
50 | marker = null;
51 | return this;
52 | }
53 | });
54 |
55 |
--------------------------------------------------------------------------------
/js/maps/map.js:
--------------------------------------------------------------------------------
1 | cg.Map = cg.Class.extend({
2 | init: function(container, options) {
3 | this.id = cg.map_counter++;
4 | this.container = container;
5 | this.listeners = [];
6 | this.controllers = [];
7 | this.options = options;
8 | },
9 | attach: function() {
10 | var controller, datasource, layer;
11 | if (arguments[0] instanceof cg.Controller) {
12 | controller = arguments[0];
13 | } else {
14 | datasource = arguments[0];
15 | layer = arguments[1];
16 | controller = new cg.Controller(this, datasource, layer);
17 | }
18 | this.controllers.push(controller);
19 | return this;
20 | },
21 | bind: function(event_type, callback) {
22 | if (!this.listeners[event_type]) {
23 | this.listeners[event_type] = [];
24 | }
25 | this.listeners[event_type].push(callback);
26 | return this;
27 | },
28 | trigger: function(event_type) {
29 | var args = Array.prototype.slice.call(arguments, 1);
30 | if (this.listeners[event_type]) {
31 | for (var i in this.listeners[event_type]) {
32 | var listener = this.listeners[event_type][i];
33 | listener.apply(listener, args);
34 | }
35 | }
36 | return this;
37 | },
38 | addMarker: function(options) { },
39 | removeMarker: function(marker) { },
40 | circle: function(options) { }
41 | });
42 |
43 |
--------------------------------------------------------------------------------
/js/maps/raphael.js:
--------------------------------------------------------------------------------
1 | cg.RaphaelMap = cg.Map.extend({
2 | init: function(container, options) {
3 | this._super(container, options);
4 | this.width = this.options.width;
5 | this.height = this.options.height;
6 | this.paper = raphael(container, this.width, this.height);
7 | },
8 | bind: function(event_type, callback) {
9 | if (!this.listeners[event_type]) {
10 | this.listeners[event_type] = [];
11 | }
12 | this.listeners[event_type].push(callback);
13 | return this;
14 | },
15 | handle: function(event_type) {
16 | var args = arguments.slice(1);
17 | if (this.listeners[event_type]) {
18 | for (var i in this.listeners[event_type]) {
19 | this.listeners[event_type][i](args);
20 | }
21 | }
22 | return this;
23 | },
24 | layer: function(layer_object) {
25 | var listeners = layer_object.listensOn();
26 | layer_object.setMap(this);
27 | for (var i in listeners) {
28 | this.bind(listeners[i], layer_object[listeners[i]]);
29 | }
30 | return this;
31 | },
32 | circle: function(lat, lng, radius, options) {
33 | return new cg.marks.raphael.Circle(this, this.lngToX(lng), this.latToY(lat), radius);
34 | },
35 | removeMarker: function(marker) {
36 | // assume marker is a valid Raphael SVG/VML object
37 | marker.remove();
38 | return this;
39 | },
40 | lngToX: function(lng) {
41 | return cg.convert(-180, 180, 0, this.width, lng);
42 | },
43 | latToY: function(lat) {
44 | return this.height - cg.convert(-90, 90, 0, this.height, lat);
45 | }
46 | });
47 |
48 |
49 |
--------------------------------------------------------------------------------
/js/marks/gmap/mark.js:
--------------------------------------------------------------------------------
1 | cg.marks.gmap = {};
2 |
3 | cg.marks.gmap.Mark = cg.Mark.extend({
4 | init: function(map) {
5 | this._super(map);
6 | this.gmap_marker = null;
7 | initGMapMarks();
8 | },
9 | getGMapMarker: function() {
10 | return this.gmap_marker;
11 | }
12 | });
13 |
14 | cg.marks.gmap.Circle = cg.marks.gmap.Mark.extend({
15 | init: function(map, lat, lng, radius) {
16 | this._super(map);
17 | this.gmap_marker = new GMapPaper(map.container, lat, lng, radius * 2 + 2, radius * 2 + 2);
18 | this.node = this.gmap_marker.paper.circle(radius + 1, radius + 1, radius);
19 | }
20 | });
21 |
22 | var GMapPaper = null;
23 |
24 | // Separate out the initilization, parent classes wouldn't be available otherwise
25 | function initGMapMarks() {
26 | if (GMapPaper !== null) {
27 | return;
28 | }
29 |
30 | GMapPaper = function(gmap, lat, lng, width, height) {
31 | this.bounds_ = null;
32 | this.map_ = null;
33 | this.div_ = null;
34 | this.image_ = null;
35 |
36 | this.point = new google.maps.LatLng(lat, lng);
37 | this.width = width;
38 | this.height = height;
39 | this.div_ = document.createElement("div"),
40 | this.div_.style.position = "absolute";
41 | this.paper = raphael(this.div_, width, height);
42 |
43 | this.setMap(gmap);
44 | };
45 |
46 | GMapPaper.prototype = new google.maps.OverlayView();
47 |
48 | GMapPaper.prototype.onAdd = function() {
49 | var panes = this.getPanes();
50 | panes.overlayLayer.appendChild(this.div_);
51 | };
52 |
53 | GMapPaper.prototype.onRemove = function() {
54 | this.div_.parentNode.removeChild(this.div_);
55 | this.div_ = null;
56 | };
57 |
58 | GMapPaper.prototype.draw = function() {
59 | var coord, height, projection = this.getProjection();
60 | coord = projection.fromLatLngToDivPixel(this.point),
61 | height = parseInt(this.div_.clientHeight);
62 | this.div_.style.left = (coord.x - (this.width * .5)) + "px";
63 | this.div_.style.top = (coord.y + (this.height * .5) - height) + "px";
64 | };
65 | }
66 |
67 |
--------------------------------------------------------------------------------
/js/marks/gmap2/mark.js:
--------------------------------------------------------------------------------
1 | cg.marks.gmap2 = {};
2 |
3 | cg.marks.gmap2.Mark = cg.Mark.extend({
4 | init: function(map) {
5 | this._super(map);
6 | this.gmap_marker = null;
7 | initGMap2Marks();
8 | },
9 | getGMapMarker: function() {
10 | return this.gmap_marker;
11 | }
12 | });
13 |
14 | cg.marks.gmap2.Circle = cg.marks.gmap2.Mark.extend({
15 | init: function(map, lat, lng, radius) {
16 | this._super(map);
17 | this.gmap_marker = new GMap2Paper(lat, lng, radius * 2 + 2, radius * 2 + 2);
18 | this.node = this.gmap_marker.paper.circle(radius + 1, radius + 1, radius);
19 | }
20 | });
21 |
22 | var GMap2Paper = null;
23 |
24 | // Separate out the initilization, parent classes wouldn't be available otherwise
25 | function initGMap2Marks() {
26 | if (GMap2Paper !== null) {
27 | return;
28 | }
29 |
30 | GMap2Paper = function(lat, lng, width, height) {
31 | this.point = new GLatLng(lat, lng);
32 | this.width = width;
33 | this.height = height;
34 | this.div_ = document.createElement("div"),
35 | this.div_.style.position = "absolute";
36 | this.paper = raphael(this.div_, width, height);
37 | };
38 |
39 | GMap2Paper.prototype = cg.inheritFrom(GOverlay);
40 |
41 | GMap2Paper.prototype.initialize = function(map) {
42 | this.map_ = map;
43 | map.getPane(G_MAP_MARKER_PANE).appendChild(this.div_);
44 | };
45 |
46 | GMap2Paper.prototype.remove = function() {
47 | this.div_.parentNode.removeChild(this.div_);
48 | this.div_ = null;
49 | };
50 |
51 | GMap2Paper.prototype.copy = function() {
52 | return new GMap2Paper(this.point, width, height);
53 | };
54 |
55 | GMap2Paper.prototype.redraw = function(force) {
56 | var coord, height;
57 | if (force) {
58 | coord = this.map_.fromLatLngToDivPixel(this.point),
59 | height = parseInt(this.div_.clientHeight);
60 | this.div_.style.left = (coord.x - (this.width * .5)) + "px";
61 | this.div_.style.top = (coord.y + (this.height * .5) - height) + "px";
62 | }
63 | };
64 |
65 | GMap2Paper.prototype.setPoint = function(point) {
66 | this.point = point;
67 | this.redraw(true);
68 | };
69 | }
70 |
--------------------------------------------------------------------------------
/js/marks/gmap2_shape.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Basic vector shape
3 | */
4 |
5 | Shape = function( point, radius, options ) {
6 | if( !point ) { return; }
7 | this.point = point;
8 | this.pixelOffset = new GSize(-1*radius,radius);
9 | this.radius = radius;
10 | this.overlap = false;
11 | this.hidden = false;
12 |
13 | var defaults = {
14 | "shape": "circle", // or "square"
15 | "color": "#fc8d59",
16 | "stroke": "#000",
17 | "colorHover": "#ffffbf",
18 | "colorActive": "#ffffbf",
19 | "opacity":0.8,
20 |
21 | /* location in google map pane hierarchy */
22 | "isBackground": false, // set to "true" to have this be unclickable in the background
23 | "zIndexProcess": null,
24 |
25 | /* interaction */
26 | "animate": false,
27 | "onclick": null,
28 | "onmouseover": null,
29 | "onmouseout": null,
30 | "infoWindow": null
31 | };
32 | this.opts = cg.parseOptions( defaults, options );
33 | if( this.opts.opacity < 1 ) {
34 | this.opts.opacity *= 100.0;
35 | }
36 |
37 | this.percentOpacity = this.opts.opacity;
38 | };
39 |
40 | Shape.prototype = cg.extend( GOverlay );
41 |
42 | Shape.prototype.makeNode = function() {
43 | // default is circle
44 | var div = document.createElement("div"),
45 | radius = ( this.opts.animate ) ? this.radius * .5 : this.radius - 1,
46 | color = this.opts.color,
47 | bcolor = Raphael.rgb2hsb( color );
48 | div.style.position = "absolute";
49 | this.paper = Raphael( div, this.radius*2, this.radius*2 );
50 | var c = this.paper.circle( this.radius, this.radius, radius ).attr({
51 | gradient:"90-hsb(" + bcolor.h + "," + bcolor.s + "," + Math.max(0,bcolor.b-.20) + ")-" + color, stroke:this.opts.stroke
52 | });
53 | this.div_ = div;
54 | if( this.opts.animate ) {
55 | c.animate({r:this.radius - 1 }, 2000, ">");
56 | }
57 | return c;
58 | };
59 |
60 | Shape.prototype.initialize = function(map) {
61 | var that = this;
62 | var c = this.makeNode();
63 | this.maintainOver = false;
64 |
65 | if( this.opts.isBackground ) {
66 | map.getPane( G_MAP_FLOAT_SHADOW_PANE ).appendChild( this.div_ );
67 | } else {
68 | if( cg.callable( that.opts.onclick ) || that.opts.infoWindow ) {
69 | c.node.style.cursor = "pointer";
70 | }
71 |
72 | try {
73 | c.node.onclick = function() {
74 | if( that.opts.infoWindow ) {
75 | var info = map.openInfoWindow( that.point, that.opts.infoWindow,
76 | { onCloseFn: function() {
77 | that.maintainOver = false;
78 | var color = that.opts.color,
79 | bcolor = Raphael.rgb2hsb( color );
80 | c.attr({ gradient:"90-hsb(" + bcolor.h + "," + bcolor.s + "," + Math.max(0,bcolor.b-.20) + ")-" + color });
81 | }});
82 | if( !that.maintainOver ) {
83 | var color = that.opts.colorHover,
84 | bcolor = Raphael.rgb2hsb( color );
85 | c.attr({ gradient:"90-hsb(" + bcolor.h + "," + bcolor.s + "," + Math.max(0,bcolor.b-.20) + ")-" + color });
86 | }
87 | that.maintainOver = !that.maintainOver;
88 | }
89 | if( cg.callable( that.opts.onclick ) ) { that.opts.onclick(); }
90 | };
91 | c.node.onmouseover = function() {
92 | var color = that.opts.colorHover,
93 | bcolor = Raphael.rgb2hsb( color );
94 | c.attr({ gradient:"90-hsb(" + bcolor.h + "," + bcolor.s + "," + Math.max(0,bcolor.b-.20) + ")-" + color });
95 | if( cg.callable( that.opts.onmouseover ) ) { that.opts.onmouseover(); }
96 | };
97 | c.node.onmouseout = function() {
98 | if(!that.maintainOver) {
99 | var color = that.opts.color,
100 | bcolor = Raphael.rgb2hsb( color );
101 | c.attr({ gradient:"90-hsb(" + bcolor.h + "," + bcolor.s + "," + Math.max(0,bcolor.b-.20) + ")-" + color });
102 | }
103 | if( cg.callable( that.opts.onmouseout ) ) { that.opts.onmouseout(); }
104 | };
105 | } catch(e) { }
106 |
107 | map.getPane( G_MAP_MARKER_PANE ).appendChild( this.div_ );
108 | }
109 |
110 | this.map_ = map;
111 |
112 | if (this.percentOpacity) {
113 | if(typeof(this.div_.style.filter)=='string'){this.div_.style.filter='alpha(opacity:'+this.percentOpacity+')';}
114 | if(typeof(this.div_.style.KHTMLOpacity)=='string'){this.div_.style.KHTMLOpacity=this.percentOpacity/100;}
115 | if(typeof(this.div_.style.MozOpacity)=='string'){this.div_.style.MozOpacity=this.percentOpacity/100;}
116 | if(typeof(this.div_.style.opacity)=='string'){this.div_.style.opacity=this.percentOpacity/100;}
117 | }
118 | if( this.opts.zIndexProcess ) {
119 | try {
120 | this.div_.style.zIndex = this.opts.zIndexProcess();
121 | } catch(e) {
122 | // TODO fix this in IE; non-essential for basic rendering
123 | }
124 | }
125 | if( this.hidden ) {
126 | this.hide();
127 | }
128 | };
129 |
130 | Shape.prototype.remove = function() {
131 | this.div_.parentNode.removeChild(this.div_);
132 | };
133 |
134 | Shape.prototype.copy = function() {
135 | return new Shape(this.point, this.pixelOffset, this.percentOpacity, this.overlap);
136 | };
137 |
138 | Shape.prototype.redraw = function(force) {
139 | var p = this.map_.fromLatLngToDivPixel(this.point);
140 | var h = parseInt(this.div_.clientHeight);
141 | this.div_.style.left = (p.x + this.pixelOffset.width) + "px";
142 | this.div_.style.top = (p.y +this.pixelOffset.height - h) + "px";
143 | };
144 |
145 | Shape.prototype.show = function() {
146 | if (this.div_) {
147 | this.div_.style.display="";
148 | this.redraw();
149 | }
150 | this.hidden = false;
151 | };
152 |
153 | Shape.prototype.hide = function() {
154 | if (this.div_) {
155 | this.div_.style.display="none";
156 | }
157 | this.hidden = true;
158 | };
159 |
160 | Shape.prototype.isHidden = function() {
161 | return this.hidden;
162 | };
163 |
164 | Shape.prototype.supportsHide = function() {
165 | return true;
166 | };
167 |
168 | Shape.prototype.setContents = function(html) {
169 | this.html = html;
170 | this.div_.innerHTML = '
' + this.html + '
' ;
171 | this.redraw(true);
172 | };
173 |
174 | Shape.prototype.setPoint = function(point) {
175 | this.point = point;
176 | if( this.overlap ) {
177 | var z = GOverlay.getZIndex( this.point.lat() );
178 | this.div_.style.zIndex = z;
179 | }
180 | this.redraw( true );
181 | };
182 |
183 | Shape.prototype.setOpacity = function( percentOpacity ) {
184 | if( percentOpacity ) {
185 | if( percentOpacity<0 ) { percentOpacity = 0; }
186 | if(percentOpacity>100){ percentOpacity=100; }
187 | }
188 | this.percentOpacity = percentOpacity;
189 | if ( this.percentOpacity ) {
190 | if( typeof( this.div_.style.filter ) == 'string' ){ this.div_.style.filter = 'alpha(opacity:' + this.percentOpacity + ')'; }
191 | if( typeof( this.div_.style.KHTMLOpacity ) == 'string' ){ this.div_.style.KHTMLOpacity = this.percentOpacity/100; }
192 | if( typeof( this.div_.style.MozOpacity ) == 'string' ){ this.div_.style.MozOpacity = this.percentOpacity/100; }
193 | if( typeof( this.div_.style.opacity ) == 'string' ){ this.div_.style.opacity = this.percentOpacity/100; }
194 | }
195 | };
196 |
197 | Shape.prototype.getPoint = function() {
198 | return this.point;
199 | };
200 |
201 |
--------------------------------------------------------------------------------
/js/marks/raphael/mark.js:
--------------------------------------------------------------------------------
1 | cg.marks.raphael = {};
2 |
3 | cg.marks.raphael.Mark = cg.Mark.extend({
4 | init: function(map) {
5 | this._super(map);
6 | }
7 | });
8 |
9 | cg.marks.raphael.Circle = cg.marks.raphael.Mark.extend({
10 | init: function(map, x, y, radius) {
11 | this._super(map);
12 | this.node = this.map.paper.circle(x, y, radius);
13 | }
14 | });
15 |
--------------------------------------------------------------------------------
/js/marks/start.js:
--------------------------------------------------------------------------------
1 | cg.marks = {};
2 |
3 | cg.Mark = cg.Class.extend({
4 | init: function(map, id) {
5 | // Rotary Map class
6 | this.map = map;
7 | // SVG node
8 | this.node = null;
9 | },
10 | remove: function() {
11 | this.map.removeMarker(this);
12 | },
13 | applyStyle: function(options, override) {
14 | cg.applySvgStyle(this.node, options, override);
15 | }
16 | });
17 |
18 |
--------------------------------------------------------------------------------
/js/options.js:
--------------------------------------------------------------------------------
1 | var OPTIONS = {};
2 |
3 | // How to style a Raphael SVG circle
4 | OPTIONS.CIRCLE = {
5 | // SVG attributes, see http://raphaeljs.com/reference.html#attr
6 | cursor: "pointer",
7 | fill: "#ff0000",
8 | opacity: 1,
9 | stroke: "#333",
10 | "stroke-width": 1,
11 |
12 | // Custom attributes
13 | emboss: true
14 | };
15 |
16 | OPTIONS.CLUSTER = {
17 | enable_dots: true,
18 | enable_grid: false,
19 | combine: function(a, b) { return a + b; },
20 | radius_from_value: function(val) { return val; },
21 |
22 | // colors for dots
23 | color: "#fc8d59",
24 | stroke: "#000",
25 | hover_color: "#ffffbf",
26 | active_color: "#ffffbf",
27 | opacity: 0.8,
28 |
29 | // grid
30 | grid_color: "#5e4fa2",
31 | grid_size: 24.0,
32 |
33 | // when user clicks on a grid cell
34 | onclick: function(grid_cell) {
35 | cg.log("onclick");
36 | }
37 | };
38 |
39 | OPTIONS.CHOROPLETH = {
40 | color_scheme: "spectral",
41 | reverse_colors: false
42 | };
43 |
44 | OPTIONS.BUBBLE = {
45 | radius: function(data_item) {
46 | return data_item && data_item.val ? data_item.val : 1;
47 | },
48 | // could be a callback function to return the color
49 | color_by: "value",
50 | // Number of seconds to fade in
51 | fade_in: null,
52 | // Fade the marker after it is created
53 | // - Integer: simple fade, starts immediately, duration is in ms
54 | // - Object: {after: N, duration: M} N, M are milliseconds
55 | fade_out: {
56 | after: null,
57 | duration: null
58 | },
59 | // When a marker is created, call this callback
60 | marker: null,
61 | circle: OPTIONS.CIRCLE
62 | };
63 |
64 | OPTIONS.CONTROLLER = {};
65 |
66 | OPTIONS.REALTIME = {
67 | expected_delay: 60000
68 | };
69 |
70 | OPTIONS.TIMELINE = {
71 | something: true
72 | };
73 |
--------------------------------------------------------------------------------
/js/start.js:
--------------------------------------------------------------------------------
1 | var RotaryMaps = window.RotaryMaps = (function(raphael) {
2 |
3 | var cg = {},
4 | map_counter = 0,
5 | data_id = 0,
6 | fudge = 0.0001;
7 |
8 | cg.log = function() {
9 | if (window.console) {
10 | console.log(arguments);
11 | }
12 | };
13 |
14 | cg.callable = function(val) {
15 | return typeof val == "function";
16 | };
17 |
18 | cg.isObject = function(val) {
19 | return typeof val == "object";
20 | };
21 |
22 | cg.convert = function(min_in, max_in, min_out, max_out, val) {
23 | val = parseFloat(Math.min(max_in, Math.max(min_in, val)))
24 | return parseFloat(val - min_in) / parseFloat(max_in - min_in) * (max_out - min_out) + min_out;
25 | };
26 |
27 | cg.extend = function() {
28 | var rtn = {},
29 | i = 0,
30 | ii = arguments.length;
31 |
32 | for (; i < ii; i++) {
33 | cg.each(arguments[i], function(val, key) {
34 | if (val && cg.isObject(val) && !cg.callable(val)) {
35 | rtn[key] = cg.extend(rtn[key] && cg.isObject(rtn[key]) ? rtn[key] : {}, val);
36 | } else {
37 | rtn[key] = val;
38 | }
39 | });
40 | }
41 | return rtn;
42 | };
43 |
44 | cg.config = function(option, value) {
45 | if (option instanceof Object) {
46 | for (var key in option) {
47 | cg.config(key, option[key]);
48 | }
49 | return;
50 | }
51 | switch (option) {
52 | case "fudge":
53 | fudge = value;
54 | break;
55 | case "logging":
56 | if (!value) {
57 | // turn off logging
58 | cg.log = function() {};
59 | }
60 | break;
61 | case "raphael":
62 | // Use a custom Raphael (e.g. a globally scoped one instead of the local one)
63 | raphael = value;
64 | break;
65 | default:
66 | cg.log(option, "Not available");
67 | };
68 | };
69 |
70 | cg.uniqueDataId = function() {
71 | return data_id++;
72 | };
73 |
74 | cg.each = function(arr, callback) {
75 | for (var i in arr) {
76 | callback(arr[i], i);
77 | }
78 | };
79 |
80 | cg.inheritFrom = function(f) {
81 | function g() {}
82 | g.prototype = f.prototype || f;
83 | return new g();
84 | };
85 |
86 |
87 | // TODO - build AUTO or TEMPLATE methods to make construction of maps simpler
88 |
89 | cg.autoController = function(dom_element, options) {
90 | var controller = new cg.Controller(options),
91 | map = new cg.SvgMap(dom_element);
92 | return controller.addMap(map);
93 | };
94 |
95 | cg.auto = {
96 | bubble: function(dom_element, data, options) {
97 | // etc etc
98 | }
99 | };
100 |
--------------------------------------------------------------------------------
/js/util/colors.js:
--------------------------------------------------------------------------------
1 | cg.GRADIENTS = {
2 | "spectral": ['#9e0142','#d53e4f','#f46d43','#fdae61','#fee08b','#ffffbf','#e6f598','#abdda4','#66c2a5','#3288bd','#5e4fa2'],
3 | "stoplight": ['#a50026','#d73027','#f46d43','#fdae61','#fee08b','#ffffbf','#d9ef8b','#a6d96a','#66bd63','#1a9850','#006837'],
4 | "accent": ['#7fc97f','#beaed4','#fdc086','#ffff99','#386cb0','#f0027f','#bf5b17','#666666'],
5 | "sunrise": ['#fff7ec','#fee8c8','#fdd49e','#fdbb84','#fc8d59','#ef6548','#d7301f','#b30000','#7f0000'],
6 | "floyd": ['#fff7fb','#ece7f2','#d0d1e6','#a6bddb','#74a9cf','#3690c0','#0570b0','#045a8d','#023858'],
7 | "majesty": ['#f7fcfd','#e0ecf4','#bfd3e6','#9ebcda','#8c96c6','#8c6bb1','#88419d','#810f7c','#4d004b'],
8 | "stoic" : ['#67001f','#b2182b','#d6604d','#f4a582','#fddbc7','#ffffff','#e0e0e0','#bababa','#878787','#4d4d4d','#1a1a1a'],
9 | "meadow": ['#fff7fb','#ece2f0','#d0d1e6','#a6bddb','#67a9cf','#3690c0','#02818a','#016c59','#014636'],
10 | "mars": ['#7f3b08','#b35806','#e08214','#fdb863','#fee0b6','#f7f7f7','#d8daeb','#b2abd2','#8073ac','#542788','#2d004b'],
11 | "jupiter": ['#ffffcc','#ffeda0','#fed976','#feb24c','#fd8d3c','#fc4e2a','#e31a1c','#bd0026','#800026'],
12 | "neptune": ['#f7fbff','#deebf7','#c6dbef','#9ecae1','#6baed6','#4292c6','#2171b5','#08519c','#08306b'],
13 | "pluto": ['#f7f4f9','#e7e1ef','#d4b9da','#c994c7','#df65b0','#e7298a','#ce1256','#980043','#67001f'],
14 | "ink": ['#ffffff','#f0f0f0','#d9d9d9','#bdbdbd','#969696','#737373','#525252','#252525','#000000'],
15 | "reef": ['#f7fcf0','#e0f3db','#ccebc5','#a8ddb5','#7bccc4','#4eb3d3','#2b8cbe','#0868ac','#084081']
16 | };
17 |
18 | cg.COLOR_BLOCKS = {
19 | // Non-gradient colors schemes. Must be simple Arrays
20 | "oneset" : ['#e41a1c','#377eb8','#4daf4a','#984ea3','#ff7f00','#ffff33','#a65628','#f781bf','#999999'],
21 | "twoset": ['#a50026','#d73027','#f46d43','#fdae61','#fee08b','#ffffbf','#d9ef8b','#a6d96a','#66bd63','#1a9850','#006837'],
22 | "threeset": ['#8dd3c7','#ffffb3','#bebada','#fb8072','#80b1d3','#fdb462','#b3de69','#fccde5','#d9d9d9','#bc80bd','#ccebc5','#ffed6f'],
23 | "falcon": ['#1b9e77','#d95f02','#7570b3','#e7298a','#66a61e','#e6ab02','#a6761d','#666666'],
24 | "paired": ['#a6cee3','#1f78b4','#b2df8a','#33a02c','#fb9a99','#e31a1c','#fdbf6f','#ff7f00','#cab2d6','#6a3d9a','#ffff99']
25 | };
26 |
27 | cg.colorScheme = function(name) {
28 | var gradient_min = null,
29 | gradient_max = null,
30 | scaled_gradient = {},
31 | num, pos;
32 |
33 | if (cg.GRADIENTS.hasOwnProperty(name)) {
34 | return cg.gradientColorScheme(name);
35 | } else if (cg.COLOR_BLOCKS.hasOwnProperty(name)) {
36 | return cg.blockColorScheme(name);
37 | } else {
38 | throw "Invalid color scheme";
39 | }
40 | };
41 |
42 | cg.gradientColorScheme = function(name) {
43 | var gradient = null,
44 | gradient_min = null,
45 | gradient_max = null,
46 | scaled_gradient = {},
47 | num, pos;
48 |
49 | if (cg.GRADIENTS.hasOwnProperty(name)) {
50 | gradient = cg.GRADIENTS[name];
51 | } else {
52 | throw "Invalid color scheme.";
53 | }
54 |
55 | for (num in gradient) {
56 | if (gradient_min === null || num < gradient_min) {
57 | gradient_min = num;
58 | }
59 | if (gradient_max === null || num > gradient_max) {
60 | gradient_max = num;
61 | }
62 | }
63 |
64 | for (num in gradient) {
65 | pos = cg.convert(gradient_min, gradient_max, 0, 1, num);
66 | scaled_gradient[pos] = gradient[num];
67 | }
68 |
69 | return function(val, lock_to_color) {
70 | // val should be from 0 to 1, and we'll return the color on the gradient
71 | // that matches
72 | var previous_step = null,
73 | pos = null;
74 |
75 | if (val - fudge < 0) {
76 | // return the first gradient element
77 | for (var key in scaled_gradient) {
78 | return scaled_gradient[key];
79 | }
80 | } else if (val + fudge > 1) {
81 | // return the last gradient element
82 | for (var key in scaled_gradient) {
83 | previous_step = scaled_gradient[key];
84 | }
85 | return previous_step;
86 | } else {
87 | // find something in the middle
88 | for (var key in scaled_gradient) {
89 | if (key == val) {
90 | return val;
91 | }
92 | if (val > key) {
93 | previous_step = key;
94 | continue;
95 | } else {
96 | pos = cg.convert(previous_step, key, 0, 1, val);
97 | // TODO Now in theory we find the color in between the two colors
98 | return previous_step;
99 | }
100 | }
101 | return previous_step;
102 | }
103 | };
104 | };
105 |
106 | cg.blockColorScheme = function(scheme_name) {
107 | // these color schemes aren't meant to be gradations
108 | var scheme = cg.COLOR_BLOCKS[scheme_name];
109 |
110 | return function(val) {
111 | var pos = parseInt(Math.floor(cg.scale(0, 1, 0, scheme.length + 1, val)));
112 | return scheme[Math.max(0, Math.min(pos, scheme.length))];
113 | };
114 | };
115 |
116 |
--------------------------------------------------------------------------------
/js/util/gmap_styles.js:
--------------------------------------------------------------------------------
1 | // Some of these are from http://www.41latitude.com/post/1268734799/google-styled-maps
2 | // Some are custom built
3 |
4 | cg.GMAP_STYLES = {};
5 |
6 | cg.GMAP_STYLES.Neutral = [
7 | {
8 | featureType: "administrative",
9 | elementType: "all",
10 | stylers: [
11 | { saturation: -100 }
12 | ]
13 | },{
14 | featureType: "landscape",
15 | elementType: "all",
16 | stylers: [
17 | { saturation: -100 }
18 | ]
19 | },{
20 | featureType: "poi",
21 | elementType: "all",
22 | stylers: [
23 | { saturation: -100 }
24 | ]
25 | },{
26 | featureType: "road",
27 | elementType: "all",
28 | stylers: [
29 | { saturation: -100 }
30 | ]
31 | },{
32 | featureType: "transit",
33 | elementType: "all",
34 | stylers: [
35 | { saturation: -100 }
36 | ]
37 | },{
38 | featureType: "water",
39 | elementType: "all",
40 | stylers: [
41 | { saturation: -100 }
42 | ]
43 | }
44 | ];
45 |
46 | cg.GMAP_STYLES.Subtle = [
47 | {
48 | featureType: "all",
49 | elementType: "all",
50 | stylers: [
51 | { saturation: -47 }
52 | ]
53 | },{
54 | featureType: "poi",
55 | elementType: "all",
56 | stylers: [
57 | { visibility: "off" }
58 | ]
59 | },{
60 | featureType: "water",
61 | elementType: "all",
62 | stylers: [
63 | { saturation: -68 },
64 | { visibility: "on" },
65 | { lightness: 50 }
66 | ]
67 | },{
68 | featureType: "road.local",
69 | elementType: "all",
70 | stylers: [
71 | { visibility: "simplified" }
72 | ]
73 | },{
74 | featureType: "road.arterial",
75 | elementType: "all",
76 | stylers: [
77 | { visibility: "simplified" }
78 | ]
79 | },{
80 | featureType: "road.highway",
81 | elementType: "all",
82 | stylers: [
83 | { visibility: "simplified" }
84 | ]
85 | },{
86 | featureType: "administrative.province",
87 | elementType: "all",
88 | stylers: [
89 | { visibility: "simplified" }
90 | ]
91 | },{
92 | featureType: "transit",
93 | elementType: "all",
94 | stylers: [
95 |
96 | ]
97 | }
98 | ];
99 |
100 | cg.setGMapStyle = function(gmap, name) {
101 | var options = {
102 | name: name
103 | },
104 | map_type = new google.maps.StyledMapType(cg.GMAP_STYLES[name], options);
105 |
106 | gmap.mapTypes.set(name, map_type);
107 | gmap.setMapTypeId(name);
108 |
109 | };
110 |
--------------------------------------------------------------------------------
/js/util/styles.js:
--------------------------------------------------------------------------------
1 | cg.applySvgStyle = function(svg_object, options, override) {
2 | var bcolor = null,
3 | style = cg.extend(options, override || {});
4 |
5 | // Custom options
6 | if (style.emboss) {
7 | bcolor = raphael.rgb2hsb(style.fill);
8 | style.fill = "90-hsb(" + bcolor.h + "," + bcolor.s + "," +
9 | Math.max(0, bcolor.b - .20) + ")-" + style.fill;
10 | }
11 |
12 | svg_object.attr(style);
13 | };
14 |
15 |
--------------------------------------------------------------------------------
/release/rotarymaps_min.0.2.js:
--------------------------------------------------------------------------------
1 | /*
2 | * RotaryMaps.js
3 | *
4 | * Distributed under the MIT License
5 | * http://www.opensource.org/licenses/mit-license.php
6 | *
7 | * Created at thumbtack.com
8 | *
9 | */
10 |
11 | /*
12 | * Raphael 1.5.2 - JavaScript Vector Library
13 | *
14 | * Copyright (c) 2010 Dmitry Baranovskiy (http://raphaeljs.com)
15 | * Licensed under the MIT (http://raphaeljs.com/license.html) license.
16 | */
17 | (function(){function aI(){if(aI.is(arguments[0],a7)){var b=arguments[0],d=F[bB](aI,b.splice(0,3+aI.is(b[0],aF))),R=d.set();for(var E=0,S=b[s];E';aB=ax.firstChild;aB.style.behavior="url(#default#VML)";if(!(aB&&typeof aB.adj=="object")){return aI.type=null}ax=null}aI.svg=!(aI.vml=aI.type=="VML");bz[bE]=aI[bE];aZ=bz[bE];aI._id=0;aI._oid=0;aI.fn={};aI.is=function(d,b){b=bG.call(b);if(b=="finite"){return !ap[ag](+d)}return(b=="null"&&d===null)||(b==typeof d)||(b=="object"&&d===Object(d))||(b=="array"&&Array.isArray&&Array.isArray(d))||aT.call(d).slice(8,-1).toLowerCase()==b};aI.angle=function(R,bH,e,S,d,E){if(d==null){var b=R-e,bI=bH-S;if(!b&&!bI){return 0}return((b<0)*180+ao.atan(-bI/-b)*180/aM+360)%360}else{return aI.angle(R,bH,d,E)-aI.angle(e,S,d,E)}};aI.rad=function(b){return b%360*aM/180};aI.deg=function(b){return b*180/aM%360};aI.snapTo=function(d,E,b){b=aI.is(b,"finite")?b:10;if(aI.is(d,a7)){var e=d.length;while(e--){if(aq(d[e]-E)<=b){return d[e]}}}else{d=+d;var R=E%d;if(Rd-b){return E-R+d}}return E};function j(){var d=[],b=0;for(;b<32;b++){d[b]=(~~(ao.random()*16))[a0](16)}d[12]=4;d[16]=((d[16]&3)|8)[a0](16);return"r-"+d[aW]("")}aI.setWindow=function(b){aQ=b;aa=aQ.document};var a9=function(E){if(aI.vml){var b=/^\s+|\s+$/g;var S;try{var bH=new ActiveXObject("htmlfile");bH.write("");bH.close();S=bH.body}catch(bI){S=createPopup().document.body}var d=S.createTextRange();a9=aA(function(bJ){try{S.style.color=bC(bJ)[bs](b,aP);var bK=d.queryCommandValue("ForeColor");bK=((bK&255)<<16)|(bK&65280)|((bK&16711680)>>>16);return"#"+("000000"+bK[a0](16)).slice(-6)}catch(bL){return"none"}})}else{var R=aa.createElement("i");R.title="Rapha\xebl Colour Picker";R.style.display="none";aa.body[bk](R);a9=aA(function(e){R.style.color=e;return aa.defaultView.getComputedStyle(R,aP).getPropertyValue("color")})}return a9(E)},aC=function(){return"hsb("+[this.h,this.s,this.b]+")"},M=function(){return"hsl("+[this.h,this.s,this.l]+")"},B=function(){return this.hex};aI.hsb2rgb=function(E,e,d,R){if(aI.is(E,"object")&&"h" in E&&"s" in E&&"b" in E){d=E.b;e=E.s;E=E.h;R=E.o}return aI.hsl2rgb(E,e,d/2,R)};aI.hsl2rgb=function(bH,bO,E,e){if(aI.is(bH,"object")&&"h" in bH&&"s" in bH&&"l" in bH){E=bH.l;bO=bH.s;bH=bH.h}if(bH>1||bO>1||E>1){bH/=360;bO/=100;E/=100}var bM={},bJ=["r","g","b"],bI,bL,S,d,bK,bN;if(!bO){bM={r:E,g:E,b:E}}else{if(E<0.5){bI=E*(1+bO)}else{bI=E+bO-E*bO}bL=2*E-bI;for(var R=0;R<3;R++){S=bH+1/3*-(R-1);S<0&&S++;S>1&&S--;if(S*6<1){bM[bJ[R]]=bL+(bI-bL)*6*S}else{if(S*2<1){bM[bJ[R]]=bI}else{if(S*3<2){bM[bJ[R]]=bL+(bI-bL)*(2/3-S)*6}else{bM[bJ[R]]=bL}}}}}bM.r*=255;bM.g*=255;bM.b*=255;bM.hex="#"+(16777216|bM.b|(bM.g<<8)|(bM.r<<16)).toString(16).slice(1);aI.is(e,"finite")&&(bM.opacity=e);bM.toString=B;return bM};aI.rgb2hsb=function(b,d,bI){if(d==null&&aI.is(b,"object")&&"r" in b&&"g" in b&&"b" in b){bI=b.b;d=b.g;b=b.r}if(d==null&&aI.is(b,af)){var bK=aI.getRGB(b);b=bK.r;d=bK.g;bI=bK.b}if(b>1||d>1||bI>1){b/=255;d/=255;bI/=255}var bH=m(b,d,bI),e=bi(b,d,bI),R,E,S=bH;if(e==bH){return{h:0,s:0,b:bH,toString:aC}}else{var bJ=(bH-e);E=bJ/bH;if(b==bH){R=(d-bI)/bJ}else{if(d==bH){R=2+((bI-b)/bJ)}else{R=4+((b-d)/bJ)}}R/=6;R<0&&R++;R>1&&R--}return{h:R,s:E,b:S,toString:aC}};aI.rgb2hsl=function(d,e,bH){if(e==null&&aI.is(d,"object")&&"r" in d&&"g" in d&&"b" in d){bH=d.b;e=d.g;d=d.r}if(e==null&&aI.is(d,af)){var bL=aI.getRGB(d);d=bL.r;e=bL.g;bH=bL.b}if(d>1||e>1||bH>1){d/=255;e/=255;bH/=255}var S=m(d,e,bH),E=bi(d,e,bH),R,bK,b=(S+E)/2,bJ;if(E==S){bJ={h:0,s:0,l:b}}else{var bI=S-E;bK=b<0.5?bI/(S+E):bI/(2-S-E);if(d==S){R=(e-bH)/bI}else{if(e==S){R=2+(bH-d)/bI}else{R=4+(d-e)/bI}}R/=6;R<0&&R++;R>1&&R--;bJ={h:R,s:bK,l:b}}bJ.toString=M;return bJ};aI._path2string=function(){return this.join(",")[bs](ba,"$1")};function aA(E,d,b){function e(){var R=Array[bE].slice.call(arguments,0),bH=R[aW]("\u25ba"),S=e.cache=e.cache||{},bI=e.count=e.count||[];if(S[ag](bH)){return b?b(S[bH]):S[bH]}bI[s]>=1000&&delete S[bI.shift()];bI[k](bH);S[bH]=E[bB](d,R);return b?b(S[bH]):S[bH]}return e}aI.getRGB=aA(function(b){if(!b||!!((b=bC(b)).indexOf("-")+1)){return{r:-1,g:-1,b:-1,hex:"none",error:1}}if(b=="none"){return{r:-1,g:-1,b:-1,hex:"none"}}!(n[ag](b.toLowerCase().substring(0,2))||b.charAt()=="#")&&(b=a9(b));var R,d,e,bH,E,bJ,bI,S=b.match(G);if(S){if(S[2]){bH=T(S[2].substring(5),16);e=T(S[2].substring(3,5),16);d=T(S[2].substring(1,3),16)}if(S[3]){bH=T((bJ=S[3].charAt(3))+bJ,16);e=T((bJ=S[3].charAt(2))+bJ,16);d=T((bJ=S[3].charAt(1))+bJ,16)}if(S[4]){bI=S[4][I](bd);d=aj(bI[0]);bI[0].slice(-1)=="%"&&(d*=2.55);e=aj(bI[1]);bI[1].slice(-1)=="%"&&(e*=2.55);bH=aj(bI[2]);bI[2].slice(-1)=="%"&&(bH*=2.55);S[1].toLowerCase().slice(0,4)=="rgba"&&(E=aj(bI[3]));bI[3]&&bI[3].slice(-1)=="%"&&(E/=100)}if(S[5]){bI=S[5][I](bd);d=aj(bI[0]);bI[0].slice(-1)=="%"&&(d*=2.55);e=aj(bI[1]);bI[1].slice(-1)=="%"&&(e*=2.55);bH=aj(bI[2]);bI[2].slice(-1)=="%"&&(bH*=2.55);(bI[0].slice(-3)=="deg"||bI[0].slice(-1)=="\xb0")&&(d/=360);S[1].toLowerCase().slice(0,4)=="hsba"&&(E=aj(bI[3]));bI[3]&&bI[3].slice(-1)=="%"&&(E/=100);return aI.hsb2rgb(d,e,bH,E)}if(S[6]){bI=S[6][I](bd);d=aj(bI[0]);bI[0].slice(-1)=="%"&&(d*=2.55);e=aj(bI[1]);bI[1].slice(-1)=="%"&&(e*=2.55);bH=aj(bI[2]);bI[2].slice(-1)=="%"&&(bH*=2.55);(bI[0].slice(-3)=="deg"||bI[0].slice(-1)=="\xb0")&&(d/=360);S[1].toLowerCase().slice(0,4)=="hsla"&&(E=aj(bI[3]));bI[3]&&bI[3].slice(-1)=="%"&&(E/=100);return aI.hsl2rgb(d,e,bH,E)}S={r:d,g:e,b:bH};S.hex="#"+(16777216|bH|(e<<8)|(d<<16)).toString(16).slice(1);aI.is(E,"finite")&&(S.opacity=E);return S}return{r:-1,g:-1,b:-1,hex:"none",error:1}},aI);aI.getColor=function(d){var e=this.getColor.start=this.getColor.start||{h:0,s:1,b:d||0.75},b=this.hsb2rgb(e.h,e.s,e.b);e.h+=0.075;if(e.h>1){e.h=0;e.s-=0.2;e.s<=0&&(this.getColor.start={h:0,s:1,b:e.b})}return b.hex};aI.getColor.reset=function(){delete this.start};aI.parsePathString=aA(function(b){if(!b){return null}var e={a:7,c:6,h:1,l:2,m:2,q:4,s:4,t:2,v:1,z:0},d=[];if(aI.is(b,a7)&&aI.is(b[0],a7)){d=aS(b)}if(!d[s]){bC(b)[bs](aR,function(R,E,bI){var bH=[],S=bG.call(E);bI[bs](aG,function(bK,bJ){bJ&&bH[k](+bJ)});if(S=="m"&&bH[s]>2){d[k]([E][bw](bH.splice(0,2)));S="l";E=E=="m"?"l":"L"}while(bH[s]>=e[S]){d[k]([E][bw](bH.splice(0,e[S])));if(!e[S]){break}}})}d[a0]=aI._path2string;return d});aI.findDotsAtSegment=function(d,b,bV,bT,bH,R,bJ,bI,bP){var bN=1-bP,bM=bl(bN,3)*d+bl(bN,2)*3*bP*bV+bN*3*bP*bP*bH+bl(bP,3)*bJ,bK=bl(bN,3)*b+bl(bN,2)*3*bP*bT+bN*3*bP*bP*R+bl(bP,3)*bI,bR=d+2*bP*(bV-d)+bP*bP*(bH-2*bV+d),bQ=b+2*bP*(bT-b)+bP*bP*(R-2*bT+b),bU=bV+2*bP*(bH-bV)+bP*bP*(bJ-2*bH+bV),bS=bT+2*bP*(R-bT)+bP*bP*(bI-2*R+bT),bO=(1-bP)*d+bP*bV,bL=(1-bP)*b+bP*bT,E=(1-bP)*bH+bP*bJ,e=(1-bP)*R+bP*bI,S=(90-ao.atan((bR-bU)/(bQ-bS))*180/aM);(bR>bU||bQ1){cd=ao.sqrt(cd);bX=cd*bX;bV=cd*bV}var e=bX*bX,b6=bV*bV,b8=(bJ==E?-1:1)*ao.sqrt(aq((e*b6-e*b2*b2-b6*b3*b3)/(e*b2*b2+b6*b3*b3))),bS=b8*bX*b2/bV+(bO+bN)/2,bR=b8*-bV*b3/bX+(cj+ci)/2,bI=ao.asin(((cj-bR)/bV).toFixed(9)),bH=ao.asin(((ci-bR)/bV).toFixed(9));bI=bObH){bI=bI-aM*2}if(!E&&bH>bI){bH=bH-aM*2}}else{bI=bQ[0];bH=bQ[1];bS=bQ[2];bR=bQ[3]}var bM=bH-bI;if(aq(bM)>bU){var bT=bH,bW=bN,bK=ci;bH=bI+bU*(E&&bH>bI?1:-1);bN=bS+bX*ao.cos(bH);ci=bR+bV*ao.sin(bH);b1=Z(bN,ci,bX,bV,bP,0,E,bW,bK,[bH,bT,bS,bR])}bM=bH-bI;var S=ao.cos(bI),ch=ao.sin(bI),R=ao.cos(bH),cg=ao.sin(bH),b4=ao.tan(bM/4),b7=4/3*bX*b4,b5=4/3*bV*b4,ce=[bO,cj],cc=[bO+b7*ch,cj-b5*S],cb=[bN+b7*cg,ci-b5*R],b9=[bN,ci];cc[0]=2*ce[0]-cc[0];cc[1]=2*ce[1]-cc[1];if(bQ){return[cc,cb,b9][bw](b1)}else{b1=[cc,cb,b9][bw](b1)[aW]()[I](",");var bZ=[];for(var ca=0,b0=b1[s];ca"1e12"&&(bI=0.5);aq(bH)>"1e12"&&(bH=0.5);if(bI>0&&bI<1){e=ac(E,d,S,R,bR,bQ,bN,bK,bI);bO[k](e.x);bL[k](e.y)}if(bH>0&&bH<1){e=ac(E,d,S,R,bR,bQ,bN,bK,bH);bO[k](e.x);bL[k](e.y)}bP=(bQ-2*R+d)-(bK-2*bQ+R);bM=2*(R-d)-2*(bQ-R);bJ=d-R;bI=(-bM+ao.sqrt(bM*bM-4*bP*bJ))/2/bP;bH=(-bM-ao.sqrt(bM*bM-4*bP*bJ))/2/bP;aq(bI)>"1e12"&&(bI=0.5);aq(bH)>"1e12"&&(bH=0.5);if(bI>0&&bI<1){e=ac(E,d,S,R,bR,bQ,bN,bK,bI);bO[k](e.x);bL[k](e.y)}if(bH>0&&bH<1){e=ac(E,d,S,R,bR,bQ,bN,bK,bH);bO[k](e.x);bL[k](e.y)}return{min:{x:bi[bB](0,bO),y:bi[bB](0,bL)},max:{x:m[bB](0,bO),y:m[bB](0,bL)}}}),V=aA(function(bP,bK){var E=y(bP),bL=bK&&y(bK),bM={x:0,y:0,bx:0,by:0,X:0,Y:0,qx:null,qy:null},b={x:0,y:0,bx:0,by:0,X:0,Y:0,qx:null,qy:null},S=function(bR,bS){var bQ,bT;if(!bR){return["C",bS.x,bS.y,bS.x,bS.y,bS.x,bS.y]}!(bR[0] in {T:1,Q:1})&&(bS.qx=bS.qy=null);switch(bR[0]){case"M":bS.X=bR[1];bS.Y=bR[2];break;case"A":bR=["C"][bw](Z[bB](0,[bS.x,bS.y][bw](bR.slice(1))));break;case"S":bQ=bS.x+(bS.x-(bS.bx||bS.x));bT=bS.y+(bS.y-(bS.by||bS.y));bR=["C",bQ,bT][bw](bR.slice(1));break;case"T":bS.qx=bS.x+(bS.x-(bS.qx||bS.x));bS.qy=bS.y+(bS.y-(bS.qy||bS.y));bR=["C"][bw](bj(bS.x,bS.y,bS.qx,bS.qy,bR[1],bR[2]));break;case"Q":bS.qx=bR[1];bS.qy=bR[2];bR=["C"][bw](bj(bS.x,bS.y,bR[1],bR[2],bR[3],bR[4]));break;case"L":bR=["C"][bw](bD(bS.x,bS.y,bR[1],bR[2]));break;case"H":bR=["C"][bw](bD(bS.x,bS.y,bR[1],bS.y));break;case"V":bR=["C"][bw](bD(bS.x,bS.y,bS.x,bR[1]));break;case"Z":bR=["C"][bw](bD(bS.x,bS.y,bS.X,bS.Y));break}return bR},d=function(bQ,bR){if(bQ[bR][s]>7){bQ[bR].shift();var bS=bQ[bR];while(bS[s]){bQ.splice(bR++,0,["C"][bw](bS.splice(0,6)))}bQ.splice(bR,1);bN=m(E[s],bL&&bL[s]||0)}},e=function(bU,bT,bR,bQ,bS){if(bU&&bT&&bU[bS][0]=="M"&&bT[bS][0]!="M"){bT.splice(bS,0,["M",bQ.x,bQ.y]);bR.bx=0;bR.by=0;bR.x=bU[bS][1];bR.y=bU[bS][2];bN=m(E[s],bL&&bL[s]||0)}};for(var bI=0,bN=m(E[s],bL&&bL[s]||0);bI0.5)*2-1);bl(bI-0.5,2)+bl(S-0.5,2)>0.25&&(S=ao.sqrt(0.25-bl(bI-0.5,2))*bS+0.5)&&S!=0.5&&(S=S.toFixed(5)-0.00001*bS)}return aP});bO=bO[I](/\s*\-\s*/);if(bL=="linear"){var bH=bO.shift();bH=-aj(bH);if(isNaN(bH)){return null}var R=[0,0,ao.cos(bH*aM/180),ao.sin(bH*aM/180)],bN=1/(m(aq(R[2]),aq(R[3]))||1);R[2]*=bN;R[3]*=bN;if(R[2]<0){R[0]=-R[2];R[2]=0}if(R[3]<0){R[1]=-R[3];R[3]=0}}var bK=w(bO);if(!bK){return null}var d=E.getAttribute(a4);d=d.match(/^url\(#(.*)\)$/);d&&b.defs.removeChild(aa.getElementById(d[1]));var e=bh(bL+"Gradient");e.id=j();bh(e,bL=="radial"?{fx:bI,fy:S}:{x1:R[0],y1:R[1],x2:R[2],y2:R[3]});b.defs[bk](e);for(var bJ=0,bP=bK[s];bJ1?E.opacity/100:E.opacity});case"stroke":E=aI.getRGB(bQ);bT[D](bS,E.hex);bS=="stroke"&&E[ag]("opacity")&&bh(bT,{"stroke-opacity":E.opacity>1?E.opacity/100:E.opacity});break;case"gradient":(({circle:1,ellipse:1})[ag](bO.type)||bC(bQ).charAt()!="r")&&g(bT,bQ,bO.paper);break;case"opacity":if(bP.gradient&&!bP[ag]("stroke-opacity")){bh(bT,{"stroke-opacity":bQ>1?bQ/100:bQ})}case"fill-opacity":if(bP.gradient){var b=aa.getElementById(bT.getAttribute(a4)[bs](/^url\(#|\)$/g,aP));if(b){var bJ=b.getElementsByTagName("stop");bJ[bJ[s]-1][D]("stop-opacity",bQ)}break}default:bS=="font-size"&&(bQ=T(bQ,10)+"px");var bM=bS[bs](/(\-.)/g,function(bZ){return bo.call(bZ.substring(1))});bT.style[bM]=bQ;bT[D](bS,bQ);break}}}P(bO,bX);if(bK){bO.rotate(bK.join(aH))}else{aj(bL)&&bO.rotate(bL,true)}};var o=1.2,P=function(b,E){if(b.type!="text"||!(E[ag]("text")||E[ag]("font")||E[ag]("font-size")||E[ag]("x")||E[ag]("y"))){return}var bJ=b.attrs,d=b.node,bL=d.firstChild?T(aa.defaultView.getComputedStyle(d.firstChild,aP).getPropertyValue("font-size"),10):10;if(E[ag]("text")){bJ.text=E.text;while(d.firstChild){d.removeChild(d.firstChild)}var e=bC(E.text)[I]("\n");for(var R=0,bK=e[s];RbH.height)&&(bH.height=S.y+S.height-bH.y);(S.x+S.width-bH.x>bH.width)&&(bH.width=S.x+S.width-bH.x)}}d&&this.hide();return bH};aU[bE].attr=function(b,bJ){if(this.removed){return this}if(b==null){var bI={};for(var R in this.attrs){if(this.attrs[ag](R)){bI[R]=this.attrs[R]}}this._.rt.deg&&(bI.rotation=this.rotate());(this._.sx!=1||this._.sy!=1)&&(bI.scale=this.scale());bI.gradient&&bI.fill=="none"&&(bI.fill=bI.gradient)&&delete bI.gradient;return bI}if(bJ==null&&aI.is(b,af)){if(b=="translation"){return A.call(this)}if(b=="rotation"){return this.rotate()}if(b=="scale"){return this.scale()}if(b==a4&&this.attrs.fill=="none"&&this.attrs.gradient){return this.attrs.gradient}return this.attrs[b]}if(bJ==null&&aI.is(b,a7)){var bL={};for(var E=0,S=b.length;E"));bW.W=bS.w=bW.paper.span.offsetWidth;bW.H=bS.h=bW.paper.span.offsetHeight;bW.X=bS.x;bW.Y=bS.y+ad(bW.H/2);switch(bS["text-anchor"]){case"start":bW.node.style["v-text-align"]="left";bW.bbx=ad(bW.W/2);break;case"end":bW.node.style["v-text-align"]="right";bW.bbx=-ad(bW.W/2);break;default:bW.node.style["v-text-align"]="center";break}}};g=function(b,bH){b.attrs=b.attrs||{};var bI=b.attrs,bK,R="linear",S=".5 .5";b.attrs.gradient=bH;bH=bC(bH)[bs](aO,function(bN,bO,bM){R="radial";if(bO&&bM){bO=aj(bO);bM=aj(bM);bl(bO-0.5,2)+bl(bM-0.5,2)>0.25&&(bM=ao.sqrt(0.25-bl(bO-0.5,2))*((bM>0.5)*2-1)+0.5);S=bO+aH+bM}return aP});bH=bH[I](/\s*\-\s*/);if(R=="linear"){var d=bH.shift();d=-aj(d);if(isNaN(d)){return null}}var E=w(bH);if(!E){return null}b=b.shape||b.node;bK=b.getElementsByTagName(a4)[0]||ay(a4);!bK.parentNode&&b.appendChild(bK);if(E[s]){bK.on=true;bK.method="none";bK.color=E[0].color;bK.color2=E[E[s]-1].color;var bL=[];for(var e=0,bJ=E[s];e')}}catch(aw){ay=function(b){return aa.createElement("<"+b+' xmlns="urn:schemas-microsoft.com:vml" class="rvml">')}}F=function(){var e=aJ[bB](0,arguments),b=e.container,bJ=e.height,bK,d=e.width,bI=e.x,bH=e.y;if(!b){throw new Error("VML container not found.")}var R=new bz,S=R.canvas=aa.createElement("div"),E=S.style;bI=bI||0;bH=bH||0;d=d||512;bJ=bJ||342;d==+d&&(d+="px");bJ==+bJ&&(bJ+="px");R.width=1000;R.height=1000;R.coordsize=u*1000+aH+u*1000;R.coordorigin="0 0";R.span=aa.createElement("span");R.span.style.cssText="position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;display:inline;";S[bk](R.span);E.cssText=aI.format("top:0;left:0;width:{0};height:{1};display:inline-block;position:relative;clip:rect(0 {0} {1} 0);overflow:hidden",d,bJ);if(b==1){aa.body[bk](S);E.left=bI+"px";E.top=bH+"px";E.position="absolute"}else{if(b.firstChild){b.insertBefore(S,b.firstChild)}else{b[bk](S)}}be.call(R,R,aI.fn);return R};aZ.clear=function(){this.canvas.innerHTML=aP;this.span=aa.createElement("span");this.span.style.cssText="position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;display:inline;";this.canvas[bk](this.span);this.bottom=this.top=null};aZ.remove=function(){this.canvas.parentNode.removeChild(this.canvas);for(var b in this){this[b]=z(b)}return true}}var U=navigator.userAgent.match(/Version\/(.*?)\s/);if((navigator.vendor=="Apple Computer, Inc.")&&(U&&U[1]<4||navigator.platform.slice(0,2)=="iP")){aZ.safari=function(){var b=this.rect(-99,-99,this.width+99,this.height+99).attr({stroke:"none"});aQ.setTimeout(function(){b.remove()})}}else{aZ.safari=function(){}}var O=function(){this.returnValue=false},bv=function(){return this.originalEvent.preventDefault()},a3=function(){this.cancelBubble=true},aD=function(){return this.originalEvent.stopPropagation()},av=(function(){if(aa.addEventListener){return function(S,E,e,d){var b=W&&bq[E]?bq[E]:E;var R=function(bK){if(W&&bq[ag](E)){for(var bI=0,bJ=bK.targetTouches&&bK.targetTouches.length;bI1&&(b=Array[bE].splice.call(arguments,0,arguments[s]));return new ah(b)};aZ.setSize=bA;aZ.top=aZ.bottom=null;aZ.raphael=aI;function C(){return this.x+aH+this.y}bc.resetScale=function(){if(this.removed){return this}this._.sx=1;this._.sy=1;this.attrs.scale="1 1"};bc.scale=function(bZ,bY,bQ,bO){if(this.removed){return this}if(bZ==null&&bY==null){return{x:this._.sx,y:this._.sy,toString:C}}bY=bY||bZ;!+bY&&(bY=bZ);var d,b,b7,b6,ca=this.attrs;if(bZ!=0){var cb=this.getBBox(),E=cb.x+cb.width/2,e=cb.y+cb.height/2,b4=aq(bZ/this._.sx),b3=aq(bY/this._.sy);bQ=(+bQ||bQ==0)?bQ:E;bO=(+bO||bO==0)?bO:e;var bJ=this._.sx>0,bI=this._.sy>0,bP=~~(bZ/aq(bZ)),bN=~~(bY/aq(bY)),S=b4*bP,R=b3*bN,b0=this.node.style,bX=bQ+aq(E-bQ)*S*(E>bQ==bJ?1:-1),bW=bO+aq(e-bO)*R*(e>bO==bI?1:-1),bT=(bZ*bP>bY*bN?b3:b4);switch(this.type){case"rect":case"image":var bL=ca.width*b4,bU=ca.height*b3;this.attr({height:bU,r:ca.r*bT,width:bL,x:bX-bL/2,y:bW-bU/2});break;case"circle":case"ellipse":this.attr({rx:ca.rx*b4,ry:ca.ry*b3,r:ca.r*bT,cx:bX,cy:bW});break;case"text":this.attr({x:bX,y:bW});break;case"path":var b2=au(ca.path),bK=true,bS=bJ?S:b4,bR=bI?R:b3;for(var b9=0,bV=b2[s];b9bK){e=b.data[bK*bL]}else{e=aI.findDotsAtSegment(R,d,bJ,bI,bR,bQ,bP,bN,bK/bL);b.data[bK]=e}bK&&(bM+=bl(bl(bH.x-e.x,2)+bl(bH.y-e.y,2),0.5));if(S!=null&&bM>=S){return e}bH=e}if(S==null){return bM}},a1=function(b,d){return function(bP,R,S){bP=V(bP);var bL,bK,e,bH,E="",bO={},bM,bJ=0;for(var bI=0,bN=bP.length;bIR){if(d&&!bO.start){bM=l(bL,bK,e[1],e[2],e[3],e[4],e[5],e[6],R-bJ);E+=["C",bM.start.x,bM.start.y,bM.m.x,bM.m.y,bM.x,bM.y];if(S){return E}bO.start=E;E=["M",bM.x,bM.y+"C",bM.n.x,bM.n.y,bM.end.x,bM.end.y,e[5],e[6]][aW]();bJ+=bH;bL=+e[5];bK=+e[6];continue}if(!b&&!d){bM=l(bL,bK,e[1],e[2],e[3],e[4],e[5],e[6],R-bJ);return{x:bM.x,y:bM.y,alpha:bM.alpha}}}bJ+=bH;bL=+e[5];bK=+e[6]}E+=e}bO.end=E;bM=b?bJ:d?bO:aI.findDotsAtSegment(bL,bK,e[1],e[2],e[3],e[4],e[5],e[6],1);bM.alpha&&(bM={x:bM.x,y:bM.y,alpha:bM.alpha});return bM}};var aK=a1(1),L=a1(),Y=a1(0,1);bc.getTotalLength=function(){if(this.type!="path"){return}if(this.node.getTotalLength){return this.node.getTotalLength()}return aK(this.attrs.path)};bc.getPointAtLength=function(b){if(this.type!="path"){return}return L(this.attrs.path,b)};bc.getSubpath=function(e,d){if(this.type!="path"){return}if(aq(this.getTotalLength()-d)<"1e-6"){return Y(this.attrs.path,e).end}var b=Y(this.attrs.path,d,1);return e?Y(b,e).end:b};aI.easing_formulas={linear:function(b){return b},"<":function(b){return bl(b,3)},">":function(b){return bl(b-1,3)+1},"<>":function(b){b=b*2;if(b<1){return bl(b,3)/2}b-=2;return(bl(b,3)+2)/2},backIn:function(d){var b=1.70158;return d*d*((b+1)*d-b)},backOut:function(d){d=d-1;var b=1.70158;return d*d*((b+1)*d+b)+1},elastic:function(e){if(e==0||e==1){return e}var d=0.3,b=d/4;return bl(2,-10*e)*ao.sin((e-b)*(2*aM)/d)+1},bounce:function(E){var d=7.5625,e=2.75,b;if(E<(1/e)){b=d*E*E}else{if(E<(2/e)){E-=(1.5/e);b=d*E*E+0.75}else{if(E<(2.5/e)){E-=(2.25/e);b=d*E*E+0.9375}else{E-=(2.625/e);b=d*E*E+0.984375}}}return b}};var X=[],bu=function(){var bI=+new Date;for(var bT=0;bTbV){return bV}while(bWbR){bW=bT}else{bV=bT}bT=(bV-bW)/2+bW}return bT}return e(bO,1/(200*bI))}bc.onAnimation=function(b){this._run=b||0;return this};bc.animate=function(b0,bQ,bP,R){var d=this;d.timeouts=d.timeouts||[];if(aI.is(bP,"function")||!bP){R=bP||null}if(d.removed){R&&R.call(d);return d}var bU={},e={},S=false,bL={};for(var bR in b0){if(b0[ag](bR)){if(am[ag](bR)||d.paper.customAttributes[ag](bR)){S=true;bU[bR]=d.attr(bR);(bU[bR]==null)&&(bU[bR]=q[bR]);e[bR]=b0[bR];switch(am[bR]){case"along":var bY=aK(b0[bR]);var bS=L(b0[bR],bY*!!b0.back);var bH=d.getBBox();bL[bR]=bY/bQ;bL.tx=bH.x;bL.ty=bH.y;bL.sx=bS.x;bL.sy=bS.y;e.rot=b0.rot;e.back=b0.back;e.len=bY;b0.rot&&(bL.r=aj(d.rotate())||0);break;case aF:bL[bR]=(e[bR]-bU[bR])/bQ;break;case"colour":bU[bR]=aI.getRGB(bU[bR]);var bT=aI.getRGB(e[bR]);bL[bR]={r:(bT.r-bU[bR].r)/bQ,g:(bT.g-bU[bR].g)/bQ,b:(bT.b-bU[bR].b)/bQ};break;case"path":var bI=V(bU[bR],e[bR]);bU[bR]=bI[0];var bN=bI[1];bL[bR]=[];for(var bX=0,bK=bU[bR][s];bXl){l=m}}for(m in q){r=a.convert(o,l,0,1,m);p[r]=q[m]}return function(v,u){var s=null,w=null;if(v-c<0){for(var t in p){return p[t]}}else{if(v+c>1){for(var t in p){s=p[t]}return s}else{for(var t in p){if(t==v){return v}if(v>t){s=t;continue}else{w=a.convert(s,t,0,1,v);return s}}return s}}}};a.blockColorScheme=function(m){var l=a.COLOR_BLOCKS[m];return function(n){var o=parseInt(Math.floor(a.scale(0,1,0,l.length+1,n)));return l[Math.max(0,Math.min(o,l.length))]}};a.applySvgStyle=function(n,l,m){var p=null,o=a.extend(l,m||{});if(o.emboss){p=k.rgb2hsb(o.fill);o.fill="90-hsb("+p.h+","+p.s+","+Math.max(0,p.b-0.2)+")-"+o.fill}n.attr(o)};a.GMAP_STYLES={};a.GMAP_STYLES.Neutral=[{featureType:"administrative",elementType:"all",stylers:[{saturation:-100}]},{featureType:"landscape",elementType:"all",stylers:[{saturation:-100}]},{featureType:"poi",elementType:"all",stylers:[{saturation:-100}]},{featureType:"road",elementType:"all",stylers:[{saturation:-100}]},{featureType:"transit",elementType:"all",stylers:[{saturation:-100}]},{featureType:"water",elementType:"all",stylers:[{saturation:-100}]}];a.GMAP_STYLES.Subtle=[{featureType:"all",elementType:"all",stylers:[{saturation:-47}]},{featureType:"poi",elementType:"all",stylers:[{visibility:"off"}]},{featureType:"water",elementType:"all",stylers:[{saturation:-68},{visibility:"on"},{lightness:50}]},{featureType:"road.local",elementType:"all",stylers:[{visibility:"simplified"}]},{featureType:"road.arterial",elementType:"all",stylers:[{visibility:"simplified"}]},{featureType:"road.highway",elementType:"all",stylers:[{visibility:"simplified"}]},{featureType:"administrative.province",elementType:"all",stylers:[{visibility:"simplified"}]},{featureType:"transit",elementType:"all",stylers:[]}];a.setGMapStyle=function(o,m){var l={name:m},n=new google.maps.StyledMapType(a.GMAP_STYLES[m],l);o.mapTypes.set(m,n);o.setMapTypeId(m)};a.Controller=a.Class.extend({init:function(o,l,n,m){this.options=a.extend(e.CONTROLLER,m);this.map=o;this.datasource=l;this.layer=n;this.datasource.addController(this);this.layer.setMap(this.map).setController(this)},refresh:function(){var l=this.layer;a.each(this.datasource.getDataItems(),function(m){l.addDataItem(m)});return this}});a.Realtime=a.Controller.extend({init:function(o,l,n,m){this._super(o,l,n,a.extend(e.REALTIME,m));setInterval(this.deleteExpired,5000)},updateOne:function(l){var m=this,n=this.convertTimeIntoFutureMs(l.timestamp);window.setTimeout(function(){m.layer.addDataItem(l)},n);return this},updateMany:function(l){var m=this;a.each(l,function(n){m.updateOne(n)});return this},convertTimeIntoFutureMs:function(m){var l=new Date().getTime();return Math.max(0,(m+this.options.expected_delay)-l)},deleteExpired:function(){}});a.Datasource=a.Class.extend({init:function(l){this.data=l&&this.formatData(l)||[];this.controllers=[];this.dirty=true;this.stats={};this.analyze()},addController:function(l){this.controllers.push(l)},load:function(){a.each(this.controllers,function(l){l.refresh()})},push:function(l){this.data.push(this.formatDataItem(l));a.each(this.controllers,function(m){m.updateOne(l)});return this},pushMany:function(m){var l=this.formatData(m);i=0;ii=l.length;for(;in)?m.val:n});this.stats.min=this.reduce(function(m,n){return(n===null||m.val