source: [view]
define("dijit/_base/focus", ["dojo", "dijit", "dojo/window", "dijit/_base/manager"], function(dojo, dijit) {
// 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 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