
/******************************************************************************\
 * 
 * LNWebLib Core
 * -----------------------------------
 *
 * Namespace:  LNWebLib
 *
 * Interfaces: LNWebLib.Core
 *
 * Classes:    None
 *
 * Properties: Core.version
 *             Core.modules (internal)
 *
 * Methods:    
 * 
\******************************************************************************/
 

/* Declare LNWebLib as global object for other modules */
var LNWebLib = {};


/* Declare LNWebLib.Core module */
LNWebLib.Core = {};

/******************************************************************************\
 * LNWebLib version number. Past versions:
 *       1 - First version
\******************************************************************************/
LNWebLib.Core.version = 1;


/******************************************************************************\
 * Array of current LNWebLib modules
\******************************************************************************/
LNWebLib.Core.modules = [];
                        

/******************************************************************************\
 * Decare new module.
 * 
 * in:  moduleName  - Name of the module being declared.
 * 
 *      finalise    - Optional paramter; a function which will be called on window 
 *                    unload.
 * 
 * out: undefined 
\******************************************************************************/
LNWebLib.Core.declareModule = function( moduleName, parentModule, initialise, finalise )
{
    if( !parentModule )
    {
        LNWebLib[ moduleName ] = {};
        LNWebLib.Core.modules.push( moduleName );
    }
    else
    {
        LNWebLib[ parentModule ][ moduleName ] = {};
        LNWebLib.Core.modules.push( parentModule + '.' + moduleName );
    }
    
    if( initialise )
    {
        LNWebLib.Event.addEventListener( window, 'load', LNWebLib[ moduleName ].initialise );
    }

    if( finalise )
    {
        LNWebLib.Event.addEventListener( window, 'unload', LNWebLib[ moduleName ].finalise );
    } 
}

/******************************************************************************\
 * Decare new JavaScript API extension.
 * 
 * in:  extension name
 * out: undefined 
\******************************************************************************/
LNWebLib.Core.declareAPIExtension = function( obj )
{
    LNWebLib.Core.modules.push( obj + '.LNWebLib' );
}
/******************************************************************************\
 * 
 * Array extensions
 * -----------------------------------
 * 
 * Methods:     average
 * 				foreach
 *              indexof
 *              intersection
 *              iterate
 *              min
 *              max
 * 				randomElement
 * 				randomise
 * 				removeElements
 * 				rotateBackwards
 *              rotateForwards
 *              sortNumbers
 *              sum
 * 				unique
 * 
\******************************************************************************/


/* Declare the Array.LNWebLib extension */

LNWebLib.Core.declareAPIExtension( 'Array' );


/******************************************************************************\
 * Averages all the values in an array
 * 
 * in:  n/a
 * 
 * out:	the average value of the array
\******************************************************************************/
Array.prototype.average = function( ) 
{
    var output=0;
	
	if( this.length > 0)
	{
		for( var i = 0, total = 0; i < this.length; i++ )
	    {    
	    	if( typeof this[ i ] == 'number' )
	        	total+=this[ i ];
	    }
		
		output=total/this.length;
	}
    
    return output;
};

/******************************************************************************\
 * Call a function on each item in the array.
 * 
 * in:  iterator - function to be called on each item
\******************************************************************************/
Array.prototype.foreach = function( iterator ) 
{
    for( var i = 0; i < this.length; i++ )
    {    
        iterator( i, this[ i ] );
    }
};

/******************************************************************************\
 * Check for the presence of an item in the array.
 * 
 * in:  item - the item being searched for
 * 
 * out: item if present and -1 if not
\******************************************************************************/

if (!Array.prototype.indexOf)
{
    Array.prototype.indexOf = function( item ) 
    {
        for ( var i = 0; i < this.length; i++ )
        {
            if ( this[ i ] == item ) return i;
        }
        return -1;
    };
}

/******************************************************************************\
 * Intersect the array with another and return the resulting array 
 * 
 * in:  setB - array against which to intersect
 * 
 * out: the result of the intersection
\******************************************************************************/
Array.prototype.intersection = function( setB )
{  
    var setA = this;  

    var setA_seen = {};  
    var setB_seen = {};  

    for ( var i = 0; i < setB.length; i++ )
    {
        setB_seen[ setB[i] ] = true;  
    }
	
    var intersection = [];  

    for ( var i = 0; i < setA.length; i++ )
    {  
        if ( !setA_seen[ setA[i] ] )
        {  
            setA_seen[ setA[i] ] = true;  
            if ( setB_seen[ setA[i] ] )
            {  
                intersection.push( setA[i] );  
    	    }  
        }
    }

	return intersection;  
};


/******************************************************************************\
 * Loop through an array, calling passed in function on each item in the array.
 * 
 * in:  iterator
\******************************************************************************/
Array.prototype.iterate = function( iterator )
{
    for( var i = 0; i < this.length; i++ )
    {
        iterator( i );
    }
};

/******************************************************************************\
 * Find the largest item in an array of numbers.
 * 
 * in:  n/a
 * 
 * out: largest integer in array
\******************************************************************************/
Array.prototype.max = function()
{
    return this.sortNumbers()[ this.length - 1 ];
};

/******************************************************************************\
 * Find the smallest item in an array of numbers.
 * 
 * in:  n/a
 * 
 * out: smallest integer in array
\******************************************************************************/
Array.prototype.min = function()
{
    return this.sortNumbers()[ 0 ];
};


/******************************************************************************\
 * Sort an array of numbers into decending order.
 * 
 * in:  n/a
 * 
 * out: sorted array
\******************************************************************************/
Array.prototype.sortNumbers = function() 
{
    /* Comparison function required by JavaScript's native sort */        
              
    function comparison( a, b ) {

        return ( a - b );
    }
            
    return this.sort(comparison);
};

/******************************************************************************\
 * Sum all the elements in an array.
 * 
 * in:  n/a
 * out: the sum of the elements
\******************************************************************************/
Array.prototype.sum = function( ) 
{
    var output=0;
	
    for(var i=0;i<this.length;i++)
    	if(typeof this[i]=='number')
    		output+=this[i];
    
	return output;
};

/******************************************************************************\
 * Return a random element from an array
 * 
 * in:  n/a
 * out: a randomly selected element from the array
\******************************************************************************/
Array.prototype.randomElement = function( )
{
	var el = Math.randomInt( this.length + 1 );
	
	if( el > this.length - 1 )
		el = this.length - 1;

	return this[ el ];	
};

/******************************************************************************\
 * Randomise an array
 * 
 * in:  n/a
 * out: the randomised array
\******************************************************************************/
Array.prototype.randomise = function( )
{
	var rand=[];
	var data=this.slice(0);
	
	do
		{
		var n=Math.randomInt(data.length);
		var item=data.splice(n,1)[0];

		rand.push(item);
		
		}while(data.length>1);

	rand.push(data[0]);

	return rand;
};

/******************************************************************************\
 * Remove matching elements from an array.
 * 
 * in:  the value to remove
 * 
 * out:	the resulting array
\******************************************************************************/
Array.prototype.removeElements = function( remove )
{
    var output=new Array();

    for( var i = 0; i < this.length; i++ )
    {
        if(remove.indexOf(this[i])==-1)
        	output.push(this[i]);
    }
    
    return output;
};

/******************************************************************************\
 * Rotate an array backwards, so that the second item becomes the first,
 * the first becomes the last, and so on.
 * 
 * in:  n/a
\******************************************************************************/
Array.prototype.rotateBackwards = function() 
{
    return this.unshift( this.pop() );
};

/******************************************************************************\
 * Rotate an array forwards, so that the last item becomes the first,
 * the first becomes the second, and so on.
 * 
 * in:  n/a
\******************************************************************************/
Array.prototype.rotateForwards = function() 
{
    return this.push( this.shift() );
};

/******************************************************************************\
 * Remove duplicate entries from an array.
 * 
 * in:  n/a
 * 
 * out:	an array containing the duplicates found, if any
\******************************************************************************/
Array.prototype.unique = function()
{
    var found=new Array();

    for( var i = 0; i < this.length; i++ )
    {
        if(found.indexOf(this[i])==-1)
        	found.push(this[i]);
    }
    
    return found;
};
/******************************************************************************\
 * 
 * Date extensions
 * -----------------------------------
 * 
 * Methods: getFormatedDate
 * 
\******************************************************************************/

/* Declare the Date.LNWebLib extension */

LNWebLib.Core.declareAPIExtension( 'Date' );

/******************************************************************************\
 * Get a complete formated date from a Unix timestamp.
 * 
 * in:  timestamp - unix timestamp to use. If undefined the current 
 *                  time is used.
 * 
 *      seperator - string used to seperate date componants. '/' is
 *                  used if parameter is undefined.
 * 
 * out: undefined
\******************************************************************************/
Date.prototype.getFormatedDate = function( timeStamp, seperator )
{
    if( seperator == undefined ) seperator = '/';
    
    if( timeStamp != undefined )
    {
        date = new Date( timeStamp );
    }
    else
    {
        date = new Date();      
    }
    
    year = seperator + date.getFullYear();
    year = year.slice( 3 );
    date = date.getDate() + seperator + ( date.getMonth() + 1 ) + seperator + year;
    
    return date;   
};
/******************************************************************************\
 * Math extensions
 * -----------------------------------
 *              
 * Methods:     cap
 * 				linear
 *              quadratic
 *              exponential
 *              logarithmic
 * 				randomDistribution
 * 				randomInt
 * 				roundToNearest
 * 				wholeMultiple
\******************************************************************************/

/* Declare the Math.LNWebLib extension */

LNWebLib.Core.declareAPIExtension( 'Math' );

Math.cap = function(n, cap)
{
	return n>cap?cap:n;
};

Math.linear = function( x, a, b ) 
{
    return ( a * x ) + b;
};
		
Math.quadratic = function( x, a, b, c ) 
{
    return ( a * x * x ) + ( b * x ) + c; 
};

Math.exponential = function( x, a, b ) 
{
    return a * Math.exp( b * x );
};
		
Math.logarithmic = function( x, a, b ) 
{
    return a * Math.log( x ) + b; 
};

Math.randomDistribution = function( mean, deviation )
{
    var rand = (Math.random() * 2 - 1) + (Math.random() * 2 - 1) + (Math.random() * 2 - 1) + ( Math.random() * 2 - 1 ) + ( Math.random() * 2 - 1 );

    return rand*deviation+mean;
};

Math.roundToNearest = function( n, multiple )
{
    return Math.round( n / multiple ) * multiple;
};

Math.randomInt = function( range )
{
	return Math.floor( Math.random() * range);
};

Math.wholeMultiple = function( n, d )
{
	return (Math.floor(n/d)==(n/d))?true:false;
};
/******************************************************************************\
 * String extensions
 * -----------------------------------
 *              
 * Methods:     countChars
 * 				parseInts
 * 				deCamelCase
 *              toCamelCase
 *              stripScripts
 *              evalScripts
 *              trim
 * 				ucFirst
\******************************************************************************/


/* Declare the String.prototype extension */

LNWebLib.Core.declareAPIExtension( 'String' );


/******************************************************************************\
 * Counts the number of a particular character in a string
 * 
 * in:  c (the character to count)
 * 
 * out: the number of occurrances
\******************************************************************************/

String.prototype.countChars = function(c) 
{
	var found=0;
	
	for(var i=0;i<this.length;i++)
		if(this.charAt(i)==c)
			found++;
		
	return found;
}

/******************************************************************************\
 * Return all numbers from a string, in array format.
 * 
 * in:  n/a
 * 
 * out: array of integers in the order in which they occurred in the string
\******************************************************************************/

String.prototype.parseInts = function() 
{
	var ints = this.split( /\D+/g );

	/* Mozilla returns empty item if split occured at beginning or end of 
	 * the string so must remove these if present 
	 */
	
	if( isNaN( parseInt( ints[ 0 ] ) ) )
	{
		ints.shift();
	}
	if( isNaN( parseInt( ints[ ints.length - 1 ] ) ) )
	{ 
		ints.pop();
	}
		
	return ints;
}

/******************************************************************************\
 * Turn a camel case string into one with spaces.
 * 
 * in:  n/a
 * 
 * out: string without camel case
\******************************************************************************/

String.prototype.deCamelCase = function() 
{
	var output = [];
	var last = 0;
	var found = false;

	for(var i = 1; i < this.length; i++)
		{
		var ch = this.charCodeAt( i );
		
		if(ch >= 65 && ch <= 90)
			{
			output.push( this.substring( last, i ) );

			last = i;
			found = true;
			}
		}

	if(found==true)
		{
		output.push( this.substring( last ) );
		output = output.join(' ').toLowerCase();
		}
	else
		output=this;

    return output;
}
		
/******************************************************************************\
 * Turn a hyphenated string into camel case.
 * 
 * in:  n/a
 * 
 * out: string in camel case
\******************************************************************************/

String.prototype.toCamelCase = function() 
{
    var sections    = this.split('-');
    var l           = sections.length;
    
    if ( l == 1 ) return sections[ 0 ];

    var camel = this.charAt( 0 ) == '-'?
        sections[ 0 ].charAt( 0 ).toUpperCase() + sections[ 0 ].substring( 1 ):
        sections[ 0 ];

    for ( var i = 1; i < l; i++ )
    {
        camel += sections[ i ].charAt( 0 ).toUpperCase() + sections[ i ].substring( 1 );
    }

    return camel;
}


/******************************************************************************\
 * Remove the script tags from a string.
 * 
 * in:  n/a
 * 
 * out: string stripped of script
\******************************************************************************/
	
String.prototype.stripScripts = function() 
{
    var RE_script = new RegExp('(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)','g');
	
    return this.replace( RE_script, '');
}


/******************************************************************************\
 * Run the scripts in a string.
 * 
 * in:  n/a
\******************************************************************************/		

String.prototype.evalScripts = function() 
{
    var RE_script = new RegExp('(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)','g');

    var scripts = this.match( RE_script ) || [];

    for( var i = 0; i < scripts.length; i++ )
    {
    	var script=scripts[ i ].replace(/<script.*?>/,'');
    	script=script.replace(/<\/script>/,'');
        eval( ( script ) );
    }
}

/******************************************************************************\
 * Remove white space or line breaks from both ends of a string.
 * 
 * in:  n/a
\******************************************************************************/       
 
String.prototype.trim = function()
{
    return this.replace( /^\s*|\s*$/g, '' );
}

/******************************************************************************\
 * Change the first letter of a string to uppercase
 * 
 * in:  n/a
 * 
 * out: string with the first letter as uppercase
\******************************************************************************/       
 String.prototype.ucFirst = function()
{
	var first = this.charAt( 0 );
	var rest =	this.substr( 1 );
	
	return first.toUpperCase()+rest;
}	
/******************************************************************************\
 * LNWebLib Ajax
 * -----------------------------------
 *
 * Namespace:   LNWebLib.Ajax
 *
 * Interfaces:  None
 *
 * Classes:     LNWebLib.Ajax
 *
 * Properties:  requestType
 *              url
 *              options
 * 
 * Methods:     getResponseObject
 *              applyHeaders
 *              convertXML
 *              handleReadyState
 *              onStateChange
 *              request
 *              update
 *              updateAtIntervals
 *              timer
 *              stopUpdates
 *              doUpdate
 *              restore
 *              setHeader
 *              clearHeader
 *              setDefaultXHRHeader
 *              setDefaultPostHeader
 *              setRequestInterval
 *              setPostHeaders
\******************************************************************************/


/* Declare the Ajax module */

LNWebLib.Core.declareModule( 'Ajax' );


/******************************************************************************\
 * Constructor function for the LNWebLib.Ajax class
 * 
 * in:  requestType - get or post
 * 
 *      url         - address of the requested data
 * 
 *      options     - an object containing the following optional parameters:
 *                      onsuccess: function to on completion of a successful request
 *                      onfailure: function to call on failure of the request
 *                      successParams: parameters to pass to the onsuccess function
 *                      failureParams: parameters to pass to the onfailure function
 *                      position:  set only when intending to call update or 
 *                                  updateAtIntervals. Specifies where to insert 
 *                                  retrieved data relative to target. Options are:
 *                                      'insertBefore' (insert before the target node)
 *                                      'insertAfter' (insert after the target node)
 *                                      'append' (append as child to the target node)
 *                                      'prepend' (insert as first child of target)
 *                                  If position option is not set, the default is 
 *                                  to replace the content of the target using 
 *                                  innerHTML.
 *                      postParameters: parameters to send with a post request.
 *                      scope
\******************************************************************************/
LNWebLib.Ajax = function( requestType, url, options ) 
{
    this.requestType            = requestType;
    this.url                    = url;
    this.options                = options || {};
    
    this.rObject                = null;
    this.isUpdate               = false;
    this.targets                = null;
    this.requestInterval        = null;
	this.evalScripts			= true;
		
    this.defaultXHRHeader       = 'XMLHTTPRequest';
    this.defaultPostHeader      = 'application/x-www-form-urlencoded';
    this.useDefaultXHRHeader    = true;
    this.useDefaultPostHeader   = true;
		
    this.postParameters         = '';	
    
    this.headers                = {};
	
    this.defaultFrequency       = false;
    this.frequency              = false;
    this.decay                  = 1;

    for( var option in options )
    {
        this[ option ] = options[ option ];
    }
}

/******************************************************************************\
 * Get the response object. The call varies between browsers.
 * 
 * in:  n/a
 * 
 * out: response object
\******************************************************************************/
LNWebLib.Ajax.prototype.getResponseObject = function() 
{
    var rObject;
    
    try 
    {
        rObject = new XMLHttpRequest();
    }
    catch( e )
    {
        try
        {
            rObject = new ActiveXObject( 'MSXML2.XMLHTTP' );
        }
        catch( e )
        {
            try
            {
                rObject = new ActiveXObject( 'Microsoft.XMLHTTP' );
            }
            catch( e ){};
        }
    }
    
    return rObject;
}

/******************************************************************************\
 * Apply the headers.
 * 
 * in:  n/a
 * 
 * out: n/a
\******************************************************************************/
LNWebLib.Ajax.prototype.applyHeaders = function() 
{
    for( property in this.headers )
    {
        this.rObject.setRequestHeader( property, this.headers[ property ] );
    }
}

/******************************************************************************\
 * Convert the reponse XML to HTML for insertion into a web page. This is
 * necessary if the response code needs to be manipulated in any way before
 * insertion.
 * 
 * in:  XML node tree
 * 
 * out: HTML node tree
\******************************************************************************/				
LNWebLib.Ajax.prototype.convertXML = function( XML ) 
{
    return LNWebLib.XML.convertToHTML( XML );
}

/******************************************************************************\
 * Function called when the ready state reaches 4. Deals with success
 * and failure outcomes.
 * 
 * in:  response object
 * 
 * out: n/a
\******************************************************************************/		
LNWebLib.Ajax.prototype.handleReadyState = function( rObject ) 
{
    var ajax    = this;
    var requestStatus;
    
    if( rObject.status && rObject.status != 'undefined' && rObject.status != 0 )
    {
        requestStatus = rObject.status;
    }
    else
    {
        requestStatus = 130303;
    }
    
    if( requestStatus >= 200 && requestStatus <= 300 ) 
    {

        if( this.isUpdate == true )
        {
            this.doUpdate( 'success', rObject );
        }
        
    	if( this.onsuccess )
        {
            if( this.successParams )
            {
                this.successParams.push( rObject );            
            }
            this.onsuccess.apply( this.scope || this, this.successParams || rObject );
        }

    }
    else 
    {
        if( this.onfailure )
        {
            if( this.failureParams )
            {
                this.onfailure.push( rObject );
            }
            this.onfailure.apply( this.scope || this, this.failureParams || rObject );
        }

        if( this.isUpdate == true )
        {
            this.doUpdate( 'failure', rObject );
        }
    }
    
    if( this.frequency ) 
    {
        if( !rObject.responseXML.childNodes )
        {
            this.frequency = this.frequency + this.decay;
        }
        else
        {
            this.frequency = this.defaultFrequency;
        }
        
        setTimeout( 
                        function( defaultFreq )
                        {
                            ajax.timer()
                        }, 
                        ( this.frequency ) * 1000
                  );
    }
    
    this.restore();
}

/******************************************************************************\
 * Event handler for the onreadystatechange event. If the ready state has 
 * reached 4 the handleReadyState function is called.
 * 
 * in:  n/a
 * 
 * out: n/a
\******************************************************************************/		
LNWebLib.Ajax.prototype.onStateChange = function() 
{
    if( this.rObject.readyState == 4 )
    {
        this.handleReadyState( this.rObject );
    }
}

/******************************************************************************\
 * Make the Ajax request and set the appropriate headers.
 * 
 * in:  n/a
 * 
 * out: response object
\******************************************************************************/				
LNWebLib.Ajax.prototype.request = function() 
{
    var rObject     = this.getResponseObject();
    this.rObject    = rObject;

    if( rObject == null ) 
    {
        this.restore();
        return null;
    }

    rObject.open( this.requestType, this.url, true );

    if( this.useDefaultXHRHeader == true )
    {
        this.clearHeader( 'X-Requested-With' );
        this.setHeader( 'X-Requested-With', this.defaultXHRHeader, true );
    }

    if( this.requestType == 'post' && this.useDefaultPostHeader == true )
    {
        this.clearHeader( 'Content-type' );
        this.clearHeader( 'Content-length' );
        this.setHeader( 'Content-type', this.defaultPostHeader );
        this.setHeader( 'Content-length', this.postParameters.length );
    }

    if (rObject.overrideMimeType)
    {
        this.setHeader( 'Connection', 'close' );
    }
	
    this.applyHeaders();

    var ajax = this;
    var readyStateResponse = function() 
    {
        return ajax.onStateChange.apply( ajax );
    };
    
    rObject.onreadystatechange = readyStateResponse;

    if ( this.requestType != 'post' )
    {
        rObject.send( null );
    }
    else
    {
        rObject.send( this.postParameters );
    }

    return rObject;
}

/******************************************************************************\
 * Set up a periodical ajax request with no update of the page.
 * 
 * in:  - frequency of the requests in seconds
 *      - decay rate of the requests in seconds
 * 
 * 
 * out: n/a
\******************************************************************************/        
LNWebLib.Ajax.prototype.requestAtIntervals = function( frequency, decay, defaultFrequency ) 
{
    this.frequency  = frequency;
    this.decay      = decay;

    this.request();
}

/******************************************************************************\
 * Make an update to a web page. Pass in a target element to indicate where the
 * new markup should be inserted. The default is to insert it inside the target.
 * If a different position is required the 'position' option can be used when
 * calling the contstructor. See the constructor function for details.
 * 
 * in:  - target
 *      - frequency (an internal parameter used by the updateAtIntervals function
 *        to specify the frequency of repeated updates.
 *      - decay (an internal parameter used by the updateAtIntervales to specify 
 *        the decay rate.  
 * 
 * out: response object
\******************************************************************************/		
LNWebLib.Ajax.prototype.update = function( target ) 
{	
    this.isUpdate = true;

    this.targets = 
    {
        success: target? target.success ? LNWebLib.Dom.getElementById( target.success ) : LNWebLib.Dom.getElementById( target ) : null,
        failure: target? target.failure ? LNWebLib.Dom.getElementById( target.failure ) : null : null
    }

    return this.request();				
}

/******************************************************************************\
 * Set up a periodical ajax request.
 * 
 * in:  - frequency of the requests in seconds
 *      - decay rate of the requests in seconds
 *      - target element for insertion of the new markup
 * 
 * 
 * out: n/a
\******************************************************************************/		
LNWebLib.Ajax.prototype.updateAtIntervals = function( target, frequency, decay, defaultFrequency ) 
{
    this.frequency  = defaultFrequency || frequency;
    this.decay      = decay;

    this.update( target );
}

/******************************************************************************\
 * Function which simply makes a new Ajax call. Used by the updateAtIntervals
 * function.
 * 
 * in:  n/a
 * 
 * out: n/a
\******************************************************************************/		
LNWebLib.Ajax.prototype.timer = function( frequency )
{
    if( this.update )
    {
        new Ajax( this.requestType, this.url, this.options ).updateAtIntervals( this.target, this.frequency, this.decay, this.defaultFrequency );
    }
    else
    {
        new Ajax( this.requestType, this.url, this.options ).requestAtIntervals( this.frequency, this.decay, this.defaultFrequency );
    }
}

/******************************************************************************\
 * Stop the periodical updates.
 * 
 * in:  n/a
 * 
 * out: n/a
\******************************************************************************/		
LNWebLib.Ajax.prototype.stopUpdates = function() 
{
    this.requestInterval = null;
}

/******************************************************************************\
 * Function responsible for performing an update.
 * 
 * in:  - result of the request (success or failure)
 *      - response object
 * 
 * out: response object
\******************************************************************************/		
LNWebLib.Ajax.prototype.doUpdate = function( result, response ) 
{
    var target      = ( result == 'success' )? this.targets.success: this.targets.failure;
    var response    = response;
    var text        = response.responseText;

    if( this.position ) 
    {
        var converter   = new XMLConverter();
        var html        = converter.toXHTML( response.responseXML );
        LNWebLib.Element[ position ]( html.firstChild );
    }
    else if( target != null )
    {
        LNWebLib.Dom.getElementById( target ).innerHTML = text;
    }

    if( this.evalScripts )
    {
        setTimeout( function() { response.responseText.evalScripts() }, 10 );
    }
}

/******************************************************************************\
 * Restore the Ajax object to its original state.
 * 
 * in:  n/a
 * 
 * out: n/a
\******************************************************************************/		
LNWebLib.Ajax.prototype.restore = function() 
{
	if( this.rObject ) 
    {
        this.rObject.onreadystatechange = function() {};
    }
    
    this.rObject    = null;
    this.isUpdate   = false;
    this.targets    = null;
    this.options    = null;
}

/******************************************************************************\
 * Set a specific header if it hasn't already been set.
 * 
 * in:  - header title as string
 *      - header value as string
 * 
 * out: n/a
\******************************************************************************/		
LNWebLib.Ajax.prototype.setHeader = function( title, value ) 
{
    var headers = this.headers;
    
    if( !headers[ title ] )
    {
        headers[ title ] = value;
    }
    else
    {
        headers[ title ] += ',' + value;
    }

}

/******************************************************************************\
 * Clear a specific header.
 * 
 * in:  title of the header to clear as a string
 * 
 * out: n/a
\******************************************************************************/
LNWebLib.Ajax.prototype.clearHeader = function( title ) 
{
    var headers = this.headers;
    if( headers[ title ] )
    {
        headers[ title ] = null
    }
}

/******************************************************************************\
 * Choose whether to set use the default XHR header or not.
 * 
 * in:  Boolean true or false
 * 
 * out: n/a
\******************************************************************************/		
LNWebLib.Ajax.prototype.setDefaultXHRHeader = function( set )
{
    this.useDefaultXHRHeader = set;
}

/******************************************************************************\
 * Choose whether to set use the default post header or not.
 * 
 * in:  Boolean true or false
 * 
 * out: n/a
\******************************************************************************/    		
LNWebLib.Ajax.prototype.setDefaultPostHeader = function( set ) 
{
    this.useDefaultPostHeader = set;
}

/******************************************************************************\
 * Set the post parameters for a post request. These may also be set using
 * the options parameter in the constructor. See the constructor for details.
 * 
 * in:  post parameters
 * 
 * out: n/a
\******************************************************************************/    			
LNWebLib.Ajax.prototype.setPostParameters = function( postParameters )
{
    this.postParameters = postParameters;    
}
/******************************************************************************\
 * 
 * Configuration
 * -----------------------------------
 *
 * Namespace:   LNWebLib.Config
 *
 * Interfaces:  LNWebLib.Config.Form
 *              LNWebLib.Config.Animation
 *
 * Classes:     None
 *
 * Properties:  LNWebLib.Config.Form.emailValidation
 *              LNWebLib.Config.Form.domainValidation        
 *              LNWebLib.Config.Form.urlValidation
 *              LNWebLib.Config.Form.phoneValidation
 *              LNWebLib.Config.Form.dateValidation
 *              LNWebLib.Config.Form.numberValidation
 *              LNWebLib.Config.Form.textValidation
 * 
 * Methods:     
 * 
\******************************************************************************/


LNWebLib.Core.declareModule( 'Config' );

LNWebLib.Core.declareModule( 'Form', 'Config' );
LNWebLib.Core.declareModule( 'Animation', 'Config' );


/******************************************************************************\
 * Regular expressions for validating form fields. 
\******************************************************************************/
LNWebLib.Config.Form.emailValidation    = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*\.(\w{2}|(com|net|org|edu|int|mil|gov|arpa|biz|aero|name|coop|info|pro|museum))$/;
LNWebLib.Config.Form.domainValidation   = /^(?:[A-Za-z]+[A-Za-z0-9-_]+\.)([A-Za-z]{2,4}\.)?([A-Za-z]{2,4}|museum)$/;
LNWebLib.Config.Form.urlValidation      = /^((ht|f)tp(s?)\:\/\/)?([\w]+:\w+)?((?:[A-Za-z]+[A-Za-z0-9-_]+\.)+([A-Za-z]{2,4}\.)?([A-Za-z]{2,4}|museum))(:?([,\?&\=\-\w\/%]+))?$/;
LNWebLib.Config.Form.phoneValidation    = /^(\+(\d)+)?([\d \(\)]+)((( )?(\()?ext(ension)?(.)?( )?([\d \(\)]+))(\))?)?$/;
LNWebLib.Config.Form.dateValidation     = /^\d{1,2}\/\d{1,2}\/(\d{2}|\d{4})$/;
LNWebLib.Config.Form.numberValidation   = /\d/;
LNWebLib.Config.Form.textValidation     = /[a-zA-Z]/;

/******************************************************************************\
 * Settings for the base animation class
\******************************************************************************/
LNWebLib.Config.Animation.defaultDuration   = 1;
LNWebLib.Config.Animation.fps               = 30;
LNWebLib.Config.Animation.timeUnits         = 1000;

/******************************************************************************\
 * 
 * Dom
 * -----------------------------------
 *
 * Namespace:   LNWebLib.Dom
 *
 * Interfaces:  LNWebLib.Dom
 *
 * Classes:     None
 *
 * Properties:  None
 * 
 * Methods:     getElementByID
 *              getElementsByClassName
 *              getElementsByAttribute
 *              addFunctionality
 *              getPrevious
 *              getNext
 *              getChildren
 *              getViewXY
 *              getDocXY
 *              getScroll
 *              getTrueOffset
 *              getPositionedOffset
 *              makeAbsolute
 *              makeRelative
 *              getAttribute
 *              setAttribute
 *              getStyle
 *              setStyle
 *              addClassName
 *              removeClassName
 * 				deleteChildren
 * 
\******************************************************************************/

/* Declare the Dom module */

LNWebLib.Core.declareModule( 'Dom' );

/******************************************************************************\
 * Retrieve a DOM element by its ID.
 * 
 * in:  id              - ID of element to return.
 * 
 *      functionality   - optional parameter to specify library modules to be
 *                        added directly to the DOM object. Options are:
 *                            'animation' - all animation modules
 *                            'fade'      - fade module
 *                            'movement'  - movement module
 *                            'dimension' - dimension module
 *                            'colour'    - colour module
 *                            'drag'      - drag and drop modules
\******************************************************************************/
LNWebLib.Dom.getElementById = function( element, functionality )
{
    var obj;
    
    if( typeof element == 'undefined' )
    {
        return;
    }
       
    if ( typeof element == 'string' )
    {
        obj = document.getElementById( element );
    }
    
    if( typeof element == 'object' )
    {
        obj = element;
    }

    if( obj && functionality )
    {
        LNWebLib.Dom.addFunctionality( obj, functionality );
    }   

    return obj;
}


/******************************************************************************\
 * Retrieve an array of DOM elements with the specified class name. To limit it 
 * to children of a particular element, pass in the parent element or ID as the 
 * optional second parameter.
 * 
 * in:  className       - string specifying class of elements to be returned.
 * 
 *      parent          - optional parent element of the elements being retrieved.
 * 
 * out: array of elements with the specified class name, or 'false' if no
 *      elements of that class are present.
\******************************************************************************/
LNWebLib.Dom.getElementsByClassName = function( className, parentElement )
{
    return LNWebLib.Dom.getElementsByAttribute( 'className', className, parentElement );
}


/******************************************************************************\
 * Retrieve an array of DOM elements with the specified attribute value. 
 * To limit it to children of a particular element, pass in the parent element 
 * or ID as the optional second parameter.
 * 
 * in:  method          - string specifying which attribute is being used. 
 * 
 *      value           - value of the attribute.
 * 
 *      parent          - optional parent element of the elements being retrieved.
 * 
 * out: array of elements with the specified attribute value, or 'false' if none
 *      present.
\******************************************************************************/
LNWebLib.Dom.getElementsByAttribute = function( method, value, parent ) 
{
    var elements    = ( LNWebLib.Dom.getElementById( parent ) || document ).getElementsByTagName( '*' );
    var matched     = new Array;
    var RE_att      = new RegExp("(^|\\s)" + value + "(\\s|$)");

    for ( var i = 0, n = elements.length; i < n; i++ ) 
    {
        if ( elements[ i ][ method ].match( RE_att ) )
        {
            matched.push( elements[ i ] );
        }
    }
    
    if( i == 0 )
    {
        return false;        
    }

    return matched;
}

/******************************************************************************\
 * Adds an instance of a library class to a DOM element as a property 
 * of that element. Once added, you can do the following:
 * 
 *      myDomObject.libClass.method()
 * 
 * instead of:
 * 
 *      myClassInstance = new LNWebLib.libraryClass( myDomObject )
 *      myClassInstance.method();
 * 
 * in:  id              - ID of element to return.
 * 
 *      functionality   - optional parameter to specify library modules to be
 *                        added directly to the DOM object. Options are:
 *                            'animation' - all animation modules
 *                            'fade'      - fade module
 *                            'movement'  - movement module
 *                            'dimension' - dimension module
 *                            'colour'    - colour module
 *                            'drag'      - drag and drop modules
\******************************************************************************/
LNWebLib.Dom.addFunctionality = function( element, functionality ) 
{
    switch( functionality ) 
    {       
        case 'animation':
        {
            for( property in LNWebLib.Animation )
            {
                if( !element[ property ] ) 
                {
                    element[ property ] = new LNWebLib.Animation[ property ]( element );
                }
            }
            break;
        }    
        case 'fade':
        {
            if( !element.Fade )
            {
                element.Fade = new LNWebLib.Animation.Fade( element );
            }
        }   
        case 'movement':
        {
            if( !element.Movement )
            {
                element.Movement = new LNWebLib.Animation.Movement( element );
            }
        }
        case 'colour':
        {
            if( !element.Colour )
            {
                element.Colour = new LNWebLib.Animation.Colour( element );
            }
        }
        case 'dragdrop':
        {
            if( !element.DragDrop )
            {
                element.DragDrop = new LNWebLib.Animation.DragDrop( element );
            }
        }  
    }                                
}

/******************************************************************************\
 * Retrieve an array of DOM elements with the specified tag names.
 * 
 * in:  tagNames        - array of tag names to be searched for. 
 * 
 *      parent          - optional parent element of the elements being retrieved.
 * 
 * out: array of elements with the specified tags, or false if none found.
\******************************************************************************/
LNWebLib.Dom.getElementsByTagNames = function( tagNames, parent )
{
    var result = [];

    for( var i = 0; i < tagNames.length; i++ )
    {
        var elements = ( LNWebLib.Dom.getElementById( parent ) || document ).getElementsByTagName( tagNames[ i ] );

        for( var j = 0; j < elements.length; j++ )
        {
            result[ result.length ] = elements[ j ];
        }
    }
    
    if( result.length == 0 )
    {
        return false;        
    }
    
    return result;
}

/******************************************************************************\
 * Returns the previous node, ignoring any empty text nodes. 
 * 
 * in:  element
 * 
 * out: previous sibling
\******************************************************************************/
LNWebLib.Dom.getPrevious = function( element ) 
{
    var previous = LNWebLib.Dom.getElementById( element ).previousSibling;
    
    while( previous.nodeType == 3 )
    {
        previous = previous.previousSibling;
    }
    
    return previous;
}

/******************************************************************************\
 * Returns the next node, ignoring any empty text nodes. 
 * 
 * in:  element
 * 
 * out: next sibling
\******************************************************************************/	 
LNWebLib.Dom.getNext = function( element )
{	
    var next = LNWebLib.Dom.getElementById( element ).nextSibling;
    
    if(next)
	    while( next.nodeType == 3 )
	    {
	        next = next.nextSibling;
	    }

    return next;
}

/******************************************************************************\
 * Returns all children of the node passed in as parameter, ignoring empty
 * text nodes.  
 * 
 * in:  parent node
 * 
 * out: child nodes as an array
\******************************************************************************/
LNWebLib.Dom.getChildren = function( element ) 
{
    var parent      = LNWebLib.Dom.getElementById( element );
    var allChildren = parent.childNodes;
    var children    = new Array();
    
    for( var i = 0; i < allChildren.length; i++ ) 
    {
        if( allChildren[ i ].nodeType != 3 )
        {
    	    children.push( allChildren[ i ] );
        }
    }

	return children;
}

/******************************************************************************\
 * Returns the size of the browser window, equalised accross browsers.
 * 
 * in:  n/a
 * 
 * out: object with x and y properties
\******************************************************************************/
LNWebLib.Dom.getViewDimensions = function() 
{
    var x,y;
    
    if ( self.innerHeight ) 
    {
        x = self.innerWidth;
        y = self.innerHeight;
    }
    
    else if ( document.documentElement && document.documentElement.clientHeight ) 
    {
        x = document.documentElement.clientWidth;
        y = document.documentElement.clientHeight;
    }
    
    else if ( document.body ) 
    {
        x = document.body.clientWidth;
        y = document.body.clientHeight;
    }
    
    return { 
               x: x,
               y: y 
           };
}

/******************************************************************************\
 * Returns the size of the document, equalised accross browsers.
 * 
 * in:  n/a
 * 
 * out: object with x and y properties
\******************************************************************************/
LNWebLib.Dom.getDocDimensions = function() 
{
    var scrollWidth = ( document.compatMode != 'CSS1Compat' )?
        document.body.scrollWidth:
        document.documentElement.scrollWidth;
        
    var x = Math.max( scrollWidth, LNWebLib.Dom.getViewDimensions().x );

    var scrollHeight = ( document.compatMode != 'CSS1Compat' )? 
        document.body.scrollHeight: 
        document.documentElement.scrollHeight;

    var y = Math.max( scrollHeight, LNWebLib.Dom.getViewDimensions().y );
    
    return  {
                x: x,
                y: y    
            };
}

/******************************************************************************\
 * By default, returns the left and top positions of the visible 
 * section of the document. If an element is passed in, it returns the top
 * and left positions of the visible portion of the element.
 * 
 * in:  n/a
 * 
 * out: object with l and t properties
\******************************************************************************/
LNWebLib.Dom.getScrollPosition = function( element ) 
{
    var l = 0
    var t = 0;
    
    if( element )
    {
        l = LNWebLib.Dom.getElementByID( element ).scrollLeft;
        t = LNWebLib.Dom.getElementByID( element ).scrollTop;
    }
    else
    {
         l = Math.max( document.documentElement.scrollLeft, document.body.scrollLeft    );
         t = Math.max( document.documentElement.scrollTop,  document.body.scrollTop     );
    }
    
    return { 
               l: l,
               t: t 
           };
}

/******************************************************************************\
 * Returns the offset of an element relative to the documents top and far left.
 * 
 * in:  element
 * 
 * out: object with l and t properties
\******************************************************************************/
LNWebLib.Dom.getTrueOffset = function( element ) 
{
    var element = LNWebLib.Dom.getElementById( element );
    var l = 0, t = 0;
    
    do 
    {
        l += element.offsetLeft || 0;
        t += element.offsetTop || 0;
        element = element.offsetParent;
    }
    while ( element );
    
    return {
               l: l,
               t: t
           };
}

/******************************************************************************\
 * Returns the offset of an element relative to the first positioned
 * element within which it is contained. It is therefore the cumulative offset
 * of the element and all its ancestors up to the first ancestor with a position
 * of 'static'.
 * 
 * in:  element
 * 
 * out: object with l and t properties
\******************************************************************************/
LNWebLib.Dom.getPositionedOffset = function( element ) 
{
    var element = LNWebLib.Dom.getElementById( element );
    var l = 0;
    var t = 0;
    
    do 
    {
        l += element.offsetLeft || 0;
        t += element.offsetTop  || 0;
        element = element.offsetParent;
        
        if ( element ) 
        {
            if ( element.tagName == 'BODY' ) 
            {
                break;
            }
            
            var p = LNWebLib.Dom.getStyle( element, 'position' );
            if( p !== 'static' ) 
            {
                break;
            }
        }
    } 
    while ( element );
    
    return  {
                l: l,
                t: t
            };
}

/******************************************************************************\
 * Turns a relatively positioned element into an absolutely positioned one,
 * and stores the relative positions and siblings as properties of the element
 * for use if returning the element's relative positioning. The properties are:
 *      oldLeft
 *      oldTop
 *      oldWidth
 *      oldHeight
 *      oldParent
 *      oldSibling
 * 
 * in:  - element to be absolutely positioned
 *      - boolean value determining whether the element is 
 * 
 * out: n/a
\******************************************************************************/
LNWebLib.Dom.makeAbsolute = function( element ) 
{
    var element = LNWebLib.Dom.getElementById( element );
    
    if( element.style.position == 'absolute')
    {
        return;
    }
    
    element.oldLeft         = LNWebLib.Dom.getStyle( element, 'left' );
    element.oldTop          = LNWebLib.Dom.getStyle( element, 'top' );
    element.oldWidth        = LNWebLib.Dom.getStyle( element, 'width' );
    element.oldHeight       = LNWebLib.Dom.getStyle( element, 'height' );
    element.oldParent       = element.parentNode;
    element.oldSibling      = element.nextSibling;
    
    var offset              = LNWebLib.Dom.getPositionedOffset( element );
    
    var left                = offset.l;
    var top                 = offset.t;
    var width               = element.clientWidth;
    var height              = element.clientHeight;
    
    element.style.position  = 'absolute';
    element.style.left      = left + 'px';
    element.style.top       = top + 'px';
    element.style.width     = width + 'px';
    element.style.height    = height + 'px';
}

/******************************************************************************\
 * Restores an element to relative positioning when it has been absolutely
 * positioned using LNWebLib.Dom.makeAbsolute.
 * 
 * in:  - element to be absolutely positioned
 *      - boolean value determining whether the element is 
 * 
 * out: n/a
\******************************************************************************/
LNWebLib.Dom.makeRelative = function( element ) 
{
    var element = LNWebLib.Dom.getElementById( element );
    
    if( element.style.position == 'relative' )
    {
        return;
    }
    element.style.position  = 'relative';
    element.style.left      = element.oldLeft;
    element.style.top       = element.oldTop;
    element.style.width     = element.oldWidth;
    element.style.height    = element.oldHeight;
}

/******************************************************************************\
 * Returns the value of specified attribute of an element. Equalises fetching 
 * of the style and class attributes accross browsers. 
 * 
 * in:  - Element whose attribute is being fetched
 *      - Attribute to fetch. For setting the class, 'className' is used,
 *        as 'class' is a forbidden variable name in Internet Explorer.
 * 
 * out: n/a
\******************************************************************************/
LNWebLib.Dom.getAttribute = function( element, attribute ) 
{
    var element = LNWebLib.Dom.getElementById( element );
    
    return element[ attribute ];
}

/******************************************************************************\
 * Set a specified attribute of an element. Equalises setting of the style and
 * class attributes accross browsers. 
 * 
 * in:  - Element whose attribute is being set
 *      - Attribute to set. For setting the class, 'className' is used,
 *        as 'class' is a forbidden variable name in Internet Explorer.
 *      - Value of the attribute to set.
 * 
 * out: n/a
\******************************************************************************/
LNWebLib.Dom.setAttribute = function( element, attribute, value ) 
{
    var element = LNWebLib.Dom.getElementById( element );
    
    element[ attribute ] = value;
}

/******************************************************************************\
 * Equalises the 'getComputedStyle(') W3 DOM method, 'currentStyle' property 
 * of Microsoft, and 'style' property.
 * 
 * in:  - Element whose style is being fetched
 *      - Style property to fetch
 * 
 * out: Value of the specified style property
\******************************************************************************/
LNWebLib.Dom.getStyle = function( element, property ) 
{
    var element = LNWebLib.Dom.getElementById( element );
    var styleList;
    
    var value = element.style[ property.toCamelCase() ];
  
    if( property == 'opacity' ) 
    {
        var regexp = /alpha\s?\(\s?opacity\s?=/ig;
        if( value.match && value.match( regexp ) )
        {
            value = value.parseInts();	
        }
    }
    
    if( !value ) 
    {
        if ( document.defaultView && document.defaultView.getComputedStyle ) 
        {
            styleList   = document.defaultView.getComputedStyle( element, null );
            value       = styleList.getPropertyValue( property );
            value       = value.toCamelCase();
        }
        
        else if( element.currentStyle )
        {
            value = element.currentStyle[property.toFirstUpper()]
        }
    }

    return value == 'auto'? null: value;    
}
	
/******************************************************************************\
 * Equalises Internet Explorer's 'opacity' property. Opacity is specified as a
 * number between 0 and 1.
 * 
 * in:  - Element whose style is being set
 *      - Style property to set
 *      - Value to set the property to
 * 
 * out: n/a
\******************************************************************************/
LNWebLib.Dom.setStyle = function( element, property, value ) 
{
    var element = LNWebLib.Dom.getElementById( element );
    var property = property.toCamelCase();
    
    if( property == 'opacity' ) 
    {
        element.style.opacity   = value;
        element.style.filter    = "alpha( opacity = " + value * 100 + " )";
    }
    
    else
    { 
        element.style[property] = value;
    }
}

/******************************************************************************\
 * Adds a class to an element without removing those already set. If the class
 * is already among those set for the element the call is ignored.
 * 
 * in:  - Element to which the new class is being added
 *      - Name of the class to add
 * 
 * out: n/a
\******************************************************************************/
LNWebLib.Dom.addClassName = function( element, className ) 
{
    if( !LNWebLib.Dom.getElementById( element ) )
    {
        return;
    }
    
    var string = LNWebLib.Dom.getAttribute( element, 'className' );
    if( string.indexOf( className ) == -1 )
    {
        string += ' ' + className;
    }
    
    LNWebLib.Dom.setAttribute( element, 'className', string )
}

/******************************************************************************\
 * Removes a class from an element without removing those already set. 
 * If the class is not present the call is ignored.
 * 
 * in:  - Element which the class is being removed from
 *      - Name of the class to remove
 * 
 * out: n/a
\******************************************************************************/			
LNWebLib.Dom.removeClassName = function( element, className ) 
{
    if( !LNWebLib.Dom.getElementById( element ) )
    {
        return;
    }

    var string = LNWebLib.Dom.getAttribute( element, 'className' );
    if( !string )
    {
        return;
    }
    
    string = string.replace( className, '' );
	LNWebLib.Dom.setAttribute( element, 'className', string );
}

/******************************************************************************\
 * Deletes all the child elements of the specific element
 * 
 * in:  element			- the element for whom to delete children 
 * 
 * out: n/a
\******************************************************************************/
LNWebLib.Dom.deleteChildren = function( element )
{
	var children=LNWebLib.Dom.getChildren( element );

	for( var i=0; i<children.length; i++ )
		LNWebLib.Element.remove(children[i]);
}
/******************************************************************************\
 * 
 * Element
 * -----------------------------------
 *
 * Namespace:   LNWebLib.Element
 *
 * Interfaces:  LNWebLib.Element
 *
 * Classes:     None
 *
 * Properties:  None
 * 
 * Methods:     build
 *              insertBefore
 *              insertAfter
 *              prepend
 *              append
 *              replace
 *              update
 *              remove
 * 
\******************************************************************************/


/* Declare the Element module */

LNWebLib.Core.declareModule( 'Element' );


/******************************************************************************\
 * Internal function for building a new HTML element node as follows:
 *  - Create new element with the specified tag name.
 *  - If the text property is set create a text node with that text value.
 *  - Loop through the list of attributes and set them on the new element.
 *  - Return the new element.
 * 
 * in:  tag         -   The tag name for the new element.
 * 
 *      attributes  -   A JSON object containing a list of attributes to set on
 *                      the new element.
 * 
 *      text        -   Option property for specifying text to attach to the new
 *                      element.
\******************************************************************************/
LNWebLib.Element.build = function( tag, attributes, text )
{
    var newEl = document.createElement( tag );

    if( text )
    {
        var textNode = document.createTextNode( text );
        newEl.appendChild( textNode );
    }

    for( attribute in attributes ) 
    {
        LNWebLib.Dom.setAttribute( newEl, attribute, attributes[ attribute ] );
    }

    return newEl;   
}

/******************************************************************************\
 * Create a new HTML element and insert it into the DOM in front of an existing 
 * element.
 * 
 * in:  destination     -   Existing element which the new element will be inserted
 *                          in front of.
 * 
 *      tag             -   Tag name of the new element.
 * 
 *      attributes      -   JSON object containing the list of attributes to set on
 *                          the new element.
 * 
 *      text            -   Optional text to append to the newly created element.
 *      
\******************************************************************************/
LNWebLib.Element.insertBefore = function( destination, tag, attributes, text ) 
{
    if( LNWebLib.Dom.getElementById( attributes.id ) )
    {
        return;   
    }

    var destination = LNWebLib.Dom.getElementById( destination );
    var parentNode  = destination.parentNode;
    
    parentNode.insertBefore( this.build( attributes ), destination );	       
}

/******************************************************************************\
 * Create a new HTML element and insert it into the DOM after an existing 
 * element.
 * 
 * in:  destination     -   Existing element which the new element will be inserted
 *                          after.
 * 
 *      tag             -   Tag name of the new element
 * 
 *      attributes      -   JSON object containing the list of attributes to set on
 *                          the new element.
 * 
 *      text            -   Optional text to append to the newly created element.
 *      
\******************************************************************************/		
LNWebLib.Element.insertAfter = function( destination, tag, attributes, text ) 
{
    if( LNWebLib.Dom.getElementById( attributes.id ) )
    {
        return;   
    }
    
    var parentNode  = LNWebLib.Dom.getElementById( destination ).parentNode;
    var destination = LNWebLib.Dom.getNext( destination );
    
    parentNode.insertBefore( this.build( tag, attributes, text ), destination );
}

/******************************************************************************\
 * Create a new HTML element and attach it as the first child of the specified
 * parent element.
 * 
 * in:  parentNode      -   Element to be the parent of the new element.
 * 
 *      tag             -   Tag name of the new element.
 * 
 *      attributes      -   JSON object containing the list of attributes to set on
 *                          the new element.
 * 
 *      text            -   Optional text to append to the newly created element.
 *      
\******************************************************************************/	    
LNWebLib.Element.prepend = function( parentNode, tag, attributes, text ) 
{
    if( LNWebLib.Dom.getElementById( attributes.id ) )
    {
        return;   
    }
    
    var destination = LNWebLib.Dom.getChildren( parentNode )[0] || null;
    
    if( destination == null )
    {
        LNWebLib.Dom.getElementById( parentNode ).appendChild( this.build( tag, attributes, text ) );
        return;      
    }
    LNWebLib.Dom.getElementById( parentNode ).insertBefore( this.build( tag, attributes, text ), destination );
}

/******************************************************************************\
 * Create a new HTML element and append it to the specified parent element.
 * 
 * in:  parentNode      -   Element to be the parent of the new element.
 * 
 *      tag             -   Tag name of the new element.
 * 
 *      attributes      -   JSON object containing the list of attributes to set on
 *                          the new element.
 * 
 *      text            -   Optional text to append to the newly created element.
 *      
\******************************************************************************/       
LNWebLib.Element.append = function( parentNode, tag, attributes, text ) 
{
    if( LNWebLib.Dom.getElementById( attributes.id ) )
    {
        return;   
    }
    
    LNWebLib.Dom.getElementById( parentNode ).appendChild( this.build( tag, attributes, text ) );
}

/******************************************************************************\
 * Replace an existing HTML element with a newly created element.
 * 
 * in:  element         -   Element to be replaced.
 * 
 *      tag             -   Tag name of the new element.
 * 
 *      attributes      -   JSON object containing the list of attributes to set on
 *                          the new element.
 * 
 *      text            -   Optional text to append to the newly created element.
 *      
\******************************************************************************/       		    
LNWebLib.Element.replace = function( element, tag, attributes, text ) 
{
    var element = LNWebLib.Dom.getElementById( element );
    if( !element )
    {
        return;   
    }
    
    var parentNode  = element.parentNode;
    var destination = element.nextSibling;

    parentNode.removeChild( element );
    parentNode.insertBefore( this.build( attributes), destination );
}

/******************************************************************************\
 * Update the attributes of an existing element.
 * 
 * in:  element         -   Element to update.
 * 
 *      attributes      -   JSON object containing the list of attributes to update on
 *                          the new element.
 *      
\******************************************************************************/       		    
LNWebLib.Element.update = function( element, attributes ) 
{
    var element = LNWebLib.Dom.getElementById( element );
    if( !element )
    {
        return;   
    }

    for ( attribute in attributes ) 
    {
        LNWebLib.Dom.setAttribute( element, attribute, attributes[ attribute ] );
    }
}

/******************************************************************************\
 * Remove an existing element if it exists.
 * 
 * in:  destination     -   Element to remove.
 *      
\******************************************************************************/       
LNWebLib.Element.remove = function( element ) 
{
    var element = LNWebLib.Dom.getElementById( element );
    if( !element )
    {
        return;   
    }
    
    element.parentNode.removeChild( element );
}
		



/******************************************************************************\
 * 
 * Event
 * -----------------------------------
 *
 * Namespace:   LNWebLib.Event
 *
 * Interfaces:  LNWebLib.Event
 *
 * Classes:     None
 *
 * Properties:  listenerStore
 * 
 * Methods:     unload
 *              addEventListener
 *              removeEventListener
 *              target
 *              relatedTarget
 *              mouse
 *              stopDefault
 *              mouseLeave
 *              wrapListener
 * 
\******************************************************************************/


/* Declare the Event module */
LNWebLib.Core.declareModule( 'Event' );


/* Array containing infomation for all added listeners. */
LNWebLib.Event.listeners = {};

LNWebLib.Event.reference = 0;

/* Unload listeners must be stored and fired in a single action to ensure they
 * are called before the listeners are all removed on unload.
 */
LNWebLib.Event.unloadListeners = [];


/******************************************************************************\
 * Adds an event listener to the specified element for the specified type
 * of event. 
 * 
 * For each listener added, information is stored in the 
 * LNWebLib.Event.listeners property. The following information
 * is stored for each listener:
 *  - the element it is being attached to
 *  - the type of listener (e.g. mouseover)
 *  - the listener object
 * 
 * The W3 method for adding an event listener is addEventListener. Internet
 * explorer uses attachEvent.
 * 
 * in:  element     - Element to attach the listener to
 * 
 *      type        - The type of listener (e.g. mousover)
 *      
 *      listener    - The listener function to attach
 * 
 *      capture     - Optional capture parameter (true/false)
 * 
 *      scope       - Optional object to use as the scope for calling the listener
 * 
 * out: A reference to the listener information in LNWebLib.Event.listenerStore.
 *      This can be used to remove the event listener later (see
 *      LNWebLib.Event.removeEventListener) for details. Zero should
 *      never be returned from the function so 1 is added to the value of the
 *      index.
\******************************************************************************/
LNWebLib.Event.addEventListener = function( element, type, listener, capture, scope ) 
{
    var element = LNWebLib.Dom.getElementById( element );
    
    if( !element )
    {
        return false;
    }
    
    var capture = capture || false;

    var wrappedListener = function( e )
    {
        return listener.call( scope || element, e );
    };

    var storeInfo =
    {
        element:    element,
        type:       type,
        listener:   wrappedListener
    };

    if( type == 'unload' )
    {
        this.unloadListeners[ this.unloadListeners.length ] = storeInfo;
    }

    this.listeners[ 'ref_' + this.reference ] = storeInfo;
    
    this.basicAddEventListener( element, type, wrappedListener, capture );
    
    return 'ref_' + this.reference++;
};

LNWebLib.Event.basicAddEventListener = function ( element, type, listener, capture )
{
    if ( element.addEventListener )
    {
       element.addEventListener ( type, listener, capture );
    }
    else if ( element.attachEvent )
    {
        element.attachEvent( 'on' + type, listener, capture );
    }        
};

/******************************************************************************\
 * Removes an event listener which has previously been added to an element
 * using LNWebLib.Event.addEventListener. 
 * 
 * The W3 method for removing an event listener is removeEventListener. Internet
 * explorer uses detachEvent.
 *  
 * in: reference string
 * 
 * out: n/a
\******************************************************************************/
LNWebLib.Event.removeEventListener = function( reference ) 
{
    LNWebLib.Event.basicRemoveEventListener( reference.element, reference.type, reference.listener );
};

LNWebLib.Event.basicRemoveEventListener = function ( element, type, listener )
{
    if ( element.removeEventListener )
    {
       element.removeEventListener ( type, listener, false );
    }
    else if ( element.detachEvent )
    {
        element.detachEvent( 'on' + type, listener );
    }        
};

/******************************************************************************\
 * Removes all event listeners. This function is called internally on window
 * unload to prevent IE memory leaks.
 * 
 * in:  n/a
 * 
 * out: n/a
\******************************************************************************/
LNWebLib.Event.unload = function( e )
{
    for( var i = 0, l = LNWebLib.Event.unloadListeners.length; i < l; i++ )
    {
        LNWebLib.Event.unloadListeners[ i ]( e );
        delete LNWebLib.Event.unloadListeners[ i ];
    }
    delete LNWebLib.Event.unloadListeners;
    
    for( listener in LNWebLib.Event.listeners )
    {
        LNWebLib.Event.removeEventListener( LNWebLib.Event.listeners[ listener ] );
        delete LNWebLib.Event.listeners[ listener ];
    }
    delete LNWebLib.Event.listeners;
    
    LNWebLib.Event.basicRemoveEventListener( window, 'unload', LNWebLib.Event.unload );
};

LNWebLib.Event.setup = function()
{
    LNWebLib.Event.basicAddEventListener( window, 'unload', LNWebLib.Event.unload );
}();


/******************************************************************************\
 * Retrieves the target element of the event. For example in a mouseover event
 * this will be the element the mouse is over when the event is triggered.
 * 
 * The W3 method is event.target. IE uses event.srcElement.
 *  
 * in:  event (IE holds this in the window.event property instead)
 * 
 * out: target element
\******************************************************************************/	
LNWebLib.Event.target = function( e ) 
{
    var e = e || window.event;
    var target = e.target? e.target : e.srcElement;

    if ( target.nodeType == 3 )
    {			    
        target = target.parentNode;
    }

    return target;
};

/******************************************************************************\
 * Retrieves the related target of a mouseover or mouseout event. 
 * For example in a mouseover event this will be the element the mouse has 
 * just left when the event is triggered.
 * 
 * The W3 method is event.relatedTarget. IE uses event.fromElement in the case
 * of a mouseover event and event.toElement in a mouseout event.
 *  
 * in:  event (IE holds this in the window.event property instead)
 * 
 * out: related target
\******************************************************************************/        
LNWebLib.Event.relatedTarget = function( e ) 
{
    var e = e || window.event;
    
    if ( e.type == 'mouseover' )
    {
        return e.fromElement;
    }
    
    else if ( e.type == 'mouseout' )
    {
        return e.toElement;
    } 
};

/******************************************************************************\
 * Retrieves the current co-ordinates of the mouse, allowing for scrolling.
 * 
 * in:  event (IE holds this in the window.event property instead)
 * 
 * out: object with x and y properties
\******************************************************************************/
LNWebLib.Event.mouse = function( e )
{
    var e = e || window.event;
			
    x = e.pageX?
        e.pageX:
        e.clientX + document.documentElement.scrollLeft + document.body.scrollLeft;

    y = e.pageY?
        e.pageY:
        e.clientY + document.documentElement.scrollTop + document.body.scrollTop;

    return { x: x, y: y };
};

/******************************************************************************\
 * Stops bubbling or propogation of an event.
 * 
 * in:  event (IE holds this in the window.event property instead)
 * 
 * out: n/a
\******************************************************************************/
LNWebLib.Event.stopDefault = function( e ) 
{
    var e = e || window.event;

    if ( e.preventDefault )
    {
        e.preventDefault();
        e.stopPropagation();
    }
				
    else 
    {
        e.returnValue = false;
        e.cancelBubble = true;
    }
};

/******************************************************************************\
 * Detects when mouse truly leaves a layer. Ignores movement from inner layer to 
 * layer with mouseout attached which would normally trigger a mouseout event due 
 * to bubbling. Also ignores movement onto a link within the layer, which is 
 * normally registered as a mouseout. Call from within event listener: if 
 * (EventUtils.mouseLeave(e) == true) proceed with listener.
 * 
 * in:  event (IE holds this in the window.event property instead)
 * 
 * out: true/false
\******************************************************************************/
LNWebLib.Event.mouseLeave = function( e ) 
{
    var e       = e || window.event;
    var target  = this.target( e );
    var rTarget = this.relatedTarget( e );
    
    while ( rTarget && rTarget !== target )
    {
        rTarget = rTarget.parentNode;
    }

    return (rTarget !== target);  
};



/******************************************************************************\
 * Create a closure around an event listener so that it is called in the correct
 * scope.
 * 
 * in:  method      - The event listener
 * 
 *      scope       - Scope to call the event listener in
 * 
 * out: event listener as a closure
\******************************************************************************/
LNWebLib.Event.wrapListener = function( method, scope ) 
{
    return function( e ) 
    {
        method.call( scope, e || window.event );
    }
};
/******************************************************************************\
 * 
 * XML
 * -----------------------------------
 *
 * Namespace:   LNWebLib.XML
 *
 * Interfaces:  LNWebLib.XML
 *
 * Classes:     None
 *
 * Properties:  None
 * 
 * Methods:     convertToXHTML
 * 
\******************************************************************************/


/* Declare the Dom Utilities module */

LNWebLib.Core.declareModule( 'XML' );


/******************************************************************************\
 * Converts an XML node tree to an HTML node tree as follows:
 *  - Obtains the child nodes of the parent XML node.
 *  - If the parent node is a string then it has no child nodes so 
 *    return an HTML text node with its value.
 *  - If the parent XML node has a nodeName property, create an HTML node of this
 *    type.
 *  - Then loop through its attributes and set the equivalent attributes on the
 *    new HTML element.
 *  - If there is no nodeName it is a document fragment and so a new HTML document
 *    fragment is created.
 *  - Loop through all the child nodes and call the function recursively on them.
 * 
 * in:  XML node tree
 * 
 * out: HTML node tree
\******************************************************************************/
LNWebLib.XML.convertToHTML = function( XML ) 
{
	var childNodes = XML.childNodes;
	
	if( XML.nodeType == 3 )
	{
		return document.createTextNode( XML.nodeValue );
	}
	else 
	{
		try 
		{
			var newNode = document.createElement( XML.nodeName );
			var attributes = XML.attributes;
			for( var i = 0; i < attributes.length; i++ )
			{
				LNWebLib.Dom.setAttribute( newNode, attributes[i].nodeName, attributes[i].nodeValue );
			}
		}
		catch( e ) 
		{
			var newNode = document.createElement( 'HTML_DOCUMENT_FRAGMENT' );
		}
	}
	for( var i = 0; i < childNodes.length; i++ )
	{
		newNode.appendChild( this.toXHTML( childNodes[ i ] ) );			
	}
	
	return newNode;
}

/******************************************************************************\
 * Converts an XML string into an XML DOM object which can then be accessed using
 * JavaScript XML DOM methods.
 * 
 * Internet Explorer uses the loadXML() method to parse an XML string, 
 * while other browsers uses the DOMParser object.
\******************************************************************************/
LNWebLib.XML.parseFromString = function( string )
{
    var xmlDoc;
    
    try
    {
        xmlDoc          = new ActiveXObject( 'Microsoft.XMLDOM' );
        xmlDoc.async    = 'false';
        
        xmlDoc.loadXML( string );
    }
    catch( e )
    {
        try
        {
            var parser  = new DOMParser();
            xmlDoc      = parser.parseFromString( string, 'text/xml' );
        }
        catch( e ){}
    }
    
    return xmlDoc;
}	
/******************************************************************************\
 * 
 * LNWebLib Utilities
 * -----------------------------------
 *
 * Namespace:   LNWebLib.Utilities
 *
 * Interfaces:  LNWebLib.Utilities
 *
 * Classes:     None
 *
 * Properties:  None
 * 
 * Methods:     email
 *              toQueryString
 * 
\******************************************************************************/


/* Declare the Utilities module */

LNWebLib.Core.declareModule( 'Utilities' );


/******************************************************************************\
 * Send email with lower risk of attracting spam.
 * 
 * in:  domain  -   Domain part of the email address to send the email to
 *      
 *      user    -   User name to precede domain name
 * 
 *      subject -   Subject of the email. Optional - default subject of
 *                  "Enquiry from website" will be used if none provided.
 * 
 * out: string of properties and values in query format (property=value&)
\******************************************************************************/

LNWebLib.Utilities.email = function ( domain, user, subject )
{
    var subject = subject || 'Enquiry from website';
    
    window.location = 'mailto:' + user + '@' + domain + '?subject=' + subject;
}


/******************************************************************************\
 * Serialise an object to a query string.
 * 
 * in:  object - Oobject for serialisation
 * 
 * out: string of properties and values in query format (property=value&)
\******************************************************************************/

LNWebLib.Utilities.toQueryString = function( object )
{
    var list = [];

    for( property in object ) 
    {
        list.push( encodeURIComponent( property ) + '=' + encodeURIComponent( object[ property ] ) );
    }
    
    var str = list.join( '&' );

    return str;    
}
var $ = LNWebLib.Dom.getElementById;
var Ajax = LNWebLib.Ajax;
var nospam = LNWebLib.Utilities.email;

function scroller(obj,target,dir){
	
	var y=LNWebLib.Dom.getScrollPosition().t;

	if(dir==undefined)
		dir=y>target?-1:1;

	var delta=Math.sqrt(Math.abs(y-target));
		
	window.scrollBy(0,delta*dir);
	
	var newY=LNWebLib.Dom.getScrollPosition().t;
	
	if(newY==y || (dir>0 && LNWebLib.Dom.getScrollPosition().t>=target) || (dir<0 && LNWebLib.Dom.getScrollPosition().t<=target))
		{
		window.scrollTo(0,target);
		window.location.hash='#'+obj;
		}
	else
		setTimeout("scroller('"+obj+"',"+target+","+dir+")",10);
}

function jwaInit(){
	
	var links=LNWebLib.Dom.getElementsByClassName('menu');
	
	var mappings=
			{
			art:			"art",
			carpentry:		"bespoke-carpentry",
			contact:		"contact",
			testimonials:	"testimonials",
			top:			"james-ward-art"
			}
	
	var contactTop=0;
	
	var pageHeight=document.getElementsByTagName('BODY')[0].offsetHeight;
	var viewHeight=LNWebLib.Dom.getViewDimensions().y;

	for(var i=0;i<links.length;i++)
		{
		var link=links[i];

		if(link.tagName=='A')
			{
			var y=0;
			var height=0;
			var str=link.href;
			
			var target=str.substring(str.indexOf('#')+1);

			var item=$('image-'+mappings[target.toLowerCase()]);
			
			if(item)
				{
				y=LNWebLib.Dom.getTrueOffset(item).t-20;

				if(pageHeight>viewHeight && (pageHeight-y)<viewHeight)
					y=pageHeight-viewHeight;
				}

			link.href="javascript:scroller('"+target.toLowerCase()+"',"+y+")";
			
			link.id='';
			
			if(target.toLowerCase()=='contact')
				contactTop=y;
			}
		}
	
	var errors=LNWebLib.Dom.getElementsByClassName('error');
	
	if(errors.length>0)
		scroller('contact',contactTop);
	
	
	var images=LNWebLib.Dom.getElementsByClassName('gallery');
	
	for(var i=0;i<images.length;i++)
		{
		var image=images[i];
		if(image.tagName=='IMG')
			{
			image.style.left="50%";
			image.style.marginLeft='-'+(image.width/2)+'px';
			image.style.visibility='visible';
			}
		}

}

function fader(lyr,d,target,init,f,pos){

	var obj=$(lyr);

	if(obj)
		{
		if(obj.id=='')
			obj.id='a'+Math.randomInt(1000).toString()+'-'+Math.randomInt(1000).toString();

		if(obj._LN_breathe)
			clearInterval(obj._LN_breathe);
		
		if(obj.fade_on==null)
			{			
			obj.fade_on=false;
			obj.fade=init;
			}
	
		if(obj.fade_on)
			{
			clearTimeout(obj.fade_on);
			obj.fade_on=false;
			}

		obj.fade_on=setTimeout("fadeMachine('"+obj.id+"',"+(d/2)+","+target+",'"+f+"',"+pos+")",15);
		}
}

function fadeMachine(obj,d,target,f,pos){

	var lyr=document.getElementById(obj);
	
	if(lyr)
		{
		lyr.fade+=(d*20);
	
		if((d>0 && lyr.fade>=target) || (d<0 && lyr.fade<=target))
			{
			if(lyr.fade>99)
				lyr.fade=100;

			if(lyr.fade<0)
				lyr.fade=0;
			
			clearTimeout(lyr.fade_on);

			lyr.fade_on=false;

			if(f && d>0)
				{
				if(f=='display')
					lyr.style.display='block';
				else
					lyr.style.visibility='visible';
				}
			}
		else
			lyr.fade_on=setTimeout("fadeMachine('"+lyr.id+"',"+d+","+target+",'"+f+"',"+pos+")",15);
	
		if(f)
			{
			if(f=='display')
				lyr.style.display=(lyr.fade<=0)?'none':'block';
			if(f=='visibility')
				lyr.style.visibility=(lyr.fade<=0)?'hidden':'visible';
	
			if(f=='remove' && lyr.fade<=0)
				LNWebLib.Element.remove(lyr);		
			}

		if(pos)
			{
			if(lyr.fade==100)
				lyr.style.position="relative";
			else
				lyr.style.position="absolute";
			}
		
		lyr.style.opacity=lyr.fade/100;
		lyr.style.filter="progid:DXImageTransform.Microsoft.Alpha(Opacity="+lyr.fade+")";
		}
}
/*
 *	RenaissanceCMS
 *	RenFront.js | core version | last modified 08/11/08
 *  
 *	Renaissance framework for the front end
 * 
 */

var RenFront={};

RenFront.Content={};
RenFront.Interactive={};
RenFront.Content.openContent={};

RenFront.Content.fetch=function(id,type,view,target,background,close_link,belongs,previous,next){

	this.openContent[target]={background: background, close_link: close_link, belongs: belongs, previous: previous, next: next};
	
	var url=RenFront.sys.path+'_content/'+id+'/'+type+'/'+view;

	var updater=new Ajax('get', url, {onsuccess: RenFront.Content.onUpdate, successParams: [target]});
	updater.update(target);

	fader(target,-1,0,100,'display');

	var str='_content/'+id+'/'+type+'/'+view;

	if(target)
		str+='/'+target;

	if(background)
		str+='/'+background;

	if(close_link)
		str+='/'+close_link;

	//window.location.hash=str;
	
	this.id=id;
	this.type=type;
	this.view=view;
}

RenFront.Content.addCloseLink=function(target){

	var close_link=RenFront.Content.openContent[target].close_link;
	var link={title: close_link, href: 'javascript:RenFront.Content.closeContent("'+target+'")', className: "interface content_close_link"};
	
	LNWebLib.Element.append(target,'A',link,close_link);
}

RenFront.Content.closeContent=function(target){

	fader(target,-1,0,100,'display');

	if(RenFront.Content.openContent[target].background)
		fader(RenFront.Content.openContent[target].background,-0.5,0,75,1);
}

RenFront.Content.customOnUpdate=function(target){
	
	var t=$(target);
	
	var image=LNWebLib.Dom.getChildren(target)[0];

	var width=parseInt(image.style.width);
	var height=parseInt(image.style.height);

	t.style.marginTop=-(height/2)+'px';
	t.style.marginLeft=-(width/2)+'px';
	
	var slideShow="RenFront.Interactive.SlideShow"+this.openContent[target].belongs;
	
	var backLink={title: 'previous image', href: 'javascript:'+slideShow+'.showImage("'+this.openContent[target].previous+'")', className: "interface content_previous_link"};
	var nextLink={title: 'next image', href: 'javascript:'+slideShow+'.showImage("'+this.openContent[target].next+'")', className: "interface content_next_link"};
	
	LNWebLib.Element.append(target,'A',backLink,'previous image');
	LNWebLib.Element.append(target,'A',nextLink,'next image');
}

RenFront.Content.loadFromHash=function(){
	var loc=window.location.hash;

	if(loc.indexOf('#_content')===0)
		{
		var pieces=loc.split('/');
		RenFront.Content.fetch(pieces[1],pieces[2],pieces[3],pieces[4],pieces[5],pieces[6]);
		var loaded=true;
		}
	else
		var loaded=false;

	return loaded;
}

RenFront.Content.onUpdate=function(target,rObject){

	if(window['_gaq'])
		_gaq.push(["_trackPageview",this.type+'/'+this.id]);

	if(RenFront.Content['customOnUpdate'])
		RenFront.Content.customOnUpdate(target);

	if(RenFront.Content.openContent[target].close_link)
		RenFront.Content.addCloseLink(target);

	fader(target,0.75,100,0,'display');

	if(RenFront.Content.openContent[target].background)
		fader(RenFront.Content.openContent[target].background,0.5,75,0,1);
}
