/* 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.provide("dojo.widget.tags"); dojo.require("dojo.lang.func"); dojo.require("dojo.lang.array"); dojo.require("dojo.lang.extras"); dojo.require("dojo.lang.declare"); dojo.require("dojo.widget.Manager"); dojo.require("dojo.event.*"); dojo.declare("dojo.widget.Widget", null, { initializer: function() { // these properties aren't primitives and need to be created on a per-item // basis. this.children = []; // this.selection = new dojo.widget.Selection(); // FIXME: need to replace this with context menu stuff this.extraArgs = {}; }, // FIXME: need to be able to disambiguate what our rendering context is // here! // // needs to be a string with the end classname. Every subclass MUST // over-ride. // // base widget properties parent: null, // obviously, top-level and modal widgets should set these appropriately isTopLevel: false, isModal: false, isEnabled: true, isHidden: false, isContainer: false, // can we contain other widgets? widgetId: "", widgetType: "Widget", // used for building generic widgets toString: function() { return '[Widget ' + this.widgetType + ', ' + (this.widgetId || 'NO ID') + ']'; }, repr: function(){ return this.toString(); }, enable: function(){ // should be over-ridden this.isEnabled = true; }, disable: function(){ // should be over-ridden this.isEnabled = false; }, hide: function(){ // should be over-ridden this.isHidden = true; }, show: function(){ // should be over-ridden this.isHidden = false; }, onResized: function(){ // Clients should override this function to do special processing, // then call this.notifyChildrenOfResize() to notify children of resize this.notifyChildrenOfResize(); }, notifyChildrenOfResize: function(){ for(var i=0; i mixInProperties"); this.mixInProperties(args, fragment, parentComp); // dojo.debug(this.widgetType, "-> postMixInProperties"); this.postMixInProperties(args, fragment, parentComp); // dojo.debug(this.widgetType, "-> dojo.widget.manager.add"); dojo.widget.manager.add(this); // dojo.debug(this.widgetType, "-> buildRendering"); this.buildRendering(args, fragment, parentComp); // dojo.debug(this.widgetType, "-> initialize"); this.initialize(args, fragment, parentComp); // dojo.debug(this.widgetType, "-> postInitialize"); this.postInitialize(args, fragment, parentComp); // dojo.debug(this.widgetType, "-> postCreate"); this.postCreate(args, fragment, parentComp); // dojo.debug(this.widgetType, "done!"); return this; }, // Destroy this widget and it's descendants destroy: function(finalize){ // FIXME: this is woefully incomplete this.destroyChildren(); this.uninitialize(); this.destroyRendering(finalize); dojo.widget.manager.removeById(this.widgetId); }, // Destroy the children of this widget, and their descendents destroyChildren: function(){ while(this.children.length > 0){ var tc = this.children[0]; this.removeChild(tc); tc.destroy(); } }, getChildrenOfType: function(type, recurse){ var ret = []; var isFunc = dojo.lang.isFunction(type); if(!isFunc){ type = type.toLowerCase(); } for(var x=0; xsi)){ 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(){ }, initialize: function(args, frag){ // dojo.unimplemented("dojo.widget.Widget.initialize"); return false; }, postInitialize: function(args, frag){ return false; }, postCreate: function(args, frag){ return false; }, uninitialize: function(){ // dojo.unimplemented("dojo.widget.Widget.uninitialize"); return false; }, buildRendering: function(){ // SUBCLASSES MUST IMPLEMENT dojo.unimplemented("dojo.widget.Widget.buildRendering, on "+this.toString()+", "); return false; }, destroyRendering: function(){ // SUBCLASSES MUST IMPLEMENT dojo.unimplemented("dojo.widget.Widget.destroyRendering"); return false; }, cleanUp: function(){ // SUBCLASSES MUST IMPLEMENT dojo.unimplemented("dojo.widget.Widget.cleanUp"); return false; }, addedTo: function(parent){ // this is just a signal that can be caught }, addChild: function(child){ // 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){ for(var x=0; x 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(type, frag, parser, parentComp, insertionIndex, localProps){ 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["dojo:"+stype]); // var tic = new Date(); var twidget = dojo.widget.manager.getImplementation(stype); if(!twidget){ throw new Error("cannot find \"" + stype + "\" widget"); }else if (!twidget.create){ throw new Error("\"" + stype + "\" widget object does not appear to implement *Widget"); } localProperties["dojoinsertionindex"] = insertionIndex; // FIXME: we loose no less than 5ms in construction! var ret = twidget.create(localProperties, frag, parentComp); // dojo.debug(new Date() - tic); return ret; } /* * Create a widget constructor function (aka widgetClass) */ dojo.widget.defineWidget = function(widgetClass /*string*/, renderer /*string*/, superclasses /*function||array*/, init /*function*/, props /*object*/){ // 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 namespace = widgetClass.split("."); var type = namespace.pop(); // type <= WidgetName, namespace <= foo.bar.baz<.renderer> var regx = "\\.(" + (renderer ? renderer + '|' : '') + dojo.widget.defineWidget.renderers + ")\\."; var r = widgetClass.search(new RegExp(regx)); namespace = (r < 0 ? namespace.join(".") : widgetClass.substr(0, r)); dojo.widget.manager.registerWidgetPackage(namespace); dojo.widget.tags.addParseTreeHandler("dojo:"+type.toLowerCase()); props=(props)||{}; props.widgetType = type; if((!init)&&(props["classConstructor"])){ init = props.classConstructor; delete props.classConstructor; } dojo.declare(widgetClass, superclasses, init, props); }