becomap 1.5.72 → 1.5.73

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/public/index.html CHANGED
@@ -1,4 +1,4 @@
1
- <!doctype html>
1
+ <!DOCTYPE html>
2
2
  <html lang="en">
3
3
 
4
4
  <head>
@@ -22,182 +22,43 @@
22
22
 
23
23
  <body>
24
24
  <div id="mapContainer"></div>
25
+
26
+ <!-- External libs -->
25
27
  <script src="https://unpkg.com/maplibre-gl@4.4.1/dist/maplibre-gl.js"></script>
26
28
  <script src="https://unpkg.com/@turf/turf@7.1.0/turf.min.js"></script>
27
- <script>// ============================================================================
29
+
30
+ <!-- Core Map SDK Integration -->
31
+ <script>
32
+ // ============================================================================
28
33
  // GLOBAL VARIABLES
29
34
  // ============================================================================
30
35
  window._mapView = null;
31
36
  window._site = null;
32
37
  window._eventListeners = new Map();
33
- window._bridgeHealth = {
34
- isConnected: false,
35
- lastHeartbeat: null,
36
- connectionAttempts: 0,
37
- maxRetries: 3
38
- };
39
- window._operationQueue = [];
40
- window._isProcessingQueue = false;
41
- window._appState = 'initializing'; // 'initializing', 'ready', 'error', 'destroyed'
42
38
 
43
39
  // ============================================================================
44
40
  // UTILITY FUNCTIONS
45
41
  // ============================================================================
46
42
 
47
43
  /**
48
- * Checks if native bridge is available
49
- * @returns {boolean} - True if native bridge is available
50
- */
51
- function isNativeBridgeAvailable() {
52
- return !!(window.webkit?.messageHandlers?.jsHandler || window.jsHandler?.postMessage);
53
- }
54
-
55
- /**
56
- * Updates bridge health status
57
- * @param {boolean} isConnected - Connection status
58
- */
59
- function updateBridgeHealth(isConnected) {
60
- window._bridgeHealth.isConnected = isConnected;
61
- window._bridgeHealth.lastHeartbeat = Date.now();
62
-
63
- if (isConnected) {
64
- window._bridgeHealth.connectionAttempts = 0;
65
- processOperationQueue();
66
- }
67
- }
68
-
69
- /**
70
- * Sends messages to native platform with retry mechanism
44
+ * Sends messages to native platform (iOS/Android)
71
45
  * @param {string} type - Message type
72
46
  * @param {any} payload - Message payload
73
- * @param {boolean} skipQueue - Skip queue and send immediately
74
- */
75
- function notifyNative(type, payload = null, skipQueue = false) {
76
- const message = { type, payload, timestamp: Date.now() };
77
-
78
- if (!isNativeBridgeAvailable()) {
79
- console.warn("Native handler not available:", message);
80
-
81
- if (!skipQueue && window._bridgeHealth.connectionAttempts < window._bridgeHealth.maxRetries) {
82
- queueOperation(() => notifyNative(type, payload, true));
83
- return;
84
- }
85
-
86
- // Fallback: store in localStorage for debugging
87
- try {
88
- const failedMessages = JSON.parse(localStorage.getItem('becomap_failed_messages') || '[]');
89
- failedMessages.push(message);
90
- localStorage.setItem('becomap_failed_messages', JSON.stringify(failedMessages.slice(-50))); // Keep last 50
91
- } catch (e) {
92
- console.warn('Failed to store message in localStorage:', e);
93
- }
94
- return;
95
- }
96
-
97
- try {
98
- const messageStr = JSON.stringify(message);
99
-
100
- if (window.webkit?.messageHandlers?.jsHandler) {
101
- // iOS
102
- window.webkit.messageHandlers.jsHandler.postMessage(messageStr);
103
- } else if (window.jsHandler?.postMessage) {
104
- // Android
105
- window.jsHandler.postMessage(messageStr);
106
- }
107
-
108
- updateBridgeHealth(true);
109
- } catch (error) {
110
- console.error('Failed to send message to native:', error, message);
111
- updateBridgeHealth(false);
112
-
113
- if (!skipQueue) {
114
- queueOperation(() => notifyNative(type, payload, true));
115
- }
116
- }
117
- }
118
-
119
- /**
120
- * Queues an operation for later execution
121
- * @param {Function} operation - Operation to queue
122
- */
123
- function queueOperation(operation) {
124
- window._operationQueue.push({
125
- operation,
126
- timestamp: Date.now(),
127
- retries: 0
128
- });
129
-
130
- // Process queue after a short delay
131
- setTimeout(processOperationQueue, 100);
132
- }
133
-
134
- /**
135
- * Processes queued operations
136
47
  */
137
- function processOperationQueue() {
138
- if (window._isProcessingQueue || !isNativeBridgeAvailable()) {
139
- return;
140
- }
141
-
142
- window._isProcessingQueue = true;
143
-
144
- try {
145
- const now = Date.now();
146
- const maxAge = 30000; // 30 seconds
147
-
148
- // Remove expired operations
149
- window._operationQueue = window._operationQueue.filter(item =>
150
- now - item.timestamp < maxAge
151
- );
152
-
153
- // Process remaining operations
154
- while (window._operationQueue.length > 0 && isNativeBridgeAvailable()) {
155
- const item = window._operationQueue.shift();
156
-
157
- try {
158
- item.operation();
159
- } catch (error) {
160
- console.error('Error processing queued operation:', error);
161
-
162
- // Retry failed operations up to 3 times
163
- if (item.retries < 3) {
164
- item.retries++;
165
- window._operationQueue.unshift(item);
166
- break;
167
- }
168
- }
169
- }
170
- } finally {
171
- window._isProcessingQueue = false;
48
+ function notifyNative(type, payload = null) {
49
+ const message = JSON.stringify({ type, payload });
50
+
51
+ if (window.webkit?.messageHandlers?.jsHandler) {
52
+ // iOS
53
+ window.webkit.messageHandlers.jsHandler.postMessage(message);
54
+ } else if (window.jsHandler?.postMessage) {
55
+ // Android
56
+ window.jsHandler.postMessage(message);
57
+ } else {
58
+ console.log("Native handler not available:", message);
172
59
  }
173
60
  }
174
61
 
175
- /**
176
- * Throttle function to prevent event flooding
177
- * @param {Function} func - Function to throttle
178
- * @param {number} delay - Delay in milliseconds
179
- * @returns {Function} - Throttled function
180
- */
181
- function throttle(func, delay) {
182
- let timeoutId;
183
- let lastExecTime = 0;
184
-
185
- return function (...args) {
186
- const currentTime = Date.now();
187
-
188
- if (currentTime - lastExecTime > delay) {
189
- func.apply(this, args);
190
- lastExecTime = currentTime;
191
- } else {
192
- clearTimeout(timeoutId);
193
- timeoutId = setTimeout(() => {
194
- func.apply(this, args);
195
- lastExecTime = Date.now();
196
- }, delay - (currentTime - lastExecTime));
197
- }
198
- };
199
- }
200
-
201
62
  /**
202
63
  * Safely converts objects to JSON with error handling
203
64
  * @param {any} obj - Object to convert
@@ -213,97 +74,20 @@
213
74
  }
214
75
 
215
76
  /**
216
- * Executes function with enhanced error handling, timeout, and retry mechanism
77
+ * Executes function with error handling and native notification
217
78
  * @param {Function} fn - Function to execute
218
79
  * @param {string} errorType - Error type for native notification
219
- * @param {any} errorDetails - Additional error details to include in the payload
220
80
  * @param {any} fallbackValue - Fallback value if function fails
221
- * @param {number} timeout - Timeout in milliseconds (default: 5000)
222
- * @param {number} maxRetries - Maximum retry attempts (default: 0)
223
- */
224
- function executeWithErrorHandling(fn, errorType, errorDetails = null, fallbackValue = null, timeout = 5000, maxRetries = 0) {
225
- return new Promise((resolve) => {
226
- let retryCount = 0;
227
-
228
- function attemptExecution() {
229
- try {
230
- // Validate pre-conditions
231
- if (window._appState === 'destroyed') {
232
- throw new Error('Application has been destroyed');
233
- }
234
-
235
- if (!window._mapView && errorDetails?.operation !== 'init') {
236
- throw new Error('MapView not initialized');
237
- }
238
-
239
- // Set up timeout
240
- const timeoutId = setTimeout(() => {
241
- throw new Error(`Operation timed out after ${timeout}ms`);
242
- }, timeout);
243
-
244
- const result = fn();
245
- clearTimeout(timeoutId);
246
-
247
- // Handle promises
248
- if (result && typeof result.then === 'function') {
249
- result
250
- .then(res => resolve(res !== undefined ? res : fallbackValue))
251
- .catch(handleError);
252
- } else {
253
- resolve(result !== undefined ? result : fallbackValue);
254
- }
255
-
256
- } catch (error) {
257
- handleError(error);
258
- }
259
- }
260
-
261
- function handleError(error) {
262
- console.error(`${errorType} error (attempt ${retryCount + 1}):`, error);
263
-
264
- const errorPayload = {
265
- message: error.message,
266
- stack: error.stack,
267
- timestamp: Date.now(),
268
- attempt: retryCount + 1,
269
- maxRetries: maxRetries + 1,
270
- appState: window._appState,
271
- bridgeHealth: { ...window._bridgeHealth },
272
- ...errorDetails
273
- };
274
-
275
- // Retry logic for transient errors
276
- if (retryCount < maxRetries && isRetriableError(error)) {
277
- retryCount++;
278
- const delay = Math.min(1000 * Math.pow(2, retryCount - 1), 5000); // Exponential backoff
279
- setTimeout(attemptExecution, delay);
280
- return;
281
- }
282
-
283
- notifyNative(errorType, errorPayload);
284
- resolve(fallbackValue);
285
- }
286
-
287
- attemptExecution();
288
- });
289
- }
290
-
291
- /**
292
- * Determines if an error is retriable
293
- * @param {Error} error - The error to check
294
- * @returns {boolean} - True if error is retriable
295
81
  */
296
- function isRetriableError(error) {
297
- const retriableMessages = [
298
- 'network error',
299
- 'timeout',
300
- 'connection failed',
301
- 'temporary failure'
302
- ];
303
-
304
- return retriableMessages.some(msg =>
305
- error.message.toLowerCase().includes(msg)
306
- );
82
+ function executeWithErrorHandling(fn, errorType, fallbackValue = null) {
83
+ try {
84
+ const result = fn();
85
+ return result !== undefined ? result : fallbackValue;
86
+ } catch (error) {
87
+ console.error(`${errorType} error:`, error);
88
+ notifyNative(errorType, { message: error.message });
89
+ return fallbackValue;
90
+ }
307
91
  }
308
92
 
309
93
  // ============================================================================
@@ -325,100 +109,44 @@
325
109
 
326
110
  // Map load event
327
111
  const loadListenerId = mapView.eventsHandler.on('load', () => {
328
- try {
329
- window._appState = 'ready';
330
- notifyNative("onRenderComplete", {
331
- site: safeToJSON(window._site),
332
- timestamp: Date.now(),
333
- appState: window._appState
334
- });
335
- } catch (error) {
336
- console.error('Error in load event handler:', error);
337
- notifyNative("onError", {
338
- message: error.message,
339
- event: 'load',
340
- timestamp: Date.now()
341
- });
342
- }
112
+ notifyNative("mapLoad", { timestamp: Date.now() });
343
113
  });
344
114
 
345
- // View change event (throttled to prevent flooding)
346
- const throttledViewChange = throttle((args) => {
347
- try {
348
- notifyNative("onViewChange", {
349
- viewOptions: safeToJSON(args.viewOptions),
350
- timestamp: Date.now()
351
- });
352
- } catch (error) {
353
- console.error('Error in viewChange event handler:', error);
354
- }
355
- }, 100); // Throttle to max 10 events per second
356
-
357
- const viewChangeListenerId = mapView.eventsHandler.on('viewChange', throttledViewChange);
115
+ // View change event
116
+ const viewChangeListenerId = mapView.eventsHandler.on('viewChange', (args) => {
117
+ notifyNative("viewChange", {
118
+ viewOptions: safeToJSON(args.viewOptions),
119
+ timestamp: Date.now()
120
+ });
121
+ });
358
122
 
359
123
  // Location selection event
360
124
  const selectListenerId = mapView.eventsHandler.on('select', (args) => {
361
- try {
362
- notifyNative("onLocationSelect", {
363
- locations: args.locations?.map(loc => safeToJSON(loc)) || [],
364
- timestamp: Date.now()
365
- });
366
- } catch (error) {
367
- console.error('Error in select event handler:', error);
368
- notifyNative("onError", {
369
- message: error.message,
370
- event: 'select',
371
- timestamp: Date.now()
372
- });
373
- }
125
+ notifyNative("locationSelect", {
126
+ locations: args.locations?.map(loc => safeToJSON(loc)) || [],
127
+ timestamp: Date.now()
128
+ });
374
129
  });
375
130
 
376
131
  // Floor switch event
377
132
  const switchToFloorListenerId = mapView.eventsHandler.on('switchToFloor', (args) => {
378
- try {
379
- notifyNative("onFloorSwitch", {
380
- floor: safeToJSON(args.floor),
381
- timestamp: Date.now()
382
- });
383
- } catch (error) {
384
- console.error('Error in switchToFloor event handler:', error);
385
- notifyNative("onError", {
386
- message: error.message,
387
- event: 'switchToFloor',
388
- timestamp: Date.now()
389
- });
390
- }
133
+ notifyNative("floorSwitch", {
134
+ floor: safeToJSON(args.floor),
135
+ timestamp: Date.now()
136
+ });
391
137
  });
392
138
 
393
139
  // Route step load event
394
140
  const stepLoadListenerId = mapView.eventsHandler.on('stepLoad', (args) => {
395
- try {
396
- notifyNative("onStepLoad", {
397
- step: safeToJSON(args.step),
398
- timestamp: Date.now()
399
- });
400
- } catch (error) {
401
- console.error('Error in stepLoad event handler:', error);
402
- notifyNative("onError", {
403
- message: error.message,
404
- event: 'stepLoad',
405
- timestamp: Date.now()
406
- });
407
- }
141
+ notifyNative("stepLoad", {
142
+ step: safeToJSON(args.step),
143
+ timestamp: Date.now()
144
+ });
408
145
  });
409
146
 
410
147
  // Walkthrough end event
411
148
  const walkthroughEndListenerId = mapView.eventsHandler.on('walkthroughEnd', () => {
412
- try {
413
- notifyNative("onWalkthroughEnd", { timestamp: Date.now() });
414
- } catch (error) {
415
- console.error('Error in walkthroughEnd event handler:', error);
416
- notifyNative("onError", {
417
- message: error.message,
418
- event: 'walkthroughEnd',
419
- timestamp: Date.now()
420
- });
421
- }
149
+ notifyNative("walkthroughEnd", { timestamp: Date.now() });
422
150
  });
423
151
 
424
152
  // Store listener IDs for cleanup
@@ -454,135 +182,36 @@
454
182
  // ============================================================================
455
183
 
456
184
  /**
457
- * Initializes the map with site options and enhanced error handling
185
+ * Initializes the map with site options
458
186
  * @param {Object} siteOptions - Site configuration options
459
187
  */
460
- function init(siteOptions) {
461
- // Validate input parameters
462
- if (!siteOptions || typeof siteOptions !== 'object') {
463
- const error = new Error('Invalid siteOptions provided');
464
- notifyNative("onError", {
465
- message: error.message,
466
- timestamp: Date.now(),
467
- operation: "init",
468
- siteOptions: siteOptions
469
- });
470
- return;
471
- }
472
-
473
- // Check if already initialized
474
- if (window._appState === 'ready' && window._mapView) {
475
- console.warn('Map already initialized');
476
- notifyNative("onRenderComplete", {
477
- site: safeToJSON(window._site),
478
- timestamp: Date.now(),
479
- appState: window._appState
480
- });
481
- return;
482
- }
483
-
484
- window._appState = 'initializing';
485
-
188
+ async function init(siteOptions) {
486
189
  try {
487
190
  const container = document.getElementById('mapContainer');
488
191
  if (!container) {
489
192
  throw new Error('Map container not found');
490
193
  }
491
194
 
492
- // Check if Becomap UMD is loaded with timeout
493
- const checkBecomapLoaded = () => {
494
- return new Promise((resolve, reject) => {
495
- const maxAttempts = 50; // 5 seconds with 100ms intervals
496
- let attempts = 0;
497
-
498
- const checkInterval = setInterval(() => {
499
- attempts++;
500
-
501
- if (window.becomap?.getSite && window.becomap?.getMapView) {
502
- clearInterval(checkInterval);
503
- resolve();
504
- } else if (attempts >= maxAttempts) {
505
- clearInterval(checkInterval);
506
- reject(new Error('Becomap UMD failed to load within timeout'));
507
- }
508
- }, 100);
509
- });
510
- };
511
-
512
- // Initialize with proper error handling and timeouts
513
- checkBecomapLoaded()
514
- .then(() => {
515
- const mapOptions = { zoom: 18.5 };
516
-
517
- // Add timeout to getSite
518
- const getSiteWithTimeout = Promise.race([
519
- window.becomap.getSite(siteOptions),
520
- new Promise((_, reject) =>
521
- setTimeout(() => reject(new Error('getSite timeout')), 10000)
522
- )
523
- ]);
524
-
525
- return getSiteWithTimeout;
526
- })
527
- .then(site => {
528
- if (!site) {
529
- throw new Error('Failed to load site data');
530
- }
531
-
532
- window._site = site;
533
-
534
- // Add timeout to getMapView
535
- const getMapViewWithTimeout = Promise.race([
536
- window.becomap.getMapView(container, site, { zoom: 18.5 }),
537
- new Promise((_, reject) =>
538
- setTimeout(() => reject(new Error('getMapView timeout')), 15000)
539
- )
540
- ]);
541
-
542
- return getMapViewWithTimeout;
543
- })
544
- .then(mapView => {
545
- if (!mapView) {
546
- throw new Error('Failed to create map view');
547
- }
548
-
549
- window._mapView = mapView;
550
-
551
- // Setup event listeners
552
- setupMapViewEventListeners(mapView);
553
-
554
- // Initialize bridge health monitoring
555
- updateBridgeHealth(isNativeBridgeAvailable());
556
-
557
- console.log('Map initialization completed successfully');
558
- })
559
- .catch(err => {
560
- window._appState = 'error';
561
- console.error('Init error:', err);
562
-
563
- notifyNative("onError", {
564
- message: err.message,
565
- stack: err.stack,
566
- timestamp: Date.now(),
567
- operation: "init",
568
- siteOptions,
569
- appState: window._appState,
570
- containerExists: !!document.getElementById('mapContainer'),
571
- becomapLoaded: !!(window.becomap?.getSite && window.becomap?.getMapView)
572
- });
573
- });
195
+ if (!window.becomap?.getSite || !window.becomap?.getMapView) {
196
+ throw new Error('Becomap UMD not loaded');
197
+ }
198
+
199
+ const mapOptions = { zoom: 18.5 };
200
+ window._site = await window.becomap.getSite(siteOptions);
201
+ window._mapView = await window.becomap.getMapView(container, window._site, mapOptions);
574
202
 
575
- } catch (err) {
576
- window._appState = 'error';
577
- console.error('Init synchronous error:', err);
203
+ // Setup event listeners
204
+ setupMapViewEventListeners(window._mapView);
578
205
 
579
- notifyNative("onError", {
206
+ notifyNative("onRenderComplete", {
207
+ site: safeToJSON(window._site),
208
+ timestamp: Date.now()
209
+ });
210
+ } catch (err) {
211
+ console.error('Init error:', err);
212
+ notifyNative("initError", {
580
213
  message: err.message,
581
- stack: err.stack,
582
- timestamp: Date.now(),
583
- operation: "init",
584
- siteOptions,
585
- appState: window._appState
214
+ timestamp: Date.now()
586
215
  });
587
216
  }
588
217
  }
@@ -593,104 +222,68 @@
593
222
 
594
223
  // Floor and Location Methods
595
224
  globalThis.getCurrentFloor = () => {
596
- executeWithErrorHandling(
225
+ const floor = executeWithErrorHandling(
597
226
  () => window._mapView?.currentFloor,
598
- "onError",
599
- { operation: "getCurrentFloor" }
600
- ).then(floor => {
601
- notifyNative("onGetCurrentFloor", safeToJSON(floor));
602
- }).catch(error => {
603
- console.error('Error in getCurrentFloor:', error);
604
- notifyNative("onGetCurrentFloor", null);
605
- });
227
+ "getCurrentFloorError"
228
+ );
229
+ notifyNative("getCurrentFloor", safeToJSON(floor));
606
230
  };
607
231
 
608
232
  globalThis.selectFloorWithId = (floor) => {
609
233
  executeWithErrorHandling(
610
234
  () => window._mapView?.selectFloorWithId(floor),
611
- "onError",
612
- { operation: "selectFloorWithId", floor }
235
+ "selectFloorError"
613
236
  );
614
237
  };
615
238
 
616
239
  globalThis.selectLocationWithId = (location) => {
617
240
  executeWithErrorHandling(
618
241
  () => window._mapView?.selectLocationWithId(location),
619
- "onError",
620
- { operation: "selectLocationWithId", location }
242
+ "selectLocationError"
621
243
  );
622
244
  };
623
245
 
624
246
  // Data Retrieval Methods
625
247
  globalThis.getCategories = () => {
626
- executeWithErrorHandling(
248
+ const categories = executeWithErrorHandling(
627
249
  () => window._mapView?.getCategories(),
628
- "onError",
629
- { operation: "getCategories" },
250
+ "getCategoriesError",
630
251
  []
631
- ).then(categories => {
632
- // Ensure categories is an array before mapping
633
- const categoriesArray = Array.isArray(categories) ? categories : [];
634
- notifyNative("onGetCategories", categoriesArray.map(cat => safeToJSON(cat)));
635
- }).catch(error => {
636
- console.error('Error in getCategories:', error);
637
- notifyNative("onGetCategories", []);
638
- });
252
+ );
253
+ notifyNative("getCategories", categories?.map(cat => safeToJSON(cat)) || []);
639
254
  };
640
255
 
641
256
  globalThis.getLocations = () => {
642
- executeWithErrorHandling(
257
+ const locations = executeWithErrorHandling(
643
258
  () => window._mapView?.getLocations(),
644
- "onError",
645
- { operation: "getLocations" },
259
+ "getLocationsError",
646
260
  []
647
- ).then(locations => {
648
- // Ensure locations is an array before mapping
649
- const locationsArray = Array.isArray(locations) ? locations : [];
650
- notifyNative("onGetLocations", locationsArray.map(loc => safeToJSON(loc)));
651
- }).catch(error => {
652
- console.error('Error in getLocations:', error);
653
- notifyNative("onGetLocations", []);
654
- });
261
+ );
262
+ notifyNative("getLocations", locations?.map(loc => safeToJSON(loc)) || []);
655
263
  };
656
264
 
657
- globalThis.getAmenities = () => {
658
- executeWithErrorHandling(
265
+ globalThis.getAllAmenities = () => {
266
+ const amenities = executeWithErrorHandling(
659
267
  () => window._mapView?.getAllAminityLocations(),
660
- "onError",
661
- { operation: "getAmenities" },
268
+ "getAllAmenitiesError",
662
269
  []
663
- ).then(amenities => {
664
- // Ensure amenities is an array before mapping
665
- const amenitiesArray = Array.isArray(amenities) ? amenities : [];
666
- notifyNative("onGetAmenities", amenitiesArray.map(amenity => safeToJSON(amenity)));
667
- }).catch(error => {
668
- console.error('Error in getAmenities:', error);
669
- notifyNative("onGetAmenities", []);
670
- });
270
+ );
271
+ notifyNative("getAllAmenities", amenities?.map(amenity => safeToJSON(amenity)) || []);
671
272
  };
672
273
 
673
- globalThis.getAmenityTypes = () => {
674
- executeWithErrorHandling(
274
+ globalThis.getAmenities = () => {
275
+ const amenities = executeWithErrorHandling(
675
276
  () => window._mapView?.getAmenities(),
676
- "onError",
677
- { operation: "getAmenityTypes" },
277
+ "getAmenitiesError",
678
278
  []
679
- ).then(amenities => {
680
- // Ensure amenities is an array before mapping
681
- const amenitiesArray = Array.isArray(amenities) ? amenities : [];
682
- notifyNative("onGetAmenityTypes", amenitiesArray.map(amenity => safeToJSON(amenity)));
683
- }).catch(error => {
684
- console.error('Error in getAmenityTypes:', error);
685
- notifyNative("onGetAmenityTypes", []);
686
- });
279
+ );
280
+ notifyNative("getAmenities", amenities?.map(amenity => safeToJSON(amenity)) || []);
687
281
  };
688
282
 
689
283
  globalThis.selectAmenities = (type) => {
690
284
  executeWithErrorHandling(
691
285
  () => window._mapView?.selectAmenities(type),
692
- "onError",
693
- { operation: "selectAmenities", type }
286
+ "selectAmenitiesError"
694
287
  );
695
288
  };
696
289
 
@@ -698,43 +291,41 @@
698
291
  globalThis.getSessionId = async () => {
699
292
  try {
700
293
  const sessionId = await window._mapView?.getSessionId();
701
- notifyNative("onGetSessionId", sessionId);
294
+ notifyNative("getSessionId", sessionId);
702
295
  } catch (err) {
703
- notifyNative("onError", {
296
+ notifyNative("getSessionIdError", {
704
297
  message: err.message,
705
- timestamp: Date.now(),
706
- operation: "getSessionId"
298
+ timestamp: Date.now()
707
299
  });
708
300
  }
709
301
  };
710
302
 
303
+ globalThis.getQuestions = () => {
304
+ const questions = executeWithErrorHandling(
305
+ () => window._mapView?.getQuestions(),
306
+ "getQuestionsError",
307
+ []
308
+ );
309
+ notifyNative("getQuestions", questions?.map(q => safeToJSON(q)) || []);
310
+ };
311
+
711
312
  globalThis.getHappenings = (type) => {
712
- executeWithErrorHandling(
313
+ const happenings = executeWithErrorHandling(
713
314
  () => window._mapView?.getHappenings(type),
714
- "onError",
715
- { operation: "getHappenings", type },
315
+ "getHappeningsError",
716
316
  []
717
- ).then(happenings => {
718
- // Ensure happenings is an array before mapping
719
- const happeningsArray = Array.isArray(happenings) ? happenings : [];
720
- notifyNative("onGetHappenings", happeningsArray.map(h => safeToJSON(h)));
721
- }).catch(error => {
722
- console.error('Error in getHappenings:', error);
723
- notifyNative("onGetHappenings", []);
724
- });
317
+ );
318
+ notifyNative("getHappenings", happenings?.map(h => safeToJSON(h)) || []);
725
319
  };
726
320
 
727
321
  globalThis.getEventSuggestions = async (sessionId, answers) => {
728
322
  try {
729
323
  const suggestions = await window._mapView?.getEventSuggestions(sessionId, answers);
730
- notifyNative("onGetEventSuggestions", suggestions?.map(s => safeToJSON(s)) || []);
324
+ notifyNative("getEventSuggestions", suggestions?.map(s => safeToJSON(s)) || []);
731
325
  } catch (err) {
732
- notifyNative("onError", {
326
+ notifyNative("getEventSuggestionsError", {
733
327
  message: err.message,
734
- timestamp: Date.now(),
735
- operation: "getEventSuggestions",
736
- sessionId,
737
- answers
328
+ timestamp: Date.now()
738
329
  });
739
330
  }
740
331
  };
@@ -743,79 +334,70 @@
743
334
  globalThis.focusTo = (location, zoom, bearing, pitch) => {
744
335
  executeWithErrorHandling(
745
336
  () => window._mapView?.focusTo(location, zoom, bearing, pitch),
746
- "onError",
747
- { operation: "focusTo", location, zoom, bearing, pitch }
337
+ "focusToError"
748
338
  );
749
339
  };
750
340
 
751
341
  globalThis.clearSelection = () => {
752
342
  executeWithErrorHandling(
753
343
  () => window._mapView?.clearSelection(),
754
- "onError",
755
- { operation: "clearSelection" }
344
+ "clearSelectionError"
756
345
  );
757
346
  };
758
347
 
759
348
  globalThis.updateZoom = (zoom) => {
760
349
  executeWithErrorHandling(
761
350
  () => window._mapView?.updateZoom(zoom),
762
- "onError",
763
- { operation: "updateZoom", zoom }
351
+ "updateZoomError"
764
352
  );
765
353
  };
766
354
 
767
355
  globalThis.updatePitch = (pitch) => {
768
356
  executeWithErrorHandling(
769
357
  () => window._mapView?.updatePitch(pitch),
770
- "onError",
771
- { operation: "updatePitch", pitch }
358
+ "updatePitchError"
772
359
  );
773
360
  };
774
361
 
775
362
  globalThis.updateBearing = (bearing) => {
776
363
  executeWithErrorHandling(
777
364
  () => window._mapView?.updateBearing(bearing),
778
- "onError",
779
- { operation: "updateBearing", bearing }
365
+ "updateBearingError"
780
366
  );
781
367
  };
782
368
 
783
369
  globalThis.enableMultiSelection = (val) => {
784
370
  executeWithErrorHandling(
785
371
  () => window._mapView?.enableMultiSelection(val),
786
- "onError",
787
- { operation: "enableMultiSelection", value: val }
372
+ "enableMultiSelectionError"
788
373
  );
789
374
  };
790
375
 
791
376
  globalThis.setBounds = (sw, ne) => {
792
377
  executeWithErrorHandling(
793
378
  () => window._mapView?.setBounds(sw, ne),
794
- "onError",
795
- { operation: "setBounds", southwest: sw, northeast: ne }
379
+ "setBoundsError"
796
380
  );
797
381
  };
798
382
 
799
383
  globalThis.setViewport = (options) => {
800
384
  executeWithErrorHandling(
801
385
  () => window._mapView?.setViewport(options),
802
- "onError",
803
- { operation: "setViewport", options }
386
+ "setViewportError"
804
387
  );
805
388
  };
806
389
 
807
390
  globalThis.resetDefaultViewport = (options) => {
808
391
  executeWithErrorHandling(
809
392
  () => window._mapView?.resetDefaultViewport(options),
810
- "onError",
811
- { operation: "resetDefaultViewport", options }
393
+ "resetDefaultViewportError"
812
394
  );
813
395
  };
814
396
 
815
397
  // Search Methods
816
398
  globalThis.searchForLocations = (q, callbackId) => {
817
399
  if (!window._mapView?.searchForLocations) {
818
- notifyNative("onSearchForLocations", {
400
+ notifyNative("searchForLocations", {
819
401
  callbackId,
820
402
  results: [],
821
403
  error: "Search method not available"
@@ -824,7 +406,7 @@
824
406
  }
825
407
 
826
408
  window._mapView.searchForLocations(q, (matches) => {
827
- notifyNative("onSearchForLocations", {
409
+ notifyNative("searchForLocations", {
828
410
  callbackId,
829
411
  results: matches?.map(m => safeToJSON(m)) || []
830
412
  });
@@ -833,7 +415,7 @@
833
415
 
834
416
  globalThis.searchForCategories = (q, callbackId) => {
835
417
  if (!window._mapView?.searchForCategories) {
836
- notifyNative("onSearchForCategories", {
418
+ notifyNative("searchForCategories", {
837
419
  callbackId,
838
420
  results: [],
839
421
  error: "Search method not available"
@@ -842,7 +424,7 @@
842
424
  }
843
425
 
844
426
  window._mapView.searchForCategories(q, (matches) => {
845
- notifyNative("onSearchForCategories", {
427
+ notifyNative("searchForCategories", {
846
428
  callbackId,
847
429
  results: matches?.map(m => safeToJSON(m)) || []
848
430
  });
@@ -856,53 +438,33 @@
856
438
  globalThis.getRoute = (startID, goalID, waypoints = [], routeOptions) => {
857
439
  try {
858
440
  const routes = window.becomap.getRouteById(startID, goalID, waypoints, routeOptions);
859
- notifyNative("onGetRoute", routes?.map(route => safeToJSON(route)) || []);
441
+ notifyNative("getRoute", routes?.map(route => safeToJSON(route)) || []);
860
442
  } catch (error) {
861
- notifyNative("onError", {
443
+ notifyNative("getRouteError", {
862
444
  message: error.message,
863
- timestamp: Date.now(),
864
- operation: "getRoute",
865
- startID,
866
- goalID,
867
- waypoints,
868
- routeOptions
445
+ timestamp: Date.now()
869
446
  });
870
447
  }
871
448
  };
872
449
 
873
- globalThis.showRoute = (segmentOrderIndex) => {
450
+ globalThis.showRoute = () => {
874
451
  executeWithErrorHandling(
875
- () => {
876
- const routeController = window._mapView?.routeController;
877
- if (!routeController) return;
878
-
879
- if (segmentOrderIndex !== undefined) {
880
- routeController.showSegmentByOrderIndex(segmentOrderIndex);
881
- } else {
882
- const segments = routeController.segments;
883
- if (segments && segments.length > 0) {
884
- routeController.showRoute(segments);
885
- }
886
- }
887
- },
888
- "onError",
889
- { operation: "showRoute", segmentOrderIndex }
452
+ () => window._mapView?.routeController?.showSavedRoute(),
453
+ "showRouteError"
890
454
  );
891
455
  };
892
456
 
893
457
  globalThis.showStep = (step) => {
894
458
  executeWithErrorHandling(
895
459
  () => window._mapView?.routeController?.showStepByOrderIndex(step),
896
- "onError",
897
- { operation: "showStep", step }
460
+ "showStepError"
898
461
  );
899
462
  };
900
463
 
901
464
  globalThis.clearAllRoutes = () => {
902
465
  executeWithErrorHandling(
903
466
  () => window._mapView?.routeController?.clearAllRoutes(),
904
- "onError",
905
- { operation: "clearAllRoutes" }
467
+ "clearAllRoutesError"
906
468
  );
907
469
  };
908
470
 
@@ -911,180 +473,28 @@
911
473
  // ============================================================================
912
474
 
913
475
  /**
914
- * Enhanced cleanup function with proper resource management
476
+ * Cleanup function to be called when the webview is destroyed
915
477
  */
916
478
  globalThis.cleanup = () => {
917
- try {
918
- console.log('Starting cleanup process...');
919
- window._appState = 'destroyed';
920
-
921
- // Clear event listeners
922
- clearMapViewEventListeners();
923
-
924
- // Clear operation queue
925
- window._operationQueue = [];
926
- window._isProcessingQueue = false;
927
-
928
- // Reset bridge health
929
- window._bridgeHealth = {
930
- isConnected: false,
931
- lastHeartbeat: null,
932
- connectionAttempts: 0,
933
- maxRetries: 3
934
- };
935
-
936
- // Cleanup map view
937
- if (window._mapView && typeof window._mapView.destroy === 'function') {
938
- window._mapView.destroy();
939
- }
940
- window._mapView = null;
941
-
942
- // Clear site data
943
- window._site = null;
944
-
945
- // Clear any stored failed messages
946
- try {
947
- localStorage.removeItem('becomap_failed_messages');
948
- } catch (e) {
949
- console.warn('Failed to clear localStorage:', e);
950
- }
951
-
952
- console.log('Cleanup completed successfully');
953
-
954
- // Final notification to native
955
- notifyNative("onCleanupComplete", {
956
- timestamp: Date.now(),
957
- appState: window._appState
958
- }, true); // Skip queue for final message
959
-
960
- } catch (error) {
961
- console.error('Error during cleanup:', error);
962
- notifyNative("onError", {
963
- message: error.message,
964
- operation: "cleanup",
965
- timestamp: Date.now()
966
- }, true);
967
- }
968
- };
969
-
970
- /**
971
- * Application state management
972
- */
973
- globalThis.getAppState = () => {
974
- const state = {
975
- appState: window._appState,
976
- bridgeHealth: { ...window._bridgeHealth },
977
- queueLength: window._operationQueue.length,
978
- hasMapView: !!window._mapView,
979
- hasSite: !!window._site,
980
- timestamp: Date.now()
981
- };
982
-
983
- notifyNative("onGetAppState", state);
984
- return state;
985
- };
986
-
987
- /**
988
- * Health check function for native to verify bridge connectivity
989
- */
990
- globalThis.healthCheck = () => {
991
- const healthData = {
992
- timestamp: Date.now(),
993
- appState: window._appState,
994
- bridgeConnected: isNativeBridgeAvailable(),
995
- mapViewReady: !!window._mapView,
996
- siteLoaded: !!window._site,
997
- queueLength: window._operationQueue.length,
998
- lastHeartbeat: window._bridgeHealth.lastHeartbeat
999
- };
1000
-
1001
- updateBridgeHealth(true); // Update heartbeat
1002
- notifyNative("onHealthCheck", healthData);
1003
-
1004
- return healthData;
1005
- };
1006
-
1007
- /**
1008
- * Error recovery function
1009
- */
1010
- globalThis.recoverFromError = () => {
1011
- try {
1012
- console.log('Attempting error recovery...');
1013
-
1014
- // Reset app state if in error
1015
- if (window._appState === 'error') {
1016
- window._appState = 'initializing';
1017
- }
1018
-
1019
- // Clear failed operations
1020
- window._operationQueue = [];
1021
- window._isProcessingQueue = false;
1022
-
1023
- // Reset bridge health
1024
- updateBridgeHealth(isNativeBridgeAvailable());
1025
-
1026
- // Process any pending operations
1027
- processOperationQueue();
1028
-
1029
- notifyNative("onErrorRecovery", {
1030
- timestamp: Date.now(),
1031
- appState: window._appState,
1032
- bridgeHealth: { ...window._bridgeHealth }
1033
- });
1034
-
1035
- console.log('Error recovery completed');
1036
-
1037
- } catch (error) {
1038
- console.error('Error during recovery:', error);
1039
- notifyNative("onError", {
1040
- message: error.message,
1041
- operation: "recoverFromError",
1042
- timestamp: Date.now()
1043
- });
1044
- }
1045
- };
1046
-
1047
- /**
1048
- * Debug information function
1049
- */
1050
- globalThis.getDebugInfo = () => {
1051
- const debugInfo = {
1052
- timestamp: Date.now(),
1053
- appState: window._appState,
1054
- bridgeHealth: { ...window._bridgeHealth },
1055
- queueLength: window._operationQueue.length,
1056
- hasMapView: !!window._mapView,
1057
- hasSite: !!window._site,
1058
- eventListeners: window._eventListeners.size,
1059
- userAgent: navigator.userAgent,
1060
- url: window.location.href,
1061
- becomapLoaded: !!(window.becomap?.getSite && window.becomap?.getMapView),
1062
- containerExists: !!document.getElementById('mapContainer')
1063
- };
1064
-
1065
- // Get failed messages from localStorage
1066
- try {
1067
- const failedMessages = JSON.parse(localStorage.getItem('becomap_failed_messages') || '[]');
1068
- debugInfo.failedMessagesCount = failedMessages.length;
1069
- debugInfo.lastFailedMessage = failedMessages[failedMessages.length - 1];
1070
- } catch (e) {
1071
- debugInfo.failedMessagesError = e.message;
1072
- }
1073
-
1074
- notifyNative("onGetDebugInfo", debugInfo);
1075
- return debugInfo;
479
+ clearMapViewEventListeners();
480
+ window._mapView = null;
481
+ window._site = null;
1076
482
  };
1077
483
 
1078
- // Initialize bridge health monitoring
1079
- updateBridgeHealth(isNativeBridgeAvailable());
1080
-
1081
- // Set up periodic health checks
1082
- setInterval(() => {
1083
- if (window._appState !== 'destroyed') {
1084
- updateBridgeHealth(isNativeBridgeAvailable());
1085
- }
1086
- }, 5000); // Check every 5 seconds
1087
-
1088
484
  // Export init function
1089
- globalThis.init = init;</script>
1090
- </body>
485
+ globalThis.init = init;
486
+ </script>
487
+
488
+ <!-- <script>
489
+ const siteOptions = {
490
+ clientId: "c079dfa3a77dad13351cfacd95841c2c2780fe08",
491
+ clientSecret: "f62a59675b2a47ddb75f1f994d88e653",
492
+ siteIdentifier: "67dcf5dd2f21c64e3225254f"
493
+ };
494
+ window.addEventListener("DOMContentLoaded", () => {
495
+ init(siteOptions);
496
+ });
497
+ </script> -->
498
+ </body>
499
+
500
+ </html>