Userscript Guidelines: verschil tussen versies Geschiedenis weergeven

kGeen bewerkingssamenvatting
(Marked this version for translation)
 
(9 tussenliggende versies door 2 gebruikers niet weergegeven)
Regel 1: Regel 1:
==Best practices for different types of crossings==
{{TopLine}}
Crossings can be classified according to the number of single and split(double) roads involved in the crossing. For each of these there exist solutions to prevent U-turns.
<languages /><translate>


Why prevent U-turns?
==Userscript guidelines== <!--T:1-->
* We do not want Waze to suggest illegal routing. Ever.  
[[File:Glodenox.png|40px]]
U-turns are rarely legal, but Waze will suggest one on each and every occasion where it thinks it is appropriate. (In Germany U-turns are allowed in most cases, except they are permitted by sign.)
<i> Written by [https://wazebenelux.slack.com/messages/D8LDQ290D glodenox] </i><br />
<br />
<i>Warning</i>: this page is very technical of nature. The information detailed here is '''not''' needed to make use of [[Scripts|userscripts]] created by the Waze Community.


* Ad-hoc routing is better when U-turns are not possible.
<!--T:2-->
When you divert from the route you are navigating on, leaving the purple line, Waze calculates a new route for you. If it is possible to make a U-turn, chances are that Waze will send you through that U-turn back to where you came from - the purple line was the best route, after all. But - you might have diverted from the purple line on purpose - to avoid a congestion that loomed in the next curve, or a road that is closed for repairs. If opportunities for a U-turn are rare, it is more likely to happen that Waze will route you around the next block or two so that you get back on track AFTER the congestion.
__TOC__


This cannot be remedied completely in editing. A roundabout always allows U-turns. Going around the block can be fast enough to make another try at the original route.
== What is a userscript from a technical point of view? == <!--T:3-->
In order to write a proper userscript, you'll need at least some basic knowledge of JavaScript. This goes beyond the scope of this page, but there are several resources available online that help you with learning the language.


*'''U-turns can be prevented by reducing the length of the segment crossing the median to less than 15m and leaving this segment unnamed.'''
<!--T:4-->
A userscript consists of two parts:
* A block of meta data that details the script's name, its namespace, where it should be executed, when it should be executed and which permissions it needs to run (amongst others). This looks like the code below:


==3-Way-Crossings==
<!--T:5-->
*Simple 3-Way-Crossing (SSS)
// ==UserScript==
[[Image:Crossing_SSS.jpg]]
// @propertyName propertyValue
// ==/UserScript==


Not much to say about this - except that it is the ideal 3-way crossing: simple, stable, solid. Not much can go wrong with this one! If you have to deal with any of the following types of crossing, you may first look if it is possible to turn them into this type, because it prevents a lot of trouble later on.
<!--T:6-->
* A block of JavaScript code that contains how the userscript works.
UserScripts are executed through add-ons to your browser. The most well-known add-ons are [http://www.greasespot.net/ GreaseMonkey] and [https://tampermonkey.net/ TamperMonkey]. It is generally adviced to verify your script works in both of them (luckily that is usually the case). These add-ons make the block of JavaScript code execute at a certain moment during the loading of the page. There are certain restrictions put on this code for security reasons. This code is restricted in how it can perform cross-domain requests, for example.


== General JavaScript remarks == <!--T:7-->
It is usually advised to follow the guidelines like those laid out at [http://www.javascripttoolbox.com/bestpractices/ the JavaScript Toolbox] or [https://sarfraznawaz.wordpress.com/2012/02/19/842/ this blog by Sarfraz Ahmed]. Two remarks in particular are important for userscripts in the editor: make sure you use the <code>var</code> keyword when declaring variables (otherwise they get put in the global scope where they may conflict with other scripts) and when you add an event listener to the map somewhere, make sure your code doesn't break (null pointers, for example) as this will swallow the event and will prevent other code (including the editor itself) from reacting to that event. Use a <code>try ... catch</code> if you want to play safe.


*Single road crossing a dual road (SSD)
<!--T:8-->
[[Image:Crossing_SSD.jpg]]
You don't need to encapsulate your code into a self-executing anonymous function any more. While early versions of the userscript add-ons used to simply insert the userscript at the bottom of the page, nowadays these are executed in a separate context that still allows access to the global scope on the page, but doesn't expose the variables you create with the <code>var</code> keyword.


The next simple type of crossing has limitations. The way it is drawn here there is nothing that stops Waze from plotting a U-turn. Coming over the double road it takes two left-turns to get back on the road you came from. Typically that is NOT what you want.
<!--T:9-->
Also it is best not to rely too much on <code>setTimeout()</code> or <code>setInterval()</code> for the inner workings of your script. If too many of these are being executed in userscripts, the user experience can suffer a lot due to performance issues. If you want to monitor changes to a certain element, consider using a [https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver MutationObserver] instead. As an example, below is shown how to execute code whenever the settings tab is opened:


<!--T:10-->
var settingsObserver = new MutationObserver(function(mutationRecords) {
  // do stuff, this is triggered whenever the class of the settings tab is changed
});
selectionObserver.observe(document.getElementById('#sidepanel-prefs').parentNode, {
  attributes: true,
  attributeFilter: ['class']
});


*Dual road crossing a single road (DDS)
<!--T:11-->
[[Image:Crossing_DDS.jpg]]
It also helps to run your code through [http://www.jshint.com JSHint] and [http://www.jslint.com JSLint] before submitting it. Those tools tend to find bugs you may otherwise overlook.


== Components of the Waze Map Editor == <!--T:12-->
When you want to write a userscript, it is of course necessary that you understand what you are trying to adjust. The Waze Map Editor is an application that is made up of several libraries and tools. Luckily, most of it is relatively exposed, so there is a lot we can play with ;)


*3 Dual roads crossing (DDD)
<!--T:13-->
[[Image:Crossing_DDD.jpg]]
The most prominent components used are [http://openlayers.org/two/ OpenLayers], [https://getbootstrap.com/docs/3.3/ Bootstrap], [https://jquery.com/ jQuery] and [http://fontawesome.io/ FontAwesome]. The main access point for data within the editor is the <code>W</code> object. For a detailed information on the technological components of the Waze Map Editor, visit the [[User:Glodenox/Technological overview of the Waze Map Editor|Technological overview of the Waze Map Editor]] page.


When this type of crossing is generated in the basemap, it looks like the image presented here. It allows for U-turns in all directions.
== Userscript Template (bootstrap) == <!--T:14-->
While a lot depends on the purpose of the userscript, a rough template that can be used for most userscripts can be found below. Userscripts may start executing when the map editor hasn't fully loaded yet or when the user hasn't logged in yet, so they need to be able to cope with this. Also, if userscripts adjust something in the side panel, they will need to adjust this again whenever the user leaves the 'Events' mode and enters the 'Default' mode again. The code below provides the framework for that.
// ==UserScript==
// @name        Your script name here
// @namespace  <nowiki>http://domainnameofyourchoicehere.something/</nowiki>
// @description A succinct description of what your userscript does
// @include    /^https:\/\/(www|beta)\.waze\.com\/(?!user\/)(.{2,6}\/)?editor.*$/
// @version    0.0.1
// @grant      none
// ==/UserScript==
// Initialisation of the script, this will only run completely one time
function init(e) {
  if (e && e.user == null) {
    return;
  }
  // if you require certain features to be loaded, you can add them here
  if (typeof I18n === 'undefined' || typeof W === 'undefined' ||  typeof W.loginManager === 'undefined') {
    setTimeout(init, 200);
    return;
  }
  if (!W.loginManager.user) {
    W.loginManager.events.register("login", null, init);
    W.loginManager.events.register("loginStatus", null, init);
    if (!W.loginManager.user) {
      return;
    }
  }
  setModeChangeListener();
  performScript();
}
// Attempt to hook into the controller that can notify us whenever the editor's mode changes
function setModeChangeListener() {
  if (!W.app || !W.app.modeController) {
    setTimeout(setModeChangeListener, 400);
    return;
  }
  W.app.modeController.model.bind('change:mode', function(model, modeId) {
    if (modeId == 0) { // 0 = Default, 1 = Events
      performScript();
    }
  });
}
function performScript() {
  // Your userscript logic can go here. The Waze editor has been loaded and seems to be ready for your userscript.
}
init();


==4-Way-Crossings==
<!--T:15-->
*Simple 4-Way-Crossing (SSSS)
Instead of executing the <code>init</code> function again to regenerate a tab (or other HTML elements) when the mode changes, you could also store the generated HTML elements in a variable and just insert them again when you recover from 'Events' mode.
[[Image:Crossing_SSSS.jpg]]


Ah, the "Mother of all Crossings"! We are lucky that almost all crossings look like this. It is possible to define each and every turn restriction with a single node, and U-turns do not happen when all 4 roads in this junction are locked (No, there is no evidence for that. But each and every time a simple crossing does allow a U-turn, at least one of the roads was found not to be locked. If all roads are locked, Waze routes you around a block instead of making a U-turn).  
<!--T:16-->
Several common problems and tasks you will encounter while writing a userscript have already been solved. You may consider adding a dependency to a library like [https://www.waze.com/forum/viewtopic.php?t=210961 WazeWrap] in your userscript so you don't need to investigate these yourself any more. This can be done by referring to them in the script header:
// ==UserScript==
// ...
// @require <nowiki>https://greasyfork.org/scripts/24851-wazewrap/code/WazeWrap.js</nowiki>
// ==/UserScript==


 
</translate>
*Dual road crossing 3 single roads (DSSS)
{{Bottomline}}
[[Image:Crossing_DSSS.jpg]]
 
This is an ugly crossing if you come from/go to the SE. You get a direction to "Turn left, turn right". First check whether the double road can be made into a single one, so you get a SSSS. If that fails, you may want to join the double lanes into a single node, creating a sort of half-mapcat-bowtie.
 
*2 Dual roads crossing 2 single roads (DDSS)
[[Image:Crossing_DDSS.jpg]]
 
This one allows for U-turns to be made if you enter over one of the double lanes.
 
*3 Dual roads crossing 1 single road (DDDS)
[[Image:Crossing_DDDS.jpg]]
 
Ugly! If you cannot turn this one into a SSSS or DDSS, consider adding an extra fork on the single road and create a DDDD.
 
*4 Dual roads crossing (DDDD)
[[Image:Crossing_DDDD.jpg]]
 
This crossing allows U-turns in each and every direction if no precautions are taken.

Huidige versie van 10 aug 2018 om 10:20

Andere talen:

Userscript guidelines

Written by glodenox

Warning: this page is very technical of nature. The information detailed here is not needed to make use of userscripts created by the Waze Community.

What is a userscript from a technical point of view?

In order to write a proper userscript, you'll need at least some basic knowledge of JavaScript. This goes beyond the scope of this page, but there are several resources available online that help you with learning the language.

A userscript consists of two parts:

  • A block of meta data that details the script's name, its namespace, where it should be executed, when it should be executed and which permissions it needs to run (amongst others). This looks like the code below:
// ==UserScript==
// @propertyName propertyValue
// ==/UserScript==
  • A block of JavaScript code that contains how the userscript works.

UserScripts are executed through add-ons to your browser. The most well-known add-ons are GreaseMonkey and TamperMonkey. It is generally adviced to verify your script works in both of them (luckily that is usually the case). These add-ons make the block of JavaScript code execute at a certain moment during the loading of the page. There are certain restrictions put on this code for security reasons. This code is restricted in how it can perform cross-domain requests, for example.

General JavaScript remarks

It is usually advised to follow the guidelines like those laid out at the JavaScript Toolbox or this blog by Sarfraz Ahmed. Two remarks in particular are important for userscripts in the editor: make sure you use the var keyword when declaring variables (otherwise they get put in the global scope where they may conflict with other scripts) and when you add an event listener to the map somewhere, make sure your code doesn't break (null pointers, for example) as this will swallow the event and will prevent other code (including the editor itself) from reacting to that event. Use a try ... catch if you want to play safe.

You don't need to encapsulate your code into a self-executing anonymous function any more. While early versions of the userscript add-ons used to simply insert the userscript at the bottom of the page, nowadays these are executed in a separate context that still allows access to the global scope on the page, but doesn't expose the variables you create with the var keyword.

Also it is best not to rely too much on setTimeout() or setInterval() for the inner workings of your script. If too many of these are being executed in userscripts, the user experience can suffer a lot due to performance issues. If you want to monitor changes to a certain element, consider using a MutationObserver instead. As an example, below is shown how to execute code whenever the settings tab is opened:

var settingsObserver = new MutationObserver(function(mutationRecords) {
  // do stuff, this is triggered whenever the class of the settings tab is changed
});
selectionObserver.observe(document.getElementById('#sidepanel-prefs').parentNode, {
  attributes: true,
  attributeFilter: ['class']
});

It also helps to run your code through JSHint and JSLint before submitting it. Those tools tend to find bugs you may otherwise overlook.

Components of the Waze Map Editor

When you want to write a userscript, it is of course necessary that you understand what you are trying to adjust. The Waze Map Editor is an application that is made up of several libraries and tools. Luckily, most of it is relatively exposed, so there is a lot we can play with ;)

The most prominent components used are OpenLayers, Bootstrap, jQuery and FontAwesome. The main access point for data within the editor is the W object. For a detailed information on the technological components of the Waze Map Editor, visit the Technological overview of the Waze Map Editor page.

Userscript Template (bootstrap)

While a lot depends on the purpose of the userscript, a rough template that can be used for most userscripts can be found below. Userscripts may start executing when the map editor hasn't fully loaded yet or when the user hasn't logged in yet, so they need to be able to cope with this. Also, if userscripts adjust something in the side panel, they will need to adjust this again whenever the user leaves the 'Events' mode and enters the 'Default' mode again. The code below provides the framework for that.

// ==UserScript==
// @name        Your script name here
// @namespace   http://domainnameofyourchoicehere.something/
// @description A succinct description of what your userscript does
// @include     /^https:\/\/(www|beta)\.waze\.com\/(?!user\/)(.{2,6}\/)?editor.*$/
// @version     0.0.1
// @grant       none
// ==/UserScript==

// Initialisation of the script, this will only run completely one time
function init(e) {
  if (e && e.user == null) {
    return;
  }
  // if you require certain features to be loaded, you can add them here
  if (typeof I18n === 'undefined' || typeof W === 'undefined' ||  typeof W.loginManager === 'undefined') {
    setTimeout(init, 200);
    return;
  }
  if (!W.loginManager.user) {
    W.loginManager.events.register("login", null, init);
    W.loginManager.events.register("loginStatus", null, init);
    if (!W.loginManager.user) {
      return;
    }
  }
  setModeChangeListener();
  performScript();
}

// Attempt to hook into the controller that can notify us whenever the editor's mode changes
function setModeChangeListener() {
  if (!W.app || !W.app.modeController) {
    setTimeout(setModeChangeListener, 400);
    return;
  }
  W.app.modeController.model.bind('change:mode', function(model, modeId) {
    if (modeId == 0) { // 0 = Default, 1 = Events
      performScript();
    }
  });
}

function performScript() {
  // Your userscript logic can go here. The Waze editor has been loaded and seems to be ready for your userscript.
}

init();

Instead of executing the init function again to regenerate a tab (or other HTML elements) when the mode changes, you could also store the generated HTML elements in a variable and just insert them again when you recover from 'Events' mode.

Several common problems and tasks you will encounter while writing a userscript have already been solved. You may consider adding a dependency to a library like WazeWrap in your userscript so you don't need to investigate these yourself any more. This can be done by referring to them in the script header:

// ==UserScript==
// ...
// @require https://greasyfork.org/scripts/24851-wazewrap/code/WazeWrap.js
// ==/UserScript==

Main Page | Index