| /* |
| 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("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.require("dojo.a11y"); |
| |
| 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) |
| |
| // test for accessibility mode |
| dojo.a11y.setAccessibleMode(); |
| //dojo.profile.start("buildWidgetFromParseTree"); |
| // FIXME: for codepath from createComponentFromScript, we are now splitting a path |
| // that we already split and then joined |
| 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); |
| } |