// ie constants
function Constants()
{
}
// all HTML events.
// Constants.abort = "abort";
// Constants.activate = "activate";
// Constants.afterprint = "afterprint";
// Constants.afterupdate = "afterupdate";
// Constants.beforeactivate = "beforeactivate";
// Constants.beforecopy = "beforecopy";
// Constants.beforecut = "beforecut";
// Constants.beforedeactivate = "beforedeactivate";
// Constants.beforeeditfocus = "beforeeditfocus";
// Constants.beforepaste = "beforepaste";
// Constants.beforeprint = "beforeprint";
// Constants.beforeunload = "beforeunload";
// Constants.beforeupdate = "beforeupdate";
// Constants.blur = "blur";
// Constants.bounce = "bounce";
// Constants.cellchange = "cellchange";
// Constants.change = "change";
Constants.click = "click";
// Constants.contextmenu = "contextmenu";
// Constants.controlselect = "controlselect";
// Constants.copy = "copy";
// Constants.cut = "cut";
// Constants.dataavailable = "dataavailable";
// Constants.datasetchanged = "datasetchanged";
// Constants.datasetcomplete = "datasetcomplete";
// Constants.dblclick = "dblclick";
// Constants.deactivate = "deactivate";
// Constants.drag = "drag";
// Constants.dragend = "dragend";
// Constants.dragenter = "dragenter";
// Constants.dragleave = "dragleave";
// Constants.dragover = "dragover";
// Constants.dragstart = "dragstart";
// Constants.drop = "drop";
// Constants.error = "error";
// Constants.errorupdate = "errorupdate";
// Constants.filterchange = "filterchange";
// Constants.finish = "finish";
// Constants.focus = "focus";
// Constants.focusin = "focusin";
// Constants.focusout = "focusout";
// Constants.help = "help";
Constants.keyDown = "keyDown";
Constants.keyPress = "keyPress";
Constants.keyUp = "keyUp";
// Constants.layoutcomplete = "layoutcomplete";
// Constants.load = "load";
// Constants.losecapture = "losecapture";
Constants.mouseDown = "mouseDown";
// Constants.mouseenter = "mouseenter";
// Constants.mouseleave = "mouseleave";
Constants.mouseMove = "mouseMove";
// Constants.mouseout = "mouseout";
// Constants.mouseover = "mouseover";
Constants.mouseUp = "mouseUp";
// Constants.mousewheel = "mousewheel";
// Constants.move = "move";
// Constants.moveend = "moveend";
// Constants.movestart = "movestart";
// Constants.paste = "paste";
// Constants.propertychange = "propertychange";
// Constants.readystatechange = "readystatechange";
// Constants.reset = "reset";
// Constants.resize = "resize";
// Constants.resizeend = "resizeend";
// Constants.resizestart = "resizestart";
// Constants.rowenter = "rowenter";
// Constants.rowexit = "rowexit";
// Constants.rowsdelete = "rowsdelete";
// Constants.rowsinserted = "rowsinserted";
// Constants.scroll = "scroll";
// Constants.select = "select";
// Constants.selectionchange = "selectionchange";
// Constants.selectstart = "selectstart";
// Constants.start = "start";
// Constants.stop = "stop";
// Constants.submit = "submit";
// Constants.unload = "unload";
Constants.check = "check";
Constants.close = "close";
Constants.goBack = "goBack";
Constants.open = "open";
Constants.refresh = "refresh";
Constants.select = "select";
Constants.type = "type";
Constants.uncheck = "uncheck";
Constants.waitForPageToLoad = "waitForPageToLoad";
Constants.waitForPopUp = "waitForPopUp";
Constants.waitForPopUpClose = "waitForPopUpClose";
Constants.sleep = "sleep";
Constants.onabort = "onabort";
Constants.onactivate = "onactivate";
Constants.onafterprint = "onafterprint";
Constants.onafterupdate = "onafterupdate";
Constants.onbeforeactivate = "onbeforeactivate";
Constants.onbeforecopy = "onbeforecopy";
Constants.onbeforecut = "onbeforecut";
Constants.onbeforedeactivate = "onbeforedeactivate";
Constants.onbeforeeditfocus = "onbeforeeditfocus";
Constants.onbeforepaste = "onbeforepaste";
Constants.onbeforeprint = "onbeforeprint";
Constants.onbeforeunload = "onbeforeunload";
Constants.onbeforeupdate = "onbeforeupdate";
Constants.onblur = "onblur";
Constants.onbounce = "onbounce";
Constants.oncellchange = "oncellchange";
Constants.onchange = "onchange";
Constants.onclick = "onclick";
Constants.oncontextmenu = "oncontextmenu";
Constants.oncontrolselect = "oncontrolselect";
Constants.oncopy = "oncopy";
Constants.oncut = "oncut";
Constants.ondataavailable = "ondataavailable";
Constants.ondatasetchanged = "ondatasetchanged";
Constants.ondatasetcomplete = "ondatasetcomplete";
Constants.ondblclick = "ondblclick";
Constants.ondeactivate = "ondeactivate";
Constants.ondrag = "ondrag";
Constants.ondragend = "ondragend";
Constants.ondragenter = "ondragenter";
Constants.ondragleave = "ondragleave";
Constants.ondragover = "ondragover";
Constants.ondragstart = "ondragstart";
Constants.ondrop = "ondrop";
Constants.onerror = "onerror";
Constants.onerrorupdate = "onerrorupdate";
Constants.onfilterchange = "onfilterchange";
Constants.onfinish = "onfinish";
Constants.onfocus = "onfocus";
Constants.onfocusin = "onfocusin";
Constants.onfocusout = "onfocusout";
Constants.onhelp = "onhelp";
Constants.onkeydown = "onkeydown";
Constants.onkeypress = "onkeypress";
Constants.onkeyup = "onkeyup";
Constants.onlayoutcomplete = "onlayoutcomplete";
Constants.onload = "onload";
Constants.onlosecapture = "onlosecapture";
Constants.onmousedown = "onmousedown";
Constants.onmouseenter = "onmouseenter";
Constants.onmouseleave = "onmouseleave";
Constants.onmousemove = "onmousemove";
Constants.onmouseout = "onmouseout";
Constants.onmouseover = "onmouseover";
Constants.onmouseup = "onmouseup";
Constants.onmousewheel = "onmousewheel";
Constants.onmove = "onmove";
Constants.onmoveend = "onmoveend";
Constants.onmovestart = "onmovestart";
Constants.onpaste = "onpaste";
Constants.onpropertychange = "onpropertychange";
Constants.onreadystatechange = "onreadystatechange";
Constants.onreset = "onreset";
Constants.onresize = "onresize";
Constants.onresizeend = "onresizeend";
Constants.onresizestart = "onresizestart";
Constants.onrowenter = "onrowenter";
Constants.onrowexit = "onrowexit";
Constants.onrowsdelete = "onrowsdelete";
Constants.onrowsinserted = "onrowsinserted";
Constants.onscroll = "onscroll";
Constants.onselect = "onselect";
Constants.onselectionchange = "onselectionchange";
Constants.onselectstart = "onselectstart";
Constants.onstart = "onstart";
Constants.onstop = "onstop";
Constants.onsubmit = "onsubmit";
Constants.onunload = "onunload";
Constants.SCRIPT_REDIRECT = "script_redirect";
Constants.ONLOAD_SUBMIT = "onload_submit";
Constants.META_REDIRECT = "meta_redirect";
Constants.FRAME = "frame";
Constants.REQUEST_MODE = "requestMode";
// various states
Constants.STOPPED = "stopped";
Constants.PLAYING = "playing";
Constants.RECORDING= "recording";
Constants.PAUSED = "paused";
Constants.INIT = "init";
Constants.RERECORDING="rerecording";
// playback methods
Constants.METHOD_HTTP = "HTTP";
Constants.METHOD_DHTML= "DHTML";
Constants.SUC_NOT_FOUND = "SUC_NOT_FOUND";
Constants.FAIL_FOUND = "FAIL_FOUND";
Constants.BROWSER_ERROR = "BROWSER_ERROR";
Constants.DOM_ERROR = "DOM_ERROR";
Constants.GROUP = "GROUP";
Constants.CONTENT_TYPE_FORM_URLENCODED = "Content-Type: application/x-www-form-urlencoded";
Constants.CP_UTF8 = 65001;
Constants.NON_SENSITIVE_VALUES = "nonsensitive_values";
Constants.SENSITIVE_VALUES = "sensitive_values";
Constants.WTI_NUM_CHARS = 8;
/**
* A set of basic HTML element related utilities.
*/
/**
* @return true if the elem display css is none.
*/
function isHidden(elem)
{
return elem && elem.style ? elem.style.display == "none" : false;
}
/**
* @return true if the elem display css is not none.
*/
function isVisible(elem)
{
return elem && elem.style ? elem.style.display != "none" : false;
}
/**
* @return true if the elem is disabled.
*/
function isDisabled(elem)
{
return elem ? elem.disabled : false;
}
function isEnabled(elem)
{
return elem ? !elem.disabled : false;
}
function isReadOnly(elem)
{
return elem ? elem.readonly : false;
}
/**
* makes the elem hidden.
*/
function hide(elem)
{
if (elem && elem.style)
{
elem.style.display = "none";
}
}
/**
* makes the elem visible.
*/
function show(elem)
{
if (elem && elem.style)
{
elem.style.display = "inline";
}
}
/**
* toggles the visible state of two elems,
* so one is visible and one is invisible.
*/
function toggleVisible(elem1, elem2)
{
if (isVisible(elem2)) {
// if both elements are visible, then
// only show the first one.
show(elem1);
hide(elem2);
} else {
hide(elem1);
show(elem2);
}
}
/**
* updates the text value of a button.
*/
/*
function setValue(elem, value)
{
if (elem)
{
elem.value = value;
}
}
*/
/**
* A string buffer implements a mutable sequence of characters.
* Current implementation only allow string concatenation.
*/
function StringBuffer()
{
/**
* buffer is stored as an array list of strings.
* @private
*/
this.buffer=[];
}
/**
* Appends one or more strings to the buffer.
*/
StringBuffer.prototype.append = function()
{
for (var i =0; i < arguments.length; i++) {
this.buffer[this.buffer.length]=arguments[i];
}
}
StringBuffer.prototype.copy = function(buf)
{
if (buf.buffer) {
var buffer = buf.buffer;
for (var i =0; i < buffer.length; i++) {
this.buffer[this.buffer.length]=buffer[i];
}
} else {
throw ("Invalid input buffer object");
}
}
/**
* Returns the concatenated string.
* @tparam String delim an optional delimiter.
* @treturn String the concatenated strings.
*/
StringBuffer.prototype.toString = function(delim)
{
return this.buffer.join(delim||'');
}
/**
* Returns true if there are no strings appended.
* @treturn bool
*/
StringBuffer.prototype.isEmpty = function()
{
return this.buffer.length == 0;
}
/*
function getParent (elem)
{
if (elem && elem.parentElement)
{
return elem.parentElement;
}
else if (elem && elem.parentNodes)
{
return elem.parentNodes;
}
return null;
}
function getChildren (elem)
{
if (elem && elem.children)
{
return elem.children;
}
else if (elem && elem.childNodes)
{
return elem.childNodes;
}
return null;
}
*/
function getFunctionName (obj, func, isShort)
{
var funcName = null;
var prototype = false;
if (obj)
{
for (var v in obj.prototype)
{
if (obj.prototype[v] == func)
{
funcName = v.toString();
prototype = true;
break;
}
}
if (!prototype)
{
for (var v in obj)
{
if (obj[v] == func)
{
funcName = v.toString();
break;
}
}
}
if (funcName != null)
{
if (typeof(obj) == "function")
{
var objStr = obj.toString();
var objNameStart = objStr.indexOf(" ");
var objNameEnd = objStr.indexOf("(");
if (objNameStart != -1 && objNameEnd != -1)
{
if (isShort)
{
return funcName;
}
else
{
return objStr.substring(objNameStart + 1, objNameEnd) + ".prototype." + funcName;
}
}
}
else
{
return funcName;
}
}
}
var funcBody = func.toString();
var funcNameStart = funcBody.indexOf(" ");
var funcNameEnd = funcBody.indexOf("(");
if (funcNameStart != -1 && funcNameEnd != -1)
{
return funcBody.substring(funcNameStart + 1, funcNameEnd);
}
else
{
return func.toString();
}
}
/*
function setCookie(name, value, expiredays)
{
// Three variables are used to set the new cookie.
// The name of the cookie, the value to be stored,
// and finally the number of days until the cookie expires.
// The first lines in the function convert
// the number of days to a valid date.
var ExpireDate = new Date ();
ExpireDate.setTime(ExpireDate.getTime() + (expiredays * 24 * 3600 * 1000));
// The next line stores the cookie, simply by assigning
// the values to the "document.cookie" object.
// Note the date is converted to Greenwich Mean time using
// the "toGMTstring()" function.
document.cookie = name + "=" + escape(value) +
((expiredays == null) ? "" : "; expires=" + ExpireDate.toGMTString());
}
function getCookie(name)
{
// First we check to see if there is a cookie stored.
// Otherwise the length of document.cookie would be zero.
if (document.cookie.length > 0)
{
// Second we check to see if the cookie's name is stored in the
// "document.cookie" object for the page.
// Since more than one cookie can be set on a
// single page it is possible that our cookie
// is not present, even though the "document.cookie" object
// is not just an empty text.
// If our cookie name is not present the value -1 is stored
// in the variable called "begin".
begin = document.cookie.indexOf(name+"=");
if (begin != -1) // Note: != means "is not equal to"
{
// Our cookie was set.
// The value stored in the cookie is returned from the function.
begin += name.length+1;
end = document.cookie.indexOf(";", begin);
if (end == -1) end = document.cookie.length;
return unescape(document.cookie.substring(begin, end)); }
}
return null;
// Our cookie was not set.
// The value "null" is returned from the function.
}
function persistElem(id)
{
var elem = getElementById(id);
if (elem)
{
setCookie(id, elem.value, 100);
}
}
*/
/*
function compose()
{
var funcs = [];
for (var i = 0; i < arguments.length; i++)
{
funcs.unshift(arguments[i]);
}
return function(x)
{
var result = x;
for (var i = 0; i < funcs.length; i++)
{
var func = funcs[i];
result = func(result);
}
return result;
}
}
*/
function foreach(arr, func)
{
if (!arr) return;
if (arr.length != undefined) {
for(var i = 0; i < arr.length; i++) {
func(arr[i]);
}
} else {
for(var p in arr) {
func(p);
}
}
}
/*
function repeat(times, func)
{
for (var i = 0; i < times; i++)
{
func();
}
}
function getElementById(x)
{
var elem = document.getElementById(x);
if (!elem) {
alert(x + " is not found");
}
return elem;
}
function count(arr)
{
if (!arr) return 0;
if (arr.length != undefined) {
return arr.length;
} else {
var i = 0;
for(var p in arr) {
i++;
}
return i;
}
}
*/
/*
function and(funcs)
{
if (funcs)
{
for (var i = 0; i < funcs.length; i++)
{
if (!funcs[i]())
{
return false;
}
}
}
return true;
}
function or(funcs)
{
if (funcs)
{
for (var i = 0; i < funcs.length; i++)
{
if (funcs[i]())
{
return true;
}
}
}
return false;
}
*/
function escapeHTML(str)
{
str = str.replace(RegExp("&", "g"),'&');
str = str.replace(RegExp("<", "g"),'<');
str = str.replace(RegExp(">", "g"),'>');
return str;
}
function encodeNameValuePairDelimiters(str)
{
str = str.replace(RegExp("%", "g"), "%25");
str = str.replace(RegExp(",", "g"), "%2C");
str = str.replace(RegExp(";", "g"), "%3B");
// no need to encode '=', because in the
// midtier, we are spliting on the first =.
return str;
}
function merge1(array1, array2, func)
{
var result = [];
var i1 = 0;
var i2 = 0;
var n1 = array1.length;
var n2 = array2.length;
while (i1 < n1 && i2 < n2) {
var elem1 = array1[i1];
var elem2 = array2[i2];
var diff = func(elem1, elem2);
if (diff <= 0) {
// elem1 is smaller
result.push(elem1);
i1++;
} else {
// elem2 is smaller
result.push(elem2);
i2++;
}
}
if (i1 < n1) {
result = result.concat(array1.slice(i1));
}
if (i2 < n2) {
result = result.concat(array2.slice(i2));
}
return result;
}
function merge2(array1, array2, func)
{
result = [];
while (array1.length > 0 && array2.length > 0) {
var diff = func(array1[0], array2[0]);
if (diff <= 0) {
result.push(array1.shift());
} else {
result.push(array2.shift());
}
}
if (array1.length > 0) {
return result.concat(array1);
}
if (array2.length > 0) {
return result.concat(array2);
}
return result;
}
function handleException (module, func, e)
{
if (stderr && stderr.log) {
stderr.log(getFunctionName(module, func) + " error: name=" + e.name + "\n message=" + e.message + "\n desc=" + e.description );
}
}
function handleStackTrace (module, func, msg)
{
var delta = "";
if (timing && timing.log && timing.getIsEnabled()) {
// if timing is enabled,
if (module._timing == null) {
module._timing = {};
}
var functionName = getFunctionName(module, func);
var _lastTime = module._timing[functionName];
var _currTime = (new Date()).valueOf();
if (msg.indexOf(" start") == 0) {
module._timing[functionName] = _currTime;
} else if ( msg.indexOf(" end") == 0) {
delta = " " + (_currTime - _lastTime);
module._timing[functionName] = null;
}
}
if (stackTrace && stackTrace.log && stackTrace.getIsEnabled()) {
stackTrace.log(getFunctionName(module, func) + msg + delta);
}
}
/*
function delegate() {
var length = arguments.length;
if (length > 2) {
var src = arguments[0];
var dst = arguments[1];
for (var i = 2; i < length; i++) {
delegateFunc(src, dst, arguments[i]);
}
}
}
function delegateFunc(src, dst, name)
{
var dstFunc = dst[name];
var dstFuncLength = dstFunc.length;
src[name] = function(args) {
return dst[name](args);
};
}
*/
function require(requiredFuncs, obj)
{
if (requiredFuncs) {
for(var i = 0; i < requiredFuncs.length; i++) {
var requiredFunc = obj[requiredFuncs[i]];
if (!requiredFunc || typeof(requiredFunc) != "function") {
throw ("the object must support function " + requiredFuncs[i]);
}
}
}
}
function createDOMDocument()
{
var objDOMDocument = null;
// if (window.ActiveXObject) {
// Microsoft
objDOMDocument = getMSDOMDocument();
/* } else {
// Mozilla | Netscape | Safari
objDOMDocument = new DOMDocumentRequest();
if (objDOMDocument != null) {
objDOMDocument.onload = handler;
objDOMDocument.onerror = handler;
}
} */
return objDOMDocument;
}
function createXMLHTTP()
{
var objXmlHttp = null;
if (window && window.XMLHttpRequest) {
// IE7 or other browsers that support XMLHttpRequest
objXmlHttp = new XMLHttpRequest();
} else {
// Microsoft
objXmlHttp = getMSXmlHttp();
}
return objXmlHttp;
}
function getMSXmlHttp()
{
var xmlHttp = null;
var clsids = [
"Msxml2.XMLHTTP.6.0",
"Msxml2.XMLHTTP.5.0",
"Msxml2.XMLHTTP.4.0",
"Msxml2.XMLHTTP.3.0",
"Msxml2.XMLHTTP.2.6",
"Microsoft.XMLHTTP.1.0",
"Microsoft.XMLHTTP.1",
"Msxml2.XMLHTTP",
"Microsoft.XMLHTTP"];
for(var i=0; i
After all the other rules are applied, the final phase is a rewrite rule. The
rewrite rule does not generate any additional information, but it rearranges
all the data gathered such that the result is similar to the step structure
that we have today in a web transaction. It makes the XSLT to a web
transaction template for recording faster and easier to write.
For agent/browser playback scenarios, the rewrite rule is not required.
@see PBReport
@see AgentReport
*/
function IERecRewriter(mediator)
{
this.m_mediator = mediator;
this.m_startTime = (new Date()).getTime();
var requiredFuncs = ["getNodeWithId", "getTxnManager"];
require(requiredFuncs, this.m_mediator);
}
IERecRewriter.prototype = new IERecProcessor();
/**
* Rewrites the web transaction. Any changes are applied
* to the XML node directly.
* @tparam XMLNode node the recorded web transaction in XML format.
*/
IERecRewriter.prototype.process = function (node)
{
try {
this._stackTrace(arguments.callee, " start");
this._genFirstGoto(node);
this._detectType(node);
// this is moved to IERecCauseAnalyzer
// this._removeBadBNs(node);
this._appendNodeToParent(node);
// _addNodeProp must happen after _appendNodeToParent
// because BN nodes are relocated in _appendNodeToParent
this._addNodeProp(node);
this._removeRedundantNode(node);
this._genRequiredProp(node);
this._genActions(node);
this._stackTrace(arguments.callee, " end");
} catch (e) { this._handleException(arguments.callee, e); }
}
IERecRewriter.prototype._genFirstGoto = function(node)
{
try {
// remove the very first node that waits for browser open
// replace
* @public
*/
function createStepName(str, replace)
{
if (str)
{
return Util.trim(str.replace(/\|+/g, replace || ""));
}
return str;
}
/**
* Returns a legal name-value property name based on the specified string. Any
* illegal name-value property name charactes are removed and replaced with the
* specified replacement string. If no replacement string is specified, "" is
* used by default.
*
* @tparam String str string with character to be replaced
* @tparam String replace replacement string [optional]
*
* @treturn String str
with illegal name-value property name
* characters replaced with replace
* @public
*/
function createNameValuePropertyName(str, replace)
{
if (str)
{
// Replaces illegal NVP chars, which include whitespace and the
// characters %,;={}[]<>
return str.replace(/[%,;={}\[\]<>\s]+/g, replace || "");
}
return str;
}
/**
* @class CharRefs
* A collection of character reference constants and lookup tables.
*
* @ctor CharRefs
* @private
*/
function CharRefs()
{
}
/**
* A mapping of character entity references (for ASCII characters) to character
* codes.
* Source: http://www.w3.org/TR/html401/sgml/entities.html
*
* NOTE: Only this subset of references is currently supported by the agent.
* @private
*/
CharRefs.ASCII_CHAR_ENTITY_REFS
= { "quot" : "\u0022",
"amp" : "\u0026",
"lt" : "\u003C",
"gt" : "\u003E",
"nbsp" : "\u00A0",
"iexcl" : "\u00A1",
"cent" : "\u00A2",
"pound" : "\u00A3",
"curren" : "\u00A4",
"yen" : "\u00A5",
"brvbar" : "\u00A6",
"sect" : "\u00A7",
"uml" : "\u00A8",
"copy" : "\u00A9",
"ordf" : "\u00AA",
"laquo" : "\u00AB",
"not" : "\u00AC",
"shy" : "\u00AD",
"reg" : "\u00AE",
"macr" : "\u00AF",
"deg" : "\u00B0",
"plusmn" : "\u00B1",
"sup2" : "\u00B2",
"sup3" : "\u00B3",
"acute" : "\u00B4",
"micro" : "\u00B5",
"para" : "\u00B6",
"middot" : "\u00B7",
"cedil" : "\u00B8",
"sup1" : "\u00B9",
"ordm" : "\u00BA",
"raquo" : "\u00BB",
"frac14" : "\u00BC",
"frac12" : "\u00BD",
"frac34" : "\u00BE",
"iquest" : "\u00BF",
"Agrave" : "\u00C0",
"Aacute" : "\u00C1",
"Acirc" : "\u00C2",
"Atilde" : "\u00C3",
"Auml" : "\u00C4",
"Aring" : "\u00C5",
"AElig" : "\u00C6",
"Ccedil" : "\u00C7",
"Egrave" : "\u00C8",
"Eacute" : "\u00C9",
"Ecirc" : "\u00CA",
"Euml" : "\u00CB",
"Igrave" : "\u00CC",
"Iacute" : "\u00CD",
"Icirc" : "\u00CE",
"Iuml" : "\u00CF",
"ETH" : "\u00D0",
"Ntilde" : "\u00D1",
"Ograve" : "\u00D2",
"Oacute" : "\u00D3",
"Ocirc" : "\u00D4",
"Otilde" : "\u00D5",
"Ouml" : "\u00D6",
"times" : "\u00D7",
"Oslash" : "\u00D8",
"Ugrave" : "\u00D9",
"Uacute" : "\u00DA",
"Ucirc" : "\u00DB",
"Uuml" : "\u00DC",
"Yacute" : "\u00DD",
"THORN" : "\u00DE",
"szlig" : "\u00DF",
"agrave" : "\u00E0",
"aacute" : "\u00E1",
"acirc" : "\u00E2",
"atilde" : "\u00E3",
"auml" : "\u00E4",
"aring" : "\u00E5",
"aelig" : "\u00E6",
"ccedil" : "\u00E7",
"egrave" : "\u00E8",
"eacute" : "\u00E9",
"ecirc" : "\u00EA",
"euml" : "\u00EB",
"igrave" : "\u00EC",
"iacute" : "\u00ED",
"icirc" : "\u00EE",
"iuml" : "\u00EF",
"eth" : "\u00F0",
"ntilde" : "\u00F1",
"ograve" : "\u00F2",
"oacute" : "\u00F3",
"ocirc" : "\u00F4",
"otilde" : "\u00F5",
"ouml" : "\u00F6",
"divide" : "\u00F7",
"oslash" : "\u00F8",
"ugrave" : "\u00F9",
"uacute" : "\u00FA",
"ucirc" : "\u00FB",
"uuml" : "\u00FC",
"yacute" : "\u00FD",
"thorn" : "\u00FE",
"yuml" : "\u00FF" };
/**
* An array of ASCII characters (indexed by character code).
*/
CharRefs.ASCII_NUMERIC_CHARS
= [ "\u0000",
"\u0001",
"\u0002",
"\u0003",
"\u0004",
"\u0005",
"\u0006",
"\u0007",
"\u0008",
"\u0009",
"\u000a",
"\u000b",
"\u000c",
"\u000d",
"\u000e",
"\u000f",
"\u0010",
"\u0011",
"\u0012",
"\u0013",
"\u0014",
"\u0015",
"\u0016",
"\u0017",
"\u0018",
"\u0019",
"\u001a",
"\u001b",
"\u001c",
"\u001d",
"\u001e",
"\u001f",
"\u0020",
"\u0021",
"\u0022",
"\u0023",
"\u0024",
"\u0025",
"\u0026",
"\u0027",
"\u0028",
"\u0029",
"\u002a",
"\u002b",
"\u002c",
"\u002d",
"\u002e",
"\u002f",
"\u0030",
"\u0031",
"\u0032",
"\u0033",
"\u0034",
"\u0035",
"\u0036",
"\u0037",
"\u0038",
"\u0039",
"\u003a",
"\u003b",
"\u003c",
"\u003d",
"\u003e",
"\u003f",
"\u0040",
"\u0041",
"\u0042",
"\u0043",
"\u0044",
"\u0045",
"\u0046",
"\u0047",
"\u0048",
"\u0049",
"\u004a",
"\u004b",
"\u004c",
"\u004d",
"\u004e",
"\u004f",
"\u0050",
"\u0051",
"\u0052",
"\u0053",
"\u0054",
"\u0055",
"\u0056",
"\u0057",
"\u0058",
"\u0059",
"\u005a",
"\u005b",
"\u005c",
"\u005d",
"\u005e",
"\u005f",
"\u0060",
"\u0061",
"\u0062",
"\u0063",
"\u0064",
"\u0065",
"\u0066",
"\u0067",
"\u0068",
"\u0069",
"\u006a",
"\u006b",
"\u006c",
"\u006d",
"\u006e",
"\u006f",
"\u0070",
"\u0071",
"\u0072",
"\u0073",
"\u0074",
"\u0075",
"\u0076",
"\u0077",
"\u0078",
"\u0079",
"\u007a",
"\u007b",
"\u007c",
"\u007d",
"\u007e",
"\u007f",
"\u0080",
"\u0081",
"\u0082",
"\u0083",
"\u0084",
"\u0085",
"\u0086",
"\u0087",
"\u0088",
"\u0089",
"\u008a",
"\u008b",
"\u008c",
"\u008d",
"\u008e",
"\u008f",
"\u0090",
"\u0091",
"\u0092",
"\u0093",
"\u0094",
"\u0095",
"\u0096",
"\u0097",
"\u0098",
"\u0099",
"\u009a",
"\u009b",
"\u009c",
"\u009d",
"\u009e",
"\u009f",
"\u00a0",
"\u00a1",
"\u00a2",
"\u00a3",
"\u00a4",
"\u00a5",
"\u00a6",
"\u00a7",
"\u00a8",
"\u00a9",
"\u00aa",
"\u00ab",
"\u00ac",
"\u00ad",
"\u00ae",
"\u00af",
"\u00b0",
"\u00b1",
"\u00b2",
"\u00b3",
"\u00b4",
"\u00b5",
"\u00b6",
"\u00b7",
"\u00b8",
"\u00b9",
"\u00ba",
"\u00bb",
"\u00bc",
"\u00bd",
"\u00be",
"\u00bf",
"\u00c0",
"\u00c1",
"\u00c2",
"\u00c3",
"\u00c4",
"\u00c5",
"\u00c6",
"\u00c7",
"\u00c8",
"\u00c9",
"\u00ca",
"\u00cb",
"\u00cc",
"\u00cd",
"\u00ce",
"\u00cf",
"\u00d0",
"\u00d1",
"\u00d2",
"\u00d3",
"\u00d4",
"\u00d5",
"\u00d6",
"\u00d7",
"\u00d8",
"\u00d9",
"\u00da",
"\u00db",
"\u00dc",
"\u00dd",
"\u00de",
"\u00df",
"\u00e0",
"\u00e1",
"\u00e2",
"\u00e3",
"\u00e4",
"\u00e5",
"\u00e6",
"\u00e7",
"\u00e8",
"\u00e9",
"\u00ea",
"\u00eb",
"\u00ec",
"\u00ed",
"\u00ee",
"\u00ef",
"\u00f0",
"\u00f1",
"\u00f2",
"\u00f3",
"\u00f4",
"\u00f5",
"\u00f6",
"\u00f7",
"\u00f8",
"\u00f9",
"\u00fa",
"\u00fb",
"\u00fc",
"\u00fd",
"\u00fe",
"\u00ff" ];
/**
* A mapping of character entity references to character codes.
* Source: http://www.w3.org/TR/html401/sgml/entities.html
* @private
*/
CharRefs.ALL_CHAR_ENITITY_REFS
= { "nbsp" : "\u00A0",
"iexcl" : "\u00A1",
"cent" : "\u00A2",
"pound" : "\u00A3",
"curren" : "\u00A4",
"yen" : "\u00A5",
"brvbar" : "\u00A6",
"sect" : "\u00A7",
"uml" : "\u00A8",
"copy" : "\u00A9",
"ordf" : "\u00AA",
"laquo" : "\u00AB",
"not" : "\u00AC",
"shy" : "\u00AD",
"reg" : "\u00AE",
"macr" : "\u00AF",
"deg" : "\u00B0",
"plusmn" : "\u00B1",
"sup2" : "\u00B2",
"sup3" : "\u00B3",
"acute" : "\u00B4",
"micro" : "\u00B5",
"para" : "\u00B6",
"middot" : "\u00B7",
"cedil" : "\u00B8",
"sup1" : "\u00B9",
"ordm" : "\u00BA",
"raquo" : "\u00BB",
"frac14" : "\u00BC",
"frac12" : "\u00BD",
"frac34" : "\u00BE",
"iquest" : "\u00BF",
"Agrave" : "\u00C0",
"Aacute" : "\u00C1",
"Acirc" : "\u00C2",
"Atilde" : "\u00C3",
"Auml" : "\u00C4",
"Aring" : "\u00C5",
"AElig" : "\u00C6",
"Ccedil" : "\u00C7",
"Egrave" : "\u00C8",
"Eacute" : "\u00C9",
"Ecirc" : "\u00CA",
"Euml" : "\u00CB",
"Igrave" : "\u00CC",
"Iacute" : "\u00CD",
"Icirc" : "\u00CE",
"Iuml" : "\u00CF",
"ETH" : "\u00D0",
"Ntilde" : "\u00D1",
"Ograve" : "\u00D2",
"Oacute" : "\u00D3",
"Ocirc" : "\u00D4",
"Otilde" : "\u00D5",
"Ouml" : "\u00D6",
"times" : "\u00D7",
"Oslash" : "\u00D8",
"Ugrave" : "\u00D9",
"Uacute" : "\u00DA",
"Ucirc" : "\u00DB",
"Uuml" : "\u00DC",
"Yacute" : "\u00DD",
"THORN" : "\u00DE",
"szlig" : "\u00DF",
"agrave" : "\u00E0",
"aacute" : "\u00E1",
"acirc" : "\u00E2",
"atilde" : "\u00E3",
"auml" : "\u00E4",
"aring" : "\u00E5",
"aelig" : "\u00E6",
"ccedil" : "\u00E7",
"egrave" : "\u00E8",
"eacute" : "\u00E9",
"ecirc" : "\u00EA",
"euml" : "\u00EB",
"igrave" : "\u00EC",
"iacute" : "\u00ED",
"icirc" : "\u00EE",
"iuml" : "\u00EF",
"eth" : "\u00F0",
"ntilde" : "\u00F1",
"ograve" : "\u00F2",
"oacute" : "\u00F3",
"ocirc" : "\u00F4",
"otilde" : "\u00F5",
"ouml" : "\u00F6",
"divide" : "\u00F7",
"oslash" : "\u00F8",
"ugrave" : "\u00F9",
"uacute" : "\u00FA",
"ucirc" : "\u00FB",
"uuml" : "\u00FC",
"yacute" : "\u00FD",
"thorn" : "\u00FE",
"yuml" : "\u00FF",
"quot" : "\u0022",
"amp" : "\u0026",
"lt" : "\u003C",
"gt" : "\u003E",
"OElig" : "\u0152",
"oelig" : "\u0153",
"Scaron" : "\u0160",
"scaron" : "\u0161",
"Yuml" : "\u0178",
"circ" : "\u02C6",
"tilde" : "\u02DC",
"ensp" : "\u2002",
"emsp" : "\u2003",
"thinsp" : "\u2009",
"zwnj" : "\u200C",
"zwj" : "\u200D",
"lrm" : "\u200E",
"rlm" : "\u200F",
"ndash" : "\u2013",
"mdash" : "\u2014",
"lsquo" : "\u2018",
"rsquo" : "\u2019",
"sbquo" : "\u201A",
"ldquo" : "\u201C",
"rdquo" : "\u201D",
"bdquo" : "\u201E",
"dagger" : "\u2020",
"Dagger" : "\u2021",
"permil" : "\u2030",
"lsaquo" : "\u2039",
"rsaquo" : "\u203A",
"euro" : "\u20AC",
"fnof" : "\u0192",
"Alpha" : "\u0391",
"Beta" : "\u0392",
"Gamma" : "\u0393",
"Delta" : "\u0394",
"Epsilon" : "\u0395",
"Zeta" : "\u0396",
"Eta" : "\u0397",
"Theta" : "\u0398",
"Iota" : "\u0399",
"Kappa" : "\u039A",
"Lambda" : "\u039B",
"Mu" : "\u039C",
"Nu" : "\u039D",
"Xi" : "\u039E",
"Omicron" : "\u039F",
"Pi" : "\u03A0",
"Rho" : "\u03A1",
"Sigma" : "\u03A3",
"Tau" : "\u03A4",
"Upsilon" : "\u03A5",
"Phi" : "\u03A6",
"Chi" : "\u03A7",
"Psi" : "\u03A8",
"Omega" : "\u03A9",
"alpha" : "\u03B1",
"beta" : "\u03B2",
"gamma" : "\u03B3",
"delta" : "\u03B4",
"epsilon" : "\u03B5",
"zeta" : "\u03B6",
"eta" : "\u03B7",
"theta" : "\u03B8",
"iota" : "\u03B9",
"kappa" : "\u03BA",
"lambda" : "\u03BB",
"mu" : "\u03BC",
"nu" : "\u03BD",
"xi" : "\u03BE",
"omicron" : "\u03BF",
"pi" : "\u03C0",
"rho" : "\u03C1",
"sigmaf" : "\u03C2",
"sigma" : "\u03C3",
"tau" : "\u03C4",
"upsilon" : "\u03C5",
"phi" : "\u03C6",
"chi" : "\u03C7",
"psi" : "\u03C8",
"omega" : "\u03C9",
"thetasym" : "\u03D1",
"upsih" : "\u03D2",
"piv" : "\u03D6",
"bull" : "\u2022",
"hellip" : "\u2026",
"prime" : "\u2032",
"Prime" : "\u2033",
"oline" : "\u203E",
"frasl" : "\u2044",
"weierp" : "\u2118",
"image" : "\u2111",
"real" : "\u211C",
"trade" : "\u2122",
"alefsym" : "\u2135",
"larr" : "\u2190",
"rarr" : "\u2192",
"darr" : "\u2193",
"harr" : "\u2194",
"crarr" : "\u21B5",
"lArr" : "\u21D0",
"uArr" : "\u21D1",
"rArr" : "\u21D2",
"dArr" : "\u21D3",
"hArr" : "\u21D4",
"forall" : "\u2200",
"part" : "\u2202",
"exist" : "\u2203",
"empty" : "\u2205",
"nabla" : "\u2207",
"isin" : "\u2208",
"notin" : "\u2209",
"ni" : "\u220B",
"prod" : "\u220F",
"sum" : "\u2211",
"minus" : "\u2212",
"lowast" : "\u2217",
"radic" : "\u221A",
"prop" : "\u221D",
"infin" : "\u221E",
"ang" : "\u2220",
"and" : "\u2227",
"or" : "\u2228",
"cap" : "\u2229",
"cup" : "\u222A",
"int" : "\u222B",
"there4" : "\u2234",
"sim" : "\u223C",
"cong" : "\u2245",
"asymp" : "\u2248",
"ne" : "\u2260",
"equiv" : "\u2261",
"le" : "\u2264",
"ge" : "\u2265",
"sub" : "\u2282",
"sup" : "\u2283",
"nsub" : "\u2284",
"sube" : "\u2286",
"supe" : "\u2287",
"oplus" : "\u2295",
"otimes" : "\u2297",
"perp" : "\u22A5",
"sdot" : "\u22C5",
"lceil" : "\u2308",
"rceil" : "\u2309",
"lfloor" : "\u230A",
"rfloor" : "\u230B",
"lang" : "\u2329",
"rang" : "\u232A",
"loz" : "\u25CA",
"spades" : "\u2660",
"clubs" : "\u2663",
"hearts" : "\u2665",
"diams" : "\u2666" };
/**
* A Logging framework.
* @ctor Logger.
*
* Create a logger.
*/
function Logger(obj, className)
{
this.m_debug = obj;
this.m_className = className;
this.m_enabled = false;
}
Logger.prototype.log = function(str, messages, doEscape)
{
if (this.m_enabled) {
if (typeof(messages) == "boolean") {
this.m_debug.log(str, null, this.m_className, true);
} else {
this.m_debug.log(str, messages, this.m_className, doEscape);
}
}
}
Logger.prototype.logNode = function(node)
{
if (this.m_enabled) {
this.m_debug.logNode(node, this.m_className);
}
}
Logger.prototype.enable = function()
{
this.m_enabled = true;
}
Logger.prototype.disable = function()
{
this.m_enabled = false;
}
Logger.prototype.toggleEnableDisable = function()
{
this.m_enabled = !this.m_enabled;
}
Logger.prototype.getIsEnabled = function()
{
return this.m_enabled;
}
/**
* @class OutputStream.
* A base class for an output data stream.
*/
function OutputStream()
{
/**
* disabled state
* @private
*/
this.m_disabled = false;
}
/**
* Disables the stream.
*/
OutputStream.prototype.disable = function()
{
this.m_disabled = true;
}
/**
* Enables the stream.
*/
OutputStream.prototype.enable = function()
{
this.m_disabled = false;
}
/**
* Gets the enable state.
* @treturn bool true if enabled, false if disabled.
*/
OutputStream.prototype.isEnabled = function()
{
return !this.m_disabled;
}
/**
* An output data stream encapsulated by a file. When data is written
* to this object, the data appears in the corresponding file.
*
* @ctor OutputStreamFile.
* Creates an output stream to a file.
* @tparam String fileName the file name.
* @treturn OutputStreamFile an output stream.
*/
function OutputStreamFile(fileName)
{
this.open(fileName);
}
OutputStreamFile.prototype = new OutputStream();
/**
* Opens the file for writing the data stream.
* @tparam String fileName the file name.
*/
OutputStreamFile.prototype.open = function (fileName)
{
try { // keep this
// this.close();
if (fileName) {
var fso = new ActiveXObject("Scripting.FileSystemObject");
var forWriting = 2;
var create = true;
this.m_fileHandle = fso.OpenTextFile(fileName, forWriting, create);
this.m_opened = true;
} else {
this.m_fileHandle = null;
this.m_opened = false;
}
} catch (e) { // keep this
// purposely ignore this
}
}
/**
* Appends a string and optional messages to the data stream.
* @tparam String str the top level string.
* @tparam Object[] messages the detail messages.
* @tparam String className not used.
* @tparam bool doEscape FIXME not implemented yet.
*/
OutputStreamFile.prototype.print = function (str, messages, className, doEscape)
{
try { // keep this
if (this.isEnabled()) {
if (str && this.m_fileHandle)
{
this.m_fileHandle.WriteLine("- " + str);
}
if (messages && this.m_fileHandle)
{
if (typeof (messages) == "string")
{
this.m_fileHandle.WriteLine(" " + messages);
}
else
{
for (var i = 0; i < messages.length; i++)
{
var s = messages[i];
if (typeof (s) == "string")
{
this.m_fileHandle.WriteLine(" " + s);
}
}
}
this.m_fileHandle.WriteLine("");
}
}
} catch (e) { // keep this
// purposely ignore this
}
}
/**
* Apends a XML node to the output stream.
* @tparam XMLNode node
* @tparam String className not used
* @tparam int indent number of spaces.
*/
OutputStreamFile.prototype.printNode = function(node, className, indent)
{
try { // keep this
if (this.isEnabled()) {
if (this.m_fileHandle) {
if (!indent) {
indent = 0;
}
var text = new StringBuffer();
if (node.nodeType == 1 /* NODE_ELEMENT */) {
if (indent > 0) {
text.append(new Array(indent+1).join(" "));
}
text.append("- ");
text.append(node.tagName);
var attrs = node.attributes;
if (attrs) {
text.append(" ");
for (var i= 0; i < attrs.length; i++) {
if (attrs.item(i).nodeValue != "") {
text.append(attrs.item(i).nodeName, "=\"", attrs.item(i).nodeValue, "\"", " ");
}
}
}
} else if (node.nodeType == 4) {
// FIXME handle nodeType == 4
}
var outputText = text.toString();
this.m_fileHandle.WriteLine(outputText);
var nodes = node.childNodes;
if (nodes) {
for (var i = 0; i < nodes.length; i++) {
this.printNode(nodes.item(i), className, indent + 2);
}
}
}
}
} catch (e) { // keep this
// purposely ignore this
}
}
/**
* Closes the file and the data stream.
*/
OutputStreamFile.prototype.close = function ()
{
try { // keep this
if (this.m_fileHandle && this.m_opened) {
this.m_opened = false;
this.m_fileHandle.Close();
delete this.m_fileHandle;
this.m_fileHandle = null;
}
} catch (e) { // keep this
// purposely ignore this
}
}
/**
* An output data stream encapsulated by a XML HTTP object. When data is written
* to this object, the data is posted to the corresponding URL.
*
* @ctor OutputStreamHTTP.
* Creates an output stream to a remote URL.
* @tparam String url the URL.
* @treturn OutputStreamHTTP an OutputStreamHTTP object.
*/
function OutputStreamHTTP(url)
{
/**
* the URL to post the data to.
* @private
*/
this.m_url = url;
this.m_text_data = null;
}
OutputStreamHTTP.prototype = new OutputStream();
OutputStreamHTTP.prototype.open = function ()
{
this.m_text_data = [];
}
OutputStreamHTTP.prototype.close = function()
{
try { // keep this
if (this.m_text_data && this.m_text_data.length > 0) {
var xmlHttpReq = createXMLHTTP();
var that = this;
// send asynchronously
xmlHttpReq.open("POST", this.m_url, true);
var xmlDoc = createDOMDocument();
if (xmlDoc) {
xmlDoc.documentElement = xmlDoc.createElement("data");
var cdata = xmlDoc.createCDATASection(this.m_text_data.join("\n"));
xmlDoc.documentElement.appendChild(cdata);
xmlHttpReq.send(xmlDoc.xml);
}
this.m_text_data = null;
}
} catch (ex) { // keep this
// purposely ignore this
}
}
/**
* Specifies the callback function.
* @tparam Function func the callback function.
* @tparam Object obj the context object, if not null,
* the function will be called with obj as this.
*/
OutputStreamHTTP.prototype.setCallbackFunc = function (func, obj)
{
this.m_func = func;
this.m_obj = obj;
}
/**
* Sends a XML node to the remote server.
* @tparam IXMLElement node
*/
OutputStreamHTTP.prototype.printNode = function(node)
{
try { // keep this
var xmlHttpReq = createXMLHTTP();
var that = this;
xmlHttpReq.onreadystatechange = function() {
if (xmlHttpReq.readyState == 4) {
if (that.m_func) {
if (that.m_obj) {
that.m_func.call(that.m_obj, xmlHttpReq);
} else {
that.m_func(xmlHttpReq);
}
}
}
}
xmlHttpReq.open("POST", this.m_url, true);
xmlHttpReq.send(node.xml);
} catch (e) { // keep this
// purposely ignore this
}
}
OutputStreamHTTP.prototype.print = function (str, messages, className, doEscape)
{
try { // keep this
if (this.isEnabled()) {
if (str && this.m_text_data) {
this.m_text_data.push("- " + str);
}
if (messages && this.m_text_data) {
if (typeof (messages) == "string") {
this.m_text_data.push(" " + messages);
} else {
for (var i = 0; i < messages.length; i++) {
var s = messages[i];
if (typeof (s) == "string") {
this.m_text_data.push(" " + s);
}
}
}
}
}
} catch (e) { // keep this
// purposely ignore this
}
}
/**
* @class OutputStreamUI.
* An output data stream encapsulated by a HTML element.
* When data is written to this object, the data appears in the corresponding HTML element.
*
* @ctor OutputStreamUI.
* Creates an output stream to a HTML element.
* @tparam HTMLElement elem the HTML element.
* @treturn OutputStreamUI an output stream.
*/
function OutputStreamUI(elem)
{
/**
* The expand state.
* @private
*/
this.m_expand = false;
if (elem) {
/**
* The HTML element.
* @private
*/
this.m_container = elem;
/**
* The HTML element's tag name.
* @private
*/
this.m_tagName = elem.tagName.toLowerCase();
}
}
OutputStreamUI.prototype = new OutputStream();
/**
* Appends a XML node to the output stream.
* @tparam XMLNode node the XML node.
* @tparam String className the css class name used to render the strings.
* @tparam HTMLElement parentElem the HTML element where the content should be added to.
*/
OutputStreamUI.prototype.printNode = function(node, className, parentElem)
{
try { // keep this
if (this.m_container && this.isEnabled()) {
if (this.m_tagName == "textarea") {
// FIXME handle OutputStreamUI.printNode to textarea
} else {
var doc = this.m_container.document ? this.m_container.document : this.m_container.ownerDocument;
if (node) {
// the parent.
var d1 = doc.createElement("div");
// + or -
var icon = doc.createElement("span");
// head line string
var s1 = doc.createElement("span");
// contain i + s1
var p1 = doc.createElement("p");
// contain messages
var div2 = doc.createElement("div");
if (!this.m_expand) {
icon.innerHTML = "+";
} else {
icon.innerHTML = "-";
}
icon.className = "tree";
icon.onclick = function() {
var m = this.parentNode.parentNode.childNodes[1];
if (m) {
m.style.display = (m.style.display == "none" ) ? "" : "none";
this.innerHTML = (this.innerHTML == "-") ? "+" : "-";
}
}
var headLine = new StringBuffer();
if (node.nodeType == 1 /* NODE_ELEMENT */) {
headLine.append(node.tagName);
var attrs = node.attributes;
if (attrs != null) {
headLine.append(" ");
for (var i = 0; i < attrs.length; i++) {
if (attrs.item(i).nodeValue != "") {
headLine.append(escapeHTML(attrs.item(i).nodeName));
headLine.append("=");
headLine.append("\"");
headLine.append(escapeHTML(attrs.item(i).nodeValue));
headLine.append("\"");
headLine.append(" ");
}
}
}
s1.innerHTML = headLine.toString();
} else if (node.nodeType == 4 /* NODE_CDATA_SECTION */) {
/*var textNode = doc.createTextNode(escapeHTML(node.data));
s1.appendChild(textNode);*/
}
p1.appendChild(icon);
p1.appendChild(s1);
p1.className = "message";
var nodes = node.childNodes;
if (nodes != null) {
for (var i = 0; i < nodes.length; i++) {
this.printNode(nodes.item(i), className, div2);
}
}
div2.className = "desc";
if (!this.m_expand)
{
div2.style.display = "none";
}
d1.appendChild(p1);
d1.appendChild(div2);
if (parentElem)
{
parentElem.appendChild(d1);
}
else
{
this.m_container.appendChild(d1);
}
if (className)
{
d1.className = className;
}
d1.scrollIntoView();
}
}
}
} catch (e) { // keep this
// purposely ignore this
}
}
/**
* Appends a string and optional messages to the HTML element.
* @tparam String str the top level string.
* @tparam Object[] messages the detail messages.
* @tparam String className the css class name used to render the strings.
* @tparam bool doEscape escapes the data so the source is displayed.
*/
OutputStreamUI.prototype.print = function(str, messages, className, doEscape)
{
try { // keep this
if (this.m_container && this.isEnabled()) {
if (this.m_tagName == "textarea") {
if (str && messages) {
this.m_container.value += str + "\n " + messages + "\n";
} else if (str) {
this.m_container.value += str + "\n";
}
} else {
var doc = this.m_container.document ? this.m_container.document : this.m_container.ownerDocument;
if (str && messages) {
var p1 = doc.createElement("p");
var icon = doc.createElement("span");
var s1 = doc.createElement("span");
var div2 = doc.createElement("div");
var d1 = doc.createElement("div");
if (!this.m_expand) {
icon.innerHTML = "+";
} else {
icon.innerHTML = "-";
}
icon.className = "tree";
icon.onclick = function() {
var m = this.parentNode.parentNode.childNodes[1];
if (m) {
m.style.display = (m.style.display == "none" ) ? "" : "none";
this.innerHTML = (this.innerHTML == "-") ? "+" : "-";
}
}
s1.innerHTML = doEscape ? escapeHTML(str) : str;
p1.appendChild(icon);
p1.appendChild(s1);
p1.className = "message";
if (typeof (messages) == "string") {
div2.innerHTML = doEscape ? escapeHTML(messages) : messages;
} else {
for (var i = 0; i < messages.length; i++) {
var message = messages[i]
var s = doEscape ? escapeHTML(message) : message;
if (typeof (s) == "string") {
var p3 = doc.createElement("p");
p3.innerHTML = s;
div2.appendChild(p3);
} else {
div2.appendChild(s);
}
}
}
div2.className = "desc";
if (!this.m_expand) {
div2.style.display = "none";
}
d1.appendChild(p1);
d1.appendChild(div2);
this.m_container.appendChild(d1);
if (className) {
d1.className = className;
}
d1.scrollIntoView();
} else if (str) {
var e = doc.createElement("div");
e.className = "message";
e.innerHTML = doEscape ? escapeHTML(str) : str;
this.m_container.appendChild(e);
if (className) {
e.className = className;
}
e.scrollIntoView();
}
}
}
} catch (e) { // keep this
// purposely ignore this
}
}
/**
* Clears the content of the HTML elment.
*/
OutputStreamUI.prototype.clear = function()
{
try { // keep this
if (this.m_container)
{
if (this.m_tagName == "textarea")
{
this.m_container.value = "";
}
else
{
this.m_container.innerHTML = "";
}
}
} catch (e) { // keep this
// purposely ignore this
}
}
/**
* Disables the output stream and the HTML element.
*/
OutputStreamUI.prototype.disable = function()
{
OutputStream.disable.call(this);
if (this.m_container)
{
this.m_container.disabled = true;
}
}
/**
* Enables the output stream and the HTML element.
*/
OutputStreamUI.prototype.enable = function()
{
OutputStream.enable.call(this);
if (this.m_container)
{
this.m_container.disabled = false;
}
}
/**
* Sets the expand state.
* if expand is true, all new messages added will be hidden by default, (only the top level string will be visible).
*
* @tparam bool value true or false
*/
OutputStreamUI.prototype.setExpand = function(value)
{
this.m_expand = value;
}
/**
* Gets the expand state.
* @treturn bool
*/
OutputStreamUI.prototype.getExpand = function()
{
return this.m_expand;
}
/**
* An output data stream encapsulated by a XML file. When data is written
* to this object, the data appears in the corresponding file.
*
* @ctor OutputStreamXMLFile.
* Creates an output stream to a file.
* @tparam String fileName the file name.
* @treturn OutputStreamXMLFile an output stream.
*/
function OutputStreamXMLFile(fileName)
{
this.open(fileName);
}
OutputStreamXMLFile.prototype = new OutputStream();
/**
* Opens the file for writing the data stream.
* @tparam String fileName the file name.
*/
OutputStreamXMLFile.prototype.open = function (fileName)
{
try { // keep this
// this.close();
if (fileName) {
var fso = new ActiveXObject("Scripting.FileSystemObject");
var forWriting = 2;
var create = true;
this.m_fileHandle = fso.OpenTextFile(fileName, forWriting, create);
this.m_opened = true;
} else {
this.m_fileHandle = null;
this.m_opened = false;
}
} catch (e) { // keep this
// purposely ignore this
}
}
/**
* Write a XML node to the output stream.
* @tparam XMLNode node
*/
OutputStreamXMLFile.prototype.printNode = function(node)
{
try { // keep this
if (this.isEnabled()) {
if (this.m_fileHandle) {
var xml = node.xml.replace(/>\n<");
this.m_fileHandle.WriteLine(xml);
}
}
} catch (e) { // keep this
// purposely ignore this
}
}
/**
* Closes the file and the data stream.
*/
OutputStreamXMLFile.prototype.close = function ()
{
try { // keep this
if (this.m_fileHandle && this.m_opened) {
this.m_opened = false;
this.m_fileHandle.Close();
delete this.m_fileHandle;
this.m_fileHandle = null;
}
} catch (e) { // keep this
// purposely ignore this
}
}
/**
* @class InputStream.
* A base class for an input data stream.
*/
function InputStream()
{
}
/**
* Reads data from the stream.
* @treturn String[] each string correspond to a single line
* from the data stream.
*/
InputStream.prototype.read = function ()
{
return null;
}
/**
* @class InputStreamFile.
* An input data stream encapsulated by a file. The content of the file
* can be read using this object. This is supported in Internet Explorer only.
*
* Note: This reads files from user's hard drive, and user prompt is required.
*
* @ctor InputStreamFile.
* Creates an input stream from a file.
* @tparam String fileName the file name.
* @treturn InputStreamFile an input stream.
*/
function InputStreamFile(fileName)
{
try { // keep this
/**
* data array.
* @private
*/
this.m_data = [];
// constructor
// if (window.ActiveXObject) {
var fso = new ActiveXObject("Scripting.FileSystemObject");
var forReading = 1;
var create = false;
var fileHandle = fso.OpenTextFile(fileName, forReading, create);
this.m_data = fileHandle.ReadAll();
fileHandle.Close();
delete fso;
delete fileHandle;
/* } else {
// not supported in other browsers set.
this.m_data = [];
} */
} catch (e) { // keep this
// purposely ignore this
}
}
InputStreamFile.prototype = new InputStream();
/**
* Reads data from the stream.
* @treturn String[] A string array where each string correspond to a single line from the file.
*/
InputStreamFile.prototype.read = function()
{
return this.m_data;
}
/**
* @class InputStreamHTTP.
* An input data stream encapsulated by a HTTP url.
*
* @ctor InputStreamHTTP.
* Creates an input stream from a HTTP url.
* @tparam String url
* @treturn InputStreamHTTP an input stream object.
*/
function InputStreamHTTP(url)
{
/**
* URL.
* @private
*/
this.m_url = url;
}
InputStreamHTTP.prototype = new InputStream();
InputStreamHTTP.prototype.open = function()
{
try { // keep this
if (this.m_url) {
this.m_xmlHttpReq = createXMLHTTP();
var that = this;
if (this.m_xmlHttpReq) {
this.m_xmlHttpReq.onreadystatechange = function() {
if (that.m_xmlHttpReq.readyState == 4) {
if (that.m_func) {
if (that.m_obj) {
that.m_func.call(that.m_obj, that.m_xmlHttpReq);
} else {
that.m_func(that.m_xmlHttpReq);
}
} else {
stderr.log("InputStreamHTTP.prototype.open that.m_func is not defined");
}
}
}
this.m_xmlHttpReq.open("GET", this.m_url, true);
this.m_xmlHttpReq.send("");
}
} else {
stderr.log('InputStreamHTTP.m_url is null');
}
} catch (e) { // keep this
// purposely ignore this
}
}
/**
* Reads data from the input stream.
* @treturn String
*/
InputStreamHTTP.prototype.read = function()
{
if (this.m_xmlHttpReq) {
return this.m_xmlHttpReq.responseText;
} else {
return "";
}
}
/**
* Specifies the callback function.
* @tparam Function func the callback function.
* @tparam Object obj the context object, if not null,
* the function will be called with obj as this.
*/
InputStreamHTTP.prototype.setCallbackFunc = function (func, obj)
{
this.m_func = func;
this.m_obj = obj;
}
/**
* @class InputStreamUI.
* An input data stream encapsulated by a HTML element.
*
* @ctor InputStreamUI.
* Creates an input stream from a HTML element.
* @tparam HTMLElement elem the HTML element.
* @treturn InputStreamUI an input stream object.
*/
function InputStreamUI(elem)
{
/**
* The HTML element.
* @private
*/
this.m_container = elem;
}
InputStreamUI.prototype = new InputStream();
/**
* Reads data from the input stream.
* @treturn String[] Currently only support HTML elements with the value attribute.
*/
InputStreamUI.prototype.read = function()
{
if (this.m_container)
{
return this.m_container.value;
}
return "";
}
/**
* @class Mvmap.
* A multi valued hash table implementation.
*
* Suppose user calls
* thisObject.add("foo", 1)
* thisObject.add("foo", 2)
* thisObject.add("foo", 3)
*
* During retrieval, user simply call thisObject.item(key)
* we internally keep an iterator so it will return different
* values each time it is called.
*
* thisObject.item("foo"); returns 1
* thisObject.item("foo"); returns 2
* thisObject.item("foo"); returns 3
* thisObject.resetIterators();
* thisObject.item("foo"); returns 1
*/
function Mvmap()
{
this.m_map = {};
this.m_itr = {};
this.m_count = 0;
}
/**
* Adds an (key, item) entry to the multi-valued map.
* Duplicate keys are allowed. Duplicate items are allowed.
*/
Mvmap.prototype.add = function(key, item)
{
var curr = this.m_map[key];
if (curr) {
if (curr.push) {
curr.push(item);
} else {
this.m_map[key] = [curr, item];
}
} else {
this.m_map[key] = item;
}
this.m_count++;
}
/**
* Returns true if the key exists.
*/
Mvmap.prototype.exists = function(key)
{
return (this.m_map[key] != null);
}
/**
* Returns the number of entries (duplicate keys count as multiple entries).
*/
Mvmap.prototype.count = function()
{
return this.m_count;
}
/**
* Gets all key/values as two arrays.
*/
Mvmap.prototype.getAllValues = function()
{
var names = [];
var values= [];
var result = {"names" : names, "values": values};
if (this.m_count > 0) {
for (var key in this.m_map) {
var value = this.m_map[key];
if (value.push) {
for (var i = 0; i < value.length; i++) {
names.push(key);
values.push(value[i]);
}
} else if (value) {
names.push(key);
values.push(value);
}
}
}
return result;
}
/**
* Removes all items associated with the key.
* Currently we do not support removing a specific item.
*/
Mvmap.prototype.remove = function(key)
{
var curr = this.m_map[key];
if (curr) {
// testing for array
if (curr.push) {
this.m_count -= curr.length;
} else {
this.m_count -= 1;
}
} else {
this.m_count -=1;
}
this.m_map[key] = null;
}
/**
* Returns a item associated with the key.
* Note: suppose a key is mapped to multiple items,
* "foo" -> {0, 1, 2}
* item("foo") will initially return 0, then 1, then 2.
*/
Mvmap.prototype.item = function(key)
{
// determine which item we should get for this key.
var itrIndex = this.m_itr[key];
if (itrIndex) {
this.m_itr[key] = ++itrIndex;
} else {
itrIndex = 1;
this.m_itr[key] = itrIndex;
}
var curr = this.m_map[key];
if (curr) {
if (itrIndex == 1) {
// testing for array, yes if itrIndex > 1, this returns undefined, which as expected.
if (curr.push) {
return curr[itrIndex-1];
} else {
return curr; // could be null
}
} else if (curr.push && curr.length >= itrIndex) {
return curr[itrIndex-1];
}
}
return null;
}
/**
* Retrieves the iterator corresponding to the key.
*
* Suppose we have foo -> {1, 2, 3}
* Suppose iteratorIndex("foo") = 0 then
* item("foo") will return 1.
* Suppose iteratorIndex("bar") = -1
* item("bar") will return null.
* Suppose iteratorIndex("foo") = 2 then
* item("foo") will return 3
*/
Mvmap.prototype.iteratorIndex = function(key) {
var itrIndex = this.m_itr[key];
if (itrIndex) {
return itrIndex;
} else if (this.m_map[key]) {
return 0;
} else {
return -1;
}
}
/**
* Resets all iterators so
* thisObject.item(key) will return the first item
*/
Mvmap.prototype.resetIterators = function()
{
this.m_itr = {};
}
Mvmap.prototype.compare = function(anotherMap)
{
var score = 0;
for(var p in this.m_map) {
if (anotherMap.exists(p)) {
var thisData = this.m_map[p];
var thatData = anotherMap.m_map[p];
if (!thisData.push && !thatData.push) {
score++;
// single value case
/* if (thisData != thatData) {
return false;
} */
} else if (thisData.length == thatData.length) {
/* // multi value case
var thisDataCopy = thisData.slice(0).sort();
var thatDataCopy = thatData.slice(0).sort();
for (var i = 0; i < thisDataCopy.length; i++) {
if (thisDataCopy[i] != thatDataCopy[i]) {
return false;
}
} */
score+=thisData.length;
} else {
// one map has more entries than others,
// this is necessary for now.
score++;
}
} else {
}
}
return score;
}
/**
* @class Player.
* A web transaction Player.
*/
function Player()
{
}
/**
* Initializes data members.
* @private
*/
Player.prototype.init = function()
{
this.m_locks = []; // never make m_locks null;
this.m_tnManager = null; // never null after start
this.m_pauseMode = false;
this.m_steps = null; // never null after start
this.m_stepIndex = 0; // m_stepIndex >= 0 && m_stepIndex < m_steps.length
this.m_timeoutId = null; // time out id for controlling when to run the next step
this.m_sleepTimeoutId = null; // time out id for controlling how much time to sleep
this.m_timeoutPeriod = 10000; // default timeout = 30 seconds.
this.m_stopFuncs = [];
this.m_pauseFuncs = [];
this.m_loopFuncs = [];
this.m_started = false;
this.m_traceMode = false;
this.m_visibleMode = true;
this.m_silentMode = false;
this.m_debugMode = false;
this.m_loopCount = 1;
this.m_loopIndex = 0;
this.m_getPasswordFunc = function (index) { return null };
}
/**
* Sets the data source where the transaction definition can be read.
* @tparam InputStream inputStream the input data as a stream.
*/
Player.prototype.setInputStream = function(inputStream)
{
this.input_stream = inputStream;
}
/**
* Sets the time in milliseconds to wait before timeout on
* a wait for load action.
*
* @tparam int val the timeout value in milliseconds
*/
Player.prototype.setTimeoutPeriod = function(val)
{
this.m_timeoutPeriod = val;
}
/**
* Sets the mode to determine how much time we should wait between steps.
* @tparam String mode
*/
Player.prototype.setRunMode = function(mode)
{
this.m_runMode = mode;
}
/**
* Sets the number of times playback should repeat.
*
* @tparam int loop can also be 'inf'
*/
Player.prototype.setLoopCount = function(loop)
{
if (loop && loop.toLowerCase() == 'inf') {
this.m_loopCount = "inf";
} else if (loop < 1) {
this.m_loopCount = 1;
} else {
this.m_loopCount = loop;
}
}
Player.prototype.setDebugMode = function(mode)
{
this.m_debugMode = mode ? true : false;
}
Player.prototype.getDebugMode = function()
{
return this.m_debugMode;
}
/**
* Adds a function to call when playback stops.
* @tparam Function func a callback function without any parameters.
*/
Player.prototype.addOnStopCallback = function(func)
{
this.m_stopFuncs.push(func);
}
/**
* Adds a function to call when playback pauses.
* @tparam Function func a callback function without any parameters.
*/
Player.prototype.addOnPauseCallback = function(func)
{
this.m_pauseFuncs.push(func);
}
/**
* Adds a function to call when playback finishes one loop.
* @tparam Function func a callback function without any parameters.
*/
Player.prototype.addOnLoopCallback = function(func)
{
this.m_loopFuncs.push(func);
}
Player.prototype.setGetPasswordFunc = function(func)
{
this.m_getPasswordFunc = func;
}
/**
* Performs a set of callback functions.
* @private
*/
Player.prototype._oncallback = function(funcArray)
{
for (var i = 0; i < funcArray.length; i++) {
try {
funcArray[i]();
} catch (e) { this._handleException(arguments.callee, e); }
}
}
/**
* Called when playback is stopped.
* @private
*/
Player.prototype._onstop = function()
{
this._oncallback(this.m_stopFuncs);
}
/**
* Called when playback is paused.
* @private
*/
Player.prototype._onpause = function()
{
this._oncallback(this.m_pauseFuncs);
}
/**
* Called when playback has completed one loop.
* @private
*/
Player.prototype._onloop = function()
{
this._oncallback(this.m_loopFuncs);
}
/**
* Sets the trace mode on or off. This should be set
* for Play with Trace functionality.
*
* @tparam bool traceMode
*/
Player.prototype.setTraceMode = function(traceMode)
{
this.m_traceMode = traceMode;
}
/**
* Sets the visibility mode on or off. This should be set
* to true normally, and false for agent playback.
*/
Player.prototype.setVisible = function(mode)
{
this.m_visibleMode = mode;
}
/**
* Sets the silent mode on or off. This should be set
* to false normally, and true for agent playback.
*/
Player.prototype.setSilent = function(mode)
{
this.m_silentMode = mode;
}
/**
* Starts play back.
*/
Player.prototype.start = function()
{
try {
// clean up any state, just in case.
this.stop();
this.m_started = true;
this.m_loopIndex = 0;
this.m_tnManager = new TxnManager(this.input_stream.read());
if (this.m_tnManager) {
this.m_steps = this._readSteps(this.m_tnManager.readActions2());
if (this.m_steps && (this.m_steps.length > 0)) {
this.doStart();
} else {
stderr.log("Unable to read steps");
this._onstop();
}
} else {
stderr.log("Unable to create a transaction manager object");
this._onstop();
}
} catch (e) { this._handleException(arguments.callee, e); this._onstop(); }
}
/**
* Stops play back.
*/
Player.prototype.stop = function()
{
try {
if (this.m_started) {
// stop all future operations.
this._clearTimeout();
this._clearSleepTimeout();
// give subclass to clean up itself.
this.doStop();
// clear all the steps states
// in the reverse order.
this.m_stepIndex = 0;
this.m_steps = null;
this.m_tnManager = null;
this.m_locks.splice(0, this.m_locks.length);
this.m_started = false;
}
} catch (e) { this._handleException(arguments.callee, e); }
}
Player.prototype.abort = function ()
{
try {
if (this.m_brListener) {
this.m_brListener.stop();
}
if (this.m_httpListener) {
this.m_httpListener.stop();
}
} catch (e) { this._handleException(arguments.callee, e); }
}
/**
* Pauses play back.
*/
Player.prototype.pause = function()
{
try {
this._clearTimeout();
this._clearSleepTimeout();
this.m_pauseMode = true;
} catch (e) { this._handleException(arguments.callee, e); }
}
/**
* Resumes play back.
*/
Player.prototype.resume = function()
{
this.m_pauseMode = false;
this.tryContinue();
}
/**
* Performs a single action and pause.
*/
Player.prototype.step = function()
{
this.m_pauseMode = false;
this.tryContinue();
this.m_pauseMode = true;
}
/**
* Runs the next step.
* @private
*/
Player.prototype._runStep = function()
{
try {
if (this.m_stepIndex >= 0 && this.m_stepIndex < this.m_steps.length) {
if (this.m_stepIndex == 0) {
// start of another loop
this._onloop();
}
this._clearTimeout();
this._doRunStep(this.m_steps[this.m_stepIndex]);
} else if (this.m_steps.length == 0) {
this._onstop();
} else if (++this.m_loopIndex < this.m_loopCount || this.m_loopCount == "inf") {
this.m_stepIndex = 0;
this.tryContinue();
} else {
window.setTimeout("if (player) {player._onstop();}", 1000);
}
} catch (e) { this._handleException(arguments.callee, e); }
}
Player.prototype._rerunStep = function()
{
try {
if (this.m_stepIndex >= 0 && this.m_stepIndex < this.m_steps.length) {
this._doRunStep(this.m_steps[this.m_stepIndex]);
} else {
window.setTimeout("if (player) {player._onstop();}", 1000);
}
} catch (e) { this._handleException(arguments.callee, e); }
}
/**
* Reads all the actions and converts them
* into an array of JavaScript functions.
* @private
*
* @tparam String str
* @treturn Function[]
*/
Player.prototype._readSteps = function(str)
{
var inputs = str.split("\n");
var steps = new Array();
var currentAction = "";
var currentWait = "";
var currentSleep = "";
var i = 0;
var sleep = false;
var pwdIndex = 0;
var pas = null;
var quote = /\*\*\*\*\*\*/g;
var pause = false;
for (var j = 0; j < inputs.length; j++) {
var step = Util.trim(inputs[j]);
pause = (currentWait != "");
/*
debug.log("currentAction " + currentAction);
debug.log("currentWait " + currentWait);
debug.log("currentSleep " + currentSleep);
debug.log("step " + step);
*/
if (step.substring(0,9) == "// Action") {
if (currentAction != "" || currentWait != "") {
if (currentAction.indexOf("\*\*\*\*\*\*") != -1) {
/*
pas = this.m_getPasswordFunc(pwdIndex++);
if (pas.length == 0 || pas == null)
{
throw new Error("Found the string ****** in the script. This means you need to enter a password");
}
*/
}
currentAction = currentAction.replace(quote, pas);
pas = null;
if (pause) {
steps[i++] = new Function("", currentWait + currentAction + "player.sleep()\n");
} else {
steps[i++] = new Function("", currentWait + currentAction + "player.tryContinue()\n");
}
currentAction = "";
currentWait = "";
} else if (currentSleep != "") {
steps[i++] = new Function("", currentSleep);
currentSleep = "";
}
} else if (step.substring(0,7) == "// Wait") {
if (currentWait == "") {
currentWait += "// Wait\n";
} else if (currentSleep != "") {
steps[i++] = new Function("", currentSleep);
currentSleep = "";
}
} else if (step.substring(0,8) == "// Sleep") {
if (currentAction != "" || currentWait != "") {
currentAction = currentAction.replace(quote, pas);
if (pause) {
steps[i++] = new Function("", currentWait + currentAction + "\n");
} else {
steps[i++] = new Function("", currentWait + currentAction + "player.tryContinue()\n");
}
currentAction = "";
currentWait = "";
}
currentSleep += "// Sleep\n";
} else if (step != "") {
if (currentWait != "") {
currentWait += step + "\n";
} else if (currentSleep != "") {
currentSleep += step + "\n";
} else {
currentAction += step + "\n";
}
}
}
pause = (currentWait != "");
if (currentAction != "" || currentWait != "") {
if (pause) {
steps[i++] = new Function("", currentWait + currentAction + "\n");
} else {
steps[i++] = new Function("", currentWait + currentAction + "player.tryContinue()\n");
}
currentAction = "";
currentWait = "";
} else if (currentSleep != "") {
steps[i++] = new Function("", currentSleep);
}
return steps;
}
/**
* Runs a single step.
* @private
*/
Player.prototype._doRunStep = function(func)
{
try
{
var str = func.toString();
scripts.log("Run Step " + this.m_stepIndex, str.split("\n"));
func();
this.m_stepIndex++;
}
catch (e)
{
// check if e is of certain type
this._handleException(arguments.callee, e);
}
}
/**
* Clears time out.
* @private
*/
Player.prototype._clearTimeout = function()
{
try {
if (this.m_timeoutId != null) {
window.clearTimeout(this.m_timeoutId);
this.m_timeoutId = null;
}
} catch (e) { this._handleException(arguments.callee, e); }
}
/**
* Clears sleep time out.
* @private
*/
Player.prototype._clearSleepTimeout = function()
{
try {
if (this.m_sleepTimeoutId != null) {
window.clearTimeout(this.m_sleepTimeoutId);
this.m_sleepTimeoutId = null;
}
this._removeLock('sleep', 0);
} catch (e) { this._handleException(arguments.callee, e); }
}
/**
* Gets the browser object at position index.
* @tparam int windowIndex
* @treturn IWebBrowser2
*/
Player.prototype.getBrowserAt = function(windowIndex)
{
alert(getFunctionName(Player, arguments.callee) + " is not implemented.");
}
// --------------------------------------------------------------------------------
// Locks Implementation
/**
* Displays a set of locks.
* @private
*/
Player.prototype._showLocks = function()
{
try {
if (this.m_locks.length == 0) {
// debug.log("No Locks");
window.txn.showLocks(this.m_locks.length);
} else {
var output = new Array();
for (var i = 0; i < this.m_locks.length; i++) {
var mylock = this.m_locks[i];
output[i] = mylock.lockType + " " + mylock.windowIndex + mylock.frame;
}
window.txn.showLocks(this.m_locks.length);
debug.log(this.m_locks.length + " Locks", output);
}
} catch (e) { this._handleException(arguments.callee, e); }
}
/**
* Waits for a particular event to occur.
*
* @tparam String type the type of event, possible values include "open", "close", "sleep"
* @tparam int windowIndex the window index (only applicable for open and close events).
*/
Player.prototype.waitFor = function(type, windowIndex, frame)
{
try {
if (this._canAddLock(type, windowIndex, frame)) {
this._addLock(type, windowIndex, frame);
}
} catch (e) { this._handleException(arguments.callee, e); }
}
/**
* Adds a lock to the lock chain.
* @tparam String locType
* @tparam int windowIndex
* @private
*/
Player.prototype._addLock = function(lockType, windowIndex, frame)
{
// stderr.log("_addLock " + lockType + " " + windowIndex + " " + frame);
var lock = new Object();
lock.lockType = lockType;
lock.windowIndex = windowIndex;
if (frame == null) {
frame = "";
}
lock.frame = frame;
this.m_locks.push(lock);
this._showLocks();
}
/**
* Before adding a lock, we should always
* check if the event that will open the lock
* has already occurred.
* @private
*/
Player.prototype._canAddLock = function(lockType, windowIndex, frame)
{
try {
var checkType = null;
// stderr.log("canAddLock " + lockType + " " + windowIndex + " " + frame);
// to add a lock to wait for window open/close,
// if there is already a lock to wait for window close/open (opposite event),
// then we can safely add it
if (lockType == "open") {
checkType = "close";
} else if (lockType == "close") {
// to add an wait on window close,
// if there is already a lock on window open,
// then we can safely add it.
checkType = "open";
}
for (var i = 0; i < this.m_locks.length; i++) {
var lock = this.m_locks[i];
if (lock.lockType == checkType && lock.windowIndex == windowIndex) {
return true;
}
}
// it doesn't make sense to add a lock that
// waits for a window open, but it is already opened.
if (lockType == "open") {
// to add a lock to wait for window open
// the window should not already exist.
return this.getBrowserAt(windowIndex) == null;
} else if (lockType == "close") {
// to add a lock to wait for window close
// the window should be currently active.
return this.getBrowserAt(windowIndex) != null;
}
// no other conditions, allow to add.
return true;
} catch (e) { this._handleException(arguments.callee, e); }
}
/**
* Removes a lock from the lock chain.
* @tparam String lockType
* @tparam int windowIndex
* @private
*/
Player.prototype._removeLock = function(lockType, windowIndex, frame)
{
try {
// stderr.log("_removeLock " + lockType + " " + windowIndex + " " + frame);
if (frame == null) {
frame = "";
}
for (var i = 0; i < this.m_locks.length; i++) {
var lock = this.m_locks[i];
// stderr.log("show lock " + i + " " + lock.lockType + " " + lock.windowIndex + " " + lock.frame);
if (lock.lockType == lockType && lock.windowIndex == windowIndex && lock.frame == frame) {
this.m_locks.splice(i, 1);
break;
}
}
this._showLocks();
} catch (e) { this._handleException(arguments.callee, e); }
}
/**
* Removes a lock and continue plays the web transaction.
* @tparam String lockType
* @tparam int windowIndex
* @protected
*/
Player.prototype._unlock = function(lockType, windowIndex, frame)
{
try {
this._removeLock(lockType, windowIndex, frame);
this.tryContinue();
} catch (e) { this._handleException(arguments.callee, e); }
}
/**
* Runs the next step, because time out has been elapsed.
* @private
*/
Player.prototype._runStepAfterTimeout = function()
{
try {
// when this function is called,
// we have a lock that is not opened,
// usually a load lock will cause this.
// unopen a load lock
stderr.log("Time out, remove a load or traffic lock.");
this._wake();
for (var i = 0; i < this.m_locks.length; i++) {
var lock = this.m_locks[i];
if (lock.lockType == "load") {
this._unlock('load', lock.windowIndex, lock.frame);
break;
}
}
if (i == this.m_locks.length) {
for (var i = 0; i < this.m_locks.length; i++) {
if (lock.lockType == "traffic") {
this._unlock('traffic', lock.windowIndex);
break;
}
}
}
// this.locks.splice(0, this.locks.length);
// this.runStep();
} catch (e) { this._handleException(arguments.callee, e); }
}
/**
* Tries to continue the playback after time out (ms).
*/
Player.prototype.tryContinue = function()
{
try {
if (this.m_pauseMode) {
return;
}
if (this.m_locks.length == 0) {
this._clearTimeout();
this.m_timeoutId = window.setTimeout("if (player) {player._runStep();}", 0);
} else if (this.m_timeoutPeriod > 0) {
// this should be configurable
this._clearTimeout();
this.m_timeoutId = window.setTimeout("if (player) {player._runStepAfterTimeout();}", this.m_timeoutPeriod);
}
} catch (e) { this._handleException(arguments.callee, e); }
}
/**
* Gets the amount of time to sleep in milliseconds.
* @private
*/
Player.prototype._getSleepTime = function()
{
if (this.m_runMode == "Run") {
return 600;
} else if (this.m_runMode == "Walk") {
return 2000;
} else if (this.m_runMode == "Crawl") {
return 10000;
} else {
return null;
}
}
/**
* Sleeps for a specific amount of time,
* or default amount of sleep time (depend on run or walk mode)
* @tparam int time time to sleep in milliseconds.
*/
Player.prototype.sleep = function(time)
{
try {
var sleepTime = time ? time :this._getSleepTime();
if (sleepTime) {
// create a lock to be opened by wake()
// in the future after specific amount of sleep time.
this._clearSleepTimeout();
// debug.log("Sleeping for " + sleepTime + " milliseconds");
this.waitFor("sleep", 0);
var that = this;
this.m_sleepTimeoutId = window.setTimeout("if (player) {player._wake();}", sleepTime);
} else {
this._onpause();
}
} catch (e) { this._handleException(arguments.callee, e); }
}
/**
* Wakes up from sleeping.
* @private
*/
Player.prototype._wake = function()
{
this._clearSleepTimeout();
this.tryContinue();
}
/**
* Exception handling.
* @private
*/
Player.prototype._handleException = function(func, e)
{
handleException(Player, func, e);
}
/**
* Displays all the steps.
* @private
*/
Player.prototype._showSteps = function()
{
try {
if (this.m_stepIndex >= 0 && this.m_stepIndex < this.m_steps.length) {
var str = this.m_steps[this.m_stepIndex].toString();
stderr.log("Step " + (this.m_stepIndex), str.split("\n"));
}
} catch (e) { this._handleException(arguments.callee, e); }
}
/**
* Abstract method for performing browser specific actions when playback starts.
* @protected
*/
Player.prototype.doStart = function()
{
alert(getFunctionName(Player, arguments.callee) + " is not implemented.");
}
/**
* Abstract method for performing browser specific actions when playback stops.
* @protected
*/
Player.prototype.doStop = function()
{
alert(getFunctionName(Player, arguments.callee) + " is not implemented.");
}
/**
* A simple play back engine does not need to record anything. igore
* @tparam String tags
* @tparam map attr
* @tparam bool genId
*/
Player.prototype.createNode = function(tags, attr, genId)
{
// do nothing
}
/**
* A simple play back engine does not need to record anything. igore
* @tparam XMLElement node
* @tparam int time
*/
Player.prototype.recordNode = function(node, time)
{
// do nothing
}
/**
* Adds listener.
* @see IEBrEventListener
* @see IEHTTPEventListener
* @tparam EventListener listener
*/
Player.prototype.addListener = function(listener)
{
try {
if (listener) {
var name = listener.getName();
if (false) {
} else if (name.indexOf("BrEventListener") != -1) {
this.m_brListener = listener;
} else if (name.indexOf("HTTPEventListener") != -1) {
this.m_httpListener = listener;
} else if (name.indexOf("DomEventListener") != -1) {
// do not add this;
} else {
throw "Unknown Listener " + name;
}
}
} catch (e) { this._handleException(arguments.callee, e); }
}
/**
* @class Recorder.
* A web transaction Recorder.
*/
function Recorder()
{
}
/**
* Inits members.
* @private
*/
Recorder.prototype.init = function()
{
try {
/**
* The set of functions to call when recording stops.
* @private
*/
this.m_stopFuncs = [];
this.m_setPasswordFunc = function(index) {
return null;
}
/**
* The trace mode.
*/
this.m_traceMode = false;
/**
* The starting url.
*/
this.m_startingUrl = null;
/**
* The record password mode.
*/
this.m_maskPassword = false;
this.m_highlight = false;
this.m_debugMode = false;
/**
* The txn name.
*/
this.m_name = "New Transaction";
this.setTimer = function(timer) {
this.m_timer = timer;
}
this.getTimer = function() {
return this.m_timer;
}
this.setControlListeners(true);
this.getTxnManager = function() {
return this.m_eventsManager.getTxnManager();
}
this.getNodeWithId = function(id) {
return this.m_eventsManager.getNodeWithId(id);
}
this.createNode = function(tag, attrs, genId) {
return this.m_eventsManager.createNode(tag, attrs, genId);
}
this.writeNode = function(node, time) {
return this.m_eventsManager.writeNode(node, time);
}
this.getWindowIndex = function(win) {
return this.m_brListener.getWindowIndex(win);
}
/**
* Transforms the recording XML into various output.
* @tparam String xsl the location of the XSLT transformation.
* @treturn IXMLElement or null
* @private
*/
this._getTransformedRecording = function(xsl) {
return this.m_eventsManager.getTxnManager().transform(xsl).documentElement;
}
} catch (e) { this._handleException(arguments.callee, e); }
}
/**
* Starts recording.
*/
Recorder.prototype.start = function()
{
try {
if (this.m_httpListener) {
this.m_httpListener.setTraceMode(this.getTraceMode());
}
if (this.m_brListener) {
this.m_brListener.setStartingUrl(this.getStartingUrl());
}
if (this.m_domListener) {
this.m_domListener.setMaskPasswordMode(this.getMaskPasswordMode());
this.m_domListener.setHighlightMode(this.getHighlightMode());
}
if (this.m_httpListener) {
this.m_httpListener.start();
}
if (this.m_brListener) {
this.m_brListener.start();
}
if (this.m_domListener) {
this.m_domListener.start();
}
} catch (e) { this._handleException(arguments.callee, e); }
}
/**
* Stops recording.
*/
Recorder.prototype.stop = function ()
{
this.abort();
}
/**
* Aborts recording.
*/
Recorder.prototype.abort = function ()
{
try {
if (this.m_domListener) {
this.m_domListener.stop();
}
if (this.m_brListener) {
this.m_brListener.stop();
}
if (this.m_httpListener) {
this.m_httpListener.stop();
}
} catch (e) { this._handleException(arguments.callee, e); }
}
/**
* Specifies the ownership of listeners.
* @tparam bool mode if true (default), this object is responsible for
* creating/starting/stopping the listeners. if false, the listeners are
* managed by something else, and must be added explicitly.
*
* @see #addListener
*/
Recorder.prototype.setControlListeners = function(mode)
{
this.m_controlListeners = mode;
}
/**
* Specifies the analyzer object.
* Analyzer analyzer knows how to analyze the recorded events.
* @tparam Analyzer analyzer
*/
Recorder.prototype.setAnalyzer = function(analyzer)
{
this.m_analyzer = analyzer;
}
/**
* Performs analysis.
* @private
*/
Recorder.prototype.analyze = function()
{
if (this.m_eventsManager) {
var xmlDoc = this.m_eventsManager.getXMLDocument();
if (xmlDoc) {
this.m_analyzer.process(xmlDoc);
}
}
}
/**
* Adds listener.
* @tparam EventListener listener
*
* Client should call addListener if it also calls
* setControlListeners(false). This way, client controls
* the listeners.
*
* @see #setControlListeners
*
* Currently only supports the following listeners.
* @see IEBrEventListener, FFBrEventListener, IEDomEventListener,
* FFDomEventListener, IEHTTPEventListener, FFHTTPEventListener
*/
Recorder.prototype.addListener = function(listener)
{
try {
if (listener) {
var name = listener.getName();
if (false) {
} else if (name.indexOf("BrEventListener") != -1) {
this.m_brListener = listener;
} else if (name.indexOf("HTTPEventListener") != -1) {
this.m_httpListener = listener;
} else if (name.indexOf("DomEventListener") != -1) {
this.m_domListener = listener;
} else {
throw "Unknown Listener " + name;
}
}
} catch (e) { this._handleException(arguments.callee, e); }
}
/**
* Performs a set of callbacks.
* @private
*/
Recorder.prototype._oncallback = function(funcArray)
{
for (var i = 0; i < funcArray.length; i++) {
try {
funcArray[i]();
} catch (e) { this._handleException(arguments.callee, e); }
}
}
/**
* Exception handling.
* @private
*/
Recorder.prototype._handleException = function (func, e)
{
handleException(Recorder, func, e);
}
/**
* Called when recording stops.
* @private
*/
Recorder.prototype._onstop = function()
{
this._oncallback(this.m_stopFuncs);
}
/**
* Adds a callback function to be performed when recording stops.
*/
Recorder.prototype.addOnStopCallback = function(func)
{
this.m_stopFuncs.push(func);
}
/**
* Sets the record password mode.
* If true, recorder will not display password in the recorded transaction.
*
* The default value is true.
* @treturn bool
*/
Recorder.prototype.setMaskPasswordMode = function(val)
{
this.m_maskPassword = val ? true : false;
}
/**
* Gets the record password mode.
* @treturn bool
*/
Recorder.prototype.getMaskPasswordMode = function()
{
return this.m_maskPassword;
}
/**
* Sets the starting URL.
* The default value is null.
* @tparam String url the starting url.
*/
Recorder.prototype.setStartingUrl = function(url)
{
this.m_startingUrl = url;
}
/**
* Gets the starting URL.
* @treturn String the starting url.
*/
Recorder.prototype.getStartingUrl = function()
{
return this.m_startingUrl;
}
/**
* Sets the transaction name.
* @tparam String name
*/
Recorder.prototype.setTxnName = function(name)
{
this.m_name = name;
}
/**
* Gets the transaction name.
* @treturn String
*/
Recorder.prototype.getTxnName = function()
{
return this.m_name;
}
/**
* Sets the trace mode.
* The default value is false.
* @tparam bool mode the new trace mode.
*/
Recorder.prototype.setTraceMode = function(mode)
{
this.m_traceMode = mode ? true : false;
}
/**
* Gets the trace mode.
* @treturn bool the trace mode.
*/
Recorder.prototype.getTraceMode = function()
{
return this.m_traceMode;
}
Recorder.prototype.setSetPasswordFunc = function(func)
{
this.m_setPasswordFunc = func;
}
Recorder.prototype.recordPassword = function(pwd)
{
this.m_setPasswordFunc(pwd);
}
Recorder.prototype.setHighlightMode = function(mode)
{
this.m_highlight = mode ? true : false;
}
Recorder.prototype.getHighlightMode = function()
{
return this.m_highlight;
}
Recorder.prototype.setDebugMode = function(mode)
{
this.m_debugMode = mode ? true : false;
}
Recorder.prototype.getDebugMode = function()
{
return this.m_debugMode;
}
/**
* Event listeners call this method when going to a URL needs to be recorded.
* @tparam WebBrowser2 frame
* @tparam String url
* @tparam int time
*/
Recorder.prototype.onGotoUrl = function(frame, url, time)
{
try {
var attrs = {
"type": "open",
"newValue": Util.escapeQuote(url),
"windowIndex":this.m_brListener.getBrowserIndex(frame),
"time": time
};
var node = this.createNode("action", attrs, false);
this.writeNode(node, time);
} catch (e) { this._handleException(arguments.callee, e); }
}
/**
* Event listeners call this method when a popup window is opened.
* @tparam int windowIndex
* @tparam int time
*/
Recorder.prototype.onNewWindow2 = function(windowIndex, time)
{
try {
if (windowIndex > 0) {
var attrs = {
"type":Constants.waitForPopUp,
"windowIndex":windowIndex,
"time": time
};
var node = this.createNode("action", attrs, false);
this.writeNode(node, time);
}
} catch (e) { this._handleException(arguments.callee, e); }
}
/**
* Event listeners call this method when a popup window is closed.
* @tparam int windowIndex
* @tparam int time
*/
Recorder.prototype.onQuit = function(windowIndex, time)
{
try {
// need to wait for child windows to close,
// never need to wait for the master window to close.
if (windowIndex && windowIndex > 0) {
var attrs = {
"type":Constants.waitForPopUpClose,
"windowIndex":windowIndex,
"time":time
};
var node = this.createNode("action", attrs, false);
this.writeNode(node, time);
}
if (this.m_brListener && this.m_brListener.getWindowCount() == 0) {
this._onstop();
}
} catch (e) { this._handleException(arguments.callee, e); }
}
/**
* Event listeners call this method when a DOM event is raised.
* @tparam map attributes
* @tparam int time
*/
Recorder.prototype.onDOMEvent = function(attributes, time)
{
try {
var node = this.createNode("action", attributes, true);
this.writeNode(node, time);
return node;
} catch (e) { this._handleException(arguments.callee, e); }
}
var _last_logged_evt = null;
var _last_logged_msg = null;
var _last_type = null;
var _startTime = (new Date()).getTime();
function log(e)
{
if (messages && messages.getIsEnabled() && e)
{
var t = new StringBuffer();
var ms = [];
if (true)
{
t.append(e.type + " ");
for (var p in e)
{
if (e[p] != null && e[p] != false && e[p] != 0)
{
if (p == "screenX" || p == "screenY" || p == "clientX" || p == "clientY")
{
}
else if (p == "boundElements")
{
stderr.log(e[p].name);
ms[ms.length]= p + ":" + e[p].toString();
}
else {
ms[ms.length]= p + ":" + e[p];
}
}
}
if (e.srcElement)
{
var tagName = (e.srcElement.tagName) ? e.srcElement.tagName.toLowerCase() : "N/A";
ms[ms.length] = tagName;
}
}
/*
if (e.altKey) m.append("altKey:" + e.altKey + " ");
if (e.altLeft) m.append("altLeft:" + e.altLeft + " ");
if (e.button) m.append("button:" + e.button + " ");
if (e.cancelBubble) m.append("cancelBubble:" + e.cancelBubble + " ");
if (e.clientX) m.append("clientX:" + e.clientX + " ");
if (e.clientY) m.append("clientY:" + e.clientY + " ");
if (e.ctrlKey) m.append("ctrlKey:" + e.ctrlKey + " ");
if (e.ctrlLeft) m.append("ctrlLeft:" + e.ctrlLeft + " ");
if (e.keyCode) m.append("e.keyCode:" + e.keyCode + " ");
if (e.reason) m.append("reason:" + e.reason + " ");
if (e.propertyName) m.append("propertyName:" + e.propertyName + " ");
if (e.fromElement) m.append("fromElement:" + e.fromElement.tagName.toLowerCase() + " ");
if (e.propertyName) m.append("property:" + e.propertyName + " ");
if (e.propertyName) t.append("property:" + e.propertyName + " ");
if (e.srcElement)
{
var tagName = (e.srcElement.tagName) ? e.srcElement.tagName.toLowerCase() : "N/A";
m.append("srcElement:" + tagName + " ");
t.append(tagName + " ");
if (e.srcElement.id) t.append("id:" + e.srcElement.id + " ");
if (e.srcElement.name) t.append("name:" + e.srcElement.name + " ");
// if (e.srcElement.innerText) m.append("text:" + e.srcElement.innerHTML + " ");
}
if (e.type == "mouseover" || e.type == "mouseenter")
{
if (e.srcElement && e.srcElement.children.length == 0)
{
// t.append("text:" + e.srcElement.innerHTML + " ");
}
// t.append("background:" + e.srcElement.runtimeStyle.backgroundColor + " ");
//t.append("activeElement:" + e.srcElement.document.activeElement.innerText + " ");
}
if (e.type == "mouseout")
{
// t.append("activeElement:" + e.srcElement.document.activeElement.innerText + " ");
}
if(e.target)
{
if(netscape) netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect UniversalBrowserRead UniversalBrowserWrite');
var tagName = (e.target.tagName) ? e.target.tagName.toLowerCase() : "N/A";
m.append("target: " + tagName + " ");
t.append("target: " + tagName + " ");
//m.append("target: " + recorder.xPathGen.getXPath(e.target)+" ");
if (e.target.hasAttribute&&e.target.hasAttribute("id")) t.append("id: " + e.target.getAttribute("id") + " ");
if (e.target.hasAttribute&&e.target.hasAttribute("name")) t.append("name: " + e.target.getAttribute("name") + " ");
}
if(e.explicitOriginalTarget )
{
if(netscape) netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect UniversalBrowserRead UniversalBrowserWrite');
var tgt = e.explicitOriginalTarget;
//PlayerFF._printDOM(tgt);
var tagName = (tgt.tagName) ? tgt.tagName.toLowerCase() : "N/A";
m.append("otarget: " + tagName + " ");
t.append("otarget: " + tagName + " ");
//m.append("otarget: " + recorder.xPathGen.getXPath(tgt)+" ");
if (tgt.hasAttribute&&tgt.hasAttribute("id")) t.append("ot id: " + tgt.getAttribute("id") + " ");
if (tgt.hasAttribute&&tgt.hasAttribute("name")) t.append("ot name: " + tgt.getAttribute("name") + " ");
}
if (e.keyCode) m.append("e.keyCode: " + e.keyCode + " ");
if (e.fromElement) m.append("fromElement: " + e.fromElement.tagName.toLowerCase() + " ");
*/
var ts = t.toString();
messages.log(ts, ms);
_last_logged_evt = ts;
_last_logged_msg = ms;
}
}
/**
* @class EventsManager
* Manages a collection of events that happens during recording or playback.
*/
function EventsManager()
{
}
/**
* Initializes members.
* @private
*/
EventsManager.prototype.init = function()
{
this.m_eventId = 0;
this.m_id2Nodes = [];
this.m_nodes = [];
this.m_tnManager = null;
}
/**
* Gets a node with a particular id.
* @tparam int id the node id.
* @treturn IXMLElement the node with id or null.
*/
EventsManager.prototype.getNodeWithId = function (id)
{
return this.m_id2Nodes[id];
}
/**
* Creates a node.
* @tparam String tag the node tag.
* @tparam Object attrs a hashtable of name value pairs.
* @tparam bool genId true means the node should have nodeId.
* @treturn IXMLElement the newly created node, the node is not added.
* @see #writeNode
*/
EventsManager.prototype.createNode = function(tag, attrs, genId)
{
try {
var node = this.getTxnManager().createElement(tag);
this.m_recProcessor.addProperties(node, tag, attrs, this.getTxnManager());
if (genId >= 0) {
node.setAttribute("nodeId", this.m_eventId);
this.m_id2Nodes[this.m_eventId] = node;
this.m_eventId++;
}
return node;
} catch (e) { this._handleException(arguments.callee, e); }
}
/**
* Writes a node.
* @tparam IXMLElement node the node to be appended.
* @tparam int time the time of the event.
*/
EventsManager.prototype.writeNode = function(node, time)
{
try {
if (node) {
this.m_nodes.push({"time": time, "node" : node});
}
} catch (e) { this._handleException(arguments.callee, e); }
}
/**
* Gets the XML Document.
* @treturn IXMLDocument
*/
EventsManager.prototype.getXMLDocument = function()
{
return this.getTxnManager().documentElement();
}
/**
* Gets the object that manages the transaction.
* @treturn TxnManager
*/
EventsManager.prototype.getTxnManager = function()
{
return this.m_tnManager;
}
/**
* Gets the array of nodes managed by this transaction.
* @treturn Array
*/
EventsManager.prototype.getNodes = function()
{
return this.m_nodes;
}
/**
* Sorts the node array.
* @tparam Function func the sorting function.
*/
EventsManager.prototype.sort = function(func)
{
this.m_nodes.sort(func);
}
/*
we don't need this for now, is because
the Timer object will always return unique
time.
EventsManager.prototype.sortFunc = function()
{
var tags = {
"BN" : 0,
"APP": 1,
"Redirect": 2,
"NC" : 3,
"DC" : 4
}
// sorting two sources
var timeSortFunc = function(a,b) {
if (a.time != b.time) {
return a.time - b.time;
} else {
var a1 = tags[a.node.tagName];
var b1 = tags[b.node.tagName];
if (a1 && b1) {
return a1 - b1;
} else {
return 0;
}
}
};
}
*/
/**
* Handles exception.
* @private
*/
EventsManager.prototype._handleException = function(func, e)
{
handleException(EventsManager, func, e);
}
/**
* @class IEEventsManager
* Manages a collection of events that specific to Internet Explorer.
*/
function IEEventsManager(mediator)
{
this.init(mediator);
}
IEEventsManager.prototype = new EventsManager();
/**
* Initialize members.
* @private
*/
IEEventsManager.prototype.init = function(mediator)
{
EventsManager.prototype.init.call(this);
this.m_htmlHelper = new ActiveXObject("OraBcnTxnUtil2.HTMLHelper");
this.m_recProcessor = new IERecProcessor();
this.m_tnManager = new TxnManager();
this.m_mediator = mediator;
}
/**
* Event listener (Collegue) calls this method when a BeforeNavigate2 event occurs.
* @tparam IWebBrowser2 frame
* @tparam String url
* @tparam int flags
* @tparam String targetFrameName
* @tparam String postdata
* @tparam String headers
* @tparam int time
*/
IEEventsManager.prototype.onBeforeNavigate2 = function(frame, url, flags, targetFrameName, postdata, headers, time, windowIndex)
{
try {
// var windowIndex = this.m_mediator.getBrowserIndex(frame);
var attrs = {
"url":url,
"flags":flags,
"targetFrameName":targetFrameName,
"postdata": postdata,
"headers": headers,
"windowIndex": windowIndex,
"time": time
};
var node = this.createNode("BN", attrs, true);
this.writeNode(node, time);
return node;
} catch (e) { this._handleException(arguments.callee, e); }
}
/**
* Event listener calls this method when a NavigateComplete2 event occurs.
* @tparam IWebBrowser2 frame
* @tparam String url
* @tparam int time
*/
IEEventsManager.prototype.onNavigateComplete2 = function(frame, url, time, windowIndex)
{
try {
var name = "";
try
{
var doc = this.m_mediator._getDocument(frame);
if (doc) {
name = doc.parentWindow.name;
}
}
catch (e)
{
// ignore this._handleException(arguments.callee, e);
}
// var windowIndex = this.m_mediator.getBrowserIndex(frame);
var attrs = {
"url":url,
"time":time,
"windowIndex": windowIndex,
"frame":name
};
var node = this.createNode("NC", attrs, true);
this.writeNode(node, time);
return node;
} catch (e) { this._handleException(arguments.callee, e); }
}
/**
* Event listener calls this method when a DocumentComplete event occurs.
* @tparam IWebBrowser2 frame
* @tparam String url
* @tparam int time
*/
IEEventsManager.prototype.onDocumentComplete = function(frame, url, time, title, windowIndex)
{
try {
var charset = null;
var name = null;
try
{
if (frame.LocationURL == url) {
frame.PutProperty("navState", "DC");
}
var doc = this.m_mediator._getDocument(frame);
if (doc) {
charset = doc.charset;
name = doc.parentWindow.name;
}
}
catch (e)
{
// ignore this._handleException(arguments.callee, e);
}
// var windowIndex = this.m_mediator.getBrowserIndex(frame);
var attrs = {
"url":url,
"time":time,
"charset":charset,
"title":title,
"windowIndex": windowIndex,
"frame":name
};
var node = this.createNode("DC", attrs, true);
this._myload(frame, node, time);
this.writeNode(node, time);
return node;
} catch (e) { this._handleException(arguments.callee, e); }
}
/**
* Event listener calls this method when a NavigateError event occurs.
* @tparam IWebBrowser2 frame
* @tparam String url
* @tparam String targetFrameName
* @tparam int statusCode
* @tparam int time
*/
IEEventsManager.prototype.onNavigateError = function(frame, url, targetFrameName, statusCode, time, windowIndex)
{
try {
var attrs = {
"url":url,
"time":time,
"targetFrameName":targetFrameName,
"statusCode":statusCode
};
if (statusCode != 200) {
var node = this.createNode(Constants.BROWSER_ERROR, attrs, true);
this.writeNode(node, time);
return node;
} else {
return null;
}
} catch (e) { this._handleException(arguments.callee, e); }
}
/**
* Handles window.onload event, this event is triggered during DocumentComplete.
* @private
*/
IEEventsManager.prototype._myload = function (frame, parentNode, time)
{
try {
var doc = this.m_mediator._getDocument(frame);
var frames = null;
var framesLength = 0;
try { // keep this
if (doc) {
frames = doc.frames;
framesLength = frames.length;
}
} catch (e) { // keep this
// ignore
}
if (frames) {
for (var i = 0; i < framesLength; i++) {
// if framesLength is non zero, then we should be able to access the frames array.
var f = frames[i];
var url = f.src;
var attrs = {
"url":url,
"name":f.name,
"fid":f.id,
"foundId": parentNode.getAttribute("nodeId"),
"time": time
};
var node = this.createNode("FRAME", attrs, true);
this.writeNode(node, time);
}
}
} catch (e) { this._handleException(arguments.callee, e); }
}
/**
* Gets the HTML helper object.
* @treturn IHTMLHelper
*/
IEEventsManager.prototype.getHTMLHelper = function()
{
return this.m_htmlHelper;
}
/**
* Handles exception.
* @private
*/
IEEventsManager.prototype._handleException = function(func, e)
{
handleException(IEEventsManager, func, e);
};
/**
* @class TxnManager.
* A transaction serializer, responsible for reading/writing transaction objects.
*/
function TxnManager(strArr)
{
var str = strArr ? strArr : null;
/**
* XML Document object.
* @private.
*/
this.m_xmlDoc = null;
// constructor
// if (window.ActiveXObject) {
// Internet Explorer Implementation
this.m_xmlDoc = createDOMDocument();
this.m_xmlDoc.async = false;
if (str) {
if (!this.m_xmlDoc.loadXML(str)) {
var err = this.m_xmlDoc.parseError;
if (err) {
throw new Error("Parse Error, reason:" + err.reason + " line:" + err.line);
}
}
}
if (!this.m_xmlDoc.documentElement) {
this.m_xmlDoc.documentElement = this.m_xmlDoc.createElement("transaction");
}
/* } else {
// Mozilla Implementation
if (str) {
var parser = new DOMParser();
this.m_xmlDoc = parser.parseFromString(str, "text/xml");
var roottag = this.m_xmlDoc.documentElement;
if (roottag.tagName == "parserError" ||
roottag.namespaceURI == "http://www.mozilla.org/newlayout/xml/parsererror.xml")
{
var serializer = new XMLSerializer();
var err = serializer.serializeToString(this.m_xmlDoc);
if (err) {
throw new Error("Parse Error, reason:" + err.reason + " line:" + err.line);
}
this.m_xmlDoc = null;
}
}
if (!this.m_xmlDoc) {
this.m_xmlDoc = document.implementation.createDocument("", "transaction", null);
}
}
*/
if (!this.m_xmlDoc) {
throw new Error("Cannot Create a Transaction Manager using XML");
}
}
/**
* Sets the transaction name.
* @tparam String name
*/
TxnManager.prototype.setTxnName = function(name)
{
this.m_xmlDoc.documentElement.setAttribute("name", name);
}
/**
* Creates an action node.
* @treturn XMLElement a new action node.
*
* Note:
* the node is not inserted into the XML.
* @see #writeAction
*/
TxnManager.prototype.createAction = function()
{
return this.m_xmlDoc.createElement("action");
}
/**
* Creates a XML element with a specific tag name.
* @treturn XMLElement a new element.
*
* Note:
* the element is not inserted into the XML.
* @see #writeNode
*/
TxnManager.prototype.createElement = function(tagName)
{
return this.m_xmlDoc.createElement(tagName);
}
/**
* Creates a XML CDATA section with a specific value.
* @treturn XMLCDATASection a new CDATA element.
*
* Note:
* the element is not inserted into the XML.
* @see #writeNode
*/
TxnManager.prototype.createCDATASection = function(value)
{
return this.m_xmlDoc.createCDATASection(value);
}
/**
* Appends a node to the XML.
* @tparam XMLElement node the node to append.
*/
TxnManager.prototype.writeNode = function(node)
{
this.m_xmlDoc.documentElement.appendChild(node);
}
/**
* Reads all the actions.
* @treturn String All the actions as JavaScript text.
*/
TxnManager.prototype.readActions = function()
{
// if (window.ActiveXObject) {
var xslDoc = createDOMDocument();
xslDoc.async = false;
xslDoc.load("js/xml2func.xsl");
var output = this.m_xmlDoc.transformNode(xslDoc);
return output;
/* } else {
var xslDoc = document.implementation.createDocument("", "", null);
xslDoc.async = false;
xslDoc.load("js/xml2func.xsl");
var objXSLTProc = new XSLTProcessor();
objXSLTProc.importStylesheet(xslDoc);
var newDoc = objXSLTProc.transformToDocument(this.m_xmlDoc);
if (newDoc)
{
return newDoc.documentElement.textContent;
}
} */
}
TxnManager.prototype.readActions2 = function()
{
var output = (new XML2Func()).process(this.m_xmlDoc.documentElement);
return output;
}
/**
* Returns the XML representation of the web transaction.
* @treturn XMLDocument
*/
TxnManager.prototype.documentElement = function()
{
return this.m_xmlDoc.documentElement;
}
/**
* Transforms XML document using the XSL.
* @tparam String xsl the XSL path.
* @treturn String the content after transformation.
*/
TxnManager.prototype.transform = function(xsl)
{
// if (window.ActiveXObject) {
var xslDoc = createDOMDocument();
xslDoc.async = false;
xslDoc.load(xsl);
// var output = this.m_xmlDoc.transformNode(xslDoc);
var result = createDOMDocument();
result.async = false;
result.validateOnParse = true;
this.m_xmlDoc.transformNodeToObject(xslDoc, result);
return result;
/* } else {
var xslDoc = document.implementation.createDocument("", "", null);
xslDoc.async = false;
xslDoc.load(xsl);
var objXSLTProc = new XSLTProcessor();
objXSLTProc.importStylesheet(xslDoc);
return objXSLTProc.transformToDocument(this.m_xmlDoc);
} */
}
/**
* Transforms XML document using the XSL.
* @tparam String xsl the XSL path.
* @treturn XMLDocument
*/
TxnManager.prototype.transform2 = function(xsl)
{
// if (window.ActiveXObject) {
var xslDoc = createDOMDocument();
xslDoc.async = false;
xslDoc.load(xsl);
return this.m_xmlDoc.transformNode(xslDoc);
// }
// return null;
}
/**
* Returns the serialized form of the XML, with a \\n between > and <.
* @treturn String the web transaction as XML.
*/
TxnManager.prototype.toString = function()
{
// if (window.ActiveXObject) {
var str = this.m_xmlDoc.xml;
var reg = />\n<");
/* } else {
var serializer = new XMLSerializer();
var str = serializer.serializeToString(this.m_xmlDoc);
var reg = />\n<");
} */
}
/**
* @class Analyzer.
*
* The base analyzer class.
*/
function Analyzer()
{
}
/**
* Adds arbitrary number of analysing modules.
* They are processed using arguments
*/
Analyzer.prototype.addChildren = function()
{
this.m_children = [];
for (var i = 0; i < arguments.length; i++) {
var analyzer = arguments[i];
require("process", analyzer);
this.m_children.push(analyzer);
}
}
/**
* Base class processes the XML node.
* @tparam XMLElement node
*/
Analyzer.prototype.process = function(node)
{
for (var i = 0; i < this.m_children.length; i++) {
this.m_children[i].process(node);
}
}
/**
* @class IERecProcessor.
*
* A base class that performs a single processing step on a recorded web transaction in Internet Explorer.
*/
function IERecProcessor()
{
}
IERecProcessor.prototype = new Analyzer();
IERecProcessor.prototype.isAPP = function(node) { return node.tagName == "APP"; }
IERecProcessor.prototype.isNC = function(node) { return node.tagName == "NC"; }
IERecProcessor.prototype.isDC = function(node) { return node.tagName == "DC"; }
IERecProcessor.prototype.isBN = function(node) { return node.tagName == "BN"; }
IERecProcessor.prototype.isAction = function(node) { return node.tagName == "action"; }
IERecProcessor.prototype.isRedirect = function(node) { return node.tagName == "Redirect"; }
IERecProcessor.prototype.isFrame = function(node) { return node.tagName == "FRAME"; }
IERecProcessor.prototype.isHtml = function(node) { return node.tagName == "html"; }
IERecProcessor.prototype.isForm = function(node) { return node.tagName == "form"; }
IERecProcessor.prototype.getTagName = function(node) {return node.tagName; }
IERecProcessor.prototype.getWindowIndex = function(node)
{
var windowIndexStr = node.getAttribute("windowIndex");
if (windowIndexStr == "") {
return 0;
} else {
var windowIndex = parseInt(windowIndexStr);
// windowIndex is suppressed when it should be 0.
if (windowIndex > 0) {
// usual case
} else {
// the NaN case. Cannot optimize this because
// NaN > 0 is false
// NaN < 0 is false
windowIndex = 0;
}
}
return windowIndex;
}
IERecProcessor.prototype.getPreviousNode = function(node, tagName, func)
{
var prev = node.previousSibling;
while (prev) {
if (tagName == prev.tagName) {
if (!func) {
return prev;
} else if (func.call(this, prev)) {
return prev;
}
}
prev = prev.previousSibling;
}
return null;
}
IERecProcessor.prototype.getNextNode = function(node, tagName, func)
{
var next = node.nextSibling;
while (next) {
if (tagName == next.tagName) {
if (!func) {
return next;
} else if (func.call(this, next)) {
return next;
}
}
next = next.nextSibling;
}
return null;
}
IERecProcessor.prototype.getFormAction = function(node)
{
return this._getProperty(node, "formAction", "value");
}
IERecProcessor.prototype.getFormName = function(node)
{
return this._getProperty(node, "formName", "value");
}
IERecProcessor.prototype.getFormId = function(node)
{
return this._getProperty(node, "formId", "value");
}
IERecProcessor.prototype.getFormMethod = function(node)
{
return this._getProperty(node, "method", "value");
}
IERecProcessor.prototype.getChildNode = function(node, name)
{
return node.selectSingleNode(name);
}
IERecProcessor.prototype.getTime = function(node)
{
return parseInt(node.getAttribute("time"));
}
IERecProcessor.prototype.getURL = function(node)
{
return this._getProperty(node, "url", "value");
}
IERecProcessor.prototype.getFrame = function(node)
{
return node.getAttribute("frame");
}
IERecProcessor.prototype.removeURL = function(node)
{
return this._removeProperty(node, "url", "value");
}
IERecProcessor.prototype.setURL = function(node, value)
{
this._setProperty(node, "url", "value", value);
}
IERecProcessor.prototype.getPropertyNode = function(node, prop)
{
var childNodes = node.childNodes;
if (childNodes) {
for (var i = 0; i < childNodes.length; i++) {
var childNode = childNodes[i];
if (childNode.tagName == prop) {
return childNode;
}
}
}
return null;
}
IERecProcessor.prototype.getProperty = function(node, prop)
{
return this._getProperty(node, prop, "value");
}
IERecProcessor.prototype.setProperty = function(node, prop, value)
{
this._setProperty(node, prop, "value", value);
}
IERecProcessor.prototype.appendProperty = function(node, prop, value)
{
var existingProp = this.getProperty(node, prop);
if (existingProp && existingProp.length > 0) {
var propBuffer = new StringBuffer();
propBuffer.append(existingProp, ",");
propBuffer.append(value);
this.setProperty(node, prop, propBuffer.toString());
} else {
this.setProperty(node, prop, value);
}
}
IERecProcessor.prototype.appendNameValueProperty = function(node, prop, name, value)
{
var existingProp = this.getProperty(node, prop);
if (existingProp && existingProp.length > 0) {
var propBuffer = new StringBuffer();
propBuffer.append(existingProp, ",");
var newValue = encodeNameValuePairDelimiters(value);
if (existingProp.indexOf(name + "=") != -1) {
var pairs = existingProp.split(",");
var newPair = name + "=" + encodeNameValuePairDelimiters(value);
for (var i in pairs) {
if (pairs[i] == newPair) {
return false;
}
}
stderr.log("entry " + name + " is already added.");
}
propBuffer.append(name + "=" + encodeNameValuePairDelimiters(value));
this.setProperty(node, prop, propBuffer.toString());
} else {
var newEntry = name + "=" + encodeNameValuePairDelimiters(value);
this.setProperty(node, prop, newEntry);
}
return true;
}
IERecProcessor.prototype.getName = function(node)
{
return this._getProperty(node, "name", "value");
}
IERecProcessor.prototype.getTargetFrameName = function(node)
{
return node.getAttribute("targetFrameName");
}
IERecProcessor.prototype.removeTargetFrameName = function(node)
{
node.removeAttribute("targetFrameName");
}
IERecProcessor.prototype._getProperty = function (node, nodeName, attrName)
{
var childNodes = node.childNodes;
if (childNodes) {
for (var i = 0; i < childNodes.length; i++) {
var childNode = childNodes[i];
if (childNode.tagName == nodeName) {
return childNode.getAttribute(attrName);
}
}
}
return null;
}
IERecProcessor.prototype._setProperty = function (node, propertyName, attrName, value)
{
var childNodes = node.childNodes;
if (childNodes) {
for (var i = 0; i < childNodes.length; i++) {
var childNode = childNodes[i];
if (childNode.tagName == propertyName) {
childNode.setAttribute(attrName, value);
return;
}
}
var attrs = {};
attrs[propertyName] = value;
var newNode = this.addProperties(node, node.tagName, attrs, this.m_mediator.getTxnManager());
}
}
IERecProcessor.prototype._removeProperty = function (node, nodeName, attrName)
{
var childNodes = node.childNodes;
if (childNodes != null) {
for (var i = 0; i < childNodes.length; i++) {
var childNode = childNodes[i];
if (childNode.tagName == nodeName && childNode.getAttribute(attrName) != null) {
node.removeChild(childNode);
}
}
}
}
IERecProcessor.prototype.addTransactionProperty = function (node, propertyName, propertyValue, delim)
{
if (node) {
var existingValue = node.getAttribute(propertyName);
if (existingValue && existingValue.length > 0) {
node.setAttribute(propertyName, existingValue + delim + propertyValue);
} else {
node.setAttribute(propertyName, propertyValue);
}
}
}
IERecProcessor.prototype.getAncestor = function(node, tagName)
{
var pNode = node.parentNode;
while (pNode) {
var tag = pNode.tagName;
if (tag == tagName) {
return pNode;
} else if (tag == "transaction") {
// root
return null;
}
pNode = pNode.parentNode;
}
return null;
}
IERecProcessor.prototype.foreachNode = function(nodes, func)
{
for (var i = 0; i < nodes.length; i++) {
func.call(this, nodes[i]);
}
}
IERecProcessor.prototype.foreachNodeReverse = function(nodes, func)
{
for (var i = nodes.length - 1; i >= 0; i--) {
func.call(this, nodes[i]);
}
}
IERecProcessor.prototype.addProperties = function(node, tag, attrs, txnManager)
{
if (attrs) {
for (var prop in attrs) {
var value = attrs[prop];
if (value == null) {
continue;
} else if (prop == "windowIndex") {
if (parseInt(value) > 0) {
node.setAttribute(prop, "" + value);
}
} else if (prop == "time" && value > 0) {
node.setAttribute(prop, "" + value);
} else if (prop == "parentId" && value >= 0) {
node.setAttribute(prop, "" + value);
} else if (prop == "targetFrameName" && value != "") {
node.setAttribute(prop, value);
} else if (prop == "frame" && value != "") {
node.setAttribute(prop, value);
} else if (prop == "htmlId" && value >= 0) {
node.setAttribute(prop, value);
} else if (prop == "DCId" && value >= 0) {
node.setAttribute(prop, value);
} else if (prop == "state" && value != "") {
node.setAttribute(prop, value);
} else if (prop == "formParentId" && value >= 0) {
node.setAttribute(prop, value);
} else if (tag == "action" && value != "") {
node.setAttribute(prop, value);
} else if (tag == "step") {
node.setAttribute(prop, value);
} else if (tag == Constants.FAIL_FOUND) {
node.setAttribute(prop, value);
} else if (tag == Constants.SUC_NOT_FOUND) {
node.setAttribute(prop, value);
} else if (tag == Constants.BROWSER_ERROR) {
node.setAttribute(prop, value);
} else if (tag == Constants.DOM_ERROR) {
node.setAttribute(prop, value);
} else if (tag == Constants.GROUP) {
node.setAttribute(prop, value);
} else if (tag == "input") {
if (value == null || value == undefined) {
stderr.log('A form element value is null or undefined');
value = "";
}
node.setAttribute(prop, value);
} else if (value != "") {
var childElement = txnManager.createElement(prop);
childElement.setAttribute("value", value);
node.appendChild(childElement);
}
}
}
}
IERecProcessor.prototype._getRegexMatch = function(htmlHelper, regex, htmlId, caseSensitive)
{
try {
var matches = new Object();
htmlHelper.checkForRegExp2(regex, htmlId, caseSensitive, matches);
if (matches.length > 0) {
return matches[matches.length - 1];
}
} catch (e) { this._handleException(arguments.callee, e); }
return null;
}
IERecProcessor.prototype._getRegexMatches = function(htmlHelper, regex, htmlId, caseSensitive)
{
var matches = new Object();
try {
htmlHelper.checkForRegExp2(regex, htmlId, caseSensitive, matches);
return matches;
} catch (e) { this._handleException(arguments.callee, e); }
return matches;
}
IERecProcessor.prototype.validateDescriptor = function(htmlHelper, htmlNode, descriptor)
{
try {
if (descriptor && htmlNode) {
var htmlId = parseInt(htmlNode.getAttribute("htmlId"));
var descPatn = descriptor.getPattern();
var descRawVal = descriptor.getMarkedValue(false);
var match = this._getRegexMatch(htmlHelper, descPatn, htmlId, true);
if (match == descRawVal) {
return true;
} else {
/* stderr.log("validateDescriptor failed with");
stderr.log("pattern =" + descPatn);
stderr.log("expected value=" + descRawVal);
stderr.log("matched value=" + match);
*/
}
}
} catch (e) { this._handleException(arguments.callee, e); }
return false;
}
IERecProcessor.prototype.applyDescriptor = function(htmlHelper, bnNode, htmlNode, descriptor)
{
try {
if (this.validateDescriptor(htmlHelper, htmlNode, descriptor)) {
var url = this.getURL(bnNode);
// descVal is the actual value after encoding.
var descVal = descriptor.getMarkedValue();
var dcId = htmlNode.getAttribute("DCId");
if (dcId > 0) {
var dcNode = this.m_mediator.getNodeWithId(dcId);
if (dcNode) {
// must make sure there is a BN as its parent.
var sourceBNNode = this.getAncestor(dcNode, "BN");
if (sourceBNNode) {
if (url && url.length > 0 && descVal && descVal.length > 0) {
var lastIndex = url.indexOf(descVal);
// stderr.log("lastIndex" + lastIndex);
if (lastIndex != -1) {
var urlBuffer = new StringBuffer();
// append everything before
urlBuffer.append(url.substr(0, lastIndex));
// append the descriptor (variable) name
urlBuffer.append("[", descriptor.getName(), "]");
// append everything after
urlBuffer.append(url.substr(lastIndex + descVal.length));
// update all the properties at once.
this.setProperty(bnNode, "query_param", descVal);
this.setURL(bnNode, urlBuffer.toString());
if (this.appendNameValueProperty(htmlNode, "regex", descriptor.getName(), descriptor.getPattern())) {
this.appendProperty(htmlNode, "regmd", descriptor.getEncoding());
}
this._setURLProcessed(bnNode);
}
} else if (descriptor) {
// although this is a case we need to handle, this is not common.
stderr.log("Cannot match " + descriptor.getPattern() + " to " + descriptor.getMarkedValue(false), true);
}
} else {
stderr.log("Cannot locate a parent BN node for DC with nodeId " + dcId);
}
} else {
stderr.log("Cannot find a DC node with nodeId " + dcId);
}
} else {
stderr.log("Cannot find a DC node for html with nodeId " + htmlNode.getAttribute("nodeId"));
}
} else {
stderr.log("Cannot find match " + bnNode.getAttribute("nodeId"));
}
} catch (e) { this._handleException(arguments.callee, e); }
}
IERecProcessor.prototype.isPlayable = function (node)
{
var requestMode = node.getAttribute(Constants.REQUEST_MODE);
if (!requestMode) {
return true;
} else if (requestMode == Constants.SCRIPT_REDIRECT) {
return true;
} else if (requestMode == Constants.ONLOAD_SUBMIT) {
return true;
} else {
return false;
}
}
/**
* Checks if a URL is processed or not.
* @private
*/
IERecProcessor.prototype._isURLProcessed = function(node)
{
return (node.getAttribute("OK") == "true");
}
/**
* Mark a URL as processed.
* @private
*/
IERecProcessor.prototype._setURLProcessed = function(node)
{
node.setAttribute("OK", "true");
}
IERecProcessor.prototype._isActionProcessed = function(node)
{
return (node.getAttribute("OK") == "true");
}
IERecProcessor.prototype._setActionProcessed = function(node)
{
node.setAttribute("OK", "true");
}
IERecProcessor.prototype._getTimeOffset = function()
{
return (new Date()).valueOf() - this.m_startTime;
}
/**
* Handle exceptions.
* @private
*/
IERecProcessor.prototype._handleException = function (func, e)
{
handleException(IERecProcessor, func, e);
}
/**
* @class IERecCauseAnalyzer.
*
This module analyzes the causality between browser events.
This module figures out which BeforeNavigate, NavigateComplete2 and
DocumentComplete events are related. This is not simple because the URL user
sends out may or may not match the URL for the corresponding NavigateComplete2
or DocumentComplete event (due to 302 redirects).
Consider the following events on the same request:
An important goal is to make sure the five events E, D, C, B and A are all
chained. To achieve this, consider the following four simpler rules:
It is possible to have multiple request and redirect events (ie A, B, C, B',
C', D, E, where B' and C' are additional redirects. (Oracle SSO)
Implementing these rules separately will be inefficient, as DOM traversal can
be expensive. An efficient algorithm is to apply these four rules in reverse
chronological order, where canCause encapsulates the rules specified above.
for (var i = nodes.length - 1; i >= 0; i--) {
var node = nodes[i];
var prev = node.previousSibling;
while (prev) {
if (this.canCause(prev, node)) {
prev.appendChild(node);
}
prev = prev.previousSibling;
}
}
This algorithm performs the following transformation for the case above:
in to
Average performance should be ~O(n), unless most of the events are totally
unrelated, in which case it is O(n^2).
We did some performance analysis, because initially this single rule was slow.
For a simple web transaction with about 10 user actions, the recorder generated
about 200 events, and this algorithm took around 20 seconds to complete.
Performance profiling suggests the reason is due to large number of DOM access
(getting and comparing node tag name, attributes). With some simple
optimization, this algorithm runs in the order of few hundred milliseconds for
analyzing 200 events.
This algorithm is greedy. There may be multiple causality chains for a given
set of events. This either means we didn’t capture enough information or it
doesn't matter. For example, in the following example, it really doesn't matter
which BeforeNavigate event matches with which DocumentComplete event.
BeforeNavigate url="a.jsp" time=100
BeforeNavigate url="a.jsp" time=00”
DocumentComplete url="a.jsp" time=00”
DocumentComplete url="a.jsp" time=10
*/
function IERecCauseAnalyzer(mediator)
{
/**
* @private
*/
this.m_mediator = mediator;
/**
* @private
*/
this.m_startTime = (new Date()).getTime();
}
IERecCauseAnalyzer.prototype = new IERecProcessor();
/**
* Analyzes the web transaction. Any changes are applied
* to the XML node directly.
* @tparam XMLNode node the recorded web transaction in XML format.
*/
IERecCauseAnalyzer.prototype.process = function(node)
{
try {
this._stackTrace(arguments.callee, " start");
this.foreachNodeReverse(node.selectNodes("APP|DC|NC|Redirect"), this._processNode);
// for each FRAME node, find its corresponding BN.
this.foreachNodeReverse(node.selectNodes("FRAME"), this._processFrame);
// this has to execute after this._processFrame,
// this is because some bad BNs may have a corresponding FRAME
// association with a FRAME node.
this.foreachNodeReverse(node.selectNodes("BN"), this._removeBadBN);
// connect FRAME node and its corresponding BN and move it under the top level BN.
var associateFrameFunc = function (foundIdNode) {
var frameBNNode = this.getAncestor(foundIdNode, "BN");
if (frameBNNode) {
var url = this.getURL(frameBNNode);
/* No need to compare the URLs with the unsupported URLs
* because it is already done in this._removeBadBN
if (url == "about:blank") {
// remove it
frameBNNode.parentNode.removeChild(frameBNNode);
} else if (url.indexOf("javascript:") == 0) {
// remove it
frameBNNode.parentNode.removeChild(frameBNNode);
} else if (url.indexOf("res:") == 0) {
// remove it
frameBNNode.parentNode.removeChild(frameBNNode);
} else */ {
var foundId = foundIdNode.getAttribute("value");
var foundNode = this.m_mediator.getNodeWithId(foundId);
if (foundNode) {
var bnNode = this.getAncestor(foundNode, "BN");
if (bnNode) {
bnNode.appendChild(frameBNNode);
} else {
debug.log("bnNode not found for foundId " + foundId);
}
} else {
debug.log("foundNode not found " + foundId);
}
}
}
}
this.foreachNode(node.selectNodes("//foundId"), associateFrameFunc);
this._stackTrace(arguments.callee, " end");
} catch (e) { this._handleException(arguments.callee, e); }
}
IERecCauseAnalyzer.prototype._processNode = function (node)
{
try {
var prev = node.previousSibling;
var parentId = node.getAttribute("parentId");
var isParentValid = parentId != null && parentId >= 0;
var nodeURL = this.getURL(node);
if (nodeURL) {
nodeURL = nodeURL.toLowerCase();
}
var windowIndex = this.getWindowIndex(node);
var size = 0;
while (prev) {
if (isParentValid && parentId == prev.getAttribute("nodeId")) {
prev.appendChild(node);
return size;
}
if (node.getAttribute("nodeId") == "679" && prev.getAttribute("nodeId") == "670") {
stderr.log("matching " + this._canCause(prev, node, nodeURL, windowIndex, null));
}
if (this._canCause(prev, node, nodeURL, windowIndex, null)) {
prev.appendChild(node);
return size;
}
size++;
prev = prev.previousSibling;
}
return 0;
} catch (e) { this._handleException(arguments.callee, e); }
}
/**
* TODO
* @private
*/
IERecCauseAnalyzer.prototype._processFrame = function(node)
{
try {
var prev = node.previousSibling;
var parentId = node.getAttribute("parentId");
var nodeURL = this.getURL(node);
if (nodeURL) {
nodeURL = nodeURL.toLowerCase();
}
var windowIndex = this.getWindowIndex(node);
var nodeAttrs = {};
if (node.tagName == "FRAME") {
// the foundId is the nodeId of the corresponding DC event,
// this is not the DC event of the frame, but the
// DC event of the parent frame.
var foundId = parseInt(this.getProperty(node, "foundId"));
if (foundId >= 0) {
var dcNode = this.m_mediator.getNodeWithId(foundId);
if (dcNode) {
// the DC node should have come after the FRAME node
// therefore, it should already have found a NC parent node.
var bnNode = this.getAncestor(dcNode, "BN");
if (bnNode) {
var bnNodeTime = parseInt(bnNode.getAttribute("time"));
var dcNodeTime = parseInt(dcNode.getAttribute("time"));
nodeAttrs["BNTime"] = bnNodeTime;
nodeAttrs["DCTime"] = dcNodeTime;
nodeAttrs["name"] = this.getProperty(node, "name");
}
}
}
}
var isParentValid = parentId != null && parentId >= 0;
var size = 0;
while (prev) {
if (isParentValid && parentId == prev.getAttribute("nodeId")) {
prev.appendChild(node);
return size;
}
if (this._canCauseFrame(prev, node, nodeURL, windowIndex, nodeAttrs)) {
prev.appendChild(node);
return size;
}
size++;
prev = prev.previousSibling;
}
return 0;
} catch (e) { this._handleException(arguments.callee, e); }
}
/**
* TODO
* @private
*/
IERecCauseAnalyzer.prototype._canCause = function(prev, node, nodeURL, windowIndex, nodeAttrs)
{
var tagName = node.tagName;
if (tagName == "APP") {
return this._canCauseAPP(prev, node, nodeURL, windowIndex);
} else if (tagName == "DC") {
return this._canCauseDC(prev, node, nodeURL, windowIndex);
} else if (tagName == "NC") {
return this._canCauseNC(prev, node, nodeURL, windowIndex);
}
return false;
}
/**
* TODO
* @private
*/
IERecCauseAnalyzer.prototype._canCauseAPP = function(prev, node, nodeURL, windowIndex)
{
var result = false;
if ((this.isBN(prev) || this.isAPP(prev) || this.isRedirect(prev))) {
var url = this.getURL(prev);
if (url) {
url = url.toLowerCase();
}
// APP nodes does not have windowIndex
result = (url == nodeURL); // && (this.getWindowIndex(prev) == windowIndex);
if (result) {
this.removeURL(node);
}
}
return result;
}
/**
* TODO
* @private
*/
IERecCauseAnalyzer.prototype._canCauseNC = function(prev, node, nodeURL, windowIndex)
{
var result = false;
if (this.isAPP(prev) || this.isRedirect(prev) ||
(this.isBN(prev) && this.getChildNode(prev, "APP") == null &&
this.getChildNode(prev, "NC") == null)) {
var url = this.getURL(prev);
if (url) {
url = url.toLowerCase();
}
result = (url == nodeURL) && (this.getWindowIndex(prev) == windowIndex || this.isAPP(prev));
if (result) {
this.removeURL(node);
}
}
return result;
}
/**
* TODO
* @private
*/
IERecCauseAnalyzer.prototype._canCauseDC = function(prev, node, nodeURL, windowIndex)
{
var result = false;
if (this.isNC(prev) &&
this.getChildNode(prev, "DC") == null) {
var url = this.getURL(prev);
if (url) {
url = url.toLowerCase();
}
result = (url == nodeURL) && (this.getWindowIndex(prev) == windowIndex);
if (result) {
this.removeURL(node);
}
}
return result;
}
/**
* TODO
* @private
*/
IERecCauseAnalyzer.prototype._canCauseFrame = function(prev, node, nodeURL, windowIndex, nodeAttrs)
{
var result = false;
// the cause of a frame is a BN event (for the frame itself).
// the name must match
// the BN must not already have a FRAME child
// the window index must match.
//
if (nodeAttrs) {
var name = nodeAttrs["name"];
if (this.isBN(prev) &&
this.getTargetFrameName(prev) == name &&
this.getChildNode(prev, "FRAME") == null &&
this.getWindowIndex(prev) == windowIndex) {
var bnNodeTime = nodeAttrs["BNTime"];
var dcNodeTime = nodeAttrs["DCTime"];
var pvNodeTime = parseInt(prev.getAttribute("time"));
// the NC node of the parent frame exist
// we need to make sure the bnNode (for the frame)
// comes after the NC node,
// and come before the DC node.
if (bnNodeTime <= pvNodeTime && pvNodeTime <= dcNodeTime) {
prev.setAttribute(Constants.REQUEST_MODE, Constants.FRAME);
this.removeTargetFrameName(node);
result = true;
// stderr.log("found " + node.getAttribute("nodeId"));
} else {
// stderr.log("inner if" + node.getAttribute("nodeId") + bnNodeTime + " " +pvNodeTime + " " +dcNodeTime);
}
} else {
// stderr.log("inner if 2:" + node.getAttribute("nodeId") + " " + this.isBN(prev) + " " + this.getTargetFrameName(prev) + " " + name);
}
} else {
// stderr.log("inner if 3");
}
return result;
}
/**
* node is the root node.
*/
IERecCauseAnalyzer.prototype._removeBadBN = function(bnNode)
{
try {
var url = this.getURL(bnNode);
if (url == "about:blank") {
bnNode.parentNode.removeChild(bnNode);
} else if (url.indexOf("javascript:") == 0) {
bnNode.parentNode.removeChild(bnNode);
} else if (url.indexOf("res:") == 0) {
bnNode.parentNode.removeChild(bnNode);
}
} catch (e) { this._handleException(arguments.callee, e); }
}
/**
* TODO
* @private
*/
IERecCauseAnalyzer.prototype._getTimeOffset = function()
{
return (new Date()).valueOf() - this.m_startTime;
}
/**
* Handles stack trace
* @private
*/
IERecCauseAnalyzer.prototype._stackTrace = function (func, msg)
{
handleStackTrace(IERecCauseAnalyzer, func, msg);
}
/**
* Handles exception
* @private
*/
IERecCauseAnalyzer.prototype._handleException = function (func, e)
{
handleException(IERecCauseAnalyzer, func, e);
}
/**
* @class IERecAnalyzer.
This module analyzes a recorded web transaction.
Events are manipulated as an in-memory XML. Initially, it is simply a linear
list of events. We need to analyze them in order to derive useful information.
During recording, we want to analyze the causality between events, remove
redundant data, and extract out common information. For example, the following
events are related.
A click event => a form submission event => a before navigate event => a HTTP
request event => a HTTP redirect event => a navigation complete event => a
document complete event. (=> means caused)
Having this information allows us to correlate between actual user actions and
subsequent HTTP requests.
During playback, we want to analyze the time of all the events to determine how
quickly each step took to execute, and whether there are any errors.
Data
The events are stored in an in-memory XML. For example, suppose initially
there are four events:
After analysis, if event B (a frame URL) was caused by event A, and event D (a
redirect) was caused by event C, we may rewrite the XML document to something
like this, and conclude that this web transaction has two steps.
Rules
The analysis phase is implemented as an order set of rules. Each rule modifies
the XML document slightly. The effects are accumulative.
There may be a lot of rules in the analysis phase, some only relevant to
specific web applications. To effectively organize these rules, each JavaScript
module implements a single method process(node)
, where the node
parameter corresponds to the root of the XML.
*/
function IERecAnalyzer(mediator)
{
/**
* TODO
* @private
*/
this.m_mediator = mediator;
}
IERecAnalyzer.prototype = new Analyzer();
/**
* Analysis processes
* @tparam XMLElement node
*/
IERecAnalyzer.prototype.process = function(node)
{
var time = (new Date()).valueOf();
(new IERecCauseAnalyzer(this.m_mediator)).process(node);
this._save(node, "c:\\stage1.xml");
(new HTMLAnalyzer(this.m_mediator)).process(node);
this._save(node, "c:\\stage2.xml");
(new IERedirectAnalyzer(this.m_mediator)).process(node);
this._save(node, "c:\\stage3.xml");
(new IERecRewriter(this.m_mediator)).process(node);
this._save(node, "c:\\stage4.xml");
var delta = (new Date()).valueOf() - time;
timing.log("Analyzed in " + delta + " milliseconds");
}
IERecAnalyzer.prototype._save = function(node, fileName)
{
if (this.m_mediator.getDebugMode()) {
var xmlWriter = new OutputStreamXMLFile(fileName);
xmlWriter.printNode(node);
xmlWriter.close();
}
}
/**
* @class IERedirectAnalyzer.
*/
function IERedirectAnalyzer(mediator)
{
this.m_mediator = mediator;
}
IERedirectAnalyzer.prototype = new IERecProcessor();
// go through each
IERedirectAnalyzer.prototype.process = function(node)
{
try {
this._stackTrace(arguments.callee, " start");
var childRedirects = node.selectNodes("MetaRedirect");
this.foreachNode(childRedirects, this._processRedirect);
var childNodes = node.selectNodes("BN");
this.foreachNodeReverse(childNodes, this._processBN);
this._stackTrace(arguments.callee, " end");
} catch (e) { this._handleException(arguments.callee, e); }
}
IERedirectAnalyzer.prototype._processBN = function(node)
{
try {
this._stackTrace(arguments.callee, " start");
if (node.getAttribute(Constants.REQUEST_MODE)) {
// already handled.
return;
}
// If the time stamp of two a BN and the previous DC nodes
// are very close and the only actions between them are wait or sleep,
//
// then the second BN is a redirect
// it is a META if meta tag is found
// it is a onload submit if there is a form action between
// it is a script redirect otherwise.
// always ignore the actual 302 redirect.
var previousBN = this.getPreviousNode(node, "BN");
if (previousBN) {
var aNode= previousBN.nextSibling;
var htmlNodes = [];
var prevBNWindowIndex = this.getProperty(previousBN, "windowIndex");
var currBNWindowIndex = this.getProperty(node, "windowIndex");
while (aNode != node) {
if (this.isAction(aNode)) {
var actionType = aNode.getAttribute("type");
if (actionType == Constants.sleep ||
actionType == Constants.waitForPageToLoad ||
actionType == Constants.waitForPopup ||
actionType == Constants.waitForPopupClose) {
} else {
// since there is a real action between this BN and the previous BN,
// it cannot be an redirect.
return;
}
} else if (this.isHtml(aNode)) {
htmlNodes.push(aNode);
}
aNode = aNode.nextSibling;
}
// when we get here,
// the second BN should be a redirect
//
// 2. check if there is a formNodeId
if (prevBNWindowIndex == currBNWindowIndex) {
if (node.getAttribute("formNodeId") != null) {
node.setAttribute(Constants.REQUEST_MODE, Constants.ONLOAD_SUBMIT);
node.setAttribute("redirectParentId", previousBN.getAttribute("nodeId"));
} else {
node.setAttribute(Constants.REQUEST_MODE, Constants.SCRIPT_REDIRECT);
node.setAttribute("redirectParentId", previousBN.getAttribute("nodeId"));
for (var i = 0; i < htmlNodes.length; i++) {
var htmlNode = htmlNodes[i];
this._checkLocationRewrite(node, htmlNode);
}
}
}
}
this._stackTrace(arguments.callee, " end");
} catch (e) { this._handleException(arguments.callee, e); }
}
IERedirectAnalyzer.prototype._checkLocationRewrite = function(bnNode, htmlNode)
{
try {
var htmlId = parseInt(htmlNode.getAttribute("htmlId"));
var initialRegex = "<[^>]*window.location[^>]*>";
var htmlHelper = this.m_mediator.getHTMLHelper();
var tag = this._getRegexMatch(htmlHelper, initialRegex, htmlId, false);
if (tag) {
var descriptor = DescriptorGenerator.generateDescriptor(tag);
this.applyDescriptor(htmlHelper, bnNode, htmlNode, descriptor);
}
} catch (e) { this._handleException(arguments.callee, e); }
}
IERedirectAnalyzer.prototype._processRedirect = function(node)
{
try {
this._stackTrace(arguments.callee, " start");
var url = this.getURL(node);
if (url) {
url = url.toLowerCase();
}
// temporarily set nextBN to the current node.
var nextBN = node;
while (nextBN = this.getNextNode(nextBN, "BN")) {
var urlBN = this.getURL(nextBN);
if (urlBN) {
urlBN = urlBN.toLowerCase();
}
// FIXME is this indexOf match sufficient?
if (urlBN && urlBN.indexOf(url) != -1) {
var timeout = this.getProperty(node, "timeout");
// beacon does not treat meta redirect with a positive timeout
if (timeout > 0) {
nextBN.setAttribute(Constants.REQUEST_MODE, Constants.SCRIPT_REDIRECT);
} else {
nextBN.setAttribute(Constants.REQUEST_MODE, Constants.META_REDIRECT);
}
nextBN.setAttribute("redirectParentId", node.getAttribute("nodeId"));
break;
}
}
this._stackTrace(arguments.callee, " end");
} catch (e) { this._handleException(arguments.callee, e); }
}
/**
* Handle exceptions.
* @private
*/
IERedirectAnalyzer.prototype._handleException = function (func, e)
{
handleException(IERedirectAnalyzer, func, e);
}
/**
* Handles stack trace
* @private
*/
IERedirectAnalyzer.prototype._stackTrace = function (func, msg)
{
handleStackTrace(IERedirectAnalyzer, func, msg);
}
/**
* @class IERecRewriter.
This module simplifies a recorded web transaction in Internet Explorer.
onmouseover onmousedown onbeforeactivate onactivate onfocus onfocusin onmouseup onclick onfocusout onblur onmouseoutIf we only care about onclick events, then recording will not work for application that goes to a subsequent page upon an onmousedown event. Due to time limitation, we have chosen a simple implementation for this release. Since we only capture onmousedown and onclick events, and onmousedown always occur before onclick, when we detect both events (on the same element) occur, and there are no other user action events in between them, we suppress the second event, and mark the first event as a click user action. This is important, because the click user action must appear before the subsequent network request event. Similar treatments need to be performed for onkeydown and onkeypress events.
// mediator – The caller object. // timer – The timer object. var m_Listener = new IEDOMEventListener(mediator, timer); m_Listener.start(); ... m_Listener.stop();
mediator.onDOMEvent(params, time)
whenever a
user action is recorded. The params parameter contains all the information
about the user action (including command and attributes).
*/
function IEDomEventListener(mediator, timer)
{
this.init(mediator, timer);
}
IEDomEventListener.prototype = new EventListener();
/**
* Initializes members.
* @private
*/
IEDomEventListener.prototype.init = function(mediator, timer)
{
try {
EventListener.prototype.init.call(this, mediator, timer);
this.setName("IEDomEventListener");
var requiredFuncs = [
"recordFormInputs",
"onDOMEvent",
"getWindowIndex"];
this.checkRequiredFuncs(requiredFuncs);
this.m_highlight = false;
// this.m_canAddWaitTraffic = false;
this.m_lastMouseActionNode = null;
this.m_lastMouseAction = null;
this.m_lastMouseElem = null;
this.m_lastKeyboardActionNode = null;
this.m_lastKeyboardAction = null;
this.m_lastKeyboardElem = null;
this.m_keypressedbuffer = null;
this.m_keypressedtime = null;
// TODO revisit in next release
this.m_fnrex = new RegExp("[^\{]*\{\(.*\)}", "g");
this.m_maskPassword = false;
var that = this;
var events = [
Constants.onload,
Constants.onunload,
Constants.onmouseover,
Constants.onmouseout,
Constants.onmousedown,
Constants.onkeydown,
Constants.onkeypress,
Constants.onkeyup,
Constants.onsubmit,
Constants.onclick,
Constants.onchange,
Constants.onfocus,
Constants.onblur,
Constants.ondblclick,
Constants.onpropertychange];
/*
for (var i = 0; i < events.length; i++) {
var evtName = events[i];
that["m_"+evtName] = function(e) { that[evtName](e||window.event); };
}
*/
foreach(events, function(evtName) { that["m_"+evtName] = function(e) { that[evtName](e||window.event); }; });
this._toggleAttachEvents = function (obj, func) {
var elemEvents = [
Constants.onclick,
Constants.onmousedown,
Constants.onblur,
Constants.onfocus,
Constants.ondblclick];
/**
for (var i = 0; i < elemEvents.length; i++) {
var evtname = elemEvents[i];
func(obj, evtName, that["m_" + evtName]);
}
*/
foreach(elemEvents, function(evtName) { func(obj, evtName, that["m_"+evtName]); });
}
this._detachImportantEvents = function (obj) {
this._toggleAttachEvents(obj, this._detachEvent);
}
this._reattachImportantEvents = function (obj) {
this._toggleAttachEvents(obj, this._reattachEvent);
}
this._reattachEvent = function (elem, evt, func) {
if (elem && elem.detachEvent && elem.attachEvent && evt && func) {
elem.detachEvent(evt, func);
elem.attachEvent(evt, func);
}
}
this._detachEvent = function (elem, evt, func) {
if (elem && elem.detachEvent && evt && func) {
elem.detachEvent(evt, func);
}
}
this._matchTitle = function(n1, n2) {
return n1.title == n2.title;
}
this._matchAlt = function(n1, n2) {
return n1.alt == n2.alt;
}
this._matchAny = function(n1, n2) {
return true;
}
this._matchValueAndType = function(n1, n2) {
return n1.value == n2.value && n2.type == n2.type;
}
this._matchSrc = function(n1, n2) {
try
{
return Util.compareURLByComponentsTrailingAuthority(n1.src, n2.src);
}
catch (e)
{
return n1.src == n2.src;
}
}
this._getTagName = function(elem)
{
return elem.tagName ? elem.tagName.toLowerCase() : "";
}
this._getType = function(elem)
{
return elem.type ? elem.type.toLowerCase() : "";
}
} catch (e) { this._handleException(arguments.callee, e); }
}
/**
* Starts listening to DOM events.
*/
IEDomEventListener.prototype.start = function ()
{
}
/**
* Stops listening to DOM events.
*/
IEDomEventListener.prototype.stop = function ()
{
}
/**
* Sets the mask password mode. If true, the password
* value is masked out.
* @tparam bool mode the mask password mode.
*/
IEDomEventListener.prototype.setMaskPasswordMode = function (mode)
{
this.m_maskPassword = mode;
}
/**
* Sets the highlight mode. If true, any activated element would be highlighted.
* Very useful for debugging.
* @tparam bool mode the highlight mode
*/
IEDomEventListener.prototype.setHighlightMode = function (mode)
{
this.m_highlight = mode;
}
/**
* Handles NC or DC event.
* @private
*/
IEDomEventListener.prototype._onNCDC = function (frame, url)
{
try {
var doc = this.m_mediator._getDocument(frame);
if (doc) {
this._reattachEvent(doc, Constants.onmouseover, this.m_onmouseover);
this._reattachEvent(doc, Constants.onmouseout, this.m_onmouseout);
this._reattachEvent(doc, Constants.onkeydown, this.m_onkeydown);
this._reattachEvent(doc, Constants.onkeypress, this.m_onkeypress);
this._reattachEvent(doc, Constants.onkeyup, this.m_onkeyup);
this._reattachEvent(doc, Constants.onpropertychange, this.m_onpropertychange);
}
} catch (e) { this._handleException(arguments.callee, e); }
}
/**
* Handles the NavigateComplete2 event.
*/
IEDomEventListener.prototype.onNavigateComplete2 = function(frame, url, time, node)
{
/*
var win = frame.Document.parentWindow;
this._reattachEvent(win, Constants.onload, this.m_onload);
this._reattachEvent(win, Constants.onunload, this.m_onunload);
*/
this._onNCDC(frame, url);
}
/**
* Handles the DocumentComplete event.
*/
IEDomEventListener.prototype.onDocumentComplete = function (frame, url, time, node)
{
try {
this._onNCDC(frame, url);
this.m_keypressedbuffer = null;
this.m_keypressedtime = null;
var forms = null;
var formsLength = 0;
var doc = this.m_mediator._getDocument(frame);
try
{
if (doc) {
forms = doc.forms;
formsLength = forms.length;
}
}
catch (ex)
{
stderr.log("exception thrown when parsing doc.forms");
// ignore
}
var mediator = this.m_mediator;
if (forms) {
for (var i = 0, n = formsLength; i < n; i++) {
var form = forms[i];
this.m_mediator.recordFormInputs(form, "before", i, node, time);
form.old_submit = form.submit;
var that = this;
var formIndex = i;
form.submit = function () {
mediator.recordFormInputs(this, "submit", formIndex, null, that.getTime());
this.old_submit();
}
this._reattachEvent(form, Constants.onsubmit, this.m_onsubmit);
}
}
} catch (e) { this._handleException(arguments.callee, e); }
}
/**
* Handles onmouseover event.
* @tparam HTMLEvent evt
*
* Registers all handled events to the element, this include all bubbled events
* in case client's code contain cancelBubble = true.
*/
IEDomEventListener.prototype.onmouseover = function (evt)
{
try {
if (evt) {
log(evt);
var elem = evt.srcElement;
if (elem) {
// in case application sets bubble to false for child nodes,
// we still have a handle on onmouseover
var children = elem.childNodes;
for (var i = 0; i < children.length; i++) {
var child = children[i];
if (child.onmouseover != null) {
this._reattachEvent(child, Constants.onmouseover, this.m_onmouseover);
this._reattachEvent(child, Constants.onmouseout, this.m_onmouseout);
}
}
this._reattachImportantEvents(elem);
// onchange event applies to these three tags only.
var tagName = this._getTagName(elem);
var type = this._getType(elem);
if (elem.onchange || (tagName == "input" && type == "text") || tagName == "select" || tagName == "textarea") {
this._reattachEvent(elem, Constants.onchange, this.m_onchange);
}
this._highlight(elem, true);
}
}
} catch (e) { this._handleException(arguments.callee, e); }
}
/**
* Unregisters all handled events on a particular element.
* @tparam HTMLEvent evt
*/
IEDomEventListener.prototype.onmouseout = function (evt)
{
try {
if (evt) {
log(evt);
var elem = evt.srcElement;
if (elem) {
this._highlight(elem, false);
// ----------------------------------------
// cannot detach onchange, because this will be called
// before the onchange event.
// ----------------------------------------
this._detachImportantEvents(elem);
var children = elem.childNodes;
for (var i = 0; i < children.length; i++) {
var child = children[i];
if (child.onmouseover != null) {
this._detachEvent(child, Constants.onmouseover, this.m_onmouseover);
this._detachEvent(child, Constants.onmouseout, this.m_onmouseout);
}
}
}
}
} catch (e) { this._handleException(arguments.callee, e); }
}
/**
* Highlights the element.
* @private
*/
IEDomEventListener.prototype._highlight = function(elem, mode)
{
try {
if (!this.m_highlight) {
return;
}
if (mode) {
var tagName = this._getTagName(elem);
if (tagName == "img") {
if (elem.style.filter != null) {
elem.oldFilter = elem.style.filter;
}
if (elem.style.opacity != null) {
elem.oldOpacity = elem.style.opacity;
}
elem.style.opacity = "0.5";
elem.style.filter = "progid:DXImageTransform.Microsoft.Alpha(opacity=50)";
} else if (tagName != "select" && tagName != "option") {
if (elem.style.backgroundImage == "") {
if (elem.style.backgroundColor != null) {
elem.oldBackgroundColor = elem.style.backgroundColor;
}
elem.style.backgroundColor = "#ffffe0";
}
}
} else {
if (elem.style.filter) {
elem.style.filter = elem.oldFilter;
}
if (elem.style.backgroundColor) {
elem.style.backgroundColor = elem.oldBackgroundColor;
}
if (elem.style.opacity) {
elem.style.opacity = elem.oldOpacity;
}
}
} catch (e) { this._handleException(arguments.callee, e); }
}
/**
* Handles property change events.
* @tparam HTMLEvent evt
*/
IEDomEventListener.prototype.onpropertychange = function (evt)
{
if (evt) {
log(evt);
}
}
/**
* Handles keyboard events.
* @private
*/
IEDomEventListener.prototype._onkeyevents = function (evt)
{
try {
var time = this.getTime();
if (evt) {
log(evt);
var elem = evt.srcElement;
if (!elem) return;
var tagName = this._getTagName(elem);
var type = this._getType(elem);
var evtType = evt.type ? evt.type.toLowerCase() : "";
// treat enter keys carefully.
if (evt.keyCode == 13 && evtType == "keyup") {
if (tagName != "textarea") {
// Enter Key
//
/* the following should be handled
* in the keydown phase.
if (this.m_keypressedbuffer != elem) {
this._ontextchanged(this.m_keypressedbuffer, evt,
this.m_keypressedtime);
this.m_keypressedbuffer = null;
this.m_keypressedtime = null;
}
*/
// process enter key directly
// this._ontextchanged(elem, evt, time);
// this._onkeypress(elem, evt, time);
return;
}
}
// a keypress or keyup is generated on elem,
// but we have a different object receiving keyboard action.
// record the value of that other element first.
// afterwards, keypressed buffer/time should be null.
if (this.m_keypressedbuffer && this.m_keypressedbuffer != elem) {
this._ontextchanged(this.m_keypressedbuffer, evt, this.m_keypressedtime);
}
// now register keyboard action with the current object.
if (this.m_keypressedbuffer == null) {
this.m_keypressedbuffer = elem;
this.m_keypressedtime = this.getTime();
}
}
} catch (e) { this._handleException(arguments.callee, e); }
}
/**
* Handles onkeyup events.
* @tparam HTMLEvent evt
*/
IEDomEventListener.prototype.onkeyup = function(evt)
{
this._onkeyevents(evt);
}
/**
* Handles onkeydown events.
* @tparam HTMLEvent evt
*/
IEDomEventListener.prototype.onkeydown = function(evt)
{
var time = this.getTime();
if (evt) {
log(evt);
var elem = evt.srcElement;
if (!elem) return;
var tagName = this._getTagName(elem);
var type = this._getType(elem);
var evtType = evt.type ? evt.type.toLowerCase() : "";
if (evt.keyCode == 13 && evtType == "keydown") {
if (tagName != "textarea") {
// Enter Key
// I think this should generate a click event
// after
if (this.m_keypressedbuffer != elem) {
this._ontextchanged(this.m_keypressedbuffer, evt,
this.m_keypressedtime);
this.m_keypressedbuffer = null;
this.m_keypressedtime = null;
}
// process enter key directly
this._ontextchanged(elem, evt, time);
this._write(Constants.keyPress, elem, "" + evt.keyCode, evt, time);
}
}
}
}
/**
* Handles onkeypress events.
* @tparam HTMLEvent evt
*/
IEDomEventListener.prototype.onkeypress = function(evt)
{
if (evt) {
log(evt);
this._onkeyevents(evt);
}
}
/**
* Handles onblur events.
* @tparam HTMLEvent evt
*/
IEDomEventListener.prototype.onblur = function(evt)
{
if (evt) {
log(evt);
}
}
/**
* Handles onfocus events.
* @tparam HTMLEvent evt
*/
IEDomEventListener.prototype.onfocus = function(evt)
{
if (evt) {
log(evt);
}
}
/**
* Handles ondblclick events.
* @tparam HTMLEvent evt
*/
IEDomEventListener.prototype.ondblclick = function(evt)
{
if (evt) {
log(evt);
}
}
/**
* Handles onmousedown events.
* @tparam HTMLEvent evt
*/
IEDomEventListener.prototype.onmousedown = function(evt)
{
if (evt) {
log(evt);
// return this._action(evt, true, Constants.mousedown);
var time = this.getTime();
var elem = evt.srcElement;
this._write(Constants.mouseDown, elem, null, evt, time);
}
}
/**
* Handles onclick events.
* @tparam HTMLEvent evt
*/
IEDomEventListener.prototype.onclick = function (evt)
{
try {
var time = this.getTime();
if (evt) {
log(evt);
var elem = evt.srcElement;
if (!elem) return;
this._ontextchanged(this.m_keypressedbuffer, evt, this.m_keypressedtime);
var tagName = this._getTagName(elem);
if (tagName == "select" ) {
this._ontextchanged(this.m_keypressedbuffer, evt, this.m_keypressedtime);
this.m_keypressedbuffer = elem;
this.m_keypressedtime = this.getTime();
} else if (tagName == "a") {
if (elem.href) {
// debug.log(elem.href);
/* html = html.replace(/&/g, "&"); */
/* var html = escapeHTML(elem.outerHTML);
debug.log(html); */
}
this._write(Constants.click, elem, null, evt, time);
} else {
/* var html = escapeHTML(elem.outerHTML);
debug.log(html);
*/
this._write(Constants.click, elem, null, evt, time);
}
}
} catch (e) { this._handleException(arguments.callee, e); }
}
/**
* Handles a click or mouse down action.
* @private
*/
IEDomEventListener.prototype._action = function (evt, check_typing, func)
{
try {
var time = this.getTime();
if (evt) {
log(evt);
var elem = evt.srcElement;
if (!elem) return;
if (check_typing) {
this._ontextchanged(this.m_keypressedbuffer, evt, this.m_keypressedtime);
}
var tagName = this._getTagName(elem);
if (tagName == "select" && func == Constants.click) {
this._ontextchanged(this.m_keypressedbuffer, evt, this.m_keypressedtime);
this.m_keypressedbuffer = elem;
this.m_keypressedtime = this.getTime();
} else {
this._write(func, elem, null, evt, time);
}
}
} catch (e) { this._handleException(arguments.callee, e); }
}
/**
* Handles onsubmit events.
* @tparam HTMLEvent evt
*/
IEDomEventListener.prototype.onsubmit = function (evt)
{
try {
var time = this.getTime();
if (evt) {
log(evt);
var form = evt.srcElement;
if (!form) return;
var formIndex = 0;
var doc = form.document;
if (doc) {
var forms = doc.forms;
if (forms) {
for (var i = 0; i < forms.length; i++) {
if (forms[i] == form) {
formIndex = i;
break;
}
}
}
}
this.m_mediator.recordFormInputs(form, "onsubmit", formIndex, null, time);
}
} catch (e) { this._handleException(arguments.callee, e); }
}
/**
* Records a single action.
* @private
*/
IEDomEventListener.prototype._write = function (func, elem, value, evt, time)
{
try {
if (!elem || !func) {
return;
}
var create = true;
// since our onclick and onkeypress handlers are executed
// after the real click and the real key press, there is
// a problem:
//
// T1 real click
// T2 BeforeNavigate2 (recorded)
// T3 our onclick handler (recorded)
//
// T3 should be less than T2, but in the above case, it is not.
//
// We use a trick that that also records mousedown handlers.
// The solution is change T2 to a onclick handler, and
// not record T5.
//
// T1 real mousedown
// T2 our mousedown handler (recorded)
// T3 real click
// T4 BeforeNavigate2 (recorded)
// T5 our click handler (recorded)
//
// The assumption here is that T2 and T5.
if (this.m_lastMouseElem == elem) {
if (this.m_lastMouseAction == Constants.mouseDown && func == Constants.click && this.m_lastMouseActionNode) {
// use the last time of mousedown
this.m_lastMouseActionNode.setAttribute("type", Constants.click);
this.m_lastMouseAction = Constants.click;
return;
}
}
if (this.m_lastKeyboardElem == elem) {
if (this.m_lastKeyboardAction == Constants.keyDown && func == Constants.keyPress && this.m_lastKeyboardActionNode) {
// use the last time of the keydown
this.m_lastKeyboardActionNode.setAttribute("type", Constants.keyPress);
this.m_lastKeyboardAction = Constants.keyPress;
return;
}
}
// if a click event causes an onchange event for select
// (typing actions are accounted for else where),
// we need to move the change event before the mousedown event
// because the mousedown event may be changed to a click event by
// our onclick handler.
/* if (func == Constants.select) {
if (this.m_lastMouseAction == Constants.mouseDown && this.m_lastMouseActionNode) {
time = parseInt(this.m_lastMouseActionNode.getAttribute("time"))-1;
} else if (this.m_lastKeyboardAction == Constants.keyDown && this.m_lastKeyboardActionNode) {
time = parseInt(this.m_lastKeyboardActionNode.getAttribute("time"))-1;
}
}
*/
var tagName = this._getTagName(elem);
var type = this._getType(elem);
// a mouse action on a (text, file, password, textarea) is not recorded.
if ((func == Constants.click || func == Constants.mouseDown ) &&
(tagName == "textarea" || (tagName == "input" && (type == "file" || type == "password" || type == "text")))) {
create = false;
}
if (create) {
var attrs = {
"type" : func,
"newValue": value,
"time": time
}
if (tagName == "area") {
if (evt.clientX) attrs.clientX = evt.clientX;
if (evt.clientY) attrs.clientY = evt.clientY;
}
if (func.indexOf("mouse") != -1 || func.indexOf("click") != -1) {
if (evt.button == 2) {
attrs.button = "right";
} else if (evt.button == 3 || evt.button == 4) {
attrs.button = "middle";
} else {
// default button is left and 1.
}
if (evt.altKey) { attrs.altKey = "true"; }
if (evt.ctrlKey) { attrs.ctrlKey = "true"; }
if (evt.shiftKey) { attrs.shiftKey = "true"; }
}
if (func.indexOf("key") != -1) {
if (evt.altKey) { attrs.altKey = "true"; }
if (evt.ctrlKey) { attrs.ctrlKey = "true"; }
// no need for shift key, it is already in the keyCode.
}
this._getRef(attrs, elem);
var node = this.m_mediator.onDOMEvent(attrs, time);
if (func.indexOf("mouse") != -1 || func.indexOf("click") != -1) {
this.m_lastMouseAction = func;
this.m_lastMouseElem = elem;
this.m_lastMouseActionNode = node;
// FIXME add altKeys, etc
} else if (func.indexOf("key") != -1) {
this.m_lastKeyboardAction = func;
this.m_lastKeyboardElem = elem;
this.m_lastKeyboardActionNode = node;
// FIXME add altKeys, etc
}
}
} catch (e) { this._handleException(arguments.callee, e); }
}
/**
* Handles onchange events.
* @tparam HTMLEvent evt
*/
IEDomEventListener.prototype.onchange = function (evt)
{
try {
var time = this.getTime();
if (evt) {
log(evt);
var elem = evt.srcElement;
var tagName = this._getTagName(elem);
var type = this._getType(elem);
// suppress keypress buffer
if (tagName == "select") {
for (var i = 0; i < elem.children.length; i++) {
var child = elem.children[i];
if (child && this._getTagName(child) == "option") {
if (child.value == elem.value) {
this._write(Constants.select, elem, child.innerHTML, evt, time);
}
}
}
}
/*
} else if (tagName == "input" && type == "text") {
this._ontextchanged(elem, evt, time);
} else if (tagName == "textarea") {
this._ontextchanged(elem, evt, time);
}
*/
}
} catch (e) { this._handleException(arguments.callee, e); }
}
/**
* Handles text changed event.
* @private
*/
IEDomEventListener.prototype._ontextchanged = function (elem, evt, time)
{
try {
if (elem) {
var tagName = this._getTagName(elem);
var type = this._getType(elem);
var record = false;
var value = elem.value;
// stderr.log("calling _ontextchanged:" + value + " at " + time);
if (tagName == "input" && type == "text") {
record = true;
} else if (tagName == "input" && type == "file") {
record = true;
} else if (tagName == "input" && type == "password") {
record = true;
if (this.m_maskPassword) {
value = "******";
}
} else if (tagName == "textarea") {
record = true;
} else {
record = false;
}
if (record) {
this._write(Constants.type, elem, value, evt, time);
if (elem == this.m_keypressedbuffer) {
this.m_keypressedbuffer = null;
this.m_keypressedtime = null;
}
}
}
} catch (e) { this._handleException(arguments.callee, e); }
}
/**
* Handles key pressed event.
* @tparam HTMLEvent evt
* @private
*/
IEDomEventListener.prototype._onkeypress = function (elem, evt, time)
{
try {
if (elem) {
// stderr.log("calling _onkeypress :" + evt.keyCode + " at " + time);
this._write(Constants.keyPress, elem, "" + evt.keyCode, evt, time);
}
} catch (e) { this._handleException(arguments.callee, e); }
}
/**
* Handles onload events.
* @tparam HTMLEvent evt
*/
IEDomEventListener.prototype.onload = function(evt)
{
}
/**
* Handles onunload events.
* @tparam HTMLEvent evt
*/
IEDomEventListener.prototype.onunload = function (evt)
{
try {
var time = this.getTime();
if (evt) {
log(evt);
if (this.m_keypressedbuffer && evt.elem && evt.elem.document == this.m_keypressedbuffer.document) {
this._ontextchanged(this.m_keypressedbuffer, evt, this.m_keypressedtime);
}
}
} catch (e) { this._handleException(arguments.callee, e); }
}
/**
* Locator that depends on the text value.
* @private
*/
IEDomEventListener.prototype._getRefByText = function (attrs, elem)
{
try {
// go with the actual text
var index = 0;
var nodes = elem.document.getElementsByTagName(elem.tagName);
var innerText = elem.innerText;
for (var i = 0; i < nodes.length; i++) {
var n = nodes[i];
if (n.children.length > 0) {
// continue;
} else if (n == elem) {
this._getInfo(attrs, elem, "text", index);
break;
} else if (n.innerText == innerText) {
index++;
}
}
} catch (e) { this._handleException(arguments.callee, e); }
}
/**
* Locator that depends on an arbitrary condition
* @private
*/
IEDomEventListener.prototype._getRefTemplateFunc = function (node, elem, nodes, condFunc, funcType)
{
try {
var index = 0;
if (nodes != null) {
for (var i = 0; i < nodes.length; i++) {
var n = nodes(i);
if (n == elem) {
this._getInfo(node, elem, funcType, index);
break;
} else if (condFunc(n, elem)) {
index++;
}
}
}
} catch (e) { this._handleException(arguments.callee, e); }
}
/**
* Locator selection.
* @private
*/
IEDomEventListener.prototype._getRef = function (attrs, elem)
{
try {
var tagName = this._getTagName(elem);
var innerText = elem.innerText;
var doc = elem.document;
if (false) {
} else if (elem.children.length == 0 &&
tagName != "input" && tagName != "textarea" &&
innerText && innerText.length > 0) {
this._getRefByText(attrs, elem);
} else if (elem.name && elem.name != "") {
var nodes = doc.getElementsByName(elem.name);
this._getRefTemplateFunc(attrs, elem, nodes, this._matchAny, "name");
} else if (elem.title && elem.title != "") {
var nodes = doc.getElementsByTagName(tagName);
this._getRefTemplateFunc(attrs, elem, nodes, this._matchTitle, "title");
} else if (elem.alt && elem.alt != "") {
var nodes = doc.getElementsByTagName(tagName);
this._getRefTemplateFunc(attrs, elem, nodes, this._matchAlt, "alt");
} else if (elem.id && elem.id != "") {
this._getInfo(attrs, elem, "id");
} else if (tagName == "input") {
var type = this._getType(elem);
if (type == "button" || type == "submit") {
var nodes = doc.getElementsByTagName(elem.tagName);
this._getRefTemplateFunc(attrs, elem, nodes, this._matchValueAndType, "buttonValue");
} else if (type == "checkbox" || type == "file" ||
type == "image" || type == "password" ||
type == "radio" || type == "reset") {
// FIXME need to support these types
stderr.log("unprocessed input " + type);
} else if (type == "text") {
// FIXME need to support type == "text"
stderr.log("unprocessed input " + type);
} else {
stderr.log("unknown input " + type);
}
} else if (tagName == "img") {
var nodes = doc.getElementsByTagName(elem.tagName);
this._getRefTemplateFunc(attrs, elem, nodes, this._matchSrc, "src");
} else if (tagName == "area") {
var nodes = doc.getElementsByTagName(elem.tagName);
var condFunc = function(n1, n2) { return false;}
// FIXME need to support tag = area
this._getRefTemplateFunc(attrs, elem, nodes, condFunc, "area");
} else if (elem.id && elem.id != "") {
this._getInfo(attrs, elem, "id");
/*
} else if (elem.onclick && elem.onclick != "") {
var nodes = doc.getElementsByTagName(tagName);
var condFunc = function(n1, n2) {
var n1_onclick = n1.onclick;
var n2_onclick = n2.onclick;
if (n1_onclick && n2_onclick) {
return (Util.trim(n1_onclick.replace(this.m_fnrex, "$1")) ==
Util.trim(n2_onclick.replace(this.m_fnrex, "$1")));
} else {
return false;
}
}
this._getRefTemplateFunc(attrs, elem, nodes, condFunc, "onclick");
*/
} else {
/* var hasChildren = elem.children.length > 0;
if (hasChildren)
{
this._getInfo(attrs, elem, "index", elem.sourceIndex);
}
else
{*/
// debug.log(elem.innerHTML);
if (elem.innerHTML.length < 20) {
var nodes = doc.getElementsByTagName(elem.tagName);
var condFunc = function(n1, n2) { return n1.innerHTML == n2.innerHTML;}
this._getRefTemplateFunc(attrs, elem, nodes, condFunc, "InnerHTML");
} else {
// FIXME need to add innerText before using index.
this._getInfo(attrs, elem, "index", elem.sourceIndex);
}
}
} catch (e) { this._handleException(arguments.callee, e); }
}
/**
* Locator Generator.
* @private
*/
IEDomEventListener.prototype._getInfo = function(attrs, elem, type, index)
{
try {
var addIndex = true;
var addTagName = false;
var addValue = false;
var addSrc = false;
var addName = false;
var addId = false;
var addText = false;
var addCoord = false;
var addTitle = false;
var addAlt = false;
var addOnclick = false;
var addPwd = false;
if (type == "InnerHTML") {
addTagName = true;
attrs.html = elem.innerHTML;
} else if (type == "index") {
} else if (type == "text") {
addTagName = true;
addText = true;
} else if (type == "title") {
addTagName = true;
addTitle = true;
if (elem.getAttribute("name")) {
addName = true;
}
} else if (type == "alt") {
addTagName = true;
addAlt = true;
} else if (type == "name") {
addName = true;
} else if (type == "id") {
addId = true;
} else if (type == "buttonValue") {
addValue = true;
addTagName = true;
} else if (type == "area") {
addTagName = true;
addCoord = true;
} else if (type == "onclick") {
addTagName = true;
addOnclick = true;
} else if (type == "src") {
addTagName = true;
addSrc = true;
}
if (addIndex && index && index > 0) {
attrs.index = index;
}
var tagName = this._getTagName(elem);
if (addTagName && tagName) {
attrs.tagName = tagName;
}
if (tagName != "a") {
var parentElem = elem.parentElement;
while (parentElem) {
var parentTag = this._getTagName(parentElem);
if (parentTag == "a") {
attrs.anchor = "true";
break;
} else if (parentTag == "td") {
break;
}
parentElem = parentElem.parentElement;
}
}
if (tagName == "input" && elem.getAttribute("type") == "password") {
addPwd = true;
}
if (addValue) {
var value = elem.value;
if (value) {
attrs.value = value;
}
}
if (addSrc) {
// FIXME remove host and protocol from the src.
var urlPart = Util.splitURL(elem.src);
var src = urlPart ? urlPart.PATH : elem.src;
if (src) {
attrs.src = src;
}
}
if (addName) {
var name = elem.name;
if (name) {
attrs.name = name;
}
}
if (addId) {
var id = elem.id;
if (id) {
attrs.id = id;
}
}
if (addText) {
var text = elem.innerText;
if (text) {
attrs.text = text;
}
}
if (addTitle) {
var title = elem.title;
if (title) {
attrs.title = title;
}
}
if (addAlt) {
var alt = elem.alt;
if (alt) {
attrs.alt = alt;
}
}
if (addOnclick) {
var onclick = elem.onclick;
if (onclick) {
onclick = onclick.replace(this.m_fnrex, "$1");
}
if (onclick) {
attrs.onclick = Util.trim(onclick);
}
}
if (addPwd) {
attrs.password = "true";
}
this._getFrameTreeIndicies(attrs, elem.document.parentWindow);
} catch (e) { this._handleException(arguments.callee, e); }
}
/**
* Frame Locator.
* @private
*/
IEDomEventListener.prototype._getFrameTreeIndicies = function (attrs, win)
{
try {
// FIXME this concats all the frame names.
var result = this.getFrameInfo(win);
if (result != "") {
attrs.frame = result;
}
var windowIndex = this.m_mediator.getWindowIndex(win.top);
if (windowIndex && windowIndex > 0) {
attrs.windowIndex = windowIndex;
}
} catch (e) { this._handleException(arguments.callee, e); }
}
/**
* Determine the position of a window relative to the top window.
* @private
*/
IEDomEventListener.prototype.getFrameInfo = function (win)
{
var result = "";
try {
// exit when win.parent is itself
while (win.parent != win) {
// at every loop, determine the position of the child window
// from the parent window.
var parent = win.parent;
for (var i = 0; i < parent.frames.length; i++) {
if (parent.frames(i) == win) {
if (result.length == 0) {
if (win.name != "") {
result = win.name + "";
} else {
result = i + "";
}
} else if (win.name != "" && isNaN(parseInt(win.name))) {
result = win.name + "|" + result;
} else {
result = i + "|" + result;
}
}
}
win = win.parent;
}
} catch (e) { this._handleException(arguments.callee, e); }
return result;
}
/**
* Handles exception.
* @private
*/
IEDomEventListener.prototype._handleException = function (func, e)
{
handleException(IEDomEventListener, func, e);
}
/**
* @class IEBrEventListener.
* An event listener that captures relevant browser events, such
* as DocumentComplete, NavigateComplete2, BeforeNavigate2.
*
* The mediator object must support these functions
* This rule generates a regular expression for query parameters on an anchor link. This rule is not compatible with 10.2.0.3 agent.
This rule generates a regular expression for form destination. This rule is not compatible with 10.2.0.3 agent.
In 10.2.0.3 and earlier versions of recorder, there is a heuristic algorithm used to calculate the 'hidden input hint' property, and the 'ignore input hint' iproperty. These two properties tell the beacon to perform/ignore runtime value substitution in the post data. It is a heuristic because the match is based on the form name and values.
In this version of the recorder, the algorithm is more deterministic:
...&username=sysadmin&password=sysadmin&...
.
Since we can perform correlation on the user actions and the corresponding POST data:
The POST data property should be rewritten to ...&username=[P_USERNAME]&password=[P_PASSWORD]&...
The action properties should be rewritten to
type name='username' newValue='[P_USERNAME]' type name='password' newValue='[P_PASSWORD]'.A nonsensitive value property should be added:
P_USERNAME = sysadmin
.
A sensitive value property should be added: P_PASSWORD = ******
(real value is masked from the user).
This makes it easy for user when credentials are changed, or when creating a web transaction template, as sensitive and nonsensitive values are automatically converted to variables. Changes only need to happen in one place.
Passwords should be automatically extracted, and stored in the sensitive value table, with the real values masked. Any user editing the web transaction (who may be different from the original creator) cannot see the password value.
The algorithm works as follows: