Page 1 of 4

Internet Explorer Element Spy (alternative to iWB2 Learner)

Posted: 14 Jan 2016, 17:07
by Bruttosozialprodukt
So in my opinion the iWB2 Learner script didn't show enough information. So I thought to myself, why not improving it a bit. But I looked at the code and decided to completely rewrite the whole thing because I think it can be done with less than half the lines of code. At the moment I'm at 99 lines of code while the iWB2 leaner is a bit over 600 and the basics are already working.

Well the main reason for writing this script is that there are a lot of scripts that are blindly depending on the index of some element, which is really bad practice because as soon as the website changes a tiny little bit, the script breaks.
So let's say you have this element <div class="redBackground">test</div> and the class "redBackground" is used in 70 other elements.
Now you could use document.getElementsByClassName("redBackground")[71] which could easily breakwhen website gets another element using that class. Or you could look for nearby parents that have an id. Imagine there is another div around that first one:

Code: Select all

<div id="testContainer">
    <div class="redBackground">test</div>
</div>
Now you could easily go like this:
document.getElementById("testContainer").getElementsByClassName("redBackground")[0]
and the chance of this breaking is way lower.

So my plan is it to display suggestions like the code above to access an element.
In addition to that I want to display more properties and allow easier selection of elements (imagine you want to select an element that lies below a transparent overlay, I want to make that possible).

This is my code so far:
16 Jan 2016 - 1

Code: Select all

#Persistent
SetBatchLines -1
OnExit("Cleanup")
ComObjError(False)
index := 1

width := 400
height := 200
Gui, +AlwaysOnTop +Resize +hwndHWND
Gui, Add, Edit, vGUI_InfoBox w500 h200
Gui, Add, Text, vGUI_HotkeyNote w400 h20, [F1]: Freeze display --- [F2]/[F3]: select next/previous element under mouse
Gui, Show, % "x" A_ScreenWidth-530 " y" 0, IE Window/Element Spy
;WinSet, Transparent, 200, ahk_id %HWND%

ActiveIeMon := Func("ActiveIeMonitor")
SetTimer, % ActiveIeMon, 100
Return

F1::
    frozen:=!frozen
Return
F2::
    index++
    ;TrayTip, Changed element selecting index, Element #%index% under the mouse will be shown.
Return
F3::
    If (index > 1)
        index--
    ;TrayTip, Changed element selecting index, Element #%index% under the mouse will be shown.
Return
ActiveIeMonitor(cleanup:=False) {
    Static lastIE, lastWindow, lastIndex
    Global index
    If (cleanup) {
        If (lastWindow.___id___) {
            EnableMouseMoveListener(lastIE,lastWindow,False)
        }
        Return
    }
    IE := IeGet()
    If (ComObjType(IE,"IID")) {
        window := ComObj(9,ComObjQuery(lastIE,"{332C4427-26CB-11D0-B483-00C04FD90119}","{332C4427-26CB-11D0-B483-00C04FD90119}"),1)
        If (!window.___id___ || window.___id___ != lastWindow.___id___) {
            If (!window.___id___)
                EnableMouseMoveListener(IE,window,True)
            If (lastWindow.___id___)
                EnableMouseMoveListener(lastIE,lastWindow,False)
        }
        If (index != lastIndex)
            OnMouseMove(IE, window, "")
    }
    ;msgbox % IE.document.uniqueID
    lastIE := IE
    lastWindow := window
    lastIndex := index
}
EnableMouseMoveListener(IE,window,enable:=True) {
    Static LastOnMouseMoveBound
    If (enable) {
        window.___id___ := A_TickCount
        window.eval("function ___injected_eventListenerHelperFuntion___(listener) { return function(e) { return listener(e); }; }")
        OnMouseMoveBound := Func("OnMouseMove").Bind(IE,window)
        window.addEventListener("mousemove", window.___injected_eventListenerHelperFuntion___(OnMouseMoveBound))
    } Else {
        window.___id___ := 0                            ;TODO: check which of these are actually necessary
        lastWindow.eval("___id___ = undefined;")        ;TODO: check which of these are actually necessary
        lastWindow.eval("window.___id___ = undefined;") ;TODO: check which of these are actually necessary
        window.eval("function ___injected_eventListenerHelperFuntion___(listener) { return function(e) { return listener(e); }; }")
        window.removeEventListener("mousemove", window.___injected_eventListenerHelperFuntion___(LastOnMouseMoveBound))
        window.eval("___injected_eventListenerHelperFuntion___ = undefined;")
        OnMouseMove("","","",True)
    }
    LastOnMouseMoveBound := OnMouseMoveBound
}
OnMouseMove(IE, window, e:="", cleanup:=False) {
    Static lastElement, lastElementOutline, lastE := {}
    Global frozen, index
    If (cleanup) {
        If (lastElement)
            lastElement.style.outline := lastElementOutline
        Return
    }
    If frozen
        Return
    If (!e)
        e := lastE
    
    If (e.pageX != lastE.pageX || e.pageY != lastE.pageY)
        index = 1
    
    document := IE.document
    x := e.pageX-window.pageXOffset
    y := e.pageY-window.pageYOffset
    If (lastElement)
        lastElement.style.outline := lastElementOutline
    
    ;MsgBox % AllElementsFromPoint(document,x,y)[1].innerHTML
    lastElement := GetElementFromPoint(document,x,y,index)
    ;lastElement := document.elementFromPoint(x,y)
    ;lastElement := document.elementsFromPoint(x,y)[1]
    lastElementOutline := lastElement.style.outline
    
    lastElement.style.outline := "2px solid red"
    
    comErrOriginal := ComObjError(False) ;prevent error msgs in case a property doesn't exist
    wb := "wb."
    
    title := document.title 
    url := IE.locationUrl
    tag := lastElement.tagName
    class := lastElement.className
    classes := StrSplit(class,[A_Tab, A_Space, "`n", "`r"])
    id := lastElement.id
    name := lastElement.name
    
    info :=          "---GENERAL INFO---"
    info .= "`r`n"   "Title: " title
    info .= "`r`n"   "Url: " url
    info .= "`r`n"   "Mouse position: x: " x " - y: " y
    info .= "`r`n"
    info .= "`r`n"   "---ELEMENT ACCESS INFO---"
    info .= "`r`n"   "tagName: " tag
    info .= "`r`n"   "classNames: " class
    info .= "`r`n"   "id: " id
    info .= "`r`n"   "name: " name
    info .= "`r`n"
    
    info .= "`r`n" "---ELEMENT ACCESS SUGGESTION---"
    info .= "`r`n"
    If (id)
        info .= wb "document.getElementById(""" id """)"
    Else If (name)
        info .= wb "document.getElementsByName(""" name """)"
    Else {
        info .= wb "document.querySelector(""" 
        info .= tag
        If (class)
            Loop % classes.MaxIndex()
                info .= "." classes[A_Index]
        info .= """)"
    }
    
    info .= "`r`n"
    info .= "`r`n"
    info .= "`r`n"   "---ELEMENT CONTENT INFO---"
    info .= "`r`n"   " value:" lastElement.value
    info .= "`r`n"
    info .= "`r`n"   " innerText:"
    info .= "`r`n"   lastElement.innerText
    info .= "`r`n"
    info .= "`r`n"   " textContent:"
    info .= "`r`n"   lastElement.textContent
    info .= "`r`n"
    info .= "`r`n"   " innerHTML:"
    info .= "`r`n"   lastElement.innerHTML
    
    ComObjError(comErrOriginal)
    GuiControl,, GUI_InfoBox, % info
    lastE := e
}
IeGet(hWnd:=0) {
    WinGetTitle, title, % (hWnd ? "ahk_id " hWnd : "A")
    For window in ComObjCreate("Shell.Application").windows
        If (InStr(window.fullName, "iexplore.exe") && window.document.title . " - Internet Explorer" = title)
            Return window
    Return {}
}

GuiSize(GuiHwnd, EventInfo, Width, Height) {
    GuiControl, Move, GUI_InfoBox, % "x" 0 " y" 0 " w" Width " h" Height-15
    GuiControl, Move, GUI_HotkeyNote, % "x" 0 " y" Height-15 " w" Width " h" 15
}
GuiClose() {
    ExitApp
}
Cleanup() {
    Global ActiveIeMon
    SetTimer, % ActiveIeMon, Off
    ActiveIeMonitor(true)
}

GetElementFromPoint(document, x, y, ByRef index) {
    If (!IsObject(document) || index <= 0) {
        Return []
    }
    If (index = 1)
        Return document.elementFromPoint(x, y)
    element := []
    elements := []
    old_visibility := []
    elementsOrdered := []
    Loop % index{
        element := document.elementFromPoint(x, y)
        If (!IsObject(element) || element.isSameNode(document.documentElement))
            Break
        elements[A_Index] := element
        old_visibility[A_Index] := element.style.visibility
        element.style.visibility := "hidden"
    }
    Loop % elements.MaxIndex()
        elements[A_Index].style.visibility := old_visibility[A_Index]
    Return elements[index]
    ;i := elements.MaxIndex()
    ;While (i > 0, i--) {
    ;    elementsOrdered[A_Index] := elements[i]
    ;}
    ;return elementsOrdered
}
It's more a proof of concept than anything else for now.

This is what it looks like atm:
Image

my test environment is Windows 8.1 with Internet Explorer 11.0.9600.18161
...
Tell me what you think, if you would use it, if you have any suggestions for improving this etc. :)


Older versions:
Spoiler

Re: Internet Explorer Element Spy (alternative to iWB2 Learner)

Posted: 14 Jan 2016, 17:12
by Joe Glines
I'm looking forward to playing with this!

Re: Internet Explorer Element Spy (alternative to iWB2 Learner)

Posted: 14 Jan 2016, 17:29
by Blackholyman
Glad to see a rewrite of this :)

Do remember that getElementsByClassName was not usable in IE when the old version was written

One thing i have had good use for is the old title bar info about the index in the element tag array

The suggestions part sound like the old iWeb lib did you see that?

Re: Internet Explorer Element Spy (alternative to iWB2 Learner)

Posted: 14 Jan 2016, 17:35
by Bruttosozialprodukt
Oh that's good to know, thanks.

No haven't seen the library yet. Could you elaborate on that?
Does it simply generate codes like this document.getElementById("testContainer").getElementsByClassName("redBackground")[0]?

edit:
Okay never mind, I found it. Interesting library for sure. Makes me thinking that macrorecorder for IE would be cool.

Re: Internet Explorer Element Spy (alternative to iWB2 Learner)

Posted: 14 Jan 2016, 21:16
by Soft
I wanted to rewrite iwb2 but I wasn't good enough to :(
glad to see this!

Re: Internet Explorer Element Spy (alternative to iWB2 Learner)

Posted: 15 Jan 2016, 07:14
by Bruttosozialprodukt
Little update. Improved performance and made sure that the last selected website gets cleaned up when the script exits or a different tab is activated etc.

Re: Internet Explorer Element Spy (alternative to iWB2 Learner)

Posted: 15 Jan 2016, 07:18
by Joe Glines
When I navigate to a new page, it isn't working (if I use the back button it works on the original page)

Re: Internet Explorer Element Spy (alternative to iWB2 Learner)

Posted: 15 Jan 2016, 07:27
by Bruttosozialprodukt
Try to reload the site, maybe there is still some code in there that the older version injected.
If that doesn't work, it would be nice to know on which sites you can reproduce this issue.
Also, which Windows and IE version are you using?

It's working fine for me btw. For example when I go to google and click a result or just type a different url in the navigation bar. It was working every time I tried it.

Re: Internet Explorer Element Spy (alternative to iWB2 Learner)

Posted: 15 Jan 2016, 07:31
by Joe Glines
It looks like it might have something to do with Amazon.com. I'm running Windows 7 and IE 11

Re: Internet Explorer Element Spy (alternative to iWB2 Learner)

Posted: 15 Jan 2016, 07:37
by Bruttosozialprodukt
I think I found the issue. It kind of breaks when you scroll down. At least that's what happens to me.
edit: Fixed it, try the new version.

The script should now work fine even if you scroll and zoom on the site etc.

Re: Internet Explorer Element Spy (alternative to iWB2 Learner)

Posted: 15 Jan 2016, 07:45
by Joe Glines
For me it acts querky on different Amazon pages (without scrolling down).

Anyway, I'm digging the tool! Thank you for your work and simplicity in design. Unless you do it later, I'm going to add hotkeys to copy the various items (classNames, ID, Name, TagName etc) to the clipboard which will make it a breeze to use as I'd prefer not to freeze it.

Thanks again!

Re: Internet Explorer Element Spy (alternative to iWB2 Learner)

Posted: 15 Jan 2016, 07:51
by Bruttosozialprodukt
I might add that, not sure yet.
This would do the job I guess:

Code: Select all

F2::
    GuiControlGet, GUI_InfoBox
    Clipboard := GUI_InfoBox
Return

Re: Internet Explorer Element Spy (alternative to iWB2 Learner)

Posted: 15 Jan 2016, 07:55
by Joe Glines
I meant specific ones like:
^+c to copy Classname
^+i to copy ID
^+n to copy Name
^+t to copy TagName

This would dump exactly what I want to the clipboard which I could then paste directly into the code I'd be writing.

Re: Internet Explorer Element Spy (alternative to iWB2 Learner)

Posted: 15 Jan 2016, 08:04
by Bruttosozialprodukt
I see, well I think I'll make hotkeys to copy the code that I'm planning to generate into the clipboard, so that you directly get things like this
document.getElementById("testContainer").getElementsByClassName("redBackground")[0] in your clipboard

Re: Internet Explorer Element Spy (alternative to iWB2 Learner)

Posted: 15 Jan 2016, 08:07
by Joe Glines
That would be pretty cool although for Noobs (and me too) I'd probably frequently forget to add the handle at the beginning. :(

Re: Internet Explorer Element Spy (alternative to iWB2 Learner)

Posted: 15 Jan 2016, 08:14
by Bruttosozialprodukt
Good point, I'll add a field in the GUI in which you can specify that.

Re: Internet Explorer Element Spy (alternative to iWB2 Learner)

Posted: 15 Jan 2016, 08:17
by Joe Glines
Damn! Now this is getting pretty cool! LOL :bravo:

BTW- have you tried your script on a page with frames? I always hated dealing with those and the IWB2 was very confusing to me on how to deal with them.

Re: Internet Explorer Element Spy (alternative to iWB2 Learner)

Posted: 15 Jan 2016, 08:21
by Bruttosozialprodukt
Yes, in fact I noticed that my script can't access them yet. I'll have to think about a good way to handle them.

Re: Internet Explorer Element Spy (alternative to iWB2 Learner)

Posted: 15 Jan 2016, 08:25
by Joe Glines
In the past couple of years I haven't found nearly as many sites using frames. Having said that, I always felt like crying a little when I would try and scrape something from a site that had them. LOL

Re: Internet Explorer Element Spy (alternative to iWB2 Learner)

Posted: 15 Jan 2016, 09:46
by Bruttosozialprodukt
Another little update.
The GUI now shows a code suggestion on how to access an element. (Not sure how reliable that is atm.)
It's far from what I want to see in the end, but it's a start.

I also added some properties to the GUI that may contain text that you want to extract from an element:
.value
.innerText
.textContent
.innerHTML