-
Notifications
You must be signed in to change notification settings - Fork 0
Clean up app/map async: resolve missing slider widget issue; Resolve KO Binding conflict #41
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
a996a76
565dc5d
8d8eb63
2f51071
518a5c2
e034b94
d9124e2
fa2d09f
c8599f6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -39,21 +39,41 @@ let postKOBindingCleanup = function() { | |||||||||||||||||||||||
| $('#user-content-notice').css('visibility','unset'); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| // RDH 2022-10-20: Async Issue -- sometimes only part of one theme would load, then upon hitting a call to 'app.map.zoom()' the function | ||||||||||||||||||||||||
| // could not be found and numerous bindings would fail. Delaying applying bindings by a second seems to resolve the issue. Attempts at | ||||||||||||||||||||||||
| // shorter timeouts (~800ms) didn't consistently fix the issue. | ||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||
| * DLP 2025-06-11: Potential solution to replace the window.setTimeout is to use ko.cleanNode(document.body) before ko.applyBindings(app.viewModel) | ||||||||||||||||||||||||
| * i.e., | ||||||||||||||||||||||||
| * ko.cleanNode(document.body); | ||||||||||||||||||||||||
| * ko.applyBindings(app.viewModel); | ||||||||||||||||||||||||
| * postKOBindingCleanup(); | ||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| window.setTimeout(function() { | ||||||||||||||||||||||||
| ko.applyBindings(app.viewModel); | ||||||||||||||||||||||||
| postKOBindingCleanup(); | ||||||||||||||||||||||||
| }, 1000) | ||||||||||||||||||||||||
| // Helper function to wait for element to exist, then activate layers | ||||||||||||||||||||||||
| // replaces clunky setTimeout call; we need app.menus and app.menuModel.menuItems to exist before we can activate layers. | ||||||||||||||||||||||||
| function waitForMenusLoadToApplyKOBindings(maxWaitTime) { | ||||||||||||||||||||||||
| maxWaitTime = maxWaitTime || 20000; // Default 20 second max wait | ||||||||||||||||||||||||
| var startTime = Date.now(); | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| function checkElement() { | ||||||||||||||||||||||||
| if ( | ||||||||||||||||||||||||
| (typeof app !== 'undefined' && app.hasOwnProperty('menus') && typeof app.menus === 'object') | ||||||||||||||||||||||||
| && (app.hasOwnProperty('menuModel') && typeof app.menuModel !== 'undefined' && app.menuModel.hasOwnProperty('menuItems') && typeof app.menuModel.menuItems === 'function') | ||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||
| ko.applyBindings(app.viewModel, document.querySelector('#primary-content')); | ||||||||||||||||||||||||
| } catch (e) { | ||||||||||||||||||||||||
| console.error('Error applying KO bindings:', e); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||
| ko.applyBindings(app.viewModel, document.querySelector('#modal-container')); | ||||||||||||||||||||||||
| } catch (e) { | ||||||||||||||||||||||||
| console.error('Error applying KO bindings:', e); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| postKOBindingCleanup(); | ||||||||||||||||||||||||
| } else if (Date.now() - startTime < maxWaitTime) { | ||||||||||||||||||||||||
| setTimeout(checkElement, 50); // Check every 50ms | ||||||||||||||||||||||||
| return false; | ||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||
| console.warn('app.menus not found after waiting:', maxWaitTime/1000, 'seconds'); | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
| console.warn('app.menus not found after waiting:', maxWaitTime/1000, 'seconds'); | |
| var missingRequirements = []; | |
| if (!(typeof app !== 'undefined' && app.hasOwnProperty('menus') && typeof app.menus === 'object')) { | |
| missingRequirements.push('app.menus'); | |
| } | |
| if (!(typeof app !== 'undefined' && app.hasOwnProperty('menuModel') && typeof app.menuModel !== 'undefined')) { | |
| missingRequirements.push('app.menuModel'); | |
| } else if (!(app.menuModel.hasOwnProperty('menuItems') && typeof app.menuModel.menuItems === 'function')) { | |
| missingRequirements.push('app.menuModel.menuItems'); | |
| } | |
| console.warn('Required KO binding prerequisites not found after waiting:', maxWaitTime/1000, 'seconds. Missing:', missingRequirements.join(', ')); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess Anna's work only removed Knockout from the data layer menu 😢 .
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -105,7 +105,6 @@ app.establishLayerLoadState = function () { | |
| * regardless of what order they come back from the AJAX calls. | ||
| */ | ||
| app.activateHashStateLayers = function() { | ||
| window.setTimeout(function() { | ||
| for (var i = 0; i < app.hashStateLayers.length; i++) { | ||
| var layerStatus = app.hashStateLayers[i].status | ||
| if (layerStatus instanceof layerModel) { | ||
|
|
@@ -121,7 +120,6 @@ app.activateHashStateLayers = function() { | |
| break; | ||
| } | ||
| } | ||
| }, 200); | ||
| } | ||
|
|
||
| app.updateHashStateLayers = function(id, status, visible) { | ||
|
|
@@ -145,8 +143,47 @@ app.updateHashStateLayers = function(id, status, visible) { | |
| }); | ||
| } | ||
|
|
||
| app.activateHashStateLayers(); | ||
| // Wait for app.map.zoom to exist before activating layers, but ensure | ||
| // only one polling loop is active at a time during state restoration. | ||
| var maxWaitTime = 20000; // Default 20 second max wait | ||
| var startTime = Date.now(); | ||
|
|
||
| function isMapZoomReady() { | ||
| return typeof app !== 'undefined' && | ||
| app.hasOwnProperty('map') && | ||
| typeof app.map !== 'undefined' && | ||
| app.map.hasOwnProperty('zoom') && | ||
| typeof app.map.zoom === 'function'; | ||
| } | ||
|
|
||
| if (isMapZoomReady()) { | ||
| app.activateHashStateLayers(); | ||
| return; | ||
| } | ||
|
|
||
| if (app._waitingForMapZoom) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| return; | ||
| } | ||
|
|
||
| app._waitingForMapZoom = true; | ||
|
|
||
| function checkElement() { | ||
| if (isMapZoomReady()) { | ||
| app._waitingForMapZoom = false; | ||
| app._mapZoomWaitTimer = null; | ||
| app.activateHashStateLayers(); | ||
| } else if (Date.now() - startTime < maxWaitTime) { | ||
| app._mapZoomWaitTimer = setTimeout(checkElement, 50); // Check every 50ms | ||
| return false; | ||
| } else { | ||
| app._waitingForMapZoom = false; | ||
| app._mapZoomWaitTimer = null; | ||
| console.warn('map.zoom not found after waiting:', maxWaitTime/1000, 'seconds'); | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| checkElement(); | ||
| } | ||
|
|
||
| app.addKnownLayerFromState = function(id, opacity, isVisible, unloadedDesigns) { | ||
|
|
@@ -258,41 +295,69 @@ app.loadCompressedState = function(state) { | |
| app.establishLayerLoadState(); | ||
| // data tab and open themes | ||
| if (state.themes) { | ||
| //$('#dataTab').tab('show'); | ||
| if (state.themes) { | ||
| $.each(app.viewModel.themes(), function (i, theme) { | ||
| if ( $.inArray(theme.id, state.themes.ids) !== -1 || $.inArray(theme.id.toString(), state.themes.ids) !== -1 ) { | ||
| if ( app.viewModel.openThemes.indexOf(theme) === -1 ) { | ||
| //app.viewModel.openThemes.push(theme); | ||
| theme.setOpenTheme(); | ||
| } | ||
| } else { | ||
| if ( app.viewModel.openThemes.indexOf(theme) !== -1 ) { | ||
| app.viewModel.openThemes.remove(theme); | ||
| } | ||
| $.each(app.viewModel.themes(), function (i, theme) { | ||
| if ( $.inArray(theme.id, state.themes.ids) !== -1 || $.inArray(theme.id.toString(), state.themes.ids) !== -1 ) { | ||
| if ( app.viewModel.openThemes.indexOf(theme) === -1 ) { | ||
| theme.setOpenTheme(); | ||
| } | ||
| }); | ||
| } | ||
| } else { | ||
| if ( app.viewModel.openThemes.indexOf(theme) !== -1 ) { | ||
| app.viewModel.openThemes.remove(theme); | ||
| } | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| //if (app.embeddedMap) { | ||
| if ( $(window).width() < 768 || app.embeddedMap ) { | ||
| state.tab = "data"; | ||
| } | ||
|
|
||
| // active tab -- the following prevents theme and data layers from loading in either tab (not sure why...disbling for now) | ||
| // it appears the dataTab show in state.themes above was causing the problem...? | ||
| // timeout worked, but then realized that removing datatab show from above worked as well... | ||
| // now reinstating the timeout which seems to fix the toggling between tours issue (toggling to ActiveTour while already in ActiveTab) | ||
| // Helper function to wait for element to exist, then show tab | ||
| function waitForElementAndShowTab(selector, maxWaitTime) { | ||
| maxWaitTime = maxWaitTime || 5000; // Default 5 second max wait | ||
| var startTime = Date.now(); | ||
|
|
||
| function checkElement() { | ||
| var element = $(selector); | ||
| if (element.length > 0 && typeof element.tab === 'function') { | ||
| try { | ||
| element.tab('show'); | ||
| return true; | ||
| } catch (e) { | ||
| console.warn('Error showing tab:', selector, e); | ||
| return false; | ||
| } | ||
| } else if (element.length > 0 && typeof element.tab !== 'function') { | ||
| // Element exists but tab functionality not available yet | ||
| if (Date.now() - startTime < maxWaitTime) { | ||
| setTimeout(checkElement, 50); // Check every 50ms | ||
| return false; | ||
| } else { | ||
| console.warn('Tab element found but tab functionality not available:', selector); | ||
| return false; | ||
| } | ||
| } else if (Date.now() - startTime < maxWaitTime) { | ||
| setTimeout(checkElement, 50); // Check every 50ms | ||
| return false; | ||
| } else { | ||
| console.warn('Tab element not found after waiting:', selector); | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| checkElement(); | ||
| } | ||
|
|
||
| // active tab -- wait for element to exist before showing | ||
| if (state.tab && state.tab === "active") { | ||
| //$('#activeTab').tab('show'); | ||
| setTimeout( function() { $('#activeTab').tab('show'); }, 200 ); | ||
| waitForElementAndShowTab('#activeTab'); | ||
| } else if (state.tab && state.tab === "designs") { | ||
| setTimeout( function() { $('#designsTab').tab('show'); }, 200 ); | ||
| waitForElementAndShowTab('#designsTab'); | ||
| } else if (state.tab && state.tab === "legend") { | ||
| setTimeout( function() { $('#legendTab').tab('show'); }, 200 ); | ||
| waitForElementAndShowTab('#legendTab'); | ||
| } else { | ||
| setTimeout( function() { $('#dataTab').tab('show'); }, 200 ); | ||
| waitForElementAndShowTab('#dataTab'); | ||
| } | ||
|
|
||
| if ( state.legends && state.legends === 'true' ) { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ko.applyBindingsis only applied to#primary-content/#modal-container. On pages that include thisapp.jsbut don’t have#primary-content(e.g.visualize/templates/visualize/mobile-map.html),document.querySelector('#primary-content')will benull,ko.applyBindingswill throw, and the catch will prevent any bindings from being applied at all (breaking alldata-bindUI on that page). Suggestion: detect the presence of these containers and (a) skip binding/logging for#modal-containerwhen it’s absent, and (b) fall back to applying bindings on an appropriate root (e.g.document.bodyor a page-specific container) when#primary-contentisn’t present, so non-planner templates keep working.