您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Shows alt names for selected segments
// ==UserScript== // @name WME Show Alt Names // @description Shows alt names for selected segments // @version 2.0.3.4 // @author The_Cre8r, SAR85 // @copyright SAR85 and The_Cre8r // @license CC BY-NC-ND // @grant none // @include /^https:\/\/(www|beta)\.waze\.com\/(?!user\/)(.{2,6}\/)?editor\/?.*$/ // @namespace https://greasyfork.org/users/9321 // @require https://greasyfork.org/scripts/24851-wazewrap/code/WazeWrap.js // ==/UserScript== // // I would like to thank SAR85 for his hard work for this project, unfortunately with his absence there wasn't anyone // who updated his script. Thank you for downloading this and feel free to contact me for any issues. // // p.s. If SAR85 does return, all of my work may be surrendered. //----------------------------------------------------------------------------------------------- /* global WazeWrap */ /* global W */ /* global OL */ (function () { var $altDiv, $altTable, alternateObjectsArray = [], altLayer, betaEditor, draggableSupported = true, highlightLayer, nameArray = [], selectedSegments = [], CSS = { 'altTable': { 'width': '100%' }, 'altTableClass': { 'border': '1px solid white', 'padding': '3px', 'border-collapse': 'collapse', '-moz-user-select': '-moz-none', '-khtml-user-select': 'none', '-webkit-user-select': 'none' }, 'altTableType': { 'width': '50px' }, 'altTableID': { 'text-align': 'center', 'border-left': 'none', 'width': '80px' }, 'altTableRoadType': { 'border-radius': '10px', 'color': 'black', 'text-shadow': '1px 1px 0 #fff,-1px -1px 0 #fff,1px -1px 0 #fff,-1px 1px 0 #fff,0px 1px 0 #fff,1px 0px 0 #fff,0px -1px 0 #fff,-1px 0px 0 #fff', 'border': '1px solid white', 'font-size': '0.8em', 'text-align': 'center', 'padding': '0 3px 0 3px', 'min-width': '32px' }, 'altTableSelected': { 'font-weight': 'bold', 'background-color': 'white', 'color': 'black' }, 'altDiv': { 'display': 'none', 'position': 'absolute', 'left': '6px', 'bottom': '60px', 'min-height': '120px', 'min-width': '335px', 'overflow-y': 'scroll', 'overflow-x': 'hidden', 'white-space': 'nowrap', 'background-color': 'rgba(0,0,0,0.8)', 'color': 'white', 'padding': '5px' }, 'altDivScrollbar': { 'width': '15px', 'border-radius': '5px' }, 'altDivScrollbarTrack': { 'border-radius': '5px', 'background': 'none', 'width': '10px' }, 'altDivScrollbarThumb': { 'background-color': 'white', 'border-radius': '5px', 'border': '2px solid black' }, 'altDivScrollbarCorner': { 'background': 'none' }, 'altOptionsButton': { 'margin': '0 0 3px 3px', 'height': '2em', 'font-size': '0.8em' }, 'autoSelectButton': { 'display': 'none' }, 'optionsDiv': { 'display': 'none', 'clear': 'both', 'border': '1px solid white', 'padding': '3px', 'margin': ' 0 0 3px 0', 'font-weight': 'normal' }, 'optionsDivTd': { 'padding-right': '5px' }, 'optionsDivInput': { 'margin-right': '3px' } }, ROAD_TYPES = { 1: { name: 'St', expColor: '#FFFFDD' }, 2: { name: 'PS', expColor: '#FDFAA7' }, 3: { name: 'Fwy', expColor: '#6870C3' }, 4: { name: 'Rmp', expColor: '#B3BFB3' }, 5: { name: 'Trl', expColor: '#B0A790' }, 6: { name: 'MH', expColor: '#469FBB' }, 7: { name: 'mH', expColor: '#69BF88' }, 8: { name: 'Dirt', expColor: '#867342' }, 10: { name: 'Bdwk', expColor: '#9A9A9A' }, 16: { name: 'Stwy', expColor: '#9A9A9A' }, 17: { name: 'Pvt', expColor: '#BEBA6C' }, 18: { name: 'RR', expColor: '#B2B6B4' }, 19: { name: 'Rwy', expColor: '#222222' }, 20: { name: 'PLR', expColor: '#ABABAB' } //add ferry }; /** * Returns a random color in rgba() notation. * @param {Number} opacity The desirected opacity (a value of the color). * returns {String} The rgba() color. */ function randomRgbaColor(opacity) { opacity = opacity || 0.8; function random255() { return Math.floor(Math.random() * 255); } return 'rgba(' + random255() + ',' + random255() + ',' + random255() + ',' + opacity + ')'; } /** * Resets the renderIntent of all features on altLayer. * @param {String} intent Optional parameter specifying the intent. The * default intent is 'default'. */ function resetRenderIntent(intent) { var i, n; intent = intent || 'default'; for (i = 0, n = altLayer.features.length; i < n; i++) { altLayer.features[i].renderIntent = intent; } } /** * Pans the map to a segment specified by its ID. * @param {Number} id The ID of the segment. */ function panToSegment(id) { var segment = id && W.model.segments.getObjectById(id); return segment && W.map.moveTo(segment.geometry.getBounds().getCenterLonLat()); } /** * Selects a segment specified by its ID. * @param {Number} id The ID of the segment. */ function selectSegment(id) { var seg = id && W.model.segments.getObjectById(id); debugger; return seg && W.selectionManager.setSelectedModels([seg]); } /** * Event handler for changing the highlight color for a segment. * @param {Event} event */ function changeHighlightColor(event) { var i, n, $this = $(this), name = $this.find('.altTable-primary-name').text() || $this.find('.altTable-alt-name').text(), city = $this.find('.altTable-primary-city').text() || $this.find('.altTable-alt-city').text(), useCity = $('#altUseCity').prop('checked'); for (i = 0, n = nameArray.length; i < n; i++) { if (nameArray[i].name === name && (useCity ? nameArray[i].city === city : true)) { nameArray[i].color = randomRgbaColor(); colorTable(); $this.trigger('mouseenter', { singleSegment: false }); break; } } } /** * Lookup function for the display color of a specified road type. Will * return the color for the experimental layer if it is activated, * otherwise it will return the color for the old roads layer. * @param {Number} type The roadType to look up. * @returns {Object} Object of form: * {typeString: 'RoadTypeName', typeColor: '#FFFFFF'}, * where RoadTypeName is an abbreviated form of the name of the road type * and typeColor is the hex value of the display color. */ function getRoadColor(type) { if (type && undefined !== typeof ROAD_TYPES[type]) { return { typeString: ROAD_TYPES[type].name, typeColor: ROAD_TYPES[type].expColor }; } else { return { typeString: 'error', typeColor: ROAD_TYPES[1].expColor }; } } /** * Data structure for segment information used to build highlight layer * features and the alternate names table. * @class * @param {Waze.Feature.Vector.Segment} The segment feature on which to * base the new instance. */ function Alternate(baseFeature) { var i, n, street, city; if (!baseFeature) { return; } this.attributes = baseFeature.model.attributes; this.segmentID = this.attributes.id; // Make a feature for highlighting on the map. this.layerFeature = new OL.Feature.Vector(baseFeature.geometry.clone(), this.attributes); // Store segment name information. street = W.model.streets.getObjectById(this.attributes.primaryStreetID); city = street && W.model.cities.objects[street.cityID].attributes; this.primaryName = street ? street.name || 'No name' : 'New road'; this.primaryCity = city ? city.name || 'No city' : 'New road'; this.alternates = []; for (i = 0, n = this.attributes.streetIDs.length; i < n; i++) { street = W.model.streets.getObjectById(this.attributes.streetIDs[i]); city = street && W.model.cities.objects[street.cityID].attributes; this.alternates.push({ name: street ? street.name || 'No name' : 'New road', city: city ? city.name || 'No city' : 'New road' }); } // Make a table row for displaying segment data. this.tableRow = this.createTableRow(); // Add name info to attributes of layer feature and add the feature to the layer. // (For compatibility with highlighting functions--old method) this.layerFeature.attributes.alt = this.alternates; this.layerFeature.attributes.primary = { name: this.primaryName, city: this.primaryCity }; } Alternate.prototype = /** @lends Alternate.prototype */ { createTableRow: function () { var i, n, $row, $cell, roadType; $row = $('<tr/>').attr('id', 'alt' + this.segmentID); //add road type to row roadType = getRoadColor(this.layerFeature.attributes.roadType); $cell = $('<td/>') .addClass('altTable-type') .css('border-right', 'none') .append($('<div/>') .addClass('altTable-roadType') .css('background-color', roadType.typeColor) .text(roadType.typeString)) .append($('<div/>') .css({ 'text-align': 'center', 'font-size': '0.8em' }) .text(this.layerFeature.attributes.length + ' m') ); $row.append($cell); //add id to row $cell = $('<td/>') .addClass('altTable-id').css('border-left', 'none') .append($('<div/>') .text(this.segmentID)); $row.append($cell); //add primary name and city to row $cell = $('<td/>').addClass('altTable-primary') .append($('<div/>') .addClass('altTable-primary-name') .text(this.primaryName)) .append($('<div/>') .addClass('altTable-primary-city') .text(this.primaryCity) ); $row.append($cell); //add alt names and cities to row for (i = 0, n = this.alternates.length; i < n; i++) { $cell = $('<td/>').addClass('altTable-alt') .append($('<div/>').addClass('altTable-alt-name').text(this.alternates[i].name)) .append($('<div/>').addClass('altTable-alt-city').text(this.alternates[i].city) ); $row.append($cell); } return $row; } }; /** * Colors the table cells based on segment/city name. */ function colorTable() { 'use strict'; var i, n, useCity = $('#altUseCity').prop('checked'); $altTable.find('.altTable-primary, .altTable-alt').each(function (index1) { var $this = $(this), name = $this.find('.altTable-primary-name').text() || $this.find('.altTable-alt-name').text(), city = $this.find('.altTable-primary-city').text() || $this.find('.altTable-alt-city').text(), match = false, color; for (i = 0, n = nameArray.length; i < n; i++) { if (nameArray[i].name === name && (useCity ? nameArray[i].city === city : true)) { $this.css('background-color', nameArray[i].color); match = true; break; } } if (match === false) { color = randomRgbaColor(); $this.css('background-color', color); nameArray.push({ name: name, city: city, color: color }); } match = false; }); } /** * Populates the table with segment information * @param {Number} maxAlternates The maxiumum number of alternates * a segment has (how many "Alt" columns are needed). * @param {Boolean} sortByNode Whether to sort the table in driving * order by node ID. */ function populateTable(maxAlternates, sortByNode) { 'use strict'; var i, n, j, m, $row; // Empty table contents. $altTable.find('tbody').empty(); $('.altTable-header-alt').remove(); // Sort if needed. if (sortByNode) { sortSegmentsByNode(); } // Add table rows for each segment. for (i = 0, n = selectedSegments.length; i < n; i++) { $row = selectedSegments[i].tableRow.clone(); for (j = selectedSegments[i].alternates.length, m = maxAlternates; j < m; j++) { $row.append($('<td/>').addClass('altTable-placeholder')); } $altTable.append($row); } // Add column headings for alt names. for (i = 1, n = maxAlternates; i <= n; i++) { $('#altTable-header').append($('<th/>') .addClass('altTable-header-alt') .text('Alt ' + i)); } $('#altShowCity').change(); colorTable(); } /** * Callback for hovering over segment name in the table that * colors all features on the altLayer with the same name/city as the * one being hovered over. * @callback * @param {jQuery} $el The cell from the table to match. * @param {String} color The rgba-formatted color value. */ function colorFeatures($el, color) { 'use strict'; var i, n, j, t, colorValues, feature, names, name = $el.find('.altTable-primary-name').text() || $el.find('.altTable-alt-name').text(), city = $el.find('.altTable-primary-city').text() || $el.find('.altTable-alt-city').text(), useCity = $('#altUseCity').prop('checked'); //remove opacity from color so it can be controlled by layer style colorValues = color.match(/\d+/g); color = 'rgb(' + colorValues[0] + ',' + colorValues[1] + ',' + colorValues[2] + ')'; for (i = 0, n = altLayer.features.length; i < n; i++) { feature = altLayer.features[i]; //combine primary and alt names in one array names = feature.attributes.alt.concat(feature.attributes.primary); //test names for match for (j = 0, t = names.length; j < t; j++) { if (names[j].name === name && (useCity ? names[j].city === city : true)) { feature.attributes.bgColor = color; feature.renderIntent = 'highlight'; } } } } /** * Callback for hovering over a segment ID in the table. Highlights the * corresponding altLayer feature black or as specified. * @callback * @param {Number} id The segment ID to highlight. * @param {String} color The rgba-formatted color (optional--default is * black). */ function colorSegment(id, color) { 'use strict'; var i, n, feature; color = color || 'rgba(0, 0, 0, 0.8)'; for (i = 0, n = altLayer.features.length; i < n; i++) { feature = altLayer.features[i]; if (feature.attributes.id == id) { feature.attributes.bgColor = color; feature.renderIntent = 'highlight'; break; } } } /** * Handles table events for hovering and calls appropriate function * for highlighting. * @callback * @param {Event} event The event object. */ function applyHighlighting(event) { var $this1, name1, city1, useCity = $('#altUseCity').prop('checked'); switch (event.type) { case 'mouseenter': $this1 = $(this); if (event.data.singleSegment) { colorSegment($this1.text()); $this1.parent().addClass('altTable-selected'); } else { colorFeatures($this1, $this1.css('background-color')); if ($this1.hasClass('altTable-primary')) { name1 = $this1.find('.altTable-primary-name').text(); city1 = $this1.find('.altTable-primary-city').text(); } else { name1 = $this1.find('.altTable-alt-name').text(); city1 = $this1.find('.altTable-alt-city').text(); } $('#altTable tbody td').each(function (index) { var $this2 = $(this), name2 = $this2.find('.altTable-primary-name').text() || $this2.find('.altTable-alt-name').text(), city2 = $this2.find('.altTable-primary-city').text() || $this2.find('.altTable-alt-city').text(); if (name1 === name2 && (useCity ? city1 === city2 : true)) { $this2.parent().addClass('altTable-selected'); } }); } break; case 'mouseleave': resetRenderIntent(); $('#altTable tr').each(function (index) { $(this).removeClass('altTable-selected'); }); break; } altLayer.redraw(); } function isSegmentSelected(){ if(W.selectionManager.hasSelectedFeatures() === false) return false; for(i=0; i<WazeWrap.getSelectedFeatures().length; i++){ if(WazeWrap.getSelectedFeatures()[i].model.type === "segment") return true; } } /** * Event handler for selection events. Checks for appropriate condions * for running script, creates Alternate objects as necessary, * displays/hides UI elements. * @callback */ function checkSelection() { var i, n, j, m, alternate, thisItem, maxAlternates = 0, selectedItems; selectedSegments = []; //$('#altAutoSelect').hide(); if (isSegmentSelected() && altLayer.getVisibility()) { selectedItems = WazeWrap.getSelectedFeatures(); if (selectedItems.length > 1) { $('#altAutoSelect').show(); } for (i = 0, n = selectedItems.length; i < n; i++) { thisItem = selectedItems[i]; if (thisItem.model.type === 'segment') { for (j = 0, m = alternateObjectsArray.length; j < m; j++) { if (alternateObjectsArray[j].segmentID === thisItem.model.attributes.id) { if (alternateObjectsArray[j].layerFeature.attributes.updatedOn !== thisItem.model.attributes.updatedOn) { alternateObjectsArray.splice(j, 1); } else { alternate = alternateObjectsArray[j]; continue; } } } if (!alternate) { alternate = new Alternate(thisItem); alternateObjectsArray.push(alternate); } selectedSegments.push(alternate); altLayer.addFeatures(alternate.layerFeature); if (maxAlternates < alternate.alternates.length) { maxAlternates = alternate.alternates.length; selectedSegments.maxAlternates = maxAlternates; } alternate = null; } } populateTable(maxAlternates, $('#altSortByNode').prop('checked')); $altDiv.fadeIn(); } else { $altDiv.fadeOut(); altLayer.removeAllFeatures(); if (alternateObjectsArray.length > 100) { alternateObjectsArray = []; } } } /** * Checks all segments in WME for alt names and adds feature for * highlighting. */ function checkAllSegments() { 'use strict'; var segID, segment; highlightLayer.removeAllFeatures(); if (altLayer.getVisibility() && $('#altHighlights').prop('checked')) { for (segID in W.model.segments.objects) { segment = W.model.segments.objects[segID]; if (W.model.segments.objects.hasOwnProperty(segID) && segment.attributes.streetIDs.length > 0) { highlightLayer.addFeatures(new OL.Feature.Vector(segment.geometry.clone())); } } } } /** * Sorts the selected segments in "driving order" starting with first * selected based on Node ID. */ function sortSegmentsByNode(useFromNode) { 'use strict'; var path = [], startingNodeID = useFromNode ? selectedSegments[0]. attributes.fromNodeID : selectedSegments[0].attributes.toNodeID; var findNextSegment = function (nodeID) { var fromNodeMatched = false, nextNode, tempPath = [], toNodeMatched = false; _.each(selectedSegments, function (segment) { console.debug('Checking segment ' + segment.attributes.id); if (path.indexOf(segment.attributes.id) !== -1) { console.debug('Segment already in path.'); return; } if (segment.attributes.fromNodeID === nodeID) { fromNodeMatched = true; tempPath.push(segment); } else if (segment.attributes.toNodeID === nodeID) { toNodeMatched = true; tempPath.push(segment); } }); if (tempPath.length === 1) { path.push(tempPath[0].attributes.id); if (fromNodeMatched) { nextNode = tempPath[0].attributes.toNodeID; } else if (toNodeMatched) { nextNode = tempPath[0].attributes.fromNodeID; } findNextSegment(nextNode); } }; path.push(selectedSegments[0].attributes.id); console.debug('Looking for connected segments at node ' + startingNodeID); findNextSegment(startingNodeID); if (path.length === 0 && !useFromNode) { console.debug('No connections at toNode. Looking at fromNode.'); sortSegmentsByNode(true); } else { selectedSegments = _.sortBy(selectedSegments, function (segment) { return path.indexOf(segment.attributes.id); }); } } /** * Uses WazeWrap to fetch route from routing server based on selected segments * then attempts to select the route segments. */ function performAutoSelect() { 'use strict'; var i, options, route, segmentsToSelect = [], selection = WazeWrap.getSelectedFeatures(), n = selection.length; /** * Callback for the route selection utility. Gets the segment IDs of * segments on the route, checks to see if they are loaded in WME and * pushes them to array for selection later. * @callback */ function routeCallback() { var segIDs, seg; segIDs = this.getRouteSegmentIDs()[0]; segIDs.forEach(function (item) { seg = W.model.segments.getObjectById(item); if (seg) { segmentsToSelect.push(seg); } }); if (this.last) { W.selectionManager.setSelectedModels(segmentsToSelect); } } /** * Fetches the route (or sub-route) via WazeWrap. * @param {OpenLayers.Feature.Vector} start The starting segment of the * sub-route. * @param {OpenLayers.Feature.Vector} end The ending segment of the * sub-route. * @param {Boolean} last Whether the sub-route is the last of the total * route. * @param {Number} The timeout before fetching the route in * milliseconds. */ function fetchRoute(start, end, last, timeout) { window.setTimeout(function () { route = new WazeWrap.Model.RouteSelection(start, end, routeCallback, options); route.last = last ? true : false; }, timeout); } if (n > 0) { options = { fastest: $('#altFastest').prop('checked'), tolls: $('#altAvoidTolls').prop('checked'), freeways: $('#altAvoidFreeways').prop('checked'), dirt: $('#altAvoidDirt').prop('checked'), longtrails: $('#altAvoidLongDirt').prop('checked'), uturns: $('#altAllowUturns').prop('checked') }; for (i = 0; i < n - 1; i++) { if ('segment' === selection[i].model.type && 'segment' === selection[i + 1].model.type) { fetchRoute(selection[i], selection[i + 1], i === n - 2 ? true : false, 1000 * i); } } } } /** * Saves checkbox states to localStorage. */ function saveOptions(event) { 'use strict'; var options = { fastest: $('#altFastest').prop('checked'), tolls: $('#altAvoidTolls').prop('checked'), freeways: $('#altAvoidFreeways').prop('checked'), dirt: $('#altAvoidDirt').prop('checked'), longtrails: $('#altAvoidLongDirt').prop('checked'), uturns: $('#altAllowUturns').prop('checked'), highlights: $('#altHighlights').prop('checked'), sortByNode: $('#altSortByNode').prop('checked'), useCity: $('#altUseCity').prop('checked'), showCity: $('#altShowCity').prop('checked') }; return window.localStorage.altNamesOptions = JSON.stringify(options); } /** * Loads checkbox states from localStorage. */ function loadOptions() { 'use strict'; var options; if ("undefined" !== typeof window.localStorage.altNamesOptions) { console.log("Shouldn't be getting here"); options = JSON.parse(window.localStorage.altNamesOptions); if (undefined !== typeof options.fastest) { $('#altFastest').prop('checked', options.fastest); } if (undefined !== typeof options.tolls) { $('#altAvoidTolls').prop('checked', options.tolls); } if (undefined !== typeof options.freeways) { $('#altAvoidFreeways').prop('checked', options.freeways); } if (undefined !== typeof options.dirt) { $('#altAvoidDirt').prop('checked', options.dirt); } if (undefined !== typeof options.longrails) { $('#altAvoidDirt').prop('checked', options.longtrails); } if (undefined !== typeof options.uturns) { $('#altAllowUturns').prop('checked', options.uturns); } if (undefined !== typeof options.highlights) { $('#altHighlights').prop('checked', options.highlights); } if (undefined !== typeof options.sortByNode) { $('#altSortByNode').prop('checked', options.sortByNode); } if (undefined !== typeof options.useCity) { $('#altUseCity').prop('checked', options.useCity); } if (undefined !== typeof options.useCity) { $('#altShowCity').prop('checked', options.showCity); } } } /** * Shows alert box with version information and changes. */ function updateAlert() { var altVersion = GM_info.script.version, alertOnUpdate = true, versionChanges = 'WME Show Alt Names has been updated to ' + altVersion + '.\n'; versionChanges += 'Changes:\n'; versionChanges += '[*] Updating to support latest WME changes (2018-04-24).\n'; if (alertOnUpdate && window.localStorage && window.localStorage.altVersion !== altVersion) { window.localStorage.altVersion = altVersion; alert(versionChanges); } } /** * Initializes the script by adding CSS and HTML to page, registering event * listeners, adding map layers, checking for beta editor, and running main * functions to check for segments loaded at init and highlighting. */ function init() { var altStyleMap, css, $header, highlightStyle, optionsHTML, $row; css = '#altTable {width: 100%;}'; css += '.altTable, .altTable th, .altTable td {border: 1px solid white; padding: 3px; border-collapse: collapse; -moz-user-select: -moz-none; -khtml-user-select: none; -webkit-user-select: none;}\n'; css += '.altTable-type {width: 50px;}\n'; css += '.altTable-id {text-align: center; border-left: none; width: 80px}\n'; css += '.altTable-roadType {border-radius: 10px; color: black; text-shadow: 1px 1px 0 #fff,-1px -1px 0 #fff,1px -1px 0 #fff,-1px 1px 0 #fff,0px 1px 0 #fff,1px 0px 0 #fff,0px -1px 0 #fff,-1px 0px 0 #fff; border: 1px solid white; font-size: 0.8em; text-align: center; padding: 0 3px 0 3px; min-width: 32px;}'; css += 'tr.altTable-selected > .altTable-id {font-weight: bold; background-color: white; color: black;}\n'; css += '#altDiv {display: none; position: absolute; left: 6px; bottom: 60px; min-height: 120px; min-width: 335px;'; css += 'overflow-y: scroll; overflow-x: hidden; white-space: nowrap; background-color: rgba(0,0,0,0.8); color: white; padding: 5px; '; css += 'z-index: 1001; border-radius: 5px; max-height: 60%;}\n'; // scroll bar CSS css += '#altDiv::-webkit-scrollbar {width: 15px; border-radius: 5px;}\n'; css += '#altDiv::-webkit-scrollbar-track {border-radius: 5px; background: none; width: 10px;}\n'; css += '#altDiv::-webkit-scrollbar-thumb {background-color: white; border-radius: 5px; border: 2px solid black;}\n'; css += '#altDiv::-webkit-scrollbar-corner {background: none;}\n'; // buttons css css += '.altOptions-button {margin: 0 0 3px 3px; height: 2em; font-size: 0.8em;}\n'; // Options Menu CSS css += '#optionsDiv {display: none; clear: both; border: 1px solid white; padding: 3px; margin: 0 0 3px 0; font-weight: normal;}'; css += '#optionsDiv td {padding-right: 5px;}'; css += '#optionsDiv input {margin-right: 3px;}'; //add css to page $('<style/>').html(css).appendTo($(document.head)); // Make the options menu. optionsHTML = '<div id="altOptions"> <button id="altAutoSelect" class="altOptions-button" style="display: none;">Auto Select</button> <label style="float: right; margin: 3px;"> <input id="altHighlights" type="checkbox">Highlight Alt Names</label> <button id="altOptionsButton" class="altOptions-button" style="float: right;">Show Options</button> </div> <div id="optionsDiv"> <div> <label style="font-weight: normal;"> <input type="checkbox" id="altShowCity">Show city name in table</label> </div> <div> <label style="font-weight: normal;"> <input type="checkbox" id="altUseCity">Use city name in name matching</label> </div> <div> <label style="font-weight: normal;"> <input type="checkbox" id="altSortByNode">Sort table by driving order (experimental)</label> </div> <form> <table> <thead> <tr> <td colspan="2" style="text-align: center; text-decoration: underline; font-weight: bold;">Auto Selection Route Options</td> </tr> </thead> <tbody> <tr> <td> <input type="checkbox" id="altAvoidTolls">Avoid toll roads</td> <td> <input type="checkbox" id="altAvoidFreeways">Avoid freeways</td> </tr> <tr> <td> <input type="checkbox" id="altAvoidLongDirt">Avoid long dirt roads</td> <td> <input type="checkbox" id="altAvoidDirt">Avoid dirt roads</td> </tr> <tr> <td> <input type="checkbox" id="altAllowUturns">Allow U-turns</td> <td> <input type="checkbox" id="altFastest">Fastest route</td> </tr> </tbody> </table> </form> </div>'; //optionsHTML = '<div id="altOptions"><label style="float: left; margin: 3px;"> <input id="altHighlights" type="checkbox">Highlight Alt Names</label> <button id="altOptionsButton" class="altOptions-button" style="float: left;">Show Options</button> </div> <div id="optionsDiv"> <div> <label style="font-weight: normal;"> <input type="checkbox" id="altShowCity">Show city name in table</label> </div> <div> <label style="font-weight: normal;"> <input type="checkbox" id="altUseCity">Use city name in name matching</label> </div> <div> <label style="font-weight: normal;"> <input type="checkbox" id="altSortByNode">Sort table by driving order (experimental)</label> </div> </div>'; // Make the table to hold segment information. $altTable = $('<table/>').attr('id', 'altTable').addClass('altTable'); $header = $('<thead/>'); $row = $('<tr/>').attr('id', 'altTable-header'); $row.append($('<th/>').attr('colspan', '2').text('Segment ID')); $row.append($('<th/>').text('Primary')); $header.append($row); $altTable.append($header); $altTable.append('<tbody/>'); // Make the main div to hold script content. $altDiv = $('<div/>').attr('id', 'altDiv'); $altDiv.append(optionsHTML); $altDiv.append($altTable); $altDiv.appendTo($('#WazeMap')); $('#altAutoSelect').click(performAutoSelect); $('#altOptionsButton').click(function () { var $optionsDiv = $('#optionsDiv'); if ($optionsDiv.css('display') === 'none') { $optionsDiv.show(); $(this).text('Hide Options'); } else { $optionsDiv.hide(); $(this).text('Show Options'); } }); $('#altSortByNode').on('change', checkSelection); $('#altHighlights').on('change', checkAllSegments); $('#altUseCity').on('change', colorTable); $('#altShowCity').on('change', function () { if ($(this).prop('checked')) { $altTable.find('.altTable-primary-city, .altTable-alt-city').each(function () { $(this).show(); }); } else { $altTable.find('.altTable-primary-city, .altTable-alt-city').each(function () { $(this).hide(); }); } }); $('#optionsDiv input[type=checkbox], #altOptions input[type=checkbox]').on('change', saveOptions); $altDiv.on('mouseenter mouseleave', 'td.altTable-primary, td.altTable-alt', { singleSegment: false }, applyHighlighting); $altDiv.on('dblclick', 'td.altTable-primary, td.altTable-alt', null, changeHighlightColor); $altDiv.on('mouseenter mouseleave', 'td.altTable-id', { singleSegment: true }, applyHighlighting); $altDiv.on('click', 'td.altTable-id', null, function () { panToSegment($(this).text()); }); $altDiv.on('dblclick', 'td.altTable-id', null, function () { selectSegment($(this).text()); }); // Make $altDiv resizable and draggable try { $altDiv.one('resize', function () { $(this).css('max-height', '100%'); }); $altDiv.resizable({ handles: 'all', containment: 'parent' }); $altDiv.one('drag', function () { $(this).css('bottom', 'auto'); }); $altDiv.draggable({ containment: 'parent' }); } catch (err) { draggableSupported = false; }; // Create the map layers for segment highlighting. altStyleMap = new OL.StyleMap({ default: new OL.Style({ stroke: false }), highlight: new OL.Style({ stroke: true, strokeWidth: 20, strokeColor: '${bgColor}', strokeOpacity: 1, strokeLinecap: 'round' }) }); altLayer = new OL.Layer.Vector('WME Show Alt Names', { styleMap: altStyleMap }); altLayer.events.register('visibilitychanged', null, checkSelection); highlightStyle = new OL.StyleMap({ default: new OL.Style({ strokeWidth: 20, strokeColor: '#FFFFFF', strokeOpacity: 0.4 }) }); highlightLayer = new OL.Layer.Vector('WME Show Alt Names - Highlight All', { styleMap: highlightStyle, displayInLayerSwitcher: false }); W.map.addLayers([highlightLayer, altLayer]); //check for beta editor due to road layer name differences if (location.href.match(/-beta/)) { betaEditor = true; } //register WME event listeners W.loginManager.events.register('afterloginchanged', null, init); W.selectionManager.events.register('selectionchanged', null, checkSelection); W.map.events.register('moveend', null, checkAllSegments); W.map.getLayersByName('Roads')[0].events.register('visibilitychanged', null, checkSelection); // Ready to go. Alert user to any updates and check for selected segments. updateAlert(); loadOptions(); checkAllSegments(); checkSelection(); } /** * Checks for key components of the page before initializing the script. */ function bootstrap(tries = 1) { console.log("bootstrap attempt "+ tries); if (W && W.map && W.model && W.loginManager.user && $ && WazeWrap.Ready) { init(); } else if (tries < 1000) { setTimeout(() => bootstrap(tries++), 200); } } bootstrap(); })();