From 5af5594f603fffeb0e0429ee8e03dd1c36ec5bb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20St=C3=B6ckel?= Date: Fri, 19 Aug 2011 16:00:44 +0000 Subject: [PATCH] Major update of the et2_widget internal structure. The following changes were made: - All attributes of the widgets are now parsed from XML before the widget itself is created. These attributes plus all default values are then added to an associative array. The associative array is passed as second parameter to the init function of et2_widget, but is also available as this.options *after* the constructor of the et2_widget baseclass has been called. The et2_widget constructor also calls a function parseArrayMgrAttrs(_attrs) - in this function widget implementations can read the values from e.g. the content and validation_errors array and merge it into the given _attrs associative array. After the complete internal widgettree is completely loaded and created the "loadingFinished" function gets called and invokes all given setter functions. After that it "glues" the DOM tree together. This should also (I didn't measure it) be a bit faster than before, when the DOM-Tree was created on the fly. Please have a look at the changes of the et2_textbox widget to see how this affects writing widgets. Note: The "id" property is copied to the object scope on the top of the et2_widget constructor. - When widgets are cloned the "options" array gets passed along to the newly created widget. This means that changes made on the widgets during runtime are not automatically copied to the clone - as this didn't happen anyhow it is not a really disadvantage. On the other side there should be no difference between widgets directly inside the "overlay" xet tag and widgets which are inside instanciated templates. - The selbox widget doesn't work anymore - it relied on the loadAttributes function which isn't available anymore. et2_selbox should use the parseArrayMgrAttrs function to access - I've commented out some of the "validator"-code in etemplate2.js as it created some error messages when destroying the widget tree. --- etemplate/js/et2_DOMWidget.js | 51 ++++- etemplate/js/et2_baseWidget.js | 53 ++--- etemplate/js/et2_box.js | 13 +- etemplate/js/et2_checkbox.js | 3 - etemplate/js/et2_common.js | 33 +++- etemplate/js/et2_grid.js | 3 - etemplate/js/et2_hbox.js | 18 +- etemplate/js/et2_inheritance.js | 64 ++++-- etemplate/js/et2_inputWidget.js | 1 + etemplate/js/et2_tabs.js | 11 +- etemplate/js/et2_template.js | 102 +++++----- etemplate/js/et2_textbox.js | 50 +---- etemplate/js/et2_valueWidget.js | 6 +- etemplate/js/et2_widget.js | 269 +++++++++++++------------- etemplate/js/etemplate2.js | 13 +- etemplate/js/test/et2_test_tabbox.xet | 12 +- etemplate/js/test/test.css | 1 + etemplate/js/test/test_xml.html | 2 +- 18 files changed, 377 insertions(+), 328 deletions(-) diff --git a/etemplate/js/et2_DOMWidget.js b/etemplate/js/et2_DOMWidget.js index b35e3046ff..4429e10107 100644 --- a/etemplate/js/et2_DOMWidget.js +++ b/etemplate/js/et2_DOMWidget.js @@ -52,6 +52,18 @@ var et2_DOMWidget = et2_widget.extend(et2_IDOMNode, { "type": "boolean", "description": "Defines whether this widget is visible.", "default": false + }, + "width": { + "name": "Width", + "type": "dimension", + "default": et2_no_init, + "description": "Width of the element in pixels, percentage or 'auto'" + }, + "height": { + "name": "Height", + "type": "dimension", + "default": et2_no_init, + "description": "Height of the element in pixels, percentage or 'auto'" } }, @@ -59,7 +71,10 @@ var et2_DOMWidget = et2_widget.extend(et2_IDOMNode, { * When the DOMWidget is initialized, it grabs the DOM-Node of the parent * object (if available) and passes it to its own "createDOMNode" function */ - init: function(_parent, _type) { + init: function() { + // Call the inherited constructor + this._super.apply(this, arguments); + this.parentNode = null; this._attachSet = { @@ -67,10 +82,7 @@ var et2_DOMWidget = et2_widget.extend(et2_IDOMNode, { "parent": null }; - this.disabled = false; - - // Call the inherited constructor - this._super.apply(this, arguments); + this._disabled = false; }, /** @@ -87,14 +99,16 @@ var et2_DOMWidget = et2_widget.extend(et2_IDOMNode, { }, /** - * Automatically tries to attach this node to the parent widget. + * Attaches the container node of this widget to the DOM-Tree */ - onSetParent: function() { + doLoadingFinished: function() { // Check whether the parent implements the et2_IDOMNode interface. If // yes, grab the DOM node and create our own. if (this._parent && this._parent.implements(et2_IDOMNode)) { this.setParentDOMNode(this._parent.getDOMNode(this)); } + + return true; }, /** @@ -192,7 +206,7 @@ var et2_DOMWidget = et2_widget.extend(et2_IDOMNode, { }, set_disabled: function(_value) { - var node = this.getDOMNode(); + var node = this.getDOMNode(this); if (node) { this.disabled = _value; @@ -206,8 +220,27 @@ var et2_DOMWidget = et2_widget.extend(et2_IDOMNode, { $j(node).show(); } } - } + }, + set_width: function(_value) { + this.width = _value; + + var node = this.getDOMNode(this); + if (node) + { + $j(node).css("width", _value); + } + }, + + set_height: function(_value) { + this.height = _value; + + var node = this.getDOMNode(this); + if (node) + { + $j(node).css("height", _value); + } + } }); diff --git a/etemplate/js/et2_baseWidget.js b/etemplate/js/et2_baseWidget.js index 3fe711cd8b..c3e16dc895 100644 --- a/etemplate/js/et2_baseWidget.js +++ b/etemplate/js/et2_baseWidget.js @@ -128,16 +128,8 @@ var et2_baseWidget = et2_DOMWidget.extend(et2_IAligned, { } }, - // XXX I really don't like this - I think I'll move attaching the DOM-Nodes - // to the loadFinished function - this should also be faster... set_align: function(_value) { - if (_value != this.align) - { - this.align = _value; - - // Reattach this node to the DOM - this.onSetParent(); - } + this.align = _value; }, get_align: function(_value) { @@ -171,32 +163,49 @@ var et2_placeholder = et2_baseWidget.extend({ // values of this object this.attrNodes = {}; + this.visible = false; + // Create the placeholder div this.placeDiv = $j(document.createElement("span")) .addClass("et2_placeholder"); var headerNode = $j(document.createElement("span")) - .text(this.type) + .text(this._type) .addClass("et2_caption") .appendTo(this.placeDiv); - this.setDOMNode(this.placeDiv[0]); - }, + var attrsCntr = $j(document.createElement("span")) + .appendTo(this.placeDiv) + .hide(); - loadAttributes: function(_attrs) { - for (var i = 0; i < _attrs.length; i++) - { - var attr = _attrs[i]; - - if (typeof this.attrNodes[attr.name] == "undefined") + headerNode.click(this, function(e) { + e.data.visible = !e.data.visible; + if (e.data.visible) { - this.attrNodes[attr.name] = $j(document.createElement("span")) - .addClass("et2_attr"); - this.placeDiv.append(this.attrNodes[attr.name]); + attrsCntr.show(); } + else + { + attrsCntr.hide(); + } + }); - this.attrNodes[attr.name].text(attr.name + "=" + attr.value); + for (var key in this.options) + { + if (typeof this.options[key] != "undefined") + { + if (typeof this.attrNodes[key] == "undefined") + { + this.attrNodes[key] = $j(document.createElement("span")) + .addClass("et2_attr"); + attrsCntr.append(this.attrNodes[key]); + } + + this.attrNodes[key].text(key + "=" + this.options[key]); + } } + + this.setDOMNode(this.placeDiv[0]); } }); diff --git a/etemplate/js/et2_box.js b/etemplate/js/et2_box.js index 17ddcc68c1..4ccf97d0dc 100644 --- a/etemplate/js/et2_box.js +++ b/etemplate/js/et2_box.js @@ -22,21 +22,16 @@ */ var et2_box = et2_baseWidget.extend({ - init: function(_parent, _type) { + createNamespace: true, + + init: function() { this._super.apply(this, arguments); this.div = $j(document.createElement("div")) - .addClass("et2_" + _type) + .addClass("et2_" + this._type) .addClass("et2_box_widget"); this.setDOMNode(this.div[0]); - }, - - set_id: function(_value) { - this._super.apply(this, arguments); - - // Check whether a namespace exists for this element - this.checkCreateNamespace(); } }); diff --git a/etemplate/js/et2_checkbox.js b/etemplate/js/et2_checkbox.js index 50c719ad0f..d29f419582 100644 --- a/etemplate/js/et2_checkbox.js +++ b/etemplate/js/et2_checkbox.js @@ -109,9 +109,6 @@ var et2_checkbox_ro = et2_checkbox.extend({ } }, - init: function(_parent) { - }, - init: function() { this._super.apply(this, arguments); diff --git a/etemplate/js/et2_common.js b/etemplate/js/et2_common.js index 0f345ae24b..8a4428b4b1 100644 --- a/etemplate/js/et2_common.js +++ b/etemplate/js/et2_common.js @@ -71,7 +71,7 @@ function et2_debug(_level) /** * Array with all types supported by the et2_checkType function. */ -var et2_validTypes = ["boolean", "string", "float", "integer", "any", "js"]; +var et2_validTypes = ["boolean", "string", "float", "integer", "any", "js", "dimension"]; /** * Object whith default values for the above types. Do not specify array or @@ -84,7 +84,8 @@ var et2_typeDefaults = { "js": null, "float": 0.0, "integer": 0, - "any": null + "any": null, + "dimension": "auto" }; function et2_evalBool(_val) @@ -144,7 +145,7 @@ function et2_checkType(_val, _type, _attr) if (_type == "js") { // Handle the default case - if (_val === null) + if (_val === null || _val instanceof Function) { return null; } @@ -204,6 +205,32 @@ function et2_checkType(_val, _type, _attr) return _err(); } + // Parse the given dimension value + if (_type == "dimension") + { + // Case 1: The value is "auto" + if (_val == "auto") + { + return _val; + } + + // Case 2: The value is simply a number, attach "px" + if (!isNaN(_val)) + { + return parseFloat(_val) + "px"; + } + + // Case 3: The value is already a valid css pixel value or a percentage + if (typeof _val == "string" && + ((_val.indexOf("px") == _val.length - 2 && !isNaN(_val.split("px")[0])) || + (_val.indexOf("%") == _val.length - 1 && !isNaN(_val.split("%")[0])))) + { + return _val; + } + + return _err(); + } + // We should never come here throw("Invalid type identifier supplied."); } diff --git a/etemplate/js/et2_grid.js b/etemplate/js/et2_grid.js index 2e13265808..140f274364 100644 --- a/etemplate/js/et2_grid.js +++ b/etemplate/js/et2_grid.js @@ -402,9 +402,6 @@ var et2_grid = et2_DOMWidget.extend({ "widget": cell.widget }); - // Trigger the "onSetParent" event of the widget - cell.widget.onSetParent(); - // Set the span values of the cell var cs = (x == w - 1) ? w - x : Math.min(w - x, cell.colSpan); var rs = (y == h - 1) ? h - y : Math.min(h - y, cell.rowSpan); diff --git a/etemplate/js/et2_hbox.js b/etemplate/js/et2_hbox.js index 2c5f3932ad..e9419ea6db 100644 --- a/etemplate/js/et2_hbox.js +++ b/etemplate/js/et2_hbox.js @@ -22,7 +22,11 @@ */ var et2_hbox = et2_baseWidget.extend({ - init: function(_parent, _type) { + createNamespace: true, + + init: function(_parent) { + this._super.apply(this, arguments); + this.alignData = { "hasAlign": false, "hasLeft": false, @@ -34,10 +38,8 @@ var et2_hbox = et2_baseWidget.extend({ this.leftDiv = null; this.rightDiv = null; - this._super.apply(this, arguments); - this.div = $j(document.createElement("div")) - .addClass("et2_" + _type) + .addClass("et2_" + this._type) .addClass("et2_box_widget"); this.setDOMNode(this.div[0]); @@ -159,15 +161,7 @@ var et2_hbox = et2_baseWidget.extend({ // Normally simply return the hbox-div return this._super.apply(this, arguments); - }, - - set_id: function(_value) { - this._super.apply(this, arguments); - - // Check whether a namespace exists for this element - this.checkCreateNamespace(); } - }); et2_register_widget(et2_hbox, ["hbox"]); diff --git a/etemplate/js/et2_inheritance.js b/etemplate/js/et2_inheritance.js index e053cdcd4e..330c94ea0a 100644 --- a/etemplate/js/et2_inheritance.js +++ b/etemplate/js/et2_inheritance.js @@ -261,12 +261,6 @@ { this.init.apply(this, arguments); } - - // Initialize the attributes - if (typeof this._attrsInitialized == "undefined") - { - this.initAttributes(); - } } } @@ -353,23 +347,67 @@ } } + /** + * generateAttributeSet sanitizes the given associative array of attributes + * (by passing each entry to "et2_checkType" and checking for existance of + * the attribute) and adds the default values to the associative array. + * + * @param _attrs is the associative array containing the attributes. + */ + Class.prototype.generateAttributeSet = function(_attrs) { + + // Sanity check and validation + for (var key in _attrs) + { + if (typeof this.attributes[key] != "undefined") + { + if (!this.attributes[key].ignore) + { + _attrs[key] = et2_checkType(_attrs[key], this.attributes[key].type, + key); + } + } + else + { + // Key does not exist - delete it and issue a warning + delete(_attrs[key]); + et2_debug("warn", this, "Attribute '" + key + + "' does not exist!"); + } + } + + // Include default values or already set values for this attribute + for (var key in this.attributes) + { + if (typeof _attrs[key] == "undefined") + { + var _default = this.attributes[key]["default"]; + if (_default == et2_no_init) + { + _default = undefined; + } + + _attrs[key] = _default; + } + } + + return _attrs; + } + /** * The initAttributes function sets the attributes to their default * values. The attributes are not overwritten, which means, that the * default is only set, if either a setter exists or this[propName] does * not exist yet. */ - Class.prototype.initAttributes = function() { - for (var key in this.attributes) + Class.prototype.initAttributes = function(_attrs) { + for (var key in _attrs) { - if (!this.attributes[key].ignore && this.attributes[key]["default"] !== et2_no_init) + if (!this.attributes[key].ignore && !(_attrs[key] == undefined)) { - this.setAttribute(key, this.attributes[key]["default"], - false); + this.setAttribute(key, _attrs[key], false); } } - - this._attrsInitialized = true; } /** diff --git a/etemplate/js/et2_inputWidget.js b/etemplate/js/et2_inputWidget.js index 087c95bb51..e553846932 100644 --- a/etemplate/js/et2_inputWidget.js +++ b/etemplate/js/et2_inputWidget.js @@ -71,6 +71,7 @@ var et2_inputWidget = et2_valueWidget.extend(et2_IInput, { $j(this.getInputNode()).attr("novalidate","novalidate"); // Stop browser from getting involved $j(this.getInputNode()).validator(); }, + detatchFromDOM: function() { if(this.getInputNode()) { $j(this.getInputNode()).data("validator").destroy(); diff --git a/etemplate/js/et2_tabs.js b/etemplate/js/et2_tabs.js index 84782c5677..b6817a359f 100644 --- a/etemplate/js/et2_tabs.js +++ b/etemplate/js/et2_tabs.js @@ -134,18 +134,13 @@ var et2_tabbox = et2_DOMWidget.extend({ entry.contentDiv = $j(document.createElement("div")) .addClass("et2_tabcntr") - .hide() .appendTo(this.tabContainer); - - // Let the widget appear on its corresponding page - entry.widget.onSetParent(); } this.setActiveTab(0); }, setActiveTab: function(_idx) { - console.log(_idx); // Remove the "active" flag from all tabs-flags $j(".et2_tabflag", this.flagContainer).removeClass("active"); @@ -175,6 +170,12 @@ var et2_tabbox = et2_DOMWidget.extend({ return null; } + }, + + set_height: function(_value) { + this.height = _value; + + this.tabContainer.css("height", _value); } }); diff --git a/etemplate/js/et2_template.js b/etemplate/js/et2_template.js index 9ade66ce3b..1820b12d2a 100644 --- a/etemplate/js/et2_template.js +++ b/etemplate/js/et2_template.js @@ -44,26 +44,60 @@ var et2_template = et2_DOMWidget.extend({ } }, + createNamespace: true, + /** * Initializes this template widget as a simple container. */ init: function(_parent) { + this._super.apply(this, arguments); + this.proxiedTemplate = null; this.isProxied = false; this.div = document.createElement("div"); - this.id = ""; - this._super.apply(this, arguments); + if (this.id != "") + { + this.createProxy(); + } + }, + + createProxy: function() { + // Check whether a template with the given name already exists and + // is not a proxy. + var tmpl = this.getRoot().getWidgetById(this.id); + if (tmpl instanceof et2_template && tmpl.proxiedTemplate == null && + tmpl != this) + { + // Detatch the proxied template from the DOM to and set its + // isProxied property to true + tmpl.makeProxied(); + + // Do not copy the id when cloning as this leads to infinit + // recursion + tmpl.options.id = ""; + + // Create a clone of the template and add it as child of this + // template (done by passing "this" to the clone function) + this.proxiedTemplate = tmpl.clone(this); + + // Reset the "ignore" flag and manually copy the id + tmpl.options.id = this.id; + this.proxiedTemplate.id = tmpl.id; + this.proxiedTemplate.isProxied = true; + + // Disallow adding any new node to this template + this.supportedWidgetClasses = []; + } }, /** * If the parent node is changed, either the DOM-Node of the proxied template * or the DOM-Node of this template is connected to the parent DOM-Node. */ - onSetParent: function() { - // Check whether the parent implements the et2_IDOMNode interface. If - // yes, grab the DOM node and create our own. + doLoadingFinished: function() { + // Check whether the parent implements the et2_IDOMNode interface. if (this._parent && this._parent.implements(et2_IDOMNode)) { var parentNode = this._parent.getDOMNode(this); @@ -72,6 +106,8 @@ var et2_template = et2_DOMWidget.extend({ if (this.proxiedTemplate) { this.proxiedTemplate.setParentDOMNode(parentNode); + this.proxiedTemplate.loadingFinished(); + return false; } else if (!this.isProxied) { @@ -79,6 +115,8 @@ var et2_template = et2_DOMWidget.extend({ } } } + + return true; }, makeProxied: function() { @@ -92,59 +130,7 @@ var et2_template = et2_DOMWidget.extend({ this.isProxied = true; }, - set_id: function(_value) { - if (_value != this.id) - { - // Check whether a template with the given name already exists and - // is not a proxy. - var tmpl = this.getRoot().getWidgetById(_value); - if (tmpl instanceof et2_template && tmpl.proxiedTemplate == null && - tmpl != this) - { - // Check whether we still have a proxied template, if yes, - // destroy it - if (this.proxiedTemplate != null) - { - this.proxiedTemplate.destroy(); - this.proxiedTemplate = null; - } - - // This element does not have a node in the tree - this.detatchFromDOM(); - - // Detatch the proxied template from the DOM to and set its - // isProxied property to true - tmpl.makeProxied(); - - // Do not copy the id when cloning as this leads to infinit - // recursion - tmpl.attributes["id"].ignore = true; - - // Create a clone of the template and add it as child of this - // template (done by passing "this" to the clone function) - this.proxiedTemplate = tmpl.clone(this); - - // Reset the "ignore" flag and manually copy the id - tmpl.attributes["id"].ignore = false; - this.proxiedTemplate.id = tmpl.id; - - // Disallow adding any new node to this template - this.supportedWidgetClasses = []; - - // Call the parent change event function - this.onSetParent(); - } - else - { - this._super(_value); - - // Check whether a namespace exists for this element - this.checkCreateNamespace(); - } - } - }, - - getDOMNode: function(_fromProxy) { + getDOMNode: function() { return this.div; }, diff --git a/etemplate/js/et2_textbox.js b/etemplate/js/et2_textbox.js index b6074fa4bb..e9fd98bc9a 100644 --- a/etemplate/js/et2_textbox.js +++ b/etemplate/js/et2_textbox.js @@ -41,13 +41,13 @@ var et2_textbox = et2_inputWidget.extend({ "rows": { "name": "Rows", "type": "integer", - "default": et2_no_init, + "default": 1, "description": "Multiline field height - better to use CSS" }, "cols": { "name": "Size", "type": "integer", - "default": et2_no_init, + "default": 1, "description": "Multiline field width - better to use CSS" }, }, @@ -56,16 +56,16 @@ var et2_textbox = et2_inputWidget.extend({ this._super.apply(this, arguments); this.input = null; - this.id = ""; this.createInputWidget(); - }, createInputWidget: function() { - if (this.multiline) + if (this.options.multiline || this.options.rows > 1 || this.options.cols > 1) { - this.input = $j(document.createElement("textarea")); + this.input = $j(document.createElement("textarea")) + .attr("rows", this.options.rows) + .attr("cols", this.options.cols); } else { @@ -80,44 +80,6 @@ var et2_textbox = et2_inputWidget.extend({ this.setDOMNode(this.input[0]); }, - set_multiline: function(_value) { - if (_value != this.multiline) - { - this.multiline = _value; - - this.createInputWidget(); - - // Write all settings again - this.update(); - } - }, - - set_rows: function(_value) { - if (_value != this.rows) - { - this.rows = _value; - if(this.rows > 1) - { - this.set_multiline(true); - this.input.attr("rows", this.rows); - } else { - this.set_multiline(false); - } - } - }, - set_cols: function(_value) { - if (_value != this.cols) - { - this.cols = _value; - if(this.cols > 1) - { - this.set_multiline(true); - this.input.attr("cols", this.cols); - } else { - this.set_multiline(false); - } - } - }, /** * Set input widget size diff --git a/etemplate/js/et2_valueWidget.js b/etemplate/js/et2_valueWidget.js index 778a970017..0fdf35ed81 100644 --- a/etemplate/js/et2_valueWidget.js +++ b/etemplate/js/et2_valueWidget.js @@ -33,18 +33,18 @@ var et2_valueWidget = et2_baseWidget.extend({ } }, - loadingFinished: function() { + parseArrayMgrAttrs: function(_attrs) { this._super.call(this, arguments); if (this.id != "") { // Set the value for this element var contentMgr = this.getArrayMgr("content"); - if(contentMgr != null) { + if (contentMgr != null) { var val = contentMgr.getValueForID(this.id); if (val !== null) { - this.setAttribute("value", val) + _attrs["value"] = val; } } } diff --git a/etemplate/js/et2_widget.js b/etemplate/js/et2_widget.js index 3bd3507954..af7884fadf 100644 --- a/etemplate/js/et2_widget.js +++ b/etemplate/js/et2_widget.js @@ -89,6 +89,11 @@ var et2_widget = Class.extend({ // attribute defines. legacyOptions: [], + /** + * Set this variable to true if this widget can have namespaces + */ + createNamespace: false, + /** * The init function is the constructor of the widget. When deriving new * classes from the widget base class, always call this constructor unless @@ -97,37 +102,54 @@ var et2_widget = Class.extend({ * @param _parent is the parent object from the XML tree which contains this * object. The default constructor always adds the new instance to the * children list of the given parent object. _parent may be NULL. - * @param _type is the node name with which the widget has been created. This - * is usefull if a single widget class implements multiple XET-Node widgets. + * @param _attrs is an associative array of attributes. */ - init: function(_parent, _type, _readonly) { + init: function(_parent, _attrs) { - if (typeof _type == "undefined") + // Check whether all attributes are available + if (typeof _parent == "undefined") { - _type = "widget"; + _parent = null; } - this.id = ""; + if (typeof _attrs == "undefined") + { + _attrs = {}; + } + + // Initialize all important parameters this._mgrs = {}; this._inst = null; + this._children = []; + this._type = _attrs["type"]; + this.id = _attrs["id"]; - // Copy the parent parameter and add this widget to its parent children - // list. + // Add this widget to the given parent widget this._parent = _parent; - this.onSetParent(); - if (_parent != null) { this._parent.addChild(this); } - this._children = []; - this.type = _type; - this.readonly = _readonly; - // The supported widget classes array defines a whitelist for all widget // classes or interfaces child widgets have to support. this.supportedWidgetClasses = [et2_widget]; + + if (_attrs["id"]) + { + // Create a namespace for this object + if (this.createNamespace) + { + this.checkCreateNamespace(); + } + + // Add all attributes hidden in the content arrays to the attributes + // parameter + this.parseArrayMgrAttrs(_attrs); + } + + // Create a local copy of the options object + this.options = et2_cloneObject(_attrs); }, /** @@ -156,7 +178,6 @@ var et2_widget = Class.extend({ this._children = []; this._parent = null; this._mgrs = {}; - this.onSetParent(); }, /** @@ -164,7 +185,7 @@ var et2_widget = Class.extend({ * constructor of the copied object. If the parameters are omitted, _parent * is defaulted to null */ - clone: function(_parent, _type) { + clone: function(_parent) { // Default _parent to null if (typeof _parent == "undefined") @@ -173,7 +194,7 @@ var et2_widget = Class.extend({ } // Create the copy - var copy = new (this.constructor)(_parent, _type, this.readonly); + var copy = new (this.constructor)(_parent, this.options); // Assign this element to the copy copy.assign(this); @@ -185,22 +206,7 @@ var et2_widget = Class.extend({ // Create a clone of all child elements of the given object for (var i = 0; i < _obj._children.length; i++) { - _obj._children[i].clone(this, _obj._children[i].type); - } - - // Copy all properties - for (var key in _obj.attributes) - { - if (!_obj.attributes[key].ignore) - { - var value = _obj.getAttribute(key); - - // Check whether the attribute is undefined - if (typeof value != "undefined") - { - this.setAttribute(key, value); - } - } + _obj._children[i].clone(this); } // Copy a reference to the content array manager @@ -214,14 +220,6 @@ var et2_widget = Class.extend({ return this._parent; }, - /** - * The set parent event is called, whenever the parent of the widget is set. - * Child classes can overwrite this function. Whe onSetParent is called, - * the change of the parent has already taken place. - */ - onSetParent: function() { - }, - /** * Returns the list of children of this widget. */ @@ -269,7 +267,7 @@ var et2_widget = Class.extend({ } else { - throw("_node is not supported by this widget class!"); + throw(_node, " is not supported by this widget class!"); } }, @@ -284,7 +282,6 @@ var et2_widget = Class.extend({ { // This element is no longer parent of the child _node._parent = null; - _node.onSetParent(); this._children.splice(idx, 1); } @@ -375,46 +372,103 @@ var et2_widget = Class.extend({ return false; }, - createElementFromNode: function(_node, _nodeName) { + /** + * The parseXMLAttrs function takes an XML DOM attributes object + * and adds the given attributes to the _target associative array. This + * function also parses the legacyOptions. + * + * @param _attrsObj is the XML DOM attributes object + * @param _target is the object to which the attributes should be written. + */ + parseXMLAttrs: function(_attrsObj, _target, _proto) { - // Check whether the type attribute exists - if yes pass it instead of - // the nodeName - if (_node.getAttribute("type")) + // Check whether the attributes object is really existing, if not abort + if (typeof _attrsObj == "undefined") { - _nodeName = _node.getAttribute("type"); + return; } - if (typeof _nodeName == "undefined") + // Iterate over the given attributes and parse them + for (var i = 0; i < _attrsObj.length; i++) { - _nodeName = _node.nodeName.toLowerCase(); - } + var attrName = _attrsObj[i].name; + var attrValue = _attrsObj[i].value; - // Get the constructor for that widget + // Special handling for the legacy options + if (attrName == "options") + { + // Parse the legacy options + var splitted = et2_csvSplit(attrValue); + + for (var j = 0; j < splitted.length && j < _proto.legacyOptions.length; j++) + { + _target[_proto.legacyOptions[j]] = splitted[j]; + } + } + else + { + var mgr = this.getArrayMgr("content"); + if (mgr != null && typeof _proto.attributes[attrName] != "undefined") + { + var attr = _proto.attributes[attrName]; + + // If the attribute is marked as boolean, parse the + // expression as bool expression. + if (attr.type == "boolean") + { + attrValue = mgr.parseBoolExpression(attrValue); + } + else + { + attrValue = mgr.expandName(attrValue); + } + } + + // Set the attribute + _target[attrName] = attrValue; + } + } + }, + + parseArrayMgrAttrs: function() { + }, + + createElementFromNode: function(_node) { + + // Fetch all attributes for this element from the XML node + var attributes = {}; + + // Parse the "readonly" and "type" flag for this element here, as they + // determine which constructor is used + var _nodeName = attributes["type"] = _node.getAttribute("type") ? + _node.getAttribute("type") : _node.nodeName.toLowerCase(); + var readonly = attributes["readonly"] = + this.getArrayMgr("readonlys").isReadOnly( + _node.getAttribute("id"), _node.getAttribute("readonly"), + this.readonly); + + // Get the constructor - if the widget is readonly, use the special "_ro" + // constructor if it is available var constructor = typeof et2_registry[_nodeName] == "undefined" ? et2_placeholder : et2_registry[_nodeName]; - - // Check whether the widget is marked as readonly and whether a special - // readonly type (suffixed with "_ro") is registered - var mgr = this.getArrayMgr("readonlys"); - var readonly = false; - if(mgr != null) { - readonly = mgr.isReadOnly( - _node.getAttribute("id"), _node.getAttribute("readonly"), this.readonly); - } if (readonly && typeof et2_registry[_nodeName + "_ro"] != "undefined") { constructor = et2_registry[_nodeName + "_ro"]; } + // Parse the attributes from the given XML attributes object + this.parseXMLAttrs(_node.attributes, attributes, constructor.prototype); + + // Do an sanity check for the attributes + constructor.prototype.generateAttributeSet(attributes); + // Creates the new widget, passes this widget as an instance and // passes the widgetType. Then it goes on loading the XML for it. - var widget = new constructor(this, _nodeName, readonly); + var widget = new constructor(this, attributes); + // Load the widget itself from XML widget.loadFromXML(_node); - // Call the "loadFinished" function of the widget - widget.loadingFinished(); - return widget; }, @@ -422,12 +476,6 @@ var et2_widget = Class.extend({ * Loads the widget tree from an XML node */ loadFromXML: function(_node) { - // Try to load the attributes of the current node - if (_node.attributes) - { - this.loadAttributes(_node.attributes); - } - // Load the child nodes. for (var i = 0; i < _node.childNodes.length; i++) { @@ -453,51 +501,6 @@ var et2_widget = Class.extend({ } }, - /** - * Loads the widget attributes from the passed DOM attributes array. - */ - loadAttributes: function(_attrs) { - for (var i = 0; i < _attrs.length; i++) - { - // Special handling for the legacy options - if (_attrs[i].name == "options") - { - // Parse the legacy options - var splitted = et2_csvSplit(_attrs[i].value); - - for (var j = 0; j < splitted.length && j < this.legacyOptions.length; j++) - { - this.setAttribute(this.legacyOptions[j], splitted[j]); - } - } - else - { - var attrName = _attrs[i].name; - var attrValue = _attrs[i].value; - - var mgr = this.getArrayMgr("content"); - if (mgr != null && typeof this.attributes[attrName] != "undefined") - { - var attr = this.attributes[attrName]; - - // If the attribute is marked as boolean, parse the - // expression as bool expression. - if (attr.type == "boolean") - { - attrValue = mgr.parseBoolExpression(attrValue); - } - else - { - attrValue = mgr.expandName(attrValue); - } - } - - // Set the attribute - this.setAttribute(attrName, attrValue); - } - } - }, - /** * Called whenever textNodes are loaded from the XML tree */ @@ -505,34 +508,28 @@ var et2_widget = Class.extend({ }, /** - * Called when loading of the widget from XML node is finished. This - * function can be used to load the data from the data arrays (content, - * readonlys, sel_options etc.) + * Called when loading the widget (sub-tree) is finished. First when this + * function is called, the DOM-Tree is created. loadingFinished is + * recursively called for all child elements. Do not directly override this + * function but the doLoadingFinished function which is executed before + * descending deeper into the DOM-Tree */ loadingFinished: function() { - }, + // Call all availble setters + this.initAttributes(this.options); - /** - * Calls the setter of each property with its current value, calls the - * update function of all child nodes. - */ - update: function() { - - // Go through every property of this object and check whether a - // corresponding setter function exists. If yes, it is called. - for (var key in this.attributes) + if (this.doLoadingFinished()) { - if (!this.attributes[key].ignore) + // Descend recursively into the tree + for (var i = 0; i < this._children.length; i++) { - this.setAttribute(key, this.getAttribute(key)); + this._children[i].loadingFinished(); } } + }, - // Call the update function of all children. - for (var i = 0; i < this._children.length; i++) - { - this._children[i].update(); - } + doLoadingFinished: function() { + return true; }, /** diff --git a/etemplate/js/etemplate2.js b/etemplate/js/etemplate2.js index c30b5c7c77..b5203bddc6 100644 --- a/etemplate/js/etemplate2.js +++ b/etemplate/js/etemplate2.js @@ -20,7 +20,7 @@ et2_description; et2_textbox; et2_number; - et2_selectbox; +// et2_selectbox; et2_checkbox; et2_radiobox; et2_styles; @@ -64,7 +64,7 @@ etemplate2.prototype.clear = function() { if (this.widgetContainer != null) { - $j(':input',this.DOMContainer).validator().data("validator").destroy(); +// $j(':input',this.DOMContainer).validator().data("validator").destroy(); this.widgetContainer.destroy(); this.widgetContainer = null; } @@ -121,7 +121,10 @@ etemplate2.prototype.load = function(_url, _data) // Asynchronously load the XET file (code below is executed ahead of the // code in the callback function) et2_loadXMLFromURL(_url, function(_xmldoc) { + // Read the XML structure this.widgetContainer.loadFromXML(_xmldoc); + // Inform the widget tree that it has been successfully loaded. + this.widgetContainer.loadingFinished(); }, this); // Clear any existing instance @@ -152,13 +155,13 @@ etemplate2.prototype.load = function(_url, _data) etemplate2.prototype.submit = function() { // Validator - var valid = true; + /*var valid = true; var inputs = $j(':input',this.DOMContainer).each(function() { if(typeof $j(this).data("validator") == "undefined") return true; valid = valid && $j(this).data("validator").checkValidity(); return true; }); - if(!valid) return false; + if(!valid) return false;*/ // Get the form values var values = this.widgetContainer.getValues(); @@ -263,7 +266,7 @@ function etemplate2_handle_response(_type, _response) throw("Error while parsing et2_load response"); } else if (_type == "et2_validation_error") { // Display validation errors - $j(':input',this.DOMContainer).data("validator").invalidate(_response.data); +// $j(':input',this.DOMContainer).data("validator").invalidate(_response.data); } return false; diff --git a/etemplate/js/test/et2_test_tabbox.xet b/etemplate/js/test/et2_test_tabbox.xet index aa310f721b..cc6eb75b03 100644 --- a/etemplate/js/test/et2_test_tabbox.xet +++ b/etemplate/js/test/et2_test_tabbox.xet @@ -1,6 +1,6 @@ - + @@ -8,10 +8,18 @@ - diff --git a/etemplate/js/test/test.css b/etemplate/js/test/test.css index 7960d0359b..cfe6cf1e38 100644 --- a/etemplate/js/test/test.css +++ b/etemplate/js/test/test.css @@ -86,6 +86,7 @@ div.et2_hbox_right { margin: 0 0 5px 0; font-weight: bold; color: #2E2E2E; + cursor: pointer; text-shadow: rgba(255, 255, 255, 0.5) 0 1px 0; } diff --git a/etemplate/js/test/test_xml.html b/etemplate/js/test/test_xml.html index b7fedc28b7..b2b24ec75e 100644 --- a/etemplate/js/test/test_xml.html +++ b/etemplate/js/test/test_xml.html @@ -20,7 +20,7 @@ - +