Merge branch 'master' into history
authorAndy Allan <andy@gravitystorm.co.uk>
Sat, 24 Mar 2012 11:42:44 +0000 (11:42 +0000)
committerAndy Allan <andy@gravitystorm.co.uk>
Sat, 24 Mar 2012 11:42:44 +0000 (11:42 +0000)
Conflicts:
net/systemeD/halcyon/connection/Entity.as
net/systemeD/halcyon/connection/XMLBaseConnection.as
net/systemeD/halcyon/connection/XMLConnection.as
net/systemeD/potlatch2/TagViewer.mxml
net/systemeD/potlatch2/controller/ControllerState.as

14 files changed:
TODO.txt
net/systemeD/halcyon/connection/Changeset.as
net/systemeD/halcyon/connection/Connection.as
net/systemeD/halcyon/connection/Entity.as
net/systemeD/halcyon/connection/EntityCollection.as
net/systemeD/halcyon/connection/Marker.as
net/systemeD/halcyon/connection/Node.as
net/systemeD/halcyon/connection/Relation.as
net/systemeD/halcyon/connection/Way.as
net/systemeD/halcyon/connection/XMLBaseConnection.as
net/systemeD/halcyon/connection/XMLConnection.as
net/systemeD/potlatch2/TagViewer.mxml
net/systemeD/potlatch2/controller/ControllerState.as
net/systemeD/potlatch2/history/HistoryDialog.mxml [new file with mode: 0644]

index 79f1076..fcb303b 100644 (file)
--- a/TODO.txt
+++ b/TODO.txt
 == l10n ==
 * Fix the en_US / default locale problem
 
+== history ==
+* Think about what happens if a node, which used to be part of a way and isn't any more, changes. Does that put a fake "version" in?
+
 == other ==
 * Simplify.as shouldn't add an action to the global undo stack, since it's called from e.g. importing into other layers
 
+
 Requested enhancements
 ----------------------
 
index bb1a2bf..de7d97a 100644 (file)
@@ -4,7 +4,7 @@ package net.systemeD.halcyon.connection {
                public static var entity_type:String = 'changeset';
 
         public function Changeset(connection:Connection, id:Number, tags:Object) {
-            super(connection, id, 0, tags, true, NaN, '');
+            super(connection, id, 0, tags, true, NaN, null, null);
         }
 
         public override function toString():String {
index 5c67ba5..94a9b05 100644 (file)
@@ -586,6 +586,7 @@ package net.systemeD.halcyon.connection {
         public function fetchUserTraces(refresh:Boolean=false):void {}
         public function fetchTrace(id:Number, callback:Function):void {}
         public function hasAccessToken():Boolean { return false; }
+        public function fetchHistory(entity:Entity, callback:Function):void {}
 
                public function loadEntity(entity:Entity):void {
                        loadEntityByID(entity.getType(),entity.id);
index e05462e..c5c6178 100644 (file)
@@ -14,6 +14,7 @@ package net.systemeD.halcyon.connection {
         private var _version:uint;
         private var _uid:Number;
         private var _timestamp:String;
+        private var _user:String;
         private var tags:Object = {};
         private var modified:Boolean = false;
         private var _loaded:Boolean = true;
@@ -25,12 +26,13 @@ package net.systemeD.halcyon.connection {
         /** Have all its parents (ie, relations that contain this object as a member, ways that contain this node) been loaded into memory */
         public var parentsLoaded:Boolean = true;
 
-        public function Entity(connection:Connection, id:Number, version:uint, tags:Object, loaded:Boolean, uid:Number, timestamp:String) {
+        public function Entity(connection:Connection, id:Number, version:uint, tags:Object, loaded:Boolean, uid:Number, timestamp:String, user:String) {
                        this._connection = connection;
             this._id = id;
             this._version = version;
             this._uid = uid;
             this._timestamp = timestamp;
+            this._user = user
             if (connection.cssTransform) tags=connection.cssTransform.run(this,tags);
             this.tags = tags;
                        this._loaded = loaded;
@@ -67,14 +69,19 @@ package net.systemeD.halcyon.connection {
             return _timestamp;
         }
 
+        /** The username who last edited this entity (from OSM API). */
+        public function get user():String {
+            return _user;
+        }
+
                /** Connection to which this entity belongs. */
                public function get connection():Connection {
                        return _connection;
                }
 
         /** Set a bunch of properties in one hit. Implicitly makes entity not deleted. */
-        public function updateEntityProperties(version:uint, tags:Object, loaded:Boolean, parentsLoaded:Boolean, uid:Number, timestamp:String):void {
-            _version=version; this.tags=tags; _loaded=loaded; this.parentsLoaded=parentsLoaded; _uid = uid; _timestamp = timestamp;
+        public function updateEntityProperties(version:uint, tags:Object, loaded:Boolean, parentsLoaded:Boolean, uid:Number, timestamp:String, user:String):void {
+            _version=version; this.tags=tags; _loaded=loaded; this.parentsLoaded=parentsLoaded; _uid = uid; _timestamp = timestamp; _user = user;
             deleted=false;
         }
 
index 0511662..1d036ab 100644 (file)
@@ -22,7 +22,7 @@ package net.systemeD.halcyon.connection {
                        var conn:Connection=entities[0].connection;
                        // ** FIXME: this really is a very nasty way of finding the connection
                        
-                       super(conn, -1, 0, {}, true, -1, "");
+                       super(conn, -1, 0, {}, true, NaN, null, null);
             _entities = entities;
                        
                        //To avoid firing on every contained entity, we wait some short time before firing the events
index e39c5c9..c95d483 100644 (file)
@@ -11,7 +11,7 @@ package net.systemeD.halcyon.connection {
         private var _lon:Number;
 
         public function Marker(connection:Connection, id:Number, version:uint, tags:Object, loaded:Boolean, lat:Number, lon:Number) {
-            super(connection, id, version, tags, loaded, 0, null);
+            super(connection, id, version, tags, loaded, NaN, null, null);
             this._lat = lat;
             this._latproj = lat2latp(lat);
             this._lon = lon;
index eaf5db1..16c6a63 100644 (file)
@@ -7,15 +7,15 @@ package net.systemeD.halcyon.connection {
         private var _latproj:Number;
         private var _lon:Number;
 
-        public function Node(connection:Connection, id:Number, version:uint, tags:Object, loaded:Boolean, lat:Number, lon:Number, uid:Number = NaN, timestamp:String = null) {
-            super(connection, id, version, tags, loaded, uid, timestamp);
+        public function Node(connection:Connection, id:Number, version:uint, tags:Object, loaded:Boolean, lat:Number, lon:Number, uid:Number = NaN, timestamp:String = null, user:String = null) {
+            super(connection, id, version, tags, loaded, uid, timestamp, user);
             this._lat = lat;
             this._latproj = lat2latp(lat);
             this._lon = lon;
         }
 
-               public function update(version:uint, tags:Object, loaded:Boolean, parentsLoaded:Boolean, lat:Number, lon:Number, uid:Number = NaN, timestamp:String = null):void {
-                       updateEntityProperties(version,tags,loaded,parentsLoaded,uid,timestamp); setLatLonImmediate(lat,lon);
+               public function update(version:uint, tags:Object, loaded:Boolean, parentsLoaded:Boolean, lat:Number, lon:Number, uid:Number = NaN, timestamp:String = null, user:String = null):void {
+                       updateEntityProperties(version,tags,loaded,parentsLoaded,uid,timestamp,user); setLatLonImmediate(lat,lon);
                }
 
         public function get lat():Number {
index 900877e..db6d435 100644 (file)
@@ -6,19 +6,19 @@ package net.systemeD.halcyon.connection {
         private var members:Array;
                public static var entity_type:String = 'relation';
 
-        public function Relation(connection:Connection, id:Number, version:uint, tags:Object, loaded:Boolean, members:Array, uid:Number = NaN, timestamp:String = null) {
-            super(connection, id, version, tags, loaded, uid, timestamp);
+        public function Relation(connection:Connection, id:Number, version:uint, tags:Object, loaded:Boolean, members:Array, uid:Number = NaN, timestamp:String = null, user:String = null) {
+            super(connection, id, version, tags, loaded, uid, timestamp, user);
             this.members = members;
                        for each (var member:RelationMember in members)
                            member.entity.addParent(this);
         }
 
-        public function update(version:uint, tags:Object, loaded:Boolean, parentsLoaded:Boolean, members:Array, uid:Number = NaN, timestamp:String = null):void {
+        public function update(version:uint, tags:Object, loaded:Boolean, parentsLoaded:Boolean, members:Array, uid:Number = NaN, timestamp:String = null, user:String = null):void {
                        var member:RelationMember;
                        for each (member in this.members)
                            member.entity.removeParent(this);
 
-                       updateEntityProperties(version,tags,loaded,parentsLoaded,uid,timestamp);
+                       updateEntityProperties(version,tags,loaded,parentsLoaded,uid,timestamp,user);
                        this.members=members;
                        for each (member in members)
                            member.entity.addParent(this);
index fcbd181..11d3214 100644 (file)
@@ -11,17 +11,17 @@ package net.systemeD.halcyon.connection {
                private var edge_b:Number;
                public static var entity_type:String = 'way';
 
-        public function Way(connection:Connection, id:Number, version:uint, tags:Object, loaded:Boolean, nodes:Array, uid:Number = NaN, timestamp:String = null) {
-            super(connection, id, version, tags, loaded, uid, timestamp);
+        public function Way(connection:Connection, id:Number, version:uint, tags:Object, loaded:Boolean, nodes:Array, uid:Number = NaN, timestamp:String = null, user:String = null) {
+            super(connection, id, version, tags, loaded, uid, timestamp, user);
             this.nodes = nodes;
                        for each (var node:Node in nodes) { node.addParent(this); }
                        calculateBbox();
         }
 
-               public function update(version:uint, tags:Object, loaded:Boolean, parentsLoaded:Boolean, nodes:Array, uid:Number = NaN, timestamp:String = null):void {
+               public function update(version:uint, tags:Object, loaded:Boolean, parentsLoaded:Boolean, nodes:Array, uid:Number = NaN, timestamp:String = null, user:String = null):void {
                        var node:Node;
                        for each (node in this.nodes) { node.removeParent(this); }
-                       updateEntityProperties(version,tags,loaded,parentsLoaded,uid,timestamp); this.nodes=nodes;
+                       updateEntityProperties(version,tags,loaded,parentsLoaded,uid,timestamp,user); this.nodes=nodes;
                        for each (node in nodes) { node.addParent(this); }
                        calculateBbox();
                }
index f8c0bfa..5de8112 100644 (file)
@@ -30,6 +30,7 @@ package net.systemeD.halcyon.connection {
                                var version:uint;
                                var uid:Number;
                                var timestamp:String;
+                               var user:String;
                                var tags:Object;
                                var node:Node, newNode:Node;
                                var unusedNodes:Object={};
@@ -51,6 +52,7 @@ package net.systemeD.halcyon.connection {
                                        version = uint(relData.@version);
                                        uid = Number(relData.@uid);
                                        timestamp = relData.@timestamp;
+                                       user = relData.@user;
                           
                                        var rel:Relation = getRelation(id);
                                        if ( rel == null || !rel.loaded || singleEntityRequest ) {
@@ -88,11 +90,11 @@ package net.systemeD.halcyon.connection {
                                                }
                                        
                                                if ( rel == null ) {
-                                                       rel=new Relation(this, id, version, tags, true, members, uid, timestamp);
+                                                       rel=new Relation(this, id, version, tags, true, members, uid, timestamp, user);
                                                        setRelation(rel, false);
                                                        createdEntities.push(rel);
                                                } else {
-                                                       rel.update(version, tags, true, false, members, uid, timestamp);
+                                                       rel.update(version, tags, true, false, members, uid, timestamp, user);
                                                        sendEvent(new EntityEvent(NEW_RELATION, rel), false);
                                                }
                                        }
@@ -109,9 +111,11 @@ package net.systemeD.halcyon.connection {
                                                                           Number(nodeData.@lat),
                                                                           Number(nodeData.@lon),
                                                                           Number(nodeData.@uid),
-                                                                          nodeData.@timestamp);
-                if ( inlineStatus ) { newNode.status = nodeData.@status; }
-                               
+                                                                          nodeData.@timestamp,
+                                                                          nodeData.@user);
+
+                    if ( inlineStatus ) { newNode.status = nodeData.@status; }
+
                                        if ( singleEntityRequest ) {
                                                // it's a revert request, so create/update the node
                                                setOrUpdateNode(newNode, true);
@@ -132,6 +136,7 @@ package net.systemeD.halcyon.connection {
                                        version = uint(data.@version);
                                        uid = Number(data.@uid);
                                        timestamp = data.@timestamp;
+                                       user = data.@user;
 
                                        var way:Way = getWay(id);
                                        if ( way == null || !way.loaded || singleEntityRequest) {
@@ -145,14 +150,14 @@ package net.systemeD.halcyon.connection {
                                                }
                                                tags = parseTags(data.tag);
                                                if ( way == null ) {
-                                                       way=new Way(this, id, version, tags, true, nodelist, uid, timestamp)
+                                                       way=new Way(this, id, version, tags, true, nodelist, uid, timestamp, user)
                                                        if ( inlineStatus ) { way.status = data.@status; }
                                                        setWay(way,false);
                                                        createdEntities.push(way);
                                                } else {
                                                        if (!way.loaded) createdEntities.push(way);
                                                        waycount++;
-                                                       way.update(version, tags, true, true, nodelist, uid, timestamp);
+                                                       way.update(version, tags, true, true, nodelist, uid, timestamp, user);
                                                        if ( inlineStatus ) { way.status = data.@status; }
                                                        sendEvent(new EntityEvent(NEW_WAY, way), false);
                                                }
@@ -174,7 +179,7 @@ package net.systemeD.halcyon.connection {
                        }
                }
 
-               private function parseTags(tagElements:XMLList):Object {
+               protected function parseTags(tagElements:XMLList):Object {
                        var tags:Object = {};
                        for each (var tagEl:XML in tagElements)
                                tags[tagEl.@k] = tagEl.@v;
index fc046c2..b177762 100644 (file)
@@ -9,6 +9,7 @@ package net.systemeD.halcyon.connection {
 
        import net.systemeD.halcyon.AttentionEvent;
        import net.systemeD.halcyon.MapEvent;
+       import net.systemeD.halcyon.ExtendedURLLoader;
     import net.systemeD.halcyon.connection.bboxes.*;
 
     /**
@@ -484,5 +485,72 @@ package net.systemeD.halcyon.connection {
                                }, errorOnMapLoad, mapLoadStatus); // needs error handlers
             dispatchEvent(new Event(LOAD_STARTED)); //specifc to map or reusable?
         }
+
+        /** Fetch the history for the given entity. The callback function will be given an array of entities of that type, representing the different versions */
+        override public function fetchHistory(entity:Entity, callback:Function):void {
+            if (entity.id >= 0) {
+              var request:URLRequest = new URLRequest(apiBaseURL + entity.getType() + "/" + entity.id + "/history");
+              var loader:ExtendedURLLoader = new ExtendedURLLoader();
+              loader.addEventListener(Event.COMPLETE, loadedHistory);
+              loader.addEventListener(IOErrorEvent.IO_ERROR, errorOnMapLoad); //needs error handlers
+              loader.addEventListener(HTTPStatusEvent.HTTP_STATUS, mapLoadStatus);
+              loader.info['callback'] = callback; //store the callback so we can use it later
+              loader.load(request);
+              dispatchEvent(new Event(LOAD_STARTED));
+            } else {
+              // objects created locally only have one state, their current one
+              callback([entity]);
+            }
+        }
+
+        private function loadedHistory(event:Event):void {
+            var _xml:XML = new XML(ExtendedURLLoader(event.target).data);
+            var results:Array = [];
+            var dummyConn:Connection = new Connection("dummy", null, null);
+
+            // only one type of entity should be returned, but this handles any
+
+            for each(var nodeData:XML in _xml.node) {
+                var newNode:Node = new Node(
+                    dummyConn,
+                    Number(nodeData.@id),
+                    uint(nodeData.@version),
+                    parseTags(nodeData.tag),
+                    true,
+                    Number(nodeData.@lat),
+                    Number(nodeData.@lon),
+                    Number(nodeData.@uid),
+                    nodeData.@timestamp,
+                    nodeData.@user
+                    );
+                results.push(newNode);
+            }
+
+            for each(var wayData:XML in _xml.way) {
+                var nodes:Array = [];
+                for each(var nd:XML in wayData.nd) {
+                  nodes.push(new Node(dummyConn,Number(nd.@ref), NaN, null, false, NaN, NaN));
+                }
+                var newWay:Way = new Way(
+                    dummyConn,
+                    Number(wayData.@id),
+                    uint(wayData.@version),
+                    parseTags(wayData.tag),
+                    true,
+                    nodes,
+                    Number(wayData.@uid),
+                    wayData.@timestamp,
+                    wayData.@user
+                    );
+                results.push(newWay);
+            }
+
+            for each(var relData:XML in _xml.relation) {
+                trace("relation history not implemented");
+            }
+
+            // use the callback we stored earlier, and pass it the results
+            ExtendedURLLoader(event.target).info['callback'](results);
+        }
        }
 }
index b6494e5..dd1fefb 100644 (file)
@@ -78,7 +78,7 @@
         </mx:VBox>
       </mx:VBox>
       <mx:VBox width="100%" height="100%" label="Advanced" id="advancedContainer" initialize="checkAdvanced()" verticalGap="1">
-        <mx:Label id="advancedID" click="openEntityPage()">
+        <mx:Label id="advancedID" click="new HistoryDialog().init(selectedEntity);">
           <mx:htmlText><![CDATA[<i>No Selection</i>]]></mx:htmlText>
         </mx:Label>
 
       import net.systemeD.halcyon.MapPaint;
       import net.systemeD.potlatch2.EditController;
       import net.systemeD.potlatch2.mapfeatures.*;
+      import net.systemeD.potlatch2.history.HistoryDialog;
       import net.systemeD.potlatch2.mapfeatures.editors.*;
       import net.systemeD.potlatch2.utils.*;
       import net.systemeD.controls.CollapsiblePanel;
               navigateToURL(new URLRequest(feature.helpURL), "potlatch_help");
       }
 
-      /** Open up a new browser page showing OSM's view of the current entity. */
-      public function openEntityPage():void {
-          if (selectedEntity != null && selectedEntity.id >= 0) {
-              // This is slightly hard-coded, but not drastically. The ../s could be changed for string manipulation of the apiBase
-              var urlBase:String = connection.apiBase + '../../browse/'
-              navigateToURL(new URLRequest(urlBase+selectedEntity.getType()+'/'+selectedEntity.id), "potlatch_browse");
-          }
-      }
-
       public function addToRelation():void {
           new RelationSelectPanel().init(selectedEntity,new Object());
       }
index e9270a0..048f122 100644 (file)
@@ -4,8 +4,10 @@ package net.systemeD.potlatch2.controller {
     import net.systemeD.halcyon.Map;
     import net.systemeD.halcyon.MapPaint;
     import net.systemeD.halcyon.connection.*;
+    import net.systemeD.halcyon.AttentionEvent;
     import net.systemeD.potlatch2.collections.Imagery;
     import net.systemeD.potlatch2.EditController;
+    import net.systemeD.potlatch2.history.HistoryDialog;
        import net.systemeD.potlatch2.save.SaveManager;
        import net.systemeD.potlatch2.utils.SnapshotConnection;
        import flash.ui.Keyboard;
@@ -80,6 +82,7 @@ package net.systemeD.potlatch2.controller {
                                case 67:        editableLayer.connection.closeChangeset(); break;                                               // C - close changeset
                                case 68:        editableLayer.alpha=1.3-editableLayer.alpha; return null;                               // D - dim
                                case 71:        FlexGlobals.topLevelApplication.trackLoader.load(); break;                              // G - GPS tracks **FIXME: move from Application to Map
+                case 72:    showHistory(); break;                                                   // H - History
                                case 83:        SaveManager.saveChanges(editableLayer.connection); break;                               // S - save
                                case 84:        controller.tagViewer.togglePanel(); return null;                                                // T - toggle tags panel
                                case 90:        if (!event.shiftKey) { MainUndoStack.getGlobalStack().undo(); return null;}// Z - undo
@@ -199,8 +202,6 @@ package net.systemeD.potlatch2.controller {
                        MainUndoStack.getGlobalStack().addAction(undo);
                         controller.updateSelectionUI();
                        object.resume();
-
-
                }
                
                /** Remove all tags from current selection. */
@@ -217,6 +218,17 @@ package net.systemeD.potlatch2.controller {
                        for each (item in _selection) item.resume();
                }
 
+        /** Show the history dialog, if only one object is selected. */
+        protected function showHistory():void {
+            if (selectCount == 1) {
+                new HistoryDialog().init(firstSelected);
+            } else if (selectCount == 0) {
+                controller.dispatchEvent(new AttentionEvent(AttentionEvent.ALERT, null, "Can't show history, nothing selected"));
+            } else {
+                controller.dispatchEvent(new AttentionEvent(AttentionEvent.ALERT, null, "Can't show history, multiple objects selected"));
+            }
+        }
+
                /** Create an action to add "source=*" tag to current entity based on background imagery. This is a convenient shorthand for users. */
                protected function setSourceTag():void {
                        if (selectCount!=1) { return; }
diff --git a/net/systemeD/potlatch2/history/HistoryDialog.mxml b/net/systemeD/potlatch2/history/HistoryDialog.mxml
new file mode 100644 (file)
index 0000000..5f41f87
--- /dev/null
@@ -0,0 +1,240 @@
+<?xml version="1.0" encoding="utf-8"?>
+<mx:TitleWindow
+        xmlns:mx="http://www.adobe.com/2006/mxml"
+        xmlns:help="net.systemeD.potlatch2.help.*"
+        layout="vertical" showCloseButton="true"
+        horizontalAlign="center" title="History"
+        width="600" height="400"
+        verticalGap="0">
+
+  <mx:VBox width="100%" height="100%">
+    <mx:HBox width="100%">
+      <mx:Text text="History for {entity.getType()} {entity.id}" selectable="false" width="50%" />
+      <mx:Text text="Loading data..." visible="{ entityStates == null }" selectable="false" />
+    </mx:HBox>
+    <mx:HBox width="100%" height="100%">
+      <mx:DataGrid dataProvider="{entityStates}" width="100%" height="100%" enabled="{ entityStates != null }">
+        <mx:columns>
+          <mx:DataGridColumn editable="false" dataField="version" headerText="version" width="50" textAlign="center"/>
+          <mx:DataGridColumn editable="false" dataField="timestamp" headerText="timestamp"/>
+          <mx:DataGridColumn editable="false" dataField="user" headerText="username" />
+          <mx:DataGridColumn editable="false">
+            <mx:itemRenderer>
+              <mx:Component>
+                <mx:HBox horizontalAlign="center" verticalAlign="middle">
+                  <mx:Button label="Contact User"
+                    click="parentDocument.message(data)"/>
+                </mx:HBox>
+              </mx:Component>
+            </mx:itemRenderer>
+          </mx:DataGridColumn>
+        </mx:columns>
+      </mx:DataGrid>
+    </mx:HBox>
+  </mx:VBox>
+
+
+  <mx:ControlBar>
+    <mx:Button label="Revert" enabled="false" styleName="titleWindowButton" />
+    <mx:Spacer width="100%"/>
+    <mx:Button label="More Details..." enabled="{entity.id >= 0}" click="openEntityPage()" styleName="titleWindowButton" />
+    <mx:Button label="Cancel" click="PopUpManager.removePopUp(this);" styleName="titleWindowButton" />
+  </mx:ControlBar>
+
+  <mx:Script><![CDATA[
+
+    import mx.managers.PopUpManager;
+    import mx.core.Application;
+    import mx.events.CloseEvent;
+    import flash.events.Event;
+    import com.adobe.utils.ArrayUtil;
+
+    import net.systemeD.halcyon.connection.*
+
+    // This is the entity that we're requesting the history for.
+    [Bindable]
+    private var entity:Entity;
+
+    // These are the various states that the entity as been in - so is a list
+    // of Nodes (all with the same id) or Ways etc
+    [Bindable]
+    private var entityStates:Array;
+
+    // store intermediate states for ways
+    private var wayStates:Array; // an array of ways
+    private var wayNodeStates:Array; // an array of arrays of nodes
+
+    // the number of outstanding asynchronous node history requests,
+    // so we know when all have been fetched
+    private var pendingNodeFetches:uint;
+
+    public function init(e:Entity):void {
+        if (e == null) {return;}
+
+        PopUpManager.addPopUp(this, Application(Application.application), true);
+        PopUpManager.centerPopUp(this);
+        this.addEventListener(CloseEvent.CLOSE, historyDialog_close);
+
+        entity = e;
+        fetchHistory();
+    }
+
+    private function historyDialog_close(evt:CloseEvent):void {
+        PopUpManager.removePopUp(this);
+    }
+
+    /** Open up a new browser page showing OSM's view of the current entity. */
+    private function openEntityPage():void {
+        if (entity != null && entity.id >= 0) {
+            // This is slightly hard-coded, but not drastically. The ../s could be changed for string manipulation of the apiBase
+            var urlBase:String = entity.connection.apiBase + '../../browse/';
+            navigateToURL(new URLRequest(urlBase+entity.getType()+'/'+entity.id), "potlatch_browse");
+        }
+    }
+
+    private function fetchHistory():void {
+        if (entity is Node) {
+            entity.connection.fetchHistory(entity, processNode);
+        } else if (entity is Way) {
+            entity.connection.fetchHistory(entity, processWay);
+        } else {
+            // not implemented
+        }
+    }
+
+    private function processNode(results:Array):void {
+        // Simply copy the nodes into the states array
+        // todo sorting or somesuch
+        entityStates = results.reverse();
+    }
+
+    private function processWay(results:Array):void {
+        // This is much more complicated that nodes.
+        // In potlatch(2) we show the user the number of different states, bearing in mind
+        // node movements (and tag changes?).
+
+        wayStates = results;
+
+        // figure out the list of nodes that have ever been involved in the way, and fetch them.
+        // pendingNode will store each one, and trigger an event when they are all downloaded.
+        wayNodeStates = [];
+        addEventListener("pendingNodesAllFetched", processWayStates);
+
+        var nodes:Object = {};
+        var count:uint = 0;
+
+        for each(var oldWay:Way in results) {
+            for (var i:uint = 0; i< oldWay.length; i++) {
+                var node:Node = oldWay.getNode(i);
+                if(!nodes[node.id]) {
+                    nodes[node.id] = node;
+                    count++;
+                }
+            }
+        }
+
+        pendingNodeFetches = count;
+
+        for each (var n:Node in nodes) {
+            entity.connection.fetchHistory(n, pendingNode);
+        }
+    }
+
+    // Callback for fetching a node history as part of a way; when there's no outstanding
+    // nodes remaining this will trigger an event.
+    private function pendingNode(results:Array):void {
+        wayNodeStates.push(results)
+        pendingNodeFetches--;
+        trace("fetched node "+results[0].id+" , "+pendingNodeFetches+" more to go");
+        if (pendingNodeFetches == 0) {
+            dispatchEvent(new Event("pendingNodesAllFetched"));
+        }
+    }
+
+    private function processWayStates(e:Event):void {
+        // we now have all the node histories for
+        // for each node that has ever been part of the way.
+
+        // Build a list of every timestamp of interest, along with who
+        // the person was that triggered that timestamp (either from a way version
+        // change, or from changing a node.
+        var revdates:Array = [];
+        var revusers:Object = {};
+
+        for each (var way:Way in wayStates) {
+            revdates.push(way.timestamp);
+            revusers[way.timestamp] = way.user;
+        }
+
+        for each (var nodeStates:Array in wayNodeStates) {
+            for each (var node:Node in nodeStates) {
+                revdates.push(node.timestamp);
+                revusers[node.timestamp] = node.user;
+            }
+        }
+
+        // sort the dates and remove duplicates and those before the first version of the way
+
+        revdates = revdates.sort();
+        revdates = ArrayUtil.createUniqueCopy(revdates); // (corelib) de-duplicates
+        revdates = revdates.filter(function(e:*, i:int, arr:Array):Boolean { return e >= wayStates[0].timestamp});
+
+        var version:int = 1;
+        var subversion:int = 0;
+        var es:Array = []; // entityStates
+
+        for each (var revdate:String in revdates) {
+          var entitystate:Object = {};
+
+          var w:Way = getEntityAtDate(wayStates, revdate) as Way;
+
+          if (w.version == version) {
+              subversion++;
+          } else {
+              version = w.version;
+              subversion = 1;
+          }
+
+          //for (i = 0, i < w.length; i++) {
+          //    var generalNode:Node = w.getNode(i);
+          //    var specificNode:Node = getEntityAtDate(wayNodeStates[generalNode.id], revdate);
+          //    where was I going with this? Oh, yes, it'll be needed for building the object to revert to.
+          //}
+
+          entitystate.version = String(version) + "." + String(subversion);
+          entitystate.timestamp = revdate;
+          entitystate.user = revusers[revdate];
+          es.push(entitystate)
+        }
+
+        entityStates = es.reverse();
+    }
+
+    // given a list of entities sorted with oldest first, find the last version before that date.
+    private function getEntityAtDate(list:Array, date:String):Entity {
+        trace("find for date : "+date);
+        for(var i:int = list.length-1; i >= 0; i--) {
+            var entity:Entity = list[i];
+            trace (entity.timestamp + " : " + date);
+            if (entity.timestamp <= date) {
+                trace("returning version " + entity.version);
+                return entity;
+            }
+        }
+        trace("ERROR");
+        return null;
+    }
+
+    public function message(entity:Entity):void {
+        if (entity.user != null) {
+            var urlBase:String = entity.connection.apiBase + '../../message/new/';
+            navigateToURL(new URLRequest(urlBase+entity.user), "potlatch_message");
+        }
+    }
+    ]]>
+  </mx:Script>
+
+
+</mx:TitleWindow>
+
+