Userscript Guidelines: verschil tussen versies Geschiedenis weergeven

Geen bewerkingssamenvatting
(Marked this version for translation)
 
(20 tussenliggende versies door 2 gebruikers niet weergegeven)
Regel 1: Regel 1:
==Classes of crossings==
{{TopLine}}
Crossings can be classified according to the number of single and split(double) roads involved in the crossing.
<languages /><translate>


==3-Way-Crossings==
==Userscript guidelines== <!--T:1-->
*Simple 3-Way-Crossing (SSS)
[[File:Glodenox.png|40px]]
[[Image:Crossing_SSS.jpg]]
<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.


Not much to say about this one - 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, since it will save you from a lot of trouble later on.
<!--T:2-->
__TOC__


== 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.


*Simple road crossing a split road (SSD)
<!--T:4-->
[[Image:Crossing_SSD.jpg]]
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:


The next simple type of crossing already 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:5-->
// ==UserScript==
// @propertyName propertyValue
// ==/UserScript==


<!--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.


*Split road crossing a simple road (DDS)
== General JavaScript remarks == <!--T:7-->
[[Image:Crossing_DDS.jpg]]
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.


<!--T:8-->
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.


*3 Split roads crossing (DDD)
<!--T:9-->
[[Image:Crossing_DDD.jpg]]
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:


==4-Way-Crossings==
<!--T:10-->
*Simple 4-Way-Crossing (SSSS)
var settingsObserver = new MutationObserver(function(mutationRecords) {
[[Image:Crossing_SSSS.jpg]]
  // 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']
});


<!--T:11-->
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 ;)


*Split road crossing 3 simple roads (DSSS)
<!--T:13-->
[[Image:Crossing_DSSS.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.


== 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();


*2 Split roads crossing 2 simple roads (DDSS)
<!--T:15-->
[[Image:Crossing_DDSS.jpg]]
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.


<!--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==


*3 Split roads crossing 1 simple road (DDDS)
</translate>
[[Image:Crossing_DDDS.jpg]]
{{Bottomline}}
 
 
*4 Split roads crossing (DDDD)
[[Image:Crossing_DDDD.jpg]]

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