template widget with TS

This commit is contained in:
Ralf Becker 2020-01-21 16:11:08 +01:00
parent 5c6f73a26e
commit af6afdcffe
4 changed files with 472 additions and 231 deletions

View File

@ -165,7 +165,7 @@ export abstract class et2_DOMWidget extends et2_widget implements et2_IDOMNode
/** /**
* Attaches the container node of this widget to the DOM-Tree * Attaches the container node of this widget to the DOM-Tree
*/ */
doLoadingFinished() doLoadingFinished() : boolean | JQueryPromise<unknown>
{ {
// Check whether the parent implements the et2_IDOMNode interface. If // Check whether the parent implements the et2_IDOMNode interface. If
// yes, grab the DOM node and create our own. // yes, grab the DOM node and create our own.

View File

@ -154,4 +154,5 @@ declare var et2_video : any;
declare var et2_IExposable : any; declare var et2_IExposable : any;
declare function et2_createWidget(type : string, params : {}, parent? : any) : any; declare function et2_createWidget(type : string, params : {}, parent? : any) : any;
declare function nm_action(_action : {}, _senders : [], _target : any, _ids? : any) : void; declare function nm_action(_action : {}, _senders : [], _target : any, _ids? : any) : void;
declare function et2_compileLegacyJS(_code : string, _widget : et2_widget, _context? : HTMLElement) : Function; declare function et2_compileLegacyJS(_code : string, _widget : et2_widget, _context? : HTMLElement) : Function;
declare function et2_loadXMLFromURL(_url : string, _callback : Function, _context? : object, _fail_callback? : Function) : void;

View File

@ -1,3 +1,4 @@
"use strict";
/** /**
* EGroupware eTemplate2 - JS Template base class * EGroupware eTemplate2 - JS Template base class
* *
@ -6,241 +7,224 @@
* @subpackage api * @subpackage api
* @link http://www.egroupware.org * @link http://www.egroupware.org
* @author Andreas Stöckel * @author Andreas Stöckel
* @copyright Stylite 2011
* @version $Id$
*/ */
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
/*egw:uses /*egw:uses
et2_core_xml; et2_core_xml;
et2_core_DOMWidget; et2_core_DOMWidget;
*/ */
require("./et2_core_interfaces");
require("./et2_core_common");
var et2_core_DOMWidget_1 = require("./et2_core_DOMWidget");
var et2_core_inheritance_1 = require("./et2_core_inheritance");
var et2_core_widget_1 = require("./et2_core_widget");
require("./et2_types");
/** /**
* Class which implements the "template" XET-Tag. When the id parameter is set, * Class which implements the "template" XET-Tag. When the id parameter is set,
* the template class checks whether another template with this id already * the template class checks whether another template with this id already
* exists. If yes, this template is removed from the DOM tree, copied and * exists. If yes, this template is removed from the DOM tree, copied and
* inserted in place of this template. * inserted in place of this template.
*
* @augments et2_DOMWidget
*/ */
var et2_template = (function(){ "use strict"; return et2_DOMWidget.extend( var et2_template = /** @class */ (function (_super) {
{ __extends(et2_template, _super);
attributes: { /**
"template": { * Constructor
"name": "Template", */
"type": "string", function et2_template(_parent, _attrs, _child) {
"description": "Name / ID of template with optional cache-buster ('?'+filemtime of template on server)", var _this =
"default": et2_no_init // Call the inherited constructor
}, _super.call(this, _parent, _attrs, et2_core_inheritance_1.ClassWithAttributes.extendAttributes(et2_core_DOMWidget_1.et2_DOMWidget._attributes, _child || {})) || this;
"group": { _this.createNamespace = true;
// TODO: Not implemented // Set this early, so it's available for creating namespace
"name": "Group", if (_attrs.content) {
"description":"Not implemented", _this.content = _attrs.content;
//"default": 0 }
"default": et2_no_init // constructor was called here before!
}, _this.div = document.createElement("div");
"version": { // Deferred object so we can load via AJAX
"name": "Version", _this.loading = jQuery.Deferred();
"type": "string", // run transformAttributes now, to get server-side modifications (url!)
"description": "Version of the template" if (_attrs.template) {
}, _this.id = _attrs.template;
"lang": { _this.transformAttributes(_attrs);
"name": "Language", _this.options = et2_cloneObject(_attrs);
"type": "string", _attrs = {};
"description": "Language the template is written in" }
}, if (_this.id != "" || _this.options.template) {
"content": { var parts = (_this.options.template || _this.id).split('?');
"name": "Content index", var cache_buster = parts.length > 1 ? parts.pop() : null;
"default": et2_no_init, var template_name = parts.pop();
"description": "Used for passing in specific content to the template other than what it would get by ID." // Check to see if XML is known
}, var xml = null;
url: { var templates = etemplate2.prototype.templates; // use global eTemplate cache
name: "URL of template", if (!(xml = templates[template_name])) {
type: "string", // Check to see if ID is short form --> prepend parent/top-level name
description: "full URL to load template incl. cache-buster" if (template_name.indexOf('.') < 0) {
}, var root = _parent ? _parent.getRoot() : null;
"onload": { var top_name = root && root._inst ? root._inst.name : null;
"name": "onload", if (top_name && template_name.indexOf('.') < 0)
"type": "js", template_name = top_name + '.' + template_name;
"default": et2_no_init, }
"description": "JS code which is executed after the template is loaded." xml = templates[template_name];
} if (!xml) {
}, // Ask server
var url = _this.options.url;
createNamespace: true, if (!_this.options.url) {
var splitted = template_name.split('.');
/** var app = splitted.shift();
* Initializes this template widget as a simple container. // use template base url from initial template, to continue using webdav, if that was loaded via webdav
* url = _this.getRoot()._inst.template_base_url + app + "/templates/default/" +
* @memberOf et2_template splitted.join('.') + ".xet" + (cache_buster ? '?download=' + cache_buster : '');
* @param {et2_widget} _parent }
* @param {object} _attrs // if server did not give a cache-buster, fall back to current time
*/ if (url.indexOf('?') == -1)
init: function(_parent, _attrs) { url += '?download=' + (new Date).valueOf();
// Set this early, so it's available for creating namespace if (_this.options.url || splitted.length) {
if(_attrs.content) var fetch_url_callback = function (_xmldoc) {
{ // Scan for templates and store them
this.content = _attrs.content; for (var i = 0; i < _xmldoc.childNodes.length; i++) {
} var template = _xmldoc.childNodes[i];
this._super.apply(this, arguments); if (template.nodeName.toLowerCase() != "template")
continue;
this.div = document.createElement("div"); templates[template.getAttribute("id")] = template;
}
// Deferred object so we can load via AJAX // Read the XML structure of the requested template
this.loading = jQuery.Deferred(); if (typeof templates[template_name] != 'undefined')
this.loadFromXML(templates[template_name]);
// run transformAttributes now, to get server-side modifications (url!) // Update flag
if (_attrs.template) this.loading.resolve();
{ };
this.id = _attrs.template; et2_loadXMLFromURL(url, fetch_url_callback, _this, function (error) {
this.transformAttributes(_attrs); url = egw.link('/' + app + "/templates/default/" +
this.options = et2_cloneObject(_attrs); splitted.join('.') + ".xet", { download: cache_buster ? cache_buster : (new Date).valueOf() });
_attrs = {}; et2_loadXMLFromURL(url, fetch_url_callback, this);
} });
if (this.id != "" || this.options.template) }
{ return _this;
var parts = (this.options.template || this.id).split('?'); }
var cache_buster = parts.length > 1 ? parts.pop() : null; }
var template_name = parts.pop(); if (xml !== null && typeof xml !== "undefined") {
_this.egw().debug("log", "Loading template from XML: ", template_name);
// Check to see if XML is known _this.loadFromXML(xml);
var xml = null; // Don't call this here - done by caller, or on whole widget tree
var templates = etemplate2.prototype.templates; // use global eTemplate cache //this.loadingFinished();
if(!(xml = templates[template_name])) // But resolve the promise
{ _this.loading.resolve();
// Check to see if ID is short form --> prepend parent/top-level name }
if(template_name.indexOf('.') < 0) else {
{ _this.egw().debug("warn", "Unable to find XML for ", template_name);
var root = _parent ? _parent.getRoot() : null; _this.loading.reject();
var top_name = root && root._inst ? root._inst.name : null; }
if (top_name && template_name.indexOf('.') < 0) template_name = top_name+'.'+template_name; }
} else {
xml = templates[template_name]; // No actual template
if(!xml) _this.loading.resolve();
{ }
// Ask server return _this;
var url = this.options.url; }
if (!this.options.url) /**
{ * Override parent to support content attribute
var splitted = template_name.split('.'); * Templates always have ID set, but seldom do we want them to
var app = splitted.shift(); * create a namespace based on their ID.
// use template base url from initial template, to continue using webdav, if that was loaded via webdav */
url = this.getRoot()._inst.template_base_url + app + "/templates/default/" + et2_template.prototype.checkCreateNamespace = function () {
splitted.join('.')+ ".xet" + (cache_buster ? '?download='+cache_buster : ''); if (this.content) {
} var old_id = this.id;
// if server did not give a cache-buster, fall back to current time this.id = this.content;
if (url.indexOf('?') == -1) url += '?download='+(new Date).valueOf(); _super.prototype.checkCreateNamespace.apply(this, arguments);
this.id = old_id;
if(this.options.url || splitted.length) }
{ };
var fetch_url_callback = function(_xmldoc) { et2_template.prototype.getDOMNode = function () {
// Scan for templates and store them return this.div;
for(var i = 0; i < _xmldoc.childNodes.length; i++) { };
var template = _xmldoc.childNodes[i]; et2_template.prototype.attachToDOM = function () {
if(template.nodeName.toLowerCase() != "template") continue; if (this.div) {
templates[template.getAttribute("id")] = template; jQuery(this.div)
} .off('.et2_template')
.bind("load.et2_template", this, function (e) {
// Read the XML structure of the requested template e.data.load.call(e.data, this);
if (typeof templates[template_name] != 'undefined') this.loadFromXML(templates[template_name]); });
}
// Update flag return _super.prototype.attachToDOM.call(this);
this.loading.resolve(); };
/**
} * Called after the template is fully loaded to handle any onload handlers
*/
et2_loadXMLFromURL(url, fetch_url_callback, this, function( error) { et2_template.prototype.load = function () {
url = egw.link('/'+ app + "/templates/default/" + if (typeof this.options.onload == 'function') {
splitted.join('.')+ ".xet", {download:cache_buster? cache_buster :(new Date).valueOf()}); // Make sure function gets a reference to the widget
var args = Array.prototype.slice.call(arguments);
et2_loadXMLFromURL(url, fetch_url_callback, this); if (args.indexOf(this) == -1)
}); args.push(this);
} return this.options.onload.apply(this, args);
return; }
} };
} /**
if(xml !== null && typeof xml !== "undefined") * Override to return the promise for deferred loading
{ */
this.egw().debug("log", "Loading template from XML: ", template_name); et2_template.prototype.doLoadingFinished = function () {
this.loadFromXML(xml); // Apply parent now, which actually puts into the DOM
// Don't call this here - done by caller, or on whole widget tree _super.prototype.doLoadingFinished.call(this);
//this.loadingFinished(); // Fire load event when done loading
this.loading.done(jQuery.proxy(function () { jQuery(this).trigger("load"); }, this.div));
// But resolve the promise // Not done yet, but widget will let you know
this.loading.resolve(); return this.loading.promise();
} };
else et2_template._attributes = {
{ "template": {
this.egw().debug("warn", "Unable to find XML for ", template_name); "name": "Template",
this.loading.reject(); "type": "string",
} "description": "Name / ID of template with optional cache-buster ('?'+filemtime of template on server)",
} "default": et2_no_init
else },
{ "group": {
// No actual template // TODO: Not implemented
this.loading.resolve(); "name": "Group",
} "description": "Not implemented",
}, //"default": 0
"default": et2_no_init
/** },
* Override parent to support content attribute "version": {
* Templates always have ID set, but seldom do we want them to "name": "Version",
* create a namespace based on their ID. "type": "string",
*/ "description": "Version of the template"
checkCreateNamespace: function() { },
if(this.content) "lang": {
{ "name": "Language",
var old_id = this.id; "type": "string",
this.id = this.content; "description": "Language the template is written in"
this._super.apply(this, arguments); },
this.id = old_id; "content": {
} "name": "Content index",
}, "default": et2_no_init,
"description": "Used for passing in specific content to the template other than what it would get by ID."
getDOMNode: function() { },
return this.div; url: {
}, name: "URL of template",
type: "string",
attachToDOM: function() { description: "full URL to load template incl. cache-buster"
if (this.div) },
{ "onload": {
jQuery(this.div) "name": "onload",
.off('.et2_template') "type": "js",
.bind("load.et2_template", this, function(e) { "default": et2_no_init,
e.data.load.call(e.data, this); "description": "JS code which is executed after the template is loaded."
}); }
} };
return et2_template;
this._super.apply(this,arguments); }(et2_core_DOMWidget_1.et2_DOMWidget));
}, et2_core_widget_1.et2_register_widget(et2_template, ["template"]);
/**
* Called after the template is fully loaded to handle any onload handlers
*/
load: function() {
if(typeof this.options.onload == 'function')
{
// Make sure function gets a reference to the widget
var args = Array.prototype.slice.call(arguments);
if(args.indexOf(this) == -1) args.push(this);
return this.options.onload.apply(this, args);
}
},
/**
* Override to return the promise for deferred loading
*/
doLoadingFinished: function() {
// Apply parent now, which actually puts into the DOM
this._super.apply(this, arguments);
// Fire load event when done loading
this.loading.done(jQuery.proxy(function() {jQuery(this).trigger("load");},this.div));
// Not done yet, but widget will let you know
return this.loading.promise();
}
});}).call(this);
et2_register_widget(et2_template, ["template"]);

View File

@ -0,0 +1,256 @@
/**
* EGroupware eTemplate2 - JS Template base class
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage api
* @link http://www.egroupware.org
* @author Andreas Stöckel
*/
/*egw:uses
et2_core_xml;
et2_core_DOMWidget;
*/
import './et2_core_interfaces';
import './et2_core_common';
import { et2_DOMWidget } from './et2_core_DOMWidget';
import { ClassWithAttributes } from "./et2_core_inheritance";
import { et2_widget, et2_createWidget, et2_register_widget, WidgetConfig } from "./et2_core_widget";
import './et2_types';
/**
* Class which implements the "template" XET-Tag. When the id parameter is set,
* the template class checks whether another template with this id already
* exists. If yes, this template is removed from the DOM tree, copied and
* inserted in place of this template.
*/
class et2_template extends et2_DOMWidget
{
static readonly _attributes : any = {
"template": {
"name": "Template",
"type": "string",
"description": "Name / ID of template with optional cache-buster ('?'+filemtime of template on server)",
"default": et2_no_init
},
"group": {
// TODO: Not implemented
"name": "Group",
"description":"Not implemented",
//"default": 0
"default": et2_no_init
},
"version": {
"name": "Version",
"type": "string",
"description": "Version of the template"
},
"lang": {
"name": "Language",
"type": "string",
"description": "Language the template is written in"
},
"content": {
"name": "Content index",
"default": et2_no_init,
"description": "Used for passing in specific content to the template other than what it would get by ID."
},
url: {
name: "URL of template",
type: "string",
description: "full URL to load template incl. cache-buster"
},
"onload": {
"name": "onload",
"type": "js",
"default": et2_no_init,
"description": "JS code which is executed after the template is loaded."
}
};
createNamespace: boolean = true;
content: string;
div: HTMLDivElement;
loading: JQueryDeferred<unknown>;
/**
* Constructor
*/
constructor(_parent, _attrs? : WidgetConfig, _child? : object)
{
// Call the inherited constructor
super(_parent, _attrs, ClassWithAttributes.extendAttributes(et2_DOMWidget._attributes, _child || {}));
// Set this early, so it's available for creating namespace
if(_attrs.content)
{
this.content = _attrs.content;
}
// constructor was called here before!
this.div = document.createElement("div");
// Deferred object so we can load via AJAX
this.loading = jQuery.Deferred();
// run transformAttributes now, to get server-side modifications (url!)
if (_attrs.template)
{
this.id = _attrs.template;
this.transformAttributes(_attrs);
this.options = et2_cloneObject(_attrs);
_attrs = {};
}
if (this.id != "" || this.options.template)
{
var parts = (this.options.template || this.id).split('?');
var cache_buster = parts.length > 1 ? parts.pop() : null;
var template_name = parts.pop();
// 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)
{
var root = _parent ? _parent.getRoot() : null;
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)
{
// Ask server
var url = this.options.url;
if (!this.options.url)
{
var splitted = template_name.split('.');
var app = splitted.shift();
// use template base url from initial template, to continue using webdav, if that was loaded via webdav
url = this.getRoot()._inst.template_base_url + app + "/templates/default/" +
splitted.join('.')+ ".xet" + (cache_buster ? '?download='+cache_buster : '');
}
// if server did not give a cache-buster, fall back to current time
if (url.indexOf('?') == -1) url += '?download='+(new Date).valueOf();
if(this.options.url || splitted.length)
{
var fetch_url_callback = 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();
};
et2_loadXMLFromURL(url, fetch_url_callback, this, function( error) {
url = egw.link('/'+ app + "/templates/default/" +
splitted.join('.')+ ".xet", {download:cache_buster? cache_buster :(new Date).valueOf()});
et2_loadXMLFromURL(url, fetch_url_callback, this);
});
}
return;
}
}
if(xml !== null && typeof xml !== "undefined")
{
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();
// But resolve the promise
this.loading.resolve();
}
else
{
this.egw().debug("warn", "Unable to find XML for ", template_name);
this.loading.reject();
}
}
else
{
// No actual template
this.loading.resolve();
}
}
/**
* Override parent to support content attribute
* Templates always have ID set, but seldom do we want them to
* create a namespace based on their ID.
*/
checkCreateNamespace()
{
if(this.content)
{
var old_id = this.id;
this.id = this.content;
super.checkCreateNamespace.apply(this, arguments);
this.id = old_id;
}
}
getDOMNode()
{
return this.div;
}
attachToDOM()
{
if (this.div)
{
jQuery(this.div)
.off('.et2_template')
.bind("load.et2_template", this, function(e) {
e.data.load.call(e.data, this);
});
}
return super.attachToDOM();
}
/**
* Called after the template is fully loaded to handle any onload handlers
*/
load()
{
if(typeof this.options.onload == 'function')
{
// Make sure function gets a reference to the widget
var args = Array.prototype.slice.call(arguments);
if(args.indexOf(this) == -1) args.push(this);
return this.options.onload.apply(this, args);
}
}
/**
* Override to return the promise for deferred loading
*/
doLoadingFinished()
{
// Apply parent now, which actually puts into the DOM
super.doLoadingFinished();
// Fire load event when done loading
this.loading.done(jQuery.proxy(function() {jQuery(this).trigger("load");},this.div));
// Not done yet, but widget will let you know
return this.loading.promise();
}
}
et2_register_widget(et2_template, ["template"]);