





                
(function(){
    var yuiLang = YAHOO.lang;

    /**
    * Default copy which is used by default instance of Copy object
    * @private
    * @static
    * @final
    */
    var DEFAULT_COPY = {
        /**
        * contains locale
        * @property LOCALE
        * @static
        * @final
        */
        LOCALE:"de_DE",
        /**
        * contains internationalized date format string
        * @property DATE_FORMAT
        * @static
        * @final
        */
        DATE_FORMAT:"dd.MM.yyyy",
        /**
        * short month names are use in instructional copy
        * @property MONTHS_SHORT
        * @static
        * @final
        */
        MONTHS_SHORT:["JAN","FEB","MÄR","APR","MAI","JUN","JUL","AUG","SEP","OKT","NOV","DEZ"],
        /**
        * long month names are used in calendar headings
        * @property MONTHS_LONG
        * @static
        * @final
        */
        MONTHS_LONG:["Januar","Februar","März","April","Mai","Juni","Juli","August","September","Oktober","November","Dezember"],
        /**
        * short weekday names are used in calendar headings
        * @property WEEKDAYS_SHORT
        * @static
        * @final
        */
        WEEKDAYS_SHORT:["So","Mo","Di","Mi","Do","Fr","Sa"], // 2 char
        /**
        * medium length weekday names are used in instructional copy
        * @property WEEKDAYS_MEDIUM
        * @static
        * @final
        */
        WEEKDAYS_MEDIUM:["So.","Mo.","Di.","Mi.","Do.","Fr.","Sa."], // 3 char
        /**
        * Step 1 instructions
        * @property RANGE_START_INSTRUCTIONS
        * @static
        * @final
        */
        RANGE_START_INSTRUCTIONS:"Bitte wählen Sie ein Check-in-Datum",
        
        /**
        * Step 2 instructions
        * @property RANGE_END_INSTRUCTIONS
        * @static
        * @final
        */
        RANGE_END_INSTRUCTIONS:"Bitte wählen Sie ein Check-out-Datum",
        
        /**
        * Step 3 instructions
        * @property SUBMIT_INSTRUCTIONS
        * @static
        * @final
        */
        SUBMIT_INSTRUCTIONS:"Daten absenden",
        /**
        * Step 1 prefix when RangeStart is selected
        * @property RANGE_START_PREFIX
        * @static
        * @final
        */
        RANGE_START_PREFIX:"Check-in",
        /**
        * Step 2 prefix when RangeEnd is selected
        * @property RANGE_END_PREFIX
        * @static
        * @final
        */
        RANGE_END_PREFIX:"Check-out",
        /**
        * used by JA/ZH in step 1 & 2
        * @property DAY
        * @static
        * @final
        */
        DAY:"So",
        /**
        * used by JA/Zh in step 1 & 2
        * @property YEAR
        * @static
        * @final
        */
        YEAR:"Jahr",
        /**
        * RangeStart tool tip copy
        * @property RANGE_START_TOOLTIP
        * @static
        * @final
        */
        RANGE_START_TOOLTIP:"Für diesen Check-in klicken",
        /**
        * RangeEnd tool tip copy
        * @property RANGE_END_TOOLTIP
        * @static
        * @final
        */
        RANGE_END_TOOLTIP:"Für diesen Check-out klicken",
        /**
        * Legend key for range start copy
        * @property RANGE_START_AVAILABLE
        * @static
        * @final
        */
        RANGE_START_AVAILABLE:"Verfügbares Check-in-Datum",
        /**
        * Legend key for range end copy
        * @property RANGE_END_AVAILABLE
        * @static
        * @final
        */
        RANGE_END_AVAILABLE:"Verfügbares Check-out-Datum",
        /**
        * Close copy
        * @property CLOSE
        * @static
        * @final
        */
        CLOSE:"Schließen",
        /**
        * Reset copy
        * @property RESET
        * @static
        * @final
        */
        RESET:"Kalender zurücksetzen",
        /**
        * Reset copy for RangeStart
        * @property RANGE_START_RESET
        * @static
        * @final
        */
        RANGE_START_RESET:"Zurücksetzen",
        /**
        * Reset copy for RangeEnd
        * @property RANGE_END_RESET
        * @static
        * @final
        */
        RANGE_END_RESET:"Zurücksetzen",
        /**
        * Night stays copy for LOS
        * @property NIGHT_STAYS
        * @static
        * @final
        */
        NIGHT_STAYS:" <b>Aufenthalt</b>",
        /**
        * Daily Rate copy
        * @property DAILY_RATE
        * @static
        * @final
        */
        DAILY_RATE:"Preis pro Tag",
        /**
        * Starpoint label
        * @property STARPOINTS
        * @static
        * @final
        */
        STARPOINTS:"Starpoints",
        /**
        * Submit button copy
        * @property SUBMIT
        * @static
        * @final
        */
        SUBMIT:"Weiter",
        /**
        * Used for title attribute on preious link
        * @property PREVIOUS
        * @static
        * @final
        */
        PREVIOUS:"Zurück",
        /**
        * Used for title attribute on preious next
        * @property NEXT
        * @static
        * @final
        */
        NEXT:"Weiter",
        /**
        * Used for availability loading message
        * @property LOADING
        * @static
        * @final
        */
        LOADING:"Ladevorgang…",
        /**
        * Error messages
        * @static
        * @final
        */
        ERROR_MAX_LOS:"Falls der Aufenthalt mehr als 31 Übernachtungen umfasst, rufen Sie bitte +(1) 866 539 3446 an.",
        ERROR_TOO_FAR:"Sie können nur bis zu 550 Tage im Voraus buchen.",
        ERROR_START_BEFORE_TODAY:"Das Anreisedatum kann nicht vor dem heutigen Tag liegen.",
        ERROR_END_BEFORE_START:"Das Abreisedatum darf nicht vor dem Anreisedatum liegen.",
        ERROR_SAME_DATES:"Anreise- und Abreisedatum dürfen nicht auf denselben Tag fallen.",
        ERROR_NONE_SELECTED:"Bitte wählen Sie ein Check-in- und Check-out-Datum.",
        ERROR_SELECT_END:"Bitte wählen Sie ein Check-out-Datum.",
        ERROR_GENERAL:"Ein Fehler ist aufgetreten. Starten Sie Ihre Suche bitte erneut.",
        /**
        * currently unused, but reserving namespace for future use
        * @property WEEKDAYS_1CHAR
        * @static
        * @final
        */
        WEEKDAYS_1CHAR:[], // 1 char
        /**
        * currently unused, but reserving namespace for future use
        * @property WEEKDAYS_LONG
        * @static
        * @final
        */
        WEEKDAYS_LONG:[]  // full
    };
    /**
    * instances of Copy are used by Calendar, CalendarGroup (and their subclasses)
    * @contstuctor
    * @namespace SW.widget.Calendar
    * @class Copy
    * @param {Copy | null} inheritFrom
    * @param {Object} copy object literal containing strings or arrays of strings to be used by calendars.
    */
    function Copy(inheritFrom,copy){
        /**
        * stores copy specific to this instance
        * @private
        */
        this._copy = {};

        if(inheritFrom){
            this._inheritedCopy = inheritFrom;
        }
        this._flattenCopy(copy);
    }

    /**
    * Default instance of Copy object
    * @private
    */
    var DEFAULT_INSTANCE = null;
    /**
    * provides global access to default instance of Copy ojbect
    * @method getDefaultInstance
    * @return {Copy}
    */
    Copy.getDefaultInstance = function(){
        DEFAULT_INSTANCE = new Copy(null,DEFAULT_COPY);
        Copy.getDefaultInstance = function (){
            return DEFAULT_INSTANCE;
        }
        return DEFAULT_INSTANCE;
    };

    Copy.prototype = {
        /**
        * contains reference to another copy instance, used recursively to look up properties
        * @property _inheritedCopy
        * @private
        */
        _inheritedCopy:null,
        /**
        * used to retrieve copy from local instance (uses inherited values, if not on instance)
        * @method _flattenConfig
        * @private
        * @param {Object} copy
        */
        _flattenCopy:function(copy){
            for(var prop in copy){
                if(yuiLang.hasOwnProperty(copy,prop)){
                    this.set(prop,copy[prop]);
                }
            }
        },
        /**
        * used to retrieve copy from local instance (uses inherited values, if not on instance)
        * @method get
        * @param {String} key string representation of the key. For arrays, use dot notation (ie "MONTHS_SHORT.1")
        * @return {String}
        */
        get:function(key){
            var val,obj = this,max=5;
            do{
                max--; // avoid recursion errors (might be able to perform check in constructor to avoid this check here)
                val = obj._copy[key];
                obj = obj._inheritedCopy;
            }while(!val && obj && max > 0);
            return val || "";
        },
        /**
        * set copy to local instance of Copy
        * @method set
        * @param {String} key string representation of the key. To set specific node in array, use dot notation (ie "MONTHS_SHORT.1")
        * @param {String | Array} string or array of strings to be set to key
        */
        set:function(key,val){
            var i;
            if(yuiLang.isArray(val)){
                for(i = 0; i < val.length; i++){
                    this._copy[key +"."+ i] = val[i];
                }
            }else{
                this._copy[key] = val;
            }
        }
    };

    // CopyCommon is intended for use with Calendar and CalendarGroup objects
    /**
    * Common methods which are attached to Calendar and CalendarGroup to access local instances of Copy objects
    * @namespace SW.widget.Calendar
    * @class CopyCommon
    */
    function CopyCommon(){

    }
    CopyCommon.prototype = {
        /**
        * creates local instance of Copy object to be associated with current instance of Calendar or CalendarGroup.
        * Note: when using CalendarGroup, or any of it's subclasses, each individual Calendar object receives its own isntance of copy inheritance from the Group's copy instance
        * @method initCopy
        * @param {Copy} Optional. If not supplied the default instance of copy will be used for inheritance
        */
        initCopy:function(inheritFrom){
            inheritFrom = inheritFrom || Copy.getDefaultInstance();
            this.copy = new Copy(inheritFrom);
        },
        /**
        * convenience method to access get method on local copy instance
        * @method setCopy
        * @param key {String}
        * @param val {String | Array}
        */
        setCopy:function(key,val){
            this.copy.set(key,val);
        },
        /**
        * convenience method to access get method on local copy instance
        * @method getCopy
        * @param key {String}
        * @return {String}
        */
        getCopy:function(key){
            return this.copy.get(key);
        }
    };
    // namespace
    YAHOO.namespace("SW.widget.Calendar");
    SW.widget.Calendar.Copy = Copy;
    SW.widget.Calendar.CopyCommon = CopyCommon
})();
(function() {
    var yuiLang = YAHOO.lang;
    var DAYS_IN_MONTH = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];

    // make singleton, where CalendarUtils is the global point of access
    // using this structure so that we can use augmentProto to gain copy methods
    function Utils(){
        this.initCopy(); // Utils only needs DATE_FORMAT and LOCALE, just need to inherit base copy for that 
    }
    Utils.prototype = {
        getDaysInMonth:function(date){
            var year,month = date.getMonth();
            if(month == 1){
                year = date.getFullYear();
                if( ( year % 4 == 0 && year % 100 != 0 ) || year % 400 == 0 ) {
                    return 29;
                }else{
                    return 28;
                }
            }else{
                return DAYS_IN_MONTH[month];
            }
        },
        getToday:function(){
            return this.copyDate(new Date());
        },
        copyDate:function(date){
            return new Date(date.getFullYear(),date.getMonth(),date.getDate());
        },
        dateFromSystemString:function(sysDate){
            var parts = sysDate.split("-");
            return new Date(parts[0],parts[1]-1,parts[2]);
        },
        /**
         * used to parse string and return a Date object using globalaztion/internationalization rules.
         * @param {String} dateString string representation of a date. If empty, or does not conform to i18n rules, returns null.
         * @return {Date | null}
         */
        dateFromString:function(dateString){
            var dateValues = [];
            var tempValues;
            var dateFormat = this.getCopy("DATE_FORMAT").toLowerCase();

            var returnDate = this.getToday(); // start with a date which has no time (hours, minutes, etc are zeroed out)
            // todo: determine if delim is too loose - could this return bad dates
            var delim = /[\/\.\u5E74\u6708-]/; // all possible date delimiters for all locales
            var sysDate = /\d{4}-\d{2}-\d{2}/; // system date format, with padded zeros
            if(!dateString || dateString.toLocaleLowerCase() == dateFormat){
                // dateString is empty or has default value
                return null;
            }

            if(sysDate.test(dateString)){
                // this looks like a system date
                return this.dateFromSystemString(dateString);
            }

            tempValues = dateString.split(delim);
            if(tempValues.length != 3) {
                // if there are not 3 parts, forget trying to parse date
                return null;
            }

            switch(dateFormat) {
                case 'mm/dd/yyyy':
                // month-day-year
                    dateValues[0] = tempValues[2];
                    dateValues[1] = tempValues[0];
                    dateValues[2] = tempValues[1];
                    break;
                case 'dd/mm/aaaa':
                case 'jj/mm/aaaa':
                case 'gg/mm/aaaa':
                case 'tt.mm.jjjj':
                case 'dd.mm.yyyy':
                // day-month-year
                    dateValues[0] = tempValues[2];
                    dateValues[1] = tempValues[1];
                    dateValues[2] = tempValues[0];
                    break;
                case 'yyyy/mm/dd':
                case "yy\u5E74mm\u6708dd\u65E5":
                // year-month-day
                    dateValues[0] = tempValues[0];
                    dateValues[1] = tempValues[1];
                    dateValues[2] = tempValues[2];
                    break;
                case 'yyyy/dd/mm':
                // year-day-month
                    dateValues[0] = tempValues[0];
                    dateValues[1] = tempValues[2];
                    dateValues[2] = tempValues[1];
                    break;
            }
            if(dateValues[0].length == 2){
                // try to make four digit year if two digits supplied
                dateValues[0] = "20"+ dateValues[0];
            }
            if(dateValues[0].length == 4){
                // only try to set year if we have four digits, anything else is ignored
                returnDate.setFullYear(dateValues[0]);
            }
            // set month (zero based)
            returnDate.setMonth(dateValues[1]-1);
            // set date
            returnDate.setDate(dateValues[2]);
            return returnDate;
        },
        setLastOfMonth:function(date,createCopy){
            var theDate = createCopy ? this.copyDate(date):date;
            theDate.setDate(this.getDaysInMonth(theDate));
            return theDate;
        },
        setFirstOfMonth:function(date,createCopy){
            var theDate = createCopy ? this.copyDate(date):date;
            theDate.setDate(1);
            return theDate;
        },
        addDays:function(daysToAdd,date,createCopy) {
            var theDate = createCopy ? this.copyDate(date):date;
            var x = new Date(2000,1,1);
            var y = new Date(2000,1,1);
            if(x.setDate(128) > y.valueOf())  {
                theDate.setDate(theDate.getDate()+daysToAdd);
                return theDate;
            }
            // Safari setDate(uint8) workaround
            if(daysToAdd < 0) {
                for(var i= -97; daysToAdd < i; daysToAdd-= i) {
                    theDate.setDate(theDate.getDate()+i);
                }
            }
            else {
                for(var i= 96; daysToAdd > i; daysToAdd-= i) {
                    theDate.setDate(theDate.getDate()+i);
                }
            }
            theDate.setDate(theDate.getDate()+daysToAdd);
            return theDate;
        },
        getLengthOfStay:function(startDate,endDate){
            return Math.abs(Math.round((endDate.getTime() - startDate.getTime()) / (1000 * 60 * 60 * 24)));
        },
        dateToSystemFormat:function(date){
            return date.getFullYear() + "-" + this.padZero(date.getMonth()+1) + "-" + this.padZero(date.getDate());
        },
        dateToLocale:function(date,locale){
            // todo: make this use locale rules
            return this.dateToSystemFormat(date);
        },
        dateToFormat:function(date,format){
            var sDateFormat	= "";
            var sTheMonth	= date.getMonth() + 1;
            sTheMonth		= ((sTheMonth) <= 9) ? "0" + sTheMonth : sTheMonth;
            var sTheDay		= date.getDate();
            sTheDay			= (sTheDay <= 9) ? "0" + sTheDay : sTheDay;
            var nTheYear	= date.getFullYear();

            switch(format.toUpperCase()){
                case "YYYY/MM/DD":
                    sDateFormat = String(nTheYear).substr(2,2) + "\u5E74" + sTheMonth  + "\u6708" + sTheDay  + "\u65E5";
                    break;
                case "DD/MM/YYYY":
                    sDateFormat = sTheDay + "/" + sTheMonth + "/" + nTheYear;
                    break;
                case "DD.MM.YYYY":
                    sDateFormat = sTheDay + "." + sTheMonth + "." + nTheYear;
                    break;
                case "MM/DD/YYYY":
                    sDateFormat = sTheMonth + "/" + sTheDay + "/" + nTheYear;
                    break;
                default:
                    sDateFormat = this.dateToSystemFormat(date);
            }
	        return sDateFormat;
        },
        padZero:function(s){
            if(s >= 0 && s <= 9){
                s = '0' + s;
            }
            return s;
        },
        // todo: move non date related methods out of this file
        getOffsetLeft:function(element) {
            // function to determine the offsetLeft of an element that is passed in
            var eLeftOffset = element.offsetLeft;
            while ((element = element.offsetParent) != null){
                eLeftOffset  += element.offsetLeft;
            }
            return eLeftOffset;
        },
        getOffsetTop:function(element) {
            // function to determine the offsetTop of an element that is passed in
            var eTopOffset = element.offsetTop;
            while ((element = element.offsetParent) != null){
                eTopOffset +=element.offsetTop;
            }
            return eTopOffset;
        }
    }

    // namespace
    YAHOO.namespace("SW.widget.Calendar");
    yuiLang.augmentProto(Utils,SW.widget.Calendar.CopyCommon);
    SW.widget.Calendar.Utils = new Utils();// create single instance
})();

(function() {
    var yuiEvent = YAHOO.util.Event,
        yuiDom = YAHOO.util.Dom,
        yuiLang = YAHOO.lang,
        customEvent = YAHOO.util.CustomEvent,
        CalUtils = SW.widget.Calendar.Utils;
    /**
     * base settings for calendar
     * @private
     * @final
     * @static
     */
    var DEFAULT_SETTINGS = {
        contianer:null,
        startDate:CalUtils.getToday(),
        minDate:null,
        maxDate:null
    };


    /**
     * base calendar, which represents a single month. Although this is a standalone object, it is generally
     * used indirectly by CalendarGroup and it's subclasses (Availability and Stay Calendars)
     * @contstuctor
     * @namespace SW.widget.Calendar
     * @class Calendar
     * @param {Object} settings
     */
    function Calendar(settings) {
        this.initSettings(settings);
        this.setStartDate(this.settings.startDate);
        /**
         * contains the custom render methods specified before each render call is made. (emptied after render is called) 
         * @property _renderStack
         * @private
         */
        this._renderStack = [];
        this.initEvents();
    }
    /**
     * Used as return value for rendering methods which indicates no further rendering methods should be run for the current date
     * @static
     * @final
     */
    Calendar.STOP_RENDERING = "STOP";
    /**
     * tag name used for individual cell
     * @static
     * @final
     */
    Calendar.CELL_TAGNAME = "div";
    /**
     * used with addRenderer, specifies that the renderer applies to a specific date. For example: new Date(2009,9,24)
     * @static
     * @final
     */
    Calendar.RENDER_TYPE_DATE = "date";
    /**
     * used with addRenderer, specifies that the renderer applies to date range. For example: new Date(2009,9,24) - new Date(2009,9,30)
     * @static
     * @final
     */
    Calendar.RENDER_TYPE_RANGE = "range";
    /**
     * used with addRenderer, specifies that the renderer applies to a day of the week. For exmpale: All Thursdays (use contstants listed below)
     * @static
     * @final
     */
    Calendar.RENDER_TYPE_WEEKDAY = "weekday";

    /**
     * used with rendering methods as value when type is Calendar.RENDER_TYPE_WEEKDAY.
     * @static
     * @final
     */
    Calendar.SUNDAY = 0;
    Calendar.MONDAY = 1;
    Calendar.TUESDAY = 2;
    Calendar.WEDNESDAY = 3;
    Calendar.THURSDAY = 4;
    Calendar.FRIDAY = 5;
    Calendar.SATURDAY = 6;

    /**
     * represents the internal selection state of the calendar
     * NOTE: Calendar has no concept of "selected", these are defined here for convenience and used by CalendarGroup.
     * @static
     * @final
     */
    Calendar.STATE_UNSELECTED = 0;
    Calendar.STATE_START_SELECTED = 1;
    Calendar.STATE_END_SELECTED = 2;
    Calendar.STATE_RANGE_SELECTED = 3;

    /**
     * These are the built in rendering methods. Some are referenced directly in the render method, other common methods are
     * defined here for convenience and can be used by CalendarGroup or subclasses
     * @static
     * @final
     */
    Calendar.renderDefault = function(date,cell){
//        yuiDom.addClass(cell,"selectable");
        cell.appendChild(document.createTextNode(date.getDate()));
    }
    Calendar.renderOutOfMonth = function(date,cell){
        yuiDom.addClass(cell,"outOfMonth");
        return Calendar.STOP_RENDERING;
    }
    Calendar.renderOutOfRange = function(date,cell){
        cell.appendChild(document.createTextNode(date.getDate()));
        yuiDom.addClass(cell,"invalid");
        return Calendar.STOP_RENDERING;
    }
    // following are specified here for convenience, they are not used directly by Calendar
    Calendar.renderAvailableRangeStart = function(date,cell){
        yuiDom.addClass(cell,"availableRangeStart");
    }
    Calendar.renderAvailableRangeEnd = function(date,cell){
        yuiDom.addClass(cell,"availableRangeEnd");
    }
    Calendar.renderRangeStart = function(date,cell){
        yuiDom.addClass(cell,"rangeStart");
    }
    Calendar.renderRangeEnd = function(date,cell){
        yuiDom.addClass(cell,"rangeEnd");
    }
    Calendar.renderUnselected = function(date,cell){
        yuiDom.addClass(cell,"unselected");
    }
    Calendar.renderSelected = function(date,cell){
        yuiDom.addClass(cell,"selected");
    }


    Calendar.prototype = {
        /**
         * name of class, can be useful for debugging
         * @property _TYPE
         * @private
         * @static
         * @final
         */
        _TYPE:"Calendar",
        /**
         * ensures that initialize is not run more than once
         * @property _initialized
         * @private
         */
        _initialized:false,

        /**
         * needs to be called after constructor, performs basic setup
         * @method initialize
         * @return {Boolean} true, false if already initialized
         */
        initialize:function(){
            if(this._initialized) { return false; }
            this._initialized = true;
//            this.render();
//            this.initHandlers();
            yuiDom.addClass(this.settings.container,"calendarMonth");
            this.initCopy(this.settings.copy);
            return true;
        },
        /**
         * sets up custom events, called by constructor
         * @method initEvents
         */
        initEvents:function(){
            this.hoverOverEvent = new customEvent("hoverOverEvent",null,false,customEvent.FLAT);
            this.hoverOutEvent = new customEvent("hoverOutEvent",null,false,customEvent.FLAT);
            this.nextEvent = new customEvent("nextEvent",null,false,customEvent.FLAT);
            this.previousEvent = new customEvent("previousEvent",null,false,customEvent.FLAT);
            this.beforeRenderEvent = new customEvent("beforeRenderEvent",null,false,customEvent.FLAT);
            this.renderEvent = new customEvent("renderEvent",null,false,customEvent.FLAT);
            this.beforeSelectEvent = new customEvent("beforeSelectEvent",null,false,customEvent.FLAT);
            this.selectEvent = new customEvent("selectEvent",null,false,customEvent.FLAT);
        },
        /**
         * parses settings object, called by constructor
         * @method initSettings
         */
        initSettings:function(config){
            var prop,
                settings = this.settings = {};
            // get defaults
            for(prop in DEFAULT_SETTINGS){
                if(yuiLang.hasOwnProperty(DEFAULT_SETTINGS,prop)){
                    settings[prop] = DEFAULT_SETTINGS[prop];
                }
            }
            // override and set additional
            for(prop in config){
                if(yuiLang.hasOwnProperty(config,prop)){
                    settings[prop] = config[prop];
                }
            }
        },
        /**
         * called after rendering to apply event handlers
         * @method applyListeners
         */
        // todo: consider making this private
        applyListeners:function(){
            var self = this,
                calendarMonthInner = yuiDom.getElementsByClassName("calendarMonthInner","div",this.settings.container)[0];
//            console.log(calendarMonthInner);
            if(!calendarMonthInner){ return false; }
            yuiEvent.addListener(calendarMonthInner,"click",this._handleClick,this);
            yuiEvent.addListener(calendarMonthInner,"mouseover",this._handleOver,this);
            yuiEvent.addListener(calendarMonthInner,"mouseout",this._handleOut,this);

            var prev = yuiDom.getElementsByClassName("previous","div",calendarMonthInner)[0];
            var next = yuiDom.getElementsByClassName("next","div",calendarMonthInner)[0];

            yuiEvent.addListener(prev,"click",function(){
                self.previousEvent.fire();
            });
            yuiEvent.addListener(next,"click",function(){
                self.nextEvent.fire();
            });
        },
        /**
         * used by event handlers to find date cell which triggered event
         * @method _findCellFromTarget
         * @private
         * @param {HTMLElement} target
         * @return {HTMLElement | null}
         */
        // helper for event handlers
        _findCellFromTarget:function(target){
//            console.log("_findCellFromTarget");
            var cell = null, d, date, index;
            var tagName = target.tagName.toLowerCase();
//                defSelector = false;

            while (tagName != Calendar.CELL_TAGNAME && !yuiDom.hasClass(target, "day")) {
//                if (!defSelector && tagName == "a" && yuiDom.hasClass(target, "selectable")) {
//                    defSelector = true;
//                }
                target = target.parentNode;
                tagName = target.tagName.toLowerCase();
                if (target == this.settings.contianer || tagName == "html") {
                    return cell;
                }
            }
            if(yuiDom.hasClass(target,"day") && !yuiDom.hasClass(target,"outOfMonth")){
                cell = target;
            }
//            console.log("_findCellFromTarget:cell",cell);
            return cell;
        },
        /**
         * delegates onclick event for all date cells
         * @method _handleClick
         * @private
         * @param {Event} e
         * @param {Calendar} cal provides scope of invocation
         */
        _handleClick:function(e,cal){
//            console.log("_handleClick");
            // find node and fire event. pass date/cell
            var node = cal._findCellFromTarget(yuiEvent.getTarget(e));
            if(node){
                // get date
                var date = node.getAttribute("date");
                if(cal.beforeSelectEvent.fire({cell:node,date:CalUtils.dateFromString(date)})){
                    cal.selectEvent.fire({cell:node,date:CalUtils.dateFromString(date),event:e});
                }
                yuiEvent.preventDefault(e);
            }
        },
        /**
         * delegates mouseover event for all date cells
         * @method _handleOver
         * @private
         * @param {Event} e
         * @param {Calendar} cal provides scope of invocation
         */
        _handleOver:function(e,cal){
            var node = cal._findCellFromTarget(yuiEvent.getTarget(e));
            if(node){
//                yuiDom.addClass(node,"hover");
                // get date
                var date = node.getAttribute("date");
                cal.hoverOverEvent.fire({cell:node,date:CalUtils.dateFromString(date),event:e});
                yuiEvent.stopPropagation(e);
            }
        },
        /**
         * delegates mouseout event for all date cells
         * @method _handleOut
         * @private
         * @param {Event} e
         * @param {Calendar} cal provides scope of invocation
         */
        _handleOut:function(e,cal){
            var node = cal._findCellFromTarget(yuiEvent.getTarget(e));
            if(node){
//                yuiDom.removeClass(node,"hover");
                // get date
                var date = node.getAttribute("date");
                cal.hoverOutEvent.fire({cell:node,date:CalUtils.dateFromString(date),event:e});
                yuiEvent.stopPropagation(e);
            }
        },
        /**
         * main rendering methods, invokes renderHeader, renderBody and renderFooter
         * @method render
         */
        render:function(){
            var startDate = this._startDate;
            var currentMonth = startDate.getMonth(),
                currentYear = startDate.getFullYear();
            if(! this.beforeRenderEvent.fire()){ return; }

            var html = [];

            html.push('<div class="calendarMonthInner">');
            html = this.renderHeader(html);
            html = this.renderBody(html);
            html = this.renderFooter(html);
            html.push('</div>');

            this.settings.container.innerHTML = html.join("\n");

            this.applyListeners();
            this.clearRenderers();
            this.renderEvent.fire();
        },
        /**
         * creates html string representing navigation and month name
         * @method renderHeader
         * @param {Array} html array of html strings which will later be inserted into this.container via innerHTML
         * @return {Array} returns the array passed in
         */
        renderHeader:function(html){
            var startDate = this._startDate;
            var currentMonth  = startDate.getMonth(),
                currentYear   = startDate.getFullYear(),
                self = this;

            html.push('<div class="calendarMonthHeader">');
            if(!this.settings.hideNext){
                if(!this.settings.maxDate || this.settings.maxDate > CalUtils.setLastOfMonth(startDate,true)){
                    html.push('  <div class="calendarNavigation next"></div>');
                }
            }
            if(!this.settings.hidePrevious){
                if(!this.settings.minDate || this.settings.minDate < startDate){
                    html.push('  <div class="calendarNavigation previous"></div>');
                }
            }
            html.push('  <div class="monthLabel">');
            html.push(      this.getCopy("MONTHS_LONG." + currentMonth) + " " + currentYear);
            html.push('  </div>');
            html.push('  <div class="clearDiv"></div>');
            html.push('</div>');
            return html;
        },
        /**
         * creates html string representing the day headings and dates, using the built-in and renderers along with any
         * custom defined renders (see addRenderer)
         * @method renderBody
         * @param {Array} html array of html strings which will later be inserted into this.container via innerHTML
         * @return {Array} returns the array passed in
         */
        renderBody:function(html) {
            var startDate = this._startDate;
            var currentMonth  = startDate.getMonth();
            var currentYear   = startDate.getFullYear();
            var currentDay    = new Date(currentYear,currentMonth,1).getDay();
            var week,day,cell,i,j,renderers;
            html.push('<div class="calendarMonthBody">');

            // create day headers
            for (i = 0; i < 7; i++){
                html.push('<div class="dayLabel">');
                html.push(  this.getCopy("WEEKDAYS_SHORT." + i));
                html.push('</div>');
            }

            html.push('<div class="clearDiv"></div>');
            var preMonthDays = this._startDate.getDay();
            var workingDate = new Date(this._startDate.getFullYear(),this._startDate.getMonth(),this._startDate.getDate())
            workingDate = CalUtils.addDays(-preMonthDays,workingDate);

            var tempContainer = document.createElement("div");
            // todo: make hideBlankWeeks && isBlankWeek work
            var hideBlankWeeks =false, isBlankWeek = false;
            for(week = 0; week < 6; week++){
                if(hideBlankWeeks && isBlankWeek){
                    break;
                }
                for(day = 0; day < 7; day++){
                    tempContainer.innerHTML = "";
                    cell = document.createElement("div");
                    yuiDom.addClass(cell,"day");

                    renderers = [];
                    // determine which rendering methods should be applied
                    if(workingDate.getMonth() != this._startDate.getMonth()){
                        renderers.push(Calendar.renderOutOfMonth);
                    }else{
                        cell.setAttribute("date", CalUtils.dateToSystemFormat(workingDate));
                        // rendertypes:date,range,weekday (day/dayOfWeek)
                        for(i = 0; i < this._renderStack.length; i++){
                            switch(this._renderStack[i].type){
                                case Calendar.RENDER_TYPE_DATE:
                                    if(workingDate.getTime() == this._renderStack[i].value.getTime()){
                                        renderers.push(this._renderStack[i].method);
                                        this._renderStack.splice(i,1);
                                    }
                                    break;
                                case Calendar.RENDER_TYPE_RANGE:
                                    if(workingDate >= this._renderStack[i].value[0] && workingDate <= this._renderStack[i].value[1]){
                                        renderers.push(this._renderStack[i].method);
                                        if(workingDate >= this._renderStack[i].value[1]){
                                            this._renderStack.splice(i,1);
                                        }
                                    }
                                    break;
                                case Calendar.RENDER_TYPE_WEEKDAY:
                                    if(workingDate.getDay() == this._renderStack[i].value){
                                        renderers.push(this._renderStack[i].method);
                                    }
                                    break;
                            }
                        }
                        if( (this.settings.minDate && workingDate < this.settings.minDate )
                            || (this.settings.maxDate && workingDate > this.settings.maxDate )){
                            // out of month
                            renderers.push(Calendar.renderOutOfRange);
                        }else{
                            // render default
                            renderers.push(Calendar.renderDefault);
                        }
                    }
                    // if needed other renderers could go here - lef,top,bottom, etc
                    
                    for(j = 0; j < renderers.length; j++){
                        if(renderers[j].call(this,workingDate,cell) === Calendar.STOP_RENDERING){
                            break;
                        }
                    }
                    tempContainer.appendChild(cell);
                    html.push(tempContainer.innerHTML);

                    CalUtils.addDays(1,workingDate);
                }
                html.push('<div class="clearDiv"></div>');
            }

            html.push('<div class="clearDiv"></div>');
            return html;
        },
        /**
         * placeholder method, no footer created by default 
         * @method renderBody
         * @param {Array} html array of html strings which will later be inserted into this.container via innerHTML
         * @return {Array} returns the array passed in
         */
        renderFooter:function(html){
            return html;
        },
        /**
         * provides access to internal start date (always the first of the month)
         * @method getStartDate
         * @return {Date}
         */
        getStartDate:function(){
            return this._startDate;
        },
        /**
         * used to change start date. The first of month will be direved from the date passed in and will become the start date
         * @method setStartDate
         * @return {Date}
         */
        setStartDate:function(date){
            this._startDate = CalUtils.setFirstOfMonth(date,true);
        },
        /**
         * use to change start month and render the calendar
         * @method changeMonth
         * @param {Int} byMonths number of months to change by. use negative number for previous
         */
        changeMonth:function(byMonths){
            this._startDate.setMonth(this._startDate.getMonth() + byMonths);
            this.render();
        },
        /**
         * shortcut to changeMonth, advances Calendar one month forward
          * @method showNext
         */
        showNext:function(){
            this.changeMonth(1);
        },
        /**
         * shortcut to changeMonth, shows previous Calendar month
          * @method showPrevious
         */
        showPrevious:function(){
            this.changeMonth(-1);
        },
        /**
         * use to add render methods
         * @method addRenderer
         * @param {String} type One of: Calendar.RENDER_TYPE_DATE, Calendar.RENDER_TYPE_RANGE, Calendar.RENDER_TYPE_WEEKDAY
         * @param {Date | Array | Int} value For type "date", value is Date; for type "range", type is array of Date [Date,Daate];
         * for type "weekday", value will Int representing day of week (see contstants)
         * @param {Function} method can be one of the provided methods, or a custom method which will
         * be passed Date and Cell are passed as params to method
         */
        addRenderer:function(type,value,method){
            this._renderStack.push( { type:type, value:value, method:method } );
        },
        /**
         * each time render is called, all renderers are removed from the stack
         * @method clearRenderers
         */
        // todo: consider making this private
        clearRenderers:function(){
            this._renderStack.length = 0;
        }
    };
    
    // namespace
    YAHOO.namespace("SW.widget.Calendar");
    yuiLang.augmentProto(Calendar,SW.widget.Calendar.CopyCommon);
    SW.widget.Calendar.Calendar = Calendar;
})();
(function(){
    var yuiDom = YAHOO.util.Dom,
        yuiEvent = YAHOO.util.Event,
        yuiLang = YAHOO.lang,
        yuiEnv = YAHOO.env,
        customEvent = YAHOO.util.CustomEvent,
        Html = SW.tools.Html,
        Calendar = SW.widget.Calendar.Calendar,
        CalUtils = SW.widget.Calendar.Utils;

    var DEFAULT_SETTINGS = {
        monthsToDisplay:2,
        maxRange:31,
        displayLimit:549,
        limitNavigation:true,
        minDate:CalUtils.getToday(),
        maxDate:null
    }
    /*
        optional config params:
        container
        trigger
    */

    function CalendarGroup(settings){
        CalendarGroup.INSTANCES.push(this);
        this._state = Calendar.STATE_UNSELECTED;
        this.errorArray = [];
        this.calendarDivMonth = [];

        this.initSettings(settings);
        this.initCopy(settings.copy);
        this.initEvents();

        // ensure we do not have a bad number for monthsToDisplay
        if(isNaN(this.settings.monthsToDisplay)){
            this.settings.monthsToDisplay =  2;
        }
        if(!this.settings.maxDate && this.settings.displayLimit > 0){
            this.settings.maxDate = CalUtils.addDays(this.settings.displayLimit, new Date());
        }
        if(this.settings.minDate){
            this.firstOfStartMonth = new Date(this.settings.minDate.getFullYear(), this.settings.minDate.getMonth(), 1)
        }
        if(this.settings.maxDate){
            this.lastOfEndMonth = CalUtils.setLastOfMonth(this.settings.maxDate,true);
        }
        /*todo old code passed a function reference and assigned value for rangeStart and checkout*/
        // perhaps listening to beforeShowEvent from StayCal will solve this? Allowing rangeStart/End here is fine regardless
        if(this.settings.rangeStartDate && this.settings.rangeEndDate){
            this.setRangeDates(this.settings.rangeStartDate,this.settings.rangeEndDate);
        }else if(this.settings.rangeStartDate){
            this.setRangeStartDate(this.settings.rangeStartDate);
        }else if(this.settings.rangeEndDate){
            this.setRangeEndDate(this.settings.rangeEndDate);
        }
        this.setStartDate(this.settings.calStartDate);

    }
    CalendarGroup.INSTANCES = [];

    CalendarGroup.prototype = {
        /* privates */
        _TYPE:"CalendarGroup",
        _initialized:false,

        handleReset:function(e){
            yuiEvent.preventDefault(e);
            this.reset();
        },
        handleResetRangeStart:function(e){
            yuiEvent.preventDefault(e);
            this.resetRangeStartDate();
        },
        handleResetRangeEnd:function(e){
            yuiEvent.preventDefault(e);
            this.resetRangeEndDate();
        },

        initialize:function(){
            if(this._initialized) { return false; }
            this._initialized = true;
            this.createFramework();
            this.initCalendars();
            this.initHandlers();
            this.render();
            return true;
        },
        initCalendars:function(){
            var x,calendar,startDate,showNext,showPrevious;
            this.calendars = [];
            for(x = 0; x < this.settings.monthsToDisplay; x++ ){
                startDate = new Date(this.calStartDate);
                startDate.setMonth(startDate.getMonth()+x);
                showNext = (x == this.settings.monthsToDisplay -1);
                showPrevious = (x == 0);
                calendar = new Calendar({
                    container:this.calendarDivMonth[x],
                    startDate:startDate,
                    today:this.settings.minDate,
                    minDate:this.settings.minDate,
                    maxDate:this.settings.maxDate,
                    hideNext:!showNext,
                    hidePrevious:!showPrevious,
                    copy:this.copy
                });
                calendar.initialize();
                this.calendars.push(calendar);
            }
        },
        initEvents:function(){
            this.hoverOverEvent = new customEvent("hoverOverEvent",null,false,customEvent.FLAT);
            this.hoverOutEvent = new customEvent("hoverOutEvent",null,false,customEvent.FLAT);
            this.beforeSelectEvent = new customEvent("beforeSelectEvent",null,false,customEvent.FLAT);
            this.selectEvent = new customEvent("selectEvent",null,false,customEvent.FLAT);
            this.previousEvent = new customEvent("previousEvent",null,false,customEvent.FLAT);
            this.nextEvent = new customEvent("nextEvent",null,false,customEvent.FLAT);
            this.beforeRenderEvent = new customEvent("beforeRenderEvent",null,false,customEvent.FLAT);
            this.renderEvent = new customEvent("renderEvent",null,false,customEvent.FLAT);
        },
        initHandlers:function(){
            var i;
            var self = this;
            // proxy most Calendar events
            for(i = 0; i < this.calendars.length; i++){
                this.calendars[i].beforeSelectEvent.subscribe(function(param){
                    return self.beforeSelectEvent.fire(param);
                });
                this.calendars[i].selectEvent.subscribe(function(param){
                    return self.selectEvent.fire(param);
                });
                this.calendars[i].previousEvent.subscribe(function(param){
                    return self.previousEvent.fire(param);
                });
                this.calendars[i].nextEvent.subscribe(function(param){
                    return self.nextEvent.fire(param);
                });
                this.calendars[i].hoverOverEvent.subscribe(function(param){
                    return self.hoverOverEvent.fire(param);
                });
                this.calendars[i].hoverOutEvent.subscribe(function(param){
                    return self.hoverOutEvent.fire(param);
                });
            }
        },
        initSettings:function(config){
            var prop,
                settings = this.settings = {};
            // get defaults
            for(prop in DEFAULT_SETTINGS){
                if(yuiLang.hasOwnProperty(DEFAULT_SETTINGS,prop)){
                    settings[prop] = DEFAULT_SETTINGS[prop];
                }
            }
            // override and set additional
            for(prop in config){
                if(yuiLang.hasOwnProperty(config,prop)){
                    settings[prop] = config[prop];
                }
            }
        },
        setRangeDates:function(stateDate,endDate){
            var s = stateDate,
                e = endDate;
            if(stateDate > endDate){
                s = endDate;
                e = stateDate;
            }
            this.rangeStartDate = s;
            this.rangeEndDate = e;
            this._updateState();
        },
        setRangeStartDate:function(date){
            this.rangeStartDate = date;
            this._updateState();
        },
        setRangeEndDate:function(date){
            this.rangeEndDate = date;
            this._updateState();
        },
        setStartDate:function(date){
            date = date || this.getDefaultStartDate();
            date = CalUtils.setFirstOfMonth(date,true);
            if( (this.settings.minDate && date < this.firstOfStartMonth)
                || (this.settings.maxDate && date > this.lastOfEndMonth) ) {
                date = this.firstOfStartMonth;                
            }
            this.calStartDate = date;
        },        
        // get calendar start date based on rangeStart and rangeEnd
        getDefaultStartDate:function(){
            return (this.rangeStartDate) ? new Date(this.rangeStartDate.getFullYear(),this.rangeStartDate.getMonth(), 1) : this.firstOfStartMonth;
        },
        selectDay:function(params){
            if(yuiDom.hasClass(params.cell,"availableRangeStart")){
                if(params.date != this.rangeStartDate){
                    if(this.rangeStartDate && !this.rangeEndDate){
                        this.setRangeDates(params.date,this.rangeStartDate);
                    }else{
                        this.setRangeStartDate(params.date);
                    }
                    this.render();
                }
            }else if(yuiDom.hasClass(params.cell,"availableRangeEnd")){
                if(params.date != this.rangeEndDate){
                    if(this.rangeEndDate && !this.rangeStartDate){
                        this.setRangeDates(params.date,this.rangeEndDate);
                    }else{
                        this.setRangeEndDate(params.date);
                    }
                    this.render();
                }
            }
        },
        /*
            whenever rangeStart/rangeEnd dates are changed, state needs to updated.
            setRangeStartDate/Out methods use this internally.
        */
        _updateState:function(){
            if(this.rangeStartDate && this.rangeEndDate){
                this._state = Calendar.STATE_RANGE_SELECTED;
            }else if(!this.rangeStartDate && !this.rangeEndDate){
                this._state = Calendar.STATE_UNSELECTED;
            }else if(this.rangeStartDate){
                this._state = Calendar.STATE_START_SELECTED;
            }else{
                this._state = Calendar.STATE_END_SELECTED;
            }
        },
        getState:function(){
            return this._state;    
        },
        // proxy prev/next methods...
        showNext:function(){
            this.changeMonth(1);
        },
        showPrevious:function(){
            this.changeMonth(-1);
        },
        changeMonth:function(byMonths){            
            var firstCalDate = this.calendars[0].getStartDate(),
                newStartDate;
            if(this.settings.limitNavigation){
                if(byMonths < 0){
                    newStartDate = new Date(firstCalDate.getFullYear(),firstCalDate.getMonth()+byMonths,firstCalDate.getDate());
                    if(newStartDate < this.firstOfStartMonth){
                        return false;
                    }
                }else{
                    newStartDate = new Date(firstCalDate.getFullYear(),firstCalDate.getMonth()+byMonths+this.settings.monthsToDisplay-1,firstCalDate.getDate());
                    if(newStartDate > this.lastOfEndMonth){
                        return false;
                    }
                }
            }
            function changeMonth(cal){
                cal.changeMonth(byMonths);
            }
            // since we are not listening to Calendar's render events, need to fire these here
            // other possibility - have Calendar's changeMonth only set the date but not render - then call render here
            if(! this.beforeRenderEvent.fire()){ return; }
            this.calendars.forEach(changeMonth);
            this.renderEvent.fire()
        },
        reset:function(){
            this.setRangeStartDate(null);
            this.setRangeEndDate(null);
            this.render();
        },
        resetRangeStartDate:function(){
            this.setRangeStartDate(null);
            this.render();
        },
        resetRangeEndDate:function(){
            this.setRangeEndDate(null);
            this.render();
        },
        addRenderer:function(type,value,method){
            function processCal(cal){
                cal.addRenderer(type,value,method);
            }
            this.calendars.forEach(processCal);
        },
        render:function(){
            if(! this.beforeRenderEvent.fire()){ return; }

            this.renderHeader();
            this.renderBody();
            this.renderFooter();
            this.renderErrors();            

            this.renderEvent.fire();
        },
        renderHeader:function(){
            // override in subclass if header may change on each render
        },
        renderBody:function(){
            function processCal(cal){
                cal.render();
            }
            this.calendars.forEach(processCal);
        },
        renderFooter:function(){
            // override in subclass if footer may change on each render
        },
        createFramework:function(){
            var head,body,foot;
            this.container = document.createElement("div");
            yuiDom.addClass(this.container,"calendarContainer");
            var innerContainer = document.createElement("div");
            yuiDom.addClass(innerContainer,"calendarContainerInner");
            this.container.appendChild(innerContainer);

            if(yuiEnv.ua.ie && yuiEnv.ua.ie <= 6){
                //create the iframe to sit behind the layer - workaround for IE and select boxes
                var calenderIframe = document.createElement("iframe");
                    calenderIframe.width = 0;
                    calenderIframe.height =  0;
                    calenderIframe.src = "/common/blank.jsp";
                    yuiDom.setStyle(calenderIframe, "zIndex", "99998");
                this.container.appendChild(calenderIframe);
            }
            head = document.createElement("div");
            body = document.createElement("div");
            foot = document.createElement("div");

            this.head = head;
            this.body = body;
            this.foot = foot;

            yuiDom.addClass(head,"calendarHeader");
            yuiDom.addClass(body,"calendarBody");
            yuiDom.addClass(foot,"calendarFooter");

            this.createHeader(head);
            this.createBody(body);
            this.createFooter(foot);

            innerContainer.appendChild(head);
            innerContainer.appendChild(body);
            innerContainer.appendChild(foot);

            Html.appendClearDiv(innerContainer);

            var parentNode = this.settings.container;
            if(!parentNode){
                parentNode = document.body;
                this.settings.container = this.container;
            }
            parentNode.appendChild(this.container);
            yuiEvent.addListener(this.container,"click",yuiEvent.stopPropagation, this,true);
        },
        // placeholder, likely overridden in subclass
        createHeader:function(container){
        },
        // createBody should not be overridden in a subclass. if it is, it must setup calendarDivMonth
        createBody:function(container){
            var innerContainer = document.createElement("div");
            for(var i = 0; i < this.settings.monthsToDisplay; i++) {
                var calEl = document.createElement("div");
                innerContainer.appendChild(calEl);
                this.calendarDivMonth[i] = calEl;
            }
            yuiDom.addClass(this.calendarDivMonth[0],"firstMonth");
            yuiDom.addClass(this.calendarDivMonth[this.calendarDivMonth.length - 1],"lastMonth");
            container.appendChild(innerContainer);
            // clear div
            Html.appendClearDiv(container);
       },
        // placeholder, likely overridden in subclass
        createFooter:function(container){
        }
    };


    // namespace
    YAHOO.namespace("SW.widget.Calendar");
    yuiLang.augmentProto(CalendarGroup,SW.widget.Calendar.CopyCommon);
    SW.widget.Calendar.Group = CalendarGroup;
})();
(function(){
    var yuiEvent = YAHOO.util.Event,
        yuiDom = YAHOO.util.Dom,
        yuiLang = YAHOO.lang,
        customEvent = YAHOO.util.CustomEvent,
        Html = SW.tools.Html,
        Calendar = SW.widget.Calendar.Calendar,
        SuperClass = SW.widget.Calendar.Group.prototype,
        CalUtils = SW.widget.Calendar.Utils,
        widget = SW.widget;

    function StayCalendar(config){
        StayCalendar.INSTANCES.push(this);
        SuperClass.constructor.call(this, config);
        this.triggers = {};
        if(this.settings.trigger){
            this.triggers.show = this.settings.trigger;
        }
    }

    StayCalendar.INSTANCES = [];

    yuiLang.extend(StayCalendar, widget.Calendar.Group,{
        _TYPE:"StayCalendar",
        _CONTAINER_CLASS:"stayCalendar",
        _isShowing:false,
        showHover:function(param){
            yuiDom.addClass(param.cell,"hover");
            var el = null,coords;
            if(yuiDom.hasClass(param.cell,"availableRangeStart") && !yuiDom.hasClass(param.cell,"rangeStart")){
                el = this._rangeStartMessage;
            }else if (yuiDom.hasClass(param.cell,"availableRangeEnd") && !yuiDom.hasClass(param.cell,"rangeEnd")){
                el = this._rangeEndMessage;
            }
            if(el){
                coords = yuiEvent.getXY(param.event);
                coords[0] += 15;
                coords[1] += 10;
                yuiDom.addClass(el,"show");
                yuiDom.setXY(el,coords,true);
            }
        },
        hideHover:function(param){
            yuiDom.removeClass(this._rangeStartMessage,"show");
            yuiDom.removeClass(this._rangeEndMessage,"show");
            if(param && param.cell){
                yuiDom.removeClass(param.cell,"hover");
            }
        },
        initialize:function(){
            // ensure bad dates aren't passed in
            if(!this.validateCalendar()){
                this.setRangeDates(null, null);
                this.setStartDate(null);
            };
            if(!SuperClass.initialize.call(this)) { return false; }            
        },
        initEvents:function(){
            SuperClass.initEvents.call(this);
            this.beforeShowEvent = new customEvent("beforeShowEvent",null,false,customEvent.FLAT);
            this.showEvent = new customEvent("showEvent",null,false,customEvent.FLAT);
            this.beforeHideEvent = new customEvent("beforeHideEvent",null,false,customEvent.FLAT);
            this.hideEvent = new customEvent("hideEvent",null,false,customEvent.FLAT);
            this.beforeSubmitEvent = new customEvent("beforeSubmitEvent",null,false,customEvent.FLAT);
            this.submitEvent = new customEvent("submitEvent",null,false,customEvent.FLAT);
        },
        initHandlers:function(){
            SuperClass.initHandlers.call(this);
            this.beforeSelectEvent.subscribe(function(params){
                var errors = this.validateSelectedDate(params);
                this.renderErrors();
                return errors;
            },this,true);

            this.selectEvent.subscribe(function(params){
                    this.hideHover();
                    this.selectDay(params);
            },this,true);

            // subclasses may need to unsubscribe these methods in order to perform animations 
            this.showEvent.subscribe(this.simpleShow,this,true);
            this.hideEvent.subscribe(this.simpleHide,this,true);

            this.hoverOverEvent.subscribe(this.showHover,this,true);
            this.hoverOutEvent.subscribe(this.hideHover,this,true);

            this.previousEvent.subscribe(this.showPrevious,this,true);
            this.nextEvent.subscribe(this.showNext,this,true);
            this.beforeRenderEvent.subscribe(this.setRenderers,this,true);
            this.beforeShowEvent.subscribe(this.setPosition,this,true);
        },
        simpleShow:function(){
            yuiDom.addClass(this.container, "show");
        },
        simpleHide:function(){
            yuiDom.removeClass(this.container, "show");
        },
        isShowing:function(){
            return this._isShowing;
        },
        show:function(){
            if(this.isShowing()){ return false; }
            if(!this._initialized){
                this.initialize();
            }
            if(this.beforeShowEvent.fire()){
                this._isShowing = true;
                this.showEvent.fire();
            }
        },
        hide:function(){
            if(!this.isShowing()){ return false; }
            if(this.beforeHideEvent.fire()){
                this._isShowing = false;
                this.hideEvent.fire();
            }
        },
        submit:function(){
            if(!this.beforeSubmitEvent.fire()){ return false; }
            this.submitEvent.fire()
        },                    
        setRenderers:function(){
            var tDate1,tDate2,tDate3,tDate4,daysBetween;
            switch(this._state){
                case Calendar.STATE_UNSELECTED:
                    // avail check in
                    this.addRenderer(Calendar.RENDER_TYPE_RANGE,[this.settings.minDate,this.settings.maxDate],Calendar.renderAvailableRangeStart);
                    break;
                case Calendar.STATE_START_SELECTED:
                    // check in
                    this.addRenderer(Calendar.RENDER_TYPE_DATE,this.rangeStartDate,Calendar.renderRangeStart);
                    //  avail check in
                    this.addRenderer(Calendar.RENDER_TYPE_RANGE,[this.settings.minDate,this.rangeStartDate],Calendar.renderAvailableRangeStart);
                    // unselected
                    tDate2 = new Date(this.rangeStartDate.getFullYear(),this.rangeStartDate.getMonth(),this.rangeStartDate.getDate());
                    tDate2 = CalUtils.addDays(-1,tDate2);
                    this.addRenderer(Calendar.RENDER_TYPE_RANGE,[this.settings.minDate,tDate2],Calendar.renderUnselected);
                    // avail check out
                    tDate1 = new Date(this.rangeStartDate.getFullYear(),this.rangeStartDate.getMonth(),this.rangeStartDate.getDate());
                    tDate1 = CalUtils.addDays(1,tDate1);
                    this.addRenderer(Calendar.RENDER_TYPE_RANGE,[tDate1,this.settings.maxDate],Calendar.renderAvailableRangeEnd);
                    break;
                case Calendar.STATE_RANGE_SELECTED:
                    // check in/out
                    this.addRenderer(Calendar.RENDER_TYPE_DATE,this.rangeStartDate,Calendar.renderRangeStart);
                    this.addRenderer(Calendar.RENDER_TYPE_DATE,this.rangeEndDate,Calendar.renderRangeEnd);
                    // selected
                    this.addRenderer(Calendar.RENDER_TYPE_RANGE,[this.rangeStartDate,this.rangeEndDate],Calendar.renderSelected);
                    // unselected
                    tDate3 = new Date(this.rangeStartDate.getFullYear(),this.rangeStartDate.getMonth(),this.rangeStartDate.getDate());
                    tDate3 = CalUtils.addDays(-1,tDate3);
                    tDate4 = new Date(this.rangeEndDate.getFullYear(),this.rangeEndDate.getMonth(),this.rangeEndDate.getDate());
                    tDate4 = CalUtils.addDays(1,tDate4);
                    this.addRenderer(Calendar.RENDER_TYPE_RANGE,[this.settings.minDate,tDate3],Calendar.renderUnselected);
                    this.addRenderer(Calendar.RENDER_TYPE_RANGE,[tDate4,this.settings.maxDate],Calendar.renderUnselected);
                    // avail check in
                    daysBetween = Math.round(Math.abs(this.rangeEndDate - this.rangeStartDate)/(1000 * 60 * 60 * 24)) - 1;
                    tDate1 = new Date(this.rangeStartDate.getFullYear(),this.rangeStartDate.getMonth(),this.rangeStartDate.getDate());
                    tDate1 = CalUtils.addDays(Math.floor(daysBetween/2),tDate1);
                    this.addRenderer(Calendar.RENDER_TYPE_RANGE,[this.settings.minDate,tDate1],Calendar.renderAvailableRangeStart);
                    // avail check out
                    tDate2 = new Date(tDate1.getFullYear(),tDate1.getMonth(),tDate1.getDate());
                    tDate2 = CalUtils.addDays(1,tDate2);
                    this.addRenderer(Calendar.RENDER_TYPE_RANGE,[tDate2,this.settings.maxDate],Calendar.renderAvailableRangeEnd);
                    break;
                case Calendar.STATE_END_SELECTED:
                    // check out
                    this.addRenderer(Calendar.RENDER_TYPE_DATE,this.rangeEndDate,Calendar.renderRangeEnd);
                    // avail check in
                    tDate1 = new Date(this.rangeEndDate.getFullYear(),this.rangeEndDate.getMonth(),this.rangeEndDate.getDate());
                    tDate1 = CalUtils.addDays(-1,tDate1);
                    this.addRenderer(Calendar.RENDER_TYPE_RANGE,[this.settings.minDate,tDate1],Calendar.renderAvailableRangeStart);
                    // avil checkout/unselected
                    tDate4 = new Date(this.rangeEndDate.getFullYear(),this.rangeEndDate.getMonth(),this.rangeEndDate.getDate());
                    tDate4 = CalUtils.addDays(1,tDate4);
                    this.addRenderer(Calendar.RENDER_TYPE_RANGE,[tDate4,this.settings.maxDate],Calendar.renderUnselected);
                    this.addRenderer(Calendar.RENDER_TYPE_RANGE,[tDate4,this.settings.maxDate],Calendar.renderAvailableRangeEnd);
                    break;
            }
        },
        renderCloseButton: function(container) {
            // create close button
            var closeButton = document.createElement("div");
            yuiDom.addClass(closeButton, "closeLink");
            var closeAction = document.createElement("a");
            closeAction.href = "javascript:void(0);";
            yuiEvent.addListener(closeAction, "click", this.handleHide, this, true);
            closeAction.title = this.getCopy("CLOSE");
            closeAction.appendChild(document.createTextNode(this.getCopy("CLOSE")));
            closeButton.appendChild(closeAction);
            container.appendChild(closeButton);
            this.triggers.hide = closeButton;
//            console.log("render and set to triggers")
            return closeButton;
        },        
        renderFooter:function(){
            var resetLink;
            // submit text doesn't change so needs to be rendered the same on each step
            this._menuElements.submitInstructions.innerHTML = this.getCopy("SUBMIT_INSTRUCTIONS");

            if(this.rangeStartDate) {
                this._menuElements.rangeStartInstructions.innerHTML = this.getCheckInCopy();
                resetLink = document.createElement("a");
                resetLink.innerHTML = this.getCopy("RANGE_START_RESET");
                resetLink.href = "#";
                yuiEvent.addListener(resetLink, "click", this.handleResetRangeStart, this, true);
                this._menuElements.rangeStartInstructions.appendChild(resetLink);

                yuiDom.removeClass(this._menuElements.rangeStartInstructions, "active");
                yuiDom.addClass(this._menuElements.rangeEndInstructions, "active");
            } else {
                this._menuElements.rangeStartInstructions.innerHTML = this.getCopy("RANGE_START_INSTRUCTIONS");
                yuiDom.addClass(this._menuElements.rangeStartInstructions, "active");
            }
            if(this.rangeEndDate) {
                this._menuElements.rangeEndInstructions.innerHTML = this.getCheckOutCopy();
                resetLink = document.createElement("a");
                resetLink.innerHTML = this.getCopy("RANGE_END_RESET");
                resetLink.href = "#";
                yuiEvent.addListener(resetLink, "click", this.handleResetRangeEnd, this, true);
                this._menuElements.rangeEndInstructions.appendChild(resetLink);

                yuiDom.removeClass(this._menuElements.rangeEndInstructions, "active");
            } else{
                this._menuElements.rangeEndInstructions.innerHTML = this.getCopy("RANGE_END_INSTRUCTIONS");
            }
            if(this.rangeStartDate && this.rangeEndDate) {
                yuiDom.addClass(this._menuElements.submitInstructions, "active");   // apply active class to check out div
                this._menuElements.continueButton.innerHTML = this.getCopy("SUBMIT");
                this._menuElements.submitInstructions.appendChild(this._menuElements.continueButtonContainer);
            } else{
                yuiDom.removeClass(this._menuElements.submitInstructions, "active");   // apply active class to check out div
                if(this._menuElements.continueButtonContainer.parentNode == this._menuElements.submitInstructions) {
                    this._menuElements.submitInstructions.removeChild(this._menuElements.continueButtonContainer);
                }
                if(!this.rangeStartDate && !this.rangeEndDate) {
                    yuiDom.addClass(this._menuElements.rangeStartInstructions, "active");   // apply active class to check out div
                    yuiDom.removeClass(this._menuElements.rangeEndInstructions, "active");
                }
            }
        },
        getCheckInCopy:function(){
            return this.getCopy("RANGE_START_PREFIX") + " - " + this.getLocaleFormattedDate(this.rangeStartDate);
        },
        getCheckOutCopy:function(){
            return this.getCopy("RANGE_END_PREFIX") + " - " + this.getLocaleFormattedDate(this.rangeEndDate);
        },
        getLocaleFormattedDate:function(date) {
            var dateString, localeCode = this.getCopy("LOCALE");
            if (localeCode == "zh_CN" || localeCode == "ja_JP") {
                dateString = this.getCopy("WEEKDAYS_MEDIUM."+date.getDay())
                    + ", " + date.getFullYear()
                    + " " + this.getCopy("YEAR")
                    + " " + this.getCopy("MONTHS_SHORT."+date.getMonth())
                    + " " + date.getDate()
                    + " " + this.getCopy("DAY");
            }
            else {
                dateString = this.getCopy("WEEKDAYS_MEDIUM."+date.getDay())
                    + ', ' + date.getDate()
                    + ' ' + this.getCopy("MONTHS_SHORT."+date.getMonth())
                    + ' ' + date.getFullYear()
            }
            return dateString;
        },
        createFramework:function(){
            SuperClass.createFramework.call(this);
            yuiDom.addClass(this.container,this._CONTAINER_CLASS);

            this._rangeStartMessage = this._createToolTip("availableRangeStartMessage","RANGE_START_TOOLTIP");
            this._rangeEndMessage = this._createToolTip("availableRangeEndMessage","RANGE_END_TOOLTIP");
        },
        _createToolTip:function(className,copy){
            var div = document.createElement("div");
            yuiDom.addClass(div, className);
            div.innerHTML = this.getCopy(copy);
            document.body.appendChild(div);
            return div;
        },
        createHeader:function(container) {

            var closeButton = this.renderCloseButton(container);
            // clear calendar
            var resetDiv = document.createElement("div");
            yuiDom.addClass(resetDiv, "actionLink");
            yuiDom.addClass(resetDiv, "resetLink");

            var resetAction = document.createElement("a");
            yuiEvent.addListener(resetAction, "click", this.handleReset, this, true);
            resetAction.title = this.getCopy("RESET");
            resetAction.appendChild(document.createTextNode(this.getCopy("RESET")));
            resetDiv.appendChild(resetAction);

            container.appendChild(resetDiv);
            // clear div
            Html.appendClearDiv(container);

        },
        createFooter:function(container){
            // create menuHolder Div
            this._menuElements = {};
            var menuHolder = document.createElement("div");
            yuiDom.addClass(menuHolder, "messageContainer");
            container.appendChild(menuHolder);
            this._menuElements.menuHolder = menuHolder;
            
            // create error display
            var errorContainer = document.createElement("ul");
            yuiDom.addClass(errorContainer, "errorContainer");
            menuHolder.appendChild(errorContainer);
            this._menuElements.errorContainer = errorContainer;

            var instructions = document.createElement("ol");
            yuiDom.addClass(instructions, "instructions");

            var checkInInstructions = document.createElement("li");
            yuiDom.addClass(checkInInstructions, "rangeStartInstructions");
            this._menuElements.rangeStartInstructions = checkInInstructions;

            var checkOutInstructions = document.createElement("li");
            yuiDom.addClass(checkOutInstructions, "rangeEndInstructions");
            this._menuElements.rangeEndInstructions = checkOutInstructions;

            var submitInstructions = document.createElement("li");
            yuiDom.addClass(submitInstructions, "submitInstructions");

            this._menuElements.submitInstructions = submitInstructions;

            var continueButtonContainer = document.createElement("div");
            yuiDom.addClass(continueButtonContainer, "actionButton");
            var continueButton = document.createElement("button");
            continueButtonContainer.appendChild(continueButton);
            this._menuElements.continueButtonContainer = continueButtonContainer;
            this._menuElements.continueButton = continueButton;

            yuiEvent.addListener(continueButton, "click", this.submit, this, true);
            instructions.appendChild(checkInInstructions);
            instructions.appendChild(checkOutInstructions);
            instructions.appendChild(submitInstructions);

            menuHolder.appendChild(instructions);
        },

        validateSelectedDate:function(params) {
            var selectedDate = params.date;
            var selectedRange = null;
            if(yuiDom.hasClass(params.cell,"availableRangeStart") && yuiDom.hasClass(params.cell,"availableRangeEnd")){
                return false;
            }
            if(this.rangeStartDate&&!this.rangeEndDate) {
                selectedRange = (selectedDate > this.rangeStartDate) ? CalUtils.getLengthOfStay(this.rangeStartDate, selectedDate) : CalUtils.getLengthOfStay(selectedDate, this.rangeStartDate);
            }
            if(this.rangeEndDate&&!this.rangeStartDate) {
                selectedRange = (selectedDate > this.rangeEndDate) ? CalUtils.getLengthOfStay(this.rangeEndDate,selectedDate) : CalUtils.getLengthOfStay(selectedDate,this.rangeEndDate);
            }
            if(this.rangeStartDate && this.rangeEndDate) {
                selectedRange = (selectedDate < this.rangeEndDate) ? CalUtils.getLengthOfStay(selectedDate, this.rangeEndDate) : CalUtils.getLengthOfStay(selectedDate, this.rangeStartDate);
            }
            if(selectedRange > this.settings.maxRange) {
                this.errorArray[this.errorArray.length] = this.getCopy("ERROR_MAX_LOS");
            }
            return !(this.errorArray.length > 0);
        },
        validateCalendar:function(){
            if(this.rangeStartDate){
                if(this.rangeStartDate > this.settings.maxDate){
                    this.errorArray[this.errorArray.length] = this.getCopy("ERROR_TOO_FAR");
                }
                if(this.rangeStartDate < this.firstOfStartMonth){
                    this.errorArray[this.errorArray.length] = this.getCopy("ERROR_START_BEFORE_TODAY");
                }
            }
            if(this.rangeStartDate && this.rangeEndDate){
                if(this.rangeEndDate > this.settings.maxDate && this.checkIn < this.settings.maxDate){
                    this.errorArray[this.errorArray.length] = this.getCopy("ERROR_TOO_FAR");
                }
                if(CalUtils.getLengthOfStay(this.rangeStartDate, this.rangeEndDate) > this.settings.maxRange){
                    this.errorArray[this.errorArray.length] = this.getCopy("ERROR_MAX_LOS");
                }
                if(this.rangeEndDate < this.rangeStartDate){
                    this.errorArray[this.errorArray.length] = this.getCopy("ERROR_END_BEFORE_START");
                }
                if(this.rangeStartDate.getTime() == this.rangeEndDate.getTime()){
                    this.errorArray[this.errorArray.length] = this.getCopy("ERROR_SAME_DATES");
                }
            }
            return !(this.errorArray.length > 0);
        },
        renderErrors:function(){
            if(this.errorArray.length > 0){
                var output = [];
                for(var i=0; i<this.errorArray.length; i++){
                    output.push('<li>' + this.errorArray[i] +'</li>');
                }
                this._menuElements.errorContainer.innerHTML = output.join("\n");
                yuiDom.addClass(this._menuElements.errorContainer, "show");
            }else{
                yuiDom.removeClass(this._menuElements.errorContainer, "show");
                this._menuElements.errorContainer.innerHTML = "";
            }
            this.errorArray = [];
        },
        setPosition:function(){
            if(this.settings.position && this.settings.trigger){
                /*todo convert calls below to yuiDom.getX, getY or getXY*/
                if (this.settings.position == "topLeft") {
                    // display to the top left of the icon
                    this.container.style.top    = (CalUtils.getOffsetTop(this.settings.trigger) - this.container.offsetHeight) + 'px';
                    this.container.style.left   = (CalUtils.getOffsetLeft(this.settings.trigger) - this.container.offsetWidth) + 'px';
                } else if (this.settings.position == "topRight") {
                    // display to the top left of the icon
                    this.container.style.top    = (CalUtils.getOffsetTop(this.settings.trigger) - this.container.offsetHeight) + 'px';
                    this.container.style.left   = CalUtils.getOffsetLeft(this.settings.trigger) + 'px';
                } else if (this.settings.position == "bottomRight") {
                    // display below and to the right of the icon
                    this.container.style.top    = CalUtils.getOffsetTop(this.settings.trigger) + 'px';
                    this.container.style.left   = CalUtils.getOffsetLeft(this.settings.trigger ) + 'px';
                } else if (this.settings.position == "bottomLeft") {
                    // display below and to the right of the icon
                    this.container.style.top    = CalUtils.getOffsetTop(this.settings.trigger) + 'px';
                    this.container.style.left   = (CalUtils.getOffsetLeft(this.settings.trigger) - this.container.offsetWidth) + 'px';
                } else if (this.settings.position == "centerLeft") {
                    // display center and to the left of the icon
                    this.container.style.top    = CalUtils.getOffsetTop(this.settings.trigger) - parseFloat(this.container.offsetHeight)/2 +  'px';
                    this.container.style.left   = (CalUtils.getOffsetLeft(this.settings.trigger) - this.container.offsetWidth) + 'px';
                } else if (this.settings.position == "centerRight") {
                    // display center and to the right of the icon
                    this.container.style.top    = CalUtils.getOffsetTop(this.settings.trigger) - parseFloat(this.container.offsetHeight)/2 +  'px';
                    this.container.style.left   = (CalUtils.getOffsetLeft(this.settings.trigger) ) + 'px';
                } else {
                    // the user tells it where to be placed
                    // expecting a string with x and y seperated by a comma
                    var userLocation = this.settings.position.split(",");
                    this.container.style.top    =  userLocation[1] + 'px';
                    this.container.style.left   =  userLocation[0] + 'px';
                }
                //this.container.iframe.style.top       = this.container.style.top;
                //this.container.iframe.style.left      = this.container.style.left;
            }
            return true;
        },
        // event handlers
        handleShow:function(e){
            yuiEvent.preventDefault(e);
            this.show();
        },
        handleHide:function(e){
            //yuiEvent.preventDefault(e);
            this.hide();
        }
    });

    // namespace
    YAHOO.namespace("SW.widget.Calendar");
    SW.widget.Calendar.StayCalendar = StayCalendar;

})();
(function(){
    var yuiEvent = YAHOO.util.Event,
        yuiDom = YAHOO.util.Dom,
        yuiLang = YAHOO.lang,
        yuiConnect = YAHOO.util.Connect,
        Html = SW.tools.Html,
        Calendar = SW.widget.Calendar.Calendar,
        SuperClass = SW.widget.Calendar.StayCalendar.prototype,
        CalUtils = SW.widget.Calendar.Utils,
        widget = SW.widget;

    function AvailabilityCalendar(config){
        AvailabilityCalendar.INSTANCES.push(this);
        // range dates not valid for Avail
        config.rangeStartDate = null;
        config.rangeEndDate = null;
        SuperClass.constructor.call(this, config);

        this.availability = {};
    }

    AvailabilityCalendar.INSTANCES = [];

    // overwrite/extend core CalendarGroup
    yuiLang.extend(AvailabilityCalendar, widget.Calendar.StayCalendar,{
        _TYPE:"AvailabilityCalendar",
        _CONTAINER_CLASS:"availabilityCalendar",
        _minLoadTime:0, // exagerated for testing

        initialize:function(){
            if(!SuperClass.initialize.call(this)) { return false; }
        },
        initHandlers:function(){
            SuperClass.initHandlers.call(this);

            // remove basic show/hide - most avail calendars will use animate in/out (this can be altered again in subclass if needed)
/*
            this.showEvent.unsubscribe(this.simpleShow);
            this.hideEvent.unsubscribe(this.simpleHide);
*/

            // standar avail loads data and uses animation in/out
            this.showEvent.subscribe(this.showAndLoadData,this,true);
            this.hideEvent.subscribe(this.animateOut,this,true);


            this.beforeSelectEvent.subscribe(this.validateSelectedDate,this,true);
            this.selectEvent.subscribe(this.selectDay,this,true);
            this.nextEvent.subscribe(this.handleNext,this,true);
            this.previousEvent.subscribe(this.handlePrevious,this,true);
            this.beforeRenderEvent.subscribe(this.setRenderers,this,true);
            this.beforeShowEvent.subscribe(this.setPosition,this,true);
        },
        handleNext:function(){
            this.getData(this.settings.monthsToDisplay-1);
        },
        handlePrevious:function(){
            this.getData(-(this.settings.monthsToDisplay-1));
        },
        animateIn:function(){
            // as the animations generally need to act on an element which is not part of container, this
            // method needs to be defined by instance/subclass
            SuperClass.simpleShow.call(this);
        },
        animateOut:function(){
            // as the animations generally need to act on an element which is not part of container, this
            // method needs to be defined by instance/subclass
            SuperClass.simpleHide.call(this);
        },
        showAndLoadData:function(){
            this.animateIn();
            this.getData();
        },
        getData:function(byMonths){
            // we now have toggle methods, which means this method could be called several times before xhr is finished
            // todo: if currently loading data - either cancel previous, or handle properly
            byMonths = byMonths || 0;
            this.showLoading();

            var monthToCheck = (byMonths <= 0) ? this.calendars[0].getStartDate() : this.calendars[this.calendars.length-1].getStartDate();
            var newRequestDate = new Date(monthToCheck.getFullYear(),((monthToCheck.getMonth())), monthToCheck.getDate());
            if(!this.availability[this.getYearMonthCombo(newRequestDate)]){
                // get data
                this.sendRequest(byMonths);
            }
            else{
                // have data
                this.setShowData();
            }
        },

        setShowData:function(){
            var self = this,
                now = new Date();
            var elapsed = now - this._startShowing;
            if(elapsed < this._minLoadTime ){
                setTimeout(function(){
                    self.showData();
                },(this._minLoadTime - elapsed) * 1000);
            }else{
                self.showData();
            }

        },
        showData:function(){
            this.render();
            this.hideLoading();
        },
        setRenderers:function(){
            var date,month,los,monthYear,dateString;
            switch(this.getState()){
                case Calendar.STATE_UNSELECTED:
                    // avail check in
                    for(var i = 0; i < this.calendars.length; i++){
                        month = this.availability[this.getYearMonthCombo(this.calendars[i].getStartDate())];                        
                        if(month){
                            for(date in month){
                                if(yuiLang.hasOwnProperty(month,date)){
                                    this.addRenderer(Calendar.RENDER_TYPE_DATE,CalUtils.dateFromString(date),Calendar.renderAvailableRangeStart);
                                }
                            }
                        }
                    }
                    break;
                case Calendar.STATE_START_SELECTED:
                    // check in
                    this.addRenderer(Calendar.RENDER_TYPE_DATE,this.rangeStartDate,Calendar.renderRangeStart);
                    // avail check
                    monthYear = this.getYearMonthCombo(this.rangeStartDate);
                    dateString = CalUtils.dateToSystemFormat(this.rangeStartDate);
                    date = CalUtils.dateFromString(dateString);
                    if(this.availability[monthYear]){
                        for(los in this.availability[monthYear][dateString]){
                            if(yuiLang.hasOwnProperty(this.availability[monthYear][dateString],los)){
                                this.addRenderer(Calendar.RENDER_TYPE_DATE,CalUtils.addDays(parseInt(los,10),date,true),Calendar.renderAvailableRangeEnd);
                            }
                        }
                    }
                    break;
                case Calendar.STATE_RANGE_SELECTED:
                    // check in
                    this.addRenderer(Calendar.RENDER_TYPE_DATE,this.rangeStartDate,Calendar.renderRangeStart);
                    // check out
                    this.addRenderer(Calendar.RENDER_TYPE_DATE,this.rangeEndDate,Calendar.renderRangeEnd);
                    // selected
                    this.addRenderer(Calendar.RENDER_TYPE_RANGE,[this.rangeStartDate,this.rangeEndDate],Calendar.renderSelected);
                    break;
            }
        },

        sendRequest : function(byMonths){           
            yuiConnect.asyncRequest(
                //new service
                "GET",
                "/corporate/checkAvail.do?" +
                "startMonth=" + this.getRequestMonth(this.calendars[0].getStartDate(), byMonths) +
                "&endMonth=" + this.getRequestMonth(this.calendars[this.calendars.length -1].getStartDate(), byMonths) +
                "&ratePlan=" + this.settings.ratePlan +
                "&propertyId=" + this.settings.propertyId +
                "&numberOfRooms=" + this.settings.numberRooms +
                "&numberOfAdults=" + this.settings.numberAdults,
                {success:this.parseResponse, failure:this.parseError, scope:this}
            );
        },

        getRequestMonth:function(date, byMonths){
            var newRequestDate = new Date(date.getFullYear(),(date.getMonth()+ byMonths),date.getDate());
            return this.getYearMonthCombo(newRequestDate);
        },

        getYearMonthCombo:function(date){
            return date.getFullYear() + "-" + CalUtils.padZero((date.getMonth()+1));
        },
        
        parseResponse:function(response){
            var dateParts,yearMonth,parsedResponse = yuiLang.JSON.parse(response.responseText);
            var dates = parsedResponse.data.availDates;
            for(var date in dates){
                if(yuiLang.hasOwnProperty(dates,date)){
                    if(!this.availability[date]){
                        this.availability[date] = {};
                    }
                    this.availability[date] = dates[date];
                }
            }
            this.availability.currencyCode = parsedResponse.data.currencyCode;
            this.availability.propertyId = parsedResponse.data.propertyId;
            this.availability.ratePlanName = parsedResponse.data.ratePlanName;
            this.setShowData();
        },

        parseError:function(){
            this.hideLoading();
            this.errorArray[this.errorArray.length] = this.getCopy("ERROR_GENERAL");
            this.renderErrors();            
        },
        // consider setting loading class on container and using css to have message appear
        showLoading:function(){
            this._loading = true;
            if(! this._loadingContainer){
                this.createLoading(this.container);
            }
            this._startShowing = new Date();
            yuiDom.addClass(this.container, "loading");
        },
        hideLoading:function(){
            this._loading = false;
            yuiDom.removeClass(this.container, "loading");
        },
        isLoading:function(){
            return this._loading;
        },
        createHeader:function(container) {

            this.createLegend(container);
            // create close button
            this.renderCloseButton(container);
/*
            var closeButton = document.createElement("div");
            yuiDom.addClass(closeButton, "closeLink");
            var closeAction = document.createElement("a");
            closeAction.href = "#";
            yuiEvent.addListener(closeAction, "click", this.handleHide, this, true);
            closeAction.title = this.getCopy("CLOSE");
            var textNode = document.createTextNode(this.getCopy("CLOSE"));
            closeAction.appendChild(textNode);
            closeButton.appendChild(closeAction);
            container.appendChild(closeButton);
            // clear div
            Html.appendClearDiv(container);
*/
       },

        createBody:function(container){
            SuperClass.createBody.call(this, container);

            // clear div
            Html.appendClearDiv(container);


            // reset calendar
            var resetDiv = document.createElement("div");
                yuiDom.addClass(resetDiv,"actionLink");
                yuiDom.addClass(resetDiv,"resetLink");

            var resetAction = document.createElement("a");
                yuiEvent.addListener(resetAction,"click",this.handleReset,this,true);
                resetAction.title = this.getCopy("RESET");
                var textNode = document.createTextNode(this.getCopy("RESET"));
                resetAction.appendChild(textNode);
            resetDiv.appendChild(resetAction);
            container.appendChild(resetDiv);

            // clear div
            Html.appendClearDiv(container);
        },
        createLoading:function(container){
            // background is used to prevent mouse interaction with calendars while loading
            var loadingBackground = document.createElement("div");
            yuiDom.addClass(loadingBackground, "loadingBackground");
            container.appendChild(loadingBackground);
            var loadingMessage = document.createElement("div");
            yuiDom.addClass(loadingMessage, "loadingContainer");
            container.appendChild(loadingMessage);
            loadingMessage.innerHTML = this.getCopy("LOADING");
            this._loadingContainer = loadingMessage;
            this._loadingBackground = loadingBackground;
        },
        createLegend:function(container){
            var textNode;
            var legend = document.createElement("div");
            yuiDom.addClass(legend, "legendContainer");
            container.appendChild(legend);

            var rangeStartKey = document.createElement("div");
            yuiDom.addClass(rangeStartKey, "legendItem");
            yuiDom.addClass(rangeStartKey, "rangeStartLegend");
            var rangeStartGraphic = document.createElement("div");
            yuiDom.addClass(rangeStartGraphic, "rangeStartGraphic");
            rangeStartKey.appendChild(rangeStartGraphic);
            var rangeStartTextContainer = document.createElement("span");
            yuiDom.addClass(rangeStartTextContainer, "legendText");
            yuiDom.addClass(rangeStartTextContainer, "rangeStartLegendText");
            rangeStartKey.appendChild(rangeStartTextContainer);
            textNode = document.createTextNode(this.getCopy("RANGE_START_AVAILABLE"));
            rangeStartTextContainer.appendChild(textNode);

            var rangeEndKey = document.createElement("div");
            yuiDom.addClass(rangeEndKey, "legendItem");
            yuiDom.addClass(rangeEndKey, "rangeEndLegend");
            var rangeEndGraphic = document.createElement("div");
            yuiDom.addClass(rangeEndGraphic, "rangeEndGraphic");
            rangeEndKey.appendChild(rangeEndGraphic);

            var rangeStartTextContainer = document.createElement("span");
            yuiDom.addClass(rangeStartTextContainer, "legendText");
            yuiDom.addClass(rangeStartTextContainer, "rangeEndLegendText");
            rangeEndKey.appendChild(rangeStartTextContainer);
            textNode = document.createTextNode(this.getCopy("RANGE_END_AVAILABLE"));
            rangeStartTextContainer.appendChild(textNode);

            legend.appendChild(rangeStartKey);
            legend.appendChild(rangeEndKey);

            // clear div
            Html.appendClearDiv(legend);
        },

        renderFooter:function(){
            if(this.rangeStartDate) {
                this._menuElements.rangeStartInstructions.innerHTML = this.getCheckInCopy();
                yuiDom.addClass(this._menuElements.rangeEndInstructions, "active");
                yuiDom.addClass(this._menuElements.rangeStartInstructions, "selected");
            } else {
                this._menuElements.rangeStartInstructions.innerHTML = this.getCopy("RANGE_START_INSTRUCTIONS");
                yuiDom.addClass(this._menuElements.rangeStartInstructions, "active");
                yuiDom.removeClass(this._menuElements.rangeStartInstructions, "selected");
            }
            if(this.rangeEndDate) {
                this._menuElements.rangeEndInstructions.innerHTML = this.getCheckOutCopy();
                yuiDom.addClass(this._menuElements.rangeEndInstructions, "selected");
            } else{
                this._menuElements.rangeEndInstructions.innerHTML = this.getCopy("RANGE_END_INSTRUCTIONS");
                yuiDom.removeClass(this._menuElements.rangeEndInstructions, "selected");
            }
            if(this.rangeStartDate && this.rangeEndDate) {
                this._menuElements.submitInstructions.innerHTML = this.getSummaryMessage();
                this._menuElements.continueButton.innerHTML = this.getCopy("SUBMIT");

                yuiDom.addClass(this._menuElements.submitInstructions, "active");   // apply active class to check out div
                this._menuElements.submitInstructions.appendChild(this._menuElements.continueButtonContainer);
            } else{
                yuiDom.removeClass(this._menuElements.submitInstructions, "active");   // apply active class to check out div
                if(this._menuElements.continueButtonContainer.parentNode == this._menuElements.submitInstructions) {
                    this._menuElements.submitInstructions.removeChild(this._menuElements.continueButtonContainer);
                }
                if(!this.rangeStartDate && !this.rangeEndDate) {
                    yuiDom.addClass(this._menuElements.rangeStartInstructions, "active");   // apply active class to check out div
                    yuiDom.removeClass(this._menuElements.rangeEndInstructions, "active");
                }
            }
        },
        getSummaryMessage:function() {
            var los, monthYear, dateString, summary = "";
            //length of stay is reported in days but we display nights in this menu so subtract 1
            los = CalUtils.getLengthOfStay(this.rangeStartDate, this.rangeEndDate);
            monthYear = this.getYearMonthCombo(this.rangeStartDate);
            dateString = CalUtils.dateToSystemFormat(this.rangeStartDate);

            summary = "<div>" + los + " " + this.getCopy("NIGHT_STAYS") + "</div>";
            summary += "<div>" + this.getCopy("DAILY_RATE") + ": ";
            // can cause issues if comming in with selected dates, but have not run any ajax calls yet
            if (this.availability[monthYear]
                && this.availability[monthYear][dateString]
                && this.availability[monthYear][dateString][los]
                && this.availability[monthYear][dateString][los].pts
                ) {
                summary += this.availability[monthYear][dateString][los].pts + " " + this.getCopy("STARPOINTS") + " + ";
            }
            summary += this.availability.currencyCode + " " + this.availability[monthYear][dateString][los].rate + "</div>";
            return summary;
        }
    });

    // namespace
    YAHOO.namespace("SW.widget.Calendar");
    SW.widget.Calendar.AvailabilityCalendar = AvailabilityCalendar;

})();