New file |
0,0 → 1,330 |
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 if(_window.getSelection){ |
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'){ |
bookmark = range.length ? dojo._toArray(range) : null; |
}else{ |
bookmark = range.getBookmark(); |
} |
}else{ |
if(dojo.global.getSelection){ |
selection = dojo.global.getSelection(); |
if(selection){ |
var range = selection.getRangeAt(0); |
bookmark = range.cloneRange(); |
} |
}else{ |
console.debug("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); |
}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.debug("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, moveToBookmark, null, [bookmark]); |
}catch(e){ |
/*squelch IE internal error, see http://trac.dojotoolkit.org/ticket/1984 */ |
} |
} |
}, |
|
// 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", null, 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; |
|
var w = dijit.getEnclosingWidget(node); |
if (w && w._setStateClass){ |
w._focused = false; |
w._setStateClass(); |
} |
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([]); }, 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) and continue tracing up the document |
node=dojo.query("iframe").filter(function(iframe){ return iframe.contentDocument.body===node; })[0]; |
}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; } |
dijit._prevFocus = dijit._curFocus; |
dijit._curFocus = node; |
dojo.publish("focusNode", [node]); |
|
// handle focus/blur styling |
var w = dijit.getEnclosingWidget(node); |
if (w && w._setStateClass){ |
w._focused = true; |
w._setStateClass(); |
} |
}, |
|
_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){ |
dojo.publish("widgetBlur", [widget]); |
if(widget._onBlur){ |
widget._onBlur(); |
} |
} |
} |
|
// for all element that have come into focus, send focus event |
for(var i=nCommon; i<newStack.length; i++){ |
var widget = dijit.byId(newStack[i]); |
if(widget){ |
dojo.publish("widgetFocus", [widget]); |
if(widget._onFocus){ |
widget._onFocus(); |
} |
} |
} |
} |
}); |
|
// register top window and all the iframes it contains |
dojo.addOnLoad(dijit.registerWin); |
|
} |