////////////////////////////////////////////////////////////////////////
var onLoadEventList = new Array();
var overlayLocked   = "no";

// preload images
if( document.images )
{
  preloadImg1 = new Image();
  preloadImg1.src = "/images/buttons/arrow_up.gif";
  preloadImg2 = new Image();
  preloadImg2.src = "/images/search_results/week_range/tab_hover.gif";
  preloadImg3 = new Image();
  preloadImg3.src = "/images/functions_panel/tabs/tab_hov.gif";
}

//Preload Images generic function
var myimages=new Array()

function preloadimages()
{
  for (i=0;i<preloadimages.arguments.length;i++){
    myimages[i]=new Image();
    myimages[i].src=preloadimages.arguments[i];
  }
}

// Preload search waiting main image in home page
// TODO: Determine where this should go as Sprocket will own the home CSS (???).
function preloadSearchWaitingImage()
{
  var searchWaiting = '/images/interstitual/bg-interstitualContainer-03.png';
  preloadimages(searchWaiting);
}

// get the dimensions of the browser's *visible* area,
// that is, not including scrollbars
function getBrowserViewportDimensions()
{
  var dimensions = {};

  if( browser.isSafari )
  {
    dimensions.width = window.innerWidth;
    dimensions.height = window.innerHeight;
  }
  else if( (typeof( document.documentElement ) != "undefined") && (typeof( document.documentElement.clientWidth ) != "undefined" ) )
  {
    dimensions.width = document.documentElement.clientWidth;
    dimensions.height = document.documentElement.clientHeight;
  }
  else if( typeof( window.innerWidth ) != "undefined" )
  {
    //Non-IE
    dimensions.width = window.innerWidth;
    dimensions.height = window.innerHeight;
  }
  else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) )
  {
    //IE 4 compatible
    dimensions.width = document.body.clientWidth;
    dimensions.height = document.body.clientHeight;
  }

  return dimensions;
}

function getBrowserScrollingOffsets()
{
  var x,y;

  if (self.pageYOffset) // all except Explorer
  {
    x = self.pageXOffset;
    y = self.pageYOffset;
  }
  else if (document.documentElement && document.documentElement.scrollTop)
    // Explorer 6 Strict
  {
    x = document.documentElement.scrollLeft;
    y = document.documentElement.scrollTop;
  }
  else if (document.body) // all other Explorers
  {
    x = document.body.scrollLeft;
    y = document.body.scrollTop;
  }

  return { x: x, y: y };
}

function setBrowserScrollingOffsets( x, y )
{
  if( document.documentElement && document.documentElement.scrollTop )
  {
    // Explorer 6 Strict
    document.documentElement.scrollLeft = x;
    document.documentElement.scrollTop = y;
  }
  else if( document.body )
  {
    document.body.scrollLeft = x;
    document.body.scrollTop = y;
  }
}

// get the coordinates of the centre of the browser viewport,
// measured from the top-left corner of the pages
function getBrowserViewportCentre()
{
  var scrollOffsets = getBrowserScrollingOffsets();
  var viewportSize = getBrowserViewportDimensions();

  var centre = {};
  centre.x = scrollOffsets.x + Math.floor( viewportSize.width / 2 );
  centre.y = scrollOffsets.y + Math.floor( viewportSize.height / 2 );

  return centre;
}

////////////////////////////////////////////////////////////////////////

function showElement(element)
{
  $(element).style.display = "inline";
}

function hideElement(element)
{
  $(element).style.display = "none";
}

function stopEventBubbling( e )
{
  if( !e )
  {
    var e = window.event;
  }
  e.cancelBubble = true; // IE
  if( e.stopPropagation )
  {
    e.stopPropagation()
  };
}

////////////////////////////////////////////////////////////////////////
//  GLOBAL OVERLAY FUNCTIONS
//
//  There can only be one overlay (ie. sticky, blinky or search overlay)
//  open at once. So whenever we open an overlay we register a function
//  to close it in a global variable. Then when we open a new overlay we
//  call the registered function to close the old one.
////////////////////////////////////////////////////////////////////////

// register the close handler function for an overlay
function globalOverlayRegisterCloseHandler( element, handler ){
  // make sure any existing overlays are closed before we overwrite
  // the close handler
  globalOverlayClose();

  // store the handler and the element that it relates to
  window.globalOverlayElement = element;
  window.globalOverlayCloseHandler = handler;
}

// clear the registered close handler if it relates to the given element
// If no element argument is supplied then the handler will be cleared
// without checking which element it was registered for
function globalOverlayClearCloseHandler( element ){
  if( !element || (window.globalOverlayElement == element) ){
    window.globalOverlayElement = null;
    window.globalOverlayCloseHandler = null;
  }
}

// close the current global overlay, if there is one
function globalOverlayClose(){
  // call the registered close handler, if there is one
  if( window.globalOverlayCloseHandler ){
    window.globalOverlayCloseHandler();
  }
  // clear the close handler because the overlay has been shut now
  globalOverlayClearCloseHandler();
}


////////////////////////////////////////////////////////////////////////

function showOverlay( unit ){
  // make sure we have the unit node, not just its ID
  var unit = $( unit );

  // if we've just closed this overlay, then don't allow it to be reopened immediately
  if( (typeof( window.functionOverlayJustClosed ) != "undefined") && (window.functionOverlayJustClosed == unit ) ){
    return;
  }

  // if the criteria unit hasn't loaded yet, then don't try doing anything!
  if( unit == null ){ return; }

  if( overlayLocked != "no" ){
    hideOverlay( overlayLocked );
  }
  var fadePanel = $( unit.id + 'Fade' );
  fadePanel.style.display = "none";
  var overlay = $( unit.id + 'Overlay' );
  var connector = $( unit.id + 'OverlayConnector' );

  // if any of the overlay bits haven't loaded yet, then don't try doing anything!
  if( (overlay == null) || (connector == null) ){
    return;
  }

  var xcoords = unit.offsetLeft;

  //account for header height
  var headerHeight = 0;
  if($('fcHeader'))
  {
     headerHeight = $('fcHeader').offsetHeight;
  }

  var ycoords = unit.offsetTop + headerHeight;
  var spmHeight = unit.offsetHeight;

  Element.addClassName( unit, "selected" );

  // close any other overlays that are open
  globalOverlayClose();

  //Show the relevant overlay, find out it's height and position it accordingly
  overlay.style.display = "block";
  var overlayHeight = overlay.offsetHeight;
  var overlayWidth = overlay.offsetWidth;
  overlay.style.left = (xcoords + 208) + "px";

  // Window fitting logic..
  var y = ((ycoords + (spmHeight/2)) - (overlayHeight/2));
  var yscroll = getBrowserScrollingOffsets().y;
  y = (y < yscroll ? yscroll + 10 : y); // Don't go higher than the viewable area
  var arrowspace = 50;
  y = (y > (ycoords + (spmHeight/2)) - arrowspace ? (ycoords + (spmHeight/2)) - arrowspace : y); // Don't go lower than the connector arrow space
  overlay.style.top = y + "px";

  //Show the relevant overlay connector and set it's height and position
  connector.style.height = spmHeight + "px";
  connector.style.display = "block";

  // make sure the overlay is up-to-date before we display it
  searchUnitOverlayUpdate( unit.id );

  // move the shim, if it exists, to be behind the overlay
  shimShowBehind( overlay, { top: 0, left: 3, bottom: 2, right: 2 } );

  overlayLocked = unit.id;

  // register a global close handler that can be called to close this overlay
  // in case we need to show another overlay
  globalOverlayRegisterCloseHandler( unit.id, function(){ hideOverlay( unit.id ); } );
}

function hideOverlay( unit )
{
  // make sure we have the unit node, not just its ID
  var unit = $( unit );
  // If we don't have a unit.
  if( unit == null ){ return; }

  var overlay = $(unit.id + 'Overlay');
  var connector = $(unit.id + 'OverlayConnector');

  if( unit != null ){ Element.removeClassName( unit, "selected" ); }
  if( connector != null ){ connector.style.display = "none"; }
  if( overlay != null ){ overlay.style.display = "none"; }

  shimHide( overlay );

  // experimentally removed this setting as it occasionally impacting closing
  // an overlay when a certain sequence of links on overlay clicked. - checks for
  // this flag left in other functions in case this needs to be rolled back
  //overlayLocked = "no";

  // remember that we've just closed this overlay, so that it can't be opened again immediately
  window.functionOverlayJustClosed = unit;
  // but only remember for a fraction of a second
  setTimeout( "window.functionOverlayJustClosed = null;", 100 );

  // remove any close handler we had registered for this overlay
  globalOverlayClearCloseHandler( unit.id );
}

// move the shim, if it exists, to be behind a particular element, with optionally specified insets from the element's edges
function shimShowBehind( element, insets )
{
  var element = $( element );

  // if there were no insets provided, then just set them all to zero
  if( typeof( insets ) == "undefined" ){
    insets = { top: 0, left: 0, bottom: 0, right: 0 };
  }

  // the shim is only necessary for older versions of IE
  if( browser.isIE && (browser.versionMajor < 7) ) {
    // if this element doesn't already have a shim, then make a new one
    if( typeof( element.shim ) == "undefined" ){
      element.shim = document.createElement( "iframe" );
      element.shim.style.position = "absolute";
      element.shim.style.display = "none";
      element.shim.style.zIndex = "201";
      element.shim.style.filter = "alpha(opacity=0);";
      element.shim.frameBorder = "0";
      // The following src value fixes a problem with the page being loaded using
      // https.
      // For further information about this fix please go to the site below:
      // http://www.zachleat.com/web/2007/04/24/adventures-in-i-frame-shims-or-how-i-learned-to-love-the-bomb/
      element.shim.src = "javascript:false;";
      element.shim.scrolling = "no";
      element.shim.id = element.id + "Shim";
      document.body.appendChild( element.shim );
    }

  if(element.className == 'alternativeFlightsOverlay alternativeFlightsOverlayZindex')
    element.shim.style.zIndex = "199";

    var elementPosition = Position.cumulativeOffset( element );
    var elementWidth = Element.getWidth( element );
    var elementHeight = Element.getHeight( element );

    // set the size and position to be the same as the element, but inset by the given values
    element.shim.style.height = elementHeight - (insets.top + insets.bottom);
    element.shim.style.width = elementWidth - (insets.left + insets.right);
    element.shim.style.top = elementPosition[1] + insets.top;
    element.shim.style.left = elementPosition[0] + insets.left;
    element.shim.style.display = "block";
  }
}


function shimHide( element ){
  // make sure our arguments are nodes, not just IDs
  var element = $( element );
  if( (element != null) && (typeof( element.shim ) != "undefined") ){
    element.shim.style.display = "none";
  }
}

//detect click on document - hide visible overlays when click not on an open overlay (todo: or its connector, launcher etc)

document.onclick = function( event ) {
//alert("doc onclik");
  // make sure we get hold of the event in both firefox and IE style event handling
  if( !event ){ var event = window.event; }

  // amended to allow loaded iframes to close overlays onclick (iframe.js in the iframes will fake a click on x=0 and y=0)
  var x = 0;
  var y = 0;

  if (event != null) {
    // find out where the mouse was clicked in the document
    x = Event.pointerX( event );
    y = Event.pointerY( event );
  }
  // if there's a sticky currently open
  if( stickyLocked != "no" ){

    // get hold of the various elements of the sticky
    var stickyOverlay = currentSticky;
    var stickyOverlayParent = currentStickyParent;

    // see if the click was within any of the sticky's elements
    var insideSticky = Position.within( stickyOverlay, x, y );
    var insideStickyParent = Position.within( stickyOverlayParent, x, y );

    // if the click was outside all the sticky's elements then hide the sticky
    if( !insideSticky && !insideStickyParent ){
      stickyHide( currentSticky );
    }
  }

  if (searchWidgetStatus > 0)
            if (searchWidgetStatus == 1)
                searchWidgetStatus = 2;
            else
    searchWidgetHide();

  // if there's an overlay currently open
  if( overlayLocked != "no" ){
    // get hold of the elements of the overlay
    var searchUnit = $( overlayLocked );
    var overlay = $( overlayLocked + "Overlay" );
    var overlayConnector = $( overlayLocked + "OverlayConnector" );
    var faderPanel = $( overlayLocked + "Fade" );


    // see if the click was within any of the overlay elements
    var insideSearchUnit = Position.within( searchUnit, x, y );
    //alert(insideSearchUnit);
    var insideOverlay = Position.within( overlay, x, y );
    var insideOverlayConnector = Position.within( overlayConnector, x, y );
    var insidefaderPanel = Position.within( faderPanel, x, y );

    // if the click was outside all the overlay elements, then hide the overlay
    if( !insideSearchUnit && !insideOverlay && !insideOverlayConnector && !insidefaderPanel ){
      hideOverlay( overlayLocked );
    }
  }
}

// show/hide an alternative flights sticky
function alternativeFlightsOverlay( sticky, show, parentLink ){
  // make sure our arguments are nodes, not just IDs
  var sticky = $( sticky );
  var parentLink = $( parentLink );

  // do we need to store the sticky in this global variable?
  theStickyID = sticky;

  if( show == "yes" ){
    sticky.style.display = "block";
    sticky.style.position = "absolute";

    // find the best place on the page for the overlay, and position it accordingly
    var position = calculateBestPopupPosition( parentLink, sticky );
    positionElementFromPageOrigin( sticky, position.x, position.y );

    // put a shim behind for IE
    shimShowBehind( sticky, { top: 0, left: 3, bottom: 2, right: 2 } );

  } else if(show == "no"){
    sticky.style.display = "none";
    shimHide( sticky );
  }
}

function showHide(unitID, show){
  if(show == "yes"){
    $(unitID).style.display = "block";
  }
  else if(show == "no"){
    $(unitID).style.display = "none";
  }
}

function lockOption(optionsInstance, ecInstance){
  var expandCollapseInstance = ("expandCollapse" + ecInstance);
  var description = optionsInstance;
  var number = ecInstance;
  var element = ("scu" + optionsInstance + "Select");
  if($(element).selectedIndex != 0){
    $(expandCollapseInstance).innerHTML = "";
  } else {
    $(expandCollapseInstance).innerHTML = "<img src=\"/images/buttons/form/collapse.gif\" alt=\"Collapse\" title=\"click to collapse the " + description + " option\" width=\"11\" height=\"11\" border=\"0\" onclick=\"collapse('" + description + "', '" + number + "', '" + element + "'); hideElement('" + element + "');\" />";
  }
}

// initialise mouseover functions on the footer links to highlight the group of links on hover
function sfHover(){
  var footer = $( "Footer" );

  // don't try to do anything if we can't find the footer!
  if( footer == null ){ return; }

  var footerLinks = footer.getElementsByTagName( "LI" );

  // run through all the links in the footer
  for( var i = 0; i < footerLinks.length; i++ ){
    // set the link's mouse over to highlight it's parent UL
    footerLinks[i].onmouseover = function(){
      Element.addClassName( this.parentNode, "hov" );
    }
    // set the link's mouse out to unhighlight it's parent UL
    footerLinks[i].onmouseout = function(){
      Element.removeClassName( this.parentNode, "hov" );
    }
  }
}

var lastTab="collapsed";

function findSearchHolidayFeaturesMoreBlocks(){
  // find all DIVS with class "accommodationSection"
  return getElementsByClassName( $( "scuFeaturesOverlay" ), "*", "moreBlock" );
}

function collapseAll(){
  var moreBlocks = findSearchHolidayFeaturesMoreBlocks();
  for( var i=1; i <= moreBlocks.length; i++ ){
    var el = $("moreBlock"+i);
    var ellink = $("moreBlock"+i+"link");

    // if this block has a "more info" link, then make sure it is closed
    if( ellink != null ){
      el.style.display = "none";
      ellink.innerHTML = 'More info<img src="/images/buttons/arrow_down.gif" alt="" width="11" height="9" />';
      ellink.blur();
    }
  }
}

function switchBlocks(obj){
  collapseAll();
  if(lastTab != obj){
    var el = $("moreBlock"+obj);
    var ellink = $("moreBlock"+obj+"link");
    if( el.style.display != "block" ){
      el.style.display = "block";
      ellink.innerHTML = 'Less info<img src="/images/buttons/arrow_up.gif" alt="" width="11" height="9" />';
      ellink.blur();
      lastTab = obj;
    }
  } else {
    lastTab="collapsed";
  }
}

//get top
// Note: there's a Prototype function - Position.cumulativeOffset() that probably performs the same calculations
function findPos(obj)
{
  var curleft = curtop = 0;

  if (obj.offsetParent)
  {
    curleft = obj.offsetLeft;
    curtop = obj.offsetTop;

    while (obj = obj.offsetParent)
    {
      curleft += obj.offsetLeft;
      curtop += obj.offsetTop;
    }
  }

  return [curleft,curtop];
}


var stickyLocked="no";
var currentSticky="";
var currentStickyParent="";

function stickyShow( owner, sticky ) {
  // make sure our arguments are nodes, not just IDs
    var sticky = $( sticky );
  currentStickyParent = $( owner );

  // don't try doing anything if the sticky hasn't loaded yet
  if( sticky == null ){ return; }

  // close any other overlays that are open
  globalOverlayClose();

  sticky.style.display = "block";

  var position = calculateBestPopupPosition( owner, sticky );

  sticky.style.left = position.x + "px";
  sticky.style.top = position.y + "px";

  // position and show the shim, if it exists
  shimShowBehind( sticky, { top: 0, left: 3, bottom: 2, right: 2 } );

  // register a global close handler that can be called to close this overlay
  // in case we need to show another overlay
  globalOverlayRegisterCloseHandler( sticky.id, function(){ stickyHide( sticky.id ); } );

  stickyLocked = "yes";
  currentSticky = sticky;
}

function stickyHide( sticky ){
  // make sure our arguments are nodes, not just IDs
  var sticky = $( sticky );

  // don't try doing anything if the sticky hasn't loaded yet
  if( sticky == null ){ return; }

  // hide the sticky
  sticky.style.display = "none";

  // hide the shim, if it exists
  shimHide( sticky );

  // remove any close handler we had registered for this overlay
  globalOverlayClearCloseHandler( sticky.id );
}

// BLINKY FUNCTIONS ////////////////////////////////////////////////////

var blinkyLocked = "no";

// schedule a blinky to appear after a small delay
function blinkyShow( owner, blinky ){
  // make sure our arguments are nodes, not just IDs
  var owner = $( owner );
  var blinky = $( blinky );

  // if we can't find the blinky or its owner for some reason,
  // eg. during page load, then we can't do anything
  if( (owner == null) || (blinky == null) ){ return; }

  // ensure that our global array for remembering which parent a
  // blinky is attached to is created
  if( typeof( window.blinkyParentLookup ) == "undefined" ){
    window.blinkyParentLookup = {};
  }

  // make a note that the blinky will be attached to the particular parent
  window.blinkyParentLookup[blinky.id] = owner;

  // tell the blinky to really appear in half a second
  owner.blinkyHoverTimerID = setTimeout( "blinkyReallyShow('" + blinky.id + "')", 500 );

}

function blinkyReallyShow( blinky ){
  // make sure our arguments are nodes, not just IDs
  var blinky = $( blinky );

  // don't do anything if the blinky doesn't exist yet, eg. during page load
  if( blinky == null ){ return; }

  // find out which element this blinky was marked to appear against
  var blinkyOwner = window.blinkyParentLookup[blinky.id];

  // find the best place on the page for the blinky, and position it accordingly
  var position = calculateBestPopupPosition( blinkyOwner, blinky );
  positionElementFromPageOrigin( blinky, position.x, position.y );

  // close any other overlays that are open
  globalOverlayClose();

  // show the blinky
  blinky.style.display = "block";

  // position and show the shim, if necessary
  shimShowBehind( blinky, { top: 0, left: 3, bottom: 2, right: 2 } );

  // register a global close handler that can be called to close this overlay
  // in case we need to show another overlay
  globalOverlayRegisterCloseHandler( blinky.id, function(){ blinkyHide( blinky.id ); } );
}

// position an element at an offset from the page origin rather than its relative parent
function positionElementFromPageOrigin( element, x, y ){
  // make sure our arguments are nodes, not just IDs
  var element = $( element );

  // find out where the relative parent starts
  var offsetParent = Position.offsetParent( element );
  var offsetParentPosition = Position.cumulativeOffset( offsetParent );

  // convert the desired coordinates into an offset against the
  // relative parent
  var offsetX = x - offsetParentPosition[0];
  var offsetY = y - offsetParentPosition[1];

  element.style.left = offsetX + "px";
  element.style.top  = offsetY + "px";
}

// position an element in the centre of the browser window
function positionElementInViewportCentre( element ){
  var element = $( element );

  // get the viewport centre position and offset it by half the width
  // and height of the element
  var viewportCentre = getBrowserViewportCentre();
  var x = viewportCentre.x - Math.floor( Element.getWidth( element ) / 2 );
  var y = viewportCentre.y - Math.floor( Element.getHeight( element ) / 2 );

  positionElementFromPageOrigin( element, x, y );
}

function blinkyHide( blinky ) {
  // make sure our arguments are nodes, not just IDs
  var blinky = $( blinky );

  // only proceed if we have an actual blinky to work with
  if(blinky != null) {

    if( typeof( window.blinkyParentLookup) != "undefined" ){
      // find out which element this blinky was marked to appear against
      var blinkyOwner = window.blinkyParentLookup[blinky.id];

      if( typeof( blinkyOwner ) != "undefined" ){
        // if there was a timer on the parent to display the blinky, then clear it
        if( typeof( blinkyOwner.blinkyHoverTimerID ) != "undefined" ){
          clearTimeout( blinkyOwner.blinkyHoverTimerID );
        }
      }
    }

    blinky.style.display = "none";

    // hide the shim, if it exists
    shimHide( blinky );

    // remove any close handler we had registered for this overlay
    globalOverlayClearCloseHandler( blinky.id );

  }
}

////////////////////////////////////////////////////////////////////////

// safe object property access with default value when property is missing
function objectGetProperty( object, property, defaultValue ){
  if( property in object ){
    return object[property];
  } else {
    return defaultValue;
  }
}

// remove all a node's children
function removeAllChildNodes( parentNode ){
  // make sure our arguments are nodes, not just IDs
  var parentNode = $( parentNode );

  var childNodes = $A( parentNode.childNodes );

  childNodes.each( function( childNode ){
    parentNode.removeChild( childNode );
  } );
}

// because Prototype 1.4 pollutes the Object prototype with its own members,
// we need a safe way to extract keys from an object that don't come from the
// prototype
// Note: this has apparently been fixed in version 1.5 of the library
function safeGetHashtableKeys( hash ){
  var keys = new Array();

  // run through all the elements and ignore any that come from the prototype
  for( key in hash ){
    if( !(key in Object.prototype) ){
      keys.push( key );
    }
  }

  return keys;
}

// recursively search up the parent hierarchy for a matching node, starting at the given node
// returns "undefined" if none found
function findMatchingParent( element, tagName ){

  if( typeof( element ) == "undefined" ){
    return undefined;
  }
  else if( element.tagName == tagName ){
    return element;
  }
  else
  {
    return findMatchingParent( element.parentNode, tagName );
  }
}

// PANEL SHOW/HIDE FUNCTIONS ///////////////////////////////////////////

function panelHide( element ){
  Element.addClassName( element, "hidden" );
}

function panelShow( element ){
  Element.removeClassName( element, "hidden" );
}

// THROBBER FUNCTIONS //////////////////////////////////////////////////

function throbberInitialise( throbber ){
  // make sure our arguments are nodes, not just IDs
  var throbber = $( throbber );

  // show the throbber
  throbber.style.display = "block";
  throbber.style.width = "185px";

  // the following line has to appear AFTER the element has been displayed;
  // make the throbber completely transparent
  new Rico.Effect.FadeTo( throbber, 0, 0, 1 );
}

function throbberThrob( throbber ){
  // make sure our arguments are nodes, not just IDs
  var throbber = $( throbber );

  // make it the size of its parent DIV
  // do this each time because the parent size may change
  var parent = $( throbber.parentNode );
  var parentSize = Element.getDimensions( parent );

  throbber.style.width = parentSize.width + "px";
  throbber.style.height = parentSize.height + "px";

  // start the fades
// new Rico.Effect.FadeTo( throbber, 1, 0, 1 );
// new Rico.Effect.FadeTo( throbber, 1, 500, 1 );
// new Rico.Effect.FadeTo( throbber, 0, 1000, 20 );

  // define the fades, in reverse order so that we can chain them together
  var fade2 = function(){ new Rico.Effect.FadeTo( throbber, 0, 1000, 20 ); };
  var fade1 = function(){ new Rico.Effect.FadeTo( throbber, 1, 0, 1, { complete: fade2 } ); };

  // trigger the first fade in the sequence
  fade1();
}

// MOVED from searchPanel.js
function throbFadePanel( fadePanel )
{
  // make sure our arguments are nodes, not just IDs

  var fadePanel       = $( fadePanel );
  var fadePanelParent = fadePanel.up();

  // don't do anything if any of the elements can't be found, eg. during loading
  if( (fadePanelParent == null) || (fadePanelParent == null) ){ return; }

  fadePanel.style.display = "block";
  fadePanel.style.width = Element.getWidth( fadePanelParent ) + "px";
  fadePanel.style.height = Element.getHeight( fadePanelParent ) + "px";

  // define the fades, in reverse order so that we can chain them together
  var fadeClear = function(){ fadePanel.style.display = "none"; };
  var fade2 = function(){new Rico.Effect.FadeTo( fadePanel, 0, 1000, 20, { complete: fadeClear } ); };
  var fade1 = function(){new Rico.Effect.FadeTo( fadePanel, 1, 0, 1, { complete: fade2 } ); };

  // trigger the first fade in the sequence
  fade1();
}

// QUANTITY WIDGET FUNCTIONS ///////////////////////////////////////////

// converts a dropdown quantity option into a +/- quantity widget
// requires elements named:
//   <item>DropDown - the dropdown box to replace
//   <item>Quantity - the container to hold the new widget
function quantityWidgetCreate( item, showAvailability ){
  var quantityContainer = $( item + "Quantity" );
  var quantityDropDown = $( item + "DropDown" );

  // hide the drop-down list - it's only displayed when javascript not available
  Element.hide( quantityDropDown );

  // give the widget a class so that we can apply styling
  Element.addClassName( quantityContainer, "quantityWidget" );

  // and add in the quantity widget controls
  new Insertion.Bottom( quantityContainer, '<a href="#" class="quantityWidgetDecrement" id="' + item + 'Decrement"  onclick="itemAdjust( event, this, -1 ); return false;"><img id="' + item + 'DecrementImage" src="/images/buttons/form/collapse.gif" alt="Minus one" title="Minus one" /></a>' );
  new Insertion.Bottom( quantityContainer, '&nbsp;<span class="quantityWidgetQuantityDisplay" id="' + item + 'QuantityDisplay">' + quantityDropDown.value + '</span>&nbsp;' );
  new Insertion.Bottom( quantityContainer, '<a href="#" class="quantityWidgetIncrement" id="' + item + 'Increment" onclick="itemAdjust( event, this, +1 ); return false;"><img id="' + item + 'IncrementImage" src="/images/buttons/form/expand.gif" alt="Plus one" title="Plus one"" /></a>' );

  if( showAvailability ){
    new Insertion.Bottom( quantityContainer, '<div class="quantityAvailable">' + quantityWidgetGetLimit( item ) + ' available</div>' );
  }

  // set the display for the widget
  quantityWidgetUpdate( item );
}

function quantityWidgetSetValue( item, value ){
  var quantityDropDown = $( item + "DropDown" );

  if( (value >= 0) && (value < quantityDropDown.options.length) ){
    // set the hidden dropdown value
    quantityDropDown.selectedIndex = value;

    quantityWidgetUpdate( item );
  }
}

// get the currently selected quantity for an item
function quantityWidgetGetValue( item ){
  var quantityDropDown = $( item + "DropDown" );
  return quantityDropDown.selectedIndex;
}

// find the maximum quantity allowed for an item
function quantityWidgetGetLimit( item ){
  var quantityDropDown = $( item + "DropDown" );
  return quantityDropDown.options.length - 1;
}

function quantityWidgetUpdate( item ){
  var quantity = quantityWidgetGetValue( item );

  // update the display
  var quantityDisplay = $( item + "QuantityDisplay" );
  Element.update( quantityDisplay, quantity );

  var decrementEnabled = (quantity > 0);
  var incrementEnabled = (quantity < quantityWidgetGetLimit( item ) );

  $( item + "DecrementImage" ).src = "/images/buttons/form/" + (decrementEnabled ? "collapse.gif" : "collapse-greyed.gif");
  $( item + "DecrementImage" ).alt = (decrementEnabled ? "Minus one" : "");
  $( item + "DecrementImage" ).title = (decrementEnabled ? "Minus one" : "");
  $( item + "IncrementImage" ).src = "/images/buttons/form/" + (incrementEnabled ? "expand.gif" : "expand-greyed.gif");
  $( item + "IncrementImage" ).alt = (incrementEnabled ? "Plus one" : "");
  $( item + "IncrementImage" ).title = (incrementEnabled ? "Plus one" : "");
}


////////////////////////////////////////////////////////////////////////

// function to allow javascript to programmatically load a CSS stylesheet
function loadStylesheet( filename, media ){
  // ensure that the browser has all the DOM methods/properties we need
  if( !(document.getElementById) ||
    !(document.childNodes) ||
    !(document.createElement) ||
    !(document.getElementsByTagName) ){
    return;
  }

  // make a new link node to the stylesheet
  var link = document.createElement( "link" );
  link.href = filename;
  link.rel = "stylesheet";
  link.type = "text/css";
  link.media = media;

  // insert the stylesheet link into the document head
  document.getElementsByTagName('head')[0].appendChild(link);
}

// function to allow javascript to programmatically load a CSS stylesheet
function loadJavaScript( filename )
{
  // ensure that the browser has all the DOM methods/properties we need
  if( !(document.getElementById) ||
    !(document.childNodes) ||
    !(document.createElement) ||
    !(document.getElementsByTagName) ){
    return;
  }

  // make a new link node to the javascript file
  var link = document.createElement( "script" );
  link.src = filename;
  link.type = "text/javascript";

  // insert the stylesheet link into the document head
  document.getElementsByTagName('head')[0].appendChild(link);
}

// load listener to load multiple fns onload
function addLoadEvent( func )
{
  var doesExist = ( onLoadEventList.indexOf( func ) != -1 );

  if( !doesExist )
  {
    onLoadEventList.push(func);

    // if there isn't currently an onload function...
    if( typeof window.onload != "function" )
    {
      // ...then just set it to this one
      window.onload = func;
    }
    else
    {
      // the old onload function
      var oldOnloadFn = window.onload;
      // and make a new onload function that will call the old one, and then func
      window.onload = function()
      {
        if( oldOnloadFn )
        {
          oldOnloadFn();
        }

        func();
        onLoadEventList.pop( func );
      };
    }
  }
  else
  {
    // Used for debugging purposes.  Simply uncomment.
    //alert( 'Duplicate function call.  The following function has already been added:\n\n' + func);
  }
}


////////////////////////////////////////////////////////////////////////

// TOGGLE PANEL FUNCTIONS

function hideTogglePanel( toggleLink, panelDiv ){
  // make sure our arguments are nodes, not just IDs
  var toggleLink = $( toggleLink );
  var panelDiv = $( panelDiv );

  panelDiv.style.display = "none";
  Element.removeClassName( toggleLink, "arrow-link-up" );
  Element.addClassName( toggleLink, "arrow-link-down" );
}

function showTogglePanel( toggleLink, panelDiv ){
  // make sure our arguments are nodes, not just IDs
  var toggleLink = $( toggleLink );
  var panelDiv = $( panelDiv );

  panelDiv.style.display = "inline";//db - changed to inline from block to avoid ie6 doubled margins...
  Element.removeClassName( toggleLink, "arrow-link-down" );
  Element.addClassName( toggleLink, "arrow-link-up" );
}

function toggleTogglePanel( toggleLink, panelDiv ){
  var toggleLink = $( toggleLink );
  var panelDiv = $( panelDiv );

  if( Element.hasClassName( toggleLink, "arrow-link-down" ) ){
    showTogglePanel( toggleLink, panelDiv );
  } else {
    hideTogglePanel( toggleLink, panelDiv );
  }
}

////////////////////////////////////////////////////////////////////////

// NOTE: this function is needed because the Prototype version doesn't
// work in IE 5.5
/*
    Written by Jonathan Snook, http://www.snook.ca/jonathan
    Add-ons by Robert Nyman, http://www.robertnyman.com
*/
function getElementsByClassName(oElm, strTagName, strClassName){
    var arrElements = (strTagName == "*" && oElm.all)? oElm.all : oElm.getElementsByTagName(strTagName);
    var arrReturnElements = new Array();
    strClassName = strClassName.replace(/\-/g, "\\-");
    var oRegExp = new RegExp("(^|\\s)" + strClassName + "(\\s|$)");
    var oElement;
    for(var i=0; i<arrElements.length; i++){
        oElement = arrElements[i];
        if(oRegExp.test(oElement.className)){
            arrReturnElements.push(oElement);
        }
    }
    return (arrReturnElements)
}

////////////////////////////////////////////////////////////////////////
// VALIDATION FUNCTIONS
////////////////////////////////////////////////////////////////////////

// find the validation message node within a given validation container
function validationMessageFind( validationContainer ){
  // make sure our arguments are nodes, not just IDs
  var validationContainer = $( validationContainer );

  var nodes = getElementsByClassName( validationContainer, "*", "validationMessage" );
  if( nodes.length > 0 ){
    return $( nodes[0] );
  }
}


// display a message within a given validation container, and make it active
function validationMessageShow( validationContainer, message ){
  var validationMessageDiv = validationMessageFind( validationContainer );

  if( validationMessageDiv ){
    validationMessageDiv.update( message );
    Element.addClassName( validationContainer, "validationContainerActive" );
    Element.removeClassName( validationContainer, "validationContainerInactive" );
  }
}


// display a message within a given validation container, without making active
function validationMessageUpdate( validationContainer, message ){
  var validationMessageDiv = validationMessageFind( validationContainer );
  if( validationMessageDiv ){
    validationMessageDiv.update( message );
  }
}

// clear any messages within a given validation container and make it inactive
function validationMessageClear( validationContainer ){
  var validationMessageDiv = validationMessageFind( validationContainer );

  if( validationMessageDiv ){
    validationMessageDiv.update( "" );
    Element.addClassName( validationContainer, "validationContainerInactive" );
    Element.removeClassName( validationContainer, "validationContainerActive" );
  }
}

////////////////////////////////////////////////////////////////////////

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

// validation test functions

function validationTestNotBlank( value ){
  var value = stringTrim( value );

  return (value.length > 0);
}

function validationTestNumeric( value ){
  var value = stringTrim( value );

  // match one or more digits
  return /^\d+$/.test( value );
}

// Determines if string contains at least one letter (A-Z & a-z)
function validationTestHasLetters( value ) {

  var hasLetter = false;

  // find a letter in the string and set boolean
    for( var i = 0; i < value.length; ++i ){
      if (/^[A-Za-z]*$/.test(value.charAt(i)))
         hasLetter = true;
   }

   return hasLetter;
}

// Determine if first character is not a special character
function firstCharacterNotSpecial( value ) {

  if(value.length < 1)
    return false;
  else
    return !(/^[ \/\-#.,;:]*$/.test(value.charAt(0)))
}

function validationTestPasswordAllowedChars( value ){
  var value = stringTrim( value );
  var alphaTotal = 0;
  var spacesTotal = 0;

  // keep a total of how many letters exist in the password
  for( var i = 0; i < value.length; ++i ){
    if (/^[A-Za-z]*$/.test(value.charAt(i))){
     alphaTotal = alphaTotal + 1;
    }
    if (value.charAt(i) == " "){
     spacesTotal = spacesTotal + 1;
    }
  }

  // check to see that the password contains at least 1 letter but is not completely made up of letters and contains no spaces
  if ((alphaTotal > 0) && (spacesTotal == 0) && (alphaTotal < value.length)){
   return true;
  }
  else{
   return false;
  }
}

function validationTestCardSecurityCode( value )
{
   var value = stringTrim( value );
   var numDigits = getSelectedCardType() != null ? getSelectedCardType().securityNumLen : 3;
   var validated = false;

  if(value == "000")
    return false;

   // Only validate if a card type was selected
   if(numDigits != null) {
    var reg = new RegExp("\\d{" + numDigits + "}");
    validated = reg.test(value);
   }
   return validated;
}

// Validate issue number of 1 to 4 digits
function validationTestIssueNumber( value ) {

 return /^\d{1,4}$/.test( value );
}

// Validate its alphabetic
function validationTestNoSpecialCharacters( value ){
  var value = stringTrim( value );
  return /^[A-Za-z\ ]*$/.test( value );
}

function validationTestLength( minLength, maxLength ){

  // make a validation test that will check that the length is between the two bounds
  var testFunction = function( value ){
    var value = stringTrim( value );
    return (value.length >= minLength ) && (value.length <= maxLength);
  };

  return testFunction;
}

// Validate passenger name
function validationTestName( value ) {

 // If name contains at least one space at beginning, not valid
 if( value.length > 0 && value.charAt(0) == ' ')
    return false;
   // If name only contains special characters, this is not valid
   else if( /^[\-\'\ ]*$/.test( value ))
    return false;
   // Otherwise, validate against expected regular expression
   else
  return /^[A-Za-z\-\'\ ]*$/.test( value );
}

// Validate postal address
function validationTestAddress( value ) {
  return (validationTestHasLetters(value) && firstCharacterNotSpecial(value)) ? (/^[0-9A-Za-z \/\-#.,;:]*$/.test( value )) : false;
}

// Validate town/city
function validationTestTownCity( value ) {
  return validationTestHasLetters( value) ? (/^[A-Za-z0-9 \/\-#.,;:]*$/.test( value )) : false;
}

// Validate country
function validationTestCountry( value ) {
  return validationTestHasLetters( value) ? (/^[A-Za-z0-9 \/\-#.,;:]*$/.test( value )) : false;
}

// Validate post code
function validationTestPostCode( value ) {

   var value = stringTrim( value );
 value = value.toUpperCase();

 return /^([A-PR-UWYZ0-9][A-HK-Y0-9][AEHMNPRTVXY0-9]?[ABEHMNPRVWXY0-9]? {0,2}[0-9][ABD-HJLN-UW-Z]{2}|GIR 0AA)$/.test( value );
}

// Validate telephone number
function validationTestPhoneNumber( value ) {

  return /^[0-9 ]*$/.test( value );
}

function validationTestEmailAddress( value ){
  // get rid of any leading or trailing spaces
  var value = stringTrim( value );

  // make sure it only allows valid characters
  var usesValidChars = /^[0-9A-Za-z@\.\-_+]*$/.test( value );
  if( !usesValidChars ){ return false; }

  // make sure we have at least seven characters
  if( value.length < 7 ){ return false; }

  // make sure we have exactly one "@" symbol
  var atPosition = value.indexOf( "@" );
  if( atPosition == -1 ){ return false; }
  if( value.lastIndexOf( "@" ) != atPosition ){ return false; }

  // get the portions of the address before and after the @ sign
  var beforeAt = value.substr( 0, atPosition );
  var afterAt = value.substr( atPosition + 1 );

  // make sure we have something to the left of the @
  if( beforeAt.length < 1 ){ return false; }

  // find the last period after the @ sign
  var lastPeriodPos = afterAt.lastIndexOf( "." );
  if( lastPeriodPos == -1 ){ return false; }

  // fine the first period after the @ sign
  var firstPeriodPos = afterAt.indexOf( "." );
  if( firstPeriodPos == -1 ){ return false; }

  // make sure there are no dots side by side
  for(var i = 1; i < afterAt.length; i++) {
     if((afterAt.charAt(i) == '.') && (afterAt.charAt(i-1) == '.'))
       return false;
  }

  // get the portions of the domain before and after the final period
  var beforePeriod = afterAt.substr( 0, lastPeriodPos );
  var afterPeriod = afterAt.substr( lastPeriodPos + 1 );

  // get portion of domain after '@' and before first period
  var beforeFirstPeriod = afterAt.substr( 0, firstPeriodPos );

  // make sure domain name itself is at least two characters long
  if( beforeFirstPeriod.length < 2 ) { return false; }

  // make sure the top-level domain has two or three characters
  if( (afterPeriod.length < 2) || (afterPeriod.length > 3) ){ return false; }

  // make sure there are at least two characters between the @ and the final period
  if( beforePeriod.length < 2 ){ return false; }

  // if it passes all the above tests then return true
  return true;
}

// perform a validation test on a single input field and display a
// message if it fails
function validateSingleInput( input, validationContainer, test, message ){
  var input = $( input );
  var validationContainer = $( validationContainer );

  var validated = test( input.value );

  if( !validated ){
    validationMessageShow( validationContainer, message );
  }

  return validated;
}

// perform a validation test on a single input field without displaying a message
function validateSingleInputSilently( input, validationContainer, test ){
  var input = $( input );
  var validationContainer = $( validationContainer );

  var validated = test( input.value );

  return validated;
}

// perform a validation test on a checkbox which requires acceptance of a child checkbox
// and display a message if it fails
function validateConditionalCheckbox( selectorCheckbox, confirmationCheckbox, validationContainer, message ){
  var validationContainer = $( validationContainer );
  var validated = true;
  if( (selectorCheckbox.checked == true) && (confirmationCheckbox.checked == false)){
    validationMessageShow( validationContainer, message );
    validated = false;
  }
  return validated;
}

// display a pagetop warning listing fields failing validation as
// links to the field within the form
// validationLinks is an array of objects of the form { text: "Field name", anchor: "fieldAnchor" }
function validationPagetopWarningDisplay( warningBlock, validationLinks ){
  // make sure our arguments are nodes, not just IDs
  var warningBlock = $( warningBlock );

  // find the warning link list within the warning block
  var nodes = getElementsByClassName( warningBlock, "*", "warningValidationLinks" );
  if( nodes.length != 1 ){

    // bail out if we can't find the link list
    return;
  }
  var validationLinkList = $( nodes[0] );

  // clear the list
  removeAllChildNodes( validationLinkList );

  // construct the list of links
  for( var i = 0; i < validationLinks.length; ++i ){

    var link = document.createElement( "li" );
    var hrf='#' + validationLinks[i].anchor;
    if(validationLinks[i].field){
      hrf = 'javascript:formSetFocus(document.forms[0].' + validationLinks[i].field + ', \'#' + validationLinks[i].anchor + '\')';
    }

  new Insertion.Bottom( link, '<a href="' + hrf + '">' + validationLinks[i].text + '</a>' );
    validationLinkList.appendChild( link );
  }

  // display the warning block
  warningBlock.style.display = "block";

  // scroll to the top left of the page
  setBrowserScrollingOffsets( 0, 0 );
}

function formSetFocus(field, anchorLoc){
  window.location.href = anchorLoc;
  field.focus();
  if(field.type == "text")
    field.select();
}

// hide the pagetop validation warning block
function validationPagetopWarningHide( warningBlock ){
  var warningBlock = $( warningBlock );

  warningBlock.style.display = "none";
}

////////////////////////////////////////////////////////////////////////


//convert links with rel external to target _blank to maintain strict compat
//TODO REMOVE this is pants!!!  - JS shouldn't control styling
// The original code didn't work in FF3 the onclick event wasn't being picked up
// therefore it has been changed, albeit it is not terribly elegant.
function targetBlank() {
  if (!document.getElementsByTagName) return;

  var anchors = document.getElementsByTagName("a");
  for (var i=0; i<anchors.length; i++)
  {
    var anchor = $( anchors[i] );
    if (anchor.getAttribute("href") && anchor.getAttribute("rel") == "external")
    {
      anchor.title = "The following link opens in a new window";

      if(!anchor.hasClassName("noicon"))
      {
        anchor.innerHTML = '<img src="/images/icons/link_new_window.gif" width="10" height="10" alt="The following link opens in a new window" /> ' + anchor.innerHTML;
      }

      var theScript = 'JavaScript:void(openWindow("' + anchor.getAttribute('href') + '"));';

      anchor.setAttribute("href", theScript);

      // We don't want the target attribute as this will cause problems with the script.
      anchor.removeAttribute("target");
    }
  }
}

//end target blank

function openBlankWindow(e, href) {
    var win = window.open(href);
    if (win && win.focus) win.focus();
    YAHOO.util.Event.stopEvent(e);
}

function openWindow(href) {
    var win = window.open(href,"","width=700,height=600,scrollbars=yes,resizable=yes");
    if (win && win.focus) win.focus();
}

// MODAL OVERLAY FUNCTIONS /////////////////////////////////////////////

function getPageSize(){
  if( (typeof( window.innerHeight ) != "undefined") && (typeof( window.scrollMaxY ) != "undefined") ){

    yScroll = window.innerHeight + window.scrollMaxY;
    xScroll = window.innerWidth + window.scrollMaxX;
    var deff = document.documentElement;
    var wff = (deff&&deff.clientWidth) || document.body.clientWidth || window.innerWidth || self.innerWidth;
    var hff = (deff&&deff.clientHeight) || document.body.clientHeight || window.innerHeight || self.innerHeight;
    xScroll -= (window.innerWidth - wff);
    yScroll -= (window.innerHeight - hff);
  } else if (document.body.scrollHeight > document.body.offsetHeight || document.body.scrollWidth > document.body.offsetWidth){ // all but Explorer Mac
    yScroll = document.body.scrollHeight;
    xScroll = document.body.scrollWidth;
  } else { // Explorer Mac...would also work in Explorer 6 Strict, Mozilla and Safari
    yScroll = document.body.offsetHeight;
    xScroll = document.body.offsetWidth;
  }

  return {"height": yScroll, "width": xScroll};
}

function modalWindowOverlayShow(){
  var overlay = $( "ModalWindowOverlay" );

  // if the overlay doesn't already exist in the page, then make a new one
  if( overlay == null ){
    overlay = modalWindowCreateOverlay();
  }

  // find the dimensions of the page and the browser window
  var pageSize = getPageSize();
  var browserSize = getBrowserViewportDimensions();

  // make the overlay big enough to cover the page (and the browser window in case the page is smaller than the window)
  var overlayWidth = Math.max( pageSize.width, browserSize.width );
  var overlayHeight = Math.max( pageSize.height, browserSize.height );

  overlay.style.width = overlayWidth + "px";
  overlay.style.height = overlayHeight + "px";

  // show the overlay
  overlay.style.display = "block";

  // put a shim behind the overlay for IE
  shimShowBehind( overlay );
}

function modalWindowCreateOverlay(){
  var body = $( document.body );

  new Insertion.Bottom( body, '<div id="ModalWindowOverlay"></div>' );

  return $( "ModalWindowOverlay" );
}

function modalWindowOverlayHide(){
  var overlay = $( "ModalWindowOverlay" );
  overlay.style.display = "none";

  // remove the shim in IE
  shimHide( overlay );
}

// TAB CHANGING FUNCTIONS //////////////////////////////////////////////

function tabPanelsCacheAll( tabPanelClassName ){
  // as it may take a fraction of a second to do the getElementsByClassName() call,
  // we do it once and store the list as a global variable

  if( typeof( window.tabPanelCache ) == "undefined" ){
    window.tabPanelCache = {};
  }
  window.tabPanelCache[tabPanelClassName] = getElementsByClassName( document, "*", tabPanelClassName );
}

// get an array of all the tab panels on the page
function tabPanelsFindAll( tabPanelClassName ){
  // create the tab list cache if it doesn't already exist
  if( (typeof( window.tabPanelCache ) == "undefined") ||
    (typeof( window.tabPanelCache[tabPanelClassName] ) == "undefined") ){
    // find all DIVS with the specified class name
    tabPanelsCacheAll( tabPanelClassName );
  }

  return window.tabPanelCache[tabPanelClassName];
}

// find the first tab on the page and display its panel
function tabPanelsDisplayFirst( tabPanelClassName ){
  // get a list of all the tab panels and then just choose the first one
  var allTabPanels = tabPanelsFindAll( tabPanelClassName );
  var firstTab = $( allTabPanels[0].id + "Tab" );

  tabPanelsDisplay( firstTab, tabPanelClassName );
}

// display the panel for a given tab, and hide all other panels
function tabPanelsDisplay( tabToDisplay, tabPanelClassName ){

  var allTabPanels = tabPanelsFindAll( tabPanelClassName );

  // run through all the tab panels
  allTabPanels.each( function( tabPanel )
  {
    var currentTab = $( tabPanel.id + "Tab" );

    // show it if it's the selected tab
    if(currentTab)
    {
      if( currentTab.id == tabToDisplay.id )
      {
        tabPanel.style.display = "block";
        Element.addClassName( currentTab, "selected" );
      }
      else
      {
        // otherwise hide it
        tabPanel.style.display = "none";
        Element.removeClassName( currentTab, "selected" );
      }
    }
  });

  firefoxForceRerender();
}

/* utility - get quirks mode or not */
function returnCompatMode(){
}

// useful functions for hex colour string conversion ///////////////////

hexDigit = new Array("0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F");

function dec2hex( dec )
{
  return( hexDigit[dec>>4] + hexDigit[dec&15] );
}

function hex2dec( hex )
{
  return( parseInt( hex, 16 ) );
}

// convert an RGB colour to a CSS-style hex string
function colourRgbToHex( r, g, b )
{
  return "#" + dec2hex( r ) + dec2hex( g ) + dec2hex( b );
}

// create a linear list of hex colours that fade between two colours specified as RGB
function colourCreateFade( startColour, endColour, steps ){
  var colourSteps = new Array();

  var rDelta = (endColour.r - startColour.r) / (steps - 1);
  var gDelta = (endColour.g - startColour.g) / (steps - 1);
  var bDelta = (endColour.b - startColour.b) / (steps - 1);

  // run through each step
  for( var i = 0; i < steps; ++i ){
    // work out the colour at this step
    var r = startColour.r + (i * rDelta);
    var g = startColour.g + (i * gDelta);
    var b = startColour.b + (i * bDelta);

    colourSteps[i] = colourRgbToHex( r, g, b );
  }

  return colourSteps;
}

// expand/collapse container toggle ////////////////////////////////////

function expandCollapseContainerToggle( element ){
  var container = $( element );

  // if the provided node wasn't an "expandCollapseContainer" then assume
  // it was a child of the container, and find the first matching parent node
  if( !(Element.hasClassName( element, "expandCollapseContainer" )) ){
    container = Element.up( container, ".expandCollapseContainer" );
  }

  // don't try to do anything if we couldn't find a container
  if( container == null ){ return; }

  // toggle the class on the container
  if( Element.hasClassName( container, "expanded" ) ){
    Element.removeClassName( container, "expanded" );
  } else {
    Element.addClassName( container, "expanded" );
  }
}

// blinky/sticky positioning logic /////////////////////////////////////

// calculate the position of a popup (ie. blinky/sticky) next to its owner
// returns an (x,y) pixel offset from the page origin
function calculateBestPopupPosition( owner, popup ) {
  // make sure our arguments are nodes, not just IDs
  var owner = $( owner );
  var popup = $( popup );

  // get some useful measurements of the popup owner
  var ownerWidth = Element.getWidth( owner );
  var ownerHeight = Element.getHeight( owner );
  var ownerPosition = Position.cumulativeOffset( owner );
  var ownerLeft = ownerPosition[0];
  var ownerTop = ownerPosition[1];
  var ownerRight = ownerLeft + ownerWidth;
  var ownerBottom = ownerTop + ownerHeight;

  // middle of the popup owner
  var ownerCentreX = ownerLeft + Math.round( ownerWidth / 2 );
  var ownerCentreY = ownerTop + Math.round( ownerHeight / 2 );

  // how big is our popup?
  var popupWidth = Element.getWidth( popup );
  var popupHeight = Element.getHeight( popup );

  // find the position and size of the current browser viewport
  var viewportDimensions = getBrowserViewportDimensions();
  var scrollingOffsets = getBrowserScrollingOffsets();

  // use this information to find the edges of the viewport
  var viewportLeft = scrollingOffsets.x;
  var viewportTop = scrollingOffsets.y;
  var viewportRight = viewportLeft + viewportDimensions.width;
  var viewportBottom = viewportTop + viewportDimensions.height;

  // work out how much space we have in the viewport around our popup owner
  var spaceLeft = ownerLeft - viewportLeft;
  var spaceRight = viewportRight - ownerRight;
  var spaceTop = ownerTop - viewportTop;
  var spaceBottom = viewportBottom - ownerBottom;

  var x, y; // the position of the popup

  // will it fit to the right, top-aligned with its owner?
  if( (popupWidth < spaceRight) && (popupHeight < (spaceBottom + ownerHeight)) ) {
    x = ownerRight;
    y = ownerTop;
  } else if( popupWidth < spaceRight ){
    x = ownerRight;
    y = ownerCentreY - (popupHeight - spaceBottom);
    y = (y <= 0 ? 0 : y);
  } else if( (popupHeight < spaceTop) && (popupHeight > spaceBottom) ){
    x = ownerCentreX - Math.round( popupWidth / 2 );
    // if the popup goes over the right hand edge of the viewport, then move it left so that it doesn't
    if( x + popupWidth > viewportRight ){ x = viewportRight - popupWidth; }
    x = (x < 0 ? 0 : x);
    y = ownerTop - popupHeight;
  } else {
    x = ownerCentreX - Math.round( popupWidth / 2 );
    // if the popup goes over the right hand edge of the viewport, then move it left so that it doesn't
    if( x + popupWidth > viewportRight ){ x = viewportRight - popupWidth; }
    x = (x < 0 ? 0 : x);
    y = ownerBottom;
  }

  return { x: x, y: y };
}

function printImagePopup( image )
{
  // make sure our arguments are nodes, not just IDs
  var image = $( image );

  var printFrame = $( "PrintFrame" );

  //if we haven't already got an iframe for printing, then make a new one
  if( printFrame == null ){
    printFrame = document.createElement( "iframe" );
    printFrame.frameBorder = "0";
    printFrame.scrolling = "no"
    printFrame.id = "PrintFrame";
    printFrame.name = "PrintFrame";
    document.body.appendChild( printFrame );
  }

  // make sure the iframe is big enough to hold the photo
  printFrame.style.width = Element.getWidth( image ) + 50 + "px";
  printFrame.style.height = Element.getHeight( image ) + 50 + "px";
  printFrame.style.display = "block";

  var iframe = frames["PrintFrame"];
  var iframeDocument = iframeGetContentDocument( iframe );

  if( iframeDocument != null ){
    // write a HTML page containing a single image, and an onload handler
    // that will tell the iframe's parent when it's ready to print
    iframeDocument.open();
    iframeDocument.write( '<html>' );
    iframeDocument.write( '<head>' );
    iframeDocument.write( '<style media="screen">' );
    iframeDocument.write( 'body{ display: none; }' );
    iframeDocument.write( '</style>' );
    iframeDocument.write( '</head>' );
    iframeDocument.write( '<body onload="parent.printImageFrameLoadedHandler( self );">' );
    iframeDocument.write( '<img src="' + image.src + '" />' );
    iframeDocument.write( '</body>' );
    iframeDocument.write( '</html>' );
    iframeDocument.close();
  }
}

// get the content document node for a given iframe
function iframeGetContentDocument( iframe )
{
  if( iframe.contentDocument ){
    return iframe.contentDocument;
  } else if( iframe.contentWindow ){
    return iframe.contentWindow.document;
  } else if( window.frames[iframe.name] ){
    return window.frames[iframe.name].document;
  }

  return null;
}

// this is called by the iframe created by printImagePopup(), and is called when
// the iframe body has finished loading
function printImageFrameLoadedHandler( iframe )
{
  iframe.focus();
  iframe.print();
}

// FIREFOX RENDERING BUG FIX ////////////////////////////////////////

function firefoxForceRerender(){
  // we need to trigger the rerender slightly in the future to give
  // the rendering threads a chance to sort themselves out
  setTimeout( firefoxForceRerenderTrigger, 10 );
}

function firefoxForceRerenderTrigger(){
  if( !(browser.isGecko || browser.isSafari) ){ return; }
  // we need to do something to the page to trick Firefox into rerendering it
  // just create a new SPAN, insert it at the end of the document and then
  // immediately delete it again
  var dummyNode = document.createElement("span");
  document.body.appendChild( dummyNode );
  Element.remove( dummyNode );
}

// HELP FUNCTIONS ///////////////////////////////////////////////////

// find the help question that contains the specified element
function helpQuestionFindParentQuestion( element ){
  var element = $( element );

  return Element.up( element, ".helpDetailQuestion" );
}

// called when the feedback "yes" radio is clicked for a help question
function helpQuestionFeedbackYesHandler( event ){
  // find the question that the event was raised in
  var question = helpQuestionFindParentQuestion( Event.element( event ) );
  if( question == null ){ return; }

  // close the comments section
  helpQuestionFeedbackSetState( question, "close" );
}

// called when the feedback "no" radio is clicked for a help question
function helpQuestionFeedbackNoHandler( event ){
  // find the question that the event was raised in
  var question = helpQuestionFindParentQuestion( Event.element( event ) );
  if( question == null ){ return; }

  // open the comments section
  helpQuestionFeedbackSetState( question, "open" );
}

// called when the feedback "submit" button is clicked for a help question
function helpQuestionFeedbackSubmitHandler( event ){
  // find the question that the event was raised in
  var question = helpQuestionFindParentQuestion( Event.element( event ) );
  if( question == null ){ return; }

  // NOTE: AJAX call to submit the comment information should occur here

  // display the acknowlegdement
  helpQuestionFeedbackSetState( question, "acknowledge" );

  // don't actually submit the form as it's processed by AJAX instead
  return false;
}

// change the state of the help question feedback controls for a particular question
function helpQuestionFeedbackSetState( question, state ){
  // get hold of all the necessary elements
  var question = $( question );
  var buttons = Element.down( question, ".helpDetailQuestionFeedbackButtons" );
  var comments = Element.down( question, ".helpDetailQuestionFeedbackComments" );
  var acknowledgement = Element.down( question, ".helpDetailQuestionFeedbackAcknowledgement" );
  var submitButton = Element.down( buttons, ".helpDetailQuestionFeedbackButtonsSubmit" );

  // don't try to do anything if we couldn't find some of the elements
  if( (question == null) || (buttons == null) || (comments == null) || (acknowledgement == null) ){ return; }

  // show and hide the respective DIVs depending on which state is chosen
  switch( state ){
    case "close":
      buttons.style.display = "block";
      submitButton.style.display = "block";
      comments.style.display = "none";
      acknowledgement.style.display = "none";
      break;
    case "open":
      buttons.style.display = "block";
      submitButton.style.display = "none";
      comments.style.display = "block";
      acknowledgement.style.display = "none";
      break;
    case "acknowledge":
      buttons.style.display = "none";
      comments.style.display = "none";
      acknowledgement.style.display = "block";
      break;
  }

  firefoxForceRerender();
}

/////////////////////////////////////////////////////////////////////

// Safari doesn't select a radio button when you click on its label,
// but we can install click handlers on the labels to emulate this
function radioLabelClickHandler( event ){
  var event = event || window.event;

  // for safari, we have to manually trigger the click event on the radio
  if( browser.isSafari ){
    var label = Event.element( event );
    var radio = $( label.htmlFor );

    if( radio != null){ radio.click(); }
  }
}

/////////////////////////////////////////////////////////////////////

// date stamp function for print
function checkTime( i )// add zero to single digit dates etc
{
  if( i < 10 ){
    i = "0" + i;
  }
  return i
}

function datestamp(){
  var today = new Date();
  var date = checkTime(today.getDate());
  var h = checkTime(today.getHours());
  var m = checkTime(today.getMinutes());
  var y = today.getFullYear();

  var month = new Array(12);
  month[0]="Jan";
  month[1]="Feb";
  month[2]="Mar";
  month[3]="Apr";
  month[4]="May";
  month[5]="Jun";
  month[6]="Jul";
  month[7]="Aug";
  month[8]="Sep";
  month[9]="Oct";
  month[10]="Nov";
  month[11]="Dec";

  var theDatestamp = $( "printHeader" );

  if( theDatestamp != null ){
    theDatestamp.innerHTML = "Printed " + date + " " + month[today.getMonth()] + " " + y + ", " + h + ":" + m;
  }
}


// check whether the value in an input field has reached its maximum length and if so move to the next field
function inputFieldAutoTab( currentInput, nextInput ){
  var currentInput = $( currentInput );
  var nextInput = $( nextInput );

  if( typeof( currentInput.getAttribute ) == "undefined" ){ return; }

  // only allow a field to autotab once, otherwise there are usability problems if a user tries to
  // go back and edit a field
  if( (typeof( currentInput.alreadyAutotabbed ) != "undefined") && currentInput.alreadyAutotabbed ){ return; }

  if( currentInput.value.length == currentInput.getAttribute( "maxlength" ) ){
    nextInput.focus();
    currentInput.alreadyAutotabbed = true;
  }
}

// search widget show..
var searchWidgetStatus = 0;

function searchWidgetShow() {
  // close any other overlays that are open
  globalOverlayClose();

  var swidget = $('searchWidget');
  swidget.style.display = "block";
  searchWidgetStatus = 1;
  swidget.onclick = function() { searchWidgetStatus = 1; }
}

function searchWidgetHide() {
  var swidget = $('searchWidget');
  swidget.style.display = "none";
  searchWidgetStatus = 0;
}

function matchItemTitle(titleNumber) {
  if( typeof( titleNumber ) != "undefined"){
    $( "dockItemTitle" ).style.display = "inline";
    $( "dockItemTitle" ).innerHTML = $( "itemTitle" + titleNumber ).innerHTML;
  }
  else{
    $( "dockItemTitle" ).innerHTML = "";
    $( "dockItemTitle" ).style.display = "none";
  }
}

// Trim Function
function trim(str, chars) {
    return ltrim(rtrim(str, chars), chars);
}
// Left Trim
function ltrim(str, chars) {
    chars = chars || "\\s";
    return str.replace(new RegExp("^[" + chars + "]+", "g"), "");
}
//Right Trim
function rtrim(str, chars) {
    chars = chars || "\\s";
    return str.replace(new RegExp("[" + chars + "]+$", "g"), "");
}

/* Formats a number into pounds, including commas. e.g. 45000300.04 => £45,000,300.04 */
function formatNumberToCurrency( num, currencyEntity, showDecimal ) {

  if( typeof( showDecimal ) == "undefined" )
  {
    showDecimal = true;
  }

  var isNegative = false;
   num = num.toString().replace(/\\$|\\,/g,'');
   if( isNaN( num ) ) {
     num = "0";
   }

   if ( num < 0 ) {
     num = Math.abs( num );
      isNegative = true;
   }

   cents = Math.floor( ( num * 100 + 0.5 ) % 100 );
   num = Math.floor( ( num * 100 + 0.5 ) / 100 ).toString();

   if( cents < 10 ) {
     cents = "0" + cents;
   }

   for( i = 0; i < Math.floor( ( num.length - ( 1 + i ) ) / 3 ); i++) {
     num = num.substring( 0 ,num.length - ( 4 * i + 3 ) ) + num.substring( num.length - ( 4 * i + 3 ) );
   }

   var result = currencyEntity + num;

   if( showDecimal )
   {
      result += '.' + cents;
   }

   if( isNegative ) {
     result = "-" + result;
   }

   return result;
}

function gotoPayment()
{
  this.document.forms[0].submit();
}

function updatePageName(params)
{
  if (typeof(spPageName) != 'undefined')
  {
    params = params + "&pagename=" + spPageName;
  }
  return params;
}

///////// FUNCTIONS TO RE-POSITION OVERLAYS ON BROWSER WINDOW RESIZE (BUGZILLA #33314) ///////////

/* Updates the active overlay's position */
function updateOverlayPosition()
{
  // Show the overlay again to update its position
  showOverlay(getActiveOverlay());

  // Disable this event (re-enabled when window is resized)
  document.body.onmouseover = null;
}

/*This is for Detailed search results for test mode. */
function getDetails()
{
    url = "/fcfalcon/page/search/detailedresultspopup.page?popup=true";
    Popup(url,700,600,'scrollbars=yes');
}

/* This is for popup functionalities for Detailed search results. */
function Popup(popURL,popW,popH,attr)
{
   if (!popH) { popH = 350 }
   if (!popW) { popW = 600 }
   var winLeft = (screen.width-popW)/2;
   var winTop = (screen.height-popH-30)/2;
   var winProp='width='+popW+',height='+popH+',left='+parseInt(winLeft)+',top='+winTop+','+attr;
   popupWin=window.open('/static/dp/blank.html',"popupWindow","\'"+winProp+"\'");
   popupWin.close()
  if(popURL.indexOf("www.360travelguide.net")> -1)
  {
     popupWin=window.open(popURL,"popupWindowVideoTour",winProp);
  }
  else
  {
     popupWin=window.open(popURL,"popupWindow",winProp);
   }
     popupWin.window.focus()
}

// function for handling transparency with PNGs in Win IE 5.5 & 6.
function correctPNG()
{
  alert("in correctPNG()");

   var arVersion = navigator.appVersion.split("MSIE")
   var version = parseFloat(arVersion[1])
   if ((version >= 5.5) && (document.body.filters))
   {
     alert("is > 5.5 and uses filters");

      for(var i=0; i<document.images.length; i++)
      {
         var img = document.images[i]
         var imgName = img.src.toUpperCase()
         if (imgName.substring(imgName.length-3, imgName.length) == "png")
         {
            var imgID = (img.id) ? "id='" + img.id + "' " : ""
            var imgClass = (img.className) ? "class='" + img.className + "' " : ""
            var imgTitle = (img.title) ? "title='" + img.title + "' " : "title='" + img.alt + "' "
            var imgStyle = "display:inline-block;" + img.style.cssText
            if (img.align == "left") imgStyle = "float:left;" + imgStyle
            if (img.align == "right") imgStyle = "float:right;" + imgStyle
            if (img.parentElement.href) imgStyle = "cursor:hand;" + imgStyle
            var strNewHTML = "<span " + imgID + imgClass + imgTitle
            + " style=\"" + "width:" + img.width + "px; height:" + img.height + "px;" + imgStyle + ";"
            + "filter:progid:DXImageTransform.Microsoft.AlphaImageLoader"
            + "(src=\'" + img.src + "\', sizingMethod='scale');\"></span>"
            alert(strNewHTML);
            img.outerHTML = strNewHTML
            i = i-1
         }
      }
   }
}

/* fc-ecom Pop up script functions to be used with prototype */
/* These popup functions look for a class that has been added to the anchor tag 'popUpWindow500x300', it looks to match the 'popUpWindow' part of the class name as the '500x300' part can vary, we can use this part of the class to specify the dimensions of the pop up window. It also adds a class "newWindow" which will give the link some left padding and the new window logo */
function popUpEvents(){
  $$("a").each(function(a){
  if(a.className.match('popUpWindow')){
       var popUpWidth = a.className.substr(a.className.indexOf("w") + 1,3);/* Extract width from class name */
       var popUpHeight = a.className.substr(a.className.indexOf("x") + 1,3);/* Extract height from class name */
       a.addClassName("newWindow");/* Add "newWindow" class name for new window logo image and padding */
         a.onclick = function (){
         openPopUpWindow(this.getAttribute("href"),'','no','no',popUpWidth,popUpHeight);
         return false;
      }
       }
    });
}

function openPopUpWindow(theURL,winName,tb,mb,wWidth,wHeight) {
var screenWidth= screen.width;
var screenHeight=screen.height;
xPos=(screenWidth/2) - (wWidth/2)
yPos=(screenHeight/2) - (wHeight/2)
window.open(theURL,winName,"copyhistory=no,location=no,directories=no,status=yes,resize=no,resizable=no,scrollbars=yes,toolbar="+tb+",menubar="+mb+",width="+wWidth+",height="+wHeight+",left="+xPos+",top="+yPos );
}
/* fc-ecom Pop up script function*/


/* fc-ecom hide any element with this class if JS is enabled */
function nonJsDisplay (){
  $$(".nonJsDisplay").each(function(njde){
  njde.style.display = "none";
  });
}
/* fc-ecom hide any element with this class if JS is enabled */

/* fc-ecom A function to find any table with the class standardTable and add the class odd to every other row so that we can style it for stripy tables */
function stripeTables() {
if (!document.getElementsByTagName) return false;
var tables = document.getElementsByTagName("table")
  for (var i=0,len=tables.length; i<len; i++){
  if (!tables[i].className.match('standardTable')) continue;
  addTdYesNoSpan();
  var odd = false;
  var rows = tables[i].getElementsByTagName("tr");
    for (var j=0,jLen=rows.length; j<jLen; j++) {
      if (odd == true) {
      $(rows[j]).addClassName("odd");
      odd = false;
      } else {
      odd = true;
      }
    }
  }
}

function addTdYesNoSpan(){
  $$("td").each(function(tds){
    var yesRegEx = /^\s{0,}yes\s{0,}$/;
    var noRegEx = /^\s{0,}no\s{0,}$/;
    if(yesRegEx.test(tds.innerHTML.toLowerCase())){
    tds.innerHTML = '<span class="yes">yes</span>';
    }
    else{
      if(noRegEx.test(tds.innerHTML.toLowerCase())){
      tds.innerHTML = '<span class="no">no</span>';
      }
    }
  });
}

/* fc-ecom */

/* prepareJumpMenu  - handles destination drop down/Jump menu onsubmit event. The function will replace the form action with the value of the selects option. If JS is disabled the default action will occur. You simply need to add the id destination_select */
function prepareJumpMenu(){
 if (!document.getElementById) return false;
 if (!document.getElementById("jumpMenu")) return false;
 var jumpMenuForm = document.getElementById("jumpMenu");
  jumpMenuForm.onsubmit = function(){
   jumpMenuForm.action = document.getElementById("destination_select").value;
  }
}

function selectOption(selectElementId, valueToMatch)
{
  var selectId = $( selectElementId );

  if ( selectId != null )
  {
    var size = selectId.length;

    for ( var i = 0; i < size; i++ )
    {
      if ( selectId.options[i].value == valueToMatch )
      {
        selectId.options[i].selectedIndex = i;
        selectId.options[i].selected = true;

        break;
      }
    }
  }
}

// MOVEDFROM - initialiseEvents.js
function detectBrows(e)
{
   if (!e){e = window.event;}
   return e;
}

function whoAmI(e)
{
   var targ;
   if (!e) var e = window.event;
   if (e.target) targ = e.target;
   else if (e.srcElement) targ = e.srcElement;
   if (targ.nodeType == 3) // defeat Safari bug
      targ = targ.parentNode;

   return targ;
}

// A general function to attach a onclick event to an element and when the event is performed
// process it using the funcToCall which is the func
function attachClickEventToElement(element, funcToCall)
{
  element = $( element );

  if ( element )
  {
    element.onclick = function(e)
    {
      return funcToCall( detectBrows(e) );
    }
  }
}

function toggleDisplay(owner, sticky){
  // make sure our arguments are nodes, not just IDs
  var sticky = $( sticky );
  currentStickyParent = $( owner );
  
  // don't try doing anything if the sticky hasn't loaded yet
  if( sticky == null ){ return; }
  
  // close any other overlays that are open
  globalOverlayClose();
  
  sticky.style.display = "block"; 
}
