Index: trunk/autoquest-htmlmonitor/src/main/js/autoquest-htmlmonitor.js
===================================================================
--- trunk/autoquest-htmlmonitor/src/main/js/autoquest-htmlmonitor.js	(revision 942)
+++ trunk/autoquest-htmlmonitor/src/main/js/autoquest-htmlmonitor.js	(revision 944)
@@ -56,7 +56,124 @@
  */
 var autoquestActionConfig = [
-    { "tag": "body", "actions": [ "onunload", "onscroll" ] },
-    { "tag": "a", "actions": [ "onclick", "ondblclick", "onfocus" ] },
-    { "tag": "input", "actions": [ "onclick", "ondblclick", "onfocus" ] }
+    { "tag": "a", "actions": [ "onclick",
+                               "onfocus" ] },
+    //{ "tag": "abbr", "actions": [  ] },
+    //{ "tag": "address", "actions": [  ] },
+    //{ "tag": "applet", "actions": [  ] },
+    { "tag": "area", "actions": [ "onclick",
+                                  "onfocus" ] },
+    //{ "tag": "article", "actions": [  ] },
+    //{ "tag": "aside", "actions": [  ] },
+    { "tag": "audio", "actions": [ "onplaying",
+                                   "onpause",
+                                   "ontimeupdate" ] },
+    { "tag": "b", "actions": [ "onclick" ] },
+    //{ "tag": "bdi", "actions": [  ] },
+    //{ "tag": "bdo", "actions": [  ] },
+    //{ "tag": "blockquote", "actions": [  ] },
+    { "tag": "body", "actions": [ "onbeforeunload",
+                                  "onunload",
+                                  //"onerror",
+                                  "onscroll",
+                                  "onpagehide",
+                                  "onpageshow",
+                                  "onundo" ] },
+    { "tag": "button", "actions": [ "onclick",
+                                    "onfocus" ] },
+    { "tag": "canvas", "actions": [ "onclick" ] },
+    //{ "tag": "caption", "actions": [  ] },
+    { "tag": "cite", "actions": [ "onclick" ] },
+    { "tag": "code", "actions": [ "onclick" ] },
+    //{ "tag": "col", "actions": [  ] },
+    //{ "tag": "colgroup", "actions": [  ] },
+    { "tag": "command", "actions": [ "onclick",
+                                     "onfocus" ] },
+    //{ "tag": "datalist", "actions": [  ] },
+    { "tag": "dd", "actions": [ "onclick" ] },
+    { "tag": "del", "actions": [ "onclick" ] },
+    //{ "tag": "details", "actions": [  ] },
+    { "tag": "dfn", "actions": [ "onclick" ] },
+    { "tag": "div", "actions": [ "onclick" ] },
+    //{ "tag": "dl", "actions": [  ] },
+    { "tag": "dt", "actions": [ "onclick" ] },
+    { "tag": "em", "actions": [ "onclick" ] },
+    { "tag": "embed", "actions": [ "onclick" ] },
+    //{ "tag": "fieldset", "actions": [  ] },
+    //{ "tag": "figcaption", "actions": [  ] },
+    //{ "tag": "figure", "actions": [  ] },
+    //{ "tag": "footer", "actions": [  ] },
+    //{ "tag": "form", "actions": [  ] },
+    //{ "tag": "header", "actions": [  ] },
+    //{ "tag": "hgroup", "actions": [  ] },
+    { "tag": "h1", "actions": [ "onclick" ] },
+    { "tag": "h2", "actions": [ "onclick" ] },
+    { "tag": "h3", "actions": [ "onclick" ] },
+    { "tag": "h4", "actions": [ "onclick" ] },
+    { "tag": "h5", "actions": [ "onclick" ] },
+    { "tag": "h6", "actions": [ "onclick" ] },
+    //{ "tag": "hr", "actions": [  ] },
+    { "tag": "i", "actions": [ "onclick" ] },
+    //{ "tag": "iframe", "actions": [  ] },
+    { "tag": "img", "actions": [ "onclick" ] },
+    { "tag": "input_text", "actions": [ "onchange",
+                                        "onfocus" ] },
+    { "tag": "input", "actions": [ "onchange",
+                                   "onfocus" ] },
+    { "tag": "ins", "actions": [ "onclick" ] },
+    { "tag": "kbd", "actions": [ "onclick" ] },
+    { "tag": "keygen", "actions": [ "onchange",
+                                    "onfocus" ] },
+    //{ "tag": "label", "actions": [  ] },
+    //{ "tag": "legend", "actions": [  ] },
+    { "tag": "li", "actions": [ "onclick" ] },
+    //{ "tag": "map", "actions": [  ] },
+    { "tag": "mark", "actions": [ "onclick" ] },
+    { "tag": "menu", "actions": [ "onclick" ] },
+    { "tag": "meter", "actions": [ "onclick" ] },
+    //{ "tag": "nav", "actions": [  ] },
+    //{ "tag": "noscript", "actions": [  ] },
+    { "tag": "object", "actions": [ "onclick" ] },
+    //{ "tag": "ol", "actions": [  ] },
+    //{ "tag": "optgroup", "actions": [  ] },
+    //{ "tag": "option", "actions": [  ] },
+    { "tag": "output", "actions": [ "onclick" ] },
+    { "tag": "p", "actions": [ "onclick" ] },
+    //{ "tag": "param", "actions": [  ] },
+    //{ "tag": "pre", "actions": [  ] },
+    { "tag": "progress", "actions": [ "onclick" ] },
+    { "tag": "q", "actions": [ "onclick" ] },
+    //{ "tag": "rp", "actions": [  ] },
+    //{ "tag": "rt", "actions": [  ] },
+    //{ "tag": "ruby", "actions": [  ] },
+    { "tag": "s", "actions": [ "onclick" ] },
+    { "tag": "samp", "actions": [ "onclick" ] },
+    //{ "tag": "section", "actions": [  ] },
+    { "tag": "select", "actions": [ "onchange",
+                                    "onfocus" ] },
+    { "tag": "small", "actions": [ "onclick" ] },
+    //{ "tag": "source", "actions": [  ] },
+    { "tag": "span", "actions": [ "onclick" ] },
+    { "tag": "strong", "actions": [ "onclick" ] },
+    //{ "tag": "sub", "actions": [  ] },
+    //{ "tag": "summary", "actions": [  ] },
+    //{ "tag": "sup", "actions": [  ] },
+    //{ "tag": "table", "actions": [  ] },
+    //{ "tag": "tbody", "actions": [  ] },
+    { "tag": "td", "actions": [ "onclick" ] },
+    { "tag": "textarea", "actions": [ "onchange",
+                                      "onfocus" ] },
+    //{ "tag": "tfoot", "actions": [  ] },
+    { "tag": "th", "actions": [ "onclick" ] },
+    //{ "tag": "thead", "actions": [  ] },
+    { "tag": "time", "actions": [ "onclick" ] },
+    //{ "tag": "tr", "actions": [  ] },
+    //{ "tag": "track", "actions": [  ] },
+    { "tag": "u", "actions": [ "onclick" ] },
+    //{ "tag": "ul", "actions": [  ] },
+    { "tag": "var", "actions": [ "onclick" ] },
+    { "tag": "video", "actions": [ "onplaying",
+                                   "onpause",
+                                   "ontimeupdate" ] },
+    //{ "tag": "wbr", "actions": [  ] },
 ];
 
@@ -139,5 +256,5 @@
  */
 function addEventHandlingAttributes(node, parentPath) {
-    var nodePath = getNodePath(node, parentPath);
+    var nodePath;
     var i;
     var k;
@@ -145,9 +262,12 @@
     
     if (node.nodeType === Node.ELEMENT_NODE) {
+        nodePath = getNodePath(node, parentPath);
+        
         for (i = 0; i < autoquestActionConfig.length; i++) {
-            if (node.tagName.toLowerCase() === autoquestActionConfig[i].tag.toLowerCase()) {
+            if (getTagName(node) === autoquestActionConfig[i].tag.toLowerCase()) {
                 for (k = 0; k < autoquestActionConfig[i].actions.length; k++) {
-                    value = "handleEvent('" + autoquestActionConfig[i].actions[k] + "', '" +
-                        nodePath + "', event);";
+                    value = "handleEvent(this, '" +
+                                         autoquestActionConfig[i].actions[k] + "', '" +
+                                         nodePath + "', event);";
 
                     if (!node.getAttribute(autoquestActionConfig[i].actions[k])) {
@@ -164,8 +284,8 @@
             }
         }
-    }
-    
-    for (i = 0; i < node.childNodes.length; i++) {
-        addEventHandlingAttributes(node.childNodes[i], nodePath);
+        
+        for (i = 0; i < node.childNodes.length; i++) {
+            addEventHandlingAttributes(node.childNodes[i], nodePath);
+        }
     }
 }
@@ -174,9 +294,9 @@
  * generates a path through the DOM-structure of the HTML-site depending on a node and the path
  * to its parent node. The result is the parent path plus a path element for the provided node.
- * If the node has a tag name, this is the first part of the path element generated for the node.
- * If the node has an id, it becomes the second part of the path element. If the node does not have
- * an id, the method calculates the index of the node within all children of the same type within
- * the parent node. This index becomes then the second part of the path element generated for the
- * node. 
+ * The first part of the path element generated for the node is the tag name returned by
+ * {@link #getTagName(node)}. If the node has an id, it becomes the second part of the path
+ * element. If the node does not have an id, the method calculates the index of the node within
+ * all children of the same type within the parent node. This index becomes then the second part
+ * of the path element generated for the node. 
  * 
  * @param node       the node of the DOM structure for which the path shall be created
@@ -191,19 +311,5 @@
     var i = 0;
     
-    if (node.nodeType === Node.ELEMENT_NODE) {
-        nodePath += node.tagName.toLowerCase();
-        
-        if ("input" === node.tagName.toLowerCase()) {
-            if (node.type && (node.type !== "")) {
-                nodePath += "_" + node.type;
-            }
-            else {
-                nodePath += "_text";
-            }
-        }
-    }
-    else {
-        nodePath += "undefined";
-    }
+    nodePath += getTagName(node);
     
     if ((node.id) && (node.id !== "")) {
@@ -211,5 +317,4 @@
     }
     else {
-        
         if (node.parentNode) {
             for (i = 0; i < node.parentNode.childNodes.length; i++) {
@@ -239,15 +344,18 @@
  * of the nodes. These attributes are generated by the
  * {@link #addEventHandlingAttributes(node,parentPath)} function. It creates a new Event object and
- * add it to the list of <code>autoquestRecordedEvents</code>. If this list achieves the maximum
+ * adds it to the list of <code>autoquestRecordedEvents</code>. If this list achieves the maximum
  * <code>autoquestPackageSize</code> the events in the list are sent to the server asynchronously
  * through calling {@link #sendRequest()}.
  * 
+ * @param node      the node that fired the event
  * @param eventName the name of the event, e.g. onscroll
  * @param nodePath  the path to the node in the HTML DOM on which the event occurred
  * @param event     the HTML event that occured
  */
-function handleEvent(eventName, nodePath, event) {
-    log("handling event " + eventName);
-    
+function handleEvent(node, eventName, nodePath, event) {
+    var eventType;
+    var eventObj = null;
+    var tagName;
+
     if (!autoquestDestination) {
         // do nothing if we have no destination to send data to
@@ -255,32 +363,70 @@
     }
     
-    var eventType = eventName.toLowerCase();
-    
-    var eventObj = autoquestRecordedEvents.pop();
-    
-    // reuse previous on scroll events to prevent too many events
-    if ((eventName !== "onscroll") || (!eventObj) || (eventObj.type !== "onscroll")) {
-        if (eventObj) {
-            autoquestRecordedEvents.push(eventObj);
-        }
+    log("handling event " + eventName + " on " + node);
+    
+    eventType = eventName.toLowerCase();
+
+    if (autoquestRecordedEvents.length > 0) {
+        eventObj = autoquestRecordedEvents[autoquestRecordedEvents.length - 1];
+
+        // check if an event showed up several times either for the same or for a parent GUI element
+        if ((eventObj.type === eventName) && (eventObj.nodePath.indexOf(nodePath) === 0)) {
+            // the event is of the same type.
+            if (eventObj.nodePath.length > nodePath.length) {
+                // the same event showed up for the parent GUI element. This must not be handled.
+                // So ignore it
+                log("discarding event " + eventName + " on " + node +
+                    " as it is already handled by a child");
+                return;
+            }
+            else if (eventName !== "onscroll") {
+                // we have the same event on the same element. If it is an onscroll, we should
+                // reuse it. But it is not an onscroll. So we ignore the existing event.
+                eventObj = null;
+            }
+        }
+        else {
+            // the event is not of an equal type as the previous one. So we will not reuse it
+            eventObj = null;
+        }
+    }
+    
+    if (!eventObj) {
+        // create a new event and add it to the list
         eventObj = new Event(eventType, nodePath);
-    }
-    
+        log("storing event " + eventName);
+        autoquestRecordedEvents.push(eventObj);
+    }
+
+    // now add further event parameters
     if ((eventType === "onclick") || (eventType === "ondblclick")) {
         eventObj.setClickCoordinates(getEventCoordinates(event));
     }
 
-    if ((eventType === "onkeypress") || (eventType === "onkeydown") || (eventType === "onkeyup")) {
-        eventObj.setKey(event.keyCode);
+    tagName = getTagName(node);
+    
+    if ("input_password" !== tagName) {
+        if ((eventType === "onkeypress") ||
+            (eventType === "onkeydown") ||
+            (eventType === "onkeyup"))
+        {
+            eventObj.setKey(event.keyCode);
+        }
+        else if (eventType == "onchange") {
+            if ((tagName.indexOf("input") === 0) ||
+                (tagName === "textarea") ||
+                (tagName === "keygen"))
+            {
+                eventObj.setSelectedValue(node.value);
+            }
+            else if (tagName === "select") {
+                eventObj.setSelectedValue(node.options.item(node.options.selectedIndex));
+            }
+        }
     }
     
     if (eventType === "onscroll") {
-        if (window.pageYOffset) {
-            eventObj.setScrollPosition(window.pageYOffset);
-        }
-    }
-
-    log("storing event " + eventName);
-    autoquestRecordedEvents.push(eventObj);
+        eventObj.setScrollPosition(getScrollCoordinates(node));
+    }
 
     if (autoquestRecordedEvents.length >= autoquestPackageSize) {
@@ -295,4 +441,30 @@
 }
 
+/**
+ * determines a tag name of a node. If the node is an input element, the tag name includes the
+ * type of element. Otherwise the method simply returns the tag name.
+ * 
+ * @param node the node for which the name must be determined
+ * 
+ * @return the name as described
+ */
+function getTagName(node) {
+    var tagName = null;
+    
+    if (node.tagName) {
+        tagName = node.tagName.toLowerCase();
+        if ("input" === tagName) {
+            if (node.type && (node.type !== "")) {
+                tagName += "_" + node.type;
+            }
+            else {
+                tagName += "_text";
+            }
+        }
+    }
+
+    return tagName;
+}
+    
 /**
  * sends the collected data to the server, named in the destination-variable. For this it generates
@@ -310,5 +482,8 @@
         log("creating message");
         eventList = autoquestRecordedEvents;
-        autoquestRecordedEvents = [];
+        
+        // put the last event into the new list to allow for checks for reoccurence of the same
+        // event
+        autoquestRecordedEvents = [ eventList.pop() ];
         
         message = "{\"message\":{\"clientInfos\":{";
@@ -354,4 +529,32 @@
         request.send(message);
     }
+}
+
+/**
+ * determines the scroll coordinates of the scrolled element
+ * 
+ * @param node the element that was scrolled
+ * 
+ * @returns the coordinates of the scrolling as an array with x and y coordinate
+ */
+function getScrollCoordinates(node) {
+    if (node.scrollLeft || node.scrollTop) {
+        return [node.scrollLeft, node.scrollTop];
+    }
+    else if ((node === window) && window.pageYOffset) {
+        return [window.pageXOffset, window.pageYOffset];
+    }
+    else if ((node == document) || (node == document.body) || (node == document.documentElement)) { 
+        if (document.body && (document.body.scrollLeft || document.body.scrollTop)) {
+            return [document.body.scrollLeft, document.body.scrollTop];
+        }
+        else if (document.documentElement &&
+                 (document.documentElement.scrollLeft || document.documentElement.scrollTop))
+        {
+            return [document.documentElement.scrollLeft, document.documentElement.scrollTop];
+        }
+    }
+
+    return [-1, -1];
 }
 
@@ -478,6 +681,10 @@
     };
     
+    this.setSelectedValue = function(value) {
+        this.selectedValue = value;
+    };
+  
     this.setScrollPosition = function(scrollPosition) {
-          this.scrollPosition = scrollPosition;
+        this.scrollPosition = scrollPosition;
     };
     
@@ -495,6 +702,10 @@
         }
         
-        if (this.scrollPosition) {
-            eventInJSON += ",\"scrollPosition\":" + this.scrollPosition;
+        if (this.selectedValue) {
+            eventInJSON += ",\"selectedValue\":\"" + this.selectedValue + "\"";
+        }
+        
+        if ("onscroll" === this.type) {
+            eventInJSON += ",\"scrollPosition\":[" + this.scrollPosition + "]";
         }
 
