- // ==UserScript==
- // @name WME History Enhancer
- // @namespace http://greasemonkey.chizzum.com
- // @description Enhances map object history entries
- // @include https://*.waze.com/*editor*
- // @include https://editor-beta.waze.com/*
- // @include https://beta.waze.com/*
- // @exclude https://www.waze.com/user/*editor/*
- // @exclude https://www.waze.com/*/user/*editor/*
- // @grant none
- // @version 1.5
- // ==/UserScript==
-
- /*
- =======================================================================================================================
- Bug fixes - MUST BE CLEARED BEFORE RELEASE
- =======================================================================================================================
-
-
- =======================================================================================================================
- Things to be checked
- =======================================================================================================================
-
- */
-
- /* JSHint Directives */
- /* globals W: true */
- /* globals I18n: */
- /* globals trustedTypes: */
- /* jshint bitwise: false */
- /* jshint eqnull: true */
- /* jshint esversion: 8 */
-
- const WHE =
- {
- showDebugOutput: false,
- enhanceHistoryItemID: null,
- enhanceHistoryItemType: null,
- itemHistoryDetails: null,
- itemHistoryLoaded: false,
- prevWazeBitsPresent: null,
- wazeBitsPresent: 0,
-
- ModifyHTML: function(htmlIn)
- {
- if(typeof trustedTypes === "undefined")
- {
- return htmlIn;
- }
- else
- {
- const escapeHTMLPolicy = trustedTypes.createPolicy("forceInner", {createHTML: (to_escape) => to_escape});
- return escapeHTMLPolicy.createHTML(htmlIn);
- }
- },
- AddLog: function(logtext)
- {
- if(WHE.showDebugOutput) console.log('WHE: '+Date()+' '+logtext);
- },
- GetRestrictionLanes: function(disposition)
- {
- let retval = '';
- if(disposition == 1) retval += 'All lanes';
- else if(disposition == 2) retval += 'Left lane';
- else if(disposition == 3) retval += 'Middle lane';
- else if(disposition == 4) retval += 'Right lane';
- else retval += ' - ';
- return retval;
- },
- GetRestrictionLaneType: function(laneType)
- {
- let retval = '';
- if(laneType === null) retval += ' - ';
- else
- {
- if(laneType == 1) retval += 'HOV';
- else if(laneType == 2) retval += 'HOT';
- else if(laneType == 3) retval += 'Express';
- else if(laneType == 4) retval += 'Bus lane';
- else if(laneType == 5) retval += 'Fast lane';
- else retval += ' - ';
- }
- return retval;
- },
- GetDirectionString: function(isForward)
- {
- if(isForward === true)
- {
- return 'A-B';
- }
- else
- {
- return 'B-A';
- }
- },
- GetVehicleDescription: function(vehicleType)
- {
- let retval = null;
- let i18nLookup = null;
- if(vehicleType === 0) i18nLookup = "TRUCK";
- else if(vehicleType === 256) i18nLookup = "PUBLIC_TRANSPORTATION";
- else if(vehicleType === 272) i18nLookup = "TAXI";
- else if(vehicleType === 288) i18nLookup = "BUS";
- else if(vehicleType === 512) i18nLookup = "RV";
- else if(vehicleType === 768) i18nLookup = "TOWING_VEHICLE";
- else if(vehicleType === 1024) i18nLookup = "MOTORCYCLE";
- else if(vehicleType === 1280) i18nLookup = "PRIVATE";
- else if(vehicleType === 1536) i18nLookup = "HAZARDOUS_MATERIALS";
- else if(vehicleType === 1792) i18nLookup = "CAV";
- else if(vehicleType === 1808) i18nLookup = "EV";
- else if(vehicleType === 1824) i18nLookup = "HYBRID";
- else if(vehicleType === 1840) i18nLookup = "CLEAN_FUEL";
- if(i18nLookup !== null)
- {
- retval = I18n.lookup("restrictions.vehicle_types."+i18nLookup);
- }
- return retval;
- },
- FormatTBR: function(tbrObj)
- {
- let retval = '';
- if(tbrObj.description !== null)
- {
- retval += ' Reason: ' + tbrObj.description + '<br>';
- }
-
- if(tbrObj.timeFrames.length > 0)
- {
- retval += ' Dates: ';
- if(tbrObj.timeFrames[0].startDate === null)
- {
- retval += 'all dates';
- }
- else
- {
- retval += tbrObj.timeFrames[0].startDate + ' to ' + tbrObj.timeFrames[0].endDate;
- }
- retval += '<br>';
-
- retval += ' Days: ';
- if(tbrObj.timeFrames[0].weekdays & (1<<0)) retval += 'S';
- else retval += '-';
- if(tbrObj.timeFrames[0].weekdays & (1<<1)) retval += 'M';
- else retval += '-';
- if(tbrObj.timeFrames[0].weekdays & (1<<2)) retval += 'T';
- else retval += '-';
- if(tbrObj.timeFrames[0].weekdays & (1<<3)) retval += 'W';
- else retval += '-';
- if(tbrObj.timeFrames[0].weekdays & (1<<4)) retval += 'T';
- else retval += '-';
- if(tbrObj.timeFrames[0].weekdays & (1<<5)) retval += 'F';
- else retval += '-';
- if(tbrObj.timeFrames[0].weekdays & (1<<6)) retval += 'S';
- else retval += '-';
- retval += '<br>';
-
- retval += ' Timespan: ';
- if(tbrObj.timeFrames[0].fromTime === null)
- {
- retval += 'all day';
- }
- else
- {
- retval += tbrObj.timeFrames[0].fromTime + ' to ' + tbrObj.timeFrames[0].toTime;
- }
- retval += '<br>';
- }
-
- let vtLength = 0;
- if(tbrObj.driveProfiles.BLOCKED !== undefined)
- {
- vtLength = tbrObj.driveProfiles.BLOCKED[0].vehicleTypes.length;
- if(vtLength > 0)
- {
- retval += ' Vehicle types prohibited:<br>';
- for(let i=0; i<vtLength; i++)
- {
- retval += ' '+WHE.GetVehicleDescription(tbrObj.driveProfiles.BLOCKED[0].vehicleTypes[i])+'<br>';
- }
- }
- }
- else if(tbrObj.driveProfiles.FREE !== undefined)
- {
- vtLength = tbrObj.driveProfiles.FREE[0].vehicleTypes.length;
- if(vtLength > 0)
- {
- retval += ' Vehicle types allowed:<br>';
- for(let i=0; i<vtLength; i++)
- {
- retval += ' '+WHE.GetVehicleDescription(tbrObj.driveProfiles.FREE[0].vehicleTypes[i])+'<br>';
- }
- }
- }
- else if(tbrObj.defaultType === "BLOCKED")
- {
- retval += ' Blocked for all vehicle types<br>';
- }
-
- if(tbrObj.defaultType === "DIFFICULT")
- {
- retval += ' Difficult Turn<br>';
- }
- return retval;
- },
- // IsObject and CompareTBRs modified from original code at
- // https://dmitripavlutin.com/how-to-compare-objects-in-javascript/
- IsObject: function(object)
- {
- return object != null && typeof object === 'object';
- },
- CompareTBRs: function(tbr1, tbr2)
- {
- let retval = true;
- const keys1 = Object.keys(tbr1);
- const keys2 = Object.keys(tbr2);
- if (keys1.length !== keys2.length)
- {
- retval = false;
- }
- else
- {
- for (const key of keys1)
- {
- const val1 = tbr1[key];
- const val2 = tbr2[key];
- const areObjects = WHE.IsObject(val1) && WHE.IsObject(val2);
- if (areObjects && !WHE.CompareTBRs(val1, val2) || !areObjects && val1 !== val2)
- {
- retval = false;
- break;
- }
- }
- }
- return retval;
- },
- FormatTBRDetails: function(tbrObj)
- {
- let retval = '';
-
- let hasOld = ((tbrObj.oldValue !== undefined) && (tbrObj.oldValue.restrictions !== undefined) && (tbrObj.oldValue.restrictions.length > 0));
- let hasNew = ((tbrObj.newValue !== undefined) && (tbrObj.newValue.restrictions !== undefined) && (tbrObj.newValue.restrictions.length > 0));
-
- if((hasOld === true) || (hasNew === true))
- {
- retval += '<i>TBR ';
- if(hasOld === false)
- {
- retval += 'Added:<br>';
- for(let i = 0; i < tbrObj.newValue.restrictions.length; ++i)
- {
- if(i > 0)
- {
- retval += '<br>';
- }
- retval += WHE.FormatTBR(tbrObj.newValue.restrictions[i]);
- }
- }
- else if (hasNew === false)
- {
- retval += 'Deleted:<br>';
- for(let i = 0; i < tbrObj.oldValue.restrictions.length; ++i)
- {
- if(i > 0)
- {
- retval += '<br>';
- }
- retval += WHE.FormatTBR(tbrObj.oldValue.restrictions[i]);
- }
- }
- else
- {
- retval += 'Changed:<br>';
-
- let oldStillPresent = [];
- let newStillPresent = [];
- for(let i = 0; i < tbrObj.oldValue.restrictions.length; ++i)
- {
- for(let j = 0; j < tbrObj.newValue.restrictions.length; ++j)
- {
- if(WHE.CompareTBRs(tbrObj.oldValue.restrictions[i], tbrObj.newValue.restrictions[j]) == true)
- {
- oldStillPresent.push(i);
- newStillPresent.push(j);
- }
- }
- }
-
- let tbrsShown = 0;
- for(let i = 0; i < tbrObj.oldValue.restrictions.length; ++i)
- {
- if(oldStillPresent.indexOf(i) == -1)
- {
- if(tbrsShown == 0)
- {
- retval += 'Removed:<br>';
- }
- else
- {
- retval += '<br>';
- }
- retval += WHE.FormatTBR(tbrObj.oldValue.restrictions[i]);
- ++tbrsShown;
- }
- }
- if(tbrsShown > 0)
- {
- retval += '<br>';
- }
- tbrsShown = 0;
- for(let i = 0; i < tbrObj.newValue.restrictions.length; ++i)
- {
- if(newStillPresent.indexOf(i) == -1)
- {
- if(tbrsShown == 0)
- {
- retval += 'Added:<br>';
- }
- else
- {
- retval += '<br>';
- }
- retval += WHE.FormatTBR(tbrObj.newValue.restrictions[i]);
- ++tbrsShown;
- }
- }
- }
- retval += '</i>';
- }
- else
- {
- // not a TBR history entry...
- }
-
- return retval;
- },
- FormatClosureReason: function(rData)
- {
- let retval = "";
- if(rData == null)
- {
- retval = "<i>not provided</i>";
- }
- else
- {
- retval = rData;
- }
- return retval;
- },
- FormatClosureMTE: function(mData)
- {
- let retval = "";
- if(mData == null)
- {
- retval = "<i>not provided</i>";
- }
- else if(W.model.majorTrafficEvents.objects[mData] === undefined)
- {
- retval = "<i>data not available</i>";
- }
- else
- {
- retval = W.model.majorTrafficEvents.objects[mData].attributes.names[0].value;
- }
- return retval;
- },
- FormatClosureDetails: function(cObjA, cObjB)
- {
- let retval = '';
- if(cObjB === null)
- {
- retval += 'Reason: ' + WHE.FormatClosureReason(cObjA.reason) + '<br>';
- retval += 'MTE: ' + WHE.FormatClosureMTE(cObjA.eventId) + '<br>';
- retval += 'From: ' + cObjA.startDate + '<br>';
- retval += 'To: ' + cObjA.endDate + '<br>';
- retval += 'Direction: ' + WHE.GetDirectionString(cObjA.forward) + '<br>';
- retval += 'Ignore traffic: ' + cObjA.permanent;
- }
- else
- {
- if(cObjA.reason !== cObjB.reason)
- {
- retval += 'Reason: ' + WHE.FormatClosureReason(cObjA.reason);
- retval += ' <i>\>\>\> ' + WHE.FormatClosureReason(cObjB.reason) + '</i><br>';
- }
- if(cObjA.eventId !== cObjB.eventId)
- {
- retval += 'MTE: ' + WHE.FormatClosureMTE(cObjA.eventId);
- retval += ' <i>\>\>\> ' + WHE.FormatClosureMTE(cObjB.eventId) + '</i><br>';
- }
- if(cObjA.startDate !== cObjB.startDate)
- {
- retval += 'From: ' + cObjA.startDate;
- retval += ' <i>\>\>\> ' + cObjB.startDate + '</i><br>';
- }
- if(cObjA.endDate !== cObjB.endDate)
- {
- retval += 'To: ' + cObjA.endDate;
- retval += ' <i>\>\>\> ' + cObjB.endDate + '<i><br>';
- }
- if(cObjA.forward !== cObjB.forward)
- {
- retval += 'Direction: ' + WHE.GetDirectionString(cObjA.forward);
- retval += ' <i>\>\>\> ' + WHE.GetDirectionString(cObjB.forward) + '</i><br>';
- }
- if(cObjA.permanent !== cObjB.permanent)
- {
- retval += 'Ignore traffic: ' + cObjA.permanent;
- retval += ' <i>\>\>\> ' + cObjB.permanent + '</i><br>';
- }
- }
- return retval;
- },
- GetTIOString: function(tioValue)
- {
- let retval = I18n.lookup("turn_tooltip.instruction_override.no_opcode");
- if(tioValue !== null)
- {
- retval = I18n.lookup("turn_tooltip.instruction_override.opcodes." + tioValue);
- }
- return retval;
- },
- SegmentHistoryNameString: function(segID)
- {
- let retval = '';
- if(W.model.segments.objects[segID] !== undefined)
- {
- if(W.model.segments.objects[segID].attributes.primaryStreetID !== undefined)
- {
- const sID = W.model.segments.objects[segID].attributes.primaryStreetID;
- const sName = W.model.streets.objects[sID].attributes.name;
- if((sName === null) || (sName === ''))
- {
- retval += 'unnamed segment';
- }
- else
- {
- retval += sName;
- }
- }
- else
- {
- retval += 'unnamed segment';
- }
- }
- else
- {
- retval += 'unknown segment';
- }
- retval += ' (ID ' + segID + ')';
- return retval;
- },
- VenueHistoryFormatChanges: function(vObj, showExtendedDetails)
- {
- let tHTML = '';
- if(vObj.type === "IMAGE")
- {
- tHTML += '<br>Image update';
- }
- else if(vObj.type === "REQUEST")
- {
- if(vObj.subType === "UPDATE")
- {
- if(vObj.changedVenue !== undefined)
- {
- if(vObj.changedVenue.categories !== undefined)
- {
- tHTML += '<br>Category update';
-
- if(showExtendedDetails === true)
- {
- tHTML += "<ul>";
- for(let j = 0; j < vObj.changedVenue.categories.length; ++j)
- {
- tHTML += '<li>'+vObj.changedVenue.categories[j];
- }
- tHTML += "</ul>";
- }
- }
- if(vObj.changedVenue.entryExitPoints !== undefined)
- {
- tHTML += '<br>Entry/exit point change';
- }
- if(vObj.changedVenue.description !== undefined)
- {
- tHTML += '<br>Description change';
- if(showExtendedDetails === true)
- {
- tHTML += '<ul><li>' + vObj.changedVenue.description + "</ul>";
- }
- }
- if(vObj.changedVenue.name !== undefined)
- {
- tHTML += '<br>Name change';
- if(showExtendedDetails === true)
- {
- tHTML += '<ul><li>' + vObj.changedVenue.name + "</ul>";
- }
- }
- if(vObj.changedVenue.openingHours !== undefined)
- {
- tHTML += '<br>Opening hours change';
- if(showExtendedDetails === true)
- {
- }
- }
- if(vObj.changedVenue.url !== undefined)
- {
- tHTML += '<br>URL change';
- if(showExtendedDetails === true)
- {
- }
- }
- if(vObj.changedVenue.services !== undefined)
- {
- tHTML += '<br>Services change';
- if(showExtendedDetails === true)
- {
- tHTML += '<ul>';
- for(let i = 0; i < vObj.changedVenue.services.length; ++i)
- {
- tHTML += '<li>' + I18n.lookup('venues.services')[vObj.changedVenue.services[i]];
- }
- tHTML += '</ul>';
- }
- }
- if(vObj.changedVenue.phone !== undefined)
- {
- tHTML += '<br>Phone number change';
- if(showExtendedDetails === true)
- {
- }
- }
- if(vObj.changedVenue.aliases !== undefined)
- {
- tHTML += '<br>Alternate name change';
- if(showExtendedDetails === true)
- {
- tHTML += '<ul>';
- for(let i = 0; i < vObj.changedVenue.aliases.length; ++i)
- {
- tHTML += '<li>' + vObj.changedVenue.aliases[i];
- }
- tHTML += '</ul>';
- }
- }
- if(vObj.changedVenue.brand !== undefined)
- {
- tHTML += '<br>Brand change';
- if(showExtendedDetails === true)
- {
- }
- }
- }
- }
- else if(vObj.subType === "FLAG")
- {
- tHTML += '<br>Flagged place';
- }
- }
-
- if(tHTML === '')
- {
- tHTML += '<br>No details';
- }
- return tHTML;
- },
- ParseHistoryObject_Venue: function(tObj)
- {
- let tHTML = '';
- //// Placeholder for now...
- //let aType = tObj.actionType;
-
- return tHTML;
- },
- ParseHistoryObject_VenueUpdateRequest: function(tObj)
- {
- let tHTML = '';
- let aType = tObj.actionType;
-
- if(aType === "DELETE")
- {
- if(tObj.oldValue.approve === true)
- {
- tHTML += '<b>Approved:</b>';
- tHTML += WHE.VenueHistoryFormatChanges(tObj.oldValue, false);
- }
- else if(tObj.oldValue.approve === false)
- {
- tHTML += '<b>Rejected:</b>';
- tHTML += WHE.VenueHistoryFormatChanges(tObj.oldValue, true);
- }
- else
- {
- tHTML += '<b>Closed, no further details</b>';
- // older venueUpdateRequest objects don't have fully populated oldValues...
- }
- }
- else if(aType === "ADD")
- {
- tHTML += '<b>Editor change pending approval:</b> ';
- tHTML += WHE.VenueHistoryFormatChanges(tObj.newValue, true);
- }
-
- return tHTML;
- },
- GetStreetBits: function(segID)
- {
- let retval = null;
-
- if(segID != undefined)
- {
- let sIdx = -1;
- let cIdx = -1;
- let stIdx = -1;
- for(let i = 0; i < WHE.itemHistoryDetails.streets.length; ++i)
- {
- if(WHE.itemHistoryDetails.streets[i].id == segID)
- {
- sIdx = i;
- break;
- }
- }
-
- const cityID = WHE.itemHistoryDetails.streets[sIdx].cityID;
- for(let i = 0; i < WHE.itemHistoryDetails.cities.length; ++i)
- {
- if(WHE.itemHistoryDetails.cities[i].id == cityID)
- {
- cIdx = i;
- break;
- }
- }
-
- const stateID = WHE.itemHistoryDetails.cities[cIdx].stateID;
- for(let i = 0; i < WHE.itemHistoryDetails.states.length; ++i)
- {
- if(WHE.itemHistoryDetails.states[i].id == stateID)
- {
- stIdx = i;
- break;
- }
- }
-
- let streetName = "";
- if(sIdx != -1)
- {
- streetName = WHE.itemHistoryDetails.streets[sIdx].name;
- }
- if(streetName == "")
- {
- streetName = "(none)";
- }
-
- let cityName = "";
- if(cIdx != -1)
- {
- cityName = WHE.itemHistoryDetails.cities[cIdx].name;
- }
- if(cityName == "")
- {
- cityName = "(none)";
- }
- let stateName = "";
- if(stIdx != -1)
- {
- stateName = WHE.itemHistoryDetails.states[stIdx].name;
- }
- if(stateName == "")
- {
- stateName = "(none)";
- }
-
- retval = [];
- retval.push(streetName);
- retval.push(cityName);
- retval.push(stateName);
- }
-
- return retval;
- },
- FormatSegmentNameDetails: function(tObj)
- {
- let oldStreetBits = null;
- let newStreetBits = null;
-
- let retval = "";
- const bitIDs = ["Street: ", "City: ", "County: "];
-
- if(tObj.oldValue != undefined)
- {
- oldStreetBits = WHE.GetStreetBits(tObj.oldValue.primaryStreetID);
- }
- if(tObj.newValue != undefined)
- {
- newStreetBits = WHE.GetStreetBits(tObj.newValue.primaryStreetID);
- }
-
- if(oldStreetBits != newStreetBits)
- {
- if(oldStreetBits == null)
- {
- retval += "Added:<br>";
- for(let i = 0; i < 3; ++i)
- {
- if(newStreetBits[i] != "(none)")
- {
- retval += " ";
- retval += bitIDs[i] + newStreetBits[i]+"<br>";
- }
- }
- }
- else if(newStreetBits == null)
- {
- retval += "Deleted: "+oldStreetBits[0]+', '+oldStreetBits[1]+', '+oldStreetBits[2];
- }
- else
- {
- retval += "Changed:<br>";
- for(let i = 0; i < 3; ++i)
- {
- if(oldStreetBits[i] != newStreetBits[i])
- {
- retval += " ";
- retval += bitIDs[i] + oldStreetBits[i]+" >> "+newStreetBits[i]+"<br>";
- }
- }
- }
- }
- return retval;
- },
- ParseHistoryObject_Segment: function(tObj)
- {
- let tHTML = '';
- let aType = tObj.actionType;
-
- if(aType === "UPDATE")
- {
- tHTML += WHE.FormatSegmentNameDetails(tObj);
- tHTML += WHE.FormatTBRDetails(tObj);
- }
-
- return tHTML;
- },
- ParseHistoryObject_RoadClosure: function(tObj)
- {
- let tHTML = '';
- let aType = tObj.actionType;
-
- let cObjA = null;
- let cObjB = null;
-
- tHTML += '<b>Road closure:</b> ';
- if(aType === "ADD")
- {
- tHTML += 'added<br>';
- cObjA = tObj.newValue;
- }
- else if(aType === "DELETE")
- {
- tHTML += 'deleted<br>';
- cObjA = tObj.oldValue;
- }
- else if(aType === "UPDATE")
- {
- tHTML += 'edited<br>';
- cObjA = tObj.oldValue;
- cObjB = tObj.newValue;
- }
- tHTML += 'ID: ' + tObj.objectID + '<br>';
- tHTML += WHE.FormatClosureDetails(cObjA, cObjB);
-
- return tHTML;
- },
- GetTurnAngleString: function(angle)
- {
- let retval = I18n.lookup('lanes.override.angles')[angle];
- if(retval == undefined)
- {
- retval = 'unknown angle';
- }
- return retval;
- },
- GetLanesString: function(lanesFrom, lanesTo)
- {
- let retval = '';
- if(lanesFrom == lanesTo)
- {
- retval = 'lane '+lanesFrom;
- }
- else
- {
- retval = 'lanes '+lanesFrom+'-'+lanesTo;
- }
- return retval;
- },
- GetGuidanceModeString: function(gMode)
- {
- let retval;
- if(gMode == 0)
- {
- retval = "Waze Selected";
- }
- else if(gMode == 1)
- {
- retval = "View Only";
- }
- else if(gMode == 2)
- {
- retval = "View and Hear";
- }
- else
- {
- retval = "";
- }
- return retval;
- },
- GetLaneGuidanceUpdateString: function(tObj)
- {
- let tHTML = '<br><i>';
- if(tObj.oldValue.lanes == undefined)
- {
- tHTML += 'Lane guidance added: ';
- let lanesFrom = tObj.newValue.lanes.fromLaneIndex + 1;
- let lanesTo = tObj.newValue.lanes.toLaneIndex + 1;
- tHTML += WHE.GetLanesString(lanesFrom, lanesTo);
- let turnAngle = tObj.newValue.lanes.laneArrowAngle;
- if(tObj.newValue.lanes.angleOverride != undefined)
- {
- turnAngle = tObj.newValue.lanes.angleOverride;
- }
- tHTML += ' '+WHE.GetTurnAngleString(turnAngle);
- }
- else if(tObj.newValue.lanes == undefined)
- {
- tHTML += 'Lane guidance removed';
- }
- else
- {
- tHTML += 'Lane guidance changed: ';
-
- let lanesFromNew = tObj.newValue.lanes.fromLaneIndex + 1;
- let lanesToNew = tObj.newValue.lanes.toLaneIndex + 1;
- let lanesFromOld = tObj.oldValue.lanes.fromLaneIndex + 1;
- let lanesToOld = tObj.oldValue.lanes.toLaneIndex + 1;
- tHTML += WHE.GetLanesString(lanesFromOld, lanesToOld);
- let lanesSame = ((lanesFromNew == lanesFromOld) && (lanesToNew == lanesToOld));
-
- let turnAngleNew = tObj.newValue.lanes.laneArrowAngle;
- if(tObj.newValue.lanes.angleOverride != undefined)
- {
- turnAngleNew = tObj.newValue.lanes.angleOverride;
- }
- let turnAngleOld = tObj.oldValue.lanes.laneArrowAngle;
- if(tObj.oldValue.lanes.angleOverride != undefined)
- {
- turnAngleOld = tObj.oldValue.lanes.angleOverride;
- }
- if((turnAngleOld != turnAngleNew) || (lanesSame == false))
- {
- tHTML += ' '+WHE.GetTurnAngleString(turnAngleOld)+' > ';
- if(lanesSame == false)
- {
- tHTML += WHE.GetLanesString(lanesFromNew, lanesToNew);
- tHTML += ' ';
- }
- tHTML += WHE.GetTurnAngleString(turnAngleNew);
- }
-
- let gModeNew = tObj.newValue.lanes.guidanceMode;
- let gModeOld = tObj.oldValue.lanes.guidanceMode;
- if(gModeOld != gModeNew)
- {
- tHTML += ' '+WHE.GetGuidanceModeString(gModeOld)+' > '+WHE.GetGuidanceModeString(gModeNew);
- }
- }
- tHTML += '</i>';
-
- return tHTML;
- },
- ParseHistoryObject_NodeConnection: function(tObj)
- {
- let tHTML = '';
- let aType = tObj.actionType;
-
- let outboundTR = (tObj.objectID.fromSegID === WHE.enhanceHistoryItemID);
- if(outboundTR === true)
- {
- tHTML += '<b>Outbound turn:</b> ';
- }
- else
- {
- tHTML += '<b>Inbound turn:</b> ';
- }
- if(aType == "DELETE") tHTML += 'disabled';
- else if(aType == "ADD") tHTML += 'enabled';
- tHTML += ' from ';
-
- if(outboundTR === true)
- {
- tHTML += 'node ';
- if(tObj.objectID.fromSegFwd === true)
- {
- tHTML += 'B';
- }
- else
- {
- tHTML += 'A';
- }
- tHTML += ' to ';
- tHTML += WHE.SegmentHistoryNameString(tObj.objectID.toSegID);
- }
- else
- {
- tHTML += WHE.SegmentHistoryNameString(tObj.objectID.fromSegID);
- tHTML += ' to node ';
- if(tObj.objectID.toSegFwd === true)
- {
- tHTML += 'A';
- }
- else
- {
- tHTML += 'B';
- }
- }
- tHTML += '<br>';
-
- if(aType === "UPDATE")
- {
- if((tObj.oldValue !== undefined) && (tObj.newValue !== undefined))
- {
- if(tObj.oldValue.instructionOpCode !== tObj.newValue.instructionOpCode)
- {
- tHTML += '<i>Instruction Override changed from '+WHE.GetTIOString(tObj.oldValue.instructionOpCode)+' to '+WHE.GetTIOString(tObj.newValue.instructionOpCode)+'</i><br>';
- }
- if(tObj.oldValue.lanes !== tObj.newValue.lanes)
- {
- tHTML += WHE.GetLaneGuidanceUpdateString(tObj);
- }
- if((tObj.oldValue.turnGuidance != null) && (tObj.newValue.turnGuidance == null))
- {
- tHTML += '<i>Turn guidance deleted</i><br>';
- }
- }
- }
- else if(aType === "ADD")
- {
- if(tObj.newValue !== undefined)
- {
- if(tObj.newValue.instructionOpCode !== null)
- {
- tHTML += '<i>Instruction Override set to ' + WHE.GetTIOString(tObj.newValue.instructionOpCode)+'</i><br>';
- }
- }
- }
- else if(aType === "DELETE")
- {
- if(tObj.oldValue !== undefined)
- {
- if(tObj.oldValue.instructionOpCode !== null)
- {
- }
- }
- }
- if(aType === "UPDATE")
- {
- tHTML += WHE.FormatTBRDetails(tObj);
- if((tObj.oldValue.navigable !== null) && (tObj.oldValue.navigable !== undefined))
- {
- if((tObj.newValue.navigable !== null) && (tObj.newValue.navigable !== undefined))
- {
- if((tObj.oldValue.navigable === false) && (tObj.newValue.navigable === true))
- {
- tHTML += '<br><i>Turn enabled</i>';
- }
- else if((tObj.oldValue.navigable === true) && (tObj.newValue.navigable === false))
- {
- tHTML += '<br><i>Turn disabled</i>';
- }
- }
- }
-
- }
- else if(aType === "ADD")
- {
- tHTML += WHE.FormatTBRDetails(tObj);
- }
-
- return tHTML;
- },
- HistoryEntryToAdjust: function(lObj)
- {
- let retval;
-
- if
- (
- (lObj.getElementsByClassName('ca-geometry').length > 0) ||
- (lObj.getElementsByClassName('ca-roadType').length > 0) ||
- (lObj.getElementsByClassName('ca-fwdLaneCount').length > 0) ||
- (lObj.getElementsByClassName('ca-revLaneCount').length > 0)
- )
- {
- // For any history entry with one of these classes, the native details are sufficient, so don't touch them at all...
- retval = null;
- }
- else if (lObj.getElementsByClassName('turn-preview').length > 0)
- {
- // For turn previews, edit just the name so that it more clearly indicates which turn the preview relates to...
- retval = lObj.getElementsByClassName('ro-name')[0];
- }
- else
- {
- // For all other history entries, nuke the whole thing and replace with our own details...
- retval = lObj;
- }
-
- return retval;
- },
- EditPanelChanged: function()
- {
- let objType = W.selectionManager.getSelectedDataModelObjects()[0]?.type;
- if((objType == "segment") || (objType == "venue"))
- {
- if((document.querySelector('.toggleHistory') != null) && (document.querySelector('#wheHideHistoryBits') == null))
- {
- document.querySelector('.elementHistoryContainer').style.display='block';
- let hhbToggle = document.createElement('label');
- hhbToggle.id = "wheHideHistoryBits";
- let hhbText = "";
- if(objType == "segment")
- {
- hhbText = "Hide closures?";
- }
- else if(objType == "venue")
- {
- hhbText = "Hide rejected updates?"
- }
- hhbToggle.innerHTML = "<input type='checkbox' id='whe_cbHideHistoryBits' />" + hhbText;
- document.querySelector(".elementHistoryContainer").insertBefore(hhbToggle, null);
-
- document.querySelector('#whe_cbHideHistoryBits').addEventListener('click', WHE.UpdateHistoryEntries, true);
- document.querySelector('.toggleHistory').addEventListener('click', WHE.WaitHistoryShown, true);
- }
- }
- },
- WaitHistoryShown: function()
- {
- if(document.querySelector('.toggleHistory').innerText == "View History")
- {
- window.setTimeout(WHE.WaitHistoryShown, 100);
- }
- else
- {
- WHE.UpdateHistoryEntries();
- }
- },
- FinaliseInit: function()
- {
- let MO_EditPanel = new MutationObserver(WHE.EditPanelChanged);
- MO_EditPanel.observe(document.querySelector('#edit-panel'),{childList: true, subtree: true});
-
- WHE.AddInterceptor();
- },
- ArrayPushUnique: function(arr, obj)
- {
- let doPush = true;
- let sObj = JSON.stringify(obj);
-
- for(const i of arr)
- {
- if(JSON.stringify(i) == sObj)
- {
- doPush = false;
- break;
- }
- }
- if(doPush === true)
- {
- arr.push(obj);
- }
- return arr;
- },
- ParseHistoryResponse: function(body)
- {
- WHE.AddLog('history response received...');
-
- if(W.selectionManager.getSelectedDataModelObjects().length === 1)
- {
- WHE.enhanceHistoryItemID = W.selectionManager.getSelectedDataModelObjects()[0].attributes.id;
- WHE.AddLog('itemID = '+WHE.enhanceHistoryItemID);
-
- for(const t of body.streets.objects)
- {
- WHE.ArrayPushUnique(WHE.itemHistoryDetails.streets, t);
- }
- for(const t of body.cities.objects)
- {
- WHE.ArrayPushUnique(WHE.itemHistoryDetails.cities, t);
- }
- for(const t of body.states.objects)
- {
- WHE.ArrayPushUnique(WHE.itemHistoryDetails.states, t);
- }
- for(const t of body.countries.objects)
- {
- WHE.ArrayPushUnique(WHE.itemHistoryDetails.countries, t);
- }
- for(const t of body.users.objects)
- {
- WHE.ArrayPushUnique(WHE.itemHistoryDetails.users, t);
- }
- for(const t of body.transactions.objects)
- {
- WHE.itemHistoryDetails.transactions.push(t);
- }
- WHE.UpdateHistoryEntries();
- }
- else
- {
- WHE.AddLog('selected item count != 1, which is odd...');
- WHE.enhanceHistoryItemID = null;
- }
- },
- ProcessNativeHistoryEntry: function(lObj, listEntries)
- {
- let newLObj = WHE.HistoryEntryToAdjust(lObj);
- if(newLObj !== null)
- {
- if(newLObj.getElementsByClassName('ca-name').length > 0)
- {
- // Keep the caption part of any entry where it's stored within the tx-changed
- // element rather than outside of it - this seems to be done for anything
- // where the caption is something other than "Allowed" or "Disallowed"
- listEntries.push(newLObj.childNodes[1]);
- }
- else
- {
- // For entries where the caption is outside the element, we can overwrite the
- // whole thing...
- listEntries.push(newLObj);
- }
- }
- },
- UpdateHistoryEntries: function()
- {
- if(document.getElementsByClassName('historyContent').length === 1)
- {
- let heContainer = document.getElementsByClassName('historyContent')[0];
-
- if(heContainer.style.display === "")
- {
- let historyLength = WHE.itemHistoryDetails.transactions.length;
- WHE.AddLog("found "+historyLength+" history entries");
- let tHTML;
- let hideBits = document.querySelector('#whe_cbHideHistoryBits').checked;
- let objType = W.selectionManager.getSelectedDataModelObjects()[0]?.type;
-
- for(let i = 0; i < historyLength; ++i)
- {
- let heEntry = heContainer.getElementsByClassName('tx-item')[i];
- if(heEntry.getElementsByClassName('tx-item-header')[0].getElementsByClassName('tx-item-toggle-icon').length == 1)
- {
- if(heEntry.classList.contains('tx-item-expanded') === false)
- {
- heEntry.getElementsByClassName('tx-item-header')[0].getElementsByClassName('tx-item-toggle-icon')[0].click();
- }
-
- let historyEntry = heEntry.getElementsByClassName('tx-item-content')[0];
- let listEntries = [];
- let listEntryIdx = 0;
-
- if(objType === 'segment')
- {
- if(historyEntry.getElementsByClassName('main-changes-list').length > 0)
- {
- let lObj = historyEntry.getElementsByClassName('main-changes-list')[0];
- WHE.ProcessNativeHistoryEntry(lObj, listEntries);
- }
- }
- if(historyEntry.getElementsByClassName('related-objects-list').length > 0)
- {
- for(let rol of historyEntry.getElementsByClassName('related-objects-list'))
- {
- for(let lObj of rol.getElementsByTagName('wz-caption'))
- {
- WHE.ProcessNativeHistoryEntry(lObj, listEntries);
- }
- }
- }
-
- let hObj = WHE.itemHistoryDetails.transactions[i];
- let uIdx = -1;
- let aIdx = -1;
- let dIdx = -1;
- let s;
- for(s = 0; s < hObj.objects.length; ++s)
- {
- if((uIdx == -1) && (hObj.objects[s].actionType == "UPDATE"))
- {
- uIdx = s;
- }
- if((aIdx == -1) && (hObj.objects[s].actionType == "ADD"))
- {
- aIdx = s;
- }
- if((dIdx == -1) && (hObj.objects[s].actionType == "DELETE"))
- {
- dIdx = s;
- }
- }
- let tList = [];
- if(uIdx != -1)
- {
- for(s = 0; s < hObj.objects.length; ++s)
- {
- if(hObj.objects[s].actionType == "UPDATE")
- {
- tList.push(hObj.objects[s]);
- }
- }
- }
- if((aIdx != -1) && (aIdx < dIdx))
- {
- for(s = 0; s < hObj.objects.length; ++s)
- {
- if(hObj.objects[s].actionType == "ADD")
- {
- tList.push(hObj.objects[s]);
- }
- }
- for(s = 0; s < hObj.objects.length; ++s)
- {
- if(hObj.objects[s].actionType == "DELETE")
- {
- tList.push(hObj.objects[s]);
- }
- }
- }
- else
- {
- for(s = 0; s < hObj.objects.length; ++s)
- {
- if(hObj.objects[s].actionType == "DELETE")
- {
- tList.push(hObj.objects[s]);
- }
- }
- for(s = 0; s < hObj.objects.length; ++s)
- {
- if(hObj.objects[s].actionType == "ADD")
- {
- tList.push(hObj.objects[s]);
- }
- }
- }
-
- if(WHE.showDebugOutput === true)
- {
- console.debug(tList);
- console.debug(listEntries);
- }
-
- let hiddenBits = 0;
- for(let k = 0; k < tList.length; ++k)
- {
- tHTML = '';
- let tObj = tList[k];
- let oType = tObj.objectType;
-
- if(oType === "nodeConnection")
- {
- tHTML += WHE.ParseHistoryObject_NodeConnection(tObj);
- }
- else if(oType === "roadClosure")
- {
- tHTML += WHE.ParseHistoryObject_RoadClosure(tObj);
- }
- else if(oType === "venue")
- {
- tHTML += WHE.ParseHistoryObject_Venue(tObj);
- }
- else if(oType === "venueUpdateRequest")
- {
- tHTML += WHE.ParseHistoryObject_VenueUpdateRequest(tObj);
- }
- else if(oType === "segment")
- {
- tHTML += WHE.ParseHistoryObject_Segment(tObj);
- }
-
- if(listEntries.length > listEntryIdx)
- {
- if(tHTML !== '')
- {
- if(listEntries[listEntryIdx] !== undefined)
- {
- listEntries[listEntryIdx].innerHTML = WHE.ModifyHTML(tHTML + '<br>');
-
- if(oType === 'roadClosure')
- {
- let pObj = listEntries[listEntryIdx];
- WHE.FindCard(pObj, hideBits);
- }
- else if(oType === 'venueUpdateRequest')
- {
- // Hide/unhide the individual transaction entries for rejected PURs
- if(tObj?.oldValue?.approve === false)
- {
- let pObj = listEntries[listEntryIdx];
- if(hideBits === true)
- {
- pObj.style.display = "none";
- }
- else
- {
- pObj.style.display = "";
- }
- ++hiddenBits;
- }
- }
- }
- ++listEntryIdx;
- }
- }
- }
-
- // Venue entry cards may contain multiple transactions, so we only want to hide/restore
- // them if all of the transaction entries have also been hidden/restored (i.e. whenever
- // tList.length = hiddenBits)...
- if(objType === "venue")
- {
- if(tList.length === hiddenBits)
- {
- // -1 as we've already incremented listEntryIdx...
- let pObj = listEntries[listEntryIdx - 1];
- WHE.FindCard(pObj, hideBits);
- }
- }
- }
- }
- }
- }
- },
- FindCard: function(pObj, hideBits)
- {
- let foundCard = false;
- while(foundCard === false)
- {
- pObj = pObj.parentElement;
- foundCard = (pObj.tagName == "WZ-CARD");
- }
- if(hideBits === true)
- {
- pObj.style.display = "none";
- }
- else
- {
- pObj.style.display = "";
- }
- },
- AddInterceptor: function()
- {
- WHE.AddLog('Adding interceptor functions...');
-
- // intercept fetch() so we can detect when requests are made for object history details, and
- // grab copies of the responses - this both enables us to know when the history is being
- // viewed (requests are only made when the user clicks View History...), and avoids the need
- // to perform our own requests to get the same data (as URO+ used to do in the original
- // iteration of this code...)
-
- // https://stackoverflow.com/questions/45425169/intercept-fetch-api-requests-and-responses-in-javascript
- const origFetch = window.fetch;
- window.fetch = async (...args) =>
- {
- let [resource, config ] = args;
-
- // as we're handling everything that goes via fetch(), we let all requests through as-is,
- // except for the ones related to history fetches - these requests always include a
- // reference to "ElementHistory" in their URLs...
- if(resource.url !== undefined)
- {
- if(resource.url.indexOf('ElementHistory') != -1)
- {
- WHE.AddLog('object history being viewed...');
- if(resource.url.indexOf('&till=') == -1)
- {
- WHE.AddLog('first history entries requested, resetting tracking vars...');
- WHE.enhanceHistoryItemID = null;
- WHE.itemHistoryDetails = {streets: [], cities: [], states: [], countries: [], users: [], transactions: []};
- }
- }
- }
-
- const response = await origFetch(resource, config);
-
- // we also let all responses through as-is, except for the ones related to history fetches...
- if(response.url !== undefined)
- {
- if(response.url.indexOf('ElementHistory') != -1)
- {
- response
- .clone()
- .json()
- .then(body => WHE.ParseHistoryResponse(body));
- }
- }
-
- return response;
- };
- },
- Initialise: function()
- {
- if(document.getElementsByClassName("sandbox").length > 0)
- {
- WHE.AddLog('WME practice mode detected, script is disabled...');
- return;
- }
-
- if(document.location.href.indexOf('user') !== -1)
- {
- WHE.AddLog('User profile page detected, script is disabled...');
- return;
- }
-
- if(typeof W === 'undefined')
- {
- window.setTimeout(WHE.Initialise, 100);
- return;
- }
-
- if (W.userscripts?.state?.isReady)
- {
- WHE.FinaliseInit();
- }
- else
- {
- document.addEventListener("wme-ready", WHE.FinaliseInit, {once: true});
- }
- }
- };
- WHE.Initialise();