// strings.js - a library of Javascript string functions.
//
// by Thomas Wm. Madron (2001)
// This library globally and the individual contents and functions,
// if not otherwise copyrighted, is:
//
// Copyright (c) 2001 by Enterprise-Wide Computing, Inc.
// All rights reserved.
//
// Purpose:
// To make string handling in Javascript a bit easier.
// The functions in this library provide BASIC-like syntax and
// string handling capability.
//
// Overview:
// String handling is important to much of the use of Javascript.
// Javascript itself has a number of functions that allow the mani-
// pulation of strings.  When compared to some other high level
// programming languages, however, Javascript lacks some important
// string handling capabilities.  Also, because Javascript was
// modeled somewhat after the syntax of C, the naming and use of
// string and other functions is not always intuitively obvious.
//
// This library of string-related functions provides wrappers for
// some of the existing Javascript functions and extends the number
// of functions available that are not part of Javascript.  The
// syntax of the functions follow, as closely as possible, the naming
// and syntax of functions of the same names in modern versions of
// structured BASIC.  Some additional string handling functions not
// a part of either Javascript or BASIC were then added.
//
// There is one important difference between these functions and the
// functions of the same name in BASIC brought about by differences
// between Javascript and BASIC.  In BASIC, when referring to the
// location of specific characters within a string, numbering of the
// relative locations starts with one ("1").  In Javascript numbering
// starts with zero ("0").  The Javascript convention has been retained
// in this library of functions.  As a side note, several of the
// functions require input of the length of a string or substring.
// "Length" in this case is the absolute number of bytes to which
// the parameter refers (the length of "abcd"=4, for example).
//
// Another important difference between the use of function in
// most BASICs and Javascript is that in BASIC the use of function
// names is case insensitive (len=Len=LEN) whereas in Javascript
// the use of function names is case sensitive (len<>Len<>LEN).
//
// The following table lists the BASIC and extended functions and,
// where possible, their Javascript equivalents.  Some of the
// functions require other functions in this library and those
// dependencies are listed.
//
//	Functions found in most modern BASICs:
//
//	BASIC						Javascript				Dependencies
//
//	len(string)					string.length			NA
//	instr(string, srchstr)		string.indexOf(srchstr)	NA
//	left(string, len)			string.substring(s, e)	NA
//	right(string, len)			string.substring(s, e)	NA
//	mid(string, loc, len)		string.substring(s, e)	NA
//	asc(char)					char.charCodeAt(0)		NA
//	chr(asc)					String.fromCharCode(asc)NA
//	ucase(string)				string.toUpperCase()	NA
//	lcase(string)				string.toLowerCase()	NA
//	str(number[,base])			number.toString([base])	NA
//	val(string)					parseFloat(string)		NA
//	ltrim(string,tstchar)		NA						len(), mid()
//	rtrim(string,tstchar)		NA						len(), mid(), left()
//	trim(string,tstchar)		NA						ltrim(), rtrim()
//
//	Extended BASIC-syntax functions:
//
//	strtok(Params,Delim,Record)	Record.split(Delim)		NA
//  parseoptval( record, delim )NA						NA
//	Array2Record(Params,Delim)	Params.join(Delim)		NA
//	padleft(string,padchr,len)	NA						left(), len()
//	padright(string,padchr,len)	NA						left(), len()
//  revinstr(string, srchstr)	string.lastIndexOf(s)	NA
//  function rand( low, high )	NA						NA
//  function xint( x )			Math.round()			NA
//
parse = new Array(1); //3 elements: 0, 1; used by parseoptval

function len ( string )
{
	// returns the number of characters in strvar
	var tmp = string.length;
	return tmp;
}

function instr ( strvar, srchstr )
{
	// returns the location (index ) of the first character of srchstr
	// in strvar.  Search is from left to right, indexing is relative
	// to zero.  Returns -1 if srchstr not found.
	var tmp = strvar.indexOf( srchstr );
	return tmp;
}

function revinstr ( strvar, srchstr )
{
	// returns the location (index ) of the first character of srchstr
	// in strvar.  Search is from right to left, indexing is relative
	// to zero.
	var tmp = strvar.lastIndexOf( srchstr );
	return tmp;
}

function strtok ( Params, Delim, Record )
{
	// An array may be passed as an argument.  Params is
	// initialized as an array when Record is split.
	// The function is returned with the length of the array.
	// Params[] is returned containing all the tokens in Record.
	// This function, and its name, was inspired by the widely use
	// C function of the same name.
	var xp = Record.split( Delim );
	var ArrLen = xp.length;
	var y = ArrLen - 1
	for (var i=0; i <= y; i++)
	{
		Params[i] = xp[i];
	}
	return ArrLen;
}

function Array2Record ( Params, Delim )
{
	// If Delim is null then join() defaults to comma delimited.
	// Array2Record joins the elements of an array together into
	// a delimited string (Record).
	var tmp = Params.join( Delim );
	return tmp;
}

function left ( tstring, tlen )
{
	// returns leftmost tlen characters from tstring.
	// Note that with strings, as with arrays, index elements start at
	// zero rather than one.
	var sloc = 0;	
	var eloc = (sloc + tlen)
	var tmp = tstring.substring(sloc, eloc);
	return tmp;
}

function right ( tstring, tlen )
{
	// returns rightmost tlen characters from tstring.
	var eloc = len( tstring );
	var sloc = eloc - tlen;
	var tmp = tstring.substring( sloc, eloc );
	return tmp;
}

function mid ( tstring, location, tlen )
{
	// if tlen is omitted, then the remainder of the string is acquired.
	if ((tlen==0) || (tlen==null))
	{
		var loc2 = len( tstring );
		var tmp = tstring.substring( location, loc2 );
	}
	else
	{
		var loc2 = location + tlen;
		var tmp = tstring.substring( location, loc2 );
	}
	return tmp;
}

function asc ( string )
{
	var tmp = string.charCodeAt(0);
	return tmp;
}

function chr ( charcode )
{
	var tmp = String.fromCharCode( charcode );
	return tmp;
}

function ucase ( tstring )
{
	var tmp = tstring.toUpperCase();
	return tmp;
}

function lcase ( tstring )
{
	var tmp = tstring.toLowerCase();
	return tmp;
}

function str ( number, base )
{
	// Convert number in base to string
	if ((base==null) || (base==0))
	{
		base = 10;
	}
	var tmp = number.toString( base );
	return tmp;
}

function val ( numericstring )
{
	var tmp = parseFloat( numericstring );
	return tmp;
}

function padleft ( tstring, padchar, tlen )
{
	var lentstring = len( tstring );
	if (lentstring > tlen)
	{
		var tmp = left( tstring, tlen );
	}
	else if (lentstring == tlen)
	{
		var tmp = tstring;
	}
		else
	{
		var lenpadchar = len( padchar );
		if (lenpadchar > 1)
		{
			var padchar = left( padchar, 1 );
			var lenpadchar = 1;
		}
		var nchars = tlen - lentstring;
		var p = "";
		for (var i=1; i<=nchars; i++)
		{
			p = p + padchar;
		}
		var tmp = p + tstring;
	}
	return tmp;
}

function padright ( tstring, padchar, tlen )
{
	var lentstring = len( tstring );
	if (lentstring > tlen)
	{
		var tmp = left( tstring, tlen );
	}
	else if (lentstring == tlen)
	{
		var tmp = tstring;
	}
	else
	{
		var lenpadchar = len( padchar );
		if (lenpadchar > 1)
		{
			var padchar = left( padchar, 1 );
			var lenpadchar = 1;
		}
		var nchars = tlen - lentstring;
		var p = "";
		for (i=1; i<=nchars; i++)
		{
			p = p + padchar;
		}
		var tmp = tstring + p;
	}
	return tmp;
}

function ltrim( tstring, tstchar )
{
	var tmp1 = tstring; //may be modified
	var tmp2 = tstring;
	var j = 1;
	var l = len( tmp2 ) - 1; //is a constant
	var k = len( tmp2 ); //is modified by loop
	for ( var i = 0; i <= l; i++ )
	{
		var s = mid( tmp2, i, 1 );
		// if (y < 0)
		// if (s == tstchar)
		// {
			// var y = instr( tstchar, s );
			// var x = mid( tstchar, y, 1 );
			if ( s == tstchar )
			{
				var tmp1 = mid( tmp1, j, k );
				var k = len( tmp1 );
			}
			else
			{
				var tmp2 = tmp1;
				break
			}
		// }
		// else
		// {
		//	break;
		// }
	}
	return tmp2;
}

function rtrim( tstring, tstchar )
{
	var tmp1 = tstring;
	var tmp2 = tstring;
	var l = len( tmp2 ) - 1; //is a constant
	var k = len( tmp1 ) - 1; //is modified by loop
	for ( var i = l; i >= 0; i-- )
	{
		var s = mid( tmp2, i, 1 );
		// if (y < 0)
		// if (s==tstchar)
		// {
			// var y = instr( tstchar, s );
			// var x = mid( tstchar, y, 1 );
			if ( s == tstchar )
			{
				var tmp1 = left( tmp1, k );
				var k = k - 1;
			}
			else
			{
				var tmp2 = tmp1;
				break;
			}
		// }
		// else
		// {
		//	break;
		// }
	}
	return tmp2;
}

function trim( tstring, tstchar )
{
	var tmp = ltrim( rtrim( tstring, tstchar ), tstchar );
	return tmp;
}

function parseoptval( record, delim )
{
//
//  The results of this function are passed in the array parse[].
//  parse[] is defined as a global array.  When xopt and value are
//  determined, parse[0] = xopt and parse[1] = value.
//
    var xopt = "local";
    var value = "local";
    var lendelim = len( delim );
    var tmp = record;
    var ii = instr( tmp, delim );
    if (ii > 0) // delimiter is embedded in the string
    {
        var tmp1 = left( tmp, ii );
        var xopt = trim( tmp1, " " );
        var tmp1 = mid( tmp, ii + lendelim );
        var value = trim( tmp1, " " );
    }
    else if (ii < 0) // delimiter does not exist
    {
        var xopt = trim( tmp, " " );
        var value = "";
    }
    else if (ii==0) // delimiter is the first character of the string
    {
        var xopt = "";
        var value = mid( tmp, ii+1 );
    }
    parse[0] = xopt;
    parse[1] = value;
}

function xint( x )
//
// xint - Rounds a real number up to the nearest integer value.
//
{
    var tmp = Math.round( x );
    return tmp;
}

function xfix( value )
//
// xfix - rounds a number down to the nearest integer value that is
//   less than or equal to value.
//
{
    tmp = Math.floor( value );
    return tmp;
}

function trunc( value )
//
// trunc - truncates a real number to its integer value.
//
{
    var tmp = str( value );
    var ii = instr( tmp, "." )
    if (ii >= 0)
    {
        var x = left( tmp, ii );
        var tmp = x;
    }
    v = val( tmp );
    return v;
}

function rand( low, high )
//
// rand generates uniformly distributed random integers between
//   low and high, inclusive.
//
{
    var r = Math.random();
	var l = low - 1;
	var h = high + 1;
	var v = ( h - l ) * r + l;
    var tmp = xint( v );
	if ((tmp < low)||(tmp > high))
	{
		while ((tmp < low)||(tmp > high))
		{
			var r = Math.random();
			var v = ( h - l ) * r + l;
			var tmp = xint( v );
		}
	}
    return tmp;
}

function format( n, template )
//
// format - formats a number, n, to a specific template (i.e., ####.##).
//
// Notes:
// 1.  Rounds up to the specified number of decimal places.
// 2.  Returns n as a string.
//
{
    var num = str( n );
    var dec = 0;
    var ii = instr( template, "." );
    if (ii >= 0)
    {
        parseoptval( template, "." );
        var l = parse[0];
        var r = parse[1];
        var dec = len( r ); // Number of decimals
    }
    if (dec > 0)
    {
        // Parse number with decimal point:
        parseoptval( num, "." );
        var lside = parse[0]; // Left side of decimal point.
        var rside = parse[1]; // Right side of decimal point.
		// If number of decimal places greater than 0, then a value
		// for rside is required.
        if ((rside=="")||(rside==null))
        {
            var rside = "0";
        }
        var lenrside = len( rside );
        if (lenrside < dec)
        {
            pch = "0";
            var r = padright( rside, pch, dec );
            var num = lside + "." + r;
        }
        else if (lenrside > dec)
        {
			// Construct a rounding decimal fraction:
            var d2 = dec + 1;
            var ax = "5";
            var a = "." + padleft( ax, "0", d2 );
            var b = val( a );
			// Construct the fractional part of n:
            var c = val( "."+rside );
			// Round c (fraction part of n) by adding b (rounding fraction):
            var d = b + c;
			// Truncate the rounded fraction to the required decimal
			// length:
            var e = mid( str( d ), 1 ); // Eliminate leading zero (0)
			var pdec = dec + 1; // Number of decimal places plus decimal point.
            var rside = left( e, pdec ); // Truncate
			// Reconstitute n as a string with a decimal fraction of
			// length dec:
            var num = lside + rside;
        }
    }
    return num;
}

