Sharing WME Code changes

Perhaps the heavy-hitter devs already have something like this behind the scenes, but I was thinking that an info-sharing spot could be useful, for when WME updates and breaks things. It would just list the most recent script-breaking changes as they are found so that other devs could more easily update their scripts.

Just as a demonstration example, this change from a couple of months ago:

Waze/Presenter/FeatureEditor/Segment changed to

Waze/Modules/FeatureEditor/Segment

Just a thought…

+1

city class definition changed:
before

Waze.model.cities.objects[<acityid>].*

now:

Waze.model.cities.objects[<acityid>].attributes.*

Maybe countries and streets will follow in the future :roll:

Due to the changes to the city object, the getByAttributes method (W.model.cities.getByAttributes()) no longer works correctly - you must write your own routine to iterate through the cities to find the one you need.

How do you guys know all of the code, API of WME? I am thinking about making a script… I searched and found nothing… Thank you!

For latecomers to script-writing like myself, the biggest single resource is: other scripts. :smiley:

Pretty much all of the script writers publish their scripts completely in the open and with no other licensing requirement than that you say thank you to us if you use our work in your own script. A couple of the most powerful scripts (Toolbox and Validator come to mind) do make efforts to restrict the availability and re-use of their code - but’s that because of the potential for that code to be used for cheating by idiots who care for nothing but being on top of the scoreboard.

The other important tool is the document inspector that’s part of your browser. If you right-click on a web page, you will probably have something like an “inspect” option. This opens another window that allows you to dig into the page code, the CSS, the document elements etc. It can be quite a daunting process at the start, but it’s an invaluable resource for investigating the page and the data model behind it.

So the best place to start is to have a look at other scripts that do similar things to what you want to accomplish. WMEFU, for example, started life when Waze moved the zoom bar to the right. First I used the inspector to examine the CSS that controlled the zoom bar position and to work out how I could alter that. Next, I looked at WMECH to see how to create a working script with it’s own tab. I then stripped all the functionality from a copy of WMECH and added in my own - eventually having a working script. I then moved on to the WME Maximised script, which did some things I liked but had stopped working.

It’s also worth looking in the Wiki. There is some information there about creating the basic beginnings of a script. That will help you produce a framework from which you can experiment.

And, of course, you’ll always find others who are willing to help. A Hangout/Slack chat with someone who knows what they’re doing (and I wouldn’t say that includes me :lol: ) can be invaluable. :mrgreen:

Lastly: please see xkcd #844

What Iain said… We all had to start somewhere, with no official documentation provided by Waze - everything we’ve learned about how WME ticks behind the scenes, whether it’s something as simple as figuring out how it renders a certain bit of the editor UI, right through to how it interacts with the backend servers to load and save data, has been learned through hours of poring over the debug console, looking at what server calls are made or received when a certain action occurs, tracing through the somewhat less than readable WME code to find references to certain objects, or just by the age old method of trial and error. The debug console, and particularly its ability to run javascript directly in the current webpage simply by typing it into the console and seeing what happens, will become your best friend and worst enemy…

Just when you think you’ve understood what’s going on, WME will throw you a curve ball and remind you that it ain’t all that easy to get to grips with, but if you continue to persevere and crack the problem at hand, you’ll be rewarded with a feeling of immense satisfaction. And, if the reason for writing a script is to help other editors as well as yourself, you’ll also start to get that warm glow you get knowing your work is making life a little bit easier for others too.

One other thing I’ll add for future reference (i.e. once you’ve found yourself well and truly hooked on script writing) is that, if possible, you shouldn’t restrict yourself to using just one browser for development. As a Windows user, my scripting environment consists of Firefox+Greasemonkey+Firebug, and Chrome. I do pretty much all of my primary development work in Firefox, partly because I prefer the way the Firebug debug console behaves when poking around in the page source code or DOM tree and also when running bits of code directly from the console, and partly because, once I’ve installed the first version of a script, I can then edit its source code directly out of the Firefox profile folder and bypass the usual script installation process each time I want to try out the latest changes I’ve made. It might not sound like a big deal, but if you’re spending a few hours working on a script and going through a period of short change/test cycles, the few seconds you save each time by not having to “officially” install the new version each time can quickly add up.

However, whilst I prefer the Firefox environment for editing, delving into the innards of WME, and some testing, I much prefer Chrome for general testing - its debug console provides better runtime error messages, showing things that Firebug is quite happy to ignore but which really need to be fixed in order to make your script run well, or indeed at all…

But to get started, just take things nice and easy. Look at some of the smaller scripts that are already out there so you get a feel for how things behave, spend some time playing around in the console - remember that if it all goes horribly wrong, a simple browser refresh/reload will get you back to where you started - and don’t be afraid to ask for help.

W.geometryEditing.editors.venue appears to have been renamed to W.geometryEditing.activeEditor

It looks like you should test the existence of W.geometryEditing.activeEditor before trying to use or modify it (I’m not sure in what scenarios it is NULL).

Credit to JustinS83 here: https://www.waze.com/forum/viewtopic.php?f=819&t=133650&p=1510528#p1510528

Several changes.

W.geometryEditing.editors was changed to activeEditor.

Further, the W.geometryEditing.activeEditor object now only exists when a Place is selected, which necessitates checking if it is null before attempting to use.

This now also does not exist but I haven’t found the new reference yet.

W.geometryEditing.activeEditor.venue.dragVertex

With the substantial object changes that were just pushed into production WME, I would like to encourage all script authors to share their findings here for the good of all :slight_smile:

Anyone have an idea why the ‘require’ function doesn’t work anymore? Seams to be the culprit in more scripts (including some of mine) :frowning:

Hi,

since Waze devs confirm that the new way of making edit was not intended to kill script, here is a piece of code to get the “require” stuff working again:

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


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



// setup a fake require and require.define
WMEAPI.require=function (e) {
    if (WMEAPI.require.define.modules.hasOwnProperty(e))
        return WMEAPI.require.define.modules[e];
    else
        console.error('Require failed on ' + e, WMEAPI.require.define.modules);
    return null;
};

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

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

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

// e is a copy of all WME funcs because function t need to access to this list
WMEAPI.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], WMEAPI.r[d] && l.push.apply(l, WMEAPI.r[d]), WMEAPI.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)
    {
        WMEAPI.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: WMEAPI.e[o]};
        }
        else
        {
            api['Waze/Unknown/' + unknownCount]={index: o, func: WMEAPI.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, WMEAPI.t);
    WMEAPI.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 = WMEAPI.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]=WMEAPI.t(api[apiFuncName].index);
            WMEAPI.require.define(module);
        }
        else
        {
            var matches = funcStr.match(/SEGMENT:"segment",/);
            if (matches)
            {
                module={};
                apiFuncName='Waze/Model/ObjectType';
                module[apiFuncName]=WMEAPI.t(api['Waze/Unknown/' + unknownCount].index);
                WMEAPI.require.define(module);
            }
            unknownCount++;
        }
    }
     

    // restore the original func
    window.webpackJsonp=WMEAPI.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=WMEAPI.require;
    // all available functions are in WMEAPI.require.define.modules
    // console.debug this variable to read it:
    // console.debug('Modules: ', WMEAPI.require.define.modules);
    
    // run your script here:
    // setTimeout(yourscript);
    
    // again taken from WME code. Not sure about what it does.
    //if (i[0]) return ret;
};

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

// hacking finished

// load again WME through our patched func
WMEAPI.WMEHACK_Injected_script = document.createElement("script");
WMEAPI.WMEHACK_Injected_script.setAttribute("type", "application/javascript");
WMEAPI.WMEHACK_Injected_script.src = WMEAPI.url;
document.body.appendChild(WMEAPI.WMEHACK_Injected_script);

What it does:

  • Detects the WME code URL
  • Creates a require command
  • Creates a patch on “webpackJsonp”. This patch will detect classes previously available in “require” and add it in our custom “require” command.
  • Loads the WME code, but in our patched function

Then, you can do a “require(…)” like before.

Note that some functions are not available in the new WME. As stated in the code, you can log modules to see what classes are available.

Also stated in the code, you can make the require command global. But other scripts can call the “require” command before our patch, so it will not work.
So, I recommend to use this patch in all script that need “require”, and not making your “require” global.

Thanks! My scripts are now working again :smiley:

Now we have to just wait for the 2 big ones…

Tried it in one of my scripts and it is working great!

Thank you d2!

Spoke too soon… the new UpdateObject() functionality is also changed…

Why oh why :roll:

Hi, How do i use the patch code?? Some manual for me pls.

If ever there was a time when we really need to be able to give a post multiple thumbs-up, this surely qualifies…

I’d just found a workaround for the require() I was using to delete unwanted cameras, but was struggling to find a similar one for adding new UR markers. And despite your code looking almost as terrifying as some of the stuff the devteam generate, getting it up and running in URO+ took all of about 5 minutes. 4m30s of which was just reformatting the code into the same style as the rest of the script :smiley:

Still got a lot of other work to do to workaround the other changes the devteam have introduced, but you’ve provided a massive step forwards here, so many many thanks indeed.

Regards,
Chris

It would be nice if that code and any other necessary to support it could be written into a new ‘script’.
That script could be called something like “WME global fix patches” and it would make it unnecessary to patch each script. Is there a way to force such a script to load first?

Well, I can wish, can’t I?

Stay tuned…

Tampermonkey can change the order of loading, IIRC…

Seeing how I am script dumb. How do I put it in my scripts to see if it would work