]> git.wh0rd.org - tt-rss.git/blame - lib/dijit/Menu.js
upgrade Dojo to 1.6.1
[tt-rss.git] / lib / dijit / Menu.js
CommitLineData
2f01fe57 1/*
81bea17a 2 Copyright (c) 2004-2011, The Dojo Foundation All Rights Reserved.
2f01fe57
AD
3 Available via Academic Free License >= 2.1 OR the modified BSD license.
4 see: http://dojotoolkit.org/license for details
5*/
6
7
81bea17a
AD
8if(!dojo._hasResource["dijit.Menu"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
9dojo._hasResource["dijit.Menu"] = true;
2f01fe57
AD
10dojo.provide("dijit.Menu");
11dojo.require("dojo.window");
12dojo.require("dijit._Widget");
13dojo.require("dijit._KeyNavContainer");
14dojo.require("dijit._Templated");
2f01fe57
AD
15dojo.require("dijit.MenuItem");
16dojo.require("dijit.PopupMenuItem");
17dojo.require("dijit.CheckedMenuItem");
18dojo.require("dijit.MenuSeparator");
81bea17a
AD
19
20
21// "dijit/MenuItem", "dijit/PopupMenuItem", "dijit/CheckedMenuItem", "dijit/MenuSeparator" for Back-compat (TODO: remove in 2.0)
22
23dojo.declare("dijit._MenuBase",
24 [dijit._Widget, dijit._Templated, dijit._KeyNavContainer],
25{
26 // summary:
27 // Base class for Menu and MenuBar
28
29 // parentMenu: [readonly] Widget
30 // pointer to menu that displayed me
31 parentMenu: null,
32
33 // popupDelay: Integer
34 // number of milliseconds before hovering (without clicking) causes the popup to automatically open.
35 popupDelay: 500,
36
37 startup: function(){
38 if(this._started){ return; }
39
40 dojo.forEach(this.getChildren(), function(child){ child.startup(); });
41 this.startupKeyNavChildren();
42
43 this.inherited(arguments);
44 },
45
46 onExecute: function(){
47 // summary:
48 // Attach point for notification about when a menu item has been executed.
49 // This is an internal mechanism used for Menus to signal to their parent to
50 // close them, because they are about to execute the onClick handler. In
51 // general developers should not attach to or override this method.
52 // tags:
53 // protected
54 },
55
56 onCancel: function(/*Boolean*/ closeAll){
57 // summary:
58 // Attach point for notification about when the user cancels the current menu
59 // This is an internal mechanism used for Menus to signal to their parent to
60 // close them. In general developers should not attach to or override this method.
61 // tags:
62 // protected
63 },
64
65 _moveToPopup: function(/*Event*/ evt){
66 // summary:
67 // This handles the right arrow key (left arrow key on RTL systems),
68 // which will either open a submenu, or move to the next item in the
69 // ancestor MenuBar
70 // tags:
71 // private
72
73 if(this.focusedChild && this.focusedChild.popup && !this.focusedChild.disabled){
74 this.focusedChild._onClick(evt);
75 }else{
76 var topMenu = this._getTopMenu();
77 if(topMenu && topMenu._isMenuBar){
78 topMenu.focusNext();
79 }
80 }
81 },
82
83 _onPopupHover: function(/*Event*/ evt){
84 // summary:
85 // This handler is called when the mouse moves over the popup.
86 // tags:
87 // private
88
89 // if the mouse hovers over a menu popup that is in pending-close state,
90 // then stop the close operation.
91 // This can't be done in onItemHover since some popup targets don't have MenuItems (e.g. ColorPicker)
92 if(this.currentPopup && this.currentPopup._pendingClose_timer){
93 var parentMenu = this.currentPopup.parentMenu;
94 // highlight the parent menu item pointing to this popup
95 if(parentMenu.focusedChild){
96 parentMenu.focusedChild._setSelected(false);
97 }
98 parentMenu.focusedChild = this.currentPopup.from_item;
99 parentMenu.focusedChild._setSelected(true);
100 // cancel the pending close
101 this._stopPendingCloseTimer(this.currentPopup);
102 }
103 },
104
105 onItemHover: function(/*MenuItem*/ item){
106 // summary:
107 // Called when cursor is over a MenuItem.
108 // tags:
109 // protected
110
111 // Don't do anything unless user has "activated" the menu by:
112 // 1) clicking it
113 // 2) opening it from a parent menu (which automatically focuses it)
114 if(this.isActive){
115 this.focusChild(item);
116 if(this.focusedChild.popup && !this.focusedChild.disabled && !this.hover_timer){
117 this.hover_timer = setTimeout(dojo.hitch(this, "_openPopup"), this.popupDelay);
118 }
119 }
120 // if the user is mixing mouse and keyboard navigation,
121 // then the menu may not be active but a menu item has focus,
122 // but it's not the item that the mouse just hovered over.
123 // To avoid both keyboard and mouse selections, use the latest.
124 if(this.focusedChild){
125 this.focusChild(item);
126 }
127 this._hoveredChild = item;
128 },
129
130 _onChildBlur: function(item){
131 // summary:
132 // Called when a child MenuItem becomes inactive because focus
133 // has been removed from the MenuItem *and* it's descendant menus.
134 // tags:
135 // private
136 this._stopPopupTimer();
137 item._setSelected(false);
138 // Close all popups that are open and descendants of this menu
139 var itemPopup = item.popup;
140 if(itemPopup){
141 this._stopPendingCloseTimer(itemPopup);
142 itemPopup._pendingClose_timer = setTimeout(function(){
143 itemPopup._pendingClose_timer = null;
144 if(itemPopup.parentMenu){
145 itemPopup.parentMenu.currentPopup = null;
146 }
147 dijit.popup.close(itemPopup); // this calls onClose
148 }, this.popupDelay);
149 }
150 },
151
152 onItemUnhover: function(/*MenuItem*/ item){
153 // summary:
154 // Callback fires when mouse exits a MenuItem
155 // tags:
156 // protected
157
158 if(this.isActive){
159 this._stopPopupTimer();
160 }
161 if(this._hoveredChild == item){ this._hoveredChild = null; }
162 },
163
164 _stopPopupTimer: function(){
165 // summary:
166 // Cancels the popup timer because the user has stop hovering
167 // on the MenuItem, etc.
168 // tags:
169 // private
170 if(this.hover_timer){
171 clearTimeout(this.hover_timer);
172 this.hover_timer = null;
173 }
174 },
175
176 _stopPendingCloseTimer: function(/*dijit._Widget*/ popup){
177 // summary:
178 // Cancels the pending-close timer because the close has been preempted
179 // tags:
180 // private
181 if(popup._pendingClose_timer){
182 clearTimeout(popup._pendingClose_timer);
183 popup._pendingClose_timer = null;
184 }
185 },
186
187 _stopFocusTimer: function(){
188 // summary:
189 // Cancels the pending-focus timer because the menu was closed before focus occured
190 // tags:
191 // private
192 if(this._focus_timer){
193 clearTimeout(this._focus_timer);
194 this._focus_timer = null;
195 }
196 },
197
198 _getTopMenu: function(){
199 // summary:
200 // Returns the top menu in this chain of Menus
201 // tags:
202 // private
203 for(var top=this; top.parentMenu; top=top.parentMenu);
204 return top;
205 },
206
207 onItemClick: function(/*dijit._Widget*/ item, /*Event*/ evt){
208 // summary:
209 // Handle clicks on an item.
210 // tags:
211 // private
212
213 // this can't be done in _onFocus since the _onFocus events occurs asynchronously
214 if(typeof this.isShowingNow == 'undefined'){ // non-popup menu
215 this._markActive();
216 }
217
218 this.focusChild(item);
219
220 if(item.disabled){ return false; }
221
222 if(item.popup){
223 this._openPopup();
224 }else{
225 // before calling user defined handler, close hierarchy of menus
226 // and restore focus to place it was when menu was opened
227 this.onExecute();
228
229 // user defined handler for click
230 item.onClick(evt);
231 }
232 },
233
234 _openPopup: function(){
235 // summary:
236 // Open the popup to the side of/underneath the current menu item
237 // tags:
238 // protected
239
240 this._stopPopupTimer();
241 var from_item = this.focusedChild;
242 if(!from_item){ return; } // the focused child lost focus since the timer was started
243 var popup = from_item.popup;
244 if(popup.isShowingNow){ return; }
245 if(this.currentPopup){
246 this._stopPendingCloseTimer(this.currentPopup);
247 dijit.popup.close(this.currentPopup);
248 }
249 popup.parentMenu = this;
250 popup.from_item = from_item; // helps finding the parent item that should be focused for this popup
251 var self = this;
252 dijit.popup.open({
253 parent: this,
254 popup: popup,
255 around: from_item.domNode,
256 orient: this._orient || (this.isLeftToRight() ?
257 {'TR': 'TL', 'TL': 'TR', 'BR': 'BL', 'BL': 'BR'} :
258 {'TL': 'TR', 'TR': 'TL', 'BL': 'BR', 'BR': 'BL'}),
259 onCancel: function(){ // called when the child menu is canceled
260 // set isActive=false (_closeChild vs _cleanUp) so that subsequent hovering will NOT open child menus
261 // which seems aligned with the UX of most applications (e.g. notepad, wordpad, paint shop pro)
262 self.focusChild(from_item); // put focus back on my node
263 self._cleanUp(); // close the submenu (be sure this is done _after_ focus is moved)
264 from_item._setSelected(true); // oops, _cleanUp() deselected the item
265 self.focusedChild = from_item; // and unset focusedChild
266 },
267 onExecute: dojo.hitch(this, "_cleanUp")
268 });
269
270 this.currentPopup = popup;
271 // detect mouseovers to handle lazy mouse movements that temporarily focus other menu items
272 popup.connect(popup.domNode, "onmouseenter", dojo.hitch(self, "_onPopupHover")); // cleaned up when the popped-up widget is destroyed on close
273
274 if(popup.focus){
275 // If user is opening the popup via keyboard (right arrow, or down arrow for MenuBar),
276 // if the cursor happens to collide with the popup, it will generate an onmouseover event
277 // even though the mouse wasn't moved. Use a setTimeout() to call popup.focus so that
278 // our focus() call overrides the onmouseover event, rather than vice-versa. (#8742)
279 popup._focus_timer = setTimeout(dojo.hitch(popup, function(){
280 this._focus_timer = null;
281 this.focus();
282 }), 0);
283 }
284 },
285
286 _markActive: function(){
287 // summary:
288 // Mark this menu's state as active.
289 // Called when this Menu gets focus from:
290 // 1) clicking it (mouse or via space/arrow key)
291 // 2) being opened by a parent menu.
292 // This is not called just from mouse hover.
293 // Focusing a menu via TAB does NOT automatically set isActive
294 // since TAB is a navigation operation and not a selection one.
295 // For Windows apps, pressing the ALT key focuses the menubar
296 // menus (similar to TAB navigation) but the menu is not active
297 // (ie no dropdown) until an item is clicked.
298 this.isActive = true;
299 dojo.replaceClass(this.domNode, "dijitMenuActive", "dijitMenuPassive");
300 },
301
302 onOpen: function(/*Event*/ e){
303 // summary:
304 // Callback when this menu is opened.
305 // This is called by the popup manager as notification that the menu
306 // was opened.
307 // tags:
308 // private
309
310 this.isShowingNow = true;
311 this._markActive();
312 },
313
314 _markInactive: function(){
315 // summary:
316 // Mark this menu's state as inactive.
317 this.isActive = false; // don't do this in _onBlur since the state is pending-close until we get here
318 dojo.replaceClass(this.domNode, "dijitMenuPassive", "dijitMenuActive");
319 },
320
321 onClose: function(){
322 // summary:
323 // Callback when this menu is closed.
324 // This is called by the popup manager as notification that the menu
325 // was closed.
326 // tags:
327 // private
328
329 this._stopFocusTimer();
330 this._markInactive();
331 this.isShowingNow = false;
332 this.parentMenu = null;
333 },
334
335 _closeChild: function(){
336 // summary:
337 // Called when submenu is clicked or focus is lost. Close hierarchy of menus.
338 // tags:
339 // private
340 this._stopPopupTimer();
341
342 var fromItem = this.focusedChild && this.focusedChild.from_item;
343
344 if(this.currentPopup){
345 // If focus is on my child menu then move focus to me,
346 // because IE doesn't like it when you display:none a node with focus
347 if(dijit._curFocus && dojo.isDescendant(dijit._curFocus, this.currentPopup.domNode)){
348 this.focusedChild.focusNode.focus();
349 }
350 // Close all popups that are open and descendants of this menu
351 dijit.popup.close(this.currentPopup);
352 this.currentPopup = null;
353 }
354
355 if(this.focusedChild){ // unhighlight the focused item
356 this.focusedChild._setSelected(false);
357 this.focusedChild._onUnhover();
358 this.focusedChild = null;
359 }
360 },
361
362 _onItemFocus: function(/*MenuItem*/ item){
363 // summary:
364 // Called when child of this Menu gets focus from:
365 // 1) clicking it
366 // 2) tabbing into it
367 // 3) being opened by a parent menu.
368 // This is not called just from mouse hover.
369 if(this._hoveredChild && this._hoveredChild != item){
370 this._hoveredChild._onUnhover(); // any previous mouse movement is trumped by focus selection
371 }
372 },
373
374 _onBlur: function(){
375 // summary:
376 // Called when focus is moved away from this Menu and it's submenus.
377 // tags:
378 // protected
379 this._cleanUp();
380 this.inherited(arguments);
381 },
382
383 _cleanUp: function(){
384 // summary:
385 // Called when the user is done with this menu. Closes hierarchy of menus.
386 // tags:
387 // private
388
389 this._closeChild(); // don't call this.onClose since that's incorrect for MenuBar's that never close
390 if(typeof this.isShowingNow == 'undefined'){ // non-popup menu doesn't call onClose
391 this._markInactive();
392 }
393 }
394});
395
396dojo.declare("dijit.Menu",
397 dijit._MenuBase,
398 {
399 // summary
400 // A context menu you can assign to multiple elements
401
402 // TODO: most of the code in here is just for context menu (right-click menu)
403 // support. In retrospect that should have been a separate class (dijit.ContextMenu).
404 // Split them for 2.0
405
406 constructor: function(){
407 this._bindings = [];
408 },
409
410 templateString: dojo.cache("dijit", "templates/Menu.html", "<table class=\"dijit dijitMenu dijitMenuPassive dijitReset dijitMenuTable\" role=\"menu\" tabIndex=\"${tabIndex}\" dojoAttachEvent=\"onkeypress:_onKeyPress\" cellspacing=\"0\">\n\t<tbody class=\"dijitReset\" dojoAttachPoint=\"containerNode\"></tbody>\n</table>\n"),
411
412 baseClass: "dijitMenu",
413
414 // targetNodeIds: [const] String[]
415 // Array of dom node ids of nodes to attach to.
416 // Fill this with nodeIds upon widget creation and it becomes context menu for those nodes.
417 targetNodeIds: [],
418
419 // contextMenuForWindow: [const] Boolean
420 // If true, right clicking anywhere on the window will cause this context menu to open.
421 // If false, must specify targetNodeIds.
422 contextMenuForWindow: false,
423
424 // leftClickToOpen: [const] Boolean
425 // If true, menu will open on left click instead of right click, similiar to a file menu.
426 leftClickToOpen: false,
427
428 // refocus: Boolean
429 // When this menu closes, re-focus the element which had focus before it was opened.
430 refocus: true,
431
432 postCreate: function(){
433 if(this.contextMenuForWindow){
434 this.bindDomNode(dojo.body());
435 }else{
436 // TODO: should have _setTargetNodeIds() method to handle initialization and a possible
437 // later set('targetNodeIds', ...) call. There's also a problem that targetNodeIds[]
438 // gets stale after calls to bindDomNode()/unBindDomNode() as it still is just the original list (see #9610)
439 dojo.forEach(this.targetNodeIds, this.bindDomNode, this);
440 }
441 var k = dojo.keys, l = this.isLeftToRight();
442 this._openSubMenuKey = l ? k.RIGHT_ARROW : k.LEFT_ARROW;
443 this._closeSubMenuKey = l ? k.LEFT_ARROW : k.RIGHT_ARROW;
444 this.connectKeyNavHandlers([k.UP_ARROW], [k.DOWN_ARROW]);
445 },
446
447 _onKeyPress: function(/*Event*/ evt){
448 // summary:
449 // Handle keyboard based menu navigation.
450 // tags:
451 // protected
452
453 if(evt.ctrlKey || evt.altKey){ return; }
454
455 switch(evt.charOrCode){
456 case this._openSubMenuKey:
457 this._moveToPopup(evt);
458 dojo.stopEvent(evt);
459 break;
460 case this._closeSubMenuKey:
461 if(this.parentMenu){
462 if(this.parentMenu._isMenuBar){
463 this.parentMenu.focusPrev();
464 }else{
465 this.onCancel(false);
466 }
467 }else{
468 dojo.stopEvent(evt);
469 }
470 break;
471 }
472 },
473
474 // thanks burstlib!
475 _iframeContentWindow: function(/* HTMLIFrameElement */iframe_el){
476 // summary:
477 // Returns the window reference of the passed iframe
478 // tags:
479 // private
480 var win = dojo.window.get(this._iframeContentDocument(iframe_el)) ||
481 // Moz. TODO: is this available when defaultView isn't?
482 this._iframeContentDocument(iframe_el)['__parent__'] ||
483 (iframe_el.name && dojo.doc.frames[iframe_el.name]) || null;
484 return win; // Window
485 },
486
487 _iframeContentDocument: function(/* HTMLIFrameElement */iframe_el){
488 // summary:
489 // Returns a reference to the document object inside iframe_el
490 // tags:
491 // protected
492 var doc = iframe_el.contentDocument // W3
493 || (iframe_el.contentWindow && iframe_el.contentWindow.document) // IE
494 || (iframe_el.name && dojo.doc.frames[iframe_el.name] && dojo.doc.frames[iframe_el.name].document)
495 || null;
496 return doc; // HTMLDocument
497 },
498
499 bindDomNode: function(/*String|DomNode*/ node){
500 // summary:
501 // Attach menu to given node
502 node = dojo.byId(node);
503
504 var cn; // Connect node
505
506 // Support context menus on iframes. Rather than binding to the iframe itself we need
507 // to bind to the <body> node inside the iframe.
508 if(node.tagName.toLowerCase() == "iframe"){
509 var iframe = node,
510 win = this._iframeContentWindow(iframe);
511 cn = dojo.withGlobal(win, dojo.body);
512 }else{
513
514 // To capture these events at the top level, attach to <html>, not <body>.
515 // Otherwise right-click context menu just doesn't work.
516 cn = (node == dojo.body() ? dojo.doc.documentElement : node);
517 }
518
519
520 // "binding" is the object to track our connection to the node (ie, the parameter to bindDomNode())
521 var binding = {
522 node: node,
523 iframe: iframe
524 };
525
526 // Save info about binding in _bindings[], and make node itself record index(+1) into
527 // _bindings[] array. Prefix w/_dijitMenu to avoid setting an attribute that may
528 // start with a number, which fails on FF/safari.
529 dojo.attr(node, "_dijitMenu" + this.id, this._bindings.push(binding));
530
531 // Setup the connections to monitor click etc., unless we are connecting to an iframe which hasn't finished
532 // loading yet, in which case we need to wait for the onload event first, and then connect
533 // On linux Shift-F10 produces the oncontextmenu event, but on Windows it doesn't, so
534 // we need to monitor keyboard events in addition to the oncontextmenu event.
535 var doConnects = dojo.hitch(this, function(cn){
536 return [
537 // TODO: when leftClickToOpen is true then shouldn't space/enter key trigger the menu,
538 // rather than shift-F10?
539 dojo.connect(cn, this.leftClickToOpen ? "onclick" : "oncontextmenu", this, function(evt){
540 // Schedule context menu to be opened unless it's already been scheduled from onkeydown handler
541 dojo.stopEvent(evt);
542 this._scheduleOpen(evt.target, iframe, {x: evt.pageX, y: evt.pageY});
543 }),
544 dojo.connect(cn, "onkeydown", this, function(evt){
545 if(evt.shiftKey && evt.keyCode == dojo.keys.F10){
546 dojo.stopEvent(evt);
547 this._scheduleOpen(evt.target, iframe); // no coords - open near target node
548 }
549 })
550 ];
551 });
552 binding.connects = cn ? doConnects(cn) : [];
553
554 if(iframe){
555 // Setup handler to [re]bind to the iframe when the contents are initially loaded,
556 // and every time the contents change.
557 // Need to do this b/c we are actually binding to the iframe's <body> node.
558 // Note: can't use dojo.connect(), see #9609.
559
560 binding.onloadHandler = dojo.hitch(this, function(){
561 // want to remove old connections, but IE throws exceptions when trying to
562 // access the <body> node because it's already gone, or at least in a state of limbo
563
564 var win = this._iframeContentWindow(iframe);
565 cn = dojo.withGlobal(win, dojo.body);
566 binding.connects = doConnects(cn);
567 });
568 if(iframe.addEventListener){
569 iframe.addEventListener("load", binding.onloadHandler, false);
570 }else{
571 iframe.attachEvent("onload", binding.onloadHandler);
572 }
573 }
574 },
575
576 unBindDomNode: function(/*String|DomNode*/ nodeName){
577 // summary:
578 // Detach menu from given node
579
580 var node;
581 try{
582 node = dojo.byId(nodeName);
583 }catch(e){
584 // On IE the dojo.byId() call will get an exception if the attach point was
585 // the <body> node of an <iframe> that has since been reloaded (and thus the
586 // <body> node is in a limbo state of destruction.
587 return;
588 }
589
590 // node["_dijitMenu" + this.id] contains index(+1) into my _bindings[] array
591 var attrName = "_dijitMenu" + this.id;
592 if(node && dojo.hasAttr(node, attrName)){
593 var bid = dojo.attr(node, attrName)-1, b = this._bindings[bid];
594 dojo.forEach(b.connects, dojo.disconnect);
595
596 // Remove listener for iframe onload events
597 var iframe = b.iframe;
598 if(iframe){
599 if(iframe.removeEventListener){
600 iframe.removeEventListener("load", b.onloadHandler, false);
601 }else{
602 iframe.detachEvent("onload", b.onloadHandler);
603 }
604 }
605
606 dojo.removeAttr(node, attrName);
607 delete this._bindings[bid];
608 }
609 },
610
611 _scheduleOpen: function(/*DomNode?*/ target, /*DomNode?*/ iframe, /*Object?*/ coords){
612 // summary:
613 // Set timer to display myself. Using a timer rather than displaying immediately solves
614 // two problems:
615 //
616 // 1. IE: without the delay, focus work in "open" causes the system
617 // context menu to appear in spite of stopEvent.
618 //
619 // 2. Avoid double-shows on linux, where shift-F10 generates an oncontextmenu event
620 // even after a dojo.stopEvent(e). (Shift-F10 on windows doesn't generate the
621 // oncontextmenu event.)
622
623 if(!this._openTimer){
624 this._openTimer = setTimeout(dojo.hitch(this, function(){
625 delete this._openTimer;
626 this._openMyself({
627 target: target,
628 iframe: iframe,
629 coords: coords
630 });
631 }), 1);
632 }
633 },
634
635 _openMyself: function(args){
636 // summary:
637 // Internal function for opening myself when the user does a right-click or something similar.
638 // args:
639 // This is an Object containing:
640 // * target:
641 // The node that is being clicked
642 // * iframe:
643 // If an <iframe> is being clicked, iframe points to that iframe
644 // * coords:
645 // Put menu at specified x/y position in viewport, or if iframe is
646 // specified, then relative to iframe.
647 //
648 // _openMyself() formerly took the event object, and since various code references
649 // evt.target (after connecting to _openMyself()), using an Object for parameters
650 // (so that old code still works).
651
652 var target = args.target,
653 iframe = args.iframe,
654 coords = args.coords;
655
656 // Get coordinates to open menu, either at specified (mouse) position or (if triggered via keyboard)
657 // then near the node the menu is assigned to.
658 if(coords){
659 if(iframe){
660 // Specified coordinates are on <body> node of an <iframe>, convert to match main document
661 var od = target.ownerDocument,
662 ifc = dojo.position(iframe, true),
663 win = this._iframeContentWindow(iframe),
664 scroll = dojo.withGlobal(win, "_docScroll", dojo);
665
666 var cs = dojo.getComputedStyle(iframe),
667 tp = dojo._toPixelValue,
668 left = (dojo.isIE && dojo.isQuirks ? 0 : tp(iframe, cs.paddingLeft)) + (dojo.isIE && dojo.isQuirks ? tp(iframe, cs.borderLeftWidth) : 0),
669 top = (dojo.isIE && dojo.isQuirks ? 0 : tp(iframe, cs.paddingTop)) + (dojo.isIE && dojo.isQuirks ? tp(iframe, cs.borderTopWidth) : 0);
670
671 coords.x += ifc.x + left - scroll.x;
672 coords.y += ifc.y + top - scroll.y;
673 }
674 }else{
675 coords = dojo.position(target, true);
676 coords.x += 10;
677 coords.y += 10;
678 }
679
680 var self=this;
681 var savedFocus = dijit.getFocus(this);
682 function closeAndRestoreFocus(){
683 // user has clicked on a menu or popup
684 if(self.refocus){
685 dijit.focus(savedFocus);
686 }
687 dijit.popup.close(self);
688 }
689 dijit.popup.open({
690 popup: this,
691 x: coords.x,
692 y: coords.y,
693 onExecute: closeAndRestoreFocus,
694 onCancel: closeAndRestoreFocus,
695 orient: this.isLeftToRight() ? 'L' : 'R'
696 });
697 this.focus();
698
699 this._onBlur = function(){
700 this.inherited('_onBlur', arguments);
701 // Usually the parent closes the child widget but if this is a context
702 // menu then there is no parent
703 dijit.popup.close(this);
704 // don't try to restore focus; user has clicked another part of the screen
705 // and set focus there
706 };
707 },
708
709 uninitialize: function(){
710 dojo.forEach(this._bindings, function(b){ if(b){ this.unBindDomNode(b.node); } }, this);
711 this.inherited(arguments);
712 }
713}
714);
715
2f01fe57 716}