You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tapestry.apache.org by jk...@apache.org on 2008/02/04 23:08:37 UTC
svn commit: r618461 [11/43] - in
/tapestry/tapestry4/trunk/tapestry-framework/src/js:
dojo-0.4.3-custom-4.1.5/ dojo-0.4.3-custom-4.1.5/nls/
dojo-0.4.3-custom-4.1.5/src/ dojo-0.4.3-custom-4.1.5/src/animation/
dojo-0.4.3-custom-4.1.5/src/cal/ dojo-0.4.3-...
Added: tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo-0.4.3-custom-4.1.5/dojo3.js.uncompressed.js
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo-0.4.3-custom-4.1.5/dojo3.js.uncompressed.js?rev=618461&view=auto
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo-0.4.3-custom-4.1.5/dojo3.js.uncompressed.js (added)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo-0.4.3-custom-4.1.5/dojo3.js.uncompressed.js Mon Feb 4 14:07:13 2008
@@ -0,0 +1,8566 @@
+/*
+ Copyright (c) 2004-2006, The Dojo Foundation
+ All Rights Reserved.
+
+ Licensed under the Academic Free License version 2.1 or above OR the
+ modified BSD license. For more information on Dojo licensing, see:
+
+ http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("layer.widget");
+dojo.provide("dojo.namespaces.dojo");
+dojo.require("dojo.ns");
+
+(function(){
+ // Mapping of all widget short names to their full package names
+ // This is used for widget autoloading - no dojo.require() is necessary.
+ // If you use a widget in markup or create one dynamically, then this
+ // mapping is used to find and load any dependencies not already loaded.
+ // You should use your own namespace for any custom widgets.
+ // For extra widgets you use, dojo.declare() may be used to explicitly load them.
+ // Experimental and deprecated widgets are not included in this table
+ var map = {
+ html: {
+ "accordioncontainer": "dojo.widget.AccordionContainer",
+ "animatedpng": "dojo.widget.AnimatedPng",
+ "button": "dojo.widget.Button",
+ "chart": "dojo.widget.Chart",
+ "checkbox": "dojo.widget.Checkbox",
+ "clock": "dojo.widget.Clock",
+ "colorpalette": "dojo.widget.ColorPalette",
+ "combobox": "dojo.widget.ComboBox",
+ "combobutton": "dojo.widget.Button",
+ "contentpane": "dojo.widget.ContentPane",
+ "currencytextbox": "dojo.widget.CurrencyTextbox",
+ "datepicker": "dojo.widget.DatePicker",
+ "datetextbox": "dojo.widget.DateTextbox",
+ "debugconsole": "dojo.widget.DebugConsole",
+ "dialog": "dojo.widget.Dialog",
+ "dropdownbutton": "dojo.widget.Button",
+ "dropdowndatepicker": "dojo.widget.DropdownDatePicker",
+ "dropdowntimepicker": "dojo.widget.DropdownTimePicker",
+ "emaillisttextbox": "dojo.widget.InternetTextbox",
+ "emailtextbox": "dojo.widget.InternetTextbox",
+ "editor": "dojo.widget.Editor",
+ "editor2": "dojo.widget.Editor2",
+ "filteringtable": "dojo.widget.FilteringTable",
+ "fisheyelist": "dojo.widget.FisheyeList",
+ "fisheyelistitem": "dojo.widget.FisheyeList",
+ "floatingpane": "dojo.widget.FloatingPane",
+ "modalfloatingpane": "dojo.widget.FloatingPane",
+ "form": "dojo.widget.Form",
+ "googlemap": "dojo.widget.GoogleMap",
+ "inlineeditbox": "dojo.widget.InlineEditBox",
+ "integerspinner": "dojo.widget.Spinner",
+ "integertextbox": "dojo.widget.IntegerTextbox",
+ "ipaddresstextbox": "dojo.widget.InternetTextbox",
+ "layoutcontainer": "dojo.widget.LayoutContainer",
+ "linkpane": "dojo.widget.LinkPane",
+ "popupmenu2": "dojo.widget.Menu2",
+ "menuitem2": "dojo.widget.Menu2",
+ "menuseparator2": "dojo.widget.Menu2",
+ "menubar2": "dojo.widget.Menu2",
+ "menubaritem2": "dojo.widget.Menu2",
+ "pagecontainer": "dojo.widget.PageContainer",
+ "pagecontroller": "dojo.widget.PageContainer",
+ "popupcontainer": "dojo.widget.PopupContainer",
+ "progressbar": "dojo.widget.ProgressBar",
+ "radiogroup": "dojo.widget.RadioGroup",
+ "realnumbertextbox": "dojo.widget.RealNumberTextbox",
+ "regexptextbox": "dojo.widget.RegexpTextbox",
+ "repeater": "dojo.widget.Repeater",
+ "resizabletextarea": "dojo.widget.ResizableTextarea",
+ "richtext": "dojo.widget.RichText",
+ "select": "dojo.widget.Select",
+ "show": "dojo.widget.Show",
+ "showaction": "dojo.widget.ShowAction",
+ "showslide": "dojo.widget.ShowSlide",
+ "slidervertical": "dojo.widget.Slider",
+ "sliderhorizontal": "dojo.widget.Slider",
+ "slider":"dojo.widget.Slider",
+ "slideshow": "dojo.widget.SlideShow",
+ "sortabletable": "dojo.widget.SortableTable",
+ "splitcontainer": "dojo.widget.SplitContainer",
+ "tabcontainer": "dojo.widget.TabContainer",
+ "tabcontroller": "dojo.widget.TabContainer",
+ "taskbar": "dojo.widget.TaskBar",
+ "textbox": "dojo.widget.Textbox",
+ "timepicker": "dojo.widget.TimePicker",
+ "timetextbox": "dojo.widget.DateTextbox",
+ "titlepane": "dojo.widget.TitlePane",
+ "toaster": "dojo.widget.Toaster",
+ "toggler": "dojo.widget.Toggler",
+ "toolbar": "dojo.widget.Toolbar",
+ "toolbarcontainer": "dojo.widget.Toolbar",
+ "toolbaritem": "dojo.widget.Toolbar",
+ "toolbarbuttongroup": "dojo.widget.Toolbar",
+ "toolbarbutton": "dojo.widget.Toolbar",
+ "toolbardialog": "dojo.widget.Toolbar",
+ "toolbarmenu": "dojo.widget.Toolbar",
+ "toolbarseparator": "dojo.widget.Toolbar",
+ "toolbarspace": "dojo.widget.Toolbar",
+ "toolbarselect": "dojo.widget.Toolbar",
+ "toolbarcolordialog": "dojo.widget.Toolbar",
+ "tooltip": "dojo.widget.Tooltip",
+ "tree": "dojo.widget.Tree",
+ "treebasiccontroller": "dojo.widget.TreeBasicController",
+ "treecontextmenu": "dojo.widget.TreeContextMenu",
+ "treedisablewrapextension": "dojo.widget.TreeDisableWrapExtension",
+ "treedociconextension": "dojo.widget.TreeDocIconExtension",
+ "treeeditor": "dojo.widget.TreeEditor",
+ "treeemphasizeonselect": "dojo.widget.TreeEmphasizeOnSelect",
+ "treeexpandtonodeonselect": "dojo.widget.TreeExpandToNodeOnSelect",
+ "treelinkextension": "dojo.widget.TreeLinkExtension",
+ "treeloadingcontroller": "dojo.widget.TreeLoadingController",
+ "treemenuitem": "dojo.widget.TreeContextMenu",
+ "treenode": "dojo.widget.TreeNode",
+ "treerpccontroller": "dojo.widget.TreeRPCController",
+ "treeselector": "dojo.widget.TreeSelector",
+ "treetoggleonselect": "dojo.widget.TreeToggleOnSelect",
+ "treev3": "dojo.widget.TreeV3",
+ "treebasiccontrollerv3": "dojo.widget.TreeBasicControllerV3",
+ "treecontextmenuv3": "dojo.widget.TreeContextMenuV3",
+ "treedndcontrollerv3": "dojo.widget.TreeDndControllerV3",
+ "treeloadingcontrollerv3": "dojo.widget.TreeLoadingControllerV3",
+ "treemenuitemv3": "dojo.widget.TreeContextMenuV3",
+ "treerpccontrollerv3": "dojo.widget.TreeRpcControllerV3",
+ "treeselectorv3": "dojo.widget.TreeSelectorV3",
+ "urltextbox": "dojo.widget.InternetTextbox",
+ "usphonenumbertextbox": "dojo.widget.UsTextbox",
+ "ussocialsecuritynumbertextbox": "dojo.widget.UsTextbox",
+ "usstatetextbox": "dojo.widget.UsTextbox",
+ "usziptextbox": "dojo.widget.UsTextbox",
+ "validationtextbox": "dojo.widget.ValidationTextbox",
+ "treeloadingcontroller": "dojo.widget.TreeLoadingController",
+ "wizardcontainer": "dojo.widget.Wizard",
+ "wizardpane": "dojo.widget.Wizard",
+ "yahoomap": "dojo.widget.YahooMap"
+ },
+ svg: {
+ "chart": "dojo.widget.svg.Chart"
+ },
+ vml: {
+ "chart": "dojo.widget.vml.Chart"
+ }
+ };
+
+ dojo.addDojoNamespaceMapping = function(/*String*/shortName, /*String*/packageName){
+ // summary:
+ // Add an entry to the mapping table for the dojo: namespace
+ //
+ // shortName: the name to be used as the widget's tag name in the dojo: namespace
+ // packageName: the path to the Javascript module in dotted package notation
+ map[shortName]=packageName;
+ };
+
+ function dojoNamespaceResolver(name, domain){
+ if(!domain){ domain="html"; }
+ if(!map[domain]){ return null; }
+ return map[domain][name];
+ }
+
+ dojo.registerNamespaceResolver("dojo", dojoNamespaceResolver);
+})();
+
+dojo.provide("dojo.xml.Parse");
+dojo.require("dojo.dom");
+
+//TODO: determine dependencies
+// currently has dependency on dojo.xml.DomUtil nodeTypes constants...
+
+// using documentFragment nomenclature to generalize in case we don't want to require passing a collection of nodes with a single parent
+
+dojo.xml.Parse = function(){
+ // summary:
+ // generic class for taking a DOM node and parsing it into an object
+ // based on the "dojo tag name" of that node.
+ //
+ // supported dojoTagName's:
+ // <prefix:tag> => prefix:tag
+ // <dojo:tag> => dojo:tag
+ // <dojoTag> => dojo:tag
+ // <tag dojoType="type"> => dojo:type
+ // <tag dojoType="prefix:type"> => prefix:type
+ // <tag dojo:type="type"> => dojo:type
+ // <tag class="classa dojo-type classb"> => dojo:type
+
+ var isIE = ((dojo.render.html.capable)&&(dojo.render.html.ie));
+
+ // get normalized (lowercase) tagName
+ // some browsers report tagNames in lowercase no matter what
+ function getTagName(node){
+ /*
+ return ((node)&&(node["tagName"]) ? node.tagName.toLowerCase() : '');
+ */
+ try{
+ return node.tagName.toLowerCase();
+ }catch(e){
+ return "";
+ }
+ }
+
+ // locate dojo qualified tag name
+ function getDojoTagName(node){
+ var tagName = getTagName(node);
+ if (!tagName){
+ return '';
+ }
+ // any registered tag
+ if((dojo.widget)&&(dojo.widget.tags[tagName])){
+ return tagName;
+ }
+ // <prefix:tag> => prefix:tag
+ var p = tagName.indexOf(":");
+ if(p>=0){
+ return tagName;
+ }
+ // <dojo:tag> => dojo:tag
+ if(tagName.substr(0,5) == "dojo:"){
+ return tagName;
+ }
+ if(dojo.render.html.capable && dojo.render.html.ie && node.scopeName != 'HTML'){
+ return node.scopeName.toLowerCase() + ':' + tagName;
+ }
+ // <dojoTag> => dojo:tag
+ if(tagName.substr(0,4) == "dojo"){
+ // FIXME: this assumes tag names are always lower case
+ return "dojo:" + tagName.substring(4);
+ }
+ // <tag dojoType="prefix:type"> => prefix:type
+ // <tag dojoType="type"> => dojo:type
+ var djt = node.getAttribute("dojoType") || node.getAttribute("dojotype");
+ if(djt){
+ if (djt.indexOf(":")<0){
+ djt = "dojo:"+djt;
+ }
+ return djt.toLowerCase();
+ }
+ // <tag dojo:type="type"> => dojo:type
+ djt = node.getAttributeNS && node.getAttributeNS(dojo.dom.dojoml,"type");
+ if(djt){
+ return "dojo:" + djt.toLowerCase();
+ }
+ // <tag dojo:type="type"> => dojo:type
+ try{
+ // FIXME: IE really really doesn't like this, so we squelch errors for it
+ djt = node.getAttribute("dojo:type");
+ }catch(e){
+ // FIXME: log?
+ }
+ if(djt){ return "dojo:"+djt.toLowerCase(); }
+ // <tag class="classa dojo-type classb"> => dojo:type
+ if((dj_global["djConfig"])&&(!djConfig["ignoreClassNames"])){
+ // FIXME: should we make this optionally enabled via djConfig?
+ var classes = node.className||node.getAttribute("class");
+ // FIXME: following line, without check for existence of classes.indexOf
+ // breaks firefox 1.5's svg widgets
+ if((classes )&&(classes.indexOf)&&(classes.indexOf("dojo-")!=-1)){
+ var aclasses = classes.split(" ");
+ for(var x=0, c=aclasses.length; x<c; x++){
+ if(aclasses[x].slice(0, 5) == "dojo-"){
+ return "dojo:"+aclasses[x].substr(5).toLowerCase();
+ }
+ }
+ }
+ }
+ // no dojo-qualified name
+ return '';
+ }
+
+
+ this.parseElement = function( /*DomNode*/node,
+ /*Boolean*/hasParentNodeSet,
+ /*Boolean*/optimizeForDojoML,
+ /*Integer*/thisIdx ){
+ // summary:
+ // recursively parse the passed node, returning a normalized data
+ // structure that represents the "attributes of interest" of said
+ // elements. If optimizeForDojoML is true, only nodes that contain
+ // a "dojo tag name" will be inspected for attributes.
+ // node: the DomNode to be treated as the root of inspection
+ // hasParentNodeSet: no-op, please pass "null"
+ // optimizeForDojoML: should we ignore non-Dojo nodes? Defaults to false.
+ // thisIdx:
+ // a way to specify a synthetic "index" property in the resulting
+ // data structure. Otherwise the index property of the top-level
+ // return element is always "0".
+
+ // TODOC: document return structure of a non-trivial element set
+
+ // run shortcuts to bail out of processing up front to save time and
+ // object alloc if possible.
+ var tagName = getTagName(node);
+ //There's a weird bug in IE where it counts end tags, e.g. </dojo:button> as nodes that should be parsed. Ignore these
+ if(isIE && tagName.indexOf("/")==0){ return null; }
+
+ try{
+ var attr = node.getAttribute("parseWidgets");
+ if(attr && attr.toLowerCase() == "false"){
+ return {};
+ }
+ }catch(e){/*continue*/}
+
+
+ // look for a dojoml qualified name
+ // process dojoml only when optimizeForDojoML is true
+ var process = true;
+ if(optimizeForDojoML){
+ var dojoTagName = getDojoTagName(node);
+ tagName = dojoTagName || tagName;
+ process = Boolean(dojoTagName);
+ }
+
+ var parsedNodeSet = {};
+ parsedNodeSet[tagName] = [];
+ var pos = tagName.indexOf(":");
+ if(pos>0){
+ var ns = tagName.substring(0,pos);
+ parsedNodeSet["ns"] = ns;
+ // honor user namespace filters
+ if((dojo.ns)&&(!dojo.ns.allow(ns))){process=false;}
+ }
+
+ if(process){
+ var attributeSet = this.parseAttributes(node);
+ for(var attr in attributeSet){
+ if((!parsedNodeSet[tagName][attr])||(typeof parsedNodeSet[tagName][attr] != "array")){
+ parsedNodeSet[tagName][attr] = [];
+ }
+ parsedNodeSet[tagName][attr].push(attributeSet[attr]);
+ }
+ // FIXME: we might want to make this optional or provide cloning instead of
+ // referencing, but for now, we include a node reference to allow
+ // instantiated components to figure out their "roots"
+ parsedNodeSet[tagName].nodeRef = node;
+ parsedNodeSet.tagName = tagName;
+ parsedNodeSet.index = thisIdx||0;
+ }
+
+ var count = 0;
+ for(var i = 0; i < node.childNodes.length; i++){
+ var tcn = node.childNodes.item(i);
+ switch(tcn.nodeType){
+ case dojo.dom.ELEMENT_NODE: // element nodes, call this function recursively
+ var ctn = getDojoTagName(tcn) || getTagName(tcn);
+ if(!parsedNodeSet[ctn]){
+ parsedNodeSet[ctn] = [];
+ }
+ parsedNodeSet[ctn].push(this.parseElement(tcn, true, optimizeForDojoML, count));
+ if( (tcn.childNodes.length == 1)&&
+ (tcn.childNodes.item(0).nodeType == dojo.dom.TEXT_NODE)){
+ parsedNodeSet[ctn][parsedNodeSet[ctn].length-1].value = tcn.childNodes.item(0).nodeValue;
+ }
+ count++;
+ break;
+ case dojo.dom.TEXT_NODE: // if a single text node is the child, treat it as an attribute
+ if(node.childNodes.length == 1){
+ parsedNodeSet[tagName].push({ value: node.childNodes.item(0).nodeValue });
+ }
+ break;
+ default: break;
+ /*
+ case dojo.dom.ATTRIBUTE_NODE: // attribute node... not meaningful here
+ break;
+ case dojo.dom.CDATA_SECTION_NODE: // cdata section... not sure if this would ever be meaningful... might be...
+ break;
+ case dojo.dom.ENTITY_REFERENCE_NODE: // entity reference node... not meaningful here
+ break;
+ case dojo.dom.ENTITY_NODE: // entity node... not sure if this would ever be meaningful
+ break;
+ case dojo.dom.PROCESSING_INSTRUCTION_NODE: // processing instruction node... not meaningful here
+ break;
+ case dojo.dom.COMMENT_NODE: // comment node... not not sure if this would ever be meaningful
+ break;
+ case dojo.dom.DOCUMENT_NODE: // document node... not sure if this would ever be meaningful
+ break;
+ case dojo.dom.DOCUMENT_TYPE_NODE: // document type node... not meaningful here
+ break;
+ case dojo.dom.DOCUMENT_FRAGMENT_NODE: // document fragment node... not meaningful here
+ break;
+ case dojo.dom.NOTATION_NODE:// notation node... not meaningful here
+ break;
+ */
+ }
+ }
+ //return (hasParentNodeSet) ? parsedNodeSet[node.tagName] : parsedNodeSet;
+ //if(parsedNodeSet.tagName)dojo.debug("parseElement: RETURNING NODE WITH TAGNAME "+parsedNodeSet.tagName);
+ return parsedNodeSet;
+ };
+
+
+ /* parses a set of attributes on a node into an object tree */
+ this.parseAttributes = function(/*DomNode*/node){
+ // summary:
+ // creates an attribute object that maps attribute values for the
+ // passed node. Note that this is similar to creating a JSON
+ // representation of a DOM node.
+ // usage:
+ // a node with the following serialization:
+ // <div foo="bar" baz="thud">...</div>
+ // would yeild the following return structure when passed into this
+ // function:
+ // {
+ // "foo": {
+ // "value": "bar"
+ // },
+ // "baz": {
+ // "value": "thud"
+ // }
+ // }
+ //
+ var parsedAttributeSet = {};
+ var atts = node.attributes;
+ // TODO: should we allow for duplicate attributes at this point...
+ // would any of the relevant dom implementations even allow this?
+ var attnode, i=0;
+ while((attnode=atts[i++])){
+ if(isIE){
+ if(!attnode){ continue; }
+ if((typeof attnode == "object")&&
+ (typeof attnode.nodeValue == 'undefined')||
+ (attnode.nodeValue == null)||
+ (attnode.nodeValue == '')){
+ continue;
+ }
+ }
+
+ var nn = attnode.nodeName.split(":");
+ nn = (nn.length == 2) ? nn[1] : attnode.nodeName;
+
+ parsedAttributeSet[nn] = {
+ value: attnode.nodeValue
+ };
+ }
+ return parsedAttributeSet;
+ };
+};
+
+dojo.provide("dojo.widget.Widget");
+
+dojo.require("dojo.lang.func");
+dojo.require("dojo.lang.array");
+dojo.require("dojo.lang.extras");
+dojo.require("dojo.lang.declare");
+dojo.require("dojo.ns");
+dojo.require("dojo.widget.Manager");
+dojo.require("dojo.event.*");
+
+dojo.declare("dojo.widget.Widget", null,
+ function(){
+ // these properties aren't primitives and need to be created on a per-item
+ // basis.
+
+ // children: Array
+ // a list of all of the widgets that have been added as children of
+ // this component. Should only have values if isContainer is true.
+ this.children = [];
+
+ // extraArgs: Object
+ // a map of properties which the widget system tried to assign from
+ // user input but did not correspond to any of the properties set on
+ // the class prototype. These names will also be available in all
+ // lower-case form in this map
+ this.extraArgs = {};
+ },
+{
+ // parent: Widget
+ // the parent of this widget
+ parent: null,
+
+ // isTopLevel: Boolean
+ // should this widget eat all events that bubble up to it?
+ // obviously, top-level and modal widgets should set these appropriately
+ isTopLevel: false,
+
+ // disabled: Boolean
+ // should this widget respond to user input?
+ // in markup, this is specified as "disabled='disabled'", or just "disabled"
+ disabled: false,
+
+ // isContainer: Boolean
+ // can this widget contain other widgets?
+ isContainer: false,
+
+ // widgetId: 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.
+ widgetId: "",
+
+ // widgetType: String
+ // used for building generic widgets
+ widgetType: "Widget",
+
+ // ns: String
+ // defaults to 'dojo'. "namespace" is a reserved word in JavaScript, so we abbreviate
+ ns: "dojo",
+
+ getNamespacedType: function(){
+ // summary:
+ // get the "full" name of the widget. If the widget comes from the
+ // "dojo" namespace and is a Button, calling this method will
+ // return "dojo:button", all lower-case
+ return (this.ns ? this.ns + ":" + this.widgetType : this.widgetType).toLowerCase(); // String
+ },
+
+ 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.getNamespacedType() + ', ' + (this.widgetId || 'NO ID') + ']'; // String
+ },
+
+ repr: function(){
+ // summary: returns the string representation of the widget.
+ return this.toString(); // String
+ },
+
+ enable: function(){
+ // summary:
+ // enables the widget, usually involving unmasking inputs and
+ // turning on event handlers. Not implemented here.
+ this.disabled = false;
+ },
+
+ disable: function(){
+ // summary:
+ // disables the widget, usually involves masking inputs and
+ // unsetting event handlers. Not implemented here.
+ this.disabled = true;
+ },
+
+ // TODO:
+ // 1) this would be better in HtmlWidget rather than here?
+ // 2) since many widgets don't care if they've been resized, maybe this should be a mixin?
+ onResized: function(){
+ // summary:
+ // A signal that widgets will call when they have been resized.
+ // Can be connected to for determining if a layout needs to be
+ // reflowed. Clients should override this function to do special
+ // processing, then call this.notifyChildrenOfResize() to notify
+ // children of resize.
+ this.notifyChildrenOfResize();
+ },
+
+ notifyChildrenOfResize: function(){
+ // summary: dispatches resized events to all children of this widget
+ for(var i=0; i<this.children.length; i++){
+ var child = this.children[i];
+ //dojo.debug(this.widgetId + " resizing child " + child.widgetId);
+ if( child.onResized ){
+ child.onResized();
+ }
+ }
+ },
+
+ create: function(args, fragment, parent, ns){
+ // summary:
+ // 'create' manages the initialization part of the widget
+ // lifecycle. It's called implicitly when any widget is created.
+ // All other initialization functions for widgets, except for the
+ // constructor, are called as a result of 'create' being fired.
+ // args: Object
+ // a normalized view of the parameters that the widget should take
+ // fragment: Object
+ // if the widget is being instantiated from markup, this object
+ // parent: Widget?
+ // the widget, if any, that this widget will be the child of. If
+ // none is passed, the global default widget is used.
+ // ns: String?
+ // what namespace the widget belongs to
+ // 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 over-ride. Of course, adventurous
+ // developers could over-ride '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:
+ //
+ // mixInProperties:
+ // takes the args and does lightweight type introspection
+ // on pre-existing object properties to initialize widget
+ // values by casting the values that are passed in args
+ // 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
+ // initialize:
+ // a stub function that you can over-ride.
+ // postInitialize:
+ // a stub function that you can over-ride.
+ // postCreate
+ // a stub function that you can over-ride to modify take
+ // actions once the widget has been placed in the UI
+ //
+ // all of these functions are passed the same arguments as are
+ // passed to 'create'
+
+ //dojo.profile.start(this.widgetType + " create");
+ if(ns){
+ this.ns = ns;
+ }
+ // dojo.debug(this.widgetType, "create");
+ //dojo.profile.start(this.widgetType + " satisfyPropertySets");
+ this.satisfyPropertySets(args, fragment, parent);
+ //dojo.profile.end(this.widgetType + " satisfyPropertySets");
+ // dojo.debug(this.widgetType, "-> mixInProperties");
+ //dojo.profile.start(this.widgetType + " mixInProperties");
+ this.mixInProperties(args, fragment, parent);
+ //dojo.profile.end(this.widgetType + " mixInProperties");
+ // dojo.debug(this.widgetType, "-> postMixInProperties");
+ //dojo.profile.start(this.widgetType + " postMixInProperties");
+ this.postMixInProperties(args, fragment, parent);
+ //dojo.profile.end(this.widgetType + " postMixInProperties");
+ // dojo.debug(this.widgetType, "-> dojo.widget.manager.add");
+ dojo.widget.manager.add(this);
+ // dojo.debug(this.widgetType, "-> buildRendering");
+ //dojo.profile.start(this.widgetType + " buildRendering");
+ this.buildRendering(args, fragment, parent);
+ //dojo.profile.end(this.widgetType + " buildRendering");
+ // dojo.debug(this.widgetType, "-> initialize");
+ //dojo.profile.start(this.widgetType + " initialize");
+ this.initialize(args, fragment, parent);
+ //dojo.profile.end(this.widgetType + " initialize");
+ // dojo.debug(this.widgetType, "-> postInitialize");
+ // postinitialize includes subcomponent creation
+ // profile is put directly to function
+ this.postInitialize(args, fragment, parent);
+ // dojo.debug(this.widgetType, "-> postCreate");
+ //dojo.profile.start(this.widgetType + " postCreate");
+ this.postCreate(args, fragment, parent);
+ //dojo.profile.end(this.widgetType + " postCreate");
+ // dojo.debug(this.widgetType, "done!");
+
+ //dojo.profile.end(this.widgetType + " create");
+
+ return this;
+ },
+
+ destroy: function(finalize){
+ // summary:
+ // Destroy this widget and it's descendants. This is the generic
+ // "destructor" function that all widget users should call to
+ // clealy 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?
+
+ // FIXME: this is woefully incomplete
+ if(this.parent){
+ this.parent.removeChild(this);
+ }
+ this.destroyChildren();
+ this.uninitialize();
+ this.destroyRendering(finalize);
+ dojo.widget.manager.removeById(this.widgetId);
+ },
+
+ destroyChildren: function(){
+ // summary:
+ // Recursively destroy the children of this widget and their
+ // descendents.
+ var widget;
+ var i=0;
+ while(this.children.length > i){
+ widget = this.children[i];
+ if (widget instanceof dojo.widget.Widget) { // find first widget
+ this.removeChild(widget);
+ widget.destroy();
+ continue;
+ }
+
+ i++; // skip data object
+ }
+
+ },
+
+ getChildrenOfType: function(/*String*/type, recurse){
+ // summary:
+ // return an array of descendant widgets who match the passed type
+ // recurse: Boolean
+ // should we try to get all descendants that match? Defaults to
+ // false.
+ var ret = [];
+ var isFunc = dojo.lang.isFunction(type);
+ if(!isFunc){
+ type = type.toLowerCase();
+ }
+ for(var x=0; x<this.children.length; x++){
+ if(isFunc){
+ if(this.children[x] instanceof type){
+ ret.push(this.children[x]);
+ }
+ }else{
+ if(this.children[x].widgetType.toLowerCase() == type){
+ ret.push(this.children[x]);
+ }
+ }
+ if(recurse){
+ ret = ret.concat(this.children[x].getChildrenOfType(type, recurse));
+ }
+ }
+ return ret; // Array
+ },
+
+ getDescendants: function(){
+ // returns: a flattened array of all direct descendants including self
+ var result = [];
+ var stack = [this];
+ var elem;
+ while ((elem = stack.pop())){
+ result.push(elem);
+ // a child may be data object without children field set (not widget)
+ if (elem.children) {
+ dojo.lang.forEach(elem.children, function(elem) { stack.push(elem); });
+ }
+ }
+ return result; // Array
+ },
+
+
+ isFirstChild: function(){
+ return this === this.parent.children[0]; // Boolean
+ },
+
+ isLastChild: function() {
+ return this === this.parent.children[this.parent.children.length-1]; // Boolean
+ },
+
+ satisfyPropertySets: function(args){
+ // summary: not implemented!
+
+ // dojo.profile.start("satisfyPropertySets");
+ // get the default propsets for our component type
+ /*
+ var typePropSets = []; // FIXME: need to pull these from somewhere!
+ var localPropSets = []; // pull out propsets from the parser's return structure
+
+ // for(var x=0; x<args.length; x++){
+ // }
+
+ for(var x=0; x<typePropSets.length; x++){
+ }
+
+ for(var x=0; x<localPropSets.length; x++){
+ }
+ */
+ // dojo.profile.end("satisfyPropertySets");
+
+ return args;
+ },
+
+ mixInProperties: function(args, /*Object*/frag){
+ // summary:
+ // takes the list of properties listed in args and sets values of
+ // the current object based on existence of properties with the
+ // same name (case insensitive) and the type of the pre-existing
+ // property. This is a lightweight conversion and is not intended
+ // to capture custom type semantics.
+ // args: Object
+ // A map of properties and values to set on the current object. By
+ // default it is assumed that properties in args are in string
+ // form and need to be converted. However, if there is a
+ // 'fastMixIn' property with the value 'true' in the args param,
+ // this assumption is ignored and all values in args are copied
+ // directly to the current object without any form of type
+ // casting.
+ // description:
+ // The mix-in code attempts to do some type-assignment based on
+ // PRE-EXISTING properties of the "this" object. When a named
+ // property of args is located, it is first tested to make
+ // sure that the current object already "has one". Properties
+ // which are undefined in the base widget are NOT settable here.
+ // The next step is to try to determine type of the pre-existing
+ // property. If it's a string, the property value is simply
+ // assigned. If a function, it is first cast using "new
+ // Function()" and the execution scope modified such that it
+ // always evaluates in the context of the current object. This
+ // listener is then added to the original function via
+ // dojo.event.connect(). If an Array, the system attempts to split
+ // the string value on ";" chars, and no further processing is
+ // attempted (conversion of array elements to a integers, for
+ // instance). If the property value is an Object
+ // (testObj.constructor === Object), the property is split first
+ // on ";" chars, secondly on ":" chars, and the resulting
+ // key/value pairs are assigned to an object in a map style. The
+ // onus is on the property user to ensure that all property values
+ // are converted to the expected type before usage. Properties
+ // which do not occur in the "this" object are assigned to the
+ // this.extraArgs map using both the original name and the
+ // lower-case name of the property. This allows for consistent
+ // access semantics regardless of the case preservation of the
+ // source of the property names.
+
+ if((args["fastMixIn"])||(frag["fastMixIn"])){
+ // dojo.profile.start("mixInProperties_fastMixIn");
+ // fast mix in assumes case sensitivity, no type casting, etc...
+ // dojo.lang.mixin(this, args);
+ for(var x in args){
+ this[x] = args[x];
+ }
+ // dojo.profile.end("mixInProperties_fastMixIn");
+ return;
+ }
+ // dojo.profile.start("mixInProperties");
+
+ var undef;
+
+ // NOTE: we cannot assume that the passed properties are case-correct
+ // (esp due to some browser bugs). Therefore, we attempt to locate
+ // properties for assignment regardless of case. This may cause
+ // problematic assignments and bugs in the future and will need to be
+ // documented with big bright neon lights.
+
+ // FIXME: fails miserably if a mixin property has a default value of null in
+ // a widget
+
+ // NOTE: caching lower-cased args in the prototype is only
+ // acceptable if the properties are invariant.
+ // if we have a name-cache, get it
+ var lcArgs = dojo.widget.lcArgsCache[this.widgetType];
+ if ( lcArgs == null ){
+ // build a lower-case property name cache if we don't have one
+ lcArgs = {};
+ for(var y in this){
+ lcArgs[((new String(y)).toLowerCase())] = y;
+ }
+ dojo.widget.lcArgsCache[this.widgetType] = lcArgs;
+ }
+ var visited = {};
+ for(var x in args){
+ if(!this[x]){ // check the cache for properties
+ var y = lcArgs[(new String(x)).toLowerCase()];
+ if(y){
+ args[y] = args[x];
+ x = y;
+ }
+ }
+ if(visited[x]){ continue; }
+ visited[x] = true;
+ if((typeof this[x]) != (typeof undef)){
+ if(typeof args[x] != "string"){
+ this[x] = args[x];
+ }else{
+ if(dojo.lang.isString(this[x])){
+ this[x] = args[x];
+ }else if(dojo.lang.isNumber(this[x])){
+ this[x] = new Number(args[x]); // FIXME: what if NaN is the result?
+ }else if(dojo.lang.isBoolean(this[x])){
+ this[x] = (args[x].toLowerCase()=="false") ? false : true;
+ }else if(dojo.lang.isFunction(this[x])){
+
+ // FIXME: need to determine if always over-writing instead
+ // of attaching here is appropriate. I suspect that we
+ // might want to only allow attaching w/ action items.
+
+ // RAR, 1/19/05: I'm going to attach instead of
+ // over-write here. Perhaps function objects could have
+ // some sort of flag set on them? Or mixed-into objects
+ // could have some list of non-mutable properties
+ // (although I'm not sure how that would alleviate this
+ // particular problem)?
+
+ // this[x] = new Function(args[x]);
+
+ // after an IRC discussion last week, it was decided
+ // that these event handlers should execute in the
+ // context of the widget, so that the "this" pointer
+ // takes correctly.
+
+ // argument that contains no punctuation other than . is
+ // considered a function spec, not code
+ if(args[x].search(/[^\w\.]+/i) == -1){
+ this[x] = dojo.evalObjPath(args[x], false);
+ }else{
+ var tn = dojo.lang.nameAnonFunc(new Function(args[x]), this);
+ dojo.event.kwConnect({
+ srcObj: this,
+ srcFunc: x,
+ adviceObj: this,
+ adviceFunc: tn
+ });
+ }
+ }else if(dojo.lang.isArray(this[x])){ // typeof [] == "object"
+ this[x] = args[x].split(";");
+ } else if (this[x] instanceof Date) {
+ this[x] = new Date(Number(args[x])); // assume timestamp
+ }else if(typeof this[x] == "object"){
+ // FIXME: should we be allowing extension here to handle
+ // other object types intelligently?
+
+ // if a plain string is passed to a property of type dojo.uri.Uri,
+ // we assume it is relative to root of dojo
+ if (this[x] instanceof dojo.uri.Uri){
+ this[x] = dojo.uri.dojoUri(args[x]);
+ }else{
+ // FIXME: unlike all other types, we do not replace the
+ // object with a new one here. Should we change that?
+ var pairs = args[x].split(";");
+ for(var y=0; y<pairs.length; y++){
+ var si = pairs[y].indexOf(":");
+ if((si != -1)&&(pairs[y].length>si)){
+ this[x][pairs[y].substr(0, si).replace(/^\s+|\s+$/g, "")] = pairs[y].substr(si+1);
+ }
+ }
+ }
+ }else{
+ // the default is straight-up string assignment. When would
+ // we ever hit this?
+ this[x] = args[x];
+ }
+ }
+ }else{
+ // collect any extra 'non mixed in' args
+ this.extraArgs[x.toLowerCase()] = args[x];
+ }
+ }
+ // dojo.profile.end("mixInProperties");
+ },
+
+ postMixInProperties: function(/*Object*/args, /*Object*/frag, /*Widget*/parent){
+ // 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.
+ },
+
+ initialize: function(/*Object*/args, /*Object*/frag, /*Widget*/parent){
+ // summary: stub function.
+ return false;
+ // dojo.unimplemented("dojo.widget.Widget.initialize");
+ },
+
+ postInitialize: function(/*Object*/args, /*Object*/frag, /*Widget*/parent){
+ // summary: stub function.
+ return false;
+ },
+
+ postCreate: function(/*Object*/args, /*Object*/frag, /*Widget*/parent){
+ // summary: stub function.
+ return false;
+ },
+
+ uninitialize: function(){
+ // summary:
+ // stub function. Over-ride to implement custom widget tear-down
+ // behavior.
+ return false;
+ },
+
+ buildRendering: function(/*Object*/args, /*Object*/frag, /*Widget*/parent){
+ // summary: stub function. SUBCLASSES MUST IMPLEMENT
+ dojo.unimplemented("dojo.widget.Widget.buildRendering, on "+this.toString()+", ");
+ return false;
+ },
+
+ destroyRendering: function(){
+ // summary: stub function. SUBCLASSES MUST IMPLEMENT
+ dojo.unimplemented("dojo.widget.Widget.destroyRendering");
+ return false;
+ },
+
+ addedTo: function(parent){
+ // summary:
+ // stub function this is just a signal that can be caught
+ // parent: Widget
+ // instance of dojo.widget.Widget that we were added to
+ },
+
+ addChild: function(child){
+ // summary: stub function. SUBCLASSES MUST IMPLEMENT
+ dojo.unimplemented("dojo.widget.Widget.addChild");
+ return false;
+ },
+
+ // Detach the given child widget from me, but don't destroy it
+ removeChild: function(/*Widget*/widget){
+ // summary:
+ // removes the passed widget instance from this widget but does
+ // not destroy it
+ for(var x=0; x<this.children.length; x++){
+ if(this.children[x] === widget){
+ this.children.splice(x, 1);
+ widget.parent=null;
+ break;
+ }
+ }
+ return widget; // Widget
+ },
+
+ getPreviousSibling: function(){
+ // summary:
+ // returns null if this is the first child of the parent,
+ // otherwise returns the next sibling to the "left".
+ var idx = this.getParentIndex();
+
+ // first node is idx=0 not found is idx<0
+ if (idx<=0) return null;
+
+ return this.parent.children[idx-1]; // Widget
+ },
+
+ getSiblings: function(){
+ // summary: gets an array of all children of our parent, including "this"
+ return this.parent.children; // Array
+ },
+
+ getParentIndex: function(){
+ // summary: what index are we at in the parent's children array?
+ return dojo.lang.indexOf(this.parent.children, this, true); // int
+ },
+
+ getNextSibling: function(){
+ // summary:
+ // returns null if this is the last child of the parent,
+ // otherwise returns the next sibling to the "right".
+
+ var idx = this.getParentIndex();
+
+ if (idx == this.parent.children.length-1){return null;} // last node
+ if (idx < 0){return null;} // not found
+
+ return this.parent.children[idx+1]; // Widget
+ }
+});
+
+// Lower case name cache: listing of the lower case elements in each widget.
+// We can't store the lcArgs in the widget itself because if B subclasses A,
+// then B.prototype.lcArgs might return A.prototype.lcArgs, which is not what we
+// want
+dojo.widget.lcArgsCache = {};
+
+// TODO: should have a more general way to add tags or tag libraries?
+// TODO: need a default tags class to inherit from for things like getting propertySets
+// TODO: parse properties/propertySets into component attributes
+// TODO: parse subcomponents
+// TODO: copy/clone raw markup fragments/nodes as appropriate
+dojo.widget.tags = {};
+dojo.widget.tags.addParseTreeHandler = function(/*String*/type){
+ // summary: deprecated!
+ dojo.deprecated("addParseTreeHandler", ". ParseTreeHandlers are now reserved for components. Any unfiltered DojoML tag without a ParseTreeHandler is assumed to be a widget", "0.5");
+ /*
+ var ltype = type.toLowerCase();
+ this[ltype] = function(fragment, widgetParser, parentComp, insertionIndex, localProps){
+ var _ltype = ltype;
+ dojo.profile.start(_ltype);
+ var n = dojo.widget.buildWidgetFromParseTree(ltype, fragment, widgetParser, parentComp, insertionIndex, localProps);
+ dojo.profile.end(_ltype);
+ return n;
+ }
+ */
+}
+
+//dojo.widget.tags.addParseTreeHandler("dojo:widget");
+
+dojo.widget.tags["dojo:propertyset"] = function(fragment, widgetParser, parentComp){
+ // FIXME: Is this needed?
+ // FIXME: Not sure that this parses into the structure that I want it to parse into...
+ // FIXME: add support for nested propertySets
+ var properties = widgetParser.parseProperties(fragment["dojo:propertyset"]);
+}
+
+// FIXME: need to add the <dojo:connect />
+dojo.widget.tags["dojo:connect"] = function(fragment, widgetParser, parentComp){
+ var properties = widgetParser.parseProperties(fragment["dojo:connect"]);
+}
+
+// FIXME: if we know the insertion point (to a reasonable location), why then do we:
+// - create a template node
+// - clone the template node
+// - render the clone and set properties
+// - remove the clone from the render tree
+// - place the clone
+// this is quite dumb
+dojo.widget.buildWidgetFromParseTree = function(/*String*/ type,
+ /*Object*/ frag,
+ /*dojo.widget.Parse*/ parser,
+ /*Widget, optional*/ parentComp,
+ /*int, optional*/ insertionIndex,
+ /*Object*/ localProps){
+
+ // summary: creates a tree of widgets from the data structure produced by the first-pass parser (frag)
+
+
+ var stype = type.split(":");
+ stype = (stype.length == 2) ? stype[1] : type;
+
+ // FIXME: we don't seem to be doing anything with this!
+ // var propertySets = parser.getPropertySets(frag);
+ var localProperties = localProps || parser.parseProperties(frag[frag["ns"]+":"+stype]);
+ var twidget = dojo.widget.manager.getImplementation(stype,null,null,frag["ns"]);
+ if(!twidget){
+ throw new Error('cannot find "' + type + '" widget');
+ }else if (!twidget.create){
+ throw new Error('"' + type + '" widget object has no "create" method and does not appear to implement *Widget');
+ }
+ localProperties["dojoinsertionindex"] = insertionIndex;
+ // FIXME: we lose no less than 5ms in construction!
+ var ret = twidget.create(localProperties, frag, parentComp, frag["ns"]);
+ // dojo.profile.end("buildWidgetFromParseTree");
+ return ret;
+}
+
+dojo.widget.defineWidget = function(widgetClass, renderer, superclasses, init, props){
+ // summary: Create a widget constructor function (aka widgetClass)
+ // widgetClass: String
+ // the location in the object hierarchy to place the new widget class constructor
+ // renderer: String
+ // usually "html", determines when this delcaration will be used
+ // superclasses: Function||Function[]
+ // can be either a single function or an array of functions to be
+ // mixed in as superclasses. If an array, only the first will be used
+ // to set prototype inheritance.
+ // init: Function
+ // an optional constructor function. Will be called after superclasses are mixed in.
+ // props: Object
+ // a map of properties and functions to extend the class prototype with
+
+ // This meta-function does parameter juggling for backward compat and overloading
+ // if 4th argument is a string, we are using the old syntax
+ // old sig: widgetClass, superclasses, props (object), renderer (string), init (function)
+ if(dojo.lang.isString(arguments[3])){
+ dojo.widget._defineWidget(arguments[0], arguments[3], arguments[1], arguments[4], arguments[2]);
+ }else{
+ // widgetClass
+ var args = [ arguments[0] ], p = 3;
+ if(dojo.lang.isString(arguments[1])){
+ // renderer, superclass
+ args.push(arguments[1], arguments[2]);
+ }else{
+ // superclass
+ args.push('', arguments[1]);
+ p = 2;
+ }
+ if(dojo.lang.isFunction(arguments[p])){
+ // init (function), props (object)
+ args.push(arguments[p], arguments[p+1]);
+ }else{
+ // props (object)
+ args.push(null, arguments[p]);
+ }
+ dojo.widget._defineWidget.apply(this, args);
+ }
+}
+
+dojo.widget.defineWidget.renderers = "html|svg|vml";
+
+dojo.widget._defineWidget = function(widgetClass /*string*/, renderer /*string*/, superclasses /*function||array*/, init /*function*/, props /*object*/){
+ // FIXME: uncomment next line to test parameter juggling ... remove when confidence improves
+ // dojo.debug('(c:)' + widgetClass + '\n\n(r:)' + renderer + '\n\n(i:)' + init + '\n\n(p:)' + props);
+ // widgetClass takes the form foo.bar.baz<.renderer>.WidgetName (e.g. foo.bar.baz.WidgetName or foo.bar.baz.html.WidgetName)
+ var module = widgetClass.split(".");
+ var type = module.pop(); // type <= WidgetName, module <= foo.bar.baz<.renderer>
+ var regx = "\\.(" + (renderer ? renderer + '|' : '') + dojo.widget.defineWidget.renderers + ")\\.";
+ var r = widgetClass.search(new RegExp(regx));
+ module = (r < 0 ? module.join(".") : widgetClass.substr(0, r));
+
+ // deprecated in favor of namespace system, remove for 0.5
+ dojo.widget.manager.registerWidgetPackage(module);
+
+ var pos = module.indexOf(".");
+ var nsName = (pos > -1) ? module.substring(0,pos) : module;
+
+ // FIXME: hrm, this might make things simpler
+ //dojo.widget.tags.addParseTreeHandler(nsName+":"+type.toLowerCase());
+
+ props=(props)||{};
+ props.widgetType = type;
+ if((!init)&&(props["classConstructor"])){
+ init = props.classConstructor;
+ delete props.classConstructor;
+ }
+ dojo.declare(widgetClass, superclasses, init, props);
+}
+
+dojo.provide("dojo.widget.Parse");
+dojo.require("dojo.widget.Manager");
+dojo.require("dojo.dom");
+
+//
+// dojoML parser should be moved out of 'widget', codifying the difference between a 'component'
+// and a 'widget'. A 'component' being anything that can be generated from a tag.
+//
+// a particular dojoML tag would be handled by a registered tagHandler with a hook for a default handler
+// if the widget system is loaded, a widget builder would be attach itself as the default handler
+//
+// widget tags are no longer registered themselves:
+// they are now arbitrarily namespaced, so we cannot register them all, and the non-prefixed portions
+// are no longer guaranteed unique
+//
+// therefore dojo.widget.tags should go with this parser code out of the widget module
+//
+
+dojo.widget.Parse = function(/*Object*/fragment){
+ this.propertySetsList = [];
+ this.fragment = fragment;
+
+ this.createComponents = function(/*Object*/frag, /*Object*/parentComp){
+ var comps = [];
+ var built = false;
+ // if we have items to parse/create at this level, do it!
+ try{
+ if(frag && frag.tagName && (frag != frag.nodeRef)){
+
+ // these are in fact, not ever for widgets per-se anymore,
+ // but for other markup elements (aka components)
+ var djTags = dojo.widget.tags;
+
+ // we split so that you can declare multiple
+ // non-destructive components from the same ctor node
+ var tna = String(frag.tagName).split(";");
+ for(var x=0; x<tna.length; x++){
+ var ltn = tna[x].replace(/^\s+|\s+$/g, "").toLowerCase();
+ // FIXME: unsure what this does
+ frag.tagName = ltn;
+ var ret;
+ if(djTags[ltn]){
+ built = true;
+ ret = djTags[ltn](frag, this, parentComp, frag.index);
+ comps.push(ret);
+ }else{
+ // we require a namespace prefix, default to dojo:
+ if(ltn.indexOf(":") == -1){
+ ltn = "dojo:"+ltn;
+ }
+ // FIXME: handling failure condition correctly?
+ // ret = djTags[ltn](frag, this, parentComp, frag.index);
+ ret = dojo.widget.buildWidgetFromParseTree(ltn, frag, this, parentComp, frag.index);
+ if(ret){
+ built = true;
+ comps.push(ret);
+ }
+ }
+ }
+ }
+ }catch(e){
+ dojo.debug("dojo.widget.Parse: error:", e);
+ // note, commenting out the next line is breaking several widgets for me
+ // throw e;
+ // IE is such a pain sometimes
+ }
+ // if there's a sub-frag, build widgets from that too
+ if(!built){
+ comps = comps.concat(this.createSubComponents(frag, parentComp));
+ }
+ return comps; // Array
+ }
+
+ this.createSubComponents = function(/*Object*/fragment, /*Object*/parentComp){
+ // summary: recurses over a raw JavaScript object structure,
+ // and calls the corresponding handler for its normalized tagName if it exists
+
+ var frag, comps = [];
+ for(var item in fragment){
+ frag = fragment[item];
+ if(frag && typeof frag == "object"
+ &&(frag!=fragment.nodeRef)
+ &&(frag!=fragment.tagName)
+ &&(!dojo.dom.isNode(frag))){// needed in IE when we have event.connected to the domNode
+ comps = comps.concat(this.createComponents(frag, parentComp));
+ }
+ }
+ return comps; // Array
+ }
+
+ this.parsePropertySets = function(/*Object*/fragment){
+ // summary: checks the top level of a raw JavaScript object
+ // structure for any propertySets. It stores an array of references to
+ // propertySets that it finds.
+ return [];
+ /*
+ var propertySets = [];
+ for(var item in fragment){
+ if((fragment[item]["tagName"] == "dojo:propertyset")){
+ propertySets.push(fragment[item]);
+ }
+ }
+ // FIXME: should we store these propertySets somewhere for later retrieval
+ this.propertySetsList.push(propertySets);
+ return propertySets;
+ */
+ }
+
+ this.parseProperties = function(/*Object*/fragment){
+ // summary: parseProperties checks a raw JavaScript object structure for
+ // properties, and returns a hash of properties that it finds.
+ var properties = {};
+ for(var item in fragment){
+ // FIXME: need to check for undefined?
+ // case: its a tagName or nodeRef
+ if((fragment[item] == fragment.tagName)||(fragment[item] == fragment.nodeRef)){
+ // do nothing
+ }else{
+ var frag = fragment[item];
+ if(frag.tagName && dojo.widget.tags[frag.tagName.toLowerCase()]){
+ // TODO: it isn't a property or property set, it's a fragment,
+ // so do something else
+ // FIXME: needs to be a better/stricter check
+ // TODO: handle xlink:href for external property sets
+ }else if(frag[0] && frag[0].value!="" && frag[0].value!=null){
+ try{
+ // FIXME: need to allow more than one provider
+ if(item.toLowerCase() == "dataprovider"){
+ var _this = this;
+ this.getDataProvider(_this, frag[0].value);
+ properties.dataProvider = this.dataProvider;
+ }
+ properties[item] = frag[0].value;
+ var nestedProperties = this.parseProperties(frag);
+ // FIXME: this kind of copying is expensive and inefficient!
+ for(var property in nestedProperties){
+ properties[property] = nestedProperties[property];
+ }
+ }catch(e){ dojo.debug(e); }
+ }
+ switch(item.toLowerCase()){
+ case "checked":
+ case "disabled":
+ if (typeof properties[item] != "boolean"){
+ properties[item] = true;
+ }
+ break;
+ }
+ }
+ }
+ return properties; // Object
+ }
+
+ this.getDataProvider = function(/*Object*/objRef, /*String*/dataUrl){
+ // FIXME: this is currently sync. To make this async, we made need to move
+ //this step into the widget ctor, so that it is loaded when it is needed
+ // to populate the widget
+ dojo.io.bind({
+ url: dataUrl,
+ load: function(type, evaldObj){
+ if(type=="load"){
+ objRef.dataProvider = evaldObj;
+ }
+ },
+ mimetype: "text/javascript",
+ sync: true
+ });
+ }
+
+ this.getPropertySetById = function(propertySetId){
+ // summary: returns the propertySet that matches the provided id
+ for(var x = 0; x < this.propertySetsList.length; x++){
+ if(propertySetId == this.propertySetsList[x]["id"][0].value){
+ return this.propertySetsList[x];
+ }
+ }
+ return ""; // String
+ }
+
+ //FIXME: doesn't use the componentType param?
+ this.getPropertySetsByType = function(componentType){
+ // summary: returns the propertySet(s) that match(es) the
+ // provided componentClass
+
+ var propertySets = [];
+ for(var x=0; x < this.propertySetsList.length; x++){
+ var cpl = this.propertySetsList[x];
+ var cpcc = cpl.componentClass || cpl.componentType || null; //FIXME: is componentType supposed to be an indirect reference?
+ var propertySetId = this.propertySetsList[x]["id"][0].value;
+ if(cpcc && (propertySetId == cpcc[0].value)){
+ propertySets.push(cpl);
+ }
+ }
+ return propertySets; // Array
+ }
+
+ this.getPropertySets = function(/*Object*/fragment){
+ // summary: returns the propertySet for a given component fragment
+
+ var ppl = "dojo:propertyproviderlist";
+ var propertySets = [];
+ var tagname = fragment.tagName;
+ if(fragment[ppl]){
+ var propertyProviderIds = fragment[ppl].value.split(" ");
+ // FIXME: should the propertyProviderList attribute contain #
+ // syntax for reference to ids or not?
+ // FIXME: need a better test to see if this is local or external
+ // FIXME: doesn't handle nested propertySets, or propertySets that
+ // just contain information about css documents, etc.
+ for(var propertySetId in propertyProviderIds){
+ if((propertySetId.indexOf("..")==-1)&&(propertySetId.indexOf("://")==-1)){
+ // get a reference to a propertySet within the current parsed structure
+ var propertySet = this.getPropertySetById(propertySetId);
+ if(propertySet != ""){
+ propertySets.push(propertySet);
+ }
+ }else{
+ // FIXME: add code to parse and return a propertySet from
+ // another document
+ // alex: is this even necessaray? Do we care? If so, why?
+ }
+ }
+ }
+ // we put the typed ones first so that the parsed ones override when
+ // iteration happens.
+ return this.getPropertySetsByType(tagname).concat(propertySets); // Array
+ }
+
+ this.createComponentFromScript = function(/*Node*/nodeRef, /*String*/componentName, /*Object*/properties, /*String?*/ns){
+ // summary:
+ // nodeRef: the node to be replaced... in the future, we might want to add
+ // an alternative way to specify an insertion point
+ // componentName: the expected dojo widget name, i.e. Button of ContextMenu
+ // properties: an object of name value pairs
+ // ns: the namespace of the widget. Defaults to "dojo"
+
+ properties.fastMixIn = true;
+ // FIXME: we pulled it apart and now we put it back together ...
+ var ltn = (ns || "dojo") + ":" + componentName.toLowerCase();
+ if(dojo.widget.tags[ltn]){
+ return [dojo.widget.tags[ltn](properties, this, null, null, properties)]; // Array
+ }
+ return [dojo.widget.buildWidgetFromParseTree(ltn, properties, this, null, null, properties)]; // Array
+ }
+}
+
+dojo.widget._parser_collection = {"dojo": new dojo.widget.Parse() };
+
+dojo.widget.getParser = function(/*String?*/name){
+ if(!name){ name = "dojo"; }
+ if(!this._parser_collection[name]){
+ this._parser_collection[name] = new dojo.widget.Parse();
+ }
+ return this._parser_collection[name];
+}
+
+dojo.widget.createWidget = function(/*String*/name, /*String*/props, /*Node*/refNode, /*String*/position){
+ // summary: Creates widget
+ // name: The name of the widget to create with optional namespace prefix,
+ // e.g."ns:widget", namespace defaults to "dojo".
+ // props: Key-Value pairs of properties of the widget
+ // refNode: If the position argument is specified, this node is used as
+ // a reference for inserting this node into a DOM tree; else
+ // the widget becomes the domNode
+ // position: The position to insert this widget's node relative to the
+ // refNode argument
+
+ var isNode = false;
+ var isNameStr = (typeof name == "string");
+ if(isNameStr){
+ var pos = name.indexOf(":");
+ var ns = (pos > -1) ? name.substring(0,pos) : "dojo";
+ if(pos > -1){ name = name.substring(pos+1); }
+ var lowerCaseName = name.toLowerCase();
+ var namespacedName = ns + ":" + lowerCaseName;
+ isNode = (dojo.byId(name) && !dojo.widget.tags[namespacedName]);
+ }
+
+ if((arguments.length == 1) && (isNode || !isNameStr)){
+ // we got a DOM node
+ var xp = new dojo.xml.Parse();
+ // FIXME: we should try to find the parent!
+ var tn = isNode ? dojo.byId(name) : name;
+ return dojo.widget.getParser().createComponents(xp.parseElement(tn, null, true))[0];
+ }
+
+ function fromScript(placeKeeperNode, name, props, ns){
+ props[namespacedName] = {
+ dojotype: [{value: lowerCaseName}],
+ nodeRef: placeKeeperNode,
+ fastMixIn: true
+ };
+ props.ns = ns;
+ return dojo.widget.getParser().createComponentFromScript(placeKeeperNode, name, props, ns);
+ }
+
+ props = props||{};
+ var notRef = false;
+ var tn = null;
+ var h = dojo.render.html.capable;
+ if(h){
+ tn = document.createElement("span");
+ }
+ if(!refNode){
+ notRef = true;
+ refNode = tn;
+ if(h){
+ dojo.body().appendChild(refNode);
+ }
+ }else if(position){
+ dojo.dom.insertAtPosition(tn, refNode, position);
+ }else{ // otherwise don't replace, but build in-place
+ tn = refNode;
+ }
+ var widgetArray = fromScript(tn, name.toLowerCase(), props, ns);
+ if( (!widgetArray)||(!widgetArray[0])||
+ (typeof widgetArray[0].widgetType == "undefined") ){
+ throw new Error("createWidget: Creation of \"" + name + "\" widget failed.");
+ }
+ try{
+ if(notRef && widgetArray[0].domNode.parentNode){
+ widgetArray[0].domNode.parentNode.removeChild(widgetArray[0].domNode);
+ }
+ }catch(e){
+ /* squelch for Safari */
+ dojo.debug(e);
+ }
+ return widgetArray[0]; // Widget
+}
+
+dojo.kwCompoundRequire({
+ common: [["dojo.uri.Uri", false, false]]
+});
+dojo.provide("dojo.uri.*");
+
+dojo.provide("dojo.widget.DomWidget");
+
+dojo.require("dojo.event.*");
+
+dojo.require("dojo.dom");
+dojo.require("dojo.html.style");
+
+
+dojo.require("dojo.lang.func");
+dojo.require("dojo.lang.extras");
+
+dojo.widget._cssFiles = {};
+dojo.widget._cssStrings = {};
+dojo.widget._templateCache = {};
+
+dojo.widget.defaultStrings = {
+ // summary: a mapping of strings that are used in template variable replacement
+ dojoRoot: dojo.hostenv.getBaseScriptUri(),
+ dojoWidgetModuleUri: dojo.uri.moduleUri("dojo.widget"),
+ baseScriptUri: dojo.hostenv.getBaseScriptUri()
+};
+
+dojo.widget.fillFromTemplateCache = function(obj, templatePath, templateString, avoidCache){
+ // summary:
+ // static method to build from a template w/ or w/o a real widget in
+ // place
+ // obj: DomWidget
+ // an instance of dojo.widget.DomWidget to initialize the template for
+ // templatePath: String
+ // the URL to get the template from. dojo.uri.Uri is often passed as well.
+ // templateString: String?
+ // a string to use in lieu of fetching the template from a URL
+ // avoidCache: Boolean?
+ // should the template system not use whatever is in the cache and
+ // always use the passed templatePath or templateString?
+
+ // dojo.debug("avoidCache:", avoidCache);
+ var tpath = templatePath || obj.templatePath;
+
+ var tmplts = dojo.widget._templateCache;
+ if(!tpath && !obj["widgetType"]) { // don't have a real template here
+ do {
+ var dummyName = "__dummyTemplate__" + dojo.widget._templateCache.dummyCount++;
+ } while(tmplts[dummyName]);
+ obj.widgetType = dummyName;
+ }
+ var wt = tpath?tpath.toString():obj.widgetType;
+
+ var ts = tmplts[wt];
+ if(!ts){
+ tmplts[wt] = {"string": null, "node": null};
+ if(avoidCache){
+ ts = {};
+ }else{
+ ts = tmplts[wt];
+ }
+ }
+
+ if((!obj.templateString)&&(!avoidCache)){
+ obj.templateString = templateString || ts["string"];
+ }
+ if(obj.templateString){
+ obj.templateString = this._sanitizeTemplateString(obj.templateString);
+ }
+
+ if((!obj.templateNode)&&(!avoidCache)){
+ obj.templateNode = ts["node"];
+ }
+ if((!obj.templateNode)&&(!obj.templateString)&&(tpath)){
+ // fetch a text fragment and assign it to templateString
+ // NOTE: we rely on blocking IO here!
+ var tstring = this._sanitizeTemplateString(dojo.hostenv.getText(tpath));
+
+ obj.templateString = tstring;
+ if(!avoidCache){
+ tmplts[wt]["string"] = tstring;
+ }
+ }
+ if((!ts["string"])&&(!avoidCache)){
+ ts.string = obj.templateString;
+ }
+}
+
+dojo.widget._sanitizeTemplateString = function(/*String*/tString){
+ //summary: Strips <?xml ...?> declarations so that external SVG and XML
+ //documents can be added to a document without worry. Also, if the string
+ //is an HTML document, only the part inside the body tag is returned.
+ if(tString){
+ tString = tString.replace(/^\s*<\?xml(\s)+version=[\'\"](\d)*.(\d)*[\'\"](\s)*\?>/im, "");
+ var matches = tString.match(/<body[^>]*>\s*([\s\S]+)\s*<\/body>/im);
+ if(matches){
+ tString = matches[1];
+ }
+ }else{
+ tString = "";
+ }
+ return tString; //String
+}
+
+dojo.widget._templateCache.dummyCount = 0;
+
+// Array: list of properties to search for node-to-property mappings
+dojo.widget.attachProperties = ["dojoAttachPoint", "id"];
+
+// String: name of the property to use for mapping DOM events to widget functions
+dojo.widget.eventAttachProperty = "dojoAttachEvent";
+
+// String: property name of code to evaluate when the widget is constructed
+dojo.widget.onBuildProperty = "dojoOnBuild";
+
+// Array: possible accessibility values to set on widget elements - role or state
+dojo.widget.waiNames = ["waiRole", "waiState"];
+
+dojo.widget.wai = {
+ // summary: Contains functions to set accessibility roles and states
+ // onto widget elements
+ waiRole: {
+ // name: String:
+ // information for mapping accessibility role
+ name: "waiRole",
+ // namespace: String:
+ // URI of the namespace for the set of roles
+ "namespace": "http://www.w3.org/TR/xhtml2",
+ // alias: String:
+ // The alias to assign the namespace
+ alias: "x2",
+ // prefix: String:
+ // The prefix to assign to the role value
+ prefix: "wairole:"
+ },
+ waiState: {
+ // name: String:
+ // information for mapping accessibility state
+ name: "waiState",
+ // namespace: String:
+ // URI of the namespace for the set of states
+ "namespace": "http://www.w3.org/2005/07/aaa",
+ // alias: String:
+ // The alias to assign the namespace
+ alias: "aaa",
+ // prefix: String:
+ // empty string - state value does not require prefix
+ prefix: ""
+ },
+ setAttr: function(/*DomNode*/node, /*String*/ ns, /*String*/ attr, /*String|Boolean*/value){
+ // summary: Use appropriate API to set the role or state attribute onto the element.
+ // description: In IE use the generic setAttribute() api. Append a namespace
+ // alias to the attribute name and appropriate prefix to the value.
+ // Otherwise, use the setAttribueNS api to set the namespaced attribute. Also
+ // add the appropriate prefix to the attribute value.
+ if(dojo.render.html.ie){
+ node.setAttribute(this[ns].alias+":"+ attr, this[ns].prefix+value);
+ }else{
+ node.setAttributeNS(this[ns]["namespace"], attr, this[ns].prefix+value);
+ }
+ },
+
+ getAttr: function(/*DomNode*/ node, /*String*/ ns, /*String|Boolena*/ attr){
+ // Summary: Use the appropriate API to retrieve the role or state value
+ // Description: In IE use the generic getAttribute() api. An alias value
+ // was added to the attribute name to simulate a namespace when the attribute
+ // was set. Otherwise use the getAttributeNS() api to retrieve the state value
+ if(dojo.render.html.ie){
+ return node.getAttribute(this[ns].alias+":"+attr);
+ }else{
+ return node.getAttributeNS(this[ns]["namespace"], attr);
+ }
+ },
+ removeAttr: function(/*DomNode*/ node, /*String*/ ns, /*String|Boolena*/ attr){
+ // summary: Use the appropriate API to remove the role or state value
+ // description: In IE use the generic removeAttribute() api. An alias value
+ // was added to the attribute name to simulate a namespace when the attribute
+ // was set. Otherwise use the removeAttributeNS() api to remove the state value
+ var success = true; //only IE returns a value
+ if(dojo.render.html.ie){
+ success = node.removeAttribute(this[ns].alias+":"+attr);
+ }else{
+ node.removeAttributeNS(this[ns]["namespace"], attr);
+ }
+ return success;
+ }
+};
+
+dojo.widget.attachTemplateNodes = function(rootNode, /*Widget*/ targetObj, events ){
+ // summary:
+ // map widget properties and functions to the handlers specified in
+ // the dom node and it's descendants. This function iterates over all
+ // nodes and looks for these properties:
+ // * dojoAttachPoint
+ // * dojoAttachEvent
+ // * waiRole
+ // * waiState
+ // * any "dojoOn*" proprties passed in the events array
+ // rootNode: DomNode
+ // the node to search for properties. All children will be searched.
+ // events: Array
+ // a list of properties generated from getDojoEventsFromStr.
+
+ // FIXME: this method is still taking WAAAY too long. We need ways of optimizing:
+ // a.) what we are looking for on each node
+ // b.) the nodes that are subject to interrogation (use xpath instead?)
+ // c.) how expensive event assignment is (less eval(), more connect())
+ // var start = new Date();
+ var elementNodeType = dojo.dom.ELEMENT_NODE;
+
+ function trim(str){
+ return str.replace(/^\s+|\s+$/g, "");
+ }
+
+ if(!rootNode){
+ rootNode = targetObj.domNode;
+ }
+
+ if(rootNode.nodeType != elementNodeType){
+ return;
+ }
+ // alert(events.length);
+
+ var nodes = rootNode.all || rootNode.getElementsByTagName("*");
+ var _this = targetObj;
+ for(var x=-1; x<nodes.length; x++){
+ var baseNode = (x == -1) ? rootNode : nodes[x];
+ // FIXME: is this going to have capitalization problems? Could use getAttribute(name, 0); to get attributes case-insensitve
+ var attachPoint = [];
+ if(!targetObj.widgetsInTemplate || !baseNode.getAttribute('dojoType')){
+ for(var y=0; y<this.attachProperties.length; y++){
+ var tmpAttachPoint = baseNode.getAttribute(this.attachProperties[y]);
+ if(tmpAttachPoint){
+ attachPoint = tmpAttachPoint.split(";");
+ for(var z=0; z<attachPoint.length; z++){
+ if(dojo.lang.isArray(targetObj[attachPoint[z]])){
+ targetObj[attachPoint[z]].push(baseNode);
+ }else{
+ targetObj[attachPoint[z]]=baseNode;
+ }
+ }
+ break;
+ }
+ }
+
+ var attachEvent = baseNode.getAttribute(this.eventAttachProperty);
+ if(attachEvent){
+ // NOTE: we want to support attributes that have the form
+ // "domEvent: nativeEvent; ..."
+ var evts = attachEvent.split(";");
+ for(var y=0; y<evts.length; y++){
+ if((!evts[y])||(!evts[y].length)){ continue; }
+ var thisFunc = null;
+ var tevt = trim(evts[y]);
+ if(evts[y].indexOf(":") >= 0){
+ // oh, if only JS had tuple assignment
+ var funcNameArr = tevt.split(":");
+ tevt = trim(funcNameArr[0]);
+ thisFunc = trim(funcNameArr[1]);
+ }
+ if(!thisFunc){
+ thisFunc = tevt;
+ }
+
+ var tf = function(){
+ var ntf = new String(thisFunc);
+ return function(evt){
+ if(_this[ntf]){
+ _this[ntf](dojo.event.browser.fixEvent(evt, this));
+ }
+ };
+ }();
+ dojo.event.browser.addListener(baseNode, tevt, tf, false, true);
+ // dojo.event.browser.addListener(baseNode, tevt, dojo.lang.hitch(_this, thisFunc));
+ }
+ }
+
+ for(var y=0; y<events.length; y++){
+ //alert(events[x]);
+ var evtVal = baseNode.getAttribute(events[y]);
+ if((evtVal)&&(evtVal.length)){
+ var thisFunc = null;
+ var domEvt = events[y].substr(4); // clober the "dojo" prefix
+ thisFunc = trim(evtVal);
+ var funcs = [thisFunc];
+ if(thisFunc.indexOf(";")>=0){
+ funcs = dojo.lang.map(thisFunc.split(";"), trim);
+ }
+ for(var z=0; z<funcs.length; z++){
+ if(!funcs[z].length){ continue; }
+ var tf = function(){
+ var ntf = new String(funcs[z]);
+ return function(evt){
+ if(_this[ntf]){
+ _this[ntf](dojo.event.browser.fixEvent(evt, this));
+ }
+ }
+ }();
+ dojo.event.browser.addListener(baseNode, domEvt, tf, false, true);
+ // dojo.event.browser.addListener(baseNode, domEvt, dojo.lang.hitch(_this, funcs[z]));
+ }
+ }
+ }
+ }
+ // continue;
+
+ // FIXME: we need to put this into some kind of lookup structure
+ // instead of direct assignment
+ var tmpltPoint = baseNode.getAttribute(this.templateProperty);
+ if(tmpltPoint){
+ targetObj[tmpltPoint]=baseNode;
+ }
+
+ dojo.lang.forEach(dojo.widget.waiNames, function(name){
+ var wai = dojo.widget.wai[name];
+ var val = baseNode.getAttribute(wai.name);
+ if(val){
+ if(val.indexOf('-') == -1){
+ dojo.widget.wai.setAttr(baseNode, wai.name, "role", val);
+ }else{
+ // this is a state-value pair
+ var statePair = val.split('-');
+ dojo.widget.wai.setAttr(baseNode, wai.name, statePair[0], statePair[1]);
+ }
+ }
+ }, this);
+
+ var onBuild = baseNode.getAttribute(this.onBuildProperty);
+ if(onBuild){
+ eval("var node = baseNode; var widget = targetObj; "+onBuild);
+ }
+ }
+
+}
+
+dojo.widget.getDojoEventsFromStr = function(str){
+ // summary:
+ // generates a list of properties with names that match the form
+ // dojoOn*
+ // str: String
+ // the template string to search
+
+ // var lstr = str.toLowerCase();
+ var re = /(dojoOn([a-z]+)(\s?))=/gi;
+ var evts = str ? str.match(re)||[] : [];
+ var ret = [];
+ var lem = {};
+ for(var x=0; x<evts.length; x++){
+ if(evts[x].length < 1){ continue; }
+ var cm = evts[x].replace(/\s/, "");
+ cm = (cm.slice(0, cm.length-1));
+ if(!lem[cm]){
+ lem[cm] = true;
+ ret.push(cm);
+ }
+ }
+ return ret; // Array
+}
+
+dojo.declare("dojo.widget.DomWidget",
+ dojo.widget.Widget,
+ function(){
+ // summary:
+ // dojo.widget.DomWidget is the superclass that provides behavior for all
+ // DOM-based renderers, including HtmlWidget and SvgWidget. DomWidget
+ // implements the templating system that most widget authors use to define
+ // the UI for their widgets.
+ if((arguments.length>0)&&(typeof arguments[0] == "object")){
+ this.create(arguments[0]);
+ }
+ },
+ {
+ // templateNode: DomNode
+ // a node that represents the widget template. Pre-empts both templateString and templatePath.
+ templateNode: null,
+
+ // templateString String:
+ // a string that represents the widget template. Pre-empts the
+ // templatePath. In builds that have their strings "interned", the
+ // templatePath is converted to an inline templateString, thereby
+ // preventing a synchronous network call.
+ templateString: null,
+
+ // templateCssString String:
+ // a string that represents the CSS for the widgettemplate.
+ // Pre-empts the templateCssPath. In builds that have their
+ // strings "interned", the templateCssPath is converted to an
+ // inline templateCssString, thereby preventing a synchronous
+ // network call.
+ templateCssString: null,
+
+ // preventClobber Boolean:
+ // should the widget not replace the node from which it was
+ // constructed? Widgets that apply behaviors to pre-existing parts
+ // of a page can be implemented easily by setting this to "true".
+ // In these cases, the domNode property will point to the node
+ // which the widget was created from.
+ preventClobber: false,
+
+ // 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,
+
+ // containerNode DomNode:
+ // holds child elements. "containerNode" is generally set via a
+ // dojoAttachPoint assignment and it designates where widgets that
+ // are defined as "children" of the parent will be placed
+ // visually.
+ containerNode: null,
+
+ // widgetsInTemplate Boolean:
+ // should we parse the template to find widgets that might be
+ // declared in markup inside it? false by default.
+ widgetsInTemplate: false,
+
+ addChild: function(/*Widget*/ widget, overrideContainerNode, pos, ref, insertIndex){
+ // summary:
+ // Process the given child widget, inserting it's dom node as
+ // a child of our dom node
+ // overrideContainerNode: DomNode?
+ // a non-default container node for the widget
+ // pos: String?
+ // can be one of "before", "after", "first", or "last". This
+ // has the same meaning as in dojo.dom.insertAtPosition()
+ // ref: DomNode?
+ // a node to place the widget relative to
+ // insertIndex: int?
+ // DOM index, same meaning as in dojo.dom.insertAtIndex()
+ // returns: the widget that was inserted
+
+ // FIXME: should we support addition at an index in the children arr and
+ // order the display accordingly? Right now we always append.
+ if(!this.isContainer){ // we aren't allowed to contain other widgets, it seems
+ dojo.debug("dojo.widget.DomWidget.addChild() attempted on non-container widget");
+ return null;
+ }else{
+ if(insertIndex == undefined){
+ insertIndex = this.children.length;
+ }
+ this.addWidgetAsDirectChild(widget, overrideContainerNode, pos, ref, insertIndex);
+ this.registerChild(widget, insertIndex);
+ }
+ return widget; // Widget
+ },
+
+ addWidgetAsDirectChild: function(/*Widget*/ widget, overrideContainerNode, pos, ref, insertIndex){
+ // summary:
+ // Process the given child widget, inserting it's dom node as
+ // a child of our dom node
+ // overrideContainerNode: DomNode
+ // a non-default container node for the widget
+ // pos: String?
+ // can be one of "before", "after", "first", or "last". This
+ // has the same meaning as in dojo.dom.insertAtPosition()
+ // ref: DomNode?
+ // a node to place the widget relative to
+ // insertIndex: int?
+ // DOM index, same meaning as in dojo.dom.insertAtIndex()
+ if((!this.containerNode)&&(!overrideContainerNode)){
+ this.containerNode = this.domNode;
+ }
+ var cn = (overrideContainerNode) ? overrideContainerNode : this.containerNode;
+ if(!pos){ pos = "after"; }
+ if(!ref){
+ if(!cn){ cn = dojo.body(); }
+ ref = cn.lastChild;
+ }
+ if(!insertIndex) { insertIndex = 0; }
+ widget.domNode.setAttribute("dojoinsertionindex", insertIndex);
+
+ // insert the child widget domNode directly underneath my domNode, in the
+ // specified position (by default, append to end)
+ if(!ref){
+ cn.appendChild(widget.domNode);
+ }else{
+ // FIXME: was this meant to be the (ugly hack) way to support insert @ index?
+ //dojo.dom[pos](widget.domNode, ref, insertIndex);
+
+ // CAL: this appears to be the intended way to insert a node at a given position...
+ if (pos == 'insertAtIndex'){
+ // dojo.debug("idx:", insertIndex, "isLast:", ref === cn.lastChild);
+ dojo.dom.insertAtIndex(widget.domNode, ref.parentNode, insertIndex);
+ }else{
+ // dojo.debug("pos:", pos, "isLast:", ref === cn.lastChild);
+ if((pos == "after")&&(ref === cn.lastChild)){
+ cn.appendChild(widget.domNode);
+ }else{
+ dojo.dom.insertAtPosition(widget.domNode, cn, pos);
+ }
+ }
+ }
+ },
+
+ registerChild: function(widget, insertionIndex){
+ // summary: record that given widget descends from me
+ // widget: Widget
+ // the widget that is now a child
+ // insertionIndex: int
+ // where in the children[] array to place it
+
+ // we need to insert the child at the right point in the parent's
+ // 'children' array, based on the insertionIndex
+
+ widget.dojoInsertionIndex = insertionIndex;
+
+ var idx = -1;
+ for(var i=0; i<this.children.length; i++){
+
+ //This appears to fix an out of order issue in the case of mixed
+ //markup and programmatically added children. Previously, if a child
+ //existed from markup, and another child was addChild()d without specifying
+ //any additional parameters, it would end up first in the list, when in fact
+ //it should be after. I can't see cases where this would break things, but
+ //I could see no other obvious solution. -dustin
+
+ if (this.children[i].dojoInsertionIndex <= insertionIndex){
+ idx = i;
+ }
+ }
+
+ this.children.splice(idx+1, 0, widget);
+
+ widget.parent = this;
+ widget.addedTo(this, idx+1);
+
+ // If this widget was created programatically, then it was erroneously added
+ // to dojo.widget.manager.topWidgets. Fix that here.
+ delete dojo.widget.manager.topWidgets[widget.widgetId];
+ },
+
+ removeChild: function(/*Widget*/ widget){
+ // summary: detach child domNode from parent domNode
+ dojo.dom.removeNode(widget.domNode);
+
+ // remove child widget from parent widget
+ return dojo.widget.DomWidget.superclass.removeChild.call(this, widget); // Widget
+ },
+
+ getFragNodeRef: function(frag){
+ // summary:
+ // returns the source node, if any, that the widget was
+ // declared from
+ // frag: Object
+ // an opaque data structure generated by the first-pass parser
+ if(!frag){return null;} // null
+ if(!frag[this.getNamespacedType()]){
+ dojo.raise("Error: no frag for widget type " + this.getNamespacedType()
+ + ", id " + this.widgetId
+ + " (maybe a widget has set it's type incorrectly)");
+ }
+ return frag[this.getNamespacedType()]["nodeRef"]; // DomNode
+ },
+
+ postInitialize: function(/*Object*/ args, /*Object*/ frag, /*Widget*/ parentComp){
+ // summary:
+ // Replace the source domNode with the generated dom
+ // structure, and register the widget with its parent.
+ // This is an implementation of the stub function defined in
+ // dojo.widget.Widget.
+
+ //dojo.profile.start(this.widgetType + " postInitialize");
+
+ var sourceNodeRef = this.getFragNodeRef(frag);
+ // Stick my generated dom into the output tree
+ //alert(this.widgetId + ": replacing " + sourceNodeRef + " with " + this.domNode.innerHTML);
+ if (parentComp && (parentComp.snarfChildDomOutput || !sourceNodeRef)){
+ // Add my generated dom as a direct child of my parent widget
+ // This is important for generated widgets, and also cases where I am generating an
+ // <li> node that can't be inserted back into the original DOM tree
+ parentComp.addWidgetAsDirectChild(this, "", "insertAtIndex", "", args["dojoinsertionindex"], sourceNodeRef);
+ } else if (sourceNodeRef){
+ // Do in-place replacement of the my source node with my generated dom
+ if(this.domNode && (this.domNode !== sourceNodeRef)){
+ this._sourceNodeRef = dojo.dom.replaceNode(sourceNodeRef, this.domNode);
+ }
+ }
+
+ // Register myself with my parent, or with the widget manager if
+ // I have no parent
+ // TODO: the code below erroneously adds all programatically generated widgets
+ // to topWidgets (since we don't know who the parent is until after creation finishes)
+ if ( parentComp ) {
+ parentComp.registerChild(this, args.dojoinsertionindex);
+ } else {
+ dojo.widget.manager.topWidgets[this.widgetId]=this;
+ }
+
+ if(this.widgetsInTemplate){
+ var parser = new dojo.xml.Parse();
+
+ var subContainerNode;
+ //TODO: use xpath here?
+ var subnodes = this.domNode.getElementsByTagName("*");
+ for(var i=0;i<subnodes.length;i++){
+ if(subnodes[i].getAttribute('dojoAttachPoint') == 'subContainerWidget'){
+ subContainerNode = subnodes[i];
+// break;
+ }
+ if(subnodes[i].getAttribute('dojoType')){
+ subnodes[i].setAttribute('isSubWidget', true);
+ }
+ }
+ if (this.isContainer && !this.containerNode){
+ //no containerNode is available, which means a widget is used as a container. find it here and move
+ //all dom nodes defined in the main html page as children of this.domNode into the actual container
+ //widget's node (at this point, the subwidgets defined in the template file is not parsed yet)
+ if(subContainerNode){
+ var src = this.getFragNodeRef(frag);
+ if (src){
+ dojo.dom.moveChildren(src, subContainerNode);
+ //do not need to follow children nodes in the main html page, as they
+ //will be dealt with in the subContainerWidget
+ frag['dojoDontFollow'] = true;
+ }
+ }else{
+ dojo.debug("No subContainerWidget node can be found in template file for widget "+this);
+ }
+ }
+
+ var templatefrag = parser.parseElement(this.domNode, null, true);
+ // createSubComponents not createComponents because frag has already been created
+ dojo.widget.getParser().createSubComponents(templatefrag, this);
+
+ //find all the sub widgets defined in the template file of this widget
+ var subwidgets = [];
+ var stack = [this];
+ var w;
+ while((w = stack.pop())){
+ for(var i = 0; i < w.children.length; i++){
+ var cwidget = w.children[i];
+ if(cwidget._processedSubWidgets || !cwidget.extraArgs['issubwidget']){ continue; }
+ subwidgets.push(cwidget);
+ if(cwidget.isContainer){
+ stack.push(cwidget);
+ }
+ }
+ }
+
+ //connect event to this widget/attach dom node
+ for(var i = 0; i < subwidgets.length; i++){
+ var widget = subwidgets[i];
+ if(widget._processedSubWidgets){
+ dojo.debug("This should not happen: widget._processedSubWidgets is already true!");
+ return;
+ }
+ widget._processedSubWidgets = true;
+ if(widget.extraArgs['dojoattachevent']){
+ var evts = widget.extraArgs['dojoattachevent'].split(";");
+ for(var j=0; j<evts.length; j++){
+ var thisFunc = null;
+ var tevt = dojo.string.trim(evts[j]);
+ if(tevt.indexOf(":") >= 0){
+ // oh, if only JS had tuple assignment
+ var funcNameArr = tevt.split(":");
+ tevt = dojo.string.trim(funcNameArr[0]);
+ thisFunc = dojo.string.trim(funcNameArr[1]);
+ }
+ if(!thisFunc){
+ thisFunc = tevt;
+ }
+ if(dojo.lang.isFunction(widget[tevt])){
+ dojo.event.kwConnect({
+ srcObj: widget,
+ srcFunc: tevt,
+ targetObj: this,
+ targetFunc: thisFunc
+ });
+ }else{
+ alert(tevt+" is not a function in widget "+widget);
+ }
+ }
+ }
+
+ if(widget.extraArgs['dojoattachpoint']){
+ //don't attach widget.domNode here, as we do not know which
+ //dom node we should connect to (in checkbox widget case,
+ //it is inputNode). So we make the widget itself available
+ this[widget.extraArgs['dojoattachpoint']] = widget;
+ }
+ }
+ }
+
+ //dojo.profile.end(this.widgetType + " postInitialize");
+
+ // Expand my children widgets
+ /* dojoDontFollow is important for a very special case
+ * basically if you have a widget that you instantiate from script
+ * and that widget is a container, and it contains a reference to a parent
+ * instance, the parser will start recursively parsing until the browser
+ * complains. So the solution is to set an initialization property of
+ * dojoDontFollow: true and then it won't recurse where it shouldn't
+ */
+ if(this.isContainer && !frag["dojoDontFollow"]){
+ //alert("recurse from " + this.widgetId);
+ // build any sub-components with us as the parent
+ dojo.widget.getParser().createSubComponents(frag, this);
+ }
+ },
+
+ // method over-ride
+ buildRendering: function(/*Object*/ args, /*Object*/ frag){
+ // summary:
+ // Construct the UI for this widget, generally from a
+ // template. This can be over-ridden for custom UI creation to
+ // to side-step the template system. This is an
+ // implementation of the stub function defined in
+ // dojo.widget.Widget.
+
+ // DOM widgets construct themselves from a template
+ var ts = dojo.widget._templateCache[this.widgetType];
+
+ // Handle style for this widget here, as even if templatePath
+ // is not set, style specified by templateCssString or templateCssPath
+ // should be applied. templateCssString has higher priority
+ // than templateCssPath
+ if(args["templatecsspath"]){
+ args["templateCssPath"] = args["templatecsspath"];
+ }
+ var cpath = args["templateCssPath"] || this.templateCssPath;
+ if(cpath && !dojo.widget._cssFiles[cpath.toString()]){
+ if((!this.templateCssString)&&(cpath)){
+ this.templateCssString = dojo.hostenv.getText(cpath);
+ this.templateCssPath = null;
+ }
+ dojo.widget._cssFiles[cpath.toString()] = true;
+ }
+
+ if((this["templateCssString"])&&(!dojo.widget._cssStrings[this.templateCssString])){
+ dojo.html.insertCssText(this.templateCssString, null, cpath);
+ dojo.widget._cssStrings[this.templateCssString] = true;
+ }
+ if(
+ (!this.preventClobber)&&(
+ (this.templatePath)||
+ (this.templateNode)||
+ (
+ (this["templateString"])&&(this.templateString.length)
+ )||
+ (
+ (typeof ts != "undefined")&&( (ts["string"])||(ts["node"]) )
+ )
+ )
+ ){
+ // if it looks like we can build the thing from a template, do it!
+ this.buildFromTemplate(args, frag);
+ }else{
+ // otherwise, assign the DOM node that was the source of the widget
+ // parsing to be the root node
+ this.domNode = this.getFragNodeRef(frag);
+ }
+ this.fillInTemplate(args, frag); // this is where individual widgets
+ // will handle population of data
+ // from properties, remote data
+ // sets, etc.
+ },
+
+ buildFromTemplate: function(/*Object*/ args, /*Object*/ frag){
+ // summary:
+ // Called by buildRendering, creates the actual UI in a DomWidget.
+
+ // var start = new Date();
+ // copy template properties if they're already set in the templates object
+ // dojo.debug("buildFromTemplate:", this);
+ var avoidCache = false;
+ if(args["templatepath"]){
+// avoidCache = true;
+ args["templatePath"] = args["templatepath"];
+ }
+ dojo.widget.fillFromTemplateCache( this,
+ args["templatePath"],
+ null,
+ avoidCache);
+ var ts = dojo.widget._templateCache[this.templatePath?this.templatePath.toString():this.widgetType];
+ if((ts)&&(!avoidCache)){
+ if(!this.templateString.length){
+ this.templateString = ts["string"];
+ }
+ if(!this.templateNode){
+ this.templateNode = ts["node"];
+ }
+ }
+ var matches = false;
+ var node = null;
+ // var tstr = new String(this.templateString);
+ var tstr = this.templateString;
+ // attempt to clone a template node, if there is one
+ if((!this.templateNode)&&(this.templateString)){
+ matches = this.templateString.match(/\$\{([^\}]+)\}/g);
+ if(matches) {
+ // if we do property replacement, don't create a templateNode
+ // to clone from.
+ var hash = this.strings || {};
+ // FIXME: should this hash of default replacements be cached in
+ // templateString?
+ for(var key in dojo.widget.defaultStrings) {
+ if(dojo.lang.isUndefined(hash[key])) {
+ hash[key] = dojo.widget.defaultStrings[key];
+ }
+ }
+ // FIXME: this is a lot of string munging. Can we make it faster?
+ for(var i = 0; i < matches.length; i++) {
+ var key = matches[i];
+ key = key.substring(2, key.length-1);
+ var kval = (key.substring(0, 5) == "this.") ? dojo.lang.getObjPathValue(key.substring(5), this) : hash[key];
+ var value;
+ if((kval)||(dojo.lang.isString(kval))){
+ value = new String((dojo.lang.isFunction(kval)) ? kval.call(this, key, this.templateString) : kval);
+ // Safer substitution, see heading "Attribute values" in
+ // http://www.w3.org/TR/REC-html40/appendix/notes.html#h-B.3.2
+ while (value.indexOf("\"") > -1) {
+ value=value.replace("\"",""");
+ }
+ tstr = tstr.replace(matches[i], value);
+ }
+ }
+ }else{
+ // otherwise, we are required to instantiate a copy of the template
+ // string if one is provided.
+
+ // FIXME: need to be able to distinguish here what should be done
+ // or provide a generic interface across all DOM implementations
+ // FIMXE: this breaks if the template has whitespace as its first
+ // characters
+ // node = this.createNodesFromText(this.templateString, true);
+ // this.templateNode = node[0].cloneNode(true); // we're optimistic here
+ this.templateNode = this.createNodesFromText(this.templateString, true)[0];
+ if(!avoidCache){
+ ts.node = this.templateNode;
+ }
+ }
+ }
+ if((!this.templateNode)&&(!matches)){
+ dojo.debug("DomWidget.buildFromTemplate: could not create template");
+ return false;
+ }else if(!matches){
+ node = this.templateNode.cloneNode(true);
+ if(!node){ return false; }
+ }else{
+ node = this.createNodesFromText(tstr, true)[0];
+ }
+
+ // recurse through the node, looking for, and attaching to, our
+ // attachment points which should be defined on the template node.
+
+ this.domNode = node;
+ // dojo.profile.start("attachTemplateNodes");
+ this.attachTemplateNodes();
+ // dojo.profile.end("attachTemplateNodes");
+
+ // relocate source contents to templated container node
+ // this.containerNode must be able to receive children, or exceptions will be thrown
+ if (this.isContainer && this.containerNode){
+ var src = this.getFragNodeRef(frag);
+ if (src){
+ dojo.dom.moveChildren(src, this.containerNode);
+ }
+ }
+ },
+
+ attachTemplateNodes: function(baseNode, targetObj){
+ // summary:
+ // hooks up event handlers and property/node linkages. Calls
+ // dojo.widget.attachTemplateNodes to do all the hard work.
+ // baseNode: DomNode
+ // defaults to "this.domNode"
+ // targetObj: Widget
+ // defaults to "this"
+ if(!baseNode){ baseNode = this.domNode; }
+ if(!targetObj){ targetObj = this; }
+ return dojo.widget.attachTemplateNodes(baseNode, targetObj,
+ dojo.widget.getDojoEventsFromStr(this.templateString));
+ },
+
+ fillInTemplate: function(){
+ // summary:
+ // stub function! sub-classes may use as a default UI
+ // initializer function. The UI rendering will be available by
+ // the time this is called from buildRendering. If
+ // buildRendering is over-ridden, this function may not be
+ // fired!
+
+ // dojo.unimplemented("dojo.widget.DomWidget.fillInTemplate");
+ },
+
+ // method over-ride
+ destroyRendering: function(){
+ // summary: UI destructor. Destroy the dom nodes associated w/this widget.
+ try{
+ dojo.dom.destroyNode(this.domNode);
+ delete this.domNode;
+ }catch(e){ /* squelch! */ }
+ if(this._sourceNodeRef){
[... 6157 lines stripped ...]