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