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