source: Dev/branches/rest-dojo-ui/client/dijit/_base/focus.js @ 256

Last change on this file since 256 was 256, checked in by hendrikvanantwerpen, 13 years ago

Reworked project structure based on REST interaction and Dojo library. As
soon as this is stable, the old jQueryUI branch can be removed (it's
kept for reference).

File size: 10.2 KB
Line 
1define([
2        "dojo/_base/array", // array.forEach
3        "dojo/dom", // dom.isDescendant
4        "dojo/_base/lang", // lang.isArray
5        "dojo/topic", // publish
6        "dojo/_base/window", // win.doc win.doc.selection win.global win.global.getSelection win.withGlobal
7        "../focus",
8        ".."    // for exporting symbols to dijit
9], function(array, dom, lang, topic, win, focus, dijit){
10
11        // module:
12        //              dijit/_base/focus
13        // summary:
14        //              Deprecated module to monitor currently focused node and stack of currently focused widgets.
15        //              New code should access dijit/focus directly.
16
17        lang.mixin(dijit, {
18                // _curFocus: DomNode
19                //              Currently focused item on screen
20                _curFocus: null,
21
22                // _prevFocus: DomNode
23                //              Previously focused item on screen
24                _prevFocus: null,
25
26                isCollapsed: function(){
27                        // summary:
28                        //              Returns true if there is no text selected
29                        return dijit.getBookmark().isCollapsed;
30                },
31
32                getBookmark: function(){
33                        // summary:
34                        //              Retrieves a bookmark that can be used with moveToBookmark to return to the same range
35                        var bm, rg, tg, sel = win.doc.selection, cf = focus.curNode;
36
37                        if(win.global.getSelection){
38                                //W3C Range API for selections.
39                                sel = win.global.getSelection();
40                                if(sel){
41                                        if(sel.isCollapsed){
42                                                tg = cf? cf.tagName : "";
43                                                if(tg){
44                                                        //Create a fake rangelike item to restore selections.
45                                                        tg = tg.toLowerCase();
46                                                        if(tg == "textarea" ||
47                                                                        (tg == "input" && (!cf.type || cf.type.toLowerCase() == "text"))){
48                                                                sel = {
49                                                                        start: cf.selectionStart,
50                                                                        end: cf.selectionEnd,
51                                                                        node: cf,
52                                                                        pRange: true
53                                                                };
54                                                                return {isCollapsed: (sel.end <= sel.start), mark: sel}; //Object.
55                                                        }
56                                                }
57                                                bm = {isCollapsed:true};
58                                                if(sel.rangeCount){
59                                                        bm.mark = sel.getRangeAt(0).cloneRange();
60                                                }
61                                        }else{
62                                                rg = sel.getRangeAt(0);
63                                                bm = {isCollapsed: false, mark: rg.cloneRange()};
64                                        }
65                                }
66                        }else if(sel){
67                                // If the current focus was a input of some sort and no selection, don't bother saving
68                                // a native bookmark.  This is because it causes issues with dialog/page selection restore.
69                                // So, we need to create psuedo bookmarks to work with.
70                                tg = cf ? cf.tagName : "";
71                                tg = tg.toLowerCase();
72                                if(cf && tg && (tg == "button" || tg == "textarea" || tg == "input")){
73                                        if(sel.type && sel.type.toLowerCase() == "none"){
74                                                return {
75                                                        isCollapsed: true,
76                                                        mark: null
77                                                }
78                                        }else{
79                                                rg = sel.createRange();
80                                                return {
81                                                        isCollapsed: rg.text && rg.text.length?false:true,
82                                                        mark: {
83                                                                range: rg,
84                                                                pRange: true
85                                                        }
86                                                };
87                                        }
88                                }
89                                bm = {};
90
91                                //'IE' way for selections.
92                                try{
93                                        // createRange() throws exception when dojo in iframe
94                                        //and nothing selected, see #9632
95                                        rg = sel.createRange();
96                                        bm.isCollapsed = !(sel.type == 'Text' ? rg.htmlText.length : rg.length);
97                                }catch(e){
98                                        bm.isCollapsed = true;
99                                        return bm;
100                                }
101                                if(sel.type.toUpperCase() == 'CONTROL'){
102                                        if(rg.length){
103                                                bm.mark=[];
104                                                var i=0,len=rg.length;
105                                                while(i<len){
106                                                        bm.mark.push(rg.item(i++));
107                                                }
108                                        }else{
109                                                bm.isCollapsed = true;
110                                                bm.mark = null;
111                                        }
112                                }else{
113                                        bm.mark = rg.getBookmark();
114                                }
115                        }else{
116                                console.warn("No idea how to store the current selection for this browser!");
117                        }
118                        return bm; // Object
119                },
120
121                moveToBookmark: function(/*Object*/ bookmark){
122                        // summary:
123                        //              Moves current selection to a bookmark
124                        // bookmark:
125                        //              This should be a returned object from dijit.getBookmark()
126
127                        var _doc = win.doc,
128                                mark = bookmark.mark;
129                        if(mark){
130                                if(win.global.getSelection){
131                                        //W3C Rangi API (FF, WebKit, Opera, etc)
132                                        var sel = win.global.getSelection();
133                                        if(sel && sel.removeAllRanges){
134                                                if(mark.pRange){
135                                                        var n = mark.node;
136                                                        n.selectionStart = mark.start;
137                                                        n.selectionEnd = mark.end;
138                                                }else{
139                                                        sel.removeAllRanges();
140                                                        sel.addRange(mark);
141                                                }
142                                        }else{
143                                                console.warn("No idea how to restore selection for this browser!");
144                                        }
145                                }else if(_doc.selection && mark){
146                                        //'IE' way.
147                                        var rg;
148                                        if(mark.pRange){
149                                                rg = mark.range;
150                                        }else if(lang.isArray(mark)){
151                                                rg = _doc.body.createControlRange();
152                                                //rg.addElement does not have call/apply method, so can not call it directly
153                                                //rg is not available in "range.addElement(item)", so can't use that either
154                                                array.forEach(mark, function(n){
155                                                        rg.addElement(n);
156                                                });
157                                        }else{
158                                                rg = _doc.body.createTextRange();
159                                                rg.moveToBookmark(mark);
160                                        }
161                                        rg.select();
162                                }
163                        }
164                },
165
166                getFocus: function(/*Widget?*/ menu, /*Window?*/ openedForWindow){
167                        // summary:
168                        //              Called as getFocus(), this returns an Object showing the current focus
169                        //              and selected text.
170                        //
171                        //              Called as getFocus(widget), where widget is a (widget representing) a button
172                        //              that was just pressed, it returns where focus was before that button
173                        //              was pressed.   (Pressing the button may have either shifted focus to the button,
174                        //              or removed focus altogether.)   In this case the selected text is not returned,
175                        //              since it can't be accurately determined.
176                        //
177                        // menu: dijit._Widget or {domNode: DomNode} structure
178                        //              The button that was just pressed.  If focus has disappeared or moved
179                        //              to this button, returns the previous focus.  In this case the bookmark
180                        //              information is already lost, and null is returned.
181                        //
182                        // openedForWindow:
183                        //              iframe in which menu was opened
184                        //
185                        // returns:
186                        //              A handle to restore focus/selection, to be passed to `dijit.focus`
187                        var node = !focus.curNode || (menu && dom.isDescendant(focus.curNode, menu.domNode)) ? dijit._prevFocus : focus.curNode;
188                        return {
189                                node: node,
190                                bookmark: node && (node == focus.curNode) && win.withGlobal(openedForWindow || win.global, dijit.getBookmark),
191                                openedForWindow: openedForWindow
192                        }; // Object
193                },
194
195                // _activeStack: dijit._Widget[]
196                //              List of currently active widgets (focused widget and it's ancestors)
197                _activeStack: [],
198
199                registerIframe: function(/*DomNode*/ iframe){
200                        // summary:
201                        //              Registers listeners on the specified iframe so that any click
202                        //              or focus event on that iframe (or anything in it) is reported
203                        //              as a focus/click event on the <iframe> itself.
204                        // description:
205                        //              Currently only used by editor.
206                        // returns:
207                        //              Handle to pass to unregisterIframe()
208                        return focus.registerIframe(iframe);
209                },
210
211                unregisterIframe: function(/*Object*/ handle){
212                        // summary:
213                        //              Unregisters listeners on the specified iframe created by registerIframe.
214                        //              After calling be sure to delete or null out the handle itself.
215                        // handle:
216                        //              Handle returned by registerIframe()
217
218                        handle && handle.remove();
219                },
220
221                registerWin: function(/*Window?*/targetWindow, /*DomNode?*/ effectiveNode){
222                        // summary:
223                        //              Registers listeners on the specified window (either the main
224                        //              window or an iframe's window) to detect when the user has clicked somewhere
225                        //              or focused somewhere.
226                        // description:
227                        //              Users should call registerIframe() instead of this method.
228                        // targetWindow:
229                        //              If specified this is the window associated with the iframe,
230                        //              i.e. iframe.contentWindow.
231                        // effectiveNode:
232                        //              If specified, report any focus events inside targetWindow as
233                        //              an event on effectiveNode, rather than on evt.target.
234                        // returns:
235                        //              Handle to pass to unregisterWin()
236
237                        return focus.registerWin(targetWindow, effectiveNode);
238                },
239
240                unregisterWin: function(/*Handle*/ handle){
241                        // summary:
242                        //              Unregisters listeners on the specified window (either the main
243                        //              window or an iframe's window) according to handle returned from registerWin().
244                        //              After calling be sure to delete or null out the handle itself.
245
246                        handle && handle.remove();
247                }
248        });
249
250        // Override focus singleton's focus function so that dijit.focus()
251        // has backwards compatible behavior of restoring selection (although
252        // probably no one is using that).
253        focus.focus = function(/*Object || DomNode */ handle){
254                // summary:
255                //              Sets the focused node and the selection according to argument.
256                //              To set focus to an iframe's content, pass in the iframe itself.
257                // handle:
258                //              object returned by get(), or a DomNode
259
260                if(!handle){ return; }
261
262                var node = "node" in handle ? handle.node : handle,             // because handle is either DomNode or a composite object
263                        bookmark = handle.bookmark,
264                        openedForWindow = handle.openedForWindow,
265                        collapsed = bookmark ? bookmark.isCollapsed : false;
266
267                // Set the focus
268                // Note that for iframe's we need to use the <iframe> to follow the parentNode chain,
269                // but we need to set focus to iframe.contentWindow
270                if(node){
271                        var focusNode = (node.tagName.toLowerCase() == "iframe") ? node.contentWindow : node;
272                        if(focusNode && focusNode.focus){
273                                try{
274                                        // Gecko throws sometimes if setting focus is impossible,
275                                        // node not displayed or something like that
276                                        focusNode.focus();
277                                }catch(e){/*quiet*/}
278                        }
279                        focus._onFocusNode(node);
280                }
281
282                // set the selection
283                // do not need to restore if current selection is not empty
284                // (use keyboard to select a menu item) or if previous selection was collapsed
285                // as it may cause focus shift (Esp in IE).
286                if(bookmark && win.withGlobal(openedForWindow || win.global, dijit.isCollapsed) && !collapsed){
287                        if(openedForWindow){
288                                openedForWindow.focus();
289                        }
290                        try{
291                                win.withGlobal(openedForWindow || win.global, dijit.moveToBookmark, null, [bookmark]);
292                        }catch(e2){
293                                /*squelch IE internal error, see http://trac.dojotoolkit.org/ticket/1984 */
294                        }
295                }
296        };
297
298        // For back compatibility, monitor changes to focused node and active widget stack,
299        // publishing events and copying changes from focus manager variables into dijit (top level) variables
300        focus.watch("curNode", function(name, oldVal, newVal){
301                dijit._curFocus = newVal;
302                dijit._prevFocus = oldVal;
303                if(newVal){
304                        topic.publish("focusNode", newVal);     // publish
305                }
306        });
307        focus.watch("activeStack", function(name, oldVal, newVal){
308                dijit._activeStack = newVal;
309        });
310
311        focus.on("widget-blur", function(widget, by){
312                topic.publish("widgetBlur", widget, by);        // publish
313        });
314        focus.on("widget-focus", function(widget, by){
315                topic.publish("widgetFocus", widget, by);       // publish
316        });
317
318        return dijit;
319});
Note: See TracBrowser for help on using the repository browser.