您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Find permanent hazards
/// <reference path="../typescript-typings/globals/openlayers/index.d.ts" /> /// <reference path="../typescript-typings/I18n.d.ts" /> /// <reference path="../typescript-typings/waze.d.ts" /> /// <reference path="../typescript-typings/globals/jquery/index.d.ts" /> /// <reference path="WME Wide-Angle Lens.user.ts" /> /// <reference path="../typescript-typings/greasyfork.d.ts" /> // ==UserScript== // @name WME Wide-Angle Lens Hazards // @namespace https://greasyfork.org/en/users/19861-vtpearce // @description Find permanent hazards // @author vtpearce and crazycaveman // @match https://*.waze.com/*editor* // @exclude https://*.waze.com/user/editor* // @exclude https://www.waze.com/discuss/* // @version 2025.04.10.002 // @grant GM_xmlhttpRequest // @copyright 2020 vtpearce // @license CC BY-SA 4.0 // @require https://greasyfork.org/scripts/24851-wazewrap/code/WazeWrap.js // @connect greasyfork.org // ==/UserScript== /*global W, OL, I18n, $, WazeWrap, WMEWAL, OpenLayers */ var WMEWAL_Hazards; (function (WMEWAL_Hazards) { const SCRIPT_NAME = GM_info.script.name; const SCRIPT_VERSION = GM_info.script.version.toString(); const DOWNLOAD_URL = GM_info.script.downloadURL; const updateText = '<ul>' + '<li>New plugin.</li>' + '</ul>'; const greasyForkPage = 'https://greasyfork.org/scripts/40645'; const wazeForumThread = 'https://www.waze.com/forum/viewtopic.php?t=206376'; const ctlPrefix = "_wmewalHazards"; const minimumWALVersionRequired = "2025.04.10.001"; let Operation; (function (Operation) { Operation[Operation["Equal"] = 1] = "Equal"; Operation[Operation["NotEqual"] = 2] = "NotEqual"; Operation[Operation["LessThan"] = 3] = "LessThan"; Operation[Operation["LessThanOrEqual"] = 4] = "LessThanOrEqual"; Operation[Operation["GreaterThan"] = 5] = "GreaterThan"; Operation[Operation["GreaterThanOrEqual"] = 6] = "GreaterThanOrEqual"; })(Operation || (Operation = {})); let Issue; (function (Issue) { Issue[Issue["MissingHouseNumber"] = 1] = "MissingHouseNumber"; Issue[Issue["MissingStreet"] = 2] = "MissingStreet"; Issue[Issue["AdLocked"] = 4] = "AdLocked"; Issue[Issue["HasUpdateRequests"] = 8] = "HasUpdateRequests"; Issue[Issue["PendingApproval"] = 16] = "PendingApproval"; Issue[Issue["UndefStreet"] = 32] = "UndefStreet"; Issue[Issue["NoExternalProviders"] = 64] = "NoExternalProviders"; Issue[Issue["NoHours"] = 128] = "NoHours"; Issue[Issue["NoEntryExitPoints"] = 256] = "NoEntryExitPoints"; Issue[Issue["MissingBrand"] = 512] = "MissingBrand"; Issue[Issue["NoPhoneNumber"] = 1024] = "NoPhoneNumber"; Issue[Issue["BadPhoneNumberFormat"] = 2048] = "BadPhoneNumberFormat"; Issue[Issue["NoWebsite"] = 4096] = "NoWebsite"; Issue[Issue["NoCity"] = 8192] = "NoCity"; Issue[Issue["NoName"] = 16384] = "NoName"; })(Issue || (Issue = {})); let HazardType; (function (HazardType) { HazardType[HazardType["Unknown"] = 0] = "Unknown"; HazardType[HazardType["Speedbump"] = 1] = "Speedbump"; HazardType[HazardType["Topes"] = 2] = "Topes"; HazardType[HazardType["Tollbooth"] = 4] = "Tollbooth"; HazardType[HazardType["SharpCurve"] = 8] = "SharpCurve"; HazardType[HazardType["ComplexIntersection"] = 16] = "ComplexIntersection"; HazardType[HazardType["ForkinRoad"] = 32] = "ForkinRoad"; HazardType[HazardType["LanesMerging"] = 64] = "LanesMerging"; HazardType[HazardType["PedCrossing"] = 128] = "PedCrossing"; HazardType[HazardType["SchoolZone"] = 256] = "SchoolZone"; HazardType[HazardType["Cameras"] = 512] = "Cameras"; HazardType[HazardType["RRCrossing"] = 1024] = "RRCrossing"; })(HazardType = WMEWAL_Hazards.HazardType || (WMEWAL_Hazards.HazardType = {})); const pluginName = "WMEWAL-Hazards"; WMEWAL_Hazards.Title = "Hazards"; WMEWAL_Hazards.MinimumZoomLevel = 15; WMEWAL_Hazards.SupportsSegments = false; WMEWAL_Hazards.SupportsVenues = true; const settingsKey = "WMEWALHazardsSettings"; const savedSettingsKey = "WMEWALHazardsSavedSettings"; let settings = null; let savedSettings = []; //let places: Array<IPlace>; let hazards; let nameRegex = null; let cityRegex = null; let websiteRegex = null; let streetRegex = null; let state; let stateName; let lastModifiedBy; let lastModifiedByName; let createdBy; let createdByName; let initCount = 0; let detectIssues = false; let haveNames = false; let haveSpeeds = false; let haveSubtypes = false; let haveScheds = false; let haveStreets = false; //let savedVenues: Array<string> let savedHazards; function onWmeReady() { initCount++; if (WazeWrap && WazeWrap.Ready && typeof (WMEWAL) !== 'undefined' && WMEWAL && WMEWAL.RegisterPlugIn) { log('debug', 'WazeWrap and WMEWAL ready.'); init(); } else { if (initCount < 60) { log('debug', 'WazeWrap or WMEWAL not ready. Trying again...'); setTimeout(onWmeReady, 1000); } else { log('error', 'WazeWrap or WMEWAL not ready. Giving up.'); } } } function bootstrap() { if (W?.userscripts?.state.isReady) { onWmeReady(); } else { document.addEventListener('wme-ready', onWmeReady, { once: true }); } } async function init() { // Check to see if WAL is at the minimum verson needed if (!(typeof WMEWAL.IsAtMinimumVersion === "function" && WMEWAL.IsAtMinimumVersion(minimumWALVersionRequired))) { log('log', "WAL not at required minimum version."); WazeWrap.Alerts.info(GM_info.script.name, "Cannot load plugin because WAL is not at the required minimum version. " + "You might need to manually update it from <a href='https://greasyfork.org/scripts/40641' target='_blank'>Greasy Fork</a>.", true, false); return; } if (typeof Storage !== "undefined") { if (localStorage[settingsKey]) { settings = JSON.parse(localStorage[settingsKey]); } if (localStorage[savedSettingsKey]) { try { savedSettings = JSON.parse(WMEWAL.LZString.decompressFromUTF16(localStorage[savedSettingsKey])); } catch (e) { } if (typeof savedSettings === "undefined" || savedSettings === null || savedSettings.length === 0) { log('debug', "decompressFromUTF16 failed, attempting decompress"); localStorage[savedSettingsKey + "Backup"] = localStorage[savedSettingsKey]; try { savedSettings = JSON.parse(WMEWAL.LZString.decompress(localStorage[savedSettingsKey])); } catch (e) { } if (typeof savedSettings === "undefined" || savedSettings === null) { log('warn', "decompress failed, savedSettings unrecoverable. Using blank"); savedSettings = []; } updateSavedSettings(); } } } if (settings == null) { initSettings(); } else { if (updateProperties()) { updateSettings(); } } log('log', "Initialized"); WazeWrap.Interface.ShowScriptUpdate(SCRIPT_NAME, SCRIPT_VERSION, updateText, greasyForkPage, wazeForumThread); WMEWAL.RegisterPlugIn(WMEWAL_Hazards); } function TranslateHazardType(wazeHazardType) { return I18n.t("permanent_hazards.type." + wazeHazardType.toString()); } WMEWAL_Hazards.TranslateHazardType = TranslateHazardType; function HazardTypeBitmaskToWazeHazardType(hazardType) { switch (hazardType) { case HazardType.Speedbump: return 1; case HazardType.Topes: return 2; case HazardType.Tollbooth: return 3; case HazardType.SharpCurve: return 4; case HazardType.ComplexIntersection: return 5; case HazardType.ForkinRoad: return 6; case HazardType.LanesMerging: return 7; case HazardType.PedCrossing: return 8; case HazardType.SchoolZone: return 9; case HazardType.Cameras: return 10; case HazardType.RRCrossing: return 11; default: return 0; } } WMEWAL_Hazards.HazardTypeBitmaskToWazeHazardType = HazardTypeBitmaskToWazeHazardType; function WazeHazardTypeToHazardTypeBitmask(hazardType) { switch (hazardType) { case 1: return HazardType.Speedbump; case 2: return HazardType.Topes; case 3: return HazardType.Tollbooth; case 4: return HazardType.SharpCurve; case 5: return HazardType.ComplexIntersection; case 6: return HazardType.ForkinRoad; case 7: return HazardType.LanesMerging; case 8: return HazardType.PedCrossing; case 9: return HazardType.SchoolZone; case 10: return HazardType.Cameras; case 11: return HazardType.RRCrossing; default: return HazardType.Unknown; } } WMEWAL_Hazards.WazeHazardTypeToHazardTypeBitmask = WazeHazardTypeToHazardTypeBitmask; function GetTab() { const rpp = "RESIDENCE_HOME"; let html = "<table style='border-collapse:separate;border-spacing:0px 1px;'>"; html += "<tbody>"; html += "<tr><td class='wal-heading'><b>Saved Filters</b></td></tr>"; html += "<tr><td class='wal-indent' style='padding-bottom: 8px'>" + `<select id='${ctlPrefix}SavedSettings'></select><br/>` + `<button class='btn btn-primary' id='${ctlPrefix}LoadSetting' title='Load'>Load</button>` + `<button class='btn btn-primary' style='margin-left: 4px;' id='${ctlPrefix}SaveSetting' title='Save'>Save</button>` + `<button class='btn btn-primary' style='margin-left: 4px;' id='${ctlPrefix}DeleteSetting' title='Delete'>Delete</button></td></tr>`; html += `<tr><td style='border-top: 1px solid'><button class='btn btn-primary' style='margin-top: 6px;margin-bottom: 6px' id='${ctlPrefix}Reset' title='Reset'>Reset</button></td></tr>`; /* html += "<tr><td class='wal-heading' style='border-top: 1px solid'>Output Options</td></tr>"; html += `<tr><td class='wal-indent'><input type='checkbox' id='${ctlPrefix}IncludeAlt' class='wal-check' name='${ctlPrefix}IncludeAlt'>` + `<label for='${ctlPrefix}IncludeAlt' class='wal-label'>Include Alt Names</label></td></tr>` */ html += "<tr><td class='wal-heading' style='border-top: 1px solid; padding-top: 4px'><b>Filters (All of these)</b></td></tr>"; /* html += "<tr><td><b>Category:</b></td></tr>"; html += `<tr><td class='wal-indent'><select id='${ctlPrefix}CategoryOp'>` + "<option value='" + Operation.Equal.toString() + "' selected='selected'>=</option>" + "<option value='" + Operation.NotEqual.toString() + "'><></option>" + "</select>"; html += `<select id='${ctlPrefix}Category'>` + "<option value=''></option>"; for (let topIx = 0; topIx < W.Config.venues.categories.length; topIx++) { const topCategory = W.Config.venues.categories[topIx]; html += ("<option value='" + topCategory + "'>" + I18n.t("venues.categories." + topCategory) + "</option>"); const subCategories = W.Config.venues.subcategories[topCategory]; for (let subIx = 0; subIx < subCategories.length; subIx++) { const subCategory = W.Config.venues.subcategories[topCategory][subIx]; html += ("<option value='" + subCategory + "'>--" + I18n.t("venues.categories." + subCategory) + "</option>"); } } html += "<option value='" + rpp + "'>" + I18n.t("venues.categories." + rpp) + "</option>"; html += "</select></td></tr>"; */ /*Speedbump = 1, Topes = 2, Tollbooth = 4, SharpCurve = 8, ComplexIntersection = 16, ForkinRoad = 32, LanesMerging = 64, PedCrossing = 128, SchoolZone = 256, Cameras = 512, RRCrossing = 1024,*/ html += "<tr><td class='wal-indent'>" + `<button id='${ctlPrefix}HazardTypeAny' class='btn btn-primary' style='margin-right: 8px' title='Any'>Any</button>` + `<button id='${ctlPrefix}HazardTypeClear' class='btn btn-primary' title='Clear'>Clear</button>` + `<div><input type='checkbox' checked='checked' id='${ctlPrefix}HazardTypeSpeedbump' data-group='${ctlPrefix}HazardType' value='${HazardType.Speedbump}' class='wal-check'/>` + `<label for='${ctlPrefix}HazardTypeSpeedbump' class='wal-label'>${TranslateHazardType(HazardTypeBitmaskToWazeHazardType(HazardType.Speedbump))}</label></div>` + `<div><input type='checkbox' id='${ctlPrefix}HazardTypeTopes' data-group='${ctlPrefix}HazardType' value='${HazardType.Topes}' class='wal-check'/>` + `<label for='${ctlPrefix}HazardTypeTopes' class='wal-label'>${TranslateHazardType(HazardTypeBitmaskToWazeHazardType(HazardType.Topes))}</label></div>` + `<div><input type='checkbox' id='${ctlPrefix}HazardTypeTollbooth' data-group='${ctlPrefix}HazardType' value='${HazardType.Tollbooth}' class='wal-check'/>` + `<label for='${ctlPrefix}HazardTypeTollbooth' class='wal-label'>${TranslateHazardType(HazardTypeBitmaskToWazeHazardType(HazardType.Tollbooth))}</label></div>` + `<div><input type='checkbox' id='${ctlPrefix}HazardTypeSharpCurve' data-group='${ctlPrefix}HazardType' value='${HazardType.SharpCurve}' class='wal-check'/>` + `<label for='${ctlPrefix}HazardTypeSharpCurve' class='wal-label'>${TranslateHazardType(HazardTypeBitmaskToWazeHazardType(HazardType.SharpCurve))}</label></div>` + `<div><input type='checkbox' id='${ctlPrefix}HazardTypeComplexIntersection' data-group='${ctlPrefix}HazardType' value='${HazardType.ComplexIntersection}' class='wal-check'/>` + `<label for='${ctlPrefix}HazardTypeComplexIntersection' class='wal-label'>${TranslateHazardType(HazardTypeBitmaskToWazeHazardType(HazardType.ComplexIntersection))}</label></div>` + `<div><input type='checkbox' id='${ctlPrefix}HazardTypeForkinRoad' data-group='${ctlPrefix}HazardType' value='${HazardType.ForkinRoad}' class='wal-check'/>` + `<label for='${ctlPrefix}HazardTypeForkinRoad' class='wal-label'>${TranslateHazardType(HazardTypeBitmaskToWazeHazardType(HazardType.ForkinRoad))}</label></div>` + `<div><input type='checkbox' id='${ctlPrefix}HazardTypeLanesMerging' data-group='${ctlPrefix}HazardType' value='${HazardType.LanesMerging}' class='wal-check'/>` + `<label for='${ctlPrefix}HazardTypeLanesMerging' class='wal-label'>${TranslateHazardType(HazardTypeBitmaskToWazeHazardType(HazardType.LanesMerging))}</label></div>` + `<div><input type='checkbox' id='${ctlPrefix}HazardTypePedCrossing' data-group='${ctlPrefix}HazardType' value='${HazardType.PedCrossing}' class='wal-check'/>` + `<label for='${ctlPrefix}HazardTypePedCrossing' class='wal-label'>${TranslateHazardType(HazardTypeBitmaskToWazeHazardType(HazardType.PedCrossing))}</label></div>` + `<div><input type='checkbox' id='${ctlPrefix}HazardTypeSchoolZone' data-group='${ctlPrefix}HazardType' value='${HazardType.SchoolZone}' class='wal-check'/>` + `<label for='${ctlPrefix}HazardTypeSchoolZone' class='wal-label'>${TranslateHazardType(HazardTypeBitmaskToWazeHazardType(HazardType.SchoolZone))}</label></div>` + `<div><input type='checkbox' id='${ctlPrefix}HazardTypeCameras' data-group='${ctlPrefix}HazardType' value='${HazardType.Cameras}' class='wal-check'/>` + `<label for='${ctlPrefix}HazardTypeCameras' class='wal-label'>${TranslateHazardType(HazardTypeBitmaskToWazeHazardType(HazardType.Cameras))}</label></div>` + `<div><input type='checkbox' id='${ctlPrefix}HazardTypeRRCrossing' data-group='${ctlPrefix}HazardType' value='${HazardType.RRCrossing}' class='wal-check'/>` + `<label for='${ctlPrefix}HazardTypeRRCrossing' class='wal-label'>${TranslateHazardType(HazardTypeBitmaskToWazeHazardType(HazardType.RRCrossing))}</label></div>` + "</td></tr>"; /*html += "<tr><td><b>Lock Level:</b></td></tr>" + "<tr><td class='wal-indent'>" + `<select id='${ctlPrefix}LockLevelOp'>` + "<option value='" + Operation.Equal.toString() + "' selected='selected'>=</option>" + "<option value='" + Operation.NotEqual.toString() + "'><></option></select>" + `<select id='${ctlPrefix}LockLevel'>` + "<option value=''></option>" + "<option value='1'>1</option>" + "<option value='2'>2</option>" + "<option value='3'>3</option>" + "<option value='4'>4</option>" + "<option value='5'>5</option>" + "<option value='6'>6</option>" + "<option value='7'>7</option>" + "</select></td></tr>"; */ /* html += "<tr><td><b>Name RegEx</b></td></tr>"; html += "<tr><td class='wal-indent'>" + `<input type='text' id='${ctlPrefix}Name' class='wal-textbox'/><br/>` + `<input id='${ctlPrefix}IgnoreCase' class='wal-check' type='checkbox'/>` + `<label for='${ctlPrefix}IgnoreCase' class='wal-indent'>Ignore case</label></td>`; html += "<tr><td><b>Street RegEx</b></td></tr>"; html += "<tr><td class='wal-indent'>" + `<input type='text' id='${ctlPrefix}Street' class='wal-textbox'/><br/>` + `<input id='${ctlPrefix}StreetIgnoreCase' class='wal-check' type='checkbox'/>` + `<label for='${ctlPrefix}StreetIgnoreCase' class='wal-indent'>Ignore case</label></td>`; html += "<tr><td><b>City RegEx:</b></td></tr>"; html += `<tr><td class='wal-indent'><input type='text' id='${ctlPrefix}City' class='wal-textbox'/><br/>` + `<input id='${ctlPrefix}CityIgnoreCase' class='wal-check' type='checkbox'/>` + `<label for='${ctlPrefix}CityIgnoreCase' style='margin-left: 8px'>Ignore case</label></td></tr>`; html += "<tr><td><b>Website RegEx:</b></td></tr>"; html += `<tr><td class='wal-indent'><input type='text' id='${ctlPrefix}Website' class='wal-textbox'/><br/>` + `<input id='${ctlPrefix}WebsiteIgnoreCase' class='wal-check' type='checkbox'/>` + `<label for='${ctlPrefix}WebsiteIgnoreCase' style='margin-left: 8px'>Ignore case</label></td></tr>`; */ /* html += "<tr><td><b>State:</b></td></tr>"; html += "<tr><td style='padding-left:20px'>" + `<select id='${ctlPrefix}StateOp'>` + "<option value='" + Operation.Equal.toString() + "' selected='selected'>=</option>" + "<option value='" + Operation.NotEqual.toString() + "'><></option></select>" + `<select id='${ctlPrefix}State'></select>`; html += "<tr><td><b>Type:</b></td></tr>" + `<tr><td class='wal-indent'><select id='${ctlPrefix}Type'>` + "<option value=''></option>" + "<option value='area'>" + I18n.t("edit.venue.type.area") + "</option>" + "<option value='point'>" + I18n.t("edit.venue.type.point") + "</option>" + "</select></td></tr>"; */ html += "<tr><td><b>Created By:</b></td></tr>"; html += "<tr><td class='wal-indent'>" + `<select id='${ctlPrefix}CreatedBy'></select></td></tr>`; html += `<tr><td><input id='${ctlPrefix}Created' class='wal-check' type='checkbox' />` + `<label for=${ctlPrefix}Created' class='wal-label'>Date Created:</label> ` + `<select id='${ctlPrefix}CreatedOp'>` + `<option value='${Operation.LessThan}'><</option>` + `<option value='${Operation.LessThanOrEqual}'><=</option>` + `<option value='${Operation.GreaterThanOrEqual}' selected='selected'>>=</option>` + `<option value='${Operation.GreaterThan}'>></option></select>` + "</td></tr>"; html += "<tr><td class='wal-indent'>" + `<input id='${ctlPrefix}CreatedDate' type='date'/> <input id='${ctlPrefix}CreatedTime' type='time'/></td></tr>`; html += "<tr><td><b>Last Updated By:</b></td></tr>"; html += "<tr><td class='wal-indent'>" + `<select id='${ctlPrefix}LastModifiedBy'></select></td></tr>`; html += `<tr><td><input id='${ctlPrefix}Updated' class='wal-check' type='checkbox' />` + `<label for=${ctlPrefix}Updated' class='wal-label'>Date Updated:</label> ` + `<select id='${ctlPrefix}UpdatedOp'>` + `<option value='${Operation.LessThan}'><</option>` + `<option value='${Operation.LessThanOrEqual}'><=</option>` + `<option value='${Operation.GreaterThanOrEqual}' selected='selected'>>=</option>` + `<option value='${Operation.GreaterThan}'>></option></select>` + "</td></tr>"; html += "<tr><td class='wal-indent'>" + `<input id='${ctlPrefix}UpdatedDate' type='date'/> <input id='${ctlPrefix}UpdatedTime' type='time'/></td></tr>`; /*html += `<tr><td><input id='${ctlPrefix}Editable' class='wal-check' type='checkbox'/>` + `<label for='${ctlPrefix}Editable' class='wal-label'>Editable by me</label></td></tr>`; html += `<tr><td><input id='${ctlPrefix}ParkingLotType' class='wal-check' type='checkbox'/>` + `<label for='${ctlPrefix}ParkingLotType' class='wal-label'>`; html += `Parking Lot Type: <select id='${ctlPrefix}ParkingLotTypeFilter'>` + "<option value='PRIVATE'>" + I18n.t("edit.venue.parking.types.parkingType.PRIVATE") + "</option>" + "<option value='PUBLIC'>" + I18n.t("edit.venue.parking.types.parkingType.PUBLIC") + "</option>" + "<option value='RESTRICTED'>" + I18n.t("edit.venue.parking.types.parkingType.RESTRICTED") + "</option>" + "</select></label></td></tr>"; */ html += "<tr><td class='wal-heading' style='border-top: 1px solid; padding-top: 4px'>Issues (Any of these)</td></tr>"; html += `<tr><td><input class='wal-check' type='checkbox' id='${ctlPrefix}NoName'/>` + `<label for='${ctlPrefix}NoName' class='wal-label'>No Name</label></td></tr>`; /* html += `<tr><td><input class='wal-check' type='checkbox' id='${ctlPrefix}NoHouseNumber'/>` + `<label for='${ctlPrefix}NoHouseNumber' class='wal-label'>Missing House Number</label></td></tr>`; html += `<tr><td><input class='wal-check' type='checkbox' id='${ctlPrefix}NoStreet'/>` + `<label for='${ctlPrefix}NoStreet' class='wal-label'>Missing Street</label></td></tr>`; html += `<tr><td><input class='wal-check' type='checkbox' id='${ctlPrefix}NoCity'/>` + `<label for='${ctlPrefix}NoCity' class='wal-label'>Missing City</label></td></tr>`; html += `<tr><td><input class='wal-check' type='checkbox' id='${ctlPrefix}AdLocked'/>` + `<label for='${ctlPrefix}AdLocked' class='wal-label'>Ad Locked</label></td></tr>`; html += `<tr><td ><input class='wal-check' type='checkbox' id='${ctlPrefix}UpdateRequests'/>` + `<label for='${ctlPrefix}UpdateRequests' class='wal-label'>Has Update Requests</label></td></tr>`; html += `<tr><td ><input class='wal-check' type='checkbox' id='${ctlPrefix}PendingApproval'/>` + `<label for='${ctlPrefix}PendingApproval' class='wal-label'>Pending Approval</label></td></tr>`; html += `<tr><td><input class='wal-check' type='checkbox' id='${ctlPrefix}UndefStreet' />` + `<label for='${ctlPrefix}UndefStreet' class='wal-label' title='Street ID not found in W.model.streets.objects, possibly as a result of a cities form Merge or Delete'>Undefined Street ID</label></td></tr>`; html += `<tr><td><input class='wal-check' type='checkbox' id='${ctlPrefix}NoExternalProviders' />` + `<label for='${ctlPrefix}NoExternalProviders' class='wal-label'>No External Provider Links</label></td></tr>`; html += `<tr><td><input class='wal-check' type='checkbox' id='${ctlPrefix}NoHours' />` + `<label for='${ctlPrefix}NoHours' class='wal-label'>No Hours</label></td></tr>`; html += `<tr><td><input class='wal-check' type='checkbox' id='${ctlPrefix}NoWebsite' />` + `<label for='${ctlPrefix}NoWebsite' class='wal-label'>No Website</label></td></tr>`; html += `<tr><td><input class='wal-check' type='checkbox' id='${ctlPrefix}NoPhoneNumber' />` + `<label for='${ctlPrefix}NoPhoneNumber' class='wal-label'>No Phone Number</label></td></tr>`; html += `<tr><td><input class='wal-check' type='checkbox' id='${ctlPrefix}BadPhoneNumberFormat' />` + `<label for='${ctlPrefix}BadPhoneNumberFormat' class='wal-label'>Bad Phone Number Format</label></td></tr>`; html += `<tr><td><input class='wal-check' type='checkbox' id='${ctlPrefix}NoEntryExitPoints' />` + `<label for='${ctlPrefix}NoEntryExitPoints' class='wal-label'>No Entry/Exit Points</label></td></tr>`; html += `<tr><td><input class='wal-check' type='checkbox' id='${ctlPrefix}MissingBrand' />` + `<label for='${ctlPrefix}MissingBrand' class='wal-label'>Missing Brand (GS)</label></td></tr>`; */ html += "</tbody></table>"; return html; } WMEWAL_Hazards.GetTab = GetTab; function TabLoaded() { loadScriptUpdateMonitor(); updateStates(); updateUsers($(`#${ctlPrefix}LastModifiedBy`)); updateUsers($(`#${ctlPrefix}CreatedBy`)); updateUI(); updateSavedSettingsList(); $(`#${ctlPrefix}State`).on("focus", updateStates); $(`#${ctlPrefix}LastModifiedBy`).on("focus", function () { updateUsers($(`#${ctlPrefix}LastModifiedBy`)); }); $(`#${ctlPrefix}CreatedBy`).on("focus", function () { updateUsers($(`#${ctlPrefix}CreatedBy`)); }); $(`#${ctlPrefix}HazardTypeAny`).on("click", function () { $(`input[data-group=${ctlPrefix}HazardType]`).prop("checked", true); }); $(`#${ctlPrefix}HazardTypeClear`).on("click", function () { $(`input[data-group=${ctlPrefix}HazardType]`).prop("checked", false); }); $(`#${ctlPrefix}LoadSetting`).on("click", loadSetting); $(`#${ctlPrefix}SaveSetting`).on("click", saveSetting); $(`#${ctlPrefix}DeleteSetting`).on("click", deleteSetting); $(`#${ctlPrefix}Reset`).on('click', reset); } WMEWAL_Hazards.TabLoaded = TabLoaded; function reset() { initSettings(); updateUI(); } function updateStates() { const selectState = $(`#${ctlPrefix}State`); // Preserve current selection const currentId = parseInt(selectState.val()); selectState.empty(); const stateObjs = []; stateObjs.push({ id: null, name: "" }); for (let s in W.model.states.objects) { if (W.model.states.objects.hasOwnProperty(s)) { const st = W.model.states.getObjectById(parseInt(s)); if (st.getAttribute('id') !== 1 && st.getAttribute('name').length !== 0) { stateObjs.push({ id: st.getAttribute('id'), name: st.getAttribute('name') }); } } } stateObjs.sort(function (a, b) { if (a.id == null) { return -1; } else { return a.name.localeCompare(b.name); } }); for (let ix = 0; ix < stateObjs.length; ix++) { const so = stateObjs[ix]; const stateOption = $("<option/>").text(so.name).attr("value", so.id ?? ""); if (currentId != null && so.id === currentId) { stateOption.attr("selected", "selected"); } selectState.append(stateOption); } } function updateUsers(selectUsernameList) { // Preserve current selection const currentId = parseInt(selectUsernameList.val()); selectUsernameList.empty(); const userObjs = []; userObjs.push({ id: null, name: "" }); for (let uo in W.model.users.objects) { if (W.model.users.objects.hasOwnProperty(uo)) { const u = W.model.users.getObjectById(parseInt(uo)); if (u.type === "user" && u.getAttribute('userName') !== null && typeof u.getAttribute('userName') !== "undefined") { userObjs.push({ id: u.getAttribute('id'), name: u.getAttribute('userName') }); } } } userObjs.sort(function (a, b) { if (a.id == null) { return -1; } else { return a.name.localeCompare(b.name); } }); for (let ix = 0; ix < userObjs.length; ix++) { const o = userObjs[ix]; const userOption = $("<option/>").text(o.name).attr("value", o.id); if (currentId != null && o.id == null) { userOption.attr("selected", "selected"); } selectUsernameList.append(userOption); } } function updateSavedSettingsList() { const s = $(`#${ctlPrefix}SavedSettings`); s.empty(); for (let ixSaved = 0; ixSaved < savedSettings.length; ixSaved++) { let opt = $("<option/>").attr("value", ixSaved).text(savedSettings[ixSaved].Name); s.append(opt); } } function updateUI() { // $(`#${ctlPrefix}OutputTo`).val(settings.OutputTo); $(`#${ctlPrefix}HazardTypeSpeedbump`).prop("checked", settings.HazardTypeMask & HazardType.Speedbump); $(`#${ctlPrefix}HazardTypeTopes`).prop("checked", settings.HazardTypeMask & HazardType.Topes); $(`#${ctlPrefix}HazardTypeTollbooth`).prop("checked", settings.HazardTypeMask & HazardType.Tollbooth); $(`#${ctlPrefix}HazardTypeSharpCurve`).prop("checked", settings.HazardTypeMask & HazardType.SharpCurve); $(`#${ctlPrefix}HazardTypeComplexIntersection`).prop("checked", settings.HazardTypeMask & HazardType.ComplexIntersection); $(`#${ctlPrefix}HazardTypeForkinRoad`).prop("checked", settings.HazardTypeMask & HazardType.ForkinRoad); $(`#${ctlPrefix}HazardTypeLanesMerging`).prop("checked", settings.HazardTypeMask & HazardType.LanesMerging); $(`#${ctlPrefix}HazardTypePedCrossing`).prop("checked", settings.HazardTypeMask & HazardType.PedCrossing); $(`#${ctlPrefix}HazardTypeSchoolZone`).prop("checked", settings.HazardTypeMask & HazardType.SchoolZone); $(`#${ctlPrefix}HazardTypeCameras`).prop("checked", settings.HazardTypeMask & HazardType.Cameras); $(`#${ctlPrefix}HazardTypeRRCrossing`).prop("checked", settings.HazardTypeMask & HazardType.RRCrossing); /* $(`#${ctlPrefix}CategoryOp`).val(settings.CategoryOperation || Operation.Equal); $(`#${ctlPrefix}Category`).val(settings.Category); $(`#${ctlPrefix}LockLevel`).val(settings.LockLevel); $(`#${ctlPrefix}LockLevelOp`).val(settings.LockLevelOperation || Operation.Equal); $(`#${ctlPrefix}NoName`).prop("checked", settings.NoName); $(`#${ctlPrefix}Name`).val(settings.Regex || ""); $(`#${ctlPrefix}IgnoreCase`).prop("checked", settings.RegexIgnoreCase); $(`#${ctlPrefix}City`).val(settings.CityRegex || ""); $(`#${ctlPrefix}CityIgnoreCase`).prop("checked", settings.CityRegexIgnoreCase); $(`#${ctlPrefix}State`).val(settings.State); $(`#${ctlPrefix}StateOp`).val(settings.StateOperation || Operation.Equal); $(`#${ctlPrefix}Type`).val(settings.PlaceType); $(`#${ctlPrefix}Editable`).prop("checked", settings.EditableByMe); $(`#${ctlPrefix}NoHouseNumber`).prop("checked", settings.NoHouseNumber); $(`#${ctlPrefix}UndefStreet`).prop("checked", settings.UndefStreet); $(`#${ctlPrefix}AdLocked`).prop("checked", settings.AdLocked); $(`#${ctlPrefix}UpdateRequests`).prop("checked", settings.UpdateRequests); $(`#${ctlPrefix}PendingApproval`).prop("checked", settings.PendingApproval); $(`#${ctlPrefix}NoStreet`).prop("checked", settings.NoStreet); $(`#${ctlPrefix}NoCity`).prop("checked", settings.NoCity); */ $(`#${ctlPrefix}LastModifiedBy`).val(settings.LastModifiedBy); $(`#${ctlPrefix}CreatedBy`).val(settings.CreatedBy); /* $(`#${ctlPrefix}NoExternalProviders`).prop("checked", settings.NoExternalProviders); $(`#${ctlPrefix}NoHours`).prop("checked", settings.NoHours); $(`#${ctlPrefix}NoPhoneNumber`).prop("checked", settings.NoPhoneNumber); $(`#${ctlPrefix}BadPhoneNumberFormat`).prop("checked", settings.BadPhoneNumberFormat); $(`#${ctlPrefix}NoWebsite`).prop("checked", settings.NoWebsite); $(`#${ctlPrefix}NoEntryExitPoints`).prop("checked", settings.NoEntryExitPoints); $(`#${ctlPrefix}ParkingLotType`).prop("checked", settings.ParkingLotType); $(`#${ctlPrefix}ParkingLotTypeFilter`).val(settings.ParkingLotTypeFilter); $(`#${ctlPrefix}MissingBrand`).prop("checked", settings.MissingBrand); $(`#${ctlPrefix}IncludeAlt`).prop("checked", settings.IncludeAlt); */ $(`#${ctlPrefix}Created`).prop("checked", settings.Created); $(`#${ctlPrefix}CreatedOp`).val(settings.CreatedOperation); if (settings.CreatedDate != null) { const createdDateTime = new Date(settings.CreatedDate); $(`#${ctlPrefix}CreatedDate`).val(createdDateTime.getFullYear().toString().padStart(4, "0") + "-" + (createdDateTime.getMonth() + 1).toString().padStart(2, "0") + "-" + createdDateTime.getDate().toString().padStart(2, "0")); $(`#${ctlPrefix}CreatedTime`).val(createdDateTime.getHours().toString().padStart(2, "0") + ":" + createdDateTime.getMinutes().toString().padStart(2, "0")); } else { $(`#${ctlPrefix}CreatedDate`).val(""); $(`#${ctlPrefix}CreatedTime`).val(""); } $(`#${ctlPrefix}Updated`).prop("checked", settings.Updated); $(`#${ctlPrefix}UpdatedOp`).val(settings.UpdatedOperation); if (settings.UpdatedDate != null) { const updatedDateTime = new Date(settings.UpdatedDate); $(`#${ctlPrefix}UpdatedDate`).val(updatedDateTime.getFullYear().toString().padStart(4, "0") + "-" + (updatedDateTime.getMonth() + 1).toString().padStart(2, "0") + "-" + updatedDateTime.getDate().toString().padStart(2, "0")); $(`#${ctlPrefix}UpdatedTime`).val(updatedDateTime.getHours().toString().padStart(2, "0") + ":" + updatedDateTime.getMinutes().toString().padStart(2, "0")); } else { $(`#${ctlPrefix}UpdatedDate`).val(""); $(`#${ctlPrefix}UpdatedTime`).val(""); } /* $(`#${ctlPrefix}Website`).val(settings.WebsiteRegex || ""); $(`#${ctlPrefix}WebsiteIgnoreCase`).prop("checked", settings.WebsiteRegexIgnoreCase); $(`#${ctlPrefix}Street`).val(settings.StreetRegex || ""); $(`#${ctlPrefix}StreetIgnoreCase`).prop("checked", settings.StreetRegexIgnoreCase); */ } function loadSetting() { const selectedSetting = parseInt($(`#${ctlPrefix}SavedSettings`).val()); if (selectedSetting == null || isNaN(selectedSetting) || selectedSetting < 0 || selectedSetting > savedSettings.length) { return; } initSettings(); const savedSetting = savedSettings[selectedSetting].Setting; for (let name in savedSetting) { if (settings.hasOwnProperty(name)) { settings[name] = savedSetting[name]; } } updateUI(); } function validateSettings() { function addMessage(error) { message += ((message.length > 0 ? "\n" : "") + error); } let message = ""; const s = getSettings(); const cat = $(`#${ctlPrefix}Category`).val(); let r; if (nullif(s.Regex, "") !== null) { try { r = (s.RegexIgnoreCase ? new RegExp(s.Regex, "i") : new RegExp(s.Regex)); } catch (error) { addMessage("Name RegEx is invalid"); } } if (nullif(s.CityRegex, "") !== null) { try { r = (s.CityRegexIgnoreCase ? new RegExp(s.CityRegex, "i") : new RegExp(s.CityRegex)); } catch (error) { addMessage("City RegEx is invalid"); } } if (nullif(s.StreetRegex, "") !== null) { try { r = (s.StreetRegexIgnoreCase ? new RegExp(s.StreetRegex, "i") : new RegExp(s.StreetRegex)); } catch (error) { addMessage("Street RegEx is invalid"); } } /* const selectedState = $(`#${ctlPrefix}State`).val(); if (nullif(selectedState, "") !== null && s.State === null) { addMessage("Invalid state selection"); } */ const selectedModifiedUser = $(`#${ctlPrefix}LastModifiedBy`).val(); if (nullif(selectedModifiedUser, "") !== null && s.LastModifiedBy === null) { addMessage("Invalid last modified user"); } const selectedCreatedUser = $(`#${ctlPrefix}CreatedBy`).val(); if (nullif(selectedCreatedUser, "") !== null && s.CreatedBy === null) { addMessage("Invalid created user"); } if (s.Created && s.CreatedDate === null) { addMessage("Select a created date on which to filter."); } if (s.Updated && s.UpdatedDate === null) { addMessage("Select an updated date on which to filter."); } if (message.length > 0) { alert(pluginName + ": " + message); return false; } return true; } function saveSetting() { if (validateSettings()) { const s = getSettings(); const sName = prompt("Enter a name for this setting"); if (sName == null) { return; } // Check to see if there is already a name that matches this for (let ixSetting = 0; ixSetting < savedSettings.length; ixSetting++) { if (savedSettings[ixSetting].Name === sName) { if (confirm("A setting with this name already exists. Overwrite?")) { savedSettings[ixSetting].Setting = s; updateSavedSettings(); } else { alert("Please pick a new name."); } return; } } const savedSetting = { Name: sName, Setting: s }; savedSettings.push(savedSetting); updateSavedSettings(); } } function getSettings() { const s = { HazardTypeMask: 0, NoName: $(`#${ctlPrefix}NoName`).prop("checked"), Regex: null, RegexIgnoreCase: $(`#${ctlPrefix}IgnoreCase`).prop("checked"), State: null, StateOperation: parseInt($(`#${ctlPrefix}StateOp`).val()), EditableByMe: $(`#${ctlPrefix}Editable`).prop("checked"), CityRegex: null, CityRegexIgnoreCase: $(`#${ctlPrefix}CityIgnoreCase`).prop("checked"), NoStreet: $(`#${ctlPrefix}NoStreet`).prop("checked"), NoCity: $(`#${ctlPrefix}NoCity`).prop("checked"), LastModifiedBy: null, CreatedBy: null, IncludeAlt: $(`#${ctlPrefix}IncludeAlt`).prop("checked"), Created: $(`#${ctlPrefix}Created`).prop("checked"), CreatedOperation: parseInt($(`#${ctlPrefix}CreatedOp`).val()), CreatedDate: null, Updated: $(`#${ctlPrefix}Updated`).prop("checked"), UpdatedOperation: parseInt($(`#${ctlPrefix}UpdatedOp`).val()), UpdatedDate: null, StreetRegex: null, StreetRegexIgnoreCase: $(`#${ctlPrefix}StreetIgnoreCase`).prop("checked") }; $(`input[data-group=${ctlPrefix}HazardType]:checked`).each(function (ix, e) { s.HazardTypeMask = s.HazardTypeMask | parseInt(e.value); }); s.Regex = nullif($(`#${ctlPrefix}Name`).val(), ""); s.CityRegex = nullif($(`#${ctlPrefix}City`).val(), ""); s.StreetRegex = nullif($(`#${ctlPrefix}Street`).val(), ""); const selectedState = $(`#${ctlPrefix}State`).val(); if (nullif(selectedState, "") !== null) { const state = W.model.states.getObjectById(parseInt(selectedState)); if (state !== null) { s.State = state.getAttribute('id'); } } const selectedModifiedUser = $(`#${ctlPrefix}LastModifiedBy`).val(); if (nullif(selectedModifiedUser, "") !== null) { const u = W.model.users.getObjectById(selectedModifiedUser); if (u !== null) { s.LastModifiedBy = u.getAttribute('id'); } } const selectedCreatedUser = $(`#${ctlPrefix}CreatedBy`).val(); if (nullif(selectedCreatedUser, "") !== null) { const u = W.model.users.getObjectById(selectedCreatedUser); if (u !== null) { s.CreatedBy = u.getAttribute('id'); } } let createdDate = $(`#${ctlPrefix}CreatedDate`).val(); if (createdDate && createdDate.length > 0) { const createdTime = $(`#${ctlPrefix}CreatedTime`).val(); if (createdTime && createdTime.length > 0) { createdDate += ` ${createdTime}`; } else { createdDate += ' 00:00'; } s.CreatedDate = (new Date(createdDate)).getTime(); } let updatedDate = $(`#${ctlPrefix}UpdatedDate`).val(); if (updatedDate && updatedDate.length > 0) { const updatedTime = $(`#${ctlPrefix}UpdatedTime`).val(); if (updatedTime && updatedTime.length > 0) { updatedDate += ` ${updatedTime}`; } else { updatedDate += ' 00:00'; } s.UpdatedDate = (new Date(updatedDate)).getTime(); } return s; } function deleteSetting() { const selectedSetting = parseInt($(`#${ctlPrefix}SavedSettings`).val()); if (selectedSetting == null || isNaN(selectedSetting) || selectedSetting < 0 || selectedSetting > savedSettings.length) { return; } if (confirm("Are you sure you want to delete this saved setting?")) { savedSettings.splice(selectedSetting, 1); updateSavedSettings(); } } function ScanStarted() { let allOk = validateSettings(); hazards = []; savedHazards = []; haveNames = false; haveSpeeds = false; haveSubtypes = false; haveScheds = false; haveStreets = false; if (allOk) { settings = getSettings(); if (nullif(settings.Regex, "") !== null) { nameRegex = (settings.RegexIgnoreCase ? new RegExp(settings.Regex, "i") : new RegExp(settings.Regex)); } else { nameRegex = null; } if (nullif(settings.CityRegex, "") !== null) { cityRegex = (settings.CityRegexIgnoreCase ? new RegExp(settings.CityRegex, "i") : new RegExp(settings.CityRegex)); } else { cityRegex = null; } if (nullif(settings.StreetRegex, "") !== null) { streetRegex = (settings.StreetRegexIgnoreCase ? new RegExp(settings.StreetRegex, "i") : new RegExp(settings.StreetRegex)); } else { streetRegex = null; } if (settings.State !== null) { state = W.model.states.getObjectById(settings.State); stateName = state.getAttribute('name'); } else { state = null; stateName = null; } if (settings.LastModifiedBy !== null) { lastModifiedBy = W.model.users.getObjectById(settings.LastModifiedBy); lastModifiedByName = lastModifiedBy.getAttribute('userName'); } else { lastModifiedBy = null; lastModifiedByName = null; } if (settings.CreatedBy !== null) { createdBy = W.model.users.getObjectById(settings.CreatedBy); createdByName = createdBy.getAttribute('userName'); } else { createdBy = null; createdByName = null; } detectIssues = settings.NoName || settings.NoStreet || settings.NoCity; updateSettings(); } return allOk; } WMEWAL_Hazards.ScanStarted = ScanStarted; function ScanExtent(segments, venues) { return new Promise(resolve => { setTimeout(function () { const count = scan(segments, venues); resolve({ ID: 'PH', count }); }); }); } WMEWAL_Hazards.ScanExtent = ScanExtent; function scan(segments, venues) { for (let h in W.model.permanentHazards.objects) { const phazard = W.model.permanentHazards.getObjectById(Number(h)); if (phazard != null) { if ((WazeHazardTypeToHazardTypeBitmask(phazard.getAttribute('type')) & settings.HazardTypeMask) && //(!settings.EditableByMe || phazard.arePropertiesEditable() || phazard.areUpdateRequestsEditable()) && (nameRegex == null || nameRegex.test(phazard.getAttribute('name'))) && (!settings.Created || (settings.CreatedOperation === Operation.LessThan && phazard.getAttribute('createdOn') < settings.CreatedDate) || (settings.CreatedOperation === Operation.LessThanOrEqual && phazard.getAttribute('createdOn') <= settings.CreatedDate) || (settings.CreatedOperation === Operation.GreaterThanOrEqual && phazard.getAttribute('createdOn') >= settings.CreatedDate) || (settings.CreatedOperation === Operation.GreaterThan && phazard.getAttribute('createdOn') > settings.CreatedDate)) && (!settings.Updated || (settings.UpdatedOperation === Operation.LessThan && (phazard.getAttribute('updatedOn') || phazard.getAttribute('createdOn')) < settings.UpdatedDate) || (settings.UpdatedOperation === Operation.LessThanOrEqual && (phazard.getAttribute('updatedOn') || phazard.getAttribute('createdOn')) <= settings.UpdatedDate) || (settings.UpdatedOperation === Operation.GreaterThanOrEqual && (phazard.getAttribute('updatedOn') || phazard.getAttribute('createdOn')) >= settings.UpdatedDate) || (settings.UpdatedOperation === Operation.GreaterThan && (phazard.getAttribute('updatedOn') || phazard.getAttribute('createdOn')) > settings.UpdatedDate)) && ((settings.CreatedBy === null) || (phazard.getCreatedBy() === settings.CreatedBy)) && ((settings.LastModifiedBy === null) || ((phazard.getUpdatedBy() ?? phazard.getCreatedBy()) === settings.LastModifiedBy))) { let issues = 0; /* if (state != null) { if (address && !address.isEmpty() && address.attributes.state) { if (settings.StateOperation === Operation.Equal && address.attributes.state.getAttribute('id') !== state.getAttribute('id') || settings.StateOperation === Operation.NotEqual && address.attributes.state.getAttribute('id') === state.getAttribute('id')) { continue; } } else if (settings.StateOperation === Operation.Equal) { continue; } } */ // if (settings.LastModifiedBy != null) { // if (venue.getAttribute('updatedBy') != null) { // if (venue.getAttribute('updatedBy') !== settings.LastModifiedBy) { // continue; // } // } else if (venue.getAttribute('createdBy') !== settings.LastModifiedBy) { // continue; // } // } // if (settings.CreatedBy != null) { // if (venue.getAttribute('createdBy') !== settings.CreatedBy) { // continue; // } // } /* let regExMatched = false; if (cityRegex != null) { regExMatched = false; if (address && !address.isEmpty() && address.attributes.city && !address.attributes.city.isEmpty() && address.attributes.city.hasName()) { regExMatched = cityRegex.test(address.attributes.city.getAttribute('name')); } if (!regExMatched) { continue; } } if (streetRegex != null) { regExMatched = false; if (address && !address.isEmpty() && !address.isEmptyStreet()) { regExMatched = streetRegex.test(address.attributes.street.getAttribute('name')); } if (!regExMatched) { continue; } } if (settings.NoName && !venue.getAttribute('name')) { issues |= Issue.NoName; } if (detectIssues && issues === 0) { // If at least one issue was chosen and this segment doesn't have any issues, then skip it continue; } */ // Don't add it if we've already done so if (savedHazards.indexOf(phazard.getID()) === -1) { savedHazards.push(phazard.getID()); const lastEditorID = phazard.getUpdatedBy() ?? phazard.getCreatedBy(); const lastEditor = W.model.users.getObjectById(lastEditorID); const createdByID = phazard.getCreatedBy(); const createdBy = W.model.users.getObjectById(createdByID); const direction = phazard.getAttribute('direction'); var speedLimit = phazard.getAttribute('speedLimit'); if (W.model.isImperial) { speedLimit = Math.round(speedLimit * 0.621371); } const scheduleId = phazard.getAttribute('scheduleId'); var scheduleText = ''; if (scheduleId) { const sch = W.model.schedules.getObjectById(scheduleId); // It appears the schedules are not automatically loaded into the model when the map is moved. // So this seems to not usually get any data. It possible to request a specific schedule, not sure if that will be added here. if (sch) { scheduleText = sch.attributes.type; if (scheduleText == 'PUBLIC') { scheduleText = sch.attributes.name; } } else { scheduleText = scheduleId.substring(scheduleId.length - 12); } } const subTypes = phazard.getAttribute('subTypes'); var subTypeList = ''; if (subTypes.length > 0) { subTypeList = subTypes.join().replace("_LANE", "").replace("MOBILE_", ""); } const segmentId = phazard.getAttribute('segmentId'); var streetName = ''; var cityName = ''; if (segmentId) { const s = W.model.segments.getObjectById(segmentId); const address = s.getAddress(W.model); streetName = (address && !address.isEmpty() && !address.isEmptyStreet()) ? address.attributes.street.getAttribute('name') : ""; cityName = (address && !address.isEmpty()) ? address.getCityName() : ""; if (address.getCity().isEmpty()) { const alt = address.attributes.altStreets; for (var i = 0; i < alt.length; i++) { const a = alt[i]; if (!a.getCity().isEmpty()) { cityName = a.getCityName(); break; } } } } const phTypeNum = phazard.getAttribute('type'); const hazard = { id: phazard.getAttribute('id'), phTypeNum, phType: I18n.t("permanent_hazards.type." + phTypeNum.toString()), direction, speedLimit, scheduleId, scheduleText, subTypes, subTypeList, streetName, cityName, name: phazard.getAttribute('name'), pointGeometry: phazard.getOLGeometry().getCentroid(), segmentId, lastEditor: lastEditor?.getAttribute('userName') ?? '', createdBy: createdBy?.getAttribute('userName') ?? '', //issues: issues, }; if (hazard.name) haveNames = true; if (hazard.speedLimit > 0) haveSpeeds = true; if (hazard.subTypes.length > 0) haveSubtypes = true; if (hazard.scheduleId) haveScheds = true; if (hazard.streetName) haveStreets = true; hazards.push(hazard); } } } } return hazards.length; } function ScanComplete() { if (hazards.length === 0) { alert(pluginName + ": No hazards found."); } else { hazards.sort(function (a, b) { return a.phType.localeCompare(b.phType); }); const isCSV = (WMEWAL.outputTo & WMEWAL.OutputTo.CSV); const isTab = (WMEWAL.outputTo & WMEWAL.OutputTo.Tab); const addBOM = WMEWAL.addBOM ?? false; const outputFields = WMEWAL.outputFields ?? ['CreatedEditor', 'LastEditor', 'LockLevel', 'Lat', 'Lon']; const includeCreatedBy = outputFields.indexOf('CreatedEditor') > -1 || settings.CreatedBy !== null; const includeLastUpdatedBy = outputFields.indexOf('LastEditor') > -1 || settings.LastModifiedBy !== null; //const includeLockLevel = outputFields.indexOf('LockLevel') > -1 || settings.LockLevel !== null; const includeLat = outputFields.indexOf('Lat') > -1; const includeLon = outputFields.indexOf('Lon') > -1; let lineArray; let columnArray; let w; let fileName; if (isCSV) { lineArray = []; columnArray = ["Type"]; if (haveNames) { columnArray.push("Name"); } if (detectIssues) { columnArray.push("Issues"); } if (haveSubtypes) { columnArray.push("SubTypes"); } if (haveSpeeds) { columnArray.push("Speed"); } if (haveScheds) { columnArray.push("Sched ID"); } if (haveStreets) { columnArray.push("Street"); } if (haveStreets) { columnArray.push("City"); } if (includeCreatedBy) { columnArray.push("Created By"); } if (includeLastUpdatedBy) { columnArray.push("Last Updated By"); } if (includeLat) { columnArray.push("Latitude"); } if (includeLon) { columnArray.push("Longitude"); } columnArray.push("Permalink"); lineArray.push(columnArray); fileName = "Hazards_" + WMEWAL.areaName; fileName += ".csv"; } function getOperationText(operation) { return operation === Operation.NotEqual ? "does not equal " : "equals "; } if (isTab) { w = window.open(); w.document.write("<html><head><title>Hazards</title></head><body>"); w.document.write("<h3>Area: " + WMEWAL.areaName + "</h3>"); w.document.write("<h4>Filters</h4>"); w.document.write("<div>Hazard Type: "); let comma = ""; for (let rt in HazardType) { if (HazardType.hasOwnProperty(rt)) { const mask = parseInt(rt); if (!isNaN(mask) && settings.HazardTypeMask & mask) { w.document.write(comma + TranslateHazardType(HazardTypeBitmaskToWazeHazardType(mask))); comma = ", "; } } } w.document.write('</div>'); if (settings.Regex != null) { w.document.write(`<div>Name matches: ${settings.Regex}`); if (settings.RegexIgnoreCase) { w.document.write(" (ignoring case)"); } w.document.write('</div>'); } /* if (streetRegex != null) { w.document.write(`<div>Street Name matches: ${settings.StreetRegex}`); if (settings.StreetRegexIgnoreCase) { w.document.write(" (ignoring case)"); } w.document.write('</div>'); } if (cityRegex != null) { w.document.write(`<div>City Name matches: ${settings.CityRegex}`); if (settings.CityRegexIgnoreCase) { w.document.write(" (ignoring case)"); } w.document.write('</div>'); } */ if (stateName != null) { w.document.write(`<div>State ${getOperationText(settings.StateOperation)}${stateName}</div>`); } if (settings.LastModifiedBy != null) { w.document.write(`<div>Last modified by ${lastModifiedByName}</div>`); } if (settings.CreatedBy != null) { w.document.write(`<div>Created by ${createdByName}</div>`); } if (settings.EditableByMe) { w.document.write('<div>Editable by me</div>'); } if (settings.Created) { w.document.write('<div>Created '); switch (settings.CreatedOperation) { case Operation.GreaterThan: w.document.write('after'); break; case Operation.GreaterThanOrEqual: w.document.write('on or after'); break; case Operation.LessThan: w.document.write('before'); break; case Operation.LessThanOrEqual: w.document.write('on or before'); break; default: break; } w.document.write(` ${new Date(settings.CreatedDate).toString()}</div>`); } if (settings.Updated) { w.document.write('<div>Updated '); switch (settings.UpdatedOperation) { case Operation.GreaterThan: w.document.write('after'); break; case Operation.GreaterThanOrEqual: w.document.write('on or after'); break; case Operation.LessThan: w.document.write('before'); break; case Operation.LessThanOrEqual: w.document.write('on or before'); break; default: break; } w.document.write(` ${new Date(settings.UpdatedDate).toString()}</div>`); } if (detectIssues) { w.document.write("<h4>Issues</h4>"); } if (settings.NoName) { w.document.write("<div>No Name</div>"); } w.document.write("<table style='border-collapse: separate; border-spacing: 8px 0px'><thead><tr><th>Type</th>"); if (haveNames) { w.document.write("<th>Name</th>"); } if (detectIssues) { w.document.write("<th>Issues</th>"); } if (haveSubtypes) { w.document.write("<th>SubTypes</th>"); } if (haveSpeeds) { w.document.write("<th>Speed</th>"); } if (haveScheds) { w.document.write("<th>Sched ID</th>"); } if (haveStreets) { w.document.write("<th>Street</th><th>City</th>"); } if (includeCreatedBy) { w.document.write("<th>Created By</th>"); } if (includeLastUpdatedBy) { w.document.write("<th>Last Updated By</th>"); } if (includeLat) { w.document.write("<th>Latitude</th>"); } if (includeLon) { w.document.write("<th>Longitude</th>"); } w.document.write("<th>Permalink</th></tr><thead><tbody>"); } for (let ixHazard = 0; ixHazard < hazards.length; ixHazard++) { const ahazard = hazards[ixHazard]; const plPlace = getHazardPL(ahazard); const latlon = OpenLayers.Layer.SphericalMercator.inverseMercator(ahazard.pointGeometry.x, ahazard.pointGeometry.y); const tName = (ahazard.name == null) ? "" : ahazard.name; const tSpeed = (ahazard.speedLimit > 0) ? ahazard.speedLimit.toString() : ""; if (isCSV) { columnArray = [`"${ahazard.phType}"`]; if (haveNames) { columnArray.push(tName); } if (detectIssues) { //columnArray.push(`"${getIssues(ahazard.issues)}"`); } if (haveSubtypes) { columnArray.push(ahazard.subTypeList); } if (haveSpeeds) { columnArray.push(tSpeed); } if (haveScheds) { columnArray.push(ahazard.scheduleText); } if (haveStreets) { columnArray.push(ahazard.streetName); columnArray.push(ahazard.cityName); } if (includeCreatedBy) { columnArray.push(`"${ahazard.createdBy}"`); } if (includeLastUpdatedBy) { columnArray.push(`"${ahazard.lastEditor}"`); } if (includeLat) { columnArray.push(latlon.lat.toString()); } if (includeLon) { columnArray.push(latlon.lon.toString()); } columnArray.push(`"${plPlace}"`); lineArray.push(columnArray); } if (isTab) { w.document.write(`<tr><td>${ahazard.phType}</td>`); if (haveNames) { w.document.write(`<td>${tName}</td>`); } if (detectIssues) { //w.document.write(`<td>${getIssues(ahazard.issues)}</td>`); } if (haveSubtypes) { w.document.write(`<td>${ahazard.subTypeList}</td>`); } if (haveSpeeds) { w.document.write(`<td>${tSpeed}</td>`); } if (haveScheds) { w.document.write(`<td>${ahazard.scheduleText}</td>`); } if (haveStreets) { w.document.write(`<td>${ahazard.streetName}</td>`); w.document.write(`<td>${ahazard.cityName}</td>`); } if (includeCreatedBy) { w.document.write(`<td>${ahazard.createdBy}</td>`); } if (includeLastUpdatedBy) { w.document.write(`<td>${ahazard.lastEditor}</td>`); } if (includeLat) { w.document.write(`<td>${latlon.lat.toString()}</td>`); } if (includeLon) { w.document.write(`<td>${latlon.lon.toString()}</td>`); } w.document.write(`<td><a href='${plPlace}' target='_blank'>Permalink</a></td></tr>`); } } if (isCSV) { const csvContent = lineArray.join("\n") + "\n" + WMEWAL.getErrCsvText(); const blobContent = []; if (addBOM) { blobContent.push('\uFEFF'); } blobContent.push(csvContent); const blob = new Blob(blobContent, { type: "data:text/csv;charset=utf-8" }); const link = document.createElement("a"); const url = URL.createObjectURL(blob); link.setAttribute("href", url); link.setAttribute("download", fileName); const node = document.body.appendChild(link); link.click(); document.body.removeChild(node); } if (isTab) { WMEWAL.writeErrText(w); w.document.write("</tbody></table></body></html>"); w.document.close(); w = null; } } hazards = null; savedHazards = null; } WMEWAL_Hazards.ScanComplete = ScanComplete; function ScanCancelled() { ScanComplete(); } WMEWAL_Hazards.ScanCancelled = ScanCancelled; function initSettings() { settings = { HazardTypeMask: 2047, NoName: false, Regex: null, RegexIgnoreCase: true, State: null, StateOperation: Operation.Equal, EditableByMe: true, CityRegex: null, CityRegexIgnoreCase: true, NoStreet: false, NoCity: false, LastModifiedBy: null, CreatedBy: null, IncludeAlt: false, Created: false, CreatedOperation: Operation.GreaterThanOrEqual, CreatedDate: null, Updated: false, UpdatedOperation: Operation.GreaterThanOrEqual, UpdatedDate: null, StreetRegex: null, StreetRegexIgnoreCase: true }; } function updateProperties() { let upd = false; if (settings !== null) { if (!settings.hasOwnProperty("NoName")) { settings.NoName = false; upd = true; } if (!settings.hasOwnProperty("NoStreet")) { settings.NoStreet = false; upd = true; } if (!settings.hasOwnProperty("NoCity")) { settings.NoCity = false; upd = true; } if (!settings.hasOwnProperty("LastModifiedBy")) { settings.LastModifiedBy = null; upd = true; } if (!settings.hasOwnProperty("IncludeAlt")) { settings.IncludeAlt = false; upd = true; } if (!settings.hasOwnProperty("CreatedDate")) { settings.CreatedDate = null; upd = true; } if (!settings.hasOwnProperty("Created")) { settings.Created = false; upd = true; } if (!settings.hasOwnProperty("CreatedOperation")) { settings.CreatedOperation = Operation.GreaterThanOrEqual; upd = true; } if (!settings.hasOwnProperty("UpdatedDate")) { settings.UpdatedDate = null; upd = true; } if (!settings.hasOwnProperty("Updated")) { settings.Updated = false; upd = true; } if (!settings.hasOwnProperty("UpdatedOperation")) { settings.UpdatedOperation = Operation.GreaterThanOrEqual; upd = true; } if (!settings.hasOwnProperty("StreetRegex")) { settings.StreetRegex = null; upd = true; } if (!settings.hasOwnProperty("StreetRegexIgnoreCase")) { settings.StreetRegexIgnoreCase = true; upd = true; } if (settings.hasOwnProperty("OutputTo")) { delete settings["OutputTo"]; upd = true; } if (settings.hasOwnProperty("Version")) { delete settings["Version"]; upd = true; } } return upd; } //https://waze.com/en-US/editor?env=usa&lat=41.12872&lon=-81.87867&zoomLevel=18&permanentHazards=140620 function getHazardPL(haz) { const latlon = OpenLayers.Layer.SphericalMercator.inverseMercator(haz.pointGeometry.x, haz.pointGeometry.y); return WMEWAL.GenerateBasePL(latlon.lat, latlon.lon, 18) + "&permanentHazards=" + haz.id; } function getIssues(issues) { const issuesList = []; if (issues & Issue.NoName) { issuesList.push("No name"); } if (issues & Issue.AdLocked) { issuesList.push("Ad locked"); } if (issues & Issue.HasUpdateRequests) { issuesList.push("Has update requests"); } if (issues & Issue.MissingHouseNumber) { issuesList.push("Missing house number"); } if (issues & Issue.MissingStreet) { issuesList.push("Missing street"); } if (issues & Issue.NoCity) { issuesList.push("No City"); } if (issues & Issue.NoExternalProviders) { issuesList.push("No external provider IDs"); } if (issues & Issue.PendingApproval) { issuesList.push("Pending approval"); } if (issues & Issue.UndefStreet) { issuesList.push("Undefined street ID"); } if (issues & Issue.NoHours) { issuesList.push("No hours"); } if (issues & Issue.NoPhoneNumber) { issuesList.push("No phone number"); } if (issues & Issue.BadPhoneNumberFormat) { issuesList.push("Bad phone number format"); } if (issues & Issue.NoHours) { issuesList.push("No website"); } if (issues & Issue.NoEntryExitPoints) { issuesList.push("No entry/exit points"); } if (issues & Issue.MissingBrand) { issuesList.push("Missing brand"); } if (issuesList.length === 0) { return "None"; } else { return issuesList.join(", "); } } function updateSettings() { if (typeof Storage !== "undefined") { localStorage[settingsKey] = JSON.stringify(settings); } } function updateSavedSettings() { if (typeof Storage !== "undefined") { localStorage[savedSettingsKey] = WMEWAL.LZString.compressToUTF16(JSON.stringify(savedSettings)); } updateSavedSettingsList(); } function log(level, ...args) { switch (level.toLocaleLowerCase()) { case "debug": case "verbose": console.debug(`${SCRIPT_NAME}:`, ...args); break; case "info": case "information": console.info(`${SCRIPT_NAME}:`, ...args); break; case "warning": case "warn": console.warn(`${SCRIPT_NAME}:`, ...args); break; case "error": console.error(`${SCRIPT_NAME}:`, ...args); break; case "log": console.log(`${SCRIPT_NAME}:`, ...args); break; default: break; } } function nullif(s, nullVal) { if (s !== null && s === nullVal) { return null; } return s; } function loadScriptUpdateMonitor() { let updateMonitor; try { updateMonitor = new WazeWrap.Alerts.ScriptUpdateMonitor(SCRIPT_NAME, SCRIPT_VERSION, DOWNLOAD_URL, GM_xmlhttpRequest); updateMonitor.start(); } catch (ex) { log('error', ex); } } bootstrap(); })(WMEWAL_Hazards || (WMEWAL_Hazards = {}));