您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Shows alt names for selected segments
当前为
/* global wLib */ /* global W */ /* global $ */ /* global jQuery */ /* global OL */ // ==UserScript== // @name WME Show Alt Names // @description Shows alt names for selected segments // @version 0.30 // @author SAR85 // @copyright SAR85 // @license CC BY-NC-ND // @grant none // @include https://www.waze.com/editor/* // @include https://www.waze.com/*/editor/* // @include https://editor-beta.waze.com/* // @namespace https://greasyfork.org/users/9321 // @require https://greasyfork.org/scripts/9794-wlib/code/wLib.js?version=52323 // ==/UserScript== var altLayer, $altDiv, betaEditor, $optionsDiv, $altTable, nameArray = [], alternateObjectsArray = [], selectedSegments = []; /** * 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 * parameter 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.get(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.get(id); return seg && W.selectionManager.select([seg]); } /** Event handler for changing the highlight color for a segment. * @param {Event} event */ function changeHighlightColor(event) { var i, n, $this = $(this); for (i = 0, n = nameArray.length; i < n; i++) { if (nameArray[i].name === $this.text()) { 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 the type * color is the hex value of the display color. */ function getRoadColor(type) { var roadTypes = { 1: { name: 'St', color: '#FFFFFF', expColor: '#FFFFDD' }, 2: { name: 'PS', color: '#CBA12E', expColor: '#FDFAA7' }, 3: { name: 'Fwy', color: '#387FB8', expColor: '#6870C3' }, 4: { name: 'Rmp', color: '#8FB838', expColor: '#B3BFB3' }, 5: { name: 'Trl', color: '#E6E6E6', expColor: '#B0A790' }, 6: { name: 'MH', color: '#C13040', expColor: '#469FBB' }, 7: { name: 'mH', color: '#ECE589', expColor: '#69BF88' }, 8: { name: 'Dirt', color: '#E6E6E6', expColor: '#867342' }, 10: { name: 'Bdwk', color: '#E6E6E6', expColor: '#9A9A9A' }, 16: { name: 'Stwy', color: '#E6E6E6', expColor: '#9A9A9A' }, 17: { name: 'PvR', color: '#E6E6E6', expColor: '#BEBA6C' }, 18: { name: 'RR', color: '#E6E6E6', expColor: '#B2B6B4' }, 19: { name: 'Rwy', color: '#E6E6E6', expColor: '#222222' }, 20: { name: 'PLR', color: '#E6E6E6', expColor: '#ABABAB' } //add ferry }, roadExpName = betaEditor ? 'Roads' : 'Roads experimental', roadsExpLayerVisible = W.map.getLayersByName(roadExpName)[0].getVisibility(); if (type && undefined !== typeof roadTypes[type]) { return { typeString: roadTypes[type].name, typeColor: roadsExpLayerVisible ? roadTypes[type].expColor : roadTypes[type].color }; } else { return { typeString: 'error', typeColor: roadTypes[1] }; } } /** * 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.segmentID = baseFeature.model.attributes.id; // Make a feature for highlighting on the map. this.layerFeature = new OL.Feature.Vector(baseFeature.geometry.clone(), baseFeature.model.attributes); // Store segment name information. street = W.model.streets.get(this.layerFeature.attributes.primaryStreetID); city = street && W.model.cities.get(street.cityID); this.primaryName = street ? street.name || 'No name' : 'error'; this.primaryCity = city ? city.name || 'No city' : 'error'; this.alternates = []; for (i = 0, n = this.layerFeature.attributes.streetIDs.length; i < n; i++) { street = W.model.streets.get(this.layerFeature.attributes.streetIDs[i]); city = street && W.model.cities.get(street.cityID); this.alternates.push({ name: street ? street.name || 'No name' : 'error', city: city ? city.name || 'No city' : 'error' }); } // 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/>') .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; } }; function colorTable() { 'use strict'; var i, n, $table = $('#altTable'); $table.find('.altTable-primary, .altTable-alt').each(function (index1) { var $this = $(this), text = $this.text(), match = false, color; for (i = 0, n = nameArray.length; i < n; i++) { if (nameArray[i].name === text) { $this.css('background-color', nameArray[i].color); match = true; break; } } if (match === false) { color = randomRgbaColor(); $this.css('background-color', color); nameArray.push({ name: text, color: color }); } match = false; }); } function populateTable(maxAlternates) { 'use strict'; var i, n, j, m, $row; $altTable.find('tbody').empty(); $('.altTable-header-alt').remove(); 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); } for (i = 1, n = maxAlternates; i <= n; i++) { $('#altTable-header').append($('<th/>') .addClass('altTable-header-alt') .text('Alt ' + i)); } colorTable(); } function colorFeatures(nameToMatch, color) { 'use strict'; var i, n, j, t, colorValues, feature, names, nameCityCombined; //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.clone(); names.push(feature.attributes.primary); //test names for match for (j = 0, t = names.length; j < t; j++) { //combine street and city name (as in text of table cell) nameCityCombined = names[j].name + names[j].city; if (nameCityCombined === nameToMatch) { feature.attributes.bgColor = color; feature.renderIntent = 'highlight'; } } } } 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; } } } function applyHighlighting(event) { var $this1; switch (event.type) { case 'mouseenter': $this1 = $(this); if (event.data.singleSegment) { colorSegment($this1.text()); } else { colorFeatures($this1.text(), $this1.css('background-color')); } $('#altTable tbody td').each(function (index) { var $this2 = $(this); if ($this1.text() === $this2.text()) { $this2.parent().addClass('altTable-selected'); } }); break; case 'mouseleave': resetRenderIntent(); $('#altTable tr').each(function (index) { $(this).removeClass('altTable-selected'); }); break; } altLayer.redraw(); } function checkSelection() { var i, n, j, m, alternate, thisItem, maxAlternates = 0, selectedItems; selectedSegments = []; if (W.selectionManager.hasSelectedItems() && altLayer.getVisibility()) { selectedItems = W.selectionManager.selectedItems; 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; } alternate = null; } } populateTable(maxAlternates); $altDiv.fadeIn(); } else { $altDiv.fadeOut(); $('#altAutoSelect').hide(); altLayer.removeAllFeatures(); if (alternateObjectsArray.length > 50) { alternateObjectsArray = []; } } } function performAutoSelect() { 'use strict'; var i, options, route, segmentsToSelect = [], selection = W.selectionManager.selectedItems, n = selection.length; function routeCallback() { var segIDs, seg; segIDs = this.getRouteSegmentIDs()[0]; segIDs.forEach(function (item) { seg = W.model.segments.get(item); if (seg) { segmentsToSelect.push(seg); } }); if (this.last) { W.selectionManager.select(segmentsToSelect); } } 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) { route = new wLib.Model.RouteSelection(selection[i], selection[i + 1], routeCallback, options); route.last = i === n - 2 ? true : false; } } } } function updateAlert() { var altVersion = "0.30", alertOnUpdate = true, versionChanges = 'WME Show Alt Names has been updated to ' + altVersion + '.\n'; versionChanges += 'Changes:\n'; versionChanges += '[*]Auto Select Tool: Multi-point routes can now be selected.\n'; versionChanges += '[*]Auto Select Tool: Added route options.\n'; versionChanges += '[*]New feature: Adds segment length to table display.\n'; versionChanges += '[*]Removed zoom limit: table is now displayed at any zoom when a segment is selected.\n'; versionChanges += '[*]Bug fix: Off-screen and out-of-range segments should now work.\n'; versionChanges += '[*]Table sorting functionality has been removed.'; if (alertOnUpdate && window.localStorage && window.localStorage.altVersion !== altVersion) { window.localStorage.altVersion = altVersion; alert(versionChanges); } } function init() { var altStyleMap, css, $header, optionsHTML, $optionsDiv, $optionsElement, $row; 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-id {text-align: center; border-left: none;}\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; height: auto; width: auto; '; 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: 50%;}\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 td {padding-right: 5px;}'; css += '#optionsDiv table input {margin-right: 3px;}'; //add css to page $('<style/>').html(css).appendTo($(document.head)); // Make some buttons. $optionsElement = $('<div/>').attr('id', 'altOptions'); $optionsElement.append( $('<button/>') .attr('id', 'altAutoSelect') .css('display', 'none') .addClass('altOptions-button') .text('Auto Select') .click(performAutoSelect) ); $optionsElement.append( $('<button/>') .addClass('altOptions-button') .css('float', 'right') .text('Show Options') .click(function () { if ($optionsDiv.css('display') === 'none') { $optionsDiv.show(); $(this).text('Hide Options'); } else { $optionsDiv.hide(); $(this).text('Show Options'); } }) ); // Make the options menu. optionsHTML = '<html> <body> <div> <form> <table> <thead> <tr> <td colspan="2" style="font-weight: bold;">Auto Selection Route Options</td> </tr> </thead> <tbody> <tr> <td> <input type="checkbox" id="altAvoidTolls"></input>Avoid toll roads</td> <td> <input type="checkbox" id="altAvoidFreeways" </input>Avoid freeways</td> </tr> <tr> <td> <input type="checkbox" id="altAvoidLongDirt"></input>Avoid long dirt roads</td> <td> <input type="checkbox" id="altAvoidDirt"></input>Avoid dirt roads</td> </tr> <tr> <td> <input type="checkbox" id="altAllowUturns"></input>Allow U-turns</td> <td> <input type="checkbox" id="altFastest"></input>Fastest route</td> </tr> </tbody> </table> </form> </div> </body> </html>'; $optionsDiv = $('<div/>') .attr('id', 'optionsDiv') .css({ 'display': 'none', 'border': '1px solid white', 'padding': '3px', 'margin': '0 0 3px 0' }); $optionsDiv.append(optionsHTML); // 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($optionsElement); $altDiv.append($optionsDiv); $altDiv.append($altTable); $altDiv.appendTo($('#WazeMap')); $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()); }); // Create the map layer 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); W.map.addLayer(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.getLayersByName('Roads')[0].events.register('visibilitychanged', null, checkSelection); if (betaEditor) { W.map.getLayersByName('Roads')[0].events.register('visibilitychanged', null, checkSelection); } else { W.map.getLayersByName('Roads experimental')[0].events.register('visibilitychanged', null, checkSelection); } // Ready to go. Alert user to any updates and check for selected segments. updateAlert(); checkSelection(); } function bootstrap() { var bGreasemonkeyServiceDefined = false; try { if ("object" === typeof Components.interfaces.gmIGreasemonkeyService) { bGreasemonkeyServiceDefined = true; } } catch (err) { /* Ignore. */ } if (undefined === typeof unsafeWindow || !bGreasemonkeyServiceDefined) { unsafeWindow = (function () { var dummyElem = document.createElement('p'); dummyElem.setAttribute('onclick', 'return window;'); return dummyElem.onclick(); })(); } /* begin running the code! */ if (undefined !== typeof $ && $('#WazeMap').length !== 0 && undefined !== typeof W.selectionManager.events.register && undefined !== typeof W.loginManager.events.register) { window.setTimeout(init, 100); } else { window.setTimeout(function () { bootstrap(); }, 1000); } } window.setTimeout(bootstrap, 100);