diff --git a/WME-Place-Harmonizer.js b/WME-Place-Harmonizer.js index ba474f2..6cb371b 100644 --- a/WME-Place-Harmonizer.js +++ b/WME-Place-Harmonizer.js @@ -1,8 +1,7 @@ -/* eslint-disable nonblock-statement-body-position, brace-style, curly, radix, no-template-curly-in-string */ // ==UserScript== // @name WME Place Harmonizer Beta // @namespace WazeUSA -// @version 2022.08.19.001 +// @version 2022.10.30.001 // @description Harmonizes, formats, and locks a selected place // @author WMEPH Development Group // @include /^https:\/\/(www|beta)\.waze\.com\/(?!user\/)(.{2,6}\/)?editor\/?.*$/ @@ -24,4306 +23,4289 @@ /* global GM_addStyle */ /* global unsafeWindow */ /* global I18n */ -/* global window */ -/* global document */ -/* global localStorage */ -/* global MutationObserver */ -/* global performance */ -/* global atob */ -/* global require */ - -// Script update info - -// BE SURE TO SET THIS TO NULL OR AN EMPTY STRING WHEN RELEASING A NEW UPDATE. -const _SCRIPT_UPDATE_MESSAGE = ''; -const _CSS = ` -#edit-panel .venue-feature-editor { - overflow: initial; -} -#WMEPH_banner .wmeph-btn { - background-color: #fbfbfb; - box-shadow: 0 2px 0 #aaa; - border: solid 1px #bbb; - font-weight:normal; - margin-bottom: 2px; - margin-right:4px -} -.wmeph-btn, .wmephwl-btn { - height: 19px; - font-family: "Boing", sans-serif; -} -.btn.wmeph-btn { - padding: 0px 3px; -} -.btn.wmephwl-btn { - padding: 0px 1px 0px 2px; - height: 18px; - box-shadow: 0 2px 0 #b3b3b3; -} -#WMEPH_banner .banner-row { - padding:2px 4px; -} -#WMEPH_banner .banner-row.red { - color:#b51212; - background-color:#f0dcdc; -} -#WMEPH_banner .banner-row.blue { - color:#3232e6; - background-color:#dcdcf0; -} -#WMEPH_banner .banner-row.yellow { - color:#584a04; - background-color:#f0f0c2; -} -#WMEPH_banner .banner-row.gray { - color:#3a3a3a; - background-color:#eeeeee; -} -#WMEPH_banner .banner-row .dupe { - padding-left:8px; -} -#WMEPH_banner { - background-color:#fff; - color:black; font-size:14px; - padding-top:8px; - padding-bottom:8px; - margin-left:4px; - margin-right:4px; - line-height:18px; - margin-top:2px; - border: solid 1px #8d8c8c; - border-radius: 6px; - margin-bottom: 4px; -} -#WMEPH_banner input[type=text] { - font-size: 13px !important; - height:22px !important; - font-family: "Open Sans", Alef, helvetica, sans-serif !important; -} -#WMEPH_banner div:last-child { - padding-bottom: 3px !important; -} -#WMEPH_runButton { - padding-bottom: 6px; - padding-top: 3px; - width: 290; - color: black; - font-size: 15px; - margin-right: auto; - margin-left: 4px; -} -#WMEPH_tools div { - padding-bottom: 2px !important; -} -.wmeph-fat-btn { - padding-left:8px; - padding-right:8px; - padding-top:4px; - margin-right:3px; - display:inline-block; - font-weight:normal; - height:24px; - font-family: "Boing", sans-serif; -} -.ui-autocomplete { - max-height: 300px; - overflow-y: auto; - overflow-x: hidden; -} -.wmeph-hr { - border-color: #ccc; -} -.wmeph-hr { - border-color: #ccc; -} -.checkmark{ - display: none -}', -.checkmark.draw:after { - animation-duration: .8s; - animation-timing-function: ease; - animation-name: checkmark; - transform: scaleX(-1) rotate(135deg)} -.checkmark:after { - opacity: 1; - height: 2em; - width: 1em; - transform-origin: left top; - border-right: 3px solid #5cb85c; - border-top: 3px solid #5cb85c; - content: ""; - right: 1em; - top: 1em; - position: absolute -} -@keyframes checkmark { - 0% { - height: 0; - width: 0; - opacity: 1 + +(function main() { + 'use strict'; + + // Script update info + + // BE SURE TO SET THIS TO NULL OR AN EMPTY STRING WHEN RELEASING A NEW UPDATE. + const _SCRIPT_UPDATE_MESSAGE = ''; + const _CSS = ` + #edit-panel .venue-feature-editor { + overflow: initial; + } + #WMEPH_banner .wmeph-btn { + background-color: #fbfbfb; + box-shadow: 0 2px 0 #aaa; + border: solid 1px #bbb; + font-weight:normal; + margin-bottom: 2px; + margin-right:4px + } + .wmeph-btn, .wmephwl-btn { + height: 19px; + font-family: "Boing", sans-serif; + } + .btn.wmeph-btn { + padding: 0px 3px; + } + .btn.wmephwl-btn { + padding: 0px 1px 0px 2px; + height: 18px; + box-shadow: 0 2px 0 #b3b3b3; + } + #WMEPH_banner .banner-row { + padding:2px 4px; + } + #WMEPH_banner .banner-row.red { + color:#b51212; + background-color:#f0dcdc; + } + #WMEPH_banner .banner-row.blue { + color:#3232e6; + background-color:#dcdcf0; + } + #WMEPH_banner .banner-row.yellow { + color:#584a04; + background-color:#f0f0c2; + } + #WMEPH_banner .banner-row.gray { + color:#3a3a3a; + background-color:#eeeeee; + } + #WMEPH_banner .banner-row .dupe { + padding-left:8px; + } + #WMEPH_banner { + background-color:#fff; + color:black; font-size:14px; + padding-top:8px; + padding-bottom:8px; + margin-left:4px; + margin-right:4px; + line-height:18px; + margin-top:2px; + border: solid 1px #8d8c8c; + border-radius: 6px; + margin-bottom: 4px; + } + #WMEPH_banner input[type=text] { + font-size: 13px !important; + height:22px !important; + font-family: "Open Sans", Alef, helvetica, sans-serif !important; + } + #WMEPH_banner div:last-child { + padding-bottom: 3px !important; + } + #WMEPH_runButton { + padding-bottom: 6px; + padding-top: 3px; + width: 290; + color: black; + font-size: 15px; + margin-right: auto; + margin-left: 4px; + } + #WMEPH_tools div { + padding-bottom: 2px !important; + } + .wmeph-fat-btn { + padding-left:8px; + padding-right:8px; + padding-top:4px; + margin-right:3px; + display:inline-block; + font-weight:normal; + height:24px; + font-family: "Boing", sans-serif; + } + .ui-autocomplete { + max-height: 300px; + overflow-y: auto; + overflow-x: hidden; } - 20%{ - height: 0; - width: 1em; - opacity: 1 - } - 40%{ - height: 2em; - width: 1em; - opacity: 1 - } - 100%{ + .wmeph-hr { + border-color: #ccc; + } + .wmeph-hr { + border-color: #ccc; + } + .checkmark{ + display: none + }', + .checkmark.draw:after { + animation-duration: .8s; + animation-timing-function: ease; + animation-name: checkmark; + transform: scaleX(-1) rotate(135deg)} + .checkmark:after { + opacity: 1; height: 2em; width: 1em; - opacity: 1 - } -}`; - -let MultiAction; -let UpdateObject; -let UpdateFeatureGeometry; -let UpdateFeatureAddress; -let OpeningHour; - -const _SCRIPT_VERSION = GM_info.script.version.toString(); // pull version from header -const _SCRIPT_NAME = GM_info.script.name; -const _IS_DEV_VERSION = /Beta/i.test(_SCRIPT_NAME); // enables dev messages and unique DOM options if the script is called "... Beta" -const _DEV_VERSION_STR = _IS_DEV_VERSION ? 'Beta' : ''; // strings to differentiate DOM elements between regular and beta script -const _PNH_DATA = { USA: {}, CAN: {} }; -const _CATEGORY_LOOKUP = {}; -const _DEFAULT_HOURS_TEXT = 'Paste Hours Here'; -const _MAX_CACHE_SIZE = 25000; -let _wordVariations; -let _resultsCache = {}; -let _initAlreadyRun = false; // This is used to skip a couple things if already run once. This could probably be handled better... -let _countryCode; -let _textEntryValues = null; // Store the values entered in text boxes so they can be re-added when the banner is reassembled. - -// vars for cat-name checking -let _hospitalPartMatch; -let _hospitalFullMatch; -let _animalPartMatch; -let _animalFullMatch; -let _schoolPartMatch; -let _schoolFullMatch; - -// Userlists -let _wmephDevList; -let _wmephBetaList; - -let _shortcutParse; -let _modifKey = 'Alt+'; - -// Whitelisting vars -let _venueWhitelist; -let _venueWhitelistStr; -let _WLSToMerge; -let _wlKeyName; -const _WL_BUTTON_TEXT = 'WL'; -const _WL_LOCAL_STORE_NAME = 'WMEPH-venueWhitelistNew'; -const _WL_LOCAL_STORE_NAME_COMPRESSED = 'WMEPH-venueWhitelistCompressed'; - -// Dupe check vars -let _dupeLayer; -let _dupeIDList = []; -let _dupeHNRangeList; -let _dupeHNRangeDistList; - -// Web search Window forming: -let _searchResultsWindowSpecs = `"resizable=yes, top=${Math.round(window.screen.height * 0.1)}, left=${ - Math.round(window.screen.width * 0.3)}, width=${Math.round(window.screen.width * 0.7)}, height=${Math.round(window.screen.height * 0.8)}"`; -const _SEARCH_RESULTS_WINDOW_NAME = '"WMEPH Search Results"'; -let _wmephMousePosition; -let _cloneMaster = null; - -// Banner Buttons objects -let _buttonBanner; -let _buttonBanner2; -let _servicesBanner; -let _dupeBanner; - -let _rppLockString = 'Lock?'; -const _PANEL_FIELDS = {}; // the fields for the sidebar -let _disableHighlightTest = false; // Set to true to temporarily disable highlight checks immediately when venues change. -let _wl = {}; -const _USER = { - ref: null, - rank: null, - name: null, - isBetaUser: false, - isDevUser: false -}; -const _SETTING_IDS = { - sfUrlWarning: 'SFURLWarning', // Warning message for first time using localized storefinder URL. - gLinkWarning: 'GLinkWarning' // Warning message for first time using Google search to not to use the Google info itself. -}; -const _URLS = { - forum: 'https://www.waze.com/forum/posting.php?mode=reply&f=819&t=215657', - usaPnh: 'https://docs.google.com/spreadsheets/d/1-f-JTWY5UnBx-rFTa4qhyGMYdHBZWNirUTOgn222zMY/edit#gid=0', - placesWiki: 'https://wazeopedia.waze.com/wiki/USA/Places', - restAreaWiki: 'https://wazeopedia.waze.com/wiki/USA/Rest_areas#Adding_a_Place' -}; -let _userLanguage; -// lock levels are offset by one -const _LOCK_LEVEL_2 = 1; -const _LOCK_LEVEL_4 = 3; -let _defaultLockLevel = _LOCK_LEVEL_2; -let _pnhLockLevel; -const _PM_USER_LIST = { // user names and IDs for PM functions - - SER: { - approvalActive: true, - mods: [ - { id: '16888799', name: 'willdanneriv' }, - // { id: '17083181', name: 'itzwolf' }, - { id: '17077334', name: 'ardan74' } - ] - }, - WMEPH: { - approvalActive: true, - mods: [ - { id: '2647925', name: 'MapOMatic' } - ] - } -}; -let _severityButt = 0; // error tracking to determine banner color (action buttons) -let _duplicateName = ''; -let _catTransWaze2Lang; // pulls the category translations -let _newName; -let _newURL; -let _tempPNHURL = ''; -let _newPhone; -let _newAliases = []; -let _newAliasesTemp = []; -let _newCategories = []; -const _WME_SERVICES_ARRAY = ['VALLET_SERVICE', 'DRIVETHROUGH', 'WI_FI', 'RESTROOMS', 'CREDIT_CARDS', 'RESERVATIONS', 'OUTSIDE_SEATING', - 'AIR_CONDITIONING', 'PARKING_FOR_CUSTOMERS', 'DELIVERIES', 'TAKE_AWAY', 'CURBSIDE_PICKUP', 'WHEELCHAIR_ACCESSIBLE', 'DISABILITY_PARKING']; -const _COLLEGE_ABBREVIATIONS = 'USF|USFSP|UF|UCF|UA|UGA|FSU|UM|SCP|FAU|FIU'; -// Change place.name to title case -const _TITLECASE_SETTINGS = { - ignoreWords: 'an|and|as|at|by|for|from|hhgregg|in|into|of|on|or|the|to|with'.split('|'), - // eslint-disable-next-line max-len - capWords: '3M|AAA|AMC|AOL|AT&T|ATM|BBC|BLT|BMV|BMW|BP|CBS|CCS|CGI|CISCO|CJ|CNG|CNN|CVS|DHL|DKNY|DMV|DSW|EMS|ER|ESPN|FCU|FCUK|FDNY|GNC|H&M|HP|HSBC|IBM|IHOP|IKEA|IRS|JBL|JCPenney|KFC|LLC|MBNA|MCA|MCI|NBC|NYPD|PDQ|PNC|TCBY|TNT|TV|UPS|USA|USPS|VW|XYZ|ZZZ'.split('|'), - specWords: 'd\'Bronx|iFix|ExtraMile'.split('|') -}; -let _newPlaceURL; -let _approveRegionURL; -let _customStoreFinder = false; // switch indicating place-specific custom store finder url -let _customStoreFinderLocal = false; // switch indicating place-specific custom store finder url with localization option (GPS/addr) -let _customStoreFinderURL = ''; // switch indicating place-specific custom store finder url -let _customStoreFinderLocalURL = ''; // switch indicating place-specific custom store finder url with localization option (GPS/addr) -let _updateURL; - -// Split out state-based data -let _psStateIx; -let _psState2LetterIx; -let _psRegionIx; -let _psGoogleFormStateIx; -let _psDefaultLockLevelIx; -// var _ps_requirePhone_ix; -// var _ps_requireURL_ix; -let _psAreaCodeIx; -let _stateDataTemp; -let _areaCodeList = '800,822,833,844,855,866,877,888'; // include toll free non-geographic area codes - -let _ixBank; -let _ixATM; -let _ixOffices; -let _layer; - -const _UPDATED_FIELDS = { - name: { updated: false, selector: '.venue .form-control[name="name"]', tab: 'general' }, - aliases: { updated: false, selector: '.venue .form-control.alias-name', tab: 'general' }, - address: { updated: false, selector: '.venue .address-edit span.full-address', tab: 'general' }, - categories: { updated: false, selector: '.venue .categories.controls .select2-container', tab: 'general' }, - description: { updated: false, selector: '.venue .form-control[name="description"]', tab: 'general' }, - lock: { updated: false, selector: '.venue .form-control.waze-radio-container', tab: 'general' }, - externalProvider: { updated: false, selector: '.venue .external-providers-view', tab: 'general' }, - brand: { updated: false, selector: '.venue .brand .select2-container', tab: 'general' }, - url: { updated: false, selector: '.venue .form-control[name="url"]', tab: 'more-info' }, - phone: { updated: false, selector: '.venue .form-control[name="phone"]', tab: 'more-info' }, - openingHours: { updated: false, selector: '.venue .opening-hours ul', tab: 'more-info' }, - cost: { updated: false, selector: '.venue .form-control[name="costType"]', tab: 'more-info' }, - canExit: { updated: false, selector: '.venue label[for="can-exit-checkbox"]', tab: 'more-info' }, - hasTBR: { updated: false, selector: '.venue label[for="has-tbr"]', tab: 'more-info' }, - lotType: { updated: false, selector: '.venue .parking-type-option', tab: 'more-info' }, - parkingSpots: { updated: false, selector: '.venue .form-control[name="estimatedNumberOfSpots"]', tab: 'more-info' }, - lotElevation: { updated: false, selector: '.venue .lot-checkbox', tab: 'more-info' }, - - getFieldProperties() { - return Object.keys(this) - .filter(key => this[key].hasOwnProperty('updated')) - .map(key => this[key]); - }, - getUpdatedTabNames() { - return _.uniq(this.getFieldProperties() - .filter(prop => prop.updated) - .map(prop => prop.tab)); - }, - // checkAddedNode(addedNode) { - // this.getFieldProperties() - // .filter(prop => prop.updated && addedNode.querySelector(prop.selector)) - // .forEach(prop => { - // $(prop.selector).css({ 'background-color': '#dfd' }); - // $(`a[href="#venue-edit-${prop.tab}"]`).css({ 'background-color': '#dfd' }); - // }); - // }, - reset() { - this.getFieldProperties().forEach(prop => { - prop.updated = false; - }); - }, - init() { - ['VALLET_SERVICE', 'DRIVETHROUGH', 'WI_FI', 'RESTROOMS', 'CREDIT_CARDS', 'RESERVATIONS', 'OUTSIDE_SEATING', 'AIR_CONDITIONING', - 'PARKING_FOR_CUSTOMERS', 'DELIVERIES', 'TAKE_AWAY', 'WHEELCHAIR_ACCESSIBLE', 'DISABILITY_PARKING', 'CURBSIDE_PICKUP', 'CARPOOL_PARKING', - 'EV_CHARGING_STATION', 'CAR_WASH', 'SECURITY', 'AIRPORT_SHUTTLE'] - .forEach(service => { - const propName = `services_${service}`; - this[propName] = { updated: false, selector: `.venue label[for="service-checkbox-${service}"]`, tab: 'more-info' }; + transform-origin: left top; + border-right: 3px solid #5cb85c; + border-top: 3px solid #5cb85c; + content: ""; + right: 1em; + top: 1em; + position: absolute + } + @keyframes checkmark { + 0% { + height: 0; + width: 0; + opacity: 1 + } + 20%{ + height: 0; + width: 1em; + opacity: 1 + } + 40%{ + height: 2em; + width: 1em; + opacity: 1 + } + 100%{ + height: 2em; + width: 1em; + opacity: 1 + } + }`; + + let MultiAction; + let UpdateObject; + let UpdateFeatureGeometry; + let UpdateFeatureAddress; + let OpeningHour; + + const _SCRIPT_VERSION = GM_info.script.version.toString(); // pull version from header + const _SCRIPT_NAME = GM_info.script.name; + const _IS_DEV_VERSION = /Beta/i.test(_SCRIPT_NAME); // enables dev messages and unique DOM options if the script is called "... Beta" + const _DEV_VERSION_STR = _IS_DEV_VERSION ? 'Beta' : ''; // strings to differentiate DOM elements between regular and beta script + const _PNH_DATA = { USA: {}, CAN: {} }; + const _CATEGORY_LOOKUP = {}; + const _DEFAULT_HOURS_TEXT = 'Paste Hours Here'; + const _MAX_CACHE_SIZE = 25000; + let _wordVariations; + let _resultsCache = {}; + let _initAlreadyRun = false; // This is used to skip a couple things if already run once. This could probably be handled better... + let _countryCode; + let _textEntryValues = null; // Store the values entered in text boxes so they can be re-added when the banner is reassembled. + + // vars for cat-name checking + let _hospitalPartMatch; + let _hospitalFullMatch; + let _animalPartMatch; + let _animalFullMatch; + let _schoolPartMatch; + let _schoolFullMatch; + + // Userlists + let _wmephDevList; + let _wmephBetaList; + + let _shortcutParse; + let _modifKey = 'Alt+'; + + // Whitelisting vars + let _venueWhitelist; + let _venueWhitelistStr; + let _WLSToMerge; + let _wlKeyName; + const _WL_BUTTON_TEXT = 'WL'; + const _WL_LOCAL_STORE_NAME = 'WMEPH-venueWhitelistNew'; + const _WL_LOCAL_STORE_NAME_COMPRESSED = 'WMEPH-venueWhitelistCompressed'; + + // Dupe check vars + let _dupeLayer; + let _dupeIDList = []; + let _dupeHNRangeList; + let _dupeHNRangeDistList; + + // Web search Window forming: + let _searchResultsWindowSpecs = `"resizable=yes, top=${Math.round(window.screen.height * 0.1)}, left=${ + Math.round(window.screen.width * 0.3)}, width=${Math.round(window.screen.width * 0.7)}, height=${Math.round(window.screen.height * 0.8)}"`; + const _SEARCH_RESULTS_WINDOW_NAME = '"WMEPH Search Results"'; + let _wmephMousePosition; + let _cloneMaster = null; + + // Banner Buttons objects + let _buttonBanner; + let _buttonBanner2; + let _servicesBanner; + let _dupeBanner; + + let _rppLockString = 'Lock?'; + const _PANEL_FIELDS = {}; // the fields for the sidebar + let _disableHighlightTest = false; // Set to true to temporarily disable highlight checks immediately when venues change. + let _wl = {}; + const _USER = { + ref: null, + rank: null, + name: null, + isBetaUser: false, + isDevUser: false + }; + const _SETTING_IDS = { + sfUrlWarning: 'SFURLWarning', // Warning message for first time using localized storefinder URL. + gLinkWarning: 'GLinkWarning' // Warning message for first time using Google search to not to use the Google info itself. + }; + const _URLS = { + forum: 'https://www.waze.com/forum/posting.php?mode=reply&f=819&t=215657', + usaPnh: 'https://docs.google.com/spreadsheets/d/1-f-JTWY5UnBx-rFTa4qhyGMYdHBZWNirUTOgn222zMY/edit#gid=0', + placesWiki: 'https://wazeopedia.waze.com/wiki/USA/Places', + restAreaWiki: 'https://wazeopedia.waze.com/wiki/USA/Rest_areas#Adding_a_Place' + }; + let _userLanguage; + // lock levels are offset by one + const _LOCK_LEVEL_2 = 1; + const _LOCK_LEVEL_4 = 3; + let _defaultLockLevel = _LOCK_LEVEL_2; + let _pnhLockLevel; + const _PM_USER_LIST = { // user names and IDs for PM functions + + SER: { + approvalActive: true, + mods: [ + { id: '16888799', name: 'willdanneriv' }, + // { id: '17083181', name: 'itzwolf' }, + { id: '17077334', name: 'ardan74' } + ] + }, + WMEPH: { + approvalActive: true, + mods: [ + { id: '2647925', name: 'MapOMatic' } + ] + } + }; + let _severityButt = 0; // error tracking to determine banner color (action buttons) + let _duplicateName = ''; + let _catTransWaze2Lang; // pulls the category translations + let _newName; + let _newURL; + let _tempPNHURL = ''; + let _newPhone; + let _newAliases = []; + let _newAliasesTemp = []; + let _newCategories = []; + const _WME_SERVICES_ARRAY = ['VALLET_SERVICE', 'DRIVETHROUGH', 'WI_FI', 'RESTROOMS', 'CREDIT_CARDS', 'RESERVATIONS', 'OUTSIDE_SEATING', + 'AIR_CONDITIONING', 'PARKING_FOR_CUSTOMERS', 'DELIVERIES', 'TAKE_AWAY', 'CURBSIDE_PICKUP', 'WHEELCHAIR_ACCESSIBLE', 'DISABILITY_PARKING']; + const _COLLEGE_ABBREVIATIONS = 'USF|USFSP|UF|UCF|UA|UGA|FSU|UM|SCP|FAU|FIU'; + // Change place.name to title case + const _TITLECASE_SETTINGS = { + ignoreWords: 'an|and|as|at|by|for|from|hhgregg|in|into|of|on|or|the|to|with'.split('|'), + // eslint-disable-next-line max-len + capWords: '3M|AAA|AMC|AOL|AT&T|ATM|BBC|BLT|BMV|BMW|BP|CBS|CCS|CGI|CISCO|CJ|CNG|CNN|CVS|DHL|DKNY|DMV|DSW|EMS|ER|ESPN|FCU|FCUK|FDNY|GNC|H&M|HP|HSBC|IBM|IHOP|IKEA|IRS|JBL|JCPenney|KFC|LLC|MBNA|MCA|MCI|NBC|NYPD|PDQ|PNC|TCBY|TNT|TV|UPS|USA|USPS|VW|XYZ|ZZZ'.split('|'), + specWords: 'd\'Bronx|iFix|ExtraMile'.split('|') + }; + let _newPlaceURL; + let _approveRegionURL; + let _customStoreFinder = false; // switch indicating place-specific custom store finder url + let _customStoreFinderLocal = false; // switch indicating place-specific custom store finder url with localization option (GPS/addr) + let _customStoreFinderURL = ''; // switch indicating place-specific custom store finder url + let _customStoreFinderLocalURL = ''; // switch indicating place-specific custom store finder url with localization option (GPS/addr) + let _updateURL; + + // Split out state-based data + let _psStateIx; + let _psState2LetterIx; + let _psRegionIx; + let _psGoogleFormStateIx; + let _psDefaultLockLevelIx; + // var _ps_requirePhone_ix; + // var _ps_requireURL_ix; + let _psAreaCodeIx; + let _stateDataTemp; + let _areaCodeList = '800,822,833,844,855,866,877,888'; // include toll free non-geographic area codes + + let _ixBank; + let _ixATM; + let _ixOffices; + let _layer; + + const _UPDATED_FIELDS = { + name: { updated: false, selector: '.venue .form-control[name="name"]', tab: 'general' }, + aliases: { updated: false, selector: '.venue .form-control.alias-name', tab: 'general' }, + address: { updated: false, selector: '.venue .address-edit span.full-address', tab: 'general' }, + categories: { updated: false, selector: '.venue .categories.controls .select2-container', tab: 'general' }, + description: { updated: false, selector: '.venue .form-control[name="description"]', tab: 'general' }, + lock: { updated: false, selector: '.venue .form-control.waze-radio-container', tab: 'general' }, + externalProvider: { updated: false, selector: '.venue .external-providers-view', tab: 'general' }, + brand: { updated: false, selector: '.venue .brand .select2-container', tab: 'general' }, + url: { updated: false, selector: '.venue .form-control[name="url"]', tab: 'more-info' }, + phone: { updated: false, selector: '.venue .form-control[name="phone"]', tab: 'more-info' }, + openingHours: { updated: false, selector: '.venue .opening-hours ul', tab: 'more-info' }, + cost: { updated: false, selector: '.venue .form-control[name="costType"]', tab: 'more-info' }, + canExit: { updated: false, selector: '.venue label[for="can-exit-checkbox"]', tab: 'more-info' }, + hasTBR: { updated: false, selector: '.venue label[for="has-tbr"]', tab: 'more-info' }, + lotType: { updated: false, selector: '.venue .parking-type-option', tab: 'more-info' }, + parkingSpots: { updated: false, selector: '.venue .form-control[name="estimatedNumberOfSpots"]', tab: 'more-info' }, + lotElevation: { updated: false, selector: '.venue .lot-checkbox', tab: 'more-info' }, + + getFieldProperties() { + return Object.keys(this) + .filter(key => this[key].hasOwnProperty('updated')) + .map(key => this[key]); + }, + getUpdatedTabNames() { + return _.uniq(this.getFieldProperties() + .filter(prop => prop.updated) + .map(prop => prop.tab)); + }, + // checkAddedNode(addedNode) { + // this.getFieldProperties() + // .filter(prop => prop.updated && addedNode.querySelector(prop.selector)) + // .forEach(prop => { + // $(prop.selector).css({ 'background-color': '#dfd' }); + // $(`a[href="#venue-edit-${prop.tab}"]`).css({ 'background-color': '#dfd' }); + // }); + // }, + reset() { + this.getFieldProperties().forEach(prop => { + prop.updated = false; }); + }, + init() { + ['VALLET_SERVICE', 'DRIVETHROUGH', 'WI_FI', 'RESTROOMS', 'CREDIT_CARDS', 'RESERVATIONS', 'OUTSIDE_SEATING', 'AIR_CONDITIONING', + 'PARKING_FOR_CUSTOMERS', 'DELIVERIES', 'TAKE_AWAY', 'WHEELCHAIR_ACCESSIBLE', 'DISABILITY_PARKING', 'CURBSIDE_PICKUP', 'CARPOOL_PARKING', + 'EV_CHARGING_STATION', 'CAR_WASH', 'SECURITY', 'AIRPORT_SHUTTLE'] + .forEach(service => { + const propName = `services_${service}`; + this[propName] = { updated: false, selector: `.venue label[for="service-checkbox-${service}"]`, tab: 'more-info' }; + }); - // 5/24/2019 (mapomatic) This observer doesn't seem to work anymore. I've added the updateEditPanelHighlights - // function that can be called after harmonizePlaceGo runs. - - // const observer = new MutationObserver(mutations => { - // mutations.forEach(mutation => { - // // Mutation is a NodeList and doesn't support forEach like an array - // for (let i = 0; i < mutation.addedNodes.length; i++) { - // const addedNode = mutation.addedNodes[i]; - // // Only fire up if it's a node - // if (addedNode.nodeType === Node.ELEMENT_NODE) { - // _UPDATED_FIELDS.checkAddedNode(addedNode); - // } - // } - // }); - // }); - // observer.observe(document.getElementById('edit-panel'), { childList: true, subtree: true }); - - W.selectionManager.events.register('selectionchanged', null, () => errorHandler(() => this.reset())); - }, - updateEditPanelHighlights() { - // Highlight fields in the editor panel that have been updated by WMEPH. - this.getFieldProperties().filter(prop => prop.updated).forEach(prop => { - $(prop.selector).css({ 'background-color': '#dfd' }); - $(`a[href="#venue-edit-${prop.tab}"]`).css({ 'background-color': '#dfd' }); - }); - } -}; - -// KB Shortcut object -const _SHORTCUT = { - all_shortcuts: {}, // All the shortcuts are stored in this array - add(shortcutCombo, callback, opt) { - // Provide a set of default options - const defaultOptions = { - type: 'keydown', propagate: false, disable_in_input: false, target: document, keycode: false - }; - if (!opt) { - opt = defaultOptions; - } else { - Object.keys(defaultOptions).forEach(dfo => { - if (typeof opt[dfo] === 'undefined') { opt[dfo] = defaultOptions[dfo]; } + // 5/24/2019 (mapomatic) This observer doesn't seem to work anymore. I've added the updateEditPanelHighlights + // function that can be called after harmonizePlaceGo runs. + + // const observer = new MutationObserver(mutations => { + // mutations.forEach(mutation => { + // // Mutation is a NodeList and doesn't support forEach like an array + // for (let i = 0; i < mutation.addedNodes.length; i++) { + // const addedNode = mutation.addedNodes[i]; + // // Only fire up if it's a node + // if (addedNode.nodeType === Node.ELEMENT_NODE) { + // _UPDATED_FIELDS.checkAddedNode(addedNode); + // } + // } + // }); + // }); + // observer.observe(document.getElementById('edit-panel'), { childList: true, subtree: true }); + + W.selectionManager.events.register('selectionchanged', null, () => errorHandler(() => this.reset())); + }, + updateEditPanelHighlights() { + // Highlight fields in the editor panel that have been updated by WMEPH. + this.getFieldProperties().filter(prop => prop.updated).forEach(prop => { + $(prop.selector).css({ 'background-color': '#dfd' }); + $(`a[href="#venue-edit-${prop.tab}"]`).css({ 'background-color': '#dfd' }); }); } - let ele = opt.target; - if (typeof opt.target === 'string') { ele = document.getElementById(opt.target); } - // var ths = this; - shortcutCombo = shortcutCombo.toLowerCase(); - // The function to be called at keypress - // eslint-disable-next-line func-names - const func = function keyPressFunc(e) { - e = e || window.event; - if (opt.disable_in_input) { // Don't enable shortcut keys in Input, Textarea fields - let element; - if (e.target) { - element = e.target; - } else if (e.srcElement) { - element = e.srcElement; - } - if (element.nodeType === 3) { element = element.parentNode; } - if (element.tagName === 'INPUT' || element.tagName === 'TEXTAREA') { return; } - } - // Find Which key is pressed - let code; - if (e.keyCode) { - code = e.keyCode; - } else if (e.which) { - code = e.which; - } - let character = String.fromCharCode(code).toLowerCase(); - if (code === 188) { character = ','; } // If the user presses , when the type is onkeydown - if (code === 190) { character = '.'; } // If the user presses , when the type is onkeydown - const keys = shortcutCombo.split('+'); - // Key Pressed - counts the number of valid keypresses - if it is same as the number of keys, the shortcut function is invoked - let kp = 0; - // Work around for stupid Shift key bug created by using lowercase - as a result the shift+num combination was broken - const shiftNums = { - '`': '~', - 1: '!', - 2: '@', - 3: '#', - 4: '$', - 5: '%', - 6: '^', - 7: '&', - 8: '*', - 9: '(', - 0: ')', - '-': '_', - '=': '+', - ';': ':', - '\'': '"', - ',': '<', - '.': '>', - '/': '?', - '\\': '|' - }; - // Special Keys - and their codes - const specialKeys = { - esc: 27, - escape: 27, - tab: 9, - space: 32, - return: 13, - enter: 13, - backspace: 8, - scrolllock: 145, - scroll_lock: 145, - scroll: 145, - capslock: 20, - caps_lock: 20, - caps: 20, - numlock: 144, - num_lock: 144, - num: 144, - pause: 19, - break: 19, - insert: 45, - home: 36, - delete: 46, - end: 35, - pageup: 33, - page_up: 33, - pu: 33, - pagedown: 34, - page_down: 34, - pd: 34, - left: 37, - up: 38, - right: 39, - down: 40, - f1: 112, - f2: 113, - f3: 114, - f4: 115, - f5: 116, - f6: 117, - f7: 118, - f8: 119, - f9: 120, - f10: 121, - f11: 122, - f12: 123 - }; - const modifiers = { - shift: { wanted: false, pressed: false }, - ctrl: { wanted: false, pressed: false }, - alt: { wanted: false, pressed: false }, - meta: { wanted: false, pressed: false } // Meta is Mac specific + }; + + // KB Shortcut object + const _SHORTCUT = { + all_shortcuts: {}, // All the shortcuts are stored in this array + add(shortcutCombo, callback, opt) { + // Provide a set of default options + const defaultOptions = { + type: 'keydown', propagate: false, disable_in_input: false, target: document, keycode: false }; - if (e.ctrlKey) { modifiers.ctrl.pressed = true; } - if (e.shiftKey) { modifiers.shift.pressed = true; } - if (e.altKey) { modifiers.alt.pressed = true; } - if (e.metaKey) { modifiers.meta.pressed = true; } - for (let i = 0; i < keys.length; i++) { - const k = keys[i]; - // Modifiers - if (k === 'ctrl' || k === 'control') { - kp++; - modifiers.ctrl.wanted = true; - } else if (k === 'shift') { - kp++; - modifiers.shift.wanted = true; - } else if (k === 'alt') { - kp++; - modifiers.alt.wanted = true; - } else if (k === 'meta') { - kp++; - modifiers.meta.wanted = true; - } else if (k.length > 1) { // If it is a special key - if (specialKeys[k] === code) { kp++; } - } else if (opt.keycode) { - if (opt.keycode === code) { kp++; } - } else if (character === k) { // The special keys did not match - kp++; - } else if (shiftNums[character] && e.shiftKey) { // Stupid Shift key bug created by using lowercase - character = shiftNums[character]; - if (character === k) { kp++; } - } - } - - if (kp === keys.length && modifiers.ctrl.pressed === modifiers.ctrl.wanted && modifiers.shift.pressed === modifiers.shift.wanted - && modifiers.alt.pressed === modifiers.alt.wanted && modifiers.meta.pressed === modifiers.meta.wanted) { - callback(e); - if (!opt.propagate) { // Stop the event - // e.cancelBubble is supported by IE - this will kill the bubbling process. - e.cancelBubble = true; - e.returnValue = false; - // e.stopPropagation works in Firefox. - if (e.stopPropagation) { - e.stopPropagation(); - e.preventDefault(); + if (!opt) { + opt = defaultOptions; + } else { + Object.keys(defaultOptions).forEach(dfo => { + if (typeof opt[dfo] === 'undefined') { opt[dfo] = defaultOptions[dfo]; } + }); + } + let ele = opt.target; + if (typeof opt.target === 'string') { ele = document.getElementById(opt.target); } + // var ths = this; + shortcutCombo = shortcutCombo.toLowerCase(); + // The function to be called at keypress + // eslint-disable-next-line func-names + const func = function keyPressFunc(e) { + e = e || window.event; + if (opt.disable_in_input) { // Don't enable shortcut keys in Input, Textarea fields + let element; + if (e.target) { + element = e.target; + } else if (e.srcElement) { + element = e.srcElement; + } + if (element.nodeType === 3) { element = element.parentNode; } + if (element.tagName === 'INPUT' || element.tagName === 'TEXTAREA') { return; } + } + // Find Which key is pressed + let code; + if (e.keyCode) { + code = e.keyCode; + } else if (e.which) { + code = e.which; + } + let character = String.fromCharCode(code).toLowerCase(); + if (code === 188) { character = ','; } // If the user presses , when the type is onkeydown + if (code === 190) { character = '.'; } // If the user presses , when the type is onkeydown + const keys = shortcutCombo.split('+'); + // Key Pressed - counts the number of valid keypresses - if it is same as the number of keys, the shortcut function is invoked + let kp = 0; + // Work around for stupid Shift key bug created by using lowercase - as a result the shift+num combination was broken + const shiftNums = { + '`': '~', + 1: '!', + 2: '@', + 3: '#', + 4: '$', + 5: '%', + 6: '^', + 7: '&', + 8: '*', + 9: '(', + 0: ')', + '-': '_', + '=': '+', + ';': ':', + '\'': '"', + ',': '<', + '.': '>', + '/': '?', + '\\': '|' + }; + // Special Keys - and their codes + const specialKeys = { + esc: 27, + escape: 27, + tab: 9, + space: 32, + return: 13, + enter: 13, + backspace: 8, + scrolllock: 145, + scroll_lock: 145, + scroll: 145, + capslock: 20, + caps_lock: 20, + caps: 20, + numlock: 144, + num_lock: 144, + num: 144, + pause: 19, + break: 19, + insert: 45, + home: 36, + delete: 46, + end: 35, + pageup: 33, + page_up: 33, + pu: 33, + pagedown: 34, + page_down: 34, + pd: 34, + left: 37, + up: 38, + right: 39, + down: 40, + f1: 112, + f2: 113, + f3: 114, + f4: 115, + f5: 116, + f6: 117, + f7: 118, + f8: 119, + f9: 120, + f10: 121, + f11: 122, + f12: 123 + }; + const modifiers = { + shift: { wanted: false, pressed: false }, + ctrl: { wanted: false, pressed: false }, + alt: { wanted: false, pressed: false }, + meta: { wanted: false, pressed: false } // Meta is Mac specific + }; + if (e.ctrlKey) { modifiers.ctrl.pressed = true; } + if (e.shiftKey) { modifiers.shift.pressed = true; } + if (e.altKey) { modifiers.alt.pressed = true; } + if (e.metaKey) { modifiers.meta.pressed = true; } + for (let i = 0; i < keys.length; i++) { + const k = keys[i]; + // Modifiers + if (k === 'ctrl' || k === 'control') { + kp++; + modifiers.ctrl.wanted = true; + } else if (k === 'shift') { + kp++; + modifiers.shift.wanted = true; + } else if (k === 'alt') { + kp++; + modifiers.alt.wanted = true; + } else if (k === 'meta') { + kp++; + modifiers.meta.wanted = true; + } else if (k.length > 1) { // If it is a special key + if (specialKeys[k] === code) { kp++; } + } else if (opt.keycode) { + if (opt.keycode === code) { kp++; } + } else if (character === k) { // The special keys did not match + kp++; + } else if (shiftNums[character] && e.shiftKey) { // Stupid Shift key bug created by using lowercase + character = shiftNums[character]; + if (character === k) { kp++; } } + } + + if (kp === keys.length && modifiers.ctrl.pressed === modifiers.ctrl.wanted && modifiers.shift.pressed === modifiers.shift.wanted + && modifiers.alt.pressed === modifiers.alt.wanted && modifiers.meta.pressed === modifiers.meta.wanted) { + callback(e); + if (!opt.propagate) { // Stop the event + // e.cancelBubble is supported by IE - this will kill the bubbling process. + e.cancelBubble = true; + e.returnValue = false; + // e.stopPropagation works in Firefox. + if (e.stopPropagation) { + e.stopPropagation(); + e.preventDefault(); + } - // 5/19/2019 (MapOMatic) Not sure if this return value is necessary. - // eslint-disable-next-line consistent-return - return false; + // 5/19/2019 (MapOMatic) Not sure if this return value is necessary. + // eslint-disable-next-line consistent-return + return false; + } } + }; + this.all_shortcuts[shortcutCombo] = { callback: func, target: ele, event: opt.type }; + // Attach the function with the event + if (ele.addEventListener) { + ele.addEventListener(opt.type, func, false); + } else if (ele.attachEvent) { + ele.attachEvent(`on${opt.type}`, func); + } else { + ele[`on${opt.type}`] = func; } - }; - this.all_shortcuts[shortcutCombo] = { callback: func, target: ele, event: opt.type }; - // Attach the function with the event - if (ele.addEventListener) { - ele.addEventListener(opt.type, func, false); - } else if (ele.attachEvent) { - ele.attachEvent(`on${opt.type}`, func); - } else { - ele[`on${opt.type}`] = func; - } - }, - // Remove the shortcut - just specify the shortcut and I will remove the binding - remove(shortcutCombo) { - shortcutCombo = shortcutCombo.toLowerCase(); - const binding = this.all_shortcuts[shortcutCombo]; - delete (this.all_shortcuts[shortcutCombo]); - if (!binding) { return; } - const type = binding.event; - const ele = binding.target; - const { callback } = binding; - if (ele.detachEvent) { - ele.detachEvent(`on${type}`, callback); - } else if (ele.removeEventListener) { - ele.removeEventListener(type, callback, false); - } else { - ele[`on${type}`] = false; + }, + // Remove the shortcut - just specify the shortcut and I will remove the binding + remove(shortcutCombo) { + shortcutCombo = shortcutCombo.toLowerCase(); + const binding = this.all_shortcuts[shortcutCombo]; + delete (this.all_shortcuts[shortcutCombo]); + if (!binding) { return; } + const type = binding.event; + const ele = binding.target; + const { callback } = binding; + if (ele.detachEvent) { + ele.detachEvent(`on${type}`, callback); + } else if (ele.removeEventListener) { + ele.removeEventListener(type, callback, false); + } else { + ele[`on${type}`] = false; + } + } + }; // END Shortcut function + + function errorHandler(callback) { + try { + callback(); + } catch (ex) { + console.error(`${_SCRIPT_NAME}:`, ex); } } -}; // END Shortcut function -function errorHandler(callback) { - try { - callback(); - } catch (ex) { - console.error(`${_SCRIPT_NAME}:`, ex); + function isNullOrWhitespace(str) { + return !str || !str.trim().length; } -} - -function isNullOrWhitespace(str) { - return !str || !str.trim().length; -} - -function getHoursHtml(label, defaultText) { - defaultText = defaultText || _DEFAULT_HOURS_TEXT; - return $('').append( - `${label}:`, - $('', { - class: 'btn btn-default btn-xs wmeph-btn', - id: 'WMEPH_noHours', - title: 'Add pasted hours to existing', - type: 'button', - value: 'Add hours', - style: 'margin-bottom:4px; margin-right:0px' - }), - $('', { - class: 'btn btn-default btn-xs wmeph-btn', - id: 'WMEPH_noHours_2', - title: 'Replace existing hours with pasted hours', - type: 'button', - value: 'Replace all hours', - style: 'margin-bottom:4px; margin-right:0px' - }), - // jquery throws an error when setting autocomplete="off" in a jquery object (must use .autocomplete() function), so just use a string here. - // eslint-disable-next-line max-len - `