/**
 * 
 * @fileOverview  Definition for the base namespaces, classed and methods to use in the library
 *  
 * @author WHL Member
 */


/**
 * @namespace This is a base namespace for all classes/objects in library.
 */
var Whl = {};

/**
 *  Copy properties of a object for other object
 *  
 * @lends Object
 * @param {object} source object
 * @param {object} destination object
 * @returns {Object}
 */
Object.extend = function(destination, source) {
    for (var key in source) {
        destination[key] = source[key];
    }
    return destination;
};

/**
 * Extend properties, methods for the built-in object : Object.
 */
(function() {
    Object.extend(Object, /** @lends Object */{
        /**
         * Get class name of object
         * @param {object} object
         * @returns {String} Class Name 
         */
        getClassName: function(object) {
            var objString = Object.prototype.toString.call(object);
            return objString.match(/^\[object\s(.*)\]$/)[1];
        },
        /**
         * Check the variable is underfined type or not.
         * @param {object} object
         * @returns {Boolean} Return true if object is of undefined type, otherwise return false
         */
        isUndefined: function(object) {
            return (typeof object === 'undefined');
        },
        /**
         * Check the variable is number or not
         * @param {object} object
         * @returns {Boolean} Return true if object is of number type, otherwise return false
         */
        isNumber: function(object) {
            return (Object.getClassName(object) === 'Number');
        },
        /**
         * Check the variable is string or not
         * @param {object} object
         * @returns {Boolean} Return true if object is of string type, otherwise return false
         */
        isString: function(object) {
            return (Object.getClassName(object) === 'String');
        },
        /**
         * Check the variable is array or not
         * @param {object} object
         * @returns {Boolean} Return true if object is of array type, otherwise return false
         */
        isArray: function(object) {
            return (Object.getClassName(object) === 'Array');
        },
        /**
         * Check the variable is a function or not
         * @param {object} object
         * @returns {Boolean} Return true if object is a function, otherwise return false
         */
        isFunction: function(object) {
            return (typeof object === 'function');
        },
        isObject: function(object) {
            return (typeof object === 'object');
        },
        /**
         * Get all keys of the object
         * @param {object} object
         * @retuns {Array} Array keys
         */
        getKeys: function(object) {
            var keys = [];
            for (var key in object) {
                keys.push(key);
            }
            return keys;
        },
        /**
         * Get all keys of the object
         * @param {object} object
         * @retuns {Array} Array keys
         */
        getValues: function(object) {
            var values = [];
            for (var key in object) {
                values.push(object[key]);
            }
            return values;
        }
        //More here ...
        
    });
})();


/**
 * Extend properties, methods for the built-in class Function
 */
(function(){
    /**
     * Convert arguments to array
     * @param {Array} args
     * @returns {Array} Actual Array
     */
    function toArray(args) {
        var array = [];
        for (var i=0; i<args.length; i++) {
            array.push(args[i]);
        }
        return array;
    }
    Object.extend(Function.prototype, /** @lends Function.prototype */ {
        /**
         * Add object for the function's current context
         * @param {object} object It will be this in function
         * @returns {Function} Return an annonymous function with object is the this context in function
         */
        bind: function(object) {
            if (arguments.length < 2 && Object.isUndefined(arguments[0])) {
                return this;
            }
            var method = this;
            var args = Array.prototype.slice.call(arguments, 1);
            return function() {
                return method.apply(object, args.concat(toArray(arguments)));
            };
        },
        /**
         * Add object for the function's current context
         * @param {object} object It will be this object of function
         * @returns {Function} Return an annonymous function with object is the this context in function
         * and event object is passed throught the first parameter.
         */
        bindEvent: function(object) {
            var method = this;
            var args = Array.prototype.slice.call(arguments, 1);
            return function(event) {
                var args1 = Array.prototype.slice.call(arguments, 1);
                args = args.concat(args1);
                return method.apply(object, args.concat([event || window.event]));
            };
        }
        
        //Add more methods for Function class ...
    });
})();

/**
 * Add more methods for the built-in class: String.
 */
Object.extend(String.prototype, /** @lends String.prototype */ {
    /**
     * trim left and right whitespace of a string
     * @returns {String}
     */
    trim: function() {
        return this.replace('/^\s+/', '').replace('/\s*$/','');
    },
    /**
     * Validate email
     * @returns {Boolean}
     */
    isEmail: function() {
		return (this.search(/^\w+((-\w+)|(\.\w+))*\@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z0-9]{2,6}$/) == -1) ? false : true ;
    },
	/**
     * Validate URL
     * @returns {Boolean}
     */
	isUrl: function() {
		return (this.search(/^(http|https):\/\/(([A-Z0-9][A-Z0-9_-]*)(\.[A-Z0-9][A-Z0-9_-]*)+)(:(\d+))?(\/)?/i) == -1) ? false : true ;
	},
    /**
     * Auto-URL from a string
     * @param {String} val Raw name
     * @returns {String} URL
     */
    autoUrl: function() {
        var url = this.replace(/[^0-9a-zA-Z]/g, " ");
        url = url.replace(/\s\s+/g, " ").trim();
        url = url.replace(/\s/g, "_");
        return url;
    },
    /**
     * Auto-URL from a string
     * @param {String} val Raw name
     * @returns {String} URL
     */
    autoUrl2: function() {
        var url = this.replace(/[^0-9a-zA-Z]/g, " ");
        url = url.replace(/\s\s+/g, " ").trim();
        url = url.replace(/\s/g, "-");
        return url.toLowerCase();
    },
    /**
     * Compare with a string
     * 
     * @param {String} str
     * @returns {Boolean}
     */
    compare: function(str) {
        return (this == str);
    }
});
/**
 * Add more static methods for the built-in class: Number.
 */
/**
 * Compare to number
 * 
 * @lends Object
 * @param {Number} num1
 * @param {Number} num2
 * @returns {Number} 0: Equal, < 0: This number is less than the number, > 0: Is greater than
 */
Number.compareFloat = function(num1, num2) {
    num1 = parseFloat(num1);
    num2 = parseFloat(num2);
    if (num1 == num2) return 0;
    else if (num1 < num2) return -1;
    else return 1;
};

/**
 * Add more static methods for the built-in class: Date.
 */
/**
 * Compare two string date
 * 
 * @lend Date
 * @param {String} date1
 * @param {String} date2
 * @param {Number} type 1: the input date in format mm dd yyyy, 0: dd mm yyyy
 * @returns {Number} 0: Equal, < 0: Lest than, > 0: greater than
 */
Date.compareDate = function(date1, date2, type) {
    if (!type) type=0;
    var dateInput=(type==1)?1:0;
    var dateType=new Array();
    dateType[0]=/^\d{1,2}(\-|\/|\.|\s)\d{1,2}(\-|\/|\.|\s)\d+$/;    //date format dd mm yyyy
    dateType[1]=/^\d{1,2}(\-|\/|\.|\s)\d{1,2}(\-|\/|\.|\s)\d+$/;    //date format mm dd yyyy

    if (date1.search(dateType[dateInput])==-1||date2.search(dateType[dateInput])==-1) throw 'Invalid date';
    
    var seperator1=(date1.indexOf("-")!=-1)?"-":(date1.indexOf("/")!=-1)?"/":(date1.indexOf(".")!=-1)?".":(date1.indexOf(" ")!=-1)?" ":"";
    var seperator2=(date2.indexOf("-")!=-1)?"-":(date2.indexOf("/")!=-1)?"/":(date2.indexOf(".")!=-1)?".":(date2.indexOf(" ")!=-1)?" ":"";
    
    if (seperator1==""||seperator2=="") throw 'Invalid date';
    
    var dateArr1=date1.split(seperator1);
    var dateArr2=date2.split(seperator2);
    
    if (dateArr1.length!=3||dateArr2.length!=3) throw 'Invalid date';
    
    if (dateInput==1){
        var year1 = dateArr1[2];
        var year2 = dateArr2[2];
        var month1 = dateArr1[0];
        var month2 = dateArr2[0];
        var day1 = dateArr1[1];
        var day2 = dateArr2[1];
    }else{
        var year1 = dateArr1[2];
        var year2 = dateArr2[2];
        var month1 = dateArr1[1];
        var month2 = dateArr2[1];
        var day1 = dateArr1[0];
        var day2 = dateArr2[0];
    }
    var result = null;
    result = Number.compareFloat(year1, year2);
    if (result != 0) return result;
    result = Number.compareFloat(month1, month2);
    if (result != 0) return result;
    result = Number.compareFloat(day1, day2);
    if (result != 0) return result;
    return 0;
};

/**
 * Compare two string date with the format dd mm yyy (eg: 01 Jan 2009)
 * 
 * @lend Date
 * @param {String} date1
 * @param {String} date2
 * @returns {Number} 0: Equal, < 0: Lest than, > 0: greater than
 */
Date.compareDate2 = function(date1, date2) {
    var format = /^\d{1,2}\s[A-Z][a-z]{2}\s[0-9]{4}$/;
    if (date1.search(format) == -1 || date2.search(format) == -1) throw 'Invalid Date';
    date1 = date1.split(' ');
    date2 = date2.split(' ');
    var shortMonth = {Jan: 1, Feb: 2, Mar: 3, Apr: 4, May: 5, Jun: 6, Jul: 7, Aug: 8, Sep: 9, Oct: 10, Nov: 11, Dec: 12};
    for( var key in shortMonth) {
        if (key == date1[1]) {
            date1[1] = shortMonth[key];
            break;
        }
    }
    for( var key in shortMonth) {
        if (key == date2[1]) {
            date2[1] = shortMonth[key];
            break;
        }
    }
    var result = null;
    result = Number.compareFloat(date1[2], date2[2]);
    if (result != 0) return result;
    result = Number.compareFloat(date1[1], date2[1]);
    if (result != 0) return result;
    result = Number.compareFloat(date1[0], date2[0]);
    if (result != 0) return result;
    return 0;
};

/**
 * Convert a string to date value - format dd mmm yyy (eg: 01 Jan 2009)
 * 
 * @lend Date
 * @param {String} strDate
 * @param {String} date2
 * @returns {Number} 0: error, > 0: date value
 */
Date.toDate = function(strDate) {
    var format = /^\d{1,2}\s[A-Z,a-z]{3}\s\d{2,4}$/;
		var resDate = new Date();
    if (strDate.match(format) == null) return(0);
    strDate = strDate.split(' ');
    var shortMonth = {jan: 1, feb: 2, mar: 3, apr: 4, may: 5, jun: 6, jul: 7, aug: 8, sep: 9, oct: 10, nov: 11, dec: 12};
		var bMatch = false; strDate[1] = strDate[1].toLowerCase();
    for( var key in shortMonth) {
        if (key == strDate[1]) {
            resDate.setMonth( shortMonth[key] );
						bMatch = true;
            break;
        }
    }
		if (!bMatch) return(0);
		strDate[2] = parseFloat(strDate[2]);
		resDate.setDate(strDate[0]);
		resDate.setYear(strDate[2]>100 ? strDate[2] : strDate[2]+2000);
    return resDate;
};

/**
* Display date as string
*
* @returns {String} dd mmm yyyy
*/
Date.prototype.toDateStringExt = function(){
    var shortMonth = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
    if (this.valueOf()==0) return('');
    return(this.getDate()+' '+shortMonth[this.getMonth()]+' '+this.getFullYear());
};

/**
* Display date/time as string
*
* @returns {String} dd mmm yyyy
*/
Date.prototype.toDateTimeString = function(){
    if (this.valueOf()==0) return('');
    return(this.toDateStringExt()+' '+this.getHours()+':'+this.getMinutes()+':'+this.getSeconds());
};


/**
 * @namespace Combo Contains functions, class for manipulating with combobox, listbox
 */
Whl.Combo = {};
/**
 * Extend more functions for this namespace
 */
Object.extend(Whl.Combo, {
    /**
     * Append a list of functions for the combo control and return the current combo control
     * @memberOf Whl.Combo
     * @param {String} elmId Combo control id
     * @returns {DOM Element} Combo control
     */
    get: function(elmId) {
        var elm = Object.isString(elmId) ? document.getElementById(elmId) : elmId;
        if (!Object.isObject(elm)) return null;
        else if (Object.isFunction(elm.addItem)) return elm;
        Object.extend(elm, {
            /**
             * Add Item Option
             * @param {Object} item             Object contain text, value
             * @param {String} item.text        Text of the option
             * @param {String} item.value       Value of the option
             * @param {Boolean} item.selected   Item is selected or not
             * @returns {Dom Element} Combo Control
             */
            addItem: function(item) {
                if (Object.isUndefined(item.selected)) {
                    this.options.add(new Option(item.text, item.value));
                } else {
                    this.options.add(new Option(item.text, item.value, item.selected));
                }
                return this;
            },
            addItems2: function(items) {
                for (var key in items) {
                    var item = {};
                    for (var id in items[key]) {
                        item.text = items[key][id];
                        item.value = id;
                        break;
                    }
                    this.addItem(item);
                }
            },
            /**
             * Remove an item option
             * @param {Number} index      Seleted item index
             * @returns {Dom Element} Combo Control
             */
            removeItem: function(index) {
                this.options[index]  = null;
                return this;
            },
            /**
             * Select first item from Combobox
             * @returns {Dom Element} Combo Control
             */
            deselectAll: function() {
                for(var i=0; i<this.options.length; i++) { 
                    this.options[i].selected = false;
                }
                return this;
            },
            /**
             * Select all items from Combobox
             * @returns {Dom Element} Combo Control
             */
            selectAll: function() {
                for(var i=0; i<this.options.length; i++) { 
                    this.options[i].selected = true;
                }
                return this;
            },
            /**
             * Remove all item
             * @returns {Dom Element} Combo Control
             */
            removeAll: function() {
                this.length = 0;
                return this;
            },
            /**
             * Check if exists items
             * @returns {Dom Element} Combo Control
             */
            hasItem: function() {
                return (this.options.length > 0) ? true : false ;
            },
            /**
             * Remove all the selected items
             * 
             * @returns {Dom Element} Combo Control
             */
            removeSelected: function() {
                for (var i=this.options.length-1; i>=0; i--) {
                    if (this.options[i].selected) {
                        this.removeItem(i);
                    }
                }
                return this;
            },
            /**
             * Get Value from the combobox and return a list of object include of text and value
             * Can remove the option which get value or not, default is not allowed to remove.
             * 
             * @param {Boolean} all     True: Get all optons
             * @param {Boolean} remove  True: Remove the option
             * @param {Array} 
             */
            getObjValue: function(all, remove) {
                var selected = all;
                var remove = remove || false;
                var obj = [];
                for (var i=this.options.length-1; i>=0; i--) {
                    var option = this.options[i];
                    var item = null;
                    if (selected) {
                        if (option.selected) {
                            item = {'text': option.text, 'value': option.value};
                        }
                    } else {
                        item = {'text': option.text, 'value': option.value};
                    }
                    if (item != null) {
                        obj.push(item);
                        if (remove) {
                            this.removeItem(i);
                        }
                    }
                }
                return obj;
            },
            /**
             * Add items to combobox
             * @param {Array} Array of object : {text: '', value: '', selected: true}
             * @param {Array} Array of value that exclude from the items
             * @returns {DOM Element} Combo control
             */
            addItems: function(items, excludedItems) {
                for(var key in items) {
                    var item = items[key];
                    if (!Object.isUndefined(excludedItems)) {
                        var exclude = false;
                        for (var i=0, length=excludedItems.length; i < length; i++) {
                            if (excludedItems[i] == item.value) {
                                exclude = true;
                                break;
                            }
                        }
                        if (!exclude) this.addItem(item);
                    } else {
                        this.addItem(item);
                    }
                }
                return this;
            },
            addItemObj: function(items, text, value, selectedItems) {
                for (var idx in items) {
                    var obj = items[idx];
                    var item = {text: '', value: ''};
                    for (var member in obj) {
                        if (member == text) {
                            item.text = obj[member];
                        } else if (member == value) {
                            item.value = obj[member];
                        }
                    }
                    this.addItem(item);
                }
                this.setSelectedItems(selectedItems);
                return this;
            },
            /**
             * Get values of the control
             * @param {Boolean} all Get all options' value
             * @returns {String} A string of value
             */
            getValues: function(all) {
                var selected = all || false;
                var values = '';
                for (var i=0, length = this.options.length; i < length; i++) {
                    var option = this.options[i];
                    if (selected) {
                        values += (values ? ',' : '') + option.value;
                    } else {
                        values += (values ? ',' : '') + option.value;
                    }
                }
                return values;
            },
            /**
             * Set selected items for the combobox
             * 
             * @param {Object} items
             * @returns {Element DOM}
             */
            setSelectedItems: function(items) {
                if (Object.isArray(items)) {
                    for (var key in items) {
                        for (var i=0, length = this.options.length; i < length; i++) {
                            if (items[key] == this.options[i].value) {
                                this.options[i].selected = true;
                                break;
                            }
                        }
                    }
                }
                return this;
            },
            /**
             * Get current selected value
             * 
             * @returns {String}
             */
            getValue: function() {
                return this.options[this.selectedIndex].value;
            }
        });
        return elm;
    }
    //add more func
});
/**
 * A control for manipulating with double combobox: Change the first combo, the second combobox will change data.
 * 
 * @class Double combobox
 * @param {String} srcId Source combobox id
 * @param {String} destId Target combobox id
 * @param {String|Object} url 
 */
Whl.Combo.Double = function(srcId, destId, url, opt) {
    this._src = Whl.Combo.get(srcId);
    this._dest = Whl.Combo.get(destId);
    this._data = null;
    this._url = null;
    this._opt = opt || {};
    this._opt.param = this._opt.param || {};
    this._opt.excludeElm = this._opt.excludeElm || null;
    this._opt.paramId = this._opt.paramId || [];
    this._opt.defaultItems = this._opt.defaultItems || [];
    this._opt.callback = this._opt.callback || function() {};
    if (Object.isObject(url)) this._data = url;
    else if (Object.isString(url)) this._url = url;
    this._initialize();
};
Whl.Combo.Double.prototype = {
    /**
     * Initialize the control
     * 
     * @returns {void}
     */
    _initialize: function() {
        if (this._url != null) {
            $(this._src).change(this.getData.bind(this, null));
        } else {
            $(this._src).change(this.load2.bind(this));
        }
    },
    _makeParam: function() {
        var param = '';
        for (var key in this._opt.param) {
            param += (param ? '&' : '') + key + '=' +  this._opt.param[key];
        }
        for (var key in this._opt.paramId) {
            param += (param ? '&' : '') + this._opt.paramId[key] + '=' +  $('#' + this._opt.paramId[key]).val();
        }
        return param;
    },
    /**
     * Get the data from the url
     * 
     * @returns {void}
     */
    getData: function() {
        var param = this._makeParam();
        param += (param ? '&' : '') + Whl.getToken() + '&' + $.param($(this._src));
        var option = {
            type: 'POST',
            url: this._url,
            dataType: 'json',
            data: param,
            success:  this.load.bind(this),
            error: function(XMLHttpRequest, textStatus, errorThrown) {
                Whl.Dialog.error({msg: textStatus});
            }
        };
        $.ajax(option);
    },
    /**
     * Load data for the second dialog
     * 
     * @param {Array} data
     * @param {String} statusText
     * @returns {void}
     */
    load: function(data, statusText) {
        this._dest.removeAll();
        if (0 < this._opt.defaultItems.length) {
            for (var key in this._opt.defaultItems) {
                this._dest.addItem(this._opt.defaultItems[key]);
            }
        }
        if (this._opt.excludeElm) {
            var excludedItems = Whl.Combo.get(this._opt.excludeElm).getValues(true);
            this._dest.addItems(data, excludedItems.split(','));
        } else {
            this._dest.addItems(data);
        }
    },
    load2: function() {
        this._dest.removeAll();
        var srcValue = $(this._src).val();
        if (0 < this._opt.defaultItems.length) {
            for (var key in this._opt.defaultItems) {
                this._dest.addItem(this._opt.defaultItems[key]);
            }
        }
        if (!Object.isUndefined(this._data[srcValue])) {
            var items = this._data[srcValue];
            this._dest.addItems2(items);
        }
        this._opt.callback.call(this);
    },
    /**
     * Set callback function to execute after change event occurs
     * 
     * @returns {void}
     */
    setCallback: function(callback) {
        this._opt.callback = callback;
    }
};

/**
 * @namespace Dialog Contains functions, class for manipulating with the model dialog
 */
Whl.Dialog = {};

Object.extend(Whl.Dialog, {
    /**
     * Display an model dialog: message/confirm/error/... dialog, default is message dialog
     *
     * @memberOf Whl.Dialog
     * @param {Object} option
     * @param {String} option.title Title of the dialog
     * @param {String} option.msg Contain of the dialog
     * @param {String} option.id Id of the dialog
     * @param {String} option.icon Icon view to display more information
     * @param {String} option.contCss Css for the contain in dialog
     * @param {String} option.container Container that contains the dialog
     * @param {Object} option.buttons Buttons for dialog
     * @param {Boolean} option.hideClose Hide the Close button of dialog
     * @param {Boolean} option.remove Remove the dialog from the form after close event occur
     * @returns {void}
     */
    show: function(option) {
        var opt = {title: 'Loading', msg: 'Loading data ... Please wait!.', id: 'msg', icon: 'ui-icon-info', contCss: '', container: 'body', buttons: {}, hideClose: false, remove: false, dlgOpt: {}, callback: function() {}};
        if (!Object.isUndefined(option) && Object.isObject(option)) Object.extend(opt, option);
        var dl = '<div id="{id}_dlg" title="">'+
                    '<p id="{id}_cont" style="margin-top: 15px; border:0px">'+
                        '<span id="{id}_icon" class="ui-icon" style="float:left; margin:0px 7px 50px 0px;"></span>'+
                        '<span id="{id}_text"></span>'+
                    '</p>'+
                 '</div>';
        //Append to body/container if not has yet
        dl = dl.replace(/{id}/g, opt.id);
        var container = $(opt.container + ' #' + opt.id + '_dlg');
        if (0 == container.size()) {
            $(opt.container).append(dl);
        } else if (1 < container.size()) {
            throw 'Invalid';
        }
        //Append text for dialog
        dl = $(opt.container + ' #' + opt.id + '_dlg').attr('title', opt.title);
        if (opt.contCss != '') $(opt.container + ' #' + opt.id + '_cont').addClass(opt.contCss);
        $(opt.container + ' #' + opt.id + '_icon').addClass(opt.icon);
        $(opt.container + ' #' + opt.id + '_text').html(opt.msg);
        //Setting for dialog
        var options = {bgiframe: true,autoOpen: false,closeOnEscape: false,width: 270,height: 'auto', minHeight: 100,resizable: true,modal: true};
        //Remove dialog while close the dialog
        options.close = function() {
            opt.callback();
            if (opt.remove) {
                $(this).dialog('destroy').remove();
            }
        };
        //Focus on the first button
        options.open = function() {
            var btn = $(this).siblings().find('button');
            if (btn.length == 2) {
                $(btn[1]).focus();
            }
        };
        Object.extend(options, {buttons: opt.buttons});
        Object.extend(options, opt.dlgOpt);
        dl.dialog(options);
        //Hide the close button of dialog
        if (opt.hideClose) dl.parents(".ui-dialog").find(".ui-dialog-titlebar-close").hide();
        dl.dialog('open');
    },
    /**
     * Show a message dialog
     * 
     * @param (Object} option (Referece to show function)
     * @returns {void}
     */
    msg: function(option) {
        var opt = {hideClose: false,dlgOpt:{resizable: false},buttons: {Close: function() { $(this).dialog('close');}}};
        if (!Object.isUndefined(option) && Object.isObject(option)) Object.extend(opt, option);
        this.show(opt);
    },
    /**
     * Show a confirm dialog
     * 
     * @param (Object} option (Referece to show function)
     * @returns {void}
     */
    confirm: function(option) {
        var opt = {
            icon: 'ui-icon-info', 
            contCss: 'ui-state-highlight'
        };
        if (!Object.isUndefined(option) && Object.isObject(option)) Object.extend(opt, option);
        this.show(opt);
    },
    /**
     * Show a delete confirm dialog
     * 
     * @param {Function} callback
     * @param (Object} option (Referece to show function)
     * @returns {void}
     */
    del: function(callback, option) {
        var opt = {
            title: 'Delete data', 
            msg: 'Do you want to delete this item.?', 
            id: 'delete', 
            buttons: {
                Cancel: function() {
                    $(this).dialog('close');
                },
                Ok: function() {
                    $(this).dialog('close');
                    callback();
                }
            }
        };
        if (!Object.isUndefined(option) && Object.isObject(option)) Object.extend(opt, option);
        this.confirm(opt);
    },
    /**
     * Show a error dialog
     * 
     * @param (Object} option (Referece to show function)
     * @returns {void}
     */
    error: function(option) {
        option = option || {};
        var opt = {
            title: 'Error occurs',
            icon: 'ui-icon-alert',
            contCss: 'ui-state-error',
            id: 'err',
            buttons: {
                Close: function() {
                    $(this).dialog('close');
                }
            },
            dlgOpt: {resizable: false,maxWidth: 300, height:'auto',maxHeight: 300}
        };
        if (Whl.isErrReq(option.msg)) {
            option.msg = option.msg.substring(4);
        }
        Object.extend(opt, option);
        this.show(opt);
    },
    /**
     * Close the model dialog
     * 
     * @param {String} id
     * @param {Boolean} remove Remove the dialog
     */
    close: function(id, remove) {
        var dlg = $('#' + id + '_dlg').dialog('close');
        if (!Object.isUndefined(remove)) dlg.remove(); 
    },
    /**
     * Close the message dialog
     * 
     * @param {Boolean} remove Remove the dialog
     */
    closeMsg: function(remove) {
        this.close('msg', remove);
    }
});
/**
 * Add more Class, functions for namespace Whl
 */
(function (){
    Object.extend(Whl, {
        /**
         * Open an URL in a new window of web browser
         * @memberOf Whl
         * @param {String} url
         * @returns {void}
         */
        openWin: function(url){
            window.open(url, "");
        },
        /**
         * Validate the control on forms, validate all controls that has the attribute 'valid'
         * @memberOf Whl
         * @param {Array} fields Format [['id'], {'validator1': 'message', 'validator2': 'message}]
         * @param {String} frmId
         * @returns {Boolean}
         */
        validate: function(fields, frmId, errMsgControlId, bValidateAll) {
            var firstControl = null;
            var isValid = true;
            frmId = frmId || 'form';
            if (frmId != 'form') frmId = '#' + frmId;
            for (var i=0, len=fields.length; i < len; i++) {
                var field = fields[i], valid = true;
                var control = $(frmId + ' #' + field[0]), message = null;
                if (!control.isExist() || control.get(0).disabled) continue;
                else {
                    //If Function, execute function: Custom validate function
                    if (Object.isFunction(field[1])) {
                        valid = field[1](); 
                        message = field[2];
                    } else {
                        //Use jquery plugin function
                        for (var validator in field[1]) {
                            //Check required fields
                            var opt = null;
                            if (Object.isArray(field[1][validator])) {
                                //opt[0]: Message, opt[1]: Compare Id
                                opt = field[1][validator];
                                message = opt[0];
                            } else {
                                message = field[1][validator];
                            }
                            if (validator == 'blank') {
                                valid = control.isBlank();
                            } else if (validator == 'email') {
                                valid = control.isEmail();
                            } else if (validator == 'url') {
                                valid = control.isUrl();
                            } else if (validator == 'domain') {
                                valid = control.isDomain();
                            } else if (validator == 'isDate') {
                                valid = Date.toDate(control.val())!=0;
                            } else if (validator == 'gtDate' && opt[1]) {
                                valid = control.gtDate(frmId + ' #' + opt[1]);
                            } else if (validator == 'gteqDate' && opt[1]) {
                                valid = control.gtDate(frmId + ' #' + opt[1], true);
                            } else if (validator == 'ltDate' && opt[1]) {
                                valid = control.ltDate(frmId + ' #' + opt[1]);
                            } else if (validator == 'lteqDate' && opt[1]) {
                                valid = control.ltDate(frmId + ' #' + opt[1], true);
                            } else if (validator == 'ltDate2' && opt[1]) {
                                valid = control.ltDate2(opt[1]);
                            } else if (validator == 'eqString' && opt[1]) {
                                valid = control.eqString(frmId + ' #' + opt[1]);
                            } else if (validator == 'gtNumber' && opt[1]) {
                                valid = control.gtNumber(frmId + ' #' + opt[1]);
                            }
                            if (!valid) {
                                if (firstControl == null) {
                                    firstControl = control;
                                }
                                break;
                            }
                        }
                    }
                    //Add error message
                    var errMsgControl = errMsgControlId ? $('#' + errMsgControlId) : $(frmId + ' #' + control.attr('id') + 'Invalid');
                    if (!valid) {
                        errMsgControl.html(message);
                        errMsgControl.removeClass('error-hide').addClass('error');
                        if (firstControl == null) {
                            firstControl = control;
                        }
                        isValid = valid;
												if (bValidateAll!=null && !bValidateAll){
													firstControl.focus();
													return(false);
												}
                    } else {
                        errMsgControl.html('');
                        errMsgControl.removeClass('error').addClass('error-hide');
                    }
                }
            }
            //Focus to the first invalid control
            if (!isValid && firstControl != null) {
                firstControl.focus();
                return false;
            }
            return true;
        },
        /**
         * Get Token from current form
         * 
         * @returns {String}
         */
        getToken: function() {
            return ('token=' + $('#token').val());
        },
        /**
         * Is response has error
         * 
         * @param {String} response
         * @returns {Boolean} 
         */
        isErrReq: function(response) {
            return (response.indexOf('WRN:') != -1);
        },
        /**
         * Check the data is a valid json format
         * 
         * @param {String} data 
         * @retuns {Boolean}
         */
        isJson: function(data) {
            return (data.search(/^(\{(.*)\}|\[(.*)\])$/) != -1 );
        },
        /**
         * Get Cookie with the name applied
         * 
         * @param {String} name
         * @returns {String|null}
         */
        getCookie: function(name) {
            if (document.cookie.length > 0) {
                var start = document.cookie.indexOf(name + "=");
                if (start != -1) {
                    start = start + name.length + 1;
                    var end = document.cookie.indexOf(";", start);
                    if (end == -1) end = document.cookie.length;
                    return unescape(document.cookie.substring(start, end));
                }
            }
            return null;
        },
        /**
		 * Set cookie with value
		 * 
		 * @param {String} name
		 * @Param {String} value
		 * @param {Number} days
		 * @returns {Object} Whl Object
		 */
		setCookie: function(name, value, days) {
		    days = (typeof days == 'undefined') ? 7 : days;
		    var date = new Date();
		    date.setTime(date.getTime()+(days*24*60*60*1000));
		    var expires = "; expires="+date.toGMTString();
		    document.cookie = name+"="+value+expires+"; path=/";
		    return this;
		},
		/**
		 * Delete the cookie
		 * 
		 * @param {String} name
		 * @returns {Object} Whl object
		 */
		deleteCookie: function(name) {
		    this.setCookie(name, '', -1);
		    return this;
		},
        /**
         * Random a number from 0 to number
         * 
         * @param {Number} number
         * @returns {Number}
         */
        rand: function(number) {
            return Math.round(Math.random() * number);
        },
        /**
         * View the details of content
         * 
         * @returns {void}
         */
        getMore: function() {
            $('.cont-more').each(function() {
                var id = this.id.split('-');
                $(this).click(function(event) {
                    event.preventDefault();
                    $('#' + id[1] + '-detail').removeClass('ui-hide').show();
                    $(this).remove();
                });
            });
        },
        /**
         * Popup function
         * 
         * @returns {void}
         */
        popUp: function( uri, width, height ) {
            var popWin = null;
            width  = (width) ? width : 680;
            height = (height) ? height : 420;
            var x = (screen.width - width)/2;
            if (popWin == null || popWin.closed) {
                popWin = window.open(uri, "", "width="+width+",height="+height+",scrollbars,resizable,center,left="+x);
            } else { popWin.location.href=uri; }
        }
        //Add more functions, class for Whl .... here
    });
})();

/**
 * Add global functions for UA
 */
Whl.UA = {};
Object.extend(Whl.UA, {
    /**
     * Intialize the global data for controls: currency
     * 
     * @returns {void}
     */
    initCurrency: function() {
        //Register event for currency combo box
        $('#currency').change(function() {
            $('#frm-currency').submit();
        });
        // Add for currency
        var currency = Whl.getCookie('currency');
        if (currency != null && currency != '') {
            $('#currency').val(currency);
        }
    },
    initData: function() {
        this.initCurrency();
        var password_onFocus = function(){
            if (this.value == '- ref. number -') {
                var newO = document.createElement('input');
                //$(newO).attr('type', 'password');
                $(newO).attr('name', $(this).attr('name'));
                $(newO).attr('style', $(this).attr('style'));
                $(newO).attr('class', $(this).attr('class'));
                $(this).replaceWith($(newO));
                $(newO).blur(password_onBlur).focus();
            }
        };
        var password_onBlur = function(){
            if (this.value == '') {
                var newO = document.createElement('input');
                $(newO).attr('type', 'text');
                $(newO).attr('value', '- ref. number -');
                $(newO).attr('name', $(this).attr('name'));
                $(newO).attr('style', $(this).attr('style'));
                $(newO).attr('class', $(this).attr('class'));
                $(this).replaceWith($(newO));
                $(newO).focus(password_onFocus);
            }
        };
        // Traveller login (User)
        $('#username').focus(function() {
            if (this.value == '- email -') this.value = '';
        }).blur(function() {
            if (this.value == '') this.value = '- email -';
        });
        $('#refno').focus(password_onFocus);
        $('#user-signin').click(function(event){
            event.preventDefault();
            $(this).closest('form').submit();
        });
        //Newsletter email
        $('#email-news').focus(function() {
            if (this.value == '- your email -') this.value = '';
        }).blur(function() {
            if (this.value == '') this.value = '- your email -';
        });
        
        // Agent Login
        $('#agent-name').focus(function() {
            if (this.value == '- email address -') this.value = '';
        }).blur(function() {
            if (this.value == '') this.value = '- email address -';
        });
        
        $('#agent-pass').click(function(){
                textFieldToPassField();
        });
        
        function textFieldToPassField(){
            if($("#agent-pass").val()=="- password -"){

                $('#agent-pass').replaceWith('<input type="password" class="tb_ac02" value="" name="uxPassword" id="agent-pass" >');
                $('#agent-pass').blur(function(){
                    passFieldToTextField();
                })
                $('#agent-pass').focus();
            }
        }
 
        function passFieldToTextField(){
            if($("#agent-pass").val()==""){                
                  $('#agent-pass').replaceWith('<input type="text" class="tb_ac02" value="- password -" name="uxPassword" id="agent-pass" >');
                  $('#agent-pass').click(function(){
                        textFieldToPassField();
                  }) 
                    
            }
        }       
        
        // Subcribe newsletter
        $('#subscribe-news').click(function(event) {
            event.preventDefault();
            if (!$('#email-news').val().isEmail()) {
                Whl.Dialog.msg({title: Message.dlg.title2, msg: Message.errEmail, remove: true, callback: function() { $('#email-news').focus();}});
            } else {
                $('#frm-subscribe').submit();
            }
        });
        // Agent signin
        $('#agent-signin').click(function(event) {
            event.preventDefault();
            if ($('#agent-name').val() != '- email address -' && $('#agent-pass').val() != '- password -') {
                $('#agent-signin-form').submit();
            }
        });		
		//send this page
		$('#send-this-page').removeClass('ui-hide').dialog({
			bgiframe: true,
			autoOpen: false,
			width: 620,
			height: 480,
			modal: true,
			open: function() {
				$(this).parents('.ui-dialog-buttonpane button:eq(1)').css('display', '');
			},
			buttons: {				
				"Close": function() { $(this).dialog('close'); },
				"Send": function() { $('#frmSendPage').submit(); }
			}
		});
		$('#btnSendThisPage').click(function() {
			var uri = window.location.pathname;
			var title = document.title;
			$("#send-this-page").load('/sendpage?uri='+uri+'&title='+title, {}, function(response, status, xhr) { 
				$(this).html(response).dialog('open'); 
				$('#sec_num').forceNumber();
				var validateFields = function() {
					var fields = [];
					fields.push(['s_fullname', {blank:Message.errRequired}]);
					fields.push(['s_email', {blank:Message.errRequired, email:Message.errInvalid}]);
					fields.push(['r_fullname', {blank:Message.errRequired}]);
					fields.push(['r_email', {blank:Message.errRequired, email:Message.errInvalid}]);
					fields.push(['sec_num', {blank:Message.errRequired}]);
					fields.push(['message', {blank:Message.errRequired}]);
					return Whl.validate(fields);
				};
				if ($.browser.msie) {
					$('#s_fullname,#s_email,#r_fullname,#r_email,#sec_num').keypress(function(e){
						if (e.keyCode == 13) {
							$('#frmSendPage').submit();
						}
					});
				}
				$('#frmSendPage').submit(function(event) {
					event.preventDefault();
					if (validateFields()) {
						Whl.Dialog.msg({id:'send', title:'Sending', msg: '... Please wait for a while ...', hideClose:false, remove:true});
						var option = {
							type: 'POST',
							url: '/sendpage?act=send',
							dataType: 'json',
							data: $("#frmSendPage").serialize(),
							success: function(response) {
								if (response.data == 'invalid') {
									$('#sec_numInvalid').html(response.msg);									
								} else {
								    //$('#send_text').html(response.msg);
									$('#send-this-page').html(response.msg);
									$(this).parents('.ui-dialog-buttonpane button:eq(1)').css('display', 'none');
								    setTimeout(function() { 
										$('#send-this-page').dialog('close'); 
									}, 5000);									
								}
								setTimeout(function() { Whl.Dialog.close('send'); }, 500);
							},
							error: function(XMLHttpRequest, textStatus, errorThrown) {
								Whl.Dialog.error({title:'Error', msg:textStatus, hideClose:false, remove:true});
							}
						};
						$.ajax(option);
					}
				});
			});
		});
    }
});

/**
 * A class for manipulating the allotment in calendar
 * 
 * @class Alm
 * @param {String} url
 * @param {Object} almData
 * @param {Number} childPolicy
 */
Whl.UA.Alm = function(url, almData, childPolicy) 
{
    this._url = url,
    this._childPolicy = childPolicy;
    this._almData = almData;
    
    this._departures = Whl.Combo.get('departure-time').getObjValue();
    this._currency = $($('.price-currency').get(0)).html();
    this._promoCode = '';
    this._disabled = true;
    this.initialize();
};
Whl.UA.Alm.prototype = {
    /**
     * Initialize the calendar control
     * 
     * @returns {void}
     */
    initialize: function() {
        //Initialize the calendar control
        var option = {dateFormat: 'MM d, yy', minDate: new Date()};
        option.beforeShowDay = this._beforeShowday.bind(this);
        option.onSelect = this._selectDate.bind(this);
        option.onChangeMonthYear = this._changeMonth.bind(this);
        this.calendar = $('#datepicker');
        this.calendar.datepicker(option);
        this._selectedDate = null;
        //View first available
        this._viewFirstAvail();
        //Submit booking event
        $('#submitBooking').click(this.submitBooking.bindEvent(this));
        //departure time change
         $("#departure-time").change(function(){
            $("input[name=departure-time]").val($(this).val());
        });
        // Seting dialog
        $('#tester-so').removeClass('ui-hide').dialog({
            bgiframe: true,
            autoOpen: false,
            width: 500,
            height:'auto',
            maxHeight: 350,
            modal:true,
            resizable:false,
            open: function() {
                $('#tester-so').parents('.ui-dialog-buttonpane button:eq(1)').focus();
                $('#promo-code').val(this._promoCode);
            }.bind(this),
            close: function() {
                $('#promo-code').val('');
            },
            buttons: {
                Cancel: function() {
                    $(this).dialog('close');
                },
                Ok: this._checkPromoCode.bind(this)
            }
        });
        $('#pcode-add').click(function(event) {
            event.preventDefault();
            $('#pcode-msg').html('');
            $('#tester-so').dialog('open');
        });
        $('#pcode-remove').click(this._removePromoCode.bindEvent(this));				
    },
    /**
     * Set allotment data for the control
     * 
     * @param {Object} almData
     * @returns {void}
     */
    setData: function(almData) {
        this._almData = almData;
    },
    /**
     * Submit booking
     * 
     * @returns {void}
     */
    submitBooking: function() {
        arguments[0].preventDefault();
        if (this._validate()) {
            Whl.Dialog.msg({id:'book', title: Message.dlg.title3, msg: Message.alm.check, buttons: {}});
            var param = 'departure_date=' + this._selectedDate.getDate() + '/' + (this._selectedDate.getMonth() + 1) + '/' + this._selectedDate.getFullYear();
            if (this._promoCode != '') {
                param += '&promo_code=' + this._promoCode;
            }
            var option = {
                type: 'POST',
                url: this._url + '?act=checkAlm',
                dataType: 'text',
                data: param,
                success:  function(almData) {
                    if (Whl.isJson(almData)) {
                        almData = eval('(' + almData + ')');
                        Whl.Dialog.close('book');
                        if (almData.result) {
                            var frm = document.frmBooking;
                            frm.departure_date.value = this._selectedDate.getDate() + '/' + (this._selectedDate.getMonth() + 1) + '/' + this._selectedDate.getFullYear();
                            frm.submit();
                        } else {
                            var err = Message[almData.promoCode[0]];
                            if (almData.promoCode[0] == 603 || almData.promoCode[0] == 605) {
                                err = err.replace(/\{0\}/g, almData.promoCode[1][0]);
                                err = err.replace(/\{1\}/g, almData.promoCode[1][1]);
                            }
                            Whl.Dialog.error({title: Message.dlg.title4, msg: err, remove: true});
                        }
                    }
                }.bind(this),
                error: function(XMLHttpRequest, textStatus, errorThrown) {
                    Whl.Dialog.error({msg: textStatus});
                }
            };
            $.ajax(option);
        }
    },
    /**
     * Change the format for the dates that have the allotment, and the dates that donot have allotment
     * 
     * @param {Date} date
     * @returns {Array}
     */
    _beforeShowday: function(date) {
        var monthYear = (date.getMonth() + 1) + '-' + date.getFullYear();
        var almMonth = null, alm = null, isAvailable = false;
        // Check allotment in the month of year
        if (!Object.isUndefined(this._almData[monthYear])) {
            almMonth = this._almData[monthYear];
            for (var key in almMonth) {
                var alm = almMonth[key];
                var almDate =  new Date(alm.date);
                if (date.getTime() == almDate.getTime()) {
                    isAvailable = true;
                    break;
                }
            }
        }
        if (isAvailable) {
            return [true, 'alm_' + date.getDate() + '-' + monthYear + '_' + key];
        } else {
            return [false, 'ui-state-disabled1'];
        }
    },
    /**
     * Function will be called after the date is selected.
     * 
     * @param {String} dateText A string date
     * @param {Object} inst     An isntance of datepicker jq
     * @returns {void}
     */
    _selectDate: function(dateText, inst) {
        var date = new Date(dateText);
        var dateCss = null;
        $("td[class*='alm_" + date.getDate() + '-' + (date.getMonth() + 1) + '-' + date.getFullYear() +"']").each(function() {
            var classNames = this.className.split(' ');
            if (0 < classNames.length) {
                for (var i=0; i< classNames.length; i++) {
                    if (classNames[i].indexOf('alm') != -1) {
                        dateCss = classNames[i];
                        break;
                    }
                }
            } else {
                dateCss = this.className;
            }
        });
        if (dateCss != null) {
            dateCss = dateCss.split('_');
            date = dateCss[1].split('-');
            this._viewPrice(this._almData[date[1] + '-' + date[2]][parseInt(dateCss[2])]);
        }
    },
    /**
     * Get new allotments for the next/pre months if month is unavailable in this object
     * 
     * @param {Number} year     The year that change to
     * @param {Number} month    The month that change to
     * @param {Object }inst     Instance of JQ datepicker
     * @returns {void}
     */
    _changeMonth: function(year, month, inst) {
        if (Object.isUndefined(this._almData[month + '-' + year])) {
            this._update('01/' + month + '/' + year);
        }
    },
    /**
     * View Price 
     * 
     * @param {Object} alm
     * @returns {void}
     */
    _viewPrice: function(alm) {
        //Departure
        var departure = Whl.Combo.get('departure-time');
        departure.removeAll();
        for (var key in this._departures) {
            for (var depId in alm.depart) {
                if (alm.depart[depId].id == this._departures[key].value) {
                    departure.addItem(this._departures[key]);
                    break;
                }
            }
        }
        if(departure.options.length == 1){
            $('select.depart').hide();
            $('span.depart').show().html(departure.options[0].text);
        }else{
            $('select.depart').show();
            $('span.depart').hide();
        }
        $('input.depart').val($('select.depart').val());
        
        // Adults & children number
        var noAdult = $('#no-adults');
        var noChild = $('#no-children');
        noAdult.unbind().change(this._viewPrice.bind(this, alm));
        noChild.unbind().change(this._viewPrice.bind(this, alm));
        
        //Price
        var adultPrice = parseFloat(alm.adultPrice.replace(/[^0-9\.]/g,''))*parseInt(noAdult.val());
        var childPrice = parseFloat(alm.childPrice.replace(/[^0-9\.]/g,''))*parseInt(noChild.val());
        $('#price-adults').html(adultPrice.toFixed(2));
        if (this._childPolicy == 1) {
            if(noChild.val() == 0){
                $('span.price-children').hide();
            }else{
                $('#price-children').html(childPrice.toFixed(2));
                $('span.price-children').show();
            }
        }
        // Add current selected date
        this._selectedDate = new Date(alm.date);
    },
    _viewFirstAvail: function() {
        // View First Available
        var firstAlm = null;
        for (var key in this._almData) {
            firstAlm = this._almData[key];
            break;
        }
        if (firstAlm === null) this._disable();
        else {
            if (this._disabled) this._enable();
            firstAlm = firstAlm[0];
            if (!this._selectedDate) {
                var date = firstAlm.date;
                this.calendar.datepicker('setDate', new Date(date));
                
                date = date.split(',');
                date = date[0].split(' ');
                
                $('#first-avail-date').html(date[1] + ' ' + date[0]);
                
                this._selectedDate = new Date(firstAlm.date);
            }
            //View price
            this._viewPrice(firstAlm);
        }
    },
    /**
     * Disable the alloment controls
     * 
     * @returns {void}
     */
    _disable: function() {
        $('#first-avail').html(Message.alm.unAvail);
        $('#first-avail-date').html('');
        Whl.Combo.get('departure-time').removeAll().disabled = true;
        $('#no-adults').attr('disabled', 'disabled');
        $('#no-children').attr('disabled', 'disabled');
        $('.price-currency').html('');
        $('#price-adults').html('');
        $('#price-children').html('');
        this._disabled = true;
        this._selectedDate = null;
    },
    /**
     * Enable the allotment controls
     * 
     * @returns {void}
     */
    _enable: function() {
        $('#first-avail').html(Message.alm.firstAvail);
        $('#first-avail-date').html('');
        $('#departure-time').attr('disabled', '');
        $('#no-adults').attr('disabled', '');
        if (this._childPolicy > 0) {
            $('#no-children').attr('disabled', '');
        }
        $('.price-currency').html(this._currency);
        this._disabled = false;
    },
    /**
     * Validate data
     * 
     * @returns {Boolean}
     */
    _validate: function() {
     // Validate Date
        var validDate = false, almNum = 0;
        if (this._selectedDate !== null) {
            var month = this._selectedDate.getMonth() + 1;
            month += '-' + this._selectedDate.getFullYear();
            if (!Object.isUndefined(this._almData[month])) {
                for (var key in this._almData[month]) {
                    var date = new Date(this._almData[month][key].date);
                    if (date.getTime() == this._selectedDate.getTime()) {
                        validDate = true;
                        almNum = this._almData[month][key];
                        break;
                    }
                }
            }
        }
        if (!validDate) {
            Whl.Dialog.msg({id:'validate',title:Message.dlg.title2,msg: Message.alm.departureDate});
            return false;
        }
        if (!$('#departure-time').val()) {
            Whl.Dialog.msg({id:'validate',title:Message.dlg.title2,msg: Message.alm.departureTime});
            return false;
        }
        for (var idx in almNum.depart) {
            var alm = almNum.depart[idx];
            if (almNum.depart[idx].id == $('#departure-time').val()) {
                almNum = almNum.depart[idx].alm;
                break;
            }
        }
        almNum = parseInt(almNum);
        // Validate the remaining allotment
        if (almNum == 0) {
            Whl.Dialog.msg({id:'validate',title:Message.dlg.title2,msg: Message[209], remove: true});
            return false;
        } else if (almNum > -1) {
            var numAdult = parseInt($('#no-adults').val());
            var numChild = this._childPolicy === 1 ? parseInt($('#no-children').val()) : 0;
            if (numAdult + numChild > almNum) {
                Whl.Dialog.msg({id:'validate',title:Message.dlg.title2,msg: Message.alm.dateAlt + almNum});
                return false;
            }
        }
        return true;
    },
    /**
     * Check promo code and update allotment with this promo code
     * 
     * @returns {void}
     */
    _checkPromoCode: function() {
        var pCode = $('#promo-code');
        if (pCode.val() == '') {
            $('#pcode-msg').html(Message.alm.inValidCode);
            pCode.focus();
        } else {
            $('#pcode-msg').html('');
            var date =  this._selectedDate != null ? this._selectedDate : new Date();
            date = '01/' + (date.getMonth() + 1) + '/' + date.getFullYear();
            this.setData({});
            this._promoCode = pCode.val();
            this._update(date);
        }
    },
    /**
     * Remove promo code and update the allotment
     * @return
     */
    _removePromoCode: function() {
        arguments[0].preventDefault();
        $('#pcode-text').html('').hide();
        $('#pcode-remove').hide();
        $('#pcode-add').show();
        this._promoCode = '';
        $('#promo-code').val('');
        $('#promo_code').val('');
        //Update allomtent
        var date = this._selectedDate != null ? this._selectedDate : new Date();
        date = '01/' + (date.getMonth() + 1) + '/' + date.getFullYear();
        this.setData({});
        this._update(date);
    },
    /**
     * Update allotment data
     * 
     * @param {String} date String date
     * @returns {void}
     */
    _update: function(date, departDate) {
        var param = 'date=' + date;
        departDate = departDate || null;
        if (this._promoCode != '') {
            param += '&promo_code=' + this._promoCode;
        }
        if (departDate != null) {
            param += '&departure_date=' + departDate.getDate() + '/' + (departDate.getMonth() + 1) + '/' + departDate.getFullYear();
        }
        var option = {
            type: 'POST',
            url: this._url,
            dataType: 'text',
            data: param,
            success:  function(almData) {
                if (Whl.isJson(almData)) {
                    almData = eval('(' + almData + ')');
                    if (almData.result) {
                        Object.extend(this._almData, almData.allotment);
                        DP_jQuery.datepicker._adjustDate('#datepicker');
                        if (this._promoCode != '') {
                            if (!almData.promoCode[0]) {
                                $('#tester-so').dialog('close');
                                $('#promo_code').val(this._promoCode);
                                $('#pcode-text').html(this._promoCode).removeClass('ui-hide').show();
                                $('#pcode-remove').removeClass('ui-hide').show();
                                $('#pcode-add').hide();
                            } else {
                                if (!$('#tester-so').dialog('isOpen')) {
                                    $('#tester-so').dialog('open');
                                }
                                var err = Message[almData.promoCode[0]];
                                if (almData.promoCode[0] == 603 || almData.promoCode[0] == 605) {
                                    err = err.replace(/\{0\}/g, almData.promoCode[1][0]);
                                    err = err.replace(/\{1\}/g, almData.promoCode[1][1]);
                                }
                                $('#pcode-msg').html(err);
                                $('#promo-code').val(this._promoCode).focus();
                                this._promoCode = '';
                            }
                        }
                        //
                        if (!this._hasData()) {
                            this._disable();
                        } else {
                            this._viewFirstAvail();
                        }
                    }
                } else {
                    Whl.Dialog.error({msg: almData});
                }
            }.bind(this),
            error: function(XMLHttpRequest, textStatus, errorThrown) {
                Whl.Dialog.error({msg: textStatus});
            }
        };
        $.ajax(option);
    },
    _hasData: function() {
        var has = false;
        for (var key in this._almData) {
            has = true;
            break;
        }
        return has;
    }
};

Whl.UA.ImageRotator = function(path, imgData, imgId)
{
    this._path = path;
    this._imgData = imgData;
    this._elmImg = $('#' + imgId);
    this.initialize();
};
Whl.UA.ImageRotator.prototype = {
    initialize: function() {
        if (this._elmImg.length == 0 || this._imgData.length == 1) return;
        var images = this._preloadImages();
        if (images.length > 0) {
            setInterval(this.run.bind(this, images), 5000);
        }
    },
    run: function(images) {
        var randImg = Whl.rand(this._imgData.length - 1);
        this._elmImg.attr('src', images[randImg].src);
    },
    _preloadImages: function() {
        var images = [];
        var rotator = this;
        $.each(this._imgData, function(i) {
            images[i] = new Image();
            if (Object.isString(this)) {
                images[i].src = this;
            } else if (Object.isObject(this)) {
                images[i].src = (rotator._path + this.src) || '';
                images[i].alt = this.alttext || '';
            }
        });
        return images;
    }
};
