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

This commit is contained in:
Ralf Becker 2015-08-20 14:57:18 +00:00
parent 177083c730
commit f57b4580a5
15 changed files with 223 additions and 422 deletions

View File

@ -166,11 +166,9 @@ class etemplate_new extends etemplate_widget_template
// Info required to load the etemplate client-side // Info required to load the etemplate client-side
$dom_id = str_replace('.','-',$this->dom_id); $dom_id = str_replace('.','-',$this->dom_id);
$filename = etemplate_widget_template::rel2path(etemplate_widget_template::relPath($name));
$load_array = array( $load_array = array(
'name' => $this->name, 'name' => $this->name,
'url' => egw_framework::link('/etemplate/template.php', array('name' => $this->name, 'download' => filemtime($filename))), 'url' => etemplate_widget_template::rel2url($this->rel_path),
// etemplate_widget_template::rel2url($this->rel_path),
'data' => $data, 'data' => $data,
'DOMNodeID' => $dom_id, 'DOMNodeID' => $dom_id,
); );

View File

@ -35,13 +35,6 @@ class etemplate_widget_template extends etemplate_widget
*/ */
protected static $cache = array(); 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 * Get instance of template specified by name, template(-set) and version
* *

View File

@ -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 * and adds the given attributes to the _target associative array. This
* function also parses the legacyOptions. * 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 {object} _target is the object to which the attributes should be written.
* @param {et2_widget} _proto prototype with attributes and legacyOptions attribute * @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 // Check whether the attributes object is really existing, if not abort
if (typeof _attrsObj == "undefined") if (typeof _attrsObj == "undefined")
{ {
@ -487,62 +488,71 @@ var et2_widget = ClassWithAttributes.extend(
} }
// Iterate over the given attributes and parse them // 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"); var mgr = this.getArrayMgr("content");
// Special handling for the legacy options for (var i = 0; i < _attrsObj.length; i++)
if (attrName == "options" && _proto.legacyOptions.length > 0)
{ {
// Check for modifications on legacy options here. Normal modifications var attrName = _attrsObj[i].name;
// are handled in widget constructor, but it's too late for legacy options then var attrValue = _attrsObj[i].value;
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);
}
// Parse the legacy options (as a string, other types not allowed) // Special handling for the legacy options
var splitted = et2_csvSplit(attrValue+""); if (attrName == "options" && _proto.legacyOptions.length > 0)
for (var j = 0; j < splitted.length && j < _proto.legacyOptions.length; j++)
{ {
// Blank = not set // Check for modifications on legacy options here. Normal modifications
if(splitted[j].trim().length == 0) continue; // are handled in widget constructor, but it's too late for legacy options then
if(_target.id && this.getArrayMgr("modifications").getEntry(_target.id))
// Check to make sure we don't overwrite a current option with a legacy option
if(typeof _target[_proto.legacyOptions[j]] === "undefined")
{ {
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);
}
/** // Parse the legacy options (as a string, other types not allowed)
If more legacy options than expected, stuff them all in the last legacy option var splitted = et2_csvSplit(attrValue+"");
Some legacy options take a comma separated list.
*/ for (var j = 0; j < splitted.length && j < _proto.legacyOptions.length; j++)
if(j == _proto.legacyOptions.length - 1 && splitted.length > _proto.legacyOptions.length) {
// 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 // If the attribute is marked as boolean, parse the
// expression as bool expression. // expression as bool expression.
@ -550,34 +560,15 @@ var et2_widget = ClassWithAttributes.extend(
{ {
attrValue = mgr.parseBoolExpression(attrValue); attrValue = mgr.parseBoolExpression(attrValue);
} }
else if (typeof attrValue != "object") else
{ {
attrValue = mgr.expandName(attrValue); 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 // Set the attribute
// expression as bool expression. _target[attrName] = attrValue;
if (attr.type == "boolean")
{
attrValue = mgr.parseBoolExpression(attrValue);
}
else
{
attrValue = mgr.expandName(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 * 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 * 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 * 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 * further initialize itself from the XML node when the widget's loadFromXML() method
* is called with the object. * is called with the node.
* *
* @param _node Object to read * @param _node XML node to read
* *
* @return et2_widget * @return et2_widget
*/ */
createElementFromObject: function(_node) { createElementFromNode: function(_node) {
var attributes = {}; var attributes = {};
if(typeof _node.attributes === 'undefined')
{
_node.attributes = {};
}
// Parse the "readonly" and "type" flag for this element here, as they // Parse the "readonly" and "type" flag for this element here, as they
// determine which constructor is used // determine which constructor is used
var _nodeName = attributes["type"] = _node.attributes.type ? var _nodeName = attributes["type"] = _node.getAttribute("type") ?
_node.attributes.type : _node.tag; _node.getAttribute("type") : _node.nodeName.toLowerCase();
var readonly = attributes["readonly"] = var readonly = attributes["readonly"] =
this.getArrayMgr("readonlys").isReadOnly( 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 ); typeof this.readonly !== 'undefined' ? this.readonly : this.options.readonly );
// Check to see if modifications change type // Check to see if modifications change type
var modifications = this.getArrayMgr("modifications"); var modifications = this.getArrayMgr("modifications");
if(modifications && _node.attributes.id) { if(modifications && _node.getAttribute("id")) {
var entry = modifications.getEntry(_node.attributes.id); var entry = modifications.getEntry(_node.getAttribute("id"));
if(entry == null) if(entry == null)
{ {
// Try again, but skip the fancy stuff // Try again, but skip the fancy stuff
// TODO: Figure out why the getEntry() call doesn't always work // 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) 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 else
{ {
// Try the root, in case a namespace got missed // 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) if(entry && entry.type)
@ -702,7 +689,7 @@ var et2_widget = ClassWithAttributes.extend(
} }
// Parse the attributes from the given XML attributes object // 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 // Do an sanity check for the attributes
constructor.prototype.generateAttributeSet(attributes); constructor.prototype.generateAttributeSet(attributes);
@ -712,31 +699,39 @@ var et2_widget = ClassWithAttributes.extend(
var widget = new constructor(this, attributes); var widget = new constructor(this, attributes);
// Load the widget itself from XML // Load the widget itself from XML
widget.loadFromJSON(_node); widget.loadFromXML(_node);
return widget; 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) loadFromXML: function(_node) {
{
if(object.content)
{
this.loadContent(object.content);
}
if(!object.children) return;
// Load the child nodes. // 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]; var node = _node.childNodes[i];
node.parentNode = object; 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 // Create the new element
this.createElementFromObject(node); this.createElementFromNode(node);
} }
}, },

View File

@ -64,14 +64,11 @@ function et2_directChildrenByTagName(_node, _tagName)
_tagName = _tagName.toLowerCase(); _tagName = _tagName.toLowerCase();
var result = []; var result = [];
var children = _node.childNodes || _node.children || []; for (var i = 0; i < _node.childNodes.length; i++)
for (var i = 0; i < children.length; i++)
{ {
var child = children[i]; if (_tagName == _node.childNodes[i].nodeName.toLowerCase())
child.parentNode = _node;
if (child.nodeName && _tagName === child.nodeName.toLowerCase() || child.tag && _tagName === child.tag)
{ {
result.push(child); result.push(_node.childNodes[i]);
} }
} }
@ -80,12 +77,10 @@ function et2_directChildrenByTagName(_node, _tagName)
function et2_filteredNodeIterator(_node, _callback, _context) function et2_filteredNodeIterator(_node, _callback, _context)
{ {
if(!_node.children) return; for (var i = 0; i < _node.childNodes.length; i++)
for (var i = 0; i < _node.children.length; i++)
{ {
var node = _node.children[i]; var node = _node.childNodes[i];
node.parentNode = _node; var nodeName = node.nodeName.toLowerCase();
var nodeName = node.tag;
if (nodeName.charAt(0) != "#") if (nodeName.charAt(0) != "#")
{ {
_callback.call(_context, node, nodeName); _callback.call(_context, node, nodeName);
@ -95,15 +90,9 @@ function et2_filteredNodeIterator(_node, _callback, _context)
function et2_readAttrWithDefault(_node, _name, _default) function et2_readAttrWithDefault(_node, _name, _default)
{ {
if( _node.getAttribute)
{
var val = _node.getAttribute(_name); var val = _node.getAttribute(_name);
}
else if (_node.attributes) return (val === null) ? _default : val;
{
var val = _node.attributes[_name];
}
return (val === null || typeof val === 'undefined') ? _default : val;
} }

View File

@ -328,13 +328,13 @@ var et2_customfields_list = et2_valueWidget.extend([et2_IDetachedDOM, et2_IInput
} }
}, },
loadFromJSON: function(_node) { loadFromXML: function(_node) {
this.loadFields(); this.loadFields();
// Load the nodes as usual // Load the nodes as usual
this._super.apply(this, arguments); this._super.apply(this, arguments);
}, },
set_value: function(_value) { set_value: function(_value) {
if(!this.options.customfields) return; if(!this.options.customfields) return;
for(var field_name in this.options.customfields) for(var field_name in this.options.customfields)

View File

@ -53,7 +53,7 @@ var et2_box = et2_baseWidget.extend([et2_IDetachedDOM],
* *
* @param {object} _node * @param {object} _node
*/ */
loadFromJSON: function(_node) { loadFromXML: function(_node) {
if(this._type != "box") if(this._type != "box")
{ {
return this._super.apply(this, arguments); return this._super.apply(this, arguments);
@ -61,16 +61,30 @@ var et2_box = et2_baseWidget.extend([et2_IDetachedDOM],
// Load the child nodes. // Load the child nodes.
var childIndex = 0; var childIndex = 0;
var repeatNode = null; 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 node = _node.childNodes[i];
var widgetType = node.tag; 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 // Create the new element, if no expansion needed
var id = et2_readAttrWithDefault(node, "id", ""); var id = et2_readAttrWithDefault(node, "id", "");
if(id.indexOf('$') < 0 || widgetType != 'box') if(id.indexOf('$') < 0 || widgetType != 'box')
{ {
this.createElementFromObject(node); this.createElementFromNode(node);
childIndex++; childIndex++;
} }
else else
@ -95,7 +109,7 @@ var et2_box = et2_baseWidget.extend([et2_IDetachedDOM],
} }
} }
this.createElementFromObject(repeatNode); this.createElementFromNode(repeatNode);
} }
// Reset // Reset

View File

@ -86,7 +86,7 @@ var et2_entry = et2_valueWidget.extend(
this.setDOMNode(document.createElement('span')); this.setDOMNode(document.createElement('span'));
}, },
loadFromJSON: function(_node) { loadFromXML: function(_node) {
// Load the nodes as usual // Load the nodes as usual
this._super.apply(this, arguments); this._super.apply(this, arguments);

View File

@ -352,9 +352,9 @@ var et2_grid = et2_DOMWidget.extend([et2_IDetachedDOM, et2_IAligned, et2_IResize
var cell = this._getCell(cells, x, y); var cell = this._getCell(cells, x, y);
// Read the span value of the element // 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 else
{ {
@ -423,9 +423,9 @@ var et2_grid = et2_DOMWidget.extend([et2_IDetachedDOM, et2_IAligned, et2_IResize
var cell = this._getCell(cells, x, y); var cell = this._getCell(cells, x, y);
// Read the span value of the element // 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 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); var span = cell.colSpan = this._forceNumber(cell.colSpan);
// Read the align value of the element // 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 // store id of nextmatch-*headers, so it is available for disabled widgets, which get not instanciated
if (nodeName.substr(0, 10) == 'nextmatch-') 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 // 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 // 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 // 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 // If row disabled, just skip it
var disabled = false; var disabled = false;
if(et2_readAttrWithDefault(node, "disabled", false) == "1") if(node.getAttribute("disabled") == "1")
{ {
disabled = true; 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 * 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 * in the first step the
* *
* @param {object} _node xml node to process * @param {object} _node xml node to process
*/ */
loadFromJSON: function(_node) { loadFromXML: function(_node) {
// Keep the node for later changing / reloading // Keep the node for later changing / reloading
this.template_node = _node; this.template_node = _node;
// Get the columns and rows tag // Get the columns and rows tag
var rowsElems = _node.children[1]; var rowsElems = et2_directChildrenByTagName(_node, "rows");
var columnsElems = _node.children[0]; var columnsElems = et2_directChildrenByTagName(_node, "columns");
if (rowsElems && columnsElems) if (rowsElems.length == 1 && columnsElems.length == 1)
{ {
var columns = columnsElems; var columns = columnsElems[0];
var rows = rowsElems; var rows = rowsElems[0];
var colData = []; var colData = [];
var rowData = []; var rowData = [];
@ -870,7 +870,7 @@ var et2_grid = et2_DOMWidget.extend([et2_IDetachedDOM, et2_IAligned, et2_IResize
} }
// Rebuild grid // Rebuild grid
this.loadFromJSON(this.template_node); this.loadFromXML(this.template_node);
// New widgets need to finish // New widgets need to finish
this.loadingFinished(); this.loadingFinished();

View File

@ -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. * a special align value.
* *
* @param {object} _node * @param {object} _node
*/ */
loadFromJSON: function(_node) { loadFromXML: function(_node) {
// Check whether any child node has an alignment tag // Check whether any child node has an alignment tag
et2_filteredNodeIterator(_node, function(_node) { et2_filteredNodeIterator(_node, function(_node) {
var align = et2_readAttrWithDefault(_node, 'align',''); var align = _node.getAttribute("align");
if (!align) if (!align)
{ {

View File

@ -398,9 +398,9 @@ var et2_selectbox = et2_inputWidget.extend(
return true; 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 // 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) if(legacy)
{ {
var legacy = legacy.split(","); var legacy = legacy.split(",");

View File

@ -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 * 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. * by this point. If there are, replace the placeholders.
*/ */
loadFromJSON: function() { loadFromXML: function() {
this._super.apply(this, arguments); this._super.apply(this, arguments);
if(this._children.length > 0) if(this._children.length > 0)
{ {

View File

@ -132,7 +132,7 @@ var et2_tabbox = et2_valueWidget.extend([et2_IInput,et2_IResizeable],
"contentDiv": null, "contentDiv": null,
"flagDiv": null, "flagDiv": null,
"hidden": hide, "hidden": hide,
"JSON": null, "XMLNode": null,
"promise": null "promise": null
}); });
} }
@ -157,7 +157,7 @@ var et2_tabbox = et2_valueWidget.extend([et2_IInput,et2_IResizeable],
if (i < tabData.length) if (i < tabData.length)
{ {
// Store node for later evaluation // Store node for later evaluation
tabData[i].JSON = node; tabData[i].XMLNode = node;
} }
else else
{ {
@ -167,7 +167,7 @@ var et2_tabbox = et2_valueWidget.extend([et2_IInput,et2_IResizeable],
}, this); }, this);
}, },
loadFromJSON: function(_node) { loadFromXML: function(_node) {
// Get the tabs and tabpanels tags // Get the tabs and tabpanels tags
var tabsElems = et2_directChildrenByTagName(_node, "tabs"); var tabsElems = et2_directChildrenByTagName(_node, "tabs");
var tabpanelsElems = et2_directChildrenByTagName(_node, "tabpanels"); var tabpanelsElems = et2_directChildrenByTagName(_node, "tabpanels");
@ -219,7 +219,7 @@ var et2_tabbox = et2_valueWidget.extend([et2_IInput,et2_IResizeable],
"contentDiv": null, "contentDiv": null,
"flagDiv": null, "flagDiv": null,
"hidden": typeof tab.hidden != "undefined" ? tab.hidden : readonly[tab_id] || false, "hidden": typeof tab.hidden != "undefined" ? tab.hidden : readonly[tab_id] || false,
"JSON": null, "XMLNode": null,
"promise": null "promise": null
}); });
} }
@ -274,12 +274,12 @@ var et2_tabbox = et2_valueWidget.extend([et2_IInput,et2_IResizeable],
_loadTab: function(index,promises) { _loadTab: function(index,promises) {
var tabData = this.tabData[index]; var tabData = this.tabData[index];
if(!tabData || tabData.loaded) return; 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 // Release the XML node
tabData.JSON = null; tabData.XMLNode = null;
} }
else if (tabData.widget_options) else if (tabData.widget_options)
{ {

View File

@ -86,9 +86,10 @@ var et2_template = et2_DOMWidget.extend(
var cache_buster = parts.length > 1 ? parts.pop() : null; var cache_buster = parts.length > 1 ? parts.pop() : null;
var template_name = parts.pop(); var template_name = parts.pop();
// Check to see if the template is known // Check to see if XML is known
var template = etemplate2.prototype.get_template_cache(template_name); var xml = null;
if(!template) 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 // Check to see if ID is short form --> prepend parent/top-level name
if(template_name.indexOf('.') < 0) 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; var top_name = root && root._inst ? root._inst.name : null;
if (top_name && template_name.indexOf('.') < 0) template_name = top_name+'.'+template_name; if (top_name && template_name.indexOf('.') < 0) template_name = top_name+'.'+template_name;
} }
template = etemplate2.prototype.get_template_cache(template_name); xml = templates[template_name];
if(!template) if(!xml)
{ {
// Ask server // Ask server
var splitted = template_name.split('.'); var splitted = template_name.split('.');
// use template base url from initial template, to continue using webdav, if that was loaded via webdav // 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 + var path = this.getRoot()._inst.template_base_url + splitted.shift() + "/templates/default/" +
splitted.join('.') + (cache_buster ? '&download='+cache_buster : splitted.join('.')+ ".xet" + (cache_buster ? '?download='+cache_buster :
// if server did not give a cache-buster, fall back to current time // if server did not give a cache-buster, fall back to current time
'&download='+(new Date).valueOf()); '?download='+(new Date).valueOf());
if(splitted.length) if(splitted.length)
{ {
jQuery.ajax({ et2_loadXMLFromURL(path, function(_xmldoc) {
url: path, // Scan for templates and store them
context: this, for(var i = 0; i < _xmldoc.childNodes.length; i++) {
type: 'GET', var template = _xmldoc.childNodes[i];
dataType: 'json', if(template.nodeName.toLowerCase() != "template") continue;
success: function(_data, _status, _xmlhttp){ templates[template.getAttribute("id")] = template;
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);
} }
});
// 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; return;
} }
} }
if(template !== null && typeof template !== "undefined") if(xml !== null && typeof xml !== "undefined")
{ {
this.egw().debug("log", "Loading template: ", template_name); this.egw().debug("log", "Loading template from XML: ", template_name);
if(template.tag) this.loadFromXML(xml);
{
this.loadFromJSON(template);
}
// Don't call this here - done by caller, or on whole widget tree // Don't call this here - done by caller, or on whole widget tree
//this.loadingFinished(); //this.loadingFinished();
@ -151,7 +142,7 @@ var et2_template = et2_DOMWidget.extend(
} }
else else
{ {
this.egw().debug("warn", "Unable to find ", template_name); this.egw().debug("warn", "Unable to find XML for ", template_name);
this.loading.reject(); this.loading.reject();
} }
} }

View File

@ -100,67 +100,22 @@ function etemplate2(_container, _menuaction)
} }
/** // List of templates (XML) that are known, not always used. Indexed by id.
* Return template from global cache, or undefined if not cached // We share list of templates with iframes and popups
* try {
* @param {string} _name if (opener && opener.etemplate2)
* @returns {object|undefined} {
*/ etemplate2.prototype.templates = opener.etemplate2.prototype.templates;
etemplate2.prototype.get_template_cache = function(_name) }
}
catch (e) {
// catch security exception if opener is from a different domain
}
if (typeof etemplate2.prototype.templates == "undefined")
{ {
try { etemplate2.prototype.templates = top.etemplate2.prototype.templates || {};
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];
};
/**
* 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 * Calls the resize event of all widgets
@ -247,8 +202,9 @@ etemplate2.prototype.clear = function()
$j(this.DOMContainer).empty(); $j(this.DOMContainer).empty();
// Remove self from the index // 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++) for(var i = 0; i < etemplate2._byTemplate[name].length; i++)
{ {
if(etemplate2._byTemplate[name][i] == this) if(etemplate2._byTemplate[name][i] == this)
@ -490,12 +446,8 @@ etemplate2.prototype.load = function(_name, _url, _data, _callback)
} }
etemplate2._byTemplate[_name].push(this); etemplate2._byTemplate[_name].push(this);
// Read the structure of the requested template // Read the XML structure of the requested template
var template = this.get_template_cache(this.name); this.widgetContainer.loadFromXML(this.templates[this.name]);
if (template && template.children)
{
this.widgetContainer.loadFromJSON(template);
}
// List of Promises from widgets that are not quite fully loaded // List of Promises from widgets that are not quite fully loaded
var deferred = []; var deferred = [];
@ -589,28 +541,20 @@ etemplate2.prototype.load = function(_name, _url, _data, _callback)
// Load & process // Load & process
var template = this.get_template_cache(_name); if(!this.templates[_name])
if(!template)
{ {
jQuery.ajax({ // Asynchronously load the XET file
url: _url, et2_loadXMLFromURL(_url, function(_xmldoc) {
context: this,
type: 'GET', // Scan for templates and store them
dataType: 'json', for(var i = 0; i < _xmldoc.childNodes.length; i++) {
success: function(_data, _status, _xmlhttp){ var template = _xmldoc.childNodes[i];
for(var i = 0; i < _data.children.length; i++) if(template.nodeName.toLowerCase() != "template") continue;
{ this.templates[template.getAttribute("id")] = template;
var template = _data.children[i]; if(!_name) this.name = template.getAttribute("id");
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);
} }
}); _load.apply(this,[]);
}, this);
// Split the given data into array manager objects and pass those to the // Split the given data into array manager objects and pass those to the
// widget container - do this here because file is loaded async // widget container - do this here because file is loaded async

View File

@ -1,123 +0,0 @@
<?php
/*
* Egroupware
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage api
* @link http://www.egroupware.org
* @author Nathan Gray
* @version $Id$
*/
const CACHE_TIME = 3600;
//Set all necessary info and fire up egroupware
$GLOBALS['egw_info']['flags'] = array(
'currentapp' => '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;
}