forked from extern/egroupware
Etemplate now uses JSON instead of XML for templates on the client side.
XML files are parsed into JSON objects on the server, then sent as JSON. Etemplate parses the JSON object on the client side instead of the XML file directly. This is supposed to be faster for IE.
This commit is contained in:
parent
beb829c315
commit
ecb972ca66
@ -166,9 +166,11 @@ 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' => etemplate_widget_template::rel2url($this->rel_path),
|
||||
'url' => egw_framework::link('/etemplate/template.php', array('name' => $this->name, 'download' => filemtime($filename))),
|
||||
// etemplate_widget_template::rel2url($this->rel_path),
|
||||
'data' => $data,
|
||||
'DOMNodeID' => $dom_id,
|
||||
);
|
||||
|
@ -35,6 +35,13 @@ 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
|
||||
*
|
||||
|
@ -471,16 +471,15 @@ var et2_widget = ClassWithAttributes.extend(
|
||||
},
|
||||
|
||||
/**
|
||||
* The parseXMLAttrs function takes an XML DOM attributes object
|
||||
* The parseJSONAttrs function takes a JSON object
|
||||
* and adds the given attributes to the _target associative array. This
|
||||
* function also parses the legacyOptions.
|
||||
*
|
||||
* @param _attrsObj is the XML DOM attributes object
|
||||
* @param _attrsObj is the JSON object
|
||||
* @param {object} _target is the object to which the attributes should be written.
|
||||
* @param {et2_widget} _proto prototype with attributes and legacyOptions attribute
|
||||
*/
|
||||
parseXMLAttrs: function(_attrsObj, _target, _proto) {
|
||||
|
||||
parseJSONAttrs: function(_attrsObj, _target, _proto) {
|
||||
// Check whether the attributes object is really existing, if not abort
|
||||
if (typeof _attrsObj == "undefined")
|
||||
{
|
||||
@ -488,71 +487,62 @@ var et2_widget = ClassWithAttributes.extend(
|
||||
}
|
||||
|
||||
// Iterate over the given attributes and parse them
|
||||
var mgr = this.getArrayMgr("content");
|
||||
for (var i = 0; i < _attrsObj.length; i++)
|
||||
for (var name in _attrsObj)
|
||||
{
|
||||
var attrName = _attrsObj[i].name;
|
||||
var attrValue = _attrsObj[i].value;
|
||||
this._parseAttr(name, _attrsObj[name], _target, _proto);
|
||||
}
|
||||
},
|
||||
|
||||
// Special handling for the legacy options
|
||||
if (attrName == "options" && _proto.legacyOptions.length > 0)
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
// 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))
|
||||
{
|
||||
// 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 = _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)
|
||||
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[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;
|
||||
}
|
||||
}
|
||||
var mod = this.getArrayMgr("modifications").getEntry(_target.id);
|
||||
if(typeof mod.options != "undefined") attrValue = attrValue = mod.options;
|
||||
}
|
||||
else
|
||||
// expand legacyOptions with content
|
||||
if(attrValue.charAt(0) == '@' || attrValue.indexOf('$') != -1)
|
||||
{
|
||||
if (mgr != null && typeof _proto.attributes[attrName] != "undefined")
|
||||
attrValue = mgr.expandName(attrValue);
|
||||
}
|
||||
|
||||
// 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")
|
||||
{
|
||||
var attr = _proto.attributes[attrName];
|
||||
attrValue = splitted[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.
|
||||
@ -560,16 +550,35 @@ var et2_widget = ClassWithAttributes.extend(
|
||||
{
|
||||
attrValue = mgr.parseBoolExpression(attrValue);
|
||||
}
|
||||
else
|
||||
else if (typeof attrValue != "object")
|
||||
{
|
||||
attrValue = mgr.expandName(attrValue);
|
||||
}
|
||||
_target[_proto.legacyOptions[j]] = attrValue;
|
||||
}
|
||||
|
||||
// Set the attribute
|
||||
_target[attrName] = 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;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@ -622,47 +631,51 @@ var et2_widget = ClassWithAttributes.extend(
|
||||
},
|
||||
|
||||
/**
|
||||
* Create a et2_widget from an XML node.
|
||||
* Create a et2_widget from a JSON object.
|
||||
*
|
||||
* 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 XML node when the widget's loadFromXML() method
|
||||
* is called with the node.
|
||||
* further initialize itself from the object when the widget's loadFromObject() method
|
||||
* is called with the object.
|
||||
*
|
||||
* @param _node XML node to read
|
||||
* @param _node Object to read
|
||||
*
|
||||
* @return et2_widget
|
||||
*/
|
||||
createElementFromNode: function(_node) {
|
||||
createElementFromObject: 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.getAttribute("type") ?
|
||||
_node.getAttribute("type") : _node.nodeName.toLowerCase();
|
||||
var _nodeName = attributes["type"] = _node.attributes.type ?
|
||||
_node.attributes.type : _node.tag;
|
||||
var readonly = attributes["readonly"] =
|
||||
this.getArrayMgr("readonlys").isReadOnly(
|
||||
_node.getAttribute("id"), _node.getAttribute("readonly"),
|
||||
_node.attributes.id, _node.attributes.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.getAttribute("id")) {
|
||||
var entry = modifications.getEntry(_node.getAttribute("id"));
|
||||
if(modifications && _node.attributes.id) {
|
||||
var entry = modifications.getEntry(_node.attributes.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.getAttribute("id")];
|
||||
var entry = modifications.data[_node.attributes.id];
|
||||
if(entry)
|
||||
{
|
||||
this.egw().debug("warn", "getEntry("+_node.getAttribute("id")+") failed, but the data is there.", modifications, entry);
|
||||
this.egw().debug("warn", "getEntry("+_node.attributes.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.getAttribute("id"));
|
||||
var entry = modifications.getRoot().getEntry(_node.attributes.id);
|
||||
}
|
||||
}
|
||||
if(entry && entry.type)
|
||||
@ -689,7 +702,7 @@ var et2_widget = ClassWithAttributes.extend(
|
||||
}
|
||||
|
||||
// Parse the attributes from the given XML attributes object
|
||||
this.parseXMLAttrs(_node.attributes, attributes, constructor.prototype);
|
||||
this.parseJSONAttrs(_node.attributes, attributes, constructor.prototype);
|
||||
|
||||
// Do an sanity check for the attributes
|
||||
constructor.prototype.generateAttributeSet(attributes);
|
||||
@ -699,39 +712,31 @@ var et2_widget = ClassWithAttributes.extend(
|
||||
var widget = new constructor(this, attributes);
|
||||
|
||||
// Load the widget itself from XML
|
||||
widget.loadFromXML(_node);
|
||||
widget.loadFromJSON(_node);
|
||||
|
||||
return widget;
|
||||
},
|
||||
|
||||
/**
|
||||
* Loads the widget tree from an XML node
|
||||
* Loads the widget tree from JSON object
|
||||
*
|
||||
* @param _node xml node
|
||||
* @param {Object} _content
|
||||
*/
|
||||
loadFromXML: function(_node) {
|
||||
// Load the child nodes.
|
||||
for (var i = 0; i < _node.childNodes.length; i++)
|
||||
loadFromJSON: function(object)
|
||||
{
|
||||
if(object.content)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
this.loadContent(object.content);
|
||||
}
|
||||
if(!object.children) return;
|
||||
// Load the child nodes.
|
||||
for (var i = 0; i < object.children.length; i++)
|
||||
{
|
||||
var node = object.children[i];
|
||||
node.parentNode = object;
|
||||
|
||||
// Create the new element
|
||||
this.createElementFromNode(node);
|
||||
this.createElementFromObject(node);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -64,11 +64,14 @@ function et2_directChildrenByTagName(_node, _tagName)
|
||||
_tagName = _tagName.toLowerCase();
|
||||
|
||||
var result = [];
|
||||
for (var i = 0; i < _node.childNodes.length; i++)
|
||||
var children = _node.childNodes || _node.children || [];
|
||||
for (var i = 0; i < children.length; i++)
|
||||
{
|
||||
if (_tagName == _node.childNodes[i].nodeName.toLowerCase())
|
||||
var child = children[i];
|
||||
child.parentNode = _node;
|
||||
if (child.nodeName && _tagName === child.nodeName.toLowerCase() || child.tag && _tagName === child.tag)
|
||||
{
|
||||
result.push(_node.childNodes[i]);
|
||||
result.push(child);
|
||||
}
|
||||
}
|
||||
|
||||
@ -77,10 +80,12 @@ function et2_directChildrenByTagName(_node, _tagName)
|
||||
|
||||
function et2_filteredNodeIterator(_node, _callback, _context)
|
||||
{
|
||||
for (var i = 0; i < _node.childNodes.length; i++)
|
||||
if(!_node.children) return;
|
||||
for (var i = 0; i < _node.children.length; i++)
|
||||
{
|
||||
var node = _node.childNodes[i];
|
||||
var nodeName = node.nodeName.toLowerCase();
|
||||
var node = _node.children[i];
|
||||
node.parentNode = _node;
|
||||
var nodeName = node.tag;
|
||||
if (nodeName.charAt(0) != "#")
|
||||
{
|
||||
_callback.call(_context, node, nodeName);
|
||||
@ -90,9 +95,15 @@ function et2_filteredNodeIterator(_node, _callback, _context)
|
||||
|
||||
function et2_readAttrWithDefault(_node, _name, _default)
|
||||
{
|
||||
if( _node.getAttribute)
|
||||
{
|
||||
var val = _node.getAttribute(_name);
|
||||
|
||||
return (val === null) ? _default : val;
|
||||
}
|
||||
else if (_node.attributes)
|
||||
{
|
||||
var val = _node.attributes[_name];
|
||||
}
|
||||
return (val === null || typeof val === 'undefined') ? _default : val;
|
||||
}
|
||||
|
||||
|
||||
|
@ -328,13 +328,13 @@ var et2_customfields_list = et2_valueWidget.extend([et2_IDetachedDOM, et2_IInput
|
||||
}
|
||||
},
|
||||
|
||||
loadFromXML: function(_node) {
|
||||
loadFromJSON: 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)
|
||||
|
@ -53,7 +53,7 @@ var et2_box = et2_baseWidget.extend([et2_IDetachedDOM],
|
||||
*
|
||||
* @param {object} _node
|
||||
*/
|
||||
loadFromXML: function(_node) {
|
||||
loadFromJSON: function(_node) {
|
||||
if(this._type != "box")
|
||||
{
|
||||
return this._super.apply(this, arguments);
|
||||
@ -61,30 +61,16 @@ var et2_box = et2_baseWidget.extend([et2_IDetachedDOM],
|
||||
// Load the child nodes.
|
||||
var childIndex = 0;
|
||||
var repeatNode = null;
|
||||
for (var i=0; i < _node.childNodes.length; i++)
|
||||
for (var i=0; i < _node.children.length; i++)
|
||||
{
|
||||
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;
|
||||
}
|
||||
var node = _node.children[i];
|
||||
var widgetType = node.tag;
|
||||
|
||||
// Create the new element, if no expansion needed
|
||||
var id = et2_readAttrWithDefault(node, "id", "");
|
||||
if(id.indexOf('$') < 0 || widgetType != 'box')
|
||||
{
|
||||
this.createElementFromNode(node);
|
||||
this.createElementFromObject(node);
|
||||
childIndex++;
|
||||
}
|
||||
else
|
||||
@ -109,7 +95,7 @@ var et2_box = et2_baseWidget.extend([et2_IDetachedDOM],
|
||||
}
|
||||
}
|
||||
|
||||
this.createElementFromNode(repeatNode);
|
||||
this.createElementFromObject(repeatNode);
|
||||
}
|
||||
|
||||
// Reset
|
||||
|
@ -86,7 +86,7 @@ var et2_entry = et2_valueWidget.extend(
|
||||
this.setDOMNode(document.createElement('span'));
|
||||
},
|
||||
|
||||
loadFromXML: function(_node) {
|
||||
loadFromJSON: function(_node) {
|
||||
// Load the nodes as usual
|
||||
this._super.apply(this, arguments);
|
||||
|
||||
|
@ -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.getAttribute("span"))
|
||||
if (node.attributes.span)
|
||||
{
|
||||
cell.rowSpan = node.getAttribute("span");
|
||||
cell.rowSpan = node.attributes.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.getAttribute("span"))
|
||||
if (node.attributes && node.attributes.span)
|
||||
{
|
||||
cell.colSpan = node.getAttribute("span");
|
||||
cell.colSpan = node.attributes.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.getAttribute("align"))
|
||||
if (node.attributes && node.attributes.align)
|
||||
{
|
||||
cell.align = node.getAttribute("align");
|
||||
cell.align = node.attributes.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.getAttribute('id');
|
||||
cell.nm_id = node.attributes.id;
|
||||
}
|
||||
// Apply widget's class to td, for backward compatability
|
||||
if(node.getAttribute("class"))
|
||||
if(node.attributes && node.attributes.class)
|
||||
{
|
||||
cell.class += (cell.class ? " " : "") + node.getAttribute("class");
|
||||
cell.class += (cell.class ? " " : "") + node.attributes.class;
|
||||
}
|
||||
|
||||
// Create the element
|
||||
@ -480,7 +480,7 @@ var et2_grid = et2_DOMWidget.extend([et2_IDetachedDOM, et2_IAligned, et2_IResize
|
||||
|
||||
}
|
||||
|
||||
var widget = this.createElementFromNode(node, nodeName);
|
||||
var widget = this.createElementFromObject(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(node.getAttribute("disabled") == "1")
|
||||
if(et2_readAttrWithDefault(node, "disabled", false) == "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 loadFromXML function and doing a two-pass reading -
|
||||
* overwriting the loadFromJSON function and doing a two-pass reading -
|
||||
* in the first step the
|
||||
*
|
||||
* @param {object} _node xml node to process
|
||||
*/
|
||||
loadFromXML: function(_node) {
|
||||
loadFromJSON: function(_node) {
|
||||
// Keep the node for later changing / reloading
|
||||
this.template_node = _node;
|
||||
|
||||
// Get the columns and rows tag
|
||||
var rowsElems = et2_directChildrenByTagName(_node, "rows");
|
||||
var columnsElems = et2_directChildrenByTagName(_node, "columns");
|
||||
var rowsElems = _node.children[1];
|
||||
var columnsElems = _node.children[0];
|
||||
|
||||
if (rowsElems.length == 1 && columnsElems.length == 1)
|
||||
if (rowsElems && columnsElems)
|
||||
{
|
||||
var columns = columnsElems[0];
|
||||
var rows = rowsElems[0];
|
||||
var columns = columnsElems;
|
||||
var rows = rowsElems;
|
||||
var colData = [];
|
||||
var rowData = [];
|
||||
|
||||
@ -870,7 +870,7 @@ var et2_grid = et2_DOMWidget.extend([et2_IDetachedDOM, et2_IAligned, et2_IResize
|
||||
}
|
||||
|
||||
// Rebuild grid
|
||||
this.loadFromXML(this.template_node);
|
||||
this.loadFromJSON(this.template_node);
|
||||
|
||||
// New widgets need to finish
|
||||
this.loadingFinished();
|
||||
|
@ -96,15 +96,15 @@ var et2_hbox = et2_baseWidget.extend(
|
||||
},
|
||||
|
||||
/**
|
||||
* The overwritten loadFromXML function checks whether any child element has
|
||||
* The overwritten function checks whether any child element has
|
||||
* a special align value.
|
||||
*
|
||||
* @param {object} _node
|
||||
*/
|
||||
loadFromXML: function(_node) {
|
||||
loadFromJSON: function(_node) {
|
||||
// Check whether any child node has an alignment tag
|
||||
et2_filteredNodeIterator(_node, function(_node) {
|
||||
var align = _node.getAttribute("align");
|
||||
var align = et2_readAttrWithDefault(_node, 'align','');
|
||||
|
||||
if (!align)
|
||||
{
|
||||
|
@ -398,9 +398,9 @@ var et2_selectbox = et2_inputWidget.extend(
|
||||
return true;
|
||||
},
|
||||
|
||||
loadFromXML: function(_node) {
|
||||
loadFromJSON: 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.getAttribute("options");
|
||||
var legacy = _node.attributes.options || false;
|
||||
if(legacy)
|
||||
{
|
||||
var legacy = legacy.split(",");
|
||||
|
@ -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.
|
||||
*/
|
||||
loadFromXML: function() {
|
||||
loadFromJSON: function() {
|
||||
this._super.apply(this, arguments);
|
||||
if(this._children.length > 0)
|
||||
{
|
||||
|
@ -132,7 +132,7 @@ var et2_tabbox = et2_valueWidget.extend([et2_IInput,et2_IResizeable],
|
||||
"contentDiv": null,
|
||||
"flagDiv": null,
|
||||
"hidden": hide,
|
||||
"XMLNode": null,
|
||||
"JSON": 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].XMLNode = node;
|
||||
tabData[i].JSON = node;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -167,7 +167,7 @@ var et2_tabbox = et2_valueWidget.extend([et2_IInput,et2_IResizeable],
|
||||
}, this);
|
||||
},
|
||||
|
||||
loadFromXML: function(_node) {
|
||||
loadFromJSON: 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,
|
||||
"XMLNode": null,
|
||||
"JSON": 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.XMLNode != null)
|
||||
if(tabData.JSON != null)
|
||||
{
|
||||
tabData.widget = this.createElementFromNode(tabData.XMLNode,tabData.XMLNode.nodeName.toLowerCase());
|
||||
tabData.widget = this.createElementFromObject(tabData.JSON,tabData.JSON.tag);
|
||||
|
||||
// Release the XML node
|
||||
tabData.XMLNode = null;
|
||||
// Release the JSON object
|
||||
tabData.JSON = null;
|
||||
}
|
||||
else if (tabData.widget_options)
|
||||
{
|
||||
|
@ -86,10 +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 XML is known
|
||||
var xml = null;
|
||||
// Check to see if the template is known
|
||||
var template = null;
|
||||
var templates = etemplate2.prototype.templates; // use global eTemplate cache
|
||||
if(!(xml = templates[template_name]))
|
||||
if(!(template = templates[template_name]))
|
||||
{
|
||||
// Check to see if ID is short form --> prepend parent/top-level name
|
||||
if(template_name.indexOf('.') < 0)
|
||||
@ -98,42 +98,52 @@ 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;
|
||||
}
|
||||
xml = templates[template_name];
|
||||
if(!xml)
|
||||
template = templates[template_name];
|
||||
if(!template)
|
||||
{
|
||||
// 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.shift() + "/templates/default/" +
|
||||
splitted.join('.')+ ".xet" + (cache_buster ? '?download='+cache_buster :
|
||||
var path = this.getRoot()._inst.template_base_url +
|
||||
splitted.join('.') + (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)
|
||||
{
|
||||
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;
|
||||
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;
|
||||
templates[template.attributes.id] = template;
|
||||
}// Read the structure of the requested template
|
||||
if (typeof templates[template_name] != 'undefined') this.loadFromJSON(templates[template_name]);
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
if(xml !== null && typeof xml !== "undefined")
|
||||
if(template !== null && typeof template !== "undefined")
|
||||
{
|
||||
this.egw().debug("log", "Loading template from XML: ", template_name);
|
||||
this.loadFromXML(xml);
|
||||
this.egw().debug("log", "Loading template: ", template_name);
|
||||
if(template.tag)
|
||||
{
|
||||
this.loadFromJSON(template);
|
||||
}
|
||||
|
||||
// Don't call this here - done by caller, or on whole widget tree
|
||||
//this.loadingFinished();
|
||||
|
||||
@ -142,7 +152,7 @@ var et2_template = et2_DOMWidget.extend(
|
||||
}
|
||||
else
|
||||
{
|
||||
this.egw().debug("warn", "Unable to find XML for ", template_name);
|
||||
this.egw().debug("warn", "Unable to find ", template_name);
|
||||
this.loading.reject();
|
||||
}
|
||||
}
|
||||
|
@ -446,8 +446,11 @@ etemplate2.prototype.load = function(_name, _url, _data, _callback)
|
||||
}
|
||||
etemplate2._byTemplate[_name].push(this);
|
||||
|
||||
// Read the XML structure of the requested template
|
||||
this.widgetContainer.loadFromXML(this.templates[this.name]);
|
||||
// Read the structure of the requested template
|
||||
if (this.templates[this.name].children)
|
||||
{
|
||||
this.widgetContainer.loadFromJSON(this.templates[this.name]);
|
||||
}
|
||||
|
||||
// List of Promises from widgets that are not quite fully loaded
|
||||
var deferred = [];
|
||||
@ -543,18 +546,25 @@ etemplate2.prototype.load = function(_name, _url, _data, _callback)
|
||||
// Load & process
|
||||
if(!this.templates[_name])
|
||||
{
|
||||
// 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");
|
||||
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.templates[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
|
||||
// widget container - do this here because file is loaded async
|
||||
|
126
etemplate/template.php
Normal file
126
etemplate/template.php
Normal file
@ -0,0 +1,126 @@
|
||||
<?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;
|
||||
}
|
||||
error_log("Filename: $filename");
|
||||
|
||||
$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) return;
|
||||
|
||||
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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user