mirror of
https://github.com/EGroupware/egroupware.git
synced 2024-11-08 09:04:53 +01:00
WIP with TypeScript
This commit is contained in:
parent
b65b21fe9c
commit
a4ce103d7e
File diff suppressed because it is too large
Load Diff
899
api/js/etemplate/et2_core_DOMWidget.ts
Normal file
899
api/js/etemplate/et2_core_DOMWidget.ts
Normal file
@ -0,0 +1,899 @@
|
||||
/**
|
||||
* EGroupware eTemplate2 - JS DOM Widget 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_interfaces;
|
||||
et2_core_widget;
|
||||
/api/js/egw_action/egw_action.js;
|
||||
*/
|
||||
|
||||
import { ClassWithAttributes } from './et2_core_inheritance';
|
||||
import './et2_core_interfaces';
|
||||
import './et2_core_common';
|
||||
import {et2_widget, et2_createWidget, et2_register_widget, WidgetConfig} from "./et2_core_widget";
|
||||
import '../egw_action/egw_action.js';
|
||||
|
||||
/**
|
||||
* Abstract widget class which can be inserted into the DOM. All widget classes
|
||||
* deriving from this class have to care about implementing the "getDOMNode"
|
||||
* function which has to return the DOM-Node.
|
||||
*
|
||||
* @augments et2_widget
|
||||
*/
|
||||
class et2_DOMWidget extends et2_widget implements et2_IDOMNode
|
||||
{
|
||||
static readonly _attributes : any = {
|
||||
"disabled": {
|
||||
"name": "Disabled",
|
||||
"type": "boolean",
|
||||
"description": "Defines whether this widget is visible. Not to be confused with an input widget's HTML attribute 'disabled'.",
|
||||
"default": false
|
||||
},
|
||||
"width": {
|
||||
"name": "Width",
|
||||
"type": "dimension",
|
||||
"default": et2_no_init,
|
||||
"description": "Width of the element in pixels, percentage or 'auto'"
|
||||
},
|
||||
"height": {
|
||||
"name": "Height",
|
||||
"type": "dimension",
|
||||
"default": et2_no_init,
|
||||
"description": "Height of the element in pixels, percentage or 'auto'"
|
||||
},
|
||||
"class": {
|
||||
"name": "CSS Class",
|
||||
"type": "string",
|
||||
"default": et2_no_init,
|
||||
"description": "CSS Class which is applied to the dom element of this node"
|
||||
},
|
||||
"overflow": {
|
||||
"name": "Overflow",
|
||||
"type": "string",
|
||||
"default": et2_no_init,
|
||||
"description": "If set, the css-overflow attribute is set to that value"
|
||||
},
|
||||
"parent_node": {
|
||||
"name": "DOM parent",
|
||||
"type": "string",
|
||||
"default": et2_no_init,
|
||||
"description": "Insert into the target DOM node instead of the normal location"
|
||||
},
|
||||
"actions": {
|
||||
"name": "Actions list",
|
||||
"type": "any",
|
||||
"default": et2_no_init,
|
||||
"description": "List of egw actions that can be done on the widget. This includes context menu, drag and drop. TODO: Link to action documentation"
|
||||
},
|
||||
default_execute: {
|
||||
name: "Default onExecute for actions",
|
||||
type: "js",
|
||||
default: et2_no_init,
|
||||
description: "Set default onExecute javascript method for action not specifying their own"
|
||||
},
|
||||
resize_ratio: {
|
||||
name: "Resize height of the widget on callback resize",
|
||||
type:"string",
|
||||
default: '',
|
||||
description: "Allow Resize height of the widget based on exess height and given ratio"
|
||||
},
|
||||
data: {
|
||||
name: "comma-separated name:value pairs set as data attributes on DOM node",
|
||||
type: "string",
|
||||
default: '',
|
||||
description: 'data="mime:${row}[mime]" would generate data-mime="..." in DOM, eg. to use it in CSS on a parent'
|
||||
},
|
||||
background: {
|
||||
name: "Add background image",
|
||||
type: "string",
|
||||
default:'',
|
||||
description: "Sets background image, left, right and scale on DOM",
|
||||
}
|
||||
}
|
||||
|
||||
parentNode : HTMLElement;
|
||||
disabled : boolean;
|
||||
private _attachSet: object;
|
||||
private _actionManager: any;
|
||||
|
||||
/**
|
||||
* When the DOMWidget is initialized, it grabs the DOM-Node of the parent
|
||||
* object (if available) and passes it to its own "createDOMNode" function
|
||||
*
|
||||
* @memberOf et2_DOMWidget
|
||||
*/
|
||||
constructor(_parent, _attrs? : WidgetConfig, _child? : object)
|
||||
{
|
||||
// Call the inherited constructor
|
||||
super(_parent, _attrs, ClassWithAttributes.extendAttributes(et2_DOMWidget._attributes, _child || {}));
|
||||
|
||||
this.parentNode = null;
|
||||
|
||||
this._attachSet = {
|
||||
"node": null,
|
||||
"parent": null
|
||||
};
|
||||
|
||||
this.disabled = false;
|
||||
this._surroundingsMgr = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detatches the node from the DOM and clears all references to the parent
|
||||
* node or the dom node of this widget.
|
||||
*/
|
||||
destroy()
|
||||
{
|
||||
this.detachFromDOM();
|
||||
this.parentNode = null;
|
||||
this._attachSet = {};
|
||||
|
||||
if(this._actionManager)
|
||||
{
|
||||
var app_om = egw_getObjectManager(this.egw().getAppName(), false,1);
|
||||
if(app_om)
|
||||
{
|
||||
var om = app_om.getObjectById(this.id);
|
||||
if(om) om.remove();
|
||||
}
|
||||
this._actionManager.remove();
|
||||
this._actionManager = null;
|
||||
}
|
||||
|
||||
if (this._surroundingsMgr)
|
||||
{
|
||||
this._surroundingsMgr.free();
|
||||
this._surroundingsMgr = null;
|
||||
}
|
||||
|
||||
this._super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches the container node of this widget to the DOM-Tree
|
||||
*/
|
||||
doLoadingFinished()
|
||||
{
|
||||
// Check whether the parent implements the et2_IDOMNode interface. If
|
||||
// yes, grab the DOM node and create our own.
|
||||
if (this.getParent() && this.getParent().implements(et2_IDOMNode)) {
|
||||
if(this.options.parent_node)
|
||||
{
|
||||
this.set_parent_node(this.options.parent_node);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.setParentDOMNode(this.getParent().getDOMNode(this));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detaches the widget from the DOM tree, if it had been attached to the
|
||||
* DOM-Tree using the attachToDOM method.
|
||||
*/
|
||||
detachFromDOM() {
|
||||
|
||||
if (this._attachSet.node && this._attachSet.parent)
|
||||
{
|
||||
// Remove the current node from the parent node
|
||||
try {
|
||||
this._attachSet.parent.removeChild(this._attachSet.node);
|
||||
} catch (e) {
|
||||
// Don't throw a DOM error if the node wasn't in the parent
|
||||
}
|
||||
|
||||
// Reset the "attachSet"
|
||||
this._attachSet = {
|
||||
"node": null,
|
||||
"parent": null
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches the widget to the DOM tree. Fails if the widget is already
|
||||
* attached to the tree or no parent node or no node for this widget is
|
||||
* defined.
|
||||
*/
|
||||
attachToDOM() {
|
||||
// Attach the DOM node of this widget (if existing) to the new parent
|
||||
var node = this.getDOMNode(this);
|
||||
if (node && this.parentNode &&
|
||||
(node != this._attachSet.node ||
|
||||
this.parentNode != this._attachSet.parent))
|
||||
{
|
||||
// If the surroundings manager exists, surround the DOM-Node of this
|
||||
// widget with the DOM-Nodes inside the surroundings manager.
|
||||
if (this._surroundingsMgr)
|
||||
{
|
||||
node = this._surroundingsMgr.getDOMNode(node);
|
||||
}
|
||||
|
||||
// Append this node at its index
|
||||
var idx = this.getDOMIndex();
|
||||
if (idx < 0 || idx >= this.parentNode.childNodes.length - 1)
|
||||
{
|
||||
this.parentNode.appendChild(node);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.parentNode.insertBefore(node, this.parentNode.childNodes[idx]);
|
||||
}
|
||||
|
||||
// Store the currently attached nodes
|
||||
this._attachSet = {
|
||||
"node": node,
|
||||
"parent": this.parentNode
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
isAttached() {
|
||||
return this.parentNode != null;
|
||||
}
|
||||
|
||||
private _surroundingsMgr : et2_surroundingsMgr;
|
||||
|
||||
getSurroundings() {
|
||||
if (!this._surroundingsMgr)
|
||||
{
|
||||
this._surroundingsMgr = new et2_surroundingsMgr(this);
|
||||
}
|
||||
|
||||
return this._surroundingsMgr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get data for the tab this widget is on.
|
||||
*
|
||||
* Will return null if the widget is not on a tab or tab data containing
|
||||
* - id
|
||||
* - label
|
||||
* - widget (top level widget)
|
||||
* - contentDiv (jQuery object for the div the tab content is in)
|
||||
*
|
||||
* @returns {Object|null} Data for tab the widget is on
|
||||
*/
|
||||
get_tab_info() {
|
||||
var parent = this;
|
||||
do {
|
||||
parent = parent._parent;
|
||||
} while (parent !== this.getRoot() && parent._type !== 'tabbox');
|
||||
|
||||
// No tab
|
||||
if(parent === this.getRoot())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Find the tab index
|
||||
for(var i = 0; i < parent.tabData.length; i++)
|
||||
{
|
||||
// Find the tab by DOM heritage
|
||||
if(parent.tabData[i].contentDiv.has(this.div).length)
|
||||
{
|
||||
return parent.tabData[i];
|
||||
}
|
||||
}
|
||||
// On a tab, but we couldn't find it by DOM nodes Maybe tab template is
|
||||
// not loaded yet. Try checking IDs.
|
||||
var template = this;
|
||||
do {
|
||||
template = template._parent;
|
||||
} while (template !== parent && template._type !== 'template');
|
||||
for(var i = parent.tabData.length - 1; i >= 0; i--)
|
||||
{
|
||||
if(template && template.id && template.id === parent.tabData[i].id)
|
||||
{
|
||||
return parent.tabData[i];
|
||||
}
|
||||
}
|
||||
// Fallback
|
||||
return this.getParent().get_tab_info();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the parent DOM node of this element. Takes a wider variety of types
|
||||
* than setParentDOMNode(), and matches the set_<attribute> naming convention.
|
||||
*
|
||||
* @param _node String|DOMNode DOM node to contain the widget, or the ID of the DOM node.
|
||||
*/
|
||||
set_parent_node(_node) {
|
||||
if(typeof _node == "string")
|
||||
{
|
||||
var parent = jQuery('#'+_node);
|
||||
if(parent.length === 0 && window.parent)
|
||||
{
|
||||
// Could not find it, try again with wider context
|
||||
// (in case there's an iframe in admin, for example)
|
||||
parent = jQuery('#'+_node, window.parent.document);
|
||||
}
|
||||
if(parent.length === 0)
|
||||
{
|
||||
this.egw().debug('warn','Unable to find DOM parent node with ID "%s" for widget %o.',_node,this);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.setParentDOMNode(parent.get(0));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.setParentDOMNode(_node);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the parent DOM node of this element. If another parent node is already
|
||||
* set, this widget removes itself from the DOM tree
|
||||
*
|
||||
* @param _node
|
||||
*/
|
||||
setParentDOMNode(_node) {
|
||||
if (_node != this.parentNode)
|
||||
{
|
||||
// Detatch this element from the DOM tree
|
||||
this.detachFromDOM();
|
||||
|
||||
this.parentNode = _node;
|
||||
|
||||
// And attatch the element to the DOM tree
|
||||
this.attachToDOM();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the parent node.
|
||||
*/
|
||||
getParentDOMNode() {
|
||||
return this.parentNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index of this element in the DOM tree
|
||||
*/
|
||||
getDOMIndex() {
|
||||
if (this.getParent())
|
||||
{
|
||||
var idx = 0;
|
||||
var children = this.getParent().getChildren();
|
||||
|
||||
if(children && children.indexOf) return children.indexOf(this);
|
||||
|
||||
egw.debug('warn', 'No Array.indexOf(), falling back to looping. ');
|
||||
for (var i = 0; i < children.length; i++)
|
||||
{
|
||||
if (children[i] == this)
|
||||
{
|
||||
return idx;
|
||||
}
|
||||
else if (children[i].isInTree())
|
||||
{
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the id of the DOM-Node.
|
||||
*
|
||||
* DOM id's have dots "." replaced with dashes "-"
|
||||
*
|
||||
* @param {string} _value id to set
|
||||
*/
|
||||
set_id(_value) {
|
||||
|
||||
this.id = _value;
|
||||
this.dom_id = _value ? this.getInstanceManager().uniqueId+'_'+_value.replace(/\./g, '-') : _value;
|
||||
|
||||
var node = this.getDOMNode(this);
|
||||
if (node)
|
||||
{
|
||||
if (_value != "")
|
||||
{
|
||||
node.setAttribute("id", this.dom_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
node.removeAttribute("id");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
set_disabled(_value) {
|
||||
var node = this._surroundingsMgr != null ? this._surroundingsMgr.getDOMNode(this.getDOMNode(this)) : this.getDOMNode(this);
|
||||
if (node && this.disabled != _value)
|
||||
{
|
||||
this.disabled = _value;
|
||||
|
||||
if (_value)
|
||||
{
|
||||
jQuery(node).hide();
|
||||
}
|
||||
else
|
||||
{
|
||||
jQuery(node).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
set_width(_value) {
|
||||
this.width = _value;
|
||||
|
||||
var node = this.getDOMNode(this);
|
||||
if (node)
|
||||
{
|
||||
jQuery(node).css("width", _value);
|
||||
}
|
||||
}
|
||||
|
||||
set_height(_value) {
|
||||
this.height = _value;
|
||||
|
||||
var node = this.getDOMNode(this);
|
||||
if (node)
|
||||
{
|
||||
jQuery(node).css("height", _value);
|
||||
}
|
||||
}
|
||||
|
||||
set_class(_value) {
|
||||
var node = this.getDOMNode(this);
|
||||
if (node)
|
||||
{
|
||||
if (this["class"])
|
||||
{
|
||||
jQuery(node).removeClass(this["class"]);
|
||||
}
|
||||
jQuery(node).addClass(_value);
|
||||
}
|
||||
|
||||
this["class"] = _value;
|
||||
}
|
||||
|
||||
set_overflow(_value) {
|
||||
this.overflow = _value;
|
||||
|
||||
var node = this.getDOMNode(this);
|
||||
if (node)
|
||||
{
|
||||
jQuery(node).css("overflow", _value);
|
||||
}
|
||||
}
|
||||
|
||||
set_data(_value)
|
||||
{
|
||||
var node = this.getDOMNode(this);
|
||||
if (node && _value)
|
||||
{
|
||||
var pairs = _value.split(/,/g);
|
||||
for(var i=0; i < pairs.length; ++i)
|
||||
{
|
||||
var name_value = pairs[i].split(':');
|
||||
jQuery(node).attr('data-'+name_value[0], name_value[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
set_background(_value)
|
||||
{
|
||||
var node = this.getDOMNode(this);
|
||||
var values = '';
|
||||
if (_value && node)
|
||||
{
|
||||
values = _value.split(',');
|
||||
jQuery(node).css({
|
||||
"background-image":'url("'+values[0]+'")',
|
||||
"background-position-x":values[1],
|
||||
"background-position-y":values[2],
|
||||
"background-scale":values[3]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Actions on the widget
|
||||
*
|
||||
* Each action is defined as an object:
|
||||
*
|
||||
* move: {
|
||||
* type: "drop",
|
||||
* acceptedTypes: "mail",
|
||||
* icon: "move",
|
||||
* caption: "Move to"
|
||||
* onExecute: javascript:mail_move"
|
||||
* }
|
||||
*
|
||||
* This will turn the widget into a drop target for "mail" drag types. When "mail" drag types are dropped,
|
||||
* the global function mail_move(egwAction action, egwActionObject sender) will be called. The ID of the
|
||||
* dragged "mail" will be in sender.id, some information about the sender will be in sender.context. The
|
||||
* etemplate2 widget involved can typically be found in action.parent.data.widget, so your handler
|
||||
* can operate in the widget context easily. The location varies depending on your action though. It
|
||||
* might be action.parent.parent.data.widget
|
||||
*
|
||||
* To customise how the actions are handled for a particular widget, override _link_actions(). It handles
|
||||
* the more widget-specific parts.
|
||||
*
|
||||
* @param {object} actions {ID: {attributes..}+} map of egw action information
|
||||
* @see api/src/Etemplate/Widget/Nextmatch.php egw_actions() method
|
||||
*/
|
||||
set_actions(actions)
|
||||
{
|
||||
if(this.id == "" || typeof this.id == "undefined")
|
||||
{
|
||||
this.egw().debug("warn", "Widget should have an ID if you want actions",this);
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize the action manager and add some actions to it
|
||||
// Only look 1 level deep
|
||||
var gam = egw_getActionManager(this.egw().appName,true,1);
|
||||
if(typeof this._actionManager != "object")
|
||||
{
|
||||
if(gam.getActionById(this.getInstanceManager().uniqueId,1) !== null)
|
||||
{
|
||||
gam = gam.getActionById(this.getInstanceManager().uniqueId,1);
|
||||
}
|
||||
if(gam.getActionById(this.id,1) != null)
|
||||
{
|
||||
this._actionManager = gam.getActionById(this.id,1);
|
||||
}
|
||||
else
|
||||
{
|
||||
this._actionManager = gam.addAction("actionManager", this.id);
|
||||
}
|
||||
}
|
||||
this._actionManager.updateActions(actions, this.egw().appName);
|
||||
if (this.options.default_execute) this._actionManager.setDefaultExecute(this.options.default_execute);
|
||||
|
||||
// Put a reference to the widget into the action stuff, so we can
|
||||
// easily get back to widget context from the action handler
|
||||
this._actionManager.data = {widget: this};
|
||||
|
||||
// Link the actions to the DOM
|
||||
this._link_actions(actions);
|
||||
}
|
||||
|
||||
set_default_execute(_default_execute)
|
||||
{
|
||||
this.options.default_execute = _default_execute;
|
||||
|
||||
if (this._actionManager) this._actionManager.setDefaultExecute(null, _default_execute);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all action-links / id's of 1.-level actions from a given action object
|
||||
*
|
||||
* This can be overwritten to not allow all actions, by not returning them here.
|
||||
*
|
||||
* @param actions
|
||||
* @returns {Array}
|
||||
*/
|
||||
_get_action_links(actions)
|
||||
{
|
||||
var action_links = [];
|
||||
for(var i in actions)
|
||||
{
|
||||
var action = actions[i];
|
||||
action_links.push(typeof action.id != 'undefined' ? action.id : i);
|
||||
}
|
||||
return action_links;
|
||||
}
|
||||
|
||||
/**
|
||||
* Link the actions to the DOM nodes / widget bits.
|
||||
*
|
||||
* @param {object} actions {ID: {attributes..}+} map of egw action information
|
||||
*/
|
||||
_link_actions(actions)
|
||||
{
|
||||
// Get the top level element for the tree
|
||||
var objectManager = egw_getAppObjectManager(true);
|
||||
var widget_object = objectManager.getObjectById(this.id);
|
||||
if (widget_object == null) {
|
||||
// Add a new container to the object manager which will hold the widget
|
||||
// objects
|
||||
widget_object = objectManager.insertObject(false, new egwActionObject(
|
||||
this.id, objectManager, new et2_action_object_impl(this),
|
||||
this._actionManager || objectManager.manager.getActionById(this.id) || objectManager.manager
|
||||
));
|
||||
}
|
||||
else
|
||||
{
|
||||
widget_object.setAOI(new et2_action_object_impl(this, this.getDOMNode()));
|
||||
}
|
||||
|
||||
// Delete all old objects
|
||||
widget_object.clear();
|
||||
widget_object.unregisterActions();
|
||||
|
||||
// Go over the widget & add links - this is where we decide which actions are
|
||||
// 'allowed' for this widget at this time
|
||||
var action_links = this._get_action_links(actions);
|
||||
widget_object.updateActionLinks(action_links);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The surroundings manager class allows to append or prepend elements around
|
||||
* an widget node.
|
||||
*
|
||||
* @augments Class
|
||||
*/
|
||||
class et2_surroundingsMgr extends ClassWithAttributes
|
||||
{
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @memberOf et2_surroundingsMgr
|
||||
* @param _widget
|
||||
*/
|
||||
init(_widget) {
|
||||
this.widget = _widget;
|
||||
|
||||
this._widgetContainer = null;
|
||||
this._widgetSurroundings = [];
|
||||
this._widgetPlaceholder = null;
|
||||
this._widgetNode = null;
|
||||
this._ownPlaceholder = true;
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this._widgetContainer = null;
|
||||
this._widgetSurroundings = null;
|
||||
this._widgetPlaceholder = null;
|
||||
this._widgetNode = null;
|
||||
}
|
||||
|
||||
prependDOMNode(_node) {
|
||||
this._widgetSurroundings.unshift(_node);
|
||||
this._surroundingsUpdated = true;
|
||||
}
|
||||
|
||||
appendDOMNode(_node) {
|
||||
// Append an placeholder first if none is existing yet
|
||||
if (this._ownPlaceholder && this._widgetPlaceholder == null)
|
||||
{
|
||||
this._widgetPlaceholder = document.createElement("span");
|
||||
this._widgetSurroundings.push(this._widgetPlaceholder);
|
||||
}
|
||||
|
||||
// Append the given node
|
||||
this._widgetSurroundings.push(_node);
|
||||
this._surroundingsUpdated = true;
|
||||
}
|
||||
|
||||
insertDOMNode(_node) {
|
||||
if (!this._ownPlaceholder || this._widgetPlaceholder == null)
|
||||
{
|
||||
this.appendDOMNode(_node);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the index of the widget placeholder and delete it, insert the
|
||||
// given node instead
|
||||
var idx = this._widgetSurroundings.indexOf(this._widgetPlaceholder);
|
||||
this._widgetSurroundings.splice(idx, 1, _node);
|
||||
|
||||
// Delete the reference to the own placeholder
|
||||
this._widgetPlaceholder = null;
|
||||
this._ownPlaceholder = false;
|
||||
}
|
||||
|
||||
removeDOMNode(_node) {
|
||||
for (var i = 0; this._widgetSurroundings && i < this._widgetSurroundings.length; i++)
|
||||
{
|
||||
if (this._widgetSurroundings[i] == _node)
|
||||
{
|
||||
this._widgetSurroundings.splice(i, 1);
|
||||
this._surroundingsUpdated = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setWidgetPlaceholder(_node) {
|
||||
if (_node != this._widgetPlaceholder)
|
||||
{
|
||||
if (_node != null && this._ownPlaceholder && this._widgetPlaceholder != null)
|
||||
{
|
||||
// Delete the current placeholder which was created by the
|
||||
// widget itself
|
||||
var idx = this._widgetSurroundings.indexOf(this._widgetPlaceholder);
|
||||
this._widgetSurroundings.splice(idx, 1);
|
||||
|
||||
// Delete any reference to the own placeholder and set the
|
||||
// _ownPlaceholder flag to false
|
||||
this._widgetPlaceholder = null;
|
||||
this._ownPlaceholder = false;
|
||||
}
|
||||
|
||||
this._ownPlaceholder = (_node == null);
|
||||
this._widgetPlaceholder = _node;
|
||||
this._surroundingsUpdated = true;
|
||||
}
|
||||
}
|
||||
|
||||
_rebuildContainer() {
|
||||
// Return if there has been no change in the "surroundings-data"
|
||||
if (!this._surroundingsUpdated)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Build the widget container
|
||||
if (this._widgetSurroundings.length > 0)
|
||||
{
|
||||
// Check whether the widgetPlaceholder is really inside the DOM-Tree
|
||||
var hasPlaceholder = et2_hasChild(this._widgetSurroundings,
|
||||
this._widgetPlaceholder);
|
||||
|
||||
// If not, append another widget placeholder
|
||||
if (!hasPlaceholder)
|
||||
{
|
||||
this._widgetPlaceholder = document.createElement("span");
|
||||
this._widgetSurroundings.push(this._widgetPlaceholder);
|
||||
|
||||
this._ownPlaceholder = true;
|
||||
}
|
||||
|
||||
// If the surroundings array only contains one element, set this one
|
||||
// as the widget container
|
||||
if (this._widgetSurroundings.length == 1)
|
||||
{
|
||||
if (this._widgetSurroundings[0] == this._widgetPlaceholder)
|
||||
{
|
||||
this._widgetContainer = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
this._widgetContainer = this._widgetSurroundings[0];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create an outer "span" as widgetContainer
|
||||
this._widgetContainer = document.createElement("span");
|
||||
|
||||
// Append the children inside the widgetSurroundings array to
|
||||
// the widget container
|
||||
for (var i = 0; i < this._widgetSurroundings.length; i++)
|
||||
{
|
||||
this._widgetContainer.appendChild(this._widgetSurroundings[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this._widgetContainer = null;
|
||||
this._widgetPlaceholder = null;
|
||||
}
|
||||
|
||||
this._surroundingsUpdated = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
update() {
|
||||
if (this._surroundingsUpdated)
|
||||
{
|
||||
var attached = this.widget ? this.widget.isAttached() : false;
|
||||
|
||||
// Reattach the widget - this will call the "getDOMNode" function
|
||||
// and trigger the _rebuildContainer function.
|
||||
if (attached && this.widget)
|
||||
{
|
||||
this.widget.detachFromDOM();
|
||||
this.widget.attachToDOM();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getDOMNode(_widgetNode) {
|
||||
// Update the whole widgetContainer if this is not the first time this
|
||||
// function has been called but the widget node has changed.
|
||||
if (this._widgetNode != null && this._widgetNode != _widgetNode)
|
||||
{
|
||||
this._surroundingsUpdated = true;
|
||||
}
|
||||
|
||||
// Copy a reference to the given node
|
||||
this._widgetNode = _widgetNode;
|
||||
|
||||
// Build the container if it didn't exist yet.
|
||||
var updated = this._rebuildContainer();
|
||||
|
||||
// Return the widget node itself if there are no surroundings arround
|
||||
// it
|
||||
if (this._widgetContainer == null)
|
||||
{
|
||||
return _widgetNode;
|
||||
}
|
||||
|
||||
// Replace the widgetPlaceholder with the given widget node if the
|
||||
// widgetContainer has been updated
|
||||
if (updated)
|
||||
{
|
||||
this._widgetPlaceholder.parentNode.replaceChild(_widgetNode,
|
||||
this._widgetPlaceholder);
|
||||
if (!this._ownPlaceholder)
|
||||
{
|
||||
this._widgetPlaceholder = _widgetNode;
|
||||
}
|
||||
}
|
||||
|
||||
// Return the widget container
|
||||
return this._widgetContainer;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The egw_action system requires an egwActionObjectInterface Interface implementation
|
||||
* to tie actions to DOM nodes. This one can be used by any widget.
|
||||
*
|
||||
* The class extension is different than the widgets
|
||||
*
|
||||
* @param {et2_DOMWidget} widget
|
||||
* @param {Object} node
|
||||
*
|
||||
*/
|
||||
function et2_action_object_impl(widget, node)
|
||||
{
|
||||
var aoi = new egwActionObjectInterface();
|
||||
var objectNode = node;
|
||||
|
||||
aoi.getWidget = function() {
|
||||
return widget;
|
||||
};
|
||||
|
||||
aoi.doGetDOMNode = function() {
|
||||
return objectNode?objectNode:widget.getDOMNode();
|
||||
};
|
||||
|
||||
// _outerCall may be used to determine, whether the state change has been
|
||||
// evoked from the outside and the stateChangeCallback has to be called
|
||||
// or not.
|
||||
aoi.doSetState = function(_state, _outerCall) {
|
||||
};
|
||||
|
||||
// The doTiggerEvent function may be overritten by the aoi if it wants to
|
||||
// support certain action implementation specific events like EGW_AI_DRAG_OVER
|
||||
// or EGW_AI_DRAG_OUT
|
||||
aoi.doTriggerEvent = function(_event, _data) {
|
||||
switch(_event)
|
||||
{
|
||||
case EGW_AI_DRAG_OVER:
|
||||
jQuery(this.node).addClass("ui-state-active");
|
||||
break;
|
||||
case EGW_AI_DRAG_OUT:
|
||||
jQuery(this.node).removeClass("ui-state-active");
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
return aoi;
|
||||
};
|
File diff suppressed because it is too large
Load Diff
780
api/js/etemplate/et2_core_common.ts
Normal file
780
api/js/etemplate/et2_core_common.ts
Normal file
@ -0,0 +1,780 @@
|
||||
/**
|
||||
* EGroupware eTemplate2 - JS Widget 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
|
||||
* @copyright Stylite 2011
|
||||
*/
|
||||
|
||||
/**
|
||||
* IE Fix for array.indexOf
|
||||
*/
|
||||
if (typeof Array.prototype.indexOf == "undefined")
|
||||
{
|
||||
Array.prototype.indexOf = function(_elem) {
|
||||
for (var i = 0; i < this.length; i++)
|
||||
{
|
||||
if (this[i] === _elem)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Array with all types supported by the et2_checkType function.
|
||||
*/
|
||||
var et2_validTypes = ["boolean", "string", "rawstring", "html", "float", "integer", "any", "js", "dimension"];
|
||||
|
||||
/**
|
||||
* Object whith default values for the above types. Do not specify array or
|
||||
* objects inside the et2_typeDefaults object, as this instance will be shared
|
||||
* between all users of it.
|
||||
*/
|
||||
var et2_typeDefaults : object = {
|
||||
"boolean": false,
|
||||
"string": "",
|
||||
"rawstring": "", // no html-entity decoding
|
||||
"html": "",
|
||||
"js": null,
|
||||
"float": 0.0,
|
||||
"integer": 0,
|
||||
"any": null,
|
||||
"dimension": "auto"
|
||||
};
|
||||
|
||||
function et2_evalBool(_val)
|
||||
{
|
||||
if (typeof _val == "string")
|
||||
{
|
||||
if (_val == "false" || _val == "0")
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return _val ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Concat et2 name together, eg. et2_concat("namespace","test[something]") == "namespace[test][something]"
|
||||
* @param variable number of arguments to contact
|
||||
* @returns string
|
||||
*/
|
||||
function et2_form_name(_cname,_name)
|
||||
{
|
||||
var parts = [];
|
||||
for(var i=0; i < arguments.length; ++i)
|
||||
{
|
||||
var name = arguments[i];
|
||||
if (typeof name == 'string' && name.length > 0) // et2_namespace("","test") === "test" === et2_namespace(null,"test")
|
||||
{
|
||||
parts = parts.concat(name.replace(/]/g,'').split('['));
|
||||
}
|
||||
}
|
||||
var name = parts.shift();
|
||||
return parts.length ? name + '['+parts.join('][')+']' : name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given value is of the given type. Strings are converted
|
||||
* into the corresponding type. The (converted) value is returned. All supported
|
||||
* types are listed in the et2_validTypes array.
|
||||
*
|
||||
* @param mixed _val value
|
||||
* @param string _type a valid type eg. "string" or "js"
|
||||
* @param string _attr attribute name
|
||||
* @param object _widget
|
||||
*/
|
||||
function et2_checkType(_val, _type, _attr, _widget)
|
||||
{
|
||||
if (typeof _attr == "undefined")
|
||||
{
|
||||
_attr = null;
|
||||
}
|
||||
|
||||
function _err() {
|
||||
var res = et2_typeDefaults[_type];
|
||||
|
||||
if(typeof _val != "undefined" && _val)
|
||||
{
|
||||
egw.debug("warn", "Widget %o: '" + _val + "' was not of specified _type '" +
|
||||
_type + (_attr != null ? "' for attribute '" + _attr + "' " : "") +
|
||||
"and is now '" + res + "'",_widget);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// If the type is "any" simply return the value again
|
||||
if (_type == "any")
|
||||
{
|
||||
return _val;
|
||||
}
|
||||
|
||||
// we dont check default-value any further, that also fixes type="js" does NOT accept null,
|
||||
// which happens on expanded values
|
||||
if (_val === et2_typeDefaults[_type])
|
||||
{
|
||||
return _val;
|
||||
}
|
||||
|
||||
// If the type is boolean, check whether the given value is exactly true or
|
||||
// false. Otherwise check whether the value is the string "true" or "false".
|
||||
if (_type == "boolean")
|
||||
{
|
||||
if (_val === true || _val === false)
|
||||
{
|
||||
return _val;
|
||||
}
|
||||
|
||||
if (typeof _val == "string")
|
||||
{
|
||||
var lcv = _val.toLowerCase();
|
||||
if (lcv === "true" || lcv === "false" || lcv === "")
|
||||
{
|
||||
return _val === "true";
|
||||
}
|
||||
if(lcv === "0" || lcv === "1")
|
||||
{
|
||||
return _val === "1";
|
||||
}
|
||||
}
|
||||
else if (typeof _val == "number")
|
||||
{
|
||||
return _val != 0;
|
||||
}
|
||||
|
||||
return _err();
|
||||
}
|
||||
|
||||
// Check whether the given value is of the type "string"
|
||||
if (_type == "string" || _type == "html" || _type == "rawstring")
|
||||
{
|
||||
if (typeof _val == "number") // as php is a bit vague here, silently convert to a string
|
||||
{
|
||||
return _val.toString();
|
||||
}
|
||||
|
||||
if (typeof _val == "string")
|
||||
{
|
||||
return _type == "string" ? html_entity_decode(_val) : _val;
|
||||
}
|
||||
|
||||
// Handle some less common possibilities
|
||||
// Maybe a split on an empty string
|
||||
if(typeof _val == "object" && jQuery.isEmptyObject(_val)) return "";
|
||||
|
||||
return _err();
|
||||
}
|
||||
|
||||
// Check whether the value is already a number, otherwise try to convert it
|
||||
// to one.
|
||||
if (_type == "float")
|
||||
{
|
||||
if (typeof _val == "number")
|
||||
{
|
||||
return _val;
|
||||
}
|
||||
|
||||
if (!isNaN(_val))
|
||||
{
|
||||
return parseFloat(_val);
|
||||
}
|
||||
|
||||
return _err();
|
||||
}
|
||||
|
||||
// Check whether the value is an integer by comparing the result of
|
||||
// parseInt(_val) to the value itself.
|
||||
if (_type == "integer")
|
||||
{
|
||||
if (parseInt(_val) == _val)
|
||||
{
|
||||
return parseInt(_val);
|
||||
}
|
||||
|
||||
return _err();
|
||||
}
|
||||
|
||||
// Parse the given dimension value
|
||||
if (_type == "dimension")
|
||||
{
|
||||
// Case 1: The value is "auto"
|
||||
if (_val == "auto")
|
||||
{
|
||||
return _val;
|
||||
}
|
||||
|
||||
// Case 2: The value is simply a number, attach "px"
|
||||
if (!isNaN(_val))
|
||||
{
|
||||
return parseFloat(_val) + "px";
|
||||
}
|
||||
|
||||
// Case 3: The value is already a valid css pixel value or a percentage
|
||||
if (typeof _val == "string" &&
|
||||
((_val.indexOf("px") == _val.length - 2 && !isNaN(_val.split("px")[0] as any)) ||
|
||||
(_val.indexOf("%") == _val.length - 1 && !isNaN(_val.split("%")[0] as any))))
|
||||
{
|
||||
return _val;
|
||||
}
|
||||
|
||||
return _err();
|
||||
}
|
||||
|
||||
// Javascript
|
||||
if (_type == "js")
|
||||
{
|
||||
if (typeof _val == "function" || typeof _val == "undefined")
|
||||
{
|
||||
return _val;
|
||||
}
|
||||
if (_val) _val = _val.replace(/window\.close\(\)/g, 'egw(window).close()');
|
||||
|
||||
// Check to see if it's a string in app.appname.function format, and wrap it in
|
||||
// a closure to make sure context is preserved
|
||||
if(typeof _val == "string" && _val.substr(0,4) == "app." && app)
|
||||
{
|
||||
var parts = _val.split('.');
|
||||
var func = parts.pop();
|
||||
var parent = window;
|
||||
for(var i=0; i < parts.length && typeof parent[parts[i]] != 'undefined'; ++i)
|
||||
{
|
||||
parent = parent[parts[i]];
|
||||
}
|
||||
if (typeof parent[func] == 'function')
|
||||
{
|
||||
try
|
||||
{
|
||||
return jQuery.proxy(parent[func],parent);
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
egw.debug('error', 'Function', _val);
|
||||
return _err();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!_val || typeof _val == "string")
|
||||
{
|
||||
return _val; // get compiled later in widgets own initAttributes, as widget is not yet initialised
|
||||
}
|
||||
}
|
||||
|
||||
// We should never come here
|
||||
throw("Invalid type identifier '" + _attr + "': '" + _type+"'");
|
||||
}
|
||||
|
||||
/**
|
||||
* If et2_no_init is set as default value, the initAttributes function will not
|
||||
* try to initialize the attribute with the default value.
|
||||
*/
|
||||
const et2_no_init = new Object();
|
||||
|
||||
/**
|
||||
* Validates the given attribute with the given id. The validation checks for
|
||||
* the existance of a human name, a description, a type and a default value.
|
||||
* If the human name defaults to the given id, the description defaults to an
|
||||
* empty string, the type defaults to any and the default to the corresponding
|
||||
* type default.
|
||||
*/
|
||||
function et2_validateAttrib(_id, _attrib)
|
||||
{
|
||||
// Default ignore to false.
|
||||
if (typeof _attrib["ignore"] == "undefined")
|
||||
{
|
||||
_attrib["ignore"] = false;
|
||||
}
|
||||
|
||||
// Break if "ignore" is set to true.
|
||||
if (_attrib.ignore)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof _attrib["name"] == "undefined")
|
||||
{
|
||||
_attrib["name"] = _id;
|
||||
egw.debug("log", "Human name ('name'-Field) for attribute '" +
|
||||
_id + "' has not been supplied, set to '" + _id + "'");
|
||||
}
|
||||
|
||||
if (typeof _attrib["description"] == "undefined")
|
||||
{
|
||||
_attrib["description"] = "";
|
||||
egw.debug("log", "Description for attribute '" +
|
||||
_id + "' has not been supplied");
|
||||
}
|
||||
|
||||
if (typeof _attrib["type"] == "undefined")
|
||||
{
|
||||
_attrib["type"] = "any";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (et2_validTypes.indexOf(_attrib["type"]) < 0)
|
||||
{
|
||||
egw.debug("error", "Invalid type '" + _attrib["type"] + "' for attribute '" + _id +
|
||||
"' supplied. Valid types are ", et2_validTypes);
|
||||
}
|
||||
}
|
||||
|
||||
// Set the defaults
|
||||
if (typeof _attrib["default"] == "undefined")
|
||||
{
|
||||
_attrib["default"] = et2_typeDefaults[_attrib["type"]];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Equivalent to the PHP array_values function
|
||||
*/
|
||||
function et2_arrayValues(_arr)
|
||||
{
|
||||
var result = [];
|
||||
for (var key in _arr)
|
||||
{
|
||||
// @ts-ignore we check key is an integer
|
||||
if (parseInt(key) == key)
|
||||
{
|
||||
result.push(_arr[key]);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Equivalent to the PHP array_keys function
|
||||
*/
|
||||
function et2_arrayKeys(_arr)
|
||||
{
|
||||
var result = [];
|
||||
for (var key in _arr)
|
||||
{
|
||||
result.push(key);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function et2_arrayIntKeys(_arr)
|
||||
{
|
||||
var result = [];
|
||||
for (var key in _arr)
|
||||
{
|
||||
result.push(parseInt(key));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Equivalent to the PHP substr function, partly take from phpjs, licensed under
|
||||
* the GPL.
|
||||
*/
|
||||
function et2_substr (str, start, len) {
|
||||
var end = str.length;
|
||||
|
||||
if (start < 0)
|
||||
{
|
||||
start += end;
|
||||
}
|
||||
end = typeof len === 'undefined' ? end : (len < 0 ? len + end : len + start);
|
||||
|
||||
return start >= str.length || start < 0 || start > end ? "" : str.slice(start, end);
|
||||
}
|
||||
|
||||
/**
|
||||
* Split a $delimiter-separated options string, which can contain parts with
|
||||
* delimiters enclosed in $enclosure. Ported from class.boetemplate.inc.php
|
||||
*
|
||||
* Examples:
|
||||
* - et2_csvSplit('"1,2,3",2,3') === array('1,2,3','2','3')
|
||||
* - et2_csvSplit('1,2,3',2) === array('1','2,3')
|
||||
* - et2_csvSplit('"1,2,3",2,3',2) === array('1,2,3','2,3')
|
||||
* - et2_csvSplit('"a""b,c",d') === array('a"b,c','d') // to escape enclosures double them!
|
||||
*
|
||||
* @param string _str
|
||||
* @param int _num=null in how many parts to split maximal, parts over this
|
||||
* number end up (unseparated) in the last part
|
||||
* @param string _delimiter=','
|
||||
* @param string _enclosure='"'
|
||||
* @return array
|
||||
*/
|
||||
function et2_csvSplit(_str : string, _num? : number, _delimiter? : string, _enclosure? : string)
|
||||
{
|
||||
// Default the parameters
|
||||
if (typeof _str == "undefined" || _str == null)
|
||||
{
|
||||
_str = "";
|
||||
}
|
||||
if (typeof _num == "undefined")
|
||||
{
|
||||
_num = null;
|
||||
}
|
||||
|
||||
if (typeof _delimiter == "undefined")
|
||||
{
|
||||
_delimiter = ",";
|
||||
}
|
||||
|
||||
if (typeof _enclosure == "undefined")
|
||||
{
|
||||
_enclosure = '"';
|
||||
}
|
||||
|
||||
// If the _enclosure string does not occur in the string, simply use the
|
||||
// split function
|
||||
if (_str.indexOf(_enclosure) == -1)
|
||||
{
|
||||
return _num === null ? _str.split(_delimiter) :
|
||||
_str.split(_delimiter, _num);
|
||||
}
|
||||
|
||||
// Split the string at the delimiter and join it again, when a enclosure is
|
||||
// found at the beginning/end of a part
|
||||
var parts = _str.split(_delimiter);
|
||||
for (var n = 0; typeof parts[n] != "undefined"; n++)
|
||||
{
|
||||
var part = parts[n];
|
||||
|
||||
if (part.charAt(0) === _enclosure)
|
||||
{
|
||||
var m = n;
|
||||
while (typeof parts[m + 1] != "undefined" && parts[n].substr(-1) !== _enclosure)
|
||||
{
|
||||
parts[n] += _delimiter + parts[++m];
|
||||
delete(parts[m]);
|
||||
}
|
||||
parts[n] = et2_substr(parts[n].replace(
|
||||
new RegExp(_enclosure + _enclosure, 'g'), _enclosure), 1 , -1);
|
||||
n = m;
|
||||
}
|
||||
}
|
||||
|
||||
// Rebuild the array index
|
||||
parts = et2_arrayValues(parts);
|
||||
|
||||
// Limit the parts to the given number
|
||||
if (_num !== null && _num > 0 && _num < parts.length && parts.length > 0)
|
||||
{
|
||||
parts[_num - 1] = parts.slice(_num - 1, parts.length).join(_delimiter);
|
||||
parts = parts.slice(0, _num);
|
||||
}
|
||||
|
||||
return parts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given string and returns an array marking parts which are URLs
|
||||
*/
|
||||
function et2_activateLinks(_content)
|
||||
{
|
||||
var _match = false;
|
||||
var arr = [];
|
||||
|
||||
function _splitPush(_matches, _proc)
|
||||
{
|
||||
if (_matches)
|
||||
{
|
||||
// We had a match
|
||||
_match = true;
|
||||
|
||||
// Replace "undefined" with ""
|
||||
for (var i = 1; i < _matches.length; i++)
|
||||
{
|
||||
if (typeof _matches[i] == "undefined")
|
||||
{
|
||||
_matches[i] = "";
|
||||
}
|
||||
}
|
||||
|
||||
// Split the content string at the given position(s)
|
||||
// but we only handle the first occurence
|
||||
var splitted = _content.split(_matches[0]);
|
||||
|
||||
// Push the not-matched part
|
||||
var left = splitted.shift();
|
||||
if (left)
|
||||
{
|
||||
// activate the links of the left string
|
||||
arr = arr.concat(et2_activateLinks(left));
|
||||
}
|
||||
|
||||
// Call the callback function which converts the matches into an object
|
||||
// and appends it to the string
|
||||
_proc(_matches);
|
||||
|
||||
// Set the new working string to the right part
|
||||
_content = splitted.join(_matches[0]);
|
||||
}
|
||||
}
|
||||
|
||||
var mail_regExp = /(mailto:)?([a-z0-9._-]+)@([a-z0-9_-]+)\.([a-z0-9._-]+)/i;
|
||||
|
||||
// First match things beginning with http:// (or other protocols)
|
||||
var protocol = '(http:\\/\\/|(ftp:\\/\\/|https:\\/\\/))'; // only http:// gets removed, other protocolls are shown
|
||||
var domain = '([\\w-]+\\.[\\w-.]+)';
|
||||
var subdir = '([\\w\\-\\.,@?^=%&;:\\/~\\+#]*[\\w\\-\\@?^=%&\\/~\\+#])?';
|
||||
var http_regExp = new RegExp(protocol + domain + subdir, 'i');
|
||||
|
||||
// Now match things beginning with www.
|
||||
var domain = 'www(\\.[\\w-.]+)';
|
||||
var subdir = '([\\w\\-\\.,@?^=%&:\\/~\\+#]*[\\w\\-\\@?^=%&\\/~\\+#])?';
|
||||
var www_regExp = new RegExp(domain + subdir, 'i');
|
||||
|
||||
do {
|
||||
_match = false;
|
||||
|
||||
// Abort if the remaining length of _content is smaller than 20 for
|
||||
// performance reasons
|
||||
if (!_content)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// No need make emailaddress spam-save, as it gets dynamically created
|
||||
_splitPush(_content.match(mail_regExp), function(_matches) {
|
||||
arr.push({
|
||||
"href": (_matches[1]?'':'mailto:')+_matches[0],
|
||||
"text": _matches[2] + "@" + _matches[3] + "." + _matches[4]
|
||||
});
|
||||
});
|
||||
|
||||
// Create hrefs for links starting with "http://"
|
||||
_splitPush(_content.match(http_regExp), function(_matches) {
|
||||
arr.push({
|
||||
"href": _matches[0],
|
||||
"text": _matches[2] + _matches[3] + _matches[4]
|
||||
});
|
||||
});
|
||||
|
||||
// Create hrefs for links starting with "www."
|
||||
_splitPush(_content.match(www_regExp), function(_matches) {
|
||||
arr.push({
|
||||
"href": "http://" + _matches[0],
|
||||
"text": _matches[0]
|
||||
});
|
||||
});
|
||||
} while (_match)
|
||||
|
||||
arr.push(_content);
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts the structure generated by et2_activateLinks into the given DOM-Node
|
||||
*/
|
||||
function et2_insertLinkText(_text, _node, _target)
|
||||
{
|
||||
if(!_node)
|
||||
{
|
||||
egw.debug("warn", "et2_insertLinkText called without node", _text, _node, _target);
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear the node
|
||||
for (var i = _node.childNodes.length - 1; i >= 0; i--)
|
||||
{
|
||||
_node.removeChild(_node.childNodes[i]);
|
||||
}
|
||||
|
||||
for (var i = 0; i < _text.length; i++)
|
||||
{
|
||||
var s = _text[i];
|
||||
|
||||
if (typeof s == "string" || typeof s == "number")
|
||||
{
|
||||
// Include line breaks
|
||||
var lines = typeof s !== "number" && s.split ? s.split('\n') : [s+""];
|
||||
|
||||
// Insert the lines
|
||||
for (var j = 0; j < lines.length; j++)
|
||||
{
|
||||
_node.appendChild(document.createTextNode(lines[j]));
|
||||
|
||||
if (j < lines.length - 1)
|
||||
{
|
||||
_node.appendChild(document.createElement("br"));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(s.text) // no need to generate a link, if there is no content in it
|
||||
{
|
||||
if(!s.href)
|
||||
{
|
||||
egw.debug("warn", "et2_activateLinks gave bad data", s, _node, _target);
|
||||
s.href = "";
|
||||
}
|
||||
var a = jQuery(document.createElement("a"))
|
||||
.attr("href", s.href)
|
||||
.text(s.text);
|
||||
|
||||
if (typeof _target != "undefined" && _target && _target != "_self" && s.href.substr(0, 7) != "mailto:")
|
||||
{
|
||||
a.attr("target", _target);
|
||||
}
|
||||
// open mailto links depending on preferences in mail app
|
||||
if (s.href.substr(0, 7) == "mailto:" &&
|
||||
(egw.user('apps').mail || egw.user('apps').felamimail) &&
|
||||
egw.preference('force_mailto','addressbook') != '1')
|
||||
{
|
||||
a.click(function(event){
|
||||
egw.open_link(this.href);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
a.appendTo(_node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a copy of the given object (non recursive)
|
||||
*/
|
||||
function et2_cloneObject(_obj)
|
||||
{
|
||||
var result = {};
|
||||
|
||||
for (var key in _obj)
|
||||
{
|
||||
result[key] = _obj[key];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given array of nodes or their children contains the given
|
||||
* child node.
|
||||
*/
|
||||
function et2_hasChild(_nodes, _child)
|
||||
{
|
||||
for (var i = 0; i < _nodes.length; i++)
|
||||
{
|
||||
if (_nodes[i] == _child)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (_nodes[i].childNodes)
|
||||
{
|
||||
var res = et2_hasChild(_nodes[i].childNodes, _child);
|
||||
|
||||
if (res)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Functions to work with ranges and range intersection (used in the dataview)
|
||||
*/
|
||||
|
||||
/**
|
||||
* Common functions used in most view classes
|
||||
*/
|
||||
|
||||
/**
|
||||
* Returns an "range" object with the given top position and height
|
||||
*/
|
||||
function et2_range(_top, _height)
|
||||
{
|
||||
return {
|
||||
"top": _top,
|
||||
"bottom": _top + _height
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an "area" object with the given top- and bottom position
|
||||
*/
|
||||
function et2_bounds(_top, _bottom)
|
||||
{
|
||||
return {
|
||||
"top": _top,
|
||||
"bottom": _bottom
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether two range objects intersect each other
|
||||
*/
|
||||
function et2_rangeIntersect(_ar1, _ar2)
|
||||
{
|
||||
return ! (_ar1.bottom < _ar2.top || _ar1.top > _ar2.bottom);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether two ranges intersect (result = 0) or their relative position
|
||||
* to each other (used to do a binary search inside a list of sorted range objects).
|
||||
*/
|
||||
function et2_rangeIntersectDir(_ar1, _ar2)
|
||||
{
|
||||
if (_ar1.bottom < _ar2.top)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (_ar1.top > _ar2.bottom)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether two ranges are equal.
|
||||
*/
|
||||
function et2_rangeEqual(_ar1, _ar2)
|
||||
{
|
||||
return _ar1.top === _ar2.top && _ar1.bottom === _ar2.bottom;
|
||||
}
|
||||
|
||||
/**
|
||||
* Substracts _ar2 from _ar1, returns an array of new ranges.
|
||||
*/
|
||||
function et2_rangeSubstract(_ar1, _ar2)
|
||||
{
|
||||
// Per default return the complete _ar1 range
|
||||
var res = [_ar1];
|
||||
|
||||
// Check whether there is an intersection between the given ranges
|
||||
if (et2_rangeIntersect(_ar1, _ar2))
|
||||
{
|
||||
res = [et2_bounds(_ar1.top, _ar2.top),
|
||||
et2_bounds(_ar2.bottom, _ar1.bottom)];
|
||||
}
|
||||
|
||||
// Remove all zero-length ranges from the result
|
||||
for (var i = res.length - 1; i >= 0; i--)
|
||||
{
|
||||
if (res[i].bottom - res[i].top <= 0)
|
||||
{
|
||||
res.splice(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode html entities so they can be added via .text(_str), eg. html_entity_decode('&') === '&'
|
||||
*
|
||||
* @param {string} _str
|
||||
* @returns {string}
|
||||
*/
|
||||
function html_entity_decode(_str)
|
||||
{
|
||||
return _str && _str.indexOf('&') != -1 ? jQuery('<span>'+_str+'</span>').text() : _str;
|
||||
}
|
@ -1,158 +1,163 @@
|
||||
"use strict";
|
||||
/**
|
||||
* EGroupware eTemplate2 - JS code for implementing inheritance with attributes
|
||||
*
|
||||
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
||||
* @package etemplate
|
||||
* @subpackage api
|
||||
* @link http://www.egroupware.org
|
||||
* @link: https://www.egroupware.org
|
||||
* @author Andreas Stöckel
|
||||
* @copyright Stylite 2011
|
||||
* @version $Id$
|
||||
*/
|
||||
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
/*egw:uses
|
||||
et2_core_common;
|
||||
egw_inheritance;
|
||||
et2_core_common;
|
||||
*/
|
||||
|
||||
var ClassWithAttributes = (function(){ "use strict"; return Class.extend(
|
||||
{
|
||||
/**
|
||||
* Returns the value of the given attribute. If the property does not
|
||||
* exist, an error message is issued.
|
||||
*
|
||||
* @param {string} _name
|
||||
* @return {*}
|
||||
*/
|
||||
getAttribute: function(_name) {
|
||||
if (typeof this.attributes[_name] != "undefined" &&
|
||||
!this.attributes[_name].ignore)
|
||||
{
|
||||
if (typeof this["get_" + _name] == "function")
|
||||
{
|
||||
return this["get_" + _name]();
|
||||
}
|
||||
else
|
||||
{
|
||||
return this[_name];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
egw.debug("error", this, "Attribute '" + _name + "' does not exist!");
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* The setAttribute function sets the attribute with the given name to
|
||||
* the given value. _override defines, whether this[_name] will be set,
|
||||
* if this key already exists. _override defaults to true. A warning
|
||||
* is issued if the attribute does not exist.
|
||||
*
|
||||
* @param {string} _name
|
||||
* @param {*} _value
|
||||
* @param {boolean} _override
|
||||
*/
|
||||
setAttribute: function(_name, _value, _override) {
|
||||
if (typeof this.attributes[_name] != "undefined")
|
||||
{
|
||||
if (!this.attributes[_name].ignore)
|
||||
{
|
||||
if (typeof _override == "undefined")
|
||||
{
|
||||
_override = true;
|
||||
}
|
||||
|
||||
var val = et2_checkType(_value, this.attributes[_name].type,
|
||||
_name, this);
|
||||
|
||||
if (typeof this["set_" + _name] == "function")
|
||||
{
|
||||
this["set_" + _name](val);
|
||||
}
|
||||
else if (_override || typeof this[_name] == "undefined")
|
||||
{
|
||||
this[_name] = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
egw.debug("warn", this, "Attribute '" + _name + "' does not exist!");
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* generateAttributeSet sanitizes the given associative array of attributes
|
||||
* (by passing each entry to "et2_checkType" and checking for existance of
|
||||
* the attribute) and adds the default values to the associative array.
|
||||
*
|
||||
* @param {object} _attrs is the associative array containing the attributes.
|
||||
*/
|
||||
generateAttributeSet: function(_attrs) {
|
||||
|
||||
// Sanity check and validation
|
||||
for (var key in _attrs)
|
||||
{
|
||||
if (typeof this.attributes[key] != "undefined")
|
||||
{
|
||||
if (!this.attributes[key].ignore)
|
||||
{
|
||||
_attrs[key] = et2_checkType(_attrs[key], this.attributes[key].type,
|
||||
key, this);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Key does not exist - delete it and issue a warning
|
||||
delete(_attrs[key]);
|
||||
egw.debug("warn", this, "Attribute '" + key +
|
||||
"' does not exist in " + _attrs.type+"!");
|
||||
}
|
||||
}
|
||||
|
||||
// Include default values or already set values for this attribute
|
||||
for (var key in this.attributes)
|
||||
{
|
||||
if (typeof _attrs[key] == "undefined")
|
||||
{
|
||||
var _default = this.attributes[key]["default"];
|
||||
if (_default == et2_no_init)
|
||||
{
|
||||
_default = undefined;
|
||||
}
|
||||
|
||||
_attrs[key] = _default;
|
||||
}
|
||||
}
|
||||
|
||||
return _attrs;
|
||||
},
|
||||
|
||||
/**
|
||||
* The initAttributes function sets the attributes to their default
|
||||
* values. The attributes are not overwritten, which means, that the
|
||||
* default is only set, if either a setter exists or this[propName] does
|
||||
* not exist yet.
|
||||
*
|
||||
* @param {object} _attrs is the associative array containing the attributes.
|
||||
*/
|
||||
initAttributes: function(_attrs) {
|
||||
for (var key in _attrs)
|
||||
{
|
||||
if (typeof this.attributes[key] != "undefined" && !this.attributes[key].ignore && !(_attrs[key] == undefined))
|
||||
{
|
||||
this.setAttribute(key, _attrs[key], false);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_validate_attributes: function(attributes)
|
||||
{
|
||||
// Validate the attributes
|
||||
for (var key in attributes)
|
||||
{
|
||||
et2_validateAttrib(key, attributes[key]);
|
||||
}
|
||||
}
|
||||
});}).call(this);
|
||||
require("../jsapi/egw_global");
|
||||
require("et2_core_common");
|
||||
var ClassWithAttributes = /** @class */ (function () {
|
||||
function ClassWithAttributes() {
|
||||
}
|
||||
/**
|
||||
* Returns the value of the given attribute. If the property does not
|
||||
* exist, an error message is issued.
|
||||
*
|
||||
* @param {string} _name
|
||||
* @return {*}
|
||||
*/
|
||||
ClassWithAttributes.prototype.getAttribute = function (_name) {
|
||||
if (typeof this.attributes[_name] != "undefined" &&
|
||||
!this.attributes[_name].ignore) {
|
||||
if (typeof this["get_" + _name] == "function") {
|
||||
return this["get_" + _name]();
|
||||
}
|
||||
else {
|
||||
return this[_name];
|
||||
}
|
||||
}
|
||||
else {
|
||||
egw.debug("error", this, "Attribute '" + _name + "' does not exist!");
|
||||
}
|
||||
};
|
||||
/**
|
||||
* The setAttribute function sets the attribute with the given name to
|
||||
* the given value. _override defines, whether this[_name] will be set,
|
||||
* if this key already exists. _override defaults to true. A warning
|
||||
* is issued if the attribute does not exist.
|
||||
*
|
||||
* @param {string} _name
|
||||
* @param {*} _value
|
||||
* @param {boolean} _override
|
||||
*/
|
||||
ClassWithAttributes.prototype.setAttribute = function (_name, _value, _override) {
|
||||
if (typeof this.attributes[_name] != "undefined") {
|
||||
if (!this.attributes[_name].ignore) {
|
||||
if (typeof _override == "undefined") {
|
||||
_override = true;
|
||||
}
|
||||
var val = et2_checkType(_value, this.attributes[_name].type, _name, this);
|
||||
if (typeof this["set_" + _name] == "function") {
|
||||
this["set_" + _name](val);
|
||||
}
|
||||
else if (_override || typeof this[_name] == "undefined") {
|
||||
this[_name] = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
egw.debug("warn", this, "Attribute '" + _name + "' does not exist!");
|
||||
}
|
||||
};
|
||||
/**
|
||||
* generateAttributeSet sanitizes the given associative array of attributes
|
||||
* (by passing each entry to "et2_checkType" and checking for existance of
|
||||
* the attribute) and adds the default values to the associative array.
|
||||
*
|
||||
* @param {object} _attrs is the associative array containing the attributes.
|
||||
*/
|
||||
ClassWithAttributes.prototype.generateAttributeSet = function (_attrs) {
|
||||
// Sanity check and validation
|
||||
for (var key in _attrs) {
|
||||
if (typeof this.attributes[key] != "undefined") {
|
||||
if (!this.attributes[key].ignore) {
|
||||
_attrs[key] = et2_checkType(_attrs[key], this.attributes[key].type, key, this);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Key does not exist - delete it and issue a warning
|
||||
delete (_attrs[key]);
|
||||
egw.debug("warn", this, "Attribute '" + key +
|
||||
"' does not exist in " + _attrs.type + "!");
|
||||
}
|
||||
}
|
||||
// Include default values or already set values for this attribute
|
||||
for (var key in this.attributes) {
|
||||
if (typeof _attrs[key] == "undefined") {
|
||||
var _default = this.attributes[key]["default"];
|
||||
if (_default == et2_no_init) {
|
||||
_default = undefined;
|
||||
}
|
||||
_attrs[key] = _default;
|
||||
}
|
||||
}
|
||||
return _attrs;
|
||||
};
|
||||
/**
|
||||
* The initAttributes function sets the attributes to their default
|
||||
* values. The attributes are not overwritten, which means, that the
|
||||
* default is only set, if either a setter exists or this[propName] does
|
||||
* not exist yet.
|
||||
*
|
||||
* @param {object} _attrs is the associative array containing the attributes.
|
||||
*/
|
||||
ClassWithAttributes.prototype.initAttributes = function (_attrs) {
|
||||
for (var key in _attrs) {
|
||||
if (typeof this.attributes[key] != "undefined" && !this.attributes[key].ignore && !(_attrs[key] == undefined)) {
|
||||
this.setAttribute(key, _attrs[key], false);
|
||||
}
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Extend current _attributes with the one from the parent class
|
||||
*
|
||||
* This gives inheritance from the parent plus the ability to override in the current class.
|
||||
*
|
||||
* @param _attributes
|
||||
* @param _parent
|
||||
*/
|
||||
ClassWithAttributes.extendAttributes = function (_attributes, _parent) {
|
||||
function _copyMerge(_new, _old) {
|
||||
var result = {};
|
||||
// Copy the new object
|
||||
if (typeof _new != "undefined") {
|
||||
for (var key in _new) {
|
||||
result[key] = _new[key];
|
||||
}
|
||||
}
|
||||
// Merge the old object
|
||||
for (var key in _old) {
|
||||
if (typeof result[key] == "undefined") {
|
||||
result[key] = _old[key];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
var attributes = {};
|
||||
// Copy the old attributes
|
||||
for (var key in _attributes) {
|
||||
attributes[key] = _copyMerge({}, _attributes[key]);
|
||||
}
|
||||
// Add the old attributes to the new ones. If the attributes already
|
||||
// exist, they are merged.
|
||||
for (var key in _parent) {
|
||||
var _old = _parent[key];
|
||||
attributes[key] = _copyMerge(attributes[key], _old);
|
||||
}
|
||||
// Validate the attributes
|
||||
for (var key in attributes) {
|
||||
et2_validateAttrib(key, attributes[key]);
|
||||
}
|
||||
return attributes;
|
||||
};
|
||||
return ClassWithAttributes;
|
||||
}());
|
||||
exports.ClassWithAttributes = ClassWithAttributes;
|
||||
|
217
api/js/etemplate/et2_core_inheritance.ts
Normal file
217
api/js/etemplate/et2_core_inheritance.ts
Normal file
@ -0,0 +1,217 @@
|
||||
/**
|
||||
* EGroupware eTemplate2 - JS code for implementing inheritance with attributes
|
||||
*
|
||||
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
||||
* @package etemplate
|
||||
* @subpackage api
|
||||
* @link: https://www.egroupware.org
|
||||
* @author Andreas Stöckel
|
||||
*/
|
||||
|
||||
/*egw:uses
|
||||
et2_core_common;
|
||||
*/
|
||||
|
||||
import '../jsapi/egw_global';
|
||||
import 'et2_core_common';
|
||||
|
||||
export class ClassWithAttributes
|
||||
{
|
||||
/**
|
||||
* Object to collect the attributes we operate on
|
||||
*/
|
||||
attributes: object;
|
||||
|
||||
/**
|
||||
* Returns the value of the given attribute. If the property does not
|
||||
* exist, an error message is issued.
|
||||
*
|
||||
* @param {string} _name
|
||||
* @return {*}
|
||||
*/
|
||||
getAttribute(_name) {
|
||||
if (typeof this.attributes[_name] != "undefined" &&
|
||||
!this.attributes[_name].ignore)
|
||||
{
|
||||
if (typeof this["get_" + _name] == "function")
|
||||
{
|
||||
return this["get_" + _name]();
|
||||
}
|
||||
else
|
||||
{
|
||||
return this[_name];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
egw.debug("error", this, "Attribute '" + _name + "' does not exist!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The setAttribute function sets the attribute with the given name to
|
||||
* the given value. _override defines, whether this[_name] will be set,
|
||||
* if this key already exists. _override defaults to true. A warning
|
||||
* is issued if the attribute does not exist.
|
||||
*
|
||||
* @param {string} _name
|
||||
* @param {*} _value
|
||||
* @param {boolean} _override
|
||||
*/
|
||||
setAttribute(_name, _value, _override)
|
||||
{
|
||||
if (typeof this.attributes[_name] != "undefined")
|
||||
{
|
||||
if (!this.attributes[_name].ignore)
|
||||
{
|
||||
if (typeof _override == "undefined")
|
||||
{
|
||||
_override = true;
|
||||
}
|
||||
|
||||
var val = et2_checkType(_value, this.attributes[_name].type,
|
||||
_name, this);
|
||||
|
||||
if (typeof this["set_" + _name] == "function")
|
||||
{
|
||||
this["set_" + _name](val);
|
||||
}
|
||||
else if (_override || typeof this[_name] == "undefined")
|
||||
{
|
||||
this[_name] = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
egw.debug("warn", this, "Attribute '" + _name + "' does not exist!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* generateAttributeSet sanitizes the given associative array of attributes
|
||||
* (by passing each entry to "et2_checkType" and checking for existance of
|
||||
* the attribute) and adds the default values to the associative array.
|
||||
*
|
||||
* @param {object} _attrs is the associative array containing the attributes.
|
||||
*/
|
||||
generateAttributeSet(_attrs)
|
||||
{
|
||||
// Sanity check and validation
|
||||
for (var key in _attrs)
|
||||
{
|
||||
if (typeof this.attributes[key] != "undefined")
|
||||
{
|
||||
if (!this.attributes[key].ignore)
|
||||
{
|
||||
_attrs[key] = et2_checkType(_attrs[key], this.attributes[key].type,
|
||||
key, this);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Key does not exist - delete it and issue a warning
|
||||
delete(_attrs[key]);
|
||||
egw.debug("warn", this, "Attribute '" + key +
|
||||
"' does not exist in " + _attrs.type+"!");
|
||||
}
|
||||
}
|
||||
|
||||
// Include default values or already set values for this attribute
|
||||
for (var key in this.attributes)
|
||||
{
|
||||
if (typeof _attrs[key] == "undefined")
|
||||
{
|
||||
var _default = this.attributes[key]["default"];
|
||||
if (_default == et2_no_init)
|
||||
{
|
||||
_default = undefined;
|
||||
}
|
||||
|
||||
_attrs[key] = _default;
|
||||
}
|
||||
}
|
||||
|
||||
return _attrs;
|
||||
}
|
||||
|
||||
/**
|
||||
* The initAttributes function sets the attributes to their default
|
||||
* values. The attributes are not overwritten, which means, that the
|
||||
* default is only set, if either a setter exists or this[propName] does
|
||||
* not exist yet.
|
||||
*
|
||||
* @param {object} _attrs is the associative array containing the attributes.
|
||||
*/
|
||||
initAttributes(_attrs)
|
||||
{
|
||||
for (var key in _attrs)
|
||||
{
|
||||
if (typeof this.attributes[key] != "undefined" && !this.attributes[key].ignore && !(_attrs[key] == undefined))
|
||||
{
|
||||
this.setAttribute(key, _attrs[key], false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extend current _attributes with the one from the parent class
|
||||
*
|
||||
* This gives inheritance from the parent plus the ability to override in the current class.
|
||||
*
|
||||
* @param _attributes
|
||||
* @param _parent
|
||||
*/
|
||||
static extendAttributes(_attributes : object, _parent : object) : object
|
||||
{
|
||||
function _copyMerge(_new, _old)
|
||||
{
|
||||
var result = {};
|
||||
|
||||
// Copy the new object
|
||||
if (typeof _new != "undefined")
|
||||
{
|
||||
for (var key in _new)
|
||||
{
|
||||
result[key] = _new[key];
|
||||
}
|
||||
}
|
||||
|
||||
// Merge the old object
|
||||
for (var key in _old)
|
||||
{
|
||||
if (typeof result[key] == "undefined")
|
||||
{
|
||||
result[key] = _old[key];
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
var attributes = {};
|
||||
|
||||
// Copy the old attributes
|
||||
for (var key in _attributes)
|
||||
{
|
||||
attributes[key] = _copyMerge({}, _attributes[key]);
|
||||
}
|
||||
|
||||
// Add the old attributes to the new ones. If the attributes already
|
||||
// exist, they are merged.
|
||||
for (var key in _parent)
|
||||
{
|
||||
var _old = _parent[key];
|
||||
|
||||
attributes[key] = _copyMerge(attributes[key], _old);
|
||||
}
|
||||
|
||||
// Validate the attributes
|
||||
for (var key in attributes)
|
||||
{
|
||||
et2_validateAttrib(key, attributes[key]);
|
||||
}
|
||||
|
||||
return attributes;
|
||||
}
|
||||
}
|
@ -6,156 +6,39 @@
|
||||
* @subpackage api
|
||||
* @link http://www.egroupware.org
|
||||
* @author Andreas Stöckel
|
||||
* @copyright Stylite 2011
|
||||
* @version $Id$
|
||||
*/
|
||||
|
||||
/*egw:uses
|
||||
et2_core_inheritance;
|
||||
*/
|
||||
|
||||
/**
|
||||
* Interface for all widget classes, which are based on a DOM node.
|
||||
* Checks if an object / et2_widget implements given methods
|
||||
*
|
||||
* @param obj
|
||||
* @param methods
|
||||
*/
|
||||
var et2_IDOMNode = new Interface({
|
||||
/**
|
||||
* Returns the DOM-Node of the current widget. The return value has to be
|
||||
* a plain DOM node. If you want to return an jQuery object as you receive
|
||||
* it with
|
||||
*
|
||||
* obj = jQuery(node);
|
||||
*
|
||||
* simply return obj[0];
|
||||
*
|
||||
* @param _sender The _sender parameter defines which widget is asking for
|
||||
* the DOMNode. Depending on that, the widget may return different nodes.
|
||||
* This is used in the grid. Normally the _sender parameter can be omitted
|
||||
* in most implementations of the getDOMNode function.
|
||||
* However, you should always define the _sender parameter when calling
|
||||
* getDOMNode!
|
||||
*/
|
||||
getDOMNode: function(_sender) {}
|
||||
});
|
||||
|
||||
/**
|
||||
* Interface for all widgets which support returning a value
|
||||
*/
|
||||
var et2_IInput = new Interface({
|
||||
/**
|
||||
* getValue has to return the value of the input widget
|
||||
*/
|
||||
getValue: function() {},
|
||||
|
||||
/**
|
||||
* Is dirty returns true if the value of the widget has changed since it
|
||||
* was loaded.
|
||||
*/
|
||||
isDirty: function() {},
|
||||
|
||||
/**
|
||||
* Causes the dirty flag to be reseted.
|
||||
*/
|
||||
resetDirty: function() {},
|
||||
|
||||
/**
|
||||
* Checks the data to see if it is valid, as far as the client side can tell.
|
||||
* Return true if it's not possible to tell on the client side, because the server
|
||||
* will have the chance to validate also.
|
||||
*
|
||||
* The messages array is to be populated with everything wrong with the data,
|
||||
* so don't stop checking after the first problem unless it really makes sense
|
||||
* to ignore other problems.
|
||||
*
|
||||
* @param {String[]} messages List of messages explaining the failure(s).
|
||||
* messages should be fairly short, and already translated.
|
||||
*
|
||||
* @return {boolean} True if the value is valid (enough), false to fail
|
||||
*/
|
||||
isValid: function(messages) {}
|
||||
});
|
||||
|
||||
/**
|
||||
* Interface for widgets which should be automatically resized
|
||||
*/
|
||||
var et2_IResizeable = new Interface({
|
||||
/**
|
||||
* Called whenever the window is resized
|
||||
*/
|
||||
resize: function() {}
|
||||
});
|
||||
|
||||
/**
|
||||
* Interface for widgets which have the align attribute
|
||||
*/
|
||||
var et2_IAligned = new Interface({
|
||||
get_align: function() {}
|
||||
});
|
||||
|
||||
/**
|
||||
* Interface for widgets which want to e.g. perform clientside validation before
|
||||
* the form is submitted.
|
||||
*/
|
||||
var et2_ISubmitListener = new Interface({
|
||||
/**
|
||||
* Called whenever the template gets submitted. Return false if you want to
|
||||
* stop submission.
|
||||
*
|
||||
* @param _values contains the values which will be sent to the server.
|
||||
* Listeners may change these values before they get submitted.
|
||||
*/
|
||||
submit: function(_values) {}
|
||||
});
|
||||
|
||||
/**
|
||||
* Interface all widgets must support which can operate on given DOM-Nodes. This
|
||||
* is used in grid lists, when only a single row gets really stored in the widget
|
||||
* tree and this instance has to work on multiple copies of the DOM-Tree elements.
|
||||
*/
|
||||
var et2_IDetachedDOM = new Interface({
|
||||
|
||||
/**
|
||||
* Creates a list of attributes which can be set when working in the
|
||||
* "detached" mode. The result is stored in the _attrs array which is provided
|
||||
* by the calling code.
|
||||
*
|
||||
* @param {array} _attrs
|
||||
*/
|
||||
getDetachedAttributes: function(_attrs) {},
|
||||
|
||||
/**
|
||||
* Returns an array of DOM nodes. The (relatively) same DOM-Nodes have to be
|
||||
* passed to the "setDetachedAttributes" function in the same order.
|
||||
*/
|
||||
getDetachedNodes: function() {},
|
||||
|
||||
/**
|
||||
* Sets the given associative attribute->value array and applies the
|
||||
* attributes to the given DOM-Node.
|
||||
*
|
||||
* @param _nodes is an array of nodes which have to be in the same order as
|
||||
* the nodes returned by "getDetachedNodes"
|
||||
* @param _values is an associative array which contains a subset of attributes
|
||||
* returned by the "getDetachedAttributes" function and sets them to the
|
||||
* given values.
|
||||
*/
|
||||
setDetachedAttributes: function(_nodes, _values) {}
|
||||
|
||||
});
|
||||
|
||||
/**
|
||||
* Interface for widgets that need to do something special before printing
|
||||
*/
|
||||
var et2_IPrint = new Interface({
|
||||
/**
|
||||
* Set up for printing
|
||||
*
|
||||
* @return {undefined|Deferred} Return a jQuery Deferred object if not done setting up
|
||||
* (waiting for data)
|
||||
*/
|
||||
beforePrint: function() {},
|
||||
|
||||
/**
|
||||
* Reset after printing
|
||||
*/
|
||||
afterPrint: function() {}
|
||||
});
|
||||
function implements_methods(obj, methods) {
|
||||
for (var i = 0; i < methods.length; ++i) {
|
||||
if (typeof obj[methods[i]] !== 'function') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
function implements_et2_IDOMNode(obj) {
|
||||
return implements_methods(obj, ["getDOMNode"]);
|
||||
}
|
||||
function implements_et2_IInput(obj) {
|
||||
return implements_methods(obj, ["getValue", "isDirty", "resetDirty", "isValid"]);
|
||||
}
|
||||
function implements_et2_IResizeable(obj) {
|
||||
return implements_methods(obj, ["resize"]);
|
||||
}
|
||||
function implements_et2_IAligned(obj) {
|
||||
return implements_methods(obj, ["get_align"]);
|
||||
}
|
||||
function implements_et2_ISubmitListener(obj) {
|
||||
return implements_methods(obj, ["submit"]);
|
||||
}
|
||||
function implements_et2_IDetachedDOM(obj) {
|
||||
return implements_methods(obj, ["getDetachedAttributes", "getDetachedNodes", "setDetachedAttributes"]);
|
||||
}
|
||||
function implements_et2_IPrint(obj) {
|
||||
return implements_methods(obj, ["beforePrint", "afterPrint"]);
|
||||
}
|
||||
|
208
api/js/etemplate/et2_core_interfaces.ts
Normal file
208
api/js/etemplate/et2_core_interfaces.ts
Normal file
@ -0,0 +1,208 @@
|
||||
/**
|
||||
* EGroupware eTemplate2 - File which contains all interfaces
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
|
||||
/**
|
||||
* Checks if an object / et2_widget implements given methods
|
||||
*
|
||||
* @param obj
|
||||
* @param methods
|
||||
*/
|
||||
function implements_methods(obj : et2_widget, methods : string[]) : boolean
|
||||
{
|
||||
for(let i=0; i < methods.length; ++i)
|
||||
{
|
||||
if (typeof obj[methods[i]] !== 'function')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for all widget classes, which are based on a DOM node.
|
||||
*/
|
||||
interface et2_IDOMNode
|
||||
{
|
||||
/**
|
||||
* Returns the DOM-Node of the current widget. The return value has to be
|
||||
* a plain DOM node. If you want to return an jQuery object as you receive
|
||||
* it with
|
||||
*
|
||||
* obj = jQuery(node);
|
||||
*
|
||||
* simply return obj[0];
|
||||
*
|
||||
* @param _sender The _sender parameter defines which widget is asking for
|
||||
* the DOMNode. Depending on that, the widget may return different nodes.
|
||||
* This is used in the grid. Normally the _sender parameter can be omitted
|
||||
* in most implementations of the getDOMNode function.
|
||||
* However, you should always define the _sender parameter when calling
|
||||
* getDOMNode!
|
||||
*/
|
||||
getDOMNode(_sender? : et2_widget) : HTMLElement
|
||||
}
|
||||
function implements_et2_IDOMNode(obj : et2_widget)
|
||||
{
|
||||
return implements_methods(obj, ["getDOMNode"]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for all widgets which support returning a value
|
||||
*/
|
||||
interface et2_IInput
|
||||
{
|
||||
/**
|
||||
* getValue has to return the value of the input widget
|
||||
*/
|
||||
getValue() : any
|
||||
|
||||
/**
|
||||
* Is dirty returns true if the value of the widget has changed since it
|
||||
* was loaded.
|
||||
*/
|
||||
isDirty() : boolean
|
||||
|
||||
/**
|
||||
* Causes the dirty flag to be reseted.
|
||||
*/
|
||||
resetDirty() : void
|
||||
|
||||
/**
|
||||
* Checks the data to see if it is valid, as far as the client side can tell.
|
||||
* Return true if it's not possible to tell on the client side, because the server
|
||||
* will have the chance to validate also.
|
||||
*
|
||||
* The messages array is to be populated with everything wrong with the data,
|
||||
* so don't stop checking after the first problem unless it really makes sense
|
||||
* to ignore other problems.
|
||||
*
|
||||
* @param {String[]} messages List of messages explaining the failure(s).
|
||||
* messages should be fairly short, and already translated.
|
||||
*
|
||||
* @return {boolean} True if the value is valid (enough), false to fail
|
||||
*/
|
||||
isValid(messages) : boolean
|
||||
}
|
||||
function implements_et2_IInput(obj : et2_widget)
|
||||
{
|
||||
return implements_methods(obj, ["getValue", "isDirty", "resetDirty", "isValid"]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for widgets which should be automatically resized
|
||||
*/
|
||||
interface et2_IResizeable
|
||||
{
|
||||
/**
|
||||
* Called whenever the window is resized
|
||||
*/
|
||||
resize() : void
|
||||
}
|
||||
function implements_et2_IResizeable(obj : et2_widget)
|
||||
{
|
||||
return implements_methods(obj, ["resize"]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for widgets which have the align attribute
|
||||
*/
|
||||
interface et2_IAligned
|
||||
{
|
||||
get_align() : string
|
||||
}
|
||||
function implements_et2_IAligned(obj : et2_widget)
|
||||
{
|
||||
return implements_methods(obj, ["get_align"]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for widgets which want to e.g. perform clientside validation before
|
||||
* the form is submitted.
|
||||
*/
|
||||
interface et2_ISubmitListener
|
||||
{
|
||||
/**
|
||||
* Called whenever the template gets submitted. Return false if you want to
|
||||
* stop submission.
|
||||
*
|
||||
* @param _values contains the values which will be sent to the server.
|
||||
* Listeners may change these values before they get submitted.
|
||||
*/
|
||||
submit(_values) : void
|
||||
}
|
||||
function implements_et2_ISubmitListener(obj : et2_widget)
|
||||
{
|
||||
return implements_methods(obj, ["submit"]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface all widgets must support which can operate on given DOM-Nodes. This
|
||||
* is used in grid lists, when only a single row gets really stored in the widget
|
||||
* tree and this instance has to work on multiple copies of the DOM-Tree elements.
|
||||
*/
|
||||
interface et2_IDetachedDOM
|
||||
{
|
||||
|
||||
/**
|
||||
* Creates a list of attributes which can be set when working in the
|
||||
* "detached" mode. The result is stored in the _attrs array which is provided
|
||||
* by the calling code.
|
||||
*
|
||||
* @param {array} _attrs
|
||||
*/
|
||||
getDetachedAttributes(_attrs : string[]) : void
|
||||
|
||||
/**
|
||||
* Returns an array of DOM nodes. The (relatively) same DOM-Nodes have to be
|
||||
* passed to the "setDetachedAttributes" function in the same order.
|
||||
*/
|
||||
getDetachedNodes() : HTMLElement[]
|
||||
|
||||
/**
|
||||
* Sets the given associative attribute->value array and applies the
|
||||
* attributes to the given DOM-Node.
|
||||
*
|
||||
* @param _nodes is an array of nodes which have to be in the same order as
|
||||
* the nodes returned by "getDetachedNodes"
|
||||
* @param _values is an associative array which contains a subset of attributes
|
||||
* returned by the "getDetachedAttributes" function and sets them to the
|
||||
* given values.
|
||||
*/
|
||||
setDetachedAttributes(_nodes : HTMLElement[], _values : object) : void
|
||||
|
||||
}
|
||||
function implements_et2_IDetachedDOM(obj : et2_widget)
|
||||
{
|
||||
return implements_methods(obj, ["getDetachedAttributes", "getDetachedNodes", "setDetachedAttributes"]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for widgets that need to do something special before printing
|
||||
*/
|
||||
interface et2_IPrint
|
||||
{
|
||||
/**
|
||||
* Set up for printing
|
||||
*
|
||||
* @return {undefined|Deferred} Return a jQuery Deferred object if not done setting up
|
||||
* (waiting for data)
|
||||
*/
|
||||
beforePrint() : JQueryPromise<any> | void
|
||||
|
||||
/**
|
||||
* Reset after printing
|
||||
*/
|
||||
afterPrint() : void
|
||||
}
|
||||
function implements_et2_IPrint(obj : et2_widget)
|
||||
{
|
||||
return implements_methods(obj, ["beforePrint", "afterPrint"]);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
1066
api/js/etemplate/et2_core_widget.ts
Normal file
1066
api/js/etemplate/et2_core_widget.ts
Normal file
File diff suppressed because it is too large
Load Diff
19
api/js/etemplate/et2_types.d.ts
vendored
19
api/js/etemplate/et2_types.d.ts
vendored
@ -3,18 +3,21 @@ declare module eT2
|
||||
|
||||
}
|
||||
declare var etemplate2 : any;
|
||||
declare var et2_DOMWidget : any;
|
||||
declare class et2_widget{}
|
||||
declare class et2_DOMWidget{}
|
||||
declare class et2_inputWidget{
|
||||
getInputNode() : HTMLElement
|
||||
}
|
||||
declare var et2_surroundingsMgr : any;
|
||||
declare var et2_arrayMgr : any;
|
||||
declare var et2_readonlysArrayMgr : any;
|
||||
declare var et2_baseWidget : any;
|
||||
declare var et2_container : any;
|
||||
declare var et2_placeholder : any;
|
||||
declare var et2_validTypes : any;
|
||||
declare var et2_typeDefaults : any;
|
||||
declare var et2_no_init : any;
|
||||
declare var et2_validTypes : string[];
|
||||
declare var et2_typeDefaults : object;
|
||||
//declare const et2_no_init : object;
|
||||
declare var et2_editableWidget : any;
|
||||
declare var et2_inputWidget : any;
|
||||
declare var et2_IDOMNode : any;
|
||||
declare var et2_IInput : any;
|
||||
declare var et2_IResizeable : any;
|
||||
@ -23,8 +26,7 @@ declare var et2_ISubmitListener : any;
|
||||
declare var et2_IDetachedDOM : any;
|
||||
declare var et2_IPrint : any;
|
||||
declare var et2_valueWidget : any;
|
||||
declare var et2_registry : any;
|
||||
declare var et2_widget : any;
|
||||
declare var et2_registry : {};
|
||||
declare var et2_dataview : any;
|
||||
declare var et2_dataview_controller : any;
|
||||
declare var et2_dataview_selectionManager : any;
|
||||
@ -145,4 +147,5 @@ declare var et2_vfsSelect : any;
|
||||
declare var et2_video : any;
|
||||
declare var et2_IExposable : 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;
|
@ -201,6 +201,28 @@ var AppJS = (function(){ "use strict"; return Class.extend(
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Push method receives push notification about updates to entries from the application
|
||||
*
|
||||
* It can use the extra _data parameter to determine if the client has read access to
|
||||
* the entry - if an update of the list is necessary.
|
||||
*
|
||||
* @param {string} _type either 'update', 'edit', 'delete', 'add' or null
|
||||
* - update: request just modified data from given rows. Sorting is not considered,
|
||||
* so if the sort field is changed, the row will not be moved.
|
||||
* - edit: rows changed, but sorting may be affected. Requires full reload.
|
||||
* - delete: just delete the given rows clientside (no server interaction neccessary)
|
||||
* - add: requires full reload for proper sorting
|
||||
* @param {string} _app application name
|
||||
* @param {(string|number)} _id id of entry to refresh or null
|
||||
* @param {mixed} _data eg. owner or responsible to decide if update is necessary
|
||||
* @returns {undefined}
|
||||
*/
|
||||
push: function(_type, _app, _id, _data)
|
||||
{
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Open an entry.
|
||||
*
|
||||
|
1
api/js/jsapi/egw_global.d.ts
vendored
1
api/js/jsapi/egw_global.d.ts
vendored
@ -1188,6 +1188,7 @@ declare var InstallTrigger : any;
|
||||
declare var app : {classes: any};
|
||||
declare var egw_globalObjectManager : any;
|
||||
declare var framework : any;
|
||||
declare var egw_LAB : any;
|
||||
|
||||
declare var mailvelope : any;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user