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