Greasy Fork 还支持 简体中文。

WME Edition Counter and Helper

It counts the edits and suggests a time for the next save.

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name             WME Edition Counter and Helper
// @name:es          WME Contador de Ediciones y Ayudante
// @description      It counts the edits and suggests a time for the next save.
// @description:es   Cuentas las ediciones y sugiere un tiempo para el próximo guardado.

// @author           Rodrigo_Reina
// @namespace        https://greasyfork.org/en/users/1192362-rodrigo-reina
// @version          2023.10.11.03
// @license          GNU GPLv3
// @icon             data:image/png;base64,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
// @contributionURL  https://ko-fi.com/wme_rodrigo_reina

// @include          /^https:\/\/(www|beta)\.waze\.com\/(?!user\/)(.{2,6}\/)?editor\/?.*$/
// @exclude          https://www.waze.com/user/*
// @exclude          https://www.waze.com/*/user/*
// @require          https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js
// @require          https://greasyfork.org/scripts/24851-wazewrap/code/WazeWrap.js
// @connect          www.waze.com
// @connect          greasyfork.org
// @grant            GM_xmlhttpRequest
// @grant            GM_addElement
// ==/UserScript==

/* global W */
/* global toastr */
/* global WazeWrap */

/**
* ===============================================
*  This script is based on the following scripts:
*  - "Waze Edit Count Monitor" (by MapOMatic)
*  - "Waze WME Edition Helper" (by EdwardNavarro)
* ===============================================
*/

// TODO: 
// - Supporting languages

(function main() {
    'use strict';

    const SCRIPT_NAME = 'WME Edition Counter and Helper';
    const SCRIPT_VERSION = '2023.10.11.03';
    const DOWNLOAD_URL = 'https://greasyfork.org/scripts/40313-waze-edit-count-monitor/code/Waze%20Edit%20Count%20Monitor.user.js';

    function wmeECHInjected() {
        const TOOLTIP_TEXT = '<b>Ediciones Diarias</b><br><small>(Clic para ver el perfil)<small>';
        const TOASTR_URL = 'https://cdnjs.cloudflare.com/ajax/libs/toastr.js/2.1.4/toastr.min.js';
        const VERSION = '2023.10.11.03';
        const DEBUG_LEVEL = 0;
        let _toastrSettings  = {
            timeBeforeSaving: 70,
            remindAtEditCount: 30,
            warnAtEditCount: 30,
            wasReminded: false,
            wasWarned: false
        };

        let _userName = '';
        let _lastTodayEditCount = 0;
        let _lastYesterdayEditCount = 0;
        let _lastDayBeforeEditCount = 0;
        let _savesWithoutIncrease = 0;
        let _totalSeconds = 0;
        let _timerInterval;

        let _buttonContainer,
            _buttonContentWrap,
            _buttonItemContainer,
            _buttonItemLink,
            _buttonItemContent,
            _progressBarWrap,
            _progressBarFill,
            _savedTimer;
        
        if (!localStorage.WMEEditionHelperScript) {
            let options = [null,_toastrSettings .timeBeforeSaving,_toastrSettings .remindAtEditCount,_toastrSettings .warnAtEditCount,false,false];
            localStorage.WMEEditionHelperScript = JSON.stringify(options);
        }

        function log(message, level, prefix = 'LOG', bgColor = 'darkslategrey', textColor = 'white') {
            if (message && level <= DEBUG_LEVEL) {
                console.log('%c%s%s', `background:${bgColor};color:${textColor};padding:5px 10px;`, `[${prefix}] WME Edition Counter And Helper >>`, message);
            }
        }

        function checkCounters() {
            window.postMessage(JSON.stringify(['wme_echGetCounts', _userName]), '*');
            _toastrSettings.wasReminded = false;
            _toastrSettings.wasWarned = false;
            toastr.remove();
        }

        function getChangedObjectCount() {
            let _count = 0;
            try{
                _count = parseInt($('#save-button > div.counter').text());
            }
            catch(err){
                // Do Nothing
            }
            return _count;
        }

        function updateEditCount(todayEditCount = 0, noIncrement) {
            let _textColor;
            let _bgColor;
            let _tooltipTextColor;
            
            if ($('#wme_ech').length === 0) {
                _buttonContainer = $('<div>', { id: 'wme_ech', style: 'border-radius: 15px;'});
                _buttonContentWrap = $('<div>', { class: 'toolbar-button' });
                _buttonItemLink = $('<a>', { href: 'https://www.waze.com/user/editor/' + _userName, target: '_blank', style:'text-decoration: none;', 'data-original-title': TOOLTIP_TEXT });
                _buttonItemContainer = $('<div>', { class: 'item-container' });
                _buttonItemContent = $('<div>', { style: 'margin: 7px; line-height: 1;' });

                _progressBarWrap = $('<div>', { style: 'width: 100%; height: 5px; background-color: #d7dadc;border: 1px solid #fff; box-sizing: border-box;' });
                _progressBarFill = $('<div>', { class: 'progress', style: 'width: 0%; height: 5px; animation-fill-mode: both; animation-name: progressBar; animation-duration:' + _toastrSettings.timeBeforeSaving + 's; animation-timing-function: linear;' });

                _savedTimer = $('<div>', { id: 'saved-timer', style: 'font-size:8px; line-height:1; text-align:right; color:darkgray;' });

                _buttonContainer.append(_buttonContentWrap);
                _buttonContentWrap.append(_buttonItemLink);
                _buttonItemLink.append(_buttonItemContainer);
                _buttonItemContainer.append(_buttonItemContent);

                _buttonItemContent.append(_progressBarWrap);
                _buttonItemContent.append(_savedTimer);
                _progressBarWrap.append(_progressBarFill);

                $('#toolbar > div > div.secondary-toolbar > div:nth-child(1)').after(_buttonContainer);

                _buttonItemLink.tooltip({
                    placement: 'auto top',
                    delay: { show: 100, hide: 100 },
                    html: true,
                    template: '<div class="tooltip" role="tooltip" style="opacity:0.95"><div class="tooltip-arrow"></div><div class="my-tooltip-header" style="display:block;"><b></b></div><div class="my-tooltip-body tooltip-inner" style="display:block;"></div></div>'
                });
            }

            if (_lastTodayEditCount !== todayEditCount) {
                _savesWithoutIncrease = 0;
            } else {
                if (!noIncrement) _savesWithoutIncrease += 1;
            }
            
            switch (_savesWithoutIncrease) {
                case 0:
                case 1:
                    _textColor = '#354148';
                    _bgColor = '';
                    _tooltipTextColor = 'white';
                    break;
                case 2:
                    _textColor = '#354148';
                    _bgColor = 'yellow';
                    _tooltipTextColor = 'black';
                    break;
                default:
                    _textColor = 'white';
                    _bgColor = 'red';
                    _tooltipTextColor = 'white';
            }

            _buttonContainer.css('background-color', _bgColor);
            _buttonItemContent.css('color', _textColor).html(`Ediciones: ${todayEditCount}`);
            _buttonItemContent.append(_progressBarWrap);
            _buttonItemContent.append(_savedTimer);

            let _daysCounterText = `<hr style="border:0 none; border-bottom:1px #999 solid; margin:5px 0;"/><div class="days-group"><div class="day-1"><h3>Hoy</h3><span>${todayEditCount}</span></div><div class="day-2"><h3>Ayer</h3><span>${_lastYesterdayEditCount}</span></div><div class="day-3"><h3>Antier</h3><span>${_lastDayBeforeEditCount}</span></div></div>`;
            let _warningText = (_savesWithoutIncrease > 0) ? `<div style="font-size:13px;border-radius:5px;padding:5px;margin-top:5px;color:${_tooltipTextColor};background-color:${_bgColor};"><b>${_savesWithoutIncrease}</b> salvadas/guardadas consecutivas sin incremento en el contador.<br><span style="font-weight:bold;font-size:16px;">¿Estás estrangulado?<span></div>` : '';
            _buttonItemLink.attr('data-original-title', TOOLTIP_TEXT + _daysCounterText + _warningText);

            _lastTodayEditCount = todayEditCount;
            _totalSeconds = 0;

            clearTimeout(_timerInterval);
            runTimer();
        }

        function runTimer(){
            _timerInterval = setInterval(setTime, 1000);
        }

        function setTime(){
            ++_totalSeconds;
            const hours = parseInt(_totalSeconds / 3600) % 24;
            const minutes = parseInt(_totalSeconds / 60) % 60;
            const seconds = _totalSeconds % 60;
            const timeString = `${(hours > 0) ? `${pad(hours)}:` : ''}${pad(minutes)}:${pad(seconds)}`;
            $('#saved-timer').html(timeString);
        }

        function pad(val) {
            const valString = val + '';
            return (valString.length < 2) ? '0' + valString : valString;
        }

        function receiveMessage(event) {
            let _msg;
            try {
                _msg = JSON.parse(event.data);
            } catch (err) {
                // Do nothing
            }

            if (_msg && _msg[0] === 'wme_echUpdateCounts') {
                const todayEditCount = _msg[1][0];
                const yesterdayEditCount = _msg[1][1];
                const dayBeforeEditCount = _msg[1][2];
                _lastYesterdayEditCount = yesterdayEditCount;
                _lastDayBeforeEditCount = dayBeforeEditCount;
                updateEditCount(todayEditCount);
            }
        }

        function errorHandler(callback) {
            try {
                callback();
            } catch (e) {
                console.error('%c%s%s', 'background:darkred;color:white;padding:5px 10px;', '[ERROR] WME Edition Counter And Helper >>', e);
            }
        }

        function checkChangedObjectCount() {
            let objectEditCount = getChangedObjectCount();
            
            if (objectEditCount >= _toastrSettings.warnAtEditCount && !_toastrSettings.wasWarned) {
                toastr.remove();
                toastr.error('<span style="font-size:16px;">Has editado al menos <b>' + _toastrSettings.warnAtEditCount + '</b> objetos.</span><br><br> Deberías considerar guardar pronto. Si obtienes un error al guardar, necesitarás deshacer algunos cambios/acciones e intentar nuevamente.', 'WME Edition Counter And Helper:', {timeOut: 25000});
                _toastrSettings.wasWarned = true;
                //log('WARMED', 0, 'ALERT', 'tomato')
            } else if (objectEditCount >= _toastrSettings.remindAtEditCount && !_toastrSettings.wasReminded) {
                toastr.remove();
                toastr.warning('<span style="font-size:16px;">Has editado al menos <b>' + _toastrSettings.remindAtEditCount + '</b> objetos.</span><br><br> Deberías considerar guardar pronto.', 'WME Edition Counter And Helper:', {timeOut: 15000});
                _toastrSettings.wasReminded = true;
                //log('REMINDED', 0, 'ALERT', 'orange')
            } else if (objectEditCount < _toastrSettings.remindAtEditCount) {
                _toastrSettings.wasWarned = false;
                _toastrSettings.wasReminded = false;
                toastr.remove();
                //log('REMOVED', 0, 'ALERT', 'sienna')
            }
        }

        /* helper functions */
        function getElementsByClassName(classname, node) {
            if(!node) node = document.getElementsByTagName("body")[0];
            let a = [];
            let re = new RegExp('\\b' + classname + '\\b');
            let els = node.getElementsByTagName("*");
            for (let i=0, j=els.length; i<j; i++) {
                if (re.test(els[i].className)) a.push(els[i]);
            }
            return a;
        }

        function getId(node) {
            return document.getElementById(node);
        }

        function updateAddonSettings(event) {
            _toastrSettings.timeBeforeSaving = getId('_ehSavingWaitTime').value;
            _toastrSettings.remindAtEditCount = getId('_ehRememberAfter').value;
            _toastrSettings.warnAtEditCount = getId('_ehAlertAfter').value;

            $('.progress').css('animation-duration', `${getId('_ehSavingWaitTime').value}s`);
        }

        async function init() {
            _userName = W.loginManager.user.getUsername();

            window.addEventListener('message', receiveMessage);

            // restore saved settings
            if (localStorage.WMEEditionHelperScript) {
                let options = JSON.parse(localStorage.WMEEditionHelperScript);

                _toastrSettings.timeBeforeSaving = options[1];
                _toastrSettings.remindAtEditCount = options[2];
                _toastrSettings.warnAtEditCount = options[3];
            }

            // check if sidebar is hidden
            let sidebar = getId('sidebar');
            if (sidebar.style.display == 'none') {
                log("Not logged in yet - will initialise at login", 0, 'WARN', 'orange');
                W.loginManager.events.register("login", null, init);
                return;
            }

            // check that user-info section is defined
            let userTabs = getId('user-info');
            if (userTabs === null) {
                log("Editor not initialised yet - trying again in a bit...", 0, 'WARN', 'orange');
                setTimeout(init, 789);
                return;
            }

            $('head').append(
                $('<link/>', {
                    rel: 'stylesheet',
                    type: 'text/css',
                    href: 'https://cdnjs.cloudflare.com/ajax/libs/toastr.js/2.1.4/toastr.min.css'
                }),
                $('<style type="text/css">'
                  + '#toast-container {position: absolute;} '
                  + '#toast-container > div {opacity: 0.95;} '
                  + '.toast-top-center {top: 30px;} '
                  + '#wme_ech { display:flex; } '
                  + '.toolbar .toolbar-icon-eh { color: #484848; font-size: 24px; margin: 8px 0; position: relative; text-align: center; width: 24px; } '
                  + '.progress { background-color: red; animation-fill-mode:both; } '
                  + '@keyframes progressBar { 0% { width: 0; } 99% { background-color: red; } 100% { width: 100%; background-color: green; } } '
                  + '.days-group { width:100%; display:flex; justify-content:space-between; align-item:center; } '
                  + '.days-group div { width:30%; padding:5px; background-color:darkgray; color:white; display:flex; flex-direction:column; align-item:center; border-radius:5px; } '
                  + '.days-group div h3 { font-size:12px; font-weight:bold; line-height:1; margin:5px 0; } '
                  + '.days-group div span { font-size:14px; font-weight:bold; } '
                  + '.days-group .day-1 { background-color:#22577a; } '
                  + '.days-group .day-2 { background-color:#38a3a5; } '
                  + '.days-group .day-3 { background-color:#57cc99; } '
                  + '</style>')
            );
            await $.getScript(TOASTR_URL);

            toastr.options = {
                target: '#map',
                timeOut: 9999999999,
                positionClass: 'toast-top-right',
                closeOnHover: false,
                closeDuration: 0,
                showDuration: 0,
                closeButton: true
            };

            W.model.actionManager.events.register('afterclearactions', null, () => errorHandler(checkCounters));
            W.model.actionManager.events.register('afteraction', null, () => errorHandler(checkChangedObjectCount));
            W.model.actionManager.events.register('afterundoaction', null, () => errorHandler(checkChangedObjectCount));
        
            checkCounters();
            toastr.success('Inicializado!', 'WME Edition Counter And Helper', {timeOut: 1500});
            log('Initialized!', 0, 'SUCCESS', 'green');
            //toastr.warning('<span style="font-size:16px;">Has editado al menos <b>' + _toastrSettings.remindAtEditCount + '</b> objetos.</span><br><br> Deberías considerar guardar pronto.', 'WME Edition Counter And Helper:', {timeOut: 1000});
            
            // add new box to left of the map
            let navTabs = getElementsByClassName('nav-tabs', userTabs)[0];
            let tabContent = getElementsByClassName('tab-content', userTabs)[0];
            let addon = document.createElement('section');
            addon.id = "edition-helper-addon";

            // advanced options
            let section = document.createElement('p');
            section.style.paddingTop = "0px";
            section.className = 'checkbox';
            section.id = 'advancedOptions';
            section.innerHTML = '<h4><span class="fa fa-pencil" title="WME Edition Counter And Helper"></span> WME Edition Counter And Helper</h4><div style="margin:5px 0 10px 0;"><b>Configuración</b></div>'
                + '<label for="_ehSavingWaitTime">Tiempo de espera para guardar</label><br>'
                + '<input type="number" min="1" max="3600" size="4" id="_ehSavingWaitTime" style="margin: 0 0 20px 0" /> segundos'
                + '<br>'
                + '<label for="_ehRememberAfter">Recomendar guardar despues de</label><br>'
                + '<input type="number" min="1" max="5000" size="4" id="_ehRememberAfter" style="margin: 0 0 20px 0" /> cambios'
                + '<br>'
                + '<label for="_ehAlertAfter">Alertar guardar despues de</label><br>'
                + '<input type="number" min="1" max="5000" size="4" id="_ehAlertAfter" style="margin: 0 0 20px 0" /> cambios'
            ;
            addon.appendChild(section);

            // Addon legal and credits
            addon.innerHTML += '<hr style="border:0 none; border-bottom:1px #ccc solid;">'
                + '<small><b><a href="https://greasyfork.org/en/scripts/434355-wme-edition-helper" target="_blank"><u>'
                + 'WME Edition Counter And Helper</u></a></b> &nbsp; v' + VERSION + '</small>';

            // Add tab button and panel content
            let newtab = document.createElement('li');
            newtab.innerHTML = '<a href="#sidepanel-edition-helper" data-toggle="tab"><span class="fa fa-pencil" title="WME Edition Counter And Helper"></span> WME ECH</a>';
            navTabs.appendChild(newtab);

            addon.id = "sidepanel-edition-helper";
            addon.className = "tab-pane";
            tabContent.appendChild(addon);

            getId('_ehSavingWaitTime').onchange = updateAddonSettings;
            getId('_ehRememberAfter').onchange = updateAddonSettings;
            getId('_ehAlertAfter').onchange = updateAddonSettings;

            // restore saved settings
            if (localStorage.WMEEditionHelperScript) {
                let options = JSON.parse(localStorage.WMEEditionHelperScript);

                getId('_ehSavingWaitTime').value = options[1];
                getId('_ehRememberAfter').value = options[2];
                getId('_ehAlertAfter').value = options[3];
            }

            // overload the WME exit function
            const saveEditionHelperOptions = function() {
                if (localStorage) {
                    let options = [];

                    // preserve previous options which may get lost after logout
                    if (localStorage.WMEEditionHelperScript) {
                        options = JSON.parse(localStorage.WMEEditionHelperScript);
                    }

                    options[1] = getId('_ehSavingWaitTime').value;
                    options[2] = getId('_ehRememberAfter').value;
                    options[3] = getId('_ehAlertAfter').value;

                    localStorage.WMEEditionHelperScript = JSON.stringify(options);
                }
            }

            window.addEventListener("beforeunload", saveEditionHelperOptions, false);
        }

        function bootstrap() {
            if (W && W.loginManager && W.loginManager.events && W.loginManager.events.register && W.map && W.loginManager.user) {
                log('Initializing...');
                init();
            } else {
                log('Bootstrap failed. Trying again...');
                setTimeout(bootstrap, 1000);
            }
        }

        bootstrap();
    }

    function getEditCountFromProfile(profile) {
        const { editingActivity } = profile;
        return editingActivity[editingActivity.length - 1];
    }

    function getEditCountByDayFromProfile(profile, day) {
        const { editingActivity } = profile;
        return editingActivity[editingActivity.length-day];
    }

    function receivePageMessage(event) {
        let _msg;
        try {
            _msg = JSON.parse(event.data);
        } catch (err) {
            // Do Nothing
        }

        if (_msg && _msg[0] === 'wme_echGetCounts') {
            const userName = _msg[1];
            GM_xmlhttpRequest({
                method: 'GET',
                url: `https://www.waze.com/Descartes/app/UserProfile/Profile?username=${userName}`,
                onload: res => {
                    const profile = JSON.parse(res.responseText);
                    window.postMessage(JSON.stringify(['wme_echUpdateCounts', [
                        getEditCountFromProfile(profile),
                        getEditCountByDayFromProfile(profile, 2),
                        getEditCountByDayFromProfile(profile, 3)
                    ]]), '*');
                }
            });
        }
    }

    function waitForWazeWrap() {
        return new Promise(resolve => {
            function loopCheck(tries = 0) {
                if (WazeWrap.Ready) {
                    resolve();
                } else if (tries < 1000) {
                    setTimeout(loopCheck, 200, ++tries);
                }
            }
            loopCheck();
        });
    }

    function injectScript() {
        GM_addElement('script', {
            textContent: `${wmeECHInjected.toString()} \nwmeECHInjected();`
        });

        window.addEventListener('message', receivePageMessage);
    }

    async function loadScriptUpdateMonitor() {
        let updateMonitor;
        await waitForWazeWrap();
        try {
            updateMonitor = new WazeWrap.Alerts.ScriptUpdateMonitor(SCRIPT_NAME, SCRIPT_VERSION, DOWNLOAD_URL, GM_xmlhttpRequest);
            updateMonitor.start();
        } catch (ex) {
            console.error(`${SCRIPT_NAME}:`, ex);
        }
    }

    function startFunction() {
        injectScript();
        loadScriptUpdateMonitor();
    }

    startFunction();
})();