source: trunk/web/addons/job_monarch/lib/extjs/source/widgets/menu/Menu.js @ 619

Last change on this file since 619 was 619, checked in by ramonb, 15 years ago

lib/:

  • added new AJAX dependancies: ExtJS, pChart, Lightbox2
File size: 18.6 KB
Line 
1/*
2 * Ext JS Library 2.2.1
3 * Copyright(c) 2006-2009, Ext JS, LLC.
4 * licensing@extjs.com
5 *
6 * http://extjs.com/license
7 */
8
9/**
10 * @class Ext.menu.Menu
11 * @extends Ext.util.Observable
12 * A menu object.  This is the container to which you add all other menu items.  Menu can also serve as a base class
13 * when you want a specialized menu based off of another component (like {@link Ext.menu.DateMenu} for example).
14 * @constructor
15 * Creates a new Menu
16 * @param {Object} config Configuration options
17 */
18Ext.menu.Menu = function(config){
19    if(Ext.isArray(config)){
20        config = {items:config};
21    }
22    Ext.apply(this, config);
23    this.id = this.id || Ext.id();
24    this.addEvents(
25        /**
26         * @event beforeshow
27         * Fires before this menu is displayed
28         * @param {Ext.menu.Menu} this
29         */
30        'beforeshow',
31        /**
32         * @event beforehide
33         * Fires before this menu is hidden
34         * @param {Ext.menu.Menu} this
35         */
36        'beforehide',
37        /**
38         * @event show
39         * Fires after this menu is displayed
40         * @param {Ext.menu.Menu} this
41         */
42        'show',
43        /**
44         * @event hide
45         * Fires after this menu is hidden
46         * @param {Ext.menu.Menu} this
47         */
48        'hide',
49        /**
50         * @event click
51         * Fires when this menu is clicked (or when the enter key is pressed while it is active)
52         * @param {Ext.menu.Menu} this
53         * @param {Ext.menu.Item} menuItem The menu item that was clicked
54         * @param {Ext.EventObject} e
55         */
56        'click',
57        /**
58         * @event mouseover
59         * Fires when the mouse is hovering over this menu
60         * @param {Ext.menu.Menu} this
61         * @param {Ext.EventObject} e
62         * @param {Ext.menu.Item} menuItem The menu item that was clicked
63         */
64        'mouseover',
65        /**
66         * @event mouseout
67         * Fires when the mouse exits this menu
68         * @param {Ext.menu.Menu} this
69         * @param {Ext.EventObject} e
70         * @param {Ext.menu.Item} menuItem The menu item that was clicked
71         */
72        'mouseout',
73        /**
74         * @event itemclick
75         * Fires when a menu item contained in this menu is clicked
76         * @param {Ext.menu.BaseItem} baseItem The BaseItem that was clicked
77         * @param {Ext.EventObject} e
78         */
79        'itemclick'
80    );
81    Ext.menu.MenuMgr.register(this);
82    Ext.menu.Menu.superclass.constructor.call(this);
83    var mis = this.items;
84    /**
85     * A MixedCollection of this Menu's items
86     * @property items
87     * @type Ext.util.MixedCollection
88     */
89
90    this.items = new Ext.util.MixedCollection();
91    if(mis){
92        this.add.apply(this, mis);
93    }
94};
95
96Ext.extend(Ext.menu.Menu, Ext.util.Observable, {
97    /**
98     * @cfg {Object} defaults
99     * A config object that will be applied to all items added to this container either via the {@link #items}
100     * config or via the {@link #add} method.  The defaults config can contain any number of
101     * name/value property pairs to be added to each item, and should be valid for the types of items
102     * being added to the menu.
103     */
104    /**
105     * @cfg {Mixed} items
106     * An array of items to be added to this menu.  See {@link #add} for a list of valid item types.
107     */
108    /**
109     * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
110     */
111    minWidth : 120,
112    /**
113     * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
114     * for bottom-right shadow (defaults to "sides")
115     */
116    shadow : "sides",
117    /**
118     * @cfg {String} subMenuAlign The {@link Ext.Element#alignTo} anchor position value to use for submenus of
119     * this menu (defaults to "tl-tr?")
120     */
121    subMenuAlign : "tl-tr?",
122    /**
123     * @cfg {String} defaultAlign The default {@link Ext.Element#alignTo} anchor position value for this menu
124     * relative to its element of origin (defaults to "tl-bl?")
125     */
126    defaultAlign : "tl-bl?",
127    /**
128     * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
129     */
130    allowOtherMenus : false,
131    /**
132     * @cfg {Boolean} ignoreParentClicks True to ignore clicks on any item in this menu that is a parent item (displays
133     * a submenu) so that the submenu is not dismissed when clicking the parent item (defaults to false).
134     */
135    ignoreParentClicks : false,
136
137    // private
138    hidden:true,
139
140    // private
141    createEl : function(){
142        return new Ext.Layer({
143            cls: "x-menu",
144            shadow:this.shadow,
145            constrain: false,
146            parentEl: this.parentEl || document.body,
147            zindex:15000
148        });
149    },
150
151    // private
152    render : function(){
153        if(this.el){
154            return;
155        }
156        var el = this.el = this.createEl();
157
158        if(!this.keyNav){
159            this.keyNav = new Ext.menu.MenuNav(this);
160        }
161        if(this.plain){
162            el.addClass("x-menu-plain");
163        }
164        if(this.cls){
165            el.addClass(this.cls);
166        }
167        // generic focus element
168        this.focusEl = el.createChild({
169            tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
170        });
171        var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
172        ul.on("click", this.onClick, this);
173        ul.on("mouseover", this.onMouseOver, this);
174        ul.on("mouseout", this.onMouseOut, this);
175        this.items.each(function(item){
176            var li = document.createElement("li");
177            li.className = "x-menu-list-item";
178            ul.dom.appendChild(li);
179            item.render(li, this);
180        }, this);
181        this.ul = ul;
182        this.autoWidth();
183    },
184
185    // private
186    autoWidth : function(){
187        var el = this.el, ul = this.ul;
188        if(!el){
189            return;
190        }
191        var w = this.width;
192        if(w){
193            el.setWidth(w);
194        }else if(Ext.isIE){
195            el.setWidth(this.minWidth);
196            var t = el.dom.offsetWidth; // force recalc
197            el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
198        }
199    },
200
201    // private
202    delayAutoWidth : function(){
203        if(this.el){
204            if(!this.awTask){
205                this.awTask = new Ext.util.DelayedTask(this.autoWidth, this);
206            }
207            this.awTask.delay(20);
208        }
209    },
210
211    // private
212    findTargetItem : function(e){
213        var t = e.getTarget(".x-menu-list-item", this.ul,  true);
214        if(t && t.menuItemId){
215            return this.items.get(t.menuItemId);
216        }
217    },
218
219    // private
220    onClick : function(e){
221        var t;
222        if(t = this.findTargetItem(e)){
223            if(t.menu && this.ignoreParentClicks){
224                t.expandMenu();
225            }else{
226                t.onClick(e);
227                this.fireEvent("click", this, t, e);
228            }
229        }
230    },
231
232    // private
233    setActiveItem : function(item, autoExpand){
234        if(item != this.activeItem){
235            if(this.activeItem){
236                this.activeItem.deactivate();
237            }
238            this.activeItem = item;
239            item.activate(autoExpand);
240        }else if(autoExpand){
241            item.expandMenu();
242        }
243    },
244
245    // private
246    tryActivate : function(start, step){
247        var items = this.items;
248        for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
249            var item = items.get(i);
250            if(!item.disabled && item.canActivate){
251                this.setActiveItem(item, false);
252                return item;
253            }
254        }
255        return false;
256    },
257
258    // private
259    onMouseOver : function(e){
260        var t;
261        if(t = this.findTargetItem(e)){
262            if(t.canActivate && !t.disabled){
263                this.setActiveItem(t, true);
264            }
265        }
266        this.over = true;
267        this.fireEvent("mouseover", this, e, t);
268    },
269
270    // private
271    onMouseOut : function(e){
272        var t;
273        if(t = this.findTargetItem(e)){
274            if(t == this.activeItem && t.shouldDeactivate(e)){
275                this.activeItem.deactivate();
276                delete this.activeItem;
277            }
278        }
279        this.over = false;
280        this.fireEvent("mouseout", this, e, t);
281    },
282
283    /**
284     * Read-only.  Returns true if the menu is currently displayed, else false.
285     * @type Boolean
286     */
287    isVisible : function(){
288        return this.el && !this.hidden;
289    },
290
291    /**
292     * Displays this menu relative to another element
293     * @param {Mixed} element The element to align to
294     * @param {String} position (optional) The {@link Ext.Element#alignTo} anchor position to use in aligning to
295     * the element (defaults to this.defaultAlign)
296     * @param {Ext.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
297     */
298    show : function(el, pos, parentMenu){
299        this.parentMenu = parentMenu;
300        if(!this.el){
301            this.render();
302        }
303        this.fireEvent("beforeshow", this);
304        this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
305    },
306
307    /**
308     * Displays this menu at a specific xy position
309     * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
310     * @param {Ext.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
311     */
312    showAt : function(xy, parentMenu, /* private: */_e){
313        this.parentMenu = parentMenu;
314        if(!this.el){
315            this.render();
316        }
317        if(_e !== false){
318            this.fireEvent("beforeshow", this);
319            xy = this.el.adjustForConstraints(xy);
320        }
321        this.el.setXY(xy);
322        this.el.show();
323        this.hidden = false;
324        this.focus();
325        this.fireEvent("show", this);
326    },
327
328
329
330    focus : function(){
331        if(!this.hidden){
332            this.doFocus.defer(50, this);
333        }
334    },
335
336    doFocus : function(){
337        if(!this.hidden){
338            this.focusEl.focus();
339        }
340    },
341
342    /**
343     * Hides this menu and optionally all parent menus
344     * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
345     */
346    hide : function(deep){
347        if(this.el && this.isVisible()){
348            this.fireEvent("beforehide", this);
349            if(this.activeItem){
350                this.activeItem.deactivate();
351                this.activeItem = null;
352            }
353            this.el.hide();
354            this.hidden = true;
355            this.fireEvent("hide", this);
356        }
357        if(deep === true && this.parentMenu){
358            this.parentMenu.hide(true);
359        }
360    },
361
362    /**
363     * Adds one or more items of any type supported by the Menu class, or that can be converted into menu items.
364     * Any of the following are valid:
365     * <ul>
366     * <li>Any menu item object based on {@link Ext.menu.BaseItem}</li>
367     * <li>An HTMLElement object which will be converted to a menu item</li>
368     * <li>A menu item config object that will be created as a new menu item</li>
369     * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
370     * it will be converted into a {@link Ext.menu.TextItem} and added</li>
371     * </ul>
372     * Usage:
373     * <pre><code>
374// Create the menu
375var menu = new Ext.menu.Menu();
376
377// Create a menu item to add by reference
378var menuItem = new Ext.menu.Item({ text: 'New Item!' });
379
380// Add a bunch of items at once using different methods.
381// Only the last item added will be returned.
382var item = menu.add(
383    menuItem,                // add existing item by ref
384    'Dynamic Item',          // new TextItem
385    '-',                     // new separator
386    { text: 'Config Item' }  // new item by config
387);
388</code></pre>
389     * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
390     * @return {Ext.menu.Item} The menu item that was added, or the last one if multiple items were added
391     */
392    add : function(){
393        var a = arguments, l = a.length, item;
394        for(var i = 0; i < l; i++){
395            var el = a[i];
396            if(el.render){ // some kind of Item
397                item = this.addItem(el);
398            }else if(typeof el == "string"){ // string
399                if(el == "separator" || el == "-"){
400                    item = this.addSeparator();
401                }else{
402                    item = this.addText(el);
403                }
404            }else if(el.tagName || el.el){ // element
405                item = this.addElement(el);
406            }else if(typeof el == "object"){ // must be menu item config?
407                Ext.applyIf(el, this.defaults);
408                item = this.addMenuItem(el);
409            }
410        }
411        return item;
412    },
413
414    /**
415     * Returns this menu's underlying {@link Ext.Element} object
416     * @return {Ext.Element} The element
417     */
418    getEl : function(){
419        if(!this.el){
420            this.render();
421        }
422        return this.el;
423    },
424
425    /**
426     * Adds a separator bar to the menu
427     * @return {Ext.menu.Item} The menu item that was added
428     */
429    addSeparator : function(){
430        return this.addItem(new Ext.menu.Separator());
431    },
432
433    /**
434     * Adds an {@link Ext.Element} object to the menu
435     * @param {Mixed} el The element or DOM node to add, or its id
436     * @return {Ext.menu.Item} The menu item that was added
437     */
438    addElement : function(el){
439        return this.addItem(new Ext.menu.BaseItem(el));
440    },
441
442    /**
443     * Adds an existing object based on {@link Ext.menu.BaseItem} to the menu
444     * @param {Ext.menu.Item} item The menu item to add
445     * @return {Ext.menu.Item} The menu item that was added
446     */
447    addItem : function(item){
448        this.items.add(item);
449        if(this.ul){
450            var li = document.createElement("li");
451            li.className = "x-menu-list-item";
452            this.ul.dom.appendChild(li);
453            item.render(li, this);
454            this.delayAutoWidth();
455        }
456        return item;
457    },
458
459    /**
460     * Creates a new {@link Ext.menu.Item} based an the supplied config object and adds it to the menu
461     * @param {Object} config A MenuItem config object
462     * @return {Ext.menu.Item} The menu item that was added
463     */
464    addMenuItem : function(config){
465        if(!(config instanceof Ext.menu.Item)){
466            if(typeof config.checked == "boolean"){ // must be check menu item config?
467                config = new Ext.menu.CheckItem(config);
468            }else{
469                config = new Ext.menu.Item(config);
470            }
471        }
472        return this.addItem(config);
473    },
474
475    /**
476     * Creates a new {@link Ext.menu.TextItem} with the supplied text and adds it to the menu
477     * @param {String} text The text to display in the menu item
478     * @return {Ext.menu.Item} The menu item that was added
479     */
480    addText : function(text){
481        return this.addItem(new Ext.menu.TextItem(text));
482    },
483
484    /**
485     * Inserts an existing object based on {@link Ext.menu.BaseItem} to the menu at a specified index
486     * @param {Number} index The index in the menu's list of current items where the new item should be inserted
487     * @param {Ext.menu.Item} item The menu item to add
488     * @return {Ext.menu.Item} The menu item that was added
489     */
490    insert : function(index, item){
491        this.items.insert(index, item);
492        if(this.ul){
493            var li = document.createElement("li");
494            li.className = "x-menu-list-item";
495            this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
496            item.render(li, this);
497            this.delayAutoWidth();
498        }
499        return item;
500    },
501
502    /**
503     * Removes an {@link Ext.menu.Item} from the menu and destroys the object
504     * @param {Ext.menu.Item} item The menu item to remove
505     */
506    remove : function(item){
507        this.items.removeKey(item.id);
508        item.destroy();
509    },
510
511    /**
512     * Removes and destroys all items in the menu
513     */
514    removeAll : function(){
515        if(this.items){
516                var f;
517                while(f = this.items.first()){
518                    this.remove(f);
519                }
520        }
521    },
522
523    /**
524     * Destroys the menu by  unregistering it from {@link Ext.menu.MenuMgr}, purging event listeners,
525     * removing all of the menus items, then destroying the underlying {@link Ext.Element}
526     */
527    destroy : function(){
528        this.beforeDestroy();
529        Ext.menu.MenuMgr.unregister(this);
530        if (this.keyNav) {
531                this.keyNav.disable();
532        }
533        this.removeAll();
534        if (this.ul) {
535                this.ul.removeAllListeners();
536        }
537        if (this.el) {
538                this.el.destroy();
539        }
540    },
541
542        // private
543    beforeDestroy : Ext.emptyFn
544
545});
546
547// MenuNav is a private utility class used internally by the Menu
548Ext.menu.MenuNav = function(menu){
549    Ext.menu.MenuNav.superclass.constructor.call(this, menu.el);
550    this.scope = this.menu = menu;
551};
552
553Ext.extend(Ext.menu.MenuNav, Ext.KeyNav, {
554    doRelay : function(e, h){
555        var k = e.getKey();
556        if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
557            this.menu.tryActivate(0, 1);
558            return false;
559        }
560        return h.call(this.scope || this, e, this.menu);
561    },
562
563    up : function(e, m){
564        if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
565            m.tryActivate(m.items.length-1, -1);
566        }
567    },
568
569    down : function(e, m){
570        if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
571            m.tryActivate(0, 1);
572        }
573    },
574
575    right : function(e, m){
576        if(m.activeItem){
577            m.activeItem.expandMenu(true);
578        }
579    },
580
581    left : function(e, m){
582        m.hide();
583        if(m.parentMenu && m.parentMenu.activeItem){
584            m.parentMenu.activeItem.activate();
585        }
586    },
587
588    enter : function(e, m){
589        if(m.activeItem){
590            e.stopPropagation();
591            m.activeItem.onClick(e);
592            m.fireEvent("click", this, m.activeItem);
593            return true;
594        }
595    }
596});
Note: See TracBrowser for help on using the repository browser.