Subversion Repositories Applications.papyrus

Rev

Blame | Last modification | View Log | RSS feed

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);

}