Use custom events where possible to replace callbacks
authorMichael M Slusarz <slusarz@curecanti.org>
Wed, 16 Dec 2009 05:39:33 +0000 (22:39 -0700)
committerMichael M Slusarz <slusarz@curecanti.org>
Wed, 16 Dec 2009 05:41:40 +0000 (22:41 -0700)
imp/js/ContextSensitive.js
imp/js/DimpBase.js
imp/js/DimpCore.js
imp/js/DimpSlider.js
imp/js/ViewPort.js
imp/js/fullmessage-dimp.js

index 5cc2edf..b2373cd 100644 (file)
@@ -9,6 +9,25 @@
  *
  * Requires prototypejs 1.6+ and scriptaculous 1.8+ (effects.js only).
  *
+ *
+ * Usage:
+ * ------
+ * cs = new ContextSensitive();
+ *
+ * Custom Events:
+ * --------------
+ * Custom events are triggered on the document element. The parameters given
+ * below are available through the 'memo' property of the Event object.
+ *
+ * ContextSensitive:click
+ *   Fired on TODO
+ *   params: (object) TODO [base, elt, trigger]
+ *
+ * ContextSensitive:show
+ *   Fired on TODO
+ *   params: (object) TODO [base, id]
+ *
+ *
  * Original code by Havard Eide (http://eide.org/) released under the MIT
  * license.
  *
 
 var ContextSensitive = Class.create({
 
-    initialize: function(opts)
+    initialize: function()
     {
         this.baseelt = null;
         this.current = [];
         this.elements = $H();
-        this.opts = opts || {};
         this.submenus = $H();
         this.triggers = [];
 
@@ -173,9 +191,7 @@ var ContextSensitive = Class.create({
                     base = this.baseelt;
                     trigger = this.triggers.last();
                     this.close();
-                    if (this.opts.onClick) {
-                        this.opts.onClick(elt, base, trigger);
-                    }
+                    document.fire('ContextSensitive:click', { base: base, elt: elt, trigger: trigger });
                 }
                 return;
             }
@@ -278,9 +294,7 @@ var ContextSensitive = Class.create({
                 : (v.width - size.width - 10);
         }
 
-        if (this.opts.onShow) {
-            this.opts.onShow(id, this.baseelt);
-        }
+        document.fire('ContextSensitive:show', { base: this.baseelt, id: id });
 
         elt.setStyle({ left: x + 'px', top: y + 'px' })
 
index b9cd7b3..336c1f8 100644 (file)
@@ -29,48 +29,6 @@ var DimpBase = {
 
     // Message selection functions
 
-    // vs = (ViewPort_Selection) A ViewPort_Selection object.
-    // opts = (object) Boolean options [delay, right]
-    _select: function(vs, opts)
-    {
-        var d = vs.get('rownum');
-        if (d.size() == 1) {
-            this.lastrow = this.pivotrow = d.first();
-        }
-
-        this.toggleButtons();
-
-        if (DIMP.conf.preview_pref) {
-            if (opts.right) {
-                this.clearPreviewPane();
-            } else {
-                if (opts.delay) {
-                    this.initPreviewPane.bind(this).delay(opts.delay);
-                } else {
-                    this.initPreviewPane();
-                }
-            }
-        }
-    },
-
-    // vs = (ViewPort_Selection) A ViewPort_Selection object.
-    // opts = (object) Boolean options [right]
-    _deselect: function(vs, opts)
-    {
-        var sel = this.viewport.getSelected(),
-            count = sel.size();
-        if (!count) {
-            this.lastrow = this.pivotrow = -1;
-        }
-
-        this.toggleButtons();
-        if (opts.right || !count) {
-            this.clearPreviewPane();
-        } else if ((count == 1) && DIMP.conf.preview_pref) {
-            this.loadPreview(sel.get('dataob').first());
-        }
-    },
-
     // id = (string) DOM ID
     // opts = (Object) Boolean options [ctrl, right, shift]
     msgSelect: function(id, opts)
@@ -384,6 +342,8 @@ var DimpBase = {
 
     _createViewPort: function()
     {
+        var container = $('msgSplitPane');
+
         [ $('msglistHeader') ].invoke(DIMP.conf.preview_pref == 'vert' ? 'hide' : 'show');
 
         this.template = {
@@ -394,7 +354,7 @@ var DimpBase = {
         this.viewport = new ViewPort({
             // Mandatory config
             ajax_url: DIMP.conf.URI_AJAX + '/ViewPort',
-            container: 'msgSplitPane',
+            container: container,
             onContent: function(r, mode) {
                 var bg, re, u,
                     thread = $H(this.viewport.getMetaData('thread')),
@@ -493,6 +453,12 @@ var DimpBase = {
             wait: DIMP.conf.viewport_wait,
 
             // Callbacks
+            onAjaxFailure: function() {
+                if ($('dimpmain_folder').visible()) {
+                    DimpCore.showNotifications([ { type: 'horde.error', message: DIMP.text.listmsg_timeout } ]);
+                }
+                this.loadingImg('viewport', false);
+            }.bind(this),
             onAjaxRequest: function(id) {
                 var p = $H();
                 if (this.folderswitch && this.isSearch(id, true)) {
@@ -524,120 +490,155 @@ var DimpBase = {
                 }
                 return this.cacheids[id];
             }.bind(this),
-            onCacheUpdate: function(id) {
-                delete this.cacheids[id];
-            }.bind(this),
-            onClear: function(r) {
-                r.each(this._removeMouseEvents.bind(this));
-            }.bind(this),
-            onContentComplete: function(rows) {
-                var row, ssc, tmp,
-                    l = this.viewport.getMetaData('label');
+            onSlide: this.setMessageListTitle.bind(this)
+        });
+
+        /* Custom ViewPort events. */
+        container.observe('ViewPort:cacheUpdate', function(e) {
+            delete this.cacheids[e.memo];
+        }.bindAsEventListener(this));
+
+        container.observe('ViewPort:clear', function(e) {
+            e.memo.each(this._removeMouseEvents.bind(this));
+        }.bindAsEventListener(this));
+
+        container.observe('ViewPort:contentComplete', function(e) {
+            var row, ssc, tmp,
+                l = this.viewport.getMetaData('label');
 
-                this.setMessageListTitle();
-                if (!this.isSearch()) {
-                    this.setFolderLabel(this.folder, this.viewport.getMetaData('unseen') || 0);
+            this.setMessageListTitle();
+            if (!this.isSearch()) {
+                this.setFolderLabel(this.folder, this.viewport.getMetaData('unseen') || 0);
+            }
+            this.updateTitle();
+
+            e.memo.each(function(row) {
+                // Add context menu
+                this._addMouseEvents({ id: row.domid, type: row.menutype });
+                new Drag(row.domid, this._msgDragConfig);
+            }, this);
+
+            if (this.uid) {
+                row = this.viewport.getSelection().search({ imapuid: { equal: [ this.uid ] }, view: { equal: [ this.folder ] } });
+                if (row.size()) {
+                    this.viewport.scrollTo(row.get('rownum').first());
+                    this.viewport.select(row);
                 }
-                this.updateTitle();
+            } else if (this.offset) {
+                this.viewport.select(this.viewport.createSelection('rownum', this.offset));
+            }
+            this.offset = this.uid = null;
 
-                rows.each(function(row) {
-                    // Add context menu
-                    this._addMouseEvents({ id: row.domid, type: row.menutype });
-                    new Drag(row.domid, this._msgDragConfig);
-                }, this);
+            // 'label' will not be set if there has been an error
+            // retrieving data from the server.
+            l = this.viewport.getMetaData('label');
+            if (l) {
+                if (this.isSearch(null, true)) {
+                    l += ' (' + this.search.mbox + ')';
+                }
+                $('folderName').update(l);
+            }
 
-                if (this.uid) {
-                    row = this.viewport.getSelection().search({ imapuid: { equal: [ this.uid ] }, view: { equal: [ this.folder ] } });
-                    if (row.size()) {
-                        this.viewport.scrollTo(row.get('rownum').first());
-                        this.viewport.select(row);
+            if (this.folderswitch) {
+                this.folderswitch = false;
+
+                tmp = $('applyfilterlink');
+                if (tmp) {
+                    if (this.isSearch() ||
+                        (!DIMP.conf.filter_any &&
+                         this.folder.toUpperCase() != 'INBOX')) {
+                        tmp.hide();
+                    } else {
+                        tmp.show();
                     }
-                } else if (this.offset) {
-                    this.viewport.select(this.viewport.createSelection('rownum', this.offset));
                 }
-                this.offset = this.uid = null;
 
-                // 'label' will not be set if there has been an error
-                // retrieving data from the server.
-                l = this.viewport.getMetaData('label');
-                if (l) {
-                    if (this.isSearch(null, true)) {
-                        l += ' (' + this.search.mbox + ')';
+                if (this.folder == DIMP.conf.spam_mbox) {
+                    if (!DIMP.conf.spam_spammbox && $('button_spam')) {
+                        [ $('button_spam').up(), $('ctx_message_spam') ].invoke('hide');
+                    }
+                    if ($('button_ham')) {
+                        [ $('button_ham').up(), $('ctx_message_ham') ].invoke('show');
+                    }
+                } else {
+                    if ($('button_spam')) {
+                        [ $('button_spam').up(), $('ctx_message_spam') ].invoke('show');
+                    }
+                    if ($('button_ham')) {
+                        [ $('button_ham').up(), $('ctx_message_ham') ].invoke(DIMP.conf.ham_spammbox ? 'hide' : 'show');
                     }
-                    $('folderName').update(l);
                 }
 
-                if (this.folderswitch) {
-                    this.folderswitch = false;
+                /* Read-only changes. 'oa_setflag' is handled elsewhere. */
+                tmp = [ $('button_deleted') ].compact().invoke('up', 'SPAN');
+                [ 'ctx_message_', 'ctx_draft_' ].each(function(c) {
+                    tmp = tmp.concat($(c + 'deleted', c + 'setflag', c + 'undeleted'));
+                });
 
-                    tmp = $('applyfilterlink');
-                    if (tmp) {
-                        if (this.isSearch() ||
-                            (!DIMP.conf.filter_any &&
-                             this.folder.toUpperCase() != 'INBOX')) {
-                            tmp.hide();
-                        } else {
-                            tmp.show();
-                        }
-                    }
+                if (this.viewport.getMetaData('readonly')) {
+                    tmp.compact().invoke('hide');
+                    $('folderName').next().show();
+                } else {
+                    tmp.compact().invoke('show');
+                    $('folderName').next().hide();
+                }
+            } else if (this.filtertoggle &&
+                       this.viewport.getMetaData('sortby') == $H(DIMP.conf.sort).get('thread').v) {
+                ssc = $H(DIMP.conf.sort).get('date').v;
+            }
 
-                    if (this.folder == DIMP.conf.spam_mbox) {
-                        if (!DIMP.conf.spam_spammbox && $('button_spam')) {
-                            [ $('button_spam').up(), $('ctx_message_spam') ].invoke('hide');
-                        }
-                        if ($('button_ham')) {
-                            [ $('button_ham').up(), $('ctx_message_ham') ].invoke('show');
-                        }
-                    } else {
-                        if ($('button_spam')) {
-                            [ $('button_spam').up(), $('ctx_message_spam') ].invoke('show');
-                        }
-                        if ($('button_ham')) {
-                            [ $('button_ham').up(), $('ctx_message_ham') ].invoke(DIMP.conf.ham_spammbox ? 'hide' : 'show');
-                        }
-                    }
+            this.setSortColumns(ssc);
+        }.bindAsEventListener(this));
 
-                    /* Read-only changes. 'oa_setflag' is handled
-                     * elsewhere. */
-                    tmp = [ $('button_deleted') ].compact().invoke('up', 'SPAN');
-                    [ 'ctx_message_', 'ctx_draft_' ].each(function(c) {
-                        tmp = tmp.concat($(c + 'deleted', c + 'setflag', c + 'undeleted'));
-                    });
+        container.observe('ViewPort:deselect', function(e) {
+            var sel = this.viewport.getSelected(),
+                count = sel.size();
+            if (!count) {
+                this.lastrow = this.pivotrow = -1;
+            }
+
+            this.toggleButtons();
+            if (e.memo.opts.right || !count) {
+                this.clearPreviewPane();
+            } else if ((count == 1) && DIMP.conf.preview_pref) {
+                this.loadPreview(sel.get('dataob').first());
+            }
+        }.bindAsEventListener(this));
+
+        container.observe('ViewPort:endFetch', this.loadingImg.bind(this, 'viewport', false));
+
+        container.observe('ViewPort:fetch', this.loadingImg.bind(this, 'viewport', true));
 
-                    if (this.viewport.getMetaData('readonly')) {
-                        tmp.compact().invoke('hide');
-                        $('folderName').next().show();
+        container.observe('ViewPort:select', function(e) {
+            var d = e.memo.vs.get('rownum');
+            if (d.size() == 1) {
+                this.lastrow = this.pivotrow = d.first();
+            }
+
+            this.toggleButtons();
+
+            if (DIMP.conf.preview_pref) {
+                if (e.memo.opts.right) {
+                    this.clearPreviewPane();
+                } else {
+                    if (e.memo.opts.delay) {
+                        this.initPreviewPane.bind(this).delay(e.memo.opts.delay);
                     } else {
-                        tmp.compact().invoke('show');
-                        $('folderName').next().hide();
+                        this.initPreviewPane();
                     }
-                } else if (this.filtertoggle &&
-                           this.viewport.getMetaData('sortby') == $H(DIMP.conf.sort).get('thread').v) {
-                    ssc = $H(DIMP.conf.sort).get('date').v;
                 }
+            }
+        }.bindAsEventListener(this));
 
-                this.setSortColumns(ssc);
-            }.bind(this),
-            onDeselect: this._deselect.bind(this),
-            onEndFetch: this.loadingImg.bind(this, 'viewport', false),
-            onFail: function() {
-                if ($('dimpmain_folder').visible()) {
-                    DimpCore.showNotifications([ { type: 'horde.error', message: DIMP.text.listmsg_timeout } ]);
-                }
-                this.loadingImg('viewport', false);
-            }.bind(this),
-            onFetch: this.loadingImg.bind(this, 'viewport', true),
-            onSelect: this._select.bind(this),
-            onSlide: this.setMessageListTitle.bind(this),
-            onSplitBarChange: function(mode) {
-                if (mode = 'horiz') {
-                    this._updatePrefs('dimp_splitbar', this.viewport.getPageSize());
-                }
-            }.bind(this),
-            onWait: function() {
-                if ($('dimpmain_folder').visible()) {
-                    DimpCore.showNotifications([ { type: 'horde.warning', message: DIMP.text.listmsg_wait } ]);
-                }
+        container.observe('ViewPort:splitBarChange', function(e) {
+            if (e.memo = 'horiz') {
+                this._updatePrefs('dimp_splitbar', this.viewport.getPageSize());
+            }
+        }.bindAsEventListener(this));
+
+        container.observe('ViewPort:wait', function() {
+            if ($('dimpmain_folder').visible()) {
+                DimpCore.showNotifications([ { type: 'horde.warning', message: DIMP.text.listmsg_wait } ]);
             }
         });
     },
@@ -667,9 +668,13 @@ var DimpBase = {
         }
     },
 
-    contextOnClick: function(parentfunc, elt, baseelt, menu)
+    contextOnClick: function(parentfunc, e)
     {
-        var flag, id = elt.readAttribute('id'), tmp;
+        var flag, tmp,
+            baseelt = e.memo.base,
+            elt = e.memo.elt,
+            id = elt.readAttribute('id'),
+            menu = e.memo.trigger;
 
         switch (id) {
         case 'ctx_folder_create':
@@ -834,15 +839,17 @@ var DimpBase = {
                 };
                 this.loadMailbox(DIMP.conf.fsearchid);
             } else {
-                parentfunc(elt, baseelt, menu);
+                parentfunc(e);
             }
             break;
         }
     },
 
-    contextOnShow: function(parentfunc, ctx_id, baseelt)
+    contextOnShow: function(parentfunc, e)
     {
-        var elts, ob, sel, tmp;
+        var elts, ob, sel, tmp,
+            baseelt = e.memo.base,
+            ctx_id = e.memo.id;
 
         switch (ctx_id) {
         case 'ctx_folder':
@@ -925,7 +932,7 @@ var DimpBase = {
             break;
 
         default:
-            parentfunc(ctx_id, baseelt);
+            parentfunc(e);
             break;
         }
     },
index 016a546..10ef75b 100644 (file)
@@ -443,13 +443,13 @@ var DimpCore = {
         }
     },
 
-    contextOnShow: function(ctx_id, baseelt)
+    contextOnShow: function(e)
     {
         var tmp;
 
-        switch (ctx_id) {
+        switch (e.memo.id) {
         case 'ctx_contacts':
-            tmp = $(ctx_id).down('DIV.contactAddr');
+            tmp = $(e.memo.id).down('DIV.contactAddr');
             if (tmp) {
                 tmp.next().remove();
                 tmp.remove();
@@ -457,23 +457,24 @@ var DimpCore = {
 
             // Add e-mail info to context menu if personal name is shown on
             // page.
-            if (baseelt.retrieve('personal')) {
-                $(ctx_id).insert({ top: new Element('DIV', { className: 'sep' }) });
-                $(ctx_id).insert({ top: new Element('DIV', { className: 'contactAddr' }).insert(baseelt.retrieve('email').escapeHTML()) });
+            if (e.memo.base.retrieve('personal')) {
+                $(e.memo.id)
+                    .insert({ top: new Element('DIV', { className: 'sep' }) })
+                    .insert({ top: new Element('DIV', { className: 'contactAddr' }).insert(e.memo.base.retrieve('email').escapeHTML()) });
             }
             break;
         }
     },
 
-    contextOnClick: function(elt, baseelt, menu)
+    contextOnClick: function(e)
     {
-        switch (elt.readAttribute('id')) {
+        switch (e.memo.elt.readAttribute('id')) {
         case 'ctx_contacts_new':
-            this.compose('new', { to: baseelt.retrieve('address') });
+            this.compose('new', { to: e.memo.baseelt.retrieve('address') });
             break;
 
         case 'ctx_contacts_add':
-            this.doAction('AddContact', { name: baseelt.retrieve('personal'), email: baseelt.retrieve('email') }, {}, true);
+            this.doAction('AddContact', { name: e.memo.baseelt.retrieve('personal'), email: e.memo.baseelt.retrieve('email') }, {}, true);
             break;
         }
     },
@@ -487,10 +488,9 @@ var DimpCore = {
         this.is_init = true;
 
         if (typeof ContextSensitive != 'undefined') {
-            this.DMenu = new ContextSensitive({
-                onClick: this.contextOnClick.bind(this),
-                onShow: this.contextOnShow.bind(this)
-            });
+            this.DMenu = new ContextSensitive();
+            document.observe('ContextSensitive:click', this.contextOnClick.bindAsEventListener(this));
+            document.observe('ContextSensitive:show', this.contextOnShow.bindAsEventListener(this));
         }
 
         /* Add Growler notification handler. */
index 93206f2..5d8bab7 100644 (file)
@@ -1,8 +1,31 @@
 /**
  * DimpSlider.js - A minimalist library to create a slider that acts like a
  * browser's native scrollbar.
+ *
  * Requires prototype.js 1.6.0.2+
  *
+ *
+ * Usage:
+ * ------
+ * slider = new DimpSlider(track, opts);
+ *
+ *   track - (element|string) TODO
+ *   opts - (object) TODO
+ *
+ * Custom Events:
+ * --------------
+ * Custom events are triggered on the track element. The parameters given
+ * below are available through the 'memo' property of the Event object.
+ *
+ * DimpSlider:change
+ *   Fired on TODO
+ *   params: NONE
+ *
+ * DimpSlider:slide
+ *   Fired on TODO
+ *   params: NONE
+ *
+ *
  * Adapted from script.aculo.us slider.js v1.8.0
  *   (c) 2005-2007 Marty Haught, Thomas Fuchs
  *   http://script.aculo.us/
@@ -110,9 +133,7 @@ var DimpSlider = Class.create({
         if (this.active) {
             this.dragging = true;
             this._setScrollPosition('px', Math.min(Math.max(0, e.pointerY() - this.offsetY - this.curroffsets[1]), this.handletop));
-            if (this.options.onSlide) {
-                this.options.onSlide();
-            }
+            this.track.fire('DimpSlider:slide');
             if (Prototype.Browser.WebKit) {
                 window.scrollBy(0,0);
             }
@@ -139,9 +160,7 @@ var DimpSlider = Class.create({
 
     _updateFinished: function()
     {
-        if (this.options.onChange) {
-            this.options.onChange();
-        }
+        this.track.fire('DimpSlider:change');
     },
 
     setHandleLength: function(pagesize, totalsize)
index ec0e9e4..d4578d2 100644 (file)
  * list_class: (string) The CSS class to use for the list container.
  * lookbehind: (integer) What percentage of the received buffer should be
  *             used to download rows before the given row number?
+ * onAjaxFailure: (function) TODO
+ * onAjaxRequest: (function) TODO
+ * onAjaxResponse: (function) TODO
+ * onCachedList: (function) TODO
+ * onSlide: (function) TODO
  * page_size: (integer) Default page size to view on load. Only used if
  *            pane_mode is 'horiz'.
  * pane_data: (Element/string) A DOM element/ID of the container to hold
  *       built.
  *
  *
- * Callbacks:
- * ----------
- * onAjaxRequest
- * onAjaxResponse
- * onCachedList
- * onCacheUpdate
- * onClear
- * onContent
- * onContentComplete
- * onDeselect
- * onEndFetch
- * onFetch
- * onSelect
- * onSlide
+ * Custom events:
+ * --------------
+ * Custom events are triggered on the container element. The parameters given
+ * below are available through the 'memo' property of the Event object.
  *
- * onSplitBarChange
- * onSplitBarEnd
- * onSplitBarStart
- *   + Passed the current pane mode (either 'horiz' or 'vert').
+ * ViewPort:cacheUpdate
+ *   Fired on TODO
+ *   params: (string) View which is being updated.
  *
- * onWait
+ * ViewPort:clear
+ *   Fired on TODO
+ *   params: (array) TODO
+ *
+ * ViewPort:contentComplete
+ *   Fired on TODO
+ *   params: (array) TODO
+ *
+ * ViewPort:deselect
+ *   Fired on TODO
+ *   params: (object) opts = (object) Boolean options [right]
+ *                    vs = (ViewPort_Selection) A ViewPort_Selection object.
+ *
+ * ViewPort:endFetch
+ *   Fired on TODO
+ *   params: (string) Current view.
+ *
+ * ViewPort:fetch
+ *   Fired on TODO
+ *   params: (string) Current view.
+ *
+ * ViewPort:select
+ *   Fired on TODO
+ *   params: (object) opts = (object) Boolean options [delay, right]
+ *                    vs = (ViewPort_Selection) A ViewPort_Selection object.
+ *
+ * ViewPort:splitBarChange
+ *   Fired on TODO
+ *   params: (string) The current pane mode ('horiz' or 'vert').
+ *
+ * ViewPort:splitBarEnd
+ *   Fired on TODO
+ *   params: (string) The current pane mode ('horiz' or 'vert').
+ *
+ * ViewPort:splitBarStart
+ *   Fired on TODO
+ *   params: (string) The current pane mode ('horiz' or 'vert').
+ *
+ * ViewPort:wait
+ *   Fired on TODO
+ *   params: (string) Current view.
  *
  *
  * Outgoing AJAX request has the following params:
@@ -248,9 +282,7 @@ var ViewPort = Class.create({
         }
 
         if (!init) {
-            if (this.opts.onClear) {
-                this.opts.onClear(this.visibleRows());
-            }
+            this.opts.content.fire('ViewPort:clear', this.visibleRows());
             this.opts.content.update();
             this.scroller.clear();
         }
@@ -372,15 +404,11 @@ var ViewPort = Class.create({
     {
         this._getBuffer(opts.view).setMetaData({ total_rows: this.getMetaData('total_rows', opts.view) - vs.size() }, true);
 
-        if (this.opts.onClear) {
-            this.opts.onClear(vs.get('div').compact());
-        }
+        this.opts.content.fire('ViewPort:clear', vs.get('div').compact());
 
         this._getBuffer().remove(vs.get('rownum'));
 
-        if (this.opts.onCacheUpdate) {
-            this.opts.onCacheUpdate(opts.view || this.view);
-        }
+        this.opts.container.fire('ViewPort:cacheUpdate', opts.view || this.view);
 
         if (!opts.noupdate) {
             this.requestContentRefresh(this.currentOffset());
@@ -518,17 +546,17 @@ var ViewPort = Class.create({
 
         this.isbusy = true;
 
-        // Only call onFetch() if we are loading in foreground.
-        if (!opts.background && this.opts.onFetch) {
-            this.opts.onFetch();
-        }
-
         var llist, lrows, rlist, tmp, type, value,
             view = (opts.view || this.view),
             b = this._getBuffer(view),
             params = $H(opts.params),
             r_id = this.request_num++;
 
+        // Only fire fetch event if we are loading in foreground.
+        if (!opts.background) {
+            this.opts.container.fire('ViewPort:fetch', view);
+        }
+
         params.update({ requestid: r_id });
 
         // Determine if we are querying via offset or a search query
@@ -714,6 +742,7 @@ var ViewPort = Class.create({
             evalJS: false,
             evalJSON: true,
             onComplete: this.ajax_response,
+            onFailure: this.opts.onAjaxFailure || Prototype.emptyFunction,
             parameters: this.addRequestParams(params, other)
         }));
     },
@@ -736,9 +765,7 @@ var ViewPort = Class.create({
 
         if (r.rangelist) {
             this.select(this.createSelection('uid', r.rangelist, r.view));
-            if (this.opts.onEndFetch) {
-                this.opts.onEndFetch();
-            }
+            this.opts.container.fire('ViewPort:endFetch', r.view);
         }
 
         if (!Object.isUndefined(r.cacheid)) {
@@ -772,9 +799,7 @@ var ViewPort = Class.create({
             total_rows: r.totalrows
         }, true);
 
-        if (this.opts.onCacheUpdate) {
-            this.opts.onCacheUpdate(r.view);
-        }
+        this.opts.container.fire('ViewPort:cacheUpdate', r.view);
 
         if (r.requestid &&
             r.requestid == this.active_req) {
@@ -789,9 +814,7 @@ var ViewPort = Class.create({
 
             buffer.setMetaData({ callback: undefined, req_offset: undefined }, true);
 
-            if (this.opts.onEndFetch) {
-                this.opts.onEndFetch();
-            }
+            this.opts.container.fire('ViewPort:endFetch', r.view);
         }
 
         // TODO: Flag for no _fetchBuffer()?
@@ -824,9 +847,7 @@ var ViewPort = Class.create({
             page_size = this.getPageSize(),
             rows;
 
-        if (this.opts.onClear) {
-            this.opts.onClear(this.visibleRows());
-        }
+        this.opts.container.fire('ViewPort:clear', this.visibleRows());
 
         this.scroller.setSize(page_size, this.getMetaData('total_rows'));
         this.scrollTo(offset + 1, { noupdate: true, top: true });
@@ -843,9 +864,7 @@ var ViewPort = Class.create({
 
         this.scroller.updateDisplay();
 
-        if (this.opts.onContentComplete) {
-            this.opts.onContentComplete(c_nodes);
-        }
+        this.opts.container.fire('ViewPort:contentComplete', c_nodes);
 
         return true;
     },
@@ -869,15 +888,11 @@ var ViewPort = Class.create({
     {
         var d = $(row.domid);
         if (d) {
-            if (this.opts.onClear) {
-                this.opts.onClear([ d ]);
-            }
+            this.opts.container.fire('ViewPort:clear', [ d ]);
 
             d.replace(this.prepareRow(row));
 
-            if (this.opts.onContentComplete) {
-                this.opts.onContentComplete([ row ]);
-            }
+            this.opts.container.fire('ViewPort:contentComplete', [ row ]);
         }
 
     },
@@ -888,8 +903,8 @@ var ViewPort = Class.create({
 
         // Server did not respond in defined amount of time.  Alert the
         // callback function and set the next timeout.
-        if (call && this.opts.onWait) {
-            this.opts.onWait();
+        if (call) {
+            this.opts.container.fire('ViewPort:wait', this.view);
         }
 
         // Call wait handler every x seconds
@@ -1047,9 +1062,7 @@ var ViewPort = Class.create({
                         orig: this.page_size,
                         pos: this.opts.content.positionedOffset()[1]
                     };
-                    if (this.opts.onSplitBarStart) {
-                        this.opts.onSplitBarStart('horiz');
-                    }
+                    this.opts.container.fire('ViewPort:splitBarStart', 'horiz');
                 }.bind(this),
                 snap: function(x, y, elt) {
                     var l = parseInt((y - this.sp.pos) / this.sp.lh);
@@ -1063,22 +1076,18 @@ var ViewPort = Class.create({
                 }.bind(this),
                 onEnd: function() {
                     this.onResize(true, this.sp.lines);
-                    if (this.opts.onSplitBarChange &&
-                        this.sp.orig != this.sp.lines) {
-                        this.opts.onSplitBarChange('horiz');
-                    }
-                    if (this.opts.onSplitBarEnd) {
-                        this.opts.onSplitBarEnd('horiz');
+                    if (this.sp.orig != this.sp.lines) {
+                        this.opts.container.fire('ViewPort:splitBarChange', 'horiz');
                     }
+                    this.opts.container.fire('ViewPort:splitBarEnd', 'horiz');
                 }.bind(this)
             });
 
             sp.currbar.observe('dblclick', function() {
                 var old_size = this.page_size;
                 this.onResize(true, this.getPageSize('default'));
-                if (this.opts.onSplitBarChange &&
-                    old_size != this.page_size) {
-                    this.opts.onSplitBarChange('horiz');
+                if (old_size != this.page_size) {
+                    this.opts.container.fire('ViewPort:splitBarChange', 'horiz');
                 }
             }.bind(this));
             break;
@@ -1090,27 +1099,21 @@ var ViewPort = Class.create({
                 nodrop: true,
                 snapToParent: true,
                 onStart: function(drag) {
-                    if (this.opts.onSplitBarStart) {
-                        this.opts.onSplitBarStart('vert');
-                    }
+                    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 (this.opts.onSplitBarChange && drag.wasDragged) {
-                        this.opts.onSplitBarChange('vert');
-                    }
-                    if (this.opts.onSplitBarEnd) {
-                        this.opts.onSplitBarEnd('vert');
+                    if (drag.wasDragged) {
+                        this.opts.container.fire('ViewPort:splitBarChange', 'vert');
                     }
+                    this.opts.container.fire('ViewPort:splitBarEnd', 'vert');
                 }.bind(this)
             });
 
             sp.currbar.observe('dblclick', function() {
                 this.opts.content.setStyle({ width: parseInt(this.opts.container.clientWidth * 0.45, 10) + 'px' });
-                if (this.opts.onSplitBarChange) {
-                    this.opts.onSplitBarChange('vert');
-                }
+                this.opts.container.fire('ViewPort:splitBarChange', 'vert');
             }.bind(this));
             break;
         }
@@ -1128,15 +1131,6 @@ var ViewPort = Class.create({
         return this.createSelection('uid', buffer ? buffer.getAllUIDs() : [], view);
     },
 
-    getMissing: function(view)
-    {
-        var buffer = this._getBuffer(view);
-        if (!buffer) {
-            return '';
-        }
-        return this.createSelection('uid', buffer ? buffer.getAllUIDs() : [], view);
-    },
-
     // vs = (Viewport_Selection | array) A Viewport_Selection object -or- if
     //       opts.range is set, an array of row numbers.
     // opts = (object) TODO [add, range, search]
@@ -1148,9 +1142,7 @@ var ViewPort = Class.create({
             sel, slice;
 
         if (opts.search) {
-            if (this.opts.onFetch) {
-                this.opts.onFetch();
-            }
+            this.opts.container.fire('ViewPort:fetch', this.view);
 
             return this._fetchBuffer({
                 callback: function(r) {
@@ -1165,9 +1157,7 @@ var ViewPort = Class.create({
         if (opts.range) {
             slice = this.createSelection('rownum', vs);
             if (vs.size() != slice.size()) {
-                if (this.opts.onFetch) {
-                    this.opts.onFetch();
-                }
+                this.opts.container.fire('ViewPort:fetch', this.view);
 
                 return this._ajaxRequest({ rangeslice: 1, slice: vs.min() + ':' + vs.size() });
             }
@@ -1181,9 +1171,7 @@ var ViewPort = Class.create({
         }
         b.select(vs);
         vs.get('div').invoke('addClassName', 'vpRowSelected');
-        if (this.opts.onSelect) {
-            this.opts.onSelect(vs, opts);
-        }
+        this.opts.container.fire('ViewPort:select', { opts: opts, vs: vs });
     },
 
     // vs = (Viewport_Selection) A Viewport_Selection object.
@@ -1195,9 +1183,7 @@ var ViewPort = Class.create({
         if (vs.size() &&
             this._getBuffer().deselect(vs, opts && opts.clearall)) {
             vs.get('div').invoke('removeClassName', 'vpRowSelected');
-            if (this.opts.onDeselect) {
-                this.opts.onDeselect(vs, opts)
-            }
+            this.opts.container.fire('ViewPort:deselect', { opts: opts, vs: vs });
         }
     },
 
@@ -1232,12 +1218,15 @@ ViewPort_Scroller = Class.create({
         this.scrollDiv = new Element('DIV', { className: 'vpScroll' }).setStyle({ overflow: 'hidden' }).hide();
         c.insert({ after: this.scrollDiv });
 
+        this.scrollDiv.observe('DimpSlider:change', this._onScroll.bind(this));
+        if (this.vp.opts.onSlide) {
+            this.scrollDiv.observe('DimpSlider:slide', this.vp.opts.onSlide);
+        }
+
         // Create scrollbar object.
         this.scrollbar = new DimpSlider(this.scrollDiv, {
             buttonclass: { up: 'vpScrollUp', down: 'vpScrollDown' },
             cursorclass: 'vpScrollCursor',
-            onChange: this._onScroll.bind(this),
-            onSlide: this.vp.opts.onSlide ? this.vp.opts.onSlide : null,
             pagesize: this.vp.getPageSize(),
             totalsize: this.vp.getMetaData('total_rows')
        });
index 8a87eee..2266c16 100644 (file)
@@ -142,9 +142,9 @@ var DimpFullmessage = {
         parentfunc(e);
     },
 
-    contextOnClick: function(parentfunc, elt, baseelt, menu)
+    contextOnClick: function(parentfunc, e)
     {
-        var id = elt.readAttribute('id');
+        var id = e.memo.elt.readAttribute('id');
 
         switch (id) {
         case 'ctx_reply_reply':
@@ -154,7 +154,7 @@ var DimpFullmessage = {
             break;
 
         default:
-            parentfunc(elt, baseelt, menu);
+            parentfunc(e);
             break;
         }
     },