source: trunk/autoquest-htmlmonitor/src/main/js/autoquest-htmlmonitor.js @ 998

Last change on this file since 998 was 992, checked in by pharms, 12 years ago
  • added some more input events to be traced
File size: 25.9 KB
Line 
1//   Copyright 2012 Georg-August-Universität Göttingen, Germany
2//
3//   Licensed under the Apache License, Version 2.0 (the "License");
4//   you may not use this file except in compliance with the License.
5//   You may obtain a copy of the License at
6//
7//       http://www.apache.org/licenses/LICENSE-2.0
8//
9//   Unless required by applicable law or agreed to in writing, software
10//   distributed under the License is distributed on an "AS IS" BASIS,
11//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//   See the License for the specific language governing permissions and
13//   limitations under the License.
14
15/**
16 * AutoQUEST - HTML Monitor
17 *
18 * Description: This script records the interactions done by an user on an
19 * HTML-Website and sends them to a server. It does not register actions on
20 * Flash, Java, or other special inputs. This script is tested on Firefox
21 * 15.0.1.
22 *
23 * To insert it on your HTML-side, you need to write <script
24 * language="JavaScript" type="text/javascript" src="autoquest-htmlmonitor.js"></script> in the
25 * head and change the src-attribute to the location, you have chosen for this
26 * script.
27 *
28 * To change the recorded events, edit the action config array. If you want to change
29 * the server where the data is send to, rewrite the destination variable. The
30 * records are send to the server, JSON-formatted, if there are 10 inputs or the
31 * user changes/closes the site.
32 *
33 * Authors: Simon Leidenbach, Simon Reuss, Patrick Harms
34 *
35 * Version: 0.1
36 */
37
38/**
39 * the server to send the recorded data to
40 */
41var autoquestDestination;
42
43/**
44 * an ID that is more or less unique for the client
45 */
46var autoquestClientId;
47
48/**
49 * the maximum number of recorded events to be put into one package sent to the server
50 */
51var autoquestPackageSize = 10;
52
53/**
54 * this variable defines the tags for which event handling shall be added, as well as the
55 * event handling action to be monitored
56 */
57var autoquestActionConfig = [
58    { "tag": "a", "actions": [ "onclick",
59                               "onfocus" ] },
60    //{ "tag": "abbr", "actions": [  ] },
61    //{ "tag": "address", "actions": [  ] },
62    //{ "tag": "applet", "actions": [  ] },
63    { "tag": "area", "actions": [ "onclick",
64                                  "onfocus" ] },
65    //{ "tag": "article", "actions": [  ] },
66    //{ "tag": "aside", "actions": [  ] },
67    { "tag": "audio", "actions": [ "onplaying",
68                                   "onpause",
69                                   "ontimeupdate" ] },
70    { "tag": "b", "actions": [ "onclick" ] },
71    //{ "tag": "bdi", "actions": [  ] },
72    //{ "tag": "bdo", "actions": [  ] },
73    //{ "tag": "blockquote", "actions": [  ] },
74    { "tag": "body", "actions": [ "onbeforeunload",
75                                  "onunload",
76                                  //"onerror",
77                                  "onscroll",
78                                  "onpagehide",
79                                  "onpageshow",
80                                  "onundo" ] },
81    { "tag": "button", "actions": [ "onclick",
82                                    "onfocus" ] },
83    { "tag": "canvas", "actions": [ "onclick" ] },
84    //{ "tag": "caption", "actions": [  ] },
85    { "tag": "cite", "actions": [ "onclick" ] },
86    { "tag": "code", "actions": [ "onclick" ] },
87    //{ "tag": "col", "actions": [  ] },
88    //{ "tag": "colgroup", "actions": [  ] },
89    { "tag": "command", "actions": [ "onclick",
90                                     "onfocus" ] },
91    //{ "tag": "datalist", "actions": [  ] },
92    { "tag": "dd", "actions": [ "onclick" ] },
93    { "tag": "del", "actions": [ "onclick" ] },
94    //{ "tag": "details", "actions": [  ] },
95    { "tag": "dfn", "actions": [ "onclick" ] },
96    { "tag": "div", "actions": [ "onclick" ] },
97    //{ "tag": "dl", "actions": [  ] },
98    { "tag": "dt", "actions": [ "onclick" ] },
99    { "tag": "em", "actions": [ "onclick" ] },
100    { "tag": "embed", "actions": [ "onclick" ] },
101    //{ "tag": "fieldset", "actions": [  ] },
102    //{ "tag": "figcaption", "actions": [  ] },
103    //{ "tag": "figure", "actions": [  ] },
104    //{ "tag": "footer", "actions": [  ] },
105    //{ "tag": "form", "actions": [  ] },
106    //{ "tag": "header", "actions": [  ] },
107    //{ "tag": "hgroup", "actions": [  ] },
108    { "tag": "h1", "actions": [ "onclick" ] },
109    { "tag": "h2", "actions": [ "onclick" ] },
110    { "tag": "h3", "actions": [ "onclick" ] },
111    { "tag": "h4", "actions": [ "onclick" ] },
112    { "tag": "h5", "actions": [ "onclick" ] },
113    { "tag": "h6", "actions": [ "onclick" ] },
114    //{ "tag": "hr", "actions": [  ] },
115    { "tag": "i", "actions": [ "onclick" ] },
116    //{ "tag": "iframe", "actions": [  ] },
117    { "tag": "img", "actions": [ "onclick" ] },
118    { "tag": "input_text", "actions": [ "onchange",
119                                        "onfocus" ] },
120    { "tag": "input_password", "actions": [ "onchange",
121                                            "onfocus" ] },
122    { "tag": "input_checkbox", "actions": [ "onchange",
123                                            "onclick",
124                                            "onfocus" ] },
125    { "tag": "input_radio", "actions": [ "onchange",
126                                         "onclick",
127                                         "onfocus" ] },
128    { "tag": "input_submit", "actions": [ "onclick",
129                                          "onfocus" ] },
130    { "tag": "input_reset", "actions": [ "onclick",
131                                         "onfocus" ] },
132    { "tag": "input_file", "actions": [ "onclick",
133                                        "onfocus" ] },
134    { "tag": "input_image", "actions": [ "onclick",
135                                         "onfocus" ] },
136    { "tag": "input_button", "actions": [ "onclick",
137                                          "onfocus" ] },
138    { "tag": "input", "actions": [ "onchange",
139                                   "onfocus" ] },
140    { "tag": "ins", "actions": [ "onclick" ] },
141    { "tag": "kbd", "actions": [ "onclick" ] },
142    { "tag": "keygen", "actions": [ "onchange",
143                                    "onfocus" ] },
144    //{ "tag": "label", "actions": [  ] },
145    //{ "tag": "legend", "actions": [  ] },
146    { "tag": "li", "actions": [ "onclick" ] },
147    //{ "tag": "map", "actions": [  ] },
148    { "tag": "mark", "actions": [ "onclick" ] },
149    { "tag": "menu", "actions": [ "onclick" ] },
150    { "tag": "meter", "actions": [ "onclick" ] },
151    //{ "tag": "nav", "actions": [  ] },
152    //{ "tag": "noscript", "actions": [  ] },
153    { "tag": "object", "actions": [ "onclick" ] },
154    //{ "tag": "ol", "actions": [  ] },
155    //{ "tag": "optgroup", "actions": [  ] },
156    //{ "tag": "option", "actions": [  ] },
157    { "tag": "output", "actions": [ "onclick" ] },
158    { "tag": "p", "actions": [ "onclick" ] },
159    //{ "tag": "param", "actions": [  ] },
160    //{ "tag": "pre", "actions": [  ] },
161    { "tag": "progress", "actions": [ "onclick" ] },
162    { "tag": "q", "actions": [ "onclick" ] },
163    //{ "tag": "rp", "actions": [  ] },
164    //{ "tag": "rt", "actions": [  ] },
165    //{ "tag": "ruby", "actions": [  ] },
166    { "tag": "s", "actions": [ "onclick" ] },
167    { "tag": "samp", "actions": [ "onclick" ] },
168    //{ "tag": "section", "actions": [  ] },
169    { "tag": "select", "actions": [ "onchange",
170                                    "onfocus" ] },
171    { "tag": "small", "actions": [ "onclick" ] },
172    //{ "tag": "source", "actions": [  ] },
173    { "tag": "span", "actions": [ "onclick" ] },
174    { "tag": "strong", "actions": [ "onclick" ] },
175    //{ "tag": "sub", "actions": [  ] },
176    //{ "tag": "summary", "actions": [  ] },
177    //{ "tag": "sup", "actions": [  ] },
178    //{ "tag": "table", "actions": [  ] },
179    //{ "tag": "tbody", "actions": [  ] },
180    { "tag": "td", "actions": [ "onclick" ] },
181    { "tag": "textarea", "actions": [ "onchange",
182                                      "onfocus" ] },
183    //{ "tag": "tfoot", "actions": [  ] },
184    { "tag": "th", "actions": [ "onclick" ] },
185    //{ "tag": "thead", "actions": [  ] },
186    { "tag": "time", "actions": [ "onclick" ] },
187    //{ "tag": "tr", "actions": [  ] },
188    //{ "tag": "track", "actions": [  ] },
189    { "tag": "u", "actions": [ "onclick" ] },
190    //{ "tag": "ul", "actions": [  ] },
191    { "tag": "var", "actions": [ "onclick" ] },
192    { "tag": "video", "actions": [ "onplaying",
193                                   "onpause",
194                                   "ontimeupdate" ] },
195    //{ "tag": "wbr", "actions": [  ] },
196];
197
198/**
199 * a possibility to trace, what is going on
200 */
201var autoquestDoLog = false;
202
203/*var matchedTags = ["A", "ABBR", "ACRONYM", "ADDRESS", "AREA", "B", "BIG", "BLOCKQUOTE", "BODY",
204                   "BUTTON", "CAPTION", "CENTER", "CITE", "CODE", "COL", "COLGROUP", "DD", "DEL",
205                   "DFN", "DIR", "DIV", "DL", "DT", "EM", "FIELDSET", "FORM", "H1", "H2", "H3",
206                   "H4", "H5", "H6", "HR", "I", "IMG", "INPUT", "INS", "KBD", "LABEL", "LEGEND",
207                   "LI", "LINK", "MAP", "MENU", "NOFRAMES", "NOSCRIPT", "OBJECT", "OL",
208                   "OPTGROUP", "OPTION", "P", "PRE", "Q", "S", "SAMP", "SELECT", "SMALL", "SPAN",
209                   "STRIKE", "STRONG", "SUB", "SUP", "TABLE", "TBODY", "TD", "TEXTAREA", "TFOOT",
210                   "TH", "THEAD", "TR", "TT", "U", "UL", "VAR"];*/
211/*var actions = ['onclick', 'ondblclick', 'onkeypress', 'onkeydown', 'onkeyup',
212'onmouseout' , 'onmousemove' ,'onfocus','onscroll'];  // edit*/
213
214/**
215 * stores events, which were recorded but not sent to the server yet
216 */
217var autoquestRecordedEvents = [];
218
219/**
220 * automatically executed to initialize the event handling
221 */
222(function() {
223    initEventHandling();
224}());
225
226
227/**
228 * initializes the event handling after the document is loaded completely
229 */
230function initEventHandling() {
231    if (document.body) {
232        log("adding event handling attributes");
233        determineDestination();
234        addEventHandlingAttributes(document.documentElement, "");
235       
236        if (document.readyState !== "complete") {
237            // if the document is not loaded yet, try to add further event handling later
238            setTimeout(initEventHandling, 200);
239        }
240    }
241    else {
242        setTimeout(initEventHandling, 200);
243    }         
244}
245
246/**
247 * traverses the DOM-structure of the HTML-site and determines the URL of this script. Based on
248 * this URL, it calculates the destination to which the traced interactions must be sent
249 */
250function determineDestination() {
251    var scriptElements = document.getElementsByTagName("script");
252    var i;
253    var index;
254   
255    for (i = 0; i < scriptElements.length; i++) {
256        if ((scriptElements[i].type === "text/javascript") && (scriptElements[i].src)) {
257            index = scriptElements[i].src.lastIndexOf("script/autoquest-htmlmonitor.js");
258            if (index > -1) {
259                autoquestDestination = scriptElements[i].src.substring(0, index - 1);
260                log("using destination " + autoquestDestination);
261            }
262        }
263    }
264}
265
266/**
267 * traverses the DOM-structure of the HTML-site and adds event handling attributes to each
268 * relevant node
269 *
270 * @param node       the node of the DOM structure that shall be adapted and whose children shall
271 *                   be traversed
272 * @param parentPath the path to the parent node of the provided node within the DOM-structure of
273 *                   the HTML-site
274 */
275function addEventHandlingAttributes(node, parentPath) {
276    var nodePath;
277    var i;
278    var k;
279    var value;
280   
281    if (node.nodeType === Node.ELEMENT_NODE) {
282        nodePath = getNodePath(node, parentPath);
283       
284        for (i = 0; i < autoquestActionConfig.length; i++) {
285            if (getTagName(node) === autoquestActionConfig[i].tag.toLowerCase()) {
286                for (k = 0; k < autoquestActionConfig[i].actions.length; k++) {
287                    value = "handleEvent(this, '" +
288                                         autoquestActionConfig[i].actions[k] + "', '" +
289                                         nodePath + "', event);";
290
291                    if (!node.getAttribute(autoquestActionConfig[i].actions[k])) {
292                        node.setAttribute(autoquestActionConfig[i].actions[k], value);
293                    }
294                    else {
295                        var oldValue = node.getAttribute(autoquestActionConfig[i].actions[k]);
296                        if (oldValue.indexOf(value) < 0) {
297                            node.setAttribute(autoquestActionConfig[i].actions[k],
298                                              value + ' ' + oldValue);
299                        }
300                    }
301                }
302            }
303        }
304       
305        for (i = 0; i < node.childNodes.length; i++) {
306            addEventHandlingAttributes(node.childNodes[i], nodePath);
307        }
308    }
309}
310
311/**
312 * generates a path through the DOM-structure of the HTML-site depending on a node and the path
313 * to its parent node. The result is the parent path plus a path element for the provided node.
314 * The first part of the path element generated for the node is the tag name returned by
315 * {@link #getTagName(node)}. If the node has an id, it becomes the second part of the path
316 * element. If the node does not have an id, the method calculates the index of the node within
317 * all children of the same type within the parent node. This index becomes then the second part
318 * of the path element generated for the node.
319 *
320 * @param node       the node of the DOM structure for which the path shall be created
321 * @param parentPath the path to the parent node of the provided node
322 *
323 * @returns a path in the DOM-structure of the HTML-site including the parent path an a path
324 *          element for the provided node
325 */
326function getNodePath(node, parentPath) {
327    var nodePath = parentPath + "/";
328    var index = -1;
329    var i = 0;
330   
331    nodePath += getTagName(node);
332   
333    if ((node.id) && (node.id !== "")) {
334        nodePath += "(id=" + node.id + ")";
335    }
336    else {
337        if (node.parentNode) {
338            for (i = 0; i < node.parentNode.childNodes.length; i++) {
339                if (node.parentNode.childNodes[i].tagName === node.tagName) {
340                    index++;
341                    // if === also returns true if the nodes are not identical but only equal,
342                    // this may fail.
343                    if (node.parentNode.childNodes[i] === node) {
344                        break;
345                    }
346                }
347            }
348           
349        }
350        else {
351            index = 0;
352        }
353       
354        nodePath += "[" + index + "]";
355    }
356   
357    return nodePath;
358}
359
360/**
361 * handles an event that happened on a node. This method is called by the event handling attributes
362 * of the nodes. These attributes are generated by the
363 * {@link #addEventHandlingAttributes(node,parentPath)} function. It creates a new Event object and
364 * adds it to the list of <code>autoquestRecordedEvents</code>. If this list achieves the maximum
365 * <code>autoquestPackageSize</code> the events in the list are sent to the server asynchronously
366 * through calling {@link #sendRequest()}.
367 *
368 * @param node      the node that fired the event
369 * @param eventName the name of the event, e.g. onscroll
370 * @param nodePath  the path to the node in the HTML DOM on which the event occurred
371 * @param event     the HTML event that occured
372 */
373function handleEvent(node, eventName, nodePath, event) {
374    var eventType;
375    var eventObj = null;
376    var tagName;
377
378    if (!autoquestDestination) {
379        // do nothing if we have no destination to send data to
380        return;
381    }
382   
383    log("handling event " + eventName + " on " + node);
384   
385    eventType = eventName.toLowerCase();
386
387    if (autoquestRecordedEvents.length > 0) {
388        eventObj = autoquestRecordedEvents[autoquestRecordedEvents.length - 1];
389
390        // check if an event showed up several times either for the same or for a parent GUI element
391        if ((eventObj.type === eventName) && (eventObj.nodePath.indexOf(nodePath) === 0)) {
392            // the event is of the same type.
393            if (eventObj.nodePath.length > nodePath.length) {
394                // the same event showed up for the parent GUI element. This must not be handled.
395                // So ignore it
396                log("discarding event " + eventName + " on " + node +
397                    " as it is already handled by a child");
398                return;
399            }
400            else if (eventName !== "onscroll") {
401                // we have the same event on the same element. If it is an onscroll, we should
402                // reuse it. But it is not an onscroll. So we ignore the existing event.
403                eventObj = null;
404            }
405        }
406        else {
407            // the event is not of an equal type as the previous one. So we will not reuse it
408            eventObj = null;
409        }
410    }
411   
412    if (!eventObj) {
413        // create a new event and add it to the list
414        eventObj = new Event(eventType, nodePath);
415        log("storing event " + eventName);
416        autoquestRecordedEvents.push(eventObj);
417    }
418
419    // now add further event parameters
420    if ((eventType === "onclick") || (eventType === "ondblclick")) {
421        eventObj.setClickCoordinates(getEventCoordinates(event));
422    }
423
424    tagName = getTagName(node);
425   
426    if ("input_password" !== tagName) {
427        if ((eventType === "onkeypress") ||
428            (eventType === "onkeydown") ||
429            (eventType === "onkeyup"))
430        {
431            eventObj.setKey(event.keyCode);
432        }
433        else if (eventType == "onchange") {
434            if ((tagName.indexOf("input") === 0) ||
435                (tagName === "textarea") ||
436                (tagName === "keygen"))
437            {
438                eventObj.setSelectedValue(node.value);
439            }
440            else if (tagName === "select") {
441                eventObj.setSelectedValue(node.options.item(node.options.selectedIndex));
442            }
443        }
444    }
445   
446    if (eventType === "onscroll") {
447        eventObj.setScrollPosition(getScrollCoordinates(node));
448    }
449
450    if (autoquestRecordedEvents.length >= autoquestPackageSize) {
451        log("initiating sending events");
452        setTimeout(sendRequest, 100);
453    }
454    else if (eventType === "onunload") {
455        log("initiating sending events");
456        sendRequest();
457    }
458
459}
460
461/**
462 * determines a tag name of a node. If the node is an input element, the tag name includes the
463 * type of element. Otherwise the method simply returns the tag name.
464 *
465 * @param node the node for which the name must be determined
466 *
467 * @return the name as described
468 */
469function getTagName(node) {
470    var tagName = null;
471   
472    if (node.tagName) {
473        tagName = node.tagName.toLowerCase();
474        if ("input" === tagName) {
475            if (node.type && (node.type !== "")) {
476                tagName += "_" + node.type;
477            }
478            else {
479                tagName += "_text";
480            }
481        }
482    }
483
484    return tagName;
485}
486   
487/**
488 * sends the collected data to the server, named in the destination-variable. For this it generates
489 * a JSON formatted message and uses Ajax and the <code>autoquestDestination</code> to send it to
490 * the server
491 */
492function sendRequest() {
493    var eventList;
494    var message;
495    var clientId;
496    var i = 0;
497    var request;
498   
499    if (autoquestRecordedEvents.length > 0) {
500        log("creating message");
501        eventList = autoquestRecordedEvents;
502       
503        // put the last event into the new list to allow for checks for reoccurence of the same
504        // event
505        autoquestRecordedEvents = [ eventList.pop() ];
506       
507        message = "{\"message\":{\"clientInfos\":{";
508       
509        log("reading client id");
510        clientId = getClientId();
511        if ((clientId) && (clientId !== "")) {
512            message += "\"clientId\":\"" + clientId + "\",";
513        }
514       
515        log("adding other infos");
516        message += "\"userAgent\":\"" + navigator.userAgent + "\",";
517        message += "\"title\":\"" + document.title + "\",";
518        message += "\"url\":\"" + document.URL + "\"},";
519       
520       
521        message += "\"events\":[";
522       
523        for (i = 0; i < eventList.length; i++) {
524            if (i > 0) {
525                message += ",";
526            }
527            message += eventList[i].toJSON();
528        }
529       
530        message += "]}}";
531       
532        request = null;
533       
534        // Mozilla
535        if (window.XMLHttpRequest) {
536            request = new XMLHttpRequest();
537        }
538        // IE
539        else if (window.ActiveXObject) {
540            request = new ActiveXObject("Microsoft.XMLHTTP");
541        }
542       
543        request.open("POST", autoquestDestination, false);
544        request.setRequestHeader("Content-Type", "application/json");
545
546        log("sending " + message);
547        request.send(message);
548    }
549}
550
551/**
552 * determines the scroll coordinates of the scrolled element
553 *
554 * @param node the element that was scrolled
555 *
556 * @returns the coordinates of the scrolling as an array with x and y coordinate
557 */
558function getScrollCoordinates(node) {
559    if (node.scrollLeft || node.scrollTop) {
560        return [node.scrollLeft, node.scrollTop];
561    }
562    else if ((node === window) && window.pageYOffset) {
563        return [window.pageXOffset, window.pageYOffset];
564    }
565    else if ((node == document) || (node == document.body) || (node == document.documentElement)) {
566        if (document.body && (document.body.scrollLeft || document.body.scrollTop)) {
567            return [document.body.scrollLeft, document.body.scrollTop];
568        }
569        else if (document.documentElement &&
570                 (document.documentElement.scrollLeft || document.documentElement.scrollTop))
571        {
572            return [document.documentElement.scrollLeft, document.documentElement.scrollTop];
573        }
574    }
575
576    return [-1, -1];
577}
578
579/**
580 * determines the coordinates of an onclick or ondblclick event. If the coordinates to not come
581 * with the provided event, they are determined based on the surrounding object
582 *
583 * @param event the event to extract the coordinates of
584 *
585 * @returns the coordinates of the event as an array with x and y coordinate
586 */
587function getEventCoordinates(event) {
588    if (event.layerX) {
589        return [event.layerX, event.layerY];
590    }
591    else if (event.offsetX) {
592        return [event.offsetX, event.offsetY];
593    }
594
595    var obj = event.target || event.srcElement;
596    var objOffset = getPageOffset(obj);
597
598    return [(event.clientX - objOffset[0]), (event.clientY - objOffset[1])];
599}
600
601/**
602 * determines the page offset of an object using the parent objects offset
603 */
604function getPageOffset(object) {
605    var top = 0;
606    var left = 0;
607    var obj = object;
608
609    while (obj.offsetParent) {
610        left += obj.offsetLeft;
611        top += obj.offsetTop;
612        obj = obj.offsetParent;
613    }
614
615    return [left, top];
616}
617
618/**
619 * generates a client id based on several information retrieved from the environment. The client
620 * id is not always unique
621 *
622 * @returns the client id
623 */
624function getClientId() {
625    var clientIdStr;
626    var clientId;
627    var i = 0;
628   
629    if (!autoquestClientId) {
630        // create something like a more or less unique checksum.
631        clientIdStr =
632            navigator.appCodeName + navigator.appName + navigator.appVersion +
633            navigator.cookieEnabled + navigator.language + navigator.platform +
634            navigator.userAgent + navigator.javaEnabled() + window.location.protocol +
635            window.location.host + new Date().getTimezoneOffset();
636
637        clientId = clientIdStr.length;
638
639        for (i = 0; i < clientIdStr.length; i++) {
640            clientId += clientIdStr.charCodeAt(i);
641        }
642       
643        autoquestClientId = clientId;
644    }
645
646    return autoquestClientId;
647}
648
649/**
650 * performs a simple logging by adding a specific div to the HTML
651 *
652 * @param text the text to be logged
653 */
654function log(text) {
655    if (autoquestDoLog) {
656        var loggingInfo = document.getElementById("loggingInfoParagraph");
657       
658        if (!loggingInfo) {
659            var div = document.createElement("div");
660            div.setAttribute("style", "z-index:1000000; background-color:#afa; " +
661                             "border:1px black solid; position:absolute; " +
662                             "top:10px; right:10px; width:350px; height:auto");
663           
664            loggingInfo = document.createElement("div");
665            loggingInfo.id = "loggingInfoParagraph";
666            div.appendChild(loggingInfo);
667           
668            var body = document.getElementsByTagName("body")[0];
669            if (!body) {
670                alert("could not enable logging");
671            }
672            else {
673                body.appendChild(div);
674            }
675        }
676       
677        loggingInfo.appendChild(document.createTextNode(text));
678        loggingInfo.appendChild(document.createElement("br"));
679    }
680}
681
682/**
683 * this class represents a single event
684 *
685 * @param type     the type of the event
686 * @param nodePath the path through the HTML DOM to the event target
687 */
688function Event(type, nodePath) {
689    this.type = type;
690    this.nodePath = nodePath;
691    this.time = new Date().getTime();
692   
693    this.setClickCoordinates = function(coordinates) {
694          this.clickCoordinates = coordinates;
695    };
696   
697    this.setKey = function(key) {
698          this.key = key;
699    };
700   
701    this.setSelectedValue = function(value) {
702        this.selectedValue = value;
703    };
704 
705    this.setScrollPosition = function(scrollPosition) {
706        this.scrollPosition = scrollPosition;
707    };
708   
709    this.toJSON = function() {
710        var eventInJSON =
711            "{\"path\":\"" + this.nodePath + "\",\"time\":" + this.time + ",\"eventType\":\"" +
712            this.type + "\"";
713
714        if ((this.clickCoordinates) && (!isNaN(this.clickCoordinates[0]))) {
715            eventInJSON += ",\"coordinates\":[" + this.clickCoordinates + "]";
716        }
717
718        if (this.key) {
719            eventInJSON += ",\"key\":" + this.key;
720        }
721       
722        if (this.selectedValue) {
723            eventInJSON += ",\"selectedValue\":\"" + this.selectedValue + "\"";
724        }
725       
726        if ("onscroll" === this.type) {
727            eventInJSON += ",\"scrollPosition\":[" + this.scrollPosition + "]";
728        }
729
730        eventInJSON += "}";
731       
732        return eventInJSON;
733    };
734}
735
Note: See TracBrowser for help on using the repository browser.