Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 72 additions & 2 deletions 00.config.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,75 @@
## Path to Closure Compiler
COMPILER="java -jar closure-compiler-v20230103.jar"
## Closure Compiler Maven Repo URLs
CC_BASE_URL="https://repo1.maven.org/maven2/com/google/javascript/closure-compiler"
CC_META_URL="${CC_BASE_URL}/maven-metadata.xml"

## Function to Verify MD5 of a local JAR against Maven Central's published checksum
## if md5sum does not match, jar is removed to be downloaded again.
verify_md5() {
_jar="$1"
_url="$2"
if ! command -v md5sum >/dev/null 2>&1; then
echo "WARNING: md5sum not found — skipping integrity check."
return 0
fi
_expected=$(curl -fsSL --max-time 15 "${_url}.md5")
if [ -z "${_expected}" ]; then
echo "WARNING: Could not fetch MD5 from Maven Central — skipping integrity check."
return 0
fi
_actual=$(md5sum "${_jar}" | cut -d' ' -f1)
if [ "${_actual}" != "${_expected}" ]; then
echo "ERROR: MD5 mismatch for ${_jar}"
echo " Expected: ${_expected}"
echo " Got: ${_actual}"
rm -f "${_jar}"
return 1
fi
echo "===> MD5 OK (${_actual})"
return 0
}

## Check if a closure-compiler JAR already exists locally
CC_JAR=$(ls closure-compiler-*.jar 2>/dev/null | head -n 1)

if [ -n "${CC_JAR}" ]; then
# JAR found — extract version from filename and verify against Maven Central
CC_VERSION="${CC_JAR#closure-compiler-}"
CC_VERSION="${CC_VERSION%.jar}"
CC_URL="${CC_BASE_URL}/${CC_VERSION}/${CC_JAR}"
echo "===> Found local ${CC_JAR} — verifying integrity..."
if ! verify_md5 "${CC_JAR}" "${CC_URL}"; then
echo "===> Local JAR failed integrity check. Re-downloading..."
CC_JAR=""
fi
fi

if [ -z "${CC_JAR}" ]; then
# JAR not found — resolve latest version and download
echo "===> Resolving latest Closure Compiler version..."
CC_VERSION=$(curl -fsSL --max-time 15 "${CC_META_URL}" | sed -n 's|.*<release>\(.*\)</release>.*|\1|p')
if [ -z "${CC_VERSION}" ]; then
echo "ERROR: Could not resolve Closure Compiler version from Maven Central."
echo " Check your internet connection or manually place the JAR in this directory."
exit 1
fi
CC_JAR="closure-compiler-${CC_VERSION}.jar"
CC_URL="${CC_BASE_URL}/${CC_VERSION}/${CC_JAR}"
echo "===> Downloading Closure Compiler ${CC_VERSION}..."
if ! curl -fSL --max-time 120 -o "${CC_JAR}" "${CC_URL}"; then
rm -f "${CC_JAR}"
echo "ERROR: Failed to download Closure Compiler ${CC_VERSION} from:"
echo " ${CC_URL}"
echo " Check your internet connection and try again."
exit 1
fi
echo "===> Downloaded ${CC_JAR}"
if ! verify_md5 "${CC_JAR}" "${CC_URL}"; then
echo "ERROR: Downloaded JAR failed integrity check."
exit 1
fi
fi

COMPILER="java -jar ${CC_JAR}"

EXT_NAME=WV
EXT_FILE_NAME="WME_Validator.user.js"
Expand Down
1 change: 1 addition & 0 deletions 99.build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ cat "${SRC_DIR}/meta/i18n-end.js" >> "${LOC_FILE}"

${COMPILER} \
--language_in ECMASCRIPT_2020 \
--language_out ECMASCRIPT_2020 \
--js "${SRC_DIR}/src/release.js" \
--js "${LOC_FILE}" \
--js "${SRC_DIR}/src/helpers.js" \
Expand Down
117 changes: 117 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## What This Is

WME Validator is a Tampermonkey/GreasyFork userscript for the [Waze Map Editor](https://waze.com). It validates map data (segments, nodes, venues) against 150+ rules across 26+ countries, highlights issues in the editor, and generates detailed reports with wiki references and solutions.

## Build Commands

The project uses shell scripts wrapping the Google Closure Compiler (Java). No `npm` or `package.json`.

```bash
./10.release.sh # Release build → build/WME_Validator.user.js (ADVANCED_OPTIMIZATIONS)
./20.debug.sh # Debug build → build/WME_Validator.debug.js (BUNDLE, DEF_DEBUG=true)
./30.gf.sh # GreasyFork build → build/WME_Validator.gf.js (WHITESPACE_ONLY)
./98.format.sh # Format the compiled output with clang-format (Google style, tab width 4)
./99.build.sh # Orchestrator — runs all three build variants
```

> **Windows / Git Bash note:** If scripts produce `: not found` errors, strip CRLF line endings with:
> ```bash
> sed -i 's/\r//' *.sh
> ```

Configuration (compiler path, output dirs) is in `./00.config.sh`. The Closure Compiler JAR is **auto-downloaded** from Maven Central on first run — no manual installation needed. The script detects any existing `closure-compiler-*.jar` in the repo root, verifies its MD5 against Maven Central, and re-downloads if missing or corrupt. Always fetches the latest published release.

There are **no automated tests**. Validation is done manually per `doc/RELENG.md`.

## Architecture

### Build Pipeline

Source files are concatenated in a specific order and fed to Closure Compiler:

1. `meta/meta-begin.js` — UserScript `@userscript` header
2. `meta/i18n-begin.js` — opens the translations object
3. `i18n/default.js` + `i18n/<CC>.js` (26 countries) — localization rules, auto-concatenated
4. `meta/i18n-end.js` — closes the translations object
5. `src/*.js` — core application code
6. `meta/meta-end.js` — closing wrapper
7. `meta/wme-externs.js` + `meta/jquery-1.9.js` — Closure Compiler extern definitions (not bundled, only used for type-checking)

The post-build steps prepend the UserScript metadata header and run `clang-format`.

### Global Namespaces

All runtime state lives in four top-level objects defined in `src/data.js`:

| Namespace | Purpose |
|-----------|---------|
| `_WV` | Private validator state and configuration |
| `_UI` | User interface state |
| `_RT` | Runtime data (current scan results, active segment info) |
| `_REP` | Report accumulator |

The `_THUI` namespace (from `src/lib/thui.js`) is a small embedded HTML UI toolkit.

### Core Execution Flow

```
WME login event
└─ F_LOGIN (src/login.js) — access control, UI initialization

User triggers scan
└─ F_VALIDATE (src/validate.js) — runs all rules against segments/nodes/venues

Map change events (segments/nodes/venues)
└─ F_ONSEGMENTSCHANGED / F_ONNODESCHANGED / F_ONVENUESCHANGED (src/other.js)
└─ re-validates affected objects

User requests report
└─ F_SHOWREPORT (src/report.js) — generates HTML/text/CSV output
```

### Source File Roles

- `src/release.js` — version string, release/expiry dates, CDN URLs, global access lists
- `src/data.js` — namespace definitions, constants, `DEF_DEBUG` flag
- `src/helpers.js` — pure utilities: `classOf`, `deepCopy`, `deepCompare`, `getDirection`
- `src/basic.js` — logging, HTML escaping, Trusted Types policy, error handling
- `src/validate.js` — validation engine; each rule is a function called per object
- `src/report.js` — report rendering (HTML table, plain text, CSV)
- `src/login.js` — login handler, access list parsing, country/user/level gates
- `src/other.js` — WME event listeners and layer change handler
- `src/lib/i18n.js` — i18n lookup helpers
- `src/lib/audio.js` — audio notification system
- `src/lib/thui.js` — tiny UI library for building form controls

### Localization and Country Rules

`i18n/` is a **git submodule** (`https://github.com/WMEValidator/i18n.git`). Each country file (`i18n/AR.js`, `i18n/DE.js`, etc.) overrides or extends the default English rules in `i18n/default.js`. When editing country-specific rules, changes go in the submodule, not in the main repo.

### Access Control

Rules can be gated by user level, username, country, or city using constants from `src/release.js`:
- `GA_FORLEVEL` — minimum WME user level
- `GA_FORUSER` / `GA_FORCOUNTRY` / `GA_FORCITY` — allowlists with negation (`"!Dekis,*"`)

These are evaluated in `F_LOGIN` against `WLM.user` attributes.

### Conventions

- Main handler functions are named `F_ACTION()` (e.g., `F_VALIDATE`, `F_LOGIN`, `F_SHOWREPORT`)
- Constants are `ALL_CAPS`
- JSDoc annotations (`@const`, `@type`, `@suppress`, `@struct`, `@define`) are used throughout — they matter for Closure Compiler's type checker and dead code elimination
- Debug-only code is guarded by `DEF_DEBUG` (a `@define` constant, stripped in release builds)
- All HTML written to the DOM must go through the Trusted Types policy defined in `src/basic.js`

## Release Checklist

See `doc/RELENG.md` for the full checklist. Key steps before releasing:
- Ensure all `window.console` calls are commented out
- Resolve all `TEST:`, `TODO:`, `BETA:` markers
- Verify header fields: country locks, user levels, release date, expiration date
- Run release build and test both in Tampermonkey and Firefox
- Check Unicode characters in the output file
8 changes: 7 additions & 1 deletion doc/ChangeLog.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
Current changes:

v2026.06.13:
- Updated PFX_PEDIA link for a waze discuss search link (temp fix)
- Added CLAUDE.md
- Improved closure compiler retrieval process
- Added --language_out ECMASCRIPT_2020 in build params

v2026.06.12:
- DaveAcincy: migrating validator to use the WME SDK.
- update US and Chile localizations.
Expand Down Expand Up @@ -870,4 +876,4 @@ Bugs & Issues:
- Run once mode permalinks were fixed

Other changes
- report of a very long street names has moved to a Polish country pack
- report of a very long street names has moved to a Polish country pack
7 changes: 3 additions & 4 deletions meta/meta-begin.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// ==UserScript==
// @name WME Validator
// @version 2026.06.12
// @version 2026.06.13
// @description This script validates a map area in Waze Map Editor, highlights issues and generates a very detailed report with wiki references and solutions
// @match https://beta.waze.com/*editor*
// @match https://www.waze.com/*editor*
Expand All @@ -12,7 +12,6 @@
// @icon https://raw.githubusercontent.com/WMEValidator/release/master/img/WV-icon96.png
// @namespace a
// @homepage https://www.waze.com/discuss/t/script-wme-validator-v2025-02-26-places-beta/44877
// @homepage https://www.waze.com/discuss/t/script-wme-validator-v2025-02-26-places-beta/44877
// @author Andriy Berestovskyy <berestovskyy@gmail.com>
// @copyright 2013-2018 Andriy Berestovskyy
// @license GPLv3
Expand All @@ -21,6 +20,7 @@
// @contributor jangliss
// @contributor Glodenox
// @contributor DaveAcincy
// @contributor miguelovergara
// ==/UserScript==
/* global turf */
/* global turf */
Expand All @@ -36,10 +36,9 @@
*
* For questions please use official forum:
* https://www.waze.com/discuss/t/script-wme-validator-v2025-02-26-places-beta/44877
* https://www.waze.com/discuss/t/script-wme-validator-v2025-02-26-places-beta/44877
*
* Report bugs on GitHub Issues Tracker:
* https://github.com/WMEValidator/validator/issues
*/

(function () {
(function () {
8 changes: 4 additions & 4 deletions src/login.js
Original file line number Diff line number Diff line change
Expand Up @@ -557,12 +557,12 @@ async function F_LOGIN() {
if (labelPL in translation) {
let l = translation[labelPL]
.replace('W:', PFX_WIKI)
.replace('P:', PFX_PEDIA)
.replace('P:', PFX_SEARCH)
.replace('F:', PFX_FORUM)
.replace('D:', PFX_DISCUSS)
;
check.PROBLEMLINK[ccode] = encodeURI(l);
if (-1 !== l.indexOf(PFX_WIKI) || -1 !== l.indexOf(PFX_PEDIA))
if (-1 !== l.indexOf(PFX_WIKI) || -1 !== l.indexOf(PFX_SEARCH))
check.PROBLEMLINKTEXT[ccode] = trS('report.link.wiki');
else
if (-1 !== l.indexOf(PFX_FORUM))
Expand All @@ -573,12 +573,12 @@ async function F_LOGIN() {
if (labelSL in translation) {
let l = translation[labelSL]
.replace('W:', PFX_WIKI)
.replace('P:', PFX_PEDIA)
.replace('P:', PFX_SEARCH)
.replace('F:', PFX_FORUM)
.replace('D:', PFX_DISCUSS)
;
check.SOLUTIONLINK[ccode] = encodeURI(l);
if (-1 !== l.indexOf(PFX_WIKI || -1 !== l.indexOf(PFX_PEDIA)))
if (-1 !== l.indexOf(PFX_WIKI || -1 !== l.indexOf(PFX_SEARCH)))
check.SOLUTIONLINKTEXT[ccode] = trS('report.link.wiki');
else
if (-1 !== l.indexOf(PFX_FORUM))
Expand Down
2 changes: 1 addition & 1 deletion src/release.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ let wmeSDK;
/** @const */
var PFX_WIKI = 'https://www.waze.com/wiki/';
/** @const */
var PFX_PEDIA = 'https://wazeopedia.waze.com/wiki/';
var PFX_SEARCH = 'https://www.waze.com/discuss/search?q=';
/** @const */
var PFX_FORUM = 'https://www.waze.com/forum/viewtopic.php?';
/** @const */
Expand Down
6 changes: 3 additions & 3 deletions src/report.js
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,7 @@ function F_SHOWREPORT(reportFormat) {
function addTextLabels(pack, label, defSet, oldPack) {
var defData = (defSet[label] || '')
.replace(new RegExp('^W:'), PFX_WIKI)
.replace(new RegExp('^P:'), PFX_PEDIA)
.replace(new RegExp('^P:'), PFX_SEARCH)
.replace(new RegExp('^F:'), PFX_FORUM)
.replace(new RegExp('^D:'), PFX_DISCUSS)
;
Expand All @@ -380,14 +380,14 @@ function F_SHOWREPORT(reportFormat) {
var oldData = origData
.replace(new RegExp('^' + GL_TODOMARKER), '')
.replace(new RegExp('^W:'), PFX_WIKI)
.replace(new RegExp('^P:'), PFX_PEDIA)
.replace(new RegExp('^P:'), PFX_SEARCH)
.replace(new RegExp('^F:'), PFX_FORUM)
.replace(new RegExp('^D:'), PFX_DISCUSS)
;
// preserve old data
var oldDataEN = (oldPack[label + '.en'] || '')
.replace(new RegExp('^W:'), PFX_WIKI)
.replace(new RegExp('^P:'), PFX_PEDIA)
.replace(new RegExp('^P:'), PFX_SEARCH)
.replace(new RegExp('^F:'), PFX_FORUM)
.replace(new RegExp('^D:'), PFX_DISCUSS)
;
Expand Down
2 changes: 1 addition & 1 deletion src/validate.js
Original file line number Diff line number Diff line change
Expand Up @@ -2309,7 +2309,7 @@ function F_VALIDATE(disabledHL) {
let foundPublicConnection = checkPublicConnection(segment, null);
if (!foundPublicConnection) {
// We might have a isolated segment. Could be a Restricted Gate
// See https://wazeopedia.waze.com/wiki/USA/Private_Installations#Specialty_Gate:_Restricted_Gate
// See: https://www.waze.com/discuss/t/private-installations/378216#p-2277317-specialty-gate-restricted-gate-30
if (nodeA.$otherSegmentsLen == 1 && nodeB.$otherSegmentsLen == 1) {
// both sides are connected to just one private segment
let nodeASegment = nodeA.$otherSegments[0];
Expand Down