Convert to new dragdrop event model
authorMichael M Slusarz <slusarz@curecanti.org>
Wed, 23 Dec 2009 10:42:41 +0000 (03:42 -0700)
committerMichael M Slusarz <slusarz@curecanti.org>
Wed, 23 Dec 2009 19:28:44 +0000 (12:28 -0700)
I have no idea if I converted the kronolith drag/drop code
correctly. IMHO, javascript closures are a bad idea (for anything other
than small function blocks with small #'s of vars) for this
reason - it is next to impossible to determine where any given variable
is being set/altered.

imp/js/DimpBase.js
imp/js/ViewPort.js
kronolith/js/kronolith.js

index 557ac17..abc0e1d 100644 (file)
@@ -1521,9 +1521,11 @@ var DimpBase = {
     },
 
     /* Drag/Drop handler. */
-    _folderDropHandler: function(drop, drag, e)
+    folderDropHandler: function(e)
     {
         var dropbase, sel, uids,
+            drag = e.memo,
+            drop = e.element(),
             foldername = drop.retrieve('mbox'),
             ftype = drop.retrieve('ftype');
 
@@ -1562,6 +1564,74 @@ var DimpBase = {
         return cnt + ' ' + (cnt == 1 ? DIMP.text.message : DIMP.text.messages);
     },
 
+    onDragStart: function(e)
+    {
+        var args,
+            elt = e.element(),
+            id = elt.identify(),
+            d = DragDrop.Drags.getDrag(id);
+
+        if (elt.hasClassName('vpRow')) {
+            args = { right: e.memo.isRightClick() };
+            d.selectIfNoDrag = false;
+
+            // Handle selection first.
+            if (DimpCore.DMenu.operaCheck(e)) {
+                if (!this.isSelected('domid', id)) {
+                    this.msgSelect(id, { right: true });
+                }
+            } else if (!args.right && (e.memo.ctrlKey || e.memo.metaKey)) {
+                this.msgSelect(id, $H({ ctrl: true }).merge(args).toObject());
+            } else if (e.memo.shiftKey) {
+                this.msgSelect(id, $H({ shift: true }).merge(args).toObject());
+            } else if (e.memo.element().hasClassName('msCheck')) {
+                this.msgSelect(id, { ctrl: true, right: true });
+            } else if (this.isSelected('domid', id)) {
+                if (!args.right && this.selectedCount()) {
+                    d.selectIfNoDrag = true;
+                }
+            } else {
+                this.msgSelect(id, args);
+            }
+        } else if (elt.hasClassName('folder')) {
+            d.opera = DimpCore.DMenu.operaCheck(e);
+        }
+    },
+
+    onDrag: function(e)
+    {
+        if (e.element().hasClassName('folder')) {
+            var d = e.memo;
+            if (!d.opera && !d.wasDragged) {
+                $('folderopts').hide();
+                $('dropbase').show();
+                d.ghost.removeClassName('on');
+            }
+        }
+    },
+
+    onDragEnd: function(e)
+    {
+        var elt = e.element(),
+            id = elt.identify(),
+            d = DragDrop.Drags.getDrag(id);
+
+        if (elt.hasClassName('vpRow')) {
+            if (d.selectIfNoDrag && !d.wasDragged) {
+                this.msgSelect(id, { right: e.memo.isRightClick() });
+            }
+        } else if (elt.hasClassName('folder')) {
+            if (!d.opera && d.wasDragged) {
+                $('folderopts').show();
+                $('dropbase').hide();
+            }
+        } else if (elt.hasClassName('splitBarVertSidebar')) {
+            $('sidebar').setStyle({ width: d.lastCoord[0] + 'px' });
+            elt.setStyle({ left: $('sidebar').clientWidth + 'px' });
+            $('dimpmain').setStyle({ left: ($('sidebar').clientWidth + elt.clientWidth) + 'px' });
+        }
+    },
+
     /* Keydown event handler */
     keydownHandler: function(e)
     {
@@ -2790,12 +2860,7 @@ var DimpBase = {
         new Drag(this.splitbar, {
             constraint: 'horizontal',
             ghosting: true,
-            nodrop: true,
-            onEnd: function(drag) {
-                $('sidebar').setStyle({ width: drag.lastCoord[0] + 'px' });
-                drag.element.setStyle({ left: $('sidebar').clientWidth + 'px' });
-                $('dimpmain').setStyle({ left: ($('sidebar').clientWidth + drag.element.clientWidth) + 'px' });
-            }
+            nodrop: true
         });
 
         $('dimpmain').setStyle({ left: ($('sidebar').clientWidth + this.splitbar.clientWidth) + 'px' });
@@ -2945,37 +3010,7 @@ DimpBase._msgDragConfig = {
     classname: 'msgdrag',
     scroll: 'normalfolders',
     threshold: 5,
-    caption: DimpBase.dragCaption.bind(DimpBase),
-    onStart: function(d, e) {
-        var args = { right: e.isRightClick() },
-            id = d.element.id;
-
-        d.selectIfNoDrag = false;
-
-        // Handle selection first.
-        if (DimpCore.DMenu.operaCheck(e)) {
-            if (!DimpBase.isSelected('domid', id)) {
-                DimpBase.msgSelect(id, { right: true });
-            }
-        } else if (!args.right && (e.ctrlKey || e.metaKey)) {
-            DimpBase.msgSelect(id, $H({ ctrl: true }).merge(args).toObject());
-        } else if (e.shiftKey) {
-            DimpBase.msgSelect(id, $H({ shift: true }).merge(args).toObject());
-        } else if (e.element().hasClassName('msCheck')) {
-            DimpBase.msgSelect(id, { ctrl: true, right: true });
-        } else if (DimpBase.isSelected('domid', id)) {
-            if (!args.right && DimpBase.selectedCount()) {
-                d.selectIfNoDrag = true;
-            }
-        } else {
-            DimpBase.msgSelect(id, args);
-        }
-    },
-    onEnd: function(d, e) {
-        if (d.selectIfNoDrag && !d.wasDragged) {
-            DimpBase.msgSelect(d.element.id, { right: e.isRightClick() });
-        }
-    }
+    caption: DimpBase.dragCaption.bind(DimpBase)
 };
 
 DimpBase._folderDragConfig = {
@@ -2983,23 +3018,7 @@ DimpBase._folderDragConfig = {
     ghosting: true,
     offset: { x: 15, y: 0 },
     scroll: 'normalfolders',
-    threshold: 5,
-    onStart: function(d, e) {
-        d.opera = DimpCore.DMenu.operaCheck(e);
-    },
-    onDrag: function(d, e) {
-        if (!d.opera && !d.wasDragged) {
-            $('folderopts').hide();
-            $('dropbase').show();
-            d.ghost.removeClassName('on');
-        }
-    },
-    onEnd: function(d, e) {
-        if (!d.opera && d.wasDragged) {
-            $('folderopts').show();
-            $('dropbase').hide();
-        }
-    }
+    threshold: 5
 };
 
 DimpBase._folderDropConfig = {
@@ -3039,10 +3058,15 @@ DimpBase._folderDropConfig = {
             }
         }
     },
-    keypress: true,
-    onDrop: DimpBase._folderDropHandler.bind(DimpBase)
+    keypress: true
 };
 
+/* Drag/drop listeners. */
+document.observe('DragDrop2:drag', DimpBase.onDrag.bindAsEventListener(DimpBase));
+document.observe('DragDrop2:drop', DimpBase.folderDropHandler.bindAsEventListener(DimpBase));
+document.observe('DragDrop2:end', DimpBase.onDragEnd.bindAsEventListener(DimpBase));
+document.observe('DragDrop2:start', DimpBase.onDragStart.bindAsEventListener(DimpBase));
+
 DimpCore.onDoActionComplete = function(r) {
     DimpBase.deleteCallback(r);
     if (DimpBase.viewport) {
index 1c2a074..d152a5d 100644 (file)
@@ -240,9 +240,11 @@ var ViewPort = Class.create({
 
         this.split_pane = {
             curr: null,
+            currbar: null,
             horiz: {
                 loc: opts.page_size
             },
+            init: false,
             vert: {
                 width: opts.pane_width
             }
@@ -1087,44 +1089,18 @@ var ViewPort = Class.create({
                 constraint: 'vertical',
                 ghosting: true,
                 nodrop: true,
-                onStart: function() {
-                    // Cache these values since we will be using them multiple
-                    // times in snap().
-                    this.sp = {
-                        lh: this._getLineHeight(),
-                        lines: this.page_size,
-                        max: this.getPageSize('splitmax'),
-                        orig: this.page_size,
-                        pos: this.opts.content.positionedOffset()[1]
-                    };
-                    this.opts.container.fire('ViewPort:splitBarStart', 'horiz');
-                }.bind(this),
                 snap: function(x, y, elt) {
-                    var l = parseInt((y - this.sp.pos) / this.sp.lh);
+                    var sp = this.split_pane,
+                        l = parseInt((y - sp.pos) / sp.lh);
                     if (l < 1) {
                         l = 1;
-                    } else if (l > this.sp.max) {
-                        l = this.sp.max;
-                    }
-                    this.sp.lines = l;
-                    return [ x, this.sp.pos + (l * this.sp.lh) ];
-                }.bind(this),
-                onEnd: function() {
-                    this.onResize(true, this.sp.lines);
-                    if (this.sp.orig != this.sp.lines) {
-                        this.opts.container.fire('ViewPort:splitBarChange', 'horiz');
+                    } else if (l > sp.max) {
+                        l = sp.max;
                     }
-                    this.opts.container.fire('ViewPort:splitBarEnd', 'horiz');
+                    sp.lines = l;
+                    return [ x, sp.pos + (l * sp.lh) ];
                 }.bind(this)
             });
-
-            sp.currbar.observe('dblclick', function() {
-                var old_size = this.page_size;
-                this.onResize(true, this.getPageSize('default'));
-                if (old_size != this.page_size) {
-                    this.opts.container.fire('ViewPort:splitBarChange', 'horiz');
-                }
-            }.bind(this));
             break;
 
         case 'vert':
@@ -1132,25 +1108,90 @@ var ViewPort = Class.create({
                 constraint: 'horizontal',
                 ghosting: true,
                 nodrop: true,
-                snapToParent: true,
-                onStart: function(drag) {
-                    this.opts.container.fire('ViewPort:splitBarStart', 'vert');
-                }.bind(this),
-                onEnd: function(drag) {
-                    sp.vert.width = drag.lastCoord[0];
-                    this.opts.content.setStyle({ width: sp.vert.width + 'px' });
-                    if (drag.wasDragged) {
-                        this.opts.container.fire('ViewPort:splitBarChange', 'vert');
-                    }
-                    this.opts.container.fire('ViewPort:splitBarEnd', 'vert');
-                }.bind(this)
+                snapToParent: true
             });
+            break;
+        }
+
+        if (!sp.init) {
+            document.observe('DragDrop2:end', this._onDragEnd.bindAsEventListener(this));
+            document.observe('DragDrop2:start', this._onDragStart.bindAsEventListener(this));
+            document.observe('dblclick', this._onDragDblClick.bindAsEventListener(this));
+            sp.init = true;
+        }
+    },
+
+    _onDragStart: function(e)
+    {
+        var sp = this.split_pane;
+
+        if (e.element() != sp.currbar) {
+            return;
+        }
+
+        if (this.pane_mode == 'horiz') {
+            // Cache these values since we will be using them multiple
+            // times in snap().
+            sp.lh = this._getLineHeight();
+            sp.lines = this.page_size;
+            sp.max = this.getPageSize('splitmax');
+            sp.orig = this.page_size;
+            sp.pos = this.opts.content.positionedOffset()[1];
+        }
+
+        this.opts.container.fire('ViewPort:splitBarStart', this.pane_mode);
+    },
+
+    _onDragEnd: function(e)
+    {
+        var change, drag,
+            sp = this.split_pane;
 
-            sp.currbar.observe('dblclick', function() {
-                this.opts.content.setStyle({ width: parseInt(this.opts.container.clientWidth * 0.45, 10) + 'px' });
-                this.opts.container.fire('ViewPort:splitBarChange', 'vert');
-            }.bind(this));
+        if (e.element() != sp.currbar) {
+            return;
+        }
+
+        switch (this.pane_mode) {
+        case 'horiz':
+            this.onResize(true, sp.lines);
+            change = (sp.orig != sp.lines);
             break;
+
+        case 'vert':
+            drag = DropDrag.Drags.get(e.element());
+            sp.vert.width = drag.lastCoord[0];
+            this.opts.content.setStyle({ width: sp.vert.width + 'px' });
+            change = drag.wasDragged;
+            break;
+        }
+
+        if (change) {
+            this.opts.container.fire('ViewPort:splitBarChange', this.pane_mode);
+        }
+        this.opts.container.fire('ViewPort:splitBarEnd', this.pane_mode);
+    },
+
+    _onDragDblClick: function(e)
+    {
+        if (e.element() != this.split_pane.currbar) {
+            return;
+        }
+
+        var change, old_size = this.page_size;
+
+        switch (this.pane_mode) {
+        case 'horiz':
+            this.onResize(true, this.getPageSize('default'));
+            change = (old_size != this.page_size);
+            break;
+
+        case 'vert':
+            this.opts.content.setStyle({ width: parseInt(this.opts.container.clientWidth * 0.45, 10) + 'px' });
+            change = true;
+        }
+
+        if (change) {
+            this.opts.container.fire('ViewPort:splitBarChange', this.pane_mode);
         }
     },
 
index f1a2a33..56806d4 100644 (file)
@@ -16,7 +16,7 @@ var frames = { horde_main: true },
 KronolithCore = {
     // Vars used and defaulting to null/false:
     //   DMenu, Growler, inAjaxCallback, is_logout, onDoActionComplete,
-    //   daySizes, viewLoading, freeBusy
+    //   daySizes, viewLoading, freeBusy, dragvars
 
     view: '',
     ecache: $H(),
@@ -592,29 +592,7 @@ KronolithCore = {
             if (dateString == today) {
                 cell.addClassName('kronolithToday');
             }
-            new Drop(cell, { onDrop: function(drop) {
-                var el = DragDrop.Drags.drag.element,
-                    eventid = el.retrieve('eventid'),
-                    cal = el.retrieve('calendar');
-                if (drop == el.parentNode) {
-                    return;
-                }
-                drop.insert(el);
-                this.startLoading(cal, start + end);
-                this.doAction('UpdateEvent',
-                              { 'cal': cal,
-                                'id': eventid,
-                                'view': this.view,
-                                'view_start': start,
-                                'view_end': end,
-                                'att': $H({ start_date: drop.retrieve('date') }).toJSON() },
-                              function(r) {
-                                  if (r.response.events) {
-                                      this._removeEvent(eventid, cal);
-                                  }
-                                  this._loadEventsCallback(r);
-                              }.bind(this));
-            }.bind(this) });
+            new Drop(cell);
             cell.store('date', dateString)
                 .down('.kronolithDay')
                 .store('date', dateString)
@@ -1260,7 +1238,7 @@ KronolithCore = {
 
             if (event.value.pe) {
                 div.addClassName('kronolithEditable');
-                    // Number of pixels that cover 10 minutes.
+                // Number of pixels that cover 10 minutes.
                 var step = this[storage].height / 6;
                 if (draggerTop) {
                     // Top position of top dragger
@@ -1302,50 +1280,18 @@ KronolithCore = {
                     maxDiv = 24 * this[storage].height - divHeight,
                     // Whether the top dragger is dragged, vs. the bottom
                     // dragger
-                    draggingTop,
                     opts = {
-                        'threshold': 5,
-                        'constraint': 'vertical',
-                        'scroll': 'kronolithBody',
-                        'nodrop': true,
-                        'parentElement': function() {
+                        threshold: 5,
+                        constraint: 'vertical',
+                        scroll: 'kronolithBody',
+                        nodrop: true,
+                        parentElement: function() {
                             return $(view == 'day' ? 'kronolithEventsDay' : 'kronolithEventsWeek' + date);
-                        },
-                        'onStart': function(d, e) {
-                            this.addClassName('kronolithSelected');
-                        }.bind(div),
-                        'onEnd': function(d, e) {
-                            this[0]._onDragEnd(d, this[1], innerDiv, event, midnight, view, step, draggingTop);
-                        }.bind([this, div]),
-                        'onDrag': function(d, e) {
-                            var div = this[1],
-                                top = d.ghost.cumulativeOffset().top,
-                                offset, height, dates;
-                            draggingTop = d.ghost.hasClassName('kronolithDraggerTop');
-                            if (draggingTop) {
-                                offset = top - dragTop;
-                                height = div.offsetHeight - offset;
-                                div.setStyle({
-                                    'top': (div.offsetTop + offset) + 'px'
-                                });
-                                offset = d.ghost.offsetTop;
-                                dragTop = top;
-                            } else {
-                                offset = top - dragBottom;
-                                height = div.offsetHeight + offset;
-                                offset = div.offsetTop;
-                                dragBottom = top;
-                            }
-                            div.setStyle({
-                                'height': height + 'px'
-                            });
-                            this[0]._calculateEventDates(event.value, storage, step, offset, height);
-                            innerDiv.update('(' + event.value.start.toString(Kronolith.conf.time_format) + ' - ' + event.value.end.toString(Kronolith.conf.time_format) + ') ' + event.value.t.escapeHTML());
-                        }.bind([this, div])
+                        }
                     };
 
                 if (draggerTop) {
-                    opts.snap = function(x, y, elm) {
+                    opts.snap = function(x, y) {
                         y = Math.max(0, step * (Math.min(maxTop, y) / step | 0));
                         return [0, y];
                     }
@@ -1353,7 +1299,7 @@ KronolithCore = {
                 }
 
                 if (draggerBottom) {
-                    opts.snap = function(x, y, elm) {
+                    opts.snap = function(x, y) {
                         y = Math.min(maxBottom + dragBottomHeight + KronolithCore[storage].spacing, step * ((Math.max(minBottom, y) + dragBottomHeight + KronolithCore[storage].spacing) / step | 0)) - dragBottomHeight - KronolithCore[storage].spacing;
                         return [0, y];
                     }
@@ -1362,50 +1308,34 @@ KronolithCore = {
 
                 if (view == 'week') {
                     var dates = this.viewDates(midnight, view),
-                        eventStart = event.value.start.clone(),
-                        eventEnd = event.value.end.clone(),
                         minLeft = $('kronolithEventsWeek' + dates[0].toString('yyyyMMdd')).offsetLeft - $('kronolithEventsWeek' + date).offsetLeft,
                         maxLeft = $('kronolithEventsWeek' + dates[1].toString('yyyyMMdd')).offsetLeft - $('kronolithEventsWeek' + date).offsetLeft,
                         stepX = (maxLeft - minLeft) / 6;
                 }
-                var startTop = div.offsetTop;
                 new Drag(div, {
-                    'threshold': 5,
-                    'nodrop': true,
-                    'parentElement': function() { return $(view == 'day' ? 'kronolithEventsDay' : 'kronolithEventsWeek' + date); },
-                    'snap': function(x, y, elm) {
-                        if (view == 'week') {
-                            x = Math.max(minLeft, stepX * ((Math.min(maxLeft, x) + stepX / 2) / stepX | 0));
-                        } else {
-                            x = 0;
-                        }
+                    threshold: 5,
+                    nodrop: true,
+                    parentElement: function() { return $(view == 'day' ? 'kronolithEventsDay' : 'kronolithEventsWeek' + date); },
+                    snap: function(x, y) {
+                        x = (view == 'week')
+                            ? Math.max(minLeft, stepX * ((Math.min(maxLeft, x) + stepX / 2) / stepX | 0))
+                            : 0;
                         y = Math.max(0, step * (Math.min(maxDiv, y) / step | 0));
                         return [x, y];
-                    },
-                    'onStart': function(d, e) {
-                        this.addClassName('kronolithSelected');
-                        this.setStyle({ 'left': 0, 'width': '100%', 'zIndex': 1 });
-                    }.bind(div),
-                    'onDrag': function(d, e) {
-                        if (Object.isUndefined(d.innerDiv)) {
-                            d.innerDiv = d.ghost.select('.kronolithEventInfo')[0];
-                        }
-                        if (view == 'week') {
-                            var offsetX = Math.round(d.ghost.offsetLeft / stepX);
-                            event.value.offsetDays = offsetX;
-                            this[0]._calculateEventDates(event.value, storage, step, d.ghost.offsetTop, divHeight, eventStart.clone().addDays(offsetX), eventEnd.clone().addDays(offsetX));
-                        } else {
-                            event.value.offsetDays = 0;
-                            this[0]._calculateEventDates(event.value, storage, step, d.ghost.offsetTop, divHeight);
-                        }
-                        event.value.offsetTop = d.ghost.offsetTop - startTop;
-                        d.innerDiv.update('(' + event.value.start.toString(Kronolith.conf.time_format) + ' - ' + event.value.end.toString(Kronolith.conf.time_format) + ') ' + event.value.t.escapeHTML());
-                        this[1].clonePosition(d.ghost);
-                    }.bind([this, div]),
-                    'onEnd': function(d, e) {
-                        this[0]._onDragEnd(d, this[1], innerDiv, event, midnight, view, step);
-                    }.bind([this, div])
+                    }
                 });
+
+                // Hack - the drag event callbacks need access to these
+                //        variables.
+                this.dragvars = {
+                    divHeight: divHeight,
+                    event: event.value,
+                    innerDiv: innerDiv,
+                    midnight: midnight,
+                    step: step,
+                    stepX: stepX,
+                    storage: storage
+                };
             }
 
             var column = 1, columns, width, left, conflict = false,
@@ -1555,49 +1485,6 @@ KronolithCore = {
     },
 
     /**
-     * Called as the event handler after dragging/resizing a day/week event.
-     */
-    _onDragEnd: function(drag, div, innerDiv, event, date, view, step, top)
-    {
-        var dates = this.viewDates(date, view),
-            start = dates[0].toString('yyyyMMdd'),
-            end = dates[1].toString('yyyyMMdd'),
-            attributes;
-        div.removeClassName('kronolithSelected');
-        this._setEventText(innerDiv, event.value);
-        drag.destroy();
-        this.startLoading(event.value.calendar, start + end);
-        if (!Object.isUndefined(event.value.offsetTop)) {
-            attributes = $H({ 'offDays': event.value.offsetDays,
-                              'offMins': event.value.offsetTop / step * 10 });
-        } else if (!Object.isUndefined(top)) {
-            if (top) {
-                attributes = $H({ 'start': event.value.start });
-            } else {
-                attributes = $H({ 'end': event.value.end });
-            }
-        } else {
-            attributes = $H({ 'start': event.value.start,
-                              'end': event.value.end });
-        }
-        this.doAction(
-            'UpdateEvent',
-            { 'cal': event.value.calendar,
-              'id': event.key,
-              'view': view,
-              'view_start': start,
-              'view_end': end,
-              'att': attributes.toJSON()
-            },
-            function(r) {
-                if (r.response.events) {
-                    this._removeEvent(event.key, event.value.calendar);
-                }
-                this._loadEventsCallback(r);
-            }.bind(this));
-    },
-
-    /**
      * Returns the task cache storage names that hold the tasks of the
      * requested task type.
      *
@@ -3109,6 +2996,144 @@ KronolithCore = {
         */
     },
 
+    onDrop: function(e)
+    {
+        var drop = e.element(),
+            el = e.memo,
+            eventid = el.retrieve('eventid'),
+            cal = el.retrieve('calendar');
+
+        if (drop == el.parentNode) {
+            return;
+        }
+
+        drop.insert(el);
+        this.startLoading(cal, start + end);
+        this.doAction('UpdateEvent',
+                      { 'cal': cal,
+                        'id': eventid,
+                        'view': this.view,
+                        'view_start': start,
+                        'view_end': end,
+                        'att': $H({ start_date: drop.retrieve('date') }).toJSON() },
+                      function(r) {
+                          if (r.response.events) {
+                              this._removeEvent(eventid, cal);
+                          }
+                          this._loadEventsCallback(r);
+                      }.bind(this));
+    },
+
+    onDragStart: function(e)
+    {
+        var elt = e.element();
+
+        if (elt.hasClassName('kronolithDragger')) {
+            elt = elt.up().addClassName('kronolithSelected');
+        } else if (elt.hasClassName('kronolithEditable')) {
+            elt.addClassName('kronolithSelected').setStyle({ 'left': 0, 'width': '100%', 'zIndex': 1 });
+        }
+    },
+
+    onDrag: function(e)
+    {
+        var elt = e.element(),
+            d = DragDrop.Drags.getDrag(elt);
+
+        if (elt.hasClassName('kronolithDragger')) {
+            var div = elt.up(),
+                top = d.ghost.cumulativeOffset().top,
+                offset, height, dates;
+
+            if (elt.hasClassName('kronolithDraggerTop')) {
+                offset = top - dragTop;
+                height = div.offsetHeight - offset;
+                div.setStyle({
+                    'top': (div.offsetTop + offset) + 'px'
+                });
+                offset = d.ghost.offsetTop;
+                dragTop = top;
+            } else {
+                offset = top - dragBottom;
+                height = div.offsetHeight + offset;
+                offset = div.offsetTop;
+                dragBottom = top;
+            }
+            div.setStyle({
+                'height': height + 'px'
+            });
+
+            this._calculateEventDates(this.dragvars.event.value, this.dragvars.storage, this.dragvars.step, offset, height);
+            innerDiv.update('(' + this.dragvars.event.value.start.toString(Kronolith.conf.time_format) + ' - ' + this.dragvars.event.value.end.toString(Kronolith.conf.time_format) + ') ' + this.dragvars.event.value.t.escapeHTML());
+        } else if (elt.hasClassName('kronolithEditable')) {
+            if (Object.isUndefined(d.innerDiv)) {
+                d.innerDiv = d.ghost.select('.kronolithEventInfo')[0];
+            }
+            if (view == 'week') {
+                var offsetX = Math.round(d.ghost.offsetLeft / this.dragvars.stepX);
+                this.dragvars.event.value.offsetDays = offsetX;
+                this._calculateEventDates(this.dragvars.event.value, this.dragvars.storage, this.dragvars.step, d.ghost.offsetTop, this.dragvars.divHeight, this.dragvars.event.value.start.clone().addDays(offsetX), this.dragvars.event.value.end.clone().addDays(offsetX));
+            } else {
+                this.dragvars.event.value.offsetDays = 0;
+                this._calculateEventDates(this.dragvars.event.value, this.dragvars.storage, this.dragvars.step, d.ghost.offsetTop, this.dragvars.divHeight);
+            }
+            this.dragvars.event.value.offsetTop = d.ghost.offsetTop - div.offsetTop;
+            d.innerDiv.update('(' + this.dragvars.event.value.start.toString(Kronolith.conf.time_format) + ' - ' + this.dragvars.event.value.end.toString(Kronolith.conf.time_format) + ') ' + this.dragvars.event.value.t.escapeHTML());
+            elt.clonePosition(d.ghost);
+        }
+    },
+
+    onDragEnd: function(e)
+    {
+        if (!e.element().hasClassName('kronolithDragger') &&
+            !e.element().hasClassName('kronolithEditable')) {
+            return;
+        }
+
+        var div = e.element(),
+            drag = DragDrop.Drags.getDrag(div),
+            innerDiv = this.dragvars.innerDiv,
+            event = this.dragvars.event,
+            date = this.dragvars.midnight,
+            view = this.dragvars.view,
+            step = this.dragvars.step,
+            dates = this.viewDates(date, view),
+            start = dates[0].toString('yyyyMMdd'),
+            end = dates[1].toString('yyyyMMdd'),
+            attributes;
+
+        div.removeClassName('kronolithSelected');
+        this._setEventText(innerDiv, event.value);
+        drag.destroy();
+        this.startLoading(event.value.calendar, start + end);
+        if (!Object.isUndefined(event.value.offsetTop)) {
+            attributes = $H({ 'offDays': event.value.offsetDays,
+                              'offMins': event.value.offsetTop / step * 10 });
+        } else if (div.hasClassName('kronolithDraggerTop')) {
+            attributes = $H({ 'start': event.value.start });
+        } else if (div.hasClassName('kronolithDraggerBottom')) {
+            attributes = $H({ 'end': event.value.end });
+        } else {
+            attributes = $H({ 'start': event.value.start,
+                              'end': event.value.end });
+        }
+        this.doAction(
+            'UpdateEvent',
+            { 'cal': event.value.calendar,
+              'id': event.key,
+              'view': view,
+              'view_start': start,
+              'view_end': end,
+              'att': attributes.toJSON()
+            },
+            function(r) {
+                if (r.response.events) {
+                    this._removeEvent(event.key, event.value.calendar);
+                }
+                this._loadEventsCallback(r);
+            }.bind(this));
+    },
+
     editEvent: function(calendar, id, date)
     {
         if (Object.isUndefined($('kronolithEventTags').autocompleter)) {
@@ -3767,3 +3792,7 @@ KronolithCore = {
 /* Initialize global event handlers. */
 document.observe('dom:loaded', KronolithCore.onDomLoad.bind(KronolithCore));
 Event.observe(window, 'resize', KronolithCore.onResize.bind(KronolithCore));
+document.observe('DragDrop2:drag', KronolithCore.onDrag.bindAsEventListener(KronolithCore));
+document.observe('DragDrop2:drop', KronolithCore.onDrop.bindAsEventListener(KronolithCore));
+document.observe('DragDrop2:end', KronolithCore.onDragEnd.bindAsEventListener(KronolithCore));
+document.observe('DragDrop2:start', KronolithCore.onDragStart.bindAsEventListener(KronolithCore));