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