From f57b4580a52bf44649d352e5c0a009fc5a1ac9e6 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Thu, 20 Aug 2015 14:57:18 +0000 Subject: [PATCH] reverting r53406, r53423: using JSON instead of XML for templates on client-side to improve IE performace, as it did not give any performance improvement --- etemplate/inc/class.etemplate_new.inc.php | 4 +- .../class.etemplate_widget_template.inc.php | 7 - etemplate/js/et2_core_widget.js | 207 +++++++++--------- etemplate/js/et2_core_xml.js | 27 +-- etemplate/js/et2_extension_customfields.js | 4 +- etemplate/js/et2_widget_box.js | 26 ++- etemplate/js/et2_widget_entry.js | 2 +- etemplate/js/et2_widget_grid.js | 38 ++-- etemplate/js/et2_widget_hbox.js | 6 +- etemplate/js/et2_widget_selectbox.js | 4 +- etemplate/js/et2_widget_split.js | 2 +- etemplate/js/et2_widget_tabs.js | 16 +- etemplate/js/et2_widget_template.js | 63 +++--- etemplate/js/etemplate2.js | 116 +++------- etemplate/template.php | 123 ----------- 15 files changed, 223 insertions(+), 422 deletions(-) delete mode 100644 etemplate/template.php diff --git a/etemplate/inc/class.etemplate_new.inc.php b/etemplate/inc/class.etemplate_new.inc.php index 54ca498bfe..9fa896933f 100644 --- a/etemplate/inc/class.etemplate_new.inc.php +++ b/etemplate/inc/class.etemplate_new.inc.php @@ -166,11 +166,9 @@ class etemplate_new extends etemplate_widget_template // Info required to load the etemplate client-side $dom_id = str_replace('.','-',$this->dom_id); - $filename = etemplate_widget_template::rel2path(etemplate_widget_template::relPath($name)); $load_array = array( 'name' => $this->name, - 'url' => egw_framework::link('/etemplate/template.php', array('name' => $this->name, 'download' => filemtime($filename))), - // etemplate_widget_template::rel2url($this->rel_path), + 'url' => etemplate_widget_template::rel2url($this->rel_path), 'data' => $data, 'DOMNodeID' => $dom_id, ); diff --git a/etemplate/inc/class.etemplate_widget_template.inc.php b/etemplate/inc/class.etemplate_widget_template.inc.php index a796f8850b..47965567b9 100644 --- a/etemplate/inc/class.etemplate_widget_template.inc.php +++ b/etemplate/inc/class.etemplate_widget_template.inc.php @@ -35,13 +35,6 @@ class etemplate_widget_template extends etemplate_widget */ protected static $cache = array(); - /** - * Tell egw framework it's ok to call this - */ - public $public_functions = array( - 'ajaxtoJSON' => true - ); - /** * Get instance of template specified by name, template(-set) and version * diff --git a/etemplate/js/et2_core_widget.js b/etemplate/js/et2_core_widget.js index 2f7d0ceb9d..f3b3a78636 100644 --- a/etemplate/js/et2_core_widget.js +++ b/etemplate/js/et2_core_widget.js @@ -471,15 +471,16 @@ var et2_widget = ClassWithAttributes.extend( }, /** - * The parseJSONAttrs function takes a JSON object + * 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 JSON object + * @param _attrsObj is the XML DOM attributes object * @param {object} _target is the object to which the attributes should be written. * @param {et2_widget} _proto prototype with attributes and legacyOptions attribute */ - parseJSONAttrs: function(_attrsObj, _target, _proto) { + parseXMLAttrs: function(_attrsObj, _target, _proto) { + // Check whether the attributes object is really existing, if not abort if (typeof _attrsObj == "undefined") { @@ -487,62 +488,71 @@ var et2_widget = ClassWithAttributes.extend( } // Iterate over the given attributes and parse them - for (var name in _attrsObj) - { - this._parseAttr(name, _attrsObj[name], _target, _proto); - } - }, - - /** - * Parse a single attribute - * - * @param {string} attrName Attribute name - * @param {object} attrValue Attribute value - * @param {object} _target is the object to which the attributes should be written. - * @param {et2_widget} _proto prototype with attributes and legacyOptions attribute - */ - _parseAttr: function(attrName, attrValue, _target, _proto) - { var mgr = this.getArrayMgr("content"); - // Special handling for the legacy options - if (attrName == "options" && _proto.legacyOptions.length > 0) + for (var i = 0; i < _attrsObj.length; i++) { - // Check for modifications on legacy options here. Normal modifications - // are handled in widget constructor, but it's too late for legacy options then - if(_target.id && this.getArrayMgr("modifications").getEntry(_target.id)) - { - var mod = this.getArrayMgr("modifications").getEntry(_target.id); - if(typeof mod.options != "undefined") attrValue = attrValue = mod.options; - } - // expand legacyOptions with content - if(attrValue.charAt(0) == '@' || attrValue.indexOf('$') != -1) - { - attrValue = mgr.expandName(attrValue); - } + var attrName = _attrsObj[i].name; + var attrValue = _attrsObj[i].value; - // Parse the legacy options (as a string, other types not allowed) - var splitted = et2_csvSplit(attrValue+""); - - for (var j = 0; j < splitted.length && j < _proto.legacyOptions.length; j++) + // Special handling for the legacy options + if (attrName == "options" && _proto.legacyOptions.length > 0) { - // Blank = not set - if(splitted[j].trim().length == 0) continue; - - // Check to make sure we don't overwrite a current option with a legacy option - if(typeof _target[_proto.legacyOptions[j]] === "undefined") + // Check for modifications on legacy options here. Normal modifications + // are handled in widget constructor, but it's too late for legacy options then + if(_target.id && this.getArrayMgr("modifications").getEntry(_target.id)) { - attrValue = splitted[j]; + var mod = this.getArrayMgr("modifications").getEntry(_target.id); + if(typeof mod.options != "undefined") attrValue = _attrsObj[i].value = mod.options; + } + // expand legacyOptions with content + if(attrValue.charAt(0) == '@' || attrValue.indexOf('$') != -1) + { + attrValue = mgr.expandName(attrValue); + } - /** - If more legacy options than expected, stuff them all in the last legacy option - Some legacy options take a comma separated list. - */ - if(j == _proto.legacyOptions.length - 1 && splitted.length > _proto.legacyOptions.length) + // Parse the legacy options (as a string, other types not allowed) + var splitted = et2_csvSplit(attrValue+""); + + for (var j = 0; j < splitted.length && j < _proto.legacyOptions.length; j++) + { + // Blank = not set + if(splitted[j].trim().length == 0) continue; + + // Check to make sure we don't overwrite a current option with a legacy option + if(typeof _target[_proto.legacyOptions[j]] === "undefined") { - attrValue = splitted.slice(j); - } + attrValue = splitted[j]; - var attr = _proto.attributes[_proto.legacyOptions[j]]; + /** + If more legacy options than expected, stuff them all in the last legacy option + Some legacy options take a comma separated list. + */ + if(j == _proto.legacyOptions.length - 1 && splitted.length > _proto.legacyOptions.length) + { + attrValue = splitted.slice(j); + } + + var attr = _proto.attributes[_proto.legacyOptions[j]]; + + // If the attribute is marked as boolean, parse the + // expression as bool expression. + if (attr.type == "boolean") + { + attrValue = mgr.parseBoolExpression(attrValue); + } + else if (typeof attrValue != "object") + { + attrValue = mgr.expandName(attrValue); + } + _target[_proto.legacyOptions[j]] = attrValue; + } + } + } + else + { + 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. @@ -550,34 +560,15 @@ var et2_widget = ClassWithAttributes.extend( { attrValue = mgr.parseBoolExpression(attrValue); } - else if (typeof attrValue != "object") + else { attrValue = mgr.expandName(attrValue); } - _target[_proto.legacyOptions[j]] = attrValue; } - } - } - else - { - 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; } - - // Set the attribute - _target[attrName] = attrValue; } }, @@ -631,51 +622,47 @@ var et2_widget = ClassWithAttributes.extend( }, /** - * Create a et2_widget from a JSON object. + * Create a et2_widget from an XML node. * * First the type and attributes are read from the node. Then the readonly & modifications * arrays are checked for changes specific to the loaded data. Then the appropriate * constructor is called. After the constructor returns, the widget has a chance to - * further initialize itself from the object when the widget's loadFromObject() method - * is called with the object. + * further initialize itself from the XML node when the widget's loadFromXML() method + * is called with the node. * - * @param _node Object to read + * @param _node XML node to read * * @return et2_widget */ - createElementFromObject: function(_node) { + createElementFromNode: function(_node) { var attributes = {}; - if(typeof _node.attributes === 'undefined') - { - _node.attributes = {}; - } // Parse the "readonly" and "type" flag for this element here, as they // determine which constructor is used - var _nodeName = attributes["type"] = _node.attributes.type ? - _node.attributes.type : _node.tag; + var _nodeName = attributes["type"] = _node.getAttribute("type") ? + _node.getAttribute("type") : _node.nodeName.toLowerCase(); var readonly = attributes["readonly"] = this.getArrayMgr("readonlys").isReadOnly( - _node.attributes.id, _node.attributes.readonly, + _node.getAttribute("id"), _node.getAttribute("readonly"), typeof this.readonly !== 'undefined' ? this.readonly : this.options.readonly ); // Check to see if modifications change type var modifications = this.getArrayMgr("modifications"); - if(modifications && _node.attributes.id) { - var entry = modifications.getEntry(_node.attributes.id); + if(modifications && _node.getAttribute("id")) { + var entry = modifications.getEntry(_node.getAttribute("id")); if(entry == null) { // Try again, but skip the fancy stuff // TODO: Figure out why the getEntry() call doesn't always work - var entry = modifications.data[_node.attributes.id]; + var entry = modifications.data[_node.getAttribute("id")]; if(entry) { - this.egw().debug("warn", "getEntry("+_node.attributes.id+") failed, but the data is there.", modifications, entry); + this.egw().debug("warn", "getEntry("+_node.getAttribute("id")+") failed, but the data is there.", modifications, entry); } else { // Try the root, in case a namespace got missed - var entry = modifications.getRoot().getEntry(_node.attributes.id); + var entry = modifications.getRoot().getEntry(_node.getAttribute("id")); } } if(entry && entry.type) @@ -702,7 +689,7 @@ var et2_widget = ClassWithAttributes.extend( } // Parse the attributes from the given XML attributes object - this.parseJSONAttrs(_node.attributes, attributes, constructor.prototype); + this.parseXMLAttrs(_node.attributes, attributes, constructor.prototype); // Do an sanity check for the attributes constructor.prototype.generateAttributeSet(attributes); @@ -712,31 +699,39 @@ var et2_widget = ClassWithAttributes.extend( var widget = new constructor(this, attributes); // Load the widget itself from XML - widget.loadFromJSON(_node); + widget.loadFromXML(_node); return widget; }, /** - * Loads the widget tree from JSON object + * Loads the widget tree from an XML node * - * @param {Object} _content + * @param _node xml node */ - loadFromJSON: function(object) - { - if(object.content) - { - this.loadContent(object.content); - } - if(!object.children) return; + loadFromXML: function(_node) { // Load the child nodes. - for (var i = 0; i < object.children.length; i++) + for (var i = 0; i < _node.childNodes.length; i++) { - var node = object.children[i]; - node.parentNode = object; - + var node = _node.childNodes[i]; + var widgetType = node.nodeName.toLowerCase(); + + if (widgetType == "#comment") + { + continue; + } + + if (widgetType == "#text") + { + if (node.data.replace(/^\s+|\s+$/g, '')) + { + this.loadContent(node.data); + } + continue; + } + // Create the new element - this.createElementFromObject(node); + this.createElementFromNode(node); } }, diff --git a/etemplate/js/et2_core_xml.js b/etemplate/js/et2_core_xml.js index 8616b9b484..ffbdd727ae 100644 --- a/etemplate/js/et2_core_xml.js +++ b/etemplate/js/et2_core_xml.js @@ -64,14 +64,11 @@ function et2_directChildrenByTagName(_node, _tagName) _tagName = _tagName.toLowerCase(); var result = []; - var children = _node.childNodes || _node.children || []; - for (var i = 0; i < children.length; i++) + for (var i = 0; i < _node.childNodes.length; i++) { - var child = children[i]; - child.parentNode = _node; - if (child.nodeName && _tagName === child.nodeName.toLowerCase() || child.tag && _tagName === child.tag) + if (_tagName == _node.childNodes[i].nodeName.toLowerCase()) { - result.push(child); + result.push(_node.childNodes[i]); } } @@ -80,12 +77,10 @@ function et2_directChildrenByTagName(_node, _tagName) function et2_filteredNodeIterator(_node, _callback, _context) { - if(!_node.children) return; - for (var i = 0; i < _node.children.length; i++) + for (var i = 0; i < _node.childNodes.length; i++) { - var node = _node.children[i]; - node.parentNode = _node; - var nodeName = node.tag; + var node = _node.childNodes[i]; + var nodeName = node.nodeName.toLowerCase(); if (nodeName.charAt(0) != "#") { _callback.call(_context, node, nodeName); @@ -95,15 +90,9 @@ function et2_filteredNodeIterator(_node, _callback, _context) function et2_readAttrWithDefault(_node, _name, _default) { - if( _node.getAttribute) - { var val = _node.getAttribute(_name); - } - else if (_node.attributes) - { - var val = _node.attributes[_name]; - } - return (val === null || typeof val === 'undefined') ? _default : val; + + return (val === null) ? _default : val; } diff --git a/etemplate/js/et2_extension_customfields.js b/etemplate/js/et2_extension_customfields.js index 49d960cd3a..dcbae50553 100644 --- a/etemplate/js/et2_extension_customfields.js +++ b/etemplate/js/et2_extension_customfields.js @@ -328,13 +328,13 @@ var et2_customfields_list = et2_valueWidget.extend([et2_IDetachedDOM, et2_IInput } }, - loadFromJSON: function(_node) { + loadFromXML: function(_node) { this.loadFields(); // Load the nodes as usual this._super.apply(this, arguments); }, - + set_value: function(_value) { if(!this.options.customfields) return; for(var field_name in this.options.customfields) diff --git a/etemplate/js/et2_widget_box.js b/etemplate/js/et2_widget_box.js index 6ee5fa278f..7aa45b1363 100644 --- a/etemplate/js/et2_widget_box.js +++ b/etemplate/js/et2_widget_box.js @@ -53,7 +53,7 @@ var et2_box = et2_baseWidget.extend([et2_IDetachedDOM], * * @param {object} _node */ - loadFromJSON: function(_node) { + loadFromXML: function(_node) { if(this._type != "box") { return this._super.apply(this, arguments); @@ -61,16 +61,30 @@ var et2_box = et2_baseWidget.extend([et2_IDetachedDOM], // Load the child nodes. var childIndex = 0; var repeatNode = null; - for (var i=0; i < _node.children.length; i++) + for (var i=0; i < _node.childNodes.length; i++) { - var node = _node.children[i]; - var widgetType = node.tag; + var node = _node.childNodes[i]; + var widgetType = node.nodeName.toLowerCase(); + + if (widgetType == "#comment") + { + continue; + } + + if (widgetType == "#text") + { + if (node.data.replace(/^\s+|\s+$/g, '')) + { + this.loadContent(node.data); + } + continue; + } // Create the new element, if no expansion needed var id = et2_readAttrWithDefault(node, "id", ""); if(id.indexOf('$') < 0 || widgetType != 'box') { - this.createElementFromObject(node); + this.createElementFromNode(node); childIndex++; } else @@ -95,7 +109,7 @@ var et2_box = et2_baseWidget.extend([et2_IDetachedDOM], } } - this.createElementFromObject(repeatNode); + this.createElementFromNode(repeatNode); } // Reset diff --git a/etemplate/js/et2_widget_entry.js b/etemplate/js/et2_widget_entry.js index 2aa2e088d8..26cfbdbecf 100644 --- a/etemplate/js/et2_widget_entry.js +++ b/etemplate/js/et2_widget_entry.js @@ -86,7 +86,7 @@ var et2_entry = et2_valueWidget.extend( this.setDOMNode(document.createElement('span')); }, - loadFromJSON: function(_node) { + loadFromXML: function(_node) { // Load the nodes as usual this._super.apply(this, arguments); diff --git a/etemplate/js/et2_widget_grid.js b/etemplate/js/et2_widget_grid.js index 553cb754ee..487ec4352e 100644 --- a/etemplate/js/et2_widget_grid.js +++ b/etemplate/js/et2_widget_grid.js @@ -352,9 +352,9 @@ var et2_grid = et2_DOMWidget.extend([et2_IDetachedDOM, et2_IAligned, et2_IResize var cell = this._getCell(cells, x, y); // Read the span value of the element - if (node.attributes.span) + if (node.getAttribute("span")) { - cell.rowSpan = node.attributes.span; + cell.rowSpan = node.getAttribute("span"); } else { @@ -423,9 +423,9 @@ var et2_grid = et2_DOMWidget.extend([et2_IDetachedDOM, et2_IAligned, et2_IResize var cell = this._getCell(cells, x, y); // Read the span value of the element - if (node.attributes && node.attributes.span) + if (node.getAttribute("span")) { - cell.colSpan = node.attributes.span; + cell.colSpan = node.getAttribute("span"); } else { @@ -441,20 +441,20 @@ var et2_grid = et2_DOMWidget.extend([et2_IDetachedDOM, et2_IAligned, et2_IResize var span = cell.colSpan = this._forceNumber(cell.colSpan); // Read the align value of the element - if (node.attributes && node.attributes.align) + if (node.getAttribute("align")) { - cell.align = node.attributes.align; + cell.align = node.getAttribute("align"); } // store id of nextmatch-*headers, so it is available for disabled widgets, which get not instanciated if (nodeName.substr(0, 10) == 'nextmatch-') { - cell.nm_id = node.attributes.id; + cell.nm_id = node.getAttribute('id'); } // Apply widget's class to td, for backward compatability - if(node.attributes && node.attributes.class) + if(node.getAttribute("class")) { - cell.class += (cell.class ? " " : "") + node.attributes.class; + cell.class += (cell.class ? " " : "") + node.getAttribute("class"); } // Create the element @@ -480,7 +480,7 @@ var et2_grid = et2_DOMWidget.extend([et2_IDetachedDOM, et2_IAligned, et2_IResize } - var widget = this.createElementFromObject(node, nodeName); + var widget = this.createElementFromNode(node, nodeName); } // Fill all cells the widget is spanning @@ -520,7 +520,7 @@ var et2_grid = et2_DOMWidget.extend([et2_IDetachedDOM, et2_IAligned, et2_IResize } // If row disabled, just skip it var disabled = false; - if(et2_readAttrWithDefault(node, "disabled", false) == "1") + if(node.getAttribute("disabled") == "1") { disabled = true; } @@ -595,23 +595,23 @@ var et2_grid = et2_DOMWidget.extend([et2_IDetachedDOM, et2_IAligned, et2_IResize /** * As the does not fit very well into the default widget structure, we're - * overwriting the loadFromJSON function and doing a two-pass reading - + * overwriting the loadFromXML function and doing a two-pass reading - * in the first step the * * @param {object} _node xml node to process */ - loadFromJSON: function(_node) { + loadFromXML: function(_node) { // Keep the node for later changing / reloading this.template_node = _node; // Get the columns and rows tag - var rowsElems = _node.children[1]; - var columnsElems = _node.children[0]; + var rowsElems = et2_directChildrenByTagName(_node, "rows"); + var columnsElems = et2_directChildrenByTagName(_node, "columns"); - if (rowsElems && columnsElems) + if (rowsElems.length == 1 && columnsElems.length == 1) { - var columns = columnsElems; - var rows = rowsElems; + var columns = columnsElems[0]; + var rows = rowsElems[0]; var colData = []; var rowData = []; @@ -870,7 +870,7 @@ var et2_grid = et2_DOMWidget.extend([et2_IDetachedDOM, et2_IAligned, et2_IResize } // Rebuild grid - this.loadFromJSON(this.template_node); + this.loadFromXML(this.template_node); // New widgets need to finish this.loadingFinished(); diff --git a/etemplate/js/et2_widget_hbox.js b/etemplate/js/et2_widget_hbox.js index a30c255d31..53f56bbe8f 100644 --- a/etemplate/js/et2_widget_hbox.js +++ b/etemplate/js/et2_widget_hbox.js @@ -96,15 +96,15 @@ var et2_hbox = et2_baseWidget.extend( }, /** - * The overwritten function checks whether any child element has + * The overwritten loadFromXML function checks whether any child element has * a special align value. * * @param {object} _node */ - loadFromJSON: function(_node) { + loadFromXML: function(_node) { // Check whether any child node has an alignment tag et2_filteredNodeIterator(_node, function(_node) { - var align = et2_readAttrWithDefault(_node, 'align',''); + var align = _node.getAttribute("align"); if (!align) { diff --git a/etemplate/js/et2_widget_selectbox.js b/etemplate/js/et2_widget_selectbox.js index 87ac181274..6cce32cfbc 100644 --- a/etemplate/js/et2_widget_selectbox.js +++ b/etemplate/js/et2_widget_selectbox.js @@ -398,9 +398,9 @@ var et2_selectbox = et2_inputWidget.extend( return true; }, - loadFromJSON: function(_node) { + loadFromXML: function(_node) { // Handle special case where legacy option for empty label is used (conflicts with rows), and rows is set as an attribute - var legacy = _node.attributes.options || false; + var legacy = _node.getAttribute("options"); if(legacy) { var legacy = legacy.split(","); diff --git a/etemplate/js/et2_widget_split.js b/etemplate/js/et2_widget_split.js index 15e780fe46..f0c09e8a61 100644 --- a/etemplate/js/et2_widget_split.js +++ b/etemplate/js/et2_widget_split.js @@ -114,7 +114,7 @@ var et2_split = et2_DOMWidget.extend([et2_IResizeable,et2_IPrint], * Tap in here to check if we have real children, because all children should be created * by this point. If there are, replace the placeholders. */ - loadFromJSON: function() { + loadFromXML: function() { this._super.apply(this, arguments); if(this._children.length > 0) { diff --git a/etemplate/js/et2_widget_tabs.js b/etemplate/js/et2_widget_tabs.js index f1b71ac416..e947d8d79f 100644 --- a/etemplate/js/et2_widget_tabs.js +++ b/etemplate/js/et2_widget_tabs.js @@ -132,7 +132,7 @@ var et2_tabbox = et2_valueWidget.extend([et2_IInput,et2_IResizeable], "contentDiv": null, "flagDiv": null, "hidden": hide, - "JSON": null, + "XMLNode": null, "promise": null }); } @@ -157,7 +157,7 @@ var et2_tabbox = et2_valueWidget.extend([et2_IInput,et2_IResizeable], if (i < tabData.length) { // Store node for later evaluation - tabData[i].JSON = node; + tabData[i].XMLNode = node; } else { @@ -167,7 +167,7 @@ var et2_tabbox = et2_valueWidget.extend([et2_IInput,et2_IResizeable], }, this); }, - loadFromJSON: function(_node) { + loadFromXML: function(_node) { // Get the tabs and tabpanels tags var tabsElems = et2_directChildrenByTagName(_node, "tabs"); var tabpanelsElems = et2_directChildrenByTagName(_node, "tabpanels"); @@ -219,7 +219,7 @@ var et2_tabbox = et2_valueWidget.extend([et2_IInput,et2_IResizeable], "contentDiv": null, "flagDiv": null, "hidden": typeof tab.hidden != "undefined" ? tab.hidden : readonly[tab_id] || false, - "JSON": null, + "XMLNode": null, "promise": null }); } @@ -274,12 +274,12 @@ var et2_tabbox = et2_valueWidget.extend([et2_IInput,et2_IResizeable], _loadTab: function(index,promises) { var tabData = this.tabData[index]; if(!tabData || tabData.loaded) return; - if(tabData.JSON != null) + if(tabData.XMLNode != null) { - tabData.widget = this.createElementFromObject(tabData.JSON,tabData.JSON.tag); + tabData.widget = this.createElementFromNode(tabData.XMLNode,tabData.XMLNode.nodeName.toLowerCase()); - // Release the JSON object - tabData.JSON = null; + // Release the XML node + tabData.XMLNode = null; } else if (tabData.widget_options) { diff --git a/etemplate/js/et2_widget_template.js b/etemplate/js/et2_widget_template.js index 65aa93b16f..a4f21a375c 100644 --- a/etemplate/js/et2_widget_template.js +++ b/etemplate/js/et2_widget_template.js @@ -86,9 +86,10 @@ var et2_template = et2_DOMWidget.extend( var cache_buster = parts.length > 1 ? parts.pop() : null; var template_name = parts.pop(); - // Check to see if the template is known - var template = etemplate2.prototype.get_template_cache(template_name); - if(!template) + // Check to see if XML is known + var xml = null; + var templates = etemplate2.prototype.templates; // use global eTemplate cache + if(!(xml = templates[template_name])) { // Check to see if ID is short form --> prepend parent/top-level name if(template_name.indexOf('.') < 0) @@ -97,52 +98,42 @@ var et2_template = et2_DOMWidget.extend( var top_name = root && root._inst ? root._inst.name : null; if (top_name && template_name.indexOf('.') < 0) template_name = top_name+'.'+template_name; } - template = etemplate2.prototype.get_template_cache(template_name); - if(!template) + xml = templates[template_name]; + if(!xml) { // Ask server var splitted = template_name.split('.'); // use template base url from initial template, to continue using webdav, if that was loaded via webdav - var path = this.getRoot()._inst.template_base_url + - splitted.join('.') + (cache_buster ? '&download='+cache_buster : + var path = this.getRoot()._inst.template_base_url + splitted.shift() + "/templates/default/" + + splitted.join('.')+ ".xet" + (cache_buster ? '?download='+cache_buster : // if server did not give a cache-buster, fall back to current time - '&download='+(new Date).valueOf()); + '?download='+(new Date).valueOf()); if(splitted.length) { - jQuery.ajax({ - url: path, - context: this, - type: 'GET', - dataType: 'json', - success: function(_data, _status, _xmlhttp){ - for(var i = 0; i < _data.children.length; i++) - { - var template = _data.children[i]; - if(template.tag !== "template") continue; - etemplate2.prototype.set_template_cache(template.attributes.id, template); - }// Read the structure of the requested template - if (template.id == template_name) this.loadFromJSON(template); - - // Update flag - this.loading.resolve(); - }, - error: function(_xmlhttp, _err) { - egw().debug('error', 'Loading eTemplate from '+_url+' failed! '+_xmlhttp.status+' '+_xmlhttp.statusText); + et2_loadXMLFromURL(path, function(_xmldoc) { + // Scan for templates and store them + for(var i = 0; i < _xmldoc.childNodes.length; i++) { + var template = _xmldoc.childNodes[i]; + if(template.nodeName.toLowerCase() != "template") continue; + templates[template.getAttribute("id")] = template; } - }); + + // Read the XML structure of the requested template + if (typeof templates[template_name] != 'undefined') this.loadFromXML(templates[template_name]); + + // Update flag + this.loading.resolve(); + + }, this); } return; } } - if(template !== null && typeof template !== "undefined") + if(xml !== null && typeof xml !== "undefined") { - this.egw().debug("log", "Loading template: ", template_name); - if(template.tag) - { - this.loadFromJSON(template); - } - + this.egw().debug("log", "Loading template from XML: ", template_name); + this.loadFromXML(xml); // Don't call this here - done by caller, or on whole widget tree //this.loadingFinished(); @@ -151,7 +142,7 @@ var et2_template = et2_DOMWidget.extend( } else { - this.egw().debug("warn", "Unable to find ", template_name); + this.egw().debug("warn", "Unable to find XML for ", template_name); this.loading.reject(); } } diff --git a/etemplate/js/etemplate2.js b/etemplate/js/etemplate2.js index d000cdd01d..d7832f4193 100644 --- a/etemplate/js/etemplate2.js +++ b/etemplate/js/etemplate2.js @@ -100,67 +100,22 @@ function etemplate2(_container, _menuaction) } -/** - * Return template from global cache, or undefined if not cached - * - * @param {string} _name - * @returns {object|undefined} - */ -etemplate2.prototype.get_template_cache = function(_name) +// List of templates (XML) that are known, not always used. Indexed by id. +// We share list of templates with iframes and popups +try { + if (opener && opener.etemplate2) + { + etemplate2.prototype.templates = opener.etemplate2.prototype.templates; + } +} +catch (e) { + // catch security exception if opener is from a different domain +} +if (typeof etemplate2.prototype.templates == "undefined") { - try { - if (opener && opener.etemplate2) - { - return opener.etemplate2.prototype.get_template_cache(_name); - } - } - catch (e) { - // catch security exception if opener is from a different domain - } - // use top window, if we are in an iframe - if (top !== window) - { - return top.etemplate2.prototype.get_template_cache(_name); - } - // we are the top window - if (typeof etemplate2.prototype.templates == "undefined") - { - etemplate2.prototype.templates = {}; - } - return etemplate2.prototype.templates[_name]; -}; + etemplate2.prototype.templates = top.etemplate2.prototype.templates || {}; +} -/** - * Store template object in global cache - * - * @param {string} _name - * @param {object} _template - */ -etemplate2.prototype.set_template_cache = function(_name, _template) -{ - try { - if (opener && opener.etemplate2) - { - return opener.etemplate2.prototype.set_template_cache(_name, _template); - } - } - catch (e) { - // catch security exception if opener is from a different domain - } - // use top window, if we are in an iframe - if (top !== window) - { - return top.etemplate2.prototype.set_template_cache(_name, _template); - } - // we are the top window - if (typeof etemplate2.prototype.templates == "undefined") - { - etemplate2.prototype.templates = {}; - } - // for IE we need to do a clone of template-object, as it might be from context of a different window - // and will become unavailable if that window closes - etemplate2.prototype.templates[_name] = jQuery.extend(true, {}, _template); -}; /** * Calls the resize event of all widgets @@ -247,8 +202,9 @@ etemplate2.prototype.clear = function() $j(this.DOMContainer).empty(); // Remove self from the index - for(name in etemplate2._byTemplate) + for(name in this.templates) { + if(typeof etemplate2._byTemplate[name] == "undefined") continue; for(var i = 0; i < etemplate2._byTemplate[name].length; i++) { if(etemplate2._byTemplate[name][i] == this) @@ -490,12 +446,8 @@ etemplate2.prototype.load = function(_name, _url, _data, _callback) } etemplate2._byTemplate[_name].push(this); - // Read the structure of the requested template - var template = this.get_template_cache(this.name); - if (template && template.children) - { - this.widgetContainer.loadFromJSON(template); - } + // Read the XML structure of the requested template + this.widgetContainer.loadFromXML(this.templates[this.name]); // List of Promises from widgets that are not quite fully loaded var deferred = []; @@ -589,28 +541,20 @@ etemplate2.prototype.load = function(_name, _url, _data, _callback) // Load & process - var template = this.get_template_cache(_name); - if(!template) + if(!this.templates[_name]) { - jQuery.ajax({ - url: _url, - context: this, - type: 'GET', - dataType: 'json', - success: function(_data, _status, _xmlhttp){ - for(var i = 0; i < _data.children.length; i++) - { - var template = _data.children[i]; - if(template.tag !== "template") continue; - this.set_template_cache(template.attributes.id, template); - if(!_name) this.name = template.attributes.id; - } - _load.apply(this,[]); - }, - error: function(_xmlhttp, _err) { - egw().debug('error', 'Loading eTemplate from '+_url+' failed! '+_xmlhttp.status+' '+_xmlhttp.statusText); + // Asynchronously load the XET file + et2_loadXMLFromURL(_url, function(_xmldoc) { + + // Scan for templates and store them + for(var i = 0; i < _xmldoc.childNodes.length; i++) { + var template = _xmldoc.childNodes[i]; + if(template.nodeName.toLowerCase() != "template") continue; + this.templates[template.getAttribute("id")] = template; + if(!_name) this.name = template.getAttribute("id"); } - }); + _load.apply(this,[]); + }, this); // Split the given data into array manager objects and pass those to the // widget container - do this here because file is loaded async diff --git a/etemplate/template.php b/etemplate/template.php deleted file mode 100644 index d280f088de..0000000000 --- a/etemplate/template.php +++ /dev/null @@ -1,123 +0,0 @@ - 'etemplate', - 'noheader' => true, - 'nonavbar' => true -); -include ('../header.inc.php'); - -if (!ajaxtoJSON($_GET['name'])) -{ - header('404 Not found'); - http_response_code(404); -} - -/** -* Gets the specified template XML file converted to JSON representation -* -* @param String $name -* @return JSON -*/ -function ajaxtoJSON($name) -{ - if(!$name) - { - $name = get_var('name'); - } - $filename = etemplate_widget_template::rel2path(etemplate_widget_template::relPath($name)); - // Bad template name - if(trim($filename) == '') - { - return false; - } - - $mtime = filemtime($filename); - - // First, check cache - $cached = egw_cache::getInstance('etemplate', $name); - - // Not found, or modified - if(!$cached || !is_array($cached) || is_array($cached) && $cached['mtime'] != $mtime) - { - // Load XML & parse into JSON - $reader = simplexml_load_file($filename); - $template = json_encode(nodeToArray($reader)); - $cached = array( - 'template' => $template, - 'mtime' => $mtime - ); - } - else if ($cached); - { - $template = $cached['template']; - } - if($cached) - { - // Keep in instance cache so we don't have to regenerate it - egw_cache::setInstance('etemplate', $name, $cached, CACHE_TIME); - } - else - { - return false; - } - - // Should set some headers so the browser can cache it too - header('Cache-Control: public, max-age='.CACHE_TIME); - header('Expires: ' . gmdate('D, d M Y H:i:s', time()+CACHE_TIME) . ' GMT'); - header('Content-type: application/json'); - header('Last-Modified: ' . gmdate('D, d M Y H:i:s', $mtime)); - header('Content-Length: ' . mb_strlen($template)); - - echo $template; - return true; -} - -function nodeToArray($xmlnode, &$jsnode = false) -{ - if(!($xmlnode instanceof SimpleXMLElement) && trim($xmlnode)) - { - $jsnode['content'] = $xmlnode; - return; - } - $nodename = $xmlnode->getName(); - $node =& $jsnode ? $jsnode : array(); - $node['tag'] = strtolower($nodename); - $node['attributes'] = array(); - - if (count($xmlnode->attributes()) > 0) - { - $node["attributes"] = array(); - foreach($xmlnode->attributes() as $key => $value) - { - $node["attributes"][$key] = (string)$value; - } - } - - if(trim($xmlnode->__toString()) != '') - { - $node['content'] = $xmlnode->__toString(); - } - - // Load children - $child_index = 0; - foreach ($xmlnode->children() as $childxmlnode) - { - $node['children'][$child_index] = array('tag' => $childxmlnode->getName()); - nodeToArray($childxmlnode, $node['children'][$child_index++]); - } - return $node; -}