/*
CORE

KD Kinetic Data Namespace
KD.calendar.ClassName

scal = small calendar
mcal = main view
  mview = month view
  wview = week view
  dview = day view

DO NOT modify the following information manually.
---------------------------------------------------------------------------
 $Rev: 278 $
 $Date: 2008-10-16 15:29:35 -0500 (Thu, 16 Oct 2008) $
---------------------------------------------------------------------------
 */

// debug cancel debug
if (!console)
    var console = {log: function(){}, trace: function(){}, dir: function(){}, error: function(){}};

if(typeof KD=='undefined')KD={}; // don't overwrite the KD namespace

KD.calendar = (function(){
    /* shorthand */
    var E = YAHOO.util.Event;
    var dom = YAHOO.util.Dom;
    var $ = dom.get;
    var Util = KD.calendar.Util;
	
    var isMSIE = /*@cc_on!@*/false;
    var isIEsix = /*@cc_on @if(@_jscript_version < 5.7) !@end @*/false;

    // internal variables to store current info
    var y, m, d;
    var weekper = 100/7;
    var selected = null;
    var widget = null; // stores the date picker widget
    var ln = [null,'January','February','March','April','May','June','July','August','September','October','November','December'];
    var ds = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'];
    var dl = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'];
    var cl = ['#32769a','#437899','#C45B00','#1E4866','#E4B716','#1E6648','#060674','#94720C','#A83B25','#0C5FEC','#662200','#33CC66','#FFCC66','#000000','#7EA30B','#437899','#C45B00','#1E4866','#E4B716','#1E6648','#060674','#94720C','#A83B25','#0C5FEC','#662200','#33CC66','#FFCC66','#000000']; // color array for calendars panel
    var evtCache = {}; // stores structured list of evtCache->calId->YYYYMM->evtTypeId->evtId->event
    var displayMap = {}; // stores a structured view of the layout for rendering decisions
    var currEvent = null;
    var currEventType = null;
    var hasEditableCalendars = false;
    var totalCalendars = 0;
    var cacheTTL = 5 * (60*1000);  // 5 minutes for the cache Time To Live
    var aepTarget = null;

    // stores a value for determining window height changes
    // required to fix IE6 resize issues
    var currentHeight = 0;


    // Default to 8am in day view unless an event occurs earlier
    function scrollToDefault(starttimeId, view){
        var scrollDefault = null;
        var prefix = 'hour_';
        var bodyId = 'day_daybody';

        // if week view, change string names
        if (view === 'w') {
            prefix = 'hour_week_'
            bodyId = 'week_daybody';
        }

        // set the default start time, will be overridden if events start earlier
        if (starttimeId === null) {
            starttimeId = '0800';
        }

        var body = $(bodyId);
        var bodyPos = YAHOO.util.Dom.getY(body);

        var midnight = $(prefix + '0000');
        var midnightPos = YAHOO.util.Dom.getY(midnight);

        var starttime = $(prefix + starttimeId);
        var startTimePos = YAHOO.util.Dom.getY(starttime);
  
        if (midnightPos === bodyPos) {
            scrollDefault = startTimePos-bodyPos;
        } else if (midnightPos < 0) {
            scrollDefault = startTimePos+Math.abs(midnightPos);
        }
        body.scrollTop=scrollDefault;
    }
  

    /* used for short dates */
    function formatDate(dt)
    {
        return ln[dt.getMonth()+1] + ' ' + dt.getDate() + ', ' + dt.getFullYear();
    }

    function formatTime(dt)
    {
        return Util.set2(dt.getHours()) + ':' + Util.set2(dt.getMinutes());
    }


    // format time with AM/PM indicator
    function formatTimeAMPM(dt)
    {
        var indicator = '';
        var hours = dt.getHours();
		
        if (hours < 12) {
            indicator = "am";
        } else {
            indicator = "pm";
        }
        if (hours > 12 ) {
            hours -= 12;
        }
        return '' + hours + ':' + Util.set2(dt.getMinutes()) + indicator;
    }

    function updateFormFields(writeable)
    {
        // get all text input and textarea fields on the form
        var els = Util.getElementsByAttribute('type', 'text', 'input', 'frmcal');
        els = els.concat(Util.getElementsByTag('textarea', 'frmcal'));

        // apply attributes to all form fields
        for (var i=0; i < els.length; i++)
        {
            if (writeable)
            {
                dom.removeClass(els[i], 'disabled');
                els[i].readOnly = false;
            } else {
                dom.addClass(els[i], 'disabled');
                els[i].readOnly = true;
            }
        }

        // get all checkbox input and select fields on the form
        var els = Util.getElementsByAttribute('type', 'checkbox', 'input', 'frmcal');
        els = els.concat(Util.getElementsByTag('select', 'frmcal'));
        // get the start and end dates, don't want the calendar widget to display
        els = els.concat($('ffromdate'));
        els = els.concat($('ftodate'));

        // apply attributes to all form fields
        for (var i=0; i < els.length; i++)
        {
            if (writeable)
            {
                dom.removeClass(els[i], 'disabled');
                els[i].disabled = false;
            } else {
                dom.addClass(els[i], 'disabled');
                els[i].disabled = true;
            }
        }
    };

    function formatResponse(text)
    {
        text = text.replace(new RegExp("(\\n|\\r)", "g"),"");
        text = text.replace(/\&quot;/gi, "\\\"");
        try{
            var obj,msg;
            obj = text.parseJSON();

            if(obj.Error || obj.message)
            {

                if(obj.Error)
                {
                    msg = obj.Error.message;
                }else{
                    msg = obj.message;
                }
                alert(msg);
                window.location.href = AppContext;
                delete obj;
            }
            return obj;
        }catch(e){  }
        return;
    }
    function cache(calendar,dtpart)
    {
        // if a date part is sent in, initialize the cache object
        // if no events are found, at least the object will have been initialized and set as cached
        if(calendar)
        {
            if(dtpart && !evtCache[calendar.id][dtpart]) {
                evtCache[calendar.id][dtpart] = {};
            }

            // set the cache age for the calendar on this month if provided
            if(dtpart) {
                if(!evtCache[calendar.id][dtpart]['age']) evtCache[calendar.id][dtpart]['age'] = {}; // initialize cache expiration age
                evtCache[calendar.id][dtpart]['age'] = new Date().getTime();
            }

            // if dtpart is not provided, loading current month plus month on either side
            if(!dtpart) {
                var now = new Date();
                var d = ''+now.getFullYear()+Util.set2(now.getMonth()+1);

                // if date is not provided, getting the full set for the current
                // month.  Set the age for the current, previous, and next months
                if(!evtCache[calendar.id][d]) evtCache[calendar.id][d] = {};
                if(!evtCache[calendar.id][d]['age']) evtCache[calendar.id][d]['age'] = {}; // initialize cache expiration age
                evtCache[calendar.id][d]['age'] = now.getTime();

                if(!evtCache[calendar.id][getNextDtpart(d)]) evtCache[calendar.id][getNextDtpart(d)] = {};
                if(!evtCache[calendar.id][getNextDtpart(d)]['age']) evtCache[calendar.id][getNextDtpart(d)]['age'] = {};
                evtCache[calendar.id][getNextDtpart(d)]['age'] = now.getTime();

                if(!evtCache[calendar.id][getPrevDtpart(d)]) evtCache[calendar.id][getPrevDtpart(d)] = {};
                if(!evtCache[calendar.id][getPrevDtpart(d)]['age']) evtCache[calendar.id][getPrevDtpart(d)]['age'] = {};
                evtCache[calendar.id][getPrevDtpart(d)]['age'] = now.getTime();
            }


            var items,item,dtpart,nextdtpart,prevdtpart;
            var evtTypes = {};
            for(var i=0, j=calendar.eventTypes.length;i<j;i++)
            {
                item = calendar.eventTypes[i];
                item.color = cl[i]; // apply a color to the evttype
                evtTypes[item.id] = item;
            }
            for(var j=0, k=calendar.eventSummaries.length;j<k;j++)
            {
                item = calendar.eventSummaries[j];
                dtpart = item.start.substring(0,7).replace(' ','');
                if(!evtCache[calendar.id][dtpart]) {evtCache[calendar.id][dtpart] = {};} //initialize date object if it hasn't already
                if(!evtCache[calendar.id][dtpart][item.typeId]) {evtCache[calendar.id][dtpart][item.typeId] = {};} //initialize eventtype object if it hasn't already
                if(!evtCache[calendar.id][dtpart][item.typeId][item.id])
                {
                    item.calendarName = calendar.name; // copy the calendar name in for easy retrieval on edit
                    item.calendarId = calendar.id; // copy the calendar id in for easy retrieval on edit
                    item.type = evtTypes[item.typeId]; // copy the event type into the event for easy retrieval

                    item.color = totalCalendars > 1 ? evtCache[calendar.id].color : item.type.color;
                    cal.createEventInCache(item);
                }
            }
        }
    }
    function getEvtCache()
    {
        return evtCache;
    };

    function getEventType(typeId){
        var etObj;
        for (var i = 0; i < calendars.length; i++) {
            if (!calendars[i]) {
                continue;
            }//IE bug finding empty cals
            var evtTypes = calendars[i].eventTypes;
            for (var k = 0; k < evtTypes.length; k++) {
                if (evtTypes[k].id == typeId) {
                    etObj = evtTypes[k];
                    break;
                }
            }
            if (etObj) {
                break;
            }
        }
        return etObj;
    }
	
    function getPrevDtpart(dtpart)
    {
        if (!dtpart) return;
        var getmonth = parseInt(dtpart.substr(4,2),10);
        var getyear  = parseInt(dtpart.substr(0,4),10);

        var m = getmonth == 1 ? 12 : getmonth - 1;
        var y = m == 12 ? getyear - 1 : getyear;

        return ''+y+Util.set2(m);
    }

    function getNextDtpart(dtpart)
    {
        if (!dtpart) return;
        var getmonth = parseInt(dtpart.substr(4,2),10);
        var getyear  = parseInt(dtpart.substr(0,4),10);

        var m = getmonth == 12 ? 1 : getmonth + 1;
        var y = m == 1 ? getyear + 1 : getyear;

        return ''+y+Util.set2(m);
    }

    function isCached(o, dtpart)
    {
        var now = new Date();     // need the current time to check ttl
        if (o && dtpart)
        {
            if (evtCache[o.id] && evtCache[o.id][dtpart] && evtCache[o.id][dtpart]['age'])
            {
                if (evtCache[o.id][dtpart]['age'] + cacheTTL > now.getTime()) return true;
            }
        }
        return false;
    }

    function loadEvents(o, getyear, getmonth, full)
    {
        for(var i in calendars)
        {
            if(full)
            {
                // check the entire set first to prevent unnecessary loads
                var thisCached = false, prevCached = false, nextCached = false;
                if (isCached(calendars[i],''+getyear+Util.set2(getmonth)))
                {
                    thisCached = true;
                }
                if (isCached(calendars[i],getPrevDtpart(''+getyear+Util.set2(getmonth))))
                {
                    prevCached = true;
                }
                if (isCached(calendars[i],getNextDtpart(''+getyear+Util.set2(getmonth))))
                {
                    nextCached = true;
                }

                // if the cache has not been loaded or expired for any of the months, refresh it
                if (!thisCached || !prevCached || !nextCached)
                {
                    getEvents(calendars[i], o, getyear, getmonth, full)
                }
            } else {
                if (!isCached(calendars[i],''+getyear+Util.set2(getmonth)))
                {
                    getEvents(calendars[i], o, getyear, getmonth, full);
                }
            }
        }
    };

    function drawAllEvents(o, startyear, startmonth, endyear, endmonth)
    {
        var cl;
        cal.clearEvents();

        for(var i in calendars)
        {
            cl = calendars[i];

            // initialize the start date
            var m = startmonth;
            var y =  startyear;

	    // calculate the number of months to get events for
            var start = startyear + ' ' + Util.set2(startmonth);
            var end = endyear + ' ' + Util.set2(endmonth);
	    var numMonths = Util.numberOfMonths(start, end)
            for (var j=0; j<numMonths; j++)
            {
                if (totalCalendars == 1 || $('iscalon' + cl.id).checked && evtCache[cl.id][''+y+Util.set2(m)])
                {
                    // load from the cache and draw it onto the proper calendar (0)
                    o.drawEvents(evtCache[cl.id][''+y+Util.set2(m)], cl.idx);
                }

                // move to the next month/year
                if (m == 12) { m = 1; y += 1; }
                else { m += 1; }
            }
        }
    }

    /*
     * Flattens a calendar into a single array of events, then sorts
     * the events.
     */
    function flattenEvents(calendar) {
        var flatten = new Array();

        // flatten the calendar cache into an array of events
        var i = 0;
        for (var eventtype in calendar) {
            for (var event in calendar[eventtype]) {
                flatten[i] = calendar[eventtype][event];
                i++;
            }
        }

        /*
         * Sort the events based on the following criteria:
         *   1 - All day events are listed above the timed events
         *   2 - Start dates are compared - so events that begin on the earliest day
         *       will appear first
         *   3 - End dates are compared - so events that span multiple days will appear
         *       before events that do not span days
         */
        return flatten.sort(function(a,b) {
            // sort by all day event vs non-all day event
            if (a.allDay && !b.allDay) return -1;
            if (!a.allDay && b.allDay) return 1;
            // sort by start date
            if (a.start < b.start) return -1;
            if (a.start > b.start) return 1;
            // start dates are the same, now sort by end date
            if (a.end > b.end) return -1;
            if (a.end < b.end) return 1;
            // dates are the same, keep order
            return 0;
        });
    };

    function getEvents(cl,o,getyear,getmonth,full)
    {
        if(full) {full = '&FullSet=true'}else{full='';}
        var fordate="";
        if(getyear && getmonth) { fordate='&ForDate='+getyear+Util.set2(getmonth)+'01';}
        if(getyear && !getmonth) { fordate='&ForDate='+getyear+'0101';}
        var tz = UserContext.timezone;
        if (tz.indexOf('+') > -1) { tz = tz.substr(tz.indexOf('+')+1); }
        // make ajax call to load event data for calendar
        var url = appContext + '/GetEvents?CalendarName=' + cl.webName + '' + full + '&TimeZoneOffset=' + tz + fordate
        var callback = {
            success:function(response){
                // check if authentication is required, and reload the page if so
                if (response.getResponseHeader['AUTHENTICATION_REQUIRED']) {
                    window.location.reload();
                    return;
                }

                // add calendar data to cache and clean up newlines from response
                var obj = formatResponse(response.responseText);
                // initialize data container in event cache just in case there's no objects
                if(!evtCache[cl.id][''+getyear+Util.set2(getmonth)]) evtCache[cl.id][''+getyear+Util.set2(getmonth)]={};
                if(full)
                {
                    // initialize data containers in event cache just in case there's no objects
                    if(!evtCache[cl.id][getPrevDtpart(''+getyear+Util.set2(getmonth))]) evtCache[cl.id][getPrevDtpart(''+getyear+Util.set2(getmonth))]={};
                    if(!evtCache[cl.id][getNextDtpart(''+getyear+Util.set2(getmonth))]) evtCache[cl.id][getNextDtpart(''+getyear+Util.set2(getmonth))]={};
                }

                if(obj)
                {
                    for(var i=0;i<obj.length;i++)
                    {
                        cache(obj[i],''+getyear+Util.set2(getmonth));
                        if(full)
                        {
                            cache(obj[i],getPrevDtpart(''+getyear+Util.set2(getmonth)));
                            cache(obj[i],getNextDtpart(''+getyear+Util.set2(getmonth)));
                        }
                    }
                }
                cal.reload();
                cal.ajax.hideLoading();
            },

            failure:function(){cal.ajax.hideLoading();},
            scope:cal
        };
        cal.ajax.showLoading();
        try{
            var transaction = YAHOO.util.Connect.asyncRequest('GET', url, callback, null);
        }catch(e){
            // console.error('Unable to connect to server');
            cal.ajax.hideLoading();
        }
    };
    function formatUrl(link)
    {
        if (link.length == 0 || Util.checkUrlScheme(link)) return link;
        var newlink = 'http://' + link;
        return newlink;
    };

    function initCalendarWidget()
    {
        // set up the calendar widget hooks
        var calwidget = document.createElement('div');
        var activeInput = null;
        calwidget.id = 'calwidget';
        calwidget.style.display = 'none';
        document.body.appendChild(calwidget);
        $('ffromdate').onclick = $('ftodate').onclick = function(e)
        {
            calDate = Util.formatCalDate(this.value);
            dt = calDate.split('/');
            widget.setMonth(dt[0]);
            widget.setYear(dt[1]);
            // need to re-render after setting the pagedate
            widget.render();
            widget.show();
            var currEvt = E.getEvent(e);
            if(currEvt)
            {
                var tgt = E.getTarget(currEvt);
                calwidget.style.left = dom.getX(tgt) + 'px';
                calwidget.style.top = (dom.getY(tgt)+ 20) + 'px';
                activeInput = tgt.id;
            }
        };

        function selEvent(type,args,obj)
        {
            var dates = args[0];
            var date = dates[0];
            var year = date[0], month = date[1], day = date[2];
            widget.hide();
            // calwidget.style.display = 'none';
            $(activeInput).value = formatDate(new Date(year,month-1, day));
            var frm = new Date($('ffromdate').value);
            var tod = new Date($('ftodate').value);
            if(tod < frm)
            {
                $('ftodate').value = formatDate(frm);
            }
            updateHiddenTime();
        }
			
        widget = new YAHOO.widget.Calendar("cal1","calwidget",{close:true,iframe:true});
        widget.selectEvent.subscribe(selEvent, this, true);
    };

    function updateHiddenTime()
    {
        // make sure that the from time is not newer than the to time.
        var fromdate = new Date($('ffromdate').value);
        var todate = new Date($('ftodate').value);
        var fh = $('fh'), fm = $('fm'), fmer = $('fmer');
        var th = $('th'), tm = $('tm'), tmer = $('tmer');
        if((fromdate.getFullYear() == todate.getFullYear() && fromdate.getMonth() == todate.getMonth() && fromdate.getDate() == todate.getDate())
            &&
            (
        ( fmer.selectedIndex > tmer.selectedIndex ) ||
            ( fmer.selectedIndex == tmer.selectedIndex && fh.selectedIndex > th.selectedIndex) ||
            ( fmer.selectedIndex == tmer.selectedIndex && fh.selectedIndex == th.selectedIndex && fm.selectedIndex > tm.selectedIndex)
    )
    )
        {
            th.selectedIndex = fh.selectedIndex;
            tm.selectedIndex = fm.selectedIndex;
            tmer.selectedIndex = fmer.selectedIndex;
        }
        // update hidden time to be sent to server

        var hrs = parseInt(fh.value,10);
        if(hrs == 12 && fmer.selectedIndex == 0) hrs = 0;
        if(hrs < 12 && fmer.selectedIndex == 1) hrs += 12;
        $('ffromtime').value = Util.set2(hrs) + ':' + Util.set2(parseInt(fm.value,10));

        var hrs = parseInt(th.value,10);
        if(hrs == 12 && tmer.selectedIndex == 0) hrs = 0;
        if(hrs < 12 && tmer.selectedIndex == 1) hrs += 12;
        $('ftotime').value = Util.set2(hrs) + ':' + Util.set2(parseInt(tm.value,10));
    };

    function validateRequiredFields() {
        var errors = false;
        var alertMsg = 'This event has the following errors:\n\n';

        // check that a calendar is selected
        if($('fcal').value == '' && $('calendarId').value == '')
        {
            alertMsg += 'You must select a calendar.\n';
            Util.addValidationError('fcal');
            errors = true;
        }
    
        // check that an event type is selected
        if($('ftype').value == '' && $('calendarId').value == '')
        {
            alertMsg += 'You must select an event type.\n';
            Util.addValidationError('ftype');
            errors = true;
        }
    
        // check if event type allows a single related item
        var relItemHolder = $('relItemsHolder');
        if (relItemHolder) { Util.removeValidationError(relItemHolder); }
        var relItem = null;
        if (dom.isAncestor($('evtNameLyr'), relItemHolder)) { relItem = relItemHolder; }

        // check that the event name has been entered, or a related item has been 
        // selected if this event type allows one.
        if($('fname').value == null || $('fname').value == '') {
            if (relItem) {
                alertMsg += 'A ' + $('fnameLabel').innerHTML + ' is required.\n';
                Util.addValidationError(relItem);
            } else {
                alertMsg += 'The event name is required.\n';
                Util.addValidationError($('fname'));
            }
            errors = true;
        } else {
            if (relItem) {
                Util.removeValidationError(relItem);
            } else {
                Util.removeValidationError($('fname'));
            }
        }
    

        // check that the from date is a valid value
        var fromdate = $('ffromdate');
        var fromdateError = false;
        if (!Util.validateDateFormat(fromdate.value)) {
            alertMsg += "The start date format is invalid.  Format: 'January 1, 2008'\n";
            Util.addValidationError(fromdate);
            errors = true;
            fromdateError = true;
        } else {
            Util.removeValidationError(fromdate);
        }

        // check that the to date is a valid value
        var todate = $('ftodate');
        var todateError = false;
        if (!Util.validateDateFormat(todate.value)) {
            alertMsg += "The end date format is invalid. Format: 'January 1, 2008'\n";
            Util.addValidationError(todate);
            errors = true;
            todateError = true;
        } else {
            Util.removeValidationError(todate);
        } 


        // check that the from date is before the to date
        if (!fromdateError && !todateError && Date.parse(fromdate.value) > Date.parse(todate.value)) {
            alertMsg += 'The end date must be equal to or later than the start date.\n';
            Util.addValidationError(todate);
            errors = true;
        } else if(!todateError) {
            Util.removeValidationError(todate);
        }
		
        // display error message, and set focus to first field with an error
        if (errors) {
            alert(alertMsg);
	    els = dom.getElementsByClassName('validationError', '', 'evtedit');
            if (els.length > 0)
            {
                els[0].focus();
            }
        }
		
        return errors;
    }

    function isEventEditable(event){
        var iswritable = true;
        if(!event.type || !event.type.canWrite){
            iswritable = false;
        }else{
            for(var i=0;i<calendars.length;i++){
                if(calendars[i] && event.calendarId == calendars[i].id && !calendars[i].canWrite){
                    iswritable = false;
                }
            }
        }
        return iswritable;
    };

    var cal = {
        init:function(dt)
        {
            if(Error && Error.type == "Error" && Error.message ){
                alert("Error on load: " + Error.message);
            }
            var stale = false;
            if(!UserContext) UserContext = {}; // check if object exists

            // initialize user
            if(UserContext.name)
            {
                $('nav').style.display = 'block';
                $('navname').innerHTML = UserContext.name;
            }else{
                $('navout').style.display = 'block';
            }

            // determine currenttimezone
            var tzs = Util.currentTZ();

            // initialize timezone on usercontext if not already set
            var userTZ = UserContext.timezone;
            if(!UserContext.timezone)
            {
                UserContext.timezone = tzs;
            }

            if(UserContext.timezone != tzs && (!UserContext.timezoneSource || UserContext.timezoneSource == 'Server') )
            {
                UserContext.timezone = tzs;
                stale = true;
            }

            $('tzt').innerHTML = Util.formatTZ(UserContext.timezone);

            // set up event handlers
            function onResize()
            {
                if(isMSIE)
                {
                    if(currentHeight == document.documentElement.clientHeight)
                    {
                        return;
                    }else{
                        currentHeight = document.documentElement.clientHeight;
                    }
                }
                KD.calendar.resize();
            }

            E.addListener( window, 'resize', onResize );
            E.addListener( $('fh'), 'change', updateHiddenTime);
            E.addListener( $('fm'), 'change', updateHiddenTime);
            E.addListener( $('fmer'), 'change', updateHiddenTime);
            E.addListener( $('th'), 'change', updateHiddenTime);
            E.addListener( $('tm'), 'change', updateHiddenTime);
            E.addListener( $('tmer'), 'change', updateHiddenTime);
            E.addListener( $('furl'), 'change', function(){
                $('urllink').href = formatUrl($('furl').value);
                $('urllink').style.display = $('furl').value == "" ? 'none' : 'inline';
            });

            y = dt.year;
            m = dt.month;
            d = dt.day;

            initCalendarWidget();

            // Get the ForDate parameter specified in the URL.
            var forDateParm = Util.getParameter('ForDate');

            // load up event cache
            for(var i=0;i<calendars.length;i++)
            {
                if(calendars[i] && calendars[i].id)
                {
                    calendars[i].idx = i;
                    // color code the calendar
                    if(!evtCache[calendars[i].id])
                    {
                        evtCache[calendars[i].id] = {}; // initialize object if it doesn't exist
                        evtCache[calendars[i].id].color = cl[totalCalendars++];

                        if(stale)
                        {
                            getEvents(calendars[i],mview, y, m, 'fullset');
                        }else{
                            if(forDateParm)
                            {
                                getEvents(calendars[i],mview, y, m, 'fullset');
                            }else{
                                cache(calendars[i]);
                            }
                        }
                    }else{
                        if(!stale){
                            var dtStr = null;
                            if(forDateParm)
                            {
                                if (forDateParm.length >= 6)
                                {
                                    dtStr = forDateParm.substr(0,6);
                                }
                                if (forDateParm.length >= 4 && forDateParm.length < 6)
                                {
                                    dtStr = forDateParm.substr(0,4);
                                }
                            }
                            cache(calendars[i], dtStr);
                        }
                        calendars.splice(i--,1); // I've got a duplicate calendar so toss it.
                    }
                }
            }

            // init elements
            calpanel.init(); // processes the calendar data and sets the calendar panel
            scal.init();
            wview.init(); // slight delay because of CSS attachment issues


            // Adjust the view if specified in the URL.
            var viewParm = Util.getParameter('View');
            if (viewParm) {
                selected = viewParm;
            } else {
                // View was not specified in the URL, see if the calendar has one defined
                // in Remedy.  NOTE - this is only valid for single calendars (not MyCalendars)
                if (totalCalendars == 1 && calendars[0] && calendars[0].defaultView)
                {
                    selected = calendars[0].defaultView;
                }
            }

            // Make sure the default view is a valid value: 'month', 'week', or 'day'
            if (selected && (selected.toLowerCase() == 'month' || selected.toLowerCase() == 'week' || selected.toLowerCase() == 'day')) {
                selected = selected.charAt(0).toLowerCase();
            } else {
                // set the default
                selected = 'm';
            }


            // draw the calendar
            KD.calendar.sel(selected, null);

            // resize the calendar window
            KD.calendar.resize();

            // delay the init because of IE issues
            //      function timed(){ KD.calendar.resize(); };
            //      setTimeout(timed,15);
        },
        go:function(dir)
        {
            cal.aep.close();
            var newdate, newdateshifted;
            switch(selected)
            {
                case 'm':
                    newdate = this.changeMonth({year:y, month:m, dir:dir});
                    mview.setDate(newdate.year, newdate.month);
                    newdateshifted = this.changeMonth({year:newdate.year, month:newdate.month, dir:dir});
                    loadEvents(mview, newdateshifted.year, newdateshifted.month);
                    break;
                case 'w':
                    newdate = this.changeWeek({year:y, month:m, day:d, dir:dir});
                    wview.setDate(newdate.year, newdate.month, newdate.day);
                    newdateshifted = this.changeWeek({year:newdate.year, month:newdate.month, month:newdate.day, dir:dir});
                    loadEvents(wview, newdateshifted.year, newdateshifted.month);
                    break;
                case 'd':
                    newdate = this.changeDay({year:y, month:m, day:d, dir:dir});
                    dview.setDate(newdate.year, newdate.month, newdate.day);
                    newdateshifted = this.changeDay({year:newdate.year, month:newdate.month, month:newdate.day, dir:dir});
                    loadEvents(dview, newdateshifted.year, newdateshifted.month);
                    break;
                default:
            }
        },
        goToday:function()
        {
            cal.aep.close();
            var td = new Date();
            switch(selected)
            {
                case 'm':
                    mview.setDate(td.getFullYear(), td.getMonth()+1);
                    break;
                case 'w':
                    wview.setDate(td.getFullYear(), td.getMonth()+1, td.getDate());
                    break;
                case 'd':
                    dview.setDate(td.getFullYear(), td.getMonth()+1, td.getDate());
                    break;
                default:
            }
            loadEvents({}, td.getFullYear(), td.getMonth()+1, true);
        },
        reload:function()
        {
            cal.aep.close();
            switch(selected)
            {
                case 'm':
                    mview.setDate(y, m);
                    break;
                case 'w':
                    wview.setDate(y,m,d);
                    break;
                case 'd':
                    dview.setDate(y,m,d);
                    break;
                default:
            }
        },
        sel:function(t, el)
        { /* select day/week/month view */
            selected = t;
            document.body.className = 'sel' + t;
            cal.aep.close();
            switch(t)
            {
                case 'm':
                    mview.setDate(scal.view.y, scal.view.m);
                    break;
                case 'w':
                    wview.setDate(scal.view.y, scal.view.m, scal.view.d);
                    break;
                case 'd':
                    try {
                        var e = E.getEvent();
                        if(e.type == 'click')
                        {
                            var td = E.getTarget(e);
                            if(td && td.tagName.toLowerCase() == 'div')
                            {
                                var thisd = td.parentNode.id.replace(/[^\d]/g,'');
                                thisd = new Date(thisd.substr(0,4), thisd.substr(4,2)-1, thisd.substr(6,2));
                                dview.setDate(thisd.getFullYear(), thisd.getMonth()+1, thisd.getDate());
                            }else{
                                dview.setDate(y, m, d);
                            }
                        }else{
                            dview.setDate(y, m, d);
                        }
                        break;
                    } catch(e) {
                        dview.setDate(y, m, d);
                    }
                default:
            }
        },
        resize:function()
        {

            if(isMSIE)
            {
                if(currentHeight != document.documentElement.clientHeight)
                {
                    //return;
                }else{
                    currheight = document.documentElement.clientHeight;
                }
            }

            /* resizes calendar area to fit screen */
            var h = (dom.getViewportHeight() - dom.getY('mcal'))
            var el = $('mcal');

            el.style.height = (h - 15) + 'px';
            var dviewports = Ext.query('div.daybody');
            dom.batch(dviewports, function(el){
                var fd = el.previousSibling;
                if(fd.nodeType == 3) fd = fd.previousSibling;
                el.style.height = (h-100)+'px';
            });
            var mviewport = dom.get('mbody'); // fix body height
            mviewport.style.height = (h-36) + 'px';

            // redraw events
            cal.reload();
        },
        changeMonth:function(d)
        {
            d.month += d.dir;
            if(d.month == 13){ d.month = 1; d.year++; }
            if(d.month == 0){ d.month = 12; d.year--; }
            return d;
        },
        changeWeek:function(d)
        {
            var dt = new Date(d.year, d.month-1, d.day);
            dt.setDate(dt.getDate() + (7*d.dir));

            d.year = dt.getFullYear();
            d.month = dt.getMonth()+1;
            d.day = dt.getDate();

            return d;
        },
        changeDay:function(d)
        {
            var dt = new Date(d.year, d.month-1, d.day);
            dt.setDate(dt.getDate() + d.dir);

            d.year = dt.getFullYear();
            d.month = dt.getMonth()+1;
            d.day = dt.getDate();

            return d;
        },
        addEventValue:function(y,m,d)
        {
            var dt = new Date(y,m-1,d);
            cal.addEvent(dt);
        },
        addEvent:function(dt)
        {
            updateFormFields(true);

            if(!dt) dt = new Date();

            // reset fields
            $('eventtypeId').value = '';
            $('othertypeId').value = '';
            $('calendarId').value = '';
            $('eventId').value = '';
            $('requestId').value = '';

            $('fname').value = '';
            $('fdesc').value = '';
            $('furl').value = '';
            $('urllink').href = '';
            $('urllink').style.display = 'none';
            $('relateditems').value = '';

            $('btnsave').style.display = '';
            $('btndeletecover').style.display = '';


            //reset event type dropdown
            var typesEl = $('ftype');
            typesEl.length = 0;
            typesEl.options[0] = new Option('Select an Event Type','');

            var fcal = $('fcal');
            // if there's only 1 calendar, select it for them
            if(fcal.options.length == 2)
            {
                fcal.selectedIndex = 1;
                fcal.onchange(); // force the onchange
            }else{
                fcal.selectedIndex = 0;
            }

            $('fdesc').value = '';
            $('ffromdate').value = formatDate(dt);
            $('ftodate').value = formatDate(dt);

            $('fh').selectedIndex = 8;
            $('fm').selectedIndex = 0;
            $('fmer').selectedIndex = 0;

            $('th').selectedIndex = 9;
            $('tm').selectedIndex = 0;
            $('tmer').selectedIndex = 0;

            $('fallday').checked = true;
            updateHiddenTime();
            cal.dialog.show('add');
        },
        editEvent:function(e, opts)
        {
            // reset the display fields
            $('exttimerange').style.display = "none";

            var id = opts.id;
            var typeId = opts.typeId;

            // find event in calendar cache
            var event;
            for(var cCal in evtCache)
            {
                for(var evtdt in evtCache[cCal])
                {
                    if(evtCache[cCal] && evtCache[cCal][evtdt])
                    {
                        if(evtCache[cCal][evtdt][typeId] && evtCache[cCal][evtdt][typeId][id])
                        {
                            event = evtCache[cCal][evtdt][typeId][id];
                            break;
                        }
                    }
                }
            }
            if(!event)
            {
                alert('Error: Event Not Found');
                return;
            }
            currEvent = event;
            currEventType=event.type;
            // if calendar isn't writable, hide save/delete buttons
            var iswritable = isEventEditable(event);

            // clear the panel for now and then worry about
            // populating it after the callback
            $('fdesc').value = '';
            $('furl').value = '';
            $('urllink').href = '';
            $('urllink').style.display = 'none';
            $('relateditems').value = '';
            if(iswritable)
            {
                updateFormFields(true);
                $('btnsave').style.display = '';
                $('btndeletecover').style.display = '';
                KD.calendar.panel.updateRelatedMenu(event, event.type);
            }else{
                updateFormFields(false);
                $('btnsave').style.display = 'none';
                $('btndeletecover').style.display = 'none';
            }

            //
            var url = appContext + '/GetEvent?requestId='+Util.URLEncode(event.requestId)+'&eventtype='+Util.URLEncode(event.typeId);
            var callback = {
                success:function(response){
                    // check if authentication is required, and reload the page if so
                    if (response.getResponseHeader['AUTHENTICATION_REQUIRED']) {
                        alert("Your session has timed out.  Please login and try again.");
                        window.location.reload();
                        return;
                    }

                    var obj = formatResponse(response.responseText);
                    var event = response.argument;
                    if(obj)
                    {
                        if(obj.description)
                        {
                            $('fdesc').value = obj.description;
                        }else{
                            $('fdesc').value = '';
                        }
                        if(obj.url)
                        {
                            $('furl').value = obj.url;
                            $('urllink').href = formatUrl(obj.url);
                            $('urllink').style.display = 'inline';
                        }else{
                            $('furl').value = '';
                            $('urllink').href = '';
                            $('urllink').style.display = 'none';
                        }
                        if(obj.relatedItemsList){
                            $('relateditems').value = obj.relatedItemsList;
                        }else{
                            $('relateditems').value ='';
                        }
                        if(isEventEditable(event)){
                            KD.calendar.panel.updateRelatedMenu(event, event.type);
                        }
                        KD.calendar.panel.updateRelatedLabels(event.type);
                        KD.calendar.panel.buildRelatedItemsLayer();
                    }
                    cal.ajax.hideLoading();},
                failure:function(){cal.ajax.hideLoading();},
                scope:cal,
                argument:event
            };
            cal.ajax.showLoading();
            try{
                var transaction = YAHOO.util.Connect.asyncRequest('GET', url, callback, null);
            }catch(e){
                alert('Unable to connect to the server');
                cal.ajax.hideLoading();
            }


            var start = Util.makeDate(event.start);
            var end = Util.makeDate(event.end);

            // populate form

            $('evtheader').innerHTML = event.calendarName;
            if(event.type) $('evttype').innerHTML = event.type.name;

            $('eventId').value = event.id;
            if(event.requestId) $('requestId').value = event.requestId;
            $('eventtypeId').value = event.typeId;
            $('othertypeId').value = event.otherTypeId;
            $('calendarId').value = event.calendarId;
            $('fname').value = event.name;
            $('ffromdate').value = formatDate(start);
            $('ftodate').value = formatDate(end);
            $('ffromtime').value = formatTime(start);
            $('ftotime').value = formatTime(end);

            var hrs = start.getHours();
            if(hrs == 0) hrs = 24;

            var si = hrs;
            if(si > 12) si -= 12;
            if(si == 12) si = 0;

            $('fh').selectedIndex = si;
            var mins = start.getMinutes();
            switch(mins)
            {
                case 15: $('fm').selectedIndex = 1; break;
                case 30: $('fm').selectedIndex = 2; break;
                case 45: $('fm').selectedIndex = 3; break;
                default: $('fm').selectedIndex = 0;
            }

            $('fmer').selectedIndex = (hrs >= 12 && hrs < 24) ? 1 : 0;

            var hrs = end.getHours();
            if(hrs == 0) hrs = 24;

            var si = hrs;
            if(si > 12) si -= 12;
            if(si == 12) si = 0;

            $('th').selectedIndex = si;

            var mins = end.getMinutes();
            switch(mins)
            {
                case 15: $('tm').selectedIndex = 1; break;
                case 30: $('tm').selectedIndex = 2; break;
                case 45: $('tm').selectedIndex = 3; break;
                default: $('tm').selectedIndex = 0;
            }

            $('tmer').selectedIndex = (hrs >= 12 && hrs < 24) ? 1 : 0;

            $('fallday').checked = event.allDay;

					
            // if timed external event, replace time dropdown with text
            if (event.type && event.type.source && $('fallday').checked !== true) {
                if (event.type.source === "Custom") {
                    $('fextfromtime').value = formatTimeAMPM(start);
                    $('fexttotime').value = formatTimeAMPM(end);
							
                    // display the external event fields 
                    $('timerange').style.display = "none";
                    $('exttimerange').style.display = "block";
                }
            }

            cal.dialog.show('edit');
        },
        clearEvents:function()
        {
            displayMap = {}; // reset displayMap
            // clearevents first
            var evtsEls = Ext.query('div.evtout,div.marker');
            for(var i=0;i<evtsEls.length;i++)
            {
                evtsEls[i].parentNode.removeChild(evtsEls[i]);
            }
        },
        createLabel:function(div, h){
            var lbl=dom.getElementsByClassName('event','div',div);
            h = h-14;
            if(h<0)h=0;
            if(lbl != null){lbl[0].style.height=(h)+'px';}
        },
        createEvent:function(opts)
        {
            // evtCache[opts.id] = opts;
            //opts: id, start, end, idx, isAllDay, calidx, name, typeId
            var label = '';

            var start = Util.makeDate(opts.start);
            var end = Util.makeDate(opts.end);

            var divouter = document.createElement('div');
            divouter.className = 'evtout';
            var moreafter = divouter.cloneNode(false);
            moreafter.className = 'after';
            divouter.appendChild(moreafter);
            var div = divouter.cloneNode(false);
            div.className = 'evtin';
            var indiv = div.cloneNode(false);
            div.id = 'evt_' + opts.calendarId + '_' + opts.typeId + '_' + opts.id;
            div.style.position = 'relative';
            E.addListener( divouter, 'click', cal.editEvent, opts );
            indiv.className = 'event';
            indiv.className += opts.allDay?' full':'';
            if(opts.allDay)
            {
                div.style.backgroundColor = opts.color;
            }else{
                div.style.color = opts.color;
                indiv.style.borderTopColor = opts.color;

                label += formatTimeAMPM(start) + ' ';
            }
            var entityName = Util.replaceHTMLEntity(opts.name);
            indiv.innerHTML = label + entityName;
            div.title = label + opts.name;
            if(opts.description && opts.description != ""){
                div.title = opts.description;
            }

            div.appendChild(indiv);
            divouter.appendChild(div);
            return divouter;
        },
        //Call to get related item info
        getMoreInfo:function(eventId, keyFieldValue, eventTypeId, el)
        {
            var url = appContext + '/GetMoreInfo?eventId=' + Util.URLEncode(eventId) + '&keyFieldValue=' + Util.URLEncode(keyFieldValue) + '&eventtype=' + Util.URLEncode(eventTypeId);
            var callback = {
                success:function(response){
                    // check if authentication is required, and reload the page if so
                    if (response.getResponseHeader['AUTHENTICATION_REQUIRED']) {
                        alert("Your session has timed out.  Please login and try again.");
                        window.location.reload();
                        return;
                    }

                    //check for errors, but we use the XML if OK
                    var text = response.responseText;
                    text = text.replace(new RegExp("(\\n|\\r)", "g"),"");
                    try{
                        var obj = text.parseJSON();
                        var isPartialHTML=false;
                        if(obj.Error){
                            cal.ajax.hideLoading();
                            alert(obj.Error.message);
                            return;
                        }
                    } catch(err){
                        //If not a JSON object for a message or XML, assuming its a partial HTML doc
                        if(!response.responseXML){
                            isPartialHTML=true;
                        }
                    }
                    //OK continue
                    var moreInfoLayer=response.argument;
                    if(!moreInfoLayer){moreInfoLayer=$('item_more_info');}
                    //re-init
                    while (moreInfoLayer.childNodes[0]) {
                        moreInfoLayer.removeChild(moreInfoLayer.childNodes[0]);
                    }
                    if(isPartialHTML){
                        moreInfoLayer.innerHTML=text;
                        moreInfoLayer.parentNode.style.display="block";
                    }else{
                        var theTable=Util.buildSingleRecordTable(response.responseXML);
                    }
                    if(theTable){
                        theTable.className="single"
                        //Multiple related items
                        if(response.argument != null){
                            theTable.className="multi";
                        }
                        theTable.id="relatedInfoTable";
                        moreInfoLayer.appendChild(theTable);
                        moreInfoLayer.parentNode.style.display="block";
                    }
                    cal.ajax.hideLoading();
                },
                failure:function(){
                    cal.ajax.hideLoading();
                },
                scope:cal,
                argument:el
            };
            cal.ajax.showLoading();
            try{
                var transaction = YAHOO.util.Connect.asyncRequest('GET', url, callback, null);
            }catch(e){
                alert('Unable to connect to the server to get more info');
                cal.ajax.hideLoading();
                //cal.dialog.close();
            }
        },

        deleteEvent:function()
        {

            if(confirm('Are you sure?'))
            {
                // make ajax call to delete an event
                var delnode;
                var event = currEvent;
                var url = appContext + '/SendEvent?calendarId=' + Util.URLEncode(event.calendarId) + '&RequestId=' + Util.URLEncode(event.requestId) + '&EventId=' + Util.URLEncode(event.id) + '&delete=true';
                var callback = {
                    success:function(response){
                        // check if authentication is required, and reload the page if so
                        if (response.getResponseHeader['AUTHENTICATION_REQUIRED']) {
                            alert("The event could not be deleted, your session has timed out.  Please login and try again.");
                            window.location.reload();
                            return;
                        }

                        var obj = formatResponse(response.responseText);
                        if(obj)
                        {
                            cal.deleteEventFromCache(event);
                            cal.reload();
                        }
                        cal.dialog.close();
                        cal.ajax.hideLoading();},
                    failure:function(){cal.ajax.hideLoading();},
                    scope:cal
                };
                cal.ajax.showLoading();
                cal.dialog.close();
                try{
                    var transaction = YAHOO.util.Connect.asyncRequest('GET', url, callback, null);
                }catch(e){
                    alert('Unable to connect to the server');
                    cal.ajax.hideLoading();
                    cal.dialog.close();
                }
                return true;
            }else{
                return false;
            }
        },
        toggleCalendar:function(el)
        {
            var sel;
            switch(selected)
            {
                case 'd': sel = 'dview'; break;
                case 'w': sel = 'wview'; break;
                case 'm': sel = 'mview'; break;

            };
            cal[sel].setDate(y,m,d);
        },
        deleteEventFromCache:function(obj,isupdate)
        {
            // if updating an event, need to clear it out first
            if (isupdate)
            {
                for (dtpart in evtCache[obj.calendarId])
                {
                    if(evtCache[obj.calendarId] && evtCache[obj.calendarId][dtpart] && evtCache[obj.calendarId][dtpart][obj.typeId])
                    {
                        if(evtCache[obj.calendarId][dtpart][obj.typeId][obj.id])
                        {
                            delete evtCache[obj.calendarId][dtpart][obj.typeId][obj.id];
                        }
                    }
                }
            } else {
	        // search event queue and clear event
	        for(var cCal in evtCache)
	        {
                    // delete the event in all months it exists
                    var numMonths = Util.numberOfMonths(obj.start, obj.end);
                    var curY = parseInt(obj.start.substr(0,4),10);
                    var curM = parseInt(obj.start.substr(5,2),10);
                    for (var c=0; c < numMonths; c++) {
                        var dtpart = ''+curY+Util.set2(curM);
                        if(evtCache[cCal] && evtCache[cCal][dtpart] && evtCache[cCal][dtpart][obj.typeId])
                        {
                            if(evtCache[cCal][dtpart][obj.typeId][obj.id])
                            {
                                delete evtCache[cCal][dtpart][obj.typeId][obj.id];
                            }
                        }
                        curM = Util.incrementMonth(curM);
                        if (curM == 1) curY++;
                    }
	        }
            }
        },
        createEventInCache:function(obj)
        {
            // update the event in all months it exists
            var numMonths = Util.numberOfMonths(obj.start, obj.end);
            var curY = parseInt(obj.start.substr(0,4),10);
            var curM = parseInt(obj.start.substr(5,2),10);
            for (var c=0; c<numMonths; c++) {
                var dtpart = ''+curY+Util.set2(curM);
                // initialize the cache if it is not already
                if(!evtCache[obj.calendarId][dtpart]) { evtCache[obj.calendarId][dtpart] = {}; }
                if(!evtCache[obj.calendarId][dtpart][obj.typeId]) { evtCache[obj.calendarId][dtpart][obj.typeId] = {}; }
	
                // update the cache
                evtCache[obj.calendarId][dtpart][obj.typeId][obj.id] = obj;
	
                // increment the date part
                curM = Util.incrementMonth(curM);
                if (curM == 1) { curY++; }
            }
        },
        /* =============================
         All Events Panel functions
         */
        aep:{
            show:function(e){
                var jsevt = E.getEvent(e);
                var el = E.getTarget(jsevt);
                KD.calendar.aepTarget = el;
                KD.calendar.aep.render('show');
            },
            redraw:function(){
	        var isOpen = $('alleventspanel').style.display;
	        if (isOpen == 'block' || isOpen == 'inline') {
                    KD.calendar.aep.render();
	        }
            },
            render:function(action){
                var el = KD.calendar.aepTarget;
                var match = el.className.match(/date(\d{8})/);
                var dt = match[1]; // the second part has just the date string
                var today = new Date(dt.substr(0,4), dt.substr(4,2)-1, dt.substr(6,2));
                var panel = $('alleventspanel');
                var eventslots = $('aepevents');
                eventslots.innerHTML = ''; //clear out events;
                $('aeplabel').innerHTML = ln[parseInt(dt.substr(4,2),10)] + ' ' + parseInt(dt.substr(6,2),10) + ' ('+ds[today.getDay()]+')';
                var div;
                if(totalCalendars > 1){
                    // calendars
                    for(var i in evtCache)
                    {
                        if(evtCache[i][dt.substr(0,6)] && $('iscalon'+ i).checked)
                        {
                            for(var typeId in evtCache[i][dt.substr(0,6)])
                            {
                                // skip the 'age' object, not really an event type
                                if (typeId == "age") continue;

                                for(var id in evtCache[i][dt.substr(0,6)][typeId])
                                {
                                    var calevt = evtCache[i][dt.substr(0,6)][typeId][id];
                                    var start = Util.makeDate(calevt.start);
                                    var end = Util.makeDate(calevt.end);
                                    for(var calidx=0;calidx<calendars.length;calidx++)
                                    {
                                        if(calendars[calidx] && calendars[calidx].id == calevt.calendarId) calevt.calidx = calidx;
                                    }

                                    var startdate = new Date(start.getFullYear(), start.getMonth(), start.getDate());
                                    var enddate = new Date(end.getFullYear(), end.getMonth(), end.getDate());
                                    var endofday = new Date(today.getFullYear(), today.getMonth(), today.getDate(),23,59,59);

                                    if(startdate <= today && enddate >= today)
                                    {
                                        startdate = today;
                                        enddate = endofday;
                                    }
                                    if(startdate <= endofday && enddate >= endofday)
                                    {
                                        div = cal.createEvent(calevt);
                                        if(start < today) dom.addClass(div, 'morebefore');
                                        if(end.valueOf() > today.valueOf() + 86400000) dom.addClass(div, 'moreafter');
                                        div.style.position = 'relative';
                                        eventslots.appendChild(div);
                                    }
                                }
                            }
                        }
                    }
                }else{
                    // event types
                    var flattened = flattenEvents(evtCache[calendars[0].id][dt.substr(0,6)])
                    for(var i in flattened)
                    {
                        // skip the 'age' object, not really an event type
                        if (i == "age") continue;

                        var calevt = flattened[i];
                        if ($('iscalon'+calevt.typeId).checked)
                        {
                            var start = Util.makeDate(calevt.start);
                            var end = Util.makeDate(calevt.end);
                            for(var calidx=0;calidx<calendars.length;calidx++)
                            {
                                if(calendars[calidx] && calendars[calidx].id == calevt.calendarId) calevt.calidx = calidx;
                            }

                            var startdate = new Date(start.getFullYear(), start.getMonth(), start.getDate());
                            var enddate = new Date(end.getFullYear(), end.getMonth(), end.getDate());
                            var endofday = new Date(today.getFullYear(), today.getMonth(), today.getDate(),23,59,59);

                            if(startdate <= today && enddate >= today)
                            {
                                startdate = today;
                                enddate = endofday;
                            }
                            if(startdate <= endofday && enddate >= endofday)
                            {
                                div = cal.createEvent(calevt);
                                if(start < today) dom.addClass(div, 'morebefore');
                                if(end.valueOf() > today.valueOf() + 86400000) dom.addClass(div, 'moreafter');
                                div.style.position = 'relative';
                                eventslots.appendChild(div);
                            }
                        }
                    }
                }

                if (action == 'show') {
                    var pos = dom.getXY(el);
                    panel.style.display = 'block';
                    // Ensure window will always be visible
                    var topPos = pos[1] - parseInt(panel.clientHeight,10) + 20;
                    if (topPos < 38) {
                        topPos = 38;
                    }
                    panel.style.top = topPos + 'px';
                    panel.style.left = pos[0] + 'px';
                }
            },
            close:function()
            {
                $('alleventspanel').style.display = 'none';
            }
        },
        /* =============================
         Add/Edit Dialog functions
         */
        dialog:{
            fnameLabel:"Name",
            relatedInfoLabel:"Related Info",
            saveNew:function()
            {
                // validate the required fields before submitting
                if (validateRequiredFields()) {
                    return false;
                }

                $("btnsave").disabled=true;
                var url = appContext + '/SendEvent';
                // grab the event type to store with the event
                var evtType, color, calName;
                var selEvtType = $('eventtypeId').value;
                for(var i in calendars)
                {
                    var evtTypes = calendars[i].eventTypes;
                    if(totalCalendars > 1 && $('calendarId').value == calendars[i].id) color = cl[i];
                    for(var j=0;j<evtTypes.length;j++)
                    {
                        if(selEvtType == evtTypes[j].id)
                        {
                            evtType = evtTypes[j];
                            calName = calendars[i].name;
                            if(totalCalendars == 1) color = cl[j];
                            break;
                        }
                    }
                    if(evtType)break;
                }

                var formObject = document.getElementById('frmcal');
                YAHOO.util.Connect.setForm(formObject);
                var callback = {
                    success:function(response){
                        // check if authentication is required, and reload the page if so
                        if (response.getResponseHeader['AUTHENTICATION_REQUIRED']) {
                            alert("The event could not be updated, your session has timed out.  Please login and try again.");
                            window.location.reload();
                            return;
                        }

                        var obj = formatResponse(response.responseText);
                        if(!obj)
                        {
                            //                alert('There was a problem saving your information.');
                        }else{
                            if(obj.id){
                                // update the record in the cache
                                obj.type = evtType; // attach the event type here before storing it in the cache
                                obj.color = color; // attach the event color
                                obj.calendarName = calName;

                                // first remove the event from the cache to clean up any
                                // existing months that are no longer used, then create the 
                                // the event in the cache
                                cal.deleteEventFromCache(obj, true);
                                cal.createEventInCache(obj);
                            }
                            // determine current view
                            cal.dialog.close();
                        }
                        cal.reload();

                        cal.ajax.hideLoading();
                        $("btnsave").disabled=false;
                    },
                    failure:function(){
                        cal.ajax.hideLoading();
                        $("btnsave").disabled=false;},
                    scope:cal
                };
                cal.ajax.showLoading();
                try{
                    var transaction = YAHOO.util.Connect.asyncRequest('POST', url, callback);
                }catch(e){
                    alert('Unable to connect to the server');
                    cal.ajax.hideLoading();
                    cal.dialog.close();
                }
            },
            show:function(dialogType)
            {
                document.onkeydown = function(e)
                {
                    var jsevt = E.getEvent(e);
                    if(jsevt.keyCode == 27) cal.dialog.close();
                }
                var shadow = document.createElement('div');
                shadow.id = 'shadow';
                if(isIEsix) {
                    shadow.style.height =  document.documentElement.clientHeight;
                }
                document.body.appendChild(shadow);
                var el = dom.get('evtedit');
                el.style.display = 'block';
                dom.addClass(el, dialogType == 'add' ? 'add':'edit');
                dom.removeClass(el, dialogType == 'edit' ? 'add':'edit');
                this.toggleAllDay();

            },
            close:function()
            {
                widget.hide();
                document.onkeydown = null;
                var el = dom.get('evtedit');
                el.style.display = 'none';
                var shadow = dom.get('shadow');
                if(shadow) shadow.parentNode.removeChild(shadow);
                cal.dialog.resetRelatedItems();
				 
                //remove validation highlighting
                Util.removeValidationError();
            },

            toggleAddRelated:function(hide){
                var display="";
                if (hide){display="none";}
                $('addRelItem').style.display=display;
            },

            resetRelatedItems:function()
            {
                $('relItemsLayer').style.display="none";
                $('relateditems').value="";
                var moreInfoLayer=$('item_more_info');
                while (moreInfoLayer.childNodes[0]) {
                    moreInfoLayer.removeChild(moreInfoLayer.childNodes[0]);
                }
                // catch if the node isn't attached to the event name
                try{
                    $('fname').parentNode.removeChild(relObjSel);
                }catch(e){}
                $('fname').style.display="";
                $('relItemsHolder').onchange=null;
                $('addRelItem').insertBefore($('relItemsHolder'), $('addbutton'));
            },

            toggleAllDay:function()
            {
                var el = $('evtedit');

                if($('fallday').checked)
                {
                    dom.addClass(el, 'isallday');
                }else{
                    dom.removeClass(el, 'isallday');
                }
            }
        }, /*end dialog */
        ajax:{
            showLoading:function()
            {
                $('ajaxloading').style.display = 'block';
            },
            hideLoading:function()
            {
                $('ajaxloading').style.display = 'none';
            }
        }
    }; // cal core

    /* =============================
         CALENDAR SIDEBAR PANEL
     */
    var calpanel = {
        init:function()
        {
            var el = $('cals');
            var isunique;
            for(var cal in calendars)
            {
                isunique = true; // reset
                var eventTypeWriteable = false;
                for(var i=0;i<cal;i++)
                {
                    if(calendars[i].id == calendars[cal].id) isunique = false;
                }
                if(calendars[cal].id && isunique)
                {
                    if(totalCalendars > 1){
                        $('calpaneltitle').innerHTML = 'Calendars';
                        var tr = document.createElement('tr');
                        var td = document.createElement('td');
                        tr.appendChild(td.cloneNode(false));
                        tr.appendChild(td.cloneNode(false));
                        tr.appendChild(td.cloneNode(false));

                        tr.childNodes[0].innerHTML = "<input type=\"checkbox\" id=\"iscalon"+calendars[cal].id+"\" checked onclick=\"KD.calendar.toggleCalendar(this);KD.calendar.aep.redraw();\">";
                        tr.childNodes[1].innerHTML = "<a href=\""+appContext+"/calendar?CalendarName="+calendars[cal].webName+"\">"+calendars[cal].name+"</a>";
                        tr.childNodes[2].innerHTML = "<div class=\"color\" style=\"background-color:"+cl[cal]+"\">&nbsp;</div>";
                        el.appendChild(tr);
                        var evtTypes = calendars[cal].eventTypes;
                        for(var type in evtTypes)
                        {
                            if(!eventTypeWriteable) eventTypeWriteable = evtTypes[type].canWrite;
                        }
                    }else{
                        $('calpaneltitle').innerHTML = 'Event Types';
                        var evtTypes = calendars[cal].eventTypes;
                        var colourIdx = 0;
                        for(var type in evtTypes)
                        {
                            var tr = document.createElement('tr');
                            var td = document.createElement('td');
                            tr.appendChild(td.cloneNode(false));
                            tr.appendChild(td.cloneNode(false));
                            tr.appendChild(td.cloneNode(false));

                            tr.childNodes[0].innerHTML = "<input type=\"checkbox\" id=\"iscalon"+evtTypes[type].id+"\" checked onclick=\"KD.calendar.toggleCalendar(this);KD.calendar.aep.redraw();\">";
                            tr.childNodes[1].innerHTML = evtTypes[type].name;
                            tr.childNodes[2].innerHTML = "<div class=\"color\" style=\"background-color:" + cl[colourIdx++] + "\">&nbsp;</div>";
                            el.appendChild(tr);
                            if(!eventTypeWriteable) eventTypeWriteable = evtTypes[type].canWrite;
                        }
                    }

                    // add it to the list of calendars in the add/edit dialog
                    if(calendars[cal].canWrite && eventTypeWriteable)
                    {
                        var sel = $('fcal');
                        hasEditableCalendars = true;
                        sel.options[sel.options.length] = new Option(calendars[cal].name, calendars[cal].id);
                    }
                }
            }
            if(!hasEditableCalendars) $('ceventadd').style.visibility = 'hidden';
        },

        updateTypes:function(o)
        {
            var calid = o.value;
            var caltypes;
            $('calendarId').value = calid;
            for(var i=0;i<calendars.length;i++)
            {
                if(calendars[i].id == calid)
                {
                    caltypes = calendars[i].eventTypes;
                    break;
                }
            }

            var typesEl = $('ftype');

            // reset drop down
            typesEl.length = 0;

            if(caltypes)
            {
                // rebuild
                for(var i=0;i<caltypes.length;i++)
                {
                    if(caltypes[i].canWrite) typesEl.options[typesEl.options.length] = new Option(caltypes[i].name, caltypes[i].id);
                }
                $('eventtypeId').value = caltypes[0].id;
            }else{
                typesEl.options[0] = new Option('Select an Event Type','');
                $('eventtypeId').value = '';
            }

            typesEl.selectedIndex = 0;
            typesEl.onchange(); //Force onchange
        },
        selectType:function(o)
        {
            $('eventtypeId').value = o.value;
            currEventType=getEventType(o.value);
            //If event type has changed, remove previous related items and reset menu
            $('relateditems').value="";
            $('fname').value="";
            $('relItemsLayer').style.display='none';
            KD.calendar.panel.updateRelatedLabels(currEventType);
            KD.calendar.panel.updateRelatedMenu(null, currEventType);
        },

        updateRelatedLabels:function(evtTypeObj){
            if(evtTypeObj && evtTypeObj.attachToMenuName && evtTypeObj.menuName && evtTypeObj.menuName != ""){
                $('fnameLabel').innerHTML=evtTypeObj.menuName;
                $('relatedInfoLabel').innerHTML=cal.dialog.relatedInfoLabel;
            } else if(evtTypeObj && !evtTypeObj.attachToMenuName && evtTypeObj.menuName){
                $('relatedInfoLabel').innerHTML=evtTypeObj.menuName;
                $('fnameLabel').innerHTML=cal.dialog.fnameLabel;
                var fname=$('fname');
                if (fname && fname.value == "") { fname.value=evtTypeObj.name; }
            } else{
                $('fnameLabel').innerHTML=cal.dialog.fnameLabel;
                $('relatedInfoLabel').innerHTML=cal.dialog.relatedInfoLabel;
            }
        },

        updateRelatedMenu:function(event, type)
        {
            var etObj = null;
            if (type) {
                etObj = type;
            }
            if(event && event.type && !etObj){
                etObj = getEventType(event.type.id);
            }

            //Add or Remove the related items menu from the Event Name layer
            // just doing a quick check as it errors out when selecting the "Select a Calendar option
            // more robust checking is probably required
            if(!etObj)return;
            var relObjSel = Util.buildSelectOptions($('relItemsHolder'),etObj.menuOptions);

            if(etObj.attachToMenuName && relObjSel.options.length > 1){
                $('fname').style.display="none";
                $('fname').parentNode.appendChild(relObjSel);

                //Set the menu value with the related item
                var relitem=formatResponse($('relateditems').value);
                var label="";
                var value="";
                //only add items if this is an existing event
                if(relitem && event){
                    for(prop in relitem){
                        label = prop;
                        value = eval("relitem['"+Util.escapePunct(label)+"']");
                        relObjSel.value=value;
                    }
                    //Create an element on the list if the current element is no longer found in menu
                    if(relObjSel.value == null || relObjSel.value==""){
                        var opt = document.createElement("option");
                        opt.setAttribute("value", value);
                        opt.appendChild(document.createTextNode(label));
                        relObjSel.appendChild(opt);
                        relObjSel.selectedIndex=relObjSel.options.length-1;
                    }
                    //We have a free text value, probably set before a menu attached
                } else if ($('fname').value != null && $('fname').value != "" && event){
                    var opt = document.createElement("option");
                    opt.setAttribute("value", "");
                    opt.appendChild(document.createTextNode($('fname').value));
                    relObjSel.appendChild(opt);
                    relObjSel.selectedIndex=relObjSel.options.length-1;
                }
                //onchange of this menu, set the related item info
                relObjSel.onchange = function(){
                    var optlbl = this.options[this.selectedIndex].text;
                    $('relateditems').value="{\""+Util.escapePunct(optlbl)+"\":\""+this.value+"\"}";
                    $('fname').value = optlbl;
                    KD.calendar.panel.buildRelatedItemsLayer();
                }
            }else{
                // catch if the node isn't attached to the event name
                try{
                    $('fname').parentNode.removeChild(relObjSel);
                }catch(e){}
                $('fname').style.display="";
                relObjSel.onchange=null;
                $('addRelItem').insertBefore(relObjSel, $('addbutton'));
            }
            //If this is new, build the layer, otherwise will be built on return of event
            if(event == null){
                KD.calendar.panel.buildRelatedItemsLayer();
            }
        },
        /*Add a new related item value to the hidden field*/
        addRelatedItem:function()
        {
            var ri=$('relateditems');//real holder
            var value=$('relItemsHolder').value;
            if(!value || value == ""){
                alert("Please select an item from the menu");
                return;
            }
            var tmp = ri.value;
            var label = $('relItemsHolder').options[$('relItemsHolder').selectedIndex].text;
            if (!tmp || tmp == "" || tmp == "{}"){tmp="{";}
            //remove the end bracket and add the new property on at the end
            tmp = tmp.replace(/}$/, ",")+"\""+label+"\":\""+value+"\"}";
            ri.value=tmp;
            KD.calendar.panel.buildRelatedItemsLayer();
        },

        /*Remove related item*/
        removeRelatedItem:function(id)
        {
            var ri=$('relateditems');//real holder
            var obj;
            if(ri && ri.value){obj = ri.value.parseJSON();}
            if(obj){
                for(prop in obj){
                    var thisid=eval("obj['"+Util.escapePunct(prop)+"']");
                    if(thisid==id){
                        eval("delete obj['"+Util.escapePunct(prop)+"']");
                        break;
                    }
                }
            }
            ri.value = toJSONString(obj);
            KD.calendar.panel.buildRelatedItemsLayer();
        },

        /*Take the value(s) in the related items hidden field and build the
         * appropriate layer to display them.
         */
        buildRelatedItemsLayer:function(){
            var event=currEvent;
            var canWrite=true;
            //If no event obj, we are adding an event
            if(event){canWrite=isEventEditable(event);}
            var eventType=currEventType;
            var ris=formatResponse($('relateditems').value);
            if(!ris && (!eventType.canSelectMultiple || !canWrite)){
                //  cal.dialog.resetRelatedItems();
                return;}

            if(!canWrite || !eventType.canSelectMultiple){
                cal.dialog.toggleAddRelated(true);
            }
            else if(canWrite && eventType.canSelectMultiple){
                cal.dialog.toggleAddRelated(false);
            }
            var theLayer=document.createElement('div');
            //Count the related items
            var count=0;
            for (prop in ris){
                count += 1;
                if(count > 1){break;}
            }
            for(prop in ris){
                var label=prop;
                var id=eval("ris['"+Util.escapePunct(label)+"']");
                if(count == 1 && !eventType.canSelectMultiple){
                    if(eventType.source == 'Default' || eventType.source == 'Custom'){
                        KD.calendar.getMoreInfo('', id, eventType.id);
                    } else {
                        KD.calendar.getMoreInfo(event.id, id, '');
                    }
                    return;
                }
                var row=document.createElement('div');
                /*label*/
                row.className="relItemsRow";
                row.setAttribute('id',"row_"+id);

                /*Related Item name/link*/
                var more=document.createElement('a');
                more.className="more";
                more.setAttribute('id', "more_"+id);
                more.setAttribute('href',"javascript:{}");
                more.onclick=function(){
                    var rowId=this.id.split("more_")[1];
                    var fldLayer=$('fields_'+rowId);
                    if(fldLayer){
                        //Just toggle display if already loaded
                        var display="none";
                        if(fldLayer.style.display=="none"){display="block";}
                        fldLayer.style.display=display;
                    } else{
                        fldLayer=document.createElement('div');
                        fldLayer.setAttribute('id','fields_'+rowId);
                        fldLayer.className="relfields";
                        this.parentNode.appendChild(fldLayer);
                        if(eventType.source == 'Default' || eventType.source == 'Custom'){
                            KD.calendar.getMoreInfo('', rowId, eventType.id, fldLayer);
                        } else {
                            KD.calendar.getMoreInfo(event.id, rowId, '', fldLayer);
                        }
                    }
                    return false;
                }
                more.appendChild(document.createTextNode(label));
                row.appendChild(more);

                /*remove button*/
                if (canWrite)
                {
                    var rmv=document.createElement('a');
                    var rmvImg=document.createElement('img');
                    rmvImg.setAttribute("src", "img/remove.gif");
                    rmvImg.setAttribute("alt", "remove item");
                    rmvImg.className="morebutton";
                    rmv.appendChild(rmvImg);
                    rmv.className="rmv";
                    rmv.setAttribute('id', "rmv_"+id);
                    rmv.setAttribute('href',"javascript:{}");
                    rmv.onclick=function(){
                        if(confirm("Are you sure?")){
                            KD.calendar.panel.removeRelatedItem(this.id.split("rmv_")[1]);
                        }
                    }
                    row.appendChild(rmv);
                }

                theLayer.appendChild(row);
            }
            //Rebuild layer every time
            var moreInfoLayer=$('item_more_info');
            while (moreInfoLayer.childNodes[0]) {
                moreInfoLayer.removeChild(moreInfoLayer.childNodes[0]);
            }
            moreInfoLayer.appendChild(theLayer);
            moreInfoLayer.parentNode.style.display="block";
        }

    };

    /* =============================
     MONTH VIEW
     */
    var mview = {

        start:null,
        end:null,
        setDate:function(yr, mn)
        {
            y = yr; m = mn;
            this.formatDays(y, m);
            scal.setDate({y:y,m:m});
            scal.formatSelected();

            /* redraw events */
            drawAllEvents(this, this.start.getFullYear(), this.start.getMonth()+1, this.end.getFullYear(), this.end.getMonth()+1)
        },
        formatDays:function(yr, mn)
        {
            $('currdatelabel').innerHTML = ln[mn] + ' ' + yr;
            var el = $('ceventadd').getElementsByTagName('a')[0];
            el.onclick=function(){KD.calendar.addEvent();}

            /* redraw days */
            var dt = new Date(yr, mn-1, 1); //get the first of the month
            dt.setDate(1-dt.getDay()); // move to Sunday
            this.start = new Date(dt);

            var rows = Ext.query('#mview div.week');
            var td = new Date();
            var numrows= 6;
            for(var i=0;i<rows.length;i++)
            {
                if(i>1)
                {
                    if(dt.getMonth()+1 != mn)
                    {
                        rows[i].style.display = 'none';
                        if(numrows == 6) numrows = i; // only change if it hasn't already been
                        continue;
                    }else{
                        rows[i].style.display = '';
                    }
                }
                for(var j=0;j<7;j++)
                {
                    var el = rows[i].childNodes[j];
                    el.childNodes[0].innerHTML = dt.getDate();  // first childnode of a cell is the day div
                    el.id = 'mview_' + dt.getFullYear() + Util.set2(dt.getMonth()+1) + Util.set2(dt.getDate());
                    if(hasEditableCalendars)
                    {
                        el.style.cursor = 'pointer';
                        el.onclick = function(e)
                        {
                            var jsevt = E.getEvent(e);
                            var el = E.getTarget(jsevt);
                            var match = el.parentNode.id.match(/mview_(\d{8})/);
                            var dt = match[1]; // the second part has just the date string
                            var today = new Date(dt.substr(0,4), dt.substr(4,2)-1, dt.substr(6,2));
                            cal.addEvent(today);
                        }
                    }
                    if(dt.getMonth()+1 != mn){
                        el.className = 'd dim';
                    }else if(dt.getFullYear() == td.getFullYear() && dt.getMonth()+1 == td.getMonth()+1 && dt.getDate() == td.getDate()){
                        el.className = 'd curr';
                    }else{
                        el.className = 'd';
                    }
                    dt.setDate(dt.getDate()+1);
                }
            }
            this.end = new Date(dt);


            // reset heights
            var totalheight = $('mbody').offsetHeight;
            for(var i=0;i<rows.length;i++)
            {
                rows[i].style.top = (i * 100/numrows) + '%';
                // rows[i].style.height = (100/numrows) + '%';
                rows[i].style.height = (totalheight/numrows) + 'px';
            }
        },
        drawEvents:function(evts, calidx)
        {
            // flatten the calendar events so they can be sorted
            var flattened = flattenEvents(evts);

            // remove events if they have already been drawn by a previous
            // period.
            for (var k=0; k<flattened.length; k++)
            {
                // check the dom to see if the event already exists, and remove
                // from the list if it does
                var id = $('evt_' + flattened[k].calendarId + '_' + flattened[k].typeId + '_' + flattened[k].id);
                if (id) flattened.splice(k--,1);
            }

            if(!displayMap.weeks)
            {
                displayMap.weeks = [[],[],[],[],[],[]]; // set up maps for each week
            }

            var rows = Ext.query('#mview div.week');
            // determine max events that can fit into a row
            var maxitems = Math.floor(rows[0].offsetHeight / 18) - 2; // -1 for the day and -1 for the marker
            // process each week separately
            for(var w=0;w<6;w++)
            {
                var el = rows[w];
                if(el.style.display != 'none')
                {
                    var _t = el.childNodes[0].id;
                    var startofweek = new Date(_t.substr(6,4),_t.substr(10,2)-1,_t.substr(12,2));
                    var endofweek = new Date( startofweek.getFullYear(), startofweek.getMonth(), startofweek.getDate()+6,23,59,59);

                    var tdy;
                    for(var i=0; i<flattened.length; i++)
                    {
                        var event = flattened[i];
                        if(totalCalendars == 1 && !($('iscalon'+ event.typeId).checked)) continue;
                        event.calidx = calidx;
                        var start = Util.makeDate(event.start);
                        var end = Util.makeDate(event.end);
                        var domarker = false;

                        var startdate = new Date(start.getFullYear(), start.getMonth(), start.getDate());
                        var enddate = new Date(end.getFullYear(), end.getMonth(), end.getDate());

                        if(start < startofweek && end > startofweek) startdate = startofweek;
                        if(end > endofweek && start < endofweek) enddate = new Date(endofweek.getFullYear(),endofweek.getMonth(),endofweek.getDate(),0,0,0);

                        var daysdiff = Math.ceil(enddate.valueOf() / 86400000) - Math.floor(startdate.valueOf() / 86400000) ;

                        if(startdate >= startofweek && enddate <= endofweek)
                        {
                            var div = cal.createEvent(event);

                            if(start < startofweek) dom.addClass(div, 'morebefore');
                            if(end.valueOf() > endofweek.valueOf()) dom.addClass(div, 'moreafter');

                            div.style.left = weekper * startdate.getDay() + '%';
                            div.style.width = weekper * daysdiff + '%';
                            var mask = Math.pow(2,daysdiff)-1;
                            mask <<= startdate.getDay();
                            // check the display map to find an open horizontal slot
                            var slotfound = false;
                            var heightslot = 0;

                            for(var j=0;j<displayMap.weeks[w].length;j++)
                            {
                                // bitwise NOT the slots and AND it with the mask to see if any slots are open
                                if(!slotfound && ((~displayMap.weeks[w][j] & mask) == mask))
                                {
                                    displayMap.weeks[w][j] |= mask; //commit the change
                                    heightslot = j;
                                    slotfound = true;
                                    if(heightslot >= maxitems)
                                    {
                                        // we'll have to do the marker for each day it's required
                                        domarker = true;
                                    }
                                }
                            }
                            if(!slotfound)
                            {
                                heightslot = displayMap.weeks[w].length;
                                displayMap.weeks[w][displayMap.weeks[w].length] = mask;
                                if(heightslot >= maxitems)
                                {
                                    // we'll have to do the marker for each day it's required
                                    domarker = true;
                                }
                            }
                            if(!domarker)
                            {
                                div.style.top = 19 * heightslot + 18 + 'px';
                                el.appendChild(div);
                            }else{
                                // go through and update markers
                                for(var dow=0;dow<7;dow++)
                                {
                                    var itemcount = 0;
                                    var mask = Math.pow(2,dow);
                                    for(var s=maxitems;s<displayMap.weeks[w].length;s++)
                                    {
                                        if((~displayMap.weeks[w][s] & mask) != mask) itemcount++;
                                    }
                                    if(itemcount)
                                    {
                                        var _t = 'marker_' + w + '_' + dow;
                                        var icel = $(_t);
                                        if(icel) icel.parentNode.removeChild(icel);
                                        var div = document.createElement('div');
                                        div.id = _t;
                                        tdy = new Date(startofweek);
                                        tdy.setDate( tdy.getDate() + dow );
                                        div.className="marker date" + tdy.getFullYear() + Util.set2(tdy.getMonth()+1) + Util.set2(tdy.getDate());
                                        div.style.left = weekper * dow + '%';
                                        div.innerHTML = '+' + itemcount + ' items';
                                        div.onclick = cal.aep.show;
                                        el.appendChild(div);
                                    }
                                }
                            }/*if !domarker*/
                        }
                    }
                }
            }
        }
    };



    /* =============================
     WEEK VIEW
     */
    var wview =
        {
        start:null,
        end:null,
        init:function()
        {
            // insert separators for full-day
            var el = Ext.query('#wview div.fullday')[0];
            var div = document.createElement('div');
            div.className = 'sep';
            for(var i=1;i<7;i++)
            {
                var nd = div.cloneNode(true);
                nd.style.left = (100/7*i) + '%';
                nd.style.height = el.clientHeight + 'px';
                el.appendChild(nd);
            }
        },
        setDate:function(yr,mn,dy)
        {

            y = yr; m = mn; d = dy;
            this.formatDays(yr, mn, dy);
            scal.setDate({y:yr,m:mn,d:dy});
            scal.formatSelected();

            var fdayel = Ext.query('#wview .fullday')[0];
            var tdayel = Ext.query('#wview .daybody')[0];
            fdayel.style.height = ''; // reset before loading

            /* redraw events */
            drawAllEvents(this, this.start.getFullYear(), this.start.getMonth()+1, this.end.getFullYear(), this.end.getMonth()+1);

            // set it to show all events
            fdayel.style.height = (fdayel.scrollHeight + 1) + 'px';
            tdayel.style.height = (dom.getViewportHeight() - dom.getY('mcal') - fdayel.scrollHeight - 42) + 'px';
        },
        drawEvents:function(evts, calidx)
        {
            var startScroll = 8;  // start time of earliest event, or 8:00 am
            if(!displayMap.allday) displayMap.allday = []; // set up fullday map
            if(!displayMap.days) displayMap.days = [];
            var start, end, startdate, enddate;
            var today = new Date(y,m-1,d);
            today = new Date(y,m-1,d - today.getDay());
            var endofweek = new Date( today.valueOf() + 7 * 86400000 - 1);

            var flattened = flattenEvents(evts);

            // remove events if they have already been drawn by a previous
            // period.
            for (var k=0; k<flattened.length; k++)
            {
                // check the dom to see if the event already exists, and remove
                // from the list if it does
                var id = $('evt_' + flattened[k].calendarId + '_' + flattened[k].typeId + '_' + flattened[k].id);
                if (id) flattened.splice(k--,1);
            }

            // map all day events
            for(i in flattened)
            {
                var event = flattened[i];

                if(totalCalendars == 1 && !($('iscalon'+ event.type.id).checked)) continue;
                if(event.allDay)
                {
                    start = Util.makeDate(event.start);
                    end = Util.makeDate(event.end);
                    startdate = new Date(start.getFullYear(), start.getMonth(), start.getDate());
                    enddate = new Date(end.getFullYear(), end.getMonth(), end.getDate());
                    start = startdate;
                    end = new Date(end.getFullYear(), end.getMonth(), end.getDate(), 23, 59, 59);

                    if(start < today && end > today) startdate = today;
                    if(end > endofweek && start < endofweek) enddate = endofweek;
                    if(startdate >= today && enddate <= endofweek)
                    {
                        event.calidx = calidx;
                        var div = cal.createEvent(event);
                        // map all day events
                        var els = Ext.query('#wview div.fullday')[0];
                        // determine position of this div
                        var daysdiff = Math.ceil(enddate.valueOf() / 86400000) - Math.floor(startdate.valueOf() / 86400000) ;
                        div.style.left = weekper * startdate.getDay() + '%';
                        div.style.width = weekper * daysdiff + '%';
                        var mask = Math.pow(2,daysdiff)-1;
                        mask <<= startdate.getDay();
                        // add arrows if event spans more than the current week
                        if(start < today) dom.addClass(div, 'morebefore');
                        if(end.valueOf() > endofweek.valueOf())
                        {
                            dom.addClass(div, 'moreafter');
                            // set width to 1 day less so the moreafter arrow displays
                            div.style.width = weekper * (daysdiff-1) + '%';
                            // if ie6, adjust the width even more
                            if (isIEsix) div.style.width = weekper * (daysdiff-1) * 0.9759 + '%';

                        }
                        // check the display map to find an open horizontal slot
                        var slotfound = false;
                        var heightslot = 0;

                        for(var k=0;k<displayMap.allday.length;k++)
                        {
                            // bitwise NOT the slots and AND it with the mask to see if any slots are open
                            if(!slotfound && ((~displayMap.allday[k] & mask) == mask))
                            {
                                displayMap.allday[k] |= mask; //commit the change
                                heightslot = k;
                                slotfound = true;
                            }
                        }
                        if(!slotfound)
                        {
                            heightslot = displayMap.allday.length;
                            displayMap.allday[displayMap.allday.length] = mask;
                        }
                        div.style.top = 20 * heightslot + 'px';
                        els.appendChild(div);
                    }/* if event = this week */
                }/* if allday */
            }/* end for evts */


            // map timed events
            var els = Ext.query('#wview div.wday');
            var dowEl; // day of week element
            for(var wd=0;wd<7;wd++)
            {/*loop through each day*/
                dowEl = els[wd];

                endoftoday = new Date(today.getFullYear(), today.getMonth(), today.getDate(),23,59,59);

                for(var i in flattened)
                {
                    var event = flattened[i];
                    if(totalCalendars == 1 && !($('iscalon'+ event.type.id).checked)) continue;
                    if(!event.allDay)
                    {
                        start = Util.makeDate(event.start);
                        end = Util.adjustMidnight(event.end);
                        if(start < today && end > today) start = new Date(today);
                        if(end > endoftoday && start < endoftoday) end = new Date(endoftoday);
                        if(start >= today && end <= endoftoday)
                        {
                            // set the scroll start
                            if (start.getHours() < startScroll) {
                                startScroll = start.getHours();
                            }
								
                            // map this event
                            event.calidx = calidx;
                            var div = cal.createEvent(event);

                            var t = 27.5 * start.getHours() * 2;
                            t += 14 * (start.getMinutes()/15);


                            var startmins = start.getHours() * 60 + start.getMinutes();
                            var endmins = end.getHours() * 60 + end.getMinutes();

                            var l = endmins - startmins;
                            var h = (27.5 * 2 * (l/60))-2;
                            if(h<0)h=0;

                            div.style.top = t + 'px';
                            div.style.height =  h + 'px';
                            cal.createLabel(div, h);
                            dowEl.appendChild(div);

                            //reposition the elements
                            var reg = dom.getRegion(div);
                            var ereg, defaultleft;
                            var allevts = dowEl.childNodes;
                            w = 100;
                            for(var l=0;l<allevts.length;l++)
                            {
                                ereg = dom.getRegion(allevts[l]);
                                if(reg.intersect(ereg) && allevts.length > 1  && div != allevts[l]) {
                                    w /= 1.25;
                                    var intersectedNode = allevts[l];
                                    if(!intersectedNode.style.width) intersectedNode.style.width = '100%';
                                    intersectedNode.style.width = parseInt(intersectedNode.style.width,10) / 1.1 + '%';
                                }
                            }
                            div.style.zIndex = t;
                            div.style.left = (100/w - 1)*w + '%';
                            div.style.width = w + '%';
                        }
                    } /* if !allday */
                }/* for i in evts */
                today.setDate(today.getDate()+1); // change today

            }/* for d */

            // scroll to the start of the first event, or 08:00
            startScroll = Util.set2(startScroll) + '00';
            scrollToDefault(startScroll, 'w');

        },
        formatDays:function(y, m, d)
        {
            //format header
            var a = new Date(y, m-1, d);
            a.setDate(a.getDate()-a.getDay());
            var b = new Date(a);
            b.setDate(a.getDate()+6);
            this.start = new Date(a);
            this.end = new Date(b);
            var mn = a.getMonth()+1;
            var el = $('ceventadd').getElementsByTagName('a')[0];
            var c = new Date(a);
            el.onclick=function(){KD.calendar.addEventValue(c.getFullYear(), c.getMonth()+1, c.getDate());}

            $('currdatelabel').innerHTML = ln[a.getMonth()+1] + ' ' + a.getDate() + ', ' + a.getFullYear() + ' - ' +
                ln[b.getMonth()+1] + ' ' + b.getDate() + ', ' + b.getFullYear();
            var hd = document.getElementById("wview").getElementsByTagName('div')[0].getElementsByTagName('div');
            for(var i=0;i<hd.length;i++)
            {
                hd[i].innerHTML = ds[a.getDay()] + ' ' + ln[a.getMonth()+1] + ' ' + a.getDate();
                hd[i].style.left = (100/7*i) + '%';
                a.setDate(a.getDate()+1);
            }
            if(isIEsix)
            {
                var wshell = Ext.query('#wview .daybody')[0];
                var wdata = Ext.query('#wview .data')[0];
                wdata.style.width = (wshell.offsetWidth - 80) + 'px';
                var daymarkers = Ext.query('#wview .wday');
                for(var i=0;i<daymarkers.length;i++)
                {
                    daymarkers[i].style.height = wshell.scrollHeight + 'px';
                }
            }
        }
    };/*wview*/



    /* =============================
     DAY VIEW
     */
    var dview =
        {
        day:null,
        setDate:function(yr,mn,dy)
        {
            y = yr; m = mn; d = dy;
            this.formatDays(yr, mn, dy);
            scal.setDate({y:yr,m:mn,d:dy});
            scal.formatSelected();

            var fdayel = Ext.query('#dview .fullday')[0];
            var tdayel = Ext.query('#dview .daybody')[0];
            fdayel.style.height = ''; // reset before loading

            /* redraw events */
            drawAllEvents(this, y, m, y, m);

            // set it to show all events
            fdayel.style.height = (fdayel.scrollHeight + 1) + 'px';
            tdayel.style.height = (dom.getViewportHeight() - dom.getY('mcal') - fdayel.scrollHeight - 42) + 'px';
        },
        formatDays:function(y, m, d)
        {
            var a = new Date(y, m-1, d);
            this.day = a;
            var lbl = dl[a.getDay()] + ', ' + ln[a.getMonth()+1] + ' ' + a.getDate() + ', ' + a.getFullYear();
            $('currdatelabel').innerHTML = lbl;
            $('dview').childNodes[0].innerHTML = lbl;
            var el = $('ceventadd').getElementsByTagName('a')[0];
            el.onclick=function(){KD.calendar.addEventValue(y,m,d);}
        },
        drawEvents:function(evts, calidx)
        {
            var startScroll = 8;  // start time of earliest event, or 8:00 am
            var r = YAHOO.util.Region; // shortcut
            var w;
            var fd = Ext.query('#dview div.fullday')[0];
            var els = Ext.query('#dview div.data')[0];
            var slots = {};
            for(i in evts)
            {
                var types = evts[i];
                for(var j in types)
                {
                    var event = types[j];
                    if(totalCalendars == 1 && !($('iscalon'+ event.type.id).checked)) continue;
                    var start = Util.makeDate(event.start);
                    var end = Util.makeDate(event.end);
                    var startdate = new Date(start.getFullYear(), start.getMonth(), start.getDate());
                    var enddate = new Date(end.getFullYear(), end.getMonth(), end.getDate(),23,59,59);
                    if(event.allDay)
                    {
                        startdate = startdate;
                        enddate = new Date(end.getFullYear(), end.getMonth(), end.getDate(), 23, 59, 59);
                    }
                    var today = new Date(y,m-1,d);
                    if(!event.allday && startdate.getDate() < today.getDate() && enddate.getDate() >= today.getDate()) startdate = today;
                    if(!event.allday && enddate.getDate() > today.getDate()) enddate = new Date(today.getFullYear(), today.getMonth(), today.getDate(), 23, 59, 59);
                    if(today >= startdate && today <=enddate)
                    {
                        event.calidx = calidx;
                        var div = cal.createEvent(event);
                        if(event.allDay)
                        {
                            div.style.top = fd.childNodes.length * 20 +'px';
                            if(start < today) dom.addClass(div, 'morebefore');
                            if(end.valueOf() > today.valueOf() + 86400000) dom.addClass(div, 'moreafter');
                            fd.appendChild(div);
                            div.style.width = 100 + '%';
                        }else{
                            if(start.valueOf() < today.valueOf()) start = today;
                            if(end.valueOf() > enddate.valueOf()) end = enddate;

                            // set the scroll start
                            if (start.getHours() < startScroll) {
                                startScroll = start.getHours();
                            }
          
                            // offset the top by start minutes
                            var t = 27.5 * start.getHours() * 2;
                            t += 14 * (start.getMinutes()/15);


                            var startmins = start.getHours() * 60 + start.getMinutes();
                            var endmins = end.getHours() * 60 + end.getMinutes();

                            var l = endmins - startmins;
                            var h = (27.5 * 2 * (l/60))-2;
                            if(h<0)h=0;
                            div.style.top = t + 'px';
                            div.style.height =  h + 'px';
                            cal.createLabel(div,h);
                            els.appendChild(div);

                            //reposition the elements
                            var reg = dom.getRegion(div);
                            var ereg, defaultleft;
                            var allevts = Ext.query('#dview .daybody .evtout');
                            w = 100;
                            for(var l=0;l<allevts.length-1;l++)
                            {
                                ereg = dom.getRegion(allevts[l]);
                                if(l==0) defaultleft = ereg.left;
                                if(reg.intersect(ereg) && allevts.length > 1 && div != allevts[l]) {
                                    w /= 1.1;
                                    var intersectedNode = allevts[l];
                                    intersectedNode.style.width = parseInt(intersectedNode.style.width,10) / 1.1 + '%';
                                }
                            }
                            div.style.left = (100/w - 1)*w + '%';
                            div.style.width = w + '%';
                        }
                    }
                } /* for j in types */
            } /* for i in evts */
			
            // scroll to the start of the first event, or 08:00
            startScroll = Util.set2(startScroll) + '00';
            scrollToDefault(startScroll, 'd');

        }
    };/* dview */

    /* scal */
    var scal = {
        view: {y:y,m:m,d:d},
        init:function()
        {
            this.view = {y:y,m:m,d:d};
        },
        change:function(dir)
        {
            var n = cal.changeMonth({year:this.view.y, month:this.view.m, dir:dir});
            this.setDate({y:n.year,m:n.month});
            this.formatSelected();
            return false;
        },
        setDate:function(dt)
        {
            this.view = {y:dt.y,m:dt.m,d:dt.d||this.view.d};
            this.formatDays(this.view.y, this.view.m);
        },
        formatDays:function(yr, mn)
        {
            $('scmonth').innerHTML = ln[mn] + ' ' + yr;

            /* redraw days */
            var dt = new Date(yr, mn-1, 1);
            var wd = dt.getDay();

            var rows = Ext.query('#smallcal tbody tr'); //$('smallcal').getElementsByTagName('tbody')[0].getElementsByTagName('tr');
            dt.setDate(1-wd);

            var td = new Date();
            for(var i=0;i<rows.length;i++)
            {
                if(i > 1)
                {
                    if(dt.getMonth()+1 != mn)
                    {
                        rows[i].style.display = 'none';
                        continue;
                    }else{
                        rows[i].style.display = '';
                    }
                }
                for(var j=0;j<7;j++)
                {
                    var el = rows[i].childNodes[j];
                    el.childNodes[0].innerHTML = dt.getDate();  // first childnode of a cell is the day div
                    el.id = 'smallcal_' + dt.getFullYear() + Util.set2(dt.getMonth()+1) + Util.set2(dt.getDate());
                    if(dt.getMonth()+1 != mn){
                        el.className = 'dim';
                    }else if(dt.getFullYear() == td.getFullYear() && dt.getMonth()+1 == td.getMonth()+1 && dt.getDate() == td.getDate()){
                        el.className = 'curr';
                    }else{
                        el.className = '';
                    }

                    dt.setDate(dt.getDate()+1);
                }
            }
        },
        formatSelected:function()
        {
            var thisd;

            if(this.view.y == y && this.view.m == m)
            {
                var rows = $('smallcal').getElementsByTagName('tbody')[0].getElementsByTagName('tr');
                for(var i=0;i<rows.length;i++)
                {
                    for(var j=0;j<7;j++)
                    {
                        switch(selected)
                        {
                            case 'm':
                                // format all of them
                                dom.addClass( rows[i].childNodes[j], 'sel' );
                                break;
                            case 'w':
                                thisd = rows[i].childNodes[j].id.replace(/[^\d]/g,'');
                                thisd = new Date(thisd.substr(0,4), thisd.substr(4,2)-1, thisd.substr(6,2));

                                if(thisd >= wview.start && thisd <= wview.end )
                                    dom.addClass( rows[i].childNodes[j], 'sel' );
                                // find the row
                                break;
                            case 'd':
                                thisd = rows[i].childNodes[j].id.replace(/[^\d]/g,'');
                                thisd = new Date(thisd.substr(0,4), thisd.substr(4,2)-1, thisd.substr(6,2));
                                if(thisd.toString() == dview.day.toString())
                                {
                                    dom.addClass( rows[i].childNodes[j], 'sel' );
                                }
                                // format just one
                                break;
                            default:
                        }

                    }
                }
            }

        },
        sel:function(t, el)
        {
            loadEvents({}, scal.view.y, scal.view.m, true);
            KD.calendar.sel(t, el);
        }
    };/*scal*/


    /* =============================
       FINAL OBJECT ATTACHMENT
     */
    cal.scal = scal;
    cal.mview = mview;
    cal.wview = wview;
    cal.dview = dview;
    cal.panel = calpanel;
    return cal;
})();

