Starting your script - WazeDev/WazeDev.github.io GitHub Wiki
Prerequisites
Before starting, ensure you have:
- Tampermonkey extension installed in your browser (Chrome, Firefox, Edge)
- Basic familiarity with browser developer tools (F12) for viewing console messages
- Access to the Waze Map Editor (requires a Waze account with editor permissions)
Starting Your Script
When creating user scripts for the Waze Map Editor (WME), you must either create a browser extension or a user script that can be installed through Greasemonkey or Tampermonkey. Tampermonkey is the recommended extension due to recent changes in how Greasemonkey functions.
This guide focuses on scripts installed via Tampermonkey. Creating browser extensions is similar, but a separate topic. We'll build a simple script to demonstrate the basics of interacting with the Waze object and the WME SDK.
Tampermonkey Script Headers
Every Tampermonkey script begins with a special header block that tells the browser extension:
- What the script does (@name, @description)
- Where it should run (@include, @exclude)
- What permissions it needs (@grant)
- How to identify it for updates (@namespace, @version)
Here is a sample default header:
// ==UserScript==
// @name New Userscript
// @namespace http://tampermonkey.net/
// @version 0.1
// @description try to take over the world!
// @author You
// @match http://*/*
// @grant none
// ==/UserScript==
💡 Learn More: Greasy Fork's rules for posted scripts!
Here you’ll assign a script name, set a namespace (often your GreasyFork profile link for auto-updates), add version, description, author, and specify what pages the script will run on.
Here’s an example for from the WME Wazebar Script:
// ==UserScript==
// @name WME Wazebar
// @namespace https://greasyfork.org/users/30701-justins83-waze
// @version 2025.05.30.01
// @description Displays a bar at the top of the editor that displays inbox, forum & wiki links
// @author JustinS83
// @include https://beta.waze.com/*
// @include https://www.waze.com/discuss/*
// @include https://webnew.waze.com/discuss/*
// @include https://www.waze.com/editor*
// @include https://www.waze.com/*/editor*
// @exclude https://www.waze.com/user/editor*
// @require https://greasyfork.org/scripts/27254-clipboard-js/code/clipboardjs.js
// @connect storage.googleapis.com
// @connect greasyfork.org
// @grant none
// ==/UserScript==
Best practice: Use @exclude for any pages you do not want your script to run on to avoid unexpected issues.
💡 Learn More: User Script Meta Keys!
For your first script, use this header:
// ==UserScript==
// @name WazeDev First Script // Display name in Tampermonkey dashboard
// @namespace http://tampermonkey.net/ // Unique identifier (use your GreasyFork profile for updates)
// @version 0.1 // Version number for update tracking
// @description Learning to script! // Brief description of functionality
// @author You // Your name or username
// @include https://beta.waze.com/* // Run on beta WME (testing environment)
// @include https://www.waze.com/editor* // Run on production WME
// @include https://www.waze.com/*/editor* // Run on localized WME URLs
// @exclude https://www.waze.com/user/editor* // Don't run on user profile pages
// @exclude https://www.waze.com/editor/sdk/* // Don't run on SDK documentation
// @grant none // No special permissions needed
// ==/UserScript==
This allows your script to run in both production and beta WME, but not on editor profile or SDK pages.
Boilerplate Script Structure
Below the header, Tampermonkey inserts a wrapper for your script:
(function() {
'use strict';
// Your code here...
})();
Your script logic goes inside this function.
Bootstrap: Initializing the WME SDK
A bootstrap routine ensures WME and its SDK are fully loaded before your script runs, preventing errors.
SDK Initialization and Bootstrap Example
const SCRIPT_NAME = GM_info.script.name; // Get the Script name from the @name tag of the UserScript Meta Keys
let wmeSDK; // Declare wmeSDK globally
// Initialization of the WME SDK
if (window.SDK_INITIALIZED) {
console.log(`${SCRIPT_NAME}: SDK initialized...`);
window.SDK_INITIALIZED.then(bootstrap).catch((err) => {
console.error(`${SCRIPT_NAME}: SDK initialization failed`, err);
});
} else {
console.warn(`${SCRIPT_NAME}: SDK_INITIALIZED is undefined`);
}
function bootstrap() {
try {
wmeSDK = getWmeSdk({
scriptId: SCRIPT_NAME.replaceAll(" ", ""),
scriptName: SCRIPT_NAME,
});
Promise.all([wmeReady()])
.then(() => {
console.log(`${SCRIPT_NAME}: All dependencies are ready.`);
init();
})
.catch((error) => {
console.error(`${SCRIPT_NAME}: Error during bootstrap`, error);
});
} catch (error) {
console.error(`${SCRIPT_NAME}: Failed to initialize SDK`, error);
}
}
function wmeReady() {
return new Promise((resolve) => {
if (wmeSDK.State.isReady()) {
console.log(`${SCRIPT_NAME}: WME is ready.`);
resolve();
} else {
console.log(`${SCRIPT_NAME}: Waiting for WME to be ready...`);
wmeSDK.Events.once({ eventName: "wme-ready" })
.then(() => {
console.log(`${SCRIPT_NAME}: WME is ready now.`);
resolve();
})
.catch((error) => {
console.error(`${SCRIPT_NAME}: Error while waiting for WME to be ready:`, error);
});
}
});
}
💡 Learn More: For complete SDK documentation, visit the WME SDK Reference Sight
What this achieves:
-
SDK Initialization Check: First, it verifies that
window.SDK_INITIALIZEDexists and waits for the SDK initialization promise to resolve. -
WME SDK Setup: Once the SDK is initialized, the
bootstrap()function creates the WME SDK instance usinggetWmeSdk()with your script's name and ID. -
WME Ready Check: The
wmeReady()function ensures the WME environment is fully loaded. This prevents errors that occur when scripts try to access WME data before it's available. The function checks if:- WME has initialized completely
- User authentication is confirmed
- Initial map data for the current view has loaded
- All WME UI components are ready for interaction
-
Event-Based Waiting: If WME isn't ready immediately, the script listens for the special "wme-ready" event using
wmeSDK.Events.once(). This event only fires once after all initialization, login, and initial data loading is complete. -
Script Initialization: Once all dependencies are confirmed ready, the
init()function is called (you can name this whatever you want). This is where you would set up your script using SDK modules like:wmeSDK.DataModel- Access segments, venues, nodes, etc.wmeSDK.Map- Control map view and add layerswmeSDK.Sidebar- Create custom UI panelswmeSDK.Events- Register for WME eventswmeSDK.Settings- Manage user preferences
Why Use a Bootstrap Pattern?
You might wonder why we need this seemingly complex initialization process instead of just writing our script code directly. The bootstrap pattern is essential for WME scripts because of how web applications load:
The Problem: Race Conditions
When a web page loads, many things happen simultaneously:
- The HTML page structure loads
- JavaScript files download and execute
- The WME application initializes its components
- User authentication occurs
- Map data downloads
- Your userscript starts running
Without a bootstrap pattern, your script might try to access WME data or create UI elements before they exist, causing errors like:
// ❌ This fails if WME isn't ready yet
const segments = wmeSDK.DataModel.Segments.getAll(); // Error: Cannot read property 'Segments' of undefined
The Solution: Wait for Dependencies
The bootstrap pattern ensures everything your script needs is available before it runs:
- Prevents Crashes: No more "undefined" errors when accessing WME objects
- Reliable Execution: Your script works consistently, regardless of network speed or system performance
- Clean Error Handling: Problems during initialization are caught and logged clearly
- Professional Behavior: Your script behaves predictably for all users
Real-World Analogy Think of it like cooking a meal:
- Without bootstrap: Start cooking immediately (but the oven might not be preheated, ingredients might not be ready)
- With bootstrap: Wait for the oven to preheat, check all ingredients are available, then start cooking
Complete First Script
Your script should look like this so far:
// ==UserScript==
// @name WazeDev First Script // Display name in Tampermonkey dashboard
// @namespace http://tampermonkey.net/ // Unique identifier (use your GreasyFork profile for updates)
// @version 0.1 // Version number for update tracking
// @description Learning to script! // Brief description of functionality
// @author You // Your name or username
// @include https://beta.waze.com/* // Run on beta WME (testing environment)
// @include https://www.waze.com/editor* // Run on production WME
// @include https://www.waze.com/*/editor* // Run on localized WME URLs
// @exclude https://www.waze.com/user/editor* // Don't run on user profile pages
// @exclude https://www.waze.com/editor/sdk/* // Don't run on SDK documentation
// @grant none // No special permissions needed
// ==/UserScript==
(function() {
'use strict';
const SCRIPT_NAME = GM_info.script.name; // Get the Script name from the @name tag of the UserScript Meta Keys
let wmeSDK; // Declare wmeSDK globally
if (window.SDK_INITIALIZED) {
console.log(`${SCRIPT_NAME}: SDK initialized...`);
window.SDK_INITIALIZED.then(bootstrap).catch((err) => {
console.error(`${SCRIPT_NAME}: SDK initialization failed`, err);
});
} else {
console.warn(`${SCRIPT_NAME}: SDK_INITIALIZED is undefined`);
}
function bootstrap() {
try {
wmeSDK = getWmeSdk({
scriptId: SCRIPT_NAME.replaceAll(' ', ''),
scriptName: SCRIPT_NAME,
});
Promise.all([wmeReady()])
.then(() => {
console.log(`${SCRIPT_NAME}: All dependencies are ready.`);
init();
})
.catch((error) => {
console.error(`${SCRIPT_NAME}: Error during bootstrap`, error);
});
} catch (error) {
console.error(`${SCRIPT_NAME}: Failed to initialize SDK`, error);
}
}
function wmeReady() {
return new Promise((resolve) => {
if (wmeSDK.State.isReady()) {
console.log(`${SCRIPT_NAME}: WME is ready.`);
resolve();
} else {
console.log(`${SCRIPT_NAME}: Waiting for WME to be ready...`);
wmeSDK.Events.once({ eventName: 'wme-ready' })
.then(() => {
console.log(`${SCRIPT_NAME}: WME is ready now.`);
resolve();
})
.catch((error) => {
console.error(`${SCRIPT_NAME}: Error while waiting for WME to be ready:`, error);
});
}
});
}
function init() {
// Your code here
console.log(`${SCRIPT_NAME}: Script initialized successfully!`);
}
})();
Expected console output:
WazeDev First Script: SDK initialized...
WazeDev First Script: WME is ready now.
WazeDev First Script: All dependencies are ready.
WazeDev First Script: Script initialized successfully!
Testing Your Script
- Install the script in Tampermonkey
- Open WME in a new tab
- Open Developer Tools (F12) and navigate to the Console tab
- Look for your script's messages - you should see the expected output above
Common Issues:
- No console output: Check that the script is enabled in Tampermonkey dashboard
- "SDK_INITIALIZED is undefined": WME may not have loaded the SDK yet - refresh the page
- Script not running: Verify the @include URLs match the WME URL in your address bar
This covers the very basics for getting started on a script. In the next section we will look at setting up a tab in the side panel where our script can create controls to toggle settings.