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
$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,
);

View File

@ -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
*

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
* 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);
}
},

View File

@ -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;
}

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();
// 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)

View File

@ -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

View File

@ -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);

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);
// 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();

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.
*
* @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)
{

View File

@ -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(",");

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

View File

@ -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)
{

View File

@ -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();
}
}

View File

@ -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

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;
}