add support for displaying history
[rails.git] / app / views / browse / start.rjs
1 page.replace_html :sidebar_title, 'Data'
2 page.replace_html :sidebar_content, :partial => 'start'
3 page << <<EOJ
4     
5     var gml, sf, objList, currentFeature, featureList, mode = "auto", currentBounds;
6     OpenLayers.Feature.Vector.style['default'].strokeWidth = 3;
7     OpenLayers.Feature.Vector.style['default'].cursor = "pointer";
8     
9     function start() {
10         openSidebar({ onclose: stopBrowse });
11         var vectors = new OpenLayers.Layer.Vector();
12     
13         box = new OpenLayers.Control.DrawFeature(vectors, OpenLayers.Handler.RegularPolygon, { 
14           handlerOptions: {
15             sides: 4,
16             snapAngle: 90,
17             irregular: true,
18             persist: true,
19             callbacks: { done: endDrag }
20           }
21         });
22         map.addControl(box);
23         map.events.register("moveend", map, showData);
24         map.events.triggerEvent("moveend");
25     }
26
27     function showData() {
28         if (mode == "manual") { return; }
29         if (map.getZoom() >= 15) {
30             useMap();
31         } else {
32             $("status").innerHTML = "Zoom in or Select an area of the map to view.";
33         }    
34     }
35     
36     function stopBrowse() {
37         if (gml) {
38             gml.destroy();
39             gml = null; 
40         } 
41         if (sf) {   
42             sf.destroy();  
43             sf = null;
44         } 
45         if (currentFeature) {
46             currentFeature.destroy(); 
47             currentFeature = null; 
48         } 
49         map.events.unregister("moveend", map, showData);
50     }
51     
52     function startDrag() {
53       $("drag_box").innerHTML='Drag a box on the map to select an area';
54        box.activate(); 
55        return false;
56     };
57     $("drag_box").onclick = startDrag;
58     
59     function useMap() {
60         var bounds = map.getExtent();
61         var projected = bounds.clone().transform(map.getProjectionObject(), epsg4326);
62         if (!currentBounds || !currentBounds.containsBounds(projected)) {
63             var center = bounds.getCenterLonLat();
64             var tileWidth = bounds.getWidth() * 1.2;
65             var tileHeight = bounds.getHeight() * 1.2;
66             var tileBounds =
67                 new OpenLayers.Bounds(center.lon - (tileWidth / 2),
68                                       center.lat - (tileHeight / 2),
69                                       center.lon + (tileWidth / 2),
70                                       center.lat + (tileHeight / 2));
71
72             currentBounds = tileBounds;
73             getData(tileBounds);
74             mode = "auto";
75             $("drag_box").style.display="inline";
76             $("use_map").style.display="none";
77         }
78         return false;
79     }
80     $("use_map").onclick = useMap;
81     
82     function endDrag(bbox) {
83         var bounds = bbox.getBounds();
84         box.deactivate();
85         getData(bounds);
86         $("drag_box").innerHTML = "Manually select a different area";
87         mode = "manual";  
88         $("use_map").style.display="inline";
89         $("drag_box").style.display="none";
90     }
91     
92     function displayFeatureWarning() {
93         $("status").innerHTML = "";
94         var div = document.createElement("div");
95         var p = document.createElement("p");
96         p.appendChild(document.createTextNode("You have loaded an area which contains " + featureList.length + " features. In general, some browsers may not cope well with displaying this quantity of data. Generally, browsers work best at displaying less than 100 features at a time: doing anything else may make your browser slow/unresponsive. If you are sure you want to display this data, you may do so by clicking the button below.")); 
97         div.appendChild(p);
98         var input = document.createElement("input");
99         input.type = "submit";
100         input.value = "Load Data";
101         input.onclick = loadFeatureList;
102         div.appendChild(input); 
103         $("object").innerHTML="";
104         $("object").appendChild(div);
105     }
106     
107     function loadFeatureList() {
108         gml.addFeatures(featureList);
109         gml.events.triggerEvent("loadend");
110         return false;
111     }    
112
113     function customDataLoader(request) { 
114         var doc = request.responseXML;
115         
116         if (!doc || !doc.documentElement) {
117             doc = request.responseText;
118         }
119         
120         var options = {};
121         
122         OpenLayers.Util.extend(options, this.formatOptions);
123         if (this.map && !this.projection.equals(this.map.getProjectionObject())) {
124             options.externalProjection = this.projection;
125             options.internalProjection = this.map.getProjectionObject();
126         }    
127         
128         var gml = this.format ? new this.format(options) : new OpenLayers.Format.GML(options);
129         var features = gml.read(doc);
130         if (!this.maxFeatures || features.length <= this.maxFeatures) {
131             this.addFeatures(features);
132             this.events.triggerEvent("loadend");
133             featureList = []; 
134         } else {
135             featureList = features;
136             displayFeatureWarning();
137         }
138     }
139
140     function getData(bounds) {
141         
142         bounds.transform(new OpenLayers.Projection("EPSG:900913"), new OpenLayers.Projection("EPSG:4326"));
143         var size = bounds.getWidth() * bounds.getHeight(); 
144         if (size > 0.25) {
145             $("status").innerHTML = "Unable to load: Bounding box size of " + size + " is too large. (Must be smaller than 0.25)<br />"; 
146             return;
147         }
148
149         var url = "/api/0.5/map?bbox="+bounds.toBBOX();
150         
151         loadGML(url);
152     }
153     function loadGML(url) {
154         $("status").innerHTML = "Loading...";
155         $("object").innerHTML = "";
156         if (!gml) {
157             var style = new OpenLayers.Style();
158             style.addRules([new OpenLayers.Rule( 
159               {'symbolizer': 
160                 {"Polygon": {'fillColor': '#ff0000', 'strokeColor': '#ff0000'},
161                  "Line": {'fillColor': '#ffff00', 'strokeColor': '#000000', strokeOpacity: '0.4'},
162                  "Point": {'fillColor': '#00ff00', 'strokeColor': '#00ff00'}}
163               }
164             )]);
165             gml = new OpenLayers.Layer.GML("Data",url, 
166                     {format: OpenLayers.Format.OSM, formatOptions: {checkTags: true},
167                      maxFeatures: 100, requestSuccess: customDataLoader,
168                      styleMap: new OpenLayers.StyleMap({'default': style, 'select': {'strokeColor': '#0000ff', strokeWidth: 8}})
169                     }
170             );
171             gml.events.register("loadend", gml, dataLoaded );
172             map.addLayer(gml);
173             
174             sf = new OpenLayers.Control.SelectFeature(gml, {'onSelect': onFeatureSelect});
175             sf.handler.stopDown = false;
176             sf.handler.stopUp = false;
177             map.addControl(sf);
178             sf.activate();
179              
180         } else {
181             gml.setUrl(url);
182         }
183
184         currentFeature = null;
185     }
186     function dataLoaded() { 
187         $("status").innerHTML = "Loaded " + this.features.length + " features. (<a href='"+ this.url+"'>API</a>)";
188         
189         objList = document.createElement("ul");
190         for (var i = 0; i < this.features.length; i++) {
191             var feature = this.features[i]; 
192             
193             // Type, for linking
194             var type ="way";
195             if (feature.geometry.CLASS_NAME == "OpenLayers.Geometry.Point") {
196                 type = "node";
197             }   
198             var nice_name = type.substr(0,1).toUpperCase() + type.substr(1,type.length); 
199             var li = document.createElement("li");
200             li.appendChild(document.createTextNode(nice_name + " "));
201             
202             // Link, for viewing in the tab
203             var link = document.createElement("a");
204             link.href =  "/browse/" + type + "/" + feature.osm_id; 
205             var name = feature.attributes.name || feature.osm_id;
206             link.appendChild(document.createTextNode(name));
207             link.feature = feature;
208             link.onclick = OpenLayers.Function.bind(viewFeatureLink, link);   
209             li.appendChild(link);
210
211             objList.appendChild(li);
212         }
213         $("object").innerHTML = "";
214         $("object").appendChild(objList); 
215     }
216     
217     function viewFeatureLink() {
218         var layer = this.feature.layer;
219         for (var i = 0; i < layer.selectedFeatures.length; i++) {
220             var f = layer.selectedFeatures[i]; 
221             layer.drawFeature(f, layer.styleMap.createSymbolizer(f, "default"));
222         }
223         onFeatureSelect(this.feature);
224         if (mode != "auto") {
225             map.setCenter(this.feature.geometry.getBounds().getCenterLonLat()); 
226         }
227         return false;
228     }
229     
230     function loadObjList() {
231         $("object").innerHTML="";
232         $("object").appendChild(objList);
233         return false;
234     }
235       
236     function onFeatureSelect(feature) {
237         // Unselect previously selected feature
238         if (currentFeature) {
239           currentFeature.layer.drawFeature(
240             currentFeature, currentFeature.layer.styleMap.createSymbolizer(currentFeature, "default")
241           );
242         }
243
244         // Redraw in selected style
245         feature.layer.drawFeature(
246           feature, feature.layer.styleMap.createSymbolizer(feature, "select")
247         );
248
249         // If the current object is the list, don't innerHTML="", since that could clar it.   
250         if ($("object").firstChild == objList) { 
251             $("object").removeChild(objList);
252         } else { 
253             $("object").innerHTML = "";
254         }   
255         
256         // Create a link back to the object list
257         var div = document.createElement("div");
258         var link = document.createElement("a");
259         link.href="#";
260         link.onclick = loadObjList;
261         link.appendChild(document.createTextNode("Back to Object List"));
262         div.appendChild(link)
263         $("object").appendChild(div);    
264         
265         // Now the list of attributes
266         var ul = document.createElement("ul");
267         var type = "way";
268         if (feature.geometry.CLASS_NAME == "OpenLayers.Geometry.Point") {
269             type = "node";
270         }    
271         var li = document.createElement("li");
272         var link = document.createElement("a");   
273         link.href =  "/browse/"+type+"/"+feature.osm_id;
274         link.appendChild(document.createTextNode(feature.osm_id));
275         li.appendChild(link);
276         ul.appendChild(li);
277         for (var key in feature.attributes) {
278             var li = document.createElement("li");
279             var b = document.createElement("b");
280             b.appendChild(document.createTextNode(key));
281             li.appendChild(b);
282             li.appendChild(document.createTextNode(": " + feature.attributes[key]));
283             ul.appendChild(li);
284         }
285         
286         var li = document.createElement("li");
287         var link = document.createElement("a");   
288         link.href =  "/browse/"+type+"/"+feature.osm_id+"/history";
289         link.appendChild(document.createTextNode("History"));
290         li.appendChild(link);
291         ul.appendChild(li);
292         link.onclick = OpenLayers.Function.bind(loadHistory, {type: type, feature: feature, link: link});
293         $("object").appendChild(ul);
294         
295         
296         // Stash the currently drawn feature
297         currentFeature = feature; 
298     }   
299     function loadHistory() {
300         this.link.href = "";
301         this.link.innerHTML = "Wait...";
302         new Ajax.Request("/api/0.5/"+this.type+"/"+this.feature.osm_id+"/history", {onComplete: OpenLayers.Function.bind(displayHistory, this)});
303         return false;
304     }
305     function displayHistory(request) {
306         if (currentFeature.osm_id != this.feature.osm_id || $("object").firstChild == objList)  { 
307             return false;
308         } 
309         this.link.parentNode.parentNode.removeChild(this.link.parentNode);
310         var doc = request.responseXML;
311         var div = document.createElement("div");
312         var h3 = document.createElement("h3"); 
313         h3.appendChild(document.createTextNode("History"));
314         div.appendChild(h3);
315         var nodes = doc.getElementsByTagName(this.type);
316         var history = document.createElement("ul");  
317         for (var i = 0; i < nodes.length; i++) {
318             var user = nodes[i].getAttribute("user") || "private user";
319             var timestamp = nodes[i].getAttribute("timestamp");
320             var item = document.createElement("li");
321             item.appendChild(document.createTextNode("Edited by " + user + " at " + timestamp));
322             history.appendChild(item);
323         }
324         div.appendChild(history);
325         $("object").appendChild(div); 
326     }
327     
328     start();
329 EOJ