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:

  1. SDK Initialization Check: First, it verifies that window.SDK_INITIALIZED exists and waits for the SDK initialization promise to resolve.

  2. WME SDK Setup: Once the SDK is initialized, the bootstrap() function creates the WME SDK instance using getWmeSdk() with your script's name and ID.

  3. 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
  4. 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.

  5. 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 layers
    • wmeSDK.Sidebar - Create custom UI panels
    • wmeSDK.Events - Register for WME events
    • wmeSDK.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

  1. Install the script in Tampermonkey
  2. Open WME in a new tab
  3. Open Developer Tools (F12) and navigate to the Console tab
  4. 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.