PageSourceSearch

https://hamza-maptify.netlify.app/script.js

js hamza-maptify.netlify.app collected 2026-10-03 10:51:16 UTC 10,132 bytes, 351 lines download raw bytes

1'use strict';
2
3class Workout {
4  date = new Date();
5  id = (Date.now() + '').slice(-10);
6  isDeleted;
7
8  constructor(coords, distance, duration) {
9    this.coords = coords; //[lat,lng]
10    this.distance = distance; // in km
11    this.duration = duration; // in min
12  }
13
14  _setDescription() {
15    // prettier-ignore
16    const months = ['January', 'February', 'March', 'April', 'May', 'June',
17  'July', 'August', 'September', 'October', 'November', 'December'];
18    this.description = `${this.type[0].toUpperCase()}${this.type.slice(1)}
19  on ${months[this.date.getMonth()]} ${this.date.getDate()}`;
20  }
21
22  click() {
23    this.clicks++;
24  }
25}
26
27class Running extends Workout {
28  type = 'running';
29  constructor(coords, distance, duration, cadence) {
30    super(coords, distance, duration);
31    this.cadence = cadence;
32    this.calcPace();
33    this._setDescription();
34  }
35
36  calcPace() {
37    // min/kim
38    this.pace = this.duration / this.distance;
39    return this.pace;
40  }
41}
42class Cycling extends Workout {
43  type = 'cycling';
44  constructor(coords, distance, duration, elavationGain) {
45    super(coords, distance, duration);
46    this.elavationGain = elavationGain;
47    // this.type = 'Cycling';
48    this.calcSpeed();
49    this._setDescription();
50  }
51
52  calcSpeed() {
53    // km/h
54    this.speed = this.distance / (this.duration / 60);
55    return this.speed;
56  }
57}
58
59// const run1 = new Running([39,-12], 5.2, 24, 178);
60// const cycling1 = new Cycling([39,-12], 27, 95, 523);
61// console.log(run1, cycling1);
62
63// --------------------------------------------
64// APP Architecture
65
66const form = document.querySelector('.form');
67const containerWorkouts = document.querySelector('.workouts');
68const inputType = document.querySelector('.form__input--type');
69const inputDistance = document.querySelector('.form__input--distance');
70const inputDuration = document.querySelector('.form__input--duration');
71const inputCadence = document.querySelector('.form__input--cadence');
72const inputElevation = document.querySelector('.form__input--elevation');
73const deleteWorkout = document.querySelector('delete-workout');
74
75class App {
76  #map;
77  #mapZoomLevel = 15;
78  #mapEvent;
79  #workouts = [];
80
81  constructor() {
82    // Get user's position
83    this._getPosition();
84
85    // Get data from local storage
86    this._getLocalStorage();
87
88    // Attach event handlers
89    form.addEventListener('submit', this._newWorkOut.bind(this));
90    inputType.addEventListener('change', this._toggleElevationField);
91    containerWorkouts.addEventListener('click', this._moveToPopup.bind(this));
92    document.addEventListener('click', this._deleteWorkout.bind(this));
93
94    // document.addEventListener('click', function (event) {
95    //   const deleteButton = event.target.closest('.delete-workout');
96    //   if (deleteButton) {
97    //     const workoutId = deleteButton.getAttribute('data-workout-id');
98    //     console.log(workoutId);
99    //     this._setNewLocalStorage(workoutId).bind(this);
100    //   }
101    // });
102  }
103
104  _getPosition() {
105    if (navigator.geolocation)
106      navigator.geolocation.getCurrentPosition(
107        this._loadMap.bind(this),
108
109        function () {
110          alert('Could not access you location!');
111        }
112      );
113  }
114  _loadMap(position) {
115    const { latitude } = position.coords;
116    const { longitude } = position.coords;
117    // console.log(`https://www.google.com/maps/@${latitude},${longitude}`);
118
119    const coords = [latitude, longitude];
120
121    this.#map = L.map('map').setView(coords, this.#mapZoomLevel);
122
123    L.tileLayer('https://tile.openstreetmap.fr/hot/{z}/{x}/{y}.png', {
124      attribution:
125        '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
126    }).addTo(this.#map);
127
128    // Handing clicks on map
129    this.#map.on('click', this._showForm.bind(this));
130
131    this.#workouts.forEach(work => {
132      this._renderWorkoutMarker(work);
133    });
134  }
135  _showForm(mapE) {
136    this.#mapEvent = mapE;
137    form.classList.remove('hidden');
138    inputDistance.focus();
139  }
140
141  _hideForm() {
142    // Empty the inputs
143    inputDistance.value =
144      inputDuration.value =
145      inputCadence.value =
146      inputElevation.value =
147        '';
148
149    form.style.display = 'none';
150    form.classList.add('hidden');
151    setTimeout(() => (form.style.display = 'grid'), 1000);
152  }
153  _toggleElevationField() {
154    inputElevation.closest('.form__row').classList.toggle('form__row--hidden');
155    inputCadence.closest('.form__row').classList.toggle('form__row--hidden');
156  }
157  _newWorkOut(e) {
158    const validateInputs = (...inputs) =>
159      inputs.every(inp => Number.isFinite(inp));
160
161    const allPositive = (...inputs) => inputs.every(inp => inp > 0);
162    e.preventDefault();
163
164    // Get data from form
165    const type = inputType.value;
166    const distance = +inputDistance.value;
167    const duration = +inputDuration.value;
168    const { lat, lng } = this.#mapEvent.latlng;
169    let workout;
170
171    // Validate the data
172
173    // If running, create running object - if cycling create cycling object
174    if (type === 'running') {
175      const cadence = +inputCadence.value;
176      // validate
177      if (
178        !validateInputs(distance, duration, cadence) ||
179        !allPositive(distance, duration, cadence)
180      )
181        return alert('Inputs have to be positive numbers!');
182
183      workout = new Running([lat, lng], distance, duration, cadence);
184    }
185
186    if (type === 'cycling') {
187      const elavation = +inputElevation.value;
188
189      if (
190        !validateInputs(distance, duration, elavation) ||
191        !allPositive(distance, duration)
192      )
193        return alert('Inputs have to be positive numbers!');
194
195      workout = new Cycling([lat, lng], distance, duration, elavation);
196    }
197
198    // Add new object to workout array
199    this.#workouts.push(workout);
200
201    // Render workout on map as marker
202    this._renderWorkoutMarker(workout);
203
204    // Render workout on the list
205    this._renderWorkout(workout);
206
207    // Hide form + Clear input fields
208    this._hideForm();
209
210    // Set local storage to all workouts
211    this._setLocalStorage();
212  }
213  _renderWorkoutMarker(workout) {
214    L.marker(workout.coords)
215      .addTo(this.#map)
216      .bindPopup(
217        L.popup({
218          maxWidth: 250,
219          minWidth: 200,
220          autoClose: false,
221          closeOnClick: false,
222          className: `${workout.type}-popup`,
223        })
224      )
225      .setPopupContent(
226        `${workout.type === 'running' ? '🏃‍♂️' : '🚴‍♀️'} ${
227          workout.description
228        }<span class="delete-workout" data-workout-id="${
229          workout.id
230        }"><i class="bi bi-trash-fill"></i></span>`
231      )
232      .openPopup();
233  }
234
235  _renderWorkout(workout) {
236    let html = `
237    <li class="workout workout--${workout.type}" data-id="${workout.id}">
238            <h2 class="workout__title">${workout.description}</h2>
239            <div class="workout__details">
240              <span class="workout__icon">${
241                workout.type === 'running' ? '🏃‍♂️' : '🚴‍♀️'
242              }</span>
243              <span class="workout__value">${workout.distance}</span>
244              <span class="workout__unit">km</span>
245            </div>
246            <div class="workout__details">
247              <span class="workout__icon">⏱</span>
248              <span class="workout__value">${workout.duration}</span>
249              <span class="workout__unit">min</span>
250            </div>
251    `;
252
253    if (workout.type === 'running')
254      html += `
255      
256      <div class="workout__details">
257        <span class="workout__icon">⚡️</span>
258        <span class="workout__value">${workout.pace.toFixed(1)}</span>
259        <span class="workout__unit">min/km</span>
260      </div>
261  
262      <div class="workout__details">
263        <span class="workout__icon">🦶🏼</span>
264        <span class="workout__value">${workout.cadence}</span>
265        <span class="workout__unit">spm</span>
266      </div>
267    </li>`;
268    if (workout.type === 'cycling')
269      html += `
270      <div class="workout__details">
271              <span class="workout__icon">⚡️</span>
272              <span class="workout__value">${workout.speed.toFixed(1)}</span>
273              <span class="workout__unit">km/h</span>
274            </div>
275            <div class="workout__details">
276              <span class="workout__icon">⛰</span>
277              <span class="workout__value">${workout.elavationGain}</span>
278              <span class="workout__unit">m</span>
279            </div>
280        </li>
281      `;
282
283    form.insertAdjacentHTML('afterend', html);
284  }
285  _moveToPopup(e) {
286    const workOutEl = e.target.closest('.workout');
287    if (!workOutEl) return;
288    const workout = this.#workouts.find(
289      work => work.id === workOutEl.dataset.id
290    );
291    // console.log(workout);
292    this.#map.setView(workout.coords, this.#mapZoomLevel, {
293      animate: true,
294      pan: {
295        duration: 1,
296      },
297    });
298  }
299
300  _setLocalStorage() {
301    localStorage.setItem('workouts', JSON.stringify(this.#workouts));
302  }
303
304  _deleteWorkout(e) {
305
306    const deleteButton = e.target.closest('.delete-workout');
307      if (deleteButton) {
308        const workoutId = deleteButton.getAttribute('data-workout-id');
309        console.log(workoutId);
310        if(!workoutId) return;
311        
312        // get the element by id
313    const meow = this.#workouts.find(
314      // worko => worko.id === workOutEl.dataset.id
315      worko => worko.id === workoutId
316    );
317    console.log(meow);
318
319    // get the index of the element
320    var removedWorkoutIndex = this.#workouts.indexOf(meow);
321    console.log(removedWorkoutIndex);
322
323    // remove the workout by index
324    this.#workouts.splice(removedWorkoutIndex, 1);
325    localStorage.setItem('workouts', JSON.stringify(this.#workouts));
326    console.log(this.#workouts);
327
328    location.reload();
329      }
330
331  }
332
333  _getLocalStorage() {
334    const data = JSON.parse(localStorage.getItem('workouts'));
335
336    if (!data) return;
337
338    this.#workouts = data;
339
340    this.#workouts.forEach(work => {
341      this._renderWorkout(work);
342    });
343  }
344
345  nukeAll() {
346    localStorage.removeItem('workouts');
347    location.reload();
348  }
349}
350
351const app = new App();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.