Passed
Push — master ( 769087...0fc13e )
by Sébastien
02:11
created

$(window).scroll   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 1
Metric Value
cc 2
eloc 6
c 2
b 0
f 1
nc 2
nop 1
dl 0
loc 8
rs 10
1
/*global $ */
2
$(window).on("scroll", function(event) {
0 ignored issues
show
Unused Code introduced by
The parameter event is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
3
  var scrollValue = $(window).scrollTop();
4
  if (scrollValue > 220) {
5
    $(".navbar").addClass("affix");
6
  } else {
7
    $(".navbar").removeClass("affix");
8
  }
9
});
10
11
/*Load location (stores2)*/
12
var stores2 = function loadStoreLocation() {
13
  stores2 = null;
14
  $.ajax({
15
    async: false,
16
    global: false,
17
    url: "https://leipzig-einkaufen.de/location.json",
18
    //"url": "http://localhost/vectortiles/museen.json",
19
    dataType: "json",
20
    success: function(data) {
21
      stores2 = data;
22
    }
23
  });
24
  return stores2;
25
}();
26
27
// Set bounds to Leipzig, Germany
28
var bounds = [
29
  [12.179, 51.227], // Southwest coordinates
30
  [12.6, 51.459] // Northeast coordinates
31
];
32
33
// declare map
34
var map = new mapboxgl.Map({
0 ignored issues
show
Bug introduced by
The variable mapboxgl seems to be never declared. If this is a global, consider adding a /** global: mapboxgl */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
35
  container: "map",
36
  style: "https://leipzig-einkaufen.de/json/style-local.json",
37
  //style: "http://localhost/vectortiles/json/style-local.json",
38
39
  center: [12.3722, 51.3272],
40
  zoom: 11,
41
  attributionControl: true,
42
  hash: false,
43
  maxZoom: 14.9,
44
  maxBounds: bounds // Sets bounds as max
45
});
46
47
/*Declare MapDirections*/
48
var mapDirections = new MapboxDirections();
0 ignored issues
show
Bug introduced by
The variable MapboxDirections seems to be never declared. If this is a global, consider adding a /** global: MapboxDirections */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
49
/*MapDirections Settings*/
50
mapDirections.accessToken =
51
  "pk.eyJ1Ijoic2hldWIiLCJhIjoiWGtobTNPNCJ9.v2JwlNSGBm_KxJUKE_WLig";
52
mapDirections.unit = "metric";
53
mapDirections.proximity = false; /*proximity ??*/
54
mapDirections.interactive = false;
55
mapDirections.profile = "driving"; //, "walking", "cycling";
56
// UI controls
57
mapDirections.controls = {
58
  inputs: true,
59
  instructions: false
60
};
61
62
/*Add mapDirections Controls*/
63
map.addControl(new MapboxDirections(mapDirections), "top-left");
64
65
map.addControl(
66
  new mapboxgl.ScaleControl({
67
    maxWidth: 80,
68
    unit: "metric"
69
  })
70
);
71
72
var directionControl = document.getElementsByClassName(
73
  "mapboxgl-ctrl-directions"
74
);
75
directionControl["0"].hidden = true;
76
var ptsWithin = null;
77
78
// Create a popup (but don't add it to the map yet)
79
var popup = new mapboxgl.Popup({
80
  closeButton: false
81
});
82
83
var filterEl = document.getElementById("feature-filter");
84
var listingsEl = document.getElementById("listings");
85
var txtCategoriesEl = document.getElementById("txtCategories");
86
87
// Empty Geojson Data
88
var bufferedLinestring = {
89
  id: "0",
90
  type: "Feature",
91
  geometry: {
92
    type: "Point",
93
    coordinates: [0, 0]
94
  },
95
  properties: {}
96
};
97
98
// Functions
99
function normalizeString(string) {
100
  return string.trim().toLowerCase();
101
}
102
103
function createPopUp(currentFeature) {
104
  var popup = new mapboxgl.Popup({
0 ignored issues
show
Bug introduced by
The variable mapboxgl seems to be never declared. If this is a global, consider adding a /** global: mapboxgl */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
Unused Code introduced by
The variable popup seems to be never used. Consider removing it.
Loading history...
105
    closeOnClick: true
106
  })
107
    .setLngLat(currentFeature.geometry.coordinates)
108
    .setHTML(
109
      "<h3>" +
110
        currentFeature.properties.name +
111
        "</h3>" +
112
        "<h4>" +
113
        currentFeature.properties.description +
114
        "</h4>"
115
    )
116
    .addTo(map);
117
}
118
119
function getUniqueFeatures(array, comparatorProperty) {
120
  var existingFeatureKeys = {};
121
  // Because features come from tiled vector data, feature geometries may be split
122
  // or duplicated across tile boundaries and, as a result, features may appear
123
  // multiple times in query results.
124
  var uniqueFeatures = array.filter(function(el) {
125
    if (existingFeatureKeys[el.properties[comparatorProperty]]) {
126
      return false;
127
    } else {
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
128
      existingFeatureKeys[el.properties[comparatorProperty]] = true;
129
      return true;
130
    }
131
  });
132
133
  return uniqueFeatures;
134
}
135
136
function colorLocationList(data) {
137
  // Iterate through the list of stores
138
  // WITHIN THE CALCULATED ROUTE !! and color in green
139
  if (data.length) {
140
    data.forEach(function(feature) {
141
      // Shorten data.feature.properties to just `prop`.
142
      var prop = feature.properties;
143
      var cardHeader = document.getElementById("heading" + prop.id);
144
      if (cardHeader === null) {
145
        return;
146
      }
147
148
      var cardTitle = cardHeader.getElementsByClassName("title");
149
      cardTitle[0].style.color = "#608BC7";
150
    });
151
  }
152
}
153
154
function buildLocationList(data) {
155
  // Iterate through the list of stores
156
  listingsEl.innerHTML = "";
157
  if (data.length) {
158
    data.forEach(function(feature) {
159
      // Shorten feature.properties to just `prop` so we're not writing this long form over and over again.
160
      var prop = feature.properties;
161
162
      // Select the listing container in the HTML and append a div  with the class 'item' for each store
163
      var card = listingsEl.appendChild(document.createElement("div"));
164
      card.className = "item card cardList";
165
      card.id = prop.id;
166
167
      var cardHeader = card.appendChild(document.createElement("div"));
168
      cardHeader.className = "card-header";
169
      cardHeader.setAttribute("role", "tab");
170
171
      cardHeader.setAttribute("id", "heading" + card.id);
172
      cardHeader.id = "heading" + card.id;
173
174
      var cardMb0 = cardHeader.appendChild(document.createElement("h5"));
175
      cardMb0.className = "mb-0";
176
177
      // Create a new link with the class 'title' for each store and fill it with the store address
178
      var link = cardMb0.appendChild(document.createElement("a"));
179
      link.setAttribute("data-toggle", "collapse");
180
      link.href = "#collapse" + card.id;
181
      link.setAttribute("aria-expanded", "false");
182
      link.setAttribute("aria-controls", "collapse" + card.id);
183
      link.className = "title";
184
      link.textContent = prop.name;
185
      link.dataPosition = card.id;
186
187
      var cardCollapse = card.appendChild(document.createElement("div"));
188
      cardCollapse.className = "collapse";
189
      cardCollapse.setAttribute("id", "collapse" + card.id);
190
      cardCollapse.setAttribute("role", "tabpanel");
191
      cardCollapse.setAttribute("aria-labelledby", "heading" + card.id);
192
      cardCollapse.setAttribute("data-parent", "#listings");
193
194
      if (prop.image) {
195
        var cardImg = cardCollapse.appendChild(document.createElement("img"));
196
        cardImg.className = "img-responsive img-listing";
197
        cardImg.src = prop.image;
198
        cardImg.alt = prop.name;
199
        cardImg.title = prop.name;
200
      }
201
202
      var cardBody = cardCollapse.appendChild(document.createElement("div"));
203
      cardBody.className = "card-body";
204
      cardBody.textContent = prop.description;
205
      cardBody.appendChild(document.createElement("br"));
206
207
      if (prop.url) {
208
        var linkBody = cardBody.appendChild(document.createElement("a"));
209
        linkBody.textContent = prop.name;
210
        linkBody.href = prop.url;
211
        linkBody.target = "_blank";
212
        linkBody.title = prop.name;
213
      }
214
215
      // Add an event listener for the links in the sidebar listing
216
      link.addEventListener("click", function(e) {
0 ignored issues
show
Unused Code introduced by
The parameter e is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
217
        // Update the currentFeature to the store associated with the clicked link
218
        var clickedListing = stores2.features[this.dataPosition];
219
220
        var popUps = document.getElementsByClassName("mapboxgl-popup");
221
        // Check if there is already a popup on the map and if so, remove it
222
        if (popUps[0]) {
223
          popUps[0].parentNode.removeChild(popUps[0]);
224
        }
225
226
        // 1. Close all other popups and display popup for clicked store
227
        createPopUp(clickedListing);
228
229
        // 2. Highlight listing in sidebar (and remove highlight for all other listings)
230
        var activeItem = document.getElementsByClassName("is-active");
231
        if (activeItem[0]) {
232
          activeItem[0].classList.remove("is-active");
233
        }
234
        this.classList.add("is-active");
235
      });
236
    });
237
  } else {
238
    var empty = document.createElement("p");
239
    empty.textContent = "Ziehen Sie die Karte, um die Ergebnisse zu füllen";
240
    listingsEl.appendChild(empty);
241
242
    // remove features filter
243
    map.setFilter("locations", ["has", "Categories"]);
244
  }
245
246
  // Populate features for the listing overlay.
247
  if (ptsWithin) {
248
    colorLocationList(ptsWithin.features);
249
  }
250
}
251
252
function filterOnRoute() {
253
  var mapDirectionsSource = map.getSource("directions");
254
  var radius = 0.6;
255
  var unit = "kilometers";
256
257
  //var distDuration = mapDirections.getDistanceAndDuration();
258
259
  // buffer the route with a area of radius 'radius'
260
  if (mapDirectionsSource._data.features.length < 2) {
261
    return;
262
  }
263
  var bufferedLinestring = turf.buffer(
0 ignored issues
show
Bug introduced by
The variable turf seems to be never declared. If this is a global, consider adding a /** global: turf */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
264
    mapDirectionsSource._data.features[2].geometry,
265
    radius,
266
    {
267
      units: unit
268
    }
269
  );
270
271
  // update bufferedTraceSource
272
  map.getSource("bufferedTraceSource").setData(bufferedLinestring);
273
274
  // Get locations rendered on the map
275
  var features = map.queryRenderedFeatures({
276
    layers: ["locations"]
277
  });
278
279
  // use featureCollection to convert features (array of features) into a collection of features (Object type FeatureCollection);
280
  var collection = turf.featureCollection(features);
281
282
  // Filter the points to the area around the direction
283
  ptsWithin = turf.pointsWithinPolygon(collection, bufferedLinestring);
284
285
  // Populate features for the listing overlay.
286
  if (ptsWithin) {
287
    buildLocationList(features);
288
  }
289
}
290
291
function displayDirectionControls() {
292
  var directionControl = document.getElementsByClassName(
293
    "mapboxgl-ctrl-directions"
294
  );
295
  if (directionControl["0"].hidden) {
296
    directionControl["0"].hidden = false;
297
    map.setLayoutProperty("bufferedTraceLayer", "visibility", "visible");
298
299
    map.setLayoutProperty("directions-origin-point", "visibility", "visible");
300
    map.setLayoutProperty(
301
      "directions-destination-point",
302
      "visibility",
303
      "visible"
304
    );
305
    map.setLayoutProperty("directions-origin-label", "visibility", "visible");
306
    map.setLayoutProperty(
307
      "directions-destination-label",
308
      "visibility",
309
      "visible"
310
    );
311
312
    map.setLayoutProperty("directions-hover-point", "visibility", "visible");
313
    map.setLayoutProperty("directions-waypoint-point", "visibility", "visible");
314
    map.setLayoutProperty("directions-route-line", "visibility", "visible");
315
    map.setLayoutProperty("directions-route-line-alt", "visibility", "visible");
316
    filterOnRoute();
317
  } else {
318
    directionControl["0"].hidden = true;
319
    // reinitialize ptsWithin
320
    ptsWithin = null;
321
322
    map.setLayoutProperty("bufferedTraceLayer", "visibility", "none");
323
    map.setLayoutProperty("directions-origin-point", "visibility", "none");
324
    map.setLayoutProperty("directions-destination-point", "visibility", "none");
325
    map.setLayoutProperty("directions-origin-label", "visibility", "none");
326
    map.setLayoutProperty("directions-destination-label", "visibility", "none");
327
328
    map.setLayoutProperty("directions-hover-point", "visibility", "none");
329
    map.setLayoutProperty("directions-waypoint-point", "visibility", "none");
330
    map.setLayoutProperty("directions-route-line", "visibility", "none");
331
    map.setLayoutProperty("directions-route-line-alt", "visibility", "none");
332
333
    var features = map.queryRenderedFeatures({
334
      layers: ["locations"]
335
    });
336
337
    if (features) {
338
      // Populate features for the listing overlay.
339
      buildLocationList(features);
340
    }
341
  }
342
}
343
344
// Call buildlist function on initialization
345
buildLocationList(stores2.features);
346
347
// Load map
348
map.on("load", function(e) {
349
  //map.loadImage('http://localhost/vectortiles/media/diagonal-noise.png', function(error, image) {
350
  map.loadImage(
351
    "https://leipzig-einkaufen.de/media/diagonal-noise.png",
352
    function(error, image) {
353
      if (error) throw error;
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
354
      map.addImage("background_pattern", image);
355
    }
356
  );
357
358
  //map.loadImage('http://localhost/vectortiles/media/Marker_with_Shadow.png', function(error, image) {
359
  map.loadImage(
360
    "https://leipzig-einkaufen.de/media/Marker_with_Shadow.png",
361
    function(error, image) {
362
      if (error) throw error;
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
363
      map.addImage("marker_z", image);
364
365
      // Add the stores2 (locations_source) to the map
366
      map.addSource("locations_source", {
367
        type: "geojson",
368
        data: stores2
369
      });
370
371
      // Add the locations_source to the map as a layer
372
      map.addLayer({
373
        id: "locations",
374
        type: "symbol",
375
        // Add a GeoJSON source containing place coordinates and information.
376
        source: "locations_source",
377
        layout: {
378
          visibility: "visible",
379
          "icon-image": "marker_z",
380
          "icon-size": 0.9,
381
          "icon-allow-overlap": true
382
        }
383
      });
384
385
      // Add the bufferedLinestring to the map as a layer
386
      map.addSource("bufferedTraceSource", {
387
        type: "geojson",
388
        data: bufferedLinestring,
389
        maxzoom: 13
390
      });
391
      map.addLayer({
392
        id: "bufferedTraceLayer",
393
        type: "fill",
394
        source: "bufferedTraceSource",
395
        layout: {
396
          visibility: "visible"
397
        },
398
        paint: {
399
          "fill-color": "rgb(0,0,0)",
400
          "fill-opacity": 1,
401
          "fill-translate": [0, 2.5],
402
          "fill-pattern": "background_pattern"
403
        }
404
      });
405
406
      // Add Fullscreen control to the map.
407
      map.addControl(new mapboxgl.FullscreenControl());
0 ignored issues
show
Bug introduced by
The variable mapboxgl seems to be never declared. If this is a global, consider adding a /** global: mapboxgl */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
408
409
      // Add geolocate control to the map.
410
      map.addControl(
411
        new mapboxgl.GeolocateControl({
412
          positionOptions: {
413
            enableHighAccuracy: true
414
          },
415
          trackUserLocation: true
416
        })
417
      );
418
419
      // When a click event occurs on a feature in the places layer, open a popup at the
420
      // location of the feature, with description HTML from its properties.
421
      map.on("click", "locations", function(e) {
422
        var currentFeature = e.features[0];
423
        // 1. Create Popup
424
        createPopUp(currentFeature);
425
426
        // 2. Highlight listing in sidebar (and remove highlight for other listing)
427
        var activeItem = document.getElementsByClassName("is-active");
428
        if (activeItem[0]) {
429
          activeItem[0].classList.remove("is-active");
430
        }
431
432
        var headingElement = document.getElementById(
433
          "heading" + currentFeature.properties.id
434
        );
435
        if (headingElement) {
436
          headingElement.classList.add("is-active");
437
        }
438
        var collapseElement = document.getElementById(
439
          "collapse" + currentFeature.properties.id
440
        );
441
        if (collapseElement) {
442
          $(collapseElement).collapse("show");
443
        }
444
      });
445
446
      map.on("moveend", function() {
447
        // Query all the rendered points in the view
448
        var features = map.queryRenderedFeatures({
449
          layers: ["locations"]
450
        });
451
452
        if (features) {
453
          //var uniqueFeatures = getUniqueFeatures(features, "Categories");
454
455
          // Populate features for the listing overlay.
456
          buildLocationList(features);
457
458
          // Clear the input container
459
          filterEl.value = "";
460
461
          // Store the current features in sn `locations_on_map` variable to later use for filtering on `keyup`.
462
          locations = features;
0 ignored issues
show
Bug introduced by
The variable locations seems to be never declared. Assigning variables without defining them first makes them global. If this was intended, consider making it explicit like using window.locations.
Loading history...
463
        }
464
      });
465
466
      map.on("mousemove", "locations", function(e) {
0 ignored issues
show
Unused Code introduced by
The parameter e is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
467
        // Change the cursor style as a UI indicator.
468
        map.getCanvas().style.cursor = "pointer";
469
      });
470
471
      map.on("mouseleave", "locations", function() {
472
        map.getCanvas().style.cursor = "";
473
        popup.remove();
474
      });
475
476
      $(".dropdown-item").click(function() {
477
        var value = normalizeString($(this).text());
478
479
        var filtered = map.querySourceFeatures("locations_source");
480
        if (value !== "alle") {
481
          // Filter visible features that don't match the input value.
482
          filtered = filtered.filter(function(feature) {
483
            var name = normalizeString(feature.properties.name);
484
            var Categories = normalizeString(feature.properties.Categories);
485
            return name.indexOf(value) > -1 || Categories.indexOf(value) > -1;
486
          });
487
        }
488
        if (!filtered) {
489
          return;
490
        }
491
492
        var uniqueFeatures = getUniqueFeatures(filtered, "Categories");
493
        // Populate the sidebar with filtered results
494
        buildLocationList(uniqueFeatures);
495
496
        // Set the filter to populate features into the layer.
497
        map.setFilter(
498
          "locations",
499
          ["in", "name"].concat(
500
            uniqueFeatures.map(function(feature) {
501
              return feature.properties.name;
502
            })
503
          )
504
        );
505
506
        txtCategoriesEl.value = value;
507
      });
508
    }
509
  );
510
});
511
512
// Direction event listener
513
mapDirections.on("route", function(e) {
0 ignored issues
show
Unused Code introduced by
The parameter e is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
514
  filterOnRoute();
515
});
516
517
// Display Direction
518
$("#btnDisplayControls").on("click", function(e) {
0 ignored issues
show
Unused Code introduced by
The parameter e is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
519
  displayDirectionControls();
520
});
521