-
Notifications
You must be signed in to change notification settings - Fork 0
Widget Creation
NOTE: SOME OF THE INFO ON THIS PAGE MIGHT BE OBSOLETE. MUCH OF THE INFORMATION WAS AUTHORED 03/2010
This page provides a quick overview of the logic in Maqetta to instantiate a live widget either via a parser (Dojo or OpenAjax) or via the "data" object.
Step | Drag/drop widget to canvas | ModifyCommand: clone via "data" | |
---|---|---|---|
Dojo widget | OpenAjax widget | ||
Widget creations starts with... | CreateTool._create() (from create() and onMouseUp()) | ModifyCommand.execute() | |
CreateTool._create() calls davinci.ve. widget.createWidget() | |||
Get Dojo class | createWidget() calls either davinci.ve. widget.loadHtmlWidget() or davinci.ve. widget._dojo().getObject(type) to return widget class | CreateTool._create() calls AddCommand.execute(), which calls davinci.ve. widget.createWidget(), which calls davinci.ve. widget.loadOpenAjaxWidget() to return OpenAjaxWidgetHelper class | ModifyCommand.execute() calls davinci.ve. widget.createWidget() (see previous two columns) |
createElement() | createWidget() calls dojo.doc.createElement() | ||
Add attributes | createWidget() adds dojoType attribute, loops through metadata.attributes() | createWidget() adds oawidget attribute. OAWidget logic for other attributes not yet written | Also calls createWidget() |
Add children |
createWidget() has this logic: if string, node.innerHTML = children else dojo.forEach(children,...) for comments or text, node.appendChild() for elements: createWidget(c) then node.appendChild() |
OAWidgets doesn't support container widgets yet | Also calls createWidget() |
Widget constructor | createWidget() calls new c(data.properties, node) | ||
Popup back to CreateTool._create() | |||
Add an "AddCommand" to command stack | _create()) Add an AddCommand to command stack | Does nothing. (Apparently, size information restored via info on "data" object) | |
Establish widget position and size | Depending on the situation, _create() sometimes adds MoveCommand and ResizeCommand to the command stack. | Does nothing. (Apparently, size information restored via info on "data" object) | |
Execute command stack | _create() calls execute() on the command stack, which causes AddCommand.execute() to run (and maybe MoveCommand and ResizeCommand) | Need to research | |
Add to DOM | AddCommand.execute() calls davinci.ve. widget.addChild(parent, widget, this._index); | Invokes this._context. rootNode.replaceChild(domNode, tempDiv); | |
attach (recursive) Sets some Maqetta properties such as widget.type, widget.metadata | AddCommand.execute() calls context.attach(widget) | OAWidget loader invoked here | ModifyCommand calls this._context. attach(newWidget); |
startup() Mostly for container widgets. For primitive widgets, this just sets a boolean inside of dijit | AddCommand.execute() calls widget.startup() | ModifyCommand invokes widget.startup() | |
render() | AddCommand.execute() invokes davinci.ve. widget.renderWidget(newWidget); which invokes widget.render() | ModifyCommand invokes davinci.ve. widget.renderWidget(newWidget); which invokes widget.render() | |
Popup back to CreateTool._create() | |||
select() | _create() calls this._context.select(widget) | Need to research | |
determine position and size | Context.select(widget) calls box = this.getDojo().position(node, true); | Need to research | |
Draw selection chrome (gray borders and knobs) | Context.select(widget) calls this.focus({box:...}) | Need to research |
Step | Open/parse an HTML file | |
---|---|---|
dojoType="..." | oawidget="..." | |
Parse document HEAD into "data" | Most of HEAD parsing logic is in davinci.ve. source.serialize() | |
innerHTML assignment of widget markup | One innerHTML assignment for entire doc in davinci.edit. Context._setSourceData() | |
Widget prep and "preserve" | dojo.query("*", bodynode) loops through all element in BODY. For each element with 'dojoType', call loadRequires(), _preserveScripts(), _preserveStates(), _preserveTagNames(). | Need to research |
Invoke widget parser | Invoke dojo.parser.parse() on root node for BODY. Converts attributes into widget properties, invokes widget constructor, invokes startup(). | Need to research. In attach()? |
Widget "restore" | _restoreScripts(), _restoreStates(). (tagNames used next time doc is saved) | Need to research |
attach (recursive) | davinci.edit. Context.attach(widget): set up widget.type, widget.metadata, _widgetIds | Additional calls in davinci.edit. Context.attach(widget) invoke OAWidget loader |
startup() Mostly for container widgets. For primitive widgets, this just sets a boolean inside of dijit | Dojo parser does this automatically | Need to research |
render() | not invoked |
The original davinci code upon which Maqetta is based used the "data" object as the centerpiece for both its serialization (i.e., save as an HTML file) and deserialization (i.e., open an HTML file) logic. It is also used by ModifyCommand when that routine needs to create a new, fresh copy of the current widget.
Here is what "data" looks like. It shows all fields used in all cases - sorry, at this time I haven't done complete research about which fields are used in which scenario. Generally, HEAD data is mostly when parsing the HTML file and WIDGET data is used when saving to HTML or during widget cloning operations:
data: { // HEAD metas:[], scripts:undefined, // for serialization, from widget._edit_scripts modules:[], // list of dojo.require()'s stylesheets:[], theme:<string>, // for deserialization, extracted from 'class' attribute on BODY bodyClasses:<string>, // for deserialization, from 'class' attribute on BODY // Note: states info parsed and stored via davinci.ve.states.store(data, states); content:<string>, // for deserialization, the HTML source between <body> and </body> template:<string>, // for deserialization, everything but the 'content' // WIDGET children:<undefined or array of "data" objects, once for each child widget>, properties:{ class:<string>, // from davinci.ve.widget.getClassNames(widget, options) // only set if there are any user classes on the widget id:undefined, // from davinci.ve.widget.getId(widget) style:"position:absolute,left:127px;top:96px;", // from davinci.ve.widget.getStyle(widget, options) // only set if style attribute has any values type:"dijit.form.TextBox" }, states:undefined, // from widget.states tagName:undefined, // from context.getTagName(widget.id) type:"dijit.form.TextBox" // from davinci.ve.widget.getType(widget) }
When it is time to save as HTML, davinci does the following:
- Invoke davinci.edit.Context.getSource(), which ...
- For each widget that is a child of the BODY element
- Invoke davinci.ve.source.serializeWidget(w), which ...
- Invokes davinci.ve.widget.getData(w) on the widget, which ...
- Converts all properties and attributes for that widget into properties on a "data" object for that widget and...
- To get children widget data, if widget.getChildren().length, then call davinci.ve.widget.getChildrenData(widget, options), and for each child widget...
- Calls davinci.ve.widget.getData(w) and attaches those recursive "data" objects onto the "children" property of the parent "data" object
When opening an HTML file, the parser (davinci.ve.source.parse()) parses the HEAD elements of the HTML source file into the HEAD parts of the "data" object above. However, for the BODY content, there is an innerHTML string assignment from the contents of the file, followed by logic that does dojo.query("*",...) for each element that has a dojoType attribute to loadRequires(), _preserveScripts(), _preserveStates() and _preserveTagName. Then there is a call to dj.parser.parse(...) to instantiate all of the Dojo widgets. Finally, there is another dojo.query("> *",...) that invokes the attach() method for each of the widgets.
The ModifyCommand command right now always creates a new replacement widget. It also uses the "data" object, but uses pretty much everything that the HTML source logic doesn't use. ModifyCommand does **not** do anything with the HEAD, but it does create all of the WIDGET parts of the "data" object and then processes all of the WIDGET parts to create the new widget as follows:
var newWidget = davinci.ve.widget.createWidget(this._newData);
containerNode.innerHTML = content; dojo.forEach(dojo.query("*", containerNode), function(n){ this.loadRequires(n.getAttribute("dojoType")); this.resolveUrl(n); if(!this.useFrame){ this._registerEscapee(n, escapees); } this._preserveScripts(n, scripts); this._preserveStates(n, states); this._preserveTagName(n); }, this); var dj = this.getDojo(); dj["require"]("dojo.parser"); dj.parser.parse(containerNode); ... this._restoreScripts(scripts); this._restoreStates(states); if(active){ dojo.query("> *", this.rootNode).map(davinci.ve.widget.getWidget).forEach(this.attach, this); }
var newWidget = davinci.ve.widget.createWidget(this._newData); ... domNode = newWidget.domNode; ... this._context.attach(newWidget); newWidget.startup(); davinci.ve.widget.renderWidget(newWidget);
- c = (the dojo class for the widget)
- var node = dojo.doc.createElement(tagName)
- Add node attributes (e.g., dojoType, oawidget, metadata.attributes)
- Add children
- if string, node.innerHTML = children
- else dojo.forEach(children,...)
- for comments or text, node.appendChild()
- for elements
var child = davinci.ve.widget.createWidget(c); if(child){ node.appendChild(child.domNode); } ... var widget = markupFactory ? markupFactory(data.properties, node, c) : new c(data.properties, node);
- Determine tagName
- Determine widget.type (e.g., dijit.form.Button or html.div or OpenAjax.whatever)
- Determine widget.metadata (by call to davinci.ve.metadata.getMetadata())
- If OAWidget, call OpenAjaxWidgetHelper.attach(), which invokes the OAWidget loader
- Get ID and call davinci.edit._add(this._widgetIds, id)
- Recursive for child widgets:
dojo.forEach(widget.getChildren(), this.attach, this);
(see ModifyCommand above)
From dijit/_Widget.js:
startup: function(){ // summary: // Processing after the DOM fragment is added to the document // description: // Called after a widget and its children have been created and added to the page, // and all related widgets have finished their create() cycle, up through postCreate(). // This is useful for composite widgets that need to control or layout sub-widgets. // Many layout widgets can use this as a wiring phase. this._started = true; },startup() is defined in _Widget, and is simply a "part of the lifecycle". It is the last step in the widget lifecycle, and is not required by all widgets. The most common case where it is absolutely required is when programatically creating layout widgets.
Most widgets do NOT require startup called, but in the cases where something inheriting from _Widget does not override the startup member, the call is essentially a no-op setting this._started = true; If you create your own startup() function, you should either call this.inherited(arguments) or simply set the _started trigger manually.
In Dojo 1.4, the lifecycle here has been adjusted slightly. Previously, a widget with widgetsInTemplate:true would call startup() on the child widgets BEFORE the startup() on the parent. In 1.4 the children's startup() will be called AFTER the parent startup(). This behavior is recursive for however many levels of nested widgets with widgetsInTemplate:true are instantiated.
Startup is automatically called by the parser in the case of parseOnLoad:true or manual execution. The parser delays calling startup() until all found child widgets have been appropriately instantiated.
(see ModifyCommand above)
if(widget.render){ widget.render();Looks like render() is seldom used. Charting widgets seem to use it.
- calls attach() recursively for each widget
- sets up grid
- Add OpenAjax script files to HEAD
- Various calls to dojo.connect()
- this._commandStack,onExecute,this.onContentChange
- containerNode,onmousedown,this.onMouseDown (move,up,out, keydown)
- setActiveTool
- bind to menu (not sure what that is)