Merge 8377:8381 from trunk.
[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   var browseBoxControl;
5   var browseActive;
6   var browseMode = "auto";
7   var browseBounds;
8   var browseFeatureList;
9   var browseActiveFeature;
10   var browseDataLayer;
11   var browseSelectControl;
12   var browseObjectList;
13
14   OpenLayers.Feature.Vector.style['default'].strokeWidth = 3;
15   OpenLayers.Feature.Vector.style['default'].cursor = "pointer";
16     
17   function startBrowse() {
18     openSidebar({ onclose: stopBrowse });
19
20     var vectors = new OpenLayers.Layer.Vector();
21     
22     browseBoxControl = new OpenLayers.Control.DrawFeature(vectors, OpenLayers.Handler.RegularPolygon, { 
23       handlerOptions: {
24         sides: 4,
25         snapAngle: 90,
26         irregular: true,
27         persist: true,
28         callbacks: { done: endDrag }
29       }
30     });
31     map.addControl(browseBoxControl);
32
33     map.events.register("moveend", map, showData);
34     map.events.triggerEvent("moveend");
35
36     browseActive = true;
37   }
38
39   function showData() {
40     if (browseMode == "auto") {
41       if (map.getZoom() >= 15) {
42           useMap();
43       } else {
44           setStatus("Zoom in or select an area of the map to view");
45       }    
46     }
47   }
48
49   function stopBrowse() {
50     if (browseActive) {
51       browseActive = false;
52
53       if (browseDataLayer) {
54         browseDataLayer.destroy();
55         browseDataLayer = null; 
56       } 
57
58       if (browseSelectControl) {   
59         browseSelectControl.destroy();  
60         browseSelectControl = null;
61       } 
62
63       if (browseBoxControl) {
64         browseBoxControl.destroy();
65         browseBoxControl = null;
66       }         
67
68       if (browseActiveFeature) {
69         browseActiveFeature.destroy(); 
70         browseActiveFeature = null; 
71       }
72
73       map.dataLayer.setVisibility(false);
74       map.events.unregister("moveend", map, showData);
75     }    
76   }
77     
78   function startDrag() {
79     $("browse_select_box").innerHTML='Drag a box on the map to select an area';
80
81     browseBoxControl.activate();
82
83     return false;
84   };
85
86   $("browse_select_box").onclick = startDrag;
87     
88   function useMap() {
89     var bounds = map.getExtent();
90     var projected = bounds.clone().transform(map.getProjectionObject(), epsg4326);
91
92     if (!browseBounds || !browseBounds.containsBounds(projected)) {
93       var center = bounds.getCenterLonLat();
94       var tileWidth = bounds.getWidth() * 1.2;
95       var tileHeight = bounds.getHeight() * 1.2;
96       var tileBounds = new OpenLayers.Bounds(center.lon - (tileWidth / 2),
97                                              center.lat - (tileHeight / 2),
98                                              center.lon + (tileWidth / 2),
99                                              center.lat + (tileHeight / 2));
100
101       browseBounds = tileBounds;
102       getData(tileBounds);
103
104       browseMode = "auto";
105
106       $("browse_select_view").style.display = "none";
107     }
108
109     return false;
110   }
111
112   $("browse_select_view").onclick = useMap;
113     
114   function endDrag(bbox) {
115     var bounds = bbox.getBounds();
116     var projected = bounds.clone().transform(map.getProjectionObject(), epsg4326);
117
118     browseBoxControl.deactivate();
119     browseBounds = projected;
120     getData(bounds);
121
122     browseMode = "manual";  
123
124     $("browse_select_box").innerHTML = "Manually select a different area";
125     $("browse_select_view").style.display = "inline";
126   }
127     
128   function displayFeatureWarning() {
129     clearStatus();
130
131     var div = document.createElement("div");
132
133     var p = document.createElement("p");
134     p.appendChild(document.createTextNode("You have loaded an area which contains " + browseFeatureList.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."));
135     div.appendChild(p);
136
137     var input = document.createElement("input");
138     input.type = "submit";
139     input.value = "Load Data";
140     input.onclick = loadFeatureList;
141     div.appendChild(input); 
142
143     $("browse_content").innerHTML = "";
144     $("browse_content").appendChild(div);
145   }
146     
147   function loadFeatureList() {
148     browseDataLayer.addFeatures(browseFeatureList);
149     browseDataLayer.events.triggerEvent("loadend");
150
151     browseFeatureList = []; 
152
153     return false;
154   }    
155
156   function customDataLoader(request) { 
157     if (browseActive) {
158       var doc = request.responseXML;
159
160       if (!doc || !doc.documentElement) {
161         doc = request.responseText;
162       }
163
164       var options = {};
165
166       OpenLayers.Util.extend(options, this.formatOptions);
167
168       if (this.map && !this.projection.equals(this.map.getProjectionObject())) {
169         options.externalProjection = this.projection;
170         options.internalProjection = this.map.getProjectionObject();
171       }    
172
173       var gml = this.format ? new this.format(options) : new OpenLayers.Format.GML(options);
174
175       browseFeatureList = gml.read(doc);
176
177       if (!this.maxFeatures || browseFeatureList.length <= this.maxFeatures) {
178         loadFeatureList();
179       } else {
180         displayFeatureWarning();
181       }
182     }
183   }
184
185   function getData(bounds) {
186     var projected = bounds.clone().transform(new OpenLayers.Projection("EPSG:900913"), new OpenLayers.Projection("EPSG:4326"));
187     var size = projected.getWidth() * projected.getHeight();
188
189     if (size > 0.25) {
190       setStatus("Unable to load: Bounding box size of " + size + " is too large (must be smaller than 0.25)");
191     } else {
192       loadGML("/api/0.5/map?bbox=" + projected.toBBOX());
193     }
194   }
195
196   function loadGML(url) {
197     setStatus("Loading...");
198     $("browse_content").innerHTML = "";
199
200     if (!browseDataLayer) {
201       var style = new OpenLayers.Style();
202
203       style.addRules([new OpenLayers.Rule({
204         symbolizer: {
205           Polygon: { fillColor: '#ff0000', strokeColor: '#ff0000' },
206           Line: { fillColor: '#ffff00', strokeColor: '#000000', strokeOpacity: '0.4' },
207           Point: { fillColor: '#00ff00', strokeColor: '#00ff00' }
208         }
209       })]);
210
211       browseDataLayer = new OpenLayers.Layer.GML("Data", url, {
212         format: OpenLayers.Format.OSM,
213         formatOptions: { checkTags: true },
214         maxFeatures: 100,
215         requestSuccess: customDataLoader,
216         displayInLayerSwitcher: false,
217         styleMap: new OpenLayers.StyleMap({
218           default: style,
219           select: { strokeColor: '#0000ff', strokeWidth: 8 }
220         })
221       });
222       browseDataLayer.events.register("loadend", browseDataLayer, dataLoaded );
223       map.addLayer(browseDataLayer);
224             
225       browseSelectControl = new OpenLayers.Control.SelectFeature(browseDataLayer, { onSelect: onFeatureSelect });
226       browseSelectControl.handler.stopDown = false;
227       browseSelectControl.handler.stopUp = false;
228       map.addControl(browseSelectControl);
229       browseSelectControl.activate();
230     } else {
231       browseDataLayer.setUrl(url);
232     }
233
234     browseActiveFeature = null;
235   }
236
237   function dataLoaded() {
238     if (browseActive) {
239       clearStatus();
240         
241       browseObjectList = document.createElement("div")
242
243       var heading = document.createElement("p");
244       heading.className = "browse_heading";
245       heading.appendChild(document.createTextNode("Object list"));
246       browseObjectList.appendChild(heading);
247
248       var list = document.createElement("ul");
249
250       for (var i = 0; i < this.features.length; i++) {
251         var feature = this.features[i]; 
252             
253         // Type, for linking
254         var type = featureType(feature);
255         var typeName = ucFirst(type);
256         var li = document.createElement("li");
257         li.appendChild(document.createTextNode(typeName + " "));
258             
259         // Link, for viewing in the tab
260         var link = document.createElement("a");
261         link.href =  "/browse/" + type + "/" + feature.osm_id; 
262         var name = feature.attributes.name || feature.osm_id;
263         link.appendChild(document.createTextNode(name));
264         link.feature = feature;
265         link.onclick = OpenLayers.Function.bind(viewFeatureLink, link);   
266         li.appendChild(link);
267
268         list.appendChild(li);
269       }
270
271       browseObjectList.appendChild(list)
272
273       var link = document.createElement("a");
274       link.href = this.url;
275       link.appendChild(document.createTextNode("API"));
276       browseObjectList.appendChild(link);
277
278       $("browse_content").innerHTML = "";
279       $("browse_content").appendChild(browseObjectList); 
280     }
281   }
282     
283   function viewFeatureLink() {
284     var layer = this.feature.layer;
285
286     for (var i = 0; i < layer.selectedFeatures.length; i++) {
287       var f = layer.selectedFeatures[i]; 
288       layer.drawFeature(f, layer.styleMap.createSymbolizer(f, "default"));
289     }
290
291     onFeatureSelect(this.feature);
292
293     if (browseMode != "auto") {
294       map.setCenter(this.feature.geometry.getBounds().getCenterLonLat()); 
295     }
296
297     return false;
298   }
299     
300   function loadObjectList() {
301     $("browse_content").innerHTML="";
302     $("browse_content").appendChild(browseObjectList);
303
304     return false;
305   }
306       
307   function onFeatureSelect(feature) {
308     // Unselect previously selected feature
309     if (browseActiveFeature) {
310       browseActiveFeature.layer.drawFeature(
311         browseActiveFeature, 
312         browseActiveFeature.layer.styleMap.createSymbolizer(browseActiveFeature, "default")
313       );
314     }
315
316     // Redraw in selected style
317     feature.layer.drawFeature(
318       feature, feature.layer.styleMap.createSymbolizer(feature, "select")
319     );
320
321     // If the current object is the list, don't innerHTML="", since that could clear it.
322     if ($("browse_content").firstChild == browseObjectList) { 
323       $("browse_content").removeChild(browseObjectList);
324     } else { 
325       $("browse_content").innerHTML = "";
326     }   
327         
328     // Create a link back to the object list
329     var div = document.createElement("div");
330     div.style.textAlign = "center";
331     div.style.marginBottom = "20px";
332     $("browse_content").appendChild(div);
333     var link = document.createElement("a");
334     link.href = "#";
335     link.onclick = loadObjectList;
336     link.appendChild(document.createTextNode("Display object list"));
337     div.appendChild(link);
338
339     var table = document.createElement("table");
340     table.width = "100%";
341     table.className = "browse_heading";
342     $("browse_content").appendChild(table);
343
344     var tr = document.createElement("tr");
345     table.appendChild(tr);
346
347     var heading = document.createElement("td");
348     heading.appendChild(document.createTextNode(featureName(feature)));
349     tr.appendChild(heading);
350
351     var td = document.createElement("td");
352     td.align = "right";
353     tr.appendChild(td);
354
355     var type = featureType(feature);
356     var link = document.createElement("a");   
357     link.href = "/browse/" + type + "/" + feature.osm_id;
358     link.appendChild(document.createTextNode("Details"));
359     td.appendChild(link);
360
361     var div = document.createElement("div");
362     div.className = "browse_details";
363
364     $("browse_content").appendChild(div);
365
366     // Now the list of attributes
367     var ul = document.createElement("ul");
368     for (var key in feature.attributes) {
369       var li = document.createElement("li");
370       var b = document.createElement("b");
371       b.appendChild(document.createTextNode(key));
372       li.appendChild(b);
373       li.appendChild(document.createTextNode(": " + feature.attributes[key]));
374       ul.appendChild(li);
375     }
376         
377     div.appendChild(ul);
378         
379     var link = document.createElement("a");   
380     link.href =  "/browse/" + type + "/" + feature.osm_id + "/history";
381     link.appendChild(document.createTextNode("Show history"));
382     link.onclick = OpenLayers.Function.bind(loadHistory, {
383       type: type, feature: feature, link: link
384     });
385         
386     div.appendChild(link);
387
388     // Stash the currently drawn feature
389     browseActiveFeature = feature; 
390   }   
391
392   function loadHistory() {
393     this.link.href = "";
394     this.link.innerHTML = "Wait...";
395
396     new Ajax.Request("/api/0.5/" + this.type + "/" + this.feature.osm_id + "/history", {
397       onComplete: OpenLayers.Function.bind(displayHistory, this)
398     });
399
400     return false;
401   }
402
403   function displayHistory(request) {
404     if (browseActiveFeature.osm_id != this.feature.osm_id || $("browse_content").firstChild == browseObjectList)  { 
405         return false;
406     } 
407
408     this.link.parentNode.removeChild(this.link);
409
410     var doc = request.responseXML;
411
412     var table = document.createElement("table");
413     table.width = "100%";
414     table.className = "browse_heading";
415     $("browse_content").appendChild(table);
416
417     var tr = document.createElement("tr");
418     table.appendChild(tr);
419
420     var heading = document.createElement("td");
421     heading.appendChild(document.createTextNode("History for " + featureName(this.feature)));
422     tr.appendChild(heading);
423
424     var td = document.createElement("td");
425     td.align = "right";
426     tr.appendChild(td);
427
428     var link = document.createElement("a");   
429     link.href = "/browse/" + this.type + "/" + this.feature.osm_id + "/history";
430     link.appendChild(document.createTextNode("Details"));
431     td.appendChild(link);
432
433     var div = document.createElement("div");
434     div.className = "browse_details";
435
436     var nodes = doc.getElementsByTagName(this.type);
437     var history = document.createElement("ul");  
438     for (var i = nodes.length - 1; i >= 0; i--) {
439       var user = nodes[i].getAttribute("user") || "private user";
440       var timestamp = nodes[i].getAttribute("timestamp");
441       var item = document.createElement("li");
442       item.appendChild(document.createTextNode("Edited by " + user + " at " + timestamp));
443       history.appendChild(item);
444     }
445     div.appendChild(history);
446
447     $("browse_content").appendChild(div); 
448   }
449
450   function featureType(feature) {
451     if (feature.geometry.CLASS_NAME == "OpenLayers.Geometry.Point") {
452       return "node";
453     } else {
454       return "way";
455     }
456   }
457   
458   function featureName(feature) {
459     if (feature.attributes.name) {
460       return feature.attributes.name;
461     } else {
462       return ucFirst(featureType(feature)) + " " + feature.osm_id;
463     }
464   }
465
466   function setStatus(status) {
467     $("browse_status").innerHTML = status;
468     $("browse_status").style.display = "block";
469   }
470   
471   function clearStatus() {
472     $("browse_status").innerHTML = "";
473     $("browse_status").style.display = "none";
474   }
475
476   function ucFirst(str) {
477     return str.substr(0,1).toUpperCase() + str.substr(1,str.length);
478   }
479
480   startBrowse();
481 EOJ