1009 lines
31 KiB
JavaScript
1009 lines
31 KiB
JavaScript
|
dojo.provide("dijit._Widget");
|
||
|
|
||
|
//>>excludeStart("dijitBaseExclude", kwArgs.customDijitBase == "true");
|
||
|
dojo.require( "dijit._base" );
|
||
|
//>>excludeEnd("dijitBaseExclude");
|
||
|
|
||
|
dojo.connect(dojo, "connect",
|
||
|
function(/*Widget*/ widget, /*String*/ event){
|
||
|
if(widget && dojo.isFunction(widget._onConnect)){
|
||
|
widget._onConnect(event);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
dijit._connectOnUseEventHandler = function(/*Event*/ event){};
|
||
|
|
||
|
(function(){
|
||
|
|
||
|
var _attrReg = {};
|
||
|
var getAttrReg = function(dc){
|
||
|
if(!_attrReg[dc]){
|
||
|
var r = [];
|
||
|
var attrs;
|
||
|
var proto = dojo.getObject(dc).prototype;
|
||
|
for(var fxName in proto){
|
||
|
if(dojo.isFunction(proto[fxName]) && (attrs = fxName.match(/^_set([a-zA-Z]*)Attr$/)) && attrs[1]){
|
||
|
r.push(attrs[1].charAt(0).toLowerCase() + attrs[1].substr(1));
|
||
|
}
|
||
|
}
|
||
|
_attrReg[dc] = r;
|
||
|
}
|
||
|
return _attrReg[dc]||[];
|
||
|
}
|
||
|
|
||
|
dojo.declare("dijit._Widget", null, {
|
||
|
// summary:
|
||
|
// Base class for all dijit widgets.
|
||
|
|
||
|
// id: [const] 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: [const] String
|
||
|
// Rarely used. Overrides the default Dojo locale used to render this widget,
|
||
|
// as defined by the [HTML LANG](http://www.w3.org/TR/html401/struct/dirlang.html#adef-lang) attribute.
|
||
|
// Value must be among the list of locales specified during by the Dojo bootstrap,
|
||
|
// formatted according to [RFC 3066](http://www.ietf.org/rfc/rfc3066.txt) (like en-us).
|
||
|
lang: "",
|
||
|
|
||
|
// dir: [const] String
|
||
|
// Unsupported by Dijit, but here for completeness. Dijit only supports setting text direction on the
|
||
|
// entire document.
|
||
|
// Bi-directional support, as defined by the [HTML DIR](http://www.w3.org/TR/html401/struct/dirlang.html#adef-dir)
|
||
|
// attribute. Either left-to-right "ltr" or right-to-left "rtl".
|
||
|
dir: "",
|
||
|
|
||
|
// class: String
|
||
|
// HTML class attribute
|
||
|
"class": "",
|
||
|
|
||
|
// style: String||Object
|
||
|
// HTML style attributes as cssText string or name/value hash
|
||
|
style: "",
|
||
|
|
||
|
// title: String
|
||
|
// HTML title attribute, used to specify the title of tabs, accordion panes, etc.
|
||
|
title: "",
|
||
|
|
||
|
// srcNodeRef: [readonly] DomNode
|
||
|
// pointer to original dom node
|
||
|
srcNodeRef: null,
|
||
|
|
||
|
// domNode: [readonly] DomNode
|
||
|
// This is our visible representation of the widget! Other DOM
|
||
|
// Nodes may by assigned to other properties, usually through the
|
||
|
// template system's dojoAttachPoint syntax, but the domNode
|
||
|
// property is the canonical "top level" node in widget UI.
|
||
|
domNode: null,
|
||
|
|
||
|
// containerNode: [readonly] DomNode
|
||
|
// Designates where children of the source dom node will be placed.
|
||
|
// "Children" in this case refers to both dom nodes and widgets.
|
||
|
// For example, for myWidget:
|
||
|
//
|
||
|
// | <div dojoType=myWidget>
|
||
|
// | <b> here's a plain dom node
|
||
|
// | <span dojoType=subWidget>and a widget</span>
|
||
|
// | <i> and another plain dom node </i>
|
||
|
// | </div>
|
||
|
//
|
||
|
// containerNode would point to:
|
||
|
//
|
||
|
// | <b> here's a plain dom node
|
||
|
// | <span dojoType=subWidget>and a widget</span>
|
||
|
// | <i> and another plain dom node </i>
|
||
|
//
|
||
|
// In templated widgets, "containerNode" is set via a
|
||
|
// dojoAttachPoint assignment.
|
||
|
//
|
||
|
// containerNode must be defined for any widget that accepts innerHTML
|
||
|
// (like ContentPane or BorderContainer or even Button), and conversely
|
||
|
// is null for widgets that don't, like TextBox.
|
||
|
containerNode: null,
|
||
|
|
||
|
// attributeMap: [protected] Object
|
||
|
// attributeMap sets up a "binding" between attributes (aka properties)
|
||
|
// of the widget and the widget's DOM.
|
||
|
// Changes to widget attributes listed in attributeMap will be
|
||
|
// reflected into the DOM.
|
||
|
//
|
||
|
// For example, calling attr('title', 'hello')
|
||
|
// on a TitlePane will automatically cause the TitlePane's DOM to update
|
||
|
// with the new title.
|
||
|
//
|
||
|
// attributeMap is a hash where the key is an attribute of the widget,
|
||
|
// and the value reflects a binding to a:
|
||
|
//
|
||
|
// - DOM node attribute
|
||
|
// | focus: {node: "focusNode", type: "attribute"}
|
||
|
// Maps this.focus to this.focusNode.focus
|
||
|
//
|
||
|
// - DOM node innerHTML
|
||
|
// | title: { node: "titleNode", type: "innerHTML" }
|
||
|
// Maps this.title to this.titleNode.innerHTML
|
||
|
//
|
||
|
// - DOM node CSS class
|
||
|
// | myClass: { node: "domNode", type: "class" }
|
||
|
// Maps this.myClass to this.domNode.className
|
||
|
//
|
||
|
// If the value is an array, then each element in the array matches one of the
|
||
|
// formats of the above list.
|
||
|
//
|
||
|
// There are also some shorthands for backwards compatibility:
|
||
|
// - string --> { node: string, type: "attribute" }, for example:
|
||
|
// | "focusNode" ---> { node: "focusNode", type: "attribute" }
|
||
|
// - "" --> { node: "domNode", type: "attribute" }
|
||
|
attributeMap: {id:"", dir:"", lang:"", "class":"", style:"", title:""},
|
||
|
|
||
|
// _deferredConnects: [protected] Object
|
||
|
// attributeMap addendum for event handlers that should be connected only on first use
|
||
|
_deferredConnects: {
|
||
|
onClick: "",
|
||
|
onDblClick: "",
|
||
|
onKeyDown: "",
|
||
|
onKeyPress: "",
|
||
|
onKeyUp: "",
|
||
|
onMouseMove: "",
|
||
|
onMouseDown: "",
|
||
|
onMouseOut: "",
|
||
|
onMouseOver: "",
|
||
|
onMouseLeave: "",
|
||
|
onMouseEnter: "",
|
||
|
onMouseUp: ""},
|
||
|
|
||
|
onClick: dijit._connectOnUseEventHandler,
|
||
|
/*=====
|
||
|
onClick: function(event){
|
||
|
// summary:
|
||
|
// Connect to this function to receive notifications of mouse click events.
|
||
|
// event:
|
||
|
// mouse Event
|
||
|
// tags:
|
||
|
// callback
|
||
|
},
|
||
|
=====*/
|
||
|
onDblClick: dijit._connectOnUseEventHandler,
|
||
|
/*=====
|
||
|
onDblClick: function(event){
|
||
|
// summary:
|
||
|
// Connect to this function to receive notifications of mouse double click events.
|
||
|
// event:
|
||
|
// mouse Event
|
||
|
// tags:
|
||
|
// callback
|
||
|
},
|
||
|
=====*/
|
||
|
onKeyDown: dijit._connectOnUseEventHandler,
|
||
|
/*=====
|
||
|
onKeyDown: function(event){
|
||
|
// summary:
|
||
|
// Connect to this function to receive notifications of keys being pressed down.
|
||
|
// event:
|
||
|
// key Event
|
||
|
// tags:
|
||
|
// callback
|
||
|
},
|
||
|
=====*/
|
||
|
onKeyPress: dijit._connectOnUseEventHandler,
|
||
|
/*=====
|
||
|
onKeyPress: function(event){
|
||
|
// summary:
|
||
|
// Connect to this function to receive notifications of printable keys being typed.
|
||
|
// event:
|
||
|
// key Event
|
||
|
// tags:
|
||
|
// callback
|
||
|
},
|
||
|
=====*/
|
||
|
onKeyUp: dijit._connectOnUseEventHandler,
|
||
|
/*=====
|
||
|
onKeyUp: function(event){
|
||
|
// summary:
|
||
|
// Connect to this function to receive notifications of keys being released.
|
||
|
// event:
|
||
|
// key Event
|
||
|
// tags:
|
||
|
// callback
|
||
|
},
|
||
|
=====*/
|
||
|
onMouseDown: dijit._connectOnUseEventHandler,
|
||
|
/*=====
|
||
|
onMouseDown: function(event){
|
||
|
// summary:
|
||
|
// Connect to this function to receive notifications of when the mouse button is pressed down.
|
||
|
// event:
|
||
|
// mouse Event
|
||
|
// tags:
|
||
|
// callback
|
||
|
},
|
||
|
=====*/
|
||
|
onMouseMove: dijit._connectOnUseEventHandler,
|
||
|
/*=====
|
||
|
onMouseMove: function(event){
|
||
|
// summary:
|
||
|
// Connect to this function to receive notifications of when the mouse moves over nodes contained within this widget.
|
||
|
// event:
|
||
|
// mouse Event
|
||
|
// tags:
|
||
|
// callback
|
||
|
},
|
||
|
=====*/
|
||
|
onMouseOut: dijit._connectOnUseEventHandler,
|
||
|
/*=====
|
||
|
onMouseOut: function(event){
|
||
|
// summary:
|
||
|
// Connect to this function to receive notifications of when the mouse moves off of nodes contained within this widget.
|
||
|
// event:
|
||
|
// mouse Event
|
||
|
// tags:
|
||
|
// callback
|
||
|
},
|
||
|
=====*/
|
||
|
onMouseOver: dijit._connectOnUseEventHandler,
|
||
|
/*=====
|
||
|
onMouseOver: function(event){
|
||
|
// summary:
|
||
|
// Connect to this function to receive notifications of when the mouse moves onto nodes contained within this widget.
|
||
|
// event:
|
||
|
// mouse Event
|
||
|
// tags:
|
||
|
// callback
|
||
|
},
|
||
|
=====*/
|
||
|
onMouseLeave: dijit._connectOnUseEventHandler,
|
||
|
/*=====
|
||
|
onMouseLeave: function(event){
|
||
|
// summary:
|
||
|
// Connect to this function to receive notifications of when the mouse moves off of this widget.
|
||
|
// event:
|
||
|
// mouse Event
|
||
|
// tags:
|
||
|
// callback
|
||
|
},
|
||
|
=====*/
|
||
|
onMouseEnter: dijit._connectOnUseEventHandler,
|
||
|
/*=====
|
||
|
onMouseEnter: function(event){
|
||
|
// summary:
|
||
|
// Connect to this function to receive notifications of when the mouse moves onto this widget.
|
||
|
// event:
|
||
|
// mouse Event
|
||
|
// tags:
|
||
|
// callback
|
||
|
},
|
||
|
=====*/
|
||
|
onMouseUp: dijit._connectOnUseEventHandler,
|
||
|
/*=====
|
||
|
onMouseUp: function(event){
|
||
|
// summary:
|
||
|
// Connect to this function to receive notifications of when the mouse button is released.
|
||
|
// event:
|
||
|
// mouse Event
|
||
|
// tags:
|
||
|
// callback
|
||
|
},
|
||
|
=====*/
|
||
|
|
||
|
// Constants used in templates
|
||
|
|
||
|
// _blankGif: [protected] URL
|
||
|
// Used by <img> nodes in templates that really get there image via CSS background-image
|
||
|
_blankGif: (dojo.config.blankGif || dojo.moduleUrl("dojo", "resources/blank.gif")),
|
||
|
|
||
|
//////////// INITIALIZATION METHODS ///////////////////////////////////////
|
||
|
|
||
|
postscript: function(/*Object?*/params, /*DomNode|String*/srcNodeRef){
|
||
|
// summary:
|
||
|
// Kicks off widget instantiation. See create() for details.
|
||
|
// tags:
|
||
|
// private
|
||
|
this.create(params, srcNodeRef);
|
||
|
},
|
||
|
|
||
|
create: function(/*Object?*/params, /*DomNode|String?*/srcNodeRef){
|
||
|
// summary:
|
||
|
// Kick off the life-cycle of a widget
|
||
|
// params:
|
||
|
// Hash of initialization parameters for widget, including
|
||
|
// scalar values (like title, duration etc.) and functions,
|
||
|
// typically callbacks like onClick.
|
||
|
// srcNodeRef:
|
||
|
// If a srcNodeRef (dom node) is specified:
|
||
|
// - use srcNodeRef.innerHTML as my contents
|
||
|
// - if this is a behavioral widget then apply behavior
|
||
|
// to that srcNodeRef
|
||
|
// - otherwise, replace srcNodeRef with my generated DOM
|
||
|
// tree
|
||
|
// description:
|
||
|
// 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
|
||
|
// tags:
|
||
|
// private
|
||
|
|
||
|
// 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 = [];
|
||
|
|
||
|
// To avoid double-connects, remove entries from _deferredConnects
|
||
|
// that have been setup manually by a subclass (ex, by dojoAttachEvent).
|
||
|
// If a subclass has redefined a callback (ex: onClick) then assume it's being
|
||
|
// connected to manually.
|
||
|
this._deferredConnects = dojo.clone(this._deferredConnects);
|
||
|
for(var attr in this.attributeMap){
|
||
|
delete this._deferredConnects[attr]; // can't be in both attributeMap and _deferredConnects
|
||
|
}
|
||
|
for(attr in this._deferredConnects){
|
||
|
if(this[attr] !== dijit._connectOnUseEventHandler){
|
||
|
delete this._deferredConnects[attr]; // redefined, probably dojoAttachEvent exists
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//mixin our passed parameters
|
||
|
if(this.srcNodeRef && (typeof this.srcNodeRef.id == "string")){ this.id = this.srcNodeRef.id; }
|
||
|
if(params){
|
||
|
this.params = 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();
|
||
|
|
||
|
if(this.domNode){
|
||
|
// Copy attributes listed in attributeMap into the [newly created] DOM for the widget.
|
||
|
this._applyAttributes();
|
||
|
|
||
|
var source = this.srcNodeRef;
|
||
|
if(source && source.parentNode){
|
||
|
source.parentNode.replaceChild(this.domNode, source);
|
||
|
}
|
||
|
|
||
|
// If the developer has specified a handler as a widget parameter
|
||
|
// (ex: new Button({onClick: ...})
|
||
|
// then naturally need to connect from dom node to that handler immediately,
|
||
|
for(attr in this.params){
|
||
|
this._onConnect(attr);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
this._created = true;
|
||
|
},
|
||
|
|
||
|
_applyAttributes: function(){
|
||
|
// summary:
|
||
|
// Step during widget creation to copy all widget attributes to the
|
||
|
// DOM as per attributeMap and _setXXXAttr functions.
|
||
|
// description:
|
||
|
// Skips over blank/false attribute values, unless they were explicitly specified
|
||
|
// as parameters to the widget, since those are the default anyway,
|
||
|
// and setting tabIndex="" is different than not setting tabIndex at all.
|
||
|
//
|
||
|
// It processes the attributes in the attribute map first, and then
|
||
|
// it goes through and processes the attributes for the _setXXXAttr
|
||
|
// functions that have been specified
|
||
|
// tags:
|
||
|
// private
|
||
|
var condAttrApply = function(attr, scope){
|
||
|
if( (scope.params && attr in scope.params) || scope[attr]){
|
||
|
scope.attr(attr, scope[attr]);
|
||
|
}
|
||
|
};
|
||
|
for(var attr in this.attributeMap){
|
||
|
condAttrApply(attr, this);
|
||
|
}
|
||
|
dojo.forEach(getAttrReg(this.declaredClass), function(a){
|
||
|
if(!(a in this.attributeMap)){
|
||
|
condAttrApply(a, this);
|
||
|
}
|
||
|
}, this);
|
||
|
},
|
||
|
|
||
|
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.
|
||
|
// tags:
|
||
|
// protected
|
||
|
},
|
||
|
|
||
|
buildRendering: function(){
|
||
|
// summary:
|
||
|
// Construct the UI for this widget, setting this.domNode. Most
|
||
|
// widgets will mixin `dijit._Templated`, which implements this
|
||
|
// method.
|
||
|
// tags:
|
||
|
// protected
|
||
|
this.domNode = this.srcNodeRef || dojo.create('div');
|
||
|
},
|
||
|
|
||
|
postCreate: function(){
|
||
|
// summary:
|
||
|
// Called after a widget's dom has been setup
|
||
|
// tags:
|
||
|
// protected
|
||
|
},
|
||
|
|
||
|
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.
|
||
|
this._started = true;
|
||
|
},
|
||
|
|
||
|
//////////// DESTROY FUNCTIONS ////////////////////////////////
|
||
|
|
||
|
destroyRecursive: function(/*Boolean?*/ preserveDom){
|
||
|
// 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.
|
||
|
// preserveDom:
|
||
|
// If true, this method will leave the original Dom structure
|
||
|
// alone of descendant Widgets. Note: This will NOT work with
|
||
|
// dijit._Templated widgets.
|
||
|
|
||
|
this.destroyDescendants(preserveDom);
|
||
|
this.destroy(preserveDom);
|
||
|
},
|
||
|
|
||
|
destroy: function(/*Boolean*/ preserveDom){
|
||
|
// summary:
|
||
|
// Destroy this widget, but not its descendants.
|
||
|
// Will, however, destroy internal widgets such as those used within a template.
|
||
|
// preserveDom: Boolean
|
||
|
// If true, this method will leave the original Dom structure alone.
|
||
|
// Note: This will not yet work with _Templated widgets
|
||
|
|
||
|
this.uninitialize();
|
||
|
dojo.forEach(this._connects, function(array){
|
||
|
dojo.forEach(array, dojo.disconnect);
|
||
|
});
|
||
|
|
||
|
// destroy widgets created as part of template, etc.
|
||
|
dojo.forEach(this._supportingWidgets||[], function(w){
|
||
|
if(w.destroy){
|
||
|
w.destroy();
|
||
|
}
|
||
|
});
|
||
|
|
||
|
this.destroyRendering(preserveDom);
|
||
|
dijit.registry.remove(this.id);
|
||
|
},
|
||
|
|
||
|
destroyRendering: function(/*Boolean?*/ preserveDom){
|
||
|
// summary:
|
||
|
// Destroys the DOM nodes associated with this widget
|
||
|
// preserveDom:
|
||
|
// If true, this method will leave the original Dom structure alone
|
||
|
// during tear-down. Note: this will not work with _Templated
|
||
|
// widgets yet.
|
||
|
// tags:
|
||
|
// protected
|
||
|
|
||
|
if(this.bgIframe){
|
||
|
this.bgIframe.destroy(preserveDom);
|
||
|
delete this.bgIframe;
|
||
|
}
|
||
|
|
||
|
if(this.domNode){
|
||
|
if(preserveDom){
|
||
|
dojo.removeAttr(this.domNode, "widgetId");
|
||
|
}else{
|
||
|
dojo.destroy(this.domNode);
|
||
|
}
|
||
|
delete this.domNode;
|
||
|
}
|
||
|
|
||
|
if(this.srcNodeRef){
|
||
|
if(!preserveDom){
|
||
|
dojo.destroy(this.srcNodeRef);
|
||
|
}
|
||
|
delete this.srcNodeRef;
|
||
|
}
|
||
|
},
|
||
|
|
||
|
destroyDescendants: function(/*Boolean?*/ preserveDom){
|
||
|
// summary:
|
||
|
// Recursively destroy the children of this widget and their
|
||
|
// descendants.
|
||
|
// preserveDom:
|
||
|
// If true, the preserveDom attribute is passed to all descendant
|
||
|
// widget's .destroy() method. Not for use with _Templated
|
||
|
// widgets.
|
||
|
|
||
|
// get all direct descendants and destroy them recursively
|
||
|
dojo.forEach(this.getChildren(), function(widget){
|
||
|
if(widget.destroyRecursive){
|
||
|
widget.destroyRecursive(preserveDom);
|
||
|
}
|
||
|
});
|
||
|
},
|
||
|
|
||
|
|
||
|
uninitialize: function(){
|
||
|
// summary:
|
||
|
// Stub function. Override to implement custom widget tear-down
|
||
|
// behavior.
|
||
|
// tags:
|
||
|
// protected
|
||
|
return false;
|
||
|
},
|
||
|
|
||
|
////////////////// MISCELLANEOUS METHODS ///////////////////
|
||
|
|
||
|
onFocus: function(){
|
||
|
// summary:
|
||
|
// Called when the widget becomes "active" because
|
||
|
// it or a widget inside of it either has focus, or has recently
|
||
|
// been clicked.
|
||
|
// tags:
|
||
|
// callback
|
||
|
},
|
||
|
|
||
|
onBlur: function(){
|
||
|
// summary:
|
||
|
// Called when the widget stops being "active" because
|
||
|
// focus moved to something outside of it, or the user
|
||
|
// clicked somewhere outside of it, or the widget was
|
||
|
// hidden.
|
||
|
// tags:
|
||
|
// callback
|
||
|
},
|
||
|
|
||
|
_onFocus: function(e){
|
||
|
// summary:
|
||
|
// This is where widgets do processing for when they are active,
|
||
|
// such as changing CSS classes. See onFocus() for more details.
|
||
|
// tags:
|
||
|
// protected
|
||
|
this.onFocus();
|
||
|
},
|
||
|
|
||
|
_onBlur: function(){
|
||
|
// summary:
|
||
|
// This is where widgets do processing for when they stop being active,
|
||
|
// such as changing CSS classes. See onBlur() for more details.
|
||
|
// tags:
|
||
|
// protected
|
||
|
this.onBlur();
|
||
|
},
|
||
|
|
||
|
_onConnect: function(/*String*/ event){
|
||
|
// summary:
|
||
|
// Called when someone connects to one of my handlers.
|
||
|
// "Turn on" that handler if it isn't active yet.
|
||
|
//
|
||
|
// This is also called for every single initialization parameter
|
||
|
// so need to do nothing for parameters like "id".
|
||
|
// tags:
|
||
|
// private
|
||
|
if(event in this._deferredConnects){
|
||
|
var mapNode = this[this._deferredConnects[event]||'domNode'];
|
||
|
this.connect(mapNode, event.toLowerCase(), event);
|
||
|
delete this._deferredConnects[event];
|
||
|
}
|
||
|
},
|
||
|
|
||
|
_setClassAttr: function(/*String*/ value){
|
||
|
// summary:
|
||
|
// Custom setter for the CSS "class" attribute
|
||
|
// tags:
|
||
|
// protected
|
||
|
var mapNode = this[this.attributeMap["class"]||'domNode'];
|
||
|
dojo.removeClass(mapNode, this["class"])
|
||
|
this["class"] = value;
|
||
|
dojo.addClass(mapNode, value);
|
||
|
},
|
||
|
|
||
|
_setStyleAttr: function(/*String||Object*/ value){
|
||
|
// summary:
|
||
|
// Sets the style attribut of the widget according to value,
|
||
|
// which is either a hash like {height: "5px", width: "3px"}
|
||
|
// or a plain string
|
||
|
// description:
|
||
|
// Determines which node to set the style on based on style setting
|
||
|
// in attributeMap.
|
||
|
// tags:
|
||
|
// protected
|
||
|
|
||
|
var mapNode = this[this.attributeMap["style"]||'domNode'];
|
||
|
|
||
|
// Note: technically we should revert any style setting made in a previous call
|
||
|
// to his method, but that's difficult to keep track of.
|
||
|
|
||
|
if(dojo.isObject(value)){
|
||
|
dojo.style(mapNode, value);
|
||
|
}else{
|
||
|
if(mapNode.style.cssText){
|
||
|
mapNode.style.cssText += "; " + value;
|
||
|
}else{
|
||
|
mapNode.style.cssText = value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
this["style"] = value;
|
||
|
},
|
||
|
|
||
|
setAttribute: function(/*String*/ attr, /*anything*/ value){
|
||
|
// summary:
|
||
|
// Deprecated. Use attr() instead.
|
||
|
// tags:
|
||
|
// deprecated
|
||
|
dojo.deprecated(this.declaredClass+"::setAttribute() is deprecated. Use attr() instead.", "", "2.0");
|
||
|
this.attr(attr, value);
|
||
|
},
|
||
|
|
||
|
_attrToDom: function(/*String*/ attr, /*String*/ value){
|
||
|
// summary:
|
||
|
// Reflect a widget attribute (title, tabIndex, duration etc.) to
|
||
|
// the widget DOM, as specified in attributeMap.
|
||
|
//
|
||
|
// description:
|
||
|
// Also sets this["attr"] to the new value.
|
||
|
// Note some attributes like "type"
|
||
|
// cannot be processed this way as they are not mutable.
|
||
|
//
|
||
|
// tags:
|
||
|
// private
|
||
|
|
||
|
var commands = this.attributeMap[attr];
|
||
|
dojo.forEach( dojo.isArray(commands) ? commands : [commands], function(command){
|
||
|
|
||
|
// Get target node and what we are doing to that node
|
||
|
var mapNode = this[command.node || command || "domNode"]; // DOM node
|
||
|
var type = command.type || "attribute"; // class, innerHTML, or attribute
|
||
|
|
||
|
switch(type){
|
||
|
case "attribute":
|
||
|
if(dojo.isFunction(value)){ // functions execute in the context of the widget
|
||
|
value = dojo.hitch(this, value);
|
||
|
}
|
||
|
if(/^on[A-Z][a-zA-Z]*$/.test(attr)){ // eg. onSubmit needs to be onsubmit
|
||
|
attr = attr.toLowerCase();
|
||
|
}
|
||
|
dojo.attr(mapNode, attr, value);
|
||
|
break;
|
||
|
case "innerHTML":
|
||
|
mapNode.innerHTML = value;
|
||
|
break;
|
||
|
case "class":
|
||
|
dojo.removeClass(mapNode, this[attr]);
|
||
|
dojo.addClass(mapNode, value);
|
||
|
break;
|
||
|
}
|
||
|
}, this);
|
||
|
this[attr] = value;
|
||
|
},
|
||
|
|
||
|
attr: function(/*String|Object*/name, /*Object?*/value){
|
||
|
// summary:
|
||
|
// Set or get properties on a widget instance.
|
||
|
// name:
|
||
|
// The property to get or set. If an object is passed here and not
|
||
|
// a string, its keys are used as names of attributes to be set
|
||
|
// and the value of the object as values to set in the widget.
|
||
|
// value:
|
||
|
// Optional. If provided, attr() operates as a setter. If omitted,
|
||
|
// the current value of the named property is returned.
|
||
|
// description:
|
||
|
// Get or set named properties on a widget. If no value is
|
||
|
// provided, the current value of the attribute is returned,
|
||
|
// potentially via a getter method. If a value is provided, then
|
||
|
// the method acts as a setter, assigning the value to the name,
|
||
|
// potentially calling any explicitly provided setters to handle
|
||
|
// the operation. For instance, if the widget has properties "foo"
|
||
|
// and "bar" and a method named "_setFooAttr", calling:
|
||
|
// | myWidget.attr("foo", "Howdy!");
|
||
|
// would be equivalent to calling:
|
||
|
// | widget._setFooAttr("Howdy!");
|
||
|
// while calling:
|
||
|
// | myWidget.attr("bar", "Howdy!");
|
||
|
// would be the same as writing:
|
||
|
// | widget.bar = "Howdy!";
|
||
|
// It also tries to copy the changes to the widget's DOM according
|
||
|
// to settings in attributeMap (see description of `dijit._Widget.attributeMap`
|
||
|
// for details)
|
||
|
// For example, calling:
|
||
|
// | myTitlePane.attr("title", "Howdy!");
|
||
|
// will do
|
||
|
// | myTitlePane.title = "Howdy!";
|
||
|
// | myTitlePane.title.innerHTML = "Howdy!";
|
||
|
// It works for dom node attributes too. Calling
|
||
|
// | widget.attr("disabled", true)
|
||
|
// will set the disabled attribute on the widget's focusNode,
|
||
|
// among other housekeeping for a change in disabled state.
|
||
|
|
||
|
// open questions:
|
||
|
// - how to handle build shortcut for attributes which want to map
|
||
|
// into DOM attributes?
|
||
|
// - what relationship should setAttribute()/attr() have to
|
||
|
// layout() calls?
|
||
|
var args = arguments.length;
|
||
|
if(args == 1 && !dojo.isString(name)){
|
||
|
for(var x in name){ this.attr(x, name[x]); }
|
||
|
return this;
|
||
|
}
|
||
|
var names = this._getAttrNames(name);
|
||
|
if(args == 2){ // setter
|
||
|
if(this[names.s]){
|
||
|
// use the explicit setter
|
||
|
return this[names.s](value) || this;
|
||
|
}else{
|
||
|
// if param is specified as DOM node attribute, copy it
|
||
|
if(name in this.attributeMap){
|
||
|
this._attrToDom(name, value);
|
||
|
}
|
||
|
|
||
|
// FIXME: what about function assignments? Any way to connect() here?
|
||
|
this[name] = value;
|
||
|
}
|
||
|
return this;
|
||
|
}else{ // getter
|
||
|
if(this[names.g]){
|
||
|
return this[names.g]();
|
||
|
}else{
|
||
|
return this[name];
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
|
||
|
_attrPairNames: {}, // shared between all widgets
|
||
|
_getAttrNames: function(name){
|
||
|
// summary:
|
||
|
// Helper function for Widget.attr().
|
||
|
// Caches attribute name values so we don't do the string ops every time.
|
||
|
// tags:
|
||
|
// private
|
||
|
|
||
|
var apn = this._attrPairNames;
|
||
|
if(apn[name]){ return apn[name]; }
|
||
|
var uc = name.charAt(0).toUpperCase() + name.substr(1);
|
||
|
return apn[name] = {
|
||
|
n: name+"Node",
|
||
|
s: "_set"+uc+"Attr",
|
||
|
g: "_get"+uc+"Attr"
|
||
|
};
|
||
|
},
|
||
|
|
||
|
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:
|
||
|
// Returns all the widgets that contained by this, i.e., all widgets underneath this.containerNode.
|
||
|
// This method should generally be avoided as it returns widgets declared in templates, which are
|
||
|
// supposed to be internal/hidden, but it's left here for back-compat reasons.
|
||
|
|
||
|
if(this.containerNode){
|
||
|
var list = dojo.query('[widgetId]', this.containerNode);
|
||
|
return list.map(dijit.byNode); // Array
|
||
|
}else{
|
||
|
return [];
|
||
|
}
|
||
|
},
|
||
|
|
||
|
getChildren: function(){
|
||
|
// summary:
|
||
|
// Returns all the widgets contained by this, i.e., all widgets underneath this.containerNode.
|
||
|
// Does not return nested widgets, nor widgets that are part of this widget's template.
|
||
|
if(this.containerNode){
|
||
|
return dijit.findWidgets(this.containerNode);
|
||
|
}else{
|
||
|
return [];
|
||
|
}
|
||
|
},
|
||
|
|
||
|
// nodesWithKeyClick: [private] String[]
|
||
|
// List of nodes that correctly handle click events via native browser support,
|
||
|
// and don't need dijit's help
|
||
|
nodesWithKeyClick: ["input", "button"],
|
||
|
|
||
|
connect: function(
|
||
|
/*Object|null*/ obj,
|
||
|
/*String|Function*/ event,
|
||
|
/*String|Function*/ method){
|
||
|
// summary:
|
||
|
// Connects specified obj/event to specified method of this object
|
||
|
// and registers for disconnect() on widget destroy.
|
||
|
// description:
|
||
|
// Provide widget-specific analog to dojo.connect, except with the
|
||
|
// implicit use of this widget as the target object.
|
||
|
// This version of connect also provides a special "ondijitclick"
|
||
|
// event which triggers on a click or space-up, enter-down in IE
|
||
|
// or enter press in FF (since often can't cancel enter onkeydown
|
||
|
// in FF)
|
||
|
// example:
|
||
|
// | var btn = new dijit.form.Button();
|
||
|
// | // when foo.bar() is called, call the listener we're going to
|
||
|
// | // provide in the scope of btn
|
||
|
// | btn.connect(foo, "bar", function(){
|
||
|
// | console.debug(this.toString());
|
||
|
// | });
|
||
|
// tags:
|
||
|
// protected
|
||
|
|
||
|
var d = dojo;
|
||
|
var dc = dojo.connect;
|
||
|
var handles =[];
|
||
|
if(event == "ondijitclick"){
|
||
|
// add key based click activation for unsupported nodes.
|
||
|
if(!this.nodesWithKeyClick[obj.nodeName]){
|
||
|
var m = d.hitch(this, method);
|
||
|
handles.push(
|
||
|
dc(obj, "onkeydown", this, function(e){
|
||
|
if(!d.isFF && e.keyCode == d.keys.ENTER &&
|
||
|
!e.ctrlKey && !e.shiftKey && !e.altKey && !e.metaKey){
|
||
|
return m(e);
|
||
|
}else if(e.keyCode == d.keys.SPACE){
|
||
|
// stop space down as it causes IE to scroll
|
||
|
// the browser window
|
||
|
d.stopEvent(e);
|
||
|
}
|
||
|
}),
|
||
|
dc(obj, "onkeyup", this, function(e){
|
||
|
if(e.keyCode == d.keys.SPACE &&
|
||
|
!e.ctrlKey && !e.shiftKey && !e.altKey && !e.metaKey){ return m(e); }
|
||
|
})
|
||
|
);
|
||
|
if(d.isFF){
|
||
|
handles.push(
|
||
|
dc(obj, "onkeypress", this, function(e){
|
||
|
if(e.keyCode == d.keys.ENTER &&
|
||
|
!e.ctrlKey && !e.shiftKey && !e.altKey && !e.metaKey){ return m(e); }
|
||
|
})
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
event = "onclick";
|
||
|
}
|
||
|
handles.push(dc(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
|
||
|
// tags:
|
||
|
// protected
|
||
|
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 page for text direction
|
||
|
// tags:
|
||
|
// protected
|
||
|
return dojo._isBodyLtr(); //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");
|
||
|
},
|
||
|
|
||
|
placeAt: function(/* String|DomNode|_Widget */reference, /* String?|Int? */position){
|
||
|
// summary:
|
||
|
// Place this widget's domNode reference somewhere in the DOM based
|
||
|
// on standard dojo.place conventions, or passing a Widget reference that
|
||
|
// contains and addChild member.
|
||
|
//
|
||
|
// description:
|
||
|
// A convenience function provided in all _Widgets, providing a simple
|
||
|
// shorthand mechanism to put an existing (or newly created) Widget
|
||
|
// somewhere in the dom, and allow chaining.
|
||
|
//
|
||
|
// reference:
|
||
|
// The String id of a domNode, a domNode reference, or a reference to a Widget posessing
|
||
|
// an addChild method.
|
||
|
//
|
||
|
// position:
|
||
|
// If passed a string or domNode reference, the position argument
|
||
|
// accepts a string just as dojo.place does, one of: "first", "last",
|
||
|
// "before", or "after".
|
||
|
//
|
||
|
// If passed a _Widget reference, and that widget reference has an ".addChild" method,
|
||
|
// it will be called passing this widget instance into that method, supplying the optional
|
||
|
// position index passed.
|
||
|
//
|
||
|
// returns: dijit._Widget
|
||
|
// Provides a useful return of the newly created dijit._Widget instance so you
|
||
|
// can "chain" this function by instantiating, placing, then saving the return value
|
||
|
// to a variable.
|
||
|
//
|
||
|
// example:
|
||
|
// | // create a Button with no srcNodeRef, and place it in the body:
|
||
|
// | var button = new dijit.form.Button({ label:"click" }).placeAt(dojo.body());
|
||
|
// | // now, 'button' is still the widget reference to the newly created button
|
||
|
// | dojo.connect(button, "onClick", function(e){ console.log('click'); });
|
||
|
//
|
||
|
// example:
|
||
|
// | // create a button out of a node with id="src" and append it to id="wrapper":
|
||
|
// | var button = new dijit.form.Button({},"src").placeAt("wrapper");
|
||
|
//
|
||
|
// example:
|
||
|
// | // place a new button as the first element of some div
|
||
|
// | var button = new dijit.form.Button({ label:"click" }).placeAt("wrapper","first");
|
||
|
//
|
||
|
// example:
|
||
|
// | // create a contentpane and add it to a TabContainer
|
||
|
// | var tc = dijit.byId("myTabs");
|
||
|
// | new dijit.layout.ContentPane({ href:"foo.html", title:"Wow!" }).placeAt(tc)
|
||
|
|
||
|
if(reference["declaredClass"] && reference["addChild"]){
|
||
|
reference.addChild(this, position);
|
||
|
}else{
|
||
|
dojo.place(this.domNode, reference, position);
|
||
|
}
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
});
|
||
|
|
||
|
})();
|