-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathscript.js
More file actions
502 lines (474 loc) · 16.7 KB
/
Copy pathscript.js
File metadata and controls
502 lines (474 loc) · 16.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
// Test data for Hamburg activities
const activities = [
{
id: 1,
name: "Visit Elbphilharmonie",
description: "Visit the iconic concert hall and enjoy the breathtaking view over the harbor",
image: "🏛️",
tags: ["experience", "indoors", "expensive", "quiet", "panorama", "cultural"],
price: "€€",
duration: "2-3 hours",
location: "HafenCity"
},
{
id: 2,
name: "Explore Speicherstadt",
description: "World Heritage site with historic warehouses and Miniatur Wunderland",
image: "🏭",
tags: ["explore", "indoors", "expensive", "quiet", "historical", "crowded"],
price: "€€",
duration: "3-4 hours",
location: "Speicherstadt"
},
{
id: 3,
name: "Planten un Blomen",
description: "Green oasis in the heart of the city with botanical garden and water features",
image: "🌺",
tags: ["relaxing", "outdoors", "cheap", "quiet", "green", "peaceful"],
price: "€",
duration: "2-3 hours",
location: "St. Pauli"
},
{
id: 4,
name: "Reeperbahn Nightlife",
description: "The famous entertainment district with bars, clubs and shows",
image: "🎭",
tags: ["exciting", "indoors", "expensive", "loud", "party", "crowded"],
price: "€€€",
duration: "4-6 hours",
location: "St. Pauli"
},
{
id: 5,
name: "Hamburg Fish Market",
description: "Traditional market with fresh fish, fruit and lively atmosphere",
image: "🐟",
tags: ["experience", "outdoors", "cheap", "loud", "early", "crowded"],
price: "€",
duration: "1-2 hours",
location: "Altona"
},
{
id: 6,
name: "Alster Boat Tour",
description: "Relaxing boat tour on the Inner and Outer Alster",
image: "⛵",
tags: ["relaxing", "outdoors", "expensive", "quiet", "water", "peaceful"],
price: "€€",
duration: "1-2 hours",
location: "Alster"
},
{
id: 7,
name: "Hamburger Kunsthalle",
description: "One of Germany's largest art museums with works from the Middle Ages to the present",
image: "🎨",
tags: ["explore", "indoors", "expensive", "quiet", "cultural", "alone"],
price: "€€",
duration: "2-3 hours",
location: "Altstadt"
},
{
id: 8,
name: "Stadtpark Hamburg",
description: "Large city park with lake, playgrounds and many recreational opportunities",
image: "🌳",
tags: ["relaxing", "outdoors", "cheap", "quiet", "active", "peaceful"],
price: "€",
duration: "2-4 hours",
location: "Winterhude"
},
{
id: 9,
name: "Hamburg City Hall",
description: "Magnificent town hall in neo-Gothic style with guided tours",
image: "🏛️",
tags: ["explore", "indoors", "cheap", "quiet", "historical", "crowded"],
price: "€",
duration: "1-2 hours",
location: "Altstadt"
},
{
id: 10,
name: "Harbor Cruise",
description: "Boat tour through Hamburg harbor with explanations",
image: "🚢",
tags: ["experience", "outdoors", "expensive", "quiet", "panorama", "water"],
price: "€€",
duration: "1-2 hours",
location: "Harbor"
},
{
id: 11,
name: "Explore Schanzenviertel",
description: "Trendy district with alternative shops, cafes and street art",
image: "🎨",
tags: ["explore", "outdoors", "cheap", "quiet", "trendy", "alone"],
price: "€",
duration: "2-3 hours",
location: "Schanze"
},
{
id: 12,
name: "Hamburger Dom",
description: "Large fairground with rides, shows and culinary specialties",
image: "🎡",
tags: ["exciting", "outdoors", "expensive", "loud", "festival", "crowded"],
price: "€€",
duration: "3-5 hours",
location: "Heiligengeistfeld"
},
{
id: 13,
name: "Blankeneser Treppenviertel",
description: "Picturesque district with stairs to the Elbe beach and historic villas",
image: "🏘️",
tags: ["relaxing", "outdoors", "cheap", "quiet", "romantic", "peaceful"],
price: "€",
duration: "2-3 hours",
location: "Blankenese"
},
{
id: 14,
name: "Hamburger Michel",
description: "Baroque church with Hamburg's highest church tower and great view",
image: "⛪",
tags: ["explore", "indoors", "cheap", "quiet", "spiritual", "alone"],
price: "€",
duration: "1-2 hours",
location: "Neustadt"
},
{
id: 15,
name: "Alster Walk",
description: "Walk around the Inner Alster with cafes and shops",
image: "🚶",
tags: ["relaxing", "outdoors", "cheap", "quiet", "active", "peaceful"],
price: "€",
duration: "1-3 hours",
location: "Alster"
},
{
id: 16,
name: "Sailing on the Alster",
description: "Experience the thrill of sailing on Hamburg's beautiful Alster lake",
image: "⛵",
tags: ["exciting", "outdoors", "expensive", "quiet", "water", "alone", "exhausting"],
price: "€€€",
duration: "3-4 hours",
location: "Alster"
},
{
id: 17,
name: "Scenic Flight over Hamburg",
description: "Spectacular helicopter flight offering bird's eye view of the entire city",
image: "🚁",
tags: ["experience", "outdoors", "expensive", "quiet", "panorama", "in air", "alone"],
price: "€€€",
duration: "1 hour",
location: "Hamburg Airport"
},
{
id: 18,
name: "Stand-up Paddleboarding",
description: "Peaceful paddleboarding session on the calm waters of the Alster",
image: "🏄",
tags: ["relaxing", "outdoors", "expensive", "quiet", "water", "alone", "peaceful"],
price: "€€",
duration: "2 hours",
location: "Alster"
},
{
id: 19,
name: "Hamburg Dungeon",
description: "Interactive horror experience through Hamburg's dark history",
image: "👻",
tags: ["exciting", "indoors", "expensive", "loud", "historical", "crowded", "exhausting"],
price: "€€",
duration: "1-2 hours",
location: "Speicherstadt"
},
{
id: 20,
name: "Kajak Tour on the Elbe",
description: "Adventure kayaking tour along the mighty Elbe river",
image: "🛶",
tags: ["exciting", "outdoors", "expensive", "loud", "water", "exhausting", "alone"],
price: "€€",
duration: "3-4 hours",
location: "Elbe River"
},
{
id: 21,
name: "Hot Air Balloon Ride",
description: "Magical balloon flight over Hamburg's countryside at sunrise",
image: "🎈",
tags: ["experience", "outdoors", "expensive", "quiet", "panorama", "in air", "romantic"],
price: "€€€",
duration: "4-5 hours",
location: "Hamburg Countryside"
},
{
id: 22,
name: "Rock Climbing at Boulderwelt",
description: "Indoor rock climbing experience for all skill levels",
image: "🧗",
tags: ["exciting", "indoors", "expensive", "loud", "active", "exhausting", "alone"],
price: "€€",
duration: "2-3 hours",
location: "Barmbek"
},
{
id: 23,
name: "Sunset Yoga at the Beach",
description: "Peaceful yoga session on the Elbe beach during golden hour",
image: "🧘",
tags: ["relaxing", "outdoors", "cheap", "quiet", "peaceful", "alone", "romantic"],
price: "€",
duration: "1 hour",
location: "Elbe Beach"
},
{
id: 24,
name: "Escape Room Challenge",
description: "Solve puzzles and escape from themed rooms with friends",
image: "🔐",
tags: ["exciting", "indoors", "expensive", "loud", "crowded", "exhausting"],
price: "€€",
duration: "1 hour",
location: "Various Locations"
},
{
id: 25,
name: "Paragliding over Hamburg",
description: "Thrilling paragliding experience with panoramic city views",
image: "🪂",
tags: ["exciting", "outdoors", "expensive", "quiet", "panorama", "in air", "alone"],
price: "€€€",
duration: "2-3 hours",
location: "Hamburg Hills"
},
{
id: 26,
name: "Meditation in the Japanese Garden",
description: "Guided meditation session in the serene Japanese garden",
image: "🌸",
tags: ["relaxing", "outdoors", "cheap", "quiet", "peaceful", "alone", "spiritual"],
price: "€",
duration: "1 hour",
location: "Planten un Blomen"
},
{
id: 27,
name: "Jet Ski Adventure",
description: "High-speed jet skiing on the Elbe river",
image: "🏍️",
tags: ["exciting", "outdoors", "expensive", "loud", "water", "exhausting", "alone"],
price: "€€€",
duration: "1 hour",
location: "Elbe River"
},
{
id: 28,
name: "Wine Tasting Cruise",
description: "Elegant wine tasting experience while cruising the harbor",
image: "🍷",
tags: ["relaxing", "outdoors", "expensive", "quiet", "water", "romantic", "crowded"],
price: "€€€",
duration: "2-3 hours",
location: "Harbor"
},
{
id: 29,
name: "Skydiving over Hamburg",
description: "Ultimate adrenaline rush with tandem skydiving over the city",
image: "🪂",
tags: ["exciting", "outdoors", "expensive", "loud", "panorama", "in air", "exhausting"],
price: "€€€",
duration: "3-4 hours",
location: "Hamburg Airport"
},
{
id: 30,
name: "Floating Sauna Experience",
description: "Unique floating sauna on the Alster with ice swimming",
image: "🧖",
tags: ["relaxing", "outdoors", "expensive", "quiet", "water", "peaceful", "alone"],
price: "€€",
duration: "2 hours",
location: "Alster"
}
];
// Alle verfügbaren Tags sammeln
const allTags = [...new Set(activities.flatMap(activity => activity.tags))];
// App State
let selectedTags = [];
let filteredActivities = [...activities];
// DOM Elements
const tagInput = document.getElementById('tagInput');
const autocomplete = document.getElementById('autocomplete');
const selectedTagsContainer = document.getElementById('selectedTags');
const tagSuggestions = document.getElementById('tagSuggestions');
const suggestionTags = document.getElementById('suggestionTags');
const activitiesGrid = document.getElementById('activitiesGrid');
const activityCount = document.getElementById('activityCount');
const noResults = document.getElementById('noResults');
// Event Listeners
tagInput.addEventListener('input', handleTagInput);
tagInput.addEventListener('keydown', handleKeydown);
document.addEventListener('click', handleDocumentClick);
// Tag Input Handler
function handleTagInput(e) {
const value = e.target.value.toLowerCase().trim();
if (value === '') {
autocomplete.classList.add('hidden');
return;
}
const matchingTags = allTags.filter(tag =>
tag.toLowerCase().includes(value) &&
!selectedTags.includes(tag)
).slice(0, 5);
if (matchingTags.length > 0) {
showAutocomplete(matchingTags);
} else {
autocomplete.classList.add('hidden');
}
}
// Show Autocomplete
function showAutocomplete(tags) {
autocomplete.innerHTML = tags.map(tag => `
<div class="tag-suggestion px-4 py-2 cursor-pointer hover:bg-gray-100" data-tag="${tag}">
${tag}
</div>
`).join('');
autocomplete.classList.remove('hidden');
// Add click listeners to suggestions
autocomplete.querySelectorAll('.tag-suggestion').forEach(el => {
el.addEventListener('click', () => selectTag(el.dataset.tag));
});
}
// Handle Keydown
function handleKeydown(e) {
if (e.key === 'Enter') {
const value = tagInput.value.toLowerCase().trim();
if (value && allTags.some(tag => tag.toLowerCase() === value)) {
const tag = allTags.find(tag => tag.toLowerCase() === value);
selectTag(tag);
}
}
}
// Document Click Handler
function handleDocumentClick(e) {
if (!tagInput.contains(e.target) && !autocomplete.contains(e.target)) {
autocomplete.classList.add('hidden');
}
}
// Select Tag
function selectTag(tag) {
if (!selectedTags.includes(tag)) {
selectedTags.push(tag);
tagInput.value = '';
autocomplete.classList.add('hidden');
updateUI();
}
}
// Remove Tag
function removeTag(tag) {
selectedTags = selectedTags.filter(t => t !== tag);
updateUI();
}
// Update UI
function updateUI() {
updateSelectedTags();
updateTagSuggestions();
filterActivities();
updateActivitiesGrid();
updateActivityCount();
}
// Update Selected Tags
function updateSelectedTags() {
selectedTagsContainer.innerHTML = selectedTags.map(tag => `
<span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-blue-100 text-blue-800">
${tag}
<button onclick="removeTag('${tag}')" class="ml-2 text-blue-600 hover:text-blue-800">×</button>
</span>
`).join('');
}
// Update Tag Suggestions
function updateTagSuggestions() {
if (selectedTags.length === 0) {
tagSuggestions.classList.add('hidden');
return;
}
// Find activities that match current selected tags
const matchingActivities = activities.filter(activity =>
selectedTags.every(tag => activity.tags.includes(tag))
);
// Get tags from matching activities that aren't already selected
const suggestionTagsList = [...new Set(
matchingActivities.flatMap(activity => activity.tags)
)].filter(tag => !selectedTags.includes(tag)).slice(0, 3);
if (suggestionTagsList.length > 0) {
suggestionTags.innerHTML = suggestionTagsList.map(tag => `
<button onclick="selectTag('${tag}')" class="px-3 py-1 rounded-full text-sm font-medium bg-gray-100 text-gray-700 hover:bg-gray-200 transition-colors">
${tag}
</button>
`).join('');
tagSuggestions.classList.remove('hidden');
} else {
tagSuggestions.classList.add('hidden');
}
}
// Filter Activities
function filterActivities() {
if (selectedTags.length === 0) {
filteredActivities = [...activities];
} else {
filteredActivities = activities.filter(activity =>
selectedTags.every(tag => activity.tags.includes(tag))
);
}
}
// Update Activities Grid
function updateActivitiesGrid() {
if (filteredActivities.length === 0) {
activitiesGrid.classList.add('hidden');
noResults.classList.remove('hidden');
} else {
activitiesGrid.classList.remove('hidden');
noResults.classList.add('hidden');
activitiesGrid.innerHTML = filteredActivities.map(activity => `
<div class="activity-card bg-white rounded-xl shadow-md overflow-hidden">
<div class="p-6">
<div class="text-4xl mb-4">${activity.image}</div>
<h3 class="text-xl font-semibold text-gray-800 mb-2">${activity.name}</h3>
<p class="text-gray-600 mb-4">${activity.description}</p>
<div class="flex flex-wrap gap-1 mb-4">
${activity.tags.map(tag => `
<span class="px-2 py-1 bg-gray-100 text-gray-600 text-xs rounded-full">
${tag}
</span>
`).join('')}
</div>
<div class="flex justify-between items-center text-sm text-gray-500">
<span>📍 ${activity.location}</span>
<span>⏱️ ${activity.duration}</span>
<span>💰 ${activity.price}</span>
</div>
</div>
</div>
`).join('');
}
}
// Update Activity Count
function updateActivityCount() {
if (selectedTags.length === 0) {
activityCount.textContent = `${filteredActivities.length} Aktivitäten`;
} else {
activityCount.textContent = `${filteredActivities.length} Aktivitäten gefunden für: ${selectedTags.join(', ')}`;
}
}
// Initialize App
updateUI();