1 if(!dojo._hasResource["dijit._base.focus"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2 dojo._hasResource["dijit._base.focus"] = true;
3 dojo.provide("dijit._base.focus");
6 // These functions are used to query or set the focus and selection.
8 // Also, they trace when widgets become actived/deactivated,
9 // so that the widget can fire _onFocus/_onBlur events.
10 // "Active" here means something similar to "focused", but
11 // "focus" isn't quite the right word because we keep track of
12 // a whole stack of "active" widgets. Example: Combobutton --> Menu -->
13 // MenuItem. The onBlur event for Combobutton doesn't fire due to focusing
14 // on the Menu or a MenuItem, since they are considered part of the
15 // Combobutton widget. It only happens when focus is shifted
16 // somewhere completely different.
21 // Currently focused item on screen
24 // _prevFocus: DomNode
25 // Previously focused item on screen
28 isCollapsed: function(){
29 // summary: tests whether the current selection is empty
30 var _window = dojo.global;
31 var _document = dojo.doc;
32 if(_document.selection){ // IE
33 return !_document.selection.createRange().text; // Boolean
35 var selection = _window.getSelection();
36 if(dojo.isString(selection)){ // Safari
37 return !selection; // Boolean
39 return selection.isCollapsed || !selection.toString(); // Boolean
44 getBookmark: function(){
45 // summary: Retrieves a bookmark that can be used with moveToBookmark to return to the same range
46 var bookmark, selection = dojo.doc.selection;
48 var range = selection.createRange();
49 if(selection.type.toUpperCase()=='CONTROL'){
52 var i=0,len=range.length;
54 bookmark.push(range.item(i++));
60 bookmark = range.getBookmark();
63 if(window.getSelection){
64 selection = dojo.global.getSelection();
66 range = selection.getRangeAt(0);
67 bookmark = range.cloneRange();
70 console.warn("No idea how to store the current selection for this browser!");
73 return bookmark; // Array
76 moveToBookmark: function(/*Object*/bookmark){
77 // summary: Moves current selection to a bookmark
78 // bookmark: This should be a returned object from dojo.html.selection.getBookmark()
79 var _document = dojo.doc;
80 if(_document.selection){ // IE
82 if(dojo.isArray(bookmark)){
83 range = _document.body.createControlRange();
84 dojo.forEach(bookmark, "range.addElement(item)"); //range.addElement does not have call/apply method, so can not call it directly
86 range = _document.selection.createRange();
87 range.moveToBookmark(bookmark);
91 var selection = dojo.global.getSelection && dojo.global.getSelection();
92 if(selection && selection.removeAllRanges){
93 selection.removeAllRanges();
94 selection.addRange(bookmark);
96 console.warn("No idea how to restore selection for this browser!");
101 getFocus: function(/*Widget?*/menu, /*Window?*/openedForWindow){
103 // Returns the current focus and selection.
104 // Called when a popup appears (either a top level menu or a dialog),
105 // or when a toolbar/menubar receives focus
108 // The menu that's being opened
111 // iframe in which menu was opened
114 // A handle to restore focus/selection
117 // Node to return focus to
118 node: menu && dojo.isDescendant(dijit._curFocus, menu.domNode) ? dijit._prevFocus : dijit._curFocus,
120 // Previously selected text
122 !dojo.withGlobal(openedForWindow||dojo.global, dijit.isCollapsed) ?
123 dojo.withGlobal(openedForWindow||dojo.global, dijit.getBookmark) :
126 openedForWindow: openedForWindow
130 focus: function(/*Object || DomNode */ handle){
132 // Sets the focused node and the selection according to argument.
133 // To set focus to an iframe's content, pass in the iframe itself.
135 // object returned by get(), or a DomNode
137 if(!handle){ return; }
139 var node = "node" in handle ? handle.node : handle, // because handle is either DomNode or a composite object
140 bookmark = handle.bookmark,
141 openedForWindow = handle.openedForWindow;
144 // Note that for iframe's we need to use the <iframe> to follow the parentNode chain,
145 // but we need to set focus to iframe.contentWindow
147 var focusNode = (node.tagName.toLowerCase()=="iframe") ? node.contentWindow : node;
148 if(focusNode && focusNode.focus){
150 // Gecko throws sometimes if setting focus is impossible,
151 // node not displayed or something like that
155 dijit._onFocusNode(node);
159 // do not need to restore if current selection is not empty
160 // (use keyboard to select a menu item)
161 if(bookmark && dojo.withGlobal(openedForWindow||dojo.global, dijit.isCollapsed)){
163 openedForWindow.focus();
166 dojo.withGlobal(openedForWindow||dojo.global, dijit.moveToBookmark, null, [bookmark]);
168 /*squelch IE internal error, see http://trac.dojotoolkit.org/ticket/1984 */
173 // _activeStack: Array
174 // List of currently active widgets (focused widget and it's ancestors)
177 registerWin: function(/*Window?*/targetWindow){
179 // Registers listeners on the specified window (either the main
180 // window or an iframe) to detect when the user has clicked somewhere.
181 // Anyone that creates an iframe should call this function.
184 targetWindow = window;
187 dojo.connect(targetWindow.document, "onmousedown", function(evt){
188 dijit._justMouseDowned = true;
189 setTimeout(function(){ dijit._justMouseDowned = false; }, 0);
190 dijit._onTouchNode(evt.target||evt.srcElement);
192 //dojo.connect(targetWindow, "onscroll", ???);
194 // Listen for blur and focus events on targetWindow's body
195 var body = targetWindow.document.body || targetWindow.document.getElementsByTagName("body")[0];
198 body.attachEvent('onactivate', function(evt){
199 if(evt.srcElement.tagName.toLowerCase() != "body"){
200 dijit._onFocusNode(evt.srcElement);
203 body.attachEvent('ondeactivate', function(evt){ dijit._onBlurNode(evt.srcElement); });
205 body.addEventListener('focus', function(evt){ dijit._onFocusNode(evt.target); }, true);
206 body.addEventListener('blur', function(evt){ dijit._onBlurNode(evt.target); }, true);
209 body = null; // prevent memory leak (apparent circular reference via closure)
212 _onBlurNode: function(/*DomNode*/ node){
214 // Called when focus leaves a node.
215 // Usually ignored, _unless_ it *isn't* follwed by touching another node,
216 // which indicates that we tabbed off the last field on the page,
217 // in which case every widget is marked inactive
218 dijit._prevFocus = dijit._curFocus;
219 dijit._curFocus = null;
221 if(dijit._justMouseDowned){
222 // the mouse down caused a new widget to be marked as active; this blur event
223 // is coming late, so ignore it.
227 // if the blur event isn't followed by a focus event then mark all widgets as inactive.
228 if(dijit._clearActiveWidgetsTimer){
229 clearTimeout(dijit._clearActiveWidgetsTimer);
231 dijit._clearActiveWidgetsTimer = setTimeout(function(){
232 delete dijit._clearActiveWidgetsTimer;
234 dijit._prevFocus = null;
238 _onTouchNode: function(/*DomNode*/ node){
240 // Callback when node is focused or mouse-downed
242 // ignore the recent blurNode event
243 if(dijit._clearActiveWidgetsTimer){
244 clearTimeout(dijit._clearActiveWidgetsTimer);
245 delete dijit._clearActiveWidgetsTimer;
248 // compute stack of active widgets (ex: ComboButton --> Menu --> MenuItem)
252 if(node.dijitPopupParent){
253 node=dijit.byId(node.dijitPopupParent).domNode;
254 }else if(node.tagName && node.tagName.toLowerCase()=="body"){
255 // is this the root of the document or just the root of an iframe?
256 if(node===dojo.body()){
257 // node is the root of the main document
260 // otherwise, find the iframe this node refers to (can't access it via parentNode,
261 // need to do this trick instead). window.frameElement is supported in IE/FF/Webkit
262 node=dijit.getDocumentWindow(node.ownerDocument).frameElement;
264 var id = node.getAttribute && node.getAttribute("widgetId");
266 newStack.unshift(id);
268 node=node.parentNode;
271 }catch(e){ /* squelch */ }
273 dijit._setStack(newStack);
276 _onFocusNode: function(/*DomNode*/ node){
278 // Callback when node is focused
279 if(node && node.tagName && node.tagName.toLowerCase() == "body"){
282 dijit._onTouchNode(node);
284 if(node==dijit._curFocus){ return; }
286 dijit._prevFocus = dijit._curFocus;
288 dijit._curFocus = node;
289 dojo.publish("focusNode", [node]);
292 _setStack: function(newStack){
294 // The stack of active widgets has changed. Send out appropriate events and record new stack
296 var oldStack = dijit._activeStack;
297 dijit._activeStack = newStack;
299 // compare old stack to new stack to see how many elements they have in common
300 for(var nCommon=0; nCommon<Math.min(oldStack.length, newStack.length); nCommon++){
301 if(oldStack[nCommon] != newStack[nCommon]){
306 // for all elements that have gone out of focus, send blur event
307 for(var i=oldStack.length-1; i>=nCommon; i--){
308 var widget = dijit.byId(oldStack[i]);
310 widget._focused = false;
311 widget._hasBeenBlurred = true;
315 if (widget._setStateClass){
316 widget._setStateClass();
318 dojo.publish("widgetBlur", [widget]);
322 // for all element that have come into focus, send focus event
323 for(i=nCommon; i<newStack.length; i++){
324 widget = dijit.byId(newStack[i]);
326 widget._focused = true;
330 if (widget._setStateClass){
331 widget._setStateClass();
333 dojo.publish("widgetFocus", [widget]);
339 // register top window and all the iframes it contains
340 dojo.addOnLoad(dijit.registerWin);