diff options
Diffstat (limited to 'lib/dijit/_base/focus.js')
-rw-r--r-- | lib/dijit/_base/focus.js | 532 |
1 files changed, 2 insertions, 530 deletions
diff --git a/lib/dijit/_base/focus.js b/lib/dijit/_base/focus.js index 55c5b682d..840329992 100644 --- a/lib/dijit/_base/focus.js +++ b/lib/dijit/_base/focus.js @@ -1,530 +1,2 @@ -/* - Copyright (c) 2004-2011, The Dojo Foundation All Rights Reserved. - Available via Academic Free License >= 2.1 OR the modified BSD license. - see: http://dojotoolkit.org/license for details -*/ - - -if(!dojo._hasResource["dijit._base.focus"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit._base.focus"] = true; -dojo.provide("dijit._base.focus"); -dojo.require("dojo.window"); -dojo.require("dijit._base.manager"); - - -// summary: -// These functions are used to query or set the focus and selection. -// -// Also, they trace when widgets become activated/deactivated, -// so that the widget can fire _onFocus/_onBlur events. -// "Active" here means something similar to "focused", but -// "focus" isn't quite the right word because we keep track of -// a whole stack of "active" widgets. Example: ComboButton --> Menu --> -// MenuItem. The onBlur event for ComboButton doesn't fire due to focusing -// on the Menu or a MenuItem, since they are considered part of the -// ComboButton widget. It only happens when focus is shifted -// somewhere completely different. - -dojo.mixin(dijit, { - // _curFocus: DomNode - // Currently focused item on screen - _curFocus: null, - - // _prevFocus: DomNode - // Previously focused item on screen - _prevFocus: null, - - isCollapsed: function(){ - // summary: - // Returns true if there is no text selected - return dijit.getBookmark().isCollapsed; - }, - - getBookmark: function(){ - // summary: - // Retrieves a bookmark that can be used with moveToBookmark to return to the same range - var bm, rg, tg, sel = dojo.doc.selection, cf = dijit._curFocus; - - if(dojo.global.getSelection){ - //W3C Range API for selections. - sel = dojo.global.getSelection(); - if(sel){ - if(sel.isCollapsed){ - tg = cf? cf.tagName : ""; - if(tg){ - //Create a fake rangelike item to restore selections. - tg = tg.toLowerCase(); - if(tg == "textarea" || - (tg == "input" && (!cf.type || cf.type.toLowerCase() == "text"))){ - sel = { - start: cf.selectionStart, - end: cf.selectionEnd, - node: cf, - pRange: true - }; - return {isCollapsed: (sel.end <= sel.start), mark: sel}; //Object. - } - } - bm = {isCollapsed:true}; - if(sel.rangeCount){ - bm.mark = sel.getRangeAt(0).cloneRange(); - } - }else{ - rg = sel.getRangeAt(0); - bm = {isCollapsed: false, mark: rg.cloneRange()}; - } - } - }else if(sel){ - // If the current focus was a input of some sort and no selection, don't bother saving - // a native bookmark. This is because it causes issues with dialog/page selection restore. - // So, we need to create psuedo bookmarks to work with. - tg = cf ? cf.tagName : ""; - tg = tg.toLowerCase(); - if(cf && tg && (tg == "button" || tg == "textarea" || tg == "input")){ - if(sel.type && sel.type.toLowerCase() == "none"){ - return { - isCollapsed: true, - mark: null - } - }else{ - rg = sel.createRange(); - return { - isCollapsed: rg.text && rg.text.length?false:true, - mark: { - range: rg, - pRange: true - } - }; - } - } - bm = {}; - - //'IE' way for selections. - try{ - // createRange() throws exception when dojo in iframe - //and nothing selected, see #9632 - rg = sel.createRange(); - bm.isCollapsed = !(sel.type == 'Text' ? rg.htmlText.length : rg.length); - }catch(e){ - bm.isCollapsed = true; - return bm; - } - if(sel.type.toUpperCase() == 'CONTROL'){ - if(rg.length){ - bm.mark=[]; - var i=0,len=rg.length; - while(i<len){ - bm.mark.push(rg.item(i++)); - } - }else{ - bm.isCollapsed = true; - bm.mark = null; - } - }else{ - bm.mark = rg.getBookmark(); - } - }else{ - console.warn("No idea how to store the current selection for this browser!"); - } - return bm; // Object - }, - - moveToBookmark: function(/*Object*/bookmark){ - // summary: - // Moves current selection to a bookmark - // bookmark: - // This should be a returned object from dijit.getBookmark() - - var _doc = dojo.doc, - mark = bookmark.mark; - if(mark){ - if(dojo.global.getSelection){ - //W3C Rangi API (FF, WebKit, Opera, etc) - var sel = dojo.global.getSelection(); - if(sel && sel.removeAllRanges){ - if(mark.pRange){ - var r = mark; - var n = r.node; - n.selectionStart = r.start; - n.selectionEnd = r.end; - }else{ - sel.removeAllRanges(); - sel.addRange(mark); - } - }else{ - console.warn("No idea how to restore selection for this browser!"); - } - }else if(_doc.selection && mark){ - //'IE' way. - var rg; - if(mark.pRange){ - rg = mark.range; - }else if(dojo.isArray(mark)){ - rg = _doc.body.createControlRange(); - //rg.addElement does not have call/apply method, so can not call it directly - //rg is not available in "range.addElement(item)", so can't use that either - dojo.forEach(mark, function(n){ - rg.addElement(n); - }); - }else{ - rg = _doc.body.createTextRange(); - rg.moveToBookmark(mark); - } - rg.select(); - } - } - }, - - getFocus: function(/*Widget?*/ menu, /*Window?*/ openedForWindow){ - // summary: - // Called as getFocus(), this returns an Object showing the current focus - // and selected text. - // - // Called as getFocus(widget), where widget is a (widget representing) a button - // that was just pressed, it returns where focus was before that button - // was pressed. (Pressing the button may have either shifted focus to the button, - // or removed focus altogether.) In this case the selected text is not returned, - // since it can't be accurately determined. - // - // menu: dijit._Widget or {domNode: DomNode} structure - // The button that was just pressed. If focus has disappeared or moved - // to this button, returns the previous focus. In this case the bookmark - // information is already lost, and null is returned. - // - // openedForWindow: - // iframe in which menu was opened - // - // returns: - // A handle to restore focus/selection, to be passed to `dijit.focus` - var node = !dijit._curFocus || (menu && dojo.isDescendant(dijit._curFocus, menu.domNode)) ? dijit._prevFocus : dijit._curFocus; - return { - node: node, - bookmark: (node == dijit._curFocus) && dojo.withGlobal(openedForWindow || dojo.global, dijit.getBookmark), - openedForWindow: openedForWindow - }; // Object - }, - - focus: function(/*Object || DomNode */ handle){ - // summary: - // Sets the focused node and the selection according to argument. - // To set focus to an iframe's content, pass in the iframe itself. - // handle: - // object returned by get(), or a DomNode - - if(!handle){ return; } - - var node = "node" in handle ? handle.node : handle, // because handle is either DomNode or a composite object - bookmark = handle.bookmark, - openedForWindow = handle.openedForWindow, - collapsed = bookmark ? bookmark.isCollapsed : false; - - // Set the focus - // Note that for iframe's we need to use the <iframe> to follow the parentNode chain, - // but we need to set focus to iframe.contentWindow - if(node){ - var focusNode = (node.tagName.toLowerCase() == "iframe") ? node.contentWindow : node; - if(focusNode && focusNode.focus){ - try{ - // Gecko throws sometimes if setting focus is impossible, - // node not displayed or something like that - focusNode.focus(); - }catch(e){/*quiet*/} - } - dijit._onFocusNode(node); - } - - // set the selection - // do not need to restore if current selection is not empty - // (use keyboard to select a menu item) or if previous selection was collapsed - // as it may cause focus shift (Esp in IE). - if(bookmark && dojo.withGlobal(openedForWindow || dojo.global, dijit.isCollapsed) && !collapsed){ - if(openedForWindow){ - openedForWindow.focus(); - } - try{ - dojo.withGlobal(openedForWindow || dojo.global, dijit.moveToBookmark, null, [bookmark]); - }catch(e2){ - /*squelch IE internal error, see http://trac.dojotoolkit.org/ticket/1984 */ - } - } - }, - - // _activeStack: dijit._Widget[] - // List of currently active widgets (focused widget and it's ancestors) - _activeStack: [], - - registerIframe: function(/*DomNode*/ iframe){ - // summary: - // Registers listeners on the specified iframe so that any click - // or focus event on that iframe (or anything in it) is reported - // as a focus/click event on the <iframe> itself. - // description: - // Currently only used by editor. - // returns: - // Handle to pass to unregisterIframe() - return dijit.registerWin(iframe.contentWindow, iframe); - }, - - unregisterIframe: function(/*Object*/ handle){ - // summary: - // Unregisters listeners on the specified iframe created by registerIframe. - // After calling be sure to delete or null out the handle itself. - // handle: - // Handle returned by registerIframe() - - dijit.unregisterWin(handle); - }, - - registerWin: function(/*Window?*/targetWindow, /*DomNode?*/ effectiveNode){ - // summary: - // Registers listeners on the specified window (either the main - // window or an iframe's window) to detect when the user has clicked somewhere - // or focused somewhere. - // description: - // Users should call registerIframe() instead of this method. - // targetWindow: - // If specified this is the window associated with the iframe, - // i.e. iframe.contentWindow. - // effectiveNode: - // If specified, report any focus events inside targetWindow as - // an event on effectiveNode, rather than on evt.target. - // returns: - // Handle to pass to unregisterWin() - - // TODO: make this function private in 2.0; Editor/users should call registerIframe(), - - var mousedownListener = function(evt){ - dijit._justMouseDowned = true; - setTimeout(function(){ dijit._justMouseDowned = false; }, 0); - - // workaround weird IE bug where the click is on an orphaned node - // (first time clicking a Select/DropDownButton inside a TooltipDialog) - if(dojo.isIE && evt && evt.srcElement && evt.srcElement.parentNode == null){ - return; - } - - dijit._onTouchNode(effectiveNode || evt.target || evt.srcElement, "mouse"); - }; - //dojo.connect(targetWindow, "onscroll", ???); - - // Listen for blur and focus events on targetWindow's document. - // IIRC, I'm using attachEvent() rather than dojo.connect() because focus/blur events don't bubble - // through dojo.connect(), and also maybe to catch the focus events early, before onfocus handlers - // fire. - // Connect to <html> (rather than document) on IE to avoid memory leaks, but document on other browsers because - // (at least for FF) the focus event doesn't fire on <html> or <body>. - var doc = dojo.isIE ? targetWindow.document.documentElement : targetWindow.document; - if(doc){ - if(dojo.isIE){ - targetWindow.document.body.attachEvent('onmousedown', mousedownListener); - var activateListener = function(evt){ - // IE reports that nodes like <body> have gotten focus, even though they have tabIndex=-1, - // Should consider those more like a mouse-click than a focus.... - if(evt.srcElement.tagName.toLowerCase() != "#document" && - dijit.isTabNavigable(evt.srcElement)){ - dijit._onFocusNode(effectiveNode || evt.srcElement); - }else{ - dijit._onTouchNode(effectiveNode || evt.srcElement); - } - }; - doc.attachEvent('onactivate', activateListener); - var deactivateListener = function(evt){ - dijit._onBlurNode(effectiveNode || evt.srcElement); - }; - doc.attachEvent('ondeactivate', deactivateListener); - - return function(){ - targetWindow.document.detachEvent('onmousedown', mousedownListener); - doc.detachEvent('onactivate', activateListener); - doc.detachEvent('ondeactivate', deactivateListener); - doc = null; // prevent memory leak (apparent circular reference via closure) - }; - }else{ - doc.body.addEventListener('mousedown', mousedownListener, true); - var focusListener = function(evt){ - dijit._onFocusNode(effectiveNode || evt.target); - }; - doc.addEventListener('focus', focusListener, true); - var blurListener = function(evt){ - dijit._onBlurNode(effectiveNode || evt.target); - }; - doc.addEventListener('blur', blurListener, true); - - return function(){ - doc.body.removeEventListener('mousedown', mousedownListener, true); - doc.removeEventListener('focus', focusListener, true); - doc.removeEventListener('blur', blurListener, true); - doc = null; // prevent memory leak (apparent circular reference via closure) - }; - } - } - }, - - unregisterWin: function(/*Handle*/ handle){ - // summary: - // Unregisters listeners on the specified window (either the main - // window or an iframe's window) according to handle returned from registerWin(). - // After calling be sure to delete or null out the handle itself. - - // Currently our handle is actually a function - handle && handle(); - }, - - _onBlurNode: function(/*DomNode*/ node){ - // summary: - // Called when focus leaves a node. - // Usually ignored, _unless_ it *isn't* follwed by touching another node, - // which indicates that we tabbed off the last field on the page, - // in which case every widget is marked inactive - dijit._prevFocus = dijit._curFocus; - dijit._curFocus = null; - - if(dijit._justMouseDowned){ - // the mouse down caused a new widget to be marked as active; this blur event - // is coming late, so ignore it. - return; - } - - // if the blur event isn't followed by a focus event then mark all widgets as inactive. - if(dijit._clearActiveWidgetsTimer){ - clearTimeout(dijit._clearActiveWidgetsTimer); - } - dijit._clearActiveWidgetsTimer = setTimeout(function(){ - delete dijit._clearActiveWidgetsTimer; - dijit._setStack([]); - dijit._prevFocus = null; - }, 100); - }, - - _onTouchNode: function(/*DomNode*/ node, /*String*/ by){ - // summary: - // Callback when node is focused or mouse-downed - // node: - // The node that was touched. - // by: - // "mouse" if the focus/touch was caused by a mouse down event - - // ignore the recent blurNode event - if(dijit._clearActiveWidgetsTimer){ - clearTimeout(dijit._clearActiveWidgetsTimer); - delete dijit._clearActiveWidgetsTimer; - } - - // compute stack of active widgets (ex: ComboButton --> Menu --> MenuItem) - var newStack=[]; - try{ - while(node){ - var popupParent = dojo.attr(node, "dijitPopupParent"); - if(popupParent){ - node=dijit.byId(popupParent).domNode; - }else if(node.tagName && node.tagName.toLowerCase() == "body"){ - // is this the root of the document or just the root of an iframe? - if(node === dojo.body()){ - // node is the root of the main document - break; - } - // otherwise, find the iframe this node refers to (can't access it via parentNode, - // need to do this trick instead). window.frameElement is supported in IE/FF/Webkit - node=dojo.window.get(node.ownerDocument).frameElement; - }else{ - // if this node is the root node of a widget, then add widget id to stack, - // except ignore clicks on disabled widgets (actually focusing a disabled widget still works, - // to support MenuItem) - var id = node.getAttribute && node.getAttribute("widgetId"), - widget = id && dijit.byId(id); - if(widget && !(by == "mouse" && widget.get("disabled"))){ - newStack.unshift(id); - } - node=node.parentNode; - } - } - }catch(e){ /* squelch */ } - - dijit._setStack(newStack, by); - }, - - _onFocusNode: function(/*DomNode*/ node){ - // summary: - // Callback when node is focused - - if(!node){ - return; - } - - if(node.nodeType == 9){ - // Ignore focus events on the document itself. This is here so that - // (for example) clicking the up/down arrows of a spinner - // (which don't get focus) won't cause that widget to blur. (FF issue) - return; - } - - dijit._onTouchNode(node); - - if(node == dijit._curFocus){ return; } - if(dijit._curFocus){ - dijit._prevFocus = dijit._curFocus; - } - dijit._curFocus = node; - dojo.publish("focusNode", [node]); - }, - - _setStack: function(/*String[]*/ newStack, /*String*/ by){ - // summary: - // The stack of active widgets has changed. Send out appropriate events and records new stack. - // newStack: - // array of widget id's, starting from the top (outermost) widget - // by: - // "mouse" if the focus/touch was caused by a mouse down event - - var oldStack = dijit._activeStack; - dijit._activeStack = newStack; - - // compare old stack to new stack to see how many elements they have in common - for(var nCommon=0; nCommon<Math.min(oldStack.length, newStack.length); nCommon++){ - if(oldStack[nCommon] != newStack[nCommon]){ - break; - } - } - - var widget; - // for all elements that have gone out of focus, send blur event - for(var i=oldStack.length-1; i>=nCommon; i--){ - widget = dijit.byId(oldStack[i]); - if(widget){ - widget._focused = false; - widget.set("focused", false); - widget._hasBeenBlurred = true; - if(widget._onBlur){ - widget._onBlur(by); - } - dojo.publish("widgetBlur", [widget, by]); - } - } - - // for all element that have come into focus, send focus event - for(i=nCommon; i<newStack.length; i++){ - widget = dijit.byId(newStack[i]); - if(widget){ - widget._focused = true; - widget.set("focused", true); - if(widget._onFocus){ - widget._onFocus(by); - } - dojo.publish("widgetFocus", [widget, by]); - } - } - } -}); - -// register top window and all the iframes it contains -dojo.addOnLoad(function(){ - var handle = dijit.registerWin(window); - if(dojo.isIE){ - dojo.addOnWindowUnload(function(){ - dijit.unregisterWin(handle); - handle = null; - }) - } -}); - -} +//>>built +define("dijit/_base/focus",["dojo/_base/array","dojo/dom","dojo/_base/lang","dojo/topic","dojo/_base/window","../focus",".."],function(_1,_2,_3,_4,_5,_6,_7){_3.mixin(_7,{_curFocus:null,_prevFocus:null,isCollapsed:function(){return _7.getBookmark().isCollapsed;},getBookmark:function(){var bm,rg,tg,_8=_5.doc.selection,cf=_6.curNode;if(_5.global.getSelection){_8=_5.global.getSelection();if(_8){if(_8.isCollapsed){tg=cf?cf.tagName:"";if(tg){tg=tg.toLowerCase();if(tg=="textarea"||(tg=="input"&&(!cf.type||cf.type.toLowerCase()=="text"))){_8={start:cf.selectionStart,end:cf.selectionEnd,node:cf,pRange:true};return {isCollapsed:(_8.end<=_8.start),mark:_8};}}bm={isCollapsed:true};if(_8.rangeCount){bm.mark=_8.getRangeAt(0).cloneRange();}}else{rg=_8.getRangeAt(0);bm={isCollapsed:false,mark:rg.cloneRange()};}}}else{if(_8){tg=cf?cf.tagName:"";tg=tg.toLowerCase();if(cf&&tg&&(tg=="button"||tg=="textarea"||tg=="input")){if(_8.type&&_8.type.toLowerCase()=="none"){return {isCollapsed:true,mark:null};}else{rg=_8.createRange();return {isCollapsed:rg.text&&rg.text.length?false:true,mark:{range:rg,pRange:true}};}}bm={};try{rg=_8.createRange();bm.isCollapsed=!(_8.type=="Text"?rg.htmlText.length:rg.length);}catch(e){bm.isCollapsed=true;return bm;}if(_8.type.toUpperCase()=="CONTROL"){if(rg.length){bm.mark=[];var i=0,_9=rg.length;while(i<_9){bm.mark.push(rg.item(i++));}}else{bm.isCollapsed=true;bm.mark=null;}}else{bm.mark=rg.getBookmark();}}else{console.warn("No idea how to store the current selection for this browser!");}}return bm;},moveToBookmark:function(_a){var _b=_5.doc,_c=_a.mark;if(_c){if(_5.global.getSelection){var _d=_5.global.getSelection();if(_d&&_d.removeAllRanges){if(_c.pRange){var n=_c.node;n.selectionStart=_c.start;n.selectionEnd=_c.end;}else{_d.removeAllRanges();_d.addRange(_c);}}else{console.warn("No idea how to restore selection for this browser!");}}else{if(_b.selection&&_c){var rg;if(_c.pRange){rg=_c.range;}else{if(_3.isArray(_c)){rg=_b.body.createControlRange();_1.forEach(_c,function(n){rg.addElement(n);});}else{rg=_b.body.createTextRange();rg.moveToBookmark(_c);}}rg.select();}}}},getFocus:function(_e,_f){var _10=!_6.curNode||(_e&&_2.isDescendant(_6.curNode,_e.domNode))?_7._prevFocus:_6.curNode;return {node:_10,bookmark:_10&&(_10==_6.curNode)&&_5.withGlobal(_f||_5.global,_7.getBookmark),openedForWindow:_f};},_activeStack:[],registerIframe:function(_11){return _6.registerIframe(_11);},unregisterIframe:function(_12){_12&&_12.remove();},registerWin:function(_13,_14){return _6.registerWin(_13,_14);},unregisterWin:function(_15){_15&&_15.remove();}});_6.focus=function(_16){if(!_16){return;}var _17="node" in _16?_16.node:_16,_18=_16.bookmark,_19=_16.openedForWindow,_1a=_18?_18.isCollapsed:false;if(_17){var _1b=(_17.tagName.toLowerCase()=="iframe")?_17.contentWindow:_17;if(_1b&&_1b.focus){try{_1b.focus();}catch(e){}}_6._onFocusNode(_17);}if(_18&&_5.withGlobal(_19||_5.global,_7.isCollapsed)&&!_1a){if(_19){_19.focus();}try{_5.withGlobal(_19||_5.global,_7.moveToBookmark,null,[_18]);}catch(e2){}}};_6.watch("curNode",function(_1c,_1d,_1e){_7._curFocus=_1e;_7._prevFocus=_1d;if(_1e){_4.publish("focusNode",_1e);}});_6.watch("activeStack",function(_1f,_20,_21){_7._activeStack=_21;});_6.on("widget-blur",function(_22,by){_4.publish("widgetBlur",_22,by);});_6.on("widget-focus",function(_23,by){_4.publish("widgetFocus",_23,by);});return _7;});
\ No newline at end of file |