WME Reverse Nodes

Serves to reverse the nodes A and B of a segment, in order to better manage restrictions on multiples segments.

当前为 2016-11-21 提交的版本,查看 最新版本

// ==UserScript==
// @name             WME Reverse Nodes
// @name:fr          WME Reverse Nodes
// @description      Serves to reverse the nodes A and B of a segment, in order to better manage restrictions on multiples segments.
// @description:fr   Sert à inverser les nodes A et B d'un segment, dans le but de mieux gérer les restrictions sur segments multiples.
// @include          https://www.waze.com/editor/*
// @include          https://www.waze.com/*/editor/*
// @include	         https://beta.waze.com/editor/*
// @include 	       https://beta.waze.com/*/editor/*
// @exclude          https://www.waze.com/user/*
// @exclude          https://www.waze.com/*/user/*
// @namespace        WME_Reverse_Nodes
// @version          0.9
// @grant            none
// ==/UserScript==





function WRNAB_Injected(){

  var WRNAB_version = "0.9";
  var debug = false;
  var WRNAB_API = {};
  WRNAB_API.require = {};
  
  /* bootstrap */
  function WRNAB_bootstrap(){
	  if (typeof(unsafeWindow) === "undefined"){
		  unsafeWindow = ( function () {
			  var dummyElem = document.createElement('p');
			  dummyElem.setAttribute('onclick', 'return window;');
			  return dummyElem.onclick();
		  }) ();
	  }
    /* begin running the code! */
	  log("Start");
	  setTimeout(initialize, 1000);
    
  }

  //==========  Helper ==============================//



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

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

  function log(msg, obj)
  {
    if (obj==null)
       console.log("WME Reverse Nodes AB v" + WRNAB_version + " - " + msg);
    else if(debug)
       console.debug("WME Reverse Nodes AB v" + WRNAB_version + " - " + msg + " " ,obj);
    
  }
      



  //==========  /Helper ==============================//

  function initialize()
  {
      initializeWazeObjects();   
  }

  function waitForObject(object)
  {
    var obj=null;
    if (object.r==true)
    {
        if (debug) log ("eval: " + (object.s!=null?object.s:'dummy') + "="+ ((typeof(window.require)=="function")?"require":"WRNAB_API.require") +"(\"" + object.o + "\")");
        eval ((object.s!=null?object.s:'dummy') + '=' + ((typeof(window.require)=="function")?'require':'WRNAB_API.require') +'("' + object.o + '")');  
        eval ("obj=" + (object.s!=null?object.s:'dummy'));
    		if (debug) log("obj", obj);
    }else{
    
        if (debug) log ("eval: " + "typeof(unsafeWindow." + object.o.replace(/\//g, '.') + ")");
        obj=eval("typeof(unsafeWindow." + object.o.replace(/\//g, '.') + ")");
    }
    if(obj === "undefined")
    {
        log(object.o + ' KO');
        window.setTimeout(waitForObject.caller, 500);
        return false;
    }
    if (debug) log(object.s + ' OK');


    if (object.s!=null && object.r==false)
        eval (object.s + "=" + object.o.replace(/\//g, '.'));

    return true;


  }


  function initializeWazeObjects()
  {
    var objectToCheck = [
    	{o: "Waze",																s: "waze",														r: false},
	    {o: "Waze.vent",													s: "wazeVent",												r: false},
	    {o: "Waze.controller",										s: "wazeController",									r: false},
	    {o: "Waze.model",													s: "wazeModel",												r: false},
	    {o: "Waze.map",														s: "wazeMap",													r: false},
	    {o: "Waze.loginManager",									s: "WazeLoginManager",										r: false},
	    {o: "Waze.selectionManager",							s: "WazeSelectionManager",								r: false},
	    {o: "Waze/Action/UpdateObject",						s: "WazeActionUpdateObject",					r: true},
	    {o: "Waze/Action/UpdateSegmentGeometry",  s: "WazeUpdateSegmentGeometry",       r: true},
		  {o: "Waze/Action/ConnectSegment",			    s: "WazeActionConnectSegment",        r: true},
	    {o: "Waze/Action/ModifyConnection",		    s: "WazeActionModifyConnection",	    r: true},
	    //{o: "Waze/Action/ModifyAllConnections",		s: "WazeActionModifyAllConnections",	r: true}, 
	    {o: "Waze.loginManager.user",             s: "me",															r: false},
      {o: "me.rank",									          s: "ul",															r: false},
      {o: "me.isAreaManager",					          s: "uam",															r: false},
	    {o: "localStorage",												s: null,															r: false}
	    ];

	  for (var i=0; i<objectToCheck.length; i++)
	  {
			  if (!waitForObject(objectToCheck[i])) return;    
	  }
    
    
    initializeWazeUI();

  }

  function initializeWazeUI()
  {


      var userInfo = getId('user-info');
      if (userInfo==null)
      {
          window.setTimeout(initializeWazeUI, 500);
          return;
      }

      var navTabs=userInfo.getElementsByTagName('ul');
      if (navTabs.length==0)
      {
          window.setTimeout(initializeWazeUI, 500);
          return;
      }
      if (typeof(navTabs[0])==='undefined')
      {
          window.setTimeout(initializeWazeUI, 500);
          return;
      }
      
      var tabContents=userInfo.getElementsByTagName('div');
      if (tabContents.length==0)
      {
          window.setTimeout(initializeWazeUI, 500);
          return;
      }
      if (typeof(tabContents[0])==='undefined')
      {
          window.setTimeout(initializeWazeUI, 500);
          return;
      }
    
      WRNAB_Init();
  }



  function WRNAB_newSelectionAvailable()
  {
      log('WazeSelectionManager',WazeSelectionManager);
      if (WazeSelectionManager.selectedItems.length!=1)
          return;
      var selectedObject = WazeSelectionManager.selectedItems[0].model;
      if (selectedObject.type!="segment")
          return;
      log('uam', uam);
		  if (!uam) return;
      var attributes = selectedObject.attributes;
		  log('attributes', attributes);
		
		  if (attributes.fwdRestrictions !== undefined && attributes.fwdRestrictions.length > 0){
		    var fwdRestrictions = attributes.fwdRestrictions;
		  }
		  if (attributes.revRestrictions !== undefined && attributes.revRestrictions.length > 0){
		    var revRestrictions = attributes.revRestrictions;
		  }
		
      var editPanel=getId('edit-panel');
      if (editPanel.firstElementChild.style.display=='none')
          window.setTimeout(WRNAB_newSelectionAvailable, 100);
      
      // ok: 1 selected item and pannel is shown
      
      
      
      if (selectedObject.type=="segment")
      {
          item=getId("segment-edit-general");
          
          var WRNAB_Controle=document.createElement('Div');
          WRNAB_Controle.id="WRNAB-Controle";

		      WRNAB_Controle.innerHTML='<input type="button" id="_btnInvertAB" value="Reverse Nodes A/B">';
				
		      item.appendChild(WRNAB_Controle);
          
          getId("_btnInvertAB").onclick=invertNodeAB;
          log("wazeModel.actionManager",wazeModel.actionManager);
      }
      
  }

  function WRNAB_Init()
  {
		
      WazeSelectionManager.events.register("selectionchanged", null, WRNAB_newSelectionAvailable);

      log('init done.');

  }

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

  function isSegmentEditable(seg)
  {
      var rep = true;
      if (seg==null) return false;
      //if (Waze.loginManager.user.isCountryManager() ) rep = true;
      if (!uam) rep = false;
      var ndA = wazeModel.nodes.get(seg.attributes.fromNodeID);
      var ndB = wazeModel.nodes.get(seg.attributes.toNodeID);
      
      if ((seg.attributes.permissions == 0) || (seg.attributes.hasClosures))
        rep = false;
      
      if ((ndA.attributes.permissions == 0) || (seg.attributes.hasClosures))
        rep = false;
      
      if ((ndB.attributes.permissions == 0) || (seg.attributes.hasClosures))
        rep = false;

      return rep;
  }

  function invertNodeAB()
  {
      if (WazeSelectionManager.selectedItems.length!=1)
          return;
      var seg =  wazeModel.segments.get(WazeSelectionManager.selectedItems[0].model.attributes.id);
      if (seg.type!="segment")
          return;
      
      var attr = seg.attributes;
      log("seg",seg);
		  log("attr",attr);
		
		
		  var sid = attr.id;
		
      // si non nomme, on touche pas:
      if (attr.primaryStreetID==null)
      {
          log("Unnamed segment. Do not put my name on it!");
          return;
      }
      
      // si locke: on edite pas
      /*if ((attr.lockRank==null && attr.rank>usrRank) ||
          (attr.lockRank!=null && attr.lockRank>usrRank)) 
      {
          log("locked: " + attr.rank + " " + attr.lockRank);
          continue;
      }
      */
      
      //var ndA = wazeModel.nodes.objects[attr.fromNodeID];
      //var ndB = wazeModel.nodes.objects[attr.toNodeID];
      var ndA = wazeModel.nodes.get(attr.fromNodeID);
      var ndB = wazeModel.nodes.get(attr.toNodeID);
      log("ndA", ndA);
      log("ndB", ndB);
      // verification de pbs sur nodes (unterminated roads par ex)
      if (!ndA || !ndB) 
      {
          log("Bad nodes: A=" + ndA + " B=" + ndB);
          return;
      }        


      // On verifie que le segment est affiche a l'ecran
      if (onScreen(ndA) && onScreen(ndB))
      {
          log("IN Screen!");
      }
      else
      {
          log("OUT of Screen");
          return;
      }
      
      // On verifie que le segment est éditable
      if (!isSegmentEditable(seg))
      {
          log("Not editable!");
          return;
      }
          
      // On mémorise les directions autorisées vers le segment 
      var sconA=[];
      var sconB=[];
      // pour les segments connecté à la node A
      for (var s=0; s<ndA.attributes.segIDs.length; s++)
      {
        var scon=ndA.attributes.segIDs[s];
        if (wazeModel.segments.objects[scon].attributes.fromConnections.hasOwnProperty(attr.id))
          sconA.push(scon);
        if (wazeModel.segments.objects[scon].attributes.toConnections.hasOwnProperty(attr.id))
          sconA.push(scon);
      }
      // pour les segments connecté à la node B    
      for (var s=0; s<ndB.attributes.segIDs.length; s++)
      {
        var scon=ndB.attributes.segIDs[s];
        if (wazeModel.segments.objects[scon].attributes.fromConnections.hasOwnProperty(attr.id))
          sconB.push(scon);
          
        if (wazeModel.segments.objects[scon].attributes.toConnections.hasOwnProperty(attr.id))
          sconB.push(scon);
      }
      log("sconA",sconA);
      log("sconB",sconB);
      

		  // Mémorisation des paramètre du segment pour les reporter après changement de sens
		  var newAttr={};
		  var fromConnections = attr.toConnections;
      var toConnections = attr.fromConnections;
		  //var fromConnections = attr.toConnections.clone();
      //var toConnections = attr.fromConnections.clone();
		  newAttr.fwdDirection = attr.revDirection;
      newAttr.revDirection = attr.fwdDirection;
      newAttr.fwdTurnsLocked = attr.revTurnsLocked;
      newAttr.revTurnsLocked = attr.fwdTurnsLocked;
      newAttr.fwdMaxSpeed = attr.revMaxSpeed;
      newAttr.revMaxSpeed = attr.fwdMaxSpeed;
      newAttr.fwdMaxSpeedUnverified = attr.revMaxSpeedUnverified;
      newAttr.revMaxSpeedUnverified = attr.fwdMaxSpeedUnverified;
      newAttr.fwdRestrictions = attr.revRestrictions.clone();
      newAttr.revRestrictions = attr.fwdRestrictions.clone();
      
      
		  log("seg",seg);
		  log("attr",attr);
		  log("newAttr", newAttr);
		  log("fromConnections", fromConnections);
		  log("toConnections", toConnections);
		
		  //on inverse la géométrie du segment
		  var geo = seg.geometry.clone();
		  geo.components.reverse();
		
      // controle de position
		  var nbPoints = geo.components.length-1;
		  if (!geo.components[0].equals(ndB.attributes.geometry)){
		    if (debug) log("point 0 et dif de node A");
		    var delta = {x:0, y:0};
		    delta.x =ndB.attributes.geometry.x - geo.components[0].x;
		    delta.y = ndB.attributes.geometry.y - geo.components[0].y;
		    geo.components[0].move(delta.x, delta.y);
		  } 
		  if (!geo.components[nbPoints].equals(ndA.attributes.geometry)){
		    if (debug) log("point "+nbPoints+ " est dif de node B");
		    var delta = {x:0, y:0};
		    delta.x = ndA.attributes.geometry.x - geo.components[nbPoints].x;
		    delta.y = ndA.attributes.geometry.y - geo.components[nbPoints].y;
		    geo.components[nbPoints].move(delta.x, delta.y);
		    
		  }
		
		  // maj de la géo du seg
		  wazeModel.actionManager.add(new WazeUpdateSegmentGeometry (seg,seg.geometry,geo));
		

		  // On reconecte le segment
		  wazeModel.actionManager.add(new WazeActionConnectSegment(ndB, seg));
		  wazeModel.actionManager.add(new WazeActionConnectSegment(ndA, seg));
		
		  //on replace les paramètre inversé
		  wazeModel.actionManager.add(new WazeActionUpdateObject(seg, newAttr));
		
		  //on replace les autorisations sortantes
		  for (var sid  in fromConnections)
      {
        if (debug) log("attr.id: " + attr.id + " --> NodeA --> sid: " + sid); 
        wazeModel.actionManager.add(new WazeActionModifyConnection(attr.id, ndB, sid, true));
      }
      for (var sid  in toConnections)
      {
        if (debug) log("attr.id: " + attr.id + " --> NodeB -->  sid: " + sid); 
        wazeModel.actionManager.add(new WazeActionModifyConnection(attr.id, ndA, sid, true));
      }
      
		  //on replace les autorisations entrantes
		  log("ndB", ndB);
    	for (var s=0; s<sconB.length; s++)
      { 
    	  if (debug) log("sconB["+s+"] :" + sconB[s] + " nodeA: " + ndB.attributes.id + " attr.id: " + attr.id);
    		wazeModel.actionManager.add(new WazeActionModifyConnection(sconB[s], ndB, attr.id, false));
     		wazeModel.actionManager.add(new WazeActionModifyConnection(sconB[s], ndB, attr.id, true));
     	}
		  log("ndA", ndA);
    	for (var s=0; s<sconA.length; s++)
      { 
        if (debug) log("sconA["+s+"] :" + sconA[s] + " nodeB: " + ndA.attributes.id + " attr.id: " + attr.id);
    	  wazeModel.actionManager.add(new WazeActionModifyConnection(sconA[s], ndA, attr.id, false));
			  wazeModel.actionManager.add(new WazeActionModifyConnection(sconA[s], ndA, attr.id, true));
		  }
		  log('Invert node for segment '+attr.id+': Ok');

  }
  

if (typeof(window.require) == "function"){
  log("bootstrap classique");
  WRNAB_bootstrap();
}else{
  ///////////////////////////////////////////////
  ///  Big thanks to dummyd2 for this Patch    //
  ///////////////////////////////////////////////
  log("load patch dummyd2");
  

  // setup one global var and put all in
  var WRNABAPI = {};


  // detect URL of WME source code
  WRNABAPI.scripts = document.getElementsByTagName('script');
  WRNABAPI.url=null;
  for (i=0;i<WRNABAPI.scripts.length;i++)
  {
      if (WRNABAPI.scripts[i].src.indexOf('/assets-editor/js/app')!=-1)
      {
          WRNABAPI.url=WRNABAPI.scripts[i].src;
          break;
      }
  }
  if (WRNABAPI.url==null)
  {
      throw new Error("WME Hack: can't detect WME main JS");
  }



  // setup a fake require and require.define
  WRNABAPI.require=function (e) {
      return WRNABAPI.require.define.modules[e];
  };

  WRNABAPI.require.define=function (m) {
      if (WRNABAPI.require.define.hasOwnProperty('modules')==false)
          WRNABAPI.require.define.modules={};
      for (var p in m)
      {
          WRNABAPI.require.define.modules[p]=m[p];
      }
  };


  // save the original webpackJsonp function
  WRNABAPI.tmp = window.webpackJsonp;

  // taken from WME code: this function is a wrapper that setup the API and may call recursively other functions
  WRNABAPI.t = function (n) {
      if (WRNABAPI.s[n]) return WRNABAPI.s[n].exports;
      var r = WRNABAPI.s[n] = {
          exports: {},
          id: n,
          loaded: !1
      };
      return WRNABAPI.e[n].call(r.exports, r, r.exports, WRNABAPI.t), r.loaded = !0, r.exports;
  };

  // e is a copy of all WME funcs because function t need to access to this list
  WRNABAPI.e=[];

  // the patch
  window.webpackJsonp = function(a, i) {
      // our API but we will use it only to build the require stuffs
      var api={};
      // taken from WME code. a is [1], so...
      for (var o, d, u = 0, l = []; u < a.length; u++) d = a[u], WRNABAPI.r[d] && l.push.apply(l, WRNABAPI.r[d]), WRNABAPI.r[d] = 0;
      
      var unknownCount=0;
      var classname, funcStr;
      
      // copy i in e and keep a link from classname to index in e
      for (o in i)
      {
          WRNABAPI.e[o] = i[o];
          funcStr = i[o].toString();
          classname = funcStr.match(/CLASS_NAME:\"([^\"]*)\"/);
          if (classname)
          {
              // keep the link.
              api[classname[1].replace(/\./g,'/').replace(/^W\//, 'Waze/')]={index: o, func: WRNABAPI.e[o]};
          }
          else
          {
              api['Waze/Unknown/' + unknownCount]={index: o, func: WRNABAPI.e[o]};
              unknownCount++;
          }
          
      }
      
      // taken from WME code: it calls the original webpackJsonp and do something else, but I don't really know what.
      // removed the call to the original webpackJsonp: still works...
      //for (tmp && tmp(a, i); l.length;) l.shift().call(null, t);
      for (; l.length;) l.shift().call(null, WRNABAPI.t);
      WRNABAPI.s[0] = 0;
      
      // run the first func of WME. This first func will call recusrsively all funcs needed to setup the API.
      // After this call, s will contain all instanciables classes.
      //var ret = WRNABAPI.t(0);
      
      // now, build the requires thanks to the link we've built in var api.
      var module={};
      var apiFuncName;
      unknownCount=0;
      
      for (o in i)
      {
          funcStr = i[o].toString();
          classname = funcStr.match(/CLASS_NAME:\"([^\"]*)\"/);
          if (classname)
          {
              module={};
              apiFuncName = classname[1].replace(/\./g,'/').replace(/^W\//, 'Waze/');
              module[apiFuncName]=WRNABAPI.t(api[apiFuncName].index);
              WRNABAPI.require.define(module);
          }
          else
          {
              var matches = funcStr.match(/SEGMENT:"segment",/);
              if (matches)
              {
                  module={};
                  apiFuncName='Waze/Model/ObjectType';
                  module[apiFuncName]=WRNABAPI.t(api['Waze/Unknown/' + unknownCount].index);
                  WRNABAPI.require.define(module);
              }
              unknownCount++;
          }
      }
       

      // restore the original func
      window.webpackJsonp=WRNABAPI.tmp;

      // set the require public if needed
      // if so: others scripts must wait for the window.require to be available before using it.
      //window.require=WRNABAPI.require;
      WRNAB_API.require=WRNABAPI.require;
      // all available functions are in WRNABAPI.require.define.modules
      // console.debug this variable to read it:
      //console.debug('WRNABAPI Modules: ', WRNABAPI.require.define.modules);
      
      // run your script here:
      setTimeout(WRNAB_bootstrap, 1000);
      
      // again taken from WME code. Not sure about what it does.
      //if (i[0]) return ret;
  };

  // some kind of global vars and init
  WRNABAPI.s = {};
  WRNABAPI.r = {
      0: 0
  };

  // hacking finished

  // load again WME through our patched func
  WRNABAPI.WMEHACK_Injected_script = document.createElement("script");
  WRNABAPI.WMEHACK_Injected_script.setAttribute("type", "application/javascript");
  WRNABAPI.WMEHACK_Injected_script.src = WRNABAPI.url;
  document.body.appendChild(WRNABAPI.WMEHACK_Injected_script);
}
}var WRNAB_Injected_script = document.createElement("script");
WRNAB_Injected_script.textContent = '' + WRNAB_Injected.toString() + ' \n' + 'WRNAB_Injected();';
WRNAB_Injected_script.setAttribute("type", "application/javascript");
document.body.appendChild(WRNAB_Injected_script);