/**
* @fileoverview
* Ajax Javascript Library
* (c)2006 LAMATEK, Inc.
* @author Tom Cole tcole@lamatek.com
* @version 2.0
*/
/**
* Factory class for handling multiple Ajax Requests. Allows you to create and
* identify AjaxRequests via a user defined string.
*/
var AjaxFactory = function () {
/**Array that stores all AjaxRequest objects */
var requests = {};
/**
* Creates a new request and adds it to the queue.
*/
this.createRequest = function (id) {
var request = new AjaxRequest();
requests[id] = request;
return request;
}
/**
* Attempts to return the value from the specified request id, if
* it exists.
*/
this.getRequest = function (id) {
var request = requests[id];
if (request) {
return request;
}
else {
return null;
}
}
/**
* Removes a request from the queue.
*/
this.deleteRequest = function (id) {
requests[id] = null;
}
}
/**
* Cross-browser implementation to obtain and process a XMLHttpRequest instance.
* It removes the need to worry about cross-browser implementation issues and provides
* a simple, consistent interface to XMLHttpRequests. It also provides a simple API
* that automatically converts the response to the expected data type (text, xml
* or JSON) and gives a simple way to parse values from XML responses.
*/
var AjaxRequest = function () {
/**Current debugging status. */
var debug = false;
/**Handle to a loading display div.*/
var async = true;
/**The resulting value (either a String or DOM object based on request type.*/
var value = null;
/**The resulting value as a String, regardless of request type.*/
var valueString = null;
/**The XMLHttpRequest used to send & receive data.*/
var xmlhttp = null;
/**The method that will get called on a successful request.*/
var handler = null;
/**Secondary handler used in autoSubmitAndLoad___Request methods. */
var handler2 = null;
/**Attempts to hold browser type. Only works after an AjaxRequest has been created.*/
var browser = (navigator.appName.indexOf('Microsoft') >= 0);
/**The text to display while requests are processing.*/
var message = "Please wait...";
/**Holds the last request type.*/
var type = "text";
/**Holds the active parnt form element. Used in the submit___Request methods.*/
var form;
/**Denotes whether the user is providing style elements him/herself*/
var isStyled = false;
/**Denotes whether or not unique id generated is desired.*/
var uniqueID = false;
/**Denotes the timeout limit for requests.*/
var timeout = null;
/**Denotes the method to call if a timeout occurs.*/
var timeouthandler = null;
/**The timeout object. Kept as a reference so we can clear it if not needed.*/
var timer = null;
/**Denotes whether a request has been completed. Used by timeoutRequest.*/
var completed = false;
/**Denotes that the request was aborted.*/
var aborted = false;
/**Array of set request header values.*/
var headers = new Array();
/**Denotes that this request is cross domain.*/
var crossDomain = false;
/**Provides DOM based drag capabilities for the log window.
*
* This script found at http://boring.youngpup.net/2001/domdrag.
*/
var Drag = {
obj : null,
init : function(o, oRoot, minX, maxX, minY, maxY, bSwapHorzRef, bSwapVertRef, fXMapper, fYMapper) {
o.onmousedown = Drag.start;
o.hmode = bSwapHorzRef ? true : false ;
o.vmode = bSwapVertRef ? false : true ;
o.root = oRoot && oRoot != null ? oRoot : o ;
if (o.hmode && isNaN(parseInt(o.root.style.left ))) o.root.style.left = "0px";
if (o.vmode && isNaN(parseInt(o.root.style.top ))) o.root.style.top = "0px";
if (!o.hmode && isNaN(parseInt(o.root.style.right ))) o.root.style.right = "0px";
if (!o.vmode && isNaN(parseInt(o.root.style.bottom))) o.root.style.bottom = "0px";
o.minX = typeof minX != 'undefined' ? minX : null;
o.minY = typeof minY != 'undefined' ? minY : null;
o.maxX = typeof maxX != 'undefined' ? maxX : null;
o.maxY = typeof maxY != 'undefined' ? maxY : null;
o.xMapper = fXMapper ? fXMapper : null;
o.yMapper = fYMapper ? fYMapper : null;
o.root.onDragStart = new Function();
o.root.onDragEnd = new Function();
o.root.onDrag = new Function();
},
start : function(e) {
var o = Drag.obj = this;
e = Drag.fixE(e);
var y = parseInt(o.vmode ? o.root.style.top : o.root.style.bottom);
var x = parseInt(o.hmode ? o.root.style.left : o.root.style.right );
o.root.onDragStart(x, y);
o.lastMouseX = e.clientX;
o.lastMouseY = e.clientY;
if (o.hmode) {
if (o.minX != null) o.minMouseX = e.clientX - x + o.minX;
if (o.maxX != null) o.maxMouseX = o.minMouseX + o.maxX - o.minX;
} else {
if (o.minX != null) o.maxMouseX = -o.minX + e.clientX + x;
if (o.maxX != null) o.minMouseX = -o.maxX + e.clientX + x;
}
if (o.vmode) {
if (o.minY != null) o.minMouseY = e.clientY - y + o.minY;
if (o.maxY != null) o.maxMouseY = o.minMouseY + o.maxY - o.minY;
} else {
if (o.minY != null) o.maxMouseY = -o.minY + e.clientY + y;
if (o.maxY != null) o.minMouseY = -o.maxY + e.clientY + y;
}
document.onmousemove = Drag.drag;
document.onmouseup = Drag.end;
return false;
},
drag : function(e) {
e = Drag.fixE(e);
var o = Drag.obj;
var ey = e.clientY;
var ex = e.clientX;
var y = parseInt(o.vmode ? o.root.style.top : o.root.style.bottom);
var x = parseInt(o.hmode ? o.root.style.left : o.root.style.right );
var nx, ny;
if (o.minX != null) ex = o.hmode ? Math.max(ex, o.minMouseX) : Math.min(ex, o.maxMouseX);
if (o.maxX != null) ex = o.hmode ? Math.min(ex, o.maxMouseX) : Math.max(ex, o.minMouseX);
if (o.minY != null) ey = o.vmode ? Math.max(ey, o.minMouseY) : Math.min(ey, o.maxMouseY);
if (o.maxY != null) ey = o.vmode ? Math.min(ey, o.maxMouseY) : Math.max(ey, o.minMouseY);
nx = x + ((ex - o.lastMouseX) * (o.hmode ? 1 : -1));
ny = y + ((ey - o.lastMouseY) * (o.vmode ? 1 : -1));
if (o.xMapper) nx = o.xMapper(y)
else if (o.yMapper) ny = o.yMapper(x)
Drag.obj.root.style[o.hmode ? "left" : "right"] = nx + "px";
Drag.obj.root.style[o.vmode ? "top" : "bottom"] = ny + "px";
Drag.obj.lastMouseX = ex;
Drag.obj.lastMouseY = ey;
Drag.obj.root.onDrag(nx, ny);
return false;
},
end : function() {
document.onmousemove = null;
document.onmouseup = null;
Drag.obj.root.onDragEnd( parseInt(Drag.obj.root.style[Drag.obj.hmode ? "left" : "right"]),
parseInt(Drag.obj.root.style[Drag.obj.vmode ? "top" : "bottom"]));
Drag.obj = null;
},
fixE : function(e){
if (typeof e == 'undefined') e = window.event;
if (typeof e.layerX == 'undefined') e.layerX = e.offsetX;
if (typeof e.layerY == 'undefined') e.layerY = e.offsetY;
return e;
}
};
/**
* This internal method resets the completed and aborted status for the next request
* and setups the client callback handler.
* @param {Function} method The end-user defined method that will be called upon successful completion of the request.
*/
var generateRequest = function (method) {
complete = false;
aborted = false;
log("----Registering client callback method...",0);
handler = method;
xmlhttp = getRequest();
if (xmlhttp) {
xmlhttp.onreadystatechange = parseResponse;
}
}
/**
* This internal method is a cross-browser implementation to obtain an
* XMLHttpRequest object.
* @type XMLHttpRequest
*/
var getRequest = function () {
var xml;
/*@cc_on
@if (@_jscript_version >= 5)
try {
xml = new ActiveXObject("Msxml2.XMLHTTP");
browser = true;
}
catch (e) {
try {
xml = new ActiveXObject("Microsoft.XMLHTTP");
browser = true;
}
catch (E) {
xml = false;
}
}
@else
xml = false;
@end @*/
if (!xml && typeof XMLHttpRequest != 'undefined') {
try {
xml = new XMLHttpRequest();
browser = false;
}
catch (e) {
xml = false;
browser = false;
}
}
if (! xml) {
this.log("------Unable to create XmlHTTPRequest.", 3);
}
return xml;
}
/**
* This internal method reads the text response from the xmlhttp request, attempts to create
* the appropriate data type out of it (based on the type) and calls the registered handler method.
*/
var parseResponse = function () {
headers = new Array(); //reset headers so they aren't used on subsequent calls.
if (xmlhttp && !aborted) {
if (xmlhttp.readyState == 4) {
clearTimeout(timer);
completed = true;
log("------XmlHTTPRequest readystate is OK...");
if (xmlhttp.status == 200) {
valueString = xmlhttp.responseText;
log("------XmlHTTPRequest status is OK...");
log("");
log("------Response received:
",1); try { switch (type) { case 'json': value = eval('(' + xmlhttp.responseText + ')'); break; case 'xml': value = xmlhttp.responseXML; break; default: value = xmlhttp.responseText; } } catch(e) { log("------Error occurred assigning response to value: " + e.message + "...", 3); value = null } if (handler) { if (! form && ! handler2) log("Forwarding control to client handler:" + valueString.replace(//g, ">") + "
", 1); handler.call(this); } } else { log("------XmlHTTPRequest status returned " + xmlhttp.status + "...", 3); value = null; } if (document.getElementById('loadingDiv')) { document.body.removeChild(document.getElementById('loadingDiv')); } delete xmlhttp.onreadystatechange; } else { log("------XmlHTTPRequest readystate is " + xmlhttp.readyState + "...", 2); } } } /** * This internal method generates and sends the XMLHttpRequest. * @param url The relative or absolute url of the server side resource receiving the request * @param method The request method to use (GET, POST or HEAD) * @param parameters The parameter list to send to the server (name=value&name2=value...) * @param callback The javascript method to call upon a successful response * @param asynchronous True or false * @param responseType The expected server response data-type "text", "xml" or "json" */ var initiateRequest = function (url, method, parameters, callback, asynchronous, responseType) { type = responseType; if (url) { var defMethod = "GET"; if (method) { defMethod = method; } defMethod = defMethod.toUpperCase(); if (uniqueID) { var uid = (new Date()).getTime(); if (parameters) { parameters += "&uniqueid=" + uid; } else { parameters = "uniqueid=" + uid; } } if (defMethod == "GET") { url += "?" + parameters; parameters = null; } log("--Request method set to " + defMethod + "..."); async = true; if (! asynchronous) { async = asynchronous; } log("--Request set to asynchronous: " + async + "..."); showLoadingDiv(); generateRequest(callback); xmlhttp.open(defMethod, url, async); if (parameters) { xmlhttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); for (var i = 0; i < headers.length; i++) { xmlhttp.setRequestHeader(headers[i].name, headers[i].value); alert("Set " + headers[i].name + " to " + headers[i].value); } } else { parameters = null; } for (var i = 0; i < headers.length; i++) { xmlhttp.setRequestHeader(headers[i].name, headers[i].value); } log("--Sending request..."); if (timeout) { timer = setTimeout(doTimeout, timeout); } completed = false; xmlhttp.send(parameters); } else { log("--Request URL is not set. Unable to process request.", 3); } } /** * This internal method is called once the user set timeout limit is hit. * If the request has not completed, it attempts to abort the request. */ var doTimeout = function () { if (xmlhttp && (! completed)) { log("--Request has hit timeout limit. Aborting request.", 2); aborted = true; xmlhttp.abort(); if (timeouthandler) { log("--Calling client timeout handler."); timeouthandler.call(this); } completed = true; if (document.getElementById('loadingDiv')) { document.body.removeChild(document.getElementById('loadingDiv')); } xmlhttp = null; } } /** * This internal method creates a floating window (if necessary) and adds a * message ine to the log. * @param message A message to add to the log window. */ var log = function (message, severity) { if (debug) { var bg = "#ffffff"; if (severity) { switch (severity) { case 3: bg = "#ff0000"; break; case 2: bg = "#ffb500"; break; case 1: bg = "#ffff00"; break; default: bg = "#ffffff"; } } if (! document.getElementById("loggerDiv")) { var logger = document.createElement("div"); logger.id = "loggerDiv"; if (browser) { logger.style.position = "absolute"; } else { logger.style.position = "fixed"; } logger.style.top = "25px"; logger.style.right = "25px"; logger.style.display = "inline"; logger.style.width = "400"; logger.style.height = "400"; logger.style.border = "1px solid #000000"; logger.style.background = "#cccccc"; var loggerBar = document.createElement("div"); loggerBar.id = "loggerBar"; loggerBar.style.height = 22; loggerBar.style.textAlign = "right"; loggerBar.innerHTML = ""; logger.appendChild(loggerBar); var loggerStatus = document.createElement("div"); loggerStatus.style.overflow = "auto"; loggerStatus.id = "loggerStatusDetails"; loggerStatus.style.background = "#ffffff"; loggerStatus.style.width = "100%"; loggerStatus.style.height = 378; logger.appendChild(loggerStatus); document.body.insertBefore(logger, document.body.firstChild); } if (! document.getElementById('loggingDetailLines')) { var loggerDiv = document.getElementById("loggerStatusDetails"); var loggingDiv = document.createElement("div"); loggingDiv.id = "loggingDetailLines"; loggerDiv.appendChild(loggingDiv); } var loggingDiv = document.getElementById('loggingDetailLines'); var logLine = document.createElement("div"); logLine.style.borderTop = "1px solid #cccccc"; logLine.innerHTML = message; logLine.style.background = bg; loggingDiv.appendChild(logLine); Drag.init(document.getElementById('loggerBar'), document.getElementById('loggerDiv')); } } /** * This internal method displays a "loading" div message for asynchronous requests. */ var showLoadingDiv = function () { if (message) { if (! document.getElementById('loadingDiv')) { var loading = document.createElement("div"); loading.id = 'loadingDiv'; loading.innerHTML = message; if (browser) { loading.style.position = 'absolute'; } else { loading.style.position = 'fixed'; } if (! isStyled) { loading.style.background = '#cc0000'; loading.style.color = '#ffffff'; loading.style.top = 2; loading.style.right = 2; loading.style.paddingLeft = 5; loading.style.paddingRight = 5; loading.style.fontFamily = "Arial"; loading.style.fontSize = "12"; } loading.className = 'loading'; document.body.appendChild(loading); } } } /** * This internal method automatically builds the parameter list from any form elements inside the given * form node as if submitted by the given submitBy element. This method does not work for multipart/form-data enctypes. * @param {String} form The id of the form node to submit. * @param {Object} submitBy The element that is submitting the form. * @type {String} returns a parameter list for the specified form submitted by the specified submittor. */ var getFormData = function (form, submitBy) { try { var parameters = ""; for (var i = 0; i < form.getElementsByTagName('input').length; i++) { var element = form.getElementsByTagName('input')[i]; var name = null; if (element.name) name = element.name; else if (element.id) name = element.id; if (name) { switch (element.type) { case 'check': if (element.checked) parameters += name + "=" + element.value + "&"; else parameters += name + "=&"; break; case 'radio': if (element.checked) parameters += name + "=" + element.value + "&"; break; case 'submit': //ignore these, handled by submitBy break; case 'button': //ignore these, handled by submitBy break; default: parameters += name + "=" + element.value + "&"; } } } for (var i = 0; i < form.getElementsByTagName('select').length; i++) { var element = form.getElementsByTagName('select')[i]; var name = null; if (element.name) name = element.name; else if (element.id) name = element.id; if (name) { if (element.type == 'select-one') { parameters += name + "=" + element.options[element.selectedIndex].value + "&"; } else if (element.type == 'select-multiple') { for (var j = 0; j < element.options.length; j++) { if (element.options[j].selected) parameters += name + "=" + element.options[j].value + "&"; } } } } for (var i = 0; i < form.getElementsByTagName('textarea').length; i++) { var element = form.getElementsByTagName('textarea')[i]; var name = null; if (element.name) name = element.name; else if (element.id) name = element.id; if (name) { parameters += name + "=" + element.value + "&"; } } if (submitBy) { var name = null; if (submitBy.name) name = submitBy.name; else if (submitBy.id) name = submitBy.id; if (name) { parameters += name + "=" + submitBy.value; } } return parameters; } catch(e) { return null; } } var setElement = function (element, rValue) { switch(element.tagName) { case 'INPUT': switch(element.typeName) { case 'check': case 'radio': if (element.value == rValue) { element.checked = true; } break; default: try { element.value = rValue; } catch(e) { log("Error attempting to assign value to element " + rName + ". Skipping it.", 1); } } break; case 'TEXTAREA': element.innerHTML = rValue; break; case 'SELECT': for (var j = 0; j < element.options.length; j++) { if (element.options[j].value == rValue) { element.options[j].selected = true; break; } } break; default: try { element.innerHTML = rValue; } catch(e) { log("Error assigning value to unidentified element " + rName + ". Skipping it.", 1); } } } /** * This internal method takes the current value object as a CSV string and parses * through it for all names. It attempts to find screen elements with id's that * match each name, and if it finds one, sets it's value to that name's value. */ var populateFormFromText = function () { try { if (form) { for (var i = 0; i < value.split(',').length; i++) { var child = value.split(',')[i]; try { var rName = child.split("=")[0]; var rValue = child.split("=")[1] || ''; var element = document.getElementById(rName); if (element) { setElement(element, rValue); } else { element = document.getElementsByName(rName); if (element) { for (var j = 0; j < element.length; j++) { setElement(element[j], rValue); } } } } catch(e) { } } } else { log("Error attempting to populate form. The current form is null.", 3); } } catch(e) { log("An error occured attempting to populate the form." + handler.toString().replace(//g, ">") + "
", 3); } if (handler2) { log("Forwarding control to client handler:" + e.message + "
", 1); handler2.call(); } } /** * This internal method takes the current value object as JSON and parses through * the values array.. * It attempts to find screen elements with id's that match each "name", and if * it finds one, sets it's value to "value". */ var populateFormFromJSON = function () { try { if (form) { var root = value; for (var i = 0; i < root.values.length; i++) { var rName = root.values[i].name; var rValue = root.values[i].value; var element = document.getElementById(rName); if (element) { setElement(element, rValue); } else { element = document.getElementsByName(rName); if (element) { for (var j = 0; j < element.length; j++) { setElement(element[j], rValue); } } } } } else { log("Error attempting to populate form. The current form is null.", 3); } } catch(e) { log("An error occured attempting to populate the form." + handler2.toString().replace(//g, ">") + "
", 3); } if (handler2) { log("Forwarding control to client handler:" + e.message + "
", 1); handler2.call(); } } /** * This internal method takes the current value object as XML and parses through * it for all nodes. It attempts to find screen elements with id's that match * each nodeName, and if it finds one, sets it's value to that node's value. */ var populateFormFromXML = function () { try { if (form) { var root = value.documentElement; for (var i = 0; i < root.childNodes.length; i++) { var child = root.childNodes[i]; var rName = child.nodeName; var rValue = child.firstChild.data; var element = document.getElementById(rName); if (element) { setElement(element, rValue); } else { element = document.getElementsByName(rName); if (element) { for (var j = 0; j < element.length; j++) { setElement(element[j], rValue); } } } } } else { log("Error attempting to populate form. The current form is null.", 3); } } catch(e) { log("An error occured attempting to populate the form." + handler2.toString().replace(//g, ">") + "
", 3); } if (handler2) { log("Forwarding control to client handler:" + e.message + "
", 1); handler2.call(); } } /** *This public method makes a JSON request, reads the response and attempts * to load any document elements with the given values. It assumes that the * response will be a valid JSON string in the given format: * * { values [ * {"name": "name1", "value": "value1}, * {"name": "name1", "value": "value1} * ] } * * Where any element with an id or name of 'name1' will be assigned the value (or innerHTML) * of 'value1'. */ this.autoSubmitAndLoadJSONRequest = function (element, callback) { handler2 = callback; this.submitJSONRequest(element, populateFormFromJSON, true); } /** * This public method initiates a request that is expected to return * a JSON text string that is to be converted into a JSON object. Parameter data, * url and call method are retrieved from the given form object. * The resulting object will be stored in the value variable and * can be retrieved using getValue(); * @param {String} form The form DOM object fro which to pull form data. (REQUIRED) * @param {Object} submitBy The element that is initiating the form submission. (OPTIONAL) * @param {Function} callback The end-user defined method that will be called upon successful completion of the request. Default is null. (OPTIONAL) * @param {Boolean} asynchronous. Set to true if the request is to be asynchronous, false if not. Default is true. (OPTIONAL) */ this.submitJSONRequest = function (element, callback, asynchronous) { try { form = element; while (! (form.tagName == 'FORM')) { form = form.parentNode; } if (form) { this.doJSONRequest(form.action, form.method || "GET", getFormData(form, element), callback, asynchronous); } else { log("Error processing Ajax form submission. Form " + form + " is not valid"); } } catch(e) { log("Error generating Ajax request in submitJSONRequest. " + e.message); } } /** * This public method initiates a request that is expected to return * a JSON text string that is to be converted into a JSON object. * The resulting object will be stored in the value variable and * can be retrieved using getValue(); * @param {String} url The url to the server resource that will handle the request. (REQUIRED) * @param {String} method The HTTP method to send the request. Must be "GET" or "POST". Default is "GET" (OPTIONAL) * @param {String} parameters A url formatted list of parameter names and values. Parameters are specified as name=value&name=value.... Default is null. (OPTIONAL) * @param {Function} callback The end-user defined method that will be called upon successful completion of the request. Default is null. (OPTIONAL) * @param {Boolean} asynchronous. Set to true if the request is to be asynchronous, false if not. Default is true. (OPTIONAL) */ this.doJSONRequest = function (url, method, parameters, callback, asynchronous) { log("Beginning new JSON request..."); initiateRequest(url, method, parameters, callback, asynchronous, "json"); } /** *This public method makes an XML request, reads the response and attempts * to load any document elements with the given values. It assumes that the * response will be a valid XML document in the given format: * * *" + handler2.toString().replace(//g, ">") + "