diff options
Diffstat (limited to 'includes/js/dijit/_base/focus.js')
-rw-r--r-- | includes/js/dijit/_base/focus.js | 342 |
1 files changed, 342 insertions, 0 deletions
diff --git a/includes/js/dijit/_base/focus.js b/includes/js/dijit/_base/focus.js new file mode 100644 index 0000000..46230b5 --- /dev/null +++ b/includes/js/dijit/_base/focus.js @@ -0,0 +1,342 @@ +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"); + +// summary: +// These functions are used to query or set the focus and selection. +// +// Also, they trace when widgets become actived/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: tests whether the current selection is empty + var _window = dojo.global; + var _document = dojo.doc; + if(_document.selection){ // IE + return !_document.selection.createRange().text; // Boolean + }else{ + var selection = _window.getSelection(); + if(dojo.isString(selection)){ // Safari + return !selection; // Boolean + }else{ // Mozilla/W3 + return selection.isCollapsed || !selection.toString(); // Boolean + } + } + }, + + getBookmark: function(){ + // summary: Retrieves a bookmark that can be used with moveToBookmark to return to the same range + var bookmark, selection = dojo.doc.selection; + if(selection){ // IE + var range = selection.createRange(); + if(selection.type.toUpperCase()=='CONTROL'){ + if(range.length){ + bookmark=[]; + var i=0,len=range.length; + while(i<len){ + bookmark.push(range.item(i++)); + } + }else{ + bookmark=null; + } + }else{ + bookmark = range.getBookmark(); + } + }else{ + if(window.getSelection){ + selection = dojo.global.getSelection(); + if(selection){ + range = selection.getRangeAt(0); + bookmark = range.cloneRange(); + } + }else{ + console.warn("No idea how to store the current selection for this browser!"); + } + } + return bookmark; // Array + }, + + moveToBookmark: function(/*Object*/bookmark){ + // summary: Moves current selection to a bookmark + // bookmark: This should be a returned object from dojo.html.selection.getBookmark() + var _document = dojo.doc; + if(_document.selection){ // IE + var range; + if(dojo.isArray(bookmark)){ + range = _document.body.createControlRange(); + dojo.forEach(bookmark, "range.addElement(item)"); //range.addElement does not have call/apply method, so can not call it directly + }else{ + range = _document.selection.createRange(); + range.moveToBookmark(bookmark); + } + range.select(); + }else{ //Moz/W3C + var selection = dojo.global.getSelection && dojo.global.getSelection(); + if(selection && selection.removeAllRanges){ + selection.removeAllRanges(); + selection.addRange(bookmark); + }else{ + console.warn("No idea how to restore selection for this browser!"); + } + } + }, + + getFocus: function(/*Widget?*/menu, /*Window?*/openedForWindow){ + // summary: + // Returns the current focus and selection. + // Called when a popup appears (either a top level menu or a dialog), + // or when a toolbar/menubar receives focus + // + // menu: + // The menu that's being opened + // + // openedForWindow: + // iframe in which menu was opened + // + // returns: + // A handle to restore focus/selection + + return { + // Node to return focus to + node: menu && dojo.isDescendant(dijit._curFocus, menu.domNode) ? dijit._prevFocus : dijit._curFocus, + + // Previously selected text + bookmark: + !dojo.withGlobal(openedForWindow||dojo.global, dijit.isCollapsed) ? + dojo.withGlobal(openedForWindow||dojo.global, dijit.getBookmark) : + null, + + 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; + + // 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) + if(bookmark && dojo.withGlobal(openedForWindow||dojo.global, dijit.isCollapsed)){ + if(openedForWindow){ + openedForWindow.focus(); + } + try{ + dojo.withGlobal(openedForWindow||dojo.global, dijit.moveToBookmark, null, [bookmark]); + }catch(e){ + /*squelch IE internal error, see http://trac.dojotoolkit.org/ticket/1984 */ + } + } + }, + + // _activeStack: Array + // List of currently active widgets (focused widget and it's ancestors) + _activeStack: [], + + registerWin: function(/*Window?*/targetWindow){ + // summary: + // Registers listeners on the specified window (either the main + // window or an iframe) to detect when the user has clicked somewhere. + // Anyone that creates an iframe should call this function. + + if(!targetWindow){ + targetWindow = window; + } + + dojo.connect(targetWindow.document, "onmousedown", function(evt){ + dijit._justMouseDowned = true; + setTimeout(function(){ dijit._justMouseDowned = false; }, 0); + dijit._onTouchNode(evt.target||evt.srcElement); + }); + //dojo.connect(targetWindow, "onscroll", ???); + + // Listen for blur and focus events on targetWindow's body + var body = targetWindow.document.body || targetWindow.document.getElementsByTagName("body")[0]; + if(body){ + if(dojo.isIE){ + body.attachEvent('onactivate', function(evt){ + if(evt.srcElement.tagName.toLowerCase() != "body"){ + dijit._onFocusNode(evt.srcElement); + } + }); + body.attachEvent('ondeactivate', function(evt){ dijit._onBlurNode(evt.srcElement); }); + }else{ + body.addEventListener('focus', function(evt){ dijit._onFocusNode(evt.target); }, true); + body.addEventListener('blur', function(evt){ dijit._onBlurNode(evt.target); }, true); + } + } + body = null; // prevent memory leak (apparent circular reference via closure) + }, + + _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){ + // summary: + // Callback when node is focused or mouse-downed + + // 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){ + if(node.dijitPopupParent){ + node=dijit.byId(node.dijitPopupParent).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=dijit.getDocumentWindow(node.ownerDocument).frameElement; + }else{ + var id = node.getAttribute && node.getAttribute("widgetId"); + if(id){ + newStack.unshift(id); + } + node=node.parentNode; + } + } + }catch(e){ /* squelch */ } + + dijit._setStack(newStack); + }, + + _onFocusNode: function(/*DomNode*/ node){ + // summary + // Callback when node is focused + if(node && node.tagName && node.tagName.toLowerCase() == "body"){ + 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(newStack){ + // summary + // The stack of active widgets has changed. Send out appropriate events and record new stack + + 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; + } + } + + // for all elements that have gone out of focus, send blur event + for(var i=oldStack.length-1; i>=nCommon; i--){ + var widget = dijit.byId(oldStack[i]); + if(widget){ + widget._focused = false; + widget._hasBeenBlurred = true; + if(widget._onBlur){ + widget._onBlur(); + } + if (widget._setStateClass){ + widget._setStateClass(); + } + dojo.publish("widgetBlur", [widget]); + } + } + + // 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; + if(widget._onFocus){ + widget._onFocus(); + } + if (widget._setStateClass){ + widget._setStateClass(); + } + dojo.publish("widgetFocus", [widget]); + } + } + } +}); + +// register top window and all the iframes it contains +dojo.addOnLoad(dijit.registerWin); + +} |