您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Displays custom icons on the map based on the text of Map Comments in Vietnam.
// ==UserScript== // @name WME Comment Icons // @namespace https://greasyfork.org/en/users/1440408-minhtanz1 // @version 2.0.6 // @description Displays custom icons on the map based on the text of Map Comments in Vietnam. // @author Waze VN // @match https://www.waze.com/editor* // @match https://www.waze.com/*/editor* // @match https://beta.waze.com/* // @exclude https://www.waze.com/user/editor* // @license MIT // @grant none // @require https://greasyfork.org/scripts/489325-wme-sdk/code/WME%20SDK.js?version=1327429 // ==/UserScript== /* global getWmeSdk, W, OpenLayers */ class WmeCommentIcons { constructor() { this.version = '2.0.6'; this.scriptName = 'WME Comment Icons'; this.debugMode = true; // Đặt là true để xem log trong console this.layerName = 'wme-comment-icons-layer'; this.layer = null; this.commentLayerCheckbox = null; this.arrayRegex = this.buildCombinedRegex() this.wmeSdk = null; // Debounce hàm cập nhật để tránh gọi quá nhiều lần khi di chuyển/thu phóng bản đồ this.debouncedUpdate = this.debounce(this.updateDisplay.bind(this), 1000); this.wmeSdk = getWmeSdk({ scriptId: 'wme-comment-icons', scriptName: this.scriptName }); this.wmeSdk.Events.once({ eventName: 'wme-ready' }).then(this.initializePlugin.bind(this)); } async initializePlugin() { this.log('WME is ready. Initializing script...'); this.layer = new OpenLayers.Layer.Vector(this.layerName, { displayInLayerSwitcher: false, // Hide from default WME layer switcher rendererOptions: { zIndexing: true } }); W.map.addLayer(this.layer); if (this.layer.div) { this.layer.div.style.pointerEvents = 'none'; } this.addLayerCheckbox(); this.listen(); this.updateDisplay(); } listen() { // Listen for map movement and zoom changes (debounced) this.wmeSdk.Events.on({ eventName: 'wme-map-move-end', eventHandler: this.debouncedUpdate }); this.wmeSdk.Events.on({ eventName: 'wme-map-zoom-changed', eventHandler: this.debouncedUpdate }); // Track changes to map comments data model this.wmeSdk.Events.trackDataModelEvents({ dataModelName: 'mapComments' }); this.wmeSdk.Events.on({ eventName: 'wme-data-model-objects-changed', eventHandler: (e) => { if (e.detail?.dataModelName === 'mapComments') { if (Object.keys(e.detail?.added || {}).length > 0 || Object.keys(e.detail?.removed || {}).length > 0 || Object.keys(e.detail?.updated || {}).length > 0) { this.debouncedUpdate(); } } } }); const mapCommentsCheckbox = document.getElementById('layer-switcher-item_map_comments'); if (mapCommentsCheckbox) { mapCommentsCheckbox.addEventListener('change', this.debouncedUpdate); } else { this.log("Could not find WME map comments layer checkbox for additional listener."); } } async updateDisplay() { this.layer.setZIndex(this.findTopLayer()+50); this.layer.removeAllFeatures(); if (!this.commentLayerCheckbox || !this.commentLayerCheckbox.checked) { this.log('Custom layer is disabled, clearing icons.'); return; } try { const mapComments = await this.wmeSdk.DataModel.MapComments.getAll(); // getAll is sync for loaded objects if (!mapComments || mapComments.length === 0) { this.log('No map comments found in data model for current view.'); return; } const featuresToAdd = []; for (const comment of mapComments) { const iconSvg = this.getCommentIcon(comment.subject); const [lon, lat] = comment.geometry.coordinates; const pointGeometry = new OpenLayers.Geometry.Point(lon, lat) .transform(new OpenLayers.Projection("EPSG:4326"), W.map.getProjectionObject()); const feature = new OpenLayers.Feature.Vector(pointGeometry); const base64Svg = this.utf8ToBase64(iconSvg); const xOffset = Math.floor(Math.random() * 41) - 20; const yOffset = Math.floor(Math.random() * 41) - 20; feature.style = { graphic: true, externalGraphic: 'data:image/svg+xml;base64,' + base64Svg, graphicHeight: 30, // Adjust size as needed graphicWidth: 30, // Adjust size as needed graphicXOffset: xOffset, // Adjust based on SVG origin graphicYOffset: yOffset, // Adjust based on SVG height/origin graphicOpacity: 0.9, label: comment.modificationData.createdBy, labelAlign:'cm', labelXOffset:yOffset, labelYOffset:xOffset, fontColor: '#ffffff', fontSize: '12px', fontOpacity: 0.8 }; featuresToAdd.push(feature); } if (featuresToAdd.length > 0) { this.layer.addFeatures(featuresToAdd); } else { this.log('No features to add after processing comments.'); } } catch (error) { console.error(`[${this.scriptName}] Error updating display:`, error); } } findTopLayer() { // Access the .layers array directly, which is standard in older OpenLayers versions. const layers = W.map.layers; let maxZ = -Infinity; let topLayer = null; // Loop through each layer to find the maximum z-index for (let i = 0; i < layers.length; i++) { const layer = layers[i]; // It's good practice to check if the layer and its method exist if (layer && typeof layer.getZIndex === 'function') { const zIndex = layer.getZIndex(); if (zIndex !== null && zIndex > maxZ) { maxZ = zIndex; topLayer = layer; } } } // If no layers with a z-index were found, default to a base value like 0. if (maxZ === -Infinity) { console.log('No layers with a z-index were found. Defaulting to 0.'); maxZ = 0; } else if (topLayer) { // Logging the top layer can be helpful for debugging console.log(`Top layer found: ${topLayer.name || 'Unnamed Layer'}, z-index: ${maxZ}`); } // Set the instance's ZIndex property this.ZIndex = maxZ; // Return the highest z-index return maxZ; } utf8ToBase64(str) { // Use TextEncoder to get UTF-8 bytes const utf8Bytes = new TextEncoder().encode(str); const binaryString = String.fromCharCode.apply(null, utf8Bytes); return btoa(binaryString); } buildCombinedRegex() { const allRegexParts = []; this.ruleMap = new Map(); this.commentRules = [ { name: 'VAO_KDC', regex: /^(?!.*\b(ngo.i|ra|h.t|h.*c)\b).*?(.*kdc|.*kdc|.*khu d.n c.|.*d.n c.|.*b.t ..u k|dc|n.i th.*|th.nh th.)/is, icon: () => this.residentialZoneIcon(false) }, { name: 'HET_KDC', regex: /^(?!.*\b(v.o|b.t ..u|v.)\b).*?(ngo.i.*|ra\S.*k.*|ra n.*|h.t.*cư|h.*dc|h.t.*kdc|th.nh th.|h.t th.*)/is, icon: () => this.residentialZoneIcon(true) }, { name: 'CAM_RE_TRAI', regex: /^(?!.*\b(v..t)\b)(bi.n|c.m|k.*g|cr).*?(tr.i|t)$/is, icon: () => this.prohibitTurnIcon('left') }, { name: 'CAM_RE_PHAI', regex: /^(bi.n|c.m|k.*g|cr).*?(phai|p)$/is, icon: () => this.prohibitTurnIcon('right') }, { name: 'CAM_QUAY_DAU', regex: /^(bi.n|c.m|k.*g).*?(quay ..u)$/is, icon: () => this.prohibitTurnIcon('u-turn') }, { name: 'CAMERA_TOC_DO', regex: /^(cam.* t.c ..|speed.*|cam.*speed)$/is, icon: () => this.cameraIcon('speed') }, { name: 'CAMERA_DEN_DO', regex: /^(cam.* ph.t ngu.i|cam.* ..n ..|..n ..|traffic light)$/is, icon: () => this.cameraIcon('red-light') }, { name: 'HET_CAM_TOC_DO', regex: /^(?:h.t|h.t bi.n)\s+.*?(\d+)/is, icon: (match) => this.speedIcon(match[1], true) }, { name: 'GH_TOC_DO', regex: /^(?!.*\b(h.t|n.a)\b).*?(\d{2,3})/is, icon: (match) => this.speedIcon(match[2], false) }, { name: 'HET_MOI_LENH_CAM', regex: /^(h.t|bi.n).*?(bi.n|c.m|l.c|t.m th.i|l..h)$/is, icon: () => this.createEndOfProhibitionsIcon() } ] for (const rule of this.commentRules) { if (!/^[a-zA-Z0-9_]+$/.test(rule.name)) { console.error(`[${this.scriptName}] Rule name "${rule.name}" is invalid. It must be a valid JavaScript identifier.`); continue; } this.ruleMap.set(rule.name, rule); const regexPattern = rule.regex.source; allRegexParts.push(`(?<${rule.name}>${regexPattern})`); } const combinedPattern = `^(${allRegexParts.join('|')})`; this.combinedRegex = new RegExp(combinedPattern, 'is'); return this.combinedRegex } getCommentIcon(subject) { if (!subject || typeof subject !== 'string') return null; const text = subject.trim(); const match = this.arrayRegex.exec(text); if (match === null) { return null; } const matchedGroupName = Object.keys(match.groups).find(groupName => match.groups[groupName] !== undefined); if (matchedGroupName) { const rule = this.ruleMap.get(matchedGroupName); if (rule) { return rule.icon(match.groups[matchedGroupName].match(rule.regex)); } } return null; } residentialZoneIcon(isEnd = true) { return isEnd ? `<svg width="239.99998" height="200" viewBox="0 0 63.499994 52.916666" version="1.1" id="svg1" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg"><defs id="defs1"><clipPath clipPathUnits="userSpaceOnUse" id="clipPath56"><path fill="none" stroke="#0046aa" stroke-width="1.32246" d="M 0.05437925,55.564166 H 158.74999 V 187.81051 H 0.05437925 Z" id="path56" /></clipPath></defs><g id="layer10" transform="translate(-0.05438337,-52.916664)"><path fill="#0046aa" d="M 0.05438337,52.916664 H 63.554376 V 105.83333 H 0.05438337 Z" id="path1" style="stroke-width:0.0529165" /><path d="m 2.7002164,92.604164 v -8.678336 l 5.185833,-5.503333 5.1858326,5.503333 v 3.915833 h 1.534584 V 78.05208 l 1.058333,-1.640418 v -9.524999 l 2.910416,-6.0325 2.910417,6.0325 v 9.524999 l 1.058333,1.640418 v 3.915833 l 3.439583,-1.852086 6.50875,4.074587 v 5.926666 h 1.693333 V 75.459164 h 0.846666 v -6.032501 h 0.582084 v -5.926666 h 5.926663 v 7.831665 h 0.899584 V 81.54458 h 2.328334 v 6.032498 h 0.952498 v -9.895417 l 4.074584,-3.598333 4.497916,4.021666 v 4.92125 l 3.651251,-3.016249 3.175,3.4925 v 9.101669 z" fill="#ffffff" id="path2" style="stroke-width:0.0529165" /><path stroke="#f00a0a" stroke-width="7.93478" d="M 0.05437925,187.81051 158.74999,55.564165" id="path3" clip-path="url(#clipPath56)" transform="matrix(0.40013706,0,0,0.40013706,0.03262422,30.683383)" /><path fill="none" stroke="#ffffff" stroke-width="0.529165" d="M 0.58354859,53.44583 H 63.025211 v 51.85833 H 0.58354859 Z" id="path4" /></g></svg>` : `<svg width="239.99997" height="200" viewBox="0 0 63.49999 52.916666" version="1.1" id="svg1" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg"><defs id="defs1" /><g id="layer11" transform="translate(-105.83333,-52.916664)"><path fill="#0046aa" d="m 105.83333,52.916664 h 63.49999 v 52.916666 h -63.49999 z" id="path1-3" style="stroke-width:0.0529164" /><path fill="none" stroke="#ffffff" stroke-width="0.529164" d="m 106.36249,53.44583 h 62.44166 v 51.85833 h -62.44166 z" id="path2-5" /><path d="m 108.47916,92.604158 v -8.678331 l 5.18584,-5.503334 5.18583,5.503334 v 3.915833 h 1.53458 v -9.789583 l 1.05833,-1.640417 v -9.524998 l 2.91042,-6.032499 2.91042,6.032499 v 9.524998 l 1.05832,1.640417 v 3.915833 l 3.43959,-1.852083 6.50876,4.074582 v 5.926665 h 1.69333 V 75.45916 h 0.84666 v -6.032498 h 0.58208 v -5.926667 h 5.92667 v 7.831665 h 0.89959 v 10.212917 h 2.32832 v 6.032497 h 0.95251 V 77.68166 l 4.07458,-3.598332 4.49792,4.021665 v 4.92125 l 3.65124,-3.01625 3.17501,3.4925 v 9.101665 z" fill="#ffffff" id="path3-7" style="stroke-width:0.0529164" /></g></svg>`; } speedIcon(speed, isProhibit = false) { const speedNum = parseInt(speed, 10); if (!isNaN(speedNum) && speedNum > 10 && speedNum < 150 && speedNum % 10 === 0) { return isProhibit ? `<svg width="200" height="200" viewBox="0 0 52.917 52.917" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"> <g transform="translate(-.076 30.541)scale(.0756)" style="display:inline"> <circle style="opacity:1;fill:#fff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5.65454;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers" cx="351" cy="-54" r="340"/> <text x="351" y="-80" font-size="450" font-family="Arial" font-weight="bold" fill="none" stroke="#000000" stroke-width="10" text-anchor="middle" dominant-baseline="central">${speed}</text> <path transform="rotate(45)" style="opacity:0.3;fill:#000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.04701;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers" d="M165.011-616.378h9.8v660h-9.8zm20.05 0h9.8v660h-9.8zm20.05 0h9.8v660h-9.8zm20.05 0h9.8v660h-9.8zm20.05 0h9.8v660h-9.8z"/> <path d="M351-404A350 350 0 0 0 1-54a350 350 0 0 0 350 350A350 350 0 0 0 701-54a350 350 0 0 0-350-350m0 49.7A300.3 300.3 0 0 1 651.3-54 300.3 300.3 0 0 1 351 246.3 300.3 300.3 0 0 1 50.7-54 300.3 300.3 0 0 1 351-354.3" style="opacity:1;fill:#0046aa;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:315.815;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"/> </g> </svg>` : `<svg xmlns="http://www.w3.org/2000/svg" width="50" height="50" viewBox="0 0 50 50"> <circle cx="25" cy="25" r="21" fill="#FFFFFF" stroke="#C20B10" stroke-width="5"/> <text x="25" y="35" font-size="25" font-family="Arial" font-weight="bold" fill="#1F2125" text-anchor="middle">${speed}</text> </svg>`; } } prohibitTurnIcon(direction) { switch (direction) { case 'left': return `<svg width="200" height="200" viewBox="0 0 52.917 52.917" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><g transform="translate(-264.583 -52.917)"><circle style="fill:#e4dfe1;fill-opacity:1;stroke-width:4.79999;stroke-linecap:round;stroke-linejoin:round;paint-order:stroke fill markers" cx="291.042" cy="79.375" r="26.458"/><g transform="translate(265.834 83.253)scale(.07182)"><circle cy="-54" cx="351" style="opacity:1;fill:#fff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:270.699;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers" r="300"/><path d="M491 156v-280c0-38.78-31.22-70-70-70H246v-35l-105 70 105 70v-35h175v280z" style="opacity:1;fill:#000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4.2306;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"/><path style="opacity:1;fill:#c20b10;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:315.815;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers" d="M351-404A350 350 0 0 0 1-54a350 350 0 0 0 350 350A350 350 0 0 0 701-54a350 350 0 0 0-350-350m0 100A250 250 0 0 1 601-54a250 250 0 0 1-250 250A250 250 0 0 1 101-54a250 250 0 0 1 250-250"/><path style="opacity:1;fill:#c20b10;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:72.0997;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers" transform="rotate(-45)" d="M261.378-129.989h50v680h-50z"/></g></g></svg>`; case 'right': return `<svg width="200" height="200" viewBox="0 0 52.917 52.917" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><g transform="translate(-317.5 -52.917)"><circle style="fill:#e4dfe1;fill-opacity:1;stroke-width:4.79999;stroke-linecap:round;stroke-linejoin:round;paint-order:stroke fill markers" cx="343.958" cy="79.375" r="26.458"/><g transform="matrix(-.07182 0 0 .07182 369.166 83.253)"><circle cy="-54" cx="351" style="opacity:1;fill:#fff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:270.699;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers" r="300"/><path d="M491 156v-280c0-38.78-31.22-70-70-70H246v-35l-105 70 105 70v-35h175v280z" style="opacity:1;fill:#000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4.2306;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"/><path style="opacity:1;fill:#c20b10;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:315.815;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers" d="M351-404A350 350 0 0 0 1-54a350 350 0 0 0 350 350A350 350 0 0 0 701-54a350 350 0 0 0-350-350m0 100A250 250 0 0 1 601-54a250 250 0 0 1-250 250A250 250 0 0 1 101-54a250 250 0 0 1 250-250"/><path style="opacity:1;fill:#c20b10;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:72.0997;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers" transform="rotate(-45)" d="M261.378-129.989h50v680h-50z"/></g></g></svg>`; case 'u-turn': return `<svg width="200" height="200" viewBox="0 0 52.917 52.917" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><g transform="translate(-370.417 -52.917)"><circle style="fill:#e4dfe1;fill-opacity:1;stroke-width:4.79999;stroke-linecap:round;stroke-linejoin:round;paint-order:stroke fill markers" cx="396.875" cy="79.375" r="26.458"/><g transform="translate(371.668 83.253)scale(.07182)"><circle style="opacity:1;fill:#c20b10;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:315.815;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers" cx="351" cy="-54" r="350"/><circle cy="-54" cx="351" style="opacity:1;fill:#fff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:225.582;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers" r="250"/><path style="opacity:1;fill:#000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5.28921;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers" d="M246 16v-175c0-38.78 31.22-70 70-70h70c38.78 0 70 31.22 70 70v315h-70v-315h-70V16h35l-70 105-70-105Z"/><path transform="rotate(-45)" style="opacity:1;fill:#c20b10;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:72.0997;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers" d="M261.378-129.99h50v680h-50z"/></g></g></svg>`; default: return null; } } cameraIcon(type) { switch (type) { case 'speed': return `<svg width="200" height="200" viewBox="0 0 52.917 52.917" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><g transform="translate(-476.25)"><circle style="fill:#e4dfe1;fill-opacity:1;stroke-width:4.79999;stroke-linecap:round;stroke-linejoin:round;paint-order:stroke fill markers" cx="502.708" cy="26.458" r="26.458"/><circle style="fill:#c20b10;fill-opacity:1;stroke-width:4.48117;stroke-linecap:round;stroke-linejoin:round;paint-order:stroke fill markers" cx="502.708" cy="26.458" r="24.701"/><circle style="fill:#fff;fill-opacity:1;stroke-width:3.174;stroke-linecap:round;stroke-linejoin:round;paint-order:stroke fill markers" cx="502.708" cy="26.458" r="17.496"/><g transform="matrix(.38687 0 0 .33923 255.134 -1.055)"><path d="M617.505 48.837h38.475a4 4 45 0 1 4 4v24.06a4 4 135 0 1-4 4h-38.475a4 4 45 0 1-4-4v-24.06a4 4 135 0 1 4-4z" style="font-variation-settings:"wght"900;display:inline;fill:#8fa1ad;fill-opacity:1;stroke:#000;stroke-width:6.35;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"/><path d="M621.583 84.377h30.32v6.337h-30.321v-6.336z" style="font-variation-settings:"wght"900;display:inline;fill:#738b9e;fill-opacity:1;stroke:#000;stroke-width:6.35;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"/><path d="M627.367 94.392h18.763v5.979h-18.763z" style="font-variation-settings:"wght"900;display:inline;fill:#7996b5;fill-opacity:1;stroke:#000;stroke-width:6.08542;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"/><path d="M638.524 110.976h27.88v5.272h-27.88z" style="font-variation-settings:"wght"900;display:inline;fill:#7ea1ba;fill-opacity:1;stroke:#000;stroke-width:6.08542;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"/><path d="M632.199 103.618h9.275v12.57H632.2v-12.57z" style="font-variation-settings:"wght"900;display:inline;fill:#8ba2b6;fill-opacity:1;stroke:#000;stroke-width:6.20394;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"/><circle style="font-variation-settings:"wght"900;fill:#b6d5f4;fill-opacity:1;stroke:#000;stroke-width:7.53681;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers" cx="636.793" cy="64.936" r="7.183"/></g></g></svg>`; case 'red-light': return `<svg width="200" height="200" viewBox="0 0 52.917 52.917" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><g transform="translate(-476.25 -52.917)"><circle style="fill:#e4dfe1;fill-opacity:1;stroke-width:4.79999;stroke-linecap:round;stroke-linejoin:round;paint-order:stroke fill markers" cx="502.708" cy="79.375" r="26.458"/><circle style="fill:#c20b10;fill-opacity:1;stroke-width:4.48117;stroke-linecap:round;stroke-linejoin:round;paint-order:stroke fill markers" cx="502.708" cy="79.375" r="24.701"/><circle style="fill:#fff;fill-opacity:1;stroke-width:3.174;stroke-linecap:round;stroke-linejoin:round;paint-order:stroke fill markers" cx="502.708" cy="79.375" r="17.496"/><g transform="translate(-19.723 59.891)scale(.85573)"><path style="font-variation-settings:"wght"900;fill:#5e6261;fill-opacity:1;stroke:#000;stroke-width:2.16141;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers" d="M597.163 10.043h9.134a2 2 45 0 1 2 2v16.073a2 2 135 0 1-2 2h-9.134a2 2 45 0 1-2-2V12.043a2 2 135 0 1 2-2z" transform="matrix(1.0615 0 0 .9969 -36.887 .683)"/><ellipse style="font-variation-settings:"wght"900;fill:#d86057;fill-opacity:1;stroke:#000;stroke-width:1.5875;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers" cx="601.854" cy="15.934" rx="3.586" ry="3.712"/><ellipse style="font-variation-settings:"wght"900;fill:#a0e885;fill-opacity:1;stroke:#000;stroke-width:1.5875;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers" cx="601.854" cy="25.936" rx="3.586" ry="3.712"/><path d="M601.856 12.091a3.586 3.712 0 0 0-.56.046 3.586 3.712 0 0 0-.548.136 3.586 3.712 0 0 0-.52.223 3.586 3.712 0 0 0-.48.304 3.586 3.712 0 0 0-.428.378 3.586 3.712 0 0 0-.365.443 3.586 3.712 0 0 0-.294.496 3.586 3.712 0 0 0-.216.538 3.586 3.712 0 0 0-.002.008 4.583 4.743 0 0 1 .411-.426 4.583 4.743 0 0 1 .524-.405 4.583 4.743 0 0 1 .574-.324 4.583 4.743 0 0 1 .613-.237 4.583 4.743 0 0 1 .639-.144 4.583 4.743 0 0 1 .652-.048 4.583 4.743 0 0 1 .232.006 4.583 4.743 0 0 1 .232.018 4.583 4.743 0 0 1 .23.03 4.583 4.743 0 0 1 .229.043 4.583 4.743 0 0 1 .225.054 4.583 4.743 0 0 1 .224.066 4.583 4.743 0 0 1 .22.078 4.583 4.743 0 0 1 .215.09 4.583 4.743 0 0 1 .211.1 4.583 4.743 0 0 1 .206.11 4.583 4.743 0 0 1 .2.123 4.583 4.743 0 0 1 .194.132 4.583 4.743 0 0 1 .187.142 4.583 4.743 0 0 1 .18.152 4.583 4.743 0 0 1 .173.161 4.583 4.743 0 0 1 .164.17 4.583 4.743 0 0 1 .086.1 3.586 3.712 0 0 0-.023-.077 3.586 3.712 0 0 0-.072-.195 3.586 3.712 0 0 0-.082-.19 3.586 3.712 0 0 0-.092-.185 3.586 3.712 0 0 0-.102-.179 3.586 3.712 0 0 0-.112-.173 3.586 3.712 0 0 0-.121-.166 3.586 3.712 0 0 0-.13-.16 3.586 3.712 0 0 0-.138-.151 3.586 3.712 0 0 0-.146-.143 3.586 3.712 0 0 0-.154-.134 3.586 3.712 0 0 0-.161-.126 3.586 3.712 0 0 0-.167-.115 3.586 3.712 0 0 0-.174-.106 3.586 3.712 0 0 0-.178-.095 3.586 3.712 0 0 0-.184-.086 3.586 3.712 0 0 0-.187-.073 3.586 3.712 0 0 0-.192-.064 3.586 3.712 0 0 0-.195-.052 3.586 3.712 0 0 0-.197-.04 3.586 3.712 0 0 0-.2-.03 3.586 3.712 0 0 0-.2-.017 3.586 3.712 0 0 0-.201-.006" style="font-variation-settings:"wght"900;opacity:.7;fill:#000;fill-opacity:1;stroke:none;stroke-width:1.5875;stroke-linecap:round;stroke-linejoin:round;paint-order:stroke fill markers"/><path d="M601.856 11.88a4.427 6.689 0 0 0-.692.082 4.427 6.689 0 0 0-.676.245 4.427 6.689 0 0 0-.641.402 4.427 6.689 0 0 0-.593.548 4.427 6.689 0 0 0-.528.681 4.427 6.689 0 0 0-.451.798 4.427 6.689 0 0 0-.363.895 4.427 6.689 0 0 0-.266.97 4.427 6.689 0 0 0-.002.014 5.658 8.548 0 0 1 .507-.767 5.658 8.548 0 0 1 .646-.731 5.658 8.548 0 0 1 .709-.584 5.658 8.548 0 0 1 .757-.427 5.658 8.548 0 0 1 .788-.259 5.658 8.548 0 0 1 .805-.087 5.658 8.548 0 0 1 .287.01 5.658 8.548 0 0 1 .286.033 5.658 8.548 0 0 1 .284.055 5.658 8.548 0 0 1 .282.077 5.658 8.548 0 0 1 .28.098 5.658 8.548 0 0 1 .275.119 5.658 8.548 0 0 1 .272.14 5.658 8.548 0 0 1 .266.16 5.658 8.548 0 0 1 .26.181 5.658 8.548 0 0 1 .254.2 5.658 8.548 0 0 1 .247.22 5.658 8.548 0 0 1 .24.239 5.658 8.548 0 0 1 .23.256 5.658 8.548 0 0 1 .222.274 5.658 8.548 0 0 1 .214.29 5.658 8.548 0 0 1 .202.306 5.658 8.548 0 0 1 .107.18 4.427 6.689 0 0 0-.029-.139 4.427 6.689 0 0 0-.089-.35 4.427 6.689 0 0 0-.101-.342 4.427 6.689 0 0 0-.114-.334 4.427 6.689 0 0 0-.126-.323 4.427 6.689 0 0 0-.138-.312 4.427 6.689 0 0 0-.15-.3 4.427 6.689 0 0 0-.16-.287 4.427 6.689 0 0 0-.17-.273 4.427 6.689 0 0 0-.18-.257 4.427 6.689 0 0 0-.19-.242 4.427 6.689 0 0 0-.2-.226 4.427 6.689 0 0 0-.205-.208 4.427 6.689 0 0 0-.215-.19 4.427 6.689 0 0 0-.22-.173 4.427 6.689 0 0 0-.227-.154 4.427 6.689 0 0 0-.231-.133 4.427 6.689 0 0 0-.237-.114 4.427 6.689 0 0 0-.24-.093 4.427 6.689 0 0 0-.244-.074 4.427 6.689 0 0 0-.246-.052 4.427 6.689 0 0 0-.247-.032 4.427 6.689 0 0 0-.25-.01z" style="font-variation-settings:"wght"900;opacity:.7;fill:#000;fill-opacity:1;stroke:none;stroke-width:2.36782;stroke-linecap:round;stroke-linejoin:round;paint-order:stroke fill markers"/><path d="M601.856 21.908a3.586 3.712 0 0 0-.56.046 3.586 3.712 0 0 0-.548.136 3.586 3.712 0 0 0-.52.223 3.586 3.712 0 0 0-.48.304 3.586 3.712 0 0 0-.428.378 3.586 3.712 0 0 0-.365.443 3.586 3.712 0 0 0-.294.497 3.586 3.712 0 0 0-.216.537 3.586 3.712 0 0 0-.002.008 4.583 4.743 0 0 1 .411-.426 4.583 4.743 0 0 1 .524-.405 4.583 4.743 0 0 1 .574-.324 4.583 4.743 0 0 1 .613-.237 4.583 4.743 0 0 1 .639-.144 4.583 4.743 0 0 1 .652-.048 4.583 4.743 0 0 1 .232.006 4.583 4.743 0 0 1 .232.018 4.583 4.743 0 0 1 .23.03 4.583 4.743 0 0 1 .229.043 4.583 4.743 0 0 1 .225.054 4.583 4.743 0 0 1 .224.066 4.583 4.743 0 0 1 .22.078 4.583 4.743 0 0 1 .215.09 4.583 4.743 0 0 1 .211.1 4.583 4.743 0 0 1 .206.11 4.583 4.743 0 0 1 .2.123 4.583 4.743 0 0 1 .194.132 4.583 4.743 0 0 1 .187.142 4.583 4.743 0 0 1 .18.152 4.583 4.743 0 0 1 .173.161 4.583 4.743 0 0 1 .164.17 4.583 4.743 0 0 1 .086.1 3.586 3.712 0 0 0-.023-.077 3.586 3.712 0 0 0-.072-.195 3.586 3.712 0 0 0-.082-.19 3.586 3.712 0 0 0-.092-.184 3.586 3.712 0 0 0-.102-.18 3.586 3.712 0 0 0-.112-.173 3.586 3.712 0 0 0-.121-.166 3.586 3.712 0 0 0-.13-.16 3.586 3.712 0 0 0-.138-.15 3.586 3.712 0 0 0-.146-.144 3.586 3.712 0 0 0-.154-.134 3.586 3.712 0 0 0-.161-.125 3.586 3.712 0 0 0-.167-.116 3.586 3.712 0 0 0-.174-.106 3.586 3.712 0 0 0-.178-.095 3.586 3.712 0 0 0-.184-.086 3.586 3.712 0 0 0-.187-.073 3.586 3.712 0 0 0-.192-.064 3.586 3.712 0 0 0-.195-.052 3.586 3.712 0 0 0-.197-.04 3.586 3.712 0 0 0-.2-.03 3.586 3.712 0 0 0-.2-.017 3.586 3.712 0 0 0-.201-.006" style="font-variation-settings:"wght"900;opacity:.7;fill:#000;fill-opacity:1;stroke:none;stroke-width:1.5875;stroke-linecap:round;stroke-linejoin:round;paint-order:stroke fill markers"/><path d="M601.856 21.697a4.427 6.689 0 0 0-.692.082 4.427 6.689 0 0 0-.676.245 4.427 6.689 0 0 0-.641.402 4.427 6.689 0 0 0-.593.548 4.427 6.689 0 0 0-.528.681 4.427 6.689 0 0 0-.451.798 4.427 6.689 0 0 0-.363.895 4.427 6.689 0 0 0-.266.97 4.427 6.689 0 0 0-.002.014 5.658 8.548 0 0 1 .507-.767 5.658 8.548 0 0 1 .646-.731 5.658 8.548 0 0 1 .709-.584 5.658 8.548 0 0 1 .757-.427 5.658 8.548 0 0 1 .788-.259 5.658 8.548 0 0 1 .805-.087 5.658 8.548 0 0 1 .287.01 5.658 8.548 0 0 1 .286.034 5.658 8.548 0 0 1 .284.054 5.658 8.548 0 0 1 .282.077 5.658 8.548 0 0 1 .28.098 5.658 8.548 0 0 1 .275.119 5.658 8.548 0 0 1 .272.14 5.658 8.548 0 0 1 .266.16 5.658 8.548 0 0 1 .26.181 5.658 8.548 0 0 1 .254.2 5.658 8.548 0 0 1 .247.22 5.658 8.548 0 0 1 .24.239 5.658 8.548 0 0 1 .23.256 5.658 8.548 0 0 1 .222.274 5.658 8.548 0 0 1 .214.29 5.658 8.548 0 0 1 .202.307 5.658 8.548 0 0 1 .107.178 4.427 6.689 0 0 0-.029-.137 4.427 6.689 0 0 0-.089-.352 4.427 6.689 0 0 0-.101-.341 4.427 6.689 0 0 0-.114-.334 4.427 6.689 0 0 0-.126-.323 4.427 6.689 0 0 0-.138-.312 4.427 6.689 0 0 0-.15-.3 4.427 6.689 0 0 0-.16-.287 4.427 6.689 0 0 0-.17-.272 4.427 6.689 0 0 0-.18-.257 4.427 6.689 0 0 0-.19-.243 4.427 6.689 0 0 0-.2-.226 4.427 6.689 0 0 0-.205-.208 4.427 6.689 0 0 0-.215-.19 4.427 6.689 0 0 0-.22-.173 4.427 6.689 0 0 0-.227-.153 4.427 6.689 0 0 0-.231-.134 4.427 6.689 0 0 0-.237-.114 4.427 6.689 0 0 0-.24-.093 4.427 6.689 0 0 0-.244-.074 4.427 6.689 0 0 0-.246-.052 4.427 6.689 0 0 0-.247-.032 4.427 6.689 0 0 0-.25-.01z" style="font-variation-settings:"wght"900;opacity:.7;fill:#000;fill-opacity:1;stroke:none;stroke-width:2.36782;stroke-linecap:round;stroke-linejoin:round;paint-order:stroke fill markers"/></g><g transform="matrix(.32618 0 0 .28601 299.846 54.468)"><path d="M617.505 48.837h38.475a4 4 45 0 1 4 4v24.06a4 4 135 0 1-4 4h-38.475a4 4 45 0 1-4-4v-24.06a4 4 135 0 1 4-4z" style="font-variation-settings:"wght"900;display:inline;fill:#8fa1ad;fill-opacity:1;stroke:#000;stroke-width:6.35;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"/><path d="M621.583 84.377h30.32v6.337h-30.321v-6.336z" style="font-variation-settings:"wght"900;display:inline;fill:#738b9e;fill-opacity:1;stroke:#000;stroke-width:6.35;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"/><path d="M627.367 94.392h18.763v5.979h-18.763z" style="font-variation-settings:"wght"900;display:inline;fill:#7996b5;fill-opacity:1;stroke:#000;stroke-width:6.08542;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"/><path d="M638.524 110.976h27.88v5.272h-27.88z" style="font-variation-settings:"wght"900;display:inline;fill:#7ea1ba;fill-opacity:1;stroke:#000;stroke-width:6.08542;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"/><path d="M632.199 103.618h9.275v12.57H632.2v-12.57z" style="font-variation-settings:"wght"900;display:inline;fill:#8ba2b6;fill-opacity:1;stroke:#000;stroke-width:6.20394;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"/><circle style="font-variation-settings:"wght"900;fill:#b6d5f4;fill-opacity:1;stroke:#000;stroke-width:7.53681;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers" cx="636.793" cy="64.936" r="7.183"/></g></g></svg>`; default: return null; } } createEndOfProhibitionsIcon() { return `<svg width="200" height="200" viewBox="0 0 52.917 52.917" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><g transform="translate(-.076 30.541)scale(.0756)" style="display:inline"><circle style="opacity:1;fill:#fff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5.65454;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers" cx="351" cy="-54" r="340"/><path transform="rotate(45)" style="opacity:1;fill:#000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.04701;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers" d="M165.011-616.378h9.8v660h-9.8zm20.05 0h9.8v660h-9.8zm20.05 0h9.8v660h-9.8zm20.05 0h9.8v660h-9.8zm20.05 0h9.8v660h-9.8z"/><path d="M351-404A350 350 0 0 0 1-54a350 350 0 0 0 350 350A350 350 0 0 0 701-54a350 350 0 0 0-350-350m0 49.7A300.3 300.3 0 0 1 651.3-54 300.3 300.3 0 0 1 351 246.3 300.3 300.3 0 0 1 50.7-54 300.3 300.3 0 0 1 351-354.3" style="opacity:1;fill:#0046aa;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:315.815;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"/></g></svg>`; } // Adds the custom layer checkbox to the WME layer switcher addLayerCheckbox() { // Use setInterval to wait for the WME layer switcher to be ready const checkInterval = setInterval(() => { // Find the existing 'Map Comments' checkbox element const mapCommentsCheckbox = document.getElementById('layer-switcher-item_map_comments'); if (mapCommentsCheckbox) { clearInterval(checkInterval); // Stop checking once found const parentListItem = mapCommentsCheckbox.closest('li'); // Create the list item element for our custom layer checkbox const layerItem = document.createElement('li'); layerItem.innerHTML = ` <div class="layer-selector"> <wz-checkbox id="layer-switcher-item_${this.layerName}" checked> <div class="layer-selector-container" title="${this.scriptName}">${this.scriptName}</div> </wz-checkbox> </div>`; // Insert our checkbox item right after the 'Map Comments' item if (parentListItem) { parentListItem.insertAdjacentElement('afterend', layerItem); } else { // Fallback if the structure changes, just add it somewhere in the layer switcher const layerSwitcherList = document.querySelector('.layer-switcher .list'); if (layerSwitcherList) { layerSwitcherList.appendChild(layerItem); } else { this.log("Could not find layer switcher list to add checkbox."); return; // Cannot add checkbox, exit function } } // Get the actual wz-checkbox element this.commentLayerCheckbox = document.getElementById(`layer-switcher-item_${this.layerName}`); if (this.commentLayerCheckbox) { // Set initial visibility this.layer.setVisibility(this.commentLayerCheckbox.checked); // Add event listener to toggle layer visibility this.commentLayerCheckbox.addEventListener('change', (e) => { this.log(`Layer checkbox toggled: ${e.target.checked}`); this.layer.setVisibility(e.target.checked); if (e.target.checked) { // If turned on, immediately update display in case comments loaded while off this.updateDisplay(); } }); } else { this.log("Could not get the created layer checkbox element."); } } }, 300); } log(message) { if (this.debugMode) { console.log(`%c[${this.scriptName} v${this.version}]%c: ${message}`, 'color: #3498db; font-weight: bold;', ''); } } // Simple debounce function debounce(func, timeout = 300) { let timer; return (...args) => { clearTimeout(timer); timer = setTimeout(() => { func.apply(this, args); }, timeout); }; } } window.SDK_INITIALIZED.then(() => new WmeCommentIcons());