What happened to the Messenger Plus! forums on msghelp.net?
Shoutbox » MsgHelp Archive » Messenger Plus! for Live Messenger » Scripting » [IDEA] plusQuery

Pages: (2): « First « 1 [ 2 ] Last »
[IDEA] plusQuery
Author: Message:
Matti
Elite Member
*****

Avatar
Script Developer and Helper

Posts: 1646
Reputation: 39
32 / Male / Flag
Joined: Apr 2004
RE: [IDEA] plusQuery
The solution I came up with also uses eval() as there's no global object in JScript which stores the global variables. (In browser JavaScript, there's the global object "window" which holds the global scope.)
However, you'll have to do one extra thing to make it work...

When adding globals using eval() in run-time, the scripting engine will not register those added functions as event handlers. That is, if you add an OnWndIdEvent_Destroyed() event using eval(), it won't get called when the window is destroyed. The trick to get these registered is to use MsgPlus.LoadScriptFile(). When calling LoadScriptFile, the script file is loaded and the global scope is rescanned for event handlers. This results in the newly added functions to be properly registered. :)

Note that you don't need to trick eval() to be called in the global scope. By simply omitting the "var" keyword, the variable will be created in the global scope. It is generally a bad idea to omit the "var" keyword, but in this case it's sort of acceptable. :P

Proof of concept:
js code:
var plusWnd = $(MsgPlus).CreateWnd("interface.xml", "yiew", 0);
//[...]
$.wrappers.MsgPlus.CreateWnd = function (XmlFile, WindowId, Options) {
    var toEval = "On" + WindowId + "Event_Cancel = function (plusWnd) { $.trigger(plusWnd, '" + WindowId + "Event_Cancel', plusQuery.$A(arguments)); };";
    toEval += "On" + WindowId + "Event_Destroyed = function (plusWnd, exitCode) { $.trigger(plusWnd, '" + WindowId + "Event_Destroyed', plusQuery.$A(arguments)); };";
    //etc
    eval(toEval);
    $(MsgPlus).LoadScriptFile("_blank.js"); // black magic happens here

    this.original.CreateWnd(XmlFile, WindowId, Options);
}
The loaded script file can contain anything. From my tests, it may not be completely empty though - one character will work though. I think the best options to fill this file with are a single space " " or a semicolon ";". You can even save it as ANSI and save a few bytes, Plus! will still parse it (although Plus! recommends Unicode).

I warned you, it's very hackish and not really recommended (eval is evil but so is LoadScriptFile) but unfortunately it's the only way I could get this to work. Feel free to use it if you think it's "good enough" but think about the consequences. If you decide to use this, don't forget to keep track of the created event handlers so you don't need to redeclare them when creating the same window twice. :P

This post was edited on 04-06-2011 at 03:05 PM by Matti.
Plus! Script Developer | Plus! Beta Tester | Creator of Countdown Live | Co-developer of Screenshot Sender 5

Found my post useful? Rate me!
04-06-2011 03:03 PM
Profile E-Mail PM Web Find Quote Report
Amec
Junior Member
**


Posts: 19
32 / Male / Flag
Joined: Sep 2008
O.P. RE: RE: [IDEA] plusQuery
quote:
Originally posted by Matti
there's no global object in JScript
This is REALLY annoying... Surely they could switch to V8 or something? But then they'd have to write their own stuff for accessing ActiveX... Hmm.

quote:
Originally posted by Matti
Note that you don't need to trick eval() to be called in the global scope. By simply omitting the "var" keyword, the variable will be created in the global scope. It is generally a bad idea to omit the "var" keyword, but in this case it's sort of acceptable. :P
It's not really "tricking eval"... It's fine, and more clear than just declaring the variables without var. And what happens if there's a variable with the same name in the local scope? ;) (I know that'll almost never happen, but still)

Now that I think about it... Wouldn't there be a message to MessengerPlusLive_MsgPump we could send to have the script reevaluated? Not 0x81CE, (restarts all scripts) but something similar?
04-06-2011 04:11 PM
Profile E-Mail PM Find Quote Report
matty
Scripting Guru
*****


Posts: 8336
Reputation: 109
39 / Male / Flag
Joined: Dec 2002
Status: Away
RE: [IDEA] plusQuery
One thing I noticed

js code:
    $.addEventListener("ChatWndReceiveMessage", function (chatWnd, origin, message, msgKind) {
        if (msgKind === 1)
        {
            $.triggerCommand($(chatWnd), $(chatWnd.Contacts).GetContact(origin), message.split(' ')[0].toUpperCase(), message.split(' ').slice(1).join(' '), message, true);
        }
    });
You cannot obtain the Contact object by passing the origin parameter to the GetContact function.

This post was edited on 04-06-2011 at 05:27 PM by matty.
04-06-2011 05:19 PM
Profile E-Mail PM Find Quote Report
Eljay
Elite Member
*****

Avatar
:O

Posts: 2949
Reputation: 77
– / Male / –
Joined: May 2004
RE: [IDEA] plusQuery
JScript does have a global scope. In regular JScript (WSH) you can just do "var global = this" in global scope then access it anywhere.

It definitely worked at some point, but use of it was removed/broken by Plus! in an update to the scripting engine.
I'm sure there was a reason but I sure as hell can't remember it :P Maybe something to do with the event handling.

I know it worked because I made a script that allowed you to hook events or something like that, and my script broke :(

Edit: found it :P [Beta-ish Utility Release] Hook System
So it was broken at some point between 2006 and now, lol.

This post was edited on 04-06-2011 at 06:12 PM by Eljay.
04-06-2011 06:03 PM
Profile PM Find Quote Report
Matti
Elite Member
*****

Avatar
Script Developer and Helper

Posts: 1646
Reputation: 39
32 / Male / Flag
Joined: Apr 2004
RE: [IDEA] plusQuery
quote:
Originally posted by matty
One thing I noticed

You cannot obtain the Contact object by passing the origin parameter to the GetContact function.
He extended the GetContact implementation so that you can search by name as well.
js code:
$.wrappers.Contacts.prototype.extend({
    GetContact: function (str) {
        if (str.toLowerCase() === Messenger.MyEmail.toLowerCase()) {
            return this[0];
        } else if (this.original.GetContact(str)) {
            return $(this.original.GetContact(str));
        } else {
            for (var i = 0; i < this.length; i++) {
                if (this[i].Name === str) {
                    return this[i]; // Only returns the first match
                }
            }
        }

        return null;
    }
});
Still, it's not a good idea to do this. The value of origin may not resolve to the right contact, for example when a contact has a nickname assigned. Contact.Name always returns the name as published by the contact, whereas origin may contain the nickname of that contact. I think it's better to simply use origin - if the developer wants to resolve this to a contact, he can just implement this in the callback or override the event himself.



Also, the local command parsing should be a bit more sophisticated. For example, script commands should be escapable by adding an extra leading slash, such as "//mycommand". Also, parameters can be separated by many sorts of whitespace characters such as \n, \r,... Therefore, you'll need a regular expression to properly detect whether a given message is a script command and correctly separate the command from the parameter. Luckily, Cookie has written an excellent regular expression for this: CookieRevised's reply to Gettin data from "/" commands.

The way I parse local commands is based upon Cookie's regular expression but instead of using the global RegExp object to retrieve the data, I use the result from exec() itself. It's better to use your local data rather than having to rely on global side effects. ;)
js code:
var match = /^\/([^ \n\r\v\xA0\/][^ \n\r\v\xA0]*)(?:[ \n\r\v\xA0]([\s\S]*))?$/.exec(message);
if ( match ) {
    var command = match[1]; // note: command will have no leading slash
    var parameters = match[2];

    var tmp = $.triggerCommand($(chatWnd), $.MyContact, command, parameters, message, false);
    return (tmp === undefined ? message : tmp);
}
This principle can't be easily ported to external commands though, since you don't know what the leading character will be (! or @ or ...). However, you won't have to deal with escaped commands either. I'd recommend to simply look for the command until the first encountered whitespace character and use the remaining part as parameters.
js code:
var match = /^([^ \n\r\v\xA0]*)[ \n\r\v\xA0]?([\s\S]*)$/.exec(sMessage);
if ( match ) {
    var command = m[1]; // note: command will retain its leading character
    var parameters = m[2];

    $.triggerCommand($(chatWnd), $.MyContact, command, parameters, message, true);
}

quote:
Originally posted by Eljay
JScript does have a global scope. In regular JScript (WSH) you can just do "var global = this" in global scope then access it anywhere.
I know, I tried that but it's inaccessible. You can't read global variables from it or add variables to it. Heck, you can't even iterate over its contents. Very sad indeed. :(

Long post is long indeed.
Plus! Script Developer | Plus! Beta Tester | Creator of Countdown Live | Co-developer of Screenshot Sender 5

Found my post useful? Rate me!
04-06-2011 06:11 PM
Profile E-Mail PM Web Find Quote Report
Eljay
Elite Member
*****

Avatar
:O

Posts: 2949
Reputation: 77
– / Male / –
Joined: May 2004
RE: [IDEA] plusQuery
Ok I found when/why global scope was broken: Patchou's reply to 300 breaks NP script

It seems that when Patchou added the global enums (STATUS_BUSY, etc.), it broke the global scope :(
04-06-2011 06:20 PM
Profile PM Find Quote Report
CookieRevised
Elite Member
*****

Avatar

Posts: 15517
Reputation: 173
– / Male / Flag
Joined: Jul 2003
Status: Away
RE: [IDEA] plusQuery
Offtopic sidenote:
quote:
Originally posted by Matti
The way I parse local commands is based upon Cookie's regular expression but instead of using the global RegExp object to retrieve the data, I use the result from exec() itself. It's better to use your local data rather than having to rely on global side effects. ;)
It's not 'better' though (it isn't worse either for that matter). There is nothing wrong with using the global RegExp object as long as both functions (exec and the resulting global RegExp object) are used right after eachother. But if they are not used right after eachother, then yes, you have a very good point. But also, it is also not a 'side effect' though; the $1 identifiers are meant to be used like that.

But, this is just semantics I guess and what you prefer....


On topic, nice to see some advanced 'toying' (if I may use that word) with the scripting engine to create such a library and create such possebilities. But although I'm sure one could come up with an exotic use or examples for it, I must say, I can't see any real use of it in practice, certainly not for the average script and scripter, but also not for the more advanced scripts. But this said, again, great work (y).

.-= A 'frrrrrrrituurrr' for Wacky =-.
04-06-2011 10:27 PM
Profile PM Find Quote Report
Amec
Junior Member
**


Posts: 19
32 / Male / Flag
Joined: Sep 2008
O.P. RE: [IDEA] plusQuery
quote:
Originally posted by Matti
Another thing I noticed is that you're extending Object.prototype.
Fix'd. Moved all of those functions to plusQuery.

quote:
Originally posted by Matti
Here's a suggestion: use combined getter/setter methods for the properties in the wrapper classes.
Done.

quote:
Originally posted by Matti
Also, the local command parsing should be a bit more sophisticated.
Aaaand done!

What I haven't done:
  • Implemented dynamic function binding for PlusWnd events. Not sure if I'm going to do this, yet... I'll have to test a lot before I decide.
  • Changed GetContact so you can't search by name. Haven't decided what I'm gonna do with this one... It's so simple with it there, but I'm aware of the pitfalls... Perhaps... ChatWnd.GetContactByName and ChatWnd.GetContactByEmail? Dunno.

quote:
Originally posted by Eljay
It seems that when Patchou added the global enums (STATUS_BUSY, etc.), it broke the global scope :(
Is he planning on fixing it? -_-

quote:
Originally posted by CookieRevised
On topic, nice to see some advanced 'toying' (if I may use that word) with the scripting engine to create such a library and create such possebilities. But although I'm sure one could come up with an exotic use or examples for it, I must say, I can't see any real use of it in practice, certainly not for the average script and scripter, but also not for the more advanced scripts. But this said, again, great work (y).
I find it useful for writing my own small scripts. Here's a recent example: I wanted to add two commands, "/afk" and "/back", which set my name to Messenger.MyName + "\xA0" + status, and back again. Pretty simple, ey? So instead of creating a whole new script for it, I just created a new .js in my "Misc" script which has _plusquery.js, and added it using $.addCommand. Much easier/faster/better(?) than creating a whole new script and doing all the parsing of commands, etc. again.
04-07-2011 05:43 AM
Profile E-Mail PM Find Quote Report
CookieRevised
Elite Member
*****

Avatar

Posts: 15517
Reputation: 173
– / Male / Flag
Joined: Jul 2003
Status: Away
RE: [IDEA] plusQuery
Sure, but as for that specific example, that could be done in much easier ways too though I think (as in using a more straightforward library). So, maybe not the best example. But as long as you (and others) find it useful, then (y)
.-= A 'frrrrrrrituurrr' for Wacky =-.
04-07-2011 09:36 AM
Profile PM Find Quote Report
Pages: (2): « First « 1 [ 2 ] Last »
« Next Oldest Return to Top Next Newest »


Threaded Mode | Linear Mode
View a Printable Version
Send this Thread to a Friend
Subscribe | Add to Favorites
Rate This Thread:

Forum Jump:

Forum Rules:
You cannot post new threads
You cannot post replies
You cannot post attachments
You can edit your posts
HTML is Off
myCode is On
Smilies are On
[img] Code is On