@bmlt-enabled/croutonjs 4.1.12 → 4.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/crouton-gmaps.js CHANGED
@@ -11189,7 +11189,9 @@ function CroutonLocalization(language) {
11189
11189
  'all': 'الجميع',
11190
11190
  'menu': 'القائمة',
11191
11191
  'search for meetings': 'البحث عن الاجتماعات',
11192
+ 'search by location': 'البحث حسب الموقع',
11192
11193
  'show meetings near...': 'أظهر الاجتماعات بالقرب من...',
11194
+ 'closest meetings': 'أقرب الاجتماعات',
11193
11195
  'filter meetings': 'سرد الاجتماعات',
11194
11196
  'visible meeting list': 'قائمة الاجتماعات المرئية',
11195
11197
  'enter a city or zip code': 'أدخل المدينة أو الرمز البريدي',
@@ -11210,6 +11212,10 @@ function CroutonLocalization(language) {
11210
11212
  "waze": "وايز",
11211
11213
  "waze_desc": "فتح في تطبيق وايز",
11212
11214
  'address_lookup_fail': 'تعذر العثور على العنوان. يرجى المحاولة مرة أخرى.',
11215
+ "favorites": "المفضلة",
11216
+ "choose one": "اختر واحدا",
11217
+ "widen search": "البحث الواسع",
11218
+ "cancel search": "إلغاء البحث"
11213
11219
  },
11214
11220
  "cs": {
11215
11221
  "days_of_the_week": ["", "Neděle", "Pondělí", "Úterý", "Středa", "Čtvrtek", "Pátek", "Sobota"],
@@ -11249,7 +11255,9 @@ function CroutonLocalization(language) {
11249
11255
  "all": "Jakékoli",
11250
11256
  "menu": "Menu",
11251
11257
  "search for meetings": "Hledat setkání",
11258
+ 'search by location': 'Vyhledávání podle lokality',
11252
11259
  "show meetings near...": "Setkání v jiných městech",
11260
+ 'closest meetings': "Nejbližší setkání",
11253
11261
  "filter meetings": "Filtr pro jazyk, formát atd.",
11254
11262
  "visible meeting list": "Viditelný seznam setkání jako tabulka",
11255
11263
  "enter a city or zip code": "Město nebo PSČ",
@@ -11260,7 +11268,6 @@ function CroutonLocalization(language) {
11260
11268
  "meeting details": "Podrobnosti o setkání",
11261
11269
  "report update": "Nahlásit aktualizaci",
11262
11270
  "filter by visibility": "Zobrazit pouze setkání, která jsou aktuálně na mapě",
11263
- "go": "Jít!",
11264
11271
  "get_directions": "Získat pokyny",
11265
11272
  "select_map_app": "Vyberte aplikaci pro mapy",
11266
11273
  "apple_maps": "Apple Mapy",
@@ -11275,6 +11282,10 @@ function CroutonLocalization(language) {
11275
11282
  "sorted by distance": "Seřazeno podle vzdálenosti",
11276
11283
  "address_lookup_fail": "Adresu se nepodařilo najít. Zkuste to prosím znovu.",
11277
11284
  "contact": "Kontakt",
11285
+ "favorites": "Oblíbené",
11286
+ "choose one": "Vyberte si jedno:",
11287
+ "widen search": "Rozšířit vyhledávání",
11288
+ "cancel search": "Zrušit vyhledávání",
11278
11289
  },
11279
11290
  "da-DK": {
11280
11291
  "days_of_the_week": ["", "Søndag", "Mandag", "Tirsdag", "Onsdag", "Torsdag", "Fredag", "Lørdag"],
@@ -11291,7 +11302,7 @@ function CroutonLocalization(language) {
11291
11302
  "formats": "Struktur",
11292
11303
  "map": "Kort",
11293
11304
  "neighborhood": "Neighborhood",
11294
- "near_me": "Find nearby meetings",
11305
+ "near_me": "Find closest meetings",
11295
11306
  "text_search": "Find meetings there",
11296
11307
  "click_search": "Find meetings near to a point on the map",
11297
11308
  "pan_and_zoom": "Pan + Zoom",
@@ -11321,7 +11332,9 @@ function CroutonLocalization(language) {
11321
11332
  'menu': 'Menu',
11322
11333
  "bmlt2ics": "Add to your calendar",
11323
11334
  'search for meetings': 'Search for meetings',
11335
+ 'search by location': 'Søg efter placering',
11324
11336
  'show meetings near...': 'Show meetings near...',
11337
+ 'closest meetings': 'Nærmeste møder',
11325
11338
  'filter meetings': 'Filter meetings',
11326
11339
  'visible meeting list': 'Visible meetings as list',
11327
11340
  'enter a city or zip code': 'Enter a city or zip code',
@@ -11341,6 +11354,10 @@ function CroutonLocalization(language) {
11341
11354
  "sorted by distance": "Sorteret efter Afstand",
11342
11355
  "address_lookup_fail": "Adressen kunne ikke findes. Prøv venligst igen.",
11343
11356
  "contact": "Kontakt",
11357
+ "favorites": "Favoritter",
11358
+ "choose one": "Vælg én:",
11359
+ "widen search": "Udvid søgning",
11360
+ "cancel search": "Annuller søgning",
11344
11361
  },
11345
11362
  "de-DE": {
11346
11363
  "days_of_the_week": ["", "Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag"],
@@ -11386,7 +11403,9 @@ function CroutonLocalization(language) {
11386
11403
  'all': 'Alle',
11387
11404
  'menu': 'Menu',
11388
11405
  'search for meetings': 'Search for meetings',
11406
+ 'search by location': 'Suche nach Standort',
11389
11407
  'show meetings near...': 'Meetings in andere Städten',
11408
+ 'closest meetings': 'Nächstgelegene Meetings',
11390
11409
  'filter meetings': 'Filter für Sprache, Format usw.',
11391
11410
  'visible meeting list': 'Sichtbare Meetings als Tabelle',
11392
11411
  'enter a city or zip code': 'Stadt oder PLZ',
@@ -11397,7 +11416,6 @@ function CroutonLocalization(language) {
11397
11416
  'meeting details': "Genaueres zum Meeting",
11398
11417
  'report update': "Report an Update",
11399
11418
  'filter by visibility': "Zeige nur Meetings, die gerade auf dem Karte stehen",
11400
- 'go': "Los!",
11401
11419
  "get_directions": "Wegbeschreibung",
11402
11420
  "select_map_app": "Karten-App auswählen",
11403
11421
  "apple_maps": "Apple Maps",
@@ -11415,6 +11433,10 @@ function CroutonLocalization(language) {
11415
11433
  "distance": "Entfernung",
11416
11434
  "all meetings": "Alle Meetings",
11417
11435
  'upcoming meetings': "Nächste 24 Stunden",
11436
+ "favorites": "Favoriten",
11437
+ "choose one": "Auswählen:",
11438
+ "widen search": "Suche ausweiten",
11439
+ "cancel search": "Abbrechen",
11418
11440
  },
11419
11441
  'el-GR': {
11420
11442
  "days_of_the_week": ["", "Κυριακή", "Δευτέρα", "Τρίτη", "Τετάρτη", "Πέμπτη", "Παρασκευή", "Σάββατο"],
@@ -11460,7 +11482,9 @@ function CroutonLocalization(language) {
11460
11482
  "all": "",
11461
11483
  "menu": "Μενού",
11462
11484
  "search for meetings": "Αναζήτηση ομάδων",
11485
+ 'search by location': 'Αναζήτηση ανά τοποθεσία',
11463
11486
  "show meetings near...": "Εμφάνιση ομάδων κοντά σε..",
11487
+ "closest meetings": "Πλησιέστερες Συναντήσεις",
11464
11488
  "filter meetings": "Φίλτραρε τις ομάδες",
11465
11489
  "visible meeting list": "Λίστα ορατών ομάδων",
11466
11490
  "enter a city or zip code": "Εισαγάγετε μια πόλη ή ταχυδρομικό κώδικα",
@@ -11472,7 +11496,6 @@ function CroutonLocalization(language) {
11472
11496
  "report update": "Ανέφερε ενημέρωση",
11473
11497
  "filter by visibility": "Φίλτραρε ανα ορατές ομάδες",
11474
11498
  "open search dialog": "’νοιξε αναζήτηση",
11475
- "go": "Έναρξη",
11476
11499
  "get_directions": "Λάβετε οδηγίες",
11477
11500
  "select_map_app": "Διαλέξτε εφαρμογή χάρτη",
11478
11501
  "apple_maps": "Χάρτες Apple",
@@ -11507,6 +11530,10 @@ function CroutonLocalization(language) {
11507
11530
  "meets virtually and in person": "Διαδικτυακή και φυσική ομάδα",
11508
11531
  "all meetings": "Όλες οι ομάδες",
11509
11532
  "upcoming meetings": "Προσεχείς ομάδες",
11533
+ "favorites": "Αγαπημένα",
11534
+ "choose one": "Επιλέξτε ένα:",
11535
+ "widen search": "Διεύρυνση αναζήτησης",
11536
+ "cancel search": "Ακύρωση αναζήτησης",
11510
11537
  },
11511
11538
  "en-AU": {
11512
11539
  "days_of_the_week": ["", "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
@@ -11523,7 +11550,7 @@ function CroutonLocalization(language) {
11523
11550
  "formats": "Formats",
11524
11551
  "map": "Map",
11525
11552
  "neighborhood": "Neighbourhood",
11526
- "near_me": "Find nearby meetings",
11553
+ "near_me": "Find closest meetings",
11527
11554
  "text_search": "Find meetings there",
11528
11555
  "click_search": "Find meetings near to a point on the map",
11529
11556
  "languages": "Languages",
@@ -11552,7 +11579,9 @@ function CroutonLocalization(language) {
11552
11579
  'all': 'All',
11553
11580
  'menu': 'Menu',
11554
11581
  'search for meetings': 'Search for meetings',
11582
+ 'search by location': 'Search by Location',
11555
11583
  'show meetings near...': 'Show meetings near...',
11584
+ 'closest meetings': "Closest Meetings",
11556
11585
  'filter meetings': 'Filter meetings',
11557
11586
  'visible meeting list': 'Visible meetings as list',
11558
11587
  'enter a city or zip code': 'Enter a city and state',
@@ -11575,6 +11604,10 @@ function CroutonLocalization(language) {
11575
11604
  "zoomed-out-message": "We can't search the entire area for meetings. Please zoom in or do a new search.",
11576
11605
  "sorted by distance": "Sorted by Distance",
11577
11606
  "address_lookup_fail": "The address could not be found. Please try again.",
11607
+ "favorites": "Favorites",
11608
+ "choose one": "Select one:",
11609
+ "widen search": "Widen Search",
11610
+ "cancel search": "Cancel Search",
11578
11611
  },
11579
11612
  "en-CA": {
11580
11613
  "days_of_the_week": ["", "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
@@ -11591,7 +11624,7 @@ function CroutonLocalization(language) {
11591
11624
  "formats": "Formats",
11592
11625
  "map": "Map",
11593
11626
  "neighborhood": "Neighborhood",
11594
- "near_me": "Find nearby meetings",
11627
+ "near_me": "Find closest meetings",
11595
11628
  "text_search": "Find meetings there",
11596
11629
  "click_search": "Find meetings near to a point on the map",
11597
11630
  "pan_and_zoom": "Pan + Zoom",
@@ -11620,7 +11653,9 @@ function CroutonLocalization(language) {
11620
11653
  'all': 'All',
11621
11654
  'menu': 'Menu',
11622
11655
  'search for meetings': 'Search for meetings',
11656
+ 'search by location': 'Search by Location',
11623
11657
  'show meetings near...': 'Show meetings near...',
11658
+ 'closest meetings': "Closest Meetings",
11624
11659
  'filter meetings': 'Filter meetings',
11625
11660
  'visible meeting list': 'Visible meetings as list',
11626
11661
  'enter a city or zip code': 'Enter a city or zip code',
@@ -11644,6 +11679,10 @@ function CroutonLocalization(language) {
11644
11679
  "zoomed-out-message": "We can't search the entire area for meetings. Please zoom in or do a new search.",
11645
11680
  "sorted by distance": "Sorted by Distance",
11646
11681
  "address_lookup_fail": "The address could not be found. Please try again.",
11682
+ "favorites": "Favorites",
11683
+ "choose one": "Select one:",
11684
+ "widen search": "Widen Search",
11685
+ "cancel search": "Cancel Search",
11647
11686
  },
11648
11687
  "en-NZ": {
11649
11688
  "days_of_the_week": ["", "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
@@ -11660,7 +11699,7 @@ function CroutonLocalization(language) {
11660
11699
  "formats": "Formats",
11661
11700
  "map": "Map",
11662
11701
  "neighborhood": "Neighborhood",
11663
- "near_me": "Find nearby meetings",
11702
+ "near_me": "Find closest meetings",
11664
11703
  "text_search": "Find meetings there",
11665
11704
  "click_search": "Find meetings near to a point on the map",
11666
11705
  "pan_and_zoom": "Pan + Zoom",
@@ -11689,7 +11728,9 @@ function CroutonLocalization(language) {
11689
11728
  'all': 'All',
11690
11729
  'menu': 'Menu',
11691
11730
  'search for meetings': 'Search for meetings',
11731
+ 'search by location': 'Search by Location',
11692
11732
  'show meetings near...': 'Show meetings near...',
11733
+ 'closest meetings': "Closest Meetings",
11693
11734
  'filter meetings': 'Filter meetings',
11694
11735
  'visible meeting list': 'Visible meetings as list',
11695
11736
  'enter a city or zip code': 'Enter a city or zip code',
@@ -11713,6 +11754,10 @@ function CroutonLocalization(language) {
11713
11754
  "zoomed-out-message": "We can't search the entire area for meetings. Please zoom in or do a new search.",
11714
11755
  "sorted by distance": "Sorted by Distance",
11715
11756
  "address_lookup_fail": "The address could not be found. Please try again.",
11757
+ "favorites": "Favorites",
11758
+ "choose one": "Select one:",
11759
+ "widen search": "Widen Search",
11760
+ "cancel search": "Cancel Search",
11716
11761
  },
11717
11762
  "en-UK": {
11718
11763
  "days_of_the_week": ["", "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
@@ -11729,7 +11774,7 @@ function CroutonLocalization(language) {
11729
11774
  "formats": "Formats",
11730
11775
  "map": "Map",
11731
11776
  "neighborhood": "Neighborhood",
11732
- "near_me": "Find nearby meetings",
11777
+ "near_me": "Find closest meetings",
11733
11778
  "text_search": "Find meetings there",
11734
11779
  "click_search": "Find meetings near to a point on the map",
11735
11780
  "pan_and_zoom": "Pan + Zoom",
@@ -11759,7 +11804,9 @@ function CroutonLocalization(language) {
11759
11804
  'menu': "Menu",
11760
11805
  "bmlt2ics": "Add to your calendar",
11761
11806
  'search for meetings': 'Search for meetings',
11807
+ 'search by location': 'Search by Location',
11762
11808
  'show meetings near...': 'Show meetings near...',
11809
+ 'closest meetings': "Closest Meetings",
11763
11810
  'filter meetings': 'Filter meetings',
11764
11811
  'visible meeting list': 'Visible meetings as list',
11765
11812
  'enter a city or zip code': 'Enter a city or zip code',
@@ -11782,6 +11829,10 @@ function CroutonLocalization(language) {
11782
11829
  "zoomed-out-message": "We can't search the entire area for meetings. Please zoom in or do a new search.",
11783
11830
  "sorted by distance": "Sorted by Distance",
11784
11831
  "address_lookup_fail": "The address could not be found. Please try again.",
11832
+ "favorites": "Favorites",
11833
+ "choose one": "Select one:",
11834
+ "widen search": "Widen Search",
11835
+ "cancel search": "Cancel Search",
11785
11836
  },
11786
11837
 
11787
11838
  "en-US": {
@@ -11799,7 +11850,7 @@ function CroutonLocalization(language) {
11799
11850
  "formats": "Formats",
11800
11851
  "map": "Map",
11801
11852
  "neighborhood": "Neighborhood",
11802
- "near_me": "Find nearby meetings",
11853
+ "near_me": "Find closest meetings",
11803
11854
  "text_search": "Find meetings there",
11804
11855
  "click_search": "Find meetings near to a point on the map",
11805
11856
  "pan_and_zoom": "Pan + Zoom",
@@ -11828,7 +11879,9 @@ function CroutonLocalization(language) {
11828
11879
  'all': 'All',
11829
11880
  'menu': "Menu",
11830
11881
  'search for meetings': 'Search for meetings',
11882
+ 'search by location': 'Search by Location',
11831
11883
  'show meetings near...': 'Show meetings near...',
11884
+ 'closest meetings': "Closest Meetings",
11832
11885
  'filter meetings': 'Filter meetings',
11833
11886
  'visible meeting list': 'Visible meetings as list',
11834
11887
  'enter a city or zip code': 'Enter a city or zip code',
@@ -11875,6 +11928,10 @@ function CroutonLocalization(language) {
11875
11928
  "more info": "More Info",
11876
11929
  "all meetings": "All Meetings",
11877
11930
  'upcoming meetings': "Upcoming Meetings",
11931
+ "favorites": "Favorites",
11932
+ "choose one": "Select one:",
11933
+ "widen search": "Widen Search",
11934
+ "cancel search": "Cancel Search",
11878
11935
  },
11879
11936
  "es-US": {
11880
11937
  "days_of_the_week": ["", "Domingo", "Lunes", "Martes", "Miércoles", "Jueves", "Viernes", "Sábado"],
@@ -11921,7 +11978,9 @@ function CroutonLocalization(language) {
11921
11978
  'all': 'Todos',
11922
11979
  'menu': "Menú",
11923
11980
  'search for meetings': 'Buscar reuniones',
11981
+ 'search by location': 'Buscar por ubicación',
11924
11982
  'show meetings near...': 'Mostrar reuniones cercanas...',
11983
+ 'closest meetings': 'Reuniones más cercanas',
11925
11984
  'filter meetings': 'Filtrar reuniones',
11926
11985
  'visible meeting list': 'Listado visible de reuniones',
11927
11986
  'enter a city or zip code': 'Introduce una ciudad o un código postal',
@@ -11945,6 +12004,10 @@ function CroutonLocalization(language) {
11945
12004
  "zoomed-out-message": "No podemos buscar reuniones en toda la zona. Por favor, acérquese o haga una nueva búsqueda.",
11946
12005
  "sorted by distance": "Ordenado por distancia",
11947
12006
  "address_lookup_fail": "The address could not be found. Please try again.",
12007
+ "favorites": "Favoritos",
12008
+ "choose one": "Elige uno:",
12009
+ "widen search": "Búsqueda amplia",
12010
+ "cancel search": "Cancelar búsqueda",
11948
12011
  },
11949
12012
  "fa-IR": {
11950
12013
  "days_of_the_week": ["", 'یَکشَنب', 'دوشَنبه', 'سه‌شنبه', 'چهار شنبه', 'پَنج شَنبه', 'جُمعه', 'شَنبه'],
@@ -11997,7 +12060,9 @@ function CroutonLocalization(language) {
11997
12060
  "all": "همه",
11998
12061
  "menu": " منوی",
11999
12062
  "search for meetings": "جستجوی جلسه ها",
12063
+ 'search by location': 'جستجو بر اساس مکان',
12000
12064
  "show meetings near...": "نمایش جلسات نزدیک",
12065
+ "closest meetings": "نزدیک ترین ملاقات ها",
12001
12066
  "filter meetings": "فیلتر جلسات",
12002
12067
  "visible meeting list": "جلسات قابل مشاهده به عنوان لیست",
12003
12068
  "enter a city or zip code": "شهر یا کد پستی را وارد کنید",
@@ -12008,7 +12073,6 @@ function CroutonLocalization(language) {
12008
12073
  "meeting details": "جزئیات جلسه",
12009
12074
  "report update": "گزارش به‌روزرسانی",
12010
12075
  "filter by visibility": "فقط این جلسات را فهرست کنید",
12011
- "go": "برو",
12012
12076
  "distance": "فاصله",
12013
12077
  "get_directions": "دریافت مسیر",
12014
12078
  "select_map_app": "انتخاب برنامه نقشه",
@@ -12027,6 +12091,10 @@ function CroutonLocalization(language) {
12027
12091
  'comments': 'توضیحات',
12028
12092
  'group details': 'نمایش گروه',
12029
12093
  "address_lookup_fail": "آدرس پیدا نشد. لطفا دوباره تلاش کنید.",
12094
+ "favorites": "مورد علاقه",
12095
+ "choose one": "یکی را انتخاب کنید",
12096
+ "widen search": "جستجوی گسترده",
12097
+ "cancel search": "لغو جستجو",
12030
12098
  },
12031
12099
  "fr-CA": {
12032
12100
  "days_of_the_week": ["", "Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi"],
@@ -12072,7 +12140,9 @@ function CroutonLocalization(language) {
12072
12140
  "all": "Tout",
12073
12141
  "menu": "Menu",
12074
12142
  "search for meetings": "Rechercher des réunions",
12143
+ 'search by location': 'Recherche par localisation',
12075
12144
  "show meetings near...": "Afficher les réunions près de...",
12145
+ "closest meetings": "Réunions les plus proches",
12076
12146
  "filter meetings": "Filtrer les réunions",
12077
12147
  "visible meeting list": "Liste des réunions visibles",
12078
12148
  "enter a city or zip code": "Entrer une ville ou un code postal",
@@ -12095,6 +12165,10 @@ function CroutonLocalization(language) {
12095
12165
  "waze_desc": "Ouvrir dans l'application Waze",
12096
12166
  "sorted by distance": "Trié par distance",
12097
12167
  "address_lookup_fail": "L'adresse est introuvable. Veuillez réessayer.",
12168
+ "favorites": "Favoris",
12169
+ "choose one": "Choisis-en un",
12170
+ "widen search": "Recherche élargie",
12171
+ "cancel search": "Annuler la recherche",
12098
12172
  },
12099
12173
  "it-IT": {
12100
12174
  "days_of_the_week": ["", "Domenica", " Lunedì", "Martedì", "Mercoledì", "Giovedì", "Venerdì", "Sabato"],
@@ -12140,7 +12214,9 @@ function CroutonLocalization(language) {
12140
12214
  'all': 'Tutti',
12141
12215
  'menu': "Menu",
12142
12216
  'search for meetings': 'Search for meetings',
12217
+ 'search by location': 'Cerca per posizione',
12143
12218
  'show meetings near...': 'Mostra incontri nelle vicinanze..',
12219
+ 'closest meetings': "Riunioni più ravvicinati",
12144
12220
  'filter meetings': 'Filtrare le riunioni',
12145
12221
  'visible meeting list': 'Elenco riunioni visibile',
12146
12222
  'enter a city or zip code': 'Inserire una città o un codice postale',
@@ -12163,6 +12239,10 @@ function CroutonLocalization(language) {
12163
12239
  "waze_desc": "Apri nell'app Waze",
12164
12240
  "sorted by distance": "Ordinati per distanza",
12165
12241
  "address_lookup_fail": "L'indirizzo non è stato trovato. Per favore riprova.",
12242
+ "favorites": "Preferiti",
12243
+ "choose one": "Scegline uno",
12244
+ "widen search": "Ricerca larga",
12245
+ "cancel search": "Annulla ricerca",
12166
12246
  },
12167
12247
  "nl-NL": {
12168
12248
  "days_of_the_week": ["", "Zondag", "Maandag", "Dinsdag", "Woensdag", "Donderdag", "Vrijdag", "Zaterdag"],
@@ -12208,6 +12288,7 @@ function CroutonLocalization(language) {
12208
12288
  'all': 'All',
12209
12289
  'menu': "Menu",
12210
12290
  'search for meetings': 'Search for meetings',
12291
+ 'search by location': 'Search by Location',
12211
12292
  'show meetings near...': 'Show meetings near...',
12212
12293
  'filter meetings': 'Filter meetings',
12213
12294
  'visible meeting list': 'Visible meetings as list',
@@ -12275,6 +12356,7 @@ function CroutonLocalization(language) {
12275
12356
  'all': 'All',
12276
12357
  'menu': "Menu",
12277
12358
  'search for meetings': 'Search for meetings',
12359
+ 'search by location': 'Search by Location',
12278
12360
  'show meetings near...': 'Show meetings near...',
12279
12361
  'filter meetings': 'Filter meetings',
12280
12362
  'visible meeting list': 'Visible meetings as list',
@@ -12298,6 +12380,10 @@ function CroutonLocalization(language) {
12298
12380
  "waze_desc": "Otwórz w aplikacji Waze",
12299
12381
  "sorted by distance": "Posortowane według odległości",
12300
12382
  "address_lookup_fail": "The address could not be found. Please try again.",
12383
+ "favorites": "Ulubione",
12384
+ "choose one": "Wybierz jedną",
12385
+ "widen search": "Widen Search",
12386
+ "cancel search": "Cancel Search",
12301
12387
  },
12302
12388
  "pt-BR": {
12303
12389
  "days_of_the_week": ["", "Domingo", "Segunda-feira", "Terça-feira", "Quarta-feira", "Quinta-feira", "Sexta-feira", "Sábado"],
@@ -12344,6 +12430,7 @@ function CroutonLocalization(language) {
12344
12430
  'menu': "Menu",
12345
12431
  "bmlt2ics": "Add to your calendar",
12346
12432
  'search for meetings': 'Search for meetings',
12433
+ 'search by location': 'Search by Location',
12347
12434
  'show meetings near...': 'Show meetings near...',
12348
12435
  'filter meetings': 'Filter meetings',
12349
12436
  'visible meeting list': 'Visible meetings as list',
@@ -12366,6 +12453,9 @@ function CroutonLocalization(language) {
12366
12453
  "waze_desc": "Abrir no aplicativo Waze",
12367
12454
  "sorted by distance": "Classificado por distância",
12368
12455
  "address_lookup_fail": "The address could not be found. Please try again.",
12456
+ "favorites": "Favoritos",
12457
+ "widen search": "Widen Search",
12458
+ "cancel search": "Cancel Search",
12369
12459
  },
12370
12460
  "ru-RU": {
12371
12461
  "days_of_the_week": ["", "Воскресенье", "Понедельник", "Вторник", "Среда", "Четверг", "Пятница", "Суббота"],
@@ -12412,7 +12502,9 @@ function CroutonLocalization(language) {
12412
12502
  'menu': "Меню",
12413
12503
  "bmlt2ics": "Добавить в календарь",
12414
12504
  'search for meetings': 'Поиск собраний',
12505
+ 'search by location': 'Поиск по местонахождению',
12415
12506
  'show meetings near...': 'Показывать собрания рядом с...',
12507
+ 'closest meetings': 'Ближайшие встречи',
12416
12508
  'filter meetings': 'Фильтровать собрания',
12417
12509
  'visible meeting list': 'Собрания в виде списка',
12418
12510
  'enter a city or zip code': 'Введите город или почтовый индек',
@@ -12422,7 +12514,6 @@ function CroutonLocalization(language) {
12422
12514
  'meeting details': "Подробности собрания",
12423
12515
  'report update': "Report an Update",
12424
12516
  'filter by visibility': "List only these meetings",
12425
- 'go': "Go!",
12426
12517
  "distance": "Расстояние",
12427
12518
  "get_directions": "Проложить маршрут",
12428
12519
  "select_map_app": "Выбрать приложение карт",
@@ -12434,6 +12525,10 @@ function CroutonLocalization(language) {
12434
12525
  "waze_desc": "Открыть в приложении Waze",
12435
12526
  "sorted by distance": "Сортировка по расстоянию",
12436
12527
  "address_lookup_fail": "Адрес не найден. Пожалуйста, попробуйте еще раз.",
12528
+ "favorites": "Избранное",
12529
+ "choose one": "Выбери одну",
12530
+ "widen search": "Расширить поиск",
12531
+ "cancel search": "Отмена поиска",
12437
12532
  },
12438
12533
  "sk": {
12439
12534
  "days_of_the_week": ["", "Nedeľa", "Pondelok", "Utorok", "Streda", "Štvrtok", "Piatok", "Sobota"],
@@ -12473,7 +12568,9 @@ function CroutonLocalization(language) {
12473
12568
  "all": "Akékoľvek",
12474
12569
  "menu": "Menu",
12475
12570
  "search for meetings": "Hľadať stretnutia",
12571
+ 'search by location': 'Vyhľadávanie podľa lokality',
12476
12572
  "show meetings near...": "Stretnutia v iných mestách",
12573
+ "closest meetings": "Najbližšie stretnutia",
12477
12574
  "filter meetings": "Filter pre jazyk, formát atď.",
12478
12575
  "visible meeting list": "Viditeľné stretnutia ako tabuľka",
12479
12576
  "enter a city or zip code": "Mesto alebo PSČ",
@@ -12484,7 +12581,6 @@ function CroutonLocalization(language) {
12484
12581
  "meeting details": "Podrobnosti o stretnutí",
12485
12582
  "report update": "Nahlásiť aktualizáciu",
12486
12583
  "filter by visibility": "Zobraziť iba stretnutia, ktoré sú práve na mape",
12487
- "go": "Ísť!",
12488
12584
  "get_directions": "Získať trasu",
12489
12585
  "select_map_app": "Vyberte aplikáciu pre mapy",
12490
12586
  "apple_maps": "Apple Maps",
@@ -12498,6 +12594,10 @@ function CroutonLocalization(language) {
12498
12594
  "zoomed-out-message": "Nemôžeme prehľadať celú oblasť pre stretnutia. Prosím, priblížte alebo vykonajte nové vyhľadávanie.",
12499
12595
  "sorted by distance": "Zoradené podľa vzdialenosti",
12500
12596
  "address_lookup_fail": "Adresa sa nenašla. Skúste to znova prosím.",
12597
+ "favorites": "Obľúbené",
12598
+ "choose one": "Vyber si jeden",
12599
+ "widen search": "Rozšíriť vyhľadávanie",
12600
+ "cancel search": "Zrušiť vyhľadávanie",
12501
12601
  },
12502
12602
  "sv-SE": {
12503
12603
  "days_of_the_week": ["", "Söndag", "Måndag", "Tisdag", "Onsdag", "Torsdag", "Fredag", "Lördag"],
@@ -12543,6 +12643,7 @@ function CroutonLocalization(language) {
12543
12643
  'all': 'All',
12544
12644
  'menu': "Menu",
12545
12645
  'search for meetings': 'Search for meetings',
12646
+ 'search by location': 'Search by Location',
12546
12647
  'show meetings near...': 'Show meetings near...',
12547
12648
  'filter meetings': 'Filter meetings',
12548
12649
  'visible meeting list': 'Visible meetings as list',
@@ -12671,6 +12772,9 @@ function CroutonHandlebarsExtensions() {
12671
12772
  ? options.fn(this)
12672
12773
  : options.inverse(this);
12673
12774
  });
12775
+ crouton_Handlebars.registerHelper('format_list_style', function(data, options) {
12776
+ return crouton.config.format_list_style === 'formatNames' ? 'formatNames' : 'formatKeys'
12777
+ });
12674
12778
  crouton_Handlebars.registerHelper('groupsConfigured', function(data, options) {
12675
12779
  if (crouton.config.groups) {
12676
12780
  return getTrueResult(options, this);
@@ -12682,8 +12786,8 @@ function CroutonHandlebarsExtensions() {
12682
12786
  * @deprecated Since version 3.12.2, will be removed in a future version.
12683
12787
  */
12684
12788
  crouton_Handlebars.registerHelper('isVirtual', function(data, options) {
12685
- return ((data['venue_type'] === venueType.HYBRID || data['venue_type'] === venueType.VIRTUAL) || ((inArray(getMasterFormatId('HY', data), getFormats(data)) && !inArray(getMasterFormatId('TC', data), getFormats(data)))
12686
- || inArray(getMasterFormatId('VM', data), getFormats(data))))
12789
+ return ((data['venue_type'] === venueType.HYBRID || data['venue_type'] === venueType.VIRTUAL) || ((getFormats(data).includes(getMasterFormatId('HY', data)) && !getFormats(data).includes(getMasterFormatId('TC', data)))
12790
+ || getFormats(data).includes(getMasterFormatId('VM', data))))
12687
12791
  && (data['virtual_meeting_link'] || data['phone_meeting_number'] || data['virtual_meeting_additional_info']) ? getTrueResult(options, this) : getFalseResult(options, this);
12688
12792
  });
12689
12793
 
@@ -12691,83 +12795,85 @@ function CroutonHandlebarsExtensions() {
12691
12795
  * Assumes consistent set of venue type formats (enforced for newly edited meetings in root server 2.16.0 or greater)
12692
12796
  */
12693
12797
  crouton_Handlebars.registerHelper('isVirtualOnly', function(data, options) {
12694
- return data['venue_type'] === venueType.VIRTUAL || inArray(getMasterFormatId('VM', data), getFormats(data)) ? getTrueResult(options, this) : getFalseResult(options, this);
12798
+ return data['venue_type'] === venueType.VIRTUAL || getFormats(data).includes(getMasterFormatId('VM', data)) ? getTrueResult(options, this) : getFalseResult(options, this);
12695
12799
  });
12696
12800
 
12697
12801
  /**
12698
12802
  * @deprecated Since version 3.12.2 will be removed in a future version.
12699
12803
  */
12700
12804
  crouton_Handlebars.registerHelper('isHybrid', function(data, options) {
12701
- return data['venue_type'] === venueType.HYBRID || inArray(getMasterFormatId('HY', data), getFormats(data)) ? getTrueResult(options, this) : getFalseResult(options, this);
12805
+ return data['venue_type'] === venueType.HYBRID || getFormats(data).includes(getMasterFormatId('HY', data)) ? getTrueResult(options, this) : getFalseResult(options, this);
12702
12806
  });
12703
12807
 
12704
12808
  /**
12705
12809
  * Assumes consistent set of venue type formats (enforced for newly edited meetings in root server 2.16.0 or greater)
12706
12810
  */
12707
12811
  crouton_Handlebars.registerHelper('isHybridOnly', function(data, options) {
12708
- return data['venue_type'] === venueType.HYBRID || inArray(getMasterFormatId('HY', data), getFormats(data)) ? getTrueResult(options, this) : getFalseResult(options, this);
12812
+ return data['venue_type'] === venueType.HYBRID || getFormats(data).includes(getMasterFormatId('HY', data)) ? getTrueResult(options, this) : getFalseResult(options, this);
12709
12813
  });
12710
12814
 
12711
12815
  crouton_Handlebars.registerHelper('isTemporarilyClosed', function(data, options) {
12712
- return inArray(getMasterFormatId('TC', data), getFormats(data)) ? getTrueResult(options, this) : getFalseResult(options, this);
12816
+ return getFormats(data).includes(getMasterFormatId('TC', data)) ? getTrueResult(options, this) : getFalseResult(options, this);
12713
12817
  });
12714
12818
 
12715
12819
  crouton_Handlebars.registerHelper('isNotTemporarilyClosed', function(data, options) {
12716
- return !inArray(getMasterFormatId('TC', data), getFormats(data)) ? getTrueResult(options, this) : getFalseResult(options, this);
12820
+ return !getFormats(data).includes(getMasterFormatId('TC', data)) ? getTrueResult(options, this) : getFalseResult(options, this);
12717
12821
  });
12718
12822
 
12719
12823
  /**
12720
12824
  * Assumes consistent set of venue type formats (enforced for newly edited meetings in root server 2.16.0 or greater)
12721
12825
  */
12722
12826
  crouton_Handlebars.registerHelper('isInPersonOrHybrid', function(data, options) {
12723
- return data['venue_type'] !== venueType.VIRTUAL && !inArray(getMasterFormatId('VM', data), getFormats(data)) ? getTrueResult(options, this) : getFalseResult(options, this);
12827
+ return data['venue_type'] !== venueType.VIRTUAL && !getFormats(data).includes(getMasterFormatId('VM', data)) ? getTrueResult(options, this) : getFalseResult(options, this);
12724
12828
  });
12725
12829
 
12726
12830
  /**
12727
12831
  * Assumes consistent set of venue type formats (enforced for newly edited meetings in root server 2.16.0 or greater)
12728
12832
  */
12729
12833
  crouton_Handlebars.registerHelper('isInPersonOnly', function(data, options) {
12730
- return data['venue_type'] === venueType.IN_PERSON || (!inArray(getMasterFormatId('VM', data), getFormats(data))
12731
- && !inArray(getMasterFormatId('HY', data), getFormats(data))) ? getTrueResult(options, this) : getFalseResult(options, this);
12834
+ return data['venue_type'] === venueType.IN_PERSON || (!getFormats(data).includes(getMasterFormatId('VM', data))
12835
+ && !getFormats(data).includes(getMasterFormatId('HY', data))) ? getTrueResult(options, this) : getFalseResult(options, this);
12732
12836
  });
12733
12837
 
12734
12838
  /**
12735
12839
  * Assumes consistent set of venue type formats (enforced for newly edited meetings in root server 2.16.0 or greater)
12736
12840
  */
12737
12841
  crouton_Handlebars.registerHelper('isVirtualOrHybrid', function(data, options) {
12738
- return (data['venue_type'] === venueType.VIRTUAL || data['venue_type'] === venueType.HYBRID) || inArray(getMasterFormatId('VM', data), getFormats(data))
12739
- || inArray(getMasterFormatId('HY', data), getFormats(data)) ? getTrueResult(options, this) : getFalseResult(options, this);
12842
+ return (data['venue_type'] === venueType.VIRTUAL || data['venue_type'] === venueType.HYBRID) || getFormats(data).includes(getMasterFormatId('VM', data))
12843
+ || getFormats(data).includes(getMasterFormatId('HY', data)) ? getTrueResult(options, this) : getFalseResult(options, this);
12740
12844
  });
12741
12845
 
12742
12846
  crouton_Handlebars.registerHelper('hasFormats', function(formats, data, options) {
12743
- var allFound = false;
12744
- var formatsResponse = data['formats'].split(",")
12745
- var formatsParam = formats.split(",");
12847
+ const formatsResponse = data['formats'].split(",")
12848
+ const formatsParam = formats.split(",");
12746
12849
  for(var i = 0;i < formatsParam.length;i++) {
12747
- allFound = inArray(formatsParam[i], formatsResponse);
12850
+ if (!formatsResponse.includes(formatsParam[i]))
12851
+ return getFalseResult(options, this);
12748
12852
  }
12749
-
12750
- return allFound ? getTrueResult(options, this) : getFalseResult(options, this);
12853
+ return getTrueResult(options, this);
12751
12854
  });
12752
12855
  crouton_Handlebars.registerHelper('temporarilyClosed', function(data, options) {
12753
- if(data['formats_expanded'].getArrayItemByObjectKeyValue('id', getMasterFormatId('TC', data)) !== undefined) {
12754
- return data['formats_expanded'].getArrayItemByObjectKeyValue('id', getMasterFormatId('TC', data))['description'];
12856
+ const found = data['formats_expanded'].find((format)=>format['id'] === getMasterFormatId('TC', data));
12857
+ if (found !== undefined) {
12858
+ return found['description'];
12755
12859
  } else {
12756
12860
  return crouton.localization.getWord("FACILITY IS TEMPORARILY CLOSED");
12757
12861
  }
12758
12862
  });
12759
12863
 
12760
12864
  crouton_Handlebars.registerHelper('meetsVirtually', function(data, options) {
12761
- if(data['formats_expanded'].getArrayItemByObjectKeyValue('id', getMasterFormatId('VM', data)) !== undefined) {
12762
- return data['formats_expanded'].getArrayItemByObjectKeyValue('id', getMasterFormatId('VM', data))['description'];
12865
+ const found = data['formats_expanded'].find((format)=>format['id'] === getMasterFormatId('VM', data));
12866
+ if (found !== undefined) {
12867
+ return found['description'];
12763
12868
  } else {
12764
12869
  return crouton.localization.getWord("MEETS VIRTUALLY");
12765
12870
  }
12766
12871
  });
12767
12872
 
12768
12873
  crouton_Handlebars.registerHelper('meetsHybrid', function(data, options) {
12769
- if(data['formats_expanded'].getArrayItemByObjectKeyValue('id', getMasterFormatId('HY', data)) !== undefined) {
12770
- return data['formats_expanded'].getArrayItemByObjectKeyValue('id', getMasterFormatId('HY', data))['description'];
12874
+ const found = data['formats_expanded'].find((format)=>format['id'] === getMasterFormatId('HY', data));
12875
+ if (found !== undefined) {
12876
+ return found['description'];
12771
12877
  } else {
12772
12878
  return crouton.localization.getWord("MEETS VIRTUALLY AND IN PERSON");
12773
12879
  }
@@ -12818,6 +12924,18 @@ function CroutonHandlebarsExtensions() {
12818
12924
  return new crouton_Handlebars.SafeString("<a href='tel:" + text + "' target='_blank'>" + text + "</a>");
12819
12925
  });
12820
12926
 
12927
+ function isFavorite(meeting) {
12928
+ const localStorageKey = "crouton-favorites";
12929
+ const favorites = JSON.parse(localStorage.getItem(localStorageKey) || "[]");
12930
+ return favorites.includes(meeting.root_server_uri + "_" + meeting.id_bigint);
12931
+ }
12932
+ crouton_Handlebars.registerHelper('favoriteIcon', function(meeting, options) {
12933
+ return new crouton_Handlebars.SafeString("<span id='crouton-favorite-" + meeting.id_bigint + "' class='crouton-favorite glyphicon glyphicon-heart" + (!isFavorite(meeting) ? "-empty" : "") + "'></span>");
12934
+ });
12935
+
12936
+ crouton_Handlebars.registerHelper('isFavorite', function(meeting, options) {
12937
+ return new crouton_Handlebars.SafeString(isFavorite(meeting) ? 1 : 0);
12938
+ });
12821
12939
  crouton_Handlebars.registerHelper('ifEquals', function(arg1, arg2, options) {
12822
12940
  return (arg1 === arg2) ? options.fn(this) : options.inverse(this);
12823
12941
  });
@@ -12893,8 +13011,31 @@ function CroutonHandlebarsExtensions() {
12893
13011
  <br/>
12894
13012
  {{/if}}
12895
13013
  `);
13014
+ crouton_Handlebars.registerPartial('formatNames', `
13015
+ {{#each formats_expanded}}
13016
+ <a
13017
+ class="bmlt-formats btn btn-primary btn-xs"
13018
+ title=""
13019
+ data-html="true"
13020
+ tabindex="0"
13021
+ data-trigger="focus"
13022
+ role="button"
13023
+ data-toggle="popover"
13024
+ data-original-title=""
13025
+ data-placement="{{{getWord 'bootstrap-popover-placement'}}}"
13026
+ data-content="{{description}}">
13027
+ <span class="glyphicon glyphicon-search"
13028
+ aria-hidden="true"
13029
+ data-toggle="popover"
13030
+ data-trigger="focus"
13031
+ data-html="true"
13032
+ role="button"></span>{{ name }}
13033
+ </a>
13034
+ {{/each}}
13035
+ `
13036
+ )
12896
13037
  crouton_Handlebars.registerPartial('formatKeys', `
12897
- {{#if this.formats}}
13038
+ {{#if this.formats}}
12898
13039
  <a
12899
13040
  class="bmlt-formats btn btn-primary btn-xs"
12900
13041
  title=""
@@ -12916,7 +13057,6 @@ function CroutonHandlebarsExtensions() {
12916
13057
  {{/if}}
12917
13058
  `
12918
13059
  )
12919
-
12920
13060
  crouton_Handlebars.registerPartial('observerLine', `
12921
13061
  {{#if (hasObserverLine name phone email) }}
12922
13062
  <div class='observerLine'>{{getWord "Contact"}}: {{name}} <a href='tel:{{phone}}'>{{phone}}</a> <a href='mailto:{{email}}'>{{email}}</a></div>
@@ -13347,8 +13487,10 @@ this["hbs_Crouton"]["templates"]["header"] = Handlebars.template({"0":function(c
13347
13487
  + ((stack1 = lookupProperty(helpers,"each").call(alias1,((stack1 = (depth0 != null ? lookupProperty(depth0,"config") : depth0)) != null ? lookupProperty(stack1,"grouping_buttons") : stack1),{"name":"each","hash":{},"fn":container.program(2, data, 0, blockParams, depths),"inverse":container.noop,"data":data,"loc":{"start":{"line":6,"column":4},"end":{"line":8,"column":13}}})) != null ? stack1 : "")
13348
13488
  + ((stack1 = lookupProperty(helpers,"each").call(alias1,((stack1 = (depth0 != null ? lookupProperty(depth0,"config") : depth0)) != null ? lookupProperty(stack1,"formattype_grouping_buttons") : stack1),{"name":"each","hash":{},"fn":container.program(3, data, 0, blockParams, depths),"inverse":container.noop,"data":data,"loc":{"start":{"line":9,"column":4},"end":{"line":11,"column":13}}})) != null ? stack1 : "")
13349
13489
  + ((stack1 = lookupProperty(helpers,"if").call(alias1,((stack1 = (depth0 != null ? lookupProperty(depth0,"config") : depth0)) != null ? lookupProperty(stack1,"map_page") : stack1),{"name":"if","hash":{},"fn":container.program(4, data, 0, blockParams, depths),"inverse":container.noop,"data":data,"loc":{"start":{"line":12,"column":4},"end":{"line":14,"column":11}}})) != null ? stack1 : "")
13350
- + "\n"
13351
- + ((stack1 = lookupProperty(helpers,"each").call(alias1,(depth0 != null ? lookupProperty(depth0,"dropdownData") : depth0),{"name":"each","hash":{},"fn":container.program(5, data, 0, blockParams, depths),"inverse":container.noop,"data":data,"loc":{"start":{"line":16,"column":4},"end":{"line":25,"column":13}}})) != null ? stack1 : "")
13490
+ + " <div class=\"bmlt-button-container\"><a id=\"crouton_favorites_button\" tabindex=0 class=\"croutonControlButton btn btn-primary btn-sm\">"
13491
+ + container.escapeExpression((lookupProperty(helpers,"getWord")||(depth0 && lookupProperty(depth0,"getWord"))||container.hooks.helperMissing).call(alias1,"Favorites",{"name":"getWord","hash":{},"data":data,"loc":{"start":{"line":15,"column":135},"end":{"line":15,"column":158}}}))
13492
+ + "</a></div>\n\n"
13493
+ + ((stack1 = lookupProperty(helpers,"each").call(alias1,(depth0 != null ? lookupProperty(depth0,"dropdownData") : depth0),{"name":"each","hash":{},"fn":container.program(5, data, 0, blockParams, depths),"inverse":container.noop,"data":data,"loc":{"start":{"line":17,"column":4},"end":{"line":26,"column":13}}})) != null ? stack1 : "")
13352
13494
  + " </div>\n";
13353
13495
  },"1":function(container,depth0,helpers,partials,data) {
13354
13496
  var lookupProperty = container.lookupProperty || function(parent, propertyName) {
@@ -13373,8 +13515,8 @@ this["hbs_Crouton"]["templates"]["header"] = Handlebars.template({"0":function(c
13373
13515
  + alias2(alias1((depth0 != null ? lookupProperty(depth0,"field") : depth0), depth0))
13374
13516
  + "\" tabindex=0 data-field=\""
13375
13517
  + alias2(alias1((depth0 != null ? lookupProperty(depth0,"title") : depth0), depth0))
13376
- + "\" class=\"groupingButton groupingButtonLogic btn btn-primary btn-sm\">"
13377
- + alias2((lookupProperty(helpers,"getWord")||(depth0 && lookupProperty(depth0,"getWord"))||container.hooks.helperMissing).call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? lookupProperty(depth0,"title") : depth0),{"name":"getWord","hash":{},"data":data,"loc":{"start":{"line":7,"column":186},"end":{"line":7,"column":208}}}))
13518
+ + "\" class=\"groupingButton croutonControlButton groupingButtonLogic btn btn-primary btn-sm\">"
13519
+ + alias2((lookupProperty(helpers,"getWord")||(depth0 && lookupProperty(depth0,"getWord"))||container.hooks.helperMissing).call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? lookupProperty(depth0,"title") : depth0),{"name":"getWord","hash":{},"data":data,"loc":{"start":{"line":7,"column":207},"end":{"line":7,"column":229}}}))
13378
13520
  + "</a></div>\n";
13379
13521
  },"3":function(container,depth0,helpers,partials,data) {
13380
13522
  var alias1=container.lambda, alias2=container.escapeExpression, lookupProperty = container.lookupProperty || function(parent, propertyName) {
@@ -13388,7 +13530,7 @@ this["hbs_Crouton"]["templates"]["header"] = Handlebars.template({"0":function(c
13388
13530
  + alias2(alias1((depth0 != null ? lookupProperty(depth0,"field") : depth0), depth0))
13389
13531
  + "\"tabindex=0 data-field=\""
13390
13532
  + alias2(alias1((depth0 != null ? lookupProperty(depth0,"field") : depth0), depth0))
13391
- + "\" class=\"groupingButton groupingButtonLogic btn btn-primary btn-sm\">"
13533
+ + "\" class=\"groupingButton croutonControlButton groupingButtonLogic btn btn-primary btn-sm\">"
13392
13534
  + alias2(alias1((depth0 != null ? lookupProperty(depth0,"title") : depth0), depth0))
13393
13535
  + "</a></div>\n";
13394
13536
  },"4":function(container,depth0,helpers,partials,data) {
@@ -13399,8 +13541,8 @@ this["hbs_Crouton"]["templates"]["header"] = Handlebars.template({"0":function(c
13399
13541
  return undefined
13400
13542
  };
13401
13543
 
13402
- return " <div class=\"bmlt-button-container\"><a id=\"groupingButton_embeddedMapPage\" tabindex=0 data-field=\"embeddedMapPage\" class=\"groupingButton btn btn-primary btn-sm\">"
13403
- + container.escapeExpression((lookupProperty(helpers,"getWord")||(depth0 && lookupProperty(depth0,"getWord"))||container.hooks.helperMissing).call(depth0 != null ? depth0 : (container.nullContext || {}),"map",{"name":"getWord","hash":{},"data":data,"loc":{"start":{"line":13,"column":168},"end":{"line":13,"column":185}}}))
13544
+ return " <div class=\"bmlt-button-container\"><a id=\"groupingButton_embeddedMapPage\" tabindex=0 data-field=\"embeddedMapPage\" class=\"groupingButton croutonControlButton btn btn-primary btn-sm\">"
13545
+ + container.escapeExpression((lookupProperty(helpers,"getWord")||(depth0 && lookupProperty(depth0,"getWord"))||container.hooks.helperMissing).call(depth0 != null ? depth0 : (container.nullContext || {}),"map",{"name":"getWord","hash":{},"data":data,"loc":{"start":{"line":13,"column":189},"end":{"line":13,"column":206}}}))
13404
13546
  + "</a></div>\n";
13405
13547
  },"5":function(container,depth0,helpers,partials,data,blockParams,depths) {
13406
13548
  var stack1, helper, alias1=container.escapeExpression, alias2=depth0 != null ? depth0 : (container.nullContext || {}), alias3=container.hooks.helperMissing, alias4="function", lookupProperty = container.lookupProperty || function(parent, propertyName) {
@@ -13413,17 +13555,17 @@ this["hbs_Crouton"]["templates"]["header"] = Handlebars.template({"0":function(c
13413
13555
  return " <div class=\"bmlt-dropdown-container\">\n <select class=\"crouton-select filter-dropdown\" style=\"width:"
13414
13556
  + alias1(container.lambda(((stack1 = (depths[1] != null ? lookupProperty(depths[1],"config") : depths[1])) != null ? lookupProperty(stack1,"dropdown_width") : stack1), depth0))
13415
13557
  + ";\" data-placeholder=\""
13416
- + alias1(((helper = (helper = lookupProperty(helpers,"placeholder") || (depth0 != null ? lookupProperty(depth0,"placeholder") : depth0)) != null ? helper : alias3),(typeof helper === alias4 ? helper.call(alias2,{"name":"placeholder","hash":{},"data":data,"loc":{"start":{"line":18,"column":116},"end":{"line":18,"column":131}}}) : helper)))
13558
+ + alias1(((helper = (helper = lookupProperty(helpers,"placeholder") || (depth0 != null ? lookupProperty(depth0,"placeholder") : depth0)) != null ? helper : alias3),(typeof helper === alias4 ? helper.call(alias2,{"name":"placeholder","hash":{},"data":data,"loc":{"start":{"line":19,"column":116},"end":{"line":19,"column":131}}}) : helper)))
13417
13559
  + "\" data-pointer=\""
13418
- + alias1(((helper = (helper = lookupProperty(helpers,"pointer") || (depth0 != null ? lookupProperty(depth0,"pointer") : depth0)) != null ? helper : alias3),(typeof helper === alias4 ? helper.call(alias2,{"name":"pointer","hash":{},"data":data,"loc":{"start":{"line":18,"column":147},"end":{"line":18,"column":158}}}) : helper)))
13560
+ + alias1(((helper = (helper = lookupProperty(helpers,"pointer") || (depth0 != null ? lookupProperty(depth0,"pointer") : depth0)) != null ? helper : alias3),(typeof helper === alias4 ? helper.call(alias2,{"name":"pointer","hash":{},"data":data,"loc":{"start":{"line":19,"column":147},"end":{"line":19,"column":158}}}) : helper)))
13419
13561
  + "\" id="
13420
- + alias1(((helper = (helper = lookupProperty(helpers,"elementId") || (depth0 != null ? lookupProperty(depth0,"elementId") : depth0)) != null ? helper : alias3),(typeof helper === alias4 ? helper.call(alias2,{"name":"elementId","hash":{},"data":data,"loc":{"start":{"line":18,"column":163},"end":{"line":18,"column":176}}}) : helper)))
13562
+ + alias1(((helper = (helper = lookupProperty(helpers,"elementId") || (depth0 != null ? lookupProperty(depth0,"elementId") : depth0)) != null ? helper : alias3),(typeof helper === alias4 ? helper.call(alias2,{"name":"elementId","hash":{},"data":data,"loc":{"start":{"line":19,"column":163},"end":{"line":19,"column":176}}}) : helper)))
13421
13563
  + ">\n <option value=\"a-\">"
13422
- + alias1((lookupProperty(helpers,"getWord")||(depth0 && lookupProperty(depth0,"getWord"))||alias3).call(alias2,"all",{"name":"getWord","hash":{},"data":data,"loc":{"start":{"line":19,"column":25},"end":{"line":19,"column":42}}}))
13564
+ + alias1((lookupProperty(helpers,"getWord")||(depth0 && lookupProperty(depth0,"getWord"))||alias3).call(alias2,"all",{"name":"getWord","hash":{},"data":data,"loc":{"start":{"line":20,"column":25},"end":{"line":20,"column":42}}}))
13423
13565
  + " "
13424
- + alias1(((helper = (helper = lookupProperty(helpers,"placeholder") || (depth0 != null ? lookupProperty(depth0,"placeholder") : depth0)) != null ? helper : alias3),(typeof helper === alias4 ? helper.call(alias2,{"name":"placeholder","hash":{},"data":data,"loc":{"start":{"line":19,"column":43},"end":{"line":19,"column":58}}}) : helper)))
13566
+ + alias1(((helper = (helper = lookupProperty(helpers,"placeholder") || (depth0 != null ? lookupProperty(depth0,"placeholder") : depth0)) != null ? helper : alias3),(typeof helper === alias4 ? helper.call(alias2,{"name":"placeholder","hash":{},"data":data,"loc":{"start":{"line":20,"column":43},"end":{"line":20,"column":58}}}) : helper)))
13425
13567
  + "</option>\n"
13426
- + ((stack1 = lookupProperty(helpers,"each").call(alias2,(lookupProperty(helpers,"call")||(depth0 && lookupProperty(depth0,"call"))||alias3).call(alias2,(depth0 != null ? lookupProperty(depth0,"uniqueData") : depth0),((stack1 = (depths[1] != null ? lookupProperty(depths[1],"meetings") : depths[1])) != null ? lookupProperty(stack1,"meetingData") : stack1),{"name":"call","hash":{},"data":data,"loc":{"start":{"line":20,"column":14},"end":{"line":20,"column":55}}}),{"name":"each","hash":{},"fn":container.program(6, data, 0, blockParams, depths),"inverse":container.noop,"data":data,"loc":{"start":{"line":20,"column":6},"end":{"line":22,"column":15}}})) != null ? stack1 : "")
13568
+ + ((stack1 = lookupProperty(helpers,"each").call(alias2,(lookupProperty(helpers,"call")||(depth0 && lookupProperty(depth0,"call"))||alias3).call(alias2,(depth0 != null ? lookupProperty(depth0,"uniqueData") : depth0),((stack1 = (depths[1] != null ? lookupProperty(depths[1],"meetings") : depths[1])) != null ? lookupProperty(stack1,"meetingData") : stack1),{"name":"call","hash":{},"data":data,"loc":{"start":{"line":21,"column":14},"end":{"line":21,"column":55}}}),{"name":"each","hash":{},"fn":container.program(6, data, 0, blockParams, depths),"inverse":container.noop,"data":data,"loc":{"start":{"line":21,"column":6},"end":{"line":23,"column":15}}})) != null ? stack1 : "")
13427
13569
  + " </select>\n </div>\n";
13428
13570
  },"6":function(container,depth0,helpers,partials,data,blockParams,depths) {
13429
13571
  var alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=container.hooks.helperMissing, alias3=container.escapeExpression, lookupProperty = container.lookupProperty || function(parent, propertyName) {
@@ -13434,9 +13576,9 @@ this["hbs_Crouton"]["templates"]["header"] = Handlebars.template({"0":function(c
13434
13576
  };
13435
13577
 
13436
13578
  return " <option value=\"a-"
13437
- + alias3((lookupProperty(helpers,"call")||(depth0 && lookupProperty(depth0,"call"))||alias2).call(alias1,(depths[1] != null ? lookupProperty(depths[1],"objectPointer") : depths[1]),depth0,{"name":"call","hash":{},"data":data,"loc":{"start":{"line":21,"column":24},"end":{"line":21,"column":54}}}))
13579
+ + alias3((lookupProperty(helpers,"call")||(depth0 && lookupProperty(depth0,"call"))||alias2).call(alias1,(depths[1] != null ? lookupProperty(depths[1],"objectPointer") : depths[1]),depth0,{"name":"call","hash":{},"data":data,"loc":{"start":{"line":22,"column":24},"end":{"line":22,"column":54}}}))
13438
13580
  + "\">"
13439
- + alias3((lookupProperty(helpers,"call")||(depth0 && lookupProperty(depth0,"call"))||alias2).call(alias1,(depths[1] != null ? lookupProperty(depths[1],"optionName") : depths[1]),depth0,{"name":"call","hash":{},"data":data,"loc":{"start":{"line":21,"column":56},"end":{"line":21,"column":83}}}))
13581
+ + alias3((lookupProperty(helpers,"call")||(depth0 && lookupProperty(depth0,"call"))||alias2).call(alias1,(depths[1] != null ? lookupProperty(depths[1],"optionName") : depths[1]),depth0,{"name":"call","hash":{},"data":data,"loc":{"start":{"line":22,"column":56},"end":{"line":22,"column":83}}}))
13440
13582
  + "</option>\n";
13441
13583
  },"7":function(container,depth0,helpers,partials,data) {
13442
13584
  var stack1, alias1=depth0 != null ? depth0 : (container.nullContext || {}), lookupProperty = container.lookupProperty || function(parent, propertyName) {
@@ -13446,9 +13588,9 @@ this["hbs_Crouton"]["templates"]["header"] = Handlebars.template({"0":function(c
13446
13588
  return undefined
13447
13589
  };
13448
13590
 
13449
- return ((stack1 = (lookupProperty(helpers,"ifEquals")||(depth0 && lookupProperty(depth0,"ifEquals"))||container.hooks.helperMissing).call(alias1,((stack1 = (depth0 != null ? lookupProperty(depth0,"config") : depth0)) != null ? lookupProperty(stack1,"view_by") : stack1),"weekdays",{"name":"ifEquals","hash":{},"fn":container.program(8, data, 0),"inverse":container.program(9, data, 0),"data":data,"loc":{"start":{"line":30,"column":4},"end":{"line":34,"column":17}}})) != null ? stack1 : "")
13591
+ return ((stack1 = (lookupProperty(helpers,"ifEquals")||(depth0 && lookupProperty(depth0,"ifEquals"))||container.hooks.helperMissing).call(alias1,((stack1 = (depth0 != null ? lookupProperty(depth0,"config") : depth0)) != null ? lookupProperty(stack1,"view_by") : stack1),"weekdays",{"name":"ifEquals","hash":{},"fn":container.program(8, data, 0),"inverse":container.program(9, data, 0),"data":data,"loc":{"start":{"line":31,"column":4},"end":{"line":35,"column":17}}})) != null ? stack1 : "")
13450
13592
  + " <ul class=\"nav nav-tabs\">\n"
13451
- + ((stack1 = lookupProperty(helpers,"each").call(alias1,((stack1 = (depth0 != null ? lookupProperty(depth0,"config") : depth0)) != null ? lookupProperty(stack1,"day_sequence") : stack1),{"name":"each","hash":{},"fn":container.program(10, data, 0),"inverse":container.noop,"data":data,"loc":{"start":{"line":36,"column":12},"end":{"line":38,"column":21}}})) != null ? stack1 : "")
13593
+ + ((stack1 = lookupProperty(helpers,"each").call(alias1,((stack1 = (depth0 != null ? lookupProperty(depth0,"config") : depth0)) != null ? lookupProperty(stack1,"day_sequence") : stack1),{"name":"each","hash":{},"fn":container.program(10, data, 0),"inverse":container.noop,"data":data,"loc":{"start":{"line":37,"column":12},"end":{"line":39,"column":21}}})) != null ? stack1 : "")
13452
13594
  + " </ul>\n </div>\n";
13453
13595
  },"8":function(container,depth0,helpers,partials,data) {
13454
13596
  var lookupProperty = container.lookupProperty || function(parent, propertyName) {
@@ -13459,7 +13601,7 @@ this["hbs_Crouton"]["templates"]["header"] = Handlebars.template({"0":function(c
13459
13601
  };
13460
13602
 
13461
13603
  return " <div class=\"bmlt-page "
13462
- + container.escapeExpression((lookupProperty(helpers,"getWord")||(depth0 && lookupProperty(depth0,"getWord"))||container.hooks.helperMissing).call(depth0 != null ? depth0 : (container.nullContext || {}),"css-direction",{"name":"getWord","hash":{},"data":data,"loc":{"start":{"line":31,"column":30},"end":{"line":31,"column":57}}}))
13604
+ + container.escapeExpression((lookupProperty(helpers,"getWord")||(depth0 && lookupProperty(depth0,"getWord"))||container.hooks.helperMissing).call(depth0 != null ? depth0 : (container.nullContext || {}),"css-direction",{"name":"getWord","hash":{},"data":data,"loc":{"start":{"line":32,"column":30},"end":{"line":32,"column":57}}}))
13463
13605
  + " show\" id=\"nav-days\">\n";
13464
13606
  },"9":function(container,depth0,helpers,partials,data) {
13465
13607
  var lookupProperty = container.lookupProperty || function(parent, propertyName) {
@@ -13470,7 +13612,7 @@ this["hbs_Crouton"]["templates"]["header"] = Handlebars.template({"0":function(c
13470
13612
  };
13471
13613
 
13472
13614
  return " <div class=\"bmlt-page "
13473
- + container.escapeExpression((lookupProperty(helpers,"getWord")||(depth0 && lookupProperty(depth0,"getWord"))||container.hooks.helperMissing).call(depth0 != null ? depth0 : (container.nullContext || {}),"css-direction",{"name":"getWord","hash":{},"data":data,"loc":{"start":{"line":33,"column":30},"end":{"line":33,"column":57}}}))
13615
+ + container.escapeExpression((lookupProperty(helpers,"getWord")||(depth0 && lookupProperty(depth0,"getWord"))||container.hooks.helperMissing).call(depth0 != null ? depth0 : (container.nullContext || {}),"css-direction",{"name":"getWord","hash":{},"data":data,"loc":{"start":{"line":34,"column":30},"end":{"line":34,"column":57}}}))
13474
13616
  + " hide\" id=\"nav-days\">\n";
13475
13617
  },"10":function(container,depth0,helpers,partials,data) {
13476
13618
  var alias1=container.escapeExpression, lookupProperty = container.lookupProperty || function(parent, propertyName) {
@@ -13483,7 +13625,7 @@ this["hbs_Crouton"]["templates"]["header"] = Handlebars.template({"0":function(c
13483
13625
  return " <li><a href=\"#tab"
13484
13626
  + alias1(container.lambda(depth0, depth0))
13485
13627
  + "\" data-toggle=\"tab\">"
13486
- + alias1((lookupProperty(helpers,"getDayOfTheWeek")||(depth0 && lookupProperty(depth0,"getDayOfTheWeek"))||container.hooks.helperMissing).call(depth0 != null ? depth0 : (container.nullContext || {}),depth0,{"name":"getDayOfTheWeek","hash":{},"data":data,"loc":{"start":{"line":37,"column":61},"end":{"line":37,"column":85}}}))
13628
+ + alias1((lookupProperty(helpers,"getDayOfTheWeek")||(depth0 && lookupProperty(depth0,"getDayOfTheWeek"))||container.hooks.helperMissing).call(depth0 != null ? depth0 : (container.nullContext || {}),depth0,{"name":"getDayOfTheWeek","hash":{},"data":data,"loc":{"start":{"line":38,"column":61},"end":{"line":38,"column":85}}}))
13487
13629
  + "</a></li>\n";
13488
13630
  },"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data,blockParams,depths) {
13489
13631
  var stack1, alias1=depth0 != null ? depth0 : (container.nullContext || {}), lookupProperty = container.lookupProperty || function(parent, propertyName) {
@@ -13495,10 +13637,38 @@ this["hbs_Crouton"]["templates"]["header"] = Handlebars.template({"0":function(c
13495
13637
 
13496
13638
  return container.escapeExpression((lookupProperty(helpers,"startup")||(depth0 && lookupProperty(depth0,"startup"))||container.hooks.helperMissing).call(alias1,depth0,{"name":"startup","hash":{},"data":data,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":16}}}))
13497
13639
  + "\n"
13498
- + ((stack1 = lookupProperty(helpers,"if").call(alias1,((stack1 = (depth0 != null ? lookupProperty(depth0,"config") : depth0)) != null ? lookupProperty(stack1,"header") : stack1),{"name":"if","hash":{},"fn":container.program(0, data, 0, blockParams, depths),"inverse":container.noop,"data":data,"loc":{"start":{"line":2,"column":0},"end":{"line":27,"column":7}}})) != null ? stack1 : "")
13640
+ + ((stack1 = lookupProperty(helpers,"if").call(alias1,((stack1 = (depth0 != null ? lookupProperty(depth0,"config") : depth0)) != null ? lookupProperty(stack1,"header") : stack1),{"name":"if","hash":{},"fn":container.program(0, data, 0, blockParams, depths),"inverse":container.noop,"data":data,"loc":{"start":{"line":2,"column":0},"end":{"line":28,"column":7}}})) != null ? stack1 : "")
13499
13641
  + "\n"
13500
- + ((stack1 = lookupProperty(helpers,"if").call(alias1,((stack1 = (depth0 != null ? lookupProperty(depth0,"config") : depth0)) != null ? lookupProperty(stack1,"has_tabs") : stack1),{"name":"if","hash":{},"fn":container.program(7, data, 0, blockParams, depths),"inverse":container.noop,"data":data,"loc":{"start":{"line":29,"column":0},"end":{"line":41,"column":7}}})) != null ? stack1 : "");
13642
+ + ((stack1 = lookupProperty(helpers,"if").call(alias1,((stack1 = (depth0 != null ? lookupProperty(depth0,"config") : depth0)) != null ? lookupProperty(stack1,"has_tabs") : stack1),{"name":"if","hash":{},"fn":container.program(7, data, 0, blockParams, depths),"inverse":container.noop,"data":data,"loc":{"start":{"line":30,"column":0},"end":{"line":42,"column":7}}})) != null ? stack1 : "");
13501
13643
  },"useData":true,"useDepths":true});
13644
+ this["hbs_Crouton"]["templates"]["locationSearch"] = Handlebars.template({"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
13645
+ var alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=container.hooks.helperMissing, alias3=container.escapeExpression, lookupProperty = container.lookupProperty || function(parent, propertyName) {
13646
+ if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
13647
+ return parent[propertyName];
13648
+ }
13649
+ return undefined
13650
+ };
13651
+
13652
+ return "<button id=\"location-search-button\" style=\"width: auto\" class=\"location-search-button\" title=\""
13653
+ + alias3((lookupProperty(helpers,"getWord")||(depth0 && lookupProperty(depth0,"getWord"))||alias2).call(alias1,"Open Location Search Dialog",{"name":"getWord","hash":{},"data":data,"loc":{"start":{"line":1,"column":94},"end":{"line":1,"column":135}}}))
13654
+ + "\">\n <span class=\"location-search-button-label\" style=\"position:initial;\">\n "
13655
+ + alias3((lookupProperty(helpers,"getWord")||(depth0 && lookupProperty(depth0,"getWord"))||alias2).call(alias1,"Search by location",{"name":"getWord","hash":{},"data":data,"loc":{"start":{"line":3,"column":8},"end":{"line":3,"column":40}}}))
13656
+ + "\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" class=\"bi bi-crosshair\" viewBox=\"0 0 16 16\" style=\"vertical-align: sub;\">\n <path d=\"M8.5.5a.5.5 0 0 0-1 0v.518A7 7 0 0 0 1.018 7.5H.5a.5.5 0 0 0 0 1h.518A7 7 0 0 0 7.5 14.982v.518a.5.5 0 0 0 1 0v-.518A7 7 0 0 0 14.982 8.5h.518a.5.5 0 0 0 0-1h-.518A7 7 0 0 0 8.5 1.018zm-6.48 7A6 6 0 0 1 7.5 2.02v.48a.5.5 0 0 0 1 0v-.48a6 6 0 0 1 5.48 5.48h-.48a.5.5 0 0 0 0 1h.48a6 6 0 0 1-5.48 5.48v-.48a.5.5 0 0 0-1 0v.48A6 6 0 0 1 2.02 8.5h.48a.5.5 0 0 0 0-1zM8 10a2 2 0 1 0 0-4 2 2 0 0 0 0 4\"/>\n </svg>\n </span>\n</button>\n<div id=\"bmlt_location_search_modal\" class=\"modal\" style=\"display: none;\">\n <div id=\"bmlt_location_search_content\" class=\"modal-content\" style=\"border-color:unset;\">\n <span id=\"close_location_search\" class=\"modal-close\">x</span>\n <div class=\"modal-title\">"
13657
+ + alias3((lookupProperty(helpers,"getWord")||(depth0 && lookupProperty(depth0,"getWord"))||alias2).call(alias1,"Search by location",{"name":"getWord","hash":{},"data":data,"loc":{"start":{"line":12,"column":33},"end":{"line":12,"column":65}}}))
13658
+ + "</div>\n <div class=\"crouton-modal-search-page\">\n <div id=\"modal-location-search-box\" class=\"modal-search\">\n <div>\n <input id=\"bmlt-location-search-goto-text\" type=\"text\" class=\"crouton-goto-text\" autocomplete=\"off\"\n placeholder=\""
13659
+ + alias3((lookupProperty(helpers,"getWord")||(depth0 && lookupProperty(depth0,"getWord"))||alias2).call(alias1,"Enter a city or zip code",{"name":"getWord","hash":{},"data":data,"loc":{"start":{"line":17,"column":33},"end":{"line":17,"column":71}}}))
13660
+ + "\">\n </div>\n <button id=\"bmlt-location-search-text-button\" class=\"filter-button\">"
13661
+ + alias3((lookupProperty(helpers,"getWord")||(depth0 && lookupProperty(depth0,"getWord"))||alias2).call(alias1,"text_search",{"name":"getWord","hash":{},"data":data,"loc":{"start":{"line":19,"column":84},"end":{"line":19,"column":109}}}))
13662
+ + "</button>\n </div>\n <div class=\"modal-search\">\n <button id=\"bmlt-location-search-nearbyMeetings\" class=\"filter-button\">"
13663
+ + alias3((lookupProperty(helpers,"getWord")||(depth0 && lookupProperty(depth0,"getWord"))||alias2).call(alias1,"near_me",{"name":"getWord","hash":{},"data":data,"loc":{"start":{"line":22,"column":87},"end":{"line":22,"column":108}}}))
13664
+ + "</button>\n </div>\n <div id=\"location-search-widen\" class=\"modal-search\">\n <button id=\"bmlt-location-search-widen\" class=\"filter-button\">"
13665
+ + alias3((lookupProperty(helpers,"getWord")||(depth0 && lookupProperty(depth0,"getWord"))||alias2).call(alias1,"Widen Search",{"name":"getWord","hash":{},"data":data,"loc":{"start":{"line":25,"column":78},"end":{"line":25,"column":104}}}))
13666
+ + "</button>\n </div>\n <div>\n <!--<button id=\"location-search-close\" class=\"advanced-search-button\" style=\"float:left\">"
13667
+ + alias3((lookupProperty(helpers,"getWord")||(depth0 && lookupProperty(depth0,"getWord"))||alias2).call(alias1,"Close",{"name":"getWord","hash":{},"data":data,"loc":{"start":{"line":28,"column":96},"end":{"line":28,"column":115}}}))
13668
+ + "</a>-->\n <button id=\"bmlt-location_search-cancel\" class=\"advanced-search-button\">"
13669
+ + alias3((lookupProperty(helpers,"getWord")||(depth0 && lookupProperty(depth0,"getWord"))||alias2).call(alias1,"Cancel Search",{"name":"getWord","hash":{},"data":data,"loc":{"start":{"line":29,"column":88},"end":{"line":29,"column":115}}}))
13670
+ + "</button>\n </div>\n </div>\n <div class=\"crouton-modal-search-choice\"></div>\n </div>\n</div>";
13671
+ },"useData":true});
13502
13672
  this["hbs_Crouton"]["templates"]["main"] = Handlebars.template({"0":function(container,depth0,helpers,partials,data) {
13503
13673
  var stack1, helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=container.hooks.helperMissing, alias3=container.escapeExpression, lookupProperty = container.lookupProperty || function(parent, propertyName) {
13504
13674
  if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
@@ -13695,11 +13865,11 @@ this["hbs_Crouton"]["templates"]["mapMenu"] = Handlebars.template({"0":function(
13695
13865
  + ((stack1 = lookupProperty(helpers,"each").call(alias2,((stack1 = (depth0 != null ? lookupProperty(depth0,"meetings") : depth0)) != null ? lookupProperty(stack1,"bydays") : stack1),{"name":"each","hash":{},"fn":container.program(4, data, 0, blockParams, depths),"inverse":container.noop,"data":data,"loc":{"start":{"line":36,"column":8},"end":{"line":38,"column":17}}})) != null ? stack1 : "")
13696
13866
  + " </table>\n </div>\n</div>\n<div id=\"geocoding_modal\" class=\"modal\" style=\"display: none;\">\n <div id=\"search_content\" class=\"modal-content\">\n <span class=\"modal-title\">"
13697
13867
  + alias1((lookupProperty(helpers,"getWord")||(depth0 && lookupProperty(depth0,"getWord"))||alias3).call(alias2,"Show meetings near...",{"name":"getWord","hash":{},"data":data,"loc":{"start":{"line":44,"column":28},"end":{"line":44,"column":63}}}))
13698
- + "</span><span id=\"close_search\" class=\"modal-close\">x</span>\n <p></p>\n <div class=\"modal-search\">\n "
13699
- + alias1((lookupProperty(helpers,"getWord")||(depth0 && lookupProperty(depth0,"getWord"))||alias3).call(alias2,"Enter a city or zip code",{"name":"getWord","hash":{},"data":data,"loc":{"start":{"line":47,"column":12},"end":{"line":47,"column":50}}}))
13700
- + "\n <input id=\"goto-text\" type=\"text\">\n <button id=\"goto-button\" class=\"filter-button\">"
13701
- + alias1((lookupProperty(helpers,"getWord")||(depth0 && lookupProperty(depth0,"getWord"))||alias3).call(alias2,"go",{"name":"getWord","hash":{},"data":data,"loc":{"start":{"line":49,"column":59},"end":{"line":49,"column":75}}}))
13702
- + "</button>\n </div>\n </div>\n</div>\n\n";
13868
+ + "</span><span id=\"close_search\" class=\"modal-close\">x</span>\n <p></p>\n <div class=\"crouton-modal-search-page\">\n <div class=\"modal-search\">\n "
13869
+ + alias1((lookupProperty(helpers,"getWord")||(depth0 && lookupProperty(depth0,"getWord"))||alias3).call(alias2,"Enter a city or zip code",{"name":"getWord","hash":{},"data":data,"loc":{"start":{"line":48,"column":16},"end":{"line":48,"column":54}}}))
13870
+ + "\n <div>\n <input id=\"goto-text\" class=\"crouton-goto-text\" type=\"text\" autocomplete=\"off\">\n </div>\n <button id=\"goto-button\" class=\"filter-button\">"
13871
+ + alias1((lookupProperty(helpers,"getWord")||(depth0 && lookupProperty(depth0,"getWord"))||alias3).call(alias2,"text_search",{"name":"getWord","hash":{},"data":data,"loc":{"start":{"line":52,"column":63},"end":{"line":52,"column":88}}}))
13872
+ + "</button>\n </div>\n </div>\n <div class=\"crouton-modal-search-choice\"></div>\n </div>\n</div>\n\n";
13703
13873
  },"usePartial":true,"useData":true,"useDepths":true});
13704
13874
  this["hbs_Crouton"]["templates"]["mapSearch"] = Handlebars.template({"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
13705
13875
  var alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=container.hooks.helperMissing, alias3=container.escapeExpression, lookupProperty = container.lookupProperty || function(parent, propertyName) {
@@ -13713,13 +13883,13 @@ this["hbs_Crouton"]["templates"]["mapSearch"] = Handlebars.template({"compiler":
13713
13883
  + alias3((lookupProperty(helpers,"getWord")||(depth0 && lookupProperty(depth0,"getWord"))||alias2).call(alias1,"Open Search Dialog",{"name":"getWord","hash":{},"data":data,"loc":{"start":{"line":1,"column":58},"end":{"line":1,"column":90}}}))
13714
13884
  + "\">\n <span class=\"menu-button-label\" style=\"position:initial;\">"
13715
13885
  + alias3((lookupProperty(helpers,"getWord")||(depth0 && lookupProperty(depth0,"getWord"))||alias2).call(alias1,"Search for meetings",{"name":"getWord","hash":{},"data":data,"loc":{"start":{"line":2,"column":62},"end":{"line":2,"column":95}}}))
13716
- + "</span>\n</button>\n<div id=\"bmltsearch_modal\" class=\"modal\" style=\"display: none;\">\n <div id=\"bmltsearch_content\" class=\"modal-content\">\n <span id=\"close_search\" class=\"modal-close\">x</span>\n <div id=\"modal-search-page\">\n <div class=\"modal-title\">"
13886
+ + "</span>\n</button>\n<div id=\"bmltsearch_modal\" class=\"modal\" style=\"display: none;\">\n <div id=\"bmltsearch_content\" class=\"modal-content\">\n <span id=\"close_search\" class=\"modal-close\">x</span>\n <div class=\"crouton-modal-search-page\">\n <div class=\"modal-title\">"
13717
13887
  + alias3((lookupProperty(helpers,"getWord")||(depth0 && lookupProperty(depth0,"getWord"))||alias2).call(alias1,"Search for meetings",{"name":"getWord","hash":{},"data":data,"loc":{"start":{"line":8,"column":37},"end":{"line":8,"column":70}}}))
13718
13888
  + "</div>\n <div id=\"zoomed-out-message\" class=\"hide\">\n <p>"
13719
13889
  + alias3((lookupProperty(helpers,"getWord")||(depth0 && lookupProperty(depth0,"getWord"))||alias2).call(alias1,"zoomed-out-message",{"name":"getWord","hash":{},"data":data,"loc":{"start":{"line":10,"column":19},"end":{"line":10,"column":51}}}))
13720
13890
  + "</p>\n </div>\n <div id=\"modal-search-box\" class=\"modal-search\">\n <input id=\"bmltsearch-goto-text\" type=\"text\"\n placeholder=\""
13721
13891
  + alias3((lookupProperty(helpers,"getWord")||(depth0 && lookupProperty(depth0,"getWord"))||alias2).call(alias1,"Enter a city or zip code",{"name":"getWord","hash":{},"data":data,"loc":{"start":{"line":14,"column":33},"end":{"line":14,"column":71}}}))
13722
- + "\" style=\"margin-bottom:5px;\">\n <button id=\"bmltsearch-text-button\" class=\"filter-button\">"
13892
+ + "\" class=\"crouton-goto-text\">\n <button id=\"bmltsearch-text-button\" class=\"filter-button\">"
13723
13893
  + alias3((lookupProperty(helpers,"getWord")||(depth0 && lookupProperty(depth0,"getWord"))||alias2).call(alias1,"text_search",{"name":"getWord","hash":{},"data":data,"loc":{"start":{"line":15,"column":74},"end":{"line":15,"column":99}}}))
13724
13894
  + "</button>\n </div>\n <div class=\"modal-search\">\n <button id=\"bmltsearch-nearbyMeetings\" class=\"filter-button\">"
13725
13895
  + alias3((lookupProperty(helpers,"getWord")||(depth0 && lookupProperty(depth0,"getWord"))||alias2).call(alias1,"near_me",{"name":"getWord","hash":{},"data":data,"loc":{"start":{"line":18,"column":77},"end":{"line":18,"column":98}}}))
@@ -13727,14 +13897,14 @@ this["hbs_Crouton"]["templates"]["mapSearch"] = Handlebars.template({"compiler":
13727
13897
  + alias3((lookupProperty(helpers,"getWord")||(depth0 && lookupProperty(depth0,"getWord"))||alias2).call(alias1,"click_search",{"name":"getWord","hash":{},"data":data,"loc":{"start":{"line":21,"column":74},"end":{"line":21,"column":100}}}))
13728
13898
  + "</button>\n </div>\n <button id=\"show-search-parameters\" class=\"advanced-search-button\">"
13729
13899
  + alias3((lookupProperty(helpers,"getWord")||(depth0 && lookupProperty(depth0,"getWord"))||alias2).call(alias1,"Adjust Search",{"name":"getWord","hash":{},"data":data,"loc":{"start":{"line":23,"column":79},"end":{"line":23,"column":106}}}))
13730
- + "</button>\n </div>\n <div id=\"modal-seach-parameters\">\n <div class=\"modal-title\">"
13731
- + alias3((lookupProperty(helpers,"getWord")||(depth0 && lookupProperty(depth0,"getWord"))||alias2).call(alias1,"Adjust Search",{"name":"getWord","hash":{},"data":data,"loc":{"start":{"line":26,"column":37},"end":{"line":26,"column":64}}}))
13900
+ + "</button>\n </div>\n <div class=\"crouton-modal-search-choice\"></div>\n <div id=\"modal-seach-parameters\">\n <div class=\"modal-title\">"
13901
+ + alias3((lookupProperty(helpers,"getWord")||(depth0 && lookupProperty(depth0,"getWord"))||alias2).call(alias1,"Adjust Search",{"name":"getWord","hash":{},"data":data,"loc":{"start":{"line":27,"column":37},"end":{"line":27,"column":64}}}))
13732
13902
  + "</div>\n <input type=\"number\" id=\"search_parameter\" name=\"search_parameter\" maxlength=\"3\" size=\"3\">\n <p>"
13733
- + alias3((lookupProperty(helpers,"getWord")||(depth0 && lookupProperty(depth0,"getWord"))||alias2).call(alias1,"This number represents",{"name":"getWord","hash":{},"data":data,"loc":{"start":{"line":28,"column":15},"end":{"line":28,"column":51}}}))
13903
+ + alias3((lookupProperty(helpers,"getWord")||(depth0 && lookupProperty(depth0,"getWord"))||alias2).call(alias1,"This number represents",{"name":"getWord","hash":{},"data":data,"loc":{"start":{"line":29,"column":15},"end":{"line":29,"column":51}}}))
13734
13904
  + ":</p>\n <input type=\"radio\" id=\"search_radius\" name=\"map_search_type\" value=\"radius\">\n <label id=\"search_radius_label\" for=\"search_radius\"></label><br>\n <input type=\"radio\" id=\"search_count\" name=\"map_search_type\" value=\"count\">\n <label for=\"search_count\">"
13735
- + alias3((lookupProperty(helpers,"getWord")||(depth0 && lookupProperty(depth0,"getWord"))||alias2).call(alias1,"Approx. number of meetings in result",{"name":"getWord","hash":{},"data":data,"loc":{"start":{"line":32,"column":38},"end":{"line":32,"column":88}}}))
13905
+ + alias3((lookupProperty(helpers,"getWord")||(depth0 && lookupProperty(depth0,"getWord"))||alias2).call(alias1,"Approx. number of meetings in result",{"name":"getWord","hash":{},"data":data,"loc":{"start":{"line":33,"column":38},"end":{"line":33,"column":88}}}))
13736
13906
  + "</label><br>\n <button id=\"show-search-page\" class=\"advanced-search-button\">"
13737
- + alias3((lookupProperty(helpers,"getWord")||(depth0 && lookupProperty(depth0,"getWord"))||alias2).call(alias1,"Back",{"name":"getWord","hash":{},"data":data,"loc":{"start":{"line":33,"column":73},"end":{"line":33,"column":91}}}))
13907
+ + alias3((lookupProperty(helpers,"getWord")||(depth0 && lookupProperty(depth0,"getWord"))||alias2).call(alias1,"Back",{"name":"getWord","hash":{},"data":data,"loc":{"start":{"line":34,"column":73},"end":{"line":34,"column":91}}}))
13738
13908
  + "</button>\n </div>\n </div>\n</div>";
13739
13909
  },"useData":true});
13740
13910
  this["hbs_Crouton"]["templates"]["meeting3ColumnRow"] = Handlebars.template({"0":function(container,depth0,helpers,partials,data) {
@@ -14041,7 +14211,9 @@ this["hbs_Crouton"]["templates"]["tableRows"] = Handlebars.template({"0":functio
14041
14211
  + alias3((lookupProperty(helpers,"formatDataFromArray")||(depth0 && lookupProperty(depth0,"formatDataFromArray"))||alias2).call(alias1,(depth0 != null ? lookupProperty(depth0,"venue_type_name") : depth0),{"name":"formatDataFromArray","hash":{},"data":data,"loc":{"start":{"line":15,"column":15},"end":{"line":15,"column":59}}}))
14042
14212
  + "\"\n data-weekdays=\""
14043
14213
  + alias3((lookupProperty(helpers,"formatDataPointer")||(depth0 && lookupProperty(depth0,"formatDataPointer"))||alias2).call(alias1,(depth0 != null ? lookupProperty(depth0,"formatted_day") : depth0),{"name":"formatDataPointer","hash":{},"data":data,"loc":{"start":{"line":16,"column":17},"end":{"line":16,"column":57}}}))
14044
- + "\">\n"
14214
+ + "\"\n data-favorite="
14215
+ + alias3((lookupProperty(helpers,"isFavorite")||(depth0 && lookupProperty(depth0,"isFavorite"))||alias2).call(alias1,depth0,{"name":"isFavorite","hash":{},"data":data,"loc":{"start":{"line":17,"column":16},"end":{"line":17,"column":35}}}))
14216
+ + ">\n"
14045
14217
  + ((stack1 = container.invokePartial(lookupProperty(partials,"tableDataRow"),depth0,{"name":"tableDataRow","data":data,"indent":"\t\t","helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : "")
14046
14218
  + " </tr>\n";
14047
14219
  },"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
@@ -14052,7 +14224,7 @@ this["hbs_Crouton"]["templates"]["tableRows"] = Handlebars.template({"0":functio
14052
14224
  return undefined
14053
14225
  };
14054
14226
 
14055
- return ((stack1 = lookupProperty(helpers,"each").call(depth0 != null ? depth0 : (container.nullContext || {}),depth0,{"name":"each","hash":{},"fn":container.program(0, data, 0),"inverse":container.noop,"data":data,"loc":{"start":{"line":1,"column":0},"end":{"line":19,"column":9}}})) != null ? stack1 : "");
14227
+ return ((stack1 = lookupProperty(helpers,"each").call(depth0 != null ? depth0 : (container.nullContext || {}),depth0,{"name":"each","hash":{},"fn":container.program(0, data, 0),"inverse":container.noop,"data":data,"loc":{"start":{"line":1,"column":0},"end":{"line":20,"column":9}}})) != null ? stack1 : "");
14056
14228
  },"usePartial":true,"useData":true});
14057
14229
  this["hbs_Crouton"]["templates"]["weekdays"] = Handlebars.template({"0":function(container,depth0,helpers,partials,data) {
14058
14230
  var stack1, helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=container.hooks.helperMissing, alias3=container.escapeExpression, lookupProperty = container.lookupProperty || function(parent, propertyName) {
@@ -14178,13 +14350,16 @@ var croutonDefaultTemplates = {
14178
14350
  </div>
14179
14351
  `,
14180
14352
  meeting_times_template: `
14181
- <div class="bmlt-day">{{this.formatted_day}}</div>
14353
+ <div class="bmlt-meeting-day-favorite">
14354
+ <div class="bmlt-day">{{this.formatted_day}}</div>
14355
+ <div class="favorite-icon">{{favoriteIcon this}}</div>
14356
+ </div>
14182
14357
  {{#ifEquals this.duration_time "24:00:00"}}
14183
14358
  <div class="bmlt-time-2">{{this.start_time_formatted}}</div>
14184
14359
  {{else}}
14185
14360
  <div class="bmlt-time-2">{{this.start_time_formatted}} - {{this.end_time_formatted}}</div>
14186
14361
  {{/ifEquals}}
14187
- {{> formatKeys }}
14362
+ {{> (format_list_style) }}
14188
14363
  <div class="bmlt-comments">{{formatLink this.formatted_comments}}</div>
14189
14364
  <div class="bmlt-observer">{{> (selectObserver) }}</div>
14190
14365
  `,
@@ -15280,16 +15455,20 @@ crouton_Handlebars.registerHelper("log", (x) => console.log(x));
15280
15455
  crouton_Handlebars.registerHelper('selectFormatPopup', () => "formatPopup");
15281
15456
  crouton_Handlebars.registerHelper('selectObserver', () => "observerTemplate");
15282
15457
 
15283
-
15458
+ /**
15459
+ * There must be exactly one crouton instance per page.
15460
+ * @param {*} config
15461
+ */
15284
15462
  function Crouton(config) {
15285
- var self = this;
15286
- self.mutex = false;
15463
+ const self = this;
15464
+ self.ready = false;
15287
15465
  self.filtering = false;
15288
15466
  self.masterFormatCodes = [];
15289
15467
  self.currentView = "weekday";
15290
15468
  self.distanceTabAllowed = false;
15469
+ self.favoritesOn = false;
15291
15470
  self.config = {
15292
- version: '4.1.12', // CroutonJS version for debugging
15471
+ version: '4.3.2', // CroutonJS version for debugging
15293
15472
  on_complete: null, // Javascript function to callback when data querying is completed.
15294
15473
  root_server: null, // The root server to use.
15295
15474
  placeholder_id: "bmlt-tabs", // The DOM id that will be used for rendering
@@ -15324,6 +15503,8 @@ function Crouton(config) {
15324
15503
  has_languages: false, // Shows the language dropdown
15325
15504
  has_common_needs: false, // Shows the Common Needs dropdown
15326
15505
  has_venues: true, // Shows the venue types dropdown
15506
+ has_favorites: false, // Shows the favorites button
15507
+ has_geolocation: false, // Shows the geolocation button in the table header
15327
15508
  has_meeting_count: false, // Shows the meeting count
15328
15509
  recurse_service_bodies: false,// Recurses service bodies when making service bodies request
15329
15510
  service_body: [], // Array of service bodies to return data for.
@@ -15366,7 +15547,6 @@ function Crouton(config) {
15366
15547
  tileUrl: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
15367
15548
  tileOptions: {
15368
15549
  attribution: 'Map data &copy; <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors, <a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>',
15369
- maxZoom: 18
15370
15550
  },
15371
15551
  minZoom: 6,
15372
15552
  maxZoom: 17,
@@ -15376,9 +15556,41 @@ function Crouton(config) {
15376
15556
  caption: false,
15377
15557
  groups: false,
15378
15558
  details_table: false,
15559
+ focusedMeetingPopupZoomLevel: 14,
15379
15560
  };
15380
15561
 
15381
15562
  self.setConfig(config);
15563
+ const body = jQuery("body");
15564
+ if (self.config['theme'] !== '') {
15565
+ body.append("<div id='custom-css'><link rel='stylesheet' type='text/css' href='" + self.config['template_path'] + '/themes/' + self.config['theme'] + ".css'>");
15566
+ }
15567
+ if (self.config['custom_css'] !== '') {
15568
+ body.append("<div id='custom-css'><style type='text/css'>" + self.config['custom_css'] + "</style></div>");
15569
+ }
15570
+ self.isIOSDevice = function() {
15571
+ if (navigator.userAgentData && navigator.userAgentData.platform) {
15572
+ if (navigator.userAgentData.platform === 'iOS') {
15573
+ return true;
15574
+ }
15575
+ }
15576
+ const userAgent = navigator.userAgent || navigator.vendor || window.opera;
15577
+ const isIOS = /iPad|iPhone|iPod/.test(userAgent) && !window.MSStream;
15578
+ const isIPadOS = navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1 && !window.MSStream;
15579
+ return isIOS || isIPadOS;
15580
+ }
15581
+
15582
+ self.isAndroidDevice = function() {
15583
+ return /Android/i.test(navigator.userAgent);
15584
+ }
15585
+ self.deviceType = self.isIOSDevice() ? 'ios' : (self.isAndroidDevice() ? 'android' : 'desktop');
15586
+
15587
+ /**
15588
+ * Do the lookups for the [crouton_map] shortcode
15589
+ * @param float latitude
15590
+ * @param float longitude
15591
+ * @param float width search width that will be sent to the server
15592
+ * @param boolean fitBounds
15593
+ */
15382
15594
  Crouton.prototype.searchByCoordinates = function(latitude, longitude, width, fitBounds=true) {
15383
15595
  const original_query = self.config['custom_query'];
15384
15596
  self.config['custom_query'] = (self.config['custom_query'] !== null ? self.config['custom_query'] : "")
@@ -15387,20 +15599,23 @@ function Crouton(config) {
15387
15599
  self.meetingSearch(function() {
15388
15600
  self.config.refresh_map=1;
15389
15601
  self.config.show_map = 1;
15390
- self.reset();
15602
+ jQuery("#" + self.config["placeholder_id"]).html("");
15391
15603
  self.render(false, fitBounds);
15392
15604
  });
15393
15605
  self.config['custom_query'] = original_query;
15394
15606
  };
15395
15607
 
15608
+ /**
15609
+ * Get the meeting list for the [bmlt_tab] shortcode.
15610
+ * @param string url
15611
+ * @param function cb
15612
+ * @returns Promise that returns when the server query finishes.
15613
+ */
15396
15614
  self.getMeetings = function(url,cb=null) {
15397
- var promises = [fetchJsonp(this.config['root_server'] + url).then(function(response) { return response.json(); })];
15615
+ const promises = [fetchJsonp(this.config['root_server'] + url).then(function(response) { return response.json(); })];
15398
15616
 
15399
15617
  if (self.config['extra_meetings'].length > 0) {
15400
- var extra_meetings_query = "";
15401
- for (var i = 0; i < self.config['extra_meetings'].length; i++) {
15402
- extra_meetings_query += "&meeting_ids[]=" + self.config["extra_meetings"][i];
15403
- }
15618
+ const extra_meetings_query = self.config['extra_meetings'].reduce((query,item) => query + "&meeting_ids[]=" + item, '');
15404
15619
  const regex = /&services\[\]=\d+/;
15405
15620
  url = url.replace(regex, '');
15406
15621
  promises.push(fetchJsonp(self.config['root_server'] + url + extra_meetings_query).then(function (response) { return response.json(); }));
@@ -15408,38 +15623,34 @@ function Crouton(config) {
15408
15623
 
15409
15624
  return Promise.all(promises)
15410
15625
  .then(function(data) {
15411
- var mainMeetings = data[0];
15412
- var extraMeetings;
15413
- var jsonMeetings = JSON.stringify(mainMeetings['meetings']);
15414
- if (data.length === 2) {
15415
- extraMeetings = data[1];
15416
- }
15417
- if (jsonMeetings === "{}" || jsonMeetings === "[]") {
15418
- var fullUrl = self.config['root_server'] + url
15626
+ const mainMeetings = data[0];
15627
+ if (mainMeetings['meetings'].length === 0) {
15628
+ const fullUrl = self.config['root_server'] + url
15419
15629
  console.log("Could not find any meetings for the criteria specified with the query <a href=\"" + fullUrl + "\" target=_blank>" + fullUrl + "</a>");
15420
15630
  jQuery('#' + self.config['placeholder_id']).html("No meetings found.");
15421
15631
  self.meetingData = [];
15422
15632
  self.formatsData = [];
15423
- self.mutex = false;
15424
15633
  cb && cb();
15634
+ self.fireReady();
15425
15635
  return;
15426
15636
  }
15637
+ // TODO: is this necessary? why not just add this to the query?
15427
15638
  if (self.config['exclude_zip_codes'].length > 0) {
15428
- mainMeetings['meetings'] = mainMeetings['meetings'].filter(function(m) { return !inArray(m['location_postal_code_1'], self.config['exclude_zip_codes']); } );
15639
+ mainMeetings['meetings'] = mainMeetings['meetings'].filter(function(m) { return !self.config['exclude_zip_codes'].includes(m['location_postal_code_1']); } );
15429
15640
  }
15430
15641
  self.meetingData = mainMeetings['meetings'];
15431
15642
  self.formatsData = mainMeetings['formats'];
15432
- if (extraMeetings) {
15433
- self.meetingData = self.meetingData.concat(extraMeetings['meetings']);
15643
+ // extra meetings
15644
+ if (data.length === 2) {
15645
+ self.meetingData = self.meetingData.concat(data[1]['meetings']);
15434
15646
  }
15435
15647
  cb && cb();
15436
- self.mutex = false;
15648
+ self.fireReady();
15437
15649
  });
15438
15650
  };
15439
- self.mutex = true;
15440
15651
 
15441
15652
  self.meetingSearch = function(cb=null) {
15442
- var url = '/client_interface/jsonp/?switcher=GetSearchResults&get_used_formats&lang_enum=' + self.config['short_language'];
15653
+ let url = '/client_interface/jsonp/?switcher=GetSearchResults&get_used_formats&lang_enum=' + self.config['short_language'];
15443
15654
 
15444
15655
  if (self.config['formats']) {
15445
15656
  url += self.config['formats'].reduce(function(prev,id) {
@@ -15468,9 +15679,7 @@ function Crouton(config) {
15468
15679
  url += self.config['custom_query'] + '&sort_keys=' + self.config['sort_keys'];
15469
15680
  return self.getMeetings(url,cb);
15470
15681
  } else if (self.config['service_body'].length > 0) {
15471
- for (var i = 0; i < self.config['service_body'].length; i++) {
15472
- url += '&services[]=' + self.config['service_body'][i];
15473
- }
15682
+ url = self.config['service_body'].reduce((curr,item)=>curr+'&services[]='+item, url);
15474
15683
 
15475
15684
  if (self.config['recurse_service_bodies']) {
15476
15685
  url += '&recursive=1';
@@ -15483,31 +15692,47 @@ function Crouton(config) {
15483
15692
  return new Promise(function(resolve, reject) {
15484
15693
  self.meetingData = [];
15485
15694
  self.formatsData = [];
15486
- self.mutex = false;
15487
15695
  resolve([]);
15696
+ self.fireReady();
15488
15697
  });
15489
15698
  }
15699
+ }
15700
+ self.readyListener = [];
15701
+ /**
15702
+ * Add a listener for the event that the crouton instance has loaded its data and we can now go ahead and execute the shortcodes.
15703
+ * Generally, each listener corresponds to different shortcode. The main ones go over the render method, but there are also listeners
15704
+ * for [meeting_count] etc.
15705
+ * @param function callback
15706
+ */
15707
+ self.listenForReady = function(callback) {
15708
+ if (self.ready) callback();
15709
+ else self.readyListener.push(callback);
15490
15710
  };
15491
-
15492
- self.lock = function(callback) {
15493
- var self = this;
15494
- var lock_id = setInterval(function() {
15495
- if (!self.mutex) {
15496
- clearInterval(lock_id);
15497
- callback();
15498
- }
15499
- }, 100);
15711
+ /**
15712
+ * FIres the event that crouton is loaded and ready.
15713
+ */
15714
+ self.fireReady = function() {
15715
+ self.ready = true;
15716
+ self.readyListener.forEach(function(callback) {
15717
+ callback();
15718
+ });
15719
+ self.readyListener = [];
15500
15720
  };
15501
-
15502
- self.dayTab = function(day_id) {
15721
+ /**
15722
+ * Bring up the appropriate tab based on the meeting id.
15723
+ * This is only used when a meeting is selected in the msp. I consider this bloat, it is superceded by the meeting details popup available in the marker text.
15724
+ * @param string id meeting id.
15725
+ */
15726
+ self.dayTabFromId = function(id) {
15727
+ const day_id = self.meetingData.find((m)=>m.id_bigint == id).weekday_tinyint;
15503
15728
  self.hideAllPages();
15504
15729
  jQuery('.nav-tabs a[href="#tab' + day_id + '"]').tab('show');
15505
15730
  jQuery("#" + day_id).removeClass("hide").addClass("show");
15506
15731
  };
15507
- self.dayTabFromId = function(id) {
15508
- day_id = self.meetingData.find((m)=>m.id_bigint == id).weekday_tinyint;
15509
- self.dayTab(day_id);
15510
- };
15732
+ /**
15733
+ * Show a page from the table (weekday, distance, city...)
15734
+ * @param string id selector, id attribute of the HtML
15735
+ */
15511
15736
  self.showPage = function (id) {
15512
15737
  jQuery(id+" .bmlt-data-row-placeholder").each(function() {
15513
15738
  const meetingId = this.dataset.meetingid;
@@ -15520,7 +15745,11 @@ function Crouton(config) {
15520
15745
  self.addStripes();
15521
15746
  jQuery(id).removeClass("hide").addClass("show");
15522
15747
  };
15523
-
15748
+ /**
15749
+ * Bring up a view.
15750
+ * @param string viewName abstract viewname (weekday, distance, city...)
15751
+ * @param int showingNow number of items currently being displayed on the page
15752
+ */
15524
15753
  self.showView = function (viewName, showingNow=0) {
15525
15754
  if (viewName === 'distance' && !self.distanceTabAllowed) {
15526
15755
  viewName = self.config.groups ? 'city' : 'day';
@@ -15581,6 +15810,7 @@ function Crouton(config) {
15581
15810
  self.dayView();
15582
15811
  return;
15583
15812
  }
15813
+ self.currentView = field;
15584
15814
  self.lowlightButton("#day");
15585
15815
  self.lowlightButton(".groupingButton");
15586
15816
  self.highlightButton("#groupingButton_" + field);
@@ -15623,8 +15853,12 @@ function Crouton(config) {
15623
15853
  jQuery(value).addClass((index % 2) ? 'oddRow' : 'evenRow');
15624
15854
  });
15625
15855
  }
15856
+ /**
15857
+ *
15858
+ * @returns a list of the IDs of meetings matching the current filters.
15859
+ */
15626
15860
  self.calcShowingNow = function() {
15627
- var showingNow = [];
15861
+ let showingNow = [];
15628
15862
  jQuery(".bmlt-data-row:not(.hide)").each(function (index, value) {
15629
15863
  const rowId = value.id.split("-");
15630
15864
  showingNow.push(rowId[rowId.length-1]);
@@ -15647,26 +15881,13 @@ function Crouton(config) {
15647
15881
  }
15648
15882
  });
15649
15883
  this.addStripes();
15650
- var showingNow = this.calcShowingNow();
15884
+ const showingNow = this.calcShowingNow();
15651
15885
  self.updateMeetingCount(showingNow);
15652
- self.updateFilters();
15653
- if (croutonMap) croutonMap.fillMap(showingNow);
15654
- self.showView(self.currentView, showingNow.length);
15655
- };
15656
- self.resetFilter = function () {
15657
- croutonMap.filterVisible(false);
15658
- if ((self.config.map_page && self.filtering) || self.config.show_map) croutonMap.fillMap();
15659
- self.filtering = false;
15660
- self.updateFilters();
15661
- self.updateMeetingCount();
15662
- jQuery(".filter-dropdown").val(null).trigger("change");
15663
- jQuery(".group-header").removeClass("hide");
15664
- jQuery(".meeting-group").removeClass("hide");
15665
- jQuery(".bmlt-data-row").removeClass("hide");
15666
- jQuery(".evenRow").removeClass("evenRow");
15667
- jQuery(".oddRow").removeClass("oddRow");
15886
+ self.updateFilters(showingNow);
15887
+ if (croutonMap) croutonMap.setFilteredIds(showingNow);
15888
+ if (self.currentView !== 'map') self.showView(self.currentView, showingNow.length);
15668
15889
  };
15669
- self.updateFilters = function() {
15890
+ self.updateFilters = function(showingNow) {
15670
15891
  if (!self.dropdownData) return;
15671
15892
  const getId = function (row) {return row.id.replace("meeting-data-row-", "")};
15672
15893
  // The options available for this filter have to take into account all other filters, but ignore the
@@ -15689,6 +15910,12 @@ function Crouton(config) {
15689
15910
  let showing = self.meetingData.filter((m) => !(hidden.includes(m.id_bigint)));
15690
15911
  dropdown.optionsShowing = dropdown.uniqueData(showing).map((o) => dropdown.optionName(o));
15691
15912
  });
15913
+ const favoritesDropdown = self.dropdownData.find((d) => d.elementId === "filter-dropdown-favorites");
15914
+ if (!favoritesDropdown || !self.shouldOfferFavorites(showingNow)) {
15915
+ jQuery("#crouton_favorites_button").addClass("hide");
15916
+ } else {
15917
+ jQuery("#crouton_favorites_button").removeClass("hide");
15918
+ }
15692
15919
  }
15693
15920
  self.renderStandaloneMap = function (selector, context, callback=null, fitBounds=true) {
15694
15921
  hbs_Crouton['localization'] = self.localization;
@@ -15696,40 +15923,14 @@ function Crouton(config) {
15696
15923
  self.setUpPartials();
15697
15924
  croutonMap.initialize(self.createBmltMapElement(),self.meetingData,context,null,fitBounds,callback,self.config['noMap']);
15698
15925
  }
15699
- self.retrieveGeolocation = function() {
15700
- return new Promise((resolve, reject) => {
15701
- if (window.storedGeolocation) {
15702
- resolve(window.storedGeolocation);
15703
- } else if (navigator.geolocation) {
15704
- navigator.geolocation.getCurrentPosition((position) => {
15705
- window.storedGeolocation = {
15706
- latitude: position.coords.latitude,
15707
- longitude: position.coords.longitude
15708
- };
15709
- resolve(window.storedGeolocation);
15710
- }, (error) => {
15711
- reject(new Error('Error getting geolocation: ' + error.message));
15712
- });
15713
- } else {
15714
- reject(new Error('Geolocation is not supported by this browser.'));
15715
- }
15716
- });
15717
- };
15718
- self.getCurrentLocation = function(callback) {
15719
- self.gretrieveGeolocation().then(position => {
15720
- callback(position);
15721
- }).catch(error => {
15722
- jQuery('.geo').removeClass("hide").addClass("show").html(`<p>${error.message}</p>`);
15723
- });
15724
- };
15725
15926
  self.renderView = function (selector, context, callback, fitBounds) {
15726
15927
  hbs_Crouton['localization'] = self.localization;
15727
- var template = hbs_Crouton.templates['main'];
15928
+ const template = hbs_Crouton.templates['main'];
15728
15929
  jQuery(selector).html(template(context));
15729
15930
  callback();
15730
15931
  };
15731
15932
  self.updateMeetingCount = function(showingNow=null) {
15732
- var self = this;
15933
+ const self = this;
15733
15934
  let meetingCount = self.meetingData.length;
15734
15935
  if (self.meetingCountCallback) self.meetingCountCallback(meetingCount);
15735
15936
  if (self.groupCountCallback) self.groupCountCallback(
@@ -15742,8 +15943,8 @@ function Crouton(config) {
15742
15943
  }
15743
15944
  self.showingNowCount = meetingCount;
15744
15945
  jQuery(".crouton_root_service_body").each(function() {
15745
- var text = "";
15746
- var field = 'name';
15946
+ let text = "";
15947
+ let field = 'name';
15747
15948
  if (this.dataset.field) field = this.dataset.field;
15748
15949
  if (self.config['service_body'].length > 0) {
15749
15950
  const sb = self.getServiceBodyDetails(self.config['service_body'][0]);
@@ -15753,19 +15954,17 @@ function Crouton(config) {
15753
15954
  });
15754
15955
  jQuery(addLive('#bmlt_tabs_meeting_count')).text(meetingCount);
15755
15956
  jQuery(addLive('#bmlt_tabs_group_count')).each(function(){
15756
- var filteredMeetings = self.meetingData;
15757
- if (showingNow!==null) filteredMeetings = self.meetingData.filter((m) => showingNow.includes(m.id_bigint));
15957
+ const filteredMeetings = (showingNow!==null) ? self.meetingData.filter((m) => showingNow.includes(m.id_bigint)) : self.meetingData;
15758
15958
  const groups = self.config.groups ? filteredMeetings : self.convertToGroups([...filteredMeetings]);
15759
- jQuery(this).text(arrayUnique(groups).length);
15959
+ jQuery(this).text([...new Set(groups)].length);
15760
15960
  });
15761
15961
  jQuery(addLive('#bmlt_tabs_service_body_names')).each(function() {
15762
- var filteredMeetings = self.meetingData;
15763
- if (showingNow!==null) filteredMeetings = self.meetingData.filter((m) => showingNow.includes(m.id_bigint));
15764
- var ids = getUniqueValuesOfKey(filteredMeetings, 'service_body_bigint');
15765
- var me = this;
15962
+ const filteredMeetings = (showingNow!==null) ? self.meetingData.filter((m) => showingNow.includes(m.id_bigint)) : self.meetingData;
15963
+ const ids = getUniqueValuesOfKey(filteredMeetings, 'service_body_bigint');
15964
+ const me = this;
15766
15965
  self.getServiceBodies(ids, false).then(function (service_bodies) {
15767
- var n = service_bodies.length;
15768
- var names = service_bodies.map((m)=>m['name']);
15966
+ const n = service_bodies.length;
15967
+ const names = service_bodies.map((m)=>m['name']);
15769
15968
  names.sort();
15770
15969
  var ret = "";
15771
15970
  if (n===1) {
@@ -15784,16 +15983,23 @@ function Crouton(config) {
15784
15983
  });
15785
15984
  }
15786
15985
  self.getServiceBodies = function(service_bodies_id, requires_parents=true) {
15787
- var url = this.config['root_server'] + '/client_interface/jsonp/?switcher=GetServiceBodies'
15788
- + (requires_parents ? '&parents=1' : '') + getServiceBodiesQueryString(service_bodies_id);
15986
+ const url = this.config['root_server'] + '/client_interface/jsonp/?switcher=GetServiceBodies'
15987
+ + (requires_parents ? '&parents=1' : '') + self.getServiceBodiesQueryString(service_bodies_id);
15789
15988
  return fetchJsonp(url)
15790
15989
  .then(function(response) {
15791
15990
  return response.json();
15792
15991
  });
15793
15992
  };
15794
15993
 
15994
+ self.getServiceBodiesQueryString = function(service_bodies_id) {
15995
+ let service_bodies_query = "";
15996
+ for (var x = 0; x < service_bodies_id.length; x++) {
15997
+ service_bodies_query += "&services[]=" + service_bodies_id[x];
15998
+ }
15999
+ return service_bodies_query;
16000
+ }
15795
16001
  self.getMasterFormats = function() {
15796
- var url = this.config['root_server'] + '/client_interface/jsonp/?switcher=GetFormats&lang_enum=en&key_strings[]=TC&key_strings[]=VM&key_strings[]=HY';
16002
+ const url = this.config['root_server'] + '/client_interface/jsonp/?switcher=GetFormats&lang_enum=en&key_strings[]=TC&key_strings[]=VM&key_strings[]=HY';
15797
16003
  return fetchJsonp(url)
15798
16004
  .then(function(response) {
15799
16005
  return response.json();
@@ -15815,16 +16021,16 @@ function Crouton(config) {
15815
16021
  self.handlebarMapOptions.lng = parseFloat(meetingDetailsData.longitude);
15816
16022
  return "<div id='bmlt-handlebars-map' class='bmlt-map'></div>"
15817
16023
  });
15818
- var parser = new DOMParser();
16024
+ const parser = new DOMParser();
15819
16025
 
15820
16026
  while (elements.length > 0) {
15821
- var element = elements.item(0);
16027
+ const element = elements.item(0);
15822
16028
  if (!element.firstChild) {
15823
16029
  console.log('<bmlt-handlebar> tag must have at least one child');
15824
16030
  element.remove();
15825
16031
  continue;
15826
16032
  }
15827
- var templateString = '';
16033
+ let templateString = '';
15828
16034
  if (element.firstChild.nodeType === 1) {
15829
16035
  if (!element.firstChild.firstChild || element.firstChild.firstChild.nodeType !== 3) {
15830
16036
  console.log('<bmlt-handlebar> tag: cannot find textnode');
@@ -15837,13 +16043,13 @@ function Crouton(config) {
15837
16043
  }
15838
16044
  var handlebarResult;
15839
16045
  try {
15840
- var template = crouton_Handlebars.compile(templateString);
16046
+ const template = crouton_Handlebars.compile(templateString);
15841
16047
  handlebarResult = template(meetingDetailsData);
15842
16048
  } catch (e) {
15843
16049
  console.log(e);
15844
16050
  handlebarResult = e.message;
15845
16051
  }
15846
- var htmlDecode = parser.parseFromString('<body>'+handlebarResult+'</body>', "text/html");
16052
+ const htmlDecode = parser.parseFromString('<body>'+handlebarResult+'</body>', "text/html");
15847
16053
  if (!htmlDecode.body || !htmlDecode.body.firstChild) {
15848
16054
  console.log('<bmlt-handlebar> tag: could not parse the Handlebars result');
15849
16055
  element.replaceWith('<bmlt-handlebar> tag: could not parse the Handlebars result');
@@ -15915,7 +16121,12 @@ function Crouton(config) {
15915
16121
  n = n.replace(/PM|pm/g, x => ampm['PM']);
15916
16122
  return n;
15917
16123
  }
15918
- self.enrichMeetings = function (meetingData) {
16124
+ /**
16125
+ * Take raw meeting data as returned from server adding values that should be conveniently displayed.
16126
+ * @param array meetingData
16127
+ * @returns the array with enriched data
16128
+ */
16129
+ self.enrichMeetings = function(meetingData) {
15919
16130
  var meetings = [];
15920
16131
 
15921
16132
  let queryStringChar = '?';
@@ -15946,8 +16157,8 @@ function Crouton(config) {
15946
16157
  meetingData[m]['day_of_the_week'] = meetingData[m]['start_time_raw'].isoWeekday() === 7 ? 1 : meetingData[m]['start_time_raw'].isoWeekday() + 1;
15947
16158
  meetingData[m]['formatted_day'] = self.localization.getDayOfTheWeekWord(meetingData[m]['day_of_the_week']);
15948
16159
 
15949
- var formats = meetingData[m]['formats'].split(",");
15950
- var formats_expanded = [];
16160
+ const formats = meetingData[m]['formats'].split(",");
16161
+ const formats_expanded = [];
15951
16162
  let formatRootServer = self.formatsData.filter((f)=>f['root_server_uri'] == meetingData[m]['root_server_uri']);
15952
16163
  meetingData[m]['wheelchair'] = false;
15953
16164
  for (var f = 0; f < formats.length; f++) {
@@ -15969,14 +16180,13 @@ function Crouton(config) {
15969
16180
 
15970
16181
  meetingData[m]['venue_type'] = parseInt(meetingData[m]['venue_type']);
15971
16182
  meetingData[m]['venue_type_name'] = getVenueTypeName(meetingData[m]);
15972
- meetingData[m]['formats_expanded'] = formats_expanded;
15973
- var addressParts = [
16183
+ meetingData[m]['formats_expanded'] = formats_expanded.sortByKey('name');
16184
+ const addressParts = [
15974
16185
  meetingData[m]['location_street'],
15975
16186
  meetingData[m]['location_municipality'].trim(),
15976
16187
  meetingData[m]['location_province'].trim(),
15977
16188
  meetingData[m]['location_postal_code_1'].trim()
15978
- ];
15979
- addressParts.clean();
16189
+ ].filter((value)=>value!=="");
15980
16190
  meetingData[m]['formatted_address'] = addressParts.join(", ");
15981
16191
  meetingData[m]['formatted_location_info'] =
15982
16192
  meetingData[m]['location_info'] != null
@@ -15995,7 +16205,7 @@ function Crouton(config) {
15995
16205
  }
15996
16206
  }
15997
16207
 
15998
- var serviceBodyInfo = self.getServiceBodyDetails(meetingData[m]['service_body_bigint'])
16208
+ const serviceBodyInfo = self.getServiceBodyDetails(meetingData[m]['service_body_bigint'])
15999
16209
  meetingData[m]['serviceBodyUrl'] = serviceBodyInfo["url"];
16000
16210
  meetingData[m]['serviceBodyPhone'] = serviceBodyInfo["helpline"];
16001
16211
  meetingData[m]['serviceBodyName'] = serviceBodyInfo["name"];
@@ -16003,7 +16213,7 @@ function Crouton(config) {
16003
16213
  meetingData[m]['serviceBodyContactEmail'] = serviceBodyInfo["contact_email"];
16004
16214
  meetingData[m]['serviceBodyType'] = self.localization.getServiceBodyType(serviceBodyInfo["type"]);
16005
16215
 
16006
- var parentBodyInfo = self.getServiceBodyDetails(serviceBodyInfo["parent_id"]);
16216
+ const parentBodyInfo = self.getServiceBodyDetails(serviceBodyInfo["parent_id"]);
16007
16217
  if (parentBodyInfo !== undefined) {
16008
16218
  meetingData[m]['parentServiceBodyId'] = serviceBodyInfo["parent_id"];
16009
16219
  meetingData[m]['parentServiceBodyUrl'] = parentBodyInfo["url"];
@@ -16032,7 +16242,27 @@ function Crouton(config) {
16032
16242
  return self.convertToGroups(meetings);
16033
16243
  }
16034
16244
  return meetings;
16035
- };
16245
+ }
16246
+ // [deprecated] Retire after root server 2.16.4 is rolled out everywhere.
16247
+ const masterFormatVenueType = {
16248
+ IN_PERSON: "IN_PERSON",
16249
+ VIRTUAL: "VIRTUAL",
16250
+ }
16251
+
16252
+ function getVenueTypeName(data) {
16253
+ if (data['venue_type'] === venueType.HYBRID || getFormats(data).includes(getMasterFormatId('HY', data))) {
16254
+ return [crouton.localization.getVenueType(masterFormatVenueType.VIRTUAL), crouton.localization.getVenueType(masterFormatVenueType.IN_PERSON)];
16255
+ } else if (data['venue_type'] === venueType.VIRTUAL || getFormats(data).includes(getMasterFormatId('VM', data))) {
16256
+ return [crouton.localization.getVenueType(masterFormatVenueType.VIRTUAL)];
16257
+ } else {
16258
+ return [crouton.localization.getVenueType(masterFormatVenueType.IN_PERSON)];
16259
+ }
16260
+ }
16261
+ /**
16262
+ * Convert from individual meetings to groups.
16263
+ * @param array meetings
16264
+ * @returns
16265
+ */
16036
16266
  self.convertToGroups = function(meetings) {
16037
16267
  if (!meetings || !meetings.length) return [];
16038
16268
  if (meetings[0].hasOwnProperty('membersOfGroup')) return meetings;
@@ -16079,6 +16309,11 @@ function Crouton(config) {
16079
16309
  });
16080
16310
  return groups;
16081
16311
  }
16312
+ /**
16313
+ * Update the distances displyed after geocoding or geolocation.
16314
+ * @param boolean initial
16315
+ * @returns
16316
+ */
16082
16317
  Crouton.prototype.updateDistances = function(initial=false) {
16083
16318
  const self = this;
16084
16319
  var knt = 0;
@@ -16113,8 +16348,8 @@ function Crouton(config) {
16113
16348
  });
16114
16349
  }
16115
16350
  const sorted = parent.children().sort(function (a, b) {
16116
- const idA =parseFloat( jQuery(a).data('meetingid').replace("meeting-data-row-", ""));
16117
- const idB =parseFloat( jQuery(b).data('meetingid').replace("meeting-data-row-", ""));
16351
+ const idA =parseFloat( (jQuery(a).data('meetingid') ?? jQuery(a).attr('id')).replace("meeting-data-row-", ""));
16352
+ const idB =parseFloat( (jQuery(b).data('meetingid') ?? jQuery(b).attr('id')).replace("meeting-data-row-", ""));
16118
16353
  const dataA = self.meetingData.find((m) => m.id_bigint==idA);
16119
16354
  const dataB = self.meetingData.find((m) => m.id_bigint==idB);
16120
16355
  const distanceA = dataA ? parseFloat(dataA['distance_in_km']) : Number.POSITIVE_INFINITY;
@@ -16146,8 +16381,6 @@ function Crouton(config) {
16146
16381
  ? "<td>{{> meetingStackedRow}}</td>"
16147
16382
  : hbs_Crouton.templates['meeting3ColumnRow'])
16148
16383
  );
16149
-
16150
-
16151
16384
  self.registerPartial("meetingDataTemplate", self.config['meeting_data_template']);
16152
16385
  self.registerPartial("meetingDataTemplate", self.config['meeting_data_template']);
16153
16386
  self.registerPartial("metaDataTemplate", self.config['metadata_template']);
@@ -16165,6 +16398,14 @@ function Crouton(config) {
16165
16398
  handlebarsExtensions.registerHelpers();
16166
16399
  handlebarsExtensions.registerPartials(self.config.groups);
16167
16400
  }
16401
+ self.getServiceBodyDetails = function(serviceBodyId) {
16402
+ return this.all_service_bodies.find((s)=>s['id'] === serviceBodyId)
16403
+ }
16404
+ /**
16405
+ * Calculate the distances sfter geocoding or geolocation, adding them to the meeting data.
16406
+ * @param boolean initial
16407
+ * @returns
16408
+ */
16168
16409
  self.calculateDistance = function(meetingData) {
16169
16410
  meetingData['distance'] = '';
16170
16411
  if (meetingData['venue_type'] != 2) {
@@ -16208,19 +16449,21 @@ function Crouton(config) {
16208
16449
  self.getUsedNext24 = function(meetings) {
16209
16450
  return [{name: 'Next24', value: 1}];
16210
16451
  }
16211
- self.isEmpty = function(obj) {
16212
- for (var key in obj) {
16213
- if(obj.hasOwnProperty(key))
16214
- return false;
16215
- }
16216
- return true;
16217
- };
16452
+ self.getUsedFavorites = function(meetings) {
16453
+ return [{name: 'Favorite', value: 1}]
16454
+ }
16218
16455
  self.createBmltMapElement = function() {
16219
16456
  if (!document.getElementById('bmlt-map')) {
16220
16457
  jQuery("#bmlt-tabs").before("<div id='bmlt-map' class='bootstrap-bmlt bmlt-map "+self.localization.getWord('css-direction')+" bmlt_map_container_div'></div>");
16221
16458
  }
16222
16459
  return 'bmlt-map';
16223
16460
  }
16461
+ self.createLocationSearchElement = function() {
16462
+ if (!document.getElementById('bmlt-location-search')) {
16463
+ jQuery("#bmlt-header").append("<div id='bmlt-location-search' class='bootstrap-bmlt bmlt-location-search "+self.localization.getWord('css-direction')+" bmlt_location_search_container_div'></div>");
16464
+ }
16465
+ return 'bmlt-location-search';
16466
+ }
16224
16467
  if (typeof window.croutonMap === 'undefined') {
16225
16468
  window.croutonMap = new MeetingMap(self.config);
16226
16469
  if (self.config['map_search']) self.searchMap();
@@ -16230,7 +16473,7 @@ function Crouton(config) {
16230
16473
  }
16231
16474
 
16232
16475
  Crouton.prototype.setConfig = function(config) {
16233
- var self = this;
16476
+ const self = this;
16234
16477
  const deprecatedNames = {
16235
16478
  button_filters: 'grouping_buttons',
16236
16479
  button_format_filters: 'formattype_grouping_buttons',
@@ -16291,43 +16534,27 @@ Crouton.prototype.setConfig = function(config) {
16291
16534
  console.log("Error parsing custom translations: " + e.message);
16292
16535
  }
16293
16536
  }
16294
- };
16295
-
16296
- Crouton.prototype.reset = function() {
16297
- var self = this;
16298
- jQuery("#custom-css").remove();
16299
- jQuery("#" + self.config["placeholder_id"]).html("");
16300
- };
16301
-
16302
- Crouton.prototype.doFilters = function() {
16303
- return this.filterMeetingsFromView();
16537
+ if (self.config.format_list_style !== 'formatNames') self.config.format_list_style = 'formatKeys';
16304
16538
  }
16305
- Crouton.prototype.getServiceBodyDetails = function(serviceBodyId) {
16306
- var self = this;
16307
- for (var s = 0; s < self.all_service_bodies.length; s++) {
16308
- var service_body = self.all_service_bodies[s];
16309
- if (service_body['id'] === serviceBodyId) {
16310
- return service_body;
16311
- }
16312
- }
16313
- }
16314
-
16539
+ /**
16540
+ * This supports the [bmlt_handlebars] shortcode, which allows a meeting's details to be inserted into a page.
16541
+ */
16315
16542
  Crouton.prototype.doHandlebars = function() {
16316
16543
  var elements = document.getElementsByTagName('bmlt-handlebar');
16317
16544
  if (elements.length === 0) {
16318
16545
  console.log('No <bmlt-handlebar> tags found');
16319
16546
  return;
16320
16547
  };
16321
- var self = this;
16322
- self.lock(function() {
16323
- if (self.isEmpty(self.meetingData)) {
16548
+ const self = this;
16549
+ self.listenForReady(function() {
16550
+ if (!self.meetingData || self.meetingData.length === 0) {
16324
16551
  for (let i = 0; i < elements.length; i++) {
16325
16552
  var element = elements.item(i);
16326
16553
  element.innerHTML = "Meeting not found!";
16327
16554
  }
16328
16555
  return;
16329
16556
  }
16330
- var promises = [self.getServiceBodies([self.meetingData[0]['service_body_bigint']])];
16557
+ const promises = [self.getServiceBodies([self.meetingData[0]['service_body_bigint']])];
16331
16558
  Promise.all(promises)
16332
16559
  .then(function(data) {
16333
16560
  hbs_Crouton['localization'] = self.localization;
@@ -16348,10 +16575,21 @@ Crouton.prototype.doHandlebars = function() {
16348
16575
  });
16349
16576
  });
16350
16577
  };
16351
-
16578
+ /**
16579
+ * Sometimes the logic in the map has to decide whether to let the map implementation (google or leaflet) do something, or just do the calculation
16580
+ * itself. So we have to ask crouton if the map is showing or not.
16581
+ * @returns boolean
16582
+ */
16583
+ Crouton.prototype.isEmbeddedMapShowing = function() {
16584
+ if (this.config['show_map']) return true;
16585
+ const embeddedMap = document.getElementById("byfield_embeddedMapPage");
16586
+ if (embeddedMap) {
16587
+ return !embeddedMap.classList.contains('hide');
16588
+ }
16589
+ return true;
16590
+ }
16352
16591
  Crouton.prototype.meetingModal = function(meetingId) {
16353
- let self = this;
16354
- this.openMeetingModal(self.meetingData.find((m) => m.id_bigint == meetingId));
16592
+ this.openMeetingModal(this.meetingData.find((m) => m.id_bigint == meetingId));
16355
16593
  return;
16356
16594
  }
16357
16595
  Crouton.prototype.openMeetingModal = function(meeting) {
@@ -16372,7 +16610,7 @@ Crouton.prototype.openMeetingModal = function(meeting) {
16372
16610
  croutonMap.loadPopupMap("bmlt-handlebars-map", meeting, {
16373
16611
  lat: parseFloat(meeting.latitude),
16374
16612
  lng: parseFloat(meeting.longitude),
16375
- zoom: 14
16613
+ zoom: self.config.focusedMeetingPopupZoomLevel ?? 14
16376
16614
  });
16377
16615
  let visibleMeetings = jQuery('.bmlt-data-row:visible');
16378
16616
  let index = -1;
@@ -16413,8 +16651,11 @@ Crouton.prototype.openMeetingModal = function(meeting) {
16413
16651
  mm.getElementsByClassName('modal-right').item(0).addEventListener("click", ev=>doSwipe("left"));
16414
16652
  }
16415
16653
  }
16654
+ /**
16655
+ * Execute the [crouton_map] shortcode.
16656
+ */
16416
16657
  Crouton.prototype.searchMap = function() {
16417
- var self = this;
16658
+ const self = this;
16418
16659
  self.distanceTabAllowed = true;
16419
16660
  if (!self.config.map_search || typeof self.config.map_search !== 'object') {
16420
16661
  self.config.map_search = {
@@ -16432,12 +16673,6 @@ Crouton.prototype.searchMap = function() {
16432
16673
  }
16433
16674
  self.config['map_page'] = false;
16434
16675
  self.config['show_map'] = false;
16435
- var body = jQuery("body");
16436
- if (self.config['theme'] !== '') {
16437
- body.append("<div id='custom-css'><link rel='stylesheet' type='text/css' href='" + self.config['template_path'] + '/themes/' + self.config['theme'] + ".css'>");
16438
- }
16439
-
16440
- body.append("<div id='custom-css'><style type='text/css'>" + self.config['custom_css'] + "</style></div>");
16441
16676
 
16442
16677
  self.meetingData = null;
16443
16678
  self.renderStandaloneMap("#" + self.config['placeholder_id'], {
@@ -16454,28 +16689,26 @@ Crouton.prototype.searchMap = function() {
16454
16689
  "location": {'latitude':0,'longitude':0,'zoom':10} // TODO: Where is this used?
16455
16690
  });
16456
16691
  }
16692
+ /**
16693
+ * This methods does the main work of creating the crouton table.
16694
+ * @param boolean doMeetingMap MeetingMap is the old name for the [bmlt_map] shortcode.
16695
+ * @param boolean fitBounds Expand the scaale and center of the map to contain all meetings.
16696
+ */
16457
16697
  Crouton.prototype.render = function(doMeetingMap = false, fitBounds=true) {
16458
- var self = this;
16459
-
16460
- self.lock(function() {
16461
- var body = jQuery("body");
16462
- if (self.config['theme'] !== '') {
16463
- body.append("<div id='custom-css'><link rel='stylesheet' type='text/css' href='" + self.config['template_path'] + '/themes/' + self.config['theme'] + ".css'>");
16464
- }
16698
+ const self = this;
16465
16699
 
16466
- body.append("<div id='custom-css'><style type='text/css'>" + self.config['custom_css'] + "</style></div>");
16467
-
16468
- if (self.isEmpty(self.meetingData)) {
16700
+ self.listenForReady(function() {
16701
+ if (!self.meetingData || self.meetingData.length === 0) {
16469
16702
  jQuery('#please-wait').remove();
16470
16703
  self.showMessage("No meetings found for parameters specified.");
16471
16704
  if (self.config['refresh_map']) {
16472
- croutonMap.refreshMeetings(self.meetingData, fitBounds, true);
16705
+ croutonMap.refreshMeetings(self.meetingData, fitBounds);
16473
16706
  }
16474
16707
  return;
16475
16708
  }
16476
16709
 
16477
16710
  self.unique_service_bodies_ids = getUniqueValuesOfKey(self.meetingData, 'service_body_bigint').sort();
16478
- var promises = [self.getMasterFormats(), self.getServiceBodies(self.unique_service_bodies_ids)];
16711
+ const promises = [self.getMasterFormats(), self.getServiceBodies(self.unique_service_bodies_ids)];
16479
16712
  Promise.all(promises)
16480
16713
  .then(function(data) {
16481
16714
  self.all_service_bodies = [];
@@ -16512,7 +16745,7 @@ Crouton.prototype.render = function(doMeetingMap = false, fitBounds=true) {
16512
16745
  var weekdaysData = [];
16513
16746
  while (day_counter < 7) {
16514
16747
  var day = self.config.day_sequence[day_counter];
16515
- var daysOfTheWeekMeetings = enrichedMeetingData.filterByObjectKeyValue('day_of_the_week', day);
16748
+ var daysOfTheWeekMeetings = enrichedMeetingData.filter((meeting)=>meeting['day_of_the_week'] == day);
16516
16749
  weekdaysData.push({
16517
16750
  "day": day,
16518
16751
  "meetings": daysOfTheWeekMeetings
@@ -16528,7 +16761,7 @@ Crouton.prototype.render = function(doMeetingMap = false, fitBounds=true) {
16528
16761
  if (groupByName.startsWith('distance')) continue;
16529
16762
  var groupByData = getUniqueValuesOfKey(daysOfTheWeekMeetings, groupByName).sort();
16530
16763
  for (var i = 0; i < groupByData.length; i++) {
16531
- var groupByMeetings = daysOfTheWeekMeetings.filterByObjectKeyValue(groupByName, groupByData[i]);
16764
+ var groupByMeetings = daysOfTheWeekMeetings.filter((meeting)=>meeting[groupByName] == groupByData[i]);
16532
16765
  if (groupingButtonsData.hasOwnProperty(groupByName) && groupingButtonsData[groupByName].hasOwnProperty(groupByData[i])) {
16533
16766
  groupingButtonsData[groupByName][groupByData[i]] = groupingButtonsData[groupByName][groupByData[i]].concat(groupByMeetings);
16534
16767
  } else if (groupingButtonsData.hasOwnProperty(groupByName)) {
@@ -16603,7 +16836,7 @@ Crouton.prototype.render = function(doMeetingMap = false, fitBounds=true) {
16603
16836
  self.dropdownData = [];
16604
16837
  if (self.config.has_days) self.dropdownData.push(
16605
16838
  {placeholder: self.localization.getWord('weekday'), pointer: 'weekdays', elementId: "filter-dropdown-weekdays",
16606
- uniqueData: (meetings) => sortListByList(getUniqueValuesOfKey(meetings, "formatted_day"), self.dayNamesSequenced),
16839
+ uniqueData: (meetings) => self.sortListByList(getUniqueValuesOfKey(meetings, "formatted_day"), self.dayNamesSequenced),
16607
16840
  objectPointer: convertToPunyCode, optionName: (s)=>s});
16608
16841
  if (self.config.has_states) self.dropdownData.push(
16609
16842
  {placeholder: self.localization.getWord('states'), pointer: 'States', elementId: "filter-dropdown-states",
@@ -16665,6 +16898,10 @@ Crouton.prototype.render = function(doMeetingMap = false, fitBounds=true) {
16665
16898
  {placeholder: '', pointer: 'next24', elementId: "filter-dropdown-next24",
16666
16899
  uniqueData: (meetings) => self.getUsedNext24(meetings),
16667
16900
  objectPointer: (s)=>s.value, optionName: (s)=>s.name});
16901
+ if (self.config.has_favorites) self.dropdownData.push(
16902
+ {placeholder: '', pointer: 'favorite', elementId: "filter-dropdown-favorites",
16903
+ uniqueData: (meetings) => self.getUsedFavorites(meetings),
16904
+ objectPointer: (s)=>s.value, optionName: (s)=>s.name});
16668
16905
  let renderer = doMeetingMap ? self.renderStandaloneMap : self.renderView;
16669
16906
  renderer("#" + self.config['placeholder_id'], {
16670
16907
  "config": self.config,
@@ -16683,20 +16920,30 @@ Crouton.prototype.render = function(doMeetingMap = false, fitBounds=true) {
16683
16920
  jQuery('#groupingButton_distance_in_km').addClass('hide');
16684
16921
  }
16685
16922
  self.addStripes();
16686
- self.calcShowingNow()
16923
+ const showingNow = self.calcShowingNow();
16687
16924
  self.updateMeetingCount();
16688
16925
  jQuery('#please-wait').remove();
16689
16926
  if (self.config['map_search'] != null || self.config['show_map']) {
16690
16927
  jQuery(".bmlt-data-row").css({cursor: "pointer"});
16691
16928
  jQuery(".bmlt-data-row").click(function (e) {
16692
- if (e.target.tagName !== 'A')
16929
+ if (e.target.tagName !== 'A' && !e.target.id.startsWith('crouton-favorite-')) {
16693
16930
  croutonMap.rowClick(parseInt(this.id.replace("meeting-data-row-", "")));
16931
+ }
16694
16932
  });
16695
16933
  }
16696
16934
 
16697
16935
  jQuery("#" + self.config['placeholder_id']).addClass("bootstrap-bmlt");
16698
16936
  jQuery("#filter-dropdown-visibile").removeClass("crouton-select").addClass("hide");
16937
+ jQuery("#filter-dropdown-visibile").parent().addClass("hide");
16699
16938
  jQuery("#filter-dropdown-next24").removeClass("crouton-select").addClass("hide");
16939
+ jQuery("#filter-dropdown-next24").parent().addClass("hide");
16940
+ jQuery("#filter-dropdown-favorites").removeClass("crouton-select").addClass("hide");
16941
+ jQuery("#filter-dropdown-favorites").parent().addClass("hide");
16942
+ if (self.shouldOfferFavorites(showingNow)) {
16943
+ jQuery("#crouton_favorites_button").removeClass("hide");
16944
+ } else {
16945
+ jQuery("#crouton_favorites_button").addClass("hide");
16946
+ }
16700
16947
  jQuery(".crouton-select").select2({
16701
16948
  dropdownAutoWidth: true,
16702
16949
  allowClear: false,
@@ -16733,6 +16980,57 @@ Crouton.prototype.render = function(doMeetingMap = false, fitBounds=true) {
16733
16980
  jQuery('.filter-dropdown').on('select2:select', function (e) {
16734
16981
  self.filterMeetingsFromView();
16735
16982
  });
16983
+ jQuery('#crouton_favorites_button').on('click', function (e) {
16984
+ if (self.favoritesOn) {
16985
+ self.favoritesOn = false;
16986
+ jQuery("#filter-dropdown-favorites").val("a-");
16987
+ self.lowlightButton("#crouton_favorites_button");
16988
+ } else {
16989
+ self.favoritesOn = true;
16990
+ jQuery("#filter-dropdown-favorites").val("a-1");
16991
+ self.highlightButton("#crouton_favorites_button");
16992
+ }
16993
+ self.filterMeetingsFromView();
16994
+ return;
16995
+ });
16996
+ if (!self.config['has_favorites'] || !self.config['header']) {
16997
+ jQuery(".favorite-icon").addClass("hide");
16998
+ }
16999
+ jQuery(document).on('click', '.crouton-favorite', function(e) {
17000
+ e.preventDefault();
17001
+ e.stopPropagation();
17002
+ const meetingId = parseInt(this.id.replace("crouton-favorite-", ""));
17003
+ const localStorageKey = "crouton-favorites";
17004
+ const favorites = JSON.parse(localStorage.getItem(localStorageKey) || "[]");
17005
+ const index = favorites.indexOf(self.config.root_server + "_" + meetingId);
17006
+ const row = e.target.closest(".bmlt-data-row");
17007
+ let shouldFilterMeetings = self.favoritesOn;
17008
+ const showingNow = self.calcShowingNow();
17009
+ const favoritesButtonHidden = jQuery("#crouton_favorites_button").hasClass("hide");
17010
+ if (index !== -1) {
17011
+ favorites.splice(index, 1);
17012
+ e.target.classList.remove("glyphicon-heart");
17013
+ e.target.classList.add("glyphicon-heart-empty");
17014
+ row.setAttribute("data-favorite", 0);
17015
+ } else {
17016
+ shouldFilterMeetings = favoritesButtonHidden;
17017
+ favorites.push(self.config.root_server + "_" + meetingId);
17018
+ e.target.classList.remove("glyphicon-heart-empty");
17019
+ e.target.classList.add("glyphicon-heart");
17020
+ row.setAttribute("data-favorite", 1);
17021
+ }
17022
+ localStorage.setItem(localStorageKey, JSON.stringify(favorites));
17023
+ if (!self.shouldOfferFavorites(showingNow)) {
17024
+ shouldFilterMeetings = true;
17025
+ self.favoritesOn = false;
17026
+ jQuery("#filter-dropdown-favorites").val("a-");
17027
+ self.lowlightButton("#crouton_favorites_button");
17028
+ jQuery("#crouton_favorites_button").addClass("hide");
17029
+ }
17030
+ if (shouldFilterMeetings) {
17031
+ self.filterMeetingsFromView();
17032
+ }
17033
+ });
16736
17034
 
16737
17035
  jQuery("#day").on('click', function () {
16738
17036
  self.showView('day');
@@ -16758,8 +17056,8 @@ Crouton.prototype.render = function(doMeetingMap = false, fitBounds=true) {
16758
17056
  jQuery(this).tab('show');
16759
17057
  });
16760
17058
 
16761
- var d = new Date();
16762
- var n = d.getDay();
17059
+ const d = new Date();
17060
+ let n = d.getDay();
16763
17061
  n++;
16764
17062
  jQuery('.nav-tabs a[href="#tab' + n + '"]').tab('show');
16765
17063
  jQuery('#tab' + n).show();
@@ -16781,27 +17079,46 @@ Crouton.prototype.render = function(doMeetingMap = false, fitBounds=true) {
16781
17079
  if (self.meetingData.filter(m => m.venue_type != 2).length==0) {
16782
17080
  jQuery('#groupingButton_embeddedMapPage').addClass('hide');
16783
17081
  }
16784
- else croutonMap.initialize('byfield_embeddedMapPage', self.meetingData, null, null, fitBoundsInitial);
17082
+ else {
17083
+ croutonMap.initialize('byfield_embeddedMapPage', self.meetingData, null, null, fitBoundsInitial);
17084
+ if (self.config['has_geolocation'] && self.config['header']) jQuery('#'+self.createLocationSearchElement()).append(croutonMap.createLocationSearchButton());
17085
+ }
16785
17086
  }
16786
17087
  if (self.config['refresh_map']) {
16787
- croutonMap.refreshMeetings(self.meetingData, fitBounds, true);
17088
+ croutonMap.refreshMeetings(self.meetingData, fitBounds);
16788
17089
  }
16789
17090
  if (self.config['view_by'] == 'map' && !self.config['map_page'])
16790
17091
  self.config['view_by'] = 'day';
16791
17092
  self.showView(self.config['view_by'], self.meetingData.length);
16792
- if (self.config['on_complete'] != null && isFunction(self.config['on_complete'])) {
17093
+ if (self.config['on_complete'] != null && {}.toString.call(self.config['on_complete']) === '[object Function]') {
16793
17094
  self.config['on_complete']();
16794
17095
  }
16795
17096
  }, !doMeetingMap || (!self.config['lat'] && !self.config['lng']));
16796
17097
  });
16797
17098
  });
16798
- Crouton.prototype.forceShowMap = function() {
16799
- if (this.config.map_page && jQuery('#byfield_embeddedMapPage').hasClass('hide')) {
16800
- this.mapView();
17099
+ self.shouldOfferFavorites = function(showingNow) {
17100
+ if (!self.config['has_favorites']) return false;
17101
+ const localStorageKey = "crouton-favorites";
17102
+ const favorites = JSON.parse(localStorage.getItem(localStorageKey) || "[]");
17103
+ for (let i = 0; i < favorites.length; i++) {
17104
+ const split = favorites[i].split("_");
17105
+ const meetingId = split[split.length-1];
17106
+ if (showingNow.includes(meetingId)) return true;
17107
+ }
17108
+ return false;
17109
+ }
17110
+ self.sortListByList = function(source, truth) {
17111
+ var goal = [];
17112
+ for (var x = 0; x < truth.length; x++) {
17113
+ for (var y = 0; y < source.length; y++) {
17114
+ if (truth[x] === source[y]) {
17115
+ goal.push(source[y])
17116
+ }
17117
+ }
16801
17118
  }
17119
+ return goal;
16802
17120
  }
16803
17121
  };
16804
-
16805
17122
  // [deprecated] Retire after root server 2.16.4 is rolled out everywhere.
16806
17123
  function getMasterFormatId(code, data) {
16807
17124
  for (var f = 0; f < crouton.masterFormatCodes.length; f++) {
@@ -16812,28 +17129,6 @@ function getMasterFormatId(code, data) {
16812
17129
  }
16813
17130
  }
16814
17131
 
16815
- // [deprecated] Retire after root server 2.16.4 is rolled out everywhere.
16816
- var masterFormatVenueType = {
16817
- IN_PERSON: "IN_PERSON",
16818
- VIRTUAL: "VIRTUAL",
16819
- }
16820
-
16821
- var venueType = {
16822
- IN_PERSON: 1,
16823
- VIRTUAL: 2,
16824
- HYBRID: 3,
16825
- }
16826
-
16827
- function getVenueTypeName(data) {
16828
- if (data['venue_type'] === venueType.HYBRID || inArray(getMasterFormatId('HY', data), getFormats(data))) {
16829
- return [crouton.localization.getVenueType(masterFormatVenueType.VIRTUAL), crouton.localization.getVenueType(masterFormatVenueType.IN_PERSON)];
16830
- } else if (data['venue_type'] === venueType.VIRTUAL || inArray(getMasterFormatId('VM', data), getFormats(data))) {
16831
- return [crouton.localization.getVenueType(masterFormatVenueType.VIRTUAL)];
16832
- } else {
16833
- return [crouton.localization.getVenueType(masterFormatVenueType.IN_PERSON)];
16834
- }
16835
- }
16836
-
16837
17132
  // TODO: Change this logic when https://github.com/bmlt-enabled/bmlt-root-server/issues/353 is released and rolled out everywhere.
16838
17133
  function getFormats(data) {
16839
17134
  return data['formats'] !== "" ? data['format_shared_id_list'].split(",") : [];
@@ -16849,17 +17144,6 @@ function getUniqueValuesOfKey(array, key){
16849
17144
  return carry;
16850
17145
  }, []);
16851
17146
  }
16852
-
16853
- function getValuesFromObject(o) {
16854
- var arr = [];
16855
- for (key in o) {
16856
- if (o.hasOwnProperty(key)) {
16857
- arr.push(o[key]);
16858
- }
16859
- }
16860
-
16861
- return arr;
16862
- }
16863
17147
  function getUniqueFormats(array){
16864
17148
  return array.reduce(function(carry, val){
16865
17149
  if (!(val.formats_expanded)) return carry;
@@ -16877,13 +17161,13 @@ function getUniqueFormatsOfType(array, type){
16877
17161
  },[]).sortByKey('name');
16878
17162
  }
16879
17163
  Crouton.prototype.renderMeetingCount = function() {
16880
- var self = this;
16881
- self.lock(function() {
17164
+ const self = this;
17165
+ self.listenForReady(function() {
16882
17166
  self.updateMeetingCount()
16883
17167
  });
16884
17168
  }
16885
17169
  Crouton.prototype.simulateFilterDropdown = function() {
16886
- self = this;
17170
+ const self = this;
16887
17171
  jQuery('.bmlt-page:not(#byfield_embeddedMapPage)').each(function () {
16888
17172
  self.hidePage(this);
16889
17173
  });
@@ -16922,76 +17206,12 @@ Crouton.prototype.getAdjustedDateTime = function(meeting_day, meeting_time, meet
16922
17206
 
16923
17207
  return meeting_date_time_obj;
16924
17208
  };
16925
-
16926
- function arrayUnique(a, b, c) {
16927
- b = a.length;
16928
- while (c = --b)
16929
- while (c--) a[b] !== a[c] || a.splice(c, 1);
16930
- return a
16931
- }
16932
-
16933
- function sortListByList(source, truth) {
16934
- var goal = [];
16935
- for (var x = 0; x < truth.length; x++) {
16936
- for (var y = 0; y < source.length; y++) {
16937
- if (truth[x] === source[y]) {
16938
- goal.push(source[y])
16939
- }
16940
- }
16941
- }
16942
-
16943
- return goal;
16944
- }
16945
-
16946
- function inArray(needle, haystack) {
16947
- return haystack.indexOf(needle) !== -1;
16948
- }
16949
-
16950
- function isFunction(functionToCheck) {
16951
- return functionToCheck && {}.toString.call(functionToCheck) === '[object Function]';
16952
- }
16953
-
16954
- function getServiceBodiesQueryString(service_bodies_id) {
16955
- var service_bodies_query = "";
16956
- for (var x = 0; x < service_bodies_id.length; x++) {
16957
- service_bodies_query += "&services[]=" + service_bodies_id[x];
16958
- }
16959
- return service_bodies_query;
16960
- }
16961
-
16962
- Array.prototype.clone = function() {
16963
- return this.slice();
17209
+ // TODO: why can't this be a const? If I set it to const, then I get an error from bootstrap???!!!!
17210
+ var venueType = {
17211
+ IN_PERSON: 1,
17212
+ VIRTUAL: 2,
17213
+ HYBRID: 3,
16964
17214
  }
16965
-
16966
- Array.prototype.filterByObjectKeyValue = function(key, value) {
16967
- var ret = [];
16968
- for (var i = 0; i < this.length; i++) {
16969
- if (this[i][key] === value) {
16970
- ret.push(this[i])
16971
- }
16972
- }
16973
-
16974
- return ret;
16975
- };
16976
-
16977
- Array.prototype.getArrayItemByObjectKeyValue = function(key, value) {
16978
- for (var i = 0; i < this.length; i++) {
16979
- if (this[i][key] === value) {
16980
- return this[i];
16981
- }
16982
- }
16983
- };
16984
-
16985
- Array.prototype.clean = function() {
16986
- for (var i = 0; i < this.length; i++) {
16987
- if (this[i] === "") {
16988
- this.splice(i, 1);
16989
- i--;
16990
- }
16991
- }
16992
- return this;
16993
- };
16994
-
16995
17215
  Array.prototype.sortByKey = function (key) {
16996
17216
  this.sort(function (a, b) {
16997
17217
  if (a[key] < b[key])
@@ -17035,21 +17255,14 @@ function swipedetect(el, callback){
17035
17255
  }, false)
17036
17256
  }
17037
17257
  function isIOSDevice() {
17038
- if (navigator.userAgentData && navigator.userAgentData.platform) {
17039
- if (navigator.userAgentData.platform === 'iOS') {
17040
- return true;
17041
- }
17042
- }
17043
- const userAgent = navigator.userAgent || navigator.vendor || window.opera;
17044
- const isIOS = /iPad|iPhone|iPod/.test(userAgent) && !window.MSStream;
17045
- const isIPadOS = navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1 && !window.MSStream;
17046
- return isIOS || isIPadOS;
17258
+ return crouton.deviceType === "ios";
17047
17259
  }
17048
17260
 
17049
17261
  function isAndroidDevice() {
17050
- return /Android/i.test(navigator.userAgent);
17262
+ return crouton.deviceType === "android";
17051
17263
  }
17052
17264
 
17265
+
17053
17266
  function isMobileDevice() {
17054
17267
  return isIOSDevice() || isAndroidDevice() || /Mobi|Android/i.test(navigator.userAgent);
17055
17268
  }
@@ -17149,8 +17362,10 @@ function openDirectionsModal(e) {
17149
17362
 
17150
17363
  function closeDirectionsModal() {
17151
17364
  const modal = document.getElementById('directionsMapModal');
17152
- modal.style.display = 'none';
17153
- if (modal.className.includes('remove-after-use')) modal.remove();
17365
+ if (modal) {
17366
+ modal.style.display = 'none';
17367
+ if (modal.className.includes('remove-after-use')) modal.remove();
17368
+ }
17154
17369
  }
17155
17370
 
17156
17371
  function openDirections(url, latitude, longitude, fromModal = true) {
@@ -17173,33 +17388,32 @@ function MeetingMap(inConfig) {
17173
17388
  const config = inConfig;
17174
17389
  if (!config.maxZoom) config.maxZoom = 17;
17175
17390
  if (!config.minZoom) config.minZoom = 6;
17391
+ if (!config.locationSearchZoom) config.locationSearchZoom = 12; // TODO: for now, not in interface. Experience will show if it is worth adding to UI.
17176
17392
  if (!config.marker_contents_template) config.marker_contents_template = croutonDefaultTemplates.marker_contents_template;
17177
17393
  var gAllMeetings = [];
17178
17394
  var gMeetingIdsFromCrouton = null;
17179
17395
  var gSearchPoint = false;
17180
- var loadedCallbackFunction = null;
17181
- var loadedCallbackArgs = [];
17182
- var oldBounds = false;
17183
- var isMouseDown = false;
17396
+ var gLoadedCallbackFunction = null;
17397
+ var gLoadedCallbackArgs = [];
17398
+ var gOldBounds = false;
17399
+ var gCurrentlyDraggingMap = false;
17400
+ var gLocationSearchResult = null;
17401
+ var gAllCities = [];
17184
17402
  function preloadApiLoadedCallback(f,a) {
17185
- loadedCallbackFunction = f;
17186
- loadedCallbackArgs = a;
17403
+ gLoadedCallbackFunction = f;
17404
+ gLoadedCallbackArgs = a;
17187
17405
  }
17188
17406
  function apiLoadedCallback() {
17189
- loadedCallbackFunction(...loadedCallbackArgs);
17407
+ gLoadedCallbackFunction(...gLoadedCallbackArgs);
17190
17408
  }
17191
-
17192
17409
  function retrieveGeolocation() {
17193
17410
  return new Promise((resolve, reject) => {
17194
- if (window.storedGeolocation) {
17195
- resolve(window.storedGeolocation);
17196
- } else if (navigator.geolocation) {
17411
+ if (navigator.geolocation) {
17197
17412
  navigator.geolocation.getCurrentPosition((position) => {
17198
- window.storedGeolocation = {
17199
- latitude: position.coords.latitude,
17200
- longitude: position.coords.longitude
17201
- };
17202
- resolve(window.storedGeolocation);
17413
+ resolve({
17414
+ lat: position.coords.latitude,
17415
+ lng: position.coords.longitude
17416
+ });
17203
17417
  }, (error) => {
17204
17418
  reject(new Error('Error getting geolocation: ' + error.message));
17205
17419
  });
@@ -17211,10 +17425,14 @@ function MeetingMap(inConfig) {
17211
17425
  function isFilterVisible() {
17212
17426
  return config.filter_visible && config.filter_visible == 1;
17213
17427
  }
17214
- /************************************************************************************//**
17215
- * \brief Load the map and set it up. *
17216
- ****************************************************************************************/
17217
-
17428
+ /**
17429
+ * Creates the map and adds the various ornaments like search buttons and menus.
17430
+ * @param HTMLElement inDiv Element that contains the map.
17431
+ * @param Object menuContext Contains info for the map menu. For instance, for [bmlt_map], the dropdown filters.
17432
+ * @param {*} handlebarMapOptions
17433
+ * @param Function cb
17434
+ * @param boolean hide
17435
+ */
17218
17436
  function loadMap(inDiv, menuContext, handlebarMapOptions=null,cb=null,hide=false) {
17219
17437
  if (inDiv) {
17220
17438
  crouton_Handlebars.registerPartial("markerContentsTemplate", crouton_Handlebars.compile(config['marker_contents_template']));
@@ -17241,8 +17459,8 @@ function MeetingMap(inConfig) {
17241
17459
  gDelegate.addListener('idle', triggerCroutonMapNewQuery, false);
17242
17460
  } else {
17243
17461
  gDelegate.addListener('zoomend', function (ev) {
17244
- if (shouldRedrawMarkers() && gAllMeetings) {
17245
- if (listOnlyVisible) {
17462
+ if (shouldRedrawMarkers() && gAllMeetings && !gLocationSearchResult) {
17463
+ if (gListOnlyVisible) {
17246
17464
  const oldValue = filterVisible(false);
17247
17465
  searchResponseCallback();
17248
17466
  filterVisible(oldValue);
@@ -17288,9 +17506,7 @@ function MeetingMap(inConfig) {
17288
17506
  controlDiv.querySelector("#bmltsearch-text-button").addEventListener('click', function () {
17289
17507
  let text = document.getElementById("bmltsearch-goto-text").value.trim();
17290
17508
  if (text === "") return;
17291
- showThrobber();
17292
- gDelegate.callGeocoder(text, null, mapSearchGeocode);
17293
- clearMessageAndClose(gSearchModal);
17509
+ gDelegate.callGeocoder(text, mapSearchGeocode);
17294
17510
  });
17295
17511
  controlDiv.querySelector("#modal-seach-parameters").style.display = 'none';
17296
17512
  controlDiv.querySelector("#show-search-parameters").addEventListener('click', function (e) {
@@ -17320,12 +17536,169 @@ function MeetingMap(inConfig) {
17320
17536
  });
17321
17537
 
17322
17538
  controlDiv.querySelector("#bmltsearch-clicksearch").addEventListener('click', clickSearch);
17323
- [...controlDiv.getElementsByClassName('modal-close')].forEach((elem)=>elem.addEventListener('click', (e)=>clearMessageAndClose(e.target)));
17539
+ [...controlDiv.getElementsByClassName('modal-close')].forEach((elem)=>elem.addEventListener('click', (e)=>closeModalWindow(e.target)));
17324
17540
  gSearchModal = controlDiv.querySelector("#bmltsearch_modal");
17325
17541
  gSearchModal.parentElement.removeChild(gSearchModal);
17326
17542
 
17327
17543
  return controlDiv;
17328
17544
  }
17545
+ var gLocationSearchModal;
17546
+ var gDefaultLocationSearchButtonLabel = '';
17547
+ function doGeolocationThenClose() {
17548
+ doGeolocation().then(closeModalWindow(gLocationSearchModal));
17549
+ }
17550
+ function addAutocompleteEvents(inp, button) {
17551
+ var currentFocus;
17552
+ inp.addEventListener("input",function(e) {
17553
+ var a, b, i, val = this.value;
17554
+ const inp = this;
17555
+ /*close any already open lists of autocompleted values*/
17556
+ closeAllLists();
17557
+ if (!val) { return false;}
17558
+ currentFocus = -1;
17559
+ /*create a DIV element that will contain the items (values):*/
17560
+ a = document.createElement("DIV");
17561
+ a.setAttribute("id", this.id + "autocomplete-list");
17562
+ a.setAttribute("class", "autocomplete-items");
17563
+ /*append the DIV element as a child of the autocomplete container:*/
17564
+ this.parentNode.appendChild(a);
17565
+ /*for each item in the array...*/
17566
+ for (i = 0; i < gAllCities.length; i++) {
17567
+ /*check if the item starts with the same letters as the text field value:*/
17568
+ if (gAllCities[i].substr(0, val.length).toUpperCase() == val.toUpperCase()) {
17569
+ /*create a DIV element for each matching element:*/
17570
+ b = document.createElement("DIV");
17571
+ /*make the matching letters bold:*/
17572
+ b.innerHTML = "<strong>" + gAllCities[i].substr(0, val.length) + "</strong>";
17573
+ b.innerHTML += gAllCities[i].substr(val.length);
17574
+ /*insert a input field that will hold the current array item's value:*/
17575
+ b.innerHTML += "<input type='hidden' value='" + gAllCities[i] + "'>";
17576
+ /*execute a function when someone clicks on the item value (DIV element):*/
17577
+ b.addEventListener("click", function(event) {
17578
+ /*insert the value for the autocomplete text field:*/
17579
+ inp.value = this.getElementsByTagName("input")[0].value;
17580
+ /*close the list of autocompleted values,
17581
+ (or any other open lists of autocompleted values:*/
17582
+ closeAllLists();
17583
+ button.click();
17584
+ });
17585
+ a.appendChild(b);
17586
+ }
17587
+ }
17588
+ });
17589
+ inp.addEventListener("keydown", function(e) {
17590
+ var x = document.getElementById(this.id + "autocomplete-list");
17591
+ if (x) x = x.getElementsByTagName("div");
17592
+ if (e.keyCode == 40) {
17593
+ /*If the arrow DOWN key is pressed,
17594
+ increase the currentFocus variable:*/
17595
+ currentFocus++;
17596
+ /*and and make the current item more visible:*/
17597
+ addActive(x);
17598
+ } else if (e.keyCode == 38) { //up
17599
+ /*If the arrow UP key is pressed,
17600
+ decrease the currentFocus variable:*/
17601
+ currentFocus--;
17602
+ /*and and make the current item more visible:*/
17603
+ addActive(x);
17604
+ } else if (e.keyCode == 13) {
17605
+ /*If the ENTER key is pressed, prevent the form from being submitted,*/
17606
+ e.preventDefault();
17607
+ if (currentFocus > -1) {
17608
+ /*and simulate a click on the "active" item:*/
17609
+ if (x) x[currentFocus].click();
17610
+ }
17611
+ }
17612
+ });
17613
+ function addActive(x) {
17614
+ /*a function to classify an item as "active":*/
17615
+ if (!x) return false;
17616
+ /*start by removing the "active" class on all items:*/
17617
+ removeActive(x);
17618
+ if (currentFocus >= x.length) currentFocus = 0;
17619
+ if (currentFocus < 0) currentFocus = (x.length - 1);
17620
+ /*add class "autocomplete-active":*/
17621
+ x[currentFocus].classList.add("autocomplete-active");
17622
+ }
17623
+ function removeActive(x) {
17624
+ /*a function to remove the "active" class from all autocomplete items:*/
17625
+ for (var i = 0; i < x.length; i++) {
17626
+ x[i].classList.remove("autocomplete-active");
17627
+ }
17628
+ }
17629
+ function closeAllLists() {
17630
+ var x = document.getElementsByClassName("autocomplete-items");
17631
+ for (var i = 0; i < x.length; i++) {
17632
+ x[i].parentNode.removeChild(x[i]);
17633
+ }
17634
+ }
17635
+ /*execute a function when someone clicks in the document:*/
17636
+ document.addEventListener("click", function (e) {
17637
+ closeAllLists(e.target);
17638
+ });
17639
+ }
17640
+ function createLocationSearchButton() {
17641
+ const template = hbs_Crouton.templates['locationSearch'];
17642
+ const controlDiv = document.createElement('div');
17643
+ const params = {};
17644
+ controlDiv.innerHTML = template(params);
17645
+ controlDiv.querySelector("#location-search-button").addEventListener('click', showLocationSearchDialog);
17646
+ gDefaultLocationSearchButtonLabel = controlDiv.querySelector("#location-search-button :first-child").innerHTML;
17647
+ controlDiv.querySelector("#bmlt-location-search-nearbyMeetings").addEventListener('click', doGeolocationThenClose);
17648
+ addAutocompleteEvents(controlDiv.querySelector("#bmlt-location-search-goto-text"), controlDiv.querySelector("#bmlt-location-search-text-button"));
17649
+ controlDiv.querySelector("#bmlt-location-search-text-button").addEventListener('click', function () {
17650
+ let text = document.getElementById("bmlt-location-search-goto-text").value.trim();
17651
+ if (text === "") return;
17652
+ if (isMapVisible()) {
17653
+ gDelegate.callGeocoder(text, locationSearchOpenMapGeocode);
17654
+ } else {
17655
+ gDelegate.callGeocoder(text, locationSearchGeocode);
17656
+ }
17657
+ });
17658
+ controlDiv.querySelector("#bmlt-location-search-widen").addEventListener('click', function () {
17659
+ if (gLocationSearchResult === null) {
17660
+ if (gListOnlyVisible && gSearchPoint) {
17661
+ gLocationSearchResult = {center: gSearchPoint, bounds: gDelegate.getBounds(), zoom: gDelegate.getZoom()};
17662
+ } else return;
17663
+ }
17664
+ if (gLocationSearchResult.zoom == config.minZoom) return;
17665
+ filterVisible(false);
17666
+ let bounds = gDelegate.calculateBoundsFromCenterAndZoom(gLocationSearchResult.center, gLocationSearchResult.zoom);
17667
+ let knt = filterMeetingsAndBounds(bounds).length;
17668
+ for (let zoom = gLocationSearchResult.zoom-1; zoom >= config.minZoom; zoom = zoom-1) {
17669
+ bounds = gDelegate.calculateBoundsFromCenterAndZoom(gLocationSearchResult.center, zoom);
17670
+ if (filterMeetingsAndBounds(bounds).length > knt) {
17671
+ if (isMapVisible()) {
17672
+ gDelegate.flyToFixedZoom(gLocationSearchResult.center, zoom, filterVisible);
17673
+ gLocationSearchResult = null;
17674
+ } else {
17675
+ gLocationSearchResult.zoom = zoom;
17676
+ gLocationSearchResult.bounds = bounds;
17677
+ gListOnlyVisible = false; // force new calc.
17678
+ filterVisible(true, gLocationSearchResult.bounds);
17679
+ }
17680
+ closeModalWindow(gLocationSearchModal);
17681
+ return;
17682
+ }
17683
+ }
17684
+ bounds = gDelegate.calculateBoundsFromCenterAndZoom(gLocationSearchResult.center, gLocationSearchResult.zoom);
17685
+ filterVisible(true, bounds);
17686
+ alert("No further matches");
17687
+ });
17688
+ controlDiv.querySelector("#bmlt-location_search-cancel").addEventListener('click', function () {
17689
+ filterVisible(false);
17690
+ setLocationSearchButtonLabel('');
17691
+ closeModalWindow(gLocationSearchModal);
17692
+ });
17693
+ [...controlDiv.getElementsByClassName('modal-close')].forEach((elem)=>elem.addEventListener('click', (e)=>closeModalWindow(e.target)));
17694
+ gLocationSearchModal = controlDiv.querySelector("#bmlt_location_search_modal");
17695
+ gLocationSearchModal.parentElement.removeChild(gLocationSearchModal);
17696
+
17697
+ return controlDiv;
17698
+ }
17699
+ function isMapVisible() {
17700
+ return gDelegate.isMapVisible() && crouton.isEmbeddedMapShowing();
17701
+ }
17329
17702
  var next24status = false;
17330
17703
  function createNext24Toggle() {
17331
17704
  const toggleSrc = `
@@ -17351,14 +17724,14 @@ function MeetingMap(inConfig) {
17351
17724
  }`
17352
17725
  .replace("__text1__", crouton.localization.getWord("Upcoming Meetings"))
17353
17726
  .replace("__text2__", crouton.localization.getWord("All Meetings"));
17354
- var controlDiv = document.createElement('div');
17727
+ const controlDiv = document.createElement('div');
17355
17728
  controlDiv.innerHTML = toggleSrc;
17356
17729
  controlDiv.querySelector(".onoffswitch").addEventListener('click', function (event) {
17357
17730
  if (event.pointerId < 0) return;
17358
17731
  next24status = !next24status;
17359
- fitDuringFilter = false;
17732
+ gAllowExpandViewport = false;
17360
17733
  crouton.filterNext24(next24status);
17361
- fitDuringFilter = true;
17734
+ gAllowExpandViewport = true;
17362
17735
  });
17363
17736
  let style = document.createElement('style');
17364
17737
  style.innerHTML = rules;
@@ -17366,15 +17739,18 @@ function MeetingMap(inConfig) {
17366
17739
  return controlDiv;
17367
17740
  }
17368
17741
  function createMenuButton(menuContext) {
17369
- var template = hbs_Crouton.templates['mapMenu'];
17370
- var controlDiv = document.createElement('div');
17742
+ const template = hbs_Crouton.templates['mapMenu'];
17743
+ const controlDiv = document.createElement('div');
17371
17744
  controlDiv.innerHTML = template(menuContext);
17372
17745
  controlDiv.querySelector("#nearbyMeetings").addEventListener('click', function (e) {
17373
17746
  retrieveGeolocation().then(position => {
17374
17747
  filterVisible(false);
17375
- gDelegate.setViewToPosition(position, filterMeetingsAndBounds, filterVisible);
17376
- gSearchPoint = {"lat": position.latitude, "lng": position.longitude};
17748
+ gSearchPoint = {"lat": position.lat, "lng": position.lng};
17377
17749
  crouton.updateDistances();
17750
+ const locationSearchResult = gDelegate.getZoomAdjustedBounds(gSearchPoint, filterMeetingsAndBounds, config.locationSearchZoom ?? 12);
17751
+ gDelegate.flyToFixedZoom(position, locationSearchResult.zoom, filterVisible);
17752
+ filterVisible(true);
17753
+ setLocationSearchButtonLabel(crouton.localization.getWord('closest meetings'));
17378
17754
 
17379
17755
  }).catch(error => {
17380
17756
  console.log(error.message);
@@ -17396,21 +17772,15 @@ function MeetingMap(inConfig) {
17396
17772
  if (dropdownContent.style.display == "inline-block") {
17397
17773
  dropdownContent.style.display = "none";
17398
17774
  } else {
17399
- jQuery("#filteringByVisibility").html(listOnlyVisible?'&#10004;':'');
17775
+ jQuery("#filteringByVisibility").html(gListOnlyVisible?'&#10004;':'');
17400
17776
  dropdownContent.style.display = "inline-block";
17401
17777
  }
17402
17778
  });
17403
17779
  [...controlDiv.getElementsByClassName('modal-close')].forEach((elem)=>elem.addEventListener('click', (e)=>closeModalWindow(e.target)));
17404
17780
  controlDiv.querySelector("#close_table").addEventListener('click', hideListView);
17405
- controlDiv.querySelector("#goto-text").addEventListener('keydown', function (event) {
17406
- if (event && event.key == "Enter") {
17407
- closeModalWindow(event.target);
17408
- lookupLocation(g_suspendedFullscreen);
17409
- }
17410
- });
17781
+ addAutocompleteEvents(controlDiv.querySelector("#goto-text"), controlDiv.querySelector("#goto-button"));
17411
17782
  controlDiv.querySelector("#goto-button").addEventListener('click', function (event) {
17412
- closeModalWindow(event.target);
17413
- lookupLocation(g_suspendedFullscreen);
17783
+ lookupLocation(document.getElementById('goto-text').value, g_suspendedFullscreen, mapMenuGeocode);
17414
17784
  });
17415
17785
 
17416
17786
  return controlDiv;
@@ -17418,6 +17788,17 @@ function MeetingMap(inConfig) {
17418
17788
  function hasMapSearch() {
17419
17789
  return 'map_search' in config;
17420
17790
  }
17791
+ /**
17792
+ * Load the meetings for [bmlt_tabs] and [bmlt_map]: all the meetings, during map initialization.
17793
+ * @param string inDiv_id selector for the div that will contain the map.
17794
+ * @param Array meetings_responseObject the meetings returned from the server and enhanced by the crouton core.
17795
+ * @param {*} menuContext information about the dropdown filters.
17796
+ * @param {*} handlebarMapOptions
17797
+ * @param boolean fitBounds should we expand our map so that all the meetings are visible?
17798
+ * @param function callback called after map has been initialized
17799
+ * @param boolean hide make the map invisisble
17800
+ * @returns
17801
+ */
17421
17802
  function loadFromCrouton(inDiv_id, meetings_responseObject, menuContext = null, handlebarMapOptions = null, fitBounds = true, callback, hide) {
17422
17803
  if (!gDelegate.isApiLoaded()) {
17423
17804
  preloadApiLoadedCallback(loadFromCrouton, [inDiv_id, meetings_responseObject, menuContext, handlebarMapOptions, fitBounds, callback, hide]);
@@ -17426,8 +17807,15 @@ function MeetingMap(inConfig) {
17426
17807
  }
17427
17808
  let inDiv = document.getElementById(inDiv_id);
17428
17809
  loadMap(inDiv, menuContext, handlebarMapOptions, callback, hide);
17429
- loadAllMeetings(meetings_responseObject, fitBounds, true);
17810
+ loadAllMeetings(meetings_responseObject, fitBounds);
17430
17811
  };
17812
+ /**
17813
+ * The map on the meeting details popup modal.
17814
+ * @param string inDiv_id selector for the div that will contain the map.
17815
+ * @param Object meeting
17816
+ * @param {*} handlebarMapOptions
17817
+ * @returns
17818
+ */
17431
17819
  function loadPopupMap(inDiv_id, meeting, handlebarMapOptions = null) {
17432
17820
  if (!gDelegate.isApiLoaded()) {
17433
17821
  preloadApiLoadedCallback(loadPopupMap, [inDiv_id, meeting, handlebarMapOptions]);
@@ -17445,25 +17833,21 @@ function MeetingMap(inConfig) {
17445
17833
  gModalDelegate = delegate;
17446
17834
  }
17447
17835
  };
17448
- var fitDuringFilter = true;
17449
- function filterFromCrouton(filter) {
17450
- gMeetingIdsFromCrouton = filter;
17836
+ var gAllowExpandViewport = true;
17837
+ function setFilteredIds(ids) {
17838
+ gMeetingIdsFromCrouton = ids;
17451
17839
  if (gAllMeetings)
17452
- searchResponseCallback(fitDuringFilter && !listOnlyVisible);
17840
+ searchResponseCallback(gAllowExpandViewport && !gListOnlyVisible);
17453
17841
  };
17454
- function clearMessageAndClose(modal) {
17455
- jQuery('#zoomed-out-message').not('.hide').addClass('hide');
17456
- closeModalWindow(modal);
17457
- }
17458
17842
  function nearMeSearch() {
17459
17843
  retrieveGeolocation().then(position => {
17460
17844
  showThrobber();
17461
- gSearchPoint = {"lat": position.latitude, "lng": position.longitude};
17462
- crouton.searchByCoordinates(position.latitude, position.longitude, config.map_search.width);
17463
- if (activeModal == gSearchModal) clearMessageAndClose(gSearchModal);
17845
+ gSearchPoint = {"lat": position.lat, "lng": position.lng};
17846
+ crouton.searchByCoordinates(position.lat, position.lng, config.map_search.width);
17847
+ if (gActiveModal == gSearchModal) closeModalWindow(gSearchModal);
17464
17848
  }).catch(error => {
17465
17849
  console.log(error.message);
17466
- if (activeModal != gSearchModal) showBmltSearchDialog();
17850
+ if (gActiveModal != gSearchModal) showBmltSearchDialog();
17467
17851
  });
17468
17852
  };
17469
17853
  function clickSearch(e) {
@@ -17473,7 +17857,7 @@ function MeetingMap(inConfig) {
17473
17857
  gSearchPoint = {"lat": lat, "lng": lng};
17474
17858
  crouton.searchByCoordinates(lat, lng, config.map_search.width);
17475
17859
  });
17476
- clearMessageAndClose(gSearchModal);
17860
+ closeModalWindow(gSearchModal);
17477
17861
  }
17478
17862
  function createThrobber(inDiv) {
17479
17863
  if (!inDiv.myThrobber) {
@@ -17483,7 +17867,7 @@ function MeetingMap(inConfig) {
17483
17867
  inDiv.myThrobber.className = 'bmlt_map_throbber_div';
17484
17868
  inDiv.myThrobber.style.display = 'none';
17485
17869
  inDiv.appendChild(inDiv.myThrobber);
17486
- var img = document.createElement("img");
17870
+ const img = document.createElement("img");
17487
17871
 
17488
17872
  if (img) {
17489
17873
  img.src = config.BMLTPlugin_images ? config.BMLTPlugin_images + "/Throbber.gif"
@@ -17508,19 +17892,104 @@ function MeetingMap(inConfig) {
17508
17892
  gInDiv.myThrobber.style.display = 'none';
17509
17893
  };
17510
17894
  };
17895
+ function setLocationSearchButtonLabel(label) {
17896
+ if (!label || label==='')
17897
+ jQuery("#location-search-button :first-child").html(gDefaultLocationSearchButtonLabel);
17898
+ else if (gDefaultLocationSearchButtonLabel && gDefaultLocationSearchButtonLabel !== '') {
17899
+ let html = gDefaultLocationSearchButtonLabel.replace(crouton.localization.getWord("Search by Location"), label);
17900
+ jQuery("#location-search-button :first-child").html(html);
17901
+ }
17902
+ }
17903
+ function openMapGeocode(resp, modalName, initial=false) {
17904
+ chooseResponse(modalName, resp, function(resp, i) {
17905
+ let center = gDelegate.getGeocodeCenterAndBounds(resp,i).center;
17906
+ gSearchPoint = {"lat": center.lat, "lng": center.lng};
17907
+ crouton.updateDistances(initial);
17908
+ const modal = document.getElementById(modalName);
17909
+ closeModalWindow(modal);
17910
+ filterVisible(false);
17911
+ const centerAndBounds = gDelegate.getGeocodeCenterAndBounds(resp, i);
17912
+ if (!centerAndBounds) return;
17913
+ const zoom = calcZoomThatContainsBoundingBox(centerAndBounds);
17914
+ const locationSearchResult = gDelegate.getZoomAdjustedBounds(gSearchPoint, filterMeetingsAndBounds, zoom);
17915
+ gDelegate.flyToFixedZoom(gSearchPoint, locationSearchResult.zoom, filterVisible);
17916
+ searchResponseCallback();
17917
+ })
17918
+ }
17919
+ function mapMenuGeocode(resp) {
17920
+ openMapGeocode(resp, "geocoding_modal");
17921
+ }
17922
+ function mapMenuGeocodeInitial(resp) {
17923
+ openMapGeocode(resp, "geocoding_modal", true);
17924
+ }
17511
17925
  function mapSearchGeocode(resp) {
17512
- showThrobber();
17513
- if (document.getElementById("bmltsearch-goto-text"))
17514
- document.getElementById("bmltsearch-goto-text").value = "";
17515
- let latlng = gDelegate.getGeocodeCenter(resp);
17516
- if (!latlng) {
17517
- hideThrobber();
17926
+ chooseResponse('bmltsearch_modal', resp, function(resp, i) {
17927
+ closeModalWindow(gSearchModal);
17928
+ if (document.getElementById("bmltsearch-goto-text"))
17929
+ document.getElementById("bmltsearch-goto-text").value = "";
17930
+ let latlng = gDelegate.getGeocodeCenterAndBounds(resp,i).center;
17931
+ if (!latlng) return;
17932
+ showThrobber();
17933
+ gSearchPoint = {"lat": latlng.lat, "lng": latlng.lng};
17934
+ crouton.searchByCoordinates(latlng.lat, latlng.lng, config.map_search.width);
17935
+ });
17936
+ }
17937
+ function chooseResponse(modalName, resp, callback) {
17938
+ if (jQuery('#'+modalName).is(":hidden")) {
17939
+ callback(resp, 0);
17518
17940
  return;
17519
17941
  }
17520
- gSearchPoint = {"lat": latlng.lat, "lng": latlng.lng};
17521
- crouton.searchByCoordinates(latlng.lat, latlng.lng, config.map_search.width);
17942
+ const choices = gDelegate.getGeocodingChoices(resp);
17943
+ if (choices.length < 2) {
17944
+ setLocationSearchButtonLabel(choices[0]);
17945
+ callback(resp, 0);
17946
+ return;
17947
+ }
17948
+ const html = choices.map((choice, index) =>
17949
+ '<button class="location-choice" data-choice="'+index+'">'+choice+'</button>'
17950
+ );
17951
+ jQuery('#'+modalName+' .crouton-modal-search-page').addClass('hide');
17952
+ jQuery('#'+modalName+' .crouton-modal-search-choice').removeClass('hide')
17953
+ .html('<div class="choose-location"><h4>'+crouton.localization.getWord('Select one')+'</h4>'+html.join('')+'</div>')
17954
+ jQuery('#'+modalName+' .location-choice').on('click', function() {
17955
+ const index = parseInt(jQuery(this).data("choice"))
17956
+ setLocationSearchButtonLabel(choices[index]);
17957
+ callback(resp, index);
17958
+ })
17959
+ }
17960
+ function locationSearchOpenMapGeocode(resp) {
17961
+ openMapGeocode(resp, 'bmlt_location_search_modal');
17962
+ }
17963
+ function calcZoomThatContainsBoundingBox(centerAndBounds) {
17964
+ const center = {lat: (centerAndBounds.southWest.lat + centerAndBounds.northEast.lat)/2,
17965
+ lng: (centerAndBounds.southWest.lng + centerAndBounds.northEast.lng)/2,};
17966
+ let zoom = config.locationSearchZoom + 1;
17967
+ do {
17968
+ if (zoom <= config.minZoom) break;
17969
+ zoom = zoom - 1;
17970
+ var bounds = gDelegate.calculateBoundsFromCenterAndZoom(center, zoom);
17971
+ } while (!gDelegate.contains(bounds, centerAndBounds.southWest.lat, centerAndBounds.southWest.lng)
17972
+ || !gDelegate.contains(bounds, centerAndBounds.northEast.lat, centerAndBounds.northEast.lng))
17973
+ return zoom;
17974
+ }
17975
+ function locationSearchGeocodeInitial(resp) {
17976
+ locationSearchGeocode(resp, true);
17977
+ }
17978
+ function locationSearchGeocode(resp, initial=false) {
17979
+ chooseResponse('bmlt_location_search_modal', resp, function(resp, i) {
17980
+ closeModalWindow(gLocationSearchModal);
17981
+ const centerAndBounds = gDelegate.getGeocodeCenterAndBounds(resp, i);
17982
+ if (!centerAndBounds) return;
17983
+ let center = centerAndBounds.center;
17984
+ filterVisible(false);
17985
+ let zoom = calcZoomThatContainsBoundingBox(centerAndBounds);
17986
+ gLocationSearchResult = gDelegate.getZoomAdjustedBounds(center, filterMeetingsAndBounds, zoom);
17987
+ gSearchPoint = {"lat": gLocationSearchResult.center.lat, "lng": gLocationSearchResult.center.lng};
17988
+ filterVisible(true, gLocationSearchResult.bounds);
17989
+ crouton.updateDistances(initial);
17990
+ });
17522
17991
  }
17523
- function loadAllMeetings(meetings_responseObject, fitBounds=true, fitAll=false) {
17992
+ function loadAllMeetings(meetings_responseObject, fitBounds=true) {
17524
17993
  if (meetings_responseObject === null && config.map_search) {
17525
17994
  if (config.map_search.auto) nearMeSearch();
17526
17995
  else if (config.map_search.coordinates_search) {
@@ -17529,12 +17998,19 @@ function MeetingMap(inConfig) {
17529
17998
  gSearchPoint = {"lat": config.map_search.latitude, "lng": config.map_search.longitude};
17530
17999
  crouton.searchByCoordinates(config.map_search.latitude, config.map_search.longitude, config.map_search.width);
17531
18000
  }
17532
- else if (config.map_search.location) gDelegate.callGeocoder(config.map_search.location, null, mapSearchGeocode);
18001
+ else if (config.map_search.location) gDelegate.callGeocoder(config.map_search.location, mapSearchGeocode);
17533
18002
  else showBmltSearchDialog();
17534
18003
  return;
17535
18004
  }
17536
18005
  gAllMeetings = meetings_responseObject.filter(m => m.venue_type != 2);
17537
- if (fitBounds) {
18006
+ gAllCities = meetings_responseObject.reduce(function (carry, m) {
18007
+ const fields = ['location_municipality', 'location_city_subsection', 'location_province', 'location_neighborhood'];
18008
+ fields.forEach(function (f) {
18009
+ if (m[f].length > 1 && !carry.includes(m[f])) carry.push(m[f]);
18010
+ });
18011
+ return carry;
18012
+ }, []);
18013
+ if (fitBounds && !config.centerMe && !config.goto) {
17538
18014
  let lat_lngs = gAllMeetings.reduce(function(a,m) {a.push([m.latitude, m.longitude]); return a;},[]);
17539
18015
  const maxRadius = config.maxTomatoWidth/2.0;
17540
18016
  if (gSearchPoint) lat_lngs.push([gSearchPoint.lat, gSearchPoint.lng]);
@@ -17550,31 +18026,45 @@ function MeetingMap(inConfig) {
17550
18026
  }
17551
18027
  searchResponseCallback();
17552
18028
  hideThrobber();
17553
- if (isFilterVisible() || config.centerMe || config.goto) crouton.forceShowMap();
17554
- if (config.centerMe) {
17555
- if (navigator.geolocation) {
17556
- navigator.geolocation.getCurrentPosition(
17557
- function (position) {
17558
- coords = {latitude: position.coords.latitude, longitude: position.coords.longitude};
17559
- filterVisible(false);
17560
- if (config.zoom) gDelegate.setZoom(false, config.zoom);
17561
- gDelegate.setViewToPosition(coords, filterMeetingsAndBounds, () => {
17562
- filterVisible(isFilterVisible());
17563
- });
17564
- gSearchPoint = {"lat": position.coords.latitude, "lng": position.coords.longitude};
17565
- crouton.updateDistances(true);
17566
- },
17567
- showGeocodingDialog
17568
- );
17569
- } else if (fitAll) {
17570
- showGeocodingDialog();
17571
- }
17572
- } else {
17573
- if ((!config.centerMe && !config.goto) && !(config.map_search && isFilterVisible())) {
17574
- gDelegate.afterInit(()=>filterVisible(isFilterVisible()));
18029
+ gDelegate.afterInit(!crouton.isEmbeddedMapShowing(), () => {
18030
+ if (config.centerMe) {
18031
+ doGeolocation(isFilterVisible(), true).catch(error => showGeocodingDialog());
18032
+ } else {
18033
+ if ((!config.centerMe && !config.goto) && !(config.map_search && isFilterVisible())) {
18034
+ filterVisible(isFilterVisible());
18035
+ }
18036
+ if (config.goto) {
18037
+ if (isMapVisible()) {
18038
+ gDelegate.callGeocoder(config.goto, mapMenuGeocodeInitial);
18039
+ } else {
18040
+ gDelegate.callGeocoder(config.goto, locationSearchGeocodeInitial);
18041
+ }
18042
+ }
17575
18043
  }
17576
- if (config.goto) gDelegate.callGeocoder(config.goto, isFilterVisible() ? resetVisibleThenFilterMeetingsAndBounds : setVisibleThenFilterMeetingsAndBounds);
17577
- }
18044
+ });
18045
+ }
18046
+ function doGeolocation(makeFilterVisible=true, initial=false) {
18047
+ return new Promise((resolve, reject) =>
18048
+ retrieveGeolocation().then(function(coords) {
18049
+ filterVisible(false);
18050
+ if (isMapVisible() || !makeFilterVisible) {
18051
+ const locationSearchResult = gDelegate.getZoomAdjustedBounds(coords, filterMeetingsAndBounds, config.locationSearchZoom ?? 12);
18052
+ gDelegate.flyToFixedZoom(coords, locationSearchResult.zoom, () => {
18053
+ filterVisible(makeFilterVisible);
18054
+ if (makeFilterVisible) setLocationSearchButtonLabel(crouton.localization.getWord('closest meetings'));
18055
+ });
18056
+ } else {
18057
+ gLocationSearchResult = gDelegate.getZoomAdjustedBounds(coords, filterMeetingsAndBounds, config.locationSearchZoom ?? 12);
18058
+ filterVisible(true, gLocationSearchResult.bounds);
18059
+ setLocationSearchButtonLabel(crouton.localization.getWord('closest meetings'));
18060
+ }
18061
+ gSearchPoint = coords;
18062
+ crouton.updateDistances(initial);
18063
+ resolve(coords);
18064
+ }).catch(error => {
18065
+ reject(error);
18066
+ })
18067
+ );
17578
18068
  }
17579
18069
  function createCityHash(allMeetings) {
17580
18070
  return allMeetings.reduce(function(prev, meeting) {
@@ -17587,11 +18077,12 @@ function MeetingMap(inConfig) {
17587
18077
  }
17588
18078
  var g_suspendedFullscreen = false;
17589
18079
  var g_overflowX;
17590
- var activeModal = null;
18080
+ var gActiveModal = null;
17591
18081
  var swipableModal = false;
17592
18082
  function closeModalWindow(modal) {
17593
18083
  gDelegate.modalOff();
17594
- activeModal = null;
18084
+ gActiveModal = null;
18085
+ if (!modal) return;
17595
18086
  if (!modal.classList.contains("modal"))
17596
18087
  return closeModalWindow(modal.parentNode);
17597
18088
  modal.style.display = "none";
@@ -17611,8 +18102,8 @@ function MeetingMap(inConfig) {
17611
18102
  }
17612
18103
  }
17613
18104
  document.addEventListener("keydown", function(event) {
17614
- if (activeModal && event.key == "Escape") {
17615
- closeModalWindow(activeModal);
18105
+ if (gActiveModal && event.key == "Escape") {
18106
+ closeModalWindow(gActiveModal);
17616
18107
  }
17617
18108
  }, true);
17618
18109
  function openModalWindow(modal,swipe=false) {
@@ -17622,9 +18113,10 @@ function MeetingMap(inConfig) {
17622
18113
  }
17623
18114
  if (!modal) return;
17624
18115
  modal.style.display = "block";
18116
+ document.body.appendChild(modal);
17625
18117
  swipableModal = swipe;
17626
18118
  modal.focus();
17627
- activeModal = modal;
18119
+ gActiveModal = modal;
17628
18120
  dd = document.getElementById("map-menu-dropdown");
17629
18121
  if (dd) dd.style.display = "none";
17630
18122
  gDelegate.modalOn();
@@ -17642,15 +18134,33 @@ function MeetingMap(inConfig) {
17642
18134
  openModalWindow(document.getElementById('filter_modal'));
17643
18135
  }
17644
18136
  function showBmltSearchDialog(e) {
17645
- if (!document.getElementById('bmltsearch_modal')) gInDiv.appendChild(gSearchModal);
18137
+ openModalWindow(gSearchModal);
17646
18138
  if (gDelegate.isMapDefined())
17647
18139
  jQuery('#bmltsearch-clicksearch').parent().show();
17648
18140
  else
17649
18141
  jQuery('#bmltsearch-clicksearch').parent().hide();
17650
- openModalWindow(gSearchModal);
18142
+ jQuery('#zoomed-out-message').addClass('hide');
18143
+ jQuery('#bmltsearch_modal .crouton-modal-search-page').removeClass('hide');
18144
+ jQuery('#bmltsearch_modal .crouton-modal-search-choice').addClass('hide');
18145
+ }
18146
+ function showLocationSearchDialog(e) {
18147
+ openModalWindow(gLocationSearchModal);
18148
+ if ((gSearchPoint !== null) && gListOnlyVisible) {
18149
+ jQuery("#location-search-widen").removeClass('hide');
18150
+ } else {
18151
+ jQuery("#location-search-widen").addClass('hide');
18152
+ }
18153
+ jQuery('#bmlt_location_search_modal .crouton-modal-search-page').removeClass('hide');
18154
+ jQuery('#bmlt_location_search_modal .crouton-modal-search-choice').addClass('hide');
18155
+ jQuery('#bmlt-location-search-goto-text').val('');
18156
+ jQuery('#bmlt-location-search-goto-textautocomplete-list').remove();
17651
18157
  }
17652
18158
  function showGeocodingDialog(e=null) {
17653
18159
  openModalWindow(document.getElementById('geocoding_modal'));
18160
+ jQuery('#geocoding_modal .crouton-modal-search-page').removeClass('hide');
18161
+ jQuery('#geocoding_modal .crouton-modal-search-choice').addClass('hide');
18162
+ jQuery('#goto-text').val('');
18163
+ jQuery('#goto-textautocomplete-list').remove();
17654
18164
  }
17655
18165
  function showListView(e=null) {
17656
18166
  filterVisible();
@@ -17666,36 +18176,17 @@ function MeetingMap(inConfig) {
17666
18176
  jQuery("#table_page").addClass("hide");
17667
18177
  jQuery("#bmlt-map").css("display", "block");
17668
18178
  }
17669
- function resetVisibleThenFilterMeetingsAndBounds(bounds, center=null) {
17670
- filterVisible(false);
17671
- const ret = filterMeetingsAndBounds(bounds);
17672
- filterVisible(true);
17673
- if (gSearchPoint.lat != center.lat || gSearchPoint.lng != center.lng) {
17674
- gSearchPoint = center;
17675
- crouton.updateDistances();
17676
- }
17677
- return ret;
17678
- }
17679
- function setVisibleThenFilterMeetingsAndBounds(bounds, center=null) {
17680
- filterVisible(false);
17681
- const ret = filterMeetingsAndBounds(bounds);
17682
- if (gSearchPoint.lat != center.lat || gSearchPoint.lng != center.lng) {
17683
- gSearchPoint = center;
17684
- crouton.updateDistances();
17685
- }
17686
- return ret;
17687
- }
17688
- function lookupLocation(fullscreen) {
17689
- if (document.getElementById('goto-text').value != '') {
18179
+ function lookupLocation(location,fullscreen, callback) {
18180
+ if (location != '') {
17690
18181
  if (fullscreen) {
17691
18182
  gDelegate.addListener('idle', function () {
17692
- gDelegate.callGeocoder(document.getElementById('goto-text').value, resetVisibleThenFilterMeetingsAndBounds);
18183
+ gDelegate.callGeocoder(location, callback);
17693
18184
  }, true);
17694
18185
  } else {
17695
- gDelegate.callGeocoder(document.getElementById('goto-text').value, resetVisibleThenFilterMeetingsAndBounds);
18186
+ gDelegate.callGeocoder(location, callback);
17696
18187
  }
17697
18188
  } else {
17698
- alert("");
18189
+ alert("Location not found");
17699
18190
  };
17700
18191
  return true;
17701
18192
  };
@@ -17703,19 +18194,9 @@ function MeetingMap(inConfig) {
17703
18194
  if (!gAllMeetings || !gAllMeetings.length) {
17704
18195
  return;
17705
18196
  };
17706
- try {
17707
- drawMarkers(expand);
17708
- if (gSearchPoint) {
17709
- gDelegate.markSearchPoint([gSearchPoint.lat, gSearchPoint.lng]);
17710
- }
17711
- } catch (e) {
17712
- console.log(e);
17713
- gDelegate.addListener('projection_changed', function (ev) {
17714
- drawMarkers(expand);
17715
- if (gSearchPoint) {
17716
- gDelegate.markSearchPoint([gSearchPoint.lat, gSearchPoint.lng]);
17717
- }
17718
- }, true);
18197
+ drawMarkers(expand);
18198
+ if (gSearchPoint) {
18199
+ gDelegate.markSearchPoint([gSearchPoint.lat, gSearchPoint.lng]);
17719
18200
  }
17720
18201
  };
17721
18202
  /****************************************************************************************
@@ -17763,33 +18244,55 @@ function MeetingMap(inConfig) {
17763
18244
  gDelegate.fitBounds(lat_lngs);
17764
18245
  }
17765
18246
  };
18247
+ function latLngToWebMercator(latlng, zoom) {
18248
+ const lat = Math.max(-85.05112878, Math.min(85.05112878, latlng.lat));
18249
+ const phi = lat * Math.PI / 180;
18250
+ const scale = 256 * Math.pow(2, zoom);
18251
+
18252
+ const x = scale * ((latlng.lng/360.) + .5);
18253
+ const y =
18254
+ scale * (Math.PI - Math.log(Math.tan(Math.PI / 4 + phi / 2))) / (2 * Math.PI);
18255
+ return { x: x, y: y };
18256
+ }
18257
+ function webMercatorToLatLng(point, zoom) {
18258
+ const scale = 256 * Math.pow(2, zoom);
18259
+
18260
+ const lng = 360 * (point.x / scale - .5);
18261
+ const t = Math.exp(Math.PI - (2 * Math.PI) * (point.y / scale));
18262
+ const phi = 2 * Math.atan(t) - Math.PI / 2; // radians
18263
+
18264
+ return { lat: phi * 180 / Math.PI, lng: lng };
18265
+ }
17766
18266
  function mapOverlappingMarkersInCity(in_meeting_array) ///< Used to draw the markers when done.
17767
18267
  {
17768
- var tolerance = 8; /* This is how many pixels we allow. */
18268
+ const tolerance = 8; /* This is how many pixels we allow. */
17769
18269
 
17770
- var ret = new Array;
18270
+ const ret = new Array;
18271
+ const zoom = gDelegate.getZoom();
18272
+ const center = latLngToWebMercator(gDelegate.getCenter(), zoom)
17771
18273
  // We create this hash because we limit looking for "matches" to within one city.
17772
18274
  for (const [city, meetings] of Object.entries(createCityHash(in_meeting_array))) {
17773
18275
  // create a tmp object so we can mark which items haven't been matched yet.
17774
- var tmp = meetings.map((meeting) => {
18276
+ const tmp = meetings.map((meeting) => {
17775
18277
  item = new Object;
17776
18278
  item.matched = false;
17777
18279
  item.meeting = meeting;
17778
- item.coords = gDelegate.fromLatLngToPoint(meeting.latitude, meeting.longitude);
18280
+ const point = latLngToWebMercator({lat: meeting.latitude, lng: meeting.longitude}, zoom);
18281
+ item.coords = {x: point.x-center.x, y: point.y-center.y};
17779
18282
  return item;
17780
18283
  });
17781
18284
  tmp.reduce(function(prev, item, index) {
17782
18285
  if (item.matched) return prev;
17783
18286
  matches = [item.meeting];
17784
- var outer_coords = item.coords;
18287
+ const outer_coords = item.coords;
17785
18288
  for (c2 = index+1; c2<meetings.length; c2++) {
17786
18289
  if (tmp[c2].matched) continue;
17787
- var inner_coords = tmp[c2].coords;
18290
+ const inner_coords = tmp[c2].coords;
17788
18291
 
17789
- var xmin = outer_coords.x - tolerance;
17790
- var xmax = outer_coords.x + tolerance;
17791
- var ymin = outer_coords.y - tolerance;
17792
- var ymax = outer_coords.y + tolerance;
18292
+ const xmin = outer_coords.x - tolerance;
18293
+ const xmax = outer_coords.x + tolerance;
18294
+ const ymin = outer_coords.y - tolerance;
18295
+ const ymax = outer_coords.y + tolerance;
17793
18296
 
17794
18297
  /* We have an overlap. */
17795
18298
  if ((inner_coords.x >= xmin) && (inner_coords.x <= xmax) && (inner_coords.y >= ymin) && (inner_coords.y <= ymax)) {
@@ -17806,8 +18309,8 @@ function MeetingMap(inConfig) {
17806
18309
  return ret;
17807
18310
  };
17808
18311
  function sortMeetingSearchResponseCallback(mtg_a, mtg_b) {
17809
- var weekday_score_a = parseInt(mtg_a.weekday_tinyint, 10);
17810
- var weekday_score_b = parseInt(mtg_b.weekday_tinyint, 10);
18312
+ const weekday_score_a = parseInt(mtg_a.weekday_tinyint, 10);
18313
+ const weekday_score_b = parseInt(mtg_b.weekday_tinyint, 10);
17811
18314
 
17812
18315
  if (weekday_score_a < config.start_week) {
17813
18316
  weekday_score_a += 7;
@@ -17823,8 +18326,8 @@ function MeetingMap(inConfig) {
17823
18326
  else if (weekday_score_a > weekday_score_b) {
17824
18327
  return 1;
17825
18328
  };
17826
- var time_a = mtg_a.start_time.toString().split(':');
17827
- var time_b = mtg_b.start_time.toString().split(':');
18329
+ const time_a = mtg_a.start_time.toString().split(':');
18330
+ const time_b = mtg_b.start_time.toString().split(':');
17828
18331
  if (parseInt(time_a[0]) < parseInt(time_b[0])) {
17829
18332
  return -1;
17830
18333
  }
@@ -17856,77 +18359,77 @@ function MeetingMap(inConfig) {
17856
18359
  * \brief This creates a single meeting's marker on the map. *
17857
18360
  ****************************************************************************************/
17858
18361
  function createMapMarker(meetings, openMarker) {
17859
- var main_point = [meetings[0].latitude, meetings[0].longitude];
18362
+ const main_point = [meetings[0].latitude, meetings[0].longitude];
17860
18363
  const marker_html = markerTemplate(meetings);
17861
18364
  const marker = gDelegate.createMarker(main_point, (meetings.length > 1), null);
17862
18365
  gDelegate.bindPopup(marker, marker_html, meetings.map((m)=>parseInt(m.id_bigint)), openMarker);
17863
18366
  };
17864
18367
  function createGroupMarker(group, openMarker) {
17865
- var main_point = [group.latitude, group.longitude];
18368
+ const main_point = [group.latitude, group.longitude];
17866
18369
  const marker = gDelegate.createMarker(main_point, group['membersOfGroup'].length > 1, null);
17867
18370
  gDelegate.addMarkerCallback(marker, function() {
17868
18371
  crouton.openMeetingModal(group);
17869
18372
  });
17870
18373
  };
17871
- var listOnlyVisible = false;
17872
- var listener = null;
18374
+ var gListOnlyVisible = false;
18375
+ var gDragStartListener = null;
17873
18376
  function filterBounds(bounds) {
17874
18377
  return gAllMeetings.filter((meeting) => gDelegate.contains(bounds, meeting.latitude, meeting.longitude));
17875
18378
  }
17876
18379
  function onDragStart() {
17877
- isMouseDown = true;
18380
+ gCurrentlyDraggingMap = true;
17878
18381
  // if no [crouton_map], then show all meetings
17879
18382
  if (!config.map_search) filterVisible(false);
17880
18383
  gDelegate.addListener('dragend', onDragEnd, true);
17881
18384
  }
17882
18385
  function onDragEnd() {
17883
- isMouseDown = false;
18386
+ gCurrentlyDraggingMap = false;
17884
18387
  // if no [crouton_map], then turn filter visible back on.
17885
18388
  if (config.map_search && isFilterVisible()) triggerCroutonMapNewQuery(null);
17886
18389
  else filterVisible(true);
17887
18390
  }
17888
18391
  function triggerCroutonMapNewQuery(ev) {
17889
- if (isMouseDown) return;
18392
+ if (gCurrentlyDraggingMap) return;
17890
18393
  gMeetingIdsFromCrouton = null;
17891
18394
  corners = gDelegate.getCorners();
17892
- if (oldBounds && gDelegate.contains(oldBounds,corners.ne.lat,corners.ne.lng)
17893
- && gDelegate.contains(oldBounds,corners.sw.lat,corners.sw.lng)) {
18395
+ if (gOldBounds && gDelegate.contains(gOldBounds,corners.ne.lat,corners.ne.lng)
18396
+ && gDelegate.contains(gOldBounds,corners.sw.lat,corners.sw.lng)) {
17894
18397
  filterVisible();
17895
18398
  } else if (getScreenRadius('km') > config.maxTomatoWidth) {
17896
18399
  jQuery('#zoomed-out-message').removeClass('hide');
17897
18400
  showBmltSearchDialog(null);
17898
18401
  } else {
17899
- oldBounds = gDelegate.getBounds();
18402
+ gOldBounds = gDelegate.getBounds();
17900
18403
  showThrobber();
17901
18404
  crouton.searchByCoordinates(gDelegate.getCenter().lat, gDelegate.getCenter().lng, getScreenRadius(), false);
17902
18405
  }
17903
18406
  }
17904
- function filterVisible(on=true) {
17905
- if (on==listOnlyVisible && !config.map_search) return on;
17906
- let mtgs = on ? filterBounds(gDelegate.getBounds()) : gAllMeetings;
18407
+ function filterVisible(on=true, bounds = gDelegate.getBounds()) {
18408
+ if (on==gListOnlyVisible && !config.map_search) return on;
18409
+ let mtgs = on ? filterBounds(bounds) : gAllMeetings;
17907
18410
  let visible = mtgs.map((m)=>m.id_bigint);
17908
18411
  jQuery(".bmlt-data-row").each(function(index,row) {
17909
18412
  row.dataset.visible = (visible.includes(row.id.split('-').pop())) ? '1' : '0';
17910
18413
  });
17911
18414
  jQuery("#byday").removeClass('hide');
17912
18415
  jQuery("#filter-dropdown-visibile").val(on?'a-1':'');
17913
- fitDuringFilter = false;
17914
- crouton.simulateFilterDropdown();
17915
- fitDuringFilter = true;
18416
+ gAllowExpandViewport = false; // calling simulateFilterDropdown will callback croutonMap to set the filtered Ids.
18417
+ crouton.simulateFilterDropdown(); // but we don't want to expand the viewport.
18418
+ gAllowExpandViewport = true;
17916
18419
  jQuery("#filteringByVisibility").html(on?'&#10004;':'');
17917
- listOnlyVisible = on;
17918
- if (on) listener = gDelegate.addListener('dragstart', onDragStart, true);
17919
- else if (listener) {
17920
- gDelegate.removeListener(listener);
17921
- listener = null;
18420
+ gListOnlyVisible = on;
18421
+ if (on) gDragStartListener = gDelegate.addListener('dragstart', onDragStart, true);
18422
+ else if (gDragStartListener) {
18423
+ gDelegate.removeListener(gDragStartListener);
18424
+ gDragStartListener = null;
17922
18425
  }
17923
18426
  return !on;
17924
18427
  }
17925
18428
  function toggleVisible() {
17926
- filterVisible(!listOnlyVisible);
18429
+ filterVisible(!gListOnlyVisible);
17927
18430
  }
17928
18431
  function filterBounds(bounds) {
17929
- var ret = gAllMeetings.filter((meeting) =>
18432
+ const ret = gAllMeetings.filter((meeting) =>
17930
18433
  gDelegate.contains(bounds, meeting.latitude, meeting.longitude));
17931
18434
  return ret;
17932
18435
  }
@@ -17936,31 +18439,31 @@ function MeetingMap(inConfig) {
17936
18439
  if ((gDelegate.getZoom()>=14) && gDelegate.contains(gDelegate.getBounds(), meeting.latitude, meeting.longitude)) {
17937
18440
  gDelegate.openMarker(meetingId);
17938
18441
  } else {
17939
- gDelegate.setViewToPosition({latitude: meeting.latitude, longitude: meeting.longitude}, filterMeetingsAndBounds, function() {gDelegate.openMarker(meetingId);});
18442
+ gDelegate.flyToFixedZoom({lat: meeting.latitude, lng: meeting.longitude}, config.focusedMeetingPopupZoomLevel ?? 14, function() {gDelegate.openMarker(meetingId);});
17940
18443
  }
17941
18444
  }
17942
18445
  function filterMeetingsAndBounds(bounds) {
17943
18446
  return filterMeetings(filterBounds(bounds));
17944
18447
  }
17945
18448
  function filterMeetings(in_meetings_array) {
17946
- var ret = in_meetings_array.filter(m => m.venue_type != 2);
18449
+ const ret = in_meetings_array.filter(m => m.venue_type != 2);
17947
18450
  if (gMeetingIdsFromCrouton != null) {
17948
18451
  return ret.filter((m) => gMeetingIdsFromCrouton.includes(m.id_bigint));
17949
18452
  }
17950
18453
  return ret;
17951
18454
  }
17952
- var _isPseudoFullscreen = false;
18455
+ var gIsPseudoFullscreen = false;
17953
18456
  function isFullscreen() {
17954
- var fullscreenElement =
18457
+ const fullscreenElement =
17955
18458
  document.fullscreenElement ||
17956
18459
  document.mozFullScreenElement ||
17957
18460
  document.webkitFullscreenElement ||
17958
18461
  document.msFullscreenElement;
17959
18462
 
17960
- return (fullscreenElement === gInDiv) || _isPseudoFullscreen;
18463
+ return (fullscreenElement === gInDiv) || gIsPseudoFullscreen;
17961
18464
  }
17962
18465
  function toggleFullscreen(options) {
17963
- var container = gInDiv;
18466
+ const container = gInDiv;
17964
18467
  if (isFullscreen()) {
17965
18468
  if (options && options.pseudoFullscreen) {
17966
18469
  _setFullscreen(false);
@@ -17991,10 +18494,9 @@ function MeetingMap(inConfig) {
17991
18494
  }
17992
18495
  }
17993
18496
  }
17994
- var _isPseudoFullscreen = false;
17995
18497
  function _setFullscreen(fullscreen) {
17996
- _isPseudoFullscreen = fullscreen;
17997
- var container = gInDiv;
18498
+ gIsPseudoFullscreen = fullscreen;
18499
+ const container = gInDiv;
17998
18500
  if (fullscreen) {
17999
18501
  L.DomUtil.addClass(container, 'leaflet-pseudo-fullscreen');
18000
18502
  } else {
@@ -18009,6 +18511,12 @@ function MeetingMap(inConfig) {
18009
18511
  }
18010
18512
  gDelegate.invalidateSize();
18011
18513
  if (!gAllMeetings) return;
18514
+ if (gLocationSearchResult) {
18515
+ searchResponseCallback();
18516
+ filterVisible(false);
18517
+ gDelegate.flyToFixedZoom(gLocationSearchResult.center, gLocationSearchResult.zoom, filterVisible);
18518
+ gLocationSearchResult = null;
18519
+ }
18012
18520
  if (fitBounds) gDelegate.fitBounds(
18013
18521
  ((gMeetingIdsFromCrouton) ? gAllMeetings.filter((m) => gMeetingIdsFromCrouton.includes(m.id_bigint)) : gAllMeetings)
18014
18522
  .reduce(function(a,m) {a.push([m.latitude, m.longitude]); return a;},[])
@@ -18019,14 +18527,14 @@ function MeetingMap(inConfig) {
18019
18527
  }
18020
18528
 
18021
18529
  function getDistance(p1, p2) {
18022
- var R = 6378137; // Earth’s mean radius in meter
18023
- var dLat = rad(p2.lat - p1.lat);
18024
- var dLong = rad(p2.lng - p1.lng);
18025
- var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
18530
+ const R = 6378137; // Earth’s mean radius in meter
18531
+ const dLat = rad(p2.lat - p1.lat);
18532
+ const dLong = rad(p2.lng - p1.lng);
18533
+ const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
18026
18534
  Math.cos(rad(p1.lat)) * Math.cos(rad(p2.lat)) *
18027
18535
  Math.sin(dLong / 2) * Math.sin(dLong / 2);
18028
- var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
18029
- var d = R * c;
18536
+ const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
18537
+ const d = R * c;
18030
18538
  return d; // returns the distance in meter
18031
18539
  }
18032
18540
  function getDistanceFromSearch(p) {
@@ -18049,7 +18557,7 @@ function MeetingMap(inConfig) {
18049
18557
  ****************************************************************************************/
18050
18558
  this.initialize = loadFromCrouton;
18051
18559
  this.showMap = showMap;
18052
- this.fillMap = filterFromCrouton;
18560
+ this.setFilteredIds = setFilteredIds;
18053
18561
  this.rowClick = focusOnMeeting;
18054
18562
  this.apiLoadedCallback = apiLoadedCallback;
18055
18563
  this.refreshMeetings = loadAllMeetings;
@@ -18059,20 +18567,25 @@ function MeetingMap(inConfig) {
18059
18567
  this.loadPopupMap = loadPopupMap;
18060
18568
  this.filterVisible = filterVisible;
18061
18569
  this.hasMapSearch = hasMapSearch;
18570
+ this.createLocationSearchButton = createLocationSearchButton;
18571
+ this.latLngToWebMercator = latLngToWebMercator;
18572
+ this.webMercatorToLatLng = webMercatorToLatLng;
18062
18573
  };
18063
18574
  MeetingMap.prototype.initialize = null;
18064
18575
  MeetingMap.prototype.showMap = null;
18065
- MeetingMap.prototype.fillMap = null;
18576
+ MeetingMap.prototype.setFilteredIds = null;
18066
18577
  MeetingMap.prototype.rowClick = null;
18067
18578
  MeetingMap.prototype.apiLoadedCallback = null;
18068
18579
  MeetingMap.prototype.refreshMeetings = null;
18069
18580
  MeetingMap.prototype.getDistanceFromSearch = null;
18070
-
18581
+ MeetingMap.prototype.latLngToWebMercator = null; // called from delegates - yuck!
18582
+ MeetingMap.prototype.webMercatorToLatLng = null; // called from delegates - yuck!
18071
18583
  MeetingMap.prototype.openModalWindow = null;
18072
18584
  MeetingMap.prototype.closeModalWindow = null;
18073
18585
  MeetingMap.prototype.loadPopupMap = null;
18074
18586
  MeetingMap.prototype.filterVisible = null;
18075
18587
  MeetingMap.prototype.hasMapSearch = null;
18588
+ MeetingMap.prototype.createLocationSearchButton = null;
18076
18589
 
18077
18590
  function MapDelegate(in_config) {
18078
18591
  const config = in_config;
@@ -18081,6 +18594,7 @@ function MapDelegate(in_config) {
18081
18594
  var g_icon_image_selected = null;
18082
18595
  var g_icon_shadow = null;
18083
18596
  var g_icon_shape = null;
18597
+ var gDiv = null;
18084
18598
  var gMainMap;
18085
18599
  var gOms = null;
18086
18600
  var gMarkerClusterer = null;
@@ -18097,15 +18611,15 @@ function MapDelegate(in_config) {
18097
18611
  var tag = document.createElement('script');
18098
18612
  gIsLoaded = true;
18099
18613
  if (typeof config['api_key'] === 'undefined') config['api_key'] = "";
18100
- tag.src = "https://maps.googleapis.com/maps/api/js?key=" + config['api_key'] + "&callback=croutonMap.apiLoadedCallback";
18614
+ tag.src = "https://maps.googleapis.com/maps/api/js?key=" + config['api_key'] + "&loading=async&callback=croutonMap.apiLoadedCallback";
18101
18615
  tag.defer = true;
18102
18616
  tag.async = true;
18103
18617
  var firstScriptTag = document.getElementsByTagName('script')[0];
18104
18618
  firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
18105
18619
  };
18106
18620
  function createMap(inDiv, inCenter, inHidden = false) {
18621
+ gDiv = inDiv;
18107
18622
  if ( inHidden ) {
18108
- gDiv = inDiv;
18109
18623
  gDiv.style.height = 'auto';
18110
18624
  gDiv.style.marginBottom = '10px';
18111
18625
  gMainMap = null;
@@ -18195,12 +18709,14 @@ function MapDelegate(in_config) {
18195
18709
  }, start);
18196
18710
  gMainMap.fitBounds(bounds);
18197
18711
  }
18198
- function setViewToPosition(position, filterMeetings, f) {
18712
+ function flyToFixedZoom(position, zoom, extra) {
18199
18713
  if (!gMainMap) return;
18200
- var latlng = new google.maps.LatLng(position.latitude, position.longitude);
18714
+ google.maps.event.addListenerOnce(gMainMap, 'idle', function() {
18715
+ if (extra) extra();
18716
+ });
18717
+ const latlng = new google.maps.LatLng(position.lat, position.lng);
18201
18718
  gMainMap.setCenter(latlng);
18202
- gMainMap.setZoom(getZoomAdjust(false, filterMeetings));
18203
- f && f();
18719
+ gMainMap.setZoom(zoom);
18204
18720
  }
18205
18721
  function getOpenMarker() {
18206
18722
  return gOpenMarker;
@@ -18218,58 +18734,39 @@ function MapDelegate(in_config) {
18218
18734
  function isFilterVisible() {
18219
18735
  return config.filter_visible && config.filter_visible == 1;
18220
18736
  }
18221
- function getZoomAdjust(only_out,filterMeetings) {
18222
- if (!gMainMap) return 12;
18223
- var ret = gMainMap.getZoom();
18224
- if (config.map_search && isFilterVisible()) return ret;
18225
- var center = gMainMap.getCenter();
18226
- var bounds = gMainMap.getBounds();
18227
- var zoomedOut = false;
18228
- while(filterMeetings(bounds, {"lat":center.lat(), "lng":center.lng()}).length==0 && ret>6) {
18229
- zoomedOut = true;
18230
- // no exact, because earth is curved
18231
- ret -= 1;
18232
- var ne = new google.maps.LatLng({
18233
- lat: (2*bounds.getNorthEast().lat())-center.lat(),
18234
- lng: (2*bounds.getNorthEast().lng())-center.lng()});
18235
- var sw = new google.maps.LatLng({
18236
- lat: (2*bounds.getSouthWest().lat())-center.lat(),
18237
- lng: (2*bounds.getSouthWest().lng())-center.lng()});
18238
- bounds = new google.maps.LatLngBounds(sw,ne);
18239
- }
18240
- if (!only_out && !zoomedOut && ret<12) {
18241
- var knt = filterMeetings(bounds).length;
18242
- while(ret<12 && knt>0) {
18243
- // no exact, because earth is curved
18244
- ret += 1;
18245
- var ne = new google.maps.LatLng({
18246
- lat: 0.5*(bounds.getNorthEast().lat()+center.lat()),
18247
- lng: 0.5*(bounds.getNorthEast().lng()+center.lng())});
18248
- var sw = new google.maps.LatLng({
18249
- lat: 0.5*(bounds.getSouthWest().lat()+center.lat()),
18250
- lng: 0.5*(bounds.getSouthWest().lng()+center.lng())});
18251
- bounds = new google.maps.LatLngBounds(sw,ne);
18252
- knt = filterMeetings(bounds).length;
18253
- }
18254
- if (knt == 0) {
18255
- ret -= 1;
18256
- }
18257
- }
18737
+ function webMercatorToLatLng(point, zoom) {
18738
+ const latLng = croutonMap.webMercatorToLatLng(point, zoom);
18739
+ return new google.maps.LatLng(latLng.lat, latLng.lng);
18740
+ }
18741
+ function calculateBounds(center, zoom, width, height) {
18742
+ const centerPoint = croutonMap.latLngToWebMercator(center, zoom);
18743
+ const southwestWebMercator = {x: centerPoint.x-width/2, y: centerPoint.y+height/2};
18744
+ const northeastWebMercator = {x: centerPoint.x+width/2, y: centerPoint.y-height/2};
18745
+ return new google.maps.LatLngBounds(webMercatorToLatLng(southwestWebMercator, zoom), webMercatorToLatLng(northeastWebMercator, zoom));
18746
+ }
18747
+ function calcOffsetWidth(gDiv) {
18748
+ let ret = gDiv.offsetWidth;
18749
+ if (ret === 0) ret = gDiv.parentElement.offsetWidth;
18258
18750
  return ret;
18259
18751
  }
18260
- function setZoom(filterMeetings, force=0) {
18261
- if (!gMainMap) return;
18262
- (force > 0) ? gMainMap.setZoom(force) :
18263
- gMainMap.setZoom(getZoomAdjust(false,filterMeetings));
18264
- }
18752
+ function getZoomAdjust(filterMeetings, zoomLevel=gMainMap.getZoom(), center=gMainMap.getCenter()) {
18753
+ if (!gMainMap) return 12;
18754
+ const mapWidth = calcOffsetWidth(gDiv);
18755
+ const mapHeight = parseInt(jQuery(gDiv).css("height").replace("px",""));
18756
+ const center_latlng = {"lat":center.lat(), "lng":center.lng()};
18757
+ ret = zoomLevel;
18758
+ if (config.map_search && isFilterVisible()) return ret;
18759
+ var bounds = calculateBounds(center_latlng, ret, mapWidth, mapHeight);
18760
+ while(filterMeetings(bounds, center_latlng).length==0 && ret>6) {
18761
+ ret -= 1;
18762
+ bounds = calculateBounds(center_latlng, ret, mapWidth, mapHeight);
18763
+ }
18764
+ return ret;
18765
+ }
18265
18766
  function getZoom() {
18266
18767
  if (!gMainMap) return 12;
18267
18768
  return gMainMap.getZoom();
18268
18769
  }
18269
- function zoomOut(filterMeetings) {
18270
- if (!gMainMap) return;
18271
- gMainMap.setZoom(getZoomAdjust(true,filterMeetings));
18272
- }
18273
18770
  function contains(bounds, lat, lng) {
18274
18771
  if (!gMainMap) return true;
18275
18772
  return bounds.contains(new google.maps.LatLng ( lat, lng));
@@ -18278,13 +18775,6 @@ function MapDelegate(in_config) {
18278
18775
  if (!gMainMap) return null;
18279
18776
  return gMainMap.getBounds();
18280
18777
  }
18281
- function fromLatLngToPoint(lat, lng) {
18282
- if (!gMainMap) return null;
18283
- var latLng = new google.maps.LatLng ( lat, lng);
18284
- var scale = 1 << gMainMap.getZoom();
18285
- var worldPoint = gMainMap.getProjection().fromLatLngToPoint(latLng);
18286
- return new google.maps.Point(worldPoint.x * scale, worldPoint.y * scale);
18287
- };
18288
18778
  function createClusterLayer() {
18289
18779
  gIsClustering = true;
18290
18780
  }
@@ -18454,14 +18944,6 @@ function addControl(div,pos,cb) {
18454
18944
  observer.observe(document, {childList: true, subtree: true});
18455
18945
  }
18456
18946
  gMainMap.controls[p].push(div);
18457
- }
18458
- /************************************************************************************//**
18459
- * \brief This catches the AJAX response, and fills in the response form. *
18460
- ****************************************************************************************/
18461
- function fitAndZoom(ev) {
18462
- if (!gMainMap) return;
18463
- gMainMap.fitBounds(this.response[0].geometry.viewport);
18464
- gMainMap.setZoom(getZoomAdjust(true,this.filterMeetings));
18465
18947
  }
18466
18948
  function openMarker(id) {
18467
18949
  if (!gMainMap) return;
@@ -18471,22 +18953,23 @@ function openMarker(id) {
18471
18953
  openInfoWindow(marker.marker)
18472
18954
  }
18473
18955
  }
18474
- function getGeocodeCenter(in_geocode_response) {
18475
- if ( in_geocode_response && in_geocode_response[0] && in_geocode_response[0].geometry && in_geocode_response[0].geometry.location )
18476
- return {lat: in_geocode_response[0].geometry.location.lat(), lng: in_geocode_response[0].geometry.location.lng()};
18477
- else alert ( crouton.localization.getWord("address_lookup_fail") );
18956
+ function getGeocodingChoices(in_geocode_response) {
18957
+ if (!Array.isArray(in_geocode_response)) return [];
18958
+ return in_geocode_response.map((r) => r.formatted_address);
18478
18959
  }
18479
- function geoCallback( in_geocode_response ) {
18480
- var callback = fitAndZoom.bind({filterMeetings:this.filterMeetings,
18481
- response: in_geocode_response});
18482
- if ( in_geocode_response && in_geocode_response[0] && in_geocode_response[0].geometry && in_geocode_response[0].geometry.location ) {
18483
- gMainMap.panTo ( in_geocode_response[0].geometry.location );
18484
- google.maps.event.addListenerOnce( gMainMap, 'idle', callback);
18485
- } else {
18486
- alert ( crouton.localization.getWord("address_lookup_fail") );
18487
- };
18488
- };
18489
- function callGeocoder(in_loc, filterMeetings, callback=geoCallback) {
18960
+ function getGeocodeCenterAndBounds(in_geocode_response, i=0) {
18961
+ if ( in_geocode_response && in_geocode_response[i] && in_geocode_response[i].geometry && in_geocode_response[i].geometry.location )
18962
+ return {
18963
+ center: {lat: in_geocode_response[i].geometry.location.lat(), lng: in_geocode_response[i].geometry.location.lng()},
18964
+ southWest: {lat: in_geocode_response[i].geometry.viewport.getSouthWest().lat(),
18965
+ lng: in_geocode_response[i].geometry.viewport.getSouthWest().lng()},
18966
+ northEast: {lat: in_geocode_response[i].geometry.viewport.getNorthEast().lat(),
18967
+ lng: in_geocode_response[i].geometry.viewport.getNorthEast().lng()},
18968
+ };
18969
+ alert ( crouton.localization.getWord("address_lookup_fail") );
18970
+ return null;
18971
+ }
18972
+ function callGeocoder(in_loc, callback) {
18490
18973
  var geocoder = new google.maps.Geocoder;
18491
18974
 
18492
18975
  if ( geocoder )
@@ -18504,14 +18987,32 @@ function geoCallback( in_geocode_response ) {
18504
18987
  new google.maps.LatLng(config.bounds.south, config.bounds.west),
18505
18988
  new google.maps.LatLng(config.bounds.north, config.bounds.east));
18506
18989
  }
18507
- if (filterMeetings)
18508
- callback = callback.bind({filterMeetings: filterMeetings});
18509
18990
  geocoder.geocode ( geoCodeParams, callback );
18510
18991
  }
18511
18992
  else // None of that stuff is defined if we couldn't create the geocoder.
18512
18993
  {
18513
18994
  alert ( crouton.localization.getWord("address_lookup_fail") );
18514
18995
  };
18996
+ }
18997
+ function getZoomAdjustedBounds(center_latlng, filterMeetings, zoomLevel) {
18998
+ if (!center_latlng) return null;
18999
+ const center = new google.maps.LatLng(center_latlng.lat, center_latlng.lng);
19000
+ const mapWidth = calcOffsetWidth(gDiv);
19001
+ const mapHeight = parseInt(jQuery(gDiv).css("height").replace("px",""));
19002
+ if (center) {
19003
+ const zoom = getZoomAdjust(filterMeetings, zoomLevel, center);
19004
+ const bounds = calculateBounds(center_latlng, zoom, mapWidth, mapHeight);
19005
+ const ret = {"center": center_latlng, "bounds": bounds, "zoom": zoom};
19006
+ return ret;
19007
+ } else {
19008
+ alert ( crouton.localization.getWord("address_lookup_fail") );
19009
+ return null;
19010
+ }
19011
+ }
19012
+ function calculateBoundsFromCenterAndZoom(center, zoomLevel) {
19013
+ const mapWidth = calcOffsetWidth(gDiv);
19014
+ const mapHeight = parseInt(jQuery(gDiv).css("height").replace("px",""));
19015
+ return calculateBounds(center, zoomLevel, mapWidth, mapHeight);
18515
19016
  }
18516
19017
  function invalidateSize() {
18517
19018
  }
@@ -18545,24 +19046,28 @@ function geoCallback( in_geocode_response ) {
18545
19046
  var center = gMainMap.getCenter();
18546
19047
  return { "lat": center.lat(), "lng": center.lng()}
18547
19048
  }
18548
- function afterInit(f) {
19049
+ function afterInit(doNotWait, f) {
18549
19050
  if (!gMainMap) return;
18550
- if (typeof gMainMap.getBounds() !== 'undefined') f();
18551
- else addListener('idle', f, true);
19051
+ if (doNotWait || (typeof gMainMap.getBounds() !== 'undefined')) f();
19052
+ else addListener('idle', function() {
19053
+ f();
19054
+ }, true);
18552
19055
  }
18553
19056
  function modalOn() {}
18554
19057
  function modalOff() {}
18555
19058
  function isMapDefined() {
18556
- return gMainMap != null;
19059
+ return gMainMap !== null && gMainMap !== undefined;
19060
+ }
19061
+ function isMapVisible() {
19062
+ return gMainMap !== null && gMainMap !== undefined && gMainMap.getProjection() !== null && gMainMap.getProjection() !== undefined;
18557
19063
  }
18558
19064
  this.createMap = createMap;
18559
19065
  this.addListener = addListener;
18560
19066
  this.addControl = addControl;
18561
- this.setViewToPosition = setViewToPosition;
19067
+ this.flyToFixedZoom = flyToFixedZoom;
18562
19068
  this.clearAllMarkers = clearAllMarkers;
18563
- this.fromLatLngToPoint = fromLatLngToPoint;
18564
19069
  this.callGeocoder = callGeocoder;
18565
- this.setZoom = setZoom;
19070
+ this.getZoomAdjustedBounds = getZoomAdjustedBounds;
18566
19071
  this.getZoom = getZoom;
18567
19072
  this.createMarker = createMarker;
18568
19073
  this.bindPopup = bindPopup;
@@ -18570,7 +19075,6 @@ function geoCallback( in_geocode_response ) {
18570
19075
  this.contains = contains;
18571
19076
  this.getBounds = getBounds;
18572
19077
  this.invalidateSize = invalidateSize;
18573
- this.zoomOut = zoomOut;
18574
19078
  this.fitBounds = fitBounds;
18575
19079
  this.openMarker = openMarker;
18576
19080
  this.isApiLoaded = isApiLoaded;
@@ -18579,26 +19083,28 @@ function geoCallback( in_geocode_response ) {
18579
19083
  this.addClusterLayer = addClusterLayer;
18580
19084
  this.removeClusterLayer = removeClusterLayer;
18581
19085
  this.clickSearch = clickSearch;
18582
- this.getGeocodeCenter = getGeocodeCenter;
19086
+ this.getGeocodeCenterAndBounds = getGeocodeCenterAndBounds;
19087
+ this.getGeocodingChoices = getGeocodingChoices;
18583
19088
  this.modalOn = modalOn;
18584
19089
  this.modalOff = modalOff;
18585
19090
  this.removeListener = removeListener;
18586
19091
  this.afterInit = afterInit;
18587
19092
  this.isMapDefined = isMapDefined;
19093
+ this.isMapVisible = isMapVisible;
18588
19094
  this.getCorners = getCorners;
18589
19095
  this.getCenter = getCenter;
18590
19096
  this.markSearchPoint = markSearchPoint;
18591
19097
  this.getOpenMarker = getOpenMarker;
19098
+ this.calculateBoundsFromCenterAndZoom = calculateBoundsFromCenterAndZoom;
18592
19099
  }
18593
19100
  MapDelegate.prototype.createMap = null;
18594
19101
  MapDelegate.prototype.addListener = null;
18595
19102
  MapDelegate.prototype.removeListener = null;
18596
19103
  MapDelegate.prototype.addControl = null;
18597
- MapDelegate.prototype.setViewToPosition = null;
19104
+ MapDelegate.prototype.flyToFixedZoom = null;
18598
19105
  MapDelegate.prototype.clearAllMarkers = null;
18599
- MapDelegate.prototype.fromLatLngToPoint = null;
18600
19106
  MapDelegate.prototype.callGeocoder = null;
18601
- MapDelegate.prototype.setZoom = null;
19107
+ MapDelegate.prototype.getZoomAdjustedBounds = null;
18602
19108
  MapDelegate.prototype.getZoom = null;
18603
19109
  MapDelegate.prototype.createMarker = null;
18604
19110
  MapDelegate.prototype.bindPopup = null;
@@ -18606,7 +19112,6 @@ MapDelegate.prototype.addMarkerCallback = null;
18606
19112
  MapDelegate.prototype.contains = null;
18607
19113
  MapDelegate.prototype.getBounds = null;
18608
19114
  MapDelegate.prototype.invalidateSize = null;
18609
- MapDelegate.prototype.zoomOut = null;
18610
19115
  MapDelegate.prototype.fitBounds = null;
18611
19116
  MapDelegate.prototype.isApiLoaded = null;
18612
19117
  MapDelegate.prototype.loadApi = null;
@@ -18615,15 +19120,18 @@ MapDelegate.prototype.createClusterLayer = null;
18615
19120
  MapDelegate.prototype.addClusterLayer = null;
18616
19121
  MapDelegate.prototype.removeClusterLayer = null;
18617
19122
  MapDelegate.prototype.clickSearch = null;
18618
- MapDelegate.prototype.getGeocodeCenter = null;
19123
+ MapDelegate.prototype.getGeocodeCenterAndBounds = null;
19124
+ MapDelegate.prototype.getGeocodingChoices = null;
18619
19125
  MapDelegate.prototype.modalOn = null;
18620
19126
  MapDelegate.prototype.modalOff = null;
18621
19127
  MapDelegate.prototype.afterInit = null;
18622
19128
  MapDelegate.prototype.isMapDefined = null;
19129
+ MapDelegate.prototype,isMapVisible = null;
18623
19130
  MapDelegate.prototype.getCorners = null;
18624
19131
  MapDelegate.prototype.getCenter= null;
18625
19132
  MapDelegate.prototype.markSearchPoint = null;
18626
19133
  MapDelegate.prototype.getOpenMarker = null;
19134
+ MapDelegate.prototype.calculateBoundsFromCenterAndZoom = null;
18627
19135
  var markerClusterer=function(t){"use strict";function e(t,e){var s={};for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&e.indexOf(r)<0&&(s[r]=t[r]);if(null!=t&&"function"==typeof Object.getOwnPropertySymbols){var o=0;for(r=Object.getOwnPropertySymbols(t);o<r.length;o++)e.indexOf(r[o])<0&&Object.prototype.propertyIsEnumerable.call(t,r[o])&&(s[r[o]]=t[r[o]])}return s}class s{static isAdvancedMarkerAvailable(t){return google.maps.marker&&!0===t.getMapCapabilities().isAdvancedMarkersAvailable}static isAdvancedMarker(t){return google.maps.marker&&t instanceof google.maps.marker.AdvancedMarkerElement}static setMap(t,e){this.isAdvancedMarker(t)?t.map=e:t.setMap(e)}static getPosition(t){if(this.isAdvancedMarker(t)){if(t.position){if(t.position instanceof google.maps.LatLng)return t.position;if(t.position.lat&&t.position.lng)return new google.maps.LatLng(t.position.lat,t.position.lng)}return new google.maps.LatLng(null)}return t.getPosition()}static getVisible(t){return!!this.isAdvancedMarker(t)||t.getVisible()}}class r{constructor(t){let{markers:e,position:s}=t;this.markers=e,s&&(s instanceof google.maps.LatLng?this._position=s:this._position=new google.maps.LatLng(s))}get bounds(){if(0===this.markers.length&&!this._position)return;const t=new google.maps.LatLngBounds(this._position,this._position);for(const e of this.markers)t.extend(s.getPosition(e));return t}get position(){return this._position||this.bounds.getCenter()}get count(){return this.markers.filter((t=>s.getVisible(t))).length}push(t){this.markers.push(t)}delete(){this.marker&&(s.setMap(this.marker,null),this.marker=void 0),this.markers.length=0}}const o=(t,e,r,o)=>{const n=i(t.getBounds(),e,o);return r.filter((t=>n.contains(s.getPosition(t))))},i=(t,e,s)=>{const{northEast:r,southWest:o}=h(t,e),i=l({northEast:r,southWest:o},s);return c(i,e)},n=(t,e,s)=>{const r=i(t,e,s),o=r.getNorthEast(),n=r.getSouthWest();return[n.lng(),n.lat(),o.lng(),o.lat()]},a=(t,e)=>{const s=(e.lat-t.lat)*Math.PI/180,r=(e.lng-t.lng)*Math.PI/180,o=Math.sin(s/2),i=Math.sin(r/2),n=o*o+Math.cos(t.lat*Math.PI/180)*Math.cos(e.lat*Math.PI/180)*i*i;return 6371*(2*Math.atan2(Math.sqrt(n),Math.sqrt(1-n)))},h=(t,e)=>({northEast:e.fromLatLngToDivPixel(t.getNorthEast()),southWest:e.fromLatLngToDivPixel(t.getSouthWest())}),l=(t,e)=>{let{northEast:s,southWest:r}=t;return s.x+=e,s.y-=e,r.x-=e,r.y+=e,{northEast:s,southWest:r}},c=(t,e)=>{let{northEast:s,southWest:r}=t;const o=e.fromDivPixelToLatLng(r),i=e.fromDivPixelToLatLng(s);return new google.maps.LatLngBounds(o,i)};class u{constructor(t){let{maxZoom:e=16}=t;this.maxZoom=e}noop(t){let{markers:e}=t;return m(e)}}class p extends u{constructor(t){var{viewportPadding:s=60}=t;super(e(t,["viewportPadding"])),this.viewportPadding=60,this.viewportPadding=s}calculate(t){let{markers:e,map:s,mapCanvasProjection:r}=t;return s.getZoom()>=this.maxZoom?{clusters:this.noop({markers:e}),changed:!1}:{clusters:this.cluster({markers:o(s,r,e,this.viewportPadding),map:s,mapCanvasProjection:r})}}}const m=t=>t.map((t=>new r({position:s.getPosition(t),markers:[t]})));function d(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}var g=function t(e,s){if(e===s)return!0;if(e&&s&&"object"==typeof e&&"object"==typeof s){if(e.constructor!==s.constructor)return!1;var r,o,i;if(Array.isArray(e)){if((r=e.length)!=s.length)return!1;for(o=r;0!=o--;)if(!t(e[o],s[o]))return!1;return!0}if(e.constructor===RegExp)return e.source===s.source&&e.flags===s.flags;if(e.valueOf!==Object.prototype.valueOf)return e.valueOf()===s.valueOf();if(e.toString!==Object.prototype.toString)return e.toString()===s.toString();if((r=(i=Object.keys(e)).length)!==Object.keys(s).length)return!1;for(o=r;0!=o--;)if(!Object.prototype.hasOwnProperty.call(s,i[o]))return!1;for(o=r;0!=o--;){var n=i[o];if(!t(e[n],s[n]))return!1}return!0}return e!=e&&s!=s},f=d(g);const k=[Int8Array,Uint8Array,Uint8ClampedArray,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array];class w{static from(t){if(!(t instanceof ArrayBuffer))throw new Error("Data must be an instance of ArrayBuffer.");const[e,s]=new Uint8Array(t,0,2);if(219!==e)throw new Error("Data does not appear to be in a KDBush format.");const r=s>>4;if(1!==r)throw new Error(`Got v${r} data when expected v1.`);const o=k[15&s];if(!o)throw new Error("Unrecognized array type.");const[i]=new Uint16Array(t,2,1),[n]=new Uint32Array(t,4,1);return new w(n,i,o,t)}constructor(t,e=64,s=Float64Array,r){if(isNaN(t)||t<0)throw new Error(`Unpexpected numItems value: ${t}.`);this.numItems=+t,this.nodeSize=Math.min(Math.max(+e,2),65535),this.ArrayType=s,this.IndexArrayType=t<65536?Uint16Array:Uint32Array;const o=k.indexOf(this.ArrayType),i=2*t*this.ArrayType.BYTES_PER_ELEMENT,n=t*this.IndexArrayType.BYTES_PER_ELEMENT,a=(8-n%8)%8;if(o<0)throw new Error(`Unexpected typed array class: ${s}.`);r&&r instanceof ArrayBuffer?(this.data=r,this.ids=new this.IndexArrayType(this.data,8,t),this.coords=new this.ArrayType(this.data,8+n+a,2*t),this._pos=2*t,this._finished=!0):(this.data=new ArrayBuffer(8+i+n+a),this.ids=new this.IndexArrayType(this.data,8,t),this.coords=new this.ArrayType(this.data,8+n+a,2*t),this._pos=0,this._finished=!1,new Uint8Array(this.data,0,2).set([219,16+o]),new Uint16Array(this.data,2,1)[0]=e,new Uint32Array(this.data,4,1)[0]=t)}add(t,e){const s=this._pos>>1;return this.ids[s]=s,this.coords[this._pos++]=t,this.coords[this._pos++]=e,s}finish(){const t=this._pos>>1;if(t!==this.numItems)throw new Error(`Added ${t} items when expected ${this.numItems}.`);return y(this.ids,this.coords,this.nodeSize,0,this.numItems-1,0),this._finished=!0,this}range(t,e,s,r){if(!this._finished)throw new Error("Data not yet indexed - call index.finish().");const{ids:o,coords:i,nodeSize:n}=this,a=[0,o.length-1,0],h=[];for(;a.length;){const l=a.pop()||0,c=a.pop()||0,u=a.pop()||0;if(c-u<=n){for(let n=u;n<=c;n++){const a=i[2*n],l=i[2*n+1];a>=t&&a<=s&&l>=e&&l<=r&&h.push(o[n])}continue}const p=u+c>>1,m=i[2*p],d=i[2*p+1];m>=t&&m<=s&&d>=e&&d<=r&&h.push(o[p]),(0===l?t<=m:e<=d)&&(a.push(u),a.push(p-1),a.push(1-l)),(0===l?s>=m:r>=d)&&(a.push(p+1),a.push(c),a.push(1-l))}return h}within(t,e,s){if(!this._finished)throw new Error("Data not yet indexed - call index.finish().");const{ids:r,coords:o,nodeSize:i}=this,n=[0,r.length-1,0],a=[],h=s*s;for(;n.length;){const l=n.pop()||0,c=n.pop()||0,u=n.pop()||0;if(c-u<=i){for(let s=u;s<=c;s++)C(o[2*s],o[2*s+1],t,e)<=h&&a.push(r[s]);continue}const p=u+c>>1,m=o[2*p],d=o[2*p+1];C(m,d,t,e)<=h&&a.push(r[p]),(0===l?t-s<=m:e-s<=d)&&(n.push(u),n.push(p-1),n.push(1-l)),(0===l?t+s>=m:e+s>=d)&&(n.push(p+1),n.push(c),n.push(1-l))}return a}}function y(t,e,s,r,o,i){if(o-r<=s)return;const n=r+o>>1;M(t,e,n,r,o,i),y(t,e,s,r,n-1,1-i),y(t,e,s,n+1,o,1-i)}function M(t,e,s,r,o,i){for(;o>r;){if(o-r>600){const n=o-r+1,a=s-r+1,h=Math.log(n),l=.5*Math.exp(2*h/3),c=.5*Math.sqrt(h*l*(n-l)/n)*(a-n/2<0?-1:1);M(t,e,s,Math.max(r,Math.floor(s-a*l/n+c)),Math.min(o,Math.floor(s+(n-a)*l/n+c)),i)}const n=e[2*s+i];let a=r,h=o;for(v(t,e,r,s),e[2*o+i]>n&&v(t,e,r,o);a<h;){for(v(t,e,a,h),a++,h--;e[2*a+i]<n;)a++;for(;e[2*h+i]>n;)h--}e[2*r+i]===n?v(t,e,r,h):(h++,v(t,e,h,o)),h<=s&&(r=h+1),s<=h&&(o=h-1)}}function v(t,e,s,r){x(t,s,r),x(e,2*s,2*r),x(e,2*s+1,2*r+1)}function x(t,e,s){const r=t[e];t[e]=t[s],t[s]=r}function C(t,e,s,r){const o=t-s,i=e-r;return o*o+i*i}const P={minZoom:0,maxZoom:16,minPoints:2,radius:40,extent:512,nodeSize:64,log:!1,generateId:!1,reduce:null,map:t=>t},_=Math.fround||(E=new Float32Array(1),t=>(E[0]=+t,E[0]));var E;const A=3,b=5,L=6;class O{constructor(t){this.options=Object.assign(Object.create(P),t),this.trees=new Array(this.options.maxZoom+1),this.stride=this.options.reduce?7:6,this.clusterProps=[]}load(t){const{log:e,minZoom:s,maxZoom:r}=this.options;e&&console.time("total time");const o=`prepare ${t.length} points`;e&&console.time(o),this.points=t;const i=[];for(let e=0;e<t.length;e++){const s=t[e];if(!s.geometry)continue;const[r,o]=s.geometry.coordinates,n=_(T(r)),a=_(j(o));i.push(n,a,1/0,e,-1,1),this.options.reduce&&i.push(0)}let n=this.trees[r+1]=this._createTree(i);e&&console.timeEnd(o);for(let t=r;t>=s;t--){const s=+Date.now();n=this.trees[t]=this._createTree(this._cluster(n,t)),e&&console.log("z%d: %d clusters in %dms",t,n.numItems,+Date.now()-s)}return e&&console.timeEnd("total time"),this}getClusters(t,e){let s=((t[0]+180)%360+360)%360-180;const r=Math.max(-90,Math.min(90,t[1]));let o=180===t[2]?180:((t[2]+180)%360+360)%360-180;const i=Math.max(-90,Math.min(90,t[3]));if(t[2]-t[0]>=360)s=-180,o=180;else if(s>o){const t=this.getClusters([s,r,180,i],e),n=this.getClusters([-180,r,o,i],e);return t.concat(n)}const n=this.trees[this._limitZoom(e)],a=n.range(T(s),j(i),T(o),j(r)),h=n.data,l=[];for(const t of a){const e=this.stride*t;l.push(h[e+b]>1?Z(h,e,this.clusterProps):this.points[h[e+A]])}return l}getChildren(t){const e=this._getOriginId(t),s=this._getOriginZoom(t),r="No cluster with the specified id.",o=this.trees[s];if(!o)throw new Error(r);const i=o.data;if(e*this.stride>=i.length)throw new Error(r);const n=this.options.radius/(this.options.extent*Math.pow(2,s-1)),a=i[e*this.stride],h=i[e*this.stride+1],l=o.within(a,h,n),c=[];for(const e of l){const s=e*this.stride;i[s+4]===t&&c.push(i[s+b]>1?Z(i,s,this.clusterProps):this.points[i[s+A]])}if(0===c.length)throw new Error(r);return c}getLeaves(t,e,s){e=e||10,s=s||0;const r=[];return this._appendLeaves(r,t,e,s,0),r}getTile(t,e,s){const r=this.trees[this._limitZoom(t)],o=Math.pow(2,t),{extent:i,radius:n}=this.options,a=n/i,h=(s-a)/o,l=(s+1+a)/o,c={features:[]};return this._addTileFeatures(r.range((e-a)/o,h,(e+1+a)/o,l),r.data,e,s,o,c),0===e&&this._addTileFeatures(r.range(1-a/o,h,1,l),r.data,o,s,o,c),e===o-1&&this._addTileFeatures(r.range(0,h,a/o,l),r.data,-1,s,o,c),c.features.length?c:null}getClusterExpansionZoom(t){let e=this._getOriginZoom(t)-1;for(;e<=this.options.maxZoom;){const s=this.getChildren(t);if(e++,1!==s.length)break;t=s[0].properties.cluster_id}return e}_appendLeaves(t,e,s,r,o){const i=this.getChildren(e);for(const e of i){const i=e.properties;if(i&&i.cluster?o+i.point_count<=r?o+=i.point_count:o=this._appendLeaves(t,i.cluster_id,s,r,o):o<r?o++:t.push(e),t.length===s)break}return o}_createTree(t){const e=new w(t.length/this.stride|0,this.options.nodeSize,Float32Array);for(let s=0;s<t.length;s+=this.stride)e.add(t[s],t[s+1]);return e.finish(),e.data=t,e}_addTileFeatures(t,e,s,r,o,i){for(const n of t){const t=n*this.stride,a=e[t+b]>1;let h,l,c;if(a)h=I(e,t,this.clusterProps),l=e[t],c=e[t+1];else{const s=this.points[e[t+A]];h=s.properties;const[r,o]=s.geometry.coordinates;l=T(r),c=j(o)}const u={type:1,geometry:[[Math.round(this.options.extent*(l*o-s)),Math.round(this.options.extent*(c*o-r))]],tags:h};let p;p=a||this.options.generateId?e[t+A]:this.points[e[t+A]].id,void 0!==p&&(u.id=p),i.features.push(u)}}_limitZoom(t){return Math.max(this.options.minZoom,Math.min(Math.floor(+t),this.options.maxZoom+1))}_cluster(t,e){const{radius:s,extent:r,reduce:o,minPoints:i}=this.options,n=s/(r*Math.pow(2,e)),a=t.data,h=[],l=this.stride;for(let s=0;s<a.length;s+=l){if(a[s+2]<=e)continue;a[s+2]=e;const r=a[s],c=a[s+1],u=t.within(a[s],a[s+1],n),p=a[s+b];let m=p;for(const t of u){const s=t*l;a[s+2]>e&&(m+=a[s+b])}if(m>p&&m>=i){let t,i=r*p,n=c*p,d=-1;const g=((s/l|0)<<5)+(e+1)+this.points.length;for(const r of u){const h=r*l;if(a[h+2]<=e)continue;a[h+2]=e;const c=a[h+b];i+=a[h]*c,n+=a[h+1]*c,a[h+4]=g,o&&(t||(t=this._map(a,s,!0),d=this.clusterProps.length,this.clusterProps.push(t)),o(t,this._map(a,h)))}a[s+4]=g,h.push(i/m,n/m,1/0,g,-1,m),o&&h.push(d)}else{for(let t=0;t<l;t++)h.push(a[s+t]);if(m>1)for(const t of u){const s=t*l;if(!(a[s+2]<=e)){a[s+2]=e;for(let t=0;t<l;t++)h.push(a[s+t])}}}}return h}_getOriginId(t){return t-this.points.length>>5}_getOriginZoom(t){return(t-this.points.length)%32}_map(t,e,s){if(t[e+b]>1){const r=this.clusterProps[t[e+L]];return s?Object.assign({},r):r}const r=this.points[t[e+A]].properties,o=this.options.map(r);return s&&o===r?Object.assign({},o):o}}function Z(t,e,s){return{type:"Feature",id:t[e+A],properties:I(t,e,s),geometry:{type:"Point",coordinates:[(r=t[e],360*(r-.5)),S(t[e+1])]}};var r}function I(t,e,s){const r=t[e+b],o=r>=1e4?`${Math.round(r/1e3)}k`:r>=1e3?Math.round(r/100)/10+"k":r,i=t[e+L],n=-1===i?{}:Object.assign({},s[i]);return Object.assign(n,{cluster:!0,cluster_id:t[e+A],point_count:r,point_count_abbreviated:o})}function T(t){return t/360+.5}function j(t){const e=Math.sin(t*Math.PI/180),s=.5-.25*Math.log((1+e)/(1-e))/Math.PI;return s<0?0:s>1?1:s}function S(t){const e=(180-360*t)*Math.PI/180;return 360*Math.atan(Math.exp(e))/Math.PI-90}class z extends u{constructor(t){var{maxZoom:s,radius:r=60}=t,o=e(t,["maxZoom","radius"]);super({maxZoom:s}),this.state={zoom:-1},this.superCluster=new O(Object.assign({maxZoom:this.maxZoom,radius:r},o))}calculate(t){let e=!1;const r={zoom:t.map.getZoom()};if(!f(t.markers,this.markers)){e=!0,this.markers=[...t.markers];const r=this.markers.map((t=>{const e=s.getPosition(t);return{type:"Feature",geometry:{type:"Point",coordinates:[e.lng(),e.lat()]},properties:{marker:t}}}));this.superCluster.load(r)}return e||(this.state.zoom<=this.maxZoom||r.zoom<=this.maxZoom)&&(e=!f(this.state,r)),this.state=r,e&&(this.clusters=this.cluster(t)),{clusters:this.clusters,changed:e}}cluster(t){let{map:e}=t;return this.superCluster.getClusters([-180,-90,180,90],Math.round(e.getZoom())).map((t=>this.transformCluster(t)))}transformCluster(t){let{geometry:{coordinates:[e,o]},properties:i}=t;if(i.cluster)return new r({markers:this.superCluster.getLeaves(i.cluster_id,1/0).map((t=>t.properties.marker)),position:{lat:o,lng:e}});const n=i.marker;return new r({markers:[n],position:s.getPosition(n)})}}class U{constructor(t,e){this.markers={sum:t.length};const s=e.map((t=>t.count)),r=s.reduce(((t,e)=>t+e),0);this.clusters={count:e.length,markers:{mean:r/e.length,sum:r,min:Math.min(...s),max:Math.max(...s)}}}}class B{render(t,e,r){let{count:o,position:i}=t;const n=`<svg fill="${o>Math.max(10,e.clusters.markers.mean)?"#ff0000":"#0000ff"}" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 240" width="50" height="50">\n<circle cx="120" cy="120" opacity=".6" r="70" />\n<circle cx="120" cy="120" opacity=".3" r="90" />\n<circle cx="120" cy="120" opacity=".2" r="110" />\n<text x="50%" y="50%" style="fill:#fff" text-anchor="middle" font-size="50" dominant-baseline="middle" font-family="roboto,arial,sans-serif">${o}</text>\n</svg>`,a=`Cluster of ${o} markers`,h=Number(google.maps.Marker.MAX_ZINDEX)+o;if(s.isAdvancedMarkerAvailable(r)){const t=(new DOMParser).parseFromString(n,"image/svg+xml").documentElement;t.setAttribute("transform","translate(0 25)");const e={map:r,position:i,zIndex:h,title:a,content:t};return new google.maps.marker.AdvancedMarkerElement(e)}const l={position:i,zIndex:h,title:a,icon:{url:`data:image/svg+xml;base64,${btoa(n)}`,anchor:new google.maps.Point(25,25)}};return new google.maps.Marker(l)}}class D{constructor(){!function(t,e){for(let s in e.prototype)t.prototype[s]=e.prototype[s]}(D,google.maps.OverlayView)}}var N;t.MarkerClustererEvents=void 0,(N=t.MarkerClustererEvents||(t.MarkerClustererEvents={})).CLUSTERING_BEGIN="clusteringbegin",N.CLUSTERING_END="clusteringend",N.CLUSTER_CLICK="click";const F=(t,e,s)=>{s.fitBounds(e.bounds)};return t.AbstractAlgorithm=u,t.AbstractViewportAlgorithm=p,t.Cluster=r,t.ClusterStats=U,t.DefaultRenderer=B,t.GridAlgorithm=class extends p{constructor(t){var{maxDistance:s=4e4,gridSize:r=40}=t;super(e(t,["maxDistance","gridSize"])),this.clusters=[],this.state={zoom:-1},this.maxDistance=s,this.gridSize=r}calculate(t){let{markers:e,map:s,mapCanvasProjection:r}=t;const i={zoom:s.getZoom()};let n=!1;return this.state.zoom>=this.maxZoom&&i.zoom>=this.maxZoom||(n=!f(this.state,i)),this.state=i,s.getZoom()>=this.maxZoom?{clusters:this.noop({markers:e}),changed:n}:{clusters:this.cluster({markers:o(s,r,e,this.viewportPadding),map:s,mapCanvasProjection:r})}}cluster(t){let{markers:e,map:s,mapCanvasProjection:r}=t;return this.clusters=[],e.forEach((t=>{this.addToClosestCluster(t,s,r)})),this.clusters}addToClosestCluster(t,e,o){let n=this.maxDistance,h=null;for(let e=0;e<this.clusters.length;e++){const r=this.clusters[e],o=a(r.bounds.getCenter().toJSON(),s.getPosition(t).toJSON());o<n&&(n=o,h=r)}if(h&&i(h.bounds,o,this.gridSize).contains(s.getPosition(t)))h.push(t);else{const e=new r({markers:[t]});this.clusters.push(e)}}},t.MarkerClusterer=class extends D{constructor(t){let{map:e,markers:s=[],algorithmOptions:r={},algorithm:o=new z(r),renderer:i=new B,onClusterClick:n=F}=t;super(),this.markers=[...s],this.clusters=[],this.algorithm=o,this.renderer=i,this.onClusterClick=n,e&&this.setMap(e)}addMarker(t,e){this.markers.includes(t)||(this.markers.push(t),e||this.render())}addMarkers(t,e){t.forEach((t=>{this.addMarker(t,!0)})),e||this.render()}removeMarker(t,e){const r=this.markers.indexOf(t);return-1!==r&&(s.setMap(t,null),this.markers.splice(r,1),e||this.render(),!0)}removeMarkers(t,e){let s=!1;return t.forEach((t=>{s=this.removeMarker(t,!0)||s})),s&&!e&&this.render(),s}clearMarkers(t){this.markers.length=0,t||this.render()}render(){const e=this.getMap();if(e instanceof google.maps.Map&&e.getProjection()){google.maps.event.trigger(this,t.MarkerClustererEvents.CLUSTERING_BEGIN,this);const{clusters:r,changed:o}=this.algorithm.calculate({markers:this.markers,map:e,mapCanvasProjection:this.getProjection()});if(o||null==o){const t=new Set;for(const e of r)1==e.markers.length&&t.add(e.markers[0]);const e=[];for(const r of this.clusters)null!=r.marker&&(1==r.markers.length?t.has(r.marker)||s.setMap(r.marker,null):e.push(r.marker));this.clusters=r,this.renderClusters(),requestAnimationFrame((()=>e.forEach((t=>s.setMap(t,null)))))}google.maps.event.trigger(this,t.MarkerClustererEvents.CLUSTERING_END,this)}}onAdd(){this.idleListener=this.getMap().addListener("idle",this.render.bind(this)),this.render()}onRemove(){google.maps.event.removeListener(this.idleListener),this.reset()}reset(){this.markers.forEach((t=>s.setMap(t,null))),this.clusters.forEach((t=>t.delete())),this.clusters=[]}renderClusters(){const e=new U(this.markers,this.clusters),r=this.getMap();this.clusters.forEach((o=>{1===o.markers.length?o.marker=o.markers[0]:(o.marker=this.renderer.render(o,e,r),o.markers.forEach((t=>s.setMap(t,null))),this.onClusterClick&&o.marker.addListener("click",(e=>{google.maps.event.trigger(this,t.MarkerClustererEvents.CLUSTER_CLICK,o),this.onClusterClick(e,o,r)}))),s.setMap(o.marker,r)}))}},t.MarkerUtils=s,t.NoopAlgorithm=class extends u{constructor(t){super(e(t,[]))}calculate(t){let{markers:e,map:s,mapCanvasProjection:r}=t;return{clusters:this.cluster({markers:e,map:s,mapCanvasProjection:r}),changed:!1}}cluster(t){return this.noop(t)}},t.SuperClusterAlgorithm=z,t.SuperClusterViewportAlgorithm=class extends p{constructor(t){var{maxZoom:s,radius:r=60,viewportPadding:o=60}=t,i=e(t,["maxZoom","radius","viewportPadding"]);super({maxZoom:s,viewportPadding:o}),this.superCluster=new O(Object.assign({maxZoom:this.maxZoom,radius:r},i)),this.state={zoom:-1,view:[0,0,0,0]}}calculate(t){const e={zoom:Math.round(t.map.getZoom()),view:n(t.map.getBounds(),t.mapCanvasProjection,this.viewportPadding)};let r=!f(this.state,e);if(!f(t.markers,this.markers)){r=!0,this.markers=[...t.markers];const e=this.markers.map((t=>{const e=s.getPosition(t);return{type:"Feature",geometry:{type:"Point",coordinates:[e.lng(),e.lat()]},properties:{marker:t}}}));this.superCluster.load(e)}return r&&(this.clusters=this.cluster(t),this.state=e),{clusters:this.clusters,changed:r}}cluster(t){let{map:e,mapCanvasProjection:s}=t;const r={zoom:Math.round(e.getZoom()),view:n(e.getBounds(),s,this.viewportPadding)};return this.superCluster.getClusters(r.view,r.zoom).map((t=>this.transformCluster(t)))}transformCluster(t){let{geometry:{coordinates:[e,o]},properties:i}=t;if(i.cluster)return new r({markers:this.superCluster.getLeaves(i.cluster_id,1/0).map((t=>t.properties.marker)),position:{lat:o,lng:e}});const n=i.marker;return new r({markers:[n],position:s.getPosition(n)})}},t.defaultOnClusterClickHandler=F,t.distanceBetweenPoints=a,t.extendBoundsToPaddedViewport=i,t.extendPixelBounds=l,t.filterMarkersToPaddedViewport=o,t.getPaddedViewport=n,t.noop=m,t.pixelBoundsToLatLngBounds=c,Object.defineProperty(t,"__esModule",{value:!0}),t}({});
18628
19136
  //# sourceMappingURL=index.min.js.map
18629
19137