📄 widget.js
字号:
/* 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(){ //dojo.debug("NEW "+this.widgetType); // 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 // Widget: the parent of this widget parent: null, // NOTE: "children" and "extraArgs" re-defined in the constructor as they need to be local to the widget // 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. children: [], // 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 extraArgs: {}, // obviously, top-level and modal widgets should set these appropriately // Boolean: should this widget eat all events that bubble up to it? isTopLevel: false, // Boolean: should this widget block other widgets? isModal: false, // Boolean: should this widget respond to user input? isEnabled: true, // Boolean: have we hidden the widget via hide()? isHidden: false, // Boolean: can this widget contain other widgets? isContainer: false, // 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: "", // String: used for building generic widgets widgetType: "Widget", // 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.isEnabled = true; }, disable: function(){ // summary: // disables the widget, usually involves masking inputs and // unsetting event handlers. Not implemented here. this.isEnabled = false; }, hide: function(){ // summary: hides the widget from view. Not implemented here. this.isHidden = true; }, show: function(){ // summary: re-adds the widget to the view. Not implemented here. this.isHidden = false; }, 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(/*Object*/ args, /*Object*/fragment, /*Widget, optional*/parent, /*String, optional*/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: // a normalized view of the parameters that the widget should take // fragment: // if the widget is being instantiated from markup, this object // parent: // the widget, if any, that this widget will be the child of. If // none is passed, the global default widget is used. // ns: 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(/*Boolean*/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: // is this function being called part of global environment // tear-down? // FIXME: this is woefully incomplete 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, /*Boolean*/recurse){ // summary: // return an array of descendant widgets who match the passed type // recurse: // 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(){ // summary: 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(/*Object*/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: // 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 = {};
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -