]> git.pond.sub.org Git - eow/blob - static/dojo-release-1.1.1/dijit/_base/focus.js
Comment class stub
[eow] / static / dojo-release-1.1.1 / dijit / _base / focus.js
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");
4
5 // summary:
6 //              These functions are used to query or set the focus and selection.
7 //
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.
17
18 dojo.mixin(dijit,
19 {
20         // _curFocus: DomNode
21         //              Currently focused item on screen
22         _curFocus: null,
23
24         // _prevFocus: DomNode
25         //              Previously focused item on screen
26         _prevFocus: null,
27
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
34                 }else{
35                         var selection = _window.getSelection();
36                         if(dojo.isString(selection)){ // Safari
37                                 return !selection; // Boolean
38                         }else{ // Mozilla/W3
39                                 return selection.isCollapsed || !selection.toString(); // Boolean
40                         }
41                 }
42         },
43
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;
47                 if(selection){ // IE
48                         var range = selection.createRange();
49                         if(selection.type.toUpperCase()=='CONTROL'){
50                                 if(range.length){
51                                         bookmark=[];
52                                         var i=0,len=range.length;
53                                         while(i<len){
54                                                 bookmark.push(range.item(i++));
55                                         }
56                                 }else{
57                                         bookmark=null;
58                                 }
59                         }else{
60                                 bookmark = range.getBookmark();
61                         }
62                 }else{
63                         if(window.getSelection){
64                                 selection = dojo.global.getSelection();
65                                 if(selection){
66                                         range = selection.getRangeAt(0);
67                                         bookmark = range.cloneRange();
68                                 }
69                         }else{
70                                 console.warn("No idea how to store the current selection for this browser!");
71                         }
72                 }
73                 return bookmark; // Array
74         },
75
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
81                         var range;
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
85                         }else{
86                                 range = _document.selection.createRange();
87                                 range.moveToBookmark(bookmark);
88                         }
89                         range.select();
90                 }else{ //Moz/W3C
91                         var selection = dojo.global.getSelection && dojo.global.getSelection();
92                         if(selection && selection.removeAllRanges){
93                                 selection.removeAllRanges();
94                                 selection.addRange(bookmark);
95                         }else{
96                                 console.warn("No idea how to restore selection for this browser!");
97                         }
98                 }
99         },
100
101         getFocus: function(/*Widget?*/menu, /*Window?*/openedForWindow){
102                 // summary:
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
106                 //
107                 // menu:
108                 //      The menu that's being opened
109                 //
110                 // openedForWindow:
111                 //      iframe in which menu was opened
112                 //
113                 // returns:
114                 //      A handle to restore focus/selection
115
116                 return {
117                         // Node to return focus to
118                         node: menu && dojo.isDescendant(dijit._curFocus, menu.domNode) ? dijit._prevFocus : dijit._curFocus,
119
120                         // Previously selected text
121                         bookmark:
122                                 !dojo.withGlobal(openedForWindow||dojo.global, dijit.isCollapsed) ?
123                                 dojo.withGlobal(openedForWindow||dojo.global, dijit.getBookmark) :
124                                 null,
125
126                         openedForWindow: openedForWindow
127                 }; // Object
128         },
129
130         focus: function(/*Object || DomNode */ handle){
131                 // summary:
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.
134                 // handle:
135                 //              object returned by get(), or a DomNode
136
137                 if(!handle){ return; }
138
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;
142
143                 // Set the focus
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
146                 if(node){
147                         var focusNode = (node.tagName.toLowerCase()=="iframe") ? node.contentWindow : node;
148                         if(focusNode && focusNode.focus){
149                                 try{
150                                         // Gecko throws sometimes if setting focus is impossible,
151                                         // node not displayed or something like that
152                                         focusNode.focus();
153                                 }catch(e){/*quiet*/}
154                         }                       
155                         dijit._onFocusNode(node);
156                 }
157
158                 // set the selection
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)){
162                         if(openedForWindow){
163                                 openedForWindow.focus();
164                         }
165                         try{
166                                 dojo.withGlobal(openedForWindow||dojo.global, dijit.moveToBookmark, null, [bookmark]);
167                         }catch(e){
168                                 /*squelch IE internal error, see http://trac.dojotoolkit.org/ticket/1984 */
169                         }
170                 }
171         },
172
173         // _activeStack: Array
174         //              List of currently active widgets (focused widget and it's ancestors)
175         _activeStack: [],
176
177         registerWin: function(/*Window?*/targetWindow){
178                 // summary:
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.
182
183                 if(!targetWindow){
184                         targetWindow = window;
185                 }
186
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);
191                 });
192                 //dojo.connect(targetWindow, "onscroll", ???);
193
194                 // Listen for blur and focus events on targetWindow's body
195                 var body = targetWindow.document.body || targetWindow.document.getElementsByTagName("body")[0];
196                 if(body){
197                         if(dojo.isIE){
198                                 body.attachEvent('onactivate', function(evt){
199                                         if(evt.srcElement.tagName.toLowerCase() != "body"){
200                                                 dijit._onFocusNode(evt.srcElement);
201                                         }
202                                 });
203                                 body.attachEvent('ondeactivate', function(evt){ dijit._onBlurNode(evt.srcElement); });
204                         }else{
205                                 body.addEventListener('focus', function(evt){ dijit._onFocusNode(evt.target); }, true);
206                                 body.addEventListener('blur', function(evt){ dijit._onBlurNode(evt.target); }, true);
207                         }
208                 }
209                 body = null;    // prevent memory leak (apparent circular reference via closure)
210         },
211
212         _onBlurNode: function(/*DomNode*/ node){
213                 // summary:
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;
220
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.
224                         return;
225                 }
226
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);
230                 }
231                 dijit._clearActiveWidgetsTimer = setTimeout(function(){
232                         delete dijit._clearActiveWidgetsTimer;
233                         dijit._setStack([]);
234                         dijit._prevFocus = null;
235                 }, 100);
236         },
237
238         _onTouchNode: function(/*DomNode*/ node){
239                 // summary:
240                 //              Callback when node is focused or mouse-downed
241
242                 // ignore the recent blurNode event
243                 if(dijit._clearActiveWidgetsTimer){
244                         clearTimeout(dijit._clearActiveWidgetsTimer);
245                         delete dijit._clearActiveWidgetsTimer;
246                 }
247
248                 // compute stack of active widgets (ex: ComboButton --> Menu --> MenuItem)
249                 var newStack=[];
250                 try{
251                         while(node){
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
258                                                 break;
259                                         }
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;
263                                 }else{
264                                         var id = node.getAttribute && node.getAttribute("widgetId");
265                                         if(id){
266                                                 newStack.unshift(id);
267                                         }
268                                         node=node.parentNode;
269                                 }
270                         }
271                 }catch(e){ /* squelch */ }
272
273                 dijit._setStack(newStack);
274         },
275
276         _onFocusNode: function(/*DomNode*/ node){
277                 // summary
278                 //              Callback when node is focused
279                 if(node && node.tagName && node.tagName.toLowerCase() == "body"){
280                         return;
281                 }
282                 dijit._onTouchNode(node);
283
284                 if(node==dijit._curFocus){ return; }
285                 if(dijit._curFocus){
286                         dijit._prevFocus = dijit._curFocus;
287                 }
288                 dijit._curFocus = node;
289                 dojo.publish("focusNode", [node]);
290         },
291
292         _setStack: function(newStack){
293                 // summary
294                 //      The stack of active widgets has changed.  Send out appropriate events and record new stack
295
296                 var oldStack = dijit._activeStack;              
297                 dijit._activeStack = newStack;
298
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]){
302                                 break;
303                         }
304                 }
305
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]);
309                         if(widget){
310                                 widget._focused = false;
311                                 widget._hasBeenBlurred = true;
312                                 if(widget._onBlur){
313                                         widget._onBlur();
314                                 }
315                                 if (widget._setStateClass){
316                                         widget._setStateClass();
317                                 }
318                                 dojo.publish("widgetBlur", [widget]);
319                         }
320                 }
321
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]);
325                         if(widget){
326                                 widget._focused = true;
327                                 if(widget._onFocus){
328                                         widget._onFocus();
329                                 }
330                                 if (widget._setStateClass){
331                                         widget._setStateClass();
332                                 }
333                                 dojo.publish("widgetFocus", [widget]);
334                         }
335                 }
336         }
337 });
338
339 // register top window and all the iframes it contains
340 dojo.addOnLoad(dijit.registerWin);
341
342 }