diff --git a/css/app.css b/css/app.css index 80010a180..9d9946040 100644 --- a/css/app.css +++ b/css/app.css @@ -5,11 +5,27 @@ body { text-rendering: optimizeLegibility; color:#444; } + +form { + margin:0; + padding:0; + display:inline-block; +} + :focus { outline-color: transparent; outline-style: none; } +#about { + position:absolute; + bottom:5px; + right:5px; + background:#fff; + padding:2px 5px; +} p { + margin:0; + padding:0; font-size: x-small; } a:visited, a { @@ -25,12 +41,6 @@ input[type=text]:focus { border-color:#222; } -* { - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; -} - text { -webkit-user-select: none; -moz-user-select: none; @@ -44,15 +54,7 @@ table th { text-align:left; } -#map { - height: 600px; - -webkit-touch-callout: none; - -webkit-user-select: none; - -khtml-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} + .help-pane { position:absolute; @@ -77,7 +79,7 @@ table th { } #modebuttons { - width:500px; + width:600px; position:absolute; left:0px; top:0px; @@ -96,9 +98,10 @@ table th { #modebuttons button { width:100px; cursor:pointer; + display:inline-block; background:#fff; color:#555; - font:bold 19px/30px 'Helvetica Neue'; + font:bold 19px/30px 'Helvetica Neue', sans-serif; border:0; border-right:1px solid #222; border-bottom:1px solid #222; @@ -107,6 +110,19 @@ table th { margin:0; } +#modebuttons button.mini, +#modebuttons button.mini { + width:50px; +} + +#modebuttons input[type=text] { + width:150px; + height:30px; + border:0; + padding:5px; + margin:0; +} + #modebuttons button:hover { background:#eee; } @@ -146,10 +162,6 @@ table th { width:135px; } -polyline { - cursor: pointer; -} - .edit-pane { position:absolute; display:none; diff --git a/icons/generic.png b/icons/generic.png new file mode 100644 index 000000000..e53102a50 Binary files /dev/null and b/icons/generic.png differ diff --git a/index.html b/index.html index 16729d1ee..a379776cc 100755 --- a/index.html +++ b/index.html @@ -1,104 +1,141 @@ - - - iD - - - - - - -
- - - - - - - - - - - + + + + + -require(["dojo/dom-geometry","dojo/dom-class","dojo/on","dojo/dom","dojo/Evented", - "iD/actions/UndoStack", - "iD/actions/CreatePOIAction", - "iD/actions/AddNodeToWayAction", - "iD/Controller", - "iD/actions/CreateEntityAction", - "iD/controller/edit/NoSelection", - "iD/controller/shape/NoSelection", - "iD/renderer/Map","iD/styleparser/RuleSet", - "iD/ui/DragAndDrop","iD/ui/StepPane", - "dojo/domReady!"], function(domGeom,domClass,on,dom,Evented){ + - var ruleset = new iD.styleparser.RuleSet(); - var connection = new iD.Connection("http://www.overpass-api.de/api/xapi?"); + + + + + + - // Load styles - ruleset.registerCallback(styleLoaded); - ruleset.loadFromCSS("potlatch.css", styleLoaded); + + - // Initialise map - var map = new iD.renderer.Map({ - lat: 51.87, - lon: -1.49, - zoom: 17, - div: "map", - connection: connection, - width: dom.byId('map').offsetWidth, - height: dom.byId('map').offsetHeight - }); - map.ruleset = ruleset; + + + + + + - // Initialise controller - var controller = new iD.Controller(map); - map.setController(controller); +
+
+
+
- // ---------------------------------------------------- - // Data is loaded and app ready to go - function styleLoaded() { - // Initialise drag-and-drop icons - new iD.ui.DragAndDrop("map", map, "dndgrid"); +
+ +
- // Initialise help pane - controller.setStepper(new iD.ui.StepPane()); + +
+

 

+ × +
+ + + + + + + + + +
KeyValue
+
+
+
+
- // Set initial controllerState - controller.setState(new iD.controller.edit.NoSelection()); +
+
+

Work in progress: introduction, + code, + docs. + Imagery © 2012 Bing, GeoEye, Getmapping, Intermap, Microsoft.

+
+ + - -
- - - -
- -
-
-
- -
- -
- - -
-
Click on the map to start a road
-
Draw the road by clicking on points along its path
-
Choose a road type
-
- - -
-

 

- × -
- - - - - - - - - -
KeyValue
-
-
-
- - - - -
-
-

Work in progress: introduction, code, docs. Imagery © 2012 Bing, GeoEye, Getmapping, Intermap, Microsoft.

- - diff --git a/js/iD/Connection.js b/js/iD/Connection.js index bfd2b95c6..9cb6d1589 100755 --- a/js/iD/Connection.js +++ b/js/iD/Connection.js @@ -1,11 +1,5 @@ -// define(["dojo/_base/xhr","dojo/_base/lang","dojox/xml/DomParser","dojo/_base/array",'dojo/_base/declare', -// "iD/Entity","iD/Node","iD/Way","iD/Relation","iD/actions/CreateEntityAction"], -// function(xhr,lang,DomParser,array,declare,Entity){ - -// ---------------------------------------------------------------------- -// Connection base class - if (typeof iD === 'undefined') iD = {}; + iD.Connection = function(apiURL) { // summary: The data store, including methods to fetch data from (and, eventually, save data to) // an OSM API server. @@ -20,12 +14,16 @@ iD.Connection = function(apiURL) { var connection = {}; + function all() { + return _.values(entities); + } + function assign(obj) { // summary: Save an entity to the data store. - switch (obj.entityType) { - case "node": entities[obj.id] = obj; break; - case "way": entities[obj.id] = obj; break; - case "relation": relations[obj.id] = obj; break; + if (obj.entityType === 'node' || obj.entityType === 'way') { + entities[obj.id] = obj; + } else if (obj.entityType === 'relation') { + relations[obj.id] = obj; } } @@ -64,70 +62,81 @@ iD.Connection = function(apiURL) { return relation; } - function getObjectsByBbox(left,right,top,bottom) { + function getObjectsByBbox(extent) { // summary: Find all drawable entities that are within a given bounding box. // Each one is an array of entities. - var o = { - inside: [], - outside: [] - }; - _.each(this.entities, function(e, id) { - if (e.within(left, right, top, bottom)) { o.inside.push(e); } - else { o.outside.push(e); } + return _.filter(this.entities, function(e, id) { + return e.within(extent); }); - return o; - } - - // ------------ - // POI handling - function updatePOIs(nodelist) { - // summary: Update the list of POIs (nodes not in ways) from a supplied array of nodes. - _.each(nodelist, function(node) { - if (node.entity.hasParentWays()) { - delete pois[node._id]; - } else { - pois[node._id] = node; - } - }); - } - - function getPOIs() { - // summary: Return a list of all the POIs in connection Connection. - return _.values(pois); - } - - function registerPOI(node) { - // summary: Register a node as a POI (not in a way). - pois[node._id] = node; - } - - function unregisterPOI(node) { - // summary: Mark a node as no longer being a POI (it's now in a way). - delete pois[node._id]; } // ---------- // OSM parser - function loadFromAPI(box, callback) { // summary: Request data within the bbox from an external OSM server. Currently hardcoded // to use Overpass API (which has the relevant CORS headers). loadFromURL("http://www.overpass-api.de/api/xapi?map?bbox=" + - [box.west, box.south, box.east, box.north], callback); + [box[0][0], box[1][1], box[1][0], box[0][1]], callback); } function loadFromURL(url, callback) { // summary: Load all data from a given URL. $.ajax({ url: url, - headers: { "X-Requested-With": null }, success: parse(callback) }); } + // Private functions to parse DOM created from XML file + function filterNodeName(n) { + return function(item) { return item.nodeName === n; }; + } + + function getAttribute(obj, name) { + return _.find(obj.attributes, filterNodeName(name)).nodeValue; + } + + function getTags(obj) { + var tags = {}; + // Doesn't use underscore for performance reasons + for (var i = 0; i < obj.childNodes.length; i++) { + var item = obj.childNodes[i]; + if (item.nodeName === 'tag') { + tags[getAttribute(item,'k')] = getAttribute(item,'v'); + } + } + return tags; + } + + function getNodes(obj) { + var nodes = []; + // Doesn't use underscore for performance reasons + for (var i = 0; i < obj.childNodes.length; i++) { + var item = obj.childNodes[i]; + if (item.nodeName === 'nd') { + nodes.push(entities[getAttribute(item,'ref')]); + } + } + return nodes; + } + + function getMembers(obj) { + return _(obj.childNodes).chain() + .filter(filterNodeName('member')) + .map(function(item) { + var id = getAttribute(item,'ref'), + type = getAttribute(item,'type'), + role = getAttribute(item,'role'); + + var obj = getOrCreate(id,type); + return new iD.RelationMember(obj,role); + }).value(); + } + function parse(callback) { return function(dom) { - var nodelist = _.compact(_.map(dom.childNodes[0].childNodes, function(obj) { + for (var i = 0; i < dom.childNodes[0].childNodes.length; i++) { + var obj = dom.childNodes[0].childNodes[i]; if (obj.nodeName === 'node') { var node = new iD.Node(connection, +getAttribute(obj, 'id'), @@ -135,7 +144,6 @@ iD.Connection = function(apiURL) { +getAttribute(obj, 'lon'), getTags(obj)); assign(node); - return node; } else if (obj.nodeName === 'way') { var way = new iD.Way(connection, getAttribute(obj, 'id'), @@ -149,51 +157,13 @@ iD.Connection = function(apiURL) { getTags(obj)); assign(relation); } - })); - updatePOIs(nodelist); - if (callback) { callback(nodelist); } - - // Private functions to parse DOM created from XML file - function filterNodeName(n) { - return function(item) { return item.nodeName === n; }; - } - - function getAttribute(obj, name) { - return _.find(obj.attributes, filterNodeName(name)).nodeValue; - } - - function getTags(obj) { - return _(obj.childNodes).chain() - .filter(filterNodeName('tag')) - .map(function(item) { - return [getAttribute(item,'k'), getAttribute(item,'v')]; - }).object().value(); - } - - function getNodes(obj) { - return _(obj.childNodes).chain() - .filter(filterNodeName('nd')) - .map(function(item) { - return entities[getAttribute(item,'ref')]; - }).value(); - } - - function getMembers(obj) { - return _(obj.childNodes).chain() - .filter(filterNodeName('member')) - .map(function(item) { - var id = getAttribute(item,'ref'), - type = getAttribute(item,'type'), - role = getAttribute(item,'role'); - - var obj = getOrCreate(id,type); - return new iD.RelationMember(obj,role); - }).value(); } + callback(); }; } connection.entities = entities; + connection.all = all; connection.relations = relations; connection.loadFromAPI = loadFromAPI; connection.loadFromURL = loadFromURL; @@ -201,9 +171,6 @@ iD.Connection = function(apiURL) { connection.doCreateNode = doCreateNode; connection.doCreateWay = doCreateWay; connection.doCreateRelation = doCreateRelation; - connection.registerPOI = registerPOI; - connection.unregisterPOI = unregisterPOI; - connection.getPOIs = getPOIs; return connection; }; diff --git a/js/iD/Controller.js b/js/iD/Controller.js index 724ee2534..9d6b3b791 100755 --- a/js/iD/Controller.js +++ b/js/iD/Controller.js @@ -1,54 +1,19 @@ -define(['dojo/_base/declare','dojo/on','dojo/Evented', - 'iD/actions/UndoStack','iD/tags/PresetList'], function(declare,on,Evented){ +if (typeof iD === 'undefined') iD = {}; -// ---------------------------------------------------------------------- -// Controller base class +iD.Controller = function() { + var controller = {}, + state = null; -declare("iD.Controller", [Evented], { - state: null, // current ControllerState - map: null, // current Map - stepper: null, // current StepPane - undoStack: null, // main undoStack - editorCache: null, // cache of tag editor objects, means we don't have to repeatedly load them - - constructor:function(map) { - // summary: The Controller marshalls ControllerStates and passes events to them. - this.map = map; - this.undoStack = new iD.UndoStack(); - this.editorCache = {}; - }, + controller.undoStack = new iD.UndoStack(); - setStepper: function(stepper) { - // summary: Set reference for the singleton-like class for the step-by-step instruction panel. - this.stepper = stepper; - }, + controller.setState = function(newState) { + // summary: Enter a new ControllerState, firing exitState on the old one, and enterState on the new one. + if (newState === state) { return; } + if (state) state.exitState(); + newState.controller = controller; + state = newState; + newState.enterState(); + }; - setState:function(newState) { - // summary: Enter a new ControllerState, firing exitState on the old one, and enterState on the new one. - if (newState === this.state) { return; } - if (this.state) { - this.emit("exitState", { - bubbles: true, - cancelable: true - }); - } - newState.controller = this; - this.state = newState; - newState.enterState(); - this.emit("enterState", { - bubbles: true, - cancelable: true - }); - }, - - entityMouseEvent:function(event,entityUI) { - // summary: Pass a MouseEvent on an EntityUI (e.g. clicking a way) to the current ControllerState. - if (!this.state) { return; } - var newState = this.state.processMouseEvent(event,entityUI); - this.setState(newState); - } -}); - -// ---------------------------------------------------------------------- -// End of module -}); + return controller; +}; diff --git a/js/iD/Node.js b/js/iD/Node.js index c2bb6dbb6..ceae6d4fa 100644 --- a/js/iD/Node.js +++ b/js/iD/Node.js @@ -9,19 +9,15 @@ iD.Node = function(connection, id, lat, lon, tags, loaded) { this.entity = new iD.Entity(); this.lat = lat; this.lon = lon; + // TODO: keep or trash this custom + this[0] = lon; + this[1] = lat; this.tags = tags; this.loaded = (loaded === undefined) ? true : loaded; - this.project(); this.modified = this.id < 0; }; iD.Node.prototype = { - project: function() { - // summary: Update the projected latitude value (this.latp) from the latitude (this.lat). - this.latp = 180/Math.PI * - Math.log(Math.tan(Math.PI/4+this.lat*(Math.PI/180)/2)); - }, - toGeoJSON: function() { return { type: 'Feature', @@ -33,40 +29,10 @@ iD.Node.prototype = { }; }, - latp2lat: function(a) { - // summary: Get a latitude from a projected latitude. - // returns: Latitude. - return 180/Math.PI * (2 * Math.atan(Math.exp(a*Math.PI/180)) - Math.PI/2); - }, - within: function(extent) { - return (this.lon >= extent.west) && - (this.lon <= extent.east) && - (this.lat >= extent.south) && - (this.lat <= extent.north); - }, - - refresh: function() { - var ways = this.parentWays(); - _.each(ways, _.bind(function(way) { this.connection.refreshEntity(way); }, this)); - this.connection.refreshEntity(this); - }, - - doSetLonLatp: function(lon, latproj, performAction) { - // summary: Change the position of a node, using an undo stack. - performAction(new iD.actions.MoveNodeAction(this, - this.latp2lat(latproj), - lon, - _.bind(this._setLatLonImmediate, this))); - }, - - _setLatLonImmediate: function(lat,lon) { - this.lat = lat; - this.lon = lon; - this.project(); - var ways = this.parentWays(); - _.each(ways, _.bind(function(way) { - way.expandBbox(this); - }, this)); + return (this.lon >= extent[0][0]) && + (this.lon <= extent[1][0]) && + (this.lat <= extent[0][1]) && + (this.lat >= extent[1][1]); } }; diff --git a/js/iD/Way.js b/js/iD/Way.js index fecf79f19..2800a791e 100644 --- a/js/iD/Way.js +++ b/js/iD/Way.js @@ -16,7 +16,6 @@ iD.Way = function(connection, id, nodes, tags, loaded) { _.each(nodes, _.bind(function(node) { node.entity.addParent(this); }, this)); - this._calculateBbox(); }; iD.Way.prototype = { @@ -46,9 +45,14 @@ iD.Way.prototype = { }; }, + bounds: function() { + return d3.geo.bounds(this.toGeoJSON()); + }, + // --------------------- // Bounding-box handling within: function(left,right,top,bottom) { + var bounds = this.bounds(); // TODO invert and just return if (!this.extent.west || (this.extent.west < left && this.extent.east < left ) || @@ -61,25 +65,6 @@ iD.Way.prototype = { } }, - _calculateBbox:function() { - this.extent = { - west: Infinity, - east: -Infinity, - south: Infinity, - north: -Infinity - }; - _.each(this.nodes, _.bind(function(n) { this.expandBbox(n); }, this)); - }, - - expandBbox:function(node) { - // summary: Enlarge the way's bounding box to make sure it - // includes the co-ordinates of a supplied node. - this.extent.west = Math.min(this.extent.west, node.lon); - this.extent.east = Math.max(this.extent.east, node.lon); - this.extent.south = Math.min(this.extent.south, node.lat); - this.extent.north = Math.max(this.extent.north, node.lat); - }, - // -------------- // Action callers diff --git a/js/iD/controller/controller.js b/js/iD/controller/controller.js new file mode 100644 index 000000000..4375209c7 --- /dev/null +++ b/js/iD/controller/controller.js @@ -0,0 +1 @@ +iD.controller = {}; diff --git a/js/iD/controller/edit/EditBaseState.js b/js/iD/controller/edit/EditBaseState.js index 60a92e907..0af0e5fd3 100644 --- a/js/iD/controller/edit/EditBaseState.js +++ b/js/iD/controller/edit/EditBaseState.js @@ -1,13 +1,8 @@ -// iD/controller/edit/EditBaseState.js - -define(['dojo/_base/declare','dojo/_base/lang','dojo/_base/array','dojo/on', - 'dijit/registry' - ], function(declare,lang,array,on,registry){ - // ---------------------------------------------------------------------- // EditBaseState class - provides shared UI functions to edit mode states -declare("iD.controller.edit.EditBaseState", null, { +iD.controller.edit.EditBaseState = function() {}; +iD.controller.edit.EditBaseState.prototype = { editortooltip: null, @@ -73,8 +68,4 @@ declare("iD.controller.edit.EditBaseState", null, { // summary: Close the tooltip. $('.edit-pane').removeClass('active').hide(); } -}); - -// ---------------------------------------------------------------------- -// End of module -}); +}; diff --git a/js/iD/controller/edit/NoSelection.js b/js/iD/controller/edit/NoSelection.js index aba04516a..fadd88cb8 100755 --- a/js/iD/controller/edit/NoSelection.js +++ b/js/iD/controller/edit/NoSelection.js @@ -1,23 +1,15 @@ -// iD/controller/edit/NoSelection.js - -define(['dojo/_base/declare', - 'iD/controller/edit/EditBaseState', - 'iD/controller/edit/SelectedWay', - 'iD/controller/edit/SelectedWayNode', - 'iD/controller/edit/SelectedPOINode' - ], function(declare){ - // ---------------------------------------------------------------------- // edit.NoSelection class -declare("iD.controller.edit.NoSelection", [iD.controller.edit.EditBaseState], { +iD.controller.edit.NoSelection = function() {}; +iD.controller.edit.NoSelection.prototype = { constructor: function() { // summary: In 'Edit object' mode but nothing selected. }, enterState: function() { - this.controller.stepper.hide(); + // this.controller.stepper.hide(); }, processMouseEvent: function(event, entityUI) { @@ -38,8 +30,4 @@ declare("iD.controller.edit.NoSelection", [iD.controller.edit.EditBaseState], { } return this; } -}); - -// ---------------------------------------------------------------------- -// End of module -}); +}; diff --git a/js/iD/controller/edit/SelectedPOINode.js b/js/iD/controller/edit/SelectedPOINode.js index 5bced9127..2330f9e2d 100755 --- a/js/iD/controller/edit/SelectedPOINode.js +++ b/js/iD/controller/edit/SelectedPOINode.js @@ -1,50 +1,43 @@ -// iD/controller/edit/SelectedPOINode.js +// ---------------------------------------------------------------------- +// edit.SelectedPOINode class -define(['dojo/_base/declare','iD/controller/edit/EditBaseState'], function(declare){ +iD.controller.edit.SelectedPOINode = function() {}; +iD.controller.edit.SelectedPOINode.prototype = { - // ---------------------------------------------------------------------- - // edit.SelectedPOINode class + node: null, + nodeUI: null, - declare("iD.controller.edit.SelectedPOINode", [iD.controller.edit.EditBaseState], { + constructor: function(node) { + // summary: In 'Edit object' mode and a POI node is selected. + this.node = node; + }, - node: null, - nodeUI: null, + enterState: function() { + var map = this.controller.map; + this.nodeUI = map.getUI(this.node); + this.nodeUI.setStateClass('selected') + .redraw(); + this.openEditorTooltip(this.node); + return this; + }, - constructor: function(node) { - // summary: In 'Edit object' mode and a POI node is selected. - this.node = node; - }, + exitState: function() { + this.nodeUI.resetStateClass('selected') + .redraw(); + this.closeEditorTooltip(); + return this; + }, - enterState: function() { - var map = this.controller.map; - this.nodeUI = map.getUI(this.node); - this.nodeUI.setStateClass('selected') - .redraw(); - this.openEditorTooltip(this.node); - return this; - }, - - exitState: function() { - this.nodeUI.resetStateClass('selected') - .redraw(); - this.closeEditorTooltip(); - return this; - }, - - processMouseEvent: function(event, entityUI) { - if (event.type !== 'click') return this; - var entity = entityUI ? entityUI.entity : null; - var entityType = entity ? entity.entityType : null; - switch (entityType) { - case null: return new iD.controller.edit.NoSelection(); - case 'node': return new iD.controller.edit.SelectedPOINode(entityUI.entity); - case 'way': return new iD.controller.edit.SelectedWay(entityUI.entity, event); - } - return this; + processMouseEvent: function(event, entityUI) { + if (event.type !== 'click') return this; + var entity = entityUI ? entityUI.entity : null; + var entityType = entity ? entity.entityType : null; + switch (entityType) { + case null: return new iD.controller.edit.NoSelection(); + case 'node': return new iD.controller.edit.SelectedPOINode(entityUI.entity); + case 'way': return new iD.controller.edit.SelectedWay(entityUI.entity, event); } + return this; + } - }); - - // ---------------------------------------------------------------------- - // End of module -}); +}; diff --git a/js/iD/controller/edit/SelectedWay.js b/js/iD/controller/edit/SelectedWay.js index 62174bc7d..884da4eb5 100755 --- a/js/iD/controller/edit/SelectedWay.js +++ b/js/iD/controller/edit/SelectedWay.js @@ -1,65 +1,57 @@ -// iD/controller/edit/SelectedWay.js - -define(['dojo/_base/declare','iD/controller/edit/EditBaseState'], function(declare){ - // ---------------------------------------------------------------------- // edit.SelectedWay class -declare("iD.controller.edit.SelectedWay", [iD.controller.edit.EditBaseState], { +iD.controller.edit.SelectedWay = function() {}; +iD.controller.edit.SelectedWay.prototype = { - way: null, - wayUI: null, - entryevent: null, + way: null, + wayUI: null, + entryevent: null, - constructor:function(way, event) { - // summary: In 'Edit object' mode and a way is selected. - this.way = way; - this.entryevent = event; - }, - enterState:function() { - this.wayUI = this.controller.map.getUI(this.way); - this.wayUI.setStateClass('selected'); - this.wayUI.setStateClass('shownodes'); - if (this.entryevent) { + constructor:function(way, event) { + // summary: In 'Edit object' mode and a way is selected. + this.way = way; + this.entryevent = event; + }, + enterState:function() { + this.wayUI = this.controller.map.getUI(this.way); + this.wayUI.setStateClass('selected'); + this.wayUI.setStateClass('shownodes'); + if (this.entryevent) { this.openEditorTooltip(this.entryevent.clientX, this.entryevent.clientY, this.way); } - this.wayUI.redraw(); - }, - exitState:function() { - this.wayUI.resetStateClass('selected'); - this.wayUI.resetStateClass('shownodes'); - this.wayUI.redraw(); - this.closeEditorTooltip(); - }, - - processMouseEvent:function(event, entityUI) { - var entity = entityUI ? entityUI.entity : null; - var entityType = entity ? entity.entityType : null; + this.wayUI.redraw(); + }, + exitState:function() { + this.wayUI.resetStateClass('selected'); + this.wayUI.resetStateClass('shownodes'); + this.wayUI.redraw(); + this.closeEditorTooltip(); + }, - if (event.type === 'click') { - switch (entityType) { - case null: - return new iD.controller.edit.NoSelection(); - case 'node': - var ways = entity.entity.parentWays(); - if (entity.entity.hasParent(this.way)) { - return new iD.controller.edit.SelectedWayNode(entity, this.way); - } else if (!ways.length) { - return new iD.controller.edit.SelectedPOINode(entity); - } else { - return new iD.controller.edit.SelectedWayNode(entity, ways[0]); - } - break; - case 'way': - return new iD.controller.edit.SelectedWay(entityUI.entity, event); - } - } else { - } - return this; - } - -}); + processMouseEvent:function(event, entityUI) { + var entity = entityUI ? entityUI.entity : null; + var entityType = entity ? entity.entityType : null; -// ---------------------------------------------------------------------- -// End of module -}); + if (event.type === 'click') { + switch (entityType) { + case null: + return new iD.controller.edit.NoSelection(); + case 'node': + var ways = entity.entity.parentWays(); + if (entity.entity.hasParent(this.way)) { + return new iD.controller.edit.SelectedWayNode(entity, this.way); + } else if (!ways.length) { + return new iD.controller.edit.SelectedPOINode(entity); + } else { + return new iD.controller.edit.SelectedWayNode(entity, ways[0]); + } + break; + case 'way': + return new iD.controller.edit.SelectedWay(entityUI.entity, event); + } + } else { } + return this; + } + +}; diff --git a/js/iD/controller/edit/SelectedWayNode.js b/js/iD/controller/edit/SelectedWayNode.js index 77d3107fb..efe347151 100755 --- a/js/iD/controller/edit/SelectedWayNode.js +++ b/js/iD/controller/edit/SelectedWayNode.js @@ -1,11 +1,7 @@ -// iD/controller/edit/SelectedWayNode.js - -define(['dojo/_base/declare','iD/controller/edit/EditBaseState'], function(declare){ - // ---------------------------------------------------------------------- // SelectedWayNode class - -declare("iD.controller.edit.SelectedWayNode", [iD.controller.edit.EditBaseState], { +iD.controller.edit.SelectedWayNode = function() {}; +iD.controller.edit.SelectedWayNode.prototype = { node: null, way: null, @@ -53,8 +49,4 @@ declare("iD.controller.edit.SelectedWayNode", [iD.controller.edit.EditBaseState] return this; } -}); - -// ---------------------------------------------------------------------- -// End of module -}); +}; diff --git a/js/iD/controller/edit/edit.js b/js/iD/controller/edit/edit.js new file mode 100644 index 000000000..a6e693128 --- /dev/null +++ b/js/iD/controller/edit/edit.js @@ -0,0 +1 @@ +iD.controller.edit = {}; diff --git a/js/iD/renderer/EntityUI.js b/js/iD/renderer/EntityUI.js deleted file mode 100755 index b6541bddc..000000000 --- a/js/iD/renderer/EntityUI.js +++ /dev/null @@ -1,134 +0,0 @@ -// iD/renderer/EntityUI.js -// EntityUI classes for iD -// **** TODO: -// multipolygon support - http://mail.dojotoolkit.org/pipermail/dojo-interest/2011-January/052042.html -// support 'interactive' -// line decoration, dots etc. -// fill images -// opacity - -define(['dojo/_base/declare','iD/Entity','iD/renderer/Map'], -function(declare) { - - // ---------------------------------------------------------------------- - // EntityUI base class - - declare("iD.renderer.EntityUI", null, { - - entity:null, // Entity this represents - map:null, // the Map object containing this - layer:0, // OSM layer - sprites:null, // array of sprites created for this EntityUI - styleList:null, // current StyleList - stateClasses:null, // list of stateClass tags to apply - - constructor: function(entity,map,stateClasses) { - // summary: Base class for a UI representing an entity. - this.entity=entity; - this.map=map; - this.stateClasses=stateClasses ? stateClasses.slice() : []; - this.sprites=[]; - }, - getConnection: function() { - // summary: Get the Connection from where the map draws its data. - return this.map.connection; // iD.Connection - }, - targetGroup: function(groupType,sublayer) { - // summary: Find a gfx.Group to render on. - return this.map.sublayer(this.layer,groupType,sublayer); // dojox.gfx.Group - }, - recordSprite: function(sprite) { - // summary: Record that an individual sprite (one stroke, icon or text item) has been added. - if (!_.include(this.sprites, sprite)) { - this.sprites.push(sprite); - } - return sprite; - }, - removeSprites: function() { - // summary: Clear all sprites currently used. - for (var i=0; i-1) { - this.stateClasses.splice(this.stateClasses.indexOf(sc),1); - this.invalidateStyleList(); - } - return this; - }, - - hasStateClass:function(sc) { - // summary: Is a particular state class set for this UI? - return this.stateClasses.indexOf(sc) > -1; - }, - - invalidateStyleList:function() { - // summary: Invalidate the StyleList so it's recalculated on next redraw. - this.styleList = null; - }, - - // -------------------- - // Mouse event handling - - entityMouseEvent:function(event) { - // summary: Receive a mouse event (e.g. clicking on the UI), and forward it to the Controller. - this.map.controller.entityMouseEvent(event, event.gfxTarget.source); - event.stopPropagation(); - } - }); - - // ---------------------------------------------------------------------- - // End of module -}); diff --git a/js/iD/renderer/Map.js b/js/iD/renderer/Map.js index 875c6f8b3..62344a131 100755 --- a/js/iD/renderer/Map.js +++ b/js/iD/renderer/Map.js @@ -1,514 +1,389 @@ // iD/renderer/Map.js // at present this combines P2's Map and MapPaint functionality -define(['dojo/_base/declare','dojo/_base/event', - 'dojo/dom-geometry', - 'dojox/gfx','dojox/gfx/matrix', - 'iD/Connection','iD/Entity','iD/renderer/EntityUI','iD/renderer/WayUI','iD/renderer/NodeUI'], -function(declare, Event, domGeom, Gfx, Matrix){ +// ---------------------------------------------------------------------- +// Connection base class - // ---------------------------------------------------------------------- - // Connection base class +iD.renderer.Map = function(obj) { + var map = {}, + selection = [], + width = obj.width || 800, + height = obj.height || 400, + controller = iD.Controller(), + projection = d3.geo.mercator() + .scale(512).translate([512, 512]), + connection = obj.connection, + layers = {}; - declare("iD.renderer.Map", null, { + var tagclasses = [ + 'highway', 'railway', 'motorway', 'amenity', 'landuse', 'building', 'bridge']; - MASTERSCALE: 5825.4222222222, - MINSCALE: 14, - MAXSCALE: 23, - zoom: NaN, - zoomfactor: NaN, - baselon: NaN, // original longitude at top left of viewport - baselat: NaN, // original latitude at top left of viewport - baselatp: NaN, // original projected latitude at top left of viewport + var linegen = d3.svg.line() + .x(function(d) { return projection(d)[0]; }) + .y(function(d) { return projection(d)[1]; }); - div: '', //
of this map - surface: null, //
.surface containing the rendering - container: null, // root-level group within the surface - backdrop: null, // coloured backdrop (MapCSS canvas element) - connection: null, // data store - controller: null, // UI controller - uis: {}, + var zoombehavior = d3.behavior.zoom() + .translate(projection.translate()) + .scale(projection.scale()) + .scaleExtent([256, 134217728]); - tilegroup: null, // group within container for adding bitmap tiles - tiles: {}, // index of tile objects - tilebaseURL: 'http://ecn.t0.tiles.virtualearth.net/tiles/a$quadkey.jpeg?g=587&mkt=en-gb&n=z', // Bing imagery URL + zoombehavior.on('zoom', redraw); - dragging: false, // current drag state - dragged: false, // was most recent click a drag? - dragx: NaN, // click co-ordinates at previously recorded drag event - dragy: NaN, // | - startdragx: NaN, // click co-ordinates at start of drag - startdragy: NaN, // | - dragtime: NaN, // timestamp of mouseup (compared to stop resulting click from firing) - dragconnect: null, // event listener for endDrag + var surface = d3.selectAll(obj.selector) + .append('svg') + .attr({ width: width, height: width }) + .call(zoombehavior); - containerx: 0, // screen co-ordinates of container - containery: 0, // | - centrelat: NaN, // lat/long and bounding box of map - centrelon: NaN, // | - extent: {}, // | - mapheight: NaN, // size of map object in pixels - mapwidth: NaN, // | + var defs = surface.append('defs'); - layers: null, // array-like object of Groups, one for each OSM layer - minlayer: -5, // minimum OSM layer supported - maxlayer: 5, // maximum OSM layer supported + var clipPath = defs.append('clipPath') + .attr('id', 'clip') + .append('rect') + .attr('id', 'clip-rect') + .attr({ x: 0, y: 0 }) + .attr({ width: width, height: height }); - elastic: null, // Group for drawing elastic band + var tilegroup = surface.append('g') + .attr('clip-path', 'url(#clip)'), + container = surface.append('g') + .attr('clip-path', 'url(#clip)'); - ruleset: null, // map style + var r = container.append('g'); - constructor: function(obj) { - // summary: The main map display, containing the individual sprites (UIs) for each entity. - // obj: Object An object containing .lat, .lon, .scale, .div (the name of the
to be used), - // .connection, .width (px) and .height (px) properties. + layers[0] = { + root: r, + fill: r.append('g'), + casing: r.append('g'), + stroke: r.append('g'), + text: r.append('g'), + hit: r.append('g') + }; - this.mapwidth = obj.width ? obj.width : 800; - this.mapheight = obj.height ? obj.height : 400; + var elastic = container.append('g'); - // Initialise variables - this.uis = {}; - this.div=document.getElementById(obj.div); - this.surface=Gfx.createSurface(obj.div, this.mapwidth, this.mapheight); - this.backdrop=this.surface.createRect({ - x: 0, - y: 0, - width: this.mapwidth, - height: this.mapheight - }).setFill(new dojo.Color([255,255,245,1])); - this.tilegroup = this.surface.createGroup(); - this.container = this.surface.createGroup(); - this.connection = obj.connection; - this.zoom = obj.zoom ? obj.zoom : 17; - this.baselon = obj.lon; - this.baselat = obj.lat; - this.baselatp = this.lat2latp(obj.lat); - this._setScaleFactor(); - this.updateCoordsFromViewportPosition(); + var download = _.debounce(function() { + connection.loadFromAPI(extent(), drawVector); + }, 1000); - // Cache the margin box, since this is expensive. - this.marginBox = domGeom.getMarginBox(this.div); + function extent() { + return [ + projection.invert([0, 0]), + projection.invert([width, height])]; + } - // Initialise layers - this.layers={}; - for (var l=this.minlayer; l<=this.maxlayer; l++) { - var r=this.container.createGroup(); - this.layers[l]={ - root: r, - fill: r.createGroup(), - casing: r.createGroup(), - stroke: r.createGroup(), - text: r.createGroup(), - hit: r.createGroup() - }; + function select(d) { + selection = [d._id]; + } + + function key(d) { return d._id; } + + function classes(pre) { + return function(d) { + var tags = d.tags; + var c = [pre]; + function clean(x) { + return tagclasses.indexOf(x) !== -1; } - - // Create group for elastic band - this.elastic = this.container.createGroup(); - - // Make draggable - this.backdrop.connect("onmousedown", _.bind(this.startDrag, this)); - this.tilegroup.connect("onmousedown", _.bind(this.startDrag, this)); - this.surface.connect("onclick", _.bind(this.clickSurface, this)); - this.surface.connect("onmousemove", _.bind(this.processMove, this)); - this.surface.connect("onmousedown", _.bind(this._mouseEvent, this)); - this.surface.connect("onmouseup", _.bind(this._mouseEvent, this)); - }, - - setController:function(controller) { - // summary: Set the controller that will handle events on the map (e.g. mouse clicks). - this.controller = controller; - }, - - _moveToPosition:function(group, position) { - // summary: Supplementary method for dojox.gfx. - // This should ideally be core Dojo stuff: see http://bugs.dojotoolkit.org/ticket/15296 - var parent=group.getParent(); - if (!parent) { return; } - this._moveChildToPosition(parent,group,position); - if (position === group.rawNode.parentNode.childNodes.length) { - group.rawNode.parentNode.appendChild(group.rawNode); - } else { - group.rawNode.parentNode.insertBefore(group.rawNode, group.rawNode.parentNode.childNodes[position]); + for (var k in tags) { + if (!clean(k)) continue; + c.push(k + '-' + tags[k]); + c.push(k); } - }, - - _moveChildToPosition: function(parent, child, position) { - for (var i = 0; i < parent.children.length; ++i){ - if (parent.children[i] === child){ - parent.children.splice(i, 1); - parent.children.splice(position, 0, child); - break; - } + if (selection.indexOf(d._id) !== -1) { + c.push('active'); } - }, + return c.join(' '); + }; + } - // ---------------------------- - // Sprite and EntityUI handling + var icons = { + tourism: ['hotel'], + shop: [ + 'convenience', + 'supermarket'], + amenity: + [ + 'atm', + 'bank', + 'cafe', + 'pub', + 'place', + 'parking', + 'bicycle_parking', + 'pharmacy', + 'pharmacy', + 'police', + 'post_box', + 'recycling', + 'restaurant', + 'school', + 'taxi', + 'telephone'] + }; - sublayer:function(layer,groupType,sublayer) { - // summary: Find the gfx.Group for a given OSM layer and rendering sublayer, creating it - // if necessary. Note that sublayers are only implemented for stroke and fill. - // groupType: String 'casing','text','hit','stroke', or 'fill' - var collection = this.layers[layer][groupType], sub; - switch (groupType) { - case 'casing': - case 'text': - case 'hit': - return collection; + function markerimage(d) { + for (var k in icons) { + if (d.tags[k] && icons[k].indexOf(d.tags[k]) !== -1) { + return 'icons/' + d.tags[k] + '.png'; } - // Find correct sublayer, inserting if necessary - var insertAt=collection.children.length; - for (var i = 0; i < collection.children.length; i++) { - sub=collection.children[i]; - if (sub.sublayer==sublayer) { return sub; } - else if (sub.sublayer>sublayer) { - sub = collection.createGroup(); - this._moveToPosition(sub,i); - sub.sublayer=sublayer; - return sub; - } - } - sub = collection.createGroup().moveToFront(); - sub.sublayer=sublayer; - return sub; // dojox.gfx.Group - }, + } + } - createUI: function(e, stateClasses) { - // summary: Create a UI (sprite) for an entity, assigning any specified state classes - // (temporary attributes such as ':hover' or ':selected') - if (!this.uis[e.id]) { - if (e.entityType === 'node') { - this.uis[e.id] = new iD.renderer.NodeUI(e, this, stateClasses); - } else if (e.entityType === 'way') { - this.uis[e.id] = new iD.renderer.WayUI(e, this, stateClasses); - } - } else { - this.uis[e.id].setStateClasses(stateClasses).redraw(); - } - }, + function selectClick(d) { + select(d); + drawVector(); + } - getUI: function(e) { - // summary: Return the UI for an entity, if it exists. - return this.uis[e.id]; // iD.renderer.EntityUI - }, + function nodeline(d) { + return linegen(d.nodes); + } - refreshUI: function(e) { - // summary: Redraw the UI for an entity. - if (this.uis[e.id]) { this.uis[e.id].redraw(); } - }, + var highway_stack = [ + 'motorway', + 'motorway_link', + 'trunk', + 'trunk_link', + 'primary', + 'primary_link', + 'secondary', + 'tertiary', + 'unclassified', + 'residential', + 'service', + 'footway' + ]; - deleteUI: function(e) { - // summary: Delete the UI for an entity. - if (this.uis[e.id]) { - this.uis[e.id].removeSprites(); - delete this.uis[e.id]; - } - }, + function waystack(a, b) { + if (!a || !b) return 0; + if (a.tags.layer !== undefined && b.tags.layer !== undefined) { + return a.tags.layer - b.tags.layer; + } + if (a.tags.bridge) return 1; + if (b.tags.bridge) return -1; + var as = 0, bs = 0; + if (a.tags.highway && b.tags.highway) { + as -= highway_stack.indexOf(a.tags.highway); + bs -= highway_stack.indexOf(b.tags.highway); + } + return as - bs; + } - download: function() { - // summary: Ask the connection to download data for the current viewport. - $('#progress').show().addClass('spinner'); - this.connection.loadFromAPI(this.extent, _.bind(this.updateUIs, this)); - }, + function drawVector() { + var all = connection.all(); - updateUIs: function() { - // summary: Draw/refresh all EntityUIs within the bbox, and remove any others. - // redraw: Boolean Should we redraw any UIs that are already present? - // remove: Boolean Should we delete any UIs that are no longer in the bbox? - $('#progress').hide().removeClass('spinner'); - var o = this.connection.getObjectsByBbox(this.extent); - var touch = _(o.inside).chain() - .filter(function(w) { return w.loaded; }) - .map(_.bind(function(e) { - if (!this.uis[e.id]) { - this.createUI(e); - } else { - this.uis[e.id].redraw(); - } - return '' + e.id; - }, this)).value(); - _.each(_.difference(_.keys(this.uis), touch), _.bind(function(k) { - console.log(k); - this.deleteUI(k); - }, this)); - }, - - // ------------- - // Zoom handling - - zoomIn: function() { - // summary: Zoom in by one level (unless maximum reached). - return this.setZoom(this.zoom + 1); - }, - - zoomOut: function() { - // summary: Zoom out by one level (unless minimum reached). - this.setZoom(this.zoom - 1); - this.download(); - return this; - }, - - setZoom: function(zoom) { - if (zoom < this.MINSCALE || zoom > this.MAXSCALE) return this; - // summary: Redraw the map at a new zoom level. - this.zoom = zoom; - this._setScaleFactor(); - this._blankTiles(); - this.setCentre({ - lat: this.centrelat, - lon: this.centrelon + var ways = all.filter(function(a) { + return a.entityType === 'way' && !a.isClosed(); + }), + areas = all.filter(function(a) { + return a.entityType === 'way' && a.isClosed(); + }), + points = all.filter(function(a) { + return a.entityType === 'node'; }); - this.updateUIs(true, true); - return this; - }, - _setScaleFactor: function() { - // summary: Calculate the scaling factor for this zoom level. - this.zoomfactor = this.MASTERSCALE/Math.pow(2, 13 - this.zoom); - }, + var defpaths = defs.selectAll('path') + .data(ways, key), + fills = layers[0].fill.selectAll('path.area') + .data(areas, key), + casings = layers[0].casing.selectAll('use.casing') + .data(ways, key), + strokes = layers[0].stroke.selectAll('use.stroke') + .data(ways, key), + texts = layers[0].text.selectAll('text') + .data(ways.filter(function(w) { + return !!w.tags.name; + }), key), + markers = layers[0].hit.selectAll('image.marker') + .data(points, key); - // ---------------------- - // Elastic band redrawing + var _id = selection[0]; + var active_entity = all.filter(function(a) { + return a._id === _id; + }); - clearElastic: function() { - // summary: Remove the elastic band used to draw new ways. - this.elastic.clear(); - }, + var handles = layers[0].hit.selectAll('circle.handle') + .data(active_entity.length ? active_entity[0].nodes : [], key); - drawElastic: function(x1,y1,x2,y2) { - // summary: Draw the elastic band (for new ways) between two points. - this.elastic.clear(); - // **** Next line is SVG-specific - this.elastic.rawNode.setAttribute("pointer-events","none"); - this.elastic.createPolyline( [{ x:x1, y:y1 }, { x:x2, y:y2 }] ).setStroke( { - color: [0, 0, 0, 1], - style: 'Solid', - width: 1 + defpaths.exit().remove(); + texts.exit().remove(); + handles.exit().remove(); + fills.exit().remove(); + markers.exit().remove(); + casings.exit().remove(); + strokes.exit().remove(); + + defpaths.enter().append('path'); + + defpaths.attr('d', nodeline) + .attr('id', function(d) { + return 'd' + d._id; }); - }, - // ------------- - // Tile handling - // ** FIXME: see docs - loadTiles: function() { - // summary: Load all tiles for the current viewport. This is a bare-bones function - // at present: it needs configurable URLs (not just Bing), attribution/logo - // support, and to be 'nudgable' (i.e. adjust the offset). - var tl = this.locationCoord({ - lat: this.extent.north, - lon: this.extent.west - }, this.zoom), - br = this.locationCoord({ - lat: this.extent.south, - lon: this.extent.east - }, this.zoom), - tileKeys = _.keys(this.tiles), - seen = [], - coord = { z: this.zoom }; + function usehref(d) { + return '#d' + d._id; + } - for (coord.x = tl.x; coord.x <= br.x; coord.x++) { - for (coord.y = tl.y; coord.y <= br.y; coord.y++) { - if (!this._getTile(coord)) { - this._fetchTile(coord); - } - seen.push(iD.Util.tileKey(coord)); - } - } + fills.enter().append('path') + .on('click', selectClick); - _.each(_.without(tileKeys, seen), _.bind(function(key) { - delete this.tiles[key]; - }, this)); - }, + fills.attr('d', nodeline) + .attr('class', classes('area')); - _fetchTile: function(coord) { - // summary: Load a tile image at the given tile co-ordinates. - var t = this.tilegroup.createImage({ - x: Math.floor(this.lon2coord(this.tile2lon(coord.x))), - y: Math.floor(this.lat2coord(this.tile2lat(coord.y))), - width: 256, - height: 256, - src: this._tileURL(coord) + casings.enter().append('use'); + casings.sort(waystack) + .attr('xlink:href', usehref) + .attr('class', classes('casing')); + + strokes.enter().append('use') + .on('click', selectClick); + + strokes.sort(waystack).attr('xlink:href', usehref) + .attr('class', classes('stroke')); + + markers.enter().append('image'); + markers.attr('class', classes('marker')) + .attr({ width: 16, height: 16 }) + .attr('xlink:href', markerimage) + .attr('transform', function(d) { + return 'translate(' + projection(d) + ')'; }); - this._assignTile(coord, t); - }, - _getTile: function(coord) { - // summary: See if this tile is already loaded. - return this.tiles[iD.Util.tileKey(coord)]; - }, + var textems = texts.enter().append('text') + .attr('dy', 3); - _assignTile: function(coord, t) { - // summary: Store a reference to the tile so we know it's loaded. - this.tiles[iD.Util.tileKey(coord)] = t; - }, + textems.append('textPath') + .attr('xlink:href', usehref) + .attr('startOffset', '50%') + .text(function(d) { return d.tags.name; }); - _tileURL: function(coord) { - // summary: Calculate the URL for a tile at the given co-ordinates. - var u = ''; - for (var zoom = coord.z; zoom > 0; zoom--) { - var byte = 0; - var mask = 1 << (zoom - 1); - if ((coord.x & mask) !== 0) byte++; - if ((coord.y & mask) !== 0) byte += 2; - u += byte.toString(); - } - return this.tilebaseURL - .replace('$z', coord.z) - .replace('$x', coord.x) - .replace('$y', coord.y) - .replace('$quadkey', u); - }, + handles.enter().append('circle') + .attr('class', 'handle') + .attr('r', 5) + .on('click', selectClick); + handles.attr('transform', function(d) { + return 'translate(' + projection(d) + ')'; + }); + } + + // ------------- + // Zoom handling + function zoomIn() { + // summary: Zoom in by one level (unless maximum reached). + return setZoom(getZoom() + 1); + } - _blankTiles: function() { - // summary: Unload all tiles and remove from the display. - this.tilegroup.clear(); - this.tiles = {}; - }, + function zoomOut() { + // summary: Zoom out by one level (unless minimum reached). + return setZoom(getZoom() - 1); + } - // ------------------------------------------- - // Co-ordinate management, dragging and redraw + function getZoom(zoom) { + var s = projection.scale(); + return Math.max(Math.log(s) / Math.log(2) - 8, 0); + } - startDrag: function(e) { - // summary: Start dragging the map in response to a mouse-down. - // e: MouseEvent The mouse-down event that triggered it. - var srcElement = (e.gfxTarget === this.backdrop) ? - e.gfxTarget : e.gfxTarget.parent; - Event.stop(e); - this.dragging = true; - this.dragged = false; - this.dragx = this.dragy=NaN; - this.startdragx = e.clientX; - this.startdragy = e.clientY; - this.dragconnect = srcElement.connect("onmouseup", _.bind(this.endDrag, this)); - }, + function setZoom(zoom) { + // summary: Redraw the map at a new zoom level. + projection.scale(256 * Math.pow(2, zoom - 1)); + zoombehavior.scale(projection.scale()); + drawVector(); + redraw(); + return map; + } - endDrag: function(e) { - // summary: Stop dragging the map in response to a mouse-up. - // e: MouseEvent The mouse-up event that triggered it. - Event.stop(e); - dojo.disconnect(this.dragconnect); - this.dragging=false; - this.dragtime=e.timeStamp; - this.updateCoordsFromViewportPosition(); - if (Math.abs(e.clientX - this.startdragx) < 3 && - Math.abs(e.clientY - this.startdragy) < 3) { - return; - } - this.download(); - }, + function tilesForView() { + var t = projection.translate(), + s = projection.scale(), + z = Math.max(Math.log(s) / Math.log(2) - 8, 0); + rz = Math.floor(z), + ts = 256 * Math.pow(2, z - rz); - processMove: function(e) { - // summary: Drag the map to a new origin. - // e: MouseEvent The mouse-move event that triggered it. - var x = e.clientX; - var y = e.clientY; - if (this.dragging) { - if (this.dragx) { - this.containerx += (x - this.dragx); - this.containery += (y - this.dragy); - this.updateOrigin(); - this.dragged=true; - } - this.dragx = x; - this.dragy = y; - } else { - this.controller.entityMouseEvent(e,null); - } - }, + // This is the 0, 0 px of the projection + var tile_origin = [s / 2 - t[0], s / 2 - t[1]], + coords = [], + cols = d3.range(Math.max(0, Math.floor((tile_origin[0] - width) / ts)), + Math.max(0, Math.ceil((tile_origin[0] + width) / ts))), + rows = d3.range(Math.max(0, Math.floor((tile_origin[1] - height) / ts)), + Math.max(0, Math.ceil((tile_origin[1] + height) / ts))); - updateOrigin: function() { - // summary: Tell Dojo to update the viewport origin. - this.container.setTransform([Matrix.translate(this.containerx, this.containery)]); - this.tilegroup.setTransform([Matrix.translate(this.containerx, this.containery)]); - }, + cols.forEach(function(x) { + rows.forEach(function(y) { coords.push([Math.floor(z), x, y]); }); + }); + return coords; + } - _mouseEvent: function(e) { - // summary: Catch mouse events on the surface but not the tiles - in other words, - // on drawn items that don't have their own hitzones, like the fill of a shape. - if (e.type=='mousedown') { this.startDrag(e); } - // ** FIXME: we may want to reinstate this at some point... - // this.controller.entityMouseEvent(e,null); - }, - updateCoordsFromViewportPosition: function(e) { - // summary: Update centre and bbox from the current viewport origin. - this._updateCoords(this.containerx, this.containery); - }, + function tileUrl(coord) { + var tmpl = 'http://ecn.t0.tiles.virtualearth.net/tiles/a$quadkey.jpeg?g=587&mkt=en-gb&n=z'; + var u = ''; + for (var zoom = coord[0]; zoom > 0; zoom--) { + var byte = 0; + var mask = 1 << (zoom - 1); + if ((coord[1] & mask) !== 0) byte++; + if ((coord[2] & mask) !== 0) byte += 2; + u += byte.toString(); + } + return tmpl.replace('$quadkey', u); + } - setCentre: function(loc) { - // summary: Update centre and bbox to a specified lat/lon. - var coord = this.locationCoord(loc, this.zoom); - this._updateCoords( - -coord.x - this.mapwidth / 2, - -coord.y - this.mapheight / 2); - return this; - }, + function redraw() { + if (d3.event) { + projection + .translate(d3.event.translate) + .scale(d3.event.scale); + } - setCenter: function(loc) { this.setCentre(loc); }, + var t = projection.translate(), + s = projection.scale(), + z = Math.max(Math.log(s) / Math.log(2) - 8, 0); + rz = Math.floor(z), + ts = 256 * Math.pow(2, z - rz); - _updateCoords:function(x, y) { - // summary: Set centre and bbox. - this.containerx = x; - this.containery = y; - this.updateOrigin(); - this.centrelon = this.coord2lon(-x + this.mapwidth/2); - this.centrelat = this.coord2lat(-y + this.mapheight/2); + // This is the 0, 0 px of the projection + var tile_origin = [s / 2 - t[0], s / 2 - t[1]], + coords = tilesForView(); - this.extent = { - north: this.coord2lat(-y), - south: this.coord2lat(-y + this.mapheight), - west: this.coord2lon(-x), - east: this.coord2lon(-x + this.mapwidth) - }; + var tiles = tilegroup.selectAll('image.tile') + .data(coords, function(d) { return d.join(','); }); - this.loadTiles(); - }, + tiles.exit().remove(); + tiles.enter().append('image') + .attr('class', 'tile') + .attr('xlink:href', tileUrl); + tiles.attr({ width: ts, height: ts }) + .attr('transform', function(d) { + return 'translate(' + [(d[1] * ts) - tile_origin[0], (d[2] * ts) - tile_origin[1]] + ')'; + }); + drawVector(); + download(); + } - clickSurface:function(e) { - // summary: Handle a click on an empty area of the map. - if (this.dragged && e.timeStamp==this.dragtime) { return; } - this.controller.entityMouseEvent(e,null); - }, + function setCentre(loc) { + // summary: Update centre and bbox to a specified lat/lon. + var t = projection.translate(), + ll = projection([loc.lon, loc.lat]); + projection.translate([ + t[0] - ll[0] + width / 2, + t[1] - ll[1] + height / 2]); + zoombehavior.translate(projection.translate()); + redraw(); + return map; + } - // ----------------------- - // Co-ordinate conversions + map.download = download; + map.extent = extent; + map.setCentre = setCentre; + map.setCenter = setCentre; - latp2coord:function(a) { return -(a-this.baselatp)*this.zoomfactor; }, - coord2latp:function(a) { return a/-this.zoomfactor+this.baselatp; }, - lon2coord:function(a) { return (a-this.baselon)*this.zoomfactor; }, - coord2lon:function(a) { return a/this.zoomfactor+this.baselon; }, - lon2screen:function(a) { return this.lon2coord(a) + this.marginBox.l + this.containerx; }, + map.getZoom = getZoom; + map.setZoom = setZoom; + map.zoomIn = zoomIn; + map.zoomOut = zoomOut; - lat2latp:function(a) { return 180/Math.PI * Math.log(Math.tan(Math.PI/4+a*(Math.PI/180)/2)); }, - latp2lat:function(a) { return 180/Math.PI * (2 * Math.atan(Math.exp(a*Math.PI/180)) - Math.PI/2); }, - lat2coord:function(a) { return -(this.lat2latp(a)-this.baselatp)*this.zoomfactor; }, - coord2lat:function(a) { return this.latp2lat(a/-this.zoomfactor+this.baselatp); }, - lat2screen:function(a) { return this.lat2coord(a) + this.marginBox.t + this.containery; }, + map.connection = connection; + map.controller = controller; + map.projection = projection; - locationCoord: function(ll, z) { - var z2 = Math.pow(2, z), d2r = Math.PI / 180; - return { - z: z, - x: Math.floor((ll.lon + 180) / 360 * z2), - y: Math.floor((1 - Math.log(Math.tan(ll.lat * d2r) + - 1 / Math.cos(ll.lat * d2r)) / Math.PI) / 2 * z2) - }; - }, - lon2tile:function(a) { return (Math.floor((a+180)/360*Math.pow(2,this.zoom))); }, - lat2tile:function(a) { return (Math.floor((1-Math.log(Math.tan(a*Math.PI/180) + 1/Math.cos(a*Math.PI/180))/Math.PI)/2 *Math.pow(2,this.zoom))); }, - tile2lon:function(a) { return (a/Math.pow(2,this.zoom)*360-180); }, - tile2lat:function(a) { - var n=Math.PI-2*Math.PI*a/Math.pow(2,this.zoom); - return (180/Math.PI*Math.atan(0.5*(Math.exp(n)-Math.exp(-n)))); - }, - - // Turn event co-ordinates into map co-ordinates - - mouseX: function(e) { return e.clientX - this.marginBox.l - this.containerx; }, - mouseY: function(e) { return e.clientY - this.marginBox.t - this.containery; } - }); - - // ---------------------------------------------------------------------- - // End of module -}); + redraw(); + return map; +}; diff --git a/js/iD/renderer/NodeUI.js b/js/iD/renderer/NodeUI.js deleted file mode 100755 index 163fcacbb..000000000 --- a/js/iD/renderer/NodeUI.js +++ /dev/null @@ -1,107 +0,0 @@ -// iD/renderer/NodeUI.js -// NodeUI classes for iD - -define(['dojo/_base/declare','dojox/gfx/_base','iD/renderer/EntityUI'], - function(declare, g) { - -// ---------------------------------------------------------------------- -// NodeUI class - -declare("iD.renderer.NodeUI", [iD.renderer.EntityUI], { - constructor:function() { - // summary: A UI (rendering) representing a node. - this.redraw(); - }, - getEnhancedTags:function() { - var tags=this.inherited(arguments); - if (!this.entity.entity.hasParentWays()) { tags[':poi']='yes'; } - // add junction and dupe - return tags; - }, - redraw:function() { - // summary: Draw the object (mostly icons) and add hitzone sprites. - var node = this.entity; - this.removeSprites(); - - // Tags, position and styleList - var x = Math.floor(this.map.lon2coord(this.entity.lon)); - var y = Math.floor(this.map.latp2coord(this.entity.latp)); - var tags = this.getEnhancedTags(); - this.refreshStyleList(tags); - - // Iterate through each subpart, drawing any styles on that layer - var drawn = false; - var s, p, t, w, h; - for (i = 0; i < this.styleList.subparts.length; i++) { - var subpart=this.styleList.subparts[i]; - p = this.styleList.pointStyles[subpart]; - if (!p || !p.drawn()) { continue; } - s = this.styleList.shapeStyles[subpart]; - t = this.styleList.textStyles[subpart]; - w = p.icon_width ? p.icon_width : 16; - h = p.icon_height ? p.icon_height: w; - - // Draw icon - var shape; - if (p.icon_image === 'square') shape = this.targetGroup('stroke', p.sublayer) - .createRect({ - x: x-w/2, - y: y-h/2, - width: w, - height: h - }); - else if (p.icon_image === 'circle') shape = this.targetGroup('stroke', p.sublayer) - .createCircle({ - cx: x, - cy: y, - r: w - }); - else shape = this.targetGroup('stroke',p.sublayer) - .createImage({ - width: w, - height: h, - x: x-w/2, - y: y-h/2, - src: p.icon_image - }); - if (p.icon_image === 'square' || p.icon_image === 'circle') { - shape.setStroke(s.shapeStrokeStyler()).setFill(s.shapeFillStyler()); - } - this.recordSprite(shape); - - // Add text label - // Add hit-zone - var hit; - if (p.icon_image === 'circle') { - hit = this.targetGroup('hit').createCircle({ - cx: x, - cy: y, - r: w - }); - } else { - hit = this.targetGroup('hit').createRect({ - x: x-w/2, - y: y-h/2, - width: w, - height: h - }); - } - hit.setFill([0,1,0,0]).setStroke({ - width:2, - color:[0,0,0,0] - }); - this.recordSprite(hit); - hit.source = this; - hit.connect("onclick", _.bind(this.entityMouseEvent, this)); - hit.connect("onmousedown", _.bind(this.entityMouseEvent, this)); - hit.connect("onmouseup", _.bind(this.entityMouseEvent, this)); - hit.connect("onmouseenter", _.bind(this.entityMouseEvent, this)); - hit.connect("onmouseleave", _.bind(this.entityMouseEvent, this)); - } - } -}); - - -// ---------------------------------------------------------------------- -// End of module -}); diff --git a/js/iD/renderer/WayUI.js b/js/iD/renderer/WayUI.js deleted file mode 100755 index 3ae0def54..000000000 --- a/js/iD/renderer/WayUI.js +++ /dev/null @@ -1,146 +0,0 @@ -// iD/renderer/WayUI.js -// WayUI classes for iD -// **** TODO: -// multipolygon support - http://mail.dojotoolkit.org/pipermail/dojo-interest/2011-January/052042.html -// support 'interactive' -// line decoration, dots etc. -// fill images -// opacity - -define(['dojo/_base/declare','iD/renderer/EntityUI'], function(declare) { - -// ---------------------------------------------------------------------- -// WayUI class - -declare("iD.renderer.WayUI", [iD.renderer.EntityUI], { - constructor: function() { - // summary: A UI (rendering) representing a way. - this.redraw(); - }, - getEnhancedTags: function() { - var tags = this.inherited(arguments); - if (this.entity.isClosed()) { tags[':area']='yes'; } - return tags; - }, - recalculate: function() { - // summary: Not yet implemented - calculate length/centrepoint of UI for use in rendering. - // ** FIXME: todo - }, - redraw: function() { - // summary: Draw the object and add hitzone sprites. - var way = this.entity, - maxwidth = 4, - i; - - this.removeSprites(); - if (!way.nodes.length) { return; } - - // Create tags and calculate styleList - var tags = this.getEnhancedTags(); - this.refreshStyleList(tags); - - // List of co-ordinates - var coords = _.map(way.nodes, _.bind(function(node) { - return { - x: this.map.lon2coord(node.lon), - y: this.map.latp2coord(node.latp) - }; - }, this)); - - // Iterate through each subpart, drawing any styles on that layer - var drawn = false; - for (i = 0; i < this.styleList.subparts.length; i++) { - var subpart = this.styleList.subparts[i]; - if (this.styleList.shapeStyles[subpart]) { - var s = this.styleList.shapeStyles[subpart], line; - - // Stroke - if (s.width) { - line = this.targetGroup('stroke',s.sublayer) - .createPolyline(coords) - .setStroke(s.strokeStyler()); - - this.recordSprite(line); - maxwidth = Math.max(maxwidth, s.width); - drawn = true; - } - - // Fill - if (!isNaN(s.fill_color)) { - line = this.targetGroup('fill',s.sublayer) - .createPolyline(coords) - .setFill(s.fillStyler()); - - this.recordSprite(line); - drawn = true; - } - - // Casing - if (s.casing_width) { - line = this.targetGroup('casing') - .createPolyline(coords) - .setStroke(s.casingStyler()); - - this.recordSprite(line); - maxwidth = Math.max(maxwidth, s.width + s.casing_width * 2); - drawn = true; - } - } - - // Text label on path - if (this.styleList.textStyles[subpart]) { - var t = this.styleList.textStyles[subpart]; - if (t.text && tags[t.text]) { - var tp=this.recordSprite(this.targetGroup('text') - .createTextPath(t.textStyler(tags[t.text])) - .setFont(t.fontStyler()) - .setFill(t.fillStyler()) - .moveTo(coords[0].x,coords[0].y)); - for (var j=1; j1) { sc.push('junction'); } - this.map.createUI(node,sc); - } - - return this; - }, - - entityMouseEvent:function(event) { - this.inherited(arguments); - } -}); - -// ---------------------------------------------------------------------- -// End of module -}); diff --git a/js/iD/renderer/renderer.js b/js/iD/renderer/renderer.js new file mode 100644 index 000000000..593d9bb7a --- /dev/null +++ b/js/iD/renderer/renderer.js @@ -0,0 +1 @@ +iD.renderer = {}; diff --git a/js/iD/styleparser/Condition.js b/js/iD/styleparser/Condition.js deleted file mode 100755 index 6c509b6f4..000000000 --- a/js/iD/styleparser/Condition.js +++ /dev/null @@ -1,45 +0,0 @@ -// iD/styleparser/Condition.js - -define(['dojo/_base/declare'], function(declare){ - -// ---------------------------------------------------------------------- -// Condition base class - -declare("iD.styleparser.Condition", null, { - type: '', // eq/ne/regex etc. - params: [], // what to test against - - constructor:function(_type) { - // summary: A condition to evaluate. - this.type =_type; - this.params=Array.prototype.slice.call(arguments,1); - }, - - test:function(tags) { - // summary: Run the condition against the supplied tags. - var p=this.params; - switch (this.type) { - case 'eq': return (tags[p[0]]==p[1]); - case 'ne': return (tags[p[0]]!=p[1]); - case 'regex': var r=new RegExp(p[1],"i"); - return (r.test(tags[p[0]])); - case 'true': return (tags[p[0]]=='true' || tags[p[0]]=='yes' || tags[p[0]]=='1'); - case 'false': return (tags[p[0]]=='false' || tags[p[0]]=='no' || tags[p[0]]=='0'); - case 'set': return (tags[p[0]] !== undefined && tags[p[0]]!==''); - case 'unset': return (tags[p[0]] === undefined || tags[p[0]]===''); - case '<': return (Number(tags[p[0]])< Number(p[1])); - case '<=': return (Number(tags[p[0]])<=Number(p[1])); - case '>': return (Number(tags[p[0]])> Number(p[1])); - case '>=': return (Number(tags[p[0]])>=Number(p[1])); - } - return false; - }, - - toString:function() { - return "["+this.type+": "+this.params+"]"; - } -}); - -// ---------------------------------------------------------------------- -// End of module -}); diff --git a/js/iD/styleparser/Rule.js b/js/iD/styleparser/Rule.js deleted file mode 100755 index 5fffb211f..000000000 --- a/js/iD/styleparser/Rule.js +++ /dev/null @@ -1,55 +0,0 @@ -// iD/styleparser/Rule.js - -define(['dojo/_base/declare','dojo/_base/array'], function(declare,array){ - -// ---------------------------------------------------------------------- -// Rule class - -declare("iD.styleparser.Rule", null, { - - conditions: [], // the Conditions to be evaluated for the Rule to be fulfilled - isAnd: true, // do all Conditions need to be true for the Rule to be fulfilled? (Always =true for MapCSS) - minZoom: 0, // minimum zoom level at which the Rule is fulfilled - maxZoom: 255, // maximum zoom level at which the Rule is fulfilled - subject: '', // entity type to which the Rule applies: 'way', 'node', 'relation', 'area' (closed way) or 'line' (unclosed way) - - constructor: function(_subject) { - // summary: A MapCSS selector. Contains a list of Conditions; the entity type to which the selector applies; - // and the zoom levels at which it is true. way[waterway=river][boat=yes] would be parsed into one Rule. - // The selectors and declaration together form a StyleChooser. - this.subject=_subject; - this.conditions=[]; - }, - - addCondition: function(_condition) { - // summary: Add a condition to this rule. - this.conditions.push(_condition); - }, - - test: function(entity,tags,zoom) { - // summary: Evaluate the Rule on the given entity, tags and zoom level. - // returns: true if the Rule passes, false if the conditions aren't fulfilled. - if ((this.subject !== '') && (entity.entityType !== this.subject)) { - return false; - } - if (zoomthis.maxZoom) { return false; } - - var v=true; var i=0; var isAnd=this.isAnd; - array.forEach(this.conditions, function(condition) { - var r=condition.test(tags); - if (i === 0) { v=r; } - else if (isAnd) { v=v && r; } - else { v = v || r; } - i++; - }); - return v; - }, - - toString:function() { - return this.subject+" z"+this.minZoom+"-"+this.maxZoom+": "+this.conditions; - } -}); - -// ---------------------------------------------------------------------- -// End of module -}); diff --git a/js/iD/styleparser/RuleChain.js b/js/iD/styleparser/RuleChain.js deleted file mode 100755 index 96b1658c0..000000000 --- a/js/iD/styleparser/RuleChain.js +++ /dev/null @@ -1,83 +0,0 @@ -// iD/styleparser/RuleChain.js - -define(['dojo/_base/declare','iD/styleparser/Rule'], function(declare){ - -// ---------------------------------------------------------------------- -// RuleChain base class -// In contrast to Halcyon, note that length() is a function, not a getter property - -/** A descendant list of MapCSS selectors (Rules). - - For example, - relation[type=route] way[highway=primary] - ^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^ - first Rule second Rule - |------------|---------| - | - one RuleChain - -*/ - -declare("iD.styleparser.RuleChain", null, { - - rules:[], // list of Rules - subpart: 'default', // subpart name, as in way[highway=primary]::centreline - - constructor:function() { - // summary: A descendant list of MapCSS selectors (Rules). - this.rules=[]; - }, - - // Functions to define the RuleChain - - addRule:function(_subject) { - this.rules.push(new iD.styleparser.Rule(_subject)); - }, - - addConditionToLast:function(_condition) { - this.rules[this.rules.length-1].addCondition(_condition); - }, - - addZoomToLast:function(z1,z2) { - this.rules[this.rules.length-1].minZoom=z1; - this.rules[this.rules.length-1].maxZoom=z2; - }, - - - length:function() { - return this.rules.length; - }, - - setSubpart:function(subpart) { - this.subpart = subpart || 'default'; - }, - - // Test a ruleChain - // - run a set of tests in the chain - // works backwards from at position "pos" in array, or -1 for the last - // separate tags object is required in case they've been dynamically retagged - // - if they fail, return false - // - if they succeed, and it's the last in the chain, return happily - // - if they succeed, and there's more in the chain, rerun this for each parent until success - - test: function(pos, entity, tags, zoom) { - // summary: Test a rule chain by running all the tests in reverse order. - if (this.rules.length === 0) { return false; } - if (pos==-1) { pos=this.rules.length-1; } - - var r = this.rules[pos]; - if (!r.test(entity, tags, zoom)) { return false; } - if (pos === 0) { return true; } - - var o = entity.entity.parentObjects(); - for (var i = 0; i < o.length; i++) { - var p=o[i]; - if (this.test(pos-1, p, p.tags, zoom)) { return true; } - } - return false; - } -}); - -// ---------------------------------------------------------------------- -// End of module -}); diff --git a/js/iD/styleparser/RuleSet.js b/js/iD/styleparser/RuleSet.js deleted file mode 100755 index f2277d0be..000000000 --- a/js/iD/styleparser/RuleSet.js +++ /dev/null @@ -1,458 +0,0 @@ -// iD/styleparser/RuleSet.js - -define(['dojo/_base/xhr','dojo/_base/lang','dojo/_base/declare','dojo/_base/array','iD/styleparser/Style','iD/styleparser/StyleChooser','iD/styleparser/Condition','iD/styleparser/StyleList'], function(xhr,lang,declare,array){ - -// ---------------------------------------------------------------------- -// RuleSet base class -// needs to cope with nested CSS files -// doesn't do untagged nodes optimisation - -declare("iD.styleparser.RuleSet", null, { - - choosers: [], // list of StyleChoosers - callback: null, - - constructor: function() { - // summary: An entire stylesheet in parsed form. - this.choosers = []; - }, - - registerCallback: function(callback) { - // summary: Set a callback function to be called when the CSS is loaded and parsed. - this.callback = callback; - }, - - getStyles: function(entity, tags, zoom) { - // summary: Find the styles for a given entity. - var sl=new iD.styleparser.StyleList(); - for (var i in this.choosers) { - this.choosers[i].updateStyles(entity, tags, sl, zoom); - } - return sl; // iD.styleparser.StyleList - }, - - loadFromCSS:function(url) { - // summary: Load a MapCSS file from a URL, then throw it at the parser when it's loaded. - xhr.get({ url: url, load: lang.hitch(this, "parseCSS") }); - }, - - parseCSS:function(css) { - // summary: Parse a CSS document into a set of StyleChoosers. - var previous=0; // what was the previous CSS word? - var sc = new iD.styleparser.StyleChooser(); // currently being assembled - this.choosers = []; - css = css.replace(/[\r\n]/g,""); // strip linebreaks because JavaScript doesn't have the /s modifier - - var o = {}; - while (css.length>0) { - - // CSS comment - if ((o=this.COMMENT.exec(css))) { - css=css.replace(this.COMMENT,''); - - // Whitespace (probably only at beginning of file) - } else if ((o=this.WHITESPACE.exec(css))) { - css=css.replace(this.WHITESPACE,''); - - // Class - .motorway, .builtup, :hover - } else if ((o=this.CLASS.exec(css))) { - if (previous==this.oDECLARATION) { this.saveChooser(sc); sc=new iD.styleparser.StyleChooser(); } - - css=css.replace(this.CLASS,''); - sc.currentChain().addConditionToLast(new iD.styleparser.Condition('set',o[1])); - previous=this.oCONDITION; - - // Not class - !.motorway, !.builtup, !:hover - } else if ((o=this.NOT_CLASS.exec(css))) { - if (previous==this.oDECLARATION) { this.saveChooser(sc); sc=new iD.styleparser.StyleChooser(); } - - css=css.replace(this.NOT_CLASS,''); - sc.currentChain().addConditionToLast(new iD.styleparser.Condition('unset',o[1])); - previous=this.oCONDITION; - - // Zoom - } else if ((o=this.ZOOM.exec(css))) { - if (previous!=this.oOBJECT && previous!=this.oCONDITION) { sc.currentChain().addRule(); } - - css=css.replace(this.ZOOM,''); - var z=parseZoom(o[1]); - sc.currentChain().addZoomToLast(z[0],z[1]); - sc.zoomSpecific=true; - previous=this.oZOOM; - - // Grouping - just a comma - } else if ((o=this.GROUP.exec(css))) { - css=css.replace(this.GROUP,''); - sc.newRuleChain(); - previous=this.oGROUP; - - // Condition - [highway=primary] - } else if ((o=this.CONDITION.exec(css))) { - if (previous==this.oDECLARATION) { this.saveChooser(sc); sc=new iD.styleparser.StyleChooser(); } - if (previous!=this.oOBJECT && previous!=this.oZOOM && previous!=this.oCONDITION) { sc.currentChain().addRule(); } - css=css.replace(this.CONDITION,''); - sc.currentChain().addConditionToLast(this.parseCondition(o[1])); - previous=this.oCONDITION; - - // Object - way, node, relation - } else if ((o=this.OBJECT.exec(css))) { - if (previous==this.oDECLARATION) { this.saveChooser(sc); sc=new iD.styleparser.StyleChooser(); } - - css=css.replace(this.OBJECT,''); - sc.currentChain().addRule(o[1]); - previous=this.oOBJECT; - - // Subpart - ::centreline - } else if ((o=this.SUBPART.exec(css))) { - if (previous==this.oDECLARATION) { this.saveChooser(sc); sc=new iD.styleparser.StyleChooser(); } - css=css.replace(this.SUBPART,''); - sc.currentChain().setSubpart(o[1]); - previous=this.oSUBPART; - - // Declaration - {...} - } else if ((o=this.DECLARATION.exec(css))) { - css=css.replace(this.DECLARATION,''); - sc.addStyles(this.parseDeclaration(o[1])); - previous=this.oDECLARATION; - - // Unknown pattern - } else if ((o=this.UNKNOWN.exec(css))) { - css=css.replace(this.UNKNOWN,''); - // console.log("unknown: "+o[1]); - - } else { - // console.log("choked on "+css); - return; - } - } - if (previous==this.oDECLARATION) { this.saveChooser(sc); sc=new iD.styleparser.StyleChooser(); } - if (this.callback) { this.callback(); } - }, - - saveChooser:function(sc) { - this.choosers.push(sc); - }, - - parseDeclaration:function(s) { - var styles=[]; - var t={}; - var o={}; - var k, v; - - // Create styles - var ss = new iD.styleparser.ShapeStyle(); - var ps = new iD.styleparser.PointStyle(); - var ts = new iD.styleparser.TextStyle(); - var hs = new iD.styleparser.ShieldStyle(); - var xs = new iD.styleparser.InstructionStyle(); - - var r=s.split(';'); - var isEval={}; - for (var i in r) { - var a=r[i]; - if ((o=this.ASSIGNMENT_EVAL.exec(a))) { k=o[1].replace(this.DASH,'_'); t[k]=o[2]; isEval[k]=true; } - else if ((o=this.ASSIGNMENT.exec(a))) { k=o[1].replace(this.DASH,'_'); t[k]=o[2]; } - else if ((o=this.SET_TAG_EVAL.exec(a))) { } // xs.addSetTag(o[1],this.saveEval(o[2])); - else if ((o=this.SET_TAG.exec(a))) { xs.addSetTag(o[1],o[2]); } - else if ((o=this.SET_TAG_TRUE.exec(a))) { xs.addSetTag(o[1],true); } - else if ((o=this.EXIT.exec(a))) { xs.setPropertyFromString('breaker',true); } - } - - // Find sublayer - var sub=5; - if (t['z_index']) { sub=Number(t['z_index']); delete t['z_index']; } - ss.sublayer=ps.sublayer=ts.sublayer=hs.sublayer=sub; - xs.sublayer=10; - - // Find "interactive" property - it's true unless explicitly set false - var inter=true; - if (t['interactive']) { inter=t['interactive'].match(this.FALSE) ? false : true; delete t['interactive']; } - ss.interactive=ps.interactive=ts.interactive=hs.interactive=xs.interactive=inter; - - // Munge special values - // (we should stop doing this and do it in the style instead) - if (t['font_weight'] ) { t['font_bold' ] = t['font_weight' ].match(this.BOLD ) ? true : false; delete t['font_weight']; } - if (t['font_style'] ) { t['font_italic'] = t['font_style' ].match(this.ITALIC) ? true : false; delete t['font_style']; } - if (t['text_decoration']) { t['font_underline'] = t['text_decoration'].match(this.UNDERLINE) ? true : false; delete t['text_decoration']; } - if (t['text_position'] ) { t['text_center'] = t['text_position' ].match(this.CENTER) ? true : false; delete t['text_position']; } - if (t['text_transform']) { - if (t['text_transform'].match(this.CAPS)) { t['font_caps']=true; } else { t['font_caps']=false; } - delete t['text_transform']; - } - - // Assign each property to the appropriate style - for (a in t) { - // Parse properties - // ** also do units, e.g. px/pt/m - if (a.match(this.COLOR)) { v = this.parseCSSColor(t[a]); } - else { v = t[a]; } - - // Set in styles - if (ss.has(a)) { ss.setPropertyFromString(a,v,isEval[a]); } - else if (ps.has(a)) { ps.setPropertyFromString(a,v,isEval[a]); } - else if (ts.has(a)) { ts.setPropertyFromString(a,v,isEval[a]); } - else if (hs.has(a)) { hs.setPropertyFromString(a,v,isEval[a]); } - else { - // console.log(a+" not found"); - } - } - - // Add each style to list - if (ss.edited) { styles.push(ss); } - if (ps.edited) { styles.push(ps); } - if (ts.edited) { styles.push(ts); } - if (hs.edited) { styles.push(hs); } - if (xs.edited) { styles.push(xs); } - return styles; - }, - - parseZoom:function(s) { - var o={}; - if ((o=this.ZOOM_MINMAX.exec(s))) { return [o[1],o[2]]; } - else if ((o=this.ZOOM_MIN.exec(s) )) { return [o[1], maxscale]; } - else if ((o=this.ZOOM_MAX.exec(s) )) { return [minscale, o[1]]; } - else if ((o=this.ZOOM_SINGLE.exec(s))) { return [o[1],o[1]]; } - return null; - }, - - parseCondition:function(s) { - var o={}; - if ((o=this.CONDITION_TRUE.exec(s))) { return new iD.styleparser.Condition('true' ,o[1]); } - else if ((o=this.CONDITION_FALSE.exec(s))) { return new iD.styleparser.Condition('false',o[1]); } - else if ((o=this.CONDITION_SET.exec(s))) { return new iD.styleparser.Condition('set' ,o[1]); } - else if ((o=this.CONDITION_UNSET.exec(s))) { return new iD.styleparser.Condition('unset',o[1]); } - else if ((o=this.CONDITION_NE.exec(s))) { return new iD.styleparser.Condition('ne' ,o[1],o[2]); } - else if ((o=this.CONDITION_GT.exec(s))) { return new iD.styleparser.Condition('>' ,o[1],o[2]); } - else if ((o=this.CONDITION_GE.exec(s))) { return new iD.styleparser.Condition('>=' ,o[1],o[2]); } - else if ((o=this.CONDITION_LT.exec(s))) { return new iD.styleparser.Condition('<' ,o[1],o[2]); } - else if ((o=this.CONDITION_LE.exec(s))) { return new iD.styleparser.Condition('<=' ,o[1],o[2]); } - else if ((o=this.CONDITION_REGEX.exec(s))) { return new iD.styleparser.Condition('regex',o[1],o[2]); } - else if ((o=this.CONDITION_EQ.exec(s))) { return new iD.styleparser.Condition('eq' ,o[1],o[2]); } - return null; - }, - - parseCSSColor:function(colorStr) { - colorStr = colorStr.toLowerCase(); - if (this.CSSCOLORS[colorStr]) { - return this.CSSCOLORS[colorStr]; - } else { - var match = this.HEX.exec(colorStr); - if ( match ) { - if ( match[1].length == 3) { - // repeat digits. #abc => 0xaabbcc - return Number("0x"+match[1].charAt(0)+match[1].charAt(0)+ - match[1].charAt(1)+match[1].charAt(1)+ - match[1].charAt(2)+match[1].charAt(2)); - } else if ( match[1].length == 6) { - return Number("0x"+match[1]); - } else { - return Number("0x000000"); //as good as any - } - } - } - return 0; - }, - - // Regular expression tests and other constants - - WHITESPACE :/^\s+/, - COMMENT :/^\/\*.+?\*\/\s*/, - CLASS :/^([\.:]\w+)\s*/, - NOT_CLASS :/^!([\.:]\w+)\s*/, - ZOOM :/^\|\s*z([\d\-]+)\s*/i, - GROUP :/^,\s*/i, - CONDITION :/^\[(.+?)\]\s*/, - OBJECT :/^(\w+)\s*/, - DECLARATION :/^\{(.+?)\}\s*/, - SUBPART :/^::(\w+)\s*/, - UNKNOWN :/^(\S+)\s*/, - - ZOOM_MINMAX :/^(\d+)\-(\d+)$/, - ZOOM_MIN :/^(\d+)\-$/, - ZOOM_MAX :/^\-(\d+)$/, - ZOOM_SINGLE :/^(\d+)$/, - - CONDITION_TRUE :/^\s*([:\w]+)\s*=\s*yes\s*$/i, - CONDITION_FALSE :/^\s*([:\w]+)\s*=\s*no\s*$/i, - CONDITION_SET :/^\s*([:\w]+)\s*$/, - CONDITION_UNSET :/^\s*!([:\w]+)\s*$/, - CONDITION_EQ :/^\s*([:\w]+)\s*=\s*(.+)\s*$/, - CONDITION_NE :/^\s*([:\w]+)\s*!=\s*(.+)\s*$/, - CONDITION_GT :/^\s*([:\w]+)\s*>\s*(.+)\s*$/, - CONDITION_GE :/^\s*([:\w]+)\s*>=\s*(.+)\s*$/, - CONDITION_LT :/^\s*([:\w]+)\s*<\s*(.+)\s*$/, - CONDITION_LE :/^\s*([:\w]+)\s*<=\s*(.+)\s*$/, - CONDITION_REGEX :/^\s*([:\w]+)\s*=~\/\s*(.+)\/\s*$/, - - ASSIGNMENT_EVAL :/^\s*(\S+)\s*\:\s*eval\s*\(\s*'(.+?)'\s*\)\s*$/i, - ASSIGNMENT :/^\s*(\S+)\s*\:\s*(.+?)\s*$/, - SET_TAG_EVAL :/^\s*set\s+(\S+)\s*=\s*eval\s*\(\s*'(.+?)'\s*\)\s*$/i, - SET_TAG :/^\s*set\s+(\S+)\s*=\s*(.+?)\s*$/i, - SET_TAG_TRUE :/^\s*set\s+(\S+)\s*$/i, - EXIT :/^\s*exit\s*$/i, - - oZOOM: 2, - oGROUP: 3, - oCONDITION: 4, - oOBJECT: 5, - oDECLARATION: 6, - oSUBPART: 7, - - DASH: /\-/g, - COLOR: /color$/, - BOLD: /^bold$/i, - ITALIC: /^italic|oblique$/i, - UNDERLINE: /^underline$/i, - CAPS: /^uppercase$/i, - CENTER: /^center$/i, - FALSE: /^(no|false|0)$/i, - - HEX: /^#([0-9a-f]+)$/i, - - CSSCOLORS: { - aliceblue:0xf0f8ff, - antiquewhite:0xfaebd7, - aqua:0x00ffff, - aquamarine:0x7fffd4, - azure:0xf0ffff, - beige:0xf5f5dc, - bisque:0xffe4c4, - black:0x000000, - blanchedalmond:0xffebcd, - blue:0x0000ff, - blueviolet:0x8a2be2, - brown:0xa52a2a, - burlywood:0xdeb887, - cadetblue:0x5f9ea0, - chartreuse:0x7fff00, - chocolate:0xd2691e, - coral:0xff7f50, - cornflowerblue:0x6495ed, - cornsilk:0xfff8dc, - crimson:0xdc143c, - cyan:0x00ffff, - darkblue:0x00008b, - darkcyan:0x008b8b, - darkgoldenrod:0xb8860b, - darkgray:0xa9a9a9, - darkgreen:0x006400, - darkkhaki:0xbdb76b, - darkmagenta:0x8b008b, - darkolivegreen:0x556b2f, - darkorange:0xff8c00, - darkorchid:0x9932cc, - darkred:0x8b0000, - darksalmon:0xe9967a, - darkseagreen:0x8fbc8f, - darkslateblue:0x483d8b, - darkslategray:0x2f4f4f, - darkturquoise:0x00ced1, - darkviolet:0x9400d3, - deeppink:0xff1493, - deepskyblue:0x00bfff, - dimgray:0x696969, - dodgerblue:0x1e90ff, - firebrick:0xb22222, - floralwhite:0xfffaf0, - forestgreen:0x228b22, - fuchsia:0xff00ff, - gainsboro:0xdcdcdc, - ghostwhite:0xf8f8ff, - gold:0xffd700, - goldenrod:0xdaa520, - gray:0x808080, - green:0x008000, - greenyellow:0xadff2f, - honeydew:0xf0fff0, - hotpink:0xff69b4, - indianred:0xcd5c5c, - indigo:0x4b0082, - ivory:0xfffff0, - khaki:0xf0e68c, - lavender:0xe6e6fa, - lavenderblush:0xfff0f5, - lawngreen:0x7cfc00, - lemonchiffon:0xfffacd, - lightblue:0xadd8e6, - lightcoral:0xf08080, - lightcyan:0xe0ffff, - lightgoldenrodyellow:0xfafad2, - lightgrey:0xd3d3d3, - lightgreen:0x90ee90, - lightpink:0xffb6c1, - lightsalmon:0xffa07a, - lightseagreen:0x20b2aa, - lightskyblue:0x87cefa, - lightslategray:0x778899, - lightsteelblue:0xb0c4de, - lightyellow:0xffffe0, - lime:0x00ff00, - limegreen:0x32cd32, - linen:0xfaf0e6, - magenta:0xff00ff, - maroon:0x800000, - mediumaquamarine:0x66cdaa, - mediumblue:0x0000cd, - mediumorchid:0xba55d3, - mediumpurple:0x9370d8, - mediumseagreen:0x3cb371, - mediumslateblue:0x7b68ee, - mediumspringgreen:0x00fa9a, - mediumturquoise:0x48d1cc, - mediumvioletred:0xc71585, - midnightblue:0x191970, - mintcream:0xf5fffa, - mistyrose:0xffe4e1, - moccasin:0xffe4b5, - navajowhite:0xffdead, - navy:0x000080, - oldlace:0xfdf5e6, - olive:0x808000, - olivedrab:0x6b8e23, - orange:0xffa500, - orangered:0xff4500, - orchid:0xda70d6, - palegoldenrod:0xeee8aa, - palegreen:0x98fb98, - paleturquoise:0xafeeee, - palevioletred:0xd87093, - papayawhip:0xffefd5, - peachpuff:0xffdab9, - peru:0xcd853f, - pink:0xffc0cb, - plum:0xdda0dd, - powderblue:0xb0e0e6, - purple:0x800080, - red:0xff0000, - rosybrown:0xbc8f8f, - royalblue:0x4169e1, - saddlebrown:0x8b4513, - salmon:0xfa8072, - sandybrown:0xf4a460, - seagreen:0x2e8b57, - seashell:0xfff5ee, - sienna:0xa0522d, - silver:0xc0c0c0, - skyblue:0x87ceeb, - slateblue:0x6a5acd, - slategray:0x708090, - snow:0xfffafa, - springgreen:0x00ff7f, - steelblue:0x4682b4, - tan:0xd2b48c, - teal:0x008080, - thistle:0xd8bfd8, - tomato:0xff6347, - turquoise:0x40e0d0, - violet:0xee82ee, - wheat:0xf5deb3, - white:0xffffff, - whitesmoke:0xf5f5f5, - yellow:0xffff00, - yellowgreen:0x9acd32 }, - -}); - -// ---------------------------------------------------------------------- -// End of module -}); diff --git a/js/iD/styleparser/Style.js b/js/iD/styleparser/Style.js deleted file mode 100755 index 4bde63d76..000000000 --- a/js/iD/styleparser/Style.js +++ /dev/null @@ -1,240 +0,0 @@ -// iD/styleparser/Style.js - -define(['dojo/_base/declare','dojo/_base/array'], function(declare,array){ - -// ---------------------------------------------------------------------- -// Style base class -// fillStyler not done for text yet - -declare("iD.styleparser.Style", null, { - merged: false, - edited: false, - sublayer: 5, - interactive: true, - properties: [], - styleType: 'Style', - evals: null, - - constructor: function() { - // summary: Base class for a set of painting attributes, into which the CSS declaration is parsed. - this.evals = {}; - }, - - drawn: function() { - return false; - }, - - has: function(k) { - return this.properties.indexOf(k)>-1; - }, - - mergeWith: function(additional) { - for (var prop in this.properties) { - if (additional[prop]) { - this[prop]=additional[prop]; - } - } - this.merged=true; - }, - - setPropertyFromString: function(k,v,isEval) { - this.edited=true; - if (isEval) { this.evals[k]=v; return; } - - if (typeof(this[k])=='boolean') { - v=Boolean(v); - } else if (typeof(this[k])=='number') { - v=Number(v); - } else if (this[k] && this[k].constructor==Array) { - v = v.split(',').map(function(a) { return Number(a); }); - } - this[k]=v; - return true; - }, - - runEvals: function(tags) { - for (var k in this.evals) { - this.setPropertyFromString(k,eval("with (tags) {"+this.evals[k]+"}"),false); - } - }, - - dojoColor: function(rgb,a) { - var b=rgb % 256; - var g=(rgb-b) % 65536; - var r=(rgb-b-g) % 16777216; - return new dojo.Color([r/65536,g/256,b,a]); - }, - - toString: function() { - var str = ''; - for (var k in this.properties) { - if (this.hasOwnProperty(k)) { str+=k+"="+this[k]+"; "; } - } - return str; - } -}); - - -// ---------------------------------------------------------------------- -// InstructionStyle class - -declare("iD.styleparser.InstructionStyle", [iD.styleparser.Style], { - set_tags: null, - breaker: false, - styleType: 'InstructionStyle', - addSetTag: function(k,v) { - this.edited=true; - if (!this.set_tags) this.set_tags={}; - this.set_tags[k]=v; - } -}); - -// ---------------------------------------------------------------------- -// PointStyle class - -declare("iD.styleparser.PointStyle", [iD.styleparser.Style], { - properties: ['icon_image','icon_width','icon_height','rotation'], - icon_image: null, - icon_width: 0, - icon_height: NaN, - rotation: NaN, - styleType: 'PointStyle', - drawn:function() { - return (this.icon_image !== null); - }, - maxwidth:function() { - return this.evals.icon_width ? 0 : this.icon_width; - } -}); - -// ---------------------------------------------------------------------- -// ShapeStyle class - -declare("iD.styleparser.ShapeStyle", [iD.styleparser.Style], { - properties: ['width','color','opacity','dashes','linecap','linejoin','line_style', - 'fill_image','fill_color','fill_opacity','casing_width','casing_color','casing_opacity','casing_dashes','layer'], - - width:0, color:NaN, opacity:NaN, dashes:[], - linecap:null, linejoin:null, line_style:null, - fill_image:null, fill_color:NaN, fill_opacity:NaN, - casing_width:NaN, casing_color:NaN, casing_opacity:NaN, casing_dashes:[], - - layer:NaN, // optional layer override (usually set by OSM tag) - styleType: 'ShapeStyle', - - drawn:function() { - return (this.fill_image || !isNaN(this.fill_color) || this.width || this.casing_width); - }, - maxwidth:function() { - // If width is set by an eval, then we can't use it to calculate maxwidth, or it'll just grow on each invocation... - if (this.evals.width || this.evals.casing_width) { return 0; } - return (this.width + (this.casing_width ? this.casing_width*2 : 0)); - }, - strokeStyler:function() { - var cap,join; - switch (this.linecap ) { case 'round': cap ='round'; break; case 'square': cap='square'; break; default: cap ='butt' ; break; } - switch (this.linejoin) { case 'bevel': join='bevel'; break; case 'miter' : join=4 ; break; default: join='round'; break; } - return { - color: this.dojoColor(this.color ? this.color : 0, this.opacity ? this.opacity : 1), - style: 'Solid', // needs to parse dashes - width: this.width, - cap: cap, - join: join - }; - }, - shapeStrokeStyler:function() { - if (isNaN(this.casing_color)) { return { width:0 }; } - return { - color: this.dojoColor(this.casing_color, this.casing_opacity ? this.casing_opacity : 1), - width: this.casing_width ? this.casing_width : 1 - }; - }, - shapeFillStyler:function() { - if (isNaN(this.color)) { return null; } - return this.dojoColor(this.color, this.opacity ? this.opacity : 1); - }, - fillStyler:function() { - return this.dojoColor(this.fill_color, this.fill_opacity ? this.fill_opacity : 1); - }, - casingStyler:function() { - var cap,join; - switch (this.linecap ) { case 'round': cap ='round'; break; case 'square': cap='square'; break; default: cap ='butt' ; break; } - switch (this.linejoin) { case 'bevel': join='bevel'; break; case 'miter' : join=4 ; break; default: join='round'; break; } - return { - color: this.dojoColor(this.casing_color ? this.casing_color : 0, this.casing_opacity ? this.casing_opacity : 1), - width: this.width+this.casing_width*2, - style: 'Solid', - cap: cap, - join: join - }; - } -}); - -// ---------------------------------------------------------------------- -// TextStyle class - -declare("iD.styleparser.TextStyle", [iD.styleparser.Style], { - - properties: ['font_family','font_bold','font_italic','font_caps','font_underline','font_size', - 'text_color','text_offset','max_width', - 'text','text_halo_color','text_halo_radius','text_center', - 'letter_spacing'], - - font_family: null, - font_bold: false, - font_italic: false, - font_underline: false, - font_caps: false, - font_size: NaN, - text_color: NaN, - text_offset: NaN, - max_width: NaN, - text: null, - text_halo_color: NaN, - text_halo_radius: 0, - text_center: true, - letter_spacing: 0, - styleType: 'TextStyle', - - drawn: function() { - return (this.text !== null); - }, - fontStyler:function() { - return { - family: this.font_family ? this.font_family : 'Arial', - size: this.font_size ? this.font_size*2 : '10px' , - weight: this.font_bold ? 'bold' : 'normal', - style: this.font_italic ? 'italic' : 'normal' - }; - }, - textStyler:function(_text) { - return { - decoration: this.font_underline ? 'underline' : 'none', - align: 'middle', - text: _text - }; - }, - fillStyler:function() { - // not implemented yet - return this.dojoColor(0,1); - } - // getTextFormat, getHaloFilter, writeNameLabel -}); - -// ---------------------------------------------------------------------- -// ShieldStyle class - -declare("iD.styleparser.ShieldStyle", [iD.styleparser.Style], { - properties: ['shield_image','shield_width','shield_height'], - shield_image: null, - shield_width: NaN, - shield_height: NaN, - styleType: 'ShieldStyle', - drawn:function() { - return (shield_image !== null); - } -}); - -// ---------------------------------------------------------------------- -// End of module -}); diff --git a/js/iD/styleparser/StyleChooser.js b/js/iD/styleparser/StyleChooser.js deleted file mode 100755 index 8b856f2b8..000000000 --- a/js/iD/styleparser/StyleChooser.js +++ /dev/null @@ -1,87 +0,0 @@ -// iD/styleparser/StyleChooser.js - -define(['dojo/_base/declare','dojo/_base/lang','iD/styleparser/RuleChain'], function(declare,lang){ - -declare("iD.styleparser.StyleChooser", null, { - - // UpdateStyles doesn't support image-widths yet - // or setting maxwidth/_width - - ruleChains:[], // array of RuleChains (each one an array of Rules) - styles:[], // array of ShapeStyle/ShieldStyle/TextStyle/PointStyle - zoomSpecific:false, // are any of the rules zoom-specific? - - rcpos:0, - stylepos:0, - - constructor:function() { - // summary: A combination of the selectors (ruleChains) and declaration (styles). - // For example, way[highway=footway] node[barrier=gate] { icon: gate.png; } is one StyleChooser. - this.ruleChains=[new iD.styleparser.RuleChain()]; - this.styles=[]; - }, - - currentChain:function() { - return this.ruleChains[this.ruleChains.length-1]; - }, - - newRuleChain:function() { - // summary: Starts a new ruleChain in this.ruleChains. - if (this.ruleChains[this.ruleChains.length-1].length()>0) { - this.ruleChains.push(new iD.styleparser.RuleChain()); - } - }, - - addStyles:function(a) { - this.styles=this.styles.concat(a); - }, - - updateStyles:function(entity, tags, sl, zoom) { - if (this.zoomSpecific) { sl.validAt=zoom; } - - // Are any of the ruleChains fulfilled? - var w; - for (var i in this.ruleChains) { - var c=this.ruleChains[i]; - if (c.test(-1, entity, tags, zoom)) { - sl.addSubpart(c.subpart); - - // Update StyleList - for (var j in this.styles) { - var r=this.styles[j]; - var a; - switch (r.styleType) { - - case 'ShapeStyle' : sl.maxwidth=Math.max(sl.maxwidth,r.maxwidth()); - a=sl.shapeStyles; break; - case 'ShieldStyle': a=sl.shieldStyles; break; - case 'TextStyle' : a=sl.textStyles; break; - case 'PointStyle' : sl.maxwidth=Math.max(sl.maxwidth,r.maxwidth()); - a=sl.pointStyles; break; - case 'InstructionStyle': - if (InstructionStyle(r).breaker) { return; } - for (var k in InstructionStyle(r).set_tags) { tags[k]=InstructionStyle(r).set_tags[k]; } - break; - } - if (r.drawn) { tags[':drawn']='yes'; } - tags._width = sl.maxwidth; - - r.runEvals(tags); - if (a[c.subpart]) { - // If there's already a style on this sublayer, then merge them - // (making a deep copy if necessary to avoid altering the root style) - if (!a[c.subpart].merged) { a[c.subpart]=lang.clone(a[c.subpart]); } - a[c.subpart].mergeWith(r); - } else { - // Otherwise, just assign it - a[c.subpart]=r; - } - } - } - } - } -}); - -// ---------------------------------------------------------------------- -// End of module -}); diff --git a/js/iD/styleparser/StyleList.js b/js/iD/styleparser/StyleList.js deleted file mode 100755 index e9bc441b7..000000000 --- a/js/iD/styleparser/StyleList.js +++ /dev/null @@ -1,81 +0,0 @@ -// iD/styleparser/StyleList.js - -define(['dojo/_base/declare'], function(declare){ - -// ---------------------------------------------------------------------- -// StyleList class - - -declare("iD.styleparser.StyleList", null, { - - shapeStyles: {}, - textStyles: {}, - pointStyles: {}, - shieldStyles: {}, - maxwidth: 0, - subparts: [], // List of subparts used in this StyleList - validAt: -1, // Zoom level this is valid at (or -1 at all levels - saves recomputing) - - constructor:function() { - // summary: A StyleList object is the full list of all styles applied to - // a drawn entity (i.e. node/way). Each array element applies to that - // sublayer (z-index). If there is no element, nothing is drawn on that sublayer. - // StyleLists are created by StyleChooser.getStyles. - this.shapeStyles={}; - this.textStyles={}; - this.pointStyles={}; - this.shieldStyles={}; - this.subparts=[]; - }, - - hasStyles:function() { - // summary: Does this StyleList contain any styles? - return (this.hasShapeStyles() || this.hasTextStyles() || this.hasPointStyles() || this.hasShieldStyles()); - }, - - hasFills:function() { - // summary: Does this StyleList contain any styles with a fill? - for (var s in this.shapeStyles) { - if (!isNaN(this.shapeStyles(s).fill_color) || this.shapeStyles(s).fill_image) return true; - } - return false; - }, - - layerOverride:function() { - // summary: If this StyleList manually forces an OSM layer, return it, otherwise null. - for (var s in this.shapeStyles) { - if (!isNaN(this.shapeStyles[s].layer)) return this.shapeStyles[s].layer; - } - return NaN; - }, - - addSubpart:function(s) { - // summary: Record that a subpart is used in this StyleList. - if (this.subparts.indexOf(s)==-1) { this.subparts.push(s); } - }, - - isValidAt:function(zoom) { - // summary: Is this StyleList valid at a given zoom? - return (this.validAt==-1 || this.validAt==zoom); - }, - - toString:function() { - // summary: Summarise StyleList as String - for debugging - var str = ''; - var k; - for (k in this.shapeStyles ) { str+="- SS "+k+"="+this.shapeStyles[k]+"\n"; } - for (k in this.textStyles ) { str+="- TS "+k+"="+this.textStyles[k]+"\n"; } - for (k in this.pointStyles ) { str+="- PS "+k+"="+this.pointStyles[k]+"\n"; } - for (k in this.shieldStyles) { str+="- sS "+k+"="+this.shieldStyles[k]+"\n"; } - return str; - }, - - hasShapeStyles:function() { for (var a in shapeStyles ) { return true; } return false; }, - hasTextStyles:function() { for (var a in textStyles ) { return true; } return false; }, - hasPointStyles:function() { for (var a in pointStyles ) { return true; } return false; }, - hasShieldStyles:function() { for (var a in shieldStyles) { return true; } return false; } -}); - -// ---------------------------------------------------------------------- -// End of module -}); diff --git a/js/lib/d3.v2.min.js b/js/lib/d3.v2.min.js new file mode 100644 index 000000000..0b4ea58fd --- /dev/null +++ b/js/lib/d3.v2.min.js @@ -0,0 +1,4 @@ +(function(){function e(e,t){try{for(var n in t)Object.defineProperty(e.prototype,n,{value:t[n],enumerable:!1})}catch(r){e.prototype=t}}function t(e){var t=-1,n=e.length,r=[];while(++t=0?e.substring(t):(t=e.length,""),r=[];while(t>0)r.push(e.substring(t-=3,t+3));return r.reverse().join(",")+n}function b(e,t){var n=Math.pow(10,Math.abs(8-t)*3);return{scale:t>8?function(e){return e/n}:function(e){return e*n},symbol:e}}function w(e){return function(t){return t<=0?0:t>=1?1:e(t)}}function E(e){return function(t){return 1-e(1-t)}}function S(e){return function(t){return.5*(t<.5?e(2*t):2-e(2-2*t))}}function x(e){return e}function T(e){return function(t){return Math.pow(t,e)}}function N(e){return 1-Math.cos(e*Math.PI/2)}function C(e){return Math.pow(2,10*(e-1))}function k(e){return 1-Math.sqrt(1-e*e)}function L(e,t){var n;return arguments.length<2&&(t=.45),arguments.length<1?(e=1,n=t/4):n=t/(2*Math.PI)*Math.asin(1/e),function(r){return 1+e*Math.pow(2,10*-r)*Math.sin((r-n)*2*Math.PI/t)}}function A(e){return e||(e=1.70158),function(t){return t*t*((e+1)*t-e)}}function O(e){return e<1/2.75?7.5625*e*e:e<2/2.75?7.5625*(e-=1.5/2.75)*e+.75:e<2.5/2.75?7.5625*(e-=2.25/2.75)*e+.9375:7.5625*(e-=2.625/2.75)*e+.984375}function M(){d3.event.stopPropagation(),d3.event.preventDefault()}function _(){var e=d3.event,t;while(t=e.sourceEvent)e=t;return e}function D(e){var t=new d,n=0,r=arguments.length;while(++n360?e-=360:e<0&&(e+=360),e<60?s+(o-s)*e/60:e<180?o:e<240?s+(o-s)*(240-e)/60:s}function i(e){return Math.round(r(e)*255)}var s,o;return e%=360,e<0&&(e+=360),t=t<0?0:t>1?1:t,n=n<0?0:n>1?1:n,o=n<=.5?n*(1+t):n+t-n*t,s=2*n-o,U(i(e+120),i(e),i(e-120))}function Z(e,t,n){return new et(e,t,n)}function et(e,t,n){this.h=e,this.c=t,this.l=n}function tt(e,t,n){return nt(n,Math.cos(e*=Math.PI/180)*t,Math.sin(e)*t)}function nt(e,t,n){return new rt(e,t,n)}function rt(e,t,n){this.l=e,this.a=t,this.b=n}function it(e,t,n){var r=(e+16)/116,i=r+t/500,s=r-n/200;return i=ot(i)*ys,r=ot(r)*bs,s=ot(s)*ws,U(at(3.2404542*i-1.5371385*r-.4985314*s),at(-0.969266*i+1.8760108*r+.041556*s),at(.0556434*i-.2040259*r+1.0572252*s))}function st(e,t,n){return Z(Math.atan2(n,t)/Math.PI*180,Math.sqrt(t*t+n*n),e)}function ot(e){return e>.206893034?e*e*e:(e-4/29)/7.787037}function ut(e){return e>.008856?Math.pow(e,1/3):7.787037*e+4/29}function at(e){return Math.round(255*(e<=.00304?12.92*e:1.055*Math.pow(e,1/2.4)-.055))}function ft(e){return Qi(e,ks),e}function lt(e){return function(){return Ss(e,this)}}function ct(e){return function(){return xs(e,this)}}function ht(e,t){function n(){this.removeAttribute(e)}function r(){this.removeAttributeNS(e.space,e.local)}function i(){this.setAttribute(e,t)}function s(){this.setAttributeNS(e.space,e.local,t)}function o(){var n=t.apply(this,arguments);n==null?this.removeAttribute(e):this.setAttribute(e,n)}function u(){var n=t.apply(this,arguments);n==null?this.removeAttributeNS(e.space,e.local):this.setAttributeNS(e.space,e.local,n)}return e=d3.ns.qualify(e),t==null?e.local?r:n:typeof t=="function"?e.local?u:o:e.local?s:i}function pt(e){return new RegExp("(?:^|\\s+)"+d3.requote(e)+"(?:\\s+|$)","g")}function dt(e,t){function n(){var n=-1;while(++n0&&(e=e.substring(0,o)),t?i:r}function St(e,t){for(var n=0,r=e.length;nt?c():(v.active=t,i.forEach(function(t,n){(n=n.call(e,m,u))&&h.push(n)}),s.start.call(e,m,u),l(r)||d3.timer(l,0,n),1)}function l(n){if(v.active!==t)return c();var r=(n-p)/d,i=o(r),a=h.length;while(a>0)h[--a].call(e,i);if(r>=1)return c(),_s=t,s.end.call(e,m,u),_s=0,1}function c(){return--v.count||delete e.__transition__,1}var h=[],p=e.delay,d=e.duration,v=(e=e.node).__transition__||(e.__transition__={active:0,count:0}),m=e.__data__;++v.count,p<=r?f(r):d3.timer(f,p,n)})},0,n),e}function Nt(e){var t=_s,n=Fs,r=Bs,i=js;return _s=this.id,Fs=this.ease(),St(this,function(t,n,r){Bs=t.delay,js=t.duration,e.call(t=t.node,t.__data__,n,r)}),_s=t,Fs=n,Bs=r,js=i,this}function Ct(e,t,n){return n!=""&&Is}function kt(e,t){return d3.tween(e,F(t))}function Lt(){var e,t=Date.now(),n=Us;while(n)e=t-n.then,e>=n.delay&&(n.flush=n.callback(e)),n=n.next;var r=At()-t;r>24?(isFinite(r)&&(clearTimeout(Ws),Ws=setTimeout(Lt,r)),zs=0):(zs=1,Xs(Lt))}function At(){var e=null,t=Us,n=Infinity;while(t)t.flush?(delete Rs[t.callback.id],t=e?e.next=t.next:Us=t.next):(n=Math.min(n,t.then+t.delay),t=(e=t).next);return n}function Ot(e,t){var n=e.ownerSVGElement||e;if(n.createSVGPoint){var r=n.createSVGPoint();if(Vs<0&&(window.scrollX||window.scrollY)){n=d3.select(document.body).append("svg").style("position","absolute").style("top",0).style("left",0);var i=n[0][0].getScreenCTM();Vs=!i.f&&!i.e,n.remove()}return Vs?(r.x=t.pageX,r.y=t.pageY):(r.x=t.clientX,r.y=t.clientY),r=r.matrixTransform(e.getScreenCTM().inverse()),[r.x,r.y]}var s=e.getBoundingClientRect();return[t.clientX-s.left-e.clientLeft,t.clientY-s.top-e.clientTop]}function Mt(){}function _t(e){var t=e[0],n=e[e.length-1];return t2?zt:Ut,a=r?q:I;return o=i(e,t,a,n),u=i(t,e,a,d3.interpolate),s}function s(e){return o(e)}var o,u;return s.invert=function(e){return u(e)},s.domain=function(t){return arguments.length?(e=t.map(Number),i()):e},s.range=function(e){return arguments.length?(t=e,i()):t},s.rangeRound=function(e){return s.range(e).interpolate(d3.interpolateRound)},s.clamp=function(e){return arguments.length?(r=e,i()):r},s.interpolate=function(e){return arguments.length?(n=e,i()):n},s.ticks=function(t){return qt(e,t)},s.tickFormat=function(t){return Rt(e,t)},s.nice=function(){return Pt(e,Ft),i()},s.copy=function(){return Bt(e,t,n,r)},i()}function jt(e,t){return d3.rebind(e,t,"range","rangeRound","interpolate","clamp")}function Ft(e){return e=Math.pow(10,Math.round(Math.log(e)/Math.LN10)-1),e&&{floor:function(t){return Math.floor(t/e)*e},ceil:function(t){return Math.ceil(t/e)*e}}}function It(e,t){var n=_t(e),r=n[1]-n[0],i=Math.pow(10,Math.floor(Math.log(r/t)/Math.LN10)),s=t/r*i;return s<=.15?i*=10:s<=.35?i*=5:s<=.75&&(i*=2),n[0]=Math.ceil(n[0]/i)*i,n[1]=Math.floor(n[1]/i)*i+i*.5,n[2]=i,n}function qt(e,t){return d3.range.apply(d3,It(e,t))}function Rt(e,t){return d3.format(",."+Math.max(0,-Math.floor(Math.log(It(e,t)[2])/Math.LN10+.01))+"f")}function Ut(e,t,n,r){var i=n(e[0],e[1]),s=r(t[0],t[1]);return function(e){return s(i(e))}}function zt(e,t,n,r){var i=[],s=[],o=0,u=Math.min(e.length,t.length)-1;e[u]0;f--)i.push(r(s)*f)}else{for(;sa;o--);i=i.slice(s,o)}return i},n.tickFormat=function(e,i){arguments.length<2&&(i=$s);if(arguments.length<1)return i;var s=Math.max(.1,e/n.ticks().length),o=t===Vt?(u=-1e-12,Math.floor):(u=1e-12,Math.ceil),u;return function(e){return e/r(o(t(e)+u))<=s?i(e):""}},n.copy=function(){return Wt(e.copy(),t)},jt(n,e)}function Xt(e){return Math.log(e<0?0:e)/Math.LN10}function Vt(e){return-Math.log(e>0?0:-e)/Math.LN10}function $t(e,t){function n(t){return e(r(t))}var r=Jt(t),i=Jt(1/t);return n.invert=function(t){return i(e.invert(t))},n.domain=function(t){return arguments.length?(e.domain(t.map(r)),n):e.domain().map(i)},n.ticks=function(e){return qt(n.domain(),e)},n.tickFormat=function(e){return Rt(n.domain(),e)},n.nice=function(){return n.domain(Pt(n.domain(),Ft))},n.exponent=function(e){if(!arguments.length)return t;var s=n.domain();return r=Jt(t=e),i=Jt(1/t),n.domain(s)},n.copy=function(){return $t(e.copy(),t)},jt(n,e)}function Jt(e){return function(t){return t<0?-Math.pow(-t,e):Math.pow(t,e)}}function Kt(e,t){function n(t){return o[((s.get(t)||s.set(t,e.push(t)))-1)%o.length]}function i(t,n){return d3.range(e.length).map(function(e){return t+n*e})}var s,o,u;return n.domain=function(i){if(!arguments.length)return e;e=[],s=new r;var o=-1,u=i.length,a;while(++o1){u=t[1],s=e[a],a++,r+="C"+(i[0]+o[0])+","+(i[1]+o[1])+","+(s[0]-u[0])+","+(s[1]-u[1])+","+s[0]+","+s[1];for(var f=2;f9&&(s=n*3/Math.sqrt(s),o[u]=s*r,o[u+1]=s*i));u=-1;while(++u<=a)s=(e[Math.min(a,u+1)][0]-e[Math.max(0,u-1)][0])/(6*(1+o[u]*o[u])),t.push([s||0,o[u]*s||0]);return t}function Cn(e){return e.length<3?an(e):e[0]+vn(e,Nn(e))}function kn(e){var t,n=-1,r=e.length,i,s;while(++n1){var r=_t(e.domain()),i,s=-1,o=t.length,u=(t[1]-t[0])/++n,a,f;while(++s0;)(f=+t[s]-a*u)>=r[0]&&i.push(f);for(--s,a=0;++ar&&(n=t,r=i);return n}function sr(e){return e.reduce(or,0)}function or(e,t){return e+t[1]}function ur(e,t){return ar(e,Math.ceil(Math.log(t.length)/Math.LN2+1))}function ar(e,t){var n=-1,r=+e[0],i=(e[1]-r)/t,s=[];while(++n<=t)s[n]=i*n+r;return s}function fr(e){return[d3.min(e),d3.max(e)]}function lr(e,t){return d3.rebind(e,t,"sort","children","value"),e.links=dr,e.nodes=function(t){return vo=!0,(e.nodes=e)(t)},e}function cr(e){return e.children}function hr(e){return e.value}function pr(e,t){return t.value-e.value}function dr(e){return d3.merge(e.map(function(e){return(e.children||[]).map(function(t){return{source:e,target:t}})}))}function vr(e,t){return e.value-t.value}function mr(e,t){var n=e._pack_next;e._pack_next=t,t._pack_prev=e,t._pack_next=n,n._pack_prev=t}function gr(e,t){e._pack_next=t,t._pack_prev=e}function yr(e,t){var n=t.x-e.x,r=t.y-e.y,i=e.r+t.r;return i*i-n*n-r*r>.001}function br(e){function t(e){r=Math.min(e.x-e.r,r),i=Math.max(e.x+e.r,i),s=Math.min(e.y-e.r,s),o=Math.max(e.y+e.r,o)}if(!(n=e.children)||!(p=n.length))return;var n,r=Infinity,i=-Infinity,s=Infinity,o=-Infinity,u,a,f,l,c,h,p;n.forEach(wr),u=n[0],u.x=-u.r,u.y=0,t(u);if(p>1){a=n[1],a.x=a.r,a.y=0,t(a);if(p>2){f=n[2],xr(u,a,f),t(f),mr(u,f),u._pack_prev=f,mr(f,a),a=u._pack_next;for(l=3;l0&&(e=r)}return e}function _r(e,t){return e.x-t.x}function Dr(e,t){return t.x-e.x}function Pr(e,t){return e.depth-t.depth}function Hr(e,t){function n(e,r){var i=e.children;if(i&&(a=i.length)){var s,o=null,u=-1,a;while(++u=0)s=r[i]._tree,s.prelim+=t,s.mod+=t,t+=s.shift+(n+=s.change)}function jr(e,t,n){e=e._tree,t=t._tree;var r=n/(t.number-e.number);e.change+=r,t.change-=r,t.shift+=n,t.prelim+=n,t.mod+=n}function Fr(e,t,n){return e._tree.ancestor.parent==t.parent?e._tree.ancestor:n}function Ir(e){return{x:e.x,y:e.y,dx:e.dx,dy:e.dy}}function qr(e,t){var n=e.x+t[3],r=e.y+t[0],i=e.dx-t[1]-t[3],s=e.dy-t[0]-t[2];return i<0&&(n+=i/2,i=0),s<0&&(r+=s/2,s=0),{x:n,y:r,dx:i,dy:s}}function Rr(e,t){function n(e,r){d3.text(e,t,function(e){r(e&&n.parse(e))})}function r(t){return t.map(i).join(e)}function i(e){return o.test(e)?'"'+e.replace(/\"/g,'""')+'"':e}var s=new RegExp("\r\n|["+e+"\r\n]","g"),o=new RegExp('["'+e+"\n]"),u=e.charCodeAt(0);return n.parse=function(e){var t;return n.parseRows(e,function(e,n){if(n){var r={},i=-1,s=t.length;while(++i=e.length)return i;if(l)return l=!1,r;var t=s.lastIndex;if(e.charCodeAt(t)===34){var n=t;while(n++0}function si(e,t,n){return(n[0]-t[0])*(e[1]-t[1])<(n[1]-t[1])*(e[0]-t[0])}function oi(e,t,n,r){var i=e[0],s=t[0],o=n[0],u=r[0],a=e[1],f=t[1],l=n[1],c=r[1],h=i-o,p=s-i,d=u-o,v=a-l,m=f-a,g=c-l,y=(d*v-g*h)/(g*p-d*m);return[i+y*p,a+y*m]}function ui(e,t){var n={list:e.map(function(e,t){return{index:t,x:e[0],y:e[1]}}).sort(function(e,t){return e.yt.y?1:e.xt.x?1:0}),bottomSite:null},r={list:[],leftEnd:null,rightEnd:null,init:function(){r.leftEnd=r.createHalfEdge(null,"l"),r.rightEnd=r.createHalfEdge(null,"l"),r.leftEnd.r=r.rightEnd,r.rightEnd.l=r.leftEnd,r.list.unshift(r.leftEnd,r.rightEnd)},createHalfEdge:function(e,t){return{edge:e,side:t,vertex:null,l:null,r:null}},insert:function(e,t){t.l=e,t.r=e.r,e.r.l=t,e.r=t},leftBound:function(e){var t=r.leftEnd;do t=t.r;while(t!=r.rightEnd&&i.rightOf(t,e));return t=t.l,t},del:function(e){e.l.r=e.r,e.r.l=e.l,e.edge=null},right:function(e){return e.r},left:function(e){return e.l},leftRegion:function(e){return e.edge==null?n.bottomSite:e.edge.region[e.side]},rightRegion:function(e){return e.edge==null?n.bottomSite:e.edge.region[wo[e.side]]}},i={bisect:function(e,t){var n={region:{l:e,r:t},ep:{l:null,r:null}},r=t.x-e.x,i=t.y-e.y,s=r>0?r:-r,o=i>0?i:-i;return n.c=e.x*r+e.y*i+(r*r+i*i)*.5,s>o?(n.a=1,n.b=i/r,n.c/=r):(n.b=1,n.a=r/i,n.c/=i),n},intersect:function(e,t){var n=e.edge,r=t.edge;if(!n||!r||n.region.r==r.region.r)return null;var i=n.a*r.b-n.b*r.a;if(Math.abs(i)<1e-10)return null;var s=(n.c*r.b-r.c*n.b)/i,o=(r.c*n.a-n.c*r.a)/i,u=n.region.r,a=r.region.r,f,l;u.y=l.region.r.x;return c&&f.side==="l"||!c&&f.side==="r"?null:{x:s,y:o}},rightOf:function(e,t){var n=e.edge,r=n.region.r,i=t.x>r.x;if(i&&e.side==="l")return 1;if(!i&&e.side==="r")return 0;if(n.a===1){var s=t.y-r.y,o=t.x-r.x,u=0,a=0;!i&&n.b<0||i&&n.b>=0?a=u=s>=n.b*o:(a=t.x+t.y*n.b>n.c,n.b<0&&(a=!a),a||(u=1));if(!u){var f=r.x-n.region.l.x;a=n.b*(o*o-s*s)h*h+p*p}return e.side==="l"?a:!a},endPoint:function(e,n,r){e.ep[n]=r;if(!e.ep[wo[n]])return;t(e)},distance:function(e,t){var n=e.x-t.x,r=e.y-t.y;return Math.sqrt(n*n+r*r)}},s={list:[],insert:function(e,t,n){e.vertex=t,e.ystar=t.y+n;for(var r=0,i=s.list,o=i.length;ru.ystar||e.ystar==u.ystar&&t.x>u.vertex.x)continue;break}i.splice(r,0,e)},del:function(e){for(var t=0,n=s.list,r=n.length;td.y&&(v=p,p=d,d=v,b="r"),y=i.bisect(p,d),h=r.createHalfEdge(y,b),r.insert(l,h),i.endPoint(y,wo[b],g),m=i.intersect(l,h),m&&(s.del(l),s.insert(l,m,i.distance(m,p))),m=i.intersect(h,c),m&&s.insert(h,m,i.distance(m,p))}}for(a=r.right(r.leftEnd);a!=r.rightEnd;a=r.right(a))t(a.edge)}function ai(){return{leaf:!0,nodes:[],point:null}}function fi(e,t,n,r,i,s){if(!e(t,n,r,i,s)){var o=(n+i)*.5,u=(r+s)*.5,a=t.nodes;a[0]&&fi(e,a[0],n,r,o,u),a[1]&&fi(e,a[1],o,r,i,u),a[2]&&fi(e,a[2],n,u,o,s),a[3]&&fi(e,a[3],o,u,i,s)}}function li(e){return{x:e[0],y:e[1]}}function ci(){this._=new Date(arguments.length>1?Date.UTC.apply(this,arguments):arguments[0])}function hi(e){return e.substring(0,3)}function pi(e,t,n,r){var i,s,o=0,u=t.length,a=n.length;while(o=a)return-1;i=t.charCodeAt(o++);if(i==37){s=Uo[t.charAt(o++)];if(!s||(r=s(e,n,r))<0)return-1}else if(i!=n.charCodeAt(r++))return-1}return r}function di(e){return new RegExp("^(?:"+e.map(d3.requote).join("|")+")","i")}function vi(e){var t=new r,n=-1,i=e.length;while(++n68?1900:2e3)}function Ci(e,t,n){zo.lastIndex=0;var r=zo.exec(t.substring(n,n+2));return r?(e.m=r[0]-1,n+=r[0].length):-1}function ki(e,t,n){zo.lastIndex=0;var r=zo.exec(t.substring(n,n+2));return r?(e.d=+r[0],n+=r[0].length):-1}function Li(e,t,n){zo.lastIndex=0;var r=zo.exec(t.substring(n,n+2));return r?(e.H=+r[0],n+=r[0].length):-1}function Ai(e,t,n){zo.lastIndex=0;var r=zo.exec(t.substring(n,n+2));return r?(e.M=+r[0],n+=r[0].length):-1}function Oi(e,t,n){zo.lastIndex=0;var r=zo.exec(t.substring(n,n+2));return r?(e.S=+r[0],n+=r[0].length):-1}function Mi(e,t,n){zo.lastIndex=0;var r=zo.exec(t.substring(n,n+3));return r?(e.L=+r[0],n+=r[0].length):-1}function _i(e,t,n){var r=Wo.get(t.substring(n,n+=2).toLowerCase());return r==null?-1:(e.p=r,n)}function Di(e){var t=e.getTimezoneOffset(),n=t>0?"-":"+",r=~~(Math.abs(t)/60),i=Math.abs(t)%60;return n+Mo(r)+Mo(i)}function Pi(e){return e.toISOString()}function Hi(e,t,n){function r(t){var n=e(t),r=s(n,1);return t-n1)while(ot?1:e>=t?0:NaN},d3.descending=function(e,t){return te?1:t>=e?0:NaN},d3.mean=function(e,t){var n=e.length,r,i=0,s=-1,o=0;if(arguments.length===1)while(++s1&&(e=e.map(t)),e=e.filter(f),e.length?d3.quantile(e.sort(d3.ascending),.5):undefined},d3.min=function(e,t){var n=-1,r=e.length,i,s;if(arguments.length===1){while(++ns&&(i=s)}else{while(++ns&&(i=s)}return i},d3.max=function(e,t){var n=-1,r=e.length,i,s;if(arguments.length===1){while(++ni&&(i=s)}else{while(++ni&&(i=s)}return i},d3.extent=function(e,t){var n=-1,r=e.length,i,s,o;if(arguments.length===1){while(++ns&&(i=s),os&&(i=s),o1);return e+t*n*Math.sqrt(-2*Math.log(i)/i)}},logNormal:function(e,t){var n=arguments.length;n<2&&(t=1),n<1&&(e=0);var r=d3.random.normal();return function(){return Math.exp(e+t*r())}},irwinHall:function(e){return function(){for(var t=0,n=0;n>>1;e.call(t,t[s],s)>>1;n0&&(i=s);return i},d3.last=function(e,t){var n=0,r=e.length,i=e[0],s;arguments.length===1&&(t=d3.ascending);while(++n=i.length)return u?u.call(n,t):o?t.sort(o):t;var a=-1,f=t.length,l=i[s++],c,h,p=new r,d,v={};while(++a=i.length)return e;var r=[],o=s[n++],u;for(u in e)r.push({key:u,values:t(e[u],n)});return o&&r.sort(function(e,t){return o(e.key,t.key)}),r}var n={},i=[],s=[],o,u;return n.map=function(t){return e(t,0)},n.entries=function(n){return t(e(n,0),0)},n.key=function(e){return i.push(e),n},n.sortKeys=function(e){return s[i.length-1]=e,n},n.sortValues=function(e){return o=e,n},n.rollup=function(e){return u=e,n},n},d3.keys=function(e){var t=[];for(var n in e)t.push(n);return t},d3.values=function(e){var t=[];for(var n in e)t.push(e[n]);return t},d3.entries=function(e){var t=[];for(var n in e)t.push({key:n,value:e[n]});return t},d3.permute=function(e,t){var n=[],r=-1,i=t.length;while(++rt)r.push(o/i);else while((o=e+n*++s)=200&&e<300||e===304?r:null)}},r.send(null)},d3.text=function(e,t,n){function r(e){n(e&&e.responseText)}arguments.length<3&&(n=t,t=null),d3.xhr(e,t,r)},d3.json=function(e,t){d3.text(e,"application/json",function(e){t(e?JSON.parse(e):null)})},d3.html=function(e,t){d3.text(e,"text/html",function(e){if(e!=null){var n=document.createRange();n.selectNode(document.body),e=n.createContextualFragment(e)}t(e)})},d3.xml=function(e,t,n){function r(e){n(e&&e.responseXML)}arguments.length<3&&(n=t,t=null),d3.xhr(e,t,r)};var ts={svg:"http://www.w3.org/2000/svg",xhtml:"http://www.w3.org/1999/xhtml",xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace",xmlns:"http://www.w3.org/2000/xmlns/"};d3.ns={prefix:ts,qualify:function(e){var t=e.indexOf(":"),n=e;return t>=0&&(n=e.substring(0,t),e=e.substring(t+1)),ts.hasOwnProperty(n)?{space:ts[n],local:e}:e}},d3.dispatch=function(){var e=new d,t=-1,n=arguments.length;while(++t0&&(r=e.substring(n+1),e=e.substring(0,n)),arguments.length<2?this[e].on(r):this[e].on(r,t)},d3.format=function(e){var t=ns.exec(e),n=t[1]||" ",r=t[3]||"",i=t[5],s=+t[6],o=t[7],u=t[8],a=t[9],f=1,l="",c=!1;u&&(u=+u.substring(1)),i&&(n="0",o&&(s-=Math.floor((s-1)/4)));switch(a){case"n":o=!0,a="g";break;case"%":f=100,l="%",a="f";break;case"p":f=100,l="%",a="r";break;case"d":c=!0,u=0;break;case"s":f=-1,a="r"}return a=="r"&&!u&&(a="g"),a=rs.get(a)||g,function(e){if(c&&e%1)return"";var t=e<0&&(e=-e)?"-":r;if(f<0){var h=d3.formatPrefix(e,u);e=h.scale(e),l=h.symbol}else e*=f;e=a(e,u);if(i){var p=e.length+t.length;p=^]))?([+\- ])?(#)?(0)?([0-9]+)?(,)?(\.[0-9]+)?([a-zA-Z%])?/,rs=d3.map({g:function(e,t){return e.toPrecision(t)},e:function(e,t){return e.toExponential(t)},f:function(e,t){return e.toFixed(t)},r:function(e,t){return d3.round(e,t=m(e,t)).toFixed(Math.max(0,Math.min(20,t)))}}),is=["y","z","a","f","p","n","μ","m","","k","M","G","T","P","E","Z","Y"].map(b);d3.formatPrefix=function(e,t){var n=0;return e&&(e<0&&(e*=-1),t&&(e=d3.round(e,m(e,t))),n=1+Math.floor(1e-12+Math.log(e)/Math.LN10),n=Math.max(-24,Math.min(24,Math.floor((n<=0?n+1:n-1)/3)*3))),is[8+n/3]};var ss=T(2),os=T(3),us=function(){return x},as=d3.map({linear:us,poly:T,quad:function(){return ss},cubic:function(){return os},sin:function(){return N},exp:function(){return C},circle:function(){return k},elastic:L,back:A,bounce:function(){return O}}),fs=d3.map({"in":x,out:E,"in-out":S,"out-in":function(e){return S(E(e))}});d3.ease=function(e){var t=e.indexOf("-"),n=t>=0?e.substring(0,t):e,r=t>=0?e.substring(t+1):"in";return n=as.get(n)||us,r=fs.get(r)||x,w(r(n.apply(null,Array.prototype.slice.call(arguments,1))))},d3.event=null,d3.transform=function(e){var t=document.createElementNS(d3.ns.prefix.svg,"g");return(d3.transform=function(e){t.setAttribute("transform",e);var n=t.transform.baseVal.consolidate();return new P(n?n.matrix:cs)})(e)},P.prototype.toString=function(){return"translate("+this.translate+")rotate("+this.rotate+")skewX("+this.skew+")scale("+this.scale+")"};var ls=180/Math.PI,cs={a:1,b:0,c:0,d:1,e:0,f:0};d3.interpolate=function(e,t){var n=d3.interpolators.length,r;while(--n>=0&&!(r=d3.interpolators[n](e,t)));return r},d3.interpolateNumber=function(e,t){return t-=e,function(n){return e+t*n}},d3.interpolateRound=function(e,t){return t-=e,function(n){return Math.round(e+t*n)}},d3.interpolateString=function(e,t){var n,r,i,s=0,o=0,u=[],a=[],f,l;hs.lastIndex=0;for(r=0;n=hs.exec(t);++r)n.index&&u.push(t.substring(s,o=n.index)),a.push({i:u.length,x:n[0]}),u.push(null),s=hs.lastIndex;s180?l+=360:l-f>180&&(f+=360),r.push({i:n.push(n.pop()+"rotate(",null,")")-2,x:d3.interpolateNumber(f,l)})):l&&n.push(n.pop()+"rotate("+l+")"),c!=h?r.push({i:n.push(n.pop()+"skewX(",null,")")-2,x:d3.interpolateNumber(c,h)}):h&&n.push(n.pop()+"skewX("+h+")"),p[0]!=d[0]||p[1]!=d[1]?(i=n.push(n.pop()+"scale(",null,",",null,")"),r.push({i:i-4,x:d3.interpolateNumber(p[0],d[0])},{i:i-2,x:d3.interpolateNumber(p[1],d[1])})):(d[0]!=1||d[1]!=1)&&n.push(n.pop()+"scale("+d+")"),i=r.length,function(e){var t=-1,s;while(++t180?s-=360:s<-180&&(s+=360),function(e){return Y(n+s*e,r+o*e,i+u*e)+""}},d3.interpolateLab=function(e,t){e=d3.lab(e),t=d3.lab(t);var n=e.l,r=e.a,i=e.b,s=t.l-n,o=t.a-r,u=t.b-i;return function(e){return it(n+s*e,r+o*e,i+u*e)+""}},d3.interpolateHcl=function(e,t){e=d3.hcl(e),t=d3.hcl(t);var n=e.h,r=e.c,i=e.l,s=t.h-n,o=t.c-r,u=t.l-i;return s>180?s-=360:s<-180&&(s+=360),function(e){return tt(n+s*e,r+o*e,i+u*e)+""}},d3.interpolateArray=function(e,t){var n=[],r=[],i=e.length,s=t.length,o=Math.min(e.length,t.length),u;for(u=0;u=0;)if(s=n[r])i&&i!==s.nextSibling&&i.parentNode.insertBefore(s,i),i=s;return this},ks.sort=function(e){e=wt.apply(this,arguments);for(var t=-1,n=this.length;++t=Zs?e?"M0,"+s+"A"+s+","+s+" 0 1,1 0,"+ -s+"A"+s+","+s+" 0 1,1 0,"+s+"M0,"+e+"A"+e+","+e+" 0 1,0 0,"+ -e+"A"+e+","+e+" 0 1,0 0,"+e+"Z":"M0,"+s+"A"+s+","+s+" 0 1,1 0,"+ -s+"A"+s+","+s+" 0 1,1 0,"+s+"Z":e?"M"+s*l+","+s*c+"A"+s+","+s+" 0 "+f+",1 "+s*h+","+s*p+"L"+e*h+","+e*p+"A"+e+","+e+" 0 "+f+",0 "+e*l+","+e*c+"Z":"M"+s*l+","+s*c+"A"+s+","+s+" 0 "+f+",1 "+s*h+","+s*p+"L0,0"+"Z"}var t=en,n=tn,r=nn,i=rn;return e.innerRadius=function(n){return arguments.length?(t=u(n),e):t},e.outerRadius=function(t){return arguments.length?(n=u(t),e):n},e.startAngle=function(t){return arguments.length?(r=u(t),e):r},e.endAngle=function(t){return arguments.length?(i=u(t),e):i},e.centroid=function(){var e=(t.apply(this,arguments)+n.apply(this,arguments))/2,s=(r.apply(this,arguments)+i.apply(this,arguments))/2+Ys;return[Math.cos(s)*e,Math.sin(s)*e]},e};var Ys=-Math.PI/2,Zs=2*Math.PI-1e-6;d3.svg.line=function(){return sn(i)};var eo=d3.map({linear:an,"linear-closed":fn,"step-before":ln,"step-after":cn,basis:gn,"basis-open":yn,"basis-closed":bn,bundle:wn,cardinal:dn,"cardinal-open":hn,"cardinal-closed":pn,monotone:Cn});eo.forEach(function(e,t){t.key=e,t.closed=/-closed$/.test(e)});var to=[0,2/3,1/3,0],no=[0,1/3,2/3,0],ro=[0,1/6,2/3,1/6];d3.svg.line.radial=function(){var e=sn(kn);return e.radius=e.x,delete e.x,e.angle=e.y,delete e.y,e},ln.reverse=cn,cn.reverse=ln,d3.svg.area=function(){return Ln(i)},d3.svg.area.radial=function(){var e=Ln(kn);return e.radius=e.x,delete e.x,e.innerRadius=e.x0,delete e.x0,e.outerRadius=e.x1,delete e.x1,e.angle=e.y,delete e.y,e.startAngle=e.y0,delete e.y0,e.endAngle=e.y1,delete e.y1,e},d3.svg.chord=function(){function e(e,u){var a=t(this,s,e,u),f=t(this,o,e,u);return"M"+a.p0+r(a.r,a.p1,a.a1-a.a0)+(n(a,f)?i(a.r,a.p1,a.r,a.p0):i(a.r,a.p1,f.r,f.p0)+r(f.r,f.p1,f.a1-f.a0)+i(f.r,f.p1,a.r,a.p0))+"Z"}function t(e,t,n,r){var i=t.call(e,n,r),s=a.call(e,i,r),o=f.call(e,i,r)+Ys,u=l.call(e,i,r)+Ys;return{r:s,a0:o,a1:u,p0:[s*Math.cos(o),s*Math.sin(o)],p1:[s*Math.cos(u),s*Math.sin(u)]}}function n(e,t){return e.a0==t.a0&&e.a1==t.a1}function r(e,t,n){return"A"+e+","+e+" 0 "+ +(n>Math.PI)+",1 "+t}function i(e,t,n,r){return"Q 0,0 "+r}var s=An,o=On,a=Mn,f=nn,l=rn;return e.radius=function(t){return arguments.length?(a=u(t),e):a},e.source=function(t){return arguments.length?(s=u(t),e):s},e.target=function(t){return arguments.length?(o=u(t),e):o},e.startAngle=function(t){return arguments.length?(f=u(t),e):f},e.endAngle=function(t){return arguments.length?(l=u(t),e):l},e},d3.svg.diagonal=function(){function e(e,i){var s=t.call(this,e,i),o=n.call(this,e,i),u=(s.y+o.y)/2,a=[s,{x:s.x,y:u},{x:o.x,y:u},o];return a=a.map(r),"M"+a[0]+"C"+a[1]+" "+a[2]+" "+a[3]}var t=An,n=On,r=Pn;return e.source=function(n){return arguments.length?(t=u(n),e):t},e.target=function(t){return arguments.length?(n=u(t),e):n},e.projection=function(t){return arguments.length?(r=t,e):r},e},d3.svg.diagonal.radial=function(){var e=d3.svg.diagonal(),t=Pn,n=e.projection;return e.projection=function(e){return arguments.length?n(Hn(t=e)):t},e},d3.svg.mouse=d3.mouse,d3.svg.touches=d3.touches,d3.svg.symbol=function(){function e(e,r){return(io.get(t.call(this,e,r))||Fn)(n.call(this,e,r))}var t=jn,n=Bn;return e.type=function(n){return arguments.length?(t=u(n),e):t},e.size=function(t){return arguments.length?(n=u(t),e):n},e};var io=d3.map({circle:Fn,cross:function(e){var t=Math.sqrt(e/5)/2;return"M"+ -3*t+","+ -t+"H"+ -t+"V"+ -3*t+"H"+t+"V"+ -t+"H"+3*t+"V"+t+"H"+t+"V"+3*t+"H"+ -t+"V"+t+"H"+ -3*t+"Z"},diamond:function(e){var t=Math.sqrt(e/(2*oo)),n=t*oo;return"M0,"+ -t+"L"+n+",0"+" 0,"+t+" "+ -n+",0"+"Z"},square:function(e){var t=Math.sqrt(e)/2;return"M"+ -t+","+ -t+"L"+t+","+ -t+" "+t+","+t+" "+ -t+","+t+"Z"},"triangle-down":function(e){var t=Math.sqrt(e/so),n=t*so/2;return"M0,"+n+"L"+t+","+ -n+" "+ -t+","+ -n+"Z"},"triangle-up":function(e){var t=Math.sqrt(e/so),n=t*so/2;return"M0,"+ -n+"L"+t+","+n+" "+ -t+","+n+"Z"}});d3.svg.symbolTypes=io.keys();var so=Math.sqrt(3),oo=Math.tan(30*Math.PI/180);d3.svg.axis=function(){function e(e){e.each(function(){var e=d3.select(this),c=a==null?t.ticks?t.ticks.apply(t,u):t.domain():a,h=f==null?t.tickFormat?t.tickFormat.apply(t,u):String:f,p=Rn(t,c,l),d=e.selectAll(".minor").data(p,String),v=d.enter().insert("line","g").attr("class","tick minor").style("opacity",1e-6),m=d3.transition(d.exit()).style("opacity",1e-6).remove(),g=d3.transition(d).style("opacity",1),y=e.selectAll("g").data(c,String),b=y.enter().insert("g","path").style("opacity",1e-6),w=d3.transition(y.exit()).style("opacity",1e-6).remove(),E=d3.transition(y).style("opacity",1),S,x=Dt(t),T=e.selectAll(".domain").data([0]),N=T.enter().append("path").attr("class","domain"),C=d3.transition(T),k=t.copy(),L=this.__chart__||k;this.__chart__=k,b.append("line").attr("class","tick"),b.append("text");var A=b.select("line"),O=E.select("line"),M=y.select("text").text(h),_=b.select("text"),D=E.select("text");switch(n){case"bottom":S=In,v.attr("y2",i),g.attr("x2",0).attr("y2",i),A.attr("y2",r),_.attr("y",Math.max(r,0)+o),O.attr("x2",0).attr("y2",r),D.attr("x",0).attr("y",Math.max(r,0)+o),M.attr("dy",".71em").attr("text-anchor","middle"),C.attr("d","M"+x[0]+","+s+"V0H"+x[1]+"V"+s);break;case"top":S=In,v.attr("y2",-i),g.attr("x2",0).attr("y2",-i),A.attr("y2",-r),_.attr("y",-(Math.max(r,0)+o)),O.attr("x2",0).attr("y2",-r),D.attr("x",0).attr("y",-(Math.max(r,0)+o)),M.attr("dy","0em").attr("text-anchor","middle"),C.attr("d","M"+x[0]+","+ -s+"V0H"+x[1]+"V"+ -s);break;case"left":S=qn,v.attr("x2",-i),g.attr("x2",-i).attr("y2",0),A.attr("x2",-r),_.attr("x",-(Math.max(r,0)+o)),O.attr("x2",-r).attr("y2",0),D.attr("x",-(Math.max(r,0)+o)).attr("y",0),M.attr("dy",".32em").attr("text-anchor","end"),C.attr("d","M"+ -s+","+x[0]+"H0V"+x[1]+"H"+ -s);break;case"right":S=qn,v.attr("x2",i),g.attr("x2",i).attr("y2",0),A.attr("x2",r),_.attr("x",Math.max(r,0)+o),O.attr("x2",r).attr("y2",0),D.attr("x",Math.max(r,0)+o).attr("y",0),M.attr("dy",".32em").attr("text-anchor","start"),C.attr("d","M"+s+","+x[0]+"H0V"+x[1]+"H"+s)}if(t.ticks)b.call(S,L),E.call(S,k),w.call(S,k),v.call(S,L),g.call(S,k),m.call(S,k);else{var P=k.rangeBand()/2,H=function(e){return k(e)+P};b.call(S,H),E.call(S,H)}})}var t=d3.scale.linear(),n="bottom",r=6,i=6,s=6,o=3,u=[10],a=null,f,l=0;return e.scale=function(n){return arguments.length?(t=n,e):t},e.orient=function(t){return arguments.length?(n=t,e):n},e.ticks=function(){return arguments.length?(u=arguments,e):u},e.tickValues=function(t){return arguments.length?(a=t,e):a},e.tickFormat=function(t){return arguments.length?(f=t,e):f},e.tickSize=function(t,n,o){if(!arguments.length)return r;var u=arguments.length-1;return r=+t,i=u>1?+n:r,s=u>0?+arguments[u]:r,e},e.tickPadding=function(t){return arguments.length?(o=+t,e):o},e.tickSubdivide=function(t){return arguments.length?(l=+t,e):l},e},d3.svg.brush=function(){function e(s){s.each(function(){var s=d3.select(this),f=s.selectAll(".background").data([0]),l=s.selectAll(".extent").data([0]),c=s.selectAll(".resize").data(a,String),h;s.style("pointer-events","all").on("mousedown.brush",i).on("touchstart.brush",i),f.enter().append("rect").attr("class","background").style("visibility","hidden").style("cursor","crosshair"),l.enter().append("rect").attr("class","extent").style("cursor","move"),c.enter().append("g").attr("class",function(e){return"resize "+e}).style("cursor",function(e){return uo[e]}).append("rect").attr("x",function(e){return/[ew]$/.test(e)?-3:null}).attr("y",function(e){return/^[ns]/.test(e)?-3:null}).attr("width",6).attr("height",6).style("visibility","hidden"),c.style("display",e.empty()?"none":null),c.exit().remove(),o&&(h=Dt(o),f.attr("x",h[0]).attr("width",h[1]-h[0]),n(s)),u&&(h=Dt(u),f.attr("y",h[0]).attr("height",h[1]-h[0]),r(s)),t(s)})}function t(e){e.selectAll(".resize").attr("transform",function(e){return"translate("+f[+/e$/.test(e)][0]+","+f[+/^s/.test(e)][1]+")"})}function n(e){e.select(".extent").attr("x",f[0][0]),e.selectAll(".extent,.n>rect,.s>rect").attr("width",f[1][0]-f[0][0])}function r(e){e.select(".extent").attr("y",f[0][1]),e.selectAll(".extent,.e>rect,.w>rect").attr("height",f[1][1]-f[0][1])}function i(){function i(){var e=d3.event.changedTouches;return e?d3.touches(v,e)[0]:d3.mouse(v)}function a(){d3.event.keyCode==32&&(S||(x=null,T[0]-=f[1][0],T[1]-=f[1][1],S=2),M())}function c(){d3.event.keyCode==32&&S==2&&(T[0]+=f[1][0],T[1]+=f[1][1],S=0,M())}function h(){var e=i(),s=!1;N&&(e[0]+=N[0],e[1]+=N[1]),S||(d3.event.altKey?(x||(x=[(f[0][0]+f[1][0])/2,(f[0][1]+f[1][1])/2]),T[0]=f[+(e[0]0?a=e:a=0:e>0&&(r.start({type:"start",alpha:a=e}),d3.timer(n.tick)),n):a},n.start=function(){function e(e,n){var i=t(r),s=-1,o=i.length,u;while(++si&&(i=u),r.push(u)}for(o=0;o0){s=-1;while(++s=a[0]&&d<=a[1]&&(l=o[d3.bisect(f,d,1,h)-1],l.y+=p,l.push(e[s]))}return o}var t=!0,n=Number,r=fr,i=ur;return e.value=function(t){return arguments.length?(n=t,e):n},e.range=function(t){return arguments.length?(r=u(t),e):r},e.bins=function(t){return arguments.length?(i=typeof t=="number"?function(e){return ar(e,t)}:u(t),e):i},e.frequency=function(n){return arguments.length?(t=!!n,e):t},e},d3.layout.hierarchy=function(){function e(t,o,u){var a=i.call(n,t,o),f=vo?t:{data:t};f.depth=o,u.push(f);if(a&&(c=a.length)){var l=-1,c,h=f.children=[],p=0,d=o+1,v;while(++l0){var l=n*f/2;Hr(o,function(e){e.r+=l}),Hr(o,br),Hr(o,function(e){e.r-=l}),f=Math.max(2*o.r/u,2*o.r/a)}return Sr(o,u/2,a/2,1/f),s}var t=d3.layout.hierarchy().sort(vr),n=0,r=[1,1];return e.size=function(t){return arguments.length?(r=t,e):r},e.padding=function(t){return arguments.length?(n=+t,e):n},lr(e,t)},d3.layout.cluster=function(){function e(e,i){var s=t.call(this,e,i),o=s[0],u,a=0,f,l;Hr(o,function(e){var t=e.children;t&&t.length?(e.x=Nr(t),e.y=Tr(t)):(e.x=u?a+=n(e,u):0,e.y=0,u=e)});var c=Cr(o),h=kr(o),p=c.x-n(c,h)/2,d=h.x+n(h,c)/2;return Hr(o,function(e){e.x=(e.x-p)/(d-p)*r[0],e.y=(1-(o.y?e.y/o.y:1))*r[1]}),s}var t=d3.layout.hierarchy().sort(null).value(null),n=Lr,r=[1,1];return e.separation=function(t){return arguments.length?(n=t,e):n},e.size=function(t){return arguments.length?(r=t,e):r},lr(e,t)},d3.layout.tree=function(){function e(e,i){function s(e,t){var r=e.children,i=e._tree;if(r&&(o=r.length)){var o,a=r[0],f,l=a,c,h=-1;while(++h0&&(jr(Fr(o,e,r),e,h),a+=h,f+=h),l+=o._tree.mod,a+=i._tree.mod,c+=u._tree.mod,f+=s._tree.mod;o&&!Or(s)&&(s._tree.thread=o,s._tree.mod+=l-f),i&&!Ar(u)&&(u._tree.thread=i,u._tree.mod+=a-c,r=e)}return r}var a=t.call(this,e,i),f=a[0];Hr(f,function(e,t){e._tree={ancestor:e,prelim:0,mod:0,change:0,shift:0,number:t?t._tree.number+1:0}}),s(f),o(f,-f._tree.prelim);var l=Mr(f,Dr),c=Mr(f,_r),h=Mr(f,Pr),p=l.x-n(l,c)/2,d=c.x+n(c,l)/2,v=h.depth||1;return Hr(f,function(e){e.x=(e.x-p)/(d-p)*r[0],e.y=e.depth/v*r[1],delete e._tree}),a}var t=d3.layout.hierarchy().sort(null).value(null),n=Lr,r=[1,1];return e.separation=function(t){return arguments.length?(n=t,e):n},e.size=function(t){return arguments.length?(r=t,e):r},lr(e,t)},d3.layout.treemap=function(){function e(e,t){var n=-1,r=e.length,i,s;while(++n0)u.push(f=a[d-1]),u.area+=f.area,(h=r(u,p))<=c?(a.pop(),c=h):(u.area-=u.pop().area,i(u,p,o,!1),p=Math.min(o.dx,o.dy),u.length=u.area=0,c=Infinity);u.length&&(i(u,p,o,!0),u.length=u.area=0),s.forEach(t)}}function n(t){var r=t.children;if(r&&r.length){var s=l(t),o=r.slice(),u,a=[];e(o,s.dx*s.dy/t.value),a.area=0;while(u=o.pop())a.push(u),a.area+=u.area,u.z!=null&&(i(a,u.z?s.dx:s.dy,s,!o.length),a.length=a.area=0);r.forEach(n)}}function r(e,t){var n=e.area,r,i=0,s=Infinity,o=-1,u=e.length;while(++oi&&(i=r)}return n*=n,t*=t,n?Math.max(t*i*p/n,n/(t*s*p)):Infinity}function i(e,t,n,r){var i=-1,s=e.length,o=n.x,a=n.y,f=t?u(e.area/t):0,l;if(t==n.dx){if(r||f>n.dy)f=n.dy;while(++in.dx)f=n.dx;while(++i50?n:s<-140?r:o<21?i:t)(e)}var t=d3.geo.albers(),n=d3.geo.albers().origin([-160,60]).parallels([55,65]),r=d3.geo.albers().origin([-160,20]).parallels([8,18]),i=d3.geo.albers().origin([-60,10]).parallels([8,18]);return e.scale=function(s){return arguments.length?(t.scale(s),n.scale(s*.6),r.scale(s),i.scale(s*1.5),e.translate(t.translate())):t.scale()},e.translate=function(s){if(!arguments.length)return t.translate();var o=t.scale()/1e3,u=s[0],a=s[1];return t.translate(s),n.translate([u-400*o,a+170*o]),r.translate([u-190*o,a+200*o]),i.translate([u+580*o,a+430*o]),e},e.scale(t.scale())},d3.geo.bonne=function(){function e(e){var u=e[0]*mo-r,a=e[1]*mo-i;if(s){var f=o+s-a,l=u*Math.cos(a)/f;u=f*Math.sin(l),a=f*Math.cos(l)-o}else u*=Math.cos(a),a*=-1;return[t*u+n[0],t*a+n[1]]}var t=200,n=[480,250],r,i,s,o;return e.invert=function(e){var i=(e[0]-n[0])/t,u=(e[1]-n[1])/t;if(s){var a=o+u,f=Math.sqrt(i*i+a*a);u=o+s-f,i=r+f*Math.atan2(i,a)/Math.cos(u)}else u*=-1,i/=Math.cos(u);return[i/mo,u/mo]},e.parallel=function(t){return arguments.length?(o=1/Math.tan(s=t*mo),e):s/mo},e.origin=function(t){return arguments.length?(r=t[0]*mo,i=t[1]*mo,e):[r/mo,i/mo]},e.scale=function( +n){return arguments.length?(t=+n,e):t},e.translate=function(t){return arguments.length?(n=[+t[0],+t[1]],e):n},e.origin([0,0]).parallel(45)},d3.geo.equirectangular=function(){function e(e){var r=e[0]/360,i=-e[1]/360;return[t*r+n[0],t*i+n[1]]}var t=500,n=[480,250];return e.invert=function(e){var r=(e[0]-n[0])/t,i=(e[1]-n[1])/t;return[360*r,-360*i]},e.scale=function(n){return arguments.length?(t=+n,e):t},e.translate=function(t){return arguments.length?(n=[+t[0],+t[1]],e):n},e},d3.geo.mercator=function(){function e(e){var r=e[0]/360,i=-(Math.log(Math.tan(Math.PI/4+e[1]*mo/2))/mo)/360;return[t*r+n[0],t*Math.max(-0.5,Math.min(.5,i))+n[1]]}var t=500,n=[480,250];return e.invert=function(e){var r=(e[0]-n[0])/t,i=(e[1]-n[1])/t;return[360*r,2*Math.atan(Math.exp(-360*i*mo))/mo-90]},e.scale=function(n){return arguments.length?(t=+n,e):t},e.translate=function(t){return arguments.length?(n=[+t[0],+t[1]],e):n},e},d3.geo.path=function(){function e(e,t){typeof s=="function"&&(o=zr(s.apply(this,arguments))),f(e);var n=a.length?a.join(""):null;return a=[],n}function t(e){return u(e).join(",")}function n(e){var t=i(e[0]),n=0,r=e.length;while(++n0){a.push("M");while(++o0){a.push("M");while(++lr&&(r=e),si&&(i=s)}),[[t,n],[r,i]]};var go={Feature:Xr,FeatureCollection:Vr,GeometryCollection:$r,LineString:Jr,MultiLineString:Kr,MultiPoint:Jr,MultiPolygon:Qr,Point:Gr,Polygon:Yr};d3.geo.circle=function(){function e(){}function t(e){return a.distance(e)=l*l+c*c?r[s].index=-1:(r[h].index=-1,d=r[s].angle,h=s,p=o)):(d=r[s].angle,h=s,p=o);i.push(u);for(s=0,o=0;s<2;++o)r[o].index!==-1&&(i.push(r[o].index),s++);v=i.length;for(;o=0?(n=e.ep.r,r=e.ep.l):(n=e.ep.l,r=e.ep.r),e.a===1?(o=n?n.y:-1e6,i=e.c-e.b*o,u=r?r.y:1e6,s=e.c-e.b*u):(i=n?n.x:-1e6,o=e.c-e.a*i,s=r?r.x:1e6,u=e.c-e.a*s);var a=[i,o],f=[s,u];t[e.region.l.index].push(a,f),t[e.region.r.index].push(a,f)}),t.map(function(t,n){var r=e[n][0],i=e[n][1];return t.forEach(function(e){e.angle=Math.atan2(e[0]-r,e[1]-i)}),t.sort(function(e,t){return e.angle-t.angle}).filter(function(e,n){return!n||e.angle-t[n-1].angle>1e-10})})};var wo={l:"r",r:"l"};d3.geom.delaunay=function(e){var t=e.map(function(){return[]}),n=[];return ui(e,function(n){t[n.region.l.index].push(e[n.region.r.index])}),t.forEach(function(t,r){var i=e[r],s=i[0],o=i[1];t.forEach(function(e){e.angle=Math.atan2(e[0]-s,e[1]-o)}),t.sort(function(e,t){return e.angle-t.angle});for(var u=0,a=t.length-1;u=u,l=t.y>=a,c=(l<<1)+f;e.leaf=!1,e=e.nodes[c]||(e.nodes[c]=ai()),f?n=u:i=u,l?r=a:o=a,s(e,t,n,r,i,o)}var u,a=-1,f=e.length;f&&isNaN(e[0].x)&&(e=e.map(li));if(arguments.length<5)if(arguments.length===3)i=r=n,n=t;else{t=n=Infinity,r=i=-Infinity;while(++ar&&(r=u.x),u.y>i&&(i=u.y);var l=r-t,c=i-n;l>c?i=n+l:r=t+c}var h=ai();return h.add=function(e){s(h,e,t,n,r,i)},h.visit=function(e){fi(e,h,t,n,r,i)},e.forEach(h.add),h},d3.time={};var Eo=Date,So=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];ci.prototype={getDate:function(){return this._.getUTCDate()},getDay:function(){return this._.getUTCDay()},getFullYear:function(){return this._.getUTCFullYear()},getHours:function(){return this._.getUTCHours()},getMilliseconds:function(){return this._.getUTCMilliseconds()},getMinutes:function(){return this._.getUTCMinutes()},getMonth:function(){return this._.getUTCMonth()},getSeconds:function(){return this._.getUTCSeconds()},getTime:function(){return this._.getTime()},getTimezoneOffset:function(){return 0},valueOf:function(){return this._.valueOf()},setDate:function(){xo.setUTCDate.apply(this._,arguments)},setDay:function(){xo.setUTCDay.apply(this._,arguments)},setFullYear:function(){xo.setUTCFullYear.apply(this._,arguments)},setHours:function(){xo.setUTCHours.apply(this._,arguments)},setMilliseconds:function(){xo.setUTCMilliseconds.apply(this._,arguments)},setMinutes:function(){xo.setUTCMinutes.apply(this._,arguments)},setMonth:function(){xo.setUTCMonth.apply(this._,arguments)},setSeconds:function(){xo.setUTCSeconds.apply(this._,arguments)},setTime:function(){xo.setTime.apply(this._,arguments)}};var xo=Date.prototype,To="%a %b %e %H:%M:%S %Y",No="%m/%d/%y",Co="%H:%M:%S",ko=So,Lo=ko.map(hi),Ao=["January","February","March","April","May","June","July","August","September","October","November","December"],Oo=Ao.map(hi);d3.time.format=function(e){function t(t){var r=[],i=-1,s=0,o,u;while(++i=12?"PM":"AM"},S:function(e){return Mo(e.getSeconds())},U:function(e){return Mo(d3.time.sundayOfYear(e))},w:function(e){return e.getDay()},W:function(e){return Mo(d3.time.mondayOfYear(e))},x:d3.time.format(No),X:d3.time.format(Co),y:function(e){return Mo(e.getFullYear()%100)},Y:function(e){return Do(e.getFullYear()%1e4)},Z:Di,"%":function(e){return"%"}},Uo={a:mi,A:gi,b:yi,B:bi,c:wi,d:ki,e:ki,H:Li,I:Li,L:Mi,m:Ci,M:Ai,p:_i,S:Oi,x:Ei,X:Si,y:Ti,Y:xi},zo=/^\s*\d+/,Wo=d3.map({am:0,pm:1});d3.time.format.utc=function(e){function t(e){try{Eo=ci;var t=new Eo;return t._=e,n(t)}finally{Eo=Date}}var n=d3.time.format(e);return t.parse=function(e){try{Eo=ci;var t=n.parse(e);return t&&t._}finally{Eo=Date}},t.toString=n.toString,t};var Xo=d3.time.format.utc("%Y-%m-%dT%H:%M:%S.%LZ");d3.time.format.iso=Date.prototype.toISOString?Pi:Xo,Pi.parse=function(e){var t=new Date(e);return isNaN(t)?null:t},Pi.toString=Xo.toString,d3.time.second=Hi(function(e){return new Eo(Math.floor(e/1e3)*1e3)},function(e,t){e.setTime(e.getTime()+Math.floor(t)*1e3)},function(e){return e.getSeconds()}),d3.time.seconds=d3.time.second.range,d3.time.seconds.utc=d3.time.second.utc.range,d3.time.minute=Hi(function(e){return new Eo(Math.floor(e/6e4)*6e4)},function(e,t){e.setTime(e.getTime()+Math.floor(t)*6e4)},function(e){return e.getMinutes()}),d3.time.minutes=d3.time.minute.range,d3.time.minutes.utc=d3.time.minute.utc.range,d3.time.hour=Hi(function(e){var t=e.getTimezoneOffset()/60;return new Eo((Math.floor(e/36e5-t)+t)*36e5)},function(e,t){e.setTime(e.getTime()+Math.floor(t)*36e5)},function(e){return e.getHours()}),d3.time.hours=d3.time.hour.range,d3.time.hours.utc=d3.time.hour.utc.range,d3.time.day=Hi(function(e){var t=new Eo(1970,0);return t.setFullYear(e.getFullYear(),e.getMonth(),e.getDate()),t},function(e,t){e.setDate(e.getDate()+t)},function(e){return e.getDate()-1}),d3.time.days=d3.time.day.range,d3.time.days.utc=d3.time.day.utc.range,d3.time.dayOfYear=function(e){var t=d3.time.year(e);return Math.floor((e-t-(e.getTimezoneOffset()-t.getTimezoneOffset())*6e4)/864e5)},So.forEach(function(e,t){e=e.toLowerCase(),t=7-t;var n=d3.time[e]=Hi(function(e){return(e=d3.time.day(e)).setDate(e.getDate()-(e.getDay()+t)%7),e},function(e,t){e.setDate(e.getDate()+Math.floor(t)*7)},function(e){var n=d3.time.year(e).getDay();return Math.floor((d3.time.dayOfYear(e)+(n+t)%7)/7)-(n!==t)});d3.time[e+"s"]=n.range,d3.time[e+"s"].utc=n.utc.range,d3.time[e+"OfYear"]=function(e){var n=d3.time.year(e).getDay();return Math.floor((d3.time.dayOfYear(e)+(n+t)%7)/7)}}),d3.time.week=d3.time.sunday,d3.time.weeks=d3.time.sunday.range,d3.time.weeks.utc=d3.time.sunday.utc.range,d3.time.weekOfYear=d3.time.sundayOfYear,d3.time.month=Hi(function(e){return e=d3.time.day(e),e.setDate(1),e},function(e,t){e.setMonth(e.getMonth()+t)},function(e){return e.getMonth()}),d3.time.months=d3.time.month.range,d3.time.months.utc=d3.time.month.utc.range,d3.time.year=Hi(function(e){return e=d3.time.day(e),e.setMonth(0,1),e},function(e,t){e.setFullYear(e.getFullYear()+t)},function(e){return e.getFullYear()}),d3.time.years=d3.time.year.range,d3.time.years.utc=d3.time.year.utc.range;var Vo=[1e3,5e3,15e3,3e4,6e4,3e5,9e5,18e5,36e5,108e5,216e5,432e5,864e5,1728e5,6048e5,2592e6,7776e6,31536e6],$o=[[d3.time.second,1],[d3.time.second,5],[d3.time.second,15],[d3.time.second,30],[d3.time.minute,1],[d3.time.minute,5],[d3.time.minute,15],[d3.time.minute,30],[d3.time.hour,1],[d3.time.hour,3],[d3.time.hour,6],[d3.time.hour,12],[d3.time.day,1],[d3.time.day,2],[d3.time.week,1],[d3.time.month,1],[d3.time.month,3],[d3.time.year,1]],Jo=[[d3.time.format("%Y"),function(e){return!0}],[d3.time.format("%B"),function(e){return e.getMonth()}],[d3.time.format("%b %d"),function(e){return e.getDate()!=1}],[d3.time.format("%a %d"),function(e){return e.getDay()&&e.getDate()!=1}],[d3.time.format("%I %p"),function(e){return e.getHours()}],[d3.time.format("%I:%M"),function(e){return e.getMinutes()}],[d3.time.format(":%S"),function(e){return e.getSeconds()}],[d3.time.format(".%L"),function(e){return e.getMilliseconds()}]],Ko=d3.scale.linear(),Qo=qi(Jo);$o.year=function(e,t){return Ko.domain(e.map(Ui)).ticks(t).map(Ri)},d3.time.scale=function(){return ji(d3.scale.linear(),$o,Qo)};var Go=$o.map(function(e){return[e[0].utc,e[1]]}),Yo=[[d3.time.format.utc("%Y"),function(e){return!0}],[d3.time.format.utc("%B"),function(e){return e.getUTCMonth()}],[d3.time.format.utc("%b %d"),function(e){return e.getUTCDate()!=1}],[d3.time.format.utc("%a %d"),function(e){return e.getUTCDay()&&e.getUTCDate()!=1}],[d3.time.format.utc("%I %p"),function(e){return e.getUTCHours()}],[d3.time.format.utc("%I:%M"),function(e){return e.getUTCMinutes()}],[d3.time.format.utc(":%S"),function(e){return e.getUTCSeconds()}],[d3.time.format.utc(".%L"),function(e){return e.getUTCMilliseconds()}]],Zo=qi(Yo);Go.year=function(e,t){return Ko.domain(e.map(Wi)).ticks(t).map(zi)},d3.time.scale.utc=function(){return ji(d3.scale.linear(),Go,Zo)}})(); \ No newline at end of file