您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
A WME developers library
当前为
此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.greatest.deepsurf.us/scripts/9794/102310/wLib.js
// ==UserScript== // @name wLib // @description A WME developers library // @version 1.0.2 // @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://greatest.deepsurf.us/users/9321 // ==/UserScript== /* global W */ /* global OL */ /* global wLib */ /* global $ */ (function () { /** * The wLib namespace. * @namespace * @global */ this.wLib = { VERSION: '1.0.2' }; }).call(this); /*** GEOMETRY ***/ (function () { /** * Namespace for functions related to geometry. * @memberof wLib * @namespace * @name wLib.Geometry */ this.Geometry = { /** * Determines if an {OpenLayers.Geometry} is within the map view. * @memberof wLib.Geometry * @param geometry {OpenLayers.Geometry} * @return {Boolean} Whether or not the geometry is in the map extent. */ isGeometryInMapExtent: function (geometry) { 'use strict'; return geometry && geometry.getBounds && W.map.getExtent().intersectsBounds(geometry.getBounds()); }, /** * Determines if an {OpenLayers.LonLat} is within the map view. * @memberof wLib.Geometry * @param {OpenLayers.LonLat} lonlat * @return {Boolean} Whether or not the LonLat is in the map extent. */ isLonLatInMapExtent: function (lonlat) { 'use strict'; return lonlat && W.map.getExtent().containsLonLat(lonlat); } }; }).call(wLib); /*** MODEL ***/ (function () { /** * Namespace for functions related to the model. * @memberof wLib * @namespace * @name wLib.Model */ this.Model = {}; /** * Gets the IDs of any selected segments. * @memberof wLib.Model * @return {Array} Array containing the IDs of selected segments. */ this.Model.getSelectedSegmentIDs = function () { 'use strict'; var i, n, selectedItems, item, segments = []; if (!W.selectionManager.hasSelectedItems()) { return false; } else { selectedItems = W.selectionManager.selectedItems; for (i = 0, n = selectedItems.length; i < n; i++) { item = selectedItems[i].model; if ('segment' === item.type) { segments.push(item.attributes.id); } } return segments.length === 0 ? false : segments; } }; /** * Retrives a route from the Waze Live Map. * @class * @name wLib.Model.RouteSelection * @param firstSegment The segment to use as the start of the route. * @param lastSegment The segment to use as the destination for the route. * @param {Array|Function} callback A function or array of funcitons to be executed after the route * is retrieved. 'This' in the callback functions will refer to the RouteSelection object. * @param {Object} options A hash of options for determining route. Valid options are: * fastest: {Boolean} Whether or not the fastest route should be used. Default is false, * which selects the shortest route. * freeways: {Boolean} Whether or not to avoid freeways. Default is false. * dirt: {Boolean} Whether or not to avoid dirt roads. Default is false. * longtrails: {Boolean} Whether or not to avoid long dirt roads. Default is false. * uturns: {Boolean} Whether or not to allow U-turns. Default is true. * @return {wLib.Model.RouteSelection} The new RouteSelection object. * @example: // The following example will retrieve a route from the Live Map and select the segments in the route. * selection = W.selectionManager.selectedItems; * myRoute = new wLib.Model.RouteSelection(selection[0], selection[1], function(){this.selectRouteSegments();}, {fastest: true}); */ this.Model.RouteSelection = function (firstSegment, lastSegment, callback, options) { var i, n, start = this.getSegmentCenterLonLat(firstSegment), end = this.getSegmentCenterLonLat(lastSegment); this.options = { fastest: options && options.fastest || false, freeways: options && options.freeways || false, dirt: options && options.dirt || false, longtrails: options && options.longtrails || false, uturns: options && options.uturns || true }; this.requestData = { from: 'x:' + start.x + ' y:' + start.y + ' bd:true', to: 'x:' + end.x + ' y:' + end.y + ' bd:true', returnJSON: true, returnGeometries: true, returnInstructions: false, type: this.options.fastest ? 'HISTORIC_TIME' : 'DISTANCE', clientVersion: '4.0.0', timeout: 60000, nPaths: 3, options: this.setRequestOptions(this.options) }; this.callbacks = []; if (callback) { if (!(callback instanceof Array)) { callback = [callback]; } for (i = 0, n = callback.length; i < n; i++) { if ('function' === typeof callback[i]) { this.callbacks.push(callback[i]); } } } this.routeData = null; this.getRouteData(); }; this.Model.RouteSelection.prototype = /** @lends wLib.Model.RouteSelection.prototype */ { /** * Formats the routing options string for the ajax request. * @private * @param {Object} options Object containing the routing options. * @return {String} String containing routing options. */ setRequestOptions: function (options) { return 'AVOID_TOLL_ROADS:' + (options.tolls ? 't' : 'f') + ',' + 'AVOID_PRIMARIES:' + (options.freeways ? 't' : 'f') + ',' + 'AVOID_TRAILS:' + (options.dirt ? 't' : 'f') + ',' + 'AVOID_LONG_TRAILS:' + (options.longtrails ? 't' : 'f') + ',' + 'ALLOW_UTURNS:' + (options.uturns ? 't' : 'f'); }, /** * Gets the center of a segment in LonLat form. * @private * @param segment A Waze model segment object. * @return {OpenLayers.LonLat} The LonLat object corresponding to the * center of the segment. */ getSegmentCenterLonLat: function (segment) { var x, y, componentsLength, midPoint; if (segment) { componentsLength = segment.geometry.components.length; midPoint = Math.floor(componentsLength / 2); if (componentsLength % 2 === 1) { x = segment.geometry.components[midPoint].x; y = segment.geometry.components[midPoint].y; } else { x = (segment.geometry.components[midPoint - 1].x + segment.geometry.components[midPoint].x) / 2; y = (segment.geometry.components[midPoint - 1].y + segment.geometry.components[midPoint].y) / 2; } return new OL.Geometry.Point(x, y).transform(W.map.getProjectionObject(), 'EPSG:4326'); } }, /** * Gets the route from Live Map and executes any callbacks upon success. * @private * @returns The ajax request object. The responseJSON property of the returned object * contains the route information. * */ getRouteData: function () { var i, n, that = this; return $.ajax({ dataType: 'json', url: this.getURL(), data: this.requestData, dataFilter: function (data, dataType) { return data.replace(/NaN/g, '0'); }, success: function (data) { that.routeData = data; for (i = 0, n = that.callbacks.length; i < n; i++) { that.callbacks[i].call(that); } } }); }, /** * Extracts the IDs from all segments on the route. * @private * @return {Array} Array containing an array of segment IDs for * each route alternative. */ getRouteSegmentIDs: function () { var i, j, route, len1, len2, segIDs = [], routeArray = [], data = this.routeData; if ('undefined' !== typeof data.alternatives) { for (i = 0, len1 = data.alternatives.length; i < len1; i++) { route = data.alternatives[i].response.results; for (j = 0, len2 = route.length; j < len2; j++) { routeArray.push(route[j].path.segmentId); } segIDs.push(routeArray); routeArray = []; } } else { route = data.response.results; for (i = 0, len1 = route.length; i < len1; i++) { routeArray.push(route[i].path.segmentId); } segIDs.push(routeArray); } return segIDs; }, /** * Gets the URL to use for the ajax request based on country. * @private * @return {String} Relative URl to use for route ajax request. */ getURL: function () { if (W.model.countries.get(235) || W.model.countries.get(40)) { return '/RoutingManager/routingRequest'; } else if (W.model.countries.get(106)) { return '/il-RoutingManager/routingRequest'; } else { return '/row-RoutingManager/routingRequest'; } }, /** * Selects all segments on the route in the editor. * @param {Integer} routeIndex The index of the alternate route. * Default route to use is the first one, which is 0. */ selectRouteSegments: function (routeIndex) { var i, n, seg, segIDs = this.getRouteSegmentIDs()[Math.floor(routeIndex) || 0], segments = []; if (undefined === typeof segIDs) { return; } for (i = 0, n = segIDs.length; i < n; i++) { seg = W.model.segments.get(segIDs[i]); if (undefined !== seg) { segments.push(seg); } } return W.selectionManager.select(segments); } }; }).call(wLib); /*** INTERFACE ***/ (function () { /** * Namespace for functions related to the WME interface * @memberof wLib * @namespace * @name wLib.Interface */ this.Interface = {}; this.Interface.MessageBar = OL.Class(this.Interface, /** @lends wLib.Interface.MessageBar.prototype */ { $el: null, messages: { exampleMessage: { messageType: 'info', messageText: 'This is an example message.', displayDuration: 5000, skipPrefix: true } }, options: { messagePrefix: null, displayDuration: 5000 }, styles: { defaultStyle: { 'border-radius': 'inherit', 'background-color': 'rgba(0,0,0,0.7)' }, error: { 'border-radius': 'inherit', 'background-color': 'rgba(180,0,0,0.9)', 'color': 'black' }, warn: { 'border-radius': 'inherit', 'background-color': 'rgba(230,230,0,0.9)', 'color': 'black' }, info: { 'border-radius': 'inherit', 'background-color': 'rgba(0,0,230,0.9)' } }, /** * Creates a new {wLib.Interface.MessageBar}. * @class * @name wLib.Interface.MessageBar * @param options {Object} Object containing options to use for the * message bar. Valid options are: * messages (an object with keys corresponding to message names and * values containing the message objects.) * messagePrefix (prefix to prepend to each message; can be * disabled per message by using skipPrefix), * displayDuration (default duration to display messages, * styles (object with keys representing messageType names and * values containing objects with css properties for the * messageType.) */ initialize: function (options) { var divStyle = { 'margin': 'auto', 'border-radius': '10px', 'text-align': 'center', 'width': '40%', 'font-size': '1em', 'font-weight': 'bold', 'color': 'white' }; OL.Util.extend(this.options, options); if (this.options.styles) { OL.Util.extend(this.styles, this.options.styles); } if (this.options.messages) { OL.Util.extend(this.messages, this.options.messages); } this.$el = $('<div/>').css(divStyle); this.appendBar(); }, appendBar: function () { var $insertTarget = $('#search'); wLib.Util.waitForElement($insertTarget, function () { this.$el.insertAfter($insertTarget); }, this); }, /** * Adds a style for a message type. * @param name {String} The name of the messageType. * @param style {Object} Object containing css properties and values to use * for the new messageType. */ addMessageType: function (name, style) { if (name && style) { this.styles[name] = style; } return this; }, /** * Removes the message bar from the page. */ destroy: function () { this.$el.remove(); }, /** * Displays a message. * @private * @param message The message object or the name of the message to look up. * @param lookupName {Boolean} If true, message parameter should be string * and this string is used as message name to look up in saved messages. */ displayMessage: function (message, lookupName) { var messageText = '', style, duration, $messageEl = $('<p/>'); // Lookup saved message by name if option is specified. if (lookupName) { if (this.messages[message]) { message = this.messages[message]; } else { console.debug('wLib: MessageBar: saved message not found.'); return; } } // Make sure message has at least text. if (!message.messageText) { return; } // Append prefix if one exists and skipPrefix is not specified. if (!message.skipPrefix && this.options.messagePrefix) { messageText = this.options.messagePrefix + ' '; } // Add messageText messageText += message.messageText; // Set style style = (message.messageType && this.styles[message.messageType]) ? this.styles[message.messageType] : this.styles.defaultStyle; // Set duration duration = (message.displayDuration && !isNaN(message.displayDuration)) ? message.displayDuration : this.options.displayDuration; // Update element attributes and add to page $messageEl.css(style).text(messageText).appendTo(this.$el); // Display message $messageEl .fadeIn('fast') .delay(duration) .fadeOut('slow', function () { $(this).remove(); }); }, /** * Hides the display. * @private */ hideMessage: function ($messageEl, wait) { setTimeout(function () { this.$el.fadeOut('slow'); $messageEl.remove(); }, wait); }, /** * Displays a message in the message bar. * @param messageText {String} The text of the message to display. * @param options {Object} Object containing message options. Valid * options are messageType (corresponds to the style to use), and * displayDuration (how long to display message in milliseconds). */ postNewMessage: function (messageText, options) { var newMessage = {}; if (messageText) { newMessage.messageText = messageText; if (options) { OL.Util.extend(newMessage, options); } } this.displayMessage(newMessage); return this; }, /** * Displays a saved message in the message bar. * @param messageName {String} The name of the saved message to display. */ postSavedMessage: function (messageName) { if (messageName) { this.displayMessage(messageName, true); } return this; }, /** * Adds message to saved messages. * @param messageName {String} The name of the new message to save. * @param options {Object} Object containing message options. Valid * options are messageType (corresponds to the style to use), messageText * (the content of the message), and displayDuration (how long to display * message in milliseconds). */ saveMessage: function (messageName, options) { var newMessage = {}; if (messageName && options && options.messageText) { OL.Util.extend(newMessage, options); this.messages[messageName] = newMessage; } else { console.debug('wLib: MessageBar: error saving message.'); } return this; }, }); this.Interface.Shortcut = OL.Class(this.Interface, /** @lends wLib.Interface.Shortcut.prototype */ { name: null, group: null, shortcut: {}, callback: null, scope: null, groupExists: false, actionExists: false, eventExists: false, /** * Creates a new {wLib.Interface.Shortcut}. * @class * @name wLib.Interface.Shortcut * @param name {String} The name of the shortcut. * @param group {String} The name of the shortcut group. * @param shortcut {String} The shortcut key(s). The shortcut should * be of the form * 'i' where i is the keyboard shortuct or include modifier keys * such as'CSA+i', * where C = the control key, S = the shift key, A = the alt key, and * i = the desired keyboard shortcut. The modifier keys are optional. * @param callback {Function} The function to be called by the * shortcut. * @param scope {Object} The object to be used as this by the * callback. * @return {wLib.Interface.Shortcut} The new shortcut object. * @example //Creates new shortcut and adds it to the map. * shortcut = new wLib.Interface.Shortcut('myName', 'myGroup', 'C+p', * callbackFunc, null).add(); */ initialize: function (name, group, shortcut, callback, scope) { var defaults = { group: 'default' }; this.CLASS_NAME = 'wLib Shortcut'; if ('string' === typeof name && name.length > 0 && 'string' === typeof shortcut && shortcut.length > 0 && 'function' === typeof callback) { this.name = name; this.group = group || defaults.group; this.callback = callback; this.shortcut[shortcut] = name; if ('object' !== typeof scope) { this.scope = null; } else { this.scope = scope; } return this; } }, /** * Determines if the shortcut's group already exists. * @private */ doesGroupExist: function () { this.groupExists = 'undefined' !== typeof W.accelerators.Groups[this.group] && undefined !== typeof W.accelerators.Groups[this.group].members && W.accelerators.Groups[this.group].length > 0; return this.groupExists; }, /** * Determines if the shortcut's action already exists. * @private */ doesActionExist: function () { this.actionExists = 'undefined' !== typeof W.accelerators.Actions[this.name]; return this.actionExists; }, /** * Determines if the shortcut's event already exists. * @private */ doesEventExist: function () { this.eventExists = 'undefined' !== typeof W.accelerators.events.listeners[this.name] && W.accelerators.events.listeners[this.name].length > 0 && this.callback === W.accelerators.events.listeners[this.name][0].func && this.scope === W.accelerators.events.listeners[this.name][0].obj; return this.eventExists; }, /** * Creates the shortcut's group. * @private */ createGroup: function () { W.accelerators.Groups[this.group] = []; W.accelerators.Groups[this.group].members = []; }, /** * Registers the shortcut's action. * @private */ addAction: function () { W.accelerators.addAction(this.name, { group: this.group }); }, /** * Registers the shortcut's event. * @private */ addEvent: function () { W.accelerators.events.register(this.name, this.scope, this.callback); }, /** * Registers the shortcut's keyboard shortcut. * @private */ registerShortcut: function () { W.accelerators.registerShortcuts(this.shortcut); }, /** * Adds the keyboard shortcut to the map. * @return {wLib.Interface.Shortcut} The keyboard shortcut. */ add: function () { /* If the group is not already defined, initialize the group. */ if (!this.doesGroupExist()) { this.createGroup(); } /* Clear existing actions with same name */ if (this.doesActionExist()) { W.accelerators.Actions[this.name] = null; } this.addAction(); /* Register event only if it's not already registered */ if (!this.doesEventExist()) { this.addEvent(); } /* Finally, register the shortcut. */ this.registerShortcut(); return this; }, /** * Removes the keyboard shortcut from the map. * @return {wLib.Interface.Shortcut} The keyboard shortcut. */ remove: function () { if (this.doesEventExist()) { W.accelerators.events.unregister(this.name, this.scope, this.callback); } if (this.doesActionExist()) { delete W.accelerators.Actions[this.name]; } //remove shortcut? return this; }, /** * Changes the keyboard shortcut and applies changes to the map. * @return {wLib.Interface.Shortcut} The keyboard shortcut. */ change: function (shortcut) { if (shortcut) { this.shortcut = {}; this.shortcut[shortcut] = this.name; this.registerShortcut(); } return this; } }), this.Interface.Tab = OL.Class(this.Interface, { /** @lends wLib.Interface.Tab */ TAB_SELECTOR: '#user-tabs ul.nav-tabs', CONTENT_SELECTOR: '#user-info > div.tab-content', callback: null, $content: null, context: null, $tab: null, /** * Creates a new {wLib.Interface.Tab}. The tab is appended to the WME * editor sidebar and contains the passed HTML content. * @class * @name wLib.Interface.Tab * @param name {String} The name of the tab. Should not contain any * special characters. * @param content {String} The HTML content of the tab. * @param callback {Function} A function to call upon successfully * appending the tab. * @param {Object} context The context in which to call the callback * function. * @return {wLib.Interface.Tab} The new tab object. * @example //Creates new tab and adds it to the page. * new wLib.Interface.Tab('thebestscriptever', '<div>Hello World!</div>'); */ initialize: function (name, content, callback, context) { var idName, i = 0; if (name && 'string' === typeof name && content && 'string' === typeof content) { if (callback && 'function' === typeof callback) { this.callback = callback; this.context = context || callback; } /* Sanitize name for html id attribute */ idName = name.replace(/[^a-z-_]/, ''); /* Make sure id will be unique on page */ while ( $('#sidepanel-' + (i ? idName + i : idName)).length > 0) { i++; } if (i) { idName = idName + i; } /* Create tab and content */ this.$tab = $('<li/>') .append($('<a/>') .attr({ 'href': '#sidepanel-' + idName, 'data-toggle': 'tab', }) .text(name)); this.$content = $('<div/>') .addClass('tab-pane') .attr('id', 'sidepanel-' + idName) .html(content); this.appendTab(); } }, appendTab: function (tries) { wLib.Util.waitForElement( this.TAB_SELECTOR + ',' + this.CONTENT_SELECTOR, function () { $(this.TAB_SELECTOR).append(this.$tab); $(this.CONTENT_SELECTOR).append(this.$content); if (this.callback) { this.callback.call(this.context); } }, this); } }); }).call(wLib); /*** Utilities ***/ (function () { /** * Namespace for utility functions. * @memberof wLib * @namespace * @name wLib.Util */ this.Util = {}; this.Util.waitForElement = function (selector, callback, context) { var jqObj; if (!selector || typeof callback !== 'function') { return; } jqObj = typeof selector === 'string' ? $(selector) : selector instanceof $ ? selector : null; if (!jqObj.size()) { window.requestAnimationFrame(function () { wLib.Util.waitForElement(selector, callback, context); }); } else { callback.call(context || callback); } }; }).call(wLib); /*** API ***/ (function () { /** * Namespace for functions related to WME actions. * @memberof wLib * @namespace * @name wLib.api */ this.api = {}; }).call(wLib);