HR$BINSTRING$VERSION = "2005-01-07"
//* ====================================================================================================================
//* hr$binstring.js -- Copyright (c) 2004,2005 Henk Reints (http://henk-reints.nl)
//* ------------------------------------------------------------------------------
//#
//# Description:
//# ------------
//#	It is possible to read (parts of) binary files into JavaScript String Objects and process the binary content.
//#	However, the 'charCodeAt' and 'fromCharCode' methods of the String Object do not always return a single byte
//#	value, but for some values (in the range 128..159) these standard methods do an implicit conversion to/from
//#	Unicode values greater than 255.
//#	This script extends the String Object with properties and methods that compensate for that behaviour, thus
//#	enabling the processing of binary data stored in a String Object, and it also adds some useful String methods
//#	for performing such operations.
//#	Also provided are some useful methods for encoding/decoding to/from various internet formats, such as
//#	Hexadecimal, Base64, and Quoted Printable, as well as MD5 and SHA computation and ARCFOUR encryption/decryption.
//#
//# --------------------------------------------------------------------------------------------------------------------
//# References:
//#	- RFC2045: Multipurpose Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies
//#		http://www.rfc-editor.org/rfc/rfc2045.txt
//#		(contains definitions of Base64 and Quoted Printable data representation).
//#
//#	- RFC1321: The MD5 Message-Digest Algorithm
//#		http://www.rfc-editor.org/rfc/rfc1321.txt
//#
//#	- FIPS PUB 180-2: Secure Hash Standard (SHS)
//#		http://csrc.nist.gov/publications/fips/fips180-2/fips180-2withchangenotice.pdf
//#
//#	- RFC3174: US Secure Hash Algorithm 1 (SHA1)
//#		http://www.rfc-editor.org/rfc/rfc3174.txt
//#
//#	- RFC2104: HMAC: Keyed-Hashing for Message Authentication
//#		http://www.rfc-editor.org/rfc/rfc2104.txt
//#
//#	- ARCFOUR: A Stream Cipher Encryption Algorithm "Arcfour"
//#		http://www.mozilla.org/projects/security/pki/nss/draft-kaukonen-cipher-arcfour-03.txt
//#
//# --------------------------------------------------------------------------------------------------------------------
//# Notes:
//#	- In most cases, the String Objects manipulated by the methods provided here will contain BINARY data,
//#	  not to be interpreted as text. If you do not understand what this is about, then you probably won't
//#	  need this package...
//#
//#	- Interpreting Strings as Byte arrays should be familiar to you if you used to program in FORTRAN or
//#	  ASSEMBLER or so, where it was in fact the other way around. In FORTRAN you would use a BYTE array to
//#	  store strings. And when I say BYTE then of course I mean LOGICAL*1, but I prefer BYTE since I have a
//#	  |D|I|G|I|T|A|L| background, programming PDPs and VAXes (lucky me).
//#
//#	- For file i/o the FileSystemObject is needed and the i/o should of course be done in Ascii mode,
//#	  not in Unicode (as far as I can find out the WScript StdIn/StdOut/StdErr streams are in Ascii mode).
//#
//#	- The FileSystemObject supports only sequential i/o, so you are still a little bit restricted,
//#	  and you should NOT use ReadLine or WriteLine methods for binary i/o.
//#	  NOTE: the built-in ReadAll method of TextStream Objects does NOT always return a correct result when it must
//#	  be interpreted as binary data! (it probably does something with bytes of which it THINKS they are newlines).
//#	  it is better (and much easier) to use the methods provided by THIS library to read or write entire files.
//#	  For reading large files you should write something to process it in blocks, using the stream.Read(blocksize)
//#	  method.
//#
//#	- This package is for handling binary data read from a file as a string of bytes, not Strings containing text.
//#	  The 'byteAt' method will return -1 if special characters are encountered that cannot be converted to a byte
//#	  value, leading to incorrect and meaningless results.
//#	  For handling String Objects containing TEXT with other Unicode Characters than those few that can be converted
//#	  to byte values you should use the to7bit/from7bit or the toURLtext/fromURLtext methods provided in this package.
//#
//#	- This package contains some methods for Base64 and Quoted-Printable encode/decoding.
//#	  Both are designed for transmission of data containing special (non-ascii) characters via a communication
//#	  channel that cannot handle or does not allow such characters. This is achieved by encoding them using
//#	  normal ascii-characters.
//#	  Quoted Printable is intended for encoding PLAIN TEXT containing SOME special characters, and Base64 is
//#	  to be used for encoding raw BINARY data (which can be interpreted as LOTS of special characters!).
//#	  Since, ultimately, text is also stored as binary data, both methods CAN be used for either sort of data.
//#	  Best practice is however to use Quoted Printable for PLAIN TEXT and Base64 for anything else.
//#	  Quoted Printable has a special way of handling line terminators (which is necessary because not all platforms
//#	  use the same way of internally storing a 'new line') so encoding+decoding MAY give output not exactly
//#	  equal to the original input. For plain text that would be no real problem, s
//#	  ince you can eas
//#	  ily con
//#	  tinue reading and understand a text even if there would be some extra line te
//#	  rminators or so, but binary data would become corrupted.
//#
//#	- In JavaScript, the standard Objects have a CONSTRUCTOR and they can have INSTANCES.
//#	  The Constructor is merely the definition of the object type and it may contain some initialisation code
//#	  for new Object Instances when they are created.
//#	  'String' is the Constructor for the String Object and the statement x="abc" creates an Instance of the
//#	  String Object, identified by its name 'x' and holding the value "abc".
//#	  Methods added to the Constructor of the String Object should be used as normal functions and they are not
//#	  available as methods to individual Object Instances. Their input can be via function arguments only and
//#	  they usually return a String Object Instance as their result.
//#	  Methods added to the 'prototype' property of the String Object become available to each individual
//#	  variable that is a String Object Instance and they can (and usually will) operate on the actual value of
//#	  that specific Instance and return a value of arbitrary type which is not necessarily a String.
//#	  So methods and properties of the Constructor are general purpose functions not being part of each individual
//#	  Object Instance and methods and properties of the Prototype become part of each individual Object Instance.
//#
//#	  EXAMPLES (given that 'fromCharCode' is a Constructor method and 'charCodeAt' is a Prototype method):
//#
//#		var x = String.fromCharCode(13,10)	creates a String Object Instance named 'x'
//#							holding the good old CRLF combination as its value.
//#
//#		var a = "Henk"			creates a String Object Instance named 'a' with value "Henk"
//#		var n = a.charCodeAt(1)		creates a Number Object Instance named 'n' with value 101 (the "e")
//#		var b = a.fromCharCode(65)	this will produce an ERROR message and the script will halt
//#						('fromCharCode' belongs to the Constructor, not to the Instance)
//#
//#
//#	- In binary arithmetic, numerical byte values are usually stored with the lowest order bit at the lowest bit
//#	  address and the highest order bit at the highest bit address (little-endian bit notation). The highest order
//#	  bit is called leftmost and the lowest order bit is called rightmost, like in normal numerical notation.
//#	  The bitwise left-shift and right-shift operations refer to these directions.
//#
//#	- When talking about addresses, the only meaningful indication of a direction is 'low-to-high' or 'high-to-low',
//#	  referring to the numerical values of the addresses. Since memory is not topological, terms like 'left-to-right'
//#	  or so are meaningless. When drawing a table on paper to visualise a  portion of memory, you can do it in any
//#	  direction you like, be it from left to right, from right to left, from top to bottom, or from bottom to top.
//#	  In different books you can find all variants.
//#
//#	- About big-endian & little-endian words
//#	  Since a byte can store (unsigned) numbers from 0 to 255, more bytes are needed to store larger numbers.
//#	  For example: for storing the number: 23433 = (91 * 256) + 137, two bytes are needed, where 91 is the high
//#	  order value and 137 the low order. But now we have a small problem: should they be stored
//#	  as 91,137 (i.e. the high order byte at the low address and the low order byte at the high address) or
//#	  as 137,91 (i.e. the low order byte at the low address and the high order byte at the high address) ?
//#	  In fact, both sequences are actually used on various computer systems. The sequence where the low byte
//#	  is at the low address is called "little-endian", and the other one is called "big-endian".
//#	  Most modern computer platforms use the little-endian convention, but big-endian is also frequently used
//#	  (especially on IBM mainframes). In THIS script, whenever I say "word", I mean the little-endian notation,
//#	  unless explicitly stated otherwise (the Secure Hash Algorithm [RFC3174] [FIPS PUB 180-2] uses big-endian).
//#
//#	- You should take good notice of the difference between Internet order and Numerical order when representing
//#	  binary data as a textual string of digits. We usually write numbers with the highest order digit at the left
//#	  side and the lowest order digit at the right. In little-endian binary notation that's not different. Since
//#	  computers are actually designed to crunch numbers, it is convenient to use "numerical" order for internal
//#	  storage of numbers. Ascii strings however are stored with each character as a numerical value in one single
//#	  byte with the first (leftmost) character at the lowest address and the last (rightmost) character at the
//#	  highest address. So for Numbers the addresses are determined by the right-to-left SIGNIFICANCE of the digits
//#	  and for Strings they are determined by the left-to-right POSITION within the string. For transmission of data,
//#	  the actual numerical value is not relevant and then it is easier not to interpret the data but simply
//#	  address the individual bytes from low to high, which is called Internet order.
//#	  For example:  (to improve readability some blanks are inserted in the numerical representation)
//#
//#	  internal storage at (relative) byte addresses:		   byte3    byte2    byte1    byte0
//#	  binary representation of the (decimal) number 123456789 is:	00000111 01011011 11001101 00010101
//#	  NUMERICAL hexadecimal representation is 07 5b cd 15:		   0   7    5   b    c   d    1   5
//#
//#	  In Internet order bytes are addressed from low to high:	   byte0    byte1    byte2    byte3
//#	  but WITHIN the bytes it's still numerical order:		00010101 11001101 01011011 00000111
//#	  so hex notation in INTERNET order is    15 cd 5b 07:		   1   5    c   d    5   b    0   7
//#
//#	  the Ascii string "ABCD" is internally stored as:		   byte0    byte1    byte2    byte3
//#									     "A"      "B"      "C"      "D"
//#	  and the corresponding binary byte values are:			01000001 01000010 01000011 01000100
//#	  which in hex notation is 41 42 43 44:				   4   1    4   2    4   3    4   4
//#
//#	  interpreting that binary pattern as a NUMBER gives:		   byte3    byte2    byte1    byte0
//#									01000100 01000011 01000010 01000001
//#	  which is decimal 1,145,258,561 and hex 44 43 42 41:		   4   4    4   3    4   2    4   1
//#
//# --------------------------------------------------------------------------------------------------------------------
//# Properties added to the Constructor of the String Object:
//# ---------------------------------------------------------
//#
//#	String.CRLF, String.CR, String.LF
//#
//#		guess what?
//#
//#
//#	String.ByteTable
//#
//#		INTENDED FOR INTERNAL USE ONLY. DO NOT MODIFY THIS.
//#		this is a 256 character String made up of the byte values 0 through 255.
//#		String.ByteTable.charAt(byte) returns the corresponding character.
//#
//#
//#	String.Base64chars
//#
//#		INTENDED FOR INTERNAL USE ONLY. DO NOT MODIFY THIS.
//#		this is the set of characters used for base64 encoding/decoding.
//#
//#
//#	String.HashPad512
//#	String.MD5table
//#
//#		INTENDED FOR INTERNAL USE ONLY. DO NOT MODIFY THIS.
//#		HashPad512 is for padding messages in MD5, SHA1, or SHA256; it is a single one-bit plus 511 zero-bits.
//#		MD5table is the "T" array as defined in RFC1321; for performance reasons it has been defined as a
//#		static Array instead of recomputing it every time when an MD5 is computed; it has been computed as
//#		follows (where two32 equals 2 raised to the power 32):
//#		for (var T = [], i = 0; i < 64; T[i++] = Math.floor(Math.abs(Math.sin(i)) * two32) );
//#
//# --------------------------------------------------------------------------------------------------------------------
//# Methods added to the Constructor of the String Object:
//# ------------------------------------------------------
//#
//#	String.randomGenerator = Math.random
//#
//#		the String.random method (below) uses String.randomGenerator to obtain random numbers
//#		between 0 (inclusive) and 1 (exclusive).
//#		By default it is just another name for the built-in Math.random method, but you can redefine it
//#		to use another random number generator, which SHOULD return random real numbers in [0,1)
//#		For example you could use a MersenneTwister Object (see hr$mersenntwister.js) as follows:
//#			<script src=hr$binstring.js      ></script>
//#			<script src=hr$mersennetwister.js></script>
//#			<script>
//#				PRNG = new MersenneTwister()		// create a MersenneTwister Object
//#				String.randomGenerator = PRNG.random	// String.random will use it from now on
//#				<rest of your code>
//#			</script>
//#		Please note that the MersenneTwister Object also has a method to generate random Strings, but those
//#		strings will be reproducible (and thus predictable) if the same seed is used to init the MersenneTwister
//#		Object. The String.random method below always includes a hashed timestamp, in an attempt to make it
//#		unpredictable. Even better is to create some entropy pool based on events that are fully out of control
//#		of the software, for example the user's mouse and keyboard events and their timing.
//#		(I am planning to make a script to do just that.)
//#
//#
//#	String.random = function random(length)
//#
//#		returns a random string of 7-bit characters from the same set as used for base64.
//#		it is made up of the base64 encoded MD5 of a time stamp and a series of randomly generated characters.
//#		the minimum length of the result is 44 characters (twice the size of the timestamp part).
//#		this has little to do with binary string handling, but it is provided here for completeness.
//#		the result can be used to generate keys for the arcfour encoding/decoding provided in this library.
//#		Please note that NEITHER the built-in Math.random method NOR the Mersenne Twister is considered
//#		cryptographycally secure! For cryptographic purposes a random string should not only be statistically
//#		good, but also unpredictable and the algorithm should never reproduce the same string.
//#
//#
//#	String.fromByte(byte) 
//#
//#		converts a single byte value to its corresponding character.
//#
//#
//#	String.fromBytes([byte1[, byte2[, ...[, byteN]]]]) 
//#
//#		accepts any number of arguments, interprets them as byte values, and returns a String
//#		made up of those byte values (similar to the standard 'String.fromCharCode' method).
//#		extension w.r.t. fromCharCode: each argument can be either a byte value OR AN ARRAY OF BYTE VALUES.
//#
//#		EXAMPLE:
//#			String.fromBytes(13,10)
//#		or:	String.fromBytes(0x0d,0x0a)	yields the good old CRLF combination
//#
//#
//#	String.fromWord  (wordsize, value)
//#	String.beFromWord(wordsize, value)
//#
//#		converts a single word value (see 'fromWords' below) to a binary string.
//#		fromWord = little-endian, beFromWord = big-endian.
//#
//#
//#	String.fromWords  (wordsize, [word1[, word2[, ...[, wordN]]]]) 
//#	String.beFromWords(wordsize, [word1[, word2[, ...[, wordN]]]]) 
//#
//#		similar to 'fromBytes', but accepts binary values larger than 1 byte. wordsize is interpreted
//#		as the number of bytes in each value and each value will be converted to 'wordsize' bytes.
//#		fromWords uses little-endian notation, beFromWords uses big-endian notation.
//#		if a value is larger than what fits in wordsize bytes then the high order remainder is ignored.
//#		NOTE: JavaScript stores numbers with a 52 bit mantissa, so the maximum wordsize = 6 bytes.
//#		(in practice you will probably use only 2 and 4).
//#
//#		EXAMPLE:
//#			String.fromWords(2,10*256+13)	yields the good old CRLF combination
//#		or:	String.fromWords(2,0x0a0d)	(note the little-endian NUMERICAL order of the hex digits)
//#
//#			String.fromWords  (4,0x44434241,0x64636261) yields "ABCDabcd" (little-endian)
//#			String.befromWords(4,0x44434241,0x64636261) yields "DCBAdcba" (big-endian)
//#
//#	String.fromHex([hexstring1[, hexstring2[, ...[, hexstringN]]]])
//#
//#		accepts any number of printable hexadecimal strings (in internet order) and converts them
//#		to binary internal storage as one large string of byte values. arguments can be either a
//#		hexadecimal string or an array of such strings.
//#
//#		EXAMPLE:
//#			String.fromHex("0d0a")		yields the good old CRLF combination, (0d = CR, 0a = LF)
//#							(please note the INTERNET order of the hex digits)
//#
//#		NOTE:	there is also a fromHex method added to the Prototype, which acts on individual
//#			String Object Instances only (and this one uses that one to translate each argument).
//#			DO NOT CONFUSE THEM !!!
//#
//#
//#	String.fromBase64([b64string1[, b64string2[, ...[, b64stringN]]]])
//#
//#		accepts any number of base-64 encoded strings and converts them to binary internal storage as one
//#		large string of byte values. arguments can be either a b64 string or or an array of such strings.
//#		(see RFC2045).
//#
//#		NOTE:	there is also a fromBase64 method added to the Prototype, which acts on individual
//#			String Object Instances only (and this one uses that one to translate each argument).
//#			DO NOT CONFUSE THEM !!!
//#
//#
//#	String.toQuotedPrintable([string1[, string2[, ...[, stringN]]]])
//#
//#		accepts any number of strings and encodes them to quoted printable.
//#		result is not a String Object but an Array of String Objects, each element being one line
//#		of QP-encoded text. each input argument can be a string or an array of strings.
//#		(see RFC2045).
//#
//#		NOTE:	there is also a toQuotedPrintable method added to the Prototype, which acts on individual
//#			String Object Instances only (and this one uses that one to translate each argument).
//#			DO NOT CONFUSE THEM !!!
//#
//#
//#	String.fromQuotedPrintable([qpr1[, qpr2[, ...[, qprN]]]])
//#
//#		accepts any number of quoted printable encoded strings and converts them to normal strings.
//#		result is not a String Object but an Array of String Objects, each element being one line
//#		of the original text that was encoded. each input argument can be a QPencoded string or an
//#		array of such strings (as produced by String.prototype.toQuotedPrintable).
//#		(see RFC2045).
//#
//#		NOTE:	there is also a fromQuotedPrintable method added to the Prototype, which acts on individual
//#			String Object Instances only (and this one uses that one to translate each argument).
//#			DO NOT CONFUSE THEM !!!
//#
//# --------------------------------------------------------------------------------------------------------------------
//# Methods added to the prototype of the String Object:
//# ----------------------------------------------------
//#
//#	String.prototype.byteAt(pos)
//#
//#		similar to the standard 'charCodeAt' method, but returns the byte value as a Number from 0 to 255.
//#		if the given position of the string contains an unrecognised Unicode character then -1 is returned.
//#
//#		EXAMPLES:
//#			"ABCD".byteAt(0)		yields 65
//#			"xy€z".byteAt(2)		yields 128
//#
//#
//#	String.prototype.wordAt  (pos,wordsize)
//#	String.prototype.beWordAt(pos,wordsize)
//#
//#		returns the binary value of a word taken from the string at given position and given size.
//#		wordAt = little-endian, beWordAt = big-endian
//#
//#
//#	String.prototype.toBytes()
//#
//#		returns an Array with all byte values of the string's characters.
//#
//#		EXAMPLES:
//#			"ABCD".toBytes()		yields [65,66,67,68]
//#			"xy€z".toBytes()		yields [120,121,128,122]
//#
//#
//#	String.prototype.toWords  (wordsize)
//#	String.prototype.beToWords(wordsize)
//#
//#		interprets the entire string as raw data consisting of a series of words of 'wordsize' bytes
//#		toWords = little-endian, beToWords = big-endian.
//#
//#		EXAMPLE:
//#			"ABCD".toWords(2)	yields [0x4241,0x4443]	(little-endian)
//#			"ABCD".beToWords(2)	yields [0x4142,0x4344]	(big-endian)
//#
//#
//# --------------------------------------------------------------------------------------------------------------------
//# MIME (and other) encoding/decoding w/o encryption:
//# --------------------------------------------------
//#
//#	String.prototype.to7bit()
//#	String.prototype.from7bit()
//#	String.prototype.toURLtext()
//#	String.prototype.fromURLtext()
//#
//#		these 4 methods do encoding/decoding to and from 7 bit printable ascii text of a String.
//#		these function can be used to convert a String containing text with very special characters,
//#		especially Strings with Unicode characters that cannot be converted to 8-bits.
//#		the to7bit and from7bit are a weaker version of the javascript built-in escape/unescape methods,
//#		converting only the non-printables, the "%", and the non-7-bit values (from7bit is identical to unescape).
//#		the URL variants are a stronger version of escape/unescape,
//#		they also do the space/"+" conversion used in URL's, which the escape/unescape methods don't.
//#
//#
//#	String.prototype.toHex(sep)
//#
//#		translates the binary content of the string to a printable string of hexadecimal digits.
//#		conversion is done in internet order.
//#		the 'sep' argument is optional, if given then any hex digits are removed from it and the remainder
//#		is used as a spearator between the individual byte values.
//#
//#		EXAMPLES:
//#			"ABCD".toHex()			yields "41424344"
//#			"ABCD".toHex(",")		yields "41,42,43,44"
//# 
//#
//#	String.prototype.fromHex()
//#
//#		assumes the string is a printable hexadecimal string (in internet order) and returns a string
//#		made up of the corresponding byte values.
//#		allows reasonably free input format, all single commas and all patterns of non-hex characters are
//#		used as substring separator, blank substrings are translated to null bytes and all odd-length
//#		substrings will have a "0" prepended to it (allowing single digit byte values).
//#
//#		EXAMPLES:
//#			"0d0a".fromHex()		yields the good old CRLF combination
//#			"0d,0a".fromHex()		yields the good old CRLF combination
//#			"d,a".fromHex()			yields the good old CRLF combination
//#			"d,,a".fromHex()		yields a CR + a null byte + a LF
//#			"dgg0a".fromHex()		yields the good old CRLF combination
//#			"dgg0,a".fromHex()		yields a CR + a null byte + a LF
//#
//#		NOTE:	there is also a fromHex method added to the Constructor, which accepts input via
//#			function arguments only (but it uses this one for the actual translation).
//#			DO NOT CONFUSE THEM !!!
//#
//#
//#	String.prototype.toBase64()
//#
//#		translates the binary content of the string to an Array of Base64 encoded printable strings.
//#		each returned array element holds 76 base64 characters (except the last one, which may be shorter).
//#		(see RFC2045).
//#
//#		EXAMPLE:
//#			var b64 = datastring.toBase64()
//#			WriteLine (b64.join(String.CRLF))	outputs the base64 result in standard internet format
//#
//#
//#	String.prototype.fromBase64()
//#
//#		translate a single base-64 encoded string to a string containing the corresponding binary data.
//#		(see RFC2045).
//#
//#		NOTE:	there is also a fromBase64 method added to the Constructor,
//#			which accepts input via function arguments only.
//#			DO NOT CONFUSE THEM !!!
//#
//#
//#	String.prototype.toQuotedPrintable()
//#
//#		translates the content of the string to an Array of Quoted Printable encoded strings (see RFC2045).
//#		each returned array element is a string of up to 76 characters.
//#		(see RFC2045).
//#
//#
//#	String.prototype.fromQuotedPrintable()
//#
//#		translates one single line of Quoted Printable encode text to normal text and returns an Array
//#		Array Object with the resulting text in element 0 and a boolean end-of-line flag in element 1.
//#		Quoted Printable ENcoding splits long lines into smaller portions of one String each and it has
//#		a special way to mark the hard end of line. As long as that hard end of line is not seen all
//#		consecutive input should be concatenated to one single decoded result line.
//#		(see RFC2045).
//#
//#		NOTE:	there is also a fromQuotedPrintable method added to the Constructor, which accepts input
//#			via function arguments only (but it uses this one for the actual translation).
//#			DO NOT CONFUSE THEM !!!
//#
//#
//# --------------------------------------------------------------------------------------------------------------------
//# Hashing algorithms:
//# -------------------
//#
//#	String.prototype.MD5()
//#
//#		returns the MD5 value of a string. (see RFC1321).
//#		result is a string holding the raw binary result, which for textual representation
//#		still needs to be converted to hexadecimal or base64 or whatever format is needed.
//#
//#	String.prototype.SHA1()
//#	String.prototype.SHA256()
//#
//#		return a String's Secure Hash according to the Secure Hash Standard FIPS180-2.
//#		result is a string holding the raw binary result, which for textual representation
//#		still needs to be converted to hexadecimal or base64 or whatever format is needed.
//#
//#	String.prototype.HMAC (hashfun, key)
//#
//#		returns a keyed hash conform RFC2104.
//#		hashfun	should be a String with the name of the hash function (i.e. "MD5", "SHA1", or "SHA256")
//#			if it equals "" then it defaults to "SHA256".
//#		key	should be a String with the key to use, preferable 64 bytes long (the blocksize of the
//#			hashing algorithm), as recommended in the RFC.
//#
//#		EXAMPLE (from rfc2104):
//#			message = "what do ya want for nothing?"
//#			key     = "Jefe"
//#			hash    = message.HMAC (key, "MD5")
//#			hash.toHex() => 750c783e6ab0b503eaa86e310a5db738
//#
//#	String.prototype.hashtext ( [hashfun [, key] ] )
//#
//#		creates a textual and reasonably pronounceable text from a message's hash. It computes the hash and uses
//#		that as the seed for a MersenneTwister Object, thus causing it to reproduce the same sequence if invoked
//#		for the same message once again. Then it creates a RandomTextGenerator Object with the MersenneTwister's
//#		'random' method as its random number generator. This RandomTextGenerator is then used to produce a
//#		phrase of several nonsense words. Essentially, this phrase is a textual hash of the original message.
//#		It will be reasonably pronounceable according to Dutch pronunciation rules. The length of the phrase is
//#		based on the size of the hash. This method is intended to be used when users may need to do a visual
//#		check if two message hashes are the same. By translating it to readable and pronounceable text the
//#		comparison will be much easier.
//#
//#		hashfun	= the name of the hash method to be used, either "MD5", "SHA1", "SHA256"
//#			  this is an optional argument, if omitted it defaults to "SHA256"
//#		key	= if this argument is given, then the hash will be done using HMAC with this key and the given
//#			  hash function. if this argument is omitted or a null string then hashfun is used as is.
//#
//#		EXAMPLES (based on the RandomTextGenerator100 version 1-00 from 07-JAN-2005):
//#
//#			"Henk.Reints".hashtext()                      
//#			=> "Pite tra, eirfed putifble aadfou veo, irhiarmenmij umzie guphe ja ungt wordou ou old ho to."
//#
//#			"Henk Reints".hashtext()                      
//#			=> "Huinwo gwolen ij la eo, eu ugpek ja fea nulra tha ofisijsla ea eo hia nij foef rijnzork ea."
//#
//#			"Henk Reints".hashtext("SHA1")                
//#			=> "Aungstarp hei erwauw wedwaglei trie zuis, ria ikabderia."
//#
//#			"Henk Reints".hashtext("MD5")                 
//#			=> "Haufuigta swo buje ou, kiekep ligza oetnear ia."
//#
//#			"Henk Reints".hashtext("MD5","e pur si muove")
//#			=> "Op aupoud ooppog okgo aundga fre nou epga tit."
//#
//#		PREREQUISITE:
//#			hr$mersennetwister.js and hr$randomtext1-00.js must be included before hr$binstring.js.
//#			the constructors of the MersenneTwister and the RandomTextGenerator Objects must both have been
//#			defined before running this script. If either one of them is missing, then the hashtext method
//#			will be unavailable.
//#
//# --------------------------------------------------------------------------------------------------------------------
//# ARCFOUR encryption/decryption:
//# ------------------------------
//#
//#	String.prototype.arcfour(passphrase)
//#	String.prototype.arcfourEncode(passphrase)
//#	String.prototype.arcfourDecode(passphrase)
//#
//#		methods for arcfour encoding and decoding of a string.
//#		the arcfour method is intended for internal use only. it performs the arcfour algorithm using the
//#		byte values of the string, not allowing encoding of special Unicode characters that cannot be converted
//#		to byte values. by apllying the arcfour algorithm with the same passphrase on its own result, the
//#		original text should be returned.
//#		(see also http://www.mozilla.org/projects/security/pki/nss/draft-kaukonen-cipher-arcfour-03.txt
//#		or use any internet search engine with keyword "arcfour")
//#
//#		the arcfourEncode and -Decode methods are wrappers around arcfour itself, using the built-in
//#		javascript escape and unescape functions, allowing any String to be encoded and the result to be
//#		transmitted via a 7-bit channel.
//#
//#		a good method is to combine the passphrase with a random once-only text
//#		('passphrase' is actually the same as 'password', but it can contain blanks as well),
//#		so that encoding of the same text yields a different result every time.
//#		the once-only part of the key should be transmitted together with the encoded result
//#		or, even better, as a separate message at a later time or via another channel.
//#		the passphrase itself should be kept secret, you should only share it with intended and
//#		trusted recipients via a secure channel!
//#
//#		EXAMPLE:
//#			sender:
//#				message    = "Albert Einstein: My wife doesn't understand me..."
//#				passphrase = "Gott wuerfelt nicht."
//#				keyonce    = String.random(64)
//#				encoded    = message.arcfourEncode(keyonce+passphrase)
//#				// transmit 'keyonce' and 'encoded' to recipient via internet
//#				// call him by phone to tell him the exact passphrase
//#			recipient:
//#				message    = encoded.arcfourDecode(keyonce+passphrase)
//#
//#	String.prototype.arcfourEncrypt(passphrase[,keylen])
//#	String.prototype.arcfourDecrypt(passphrase)
//#
//#		these methods do more or less (more less than more) what the above example shows.
//#		the 'arcfourEncrypt' method generates a random key using String.random(keylen),
//#		(keylen is optional, if omitted it reverts to the default of String.random).
//#		this key is intermixed with the passphrase.
//#		it returns the generated key (w/o the passphrase) + CRLF + the encoded message.
//#		the 'arcfourDecrypt' method accepts such input and decodes it.
//#
//#	String.prototype.arcfourEncodeHashed  (passphrase [, hashfun] )
//#	String.prototype.arcfourDecodeHashed  (passphrase [, hashfun] )
//#	String.prototype.arcfourEncryptHashed (passphrase [, hashfun [, keylen] ] )
//#	String.prototype.arcfourDecryptHashed (passphrase [, hashfun] )
//#
//#		these methods do the arcfour stuff including a hash of the original message, thus allowing an
//#		integrity check when decoding. the encode/encrypt methods simply include the message's hash in
//#		their result. the decode/decrypt methods split the decoded result into hash and message and then
//#		check if the hash is ok. the decode/decrypt methods return an unnamed Object with two properties:
//#		'value' = the decoded message, 'ok' = a boolean indicating if the hash check was OK or not.
//#		hashfun is an optional argument specifying the name of the hash function to be used ("MD5", "SHA1",
//#		or "SHA256"). if omitted or a null string then it defaults to "SHA256".
//#
//#		EXAMPLE:
//#			theSecret = "The world goes round and round."
//#			encrypted = theSecret.arcfourEncodeHashed("NobodyKnowsThisExceptYouAndMe")
//#
//#			decrypted = encrypted.arcfourDecodeHashed("NobodyKnowsThisExceptMeAndYou")	// wrong!
//#			WScript.Echo(decrypted.value) => an incorrectly decoded unreadable string containing nonsense
//#			WScript.Echo(decrypted.ok   ) => false
//#
//#			decrypted = encrypted.arcfourDecodeHashed("NobodyKnowsThisExceptYouAndMe")	// good!
//#			WScript.Echo(decrypted.value) => "The world goes round and round."
//#			WScript.Echo(decrypted.ok   ) => true
//#
//# --------------------------------------------------------------------------------------------------------------------
//# file I/O methods:
//# -----------------
//#
//#	String.prototype.ReadFile   (fso)
//#	String.prototype.CreateFile (fso, data, overwrite)
//#
//#		These methods perform binary file I/O by reading or writing ENTIRE files at once.
//#		They use the String itself as the file specification (path) to be read or written.
//#		'ReadFile' returns the full uninterpreted content of the file,
//#		'CreateFile' writes 'data' to a new file. The 'overwrite' argument is a boolean that determines
//#		whether or not to overwrite any existing file, it is an optional argument, default value = 'false'.
//#		In all 3 methods, _YOU_ (i.e. the calling script) must supply a FileSystemObject in the 'fso' argument.
//#		All I/O is in uninterpreted binary mode as far as is possible in JavaScript, with one exception: if the
//#		'data' argument is an Array, then each Array element will be written as a normal String _WITH_ a
//#		trailing new line after every array element (so nothing binary at all). In other cases: if data is
//#		not a String Object then its own 'toString' method will be used implicitly by JavaScript.
//#
//#		These methods do not perform error checking, so they will cause a fatal script termination if you supply
//#		incorrect data (e.g. if attempting to read a non-existing file or). You can embed the method invocation
//#		in a try/catch structure to prevent errors being fatal and take corrective actions in your script.
//#
//#		EXAMPLES:
//#			fso  = WScript.CreateObject ("Scripting.FileSystemObject")
//#			path = "C:\temp\test.dat"
//#			data = String.fromBytes (byte0, byte1, etc.)
//#			path.CreateFile (fso, data)		// writes data to c:\temp\test.dat w/o trailing newline
//#			temp = path.ReadFile (fso)		// reads binary content of c:\temp\test.dat into temp
//#			if (temp != data) WScript.Echo("error!")
//#
//#			try
//#			{	temp = path.CreateFile (fso, data)	// will fail since file exists
//#			}
//#			catch (e)
//#			{	// interpret 'e' to find out what error occurred and take appropriate corrective action
//#			}
//#
//#			fso.DeleteFile(path)
//#			temp = path.ReadFile (fso)		// causes a fatal script error
//#
//#		NOTE:
//#		When you read an entire file using the built-in 'ReadAll' method of the TextStream Object, it does not
//#		always return the correct BINARY file content, as I found out during testing of my SHA1 method (it cost
//#		me quite a lot of headache to find out that it was this built-in method that caused all the trouble).
//#		Instead of 'stream.ReadAll()', the 'ReadFile' method uses 'stream.Read(file.Size)', which appears
//#		to return the correct result as far as I can find out.
//#
//# ====================================================================================================================
//# ====================================================================================================================


// ---------------------------------------------
// properties added to the String Object itself:
// ---------------------------------------------

	String.CRLF = String.fromCharCode(13,10)
	String.CR   = String.fromCharCode(13)
	String.LF   = String.fromCharCode(10)

	String.ByteTable     // conversion table determined empirically by reading a binary file containing 0 throug 255
	= unescape
		("%00%01%02%03%04%05%06%07%08%09%0A%0B%0C%0D%0E%0F%10%11%12%13%14%15%16%17%18%19%1A%1B%1C%1D%1E%1F"
		+" !\"#$%25&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~%7F"
		+"%u20AC%81%u201A%u0192%u201E%u2026%u2020%u2021%u02C6%u2030%u0160%u2039%u0152%8D%u017D%8F%90"
		+"%u2018%u2019%u201C%u201D%u2022%u2013%u2014%u02DC%u2122%u0161%u203A%u0153%9D%u017E%u0178"
		+"%A0%A1%A2%A3%A4%A5%A6%A7%A8%A9%AA%AB%AC%AD%AE%AF%B0%B1%B2%B3%B4%B5%B6%B7%B8%B9%BA%BB%BC%BD%BE%BF"
		+"%C0%C1%C2%C3%C4%C5%C6%C7%C8%C9%CA%CB%CC%CD%CE%CF%D0%D1%D2%D3%D4%D5%D6%D7%D8%D9%DA%DB%DC%DD%DE%DF"
		+"%E0%E1%E2%E3%E4%E5%E6%E7%E8%E9%EA%EB%EC%ED%EE%EF%F0%F1%F2%F3%F4%F5%F6%F7%F8%F9%FA%FB%FC%FD%FE%FF"
		)

	String.Base64chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"

	// HashPad512 is a single "1" bit plus 511 "0" bits, left justified
	// in hex it is 0x80 padded with 63 times 0x00, byte code 0x80 corresponds to unicode %u20AC (euro-sign "€")

	// MD5table is the "T" array needed for MD5 computation as defined in RFC1321, computed as follows:
	// for (var T = [], i = 0; i < 64; T[i++] = Math.floor(Math.abs(Math.sin(i)) * two32) );
	// (where two32 equals 2 raised to the power 32)

	String.HashPad512 = unescape("%u20AC") + (new Array(64)).join(String.fromCharCode(0))
	String.MD5table
	=	[3614090360,3905402710, 606105819,3250441966,4118548399,1200080426,2821735955,4249261313
		,1770035416,2336552879,4294925233,2304563134,1804603682,4254626195,2792965006,1236535329
		,4129170786,3225465664, 643717713,3921069994,3593408605,  38016083,3634488961,3889429448
		, 568446438,3275163606,4107603335,1163531501,2850285829,4243563512,1735328473,2368359562
		,4294588738,2272392833,1839030562,4259657740,2763975236,1272893353,4139469664,3200236656
		, 681279174,3936430074,3572445317,  76029189,3654602809,3873151461, 530742520,3299628645
		,4096336452,1126891415,2878612391,4237533241,1700485571,2399980690,4293915773,2240044497
		,1873313359,4264355552,2734768916,1309151649,4149444226,3174756917, 718787259,3951481745
		]

// ------------------------------------------------------------------------------------------------------
// next is intended for internal use only! it is not documented above. it list available hash functions
// and which one is the default, and a method to check a given hash function name:
// ------------------------------------------------------------------------------------------------------
	String.hashFuns       = "/MD5/SHA1/SHA256/"
	String.defaultHashFun = "SHA256"
	String.checkHashFun   = function checkHashFun(hashfun,caller)
	{	var hfun = "" + hashfun
		if (hfun == "") hfun = String.defaultHashFun
		if (String.hashFuns.indexOf("/"+hfun+"/") >= 0) return hfun
		throw "Invalid hash function '" + hfun + "' for hr$binstring.js " + caller
	}

// ------------------------------------------
// methods added to the String Object itself:
// ------------------------------------------

	String.randomGenerator = Math.random

	String.random = function random(length)
	{	var C = String.Base64chars
		var T = new Date(), t = T.toString().split(" "), M = T.getMilliseconds(), L = T.toLocaleString()
		var x = [t[0],t[2],t[1],t[5],t[3]+"."+("000"+M).slice(-3),t[4],L].join(" ")
		var X = x.MD5().toBase64().join("").replace(/=/g,"")
		var r = "", i=0, j=0, Lr=Math.max(length,2*X.length)
		for	(; i < Lr;
			r += ((i++)&1 && j<X.length
			? X.charAt(j++)
			: C.charAt(Math.floor(String.randomGenerator()*C.length)))
			);
		return r
	}

	// -------------------------------------------------------------------------------------------------------------
	// the fromByte and fromBytes functions masks all values with 255,
	// thus reading only the low order byte of the value:
	// -------------------------------------------------------------------------------------------------------------

	String.fromByte = function fromByte (byte)
	{	return String.ByteTable.charAt(255&byte)
	}

	String.fromBytes = function fromBytes()
	{	for (var result = "", i = 0; i < arguments.length; i++)
		{	var arg = (arguments[i].constructor == Array ? arguments[i] : [arguments[i]] )
			for (var j = 0; j < arg.length; result += String.fromByte(arg[j++]) );
		}
		return result
	}

	// -------------------------------------------------------------------------------------------------------------
	// words may become negative when using bitwise operators such as left-shift. those operations are not performed
	// by the floating point processor but by the cpu itself, and whenever javascript gets such a 32-bit number back
	// from the cpu, it always interprets it as a 2's complement signed integer (e.g. 2147483647 = 0x7fffffff << 1
	// yields 0xfffffffe, and the latter is returned as -2 and not as 2 * 2147483647 = 4294967294. Since you may
	// pass in an array of words that have been computed using bitwise operators (such as MD5) this must be taken
	// care of. The fromWord method unsigns the word by adding 256**wordsize to negative numbers.
	// the 'toWords' method always returns unsigned word values since is does not use bitwise operators.
	// -------------------------------------------------------------------------------------------------------------
	// do NOT use the bitwise operations to break down word values, but DIVIDE, since values may exceed 32 bits.
	// ---------------------------------------------------------------------------------------------------------

	String.fromWord = function fromWord (wordsize,value)
	{	var v = value
		if (v < 0) {for (var i = 0, W = 1; i < wordsize; W *= 256, i++); v += W}
		for (var result = "", b, k = 0; k < wordsize; result += String.fromByte(b=v%256), v=(v-b)/256, k++);
		return result
	}

	String.beFromWord = function beFromWord (wordsize,value)
	{	var v = value
		if (v < 0) {for (var i = 0, W = 1; i < wordsize; W *= 256, i++); v += W}
		for (var result = "", b, k = 0; k < wordsize; result=String.fromByte(b=v%256)+result, v=(v-b)/256, k++);
		return result
	}

	String.fromWords = function fromWords(wordsize)
	{	for (var result = "", i = 1; i < arguments.length; i++)
		{	var arg = (arguments[i].constructor == Array ? arguments[i] : [arguments[i]] )
			for (var j = 0; j < arg.length; result += String.fromWord(wordsize,arg[j++]) );
		}
		return result
	}

	String.beFromWords = function beFromWords(wordsize)
	{	for (var result = "", i = 1; i < arguments.length; i++)
		{	var arg = (arguments[i].constructor == Array ? arguments[i] : [arguments[i]] )
			for (var j = 0; j < arg.length; result += String.beFromWord(wordsize,arg[j++]) );
		}
		return result
	}

// -----------------------
// MIME encoding/decoding:
// -----------------------

	String.fromHex = function fromHex()
	{	for (var result = "", i = 0; i < arguments.length; i++)
		{	var arg = (arguments[i].constructor == Array ? arguments[i] : [arguments[i]] )
			for (var j = 0; j < arg.length; result += arg[j++].fromHex() );
		}
		return result
	}

	String.fromBase64 = function fromBase64()
	{	for (var result = "", i = 0; i < arguments.length; i++)
		{	var arg = (arguments[i].constructor == Array ? arguments[i] : [arguments[i]] )
			for (var j = 0; j < arg.length; result += arg[j++].fromBase64() );
		}
		return result
	}

	String.toQuotedPrintable = function toQuotedPrintable()
	{	for (var result = [], i = 0; i < arguments.length; i++)
		{	var arg = (arguments[i].constructor == Array ? arguments[i] : [arguments[i]] )
			for (var j = 0; j < arg.length; result = result.concat(arg[j++].toQuotedPrintable()) );
		}
		return result
	}

	String.fromQuotedPrintable = function fromQuotedPrintable()
	{	var temp
		for (var result = [], x = "", k = 0, i = 0; i < arguments.length; i++)
		{	var arg = (arguments[i].constructor == Array ? arguments[i] : [arguments[i]] )
			for (var j = 0; j < arg.length; j++)
			{	temp = arg[j].fromQuotedPrintable()
				x += temp[0]
				if (temp[1]) {result[k++] = x; x = ""}
		}	}
		if (x != "") result[k++] = x	// missing hard eol in last qpr string!
		return result
	}

// ----------------------------------------------------
// methods added to the prototype of the String Object:
// ----------------------------------------------------

	String.prototype.byteAt = function byteAt(pos)
	{	var c = this.charCodeAt(pos)
		return (c < 256 ? c : String.ByteTable.indexOf(this.charAt(pos),128) )
	}

	// ---------------------------------------------------------------------------------------------------------
	// do NOT use bitwise shift operations to compute word values, but MULTIPLY since values may exceed 32 bits.
	// ---------------------------------------------------------------------------------------------------------

	String.prototype.wordAt = function wordAt(pos,wordsize)
	{	for (var x = this.substr(pos,wordsize), v = 0, j = x.length; j > 0; v = v * 256 + x.byteAt(--j) );
		return v
	}

	String.prototype.beWordAt = function beWordAt(pos,wordsize)
	{	for (var x = this.substr(pos,wordsize), v = 0, j = 0; j < x.length; v = v * 256 + x.byteAt(j++) );
		return v
	}

	String.prototype.toBytes = function toBytes()
	{	for (var result = [], k = 0, i = 0; i < this.length; result[k++] = this.byteAt(i++) );
		return result
	}

	String.prototype.toWords = function toWords(wordsize)
	{	for (var result = [], k = 0, i = 0; i < this.length; result[k++] = this.wordAt(i,wordsize), i+=wordsize);
		return result
	}
	String.prototype.beToWords = function beToWords(wordsize)
	{	for (var result = [], k = 0, i = 0; i < this.length; result[k++] = this.beWordAt(i,wordsize), i+=wordsize);
		return result
	}

// ----------------------------------------------------
// methods for (MIME or other) encoding w/o encryption:
// ----------------------------------------------------

	String.prototype.to7bit = function to7bit()
	{	var r="",c,b,i
		for(i=0;i<this.length;c=this.charAt(i++),b=c.charCodeAt(0),r+=(b<32||c=="%"||b>126?escape(c):c));
		return r
	}

	String.prototype.from7bit = function from7bit()
	{	return unescape(this)
	}

	String.prototype.toURLtext= function toURLtext()
	{	return escape(this).replace(/\+/g,"%2B").replace(/%20/g,"+")
	}

	String.prototype.fromURLtext = function fromURLtext()
	{	return unescape(this.replace(/\+/g," "))
	}

	String.prototype.toHex = function toHex(sep)
	{	var S = (arguments.length > 0 ? (""+sep).replace(/[0-9A-Fa-f]+/g,"") : ""), s = ""
		for (var result="", i=0; i < this.length; result+=s+("0"+this.byteAt(i++).toString(16)).slice(-2),s=S);
		return result
	}

	// -------------------------------------------------------------------------------------------------------------
	// in the fromHex function we'll allow a reasonably free format. every single comma or non-hex pattern is
	// interpreted as a substring separator. if a substring is blank (between commas) it is interpreted as a single
	// byte with value 0 (so ",,," represents a 32 bit zero). if a substring has odd length a "0" will be PREpended
	// to it, mainly to allow bytes represented by single hex digits separated by non-hex characters (longer
	// substrings SHOULD have an even length)
	// -------------------------------------------------------------------------------------------------------------

	String.prototype.fromHex = function fromHex()
	{	if (this.length == 0) return ""
		for (var result = "", A = this.replace(/[^0-9A-Fa-f,]+/g,",").split(","), i = 0; i < A.length; i++)
		{	var L = A[i].length, H = (L < 1 ? "00" : L%2 == 1 ? "0" + A[i] : A[i])
			for (var j=0; j < H.length; result += String.ByteTable.charAt(parseInt(H.slice(j++,++j),16)) );
		}
		return result
	}

	String.prototype.toBase64 = function toBase64()
	{	var C = String.Base64chars + "="
		var L = this.length, M = L - L % 3, R = [], r = ""
		for (var k = 0, i = 0, t = 0; i < M; )
		{	r += C.charAt( (this.byteAt(i)&0xff) >>>2)
			r += C.charAt(((this.byteAt(i)&0x03) << 4) | ((this.byteAt(++i)&0xf0) >>> 4))
			r += C.charAt(((this.byteAt(i)&0x0f) << 2) | ((this.byteAt(++i)&0xc0) >>> 6))
			r += C.charAt(this.byteAt(i++)&0x3f)
			if (r.length == 76) {R[k++] = r; r = ""}
		}
		if (M < L)
		{	r += C.charAt((this.byteAt(M)&0xff) >>>2)
			r += C.charAt((this.byteAt(M)&0x03) << 4 | (++M < L ? (this.byteAt(M)&0xf0) >>> 4 : 0))
			r += C.charAt(M < L ? (this.byteAt(M)&0x0f) << 2 : 64)
			r += "="
		}
		if (r != "") R[k++] = r
		return R
	}

	String.prototype.fromBase64 = function fromBase64()
	{	var C = String.Base64chars
		var Q = [], R = ""
		for (var k = 0, i = 0; i < this.length; i++)
		{	var c = C.indexOf(this.charAt(i))
			if (c >= 0) Q[k++] = c
			if (Q.length == 4)
			{	R += String.ByteTable.charAt( (  Q[0]       << 2) | (Q[1] >>> 4) )
				R += String.ByteTable.charAt( ( (Q[1]&0x0f) << 4) | (Q[2] >>> 2) )
				R += String.ByteTable.charAt( ( (Q[2]&0x03) << 6) |  Q[3]        )
				Q = [], k = 0
			}
		}
		if (Q.length > 1) R += String.ByteTable.charAt ( (  Q[0]       << 2) | (Q[1] >>> 4) )
		if (Q.length > 2) R += String.ByteTable.charAt ( ( (Q[1]&0x0f) << 4) | (Q[2] >>> 2) )
		return R
	}

	String.prototype.toQuotedPrintable = function toQuotedPrintable()
	{	var R = [], r = "", k = 0
		function pad (x) {if (r.length + x.length < 76) {r += x} else {R[k++] = r + "="; r = x} }
		for (var i = 0; i < this.length; i++)
		{	var c = this.byteAt(i)
			if (c > 31 && c != 61 && c < 127) pad (this.charAt(i))
			else pad ("=" + (0x100+c).toString(16).slice(-2).toUpperCase())
		}
		if	(i == 0)		{R[k++] = ""			}
		else if	(r.slice(-1) != " ")	{R[k++] = r			}
		else if (r.length < 74)		{R[k++] = r.slice( 0,-1) + "=20"}
		else				{R[k++] = r.slice( 0,74) + "="
						;R[k++] = r.slice(74,-1) + "=20"}
		return R
	}

	String.prototype.fromQuotedPrintable = function fromQuotedPrintable()
	{	var c, R = "", L = this.replace(/\s+$/,"").length, eol = this.charAt(--L) != "="; if (eol) L++
		for (var i = 0; i < L;	c = this.charAt(i++),
					R += (c != "=" ? c : String.ByteTable.charAt(parseInt(this.slice(i++,++i),16))) );
		return [R,eol]
	}

// --------------------------------------------------------
// Hashing algorithms (they all operate in byte mode only):
// --------------------------------------------------------

	String.prototype.MD5 = function MD5()
	{	var m = 0x100000000, L=this.length, L1=L%64, L0=L-L1, L2=64-L1, len=L*8, llo=len%m, lhi=(len-llo)/m
		var pad = String.HashPad512.slice(0,1+((959-(len&511))&511)>>>3) + String.fromWords(4,llo,lhi)
		var N = (L+pad.length)/4, M=0xffffffff, A=0x67452301, B=0xefcdab89, C=0x98badcfe, D=0x10325476
		var Q = [7,12,17,22,5,9,14,20,4,11,16,23,6,10,15,21], T = String.MD5table
		function blk(k,x)  {return k<L0 ? x.substr(k,64) : k<L ? x.slice(k)+pad.slice(0,L2) : pad.slice(k-L) }
		function F (x,y,z) {P = (P+1)&15; return (x & y) | (~x & z) }
		function G (x,y,z) {P = (P+5)&15; return (x & z) | (y & ~z) }
		function H (x,y,z) {P = (P+3)&15; return x ^ y ^ z}
		function I (x,y,z) {P = (P+7)&15; return y ^ (x | ~z) }
		function R (w,n)   {var W = w & M; return ( (W << n) | (W >>> (32 - n) ) ) & M}
		function S (f,a,b,c,d) {return (b + R (a + f(b,c,d) + W[P] + T[J], Q[((J>>>4)<<2)|(J++)&3] ) )&M}
		for (var k = 0; k < N; k += 16)
		{	var AA = A, BB = B, CC = C, DD = D, J = 0, W = blk(k<<2,this).toWords(4)
			for (var P=15,i=0; i<4; A=S(F,A,B,C,D), D=S(F,D,A,B,C), C=S(F,C,D,A,B), B=S(F,B,C,D,A), i++);
			for (var P=12,i=0; i<4; A=S(G,A,B,C,D), D=S(G,D,A,B,C), C=S(G,C,D,A,B), B=S(G,B,C,D,A), i++);
			for (var P= 2,i=0; i<4; A=S(H,A,B,C,D), D=S(H,D,A,B,C), C=S(H,C,D,A,B), B=S(H,B,C,D,A), i++);
			for (var P= 9,i=0; i<4; A=S(I,A,B,C,D), D=S(I,D,A,B,C), C=S(I,C,D,A,B), B=S(I,B,C,D,A), i++);
			A = (A + AA) & M; B = (B + BB) & M; C = (C + CC) & M; D = (D + DD) & M;
		}
		return String.fromWords(4,A,B,C,D)
	}	// Please note that the sample c-program of the MD5 algorithm in rfc1321 is SEVENTEEN PAGES!

	// -------------------------------------------------------------------------------------------------------------
	// SHA1 padding is the same as for MD5, except for the last two words (MD5 uses little endian, SHA big endian);
	// the core is coded w/o any function calls, which reduces computation time significantly (factor 2).
	// (maybe I'll rewrite MD5 all inline as well. some time.)
	//
	// NOTE 1:
	//   http://csrc.nist.gov/cryptval/shs/sha1-vectors.zip gives a lot of test messages and their SHA1 values.
	//   I've tested them all successfully with the implementation below (only the byte-mode samples).
	//
	// NOTE 2:
	//   RFC3174   defines f(t;B,C,D) for  0<=t<20 as:                      (B AND C)  OR ((NOT B) AND D)
	//   FIPS180-2 defines f(t;x,y,z) for  0<=t<20 as: Ch(x,y,z)  which is: (x AND y) XOR ( NOT x  AND z)
	//   RFC3174   defines f(t;B,C,D) for 40<=t<60 as:                      (B AND C)  OR (B AND D)  OR (C AND D)
	//   FIPS180-2 defines f(t;x,y,z) for 40<=t<60 as: Maj(x,y,z) which is: (x AND y) XOR (x AND z) XOR (y AND z)
	//   My tests (mentioned above) have passed with _BOTH_ variants! I've implemented the FIPS180-2 thing below,
	//   considering the FIPS document as the primary source having precedence over the RFC.
	//   the table below shows that both are in fact equivalent:
	//	x,y,z		(x&y)|((~x)&z)	(x&y)^((~x)&z)		(x&y)|(x&z)|(y&z)	(x&y)^(x&z)^(y&z)
	//	-----		--------------	--------------		-----------------	-----------------
	//	0,0,0		0		0			0			0
	//	0,0,1		1		1			0			0
	//	0,1,0		0		0			0			0
	//	0,1,1		1		1			1			1
	//	1,0,0		0		0			0			0
	//	1,0,1		0		0			1			1
	//	1,1,0		1		1			1			1
	//	1,1,1		1		1			1			1
	// -------------------------------------------------------------------------------------------------------------

	String.prototype.SHA1 = function SHA1()
	{	var m = 0x100000000, L=this.length, L1=L%64, L0=L-L1, L2=64-L1, len=L*8, llo=len%m, lhi=(len-llo)/m
		var pad = String.HashPad512.slice(0,1+((959-(len&511))&511)>>>3) + String.beFromWords(4,lhi,llo)
		var N = (L+pad.length)/4, M=0xffffffff, t,X,A,B,C,D,E, k,k4,W,w
		var H0 = 0x67452301, H1 = 0xefcdab89, H2 = 0x98badcfe, H3 = 0x10325476, H4 = 0xc3d2e1f0
		var K0 = 0x5a827999, K1 = 0x6ed9eba1, K2 = 0x8f1bbcdc, K3 = 0xca62c1d6
		for (k = 0; k < N; k += 16)
		{ k4 = k<<2
		  W =(k4<L0 ? this.substr(k4,64) : k4<L ? this.slice(k4)+pad.slice(0,L2) : pad.slice(k4-L)).beToWords(4)
		  for (t = 16; t < 80; w = W[t-3]^W[t-8]^W[t-14]^W[t-16], W[t++] = (w<<1)|(w>>>31) );
		  A=H0, B=H1, C=H2, D=H3, E=H4, t=0
		  for(;t<20;){X=(((A<<5)|(A>>>27))+((B&C)^((~B)&D))   +E+W[t++]+K0)&M,E=D,D=C,C=(B<<30)|(B>>>2),B=A,A=X}
		  for(;t<40;){X=(((A<<5)|(A>>>27))+(B^C^D)            +E+W[t++]+K1)&M,E=D,D=C,C=(B<<30)|(B>>>2),B=A,A=X}
		  for(;t<60;){X=(((A<<5)|(A>>>27))+((B&C)^(B&D)^(C&D))+E+W[t++]+K2)&M,E=D,D=C,C=(B<<30)|(B>>>2),B=A,A=X}
		  for(;t<80;){X=(((A<<5)|(A>>>27))+(B^C^D)            +E+W[t++]+K3)&M,E=D,D=C,C=(B<<30)|(B>>>2),B=A,A=X}
		  H0 = (H0 + A) & M; H1 = (H1 + B) & M; H2 = (H2 + C) & M; H3 = (H3 + D) & M; H4 = (H4 + E) & M;
		}
		return String.beFromWords(4,H0,H1,H2,H3,H4)
	}

	// -------------------------------------------------------------------------------------------------------------
	// SHA256 padding is the same as SHA1 padding.
	// Coding it all inline - instead of the well-structured thing with function calls - gained a speed factor 3.
	// Code is comform FIPS180-2 (see notes above about 'Ch' and 'Maj').
	// -------------------------------------------------------------------------------------------------------------

	String.prototype.SHA256 = function SHA256()
	{	var m = 0x100000000, L=this.length, L1=L%64, L0=L-L1, L2=64-L1, len=L*8, llo=len%m, lhi=(len-llo)/m
		var pad = String.HashPad512.slice(0,1+((959-(len&511))&511)>>>3) + String.beFromWords(4,lhi,llo)
		var N = (L+pad.length)/4, M=0xffffffff, k,k4,blk,W,t
		var K = [0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5
			,0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174
			,0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da
			,0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967
			,0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85
			,0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070
			,0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3
			,0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2
			]
		var H0 = 0x6a09e667, H1 = 0xbb67ae85, H2 = 0x3c6ef372, H3 = 0xa54ff53a
		var H4 = 0x510e527f, H5 = 0x9b05688c, H6 = 0x1f83d9ab, H7 = 0x5be0cd19
		for (k = 0; k < N; k += 16)
		{	k4  = k<<2
			blk = (k4<L0 ? this.substr(k4,64) : k4<L ? this.slice(k4)+pad.slice(0,L2) : pad.slice(k4-L))
			W   = blk.beToWords(4)
			for (t = 16; t < 64; t++)
			{	var x = W[t- 2], sigma1x = ((x>>>17)|(x<<15))^((x>>>19)|(x<<13))^(x>>>10)
				var y = W[t-15], sigma0y = ((y>>> 7)|(y<<25))^((y>>>18)|(y<<14))^(y>>> 3)
				W[t] = (sigma1x + W[t-7] + sigma0y + W[t-16] )&M
			}
			var a = H0, b = H1, c = H2, d = H3, e = H4, f = H5, g = H6, h = H7
			for (t = 0; t < 64; t++)
			{	var SIGMA1e = ((e>>> 6)|(e<<26))^((e>>>11)|(e<<21))^((e>>>25)|(e<< 7))
				var SIGMA0a = ((a>>> 2)|(a<<30))^((a>>>13)|(a<<19))^((a>>>22)|(a<<10))
				var Ch_efg  = (e&f)^((~e)&g)
				var Maj_abc = (a&b)^(a&c)^(b&c)
				var T1 = h + SIGMA1e + Ch_efg + K[t] + W[t]
				var T2 =     SIGMA0a + Maj_abc
				h = g, g = f, f = e, e = (d+T1)&M, d = c, c = b, b = a, a = (T1+T2)&M
			}
			H0=(H0+a)&M,H1=(H1+b)&M,H2=(H2+c)&M,H3=(H3+d)&M,H4=(H4+e)&M,H5=(H5+f)&M,H6=(H6+g)&M,H7=(H7+h)&M
		}
		return String.beFromWords(4,H0,H1,H2,H3,H4,H5,H6,H7)
	}

	// ----------------------------------------------------------------------------------------------------
	// the HMAC method sets the hash function blocksize B hard-coded to 64 bytes (ok for MD5, SHA1, SHA256)
	// which will need more sophistication when other hash functions are implemented:
	// ----------------------------------------------------------------------------------------------------

	String.prototype.HMAC = function HMAC (hashfun, key)
	{	var hfun = String.checkHashFun(hashfun,"HMAC")
		var B = 64
		var H = new Function ("x", "return x." + hfun + "()")
		var K = (key.length > B ? H(key) : key); K += (new Array(1+B-K.length)).join(String.fromByte(0))
		for (var Ki = "", i = 0; i < B; Ki += String.fromByte(K.byteAt(i)^0x36), i++);	// Ki = K xor ipad
		for (var Ko = "", i = 0; i < B; Ko += String.fromByte(K.byteAt(i)^0x5c), i++);	// Ko = K xor opad
		return H (Ko + H (Ki + this) )
	}

	// the hashtext method will only be available if both MersenneTwister
	// and RandomTextGenerator100 have already been defined:

if ( (typeof MersenneTwister == "function") && (typeof RandomTextGenerator100 == "function") )
{
	String.prototype.hashtext = function hashtext (hashfun, key)
	{	var na = arguments.length, hfun = String.checkHashFun( (na < 1 ? "" : hashfun), "hashtext")
		var seed = (na > 1 && key != "" ? this.HMAC(hfun,key) : eval("this."+hfun+"()") ).beToWords(4)
		var mt = new MersenneTwister(seed.shift(),seed)
		return (new RandomTextGenerator100(function(){return mt.random()})).Zin(13+seed.length*11)
	}
}

// ------------------------------------------
// ARCFOUR encryption and decryption methods:
// ------------------------------------------

	String.prototype.arcfour = function arcfour(passphrase)
	{	var r="", k=""+passphrase, L=k.length, s,t,i,j,m
		for (s=[],i=0; i<256; s[i]=i++);
		for (i=0 ,j=0; i<256; j=(j+s[i]+k.charCodeAt(i%L))&255, t=s[i],s[i++]=s[j],s[j]=t); k=""
		for (i=0 ,j=0,m=0; m<this.length; i=(i+1)&255, j=(j+s[i])&255, t=s[i],s[i]=s[j],s[j]=t,
						r+=String.fromByte(s[(s[i]+s[j])&255]^this.byteAt(m++)) );
  		return r
	}
	String.prototype.arcfourEncode = function arcfourEncode(passphrase)
	{	return this.to7bit().arcfour(passphrase).to7bit()
	}
	String.prototype.arcfourDecode = function arcfourDecode(passphrase)
	{	return this.from7bit().arcfour(passphrase).from7bit()
	}

	String.prototype.arcfourEncrypt = function arcfourEncrypt(passphrase,keylen)
	{	var k = String.random(arguments.length > 1 ? keylen : 0)
		var Lk = k.length, Lp = passphrase.length, L = Math.max(Lk,Lp)
		for (var kp = "", i = 0; i < L; kp += passphrase.charAt(i%Lp)+k.charAt(i%Lk), i++);
		return k + String.CRLF + this.arcfourEncode(kp)
	}
	String.prototype.arcfourDecrypt = function arcfourDecrypt(passphrase)
	{	var i = this.indexOf(String.CRLF), k = this.slice(0,i)
		var Lk = k.length, Lp = passphrase.length, L = Math.max(Lk,Lp)
		for (var kp = "", i = 0; i < L; kp += passphrase.charAt(i%Lp)+k.charAt(i%Lk), i++);
		return this.slice(i+String.CRLF.length).arcfourDecode(kp)
	}

	String.prototype.arcfourEncodeHashed = function arcfourEncodeHashed(passphrase,hashfun)
	{	var hfun = String.checkHashFun( (arguments.length < 2 ? "" : hashfun), "arcfourEncodeHashed")
		return (eval("this."+hfun+"()").toBase64()+"|"+this).arcfourEncode(passphrase)
	}
	String.prototype.arcfourDecodeHashed = function arcfourDecodeHashed(passphrase,hashfun)
	{	var hfun = String.checkHashFun( (arguments.length < 2 ? "" : hashfun), "arcfourDecodeHashed")
		var x = this.arcfourDecode(passphrase), m = x.indexOf("|"), h = x.slice(0,m), y = x.slice(++m)
		return {value: y, ok: (eval("y."+hfun+"()").toBase64() == h ? true : false) }
	}
	String.prototype.arcfourEncryptHashed = function arcfourEncryptHashed(passphrase,hashfun,keylen)
	{	var hfun = String.checkHashFun( (arguments.length < 2 ? "" : hashfun), "arcfourEncryptHashed")
		var temp = (eval("this."+hfun+"()").toBase64()+"|"+this)
		return (arguments.length < 3 ? temp.arcfourEncrypt(passphrase) : temp.arcfourEncrypt(passphrase,keylen))
	}
	String.prototype.arcfourDecryptHashed = function arcfourDecryptHashed(passphrase,hashfun)
	{	var hfun = String.checkHashFun( (arguments.length < 2 ? "" : hashfun), "arcfourDecryptHashed")
		var x = this.arcfourDecrypt(passphrase), m = x.indexOf("|"), h = x.slice(0,m), y = x.slice(++m)
		return {value: y, ok: (eval("y."+hfun+"()").toBase64() == h ? true : false) }
	}

// -----------------
// file I/O methods:
// -----------------

	String.prototype.ReadFile = function ReadFile (fso)
	{	var file = fso.GetFile(this)
		var inp  = file.OpenAsTextStream(1,0)	// ForReading,AsciiMode
		var data = inp.Read(file.Size)		// 'ReadAll' does _NOT_ give the correct result!
		inp.Close()
		return data
	}

	String.prototype.CreateFile = function CreateFile (fso, data, overwrite)
	{	var out = fso.CreateTextFile (this, (overwrite ? true : false), false)	// arg3 = false => ascii mode
		if (data.constructor == Array)
			out.Write (data.join(String.CRLF) + String.CRLF)
		else	out.Write (data)						// do _NOT_ use 'WriteLine' !!!
		out.Close()
	}

// ---------------------------------------------------------------------------------------------------------------------
// Finally, there's a bonus!
// The methods below convert an Array of Numbers to a String representation of the Array in JavaScript syntax,
// where each word is truncated to the given number of bits (8, 16, or 32) and represented in '0x' hexadecimal
// numerical notation, e.g. if an Array would contain the values 13, 35, and 65535, then toHex32() returns
// "[0x0000000d,0x00000023,0x0000ffff]", which can be re-interpreted as normal JavaScript code for an Array definition.
// 'toHex$' is intended for internal use only; the 'bits' argument MUST be a multiple of 4, with 4 <= bits <= 32.
// ---------------------------------------------------------------------------------------------------------------------

	Array.prototype.toHex32 = function toHex32() {return this.toHex$(32)}
	Array.prototype.toHex16 = function toHex16() {return this.toHex$(16)}
	Array.prototype.toHex8  = function toHex8 () {return this.toHex$( 8)}

	Array.prototype.toHex$  = function toHex$(bits)
	{	var D = -bits/4, M = 0xffffffff>>>(32-bits), R = M+1, r = "[", i = 0
		for (; i < this.length; r += (i>0?",":"") + "0x" + (R+(this[i++]&M)).toString(16).slice(D) );
		return r + "]"
	}

//* ====================================================================================================================
//* [end of file] hr$binstring.js -- Copyright (c) 2004,2005 Henk Reints (http://henk-reints.nl)
//* --------------------------------------------------------------------------------------------

