/***************************************************************************/
/* jQuery plugins                                                          */
/***************************************************************************/

// Viewport selectors
// http://www.appelsiini.net/projects/viewport
(function($){$.belowthefold=function(element,settings){var fold=$(window).height()+$(window).scrollTop();return fold<=$(element).offset().top-settings.threshold;};$.abovethetop=function(element,settings){var top=$(window).scrollTop();return top>=$(element).offset().top+$(element).height()-settings.threshold;};$.rightofscreen=function(element,settings){var fold=$(window).width()+$(window).scrollLeft();return fold<=$(element).offset().left-settings.threshold;};$.leftofscreen=function(element,settings){var left=$(window).scrollLeft();return left>=$(element).offset().left+$(element).width()-settings.threshold;};$.inviewport=function(element,settings){return!$.rightofscreen(element,settings)&&!$.leftofscreen(element,settings)&&!$.belowthefold(element,settings)&&!$.abovethetop(element,settings);};$.extend($.expr[':'],{"below-the-fold":function(a,i,m){return $.belowthefold(a,{threshold:0});},"above-the-top":function(a,i,m){return $.abovethetop(a,{threshold:0});},"left-of-screen":function(a,i,m){return $.leftofscreen(a,{threshold:0});},"right-of-screen":function(a,i,m){return $.rightofscreen(a,{threshold:0});},"in-viewport":function(a,i,m){return $.inviewport(a,{threshold:0});}});})(jQuery);

// URL parser
// http://projects.allmarkedup.com/jquery_url_parser/jquery.url.packed.js
jQuery.url=function(){var segments={};var parsed={};var options={url:window.location,strictMode:false,key:["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"],q:{name:"queryKey",parser:/(?:^|&)([^&=]*)=?([^&]*)/g},parser:{strict:/^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,loose:/^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/}};var parseUri=function(){str=decodeURI(options.url);var m=options.parser[options.strictMode?"strict":"loose"].exec(str);var uri={};var i=14;while(i--){uri[options.key[i]]=m[i]||""}uri[options.q.name]={};uri[options.key[12]].replace(options.q.parser,function($0,$1,$2){if($1){uri[options.q.name][$1]=$2}});return uri};var key=function(key){if(!parsed.length){setUp()}if(key=="base"){if(parsed.port!==null&&parsed.port!==""){return parsed.protocol+"://"+parsed.host+":"+parsed.port+"/"}else{return parsed.protocol+"://"+parsed.host+"/"}}return(parsed[key]==="")?null:parsed[key]};var param=function(item){if(!parsed.length){setUp()}return(parsed.queryKey[item]===null)?null:parsed.queryKey[item]};var setUp=function(){parsed=parseUri();getSegments()};var getSegments=function(){var p=parsed.path;segments=[];segments=parsed.path.length==1?{}:(p.charAt(p.length-1)=="/"?p.substring(1,p.length-1):path=p.substring(1)).split("/")};return{setMode:function(mode){strictMode=mode=="strict"?true:false;return this},setUrl:function(newUri){options.url=newUri===undefined?window.location:newUri;setUp();return this},segment:function(pos){if(!parsed.length){setUp()}if(pos===undefined){return segments.length}return(segments[pos]===""||segments[pos]===undefined)?null:segments[pos]},attr:key,param:param}}();

// URL encoding/decoding
// http://0061276.netsolhost.com/tony/javascript/urlEncode.js
// (subsequently compressed)
$.extend({URLEncode:function(a){var e="",d=0;a=a.toString();for(var f=/(^[a-zA-Z0-9_.]*)/;d<a.length;){var c=f.exec(a.substr(d));if(c!=null&&c.length>1&&c[1]!=""){e+=c[1];d+=c[1].length}else{if(a[d]==" ")e+="+";else{c=a.charCodeAt(d);c=c.toString(16);e+="%"+(c.length<2?"0":"")+c.toUpperCase()}d++}}return e},URLDecode:function(a){a=a;for(var e,d=/(%[^%]{2})/;(m=d.exec(a))!=null&&m.length>1&&m[1]!="";){b=parseInt(m[1].substr(1),16);e=String.fromCharCode(b);a=a.replace(m[1],e)}return a}});

/***************************************************************************/
/* HR global object                                                        */
/***************************************************************************/
// HR.constants.messages.<namespace>.<type>.<code>.<dataKey>
HR = {
    constants:{
        messages:{
            validation:{
                error:{
                    100:{
                        'email':{
                            heading:'Email address missing!'
                        },
                        'password':{
                            heading:'Password missing!'
                        },
                        'message':{
                            heading:'Message missing!'
                        }
                    }
                }
            }
        }
    },

    log:function(object){
        if (!HR.internal.isLive()) {
            try {
                if (typeof console == 'object') {
                    console.log(object);
                }
            } catch (e) {
                //alert(object);
            }
        }
    },

    internal:{
        message:function(options) {
            var defaults = {
                note:'',
                namespace:'',
                type:'',
                code:0,                
                data:null
            };

            var settings = $.extend(defaults, options);

            this.data = settings.data;
            this.note = settings.note;
            this.type = settings.type;
            this.code = settings.code;
            this.namespace = settings.namespace;
        },

        customHandler:{
            get:function(basePath) {
                var customObject = HR;
                var stringPath = 'HR.'+basePath;
                var basePathParts = basePath.split('.');
                for (var basePathPartIndex in basePathParts) {
                    if (basePathParts.hasOwnProperty(basePathPartIndex)) {
                        customObject = customObject[basePathParts[basePathPartIndex]];
                    }
                }

                var pathParts = window.location.pathname.split('/');
                var parentObjectExists = true;

                for (var pathPartIndex in pathParts) {
                    if (parentObjectExists === true) {
                        var pathPart = $.trim(pathParts[pathPartIndex]);

                        if (pathPart.length > 0) {
                            customObject = customObject[pathPart];
                            stringPath += '.'+pathPart;

                            HR.log("Looking for: "+stringPath);
                            switch (typeof customObject) {
                            case 'undefined':
                                return false;

                            case 'function':
                                HR.log("... found");
                                return customObject;

                            default:
                               break;
                            }
                        }
                    }
                }

                return false;
            },

            run:function(basePath, context) {
                var customHandler = HR.internal.customHandler.get(basePath);
                if (typeof customHandler != 'function') {
                    return true;
                }

                return customHandler.call(context);
            }
        },

        isLive:function() {
            var nonLiveDomains = ['dev', 'local'];

            for (var domainIndex in nonLiveDomains) {
                if (nonLiveDomains.hasOwnProperty(domainIndex)) {
                    if (window.location.host.indexOf(nonLiveDomains[domainIndex]+'.') > -1) {
                        return false;
                    }
                }
            }

            return true;
        },

        isSecure:function() {
            return (window.location.protocol == 'https:' && (window.location.hostname.indexOf('secure.') != -1));
        },

        url:function(options) {
            var defaults = {
                relativePath:'',
                secure:'default',
                live:'default'
            };

            var settings = $.extend(defaults, options);            
            if (typeof settings.secure  != 'boolean') {
                settings.secure = HR.internal.isSecure();
            }

            if (typeof settings.live != 'boolean') {
                settings.live = HR.internal.isLive();
            }
            
            var url = '';
            url += (settings.secure) ? 'https://secure.' : 'http://';
            url += (settings.live === false) ? 'dev.' : '';
            url += 'hostingreborn.com/' + options.relativePath;               
            
            return url;
        }
    },

    api:{
        api:{
            query:function() {

            },

            read:function(options) {
                var ajax = null;
                var responseData = null;

                this.ajax = function() {
                    return ajax;
                };

                this.responseData = function() {
                    return responseData;
                };

                var url = function() {
                    var url = '';
                    url += (HR.internal.isSecure()) ? 'https://secure.' : 'http://';
                    url += (!HR.internal.isLive()) ? 'dev.' : '';
                    url += 'hostingreborn.com/ajax/';

                    return url;
                };
                
                var defaults = {
                    async:true,                 // Boolean: asynchronous?
                    cache:true,                 // Boolean: browser should cache results?
                    complete:function(){},      // Callback: when request is completed (after success and error)
                    contentType:'application/x-www-form-urlencoded',
                    data:{},                    // Object or query string
                    dataType:'json',            // Data type expected back from the server
                    error:function(){},         // Callback: called on error: function(XMLHttpRequest, textStatus, errorThrown) {}
                    success:function(){},
                    timeout:10000,              // Number
                    path:''                     // Request path, relative to API proxy URL
                };

                var settings = $.extend(defaults, options);
                settings.type = 'GET';
                settings.url = url()+settings.path;
                
                settings.error = function(XMLHttpRequest, textStatus, errorThrown) {
                    var messageNote = null;
                    var messageCode = null;

                    switch (textStatus) {
                    case 'timeout':
                        messageNote = 'apiException: timeout';
                        messageCode = 200;
                        break;

                    case 'parsererror':
                        messageNote = 'apiExcepion: parseError';
                        messageCode = 201;
                        break;

                    default:
                        messageNote = 'apiException: unknown error';
                        messageCode = 299;
                        break;
                    }

                    var message = new HR.internal.message({
                        note:messageNote,
                        type:'error',
                        code:messageCode,
                        namespace:'api',
                        data:ajax
                    });

                    throw message;
                };

                settings.success = function(data, textStatus) {
                    responseData = data;
                };

                $.ajax(settings);
                return true;                             
            },

            write:function(){

            }
        },

        hostingaccount:{

        },

        notification:{

        },

        price:{

        },

        server:{

        },

        transaction:{

        }
    },

    exception:function(options) {
        var defaults = {
            message:'',
            code:0,
            type:'',
            data:null
        };

        var settings = $.extend(defaults, options);

        this.message = function() {
            return settings.message;
        };

        this.code = function() {
            return settings.code;
        };

        this.type = function() {
            return settings.type;
        };

        this.data = function() {
            return settings.data;
        };
    },

    window:{
        scrollTo2:function(options) {
            var defaults = {
                offset:0,
                duration:400,
                easing:'swing',
                speed:false,
                target:'',
                source:''
            };

            var settings = $.extend({}, defaults, options);
            var hasSpeed = function() {
                return typeof settings.speed == 'number';
            };

            var calculateDurationFromSpeed = function () {
                var source = $(settings.source);
                var distance = $(settings.target).offset().top - source.offset().top;

                return (distance / settings.speed) * 1000;
            };

            var duration = settings.duration;
            if (hasSpeed()) {
                duration = calculateDurationFromSpeed();
            }

            $('html, body').animate({
                scrollTop: $(settings.target).offset().top + settings.offset
            }, duration, settings.easing);
        },

        scrollTo:function(to, duration, easing, offset){
            if (typeof offset != 'number') {
                offset = 0;
            }

            $('html, body').animate({
                scrollTop: $(to).offset().top + offset
            }, duration, easing);
        }
    },
    
    validate:{
        isBlankString:function(value) {
            if (typeof(value) != 'string' && typeof(value) != 'number') {
                return true;
            }

            return $.trim(value) == '';
        },

        isNumeric:function(value) {
            if (isNaN(value)) {
                return false;
            }

            if (typeof(value) == 'number') {
                return true;
            }

            if ((typeof(value) != 'string')) {
                return false;
            }

            var decimalPointFound = false;
            for (var characterIndex in value) {
                if (value.hasOwnProperty(characterIndex)) {
                    var currentCharacter = value.charAt(characterIndex);

                    if (!(currentCharacter >= '0' && currentCharacter <= '9')) {
                        if ((currentCharacter != '.')) {
                            return false;
                        }

                        if (decimalPointFound === true) {
                            return false;
                        } else {
                            decimalPointFound = true;
                        }
                    }
                }               
            }

            return true;
        }

    },

    url:{
        query:{
            value:function(name, queryString) {
                if (queryString === undefined) {
                    queryString = window.location.search.substring(1);
                }

                if (queryString.indexOf('?') > -1) {
                    queryString = queryString.substring(queryString.indexOf('?') + 1);
                }

                var queryArguments = queryString.split("&");
                for (var argumentIndex in queryArguments) {
                    if (queryArguments.hasOwnProperty(argumentIndex)) {
                        var pair = queryArguments[argumentIndex].split("=");
                        if (pair[0] == name) {
                            return pair[1];
                        }
                    }
                }

                return undefined;
            },
            
            build:function(values) {
                var query = '';

                for (var valueIndex in values) {
                    if (values.hasOwnProperty(valueIndex)) {
                        query += valueIndex+'='+encodeURIComponent(values[valueIndex])+'&';
                    }
                }
                
                if (query.length) {
                    query = query.substr(0, query.length-1);
                }
                
                return query;
            }


        }
    },

    location:{
        hash:{

            present:function() {
                return (window.location.hash.indexOf('#') > -1 && window.location.hash.indexOf('#') < 1);
            },

            get:function(name) {
                if (!this.present()) {
                    return undefined;
                }

                var parts = window.location.hash.substr(1).split('&');
                var queryArguments = [];

                for (var i in parts) {
                    if (parts.hasOwnProperty(i)) {
                        var values = parts[i].split('=');

                        if (name === undefined) {
                            queryArguments[values[0]] = values[1];
                        } else {
                            if (values[0] == name) {
                                return values[1];
                            }
                        }
                    }
                }

                if (name === undefined) {
                    return queryArguments;
                }

                return undefined;
            },

            set:function(name, value) {
                switch (typeof(name)) {
                case 'undefined':
                case 'function':
                case 'object':
                    return false;

                case 'object':
                    if (typeof(name.toString) == 'function') {
                        name = name.toString();
                    } else {
                        return false;
                    }
                    break;

                case 'boolean':
                    name = (name) ? 'true' : 'false';
                    break;

                case 'number':
                    name+='';
                    break;

                default:
                    break;
                }

                if (!this.present()) {
                    window.location.hash = '#'+name+"="+value;
                    return true;
                }

                var newHash = '#';
                var queryArguments = this.get();

                for (var currentName in queryArguments) {
                        if (queryArguments.hasOwnProperty(currentName)) {
                        if (currentName == name) {
                            newHash += name +"="+ value;
                        } else {
                            if (this.get(currentName) === undefined) {
                                newHash += currentName;
                            } else {
                                newHash += currentName+"="+this.get(currentName);
                            }
                        }

                        newHash += '&';
                    }
                }

                if (this.get(name) === undefined) {
                    newHash += name +"="+ value+'&';
                }

                window.location.hash = newHash.substring(0, newHash.length-1);
                return true;
            }
        }
    },

    format:{
        GBP_readable:function(value) {
            var valueType = typeof(value);            
            if (!(valueType == 'string' || valueType == 'number')) {
                return 0+'p';
            }
            
            value = parseInt(value, 10);

            // Less than one hundred? If so, return value as pence.
            if (value < 100) {
                return value.toFixed(2)+'p';
            }

            // Get pound representation of pence
            // -> with optional shrinkage (changing resolution as value increases/decreases)
            var pounds = value/100;

            if (pounds >= 1000) {
                if (pounds >= 1000000000000) {
                    return "Way too much!";
                }

                if (pounds >= 1000000000) {
                    var billions = pounds / 1000000000;
                    return "&pound;"+billions.toFixed(2)+"B";
                } else {
                    if (pounds >= 1000000) {
                        var millions = pounds / 1000000;
                        return "&pound;"+millions.toFixed(2)+"M";
                    } else {
                        var thousands = pounds / 1000;
                        return "&pound;"+thousands.toFixed(2)+"K";
                    }
                }
            }

            return "&pound;"+(value/100).toFixed(2);
        }
    },

    optionSet:function(inboundOptions) {
        var options = inboundOptions;

        this.contains = function(key) {
            return key in options;
        };

        this.get = function(key) {
            if (this.contains(key)) {
                return options[key];
            }

            return null;
        };

        this.set = function(key, value) {
            options[key] = value;
        };
    },

    forms:{
        disableInputButtons:function() {
            $('input[type=submit], input[type=button], input[type=image]').attr('disabled', 'disabled');
        },

        enableInputButtons:function() {
            $('input[type=submit], input[type=button], input[type=image]').each(function() {
                if (($(this).hasClass('disabled')) === false) {
                    $(this).removeAttr('disabled');
                }
            });
        }
    },

    form:{
        submit:{
            requestHandler:function(options){
                var ajax = null;
                var responseData = null;

                this.ajax = function() {
                    return ajax;
                };

                this.responseData = function() {
                    return responseData;
                };

                var url = function() {
                    var url = '';
                    url += (HR.internal.isSecure()) ? 'https://secure.' : 'http://';
                    url += (!HR.internal.isLive()) ? 'dev.' : '';
                    url += 'hostingreborn.com/ajax/';

                    return url;
                };

                var defaults = {
                    cache:true,                 // Boolean: browser should cache results?
                    complete:function(){},      // Callback: when request is completed (after success and error)
                    contentType:'application/x-www-form-urlencoded',
                    data:{},                    // Object or query string
                    dataType:'json',            // Data type expected back from the server
                    error:function(){},         // Callback: called on error: function(XMLHttpRequest, textStatus, errorThrown) {}
                    success:function(){},
                    timeout:10000,              // Number
                    type:'GET',
                    beforeSend:function(XMLHttpRequest){
                        XMLHttpRequest.setRequestHeader('X-Environment', 'javascript');
                    }
                };

                var settings = $.extend(defaults, options);
                settings.async = false;

                settings.error = function(XMLHttpRequest, textStatus, errorThrown) {
                    HR.log('error');
                    HR.log(textStatus);
                    HR.log(errorThrown);
                    HR.log(XMLHttpRequest);
                    return false;
                };

                settings.success = function(data, textStatus) {
                    responseData = data;
                    
                    switch (responseData.type) {
                    case 'redirect':
                        HR.log("Redirecting to: "+responseData.value);
                        window.location.href = responseData.value;
                        throw false;

                    case 'error':
                    case 'confirm':
                        var message = new HR.internal.message({
                            note:responseData.note,
                            type:responseData.type,
                            code:responseData.code,
                            namespace:responseData.namespace,
                            data:{
                                key:responseData.field,
                                object:$('input[name='+responseData.field+']')
                            }
                        });

                        throw message;

                    case 'confirm':
                        break;
                    }

                    HR.log('in success');
                    
                    throw responseData;
                };

                $.ajax(settings);                
                return true;
            },
            
            getResponse:function() {
                // this = form being submitted
                var form = $(this);
                var url = form.attr('action');
                var data = form.serialize();
                var type = form.attr('method').toUpperCase();
                
                try {
                    var handler = new HR.form.submit.requestHandler({
                        data:data,
                        url:url,
                        type:type
                    });
                } catch (message) {
                    return message;
                }
                
                return false;
            }

        },

        cloneMessageFields:function(form) {
            $('input, textarea, select', $('div.message', form)).each(function() {
                var inputField = $(this);
                var addHiddenField = function(name, value) {
                    if ($('input[type=hidden][name='+name+']', form).length === 0) {
                        $(form).append(
                            $('<input type="hidden" name="'+name+'" />')
                        );
                    }

                    $('input[type=hidden][name='+name+']', form).val(value);
                    return true;

                };

                switch (this.nodeName.toLowerCase()) {
                    case 'select':
                        break;

                    case 'textarea':
                        break;

                    default:
                        switch (inputField.attr('type')) {
                            case 'checkbox':
                                if (inputField.is(':checked')) {
                                    addHiddenField(inputField.attr('name'), inputField.val());
                                }
                                break;
                                
                            case 'radio':
                                break;
                                
                            default:
                                break;
                        }
                        break;
                }

                return true;
            });
        },

        validate:function(form) {
            // Text inputs
            try {
                $('input[type=text], input[type=password], textarea', form).each(function(){
                    if (!$(this).hasClass('optional')) {
                        var field = new HR.form.textInput({
                            selector:'#'+$(this).attr('id')
                        });

                        var fieldResponse = field.validate();
                        if (fieldResponse !== true) {
                            throw fieldResponse;
                        }
                    }
                });
            } catch (fieldResponse) {
                if (fieldResponse instanceof HR.internal.message) {
                    return fieldResponse;
                }
            }

            return true;
        },

        rememberSelectedButton:function(event, form) {
            if ($('input[type=hidden][name=selectedButton]', form).length === 0) {
                $(form).append(
                    $('<input type="hidden" name="selectedButton" />')
                );
            }
            
            $('input[type=hidden][name=selectedButton]', form).val($(event.target).attr('name'));
            return true;             
        },

        textInput:function(inboundOptions) {
            var self = this;
            var options = new HR.optionSet(inboundOptions);
            var element = null;
            
            if (options.contains('scope')) {
                element = $(options.get('selector'), options.scope);
            } else {
                element = $(options.get('selector'));
            }

            this.prepare = function() {
                element.val($.trim(element.val()));
            };

            this.validate = function() {
                this.prepare();

                if (!this.isFilled()) {
                    element.key = element.attr('name');

                    var message = new HR.internal.message({
                        note:'input is not allowed to be blank',
                        type:'error',
                        code:100,
                        namespace:'validation',
                        data:{
                            key:element.attr('name'),
                            object:element
                        }
                    });

                    return message;
                }

                return true;
            };

            this.element = function() {
                return element;
            };

            this.isFilled = function() {
                return $.trim(element.val()) != '';
            };

            this.name = function() {
                return element.attr('name');
            };

            this.value = function() {
                return element.val();
            };

            this.persist = function() {
                var setInHash = function() {
                    HR.location.hash.set(self.name(), self.value());
                };
                
                var setInLinks = function() {
                    $('.'+element.attr('id')).each(function(){
                        var currentLink = $(this);                        
                        var currentHref = currentLink.attr('href');

                        var newHref = currentHref.replace(
                            self.name()+'='+$.url.setUrl(currentHref).param(self.name()),
                            self.name()+'='+$.URLEncode(self.value())
                        );

                        currentLink.attr('href', newHref);
                    });
                };
                
                // Retrieve value from hash
                if (this.isFilled()) {
                    setInHash();
                } else {
                    element.val(HR.location.hash.get(this.name()));
                }

                setInLinks();
                
                // Retain value in URL hash
                element.change(function(){
                    setInHash();
                    setInLinks();
                }).keyup(function(){
                    setInHash();
                    setInLinks();
                });
            };
        }
    },

    date:{
        daysInMonth:function() {
            var now = new Date();
            return 32 - new Date(now.getFullYear(), now.getMonth(), 32).getDate();
        }
    },

    incrementor:function(suppliedOptions) {
        var self = this;
        var globalOptions = suppliedOptions;

        var base = 2;
        var delay = 200;
        var startTime = false;
        var amount = globalOptions.initialAmount;
        var timeout = false;

        var tickInterval = false;

        var hasStarted = function() {
            return (startTime !== false);
        };

        this.duration = function() {
            if (startTime === false) {
                startTime = new Date();
            }

            var now = Math.floor(new Date().getTime() / 1000);
            var start = Math.floor(startTime.getTime() / 1000);

            return now - start;
        };

        this.logDuration = function() {
            var duration = this.duration();
            return (duration === 0) ? 0 : Math.log(duration)/Math.log(base);
        };

        var increment = function(incrementOptions) {
            if (incrementOptions.direction == 'up' && amount >= globalOptions.maximum) {
                return false;
            } else if (incrementOptions.direction == 'down' && amount <= globalOptions.minimum) {
                return false;
            }

            var incrementFactor = Math.ceil(self.duration());
            var incrementAmount = Math.pow(base,incrementFactor);
            var difference = 0;

            for (var i=0; i<incrementAmount; i++) {
                if (incrementOptions.direction == 'up') {
                    if (amount <= globalOptions.maximum) {
                        difference = globalOptions.maximum - amount;
                        if (difference <= incrementOptions.delta) {
                            amount = parseInt(amount) + difference;
                        } else {
                            amount = parseInt(amount) + incrementOptions.delta
                        }
                    }            
                    
                } else {
                    if (amount >= globalOptions.minimum) {
                        difference = amount - globalOptions.minimum;
                        if (difference <= incrementOptions.delta) {
                            amount = parseInt(amount) - difference;

                        } else {
                            amount = parseInt(amount) - incrementOptions.delta;
                        }

                        if (amount <= 0) {
                            amount = 0;
                        }
                    }
                }
            }

            timeout = window.setTimeout(function(){
                increment(incrementOptions);
            }, delay);

            return true;
        };

        this.setAmount = function(newAmount) {
            amount = newAmount;
        };

        this.start = function(incrementOptions) {
            if (hasStarted()) {
                return true;
            }

            if (typeof(incrementOptions.callback) == 'function') {
                tickInterval = window.setInterval(
                    incrementOptions.callback, incrementOptions.callbackInterval
                );
            }

            return increment(incrementOptions);
        };

        this.stop = function() {
            startTime = false;
            window.clearInterval(tickInterval);
            window.clearTimeout(timeout);

            return true;
        };

        this.amount = function() {
            return amount;
        };

    },

    ui:{
        addClass:function(object, className, speed){
            if (jQuery.browser.msie) {
                return object.addClass(className);
            } else {
                return object.addClass(className, speed);
            }  
        },

        removeClass:function(object, className, speed, callback){
            if (jQuery.browser.msie) {
                object.removeClass(className);
                if (typeof callback == 'function') {
                    callback();
                }

                return object;
            } else {
                return object.removeClass(className, speed, callback);
            }
        },

        message:{
            exists:function(message){
                // HR.constants.messages.<namespace>.<type>.<code>.<dataKey>
               
                if (typeof(HR.constants.messages[message.namespace]) == 'undefined') {
                    return false;
                }
                
                if (typeof(HR.constants.messages[message.namespace][message.type]) == 'undefined') {
                    return false;
                }      
                
                if (typeof(HR.constants.messages[message.namespace][message.type][message.code]) == 'undefined') {
                    return false;
                }   
                
                if (typeof(HR.constants.messages[message.namespace][message.type][message.code][message.data.key]) == 'undefined') {
                    return false;
                }

                return true;
            },

            getKeyString:function(response){
                // HR.constants.messages.<namespace>.<type>.<code>.<dataKey>
                return 'HR.constants.messages.'+response.namespace+'.'+response.type+'.'+response.code+'.'+response.data.key;
            },

            message:function(message) {
                if (!HR.ui.message.exists(message)) {
                    return false;
                }

                var returnMessage = {
                    heading:'',
                    body:''
                };

                if (typeof HR.constants.messages[message.namespace][message.type][message.code][message.data.key].heading == 'string') {
                    returnMessage.heading = HR.constants.messages[message.namespace][message.type][message.code][message.data.key].heading;
                }

                if (typeof HR.constants.messages[message.namespace][message.type][message.code][message.data.key].body == 'string') {
                    returnMessage.body = HR.constants.messages[message.namespace][message.type][message.code][message.data.key].body;
                }

                return returnMessage;
            },

            clearAll:function() {
                $('div.message.error, div.message.confirm').each(function(){                    
                    if ($('form', this).length > 0) {
                        return true;
                    }
                    
                    if ($(this).is('.persist')) {
                        return true;
                    }

                    if ($('form', this).length === 0) {
                        $(this).remove();
                    }                   
                });
            },

            fadeAll:function(target) {
                if (typeof(target) != 'undefined') {
                    var errorContainer = target.parents('div.message.error:closest');
                    if (errorContainer.length === 0) {
                        errorContainer = target.parents('div.message.confirm:closest');
                    }

                    var classes = errorContainer.attr('class').split(' ');

                    var classMatcher = /for=.*/;
                    var forId = null;

                    for (var classIndex in classes) {
                        if (classes.hasOwnProperty(classIndex)) {
                            if (classMatcher.test(classes[classIndex])) {
                                forId = classes[classIndex].replace('for=', '');
                            }
                        }
                    }

                    if (forId !== null) {
                        $('#'+forId).focus();
                    }
                }

                $('div.message.error, div.message.confirm').each(function(){
                    if ($(this).is('.persist')) {
                        return true;
                    }

                    $(this).fadeOut('normal', function(){
                        window.setTimeout(function(){
                            if ($.browser.msie) {
                                $('input.error, textarea.error').removeClass('error');
                            } else {
                                $('input.error, textarea.error').removeClass('error', 300);
                            }
                            
                            window.setTimeout(function(){
                                HR.ui.message.clearAll();
                            }, 300);
                           
                        }, 350);
                    });

                    return true;
                });

            },
            
            display:function(options) {
                var defaults = {
                    content:{
                        heading:'Error!',
                        body:null
                    },
                    type:'error',
                    preCallback:null,
                    postCallback:null,
                    callback:null,
                    data:null,
                    mappings:null
                };               
                
                var settings = $.extend(defaults, options);
                
                HR.ui.message.clearAll();
                if (typeof(settings.preCallback) == 'function') {
                    settings.preCallback();
                }
                
                window.setTimeout(function(){
                    if (settings.data.mappings instanceof Object) {
                        for (var mappingIndex in settings.data.mappings) {
                            if (settings.data.mappings.hasOwnProperty(mappingIndex)) {
                                var replacementKey = '{'+mappingIndex+'}';

                                while (settings.content.heading.indexOf(replacementKey) != -1) {
                                    settings.content.heading = settings.content.heading.replace(replacementKey, settings.data.mappings[mappingIndex]);
                                }

                                while (settings.content.body.indexOf(replacementKey) != -1) {
                                    settings.content.body = settings.content.body.replace(replacementKey, settings.data.mappings[mappingIndex]);
                                }
                            }
                        }
                    }

                    var error = $('<div />').addClass('message').addClass(settings.type).append(
                                   $('<div />').append(
                                       $('<h2 />').text(settings.content.heading)
                                   ).append(
                                       settings.content.body
                                   )
                                ).hide();

                    $('#main').before(error);

                    error.fadeIn('slow', settings.postCallback);
                    if (settings.data.object) {
                        error.addClass('for='+settings.data.object.attr('id'));
                    }

                    HR.ui.message.displayCloseIcon();
                    HR.forms.enableInputButtons();
                }, 300);
            },

            displayCloseIcon:function() {
                $('div.message.error, div.message.confirm').each(function(){
                    var message = $(this);
                    if (message.is('.persist')) {
                        return true;
                    }

                    var messageType = message.is('.error') ? 'error' : 'confirm';
                    var close = $('<span title="Close" />').text('[x] Close');
                    message.prepend(close);

                    var colours = {
                        error:{
                            standard:'#DB0C07',
                            hover:'#f00'
                        },
                        confirm:{
                            standard:'#008000',
                            hover:'#079525'
                        }

                    };

                    close.css({
                        'float':'right',
                        'top':0,
                        'cursor':'pointer',
                        'color':colours[messageType].standard,
                        'padding':'10px',
                        'margin':'-20px -20px 0 0'
                    }).mouseover(function(){
                        $(this).css({
                            'color':colours[messageType].hover
                        });
                    }).mouseout(function(){
                        $(this).css({
                            'color':colours[messageType].standard
                        });
                    });

                    close.click(function(){
                        HR.ui.message.fadeAll($(this));
                    });
                });
            }

        },
        
        expandable:{
            initialise:function() {
                var expandableSections = $('.expandable');
                
                expandableSections.each(function(){
                    if ($('.expandable-control', this).length === 0) {
                        return false;
                    }

                    if ($('.expandable-feature', this).length === 0) {
                        return false;
                    }
                    
                    var control = $($('.expandable-control', this).get(0));
                    var feature = $($('.expandable-feature', this).get(0));

                    // Hide feature if it doesn't contain a state=1 flag'
                    if ($('.expandable-state', this).length === 0) {
                        feature.hide();
                    } else {
                        var showFeature = ($($('.expandable-state', this).get(0)).val());
                        if (showFeature != 1) {
                            feature.hide();
                        }
                    }

                    // Add show/hide handler
                     var controlAnchor = $('<a href="#" />')
                                         .text(control.text())
                                         .click(function () {
                                             return HR.ui.expandable.click(this);
                                         }).append($('<span>&#160;</span>')
                                         ).append('<span class="indicator">&#9660;</span>');

                     control.html(controlAnchor);
                     return true;
                });
            },
            
            click:function(controlAnchor) {                
                var feature = $($('.expandable-feature', $(controlAnchor).closest('.expandable')).get(0));
                var arrow;
                
                if (feature.css('display') == 'block') {
                    feature.slideUp();
                    arrow = $('<span class="indicator">&#9660;</span>');                    
                } else {
                    feature.slideDown();
                    arrow = $('<span class="indicator">&#9650;</span>');                    
                }

                $('span.indicator', $(controlAnchor).closest('.expandable-control')).replaceWith(arrow);
                
                return false;
            }
        }
    },    
    
    initialise:{
        global:function() {
            // Smooth scrolling for in-page links
            $('a[href^=#]').click(function() {

                HR.window.scrollTo2({
                    target: $(this).attr('href'),
                    offset: -30,
                    duration:600
                });
            });

            // Persist form values
            $('input.persist').each(function(){
                var currentField = new HR.form.textInput({
                    selector:'#'+$(this).attr('id')
                });

                currentField.persist();
            });

            // Set default input field style,
            // Highlight/dehighlight selected input field
            $('input[type=text],input[type=password], select, textarea')
                .addClass('standby')
                .focus(function() {
                    $(this).addClass('focused');
                })
                .blur(function() {
                    $(this).removeClass('focused');
                });

            // Style focused links the same has hovered links
            // -> makes tabbing through links more usable
            $('a')
            .focus(function(){
                $(this).addClass('focused');
            })
            .blur(function(){
                $(this).removeClass('focused');
            });

            // Zebra-stripe all tables
            $("tr:nth-child(even)").addClass("highlightedRow");

            // Move focus to the first element that asks for it
            if ($('.givemefocus').length) {
                $('.givemefocus').get(0).focus();
            }

            // Expandable
            HR.ui.expandable.initialise();

            // Add close icon to errors
            $('div.message.error, div.message.confirm').hide();
            HR.ui.message.displayCloseIcon();
            $('div.message.error, div.message.confirm').fadeIn(2000);
            
            // Remember the clicked upon button for multio-button forms
            // -> because form buttons are disabled on submission and so those data values aren't sent
            $('form').click(function(event){
                HR.form.rememberSelectedButton(event, this);
            });

            // Add standard form handling
            $('form').submit(function(event){
                HR.forms.disableInputButtons();

                try {
                    // Validation methods should throw exceptions when errors occur.
                    // If we get to the end of this try block then validation
                    // is assumed to have been successful.
                    // We will return true to let the form submit.
                    // Return a function from your custom validator to override this.

                    // Copy all fields from error messages into hidden fields in this form
                    // -> error messages are disposed of, and with them any inputs they contain
                    HR.form.cloneMessageFields(this);
                    HR.ui.message.clearAll();

                    var standardValidationResponse = HR.form.validate($(this));
                    if (standardValidationResponse instanceof HR.internal.message) {
                        throw standardValidationResponse;
                    }

                    var customValidationResponse = HR.internal.customHandler.run('form.validate', this);
                    switch (typeof customValidationResponse) {
                    case 'function':
                        return customValidationResponse();

                    case 'object':
                        if (customValidationResponse instanceof HR.internal.message) {
                            throw customValidationResponse;
                        }

                        return false;

                    case 'boolean':
                        if (customValidationResponse === false) {
                            HR.forms.enableInputButtons();
                            return false;
                        }
                    }
                    
                    if ($(this).is('.handleSubmission')) {
                        var customHandler = HR.internal.customHandler.get('form.submit');
                        if (typeof customHandler == 'function') {
                            HR.log("Handling submission ...");

                            var submissionResponse = HR.form.submit.getResponse.call(this);
                            var submissionHandlerResponse = customHandler(submissionResponse, this);
                            
                            switch (typeof submissionHandlerResponse) {
                            case 'function':
                                return submissionHandlerResponse();

                            case 'object':
                                if (submissionHandlerResponse instanceof HR.internal.message) {
                                    throw submissionHandlerResponse;
                                }

                                return false;

                            case 'boolean':
                                if (submissionHandlerResponse === false) {
                                    HR.forms.enableInputButtons();
                                    return false;
                                }
                            }
                        }
                    }
                    
                    return true;
                } catch (response) {
                    if (response instanceof HR.internal.message) {
                        if (HR.ui.message.exists(response)) {
                            
                            HR.ui.message.display({
                                content:HR.ui.message.message(response),
                                type:response.type,
                                data:response.data,
                                postCallback:function(){
                                    response.data.object.focus();                                    
                                    if (response.type == 'error') {
                                        HR.ui.addClass(response.data.object, 'error', 'normal');
                                    }
                                }
                            });

                            return false;
                        } else {
                            if (HR.internal.isLive()) {
                                return true;
                            }

                            HR.log('error, no message for it');
                            HR.log(response);
                            HR.log(HR.ui.message.getKeyString(response));
                            return false;
                        }                       
                    }
                    
                    return true;
                }

                return false;
            });

            var timeouts = {};

            $('input').each(function(){
                var removeError = function(input){
                    return function() {
                        HR.ui.removeClass(input, 'error', 300, function(){
                            delete(timeouts[$(this).attr('id')]);                            
                        });
                    };
                    
                };

                var removeErrorIfPresent = function(field) {
                    if (field.hasClass('error')) {
                        if (typeof(timeouts[field.attr('id')]) == 'undefined') {
                            timeouts[field.attr('id')] = window.setTimeout(
                                removeError(field),
                                750
                            );
                        }
                    }
                };

                $(this).keydown(function(){
                    removeErrorIfPresent($(this));
                });

                $(this).change(function(){
                    removeErrorIfPresent($(this));
                });

                $(this).mousedown(function(){
                    removeErrorIfPresent($(this));
                });
            });

            // Run page-specific initialisers
            HR.internal.customHandler.run('initialise');
        }
    }
};

// Go!
$(document).ready(function () {
    HR.initialise.global();
});