Follow Us:

About Secured Storage Knob Noster

Secured Storage Knob Noster, climate controlled storage facility, provides storage in Knob Noster, MO. Offering a variety of storage sizes. Five miles from Whiteman AFB Commutes and Destinations Map

Estimate commute time

See travel time and directions for places nearby

'); const destinationContainerEl = destinationPanelEl.list.lastElementChild; destinationContainerEl.addEventListener('click', () => { handleRouteClick(destination, destinationIdx); }); editButtonEl = destinationContainerEl.querySelector('.edit-button'); destinationPanelEl.container.scrollLeft = destinationPanelEl.container.scrollWidth; break; case DestinationOperation.EDIT: const activeDestinationContainerEl = destinationPanelEl.getActiveDestination().parentElement; activeDestinationContainerEl.innerHTML = generateDestinationTemplate(destination); activeDestinationContainerEl.addEventListener('click', () => { handleRouteClick(destination, destinationIdx); }); editButtonEl = activeDestinationContainerEl.querySelector('.edit-button'); break; case DestinationOperation.DELETE: default: } editButtonEl.addEventListener('click', () => { destinationModalEl.title.innerHTML = 'Edit destination'; destinationModalEl.destinationInput.value = destination.name; showElement(destinationModalEl.deleteButton); showElement(destinationModalEl.editButton); hideElement(destinationModalEl.addButton); showModal(); const travelModeId = destination.travelModeEnum.toLowerCase() + '-mode'; document.forms['destination-form'][travelModeId].checked = true; // Update the autocomplete widget as if it was user input. destinationModalEl.destinationInput.dispatchEvent(new Event('input')); }); } /** * Updates view of commutes panel depending on the operation: * - build/update destination template if add or edit. * - remove destination from destination list and rebuild template. */ function updateCommutesPanel( destination, destinationIdx, destinationOperation) { switch (destinationOperation) { case DestinationOperation.ADD: hideElement(commutesEl.initialStatePanel); showElement(commutesEl.destinationPanel); // fall through case DestinationOperation.EDIT: buildDestinationCardTemplate( destination, destinationIdx, destinationOperation); break; case DestinationOperation.DELETE: destinations.splice(destinationIdx, 1); destinationPanelEl.list.innerHTML = ''; for (let i = 0; i < destinations.length; i++) { buildDestinationCardTemplate( destinations[i], i, DestinationOperation.ADD); assignMapObjectListeners(destinations[i], i); } default: } if (!destinations.length) { showElement(commutesEl.initialStatePanel, commutesEl.initialStatePanel); hideElement(commutesEl.destinationPanel); activeDestinationIndex = undefined; return; } destinationPanelEl.container.addEventListener('scroll', handlePanelScroll); destinationPanelEl.container.dispatchEvent(new Event('scroll')); } /** * Adds new destination to the list and get directions and commutes info. */ function addDestinationToList(destinationToAdd, travelModeEnum) { const destinationConfig = createDestinationConfig(destinationToAdd, travelModeEnum); const newDestinationIndex = destinations.length; getDirections(destinationConfig) .then((response) => { if (!response) return; destinations.push(destinationConfig); getCommutesInfo(response, destinationConfig); assignMapObjectListeners(destinationConfig, newDestinationIndex); updateCommutesPanel( destinationConfig, newDestinationIndex, DestinationOperation.ADD); handleRouteClick(destinationConfig, newDestinationIndex); destinationPanelEl.addButton.focus(); }) .catch((e) => console.error('Adding destination failed due to ' + e)); } /** * Returns a new marker label on each call. Marker labels are the capital * letters of the alphabet in order. */ function getNextMarkerLabel() { const markerLabels = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; const label = markerLabels[markerIndex]; markerIndex = (markerIndex + 1) % markerLabels.length; return label; } /** * Creates a destination config object from the given data. The label argument * is optional; a new label will be generated if not provided. */ function createDestinationConfig(destinationToAdd, travelModeEnum, label) { return { name: destinationToAdd.name, place_id: destinationToAdd.place_id, label: label || getNextMarkerLabel(), travelModeEnum: travelModeEnum, url: generateMapsUrl(destinationToAdd, travelModeEnum), }; } /** * Gets directions to destination from origin, add route to map view, and * update commutes panel with distance and directions info. */ function getDirections(destination) { const request = { origin: origin, destination: {'placeId': destination.place_id}, travelMode: destination.travelModeEnum, unitSystem: configuration.distanceMeasurementType === 'METRIC' ? google.maps.UnitSystem.METRIC : google.maps.UnitSystem.IMPERIAL, }; const directionsService = new google.maps.DirectionsService(); return directionsService.route(request).then(response => { return response; }); } /** * Adds route polyline, marker, and commutes info to map and destinations * list. */ function getCommutesInfo(directionResponse, destination) { if (!directionResponse) return; const path = directionResponse.routes[0].overview_path; const bounds = directionResponse.routes[0].bounds; const directionLeg = directionResponse.routes[0].legs[0]; const destinationLocation = directionLeg.end_location; const distance = directionLeg.distance.text; const duration = convertDurationValueAsString(directionLeg.duration.value); const innerStroke = new google.maps.Polyline({ path: path, strokeColor: STROKE_COLORS.inactive.innerStroke, strokeOpacity: 1.0, strokeWeight: 3, zIndex: 10 }); const outerStroke = new google.maps.Polyline({ path: path, strokeColor: STROKE_COLORS.inactive.outerStroke, strokeOpacity: 1.0, strokeWeight: 6, zIndex: 1 }); const marker = createMarker(destinationLocation, destination.label); innerStroke.setMap(commutesMap); outerStroke.setMap(commutesMap); destination.distance = distance; destination.duration = duration; destination.marker = marker; destination.polylines = {innerStroke, outerStroke}; destination.bounds = bounds; } /** * Assigns event target listeners to map objects of corresponding destination * index. */ function assignMapObjectListeners(destination, destinationIdx) { google.maps.event.clearListeners(destination.marker, 'click'); google.maps.event.addListener(destination.marker, 'click', () => { handleRouteClick(destination, destinationIdx); destinationPanelEl.list.querySelectorAll('.destination')[destinationIdx].focus(); }); google.maps.event.addListener(destination.marker, 'mouseover', () => { changeMapObjectStrokeWeight(destination, true); }); google.maps.event.addListener(destination.marker, 'mouseout', () => { changeMapObjectStrokeWeight(destination, false); }); for (const strokeLine in destination.polylines) { google.maps.event.clearListeners(destination.polylines[strokeLine], 'click'); google.maps.event.clearListeners(destination.polylines[strokeLine], 'mouseover'); google.maps.event.addListener(destination.polylines[strokeLine], 'click', () => { handleRouteClick(destination, destinationIdx); destinationPanelEl.list.querySelectorAll('.destination')[destinationIdx].focus(); }); google.maps.event.addListener(destination.polylines[strokeLine], 'mouseover', () => { changeMapObjectStrokeWeight(destination, true); }); google.maps.event.addListener(destination.polylines[strokeLine], 'mouseout', () => { changeMapObjectStrokeWeight(destination, false); }); } } /** * Generates the Google Map url for direction from origin to destination with * corresponding travel mode. */ function generateMapsUrl(destination, travelModeEnum) { let googleMapsUrl = 'https://www.google.com/maps/dir/?api=1'; googleMapsUrl += `&origin=${origin.lat},${origin.lng}`; googleMapsUrl += '&destination=' + encodeURIComponent(destination.name) + '&destination_place_id=' + destination.place_id; googleMapsUrl += '&travelmode=' + travelModeEnum.toLowerCase(); return googleMapsUrl; } /** * Handles changes to destination polyline and map icon stroke weight. */ function changeMapObjectStrokeWeight(destination, mouseOver) { const destinationMarkerIcon = destination.marker.icon; if (mouseOver) { destination.polylines.outerStroke.setOptions({strokeWeight: 8}); destinationMarkerIcon.strokeWeight = 2; destination.marker.setIcon(destinationMarkerIcon); } else { destination.polylines.outerStroke.setOptions({strokeWeight: 6}); destinationMarkerIcon.strokeWeight = 1; destination.marker.setIcon(destinationMarkerIcon); } } /** * Handles route clicks. Originally active routes are set to inactive * states. Newly selected route's map polyline/marker objects and destination * template are assigned active class styling and coloring. */ function handleRouteClick(destination, destinationIdx) { if (activeDestinationIndex !== undefined) { // Set currently active stroke to inactive destinations[activeDestinationIndex].polylines.innerStroke.setOptions( {strokeColor: STROKE_COLORS.inactive.innerStroke, zIndex: 2}); destinations[activeDestinationIndex].polylines.outerStroke.setOptions( {strokeColor: STROKE_COLORS.inactive.outerStroke, zIndex: 1}); // Set current active marker to grey destinations[activeDestinationIndex].marker.setIcon( destinationMarkerIcon); destinations[activeDestinationIndex].marker.label.color = MARKER_ICON_COLORS.inactive.label; // Remove styling of current active destination. const activeDestinationEl = destinationPanelEl.getActiveDestination(); if (activeDestinationEl) { activeDestinationEl.classList.remove('active'); } } activeDestinationIndex = destinationIdx; setTravelModeLayer(destination.travelModeEnum); // Add active class const newDestinationEl = destinationPanelEl.list.querySelectorAll( '.destination')[destinationIdx]; newDestinationEl.classList.add('active'); // Scroll into view newDestinationEl.scrollIntoView({behavior: 'smooth', block: 'center'}); // Make line active destination.polylines.innerStroke.setOptions( {strokeColor: STROKE_COLORS.active.innerStroke, zIndex: 101}); destination.polylines.outerStroke.setOptions( {strokeColor: STROKE_COLORS.active.outerStroke, zIndex: 99}); destination.marker.setIcon(originMarkerIcon); destination.marker.label.color = '#ffffff'; commutesMap.fitBounds(destination.bounds); } /** * Generates new marker based on location and label. */ function createMarker(location, label) { const isOrigin = label === undefined ? true : false; const markerIconConfig = isOrigin ? originMarkerIcon : destinationMarkerIcon; const labelColor = isOrigin ? MARKER_ICON_COLORS.active.label : MARKER_ICON_COLORS.inactive.label; const labelText = isOrigin ? '●' : label; const mapOptions = { position: location, map: commutesMap, label: { text: labelText, fontFamily: 'Arial, sans-serif', color: labelColor, fontSize: '16px', }, icon: markerIconConfig }; if (isOrigin) { mapOptions.label.className += ' origin-pin-label'; mapOptions.label.fontSize = '20px'; } const marker = new google.maps.Marker(mapOptions); return marker; } /** * Returns a TravelMode enum parsed from the input string, or null if no match is found. */ function parseTravelModeEnum(travelModeString) { switch (travelModeString) { case 'DRIVING': return TravelMode.DRIVING; case 'BICYCLING': return TravelMode.BICYCLING; case 'PUBLIC_TRANSIT': return TravelMode.TRANSIT; case 'WALKING': return TravelMode.WALKING; default: return null; } } /** * Sets map layer depending on the chosen travel mode. */ function setTravelModeLayer(travelModeEnum) { switch (travelModeEnum) { case TravelMode.BICYCLING: publicTransitLayer.setMap(null); bikeLayer.setMap(commutesMap); break; case TravelMode.TRANSIT: bikeLayer.setMap(null); publicTransitLayer.setMap(commutesMap); break; default: publicTransitLayer.setMap(null); bikeLayer.setMap(null); } } /** * Convert time from durationValue in seconds into readable string text. */ function convertDurationValueAsString(durationValue) { if (!durationValue) { return ''; } if (durationValue < MIN_IN_SECONDS) { return '<1 min'; } if (durationValue > HOUR_IN_SECONDS * 10) { return '10+ hours'; } const hours = Math.floor(durationValue / HOUR_IN_SECONDS); const minutes = Math.floor(durationValue % HOUR_IN_SECONDS / 60); const hoursString = hours > 0 ? hours + ' h' : ''; const minutesString = minutes > 0 ? minutes + ' min' : ''; const spacer = hoursString && minutesString ? ' ' : ''; return hoursString + spacer + minutesString; } /** * Shows the destination modal window, saving a reference to the currently * focused element so that focus can be restored by hideModal(). */ function showModal() { lastActiveEl = document.activeElement; showElement(commutesEl.modal, destinationModalEl.destinationInput); } /** * Hides the destination modal window, setting focus to focusEl if provided. * If no argument is passed, focus is restored to where it was when * showModal() was called. */ function hideModal(focusEl) { hideElement(commutesEl.modal, focusEl || lastActiveEl); } } /** * Hides a DOM element and optionally focuses on focusEl. */ function hideElement(el, focusEl) { el.style.display = 'none'; if (focusEl) focusEl.focus(); } /** * Shows a DOM element that has been hidden and optionally focuses on focusEl. */ function showElement(el, focusEl) { el.style.display = 'flex'; if (focusEl) focusEl.focus(); } /** * Event handler function for scroll buttons. */ function handleScrollButtonClick(e) { const multiplier = 1.25; const direction = e.target.dataset.direction; const cardWidth = destinationPanelEl.list.firstElementChild.offsetWidth; destinationPanelEl.container.scrollBy( {left: (direction * cardWidth * multiplier), behavior: 'smooth'}); } /** * Event handler on scroll to add scroll buttons only if scroll width is larger * than width. Hide scroll buttons if scrolled to the start or end of the panel. */ function handlePanelScroll() { const position = destinationPanelEl.container.scrollLeft; const scrollWidth = destinationPanelEl.container.scrollWidth; const width = destinationPanelEl.container.offsetWidth; if (scrollWidth > width) { if (position === 0) { destinationPanelEl.scrollLeftButton.classList.remove('visible'); } else { destinationPanelEl.scrollLeftButton.classList.add('visible'); } if (Math.ceil(position + width) >= scrollWidth) { destinationPanelEl.scrollRightButton.classList.remove('visible'); } else { destinationPanelEl.scrollRightButton.classList.add('visible'); } } } /** * Generates new destination template based on destination info properties. */ function generateDestinationTemplate(destination) { const travelModeIconTemplate = ''; return `
To ${destination.name}
${destination.duration}
`; }

Location / Contact

Get a Free Self-storage Facility Quote

Protect What Matters with Secured Storage Knob Noster

Looking for trusted Self-storage Facility services in Knob Noster? Secured Storage Knob Noster is your local expert for alarms, surveillance, access control, and more. Whether you're securing your home or business, get help from pros who know how to keep you safe. Fast. Simple. No pressure.

Expert Security Tips & Insights

Discover guides and advice from industry pros
to help you secure your home, business, and peace of mind.