Waze Manage Bookmarks with Backup (API Version)

Displays a UI for managing bookmarks in the Waze editor using the Userscript API, with export/import features.

当前为 2025-03-24 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Waze Manage Bookmarks with Backup (API Version)
// @namespace    https://greasyfork.org/ja/users/735907-cauliflower-carrot
// @version      2.5
// @description  Displays a UI for managing bookmarks in the Waze editor using the Userscript API, with export/import features.
// @author       aoi
// @match        https://www.waze.com/*/editor*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    const SCRIPT_ID = 'waze-bookmarks-manager';
    const defaultFontSize = '16px';
    let allowedUrls = JSON.parse(localStorage.getItem('allowedUrls')) || [];

    // Wait for WME to be fully ready
    function waitForWmeReady() {
        if (W?.userscripts?.state.isReady) {
            initializeScript();
        } else {
            document.addEventListener('wme-ready', initializeScript, { once: true });
        }
    }

    async function initializeScript() {
        // Register sidebar tab
        const { tabLabel, tabPane } = W.userscripts.registerSidebarTab(SCRIPT_ID);

        // Set tab label
        tabLabel.innerText = 'BM';
        tabLabel.title = 'Bookmark Manager';

        // Create tab content
        tabPane.innerHTML = `
            <div style="padding: 10px;">
                <h3 style="margin: 0 0 10px 0;">Bookmark Management</h3>
                <input type="text" id="urlInput" placeholder="Enter URL" style="width: 100%; padding: 5px; box-sizing: border-box;">
                <input type="text" id="nameInput" placeholder="Enter Bookmark Name" style="width: 100%; padding: 5px; box-sizing: border-box; margin-top: 5px;">
                <button id="addUrlButton" style="width: 100%; margin-top: 5px; padding: 5px; background-color: #4CAF50; color: white; border: none; border-radius: 5px;">Add Bookmark</button>
                <ul id="urlList" style="list-style: none; padding: 0; margin: 10px 0 0 0; max-height: 200px; overflow-y: auto;"></ul>
                <button id="clearCacheButton" style="width: 100%; margin-top: 15px; padding: 5px; background-color: #2196F3; color: white; border: none; border-radius: 5px;">Clear Cache</button>
                <button id="exportButton" style="width: 100%; margin-top: 10px; padding: 5px; background-color: #FF9800; color: white; border: none; border-radius: 5px;">Export Bookmarks</button>
                <input type="file" id="importButton" accept=".json" style="width: 100%; margin-top: 10px;" title="Import Bookmarks">
            </div>
        `;

        // Wait for tab to be connected to DOM
        await W.userscripts.waitForElementConnected(tabPane);

        // Update bookmark list
        updateUrlList();

        // Set up event listeners
        setupEventListeners();
    }

    // Function to update bookmark list
    function updateUrlList() {
        const urlList = document.getElementById('urlList');
        urlList.innerHTML = '';
        allowedUrls.forEach((item) => {
            const li = document.createElement('li');
            li.style.display = 'flex';
            li.style.justifyContent = 'space-between';
            li.style.alignItems = 'center';
            li.style.marginBottom = '5px';
            li.innerHTML = `<span style="flex: 1; font-size: ${defaultFontSize};"><a href="${item.url}" style="color: #2196F3;" class="bookmarkLink" data-url="${item.url}">${item.name || item.url}</a></span>`;
            const removeButton = document.createElement('button');
            removeButton.textContent = 'Remove';
            removeButton.style.marginLeft = '10px';
            removeButton.style.backgroundColor = '#F44336';
            removeButton.style.color = 'white';
            removeButton.style.border = 'none';
            removeButton.style.borderRadius = '3px';
            removeButton.style.padding = '2px 5px';
            removeButton.addEventListener('click', () => {
                allowedUrls = allowedUrls.filter(i => i.url !== item.url);
                localStorage.setItem('allowedUrls', JSON.stringify(allowedUrls));
                updateUrlList();
            });
            li.appendChild(removeButton);
            urlList.appendChild(li);
        });
    }

    // Function to clear cache and navigate
    function clearCacheAndNavigate(url) {
        caches.keys().then(names => {
            return Promise.all(names.map(name => caches.delete(name)));
        }).then(() => {
            window.location.href = url;
        });
    }

    // Set up event listeners
    function setupEventListeners() {
        document.getElementById('addUrlButton').addEventListener('click', () => {
            const urlInput = document.getElementById('urlInput');
            const nameInput = document.getElementById('nameInput');
            const newUrl = urlInput.value.trim();
            const newName = nameInput.value.trim();

            if (newUrl && !allowedUrls.some(item => item.url === newUrl)) {
                allowedUrls.push({ url: newUrl, name: newName });
                localStorage.setItem('allowedUrls', JSON.stringify(allowedUrls));
                urlInput.value = '';
                nameInput.value = '';
                updateUrlList();
            } else if (allowedUrls.some(item => item.url === newUrl)) {
                alert('This bookmark already exists.');
            } else {
                alert('Please enter a URL.');
            }
        });

        document.getElementById('clearCacheButton').addEventListener('click', () => {
            clearCacheAndNavigate(window.location.href);
        });

        document.getElementById('exportButton').addEventListener('click', () => {
            const dataStr = JSON.stringify(allowedUrls);
            const dataUri = 'data:application/json;charset=utf-8,' + encodeURIComponent(dataStr);
            const linkElement = document.createElement('a');
            linkElement.setAttribute('href', dataUri);
            linkElement.setAttribute('download', 'bookmarks.json');
            linkElement.click();
        });

        document.getElementById('importButton').addEventListener('change', (event) => {
            const file = event.target.files[0];
            if (file) {
                const reader = new FileReader();
                reader.onload = (e) => {
                    allowedUrls = JSON.parse(e.target.result);
                    localStorage.setItem('allowedUrls', JSON.stringify(allowedUrls));
                    updateUrlList();
                };
                reader.readAsText(file);
            }
        });

        document.addEventListener('click', (e) => {
            if (e.target.classList.contains('bookmarkLink')) {
                e.preventDefault();
                const targetUrl = e.target.getAttribute('data-url');
                clearCacheAndNavigate(targetUrl);
            }
        });
    }

    // Start script initialization
    waitForWmeReady();
})();