Move ContextSensitive to Horde
authorMichael M Slusarz <slusarz@curecanti.org>
Thu, 28 Jan 2010 17:58:06 +0000 (10:58 -0700)
committerMichael M Slusarz <slusarz@curecanti.org>
Thu, 28 Jan 2010 17:58:06 +0000 (10:58 -0700)
horde/js/ContextSensitive.js [new file with mode: 0644]
imp/index-dimp.php
imp/js/ContextSensitive.js [deleted file]
imp/message-dimp.php

diff --git a/horde/js/ContextSensitive.js b/horde/js/ContextSensitive.js
new file mode 100644 (file)
index 0000000..ade1783
--- /dev/null
@@ -0,0 +1,389 @@
+/**
+ * ContextSensitive: a library for generating context-sensitive content on
+ * HTML elements. It will take over the click/oncontextmenu functions for the
+ * document, and works only where these are possible to override.  It allows
+ * contextmenus to be created via both a left and right mouse click.
+ *
+ * On Opera, the context menu is triggered by a left click + SHIFT + CTRL
+ * combination.
+ *
+ * Requires prototypejs 1.6+ and scriptaculous 1.8+ (effects.js only).
+ *
+ *
+ * Usage:
+ * ------
+ * cs = new ContextSensitive();
+ *
+ * Custom Events:
+ * --------------
+ * Custom events are triggered on the base element. The parameters given
+ * below are available through the 'memo' property of the Event object.
+ *
+ * ContextSensitive:click
+ *   Fired when a contextmenu element is clicked on.
+ *   params: (object) elt - (Element) The menu element clicked on.
+ *                    trigger - (string) The parent menu.
+ *
+ * ContextSensitive:show
+ *   Fired before a contextmenu is displayed.
+ *   params: (string) The DOM ID of the context menu.
+ *
+ *
+ * Original code by Havard Eide (http://eide.org/) released under the MIT
+ * license.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @author Michael Slusarz <slusarz@horde.org>
+ */
+
+var ContextSensitive = Class.create({
+
+    initialize: function()
+    {
+        this.baseelt = null;
+        this.current = [];
+        this.elements = $H();
+        this.submenus = $H();
+        this.triggers = [];
+
+        if (!Prototype.Browser.Opera) {
+            document.observe('contextmenu', this._rightClickHandler.bindAsEventListener(this));
+        }
+        document.observe('click', this._leftClickHandler.bindAsEventListener(this));
+        document.observe(Prototype.Browser.Gecko ? 'DOMMouseScroll' : 'mousescroll', this.close.bind(this));
+    },
+
+    /**
+     * Elements are of type ContextSensitive.Element.
+     */
+    addElement: function(id, target, opts)
+    {
+        var left = Boolean(opts.left);
+        if (id && !this.validElement(id, left)) {
+            this.elements.set(id + Number(left), new ContextSensitive.Element(id, target, opts));
+        }
+    },
+
+    /**
+     * Remove a registered element.
+     */
+    removeElement: function(id)
+    {
+        this.elements.unset(id + '0');
+        this.elements.unset(id + '1');
+    },
+
+    /**
+     * Hide the currently displayed element(s).
+     */
+    close: function()
+    {
+        this._closeMenu(0, true);
+    },
+
+    /**
+     * Close all menus below a specified level.
+     */
+    _closeMenu: function(idx, immediate)
+    {
+        if (this.current.size()) {
+            this.current.splice(idx, this.current.size() - idx).each(function(s) {
+                // Fade-out on final display.
+                if (!immediate && idx == 0) {
+                    s.fade({ duration: 0.15 });
+                } else {
+                    $(s).hide();
+                }
+            });
+
+            this.triggers.splice(idx, this.triggers.size() - idx).each(function(s) {
+                $(s).removeClassName('contextHover');
+            });
+
+            if (idx == 0) {
+                this.baseelt = null;
+            }
+        }
+    },
+
+    /**
+     * Returns the current displayed menu element ID, if any. If more than one
+     * submenu is open, returns the last ID opened.
+     */
+    currentmenu: function()
+    {
+        return this.current.last();
+    },
+
+    /**
+     * Get a valid element (the ones that can be right-clicked) based
+     * on a element ID.
+     */
+    validElement: function(id, left)
+    {
+        return this.elements.get(id + Number(Boolean(left)));
+    },
+
+    /**
+     * Set the disabled flag of an event.
+     */
+    disable: function(id, left, disable)
+    {
+        var e = this.validElement(id, left);
+        if (e) {
+            e.disable = disable;
+        }
+    },
+
+    /**
+     * Called when a left click event occurs. Will return before the
+     * element is closed if we click on an element inside of it.
+     */
+    _leftClickHandler: function(e)
+    {
+        var base, elt, elt_up, trigger;
+
+        if (this.operaCheck(e)) {
+            this._rightClickHandler(e, false);
+            e.stop();
+            return;
+        }
+
+        // Check for a right click. FF on Linux triggers an onclick event even
+        // w/a right click, so disregard.
+        if (e.isRightClick()) {
+            return;
+        }
+
+        // Check for click in open contextmenu.
+        if (this.current.size()) {
+            elt = e.element();
+            if (!elt.match('A')) {
+                elt = elt.up('A');
+                if (!elt) {
+                    this._rightClickHandler(e, true);
+                    return;
+                }
+            }
+            elt_up = elt.up('.contextMenu');
+
+            if (elt_up) {
+                e.stop();
+
+                if (elt.hasClassName('contextSubmenu') &&
+                    elt_up.readAttribute('id') != this.currentmenu()) {
+                    this._closeMenu(this.current.indexOf(elt.readAttribute('id')));
+                } else {
+                    base = this.baseelt;
+                    trigger = this.triggers.last();
+                    this.close();
+                    base.fire('ContextSensitive:click', { elt: elt, trigger: trigger });
+                }
+                return;
+            }
+        }
+
+        // Check if the mouseclick is registered to an element now.
+        this._rightClickHandler(e, true);
+    },
+
+    /**
+     * Checks if the Opera right-click emulation is present.
+     */
+    operaCheck: function(e)
+    {
+        return Prototype.Browser.Opera && e.shiftKey && e.ctrlKey;
+    },
+
+    /**
+     * Called when a right click event occurs.
+     */
+    _rightClickHandler: function(e, left)
+    {
+        if (this.trigger(e.element(), left, e.pointerX(), e.pointerY())) {
+            e.stop();
+        };
+    },
+
+    /**
+     * Display context menu if valid element has been activated.
+     */
+    trigger: function(target, leftclick, x, y)
+    {
+        var ctx, el, el_id, offset, offsets, voffsets;
+
+        [ target ].concat(target.ancestors()).find(function(n) {
+            ctx = this.validElement(n.id, leftclick);
+            return ctx;
+        }, this);
+
+        // Return if event not found or event is disabled.
+        if (!ctx || ctx.disable) {
+            this.close();
+            return false;
+        }
+
+        // Try to retrieve the context-sensitive element we want to
+        // display. If we can't find it we just return.
+        el = $(ctx.ctx);
+        if (!el) {
+            this.close();
+            return false;
+        }
+
+        el_id = el.readAttribute('id');
+        if (leftclick && el_id == this.currentmenu()) {
+            return false;
+        }
+
+        this.close();
+
+        // Register the element that was clicked on.
+        this.baseelt = target;
+
+        offset = ctx.opts.offset;
+        if (!offset && (Object.isUndefined(x) || Object.isUndefined(y))) {
+            offset = target.readAttribute('id');
+        }
+        offset = $(offset);
+
+        if (offset) {
+            offsets = offset.viewportOffset();
+            voffsets = document.viewport.getScrollOffsets();
+            x = offsets[0] + voffsets.left;
+            y = offsets[1] + offset.getHeight() + voffsets.top;
+        }
+
+        this._displayMenu(el, x, y);
+        this.triggers.push(el_id);
+
+        return true;
+    },
+
+    /**
+     * Display the [sub]menu on the screen.
+     */
+    _displayMenu: function(elt, x, y)
+    {
+        // Get window/element dimensions
+        var id = elt.readAttribute('id'),
+            size = elt.getDimensions(),
+            v = document.viewport.getDimensions();
+
+        // Make sure context window is entirely on screen
+        if ((y + size.height) > v.height) {
+            y = v.height - size.height - 10;
+        }
+        if ((x + size.width) > v.width) {
+            x = this.current.size()
+                ? ($(this.current.last()).viewportOffset()[0] - size.width)
+                : (v.width - size.width - 10);
+        }
+
+        this.baseelt.fire('ContextSensitive:show', id);
+
+        elt.setStyle({ left: x + 'px', top: y + 'px' })
+
+        if (this.current.size()) {
+            elt.show();
+        } else {
+            // Fade-in on initial display.
+            elt.appear({ duration: 0.15 });
+        }
+
+        this.current.push(id);
+    },
+
+    /**
+     * Add a submenu to an existing menu.
+     */
+    addSubMenu: function(id, submenu)
+    {
+        if (!this.submenus.get(id)) {
+            if (!this.submenus.size()) {
+                document.observe('mouseover', this._mouseoverHandler.bindAsEventListener(this));
+            }
+            this.submenus.set(id, submenu);
+            $(submenu).addClassName('contextMenu');
+            $(id).addClassName('contextSubmenu');
+        }
+    },
+
+    /**
+     * Mouseover DOM Event handler.
+     */
+    _mouseoverHandler: function(e)
+    {
+        if (!this.current.size()) {
+            return;
+        }
+
+        var cm = this.currentmenu(),
+            elt = e.element(),
+            elt_up = elt.up('.contextMenu'),
+            id = elt.readAttribute('id'),
+            id_div, offsets, sub, voffsets, x, y;
+
+        if (!elt_up) {
+            return;
+        }
+
+        id_div = elt_up.readAttribute('id');
+
+        if (elt.hasClassName('contextSubmenu')) {
+            sub = this.submenus.get(id);
+            if (sub != cm || this.currentmenu() != id) {
+                if (id_div != cm) {
+                    this._closeMenu(this.current.indexOf(id_div) + 1);
+                }
+
+                offsets = elt.viewportOffset();
+                voffsets = document.viewport.getScrollOffsets();
+                x = offsets[0] + voffsets.left + elt.getWidth();
+                y = offsets[1] + voffsets.top;
+                this._displayMenu($(sub), x, y, id);
+                this.triggers.push(id);
+                elt.addClassName('contextHover');
+            }
+        } else if ((this.current.size() > 1) &&
+                   id_div != cm) {
+            this._closeMenu(this.current.indexOf(id));
+        }
+    }
+
+});
+
+ContextSensitive.Element = Class.create({
+
+    // opts: 'left' -> monitor left click; 'offset' -> id of element used to
+    //       determine offset placement
+    initialize: function(id, target, opts)
+    {
+        this.id = id;
+        this.ctx = target;
+        this.opts = opts;
+        this.opts.left = Boolean(opts.left);
+        this.disable = false;
+
+        target = $(target);
+        if (target) {
+            target.addClassName('contextMenu');
+        }
+    }
+
+});
index 0892a20..5a5ff27 100644 (file)
@@ -16,12 +16,12 @@ require_once dirname(__FILE__) . '/lib/Application.php';
 Horde_Registry::appInit('imp');
 
 $scripts = array(
-    array('ContextSensitive.js', 'imp'),
     array('DimpBase.js', 'imp'),
     array('ViewPort.js', 'imp'),
     array('dialog.js', 'imp'),
     array('mailbox-dimp.js', 'imp'),
     array('imp.js', 'imp'),
+    array('ContextSensitive.js', 'horde'),
     array('dragdrop2.js', 'horde'),
     array('popup.js', 'horde'),
     array('redbox.js', 'horde'),
diff --git a/imp/js/ContextSensitive.js b/imp/js/ContextSensitive.js
deleted file mode 100644 (file)
index ade1783..0000000
+++ /dev/null
@@ -1,389 +0,0 @@
-/**
- * ContextSensitive: a library for generating context-sensitive content on
- * HTML elements. It will take over the click/oncontextmenu functions for the
- * document, and works only where these are possible to override.  It allows
- * contextmenus to be created via both a left and right mouse click.
- *
- * On Opera, the context menu is triggered by a left click + SHIFT + CTRL
- * combination.
- *
- * Requires prototypejs 1.6+ and scriptaculous 1.8+ (effects.js only).
- *
- *
- * Usage:
- * ------
- * cs = new ContextSensitive();
- *
- * Custom Events:
- * --------------
- * Custom events are triggered on the base element. The parameters given
- * below are available through the 'memo' property of the Event object.
- *
- * ContextSensitive:click
- *   Fired when a contextmenu element is clicked on.
- *   params: (object) elt - (Element) The menu element clicked on.
- *                    trigger - (string) The parent menu.
- *
- * ContextSensitive:show
- *   Fired before a contextmenu is displayed.
- *   params: (string) The DOM ID of the context menu.
- *
- *
- * Original code by Havard Eide (http://eide.org/) released under the MIT
- * license.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * @author Chuck Hagenbuch <chuck@horde.org>
- * @author Michael Slusarz <slusarz@horde.org>
- */
-
-var ContextSensitive = Class.create({
-
-    initialize: function()
-    {
-        this.baseelt = null;
-        this.current = [];
-        this.elements = $H();
-        this.submenus = $H();
-        this.triggers = [];
-
-        if (!Prototype.Browser.Opera) {
-            document.observe('contextmenu', this._rightClickHandler.bindAsEventListener(this));
-        }
-        document.observe('click', this._leftClickHandler.bindAsEventListener(this));
-        document.observe(Prototype.Browser.Gecko ? 'DOMMouseScroll' : 'mousescroll', this.close.bind(this));
-    },
-
-    /**
-     * Elements are of type ContextSensitive.Element.
-     */
-    addElement: function(id, target, opts)
-    {
-        var left = Boolean(opts.left);
-        if (id && !this.validElement(id, left)) {
-            this.elements.set(id + Number(left), new ContextSensitive.Element(id, target, opts));
-        }
-    },
-
-    /**
-     * Remove a registered element.
-     */
-    removeElement: function(id)
-    {
-        this.elements.unset(id + '0');
-        this.elements.unset(id + '1');
-    },
-
-    /**
-     * Hide the currently displayed element(s).
-     */
-    close: function()
-    {
-        this._closeMenu(0, true);
-    },
-
-    /**
-     * Close all menus below a specified level.
-     */
-    _closeMenu: function(idx, immediate)
-    {
-        if (this.current.size()) {
-            this.current.splice(idx, this.current.size() - idx).each(function(s) {
-                // Fade-out on final display.
-                if (!immediate && idx == 0) {
-                    s.fade({ duration: 0.15 });
-                } else {
-                    $(s).hide();
-                }
-            });
-
-            this.triggers.splice(idx, this.triggers.size() - idx).each(function(s) {
-                $(s).removeClassName('contextHover');
-            });
-
-            if (idx == 0) {
-                this.baseelt = null;
-            }
-        }
-    },
-
-    /**
-     * Returns the current displayed menu element ID, if any. If more than one
-     * submenu is open, returns the last ID opened.
-     */
-    currentmenu: function()
-    {
-        return this.current.last();
-    },
-
-    /**
-     * Get a valid element (the ones that can be right-clicked) based
-     * on a element ID.
-     */
-    validElement: function(id, left)
-    {
-        return this.elements.get(id + Number(Boolean(left)));
-    },
-
-    /**
-     * Set the disabled flag of an event.
-     */
-    disable: function(id, left, disable)
-    {
-        var e = this.validElement(id, left);
-        if (e) {
-            e.disable = disable;
-        }
-    },
-
-    /**
-     * Called when a left click event occurs. Will return before the
-     * element is closed if we click on an element inside of it.
-     */
-    _leftClickHandler: function(e)
-    {
-        var base, elt, elt_up, trigger;
-
-        if (this.operaCheck(e)) {
-            this._rightClickHandler(e, false);
-            e.stop();
-            return;
-        }
-
-        // Check for a right click. FF on Linux triggers an onclick event even
-        // w/a right click, so disregard.
-        if (e.isRightClick()) {
-            return;
-        }
-
-        // Check for click in open contextmenu.
-        if (this.current.size()) {
-            elt = e.element();
-            if (!elt.match('A')) {
-                elt = elt.up('A');
-                if (!elt) {
-                    this._rightClickHandler(e, true);
-                    return;
-                }
-            }
-            elt_up = elt.up('.contextMenu');
-
-            if (elt_up) {
-                e.stop();
-
-                if (elt.hasClassName('contextSubmenu') &&
-                    elt_up.readAttribute('id') != this.currentmenu()) {
-                    this._closeMenu(this.current.indexOf(elt.readAttribute('id')));
-                } else {
-                    base = this.baseelt;
-                    trigger = this.triggers.last();
-                    this.close();
-                    base.fire('ContextSensitive:click', { elt: elt, trigger: trigger });
-                }
-                return;
-            }
-        }
-
-        // Check if the mouseclick is registered to an element now.
-        this._rightClickHandler(e, true);
-    },
-
-    /**
-     * Checks if the Opera right-click emulation is present.
-     */
-    operaCheck: function(e)
-    {
-        return Prototype.Browser.Opera && e.shiftKey && e.ctrlKey;
-    },
-
-    /**
-     * Called when a right click event occurs.
-     */
-    _rightClickHandler: function(e, left)
-    {
-        if (this.trigger(e.element(), left, e.pointerX(), e.pointerY())) {
-            e.stop();
-        };
-    },
-
-    /**
-     * Display context menu if valid element has been activated.
-     */
-    trigger: function(target, leftclick, x, y)
-    {
-        var ctx, el, el_id, offset, offsets, voffsets;
-
-        [ target ].concat(target.ancestors()).find(function(n) {
-            ctx = this.validElement(n.id, leftclick);
-            return ctx;
-        }, this);
-
-        // Return if event not found or event is disabled.
-        if (!ctx || ctx.disable) {
-            this.close();
-            return false;
-        }
-
-        // Try to retrieve the context-sensitive element we want to
-        // display. If we can't find it we just return.
-        el = $(ctx.ctx);
-        if (!el) {
-            this.close();
-            return false;
-        }
-
-        el_id = el.readAttribute('id');
-        if (leftclick && el_id == this.currentmenu()) {
-            return false;
-        }
-
-        this.close();
-
-        // Register the element that was clicked on.
-        this.baseelt = target;
-
-        offset = ctx.opts.offset;
-        if (!offset && (Object.isUndefined(x) || Object.isUndefined(y))) {
-            offset = target.readAttribute('id');
-        }
-        offset = $(offset);
-
-        if (offset) {
-            offsets = offset.viewportOffset();
-            voffsets = document.viewport.getScrollOffsets();
-            x = offsets[0] + voffsets.left;
-            y = offsets[1] + offset.getHeight() + voffsets.top;
-        }
-
-        this._displayMenu(el, x, y);
-        this.triggers.push(el_id);
-
-        return true;
-    },
-
-    /**
-     * Display the [sub]menu on the screen.
-     */
-    _displayMenu: function(elt, x, y)
-    {
-        // Get window/element dimensions
-        var id = elt.readAttribute('id'),
-            size = elt.getDimensions(),
-            v = document.viewport.getDimensions();
-
-        // Make sure context window is entirely on screen
-        if ((y + size.height) > v.height) {
-            y = v.height - size.height - 10;
-        }
-        if ((x + size.width) > v.width) {
-            x = this.current.size()
-                ? ($(this.current.last()).viewportOffset()[0] - size.width)
-                : (v.width - size.width - 10);
-        }
-
-        this.baseelt.fire('ContextSensitive:show', id);
-
-        elt.setStyle({ left: x + 'px', top: y + 'px' })
-
-        if (this.current.size()) {
-            elt.show();
-        } else {
-            // Fade-in on initial display.
-            elt.appear({ duration: 0.15 });
-        }
-
-        this.current.push(id);
-    },
-
-    /**
-     * Add a submenu to an existing menu.
-     */
-    addSubMenu: function(id, submenu)
-    {
-        if (!this.submenus.get(id)) {
-            if (!this.submenus.size()) {
-                document.observe('mouseover', this._mouseoverHandler.bindAsEventListener(this));
-            }
-            this.submenus.set(id, submenu);
-            $(submenu).addClassName('contextMenu');
-            $(id).addClassName('contextSubmenu');
-        }
-    },
-
-    /**
-     * Mouseover DOM Event handler.
-     */
-    _mouseoverHandler: function(e)
-    {
-        if (!this.current.size()) {
-            return;
-        }
-
-        var cm = this.currentmenu(),
-            elt = e.element(),
-            elt_up = elt.up('.contextMenu'),
-            id = elt.readAttribute('id'),
-            id_div, offsets, sub, voffsets, x, y;
-
-        if (!elt_up) {
-            return;
-        }
-
-        id_div = elt_up.readAttribute('id');
-
-        if (elt.hasClassName('contextSubmenu')) {
-            sub = this.submenus.get(id);
-            if (sub != cm || this.currentmenu() != id) {
-                if (id_div != cm) {
-                    this._closeMenu(this.current.indexOf(id_div) + 1);
-                }
-
-                offsets = elt.viewportOffset();
-                voffsets = document.viewport.getScrollOffsets();
-                x = offsets[0] + voffsets.left + elt.getWidth();
-                y = offsets[1] + voffsets.top;
-                this._displayMenu($(sub), x, y, id);
-                this.triggers.push(id);
-                elt.addClassName('contextHover');
-            }
-        } else if ((this.current.size() > 1) &&
-                   id_div != cm) {
-            this._closeMenu(this.current.indexOf(id));
-        }
-    }
-
-});
-
-ContextSensitive.Element = Class.create({
-
-    // opts: 'left' -> monitor left click; 'offset' -> id of element used to
-    //       determine offset placement
-    initialize: function(id, target, opts)
-    {
-        this.id = id;
-        this.ctx = target;
-        this.opts = opts;
-        this.opts.left = Boolean(opts.left);
-        this.disable = false;
-
-        target = $(target);
-        if (target) {
-            target.addClassName('contextMenu');
-        }
-    }
-
-});
index 019dbcf..c4964ec 100644 (file)
@@ -40,7 +40,7 @@ if (isset($show_msg_result['error'])) {
 }
 
 $scripts = array(
-    array('ContextSensitive.js', 'imp'),
+    array('ContextSensitive.js', 'horde'),
     array('fullmessage-dimp.js', 'imp')
 );