Subversion Repositories Applications.papyrus

Rev

Blame | Last modification | View Log | RSS feed

if(!dojo._hasResource["dijit._Widget"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dijit._Widget"] = true;
dojo.provide("dijit._Widget");

dojo.require("dijit._base");

dojo.declare("dijit._Widget", null, {
        // summary:
        //              The foundation of dijit widgets.        
        //
        // id: String
        //              a unique, opaque ID string that can be assigned by users or by the
        //              system. If the developer passes an ID which is known not to be
        //              unique, the specified ID is ignored and the system-generated ID is
        //              used instead.
        id: "",

        // lang: String
        //      Language to display this widget in (like en-us).
        //      Defaults to brower's specified preferred language (typically the language of the OS)
        lang: "",

        // dir: String
        //  Bi-directional support, as defined by the HTML DIR attribute. Either left-to-right "ltr" or right-to-left "rtl".
        dir: "",

        // class: String
        // HTML class attribute
        "class": "",

        // style: String
        // HTML style attribute
        style: "",

        // title: String
        // HTML title attribute
        title: "",

        // srcNodeRef: DomNode
        //              pointer to original dom node
        srcNodeRef: null,

        // domNode: DomNode
        //              this is our visible representation of the widget! Other DOM
        //              Nodes may by assigned to other properties, usually through the
        //              template system's dojoAttachPonit syntax, but the domNode
        //              property is the canonical "top level" node in widget UI.
        domNode: null,

        // attributeMap: Object
        //              A map of attributes and attachpoints -- typically standard HTML attributes -- to set
        //              on the widget's dom, at the "domNode" attach point, by default.
        //              Other node references can be specified as properties of 'this'
        attributeMap: {id:"", dir:"", lang:"", "class":"", style:"", title:""},  // TODO: add on* handlers?

        //////////// INITIALIZATION METHODS ///////////////////////////////////////

        postscript: function(params, srcNodeRef){
                this.create(params, srcNodeRef);
        },

        create: function(params, srcNodeRef){
                // summary:
                //              To understand the process by which widgets are instantiated, it
                //              is critical to understand what other methods create calls and
                //              which of them you'll want to override. Of course, adventurous
                //              developers could override create entirely, but this should
                //              only be done as a last resort.
                //
                //              Below is a list of the methods that are called, in the order
                //              they are fired, along with notes about what they do and if/when
                //              you should over-ride them in your widget:
                //                      
                //                      postMixInProperties:
                //                              a stub function that you can over-ride to modify
                //                              variables that may have been naively assigned by
                //                              mixInProperties
                //                      # widget is added to manager object here
                //                      buildRendering
                //                              Subclasses use this method to handle all UI initialization
                //                              Sets this.domNode.  Templated widgets do this automatically
                //                              and otherwise it just uses the source dom node.
                //                      postCreate
                //                              a stub function that you can over-ride to modify take
                //                              actions once the widget has been placed in the UI

                // store pointer to original dom tree
                this.srcNodeRef = dojo.byId(srcNodeRef);

                // For garbage collection.  An array of handles returned by Widget.connect()
                // Each handle returned from Widget.connect() is an array of handles from dojo.connect()
                this._connects=[];

                // _attaches: String[]
                //              names of all our dojoAttachPoint variables
                this._attaches=[];

                //mixin our passed parameters
                if(this.srcNodeRef && (typeof this.srcNodeRef.id == "string")){ this.id = this.srcNodeRef.id; }
                if(params){
                        dojo.mixin(this,params);
                }
                this.postMixInProperties();

                // generate an id for the widget if one wasn't specified
                // (be sure to do this before buildRendering() because that function might
                // expect the id to be there.
                if(!this.id){
                        this.id=dijit.getUniqueId(this.declaredClass.replace(/\./g,"_"));
                }
                dijit.registry.add(this);

                this.buildRendering();

                // Copy attributes listed in attributeMap into the [newly created] DOM for the widget.
                // The placement of these attributes is according to the property mapping in attributeMap.
                // Note special handling for 'style' and 'class' attributes which are lists and can
                // have elements from both old and new structures, and some attributes like "type"
                // cannot be processed this way as they are not mutable.
                if(this.domNode){
                        for(var attr in this.attributeMap){
                                var mapNode = this[this.attributeMap[attr] || "domNode"];
                                var value = this[attr];
                                if(typeof value != "object" && (value !== "" || (params && params[attr]))){
                                        switch(attr){
                                        case "class":
                                                dojo.addClass(mapNode, value);
                                                break;
                                        case "style":
                                                if(mapNode.style.cssText){
                                                        mapNode.style.cssText += "; " + value;// FIXME: Opera
                                                }else{
                                                        mapNode.style.cssText = value;
                                                }
                                                break;
                                        default:
                                                mapNode.setAttribute(attr, value);
                                        }
                                }
                        }
                }

                if(this.domNode){
                        this.domNode.setAttribute("widgetId", this.id);
                }
                this.postCreate();

                // If srcNodeRef has been processed and removed from the DOM (e.g. TemplatedWidget) then delete it to allow GC.
                if(this.srcNodeRef && !this.srcNodeRef.parentNode){
                        delete this.srcNodeRef;
                }       
        },

        postMixInProperties: function(){
                // summary
                //      Called after the parameters to the widget have been read-in,
                //      but before the widget template is instantiated.
                //      Especially useful to set properties that are referenced in the widget template.
        },

        buildRendering: function(){
                // summary:
                //              Construct the UI for this widget, setting this.domNode.
                //              Most widgets will mixin TemplatedWidget, which overrides this method.
                this.domNode = this.srcNodeRef || dojo.doc.createElement('div');
        },

        postCreate: function(){
                // summary:
                //              Called after a widget's dom has been setup
        },

        startup: function(){
                // summary:
                //              Called after a widget's children, and other widgets on the page, have been created.
                //              Provides an opportunity to manipulate any children before they are displayed
                //              This is useful for composite widgets that need to control or layout sub-widgets
                //              Many layout widgets can use this as a wiring phase
        },

        //////////// DESTROY FUNCTIONS ////////////////////////////////

        destroyRecursive: function(/*Boolean*/ finalize){
                // summary:
                //              Destroy this widget and it's descendants. This is the generic
                //              "destructor" function that all widget users should call to
                //              cleanly discard with a widget. Once a widget is destroyed, it's
                //              removed from the manager object.
                // finalize: Boolean
                //              is this function being called part of global environment
                //              tear-down?

                this.destroyDescendants();
                this.destroy();
        },

        destroy: function(/*Boolean*/ finalize){
                // summary:
                //              Destroy this widget, but not its descendants
                // finalize: Boolean
                //              is this function being called part of global environment
                //              tear-down?
                this.uninitialize();
                dojo.forEach(this._connects, function(array){
                        dojo.forEach(array, dojo.disconnect);
                });
                this.destroyRendering(finalize);
                dijit.registry.remove(this.id);
        },

        destroyRendering: function(/*Boolean*/ finalize){
                // summary:
                //              Destroys the DOM nodes associated with this widget
                // finalize: Boolean
                //              is this function being called part of global environment
                //              tear-down?

                if(this.bgIframe){
                        this.bgIframe.destroy();
                        delete this.bgIframe;
                }

                if(this.domNode){
                        dojo._destroyElement(this.domNode);
                        delete this.domNode;
                }

                if(this.srcNodeRef){
                        dojo._destroyElement(this.srcNodeRef);
                        delete this.srcNodeRef;
                }
        },

        destroyDescendants: function(){
                // summary:
                //              Recursively destroy the children of this widget and their
                //              descendants.

                // TODO: should I destroy in the reverse order, to go bottom up?
                dojo.forEach(this.getDescendants(), function(widget){ widget.destroy(); });
        },

        uninitialize: function(){
                // summary:
                //              stub function. Over-ride to implement custom widget tear-down
                //              behavior.
                return false;
        },

        ////////////////// MISCELLANEOUS METHODS ///////////////////

        toString: function(){
                // summary:
                //              returns a string that represents the widget. When a widget is
                //              cast to a string, this method will be used to generate the
                //              output. Currently, it does not implement any sort of reversable
                //              serialization.
                return '[Widget ' + this.declaredClass + ', ' + (this.id || 'NO ID') + ']'; // String
        },

        getDescendants: function(){
                // summary:
                //      return all the descendant widgets
                var list = dojo.query('[widgetId]', this.domNode);
                return list.map(dijit.byNode);          // Array
        },

        nodesWithKeyClick : ["input", "button"],

        connect: function(
                        /*Object|null*/ obj,
                        /*String*/ event,
                        /*String|Function*/ method){

                // summary:
                //              Connects specified obj/event to specified method of this object
                //              and registers for disconnect() on widget destroy.
                //              Special event: "ondijitclick" triggers on a click or enter-down or space-up
                //              Similar to dojo.connect() but takes three arguments rather than four.
                var handles =[];
                if(event == "ondijitclick"){
                        var w = this;
                        // add key based click activation for unsupported nodes.
                        if(!this.nodesWithKeyClick[obj.nodeName]){
                                handles.push(dojo.connect(obj, "onkeydown", this,
                                        function(e){
                                                if(e.keyCode == dojo.keys.ENTER){
                                                        return (dojo.isString(method))?
                                                                w[method](e) : method.call(w, e);
                                                }else if(e.keyCode == dojo.keys.SPACE){
                                                        // stop space down as it causes IE to scroll
                                                        // the browser window
                                                        dojo.stopEvent(e);
                                                }
                                        }));
                                handles.push(dojo.connect(obj, "onkeyup", this,
                                        function(e){
                                                if(e.keyCode == dojo.keys.SPACE){
                                                        return dojo.isString(method) ?
                                                                w[method](e) : method.call(w, e);
                                                }
                                        }));
                        }
                        event = "onclick";
                }
                handles.push(dojo.connect(obj, event, this, method));

                // return handles for FormElement and ComboBox
                this._connects.push(handles);
                return handles;
        },

        disconnect: function(/*Object*/ handles){
                // summary:
                //              Disconnects handle created by this.connect.
                //              Also removes handle from this widget's list of connects
                for(var i=0; i<this._connects.length; i++){
                        if(this._connects[i]==handles){
                                dojo.forEach(handles, dojo.disconnect);
                                this._connects.splice(i, 1);
                                return;
                        }
                }
        },

        isLeftToRight: function(){
                // summary:
                //              Checks the DOM to for the text direction for bi-directional support
                // description:
                //              This method cannot be used during widget construction because the widget
                //              must first be connected to the DOM tree.  Parent nodes are searched for the
                //              'dir' attribute until one is found, otherwise left to right mode is assumed.
                //              See HTML spec, DIR attribute for more information.

                if(typeof this._ltr == "undefined"){
                        this._ltr = dojo.getComputedStyle(this.domNode).direction != "rtl";
                }
                return this._ltr; //Boolean
        },

        isFocusable: function(){
                // summary:
                //              Return true if this widget can currently be focused
                //              and false if not
                return this.focus && (dojo.style(this.domNode, "display") != "none");
        }
});

}