]> git.openstreetmap.org Git - rails.git/blob - app/assets/javascripts/index/history-changesets-layer.js
Fix next page boundary condition for user notes
[rails.git] / app / assets / javascripts / index / history-changesets-layer.js
1 OSM.HistoryChangesetBboxLayer = L.FeatureGroup.extend({
2   getLayerId: function (layer) {
3     return layer.id;
4   },
5
6   addChangesetLayer: function (changeset) {
7     const style = this._getChangesetStyle(changeset);
8     const rectangle = L.rectangle(changeset.bounds, {
9       ...style,
10       contextmenu: true,
11       contextmenuItems: [{
12         text: OSM.i18n.t("javascripts.context.scroll_to_changeset"),
13         callback: () => {
14           this.fire("requestscrolltochangeset", { id: changeset.id }, true);
15         },
16         index: 0
17       }, {
18         separator: true,
19         index: 1
20       }]
21     });
22     rectangle.id = changeset.id;
23     rectangle.on("click", function (e) {
24       OSM.router.click(e.originalEvent, $(`#changeset_${changeset.id} a.changeset_id`).attr("href"));
25     });
26     return this.addLayer(rectangle);
27   },
28
29   updateChangesetLayerBounds: function (changeset) {
30     this.getLayer(changeset.id)?.setBounds(changeset.bounds);
31   },
32
33   _getSidebarRelativeClassName: function ({ sidebarRelativePosition }) {
34     if (sidebarRelativePosition > 0) {
35       return "changeset-above-sidebar-viewport";
36     } else if (sidebarRelativePosition < 0) {
37       return "changeset-below-sidebar-viewport";
38     } else {
39       return "changeset-in-sidebar-viewport";
40     }
41   }
42 });
43
44 OSM.HistoryChangesetBboxAreaLayer = OSM.HistoryChangesetBboxLayer.extend({
45   _getChangesetStyle: function (changeset) {
46     return {
47       stroke: false,
48       fillOpacity: 0,
49       className: this._getSidebarRelativeClassName(changeset)
50     };
51   }
52 });
53
54 OSM.HistoryChangesetBboxOutlineLayer = OSM.HistoryChangesetBboxLayer.extend({
55   _getChangesetStyle: function (changeset) {
56     return {
57       weight: 4,
58       color: "var(--changeset-outline-color)",
59       fill: false,
60       className: this._getSidebarRelativeClassName(changeset)
61     };
62   }
63 });
64
65 OSM.HistoryChangesetBboxBorderLayer = OSM.HistoryChangesetBboxLayer.extend({
66   _getChangesetStyle: function (changeset) {
67     return {
68       weight: 2,
69       color: "var(--changeset-border-color)",
70       fill: false,
71       className: this._getSidebarRelativeClassName(changeset)
72     };
73   }
74 });
75
76 OSM.HistoryChangesetBboxHighlightAreaLayer = OSM.HistoryChangesetBboxLayer.extend({
77   _getChangesetStyle: function (changeset) {
78     return {
79       interactive: false,
80       stroke: false,
81       fillColor: "var(--changeset-fill-color)",
82       fillOpacity: 0.3,
83       className: this._getSidebarRelativeClassName(changeset)
84     };
85   }
86 });
87
88 OSM.HistoryChangesetBboxHighlightOutlineLayer = OSM.HistoryChangesetBboxLayer.extend({
89   _getChangesetStyle: function (changeset) {
90     return {
91       interactive: false,
92       weight: changeset.sidebarRelativePosition === 0 ? 8 : 6,
93       color: "var(--changeset-outline-color)",
94       fill: false,
95       className: this._getSidebarRelativeClassName(changeset) + " changeset-highlight-outline"
96     };
97   }
98 });
99
100 OSM.HistoryChangesetBboxHighlightBorderLayer = OSM.HistoryChangesetBboxLayer.extend({
101   _getChangesetStyle: function (changeset) {
102     return {
103       interactive: false,
104       weight: 4,
105       color: "var(--changeset-border-color)",
106       fill: false,
107       className: this._getSidebarRelativeClassName(changeset)
108     };
109   }
110 });
111
112 OSM.HistoryChangesetsLayer = L.FeatureGroup.extend({
113   updateChangesets: function (map, changesets) {
114     this._changesets = new Map(changesets.map(changeset => [changeset.id, changeset]));
115     this.updateChangesetsGeometry(map);
116   },
117
118   updateChangesetsGeometry: function (map) {
119     const changesetSizeLowerBound = 20, // Min width/height of changeset in pixels
120           mapViewExpansion = 2; // Half of bbox border+outline width in pixels
121
122     const mapViewCenterLng = map.getCenter().lng,
123           mapViewPixelBounds = map.getPixelBounds();
124
125     mapViewPixelBounds.min.x -= mapViewExpansion;
126     mapViewPixelBounds.min.y -= mapViewExpansion;
127     mapViewPixelBounds.max.x += mapViewExpansion;
128     mapViewPixelBounds.max.y += mapViewExpansion;
129
130     for (const changeset of this._changesets.values()) {
131       const changesetNorthWestLatLng = L.latLng(changeset.bbox.maxlat, changeset.bbox.minlon),
132             changesetSouthEastLatLng = L.latLng(changeset.bbox.minlat, changeset.bbox.maxlon),
133             changesetCenterLng = (changesetNorthWestLatLng.lng + changesetSouthEastLatLng.lng) / 2,
134             shiftInWorldCircumferences = Math.round((changesetCenterLng - mapViewCenterLng) / 360);
135
136       if (shiftInWorldCircumferences) {
137         changesetNorthWestLatLng.lng -= shiftInWorldCircumferences * 360;
138         changesetSouthEastLatLng.lng -= shiftInWorldCircumferences * 360;
139       }
140
141       const changesetMinCorner = map.project(changesetNorthWestLatLng),
142             changesetMaxCorner = map.project(changesetSouthEastLatLng),
143             changesetSizeX = changesetMaxCorner.x - changesetMinCorner.x,
144             changesetSizeY = changesetMaxCorner.y - changesetMinCorner.y;
145
146       if (changesetSizeX < changesetSizeLowerBound) {
147         changesetMinCorner.x -= (changesetSizeLowerBound - changesetSizeX) / 2;
148         changesetMaxCorner.x += (changesetSizeLowerBound - changesetSizeX) / 2;
149       }
150
151       if (changesetSizeY < changesetSizeLowerBound) {
152         changesetMinCorner.y -= (changesetSizeLowerBound - changesetSizeY) / 2;
153         changesetMaxCorner.y += (changesetSizeLowerBound - changesetSizeY) / 2;
154       }
155
156       changeset.bounds = L.latLngBounds(map.unproject(changesetMinCorner),
157                                         map.unproject(changesetMaxCorner));
158
159       const changesetPixelBounds = L.bounds(changesetMinCorner, changesetMaxCorner);
160
161       changeset.hasEdgesInMapView = changesetPixelBounds.overlaps(mapViewPixelBounds) &&
162                                     !changesetPixelBounds.contains(mapViewPixelBounds);
163     }
164
165     this.updateChangesetsOrder();
166   },
167
168   updateChangesetsOrder: function () {
169     const changesetEntries = [...this._changesets];
170     changesetEntries.sort(([, a], [, b]) => b.bounds.getSize() - a.bounds.getSize());
171     this._changesets = new Map(changesetEntries);
172
173     for (const layer of this._bboxLayers) {
174       layer.clearLayers();
175     }
176
177     for (const changeset of this._changesets.values()) {
178       if (changeset.sidebarRelativePosition !== 0 && changeset.hasEdgesInMapView) {
179         this._areaLayer.addChangesetLayer(changeset);
180       }
181     }
182
183     for (const changeset of this._changesets.values()) {
184       if (changeset.sidebarRelativePosition === 0 && changeset.hasEdgesInMapView) {
185         this._areaLayer.addChangesetLayer(changeset);
186       }
187     }
188
189     for (const changeset of this._changesets.values()) {
190       if (changeset.sidebarRelativePosition !== 0) {
191         this._borderLayer.addChangesetLayer(changeset);
192       }
193     }
194
195     for (const changeset of this._changesets.values()) {
196       if (changeset.sidebarRelativePosition === 0) {
197         this._outlineLayer.addChangesetLayer(changeset);
198       }
199     }
200
201     for (const changeset of this._changesets.values()) {
202       if (changeset.sidebarRelativePosition === 0) {
203         this._borderLayer.addChangesetLayer(changeset);
204       }
205     }
206   },
207
208   toggleChangesetHighlight: function (id, state) {
209     const changeset = this._changesets.get(id);
210     if (!changeset) return;
211
212     this._highlightAreaLayer.clearLayers();
213     this._highlightOutlineLayer.clearLayers();
214     this._highlightBorderLayer.clearLayers();
215
216     if (state) {
217       this._highlightAreaLayer.addChangesetLayer(changeset);
218       this._highlightOutlineLayer.addChangesetLayer(changeset);
219       this._highlightBorderLayer.addChangesetLayer(changeset);
220     }
221   },
222
223   setChangesetSidebarRelativePosition: function (id, state) {
224     const changeset = this._changesets.get(id);
225     if (!changeset) return;
226     changeset.sidebarRelativePosition = state;
227   }
228 });
229
230 OSM.HistoryChangesetsLayer.addInitHook(function () {
231   this._changesets = new Map;
232
233   this._bboxLayers = [
234     this._areaLayer = new OSM.HistoryChangesetBboxAreaLayer().addTo(this),
235     this._outlineLayer = new OSM.HistoryChangesetBboxOutlineLayer().addTo(this),
236     this._borderLayer = new OSM.HistoryChangesetBboxBorderLayer().addTo(this),
237     this._highlightAreaLayer = new OSM.HistoryChangesetBboxHighlightAreaLayer().addTo(this),
238     this._highlightOutlineLayer = new OSM.HistoryChangesetBboxHighlightOutlineLayer().addTo(this),
239     this._highlightBorderLayer = new OSM.HistoryChangesetBboxHighlightBorderLayer().addTo(this)
240   ];
241 });