/*
Qantas JavaScript API
---------------------

Comments on classes and methods follow the style of Javadoc.

In general methods will be either public or private in their use. As Javascript makes
no distinctions between the two, we simply make it a convention to prefix all private
methods with an underscore "_" character.

The API supports principally the DOM Level 1 Specification as published by the w3c (1998).
The market reach of Microsoft's DOM (as implimented in IE4, IE5, and IE5.5) and Netscape's
DOM (as implimented in NN4), gave reason for this API including support for these DOMs as
well.

Please note that the API does not support the Microsoft Internet Explorer 4.5 for Macintosh DOM
as the DOM implementation was seen as too divergant and buggy.

Archetected (with UML) by Anders Holmgren aholmgren@qantas.com.au
JavaScript by Michael Coxon mcoxon@qantas.com.au
Documentation by Michael Coxon mcoxon@qantas.com.au

Document core.js
----------------

Carries all of the basic components of the API including browser detection, element modelling,
and basic utility classes for size, position, and layout.

The document provides the following utilities to the JavaScript developer:

(a) b - an instance of the Client object (b for browser)
(b) tb - a StrBuf for testing purposes (tb for test String Buffer)
(c) bw - an instance of BufferedWriter for performing document.write()s

(d) getCookieValue(name) - to retrieve a value for the given name
(e) setCookie(name,value) - to make a new cookie of name,value pair
(f) getPageUri() - to return the path information from the URL


*/
/**  Client
 * Provides a set user agent, platform and document dimension properties that represent
 * the client from the point of view of this API. Properties include: ie (Microsoft Internet Explorer),
 * ns (Netscape), and ns6 (Netscape version 6).
 * 					
 * Based on code by Dan Steinman & Christian Bodart
 * http://www.dansteinman.com/dynapi
 *
 *     Interface:
 *         init() - finds the width and height of the document setting docw,doch
 *         isSupported() - returns true if the user agent is ie or ns 4+
 *         reload() - the navigator 4 resize handler, automaticaly fires on resize
 */
 
/**
 *   Constructor:
 *       var b = new Client()
 *           - self initialises
 *     
 *   Properties (most relevant)
 *       ie - is the browser Internet Explorer?
 *       ns - is the browser a Netscape product?
 *       ie4 - is the browser Internet Explorer version 4?
 *       ie5 - is the browser Internet Explorer version 5?
 *       ie55 - is the browser Internet Explorer version 5.5?
 *       iex - any non w3c ie browser of version>=4
 *       ns4 - is the browser Netscape Navigator version 4?
 *       ns6 - is the browser Netscape Navigator version 6?
 *       w3c - either ns6 or ie6, or any non-ie or non-ns browser
 *       win - is the platform of the browser windows?
 * 		mac - is the platform of the browser macintosh?
 * 		docw - overall width of the document (used to position JSComponents)
 * 		doch - overall height of the document (used to position JSComponents)
 */
function Client() {
	var br=navigator.appName
	if (br=="Netscape") this.br="ns"
	else if (br=="Microsoft Internet Explorer") this.br="ie"
	this.ver=navigator.appVersion
	this.v=parseInt(this.ver)
	this.ns=(this.br=="ns" && this.v>=4)
	this.ns4=(this.br=="ns" && this.v==4)
	this.ns6=(this.br=="ns" && this.v==5)
	this.ie=(this.br=="ie" && this.v>=4)
	this.ie4=(this.ver.indexOf('MSIE 4')>0)
	this.ie5=(this.ver.indexOf('MSIE 5')>0)
	this.ie55=(this.ver.indexOf('MSIE 5.5')>0)
    this.ie6=(this.ver.indexOf('MSIE 6')>0)
	var ua=navigator.userAgent.toLowerCase()
	this.ns60=(ua.indexOf("netscape6/6.0")>-1)
	this.ns61=(ua.indexOf("netscape6/6.1")>-1)
	this.ns62=(ua.indexOf("netscape6/6.2")>-1)
	this.moz92=(ua.indexOf("rv:0.9.2")>-1)
	this.moz93=(ua.indexOf("rv:0.9.3")>-1)
	this.moz94=(ua.indexOf("rv:0.9.4")>-1)
	this.moz95=(ua.indexOf("rv:0.9.5")>-1)
	this.moz96=(ua.indexOf("rv:0.9.6")>-1)
	this.win=(ua.indexOf("win")>-1)
	this.mac=(ua.indexOf("mac")>-1)
	// ie6 removed from w3c set because of non-compliance - need to follow this up
    this.w3c=(this.ns6||(!this.ns&&!this.ie))
    this.iex=((this.ie4&&!this.mac)||this.ie5||this.ie55||this.ie6)
}

b = new Client()
CLp=Client.prototype
// Reloads the document, if the dimensions of the page have changed. (NS4 only)
CLp.reload = function(){
//	Varifies that there is a Client.docw property
    if (!this.docw) {
// ...or makes one through Browser.init()
        this.init()
        return
    }
//	tests to see if the netscape-specific properties (innerWidth and innerHeight)
//	vary from the stored values (Client.docw and Client.doch).
	if (innerWidth != this.docw || innerHeight != this.doch)
//	if they do, perform the reload
        self.location.reload()
}

// Stores the current page width and height to properties of the Client object
CLp.init = function() {
	this.docw=(b.iex)?document.body.clientWidth:innerWidth
	this.doch=(b.iex)?document.body.clientHeight:innerHeight
}

// handles onresize event with the reinit() function
onresize = reinit
// performs necessary reinitilisation of the page. The method is fired on a page reload event occurs.
function reinit() {
//	calls JSBrowser.reload() if the browser is netscape 4, because NS4 will need to reload()
//	the page if the document dimensions have changed
    if (b.ns4) b.reload()
//	otherwise calls the base init() method
	else b.init()
}

/**
 * Effectively performs inheritance in JavaScript. Use this to make a pre-existing 
 * "theSuper" a super class of your current "theSub". Read the method as: "theSub" 
 * extends "theSuper" it ties theSub.prototype to theSuper.prototype.
 *
 * Returns theSub.prototype now inherited from theSuper	.
 */
function getSuperProto(theSub,theSuper){
	if(b.ns4)eval(theSub+".prototype.__proto__="+theSuper+".prototype")
	else{
		eval(theSub+".prototype=new "+theSuper+"()")
		eval(theSub+".prototype.constructor="+theSuper)
	}
	return eval(theSub+".prototype")
}

/**  Dimension Object
 *   Utility class for storing instance widths and heights.
 *   this is currently configured to return a zero dimension (0,0)
 *   if negative values are passed to the constructor.
 *   outputs integer values only.
 *     
 *   Constructor:
 *       var d = new Dimension(width,height)
 *     
 *   Properties (most relevant)
 *       w - width of the dimension
 *       h - height of the dimension
 * 
 *     Interface:
 *         none
 *     Usage: 
 *		var d = new Dimension(100,20)
 *		lyr.style.width = d.w
 *		lyr.style.height = d.h
 */
function Dimension(w,h){this.w=(w>0)?parseInt(w):0;this.h=(h>0)?parseInt(h):0}

/**  Name:           Point Object
 *   Description:    Utility class for storing instance positions.
 *                   outputs integer values only.
 *     
 *   Constructor:
 *       var p = new Point(x,y)
 *     
 *   Properties (most relevant)
 *       x - left posion of the point
 *       y - top position of the point
 * 
 *     Interface:
 *         none
 *     Usage: 
 *		var p = new Point(20,30)
 *		lyr.style.left = p.x
 *		lyr.style.top = p.y
 */
function Point(x,y){this.x=parseInt(x);this.y=parseInt(y)}

/**	Name:			StrBuf Object
 *	Description:	Utility class for buffering (assembling) Strings.
 *               	implimentation offers better performance over assembling strings 
 *					with the += operator
 *     
 *	Constructor:
 *		var sb = new StrBuf()
 *     
 *	Properties (most relevant)
 *		array - the string assembly array
 *		end - the declared ending index of the StrBuf
 * 
 *	Interface:
 *		append(String s) - appends s to the StrBuf
 *		toString() - converts the contents of the StrBuf to s String
 *		setLength(int i) - chops the StrBuf to index i
 *         
 *	Usage: 
 *		var sb = new StrBuf()
 *		sb.append("hello")
 *		document.write(sb.toString())
 */

function StrBuf(){this.array=[];this.end=0}
SBp=StrBuf.prototype
SBp.append=function(s){this.array[this.end++]=s}
SBp.toString=function(){return this.array.join("")}
SBp.setLength=function(l){this.end=l}
tb=new StrBuf()
// utility methods for strings. Here mainly because IE wont
// support string.replace(/ /,otherString). It fails to parse
// the / / expression.
function QString(str){this.s=str}
QSp=QString.prototype
QSp.getHead=function(str){
	found=this.s.indexOf(str)
	if(found==-1)return null
	return this.s.substring(0,found)
}
QSp.getTail=function(str){
	found=this.s.indexOf(str)
	if(found==-1)return null
	return this.s.substring(found+str.length,this.s.length)
}
QSp.insertAt=function(insStr,atStr){
	head=this.getHead(atStr)
	tail=this.getTail(atStr)
	if(head!=null&&tail!=null)this.s=head+insStr+atStr+tail
}
QSp.remove=function(str){
	this.replace("",str)
}
QSp.replace=function(repStr,atStr){
	head=this.getHead(atStr)
	tail=this.getTail(atStr)
	if(head!=null&&tail!=null)this.s=head+repStr+tail
}
QSp.replaceAll=function(repStr,atStr){
	this.replace(repStr,atStr)
	this.replace(repStr,atStr)
	this.replace(repStr,atStr)
	this.replace(repStr,atStr)
}

/**	BufferedWriter Object
 * A buffered document writer that wraps the document.write function, providing
 * buffering to improve performance. Writes to document on flush.
 *     
 *	Constructor:
 *		var bw = new BufferedWriter()
 *		self-initialises
 *     
 *	Properties (most relevant)
 *		buffer - a StrBuf object
 * 
 *	Interface:
 *		write() - Commits the String to the document (ns4), or appends it to a buffer.
 *		flush() - Commits the string buffer contents to the document, and clears the buffer.
 *         
 *	Usage: 
 *		bw.write(sb.toString())
 *		bw.write("hello")
 *		bw.flush()
 *
 * switching used on the two methods, because of an undocumented buffering problem
 * with netscape 4. Seems to have something to do with the speed at which strings
 * are written to the document!
 */
function BufferedWriter() {this.buffer=new StrBuf()}
BWp=BufferedWriter.prototype

//	Commits the String to the document (ns4), or appends it to a buffer.
BWp.write=function(s){
	if(b.ns4)document.write(s)
	else this.buffer.append(s)
}

//	Commits the string buffer contents to the document, and clears the buffer.
BWp.flush=function(){
	if(!b.ns4){
		document.write(this.buffer.toString())
		this.buffer=new StrBuf()
	}
}
bw=new BufferedWriter()
var styleRulesString

/**	Style Object
 *	Assembles a style object for a given array of w3c-format style rules.
 *  The object turns these key-value pairs into elements of an associative array.
 *	The support of Netscape Navigator 4 places limitations on the sorts of w3c style
 *	rules that can be supported ubiquotously. In particular, watch out for border 
 *	and background characteristics.
 *
 *	The following rules combination works quite well.
 *     
 *	Constructor (two types of construction, exemplified below):
 *		var stdoff=new Style(".stdoff",
 *		["position","absolute"],
 *		["background-color","#003366"],
 *		["color","#ffffff"],
 *		["display","block"],
 *		["padding-bottom","3px"],
 *		["padding-left","3px"],
 *		["padding-right","3px"],
 *		["padding-top","3px"],
 *		["border-top","1px solid #7b9ab5"],
 *		["border-left","1px solid #7b9ab5"],
 *		["border-bottom","1px solid #000000"],
 *		["border-right","1px solid #000000"],
 *		["border-style","outset"],//must appear after the above 4 lines
 *		["cursor","pointer"],
 *		["font-family","verdana,helvetica,arial"],
 *		["font-size","10px"],
 *		["line-height","normal"],
 *		["font-weight","bold"],
 *		["text-align","center"],
 *		["text-decoration","none"],
 *		["vertical-align","middle"],
 *		["z-index","500"],
 *		["white-space","nowrap"],
 *		["visibility","hidden"]
 *		)
 *		var stdon=new Style(".stdon",
 *		["getPropertiesFrom","stdoff"],		---a[1][0] and a[1][1], respectively
 *		["background-color","#33CCFF"]
 *		)
 *     
 *	Properties (most relevant)
 *		prefix - string, which is either "#", or "."
 *		class - string being the name of the class
 *		theCSS - assembly string for the style rule
 *		theHTML - (NS4 only) assembly string for HTML Div Element styling place-holder
 *		rest are variable in number - based on construction above, properties represent 
 *								the individual style KEYS (LHS of ":"), 
 *								each property is initialised with style VALUES (RHS of ":")
 * 
 *	Interface:
 *		isKey()
 *		makeStyle()
 */

function Style() {
	a=arguments,start=1
	if(a[1][0]=="getPropertiesFrom"){
//		perform inheritance from given class (referred to as ss)
		var ss=eval(a[1][1])
//		iterate through its properties
		for (var i in ss){
			if (i=="makeStyle"||i=="isKey"||i=="prefix"||i=="theClass"||i=="theCSS")continue
//		and set the key-value pairs from it
			this[i]=ss[i]
		}
		start=2
	}
//		iterate through the array for the current style, and set properties
//			replacing all instances of illegal "-" characters with "_"
	for (var i=start;i<a.length;i++){this[a[i][0].replace(/-/g,'_')]=a[i][1]}
//		from the first element in the construction statement, set prefix and class 
//			for the makeStyle() method
	this.prefix = a[0].charAt(0)
	this.theClass = a[0].substring(1)
}
Sp=Style.prototype

// returns true if the property has been defined
Sp.isKey=function(prop){
	for (var i in this) if(i==prop)return true
	return false
}

// Aggregates the output from style objects into a single style
// block, with surrounding STYLE tags. For NN4 the class also aggregates the
// peering Divs. Peering Divs are empty HTML blocks that represent the style rules
// on the page. Netscape requires these for it to incorporate the rules into
// its DOM
function StyleRules(){this.theCSS="";this.theHTML=""}
SRp=StyleRules.prototype

// An aggregation function, implemented with a simple += operator to aggregate
// incomping HTML and CSS strings from each rule defined by the Style.makeStyle() method.
SRp.add=function(theCSS,theHTML){
	this.theCSS+=theCSS
	if(theHTML){this.theHTML+=theHTML}
}

// commits the CSS aggregate to the document.
SRp.writeCSS=function(){bw.write('<style>\n'+this.theCSS+'</style>\n')}

// commits the HTML Peering Div aggregate to the document.
SRp.writeHTML=function(){if(this.theHTML)bw.write(this.theHTML)}

// used in conjunction with an array of Styles (a) to assemble a style block
// and placeholder HTML DIV Elements (for NN4).
styleRulesString=new StyleRules()
function styleCompiler(a){
	for(var i=0;i<a.length;i++){
		a[i].makeStyle()
		styleRulesString.add(a[i].theCSS,a[i].theHTML)
	}
}
/**  JSComponent
 * JSComponent is the fundamental component of the JavaScript Presentation API. It uses
 * a positionable block (as per the W3C definition of a Block) as its peer component
 * in the DOM. Manipulation of the JSComponent results in manipulation of the peer object. 
 * It contains methods to create and attach to the peer object. Creation is achieved
 * by writing the html (Styles & DIVs) code into the document at the appropriate places.
 * It exposes peer originating mouse events to registered listeners
 * (via the addMouseListener method).
 * 					
 * Based on UML by Anders Holmgren
 * 
 *     Constructor:
 *         var myDivID = new JSComponent(myDivID+"Div") 
 * 		
 * 		principally called by:
 * 		Widget API components such as the Qantas Menu Generator
 *     
 *     Properties (relevant)
 *      elm - the peer
 *      css - referring to the peer's CSS properties such as background color
 * 		doc - referring to the peer's document properties such as contained images
 * 		x - the left position of the layer
 * 		y - the top position of the layer
 * 		w - the width of the layer
 * 		h - the height of the layer
 * 
 *     Interface:
 * 			jscCTR()
 * 			getName()
 * 			getSize()
 * 			getMinSize()
 * 			getMaxSize()
 * 			getPrefSize()
 * 			getMinContentSize()
 * 			getMaxContentSize()
 * 			getPrefContentSize()
 * 			writeHTML()
 * 			addChild()
 * 			setParent()
 * 			setLayout()
 * 			attachPeer()
 * 			validate()
 * 			layout()
 * 			getRelLocation()
 * 			getAbsLocation()
 * 			setUI()
 * 			setVisible()
 * 			fireEvent()
 * 			addMouseListener()
 * 
 * 	Bridged Interface (browser specific implementations)
 * 			
 * 			setAbsLocation()
 * 			setRelLocation()
 * 			setSize()
 * 			setStyle()
 * 			setImage()
 * 			writeInto()
 * 			writeCSS()
 * 			init()
 * 			captureMouseEvents()
 */
function JSComponent(id,frame,style,contentHTML) {
	this.jscCTR(id,frame,style,contentHTML)
}
Cp=JSComponent.prototype

/**
 * Constructor for the JSComponent, which:
 * (a) sets a group of properties from its arguments
 * (b) initialises the children array
 * (c) calls dimensionInit() if there is contentHTML
 n		decoupling JS class declaration and JS object construction
 n		items in the constructor, need not be implemented in the inherited classes.
 */
Cp.jscCTR=function(id,frame,style,contentHTML) {
	this.frame = frame
	this.style=style||null
	this.id = (id)?id:""
	this.children=[]
	this.contentHTML=contentHTML||""
	if(contentHTML)this._dimensionInit()
}

// returns the name of the JSComponent, which is derived from the JSComponent.id
// we name each JSComponent, by removing the substring "Div" from the id.
Cp.getName=function(){return this.id.substring(0,this.id.indexOf("Div"))}

// returns a Dimension of the JSComponent's size
Cp.getSize=function(){return this.size}

// returns a Dimension of the JSComponent's minimum size
// either the JSComponent has child elements and therefore has a layout manager,
// and we determine the size from the layout manager
// or the JSComponent has content (such as images and text) and we determine the size from that
Cp.getMinSize=function(){return (this.layoutMgr)?this.layoutMgr.minSize(this):this.getMinContentSize()}

// returns a Dimension of the JSComponent's maximum size
// either the JSComponent has child elements and therefore has a layout manager,
// and we determine the size from the layout manager
// or the JSComponent has content (such as images and text) and we determine the size from that
Cp.getMaxSize=function(){return (this.layoutMgr)?this.layoutMgr.maxSize(this):this.getMaxContentSize()}

// returns a Dimension of the JSComponent's preferred size
// either the JSComponent has child elements and therefore has a layout manager,
// and we determine the size from the layout manager
// or the JSComponent has content (such as images and text) and we determine the size from that
Cp.getPrefSize=function(){return (this.layoutMgr)?this.layoutMgr.prefSize(this):this.getPrefContentSize()}

// returns the minimum size for this JSComponent, based on its HTML contents
Cp.getMinContentSize=function(){return this.minContentSize}

// returns the maximum size for this JSComponent, based on its HTML contents
Cp.getMaxContentSize=function(){return this.maxContentSize}

// returns the preferred size for this JSComponent, based on its HTML contents
Cp.getPrefContentSize=function(){return this.prefContentSize}

/**
 * sets the minimum, maximum, and preferred content size for this JSComponent,
 * based on its HTML contents, their styling attributes, and presence of images
 i		we have tightened the logic to present simple defaults for minContentSize
 i		and maxContentSize, whereas we should set them independently at each branch.
 */
var MAX_SIZE=new Dimension(1000000,1000000)
Cp._dimensionInit=function(){
	if(this.prefContentSize)return
	var w,h,cs=this.contentHTML
/*	The <img...> case is not handled by the API, so the first choice presented has 
	been removed for the moment.
	if(cs.indexOf("<img")>-1&&cs.lastIndexOf("<img")==cs.indexOf("<img")){
		endw=startw=cs.indexOf("width")+7
		endh=starth=cs.indexOf("height")+8
		var csl=cs.length
		for (var i=startw;i<csl;i++){
			if(cs.charAt(i)=='"')break
			endw++
		}
		for (var i=starth;i<csl;i++){
			if(cs.charAt(i)=='"')break
			endh++
		}
		w=parseInt(cs.substring(startw,endw))
		h=parseInt(cs.substring(starth,endh))
		mx=this.prefContentSize
	}else 
*/	
	if(this.style&&cs){
		w=this._textLength(this)
		h=this._textHeight(this)
	}else if(this.elm!=null){
		w=this._getContentW()
		h=this._getContentH()+parseInt(this.style.font_size)
	}
	this.prefContentSize=new Dimension(w,h)
	this.maxContentSize=MAX_SIZE
	this.minContentSize=this.prefContentSize
}


// used by JSComponent.dimensionInit()
// function returns probable length of displayed text in pixels, given text string,
// and styling attributes
// the numbers in the array are based on the verdana font, taken at 10px, and
// may be inaccurate for other fonts and sizes. To ensure adequate display, some
// exaggeration is made.
ua=[0.4,0.4,0.43,0.72,0.62,1.02,0.62,0.22,0.42,0.42,0.62,0.72,0.32,0.41,0.31,0.41,// !"#$%&'()*+,-./
0.62,0.62,0.62,0.62,0.62,0.62,0.62,0.62,0.62,0.62,//numbers
0.41,0.41,0.71,0.71,0.71,0.51,0.91,//:;<=>?@
0.62,0.62,0.72,0.72,0.52,0.52,0.72,0.72,0.42,0.42,0.62,0.52,0.83,0.62,0.82,0.62,0.82,0.72,0.62,0.62,0.72,0.62,1.05,0.62,0.62,0.62,//uppercase
0.41,0.41,0.41,0.71,0.51,0.51,//[\]^_`
0.61,0.61,0.42,0.61,0.61,0.31,0.61,0.61,0.21,0.21,0.61,0.21,1.03,0.61,0.61,0.61,0.61,0.31,0.51,0.31,0.61,0.51,0.61,0.61,0.51,0.51,//lowercase
]
Cp._textLength=function(){
	var c=this.contentHTML,s=this.style,sp=0,st=parseInt(s.font_size),f=1.05,fnt=s.font_family
//	We are only considering three font displays in calculating size
//	clearly, if comic sans or courier is used, the display will likely self-destruct.
	if(fnt.indexOf("arial")==0)st=st*0.8
	else if(fnt.indexOf("helvetica")==0)st=st*0.8
	else if(fnt.indexOf("times")==0)st=st*0.8
	f=(b.ns4&&b.mac)?1.4:1.05
	for(var i=0; i<c.length;i++){
		if(c.indexOf("&gt;")==i){
			sp+=0.41*st+f;i=i+4
		}
// support for other character entities
//		else if(c.indexOf("&ndash;")==i){
//			sp+=0.41*st+f;i=i+7
//		}
//		else if(c.indexOf("&shy;")==i){
//			sp+=0.41*st+f;i=i+5
//		}
//		else if(c.indexOf("&brvbar;")==i){
//			sp+=0.41*st+f;i=i+8
//		}
		else if(c.indexOf("&amp;")==i){
			sp+=0.41*st+f;i=i+5
		}
		else if(c.indexOf("&nbsp;")==i){
			sp+=0.41*st+f;i=i+6
		}
		else sp+=ua[c.charCodeAt(i)-32]*st+f
	}
	var br=(b.ns4||b.iex)?1.03:1
	if(b.ns4&&b.mac)br=0.98
	if(s.font_weight=="bold")return parseInt(sp*1.08*br)+parseInt(s.padding_left)+parseInt(s.padding_right)
	else return parseInt(sp*1.05*br)+parseInt(s.padding_left)+parseInt(s.padding_right)
}

// used by JSComponent.dimensionInit()
// function returns probable height of displayed text in pixels, given styling attributes
Cp._textHeight=function(){
	s=this.style
	bt=(s.border_top)?parseInt(s.border_top.substring(0,1)):0
	bb=(s.border_bottom)?parseInt(s.border_bottom.substring(0,1)):0
	pt=(s.padding_top)?parseInt(s.padding_top):0
	pb=(s.padding_bottom)?parseInt(s.padding_bottom):0
	//fudge factor added to end of next line
	fs=parseInt(s.font_size)+2
	return bt+bb+pt+pb+fs
}

// Writes the JSComponent and its children to the buffered document writer
Cp.writeHTML=function(){
//	add style attribute if the JSComponent has a Style object
	var cls=(this.style)?' class="'+this.style.theClass+'"':''
	bw.write('\n<div id="'+this.id+'"'+cls+'>'+this.contentHTML)
	for (var i=0; i<this.children.length;i++){
		this.children[i].writeHTML()
	}
	bw.write("</div>")
}

// sets the specified JSComponent as a child of this JSComponent
Cp.addChild=function(child){
	this.children[this.children.length]=child
	child.setParent(this)
}

// sets the specified JSComponent as a parent of this JSComponent
Cp.setParent=function(parent){this.parent=parent}

// sets the layout manager for this JSComponent to the object supplied by "mgr"
Cp.setLayout=function(mgr){this.layoutMgr=mgr}

// causes the jsc and its children to locate and associate themselves with their
// peer elements in the DOM.
Cp.attachPeer=function(){
//	assemble the component-to-element map if not already done so.
//	This will be an exclusive map of only those elements that have
//	JSComponents modelled after them.
	if(!cToE.made)cToE.make()
	this.init(cToE.getElm(this.id))
	for(var i=0;i<this.children.length;i++){
		this.children[i].attachPeer()
	}
	if (this.mouseListeners) this.captureMouseEvents()
}

// validates the layout of the JSComponent and its children. After this call,
// the JSComponent and its children will be layed out.
Cp.validate=function(){
	if (this.valid) return
	this.layout()
	for (var i=0;i<this.children.length;i++){
		this.children[i].validate()
	}
	this.valid=true
}

// lays out the children of this component.
Cp.layout=function(){if(this.layoutMgr)this.layoutMgr.layout(this)}

// returns the location of the JSComponent, relative to its parent
Cp.getRelLocation=function(){return this.relLocation}

// returns the location of the JSComponent, relative to the page origin
Cp.getAbsLocation=function(x,y){
	x=(x)?x+this.relLocation.x:this.relLocation.x
	y=(y)?y+this.relLocation.y:this.relLocation.y
	if(this.parent){
		p=this.parent.getAbsLocation(x,y)
		x=p.x
		y=p.y
	}
	return new Point(x,y)
}

// sets the location to the new value.
Cp.setAbsLocation=function(pt){
	x=pt.x;y=pt.y
	if(this.parent){
		ppt=this.parent.getRelLocation()
		p=new Point(pt.x-ppt.x,pt.y-ppt.y)
		this.setAbsLocation(p)
	}
	this.setRelLocation(pt)
}

// UI components are responsible for the look and feel of the component
// and may add children and layout managers to the component
Cp.setUI=function(ui){ui.initUI(this,this.parent)}

// sets the visibility of the JSComponent to that of its argument
Cp.setVisible=function(state) {
	this.visibility=(state)?this.VISIBLE_INHERIT:this.VISIBLE_HIDDEN
	if (this.css!=null) {
		this.validate()
		this.css.visibility = this.visibility
	}
}

// fire events for all listed MouseListeners.
Cp.fireEvent=function(e) {
	for (var i=0;i<this.mouseListeners.length;i++) this.mouseListeners[i].handleEvent(e)
}

// adds a listener of mouse events
Cp.addMouseListener=function(listener) {
	if (!this.mouseListeners) this.mouseListeners=[]
	for (var i in this.mouseListeners) if (this.mouseListeners[i]==listener) return
	this.mouseListeners[this.mouseListeners.length]=listener
}

// Models a mouse event to retaining event information beyond the chain
// of execution.
MouseEvent=function() {}
MouseEvent.prototype.mouseEventCTR=function(src,e) {
	this.src=src
	this.type=e.type
	this.orig=e
}
 
/**
 * Function representation of the JSCImplFactory, part of an abstract factory
 * pattern. This is implimented differently in JavaScript in that it decides
 * which impl module (js file) to load based on the browser rather than creating
 * the impl object directly. As such this is just a function, rather than a class
 */
function jscomponentImp(path){
	var o='<script language="JavaScript" src="'+path+'jsc_',c='.js"></script>',s
	s=(b.ns4)?o+'ns'+c:(b.iex)?o+'ie'+c:o+'w3c'+c
	document.write(s)
}

/** TableLayoutManager
 *  Author Ben Bradey
 *  bbradey@qantas.com.au
 *  A layout manager that lays out the children of
 * 	the JSComponent in a Table.
 * 	Various aspects of this table layout can be configured.
 *
 *     Constructor:
 *         var tableLayoutManager = new TableLayoutManager(b)
 *
 * 		principally called by:
 * 		ComponentUI
 *
 *     Properties (relevant)
 *              s - given dimension of parent
 *              r - number of rows .. enter false if you want to be dominated by cols.
 *              c - number of cols
 *              st - struts between cells given as dimension both horizontal and vertical
 *              rhn - cells in a row have the same height true, given number or array for diff. row heights.
 *              rwn - cells in a row have the same width
 *              rsn - cells in a row have same width && height && width==height
 *              cwn - cells in a row have the same width true, given number or array for diff. row heights.
 *              chn - cells in a col have the same height
 *              csn - cells in a col have same width && height && width==height
 *              cs - cells in a col have same width && height && width==height
 *              fw - cells in all rows fill width of parent
 *              fh - cells in all cols fill height of parent
 *				lcf - last cell fill
 *
 *     Interface:
 * 		layout()
 * 		minSize() - the minimum size the layout is allowed to be
 *		maxSize() - the maximum size the layout is allowed to be
 *		prefSize() - the size the layout will be if left to its own devices
 * 		isFatKid()
 *
 *
 */

function TableLayoutManager(s,r,c,st,rhn,rwn,rsn,cwn,chn,csn,cs,fw,fh,lcf){
	if(s)this.s=s                       //given dimension of parent
	this.r=r                            //prescribed number of rows
	this.c=c                            //prescribed number of columns
	if(st)this.struts=st				//Dimension representing the gap between components
	else this.struts=new Dimension(0,0) 
	this.RHNormalise=rhn                //given as boolean,integer or array
	this.isRWNormalise=rwn              //given as boolean
	this.isRSNormalise=rsn              //given as boolean
	this.CWNormalise=cwn                //given as boolean,integer or array
	this.isCHNormalise=chn              //given as boolean
	this.isCSNormalise=csn              //given as boolean
	this.fillWidth=fw                   //given as boolean
	this.fillHeight=fh                  //given as boolean
	this.isCellSquare=cs                //given as boolean
	this.lCFill=lcf						//given as boolean
}
TLMp=TableLayoutManager.prototype

/*
 * lays out the children of this component
 * if(this.s)tms=this.s   table mandate size
 * fatKids=0              number of fat kids
 * this.rh=0              row height
 * this.cw=0              col width
 * this.rowCellCount=1    counts the cells in a row
 * this.cellCount=0       which cell the loop is up too
 * this.gh=0              greatest height of all cells given
 * this.gw=0              greatest width of all cells given
 * this.rowNo=0           the row number
 * this.rowHeight=[]      an array of all the rows heights
 * this.rowCellWidth=[]   an array of the width of each cell in the row
 * this.rowArray=[]       2 dimensional array of the rows and each cell in that row
 * this.rowPrefWidth=[]   array of the preferred width of each row
 * this.rowFatKids=[]     array of the number of fats kids in each row
 * this.colArray=[]       2 dimensional array of the cols and each cell in that col
 * this.colFatKids=[]     array of the number of fats kids in each col
 * this.colPrefHeight=[]  array of the preferred height of each col
 */

TLMp.layout=function(c){
	if(c.getSize())this.s=c.getSize()//may return null
	else if(!this.s)this.s=new Dimension(0,0)//true if this.s is 0,false, null or undefined
	// at this point, this.s is always defined
	fatKids=0
	this.rh=0,this.cw=0
	this.rowCellCount=1
	this.cellCount=0
	this.gh=0,this.gw=0
	this.rowNo=0
	this.rowHeight=[]
	this.rowCellWidth=[]
	this.rowArray=[]
	this.rowArray[0]=[]
	this.cRArray=[]
	this.cRArray[0]=[]
	this.rowPrefWidth=[]
	this.rowFatKids=[]
	this.colArray=[]
	this.colFatKids=[]
	this.colPrefHeight=[]
	this.ccl=c.children.length
	this.x=0,this.y=0
	//if the number of rows is prescribed, the number of columns is overridden
	if(this.r && this.r!=false)this.c=Math.floor(this.ccl/this.r)
	
	// This is the initial layout with minimal rules. It just thows the cells into line and
	// starts a new row when needed. Also sets up the rowArray and colArray
	// Gets the greatest height of each row and sets up the rowHeight array according to the rules
	// The number of fat kids in each row
	for(var i=0;i<this.ccl;i++){
		this.childPlaced=false
		// must use a member for the current child, because of a scoping issue when we go down
		// into this layout again in the call to getPrefSize()
		this.cci=c.children[i]
		/*if parent size is given and the y coord + the height of the new row is greater than the parent height: alert*/
	    /*if(this.s && (y+this.cci.getPrefSize().h>this.s.h))(alert('The size of the parent Div is too small'))*/
		
		// basically, this if block will process and finalize the current row
		// this.c will be defined when rows are prescribed
		// has the row cell count got to the prescribed column limit?
		var hasReachedColLimit=(this.rowCellCount==this.c)
		// the current child is too big to fit in the remaining space on the row (-5px) (and given a prescribed size)
		var hasNoRoomInRow=(this.s.w<(this.cci.getPrefSize().w+this.struts.w+this.x))
		// have we got to the last child? In which case we just want to process the previous row
		var isLastChild=((this.cellCount+this.rowCellCount)==this.ccl)
		// if the last condition is met, then we must add the candidate before processing
		if(hasNoRoomInRow && !isLastChild && !hasReachedColLimit){
			this.rowCellCount--
			i--
		}
		this.cci.setSize(this.cci.getPrefSize())
		if(this.s.w==0)this.s=this.cci.getPrefSize()
		if( hasReachedColLimit || hasNoRoomInRow || isLastChild ){
		// row is complete - start next row
			if((hasReachedColLimit || isLastChild) && !hasNoRoomInRow){				
		// news and offers picked it up
				this.cci.setRelLocation(new Point(this.x,this.y))
				this.x+=this.cci.getSize().w+this.struts.w// something happens here that blows x out
				this._setAsLargestChild(this.cci)
				this.childPlaced=true
			}
			
			// so we are positioning the item on the basis of a carriage return
			// new row
			this.rowArray[this.rowNo]=[]
			
			// process the previous row???
			// start fatkid count at 0
			var u=0
			// start the preferred width count at 0
			var rpw=0  //rowPrefWidth
			for(var j=0;j<this.rowCellCount;j++){
				//get each child in the row...
				this.cc=c.children[this.cellCount]
				
				rn=this.rowNo
				rpw=rpw+this.cc.getPrefSize().w
				
				//if we are in columns mode, we set the current child to it
				if(this.c){
					// new col
					if(rn==0)this.colArray[j]=[]
					// the 2nd lvl array colArray[j][rownumber]=currentCell
					this.colArray[j][rn]=this.cc
				}
				// otherwise, we set it to the row array
				// the 2nd lvl array rowArray[rownumber][j]=currentCell
				this.rowArray[rn][j]=this.cc
				// increment the cellCount
				this.cellCount++
				this.v=false
				if(this.isFatKid(this.cc))u++;this.rowFatKids[rn]=u
			}
			this.rowPrefWidth[rn]=rpw
			// re-initialise cell count for the new row
			this.rowCellCount=0

      /*This will set up the height of the rows*/
	  		// further process
			if(this.RHNormalise || this.isCellSquare){
				if(this.RHNormalise==true)this.rh=this.gh  /*sets height of the row at the max *if row height is set to one it will trigger as if true**/
				else if(this.RHNormalise>0)this.rh=this.RHNormalise /*sets the row height at a given figure*/
				else if(this.RHNormalise[0])this.rh=this.RHNormalise[this.rowNo] /*sets the row height at a figure specified for all rows from an array.*/
			}else this.rh=this.gh
			this.rowHeight[this.rowNo]=this.rh /*add the row height to the array of row heights for later use*/
			this.y=this.y+this.rh+this.struts.h /*set the start pos of the new row*/
			this.gh=0 /* reset the max height for the new row*/
			this.x=0 /* reset the x coord. to start new row*/
			if(this.isRWNormalise || this.isRSNormalise)this.rowCellWidth[this.rowNo]=this.gw
			this.gw=0
			this.rowNo++   /*increase the row number, move to next row*/
		}
		if(this.childPlaced==false){
			this.cci.setRelLocation(new Point(this.x,this.y))
			// increment x
			this.x+=this.cci.getSize().w+this.struts.w// something happens here that blows x out
			// set the size of the largest component in the row to be the current child
			// this will be set to current child if there isn't another, larger child
			// already in there.
			this._setAsLargestChild(this.cci)
			this.childPlaced=true
		}
		this.rowCellCount++
		this.cRArray[0][i]=this.cci
	}//end of for
	
	
	this.done=true
	// this.colWidth=[]
	// this.colCellHeight=[]
	// The above arrays are initialised here to save processing if the rules are not met

	// This runs through the cols and sets up the colWidth array and the colCellHeight array
	// according to the rules.
	// Also sets up the colPrefHeight for the fillHeight rule
	// The number of fat kids in each col is also calc.
	if(this.CWNormalise || this.isCHNormalise ||this.isCSNormalise || this.isCellSquare || this.fillHeight){
		this.colWidth=[]
		this.colCellHeight=[]
		for (var i=0;i<this.colArray.length;i++){
			var u=0
			var cphi=0
			for (var j=0;j<this.colArray[i].length;j++){
				cij=this.colArray[i][j]
				cphi=cphi+cij.getPrefSize().h
				this._setAsLargestChild(cij)
				this.v=true
				if(this.isFatKid(cij))u++;this.colFatKids[i]=u
			}
			this.colPrefHeight[i]=cphi
			if(this.CWNormalise==true || this.isCSNormalise || this.isCellSquare)this.cw=this.gw //sets height of the row at the max *if row height is set to one it will trigger as if true*
			else if(this.CWNormalise>0)this.cw=this.CWNormalise //sets the row height at a given figure
			if(this.isCHNormalise || this.isCSNormalise)this.ch=this.gh //sets height of the row at the max *if row height is set to one it will trigger as if true*
			this.colCellHeight[i]=this.ch
			this.colWidth[i]=this.cw
			this.gw=0
		}
		if(this.CWNormalise[0])this.colWidth=this.CWNormalise
    }


	// This is the final interation of the layout.
	// Using all the rules and the obtained data from the above loops,
	// its sets the width and height of the cells
	// as well as the x and y cords of the cells.
	this.x=0,this.y=0
	ral=this.rowArray.length
	cal=this.colArray.length
	tcw=this.colWidth
	tcch=this.colCellHeight
	trh=this.rowHeight
	cph=this.colPrefHeight
	rpw=this.rowPrefWidth
	rh=0
		 
    for(var i=0;i<ral;i++){
      var yheight=0
      rai=this.rowArray[i]
      rcw=this.rowCellWidth[i]
      for (var j=0;j<rai.length;j++){
	if(tcch)cch=tcch[j]
        if(tcw){
          cw=tcw[j]
          var n=0
	  if(this.fillWidth && this.CWNormalise){
            for(var k=0;k<tcw.length;k++){
              n=n+tcw[k]
            }
            this.rowPrefWidth[j]=n
          }
        }
        if(this.RHNormalise){
          rh=trh[i]
          var m=0
          if(this.fillHeight){
            for(var k=0;k<trh.length;k++)m=m+trh[k]
            this.colPrefHeight[j]=m
          }
        }
        else rh=rai[j].getPrefSize().h
        if(this.isRWNormalise){
        width=rcw
        this.rowPrefWidth[i]=cal*width
        }else width=rai[j].getPrefSize().w
	if(tcch && tcch[0])height=cch
        else height=rh
        if(tcw && tcw[0])width=cw
        if(this.isCellSquare){ /*if the cells are square then the width and height of each cell = the max width/height of largest cell*/
          width=Math.max(this.maxSize.w,this.maxSize.h)
          height=width
          this.colPrefHeight[j]=width*ral
          this.rowPrefWidth[i]=height*cal
        }
        else if(this.isRSNormalise){ /*width and height of this cell = the max width/height of the largest cell in this row*/
          width=Math.max(rcw,rh)
          height=width
        }
        else if(this.isCSNormalise){ /*width and height of this cell = the max width/height of the largest cell in this col*/
          height=Math.max(cch,cw)
          width=height
        }
        if(this.rowFatKids[i]>0){
		snackSizew=(this.s.w-this.rowPrefWidth[i]-(this.struts.w*(cal-1)))/this.rowFatKids[i]
		}
		else snackSizew=0
        if(this.colFatKids[j]>0)snackSizeh=(this.s.h-this.colPrefHeight[j]-(this.struts.h*(ral-1)))/this.colFatKids[j]
        else snackSizeh=0
		
        if(this.fillWidth){
          this.isFatKid(rai[j])?width=width+snackSizew:width
		}
        if(this.fillHeight){
          this.isFatKid(rai[j])?height=height+snackSizeh:height
        }
		if(this.lCFill && rai.length-1==j && ral-1==i){
			if(this.rowArray[i].length>1 && ral>1)width=this.rowPrefWidth[i-1]-this.x
		}
        if((j==0 || rai.length-1==j) && (rai[j].id.indexOf("separator")>-1 || rai[j].id.indexOf("Separator")>-1) && ral-1!=i)rai[j].children[0].contentHTML="&nbsp;"
        if(j==(rai.length-1) && this.fillWidth)width=this.s.w-this.x /*Last child to fill up the gap*/
        if(yheight<height)yheight=height
        rai[j].setSize(new Dimension(width,height))/*reset the size according to the rules*/
        rai[j].setRelLocation(new Point(this.x,this.y)) /*set the new location according to the size*/		
		if(j!=(rai.length-1))this.x+=width+this.struts.w
      }
      this.y=this.y+yheight+this.struts.h
      this.x=0  /* reset the x coord. to start new row*/  
	}
}
	
/**
 * Appears to set c as the current largest child component
 * since gh=greatest height, and gw=greatest width
 */
TLMp._setAsLargestChild=function(c){
	// sets up two variables, the max width and max height of provided cells
	// Dimesion maxSize (max size of all known cells) initialised, if all the cells have to be square
	var a=c.getPrefSize().h
	var b=c.getPrefSize().w
	if(this.gh<a)this.gh=a
	if(this.gw<b)this.gw=b
	if(this.isCellSquare)this.maxSize=new Dimension(this.gw,this.gh)
}

/**
 * returns true if the child JSComponent is capable of gobbling up extra stack space
 */
TLMp.isFatKid=function(c){
  if(this.v)return (c.getMaxSize().w>c.getPrefSize().w)
  else return (c.getMaxSize().h>c.getPrefSize().h)
}
// mw=final greatest min Width of all rows.
// mh=final greatest min Height of all rows.
// mmh=current greatest height for the current row.
// mmw=current greatest width for the current row.
TLMp._sizeGetter=function(cat,c){
	if(!this.done)this.layout(c)
	mw=0,mh=0,mmh=0,mmw=0
	rn = this.rowNo
	for (i=0;i<rn;i++){
		for(j=0;j<this.rowArray[i].length;j++){
			s=eval("this.rowArray[i][j].get"+cat+"Size()")
			mmh=(mmh<s.h)?s.h:mmh
			mmw+=s.w
		}
		mh+=mmh
		mmh=0
		mw=(mw<mmw)?mmw:mw
		mmw=0
	}
	mw+=(this.rowArray[rn-1].length-1)*this.struts.w
	mh+=(rn-1)*this.struts.h
	xx = new Dimension(mw,mh)
	return new Dimension(mw,mh)
}

/**
 * returns minimum size of the stack given the stack's outermost JSComponent
 */
TLMp.minSize=function(c){
	if(!this._minSize)this._minSize=this._sizeGetter("Min",c)
	return this._minSize
}

/**
 * returns maximum size of the stack given the stack's outermost JSComponent
 */
TLMp.maxSize=function(c){
	if(!this._maxSize)this._maxSize=this._sizeGetter("Max",c)
	return this._maxSize
}
/**
 * returns preferred size of the stack given the stack's outermost JSComponent
 */
TLMp.prefSize=function(c){
	if(!this._prefSize)this._prefSize=this._sizeGetter("Pref",c)
	return this._prefSize
}
/*
JSComponentUI interface
function JSComponentUI(){}
UIp=JSComponentUI.prototype
UIp.initUI=function(jsc){}
*/
function PopWindow(){
	a=arguments;this.name=a[0];this.orig=a[1];this.url=a[2];p=""
	if(a[3])p+="width="+a[3]+","
	if(a[4])p+="height="+a[4]+","
	if(a[5])p+="location="+a[5]+","
	if(a[6])p+="menubar="+a[6]+","
	if(a[7])p+="resizable="+a[7]+","
	if(a[8])p+="scrollbars="+a[8]+","
	if(a[9])p+="status="+a[9]+","
	if(a[10])p+="toolbar="+a[10]+","
	if(a[11])p+=(b.ie)?"top="+a[11]+",":"screenY="+a[11]+","
	if(a[12])p+=(b.ie)?"left="+a[12]:"screenX="+a[12]
	if(p.lastIndexOf(",")>p.length-2)p.length=p.length-1
	this.prop=p
}
PWp=PopWindow.prototype
PWp.launch=function(){
  this.obj=open(this.url,this.name,this.prop)
  this.opener=(this.obj.opener==null)?window:this.obj.opener
  this.opener.name=this.orig
}
// Utility to return the value of a cookie with the given name or null if it
// does not exist
function getCookieValue(n) {
    cks=document.cookie
    i=cks.indexOf(n)
    if(i==-1)return null
    start=i+n.length+1
    end=cks.indexOf(';',start)
    if(end==-1)end=cks.length
    return unescape(cks.substring(start,end))
}
// Utility to set the value of a cookie with the given name
function setCookie(n,v) {
    briansUniqueStringName=n+"="+escape(v)+"; path=/"
    document.cookie=briansUniqueStringName
}
//utility to set the cookie with not only the name and value 
//but also the expiry date for this cookie
function setExpireCookie(n,v,days){
     var expires=""
     if(days) {
       var date = new Date()
       date.setTime(date.getTime()+(days*24*60*60*1000))
       expires = "; expires="+date.toGMTString()
     }
     else expires = ""
     document.cookie=n+"="+escape(v)+expires+"; path="
 }
//utility to erase the cookie given a cookie name, 
//The idea is just to create / call the same cookie
//with the date before.
function eraseCookie(n){
    var v=getCookieValue(n)
    setExpireCookie(n,v,-1)
}
// utility to return the partial page uri (i.e without the http & domain name)
function getPageUri() {
    var pu=document.location.toString()
    var i=pu.indexOf("//")
    i=pu.indexOf("/", i+2)
    pu=pu.substring(i)
    return pu
}
function init() {}