WME Beta Review Edits

copies information from WME and adds to google sheet for record of reviewed edits.

// ==UserScript==
// @name         WME Beta Review Edits
// @namespace    https://greasyfork.org/en/scripts/382070-wme-reviewedits
// @version      2024-01-23
// @description  copies information from WME and adds to google sheet for record of reviewed edits.
// @author       ramblinwreck_81
// @match     https://www.waze.com/en-US/editor*
// @match        https://www.waze.com/user/editor/*
// @grant        none
// @require      https://greasyfork.org/scripts/24851-wazewrap/code/WazeWrap.js

// ==/UserScript==
//  Much credit to crazycaveman's form filler script where a lot of this code came from./*global W, $, WazeWrap */

(function() {
    'use strict';

    var RE_Name = GM_info.script.name;
    var RE_Version = GM_info.script.version;
    var settings = {};
    var venuesObject;
    var eID;
    var rank;
    var geoOffset = 'nada';
    var objectType = '';
    var RE_Element = "";
    var maxSelectedSegments = 0;

    const objEditor = {
        name: "",
        iD: "",
        rank: "",
        date: ""
    }

    function RE_log(message) {
        console.log(`Review Edits: ${message}`);
    }


    if(W?.userscripts?.state.isReady) {
        console.log("user:", W.loginManager.user);
        console.log("segments", W.model.segments.getObjectArray());
    } else {
        document.addEventListener("wme-ready", RE_init, {
            once: true,})
    }
    getUserName(); // looks on editor profile page for reviewed editor info
    function getUserName(tries = 0) {
        if (window.location.href.indexOf("https://www.waze.com/user/editor/") > -1)
        {
            if ((W.EditorProfile.data == undefined) && tries < 30) {
                setTimeout(() => getUserName(++tries), 100);

            } else {
                localStorage.setItem("reviewEditsEditor", W.EditorProfile.data.username);
                localStorage.setItem("RE_rank", W.EditorProfile.data.rank + 1);
                localStorage.setItem("RE_eID", W.EditorProfile.data.userID);
 //               profilePageTested = true;
            }
        }

    }

    function RE_init(){
        console.log('good results');
        const { tabLabel, tabPane } = W.userscripts.registerSidebarTab("SER Review Edits");

        tabLabel.innerText = 'SER Review Edits';
        //    tabLabel.title = 'Cool script';
        tabPane.innerHTML = tabBuilder();
        //    tabPane.innerHTML = "<h1>HELLO WORLD!</h1>";

        tabPane.addEventListener("element-connected", () => {
            // at this point the tabPane is in the DOM
            RE_log('tab pane added')
            RE_addUserTab();
        }, { once: true });


        var newEdName = "";
        var editorURL = '';
        var RE_Element;

         W.selectionManager.events.on('selectionchanged', evt => {
             RE_Element = document.getElementById("reviewEditsDiv");
             if(!(RE_Element)) {
                 const model = evt.detail.selected[0]?._wmeObject;
                 if(model?.type === 'segment') {
                     objectType = 'segment';
 //                    if (evt.selected.length === 1){ // && maxSelectedSegments === 1) {
                         RE_waitForSegmentEditDiv(RE_addFormBtn);
//                     } else {
//                     }
                 }
                 if(model?.type === 'venue' || model?.type === 'residential') {

                     if (evt.detail.selected.length === 1) {

                         objectType = 'venue';
                         RE_waitForSegmentEditDiv(RE_addFormBtn);

                     }
                 }
             }
         });
        function RE_waitForSegmentEditDiv(callback, callCount = 0) {
            const div_S = $('div.segment-edit-section');
            const div_V = $('div.venue-edit-section');
            if ((div_S.length) || (div_V.length)) {
                RE_log('div_S: ' + div_S.length + 'div_V.length: ' + div_V.length);
                const selection = W.selectionManager.getSelectedFeatures()[0];
                callback();
                RE_log('callcount: ' + callCount);
            } else if (callCount < 30) {
                setTimeout(() => RE_waitForSegmentEditDiv(callback, ++callCount), 100);
            }
        }
        function RE_addFormBtn() {
            RE_log(`adding form button`);
            const selection = W.selectionManager.getSelectedFeatures();
            var REDiv = document.createElement("div"),
                REMnu = document.createElement("select"),
                REBtn = document.createElement("button");
            var formWindowName = "Review Edits result",
                formWindowSpecs = "resizable=1,menubar=0,scrollbars=1,status=0,toolbar=0";
            var editPanel,
                selElem,
                formLink;
            REDiv.id = "reviewEditsDiv";
            REDiv.className = "reviewEditsWrapper";
            //          REDiv.id = "reviewEditsDiv";
            editPanel = document.getElementById("edit-panel");
            RE_Element = "div." + objectType + "-feature-editor";
            selElem = document.querySelector(RE_Element);
            if (selection.length === 0) {
                console.log('no selection has been made');
            }
            if (document.getElementById("reviewEditsDiv") !='undefined' && document.getElementById("reviewEditsDiv") != null) {
                RE_log('element reviewEditsDiv already exists and you are adding another.')
            }

            var forms = [{
                name: "SER Editor Review",
                // testing url: "https://docs.google.com/forms/d/e/1FAIpQLSepKZpDjeHySl95eArUn5iwTuOTUPvpz0ZvqaHg7LbvmBB1Lw/viewform",
                url: "https://docs.google.com/forms/d/e/1FAIpQLSfc27FGWsN2RoX8WXYAYZ2xVIkJ1cI8u85ezV--AYsBCVuVKg/viewform",
                fields: {
                    editDate: "150500272",
                    specificPermalink: "760385393",
                    reviewingEditor: "1935699410",
                    sentToEditorPL: "325109063",
                    editorName: "343499315",
                    nameOfVenue: "418181794",
                    editorRank: "986567855"
                }
            }];

            forms.forEach(function (key, i) {
                REMnu.options.add(new Option(forms[i].name, i));
            });
            REBtn.innerHTML = "Go to Form";
            REBtn.onclick = function ()
            {
                //alert(ffMnu.options[ffMnu.selectedIndex].value+": "+forms[ffMnu.options[ffMnu.selectedIndex].value].name);
                RE_saveSettings();
                if (localStorage.getItem("reviewEditsEditor") !== "")
                {
                    objEditor.name = localStorage.getItem("reviewEditsEditor");
                    objEditor.iD = localStorage.getItem("RE_eID");
                    objEditor.rank = localStorage.getItem("RE_rank");
                } else
                {
                    alert("No editor information is available.  Before attempting to open the review edits form, open " +
                          "the respective editor profile page. Keep the profile page open while conducting the review." );
                    RE_log("No information available for reviewed editor.");
                    return;
                }
                formLink = RE_createFormLink(forms[REMnu.options[REMnu.selectedIndex].value]);
                if (typeof formLink === "undefined")
                {
                    return;
                }

                if ($("#RE-open-in-tab").prop("checked"))
                {
                    window.open(formLink, "_blank");
                } else
                {
                    window.open(formLink, formWindowName, formWindowSpecs);
                }
            }; // end of onclick function
            if (selection.length > 0)
            {
                REDiv.appendChild(REMnu);
                REDiv.appendChild(REBtn);
                $(RE_Element)[0].prepend(REDiv);

                //                return;
            }
        } // end of RE_addFormBtn
        function createURL(selected)
        {
            var permalink = "https://www.waze.com/en-US/editor?",
            segIDs = [];
            var latLon;
            var lat;
            var lon;
            var glat;
            var glon;
            var env = W.location ? W.location.code : W.app.getAppRegionCode();
            var zoom = W.map.olMap.zoom;
            var latOffset;
            var lonOffset;
            var zoomOffset;
            var type;
//            var rank = venuesObject.rank + 1;
            geoOffset = setGeoOffset(rank);
            zoomOffset = 17;
            if (selected[0]._wmeObject.type === "venue")
                { // code for selection is a place venue
                    RE_log(`Venue selected!`)
                    // works for point and area places
//                     glat = selected[0]._wmeObject.attributes.geometry.y;
//                     glon = selected[0]._wmeObject.attributes.geometry.x;
//                     latLon = WazeWrap.Geometry.ConvertTo4326(glon, glat);
//                     lat = latLon.lat;
//                     lon = latLon.lon;
                    latLon = WazeWrap.Geometry.ConvertTo4326(W.map.getOLMap().center.lon, W.map.getOLMap().center.lat)
                    lat = latLon.lat;
                    lon = latLon.lon;
                    permalink += "env=" + env + "&lon=" + lon + "&lat=" + lat + "&zoomLevel=" + zoom.toString() + "&venues=" + selected[0].id;
                } else
                { // code for if selection is a segment(s)
                    type = 'segments'
                    var i;

                    latLon = WazeWrap.Geometry.ConvertTo4326(W.map.getOLMap().center.lon, W.map.getOLMap().center.lat);
                    lat = latLon.lat;
                    lon = latLon.lon;
                    //                }

                    var zoomToRoadType = function (e) {
                        switch (e) {
                            case 12:
                            case 13:
                                return [];
                            case 14:
                                return [2, 3, 4, 6, 7, 15];
                            case 15:
                                return [2, 3, 4, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22];
                            case 16:
                            case 17:
                            case 18:
                            case 19:
                            case 20:
                            case 21:
                            case 22:
                            default:
                                return [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22];
                   }
                };
                for (i = 0; i < selected.length; i += 1)
                {
                    var segment = selected[i]._wmeObject;
                    if (segment.type === "segment")
                    {
                        segIDs.push(segment.attributes.id);
                        if (zoomToRoadType(zoom) === 0 || zoomToRoadType(zoom).indexOf(segment.attributes.roadType) === -1)
                        {
                            alert("This zoom level (" + zoom.toString() + ") cannot be used for this road type! Please increase your zoom:\n" +
                                  "Streets: 16+\nOther drivable and Non-drivable: 15+\nHighways and PS: 14+");
                            RE_log("Zoom level not correct for segment: " + zoom.toString() + " " + segment.attributes.roadType.toString());
                            return;
                        }
                    }
                }
                permalink += "env=" + env + "&lon=" + lon + "&lat=" + lat + "&zoomLevel=" + zoom.toString() + "&" + type + "=" + segIDs.join();
            } // end of model.type if test
            latOffset = lat + geoOffset;
            lonOffset = lon - geoOffset;
            function setZoom(level)
            {
                switch (level)
                {
                    case 1:
                    case 2:
                        zoomOffset = 7;
                        break;
                    case 3:
                        zoomOffset = 6;
                        break;
                    case 4:
                        zoomOffset = 6;
                        break;
                    case 5:
                    case 6:
                        zoomOffset = 6;
                        break;
                }
                return zoomOffset;
            }
            function setGeoOffset(level)
            {
                // Added 1/3/20
                geoOffset = 0;
                return geoOffset;
                switch (level)
                {
                    case 1:
                    case 2 :
                        geoOffset = .003;
                        break;
                    case 3:
                        geoOffset = .004
                        break;
                    case 4:
                        geoOffset = .005
                        break;
                    case 5:
                    case 6:
                        geoOffset = .006
                        break;
                }
                return geoOffset;
            }
            var strStartURL = 'https://www.waze.com/en-US/editor?env=usa&lon=';
            var correctURL;
            var newURL;
            editorURL = strStartURL + lonOffset + '&lat=' + latOffset + '&zoomLevel=' + zoomOffset;
            return permalink;
        } // end of createURL function

        function RE_createFormLink(formSel)
        {
            const selection = W.selectionManager.getSelectedFeatures();
            var formValues = {};
            var formFields = formSel.fields;
            var formLink = formSel.url + "?entry.";
            var formArgs = [];
            if (selection.length === 0)
            { // || selection[0].model.type !== "segment") {
                RE_log(`Nothing selected.`);
                return;
            } else
            {
                venuesObject = obtainVenuesObject();
                checkForVenue();
            }
        function checkForVenue()
        {
            if (venuesObject === undefined)
            {
                setTimeout (function()
                            {
                               checkForVenue();
                            }, 200);
            } else
            {
                completeForm();
                return;

            }
        }

        function obtainVenuesObject()
        {
           var editorActivities = [];
           var lastDate;
           var a;
           var selection = W.selectionManager.getSelectedFeatures();
//           var eID;
           var selectedObject = W.selectionManager.getSelectedFeatures()[0]._wmeObject.type;
//               console.log('RPP');
               var objId = selection[0]._wmeObject.attributes.id;
               var objUrl = "https://www.waze.com/Descartes/app/ElementHistory?objectType=" + selectedObject + "&objectID=" + objId;
               a = $.ajax(
                   {
                   url:objUrl,
                   datatype: 'json',
                   success: function(data)
                       {
                           RE_log('Descartes API call complete. Returned was: ' + a)
                       },
                   async: false
                   }).responseJSON.transactions.objects;
               console.log(a);
               // now compare username in "objEditor" to the API returned list of usernames.
               if (objEditor.name !== "")
               {
                   a.forEach((element, index) =>
                         {
                             if ((a[index].userID).toString() === objEditor.iD )
                                 {
                                     editorActivities.push(a[index].date)
                                 }

                        });
                   console.log(editorActivities);
                   switch (editorActivities.length)
                   {
                       case 0:
                       // editor name is not on the Waze map object
                           alert("The editor under review was not located on the selected map object.  Make sure " +
                                 "the editor you are reviewing has an open profile page, and if it is already open, " +
                                 "reload the profile page and try again.  Or, search for this editor on a different map object.")
                           lastDate = 0;
                           break;
                       case 1:
                           lastDate = editorActivities[0];
                           break;
                       default:
                       // editor's name appears more than once so get the most recent edit date
                       lastDate = editorActivities[0]
                       editorActivities.forEach((element, index) =>
                           {
                               if (element > lastDate)
                           {
                               lastDate = element;
                           }
                       });
                   } // end switch
               } // end of if objEditor.name is undefined
               const date = new Date(lastDate);
               const formattedDate = date.toLocaleDateString('en-US', { year: 'numeric', month: 'numeric', day: 'numeric'})
               objEditor.date = formattedDate;


//             { // it's either a point place, area place or segment but not an RPP
//                 eID = selection[0].attributes.wazeFeature._wmeObject.attributes.updatedBy;
//                 if (eID === null)
//                 {
//                     RE_log(`Unable to get updatedBy on ${selection[0].attributes.wazeFeature._wmeObject.attributes.id}`);
//                     eID = selection[0].attributes.wazeFeature._wmeObject.attributes.createdBy;
//                 }
//             }
//             objEditor.iD = eID;
//             objEditor.name = W.model.users.getObjectById(eID).attributes.userName;
//             objEditor.rank = W.model.users.getObjectById(eID).attributes.rank + 1;
            return objEditor
        } // end of obtainVenuesObject
        function completeForm()
        {
            var onePL;
            Object.keys(formFields).forEach(function (key, index)
                {
                    switch (key) {

                        case "reviewingEditor":
                            formValues[key] = W.loginManager.user.attributes.userName;
                            break;
                        case "specificPermalink":
                            onePL = createURL(selection);
                            formValues[key] = onePL;
                            if (typeof formValues.specificPermalink === "undefined")
                            {
                                RE_log(`No permalink generated`);
                                return;
                            }
                            break;
                    case "sentToEditorPL":
                        formValues[key] = onePL;
                        break;
                    case "editDate":
//                         var a;
//  //                       if(selection[0].attributes.wazeFeature._wmeObject.attributes.updatedOn === undefined)
//                         if(W.selectionManager.getSelectedFeatures()[0].attributes.wazeFeature._wmeObject.attributes.updatedOn === null)
//                         {
//                             a = W.selectionManager.getSelectedFeatures()[0].attributes.wazeFeature._wmeObject.attributes.createdOn;
//                         } else
//                         {
//                             a = W.selectionManager.getSelectedFeatures()[0].attributes.wazeFeature._wmeObject.attributes.updatedOn;
//                         }
//                         var b = new Date(a).toLocaleDateString();
//  //                       var b = new(Date(a).toLocaleDateString();
                        formValues[key] = objEditor.date;
                        break;
                    case "editorName":
 //                       var c = RE_getLastEditor(selection);
                        formValues[key] = objEditor.name;
                        break;
                    case "nameOfVenue":
                        if(selection[0]._wmeObject.type === 'venue')
                        {
                            if(selection[0]._wmeObject.attributes.residential === true)
                            {
                                formValues[key] = 'RPP';
                            } else
                            {
                                formValues[key] = selection[0]._wmeObject.attributes.name;
                            }
                        } else
                        {
                            formValues[key] = 'N/A';
                        }
                        break;
                    case "editorRank":
                        formValues[key] = objEditor.rank;
//                         if(selection[0].attributes.wazeFeature._wmeObject.type === 'venue')
//                         {
//                             formValues[key] = venuesObject.rank + 1;
//                         } else
//                         {
//                             formValues[key] = W.model.users.getObjectById(eID).rank + 1;
//                         }
                        break;
                    default:
                        RE_log(`Nothing defined for ${key}`);
                        break;
                } // end of switch

                //Add entry to form URL, if there's something to add
                if (typeof formValues[key] !== "undefined" && formValues[key] !== "")
                {
                    formArgs[index] = formFields[key] + "=" + encodeURIComponent(formValues[key]);
                }
            });
        } // end of completeForm
        formLink += formArgs.join("&entry.");
        if (objEditor.date === "12/31/1969")
        {
            return;
        } else
        {
            RE_log(`${formLink}`);
            objEditor.name = '';
            objEditor.rank = '';
            objEditor.iD = '';
            return formLink;
        }
    } // end of createFormLink


        // Unit switched (imperial/metric)
        if (W.prefs)
        {
            W.prefs.on("change:isImperial", RE_addUserTab);
        }

        if (!W.selectionManager.getSelectedFeatures)
        {
            W.selectionManager.getSelectedFeatures = W.selectionManager.getSelectedItems;
        }
        RE_log(`Init done`);
        return;
    } //end of RE_init
    function tabBuilder()
    {
        var $section = $("<div>");
        $section.html([
            '<div id="SER Review Edits">',
            '<h2>Review Edits</h2>',
            '<input type="checkbox" id="RE_Enabled" class="RE_SettingsCheckbox"><label for="RE_Enabled">Enable This Script</label>',
            '<hr>',
            '<hr>',
            '<div>',
            '</div>',
            '</div>'
        ].join(' '));
        return $section.html();
    } // end of tabBuilder function

    function RE_addUserTab()
    {
        RE_log(`adding tab`);
        var userInfo = document.getElementById("SER Review Edits"),
            REPanel = document.createElement("div"),
            RENewTabBox = document.createElement("input"),
            RENewTabLabel = document.createElement("label"),
            RETabInfo = document.createElement("div");
        REPanel.id = "sidepanel-reviewEdits";
        REPanel.className = "tab-pane";
        RE_log(`name: ${RE_Name}, Version: ${RE_Version}`);
        RETabInfo.innerHTML = '<b>' + RE_Name + '</b> v' + RE_Version;
        RENewTabBox.id = "RE-open-in-tab";
        RENewTabBox.type = "checkbox";
        RENewTabBox.name = "RE_open_tab";
        RENewTabLabel.innerHTML = "Open form in new tab";
        RENewTabLabel.for = "RE_open_tab";
        REPanel.appendChild(RETabInfo);
        REPanel.appendChild(RENewTabBox);
        REPanel.appendChild(RENewTabLabel);
        userInfo.appendChild(REPanel);
        RE_loadSettings();

    }
    function RE_loadSettings()
    {

        var REOpenInTab = localStorage.getItem("RE-open-in-tab");
        if (REOpenInTab === "1") {
            $("#RE-open-in-tab").trigger("click");
        }
        return;
    }

    function RE_saveSettings()
    {
        if ($("#RE-open-in-tab").prop("checked"))
        {
            localStorage.setItem("RE-open-in-tab", "1");
        } else {
            localStorage.setItem("RE-open-in-tab", "0");
        }
        return;
    }
})();