    /*!
     * Ext JS Library 3.3.1
     * Copyright(c) 2006-2010 Sencha Inc.
     * licensing@sencha.com
     * http://www.sencha.com/license
     */
    /*
     * Internal drag zone implementation for the calendar components. This provides base functionality
     * and is primarily for the month view -- DayViewDD adds day/week view-specific functionality.
     */
    Ext.calendar.DragZone = Ext.extend(Ext.dd.DragZone, {
        ddGroup: 'CalendarDD',
        eventSelector: '.ext-cal-evt',

        constructor: function(el, config) {
            if (!Ext.calendar._statusProxyInstance) {
                Ext.calendar._statusProxyInstance = new Ext.calendar.StatusProxy();
            }
            this.proxy = Ext.calendar._statusProxyInstance;
            Ext.calendar.DragZone.superclass.constructor.call(this, el, config);
        },

        getDragData: function(e) {
            // Check whether we are dragging on an event first
            var t = e.getTarget(this.eventSelector, 3);
            if (t) {
                var rec = this.view.getEventRecordFromEl(t);
                return {
                    type: 'eventdrag',
                    ddel: t,
                    eventStart: rec.data[Ext.calendar.EventMappings.StartDate.name],
                    eventEnd: rec.data[Ext.calendar.EventMappings.EndDate.name],
                    proxy: this.proxy
                };
            }

            // If not dragging an event then we are dragging on
            // the calendar to add a new event
            t = this.view.getDayAt(e.getPageX(), e.getPageY());
            if (t.el) {
                return {
                    type: 'caldrag',
                    start: t.date,
                    proxy: this.proxy
                };
            }
            return null;
        },

        onInitDrag: function(x, y) {
            if (this.dragData.ddel) {
                var ghost = this.dragData.ddel.cloneNode(true),
                child = Ext.fly(ghost).child('dl');

                Ext.fly(ghost).setWidth('auto');

                if (child) {
                    // for IE/Opera
                    child.setHeight('auto');
                }
                this.proxy.update(ghost);
                this.onStartDrag(x, y);
            }
            else if (this.dragData.start) {
                this.onStartDrag(x, y);
            }
            this.view.onInitDrag();
            return true;
        },

        afterRepair: function() {
            if (Ext.enableFx && this.dragData.ddel) {
                Ext.Element.fly(this.dragData.ddel).highlight(this.hlColor || 'c3daf9');
            }
            this.dragging = false;
        },

        getRepairXY: function(e) {
            if (this.dragData.ddel) {
                return Ext.Element.fly(this.dragData.ddel).getXY();
            }
        },

        afterInvalidDrop: function(e, id) {
            Ext.select('.ext-dd-shim').hide();
        }
    });

    /*
     * Internal drop zone implementation for the calendar components. This provides base functionality
     * and is primarily for the month view -- DayViewDD adds day/week view-specific functionality.
     */
    Ext.calendar.DropZone = Ext.extend(Ext.dd.DropZone, {
        ddGroup: 'CalendarDD',
        eventSelector: '.ext-cal-evt',

        // private
        shims: [],

        getTargetFromEvent: function(e) {
            var dragOffset = this.dragOffset || 0,
            y = e.getPageY() - dragOffset,
            d = this.view.getDayAt(e.getPageX(), y);

            return d.el ? d: null;
        },

        onNodeOver: function(n, dd, e, data) {
            var D = Ext.calendar.Date,
            start = data.type == 'eventdrag' ? n.date: D.min(data.start, n.date),
            end = data.type == 'eventdrag' ? n.date.add(Date.DAY, D.diffDays(data.eventStart, data.eventEnd)) :
            D.max(data.start, n.date);

            if (!this.dragStartDate || !this.dragEndDate || (D.diffDays(start, this.dragStartDate) != 0) || (D.diffDays(end, this.dragEndDate) != 0)) {
                this.dragStartDate = start;
                this.dragEndDate = end.clearTime().add(Date.DAY, 1).add(Date.MILLI, -1);
                this.shim(start, end);

                var range = start.format('n/j');
                if (D.diffDays(start, end) > 0) {
                    range += '-' + end.format('n/j');
                }
                var msg = String.format(data.type == 'eventdrag' ? this.moveText: this.createText, range);
                data.proxy.updateMsg(msg);
            }
            return this.dropAllowed;
        },

        shim: function(start, end) {
            this.currWeek = -1;
            var dt = start.clone(),
                i = 0,
                shim,
                box,
                cnt = Ext.calendar.Date.diffDays(dt, end) + 1;

            Ext.each(this.shims,
                function(shim) {
                    if (shim) {
                        shim.isActive = false;
                    }
                }
            );

            while (i++<cnt) {
                var dayEl = this.view.getDayEl(dt);

                // if the date is not in the current view ignore it (this
                // can happen when an event is dragged to the end of the
                // month so that it ends outside the view)
                if (dayEl) {
                    var wk = this.view.getWeekIndex(dt);
                    shim = this.shims[wk];

                    if (!shim) {
                        shim = this.createShim();
                        this.shims[wk] = shim;
                    }
                    if (wk != this.currWeek) {
                        shim.boxInfo = dayEl.getBox();
                        this.currWeek = wk;
                    }
                    else {
                        box = dayEl.getBox();
                        shim.boxInfo.right = box.right;
                        shim.boxInfo.width = box.right - shim.boxInfo.x;
                    }
                    shim.isActive = true;
                }
                dt = dt.add(Date.DAY, 1);
            }

            Ext.each(this.shims,
            function(shim) {
                if (shim) {
                    if (shim.isActive) {
                        shim.show();
                        shim.setBox(shim.boxInfo);
                    }
                    else if (shim.isVisible()) {
                        shim.hide();
                    }
                }
            });
        },

        createShim: function() {
            if (!this.shimCt) {
                this.shimCt = Ext.get('ext-dd-shim-ct');
                if (!this.shimCt) {
                    this.shimCt = document.createElement('div');
                    this.shimCt.id = 'ext-dd-shim-ct';
                    Ext.getBody().appendChild(this.shimCt);
                }
            }
            var el = document.createElement('div');
            el.className = 'ext-dd-shim';
            this.shimCt.appendChild(el);

            return new Ext.Layer({
                shadow: false,
                useDisplay: true,
                constrain: false
            },
            el);
        },

        clearShims: function() {
            Ext.each(this.shims,
            function(shim) {
                if (shim) {
                    shim.hide();
                }
            });
        },

        onContainerOver: function(dd, e, data) {
            return this.dropAllowed;
        },

        onCalendarDragComplete: function() {
            delete this.dragStartDate;
            delete this.dragEndDate;
            this.clearShims();
        },

        onNodeDrop: function(n, dd, e, data) {
            if (n && data) {
                if (data.type == 'eventdrag') {
                    var rec = this.view.getEventRecordFromEl(data.ddel),
                    dt = Ext.calendar.Date.copyTime(rec.data[Ext.calendar.EventMappings.StartDate.name], n.date);

                    this.view.onEventDrop(rec, dt);
                    this.onCalendarDragComplete();
                    return true;
                }
                if (data.type == 'caldrag') {
                    this.view.onCalendarEndDrag(this.dragStartDate, this.dragEndDate,
                    this.onCalendarDragComplete.createDelegate(this));
                    //shims are NOT cleared here -- they stay visible until the handling
                    //code calls the onCalendarDragComplete callback which hides them.
                    return true;
                }
            }
            this.onCalendarDragComplete();
            return false;
        },

        onContainerDrop: function(dd, e, data) {
            this.onCalendarDragComplete();
            return false;
        },

        destroy: function() {
            Ext.calendar.DropZone.superclass.destroy.call(this);
            Ext.destroy(this.shimCt);
        }
    });

    </pre>