Page 24 of 54

Re: [Script] WME Junction Node Fixer v0.0.7.5 Dec 23 2012

Posted: Sat Oct 26, 2013 5:28 pm
by drive4fun2me
AlanOfTheBerg wrote:JNF was specifically designed to NOT enable the u-turn at the dead-end junction due to the way Waze overuses them. When the routing engine is fixed to stop doing this, then I'd agree that JNF could change.
I was not aware of the Waze routing issue. With that in mind, it makes absolute sense that JNF does not make u turns at dead end roads.

Re: [Script] WME Junction Node Fixer v0.1.6 2016-12-25

Posted: Mon Dec 26, 2016 4:26 pm
by eaglestailg8ter
New JNF isn't working for me? Using IOS

Re: [Script] WME Junction Node Fixer v0.0.8.2 2014-01-29

Posted: Sun May 11, 2014 8:05 pm
by elp2tlh
The site is apparently still having issues. However, if you modify the link to:

http://userscripts.org:8080/scripts/show/144939

(adding the ":8080" alternate port information) it will work.

Re: [Script] WME Junction Node Fixer v0.0.7.5 Dec 23 2012

Posted: Sat Dec 21, 2013 8:24 pm
by enhket
no, it is not working ;)

Re: [Script] WME Junction Node Fixer v0.0.8.2 2014-01-29

Posted: Sat Feb 08, 2014 1:50 am
by enhket
it fails when you have 2 editor tabs open (it fails on my side at least).

Re: [Script] WME Junction Node Fixer v0.0.7.5 Dec 23 2012

Posted: Thu Apr 18, 2013 5:19 pm
by esochan
There seems to be an issue with JNF0.0.7.5 today. With it on, I cannot enable/disable individual turn restrictions. With it off, I have no problems doing so.

Is anyone else having this problem?

Re: [Script] WME Junction Node Fixer v0.0.7.5 Dec 23 2012

Posted: Thu Apr 18, 2013 5:45 pm
by esochan
Sorry. I meant .7.6 (that's what I have installed), but the problem persists.

Re: [Script] WME Junction Node Fixer v0.0.8.2 2014-01-29

Posted: Fri Feb 07, 2014 8:25 pm
by fred-porto
Hi,

The "q" overload function is not working... Using latest chrome, intl editor, wme toolbox and other scripts installed. Q presses now disable all turns.

Re: [Script] WME Junction Node Fixer v0.0.9.0 2014-08-18

Posted: Mon Aug 25, 2014 5:03 am
by Fredo-p
Taco909 wrote:
doctorkb wrote:
Taco909 wrote:I'm running 0.0.8.2 and that is the only version available on Userscripts (yes, it is back up).
This is the .user.js file from the distribution.

See if you can use this in GM -- I have it working in TM.
No luck with this on GM
[EDIT]
I found that the 0.0.8.2 had stayed behind after I had removed it. I refreshed and 0.0.9.1 is not installed. Testing now.

No luck with me on Chrome with GM. It reverts to 0.0.8.2

Re: [Script] WME Junction Node Fixer v0.0.9.0 2014-08-18

Posted: Mon Aug 25, 2014 5:09 am
by Fredo-p
doctorkb wrote: See if you can use this in GM -- I have it working in TM.

Code: Select all

// ==UserScript==
// @name                WME Junction Node Fixer
// @description         Creates a new editor hotkey to lock turns, fix reverse connectivity, and restore original restricted turns.
// @include             https://*.waze.com/editor/*
// @version             0.0.9.1
// ==/UserScript==

WME_JNF_Version = "v0.0.9.1";

function WME_JNF_Bootstrap()
{
  var bGreasemonkeyServiceDefined = false;

  try {
    bGreasemonkeyServiceDefined = (typeof Components.interfaces.gmIGreasemonkeyService === "object");
  }
  catch (err) { /* Ignore */ }

  if (typeof unsafeWindow === "undefined" || ! bGreasemonkeyServiceDefined) {
    unsafeWindow    = ( function () {
      var dummyElem = document.createElement('p');
      dummyElem.setAttribute('onclick', 'return window;');
      return dummyElem.onclick();
    }) ();
  }

  /* begin running the code! */
  setTimeout(WME_JNF_Init, 500);
}

var matched = false;
var WME_Version = undefined;

// Feature detect + local reference
var storage, fail, uid;
var options = {};

var WCENC = null;
var WCSAVE = null;
var WCCHAT = null;
var WCLU = null;
var WCUR = null;
var WCMP = null;

function WME_JNF_PatchAndReload() {
  var patch;
}

function WME_JNF_SaveEnd(b) {
  console.log("WME-JNF: Save %s", b.success ? "succeeded" : "failed");
  if (b.success) {
    WCSAVE.controller.reload();
  }
}


function WME_JNF_smn(s1, s2)
{
  var pmap = [0, 10, 11, 15, 14, 1, 13, 12, 8, 0, 3, 0, 0, 0, 0, 0, 2, 7, 5, 4, 6, 9];
  var mod = null;
  if (s1.isGeometryEditable() && !s2.isGeometryEditable()) {
    console.log("only s1 can be modified");
    mod = s1;
  } else if (!s1.isGeometryEditable() && s2.isGeometryEditable()) {
    console.log("only s2 can be modified");
    mod = s2;
  } else if (s1.isGeometryEditable() && s2.isGeometryEditable()) {
    /* pick one */
    if (pmap[s1.attributes.roadType] < pmap[s2.attributes.roadType]) {
      console.log("s1 lower type");
      mod = s1;
    } else if (pmap[s1.attributes.roadType] > pmap[s2.attributes.roadType]) {
      console.log("s2 lower type");
      mod = s2;
    } else {
      if (s1.attributes.length > s2.attributes.length) {
        console.log("s1 longer");
        mod = s1;
      } else if (s1.attributes.length < s2.attributes.length) {
        console.log("s2 longer");
        mod = s2;
      } else {
        if (s1.attributes.createdOn > s2.attributes.createdOn) {
          console.log("s1 newer");
          mod = s1;
        } else if (s1.attributes.createdOn < s2.attributes.createdOn) {
          console.log("s2 newer");
          mod = s2;
        } else {
          if (s1.getID() > s2.getID()) {
            console.log("s1 higher id");
            mod = s1;
          } else {
            console.log("s2 higher id");
            mod = s2;
          }
        }
      }
    }
  } else {
    console.log("cannot modify either.");
  }
  if (mod) {
    var point;
    if (mod.geometry.components.length > 2) {
      console.log("Splitting: " + mod.getID() + " on geo point " + (Math.ceil(mod.geometry.components.length / 2) - 1) + " of " + mod.geometry.components.length);
      point = mod.geometry.components[Math.ceil(mod.geometry.components.length / 2) - 1];
    } else {
      point = mod.getCenter();
      console.log("Splitting:", mod.getID(), "at center.");
    }
    W.model.actionManager.add(new Waze.Action.SplitSegments(mod, {splitAtPoint: point}));
  }
}

function WME_JNF_CleanRBT(jct) {
  var roadTypes = { "street": 1,      "primary": 2,   "freeway": 3,   "ramp": 4,
                    "trail": 5,       "major": 6,     "minor": 7,     "dirt": 8,
                    "boardwalk": 10,  "stairway": 16, "private": 17,  "railroad": 18,
                    "runway": 19,     "parking": 20,  "service": 21};
  var typenames = { 1: "street",      2: "primary",   3: "freeway",   4: "ramp",
                    5: "trail",       6: "major",     7: "minor",     8: "dirt",
                    10: "boardwalk",  16: "stairway", 17: "private",  18: "railroad",
                    19: "runway",     20: "parking",  21: "service"};
  var prec = [4, 6, 7, 2, 1, 21, 17, 20, 8];
  if (jct.valid == true) {
    var types = {};
    var roadtype = false;
    var cities = {};
    var i = 0;
    var cityid = 0;
    var street = null;
    var city = null;
    var state = null;
    var country = null;
    var update = true;
    var street_updated = false;
    var type_updated = false;
    var nodes = {};
    
    jct.segIDs.forEach(function(segid) {
      var seg = W.model.segments.get(segid);
      for (var i = 0; i < seg.geometry.components.length; i++)
        if (!onScreen(seg))
          update = false;
    });

    if (update == false)
      return;
    
    jct.segIDs.forEach(function(segid) {
      var seg = W.model.segments.get(segid);
      if (seg.attributes.primaryStreetID) {
        street = W.model.streets.get(seg.attributes.primaryStreetID);
        city = W.model.cities.get(street.cityID);
        if (city) {
          if (!cities[street.cityID])
            cities[street.cityID] = 0;
          if (!city.isEmpty)
            cities[street.cityID] += 100;
        }
      }
      nodes[seg.attributes.toNodeID] = W.model.nodes.get(seg.attributes.toNodeID);
      nodes[seg.attributes.fromNodeID] = W.model.nodes.get(seg.attributes.fromNodeID);
    });
    Object.forEach(nodes, function(k, node) {
      node.attributes.segIDs.forEach(function(csegid) {
        var cseg = W.model.segments.get(csegid);
        if (!cseg.attributes.junctionID) {
          if (cseg.attributes.roadType != roadTypes["freeway"]) {
            if (!types[cseg.attributes.roadType])
              types[cseg.attributes.roadType] = 0;
            if (cseg.attributes.fwdDirection)
              types[cseg.attributes.roadType] += 1;
            if (cseg.attributes.revDirection)
              types[cseg.attributes.roadType] += 1;
            if (cseg.attributes.primaryStreetID) {
              street = W.model.streets.get(cseg.attributes.primaryStreetID);
              city = W.model.cities.get(street.cityID);
              if (city) {
                if (!cities[street.cityID])
                  cities[street.cityID] = 0;
                if (city.isEmpty) {
                  cities[street.cityID] += 1; 
                } else {
                  cities[street.cityID] += 2;
                }
              }
            }
          } 
        }
      });
    });
    i = 0;
    Object.forEach(cities, function(k, v) {
      if (i < v) {
        i = v;
        cityid = k;
      }
    });
    street = W.model.streets.getByAttributes({isEmpty: true, cityID: cityid}).first();
    city = W.model.cities.get(cityid);
    if (city)
      country = W.model.countries.get(city.countryID);
    state = null;
    if (city && city.stateID)
      state = W.model.states.get(city.stateID);
    var j;
    for (i = 0; i < prec.length && !roadtype; i++) {
      if (city.countryID == 234 && prec[i] == 4)
        continue;
      if (prec[i] in types) {
        if (types[prec[i]] > 3 || (types[prec[i]] && city.countryID == 234)) {
          roadtype = prec[i];
        } else {
          for (j = i+1; j < prec.length && !roadtype; j++) {
            if (types[prec[j]] > 1) {
              roadtype = prec[j];
            }
          }
        }
        if (!roadtype)
          roadtype = prec[i];
      }
    }
    jct.segIDs.forEach(function(segid) {
      var seg = W.model.segments.get(segid);
      if (seg.attributes.roadType != roadtype) {
        W.model.actionManager.add(new W.Action.UpdateObject(seg, {roadType: roadtype}));
        if (!type_updated) {
          console.log("JNF_RBT: road type: " + roadtype + " " + typenames[roadtype]);
          type_updated = true;
        }
      }
      if (!seg.attributes.primaryStreetID || (street && seg.attributes.primaryStreetID != street.id)) {
        W.model.actionManager.add(new W.Action.UpdateSegmentAddress(seg, {countryID: city.countryID, stateID: city.stateID, cityName: city.name, emptyStreet: true}));
        if (!street_updated) {
          if (state.name != "Other")
            console.log("JNF_RBT: " + city.name + ", " + state.name + ", " + country.name);
          else
            console.log("JNF_RBT: " + city.name + ", " + country.name);
          street = W.model.streets.getByAttributes({isEmpty: true, cityID: cityid}).first();
          console.log("JNF_RBT: street: %o ", street);
          street_updated = true;
        }
      }
    });
    Object.forEach(nodes, function(k, node) {
      WME_JNF_FixNode(node, false);
    });
  }
}

function WME_JNF_DAT(a) {
  if (!a.enabled)
    return;
  WME_JNF_FixNode(a.selectedFeature, true);
}

function onScreen(obj) {
  if (obj.geometry) {
    return(W.map.getExtent().intersectsBounds(obj.geometry.getBounds()));
  }
  return(false);
}

WME_JNF_FixNode = function(node, doJunctions) {
  if (!node)
    return;
  if (!node.type)
    return;
  if (node.type != "node")
    return;
  if (node.areConnectionsEditable() && onScreen(node)) {
    connections = {};
    junctions = {};
    
    for (var i = 0; i < node.attributes.segIDs.length; i++) {
      var seg = W.model.segments.get(node.attributes.segIDs[i]);
      if (seg) {
        if (seg.attributes.toNodeID == seg.attributes.fromNodeID) {
          if (seg.attributes.junctionID) {
            console.log("single node rb");
          } else {
            console.log("single node loop");
          }
          var seg1geo = seg.geometry.clone();
          var seg2geo = seg.geometry.clone();
          var seg3geo = seg.geometry.clone();
          var mod3 = seg.geometry.components.length % 3;
          for (var i = 0; i < seg.geometry.components.length / 3 - 1; i++) {
            seg1geo.components.pop();
            seg1geo.components.pop();
            seg2geo.components.pop();
            seg2geo.components.shift();
            seg3geo.components.shift();
            seg3geo.components.shift();
          }
          if (mod3 == 2) {
            seg1geo.components.pop();
            seg3geo.components.shift();
          }
          if (mod3 == 0) {
            seg1geo.components.pop();
            seg1geo.components.pop();
            seg3geo.components.shift();
            seg3geo.components.shift();
          }
          var newseg1, newseg3, ns1ls, ns3ls;
          if (node.attributes.connections) {
            newseg1 = new Waze.Feature.Vector.Segment(seg1geo);
            newseg3 = new Waze.Feature.Vector.Segment(seg3geo);
          } else {
            ns1ls = new OpenLayers.Geometry.LineString(seg1geo.getVertices());
            ns3ls = new OpenLayers.Geometry.LineString(seg3geo.getVertices());
            newseg1 = new Waze.Feature.Vector.Segment;
            newseg3 = new Waze.Feature.Vector.Segment;
          }
          newseg1.copyAttributes(seg);
          newseg3.copyAttributes(seg);
          newseg1.attributes.junctionID = null;
          newseg3.attributes.junctionID = null;
          newseg1.attributes.fromNodeID = null;
          newseg3.attributes.fromNodeID = null;
          newseg1.attributes.toNodeID = null;
          newseg3.attributes.toNodeID = null;
          if (!node.attributes.connections) {
            newseg1.geometry = ns1ls;
            newseg3.geometry = ns3ls;
            newseg1.setID(null);
            newseg3.setID(null);
            console.log("ns1:", newseg1);
            console.log("ns3:", newseg3);
          }
          var joinsegs = [];
          joinsegs.push(newseg1);
          joinsegs.push(seg);
          console.log("Disconnect:", seg.getID(), seg, "from:", node.getID(), node);
          W.model.actionManager.add(new W.Action.DisconnectSegment(seg, node));
          console.log("Disconnect:", seg.getID(), seg, "from:", node.getID(), node);
          W.model.actionManager.add(new W.Action.DisconnectSegment(seg, node));
          console.log("W.A.AddSegment(W.F.V.S(seg1geo))");
          W.model.actionManager.add(new W.Action.AddSegment(newseg1));
          console.log("W.A.AddSegment(W.F.V.S(seg3geo))");
          W.model.actionManager.add(new W.Action.AddSegment(newseg3));
          console.log("ns1:", newseg1);
          console.log("ns3:", newseg3);
          console.log("UpdateSegmentGeometry:", seg.getID(), seg);
          W.model.actionManager.add(new W.Action.UpdateSegmentGeometry(seg, seg.geometry, seg2geo));
          W.model.actionManager.add(new W.Action.ConnectSegment(node, newseg1));
          W.model.actionManager.add(new W.Action.ConnectSegment(node, newseg3));
          console.log("Add Node at", seg1geo.components.last());
          W.model.actionManager.add(new W.Action.AddNode(seg1geo.components.last(), joinsegs));
          joinsegs = []
          joinsegs.push(seg);
          joinsegs.push(newseg3);
          console.log("Add Node at", seg3geo.components.first());
          W.model.actionManager.add(new W.Action.AddNode(seg3geo.components.first(), joinsegs));
          W.model.actionManager.add(new W.Action.UpdateObject(newseg1, {fwdTurnsLocked: true, revTurnsLocked: true}));
          W.model.actionManager.add(new W.Action.UpdateObject(seg, {fwdTurnsLocked: true, revTurnsLocked: true}));
          W.model.actionManager.add(new W.Action.UpdateObject(newseg3, {fwdTurnsLocked: true, revTurnsLocked: true}));
          W.model.actionManager.add(new W.Action.ModifyAllConnections(newseg1.getToNode(), true));
          W.model.actionManager.add(new W.Action.ModifyAllConnections(newseg1.getFromNode(), true));
          W.model.actionManager.add(new W.Action.ModifyAllConnections(seg.getToNode(), true));
          W.model.actionManager.add(new W.Action.ModifyAllConnections(seg.getFromNode(), true));
          W.model.actionManager.add(new W.Action.ModifyAllConnections(newseg3.getToNode(), true));
          W.model.actionManager.add(new W.Action.ModifyAllConnections(newseg3.getFromNode(), true));
        }
        if (!seg.isDeleted()) {
          // store any roundabouts we see
          if (seg.attributes.junctionID) {
            junctions[seg.attributes.junctionID] = W.model.junctions.get(seg.attributes.junctionID);
          }

          // terminate unterminated dead-ends
          var segments = [];
          segments.push(seg);
          if (seg.attributes.toNodeID == null) {
            W.model.actionManager.add(new W.Action.AddNode(seg.geometry.components.last(), segments));
          }
          if (seg.attributes.fromNodeID == null) {
            W.model.actionManager.add(new W.Action.AddNode(seg.geometry.components.first(), segments));
          }

          var toNode = seg.getToNode();
          var fromNode = seg.getFromNode();
          if (toNode && fromNode && !toNode.isDeleted() && !fromNode.isDeleted()) {
            if (onScreen(toNode) && onScreen(fromNode)) {
              if ((seg.attributes.fwdDirection == false || seg.attributes.revDirection == false) && (toNode.attributes.segIDs.length < 2 || fromNode.attributes.segIDs.length < 2))
              {
                console.log("JNF: updating dead-end segment " + seg.getID() + " to two-way");
                W.model.actionManager.add(new W.Action.UpdateObject(seg, {fwdDirection: true, revDirection: true}));
              }
              if (toNode.attributes.connections) {
                // old editor
                if (toNode.attributes.segIDs.length < 2 && !toNode.isTurnAllowed(seg, seg)) {
                  console.log("Enabling dead-end u-turn at", toNode.getID(), "on", seg.getID());
                  W.model.actionManager.add(new W.Action.ModifyConnection(seg.getID(), toNode, seg.getID(), true));
                  if (!seg.attributes.fwdTurnsLocked) {
                     W.model.actionManager.add(new W.Action.UpdateObject(seg, {fwdTurnsLocked: true}));
                  }
                }
                if (fromNode.attributes.segIDs.length < 2 && !fromNode.isTurnAllowed(seg, seg)) {
                  console.log("Enabling dead-end u-turn at", toNode.getID(), "on", seg.getID());
                  W.model.actionManager.add(new W.Action.ModifyConnection(seg.getID(), fromNode, seg.getID(), true));
                  if (!seg.attributes.revTurnsLocked) {
                     W.model.actionManager.add(new W.Action.UpdateObject(seg, {revTurnsLocked: true}));
                  }
                }
              } else {
                // beta editor
                if (toNode.attributes.segIDs.length < 2 && !seg.isTurnAllowed(seg, toNode)) {
                  console.log("Enabling dead-end u-turn at", toNode.getID(), "on", seg.getID());
                  W.model.actionManager.add(new W.Action.ModifyConnection(seg.getID(), toNode, seg.getID(), true));
                }
                if (fromNode.attributes.segIDs.length < 2 && !seg.isTurnAllowed(seg, fromNode)) {
                  console.log("Enabling dead-end u-turn at", toNode.getID(), "on", seg.getID());
                  W.model.actionManager.add(new W.Action.ModifyConnection(seg.getID(), fromNode, seg.getID(), true));
                }
              }
            }
            if (node.attributes.segIDs.length > 1) {
              // disable u-turns
              var turnAllowed;
              if (node.attributes.connections) {
                // old editor
                turnAllowed = node.isTurnAllowed(seg, seg);
              } else {
                // beta editor
                turnAllowed = seg.isTurnAllowed(seg, node);
              }
              if (turnAllowed) {
                console.log("Disabling U-Turn at", node.getID(), "on", seg.getID());
                W.model.actionManager.add(new W.Action.ModifyConnection(seg.getID(), node, seg.getID(), false));
              }
            }
          }
        }
      }
    }
    
    if (node.attributes.segIDs.length > 1) {
      var seg1, seg2;
      for (var i = 0; i < node.attributes.segIDs.length - 1; i++) {
        seg1 = W.model.segments.get(node.attributes.segIDs[i]);
        for (var j = i + 1; j < node.attributes.segIDs.length; j++) {
          seg2 = W.model.segments.get(node.attributes.segIDs[j]);
          if (seg1.isDeleted() == false && seg2.isDeleted() == false) {
            var fwd_t;
            var rev_t;
            var fwd_a;
            var rev_a;
            if (node.attributes.connections) {
              fwd_t = node.isTurnAllowed(seg1, seg2);
              rev_t = node.isTurnAllowed(seg2, seg1);
            } else {
              fwd_t = seg1.isTurnAllowed(seg2, node);
              rev_t = seg2.isTurnAllowed(seg1, node);
            }
            fwd_a = node.isTurnAllowedBySegDirections(seg1, seg2);
            rev_a = node.isTurnAllowedBySegDirections(seg2, seg1);
            if (fwd_t && !fwd_a) {
              console.log("Disabling RevCon at", node.getID(), "into", seg2.getID());
              W.model.actionManager.add(new W.Action.ModifyConnection(seg1.getID(), node, seg2.getID(), false));
            }
            if (rev_t && !rev_a) {
              console.log("Disabling RevCon at", node.getID(), "into", seg1.getID());
              W.model.actionManager.add(new W.Action.ModifyConnection(seg2.getID(), node, seg1.getID(), false));
            }
            if ((seg1.attributes.fromNodeID == seg2.attributes.fromNodeID && seg1.attributes.toNodeID == seg2.attributes.toNodeID) ||
                (seg1.attributes.fromNodeID == seg2.attributes.toNodeID && seg1.attributes.toNodeID == seg2.attributes.fromNodeID)) {
              console.log("sid:", seg1.getID(), "and sid:", seg2.getID(), "connected to same nodes:", seg1.attributes.fromNodeID, seg1.attributes.toNodeID);
              WME_JNF_smn(seg1, seg2);
            }
          }
        }
        if (!seg1.isDeleted() && !seg1.areTurnsLocked(node)) {
          var attr = seg1.getTurnsLockAttribute(node);
          var dict = {}
          dict[attr] = true;
          console.log("Locking Turns at", node.getID(), "on", seg1.getID());
          W.model.actionManager.add(new W.Action.UpdateObject(seg1, dict));
        }
      }
      if (!seg2.isDeleted() && !seg2.areTurnsLocked(node)) {
        var attr = seg2.getTurnsLockAttribute(node);
        var dict = {}
        dict[attr] = true;
        console.log("Locking Turns at", node.getID(), "on", seg2.getID());
        W.model.actionManager.add(new W.Action.UpdateObject(seg2, dict));
      }
    }

    if (doJunctions) {
      // clean up roundabouts
      Object.forEach(junctions, function(i, j) {
        WME_JNF_CleanRBT(j);
      });
    }
    
    // refresh turn arrows
    WCENC.toggleShowAllArrows();
    WCENC.toggleShowAllArrows();
  }
}

function WME_JNF_CheckAPI() {
  if (typeof(Waze) != "object") {
    matched = "Waze";
    return false;
  }
  if (typeof(W.model) != "object") {
    matched = "W.model";
    return false;
  }
  if (typeof(W.map) != "object") {
    matched = "W.map";
    return false;
  }
  if (typeof(W.map.controls) != "object") {
    matched = "W.map.controls";
    return false;
  }
  if (typeof(W.map.controls[0]) != "object") {
    matched = "W.map.controls[0]";
    return false;
  }
  if (typeof(W.map.controls[0].displayClass) != "string") {
    matched = "W.map.controls[0].displayClass";
    return false;
  }
  Object.forEach(W.map.controls, function(k, v) {
    if (v.displayClass == "WazeControlEditNodeConnections") {
      WCENC = v;
    }
    if (v.displayClass == "WazeControlSave") {
      WCSAVE = v;
    }
    if (v.displayClass == "WazeControlChat") {
      WCCHAT = v;
    }
    if (v.displayClass == "WazeControlMapProblems") {
      WCMP = v;
    }
    if (v.displayClass == "WazeControlUpdateRequests") {
      WCUR = v;
    }
    if (v.displayClass == "WazeControlLiveUsers") {
      WCLU = v;
    }
  });
  if (typeof(WCENC) != "object") {
    matched = "WCENC";
    return false;
  }
  if (typeof(WCSAVE) != "object") {
    matched = "WCSAVE";
    return false;
  }
  if (typeof(WCCHAT) != "object") {
    matched = "WCCHAT";
    return false;
  }
  if (typeof(WCMP) != "object") {
    matched = "WCMP";
    return false;
  }
  if (typeof(WCUR) != "object") {
    matched = "WCUR";
    return false;
  }
  if (typeof(WCLU) != "object") {
    matched = "WCLU";
    return false;
  }
  if (typeof(Waze.Config) != "object") {
    matched = "Waze.Config";
    return false;
  }
  if (typeof(Waze.Config.cameras) != "object") {
    matched = "Waze.Config.cameras";
    return false;
  }
  if (typeof(Waze.Config.cameras.minDisplayZoom) != "number") {
    matched = "Waze.Config.cameras.minDisplayZoom";
    return false;
  }
  if (typeof(W.model.cameras) != "object") {
    matched = "W.model.cameras";
    return false;
  }
  if (typeof(W.model.cameras.minZoom) != "number") {
    matched = "W.model.cameras.minZoom";
    return false;
  }
  if (typeof(W.map.toggleFullscreen) != "function") {
    matched = "W.map.toggleFullscreen";
    return false;
  }
  if (typeof(WCENC.showAllArrows) != "boolean") {
    matched = "WCENC.showAllArrows";
    return false;
  }
  if (typeof(WCENC.showArrows) != "boolean") {
    matched = "WCENC.showArrows";
    return false;
  }
  if (typeof(WCENC.toggleShowAllArrows) != "function") {
    matched = "WCENC.toggleShowAllArrows";
    return false;
  }
  if (typeof(WCSAVE.controller) != "object") {
    matched = "WCSAVE.controller";
    return false;
  }
  if (typeof(WCSAVE.controller.events) != "object") {
    matched = "WCSAVE.controller.events";
    return false;
  }
  if (typeof(WCSAVE.controller.events.register) != "function") {
    matched = "WCSAVE.controller.events.register";
    return false;
  }
  if (typeof(W.map.DefaultPanInPixel) != "number") {
    matched = "W.map.DefaultPanInPixel";
    return false;
  }
  if (typeof(Waze.accelerators) != "object") {
    if (typeof(Waze.Accelerators) != "object") {
      matched = "Waze.accelerators";
      return false;
    } else {
      Waze.accelerators = Waze.Accelerators;
    }
  }
  if (typeof(Waze.accelerators.events) != "object") {
    matched = "Waze.accelerators.events";
    return false;
  }
  if (typeof(Waze.accelerators.events.listeners) != "object") {
    matched = "Waze.accelerators.events.listeners";
    return false;
  }
  if (typeof(Waze.accelerators.events.listeners.disallowAllConnections) != "object") {
    matched = "Waze.accelerators.events.listeners.disallowAllConnections";
    return false;
  }
  if (typeof(Waze.accelerators.events.listeners.disallowAllConnections[0]) != "object") {
    matched = "Waze.accelerators.events.listeners.disallowAllConnections[0]";
    return false;
  }
  if (typeof(Waze.accelerators.events.listeners.disallowAllConnections[0].func) != "function") {
    matched = "Waze.accelerators.events.listeners.disallowAllConnections[0].func";
    return false;
  }
  if (typeof(Waze.accelerators.events.listeners.allowAllConnections) != "object") {
    matched = "Waze.accelerators.events.listeners.allowAllConnections";
    return false;
  }
  if (typeof(Waze.accelerators.events.listeners.allowAllConnections[0]) != "object") {
    matched = "Waze.accelerators.events.listeners.allowAllConnections[0]";
    return false;
  }
  if (typeof(Waze.accelerators.events.listeners.allowAllConnections[0].func) != "function") {
    matched = "Waze.accelerators.events.listeners.allowAllConnections[0].func";
    return false;
  }
  if (typeof(W.map.getExtent) != "function") {
    matched = "W.map.getExtent";
    return false;
  }
  var tstvar = W.map.getExtent();
  if (typeof(tstvar) != "object") {
    matched = "W.map.getExtent()";
    return false;
  }
  if (typeof(tstvar.toGeometry) != "function") {
    matched = "W.map.getExtent().toGeometry";
    return false;
  }
  var tstvar = tstvar.toGeometry();
  if (typeof(tstvar) != "object") {
    matched = "W.map.getExtent().toGeometry() == object";
    return false;
  }
  if (typeof(tstvar.containsPoint) != "function") {
    matched = "W.map.getExtent().toGeometry().containsPoint";
    return false;
  }
  if (typeof(W.model.segments) != "object") {
    matched = "W.model.segments";
    return false;
  }
  if (typeof(W.model.segments.get) != "function") {
    matched = "W.model.segments.get";
    return false;
  }
  if (typeof(W.model.nodes) != "object") {
    matched = "W.model.nodes";
    return false;
  }
  if (typeof(W.model.nodes.get) != "function") {
    matched = "W.model.nodes.get";
    return false;
  }
  if (typeof(W.model.junctions) != "object") {
    matched = "W.model.junctions";
    return false;
  }
  if (typeof(W.model.junctions.get) != "function") {
    matched = "W.model.junctions.get";
    return false;
  }
  if (typeof(W.model.streets) != "object") {
    matched = "W.model.streets";
    return false;
  }
  if (typeof(W.model.streets.get) != "function") {
    matched = "W.model.streets.get";
    return false;
  }
  if (typeof(W.model.streets.getByAttributes) != "function") {
    matched = "W.model.streets.getByAttributes";
    return false;
  }
  if (typeof(W.model.cities) != "object") {
    matched = "W.model.cities";
    return false;
  }
  if (typeof(W.model.cities.get) != "function") {
    matched = "W.model.cities.get";
    return false;
  }
  if (typeof(W.model.countries) != "object") {
    matched = "W.model.countries";
    return false;
  }
  if (typeof(W.model.countries.get) != "function") {
    matched = "W.model.countries.get";
    return false;
  }
  if (typeof(W.model.actionManager) != "object") {
    matched = "W.model.actionManager";
    return false;
  }
  if (typeof(W.model.actionManager.add) != "function") {
    matched = "W.model.actionManager.add";
    return false;
  }
  if (typeof(W.Action) != "function") {
    matched = "W.Action";
    return false;
  }
  if (typeof(W.Action.UpdateObject) != "function") {
    matched = "W.Action.UpdateObject";
    return false;
  }
  if (typeof(W.Action.UpdateSegmentAddress) != "function") {
    matched = "W.Action.UpdateSegmentAddress";
    return false;
  }
  if (typeof(W.Action.DisconnectSegment) != "function") {
    matched = "W.Action.DisconnectSegment";
    return false;
  }
  if (typeof(W.Action.UpdateSegmentGeometry) != "function") {
    matched = "W.Action.UpdateSegmentGeometry";
    return false;
  }
  if (typeof(W.Action.AddSegment) != "function") {
    matched = "W.Action.AddSegment";
    return false;
  }
  if (typeof(W.Action.AddNode) != "function") {
    matched = "W.Action.AddNode";
    return false;
  }
  if (typeof(W.Action.ConnectSegment) != "function") {
    matched = "W.Action.ConnectSegment";
    return false;
  }
  if (typeof(W.Action.ModifyAllConnections) != "function") {
    matched = "W.Action.ModifyAllConnections";
    return false;
  }
  if (typeof(Waze.Feature) != "object") {
    matched = "Waze.Feature";
    return false;
  }
  if (typeof(Waze.Feature.Vector) != "function") {
    matched = "Waze.Feature.Vector";
    return false;
  }
  if (typeof(Waze.Feature.Vector.Segment) != "function") {
    matched = "Waze.Feature.Vector.Segment";
    return false;
  }
  if (typeof(W.model.nodes.objects) != "object") {
    matched = "W.model.nodes.objects";
    return false;
  }
  tstvar = Object.keys(W.model.nodes.objects);
  if (typeof(tstvar) != "object") {
    matched = "W.model.nodes.objects keys";
    return false;
  }
  return true;
}

function WME_JNF_RestoreSettings() {
  // restore saved setting
  if (storage) {
    console.log("WME-JNF: loading options");
    options = JSON.parse(storage.getItem('WME_JNF'));
    if (options == null) {
      console.log("no options");
      return;
    }
    if (WCENC) {
      WCENC.showAllArrows = options['showallarrows'];
      WCENC.showArrows = options['showarrows'];
      WCENC.toggleShowAllArrows();
      WCENC.toggleShowAllArrows();
    } else {
      console.log("no WCENC");
    }
    if (WCSAVE) {
      WCSAVE.controller.events.register("saveend", this, WME_JNF_SaveEnd);
      WME_JNF_PatchAndReload();
    } else {
      console.log("no WCSAVE");
    }
    if (options['fullscreen']) {
      if (options['fullscreen'] == "fullscreen" && document.body.className != "fullscreen") {
        console.log("going fullscreen");
        W.map.toggleFullscreen();
      }
    }
  }
//  W.model.events.unregister("mergeend", this, WME_JNF_RestoreSettings);
}

function WME_JNF_OnUnload() {
    if (storage) {
      console.log("WME-JNF: saving options");
      options = {};

      //    options['hotkey'] = getId('_cbJNF_Hotkey');
      Object.forEach(W.map.controls, function(k, v) {
        if (v.displayClass == "WazeControlEditNodeConnections") {
          options['showallarrows'] = v.showAllArrows;
          options['showarrows'] = v.showArrows;
        }
      });
      options['fullscreen'] = document.body.className;
      storage.setItem('WME_JNF', JSON.stringify(options));
    }
}

function WME_JNF_Hook() {
  console.log("WME-JNF: Hook");
  // make cameras visible at zoom 0 and load at zoom 1
  Waze.Config.cameras.minDisplayZoom = 0;
  W.model.cameras.minZoom = 0;
  
  // update pan amount so keyboard panning is useful
  W.map.DefaultPanInPixel = W.map.size.h / 4;

  $(window).on("beforeunload", WME_JNF_OnUnload);

  // hook 'q'
  Waze.accelerators.events.listeners.disallowAllConnections[0].func = function() {
    WME_JNF_DAT(this);
  }

  // hook 'w'
  Waze.accelerators.events.listeners.allowAllConnections[0].func = function() {
    if (typeof(allowAllConnections) == 'function') {
      allowAllConnections();
    } else {
      this.setAllConnections(true);
    }
    
    // refresh turn arrows
    WCENC.toggleShowAllArrows();
    WCENC.toggleShowAllArrows();
  }
//  W.model.events.unregister("mergeend", this, WME_JNF_Hook);
}

var init_tries = 0;

function WME_JNF_Init() {
  console.log("WME-JNF: " + WME_JNF_Version + " starting");
  
  try {
    uid = new Date;
    (storage = window.localStorage).setItem(uid, uid);
    fail = storage.getItem(uid) != uid;
    storage.removeItem(uid);
    fail && (storage = false);
  } catch(e) {}
  
  console.log("WME-JNF: Checking API");
  if (WME_JNF_CheckAPI()) {
    WME_JNF_RestoreSettings();
    WME_JNF_Hook();
  } else {
    console.log("WME-JNF: failed API check, exiting. " + matched);
    alert("WME Junction Node Fixer has failed to load due to API check: " + matched);
    WME_JNF_FixNode = undefined;
    return;
  }
}

$(document).ready(WME_JNF_Bootstrap);
I would like to verify this works for me on Google Chrome with GM.