Merge branch 'tagging' into tagging-merge

Conflicts:
	index.html
	js/iD/Controller.js
	js/iD/Entity.js
	js/iD/controller/edit/EditBaseState.js
This commit is contained in:
Tom MacWright
2012-10-17 12:29:28 -04:00
45 changed files with 351 additions and 36 deletions
+1 -1
View File
@@ -48,6 +48,6 @@ Scoping in JavaScript is famously broken: Dojo's lang.hitch method will save you
Instance methods and variables _always_ need to be accessed with 'this.'. This is a fairly frequent gotcha if you're coming from another language.
The [Dojo site](http://dojotoolkit.org/) has lots of documentation. iD currently uses Dojo 1.7.2 - this has a much better module architecture (AMD) than previously.
The [Dojo site](http://dojotoolkit.org/) has lots of documentation. iD currently uses Dojo 1.8 - this has a much better module architecture (AMD) than previously.
Come on in, the water's lovely. More help? Ping RichardF on IRC (irc.oftc.net, in #osm-dev or #osm), on the OSM mailing lists or at richard@systemeD.net.
+3
View File
@@ -18,6 +18,9 @@ Renderer to do
* Node headings (for locks etc.)
* Dragging needs tolerance (i.e. less than n pixels and n seconds)
Tagging to do
* dijitTooltipConnector is wrong when an entityUI is clicked
ControllerStates to do:
* Try to share as much code as possible
* Draw way controller states:
+13 -9
View File
@@ -4,11 +4,11 @@
<meta charset="utf-8">
<title>iD</title>
<!-- load Dojo -->
<link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/dojo/1.7.2/dijit/themes/claro/claro.css">
<link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/dojo/1.7.2/dojox/layout/resources/FloatingPane.css">
<link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/dojo/1.7.2/dojox/layout/resources/ResizeHandle.css">
<link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/dojo/1.8/dijit/themes/claro/claro.css">
<link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/dojo/1.8/dojox/layout/resources/FloatingPane.css">
<link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/dojo/1.8/dojox/layout/resources/ResizeHandle.css">
<link rel="stylesheet" href="css/app.css">
<script src="http://ajax.googleapis.com/ajax/libs/dojo/1.7.2/dojo/dojo.js" data-dojo-config="async: true, parseOnLoad: true, baseUrl: 'js/iD/'"></script>
<script src="http://ajax.googleapis.com/ajax/libs/dojo/1.8/dojo/dojo.js" data-dojo-config="async: true, parseOnLoad: true, baseUrl: 'js/iD/'"></script>
<style type="text/css">
:focus { outline-color: transparent; outline-style: none; }
* { font-family: Helvetica, Arial; }
@@ -28,7 +28,7 @@
<script type="text/javascript" src="js/iD/Connection.js"></script>
<script>
require(["dojo/_base/lang","dojo/dom-geometry","dojo/dom-class","dojo/on","dojo/dom",
require(["dojo/_base/lang","dojo/dom-geometry","dojo/dom-class","dojo/on","dojo/dom","dojo/Evented",
"dijit/form/Button","dijit/form/ToggleButton",
"dojox/layout/FloatingPane",
"iD/actions/UndoStack","iD/actions/CreatePOIAction",
@@ -38,7 +38,7 @@ require(["dojo/_base/lang","dojo/dom-geometry","dojo/dom-class","dojo/on","dojo/
"iD/controller/shape/NoSelection",
"iD/renderer/Map","iD/styleparser/RuleSet",
"iD/ui/DragAndDrop","iD/ui/StepPane",
"dojo/domReady!"], function(lang,domGeom,domClass,on,dom) {
"dojo/domReady!"], function(lang,domGeom,domClass,on,dom,Evented){
var ruleset=new iD.styleparser.RuleSet();
var conn=new iD.Connection("http://www.overpass-api.de/api/xapi?");
@@ -65,8 +65,8 @@ require(["dojo/_base/lang","dojo/dom-geometry","dojo/dom-class","dojo/on","dojo/
map.setController(controller);
// Initialise event listeners
on(window, "enterState", enterStateListener);
on(window, "exitState", exitStateListener);
controller.on("enterState", enterStateListener);
controller.on("exitState", exitStateListener);
// ----------------------------------------------------
// Data is loaded and app ready to go
@@ -80,6 +80,10 @@ require(["dojo/_base/lang","dojo/dom-geometry","dojo/dom-class","dojo/on","dojo/
// Set initial controllerState
controller.setState(new iD.controller.edit.NoSelection());
// Load presets
controller.setTagPresets('way','presets/ways.json');
controller.setTagPresets('node','presets/nodes.json');
// Load data
map.download();
@@ -134,7 +138,7 @@ require(["dojo/_base/lang","dojo/dom-geometry","dojo/dom-class","dojo/on","dojo/
<div id="modebuttons" style="position:absolute; left: 10px; top: 10px;">
<div id="addPOI" data-dojo-type="dijit.form.DropDownButton" data-dojo-props="iconClass:'dijitIconApplication', onClick:function(){}">
<span>Add point</span>
        <div data-dojo-type="dijit.TooltipDialog">
<div data-dojo-type="dijit.TooltipDialog">
<p>Drag points onto the map</p>
<table id="dndgrid">
</table>
+15 -5
View File
@@ -1,18 +1,23 @@
define(['dojo/_base/declare','dojo/on','iD/actions/UndoStack'], function(declare,on){
define(['dojo/_base/declare','dojo/on','dojo/Evented',
'iD/actions/UndoStack','iD/tags/PresetList'], function(declare,on,Evented){
// ----------------------------------------------------------------------
// Controller base class
declare("iD.Controller", null, {
declare("iD.Controller", [Evented], {
state: null, // current ControllerState
map: null, // current Map
stepper: null, // current StepPane
undoStack: null, // main undoStack
presets: null, // tag presets
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.actions.UndoStack();
this.presets={};
this.editorCache={};
},
setStepper:function(_stepper) {
@@ -20,17 +25,22 @@ declare("iD.Controller", null, {
this.stepper=_stepper;
},
setTagPresets:function(type,url) {
// summary: Load and store a JSON tag preset file.
this.presets[type]=new iD.tags.PresetList(type,url);
},
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.state.exitState(newState);
on.emit(window, "exitState", { bubbles: true, cancelable: true, state: this.state.stateNameAsArray() });
this.emit("exitState", { bubbles: true, cancelable: true, state: this.state.stateNameAsArray() });
}
newState.setController(this);
this.state=newState;
newState.enterState();
on.emit(window, "enterState", { bubbles: true, cancelable: true, state: this.state.stateNameAsArray() });
this.emit("enterState", { bubbles: true, cancelable: true, state: this.state.stateNameAsArray() });
},
entityMouseEvent:function(event,entityUI) {
-11
View File
@@ -14,33 +14,22 @@ iD.Entity = function() {
};
iD.Entity.prototype = {
isType:function(type) {
// summary: Is this entity of the specified type ('node','way','relation')?
return this.entityType === type;
},
toString:function() {
return this.entityType + " . " + this.id;
},
// --------------------------------
// Provoke redraw and other changes
refresh:function() {
// summary: Ask the connection to provoke redraw and other changes.
this.connection.refreshEntity(this);
},
// -------------------------------------
// Bounding box check (to be overridden)
within:function(left, right, top, bottom) {
// summary: Is the entity within the specified bbox?
return !this.deleted; // Boolean
},
// ---------------
// Parent-handling
addParent: function(entity) {
// summary: Record a parent (a relation or way which contains this entity).
+20 -8
View File
@@ -1,7 +1,8 @@
// iD/controller/edit/EditBaseState.js
define(['dojo/_base/declare','dijit/TooltipDialog','dijit/popup',
'iD/controller/ControllerState'], function(declare){
define(['dojo/_base/declare','dojo/_base/lang','dojo/_base/array','dojo/on',
'dijit/registry','dijit/TooltipDialog','dijit/Dialog','dijit/popup',
'iD/controller/ControllerState','iD/tags/TagEditor'], function(declare,lang,array,on,registry){
// ----------------------------------------------------------------------
// EditBaseState class - provides shared UI functions to edit mode states
@@ -20,20 +21,31 @@ declare("iD.controller.edit.EditBaseState", [iD.controller.ControllerState], {
// y: Number Screen co-ordinate.
// entity: iD.Entity The entity to be edited.
var h = iD.Util.friendlyName(entity);
if (h !== '') h = h + "<br/>";
this.editortooltip = new dijit.TooltipDialog({
content: h+"<button data-dojo-type='dijit.form.Button' type='submit'>Edit tags</button> " +
"<button data-dojo-type='dijit.form.Button' type='submit'>Edit shape</button> ",
autoFocus: false
this.editortooltip = new dijit.TooltipDialog({
content: h+"<button data-dojo-type='dijit.form.Button' id='editTags' parseOnLoad='false' type='submit'>Edit tags</button> "
+"<button data-dojo-type='dijit.form.Button' id='editShape' parseOnLoad='false' type='submit'>Edit shape</button> ",
autoFocus: false
});
on(registry.byId('editTags'), 'click', lang.hitch(this,this.editTags,entity));
dijit.popup.open({ popup: this.editortooltip, x: x, y: y });
},
closeEditorTooltip:function() {
// summary: Close the tooltip.
array.forEach(['editTags','editShape'], function(b){
if (!registry.byId(b)) return;
registry.byId(b).type=''; // fix Safari issue
registry.byId(b).destroy(); // remove from registry so we can reinitialise next time
});
if (this.editortooltip) { dijit.popup.close(this.editortooltip); }
},
editTags:function(entity) {
// summary: Open a tag editor for the selected entity.
var tagEditor = new iD.tags.TagEditor(entity,this.controller);
this.closeEditorTooltip();
}
});
// ----------------------------------------------------------------------
+1 -1
View File
@@ -27,7 +27,7 @@ declare("iD.controller.edit.NoSelection", [iD.controller.edit.EditBaseState], {
this.inherited(arguments);
switch (entity.entityType) {
case 'node':
var ways=entity.parentWays();
var ways=entity.entity.parentWays();
if (!ways.length) { return new iD.controller.edit.SelectedPOINode(entity); }
else { return new iD.controller.edit.SelectedWayNode(entity,ways[0]); }
break;
+1 -1
View File
@@ -143,7 +143,7 @@ declare("iD.styleparser.ShapeStyle", [iD.styleparser.Style], {
};
},
shapeStrokeStyler:function() {
if (isNaN(this.casing_color)) { return { width:0 }; }
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
+52
View File
@@ -0,0 +1,52 @@
// iD/tags/PresetList.js
// List of presets for a given type (e.g. nodes, ways)
define(['dojo/_base/declare','dojo/_base/lang','dojo/_base/xhr'], function(declare,lang,xhr){
declare("iD.tags.PresetList", null, {
entityType: null,
presets: null,
constructor:function(_type,_url) {
// summary: List of presets for a given type (e.g. nodes, ways)
this.entityType=_type;
dojo.xhrGet({
url: _url,
handleAs: "json",
load: lang.hitch(this, this.loaded),
error: function(err) { console.log("Couldn't load presets"); }
});
},
loaded:function(_obj) {
this.presets=_obj;
console.log("Loaded presets for "+this.entityType);
},
assembleEditorsForEntity:function(_entity) {
if (_entity.entityType!=this.entityType) return false;
var presetList={};
var editorList=[];
for (var group in this.presets) {
for (var preset in this.presets[group]) {
var props=this.presets[group][preset];
if (_entity.matchesTags(props.tags)) {
presetList[preset]=props;
for (var i in props.editors) {
var editor=props.editors[i];
if (editorList.indexOf(editor)==-1) { editorList.push(editor); }
}
}
}
}
return { presets:presetList, editors: editorList };
}
});
// ----------------------------------------------------------------------
// End of module
});
+118
View File
@@ -0,0 +1,118 @@
// iD/tags/TagEditor.js
define(['dojo/_base/declare','dojo/_base/lang','dojo/_base/xhr','dojo/dom-construct',
'dijit/Dialog','dijit/form/Form','dijit/form/Button','dijit/form/TextBox'],
function(declare,lang,xhr,domConstruct){
declare("iD.tags.TagEditor", null, {
entity: null,
controller: null,
dialog: null,
editorContainers: null, // hash of DOM nodes to put editors in
constructor:function(_entity,_controller) {
// summary: Construct a tag editor dialog box.
console.log("TagEditor constructor");
this.entity=_entity;
this.controller=_controller;
this.editorContainers={};
// Create the dialog, and the form to put the editors in
this.dialog = new dijit.Dialog({ title: "My Dialog", content: "Test content.", style: "width: 300px" });
var form = new dijit.form.Form({ encType: 'multipart/form-data', action: '', method: '',
onSubmit: function(event) { console.log('submit'); }
}, dojo.doc.createElement('div'));
this.dialog.set('content',form);
this.dialog.show();
// What editors are relevant?
var presetList=this.controller.presets[_entity.entityType];
var applicablePresets=presetList.assembleEditorsForEntity(_entity);
// Add preset types
for (var i in applicablePresets.presets) {
this.appendPreset(i,applicablePresets.presets[i],form.domNode);
}
// Add each editor
for (var i in applicablePresets.editors) {
this.appendEditor(applicablePresets.editors[i],form.domNode);
}
},
// ------------
// Presets
appendPreset:function(_name,_preset,_destination) {
var element=domConstruct.create('h2');
element.appendChild(domConstruct.create('img', { src: 'presets/'+_preset.icon }));
element.appendChild(dojo.doc.createTextNode(_name));
_destination.appendChild(element);
},
// ------------
// Editors
appendEditor:function(_editor,_destination) {
// summary: Request an editor (cached if available, XHR if not), and call renderEditor when it's available.
if (this.controller.editorCache[_editor]) {
this.renderEditor(_editor,_destination);
} else {
dojo.xhrGet({
url: "presets/editors/"+_editor+".json",
handleAs: "json",
load: lang.hitch(this, this.loadedEditor, _editor, _destination),
error: function(err) { console.log("Couldn't load editor"); }
});
}
},
loadedEditor:function(_editor,_destination,_obj) {
// summary: Editor has loaded via XHR, so store it in the cache and render it.
this.controller.editorCache[_editor]=_obj;
this.renderEditor(_editor,_destination);
},
renderEditor:function(_editor,_destination) {
// summary: Render an editor as a form.
var editor=this.controller.editorCache[_editor];
// Add the subhead
var element=domConstruct.create('h3');
element.appendChild(dojo.doc.createTextNode(_editor));
_destination.appendChild(element);
// Add each form element
for (var label in editor) {
var item=editor[label];
var value=this.getTagValue(item.key);
element=domConstruct.create('div');
switch (item.type) {
case 'text':
var textbox = new dijit.form.TextBox({ name: item.key, value: value, type: 'text' }, domConstruct.create('input'));
element.appendChild(dojo.doc.createTextNode(label));
element.appendChild(textbox.domNode);
break;
case 'dropdown':
case 'relation':
case 'hidden':
}
_destination.appendChild(element);
}
// var submitbtn = new dijit.form.Button({ name: 'submit', type: 'submit', value: 'Submit', label: "Submit" }, dojo.doc.createElement('button'));
// var resetbtn = new dijit.form.Button({ type: 'reset', label: 'Reset' }, dojo.doc.createElement('button'));
// _destination.appendChild(submitbtn.domNode);
// _destination.appendChild(resetbtn.domNode);
},
getTagValue:function(k) {
// summary: Get the value of a tag for the current entity, or empty string if unset.
return this.entity.tags[k] ? this.entity.tags[k] : '';
},
});
// ----------------------------------------------------------------------
// End of module
});
+2
View File
@@ -0,0 +1,2 @@
{
}
+20
View File
@@ -0,0 +1,20 @@
{
'1': { key: 'type', value: 'route', type: 'hidden' },
'2': { key: 'route', value: 'bicycle', type: 'hidden' },
'Route name': {
key: 'name',
type: 'text'
},
'Route number': {
key: 'ref',
type: 'text'
},
'Network': {
key: 'network',
type: 'dropdown',
value: { 'National': 'ncn', 'Regional': 'rcn', 'Local': 'lcn' }
}
}
+12
View File
@@ -0,0 +1,12 @@
{
'Bikes allowed?': {
key: 'bicycle',
type: 'dropdown',
value: { 'Yes': 'yes', 'No': 'no', 'Permissive': 'permissive' }
},
'Cycle routes': {
type: 'relation',
editor: 'cycle_route'
}
}
+2
View File
@@ -0,0 +1,2 @@
{
}
+11
View File
@@ -0,0 +1,11 @@
{
'Road name': {
key: 'name',
type: 'text'
},
'Road number': {
key: 'ref',
type: 'text'
}
}
+25
View File
@@ -0,0 +1,25 @@
'Mapzen' lets you create and maintain OpenStreetMap maps.
Copyright (C) 2012 Cloud Made Ltd (CloudMade) mapzen@cloudmade.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the <organization> nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+2
View File
@@ -0,0 +1,2 @@
{
}
+53
View File
@@ -0,0 +1,53 @@
{
'Roads': {
'Motorway': {
tags: { highway: 'motorway' },
implied: { oneway: 'yes', bicycle: 'no', foot: 'no', horse: 'no' },
icon: 'ways/highway__motorway.png',
editors: ['road']
},
'Trunk road': {
tags: { highway: 'secondary' },
icon: 'ways/highway__secondary.png',
editors: ['road','cycling','bus']
},
'Primary road': {
tags: { highway: 'primary' },
icon: 'ways/highway__primary.png',
editors: ['road','cycling','bus']
},
'Secondary road': {
tags: { highway: 'secondary' },
icon: 'ways/highway__secondary.png',
editors: ['road','cycling','bus']
},
'Tertiary road': {
tags: { highway: 'tertiary' },
icon: 'ways/highway__tertiary.png',
editors: ['road','cycling','bus']
},
'Minor road': {
tags: { highway: 'unclassified' },
icon: 'ways/highway__unclassified.png',
editors: ['road','cycling','bus']
},
},
'Railways': {
'Railway': {
tags: { railway: 'rail' },
icon: 'ways/railway__rail.png',
editors: ['rail']
},
'Disused tracks': {
tags: { railway: 'disused' },
icon: 'ways/railway__disused.png',
editors: ['rail']
},
'Abandoned trackbed': {
tags: { railway: 'abandoned' },
icon: 'ways/railway__abandoned.png',
editors: ['rail']
},
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB