(Marked this version for translation) |
|||
(27 tussenliggende versies door 2 gebruikers niet weergegeven) | |||
Regel 1: | Regel 1: | ||
{{TopLine}} | |||
<languages /><translate> | |||
== | ==Userscript guidelines== <!--T:1--> | ||
[[File:Glodenox.png|40px]] | |||
[[ | <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. | |||
<!--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. | |||
== | <!--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: | |||
* | |||
<!--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. | |||
== 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. | |||
<!--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. | |||
<!--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'] | |||
}); | |||
<!--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 ;) | |||
<!--T:13--> | |||
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(); | |||
<!--T:15--> | |||
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== | |||
</translate> | |||
{{Bottomline}} |
Huidige versie van 10 aug 2018 om 10:20
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==