
/**
 * @version 1
 * @copyright Copyright Flight Centre Ltd. All rights reserved.
 * @created Tue Sep 21 15:24:19 2010
 */

/*___ FILE: "fcl.js" ___*/
/**
 * FCL Object
 *
 * @id              $Id: fcl.js 1117 2010-08-03 07:09:16Z blundenr $
 * @author          Ryan Blunden
 * @modifiedby      $LastChangedBy: blundenr $
 * @copyright       Copyright Flight Centre Ltd. All rights reserved.
 * @version         $Revision: 1117 $
 * @lastmodified    $Date: 2010-08-03 17:09:16 +1000 (Tue, 03 Aug 2010) $
 */
var FCL = {};


/*___ FILE: "fcl.settings.js" ___*/
/**
 * Provides default settings for various FCL widgets
 *
 * @id              $Id: fcl.settings.js 1118 2010-08-03 07:12:08Z blundenr $
 * @author          Ryan Blunden
 * @modifiedby      $LastChangedBy: blundenr $
 * @copyright       Copyright Flight Centre Ltd. All rights reserved.
 * @version         $Revision: 1118 $
 * @lastmodified    $Date: 2010-08-03 17:12:08 +1000 (Tue, 03 Aug 2010) $
 * @requires        FCL
 * @static
 * @class
 */
FCL.SETTINGS =
{
    /**
     * Name of this class (used for error handling and/or debugging purposes)
     * @type String
     */
    name: 'FCL.SETTINGS',

    /**
     * Default config variables when using SWFObject
     * @type Object
     */
    flashVars:
    {
        expressInstall: '/fcl/js/ext/swfobject/expressInstall.swf', // Trigger expressInstall by default
        minVersion: '9.0.0' // Minimum Flash Player version to target
    },

    /**
     * jQuery UI datepicker defaults
     * @type Object
     */
    datePicker:
    {
        showOn: 'both', // Show date picker if field or calendar image is clicked
        buttonImage: '/fcl/img/datepicker.gif', // Image to use for calendar image
        dateFormat: 'dd\/mm\/yy', // Custom Australian date format
        buttonText: 'Choose date', // Alt and title text for calendar image
        buttonImageOnly: true // Don't set calendar image to display on button, use image only
    }
};


/*___ FILE: "fcl.util.js" ___*/
;(function($)
{
    /**
     * General utility functions
     * @id              $Id: fcl.util.js 1502 2010-09-06 00:37:06Z blundenr $
     * @author          Ryan Blunden
     * @modifiedby      $LastChangedBy: blundenr $
     * @copyright       Copyright Flight Centre Ltd. All rights reserved.
     * @version         $Revision: 1502 $
     * @lastmodified    $Date: 2010-09-06 10:37:06 +1000 (Mon, 06 Sep 2010) $
     * @requires        FCL
     * @class
     * @static
     */
    FCL.UTIL =
    {
        /**
         * Name of this class (used for error handling and/or debugging purposes)
         * @type String
         */
        name: 'FCL.UTIL',

        /**
         * Define jQuery api for some of the FCL.UTIL methods
         */
        init: function()
        {
            $.fn.fclExternalLinks = function()
            {
                return FCL.UTIL.externalLinks(this);
            };

            $.fn.fclTruncateText = function(settings)
            {
                settings = settings||{};
                return FCL.UTIL.truncateText(this, settings);
            };

            String.prototype.fclFormat = this.format;
        },

        /**
         * Launch a popup window
         * TODO Test if popup blocked
         * @param {String} url
         * @param {String} [name="popup"] Name of popup window
         * @param {Interger} [width="380"] Width of popup window
         * @param {Integer} [height="220"] Height of popup window
         */
        popup: function(url, windowName, width, height)
        {
            var name = (typeof windowName == 'undefined') ? 'popup' : windowName;
            var width = (typeof width == 'undefined') ? 380 : width;
            var height = (typeof height == 'undefined') ? 220 : height;
            var win = window.open(url, windowName, 'toolbar=no,location=no,directories=no,status=yes,menubar=no,scrollbars=yes,resizable=yes,copyhistory=no,width='+width+',height='+height);
            win.focus();
            return win;
        },

        /**
         * Validate an email address
         * @param {String} email
         * @returns {Boolean}
         */
        isEmailValid: function(email)
        {
            // By Scott Gonzalez: http://projects.scottsplayground.com/email_address_validation/
            return /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i.test(email);
        },

        /**
         * Validate an Australian post code
         * @param {String} postCode
         * @returns {Boolean}
         */
        isValidPostCode: function(postCode)
        {
            postCode = postCode.toString().match(/\d+/g)[0];
            if(postCode.length !== 4 || parseInt(postCode, 10) < 0 || parseInt(postCode, 10) >= 9000)
            {
                return false
            }

            return true;
        },

        /**
         * Return amount in Australian currency format
         * @param {Float}
         * @returns {String}
         */
        formatCurrency: function(amount)
        {
            var amount, amountArray;

            amount = amount.toString().match(/[\d\.]+/)[0];
            amountArray = [];

            // If amount has a decimal, add this to the array and extract it from amount
            if(amount.indexOf('.') > -1)
            {
                amountArray.unshift(amount.substring(amount.indexOf('.')));
                amount = amount.substring(0, amount.indexOf('.'));
            }

            // Split the amount into array elements every three numbers
            for(var i=amount.length; i>0; i-=3)
            {
                amountArray.unshift(amount.substring(i, i-3));
            }

            // Put it all back together again (replacing ,. is for numbers with decimals)
            return amountArray.join(',').replace(',.', '.');
        },

        /**
         * Return object with query string variables as object properties
         * @param {String} [strUrl=window.location.search]
         * @param {Stsring} [param="getVariable"] Use if you're only after one specific query string variable (Use FCL.UTIL.getUrlVar if this is the case)
         * @returns {Object or String} What gets returned depends on whether entire collection is returned or just one variable.
         * Returns an empty string if no query string variables are found.
         */
        getUrlVars: function(strUrl, param)
        {
            var queryString='', urlVars='', urlObj={};

            if (strUrl == '' || typeof strUrl == 'undefined')
            {
                queryString = window.location.search;
            }
            else
            {
                queryString = strUrl;
            }

            // if no URL parameters exist, return null
            if (queryString == '')
            {
                return '';
            }

            // remove '?'
            urlVars = queryString.substring(1).split('&');

            for(var i=0; i<urlVars.length; i++)
            {
                var urlVar = urlVars[i].split('=');
                urlObj[urlVar[0]] = decodeURIComponent(urlVar[1]);
            };

            if(typeof param != 'undefined')
            {
                if(typeof urlObj[param] != 'undefined')
                {
                    return urlObj[param];
                }
                else
                {
                    return '';
                }
            }

            return urlObj;
        },

        /**
         * Get a single query string variable
         * @param {String} key Query string variable name
         * @returns {String} Query string variable if exists or empty string if not
         */
        getUrlVar: function(key)
        {
            return this.getUrlVars('', key);
        },

        /**
         * Attempt to log an expression to the console
         * @param {Mixed} expression
         */
        log: function(expression)
        {
            try
            {
                console.log(expression);
            }
            catch(e){};
        },

        /**
         * Takes a camelCased string and breaks it into it's word components.
         * Taken from http://4umi.com/web/javascript/camelcase.php
         * @param {String}
         * @returns {String}
         */
        camelCaseSplit: function(str)
        {
            var s = this.trim(str);
            return ( /\S[A-Z]/.test( s ) ) ?
                s.replace(/(.)([A-Z])/g, function(t,a,b)
                { return a + ' ' + b.toLowerCase(); }) :
                s.replace(/( )([a-z])/g, function(t,a,b) { return b.toUpperCase(); });
        },

        /**
         * Break a camel cased string apart, separating each word with a space and capitalising each word
         * @param {String} str
         * @returns {String}
         */
        titleCaps: function(str)
        {
            var numbers, parts, result;

            /* There is a bug with the FCL.UTIL.titleCaps which doesn't apply
             * capitalisation if a number is present. Therefore, we convert all
             * numbers to @ symbols, feed the string into the functions, then
             * replace the @ symbols with the original number values before
             * returning result.
             */

            // Replace numbers with @ symbol
            numbers = str.match(/\d+/g);
            str = str.replace(/\d+/g, '@');

            parts = FCL.UTIL.camelCaseSplit(str);
            result = FCL.UTIL._titleCaps(parts);

            // Replace @ symbols numbers
            if(numbers === null) { return result; }

            for(var i=0; i<numbers.length; i++)
            {
                result = result.replace(/@/, numbers[i]);
            }

            return result;
        },

        /**
         * Takes a string as the first argument with n arguments after with which to perform variable substitution
         * @param {String} String for formatting
         * @returns {String}
         * @example FCL.UTIL.format('My name is {1} {2}', 'Ryan', 'Blunden');
         */
        format: function()
        {
            var args = [].slice.call(arguments);
            if(this.toString() != '[object Object]')
            {
                args.unshift(this.toString());
            }

            var pattern = new RegExp('{([1-' + args.length + '])}','g');
            return String(args[0]).replace(pattern, function(match, index) { return args[index]; });
        },

        /**
         * Replace newline characters with <br /> tags
         * @param {String} str
         * @returns {String}
         */
        newLineToBr: function(str)
        {
            return str.replace(/(\r\n|[\r\n])/g, '<br />');
        },

        /**
         * Smart truncate text function which by default, searches for position last instance of stop
         * character within maximum string length at which to truncate string. String will be truncated
         * at max length (len) with finish character (finish) if "ignoreStopChar" is set to true, or if
         * stop character (stopChar is not found within max length (len) of string.
         *
         * Nothing is returned from this function as it replaces existing text with truncated version of
         * each element
         *
         * @param {jQuery} $el jQuery element collection
         * @param {Object} [settings] Truncate text settings
         * @param {Integer} [settings.maxLength=180] Max length of string before truncating
         * @param {String} [settings.finish="&hellip;"] String to append after tuncating
         * @param {String} [settings.stopChar="."] Character to search for and stop at if within string length (len)
         * @param {Boolean} [settings.ignoreStopChar=false] If set to true, string is truncated at len (or less) with finish character regardless of existence of stopchar
         */
        truncateText: function($e, settings)
        {
            if($e.length == 0)
            {
                return $e;
            }

            var defaults =
            {
                maxLength: 180,
                finish: '&hellip;',
                stopChar: '.',
                ignoreStopChar: false
            };

            settings = (typeof settings != 'undefined') ? $.extend(defaults, settings) : defaults;

            return $e.each(function()
            {
                var text = $.trim($(this).text()).replace(/[\n\r\t]+/gm, ' ').replace(/\.[ ]+([A-Z0-9a-z]+)/gm, '. $1').replace(/\.([a-zA-Z0-9]+)/gmi, '. $1')
                var position = settings.maxLength;
                var prevStrPos = 0;
                var search = true;
                var count = 0;

                while(search)
                {
                    // Find position of stop character
                    var strPos = prevStrPos + text.substring(prevStrPos).indexOf(settings.stopChar);

                    // If the prev stop char position and the new position are the same, then we're not finding anything new so break
                    if(strPos >= settings.maxLength || (prevStrPos - 1) == strPos || count > 10)
                    {
                        search = false;
                        break;
                    }

                    // If position of stop char is less than max length (len), then change position
                    if(strPos < settings.maxLength)
                    {
                        position = strPos;
                        prevStrPos = position + 1;
                    }
                }

                var truncatedString = (settings.ignoreStopChar) ? text.substring(0, settings.maxLength).replace('&hellip;', '').replace('…', '') : text.substring(0, position);
                
                // If stop position is found before max length, display truncated string without finish as it's a complete sentence
                // unless ignoreStopChar is set to true
                if(position != settings.maxLength)
                {
                    $(this).html(truncatedString + settings.stopChar);
                    if(settings.ignoreStopChar)
                    {
                        $(this).html($(this).html().substring(0, $(this).html().length-1) + settings.finish);
                    }
                }
                else
                {
                    // if stop char not found and length of text is less than max length, append finish to truncated string
                    if(truncatedString.indexOf(settings.stopChar) == -1 && text.length < settings.maxLength)
                    {
                        $(this).html(truncatedString + settings.finish);
                    }
                    // We're stuck most likely in the middle of a word so back track until we find the first space char, append finish to truncated string
                    else
                    {
                        var spaceCharPos = truncatedString.lastIndexOf(' ');
                        $(this).html(truncatedString.substring(0, spaceCharPos) + settings.finish);
                    }
                }



                var html = $(this).html();
                var lastChar = html.charAt(html.length - 2);
                if(lastChar == settings.stopChar || lastChar == ' ')
                {
                    $(this).html(html.substring(0, html.length-2) + settings.finish);
                }

                return $(this);
            });
        },

        /**
         * Strip out all potentially harmful characters from an input field
         * @param {String} str
         * @returns {String}
         */
        filterInputText: function(str)
        {
            try
            {
                return str.replace(/\s+/gm, ' ').match(/[a-zA-Z0-9\(\)"\', \.!\/:%@&\?\+_=\-\$]+/gm).join('');
            }
            catch(e)
            {
                return '';
            }
        },

        /**
         * Grab the title and break url down into segments to fill out the the default set of Web Trends
         * variables for each page.
         */
        automateWebTrendsVars: function()
        {
            var template = '<meta name="{1}" content="{2}" />';
            var $head = $('head');
            var metaKeys = ['WT.ti', 'WT.cg_n', 'WT.cg_s', 'WT.seg_2'];
            var metaProps  = window.location.pathname.split('/');
            metaProps[0] = document.title;

            for(var i=0; i<metaKeys.length; i++)
            {
                if($head.find('meta[name="'+ metaKeys[i] +'"]').length == 0 && typeof metaProps[i] != 'undefined')
                {
                    $head.append(this.format(template, metaKeys[i], metaProps[i]));
                }
            }
        },

        /**
         * Dynamically register a Google Analytics page view. Default usage is for general enquiries.
         * @param {String} Page view path
         */
        registerGAPageView: function(path)
        {
            try
            {
                window.pageTracker._trackPageview(path);
            }
            catch(e){/* Will Fail if GA script is not on this page */}
        },

        /**
         * Trim function for use if jQuery is not around. Otherwise, just use $.trim
         * @param {String} str
         * @returns {String}
         */
        trim: function(str)
        {
            return str.replace(/^\s+|\s+$/g, '');
        },

        /**
         * Removes non word characters and turns whitespace into hyphens
         * @param  {String} str
         * @returns {String}
         * @deprecated
         */
        sluggify: function(str)
        {
            return this.urlify(str);
        },

        /**
         * Removes non-word characters and turns whitespace into hyphens
         *
         * @param   {String} str
         * @returns {String}
         */
        urlify: function(str)
        {
            return str.replace(/^\s+|\s+$/g, '').replace(/[^a-zA-Z0-9 ]+/g, '').replace(/ /g, '-').toLowerCase();
        },

        /**
         * Return string determine what environment we're running in
         * @returns {String} Returns "development", "staging" or "production"
         */
        getEnv: function()
        {
            var host = window.location.host.substring(0, window.location.host.indexOf('.'));
            var env = '';

            switch(host)
            {
                case 'int':
                    env = 'development';
                    break;

                case 'stage':
                    env = 'staging';
                    break;

                case 'newstage':
                    env = 'staging';
                    break;

                case 'www':
                    env = 'production';
                    break;

                default:
                    env = 'production';
                    break;
            }

            return env;
        },

        /**
         * Load a JavaScript file from any domain with the option of passing in a callback to be executed on load
         * @param {String} path Path to JavaScript file
         * @param [method] Callback to executed on script load
         */
        getJS: function(path, method)
        {
            $(function()
            {
                $.getScript(path, function(data)
                {
                    if(typeof method == 'string')
                    {
                        eval(method);
                    }

                    if(typeof method == 'function')
                    {
                        method();
                    }
                });
            });
        },

        externalLinks: function($el)
        {
            return $el.bind('click', function(e)
            {
                e.preventDefault();
                window.open($(this).attr('href'));
            });
        },

        /**
         * Handle JavaScript errors gracefully so they don't show up in production but provide useful information
         * when in development or staging environments, or if 'debug=True' is found in the query string
         * Extended by Ryan Blunden - 2 July 2010
         * Original by Nicholas C. Zakas - http://www.nczonline.net/blog/2009/04/28/javascript-error-handling-anti-pattern/
         * License: http://www.opensource.org/licenses/mit-license.php
         */
        handleErrors: function(object)
        {
            var func, method;

            for(func in object)
            {
                method = object[func];
                if(typeof method == 'function')
                {
                    object[func] = function(func, method)
                    {
                        return function()
                        {
                            try
                            {
                                return method.apply(this, arguments);
                            }
                            catch(e)
                            {
                                var objectName, errorMessage, lineNumber;
                                objectName = (typeof object.name != 'undefined') ? object.name + '.' : '';
                                lineNumber = (typeof e.lineNumber != 'undefined') ? ' (line number ' + e.lineNumber + ')' : '';
                                errorMessage = 'ERROR: '+ objectName + func + "(): " + e.message + lineNumber;

                                FCL.UTIL.log(errorMessage);
                                if(FCL.UTIL.getUrlVar('debug').toLowerCase() == 'true' || FCL.UTIL.getEnv() == 'development')
                                {
                                    alert(errorMessage);
                                }
                            }
                        };
                    }(func, method);
                }
            }
        },

        /**
         * Alert error message if we're not in production
         * @param {String} message Error message
         */
        throwError: function(message)
        {
            var env = this.getEnv();
            if(env == 'staging' || env == 'development')
            {
                alert('ERROR: '+ message);
            }
        }
    };

    /**
     * Title Caps
     *
     * Ported to JavaScript By John Resig - http://ejohn.org/ - 21 May 2008
     * Original by John Gruber - http://daringfireball.net/ - 10 May 2008
     * License: http://www.opensource.org/licenses/mit-license.php
     */
    (function()
    {
        var small = "(a|an|and|as|at|but|by|en|for|if|in|of|on|or|the|to|v[.]?|via|vs[.]?)";
        var punct = "([!\"#$%&'()*+,./:;<=>?@[\\\\\\]^_`{|}~-]*)";

        /**
         * Apply title caps to the supplied string
         * @param {String} title
         * @returns {String}
         */
        FCL.UTIL._titleCaps = function(title)
        {
            var parts = [], split = /[:.;?!] |(?: |^)["Ò]/g, index = 0;

            while (true)
            {
                var m = split.exec(title);

                parts.push( title.substring(index, m ? m.index : title.length)
                    .replace(/\b([A-Za-z][a-z.'Õ]*)\b/g, function(all)
                    {
                        return /[A-Za-z]\.[A-Za-z]/.test(all) ? all : upper(all);
                    })
                    .replace(RegExp("\\b" + small + "\\b", "ig"), lower)
                    .replace(RegExp("^" + punct + small + "\\b", "ig"), function(all, punct, word)
                    {
                        return punct + upper(word);
                    })
                    .replace(RegExp("\\b" + small + punct + "$", "ig"), upper));

                index = split.lastIndex;

                if(m) parts.push(m[0]);
                else break;
            }

            return parts.join("").replace(/ V(s?)\. /ig, " v$1. ")
                .replace(/(['Õ])S\b/ig, "$1s")
                .replace(/\b(AT&T|Q&A)\b/ig, function(all){
                    return all.toUpperCase();
                });
        };

        function lower(word)
        {
            return word.toLowerCase();
        }

        function upper(word)
        {
          return word.substr(0,1).toUpperCase() + word.substr(1);
        }
    })();

    FCL.UTIL.handleErrors(FCL.UTIL);
    FCL.UTIL.init();
    
})(jQuery);


/*___ FILE: "fcl.datetime.js" ___*/
;(function($)
{
    /**
     * Date and time functions
     *
     * @id              $Id: fcl.datetime.js 1587 2010-09-14 05:01:19Z blundenr $
     * @author          Ryan Blunden
     * @modifiedby      $LastChangedBy: blundenr $
     * @copyright       Copyright Flight Centre Ltd. All rights reserved.
     * @version         $Revision: 1587 $
     * @lastmodified    $Date: 2010-09-14 15:01:19 +1000 (Tue, 14 Sep 2010) $
     * @requires        FCL, FCL.UTIL
     * @class
     * @static
     */
    FCL.DATETIME =
    {
        /**
         * Name of this class (used for error handling and/or debugging purposes)
         * @type String
         */
        name: 'FCL.DATETIME',

        init: function()
        {
            Date.prototype.fclFormat = this.format;
        },

        /**
         * Return today's date in dd/mm/yyyy format
         * @returns {String} Date in dd/mm/yyyy format
         */
        todaysDate: function()
        {
            return this.futureDateDays(0);
        },

        /**
         * Return tomorrow's date in dd/mm/yyyy format
         * @returns {String} Date in dd/mm/yyyy format
         */
        tomorrowsDate: function()
        {
            return this.futureDateDays(1);
        },

        /**
         * Return date 7 days from now in dd/mm/yyyy format
         * @returns {String} Date in dd/mm/yyyy format
         */
        weekFromToday: function()
        {
            return this.futureDateDays(7);
        },

        /**
         * Return the first day of the next month
         * @returns {String} Date in dd/mm/yyyy format
         */
        firstDayNextMonth: function()
        {
            var today = new Date();
            nextMonth = new Date(today.getFullYear(), today.getMonth() + 1, 1);
            nextMonth.getDate() +'/'+ (nextMonth.getMonth() + 1) +'/'+ nextMonth.getFullYear();
            return this.leadingZero(nextMonth.getDate()) +'/'+ this.leadingZero(nextMonth.getMonth() + 1) +'/'+ nextMonth.getFullYear();
        },

        /**
         * Returns x number of dates date in the future in dd/mm/yyyy format
         * @param {Integer} days Number of days into the future
         * @returns {String} Date in dd/mm/yyyy format
         */
        futureDateDays: function(days)
        {
            var futureDate = new Date();
            futureDate.setDate(futureDate.getDate() + days);
            return this.leadingZero(futureDate.getDate()) +'/'+ this.leadingZero(futureDate.getMonth() + 1) +'/'+ this.leadingZero(futureDate .getFullYear());
        },

        /**
         * Return the current time in HHMM format
         * @returns {String} Time in HHMM (e.g. 23:12) format
         */
        timeHHMM: function()
        {
            var today = new Date();
            return this.leadingZero(today.getHours()) + this.leadingZero(today.getMinutes());
        },

        /**
         * Return the current time in HHMMSS format
         * @returns {String} Time in HHMMSS (e.g. 23:12:33) format
         */
        timeHHMMSS: function()
        {
            var today = new Date();
            return this.leadingZero(today.getHours()) +':'+ this.leadingZero(today.getMinutes()) +':'+ this.leadingZero(today.getSeconds());
        },

        /**
         * Takes a date string in Australian format and returns date string in US format
         * @param {String} dateStr Date in dd/mm/yyyy format
         * @param {String} [separator="-"] separator character in return date string
         * @returns {String} date in mm/dd/yyyy format
         */
        convertUSFormat: function(dateStr, separator)
        {
            var separator = (typeof(separator) == 'undefined') ? '-' : separator;
            var re = new RegExp('([0-9]{2})/([0-9]{2})/([0-9]{4})', 'm');
            var matches = re.exec(dateStr);
            return matches[2] + separator + matches[1] + separator + matches[3];
        },

        /**
         * Convert date in mm/dd/yyyy format and return in dd-mm-yyyy format (depending upon separator)
         * @param {String} dateStr Date in mm/dd/yyyy format
         * @param {String} [separator="-"] Separator character in return date string
         * @returns {String} Date in mm-dd-yyyy format (presuming "-" is separator character)
         */
        convertUStoAUSDate: function(dateStr, separator)
        {
            var separator = (typeof(separator) == 'undefined') ? '-' : separator;
            var re = new RegExp('([0-9]{2})/([0-9]{2})/([0-9]{4})', 'm');
            var matches = re.exec(dateStr);
            return matches[2] + separator + matches[1] + separator + matches[3];
        },

        /**
         * Return whether the supplied date components form the expected date
         * @param {String} year
         * @param {String} month
         * @param {String} day
         * @returns {Boolean} True if the date components match the date values in Date object
         */
        isValidDate: function(year, month, day)
        {
            var dt = new Date(parseInt(year, 10), parseInt(month, 10)-1, parseInt(day, 10));
            if(dt.getDate() != parseInt(day, 10) || dt.getMonth() != (parseInt(month, 10)-1) || dt.getFullYear() != parseInt(year, 10))
            {
                return false;
            }

            return true;
        },

        /**
         * Takes a date object and returns in yyyymmdd format
         * @param {Date Object} dateObj
         * @returns {String} Date in yyyymmdd format
         */
        dateToYYYYMMDD: function(dateObj)
        {
            return  (dateObj.getFullYear() + this.leadingZero(dateObj.getMonth() + 1) + this.leadingZero(dateObj.getDate())).toString();
        },

        /**
         * Takes a date object and returns in ddmmyyyy format
         * @param {Date Object} dateObj
         * @returns {String} Date in ddmmyyyy format
         */
        dateToDDMMYYYY: function(dateObj)
        {
            return  (this.leadingZero(dateObj.getDate()) + this.leadingZero(dateObj.getMonth() + 1) + dateObj.getFullYear()).toString();
        },

        /**
         * Takes a date string in dd/mm/yyyy format
         * @param {String} dateString Date in dd/mm/yyyy format
         * @returns {Date Object} Returns false if date sring is invalid
         */
        stringToDate: function(dateString)
        {
            try
            {
                var matches = dateString.match(/([0-9]{2})\/([0-9]{2})\/([0-9]{4})/);
                if(this.isValidDate(matches[3], matches[2], matches[1]) === false)
                {
                    return false;
                }

                return new Date(matches[3], parseInt(matches[2], 10)-1, parseInt(matches[1], 10));
            }
            catch(e)
            {
                return false;
            }
        },

        /**
         * Adds leading zero if passed value is single digit
         * @param {String} val
         * @returns {String}
         */
        leadingZero: function(val)
        {
            var str = val.toString();
            if(str.length == 1)
            {
                str = '0' + str;
            }

            return str;
        },

        /**
         * Checks if return date is equal or after departure date
         * @param {String} departureDate
         * @param {String} returnDate
         * @returns {Boolean}
         */
        isDepartureReturnDateValid: function(departureDate, returnDate)
        {
            var dep = this.stringToDate(departureDate);
            var ret = this.stringToDate(returnDate);
            if(dep > ret)
            {
                return false;
            }

            return true;
        },

        /**
         * Detect whether the year supplied is a leap year
         * @param {Integer} year
         * @returns {Boolean}
         */
        isLeapYear: function(year)
        {
            year = parseInt(year, 10);
            if(year % 4 == 0)
            {
                if(year % 100 != 0)
                {
                    return true;
                }
                else
                {
                    if(year % 400 == 0)
                    {
                        return true;
                    }
                    else
                    {
                        return false;
                    }
                }
            }
            return false;
        },

        compareDates: function(from, to)
        {
            var dateResult = to.getTime() - from.getTime();
            var dateObj = {};
            dateObj.weeks =  Math.round(dateResult/(1000 * 60 * 60 * 24 * 7));
            dateObj.days = Math.ceil(dateResult/(1000 * 60 * 60 * 24));
            dateObj.hours = Math.ceil(dateResult/(1000 * 60 * 60));
            dateObj.minutes = Math.ceil(dateResult/(1000 * 60));
            dateObj.seconds = Math.ceil(dateResult/(1000));
            dateObj.milliseconds = dateResult;
            return dateObj;
        },

        compareDatesDDMMYYYY: function(from, to)
        {
            from = from.split('/');
            from = new Date(from[2], from[1], from[0]);
            to = to.split('/');
            to = new Date(to[2], to[1], to[0]);
            return this.compareDates(from, to);
        },

        /**
         * Allow nice formatting of dates like PHP's Date function
         * Derived from code written by Jac Wright at http://jacwright.com/projects/javascript/date_format
         * @param {Date} date JavaScript date object
         * @param {String} format Date format string
         * @returns {String}
         */
        format: function()
        {
            var date,
                format,
                args = [].slice.call(arguments),
                returnStr = '',
                curChar = '',
                months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
                days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
                methods =
                {
                    // Day
                    d: function() { return (date.getDate() < 10 ? '0' : '') + date.getDate(); },
                    D: function() { return days[date.getDay()].substring(0, 3); },
                    j: function() { return date.getDate(); },
                    l: function() { return days[date.getDay()]; },
                    N: function() { return date.getDay() + 1; },
                    S: function() { return (date.getDate() % 10 == 1 && date.getDate() != 11 ? 'st' : (date.getDate() % 10 == 2 && date.getDate() != 12 ? 'nd' : (date.getDate() % 10 == 3 && date.getDate() != 13 ? 'rd' : 'th'))); },
                    w: function() { return date.getDay(); },

                    // Month
                    F: function() { return months[date.getMonth()]; },
                    m: function() { return (date.getMonth() < 9 ? '0' : '') + (date.getMonth() + 1); },
                    M: function() { return months[date.getMonth()].substring(0, 3); },
                    n: function() { return date.getMonth() + 1; },
                    Y: function() { return date.getFullYear(); },
                    y: function() { return ('' + date.getFullYear()).substr(2); },

                    // Time
                    a: function() { return date.getHours() < 12 ? 'am' : 'pm'; },
                    A: function() { return date.getHours() < 12 ? 'AM' : 'PM'; },
                    g: function() { return date.getHours() % 12 || 12; },
                    G: function() { return date.getHours(); },
                    h: function() { return ((date.getHours() % 12 || 12) < 10 ? '0' : '') + (date.getHours() % 12 || 12); },
                    H: function() { return (date.getHours() < 10 ? '0' : '') + date.getHours(); },
                    i: function() { return (date.getMinutes() < 10 ? '0' : '') + date.getMinutes(); },
                    s: function() { return (date.getSeconds() < 10 ? '0' : '') + date.getSeconds(); },

                    // Timezone
                    O: function() { return (-date.getTimezoneOffset() < 0 ? '-' : '+') + (Math.abs(date.getTimezoneOffset() / 60) < 10 ? '0' : '') + (Math.abs(date.getTimezoneOffset() / 60)) + '00'; },
                    P: function() { return (-date.getTimezoneOffset() < 0 ? '-' : '+') + (Math.abs(date.getTimezoneOffset() / 60) < 10 ? '0' : '') + (Math.abs(date.getTimezoneOffset() / 60)) + ':' + (Math.abs(date.getTimezoneOffset() % 60) < 10 ? '0' : '') + (Math.abs(date.getTimezoneOffset() % 60)); },
                    T: function() { var m = date.getMonth(); date.setMonth(0); var result = date.toTimeString().replace(/^.+ \(?([^\)]+)\)?$/, '$1'); date.setMonth(m); return result;},
                    Z: function() { return -date.getTimezoneOffset() * 60; },

                    // Full Date/Time
                    c: function() { return date.format("Y-m-d") + "T" + date.format("H:i:sP"); },
                    r: function() { return date.toString(); },
                    U: function() { return date.getTime() / 1000; }
                };

            if(typeof this.getMonth == 'function')
            {
                date = this;
                format = args[0];
            }
            else
            {
                date = args[0];
                format = args[1];
            }

            for(var i = 0; i < format.length; i++)
            {
                var curChar = format.charAt(i);
                if(methods[curChar])
                {
                    returnStr += methods[curChar].call();
                }
                else
                {
                    returnStr += curChar;
                }
            }
            return returnStr;
        }
    };
    FCL.UTIL.handleErrors(FCL.DATETIME);
    FCL.DATETIME.init();
})(jQuery);


/*___ FILE: "fcl.forms.js" ___*/
;(function($)
{
    /**
     * Form utility functions
     *
     * @id              $Id: fcl.forms.js 1341 2010-08-25 14:30:29Z blundenr $
     * @author          Ryan Blunden
     * @modifiedby      $LastChangedBy: blundenr $
     * @copyright       Copyright Flight Centre Ltd. All rights reserved.
     * @version         $Revision: 1341 $
     * @lastmodified    $Date: 2010-08-26 00:30:29 +1000 (Thu, 26 Aug 2010) $
     * @requires        FCL, FCL.UTIL
     * @class
     * @static
     */    
    FCL.FORMS =
    {
        /**
         * Name of this class (used for error handling and/or debugging purposes)
         * @type String
         */
        name: 'FCL.FORMS',

        /**
         * Define jQuery api for some of the FCL.FORMS methods
         */
        init: function()
        {
            $.fn.fclDefaultInput = function(defaultString)
            {
                return FCL.FORMS.inputTextShowHide(this, defaultString);
            };

            $.fn.fclReplaceSubmitButton = function(imageUrl)
            {
                return FCL.FORMS.replaceSubmitButton(this, imageUrl);
            };
        },

        /**
         * Default handling of forms that don't validate
         * @param {Object} e Event object
         * @param {Object} validator Validator instance
         * @param {Object} fieldMappings Map actual field names to nicer names for validation feedback
         * @param {Boolean} returnErrors Return error string if set to true, otherwise alert error string
         */
        invalidForm: function(e, validator, fieldMappings, returnErrors)
        {
            var errorString = '';
            $.each(validator.errorMap, function(key, val)
            {
                var fieldKey = '';
                if(typeof fieldMappings != 'undefined' && typeof fieldMappings[key] != 'undefined')
                {
                    fieldKey = fieldMappings[key];
                }
                else
                {
                    fieldKey = FCL.UTIL.titleCaps(key);
                }
                errorString += '  - ' + fieldKey + ': ' + val + '\n';
            });
            errorString = errorString.substring(0, errorString.length-1);

            var errors = validator.numberOfInvalids();
            if(errors)
            {
                var message = errors == 1
                    ? '1 field has errors: \n ' + errorString
                    : errors + ' fields have errors: \n' + errorString;

                if(returnErrors === true)
                {
                    return message;
                }
                
                window.alert(message);
            }
        },

        /**
         * Behaviour for fields where you want an initial value to be displayed such as 'Search...'. When focus is
         * applied, the default value is hidden and when the field is blurred, the default value is displayed again
         * unless the user has entered their own value in which case hiding and showing behaviour is stopped.
         * @param {jQuery} $el Field
         * @param {String} defaultText Default input text value
         * @returns {jQuery} Field
         * @example
         * // Apply functionality to search field
         * $('#form1 input[name="search"]').fclDefaultInput('Search...');
         */
        inputTextShowHide: function($el, defaultText)
        {           
            if($el.length === 0)
            {
                return $el;
            }

            if(typeof defaultText == 'undefined')
            {
                return FCL.UTIL.throwError('Please specify "defaultText argument');
            }

            if($el.val() == '')
            {
                $el.val(defaultText);
            }

            try
            {
                $el.data('defaultText', defaultText);
                $el.bind('focus', function(e)
                {
                    if($(this).val() === $(this).data('defaultText'))
                    {
                        $(this).val('');
                    }
                });

                $el.bind('blur', function(e)
                {
                    if($(this).val() == '')
                    {
                        $(this).val($(this).data('defaultText'));
                    }
                });
            }
            catch(e){}

            return $el;
        },

        /**
         * Insert a replacement submit button image after the submit button and attach an event listener
         * that when fired, hides the submit button and shows its replacement
         * @param {jQuery} $button Form submit button 
         * @param {String} imageUrl Url of the image to replace the submit button with
         * @returns {jQuery} Form submit button element
         * @example
         * // Attach the bahaviour to the submit button of the target form
         * $('#form2 button').fclReplaceSubmitButton('/img/ajax-loader.gif');
         *
         * // Inside your submit handler code, you trigger the "replaceSubmit" event which replaces
         * // the submit button with the loading graphic.
         * $('#form2').trigger('replaceSubmit'); 
         */
        replaceSubmitButton: function($button, imageUrl)
        {
            if($button.length == 0 || imageUrl == '')
            {
                return $button;
            }

            var $form = $button;
            while($form[0].tagName.toLowerCase() !== 'form')
            {
                $form = $form.parent();
            }

            if($form.find('img.formProcessing').length == 0)
            {
                $button.after('<img />').next()
                                        .hide()
                                        .attr('class', 'formProcessing')
                                        .attr('src', imageUrl)
                                        .attr('alt', 'Your form request is being processed')
                                        .attr('title', 'Your form request is being processed');
            }

            $form.bind('replaceSubmit', function()
            {
                $button.hide();
                $(this).find('.formProcessing').show();
            });

            return $button;
        },

        /**
         * Return true if a field exists and is not empty, otherwise return false
         * @param {jQuery} $element
         * @returns {Boolean}
         */
        checkFormFieldExistsNotEmpty: function($element)
        {
            //  Check obvious
            if($element.length == 0 || $element.val() == '')
            {
                return false;
            }

            // Check checkboxes and radio
            var type = $($element[0]).attr('type');
            if(type != 'radio' && type != 'checkbox')
            {
                return true;
            }

            var checked = false;
            $element.each(function()
            {
                if($(this).attr('checked'))
                {
                    checked = true;
                }
            });

            return checked;
        }
    };
    FCL.UTIL.handleErrors(FCL.FORMS);
    FCL.FORMS.init();
})(jQuery);


/*___ FILE: "fcl.tm.js" ___*/
;(function($)
{
    /**
     * Tourism Media receive commission for each enquiry if the enquiry is made within 3 clicks from a Tourism
     * Media content page.
     *
     * Therefore, we use a counting cookie "tourismMediaClicks", set this to 0 whenever the user is on a Tourism
     * Media page and when they are on an ET page, the cookie value is incremented.
     *
     * The cookie is also not created until the user visits a Tourism Media page.
     *
     * @id              $Id: fcl.tm.js 1440 2010-09-01 23:50:24Z blundenr $
     * @author          Ryan Blunden
     * @modifiedby      $LastChangedBy: blundenr $
     * @copyright       Copyright Flight Centre Ltd. All rights reserved.
     * @version         $Revision: 1440 $
     * @lastmodified    $Date: 2010-09-02 09:50:24 +1000 (Thu, 02 Sep 2010) $
     * @requires        FCL, FCL.UTIL
     * @class
     * @static
     */
    FCL.TM =
    {
        /**
         * Name of this class (used for error handling and/or debugging purposes)
         * @type String
         */
        name: 'FCL.TM',

        /**
         * Fire up cookie tracking
         */
        init: function()
        {
            if(typeof $.cookie != 'function')
            {
                FCL.UTIL.throwError('Tourism Media Tracking requires jQuery cookie plugin. Aborting.');
            }
            this.cookieTracking();
        },

        /**
         * Determines whether the user is within three clicks of a Tourism Media page
         */
        isWithinThreeClicks: function()
        {
            return ($.cookie('tourismMediaClicks') != null && parseInt($.cookie('tourismMediaClicks'), 10) <= 3);
        },

        /**
         * Fire up Tourism Media cookie tracking machinery
         */
        cookieTracking: function()
        {
            if(window.location.pathname.indexOf('world-travel') == 1)
            {
                $.cookie('tourismMediaClicks', 0, { path: '/' });
            }

            if(window.location.pathname.indexOf('world-travel') != 1 && $.cookie('tourismMediaClicks') != null)
            {
                $.cookie('tourismMediaClicks', parseInt($.cookie('tourismMediaClicks'), 10) + 1,  { path: '/' });
            }
        },

        /**
         * Record a custom page view in Google Analytics if within 3 clicks of Tourism Media page
         *
         * @param {Integer} [timestamp] Unique indentifier to link GA stats to enquiry emails
         */
        googleAnalyticsReporting: function(timestamp)
        {
            timestamp = (typeof(timestamp) == 'undefined' ? function() { d = new Date(); return d.getTime(); }() : timestamp);
            if(this.isWithinThreeClicks() || FCL.UTIL.getUrlVar('tourismMediaTracking') == 'true')
            {
                FCL.UTIL.registerGAPageView({ path: 'tourism-media/' + timestamp });
            }
        }
    };
    FCL.UTIL.handleErrors(FCL.TM);
    FCL.TM.init();
})(jQuery);


/*___ FILE: "fcl.ctc.js" ___*/
;(function($)
{
    /**
     * Click to Call from Pep Talk. The first part of the code (FCL.CTC) sets up the form validation and subsequent
     * events while the rest is the original PepTalk click to call code largely untouched so upgrading to new versions
     * of this code is as easy as possible
     * @static
     * @class
     * @id              $Id: fcl.ctc.js 1449 2010-09-02 01:52:57Z blundenr $
     * @author          Ryan Blunden
     * @modifiedby      $LastChangedBy: blundenr $
     * @copyright       Copyright Flight Centre Ltd. All rights reserved.
     * @version         $Revision: 1449 $
     * @lastmodified    $Date: 2010-09-02 11:52:57 +1000 (Thu, 02 Sep 2010) $
     * @requires        FCL, FCL.UTIL
     */
    FCL.CTC =
    {
        /**
         * Name of this class (used for error handling and/or debugging purposes)
         * @type String
         */
        name: 'FCL.CTC',

        /**
         * Brand name of the company
         * @type String
         */
        brand: '',

        /**
         * Name of this class (used for error handling and/or debugging purposes)
         * @type String
         */
        statesPlid:
        {
            '0': '', // NSW, ACT, NT
            '1': '', // NSW, ACT, NT
            '2': '', // NSW, ACT, NT
            '3': '', // VIC, TAS
            '4': '', // QLD (Seems to be the default)
            '5': '', // SA
            '6': '', // WA
            '7': '', // VIC, TAS
            '8': '', // NSW, ACT, NT
            'default': ''
        },

        /**
         * Cached jQuery selector for click to call form
         * @type jQuery
         */
        $form: null,

        /**
         * Overrides actual field names when validation errors are displayed
         * @type Object
         */
        fieldMappings:
        {
            pcode: 'Post Code'
        },

        /**
         * Initialise form and setup validation
         *
         * @param {String} brand Brand name (e.g. Escape Travel)
         * @param {Object} plid Brand Click to Call identifier
         */
        init: function(brand, plids)
        {
            if(typeof brand == 'undefined')
            {
                FCL.UTIL.throwError('No brand supplied, aborting setup of click to call');
                return;
            }

            if(typeof plids == 'undefined')
            {
                FCL.UTIL.throwError('No plids supplied, aborting setup of click to call');
                return;
            }
            this.statesPlid = plids;

            this.$form = $('#ctcForm');
            this.setupForm();
            this.setupValidation();
        },

        /**
         * Setup form interaction event handlers
         */
        setupForm: function()
        {
            this.$form.find('#phone').bind('keypress', function()
            {
                getKeyCode(1);
            });

            this.$form.find('#pcode').bind('keypress', function()
            {
                getKeyCode(2);
            });
        },

        /**
         * Dynamically set Plid on form submit based off first character of post code field
         */
        setPlid: function()
        {
            var statePrefix = this.$form.find('#pcode').val().charAt(0);
            var plid = (typeof this.statesPlid[statePrefix] != 'undefined') ? this.statesPlid[statePrefix] : this.statesPlid['default'];
            this.$form.find('#plid').val(plid);
        },

        /**
         * Setup validation for click to call form
         */
        setupValidation: function()
        {
            var self = this;

            $.validator.addMethod('phone', function(value, element, params)
            {
                try
                {
                    // Remove all non-numeric characters from phone field
                    $(element).val(function(i, value) { return value.match(/\d+/g).join('') });
                    if($(element).val().length < 8)
                    {
                        return false;
                    }
                }
                catch(e)
                {
                    return false;
                }

                return true;
            }, $.format('Phone number must consist of at least 8 digits'));

            this.$form.validate(
            {
                rules:
                {
                    phone:
                    {
                        required: true,
                        phone: true
                    },
                    pcode:
                    {
                        required: true,
                        digits: true
                    }
                },

                invalidHandler: function(e, validator)
                {
                    FCL.FORMS.invalidForm(e, validator, self.fieldMappings);
                },

                submitHandler: function(form)
                {
                    try
                    {
                        do_dsdial();

                        // Record event in Google Analytics
                        FCL.UTIL.registerGAPageView(
                        {
                            basePath: '/on-page-event/',
                            path: 'click-to-call'
                        });
                    }
                    catch(e)
                    {
                        FCL.UTIL.log(e);
                    }
                    return false;
                }
            });
        }
    };
    FCL.UTIL.handleErrors(FCL.CTC);
})(jQuery);

var callinprogress = 0;
var myDomain = document.URL.substring(7,document.URL.indexOf("/",7));

function setPlidForState()
{
    FCL.CTC.setPlid();
}

function do_dsdial()
 {
    var zero_re = /^0/;
    var plus_re = /^\+/;
    var let_re = /\w/;
    var phone = document.getElementById("phone").value;

    var myMsg = document.getElementById("ctc_info");
    var myPhone = document.getElementById("phone");
    var PCode = document.getElementById("pcode").value;

    if (callinprogress == 1)
    {
        myMsg.innerHTML = '<span>Call in progress...</span>';
        return;
    }
    phone = phone.replace(let_re, "");

    if (phone == '')
    {
        myPhone.value = 'Enter your number';
        return;
    }


    var theTelephone = document.getElementById("phone").value;
    if ((theTelephone.length >= 1) && (theTelephone.substring(0, 1) != 0))
    {
        myMsg.innerHTML = '<span>Please enter your area code before your telephone number</span>';
        return;
    }
    //check postcode validity
    if (document.getElementById("pcode").value == '')
    {
        myMsg.innerHTML = '<span class="hghlghterr">Please enter your postcode</span>';
        return;
    }
    errorMsg = 'Australian postcodes are 3 or 4 digit numbers between 200-300\nor 800-9999.';
    // Postcode must contain 3 or 4 digits, and no characters.
    numberOfDigits = 0;
    for (i = 0; i < document.getElementById("pcode").value.length; i++)
    {
        digit = parseInt(document.getElementById("pcode").value.charAt(i));
        if (isNaN(digit))
        {
            myMsg.innerHTML = errorMsg;
            return;
        }
        numberOfDigits++;
    }
    if (numberOfDigits != 3 && numberOfDigits != 4)
    {
        myMsg.innerHTML = errorMsg;
        return;
    }
    setPlidForState();
    var plid = document.getElementById("plid").value;

    // Ok, we should do something with the country codes
    phone = phone.replace(zero_re, "");
    phone = phone.replace(/\s/g, "");
    var Ephone = phone;
    Ephone = Ephone.replace(plus_re, "%2B");


    var myProto = document.location.protocol;

    var myUrl = myProto + '//' + myDomain + '/ptt/pt-dialer.cgi'
    var Query = 'PLID=' + plid + '&wantxml=yes&cmd=call&aparty=' + Ephone + '&pcode=' + PCode;
    myMsg.style.display = 'block';
    myMsg.innerHTML = '<span>Connecting you now...</span>';
    resetCookie("phone", Ephone);

    ajax(myUrl, Query, parse_results);

}

function parse_results(xmlDoc)
 {
    var myMsg = document.getElementById("ctc_info");

    try
    {
        var rc = xmlDoc.getElementsByTagName('rc').item(0).firstChild.data;
    }
    catch(err)
    {
        myMsg.style.display = 'block';
        myMsg.innerHTML = '<span>Thank you for calling '+ FCL.CTC.brand +'. You have contacted us outside of office hours. Please try us again between 9am and 5pm.</span>';
        return;
    }
    if (rc != 1)
    {
        var errmsg = xmlDoc.getElementsByTagName('errmsg').item(0).firstChild.data;
        if (errmsg == 'no credit') {
            myMsg.style.display = 'block';
            myMsg.innerHTML = '<span>' + xmlDoc.getElementsByTagName('reason').item(0).firstChild.data + '</span>';
        }
        else if (errmsg == 'maximum free calls reached')
        {
            myMsg.style.display = 'block';
            myMsg.innerHTML = '<span>You have used all your free calls.<br>Please join to continue using Pep-Talk</span>';
        }
        else if (errmsg == 'aparty number invalid')
        {
            myMsg.style.display = 'block';
            myMsg.innerHTML = '<span>The number you have entered is invalid. Please enter your full number including area code.</span>';
        }
        else if (errmsg == 'out of hours')
        {
            myMsg.style.display = 'block';
            myMsg.innerHTML = '<span>Thank you for calling '+ FCL.CTC.brand +'. You have contacted us outside of office hours. Please try us again between 9am and 5pm.</span>';
        }
        else
        {
            myMsg.innerHTML = '<span>' + errmsg + '</span>';
        }
        callinprogress = 0;
    }
    else
    {
        var status = '';
        try
        {
            status = xmlDoc.getElementsByTagName('status').item(0).firstChild.data;
        }
        catch(err)
        {
            /* Call has probably termintated, hence no status for the ptid */
            callinprogress = 0;
        }
        var ptid = xmlDoc.getElementsByTagName('ptid').item(0).firstChild.data;
        if (status == 'dialling')
        {
            myMsg.style.display = 'block';
            myMsg.innerHTML = '<span>Dialling...</span>';
            callinprogress = 1;
        }
        else if (status == 'call')
        {
            myMsg.style.display = 'block';

            myMsg.innerHTML = '<span>Connected</span>';
            callinprogress = 0;
        }
        else if (status == 'ready')
        {
            myMsg.style.display = 'block';
            myMsg.innerHTML = '<span>Ringing...</span>';
            callinprogress = 1;
        }
        else if (status == 'failed')
        {
            var Reason = xmlDoc.getElementsByTagName('reason').item(0).firstChild.data;
            callinprogress = 0;
            if (Reason == 'busy')
            {
                myMsg.style.display = 'block';

                myMsg.innerHTML = '<span>An ET Consultant has tried to contact you but was unable to reach you.</span>';
            }
            else if (Reason == 'busy-b')
            {
                myMsg.style.display = 'block';
                myMsg.innerHTML = '<span>Our consultants are unavailable just at the moment. Please try us again shortly.</span>';
            }
            else if (Reason == 'noanswer')
            {
                myMsg.style.display = 'block';
                myMsg.innerHTML = '<span>One of our Travel Consultants has just tried calling you.  When you are ready please try us again.</span>';
            }
            else if (Reason == 'noanswer-b')
            {
                myMsg.style.display = 'block';

                myMsg.innerHTML = '<span>All our Consultants are on other calls at the moment. Please try calling back in a moment.</span>';
            }
            else if (Reason == 'hangup')
            {
                myMsg.style.display = 'block';

                myMsg.innerHTML = '<span>It appears our call has ended. Please call back to speak with one of our travel consultants.</span>';
            }
        }

        if ((status != 'call') && (status != 'failed') && (status != ''))
        {
            var myProto = document.location.protocol;
            var myUrl = myProto + '//' + myDomain + '/ptt/pt-popoverlib.cgi';
            var Query = 'cmd=getstatus&ptid=' + ptid;
            ajax(myUrl, Query, parse_results);
        }
    }
}

function ajax(url, vars, callbackFunction)
 {
    var request = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("MSXML2.XMLHTTP.3.0");
    request.open("POST", url, true);
    if (request.overrideMimeType)
    {
        request.overrideMimeType('text/xml');
    }
    request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");

    request.onreadystatechange = function()
    {

        if (request.readyState == 4 && request.status == 200)
        {
            var xmlDoc = request.responseXML;
            if (xmlDoc)
            {
                callbackFunction(xmlDoc);
            }
        }
    }
    request.send(vars);
}

function URLencode(myString)
 {
    var result = '';
    var x = 0;
    myString = myString.toString();
    var re = /(^[a-zA-Z0-9_.]*)/;
    while (x < myString.length) {
        var match = re.exec(myString.substr(x));
        if (match != null && match.length > 1 && match[1] != '')
        {
            result += match[1];
            x += match[1].length;
        }
        else
        {
            if (myString[x] == ' ')
            {
                result += '+';
            }
            else
            {
                var chars = myString.charCodeAt(x);
                var val = chars.toString(16);
                result += '%' + (val.length < 2 ? '0': '') + val.toUpperCase();
            }
            x++;
        }
    }
    return result;
}
function do_rollover()
{

}
function do_rollback()
{

}

function resetCookie(cookieName, cookieValue) {
    if (readCookie(cookieName))
    {
        eraseCookie(cookieName);
        setCookie(cookieName, cookieValue);
    }
}

function setCookie(name, value, days)
{
    if (days) {
        var date = new Date();
        date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
        var expires = "; expires=" + date.toGMTString();
    }
    else var expires = "";
    document.cookie = name + "=" + value + expires + "; path=/";
}

function readCookie(name)
{
    var nameEQ = name + "=";
    var ca = document.cookie.split(';');
    for (var i = 0; i < ca.length; i++)
    {
        var c = ca[i];
        while (c.charAt(0) == ' ') c = c.substring(1, c.length);
        if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);
    }
    return null;
}

function eraseCookie(name)
{
    setCookie(name, "", -1);
}

function ShowCTCHelper()
{
    elem = document.getElementById("side-ctc-helper");
    elem.style.display = "block";
}
function HideCTCHelper(elementID)
{
    elem = document.getElementById("side-ctc-helper");
    elem.style.display = "none";
}

function getKeyCode(x)
{
    var charfield;
    if (x == 1) {
        charfield = document.getElementById("phone");
    } else if (x == 2) {
        charfield = document.getElementById("pcode");
    }
    charfield.onkeydown = function(e) {
        var e = window.event || e
        if (e.keyCode == 13) {
            do_dsdial();
        }
    }
}


/*___ FILE: "fcl.tabs.js" ___*/
/**
 *
 * @id              $Id: fcl.tabs.js 1581 2010-09-14 03:56:13Z blundenr $
 * @author          Ryan Blunden
 * @modifiedby      $LastChangedBy: blundenr $
 * @copyright       Copyright Flight Centre Ltd. All rights reserved.
 * @version         $Revision: 1581 $
 * @lastmodified    $Date: 2010-09-14 13:56:13 +1000 (Tue, 14 Sep 2010) $
 * @requires        FCL, FCL.UTIL
 */

(function($)
{
    FCL.UTIL.handleErrors(FCL.TABS);

    // Create jQuery tabs API
    $.fn.fclTabs = function(options)
    {
        // Define list of public methods
        var getters = ['showTab'];

        // Create array of arguments from the first argument onwards (as arg 0 is options)
        // We need these in case one of our getters requires additional arguments
        var otherArgs = Array.prototype.slice.call(arguments, 1);

        // If the value for options matches an item in getters, then because of the naming
        // convention of adding a "_" before the name of each getter, call this function,
        // setting the context to FCL.TABS (so "this" refers to FCL.TABS) and send any
        // additional arguments (if any) to this function
        if ($.inArray(options, getters) > -1)
        {
            return FCL.TABS['_' + options].apply(FCL.TABS, [this[0]].concat(otherArgs));
        }

        // Loop through all element instances, ensuring we return the instance so the call
        // to "fclTabs" is chainable
        return this.each(function()
        {
            // If options is a string, then this is a call to a public method
            if (typeof options == 'string')
            {
                FCL.TABS['_' + options].apply(FCL.TABS, [this].concat(otherArgs));
            }
            // Otherwise, this is a standard call to initialise the tabs with a set of options
            else
            {
                FCL.TABS.init(this, options || {});
            }
        });
    };

    /**
     * Basic tabs jQuery plugin - see code for private methods documentation.
     * @class
     * @static
     */
    FCL.TABS =
    {
        /**
         * Name of this class (used for error handling and/or debugging purposes)
         * @type String
         */
        name: 'FCL.TABS',

        /**
         * Initialise tabs
         * @param {Object} [options]
         * @param {Boolean} [options.persistent=false] Store the last opened tab and set this to be the opened tab on next page load
         * @param {String} [options.cookieTitle="tabCookie"] Set the name of the tab cookie for persistent tabs
         * @param {Number} [options.selectedTabIndex=0] Zero index tab number that is open by default
         * @param {Boolean} [options.sameHeight=false] Set to true if you want height of tab container to remain constant
         * @example
         * // To enable tabs
         * $('#tabs').fclTabs(options);
         *
         * // To show the third tab
         * $('#tabs a:nth(2)').fclTabs.showTab();
         */
        init: function(tabs, options)
        {
            var $tabs = (typeof tabs.jquery == 'undefined' ? $(tabs) : tabs);
            var selectedTabIndex = 0;
            var defaults =
            {
                persistent: false,
                cookieTitle: 'tabCookie',
                selectedTabIndex: 0,
                sameHeight: false
            };
            var options = $.extend(defaults, options);

            /*___ DETERMINE SELECTED TAB INDEX ___*/

            // See if we're meant to show tabs as they were last viewed
            if(options.persistent)
            {
                // Will fail without $.cookie plugin or if no cookie value is stored
                try
                {
                    var $targetTab = $tabs.find('a[href="'+ $.cookie(options.cookieTitle) +'"]');
                    if($targetTab.length > 0)
                    {
                        options.selectedTabIndex = $($targetTab.attr('href')).index();
                    }
                }
                catch(e){}
            }

            options.selectedTabIndex = (typeof options.selectedTabIndex == 'undefined') ? 0 : parseInt(options.selectedTabIndex, 10);

            // Check to see if supplied tab index is valid
            if($tabs.find('a:eq('+ options.selectedTabIndex +')').length === 0)
            {
                options.selectedTabIndex = 0;
            }

            if(options.sameHeight)
            {
                this._setupChangeListener();
            }

            $tabs.find('a').each(function()
            {
                // Only go to load tab content if href is a hash (honour normal links)
                if(FCL.TABS._isContentTab($(this)) == false)
                {
                    return;
                }

                if(options.sameHeight)
                {
                    $($(this).attr('href')).tabContentChange(function()
                    {
                        FCL.TABS._refreshTabs($tabs);
                    });
                }

                // Tab click shows selected tab and remembers tab if persistent is set to true
                $(this).bind('click', function(e)
                {
                    e.preventDefault();
                    FCL.TABS._showTab($tabs, $(this).parent().index());

                    if(options.persistent)
                    {
                        $.cookie(options.cookieTitle, $(this).attr('href'));
                    }
                });
            });

            if(options.sameHeight)
            {
                this._refreshTabs($tabs);
            }

            $tabs.find('a:eq('+ options.selectedTabIndex +')').trigger('click');

           return $tabs;
        },

        /**
         * Determine if current tab is a content tab or link to another page
         * @private
         * @param {jQuery} $tab
         * @returns {Boolean}
         */
        _isContentTab: function($tab)
        {
            return ($tab.attr('href').indexOf('#') === 0);
        },

        /**
         * Setup "tabContentChange" listener to detect when the content of a tab changes
         * This is useful when "sameHeight" is true and you want the vertical size of the tabs to change when
         * new content is loaded in via AJAX for example.
         * @private
         * @param {jQuery} $tabs
         * @returns {jQuery} jQuery element selector
         */
        _setupChangeListener: function($tabs)
        {
            var interval;

            $.fn.tabContentChange = function(fn)
            {
                return this.bind('tabContentChange', fn);
            };

            $.event.special.tabContentChange =
            {
                setup: function(data, namespaces)
                {
                    var self = this,
                        $this = $(this),
                        $originalContent = $this.text();
                    interval = setInterval(function()
                    {
                        if($originalContent != $this.text())
                        {
                                $originalContent = $this.text();
                                $.event.special.tabContentChange.handler.call(self);
                        }
                    },500);
                },
                teardown: function(namespaces)
                {
                    clearInterval(interval);
                },
                handler: function(event)
                {
                    $.event.handle.call(this, { type:'tabContentChange' });
                }
            };

            return $tabs;
        },

        /**
         * Determine height of each tab and make tab container height the height of the highest tab
         * @private
         * @param {jQuery} $tabs
         * returns {jQuery} jQuery element selector
         */
        _refreshTabs: function($tabs)
        {
            // Figure out max height of a tab and set container to be that height
            var maxTabHeight = 0;
            $tabs.find('a').each(function(e)
            {
                if(FCL.TABS._isContentTab($(this)) == false)
                {
                    return;
                }

                var $tabContent =  $($(this).attr('href'));
                $tabContent.css({ height: 'auto'});
                maxTabHeight = ($tabContent.height() > maxTabHeight) ? $tabContent.height() : maxTabHeight;
            });

            // Apply max height to all tabs
            $tabs.find('a').each(function(e)
            {
                if(FCL.TABS._isContentTab($(this)) == false)
                {
                    return;
                }

                $($(this).attr('href')).css('height', maxTabHeight);
            });

            return $tabs;
        },

        /**
         * Show the selected tab
         * @param {jQuery} $tab
         * @returns {jQuery} jQuery element selector
         * @private
         */
        _showTab: function(tabs, index)
        {
            $tabs = $(tabs);
            var $tab = $tabs.find('li:nth('+ index +') a');

            // The tabs can't be nested higher than 2 levels within their container
            var $tabsParent = $tab.parent().parent();
            $currentSelectedTab = $tabsParent.find('.selected');
            $tabContent = $($tab.attr('href'));

            // Hide all tabs
            $tabsParent.find('a').each(function()
            {
               if(FCL.TABS._isContentTab($(this)) == false)
                {
                    return;
                }

                if($(this).attr('href') != $tab.attr('href'))
                {
                    $($(this).attr('href')).addClass('hide');
                }
            });

            $currentSelectedTab.removeClass('selected');
            $tab.addClass('selected');
            // Set new tab as selected and show tab content
            if($tabContent.attr('class').indexOf('hide') > -1 || $tabContent.attr('class').indexOf('hidden') > -1)
            {
                $tabContent.css('opacity', 0).removeClass('hide').removeClass('hidden');
                $tabContent.animate(
                {
                    opacity: 1
                }, 350);
            }

            return $tab;
        }
    };
})(jQuery);


/*___ FILE: "fcl.gimp.js" ___*/
;(function($)
{
    /**
     * GIMP helper methods
     *
     * @id              $Id: fcl.gimp.js 1439 2010-09-01 23:48:10Z blundenr $
     * @author          Ryan Blunden
     * @modifiedby      $LastChangedBy: blundenr $
     * @copyright       Copyright Flight Centre Ltd. All rights reserved.
     * @version         $Revision: 1439 $
     * @lastmodified    $Date: 2010-09-02 09:48:10 +1000 (Thu, 02 Sep 2010) $
     * @requires        FCL, FCL.UTIL, FCL.FORMS
     * @static
     * @class
     */
    FCL.GIMP =
    {
        /**
         * Name of this class (used for error handling and/or debugging purposes)
         * @type String
         */
        name: 'FCL.GIMP',

        /**
         * Default (fallback) GIMP settings
         * @type Object
         */
        settings:
        {
            baseUrl: '/sendEnquiry?',
            brand: 'FC',
            forwardUrl: '/company/contact-us/enquiry-success',
            failUrl: '/company/contact-us/enquiry-failure',
            ignoreFields: ['failUrl', 'forwardUrl']
        },

        /**
         *
         * @param {jQuery} $form
         */
        setSubject: function($form)
        {
            // if no subject field found, abort (GIMP will use default subject based on Type)
            if($form.find('input[name="subject"]').length == 0) { return; }

            var $subject = $form.find('input[name="subject"]');
            var matches = $subject.val().match(/\{([a-zA-Z0-9])+\}/g);

            // If no tokens found, abort
            if(matches == null) { return; }

            $.each(matches, function(i)
            {
                var fieldName = matches[i].match(/[a-zA-Z0-9]+/)[0];
                var $field = $form.find(FCL.UTIL.format('input[name="{1}"]', fieldName));
                var fieldValue = (typeof $field.val() != 'undefined') ? $field.val() : '';
                $subject.val($subject.val().replace(matches[i], fieldValue));
            });
        },

        /**
         * Provide defaults for required GIMP values if they are missing in a form
         * @param {jQuery} $form Form to process
         * @param {String} urlStr Current GIMP request string
         * @returns {String} GIMP request string
         */
        checkRequiredFields: function($form, urlStr)
        {
            urlStr += (FCL.FORMS.checkFormFieldExistsNotEmpty($form.find('input[name="type"]'))) ? '' : 'type=General+Enquiry+Contact+Details&';
            urlStr += (FCL.FORMS.checkFormFieldExistsNotEmpty($form.find('input[name="email"]'))) ? '' : 'email=email@email.com&';
            urlStr += (FCL.FORMS.checkFormFieldExistsNotEmpty($form.find('input[name="postCode"]'))) ? '' : 'postCode=0000&';
            urlStr += (FCL.FORMS.checkFormFieldExistsNotEmpty($form.find('input[name="brand"]'))) ? '' : 'brand='+ this.settings.brand + '&';
            urlStr += (FCL.FORMS.checkFormFieldExistsNotEmpty($form.find('input[name="emailNewsletter"]'))) ? '' : 'emailNewsletter=No'+ '&';
            urlStr += (FCL.FORMS.checkFormFieldExistsNotEmpty($form.find('input[name="forwardUrl"]'))) ? '' : 'forwardUrl='+ this.settings.forwardUrl + '&';
            urlStr += (FCL.FORMS.checkFormFieldExistsNotEmpty($form.find('input[name="failUrl"]'))) ? '' : 'failUrl='+ this.settings.failUrl + '&';
            return urlStr;
        },

        /**
         * Take the supplied $ form referrence, collect all relevant form data, build a GIMP URL string and return it
         * @param {jQuery} $form Form to process
         * @returns {String} GIMP request URL
         */
        getUrl: function($form)
        {
            var gimpUrl = this.settings.baseUrl;

            this.setSubject($form);

            $form.find('input[type="text"], input[type="hidden"], input[type="checkbox"]:checked, input[type="radio"]:checked, select, textarea').each(function()
            {
                if($(this).attr('name') != '')
                {
                    var fieldName = $(this).attr('name');
                    gimpUrl += fieldName + '=' + encodeURIComponent(FCL.UTIL.filterInputText($(this).val())) + '&';
                }
            });

            gimpUrl = this.checkRequiredFields($form, gimpUrl);
            return gimpUrl.substring(0, gimpUrl.length -1);
        }
    };
    FCL.UTIL.handleErrors(FCL.GIMP);
})(jQuery);


/*___ FILE: "fcl.mailer.js" ___*/
;(function($)
{
    /**
     * Mailer helper methods for sending emails through the SiteCAt Email (Mailer) servlet
     *
     * @id              $Id: fcl.mailer.js 1117 2010-08-03 07:09:16Z blundenr $
     * @author          Ryan Blunden
     * @modifiedby      $LastChangedBy: blundenr $
     * @copyright       Copyright Flight Centre Ltd. All rights reserved.
     * @version         $Revision: 1117 $
     * @lastmodified    $Date: 2010-08-03 17:09:16 +1000 (Tue, 03 Aug 2010) $
     * @requires        FCL, FCL.UTIL
     * @static
     * @class
     */
    FCL.MAILER =
    {
        /**
         * Name of this class (used for error handling and/or debugging purposes)
         * @type String
         */
        name: 'FCL.MAILER',

        /**
         * Name of this class (used for error handling and/or debugging purposes)
         * @type String
         */
        settings:
        {
            baseUrl:            '/mailer?', // The path to the SiteCat Email (Mailer) servlet
            EMAIL_TO:           '',         // Who the email will be sent to REQUIRED
            EMAIL_CC:           '',         // CC
            EMAIL_BCC:          '',         // BCC
            EMAIL_SUBJECT:      '',         // Email Subject REQUIRED
            EMAIL_SUCCESS_URL:  '',         // WCM content path to success url REQUIRED
            EMAIL_FAIL_URL:     '',         // WCM content path to failure url REQUIRED
            EMAIL_TEMPLATE:     '',         // WCM content path to HTML email template REQUIRED
            EMAIL_CONTENT1:     '',         // Content Field
            EMAIL_CONTENT2:     '',         // Content Field
            EMAIL_CONTENT3:     '',         // Content Field
            EMAIL_CONTENT4:     '',         // Content Field
            EMAIL_CONTENT5:     ''          // Content Field
        },

        /**
         * Take an object of MAILER settings, build a URL string and return it
         *
         * @param   {Object} mailerSettings
         * @returns {String} Mailer request url
         */
        getRequestUrl: function(mailerSettings)
        {
            var mailer = $.extend(this.settings, mailerSettings);
            var mailerUrl = mailer.baseUrl;

            $.each(mailer, function(key, val)
            {
                if(val != '' && key != 'baseUrl')
                {
                    mailerUrl += key + '=' + FCL.UTIL.filterInputText(val) + '&';
                }
            });

            return mailerUrl.substring(0, mailerUrl.length-1);
        }
    };
    FCL.UTIL.handleErrors(FCL.MAILER);
})(jQuery);


/*___ FILE: "fcl.currency.js" ___*/
;(function($)
{
    /**
     * Currency conversion class that replaces default functionality from external provider
     * in order to improve usability
     *
     * @id              $Id: fcl.currency.js 1342 2010-08-25 14:46:43Z blundenr $
     * @author          Ryan Blunden
     * @modifiedby      $LastChangedBy: blundenr $
     * @copyright       Copyright Flight Centre Ltd. All rights reserved.
     * @version         $Revision: 1342 $
     * @lastmodified    $Date: 2010-08-26 00:46:43 +1000 (Thu, 26 Aug 2010) $
     * @requires        FCL
     * @class
     * @static
     */
    ;FCL.CURRENCY =
    {
        /**
         * Name of this class (used for error handling and/or debugging purposes)
         * @type String
         */
        name: 'FCL.CURRENCY',

        /**
         * Attach form field change and submit event handlers
         */
        init: function()
        {
            var self = this;
            $.getScript('http://www.aquariussoft.com/scripts/getdatajs.aspx', function()
            {
                $.getScript('http://www.aquariussoft.com/scripts/convertcurrency.js', function()
                {
                    // Erase calculated value as soon as the variables change
                    $('#cur_convertForm, #cur_convertTo, #cur_fromAmount').bind('change', function()
                    {
                        $('#cur_toAmount').val('');
                    });

                    // Calculate and display conversion on form submit
                    $('#currencyConversionForm').bind('submit', function(e)
                    {
                        e.preventDefault();
                        calculate(0);
                    });
                });
            });
        }
    };
    FCL.UTIL.handleErrors(FCL.CURRENCY);
})(jQuery);
