//*****************************************************************************
// Module:  Cross-Browser JavaScript Utility Library
// Project: Raven Client
//
// Hummingbird Ltd
//
// Author(s): Aleksandar Susnjar
// Created:   2001-08-01
// Version:   1.00/a1 (2001-08-01)
// 
// HISTORY:
//
// ------------- ----------- --------------------------------------------------
//  DATE STAMP     VERSION                          CHANGE
// YYYY-MM-DD-NN VV.vv/Sbbbb                      DESCRIPTION
// ------------- ----------- --------------------------------------------------
// 2001-08-01-01  1.00/a1    Started. [Aleksandar Susnjar]
//
// 2002-06-05-01  1.00/r1    Solved CRs 30632/30633 and 30634/30636
//                           [Aleksandar Susnjar]
//
// 2002-12-06-01  1.01/a1    Re: CR 35083 - needed to detect Netscape 7 and
//                           equivalent Mozilla. Added this functionality.
//                           [Aleksandar Susnjar]
// ------------- ----------- --------------------------------------------------
//
// Legend:
//
// YYYY - 4-digit  zero-padded year
// MM   - 2-digit  zero-padded month
// DD   - 2-digit  zero-padded day of month
// NN   - 2-sigit  zero-padded daily serial number
// VV   - 2-digit space-padded target major version
// vv   - 2-digit  zero-padded target minor version
// S    - Stage: [A]lpha, [B]eta, [C]andidate, [R]elease
// bbbb - up to 4-digit version (not stage) build number
//*****************************************************************************


//-----------------------------------------------------------------------------
// All functions provided here packaged in a single object for easy reference:
//-----------------------------------------------------------------------------

function XBUtils() {
    // Functions
    this.constructor.prototype.xbApply           = xbApply;
    this.constructor.prototype.xbCall            = xbCall;
    this.constructor.prototype.xbFunctionName    = xbFunctionName;
    this.constructor.prototype.xbClassName       = xbClassName;
    this.constructor.prototype.xbUpdateInnerHTML = xbUpdateInnerHTML;
    this.constructor.prototype.xbTemporaryID     = xbTemporaryID;
    this.constructor.prototype.xbSetFrameURL     = xbSetFrameURL;
    this.constructor.prototype.examine           = examine;
    this.constructor.prototype.escapeTextForHTML = escapeTextForHTML;
    
    
    // Data (see xbDetectBrowser in-line documentation)
    this.constructor.prototype.browserType       = browserType;
    this.constructor.prototype.browserVersion    = browserVersion;
    this.constructor.prototype.mozillaVersion    = mozillaVersion;
    this.constructor.prototype.browserRelease    = browserRelease;
    this.constructor.prototype.geckoRelease      = geckoRelease;    
    this.constructor.prototype.scriptEngine      = scriptEngine;
    
    this.constructor.prototype.css1Supported     = css1Supported;
    this.constructor.prototype.css2Supported     = css2Supported;
    
    this.constructor.prototype.Opera             = Opera;
    
    this.constructor.prototype.IE                = IE;
    this.constructor.prototype.IE4               = IE4;
    this.constructor.prototype.IE5               = IE5;
    this.constructor.prototype.IE50              = IE50;
    this.constructor.prototype.IE55              = IE55;
    this.constructor.prototype.IE6               = IE6;
    this.constructor.prototype.IE40plus          = IE40plus;
    this.constructor.prototype.IE50plus          = IE50plus;
    this.constructor.prototype.IE55plus          = IE55plus;
    this.constructor.prototype.IE60plus          = IE60plus;
    
    this.constructor.prototype.NN                = NN;
    this.constructor.prototype.NN4               = NN4;
    this.constructor.prototype.NN6               = NN6;
    this.constructor.prototype.NN60              = NN60;
    this.constructor.prototype.NN61              = NN61;
    this.constructor.prototype.NN62              = NN62;    
    this.constructor.prototype.NN63              = NN63;    
    this.constructor.prototype.NN7               = NN7;
    this.constructor.prototype.NN70              = NN70;
    this.constructor.prototype.NN4plus           = NN4plus;
    this.constructor.prototype.NN6plus           = NN6plus;
    this.constructor.prototype.NN60plus          = NN60plus;
    this.constructor.prototype.NN61plus          = NN61plus;
    this.constructor.prototype.NN62plus          = NN62plus;
    this.constructor.prototype.NN7plus           = NN7plus;
    this.constructor.prototype.NN70plus           = NN70plus;
    
    this.constructor.prototype.Mozilla           = Mozilla;
    this.constructor.prototype.Mozilla088        = Mozilla088;
    this.constructor.prototype.Mozilla092        = Mozilla092;
    this.constructor.prototype.Mozilla094        = Mozilla094;
    this.constructor.prototype.Mozilla099        = Mozilla099;
    this.constructor.prototype.Mozilla1          = Mozilla1;
    this.constructor.prototype.Mozilla101        = Mozilla101;
    
    this.constructor.prototype.Mozilla088plus    = Mozilla088plus;
    this.constructor.prototype.Mozilla092plus    = Mozilla092plus;
    this.constructor.prototype.Mozilla094plus    = Mozilla094plus;
    this.constructor.prototype.Mozilla099plus    = Mozilla099plus;
    this.constructor.prototype.Mozilla1plus      = Mozilla1plus;
    this.constructor.prototype.Mozilla101plus    = Mozilla101plus;
    
    this.constructor.prototype.MN                = MN;
    this.constructor.prototype.MN6               = MN6;
    this.constructor.prototype.MN60              = MN60;
    this.constructor.prototype.MN61              = MN61;
    this.constructor.prototype.MN62              = MN62;    
    this.constructor.prototype.MN63              = MN63;    
    this.constructor.prototype.MN7               = MN7;    
    this.constructor.prototype.MN70              = MN70;    
    this.constructor.prototype.MN60plus          = MN60plus;
    this.constructor.prototype.MN61plus          = MN61plus;
    this.constructor.prototype.MN62plus          = MN62plus;
    this.constructor.prototype.MN63plus          = MN63plus;
    this.constructor.prototype.MN7plus           = MN7plus;
    this.constructor.prototype.MN70plus          = MN70plus;
    
    
}	

//-----------------------------------------------------------------------------
// xbDetectBrowser
//
// Description:
// Detects the browser and its version and initializes a number of easy-to-use
// variables. See side-effects for details.
//
// Arguments: 
//
//    No arguments.
//
// Result:
//
//    None. See Side-effects.
//
// Side-effects:
//
//    Creates a number of easy-to-use variables for browser-dependent code:
//
//    Variable name     Type                   Description
//    ----------------- -------  ----------------------------------------------
//    browserType       String   "IE" for IE, "NN" for Netscape Navigator.
//    browserVersion    String   version of the browser, e.g. "6.0"
//    IE                Boolean  True if IE. False otherwise.
//    IE4               Boolean  True if IE 4.x. False otherwise.
//    IE5               Boolean  True if IE 5.x. False otherwise.
//    IE50              Boolean  True if IE 5.0-4. False otherwise.
//    IE55              Boolean  True if IE 5.5-9. False otherwise.
//    IE6               Boolean  True if IE 6.x. False otherwise.
//    IE40plus          Boolean  True if IE 4.0 or newer. False otherwise.
//    IE50plus          Boolean  True if IE 5.0 or newer. False otherwise.
//    IE55plus          Boolean  True if IE 5.5 or newer. False otherwise.
//    IE60plus          Boolean  True if IE 6.0 or newer. False otherwise.
//    NN                Boolean  True if Netscape. False otherwise.
//    NN4               Boolean  True if Netscape 4.x. False otherwise.
//    NN6               Boolean  True if Netscape 6.x. False otherwise.
//    NN4plus           Boolean  True if Netscape 4.0 or newer. False otherwise
//    NN6plus           Boolean  True if Netscape 6.0 or newer. False otherwise
//
// Change date-stamps:
// 2001-08-01-01 - Created. [Aleksandar Susnjar]
//-----------------------------------------------------------------------------


var browserType, browserVersion, mozillaVersion, browserRelease, geckoRelease;
var Opera;
var IE, IE4, IE5, IE50, IE55, IE6, IE40plus, IE50plus, IE55plus, IE60plus;
var NN, NN4, NN6, NN60, NN61, NN62, NN63, NN7, NN70, NN4plus, NN6plus, NN60plus, NN61plus, NN62plus, NN63plus, NN7plus, NN70plus;
var MN, MN6, MN60, MN61, MN62, MN63, MN7, MN70, MN6plus, MN60plus, MN61plus, MN62plus, MN63plus, MN70plus, MN7plus;
var Mozilla, Mozilla088, Mozilla092, Mozilla094, Mozilla099, Mozilla1, Mozilla101, Mozilla088plus, Mozilla092plus, Mozilla094plus, Mozilla099plus, Mozilla1plus, Mozilla101plus;

var css1Supported, css2Supported;

function xbDetectBrowser() {
   IE   = false;
   IE4  = false; IE40plus = false;
   IE5  = false; 
   IE50 = false; IE50plus = false;
   IE55 = false; IE55plus = false;
   IE6  = false; IE60plus = false;
   
   NN   = false;
   NN4  = false; NN4plus  = false;
   NN6  = false; NN60     = false; NN6plus = false; NN60plus = false;
   NN60 = false; NN60plus = false;
   NN61 = false; NN61plus = false;
   NN62 = false; NN62plus = false;
   NN7  = false; NN70     = false; NN7plus = false; NN70plus = false;
   
   MN   = false;
   MN6  = false; MN60     = false; MN60plus = false;
   MN60 = false; MN60plus = false;
   MN61 = false; MN61plus = false;
   MN62 = false; MN62plus = false;
   MN7  = false; MN70     = false; MN7plus = false; MN70plus = false;
   
   Opera = false;
   
   Mozilla = false;
   Mozilla088 = Mozilla092 = Mozilla094 = Mozila099 = Mozilla1 = Mozilla101 = false;
   Mozilla088plus = Mozilla092plus = Mozilla094plus = Mozila099plus = Mozilla1plus = Mozilla101plus = false;
   
   scriptEngine = null;
   geckoRelease = null;
   mozillaVersion = null;
   
      
   if (document.all) { 
      // This is IE
      browserType    = "IE";
      
      var av = ""+navigator.userAgent;      
      opera          = av.match(/Opera/gi);
      if (opera) {
         browserType = "Opera";
         Opera = true;
         return;
      }
      
      browserVersion = ""+navigator.appVersion.match(/MSIE \d+.\d+/);
      browserVersion = browserVersion.match(/\d+.\d+/);
      browserRelease = '';
      geckoRelease   = null;
      mozillaVersion = null;
      
      IE = true;
      
      IE60plus = (browserVersion >= "6.0");
      IE55plus = (browserVersion >= "5.5");
      IE50plus = (browserVersion >= "5.0");
      IE40plus = (browserVersion >= "4.0");
      
      IE6 = IE60plus;
      
      IE55 = IE55plus && !IE60plus;
      IE50 = IE50plus && !IE55plus;
      IE5  = IE50 || IE55;
      IE40 = IE40plus && !IE5;
      
      css1Supported = IE50plus;
      css2Supported = IE55plus;
      
      scriptEngine = ScriptEngineMajorVersion() + "." + ScriptEngineMinorVersion() + "." + ScriptEngineBuildVersion();      
   } else {
      // Try Mozilla/Netscape     
      
      userAgent      = ""+navigator.userAgent;      
      mozilla        = ""+userAgent.match(/Mozilla\/\d+\.\d+/);            
      mozillaVersion = mozilla.match(/\d+\.\d+/);
      
      opera          = userAgent.match(/Opera/);
      if (opera) {
         browserType = "Opera";
         Opera = true;
         return;
      }      
            
      if (mozillaVersion >= "5") {
      	 browserRelease = userAgent.match(/rv\:([^; \)]+)\)/);
      	 if (browserRelease) {
      		 browserRelease = browserRelease[1];
      	 } else {
      	     browserRelease = userAgent.match(/(m[^; \)]+)\)/);
      	    
      	     if (browserRelease) {
      	    	 browserRelease = browserRelease[1];
      	     } else {
      	    	 browserRelease = 'unknown: '+userAgent;
      	     }      	    
      	 }
      	 
         geckoRelease   = userAgent.match(/Gecko\/(\d+)/)[1];
      	 netscape       = userAgent.match(/Netscape\d*\/(\d+\.\d+[^ \/]*)?/);
      	 
      	 if (netscape) {
      	 	browserVersion = netscape  = ""+netscape[1];
      	 	browserType = 'NN';
      	    //browserVersion = netscape.match(/(\d+\.\d+[^ \/]*)/)[0];      	       	   
            NN = true;      
            
            NN4plus = (browserVersion >= "4.0");
            MN6plus = MN60plus = NN60plus = NN6plus = (browserVersion >= "6.0");
            MN61plus = NN61plus = (browserVersion >= "6.1");
            MN62plus = NN62plus = (browserVersion >= "6.2");
            MN7plus = MN70plus = NN7plus = NN70plus = (browserVersion >= "7.");
            
            MN = NN6plus;
            
            NN4  = NN4plus && !NN6plus;      
            MN6  = NN6  = NN6plus  && !(browserVersion >= "7");
            MN60 = NN60 = NN6      && !NN61plus;
            MN61 = NN61 = NN61plus && !NN62plus;
            MN62 = NN62 = NN62plus && !(browserVersion >= "6.3");
            
            MN7  = NN7  = NN7plus  && !(browserVersion >= "8");
            MN70 = NN70 = NN7      && !(browserVersion >= "7.1");
            
      	 } else {
      	 	browserType = 'Mozilla';
      	 	browserVersion = mozillaVersion;
      	 	Mozilla = true;
      	 	NN      = false;      	 	
      	 	MN      = true;
      	 	
      	 	var M5  = (mozillaVersion = "5.0");
      	 	var M5x = (mozillaVersion > "5.0");
      	 	
      	 	MN6plus = MN60plus = Mozilla088plus = (M5 && (browserVersion >= "0.8.8")) || M5x;
      	 	MN61plus = Mozilla092plus = (M5 && (browserVersion >= "0.9.2")) || M5x;
      	 	MN62plus = Mozilla094plus = (M5 && (browserVersion >= "0.9.4")) || M5x;
      	 	MN63plus = Mozilla099plus = (M5 && (browserVersion >= "0.9.9")) || M5x;
      	 	MN64plus = Mozilla1plus   = (M5 && (browserVersion >= "1"))     || M5x;
      	 	MN7plus = MN70plus = Mozilla101plus = (M5 && (browserVersion >= "1.0.1")) || M5x;
      	 	
      	 	MN6  = (mozillaVersion >= "5.") && (mozillaVersion < "6.") && (browserVersion < "1.0.1");
      	 	MN60 = Mozilla088 = M5 && (browserVersion == "0.8.8");
      	 	MN61 = Mozilla092 = M5 && (browserVersion == "0.9.2");
      	 	MN62 = Mozilla094 = M5 && (browserVersion == "0.9.4");      	 	
      	 	MN63 = Mozilla099 = M5 && (browserVersion == "0.9.9");      	 	
      	 	Mozilla1 = M5 && (browserVersion >= "1") && (browserVersion <= "2");      	 	
      	 	
      	 	MN7  = (mozillaVersion >= "5.") && (mozillaVersion < "6.") && (browserVersion >= "1.0.1");
      	 	MN70 = Mozilla101 = M5 && (browserVersion == "1.0.1");      	 	      	 	
      	 }      	 
      } else {
      	 // Old Mozilla stuff      	 
         browserVersion = mozillaVersion;
         NN = true;
         NN4plus = (browserVersion >= "4.");
         NN4 = NN4plus && !(browserVersion >= "5.");
         browserType = 'NN';
         geckoRelease = null;
         
      }
                  
      css1Supported = MN60plus;
      css2Supported = MN60plus;
      
   }
}

xbDetectBrowser();

//-----------------------------------------------------------------------------
// xbApply
//
// Description:
// Abstracts the 'apply' method of function object for browsers that do not 
// have it. Works for up to 10 arguments if apply method is not supported.
//
// Arguments: 
// 
//    method         - method to invoke inside host
//    host           - equivalent of thisArg of apply method
//    argumentArray  - array of arguments to pass
//    tempMethodName - OPTIONAL temporary method name - usen in simulation only
//
// Result:
//    Passes the result of invoked method.
//
// Side-effects:
//
//    Temporary method property is created in the host object in case 'apply' 
//    method is not available. The name is equal to tempMethodName argument or,
//    if the argument is not specified, follows the structure:
//
//                  __APPLY__<method name>
//
// Change date-stamps:
// 2001-08-01-01 - Created. [Aleksandar Susnjar]
//-----------------------------------------------------------------------------
function xbApply(method, host, argumentArray, tempMethodName) {
   if (method.apply) { 
      if (argumentArray)
         return method.apply(host, argumentArray);
      else
         return method.apply(host, []);
      
   } else {
      if (!tempMethodName)
         tempMethodName = "__APPLY__"+xbFunctionName(method);

      host[tempMethodName] = method;
      
      var result;
      
      if (!argumentArray) 
         result = host[tempMethodName]();
      else
      
      switch (argumentArray.length) {
         case 0:
            result = host[tempMethodName]();
            break;
            
         case 1:
            result = host[tempMethodName](argumentArray[0]);
            break;
            
         case 2:
            result = host[tempMethodName](
               argumentArray[0], 
               argumentArray[1]
            );
            break;   
         
         case 3:
            result = host[tempMethodName](
               argumentArray[0], 
               argumentArray[1],
               argumentArray[2]
            );
            break;
            
         case 4:
            result = host[tempMethodName](
               argumentArray[0], 
               argumentArray[1],
               argumentArray[2],
               argumentArray[3]
            );
            break;
            
         case 5:
            result = host[tempMethodName](
               argumentArray[0], 
               argumentArray[1],
               argumentArray[2],
               argumentArray[3],
               argumentArray[4]
            );
            break;
            
         case 6:
            result = host[tempMethodName](
               argumentArray[0], 
               argumentArray[1],
               argumentArray[2],
               argumentArray[3],
               argumentArray[4],
               argumentArray[5]
            );
            break;
            
         case 7:
            result = host[tempMethodName](
               argumentArray[0], 
               argumentArray[1],
               argumentArray[2],
               argumentArray[3],
               argumentArray[4],
               argumentArray[5],
               argumentArray[6]
            );
            break;
            
         case 8:
            result = host[tempMethodName](
               argumentArray[0], 
               argumentArray[1],
               argumentArray[2],
               argumentArray[3],
               argumentArray[4],
               argumentArray[5],
               argumentArray[6],
               argumentArray[7]
            );
            break;
            
         case 9:
            result = host[tempMethodName](
               argumentArray[0], 
               argumentArray[1],
               argumentArray[2],
               argumentArray[3],
               argumentArray[4],
               argumentArray[5],
               argumentArray[6],
               argumentArray[7],
               argumentArray[8]
               
            );
            break;
            
         case 10:
         default:
            result = host[tempMethodName](
               argumentArray[0], 
               argumentArray[1],
               argumentArray[2],
               argumentArray[3],
               argumentArray[4],
               argumentArray[5],
               argumentArray[6],
               argumentArray[7],
               argumentArray[8],
               argumentArray[9]
            );
            break;
            
      }        
      delete host[tempMethodName];  
      return result;           
   }
}

//-----------------------------------------------------------------------------
// xbCall
//
// Description:
// Abstracts the 'call' method of function object for browsers that do not 
// have it by using xbApply.
//
// Arguments: 
//    1. argument: method - same as 'method' argument of xbApply
//    2. argument: host   - same as 'host' argument of xbApply
//    3+. arguments form an argumentArray for xbApply
//
// Result:
//    Passes the result of invoked method.
//
// Change date-stamps:
// 2001-08-01-01 - Created. [Aleksandar Susnjar]
//-----------------------------------------------------------------------------
function xbCall() {
   var argCount = arguments.length - 2;
   var args = new Array(argCount);
   for (i=0; i<argCount; i++) 
      args[i] = arguments[i+2];
          	      
   return xbApply(arguments[0], arguments[1], args);
}

//-----------------------------------------------------------------------------
// xbFunctionName
//
// Description:
// Returns the name of the function specified as an argument. Uses regular
// expressions to extract this name from function's source acquired by
// toString() method.
//
// Arguments: 
//
//    f - reference to the function
//
// Result:
//
//    Name of the function in form of a string.
//
// Change date-stamps:
//
// 2001-08-01-01 - Created. [Aleksandar Susnjar]
//-----------------------------------------------------------------------------
function xbFunctionName(f) { 
   return f.toString().match(/^\s*function\s+(\w+)/)[1];
}

//-----------------------------------------------------------------------------
// xbClassName
//
// Description:
// Returns the name of the constructor function used to construct the object
// referenced by the <obj> argument. However, if object's prototype contains
// 'className' property, that property will be returned instead.
//
// Arguments: 
//
//    obj - reference to the object being inspected
//
// Result:
//
//    Name of the class of the object.
//
// Dependencies/Notes:
//
//    className prototypic property dependency:
//    This property is used for the result if it exists for the object.
//
// Change date-stamps:
//
// 2001-08-01-01 - Created. [Aleksandar Susnjar]
//-----------------------------------------------------------------------------
function xbClassName(obj) {
   return (obj.constructor.prototype.className) ? obj.constructor.prototype.className : xbFunctionName(obj.constructor);
}


//-----------------------------------------------------------------------------
// xbUpdateInnerHTML
//
// Description:
// Updates innerHTML in a cross-browser compatible way.
//
// Arguments: 
//
//    doc     - document containing the element to be updated
//    id      - id of the element to be updated
//    content - new content for the element
//
// Result:
//
//    None.
//
// Dependencies/Notes:
//
//       Does not work for NN while loading the page. 
//       Has to be run after loading is finished.
//       Netscape also requires style="position: absolute;" 
//
// Change date-stamps:
//
// 2001-08-01-01 - Created. [Aleksandar Susnjar]
//-----------------------------------------------------------------------------

function xbUpdateInnerHTML(doc, id, content) {
   if (IE) {
      doc.all[id].innerHTML = content;
   } else
   
   if (NN) {
      with (doc[id].document) {
         open();
         write(content);
         close();
      }
   }
}

//-----------------------------------------------------------------------------
// xbTemporaryID
//
// Description:
// Returns a unique ID by attaching given prefix and postfix to the current
// value of internal counter.
//
// Arguments: 
//
//    prefix  - [OPTIONAL] string to precede the number. 
//              Will use "$TMP" if not specified.
//
//    postfix - [OPTIONAL] string to follow the number.
//              Will use "" (empty styring) if not specified.
//
// Result:
//
//    Unique ID string.
//
// Change date-stamps:
//
// 2001-08-01-01 - Created. [Aleksandar Susnjar]
//-----------------------------------------------------------------------------

function xbTemporaryID(prefix, suffix) {
   if (!xbTemporaryID.prototype.counter)
      xbTemporaryID.prototype.counter = 0;
      
   return (prefix ? prefix : "$TMP")+(++xbTemporaryID.prototype.counter) + (suffix ? suffix : "");
}

function xbSetFrameURL(frame, url) {
   if (frame.src)
      frame.src = url;
   else
      frame.location = url;
}

function escapeHelper(p1) { return '&#'+p1.charCodeAt(0)+';'; }
var escapeRegExp = /([<>&'"])/g;
escapeRegExp.compile('([<>&\'"])', 'g');

function escapeTextForHTML(txt, spaceEscape) {
	if (spaceEscape) 
	   return txt.replace(escapeRegExp, escapeHelper).replace(/ /g, spaceEscape);
	else
	   return txt.replace(escapeRegExp, escapeHelper);
	
}

function repaintWindow(wnd) {
   if (NN) {
   	if (wnd)
   	   wnd.resizeBy(0,0);
   	else {
   	   //alert("About to resize");
   	   window.offscreenBuffering = false;
   	   window.resizeBy(-10,-10);
   	   //alert("resized");
   	   window.resizeBy(10,10);
   	   window.offscreenBuffering = true;
   	}
   }
}

  function examine(obj) {
   	var rep="Object:\n";
   	
   	for (i in obj)
   	   rep += "\n"+i;
   	   
   	alert(rep);
   }
   
// This package must be initialized at the end!
var xbUtils = new XBUtils();

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//
//  ######## ########  ######  ########     ######  #######  ########  ###### 
//     ##    ##       ##    ##    ##       ##    ## ##    ## ##       ##    ##
//     ##    ##       ##          ##       ##    ## ##    ## ##       ##    ##
//     ##    ######    ######     ##       ######## #######  ######   ########
//     ##    ##             ##    ##       ##    ## ##  ##   ##       ##    ##
//     ##    ##       ##    ##    ##       ##    ## ##   ##  ##       ##    ##
//     ##    ########  ######     ##       ##    ## ##    ## ######## ##    ##
//-----------------------------------------------------------------------------
//    TEST AREA FOLLOWS. CONTAINS UNIT-TESTING CODE NOT USEABLE OTHERWISE.
//-----------------------------------------------------------------------------

