// %Basic functions that can be useful in most scripts.%
/*===========================================================================================
    Program:	sw_basics.js
     Author:	Bates
       Date:	1/17/06
    Purpose:	These functions provide basic but useful features.

   Required:	<script language=javascript src="/non-sm/sw_tools/sw_basics.js"></script>

		if 'tip' parameters are added to 'live' items (see the _makeLiveTD() function),
		the following is required: (see the sw_tips tool for additional requirements.)

                <script language=javascript src="/non-sm/sw_tools/sw_tips.js"></script>

		swbasics_viewElement() also requires:

		<link href="/non-sm/sw_tools/css/master.css" rel="stylesheet" type="text/css">
                <script language=javascript src="/non-sm/sw_tools/sw_cursor.js"></script>
                <script language=javascript src="/non-sm/sw_tools/sw_popup.js"></script>
  -------------------------------------------------------------------------------------------
   Function:	sw_browser()
		Return a lower-case version of the browser's userAgent string.
		This can be useful when dealing with browser personality qeurks.

      Input:	none

     Output:	null

   Examples:	browser		returned
		-------------	----------------------------------------------------------
		IE 6.0		mozilla/4.0 (compatible; msie 6.0; windows nt 5.1;
				sv1; .net clr 1.1.4322)

		FF 1.5		mozilla/5.0 (windows; u; windows nt 5.1; en-us; rv:1.8.0.2)
				gecko/20060308 firefox/1.5.0.2

		NS 8.1		mozilla/5.0 (windows; u; windows nt 5.1; en-us; rv:1.7.5)
				gecko/20060127 netscape/8.1


       Demo:	<a_href="test/whatbrowser.html">What browser are you using?.</a>
  -------------------------------------------------------------------------------------------
   Function:	refDiv(id)
		Return a handle to a div/span/td/p element with the provided id.

      Input:	id - the value that was specified in the 'id="..."' portion of the
		     div, span, td, or p element.

     Output:	Success -> The handle to the element
		Failure -> null.  (Either no tag with the given id exists OR more than one
		                   exists.)

    Example:	<script language=javascript>
		  ...
		  refDiv("myDiv").innerHTML = "Hoo-Ha!";
		  ...
		</script>
		<body>
		  ...
		  <div id="myDiv"></div>
		  ...
		</body>
  -------------------------------------------------------------------------------------------
   Function:	parseForm()
		Parse the parameter portion of the page's URL, creating a hashed ARGV array.

      Input:	none

     Output:	A hashed array.

    Example:	If the URL is "http://www.righthand.com/fingers.html?name=Ruffus T. Potter&age=25"
		then 'var ARGV = parseForm();' will create:
			ARGV['name'] = "Ruffus T. Potter"
			ARGV['age' ] = 25
  -------------------------------------------------------------------------------------------
   Function:	swbasics_makeHash(dataList,sep)
		Create a hash from the specified data.

      Input:	dataList - an array or a ","-delimited list in a string.
		     sep - the separator between items when dataList is a string.

     Output:	A hash array built from the specified list of items above.  For each item;
		o if the item is in the form of "L=R" then 'L' is the hash key and 'R' is
		  the value.
		o else if the item contains space-delimited words, the first word in the
		  hash key and the remainder of the item is the value.
		o Otherwise, the entire item is used for both the hash key AND the value.
  -------------------------------------------------------------------------------------------
   Function:	swbasics_copyHash(hash)
		Copy a hash structure, returning the new copy.

      Input:	hash - the hash to be copied.

     Output:	the new hash.
  -------------------------------------------------------------------------------------------
   Function:	swbasics_compareHash(hashA,hashB)
		Return the count of the differences between two hashes.

      Input:	hashA,hashB - the two hashes to be compared.

     Output:	 0 = the two are identical.
		>0 = the number of differences.
  -------------------------------------------------------------------------------------------
   Function:	swbasics_makeLR(hash,Lid,Rid,joint)
		Given a hash, create an array of LR values where each row of the created array
		is composed of:
			the 'Lid' value from 'hash' on the left,
		        the 'Rid' value from 'hash' on the right,
			the two are joined together by 'joint'.

      Input:	 hash - the hash array from which the values are to be pulled
		  Lid - the hash key from which the 'L' value will be taken
		  Rid - the hash key from which the 'R' value will be taken
		joint - the string to be used to join L to R

     Output:	An array, each element of which contains an LR definition durrived
		from a single row in tfhe hash.

    Example:	// Lookup the 'type' and 'description' values from the 'apps' table.
		// Build an LR array of 'description:type' data for each row of the table.
		// Stuff the 'appTypeSelect' form field with the data found...
		//
		appTypes = swsql_select('select type,description from apps',DATABASE,0);
		typesLR  = swbasics_makeLR(appTypes,'description','type',':');
		swform_stuffSelect("appTypeSelect",typesLR);
  -------------------------------------------------------------------------------------------
   Function:	swbasics_hashFind(hash,keyField,keyValue,neededField,defValue)
		Find the row in 'hash' who's [keyField] element == 'keyValue' and return the
		contents of the [neededField] element.  if not found, return the 'defValue'
		value or 'null' if 'defValue' not provided.

      Input:	       hash - the array of hashes to be searched
		   keyField - the hash key to be matched
		   keyValue - the value to be matched
		neededField - the hash key who's value is desired
		   defValue - (optional) the default value in the event a match is not found

     Output:	If a row is found in 'hash' where hash[keyField] == keyValue, then
		hash[neededField] is returned.

		If such a match is NOT found and 'defValue' is provided, then return 'defValue',

		Otherwise return null.

    Example:	// Find the street name used in the TV show who's "dad" character is named
		// "Herman Munster".  Expect "unkown" if the match cannot be found...
		//
		var street = swbasics_hashFind(tvShows,"dad","Herman Munster","street","unknown");
  -------------------------------------------------------------------------------------------
   Function:	hashKeys(hash)
		Return an array containing the hash keys from the provided hash.

      Input:	hash - a hash array

     Output:	An array of strings

    Example:	var myHash = new Array();
		myHash['Gomer'] = "Pyle";
		myHash['Bozo' ] = "Clown";
		myHash['Oscar'] = "Grouch";
		myKeys = hashKeys(myHash);	// myKeys[0]="Pyle", myKeys[1]="Clown", myKeys[2]="Grouch";

      Note:	Depending upon the browser used the order of the keys may NOT be returned in 'creation' order.
  -------------------------------------------------------------------------------------------
   Function:	swbasics_max(items)
		Return the greatest value from the provided list.

      Input:	items - an array (or ","-delimited list) of items to be considered.

     Output:	The greatest/largest item from the list.
  -------------------------------------------------------------------------------------------
   Function:	swbasics_min(items)
		Return the smallest value from the provided list.

      Input:	items - an array (or ","-delimited list) of items to be considered.

     Output:	The lowest/smallest item from the list.
  -------------------------------------------------------------------------------------------
   Function:	swbasics_pos(item)
		Return the actual screen position of the given element.

      Input:	item - the handle to a page element (e.g.; from refDiv())

     Output:	A hashed array wherein:
		array.X = the X position of the upper-left corner of the item
		array.Y = the Y position of the upper-left corner of the item

    Example:	it = refDiv("myTable");
		var pos = swbasics_myPos(it);
		alert("myTable is at " + pos.X + ":" + pos.Y);
  -------------------------------------------------------------------------------------------
   Function:	swbasics_left(item,seed)
		Return the actual screen 'left' position of the given element.

      Input:	item - the handle to a page element (e.g.; from refDiv())
		seed - a minimum offset, usually 0.

     Output:	An integer value >= seed.

    Example:	it = refDiv("myTable");
		var X = swbasics_left(it);
		alert("myTable is " + X + "pixels from the left edge of the screen.");
  -------------------------------------------------------------------------------------------
   Function:	swbasics_top(item,seed)
		Return the actual screen 'top' position of the given element.

      Input:	item - the handle to a page element (e.g.; from refDiv())
		seed - a minimum offset, usually 0.

     Output:	An integer value >= seed.

    Example:	it = refDiv("myTable");
		var Y = swbasics_top(it);
		alert("myTable is " + Y + "pixels from the top edge of the screen.");
  -------------------------------------------------------------------------------------------
   Function:	swbasics_properCase(items)
		Convert every word in 'items' to proper case.

      Input:	items - the words to be converted.  This can be:
		        o a string containing a word
		        o a string containing a phrase
		        o a string containing a list of ","-delimited phrases
		        o an array containing 1 or more phrases

     Output:	if 'items' is a string then a string is returned, otherwise
		if 'items' is an array then a similar array is returned.

      Notes:	Any character following any of these: - & / . or ' is forced to upper case.
		See swbasics_addStandardTranslation() for more.

    Example:	var properName = swbasics_properCase("NAZEL T. LARDBUTT");
		This returns the string "Nazel T. Lardbutt"

    Samples:	This:                  	Translates to:
		=======================	==========================
		RANDAL BATES		Randal Bates
		peter o'tool		Peter O'Tool
		steve mcqueen		Steve McQueen
		JOHN ANDERSON III	John Anderson III
  -------------------------------------------------------------------------------------------
   Function:	swbasics_addStandardTranslation(item[s])
		Add conversion specifications for swbasics_properCase() to consider.

      Input:	item[s] - An array (or ','-delimited string) containing one or more translation
		          specifications, each in a "This=that" format.

     Output:	null

   Examples:	swbasics_addStandardTranslation(["Soc=SOC","Roc=ROC"]);
		swbasics_addStandardTranslation("Soc=SOC,Roc=ROC");
		// In both cases, swbasics_properCase() is instructed to translate "Soc" to "SOC"
		// and "Roc" to "ROC" when ever found as a stand-alone word.

      Notes:	o Two standard translations exist by default: "Iii=III" and "Ii=II".
		o In any specification, the "This" (left) portion will be forced into the
		  'Initial Cap' form.
		o In any specification, the "This" (left) portion MUST BE A SINGLE WORD!
		  OK			Not OK
		  =============		==============
		  "This=THAT"		"This here=that there"
		  "orange=lemon		"orange jell-o=lemon jell-o"
  -------------------------------------------------------------------------------------------
   Function:	swbasics_makeLiveTD(id,parmList)
		Build a TD definition that can be used in a menu, listbox, etc. where the
		entire surface of the TD is 'clickable'.

      Input:	      id - the unique id of this TD
		parmList - an array (or ","-delimited list) of parameters, as follows:
			o callback  - the name of a function to be attached to 'single click'
			o doublecb  - the name of a function to be attached to 'double click'
			o mouseover - the name of a function to be attached to 'mouse over'
			o mouseout  - the name of a function to be attached to 'mouse out'
			o grab      - the name of a function to be attached to 'mouse down'
			o drag      - the name of a function to be attached to 'mose move'
			              while 'grabbed'
			o drop      - the name of a function to be attached to 'mouse up'
			              after a 'grab'
			o menu      - the name of a function to be attached to 'right click'
			o contents  - the data to be placed within the '<TD>...</TD>' tags
			o source    - a fully-composed '<td>..</td>' tag to be used as the
			              template for the new item.
			o tip       - an optional string detailing a too-tip-type (see
		                      sw_tips) and 'tool-tip' text to be displayed on
		                      mouseover.  Example: "tip=alert:Required Entry!"

			Anything else will be placed within the '<TD...>' definition.

     Output:	A string containing the fully constructed TD tag.

      Notes:	If the 'source' option is provided then the 'contents' option is ignored,
		as the innerHTML of the '<td>..</td>' string found in 'source' will be
		used instead.

    Example:	// create a 'Sort' TD that calls the 'doSort()' function and uses the
		// 'standard' css definition for TD...
		sortTD = swbasics_makeLiveTD("sorter",["callback=doSort()",
		                                       "contents=Sort",
		                                       "class='standard'"]);
		swbasics_activateLiveTD("sorter");
  -------------------------------------------------------------------------------------------
   Function:	swbasics_adaptTD(id,parmList)
		Adapt an existing TD cell to behave as a 'live TD' (see swbasics_makeLiveTD()).

      Input:	      id - the unique id of this TD
		parmList - an array (or ","-delimited list) of parameters, as follows:
			o callback  - the name of a function to be attached to 'single click'
			o doublecb  - the name of a function to be attached to 'double click'
			o mouseover - the name of a function to be attached to 'mouse over'
			o mouseout  - the name of a function to be attached to 'mouse out'
			o grab      - the name of a function to be attached to 'mouse down'
			o drag      - the name of a function to be attached to 'mose move'
			              while 'grabbed'
			o drop      - the name of a function to be attached to 'mouse up'
			              after a 'grab'
			o menu      - the name of a function to be attached to 'right click'
			o tip       - an optional string detailing a too-tip-type (see
		                      sw_tips) and 'tool-tip' text to be displayed on
		                      mouseover.  Example: "tip=alert:Required Entry!"

			Anything else will be ignored.

     Output:	null.

       Note:	The element will not become 'live' until swbasics_activateLiveTD() is called.
  -------------------------------------------------------------------------------------------
   Function:	swbasics_activateLiveTD(idList)
		Activate the event listeners attached to a TD tag created by swbasics_makeLiveTD()
		(or a set of such TDs).

      Input:	idList - an array (or ","-delimited list) of id's that were used as the 'id'
		         parameter when calling swbasics_makeLiveTD().

     Output:	null

    Example:	sortTD = swbasics_makeLiveTD("sorter",["callback=doSort()","contents=Sort"]);
		pullTD = swbasics_makeLiveTD("puller",["callback=doPull()","contents=Pull This"]);
		swbasics_activateLiveTD(["sorter","puller"]);
  -------------------------------------------------------------------------------------------
   Function:	swbasics_addEventHandler(id,parmList)
		Add 1 or more event handlers to an existing element.

      Input:	      id - the unique id of this TD
		parmList - an array (or ","-delimited list) of parameters (see swbasics_makeLiveTD())

     Output:	null

      Notes:	1) The element MUST have an id.
		2) swbasics_activateLiveTD() need not be called.
  -------------------------------------------------------------------------------------------
   Function:	swbasics_catchErrors(options)
		Set up a handler to gracefully catch javascript errors.

		If a js error is encountered on the page, an error page describing the situation
		is presented either in place of the offending page or in a new window.

		If the 'admin' parameter is provided, that person is identified on the error page
		as the site administrator.

		If the 'email' parameter is provided, that is also indicated on the error page
		along with a link to have the admin person alerted via email about the error.

		If the browser is IE, information about the error is also provided on
		the error page.

      Input:	options - an array (or ","-delimited list) of optional parameters which may
		be any combination of any of the following:
		    environment= (optional) one of: "production" | "test" | "devel"    (1)
		                            (default: "production")
			  admin= the name of the site administrator.
			  email= the email address of the site admin.
			    url= the name of a custom error handling page (to use instead of
			         the error handler's default page.)
			  style= the handler style which may be one of these:
				  replace - (default) replace the offending page with the error page
				      new - bring the error page into a new window, leaving
				            the offending page in place
				   ignore - clear the error and do nothing
		Ad-hoc options:
		  Anything else provided in a "name=value" format and will be included in the error
		  report.

		(1) The optional 'environment' parameter identifies the current running environment and
		    effects which version of the "jsError.html" file is used when reporting an error.

     Output:	null

    Example:	swbasics_catchErrors(["admin=Bozo T. Clowne","email=bozo@bigtop.org","userKey="+userKey]);

		(note: In the example above, the '"userKey="+userKey' parameter is an application-provided
		       ad-hoc parameter to be included in the error report.)

      Notes:	If the page exists within a frame and the style is "replace" then the top-most
		page is replaced with the error page.

       Demo:	<a_href="test/testerror.html">A non-frames example.</a>
       Demo:	<a_href="test/testerror2.html">A frames example.</a>
       Demo:	<a_href="test/testerror3.html">An example wherein all JS errors are ignored.</a>
       Demo:	<a_href="test/testerror4.html">An example brings the error page into a new window.</a>
  -------------------------------------------------------------------------------------------
   Function:	swbasics_funcStack(action,parameters)
		(Attempt to) manage a local "call stack" to be provided when an error is reported
		via swbasics_catchErrors().

      Input:	    action - one of: "push" | "pop" | "read".
		parameters - a string or array of context-specific parameters as follows:

			when action="push"...

				if 'parameters' is a string then it is the name of the function being entered,
				otherwise 'parameters' is an array where
					parameters[0] is the name of the function being entered
					parameters[1..n] are parameters supplied to that function.

			when action="pop"...

				if 'parameters' is NULL then NULL is returned after the pop.
				if 'parameters' is a string then that string is returned after the pop.
				if 'parameters' is an array then...
					parameters[0] is returned after the pop
					parameters[1] selects:
						0 = ignore stack underrun
						1 = report stack underrun

			when action="read"...

				'parameters' is ignored.

     Output:	action = "push" or "pop": 0.
		action = "read": a string containing a '||'-delimited list of functions representing
		                 the 'backtrace'.

    Example:	swbasics_funcStack("push","myFunction",["0",'"goober"']);
		swbasics_funcStack("push","myOtherFunc");
		swbasics_funcStack("push","myThirdFunc","this=that");
		swbasics_funckStack("pop",0);		// "myThirdThread" is popped off the stack and 0
							// is returned.
		var backTrace = swbasics_funcStack("read",0);

		// backTrace should now contain:
		//
		// myFunction(0,"goober")
		// `-> myOtherFunc()

      Notes:	action="push" should be used at the beginning of a function to add that
		       function to the call stack.
		action="pop" should be used just before the function returns to remove
		       it from the call stack.
		action="read" will be used by swbasics_handleError() to add the call stack
		       to the error report.
  -------------------------------------------------------------------------------------------
   Function:	swbasics_isNum(arg)
		Return true is arg represents an integer number.

      Input:	arg - the value to be tested.

     Output:	boolean result

       Note:	!isNaN(arg) might be the same and perform more quickly.  (sigh)
  -------------------------------------------------------------------------------------------
   Function:	swbasics_formatDate(then,format)
		Format a date object according to an application-supplied format string.

      Input:	  then - a date object (as returned by 'new Date()', for example)
		format - an of the following:
			Format			Sample result
			---------------------	------------------------
			mm/dd/yy		12/31/06
			mm/dd/yyyy		12/31/2006
			yy-mm-dd		06-12-31
			yyyy-mm-dd		2006-12-31
			mon dd, yy		Dec. 31, 06
			mon dd, yyyy		Dec. 31, 2006
			month dd, yy		December 31, 06
			month dd, yyyy		December 31, 2006

     Output:	The formatted string.

      Notes:	The basic rule 'GIGO' applies!
  -------------------------------------------------------------------------------------------
   Function:	swbasics_pad(it,pChar,len)
		Pad 'it' on the RIGHT with 'pChar' until it is at least 'len' characters long.

      Input:	   it - the string to be stretched
		pChar - the character to be used
		  len - the desired length

     Output:	it, modified accordingly.

   Examples:	swbasics_pad("Goober",".",10) will return "Goober...."
		swbasics_pad("Goober",".",3)  will return "Goober"

      Notes:	o if 'it' is already 'len' characters long (or longer!) it will not be changed.
		o if 'pChar' is more than one character long, don't be surprised if the returned
		  string is longer than 'len'.
  -------------------------------------------------------------------------------------------
   Function:	swbasics_lpad(it,pChar,len)
		Pad 'it' on the LEFT with 'pChar' until it is at least 'len' characters long.

      Input:	   it - the string to be stretched
		pChar - the character to be used
		  len - the desired length

     Output:	it, modified accordingly.

   Examples:	swbasics_lpad("Goober",".",10) will return "....Goober"
		swbasics_lpad("Goober",".",3)  will return "Goober"

      Notes:	o if 'it' is already 'len' characters long (or longer!) it will not be changed.
		o if 'pChar' is more than one character long, don't be surprised if the returned
		  string is longer than 'len'.
  -------------------------------------------------------------------------------------------
   Function:	swbasics_viewElement(id)
		View the properties of a page element.

      Input:	id - a string naming the page element to be viewed.

     Output:	A div is produced on top of the current page to present the element's properties.

   Requires:	<link href="/non-sm/sw_tools/css/master.css" rel="stylesheet" type="text/css">
                <script language=javascript src="/non-sm/sw_tools/sw_cursor.js"></script>
                <script language=javascript src="/non-sm/sw_tools/sw_popup.js"></script>
  -------------------------------------------------------------------------------------------
   Function:	trim(it)
		Remove unnecessary spaces from both ends of 'it'.

      Input:	it - a string to be trimmed.

     Output:	The trimmed string.
  -------------------------------------------------------------------------------------------
   Function:	ltrim(it)
		Remove unnecessary spaces from the left (leading) end of 'it'.

      Input:	it - a string to be trimmed.

     Output:	The trimmed string.
  -------------------------------------------------------------------------------------------
   Function:	rtrim(it)
		Remove unnecessary spaces from the right (trailing) end of 'it'.

      Input:	it - a string to be trimmed.

     Output:	The trimmed string.
  -------------------------------------------------------------------------------------------
   Function:	sprintf(format,arguments)
		Simulate the c sprintf() function.

      Input:	   format - a string defining the desired output.
		arguments - an array of values to be used within the format.

     Output:	A formatted string.

    Formats:	The following flags are supported:

		Format				The corresponding argument...
		============================	====================================================
		%c				...'s first character is used.
		%[flags][width]d		(see %i.)
		%[flags][width[.precision]]E	(same as %e but output is in upper-case.)
		%[flags][width[.precision]]e	...is shown in scientific notation.
		%[flags][width[.precision]]f	...is shown in floating point notation.
		%[flags][width[.precision]]G	(same as %g but output is in upper-case.)
		%[flags][width[.precision]]g	...is shown in double-precision format.
		%[flags][width]i		...argument is shown as a base-10 integer.
		%[flags][width[.precision]]o	...argument is shown as an octal (base-8) integer.
		%[flags][width[.precision]]p	(same as %s but the output is forced to 'propper' case.)
		%[flags][width[.precision]]S	(same as %s but the output is forced to upper-case.)
		%[flags][width[.precision]]s	...argument is shown as a (possibly padded) string.
		%[flags][width[.precision]]u	(same as %i but forced to unsigned notation.)
		%[flags][width[.precision]]X	(same as %x but output is in upper-case.)
		%[flags][width[.precision]]x	...argument is shown as a hex (base-16) integer.

		Flags:	(where they apply...)
			blank	Positive numeric values are prepended with a ' '.
			+	Positive numeric values are prepended with a '+'.
			-	Negative numeric values are prepended with a '-'.
			(	Negative numeric values are bracketed with parens, '-' is stripped.
			$	Force the numeric output into a US-Dollar format ( [##,]###.## ).
			,	Force US ',' rules on the whole portion of a numeric output.
			#	Context-specific:
				%#o (Octal) 0 prefix forced.
				%#x (Hex)   0x prefix forced on non-zero values.
				%#X (Hex)   0X prefix forced on non-zero values.
				%#e         Always show the decimal point.
				%#E         Always show the decimal point.
				%#f         Always show the decimal point.
				%#g         Always show the decimal point trailing zeros not removed.
				%#G         Always show the decimal point trailing zeros not removed.

		Width:	When dealing with numerics the value is padded on the LEFT with spaces (*)
			until the result is at least 'width' characters long.
			(* if the width begins with a '0' then the value is padded with '0's instead)

			When dealing with strings the output is padded on the RIGHT ('-' flag provided)
			or on the LEFT ('+' flag, or no flag) with spaces until it is at least 'width'
			characters long.
			sprintf("%10s","Randal")  ->  "    Randal"
			sprintf("%-10s","Randal") ->  "Randal    "

		Precision:
			When dealing with numerics this defines the number of decimal places are allowed
			(where they apply.)

			When dealing with strings this defines the MAXIMUM length of the resulting value
			to be used (trimming off on the RIGHT) BEFORE padding is applied.
			sprintf("%10.5s","Randal")  ->  "     Randa"
			sprintf("%-10.5s","Randal") ->  "Randa     "
  -------------------------------------------------------------------------------------------
   Function:	appendScript(filename)
		Append to the HEAD element a new script (js, php, etc.) as a javascript.
		Care is taken to append each file only once.

      Input:	filename - a string naming the file to be added.

     Output:	null
  -------------------------------------------------------------------------------------------
   Function:	appendCSS(filename)
		Append to the HEAD element a new css file.
		Care is taken to append each file only once.

      Input:	filename - a string naming the file to be added.

     Output:	null
  -------------------------------------------------------------------------------------------
   Function:	swbasics_dimensions(it)
		Return a hash containing the 'wide' and 'tall' dimensions of an element.
		If no element is named - or the named element doesn't exist - then the
		dimensions of the 'body' are used.

      Input:	it - (optional) the id or name of the element of interest.  If not provided
		     then the 'body' element is used.

     Output:	A hash, as follows:
		['item'] = (str) the name of the item that was measured.
		['wide'] = (int) the width of the element.
		['tall'] = (int) the height of the element.

      Notes:	If 'item' in the returned data is 'body' instead of the named element
		then the named element doesn't exist.

   Examples:	var dims = swbasics_dimensions("myDiv");
		alert("myDiv is " + dims['wide'] + " X " + dims['tall'] + " pixels.");

		var dims = swbasics_dimensions();
		alert("The body is " + dims['wide'] + " X " + dims['tall'] + " pixels.");

		var dims = swbasics_dimensions("myDiv");
		if (dims['item'] == "body") alert("Error: it appears that myDiv does not exist!");
  -------------------------------------------------------------------------------------------

===========================================================================================*/

var swbasics_debug_flag = 0;
function swbasics_debug(sw) {
  swbasics_debug_flag = parseInt(sw);
}


var swbasics_standardTranslations = swbasics_makeHash(["Iii=III",
                                                       "Ii=II"]);

function swbasics_addStandardTranslation(them) {

  var items = (typeof them == "string")? them.split(",") : them;

  for (var i=0; i<items.length; i++) {
    var pieces = items[i].split("=");
    var key    = pieces.shift();
    var val    = pieces.join("=");

    key = key.substr(0,1).toUpperCase() + key.substr(1).toLowerCase();
    key = key.replace(/ /g,"");

    swbasics_standardTranslations[key] = val;
  }
}


// Create a "getElementById()" function if the browser doesn't have one...
//
if (!document.getElementById) {
  document.getElementById = function(id) {
    if (!document.all) {
      alert("refDiv(): Incompatible browser");
      return(null);
    }

    return(eval("document.all."+id));
  }
}

function refDiv(id) {

  if (id.indexOf(":") > 0) {
    var dp  = id.split(":")[0] + ".document";
    var doc = eval(dp);
    if (doc) {
      var id = id.split(":")[1];
      var it = (doc.getElementById)? doc.getElementById(id) : eval(doc + ".all." + id);

      return(it);
    }
  }

  return(document.getElementById(id));
}


function sw_browser() {
  return(navigator.userAgent.toLowerCase());
}

function parseForm() {
  var ARGV = new Array();
  if (location.search.length > 1) {
    var formParts = location.search.substr(1).split("&");
    for (i in formParts) {
      if (formParts[i].indexOf("=") > 0) {
        L = formParts[i].split("=")[0];
        R = formParts[i].split("=")[1];
      } else {
        L = formParts[i];
        R = formParts[i];
      }
      ARGV[L] = R;
    }
  }
  return(ARGV);
}

function swbasics_makeHash(dataList,sep) {

  // If 'sep' is specified, split 'dataList' on that value,
  // otherwise, if 'dataList' is a string then split it on ",",
  // otherwise use it as the array that it already is...
  //
  var data = (sep)? dataList.split(sep) : (typeof dataList == "string")? dataList.split(",") : dataList;
  var hash = new Array();
  var L;
  var R;

  for (var d in data) {                         // For each item in dataList...
    if (data[d].indexOf("=") > 0) {
      var pieces = data[d].split("=");          // if it has an '=' sign in it then
      L = pieces.shift();                       // L is the part left of the '=' and
      R = pieces.join("=");                     // R is everything to the right.
    } else if (data[d].indexOf(":") > 0) {
      var pieces = data[d].split(":");
      L = pieces.pop();
      R = pieces.join(":");
    } else if (data[d].indexOf(" ") > 0) {
      var pieces = data[d].split(" ");          // otherwise, if it has a ' ' in it then
      L = pieces.shift();                       // L is the first 'word' and
      R = pieces.join(" ");                     // R is everything else
    } else {
      L = data[d];                              // if all else fails, L and R both
      R = data[d];                              // get the entire item
    }

    hash[L] = R;                                // create an item in the hash array where
  }                                             // L is the hash key and R is it's value

  return(hash);
}

function swbasics_copyHash(it) {

  var hash = null;

  if (it) {
    hash = new Array();

    for (that in it) {

      if (typeof it[that] != "object") {
        hash[that] = it[that];
      } else {
        hash[that] = swbasics_copyHash(it[that]);
      }
    }
  }

  return(hash);
}

var ranmanStack = new Array();

function swbasics_compareHash(A,B) {
  var diffs = 0;

  var hkA = hashKeys(A).join(",");
  var hkB = hashKeys(B).join(",");
  if (hkA != hkB) { return(1); }

  for (var it in A) {
    if (!isNaN(it)) {
      if (parseInt(it) == 0) {
        for (var x=0; x<A.length; x++) {
          if (typeof A[x] == "object") {
            ranmanStack.push(x);
            diffs += swbasics_compareHash(A[x],B[x]);
            ranmanStack.pop();
          } else {
            if (A[x] != B[x]) {
              if (swbasics_debug_flag  &&  (swbasics_debug_flag == 1))
                alert(A[x]+" != "+B[x]+" (where x='"+[ranmanStack.join(":"),x].join(":")+"')");
              ++diffs;
            }
          }
        }
      }
    } else if (typeof A[it] != "object") {
      if (A[it] != B[it]) {
        if (swbasics_debug_flag  &&  (swbasics_debug_flag == 1))
          alert(A[it]+" != "+B[it]+" (where it='"+[ranmanStack.join(":"),it].join(":")+"')");
        diffs++;
      }
    } else {
      ranmanStack.push(it);
      diffs += swbasics_compareHash(A[it],B[it]);
      ranmanStack.pop();
    }
  }

  return(diffs);
}


// given a hash, create an array of LR values.
// the 'Lid' value from each row of 'hash' is used on the left.
// the 'Rid' value from each row of 'hash' is used on the right.
// the two are joined together by 'joint'...
//
function swbasics_makeLR(hash,Lid,Rid,joint) {

  var list = new Array();

  for (var i=0; i<hash.length; i++) {
    if (hash[i][Lid]  &&  hash[i][Rid]) {
      list.push([hash[i][Lid],hash[i][Rid]].join(joint));
    }
  }

  return(list);
}

// find the row in 'hash' who's [keyField] element == 'keyValue' and return the
// contents of the [neededField] element.  if not found, return the 'defValue'
// value or 'null' if 'defValue' not provided...
//
function swbasics_hashFind(hash,keyField,keyValue,neededField,defValue) {

  var rv = (defValue)? defValue : null;

  if (hash) {
    for (var x in hash) {
      if (undefined != hash[x][keyField]  &&  undefined != hash[x][neededField]) {
        if (hash[x][keyField] == keyValue) {
          rv = hash[x][neededField];
        }
      }
    }
  }

  return(rv);
}


function hashKeys(that) {
  var res = new Array();
  for (var it in that) {
    res.push(it);
  }
  return(res.sort());
}

function hashWalk(that) {
  for (var it in that) {
    if (!confirm(it + "=" + that[it])) return;
  }
}

function swbasics_viewElement(ref) {

  var popup = (window.swpopup_init  &&  (typeof window.swpopup_init == "function"))? true : false;


  var them = ref.split(".");
  var that = refDiv(them[0]);
  if (that &&  (them.length > 1)) {
    for (var i=1; that && (i<them.length); i++) {
      that = that[them[i]];
    }
  }
  if (!that) {
    alert("couldn't resolve "+ref);
    if (them.length == 1) swbasics_viewElementDone();
    else {
      them.length -= 1;
      swbasics_viewElement(them.join("."));
    }
    return;
  }

  var obj = new Array();
  var fun = new Array();
  var str = new Array();
  var num = new Array();
  var bul = new Array();

  for (var it in that) {
    switch (typeof that[it]) {
      case  "string": str.push(it);
                      break;
      case  "object": if (it.substr(0,2) == "on") fun.push(it);
                      else                        obj.push(it);
                      break;
      case "boolean": bul.push(it);
                      break;
             default: num.push(it);
    }
  }

  var db = '<input type=button value="Done" onClick="swbasics_viewElementDone()">';
  var bb = '';
  if (them.length > 1) {
    them.length -= 1;
    bb = '<input type=button value="Back" onClick=swbasics_viewElement("' + them.join(".") + '")>';
  }

  var ihtml = '<form><table width=100%><tr><td><b><u>' + ref + '</u></b>:</td><td align=right>' + bb + db + '</td></tr></table></form>'
            + '<table><tr>';

  // Objects...
  //
  if (obj.length > 0) {
    obj.sort();
    ihtml += '<td valign=top><table><tr bgcolor=D5DFE6><th>OBJECTS</th></tr>';
    for (var i=0; i<obj.length; i++) {
      var link = that[obj[i]];
      if (link != "null") link = '<a href=javascript:swbasics_viewElement("' + ref+"."+obj[i] + '");>' + obj[i] + '</a>';
      ihtml += '<tr><td>' + link + '</td></tr>';
    }
    ihtml += '</table></td>';
  }

  // Functions...
  //
  if (fun.length > 0) {
    fun.sort();
    ihtml += '<td valign=top style="border-left:1px solid #000000;"><table><tr bgcolor=D5DFE6><th>FUNCTIONS</th></tr>';
    for (var i=0; i<fun.length; i++) {
      ihtml += '<tr><td>' + fun[i] + '</td></tr>';
    }
    ihtml += '</table></td>';
  }

  // Strings...
  //
  if (str.length > 0) {
    str.sort();
    ihtml += '<td valign=top style="border-left:1px solid #000000;"><table><tr bgcolor=D5DFE6><th colspan=2>STRINGS</th></tr>';
    for (var i=0; i<str.length; i++) {
      var innerds = that[str[i]];
      if (str[i].toUpperCase().indexOf("HTML") >= 0) {
        innerds = "<code>" + innerds.replace(/\</g,"&lt;").replace(/\>/g,"&gt;") + "</code>";
      }
      ihtml += '<tr><td align=right valign=top>' + str[i] + '=</td><td>' + innerds + '</td></tr>';
    }
    ihtml += '</table></td>';
  }

  // Booleans...
  //
  if (bul.length > 0) {
    bul.sort();
    ihtml += '<td valign=top style="border-left:1px solid #000000;"><table><tr bgcolor=D5DFE6><th colspan=2>BOOLS</th></tr>';
    for (var i=0; i<bul.length; i++) ihtml += '<tr><td align=right>' + bul[i] + '=</td><td>' + that[bul[i]] + '</td></tr>';
    ihtml += '</table></td>';
  }

  // Numerics...
  //
  if (num.length > 0) {
    num.sort();
    ihtml += '<td valign=top style="border-left:1px solid #000000;"><table><tr bgcolor=D5DFE6><th colspan=2>VALUES</th></tr>';
    for (var i=0; i<num.length; i++) ihtml += '<tr><td align=right>' + num[i] + '=</td><td>' + that[num[i]] + '</td></tr>';
    ihtml += '</table></td>';
  }

  ihtml += '</tr></table>';

  if (popup) {
    ihtml = '<div class="viewElementPopup">' + ihtml + '</div';
    swpopup_init("viewElement");
    swpopup_show("viewElement",ihtml,null,ref);
  } else {
    var hdiv = refDiv("swbasics_viewElement_div");
    if (!hdiv) {
      var hdiv = document.createElement("DIV");
          hdiv.setAttribute("id",       "swbasics_viewElement_div");
          hdiv.setAttribute("name",     "swbasics_viewElement_div");
          hdiv.setAttribute("className","viewElement");
          hdiv.setAttribute("class",    "viewElement");   // works with opera
      document.body.appendChild(hdiv);
    }
    hdiv.innerHTML = ihtml;
    hdiv.style.visibility = "visible";
  }
}

function swbasics_viewElementDone() {
  if (window.swpopup_init  &&  (typeof window.swpopup_init == "function")) {
    swpopup_hide("viewElement");
  } else {
    var hdiv = refDiv("swbasics_viewElement_div");
    if (hdiv) hdiv.style.visibility = "hidden";
  }
}

function arrayWalk(that) {
  for (var it in that) {
    if (!confirm(that + "[" + it + "]=" + that[it])) return;
  }
}

function swbasics_max(itemList) {
  var items = (typeof itemList == "string")? itemList.split(",") : itemList;
  items.sort();

  return(items.pop());
}

function swbasics_min(itemList) {
  var items = (typeof itemList == "string")? itemList.split(",") : itemList;
  items.sort();
  return(items[0]);
}

// Note: sepChar is UNDOCUMENTED and is for INTERNAL USE ONLY!
//
function swbasics_properCase(itemList,sepChar) {
  var items = (typeof itemList == "string")? itemList.split(",") : itemList;

  var sep = (sepChar)? sepChar : " ";

  for (var i=0; i<items.length; i++) {
    var words = items[i].split(sep);

    for (var w=0; w<words.length; w++) {

      words[w] = words[w].substr(0,1).toUpperCase() + words[w].substr(1).toLowerCase();

      if (sep == " ") {
        if (words[w].indexOf("-") >= 0) words[w] = swbasics_properCase(words[w],"-");
        if (words[w].indexOf("&") >= 0) words[w] = swbasics_properCase(words[w],"&");
        if (words[w].indexOf("/") >= 0) words[w] = swbasics_properCase(words[w],"/");
        if (words[w].indexOf(".") >= 0) words[w] = swbasics_properCase(words[w],".");
        if (words[w].indexOf("'") >= 0) words[w] = swbasics_properCase(words[w],"'");
      }

      if (words[w].indexOf("Mc") == 0) {
        words[w] = words[w].substr(0,2) + words[w].substr(2,1).toUpperCase() + words[w].substr(3);
      } else {
        if (swbasics_standardTranslations[words[w]]) {
          words[w] = swbasics_standardTranslations[words[w]];
        } else if (top.swbasics_standardTranslations[words[w]]) {
          words[w] = top.swbasics_standardTranslations[words[w]];
        }
      }
    }
    items[i] = words.join(sep);
  }

  var res = (typeof itemList == "string")? items.join(",") : items;

  return(res);
}

function swbasics_top(it,y,debug) {
  var report = new Array();
  if (debug) {
    report.push("swbasics_top("+it.id+","+y+",true)");
    var Y = y + ((it.offsetTop)? it.offsetTop : 0);
    report.push("received: "+y+", computed: "+Y);
  } else {
    var Y = y + ((it.offsetTop)? it.offsetTop : 0);
  }
  if (it.offsetParent) Y = swbasics_top(it.offsetParent,Y,debug);
  if (debug) {
    if (it.id) report.push("it.id = "+it.id);
    if (it.name) report.push("it.name = "+it.name);
    for (var t in it) {
      if (t.indexOf("Top")>=0  ||  t.indexOf("Y")>=0  ||  t.toLowerCase().indexOf("parent")>=0) {
        report.push("it."+t+" = "+it[t]);
      }
    }
    report.push("returning: "+Y);
    alert(report.join("\n"));
    if (confirm("Walk this?")) hashWalk(it);
  }
  return(Y);
}

function swbasics_left(it,x,debug) {
  var report = new Array();
  if (debug) {
    report.push("swbasics_left("+it.id+","+x+",true)");
    var X = x + ((it.offsetLeft)? it.offsetLeft : 0);
    report.push("received: "+x+", computed: "+X);
  } else {
    var X = x + ((it.offsetLeft)? it.offsetLeft : 0);
  }
  if (it.offsetParent) X = swbasics_left(it.offsetParent,X,debug);
  if (debug) {
    report.push("swbasics_left()...");
    if (it.id) report.push("it.id = "+it.id);
    if (it.name) report.push("it.name = "+it.name);
    for (var t in it) {
      if (t.indexOf("Left")>=0  ||  t.indexOf("X")>=0  ||  t.toLowerCase().indexOf("parent")>=0) {
        report.push("it."+t+" = "+it[t]);
      }
    }
    report.push("returning: "+X);
    alert(report.join("\n"));
    if (confirm("Walk this?")) hashWalk(it);
  }
  return(X);
}

function swbasics_pos(it,debug) {
  if (debug) alert(it.id+"... before: (" + it.offsetLeft + ":" + it.offsetTop + ")");
  var pos = new Array();
      pos.X=swbasics_left(it,0,debug);
      pos.Y=swbasics_top(it,0,debug);
  if (debug) alert(it.id+"... after: (" + pos.X + ":" + pos.Y + ")");
  return(pos);
}

// -------------------------------------------------------------------------------------------------
//
// Here's the concept:
// 1) While building a table of items, give each item a unique id and build it's TD tag by calling:
//    td = swbasics_makeLiveTD(id,class,contents,callback);
// 2) Once the table has been rendered onto the page, activate all of the new TD's by calling:
//    swbasics_activateLiveTD(ids);
//
// -------------------------------------------------------------------------------------------------
var swbasics_callbacks  = new Array();
var swbasics_doublecbs  = new Array();
var swbasics_mouseovers = new Array();
var swbasics_mouseouts  = new Array();
var swbasics_grabs      = new Array();
var swbasics_grabbed    = new Array();
var swbasics_drags      = new Array();
var swbasics_drops      = new Array();
var swbasics_menus      = new Array();
var swbasics_tooltips   = new Array();

swbasics_Listener = new Object();
swbasics_Listener.click = function(e) {
  var ev = (window.event)? window.event : e;
  var id = (ev.srcElement)? ev.srcElement.id : ev.target.id;
  if (swbasics_callbacks[id]) eval(swbasics_callbacks[id]);
}
swbasics_Listener.dblclick = function(e) {
  var ev = (window.event)? window.event : e;
  var id = (ev.srcElement)? ev.srcElement.id : ev.target.id;
  var cb = swbasics_doublecbs[id];
      cb = cb.replace(/event/,"ev");
  if (swbasics_doublecbs[id]) eval(cb);
}
swbasics_Listener.mouseover = function(e) {
  var ev = (window.event)? window.event : e;
  var id = (ev.srcElement)? ev.srcElement.id : ev.target.id;
  if (swbasics_mouseovers[id]) eval(swbasics_mouseovers[id]);
}
swbasics_Listener.mouseout = function(e) {
  var ev = (window.event)? window.event : e;
  var id = (ev.srcElement)? ev.srcElement.id : ev.target.id;
  if (swbasics_drops[id]  &&  swbasics_grabbed[id]) eval("swbasics_drop('"+id+"');"+swbasics_drops[id]);
  else if (swbasics_mouseouts[id]) eval(swbasics_mouseouts[id]);
}
swbasics_Listener.mousedown = function(e) {
  var ev = (window.event)? window.event : e;
  var id = (ev.srcElement)? ev.srcElement.id : ev.target.id;
  if (swbasics_grabs[id]) eval("swbasics_grab('"+id+"');"+swbasics_grabs[id]);
}
swbasics_Listener.mouseup = function(e) {
  var ev = (window.event)? window.event : e;
  var id = (ev.srcElement)? ev.srcElement.id : ev.target.id;
  if (swbasics_drops[id]) eval("swbasics_drop('"+id+"');"+swbasics_drops[id]);
}
swbasics_Listener.mousemove = function(e) {
  var ev = (window.event)? window.event : e;
  var id = (ev.srcElement)? ev.srcElement.id : ev.target.id;
  if (swbasics_drags[id]  &&  swbasics_grabbed[id]) eval(swbasics_drags[id]);
}
swbasics_Listener.menu = function(e) {
  var ev = (window.event)? window.event : e;
  var id = (ev.srcElement)? ev.srcElement.id : ev.target.id;
  if (swbasics_menus[id]) {
    eval(swbasics_menus[id]);
    if (sw_browser().indexOf("msie") < 0) e.stopPropagation();
    return false;
  }
}

function swbasics_grab(id) {
  swbasics_grabbed[id] = true;
}

function swbasics_drop(id) {
  swbasics_grabbed[id] = false;
}

function swbasics_makeLiveTD(id,parmList) {
  var parms = (typeof parmList == "string")? parmList.split(",") : parmList;

  var contents = "";

  var source   = "";

  var parts = new Array();
  parts.push('id="'+id+'"');

  swbasics_grabbed[id] = false;

  for (var p=0; p<parms.length; p++) {
    if (parms[p].indexOf("=") > 0) {
      var parmNuggets = parms[p].split("=");
      var L = parmNuggets.shift().toLowerCase();
      var R = parmNuggets.join("=");
      switch(L) {
        case  "callback": swbasics_callbacks[id] = R;
                          break;
        case  "doublecb": swbasics_doublecbs[id] = R;
                          break;
        case "mouseover": swbasics_mouseovers[id] = R;
                          break;
        case  "mouseout": swbasics_mouseouts[id] = R;
                          break;
        case      "grab": swbasics_grabs[id] = R;
                          break;
        case      "drag": swbasics_drags[id] = R;
                          break;
        case      "drop": swbasics_drops[id] = R;
                          break;
        case      "menu": swbasics_menus[id] = R;
                          break;
        case  "contents": contents = R;
                          break;
        case    "source": source = R;
                          break;
        case       "tip": swbasics_tooltips[id] = R;
                          break;
                 default: parts.push(parms[p]);
      }
    } else {
      parts.push(parms[p]);
    }
  }

  var res;

  if (source == "") {

    res  = '<td ' + parts.join(" ") + '>' + contents + '</td>';

  } else {

    var pieces = source.split(">");
    res = pieces.shift() + " " + parts.join(" ") + ">" + pieces.join(">");
  }

  return(res);
}


function swbasics_adaptTD(id,parmList) {
  var parms = (typeof parmList == "string")? parmList.split(",") : parmList;

  swbasics_grabbed[id] = false;

  for (var p=0; p<parms.length; p++) {
    if (parms[p].indexOf("=") > 0) {
      var parmNuggets = parms[p].split("=");
      var L = parmNuggets.shift().toLowerCase();
      var R = parmNuggets.join("=");
      switch(L) {
        case  "callback": swbasics_callbacks[id] = R;
                          break;
        case  "doublecb": swbasics_doublecbs[id] = R;
                          break;
        case "mouseover": swbasics_mouseovers[id] = R;
                          break;
        case  "mouseout": swbasics_mouseouts[id] = R;
                          break;
        case      "grab": swbasics_grabs[id] = R;
                          break;
        case      "drag": swbasics_drags[id] = R;
                          break;
        case      "drop": swbasics_drops[id] = R;
                          break;
        case      "menu": swbasics_menus[id] = R;
                          break;
        case       "tip": swbasics_tooltips[id] = R;
                          break;
      }
    }
  }
}



function swbasics_activateLiveTD(idList) {
  var ids = (typeof idList == "string")? idList.split(",") : idList;

  for (var i in ids) {
    var id = ids[i];
    var it = refDiv(id);
    if (it) {
      swcursor_set("hand",id);

      if (swbasics_tooltips[id]) {
        var tipPieces = swbasics_tooltips[id].split(":");
        var tipType   = tipPieces.shift();
        var tipText   = tipPieces.join(":");
        swtips_setTip(id,tipType,tipText);
      }

      if (swbasics_callbacks[id]) {
        if (it.addEventListener) it.addEventListener("click",swbasics_Listener.click,false);
        else                     it.attachEvent("onclick",swbasics_Listener.click);
      } else if (top.swbasics_callbacks[id]) {
        if (it.addEventListener) it.addEventListener("click",top.swbasics_Listener.click,false);
        else                     it.attachEvent("onclick",top.swbasics_Listener.click);
      }

      if (swbasics_doublecbs[id]) {
        if (it.addEventListener) it.addEventListener("dblclick",swbasics_Listener.dblclick,false);
        else                     it.attachEvent("ondblclick",swbasics_Listener.dblclick);
      } else if (top.swbasics_doublecbs[id]) {
        if (it.addEventListener) it.addEventListener("dblclick",top.swbasics_Listener.dblclick,false);
        else                     it.attachEvent("ondblclick",top.swbasics_Listener.dblclick);
      }

      if (swbasics_mouseovers[id]) {
        if (it.addEventListener) it.addEventListener("mouseover",swbasics_Listener.mouseover,false);
        else                     it.attachEvent("onmouseover",swbasics_Listener.mouseover);
      } else if (top.swbasics_mouseovers[id]) {
        if (it.addEventListener) it.addEventListener("mouseover",top.swbasics_Listener.mouseover,false);
        else                     it.attachEvent("onmouseover",top.swbasics_Listener.mouseover);
      }

      if (swbasics_mouseouts[id]) {
        if (it.addEventListener) it.addEventListener("mouseout",swbasics_Listener.mouseout,false);
        else                     it.attachEvent("onmouseout",swbasics_Listener.mouseout);
      } else if (top.swbasics_mouseouts[id]) {
        if (it.addEventListener) it.addEventListener("mouseout",top.swbasics_Listener.mouseout,false);
        else                     it.attachEvent("onmouseout",top.swbasics_Listener.mouseout);
      }

      if (swbasics_grabs[id]) {
        if (it.addEventListener) { it.addEventListener("mousedown",swbasics_Listener.mousedown,false);
                                   it.addEventListener("mouseout",swbasics_Listener.mouseout,false); }
        else                     { it.attachEvent("onmousedown",swbasics_Listener.mousedown);
                                   it.attachEvent("onmouseout",swbasics_Listener.mouseout); }
      } else if (top.swbasics_grabs[id]) {
        if (it.addEventListener) { it.addEventListener("mousedown",top.swbasics_Listener.mousedown,false);
                                   it.addEventListener("mouseout",top.swbasics_Listener.mouseout,false); }
        else                     { it.attachEvent("onmousedown",top.swbasics_Listener.mousedown);
                                   it.attachEvent("onmouseout",top.swbasics_Listener.mouseout); }
      }

      if (swbasics_drags[id]) {
        if (it.addEventListener) it.addEventListener("mousemove",swbasics_Listener.mousemove,false);
        else                     it.attachEvent("onmousemove",swbasics_Listener.mousemove);
      } else if (top.swbasics_grabs[id]) {
        if (it.addEventListener) it.addEventListener("mousemove",top.swbasics_Listener.mousemove,false);
        else                     it.attachEvent("onmousemove",top.swbasics_Listener.mousemove);
      }

      if (swbasics_drops[id]) {
        if (it.addEventListener) it.addEventListener("mouseup",swbasics_Listener.mouseup,false);
        else                     it.attachEvent("onmouseup",swbasics_Listener.mouseup);
      } else if (top.swbasics_grabs[id]) {
        if (it.addEventListener) it.addEventListener("mouseup",top.swbasics_Listener.mouseup,false);
        else                     it.attachEvent("onmouseup",top.swbasics_Listener.mouseup);
      }

      if (swbasics_menus[id]) {
        if (it.addEventListener) it.addEventListener("contextmenu",swbasics_Listener.menu,false);
        else                     it.attachEvent("oncontextmenu",swbasics_Listener.menu);
      } else if (top.swbasics_menus[id]) {
        if (it.addEventListener) it.addEventListener("contextmenu",top.swbasics_Listener.menu,false);
        else                     it.attachEvent("oncontextmenu",top.swbasics_Listener.menu);
      }
//  } else {
//    alert("Failed to find "+id);
    }
  }
}

function swbasics_addEventHandler(id,parmList) {
  var parms = (typeof parmList == "string")? parmList.split(",") : parmList;

  swbasics_grabbed[id] = false;

  for (var p=0; p<parms.length; p++) {
    if (parms[p].indexOf("=") > 0) {
      var parmNuggets = parms[p].split("=");
      var L = parmNuggets.shift().toLowerCase();
      var R = parmNuggets.join("=");
      switch(L) {
        case  "callback": swbasics_callbacks[id] = R;
                          break;
        case  "doublecb": swbasics_doublecbs[id] = R;
                          break;
        case "mouseover": swbasics_mouseovers[id] = R;
                          break;
        case  "mouseout": swbasics_mouseouts[id] = R;
                          break;
        case      "grab": swbasics_grabs[id] = R;
                          break;
        case      "drag": swbasics_drags[id] = R;
                          break;
        case      "drop": swbasics_drops[id] = R;
                          break;
        case      "menu": swbasics_menus[id] = R;
                          break;
        case  "contents": contents = R;
                          break;
        case    "source": source = R;
                          break;
        case       "tip": swbasics_tooltips[id] = R;
                          break;
      }
    }
  }

  swbasics_activateLiveTD(id);
}


function swbasics_seeHandlers(typeList) {
  var types = (typeof typeList == "string")? typeList.split(",") : typeList;

  for (var t in types) {
    var them = null;
    switch(types[t].toLowerCase()) {
      case       "click": alert("click...");
                          them = swbasics_callbacks;
                          break;
      case "doubleclick": alert("doubleclick...");
                          them = swbasics_doublecbs;
                          break;
      case   "mouseover": alert("mouseover...");
                          them = swbasics_mouseovers;
                          break;
      case    "mouseout": alert("mouseout...");
                          them = swbasics_mouseouts;
                          break;
      case        "menu": alert("menu (RMB)...");
                          them = swbasics_menus;
                          break;
    }
    if (them != null) {
      for (var id in them) alert(id+" -> "+them[id]);
    }
  }
}


// ------------------------------------------------------- ERROR HANDLING -------------------

var swbasics_errors = new Array();
    swbasics_errors['admin'  ] = "(unspecified)";
    swbasics_errors['url'    ] = "/non-sm/sw_tools/jsError.html";
    swbasics_errors['style'  ] = "replace";

function swbasics_handleError() {

  if (swbasics_errors['style'].toLowerCase() != "ignore") {
    var pieces = new Array();
   
    pieces.push("admin="+swbasics_errors['admin']);
    pieces.push("style="+swbasics_errors['style']);
    pieces.push("page="+escape(document.location));

    if (window.top  &&  window.top.document.location != document.location) pieces.push("doctop=" + window.top.document.location);

    if (swbasics_errors['email']) pieces.push("email="+swbasics_errors['email']);

    if (window.event) {
      pieces.push("mess="+escape(window.event.errorMessage));
      pieces.push("line="+window.event.errorLine);
      pieces.push("char="+window.event.errorCharacter);
      pieces.push("code="+window.event.errorCode);
    }

    // Ad-hoc provided parameters...
    //
    for (var it in swbasics_errors) {
      if ("admin url style".indexOf(it) < 0) {
        pieces.push(it+"="+swbasics_errors[it]);
      }
    }

    var backTrace = swbasics_funcStack("read");
    if (backTrace != "") {
      pieces.push("backtrace="+backTrace);
    }

    var errorURL = swbasics_errors['url'] + "?" + pieces.join("&");
   
    errorURL = errorURL.replace(/%27/g,"&QUOT;");

    if (swbasics_errors['style'].toLowerCase() == "new") {
      var erWin = window.open(errorURL,"JSError","scrollbars=yes,dependent=yes,height=600,width=800");
      erWin.focus();
    } else {
      if (window.top) window.top.location.replace(errorURL);
      else            window.location.replace(errorURL);
    }
  }
  return true;
}

function swbasics_catchErrors(optionList) {
  var options = (typeof optionList == "string")? optionList.split(",") : optionList;

  for (var o in options) {
    var optionPieces = options[o].split("=");
    var L = optionPieces.shift().toLowerCase();
    var R = optionPieces.join("=");

    if (L == "environment") {
      R = R.toLowerCase();
      if (R != "production") swbasics_errors['url'] = "/non-sm/sw_tools/" + R + "/jsError.html";
    }

    swbasics_errors[L] = R;
  }

  window.onerror = swbasics_handleError;
}

var swbasics_fs = new Array();

function swbasics_funcStack(action,fsParms) {

  var res = null;

  switch (action.toLowerCase()) {

    case  "pop": var mode = 0;
                 if (undefined != fsParms) {
                   if (typeof fsParms != "object") {
                     res = fsParms;
                   } else {
                     res = fsParms[0];
                     mode = parseInt(fsParms[1]);
                   }
                 }
                 if (swbasics_fs.length == 0) {
                   if (mode==1) alert('swbasics_funcStack("pop") - stack under-run!');
                 } else {
                   swbasics_fs.length = swbasics_fs.length - 1;
                 }
                 break;
    case "push": swbasics_funcStackPush(fsParms);
                 break;
    case "read": res = (swbasics_fs.length == 0)? "" : swbasics_fs.join("||");
                 break;
  }

  if (res == null) return;

  return(res);
}

function swbasics_funcStackPush(fsParms) {			// INTERNAL FUNCTION

  var funcName  = "unknown";
  var funcParms = new Array();

  if (typeof fsParms != "object") {
    funcName = fsParms;
  } else {
    funcName = fsParms[0];
    for (var p=1; p<fsParms.length; p++) {
      if (typeof fsParms[p] == "string") {
        funcParms.push('"' + fsParms[p] + '"');
      } else if (typeof fsParms[p] != "object") {
        funcParms.push(fsParms[p]);
      } else {
        var subParms = new Array();
        for (var pp=0; pp<fsParms[p].length; pp++) {
          var sp = fsParms[p][pp];
          if (typeof sp == "string") subParms.push('"' + sp + '"');
          else if (typeof sp == "object") subParms.push("[object]");
          else                            subParms.push(sp);
        }
        funcParms.push("[" + subParms.join(",") + "]");
      }
    }
  }

  swbasics_fs.push(funcName + '(' + funcParms.join(",") + ')');
}

function swbasics_isNum(argvalue) {

  var digit = "";

  argvalue = argvalue.toString();

  if (argvalue.length == 0) return false;

  for (var d=0; d<argvalue.length; d++) {
    digit = argvalue.substr(d,1);
    if (digit < "0" || digit > "9") return false;
  }

  return true;
}

function swbasics_formatDate(then,format) {

  var day   = then.getDate();
  var month = then.getMonth() + 1;
  var year  = then.getFullYear();
  var res   = "";

  res = (format)? swbasics_applyDateFormat(format,month,day,year)
                : [swbasics_lpad(month,'0',2),swbasics_lpad(day,'0',2),year].join("/");

  return(res);
}

function swbasics_pad(it,pChar,len) {

  if (typeof it != "string") it = ""+it;
  while (it.length < len) it = it + pChar;

  return(it);
}

function swbasics_lpad(it,pChar,len) {

  if (typeof it != "string") it = ""+it;
  while (it.length < len) it = pChar + it;

  return(it);
}

function swbasics_applyDateFormat(format,m,d,y) {

  var months = "January,February,March,April,May,June,July,August,September,October,November,December".split(",");
  var parts;
  var res = new Array();
  var schar;

       if (format.indexOf("/") > 0) schar = '/';
  else if (format.indexOf("-") > 0) schar = '-';
  else                              schar = ' ';

  parts = format.split(schar);

  for (var p=0; p<parts.length; p++) {

    parts[p] = parts[p].replace(/\ /g,"");

    switch(parts[p].toLowerCase()) {

      case    'mm': res.push(swbasics_lpad(m,'0',2));
                    break;

      case    'dd': dd = swbasics_lpad(d,'0',2);
                    if (schar == ' ') dd += ',';
                    res.push(dd);
                    break;

      case    'yy': y = ""+y;
                    res.push(y.substr(y.length-2));
                    break;

      case  'yyyy': res.push(""+y);
                    break;

      case 'month':
      case   'mon': m = parseInt(m);
                    if (m < 1 ) m = 1;
                    if (m > 12) m = 12;
                    var mon = months[m-1];
                    if (parts[p].toLowerCase() == 'mon') mon = mon.substr(0,3) + '.';
                    res.push(mon);
                    break;
    }
  }

  return(res.join(schar));
}

function  trim(stringToTrim) { return stringToTrim.replace(/^\s+|\s+$/g,""); }
function ltrim(stringToTrim) { return stringToTrim.replace(/^\s+/,"");       }
function rtrim(stringToTrim) { return stringToTrim.replace(/\s+$/,"");       }

function sprintf(fmt,arguments) {

  if (typeof arguments != "object") {	// Ensure that the 'arguments' parameter
    var temp = arguments;		// is, in fact, an array - even if it
    arguments = new Array();		// has only one value.
    arguments.push(temp);
  }

  var pad =          function(str,ch,len) {
                       var ps='';
                       for (var i=0; i<Math.abs(len); i++) ps+=ch;
                       return (len>0)? str+ps : ps+str;
                     }				// end of function

  var processFlags = function(flags,width,rs,arg) {
                       var pn = function(flags,arg,rs) {
                                  if (arg>=0) {
                                         if (flags.indexOf(' ')>=0) rs = ' ' + rs;
                                    else if (flags.indexOf('+')>=0) rs = '+' + rs;
                                  } else {
                                         if (flags.indexOf('(')>=0) rs = '(' + rs + ')';
                                       else rs = '-' + rs;
                                  }
                                  if (flags.indexOf('$')>=0) rs = makeDollars(rs);
                                  return rs;
                                }	// end of sub-function
                       var iWidth = parseInt(width,10);
                       if (width.charAt(0) == '0') {
                         var ec=0;
                         if (flags.indexOf(' ')>=0 || flags.indexOf('+')>=0) ec++;
                         if (rs.length<(iWidth-ec)) rs = pad(rs,'0',rs.length-(iWidth-ec));
                         return pn(flags,arg,rs);
                       }
                       rs = pn(flags,arg,rs);
                       if (rs.length<iWidth) {
                         if (flags.indexOf('-')<0) rs = pad(rs,' ',rs.length-iWidth);
                         else                      rs = pad(rs,' ',iWidth - rs.length);
                       }
                       return rs;
                     }			// end of function

  var applyComas   = function(it,money) {
                       var neg   = "";
                       var paren = false;

                       if (it.indexOf('-') == 0) {
                         neg = "-";
                         it = it.replace(/\-/,"");
                       }

                       if (it.indexOf('(') == 0) {
                         paren = true;
                         it = it.replace(/\(/,"").replace(/\)/,"");
                       }

                       var L = ("" + it).split('.')[0].replace(/\,/g,"").replace(/\ /g,"");
                       var R = ("" + it).split('.')[1];
                       if (money) {
                         R = (undefined == R)? ".00" : "." + (R+"00").substr(0,2);
                       } else {
                         R = (undefined == R)? "" : "." + R;
                       } 
                       var M = new Array();
                       while (L.length >3) {
                         M.unshift(L.substr(L.length - 3));
                         L = L.substr(0,L.length-3);
                       }
                       M.unshift(L);
                       it = M.join(",") + R;
                       if (money) it = "$" + neg + it;
                       if (paren) it = '(' + it + ')';

                       return(it);
                     }			// end of function

  var makeDollars  = function(it) {
                       return(applyComas(it,true));
                     }


  var converters = new Array();
  converters['c'] =  function(flags,width,precision,arg) {
                       if (typeof(arg) == 'number') return String.fromCharCode(arg);
                       if (typeof(arg) == 'string') return arg.charAt(0);
                       return '';
                     }			// end of function

  converters['d'] =  function(flags,width,precision,arg) {
                       return converters['i'](flags,width,precision,arg); 
                     }			// end of function

  converters['u'] =  function(flags,width,precision,arg) {
                       return converters['i'](flags,width,precision,Math.abs(arg)); 
                     }			// end of function

  converters['i'] =  function(flags,width,precision,arg) {
                       var cflag = (width.indexOf(",") >= 0)? true : false;
                       width = width.replace(/\,/g,"");
                       var iPrecision = parseInt(precision);
                       var rs         = ((Math.abs(arg)).toString().split('.'))[0];
                       if (rs.length<iPrecision) rs=pad(rs,' ',iPrecision - rs.length);
                       rs = processFlags(flags,width,rs,arg);
                       if (cflag == true) rs = applyComas(rs);
                       return(rs);
                     }			// end of function

  converters['E'] =  function(flags,width,precision,arg) {
                       return (converters['e'](flags,width,precision,arg)).toUpperCase();
                     }			// end of function

  converters['e'] =  function(flags,width,precision,arg) {
                       var cflag = (width.indexOf(",") >= 0)? true : false;
                       width = width.replace(/\,/g,"");
                       iPrecision = parseInt(precision);
                       if (isNaN(iPrecision)) iPrecision = 6;
                       rs = (Math.abs(arg)).toExponential(iPrecision);
                       if (rs.indexOf('.')<0 && flags.indexOf('#')>=0) rs = rs.replace(/^(.*)(e.*)$/,'$1.$2');
                       rs = processFlags(flags,width,rs,arg);
                       if (cflag == true) rs = applyComas(rs);
                       return(rs);
                     }			// end of function

  converters['f'] =  function(flags,width,precision,arg) {
                       var cflag = (width.indexOf(",") >= 0)? true : false;
                       width = width.replace(/\,/g,"");
                       iPrecision = parseInt(precision);
                       if (isNaN(iPrecision)) iPrecision = 6;
                       rs = (Math.abs(arg)).toFixed(iPrecision);
                       if (rs.indexOf('.')<0 && flags.indexOf('#')>=0) rs = rs + '.';
                       rs = processFlags(flags,width,rs,arg);
                       if (cflag == true) rs = applyComas(rs);
                       return(rs);
                     }			// end of function

  converters['G'] =  function(flags,width,precision,arg) {
                       return (converters['g'](flags,width,precision,arg)).toUpperCase();
                     }			// end of function

  converters['g'] =  function(flags,width,precision,arg) {
                       var cflag = (width.indexOf(",") >= 0)? true : false;
                       width = width.replace(/\,/g,"");
                       iPrecision = parseInt(precision);
                       absArg = Math.abs(arg);
                       rse = absArg.toExponential();
                       rsf = absArg.toFixed(6);
                       if (!isNaN(iPrecision)) {
                         rsep = absArg.toExponential(iPrecision);
                         rse = rsep.length < rse.length ? rsep : rse;
                         rsfp = absArg.toFixed(iPrecision);
                         rsf = rsfp.length < rsf.length ? rsfp : rsf;
                       }
                       if (rse.indexOf('.')<0 && flags.indexOf('#')>=0) rse = rse.replace(/^(.*)(e.*)$/,'$1.$2');
                       if (rsf.indexOf('.')<0 && flags.indexOf('#')>=0) rsf = rsf + '.';
                       rs = rse.length<rsf.length ? rse : rsf;
                       rs = processFlags(flags,width,rs,arg);
                       if (cflag == true) rs = applyComas(rs);
                       return(rs);
                     }			// end of function

  converters['o'] =  function(flags,width,precision,arg) {
                       var iPrecision = parseInt(precision);
                       var rs = Math.round(Math.abs(arg)).toString(8);
                       if (rs.length<iPrecision) rs=pad(rs,' ',iPrecision - rs.length);
                       if (flags.indexOf('#')>=0) rs='0'+rs;
                       return processFlags(flags,width,rs,arg); 
                     }			// end of function

  converters['X'] =  function(flags,width,precision,arg) {
                       return (converters['x'](flags,width,precision,arg)).toUpperCase();
                     }			// end of function

  converters['x'] =  function(flags,width,precision,arg) {
                       var iPrecision = parseInt(precision);
                       arg = Math.abs(arg);
                       var rs = Math.round(arg).toString(16);
                       if (rs.length<iPrecision) rs=pad(rs,' ',iPrecision - rs.length);
                       if (flags.indexOf('#')>=0) rs='0x'+rs;
                       return processFlags(flags,width,rs,arg); 
                     }			// end of function

  converters['p'] =  function(flags,width,precision,arg) {
                       var rs = converters['s'](flags,width,precision,arg);
                       rs = swbasics_properCase(rs);
                       return(rs);
                     }			// end of function

  converters['S'] =  function(flags,width,precision,arg) {
                       return (converters['s'](flags,width,precision,arg)).toUpperCase();
                     }			// end of function

  converters['s'] =  function(flags,width,precision,arg) {
                       var iPrecision = parseInt(precision);
                       var rs = arg; 
                       if (rs.length > iPrecision) rs = rs.substring(0,iPrecision);
                       return processFlags(flags,width,rs,0);
                     }			// end of function

  farr = fmt.split('%');
  retstr = "";

  fpRE = /^([-+ #\$\(]*)(\d*\,?\d*)\.?(\d*)([cdieEfFgGopSsuxX])(.*)$/;

  for(var i=0; i<farr.length; i++) {
    fps = fpRE.exec(farr[i]);
    if (!fps) {
      retstr += farr[i];
      arguments.unshift(" ");
      continue;
    }
    if (arguments[i]!=null) retstr+=converters[fps[4]](fps[1],fps[2],fps[3],arguments[i]);
    retstr += fps[5];
  }
  return retstr;
}

// ======================================================= Dynamic loading...

var swbasicsAppendedFiles = new Array();

// Append to the HEAD element a new script (js, php, etc.) as a javascript.
//
function appendScript(filename) {

  var testName = filename.replace(/\//g,"_").replace(/\./g,"_");
  if (undefined == swbasicsAppendedFile[testName]) {

    var fileref = document.createElement('script');

    fileref.setAttribute("type","text/javascript");
    fileref.setAttribute("src", filename         );

    if (typeof fileref != "undefined") {
      document.getElementsByTagName("head")[0].appendChild(fileref);
      swbasicsAppendedFiles[testName] = 1;
    }
  }
}

// Append to the HEAD element a new css file.
//
function appendCSS(filename) {

  var testName = filename.replace(/\//g,"_").replace(/\./g,"_");
  if (undefined == swbasicsAppendedFile[testName]) {

    var fileref = document.createElement('link');

    fileref.setAttribute("rel", "stylesheet");
    fileref.setAttribute("type","text/css"  );
    fileref.setAttribute("href",filename    );

    if (typeof fileref != "undefined") {
      document.getElementsByTagName("head")[0].appendChild(fileref);
      swbasicsAppendedFiles[testName] = 1;
    }
  }
}

function swbasics_dimensions(it) {

  // By default, return the size of the document area.
  //
  var bods = document.getElementsByTagName("body");
  var item = bods[bods.length-1];
  var dimensions = new Array();
      dimensions['item'] = "body";
      dimensions['wide'] = item.clientWidth;
      dimensions['tall'] = item.clientHeight;

  // If an element was named -and it exists- then return it's dimensions instead.
  //
  if (it) {

    item = refDiv(it);
    if (item) {
      dimensions['item'] = it;
      dimensions['wide'] = item.clientWidth;
      dimensions['tall'] = item.clientHeight;
    }
  }

  return(dimensions);
}
