Subversion Repositories eFlore/Applications.cel

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
27 jpm 1
/*
2
 * Ext JS Library 2.0.2
3
 * Copyright(c) 2006-2008, 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 a as a base class
13
 * when you want a specialzed 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
 */
18
Ext.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
 
96
Ext.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
    hidden:true,
133
 
134
    createEl : function(){
135
        return new Ext.Layer({
136
            cls: "x-menu",
137
            shadow:this.shadow,
138
            constrain: false,
139
            parentEl: this.parentEl || document.body,
140
            zindex:15000
141
        });
142
    },
143
 
144
    // private
145
    render : function(){
146
        if(this.el){
147
            return;
148
        }
149
        var el = this.el = this.createEl();
150
 
151
        if(!this.keyNav){
152
            this.keyNav = new Ext.menu.MenuNav(this);
153
        }
154
        if(this.plain){
155
            el.addClass("x-menu-plain");
156
        }
157
        if(this.cls){
158
            el.addClass(this.cls);
159
        }
160
        // generic focus element
161
        this.focusEl = el.createChild({
162
            tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
163
        });
164
        var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
165
        ul.on("click", this.onClick, this);
166
        ul.on("mouseover", this.onMouseOver, this);
167
        ul.on("mouseout", this.onMouseOut, this);
168
        this.items.each(function(item){
169
            var li = document.createElement("li");
170
            li.className = "x-menu-list-item";
171
            ul.dom.appendChild(li);
172
            item.render(li, this);
173
        }, this);
174
        this.ul = ul;
175
        this.autoWidth();
176
    },
177
 
178
    // private
179
    autoWidth : function(){
180
        var el = this.el, ul = this.ul;
181
        if(!el){
182
            return;
183
        }
184
        var w = this.width;
185
        if(w){
186
            el.setWidth(w);
187
        }else if(Ext.isIE){
188
            el.setWidth(this.minWidth);
189
            var t = el.dom.offsetWidth; // force recalc
190
            el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
191
        }
192
    },
193
 
194
    // private
195
    delayAutoWidth : function(){
196
        if(this.el){
197
            if(!this.awTask){
198
                this.awTask = new Ext.util.DelayedTask(this.autoWidth, this);
199
            }
200
            this.awTask.delay(20);
201
        }
202
    },
203
 
204
    // private
205
    findTargetItem : function(e){
206
        var t = e.getTarget(".x-menu-list-item", this.ul,  true);
207
        if(t && t.menuItemId){
208
            return this.items.get(t.menuItemId);
209
        }
210
    },
211
 
212
    // private
213
    onClick : function(e){
214
        var t;
215
        if(t = this.findTargetItem(e)){
216
            t.onClick(e);
217
            this.fireEvent("click", this, t, e);
218
        }
219
    },
220
 
221
    // private
222
    setActiveItem : function(item, autoExpand){
223
        if(item != this.activeItem){
224
            if(this.activeItem){
225
                this.activeItem.deactivate();
226
            }
227
            this.activeItem = item;
228
            item.activate(autoExpand);
229
        }else if(autoExpand){
230
            item.expandMenu();
231
        }
232
    },
233
 
234
    // private
235
    tryActivate : function(start, step){
236
        var items = this.items;
237
        for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
238
            var item = items.get(i);
239
            if(!item.disabled && item.canActivate){
240
                this.setActiveItem(item, false);
241
                return item;
242
            }
243
        }
244
        return false;
245
    },
246
 
247
    // private
248
    onMouseOver : function(e){
249
        var t;
250
        if(t = this.findTargetItem(e)){
251
            if(t.canActivate && !t.disabled){
252
                this.setActiveItem(t, true);
253
            }
254
        }
255
        this.fireEvent("mouseover", this, e, t);
256
    },
257
 
258
    // private
259
    onMouseOut : function(e){
260
        var t;
261
        if(t = this.findTargetItem(e)){
262
            if(t == this.activeItem && t.shouldDeactivate(e)){
263
                this.activeItem.deactivate();
264
                delete this.activeItem;
265
            }
266
        }
267
        this.fireEvent("mouseout", this, e, t);
268
    },
269
 
270
    /**
271
     * Read-only.  Returns true if the menu is currently displayed, else false.
272
     * @type Boolean
273
     */
274
    isVisible : function(){
275
        return this.el && !this.hidden;
276
    },
277
 
278
    /**
279
     * Displays this menu relative to another element
280
     * @param {Mixed} element The element to align to
281
     * @param {String} position (optional) The {@link Ext.Element#alignTo} anchor position to use in aligning to
282
     * the element (defaults to this.defaultAlign)
283
     * @param {Ext.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
284
     */
285
    show : function(el, pos, parentMenu){
286
        this.parentMenu = parentMenu;
287
        if(!this.el){
288
            this.render();
289
        }
290
        this.fireEvent("beforeshow", this);
291
        this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
292
    },
293
 
294
    /**
295
     * Displays this menu at a specific xy position
296
     * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
297
     * @param {Ext.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
298
     */
299
    showAt : function(xy, parentMenu, /* private: */_e){
300
        this.parentMenu = parentMenu;
301
        if(!this.el){
302
            this.render();
303
        }
304
        if(_e !== false){
305
            this.fireEvent("beforeshow", this);
306
            xy = this.el.adjustForConstraints(xy);
307
        }
308
        this.el.setXY(xy);
309
        this.el.show();
310
        this.hidden = false;
311
        this.focus();
312
        this.fireEvent("show", this);
313
    },
314
 
315
 
316
 
317
    focus : function(){
318
        if(!this.hidden){
319
            this.doFocus.defer(50, this);
320
        }
321
    },
322
 
323
    doFocus : function(){
324
        if(!this.hidden){
325
            this.focusEl.focus();
326
        }
327
    },
328
 
329
    /**
330
     * Hides this menu and optionally all parent menus
331
     * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
332
     */
333
    hide : function(deep){
334
        if(this.el && this.isVisible()){
335
            this.fireEvent("beforehide", this);
336
            if(this.activeItem){
337
                this.activeItem.deactivate();
338
                this.activeItem = null;
339
            }
340
            this.el.hide();
341
            this.hidden = true;
342
            this.fireEvent("hide", this);
343
        }
344
        if(deep === true && this.parentMenu){
345
            this.parentMenu.hide(true);
346
        }
347
    },
348
 
349
    /**
350
     * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
351
     * Any of the following are valid:
352
     * <ul>
353
     * <li>Any menu item object based on {@link Ext.menu.Item}</li>
354
     * <li>An HTMLElement object which will be converted to a menu item</li>
355
     * <li>A menu item config object that will be created as a new menu item</li>
356
     * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
357
     * it will be converted into a {@link Ext.menu.TextItem} and added</li>
358
     * </ul>
359
     * Usage:
360
     * <pre><code>
361
// Create the menu
362
var menu = new Ext.menu.Menu();
363
 
364
// Create a menu item to add by reference
365
var menuItem = new Ext.menu.Item({ text: 'New Item!' });
366
 
367
// Add a bunch of items at once using different methods.
368
// Only the last item added will be returned.
369
var item = menu.add(
370
    menuItem,                // add existing item by ref
371
    'Dynamic Item',          // new TextItem
372
    '-',                     // new separator
373
    { text: 'Config Item' }  // new item by config
374
);
375
</code></pre>
376
     * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
377
     * @return {Ext.menu.Item} The menu item that was added, or the last one if multiple items were added
378
     */
379
    add : function(){
380
        var a = arguments, l = a.length, item;
381
        for(var i = 0; i < l; i++){
382
            var el = a[i];
383
            if(el.render){ // some kind of Item
384
                item = this.addItem(el);
385
            }else if(typeof el == "string"){ // string
386
                if(el == "separator" || el == "-"){
387
                    item = this.addSeparator();
388
                }else{
389
                    item = this.addText(el);
390
                }
391
            }else if(el.tagName || el.el){ // element
392
                item = this.addElement(el);
393
            }else if(typeof el == "object"){ // must be menu item config?
394
                Ext.applyIf(el, this.defaults);
395
                item = this.addMenuItem(el);
396
            }
397
        }
398
        return item;
399
    },
400
 
401
    /**
402
     * Returns this menu's underlying {@link Ext.Element} object
403
     * @return {Ext.Element} The element
404
     */
405
    getEl : function(){
406
        if(!this.el){
407
            this.render();
408
        }
409
        return this.el;
410
    },
411
 
412
    /**
413
     * Adds a separator bar to the menu
414
     * @return {Ext.menu.Item} The menu item that was added
415
     */
416
    addSeparator : function(){
417
        return this.addItem(new Ext.menu.Separator());
418
    },
419
 
420
    /**
421
     * Adds an {@link Ext.Element} object to the menu
422
     * @param {Mixed} el The element or DOM node to add, or its id
423
     * @return {Ext.menu.Item} The menu item that was added
424
     */
425
    addElement : function(el){
426
        return this.addItem(new Ext.menu.BaseItem(el));
427
    },
428
 
429
    /**
430
     * Adds an existing object based on {@link Ext.menu.Item} to the menu
431
     * @param {Ext.menu.Item} item The menu item to add
432
     * @return {Ext.menu.Item} The menu item that was added
433
     */
434
    addItem : function(item){
435
        this.items.add(item);
436
        if(this.ul){
437
            var li = document.createElement("li");
438
            li.className = "x-menu-list-item";
439
            this.ul.dom.appendChild(li);
440
            item.render(li, this);
441
            this.delayAutoWidth();
442
        }
443
        return item;
444
    },
445
 
446
    /**
447
     * Creates a new {@link Ext.menu.Item} based an the supplied config object and adds it to the menu
448
     * @param {Object} config A MenuItem config object
449
     * @return {Ext.menu.Item} The menu item that was added
450
     */
451
    addMenuItem : function(config){
452
        if(!(config instanceof Ext.menu.Item)){
453
            if(typeof config.checked == "boolean"){ // must be check menu item config?
454
                config = new Ext.menu.CheckItem(config);
455
            }else{
456
                config = new Ext.menu.Item(config);
457
            }
458
        }
459
        return this.addItem(config);
460
    },
461
 
462
    /**
463
     * Creates a new {@link Ext.menu.TextItem} with the supplied text and adds it to the menu
464
     * @param {String} text The text to display in the menu item
465
     * @return {Ext.menu.Item} The menu item that was added
466
     */
467
    addText : function(text){
468
        return this.addItem(new Ext.menu.TextItem(text));
469
    },
470
 
471
    /**
472
     * Inserts an existing object based on {@link Ext.menu.Item} to the menu at a specified index
473
     * @param {Number} index The index in the menu's list of current items where the new item should be inserted
474
     * @param {Ext.menu.Item} item The menu item to add
475
     * @return {Ext.menu.Item} The menu item that was added
476
     */
477
    insert : function(index, item){
478
        this.items.insert(index, item);
479
        if(this.ul){
480
            var li = document.createElement("li");
481
            li.className = "x-menu-list-item";
482
            this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
483
            item.render(li, this);
484
            this.delayAutoWidth();
485
        }
486
        return item;
487
    },
488
 
489
    /**
490
     * Removes an {@link Ext.menu.Item} from the menu and destroys the object
491
     * @param {Ext.menu.Item} item The menu item to remove
492
     */
493
    remove : function(item){
494
        this.items.removeKey(item.id);
495
        item.destroy();
496
    },
497
 
498
    /**
499
     * Removes and destroys all items in the menu
500
     */
501
    removeAll : function(){
502
        var f;
503
        while(f = this.items.first()){
504
            this.remove(f);
505
        }
506
    },
507
 
508
    /**
509
     * Destroys the menu by  unregistering it from {@link Ext.menu.MenuMgr}, purging event listeners,
510
     * removing all of the menus items, then destroying the underlying {@link Ext.Element}
511
     */
512
    destroy : function(){
513
        this.beforeDestroy();
514
        Ext.menu.MenuMgr.unregister(this);
515
        if (this.keyNav) {
516
        	this.keyNav.disable();
517
        }
518
        this.removeAll();
519
        if (this.ul) {
520
        	this.ul.removeAllListeners();
521
        }
522
        if (this.el) {
523
        	this.el.destroy();
524
        }
525
    },
526
 
527
	// private
528
    beforeDestroy : Ext.emptyFn
529
 
530
});
531
 
532
// MenuNav is a private utility class used internally by the Menu
533
Ext.menu.MenuNav = function(menu){
534
    Ext.menu.MenuNav.superclass.constructor.call(this, menu.el);
535
    this.scope = this.menu = menu;
536
};
537
 
538
Ext.extend(Ext.menu.MenuNav, Ext.KeyNav, {
539
    doRelay : function(e, h){
540
        var k = e.getKey();
541
        if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
542
            this.menu.tryActivate(0, 1);
543
            return false;
544
        }
545
        return h.call(this.scope || this, e, this.menu);
546
    },
547
 
548
    up : function(e, m){
549
        if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
550
            m.tryActivate(m.items.length-1, -1);
551
        }
552
    },
553
 
554
    down : function(e, m){
555
        if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
556
            m.tryActivate(0, 1);
557
        }
558
    },
559
 
560
    right : function(e, m){
561
        if(m.activeItem){
562
            m.activeItem.expandMenu(true);
563
        }
564
    },
565
 
566
    left : function(e, m){
567
        m.hide();
568
        if(m.parentMenu && m.parentMenu.activeItem){
569
            m.parentMenu.activeItem.activate();
570
        }
571
    },
572
 
573
    enter : function(e, m){
574
        if(m.activeItem){
575
            e.stopPropagation();
576
            m.activeItem.onClick(e);
577
            m.fireEvent("click", this, m.activeItem);
578
            return true;
579
        }
580
    }
581
});