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