Merge branch 'typescript'

This commit is contained in:
Hadi Nategh 2020-03-05 15:00:21 +01:00
commit 4f9f699827
185 changed files with 106557 additions and 55237 deletions

3
.gitignore vendored
View File

@ -6,6 +6,7 @@
*.min.js.map
*.min.css
*.min.css.map
*.js.map
*.sql
*.diff
*.patch
@ -43,6 +44,8 @@
/sambaadmin/
/sitemgr/
/stylite/
/swool/
/test/
/tracker/
/usage/
/vendor/

View File

@ -1223,3 +1223,4 @@ var AdminApp = /** @class */ (function (_super) {
*/
));
app.classes.admin = AdminApp;
//# sourceMappingURL=app.js.map

View File

@ -17,7 +17,7 @@ import 'jqueryui';
import '../jsapi/egw_global';
import '../etemplate/et2_types';
import {EgwApp} from '../../api/js/jsapi/egw_app';
import { EgwApp } from '../../api/js/jsapi/egw_app';
/**
* UI for Admin

View File

@ -145,7 +145,7 @@ declare class egwAction {
previous: string;
close: string;
};
updateActions(_actions: any, _app: string): void;
updateActions(_actions: any, _app?: string): void;
not_disableClass(_action: any, _senders: any, _target: any): boolean;
enableClass(_action: any, _senders: any, _target: any): boolean;
enableId(_action: any, _senders: any, _target: any): any;
@ -250,7 +250,7 @@ declare class egwActionLink {
* @param {number} _flags a set of additional flags being applied to the object,
* defaults to 0
*/
declare function egwActionObject(_id: string, _parent: egwActionObject, _iface: egwActionObjectInterface, _manager: typeof egwActionManager, _flags: number): void;
declare function egwActionObject(_id: string, _parent: egwActionObject, _iface?: egwActionObjectInterface, _manager?: typeof egwActionManager, _flags?: number): void;
declare class egwActionObject {
/**
* The egwActionObject represents an abstract object to which actions may be
@ -268,7 +268,7 @@ declare class egwActionObject {
* @param {number} _flags a set of additional flags being applied to the object,
* defaults to 0
*/
constructor(_id: string, _parent: egwActionObject, _iface: egwActionObjectInterface, _manager: typeof egwActionManager, _flags: number);
constructor(_id: string, _parent: egwActionObject, _iface?: egwActionObjectInterface, _manager?: typeof egwActionManager, _flags?: number);
id: string;
parent: egwActionObject;
children: any[];
@ -284,7 +284,7 @@ declare class egwActionObject {
iface: egwActionObjectInterface;
getObjectById(_id: string, _search_depth?: number): egwActionObject;
addObject(_id: any, _interface: any, _flags?: number): any;
insertObject(_index: number, _id: any, _iface: any, _flags: number): any;
insertObject(_index: number | boolean, _id: any, _iface?: any, _flags?: number): any;
clear(): void;
remove(): void;
getRootObject(): any;
@ -478,7 +478,7 @@ declare class egwEventQueue {
* @param array _acceptedTypes is an array of types which contains the "typeof"
* strings of accepted non-functions in setValue
*/
declare function egwFnct(_context: any, _default: any, _acceptedTypes: any): void;
declare function egwFnct(_context: any, _default: any, _acceptedTypes?: any): void;
declare class egwFnct {
/**
* Class which is used to be able to handle references to JavaScript functions
@ -491,7 +491,7 @@ declare class egwFnct {
* @param array _acceptedTypes is an array of types which contains the "typeof"
* strings of accepted non-functions in setValue
*/
constructor(_context: any, _default: any, _acceptedTypes: any);
constructor(_context: any, _default: any, _acceptedTypes?: any);
context: any;
acceptedTypes: any;
fnct: any;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,930 @@
/**
* 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, WidgetConfig} from "./et2_core_widget";
import '../egw_action/egw_action.js';
import './et2_types';
/**
* 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
*/
export abstract 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 = null;
disabled : boolean = false;
protected _attachSet : any = {
"node": null,
"parent": null
};
protected _actionManager: any;
width: number;
height: number;
dom_id: string;
overflow: string;
/**
* 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._surroundingsMgr = null;
}
abstract getDOMNode(_sender?: et2_widget): HTMLElement
/**
* 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.destroy();
this._surroundingsMgr = null;
}
super.destroy();
}
/**
* Attaches the container node of this widget to the DOM-Tree
*/
doLoadingFinished() : boolean | JQueryPromise<unknown>
{
// 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((<et2_IDOMNode><unknown>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 && 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 &&
(!this._attachSet || this._attachSet && 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() : et2_surroundingsMgr
{
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() : {id: string, label: string, widget: et2_widget, contentDiv: JQuery, flagDiv: JQuery} | null
{
var parent : et2_widget = this;
do {
parent = parent.getParent();
} while (parent !== this.getRoot() && parent.getType() !== 'tabbox');
// No tab
if(parent === this.getRoot())
{
return null;
}
let tabbox : et2_tabbox = <et2_tabbox><unknown>parent;
// Find the tab index
for(var i = 0; i < tabbox.tabData.length; i++)
{
// Find the tab by DOM heritage
// @ts-ignore
if(tabbox.tabData[i].contentDiv.has(this.div).length)
{
return tabbox.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 : et2_widget = this;
do {
template = template.getParent();
// @ts-ignore
} while (template !== tabbox && template.getType() !== 'template');
for (var i = tabbox.tabData.length - 1; i >= 0; i--)
{
if (template && template.id && template.id === tabbox.tabData[i].id)
{
return tabbox.tabData[i];
}
}
// Fallback
let fallback = <et2_DOMWidget><unknown>this.getParent();
if (typeof fallback.get_tab_info === 'function')
{
return fallback.get_tab_info();
}
return null;
}
/**
* 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 : HTMLElement)
{
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() : HTMLElement
{
return this.parentNode;
}
/**
* Returns the index of this element in the DOM tree
*/
getDOMIndex() : number
{
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 = window.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)).getAOI(),
this._actionManager || objectManager.manager.getActionById(this.id) || objectManager.manager
));
}
else
{
widget_object.setAOI((new et2_action_object_impl(this, this.getDOMNode())).getAOI());
}
// 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.
*/
class et2_surroundingsMgr extends ClassWithAttributes
{
widget: et2_DOMWidget;
private _widgetContainer: any = null;
private _widgetSurroundings: any[] = [];
private _widgetPlaceholder: any = null;
private _widgetNode: HTMLElement = null;
private _ownPlaceholder: boolean = true;
private _surroundingsUpdated: boolean = false;
/**
* Constructor
*
* @memberOf et2_surroundingsMgr
* @param _widget
*/
constructor(_widget : et2_DOMWidget)
{
super();
this.widget = _widget;
}
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;
}
}
private _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;
}
getWidgetSurroundings() : HTMLElement []
{
return this._widgetSurroundings;
}
}
/**
* 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
*
*/
export class et2_action_object_impl
{
aoi : egwActionObjectInterface;
constructor(_widget : et2_DOMWidget, _node? : HTMLElement)
{
var widget = _widget;
var objectNode = _node;
this.aoi = new egwActionObjectInterface();
this.aoi.getWidget = function () {
return widget;
};
this.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.
this.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
this.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;
}
};
}
getAOI()
{
return this.aoi;
}
}

View File

@ -1,3 +1,4 @@
"use strict";
/**
* EGroupware eTemplate2 - JS content array manager
*
@ -6,488 +7,415 @@
* @subpackage api
* @link http://www.egroupware.org
* @author Andreas Stöckel
* @copyright Stylite 2011
* @version $Id$
*/
*
/*egw:uses
et2_core_common;
egw_inheritance;
et2_core_phpExpressionCompiler;
et2_core_common;
egw_inheritance;
et2_core_phpExpressionCompiler;
*/
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 });
/**
* @augments Class
* Manage access to various template customisation arrays passed to etemplate->exec().
*
* This manages access to content, modifications and readonlys arrays
*/
var et2_arrayMgr = (function(){ "use strict"; return Class.extend(
{
splitIds: true,
/**
* Constructor
*
* @memberOf et2_arrayMgr
* @param _data
* @param _parentMgr
*/
init: function(_data, _parentMgr) {
if (typeof _parentMgr == "undefined")
{
_parentMgr = null;
}
// Copy the parent manager which is needed to access relative data when
// being in a relative perspective of the manager
this.parentMgr = _parentMgr;
// Hold a reference to the data
if (typeof _data == "undefined" || !_data)
{
egw.debug("log", "No data passed to content array manager. Probably a mismatch between template namespaces and data.");
_data = {};
}
// Expand sub-arrays that have been shmushed together, so further perspectives work
// Shmushed keys look like: ${row}[info_cat]
// Expanded: ${row}: Object{info_cat: ..value}
if (this.splitIds)
{
// For each index, we need a key: {..} sub array
for(var key in _data) {
// Split up indexes
var indexes = key.replace(/&#x5B;/g,"[").split('[');
// Put data in the proper place
if(indexes.length > 1)
{
var value = _data[key];
var target = _data;
for(var i = 0; i < indexes.length; i++) {
indexes[i] = indexes[i].replace(/&#x5D;/g,'').replace(']','');
if(typeof target[indexes[i]] == "undefined" || target[indexes[i]] === null) {
target[indexes[i]] = i == indexes.length-1 ? value : {};
}
target = target[indexes[i]];
}
delete _data[key];
}
}
}
this.data = _data;
// Holds information about the current perspective
this.perspectiveData = {
"owner": null,
"key": null,
"row": null
};
},
/**
* Returns the root content array manager object
*/
getRoot : function() {
if (this.parentMgr != null)
{
return this.parentMgr.getRoot();
}
return this;
},
/* getValueForID : function(_id) {
if (typeof this.data[_id] != "undefined")
{
return this.data[_id];
}
return null;
},*/
/**
* Explodes compound keys (eg IDs) into a list of namespaces
* This uses no internal values, just expands
*
* eg:
* a[b][c] => [a,b,c]
* col_filter[tr_tracker] => [col_filter, tr_tracker]
*
* @param {string} _key
*
* @return {string[]}
*/
explodeKey: function(_key)
{
// Parse the given key by removing the "]"-chars and splitting at "["
var indexes = [_key];
if(typeof _key === "string")
{
_key = _key.replace(/&#x5B;/g,"[").replace(/&#x5D;/g,"]");
indexes = _key.split('[');
}
if (indexes.length > 1)
{
indexes = [indexes.shift(), indexes.join('[')];
indexes[1] = indexes[1].substring(0,indexes[1].length-1);
var children = indexes[1].split('][');
if(children.length)
{
indexes = jQuery.merge([indexes[0]], children);
}
}
return indexes;
},
/**
* Returns the path to this content array manager perspective as an array
* containing the key values
*
* @param _path is used internally, do not supply it manually.
*/
getPath : function(_path) {
if (typeof _path == "undefined")
{
_path = [];
}
if (this.perspectiveData.key != null)
{
// prepend components of this.perspectiveData.key to path, can be more then one eg. "nm[rows]"
_path = this.perspectiveData.key.replace(/]/g, '').split('[').concat(_path);
}
if (this.parentMgr != null)
{
_path = this.parentMgr.getPath(_path);
}
return _path;
},
/**
* Get array entry is the equivalent to the boetemplate get_array function.
* It returns a reference to the (sub) array with the given key. This also works
* for keys using the ETemplate referencing scheme like a[b][c]
*
* @param _key is the string index, may contain sub-indices like a[b]
* @param _referenceInto if true none-existing sub-arrays/-indices get created
* to be returned as reference, else false is returned. Defaults to false
* @param _skipEmpty returns null if _key is not present in this content array.
* Defaults to false.
*/
getEntry : function(_key, _referenceInto, _skipEmpty) {
if (typeof _referenceInto == "undefined")
{
_referenceInto = false;
}
if (typeof _skipEmpty == "undefined")
{
_skipEmpty = false;
}
// Parse the given key by removing the "]"-chars and splitting at "["
var indexes = this.explodeKey(_key);
var entry = this.data;
for (var i = 0; i < indexes.length; i++)
{
// Abort if the current entry is not an object (associative array) and
// we should descend further into it.
var isObject = typeof entry === 'object';
if (!isObject && !_referenceInto || entry == null || jQuery.isEmptyObject(entry))
{
return null;
}
// Check whether the entry actually exists
var idx = indexes[i];
if (_skipEmpty && (!isObject || typeof entry[idx] == "undefined"))
{
return null;
}
entry = entry[idx];
}
return entry;
},
compiledExpressions: {},
/**
* Equivaltent to the boetemplate::expand_name function.
*
* Expands variables inside the given identifier to their values inside the
* content array.
*
* @param {string} _ident Key used to reference into managed array
* @return {*}
*/
expandName : function(_ident) {
// Check whether the identifier refers to an index in the content array
var is_index_in_content = _ident.charAt(0) == '@';
// Check whether "$" occurs in the given identifier
var pos_var = _ident.indexOf('$');
if (pos_var >= 0 && (this.perspectiveData.row != null || !_ident.match(/\$\{?row\}?/)))
{
// Get the content array for the current row
var row = this.perspectiveData.row;
var row_cont = this.data[row] || {};
// $cont is NOT root but current name-space in old eTemplate
var cont = this.data;//getRoot().data;
var _cont = this.data;// according to a grep only used in ImportExport just twice
// Check whether the expression has already been compiled - if not,
// try to compile it first. If an error occurs, the identifier
// function is set to null
var proto = this.constructor.prototype;
if (typeof proto.compiledExpressions[_ident] == "undefined")
{
try
{
if(this.perspectiveData.row == null)
{
// No row, compile for only top level content
proto.compiledExpressions[_ident] = et2_compilePHPExpression(
_ident, ["cont","_cont"]);
}
else
{
proto.compiledExpressions[_ident] = et2_compilePHPExpression(
_ident, ["row", "cont", "row_cont", "_cont"]);
}
}
catch(e)
{
proto.compiledExpressions[_ident] = null;
egw.debug("error", "Error while compiling PHP->JS ", e);
}
}
// Execute the previously compiled expression, if it is not "null"
// because compilation failed. The parameters have to be in the same
// order as defined during compilation.
if (proto.compiledExpressions[_ident])
{
try
{
if(this.perspectiveData.row == null)
{
// No row, exec with only top level content
_ident = proto.compiledExpressions[_ident](cont,_cont);
}
else
{
_ident = proto.compiledExpressions[_ident](row, cont, row_cont,_cont);
}
}
catch(e)
{
// only log error, as they are no real errors but missing data
egw.debug("log", typeof e == 'object' ? e.message : e);
_ident = null;
}
}
}
if (is_index_in_content)
{
// If an additional "@" is specified, this means that we have to return
// the entry from the root element
if (_ident.charAt(1) == '@')
{
_ident = this.getRoot().getEntry(_ident.substr(2));
}
else
{
_ident = this.getEntry(_ident.substr(1));
}
}
return _ident;
},
parseBoolExpression: function(_expression) {
// If the first char of the expression is a '!' this means, that the value
// is to be negated.
if (_expression.charAt(0) == '!')
{
return !this.parseBoolExpression(_expression.substr(1));
}
// Split the expression at a possible "="
var parts = _expression.split('=');
// Expand the first value
var val = this.expandName(parts[0]);
// If a second expression existed, test that one
if (typeof parts[1] != "undefined")
{
// Expand the second value
var checkVal = this.expandName(parts[1]);
// Values starting with / are treated as regular expression. It is
// checked whether the first value matches the regular expression
if (checkVal.charAt(0) == '/')
{
return (new RegExp(checkVal.substr(1, checkVal.length - 2)))
.test(val) ? true : false;
}
// Otherwise check for simple equality
return val == checkVal;
}
return et2_evalBool(val);
},
/**
* ?
*
* @param {object} _owner owner object
* @param {(string|null|object)} _root string with key, null for whole data or object with data
* @param {number?} _row key for into the _root for the desired row
*/
openPerspective: function(_owner, _root, _row)
{
// Get the root node
var root = typeof _root == "string" ? this.data[_root] :
(_root == null ? this.data : _root);
if(typeof root == "undefined" && typeof _root == "string") root = this.getEntry(_root);
// Create a new content array manager with the given root
var constructor = this.isReadOnly ? et2_readonlysArrayMgr : et2_arrayMgr;
var mgr = new constructor(root, this);
// Set the owner
mgr.perspectiveData.owner = _owner;
// Set the root key
if (typeof _root == "string")
{
mgr.perspectiveData.key = _root;
}
// Set _row parameter
if (typeof _row != "undefined")
{
mgr.perspectiveData.row = _row;
}
return mgr;
}
});}).call(this);
var et2_arrayMgr = /** @class */ (function () {
/**
* Constructor
*
* @memberOf et2_arrayMgr
* @param _data
* @param _parentMgr
*/
function et2_arrayMgr(_data, _parentMgr) {
if (_data === void 0) { _data = {}; }
this.splitIds = true;
// Holds information about the current perspective
this.perspectiveData = {
"owner": null,
"key": null,
"row": null
};
this.readOnly = false;
if (typeof _parentMgr == "undefined") {
_parentMgr = null;
}
// Copy the parent manager which is needed to access relative data when
// being in a relative perspective of the manager
this._parentMgr = _parentMgr;
// Hold a reference to the data
if (typeof _data == "undefined" || !_data) {
egw.debug("log", "No data passed to content array manager. Probably a mismatch between template namespaces and data.");
_data = {};
}
// Expand sub-arrays that have been shmushed together, so further perspectives work
// Shmushed keys look like: ${row}[info_cat]
// Expanded: ${row}: Object{info_cat: ..value}
if (this.splitIds) {
// For each index, we need a key: {..} sub array
for (var key in _data) {
// Split up indexes
var indexes = key.replace(/&#x5B;/g, "[").split('[');
// Put data in the proper place
if (indexes.length > 1) {
var value = _data[key];
var target = _data;
for (var i = 0; i < indexes.length; i++) {
indexes[i] = indexes[i].replace(/&#x5D;/g, '').replace(']', '');
if (typeof target[indexes[i]] == "undefined" || target[indexes[i]] === null) {
target[indexes[i]] = i == indexes.length - 1 ? value : {};
}
target = target[indexes[i]];
}
delete _data[key];
}
}
}
this.data = _data;
}
/**
* Returns the root content array manager object
*/
et2_arrayMgr.prototype.getRoot = function () {
if (this._parentMgr != null) {
return this._parentMgr.getRoot();
}
return this;
};
et2_arrayMgr.prototype.getParentMgr = function () {
return this._parentMgr;
};
et2_arrayMgr.prototype.getPerspectiveData = function () {
return this.perspectiveData;
};
et2_arrayMgr.prototype.setPerspectiveData = function (new_perspective) {
this.perspectiveData = new_perspective;
};
et2_arrayMgr.prototype.setRow = function (new_row) {
this.perspectiveData.row = new_row;
};
/**
* Explodes compound keys (eg IDs) into a list of namespaces
* This uses no internal values, just expands
*
* eg:
* a[b][c] => [a,b,c]
* col_filter[tr_tracker] => [col_filter, tr_tracker]
*
* @param {string} _key
*
* @return {string[]}
*/
et2_arrayMgr.prototype.explodeKey = function (_key) {
// Parse the given key by removing the "]"-chars and splitting at "["
var indexes = [_key];
if (typeof _key === "string") {
_key = _key.replace(/&#x5B;/g, "[").replace(/&#x5D;/g, "]");
indexes = _key.split('[');
}
if (indexes.length > 1) {
indexes = [indexes.shift(), indexes.join('[')];
indexes[1] = indexes[1].substring(0, indexes[1].length - 1);
var children = indexes[1].split('][');
if (children.length) {
indexes = jQuery.merge([indexes[0]], children);
}
}
return indexes;
};
/**
* Returns the path to this content array manager perspective as an array
* containing the key values
*
* @param _path is used internally, do not supply it manually.
*/
et2_arrayMgr.prototype.getPath = function (_path) {
if (typeof _path == "undefined") {
_path = [];
}
if (this.perspectiveData.key != null) {
// prepend components of this.perspectiveData.key to path, can be more then one eg. "nm[rows]"
_path = this.perspectiveData.key.replace(/]/g, '').split('[').concat(_path);
}
if (this._parentMgr != null) {
_path = this._parentMgr.getPath(_path);
}
return _path;
};
/**
* Get array entry is the equivalent to the boetemplate get_array function.
* It returns a reference to the (sub) array with the given key. This also works
* for keys using the ETemplate referencing scheme like a[b][c]
*
* @param _key is the string index, may contain sub-indices like a[b]
* @param _referenceInto if true none-existing sub-arrays/-indices get created
* to be returned as reference, else false is returned. Defaults to false
* @param _skipEmpty returns null if _key is not present in this content array.
* Defaults to false.
*/
et2_arrayMgr.prototype.getEntry = function (_key, _referenceInto, _skipEmpty) {
if (typeof _referenceInto == "undefined") {
_referenceInto = false;
}
if (typeof _skipEmpty == "undefined") {
_skipEmpty = false;
}
// Parse the given key by removing the "]"-chars and splitting at "["
var indexes = this.explodeKey(_key);
var entry = this.data;
for (var i = 0; i < indexes.length; i++) {
// Abort if the current entry is not an object (associative array) and
// we should descend further into it.
var isObject = typeof entry === 'object';
if (!isObject && !_referenceInto || entry == null || jQuery.isEmptyObject(entry)) {
return null;
}
// Check whether the entry actually exists
var idx = indexes[i];
if (_skipEmpty && (!isObject || typeof entry[idx] == "undefined")) {
return null;
}
entry = entry[idx];
}
return entry;
};
/**
* Equivalent to the boetemplate::expand_name function.
*
* Expands variables inside the given identifier to their values inside the
* content array.
*
* @param {string} _ident Key used to reference into managed array
* @return {*}
*/
et2_arrayMgr.prototype.expandName = function (_ident) {
// Check whether the identifier refers to an index in the content array
var is_index_in_content = _ident.charAt(0) == '@';
// Check whether "$" occurs in the given identifier
var pos_var = _ident.indexOf('$');
if (pos_var >= 0 && (this.perspectiveData.row != null || !_ident.match(/\$\{?row\}?/))) {
// Get the content array for the current row
var row = typeof this.perspectiveData.row == 'number' ? this.perspectiveData.row : '';
var row_cont = this.data[row] || {};
// $cont is NOT root but current name-space in old eTemplate
var cont = this.data; //getRoot().data;
var _cont = this.data; // according to a grep only used in ImportExport just twice
// Check whether the expression has already been compiled - if not,
// try to compile it first. If an error occurs, the identifier
// function is set to null
if (typeof et2_arrayMgr.compiledExpressions[_ident] == "undefined") {
try {
if (this.perspectiveData.row == null) {
// No row, compile for only top level content
// @ts-ignore
et2_arrayMgr.compiledExpressions[_ident] = et2_compilePHPExpression(_ident, ["cont", "_cont"]);
}
else {
// @ts-ignore
et2_arrayMgr.compiledExpressions[_ident] = et2_compilePHPExpression(_ident, ["row", "cont", "row_cont", "_cont"]);
}
}
catch (e) {
et2_arrayMgr.compiledExpressions[_ident] = null;
egw.debug("error", "Error while compiling PHP->JS ", e);
}
}
// Execute the previously compiled expression, if it is not "null"
// because compilation failed. The parameters have to be in the same
// order as defined during compilation.
if (et2_arrayMgr.compiledExpressions[_ident]) {
try {
if (this.perspectiveData.row == null) {
// No row, exec with only top level content
_ident = et2_arrayMgr.compiledExpressions[_ident](cont, _cont);
}
else {
_ident = et2_arrayMgr.compiledExpressions[_ident](row, cont, row_cont, _cont);
}
}
catch (e) {
// only log error, as they are no real errors but missing data
egw.debug("log", typeof e == 'object' ? e.message : e);
_ident = null;
}
}
}
if (is_index_in_content && _ident) {
// If an additional "@" is specified, this means that we have to return
// the entry from the root element
if (_ident.charAt(1) == '@') {
return this.getRoot().getEntry(_ident.substr(2));
}
else {
return this.getEntry(_ident.substr(1));
}
}
return _ident;
};
et2_arrayMgr.prototype.parseBoolExpression = function (_expression) {
// If the first char of the expression is a '!' this means, that the value
// is to be negated.
if (_expression.charAt(0) == '!') {
return !this.parseBoolExpression(_expression.substr(1));
}
// Split the expression at a possible "="
var parts = _expression.split('=');
// Expand the first value
var val = this.expandName(parts[0]);
val = typeof val == "undefined" ? '' : '' + val;
// If a second expression existed, test that one
if (typeof parts[1] != "undefined") {
// Expand the second value
var checkVal = '' + this.expandName(parts[1]);
// Values starting with / are treated as regular expression. It is
// checked whether the first value matches the regular expression
if (checkVal.charAt(0) == '/') {
return (new RegExp(checkVal.substr(1, checkVal.length - 2)))
.test(val);
}
// Otherwise check for simple equality
return val == checkVal;
}
return et2_evalBool(val);
};
/**
* ?
*
* @param {object} _owner owner object
* @param {(string|null|object)} _root string with key, null for whole data or object with data
* @param {number?} _row key for into the _root for the desired row
*/
et2_arrayMgr.prototype.openPerspective = function (_owner, _root, _row) {
// Get the root node
var root = typeof _root == "string" ? this.data[_root] :
(_root == null ? this.data : _root);
if (typeof root == "undefined" && typeof _root == "string")
root = this.getEntry(_root);
// Create a new content array manager with the given root
var constructor = this.readOnly ? et2_readonlysArrayMgr : et2_arrayMgr;
var mgr = new constructor(root, this);
// Set the owner
mgr.perspectiveData.owner = _owner;
// Set the root key
if (typeof _root == "string") {
mgr.perspectiveData.key = _root;
}
// Set _row parameter
if (typeof _row != "undefined") {
mgr.perspectiveData.row = _row;
}
return mgr;
};
et2_arrayMgr.compiledExpressions = {};
return et2_arrayMgr;
}());
exports.et2_arrayMgr = et2_arrayMgr;
/**
* @augments et2_arrayMgr
*/
var et2_readonlysArrayMgr = (function(){ "use strict"; return et2_arrayMgr.extend(
{
/**
* @memberOf et2_readonlysArrayMgr
* @param _id
* @param _attr
* @param _parent
* @returns
*/
isReadOnly: function(_id, _attr, _parent) {
var entry = null;
if (_id != null)
{
if(_id.indexOf('$') >= 0 || _id.indexOf('@') >= 0)
{
_id = this.expandName(_id);
}
// readonlys was not namespaced in old eTemplate, therefore if we dont find data
// under current namespace, we look into parent
// (if there is anything namespaced, we will NOT look for parent!)
var mgr = this;
while (mgr.parentMgr && jQuery.isEmptyObject(mgr.data))
{
mgr = mgr.parentMgr;
}
entry = mgr.getEntry(_id);
}
// Let the array entry override the read only attribute entry
if (typeof entry != "undefined" && !(typeof entry === 'object'))
{
return entry;
}
// If the attribute is set, return that
if (typeof _attr != "undefined" && _attr !== null)
{
// Accept 'editable', but otherwise boolean
return this.expandName(_attr) === 'editable' ? 'editable' : et2_evalBool(_attr);
}
// Otherwise take into accounf whether the parent is readonly
if (typeof _parent != "undefined" && _parent)
{
return true;
}
// Otherwise return the default value
entry = this.getEntry("__ALL__");
return entry !== null && (typeof entry != "undefined");
},
/**
* Override parent to handle cont and row_cont.
*
* Normally these should refer to the readonlys data, but that's not
* useful, so we use the owner inside perspective data to expand using content.
*
* @param {string} ident Key for searching into the array.
* @returns {*}
*/
expandName: function(ident)
{
return this.perspectiveData.owner.getArrayMgr('content').expandName(ident);
}
});}).call(this);
var et2_readonlysArrayMgr = /** @class */ (function (_super) {
__extends(et2_readonlysArrayMgr, _super);
function et2_readonlysArrayMgr() {
var _this = _super !== null && _super.apply(this, arguments) || this;
_this.readOnly = true;
return _this;
}
/**
* Find out if the given ID is readonly, according to the array data
*
* @memberOf et2_readonlysArrayMgr
* @param _id
* @param _attr
* @param _parent
* @returns
*/
et2_readonlysArrayMgr.prototype.isReadOnly = function (_id, _attr, _parent) {
var entry = null;
if (_id != null) {
if (_id.indexOf('$') >= 0 || _id.indexOf('@') >= 0) {
_id = this.expandName(_id);
}
// readonlys was not namespaced in old eTemplate, therefore if we dont find data
// under current namespace, we look into parent
// (if there is anything namespaced, we will NOT look for parent!)
var mgr = this;
while (mgr.getParentMgr() && jQuery.isEmptyObject(mgr.data)) {
mgr = mgr.getParentMgr();
}
entry = mgr.getEntry(_id);
}
// Let the array entry override the read only attribute entry
if (typeof entry != "undefined" && !(typeof entry === 'object')) {
return entry;
}
// If the attribute is set, return that
if (typeof _attr != "undefined" && _attr !== null) {
// Accept 'editable', but otherwise boolean
return this.expandName(_attr) === 'editable' ? 'editable' : et2_evalBool(_attr);
}
// Otherwise take into accounf whether the parent is readonly
if (typeof _parent != "undefined" && _parent) {
return true;
}
// Otherwise return the default value
entry = this.getEntry("__ALL__");
return entry !== null && (typeof entry != "undefined");
};
/**
* Override parent to handle cont and row_cont.
*
* Normally these should refer to the readonlys data, but that's not
* useful, so we use the owner inside perspective data to expand using content.
*
* @param {string} ident Key for searching into the array.
* @returns {*}
*/
et2_readonlysArrayMgr.prototype.expandName = function (ident) {
return this.perspectiveData.owner.getArrayMgr('content').expandName(ident);
};
return et2_readonlysArrayMgr;
}(et2_arrayMgr));
exports.et2_readonlysArrayMgr = et2_readonlysArrayMgr;
/**
* Creates a new set of array managers
*
* @param _owner is the owner object of the array managers - this object (a widget)
* will free the array manager
* will free the array manager
* @param _mgrs is the original set of array managers, the array managers are
* inside an associative array as recived from et2_widget::getArrayMgrs()
* inside an associative array as recived from et2_widget::getArrayMgrs()
* @param _data is an associative array of new data which will be merged into the
* existing array managers.
* existing array managers.
* @param _row is the row for which the array managers will be opened.
*/
function et2_arrayMgrs_expand(_owner, _mgrs, _data, _row)
{
// Create a copy of the given _mgrs associative array
var result = {};
// Merge the given data associative array into the existing array managers
for (var key in _mgrs)
{
result[key] = _mgrs[key];
if (typeof _data[key] != "undefined")
{
// Open a perspective for the given data row
var rowData = {};
rowData[_row] = _data[key];
result[key] = _mgrs[key].openPerspective(_owner, rowData, _row);
}
}
// Return the resulting managers object
return result;
function et2_arrayMgrs_expand(_owner, _mgrs, _data, _row) {
// Create a copy of the given _mgrs associative array
var result = {};
// Merge the given data associative array into the existing array managers
for (var key in _mgrs) {
result[key] = _mgrs[key];
if (typeof _data[key] != "undefined") {
// Open a perspective for the given data row
var rowData = {};
rowData[_row] = _data[key];
result[key] = _mgrs[key].openPerspective(_owner, rowData, _row);
}
}
// Return the resulting managers object
return result;
}
exports.et2_arrayMgrs_expand = et2_arrayMgrs_expand;
//# sourceMappingURL=et2_core_arrayMgr.js.map

View File

@ -0,0 +1,456 @@
/**
* EGroupware eTemplate2 - JS content array manager
*
* @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_common;
egw_inheritance;
et2_core_phpExpressionCompiler;
*/
import {et2_widget} from "./et2_core_widget";
/**
* Manage access to various template customisation arrays passed to etemplate->exec().
*
* This manages access to content, modifications and readonlys arrays
*/
export class et2_arrayMgr
{
splitIds: boolean = true;
public data: object;
// Holds information about the current perspective
public perspectiveData: { owner: et2_widget; row: number; key: string } = {
"owner": null,
"key": null,
"row": null
};
protected static compiledExpressions: object = {};
private readonly _parentMgr: et2_arrayMgr;
protected readOnly: boolean = false;
/**
* Constructor
*
* @memberOf et2_arrayMgr
* @param _data
* @param _parentMgr
*/
constructor(_data: object = {}, _parentMgr?: et2_arrayMgr) {
if (typeof _parentMgr == "undefined") {
_parentMgr = null;
}
// Copy the parent manager which is needed to access relative data when
// being in a relative perspective of the manager
this._parentMgr = _parentMgr;
// Hold a reference to the data
if (typeof _data == "undefined" || !_data) {
egw.debug("log", "No data passed to content array manager. Probably a mismatch between template namespaces and data.");
_data = {};
}
// Expand sub-arrays that have been shmushed together, so further perspectives work
// Shmushed keys look like: ${row}[info_cat]
// Expanded: ${row}: Object{info_cat: ..value}
if (this.splitIds) {
// For each index, we need a key: {..} sub array
for (let key in _data) {
// Split up indexes
const indexes = key.replace(/&#x5B;/g, "[").split('[');
// Put data in the proper place
if (indexes.length > 1) {
const value = _data[key];
let target = _data;
for (let i = 0; i < indexes.length; i++) {
indexes[i] = indexes[i].replace(/&#x5D;/g, '').replace(']', '');
if (typeof target[indexes[i]] == "undefined" || target[indexes[i]] === null) {
target[indexes[i]] = i == indexes.length - 1 ? value : {};
}
target = target[indexes[i]];
}
delete _data[key];
}
}
}
this.data = _data;
}
/**
* Returns the root content array manager object
*/
getRoot(): et2_arrayMgr {
if (this._parentMgr != null) {
return this._parentMgr.getRoot();
}
return this;
}
getParentMgr(): et2_arrayMgr {
return this._parentMgr;
}
getPerspectiveData(): { owner: et2_widget; row: number; key: string } {
return this.perspectiveData;
}
setPerspectiveData(new_perspective: { owner: et2_widget; row: number; key: string }) {
this.perspectiveData = new_perspective;
}
setRow(new_row: number) {
this.perspectiveData.row = new_row;
}
/**
* Explodes compound keys (eg IDs) into a list of namespaces
* This uses no internal values, just expands
*
* eg:
* a[b][c] => [a,b,c]
* col_filter[tr_tracker] => [col_filter, tr_tracker]
*
* @param {string} _key
*
* @return {string[]}
*/
explodeKey(_key: string): string[] {
// Parse the given key by removing the "]"-chars and splitting at "["
let indexes = [_key];
if (typeof _key === "string") {
_key = _key.replace(/&#x5B;/g, "[").replace(/&#x5D;/g, "]");
indexes = _key.split('[');
}
if (indexes.length > 1) {
indexes = [indexes.shift(), indexes.join('[')];
indexes[1] = indexes[1].substring(0, indexes[1].length - 1);
const children = indexes[1].split('][');
if (children.length) {
indexes = jQuery.merge([indexes[0]], children);
}
}
return indexes;
}
/**
* Returns the path to this content array manager perspective as an array
* containing the key values
*
* @param _path is used internally, do not supply it manually.
*/
getPath(_path?: string[]): string[] {
if (typeof _path == "undefined") {
_path = [];
}
if (this.perspectiveData.key != null) {
// prepend components of this.perspectiveData.key to path, can be more then one eg. "nm[rows]"
_path = this.perspectiveData.key.replace(/]/g, '').split('[').concat(_path);
}
if (this._parentMgr != null) {
_path = this._parentMgr.getPath(_path);
}
return _path;
}
/**
* Get array entry is the equivalent to the boetemplate get_array function.
* It returns a reference to the (sub) array with the given key. This also works
* for keys using the ETemplate referencing scheme like a[b][c]
*
* @param _key is the string index, may contain sub-indices like a[b]
* @param _referenceInto if true none-existing sub-arrays/-indices get created
* to be returned as reference, else false is returned. Defaults to false
* @param _skipEmpty returns null if _key is not present in this content array.
* Defaults to false.
*/
getEntry(_key: string, _referenceInto?: boolean, _skipEmpty?: boolean): string | object {
if (typeof _referenceInto == "undefined") {
_referenceInto = false;
}
if (typeof _skipEmpty == "undefined") {
_skipEmpty = false;
}
// Parse the given key by removing the "]"-chars and splitting at "["
const indexes = this.explodeKey(_key);
let entry = this.data;
for (let i = 0; i < indexes.length; i++) {
// Abort if the current entry is not an object (associative array) and
// we should descend further into it.
const isObject = typeof entry === 'object';
if (!isObject && !_referenceInto || entry == null || jQuery.isEmptyObject(entry)) {
return null;
}
// Check whether the entry actually exists
const idx = indexes[i];
if (_skipEmpty && (!isObject || typeof entry[idx] == "undefined")) {
return null;
}
entry = entry[idx];
}
return entry;
}
/**
* Equivalent to the boetemplate::expand_name function.
*
* Expands variables inside the given identifier to their values inside the
* content array.
*
* @param {string} _ident Key used to reference into managed array
* @return {*}
*/
expandName(_ident: string): string | object {
// Check whether the identifier refers to an index in the content array
const is_index_in_content = _ident.charAt(0) == '@';
// Check whether "$" occurs in the given identifier
const pos_var = _ident.indexOf('$');
if (pos_var >= 0 && (this.perspectiveData.row != null || !_ident.match(/\$\{?row\}?/))) {
// Get the content array for the current row
const row = typeof this.perspectiveData.row == 'number' ? this.perspectiveData.row : '';
const row_cont = this.data[row] || {};
// $cont is NOT root but current name-space in old eTemplate
const cont = this.data;//getRoot().data;
const _cont = this.data;// according to a grep only used in ImportExport just twice
// Check whether the expression has already been compiled - if not,
// try to compile it first. If an error occurs, the identifier
// function is set to null
if (typeof et2_arrayMgr.compiledExpressions[_ident] == "undefined") {
try {
if (this.perspectiveData.row == null) {
// No row, compile for only top level content
// @ts-ignore
et2_arrayMgr.compiledExpressions[_ident] = et2_compilePHPExpression(
_ident, ["cont", "_cont"]);
} else {
// @ts-ignore
et2_arrayMgr.compiledExpressions[_ident] = et2_compilePHPExpression(
_ident, ["row", "cont", "row_cont", "_cont"]);
}
} catch (e) {
et2_arrayMgr.compiledExpressions[_ident] = null;
egw.debug("error", "Error while compiling PHP->JS ", e);
}
}
// Execute the previously compiled expression, if it is not "null"
// because compilation failed. The parameters have to be in the same
// order as defined during compilation.
if (et2_arrayMgr.compiledExpressions[_ident]) {
try {
if (this.perspectiveData.row == null) {
// No row, exec with only top level content
_ident = et2_arrayMgr.compiledExpressions[_ident](cont, _cont);
} else {
_ident = et2_arrayMgr.compiledExpressions[_ident](row, cont, row_cont, _cont);
}
} catch (e) {
// only log error, as they are no real errors but missing data
egw.debug("log", typeof e == 'object' ? e.message : e);
_ident = null;
}
}
}
if (is_index_in_content && _ident) {
// If an additional "@" is specified, this means that we have to return
// the entry from the root element
if (_ident.charAt(1) == '@') {
return this.getRoot().getEntry(_ident.substr(2));
} else {
return this.getEntry(_ident.substr(1));
}
}
return _ident;
}
parseBoolExpression(_expression: string) {
// If the first char of the expression is a '!' this means, that the value
// is to be negated.
if (_expression.charAt(0) == '!') {
return !this.parseBoolExpression(_expression.substr(1));
}
// Split the expression at a possible "="
const parts = _expression.split('=');
// Expand the first value
let val = this.expandName(parts[0]);
val = typeof val == "undefined" ? '' : '' + val;
// If a second expression existed, test that one
if (typeof parts[1] != "undefined") {
// Expand the second value
const checkVal = '' + this.expandName(parts[1]);
// Values starting with / are treated as regular expression. It is
// checked whether the first value matches the regular expression
if (checkVal.charAt(0) == '/') {
return (new RegExp(checkVal.substr(1, checkVal.length - 2)))
.test(val);
}
// Otherwise check for simple equality
return val == checkVal;
}
return et2_evalBool(val);
}
/**
* ?
*
* @param {object} _owner owner object
* @param {(string|null|object)} _root string with key, null for whole data or object with data
* @param {number?} _row key for into the _root for the desired row
*/
openPerspective(_owner: et2_widget, _root: (string | null | object), _row: number | null): et2_arrayMgr {
// Get the root node
let root = typeof _root == "string" ? this.data[_root] :
(_root == null ? this.data : _root);
if (typeof root == "undefined" && typeof _root == "string") root = this.getEntry(_root);
// Create a new content array manager with the given root
const constructor = this.readOnly ? et2_readonlysArrayMgr : et2_arrayMgr;
const mgr = new constructor(root, this);
// Set the owner
mgr.perspectiveData.owner = _owner;
// Set the root key
if (typeof _root == "string") {
mgr.perspectiveData.key = _root;
}
// Set _row parameter
if (typeof _row != "undefined") {
mgr.perspectiveData.row = _row;
}
return mgr;
}
}
/**
* @augments et2_arrayMgr
*/
export class et2_readonlysArrayMgr extends et2_arrayMgr {
readOnly : boolean = true;
/**
* Find out if the given ID is readonly, according to the array data
*
* @memberOf et2_readonlysArrayMgr
* @param _id
* @param _attr
* @param _parent
* @returns
*/
isReadOnly(_id: string, _attr: string, _parent?: et2_arrayMgr): boolean | string {
let entry = null;
if (_id != null) {
if (_id.indexOf('$') >= 0 || _id.indexOf('@') >= 0) {
_id = this.expandName(_id);
}
// readonlys was not namespaced in old eTemplate, therefore if we dont find data
// under current namespace, we look into parent
// (if there is anything namespaced, we will NOT look for parent!)
let mgr: et2_arrayMgr = this;
while (mgr.getParentMgr() && jQuery.isEmptyObject(mgr.data)) {
mgr = mgr.getParentMgr();
}
entry = mgr.getEntry(_id);
}
// Let the array entry override the read only attribute entry
if (typeof entry != "undefined" && !(typeof entry === 'object')) {
return entry;
}
// If the attribute is set, return that
if (typeof _attr != "undefined" && _attr !== null) {
// Accept 'editable', but otherwise boolean
return this.expandName(_attr) === 'editable' ? 'editable' : et2_evalBool(_attr);
}
// Otherwise take into accounf whether the parent is readonly
if (typeof _parent != "undefined" && _parent) {
return true;
}
// Otherwise return the default value
entry = this.getEntry("__ALL__");
return entry !== null && (typeof entry != "undefined");
}
/**
* Override parent to handle cont and row_cont.
*
* Normally these should refer to the readonlys data, but that's not
* useful, so we use the owner inside perspective data to expand using content.
*
* @param {string} ident Key for searching into the array.
* @returns {*}
*/
expandName(ident: string): any {
return this.perspectiveData.owner.getArrayMgr('content').expandName(ident);
}
}
/**
* Creates a new set of array managers
*
* @param _owner is the owner object of the array managers - this object (a widget)
* will free the array manager
* @param _mgrs is the original set of array managers, the array managers are
* inside an associative array as recived from et2_widget::getArrayMgrs()
* @param _data is an associative array of new data which will be merged into the
* existing array managers.
* @param _row is the row for which the array managers will be opened.
*/
export function et2_arrayMgrs_expand(_owner: et2_widget, _mgrs: object, _data: object, _row: number) {
// Create a copy of the given _mgrs associative array
let result = {};
// Merge the given data associative array into the existing array managers
for (let key in _mgrs) {
result[key] = _mgrs[key];
if (typeof _data[key] != "undefined") {
// Open a perspective for the given data row
let rowData = {};
rowData[_row] = _data[key];
result[key] = _mgrs[key].openPerspective(_owner, rowData, _row);
}
}
// Return the resulting managers object
return result;
}

View File

@ -1,3 +1,4 @@
"use strict";
/**
* EGroupware eTemplate2 - JS Widget base class
*
@ -6,16 +7,31 @@
* @subpackage api
* @link http://www.egroupware.org
* @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
/vendor/bower-asset/jquery/dist/jquery.js;
lib/tooltip;
et2_core_DOMWidget;
/vendor/bower-asset/jquery/dist/jquery.js;
lib/tooltip;
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");
/**
* Class which manages the DOM node itself. The simpleWidget class is derrived
* from et2_DOMWidget and implements the getDOMNode function. A setDOMNode
@ -23,407 +39,328 @@
*
* @augments et2_DOMWidget
*/
var et2_baseWidget = (function(){ "use strict"; return et2_DOMWidget.extend(et2_IAligned,
{
attributes: {
"statustext": {
"name": "Tooltip",
"type": "string",
"description": "Tooltip which is shown for this element",
"translate": true
},
"statustext_html": {
"name": "Tooltip is html",
"type": "boolean",
"description": "Flag to allow html content in tooltip",
"default": false
},
"align": {
"name": "Align",
"type": "string",
"default": "left",
"description": "Position of this element in the parent hbox"
},
"onclick": {
"name": "onclick",
"type": "js",
"default": et2_no_init,
"description": "JS code which is executed when the element is clicked."
}
},
/**
* Constructor
*
* @memberOf et2BaseWidget
*/
init: function() {
this.align = "left";
this._super.apply(this, arguments);
this.node = null;
this.statustext = "";
this._messageDiv = null;
this._tooltipElem = null;
},
destroy: function() {
this._super.apply(this, arguments);
this.node = null;
this._messageDiv = null;
},
/**
* The setMessage function can be used to attach a small message box to the
* widget. This is e.g. used to display validation errors or success messages
*
* @param _text is the text which should be displayed as a message
* @param _type is an css class which is attached to the message box.
* Currently available are "hint", "success" and "validation_error", defaults
* to "hint"
* @param _floating if true, the object will be in one row with the element,
* defaults to true
* @param _prepend if set, the message is displayed behind the widget node
* instead of before. Defaults to false.
*/
showMessage: function(_text, _type, _floating, _prepend) {
// Preset the parameters
if (typeof _type == "undefined")
{
_type = "hint";
}
if (typeof _floating == "undefined")
{
_floating = true;
}
if (typeof _prepend == "undefined")
{
_prepend = false;
}
var surr = this.getSurroundings();
// Remove the message div from the surroundings before creating a new
// one
this.hideMessage(false, true);
// Create the message div and add it to the "surroundings" manager
this._messageDiv = jQuery(document.createElement("div"))
.addClass("message")
.addClass(_type)
.addClass(_floating ? "floating" : "")
.text(_text.valueOf() + "");
// Decide whether to prepend or append the div
if (_prepend)
{
surr.prependDOMNode(this._messageDiv[0]);
}
else
{
surr.appendDOMNode(this._messageDiv[0]);
}
surr.update();
},
/**
* The hideMessage function can be used to hide a previously shown message.
*
* @param _fade if true, the message div will fade out, otherwise the message
* div is removed immediately. Defaults to true.
* @param _noUpdate is used internally to prevent an update of the surroundings
* manager.
*/
hideMessage: function(_fade, _noUpdate) {
if (typeof _fade == "undefined")
{
_fade = true;
}
if (typeof _noUpdate == "undefined")
{
_noUpdate = false;
}
// Remove the message from the surroundings manager and remove the
// reference to it
if (this._messageDiv != null)
{
var surr = this.getSurroundings();
var self = this;
var messageDiv = this._messageDiv;
self._messageDiv = null;
var _done = function() {
surr.removeDOMNode(messageDiv[0]);
// Update the surroundings manager
if (!_noUpdate)
{
surr.update();
}
};
// Either fade out or directly call the function which removes the div
if (_fade)
{
messageDiv.fadeOut("fast", _done);
}
else
{
_done();
}
}
},
detachFromDOM: function() {
// Detach this node from the tooltip node
if (this._tooltipElem)
{
this.egw().tooltipUnbind(this._tooltipElem);
this._tooltipElem = null;
}
// Remove the binding to the click handler
if (this.node)
{
jQuery(this.node).unbind("click.et2_baseWidget");
}
this._super.apply(this, arguments);
},
attachToDOM: function() {
this._super.apply(this, arguments);
// Add the binding for the click handler
if (this.node)
{
jQuery(this.node).bind("click.et2_baseWidget", this, function(e) {
return e.data.click.call(e.data, e, this);
});
if (typeof this.onclick == 'function') jQuery(this.node).addClass('et2_clickable');
}
// Update the statustext
this.set_statustext(this.statustext);
},
setDOMNode: function(_node) {
if (_node != this.node)
{
// Deatch the old node from the DOM
this.detachFromDOM();
// Set the new DOM-Node
this.node = _node;
// Attatch the DOM-Node to the tree
return this.attachToDOM();
}
return false;
},
getDOMNode: function() {
return this.node;
},
getTooltipElement: function() {
return this.getDOMNode(this);
},
/**
* Click handler calling custom handler set via onclick attribute to this.onclick
*
* @param _ev
* @returns
*/
click: function(_ev) {
if(typeof this.onclick == 'function')
{
// Make sure function gets a reference to the widget, splice it in as 2. argument if not
var args = Array.prototype.slice.call(arguments);
if(args.indexOf(this) == -1) args.splice(1, 0, this);
return this.onclick.apply(this, args);
}
return true;
},
set_statustext: function(_value) {
// Tooltip should not be shown in mobile view
if (egwIsMobile()) return;
// Don't execute the code below, if no tooltip will be attached/detached
if (_value == "" && !this._tooltipElem)
{
return;
}
// allow statustext to contain multiple translated sub-strings eg: {Firstname}.{Lastname}
if (_value.indexOf('{') !== -1)
{
var egw = this.egw();
_value = _value.replace(/{([^}]+)}/g, function(str,p1)
{
return egw.lang(p1);
});
}
this.statustext = _value;
//Get the domnode the tooltip should be attached to
var elem = jQuery(this.getTooltipElement());
if (elem)
{
//If a tooltip is already attached to the element, remove it first
if (this._tooltipElem)
{
this.egw().tooltipUnbind(this._tooltipElem);
this._tooltipElem = null;
}
if (_value && _value != '')
{
this.egw().tooltipBind(elem, _value, this.options.statustext_html);
this._tooltipElem = elem;
}
}
},
set_align: function(_value) {
this.align = _value;
},
get_align: function(_value) {
return this.align;
}
});}).call(this);
var et2_baseWidget = /** @class */ (function (_super) {
__extends(et2_baseWidget, _super);
/**
* Constructor
*/
function et2_baseWidget(_parent, _attrs, _child) {
var _this =
// Call the inherited constructor
_super.call(this, _parent, _attrs, et2_core_inheritance_1.ClassWithAttributes.extendAttributes(et2_baseWidget._attributes, _child || {})) || this;
_this.align = 'left';
_this.node = null;
_this.statustext = '';
_this._messageDiv = null;
_this._tooltipElem = null;
return _this;
}
et2_baseWidget.prototype.destroy = function () {
_super.prototype.destroy.call(this);
this.node = null;
this._messageDiv = null;
};
/**
* The setMessage function can be used to attach a small message box to the
* widget. This is e.g. used to display validation errors or success messages
*
* @param _text is the text which should be displayed as a message
* @param _type is an css class which is attached to the message box.
* Currently available are "hint", "success" and "validation_error", defaults
* to "hint"
* @param _floating if true, the object will be in one row with the element,
* defaults to true
* @param _prepend if set, the message is displayed behind the widget node
* instead of before. Defaults to false.
*/
et2_baseWidget.prototype.showMessage = function (_text, _type, _floating, _prepend) {
// Preset the parameters
if (typeof _type == "undefined") {
_type = "hint";
}
if (typeof _floating == "undefined") {
_floating = true;
}
if (typeof _prepend == "undefined") {
_prepend = false;
}
var surr = this.getSurroundings();
// Remove the message div from the surroundings before creating a new
// one
this.hideMessage(false, true);
// Create the message div and add it to the "surroundings" manager
this._messageDiv = jQuery(document.createElement("div"))
.addClass("message")
.addClass(_type)
.addClass(_floating ? "floating" : "")
.text(_text.valueOf() + "");
// Decide whether to prepend or append the div
if (_prepend) {
surr.prependDOMNode(this._messageDiv[0]);
}
else {
surr.appendDOMNode(this._messageDiv[0]);
}
surr.update();
};
/**
* The hideMessage function can be used to hide a previously shown message.
*
* @param _fade if true, the message div will fade out, otherwise the message
* div is removed immediately. Defaults to true.
* @param _noUpdate is used internally to prevent an update of the surroundings
* manager.
*/
et2_baseWidget.prototype.hideMessage = function (_fade, _noUpdate) {
if (typeof _fade == "undefined") {
_fade = true;
}
if (typeof _noUpdate == "undefined") {
_noUpdate = false;
}
// Remove the message from the surroundings manager and remove the
// reference to it
if (this._messageDiv != null) {
var surr = this.getSurroundings();
var self = this;
var messageDiv = this._messageDiv;
self._messageDiv = null;
var _done = function () {
surr.removeDOMNode(messageDiv[0]);
// Update the surroundings manager
if (!_noUpdate) {
surr.update();
}
};
// Either fade out or directly call the function which removes the div
if (_fade) {
messageDiv.fadeOut("fast", _done);
}
else {
_done();
}
}
};
et2_baseWidget.prototype.detachFromDOM = function () {
// Detach this node from the tooltip node
if (this._tooltipElem) {
this.egw().tooltipUnbind(this._tooltipElem);
this._tooltipElem = null;
}
// Remove the binding to the click handler
if (this.node) {
jQuery(this.node).unbind("click.et2_baseWidget");
}
return _super.prototype.detachFromDOM.call(this);
};
et2_baseWidget.prototype.attachToDOM = function () {
var ret = _super.prototype.attachToDOM.call(this);
// Add the binding for the click handler
if (this.node) {
jQuery(this.node).bind("click.et2_baseWidget", this, function (e) {
return e.data.click.call(e.data, e, this);
});
if (typeof this.onclick == 'function')
jQuery(this.node).addClass('et2_clickable');
}
// Update the statustext
this.set_statustext(this.statustext);
return ret;
};
et2_baseWidget.prototype.setDOMNode = function (_node) {
if (_node != this.node) {
// Deatch the old node from the DOM
this.detachFromDOM();
// Set the new DOM-Node
this.node = _node;
// Attatch the DOM-Node to the tree
return this.attachToDOM();
}
return false;
};
et2_baseWidget.prototype.getDOMNode = function (_sender) {
return this.node;
};
et2_baseWidget.prototype.getTooltipElement = function () {
return this.getDOMNode(this);
};
/**
* Click handler calling custom handler set via onclick attribute to this.onclick
*
* @param _ev
* @returns
*/
et2_baseWidget.prototype.click = function (_ev) {
if (typeof this.onclick == 'function') {
// Make sure function gets a reference to the widget, splice it in as 2. argument if not
var args = Array.prototype.slice.call(arguments);
if (args.indexOf(this) == -1)
args.splice(1, 0, this);
return this.onclick.apply(this, args);
}
return true;
};
et2_baseWidget.prototype.set_statustext = function (_value) {
// Tooltip should not be shown in mobile view
if (egwIsMobile())
return;
// Don't execute the code below, if no tooltip will be attached/detached
if (_value == "" && !this._tooltipElem) {
return;
}
// allow statustext to contain multiple translated sub-strings eg: {Firstname}.{Lastname}
if (_value.indexOf('{') !== -1) {
var egw = this.egw();
_value = _value.replace(/{([^}]+)}/g, function (str, p1) {
return egw.lang(p1);
});
}
this.statustext = _value;
//Get the domnode the tooltip should be attached to
var elem = jQuery(this.getTooltipElement());
if (elem) {
//If a tooltip is already attached to the element, remove it first
if (this._tooltipElem) {
this.egw().tooltipUnbind(this._tooltipElem);
this._tooltipElem = null;
}
if (_value && _value != '') {
this.egw().tooltipBind(elem, _value, this.options.statustext_html);
this._tooltipElem = elem;
}
}
};
et2_baseWidget.prototype.set_align = function (_value) {
this.align = _value;
};
et2_baseWidget.prototype.get_align = function () {
return this.align;
};
et2_baseWidget._attributes = {
"statustext": {
"name": "Tooltip",
"type": "string",
"description": "Tooltip which is shown for this element",
"translate": true
},
"statustext_html": {
"name": "Tooltip is html",
"type": "boolean",
"description": "Flag to allow html content in tooltip",
"default": false
},
"align": {
"name": "Align",
"type": "string",
"default": "left",
"description": "Position of this element in the parent hbox"
},
"onclick": {
"name": "onclick",
"type": "js",
"default": et2_no_init,
"description": "JS code which is executed when the element is clicked."
}
};
return et2_baseWidget;
}(et2_core_DOMWidget_1.et2_DOMWidget));
exports.et2_baseWidget = et2_baseWidget;
/**
* Simple container object
*
* @augments et2_baseWidget
*/
var et2_container = (function(){ "use strict"; return et2_baseWidget.extend(
{
/**
* Constructor
*
* @memberOf et2_container
*/
init: function() {
this._super.apply(this, arguments);
this.setDOMNode(document.createElement("div"));
},
/**
* The destroy function destroys all children of the widget, removes itself
* from the parents children list.
* Overriden to not try to remove self from parent, as that's not possible.
*/
destroy: function() {
// Call the destructor of all children
for (var i = this._children.length - 1; i >= 0; i--)
{
this._children[i].free();
}
// Free the array managers if they belong to this widget
for (var key in this._mgrs)
{
if (this._mgrs[key] && this._mgrs[key].owner == this)
{
this._mgrs[key].free();
}
}
}
});}).call(this);
var et2_container = /** @class */ (function (_super) {
__extends(et2_container, _super);
/**
* Constructor
*/
function et2_container(_parent, _attrs, _child) {
var _this =
// Call the inherited constructor
_super.call(this, _parent, _attrs, et2_core_inheritance_1.ClassWithAttributes.extendAttributes(et2_container._attributes, _child || {})) || this;
_this.setDOMNode(document.createElement("div"));
return _this;
}
/**
* The destroy function destroys all children of the widget, removes itself
* from the parents children list.
* Overriden to not try to remove self from parent, as that's not possible.
*/
et2_container.prototype.destroy = function () {
// Call the destructor of all children
for (var i = this._children.length - 1; i >= 0; i--) {
this._children[i].destroy();
}
// Free the array managers if they belong to this widget
for (var key in this._mgrs) {
if (this._mgrs[key] && this._mgrs[key].owner == this) {
this._mgrs[key].destroy();
}
}
};
return et2_container;
}(et2_baseWidget));
exports.et2_container = et2_container;
// Register widget for attributes, but not for any xml tags
et2_core_widget_1.et2_register_widget(et2_container, []);
/**
* Container object for not-yet supported widgets
*
* @augments et2_baseWidget
*/
var et2_placeholder = (function(){ "use strict"; return et2_baseWidget.extend([et2_IDetachedDOM],
{
/**
* Constructor
*
* @memberOf et2_placeholder
*/
init: function() {
this._super.apply(this, arguments);
// The attrNodes object will hold the DOM nodes which represent the
// values of this object
this.attrNodes = {};
this.visible = false;
// Create the placeholder div
this.placeDiv = jQuery(document.createElement("span"))
.addClass("et2_placeholder");
var headerNode = jQuery(document.createElement("span"))
.text(this._type || "")
.addClass("et2_caption")
.appendTo(this.placeDiv);
var attrsCntr = jQuery(document.createElement("span"))
.appendTo(this.placeDiv)
.hide();
headerNode.click(this, function(e) {
e.data.visible = !e.data.visible;
if (e.data.visible)
{
attrsCntr.show();
}
else
{
attrsCntr.hide();
}
});
for (var key in this.options)
{
if (typeof this.options[key] != "undefined")
{
if (typeof this.attrNodes[key] == "undefined")
{
this.attrNodes[key] = jQuery(document.createElement("span"))
.addClass("et2_attr");
attrsCntr.append(this.attrNodes[key]);
}
this.attrNodes[key].text(key + "=" + this.options[key]);
}
}
this.setDOMNode(this.placeDiv[0]);
},
getDetachedAttributes: function(_attrs) {
_attrs.push("value");
},
getDetachedNodes: function() {
return [this.placeDiv[0]];
},
setDetachedAttributes: function(_nodes, _values) {
this.placeDiv = jQuery(_nodes[0]);
}
});}).call(this);
var et2_placeholder = /** @class */ (function (_super) {
__extends(et2_placeholder, _super);
/**
* Constructor
*/
function et2_placeholder(_parent, _attrs, _child) {
var _this =
// Call the inherited constructor
_super.call(this, _parent, _attrs, et2_core_inheritance_1.ClassWithAttributes.extendAttributes(et2_placeholder._attributes, _child || {})) || this;
_this.visible = false;
_this.attrNodes = {};
// Create the placeholder div
_this.placeDiv = jQuery(document.createElement("span"))
.addClass("et2_placeholder");
var headerNode = jQuery(document.createElement("span"))
.text(_this.getType() || "")
.addClass("et2_caption")
.appendTo(_this.placeDiv);
var attrsCntr = jQuery(document.createElement("span"))
.appendTo(_this.placeDiv)
.hide();
headerNode.click(_this, function (e) {
e.data.visible = !e.data.visible;
if (e.data.visible) {
attrsCntr.show();
}
else {
attrsCntr.hide();
}
});
for (var key in _this.options) {
if (typeof _this.options[key] != "undefined") {
if (typeof _this.attrNodes[key] == "undefined") {
_this.attrNodes[key] = jQuery(document.createElement("span"))
.addClass("et2_attr");
attrsCntr.append(_this.attrNodes[key]);
}
_this.attrNodes[key].text(key + "=" + _this.options[key]);
}
}
_this.setDOMNode(_this.placeDiv[0]);
return _this;
}
et2_placeholder.prototype.getDetachedAttributes = function (_attrs) {
_attrs.push("value");
};
et2_placeholder.prototype.getDetachedNodes = function () {
return [this.placeDiv[0]];
};
et2_placeholder.prototype.setDetachedAttributes = function (_nodes, _values) {
this.placeDiv = jQuery(_nodes[0]);
};
return et2_placeholder;
}(et2_baseWidget));
// Register widget, but no tags
et2_core_widget_1.et2_register_widget(et2_placeholder, []);
//# sourceMappingURL=et2_core_baseWidget.js.map

View File

@ -0,0 +1,454 @@
/**
* 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
*/
/*egw:uses
/vendor/bower-asset/jquery/dist/jquery.js;
lib/tooltip;
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_register_widget, et2_widget, WidgetConfig} from "./et2_core_widget";
/**
* Class which manages the DOM node itself. The simpleWidget class is derrived
* from et2_DOMWidget and implements the getDOMNode function. A setDOMNode
* function is provided, which attatches the given node to the DOM if possible.
*
* @augments et2_DOMWidget
*/
export class et2_baseWidget extends et2_DOMWidget implements et2_IAligned
{
static readonly _attributes: any = {
"statustext": {
"name": "Tooltip",
"type": "string",
"description": "Tooltip which is shown for this element",
"translate": true
},
"statustext_html": {
"name": "Tooltip is html",
"type": "boolean",
"description": "Flag to allow html content in tooltip",
"default": false
},
"align": {
"name": "Align",
"type": "string",
"default": "left",
"description": "Position of this element in the parent hbox"
},
"onclick": {
"name": "onclick",
"type": "js",
"default": et2_no_init,
"description": "JS code which is executed when the element is clicked."
}
};
align: string = 'left';
node: HTMLElement = null;
statustext: string = '';
private _messageDiv: JQuery = null;
protected _tooltipElem: JQuery = null;
onclick: any;
/**
* Constructor
*/
constructor(_parent, _attrs? : WidgetConfig, _child? : object)
{
// Call the inherited constructor
super(_parent, _attrs, ClassWithAttributes.extendAttributes(et2_baseWidget._attributes, _child || {}));
}
destroy()
{
super.destroy();
this.node = null;
this._messageDiv = null;
}
/**
* The setMessage function can be used to attach a small message box to the
* widget. This is e.g. used to display validation errors or success messages
*
* @param _text is the text which should be displayed as a message
* @param _type is an css class which is attached to the message box.
* Currently available are "hint", "success" and "validation_error", defaults
* to "hint"
* @param _floating if true, the object will be in one row with the element,
* defaults to true
* @param _prepend if set, the message is displayed behind the widget node
* instead of before. Defaults to false.
*/
showMessage(_text, _type?, _floating?, _prepend?)
{
// Preset the parameters
if (typeof _type == "undefined")
{
_type = "hint";
}
if (typeof _floating == "undefined")
{
_floating = true;
}
if (typeof _prepend == "undefined")
{
_prepend = false;
}
var surr = this.getSurroundings();
// Remove the message div from the surroundings before creating a new
// one
this.hideMessage(false, true);
// Create the message div and add it to the "surroundings" manager
this._messageDiv = jQuery(document.createElement("div"))
.addClass("message")
.addClass(_type)
.addClass(_floating ? "floating" : "")
.text(_text.valueOf() + "");
// Decide whether to prepend or append the div
if (_prepend)
{
surr.prependDOMNode(this._messageDiv[0]);
}
else
{
surr.appendDOMNode(this._messageDiv[0]);
}
surr.update();
}
/**
* The hideMessage function can be used to hide a previously shown message.
*
* @param _fade if true, the message div will fade out, otherwise the message
* div is removed immediately. Defaults to true.
* @param _noUpdate is used internally to prevent an update of the surroundings
* manager.
*/
hideMessage(_fade? : boolean, _noUpdate? : boolean)
{
if (typeof _fade == "undefined")
{
_fade = true;
}
if (typeof _noUpdate == "undefined")
{
_noUpdate = false;
}
// Remove the message from the surroundings manager and remove the
// reference to it
if (this._messageDiv != null)
{
var surr = this.getSurroundings();
var self = this;
var messageDiv = this._messageDiv;
self._messageDiv = null;
var _done = function() {
surr.removeDOMNode(messageDiv[0]);
// Update the surroundings manager
if (!_noUpdate)
{
surr.update();
}
};
// Either fade out or directly call the function which removes the div
if (_fade)
{
messageDiv.fadeOut("fast", _done);
}
else
{
_done();
}
}
}
detachFromDOM()
{
// Detach this node from the tooltip node
if (this._tooltipElem)
{
this.egw().tooltipUnbind(this._tooltipElem);
this._tooltipElem = null;
}
// Remove the binding to the click handler
if (this.node)
{
jQuery(this.node).unbind("click.et2_baseWidget");
}
return super.detachFromDOM();
}
attachToDOM()
{
let ret = super.attachToDOM();
// Add the binding for the click handler
if (this.node)
{
jQuery(this.node).bind("click.et2_baseWidget", this, function(e) {
return e.data.click.call(e.data, e, this);
});
if (typeof this.onclick == 'function') jQuery(this.node).addClass('et2_clickable');
}
// Update the statustext
this.set_statustext(this.statustext);
return ret;
}
setDOMNode(_node)
{
if (_node != this.node)
{
// Deatch the old node from the DOM
this.detachFromDOM();
// Set the new DOM-Node
this.node = _node;
// Attatch the DOM-Node to the tree
return this.attachToDOM();
}
return false;
}
getDOMNode(_sender?: et2_widget)
{
return this.node;
}
getTooltipElement()
{
return this.getDOMNode(this);
}
/**
* Click handler calling custom handler set via onclick attribute to this.onclick
*
* @param _ev
* @returns
*/
click(_ev)
{
if(typeof this.onclick == 'function')
{
// Make sure function gets a reference to the widget, splice it in as 2. argument if not
var args = Array.prototype.slice.call(arguments);
if(args.indexOf(this) == -1) args.splice(1, 0, this);
return this.onclick.apply(this, args);
}
return true;
}
set_statustext(_value)
{
// Tooltip should not be shown in mobile view
if (egwIsMobile()) return;
// Don't execute the code below, if no tooltip will be attached/detached
if (_value == "" && !this._tooltipElem)
{
return;
}
// allow statustext to contain multiple translated sub-strings eg: {Firstname}.{Lastname}
if (_value.indexOf('{') !== -1)
{
var egw = this.egw();
_value = _value.replace(/{([^}]+)}/g, function(str,p1)
{
return egw.lang(p1);
});
}
this.statustext = _value;
//Get the domnode the tooltip should be attached to
var elem = jQuery(this.getTooltipElement());
if (elem)
{
//If a tooltip is already attached to the element, remove it first
if (this._tooltipElem)
{
this.egw().tooltipUnbind(this._tooltipElem);
this._tooltipElem = null;
}
if (_value && _value != '')
{
this.egw().tooltipBind(elem, _value, this.options.statustext_html);
this._tooltipElem = elem;
}
}
}
set_align(_value)
{
this.align = _value;
}
get_align()
{
return this.align;
}
}
/**
* Simple container object
*/
export class et2_container extends et2_baseWidget
{
/**
* Constructor
*/
constructor(_parent, _attrs? : WidgetConfig, _child? : object)
{
// Call the inherited constructor
super(_parent, _attrs, ClassWithAttributes.extendAttributes(et2_container._attributes, _child || {}));
this.setDOMNode(document.createElement("div"));
}
/**
* The destroy function destroys all children of the widget, removes itself
* from the parents children list.
* Overriden to not try to remove self from parent, as that's not possible.
*/
destroy()
{
// Call the destructor of all children
for (var i = this._children.length - 1; i >= 0; i--)
{
this._children[i].destroy();
}
// Free the array managers if they belong to this widget
for (var key in this._mgrs)
{
if (this._mgrs[key] && this._mgrs[key].owner == this)
{
this._mgrs[key].destroy();
}
}
}
}
// Register widget for attributes, but not for any xml tags
et2_register_widget(et2_container, []);
/**
* Container object for not-yet supported widgets
*
* @augments et2_baseWidget
*/
class et2_placeholder extends et2_baseWidget implements et2_IDetachedDOM
{
/**
* he attrNodes object will hold the DOM nodes which represent the
* values of this object
*/
attrNodes: {};
visible: boolean = false;
placeDiv: JQuery;
/**
* Constructor
*/
constructor(_parent, _attrs? : WidgetConfig, _child? : object)
{
// Call the inherited constructor
super(_parent, _attrs, ClassWithAttributes.extendAttributes(et2_placeholder._attributes, _child || {}));
this.attrNodes = {};
// Create the placeholder div
this.placeDiv = jQuery(document.createElement("span"))
.addClass("et2_placeholder");
var headerNode = jQuery(document.createElement("span"))
.text(this.getType() || "")
.addClass("et2_caption")
.appendTo(this.placeDiv);
var attrsCntr = jQuery(document.createElement("span"))
.appendTo(this.placeDiv)
.hide();
headerNode.click(this, function(e) {
e.data.visible = !e.data.visible;
if (e.data.visible)
{
attrsCntr.show();
}
else
{
attrsCntr.hide();
}
});
for (var key in this.options)
{
if (typeof this.options[key] != "undefined")
{
if (typeof this.attrNodes[key] == "undefined")
{
this.attrNodes[key] = jQuery(document.createElement("span"))
.addClass("et2_attr");
attrsCntr.append(this.attrNodes[key]);
}
this.attrNodes[key].text(key + "=" + this.options[key]);
}
}
this.setDOMNode(this.placeDiv[0]);
}
getDetachedAttributes(_attrs)
{
_attrs.push("value");
}
getDetachedNodes()
{
return [this.placeDiv[0]];
}
setDetachedAttributes(_nodes, _values)
{
this.placeDiv = jQuery(_nodes[0]);
}
}
// Register widget, but no tags
et2_register_widget(et2_placeholder, []);

File diff suppressed because it is too large Load Diff

View 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('&amp;') === '&'
*
* @param {string} _str
* @returns {string}
*/
function html_entity_decode(_str)
{
return _str && _str.indexOf('&') != -1 ? jQuery('<span>'+_str+'</span>').text() : _str;
}

View File

@ -1,3 +1,4 @@
"use strict";
/**
* EGroupware eTemplate2 - JS Widget base class
*
@ -5,15 +6,27 @@
* @package etemplate
* @subpackage api
* @link http://www.egroupware.org
* @author Andreas Stöckel
* @copyright Stylite 2011
* @version $Id$
* @author Nathan Gray
*/
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
et2_core_inputWidget;
et2_core_inputWidget;
*/
var et2_core_inputWidget_1 = require("./et2_core_inputWidget");
var et2_core_inheritance_1 = require("./et2_core_inheritance");
/**
* et2_editableWidget derives from et2_inputWidget and adds the ability to start
* readonly, then turn editable on double-click. If we decide to do this with
@ -21,184 +34,161 @@
*
* @augments et2_inputWidget
*/
var et2_editableWidget = (function(){ "use strict"; return et2_inputWidget.extend(et2_ISubmitListener,
{
attributes: {
readonly: {
name: "readonly",
type: "string", // | boolean
default: false,
description: "If set to 'editable' will start readonly, double clicking will make it editable and clicking out will save"
},
toggle_readonly: {
name: "toggle_readonly",
type: "boolean",
default: true,
description: "Double clicking makes widget editable. If off, must be made editable in some other way."
},
save_callback: {
name: "save_callback",
type: "string",
default: et2_no_init,
description: "Ajax callback to save changed value when readonly is 'editable'. If not provided, a regular submit is done."
},
save_callback_params: {
name: "readonly",
type: "string",
default: et2_no_init,
description: "Additional parameters passed to save_callback"
},
editable_height: {
name: "Editable height",
description: "Set height for widget while in edit mode",
type: "string"
}
},
/**
* Constructor
*
* @memberOf et2_inputWidget
*/
init: function(_parent, _attrs) {
// 'Editable' really should be boolean for everything else to work
if(_attrs.readonly && typeof _attrs.readonly === 'string')
{
_attrs.readonly = true;
this._toggle_readonly = _attrs.toggle_readonly;
}
this._super.apply(this, arguments);
},
destroy: function() {
var node = this.getInputNode();
if (node)
{
jQuery(node).off('.et2_editableWidget');
}
this._super.apply(this, arguments);
},
/**
* Load the validation errors from the server
*
* @param {object} _attrs
*/
transformAttributes: function(_attrs) {
this._super.apply(this, arguments);
},
attachToDOM: function() {
this._super.apply(this,arguments);
var node = this.getDOMNode();
if (node && this._toggle_readonly)
{
jQuery(node)
.off('.et2_editableWidget')
.on("dblclick.et2_editableWidget", this, function(e) {
e.data.dblclick.call(e.data, this);
})
.addClass('et2_clickable et2_editable');
}
else
{
jQuery(node).addClass('et2_editable_readonly');
}
},
detatchFromDOM: function() {
this._super.apply(this,arguments);
},
/**
* Handle double click
*
* Turn widget editable
*
* @param {DOMNode} _node
*/
dblclick: function (_node) {
// Turn off readonly
this.set_readonly(false);
jQuery('body').on("click.et2_editableWidget", this, function(e) {
// Make sure click comes from body, not a popup
if(jQuery.contains(this, e.target) && e.target.type != 'textarea')
{
jQuery(this).off("click.et2_editableWidget");
e.data.focusout.call(e.data, this);
}
});
},
/**
* User clicked somewhere else, save and turn back to readonly
*
* @param {DOMNode} _node Body node
* @returns {et2_core_editableWidgetet2_editableWidget.et2_core_editableWidgetAnonym$0@call;getInstanceManager@call;submit}
*/
focusout: function (_node)
{
var value = this.get_value();
var oldValue = this._oldValue;
// Change back to readonly
this.set_readonly(true);
// No change, do nothing
if(value == oldValue) return;
// Submit
if(this.options.save_callback)
{
var params = [value];
if(this.options.save_callback_params)
{
params = params.concat(this.options.save_callback_params.split(','));
}
egw.json(this.options.save_callback, params, function() {
}, this, true, this).sendRequest();
}
else
{
this.set_value(value);
return this.getInstanceManager().submit();
}
},
/**
* Called whenever the template gets submitted.
* If we have a save_callback, we call that before the submit (no check on
* the result)
*
* @param _values contains the values which will be sent to the server.
* Listeners may change these values before they get submitted.
*/
submit: function(_values) {
if(this.options.readonly)
{
// Not currently editing, just continue on
return true;
}
// Change back to readonly
this.set_readonly(true);
var params = [this.get_value()];
if(this.options.save_callback_params)
{
params = params.concat(this.options.save_callback_params.split(','));
}
if(this.options.save_callback)
{
egw.json(this.options.save_callback, params, function() {
}, this, true, this).sendRequest();
}
return true;
}
});}).call(this);
var et2_editableWidget = /** @class */ (function (_super) {
__extends(et2_editableWidget, _super);
/**
* Constructor
*/
function et2_editableWidget(_parent, _attrs, _child) {
var _this = this;
// 'Editable' really should be boolean for everything else to work
if (_attrs.readonly && typeof _attrs.readonly === 'string') {
_attrs.readonly = true;
var toggle_readonly = _attrs.toggle_readonly;
}
// Call the inherited constructor
_this = _super.call(this, _parent, _attrs, et2_core_inheritance_1.ClassWithAttributes.extendAttributes(et2_editableWidget._attributes, _child || {})) || this;
if (typeof toggle_readonly != 'undefined')
_this._toggle_readonly = toggle_readonly;
return _this;
}
et2_editableWidget.prototype.destroy = function () {
var node = this.getInputNode();
if (node) {
jQuery(node).off('.et2_editableWidget');
}
_super.prototype.destroy.call(this);
};
/**
* Load the validation errors from the server
*
* @param {object} _attrs
*/
et2_editableWidget.prototype.transformAttributes = function (_attrs) {
_super.prototype.transformAttributes.call(this, _attrs);
};
et2_editableWidget.prototype.attachToDOM = function () {
var res = _super.prototype.attachToDOM.call(this);
var node = this.getDOMNode();
if (node && this._toggle_readonly) {
jQuery(node)
.off('.et2_editableWidget')
.on("dblclick.et2_editableWidget", this, function (e) {
e.data.dblclick.call(e.data, this);
})
.addClass('et2_clickable et2_editable');
}
else {
jQuery(node).addClass('et2_editable_readonly');
}
return res;
};
et2_editableWidget.prototype.detatchFromDOM = function () {
_super.prototype.detatchFromDOM.call(this);
};
/**
* Handle double click
*
* Turn widget editable
*
* @param {DOMNode} _node
*/
et2_editableWidget.prototype.dblclick = function (_node) {
// Turn off readonly
this.set_readonly(false);
jQuery('body').on("click.et2_editableWidget", this, function (e) {
// Make sure click comes from body, not a popup
if (jQuery.contains(this, e.target) && e.target.type != 'textarea') {
jQuery(this).off("click.et2_editableWidget");
e.data.focusout.call(e.data, this);
}
});
};
/**
* User clicked somewhere else, save and turn back to readonly
*
* @param {DOMNode} _node Body node
* @returns {et2_core_editableWidgetet2_editableWidget.et2_core_editableWidgetAnonym$0@call;getInstanceManager@call;submit}
*/
et2_editableWidget.prototype.focusout = function (_node) {
var value = this.get_value();
var oldValue = this._oldValue;
// Change back to readonly
this.set_readonly(true);
// No change, do nothing
if (value == oldValue)
return;
// Submit
if (this.options.save_callback) {
var params = [value];
if (this.options.save_callback_params) {
params = params.concat(this.options.save_callback_params.split(','));
}
egw.json(this.options.save_callback, params, function () {
}, this, true, this).sendRequest();
}
else {
this.set_value(value);
return this.getInstanceManager().submit();
}
};
/**
* Called whenever the template gets submitted.
* If we have a save_callback, we call that before the submit (no check on
* the result)
*
* @param _values contains the values which will be sent to the server.
* Listeners may change these values before they get submitted.
*/
et2_editableWidget.prototype.submit = function (_values) {
if (this.options.readonly) {
// Not currently editing, just continue on
return true;
}
// Change back to readonly
this.set_readonly(true);
var params = [this.get_value()];
if (this.options.save_callback_params) {
params = params.concat(this.options.save_callback_params.split(','));
}
if (this.options.save_callback) {
egw.json(this.options.save_callback, params, function () {
}, this, true, this).sendRequest();
}
return true;
};
et2_editableWidget._attributes = {
readonly: {
name: "readonly",
type: "string",
default: false,
description: "If set to 'editable' will start readonly, double clicking will make it editable and clicking out will save"
},
toggle_readonly: {
name: "toggle_readonly",
type: "boolean",
default: true,
description: "Double clicking makes widget editable. If off, must be made editable in some other way."
},
save_callback: {
name: "save_callback",
type: "string",
default: et2_no_init,
description: "Ajax callback to save changed value when readonly is 'editable'. If not provided, a regular submit is done."
},
save_callback_params: {
name: "readonly",
type: "string",
default: et2_no_init,
description: "Additional parameters passed to save_callback"
},
editable_height: {
name: "Editable height",
description: "Set height for widget while in edit mode",
type: "string"
}
};
return et2_editableWidget;
}(et2_core_inputWidget_1.et2_inputWidget));
exports.et2_editableWidget = et2_editableWidget;
//# sourceMappingURL=et2_core_editableWidget.js.map

View File

@ -0,0 +1,213 @@
/**
* 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 Nathan Gray
*/
/*egw:uses
et2_core_inputWidget;
*/
import {et2_inputWidget} from "./et2_core_inputWidget";
import {WidgetConfig} from "./et2_core_widget";
import {ClassWithAttributes} from "./et2_core_inheritance";
/**
* et2_editableWidget derives from et2_inputWidget and adds the ability to start
* readonly, then turn editable on double-click. If we decide to do this with
* more widgets, it should just be merged with et2_inputWidget.
*
* @augments et2_inputWidget
*/
export class et2_editableWidget extends et2_inputWidget implements et2_ISubmitListener
{
static readonly _attributes : any = {
readonly: {
name: "readonly",
type: "string", // | boolean
default: false,
description: "If set to 'editable' will start readonly, double clicking will make it editable and clicking out will save"
},
toggle_readonly: {
name: "toggle_readonly",
type: "boolean",
default: true,
description: "Double clicking makes widget editable. If off, must be made editable in some other way."
},
save_callback: {
name: "save_callback",
type: "string",
default: et2_no_init,
description: "Ajax callback to save changed value when readonly is 'editable'. If not provided, a regular submit is done."
},
save_callback_params: {
name: "readonly",
type: "string",
default: et2_no_init,
description: "Additional parameters passed to save_callback"
},
editable_height: {
name: "Editable height",
description: "Set height for widget while in edit mode",
type: "string"
}
};
private _toggle_readonly : boolean;
/**
* Constructor
*/
constructor(_parent, _attrs? : WidgetConfig, _child? : object)
{
// 'Editable' really should be boolean for everything else to work
if(_attrs.readonly && typeof _attrs.readonly === 'string')
{
_attrs.readonly = true;
var toggle_readonly = _attrs.toggle_readonly;
}
// Call the inherited constructor
super(_parent, _attrs, ClassWithAttributes.extendAttributes(et2_editableWidget._attributes, _child || {}));
if (typeof toggle_readonly != 'undefined') this._toggle_readonly = toggle_readonly;
}
destroy()
{
let node = this.getInputNode();
if (node)
{
jQuery(node).off('.et2_editableWidget');
}
super.destroy();
}
/**
* Load the validation errors from the server
*
* @param {object} _attrs
*/
transformAttributes(_attrs)
{
super.transformAttributes(_attrs);
}
attachToDOM()
{
let res = super.attachToDOM();
let node = this.getDOMNode();
if (node && this._toggle_readonly)
{
jQuery(node)
.off('.et2_editableWidget')
.on("dblclick.et2_editableWidget", this, function(e) {
e.data.dblclick.call(e.data, this);
})
.addClass('et2_clickable et2_editable');
}
else
{
jQuery(node).addClass('et2_editable_readonly');
}
return res;
}
detatchFromDOM()
{
super.detatchFromDOM();
}
/**
* Handle double click
*
* Turn widget editable
*
* @param {DOMNode} _node
*/
dblclick(_node)
{
// Turn off readonly
this.set_readonly(false);
jQuery('body').on("click.et2_editableWidget", this, function(e) {
// Make sure click comes from body, not a popup
if(jQuery.contains(this, e.target) && e.target.type != 'textarea')
{
jQuery(this).off("click.et2_editableWidget");
e.data.focusout.call(e.data, this);
}
});
}
/**
* User clicked somewhere else, save and turn back to readonly
*
* @param {DOMNode} _node Body node
* @returns {et2_core_editableWidgetet2_editableWidget.et2_core_editableWidgetAnonym$0@call;getInstanceManager@call;submit}
*/
focusout(_node)
{
var value = this.get_value();
var oldValue = this._oldValue;
// Change back to readonly
this.set_readonly(true);
// No change, do nothing
if(value == oldValue) return;
// Submit
if(this.options.save_callback)
{
var params = [value];
if(this.options.save_callback_params)
{
params = params.concat(this.options.save_callback_params.split(','));
}
egw.json(this.options.save_callback, params, function() {
}, this, true, this).sendRequest();
}
else
{
this.set_value(value);
return this.getInstanceManager().submit();
}
}
/**
* Called whenever the template gets submitted.
* If we have a save_callback, we call that before the submit (no check on
* the result)
*
* @param _values contains the values which will be sent to the server.
* Listeners may change these values before they get submitted.
*/
submit(_values)
{
if(this.options.readonly)
{
// Not currently editing, just continue on
return true;
}
// Change back to readonly
this.set_readonly(true);
var params = [this.get_value()];
if(this.options.save_callback_params)
{
params = params.concat(this.options.save_callback_params.split(','));
}
if(this.options.save_callback)
{
egw.json(this.options.save_callback, params, function() {
}, this, true, this).sendRequest();
}
return true;
}
}

View File

@ -1,158 +1,206 @@
"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.generateAttributeSet = function (widget, _attrs) {
// Sanity check and validation
for (var key in _attrs) {
if (typeof widget[key] != "undefined") {
if (!widget[key].ignore) {
_attrs[key] = et2_checkType(_attrs[key], widget[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 widget) {
if (typeof _attrs[key] == "undefined") {
var _default = widget[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);
}
}
};
ClassWithAttributes.buildAttributes = function (class_prototype) {
var class_tree = [];
var attributes = {};
var n = 0;
do {
n++;
class_tree.push(class_prototype);
class_prototype = Object.getPrototypeOf(class_prototype);
} while (class_prototype !== ClassWithAttributes && n < 50);
for (var i = class_tree.length - 1; i >= 0; i--) {
attributes = ClassWithAttributes.extendAttributes(attributes, class_tree[i]._attributes);
}
return attributes;
};
/**
* 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 (_parent, _attributes) {
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;
};
/**
* The implements function can be used to check whether the object
* implements the given interface.
*
* As TypeScript can not (yet) check if an objects implements an interface on runtime,
* we currently implements with each interface a function called 'implements_'+interfacename
* to be able to check here.
*
* @param _iface name of interface to check
*/
ClassWithAttributes.prototype.implements = function (_iface_name) {
if (typeof window['implements_' + _iface_name] === 'function' &&
window['implements_' + _iface_name](this)) {
return true;
}
return false;
};
/**
* Check if object is an instance of a class or implements an interface (specified by the interfaces name)
*
* @param _class_or_interfacename class(-name) or string with name of interface
*/
ClassWithAttributes.prototype.instanceOf = function (_class_or_interfacename) {
if (typeof _class_or_interfacename === 'string') {
return this.implements(_class_or_interfacename);
}
return this instanceof _class_or_interfacename;
};
return ClassWithAttributes;
}());
exports.ClassWithAttributes = ClassWithAttributes;
//# sourceMappingURL=et2_core_inheritance.js.map

View File

@ -0,0 +1,269 @@
/**
* 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.
*/
static generateAttributeSet(widget, _attrs)
{
// Sanity check and validation
for (var key in _attrs)
{
if (typeof widget[key] != "undefined")
{
if (!widget[key].ignore)
{
_attrs[key] = et2_checkType(_attrs[key], widget[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 widget)
{
if (typeof _attrs[key] == "undefined")
{
var _default = widget[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);
}
}
}
static buildAttributes(class_prototype: object)
{
let class_tree = [];
let attributes = {};
let n = 0;
do
{
n++;
class_tree.push(class_prototype);
class_prototype = Object.getPrototypeOf(class_prototype);
} while (class_prototype !== ClassWithAttributes && n < 50);
for(let i = class_tree.length - 1; i >= 0; i--)
{
attributes = ClassWithAttributes.extendAttributes(attributes, class_tree[i]._attributes);
}
return attributes;
}
/**
* 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(_parent : object, _attributes : 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;
}
/**
* The implements function can be used to check whether the object
* implements the given interface.
*
* As TypeScript can not (yet) check if an objects implements an interface on runtime,
* we currently implements with each interface a function called 'implements_'+interfacename
* to be able to check here.
*
* @param _iface name of interface to check
*/
implements (_iface_name : string)
{
if (typeof window['implements_'+_iface_name] === 'function' &&
window['implements_'+_iface_name](this))
{
return true
}
return false;
}
/**
* Check if object is an instance of a class or implements an interface (specified by the interfaces name)
*
* @param _class_or_interfacename class(-name) or string with name of interface
*/
instanceOf(_class_or_interfacename: any) : boolean
{
if (typeof _class_or_interfacename === 'string')
{
return this.implements(_class_or_interfacename);
}
return this instanceof _class_or_interfacename;
}
}

View File

@ -1,3 +1,4 @@
"use strict";
/**
* EGroupware eTemplate2 - JS Widget base class
*
@ -6,317 +7,280 @@
* @subpackage api
* @link http://www.egroupware.org
* @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
/vendor/bower-asset/jquery/dist/jquery.js;
et2_core_interfaces;
et2_core_valueWidget;
/vendor/bower-asset/jquery/dist/jquery.js;
et2_core_interfaces;
et2_core_valueWidget;
*/
require("./et2_core_common");
var et2_core_inheritance_1 = require("./et2_core_inheritance");
var et2_core_valueWidget_1 = require("./et2_core_valueWidget");
require("./et2_types");
/**
* et2_inputWidget derrives from et2_simpleWidget and implements the IInput
* interface. When derriving from this class, call setDOMNode with an input
* DOMNode.
*
* @augments et2_valueWidget
*/
var et2_inputWidget = (function(){ "use strict"; return et2_valueWidget.extend([et2_IInput,et2_ISubmitListener],
{
attributes: {
"needed": {
"name": "Required",
"default": false,
"type": "boolean",
"description": "If required, the user must enter a value before the form can be submitted"
},
"onchange": {
"name": "onchange",
"type": "js",
"default": et2_no_init,
"description": "JS code which is executed when the value changes."
},
"onfocus": {
"name": "onfocus",
"type": "js",
"default": et2_no_init,
"description": "JS code which get executed when wiget receives focus."
},
"validation_error": {
"name": "Validation Error",
"type": "string",
"default": et2_no_init,
"description": "Used internally to store the validation error that came from the server."
},
"tabindex": {
"name": "Tab index",
"type": "integer",
"default": et2_no_init,
"description": "Specifies the tab order of a widget when the 'tab' button is used for navigating."
},
readonly: {
name: "readonly",
type: "boolean",
"default": false,
description: "Does NOT allow user to enter data, just displays existing data"
}
},
/**
* Constructor
*
* @memberOf et2_inputWidget
*/
init: function() {
this._super.apply(this, arguments);
// mark value as not initialised, so set_value can determine if it is necessary to trigger change event
this._oldValue = et2_no_init;
this._labelContainer = null;
},
destroy: function() {
var node = this.getInputNode();
if (node)
{
jQuery(node).unbind("change.et2_inputWidget");
jQuery(node).unbind("focus");
}
this._super.apply(this, arguments);
this._labelContainer = null;
},
/**
* Load the validation errors from the server
*
* @param {object} _attrs
*/
transformAttributes: function(_attrs) {
this._super.apply(this, arguments);
// Check whether an validation error entry exists
if (this.id && this.getArrayMgr("validation_errors"))
{
var val = this.getArrayMgr("validation_errors").getEntry(this.id);
if (val)
{
_attrs["validation_error"] = val;
}
}
},
attachToDOM: function() {
var node = this.getInputNode();
if (node)
{
jQuery(node)
.off('.et2_inputWidget')
.bind("change.et2_inputWidget", this, function(e) {
e.data.change.call(e.data, this);
})
.bind("focus.et2_inputWidget", this, function(e) {
e.data.focus.call(e.data, this);
});
}
this._super.apply(this,arguments);
// jQuery(this.getInputNode()).attr("novalidate","novalidate"); // Stop browser from getting involved
// jQuery(this.getInputNode()).validator();
},
detatchFromDOM: function() {
// if(this.getInputNode()) {
// jQuery(this.getInputNode()).data("validator").destroy();
// }
this._super.apply(this,arguments);
},
change: function(_node) {
var messages = [];
var valid = this.isValid(messages);
// Passing false will clear any set messages
this.set_validation_error(valid ? false : messages);
if (valid && this.onchange)
{
if(typeof this.onchange == '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.onchange.apply(this, args);
} else {
return (et2_compileLegacyJS(this.options.onchange, this, _node))();
}
}
return valid;
},
focus: function(_node)
{
if(typeof this.options.onfocus == '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.onfocus.apply(this, args);
}
},
/**
* Set value of widget and trigger for real changes a change event
*
* First initialisation (_oldValue === et2_no_init) is NOT considered a change!
*
* @param {string} _value value to set
*/
set_value: function(_value)
{
var node = this.getInputNode();
if (node)
{
jQuery(node).val(_value);
if(this.isAttached() && this._oldValue !== et2_no_init && this._oldValue !== _value)
{
jQuery(node).change();
}
}
this._oldValue = _value;
},
set_id: function(_value) {
this.id = _value;
this.dom_id = _value && this.getInstanceManager() ? this.getInstanceManager().uniqueId+'_'+this.id : _value;
// Set the id of the _input_ node (in contrast to the default
// implementation, which sets the base node)
var node = this.getInputNode();
if (node)
{
// Unique ID to prevent DOM collisions across multiple templates
if (_value != "")
{
node.setAttribute("id", this.dom_id);
node.setAttribute("name", _value);
}
else
{
node.removeAttribute("id");
node.removeAttribute("name");
}
}
},
set_needed: function(_value) {
var node = this.getInputNode();
if (node)
{
if(_value && !this.options.readonly) {
jQuery(node).attr("required", "required");
} else {
node.removeAttribute("required");
}
}
},
set_validation_error: function(_value) {
var node = this.getInputNode();
if (node)
{
if (_value === false)
{
this.hideMessage();
jQuery(node).removeClass("invalid");
}
else
{
this.showMessage(_value, "validation_error");
jQuery(node).addClass("invalid");
// If on a tab, switch to that tab so user can see it
var widget = this;
while(widget._parent && widget._type != 'tabbox')
{
widget = widget._parent;
}
if (widget._type == 'tabbox') widget.activateTab(this);
}
}
},
/**
* Set tab index
*
* @param {number} index
*/
set_tabindex: function(index) {
jQuery(this.getInputNode()).attr("tabindex", index);
},
getInputNode: function() {
return this.node;
},
get_value: function() {
return this.getValue();
},
getValue: function() {
var node = this.getInputNode();
if (node)
{
var val = jQuery(node).val();
return val;
}
return this._oldValue;
},
isDirty: function() {
return this._oldValue != this.getValue();
},
resetDirty: function() {
this._oldValue = this.getValue();
},
isValid: function(messages) {
var ok = true;
// Check for required
if (this.options && this.options.needed && !this.options.readonly && !this.disabled &&
(this.getValue() == null || this.getValue().valueOf() == ''))
{
messages.push(this.egw().lang('Field must not be empty !!!'));
ok = false;
}
return ok;
},
/**
* Called whenever the template gets submitted. We return false if the widget
* is not valid, which cancels the 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) {
var messages = [];
var valid = this.isValid(messages);
// Passing false will clear any set messages
this.set_validation_error(valid ? false : messages);
return valid;
}
});}).call(this);
var et2_inputWidget = /** @class */ (function (_super) {
__extends(et2_inputWidget, _super);
/**
* Constructor
*/
function et2_inputWidget(_parent, _attrs, _child) {
var _this =
// Call the inherited constructor
_super.call(this, _parent, _attrs, et2_core_inheritance_1.ClassWithAttributes.extendAttributes(et2_inputWidget._attributes, _child || {})) || this;
// mark value as not initialised, so set_value can determine if it is necessary to trigger change event
_this._oldValue = et2_no_init;
_this._labelContainer = null;
return _this;
}
et2_inputWidget.prototype.destroy = function () {
var node = this.getInputNode();
if (node) {
jQuery(node).unbind("change.et2_inputWidget");
jQuery(node).unbind("focus");
}
_super.prototype.destroy.call(this);
this._labelContainer = null;
};
/**
* Load the validation errors from the server
*
* @param {object} _attrs
*/
et2_inputWidget.prototype.transformAttributes = function (_attrs) {
_super.prototype.transformAttributes.call(this, _attrs);
// Check whether an validation error entry exists
if (this.id && this.getArrayMgr("validation_errors")) {
var val = this.getArrayMgr("validation_errors").getEntry(this.id);
if (val) {
_attrs["validation_error"] = val;
}
}
};
et2_inputWidget.prototype.attachToDOM = function () {
var node = this.getInputNode();
if (node) {
jQuery(node)
.off('.et2_inputWidget')
.bind("change.et2_inputWidget", this, function (e) {
e.data.change.call(e.data, this);
})
.bind("focus.et2_inputWidget", this, function (e) {
e.data.focus.call(e.data, this);
});
}
return _super.prototype.attachToDOM.call(this);
// jQuery(this.getInputNode()).attr("novalidate","novalidate"); // Stop browser from getting involved
// jQuery(this.getInputNode()).validator();
};
et2_inputWidget.prototype.detatchFromDOM = function () {
// if(this.getInputNode()) {
// jQuery(this.getInputNode()).data("validator").destroy();
// }
_super.prototype.detachFromDOM.call(this);
};
et2_inputWidget.prototype.change = function (_node, _widget, _value) {
var messages = [];
var valid = this.isValid(messages);
// Passing false will clear any set messages
this.set_validation_error(valid ? false : messages);
if (valid && this.onchange) {
if (typeof this.onchange == '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.onchange.apply(this, args);
}
else {
return (et2_compileLegacyJS(this.options.onchange, this, _node))();
}
}
return valid;
};
et2_inputWidget.prototype.focus = function (_node) {
if (typeof this.options.onfocus == '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.onfocus.apply(this, args);
}
};
/**
* Set value of widget and trigger for real changes a change event
*
* First initialisation (_oldValue === et2_no_init) is NOT considered a change!
*
* @param {string} _value value to set
*/
et2_inputWidget.prototype.set_value = function (_value) {
var node = this.getInputNode();
if (node) {
jQuery(node).val(_value);
if (this.isAttached() && this._oldValue !== et2_no_init && this._oldValue !== _value) {
jQuery(node).change();
}
}
this._oldValue = _value;
};
et2_inputWidget.prototype.set_id = function (_value) {
this.id = _value;
this.dom_id = _value && this.getInstanceManager() ? this.getInstanceManager().uniqueId + '_' + this.id : _value;
// Set the id of the _input_ node (in contrast to the default
// implementation, which sets the base node)
var node = this.getInputNode();
if (node) {
// Unique ID to prevent DOM collisions across multiple templates
if (_value != "") {
node.setAttribute("id", this.dom_id);
node.setAttribute("name", _value);
}
else {
node.removeAttribute("id");
node.removeAttribute("name");
}
}
};
et2_inputWidget.prototype.set_needed = function (_value) {
var node = this.getInputNode();
if (node) {
if (_value && !this.options.readonly) {
jQuery(node).attr("required", "required");
}
else {
node.removeAttribute("required");
}
}
};
et2_inputWidget.prototype.set_validation_error = function (_value) {
var node = this.getInputNode();
if (node) {
if (_value === false) {
this.hideMessage();
jQuery(node).removeClass("invalid");
}
else {
this.showMessage(_value, "validation_error");
jQuery(node).addClass("invalid");
// If on a tab, switch to that tab so user can see it
var widget = this;
while (widget.getParent() && widget.getType() != 'tabbox') {
widget = widget.getParent();
}
if (widget.getType() == 'tabbox')
widget.activateTab(this);
}
}
};
/**
* Set tab index
*
* @param {number} index
*/
et2_inputWidget.prototype.set_tabindex = function (index) {
jQuery(this.getInputNode()).attr("tabindex", index);
};
et2_inputWidget.prototype.getInputNode = function () {
return this.node;
};
et2_inputWidget.prototype.get_value = function () {
return this.getValue();
};
et2_inputWidget.prototype.getValue = function () {
var node = this.getInputNode();
if (node) {
var val = jQuery(node).val();
return val;
}
return this._oldValue;
};
et2_inputWidget.prototype.isDirty = function () {
return this._oldValue != this.getValue();
};
et2_inputWidget.prototype.resetDirty = function () {
this._oldValue = this.getValue();
};
et2_inputWidget.prototype.isValid = function (messages) {
var ok = true;
// Check for required
if (this.options && this.options.needed && !this.options.readonly && !this.disabled &&
(this.getValue() == null || this.getValue().valueOf() == '')) {
messages.push(this.egw().lang('Field must not be empty !!!'));
ok = false;
}
return ok;
};
/**
* Called whenever the template gets submitted. We return false if the widget
* is not valid, which cancels the submission.
*
* @param _values contains the values which will be sent to the server.
* Listeners may change these values before they get submitted.
*/
et2_inputWidget.prototype.submit = function (_values) {
var messages = [];
var valid = this.isValid(messages);
// Passing false will clear any set messages
this.set_validation_error(valid ? false : messages);
return valid;
};
et2_inputWidget._attributes = {
"needed": {
"name": "Required",
"default": false,
"type": "boolean",
"description": "If required, the user must enter a value before the form can be submitted"
},
"onchange": {
"name": "onchange",
"type": "js",
"default": et2_no_init,
"description": "JS code which is executed when the value changes."
},
"onfocus": {
"name": "onfocus",
"type": "js",
"default": et2_no_init,
"description": "JS code which get executed when wiget receives focus."
},
"validation_error": {
"name": "Validation Error",
"type": "string",
"default": et2_no_init,
"description": "Used internally to store the validation error that came from the server."
},
"tabindex": {
"name": "Tab index",
"type": "integer",
"default": et2_no_init,
"description": "Specifies the tab order of a widget when the 'tab' button is used for navigating."
},
readonly: {
name: "readonly",
type: "boolean",
"default": false,
description: "Does NOT allow user to enter data, just displays existing data"
}
};
return et2_inputWidget;
}(et2_core_valueWidget_1.et2_valueWidget));
exports.et2_inputWidget = et2_inputWidget;
//# sourceMappingURL=et2_core_inputWidget.js.map

View File

@ -0,0 +1,343 @@
/**
* 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
*/
/*egw:uses
/vendor/bower-asset/jquery/dist/jquery.js;
et2_core_interfaces;
et2_core_valueWidget;
*/
import './et2_core_common';
import { ClassWithAttributes } from "./et2_core_inheritance";
import { et2_widget, et2_createWidget, et2_register_widget, WidgetConfig } from "./et2_core_widget";
import { et2_DOMWidget } from './et2_core_DOMWidget'
import { et2_valueWidget } from './et2_core_valueWidget'
import './et2_types';
/**
* et2_inputWidget derrives from et2_simpleWidget and implements the IInput
* interface. When derriving from this class, call setDOMNode with an input
* DOMNode.
*/
export class et2_inputWidget extends et2_valueWidget implements et2_IInput, et2_ISubmitListener
{
static readonly _attributes : any = {
"needed": {
"name": "Required",
"default": false,
"type": "boolean",
"description": "If required, the user must enter a value before the form can be submitted"
},
"onchange": {
"name": "onchange",
"type": "js",
"default": et2_no_init,
"description": "JS code which is executed when the value changes."
},
"onfocus": {
"name": "onfocus",
"type": "js",
"default": et2_no_init,
"description": "JS code which get executed when wiget receives focus."
},
"validation_error": {
"name": "Validation Error",
"type": "string",
"default": et2_no_init,
"description": "Used internally to store the validation error that came from the server."
},
"tabindex": {
"name": "Tab index",
"type": "integer",
"default": et2_no_init,
"description": "Specifies the tab order of a widget when the 'tab' button is used for navigating."
},
readonly: {
name: "readonly",
type: "boolean",
"default": false,
description: "Does NOT allow user to enter data, just displays existing data"
}
}
protected _oldValue: any;
onchange: Function;
/**
* Constructor
*/
constructor(_parent, _attrs? : WidgetConfig, _child? : object)
{
// Call the inherited constructor
super(_parent, _attrs, ClassWithAttributes.extendAttributes(et2_inputWidget._attributes, _child || {}));
// mark value as not initialised, so set_value can determine if it is necessary to trigger change event
this._oldValue = et2_no_init;
this._labelContainer = null;
}
destroy()
{
var node = this.getInputNode();
if (node)
{
jQuery(node).unbind("change.et2_inputWidget");
jQuery(node).unbind("focus");
}
super.destroy();
this._labelContainer = null;
}
/**
* Load the validation errors from the server
*
* @param {object} _attrs
*/
transformAttributes(_attrs)
{
super.transformAttributes(_attrs);
// Check whether an validation error entry exists
if (this.id && this.getArrayMgr("validation_errors"))
{
var val = this.getArrayMgr("validation_errors").getEntry(this.id);
if (val)
{
_attrs["validation_error"] = val;
}
}
}
attachToDOM()
{
var node = this.getInputNode();
if (node)
{
jQuery(node)
.off('.et2_inputWidget')
.bind("change.et2_inputWidget", this, function(e) {
e.data.change.call(e.data, this);
})
.bind("focus.et2_inputWidget", this, function(e) {
e.data.focus.call(e.data, this);
});
}
return super.attachToDOM();
// jQuery(this.getInputNode()).attr("novalidate","novalidate"); // Stop browser from getting involved
// jQuery(this.getInputNode()).validator();
}
detatchFromDOM()
{
// if(this.getInputNode()) {
// jQuery(this.getInputNode()).data("validator").destroy();
// }
super.detachFromDOM();
}
change(_node, _widget?, _value?)
{
var messages = [];
var valid = this.isValid(messages);
// Passing false will clear any set messages
this.set_validation_error(valid ? false : messages);
if (valid && this.onchange)
{
if(typeof this.onchange == '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.onchange.apply(this, args);
} else {
return (et2_compileLegacyJS(this.options.onchange, this, _node))();
}
}
return valid;
}
focus(_node)
{
if(typeof this.options.onfocus == '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.onfocus.apply(this, args);
}
}
/**
* Set value of widget and trigger for real changes a change event
*
* First initialisation (_oldValue === et2_no_init) is NOT considered a change!
*
* @param {string} _value value to set
*/
set_value(_value : any | null)
{
var node = this.getInputNode();
if (node)
{
jQuery(node).val(_value);
if(this.isAttached() && this._oldValue !== et2_no_init && this._oldValue !== _value)
{
jQuery(node).change();
}
}
this._oldValue = _value;
}
set_id(_value)
{
this.id = _value;
this.dom_id = _value && this.getInstanceManager() ? this.getInstanceManager().uniqueId+'_'+this.id : _value;
// Set the id of the _input_ node (in contrast to the default
// implementation, which sets the base node)
var node = this.getInputNode();
if (node)
{
// Unique ID to prevent DOM collisions across multiple templates
if (_value != "")
{
node.setAttribute("id", this.dom_id);
node.setAttribute("name", _value);
}
else
{
node.removeAttribute("id");
node.removeAttribute("name");
}
}
}
set_needed(_value)
{
var node = this.getInputNode();
if (node)
{
if(_value && !this.options.readonly) {
jQuery(node).attr("required", "required");
} else {
node.removeAttribute("required");
}
}
}
set_validation_error(_value)
{
var node = this.getInputNode();
if (node)
{
if (_value === false)
{
this.hideMessage();
jQuery(node).removeClass("invalid");
}
else
{
this.showMessage(_value, "validation_error");
jQuery(node).addClass("invalid");
// If on a tab, switch to that tab so user can see it
let widget : et2_widget = this;
while(widget.getParent() && widget.getType() != 'tabbox')
{
widget = widget.getParent();
}
if (widget.getType() == 'tabbox') (<et2_tabbox><unknown>widget).activateTab(this);
}
}
}
/**
* Set tab index
*
* @param {number} index
*/
set_tabindex(index)
{
jQuery(this.getInputNode()).attr("tabindex", index);
}
getInputNode()
{
return this.node;
}
get_value()
{
return this.getValue();
}
getValue()
{
var node = this.getInputNode();
if (node)
{
var val = jQuery(node).val();
return val;
}
return this._oldValue;
}
isDirty()
{
return this._oldValue != this.getValue();
}
resetDirty()
{
this._oldValue = this.getValue();
}
isValid(messages)
{
var ok = true;
// Check for required
if (this.options && this.options.needed && !this.options.readonly && !this.disabled &&
(this.getValue() == null || this.getValue().valueOf() == ''))
{
messages.push(this.egw().lang('Field must not be empty !!!'));
ok = false;
}
return ok;
}
/**
* Called whenever the template gets submitted. We return false if the widget
* is not valid, which cancels the submission.
*
* @param _values contains the values which will be sent to the server.
* Listeners may change these values before they get submitted.
*/
submit(_values)
{
var messages = [];
var valid = this.isValid(messages);
// Passing false will clear any set messages
this.set_validation_error(valid ? false : messages);
return valid;
}
}

View File

@ -6,156 +6,51 @@
* @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;
}
var et2_IDOMNode = "et2_IDOMNode";
function implements_et2_IDOMNode(obj) {
return implements_methods(obj, ["getDOMNode"]);
}
var et2_IInput = "et2_IInput";
function implements_et2_IInput(obj) {
return implements_methods(obj, ["getValue", "isDirty", "resetDirty", "isValid"]);
}
var et2_IResizeable = "et2_IResizeable";
function implements_et2_IResizeable(obj) {
return implements_methods(obj, ["resize"]);
}
var et2_IAligned = "et2_IAligned";
function implements_et2_IAligned(obj) {
return implements_methods(obj, ["get_align"]);
}
var et2_ISubmitListener = "et2_ISubmitListener";
function implements_et2_ISubmitListener(obj) {
return implements_methods(obj, ["submit"]);
}
var et2_IDetachedDOM = "et2_IDetachedDOM";
function implements_et2_IDetachedDOM(obj) {
return implements_methods(obj, ["getDetachedAttributes", "getDetachedNodes", "setDetachedAttributes"]);
}
var et2_IPrint = "et2_IPrint";
function implements_et2_IPrint(obj) {
return implements_methods(obj, ["beforePrint", "afterPrint"]);
}
var et2_IExposable = "et2_IExposable";
function implements_et2_IExposable(obj) {
return implements_methods(obj, ["getMedia"]);
}
//# sourceMappingURL=et2_core_interfaces.js.map

View File

@ -0,0 +1,230 @@
/**
* 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
}
var et2_IDOMNode = "et2_IDOMNode";
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
}
var et2_IInput = "et2_IInput";
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(number) : void
}
var et2_IResizeable = "et2_IResizeable";
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
}
var et2_IAligned = "et2_IAligned";
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
}
var et2_ISubmitListener = "et2_ISubmitListener";
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, _data?) : void
}
var et2_IDetachedDOM = "et2_IDetachedDOM";
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
}
var et2_IPrint = "et2_IPrint";
function implements_et2_IPrint(obj : et2_widget)
{
return implements_methods(obj, ["beforePrint", "afterPrint"]);
}
/**
* Interface all exposed widget must support in order to getMedia for the blueimp Gallery.
*/
interface et2_IExposable
{
/**
* get media an array of media objects to pass to blueimp Gallery
* @param {array} _attrs
*/
getMedia(_attrs) : void;
}
var et2_IExposable = "et2_IExposable";
function implements_et2_IExposable(obj : et2_widget)
{
return implements_methods(obj, ["getMedia"]);
}

View File

@ -1,3 +1,4 @@
"use strict";
/**
* EGroupware eTemplate2 - JS widget class with value attribute and auto loading
*
@ -9,124 +10,125 @@
* @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
/vendor/bower-asset/jquery/dist/jquery.js;
et2_core_baseWidget;
/vendor/bower-asset/jquery/dist/jquery.js;
et2_core_baseWidget;
*/
var et2_core_baseWidget_1 = require("./et2_core_baseWidget");
require("./et2_core_common");
var et2_core_inheritance_1 = require("./et2_core_inheritance");
/**
* et2_valueWidget is the base class for et2_inputWidget - valueWidget introduces
* the "value" attribute and automatically loads it from the "content" array
* after loading from XML.
*
* @augments et2_baseWidget
*/
var et2_valueWidget = (function(){ "use strict"; return et2_baseWidget.extend(
{
attributes: {
"label": {
"name": "Label",
"default": "",
"type": "string",
"description": "The label is displayed by default in front (for radiobuttons behind) each widget (if not empty). If you want to specify a different position, use a '%s' in the label, which gets replaced by the widget itself. Eg. '%s Name' to have the label Name behind a checkbox. The label can contain variables, as descript for name. If the label starts with a '@' it is replaced by the value of the content-array at this index (with the '@'-removed and after expanding the variables).",
"translate": true
},
"value": {
"name": "Value",
"description": "The value of the widget",
"type": "rawstring", // no html-entity decoding
"default": et2_no_init
}
},
/**
*
*
* @memberOf et2_valueWidget
* @param _attrs
*/
transformAttributes: function(_attrs) {
this._super.apply(this, arguments);
if (this.id)
{
// Set the value for this element
var contentMgr = this.getArrayMgr("content");
if (contentMgr != null) {
var val = contentMgr.getEntry(this.id,false,true);
if (val !== null)
{
_attrs["value"] = val;
}
}
// Check for already inside namespace
if(this.createNamespace && this.getArrayMgr("content").perspectiveData.owner == this)
{
_attrs["value"] = this.getArrayMgr("content").data;
}
}
},
set_label: function(_value) {
// Abort if ther was no change in the label
if (_value == this.label)
{
return;
}
if (_value)
{
// Create the label container if it didn't exist yet
if (this._labelContainer == null)
{
this._labelContainer = jQuery(document.createElement("label"))
.addClass("et2_label");
this.getSurroundings().insertDOMNode(this._labelContainer[0]);
}
// Clear the label container.
this._labelContainer.empty();
// Create the placeholder element and set it
var ph = document.createElement("span");
this.getSurroundings().setWidgetPlaceholder(ph);
// Split the label at the "%s"
var parts = et2_csvSplit(_value, 2, "%s");
// Update the content of the label container
for (var i = 0; i < parts.length; i++)
{
if (parts[i])
{
this._labelContainer.append(document.createTextNode(parts[i]));
}
if (i == 0)
{
this._labelContainer.append(ph);
}
}
// add class if label is empty
this._labelContainer.toggleClass('et2_label_empty', !_value || !parts[0]);
}
else
{
// Delete the labelContainer from the surroundings object
if (this._labelContainer)
{
this.getSurroundings().removeDOMNode(this._labelContainer[0]);
}
this._labelContainer = null;
}
// Update the surroundings in order to reflect the change in the label
this.getSurroundings().update();
// Copy the given value
this.label = _value;
}
});}).call(this);
var et2_valueWidget = /** @class */ (function (_super) {
__extends(et2_valueWidget, _super);
/**
* Constructor
*/
function et2_valueWidget(_parent, _attrs, _child) {
var _this =
// Call the inherited constructor
_super.call(this, _parent, _attrs, et2_core_inheritance_1.ClassWithAttributes.extendAttributes(et2_valueWidget._attributes, _child || {})) || this;
_this.label = '';
_this._labelContainer = null;
return _this;
}
/**
*
* @param _attrs
*/
et2_valueWidget.prototype.transformAttributes = function (_attrs) {
_super.prototype.transformAttributes.call(this, _attrs);
if (this.id) {
// Set the value for this element
var contentMgr = this.getArrayMgr("content");
if (contentMgr != null) {
var val = contentMgr.getEntry(this.id, false, true);
if (val !== null) {
_attrs["value"] = val;
}
}
// Check for already inside namespace
if (this._createNamespace() && this.getArrayMgr("content").perspectiveData.owner == this) {
_attrs["value"] = this.getArrayMgr("content").data;
}
}
};
et2_valueWidget.prototype.set_label = function (_value) {
// Abort if there was no change in the label
if (_value == this.label) {
return;
}
if (_value) {
// Create the label container if it didn't exist yet
if (this._labelContainer == null) {
this._labelContainer = jQuery(document.createElement("label"))
.addClass("et2_label");
this.getSurroundings().insertDOMNode(this._labelContainer[0]);
}
// Clear the label container.
this._labelContainer.empty();
// Create the placeholder element and set it
var ph = document.createElement("span");
this.getSurroundings().setWidgetPlaceholder(ph);
// Split the label at the "%s"
var parts = et2_csvSplit(_value, 2, "%s");
// Update the content of the label container
for (var i = 0; i < parts.length; i++) {
if (parts[i]) {
this._labelContainer.append(document.createTextNode(parts[i]));
}
if (i == 0) {
this._labelContainer.append(ph);
}
}
// add class if label is empty
this._labelContainer.toggleClass('et2_label_empty', !_value || !parts[0]);
}
else {
// Delete the labelContainer from the surroundings object
if (this._labelContainer) {
this.getSurroundings().removeDOMNode(this._labelContainer[0]);
}
this._labelContainer = null;
}
// Update the surroundings in order to reflect the change in the label
this.getSurroundings().update();
// Copy the given value
this.label = _value;
};
et2_valueWidget._attributes = {
"label": {
"name": "Label",
"default": "",
"type": "string",
"description": "The label is displayed by default in front (for radiobuttons behind) each widget (if not empty). If you want to specify a different position, use a '%s' in the label, which gets replaced by the widget itself. Eg. '%s Name' to have the label Name behind a checkbox. The label can contain variables, as descript for name. If the label starts with a '@' it is replaced by the value of the content-array at this index (with the '@'-removed and after expanding the variables).",
"translate": true
},
"value": {
"name": "Value",
"description": "The value of the widget",
"type": "rawstring",
"default": et2_no_init
}
};
return et2_valueWidget;
}(et2_core_baseWidget_1.et2_baseWidget));
exports.et2_valueWidget = et2_valueWidget;
//# sourceMappingURL=et2_core_valueWidget.js.map

View File

@ -0,0 +1,147 @@
/**
* EGroupware eTemplate2 - JS widget class with value attribute and auto loading
*
* @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
* @version $Id$
*/
/*egw:uses
/vendor/bower-asset/jquery/dist/jquery.js;
et2_core_baseWidget;
*/
import { et2_baseWidget } from './et2_core_baseWidget'
import './et2_core_common';
import {WidgetConfig} from "./et2_core_widget";
import {ClassWithAttributes} from "./et2_core_inheritance";
/**
* et2_valueWidget is the base class for et2_inputWidget - valueWidget introduces
* the "value" attribute and automatically loads it from the "content" array
* after loading from XML.
*/
export class et2_valueWidget extends et2_baseWidget
{
static readonly _attributes : any = {
"label": {
"name": "Label",
"default": "",
"type": "string",
"description": "The label is displayed by default in front (for radiobuttons behind) each widget (if not empty). If you want to specify a different position, use a '%s' in the label, which gets replaced by the widget itself. Eg. '%s Name' to have the label Name behind a checkbox. The label can contain variables, as descript for name. If the label starts with a '@' it is replaced by the value of the content-array at this index (with the '@'-removed and after expanding the variables).",
"translate": true
},
"value": {
"name": "Value",
"description": "The value of the widget",
"type": "rawstring", // no html-entity decoding
"default": et2_no_init
}
};
label: string = '';
protected _labelContainer: JQuery = null;
/**
* Constructor
*/
constructor(_parent, _attrs? : WidgetConfig, _child? : object)
{
// Call the inherited constructor
super(_parent, _attrs, ClassWithAttributes.extendAttributes(et2_valueWidget._attributes, _child || {}));
}
/**
*
* @param _attrs
*/
transformAttributes(_attrs : object)
{
super.transformAttributes(_attrs);
if (this.id)
{
// Set the value for this element
var contentMgr = this.getArrayMgr("content");
if (contentMgr != null) {
let val = contentMgr.getEntry(this.id, false, true);
if (val !== null)
{
_attrs["value"] = val;
}
}
// Check for already inside namespace
if(this._createNamespace() && this.getArrayMgr("content").perspectiveData.owner == this)
{
_attrs["value"] = this.getArrayMgr("content").data;
}
}
}
set_label(_value : string)
{
// Abort if there was no change in the label
if (_value == this.label)
{
return;
}
if (_value)
{
// Create the label container if it didn't exist yet
if (this._labelContainer == null)
{
this._labelContainer = jQuery(document.createElement("label"))
.addClass("et2_label");
this.getSurroundings().insertDOMNode(this._labelContainer[0]);
}
// Clear the label container.
this._labelContainer.empty();
// Create the placeholder element and set it
var ph = document.createElement("span");
this.getSurroundings().setWidgetPlaceholder(ph);
// Split the label at the "%s"
var parts = et2_csvSplit(_value, 2, "%s");
// Update the content of the label container
for (var i = 0; i < parts.length; i++)
{
if (parts[i])
{
this._labelContainer.append(document.createTextNode(parts[i]));
}
if (i == 0)
{
this._labelContainer.append(ph);
}
}
// add class if label is empty
this._labelContainer.toggleClass('et2_label_empty', !_value || !parts[0]);
}
else
{
// Delete the labelContainer from the surroundings object
if (this._labelContainer)
{
this.getSurroundings().removeDOMNode(this._labelContainer[0]);
}
this._labelContainer = null;
}
// Update the surroundings in order to reflect the change in the label
this.getSurroundings().update();
// Copy the given value
this.label = _value;
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -6,10 +6,7 @@
* @subpackage api
* @link http://www.egroupware.org
* @author Andreas Stöckel
* @copyright Stylite 2011
* @version $Id$
*/
/**
* Loads the given URL asynchronously from the server
*
@ -21,84 +18,64 @@
* @param {object} _context for _callback
* @param {function} _fail_callback function(_xml)
*/
function et2_loadXMLFromURL(_url, _callback, _context, _fail_callback)
{
if (typeof _context == "undefined")
{
_context = null;
}
// use window object from main window with same algorithm as for the template cache
var win;
try {
if (opener && opener.etemplate2)
{
win = opener;
}
}
catch (e) {
// catch security exception if opener is from a different domain
}
if (typeof win == "undefined")
{
win = top;
}
win.jQuery.ajax({
// we add the full url (protocol and domain) as sometimes just the path
// gives a CSP error interpreting it as file:///path
// (if there are a enough 404 errors in html content ...)
url: (_url[0]=='/' ? location.protocol+'//'+location.host : '')+_url,
context: _context,
type: 'GET',
dataType: 'xml',
success: function(_data, _status, _xmlhttp){
_callback.call(_context, _data.documentElement);
},
error: function(_xmlhttp, _err) {
egw().debug('error', 'Loading eTemplate from '+_url+' failed! '+_xmlhttp.status+' '+_xmlhttp.statusText);
if(typeof _fail_callback !== 'undefined')
{
_fail_callback.call(_context, _err);
}
}
});
function et2_loadXMLFromURL(_url, _callback, _context, _fail_callback) {
if (typeof _context == "undefined") {
_context = null;
}
// use window object from main window with same algorithm as for the template cache
var win;
try {
if (opener && opener.etemplate2) {
win = opener;
}
}
catch (e) {
// catch security exception if opener is from a different domain
}
if (typeof win == "undefined") {
win = top;
}
win.jQuery.ajax({
// we add the full url (protocol and domain) as sometimes just the path
// gives a CSP error interpreting it as file:///path
// (if there are a enough 404 errors in html content ...)
url: (_url[0] == '/' ? location.protocol + '//' + location.host : '') + _url,
context: _context,
type: 'GET',
dataType: 'xml',
success: function (_data, _status, _xmlhttp) {
_callback.call(_context, _data.documentElement);
},
error: function (_xmlhttp, _err) {
egw().debug('error', 'Loading eTemplate from ' + _url + ' failed! ' + _xmlhttp.status + ' ' + _xmlhttp.statusText);
if (typeof _fail_callback !== 'undefined') {
_fail_callback.call(_context, _err);
}
}
});
}
function et2_directChildrenByTagName(_node, _tagName)
{
// Normalize the tag name
_tagName = _tagName.toLowerCase();
var result = [];
for (var i = 0; i < _node.childNodes.length; i++)
{
if (_tagName == _node.childNodes[i].nodeName.toLowerCase())
{
result.push(_node.childNodes[i]);
}
}
return result;
function et2_directChildrenByTagName(_node, _tagName) {
// Normalize the tag name
_tagName = _tagName.toLowerCase();
var result = [];
for (var i = 0; i < _node.childNodes.length; i++) {
if (_tagName == _node.childNodes[i].nodeName.toLowerCase()) {
result.push(_node.childNodes[i]);
}
}
return result;
}
function et2_filteredNodeIterator(_node, _callback, _context)
{
for (var i = 0; i < _node.childNodes.length; i++)
{
var node = _node.childNodes[i];
var nodeName = node.nodeName.toLowerCase();
if (nodeName.charAt(0) != "#")
{
_callback.call(_context, node, nodeName);
}
}
function et2_filteredNodeIterator(_node, _callback, _context) {
for (var i = 0; i < _node.childNodes.length; i++) {
var node = _node.childNodes[i];
var nodeName = node.nodeName.toLowerCase();
if (nodeName.charAt(0) != "#") {
_callback.call(_context, node, nodeName);
}
}
}
function et2_readAttrWithDefault(_node, _name, _default)
{
var val = _node.getAttribute(_name);
return (val === null) ? _default : val;
function et2_readAttrWithDefault(_node, _name, _default) {
var val = _node.getAttribute(_name);
return (val === null) ? _default : val;
}
//# sourceMappingURL=et2_core_xml.js.map

View File

@ -0,0 +1,103 @@
/**
* EGroupware eTemplate2 - JS XML Code
*
* @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
*/
/**
* Loads the given URL asynchronously from the server
*
* We make the Ajax call through main-windows jQuery object, to ensure cached copy
* in main-windows etemplate2 prototype works in IE too!
*
* @param {string} _url
* @param {function} _callback function(_xml)
* @param {object} _context for _callback
* @param {function} _fail_callback function(_xml)
*/
function et2_loadXMLFromURL(_url : string, _callback : Function, _context : object, _fail_callback : Function)
{
if (typeof _context == "undefined")
{
_context = null;
}
// use window object from main window with same algorithm as for the template cache
let win;
try {
if (opener && opener.etemplate2)
{
win = opener;
}
}
catch (e) {
// catch security exception if opener is from a different domain
}
if (typeof win == "undefined")
{
win = top;
}
win.jQuery.ajax({
// we add the full url (protocol and domain) as sometimes just the path
// gives a CSP error interpreting it as file:///path
// (if there are a enough 404 errors in html content ...)
url: (_url[0]=='/' ? location.protocol+'//'+location.host : '')+_url,
context: _context,
type: 'GET',
dataType: 'xml',
success: function(_data, _status, _xmlhttp){
_callback.call(_context, _data.documentElement);
},
error: function(_xmlhttp, _err) {
egw().debug('error', 'Loading eTemplate from '+_url+' failed! '+_xmlhttp.status+' '+_xmlhttp.statusText);
if(typeof _fail_callback !== 'undefined')
{
_fail_callback.call(_context, _err);
}
}
});
}
function et2_directChildrenByTagName(_node : HTMLElement, _tagName : String) : HTMLElement[]
{
// Normalize the tag name
_tagName = _tagName.toLowerCase();
let result = [];
for (let i = 0; i < _node.childNodes.length; i++)
{
if (_tagName == _node.childNodes[i].nodeName.toLowerCase())
{
result.push(_node.childNodes[i]);
}
}
return result;
}
function et2_filteredNodeIterator(_node : HTMLElement, _callback : Function, _context : object)
{
for (let i = 0; i < _node.childNodes.length; i++)
{
let node = _node.childNodes[i];
let nodeName = node.nodeName.toLowerCase();
if (nodeName.charAt(0) != "#")
{
_callback.call(_context, node, nodeName);
}
}
}
function et2_readAttrWithDefault(_node : HTMLElement, _name : string, _default : string) : string
{
let val = _node.getAttribute(_name);
return (val === null) ? _default : val;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,669 @@
/**
* EGroupware eTemplate2 - dataview code
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage dataview
* @link http://www.egroupware.org
* @author Andreas Stöckel
* @copyright Stylite 2011-2012
* @version $Id$
*
/*egw:uses
/vendor/bower-asset/jquery/dist/jquery.js;
et2_core_common;
et2_dataview_model_columns;
et2_dataview_view_grid;
et2_dataview_view_rowProvider;
et2_dataview_view_resizeable;
*/
import {et2_dataview_columns} from './et2_dataview_model_columns';
import {et2_dataview_view_resizable} from "./et2_dataview_view_resizeable";
import {et2_dataview_grid} from "./et2_dataview_view_grid";
import {et2_dataview_rowProvider} from "./et2_dataview_view_rowProvider"
/**
* The et2_dataview class is the main class for displaying a dataview. The
* dataview class manages the creation of the outer html nodes (like the table,
* header, etc.) and contains the root container: an instance of
* et2_dataview_view_grid, which can be accessed using the "grid" property of
* this object.
*
* @augments Class
*/
export class et2_dataview
{
/**
* Constant which regulates the column padding.
*/
columnPadding: number;
/**
* Some browser dependant variables which will be calculated on creation of
* the first gridContainer object.
*/
scrollbarWidth: number;
headerBorderWidth: number;
columnBorderWidth: number;
private width: number;
private height: number;
private uniqueId: string;
/**
* Hooks to allow parent to keep up to date if things change
*/
onUpdateColumns: Function;
selectColumnsClick: Function;
private parentNode: JQuery;
egw: any;
private columnNodes: any[];
private columns: any[];
private columnMgr: et2_dataview_columns;
private rowProvider: et2_dataview_rowProvider;
private grid: et2_dataview_grid;
// DOM stuff
private selectColIcon: JQuery;
private headTr: any;
private containerTr: JQuery;
private selectCol: JQuery;
private thead: JQuery;
private tbody: JQuery;
private table: JQuery;
private visibleColumnCount: number;
/**
* Constructor for the grid container
*
* @param {DOMElement} _parentNode is the DOM-Node into which the grid view will be inserted
* @param {egw} _egw
* @memberOf et2_dataview
*/
constructor(_parentNode, _egw) {
// Copy the arguments
this.parentNode = jQuery(_parentNode);
this.egw = _egw;
// Initialize some variables
this.columnNodes = []; // Array with the header containers
this.columns = [];
this.columnMgr = null;
this.rowProvider = null;
this.width = 0;
this.height = 0;
this.uniqueId = "gridCont_" + this.egw.uid();
// Build the base nodes
this._createElements();
// Read the browser dependant variables
this._getDepVars();
}
/**
* Destroys the object, removes all dom nodes and clears all references.
*/
destroy()
{
// Clear the columns
this._clearHeader();
// Free the grid
if (this.grid)
{
this.grid.destroy();
}
// Free the row provider
if (this.rowProvider)
{
this.rowProvider.destroy();
}
// Detatch the outer element
this.table.remove();
}
/**
* Clears all data rows and reloads them
*/
clear()
{
if (this.grid)
{
this.grid.clear();
}
}
/**
* Returns the column container node for the given column index
*
* @param _columnIdx the integer column index
*/
getHeaderContainerNode(_columnIdx)
{
if (typeof this.columnNodes[_columnIdx] != "undefined")
{
return this.columnNodes[_columnIdx].container[0];
}
return null;
}
/**
* Sets the column descriptors and creates the column header according to it.
* The inner grid will be emptied if it has already been built.
*/
setColumns(_columnData)
{
// Free all column objects which have been created till this moment
this._clearHeader();
// Copy the given column data
this.columnMgr = new et2_dataview_columns(_columnData);
// Create the stylesheets
this.updateColumns();
// Build the header row
this._buildHeader();
// Build the grid
this._buildGrid();
}
/**
* Resizes the grid
*/
resize(_w: number, _h: number)
{
// Not fully initialized yet...
if (!this.columnMgr) return;
if (this.width != _w)
{
this.width = _w;
// Take grid border width into account
_w -= (this.table.outerWidth(true) - this.table.innerWidth());
// Take grid header border's width into account. eg. category colors may add extra pixel into width
_w = _w - (this.thead.find('tr').outerWidth() - this.thead.find('tr').innerWidth());
// Rebuild the column stylesheets
this.columnMgr.setTotalWidth(_w - this.scrollbarWidth);
this._updateColumns();
}
if (this.height != _h)
{
this.height = _h;
// Set the height of the grid.
if (this.grid)
{
this.grid.setScrollHeight(this.height -
this.headTr.outerHeight(true));
}
}
}
/**
* Returns the column manager object. You can use it to set the visibility
* of columns etc. Call "updateHeader" if you did any changes.
*/
getColumnMgr() {
return this.columnMgr;
}
/**
* Recalculates the stylesheets which determine the column visibility and
* width.
*
* @param setDefault boolean Allow admins to save current settings as default for all users
*/
updateColumns(setDefault : boolean = false)
{
if (this.columnMgr)
{
this._updateColumns();
}
// Ability to notify parent / someone else
if (this.onUpdateColumns)
{
this.onUpdateColumns(setDefault);
}
}
/* --- PRIVATE FUNCTIONS --- */
/* --- Code for building the grid container DOM-Tree elements ---- */
/**
* Builds the base DOM-Tree elements
*/
private _createElements()
{
/*
Structure:
<table class="egwGridView_outer">
<thead>
<tr> [HEAD] </tr>
</thead>
<tbody>
<tr> [GRID CONTAINER] </tr>
</tbody>
</table>
*/
this.containerTr = jQuery(document.createElement("tr"));
this.headTr = jQuery(document.createElement("tr"));
this.thead = jQuery(document.createElement("thead"))
.append(this.headTr);
this.tbody = jQuery(document.createElement("tbody"))
.append(this.containerTr);
this.table = jQuery(document.createElement("table"))
.addClass("egwGridView_outer")
.append(this.thead, this.tbody)
.appendTo(this.parentNode);
}
/* --- Code for building the header row --- */
/**
* Clears the header row
*/
private _clearHeader ()
{
if (this.columnMgr)
{
this.columnMgr.destroy();
this.columnMgr = null;
}
// Remove dynamic CSS,
for (var i = 0; i < this.columns.length; i++)
{
if(this.columns[i].tdClass)
{
this.egw.css('.'+this.columns[i].tdClass);
}
if(this.columns[i].divClass)
{
this.egw.css('.'+this.columns[i].divClass);
this.egw.css(".egwGridView_outer ." + this.columns[i].divClass);
this.egw.css(".egwGridView_grid ." + this.columns[i].divClass);
}
}
this.egw.css(".egwGridView_grid ." + this.uniqueId + "_div_fullRow");
this.egw.css(".egwGridView_outer ." + this.uniqueId + "_td_fullRow");
this.egw.css(".egwGridView_outer ." + this.uniqueId + "_spacer_fullRow");
// Reset the headerColumns array and empty the table row
this.columnNodes = [];
this.columns = [];
this.headTr.empty();
}
/**
* Sets the column data which is retrieved by calling egwGridColumns.getColumnData.
* The columns will be updated.
*/
private _updateColumns()
{
// Copy the columns data
this.columns = this.columnMgr.getColumnData();
// Count the visible rows
var total_cnt = 0;
for (var i = 0; i < this.columns.length; i++)
{
if (this.columns[i].visible)
{
total_cnt++;
}
}
// Set the grid column styles
var first = true;
var vis_col = this.visibleColumnCount = 0;
var totalWidth = 0;
for (var i = 0; i < this.columns.length; i++)
{
var col = this.columns[i];
col.tdClass = this.uniqueId + "_td_" + col.id;
col.divClass = this.uniqueId + "_div_" + col.id;
if (col.visible)
{
vis_col++;
this.visibleColumnCount++;
// Update the visibility of the column
this.egw.css("." + col.tdClass,
"display: table-cell; " +
"!important;");
// Ugly browser dependant code - each browser seems to treat the
// right (collapsed) border of the row differently
var subBorder = 0;
var subHBorder = 0;
/*
if (jQuery.browser.mozilla)
{
var maj = jQuery.browser.version.split(".")[0];
if (maj < 2) {
subBorder = 1; // Versions <= FF 3.6
}
}
if (jQuery.browser.webkit)
{
if (!first)
{
subBorder = 1;
}
subHBorder = 1;
}
if ((jQuery.browser.msie || jQuery.browser.opera) && first)
{
subBorder = -1;
}
*/
// Make the last columns one pixel smaller, to prevent a horizontal
// scrollbar from showing up
if (vis_col == total_cnt)
{
subBorder += 1;
}
// Write the width of the header columns
var headerWidth = Math.max(0, (col.width - this.headerBorderWidth - subHBorder));
this.egw.css(".egwGridView_outer ." + col.divClass,
"width: " + headerWidth + "px;");
// Write the width of the body-columns
var columnWidth = Math.max(0, (col.width - this.columnBorderWidth - subBorder));
this.egw.css(".egwGridView_grid ." + col.divClass,
"width: " + columnWidth + "px;");
totalWidth += col.width;
first = false;
}
else
{
this.egw.css("." + col.tdClass, "display: none;");
}
}
// Add the full row and spacer class
this.egw.css(".egwGridView_grid ." + this.uniqueId + "_div_fullRow",
"width: " + (totalWidth - this.columnBorderWidth - 2) + "px; border-right-width: 0 !important;");
this.egw.css(".egwGridView_outer ." + this.uniqueId + "_td_fullRow",
"border-right-width: 0 !important;");
this.egw.css(".egwGridView_outer ." + this.uniqueId + "_spacer_fullRow",
"width: " + (totalWidth - 1) + "px; border-right-width: 0 !important;");
}
/**
* Builds the containers for the header row
*/
private _buildHeader()
{
var self = this;
var handler = function(event) {
};
for (var i = 0; i < this.columns.length; i++)
{
var col = this.columns[i];
// Create the column header and the container element
var cont = jQuery(document.createElement("div"))
.addClass("innerContainer")
.addClass(col.divClass);
var column = jQuery(document.createElement("th"))
.addClass(col.tdClass)
.attr("align", "left")
.append(cont)
.appendTo(this.headTr);
if(this.columnMgr && this.columnMgr.getColumnById(i))
{
column.addClass(this.columnMgr.getColumnById(i).fixedWidth ? 'fixedWidth' : 'relativeWidth');
if(this.columnMgr.getColumnById(i).visibility === et2_dataview_column.ET2_COL_VISIBILITY_ALWAYS_NOSELECT)
{
column.addClass('noResize');
}
}
// make column resizable
var enc_column = self.columnMgr.getColumnById(col.id);
if(enc_column.visibility !== et2_dataview_column.ET2_COL_VISIBILITY_ALWAYS_NOSELECT)
{
et2_dataview_view_resizable.makeResizeable(column, function(_w) {
// User wants the column to stay where they put it, even for relative
// width columns, so set it explicitly first and adjust other relative
// columns to match.
if(this.relativeWidth)
{
// Set to selected width
this.set_width(_w + "px");
self.columnMgr.updated();
// Just triggers recalculation
self.columnMgr.getColumnWidth(0);
// Set relative widths to match
var relative = self.columnMgr.totalWidth - self.columnMgr.totalFixed + _w;
this.set_width(_w / relative);
for(var i = 0; i < self.columnMgr.columnCount(); i++)
{
var col = self.columnMgr.getColumnById(i);
if(col == this || col.fixedWidth) continue;
col.set_width(self.columnMgr.getColumnWidth(i) / relative);
}
// Triggers column change callback, which saves
self.updateColumns();
}
else
{
this.set_width(this.relativeWidth ? (_w / self.columnMgr.totalWidth) : _w + "px");
self.columnMgr.updated();
self.updateColumns();
}
}, enc_column);
}
// Store both nodes in the columnNodes array
this.columnNodes.push({
"column": column,
"container": cont
});
}
this._buildSelectCol();
}
/**
* Builds the select cols column
*/
private _buildSelectCol()
{
// Build the "select columns" icon
this.selectColIcon = jQuery(document.createElement("span"))
.addClass("selectcols")
.css('display', 'inline-block'); // otherwise jQuery('span.selectcols',this.dataview.headTr).show() set it to "inline" causing it to not show up because 0 height
// Build the option column
this.selectCol = jQuery(document.createElement("th"))
.addClass("optcol")
.append(this.selectColIcon)
// Toggle display of option popup
.click(this, function(e) {if(e.data.selectColumnsClick) e.data.selectColumnsClick(e);})
.appendTo(this.headTr);
this.selectCol.css("width", this.scrollbarWidth - this.selectCol.outerWidth()
+ this.selectCol.width() + 1);
}
/**
* Builds the inner grid class
*/
private _buildGrid()
{
// Create the collection of column ids
var colIds = new Array(this.columns.length);
for (var i = 0; i < this.columns.length; i++)
{
colIds[i] = this.columns[i].id;
}
// Create the row provider
if (this.rowProvider)
{
this.rowProvider.destroy();
}
this.rowProvider = new et2_dataview_rowProvider(this.uniqueId, colIds);
// Create the grid class and pass "19" as the starting average row height
this.grid = new et2_dataview_grid(null, null, this.egw, this.rowProvider, 19);
// Insert the grid into the DOM-Tree
var tr = jQuery(this.grid.getFirstNode());
this.containerTr.replaceWith(tr);
this.containerTr = tr;
}
/* --- Code for calculating the browser/css depending widths --- */
/**
* Reads the browser dependant variables
*/
private _getDepVars()
{
if (typeof this.scrollbarWidth === 'undefined')
{
// Clone the table and attach it to the outer body tag
var clone = this.table.clone();
jQuery(window.top.document.getElementsByTagName("body")[0])
.append(clone);
// Read the scrollbar width
this.scrollbarWidth = this.constructor.prototype.scrollbarWidth =
this._getScrollbarWidth(clone);
// Read the header border width
this.headerBorderWidth = this.constructor.prototype.headerBorderWidth =
this._getHeaderBorderWidth(clone);
// Read the column border width
this.columnBorderWidth = this.constructor.prototype.columnBorderWidth =
this._getColumnBorderWidth(clone);
// Remove the cloned DOM-Node again from the outer body
clone.remove();
}
}
/**
* Reads the scrollbar width
*/
private _getScrollbarWidth(_table: JQuery)
{
// Create a temporary td and two divs, which are inserted into the
// DOM-Tree. The outer div has a fixed size and "overflow" set to auto.
// When the second div is inserted, it will be forced to display a scrollbar.
var div_inner = jQuery(document.createElement("div"))
.css("height", "1000px");
var div_outer = jQuery(document.createElement("div"))
.css("height", "100px")
.css("width", "100px")
.css("overflow", "auto")
.append(div_inner);
var td = jQuery(document.createElement("td"))
.append(div_outer);
// Store the scrollbar width statically.
jQuery("tbody tr", _table).append(td);
var width = Math.max(10, div_outer.outerWidth() - div_inner.outerWidth());
// Remove the elements again
div_outer.remove();
return width;
}
/**
* Calculates the total width of the header column border
*/
private _getHeaderBorderWidth(_table: JQuery)
{
// Create a temporary th which is appended to the outer thead row
var cont = jQuery(document.createElement("div"))
.addClass("innerContainer");
var th = jQuery(document.createElement("th"))
.append(cont);
// Insert the th into the document tree
jQuery("thead tr", _table).append(th);
// Calculate the total border width
var width = th.outerWidth(true) - cont.width();
// Remove the appended element again
th.remove();
return width;
}
/**
* Calculates the total width of the column border
*/
private _getColumnBorderWidth(_table: JQuery)
{
// Create a temporary th which is appended to the outer thead row
var cont = jQuery(document.createElement("div"))
.addClass("innerContainer");
var td = jQuery(document.createElement("td"))
.append(cont);
// Insert the th into the document tree
jQuery("tbody tr", _table).append(td);
// Calculate the total border width
_table.addClass("egwGridView_grid");
var width = td.outerWidth(true) - cont.width();
// Remove the appended element again
td.remove();
return width;
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,720 @@
/**
* EGroupware eTemplate2
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage dataview
* @link http://www.egroupware.org
* @author Andreas Stöckel
* @copyright Stylite 2011-2012
*
/*egw:uses
et2_dataview_view_aoi;
egw_action.egw_keymanager;
*/
/**
* The selectioManager is internally used by the et2_dataview_controller class
* to manage the row selection.
* As the action system does not allow selection of entries which are currently
* not in the dom tree, we have to manage this in this class. The idea is to
* manage an external action object interface for each visible row and proxy all
* state changes between an dummy action object, that does no selection handling,
* and the external action object interface.
*
* @augments Class
*/
export class et2_dataview_selectionManager
{
// Maximum number of rows we can safely fetch for selection
// Actual selection may have more rows if we already have some
public static readonly MAX_SELECTION = 1000;
private _parent: any;
private _indexMap: any;
private _actionObjectManager: any;
private _makeVisibleCallback: any;
private _queryRangeCallback: any;
private select_callback: null;
private _context: any;
private _registeredRows: {};
private _focusedEntry: null;
private _invertSelection: boolean;
private _selectAll: boolean;
private _inUpdate: boolean;
private _total: number;
private _children: any[];
/**
* Constructor
*
* @param _parent
* @param _indexMap
* @param _actionObjectManager
* @param _queryRangeCallback
* @param _makeVisibleCallback
* @param _context
* @memberOf et2_dataview_selectionManager
*/
constructor(_parent, _indexMap, _actionObjectManager,
_queryRangeCallback, _makeVisibleCallback, _context)
{
// Copy the arguments
this._parent = _parent;
this._indexMap = _indexMap;
this._actionObjectManager = _actionObjectManager;
this._queryRangeCallback = _queryRangeCallback;
this._makeVisibleCallback = _makeVisibleCallback;
this._context = _context;
// Attach this manager to the parent manager if one is given
if (this._parent)
{
this._parent._children.push(this);
}
// Use our selection instead of object manager's to handle not-loaded rows
if(_actionObjectManager)
{
this._actionObjectManager.getAllSelected = jQuery.proxy(
this.getAllSelected, this
);
}
// Internal map which contains all curently selected uids and their
// state
this._registeredRows = {};
this._focusedEntry = null;
this._invertSelection = false;
this._selectAll = false;
this._inUpdate = false;
this._total = 0;
this._children = [];
// Callback for when the selection changes
this.select_callback = null;
}
destroy( )
{
// If we have a parent, unregister from that
if (this._parent)
{
var idx = this._parent._children.indexOf(this);
this._parent._children.splice(idx, 1);
}
// Destroy all children
for (var i = this._children.length - 1; i >= 0; i--)
{
this._children[i].destroy();
}
// Delete all still registered rows
for (var key in this._registeredRows)
{
this.unregisterRow(key, this._registeredRows[key].tr);
}
this.select_callback = null;
}
clear( )
{
for (var key in this._registeredRows)
{
this.unregisterRow(key, this._registeredRows[key].tr);
delete this._registeredRows[key];
}
if(this._actionObjectManager)
{
this._actionObjectManager.clear();
}
for (key in this._indexMap) {
delete this._indexMap[key];
}
this._total = 0;
this._focusedEntry = null;
this._invertSelection = false;
this._selectAll = false;
this._inUpdate = false;
}
setIndexMap( _indexMap)
{
this._indexMap = _indexMap;
}
setTotalCount( _total)
{
this._total = _total;
}
registerRow( _uid, _idx, _tr, _links)
{
// Get the corresponding entry from the registered rows array
var entry = this._getRegisteredRowsEntry(_uid);
// If the row has changed unregister the old one and do not delete
// entry from the entry map
if (entry.tr && entry.tr !== _tr) {
this.unregisterRow(_uid, entry.tr, true);
}
// Create the AOI for the tr
if (!entry.tr && _links)
{
this._attachActionObjectInterface(entry, _tr, _uid);
this._attachActionObject(entry, _tr, _uid, _links, _idx);
}
// Update the entry
if(entry.ao) entry.ao._index;
entry.idx = _idx;
entry.tr = _tr;
// Update the visible state of the _tr
this._updateEntryState(entry, entry.state);
}
unregisterRow( _uid, _tr, _noDelete? : boolean)
{
// _noDelete defaults to false
_noDelete = _noDelete ? true : false;
if (typeof this._registeredRows[_uid] !== "undefined"
&& this._registeredRows[_uid].tr === _tr)
{
this._inUpdate = true;
this._registeredRows[_uid].tr = null;
this._registeredRows[_uid].aoi = null;
// Remove the action object from its container
if (this._registeredRows[_uid].ao)
{
this._registeredRows[_uid].ao.remove();
this._registeredRows[_uid].ao = null;
}
if (!_noDelete
&& this._registeredRows[_uid].state === EGW_AO_STATE_NORMAL)
{
delete this._registeredRows[_uid];
}
this._inUpdate = false;
}
}
resetSelection( )
{
this._invertSelection = false;
this._selectAll = false;
this._actionObjectManager.setAllSelected(false);
for (var key in this._registeredRows)
{
this.setSelected(key, false);
}
for(var i = 0; i < this._children.length; i++)
{
this._children[i].resetSelection();
}
}
setSelected( _uid, _selected)
{
this._selectAll = false;
var entry = this._getRegisteredRowsEntry(_uid);
this._updateEntryState(entry,
egwSetBit(entry.state, EGW_AO_STATE_SELECTED, _selected));
}
getAllSelected()
{
var selected = this.getSelected();
return selected.all || (selected.ids.length === this._total);
}
setFocused( _uid, _focused)
{
// Reset the state of the currently focused entry
if (this._focusedEntry)
{
this._updateEntryState(this._focusedEntry,
egwSetBit(this._focusedEntry.state, EGW_AO_STATE_FOCUSED,
false));
this._focusedEntry = null;
}
// Mark the new given uid as focused
if (_focused)
{
//console.log('et2_dataview_controller_selection::setFocused -> UID:'+_uid+' is focused by:'+this._actionObjectManager.name);
var entry = this._focusedEntry = this._getRegisteredRowsEntry(_uid);
this._updateEntryState(entry,
egwSetBit(entry.state, EGW_AO_STATE_FOCUSED, true));
}
}
selectAll( )
{
// Reset the selection
this.resetSelection();
this._selectAll = true;
// Run as a range if there's less then the max
if(egw.dataKnownUIDs(this._context._dataProvider.dataStorePrefix).length !== this._total &&
this._total <= et2_dataview_selectionManager.MAX_SELECTION
)
{
this._selectRange(0, this._total);
}
// Tell action manager to do all
this._actionObjectManager.setAllSelected(true);
// Update the selection
for (var key in this._registeredRows)
{
var entry = this._registeredRows[key];
this._updateEntryState(entry, entry.state);
}
this._selectAll = true;
}
getSelected( )
{
// Collect all currently selected ids
var ids = [];
for (var key in this._registeredRows)
{
if (egwBitIsSet(this._registeredRows[key].state, EGW_AO_STATE_SELECTED))
{
ids.push(key);
}
}
// Push all events of the child managers onto the list
for (var i = 0; i < this._children.length; i++)
{
ids = ids.concat(this._children[i].getSelected().ids);
}
// Return an array containing those ids
// RB: we are currently NOT using "inverted"
return {
//"inverted": this._invertSelection,
"all": this._selectAll,
"ids": ids
};
}
/** -- PRIVATE FUNCTIONS -- **/
_attachActionObjectInterface( _entry, _tr, _uid)
{
// Create the AOI which is used internally in the selection manager
// this AOI is not connected to the AO, as the selection manager
// cares about selection etc.
_entry.aoi = new et2_dataview_rowAOI(_tr);
_entry.aoi.setStateChangeCallback(
function (_newState, _changedBit, _shiftState) {
if (_changedBit === EGW_AO_STATE_SELECTED)
{
// Call the select handler
this._handleSelect(
_uid,
_entry,
egwBitIsSet(_shiftState, EGW_AO_SHIFT_STATE_BLOCK),
egwBitIsSet(_shiftState, EGW_AO_SHIFT_STATE_MULTI)
);
}
}, this);
}
_getDummyAOI( _entry, _tr, _uid, _idx)
{
// Create AOI
var dummyAOI = new egwActionObjectInterface();
var self = this;
// Handling for Action Implementations updating the state
dummyAOI.doSetState = function (_state) {
if (!self._inUpdate)
{
// Update the "focused" flag
self.setFocused(_uid, egwBitIsSet(_state, EGW_AO_STATE_FOCUSED));
// Generally update the state
self._updateState(_uid, _state);
}
};
// Handle the "make visible" event, pass the request to the parent
// controller
dummyAOI.doMakeVisible = function () {
self._makeVisibleCallback.call(self._context, _idx);
};
// Connect the the two AOIs
dummyAOI.doTriggerEvent = _entry.aoi.doTriggerEvent;
// Implementation of the getDOMNode function, so that the event
// handlers can be properly bound
dummyAOI.getDOMNode = function () { return _tr; };
return dummyAOI;
}
_attachActionObject( _entry, _tr, _uid, _links, _idx)
{
// Get the dummyAOI which connects the action object to the tr but
// does no selection handling
var dummyAOI = this._getDummyAOI(_entry, _tr, _uid, _idx);
// Create an action object for the tr and connect it to a dummy AOI
if(this._actionObjectManager)
{
if(this._actionObjectManager.getObjectById(_uid))
{
var state = _entry.state;
this._actionObjectManager.getObjectById(_uid).remove();
_entry.state = state;
}
_entry.ao = this._actionObjectManager.addObject(_uid, dummyAOI);
}
// Force context (actual widget) in here, it's the last place it's available
_entry.ao._context = this._context;
_entry.ao.updateActionLinks(_links);
_entry.ao._index = _idx;
// Overwrite some functions like "traversePath", "getNext" and
// "getPrevious"
var self = this;
function getIndexAO (_idx) {
// Check whether the index is in the index map
if (typeof self._indexMap[_idx] !== "undefined"
&& self._indexMap[_idx].uid)
{
return self._getRegisteredRowsEntry(self._indexMap[_idx].uid).ao;
}
return null;
}
function getElementRelatively (_step) {
var total = self._total || Object.keys(self._indexMap).length;
var max_index = Math.max.apply(Math,Object.keys(self._indexMap));
// Get a reasonable number of iterations - not all
var count = Math.max(1,Math.min(self._total,50));
var element = null;
var idx = _entry.idx;
while(element == null && count > 0 && max_index > 0)
{
count--;
element = getIndexAO(Math.max(0,
Math.min(max_index, idx += _step)));
}
return element;
}
_entry.ao.getPrevious = function (_step) {
return getElementRelatively(-_step);
};
_entry.ao.getNext = function (_step) {
return getElementRelatively(_step);
};
_entry.ao.traversePath = function (_obj) {
// Get the start and the stop index
var s = Math.min(this._index, _obj._index);
var e = Math.max(this._index, _obj._index);
var result = [];
for (var i = s; i < e; i++)
{
var ao = getIndexAO(i);
if (ao)
{
result.push(ao);
}
}
return result;
};
}
_updateState( _uid, _state)
{
var entry = this._getRegisteredRowsEntry(_uid);
this._updateEntryState(entry, _state);
return entry;
}
_updateEntryState( _entry, _state)
{
if (this._selectAll)
{
_state |= EGW_AO_STATE_SELECTED;
}
else if (this._invertSelection)
{
_state ^= EGW_AO_STATE_SELECTED;
}
// Attach ao if not there, happens for rows loaded for selection, but
// not displayed yet
if(!_entry.ao && _entry.uid && this._actionObjectManager)
{
var _links = [];
for (var key in this._registeredRows)
{
if(this._registeredRows[key].ao && this._registeredRows[key].ao.actionLinks)
{
_links = this._registeredRows[key].ao.actionLinks;
break;
}
}
if(_links)
{
this._attachActionObjectInterface(_entry, null, _entry.uid);
this._attachActionObject(_entry, null, _entry.uid, _links, _entry.idx);
}
}
// Update the state if it has changed
if ((_entry.aoi && _entry.aoi.getState() !== _state) || _entry.state != _state)
{
this._inUpdate = true; // Recursion prevention
// Update the state of the action object
if (_entry.ao)
{
_entry.ao.setSelected(egwBitIsSet(_state, EGW_AO_STATE_SELECTED));
_entry.ao.setFocused(egwBitIsSet(_state, EGW_AO_STATE_FOCUSED));
}
this._inUpdate = false;
// Delete the element if state was set to "NORMAL" and there is
// no tr
if (_state === EGW_AO_STATE_NORMAL && !_entry.tr)
{
delete this._registeredRows[_entry.uid];
}
}
// Update the visual state
if (_entry.aoi && _entry.aoi.doSetState)
{
_entry.aoi.doSetState(_state);
}
// Update the state of the entry
_entry.state = _state;
}
_getRegisteredRowsEntry( _uid)
{
if (typeof this._registeredRows[_uid] === "undefined")
{
this._registeredRows[_uid] = {
"uid": _uid,
"idx": null,
"state": EGW_AO_STATE_NORMAL,
"tr": null,
"aoi": null,
"ao": null
};
}
return this._registeredRows[_uid];
}
_handleSelect( _uid, _entry, _shift, _ctrl)
{
// If not "_ctrl" is set, reset the selection
if (!_ctrl)
{
var top = this;
while(top._parent !== null)
{
top = top._parent;
}
top.resetSelection();
this._actionObjectManager.setAllSelected(false); // needed for hirachical stuff
}
// Mark the element that was clicked as selected
var entry = this._getRegisteredRowsEntry(_uid);
this.setSelected(_uid,
!_ctrl || !egwBitIsSet(entry.state, EGW_AO_STATE_SELECTED));
// Focus the element if shift is not pressed
if (!_shift)
{
this.setFocused(_uid, true);
}
else if (this._focusedEntry)
{
this._selectRange(this._focusedEntry.idx, _entry.idx);
}
if(this.select_callback && typeof this.select_callback == "function")
{
this.select_callback.apply(this._context, arguments);
}
}
_selectRange( _start, _stop)
{
// Contains ranges that are not currently in the index map and that have
// to be queried
var queryRanges = [];
// Iterate over the given range and select the elements in the range
// from _start to _stop
var naStart = false;
var s = Math.min(_start, _stop);
var e = Math.max(_stop, _start);
var RANGE_MAX = 50;
var range_break = s + RANGE_MAX;
for (var i = s; i <= e; i++)
{
if (typeof this._indexMap[i] !== "undefined" &&
this._indexMap[i].uid && egw.dataGetUIDdata(this._indexMap[i].uid))
{
// Add the range to the "queryRanges"
if (naStart !== false)
{
queryRanges.push(et2_bounds(naStart, i - 1));
naStart = false;
range_break += RANGE_MAX;
}
// Select the element, unless flagged for exclusion
// Check for no_actions flag via data
var data = egw.dataGetUIDdata(this._indexMap[i].uid);
if(data && data.data && !data.data.no_actions)
{
this.setSelected(this._indexMap[i].uid, true);
}
}
else if (naStart === false)
{
naStart = i;
range_break = naStart + RANGE_MAX;
}
else if(i >= range_break)
{
queryRanges.push(et2_bounds(naStart ? naStart : s, i - 1));
naStart = i;
range_break += RANGE_MAX;
}
}
// Add the last range to the "queryRanges"
if (naStart !== false)
{
queryRanges.push(et2_bounds(naStart, i - 1));
naStart = false;
}
// Query all unknown ranges from the server
if(queryRanges.length > 0)
{
this._query_ranges(queryRanges);
}
}
_query_ranges(queryRanges)
{
var that = this;
var record_count = 0;
var range_index = 0;
var range_count = queryRanges.length;
var cont = true;
var fetchPromise = new Promise(function (resolve) {
resolve();
});
// Found after dialog loads
var progressbar;
var parent = et2_dialog._create_parent();
var dialog = et2_createWidget("dialog", {
callback:
// Abort the long task if they canceled the data load
function() {cont = false},
template: egw.webserverUrl+'/api/templates/default/long_task.xet',
message: egw.lang('Loading'),
title: egw.lang('please wait...'),
buttons: [{"button_id": et2_dialog.CANCEL_BUTTON,"text": egw.lang('cancel'),id: 'dialog[cancel]',image: 'cancel'}],
width: 300
}, parent);
jQuery(dialog.template.DOMContainer).on('load', function() {
// Get access to template widgets
progressbar = dialog.template.widgetContainer.getWidgetById('progressbar');
});
for (var i = 0; i < queryRanges.length; i++)
{
if(record_count + (queryRanges[i].bottom - queryRanges[i].top+1) > that.MAX_SELECTION)
{
egw.message(egw.lang('Too many rows selected.<br />Select all, or less than %1 rows', that.MAX_SELECTION));
break;
}
else
{
record_count += (queryRanges[i].bottom - queryRanges[i].top+1);
fetchPromise = fetchPromise.then((function ()
{
// Check for abort
if(!cont) return;
return new Promise(function(resolve) {
that._queryRangeCallback.call(that._context, this,
function (_order) {
for (var j = 0; j < _order.length; j++)
{
// Check for no_actions flag via data since entry isn't there/available
var data = egw.dataGetUIDdata(_order[j]);
if(!data || data && data.data && !data.data.no_actions)
{
var entry = this._getRegisteredRowsEntry(_order[j]);
this._updateEntryState(entry,
egwSetBit(entry.state, EGW_AO_STATE_SELECTED, true));
}
}
progressbar.set_value(100*(++range_index/range_count));
resolve();
}, that);
}.bind(this));
}).bind(queryRanges[i]));
}
}
fetchPromise.finally(function() {
dialog.destroy();
});
}
}

View File

@ -1,3 +1,4 @@
"use strict";
/**
* EGroupware eTemplate2 - Contains interfaces used inside the dataview
*
@ -9,89 +10,17 @@
* @copyright Stylite 2011
* @version $Id$
*/
/*egw:uses
et2_core_inheritance;
*/
var et2_dataview_IInvalidatable = new Interface({
invalidate: function() {}
});
var et2_dataview_IViewRange = new Interface({
setViewRange: function(_range) {}
});
/**
* Interface a data provider has to implement. The data provider functions are
* called by the et2_dataview_controller class. The data provider basically acts
* like the egw api egw_data extension, but some etemplate specific stuff has
* been stripped away -- the implementation (for the nextmatch widget that is
* et2_extension_nextmatch_dataprovider) has to take care of that.
*/
var et2_IDataProvider = new Interface({
/**
* This function is used by the et2_dataview_controller to fetch data for
* a certain range. The et2_dataview_controller provides data which allows
* to only update elements which really have changed.
*
* @param queriedRange is an object of the following form:
* {
* start: <START INDEX>,
* num_rows: <COUNT OF ENTRIES>
* }
* @param knownRange is an array of the above form and informs the
* implementation which range is already known to the client. This parameter
* may be null in order to indicate that the client currently has no valid
* data.
* @param lastModification is the last timestamp that was returned from the
* data provider and for which the client has data. It may be null in order
* to indicate, that the client currently has no data or needs a complete
* refresh.
* @param callback is the function that should get called, once the data
* is available. The data passed to the callback function has the
* following form:
* {
* order: [uid, ...],
* total: <TOTAL COUNT>,
* lastModification: <LAST MODIFICATION TIMESTAMP>
* }
* @param context is the context in which the callback function will get
* called.
*/
dataFetch: function (_queriedRange, _lastModification, _callback, _context) {},
/**
* Registers the intrest in a certain uid for a callback function. If
* the data for that uid changes or gets loaded, the given callback
* function is called. If the data for the given uid is available at the
* time of registering the callback, the callback is called immediately.
*
* @param _uid is the uid for which the callback should be registered.
* @param _callback is the callback which should get called.
* @param _context is an optional parameter which can
*/
dataRegisterUID: function (_uid, _callback, _context) {},
/**
* Unregisters the intrest of updates for a certain data uid.
*
* @param _uid is the data uid for which the callbacks should be
* unregistered.
* @param _callback specifies the specific callback that should be
* unregistered. If it evaluates to false, all callbacks (or those
* matching the optionally given context) are removed.
* @param _context specifies the callback context that should be
* unregistered. If it evaluates to false, all callbacks (or those
* matching the optionally given callback function) are removed.
*/
dataUnregisterUID: function (_uid, _callback, _context) {}
});
Object.defineProperty(exports, "__esModule", { value: true });
var et2_dataviewIInvalidatable = "et2_dataview_IInvalidatable";
function implements_et2_dataview_IInvalidatable(obj) {
return implements_methods(obj, ["invalidate"]);
}
var et2_dataview_IViewRange = "et2_dataview_IViewRange";
function implements_et2_dataview_IViewRange(obj) {
return implements_methods(obj, ["setViewRange"]);
}
var et2_IDataProvider = "et2_IDataProvider";
function implements_et2_IDataProvider(obj) {
return implements_methods(obj, ["dataFetch", "dataRegisterUID", "dataUnregisterUID"]);
}
//# sourceMappingURL=et2_dataview_interfaces.js.map

View File

@ -0,0 +1,110 @@
/**
* EGroupware eTemplate2 - Contains interfaces used inside the dataview
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage dataview
* @link http://www.egroupware.org
* @author Andreas Stöckel
* @copyright Stylite 2011
* @version $Id$
*/
/*egw:uses
et2_core_inheritance;
*/
export interface et2_dataview_IInvalidatable
{
invalidate()
}
var et2_dataviewIInvalidatable = "et2_dataview_IInvalidatable";
function implements_et2_dataview_IInvalidatable(obj : et2_widget)
{
return implements_methods(obj, ["invalidate"]);
}
export interface et2_dataview_IViewRange
{
setViewRange(_range)
}
var et2_dataview_IViewRange = "et2_dataview_IViewRange";
function implements_et2_dataview_IViewRange(obj : et2_widget)
{
return implements_methods(obj, ["setViewRange"]);
}
/**
* Interface a data provider has to implement. The data provider functions are
* called by the et2_dataview_controller class. The data provider basically acts
* like the egw api egw_data extension, but some etemplate specific stuff has
* been stripped away -- the implementation (for the nextmatch widget that is
* et2_extension_nextmatch_dataprovider) has to take care of that.
*/
export interface et2_IDataProvider
{
/**
* This function is used by the et2_dataview_controller to fetch data for
* a certain range. The et2_dataview_controller provides data which allows
* to only update elements which really have changed.
*
* @param _queriedRange is an object of the following form:
* {
* start: <START INDEX>,
* num_rows: <COUNT OF ENTRIES>
* }
* @param _knownRange is an array of the above form and informs the
* implementation which range is already known to the client. This parameter
* may be null in order to indicate that the client currently has no valid
* data.
* @param _lastModification is the last timestamp that was returned from the
* data provider and for which the client has data. It may be null in order
* to indicate, that the client currently has no data or needs a complete
* refresh.
* @param _callback is the function that should get called, once the data
* is available. The data passed to the callback function has the
* following form:
* {
* order: [uid, ...],
* total: <TOTAL COUNT>,
* lastModification: <LAST MODIFICATION TIMESTAMP>
* }
* @param _context is the context in which the callback function will get
* called.
*/
dataFetch (_queriedRange : {start: number, num_rows:number}, _lastModification, _callback : Function, _context : object)
/**
* Registers the intrest in a certain uid for a callback function. If
* the data for that uid changes or gets loaded, the given callback
* function is called. If the data for the given uid is available at the
* time of registering the callback, the callback is called immediately.
*
* @param _uid is the uid for which the callback should be registered.
* @param _callback is the callback which should get called.
* @param _context is an optional parameter which can
*/
dataRegisterUID (_uid : string, _callback : Function, _context : object)
/**
* Unregisters the intrest of updates for a certain data uid.
*
* @param _uid is the data uid for which the callbacks should be
* unregistered.
* @param _callback specifies the specific callback that should be
* unregistered. If it evaluates to false, all callbacks (or those
* matching the optionally given context) are removed.
* @param _context specifies the callback context that should be
* unregistered. If it evaluates to false, all callbacks (or those
* matching the optionally given callback function) are removed.
*/
dataUnregisterUID (_uid : string, _callback : Function, _context : object)
}
var et2_IDataProvider = "et2_IDataProvider";
function implements_et2_IDataProvider(obj : et2_widget)
{
return implements_methods(obj, ["dataFetch", "dataRegisterUID", "dataUnregisterUID"]);
}

View File

@ -1,3 +1,4 @@
"use strict";
/**
* EGroupware eTemplate2 - Class which contains a the columns model
*
@ -9,451 +10,421 @@
* @copyright Stylite 2011
* @version $Id$
*/
Object.defineProperty(exports, "__esModule", { value: true });
/*egw:uses
et2_inheritance;
et2_core_inheritance;
et2_inheritance;
*/
var ET2_COL_TYPE_DEFAULT = 0;
var ET2_COL_TYPE_NAME_ICON_FIXED = 1;
var ET2_COL_VISIBILITY_ALWAYS = 0;
var ET2_COL_VISIBILITY_VISIBLE = 1;
var ET2_COL_VISIBILITY_INVISIBLE = 2;
var ET2_COL_VISIBILITY_ALWAYS_NOSELECT = 3;
var ET2_COL_VISIBILITY_DISABLED = 4;
/**
* Class which stores the data of a single column.
*
* @augments Class
*/
var et2_dataview_column = (function(){ "use strict"; return ClassWithAttributes.extend({
attributes: {
"id": {
"name": "ID",
"type": "string",
"description": "Unique identifier for this column. It is used to " +
"store changed column widths or visibilities."
},
"visibility": {
"name": "Visibility",
"type": "integer",
"default": ET2_COL_VISIBILITY_VISIBLE,
"description": "Defines the visibility state of this column."
},
"caption": {
"name": "Caption",
"type": "string",
"description": "Caption of the column as it is displayed in the " +
"select columns popup."
},
"type": {
"name": "Column type",
"type": "integer",
"default": ET2_COL_TYPE_DEFAULT,
"description": "Type of the column"
},
"width": {
"name": "Width",
"type": "dimension",
"default": "80px",
"description": "Width of the column."
},
"minWidth": {
"name": "Minimum width",
"type": "integer",
"default": 20,
"description": "Minimum width of the column, in pixels. Values below this are rejected."
},
"maxWidth": {
"name": "Maximum width",
"type": "integer",
"default": 0,
"description": "Maximum width of the column"
}
},
/**
* Constructor
*
* @param _attrs
* @memberOf et2_dataview_column
*/
init: function(_attrs) {
this.fixedWidth = false;
this.relativeWidth = false;
// Do the sanity check on the attributes and load them
this.generateAttributeSet(_attrs);
this.initAttributes(_attrs);
},
/**
* Set the column width
*
* Posible value types are:
* 1. "100" => fixedWidth 100px
* 2. "100px" => fixedWidth 100px
* 3. "50%" => relativeWidth 50%
* 4. 0.5 => relativeWidth 50%
*
* @param {float|string} _value
*/
set_width: function(_value) {
// Parse the width parameter.
this.relativeWidth = false;
this.fixedWidth = false;
var w = _value;
if (typeof w == 'number')
{
this.relativeWidth = parseFloat(w.toFixed(3));
}
else if (w.charAt(w.length - 1) == "%" && !isNaN(w.substr(0, w.length - 1)))
{
this.relativeWidth = parseInt(w.substr(0, w.length - 1)) / 100;
// Relative widths with more than 100% are not allowed!
if (this.relativeWidth > 1)
{
this.relativeWidth = false;
}
}
else if (w.substr(w.length - 2, 2) == "px" && !isNaN(w.substr(0, w.length - 2)))
{
this.fixedWidth = parseInt(w.substr(0, w.length - 2));
}
else if (typeof w == 'string' && !isNaN(w))
{
this.fixedWidth = parseInt(w);
}
},
set_visibility: function(_value) {
// If visibility is always, don't turn it off
if(this.visibility == ET2_COL_VISIBILITY_ALWAYS || this.visibility == ET2_COL_VISIBILITY_ALWAYS_NOSELECT) return;
if(_value === true)
{
this.visibility = ET2_COL_VISIBILITY_VISIBLE;
}
else if (_value === false)
{
this.visibility = ET2_COL_VISIBILITY_INVISIBLE;
}
else if (typeof _value == "number")
{
this.visibility = _value;
}
else
{
this.egw().debug("warn", "Invalid visibility option for column: ", _value);
}
}
});}).call(this);
var et2_dataview_column = /** @class */ (function () {
/**
* Constructor
*/
function et2_dataview_column(_attrs) {
/**
* Defines the visibility state of this column.
*/
this.visibility = et2_dataview_column.ET2_COL_VISIBILITY_VISIBLE;
this.caption = '';
/**
* Column type - Type of the column
*
* One of ET2_COL_TYPE_DEFAULT or ET2_COL_TYPE_NAME_ICON_FIXED
*/
this.type = et2_dataview_column.ET2_COL_TYPE_DEFAULT;
/**
* Width of the column
*/
this.width = 80;
/**
* Maximum width of the column
*/
this.maxWidth = 0;
/**
* Minimum width of the column, in pixels. Values below this are rejected.
*/
this.minWidth = 20;
this.id = _attrs.id;
if (typeof _attrs.visibility !== "undefined") {
this.visibility = _attrs.visibility;
}
this.caption = _attrs.caption;
if (typeof _attrs.type !== "undefined") {
this.type = _attrs.type;
}
if (typeof _attrs.width !== "undefined") {
this.set_width(_attrs.width);
}
if (typeof _attrs.maxWidth !== "undefined") {
this.maxWidth = _attrs.maxWidth;
}
if (typeof _attrs.minWidth !== "undefined") {
this.minWidth = _attrs.minWidth;
}
}
/**
* Set the column width
*
* Posible value types are:
* 1. "100" => fixedWidth 100px
* 2. "100px" => fixedWidth 100px
* 3. "50%" => relativeWidth 50%
* 4. 0.5 => relativeWidth 50%
*
* @param {float|string} _value
*/
et2_dataview_column.prototype.set_width = function (_value) {
// Parse the width parameter.
this.relativeWidth = false;
this.fixedWidth = false;
var w = _value;
if (typeof w == 'number') {
this.relativeWidth = parseFloat(w.toFixed(3));
}
else if (w.charAt(w.length - 1) == "%" && !isNaN(w.substr(0, w.length - 1))) {
this.relativeWidth = parseInt(w.substr(0, w.length - 1)) / 100;
// Relative widths with more than 100% are not allowed!
if (this.relativeWidth > 1) {
this.relativeWidth = false;
}
}
else if (w.substr(w.length - 2, 2) == "px" && !isNaN(w.substr(0, w.length - 2))) {
this.fixedWidth = parseInt(w.substr(0, w.length - 2));
}
else if (typeof w == 'string' && !isNaN(parseFloat(w))) {
this.fixedWidth = parseInt(w);
}
};
et2_dataview_column.prototype.set_visibility = function (_value) {
// If visibility is always, don't turn it off
if (this.visibility == et2_dataview_column.ET2_COL_VISIBILITY_ALWAYS || this.visibility == et2_dataview_column.ET2_COL_VISIBILITY_ALWAYS_NOSELECT)
return;
if (_value === true) {
this.visibility = et2_dataview_column.ET2_COL_VISIBILITY_VISIBLE;
}
else if (_value === false) {
this.visibility = et2_dataview_column.ET2_COL_VISIBILITY_INVISIBLE;
}
else if (typeof _value == "number") {
this.visibility = _value;
}
else {
egw().debug("warn", "Invalid visibility option for column: ", _value);
}
};
et2_dataview_column.ET2_COL_TYPE_DEFAULT = 0;
et2_dataview_column.ET2_COL_TYPE_NAME_ICON_FIXED = 1;
et2_dataview_column.ET2_COL_VISIBILITY_ALWAYS = 0;
et2_dataview_column.ET2_COL_VISIBILITY_VISIBLE = 1;
et2_dataview_column.ET2_COL_VISIBILITY_INVISIBLE = 2;
et2_dataview_column.ET2_COL_VISIBILITY_ALWAYS_NOSELECT = 3;
et2_dataview_column.ET2_COL_VISIBILITY_DISABLED = 4;
et2_dataview_column._attributes = {
"id": {
"name": "ID",
"type": "string",
"description": "Unique identifier for this column. It is used to " +
"store changed column widths or visibilities."
},
"visibility": {
"name": "Visibility",
"type": "integer",
"default": et2_dataview_column.ET2_COL_VISIBILITY_VISIBLE,
"description": "Defines the visibility state of this column."
},
"caption": {
"name": "Caption",
"type": "string",
"description": "Caption of the column as it is displayed in the " +
"select columns popup."
},
"type": {
"name": "Column type",
"type": "integer",
"default": et2_dataview_column.ET2_COL_TYPE_DEFAULT,
"description": "Type of the column"
},
"width": {
"name": "Width",
"type": "dimension",
"default": "80px",
"description": "Width of the column."
},
"minWidth": {
"name": "Minimum width",
"type": "integer",
"default": 20,
"description": "Minimum width of the column, in pixels. Values below this are rejected."
},
"maxWidth": {
"name": "Maximum width",
"type": "integer",
"default": 0,
"description": "Maximum width of the column"
}
};
return et2_dataview_column;
}());
exports.et2_dataview_column = et2_dataview_column;
/**
* Contains logic for the columns class. The columns class represents the unique set
* of columns a grid view owns. The parameters of the columns (except for visibility)
* do normaly not change.
*/
var et2_dataview_columns = (function(){ "use strict"; return Class.extend({
init: function(_columnData) {
// Initialize some variables
this.totalWidth = 0;
this.totalFixed = 0;
this.columnWidths = [];
// Create the columns object
this.columns = new Array(_columnData.length);
for (var i = 0; i < _columnData.length; i++)
{
this.columns[i] = new et2_dataview_column(_columnData[i]);
}
this.updated = true;
},
destroy: function() {
// Free all column objects
for (var i = 0; i < this.columns.length; i++)
{
this.columns[i].free();
}
},
/**
* Set the total width of the header row
*
* @param {(string|number)} _width
*/
setTotalWidth: function(_width) {
if (_width != this.totalWidth && _width > 0)
{
this.totalWidth = _width;
this.updated = true;
}
},
/**
* Returns the index of the colum with the given id
*
* @param {string} _id
*/
getColumnIndexById: function(_id) {
for (var i = 0; i < this.columns.length; i++)
{
if (this.columns[i].id == _id)
{
return i;
}
}
return -1;
},
/**
* Returns the column with the given id
*
* @param {string} _id
*/
getColumnById: function(_id) {
var idx = this.getColumnIndexById(_id);
return (idx == -1) ? null : this.columns[idx];
},
/**
* Returns the width of the column with the given index
*
* @param {number} _idx
*/
getColumnWidth: function(_idx) {
if (this.totalWidth > 0 && _idx >= 0 && _idx < this.columns.length)
{
// Recalculate the column widths if something has changed.
if (this.updated)
{
this._calculateWidths();
this.updated = false;
}
// Return the calculated width for the column with the given index.
return this.columnWidths[_idx];
}
return 0;
},
/**
* Returns an array containing the width of the column and its visibility
* state.
*/
getColumnData: function() {
var result = [];
for (var i = 0; i < this.columns.length; i++)
{
result.push({
"id": this.columns[i].id,
"width": this.getColumnWidth(i),
"visible": this.columns[i].visibility !== ET2_COL_VISIBILITY_INVISIBLE &&
this.columns[i].visibility !== ET2_COL_VISIBILITY_DISABLED
});
}
return result;
},
/**
* Returns an associative array which contains data about the visibility
* state of the columns.
*/
getColumnVisibilitySet: function() {
var result = {};
for (var i = 0; i < this.columns.length; i++)
{
if (this.columns[i].visibility != ET2_COL_VISIBILITY_ALWAYS_NOSELECT)
{
result[this.columns[i].id] = {
"caption": this.columns[i].caption,
"enabled": (this.columns[i].visibility != ET2_COL_VISIBILITY_ALWAYS) &&
(this.columns[i].visibility != ET2_COL_VISIBILITY_DISABLED) &&
(this.columns[i].type != ET2_COL_TYPE_NAME_ICON_FIXED),
"visible": this.columns[i].visibility != ET2_COL_VISIBILITY_INVISIBLE
};
}
}
return result;
},
/**
* Sets a column visiblity set
*
* @param {object} _set
*/
setColumnVisibilitySet: function(_set) {
for (var k in _set)
{
var col = this.getColumnById(k);
if (col)
{
col.set_visibility(_set[k].visible ? ET2_COL_VISIBILITY_VISIBLE :
ET2_COL_VISIBILITY_INVISIBLE);
}
}
this.updated = true;
},
/* ---- PRIVATE FUNCTIONS ---- */
/**
* Calculates the absolute column width depending on the previously set
* "totalWidth" value. The calculated values are stored in the columnWidths
* array.
*/
_calculateWidths: function()
{
// Reset some values which are used during the calculation
for (var i = 0; i < this.columns.length; i++)
{
this.columns[i]._larger = false;
this.columns[i]._newWidth = false;
}
// Remove the spacing between the columns from the total width
var tw = this.totalWidth;
// Calculate how many space is - relatively - not occupied with columns with
// relative or fixed width
var totalRelative = 0;
var fixedCount = 0;
this.totalFixed = 0;
for (var i = 0; i < this.columns.length; i++)
{
var col = this.columns[i];
if (col.visibility !== ET2_COL_VISIBILITY_INVISIBLE &&
col.visibility !== ET2_COL_VISIBILITY_DISABLED
)
{
// Some bounds sanity checking
if(col.fixedWidth > tw || col.fixedWidth < 0)
{
col.fixedWidth = false;
}
else if (col.relativeWidth > 1 || col.relativeWidth < 0)
{
col.relativeWidth = false;
}
if (col.relativeWidth)
{
totalRelative += col.relativeWidth;
}
else if (col.fixedWidth)
{
this.totalFixed += col.fixedWidth;
fixedCount++;
}
}
}
// Now calculate the absolute width of the columns in pixels
var usedTotal = 0;
this.columnWidths = [];
for (var i = 0; i < this.columns.length; i++)
{
var w = 0;
var col = this.columns[i];
if (col.visibility != ET2_COL_VISIBILITY_INVISIBLE &&
col.visibility !== ET2_COL_VISIBILITY_DISABLED
)
{
if (col._larger)
{
w = col.maxWidth;
}
else if (col.fixedWidth)
{
w = col.fixedWidth;
}
else if (col.relativeWidth)
{
// Reset relative to an actual percentage (of 1.00) or
// resizing eventually sends them to 0
col.relativeWidth = col.relativeWidth / totalRelative;
w = Math.round((tw-this.totalFixed) * col.relativeWidth);
}
if (w > tw || (col.maxWidth && w > col.maxWidth))
{
w = Math.min(tw - usedTotal, col.maxWidth);
}
if (w < 0 || w < col.minWidth)
{
w = Math.max(0, col.minWidth);
}
}
this.columnWidths.push(w);
usedTotal += w;
}
// Deal with any accumulated rounding errors
if(usedTotal != tw)
{
var column, columnIndex;
var remaining_width = (usedTotal - tw);
// Pick the first relative column and use it
for(columnIndex = 0; columnIndex < this.columns.length; columnIndex++)
{
if(this.columns[columnIndex].visibility === ET2_COL_VISIBILITY_INVISIBLE ||
this.columns[columnIndex].visibility === ET2_COL_VISIBILITY_DISABLED ||
this.columnWidths[columnIndex] <= 0 ||
remaining_width > 0 && this.columnWidths[columnIndex] <= this.columns[columnIndex].minWidth)
{
continue;
}
var col = this.columns[columnIndex];
if(col.relativeWidth || !col.fixedWidth)
{
column = col;
break;
}
else if (!col.fixedWidth)
{
column = col;
}
}
if(!column)
{
// Distribute shortage over all fixed width columns
var diff = Math.round(remaining_width / fixedCount);
for(var i = 0; i < this.columns.length; i++)
{
var col = this.columns[i];
var col_diff = (diff < 0 ?
Math.max(remaining_width, diff) :
Math.min(remaining_width, diff)
);
if(!col.fixedWidth) continue;
var new_width = this.columnWidths[i] - col_diff;
remaining_width -= col_diff;
this.columnWidths[i] = Math.max(0, Math.min(new_width,tw));
}
}
else
{
this.columnWidths[columnIndex] = Math.max(column.minWidth, this.columnWidths[columnIndex] - remaining_width);
}
}
}
});}).call(this);
var et2_dataview_columns = /** @class */ (function () {
function et2_dataview_columns(_columnData) {
// Initialize some variables
this._totalWidth = 0;
this._totalFixed = 0;
this.columnWidths = [];
// Create the columns object
this.columns = new Array(_columnData.length);
for (var i = 0; i < _columnData.length; i++) {
this.columns[i] = new et2_dataview_column(_columnData[i]);
}
this._updated = true;
}
et2_dataview_columns.prototype.destroy = function () {
// Free all column objects
for (var i = 0; i < this.columns.length; i++) {
this.columns[i] = null;
}
};
et2_dataview_columns.prototype.updated = function () {
this._updated = true;
};
et2_dataview_columns.prototype.columnCount = function () {
return this.columns.length;
};
Object.defineProperty(et2_dataview_columns.prototype, "totalWidth", {
get: function () {
return this._totalWidth;
},
enumerable: true,
configurable: true
});
Object.defineProperty(et2_dataview_columns.prototype, "totalFixed", {
get: function () {
return this._totalFixed ? this._totalFixed : 0;
},
enumerable: true,
configurable: true
});
/**
* Set the total width of the header row
*
* @param {(string|number)} _width
*/
et2_dataview_columns.prototype.setTotalWidth = function (_width) {
if (_width != this._totalWidth && _width > 0) {
this._totalWidth = _width;
this._updated = true;
}
};
/**
* Returns the index of the colum with the given id
*
* @param {string} _id
*/
et2_dataview_columns.prototype.getColumnIndexById = function (_id) {
for (var i = 0; i < this.columns.length; i++) {
if (this.columns[i].id == _id) {
return i;
}
}
return -1;
};
/**
* Returns the column with the given id
*
* @param {string} _id
*/
et2_dataview_columns.prototype.getColumnById = function (_id) {
var idx = this.getColumnIndexById(_id);
return (idx == -1) ? null : this.columns[idx];
};
/**
* Returns the width of the column with the given index
*
* @param {number} _idx
*/
et2_dataview_columns.prototype.getColumnWidth = function (_idx) {
if (this._totalWidth > 0 && _idx >= 0 && _idx < this.columns.length) {
// Recalculate the column widths if something has changed.
if (this._updated) {
this._calculateWidths();
this._updated = false;
}
// Return the calculated width for the column with the given index.
return this.columnWidths[_idx];
}
return 0;
};
/**
* Returns an array containing the width of the column and its visibility
* state.
*/
et2_dataview_columns.prototype.getColumnData = function () {
var result = [];
for (var i = 0; i < this.columns.length; i++) {
result.push({
"id": this.columns[i].id,
"width": this.getColumnWidth(i),
"visible": this.columns[i].visibility !== et2_dataview_column.ET2_COL_VISIBILITY_INVISIBLE &&
this.columns[i].visibility !== et2_dataview_column.ET2_COL_VISIBILITY_DISABLED
});
}
return result;
};
/**
* Returns an associative array which contains data about the visibility
* state of the columns.
*/
et2_dataview_columns.prototype.getColumnVisibilitySet = function () {
var result = {};
for (var i = 0; i < this.columns.length; i++) {
if (this.columns[i].visibility != et2_dataview_column.ET2_COL_VISIBILITY_ALWAYS_NOSELECT) {
result[this.columns[i].id] = {
"caption": this.columns[i].caption,
"enabled": (this.columns[i].visibility != et2_dataview_column.ET2_COL_VISIBILITY_ALWAYS) &&
(this.columns[i].visibility != et2_dataview_column.ET2_COL_VISIBILITY_DISABLED) &&
(this.columns[i].type != et2_dataview_column.ET2_COL_TYPE_NAME_ICON_FIXED),
"visible": this.columns[i].visibility != et2_dataview_column.ET2_COL_VISIBILITY_INVISIBLE
};
}
}
return result;
};
/**
* Sets a column visiblity set
*
* @param {object} _set
*/
et2_dataview_columns.prototype.setColumnVisibilitySet = function (_set) {
for (var k in _set) {
var col = this.getColumnById(k);
if (col) {
col.set_visibility(_set[k].visible ? et2_dataview_column.ET2_COL_VISIBILITY_VISIBLE :
et2_dataview_column.ET2_COL_VISIBILITY_INVISIBLE);
}
}
this._updated = true;
};
/* ---- PRIVATE FUNCTIONS ---- */
/**
* Calculates the absolute column width depending on the previously set
* "totalWidth" value. The calculated values are stored in the columnWidths
* array.
*/
et2_dataview_columns.prototype._calculateWidths = function () {
// Reset some values which are used during the calculation
var _larger = Array(this.columns.length);
for (var i = 0; i < this.columns.length; i++) {
_larger[i] = false;
}
// Remove the spacing between the columns from the total width
var tw = this._totalWidth;
// Calculate how many space is - relatively - not occupied with columns with
// relative or fixed width
var totalRelative = 0;
var fixedCount = 0;
this._totalFixed = 0;
for (var i = 0; i < this.columns.length; i++) {
var col = this.columns[i];
if (col.visibility !== et2_dataview_column.ET2_COL_VISIBILITY_INVISIBLE &&
col.visibility !== et2_dataview_column.ET2_COL_VISIBILITY_DISABLED) {
// Some bounds sanity checking
if (col.fixedWidth > tw || col.fixedWidth < 0) {
col.fixedWidth = false;
}
else if (col.relativeWidth > 1 || col.relativeWidth < 0) {
col.relativeWidth = false;
}
if (col.relativeWidth) {
totalRelative += col.relativeWidth;
}
else if (col.fixedWidth) {
this._totalFixed += col.fixedWidth;
fixedCount++;
}
}
}
// Now calculate the absolute width of the columns in pixels
var usedTotal = 0;
this.columnWidths = [];
for (var i = 0; i < this.columns.length; i++) {
var w = 0;
var col = this.columns[i];
if (col.visibility != et2_dataview_column.ET2_COL_VISIBILITY_INVISIBLE &&
col.visibility !== et2_dataview_column.ET2_COL_VISIBILITY_DISABLED) {
if (_larger[i]) {
w = col.maxWidth;
}
else if (col.fixedWidth) {
w = col.fixedWidth;
}
else if (col.relativeWidth) {
// Reset relative to an actual percentage (of 1.00) or
// resizing eventually sends them to 0
col.relativeWidth = col.relativeWidth / totalRelative;
w = Math.round((tw - this._totalFixed) * col.relativeWidth);
}
if (w > tw || (col.maxWidth && w > col.maxWidth)) {
w = Math.min(tw - usedTotal, col.maxWidth);
}
if (w < 0 || w < col.minWidth) {
w = Math.max(0, col.minWidth);
}
}
this.columnWidths.push(w);
usedTotal += w;
}
// Deal with any accumulated rounding errors
if (usedTotal != tw) {
var column, columnIndex;
var remaining_width = (usedTotal - tw);
// Pick the first relative column and use it
for (columnIndex = 0; columnIndex < this.columns.length; columnIndex++) {
if (this.columns[columnIndex].visibility === et2_dataview_column.ET2_COL_VISIBILITY_INVISIBLE ||
this.columns[columnIndex].visibility === et2_dataview_column.ET2_COL_VISIBILITY_DISABLED ||
this.columnWidths[columnIndex] <= 0 ||
remaining_width > 0 && this.columnWidths[columnIndex] <= this.columns[columnIndex].minWidth) {
continue;
}
var col = this.columns[columnIndex];
if (col.relativeWidth || !col.fixedWidth) {
column = col;
break;
}
else if (!col.fixedWidth) {
column = col;
}
}
if (!column) {
// Distribute shortage over all fixed width columns
var diff = Math.round(remaining_width / fixedCount);
for (var i = 0; i < this.columns.length; i++) {
var col = this.columns[i];
var col_diff = (diff < 0 ?
Math.max(remaining_width, diff) :
Math.min(remaining_width, diff));
if (!col.fixedWidth)
continue;
var new_width = this.columnWidths[i] - col_diff;
remaining_width -= col_diff;
this.columnWidths[i] = Math.max(0, Math.min(new_width, tw));
}
}
else {
this.columnWidths[columnIndex] = Math.max(column.minWidth, this.columnWidths[columnIndex] - remaining_width);
}
}
};
return et2_dataview_columns;
}());
exports.et2_dataview_columns = et2_dataview_columns;
//# sourceMappingURL=et2_dataview_model_columns.js.map

View File

@ -0,0 +1,548 @@
/**
* EGroupware eTemplate2 - Class which contains a the columns model
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage dataview
* @link http://www.egroupware.org
* @author Andreas Stöckel
* @copyright Stylite 2011
* @version $Id$
*/
/*egw:uses
et2_core_inheritance;
et2_inheritance;
*/
/**
* Class which stores the data of a single column.
*
* @augments Class
*/
export class et2_dataview_column
{
public static readonly ET2_COL_TYPE_DEFAULT = 0;
public static readonly ET2_COL_TYPE_NAME_ICON_FIXED = 1;
public static readonly ET2_COL_VISIBILITY_ALWAYS = 0;
public static readonly ET2_COL_VISIBILITY_VISIBLE = 1;
public static readonly ET2_COL_VISIBILITY_INVISIBLE = 2;
public static readonly ET2_COL_VISIBILITY_ALWAYS_NOSELECT = 3;
public static readonly ET2_COL_VISIBILITY_DISABLED = 4;
static readonly _attributes: any = {
"id": {
"name": "ID",
"type": "string",
"description": "Unique identifier for this column. It is used to " +
"store changed column widths or visibilities."
},
"visibility": {
"name": "Visibility",
"type": "integer",
"default": et2_dataview_column.ET2_COL_VISIBILITY_VISIBLE,
"description": "Defines the visibility state of this column."
},
"caption": {
"name": "Caption",
"type": "string",
"description": "Caption of the column as it is displayed in the " +
"select columns popup."
},
"type": {
"name": "Column type",
"type": "integer",
"default": et2_dataview_column.ET2_COL_TYPE_DEFAULT,
"description": "Type of the column"
},
"width": {
"name": "Width",
"type": "dimension",
"default": "80px",
"description": "Width of the column."
},
"minWidth": {
"name": "Minimum width",
"type": "integer",
"default": 20,
"description": "Minimum width of the column, in pixels. Values below this are rejected."
},
"maxWidth": {
"name": "Maximum width",
"type": "integer",
"default": 0,
"description": "Maximum width of the column"
}
};
fixedWidth: number | boolean;
relativeWidth: number | boolean;
/**
* Unique identifier for this column. It is used to store changed column widths or visibilities.
*/
public id: string;
/**
* Defines the visibility state of this column.
*/
public visibility: number = et2_dataview_column.ET2_COL_VISIBILITY_VISIBLE;
public caption: string = '';
/**
* Column type - Type of the column
*
* One of ET2_COL_TYPE_DEFAULT or ET2_COL_TYPE_NAME_ICON_FIXED
*/
public type: number = et2_dataview_column.ET2_COL_TYPE_DEFAULT;
/**
* Width of the column
*/
public width: number = 80;
/**
* Maximum width of the column
*/
public maxWidth: number = 0;
/**
* Minimum width of the column, in pixels. Values below this are rejected.
*/
public minWidth: number = 20;
/**
* Constructor
*/
constructor(_attrs)
{
this.id = _attrs.id;
if(typeof _attrs.visibility !== "undefined")
{
this.visibility = _attrs.visibility;
}
this.caption = _attrs.caption;
if(typeof _attrs.type !== "undefined")
{
this.type = _attrs.type;
}
if(typeof _attrs.width !== "undefined")
{
this.set_width( _attrs.width );
}
if(typeof _attrs.maxWidth !== "undefined")
{
this.maxWidth = _attrs.maxWidth;
}
if(typeof _attrs.minWidth !== "undefined")
{
this.minWidth = _attrs.minWidth;
}
}
/**
* Set the column width
*
* Posible value types are:
* 1. "100" => fixedWidth 100px
* 2. "100px" => fixedWidth 100px
* 3. "50%" => relativeWidth 50%
* 4. 0.5 => relativeWidth 50%
*
* @param {float|string} _value
*/
set_width(_value)
{
// Parse the width parameter.
this.relativeWidth = false;
this.fixedWidth = false;
var w = _value;
if (typeof w == 'number')
{
this.relativeWidth = parseFloat(w.toFixed(3));
}
else if (w.charAt(w.length - 1) == "%" && !isNaN(w.substr(0, w.length - 1)))
{
this.relativeWidth = parseInt(w.substr(0, w.length - 1)) / 100;
// Relative widths with more than 100% are not allowed!
if (this.relativeWidth > 1)
{
this.relativeWidth = false;
}
}
else if (w.substr(w.length - 2, 2) == "px" && !isNaN(w.substr(0, w.length - 2)))
{
this.fixedWidth = parseInt(w.substr(0, w.length - 2));
}
else if (typeof w == 'string' && !isNaN(parseFloat(w)))
{
this.fixedWidth = parseInt(w);
}
}
set_visibility(_value)
{
// If visibility is always, don't turn it off
if(this.visibility == et2_dataview_column.ET2_COL_VISIBILITY_ALWAYS || this.visibility == et2_dataview_column.ET2_COL_VISIBILITY_ALWAYS_NOSELECT) return;
if(_value === true)
{
this.visibility = et2_dataview_column.ET2_COL_VISIBILITY_VISIBLE;
}
else if (_value === false)
{
this.visibility = et2_dataview_column.ET2_COL_VISIBILITY_INVISIBLE;
}
else if (typeof _value == "number")
{
this.visibility = _value;
}
else
{
egw().debug("warn", "Invalid visibility option for column: ", _value);
}
}
}
/**
* Contains logic for the columns class. The columns class represents the unique set
* of columns a grid view owns. The parameters of the columns (except for visibility)
* do normaly not change.
*/
export class et2_dataview_columns
{
private _totalWidth: number;
private _totalFixed: number | boolean;
private columnWidths: any[];
private columns: et2_dataview_column[];
private _updated: boolean;
constructor(_columnData)
{
// Initialize some variables
this._totalWidth = 0;
this._totalFixed = 0;
this.columnWidths = [];
// Create the columns object
this.columns = new Array(_columnData.length);
for (var i = 0; i < _columnData.length; i++)
{
this.columns[i] = new et2_dataview_column(_columnData[i]);
}
this._updated = true;
}
destroy() {
// Free all column objects
for (var i = 0; i < this.columns.length; i++)
{
this.columns[i] = null;
}
}
public updated()
{
this._updated = true;
}
columnCount() : number
{
return this.columns.length;
}
get totalWidth(): number
{
return this._totalWidth;
}
get totalFixed(): number {
return this._totalFixed ? <number>this._totalFixed : 0;
}
/**
* Set the total width of the header row
*
* @param {(string|number)} _width
*/
setTotalWidth(_width)
{
if (_width != this._totalWidth && _width > 0)
{
this._totalWidth = _width;
this._updated = true;
}
}
/**
* Returns the index of the colum with the given id
*
* @param {string} _id
*/
getColumnIndexById( _id)
{
for (var i = 0; i < this.columns.length; i++)
{
if (this.columns[i].id == _id)
{
return i;
}
}
return -1;
}
/**
* Returns the column with the given id
*
* @param {string} _id
*/
getColumnById( _id)
{
var idx = this.getColumnIndexById(_id);
return (idx == -1) ? null : this.columns[idx];
}
/**
* Returns the width of the column with the given index
*
* @param {number} _idx
*/
getColumnWidth( _idx)
{
if (this._totalWidth > 0 && _idx >= 0 && _idx < this.columns.length)
{
// Recalculate the column widths if something has changed.
if (this._updated)
{
this._calculateWidths();
this._updated = false;
}
// Return the calculated width for the column with the given index.
return this.columnWidths[_idx];
}
return 0;
}
/**
* Returns an array containing the width of the column and its visibility
* state.
*/
getColumnData( )
{
var result = [];
for (var i = 0; i < this.columns.length; i++)
{
result.push({
"id": this.columns[i].id,
"width": this.getColumnWidth(i),
"visible": this.columns[i].visibility !== et2_dataview_column.ET2_COL_VISIBILITY_INVISIBLE &&
this.columns[i].visibility !== et2_dataview_column.ET2_COL_VISIBILITY_DISABLED
});
}
return result;
}
/**
* Returns an associative array which contains data about the visibility
* state of the columns.
*/
getColumnVisibilitySet()
{
var result = {};
for (var i = 0; i < this.columns.length; i++)
{
if (this.columns[i].visibility != et2_dataview_column.ET2_COL_VISIBILITY_ALWAYS_NOSELECT)
{
result[this.columns[i].id] = {
"caption": this.columns[i].caption,
"enabled": (this.columns[i].visibility != et2_dataview_column.ET2_COL_VISIBILITY_ALWAYS) &&
(this.columns[i].visibility != et2_dataview_column.ET2_COL_VISIBILITY_DISABLED) &&
(this.columns[i].type != et2_dataview_column.ET2_COL_TYPE_NAME_ICON_FIXED),
"visible": this.columns[i].visibility != et2_dataview_column.ET2_COL_VISIBILITY_INVISIBLE
};
}
}
return result;
}
/**
* Sets a column visiblity set
*
* @param {object} _set
*/
setColumnVisibilitySet( _set)
{
for (var k in _set)
{
var col = this.getColumnById(k);
if (col)
{
col.set_visibility(_set[k].visible ? et2_dataview_column.ET2_COL_VISIBILITY_VISIBLE :
et2_dataview_column.ET2_COL_VISIBILITY_INVISIBLE);
}
}
this._updated = true;
}
/* ---- PRIVATE FUNCTIONS ---- */
/**
* Calculates the absolute column width depending on the previously set
* "totalWidth" value. The calculated values are stored in the columnWidths
* array.
*/
_calculateWidths()
{
// Reset some values which are used during the calculation
let _larger = Array(this.columns.length);
for (var i = 0; i < this.columns.length; i++)
{
_larger[i] = false;
}
// Remove the spacing between the columns from the total width
var tw = this._totalWidth;
// Calculate how many space is - relatively - not occupied with columns with
// relative or fixed width
var totalRelative: number = 0;
var fixedCount = 0;
this._totalFixed = 0;
for (var i = 0; i < this.columns.length; i++)
{
var col = this.columns[i];
if (col.visibility !== et2_dataview_column.ET2_COL_VISIBILITY_INVISIBLE &&
col.visibility !== et2_dataview_column.ET2_COL_VISIBILITY_DISABLED
)
{
// Some bounds sanity checking
if(col.fixedWidth > tw || col.fixedWidth < 0)
{
col.fixedWidth = false;
}
else if (col.relativeWidth > 1 || col.relativeWidth < 0)
{
col.relativeWidth = false;
}
if (col.relativeWidth)
{
totalRelative += <number>col.relativeWidth;
}
else if (col.fixedWidth)
{
this._totalFixed += <number>col.fixedWidth;
fixedCount++;
}
}
}
// Now calculate the absolute width of the columns in pixels
var usedTotal = 0;
this.columnWidths = [];
for (var i = 0; i < this.columns.length; i++)
{
var w = 0;
var col = this.columns[i];
if (col.visibility != et2_dataview_column.ET2_COL_VISIBILITY_INVISIBLE &&
col.visibility !== et2_dataview_column.ET2_COL_VISIBILITY_DISABLED
)
{
if (_larger[i])
{
w = col.maxWidth;
}
else if (col.fixedWidth)
{
w = <number>col.fixedWidth;
}
else if (col.relativeWidth)
{
// Reset relative to an actual percentage (of 1.00) or
// resizing eventually sends them to 0
col.relativeWidth = <number>col.relativeWidth / totalRelative;
w = Math.round((tw-this._totalFixed) * col.relativeWidth);
}
if (w > tw || (col.maxWidth && w > col.maxWidth))
{
w = Math.min(tw - usedTotal, col.maxWidth);
}
if (w < 0 || w < col.minWidth)
{
w = Math.max(0, col.minWidth);
}
}
this.columnWidths.push(w);
usedTotal += w;
}
// Deal with any accumulated rounding errors
if(usedTotal != tw)
{
var column, columnIndex;
var remaining_width = (usedTotal - tw);
// Pick the first relative column and use it
for(columnIndex = 0; columnIndex < this.columns.length; columnIndex++)
{
if(this.columns[columnIndex].visibility === et2_dataview_column.ET2_COL_VISIBILITY_INVISIBLE ||
this.columns[columnIndex].visibility === et2_dataview_column.ET2_COL_VISIBILITY_DISABLED ||
this.columnWidths[columnIndex] <= 0 ||
remaining_width > 0 && this.columnWidths[columnIndex] <= this.columns[columnIndex].minWidth)
{
continue;
}
var col = this.columns[columnIndex];
if(col.relativeWidth || !col.fixedWidth)
{
column = col;
break;
}
else if (!col.fixedWidth)
{
column = col;
}
}
if(!column)
{
// Distribute shortage over all fixed width columns
var diff = Math.round(remaining_width / fixedCount);
for(var i = 0; i < this.columns.length; i++)
{
var col = this.columns[i];
var col_diff = (diff < 0 ?
Math.max(remaining_width, diff) :
Math.min(remaining_width, diff)
);
if(!col.fixedWidth) continue;
var new_width = this.columnWidths[i] - col_diff;
remaining_width -= col_diff;
this.columnWidths[i] = Math.max(0, Math.min(new_width,tw));
}
}
else
{
this.columnWidths[columnIndex] = Math.max(column.minWidth, this.columnWidths[columnIndex] - remaining_width);
}
}
}
}

View File

@ -1,3 +1,4 @@
"use strict";
/**
* EGroupware eTemplate2 - dataview code
*
@ -9,12 +10,7 @@
* @copyright Stylite 2012
* @version $Id$
*/
/*egw:uses
/vendor/bower-asset/jquery/dist/jquery.js;
et2_dataview_interfaces;
*/
Object.defineProperty(exports, "__esModule", { value: true });
/**
* The et2_dataview_container class is the main object each dataview consits of.
* Each row, spacer as well as the grid itself are containers. A container is
@ -31,352 +27,287 @@
*
* @augments Class
*/
var et2_dataview_container = (function(){ "use strict"; return Class.extend(et2_dataview_IInvalidatable,
{
/**
* Initializes the container object.
*
* @param _parent is an object which implements the IInvalidatable
* interface. _parent may not be null.
* @memberOf et2_dataview_container
*/
init: function(_parent) {
// Copy the given invalidation element
this._parent = _parent;
this._nodes = []; // contains all DOM-Nodes this container exists of
this._inTree = false; //
this._attachData = {"node": null, "prepend": false};
this._destroyCallback = null;
this._destroyContext = null;
this._height = false;
this._index = 0;
this._top = 0;
},
/**
* Destroys this container. Classes deriving from et2_dataview_container
* should override this method and take care of unregistering all event
* handlers etc.
*/
destroy: function() {
// Remove the nodes from the tree
this.removeFromTree();
// Call the callback function (if one is registered)
if (this._destroyCallback)
{
this._destroyCallback.call(this._destroyContext, this);
}
},
/**
* Sets the "destroyCallback" -- the given function gets called whenever
* the container is destroyed. This instance is passed as an parameter to
* the callback.
*
* @param {function} _callback
* @param {object} _context
*/
setDestroyCallback: function(_callback, _context) {
this._destroyCallback = _callback;
this._destroyContext = _context;
},
/**
* Inserts all container nodes into the DOM tree after or before the given
* element.
*
* @param _node is the node after/before which the container "tr"s should
* get inserted. _node should be a simple DOM node, not a jQuery object.
* @param _prepend specifies whether the container should be inserted before
* or after the given node. Inserting before is needed for inserting the
* first element in front of an spacer.
*/
insertIntoTree: function(_node, _prepend) {
if (!this._inTree && _node != null && this._nodes.length > 0)
{
// Store the parent node and indicate that this element is now in
// the tree.
this._attachData = {"node": _node, "prepend": _prepend};
this._inTree = true;
for (var i = 0; i < this._nodes.length; i++)
{
if (i == 0)
{
if (_prepend)
{
_node.before(this._nodes[0]);
}
else
{
_node.after(this._nodes[0]);
}
}
else
{
// Insert all following nodes after the previous node
this._nodes[i - 1].after(this._nodes[i]);
}
}
// Invalidate this element in order to update the height of the
// parent
this.invalidate();
}
},
/**
* Removes all container nodes from the tree.
*/
removeFromTree: function() {
if (this._inTree)
{
// Call the jQuery remove function to remove all nodes from the tree
// again.
for (var i = 0; i < this._nodes.length; i++)
{
this._nodes[i].remove();
}
// Reset the "attachData"
this._inTree = false;
this._attachData = {"node": null, "prepend": false};
}
},
/**
* Appends a node to the container.
*
* @param _node is the DOM-Node which should be appended.
*/
appendNode: function(_node) {
// Add the given node to the "nodes" array
this._nodes.push(_node);
// If the container is already in the tree, attach the given node to the
// tree.
if (this._inTree)
{
if (this._nodes.length === 1)
{
if (this._attachData.prepend)
{
this._attachData.node.before(_node);
}
else
{
this._attachData.node.after(_node);
}
}
else
{
this._nodes[this._nodes.length - 2].after(_node);
}
this.invalidate();
}
},
/**
* Removes a certain node from the container
*
* @param {DOMElement} _node
*/
removeNode: function(_node) {
// Get the index of the node in the nodes array
var idx = this._nodes.indexOf(_node);
if (idx >= 0)
{
// Remove the node if the container is currently attached
if (this._inTree)
{
_node.parentNode.removeChild(_node);
}
// Remove the node from the nodes array
this._nodes.splice(idx, 1);
}
},
/**
* Returns the last node of the container - new nodes have to be appended
* after it.
*/
getLastNode: function() {
if (this._nodes.length > 0)
{
return this._nodes[this._nodes.length - 1];
}
return null;
},
/**
* Returns the first node of the container.
*/
getFirstNode: function() {
return this._nodes.length > 0 ? this._nodes[0] : null;
},
/**
* Returns the accumulated height of all container nodes. Only visible nodes
* (without "display: none" etc.) are taken into account.
*/
getHeight: function() {
if (this._height === false && this._inTree)
{
this._height = 0;
// Setting this before measuring height helps with issues getting the
// wrong height due to margins & collapsed borders
this.tr.css('display','block');
// Increment the height value for each visible container node
for (var i = 0; i < this._nodes.length; i++)
{
if (this._isVisible(this._nodes[i][0]))
{
this._height += this._nodeHeight(this._nodes[i][0]);
}
}
this.tr.css('display','');
}
return this._height === false ? 0 : this._height;
},
/**
* Returns a datastructure containing information used for calculating the
* average row height of a grid.
* The datastructure has the
* {
* avgHeight: <the calculated average height of this element>,
* avgCount: <the element count this calculation was based on>
* }
*/
getAvgHeightData: function() {
return {
"avgHeight": this.getHeight(),
"avgCount": 1
};
},
/**
* Returns the previously set "pixel top" of the container.
*/
getTop: function() {
return this._top;
},
/**
* Returns the "pixel bottom" of the container.
*/
getBottom: function() {
return this._top + this.getHeight();
},
/**
* Returns the range of the element.
*/
getRange: function() {
return et2_bounds(this.getTop(), this.getBottom());
},
/**
* Returns the index of the element.
*/
getIndex: function() {
return this._index;
},
/**
* Returns how many elements this container represents.
*/
getCount: function() {
return 1;
},
/**
* Sets the top of the element.
*
* @param {number} _value
*/
setTop: function(_value) {
this._top = _value;
},
/**
* Sets the index of the element.
*
* @param {number} _value
*/
setIndex: function(_value) {
this._index = _value;
},
/* -- et2_dataview_IInvalidatable -- */
/**
* Broadcasts an invalidation through the container tree. Marks the own
* height as invalid.
*/
invalidate: function() {
// Abort if this element is already marked as invalid.
if (this._height !== false)
{
// Delete the own, probably computed height
this._height = false;
// Broadcast the invalidation to the parent element
this._parent.invalidate();
}
},
/* -- PRIVATE FUNCTIONS -- */
/**
* Used to check whether an element is visible or not (non recursive).
*
* @param _obj is the element which should be checked for visibility, it is
* only checked whether some stylesheet makes the element invisible, not if
* the given object is actually inside the DOM.
*/
_isVisible: function(_obj) {
// Check whether the element is localy invisible
if (_obj.style && (_obj.style.display === "none"
|| _obj.style.visiblity === "none"))
{
return false;
}
// Get the computed style of the element
var style = window.getComputedStyle ? window.getComputedStyle(_obj, null)
: _obj.currentStyle;
if (style.display === "none" || style.visibility === "none")
{
return false;
}
return true;
},
/**
* Returns the height of a node in pixels and zero if the element is not
* visible. The height is clamped to positive values.
*
* @param {DOMElement} _node
*/
_nodeHeight: function(_node)
{
return _node.offsetHeight;
}
});}).call(this);
var et2_dataview_container = /** @class */ (function () {
/**
* Initializes the container object.
*
* @param _parent is an object which implements the IInvalidatable
* interface. _parent may not be null.
* @memberOf et2_dataview_container
*/
function et2_dataview_container(_parent) {
// Copy the given invalidation element
this._parent = _parent;
this._nodes = [];
this._inTree = false;
this._attachData = { "node": null, "prepend": false };
this._destroyCallback = null;
this._destroyContext = null;
this._height = -1;
this._index = 0;
this._top = 0;
}
/**
* Destroys this container. Classes deriving from et2_dataview_container
* should override this method and take care of unregistering all event
* handlers etc.
*/
et2_dataview_container.prototype.destroy = function () {
// Remove the nodes from the tree
this.removeFromTree();
// Call the callback function (if one is registered)
if (this._destroyCallback) {
this._destroyCallback.call(this._destroyContext, this);
}
};
/**
* Sets the "destroyCallback" -- the given function gets called whenever
* the container is destroyed. This instance is passed as an parameter to
* the callback.
*
* @param {function} _callback
* @param {object} _context
*/
et2_dataview_container.prototype.setDestroyCallback = function (_callback, _context) {
this._destroyCallback = _callback;
this._destroyContext = _context;
};
/**
* Inserts all container nodes into the DOM tree after or before the given
* element.
*
* @param _node is the node after/before which the container "tr"s should
* get inserted. _node should be a simple DOM node, not a jQuery object.
* @param _prepend specifies whether the container should be inserted before
* or after the given node. Inserting before is needed for inserting the
* first element in front of an spacer.
*/
et2_dataview_container.prototype.insertIntoTree = function (_node, _prepend) {
if (!this._inTree && _node != null && this._nodes.length > 0) {
// Store the parent node and indicate that this element is now in
// the tree.
this._attachData = { node: _node, prepend: _prepend };
this._inTree = true;
for (var i = 0; i < this._nodes.length; i++) {
if (i == 0) {
if (_prepend) {
_node.before(this._nodes[0]);
}
else {
_node.after(this._nodes[0]);
}
}
else {
// Insert all following nodes after the previous node
this._nodes[i - 1].after(this._nodes[i]);
}
}
// Invalidate this element in order to update the height of the
// parent
this.invalidate();
}
};
/**
* Removes all container nodes from the tree.
*/
et2_dataview_container.prototype.removeFromTree = function () {
if (this._inTree) {
// Call the jQuery remove function to remove all nodes from the tree
// again.
for (var i = 0; i < this._nodes.length; i++) {
this._nodes[i].remove();
}
// Reset the "attachData"
this._inTree = false;
this._attachData = { "node": null, "prepend": false };
}
};
/**
* Appends a node to the container.
*
* @param _node is the DOM-Node which should be appended.
*/
et2_dataview_container.prototype.appendNode = function (_node) {
// Add the given node to the "nodes" array
this._nodes.push(_node);
// If the container is already in the tree, attach the given node to the
// tree.
if (this._inTree) {
if (this._nodes.length === 1) {
if (this._attachData.prepend) {
this._attachData.node.before(_node);
}
else {
this._attachData.node.after(_node);
}
}
else {
this._nodes[this._nodes.length - 2].after(_node);
}
this.invalidate();
}
};
/**
* Removes a certain node from the container
*
* @param {HTMLElement} _node
*/
et2_dataview_container.prototype.removeNode = function (_node) {
// Get the index of the node in the nodes array
var idx = this._nodes.indexOf(_node);
if (idx >= 0) {
// Remove the node if the container is currently attached
if (this._inTree) {
_node.parentNode.removeChild(_node);
}
// Remove the node from the nodes array
this._nodes.splice(idx, 1);
}
};
/**
* Returns the last node of the container - new nodes have to be appended
* after it.
*/
et2_dataview_container.prototype.getLastNode = function () {
if (this._nodes.length > 0) {
return this._nodes[this._nodes.length - 1];
}
return null;
};
/**
* Returns the first node of the container.
*/
et2_dataview_container.prototype.getFirstNode = function () {
return this._nodes.length > 0 ? this._nodes[0] : null;
};
/**
* Returns the accumulated height of all container nodes. Only visible nodes
* (without "display: none" etc.) are taken into account.
*/
et2_dataview_container.prototype.getHeight = function () {
if (this._height === -1 && this._inTree) {
this._height = 0;
// Setting this before measuring height helps with issues getting the
// wrong height due to margins & collapsed borders
this.tr.css('display', 'block');
// Increment the height value for each visible container node
for (var i = 0; i < this._nodes.length; i++) {
if (et2_dataview_container._isVisible(this._nodes[i][0])) {
this._height += et2_dataview_container._nodeHeight(this._nodes[i][0]);
}
}
this.tr.css('display', '');
}
return (this._height === -1) ? 0 : this._height;
};
/**
* Returns a datastructure containing information used for calculating the
* average row height of a grid.
* The datastructure has the
* {
* avgHeight: <the calculated average height of this element>,
* avgCount: <the element count this calculation was based on>
* }
*/
et2_dataview_container.prototype.getAvgHeightData = function () {
return {
"avgHeight": this.getHeight(),
"avgCount": 1
};
};
/**
* Returns the previously set "pixel top" of the container.
*/
et2_dataview_container.prototype.getTop = function () {
return this._top;
};
/**
* Returns the "pixel bottom" of the container.
*/
et2_dataview_container.prototype.getBottom = function () {
return this._top + this.getHeight();
};
/**
* Returns the range of the element.
*/
et2_dataview_container.prototype.getRange = function () {
return et2_bounds(this.getTop(), this.getBottom());
};
/**
* Returns the index of the element.
*/
et2_dataview_container.prototype.getIndex = function () {
return this._index;
};
/**
* Returns how many elements this container represents.
*/
et2_dataview_container.prototype.getCount = function () {
return 1;
};
/**
* Sets the top of the element.
*
* @param {number} _value
*/
et2_dataview_container.prototype.setTop = function (_value) {
this._top = _value;
};
/**
* Sets the index of the element.
*
* @param {number} _value
*/
et2_dataview_container.prototype.setIndex = function (_value) {
this._index = _value;
};
/* -- et2_dataview_IInvalidatable -- */
/**
* Broadcasts an invalidation through the container tree. Marks the own
* height as invalid.
*/
et2_dataview_container.prototype.invalidate = function () {
// Abort if this element is already marked as invalid.
if (this._height !== -1) {
// Delete the own, probably computed height
this._height = -1;
// Broadcast the invalidation to the parent element
this._parent.invalidate();
}
};
/* -- PRIVATE FUNCTIONS -- */
/**
* Used to check whether an element is visible or not (non recursive).
*
* @param _obj is the element which should be checked for visibility, it is
* only checked whether some stylesheet makes the element invisible, not if
* the given object is actually inside the DOM.
*/
et2_dataview_container._isVisible = function (_obj) {
// Check whether the element is localy invisible
if (_obj.style && (_obj.style.display === "none"
|| _obj.style.visibility === "none")) {
return false;
}
// Get the computed style of the element
var style = window.getComputedStyle ? window.getComputedStyle(_obj, null)
// @ts-ignore
: _obj.currentStyle;
if (style.display === "none" || style.visibility === "none") {
return false;
}
return true;
};
/**
* Returns the height of a node in pixels and zero if the element is not
* visible. The height is clamped to positive values.
*
* @param {HTMLElement} _node
*/
et2_dataview_container._nodeHeight = function (_node) {
return _node.offsetHeight;
};
return et2_dataview_container;
}());
exports.et2_dataview_container = et2_dataview_container;
//# sourceMappingURL=et2_dataview_view_container.js.map

View File

@ -0,0 +1,421 @@
/**
* EGroupware eTemplate2 - dataview code
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage dataview
* @link http://www.egroupware.org
* @author Andreas Stöckel
* @copyright Stylite 2012
* @version $Id$
*/
/*egw:uses
/vendor/bower-asset/jquery/dist/jquery.js;
et2_dataview_interfaces;
*/
import {et2_dataview_IInvalidatable} from "./et2_dataview_interfaces";
/**
* The et2_dataview_container class is the main object each dataview consits of.
* Each row, spacer as well as the grid itself are containers. A container is
* described by its parent element and a certain height. On the DOM-Level a
* container may consist of multiple "tr" nodes, which are treated as a unit.
* Some containers (like grid containers) are capable of managing a set of child
* containers. Each container can indicate, that it thinks that it's height
* might have changed. In that case it informs its parent element about that.
* The only requirement for the parent element is, that it implements the
* et2_dataview_IInvalidatable interface.
* A container does not know where it resides inside the grid, or whether it is
* currently visible or not -- this information is efficiently managed by the
* et2_dataview_grid container.
*
* @augments Class
*/
export class et2_dataview_container implements et2_dataview_IInvalidatable
{
protected _parent: any;
// contains all DOM-Nodes this container exists of
private _nodes: any[];
private _inTree: boolean;
private _attachData: { node: JQuery; prepend: boolean };
private _destroyCallback: Function;
_destroyContext: any;
private _height: number;
private _index: number;
private _top: number;
protected tr: any;
/**
* Initializes the container object.
*
* @param _parent is an object which implements the IInvalidatable
* interface. _parent may not be null.
* @memberOf et2_dataview_container
*/
constructor(_parent)
{
// Copy the given invalidation element
this._parent = _parent;
this._nodes = [];
this._inTree = false;
this._attachData = {"node": null, "prepend": false};
this._destroyCallback = null;
this._destroyContext = null;
this._height = -1;
this._index = 0;
this._top = 0;
}
/**
* Destroys this container. Classes deriving from et2_dataview_container
* should override this method and take care of unregistering all event
* handlers etc.
*/
destroy()
{
// Remove the nodes from the tree
this.removeFromTree();
// Call the callback function (if one is registered)
if (this._destroyCallback)
{
this._destroyCallback.call(this._destroyContext, this);
}
}
/**
* Sets the "destroyCallback" -- the given function gets called whenever
* the container is destroyed. This instance is passed as an parameter to
* the callback.
*
* @param {function} _callback
* @param {object} _context
*/
setDestroyCallback(_callback : Function, _context : object) {
this._destroyCallback = _callback;
this._destroyContext = _context;
}
/**
* Inserts all container nodes into the DOM tree after or before the given
* element.
*
* @param _node is the node after/before which the container "tr"s should
* get inserted. _node should be a simple DOM node, not a jQuery object.
* @param _prepend specifies whether the container should be inserted before
* or after the given node. Inserting before is needed for inserting the
* first element in front of an spacer.
*/
insertIntoTree(_node: JQuery, _prepend: boolean)
{
if (!this._inTree && _node != null && this._nodes.length > 0)
{
// Store the parent node and indicate that this element is now in
// the tree.
this._attachData = {node: _node, prepend: _prepend};
this._inTree = true;
for (let i = 0; i < this._nodes.length; i++)
{
if (i == 0)
{
if (_prepend)
{
_node.before(this._nodes[0]);
}
else
{
_node.after(this._nodes[0]);
}
}
else
{
// Insert all following nodes after the previous node
this._nodes[i - 1].after(this._nodes[i]);
}
}
// Invalidate this element in order to update the height of the
// parent
this.invalidate();
}
}
/**
* Removes all container nodes from the tree.
*/
removeFromTree()
{
if (this._inTree)
{
// Call the jQuery remove function to remove all nodes from the tree
// again.
for (let i = 0; i < this._nodes.length; i++)
{
this._nodes[i].remove();
}
// Reset the "attachData"
this._inTree = false;
this._attachData = {"node": null, "prepend": false};
}
}
/**
* Appends a node to the container.
*
* @param _node is the DOM-Node which should be appended.
*/
appendNode(_node : JQuery | HTMLElement)
{
// Add the given node to the "nodes" array
this._nodes.push(_node);
// If the container is already in the tree, attach the given node to the
// tree.
if (this._inTree)
{
if (this._nodes.length === 1)
{
if (this._attachData.prepend)
{
this._attachData.node.before(_node);
}
else
{
this._attachData.node.after(_node);
}
}
else
{
this._nodes[this._nodes.length - 2].after(_node);
}
this.invalidate();
}
}
/**
* Removes a certain node from the container
*
* @param {HTMLElement} _node
*/
removeNode(_node: HTMLElement)
{
// Get the index of the node in the nodes array
const idx = this._nodes.indexOf(_node);
if (idx >= 0)
{
// Remove the node if the container is currently attached
if (this._inTree)
{
_node.parentNode.removeChild(_node);
}
// Remove the node from the nodes array
this._nodes.splice(idx, 1);
}
}
/**
* Returns the last node of the container - new nodes have to be appended
* after it.
*/
getLastNode()
{
if (this._nodes.length > 0)
{
return this._nodes[this._nodes.length - 1];
}
return null;
}
/**
* Returns the first node of the container.
*/
getFirstNode()
{
return this._nodes.length > 0 ? this._nodes[0] : null;
}
/**
* Returns the accumulated height of all container nodes. Only visible nodes
* (without "display: none" etc.) are taken into account.
*/
getHeight()
{
if (this._height === -1 && this._inTree)
{
this._height = 0;
// Setting this before measuring height helps with issues getting the
// wrong height due to margins & collapsed borders
this.tr.css('display','block');
// Increment the height value for each visible container node
for (let i = 0; i < this._nodes.length; i++)
{
if (et2_dataview_container._isVisible(this._nodes[i][0]))
{
this._height += et2_dataview_container._nodeHeight(this._nodes[i][0]);
}
}
this.tr.css('display','');
}
return ( this._height === -1 ) ? 0 : this._height;
}
/**
* Returns a datastructure containing information used for calculating the
* average row height of a grid.
* The datastructure has the
* {
* avgHeight: <the calculated average height of this element>,
* avgCount: <the element count this calculation was based on>
* }
*/
getAvgHeightData()
{
return {
"avgHeight": this.getHeight(),
"avgCount": 1
};
}
/**
* Returns the previously set "pixel top" of the container.
*/
getTop()
{
return this._top;
}
/**
* Returns the "pixel bottom" of the container.
*/
getBottom()
{
return this._top + this.getHeight();
}
/**
* Returns the range of the element.
*/
getRange()
{
return et2_bounds(this.getTop(), this.getBottom());
}
/**
* Returns the index of the element.
*/
getIndex()
{
return this._index;
}
/**
* Returns how many elements this container represents.
*/
getCount()
{
return 1;
}
/**
* Sets the top of the element.
*
* @param {number} _value
*/
setTop(_value)
{
this._top = _value;
}
/**
* Sets the index of the element.
*
* @param {number} _value
*/
setIndex(_value)
{
this._index = _value;
}
/* -- et2_dataview_IInvalidatable -- */
/**
* Broadcasts an invalidation through the container tree. Marks the own
* height as invalid.
*/
invalidate()
{
// Abort if this element is already marked as invalid.
if ( this._height !== -1)
{
// Delete the own, probably computed height
this._height = -1;
// Broadcast the invalidation to the parent element
this._parent.invalidate();
}
}
/* -- PRIVATE FUNCTIONS -- */
/**
* Used to check whether an element is visible or not (non recursive).
*
* @param _obj is the element which should be checked for visibility, it is
* only checked whether some stylesheet makes the element invisible, not if
* the given object is actually inside the DOM.
*/
private static _isVisible(_obj : HTMLElement)
{
// Check whether the element is localy invisible
if (_obj.style && (_obj.style.display === "none"
|| _obj.style.visibility === "none"))
{
return false;
}
// Get the computed style of the element
const style = window.getComputedStyle ? window.getComputedStyle(_obj, null)
// @ts-ignore
: _obj.currentStyle;
if (style.display === "none" || style.visibility === "none")
{
return false;
}
return true;
}
/**
* Returns the height of a node in pixels and zero if the element is not
* visible. The height is clamped to positive values.
*
* @param {HTMLElement} _node
*/
private static _nodeHeight(_node : HTMLElement)
{
return _node.offsetHeight;
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,4 @@
"use strict";
/**
* EGroupware eTemplate2 - Functions which allow resizing of table headers
*
@ -9,222 +10,174 @@
* @copyright Stylite 2011
* @version $Id$
*/
Object.defineProperty(exports, "__esModule", { value: true });
/**
* This set of functions is currently only supporting resizing in ew-direction
*/
(function()
{
"use strict";
// Define some constants
var RESIZE_BORDER = 12;
var RESIZE_MIN_WIDTH = 25;
var RESIZE_ADD = 2; // Used to ensure mouse is under the resize element after resizing has finished
// In resize region returns whether the mouse is currently in the
// "resizeRegion"
function inResizeRegion(_x, _elem)
{
var ol = _x - _elem.offset().left;
return (ol > (_elem.outerWidth(true) - RESIZE_BORDER));
}
var helper = null;
var overlay = null;
var didResize = false;
var resizeWidth = 0;
function startResize(_outerElem, _elem, _callback, _column)
{
if (overlay == null || helper == null)
{
// Prevent text selection
// FireFox handles highlight prevention (text selection) different than other browsers
if (typeof _elem[0].style.MozUserSelect !="undefined")
{
_elem[0].style.MozUserSelect = "none";
}
else
{
_elem[0].onselectstart = function() {
return false;
};
}
// Indicate resizing is in progress
jQuery(_outerElem).addClass('egwResizing');
// Reset the "didResize" flag
didResize = false;
// Create the resize helper
var left = _elem.offset().left;
helper = jQuery(document.createElement("div"))
.addClass("egwResizeHelper")
.appendTo("body")
.css("top", _elem.offset().top + "px")
.css("left", left + "px")
.css("height", _outerElem.outerHeight(true) + "px");
// Create the overlay which will be catching the mouse movements
overlay = jQuery(document.createElement("div"))
.addClass("egwResizeOverlay")
.bind("mousemove", function(e) {
didResize = true;
resizeWidth = Math.max(e.pageX - left + RESIZE_ADD,
_column && _column.minWidth ? _column.minWidth : RESIZE_MIN_WIDTH
);
helper.css("width", resizeWidth + "px");
})
.bind("mouseup", function() {
stopResize(_outerElem);
// Reset text selection
_elem[0].onselectstart = null;
// Call the callback if the user actually performed a resize
if (didResize)
{
_callback(resizeWidth);
}
})
.appendTo("body");
}
}
function stopResize(_outerElem)
{
jQuery(_outerElem).removeClass('egwResizing');
if (helper != null)
{
helper.remove();
helper = null;
}
if (overlay != null)
{
overlay.remove();
overlay = null;
}
}
this.et2_dataview_makeResizeable = function(_elem, _callback, _context)
{
// Get the table surrounding the given element - this element is used to
// align the helper properly
var outerTable = _elem.closest("table");
// Bind the "mousemove" event in the "resize" namespace
_elem.bind("mousemove.resize", function(e) {
var stopResize = false;
// Stop switch to resize cursor if the mouse position
// is more intended for scrollbar not the resize edge
// 8pixel is an arbitary number for scrolbar area
if (e.target.clientHeight < e.target.scrollHeight && e.target.offsetWidth - e.offsetX <= 8)
{
stopResize = true;
}
_elem.css("cursor", inResizeRegion(e.pageX, _elem) && !stopResize? "ew-resize" : "auto");
});
// Bind the "mousedown" event in the "resize" namespace
_elem.bind("mousedown.resize", function(e) {
var stopResize = false;
// Stop resize if the mouse position is more intended
// for scrollbar not the resize edge
// 8pixel is an arbitary number for scrolbar area
if (e.target.clientHeight < e.target.scrollHeight && e.target.offsetWidth - e.offsetX <= 8)
{
stopResize = true;
}
// Do not triger startResize if clicked element is select-tag, as it may causes conflict in some browsers
if (inResizeRegion(e.pageX, _elem) && e.target.tagName != 'SELECT' && !stopResize)
{
// Start the resizing
startResize(outerTable, _elem, function(_w) {
_callback.call(_context, _w);
}, _context);
}
});
// Bind double click for auto-size
_elem.dblclick(function(e) {
// Just show message for relative width columns
if(_context && _context.relativeWidth)
{
return egw.message(egw.lang('You tried to automatically size a flex column, which always takes the rest of the space','info'));
}
// Find column class - it's usually the first one
var col_class = '';
for(var i = 0; i < this.classList.length; i++)
{
if(this.classList[i].indexOf('gridCont') === 0)
{
col_class = this.classList[i];
break;
}
}
// Find widest part, including header
var column = jQuery(this);
column.children().css('width','auto');
var max_width = column.children().children().innerWidth();
var padding = column.outerWidth(true) - max_width;
var resize = jQuery(this).closest('.egwGridView_outer')
.find('tbody td.'+col_class+'> div:first-child')
.add(column.children())
// Set column width to auto to allow space for everything to flow
.css('width','auto');
resize.children()
.css({'white-space':'nowrap'})
.each(function()
{
var col = jQuery(this);
// Find visible (text) children and force them to not wrap
var children = col.find('span:visible, time:visible, label:visible')
.css({'white-space':'nowrap'})
this.offsetWidth;
children.each(function()
{
var child = jQuery(this);
this.offsetWidth;
if(child.outerWidth() > max_width)
{
max_width = child.outerWidth();
}
window.getComputedStyle(this).width;
});
this.offsetWidth;
if(col.innerWidth() > max_width)
{
max_width = col.innerWidth();
}
// Reset children
children.css('white-space','');
children.css('display','');
}
)
.css({'white-space':''});
// Reset column
column.children().css('width','');
resize.css('width','');
_callback.call(_context, max_width+padding);
});
};
this.et2_dataview_resetResizeable = function(_elem)
{
// Remove all events in the ".resize" namespace from the element
_elem.unbind(".resize");
};
}).call(window);
var et2_dataview_view_resizable = /** @class */ (function () {
function et2_dataview_view_resizable() {
}
// In resize region returns whether the mouse is currently in the
// "resizeRegion"
et2_dataview_view_resizable.inResizeRegion = function (_x, _elem) {
var ol = _x - _elem.offset().left;
return (ol > (_elem.outerWidth(true) - et2_dataview_view_resizable.RESIZE_BORDER));
};
et2_dataview_view_resizable.startResize = function (_outerElem, _elem, _callback, _column) {
if (this.overlay == null || this.helper == null) {
// Prevent text selection
// FireFox handles highlight prevention (text selection) different than other browsers
if (typeof _elem[0].style.MozUserSelect != "undefined") {
_elem[0].style.MozUserSelect = "none";
}
else {
_elem[0].onselectstart = function () {
return false;
};
}
// Indicate resizing is in progress
jQuery(_outerElem).addClass('egwResizing');
// Reset the "didResize" flag
this.didResize = false;
// Create the resize helper
var left = _elem.offset().left;
this.helper = jQuery(document.createElement("div"))
.addClass("egwResizeHelper")
.appendTo("body")
.css("top", _elem.offset().top + "px")
.css("left", left + "px")
.css("height", _outerElem.outerHeight(true) + "px");
// Create the overlay which will be catching the mouse movements
this.overlay = jQuery(document.createElement("div"))
.addClass("egwResizeOverlay")
.bind("mousemove", function (e) {
this.didResize = true;
this.resizeWidth = Math.max(e.pageX - left + et2_dataview_view_resizable.RESIZE_ADD, _column && _column.minWidth ? _column.minWidth : et2_dataview_view_resizable.RESIZE_MIN_WIDTH);
this.helper.css("width", this.resizeWidth + "px");
}.bind(this))
.bind("mouseup", function () {
this.stopResize(_outerElem);
// Reset text selection
_elem[0].onselectstart = null;
// Call the callback if the user actually performed a resize
if (this.didResize) {
_callback(this.resizeWidth);
}
}.bind(this))
.appendTo("body");
}
};
et2_dataview_view_resizable.stopResize = function (_outerElem) {
jQuery(_outerElem).removeClass('egwResizing');
if (this.helper != null) {
this.helper.remove();
this.helper = null;
}
if (this.overlay != null) {
this.overlay.remove();
this.overlay = null;
}
};
// Define some constants
et2_dataview_view_resizable.RESIZE_BORDER = 12;
et2_dataview_view_resizable.RESIZE_MIN_WIDTH = 25;
et2_dataview_view_resizable.RESIZE_ADD = 2; // Used to ensure mouse is under the resize element after resizing has finished
et2_dataview_view_resizable.helper = null;
et2_dataview_view_resizable.overlay = null;
et2_dataview_view_resizable.didResize = false;
et2_dataview_view_resizable.resizeWidth = 0;
et2_dataview_view_resizable.makeResizeable = function (_elem, _callback, _context) {
// Get the table surrounding the given element - this element is used to
// align the helper properly
var outerTable = _elem.closest("table");
// Bind the "mousemove" event in the "resize" namespace
_elem.bind("mousemove.resize", function (e) {
var stopResize = false;
// Stop switch to resize cursor if the mouse position
// is more intended for scrollbar not the resize edge
// 8pixel is an arbitary number for scrolbar area
if (e.target.clientHeight < e.target.scrollHeight && e.target.offsetWidth - e.offsetX <= 8) {
stopResize = true;
}
_elem.css("cursor", et2_dataview_view_resizable.inResizeRegion(e.pageX, _elem) && !stopResize ? "ew-resize" : "auto");
});
// Bind the "mousedown" event in the "resize" namespace
_elem.bind("mousedown.resize", function (e) {
var stopResize = false;
// Stop resize if the mouse position is more intended
// for scrollbar not the resize edge
// 8pixel is an arbitary number for scrolbar area
if (e.target.clientHeight < e.target.scrollHeight && e.target.offsetWidth - e.offsetX <= 8) {
stopResize = true;
}
// Do not triger startResize if clicked element is select-tag, as it may causes conflict in some browsers
if (et2_dataview_view_resizable.inResizeRegion(e.pageX, _elem) && e.target.tagName != 'SELECT' && !stopResize) {
// Start the resizing
et2_dataview_view_resizable.startResize(outerTable, _elem, function (_w) {
_callback.call(_context, _w);
}, _context);
}
});
// Bind double click for auto-size
_elem.dblclick(function (e) {
// Just show message for relative width columns
if (_context && _context.relativeWidth) {
return egw.message(egw.lang('You tried to automatically size a flex column, which always takes the rest of the space', 'info'));
}
// Find column class - it's usually the first one
var col_class = '';
for (var i = 0; i < this.classList.length; i++) {
if (this.classList[i].indexOf('gridCont') === 0) {
col_class = this.classList[i];
break;
}
}
// Find widest part, including header
var column = jQuery(this);
column.children().css('width', 'auto');
var max_width = column.children().children().innerWidth();
var padding = column.outerWidth(true) - max_width;
var resize = jQuery(this).closest('.egwGridView_outer')
.find('tbody td.' + col_class + '> div:first-child')
.add(column.children())
// Set column width to auto to allow space for everything to flow
.css('width', 'auto');
resize.children()
.css({ 'white-space': 'nowrap' })
.each(function () {
var col = jQuery(this);
// Find visible (text) children and force them to not wrap
var children = col.find('span:visible, time:visible, label:visible')
.css({ 'white-space': 'nowrap' });
this.offsetWidth;
children.each(function () {
var child = jQuery(this);
this.offsetWidth;
if (child.outerWidth() > max_width) {
max_width = child.outerWidth();
}
window.getComputedStyle(this).width;
});
this.offsetWidth;
if (col.innerWidth() > max_width) {
max_width = col.innerWidth();
}
// Reset children
children.css('white-space', '');
children.css('display', '');
})
.css({ 'white-space': '' });
// Reset column
column.children().css('width', '');
resize.css('width', '');
_callback.call(_context, max_width + padding);
});
};
et2_dataview_view_resizable.et2_dataview_resetResizeable = function (_elem) {
// Remove all events in the ".resize" namespace from the element
_elem.unbind(".resize");
};
return et2_dataview_view_resizable;
}());
exports.et2_dataview_view_resizable = et2_dataview_view_resizable;
//# sourceMappingURL=et2_dataview_view_resizeable.js.map

View File

@ -0,0 +1,230 @@
/**
* EGroupware eTemplate2 - Functions which allow resizing of table headers
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage dataview
* @link http://www.egroupware.org
* @author Andreas Stöckel
* @copyright Stylite 2011
* @version $Id$
*/
/**
* This set of functions is currently only supporting resizing in ew-direction
*/
export class et2_dataview_view_resizable
{
// Define some constants
public static readonly RESIZE_BORDER = 12;
public static readonly RESIZE_MIN_WIDTH = 25;
public static readonly RESIZE_ADD = 2; // Used to ensure mouse is under the resize element after resizing has finished
public static helper : JQuery = null;
public static overlay : JQuery = null;
public static didResize = false;
public static resizeWidth = 0;
// In resize region returns whether the mouse is currently in the
// "resizeRegion"
public static inResizeRegion(_x, _elem)
{
var ol = _x - _elem.offset().left;
return (ol > (_elem.outerWidth(true) - et2_dataview_view_resizable.RESIZE_BORDER));
}
public static startResize(_outerElem, _elem, _callback, _column)
{
if (this.overlay == null || this.helper == null)
{
// Prevent text selection
// FireFox handles highlight prevention (text selection) different than other browsers
if (typeof _elem[0].style.MozUserSelect !="undefined")
{
_elem[0].style.MozUserSelect = "none";
}
else
{
_elem[0].onselectstart = function() {
return false;
};
}
// Indicate resizing is in progress
jQuery(_outerElem).addClass('egwResizing');
// Reset the "didResize" flag
this.didResize = false;
// Create the resize helper
var left = _elem.offset().left;
this.helper = jQuery(document.createElement("div"))
.addClass("egwResizeHelper")
.appendTo("body")
.css("top", _elem.offset().top + "px")
.css("left", left + "px")
.css("height", _outerElem.outerHeight(true) + "px");
// Create the overlay which will be catching the mouse movements
this.overlay = jQuery(document.createElement("div"))
.addClass("egwResizeOverlay")
.bind("mousemove", function(e) {
this.didResize = true;
this.resizeWidth = Math.max(e.pageX - left + et2_dataview_view_resizable.RESIZE_ADD,
_column && _column.minWidth ? _column.minWidth : et2_dataview_view_resizable.RESIZE_MIN_WIDTH
);
this.helper.css("width", this.resizeWidth + "px");
}.bind(this))
.bind("mouseup", function() {
this.stopResize(_outerElem);
// Reset text selection
_elem[0].onselectstart = null;
// Call the callback if the user actually performed a resize
if (this.didResize)
{
_callback(this.resizeWidth);
}
}.bind(this))
.appendTo("body");
}
}
public static stopResize(_outerElem)
{
jQuery(_outerElem).removeClass('egwResizing');
if (this.helper != null)
{
this.helper.remove();
this.helper = null;
}
if (this.overlay != null)
{
this.overlay.remove();
this.overlay = null;
}
}
public static makeResizeable = function(_elem, _callback, _context)
{
// Get the table surrounding the given element - this element is used to
// align the helper properly
var outerTable = _elem.closest("table");
// Bind the "mousemove" event in the "resize" namespace
_elem.bind("mousemove.resize", function(e) {
var stopResize = false;
// Stop switch to resize cursor if the mouse position
// is more intended for scrollbar not the resize edge
// 8pixel is an arbitary number for scrolbar area
if (e.target.clientHeight < e.target.scrollHeight && e.target.offsetWidth - e.offsetX <= 8)
{
stopResize = true;
}
_elem.css("cursor", et2_dataview_view_resizable.inResizeRegion(e.pageX, _elem) && !stopResize? "ew-resize" : "auto");
});
// Bind the "mousedown" event in the "resize" namespace
_elem.bind("mousedown.resize", function(e) {
var stopResize = false;
// Stop resize if the mouse position is more intended
// for scrollbar not the resize edge
// 8pixel is an arbitary number for scrolbar area
if (e.target.clientHeight < e.target.scrollHeight && e.target.offsetWidth - e.offsetX <= 8)
{
stopResize = true;
}
// Do not triger startResize if clicked element is select-tag, as it may causes conflict in some browsers
if (et2_dataview_view_resizable.inResizeRegion(e.pageX, _elem) && e.target.tagName != 'SELECT' && !stopResize)
{
// Start the resizing
et2_dataview_view_resizable.startResize(outerTable, _elem, function(_w) {
_callback.call(_context, _w);
}, _context);
}
});
// Bind double click for auto-size
_elem.dblclick(function(e) {
// Just show message for relative width columns
if(_context && _context.relativeWidth)
{
return egw.message(egw.lang('You tried to automatically size a flex column, which always takes the rest of the space','info'));
}
// Find column class - it's usually the first one
var col_class = '';
for(var i = 0; i < this.classList.length; i++)
{
if(this.classList[i].indexOf('gridCont') === 0)
{
col_class = this.classList[i];
break;
}
}
// Find widest part, including header
var column = jQuery(this);
column.children().css('width','auto');
var max_width = column.children().children().innerWidth();
var padding = column.outerWidth(true) - max_width;
var resize = jQuery(this).closest('.egwGridView_outer')
.find('tbody td.'+col_class+'> div:first-child')
.add(column.children())
// Set column width to auto to allow space for everything to flow
.css('width','auto');
resize.children()
.css({'white-space':'nowrap'})
.each(function()
{
var col = jQuery(this);
// Find visible (text) children and force them to not wrap
var children = col.find('span:visible, time:visible, label:visible')
.css({'white-space':'nowrap'});
this.offsetWidth;
children.each(function()
{
var child = jQuery(this);
this.offsetWidth;
if(child.outerWidth() > max_width)
{
max_width = child.outerWidth();
}
window.getComputedStyle(this).width;
});
this.offsetWidth;
if(col.innerWidth() > max_width)
{
max_width = col.innerWidth();
}
// Reset children
children.css('white-space','');
children.css('display','');
}
)
.css({'white-space':''});
// Reset column
column.children().css('width','');
resize.css('width','');
_callback.call(_context, max_width+padding);
});
};
public static et2_dataview_resetResizeable = function(_elem)
{
// Remove all events in the ".resize" namespace from the element
_elem.unbind(".resize");
}
}

View File

@ -1,3 +1,4 @@
"use strict";
/**
* EGroupware eTemplate2 - dataview
*
@ -8,188 +9,152 @@
* @author Andreas Stöckel
* @copyright Stylite 2011-2012
* @version $Id$
*/
*
/*egw:uses
egw_action.egw_action;
egw_action.egw_action;
et2_dataview_view_container;
et2_dataview_view_container;
*/
/**
* @augments et2_dataview_container
*/
var et2_dataview_row = (function(){ "use strict"; return et2_dataview_container.extend(et2_dataview_IViewRange,
{
/**
* Creates the row container. Use the "setRow" function to load the actual
* row content.
*
* @param _parent is the row parent container.
* @memberOf et2_dataview_row
*/
init: function(_parent) {
// Call the inherited constructor
this._super(_parent);
// Create the outer "tr" tag and append it to the container
this.tr = jQuery(document.createElement("tr"));
this.appendNode(this.tr);
// Grid row which gets expanded when clicking on the corresponding
// button
this.expansionContainer = null;
this.expansionVisible = false;
// Toggle button which is used to show and hide the expansionContainer
this.expansionButton = null;
},
destroy: function () {
if (this.expansionContainer != null)
{
this.expansionContainer.free();
}
this._super();
},
clear: function() {
this.tr.empty();
},
makeExpandable: function (_expandable, _callback, _context) {
if (_expandable)
{
// Create the tr and the button if this has not been done yet
if (!this.expansionButton)
{
this.expansionButton = jQuery(document.createElement("span"));
this.expansionButton.addClass("arrow closed");
}
// Update context
var self = this;
this.expansionButton.off("click").on("click", function (e) {
self._handleExpansionButtonClick(_callback, _context);
e.stopImmediatePropagation();
});
jQuery("td:first", this.tr).prepend(this.expansionButton);
}
else
{
// If the row is made non-expandable, remove the created DOM-Nodes
if (this.expansionButton)
{
this.expansionButton.remove();
}
if (this.expansionContainer)
{
this.expansionContainer.free();
}
this.expansionButton = null;
this.expansionContainer = null;
}
},
removeFromTree: function () {
if (this.expansionContainer)
{
this.expansionContainer.removeFromTree();
}
this.expansionContainer = null;
this.expansionButton = null;
this._super();
},
getDOMNode: function () {
return this.tr[0];
},
getJNode: function () {
return this.tr;
},
getHeight: function () {
var h = this._super();
if (this.expansionContainer && this.expansionVisible)
{
h += this.expansionContainer.getHeight();
}
return h;
},
getAvgHeightData: function() {
// Only take the height of the own tr into account
//var oldVisible = this.expansionVisible;
this.expansionVisible = false;
var res = {
"avgHeight": this.getHeight(),
"avgCount": 1
};
this.expansionVisible = true;
return res;
},
/** -- PRIVATE FUNCTIONS -- **/
_handleExpansionButtonClick: function (_callback, _context) {
// Create the "expansionContainer" if it does not exist yet
if (!this.expansionContainer)
{
this.expansionContainer = _callback.call(_context);
this.expansionContainer.insertIntoTree(this.tr);
this.expansionVisible = false;
}
// Toggle the visibility of the expansion tr
this.expansionVisible = !this.expansionVisible;
jQuery(this.expansionContainer._nodes[0]).toggle(this.expansionVisible);
// Set the class of the arrow
if (this.expansionVisible)
{
this.expansionButton.addClass("opened");
this.expansionButton.removeClass("closed");
}
else
{
this.expansionButton.addClass("closed");
this.expansionButton.removeClass("opened");
}
this.invalidate();
},
/** -- Implementation of et2_dataview_IViewRange -- **/
setViewRange: function (_range) {
if (this.expansionContainer && this.expansionVisible
&& this.expansionContainer.implements(et2_dataview_IViewRange))
{
// Substract the height of the own row from the container
var oh = jQuery(this._nodes[0]).height();
_range.top -= oh;
// Proxy the setViewRange call to the expansion container
this.expansionContainer.setViewRange(_range);
}
}
});}).call(this);
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 });
var et2_dataview_row = /** @class */ (function (_super) {
__extends(et2_dataview_row, _super);
/**
* Creates the row container. Use the "setRow" function to load the actual
* row content.
*
* @param _parent is the row parent container.
*/
function et2_dataview_row(_parent) {
var _this =
// Call the inherited constructor
_super.call(this, _parent) || this;
// Create the outer "tr" tag and append it to the container
_this.tr = jQuery(document.createElement("tr"));
_this.appendNode(_this.tr);
// Grid row which gets expanded when clicking on the corresponding
// button
_this.expansionContainer = null;
_this.expansionVisible = false;
// Toggle button which is used to show and hide the expansionContainer
_this.expansionButton = null;
return _this;
}
et2_dataview_row.prototype.destroy = function () {
if (this.expansionContainer != null) {
this.expansionContainer.destroy();
}
_super.prototype.destroy.call(this);
};
et2_dataview_row.prototype.clear = function () {
this.tr.empty();
};
et2_dataview_row.prototype.makeExpandable = function (_expandable, _callback, _context) {
if (_expandable) {
// Create the tr and the button if this has not been done yet
if (!this.expansionButton) {
this.expansionButton = jQuery(document.createElement("span"));
this.expansionButton.addClass("arrow closed");
}
// Update context
var self = this;
this.expansionButton.off("click").on("click", function (e) {
self._handleExpansionButtonClick(_callback, _context);
e.stopImmediatePropagation();
});
jQuery("td:first", this.tr).prepend(this.expansionButton);
}
else {
// If the row is made non-expandable, remove the created DOM-Nodes
if (this.expansionButton) {
this.expansionButton.remove();
}
if (this.expansionContainer) {
this.expansionContainer.destroy();
}
this.expansionButton = null;
this.expansionContainer = null;
}
};
et2_dataview_row.prototype.removeFromTree = function () {
if (this.expansionContainer) {
this.expansionContainer.removeFromTree();
}
this.expansionContainer = null;
this.expansionButton = null;
_super.prototype.removeFromTree.call(this);
};
et2_dataview_row.prototype.getDOMNode = function () {
return this.tr[0];
};
et2_dataview_row.prototype.getJNode = function () {
return this.tr;
};
et2_dataview_row.prototype.getHeight = function () {
var h = _super.prototype.getHeight.call(this);
if (this.expansionContainer && this.expansionVisible) {
h += this.expansionContainer.getHeight();
}
return h;
};
et2_dataview_row.prototype.getAvgHeightData = function () {
// Only take the height of the own tr into account
//var oldVisible = this.expansionVisible;
this.expansionVisible = false;
var res = {
"avgHeight": this.getHeight(),
"avgCount": 1
};
this.expansionVisible = true;
return res;
};
/** -- PRIVATE FUNCTIONS -- **/
et2_dataview_row.prototype._handleExpansionButtonClick = function (_callback, _context) {
// Create the "expansionContainer" if it does not exist yet
if (!this.expansionContainer) {
this.expansionContainer = _callback.call(_context);
this.expansionContainer.insertIntoTree(this.tr);
this.expansionVisible = false;
}
// Toggle the visibility of the expansion tr
this.expansionVisible = !this.expansionVisible;
jQuery(this.expansionContainer._nodes[0]).toggle(this.expansionVisible);
// Set the class of the arrow
if (this.expansionVisible) {
this.expansionButton.addClass("opened");
this.expansionButton.removeClass("closed");
}
else {
this.expansionButton.addClass("closed");
this.expansionButton.removeClass("opened");
}
this.invalidate();
};
/** -- Implementation of et2_dataview_IViewRange -- **/
et2_dataview_row.prototype.setViewRange = function (_range) {
if (this.expansionContainer && this.expansionVisible
&& this.expansionContainer.implements(et2_dataview_IViewRange)) {
// Substract the height of the own row from the container
var oh = jQuery(this._nodes[0]).height();
_range.top -= oh;
// Proxy the setViewRange call to the expansion container
this.expansionContainer.setViewRange(_range);
}
};
return et2_dataview_row;
}(et2_dataview_container));
exports.et2_dataview_row = et2_dataview_row;
//# sourceMappingURL=et2_dataview_view_row.js.map

View File

@ -0,0 +1,203 @@
/**
* EGroupware eTemplate2 - dataview
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage dataview
* @link http://www.egroupware.org
* @author Andreas Stöckel
* @copyright Stylite 2011-2012
* @version $Id$
*
/*egw:uses
egw_action.egw_action;
et2_dataview_view_container;
*/
import {et2_dataview_IViewRange} from "./et2_dataview_interfaces";
export class et2_dataview_row extends et2_dataview_container implements et2_dataview_IViewRange
{
/**
* Creates the row container. Use the "setRow" function to load the actual
* row content.
*
* @param _parent is the row parent container.
*/
constructor( _parent)
{
// Call the inherited constructor
super(_parent);
// Create the outer "tr" tag and append it to the container
this.tr = jQuery(document.createElement("tr"));
this.appendNode(this.tr);
// Grid row which gets expanded when clicking on the corresponding
// button
this.expansionContainer = null;
this.expansionVisible = false;
// Toggle button which is used to show and hide the expansionContainer
this.expansionButton = null;
}
destroy( )
{
if (this.expansionContainer != null)
{
this.expansionContainer.destroy();
}
super.destroy();
}
clear( )
{
this.tr.empty();
}
makeExpandable( _expandable, _callback, _context)
{
if (_expandable)
{
// Create the tr and the button if this has not been done yet
if (!this.expansionButton)
{
this.expansionButton = jQuery(document.createElement("span"));
this.expansionButton.addClass("arrow closed");
}
// Update context
var self = this;
this.expansionButton.off("click").on("click", function (e) {
self._handleExpansionButtonClick(_callback, _context);
e.stopImmediatePropagation();
});
jQuery("td:first", this.tr).prepend(this.expansionButton);
}
else
{
// If the row is made non-expandable, remove the created DOM-Nodes
if (this.expansionButton)
{
this.expansionButton.remove();
}
if (this.expansionContainer)
{
this.expansionContainer.destroy();
}
this.expansionButton = null;
this.expansionContainer = null;
}
}
removeFromTree( )
{
if (this.expansionContainer)
{
this.expansionContainer.removeFromTree();
}
this.expansionContainer = null;
this.expansionButton = null;
super.removeFromTree();
}
getDOMNode( )
{
return this.tr[0];
}
getJNode( )
{
return this.tr;
}
getHeight( )
{
var h = super.getHeight();
if (this.expansionContainer && this.expansionVisible)
{
h += this.expansionContainer.getHeight();
}
return h;
}
getAvgHeightData( )
{
// Only take the height of the own tr into account
//var oldVisible = this.expansionVisible;
this.expansionVisible = false;
var res = {
"avgHeight": this.getHeight(),
"avgCount": 1
};
this.expansionVisible = true;
return res;
}
/** -- PRIVATE FUNCTIONS -- **/
_handleExpansionButtonClick( _callback, _context)
{
// Create the "expansionContainer" if it does not exist yet
if (!this.expansionContainer)
{
this.expansionContainer = _callback.call(_context);
this.expansionContainer.insertIntoTree(this.tr);
this.expansionVisible = false;
}
// Toggle the visibility of the expansion tr
this.expansionVisible = !this.expansionVisible;
jQuery(this.expansionContainer._nodes[0]).toggle(this.expansionVisible);
// Set the class of the arrow
if (this.expansionVisible)
{
this.expansionButton.addClass("opened");
this.expansionButton.removeClass("closed");
}
else
{
this.expansionButton.addClass("closed");
this.expansionButton.removeClass("opened");
}
this.invalidate();
}
/** -- Implementation of et2_dataview_IViewRange -- **/
setViewRange( _range)
{
if (this.expansionContainer && this.expansionVisible
&& this.expansionContainer.implements(et2_dataview_IViewRange))
{
// Substract the height of the own row from the container
var oh = jQuery(this._nodes[0]).height();
_range.top -= oh;
// Proxy the setViewRange call to the expansion container
this.expansionContainer.setViewRange(_range);
}
}
}

View File

@ -1,3 +1,4 @@
"use strict";
/**
* EGroupware eTemplate2 - Class which contains a factory method for rows
*
@ -9,121 +10,103 @@
* @copyright Stylite 2011
* @version $Id$
*/
Object.defineProperty(exports, "__esModule", { value: true });
/*egw:uses
/vendor/bower-asset/jquery/dist/jquery.js;
et2_core_inheritance;
et2_core_interfaces;
et2_core_arrayMgr;
et2_core_widget;
/vendor/bower-asset/jquery/dist/jquery.js;
et2_core_inheritance;
et2_core_interfaces;
et2_core_arrayMgr;
et2_core_widget;
*/
/**
* The row provider contains prototypes (full clonable dom-trees)
* for all registered row types.
*
* @augments Class
*/
var et2_dataview_rowProvider = (function(){ "use strict"; return Class.extend(
{
/**
*
* @param _outerId
* @param _columnIds
* @memberOf et2_dataview_rowProvider
*/
init: function(_outerId, _columnIds) {
// Copy the given parameters
this._outerId = _outerId;
this._columnIds = _columnIds;
this._prototypes = {};
this._template = null;
this._mgrs = null;
this._rootWidget = null;
// Create the default row "prototypes"
this._createFullRowPrototype();
this._createDefaultPrototype();
this._createEmptyPrototype();
this._createLoadingPrototype();
},
getColumnCount: function() {
return this._columnIds.length;
},
/**
* Returns a clone of the prototype with the given name. If the generator
* callback function is given, this function is called if the prototype
* does not yet registered.
*
* @param {string} _name
* @param {function} _generator
* @param {object} _context
*/
getPrototype: function(_name, _generator, _context) {
if (typeof this._prototypes[_name] == "undefined")
{
if (typeof _generator != "undefined")
{
this._prototypes[_name] = _generator.call(_context, this._outerId,
this._columnIds);
}
else
{
return null;
}
}
return this._prototypes[_name].clone();
},
/* ---- PRIVATE FUNCTIONS ---- */
_createFullRowPrototype: function() {
var tr = jQuery(document.createElement("tr"));
var td = jQuery(document.createElement("td"))
.addClass(this._outerId + "_td_fullRow")
.attr("colspan", this._columnIds.length)
.appendTo(tr);
var div = jQuery(document.createElement("div"))
.addClass(this._outerId + "_div_fullRow")
.appendTo(td);
this._prototypes["fullRow"] = tr;
},
_createDefaultPrototype: function() {
var tr = jQuery(document.createElement("tr"));
// Append a td for each column
for (var i = 0; i < this._columnIds.length; i++)
{
var td = jQuery(document.createElement("td"))
.addClass(this._outerId + "_td_" + this._columnIds[i])
.appendTo(tr);
var div = jQuery(document.createElement("div"))
.addClass(this._outerId + "_div_" + this._columnIds[i])
.addClass("innerContainer")
.appendTo(td);
}
this._prototypes["default"] = tr;
},
_createEmptyPrototype: function() {
this._prototypes["empty"] = jQuery(document.createElement("tr"));
},
_createLoadingPrototype: function() {
var fullRow = this.getPrototype("fullRow");
jQuery("div", fullRow).addClass("loading");
this._prototypes["loading"] = fullRow;
}
});}).call(this);
var et2_dataview_rowProvider = /** @class */ (function () {
/**
*
* @param _outerId
* @param _columnIds
*/
function et2_dataview_rowProvider(_outerId, _columnIds) {
// Copy the given parameters
this._outerId = _outerId;
this._columnIds = _columnIds;
this._prototypes = {};
this._template = null;
this._mgrs = null;
this._rootWidget = null;
// Create the default row "prototypes"
this._createFullRowPrototype();
this._createDefaultPrototype();
this._createEmptyPrototype();
this._createLoadingPrototype();
}
et2_dataview_rowProvider.prototype.destroy = function () {
this._template = null;
this._mgrs = null;
this._rootWidget = null;
this._prototypes = {};
this._columnIds = [];
};
et2_dataview_rowProvider.prototype.getColumnCount = function () {
return this._columnIds.length;
};
/**
* Returns a clone of the prototype with the given name. If the generator
* callback function is given, this function is called if the prototype
* does not yet registered.
*
* @param {string} _name
* @param {function} _generator
* @param {object} _context
*/
et2_dataview_rowProvider.prototype.getPrototype = function (_name, _generator, _context) {
if (typeof this._prototypes[_name] == "undefined") {
if (typeof _generator != "undefined") {
this._prototypes[_name] = _generator.call(_context, this._outerId, this._columnIds);
}
else {
return null;
}
}
return this._prototypes[_name].clone();
};
/* ---- PRIVATE FUNCTIONS ---- */
et2_dataview_rowProvider.prototype._createFullRowPrototype = function () {
var tr = jQuery(document.createElement("tr"));
var td = jQuery(document.createElement("td"))
.addClass(this._outerId + "_td_fullRow")
.attr("colspan", this._columnIds.length)
.appendTo(tr);
var div = jQuery(document.createElement("div"))
.addClass(this._outerId + "_div_fullRow")
.appendTo(td);
this._prototypes["fullRow"] = tr;
};
et2_dataview_rowProvider.prototype._createDefaultPrototype = function () {
var tr = jQuery(document.createElement("tr"));
// Append a td for each column
for (var i = 0; i < this._columnIds.length; i++) {
var td = jQuery(document.createElement("td"))
.addClass(this._outerId + "_td_" + this._columnIds[i])
.appendTo(tr);
var div = jQuery(document.createElement("div"))
.addClass(this._outerId + "_div_" + this._columnIds[i])
.addClass("innerContainer")
.appendTo(td);
}
this._prototypes["default"] = tr;
};
et2_dataview_rowProvider.prototype._createEmptyPrototype = function () {
this._prototypes["empty"] = jQuery(document.createElement("tr"));
};
et2_dataview_rowProvider.prototype._createLoadingPrototype = function () {
var fullRow = this.getPrototype("fullRow");
jQuery("div", fullRow).addClass("loading");
this._prototypes["loading"] = fullRow;
};
return et2_dataview_rowProvider;
}());
exports.et2_dataview_rowProvider = et2_dataview_rowProvider;
//# sourceMappingURL=et2_dataview_view_rowProvider.js.map

View File

@ -0,0 +1,148 @@
/**
* EGroupware eTemplate2 - Class which contains a factory method for rows
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage dataview
* @link http://www.egroupware.org
* @author Andreas Stöckel
* @copyright Stylite 2011
* @version $Id$
*/
/*egw:uses
/vendor/bower-asset/jquery/dist/jquery.js;
et2_core_inheritance;
et2_core_interfaces;
et2_core_arrayMgr;
et2_core_widget;
*/
/**
* The row provider contains prototypes (full clonable dom-trees)
* for all registered row types.
*/
export class et2_dataview_rowProvider
{
private _outerId: any;
private _columnIds: any;
private _prototypes: {};
private _template: null;
private _mgrs: null;
private _rootWidget: null;
/**
*
* @param _outerId
* @param _columnIds
*/
constructor( _outerId, _columnIds)
{
// Copy the given parameters
this._outerId = _outerId;
this._columnIds = _columnIds;
this._prototypes = {};
this._template = null;
this._mgrs = null;
this._rootWidget = null;
// Create the default row "prototypes"
this._createFullRowPrototype();
this._createDefaultPrototype();
this._createEmptyPrototype();
this._createLoadingPrototype();
}
public destroy()
{
this._template = null;
this._mgrs = null;
this._rootWidget = null;
this._prototypes = {};
this._columnIds = [];
}
public getColumnCount()
{
return this._columnIds.length;
}
/**
* Returns a clone of the prototype with the given name. If the generator
* callback function is given, this function is called if the prototype
* does not yet registered.
*
* @param {string} _name
* @param {function} _generator
* @param {object} _context
*/
getPrototype( _name : string, _generator? : Function, _context? : any)
{
if (typeof this._prototypes[_name] == "undefined")
{
if (typeof _generator != "undefined")
{
this._prototypes[_name] = _generator.call(_context, this._outerId,
this._columnIds);
}
else
{
return null;
}
}
return this._prototypes[_name].clone();
}
/* ---- PRIVATE FUNCTIONS ---- */
_createFullRowPrototype( )
{
var tr = jQuery(document.createElement("tr"));
var td = jQuery(document.createElement("td"))
.addClass(this._outerId + "_td_fullRow")
.attr("colspan", this._columnIds.length)
.appendTo(tr);
var div = jQuery(document.createElement("div"))
.addClass(this._outerId + "_div_fullRow")
.appendTo(td);
this._prototypes["fullRow"] = tr;
}
_createDefaultPrototype( )
{
var tr = jQuery(document.createElement("tr"));
// Append a td for each column
for (var i = 0; i < this._columnIds.length; i++)
{
var td = jQuery(document.createElement("td"))
.addClass(this._outerId + "_td_" + this._columnIds[i])
.appendTo(tr);
var div = jQuery(document.createElement("div"))
.addClass(this._outerId + "_div_" + this._columnIds[i])
.addClass("innerContainer")
.appendTo(td);
}
this._prototypes["default"] = tr;
}
_createEmptyPrototype( )
{
this._prototypes["empty"] = jQuery(document.createElement("tr"));
}
_createLoadingPrototype( )
{
var fullRow = this.getPrototype("fullRow");
jQuery("div", fullRow).addClass("loading");
this._prototypes["loading"] = fullRow;
}
}

View File

@ -1,3 +1,4 @@
"use strict";
/**
* EGroupware eTemplate2 - Class which contains the spacer container
*
@ -9,97 +10,94 @@
* @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
/vendor/bower-asset/jquery/dist/jquery.js;
et2_dataview_view_container;
/vendor/bower-asset/jquery/dist/jquery.js;
et2_dataview_view_container;
*/
/**
* @augments et2_dataview_container
*/
var et2_dataview_spacer = (function(){ "use strict"; return et2_dataview_container.extend(
{
/**
* Constructor
*
* @param _parent
* @param _rowProvider
* @memberOf et2_dataview_spacer
*/
init: function (_parent, _rowProvider) {
// Call the inherited container constructor
this._super(_parent);
// Initialize the row count and the row height
this._count = 0;
this._rowHeight = 19;
this._avgSum = 0;
this._avgCount = 0;
// Get the spacer row and append it to the container
this.spacerNode = _rowProvider.getPrototype("spacer",
this._createSpacerPrototype, this);
this._phDiv = jQuery("td", this.spacerNode);
this.appendNode(this.spacerNode);
},
setCount: function (_count, _rowHeight) {
// Set the new count and _rowHeight if given
this._count = _count;
if (typeof _rowHeight !== "undefined")
{
this._rowHeight = _rowHeight;
}
// Update the element height
this._phDiv.height(this._count * this._rowHeight);
// Call the invalidate function
this.invalidate();
},
getCount: function () {
return this._count;
},
getHeight: function () {
// Set the calculated height, so that "invalidate" will work correctly
this._height = this._count * this._rowHeight;
return this._height;
},
getAvgHeightData: function () {
if (this._avgCount > 0)
{
return {
"avgHeight": this._avgSum / this._avgCount,
"avgCount": this._avgCount
};
}
return null;
},
addAvgHeight: function (_height) {
this._avgSum += _height;
this._avgCount++;
},
/* ---- PRIVATE FUNCTIONS ---- */
_createSpacerPrototype: function (_outerId, _columnIds) {
var tr = jQuery(document.createElement("tr"));
var td = jQuery(document.createElement("td"))
.addClass("egwGridView_spacer")
.addClass(_outerId + "_spacer_fullRow")
.attr("colspan", _columnIds.length)
.appendTo(tr);
return tr;
}
});}).call(this);
var et2_dataview_spacer = /** @class */ (function (_super) {
__extends(et2_dataview_spacer, _super);
/**
* Constructor
*
* @param _parent
* @param _rowProvider
* @memberOf et2_dataview_spacer
*/
function et2_dataview_spacer(_parent, _rowProvider) {
var _this =
// Call the inherited container constructor
_super.call(this, _parent) || this;
// Initialize the row count and the row height
_this._count = 0;
_this._rowHeight = 19;
_this._avgSum = 0;
_this._avgCount = 0;
// Get the spacer row and append it to the container
_this.spacerNode = _rowProvider.getPrototype("spacer", _this._createSpacerPrototype, _this);
_this._phDiv = jQuery("td", _this.spacerNode);
_this.appendNode(_this.spacerNode);
return _this;
}
et2_dataview_spacer.prototype.setCount = function (_count, _rowHeight) {
// Set the new count and _rowHeight if given
this._count = _count;
if (typeof _rowHeight !== "undefined") {
this._rowHeight = _rowHeight;
}
// Update the element height
this._phDiv.height(this._count * this._rowHeight);
// Call the invalidate function
this.invalidate();
};
et2_dataview_spacer.prototype.getCount = function () {
return this._count;
};
et2_dataview_spacer.prototype.getHeight = function () {
// Set the calculated height, so that "invalidate" will work correctly
this._height = this._count * this._rowHeight;
return this._height;
};
et2_dataview_spacer.prototype.getAvgHeightData = function () {
if (this._avgCount > 0) {
return {
"avgHeight": this._avgSum / this._avgCount,
"avgCount": this._avgCount
};
}
return null;
};
et2_dataview_spacer.prototype.addAvgHeight = function (_height) {
this._avgSum += _height;
this._avgCount++;
};
/* ---- PRIVATE FUNCTIONS ---- */
et2_dataview_spacer.prototype._createSpacerPrototype = function (_outerId, _columnIds) {
var tr = jQuery(document.createElement("tr"));
var td = jQuery(document.createElement("td"))
.addClass("egwGridView_spacer")
.addClass(_outerId + "_spacer_fullRow")
.attr("colspan", _columnIds.length)
.appendTo(tr);
return tr;
};
return et2_dataview_spacer;
}(et2_dataview_container));
exports.et2_dataview_spacer = et2_dataview_spacer;
//# sourceMappingURL=et2_dataview_view_spacer.js.map

View File

@ -0,0 +1,112 @@
/**
* EGroupware eTemplate2 - Class which contains the spacer container
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage dataview
* @link http://www.egroupware.org
* @author Andreas Stöckel
* @copyright Stylite 2011
* @version $Id$
*/
/*egw:uses
/vendor/bower-asset/jquery/dist/jquery.js;
et2_dataview_view_container;
*/
/**
* @augments et2_dataview_container
*/
export class et2_dataview_spacer extends et2_dataview_container
{
/**
* Constructor
*
* @param _parent
* @param _rowProvider
* @memberOf et2_dataview_spacer
*/
constructor( _parent, _rowProvider)
{
// Call the inherited container constructor
super(_parent);
// Initialize the row count and the row height
this._count = 0;
this._rowHeight = 19;
this._avgSum = 0;
this._avgCount = 0;
// Get the spacer row and append it to the container
this.spacerNode = _rowProvider.getPrototype("spacer",
this._createSpacerPrototype, this);
this._phDiv = jQuery("td", this.spacerNode);
this.appendNode(this.spacerNode);
}
setCount( _count, _rowHeight)
{
// Set the new count and _rowHeight if given
this._count = _count;
if (typeof _rowHeight !== "undefined")
{
this._rowHeight = _rowHeight;
}
// Update the element height
this._phDiv.height(this._count * this._rowHeight);
// Call the invalidate function
this.invalidate();
}
getCount( )
{
return this._count;
}
getHeight( )
{
// Set the calculated height, so that "invalidate" will work correctly
this._height = this._count * this._rowHeight;
return this._height;
}
getAvgHeightData( )
{
if (this._avgCount > 0)
{
return {
"avgHeight": this._avgSum / this._avgCount,
"avgCount": this._avgCount
};
}
return null;
}
addAvgHeight( _height)
{
this._avgSum += _height;
this._avgCount++;
}
/* ---- PRIVATE FUNCTIONS ---- */
_createSpacerPrototype( _outerId, _columnIds)
{
var tr = jQuery(document.createElement("tr"));
var td = jQuery(document.createElement("td"))
.addClass("egwGridView_spacer")
.addClass(_outerId + "_spacer_fullRow")
.attr("colspan", _columnIds.length)
.appendTo(tr);
return tr;
}
}

View File

@ -1,3 +1,4 @@
"use strict";
/**
* EGroupware eTemplate2 - dataview code
*
@ -9,96 +10,99 @@
* @copyright Nathan Gray 2014
* @version $Id: et2_dataview_view_container_1.js 46338 2014-03-20 09:40:37Z ralfbecker $
*/
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
/vendor/bower-asset/jquery/dist/jquery.js;
et2_dataview_interfaces;
/vendor/bower-asset/jquery/dist/jquery.js;
et2_dataview_interfaces;
*/
/**
* Displays tiles or thumbnails (squares) instead of full rows.
*
* It's important that the template specifies a fixed width and height (via CSS)
* so that the rows and columns work out properly.
*
* @augments et2_dataview_container
*/
var et2_dataview_tile = (function(){ "use strict"; return et2_dataview_row.extend([],
{
columns: 4,
/**
* Creates the row container. Use the "setRow" function to load the actual
* row content.
*
* @param _parent is the row parent container.
* @memberOf et2_dataview_row
*/
init: function(_parent) {
// Call the inherited constructor
this._super(_parent);
// Make sure the needed class is there to get the CSS
this.tr.addClass('tile');
},
makeExpandable: function (_expandable, _callback, _context) {
// Nope. It mostly works, it's just weird.
},
getAvgHeightData: function() {
var res = {
"avgHeight": this.getHeight() / this.columns,
"avgCount": this.columns
};
return res;
},
/**
* Returns the height for the tile.
*
* This is where we do the magic. If a new row should start, we return the proper
* height. If this should be another tile in the same row, we say it has 0 height.
* @returns {Number}
*/
getHeight: function() {
if(this._index % this.columns == 0)
{
return this._super();
}
else
{
return 0;
}
},
/**
* Broadcasts an invalidation through the container tree. Marks the own
* height as invalid.
*/
invalidate: function() {
if(this._inTree && this.tr)
{
var template_width = jQuery('.innerContainer',this.tr).children().outerWidth(true);
if(template_width)
{
this.tr.css('width', template_width + (this.tr.outerWidth(true) - this.tr.width()));
}
}
this._recalculate_columns();
this._super();
},
/**
* Recalculate how many columns we can fit in a row.
* While browser takes care of the actual layout, we need this for proper
* pagination.
*/
_recalculate_columns: function() {
if(this._inTree && this.tr && this.tr.parent())
{
this.columns = Math.max(1,parseInt(this.tr.parent().innerWidth() / this.tr.outerWidth(true)));
}
}
});}).call(this);
var et2_dataview_tile = /** @class */ (function (_super) {
__extends(et2_dataview_tile, _super);
/**
* Creates the row container. Use the "setRow" function to load the actual
* row content.
*
* @param _parent is the row parent container.
* @memberOf et2_dataview_row
*/
function et2_dataview_tile(_parent) {
var _this =
// Call the inherited constructor
_super.call(this, _parent) || this;
_this.columns = 4;
// Make sure the needed class is there to get the CSS
_this.tr.addClass('tile');
return _this;
}
et2_dataview_tile.prototype.makeExpandable = function (_expandable, _callback, _context) {
// Nope. It mostly works, it's just weird.
};
et2_dataview_tile.prototype.getAvgHeightData = function () {
var res = {
"avgHeight": this.getHeight() / this.columns,
"avgCount": this.columns
};
return res;
};
/**
* Returns the height for the tile.
*
* This is where we do the magic. If a new row should start, we return the proper
* height. If this should be another tile in the same row, we say it has 0 height.
* @returns {Number}
*/
et2_dataview_tile.prototype.getHeight = function () {
if (this._index % this.columns == 0) {
return _super.prototype.getHeight.call(this);
}
else {
return 0;
}
};
/**
* Broadcasts an invalidation through the container tree. Marks the own
* height as invalid.
*/
et2_dataview_tile.prototype.invalidate = function () {
if (this._inTree && this.tr) {
var template_width = jQuery('.innerContainer', this.tr).children().outerWidth(true);
if (template_width) {
this.tr.css('width', template_width + (this.tr.outerWidth(true) - this.tr.width()));
}
}
this._recalculate_columns();
_super.prototype.invalidate.call(this);
};
/**
* Recalculate how many columns we can fit in a row.
* While browser takes care of the actual layout, we need this for proper
* pagination.
*/
et2_dataview_tile.prototype._recalculate_columns = function () {
if (this._inTree && this.tr && this.tr.parent()) {
this.columns = Math.max(1, parseInt(this.tr.parent().innerWidth() / this.tr.outerWidth(true)));
}
};
return et2_dataview_tile;
}(et2_dataview_row));
exports.et2_dataview_tile = et2_dataview_tile;
//# sourceMappingURL=et2_dataview_view_tile.js.map

View File

@ -0,0 +1,107 @@
/**
* EGroupware eTemplate2 - dataview code
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage dataview
* @link http://www.egroupware.org
* @author Nathan Gray
* @copyright Nathan Gray 2014
* @version $Id: et2_dataview_view_container_1.js 46338 2014-03-20 09:40:37Z ralfbecker $
*/
/*egw:uses
/vendor/bower-asset/jquery/dist/jquery.js;
et2_dataview_interfaces;
*/
/**
* Displays tiles or thumbnails (squares) instead of full rows.
*
* It's important that the template specifies a fixed width and height (via CSS)
* so that the rows and columns work out properly.
*
*/
export class et2_dataview_tile extends et2_dataview_row {
columns: number = 4;
/**
* Creates the row container. Use the "setRow" function to load the actual
* row content.
*
* @param _parent is the row parent container.
* @memberOf et2_dataview_row
*/
constructor(_parent)
{
// Call the inherited constructor
super(_parent);
// Make sure the needed class is there to get the CSS
this.tr.addClass('tile');
}
makeExpandable(_expandable, _callback, _context)
{
// Nope. It mostly works, it's just weird.
}
getAvgHeightData()
{
var res = {
"avgHeight": this.getHeight() / this.columns,
"avgCount": this.columns
};
return res;
}
/**
* Returns the height for the tile.
*
* This is where we do the magic. If a new row should start, we return the proper
* height. If this should be another tile in the same row, we say it has 0 height.
* @returns {Number}
*/
getHeight()
{
if (this._index % this.columns == 0)
{
return super.getHeight();
}
else
{
return 0;
}
}
/**
* Broadcasts an invalidation through the container tree. Marks the own
* height as invalid.
*/
invalidate()
{
if (this._inTree && this.tr)
{
var template_width = jQuery('.innerContainer', this.tr).children().outerWidth(true);
if (template_width)
{
this.tr.css('width', template_width + (this.tr.outerWidth(true) - this.tr.width()));
}
}
this._recalculate_columns();
super.invalidate();
}
/**
* Recalculate how many columns we can fit in a row.
* While browser takes care of the actual layout, we need this for proper
* pagination.
*/
_recalculate_columns()
{
if (this._inTree && this.tr && this.tr.parent())
{
this.columns = Math.max(1, parseInt(this.tr.parent().innerWidth() / this.tr.outerWidth(true)));
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,758 @@
/**
* EGroupware eTemplate2 - JS Custom fields object
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage api
* @link http://www.egroupware.org
* @author Nathan Gray
* @copyright Nathan Gray 2011
* @version $Id$
*/
/*egw:uses
lib/tooltip;
/vendor/bower-asset/jquery/dist/jquery.js;
et2_core_xml;
et2_core_DOMWidget;
et2_core_inputWidget;
*/
import {et2_register_widget, WidgetConfig} from "./et2_core_widget";
import {ClassWithAttributes} from "./et2_core_inheritance";
import {et2_valueWidget} from "./et2_core_valueWidget";
export class et2_customfields_list extends et2_valueWidget implements et2_IDetachedDOM, et2_IInput
{
static readonly _attributes = {
'customfields': {
'name': 'Custom fields',
'description': 'Auto filled',
'type': 'any'
},
'fields': {
'name': 'Custom fields',
'description': 'Auto filled',
'type': 'any'
},
'value': {
'name': 'Custom fields',
'description': 'Auto filled',
'type': "any"
},
'type_filter': {
'name': 'Field filter',
"default": "",
"type": "any", // String or array
"description": "Filter displayed custom fields by their 'type2' attribute"
},
'private': {
ignore: true,
type: 'boolean'
},
'sub_app': {
'name': 'sub app name',
'type': "string",
'description': "Name of sub application"
},
// Allow onchange so you can put handlers on the sub-widgets
'onchange': {
"name": "onchange",
"type": "string",
"default": et2_no_init,
"description": "JS code which is executed when the value changes."
}
};
legacyOptions = ["type_filter","private", "fields"]; // Field restriction & private done server-side
public static readonly PREFIX = '#';
public static readonly DEFAULT_ID = "custom_fields";
private tbody: JQuery;
private table: JQuery;
private rows = {};
private widgets = {};
private detachedNodes = [];
constructor(_parent?, _attrs? : WidgetConfig, _child? : object)
{
super(_parent, _attrs, ClassWithAttributes.extendAttributes(et2_customfields_list._attributes, _child || {}));
// Some apps (infolog edit) don't give ID, so assign one to get settings
if(!this.id)
{
this.id = _attrs.id = et2_customfields_list.DEFAULT_ID;
// Add all attributes hidden in the content arrays to the attributes
// parameter
this.transformAttributes(_attrs);
// Create a local copy of the options object
this.options = et2_cloneObject(_attrs);
}
// Create the table body and the table
this.tbody = jQuery(document.createElement("tbody"));
this.table = jQuery(document.createElement("table"))
.addClass("et2_grid et2_customfield_list");
this.table.append(this.tbody);
if(!this.options.fields) this.options.fields = {};
if(typeof this.options.fields === 'string')
{
const fields = this.options.fields.split(',');
this.options.fields = {};
for(var i = 0; i < fields.length; i++)
{
this.options.fields[fields[i]] = true;
}
}
if(this.options.type_filter && typeof this.options.type_filter == "string")
{
this.options.type_filter = this.options.type_filter.split(",");
}
if(this.options.type_filter)
{
const already_filtered = !jQuery.isEmptyObject(this.options.fields);
for(let field_name in this.options.customfields)
{
// Already excluded?
if(already_filtered && !this.options.fields[field_name]) continue;
if(!this.options.customfields[field_name].type2 || this.options.customfields[field_name].type2.length == 0 ||
this.options.customfields[field_name].type2 == '0')
{
// No restrictions
this.options.fields[field_name] = true;
continue;
}
const types = typeof this.options.customfields[field_name].type2 == 'string' ? this.options.customfields[field_name].type2.split(",") : this.options.customfields[field_name].type2;
this.options.fields[field_name] = false;
for(var i = 0; i < types.length; i++)
{
if(jQuery.inArray(types[i],this.options.type_filter) > -1)
{
this.options.fields[field_name] = true;
}
}
}
}
this.setDOMNode(this.table[0]);
}
destroy( )
{
super.destroy();
this.rows = {};
this.widgets = {};
this.detachedNodes = [];
this.tbody = null;
}
/**
* What does this do? I don't know, but when everything is done the second
* time, this makes it work. Otherwise, all custom fields are lost.
*/
assign( _obj)
{
this.loadFields();
}
getDOMNode( _sender)
{
// Check whether the _sender object exists inside the management array
if(this.rows && _sender.id && this.rows[_sender.id])
{
return this.rows[_sender.id];
}
return super.getDOMNode(_sender);
}
/**
* Initialize widgets for custom fields
*/
loadFields( )
{
if(!this.options || !this.options.customfields) return;
// Already set up - avoid duplicates in nextmatch
if(this.getType() == 'customfields-list' && !this.isInTree()) return;
if(!jQuery.isEmptyObject(this.widgets)) return;
// Check for global setting changes (visibility)
const global_data = this.getArrayMgr("modifications").getRoot().getEntry('~custom_fields~');
if(global_data && global_data.fields && !this.options.fields) this.options.fields = global_data.fields;
// For checking app entries
const apps = this.egw().link_app_list();
// Create the table rows
for(let field_name in this.options.customfields)
{
// Skip fields if we're filtering
if(this.getType() != 'customfields-list' && !jQuery.isEmptyObject(this.options.fields) && !this.options.fields[field_name]) continue;
const field = this.options.customfields[field_name];
let id = et2_customfields_list.PREFIX + field_name;
// Need curlies around ID for nm row expansion
if(this.id == '$row')
{
id = "{" + this.id + "}" + "["+et2_customfields_list.PREFIX + field_name+"]";
}
else if (this.id != et2_customfields_list.DEFAULT_ID)
{
// Prefix this ID to avoid potential ID collisions
id = this.id + "["+id+"]";
}
// Avoid creating field twice
if(!this.rows[id])
{
const row = jQuery(document.createElement("tr"))
.appendTo(this.tbody)
.addClass(this.id + '_' + id);
let cf = jQuery(document.createElement("td"))
.appendTo(row);
if(!field.type) field.type = 'text";';
const setup_function = '_setup_' + (apps[field.type] ? 'link_entry' : field.type.replace("-", "_"));
const attrs: any = {
'id': id,
'statustext': field.help,
'needed': field.needed,
'readonly': this.getArrayMgr("readonlys").isReadOnly(id, null, this.options.readonly),
'value': this.options.value[et2_customfields_list.PREFIX + field_name]
};
// Can't have a required readonly, it will warn & be removed later, so avoid the warning
if(attrs.readonly === true) delete attrs.needed;
if(this.options.onchange)
{
attrs.onchange = this.options.onchange;
}
if(this[setup_function]) {
const no_skip = this[setup_function].call(this, field_name, field, attrs);
if(!no_skip) continue;
}
if(this.getType() == 'customfields-list') {
// No label, cust widget
attrs.readonly = true;
// Widget tooltips don't work in nextmatch because of the creation / binding separation
// Set title to field label so browser will show something
// Add field label & help as data attribute to row, so it can be stylied with CSS (title should be disabled)
row.attr('title', field.label);
row.attr('data-label', field.label);
row.attr('data-field', field_name);
row.attr('data-help', field.help);
this.detachedNodes.push(row[0]);
}
else
{
// Label in first column, widget in 2nd
cf.text(field.label + "");
cf = jQuery(document.createElement("td"))
.appendTo(row);
}
this.rows[id] = cf[0];
// Set any additional attributes set in options, but not for widgets that pass actual options
if(['select','radio','radiogroup','checkbox','button'].indexOf(field.type) == -1 && !jQuery.isEmptyObject(field.values))
{
const w = et2_registry[attrs.type ? attrs.type : field.type];
for(let attr_name in field.values)
{
if (typeof w.prototype.attributes[attr_name] != "undefined")
attrs[attr_name] = field.values[attr_name];
}
}
// Create widget
const widget = this.widgets[field_name] = et2_createWidget(attrs.type ? attrs.type : field.type, attrs, this);
}
// Field is not to be shown
if(!this.options.fields || jQuery.isEmptyObject(this.options.fields) || this.options.fields[field_name] == true)
{
jQuery(this.rows[field_name]).show();
}
else
{
jQuery(this.rows[field_name]).hide();
}
}
}
/**
* Read needed info on available custom fields from various places it's stored.
*/
transformAttributes( _attrs)
{
super.transformAttributes(_attrs);
// Add in settings that are objects
// Customized settings for this widget (unlikely)
const data = this.getArrayMgr("modifications").getEntry(this.id);
// Check for global settings
const global_data = this.getArrayMgr("modifications").getRoot().getEntry('~custom_fields~', true);
if(global_data)
{
for(let key in data)
{
// Don't overwrite fields with global values
if(global_data[key] && key !== 'fields')
{
data[key] = jQuery.extend(true, {}, data[key], global_data[key]);
}
}
}
for(var key in data)
{
_attrs[key] = data[key];
}
for(let key in global_data)
{
if(typeof global_data[key] != 'undefined' && ! _attrs[key]) _attrs[key] = global_data[key];
}
if (this.id)
{
// Set the value for this element
const contentMgr = this.getArrayMgr("content");
if (contentMgr != null) {
const val = contentMgr.getEntry(this.id);
_attrs["value"] = {};
if (val !== null)
{
if(this.id.indexOf(et2_customfields_list.PREFIX) === 0 && typeof data.fields != 'undefined' && data.fields[this.id.replace(et2_customfields_list.PREFIX,'')] === true)
{
_attrs['value'][this.id] = val;
}
else
{
// Only set the values that match desired custom fields
for(let key in val)
{
if(key.indexOf(et2_customfields_list.PREFIX) == 0) {
_attrs["value"][key] = val[key];
}
}
}
//_attrs["value"] = val;
}
else
{
// Check for custom fields directly in record
for(var key in _attrs.customfields)
{
_attrs["value"][et2_customfields_list.PREFIX + key] = contentMgr.getEntry(et2_customfields_list.PREFIX + key);
}
}
}
}
}
loadFromXML( _node)
{
this.loadFields();
// Load the nodes as usual
super.loadFromXML(_node);
}
set_value( _value)
{
if(!this.options.customfields) return;
for(let field_name in this.options.customfields)
{
// Skip fields if we're filtering
if(!jQuery.isEmptyObject(this.options.fields) && !this.options.fields[field_name]) continue;
// Make sure widget is created, and has the needed function
if(!this.widgets[field_name] || !this.widgets[field_name].set_value) continue;
let value = _value[et2_customfields_list.PREFIX + field_name] ? _value[et2_customfields_list.PREFIX + field_name] : null;
// Check if ID was missing
if(value == null && this.id == et2_customfields_list.DEFAULT_ID && this.getArrayMgr("content").getEntry(et2_customfields_list.PREFIX + field_name))
{
value = this.getArrayMgr("content").getEntry(et2_customfields_list.PREFIX + field_name);
}
switch(this.options.customfields[field_name].type)
{
case 'date':
// Date custom fields are always in Y-m-d, which seldom matches user's preference
// which fails when sent to date widget. This is only used for nm rows, when possible
// this is fixed server side
if(value && isNaN(value))
{
value = jQuery.datepicker.parseDate("yy-mm-dd",value);
}
break;
}
this.widgets[field_name].set_value(value);
}
}
/**
* et2_IInput so the custom field can be it's own widget.
*/
getValue( )
{
// Not using an ID means we have to grab all the widget values, and put them where server knows to look
if(this.id != et2_customfields_list.DEFAULT_ID)
{
return null;
}
const value = {};
for(let field_name in this.widgets)
{
if(this.widgets[field_name].getValue && !this.widgets[field_name].options.readonly)
{
value[et2_customfields_list.PREFIX + field_name] = this.widgets[field_name].getValue();
}
}
return value;
}
isDirty( )
{
let dirty = true;
for(let field_name in this.widgets)
{
if(this.widgets[field_name].isDirty)
{
dirty = dirty && this.widgets[field_name].isDirty();
}
}
return dirty;
}
resetDirty( )
{
for(let field_name in this.widgets)
{
if(this.widgets[field_name].resetDirty)
{
this.widgets[field_name].resetDirty();
}
}
}
isValid( )
{
// Individual customfields will handle themselves
return true;
}
/**
* Adapt provided attributes to match options for widget
*
* rows > 1 --> textarea, with rows=rows and cols=len
* !rows --> input, with size=len
* rows = 1 --> input, with size=len, maxlength=len
*/
_setup_text( field_name, field, attrs)
{
// No label on the widget itself
delete(attrs.label);
field.type = 'textbox';
attrs.rows = field.rows > 1 ? field.rows : null;
if(field.len)
{
attrs.size = field.len;
if (field.rows == 1) attrs.maxlength = field.len;
}
return true;
}
_setup_ajax_select( field_name, field, attrs)
{
const attributes = ['get_rows', 'get_title', 'id_field', 'template'];
if(field.values)
{
for(let i = 0; i < attributes.length; i++)
{
if(typeof field.values[attributes[i]] !== 'undefined')
{
attrs[attributes[i]] = field.values[attributes[i]];
}
}
}
return true;
}
_setup_float( field_name, field, attrs)
{
// No label on the widget itself
delete(attrs.label);
field.type = 'float';
if(field.len)
{
attrs.size = field.len;
}
return true;
}
_setup_select( field_name, field, attrs)
{
// No label on the widget itself
delete(attrs.label);
attrs.rows = field.rows;
// select_options are now send from server-side incl. ones defined via a file in EGroupware root
attrs.tags = field.tags;
return true;
}
_setup_select_account( field_name, field, attrs)
{
attrs.empty_label = egw.lang('Select');
if(field.account_type)
{
attrs.account_type = field.account_type;
}
return this._setup_select(field_name, field, attrs);
}
_setup_date(field_name, field, attrs) {
attrs.data_format = field.values && field.values.format ? field.values.format : 'Y-m-d';
return true;
}
_setup_date_time( field_name, field, attrs)
{
attrs.data_format = field.values && field.values.format ? field.values.format : 'Y-m-d H:i:s';
return true;
}
_setup_htmlarea( field_name, field, attrs)
{
attrs.config = field.config ? field.config : {};
attrs.config.toolbarStartupExpanded = false;
if(field.len)
{
attrs.config.width = field.len+'px';
}
attrs.config.height = (((field.rows > 0 && field.rows !='undefined') ? field.rows : 5) *16) +'px';
// We have to push the config modifications into the modifications array, or they'll
// be overwritten by the site config from the server
const data = this.getArrayMgr("modifications").getEntry(et2_customfields_list.PREFIX + field_name);
if(data) jQuery.extend(data.config, attrs.config);
return true;
}
_setup_radio( field_name, field, attrs)
{
// 'Empty' label will be first
delete(attrs.label);
if(field.values && field.values[''])
{
attrs.label = field.values[''];
delete field.values[''];
}
field.type = 'radiogroup';
attrs.options = field.values;
return true;
}
_setup_checkbox( field_name, field, attrs)
{
// Read-only checkbox is just text
if(attrs.readonly)
{
attrs.ro_true = field.label;
}
return true;
}
/**
* People set button attributes as
* label: javascript
*/
_setup_button( field_name, field, attrs)
{
// No label on the widget itself
delete(attrs.label);
attrs.label = field.label;
if (this.getType() == 'customfields-list')
{
// No buttons in a list, it causes problems with detached nodes
return false;
}
// Simple case, one widget for a custom field
if(!field.values || typeof field.values != 'object' || Object.keys(field.values).length == 1)
{
for(let key in field.values)
{
attrs.label = key;
attrs.onclick = field.values[key];
}
if (!attrs.label)
{
attrs.label = 'No "label=onclick" in values!';
attrs.onclick = function(){ return false; };
}
return !attrs.readonly;
}
else
{
// Complicated case, a single custom field you get multiple widgets
// Handle it all here, since this is the exception
const row = jQuery('tr', this.tbody).last();
let cf = jQuery('td', row);
// Label in first column, widget in 2nd
cf.text(field.label + "");
cf = jQuery(document.createElement("td"))
.appendTo(row);
for(var key in field.values)
{
const button_attrs = jQuery.extend({}, attrs);
button_attrs.label = key;
button_attrs.onclick = field.values[key];
button_attrs.id = attrs.id + '_' + key;
// This controls where the button is placed in the DOM
this.rows[button_attrs.id] = cf[0];
// Do not store in the widgets list, one name for multiple widgets would cause problems
/*this.widgets[field_name] = */ et2_createWidget(attrs.type ? attrs.type : field.type, button_attrs, this);
}
return false;
}
}
_setup_link_entry( field_name, field, attrs)
{
if(field.type === 'filemanager')
{
return this._setup_filemanager(field_name, field, attrs);
}
// No label on the widget itself
delete(attrs.label);
attrs.type = "link-entry";
attrs.only_app = field.type;
return true;
}
_setup_filemanager( field_name, field, attrs)
{
attrs.type = 'vfs-upload';
delete(attrs.label);
if (this.getType() == 'customfields-list')
{
// No special UI needed?
return true;
}
else
{
// Complicated case, a single custom field you get multiple widgets
// Handle it all here, since this is the exception
const row = jQuery('tr', this.tbody).last();
let cf = jQuery('td', row);
// Label in first column, widget in 2nd
cf.text(field.label + "");
cf = jQuery(document.createElement("td"))
.appendTo(row);
// Create upload widget
let widget = this.widgets[field_name] = et2_createWidget(attrs.type ? attrs.type : field.type, attrs, this);
// This controls where the widget is placed in the DOM
this.rows[attrs.id] = cf[0];
jQuery(widget.getDOMNode(widget)).css('vertical-align','top');
// Add a link to existing VFS file
const select_attrs = jQuery.extend({},
attrs,
// Filemanager select
{
label: '',
mode: widget.options.multiple ? 'open-multiple' : 'open',
method: 'EGroupware\\Api\\Etemplate\\Widget\\Link::ajax_link_existing',
method_id: attrs.path,
button_label: egw.lang('Link')
}, {type: 'vfs-select'});
select_attrs.id = attrs.id + '_vfs_select';
// This controls where the button is placed in the DOM
this.rows[select_attrs.id] = cf[0];
// Do not store in the widgets list, one name for multiple widgets would cause problems
widget = et2_createWidget(select_attrs.type, select_attrs, this);
jQuery(widget.getDOMNode(widget)).css('vertical-align','top').prependTo(cf);
}
return false;
}
/**
* Set which fields are visible, by name
*
* Note: no # prefix on the name
*
*/
set_visible( _fields)
{
for(let name in _fields)
{
if(this.rows[et2_customfields_list.PREFIX + name])
{
if(_fields[name])
{
jQuery(this.rows[et2_customfields_list.PREFIX+name]).show();
}
else
{
jQuery(this.rows[et2_customfields_list.PREFIX+name]).hide();
}
}
this.options.fields[name] = _fields[name];
}
}
/**
* Code for implementing et2_IDetachedDOM
*/
getDetachedAttributes(_attrs)
{
_attrs.push("value", "class");
}
getDetachedNodes()
{
return this.detachedNodes ? this.detachedNodes : [];
}
setDetachedAttributes(_nodes, _values)
{
// Individual widgets are detected and handled by the grid, but the interface is needed for this to happen
// Show the row if there's a value, hide it if there is no value
for(let i = 0; i < _nodes.length; i++)
{
// toggle() needs a boolean to do what we want
const key = _nodes[i].getAttribute('data-field');
jQuery(_nodes[i]).toggle(_values.fields[key] && _values.value[et2_customfields_list.PREFIX + key]?true:false);
}
}
}
et2_register_widget(et2_customfields_list, ["customfields", "customfields-list"]);

View File

@ -10,19 +10,16 @@
* @author Nathan Gray
* @copyright 2012 Christian Binder
* @copyright 2011 Nathan Gray
* @version $Id: et2_widget_itempicker.js 38623 2012-03-26 23:27:53Z jaytraxx $
*/
function itempickerDocumentAction(context, data)
{
"use strict";
var formid = "itempicker_action_form";
var form = "<form id='" + formid + "' action='index.php?menuaction=" + data.app + "." + data.app + "_merge.download_by_request' method='POST'>"
+ "<input type='hidden' name='data_document_name' value='" + data.value.name + "' />"
+ "<input type='hidden' name='data_document_dir' value='" + data.value.dir + "' />"
+ "<input type='hidden' name='data_checked' value='" + data.checked.join(',') + "' />"
+ "</form>";
jQuery("body").append(form);
jQuery("#" + formid).submit().remove();
function itempickerDocumentAction(context, data) {
"use strict";
var formid = "itempicker_action_form";
var form = "<form id='" + formid + "' action='index.php?menuaction=" + data.app + "." + data.app + "_merge.download_by_request' method='POST'>"
+ "<input type='hidden' name='data_document_name' value='" + data.value.name + "' />"
+ "<input type='hidden' name='data_document_dir' value='" + data.value.dir + "' />"
+ "<input type='hidden' name='data_checked' value='" + data.checked.join(',') + "' />"
+ "</form>";
jQuery("body").append(form);
jQuery("#" + formid).submit().remove();
}
//# sourceMappingURL=et2_extension_itempicker_actions.js.map

View File

@ -0,0 +1,28 @@
/**
* EGroupware eTemplate2 - JS Itempicker object
* derived from et2_link_entry widget @copyright 2011 Nathan Gray
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage api
* @link http://www.egroupware.org
* @author Christian Binder
* @author Nathan Gray
* @copyright 2012 Christian Binder
* @copyright 2011 Nathan Gray
*/
function itempickerDocumentAction(context, data)
{
"use strict";
let formid = "itempicker_action_form";
let form = "<form id='" + formid + "' action='index.php?menuaction=" + data.app + "." + data.app + "_merge.download_by_request' method='POST'>"
+ "<input type='hidden' name='data_document_name' value='" + data.value.name + "' />"
+ "<input type='hidden' name='data_document_dir' value='" + data.value.dir + "' />"
+ "<input type='hidden' name='data_checked' value='" + data.checked.join(',') + "' />"
+ "</form>";
jQuery("body").append(form);
jQuery("#" + formid).submit().remove();
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,769 @@
/**
* EGroupware eTemplate2 - Class which contains a the data model for nextmatch widgets
*
* @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 2012
* @version $Id$
*
/*egw:uses
et2_core_common;
et2_core_inheritance;
et2_dataview_view_row;
et2_dataview_controller;
et2_dataview_interfaces;
et2_dataview_view_tile;
et2_extension_nextmatch_actions; // Contains nm_action
egw_data;
*/
import {et2_IDataProvider} from "./et2_dataview_interfaces";
import {et2_dataview_row} from "./et2_dataview_view_row";
import {et2_dataview_tile} from "./et2_dataview_view_tile";
/**
* @augments et2_dataview_controller
*/
export class et2_nextmatch_controller extends et2_dataview_controller implements et2_IDataProvider
{
// Display constants
public static readonly VIEW_ROW : string = 'row';
public static readonly VIEW_TILE: string = 'tile';
/**
* Initializes the nextmatch controller.
*
* @param _parentController is the parent nextmatch controller instance
* @param _egw is the api instance
* @param _execId is the execId of the etemplate
* @param _widget is the nextmatch-widget we are fetching data for.
* @param _grid is the grid the grid controller will be controlling
* @param _rowProvider is the nextmatch row provider instance.
* @param _objectManager is the parent object manager (if null, the object
* manager) will be created using
* @param _actionLinks contains the action links
* @param _actions contains the actions, may be null if an object manager
* is given.
* @memberOf et2_nextmatch_controller
*/
constructor( _parentController, _egw, _execId, _widget, _parentId,
_grid, _rowProvider, _actionLinks, _objectManager, _actions?)
{
// Call the parent et2_dataview_controller constructor
super(_parentController, _grid);
this.setDataProvider(this);
this.setRowCallback(this._rowCallback);
this.setLinkCallback(this._linkCallback);
this.setContext(this);
// Copy the egw reference
this.egw = _egw;
// Keep a reference to the widget
this._widget = _widget;
// Copy the given parameters
this._actionLinks = _actionLinks;
this._execId = _execId;
// Get full widget ID, including path
var id = _widget.getArrayMgr('content').getPath();
if(typeof id == 'string')
{
this._widgetId = id;
}
else if (id.length === 1)
{
this._widgetId = id[0];
}
else
{
this._widgetId = id.shift() + '[' + id.join('][') + ']';
}
this._parentId = _parentId;
this._rowProvider = _rowProvider;
// Initialize the action and the object manager
// _initActions calls _init_link_dnd, which uses this._actionLinks,
// so this must happen after the above parameter copying
if (!_objectManager)
{
this._initActions(_actions);
}
else
{
this._actionManager = null;
this._objectManager = _objectManager;
}
this.setActionObjectManager(this._objectManager);
// Add our selection callback to selection manager
var self = this;
this._objectManager.setSelectedCallback = function() {self._selectCallback.apply(self,[this,arguments]);};
// We start with no filters
this._filters = {};
// Keep selection across filter changes
this.kept_selection = null;
this.kept_focus = null;
this.kept_expansion = [];
// Directly use the API-Implementation of dataRegisterUID and
// dataUnregisterUID
this.dataUnregisterUID = _egw.dataUnregisterUID;
// Default to rows
this._view = et2_nextmatch_controller.VIEW_ROW;
}
destroy( )
{
// If the actionManager variable is set, the object- and actionManager
// were created by this instance -- clear them
if (this._actionManager)
{
this._objectManager.remove();
this._actionManager.remove();
}
super.destroy();
}
/**
* Updates the filter instance.
*/
setFilters( _filters)
{
// Update the filters
this._filters = _filters;
}
/**
* Keep the selection, if possible, across a data fetch and restore it
* after
*/
keepSelection( )
{
this.kept_selection = this._selectionMgr ? this._selectionMgr.getSelected() : null;
this.kept_focus = this._selectionMgr && this._selectionMgr._focusedEntry ?
this._selectionMgr._focusedEntry.uid || null : null;
// Find expanded rows
var nm = this._widget;
var controller = this;
jQuery('.arrow.opened',this._widget.getDOMNode(this._widget)).each(function() {
var entry = controller.getRowByNode(this);
if(entry && entry.uid)
{
controller.kept_expansion.push(entry.uid);
}
});
}
getObjectManager( )
{
return this._objectManager;
}
/**
* Deletes a row from the grid
*
* @param {string} uid
*/
deleteRow( uid)
{
var entry = this._selectionMgr._getRegisteredRowsEntry(uid);
// Unselect
this._selectionMgr.setSelected(uid,false);
if(entry && entry.idx !== null)
{
// This will remove the row, but add an empty to the end.
// That's OK, because it will be removed when we update the row count
this._grid.deleteRow(entry.idx);
// Trigger controller to remove from internals
this.egw.dataStoreUID(uid,null);
// Stop caring about this ID
this.egw.dataDeleteUID(uid);
// Remove from internal map
delete this._indexMap[entry.idx];
// Update the indices of all elements after the current one
for(var mapIndex = entry.idx + 1; typeof this._indexMap[mapIndex] !== 'undefined'; mapIndex++)
{
var entry = this._indexMap[mapIndex];
entry.idx = mapIndex-1;
this._indexMap[mapIndex-1] = entry;
// Update selection mgr too
if(entry.uid && typeof this._selectionMgr._registeredRows[entry.uid] !== 'undefined')
{
var reg = this._selectionMgr._getRegisteredRowsEntry(entry.uid);
reg.idx = entry.idx;
if(reg.ao && reg.ao._index) reg.ao._index = entry.idx;
}
}
// Remove last one, it was moved to mapIndex-1 before increment
delete this._indexMap[mapIndex-1];
// Not needed, they share by reference
// this._selectionMgr.setIndexMap(this._indexMap);
}
}
/** -- PRIVATE FUNCTIONS -- **/
/**
* Create a new row, either normal or tiled
*
* @param {type} ctx
* @returns {et2_dataview_container}
*/
_createRow( ctx)
{
switch(this._view)
{
case et2_nextmatch_controller.VIEW_TILE:
var row = new et2_dataview_tile(this._grid);
// Try to overcome chrome rendering issue where float is not
// applied properly, leading to incomplete rows
window.setTimeout(function() {
if(!row.tr) return;
row.tr.css('float','none');
window.setTimeout(function() {
if(!row.tr) return;
row.tr.css('float','left');
},50);
},100);
return row;
case et2_nextmatch_controller.VIEW_ROW:
default:
return new et2_dataview_row(this._grid);
}
}
/**
* Initializes the action and the object manager.
*/
_initActions( _actions)
{
// Generate a uid for the action and object manager
var uid = this._widget.id||this.egw.uid();
if(_actions == null) _actions = [];
// 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(this._actionManager == null)
{
this._actionManager = gam.addAction("actionManager", uid);
}
this._actionManager.updateActions(_actions, this.egw.appName);
var data = this._actionManager.data;
if (data == 'undefined' || !data)
{
data = {};
}
data.nextmatch = this._widget;
this._actionManager.set_data(data);
// Set the default execute handler
var self = this;
this._actionManager.setDefaultExecute(function (_action, _senders, _target) {
// Get the selected ids descriptor object
var ids = self._selectionMgr.getSelected();
// Pass a reference to the actual widget
if (typeof _action.data == 'undefined' || !_action.data) _action.data = {};
_action.data.nextmatch = self._widget;
// Call the nm_action function with the ids
nm_action(_action, _senders, _target, ids);
});
// Set the 'Select All' handler
var select_all = this._actionManager.getActionById('select_all');
if(select_all)
{
select_all.set_onExecute(jQuery.proxy(function(action, selected) {
this._selectionMgr.selectAll();
}, this));
}
// Initialize the object manager - look for application
// object manager 1 level deep
var gom = egw_getObjectManager(this.egw.appName,true,1);
if(this._objectManager == null)
{
this._objectManager = gom.addObject(
new egwActionObjectManager(uid, this._actionManager));
this._objectManager.handleKeyPress = function(_keyCode, _shift, _ctrl, _alt) {
for(var i = 0; i < self._actionManager.children.length; i++)
{
if(typeof self._actionManager.children[i].shortcut === 'object' &&
self._actionManager.children[i].shortcut &&
_keyCode == self._actionManager.children[i].shortcut.keyCode)
{
return this.executeActionImplementation(
{
"keyEvent": {
"keyCode": _keyCode,
"shift": _shift,
"ctrl": _ctrl,
"alt": _alt
}
}, "popup", EGW_AO_EXEC_SELECTED);
}
}
return egwActionObject.prototype.handleKeyPress.call(this, _keyCode,_shift,_ctrl,_alt);
}
}
this._objectManager.flags = this._objectManager.flags
| EGW_AO_FLAG_DEFAULT_FOCUS | EGW_AO_FLAG_IS_CONTAINER;
this._init_links_dnd();
if(this._selectionMgr)
{
// Need to update the action links for every registered row too
for (var uid in this._selectionMgr._registeredRows)
{
// Get the corresponding entry from the registered rows array
var entry = this._selectionMgr._getRegisteredRowsEntry(uid);
if(entry.ao)
{
entry.ao.updateActionLinks(this._actionLinks);
}
}
}
}
/**
* Automatically add dnd support for linking
*/
_init_links_dnd( )
{
var mgr = this._actionManager;
var self = this;
var drop_action = mgr.getActionById('egw_link_drop');
var drag_action = mgr.getActionById('egw_link_drag');
var drop_cancel = mgr.getActionById('egw_cancel_drop');
if(!this._actionLinks)
{
this._actionLinks = [];
}
if (!drop_cancel)
{
// Create a generic cancel action in order to cancel drop action
// applied for all apps plus file and link action.
drop_cancel = mgr.addAction('drop', 'egw_cancel_drop', this.egw.lang('Cancel'), egw.image('cancel'), function(){},true);
drop_cancel.set_group('99');
drop_cancel.acceptedTypes = drop_cancel.acceptedTypes.concat(Object.keys(egw.user('apps')).concat(['link', 'file']));
this._actionLinks.push (drop_cancel.id);
}
// Check if this app supports linking
if(!egw.link_get_registry(this.dataStorePrefix || this.egw.appName, 'query') ||
egw.link_get_registry(this.dataStorePrefix || this.egw.appName, 'title'))
{
if(drop_action)
{
drop_action.remove();
if(this._actionLinks.indexOf(drop_action.id) >= 0)
{
this._actionLinks.splice(this._actionLinks.indexOf(drop_action.id),1);
}
}
if(drag_action)
{
drag_action.remove();
if(this._actionLinks.indexOf(drag_action.id) >= 0)
{
this._actionLinks.splice(this._actionLinks.indexOf(drag_action.id),1);
}
}
return;
}
// Don't re-add
if(drop_action == null)
{
// Create the drop action that links entries
drop_action = mgr.addAction('drop', 'egw_link_drop', this.egw.lang('Create link'), egw.image('link'), function(action, source, dropped) {
// Extract link IDs
var links = [];
var id = '';
for(var i = 0; i < source.length; i++)
{
if(!source[i].id) continue;
id = source[i].id.split('::');
links.push({app: id[0] == 'filemanager' ? 'link' : id[0], id: id[1]});
}
if(!links.length)
{
return;
}
// Link the entries
self.egw.json("EGroupware\\Api\\Etemplate\\Widget\\Link::ajax_link",
dropped.id.split('::').concat([links]),
function(result) {
if(result)
{
for (var i=0; i < this._objectManager.selectedChildren.length; i++)
{
this._widget.refresh(this._objectManager.selectedChildren[i].id,'update');
}
this._widget.egw().message('Linked');
// Update the target to show the liks
this._widget.refresh(dropped.id,'update');
}
},
self,
true,
self
).sendRequest();
},true);
}
if(this._actionLinks.indexOf(drop_action.id) < 0)
{
this._actionLinks.push(drop_action.id);
}
// Accept other links, and files dragged from the filemanager
// This does not handle files dragged from the desktop. They are
// handled by et2_nextmatch, since it needs DOM stuff
if(drop_action.acceptedTypes.indexOf('link') == -1)
{
drop_action.acceptedTypes.push('link');
}
// Don't re-add
if(drag_action == null)
{
// Create drag action that allows linking
drag_action = mgr.addAction('drag', 'egw_link_drag', this.egw.lang('link'), 'link', function(action, selected) {
// Drag helper - list titles. Arbitrarily limited to 10.
var helper = jQuery(document.createElement("div"));
for(var i = 0; i < selected.length && i < 10; i++)
{
var id = selected[i].id.split('::');
var span = jQuery(document.createElement('span')).appendTo(helper);
self.egw.link_title(id[0],id[1], function(title) {
this.append(title);
this.append('<br />');
}, span);
}
// As we wanted to have a general defaul helper interface, we return null here and not using customize helper for links
// TODO: Need to decide if we need to create a customized helper interface for links anyway
//return helper;
return null;
},true);
}
if(this._actionLinks.indexOf(drag_action.id) < 0)
{
this._actionLinks.push(drag_action.id);
}
drag_action.set_dragType('link');
}
/**
* Set the data cache prefix
* Overridden from the parent to re-check automatically the added link dnd
* since the prefix is used in support detection.
*/
setPrefix( prefix)
{
super.setPrefix(prefix);
this._init_links_dnd();
}
/**
* Overwrites the inherited _destroyCallback function in order to be able
* to free the "rowWidget".
*/
_destroyCallback( _row)
{
// Destroy any widget associated to the row
if (this.entry.widget)
{
this.entry.widget.destroy();
this.entry.widget = null;
}
// Call the inherited function
super._destroyCallback(_row);
}
/**
* Creates the actual data row.
*
* @param _data is an array containing the row data
* @param _tr is the tr into which the data will be inserted
* @param _idx is the index of the row
* @param _entry is the internal row datastructure of the controller, in
* this special case used to store the rowWidget reference, so that it can
* be properly freed.
*/
_rowCallback( _data, _tr, _idx, _entry)
{
// Let the row provider fill in the data row -- store the returned
// rowWidget inside the _entry
_entry.widget = this._rowProvider.getDataRow(
{ "content": _data }, _tr, _idx, this);
}
/**
* Returns the names of action links for a given data row -- currently these are
* always the same links, as we controll enabled/disabled over the row
* classes, unless the row UID is "", then it's an 'empty' row.
*
* The empty row placeholder can still have actions, but nothing that requires
* an actual UID.
*
* @TODO: Currently empty row is just add, need to actually filter somehow. Here
* might not be the right place.
*
* @param _data Object The data for the row
* @param _idx int The row index
* @param _uid String The row's ID
*
* @return Array List of action names that valid for the row
*/
_linkCallback( _data, _idx, _uid)
{
if(_uid.trim() != "")
{
return this._actionLinks;
}
// No UID, so return a filtered list of actions that doesn't need a UID
var links = [];
try {
links = typeof this._widget.options.settings.placeholder_actions != 'undefined' ?
this._widget.options.settings.placeholder_actions : (this._widget.options.add ? ["add"] : []);
// Make sure that placeholder actions are defined and existed in client-side,
// otherwise do not set them as placeholder. for instance actions with
// attribute hideOnMobile do not get sent to client-side.
var action_search = function(current) {
if (typeof this._widget.options.actions[current] !== 'undefined') return true;
// Check children
for(var action in this._widget.options.actions)
{
action = this._widget.options.actions[action];
if( action.children && action.children[current]) return true;
}
return false;
};
links = links.filter(action_search, this);
} catch (e) {
}
return links;
}
/**
* Overridden from the parent to also process any additional data that
* the data source adds, such as readonlys and additonal content.
* For example, non-numeric IDs in rows are added to the content manager
*/
_fetchCallback( _response)
{
var nm = this.self._widget;
if(!nm)
{
// Nextmatch either not connected, or it tried to destroy this
// but the server returned something
return;
}
// Readonlys
// Other stuff
for(var i in _response.rows)
{
if(jQuery.isNumeric(i)) continue;
if(i == 'sel_options')
{
var mgr = nm.getArrayMgr(i);
for(var id in _response.rows.sel_options)
{
mgr.data[id] = _response.rows.sel_options[id];
var select = nm.getWidgetById(id);
if(select && select.set_select_options)
{
select.set_select_options(_response.rows.sel_options[id]);
}
// Clear rowProvider internal cache so it uses new values
if(id == 'cat_id')
{
this.self._rowProvider.categories = null;
}
// update array mgr so select widgets in row also get refreshed options
nm.getParent().getArrayMgr('sel_options').data[id] = _response.rows.sel_options[id];
}
}
else
{
var mgr = nm.getArrayMgr('content');
mgr.data[i] = _response.rows[i];
// It's not enough to just update the data, the widgets need to
// be updated too, if there are matching widgets.
var widget = nm.getWidgetById(i);
if(widget && widget.set_value)
{
widget.set_value(mgr.getEntry(i));
}
}
}
// Might be trying to disable a column
var col_refresh = false;
for(var column_index = 0; column_index < nm.columns.length; column_index++)
{
if(typeof nm.columns[column_index].disabled === 'string' &&
nm.columns[column_index].disabled[0] === '@')
{
col_refresh = true;
nm.dataview.columnMgr.getColumnById('col_'+column_index)
.set_visibility(
nm.getArrayMgr('content').parseBoolExpression(nm.columns[column_index].disabled) ?
et2_dataview_column.ET2_COL_VISIBILITY_DISABLED :
nm.columns[column_index].visible
);
}
}
if(col_refresh)
{
nm.dataview.columnMgr.updated();
nm.dataview._updateColumns();
}
// If we're doing an autorefresh and the count decreases, preserve the
// selection or it will be lost when the grid rows are shuffled. Increases
// are fine though.
if(this.self && this.self.kept_selection == null &&
!this.refresh && this.self._grid.getTotalCount() > _response.total)
{
this.self.keepSelection();
}
// Call the inherited function
super._fetchCallback.apply(this, arguments);
// Restore selection, if passed
if(this.self && this.self.kept_selection && this.self._selectionMgr)
{
if(this.self.kept_selection.all)
{
this.self._selectionMgr.selectAll();
}
for(var i = (this.self.kept_selection.ids.length || 1)-1; i >= 0; i--)
{
// Only keep the selected if they came back in the fetch
if(_response.order.indexOf(this.self.kept_selection.ids[i]) >= 0)
{
this.self._selectionMgr.setSelected(this.self.kept_selection.ids[i],true);
this.self.kept_selection.ids.splice(i,1);
}
else
{
this.self.kept_selection.ids.splice(i,1);
}
}
if(this.self.kept_focus && _response.order.indexOf(this.self.kept_focus) >= 0)
{
this.self._selectionMgr.setFocused(this.self.kept_focus,true);
}
// Re-expanding rows handled in et2_extension_nextmatch_rowProvider
// Expansions might still be valid, so we don't clear them
if(this.self.kept_selection != null && typeof this.self.kept_selection.ids != 'undefined' && this.self.kept_selection.ids.length == 0)
{
this.self.kept_selection = null;
}
this.self.kept_focus = null;
}
}
/**
* Execute the select callback when the row selection changes
*/
_selectCallback(action,senders)
{
if(typeof senders == "undefined")
{
senders = [];
}
if(!this._widget) return;
// inform mobile framework about nm selections, need to update status of header objects on selection
if (egwIsMobile()) framework.nm_onselect_ctrl(this._widget, action, senders);
this._widget.onselect.call(this._widget, action,senders);
}
/** -- Implementation of et2_IDataProvider -- **/
dataFetch( _queriedRange, _callback, _context)
{
// Merge the parent id into the _queriedRange if it is set
if (this._parentId !== null)
{
_queriedRange["parent_id"] = this._parentId;
}
// sub-levels dont have there own _filters object, need to use the one from parent (or it's parents parent)
var obj = this;
while((typeof obj._filters == 'undefined' || jQuery.isEmptyObject(obj._filters)) && obj._parentController)
{
obj = obj._parentController;
}
// Pass the fetch call to the API, multiplex the data about the
// nextmatch instance into the call.
this.egw.dataFetch(
this._widget.getInstanceManager().etemplate_exec_id || this._execId,
_queriedRange,
obj._filters,
this._widgetId,
_callback,
_context);
}
dataRegisterUID( _uid, _callback, _context)
{
this.egw.dataRegisterUID(_uid, _callback, _context,
this._widget.getInstanceManager().etemplate_exec_id || this._execId,
this._widgetId
);
}
dataUnregisterUID( )
{
// Overwritten in the constructor
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,714 @@
/**
* EGroupware eTemplate2 - Class which contains a factory method for rows
*
* @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 2012
* @version $Id$
*/
/*egw:uses
/vendor/bower-asset/jquery/dist/jquery.js;
et2_core_inheritance;
et2_core_interfaces;
et2_core_arrayMgr;
et2_core_widget;
et2_dataview_view_rowProvider;
*/
import {et2_widget} from "./et2_core_widget";
import {et2_arrayMgrs_expand} from "./et2_core_arrayMgr";
/**
* The row provider contains prototypes (full clonable dom-trees)
* for all registered row types.
*
*/
export class et2_nextmatch_rowProvider
{
private _rowProvider: any;
private _subgridCallback: any;
private _context: any;
private _rootWidget: any;
private _template: any;
private _dataRow: any;
/**
* Creates the nextmatch row provider.
*
* @param {et2_nextmatch_rowProvider} _rowProvider
* @param {function} _subgridCallback
* @param {object} _context
* @memberOf et2_nextmatch_rowProvider
*/
constructor( _rowProvider, _subgridCallback, _context)
{
// Copy the arguments
this._rowProvider = _rowProvider;
this._subgridCallback = _subgridCallback;
this._context = _context;
this._createEmptyPrototype();
}
destroy()
{
this._rowProvider.destroy();
this._subgridCallback = null;
this._context = null;
this._dataRow = null;
}
/**
* Creates the data row prototype.
*
* @param _widgets is an array containing the root widget for each column.
* @param _rowData contains the properties of the root "tr" (like its class)
* @param _rootWidget is the parent widget of the data rows (i.e.
* the nextmatch)
*/
setDataRowTemplate( _widgets, _rowData, _rootWidget)
{
// Copy the root widget
this._rootWidget = _rootWidget;
// Create the base row
var row = this._rowProvider.getPrototype("default");
// Copy the row template
var rowTemplate = {
"row": row[0],
"rowData": _rowData,
"widgets": _widgets,
"root": _rootWidget,
"seperated": null,
"mgrs": _rootWidget.getArrayMgrs()
};
// Create the row widget and insert the given widgets into the row
var rowWidget = new et2_nextmatch_rowWidget(rowTemplate.mgrs, row[0]);
rowWidget._parent = _rootWidget;
rowWidget.createWidgets(_widgets);
// Get the set containing all variable attributes
var variableAttributes = this._getVariableAttributeSet(rowWidget);
// Filter out all widgets which do not implement the et2_IDetachedDOM
// interface or do not support all attributes listed in the et2_IDetachedDOM
// interface. A warning is issued for all those widgets as they heavily
// degrade the performance of the dataview
var seperated = rowTemplate.seperated =
this._seperateWidgets(variableAttributes);
// Remove all DOM-Nodes of all widgets inside the "remaining" slot from
// the row-template, then build the access functions for the detachable
// widgets
this._stripTemplateRow(rowTemplate);
this._buildNodeAccessFuncs(rowTemplate);
// Create the DOM row template
var tmpl = document.createDocumentFragment();
row.children().each(function() { tmpl.appendChild(this); });
this._dataRow = tmpl;
this._template = rowTemplate;
}
getDataRow( _data : any, _row, _idx, _controller)
{
// Clone the row template
var row = this._dataRow.cloneNode(true);
// Create array managers with the given data merged in
var mgrs = et2_arrayMgrs_expand(rowWidget, this._template.mgrs,
_data, _idx);
// Insert the widgets into the row which do not provide the functions
// to set the _data directly
var rowWidget : et2_nextmatch_rowTemplateWidget = null;
if (this._template.seperated.remaining.length > 0)
{
// Transform the variable attributes
for (var i = 0; i < this._template.seperated.remaining.length; i++)
{
var entry = this._template.seperated.remaining[i];
for (var j = 0; j < entry.data.length; j++)
{
var set = entry.data[j];
entry.widget.options[set.attribute] = mgrs["content"].expandName(set.expression);
}
}
// Create the row widget
var rowWidget = new et2_nextmatch_rowTemplateWidget(this._rootWidget,
row);
// Let the row widget create the widgets
rowWidget.createWidgets(mgrs, this._template.placeholders);
}
// Update the content of all other widgets
for (var i = 0; i < this._template.seperated.detachable.length; i++)
{
var entry = this._template.seperated.detachable[i];
// Parse the attribute expressions
var data : any = {};
for (var j = 0; j < entry.data.length; j++)
{
var set = entry.data[j];
data[set.attribute] = mgrs["content"].expandName(set.expression);
}
// Retrieve all DOM-Nodes
var nodes = new Array(entry.nodeFuncs.length);
for (var j = 0; j < nodes.length; j++)
{
// Use the previously compiled node function to get the node
// from the entry
nodes[j] = entry.nodeFuncs[j](row);
}
// Set the array managers first
entry.widget._mgrs = mgrs;
if (typeof data.id != "undefined")
{
entry.widget.id = data.id;
}
// Adjust data for that row
entry.widget.transformAttributes.call(entry.widget,data);
// Call the setDetachedAttributes function
entry.widget.setDetachedAttributes(nodes, data, _data);
}
// Insert the row into the tr
var tr = _row.getDOMNode();
tr.appendChild(row);
// Make the row expandable
if (typeof _data.content["is_parent"] !== "undefined"
&& _data.content["is_parent"])
{
_row.makeExpandable(true, function () {
return this._subgridCallback.call(this._context,
_row, _data, _controller);
}, this);
// Check for kept expansion, and set the row up to be re-expanded
// Only the top controller tracks expanded, including sub-grids
var top_controller = _controller;
while(top_controller._parentController != null)
{
top_controller = top_controller._parentController;
}
var expansion_index = top_controller.kept_expansion.indexOf(
top_controller.dataStorePrefix + '::' + _data.content[this._context.settings.row_id]
);
if(top_controller.kept_expansion && expansion_index >=0)
{
top_controller.kept_expansion.splice(expansion_index,1);
// Use a timeout since the DOM nodes might not be finished yet
window.setTimeout(function() {
_row.expansionButton.trigger('click');
},et2_dataview_grid.ET2_GRID_INVALIDATE_TIMEOUT);
}
}
// Set the row data
this._setRowData(this._template.rowData, tr, mgrs);
return rowWidget;
}
/**
* Placeholder for empty row
*
* The empty row placeholder is used when there are no results to display.
* This allows the user to still have a drop target, or use actions that
* do not require a row ID, such as 'Add new'.
*/
_createEmptyPrototype( )
{
var label = this._context && this._context.options && this._context.options.settings.placeholder;
var placeholder = jQuery(document.createElement("td"))
.attr("colspan",this._rowProvider.getColumnCount())
.css("height","19px")
.text(typeof label != "undefined" && label ? label : egw().lang("No matches found"));
this._rowProvider._prototypes["empty"] = jQuery(document.createElement("tr"))
.addClass("egwGridView_empty")
.append(placeholder);
}
/** -- PRIVATE FUNCTIONS -- **/
/**
* Returns an array containing objects which have variable attributes
*
* @param {et2_widget} _widget
*/
_getVariableAttributeSet( _widget)
{
var variableAttributes = [];
_widget.iterateOver(function(_widget) {
// Create the attribtues
var hasAttr = false;
var widgetData = {
"widget": _widget,
"data": []
};
// Get all attribute values
for (var key in _widget.attributes)
{
if (!_widget.attributes[key].ignore &&
typeof _widget.options[key] != "undefined")
{
var val = _widget.options[key];
// TODO: Improve detection
if (typeof val == "string" && val.indexOf("$") >= 0)
{
hasAttr = true;
widgetData.data.push({
"attribute": key,
"expression": val
});
}
}
}
// Add the entry if there is any data in it
if (hasAttr)
{
variableAttributes.push(widgetData);
}
}, this);
return variableAttributes;
}
_seperateWidgets( _varAttrs)
{
// The detachable array contains all widgets which implement the
// et2_IDetachedDOM interface for all needed attributes
var detachable = [];
// The remaining array creates all widgets which have to be completely
// cloned when the widget tree is created
var remaining = [];
// Iterate over the widgets
for (var i = 0; i < _varAttrs.length; i++)
{
var widget = _varAttrs[i].widget;
// Check whether the widget parents are not allready in the "remaining"
// slot - if this is the case do not include the widget at all.
var insertWidget = true;
var checkWidget = function (_widget) {
if (_widget.parent != null)
{
for (var i = 0; i < remaining.length; i++)
{
if (remaining[i].widget == _widget.parent)
{
insertWidget = false;
return;
}
}
checkWidget(_widget.parent);
}
};
checkWidget(widget);
// Handle the next widget if this one should not be included.
if (!insertWidget)
{
continue;
}
// Check whether the widget implements the et2_IDetachedDOM interface
var isDetachable = false;
if (widget.implements(et2_IDetachedDOM))
{
// Get all attributes the widgets supports to be set in the
// "detached" mode
var supportedAttrs = [];
widget.getDetachedAttributes(supportedAttrs);
supportedAttrs.push("id");
isDetachable = true;
for (var j = 0; j < _varAttrs[i].data.length/* && isDetachable*/; j++)
{
var data = _varAttrs[i].data[j];
var supportsAttr = supportedAttrs.indexOf(data.attribute) != -1;
if (!supportsAttr)
{
egw.debug("warn", "et2_IDetachedDOM widget " +
widget._type + " does not support " + data.attribute);
}
isDetachable = isDetachable && supportsAttr;
}
}
// Insert the widget into the correct slot
if (isDetachable)
{
detachable.push(_varAttrs[i]);
}
else
{
remaining.push(_varAttrs[i]);
}
}
return {
"detachable": detachable,
"remaining": remaining
};
}
/**
* Removes to DOM code for all widgets in the "remaining" slot
*
* @param {object} _rowTemplate
*/
_stripTemplateRow( _rowTemplate)
{
_rowTemplate.placeholders = [];
for (var i = 0; i < _rowTemplate.seperated.remaining.length; i++)
{
var entry = _rowTemplate.seperated.remaining[i];
// Issue a warning - widgets which do not implement et2_IDOMNode
// are very slow
egw.debug("warn", "Non-clonable widget '"+ entry.widget._type + "' in dataview row - this " +
"might be slow", entry);
// Set the placeholder for the entry to null
entry.placeholder = null;
// Get the outer DOM-Node of the widget
if (entry.widget.implements(et2_IDOMNode))
{
var node = entry.widget.getDOMNode(entry.widget);
if (node && node.parentNode)
{
// Get the parent node and replace the node with a placeholder
entry.placeholder = document.createElement("span");
node.parentNode.replaceChild(entry.placeholder, node);
_rowTemplate.placeholders.push({
"widget": entry.widget,
"func": this._compileDOMAccessFunc(_rowTemplate.row,
entry.placeholder)
});
}
}
}
}
_nodeIndex( _node)
{
if(_node.parentNode == null)
{
return 0;
}
for (var i = 0; i < _node.parentNode.childNodes.length; i++)
{
if (_node.parentNode.childNodes[i] == _node)
{
return i;
}
}
return -1;
}
/**
* Returns a function which does a relative access on the given DOM-Node
*
* @param {DOMElement} _root
* @param {DOMElement} _target
*/
_compileDOMAccessFunc( _root, _target)
{
function recordPath(_root, _target, _path)
{
if (typeof _path == "undefined")
{
_path = [];
}
if (_root != _target && _target)
{
// Get the index of _target in its parent node
var idx = this._nodeIndex(_target);
if (idx >= 0)
{
// Add the access selector
_path.unshift("childNodes[" + idx + "]");
// Record the remaining path
return recordPath.call(this, _root, _target.parentNode, _path);
}
throw("Internal error while compiling DOM access function.");
}
else
{
_path.unshift("_node");
return "return " + _path.join(".") + ";";
}
}
return new Function("_node", recordPath.call(this, _root, _target));
}
/**
* Builds relative paths to the DOM-Nodes and compiles fast-access functions
*
* @param {object} _rowTemplate
*/
_buildNodeAccessFuncs( _rowTemplate)
{
for (var i = 0; i < _rowTemplate.seperated.detachable.length; i++)
{
var entry = _rowTemplate.seperated.detachable[i];
// Get all needed nodes from the widget
var nodes = entry.widget.getDetachedNodes();
var nodeFuncs = entry.nodeFuncs = new Array(nodes.length);
// Record the path to each DOM-Node
for (var j = 0; j < nodes.length; j++)
{
nodeFuncs[j] = this._compileDOMAccessFunc(_rowTemplate.row,
nodes[j]);
}
}
}
/**
* Match category-ids from class attribute eg. "cat_15" or "123,456,789 "
*
* Make sure to not match numbers inside other class-names.
*
* We can NOT use something like /(^| |,|cat_)([0-9]+)( |,|$)/g as it wont find all cats in "123,456,789 "!
*/
cat_regexp: RegExp = /(^| |,|cat_)([0-9]+)/g;
/**
* Regular expression used to filter out non-nummerical chars from above matches
*/
cat_cleanup: RegExp = /[^0-9]/g;
/**
* Applies additional row data (like the class) to the tr
*
* @param {object} _data
* @param {DOMElement} _tr
* @param {object} _mgrs
*/
_setRowData( _data, _tr, _mgrs)
{
// TODO: Implement other fields than "class"
if (_data["class"])
{
var classes = _mgrs["content"].expandName(_data["class"]);
// Get fancy with categories
var cats = [];
// Assume any numeric class is a category
if(_data["class"].indexOf("cat") !== -1 || classes.match(/[0-9]+/))
{
// Accept either cat, cat_id or category as ID, and look there for category settings
var category_location = _data["class"].match(/(cat(_id|egory)?)/);
if(category_location) category_location = category_location[0];
cats = classes.match(this.cat_regexp) || [];
classes = classes.replace(this.cat_regexp, '');
// Set category class
for(var i = 0; i < cats.length; i++)
{
// Need cat_, classes can't start with a number
var cat_id = cats[i].replace(this.cat_cleanup, '');
var cat_class = 'cat_'+cat_id;
classes += ' '+cat_class;
}
classes += " row_category";
}
classes += " row";
_tr.setAttribute("class", classes);
}
if(_data['valign'])
{
var align = _mgrs["content"].expandName(_data["valign"]);
_tr.setAttribute("valign", align);
}
}
}
/**
* @augments et2_widget
*/
class et2_nextmatch_rowWidget extends et2_widget implements et2_IDOMNode
{
private _widgets: any[];
private _row: any;
/**
* Constructor
*
* @param _mgrs
* @param _row
* @memberOf et2_nextmatch_rowWidget
*/
constructor( _mgrs, _row)
{
// Call the parent constructor with some dummy attributes
super(null, {"id": "", "type": "rowWidget"});
// Initialize some variables
this._widgets = [];
// Copy the given DOM node and the content arrays
this._mgrs = _mgrs;
this._row = _row;
}
/**
* Copies the given array manager and clones the given widgets and inserts
* them into the row which has been passed in the constructor.
*
* @param {array} _widgets
*/
createWidgets( _widgets)
{
// Clone the given the widgets with this element as parent
this._widgets = new Array(_widgets.length);
for (var i = 0; i < _widgets.length; i++)
{
// Disabled columns might be missing widget - skip it
if(!_widgets[i]) continue;
this._widgets[i] = _widgets[i].clone(this);
this._widgets[i].loadingFinished();
// Set column alignment from widget
if(this._widgets[i].align)
{
this._row.childNodes[i].align = this._widgets[i].align;
}
}
}
/**
* Returns the column node for the given sender
*
* @param {et2_widget} _sender
* @return {DOMElement}
*/
getDOMNode( _sender)
{
for (var i = 0; i < this._widgets.length; i++)
{
if (this._widgets[i] == _sender)
{
return this._row.childNodes[i].childNodes[0]; // Return the i-th td tag
}
}
return null;
}
}
/**
* @augments et2_widget
*/
class et2_nextmatch_rowTemplateWidget extends et2_widget implements et2_IDOMNode
{
private _root: any;
private _row: any;
private _widgets: any[];
/**
* Constructor
*
* @param _root
* @param _row
* @memberOf et2_nextmatch_rowTemplateWidget
*/
constructor( _root, _row)
{
// Call the parent constructor with some dummy attributes
super(null, {"id": "", "type": "rowTemplateWidget"});
this._root = _root;
this._mgrs = {};
this._row = _row;
// Set parent to root widget, so sub-widget calls still work
this._parent = _root;
// Clone the widgets inside the placeholders array
this._widgets = [];
}
createWidgets( _mgrs, _widgets : {widget : et2_widget,func(_row: any): any; }[])
{
// Set the array managers - don't use setArrayMgrs here as this creates
// an unnecessary copy of the object
this._mgrs = _mgrs;
this._widgets = new Array(_widgets.length);
for (var i = 0; i < _widgets.length; i++)
{
this._row.childNodes[0].childNodes[0];
this._widgets[i] = {
"widget": _widgets[i].widget.clone(this),
"node": _widgets[i].func(this._row)
};
this._widgets[i].widget.loadingFinished();
}
}
/**
* Returns the column node for the given sender
*
* @param {et2_widget} _sender
* @return {DOMElement}
*/
getDOMNode( _sender: et2_widget): HTMLElement
{
for (var i = 0; i < this._widgets.length; i++)
{
if (this._widgets[i].widget == _sender)
{
return this._widgets[i].node;
}
}
return null;
}
}

View File

@ -3,28 +3,46 @@ declare module eT2
}
declare var etemplate2 : any;
declare var et2_DOMWidget : any;
declare class et2_widget{
destroy()
getWidgetById(string) : et2_widget;
}
declare class et2_DOMWidget extends et2_widget{}
declare class et2_baseWidget extends et2_DOMWidget{}
declare class et2_valueWidget extends et2_baseWidget{}
declare class et2_inputWidget extends et2_valueWidget{
getInputNode() : HTMLElement;
public set_value(value: string | object | number);
public getValue() : any;
}
declare class et2_tabbox extends et2_valueWidget {
tabData : any;
activateTab(et2_widget);
}
declare class et2_button extends et2_DOMWidget {
click() : boolean;
onclick: Function;
set_disabled(b: boolean) : void;
}
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_editableWidget : any;
declare var et2_inputWidget : any;
declare var et2_IDOMNode : any;
declare var et2_validTypes : string[];
declare var et2_typeDefaults : object;
//declare const et2_no_init : object;
declare class et2_editableWidget extends et2_inputWidget {
public set_readonly(value : boolean);
}
/*declare var et2_IDOMNode : any;
declare var et2_IInput : any;
declare var et2_IResizeable : any;
declare var et2_IAligned : any;
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_IPrint : any;*/
declare var et2_registry : {};
declare var et2_dataview : any;
declare var et2_dataview_controller : any;
declare var et2_dataview_selectionManager : any;
@ -39,22 +57,27 @@ declare var et2_dataview_row : any;
declare var et2_dataview_rowProvider : any;
declare var et2_dataview_spacer : any;
declare var et2_dataview_tile : any;
declare var et2_customfields_list : any;
declare var et2_INextmatchHeader : any;
declare var et2_INextmatchSortable : any;
declare var et2_nextmatch : any;
declare class et2_customfields_list extends et2_valueWidget {
constructor(_parent: any, _attrs: WidgetConfig, object: object);
public static readonly prefix : string;
public customfields : any;
set_visible(visible : boolean);
}
declare class et2_nextmatch extends et2_DOMWidget {
}
declare var et2_nextmatch_header_bar : any;
declare var et2_nextmatch_header : any;
declare var et2_nextmatch_customfields : any;
declare var et2_nextmatch_sortheader : any;
declare var et2_nextmatch_filterheader : any;
declare var et2_nextmatch_accountfilterheader : any;
declare var et2_nextmatch_taglistheader : any;
declare var et2_nextmatch_entryheader : any;
declare var et2_nextmatch_customfilter : any;
declare var et2_nextmatch_controller : any;
declare var et2_dynheight : any;
declare var et2_nextmatch_rowProvider : any;
declare class et2_dynheight {
constructor(_outerNode, _innerNode, _minHeight);
outerNode : any;
update : any;
free : any;
}
declare class et2_nextmatch_rowProvider {}
declare var et2_nextmatch_rowWidget : any;
declare var et2_nextmatch_rowTemplateWidget : any;
declare var et2_ajaxSelect : any;
@ -62,7 +85,6 @@ declare var et2_ajaxSelect_ro : any;
declare var et2_barcode : any;
declare var et2_box : any;
declare var et2_details : any;
declare var et2_button : any;
declare var et2_checkbox : any;
declare var et2_checkbox_ro : any;
declare var et2_color : any;
@ -78,7 +100,7 @@ declare var et2_diff : any;
declare var et2_dropdown_button : any;
declare var et2_entry : any;
declare var et2_favorites : any;
declare var et2_file : any;
declare class et2_file extends et2_widget {}
declare var et2_grid : any;
declare var et2_groupbox : any;
declare var et2_groupbox_legend : any;
@ -87,7 +109,9 @@ declare var et2_historylog : any;
declare var et2_hrule : any;
declare var et2_html : any;
declare var et2_htmlarea : any;
declare var et2_iframe : any;
declare class et2_iframe extends et2_valueWidget {
public set_src(string);
}
declare var et2_image : any;
declare var et2_appicon : any;
declare var et2_avatar : any;
@ -110,13 +134,19 @@ declare var et2_radioGroup : any;
declare var et2_script : any;
declare var et2_selectAccount : any;
declare var et2_selectAccount_ro : any;
declare var et2_selectbox : any;
declare class et2_selectbox extends et2_inputWidget {
protected options : any;
public createInputWidget();
public set_multiple(boolean);
public set_select_options(options: any);
}
declare var et2_selectbox_ro : any;
declare var et2_menulist : any;
declare var et2_split : any;
declare var et2_styles : any;
declare var et2_tabbox : any;
declare var et2_taglist : any;
declare class et2_taglist extends et2_selectbox {
protected div : JQuery;
}
declare var et2_taglist_account : any;
declare var et2_taglist_email : any;
declare var et2_taglist_category : any;
@ -128,7 +158,7 @@ declare var et2_textbox : any;
declare var et2_textbox_ro : any;
declare var et2_searchbox : any;
declare var et2_timestamper : any;
declare var et2_toolbar : any;
declare class et2_toolbar extends et2_DOMWidget {}
declare var et2_tree : any;
declare var et2_url : any;
declare var et2_url_ro : any;
@ -143,13 +173,36 @@ declare var et2_vfsUid : any;
declare var et2_vfsUpload : any;
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 var tinymce : any;
declare var date : any;
declare var tinyMCE : any;
declare class et2_nextmatch_sortheader extends et2_nextmatch_header {}
declare class et2_nextmatch_filterheader extends et2_nextmatch_header {}
declare class et2_nextmatch_accountfilterheader extends et2_nextmatch_header {}
declare class et2_nextmatch_taglistheader extends et2_nextmatch_header {}
declare class et2_nextmatch_entryheader extends et2_nextmatch_header {}
declare class et2_nextmatch_customfilter extends et2_nextmatch_filterheader {}
declare function et2_createWidget(type : string, params? : {}, parent? : any) : any;
declare function nm_action(_action : {}, _senders : [], _target? : any, _ids? : any) : void;
declare function et2_compileLegacyJS(_code : string, _widget : et2_widget, _context? : HTMLElement) : Function;
// et2_core_xml.js
declare function et2_loadXMLFromURL(_url : string, _callback : Function, _context? : object, _fail_callback? : Function) : void;
declare function et2_directChildrenByTagName(_node, _tagName);
declare function et2_filteredNodeIterator(_node, _callback, _context);
declare function et2_readAttrWithDefault(_node, _name, _default?);
declare function sprintf(format : string, ...args : any) : string;
declare function fetchAll(ids, nextmatch, callback : Function) : boolean;
declare function doLongTask(idsArr : string[], all : boolean, _action : any, nextmatch : any) : boolean;
declare function nm_compare_field(_action, _senders, _target) : boolean;
declare function nm_open_popup(_action, _selected) : void;
declare function nm_submit_popup(button) : void;
declare function nm_hide_popup(element, div_id) : false;
declare function nm_activate_link(_action, _senders) : void;
declare function nm_activate_link(_action, _senders) : void;
declare function egw_seperateJavaScript(_html) : void;
declare class Resumable {
constructor(asyncOptions: any);
}
declare class dhtmlXTreeObject {
constructor(options : any);
}
declare function expose(widget:any) : any;

View File

@ -1,3 +1,4 @@
"use strict";
/**
* EGroupware eTemplate2 - JS Ajax select / auto complete object
@ -8,16 +9,32 @@
* @link http://www.egroupware.org
* @author Nathan Gray
* @copyright Nathan Gray 2012
* @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
/vendor/bower-asset/jquery/dist/jquery.js;
/vendor/bower-asset/jquery-ui/jquery-ui.js;
et2_core_inputWidget;
et2_core_valueWidget;
/vendor/bower-asset/jquery/dist/jquery.js;
/vendor/bower-asset/jquery-ui/jquery-ui.js;
et2_core_inputWidget;
et2_core_valueWidget;
*/
var et2_core_widget_1 = require("./et2_core_widget");
var et2_core_inputWidget_1 = require("./et2_core_inputWidget");
var et2_core_inheritance_1 = require("./et2_core_inheritance");
var et2_core_valueWidget_1 = require("./et2_core_valueWidget");
var et2_widget_selectbox_1 = require("./et2_widget_selectbox");
/**
* Using AJAX, this widget allows a type-ahead find similar to a ComboBox, where as the user enters information,
* a drop-down box is populated with the n closest matches. If the user clicks on an item in the drop-down, that
@ -27,227 +44,205 @@
* This widget can get data from any function that can provide data to a nextmatch widget.
* @augments et2_inputWidget
*/
var et2_ajaxSelect = (function(){ "use strict"; return et2_inputWidget.extend(
{
attributes: {
'get_rows': {
"name": "Data source",
"type": "any",
"default": "",
"description": "Function to get search results, either a javascript function or server-side."
},
'get_title': {
"name": "Title function",
"type": "any",
"default": "",
"description": "Function to get title for selected entry. Used when closed, and if no template is given."
},
'id_field': {
"name": "Result ID field",
"type": "string",
"default": "value",
"description": "Which key in result sub-array to look for row ID. If omitted, the key for the row will be used."
},
'template': {
"name": "Row template",
"type": "string",
"default": "",
"description": "ID of the template to use to display rows. If omitted, title will be shown for each result."
},
'filter': {
"name": "Filter",
"type": "string",
"default": "",
"description": "Apply filter to search results. Same as nextmatch."
},
'filter2': {
"name": "Filter 2",
"type": "string",
"default": "",
"description": "Apply filter to search results. Same as nextmatch."
},
'link': {
"name": "Read only link",
"type": "boolean",
"default": "true",
"description": "If readonly, widget will be text. If link is set, widget will be a link."
},
// Pass by code only
'values': {
"name": "Values",
"type": "any",
"default": {},
"description": "Specify the available options. Use this, or Data source."
}
},
/**
* Constructor
*
* @memberOf et2_ajaxSelect
*/
init: function(parent, attrs) {
this._super.apply(this, arguments);
if(typeof attrs.get_rows == 'string')
{
attrs.get_rows = this.egw().link('/index.php', {
menuaction: this.options.get_rows
})
}
this.createInputWidget();
this.input = null;
this.createInputWidget();
},
createInputWidget: function() {
this.input = jQuery(document.createElement("input"));
this.input.addClass("et2_textbox");
this.setDOMNode(this.input[0]);
var widget = this;
this.input.autocomplete({
delay: 100,
source: this.options.get_rows ?
this.options.get_rows :
et2_selectbox.find_select_options(this,this.options.values),
select: function(event, ui) {
widget.value = ui.item[widget.options.id_field];
if(widget.options.get_title)
{
if(typeof widget.options.get_title == 'function')
{
widget.input.val(widget.options.get_title.call(widget.value));
}
else if (typeof widget.options.get_title == 'string')
{
// TODO: Server side callback
}
}
else
{
widget.input.val(ui.item.label);
}
// Prevent default action of setting field to the value
return false;
}
});
},
getValue: function()
{
if(this.options.blur && this.input.val() == this.options.blur) return "";
return this.value;
},
set_value: function(_value)
{
this.value = _value;
if(this.input.autocomplete('instance'))
{
var source = this.input.autocomplete('option','source');
if(typeof source == 'object')
{
for(var i in source)
{
if(typeof source[i].value != 'undefined' && typeof source[i].label != 'undefined' && source[i].value === _value)
{
this.input.val(source[i].label)
}
else if (typeof source[i] == 'string')
{
this.input.val(source[_value]);
break;
}
}
}
else if(typeof source == 'function')
{
// TODO
}
}
},
set_blur: function(_value) {
if(_value) {
this.input.attr("placeholder", _value + ""); // HTML5
if(!this.input[0].placeholder) {
// Not HTML5
if(this.input.val() == "") this.input.val(this.options.blur);
this.input.focus(this,function(e) {
if(e.data.input.val() == e.data.options.blur) e.data.input.val("");
}).blur(this, function(e) {
if(e.data.input.val() == "") e.data.input.val(e.data.options.blur);
});
}
} else {
this.input.removeAttr("placeholder");
}
}
});}).call(this);
et2_register_widget(et2_ajaxSelect, ["ajax_select"]);
var et2_ajaxSelect = /** @class */ (function (_super) {
__extends(et2_ajaxSelect, _super);
/**
* Constructor
*
* @memberOf et2_ajaxSelect
*/
function et2_ajaxSelect(_parent, _attrs, _child) {
var _this =
// Call the inherited constructor
_super.call(this, _parent, _attrs, et2_core_inheritance_1.ClassWithAttributes.extendAttributes(et2_ajaxSelect._attributes, _child || {})) || this;
_this.input = null;
if (typeof _attrs.get_rows == 'string') {
_attrs.get_rows = _this.egw().link('/index.php', {
menuaction: _this.options.get_rows
});
}
_this.createInputWidget();
_this.input = null;
_this.createInputWidget();
return _this;
}
et2_ajaxSelect.prototype.createInputWidget = function () {
this.input = jQuery(document.createElement("input"));
this.input.addClass("et2_textbox");
this.setDOMNode(this.input[0]);
var widget = this;
this.input.autocomplete({
delay: 100,
source: this.options.get_rows ?
this.options.get_rows :
et2_widget_selectbox_1.et2_selectbox.find_select_options(this, this.options.values),
select: function (event, ui) {
widget.value = ui.item[widget.options.id_field];
if (widget.options.get_title) {
if (typeof widget.options.get_title == 'function') {
widget.input.val(widget.options.get_title.call(widget.value));
}
else if (typeof widget.options.get_title == 'string') {
// TODO: Server side callback
}
}
else {
widget.input.val(ui.item.label);
}
// Prevent default action of setting field to the value
return false;
}
});
};
et2_ajaxSelect.prototype.getValue = function () {
if (this.options.blur && this.input.val() == this.options.blur)
return "";
return this.value;
};
et2_ajaxSelect.prototype.set_value = function (_value) {
this.value = _value;
if (this.input.autocomplete('instance')) {
var source = this.input.autocomplete('option', 'source');
if (typeof source == 'object') {
for (var i in source) {
if (typeof source[i].value != 'undefined' && typeof source[i].label != 'undefined' && source[i].value === _value) {
this.input.val(source[i].label);
}
else if (typeof source[i] == 'string') {
this.input.val(source[_value]);
break;
}
}
}
else if (typeof source == 'function') {
// TODO
}
}
};
et2_ajaxSelect.prototype.set_blur = function (_value) {
if (_value) {
this.input.attr("placeholder", _value + ""); // HTML5
if (!this.input[0]["placeholder"]) {
// Not HTML5
if (this.input.val() == "")
this.input.val(this.options.blur);
this.input.focus(this, function (e) {
if (e.data.input.val() == e.data.options.blur)
e.data.input.val("");
}).blur(this, function (e) {
if (e.data.input.val() == "")
e.data.input.val(e.data.options.blur);
});
}
}
else {
this.input.removeAttr("placeholder");
}
};
et2_ajaxSelect._attributes = {
'get_rows': {
"name": "Data source",
"type": "any",
"default": "",
"description": "Function to get search results, either a javascript function or server-side."
},
'get_title': {
"name": "Title function",
"type": "any",
"default": "",
"description": "Function to get title for selected entry. Used when closed, and if no template is given."
},
'id_field': {
"name": "Result ID field",
"type": "string",
"default": "value",
"description": "Which key in result sub-array to look for row ID. If omitted, the key for the row will be used."
},
'template': {
"name": "Row template",
"type": "string",
"default": "",
"description": "ID of the template to use to display rows. If omitted, title will be shown for each result."
},
'filter': {
"name": "Filter",
"type": "string",
"default": "",
"description": "Apply filter to search results. Same as nextmatch."
},
'filter2': {
"name": "Filter 2",
"type": "string",
"default": "",
"description": "Apply filter to search results. Same as nextmatch."
},
'link': {
"name": "Read only link",
"type": "boolean",
"default": "true",
"description": "If readonly, widget will be text. If link is set, widget will be a link."
},
// Pass by code only
'values': {
"name": "Values",
"type": "any",
"default": {},
"description": "Specify the available options. Use this, or Data source."
}
};
return et2_ajaxSelect;
}(et2_core_inputWidget_1.et2_inputWidget));
et2_core_widget_1.et2_register_widget(et2_ajaxSelect, ["ajax_select"]);
/**
* et2_textbox_ro is the dummy readonly implementation of the textbox.
* @augments et2_valueWidget
*/
var et2_ajaxSelect_ro = (function(){ "use strict"; return et2_valueWidget.extend([et2_IDetachedDOM],
{
/**
* Ignore all more advanced attributes.
*/
attributes: {
"multiline": {
"ignore": true
}
},
/**
* Constructor
*
* @memberOf et2_ajaxSelect_ro
*/
init: function() {
this._super.apply(this, arguments);
this.value = "";
this.span = jQuery(document.createElement("span"));
this.setDOMNode(this.span[0]);
},
set_value: function(_value) {
this.value = _value;
if(!_value) _value = "";
this.span.text(_value);
},
/**
* Code for implementing et2_IDetachedDOM
*/
getDetachedAttributes: function(_attrs)
{
_attrs.push("value");
},
getDetachedNodes: function()
{
return [this.span[0]];
},
setDetachedAttributes: function(_nodes, _values)
{
this.span = jQuery(_nodes[0]);
if(typeof _values["value"] != 'undefined')
{
this.set_value(_values["value"]);
}
}
});}).call(this);
et2_register_widget(et2_ajaxSelect_ro, ["ajax_select_ro"]);
* et2_textbox_ro is the dummy readonly implementation of the textbox.
* @augments et2_valueWidget
*/
var et2_ajaxSelect_ro = /** @class */ (function (_super) {
__extends(et2_ajaxSelect_ro, _super);
/**
* Constructor
*
* @memberOf et2_ajaxSelect_ro
*/
function et2_ajaxSelect_ro(_parent, _attrs, _child) {
var _this =
// Call the inherited constructor
_super.call(this, _parent, _attrs, et2_core_inheritance_1.ClassWithAttributes.extendAttributes(et2_ajaxSelect_ro._attributes, _child || {})) || this;
_this.value = "";
_this.span = jQuery(document.createElement("span"));
_this.setDOMNode(_this.span[0]);
return _this;
}
et2_ajaxSelect_ro.prototype.set_value = function (_value) {
this.value = _value;
if (!_value)
_value = "";
this.span.text(_value);
};
/**
* Code for implementing et2_IDetachedDOM
*/
et2_ajaxSelect_ro.prototype.getDetachedAttributes = function (_attrs) {
_attrs.push("value");
};
et2_ajaxSelect_ro.prototype.getDetachedNodes = function () {
return [this.span[0]];
};
et2_ajaxSelect_ro.prototype.setDetachedAttributes = function (_nodes, _values) {
this.span = jQuery(_nodes[0]);
if (typeof _values["value"] != 'undefined') {
this.set_value(_values["value"]);
}
};
/**
* Ignore all more advanced attributes.
*/
et2_ajaxSelect_ro._attributes = {
"multiline": {
"ignore": true
}
};
return et2_ajaxSelect_ro;
}(et2_core_valueWidget_1.et2_valueWidget));
et2_core_widget_1.et2_register_widget(et2_ajaxSelect_ro, ["ajax_select_ro"]);
//# sourceMappingURL=et2_widget_ajaxSelect.js.map

View File

@ -0,0 +1,268 @@
/**
* EGroupware eTemplate2 - JS Ajax select / auto complete object
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage api
* @link http://www.egroupware.org
* @author Nathan Gray
* @copyright Nathan Gray 2012
*/
/*egw:uses
/vendor/bower-asset/jquery/dist/jquery.js;
/vendor/bower-asset/jquery-ui/jquery-ui.js;
et2_core_inputWidget;
et2_core_valueWidget;
*/
import {et2_register_widget, WidgetConfig} from "./et2_core_widget";
import {et2_inputWidget} from "./et2_core_inputWidget";
import {ClassWithAttributes} from "./et2_core_inheritance";
import {et2_valueWidget} from "./et2_core_valueWidget";
import {et2_selectbox} from "./et2_widget_selectbox";
/**
* Using AJAX, this widget allows a type-ahead find similar to a ComboBox, where as the user enters information,
* a drop-down box is populated with the n closest matches. If the user clicks on an item in the drop-down, that
* value is selected.
* n is the maximum number of results set in the user's preferences.
* The user is restricted to selecting values in the list.
* This widget can get data from any function that can provide data to a nextmatch widget.
* @augments et2_inputWidget
*/
class et2_ajaxSelect extends et2_inputWidget
{
static readonly _attributes : any = {
'get_rows': {
"name": "Data source",
"type": "any",
"default": "",
"description": "Function to get search results, either a javascript function or server-side."
},
'get_title': {
"name": "Title function",
"type": "any",
"default": "",
"description": "Function to get title for selected entry. Used when closed, and if no template is given."
},
'id_field': {
"name": "Result ID field",
"type": "string",
"default": "value",
"description": "Which key in result sub-array to look for row ID. If omitted, the key for the row will be used."
},
'template': {
"name": "Row template",
"type": "string",
"default": "",
"description": "ID of the template to use to display rows. If omitted, title will be shown for each result."
},
'filter': {
"name": "Filter",
"type": "string",
"default": "",
"description": "Apply filter to search results. Same as nextmatch."
},
'filter2': {
"name": "Filter 2",
"type": "string",
"default": "",
"description": "Apply filter to search results. Same as nextmatch."
},
'link': {
"name": "Read only link",
"type": "boolean",
"default": "true",
"description": "If readonly, widget will be text. If link is set, widget will be a link."
},
// Pass by code only
'values': {
"name": "Values",
"type": "any",
"default": {},
"description": "Specify the available options. Use this, or Data source."
}
};
private input: JQuery = null;
private value : any;
/**
* Constructor
*
* @memberOf et2_ajaxSelect
*/
constructor(_parent, _attrs? : WidgetConfig, _child? : object)
{
// Call the inherited constructor
super(_parent, _attrs, ClassWithAttributes.extendAttributes(et2_ajaxSelect._attributes, _child || {}));
if(typeof _attrs.get_rows == 'string')
{
_attrs.get_rows = this.egw().link('/index.php', {
menuaction: this.options.get_rows
})
}
this.createInputWidget();
this.input = null;
this.createInputWidget();
}
createInputWidget() {
this.input = jQuery(document.createElement("input"));
this.input.addClass("et2_textbox");
this.setDOMNode(this.input[0]);
let widget = this;
this.input.autocomplete({
delay: 100,
source: this.options.get_rows ?
this.options.get_rows :
et2_selectbox.find_select_options(this, this.options.values),
select: function(event, ui) {
widget.value = ui.item[widget.options.id_field];
if(widget.options.get_title)
{
if(typeof widget.options.get_title == 'function')
{
widget.input.val(widget.options.get_title.call(widget.value));
}
else if (typeof widget.options.get_title == 'string')
{
// TODO: Server side callback
}
}
else
{
widget.input.val(ui.item.label);
}
// Prevent default action of setting field to the value
return false;
}
});
}
getValue()
{
if(this.options.blur && this.input.val() == this.options.blur) return "";
return this.value;
}
set_value(_value)
{
this.value = _value;
if(this.input.autocomplete('instance'))
{
let source = this.input.autocomplete('option','source');
if(typeof source == 'object')
{
for(let i in source)
{
if(typeof source[i].value != 'undefined' && typeof source[i].label != 'undefined' && source[i].value === _value)
{
this.input.val(source[i].label)
}
else if (typeof source[i] == 'string')
{
this.input.val(source[_value]);
break;
}
}
}
else if(typeof source == 'function')
{
// TODO
}
}
}
set_blur(_value)
{
if(_value) {
this.input.attr("placeholder", _value + ""); // HTML5
if(!this.input[0]["placeholder"]) {
// Not HTML5
if(this.input.val() == "") this.input.val(this.options.blur);
this.input.focus(this,function(e) {
if(e.data.input.val() == e.data.options.blur) e.data.input.val("");
}).blur(this, function(e) {
if(e.data.input.val() == "") e.data.input.val(e.data.options.blur);
});
}
} else {
this.input.removeAttr("placeholder");
}
}
}
et2_register_widget(et2_ajaxSelect, ["ajax_select"]);
/**
* et2_textbox_ro is the dummy readonly implementation of the textbox.
* @augments et2_valueWidget
*/
class et2_ajaxSelect_ro extends et2_valueWidget implements et2_IDetachedDOM
{
/**
* Ignore all more advanced attributes.
*/
static readonly _attributes : any = {
"multiline": {
"ignore": true
}
};
private value: string;
private span: JQuery;
/**
* Constructor
*
* @memberOf et2_ajaxSelect_ro
*/
constructor(_parent, _attrs? : WidgetConfig, _child? : object)
{
// Call the inherited constructor
super(_parent, _attrs, ClassWithAttributes.extendAttributes(et2_ajaxSelect_ro._attributes, _child || {}));
this.value = "";
this.span = jQuery(document.createElement("span"));
this.setDOMNode(this.span[0]);
}
set_value(_value)
{
this.value = _value;
if(!_value) _value = "";
this.span.text(_value);
}
/**
* Code for implementing et2_IDetachedDOM
*/
getDetachedAttributes(_attrs)
{
_attrs.push("value");
}
getDetachedNodes()
{
return [this.span[0]];
}
setDetachedAttributes(_nodes, _values)
{
this.span = jQuery(_nodes[0]);
if(typeof _values["value"] != 'undefined')
{
this.set_value(_values["value"]);
}
}
}
et2_register_widget(et2_ajaxSelect_ro, ["ajax_select_ro"]);

View File

@ -1,3 +1,4 @@
"use strict";
/**
* EGroupware eTemplate2 - JS barcode widget
*
@ -9,14 +10,26 @@
* @copyright Stylite AG
* @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
/vendor/bower-asset/jquery/dist/jquery.js;
/api/js/jquery/barcode/jquery-barcode.min.js;
et2_core_interfaces;
et2_core_baseWidget;
/vendor/bower-asset/jquery/dist/jquery.js;
/api/js/jquery/barcode/jquery-barcode.min.js;
et2_core_interfaces;
et2_core_baseWidget;
*/
/**
* This widget creates barcode out of a given text
*
@ -38,123 +51,112 @@
*
* Further information about types and formats are defined in static part of the class at the end
*/
var et2_core_widget_1 = require("./et2_core_widget");
var et2_core_inheritance_1 = require("./et2_core_inheritance");
var et2_core_valueWidget_1 = require("./et2_core_valueWidget");
/**
* Class which implements the "barcode" XET-Tag
*
* @augments et2_baseWidget
*/
var et2_barcode = (function(){ "use strict"; return et2_valueWidget.extend(
{
attributes : {
"code_type": {
"name": "code type",
"type": "string",
"default": "datamatrix", //et2_barcode.TYPE_DATAMATRIX
"description": "Barcode type to be generated, default is QR barcode"
},
bgColor: {
"name":"bgColor",
"type": "string",
"default":'#FFFFFF',
"description": "Defines backgorund color of barcode container"
},
barColor: {
"name":"barColor",
"type": "string",
"default":'#000000',
"description": "Defines color of the bars in barcode."
},
format: {
"name":"format",
"type": "string",
"default":'css', //et2_barcode.FORMAT_CSS
"description": "Defines in which format the barcode should be rendered. Default is SVG."
},
barWidth: {
"name":"bar width",
"type": "string",
"default":'1',
"description": "Defines width of each bar in the barcode."
},
barHeight: {
"name":"bar height",
"type": "string",
"default":'50',
"description": "Defines heigh of each bar in the barcode."
},
},
/**
* Constructor
*
* @memberOf et2_video
*/
init: function()
{
this._super.apply(this, arguments);
this.div = jQuery(document.createElement('div')).attr({ class:'et2_barcode' });
// Set domid
this.set_id(this.id);
this.setDOMNode(this.div[0]);
this.createWidget();
},
createWidget: function ()
{
this.settings = {
output:this.options.format,
bgColor: this.options.bgColor,
color: this.options.barColor,
barWidth: this.options.barWidth,
barHeight: this.options.barHeight,
};
if (this.get_value()) this.div.barcode(this.get_value(), this.options.code_type, this.settings);
},
set_value: function (_val)
{
if (typeof _val !== 'undefined')
{
this.value = _val;
this.createWidget();
}
},
get_value: function()
{
return this.value;
}
});}).call(this);
et2_register_widget(et2_barcode, ["barcode"]);
// Static part of the class
jQuery.extend(et2_barcode,
{
// Class Constants
/*
* type const
*/
TYPE_CODEBAR: "codebar",
TYPE_CODE11: "code11", //(code 11)
TYPE_CODE39: "code39", //(code 39)
TYPE_CODE128: "code128", //(code 128)
TYPE_EAN8: "ean8", //(ean 8) - http://barcode-coder.com/en/ean-8-specification-101.html
TYPE_EAN13: "ean13", //(ean 13) - http://barcode-coder.com/en/ean-13-specification-102.html
TYPE_STD25: "std25", //(standard 2 of 5 - industrial 2 of 5) - http://barcode-coder.com/en/standard-2-of-5-specification-103.html
TYPE_INT25: "int25", //(interleaved 2 of 5)
TYPE_MSI: "msi",
TYPE_DATAMATRIX: "datamatrix", //(ASCII + extended) - http://barcode-coder.com/en/datamatrix-specification-104.html
/**
* Formats consts
*/
FORMAT_CSS: "css",
FORMAT_SVG: "svg",
FORMAT_bmp: "bmp",
FORMAT_CANVAS: "canvas",
});
var et2_barcode = /** @class */ (function (_super) {
__extends(et2_barcode, _super);
/**
* Constructor
*/
function et2_barcode(_parent, _attrs, _child) {
var _this =
// Call the inherited constructor
_super.call(this, _parent, _attrs, et2_core_inheritance_1.ClassWithAttributes.extendAttributes(et2_barcode._attributes, _child || {})) || this;
_this.div = jQuery(document.createElement('div')).attr({ class: 'et2_barcode' });
// Set domid
_this.set_id(_this.id);
_this.setDOMNode(_this.div[0]);
_this.createWidget();
return _this;
}
et2_barcode.prototype.createWidget = function () {
this.settings = {
output: this.options.format,
bgColor: this.options.bgColor,
color: this.options.barColor,
barWidth: this.options.barWidth,
barHeight: this.options.barHeight,
};
if (this.get_value()) {
// @ts-ignore
this.div.barcode(this.get_value(), this.options.code_type, this.settings);
}
};
et2_barcode.prototype.set_value = function (_val) {
if (typeof _val !== 'undefined') {
this.value = _val;
this.createWidget();
}
};
et2_barcode.prototype.get_value = function () {
return this.value;
};
// Class Constants
/*
* type const
*/
et2_barcode.TYPE_CODEBAR = "codebar";
et2_barcode.TYPE_CODE11 = "code11"; //(code 11)
et2_barcode.TYPE_CODE39 = "code39"; //(code 39)
et2_barcode.TYPE_CODE128 = "code128"; //(code 128)
et2_barcode.TYPE_EAN8 = "ean8"; //(ean 8) - http://barcode-coder.com/en/ean-8-specification-101.html
et2_barcode.TYPE_EAN13 = "ean13"; //(ean 13) - http://barcode-coder.com/en/ean-13-specification-102.html
et2_barcode.TYPE_STD25 = "std25"; //(standard 2 of 5 - industrial 2 of 5) - http://barcode-coder.com/en/standard-2-of-5-specification-103.html
et2_barcode.TYPE_INT25 = "int25"; //(interleaved 2 of 5)
et2_barcode.TYPE_MSI = "msi";
et2_barcode.TYPE_DATAMATRIX = "datamatrix"; //(ASCII + extended) - http://barcode-coder.com/en/datamatrix-specification-104.html
/**
* Formats consts
*/
et2_barcode.FORMAT_CSS = "css";
et2_barcode.FORMAT_SVG = "svg";
et2_barcode.FORMAT_bmp = "bmp";
et2_barcode.FORMAT_CANVAS = "canvas";
et2_barcode._attributes = {
"code_type": {
"name": "code type",
"type": "string",
"default": et2_barcode.TYPE_DATAMATRIX,
"description": "Barcode type to be generated, default is QR barcode"
},
bgColor: {
"name": "bgColor",
"type": "string",
"default": '#FFFFFF',
"description": "Defines backgorund color of barcode container"
},
barColor: {
"name": "barColor",
"type": "string",
"default": '#000000',
"description": "Defines color of the bars in barcode."
},
format: {
"name": "format",
"type": "string",
"default": 'css',
"description": "Defines in which format the barcode should be rendered. Default is SVG."
},
barWidth: {
"name": "bar width",
"type": "string",
"default": '1',
"description": "Defines width of each bar in the barcode."
},
barHeight: {
"name": "bar height",
"type": "string",
"default": '50',
"description": "Defines heigh of each bar in the barcode."
},
};
return et2_barcode;
}(et2_core_valueWidget_1.et2_valueWidget));
exports.et2_barcode = et2_barcode;
et2_core_widget_1.et2_register_widget(et2_barcode, ["barcode"]);
//# sourceMappingURL=et2_widget_barcode.js.map

View File

@ -0,0 +1,166 @@
/**
* EGroupware eTemplate2 - JS barcode widget
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage api
* @link http://www.egroupware.org
* @author Hadi Nategh <hn[at]stylite.de>
* @copyright Stylite AG
* @version $Id:$
*/
/*egw:uses
/vendor/bower-asset/jquery/dist/jquery.js;
/api/js/jquery/barcode/jquery-barcode.min.js;
et2_core_interfaces;
et2_core_baseWidget;
*/
/**
* This widget creates barcode out of a given text
*
* The widget can be created in the following ways:
* <code>
* var barcodeTag = et2_createWidget("barcode", {
* code_type:et2_barcode.TYPE_CSS,
* bgColor:"#FFFFFF",
* barColor:"#000000",
* format:et2_barcode.FORMAT_SVG,
* barWidth:"1",
* barHeight:"50"
* });
* </code>
* Or by adding XET-tag in your template (.xet) file:
* <code>
* <barcode [attributes...]/>
* </code>
*
* Further information about types and formats are defined in static part of the class at the end
*/
import {et2_register_widget, WidgetConfig} from "./et2_core_widget";
import {ClassWithAttributes} from "./et2_core_inheritance";
import {et2_valueWidget} from "./et2_core_valueWidget";
/**
* Class which implements the "barcode" XET-Tag
*
*/
export class et2_barcode extends et2_valueWidget
{
// Class Constants
/*
* type const
*/
public static readonly TYPE_CODEBAR = "codebar";
public static readonly TYPE_CODE11 = "code11"; //(code 11)
public static readonly TYPE_CODE39 = "code39"; //(code 39)
public static readonly TYPE_CODE128 = "code128"; //(code 128)
public static readonly TYPE_EAN8 = "ean8"; //(ean 8) - http://barcode-coder.com/en/ean-8-specification-101.html
public static readonly TYPE_EAN13 = "ean13"; //(ean 13) - http://barcode-coder.com/en/ean-13-specification-102.html
public static readonly TYPE_STD25 = "std25"; //(standard 2 of 5 - industrial 2 of 5) - http://barcode-coder.com/en/standard-2-of-5-specification-103.html
public static readonly TYPE_INT25 = "int25"; //(interleaved 2 of 5)
public static readonly TYPE_MSI = "msi";
public static readonly TYPE_DATAMATRIX = "datamatrix"; //(ASCII + extended) - http://barcode-coder.com/en/datamatrix-specification-104.html
/**
* Formats consts
*/
public static readonly FORMAT_CSS = "css";
public static readonly FORMAT_SVG = "svg";
public static readonly FORMAT_bmp = "bmp";
public static readonly FORMAT_CANVAS = "canvas";
static readonly _attributes = {
"code_type": {
"name": "code type",
"type": "string",
"default": et2_barcode.TYPE_DATAMATRIX,
"description": "Barcode type to be generated, default is QR barcode"
},
bgColor: {
"name":"bgColor",
"type": "string",
"default":'#FFFFFF',
"description": "Defines backgorund color of barcode container"
},
barColor: {
"name":"barColor",
"type": "string",
"default":'#000000',
"description": "Defines color of the bars in barcode."
},
format: {
"name":"format",
"type": "string",
"default":'css', //et2_barcode.FORMAT_CSS
"description": "Defines in which format the barcode should be rendered. Default is SVG."
},
barWidth: {
"name":"bar width",
"type": "string",
"default":'1',
"description": "Defines width of each bar in the barcode."
},
barHeight: {
"name":"bar height",
"type": "string",
"default":'50',
"description": "Defines heigh of each bar in the barcode."
},
};
private div: JQuery;
private value: string;
private settings: { output: string; barWidth: string; barHeight: string; bgColor: string; color: string };
/**
* Constructor
*/
constructor(_parent, _attrs? : WidgetConfig, _child? : object)
{
// Call the inherited constructor
super(_parent, _attrs, ClassWithAttributes.extendAttributes(et2_barcode._attributes, _child || {}));
this.div = jQuery(document.createElement('div')).attr({ class:'et2_barcode' });
// Set domid
this.set_id(this.id);
this.setDOMNode(this.div[0]);
this.createWidget();
}
createWidget()
{
this.settings = {
output:this.options.format,
bgColor: this.options.bgColor,
color: this.options.barColor,
barWidth: this.options.barWidth,
barHeight: this.options.barHeight,
};
if (this.get_value())
{
// @ts-ignore
this.div.barcode(this.get_value(), this.options.code_type, this.settings);
}
}
set_value(_val)
{
if (typeof _val !== 'undefined')
{
this.value = _val;
this.createWidget();
}
}
get_value()
{
return this.value;
}
}
et2_register_widget(et2_barcode, ["barcode"]);

View File

@ -1,3 +1,4 @@
"use strict";
/**
* EGroupware eTemplate2 - JS Box object
*
@ -9,12 +10,26 @@
* @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
/vendor/bower-asset/jquery/dist/jquery.js;
et2_core_baseWidget;
/vendor/bower-asset/jquery/dist/jquery.js;
et2_core_baseWidget;
*/
var et2_core_widget_1 = require("./et2_core_widget");
var et2_core_baseWidget_1 = require("./et2_core_baseWidget");
/**
* Class which implements box and vbox tag
*
@ -23,137 +38,110 @@
*
* @augments et2_baseWidget
*/
var et2_box = (function(){ "use strict"; return et2_baseWidget.extend([et2_IDetachedDOM],
{
attributes: {
// Not needed
"rows": {"ignore": true},
"cols": {"ignore": true}
},
createNamespace: true,
/**
* Constructor
*
* @memberOf et2_box
*/
init: function() {
this._super.apply(this, arguments);
this.div = jQuery(document.createElement("div"))
.addClass("et2_" + this._type)
.addClass("et2_box_widget");
this.setDOMNode(this.div[0]);
},
/**
* Overriden so we can check for autorepeating children. We only check for
* $ in the immediate children & grandchildren of this node.
*
* @param {object} _node
*/
loadFromXML: function(_node) {
if(this._type != "box")
{
return this._super.apply(this, arguments);
}
// Load the child nodes.
var childIndex = 0;
var repeatNode = null;
for (var i=0; i < _node.childNodes.length; i++)
{
var node = _node.childNodes[i];
var widgetType = node.nodeName.toLowerCase();
if (widgetType == "#comment")
{
continue;
}
if (widgetType == "#text")
{
if (node.data.replace(/^\s+|\s+$/g, ''))
{
this.loadContent(node.data);
}
continue;
}
// Create the new element, if no expansion needed
var id = et2_readAttrWithDefault(node, "id", "");
if(id.indexOf('$') < 0 || widgetType != 'box')
{
this.createElementFromNode(node);
childIndex++;
}
else
{
repeatNode = node;
}
}
// Only the last child repeats(?)
if(repeatNode != null)
{
var currentPerspective = this.getArrayMgr("content").perspectiveData;
// Extra content
for(childIndex; typeof this.getArrayMgr("content").data[childIndex] != "undefined" && this.getArrayMgr("content").data[childIndex]; childIndex++) {
// Adjust for the row
var mgrs = this.getArrayMgrs();
for(var name in mgrs)
{
if(this.getArrayMgr(name).getEntry(childIndex))
{
this.getArrayMgr(name).perspectiveData.row = childIndex;
}
}
this.createElementFromNode(repeatNode);
}
// Reset
for(var name in this.getArrayMgrs())
{
this.getArrayMgr(name).perspectiveData = currentPerspective;
}
}
},
/**
* Code for implementing et2_IDetachedDOM
* This doesn't need to be implemented.
* Individual widgets are detected and handled by the grid, but the interface is needed for this to happen
*
* @param {array} _attrs array to add further attributes to
*/
getDetachedAttributes: function(_attrs)
{
_attrs.push('data');
},
getDetachedNodes: function()
{
return [this.getDOMNode()];
},
setDetachedAttributes: function(_nodes, _values)
{
if (_values.data)
{
var pairs = _values.data.split(/,/g);
for(var i=0; i < pairs.length; ++i)
{
var name_value = pairs[i].split(':');
jQuery(_nodes[0]).attr('data-'+name_value[0], name_value[1]);
}
}
}
});}).call(this);
et2_register_widget(et2_box, ["vbox", "box"]);
var et2_box = /** @class */ (function (_super) {
__extends(et2_box, _super);
/**
* Constructor
*
* @memberOf et2_box
*/
function et2_box(_parent, _attrs, _child) {
var _this = _super.call(this, _parent, _attrs, _child) || this;
_this.div = jQuery(document.createElement("div"))
.addClass("et2_" + _this.getType())
.addClass("et2_box_widget");
_this.setDOMNode(_this.div[0]);
return _this;
}
et2_box.prototype._createNamespace = function () {
return true;
};
/**
* Overriden so we can check for autorepeating children. We only check for
* $ in the immediate children & grandchildren of this node.
*
* @param {object} _node
*/
et2_box.prototype.loadFromXML = function (_node) {
if (this.getType() != "box") {
return _super.prototype.loadFromXML.call(this, _node);
}
// Load the child nodes.
var childIndex = 0;
var repeatNode = null;
for (var i = 0; i < _node.childNodes.length; i++) {
var node = _node.childNodes[i];
var widgetType = node.nodeName.toLowerCase();
if (widgetType == "#comment") {
continue;
}
if (widgetType == "#text") {
if (node.data.replace(/^\s+|\s+$/g, '')) {
this.loadContent(node.data);
}
continue;
}
// Create the new element, if no expansion needed
var id = et2_readAttrWithDefault(node, "id", "");
if (id.indexOf('$') < 0 || widgetType != 'box') {
this.createElementFromNode(node);
childIndex++;
}
else {
repeatNode = node;
}
}
// Only the last child repeats(?)
if (repeatNode != null) {
var currentPerspective = this.getArrayMgr("content").perspectiveData;
// Extra content
for (childIndex; typeof this.getArrayMgr("content").data[childIndex] != "undefined" && this.getArrayMgr("content").data[childIndex]; childIndex++) {
// Adjust for the row
var mgrs = this.getArrayMgrs();
for (var name in mgrs) {
if (this.getArrayMgr(name).getEntry(childIndex)) {
this.getArrayMgr(name).setRow(childIndex);
}
}
this.createElementFromNode(repeatNode);
}
// Reset
for (var name in this.getArrayMgrs()) {
this.getArrayMgr(name).setPerspectiveData(currentPerspective);
}
}
};
/**
* Code for implementing et2_IDetachedDOM
* This doesn't need to be implemented.
* Individual widgets are detected and handled by the grid, but the interface is needed for this to happen
*
* @param {array} _attrs array to add further attributes to
*/
et2_box.prototype.getDetachedAttributes = function (_attrs) {
_attrs.push('data');
};
et2_box.prototype.getDetachedNodes = function () {
return [this.getDOMNode()];
};
et2_box.prototype.setDetachedAttributes = function (_nodes, _values) {
if (_values.data) {
var pairs = _values.data.split(/,/g);
for (var i = 0; i < pairs.length; ++i) {
var name_value = pairs[i].split(':');
jQuery(_nodes[0]).attr('data-' + name_value[0], name_value[1]);
}
}
};
et2_box._attributes = {
// Not needed
"rows": { "ignore": true },
"cols": { "ignore": true }
};
return et2_box;
}(et2_core_baseWidget_1.et2_baseWidget));
exports.et2_box = et2_box;
et2_core_widget_1.et2_register_widget(et2_box, ["vbox", "box"]);
/**
* Details widget implementation
* widget name is "details" and can be use as a wrapping container
@ -167,80 +155,74 @@ et2_register_widget(et2_box, ["vbox", "box"]);
* <details/>
*
*/
var et2_details = (function(){ "use strict"; return et2_box.extend(
{
attributes:{
"toggle_align": {
name: "Toggle button alignment",
description:" Defines where to align the toggle button, default is right alignment",
type:"string",
default: "right"
},
title: {
name: "title",
description:"Set a header title for box and shows it next to toggle button, default is no title",
type:"string",
default: "",
translate: true
}
},
init: function() {
this._super.apply(this, arguments);
this.div = jQuery(document.createElement('div')).addClass('et2_details');
this.title = jQuery(document.createElement('span'))
.addClass('et2_label et2_details_title')
.appendTo(this.div);
this.span = jQuery(document.createElement('span'))
.addClass('et2_details_toggle')
.appendTo(this.div);
this.wrapper = jQuery(document.createElement('div'))
.addClass('et2_details_wrapper')
.appendTo(this.div);
this._createWidget();
},
/**
* Function happens on toggle action
*/
_toggle: function (){
this.div.toggleClass('et2_details_expanded');
},
/**
* Create widget, set contents, and binds handlers
*/
_createWidget: function () {
var self = this;
this.span.on('click', function (e){
self._toggle();
});
//Set header title
if (this.options.title)
{
this.title
.click (function(){self._toggle();})
.text(this.options.title);
}
// Align toggle button left/right
if (this.options.toggle_align === "left") this.span.css({float:'left'});
},
getDOMNode: function(_sender) {
if (!_sender || _sender === this)
{
return this.div[0];
}
else
{
return this.wrapper[0];
}
}
});}).call(this);
et2_register_widget(et2_details, ["details"]);
var et2_details = /** @class */ (function (_super) {
__extends(et2_details, _super);
function et2_details(_parent, _attrs, _child) {
var _this = _super.call(this, _parent, _attrs, _child) || this;
_this.div = jQuery(document.createElement('div')).addClass('et2_details');
_this.title = jQuery(document.createElement('span'))
.addClass('et2_label et2_details_title')
.appendTo(_this.div);
_this.span = jQuery(document.createElement('span'))
.addClass('et2_details_toggle')
.appendTo(_this.div);
_this.wrapper = jQuery(document.createElement('div'))
.addClass('et2_details_wrapper')
.appendTo(_this.div);
_this._createWidget();
return _this;
}
/**
* Function happens on toggle action
*/
et2_details.prototype._toggle = function () {
this.div.toggleClass('et2_details_expanded');
};
/**
* Create widget, set contents, and binds handlers
*/
et2_details.prototype._createWidget = function () {
var self = this;
this.span.on('click', function (e) {
self._toggle();
});
//Set header title
if (this.options.title) {
this.title
.click(function () {
self._toggle();
})
.text(this.options.title);
}
// Align toggle button left/right
if (this.options.toggle_align === "left")
this.span.css({ float: 'left' });
};
et2_details.prototype.getDOMNode = function (_sender) {
if (!_sender || _sender === this) {
return this.div[0];
}
else {
return this.wrapper[0];
}
};
et2_details._attributes = {
"toggle_align": {
name: "Toggle button alignment",
description: " Defines where to align the toggle button, default is right alignment",
type: "string",
default: "right"
},
title: {
name: "title",
description: "Set a header title for box and shows it next to toggle button, default is no title",
type: "string",
default: "",
translate: true
}
};
return et2_details;
}(et2_box));
exports.et2_details = et2_details;
et2_core_widget_1.et2_register_widget(et2_details, ["details"]);
//# sourceMappingURL=et2_widget_box.js.map

View File

@ -0,0 +1,262 @@
/**
* EGroupware eTemplate2 - JS Box object
*
* @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
* @version $Id$
*/
/*egw:uses
/vendor/bower-asset/jquery/dist/jquery.js;
et2_core_baseWidget;
*/
import {et2_register_widget, WidgetConfig} from "./et2_core_widget";
import {et2_baseWidget} from "./et2_core_baseWidget";
/**
* Class which implements box and vbox tag
*
* Auto-repeat: In order to get box auto repeat to work we need to have another
* box as a wrapper with an id set.
*
* @augments et2_baseWidget
*/
export class et2_box extends et2_baseWidget implements et2_IDetachedDOM
{
static readonly _attributes: any = {
// Not needed
"rows": {"ignore": true},
"cols": {"ignore": true}
};
div: JQuery;
/**
* Constructor
*
* @memberOf et2_box
*/
constructor(_parent, _attrs? : WidgetConfig, _child? : object)
{
super(_parent, _attrs, _child);
this.div = jQuery(document.createElement("div"))
.addClass("et2_" + this.getType())
.addClass("et2_box_widget");
this.setDOMNode(this.div[0]);
}
_createNamespace() : boolean
{
return true;
}
/**
* Overriden so we can check for autorepeating children. We only check for
* $ in the immediate children & grandchildren of this node.
*
* @param {object} _node
*/
loadFromXML(_node) {
if(this.getType() != "box")
{
return super.loadFromXML(_node);
}
// Load the child nodes.
var childIndex = 0;
var repeatNode = null;
for (var i=0; i < _node.childNodes.length; i++)
{
var node = _node.childNodes[i];
var widgetType = node.nodeName.toLowerCase();
if (widgetType == "#comment")
{
continue;
}
if (widgetType == "#text")
{
if (node.data.replace(/^\s+|\s+$/g, ''))
{
this.loadContent(node.data);
}
continue;
}
// Create the new element, if no expansion needed
var id = et2_readAttrWithDefault(node, "id", "");
if(id.indexOf('$') < 0 || widgetType != 'box')
{
this.createElementFromNode(node);
childIndex++;
}
else
{
repeatNode = node;
}
}
// Only the last child repeats(?)
if(repeatNode != null)
{
var currentPerspective = this.getArrayMgr("content").perspectiveData;
// Extra content
for(childIndex; typeof this.getArrayMgr("content").data[childIndex] != "undefined" && this.getArrayMgr("content").data[childIndex]; childIndex++) {
// Adjust for the row
var mgrs = this.getArrayMgrs();
for(var name in mgrs)
{
if(this.getArrayMgr(name).getEntry(childIndex))
{
this.getArrayMgr(name).setRow(childIndex);
}
}
this.createElementFromNode(repeatNode);
}
// Reset
for(var name in this.getArrayMgrs()) {
this.getArrayMgr(name).setPerspectiveData(currentPerspective);
}
}
}
/**
* Code for implementing et2_IDetachedDOM
* This doesn't need to be implemented.
* Individual widgets are detected and handled by the grid, but the interface is needed for this to happen
*
* @param {array} _attrs array to add further attributes to
*/
getDetachedAttributes(_attrs)
{
_attrs.push('data');
}
getDetachedNodes()
{
return [this.getDOMNode()];
}
setDetachedAttributes(_nodes, _values)
{
if (_values.data)
{
var pairs = _values.data.split(/,/g);
for(var i=0; i < pairs.length; ++i)
{
var name_value = pairs[i].split(':');
jQuery(_nodes[0]).attr('data-'+name_value[0], name_value[1]);
}
}
}
}
et2_register_widget(et2_box, ["vbox", "box"]);
/**
* Details widget implementation
* widget name is "details" and can be use as a wrapping container
* in order to make its children collapsible.
*
* Note: details widget does not represent html5 "details" tag in DOM
*
* <details>
* <widgets>
* ....
* <details/>
*
*/
export class et2_details extends et2_box
{
static readonly _attributes: any = {
"toggle_align": {
name: "Toggle button alignment",
description:" Defines where to align the toggle button, default is right alignment",
type:"string",
default: "right"
},
title: {
name: "title",
description: "Set a header title for box and shows it next to toggle button, default is no title",
type: "string",
default: "",
translate: true
}
};
private title: JQuery;
private span: JQuery;
private readonly wrapper: JQuery;
constructor(_parent, _attrs?: WidgetConfig, _child?: object) {
super(_parent, _attrs, _child);
this.div = jQuery(document.createElement('div')).addClass('et2_details');
this.title = jQuery(document.createElement('span'))
.addClass('et2_label et2_details_title')
.appendTo(this.div);
this.span = jQuery(document.createElement('span'))
.addClass('et2_details_toggle')
.appendTo(this.div);
this.wrapper = jQuery(document.createElement('div'))
.addClass('et2_details_wrapper')
.appendTo(this.div);
this._createWidget();
}
/**
* Function happens on toggle action
*/
_toggle ()
{
this.div.toggleClass('et2_details_expanded');
}
/**
* Create widget, set contents, and binds handlers
*/
_createWidget() {
const self = this;
this.span.on('click', function (e) {
self._toggle();
});
//Set header title
if (this.options.title) {
this.title
.click(function () {
self._toggle();
})
.text(this.options.title);
}
// Align toggle button left/right
if (this.options.toggle_align === "left") this.span.css({float:'left'});
}
getDOMNode(_sender)
{
if (!_sender || _sender === this)
{
return this.div[0];
}
else
{
return this.wrapper[0];
}
}
}
et2_register_widget(et2_details, ["details"]);

View File

@ -1,3 +1,4 @@
"use strict";
/**
* EGroupware eTemplate2 - JS Button object
*
@ -6,440 +7,387 @@
* @subpackage api
* @link http://www.egroupware.org
* @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
/vendor/bower-asset/jquery/dist/jquery.js;
et2_core_interfaces;
et2_core_baseWidget;
/vendor/bower-asset/jquery/dist/jquery.js;
et2_core_interfaces;
et2_core_baseWidget;
*/
require("./et2_core_common");
var et2_core_inheritance_1 = require("./et2_core_inheritance");
var et2_core_widget_1 = require("./et2_core_widget");
var et2_core_baseWidget_1 = require("./et2_core_baseWidget");
require("./et2_types");
/**
* Class which implements the "button" XET-Tag
* @augments et2_baseWidget
*/
var et2_button = (function(){ "use strict"; return et2_baseWidget.extend([et2_IInput, et2_IDetachedDOM],
{
attributes: {
"label": {
"name": "caption",
"type": "string",
"description": "Label of the button",
"translate": true
},
"image": {
"name": "Icon",
"type": "string",
"description": "Use an icon instead of label (when available)"
},
"ro_image": {
"name": "Read-only Icon",
"type": "string",
"description": "Use this icon instead of hiding for read-only"
},
"onclick": {
"description": "JS code which gets executed when the button is clicked"
},
"accesskey": {
"name": "Access Key",
"type": "string",
"default": et2_no_init,
"description": "Alt + <key> activates widget"
},
"tabindex": {
"name": "Tab index",
"type": "integer",
"default": et2_no_init,
"description": "Specifies the tab order of a widget when the 'tab' button is used for navigating."
},
background_image: {
name: "Add image in front of text",
type: "boolean",
description: "Adds image in front of text instead of just using an image with text as tooltip",
default: et2_no_init // to leave it undefined, if not defined, so background-image is assigned by default
},
novalidate: {
name: "Do NOT validate form",
type: "boolean",
description: "Do NOT validate form before submitting it",
default: false
},
// No such thing as a required button
"needed": {
"ignore": true
}
},
legacyOptions: ["image", "ro_image"],
/**
* Constructor
*
* @memberOf et2_button
*/
init: function() {
this._super.apply(this, arguments);
this.label = "";
this.clicked = false;
this.btn = null;
this.image = null;
if (!this.options.background_image && (this.options.image || this.options.ro_image))
{
this.image = jQuery(document.createElement("img"))
.addClass("et2_button et2_button_icon");
if (!this.options.readonly) this.image.addClass("et2_clickable");
this.setDOMNode(this.image[0]);
return;
}
if (!this.options.readonly || this.options.ro_image)
{
this.btn = jQuery(document.createElement("button"))
.addClass("et2_button")
.attr({type:"button"});
this.setDOMNode(this.btn[0]);
}
if (this.options.image) this.set_image(this.options.image);
},
/**
* Apply the "modifications" to the element and translate attributes marked
* with "translate: true"
*
* Reimplemented here to assign default background-images to buttons
*
* @param {object} _attrs
*/
transformAttributes: function(_attrs)
{
if (this.id && typeof _attrs.background_image == 'undefined' && !_attrs.image)
{
for(var image in et2_button.default_background_images)
{
if (this.id.match(et2_button.default_background_images[image]))
{
_attrs.image = image;
_attrs.background_image = true;
break;
}
}
}
for(var name in et2_button.default_classes)
{
if (this.id.match(et2_button.default_classes[name]))
{
_attrs.class = (typeof _attrs.class == 'undefined' ? '' : _attrs.class+' ')+name;
break;
}
}
this._super.apply(this, arguments);
},
set_accesskey: function(key) {
jQuery(this.node).attr("accesskey", key);
},
/**
* Set image and update current image
*
* @param _image
*/
set_image: function(_image) {
this.options.image = _image;
this.update_image();
},
/**
* Set readonly image and update current image
*
* @param _image
*/
set_ro_image: function(_image) {
this.options.ro_image = _image;
this.update_image();
},
/**
* Set current image (dont update options.image)
*
* @param _image
*/
update_image: function(_image) {
if(!this.isInTree() || !this.options.background_image && this.image == null) return;
if (typeof _image == 'undefined')
_image = this.options.readonly ? (this.options.ro_image || this.options.image) : this.options.image;
// Silently blank for percentages instead of warning about missing image - use a progress widget
if(_image.match(/^[0-9]+\%$/))
{
_image = "";
//this.egw().debug("warn", "Use a progress widget instead of percentage images", this);
}
var found_image = false;
if(_image != "")
{
var src = this.egw().image(_image);
if(src)
{
found_image = true;
}
else if (_image[0] == '/' || _image.substr(0,4) == 'http')
{
src= image;
found_image = true;
}
if(found_image)
{
if(this.image != null)
{
this.image.attr("src", src);
}
else if (this.options.background_image && this.btn)
{
this.btn.css("background-image","url("+src+")");
this.btn.addClass('et2_button_with_image');
}
}
}
if(!found_image)
{
this.set_label(this.label);
if(this.btn)
{
this.btn.css("background-image","");
this.btn.removeClass('et2_button_with_image');
}
}
},
/**
* Set options.readonly and update image
*
* @param {boolean} _ro
*/
set_readonly: function(_ro)
{
if (_ro != this.options.readonly)
{
this.options.readonly = _ro;
if (this.options.image || this.options.ro_image)
{
this.update_image();
}
// dont show readonly buttons as clickable
if (this.btn || this.image)
{
(this.btn || this.image)
.toggleClass('et2_clickable', !_ro)
.toggleClass('et2_button_ro', _ro)
.css('cursor', _ro ? 'default' : 'pointer'); // temp. 'til it is removed from et2_button
}
}
},
attachToDOM: function() {
this._super.apply(this, arguments);
if (this.options.readonly && (this.btn || this.image))
{
(this.btn || this.image)
.removeClass('et2_clickable')
.addClass('et2_button_ro')
.css('cursor', 'default'); // temp. 'til it is removed from et2_button
}
},
getDOMNode: function() {
return this.btn ? this.btn[0] : (this.image ? this.image[0] : null);
},
/**
* Overwritten to maintain an internal clicked attribute
*
* @param _ev
* @returns {Boolean}
*/
click: function(_ev) {
// ignore click on readonly button
if (this.options.readonly) return false;
this.clicked = true;
if (!this._super.apply(this, arguments))
{
this.clicked = false;
return false;
}
// Submit the form
if (this._type != "buttononly")
{
this.getInstanceManager().submit(this, false, this.options.novalidate); //TODO: this only needs to be passed if it's in a datagrid
}
this.clicked = false;
return true;
},
set_label: function(_value) {
if (this.btn)
{
this.label = _value;
this.btn.text(_value);
if (_value && !this.image)
this.btn.addClass('et2_button_text');
else
this.btn.removeClass('et2_button_text');
}
if(this.image)
{
this.image.attr("alt", _value);
// Don't set title if there's a tooltip, browser may show both
if(!this.options.statustext)
{
this.image.attr("title",_value);
}
}
},
/**
* Set tab index
*
* @param {number} index
*/
set_tabindex: function(index) {
jQuery(this.btn).attr("tabindex", index);
},
/**
* Implementation of the et2_IInput interface
*/
/**
* Always return false as a button is never dirty
*/
isDirty: function() {
return false;
},
resetDirty: function() {
},
getValue: function() {
if (this.clicked)
{
return true;
}
// If "null" is returned, the result is not added to the submitted
// array.
return null;
},
isValid: function() {
return true;
},
/**
* et2_IDetachedDOM
*
* @param {array} _attrs
*/
getDetachedAttributes: function(_attrs)
{
_attrs.push("label", "value", "class", "image", "ro_image", "onclick", "background_image" );
},
getDetachedNodes: function()
{
return [
this.btn != null ? this.btn[0] : null,
this.image != null ? this.image[0] : null
];
},
setDetachedAttributes: function(_nodes, _values)
{
// Datagrid puts in the row for null
this.btn = _nodes[0].nodeName[0] != '#' ? jQuery(_nodes[0]) : null;
this.image = jQuery(_nodes[1]);
if (typeof _values["id"] != "undefined")
{
this.set_id(_values["id"]);
}
if (typeof _values["label"] != "undefined")
{
this.set_label(_values["label"]);
}
if (typeof _values["value"] != "undefined")
{
}
if (typeof _values["image"] != "undefined")
{
this.set_image(_values["image"]);
}
if (typeof _values["ro_image"] != "undefined")
{
this.set_ro_image(_values["ro_image"]);
}
if (typeof _values["class"] != "undefined")
{
this.set_class(_values["class"]);
}
if (typeof _values["onclick"] != "undefined")
{
this.options.onclick = _values["onclick"];
}
var type = this._type;
var attrs = jQuery.extend(_values, this.options);
var parent = this._parent;
jQuery(this.getDOMNode()).bind("click.et2_baseWidget", this, function(e) {
var widget = et2_createWidget(type,attrs,parent);
e.data = widget;
e.data.set_id(_values["id"]);
return e.data.click.call(e.data,e);
});
}
});}).call(this);
et2_register_widget(et2_button, ["button", "buttononly"]);
// Static class stuff
jQuery.extend(et2_button,
/** @lends et2_button */
{
/**
* images to be used as background-image, if none is explicitly applied and id matches given regular expression
*/
default_background_images: {
save: /save(&|\]|$)/,
apply: /apply(&|\]|$)/,
cancel: /cancel(&|\]|$)/,
delete: /delete(&|\]|$)/,
discard: /discard(&|\]|$)/,
edit: /edit(&|\[\]|$)/,
next: /(next|continue)(&|\]|$)/,
finish: /finish(&|\]|$)/,
back: /(back|previous)(&|\]|$)/,
copy: /copy(&|\]|$)/,
more: /more(&|\]|$)/,
check: /(yes|check)(&|\]|$)/,
cancelled: /no(&|\]|$)/,
ok: /ok(&|\]|$)/,
close: /close(&|\]|$)/,
add: /(add(&|\]|$)|create)/ // customfields use create*
},
/**
* Classnames added automatic to buttons to set certain hover background colors
*/
default_classes: {
et2_button_cancel: /cancel(&|\]|$)/, // yellow
et2_button_question: /(yes|no)(&|\]|$)/, // yellow
et2_button_delete: /delete(&|\]|$)/ // red
}
});
var et2_button = /** @class */ (function (_super) {
__extends(et2_button, _super);
/**
* Constructor
*/
function et2_button(_parent, _attrs, _child) {
var _this =
// Call the inherited constructor
_super.call(this, _parent, _attrs, et2_core_inheritance_1.ClassWithAttributes.extendAttributes(et2_button._attributes, _child || {})) || this;
_this.legacyOptions = ["image", "ro_image"];
_this.label = "";
_this.clicked = false;
_this.btn = null;
_this.image = null;
if (!_this.options.background_image && (_this.options.image || _this.options.ro_image)) {
_this.image = jQuery(document.createElement("img"))
.addClass("et2_button et2_button_icon");
if (!_this.options.readonly)
_this.image.addClass("et2_clickable");
_this.setDOMNode(_this.image[0]);
return _this;
}
if (!_this.options.readonly || _this.options.ro_image) {
_this.btn = jQuery(document.createElement("button"))
.addClass("et2_button")
.attr({ type: "button" });
_this.setDOMNode(_this.btn[0]);
}
if (_this.options.image)
_this.set_image(_this.options.image);
return _this;
}
/**
* Apply the "modifications" to the element and translate attributes marked
* with "translate: true"
*
* Reimplemented here to assign default background-images to buttons
*
* @param {object} _attrs
*/
et2_button.prototype.transformAttributes = function (_attrs) {
if (this.id && typeof _attrs.background_image == 'undefined' && !_attrs.image) {
for (var image in et2_button.default_background_images) {
if (this.id.match(et2_button.default_background_images[image])) {
_attrs.image = image;
_attrs.background_image = true;
break;
}
}
}
for (var name in et2_button.default_classes) {
if (this.id.match(et2_button.default_classes[name])) {
_attrs.class = (typeof _attrs.class == 'undefined' ? '' : _attrs.class + ' ') + name;
break;
}
}
_super.prototype.transformAttributes.call(this, _attrs);
};
et2_button.prototype.set_accesskey = function (key) {
jQuery(this.node).attr("accesskey", key);
};
/**
* Set image and update current image
*
* @param _image
*/
et2_button.prototype.set_image = function (_image) {
this.options.image = _image;
this.update_image();
};
/**
* Set readonly image and update current image
*
* @param _image
*/
et2_button.prototype.set_ro_image = function (_image) {
this.options.ro_image = _image;
this.update_image();
};
/**
* Set current image (dont update options.image)
*
* @param _image
*/
et2_button.prototype.update_image = function (_image) {
if (!this.isInTree() || !this.options.background_image && this.image == null)
return;
if (typeof _image == 'undefined')
_image = this.options.readonly ? (this.options.ro_image || this.options.image) : this.options.image;
// Silently blank for percentages instead of warning about missing image - use a progress widget
if (_image.match(/^[0-9]+\%$/)) {
_image = "";
//this.egw().debug("warn", "Use a progress widget instead of percentage images", this);
}
var found_image = false;
if (_image != "") {
var src = this.egw().image(_image);
if (src) {
found_image = true;
}
else if (_image[0] == '/' || _image.substr(0, 4) == 'http') {
src = _image;
found_image = true;
}
if (found_image) {
if (this.image != null) {
this.image.attr("src", src);
}
else if (this.options.background_image && this.btn) {
this.btn.css("background-image", "url(" + src + ")");
this.btn.addClass('et2_button_with_image');
}
}
}
if (!found_image) {
this.set_label(this.label);
if (this.btn) {
this.btn.css("background-image", "");
this.btn.removeClass('et2_button_with_image');
}
}
};
/**
* Set options.readonly and update image
*
* @param {boolean} _ro
*/
et2_button.prototype.set_readonly = function (_ro) {
if (_ro != this.options.readonly) {
this.options.readonly = _ro;
if (this.options.image || this.options.ro_image) {
this.update_image();
}
// dont show readonly buttons as clickable
if (this.btn || this.image) {
(this.btn || this.image)
.toggleClass('et2_clickable', !_ro)
.toggleClass('et2_button_ro', _ro)
.css('cursor', _ro ? 'default' : 'pointer'); // temp. 'til it is removed from et2_button
}
}
};
et2_button.prototype.attachToDOM = function () {
var ret = _super.prototype.attachToDOM.call(this);
if (this.options.readonly && (this.btn || this.image)) {
(this.btn || this.image)
.removeClass('et2_clickable')
.addClass('et2_button_ro')
.css('cursor', 'default'); // temp. 'til it is removed from et2_button
}
return ret;
};
et2_button.prototype.getDOMNode = function () {
return this.btn ? this.btn[0] : (this.image ? this.image[0] : null);
};
/**
* Overwritten to maintain an internal clicked attribute
*
* @param _ev
* @returns {Boolean}
*/
et2_button.prototype.click = function (_ev) {
// ignore click on readonly button
if (this.options.readonly)
return false;
this.clicked = true;
if (!_super.prototype.click.apply(this, arguments)) {
this.clicked = false;
return false;
}
// Submit the form
if (this.getType() != "buttononly") {
this.getInstanceManager().submit(this, false, this.options.novalidate); //TODO: this only needs to be passed if it's in a datagrid
}
this.clicked = false;
return true;
};
et2_button.prototype.set_label = function (_value) {
if (this.btn) {
this.label = _value;
this.btn.text(_value);
if (_value && !this.image)
this.btn.addClass('et2_button_text');
else
this.btn.removeClass('et2_button_text');
}
if (this.image) {
this.image.attr("alt", _value);
// Don't set title if there's a tooltip, browser may show both
if (!this.options.statustext) {
this.image.attr("title", _value);
}
}
};
/**
* Set tab index
*
* @param {number} index
*/
et2_button.prototype.set_tabindex = function (index) {
jQuery(this.btn).attr("tabindex", index);
};
/**
* Implementation of the et2_IInput interface
*/
/**
* Always return false as a button is never dirty
*/
et2_button.prototype.isDirty = function () {
return false;
};
et2_button.prototype.resetDirty = function () {
};
et2_button.prototype.getValue = function () {
if (this.clicked) {
return true;
}
// If "null" is returned, the result is not added to the submitted
// array.
return null;
};
et2_button.prototype.isValid = function () {
return true;
};
/**
* et2_IDetachedDOM
*
* @param {array} _attrs
*/
et2_button.prototype.getDetachedAttributes = function (_attrs) {
_attrs.push("label", "value", "class", "image", "ro_image", "onclick", "background_image");
};
et2_button.prototype.getDetachedNodes = function () {
return [
this.btn != null ? this.btn[0] : null,
this.image != null ? this.image[0] : null
];
};
et2_button.prototype.setDetachedAttributes = function (_nodes, _values) {
// Datagrid puts in the row for null
this.btn = _nodes[0].nodeName[0] != '#' ? jQuery(_nodes[0]) : null;
this.image = jQuery(_nodes[1]);
if (typeof _values["id"] != "undefined") {
this.set_id(_values["id"]);
}
if (typeof _values["label"] != "undefined") {
this.set_label(_values["label"]);
}
if (typeof _values["value"] != "undefined") {
}
if (typeof _values["image"] != "undefined") {
this.set_image(_values["image"]);
}
if (typeof _values["ro_image"] != "undefined") {
this.set_ro_image(_values["ro_image"]);
}
if (typeof _values["class"] != "undefined") {
this.set_class(_values["class"]);
}
if (typeof _values["onclick"] != "undefined") {
this.options.onclick = _values["onclick"];
}
var type = this.getType();
var attrs = jQuery.extend(_values, this.options);
var parent = this.getParent();
jQuery(this.getDOMNode()).bind("click.et2_baseWidget", this, function (e) {
var widget = et2_core_widget_1.et2_createWidget(type, attrs, parent);
e.data = widget;
e.data.set_id(_values["id"]);
return e.data.click.call(e.data, e);
});
};
et2_button._attributes = {
"label": {
"name": "caption",
"type": "string",
"description": "Label of the button",
"translate": true
},
"image": {
"name": "Icon",
"type": "string",
"description": "Use an icon instead of label (when available)"
},
"ro_image": {
"name": "Read-only Icon",
"type": "string",
"description": "Use this icon instead of hiding for read-only"
},
"onclick": {
"description": "JS code which gets executed when the button is clicked",
"type": "js"
},
"accesskey": {
"name": "Access Key",
"type": "string",
"default": et2_no_init,
"description": "Alt + <key> activates widget"
},
"tabindex": {
"name": "Tab index",
"type": "integer",
"default": et2_no_init,
"description": "Specifies the tab order of a widget when the 'tab' button is used for navigating."
},
background_image: {
name: "Add image in front of text",
type: "boolean",
description: "Adds image in front of text instead of just using an image with text as tooltip",
default: et2_no_init // to leave it undefined, if not defined, so background-image is assigned by default
},
novalidate: {
name: "Do NOT validate form",
type: "boolean",
description: "Do NOT validate form before submitting it",
default: false
},
// No such thing as a required button
"needed": {
"ignore": true
}
};
/**
* images to be used as background-image, if none is explicitly applied and id matches given regular expression
*/
et2_button.default_background_images = {
save: /save(&|\]|$)/,
apply: /apply(&|\]|$)/,
cancel: /cancel(&|\]|$)/,
delete: /delete(&|\]|$)/,
discard: /discard(&|\]|$)/,
edit: /edit(&|\[\]|$)/,
next: /(next|continue)(&|\]|$)/,
finish: /finish(&|\]|$)/,
back: /(back|previous)(&|\]|$)/,
copy: /copy(&|\]|$)/,
more: /more(&|\]|$)/,
check: /(yes|check)(&|\]|$)/,
cancelled: /no(&|\]|$)/,
ok: /ok(&|\]|$)/,
close: /close(&|\]|$)/,
add: /(add(&|\]|$)|create)/ // customfields use create*
};
/**
* Classnames added automatic to buttons to set certain hover background colors
*/
et2_button.default_classes = {
et2_button_cancel: /cancel(&|\]|$)/,
et2_button_question: /(yes|no)(&|\]|$)/,
et2_button_delete: /delete(&|\]|$)/ // red
};
return et2_button;
}(et2_core_baseWidget_1.et2_baseWidget));
exports.et2_button = et2_button;
et2_core_widget_1.et2_register_widget(et2_button, ["button", "buttononly"]);
//# sourceMappingURL=et2_widget_button.js.map

View File

@ -0,0 +1,459 @@
/**
* EGroupware eTemplate2 - JS Button object
*
* @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
/vendor/bower-asset/jquery/dist/jquery.js;
et2_core_interfaces;
et2_core_baseWidget;
*/
import './et2_core_common';
import {ClassWithAttributes} from "./et2_core_inheritance";
import {et2_createWidget, et2_register_widget, WidgetConfig} from "./et2_core_widget";
import {et2_baseWidget} from './et2_core_baseWidget'
import './et2_types';
/**
* Class which implements the "button" XET-Tag
*/
export class et2_button extends et2_baseWidget implements et2_IInput, et2_IDetachedDOM
{
static readonly _attributes : any = {
"label": {
"name": "caption",
"type": "string",
"description": "Label of the button",
"translate": true
},
"image": {
"name": "Icon",
"type": "string",
"description": "Use an icon instead of label (when available)"
},
"ro_image": {
"name": "Read-only Icon",
"type": "string",
"description": "Use this icon instead of hiding for read-only"
},
"onclick": {
"description": "JS code which gets executed when the button is clicked",
"type": "js"
},
"accesskey": {
"name": "Access Key",
"type": "string",
"default": et2_no_init,
"description": "Alt + <key> activates widget"
},
"tabindex": {
"name": "Tab index",
"type": "integer",
"default": et2_no_init,
"description": "Specifies the tab order of a widget when the 'tab' button is used for navigating."
},
background_image: {
name: "Add image in front of text",
type: "boolean",
description: "Adds image in front of text instead of just using an image with text as tooltip",
default: et2_no_init // to leave it undefined, if not defined, so background-image is assigned by default
},
novalidate: {
name: "Do NOT validate form",
type: "boolean",
description: "Do NOT validate form before submitting it",
default: false
},
// No such thing as a required button
"needed": {
"ignore": true
}
};
legacyOptions: string[] = ["image", "ro_image"];
/**
* images to be used as background-image, if none is explicitly applied and id matches given regular expression
*/
static readonly default_background_images: object = {
save: /save(&|\]|$)/,
apply: /apply(&|\]|$)/,
cancel: /cancel(&|\]|$)/,
delete: /delete(&|\]|$)/,
discard: /discard(&|\]|$)/,
edit: /edit(&|\[\]|$)/,
next: /(next|continue)(&|\]|$)/,
finish: /finish(&|\]|$)/,
back: /(back|previous)(&|\]|$)/,
copy: /copy(&|\]|$)/,
more: /more(&|\]|$)/,
check: /(yes|check)(&|\]|$)/,
cancelled: /no(&|\]|$)/,
ok: /ok(&|\]|$)/,
close: /close(&|\]|$)/,
add: /(add(&|\]|$)|create)/ // customfields use create*
};
/**
* Classnames added automatic to buttons to set certain hover background colors
*/
static readonly default_classes: object = {
et2_button_cancel: /cancel(&|\]|$)/, // yellow
et2_button_question: /(yes|no)(&|\]|$)/, // yellow
et2_button_delete: /delete(&|\]|$)/ // red
};
label: string = "";
clicked: boolean = false;
btn: JQuery = null;
image: JQuery = null;
/**
* Constructor
*/
constructor(_parent, _attrs? : WidgetConfig, _child? : object)
{
// Call the inherited constructor
super(_parent, _attrs, ClassWithAttributes.extendAttributes(et2_button._attributes, _child || {}));
if (!this.options.background_image && (this.options.image || this.options.ro_image))
{
this.image = jQuery(document.createElement("img"))
.addClass("et2_button et2_button_icon");
if (!this.options.readonly) this.image.addClass("et2_clickable");
this.setDOMNode(this.image[0]);
return;
}
if (!this.options.readonly || this.options.ro_image)
{
this.btn = jQuery(document.createElement("button"))
.addClass("et2_button")
.attr({type:"button"});
this.setDOMNode(this.btn[0]);
}
if (this.options.image) this.set_image(this.options.image);
}
/**
* Apply the "modifications" to the element and translate attributes marked
* with "translate: true"
*
* Reimplemented here to assign default background-images to buttons
*
* @param {object} _attrs
*/
transformAttributes(_attrs)
{
if (this.id && typeof _attrs.background_image == 'undefined' && !_attrs.image)
{
for(var image in et2_button.default_background_images)
{
if (this.id.match(et2_button.default_background_images[image]))
{
_attrs.image = image;
_attrs.background_image = true;
break;
}
}
}
for(var name in et2_button.default_classes)
{
if (this.id.match(et2_button.default_classes[name]))
{
_attrs.class = (typeof _attrs.class == 'undefined' ? '' : _attrs.class+' ')+name;
break;
}
}
super.transformAttributes(_attrs);
}
set_accesskey(key)
{
jQuery(this.node).attr("accesskey", key);
}
/**
* Set image and update current image
*
* @param _image
*/
set_image(_image)
{
this.options.image = _image;
this.update_image();
}
/**
* Set readonly image and update current image
*
* @param _image
*/
set_ro_image(_image)
{
this.options.ro_image = _image;
this.update_image();
}
/**
* Set current image (dont update options.image)
*
* @param _image
*/
update_image(_image?)
{
if(!this.isInTree() || !this.options.background_image && this.image == null) return;
if (typeof _image == 'undefined')
_image = this.options.readonly ? (this.options.ro_image || this.options.image) : this.options.image;
// Silently blank for percentages instead of warning about missing image - use a progress widget
if(_image.match(/^[0-9]+\%$/))
{
_image = "";
//this.egw().debug("warn", "Use a progress widget instead of percentage images", this);
}
var found_image = false;
if(_image != "")
{
var src = this.egw().image(_image);
if(src)
{
found_image = true;
}
else if (_image[0] == '/' || _image.substr(0,4) == 'http')
{
src = _image;
found_image = true;
}
if(found_image)
{
if(this.image != null)
{
this.image.attr("src", src);
}
else if (this.options.background_image && this.btn)
{
this.btn.css("background-image","url("+src+")");
this.btn.addClass('et2_button_with_image');
}
}
}
if(!found_image)
{
this.set_label(this.label);
if(this.btn)
{
this.btn.css("background-image","");
this.btn.removeClass('et2_button_with_image');
}
}
}
/**
* Set options.readonly and update image
*
* @param {boolean} _ro
*/
set_readonly(_ro)
{
if (_ro != this.options.readonly)
{
this.options.readonly = _ro;
if (this.options.image || this.options.ro_image)
{
this.update_image();
}
// dont show readonly buttons as clickable
if (this.btn || this.image)
{
(this.btn || this.image)
.toggleClass('et2_clickable', !_ro)
.toggleClass('et2_button_ro', _ro)
.css('cursor', _ro ? 'default' : 'pointer'); // temp. 'til it is removed from et2_button
}
}
}
attachToDOM()
{
let ret = super.attachToDOM();
if (this.options.readonly && (this.btn || this.image))
{
(this.btn || this.image)
.removeClass('et2_clickable')
.addClass('et2_button_ro')
.css('cursor', 'default'); // temp. 'til it is removed from et2_button
}
return ret;
}
getDOMNode()
{
return this.btn ? this.btn[0] : (this.image ? this.image[0] : null);
}
/**
* Overwritten to maintain an internal clicked attribute
*
* @param _ev
* @returns {Boolean}
*/
click(_ev)
{
// ignore click on readonly button
if (this.options.readonly) return false;
this.clicked = true;
if (!super.click.apply(this, arguments))
{
this.clicked = false;
return false;
}
// Submit the form
if (this.getType() != "buttononly")
{
this.getInstanceManager().submit(this, false, this.options.novalidate); //TODO: this only needs to be passed if it's in a datagrid
}
this.clicked = false;
return true;
}
set_label(_value)
{
if (this.btn)
{
this.label = _value;
this.btn.text(_value);
if (_value && !this.image)
this.btn.addClass('et2_button_text');
else
this.btn.removeClass('et2_button_text');
}
if(this.image)
{
this.image.attr("alt", _value);
// Don't set title if there's a tooltip, browser may show both
if(!this.options.statustext)
{
this.image.attr("title",_value);
}
}
}
/**
* Set tab index
*
* @param {number} index
*/
set_tabindex(index)
{
jQuery(this.btn).attr("tabindex", index);
}
/**
* Implementation of the et2_IInput interface
*/
/**
* Always return false as a button is never dirty
*/
isDirty()
{
return false;
}
resetDirty()
{
}
getValue()
{
if (this.clicked)
{
return true;
}
// If "null" is returned, the result is not added to the submitted
// array.
return null;
}
isValid()
{
return true;
}
/**
* et2_IDetachedDOM
*
* @param {array} _attrs
*/
getDetachedAttributes(_attrs)
{
_attrs.push("label", "value", "class", "image", "ro_image", "onclick", "background_image" );
}
getDetachedNodes()
{
return [
this.btn != null ? this.btn[0] : null,
this.image != null ? this.image[0] : null
];
}
setDetachedAttributes(_nodes, _values)
{
// Datagrid puts in the row for null
this.btn = _nodes[0].nodeName[0] != '#' ? jQuery(_nodes[0]) : null;
this.image = jQuery(_nodes[1]);
if (typeof _values["id"] != "undefined")
{
this.set_id(_values["id"]);
}
if (typeof _values["label"] != "undefined")
{
this.set_label(_values["label"]);
}
if (typeof _values["value"] != "undefined")
{
}
if (typeof _values["image"] != "undefined")
{
this.set_image(_values["image"]);
}
if (typeof _values["ro_image"] != "undefined")
{
this.set_ro_image(_values["ro_image"]);
}
if (typeof _values["class"] != "undefined")
{
this.set_class(_values["class"]);
}
if (typeof _values["onclick"] != "undefined")
{
this.options.onclick = _values["onclick"];
}
var type = this.getType();
var attrs = jQuery.extend(_values, this.options);
var parent = this.getParent();
jQuery(this.getDOMNode()).bind("click.et2_baseWidget", this, function(e) {
var widget = et2_createWidget(type,attrs,parent);
e.data = widget;
e.data.set_id(_values["id"]);
return e.data.click.call(e.data,e);
});
}
}
et2_register_widget(et2_button, ["button", "buttononly"]);

View File

@ -1,3 +1,4 @@
"use strict";
/**
* EGroupware eTemplate2 - JS Checkbox object
*
@ -9,255 +10,256 @@
* @copyright Nathan Gray 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
/vendor/bower-asset/jquery/dist/jquery.js;
et2_core_inputWidget;
et2_core_valueWidget;
/vendor/bower-asset/jquery/dist/jquery.js;
et2_core_inputWidget;
et2_core_valueWidget;
*/
var et2_core_widget_1 = require("./et2_core_widget");
var et2_core_inputWidget_1 = require("./et2_core_inputWidget");
var et2_core_inheritance_1 = require("./et2_core_inheritance");
/**
* Class which implements the "checkbox" XET-Tag
*
* @augments et2_inputWidget
*/
var et2_checkbox = (function(){ "use strict"; return et2_inputWidget.extend(
{
attributes: {
"selected_value": {
"name": "Set value",
"type": "string",
"default": "true",
"description": "Value when checked"
},
"unselected_value": {
"name": "Unset value",
"type": "string",
"default": "",
"description": "Value when not checked"
},
"ro_true": {
"name": "Read only selected",
"type": "string",
"default": "X ",
"description": "What should be displayed when readonly and selected"
},
"ro_false": {
"name": "Read only unselected",
"type": "string",
"default": "",
"description": "What should be displayed when readonly and not selected"
},
"value": {
// Stop framework from messing with value
"type": "any"
},
"toggle_on": {
"name": "Toggle on caption",
"type": "string",
"default": "",
"description": "String caption to show for ON status",
"translate": true
},
"toggle_off": {
"name": "Toggle off caption",
"type": "string",
"default": "",
"description": "String caption to show OFF status",
"translate": true
}
},
legacyOptions: ["selected_value", "unselected_value", "ro_true", "ro_false"],
/**
* Constructor
*
* @memberOf et2_checkbox
*/
init: function() {
this._super.apply(this, arguments);
this.input = null;
this.createInputWidget();
},
createInputWidget: function() {
this.input = jQuery(document.createElement("input")).attr("type", "checkbox");
this.input.addClass("et2_checkbox");
if (this.options.toggle_on || this.options.toggle_off)
{
var self = this;
// checkbox container
this.toggle = jQuery(document.createElement('span'))
.addClass('et2_checkbox_slideSwitch')
.append(this.input);
// update switch status on change
this.input.change(function(){
self.getValue();
return true;
});
// switch container
var area = jQuery(document.createElement('span')).addClass('slideSwitch_container').appendTo(this.toggle);
// on span tag
var on = jQuery(document.createElement('span')).addClass('on').appendTo(area);
// off span tag
var off = jQuery(document.createElement('span')).addClass('off').appendTo(area);
on.text(this.options.toggle_on);
off.text(this.options.toggle_off);
// handle a tag
var handle = jQuery(document.createElement('a')).appendTo(area);
this.setDOMNode(this.toggle[0]);
}
else
{
this.setDOMNode(this.input[0]);
}
},
/**
* Override default to place checkbox before label, if there is no %s in the label
*
* @param {string} label
*/
set_label: function(label) {
if(label.length && label.indexOf('%s') < 0)
{
label = '%s'+label;
}
this._super.apply(this, [label]);
jQuery(this.getSurroundings()._widgetSurroundings).addClass('et2_checkbox_label');
},
/**
* Override default to match against set/unset value
*
* @param {string|boolean} _value
*/
set_value: function(_value)
{
// in php, our database storage and et2_checkType(): "0" == false
if (_value === "0" && this.options.selected_value != "0")
{
_value = false;
}
if(_value != this.value) {
if(_value == this.options.selected_value ||
_value && this.options.selected_value == this.attributes.selected_value["default"] &&
_value != this.options.unselected_value) {
if (this.options.toggle_on || this.options.toggle_off) this.toggle.addClass('switchOn');
this.input.prop("checked", true);
} else {
this.input.prop("checked", false);
if (this.options.toggle_on || this.options.toggle_off) this.toggle.removeClass('switchOn');
}
}
},
/**
* Disable checkbox on runtime
*
* @param {boolean} _ro
*/
set_readonly: function(_ro)
{
jQuery(this.getDOMNode()).attr('disabled', _ro);
},
/**
* Override default to return unchecked value
*/
getValue: function() {
if(this.input.prop("checked")) {
if (this.options.toggle_on || this.options.toggle_off) this.toggle.addClass('switchOn');
return this.options.selected_value;
} else {
if (this.options.toggle_on || this.options.toggle_off) this.toggle.removeClass('switchOn');
return this.options.unselected_value;
}
}
});}).call(this);
et2_register_widget(et2_checkbox, ["checkbox"]);
var et2_checkbox = /** @class */ (function (_super) {
__extends(et2_checkbox, _super);
/**
* Constructor
*
* @memberOf et2_checkbox
*/
function et2_checkbox(_parent, _attrs, _child) {
var _this =
// Call the inherited constructor
_super.call(this, _parent, _attrs, et2_core_inheritance_1.ClassWithAttributes.extendAttributes(et2_checkbox._attributes, _child || {})) || this;
_this.legacyOptions = ["selected_value", "unselected_value", "ro_true", "ro_false"];
_this.input = null;
_this.toggle = null;
_this.input = null;
_this.createInputWidget();
return _this;
}
et2_checkbox.prototype.createInputWidget = function () {
this.input = jQuery(document.createElement("input")).attr("type", "checkbox");
this.input.addClass("et2_checkbox");
if (this.options.toggle_on || this.options.toggle_off) {
var self_1 = this;
// checkbox container
this.toggle = jQuery(document.createElement('span'))
.addClass('et2_checkbox_slideSwitch')
.append(this.input);
// update switch status on change
this.input.change(function () {
self_1.getValue();
return true;
});
// switch container
var area = jQuery(document.createElement('span')).addClass('slideSwitch_container').appendTo(this.toggle);
// on span tag
var on = jQuery(document.createElement('span')).addClass('on').appendTo(area);
// off span tag
var off = jQuery(document.createElement('span')).addClass('off').appendTo(area);
on.text(this.options.toggle_on);
off.text(this.options.toggle_off);
// handle a tag
jQuery(document.createElement('a')).appendTo(area);
this.setDOMNode(this.toggle[0]);
}
else {
this.setDOMNode(this.input[0]);
}
};
/**
* Override default to place checkbox before label, if there is no %s in the label
*
* @param {string} label
*/
et2_checkbox.prototype.set_label = function (label) {
if (label.length && label.indexOf('%s') < 0) {
label = '%s' + label;
}
_super.prototype.set_label.call(this, label);
jQuery(this.getSurroundings().getWidgetSurroundings()).addClass('et2_checkbox_label');
};
/**
* Override default to match against set/unset value
*
* @param {string|boolean} _value
*/
et2_checkbox.prototype.set_value = function (_value) {
// in php, our database storage and et2_checkType(): "0" == false
if (_value === "0" && this.options.selected_value != "0") {
_value = false;
}
if (_value != this.value) {
if (_value == this.options.selected_value ||
_value && this.options.selected_value == this.attributes["selected_value"]["default"] &&
_value != this.options.unselected_value) {
if (this.options.toggle_on || this.options.toggle_off)
this.toggle.addClass('switchOn');
this.input.prop("checked", true);
}
else {
this.input.prop("checked", false);
if (this.options.toggle_on || this.options.toggle_off)
this.toggle.removeClass('switchOn');
}
}
};
/**
* Disable checkbox on runtime
*
* @param {boolean} _ro
*/
et2_checkbox.prototype.set_readonly = function (_ro) {
jQuery(this.getDOMNode()).attr('disabled', _ro);
};
/**
* Override default to return unchecked value
*/
et2_checkbox.prototype.getValue = function () {
if (this.input.prop("checked")) {
if (this.options.toggle_on || this.options.toggle_off)
this.toggle.addClass('switchOn');
return this.options.selected_value;
}
else {
if (this.options.toggle_on || this.options.toggle_off)
this.toggle.removeClass('switchOn');
return this.options.unselected_value;
}
};
et2_checkbox._attributes = {
"selected_value": {
"name": "Set value",
"type": "string",
"default": "true",
"description": "Value when checked"
},
"unselected_value": {
"name": "Unset value",
"type": "string",
"default": "",
"description": "Value when not checked"
},
"ro_true": {
"name": "Read only selected",
"type": "string",
"default": "X ",
"description": "What should be displayed when readonly and selected"
},
"ro_false": {
"name": "Read only unselected",
"type": "string",
"default": "",
"description": "What should be displayed when readonly and not selected"
},
"value": {
// Stop framework from messing with value
"type": "any"
},
"toggle_on": {
"name": "Toggle on caption",
"type": "string",
"default": "",
"description": "String caption to show for ON status",
"translate": true
},
"toggle_off": {
"name": "Toggle off caption",
"type": "string",
"default": "",
"description": "String caption to show OFF status",
"translate": true
}
};
return et2_checkbox;
}(et2_core_inputWidget_1.et2_inputWidget));
et2_core_widget_1.et2_register_widget(et2_checkbox, ["checkbox"]);
/**
* et2_checkbox_ro is the dummy readonly implementation of the checkbox
* @augments et2_checkbox
*/
var et2_checkbox_ro = (function(){ "use strict"; return et2_checkbox.extend([et2_IDetachedDOM],
{
/**
* Ignore unset value
*/
attributes: {
"unselected_value": {
"ignore": true
}
},
/**
* Constructor
*
* @memberOf et2_checkbox_ro
*/
init: function() {
this._super.apply(this, arguments);
this.value = "";
this.span = jQuery(document.createElement("span"))
.addClass("et2_checkbox_ro");
this.setDOMNode(this.span[0]);
},
/**
* note: checkbox is checked if even there is a value but not only if the _value is only "true"
* it's an exceptional validation for cases that we pass non boolean values as checkbox _value
*
* @param {string|boolean} _value
*/
set_value: function(_value) {
if(_value == this.options.selected_value ||_value && this.options.selected_value == this.attributes.selected_value["default"] &&
_value != this.options.unselected_value) {
this.span.text(this.options.ro_true);
this.value = _value;
} else {
this.span.text(this.options.ro_false);
}
},
/**
* Code for implementing et2_IDetachedDOM
*
* @param {array} _attrs
*/
getDetachedAttributes: function(_attrs)
{
_attrs.push("value", "class");
},
getDetachedNodes: function()
{
return [this.span[0]];
},
setDetachedAttributes: function(_nodes, _values)
{
// Update the properties
if (typeof _values["value"] != "undefined")
{
this.span = jQuery(_nodes[0]);
this.set_value(_values["value"]);
}
if (typeof _values["class"] != "undefined")
{
_nodes[0].setAttribute("class", _values["class"]);
}
}
});}).call(this);
et2_register_widget(et2_checkbox_ro, ["checkbox_ro"]);
* et2_checkbox_ro is the dummy readonly implementation of the checkbox
* @augments et2_checkbox
*/
var et2_checkbox_ro = /** @class */ (function (_super) {
__extends(et2_checkbox_ro, _super);
/**
* Constructor
*
* @memberOf et2_checkbox_ro
*/
function et2_checkbox_ro(_parent, _attrs, _child) {
var _this =
// Call the inherited constructor
_super.call(this, _parent, _attrs, et2_core_inheritance_1.ClassWithAttributes.extendAttributes(et2_checkbox_ro._attributes, _child || {})) || this;
_this.span = null;
_this.value = "";
_this.span = jQuery(document.createElement("span"))
.addClass("et2_checkbox_ro");
_this.setDOMNode(_this.span[0]);
return _this;
}
/**
* note: checkbox is checked if even there is a value but not only if the _value is only "true"
* it's an exceptional validation for cases that we pass non boolean values as checkbox _value
*
* @param {string|boolean} _value
*/
et2_checkbox_ro.prototype.set_value = function (_value) {
if (_value == this.options.selected_value || _value && this.options.selected_value == this.attributes["selected_value"]["default"] &&
_value != this.options.unselected_value) {
this.span.text(this.options.ro_true);
this.value = _value;
}
else {
this.span.text(this.options.ro_false);
}
};
/**
* Code for implementing et2_IDetachedDOM
*
* @param {array} _attrs
*/
et2_checkbox_ro.prototype.getDetachedAttributes = function (_attrs) {
_attrs.push("value", "class");
};
et2_checkbox_ro.prototype.getDetachedNodes = function () {
return [this.span[0]];
};
et2_checkbox_ro.prototype.setDetachedAttributes = function (_nodes, _values) {
// Update the properties
if (typeof _values["value"] != "undefined") {
this.span = jQuery(_nodes[0]);
this.set_value(_values["value"]);
}
if (typeof _values["class"] != "undefined") {
_nodes[0].setAttribute("class", _values["class"]);
}
};
/**
* Ignore unset value
*/
et2_checkbox_ro._attributes = {
"unselected_value": {
"ignore": true
}
};
return et2_checkbox_ro;
}(et2_checkbox));
et2_core_widget_1.et2_register_widget(et2_checkbox_ro, ["checkbox_ro"]);
//# sourceMappingURL=et2_widget_checkbox.js.map

View File

@ -0,0 +1,276 @@
/**
* EGroupware eTemplate2 - JS Checkbox object
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage api
* @link http://www.egroupware.org
* @author Nathan Gray
* @copyright Nathan Gray 2011
* @version $Id$
*/
/*egw:uses
/vendor/bower-asset/jquery/dist/jquery.js;
et2_core_inputWidget;
et2_core_valueWidget;
*/
import {et2_register_widget, WidgetConfig} from "./et2_core_widget";
import {et2_inputWidget} from "./et2_core_inputWidget";
import {ClassWithAttributes} from "./et2_core_inheritance";
/**
* Class which implements the "checkbox" XET-Tag
*
* @augments et2_inputWidget
*/
class et2_checkbox extends et2_inputWidget
{
static readonly _attributes : any = {
"selected_value": {
"name": "Set value",
"type": "string",
"default": "true",
"description": "Value when checked"
},
"unselected_value": {
"name": "Unset value",
"type": "string",
"default": "",
"description": "Value when not checked"
},
"ro_true": {
"name": "Read only selected",
"type": "string",
"default": "X ",
"description": "What should be displayed when readonly and selected"
},
"ro_false": {
"name": "Read only unselected",
"type": "string",
"default": "",
"description": "What should be displayed when readonly and not selected"
},
"value": {
// Stop framework from messing with value
"type": "any"
},
"toggle_on": {
"name": "Toggle on caption",
"type": "string",
"default": "",
"description": "String caption to show for ON status",
"translate": true
},
"toggle_off": {
"name": "Toggle off caption",
"type": "string",
"default": "",
"description": "String caption to show OFF status",
"translate": true
}
};
legacyOptions : string[] = ["selected_value", "unselected_value", "ro_true", "ro_false"];
input : JQuery = null;
toggle : JQuery = null;
value : string | boolean;
/**
* Constructor
*
* @memberOf et2_checkbox
*/
constructor(_parent, _attrs? : WidgetConfig, _child? : object)
{
// Call the inherited constructor
super(_parent, _attrs, ClassWithAttributes.extendAttributes(et2_checkbox._attributes, _child || {}));
this.input = null;
this.createInputWidget();
}
createInputWidget()
{
this.input = jQuery(document.createElement("input")).attr("type", "checkbox");
this.input.addClass("et2_checkbox");
if (this.options.toggle_on || this.options.toggle_off)
{
let self = this;
// checkbox container
this.toggle = jQuery(document.createElement('span'))
.addClass('et2_checkbox_slideSwitch')
.append(this.input);
// update switch status on change
this.input.change(function(){
self.getValue();
return true;
});
// switch container
let area = jQuery(document.createElement('span')).addClass('slideSwitch_container').appendTo(this.toggle);
// on span tag
let on = jQuery(document.createElement('span')).addClass('on').appendTo(area);
// off span tag
let off = jQuery(document.createElement('span')).addClass('off').appendTo(area);
on.text(this.options.toggle_on);
off.text(this.options.toggle_off);
// handle a tag
jQuery(document.createElement('a')).appendTo(area);
this.setDOMNode(this.toggle[0]);
}
else
{
this.setDOMNode(this.input[0]);
}
}
/**
* Override default to place checkbox before label, if there is no %s in the label
*
* @param {string} label
*/
set_label(label) {
if(label.length && label.indexOf('%s') < 0)
{
label = '%s'+label;
}
super.set_label(label);
jQuery(this.getSurroundings().getWidgetSurroundings()).addClass('et2_checkbox_label');
}
/**
* Override default to match against set/unset value
*
* @param {string|boolean} _value
*/
set_value(_value : string | boolean)
{
// in php, our database storage and et2_checkType(): "0" == false
if (_value === "0" && this.options.selected_value != "0")
{
_value = false;
}
if(_value != this.value) {
if(_value == this.options.selected_value ||
_value && this.options.selected_value == this.attributes["selected_value"]["default"] &&
_value != this.options.unselected_value) {
if (this.options.toggle_on || this.options.toggle_off) this.toggle.addClass('switchOn');
this.input.prop("checked", true);
} else {
this.input.prop("checked", false);
if (this.options.toggle_on || this.options.toggle_off) this.toggle.removeClass('switchOn');
}
}
}
/**
* Disable checkbox on runtime
*
* @param {boolean} _ro
*/
set_readonly(_ro)
{
jQuery(this.getDOMNode()).attr('disabled', _ro);
}
/**
* Override default to return unchecked value
*/
getValue()
{
if(this.input.prop("checked")) {
if (this.options.toggle_on || this.options.toggle_off) this.toggle.addClass('switchOn');
return this.options.selected_value;
} else {
if (this.options.toggle_on || this.options.toggle_off) this.toggle.removeClass('switchOn');
return this.options.unselected_value;
}
}
}
et2_register_widget(et2_checkbox, ["checkbox"]);
/**
* et2_checkbox_ro is the dummy readonly implementation of the checkbox
* @augments et2_checkbox
*/
class et2_checkbox_ro extends et2_checkbox implements et2_IDetachedDOM
{
/**
* Ignore unset value
*/
static readonly _attributes : any = {
"unselected_value": {
"ignore": true
}
};
span : JQuery = null;
/**
* Constructor
*
* @memberOf et2_checkbox_ro
*/
constructor(_parent, _attrs? : WidgetConfig, _child? : object)
{
// Call the inherited constructor
super(_parent, _attrs, ClassWithAttributes.extendAttributes(et2_checkbox_ro._attributes, _child || {}));
this.value = "";
this.span = jQuery(document.createElement("span"))
.addClass("et2_checkbox_ro");
this.setDOMNode(this.span[0]);
}
/**
* note: checkbox is checked if even there is a value but not only if the _value is only "true"
* it's an exceptional validation for cases that we pass non boolean values as checkbox _value
*
* @param {string|boolean} _value
*/
set_value(_value)
{
if(_value == this.options.selected_value ||_value && this.options.selected_value == this.attributes["selected_value"]["default"] &&
_value != this.options.unselected_value) {
this.span.text(this.options.ro_true);
this.value = _value;
} else {
this.span.text(this.options.ro_false);
}
}
/**
* Code for implementing et2_IDetachedDOM
*
* @param {array} _attrs
*/
getDetachedAttributes(_attrs)
{
_attrs.push("value", "class");
}
getDetachedNodes()
{
return [this.span[0]];
}
setDetachedAttributes(_nodes, _values)
{
// Update the properties
if (typeof _values["value"] != "undefined")
{
this.span = jQuery(_nodes[0]);
this.set_value(_values["value"]);
}
if (typeof _values["class"] != "undefined")
{
_nodes[0].setAttribute("class", _values["class"]);
}
}
}
et2_register_widget(et2_checkbox_ro, ["checkbox_ro"]);

View File

@ -1,3 +1,4 @@
"use strict";
/**
* EGroupware eTemplate2 - JS Color picker object
*
@ -9,109 +10,111 @@
* @copyright Nathan Gray 2012
* @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
/vendor/bower-asset/jquery/dist/jquery.js;
et2_core_inputWidget;
et2_core_valueWidget;
/vendor/bower-asset/jquery/dist/jquery.js;
et2_core_inputWidget;
et2_core_valueWidget;
*/
var et2_core_widget_1 = require("./et2_core_widget");
var et2_core_valueWidget_1 = require("./et2_core_valueWidget");
var et2_core_inputWidget_1 = require("./et2_core_inputWidget");
var et2_core_inheritance_1 = require("./et2_core_inheritance");
/**
* Class which implements the "colorpicker" XET-Tag
*
* @augments et2_inputWidget
*/
var et2_color = (function(){ "use strict"; return et2_inputWidget.extend(
{
attributes: {
},
/**
* Constructor
*
* @memberOf et2_color
*/
init: function() {
this._super.apply(this, arguments);
// included via etemplate2.css
//this.egw().includeCSS("phpgwapi/js/jquery/jpicker/css/jPicker-1.1.6.min.css");
this.input = this.$node = jQuery("<input type='color' class='et2_color'/>");
this.setDOMNode(this.$node[0]);
this.set_value(this.options.value);
},
getValue: function() {
var value = this.$node.val();
if(value === '#FFFFFF' || value === '#ffffff')
{
return '';
}
return value;
},
set_value: function(color) {
if(!color)
{
color = '#ffffff';
}
this.$node.val(color);
}
});}).call(this);
et2_register_widget(et2_color, ["colorpicker"]);
var et2_color = /** @class */ (function (_super) {
__extends(et2_color, _super);
/**
* Constructor
*/
function et2_color(_parent, _attrs, _child) {
var _this =
// Call the inherited constructor
_super.call(this, _parent, _attrs, et2_core_inheritance_1.ClassWithAttributes.extendAttributes(et2_color._attributes, _child || {})) || this;
// included via etemplate2.css
//this.egw().includeCSS("phpgwapi/js/jquery/jpicker/css/jPicker-1.1.6.min.css");
_this.input = jQuery("<input type='color' class='et2_color'/>");
_this.setDOMNode(_this.input[0]);
return _this;
}
et2_color.prototype.getValue = function () {
var value = this.input.val();
if (value === '#FFFFFF' || value === '#ffffff') {
return '';
}
return value;
};
et2_color.prototype.set_value = function (color) {
if (!color) {
color = '#ffffff';
}
this.input.val(color);
};
return et2_color;
}(et2_core_inputWidget_1.et2_inputWidget));
exports.et2_color = et2_color;
et2_core_widget_1.et2_register_widget(et2_color, ["colorpicker"]);
/**
* et2_textbox_ro is the dummy readonly implementation of the textbox.
* @augments et2_valueWidget
*/
var et2_color_ro = (function(){ "use strict"; return et2_valueWidget.extend([et2_IDetachedDOM],
{
/**
* Constructor
*
* @memberOf et2_color_ro
*/
init: function() {
this._super.apply(this, arguments);
this.value = "";
this.$node = jQuery(document.createElement("div"))
.addClass("et2_color");
this.setDOMNode(this.$node[0]);
},
set_value: function(_value) {
this.value = _value;
if(!_value) _value = "inherit";
this.$node.css("background-color", _value);
},
/**
* Code for implementing et2_IDetachedDOM
*
* @param {array} _attrs array to add further attributes to
*/
getDetachedAttributes: function(_attrs)
{
_attrs.push("value");
},
getDetachedNodes: function()
{
return [this.node];
},
setDetachedAttributes: function(_nodes, _values)
{
this.$node = jQuery(_nodes[0]);
if(typeof _values["value"] != 'undefined')
{
this.set_value(_values["value"]);
}
}
});}).call(this);
et2_register_widget(et2_color_ro, ["colorpicker_ro"]);
var et2_color_ro = /** @class */ (function (_super) {
__extends(et2_color_ro, _super);
/**
* Constructor
*
* @memberOf et2_color_ro
*/
function et2_color_ro(_parent, _attrs, _child) {
var _this =
// Call the inherited constructor
_super.call(this, _parent, _attrs, _child || {}) || this;
_this.value = "";
_this.$node = jQuery(document.createElement("div"))
.addClass("et2_color");
_this.setDOMNode(_this.$node[0]);
return _this;
}
et2_color_ro.prototype.set_value = function (_value) {
this.value = _value;
if (!_value)
_value = "inherit";
this.$node.css("background-color", _value);
};
/**
* Code for implementing et2_IDetachedDOM
*
* @param {array} _attrs array to add further attributes to
*/
et2_color_ro.prototype.getDetachedAttributes = function (_attrs) {
_attrs.push("value");
};
et2_color_ro.prototype.getDetachedNodes = function () {
return [this.node];
};
et2_color_ro.prototype.setDetachedAttributes = function (_nodes, _values) {
this.$node = jQuery(_nodes[0]);
if (typeof _values["value"] != 'undefined') {
this.set_value(_values["value"]);
}
};
return et2_color_ro;
}(et2_core_valueWidget_1.et2_valueWidget));
exports.et2_color_ro = et2_color_ro;
et2_core_widget_1.et2_register_widget(et2_color_ro, ["colorpicker_ro"]);
//# sourceMappingURL=et2_widget_color.js.map

View File

@ -0,0 +1,123 @@
/**
* EGroupware eTemplate2 - JS Color picker object
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage api
* @link http://www.egroupware.org
* @author Nathan Gray
* @copyright Nathan Gray 2012
* @version $Id$
*/
/*egw:uses
/vendor/bower-asset/jquery/dist/jquery.js;
et2_core_inputWidget;
et2_core_valueWidget;
*/
import {et2_register_widget, WidgetConfig} from "./et2_core_widget";
import {et2_valueWidget} from "./et2_core_valueWidget";
import {et2_inputWidget} from "./et2_core_inputWidget";
import {ClassWithAttributes} from "./et2_core_inheritance";
/**
* Class which implements the "colorpicker" XET-Tag
*
*/
export class et2_color extends et2_inputWidget
{
private input: JQuery;
/**
* Constructor
*/
constructor(_parent, _attrs? : WidgetConfig, _child? : object)
{
// Call the inherited constructor
super(_parent, _attrs, ClassWithAttributes.extendAttributes(et2_color._attributes, _child || {}));
// included via etemplate2.css
//this.egw().includeCSS("phpgwapi/js/jquery/jpicker/css/jPicker-1.1.6.min.css");
this.input = jQuery("<input type='color' class='et2_color'/>");
this.setDOMNode(this.input[0]);
}
getValue( )
{
var value = this.input.val();
if(value === '#FFFFFF' || value === '#ffffff')
{
return '';
}
return value;
}
set_value( color)
{
if(!color)
{
color = '#ffffff';
}
this.input.val(color);
}
}
et2_register_widget(et2_color, ["colorpicker"]);
/**
* et2_textbox_ro is the dummy readonly implementation of the textbox.
* @augments et2_valueWidget
*/
export class et2_color_ro extends et2_valueWidget implements et2_IDetachedDOM
{
private value: string;
private $node: JQuery;
/**
* Constructor
*
* @memberOf et2_color_ro
*/
constructor(_parent, _attrs? : WidgetConfig, _child? : object)
{
// Call the inherited constructor
super(_parent, _attrs, _child || {});
this.value = "";
this.$node = jQuery(document.createElement("div"))
.addClass("et2_color");
this.setDOMNode(this.$node[0]);
}
set_value( _value)
{
this.value = _value;
if(!_value) _value = "inherit";
this.$node.css("background-color", _value);
}
/**
* Code for implementing et2_IDetachedDOM
*
* @param {array} _attrs array to add further attributes to
*/
getDetachedAttributes(_attrs)
{
_attrs.push("value");
}
getDetachedNodes()
{
return [this.node];
}
setDetachedAttributes(_nodes, _values)
{
this.$node = jQuery(_nodes[0]);
if(typeof _values["value"] != 'undefined')
{
this.set_value(_values["value"]);
}
}
}
et2_register_widget(et2_color_ro, ["colorpicker_ro"]);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,4 @@
"use strict";
/**
* EGroupware eTemplate2 - JS Description object
*
@ -6,410 +7,360 @@
* @subpackage api
* @link http://www.egroupware.org
* @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 __());
};
})();
var _a;
Object.defineProperty(exports, "__esModule", { value: true });
/*egw:uses
/vendor/bower-asset/jquery/dist/jquery.js;
et2_core_baseWidget;
expose;
/vendor/bower-asset/jquery/dist/jquery.js;
et2_core_baseWidget;
expose;
*/
require("./et2_core_common");
var et2_core_inheritance_1 = require("./et2_core_inheritance");
var et2_core_widget_1 = require("./et2_core_widget");
var et2_core_baseWidget_1 = require("./et2_core_baseWidget");
require("./et2_types");
/**
* Class which implements the "description" XET-Tag
*
* @augments et2_baseWidget
*/
var et2_description = (function(){ "use strict"; return expose(et2_baseWidget.extend([et2_IDetachedDOM],
{
attributes: {
"label": {
"name": "Label",
"default": "",
"type": "string",
"description": "The label is displayed by default in front (for radiobuttons behind) each widget (if not empty). If you want to specify a different position, use a '%s' in the label, which gets replaced by the widget itself. Eg. '%s Name' to have the label Name behind a checkbox. The label can contain variables, as descript for name. If the label starts with a '@' it is replaced by the value of the content-array at this index (with the '@'-removed and after expanding the variables).",
"translate": true
},
"value": {
"name": "Value",
"type": "string",
"description": "Displayed text",
"translate": "!no_lang",
"default": ""
},
/**
* Options converted from the "options"-attribute.
*/
"font_style": {
"name": "Font Style",
"type": "string",
"description": "Style may be a compositum of \"b\" and \"i\" which " +
" renders the text bold and/or italic."
},
"href": {
"name": "Link URL",
"type": "string",
"description": "Link URL, empty if you don't wan't to display a link."
},
"activate_links": {
"name": "Replace URLs",
"type": "boolean",
"default": false,
"description": "If set, URLs in the text are automatically replaced " +
"by links"
},
"for": {
"name": "Label for widget",
"type": "string",
"description": "Marks the text as label for the given widget."
},
"extra_link_target": {
"name": "Link target",
"type": "string",
"default": "_browser",
"description": "Link target for href attribute"
},
"extra_link_popup": {
"name": "Popup",
"type": "string",
"description": "widthxheight, if popup should be used, eg. 640x480"
},
"expose_view":{
name: "Expose view",
type: "boolean",
default: false,
description: "Clicking on description with href value would popup an expose view, and will show content referenced by href."
},
mime:{
name: "Mime type",
type: "string",
default: '',
description: "Mime type of the registered link"
},
mime_data:{
name: "Mime data",
type: "string",
default: '',
description: "hash for data stored on service-side with egw_link::(get|set)_data()"
},
hover_action: {
"name": "hover action",
"type": "js",
"default": et2_no_init,
"description": "JS code which is executed when clicking on action button. This action is explicitly for attached nodes, like in nm."
},
hover_action_title: {
"name": "hover action title",
"type": "string",
"default": "Edit",
"description": "Text to show as tooltip of defined action"
}
},
legacyOptions: ["font_style", "href", "activate_links", "for",
"extra_link_target", "extra_link_popup", "statustext"],
/**
* Constructor
*
* @memberOf et2_description
*/
init: function() {
this._super.apply(this, arguments);
// Create the span/label tag which contains the label text
this.span = jQuery(document.createElement(this.options["for"] ? "label" : "span"))
.addClass("et2_label");
et2_insertLinkText(this._parseText(this.options.value), this.span[0],
this.options.href ? this.options.extra_link_target : '_blank');
this.setDOMNode(this.span[0]);
},
transformAttributes: function(_attrs) {
this._super.apply(this, arguments);
if (this.id)
{
var val = this.getArrayMgr("content").getEntry(this.id);
if (val)
{
_attrs["value"] = val;
}
}
},
doLoadingFinished: function() {
this._super.apply(this, arguments);
// Get the real id of the 'for' widget
var for_widget = null;
if (this.options["for"] && (
(for_widget = this.getParent().getWidgetById(this.options.for)) ||
(for_widget = this.getRoot().getWidgetById(this.options.for))
) && for_widget && for_widget.id)
{
if(for_widget.dom_id)
{
this.span.attr("for", for_widget.dom_id);
}
else
{
// Target widget is not done yet, need to wait
var tab_deferred = jQuery.Deferred();
window.setTimeout(function() {
this.span.attr("for", for_widget.dom_id);
tab_deferred.resolve();
}.bind(this),0);
return tab_deferred.promise();
}
}
return true;
},
set_label: function(_value) {
// Abort if ther was no change in the label
if (_value == this.label)
{
return;
}
if (_value)
{
// Create the label container if it didn't exist yet
if (this._labelContainer == null)
{
this._labelContainer = jQuery(document.createElement("label"))
.addClass("et2_label");
this.getSurroundings().insertDOMNode(this._labelContainer[0]);
}
// Clear the label container.
this._labelContainer.empty();
// Create the placeholder element and set it
var ph = document.createElement("span");
this.getSurroundings().setWidgetPlaceholder(ph);
// Split the label at the "%s"
var parts = et2_csvSplit(_value, 2, "%s");
// Update the content of the label container
for (var i = 0; i < parts.length; i++)
{
if (parts[i])
{
this._labelContainer.append(document.createTextNode(parts[i]));
}
if (i == 0)
{
this._labelContainer.append(ph);
}
}
// add class if label is empty
this._labelContainer.toggleClass('et2_label_empty', !_value || !parts[0]);
}
else
{
// Delete the labelContainer from the surroundings object
if (this._labelContainer)
{
this.getSurroundings().removeDOMNode(this._labelContainer[0]);
}
this._labelContainer = null;
}
// Update the surroundings in order to reflect the change in the label
this.getSurroundings().update();
// Copy the given value
this.label = _value;
},
/**
* Function to get media content to feed the expose
* @param {type} _value
* @returns {Array|Array.getMedia.mediaContent}
*/
getMedia: function (_value)
{
var base_url = egw.webserverUrl.match(/^\//,'ig')?egw(window).window.location.origin :'';
var mediaContent = [];
if (_value)
{
mediaContent = [{
title: this.options.label,
href: base_url + _value,
type: this.options.type + "/*",
thumbnail: base_url + _value
}];
if (_value.match(/\/webdav.php/,'ig')) mediaContent[0]["download_href"] = base_url + _value + '?download';
}
return mediaContent;
},
set_value: function(_value) {
if (!_value) _value = "";
if (!this.options.no_lang) _value = this.egw().lang(_value);
if (this.options.value && (this.options.value+"").indexOf('%s') != -1)
{
_value = this.options.value.replace(/%s/g, _value);
}
et2_insertLinkText(this._parseText(_value),
this.span[0],
this.options.href ? this.options.extra_link_target : '_blank'
);
// Add hover action button (Edit)
if (this.options.hover_action)
{
this._build_hover_action();
}
if(this.options.extra_link_popup || this.options.mime)
{
var href = this.options.href;
var mime_data = this.options.mime_data;
var self= this;
var $span = this.options.mime_data? jQuery(this.span): jQuery('a',this.span);
$span.click(function(e) {
if (self.options.expose_view && typeof self.options.mime !='undefined' && self.options.mime.match(self.mime_regexp,'ig'))
{
self._init_blueimp_gallery(e, href);
}
else
{
egw(window).open_link(mime_data || href, self.options.extra_link_target, self.options.extra_link_popup, null, null, self.options.mime);
}
e.preventDefault();
return false;
});
}
},
_parseText: function(_value) {
if (this.options.href)
{
var href = this.options.href;
if (href.indexOf('/')==-1 && href.split('.').length >= 3 &&
!(href.indexOf('mailto:')!=-1 || href.indexOf('://') != -1 || href.indexOf('javascript:') != -1)
)
{
href = "/index.php?menuaction="+href;
}
if (href.charAt(0) == '/') // link relative to eGW
{
href = egw.link(href);
}
return [{
"href": href,
"text": _value
}];
}
else if (this.options.activate_links)
{
return et2_activateLinks(_value);
}
else
{
return [_value];
}
},
set_font_style: function(_value) {
this.font_style = _value;
this.span.toggleClass("et2_bold", _value.indexOf("b") >= 0);
this.span.toggleClass("et2_italic", _value.indexOf("i") >= 0);
},
/**
* Code for implementing et2_IDetachedDOM
*
* @param {array} _attrs
*/
getDetachedAttributes: function(_attrs)
{
_attrs.push("value", "class", "href");
},
getDetachedNodes: function()
{
return [this.span[0]];
},
setDetachedAttributes: function(_nodes, _values, _data)
{
// Update the properties
var updateLink = false;
if (typeof _values["href"] != "undefined")
{
updateLink = true;
this.options.href = _values["href"];
}
if (typeof _values["value"] != "undefined" || (updateLink && (_values["value"] || this.options.value)))
{
this.span = jQuery(_nodes[0]);
this.set_value(_values["value"]);
}
if (typeof _values["class"] != "undefined")
{
_nodes[0].setAttribute("class", _values["class"]);
}
// Add hover action button (Edit), _data is nm's row data
if (this.options.hover_action)
{
this._build_hover_action(_data);
}
},
/**
* Builds button for hover action
* @param {object} _data
*/
_build_hover_action: function(_data)
{
var content = _data && _data.content ? _data.content: undefined;
var widget = this;
this.span.off().on('mouseenter', jQuery.proxy(function(event) {
event.stopImmediatePropagation();
var self = this;
this.span.tooltip({
items: 'span.et2_label',
position: {my:"right top", at:"left top", collision:"flipfit"},
tooltipClass: "et2_email_popup",
content: function()
{
return jQuery('<a href="#" class= "et2_url_email_contactPlus" title="'+egw.lang(widget.options.hover_action_title)+'"><img src="'
+egw.image("edit") +'"/></a>')
.on('click', function() {
widget.options.hover_action.call(self, self.widget, content);
});
},
close: function( event, ui )
{
ui.tooltip.hover(
function () {
jQuery(this).stop(true).fadeTo(400, 1);
},
function () {
jQuery(this).fadeOut("400", function(){ jQuery(this).remove();});
}
);
}
})
.tooltip("open");
}, {widget: this, span: this.span}));
}
}));}).call(this);
et2_register_widget(et2_description, ["description", "label"]);
var et2_description = /** @class */ (function (_super) {
__extends(et2_description, _super);
function et2_description() {
return _super !== null && _super.apply(this, arguments) || this;
}
return et2_description;
}(expose((_a = /** @class */ (function (_super) {
__extends(et2_description, _super);
/**
* Constructor
*/
function et2_description(_parent, _attrs, _child) {
var _this =
// Call the inherited constructor
_super.call(this, _parent, _attrs, et2_core_inheritance_1.ClassWithAttributes.extendAttributes(et2_description._attributes, _child || {})) || this;
_this.legacyOptions = ["font_style", "href", "activate_links", "for",
"extra_link_target", "extra_link_popup", "statustext"];
_this._labelContainer = null;
// Create the span/label tag which contains the label text
_this.span = jQuery(document.createElement(_this.options["for"] ? "label" : "span"))
.addClass("et2_label");
et2_insertLinkText(_this._parseText(_this.options.value), _this.span[0], _this.options.href ? _this.options.extra_link_target : '_blank');
_this.setDOMNode(_this.span[0]);
return _this;
}
et2_description.prototype.transformAttributes = function (_attrs) {
_super.prototype.transformAttributes.call(this, _attrs);
if (this.id) {
var val = this.getArrayMgr("content").getEntry(this.id);
if (val) {
_attrs["value"] = val;
}
}
};
et2_description.prototype.doLoadingFinished = function () {
_super.prototype.doLoadingFinished.call(this);
// Get the real id of the 'for' widget
var for_widget = null;
if (this.options["for"] && ((for_widget = this.getParent().getWidgetById(this.options.for)) ||
(for_widget = this.getRoot().getWidgetById(this.options.for))) && for_widget && for_widget.id) {
if (for_widget.dom_id) {
this.span.attr("for", for_widget.dom_id);
}
else {
// Target widget is not done yet, need to wait
var tab_deferred = jQuery.Deferred();
window.setTimeout(function () {
this.span.attr("for", for_widget.dom_id);
tab_deferred.resolve();
}.bind(this), 0);
return tab_deferred.promise();
}
}
return true;
};
et2_description.prototype.set_label = function (_value) {
// Abort if ther was no change in the label
if (_value == this.label) {
return;
}
if (_value) {
// Create the label container if it didn't exist yet
if (this._labelContainer == null) {
this._labelContainer = jQuery(document.createElement("label"))
.addClass("et2_label");
this.getSurroundings().insertDOMNode(this._labelContainer[0]);
}
// Clear the label container.
this._labelContainer.empty();
// Create the placeholder element and set it
var ph = document.createElement("span");
this.getSurroundings().setWidgetPlaceholder(ph);
// Split the label at the "%s"
var parts = et2_csvSplit(_value, 2, "%s");
// Update the content of the label container
for (var i = 0; i < parts.length; i++) {
if (parts[i]) {
this._labelContainer.append(document.createTextNode(parts[i]));
}
if (i == 0) {
this._labelContainer.append(ph);
}
}
// add class if label is empty
this._labelContainer.toggleClass('et2_label_empty', !_value || !parts[0]);
}
else {
// Delete the labelContainer from the surroundings object
if (this._labelContainer) {
this.getSurroundings().removeDOMNode(this._labelContainer[0]);
}
this._labelContainer = null;
}
// Update the surroundings in order to reflect the change in the label
this.getSurroundings().update();
// Copy the given value
this.label = _value;
};
/**
* Function to get media content to feed the expose
* @param {type} _value
* @returns {Array|Array.getMedia.mediaContent}
*/
et2_description.prototype.getMedia = function (_value) {
var base_url = egw.webserverUrl.match(new RegExp(/^\//, 'ig')) ? egw(window).window.location.origin : '';
var mediaContent = [];
if (_value) {
mediaContent = [{
title: this.options.label,
href: base_url + _value,
type: this.options.type + "/*",
thumbnail: base_url + _value
}];
if (_value.match(/\/webdav.php/, 'ig'))
mediaContent[0]["download_href"] = base_url + _value + '?download';
}
return mediaContent;
};
et2_description.prototype.set_value = function (_value) {
if (!_value)
_value = "";
if (!this.options.no_lang)
_value = this.egw().lang(_value);
if (this.options.value && (this.options.value + "").indexOf('%s') != -1) {
_value = this.options.value.replace(/%s/g, _value);
}
et2_insertLinkText(this._parseText(_value), this.span[0], this.options.href ? this.options.extra_link_target : '_blank');
// Add hover action button (Edit)
if (this.options.hover_action) {
this._build_hover_action();
}
if (this.options.extra_link_popup || this.options.mime) {
var href = this.options.href;
var mime_data = this.options.mime_data;
var self = this;
var $span = this.options.mime_data ? jQuery(this.span) : jQuery('a', this.span);
$span.click(function (e) {
if (self.options.expose_view && typeof self.options.mime != 'undefined' && self.options.mime.match(self.mime_regexp, 'ig')) {
self._init_blueimp_gallery(e, href);
}
else {
egw(window).open_link(mime_data || href, self.options.extra_link_target, self.options.extra_link_popup, null, null, self.options.mime);
}
e.preventDefault();
return false;
});
}
};
et2_description.prototype._parseText = function (_value) {
if (this.options.href) {
var href = this.options.href;
if (href.indexOf('/') == -1 && href.split('.').length >= 3 &&
!(href.indexOf('mailto:') != -1 || href.indexOf('://') != -1 || href.indexOf('javascript:') != -1)) {
href = "/index.php?menuaction=" + href;
}
if (href.charAt(0) == '/') // link relative to eGW
{
href = egw.link(href);
}
return [{
"href": href,
"text": _value
}];
}
else if (this.options.activate_links) {
return et2_activateLinks(_value);
}
else {
return [_value];
}
};
et2_description.prototype.set_font_style = function (_value) {
this.font_style = _value;
this.span.toggleClass("et2_bold", _value.indexOf("b") >= 0);
this.span.toggleClass("et2_italic", _value.indexOf("i") >= 0);
};
/**
* Code for implementing et2_IDetachedDOM
*
* @param {array} _attrs
*/
et2_description.prototype.getDetachedAttributes = function (_attrs) {
_attrs.push("value", "class", "href");
};
et2_description.prototype.getDetachedNodes = function () {
return [this.span[0]];
};
et2_description.prototype.setDetachedAttributes = function (_nodes, _values, _data) {
// Update the properties
var updateLink = false;
if (typeof _values["href"] != "undefined") {
updateLink = true;
this.options.href = _values["href"];
}
if (typeof _values["value"] != "undefined" || (updateLink && (_values["value"] || this.options.value))) {
this.span = jQuery(_nodes[0]);
this.set_value(_values["value"]);
}
if (typeof _values["class"] != "undefined") {
_nodes[0].setAttribute("class", _values["class"]);
}
// Add hover action button (Edit), _data is nm's row data
if (this.options.hover_action) {
this._build_hover_action(_data);
}
};
/**
* Builds button for hover action
* @param {object} _data
*/
et2_description.prototype._build_hover_action = function (_data) {
var content = _data && _data.content ? _data.content : undefined;
var widget = this;
this.span.off().on('mouseenter', jQuery.proxy(function (event) {
event.stopImmediatePropagation();
var self = this;
this.span.tooltip({
items: 'span.et2_label',
position: { my: "right top", at: "left top", collision: "flipfit" },
tooltipClass: "et2_email_popup",
content: function () {
return jQuery('<a href="#" class= "et2_url_email_contactPlus" title="' + egw.lang(widget.options.hover_action_title) + '"><img src="'
+ egw.image("edit") + '"/></a>')
.on('click', function () {
widget.options.hover_action.call(self, self.widget, content);
});
},
close: function (event, ui) {
ui.tooltip.hover(function () {
jQuery(this).stop(true).fadeTo(400, 1);
}, function () {
jQuery(this).fadeOut("400", function () { jQuery(this).remove(); });
});
}
})
.tooltip("open");
}, { widget: this, span: this.span }));
};
return et2_description;
}(et2_core_baseWidget_1.et2_baseWidget)),
_a._attributes = {
"label": {
"name": "Label",
"default": "",
"type": "string",
"description": "The label is displayed by default in front (for radiobuttons behind) each widget (if not empty). If you want to specify a different position, use a '%s' in the label, which gets replaced by the widget itself. Eg. '%s Name' to have the label Name behind a checkbox. The label can contain variables, as descript for name. If the label starts with a '@' it is replaced by the value of the content-array at this index (with the '@'-removed and after expanding the variables).",
"translate": true
},
"value": {
"name": "Value",
"type": "string",
"description": "Displayed text",
"translate": "!no_lang",
"default": ""
},
/**
* Options converted from the "options"-attribute.
*/
"font_style": {
"name": "Font Style",
"type": "string",
"description": "Style may be a compositum of \"b\" and \"i\" which " +
" renders the text bold and/or italic."
},
"href": {
"name": "Link URL",
"type": "string",
"description": "Link URL, empty if you don't wan't to display a link."
},
"activate_links": {
"name": "Replace URLs",
"type": "boolean",
"default": false,
"description": "If set, URLs in the text are automatically replaced " +
"by links"
},
"for": {
"name": "Label for widget",
"type": "string",
"description": "Marks the text as label for the given widget."
},
"extra_link_target": {
"name": "Link target",
"type": "string",
"default": "_browser",
"description": "Link target for href attribute"
},
"extra_link_popup": {
"name": "Popup",
"type": "string",
"description": "widthxheight, if popup should be used, eg. 640x480"
},
"expose_view": {
name: "Expose view",
type: "boolean",
default: false,
description: "Clicking on description with href value would popup an expose view, and will show content referenced by href."
},
mime: {
name: "Mime type",
type: "string",
default: '',
description: "Mime type of the registered link"
},
mime_data: {
name: "Mime data",
type: "string",
default: '',
description: "hash for data stored on service-side with egw_link::(get|set)_data()"
},
hover_action: {
"name": "hover action",
"type": "js",
"default": et2_no_init,
"description": "JS code which is executed when clicking on action button. This action is explicitly for attached nodes, like in nm."
},
hover_action_title: {
"name": "hover action title",
"type": "string",
"default": "Edit",
"description": "Text to show as tooltip of defined action"
}
},
_a))));
exports.et2_description = et2_description;
;
et2_core_widget_1.et2_register_widget(et2_description, ["description", "label"]);
//# sourceMappingURL=et2_widget_description.js.map

View File

@ -0,0 +1,427 @@
/**
* EGroupware eTemplate2 - JS Description object
*
* @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
/vendor/bower-asset/jquery/dist/jquery.js;
et2_core_baseWidget;
expose;
*/
import './et2_core_common';
import {ClassWithAttributes} from "./et2_core_inheritance";
import {et2_register_widget, WidgetConfig} from "./et2_core_widget";
import {et2_baseWidget} from './et2_core_baseWidget'
import './et2_types';
/**
* Class which implements the "description" XET-Tag
*/
export class et2_description extends expose(class et2_description extends et2_baseWidget implements et2_IDetachedDOM, et2_IExposable
{
static readonly _attributes : any = {
"label": {
"name": "Label",
"default": "",
"type": "string",
"description": "The label is displayed by default in front (for radiobuttons behind) each widget (if not empty). If you want to specify a different position, use a '%s' in the label, which gets replaced by the widget itself. Eg. '%s Name' to have the label Name behind a checkbox. The label can contain variables, as descript for name. If the label starts with a '@' it is replaced by the value of the content-array at this index (with the '@'-removed and after expanding the variables).",
"translate": true
},
"value": {
"name": "Value",
"type": "string",
"description": "Displayed text",
"translate": "!no_lang",
"default": ""
},
/**
* Options converted from the "options"-attribute.
*/
"font_style": {
"name": "Font Style",
"type": "string",
"description": "Style may be a compositum of \"b\" and \"i\" which " +
" renders the text bold and/or italic."
},
"href": {
"name": "Link URL",
"type": "string",
"description": "Link URL, empty if you don't wan't to display a link."
},
"activate_links": {
"name": "Replace URLs",
"type": "boolean",
"default": false,
"description": "If set, URLs in the text are automatically replaced " +
"by links"
},
"for": {
"name": "Label for widget",
"type": "string",
"description": "Marks the text as label for the given widget."
},
"extra_link_target": {
"name": "Link target",
"type": "string",
"default": "_browser",
"description": "Link target for href attribute"
},
"extra_link_popup": {
"name": "Popup",
"type": "string",
"description": "widthxheight, if popup should be used, eg. 640x480"
},
"expose_view":{
name: "Expose view",
type: "boolean",
default: false,
description: "Clicking on description with href value would popup an expose view, and will show content referenced by href."
},
mime:{
name: "Mime type",
type: "string",
default: '',
description: "Mime type of the registered link"
},
mime_data:{
name: "Mime data",
type: "string",
default: '',
description: "hash for data stored on service-side with egw_link::(get|set)_data()"
},
hover_action: {
"name": "hover action",
"type": "js",
"default": et2_no_init,
"description": "JS code which is executed when clicking on action button. This action is explicitly for attached nodes, like in nm."
},
hover_action_title: {
"name": "hover action title",
"type": "string",
"default": "Edit",
"description": "Text to show as tooltip of defined action"
}
};
legacyOptions: string[] = ["font_style", "href", "activate_links", "for",
"extra_link_target", "extra_link_popup", "statustext"];
span: JQuery;
label: string;
private _labelContainer: JQuery = null;
font_style: string;
mime_regexp: any;
/**
* Constructor
*/
constructor(_parent, _attrs? : WidgetConfig, _child? : object)
{
// Call the inherited constructor
super(_parent, _attrs, ClassWithAttributes.extendAttributes(et2_description._attributes, _child || {}));
// Create the span/label tag which contains the label text
this.span = jQuery(document.createElement(this.options["for"] ? "label" : "span"))
.addClass("et2_label");
et2_insertLinkText(this._parseText(this.options.value), this.span[0],
this.options.href ? this.options.extra_link_target : '_blank');
this.setDOMNode(this.span[0]);
}
transformAttributes(_attrs)
{
super.transformAttributes(_attrs);
if (this.id)
{
var val = this.getArrayMgr("content").getEntry(this.id);
if (val)
{
_attrs["value"] = val;
}
}
}
doLoadingFinished()
{
super.doLoadingFinished();
// Get the real id of the 'for' widget
var for_widget = null;
if (this.options["for"] && (
(for_widget = this.getParent().getWidgetById(this.options.for)) ||
(for_widget = this.getRoot().getWidgetById(this.options.for))
) && for_widget && for_widget.id)
{
if(for_widget.dom_id)
{
this.span.attr("for", for_widget.dom_id);
}
else
{
// Target widget is not done yet, need to wait
var tab_deferred = jQuery.Deferred();
window.setTimeout(function() {
this.span.attr("for", for_widget.dom_id);
tab_deferred.resolve();
}.bind(this),0);
return tab_deferred.promise();
}
}
return true;
}
set_label(_value)
{
// Abort if ther was no change in the label
if (_value == this.label)
{
return;
}
if (_value)
{
// Create the label container if it didn't exist yet
if (this._labelContainer == null)
{
this._labelContainer = jQuery(document.createElement("label"))
.addClass("et2_label");
this.getSurroundings().insertDOMNode(this._labelContainer[0]);
}
// Clear the label container.
this._labelContainer.empty();
// Create the placeholder element and set it
var ph = document.createElement("span");
this.getSurroundings().setWidgetPlaceholder(ph);
// Split the label at the "%s"
var parts = et2_csvSplit(_value, 2, "%s");
// Update the content of the label container
for (var i = 0; i < parts.length; i++)
{
if (parts[i])
{
this._labelContainer.append(document.createTextNode(parts[i]));
}
if (i == 0)
{
this._labelContainer.append(ph);
}
}
// add class if label is empty
this._labelContainer.toggleClass('et2_label_empty', !_value || !parts[0]);
}
else
{
// Delete the labelContainer from the surroundings object
if (this._labelContainer)
{
this.getSurroundings().removeDOMNode(this._labelContainer[0]);
}
this._labelContainer = null;
}
// Update the surroundings in order to reflect the change in the label
this.getSurroundings().update();
// Copy the given value
this.label = _value;
}
/**
* Function to get media content to feed the expose
* @param {type} _value
* @returns {Array|Array.getMedia.mediaContent}
*/
getMedia(_value)
{
let base_url = egw.webserverUrl.match(new RegExp(/^\//,'ig'))?egw(window).window.location.origin :'';
let mediaContent = [];
if (_value)
{
mediaContent = [{
title: this.options.label,
href: base_url + _value,
type: this.options.type + "/*",
thumbnail: base_url + _value
}];
if (_value.match(/\/webdav.php/,'ig')) mediaContent[0]["download_href"] = base_url + _value + '?download';
}
return mediaContent;
}
set_value(_value)
{
if (!_value) _value = "";
if (!this.options.no_lang) _value = this.egw().lang(_value);
if (this.options.value && (this.options.value+"").indexOf('%s') != -1)
{
_value = this.options.value.replace(/%s/g, _value);
}
et2_insertLinkText(this._parseText(_value),
this.span[0],
this.options.href ? this.options.extra_link_target : '_blank'
);
// Add hover action button (Edit)
if (this.options.hover_action)
{
this._build_hover_action();
}
if(this.options.extra_link_popup || this.options.mime)
{
var href = this.options.href;
var mime_data = this.options.mime_data;
var self= this;
var $span = this.options.mime_data? jQuery(this.span): jQuery('a',this.span);
$span.click(function(e) {
if (self.options.expose_view && typeof self.options.mime !='undefined' && self.options.mime.match(self.mime_regexp,'ig'))
{
self._init_blueimp_gallery(e, href);
}
else
{
egw(window).open_link(mime_data || href, self.options.extra_link_target, self.options.extra_link_popup, null, null, self.options.mime);
}
e.preventDefault();
return false;
});
}
}
_parseText(_value)
{
if (this.options.href)
{
var href = this.options.href;
if (href.indexOf('/')==-1 && href.split('.').length >= 3 &&
!(href.indexOf('mailto:')!=-1 || href.indexOf('://') != -1 || href.indexOf('javascript:') != -1)
)
{
href = "/index.php?menuaction="+href;
}
if (href.charAt(0) == '/') // link relative to eGW
{
href = egw.link(href);
}
return [{
"href": href,
"text": _value
}];
}
else if (this.options.activate_links)
{
return et2_activateLinks(_value);
}
else
{
return [_value];
}
}
set_font_style(_value)
{
this.font_style = _value;
this.span.toggleClass("et2_bold", _value.indexOf("b") >= 0);
this.span.toggleClass("et2_italic", _value.indexOf("i") >= 0);
}
/**
* Code for implementing et2_IDetachedDOM
*
* @param {array} _attrs
*/
getDetachedAttributes(_attrs)
{
_attrs.push("value", "class", "href");
}
getDetachedNodes()
{
return [this.span[0]];
}
setDetachedAttributes(_nodes, _values, _data?)
{
// Update the properties
var updateLink = false;
if (typeof _values["href"] != "undefined")
{
updateLink = true;
this.options.href = _values["href"];
}
if (typeof _values["value"] != "undefined" || (updateLink && (_values["value"] || this.options.value)))
{
this.span = jQuery(_nodes[0]);
this.set_value(_values["value"]);
}
if (typeof _values["class"] != "undefined")
{
_nodes[0].setAttribute("class", _values["class"]);
}
// Add hover action button (Edit), _data is nm's row data
if (this.options.hover_action)
{
this._build_hover_action(_data);
}
}
/**
* Builds button for hover action
* @param {object} _data
*/
_build_hover_action(_data?)
{
var content = _data && _data.content ? _data.content: undefined;
var widget = this;
this.span.off().on('mouseenter', jQuery.proxy(function(event) {
event.stopImmediatePropagation();
var self = this;
this.span.tooltip({
items: 'span.et2_label',
position: {my:"right top", at:"left top", collision:"flipfit"},
tooltipClass: "et2_email_popup",
content()
{
return jQuery('<a href="#" class= "et2_url_email_contactPlus" title="'+egw.lang(widget.options.hover_action_title)+'"><img src="'
+egw.image("edit") +'"/></a>')
.on('click', function() {
widget.options.hover_action.call(self, self.widget, content);
});
},
close( event, ui )
{
ui.tooltip.hover(
function () {
jQuery(this).stop(true).fadeTo(400, 1);
},
function () {
jQuery(this).fadeOut("400", function(){ jQuery(this).remove();});
}
);
}
})
.tooltip("open");
}, {widget: this, span: this.span}));
}
}){};
et2_register_widget(et2_description, ["description", "label"]);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,837 @@
/**
* EGroupware eTemplate2 - JS Dialog Widget class
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @link http://www.egroupware.org
* @author Nathan Gray
* @copyright Nathan Gray 2013
* @version $Id$
*/
/*egw:uses
et2_core_widget;
/vendor/bower-asset/jquery-ui/jquery-ui.js;
*/
import {et2_register_widget, WidgetConfig} from "./et2_core_widget";
import {et2_widget} from "./et2_core_widget";
import {et2_button} from "./et2_widget_button";
import {ClassWithAttributes} from "./et2_core_inheritance";
import {et2_DOMWidget} from "./et2_core_DOMWidget";
/**
* A common dialog widget that makes it easy to imform users or prompt for information.
*
* It is possible to have a custom dialog by using a template, but you can also use
* the static method et2_dialog.show_dialog(). At its simplest, you can just use:
* <code>
* et2_dialog.show_dialog(false, "Operation completed");
* </code>
* Or a more complete example:
* <code>
* var callback = function (button_id)
* {
* if(button_id == et2_dialog.YES_BUTTON)
* {
* // Do stuff
* }
* else if (button_id == et2_dialog.NO_BUTTON)
* {
* // Other stuff
* }
* else if (button_id == et2_dialog.CANCEL_BUTTON)
* {
* // Abort
* }
* }.
* var dialog = et2_dialog.show_dialog(
* callback, "Erase the entire database?","Break things", {} // value
* et2_dialog.BUTTONS_YES_NO_CANCEL, et2_dialog.WARNING_MESSAGE
* );
* </code>
*
*
* The parameters for the above are all optional, except callback and message:
* callback - function called when the dialog closes, or false/null.
* The ID of the button will be passed. Button ID will be one of the et2_dialog.*_BUTTON constants.
* The callback is _not_ called if the user closes the dialog with the X in the corner, or presses ESC.
* message - (plain) text to display
* title - Dialog title
* value (for prompt)
* buttons - et2_dialog BUTTONS_* constant, or an array of button settings
* dialog_type - et2_dialog *_MESSAGE constant
* icon - URL of icon
*
* Note that these methods will _not_ block program flow while waiting for user input.
* The user's input will be provided to the callback.
*
* You can also use the standard et2_createWidget() to create a custom dialog using an etemplate, even setting all
* the buttons yourself.
* <code>
* var dialog = et2_createWidget("dialog",{
* // If you use a template, the second parameter will be the value of the template, as if it were submitted.
* callback: function(button_id, value) {...}, // return false to prevent dialog closing
* buttons: [
* // These ones will use the callback, just like normal
* {text: egw.lang("OK"),id:"OK", class="ui-priority-primary", default: true},
* {text: egw.lang("Yes"),id:"Yes"},
* {text: egw.lang("Sure"),id:"Sure"},
* {text: egw.lang("Maybe"),click: function() {
* // If you override, 'this' will be the dialog DOMNode.
* // Things get more complicated.
* // Do what you like, but don't forget this line:
* jQuery(this).dialog("close")
* }, class="ui-state-error"},
*
* ],
* title: 'Why would you want to do this?',
* template:"/egroupware/addressbook/templates/default/edit.xet",
* value: { content: {...default values}, sel_options: {...}...}
* });
* </code>
* @augments et2_widget
* @see http://api.jqueryui.com/dialog/
*/
export class et2_dialog extends et2_widget {
static readonly _attributes: any = {
callback: {
name: "Callback",
type: "js",
description: "Callback function is called with the value when the dialog is closed",
"default": function (button_id) {
egw.debug("log", "Button ID: %d", button_id);
}
},
beforeClose: {
name: "before close callback",
type: "js",
description: "Callback function before dialog is closed, return false to prevent that",
"default": function () {
}
},
message: {
name: "Message",
type: "string",
description: "Dialog message (plain text, no html)",
"default": "Somebody forgot to set this..."
},
dialog_type: {
name: "Dialog type",
type: "integer",
description: "To use a pre-defined dialog style, use et2_dialog.ERROR_MESSAGE, INFORMATION_MESSAGE,WARNING_MESSAGE,QUESTION_MESSAGE,PLAIN_MESSAGE constants. Default is et2_dialog.PLAIN_MESSAGE",
"default": 0 //this.PLAIN_MESSAGE
},
buttons: {
name: "Buttons",
type: "any",
"default": 0, //this.BUTTONS_OK,
description: "Buttons that appear at the bottom of the dialog. You can use the constants et2_dialog.BUTTONS_OK, BUTTONS_YES_NO, BUTTONS_YES_NO_CANCEL, BUTTONS_OK_CANCEL, or pass in an array for full control"
},
icon: {
name: "Icon",
type: "string",
description: "URL of an icon for the dialog. If omitted, an icon based on dialog_type will be used.",
"default": ""
},
title: {
name: "Title",
type: "string",
description: "Title for the dialog box (plain text, no html)",
"default": ""
},
modal: {
name: "Modal",
type: "boolean",
description: "Prevent the user from interacting with the page",
"default": true
},
resizable: {
name: "Resizable",
type: "boolean",
description: "Allow the user to resize the dialog",
"default": true
},
value: {
"name": "Value",
"description": "The (default) value of the dialog. Use with template.",
"type": "any",
"default": et2_no_init
},
template: {
"name": "Template",
"description": "Instead of displaying a simple message, a full template can be loaded instead. Set defaults with value.",
"type": "string",
"default": et2_no_init
},
minWidth: {
name: "minimum width",
type: "integer",
description: "Define minimum width of dialog",
"default": 0
},
minHeight: {
name: "minimum height",
type: "integer",
description: "Define minimum height of dialog",
"default": 0
},
width: {
name: "width",
type: "string",
description: "Define width of dialog, the default is auto",
"default": 'auto'
},
height: {
name: "height",
type: "string",
description: "Define width of dialog, the default is auto",
"default": 'auto'
},
position: {
name: "position",
type: "string",
description: "Define position of dialog in the main window",
default: "center"
}
};
/**
* Details for dialog type options
*/
private readonly _dialog_types: any = [
//PLAIN_MESSAGE: 0
"",
//INFORMATION_MESSAGE: 1,
"dialog_info",
//QUESTION_MESSAGE: 2,
"dialog_help",
//WARNING_MESSAGE: 3,
"dialog_warning",
//ERROR_MESSAGE: 4,
"dialog_error"
];
private readonly _buttons: any = [
/*
Pre-defined Button combos
- button ids copied from et2_dialog static, since the constants are not defined yet
- image get replaced by 'style="background-image: url('+egw.image(image)+')' for an image prefixing text
*/
//BUTTONS_OK: 0,
[{"button_id": 1, "text": 'ok', id: 'dialog[ok]', image: 'check', "default": true}],
//BUTTONS_OK_CANCEL: 1,
[
{"button_id": 1, "text": 'ok', id: 'dialog[ok]', image: 'check', "default": true},
{"button_id": 0, "text": 'cancel', id: 'dialog[cancel]', image: 'cancel'}
],
//BUTTONS_YES_NO: 2,
[
{"button_id": 2, "text": 'yes', id: 'dialog[yes]', image: 'check', "default": true},
{"button_id": 3, "text": 'no', id: 'dialog[no]', image: 'cancelled'}
],
//BUTTONS_YES_NO_CANCEL: 3,
[
{"button_id": 2, "text": 'yes', id: 'dialog[yes]', image: 'check', "default": true},
{"button_id": 3, "text": 'no', id: 'dialog[no]', image: 'cancelled'},
{"button_id": 0, "text": 'cancel', id: 'dialog[cancel]', image: 'cancel'}
]
];
/**
* Types
* @constant
*/
public static PLAIN_MESSAGE: number = 0;
public static INFORMATION_MESSAGE: number = 1;
public static QUESTION_MESSAGE: number = 2;
public static WARNING_MESSAGE: number = 3;
public static ERROR_MESSAGE: number = 4;
/* Pre-defined Button combos */
public static BUTTONS_OK: number = 0;
public static BUTTONS_OK_CANCEL: number = 1;
public static BUTTONS_YES_NO: number = 2;
public static BUTTONS_YES_NO_CANCEL: number = 3;
/* Button constants */
public static CANCEL_BUTTON: number = 0;
public static OK_BUTTON: number = 1;
public static YES_BUTTON: number = 2;
public static NO_BUTTON: number = 3;
div: JQuery = null;
template: any = null;
constructor(_parent?, _attrs? : WidgetConfig, _child? : object) {
super(_parent, _attrs, ClassWithAttributes.extendAttributes(et2_dialog._attributes, _child || {}));
// Define this as null to avoid breaking any hierarchies (eg: destroy())
if (this.getParent() != null) this.getParent().removeChild(this);
// Button callbacks need a reference to this
let self = this;
for (let i = 0; i < this._buttons.length; i++) {
for (let j = 0; j < this._buttons[i].length; j++) {
this._buttons[i][j].click = (function (id) {
return function (event) {
self.click(event.target, id);
};
})(this._buttons[i][j].button_id);
// translate button texts, as translations are not available before
this._buttons[i][j].text = egw.lang(this._buttons[i][j].text);
}
}
this.div = jQuery(document.createElement("div"));
this._createDialog();
}
/**
* Clean up dialog
*/
destroy() {
if (this.div != null) {
// Un-dialog the dialog
this.div.dialog("destroy");
if (this.template) {
this.template.clear();
this.template = null;
}
this.div = null;
}
// Call the inherited constructor
super.destroy();
}
/**
* Internal callback registered on all standard buttons.
* The provided callback is called after the dialog is closed.
*
* @param target DOMNode The clicked button
* @param button_id integer The ID of the clicked button
*/
click(target: HTMLElement, button_id: number) {
if (this.options.callback) {
if (this.options.callback.call(this, button_id, this.get_value()) === false) return;
}
// Triggers destroy too
this.div.dialog("close");
}
/**
* Returns the values of any widgets in the dialog. This does not include
* the buttons, which are only supplied for the callback.
*/
get_value() {
var value = this.options.value;
if (this.template) {
value = this.template.getValues(this.template.widgetContainer);
}
return value;
}
/**
* Set the displayed prompt message
*
* @param {string} message New message for the dialog
*/
set_message(message) {
this.options.message = message;
this.div.empty()
.append("<img class='dialog_icon' />")
.append(jQuery('<div/>').text(message));
}
/**
* Set the dialog type to a pre-defined type
*
* @param {integer} type constant from et2_dialog
*/
set_dialog_type(type) {
if (this.options.dialog_type != type && typeof this._dialog_types[type] == "string") {
this.options.dialog_type = type;
}
this.set_icon(this._dialog_types[type] ? egw.image(this._dialog_types[type]) : "");
}
/**
* Set the icon for the dialog
*
* @param {string} icon_url
*/
set_icon(icon_url) {
if (icon_url == "") {
jQuery("img.dialog_icon", this.div).hide();
} else {
jQuery("img.dialog_icon", this.div).show().attr("src", icon_url);
}
}
/**
* Set the dialog buttons
*
* Use either the pre-defined options in et2_dialog, or an array
* @see http://api.jqueryui.com/dialog/#option-buttons
* @param {array} buttons
*/
set_buttons(buttons) {
this.options.buttons = buttons;
if (buttons instanceof Array) {
for (var i = 0; i < buttons.length; i++) {
var button = buttons[i];
if (!button.click) {
button.click = jQuery.proxy(this.click, this, null, button.id);
}
// set a default background image and css class based on buttons id
if (button.id && typeof button.class == 'undefined') {
for (var name in et2_button.default_classes) {
if (button.id.match(et2_button.default_classes[name])) {
button.class = (typeof button.class == 'undefined' ? '' : button.class + ' ') + name;
break;
}
}
}
if (button.id && typeof button.image == 'undefined' && typeof button.style == 'undefined') {
for (var name in et2_button.default_background_images) {
if (button.id.match(et2_button.default_background_images[name])) {
button.image = name;
break;
}
}
}
if (button.image) {
button.style = 'background-image: url(' + this.egw().image(button.image, 'api') + ')';
delete button.image;
}
}
}
// If dialog already created, update buttons
if (this.div.data('ui-dialog')) {
this.div.dialog("option", "buttons", buttons);
// Focus default button so enter works
jQuery('.ui-dialog-buttonpane button[default]', this.div.parent()).focus();
}
}
/**
* Set the dialog title
*
* @param {string} title New title for the dialog
*/
set_title(title) {
this.options.title = title;
this.div.dialog("option", "title", title);
}
/**
* Block interaction with the page behind the dialog
*
* @param {boolean} modal Block page behind dialog
*/
set_modal(modal) {
this.options.modal = modal;
this.div.dialog("option", "modal", modal);
}
/**
* Load an etemplate into the dialog
*
* @param template String etemplate file name
*/
set_template(template) {
if (this.template && this.options.template != template) {
this.template.clear();
}
this.template = new etemplate2(this.div[0], false);
if (template.indexOf('.xet') > 0) {
// File name provided, fetch from server
this.template.load("", template, this.options.value || {content: {}}, jQuery.proxy(function () {
// Set focus to the first input
jQuery('input', this.div).first().focus();
}, this));
} else {
// Just template name, it better be loaded already
this.template.load(template, '', this.options.value || {},
// true: do NOT call et2_ready, as it would overwrite this.et2 in app.js
undefined, undefined, true);
}
// set template-name as id, to allow to style dialogs
this.div.children().attr('id', template.replace(/^(.*\/)?([^/]+)(\.xet)?$/, '$2').replace(/\./g, '-'));
}
/**
* Actually create and display the dialog
*/
_createDialog() {
if (this.options.template) {
this.set_template(this.options.template);
} else {
this.set_message(this.options.message);
this.set_dialog_type(this.options.dialog_type);
}
this.set_buttons(typeof this.options.buttons == "number" ? this._buttons[this.options.buttons] : this.options.buttons);
let options = {
// Pass the internal object, not the option
buttons: this.options.buttons,
modal: this.options.modal,
resizable: this.options.resizable,
minWidth: this.options.minWidth,
minHeight: this.options.minHeight,
maxWidth: 640,
height: this.options.height,
title: this.options.title,
open: function () {
// Focus default button so enter works
jQuery(this).parents('.ui-dialog-buttonpane button[default]').focus();
window.setTimeout(function () {
jQuery(this).dialog('option', 'position', {
my: "center",
at: "center",
of: window
});
}.bind(this), 0);
},
close: jQuery.proxy(function () {
this.destroy();
}, this),
beforeClose: this.options.beforeClose,
closeText: this.egw().lang('close'),
position: {my: this.options.position, at: this.options.position, of: window}
};
// Leaving width unset lets it size itself according to contents
if (this.options.width) {
options['width'] = this.options.width;
}
this.div.dialog(options);
// Make sure dialog is wide enough for the title
// Arbitrary numbers that seem to work nicely.
let title_width = 20 + 10 * this.options.title.length;
if (this.div.width() < title_width && this.options.title.trim()) {
// Auto-sizing chopped the title
this.div.dialog('option', 'width', title_width);
}
}
/**
* Create a parent to inject application specific egw object with loaded translations into et2_dialog
*
* @param {string|egw} _egw_or_appname egw object with already loaded translations or application name to load translations for
*/
static _create_parent(_egw_or_appname? : string | IegwAppLocal) {
if (typeof _egw_or_appname == 'undefined') {
// @ts-ignore
_egw_or_appname = egw_appName;
}
// create a dummy parent with a correct reference to an application specific egw object
let parent = new et2_widget();
// if egw object is passed in because called from et2, just use it
if (typeof _egw_or_appname != 'string') {
parent.setApiInstance(_egw_or_appname);
}
// otherwise use given appname to create app-specific egw instance and load default translations
else {
parent.setApiInstance(egw(_egw_or_appname));
parent.egw().langRequireApp(parent.egw().window, _egw_or_appname);
}
return parent;
}
/**
* Show a confirmation dialog
*
* @param {function} _callback Function called when the user clicks a button. The context will be the et2_dialog widget, and the button constant is passed in.
* @param {string} _message Message to be place in the dialog.
* @param {string} _title Text in the top bar of the dialog.
* @param _value passed unchanged to callback as 2. parameter
* @param {integer|array} _buttons One of the BUTTONS_ constants defining the set of buttons at the bottom of the box
* @param {integer} _type One of the message constants. This defines the style of the message.
* @param {string} _icon URL of an icon to display. If not provided, a type-specific icon will be used.
* @param {string|egw} _egw_or_appname egw object with already laoded translations or application name to load translations for
*/
static show_dialog(_callback? : Function, _message? : string, _title? : string, _value? : object, _buttons?, _type? : number, _icon? : string, _egw_or_appname? : string | IegwAppLocal) {
let parent = et2_dialog._create_parent(_egw_or_appname);
// Just pass them along, widget handles defaults & missing
return et2_createWidget("dialog", {
callback: _callback || function () {
},
message: _message,
title: _title || parent.egw().lang('Confirmation required'),
buttons: typeof _buttons != 'undefined' ? _buttons : et2_dialog.BUTTONS_YES_NO,
dialog_type: typeof _type != 'undefined' ? _type : et2_dialog.QUESTION_MESSAGE,
icon: _icon,
value: _value,
width: 'auto'
}, parent);
};
/**
* Show an alert message with OK button
*
* @param {string} _message Message to be place in the dialog.
* @param {string} _title Text in the top bar of the dialog.
* @param {integer} _type One of the message constants. This defines the style of the message.
*/
static alert(_message? : string, _title? : string, _type?) {
let parent = et2_dialog._create_parent(et2_dialog._create_parent().egw());
et2_createWidget("dialog", {
callback: function () {
},
message: _message,
title: _title,
buttons: et2_dialog.BUTTONS_OK,
dialog_type: _type || et2_dialog.INFORMATION_MESSAGE
}, parent);
}
/**
* Show a prompt dialog
*
* @param {function} _callback Function called when the user clicks a button. The context will be the et2_dialog widget, and the button constant is passed in.
* @param {string} _message Message to be place in the dialog.
* @param {string} _title Text in the top bar of the dialog.
* @param {string} _value for prompt, passed to callback as 2. parameter
* @param {integer|array} _buttons One of the BUTTONS_ constants defining the set of buttons at the bottom of the box
* @param {string|egw} _egw_or_appname egw object with already laoded translations or application name to load translations for
*/
static show_prompt(_callback, _message, _title?, _value?, _buttons?, _egw_or_appname?) {
var callback = _callback;
// Just pass them along, widget handles defaults & missing
return et2_createWidget("dialog", {
callback: function (_button_id, _value) {
if (typeof callback == "function") {
callback.call(this, _button_id, _value.value);
}
},
title: _title || egw.lang('Input required'),
buttons: _buttons || et2_dialog.BUTTONS_OK_CANCEL,
value: {
content: {
value: _value,
message: _message
}
},
template: egw.webserverUrl + '/api/templates/default/prompt.xet',
class: "et2_prompt"
}, et2_dialog._create_parent(_egw_or_appname));
}
/**
* Method to build a confirmation dialog only with
* YES OR NO buttons and submit content back to server
*
* @param {widget} _senders widget that has been clicked
* @param {String} _dialogMsg message shows in dialog box
* @param {String} _titleMsg message shows as a title of the dialog box
* @param {Bool} _postSubmit true: use postSubmit instead of submit
*
* @description submit the form contents including the button that has been pressed
*/
static confirm(_senders, _dialogMsg, _titleMsg, _postSubmit) {
var senders = _senders;
var buttonId = _senders.id;
var dialogMsg = (typeof _dialogMsg != "undefined") ? _dialogMsg : '';
var titleMsg = (typeof _titleMsg != "undefined") ? _titleMsg : '';
var egw = _senders instanceof et2_widget ? _senders.egw() : et2_dialog._create_parent().egw();
var callbackDialog = function (button_id) {
if (button_id == et2_dialog.YES_BUTTON) {
if (_postSubmit) {
senders.getRoot().getInstanceManager().postSubmit(buttonId);
} else {
senders.getRoot().getInstanceManager().submit(buttonId);
}
}
};
et2_dialog.show_dialog(callbackDialog, egw.lang(dialogMsg), egw.lang(titleMsg), {},
et2_dialog.BUTTONS_YES_NO, et2_dialog.WARNING_MESSAGE, undefined, egw);
};
/**
* Show a dialog for a long-running, multi-part task
*
* Given a server url and a list of parameters, this will open a dialog with
* a progress bar, asynchronously call the url with each parameter, and update
* the progress bar.
* Any output from the server will be displayed in a box.
*
* When all tasks are done, the callback will be called with boolean true. It will
* also be called if the user clicks a button (OK or CANCEL), so be sure to
* check to avoid executing more than intended.
*
* @param {function} _callback Function called when the user clicks a button,
* or when the list is done processing. The context will be the et2_dialog
* widget, and the button constant is passed in.
* @param {string} _message Message to be place in the dialog. Usually just
* text, but DOM nodes will work too.
* @param {string} _title Text in the top bar of the dialog.
* @param {string} _menuaction the menuaction function which should be called and
* which handles the actual request. If the menuaction is a full featured
* url, this one will be used instead.
* @param {Array[]} _list - List of parameters, one for each call to the
* address. Multiple parameters are allowed, in an array.
* @param {string|egw} _egw_or_appname egw object with already laoded translations or application name to load translations for
*
* @return {et2_dialog}
*/
static long_task(_callback, _message, _title, _menuaction, _list, _egw_or_appname)
{
let parent = et2_dialog._create_parent(_egw_or_appname);
let egw = parent.egw();
// Special action for cancel
let buttons = [
{"button_id": et2_dialog.OK_BUTTON, "text": egw.lang('ok'), "default": true, "disabled": true},
{
"button_id": et2_dialog.CANCEL_BUTTON, "text": egw.lang('cancel'), click: function () {
// Cancel run
cancel = true;
jQuery("button[button_id=" + et2_dialog.CANCEL_BUTTON + "]", dialog.div.parent()).button("disable");
update.call(_list.length, '');
}
}
];
let dialog = et2_createWidget("dialog", {
template: egw.webserverUrl + '/api/templates/default/long_task.xet',
value: {
content: {
message: _message
}
},
callback: function (_button_id, _value) {
if (_button_id == et2_dialog.CANCEL_BUTTON) {
cancel = true;
}
if (typeof _callback == "function") {
_callback.call(this, _button_id, _value.value);
}
},
title: _title || egw.lang('please wait...'),
buttons: buttons
}, parent);
// OK starts disabled
jQuery("button[button_id=" + et2_dialog.OK_BUTTON + "]", dialog.div.parent()).button("disable");
let log = null;
let progressbar = null;
let cancel = false;
let totals = {
success: 0,
skipped: 0,
failed: 0,
widget: null
};
// Updates progressbar & log, calls next step
let update = function (response) {
// context is index
let index = this || 0;
progressbar.set_value(100 * (index / _list.length));
progressbar.set_label(index + ' / ' + _list.length);
// Display response information
switch (response.type) {
case 'error':
jQuery("<div class='message error'></div>")
.text(response.data)
.appendTo(log);
totals.failed++;
// Ask to retry / ignore / abort
et2_createWidget("dialog", {
callback: function (button) {
switch (button) {
case 'dialog[cancel]':
cancel = true;
return update.call(index, '');
case 'dialog[skip]':
// Continue with next index
totals.skipped++;
return update.call(index, '');
default:
// Try again with previous index
return update.call(index - 1, '');
}
},
message: response.data,
title: '',
buttons: [
// These ones will use the callback, just like normal
{text: egw.lang("Abort"), id: 'dialog[cancel]'},
{text: egw.lang("Retry"), id: 'dialog[retry]'},
{text: egw.lang("Skip"), id: 'dialog[skip]', class: "ui-priority-primary", default: true}
],
dialog_type: et2_dialog.ERROR_MESSAGE
}, parent);
// Early exit
return;
default:
if (response) {
totals.success++;
jQuery("<div class='message'></div>")
.text(response)
.appendTo(log);
}
}
// Scroll to bottom
let height = log[0].scrollHeight;
log.scrollTop(height);
// Update totals
totals.widget.set_value(egw.lang(
"Total: %1 Successful: %2 Failed: %3 Skipped: %4",
_list.length, <string><unknown>totals.success, <string><unknown>totals.failed, <string><unknown>totals.skipped
));
// Fire next step
if (!cancel && index < _list.length) {
var parameters = _list[index];
if (typeof parameters != 'object') parameters = [parameters];
// Async request, we'll take the next step in the callback
// We can't pass index = 0, it looks like false and causes issues
egw.json(_menuaction, parameters, update, index + 1, true, index + 1).sendRequest();
} else {
// All done
if (!cancel) progressbar.set_value(100);
jQuery("button[button_id=" + et2_dialog.CANCEL_BUTTON + "]", dialog.div.parent()).button("disable");
jQuery("button[button_id=" + et2_dialog.OK_BUTTON + "]", dialog.div.parent()).button("enable");
if (!cancel && typeof _callback == "function") {
_callback.call(dialog, true, response);
}
}
};
jQuery(dialog.template.DOMContainer).on('load', function () {
// Get access to template widgets
log = jQuery(dialog.template.widgetContainer.getWidgetById('log').getDOMNode());
progressbar = dialog.template.widgetContainer.getWidgetById('progressbar');
progressbar.set_label('0 / ' + _list.length);
totals.widget = dialog.template.widgetContainer.getWidgetById('totals');
// Start
window.setTimeout(function () {
update.call(0, '');
}, 0);
});
return dialog;
}
}
et2_register_widget(et2_dialog, ["dialog"]);

View File

@ -1,3 +1,4 @@
"use strict";
/**
* EGroupware eTemplate2 - JS Diff object
*
@ -9,186 +10,180 @@
* @copyright Nathan Gray 2012
* @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
/vendor/bower-asset/jquery/dist/jquery.js;
/vendor/bower-asset/jquery-ui/jquery-ui.js;
/vendor/bower-asset/diff2html/dist/diff2html.min.js;
et2_core_valueWidget;
/vendor/bower-asset/jquery/dist/jquery.js;
/vendor/bower-asset/jquery-ui/jquery-ui.js;
/vendor/bower-asset/diff2html/dist/diff2html.min.js;
et2_core_valueWidget;
*/
var et2_core_widget_1 = require("./et2_core_widget");
var et2_core_inheritance_1 = require("./et2_core_inheritance");
var et2_core_valueWidget_1 = require("./et2_core_valueWidget");
/**
* Class that displays the diff between two [text] values
*
* @augments et2_valueWidget
*/
var et2_diff = (function(){ "use strict"; return et2_valueWidget.extend([et2_IDetachedDOM],
{
attributes: {
"value": {
"type": "any"
}
},
diff_options: {
"inputFormat":"diff",
"matching": "words"
},
/**
* Constructor
*
* @memberOf et2_diff
*/
init: function() {
this._super.apply(this, arguments);
this.mini = true;
// included via etemplate2.css
//this.egw().includeCSS('../../../vendor/bower-asset/dist/dist2html.css');
this.div = document.createElement("div");
jQuery(this.div).addClass('et2_diff');
},
set_value: function(value) {
jQuery(this.div).empty();
if(typeof value == 'string') {
// Diff2Html likes to have files, we don't have them
if(value.indexOf('---') !== 0)
{
value = "--- diff\n+++ diff\n"+value;
}
var diff = Diff2Html.getPrettyHtml(value, this.diff_options);
// var ui = new Diff2HtmlUI({diff: diff});
// ui.draw(jQuery(this.div), this.diff_options);
jQuery(this.div).append(diff);
}
else if(typeof value != 'object')
{
jQuery(this.div).append(value);
}
this.check_mini();
},
check_mini: function() {
if(!this.mini)
{
return false;
}
var view = jQuery(this.div).children();
this.minify(view);
var self = this;
jQuery('<span class="ui-icon ui-icon-circle-plus">&nbsp;</span>')
.appendTo(self.div)
.css("cursor", "pointer")
.click({diff: view, div: self.div, label: self.options.label}, function(e) {
var diff = e.data.diff;
var div = e.data.div;
self.un_minify(diff);
var dialog_div = jQuery('<div>')
.append(diff);
dialog_div.dialog({
title: e.data.label,
width: 'auto',
autoResize: true,
modal: true,
buttons: [{text: self.egw().lang('ok'), click: function() {jQuery(this).dialog("close");}}],
open: function() {
if(jQuery(this).parent().height() > jQuery(window).height())
{
jQuery(this).height(jQuery(window).height() *0.7);
}
jQuery(this).addClass('et2_diff').dialog({position: "center"});
},
close: function(event, ui) {
// Need to destroy the dialog, etemplate widget needs divs back where they were
dialog_div.dialog("destroy");
self.minify(this);
// Put it back where it came from, or et2 will error when clear() is called
diff.prependTo(div);
}
});
});
},
set_label: function(_label) {
this.options.label = _label;
},
/**
* Make the diff into a mini-diff
*
* @param {DOMNode|String} view
*/
minify: function(view) {
view = jQuery(view)
.addClass('mini')
// Dialog changes these, if resized
.width('100%').css('height', 'inherit')
.show();
jQuery('th', view).hide();
jQuery('td.equal',view).hide()
.prevAll().hide();
},
/**
* Expand mini-diff
*
* @param {DOMNode|String} view
*/
un_minify: function(view) {
jQuery(view).removeClass('mini').show();
jQuery('th',view).show();
jQuery('td.equal',view).show();
},
/**
* Code for implementing et2_IDetachedDOM
* Fast-clonable read-only widget that only deals with DOM nodes, not the widget tree
*/
/**
* Build a list of attributes which can be set when working in the
* "detached" mode in the _attrs array which is provided
* by the calling code.
*
* @param {object} _attrs
*/
getDetachedAttributes: function(_attrs) {
_attrs.push("value", "label");
},
/**
* Returns an array of DOM nodes. The (relativly) same DOM-Nodes have to be
* passed to the "setDetachedAttributes" function in the same order.
*/
getDetachedNodes: function() {
return [this.div];
},
/**
* Sets the given associative attribute->value array and applies the
* attributes to the given DOM-Node.
*
* @param _nodes is an array of nodes which has 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) {
this.div = _nodes[0];
if(typeof _values['label'] != 'undefined')
{
this.set_label(_values['label']);
}
if(typeof _values['value'] != 'undefined')
{
this.set_value(_values['value']);
}
}
});}).call(this);
et2_register_widget(et2_diff, ["diff"]);
var et2_diff = /** @class */ (function (_super) {
__extends(et2_diff, _super);
/**
* Constructor
*/
function et2_diff(_parent, _attrs, _child) {
var _this =
// Call the inherited constructor
_super.call(this, _parent, _attrs, et2_core_inheritance_1.ClassWithAttributes.extendAttributes(et2_diff._attributes, _child || {})) || this;
_this.mini = true;
// included via etemplate2.css
//this.egw().includeCSS('../../../vendor/bower-asset/dist/dist2html.css');
_this.div = document.createElement("div");
jQuery(_this.div).addClass('et2_diff');
return _this;
}
et2_diff.prototype.set_value = function (value) {
jQuery(this.div).empty();
if (typeof value == 'string') {
// Diff2Html likes to have files, we don't have them
if (value.indexOf('---') !== 0) {
value = "--- diff\n+++ diff\n" + value;
}
// @ts-ignore
var diff = Diff2Html.getPrettyHtml(value, this.diff_options);
// var ui = new Diff2HtmlUI({diff: diff});
// ui.draw(jQuery(this.div), this.diff_options);
jQuery(this.div).append(diff);
}
else if (typeof value != 'object') {
jQuery(this.div).append(value);
}
this.check_mini();
};
et2_diff.prototype.check_mini = function () {
if (!this.mini) {
return false;
}
var view = jQuery(this.div).children();
this.minify(view);
var self = this;
jQuery('<span class="ui-icon ui-icon-circle-plus">&nbsp;</span>')
.appendTo(self.div)
.css("cursor", "pointer")
.click({ diff: view, div: self.div, label: self.options.label }, function (e) {
var diff = e.data.diff;
var div = e.data.div;
self.un_minify(diff);
var dialog_div = jQuery('<div>')
.append(diff);
dialog_div.dialog({
title: e.data.label,
width: 'auto',
modal: true,
buttons: [{ text: self.egw().lang('ok'), click: function () { jQuery(this).dialog("close"); } }],
open: function () {
if (jQuery(this).parent().height() > jQuery(window).height()) {
jQuery(this).height(jQuery(window).height() * 0.7);
}
jQuery(this).addClass('et2_diff').dialog({ position: "center" });
},
close: function (event, ui) {
// Need to destroy the dialog, etemplate widget needs divs back where they were
dialog_div.dialog("destroy");
self.minify(this);
// Put it back where it came from, or et2 will error when clear() is called
diff.prependTo(div);
}
});
});
};
et2_diff.prototype.set_label = function (_label) {
this.options.label = _label;
};
/**
* Make the diff into a mini-diff
*
* @param {DOMNode|String} view
*/
et2_diff.prototype.minify = function (view) {
view = jQuery(view)
.addClass('mini')
// Dialog changes these, if resized
.width('100%').css('height', 'inherit')
.show();
jQuery('th', view).hide();
jQuery('td.equal', view).hide()
.prevAll().hide();
};
/**
* Expand mini-diff
*
* @param {DOMNode|String} view
*/
et2_diff.prototype.un_minify = function (view) {
jQuery(view).removeClass('mini').show();
jQuery('th', view).show();
jQuery('td.equal', view).show();
};
/**
* Code for implementing et2_IDetachedDOM
* Fast-clonable read-only widget that only deals with DOM nodes, not the widget tree
*/
/**
* Build a list of attributes which can be set when working in the
* "detached" mode in the _attrs array which is provided
* by the calling code.
*
* @param {object} _attrs
*/
et2_diff.prototype.getDetachedAttributes = function (_attrs) {
_attrs.push("value", "label");
};
/**
* Returns an array of DOM nodes. The (relativly) same DOM-Nodes have to be
* passed to the "setDetachedAttributes" function in the same order.
*/
et2_diff.prototype.getDetachedNodes = function () {
return [this.div];
};
/**
* Sets the given associative attribute->value array and applies the
* attributes to the given DOM-Node.
*
* @param _nodes is an array of nodes which has 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.
*/
et2_diff.prototype.setDetachedAttributes = function (_nodes, _values) {
this.div = _nodes[0];
if (typeof _values['label'] != 'undefined') {
this.set_label(_values['label']);
}
if (typeof _values['value'] != 'undefined') {
this.set_value(_values['value']);
}
};
et2_diff._attributes = {
"value": {
"type": "any"
}
};
return et2_diff;
}(et2_core_valueWidget_1.et2_valueWidget));
exports.et2_diff = et2_diff;
et2_core_widget_1.et2_register_widget(et2_diff, ["diff"]);
//# sourceMappingURL=et2_widget_diff.js.map

View File

@ -0,0 +1,209 @@
/**
* EGroupware eTemplate2 - JS Diff object
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage api
* @link http://www.egroupware.org
* @author Nathan Gray
* @copyright Nathan Gray 2012
* @version $Id$
*/
/*egw:uses
/vendor/bower-asset/jquery/dist/jquery.js;
/vendor/bower-asset/jquery-ui/jquery-ui.js;
/vendor/bower-asset/diff2html/dist/diff2html.min.js;
et2_core_valueWidget;
*/
import {et2_register_widget, WidgetConfig} from "./et2_core_widget";
import {ClassWithAttributes} from "./et2_core_inheritance";
import {et2_valueWidget} from "./et2_core_valueWidget";
/**
* Class that displays the diff between two [text] values
*
* @augments et2_valueWidget
*/
export class et2_diff extends et2_valueWidget implements et2_IDetachedDOM
{
static readonly _attributes = {
"value": {
"type": "any"
}
};
private readonly diff_options: {
"inputFormat":"diff",
"matching": "words"
};
private div: HTMLDivElement;
private mini: boolean = true;
/**
* Constructor
*/
constructor(_parent, _attrs? : WidgetConfig, _child? : object)
{
// Call the inherited constructor
super(_parent, _attrs, ClassWithAttributes.extendAttributes(et2_diff._attributes, _child || {}));
// included via etemplate2.css
//this.egw().includeCSS('../../../vendor/bower-asset/dist/dist2html.css');
this.div = document.createElement("div");
jQuery(this.div).addClass('et2_diff');
}
set_value( value)
{
jQuery(this.div).empty();
if(typeof value == 'string') {
// Diff2Html likes to have files, we don't have them
if(value.indexOf('---') !== 0)
{
value = "--- diff\n+++ diff\n"+value;
}
// @ts-ignore
var diff = Diff2Html.getPrettyHtml(value, this.diff_options);
// var ui = new Diff2HtmlUI({diff: diff});
// ui.draw(jQuery(this.div), this.diff_options);
jQuery(this.div).append(diff);
}
else if(typeof value != 'object')
{
jQuery(this.div).append(value);
}
this.check_mini();
}
check_mini( )
{
if(!this.mini)
{
return false;
}
var view = jQuery(this.div).children();
this.minify(view);
var self = this;
jQuery('<span class="ui-icon ui-icon-circle-plus">&nbsp;</span>')
.appendTo(self.div)
.css("cursor", "pointer")
.click({diff: view, div: self.div, label: self.options.label}, function(e) {
var diff = e.data.diff;
var div = e.data.div;
self.un_minify(diff);
var dialog_div = jQuery('<div>')
.append(diff);
dialog_div.dialog({
title: e.data.label,
width: 'auto',
modal: true,
buttons: [{text: self.egw().lang('ok'), click: function() {jQuery(this).dialog("close");}}],
open( )
{
if(jQuery(this).parent().height() > jQuery(window).height())
{
jQuery(this).height(jQuery(window).height() *0.7);
}
jQuery(this).addClass('et2_diff').dialog({position: "center"});
},
close( event, ui)
{
// Need to destroy the dialog, etemplate widget needs divs back where they were
dialog_div.dialog("destroy");
self.minify(this);
// Put it back where it came from, or et2 will error when clear() is called
diff.prependTo(div);
}
});
});
}
set_label( _label)
{
this.options.label = _label;
}
/**
* Make the diff into a mini-diff
*
* @param {DOMNode|String} view
*/
minify( view)
{
view = jQuery(view)
.addClass('mini')
// Dialog changes these, if resized
.width('100%').css('height', 'inherit')
.show();
jQuery('th', view).hide();
jQuery('td.equal',view).hide()
.prevAll().hide();
}
/**
* Expand mini-diff
*
* @param {DOMNode|String} view
*/
un_minify( view)
{
jQuery(view).removeClass('mini').show();
jQuery('th',view).show();
jQuery('td.equal',view).show();
}
/**
* Code for implementing et2_IDetachedDOM
* Fast-clonable read-only widget that only deals with DOM nodes, not the widget tree
*/
/**
* Build a list of attributes which can be set when working in the
* "detached" mode in the _attrs array which is provided
* by the calling code.
*
* @param {object} _attrs
*/
getDetachedAttributes(_attrs)
{
_attrs.push("value", "label");
}
/**
* Returns an array of DOM nodes. The (relativly) same DOM-Nodes have to be
* passed to the "setDetachedAttributes" function in the same order.
*/
getDetachedNodes()
{
return [this.div];
}
/**
* Sets the given associative attribute->value array and applies the
* attributes to the given DOM-Node.
*
* @param _nodes is an array of nodes which has 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, _values)
{
this.div = _nodes[0];
if(typeof _values['label'] != 'undefined')
{
this.set_label(_values['label']);
}
if(typeof _values['value'] != 'undefined')
{
this.set_value(_values['value']);
}
}
}
et2_register_widget(et2_diff, ["diff"]);

View File

@ -1,3 +1,4 @@
"use strict";
/**
* EGroupware eTemplate2 - JS Dropdown Button object
*
@ -9,13 +10,28 @@
* @copyright Nathan Gray 2013
* @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
/vendor/bower-asset/jquery/dist/jquery.js;
/vendor/bower-asset/jquery-ui/jquery-ui.js;
et2_baseWidget;
/vendor/bower-asset/jquery/dist/jquery.js;
/vendor/bower-asset/jquery-ui/jquery-ui.js;
et2_baseWidget;
*/
var et2_core_inputWidget_1 = require("./et2_core_inputWidget");
var et2_core_widget_1 = require("./et2_core_widget");
var et2_core_inheritance_1 = require("./et2_core_inheritance");
/**
* A split button - a button with a dropdown list
*
@ -30,393 +46,337 @@
*
* @augments et2_inputWidget
*/
var et2_dropdown_button = (function(){ "use strict"; return et2_inputWidget.extend(
{
attributes: {
"label": {
"name": "caption",
"type": "string",
"description": "Label of the button",
"translate": true,
"default": "Select..."
},
"label_updates": {
"name": "Label updates",
"type": "boolean",
"description": "Button label updates when an option is selected from the menu",
"default": true
},
"image": {
"name": "Icon",
"type": "string",
"description": "Add an icon"
},
"ro_image": {
"name": "Read-only Icon",
"type": "string",
"description": "Use this icon instead of hiding for read-only"
},
"onclick": {
"description": "JS code which gets executed when the button is clicked"
},
"select_options": {
"type": "any",
"name": "Select options",
"default": {},
"description": "Select options for dropdown. Can be a simple key => value list, or value can be full HTML",
// Skip normal initialization for this one
"ignore": true
},
"accesskey": {
"name": "Access Key",
"type": "string",
"default": et2_no_init,
"description": "Alt + <key> activates widget"
},
"tabindex": {
"name": "Tab index",
"type": "integer",
"default": et2_no_init,
"description": "Specifies the tab order of a widget when the 'tab' button is used for navigating."
},
// No such thing as a required button
"required": {
"ignore": true
}
},
internal_ids: {
div: "",
button: "",
menu: ""
},
div: null,
buttons: null,
button: null,
menu: null,
/**
* Default menu, so there is something for the widget browser / editor to show
*/
default_menu: '<ul> \
<li data-id="opt_1.1"><a href="#">Option-1.1</a></li>\
<li data-id="opt_1.2"><a href="#">Option-1.2</a></li>\
<li data-id="opt_1.3"><a href="#">Option-1.3</a></li>\
<li data-id="opt_1.4"><a href="#">Option-1.4<br>\
<small>with second line</small>\
</a></li>\
<li data-id="opt_1.5"><a href="#">Option-1.5</a></li>\
</ul>',
/**
* Constructor
*
* @memberOf et2_dropdown_button
*/
init: function() {
this._super.apply(this, arguments);
this.clicked = false;
var self = this;
// Create the individual UI elements
// Menu is a UL
this.menu = jQuery(this.default_menu).attr("id",this.internal_ids.menu)
.hide()
.menu({
select: function(event,ui) {
self.onselect.call(self,event,ui.item);
}
});
this.buttons = jQuery(document.createElement("div"))
.addClass("et2_dropdown");
// Main "wrapper" div
this.div = jQuery(document.createElement("div"))
.attr("id", this.internal_ids.div)
.append(this.buttons)
.append(this.menu);
// Left side - activates click action
this.button = jQuery(document.createElement("button"))
.attr("id", this.internal_ids.button)
.attr("type", "button")
.addClass("ui-widget ui-corner-left").removeClass("ui-corner-all")
.appendTo(this.buttons);
// Right side - shows dropdown
this.arrow = jQuery(document.createElement("button"))
.addClass("ui-widget ui-corner-right").removeClass("ui-corner-all")
.attr("type", "button")
.click(function() {
// ignore click on readonly button
if (self.options.readonly) return false;
// Clicking it again hides menu
if(self.menu.is(":visible"))
{
self.menu.hide();
return false;
}
// Show menu dropdown
var menu = self.menu.show().position({
my: "left top",
at: "left bottom",
of: self.buttons
});
// Hide menu if clicked elsewhere
jQuery( document ).one( "click", function() {
menu.hide();
});
return false;
})
// This is the actual down arrow icon
.append("<div class='ui-icon ui-icon-triangle-1-s'/>")
.appendTo(this.buttons);
// Common button UI
this.buttons.children("button")
.addClass("ui-state-default")
.hover(
function() {jQuery(this).addClass("ui-state-hover");},
function() {jQuery(this).removeClass("ui-state-hover");}
);
// Icon
this.image = jQuery(document.createElement("img"));
this.setDOMNode(this.div[0]);
},
destroy: function() {
// Destroy widget
if(this.menu && this.menu.data('ui-menu')) this.menu.menu("destroy");
// Null children
this.image = null;
this.button = null;
this.arrow = null;
this.buttons = null;
this.menu = null;
// Remove
this.div.empty().remove();
},
set_id: function(_id) {
this._super.apply(this, arguments);
// Update internal IDs - not really needed since we refer by internal
// javascript reference, but good to keep up to date
this.internal_ids = {
div: this.dom_id + "_wrapper",
button: this.dom_id,
menu: this.dom_id + "_menu"
};
for(var key in this.internal_ids)
{
if(this[key] == null) continue;
this[key].attr("id", this.internal_ids[key]);
}
},
/**
* Set if the button label changes to match the selected option
*
* @param updates boolean Turn updating on or off
*/
set_label_updates: function(updates) {
this.label_updates = updates;
},
set_accesskey: function(key) {
jQuery(this.node).attr("accesskey", key);
},
set_ro_image: function(_image) {
if(this.options.readonly)
{
this.set_image(_image);
}
},
set_image: function(_image) {
if(!this.isInTree() || this.image == null) return;
var found_image = false;
if(!_image.trim())
{
this.image.hide();
}
else
{
this.image.show();
}
var src = this.egw().image(_image);
if(src)
{
this.image.attr("src", src);
found_image = true;
}
// allow url's too
else if (_image[0] == '/' || _image.substr(0,4) == 'http')
{
this.image.attr('src', _image);
found_image = true;
}
else
{
this.image.hide();
}
},
/**
* Overwritten to maintain an internal clicked attribute
*
* @param _ev
* @returns {Boolean}
*/
click: function(_ev) {
// ignore click on readonly button
if (this.options.readonly) return false;
this.clicked = true;
if (!this._super.apply(this, arguments))
{
this.clicked = false;
return false;
}
this.clicked = false;
return true;
},
onselect: function(event, selected_node) {
this.set_value(selected_node.attr("data-id"));
this.change(selected_node);
},
attachToDOM: function() {
this._super.apply(this, arguments);
// Move the parent's handler to the button, or we can't tell the difference between the clicks
jQuery(this.node).unbind("click.et2_baseWidget");
this.button.off().bind("click.et2_baseWidget", this, function(e) {
return e.data.click.call(e.data, this);
});
},
set_label: function(_value) {
if (this.button)
{
this.label = _value;
this.button.text(_value)
.prepend(this.image);
}
},
/**
* Set the options for the dropdown
*
* @param options Object ID => Label pairs
*/
set_select_options: function(options) {
this.menu.first().empty();
// Allow more complicated content, if passed
if(typeof options == "string")
{
this.menu.append(options);
}
else
{
var add_complex = function(node, options)
{
for(var key in options)
{
var item;
if(typeof options[key] == "string")
{
item = jQuery("<li data-id='"+key+"'><a href='#'>"+options[key]+"</a></li>");
}
else if (options[key]["label"])
{
item =jQuery("<li data-id='"+key+"'><a href='#'>"+options[key]["label"]+"</a></li>");
}
// Optgroup
else
{
item = jQuery("<li><a href='#'>"+key+"</a></li>");
add_complex(node.append("<ul>"), options[key]);
}
node.append(item);
if(item && options[key].icon)
{
// we supply a applicable class for item images
jQuery('a',item).prepend('<img class="et2_button_icon" src="' + (options[key].icon.match(/^(http|https|\/)/) ? options[key].icon : egw.image(options[key].icon)) +'"/>');
}
}
}
add_complex(this.menu.first(), options);
}
this.menu.menu("refresh");
},
/**
* Set tab index
*/
set_tabindex: function(index) {
jQuery(this.button).attr("tabindex", index);
},
set_value: function(new_value) {
var menu_item = jQuery("[data-id='"+new_value+"']",this.menu);
if(menu_item.length)
{
this.value = new_value;
if(this.label_updates)
{
this.set_label(menu_item.text());
}
}
else
{
this.value = null;
if(this.label_updates)
{
this.set_label(this.options.label);
}
}
},
getValue: function() {
return this.value;
},
/**
* Set options.readonly
*
* @param {boolean} _ro
*/
set_readonly: function(_ro)
{
if (_ro != this.options.readonly)
{
this.options.readonly = _ro;
// don't make readonly dropdown buttons clickable
if (this.buttons)
{
this.buttons.find('button')
.toggleClass('et2_clickable', !_ro)
.toggleClass('et2_button_ro', _ro)
.css('cursor', _ro ? 'default' : 'pointer');
}
}
}
});}).call(this);
et2_register_widget(et2_dropdown_button, ["dropdown_button"]);
var et2_dropdown_button = /** @class */ (function (_super) {
__extends(et2_dropdown_button, _super);
/**
* Constructor
*
* @memberOf et2_dropdown_button
*/
function et2_dropdown_button(_parent, _attrs, _child) {
var _this = _super.call(this, _parent, _attrs, et2_core_inheritance_1.ClassWithAttributes.extendAttributes(et2_dropdown_button._attributes, _child || {})) || this;
_this.internal_ids = {
div: "",
button: "",
menu: ""
};
_this.div = null;
_this.buttons = null;
_this.button = null;
_this.arrow = null;
_this.menu = null;
_this.image = null;
_this.clicked = false;
_this.label_updates = true;
_this.value = null;
/**
* Default menu, so there is something for the widget browser / editor to show
*/
_this.default_menu = '<ul> \
<li data-id="opt_1.1"><a href="#">Option-1.1</a></li>\
<li data-id="opt_1.2"><a href="#">Option-1.2</a></li>\
<li data-id="opt_1.3"><a href="#">Option-1.3</a></li>\
<li data-id="opt_1.4"><a href="#">Option-1.4<br>\
<small>with second line</small>\
</a></li>\
<li data-id="opt_1.5"><a href="#">Option-1.5</a></li>\
</ul>';
_this.clicked = false;
var self = _this;
// Create the individual UI elements
// Menu is a UL
_this.menu = jQuery(_this.default_menu).attr("id", _this.internal_ids.menu)
.hide()
.menu({
select: function (event, ui) {
self.onselect.call(self, event, ui.item);
}
});
_this.buttons = jQuery(document.createElement("div"))
.addClass("et2_dropdown");
// Main "wrapper" div
_this.div = jQuery(document.createElement("div"))
.attr("id", _this.internal_ids.div)
.append(_this.buttons)
.append(_this.menu);
// Left side - activates click action
_this.button = jQuery(document.createElement("button"))
.attr("id", _this.internal_ids.button)
.attr("type", "button")
.addClass("ui-widget ui-corner-left").removeClass("ui-corner-all")
.appendTo(_this.buttons);
// Right side - shows dropdown
_this.arrow = jQuery(document.createElement("button"))
.addClass("ui-widget ui-corner-right").removeClass("ui-corner-all")
.attr("type", "button")
.click(function () {
// ignore click on readonly button
if (self.options.readonly)
return false;
// Clicking it again hides menu
if (self.menu.is(":visible")) {
self.menu.hide();
return false;
}
// Show menu dropdown
var menu = self.menu.show().position({
my: "left top",
at: "left bottom",
of: self.buttons
});
// Hide menu if clicked elsewhere
jQuery(document).one("click", function () {
menu.hide();
});
return false;
})
// This is the actual down arrow icon
.append("<div class='ui-icon ui-icon-triangle-1-s'/>")
.appendTo(_this.buttons);
// Common button UI
_this.buttons.children("button")
.addClass("ui-state-default")
.hover(function () { jQuery(this).addClass("ui-state-hover"); }, function () { jQuery(this).removeClass("ui-state-hover"); });
// Icon
_this.image = jQuery(document.createElement("img"));
_this.setDOMNode(_this.div[0]);
return _this;
}
et2_dropdown_button.prototype.destroy = function () {
// Destroy widget
if (this.menu && this.menu.data('ui-menu'))
this.menu.menu("destroy");
// Null children
this.image = null;
this.button = null;
this.arrow = null;
this.buttons = null;
this.menu = null;
// Remove
this.div.empty().remove();
};
et2_dropdown_button.prototype.set_id = function (_id) {
_super.prototype.set_id.call(this, _id);
// Update internal IDs - not really needed since we refer by internal
// javascript reference, but good to keep up to date
this.internal_ids = {
div: this.dom_id + "_wrapper",
button: this.dom_id,
menu: this.dom_id + "_menu"
};
for (var key in this.internal_ids) {
if (this[key] == null)
continue;
this[key].attr("id", this.internal_ids[key]);
}
};
/**
* Set if the button label changes to match the selected option
*
* @param updates boolean Turn updating on or off
*/
et2_dropdown_button.prototype.set_label_updates = function (updates) {
this.label_updates = updates;
};
et2_dropdown_button.prototype.set_accesskey = function (key) {
jQuery(this.node).attr("accesskey", key);
};
et2_dropdown_button.prototype.set_ro_image = function (_image) {
if (this.options.readonly) {
this.set_image(_image);
}
};
et2_dropdown_button.prototype.set_image = function (_image) {
if (!this.isInTree() || this.image == null)
return;
if (!_image.trim()) {
this.image.hide();
}
else {
this.image.show();
}
var src = this.egw().image(_image);
if (src) {
this.image.attr("src", src);
}
// allow url's too
else if (_image[0] == '/' || _image.substr(0, 4) == 'http') {
this.image.attr('src', _image);
}
else {
this.image.hide();
}
};
/**
* Overwritten to maintain an internal clicked attribute
*
* @param _ev
* @returns {Boolean}
*/
et2_dropdown_button.prototype.click = function (_ev) {
// ignore click on readonly button
if (this.options.readonly)
return false;
this.clicked = true;
if (!_super.prototype.click.call(this, _ev)) {
this.clicked = false;
return false;
}
this.clicked = false;
return true;
};
et2_dropdown_button.prototype.onselect = function (event, selected_node) {
this.set_value(selected_node.attr("data-id"));
this.change(selected_node);
};
et2_dropdown_button.prototype.attachToDOM = function () {
var res = _super.prototype.attachToDOM.call(this);
// Move the parent's handler to the button, or we can't tell the difference between the clicks
jQuery(this.node).unbind("click.et2_baseWidget");
this.button.off().bind("click.et2_baseWidget", this, function (e) {
return e.data.click.call(e.data, this);
});
return res;
};
et2_dropdown_button.prototype.set_label = function (_value) {
if (this.button) {
this.label = _value;
this.button.text(_value)
.prepend(this.image);
}
};
/**
* Set the options for the dropdown
*
* @param options Object ID => Label pairs
*/
et2_dropdown_button.prototype.set_select_options = function (options) {
this.menu.first().empty();
// Allow more complicated content, if passed
if (typeof options == "string") {
this.menu.append(options);
}
else {
var add_complex_1 = function (node, options) {
for (var key in options) {
var item = void 0;
if (typeof options[key] == "string") {
item = jQuery("<li data-id='" + key + "'><a href='#'>" + options[key] + "</a></li>");
}
else if (options[key]["label"]) {
item = jQuery("<li data-id='" + key + "'><a href='#'>" + options[key]["label"] + "</a></li>");
}
// Optgroup
else {
item = jQuery("<li><a href='#'>" + key + "</a></li>");
add_complex_1(node.append("<ul>"), options[key]);
}
node.append(item);
if (item && options[key].icon) {
// we supply a applicable class for item images
jQuery('a', item).prepend('<img class="et2_button_icon" src="' + (options[key].icon.match(/^(http|https|\/)/) ? options[key].icon : egw.image(options[key].icon)) + '"/>');
}
}
};
add_complex_1(this.menu.first(), options);
}
this.menu.menu("refresh");
};
/**
* Set tab index
*/
et2_dropdown_button.prototype.set_tabindex = function (index) {
jQuery(this.button).attr("tabindex", index);
};
et2_dropdown_button.prototype.set_value = function (new_value) {
var menu_item = jQuery("[data-id='" + new_value + "']", this.menu);
if (menu_item.length) {
this.value = new_value;
if (this.label_updates) {
this.set_label(menu_item.text());
}
}
else {
this.value = null;
if (this.label_updates) {
this.set_label(this.options.label);
}
}
};
et2_dropdown_button.prototype.getValue = function () {
return this.value;
};
/**
* Set options.readonly
*
* @param {boolean} _ro
*/
et2_dropdown_button.prototype.set_readonly = function (_ro) {
if (_ro != this.options.readonly) {
this.options.readonly = _ro;
// don't make readonly dropdown buttons clickable
if (this.buttons) {
this.buttons.find('button')
.toggleClass('et2_clickable', !_ro)
.toggleClass('et2_button_ro', _ro)
.css('cursor', _ro ? 'default' : 'pointer');
}
}
};
et2_dropdown_button.attributes = {
"label": {
"name": "caption",
"type": "string",
"description": "Label of the button",
"translate": true,
"default": "Select..."
},
"label_updates": {
"name": "Label updates",
"type": "boolean",
"description": "Button label updates when an option is selected from the menu",
"default": true
},
"image": {
"name": "Icon",
"type": "string",
"description": "Add an icon"
},
"ro_image": {
"name": "Read-only Icon",
"type": "string",
"description": "Use this icon instead of hiding for read-only"
},
"onclick": {
"description": "JS code which gets executed when the button is clicked"
},
"select_options": {
"type": "any",
"name": "Select options",
"default": {},
"description": "Select options for dropdown. Can be a simple key => value list, or value can be full HTML",
// Skip normal initialization for this one
"ignore": true
},
"accesskey": {
"name": "Access Key",
"type": "string",
"default": et2_no_init,
"description": "Alt + <key> activates widget"
},
"tabindex": {
"name": "Tab index",
"type": "integer",
"default": et2_no_init,
"description": "Specifies the tab order of a widget when the 'tab' button is used for navigating."
},
// No such thing as a required button
"required": {
"ignore": true
}
};
return et2_dropdown_button;
}(et2_core_inputWidget_1.et2_inputWidget));
exports.et2_dropdown_button = et2_dropdown_button;
et2_core_widget_1.et2_register_widget(et2_dropdown_button, ["dropdown_button"]);
//# sourceMappingURL=et2_widget_dropdown_button.js.map

View File

@ -0,0 +1,441 @@
/**
* EGroupware eTemplate2 - JS Dropdown Button object
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage api
* @link http://www.egroupware.org
* @author Nathan Gray
* @copyright Nathan Gray 2013
* @version $Id$
*/
/*egw:uses
/vendor/bower-asset/jquery/dist/jquery.js;
/vendor/bower-asset/jquery-ui/jquery-ui.js;
et2_baseWidget;
*/
import {et2_inputWidget} from './et2_core_inputWidget';
import {WidgetConfig, et2_register_widget} from "./et2_core_widget";
import {ClassWithAttributes} from "./et2_core_inheritance";
/**
* A split button - a button with a dropdown list
*
* There are several parts to the button UI:
* - Container: This is what is percieved as the dropdown button, the whole package together
* - Button: The part on the left that can be clicked
* - Arrow: The button to display the choices
* - Menu: The list of choices
*
* Menu options are passed via the select_options. They are normally ID => Title pairs,
* as for a select box, but the title can also be full HTML if needed.
*
* @augments et2_inputWidget
*/
export class et2_dropdown_button extends et2_inputWidget
{
static readonly attributes : any = {
"label": {
"name": "caption",
"type": "string",
"description": "Label of the button",
"translate": true,
"default": "Select..."
},
"label_updates": {
"name": "Label updates",
"type": "boolean",
"description": "Button label updates when an option is selected from the menu",
"default": true
},
"image": {
"name": "Icon",
"type": "string",
"description": "Add an icon"
},
"ro_image": {
"name": "Read-only Icon",
"type": "string",
"description": "Use this icon instead of hiding for read-only"
},
"onclick": {
"description": "JS code which gets executed when the button is clicked"
},
"select_options": {
"type": "any",
"name": "Select options",
"default": {},
"description": "Select options for dropdown. Can be a simple key => value list, or value can be full HTML",
// Skip normal initialization for this one
"ignore": true
},
"accesskey": {
"name": "Access Key",
"type": "string",
"default": et2_no_init,
"description": "Alt + <key> activates widget"
},
"tabindex": {
"name": "Tab index",
"type": "integer",
"default": et2_no_init,
"description": "Specifies the tab order of a widget when the 'tab' button is used for navigating."
},
// No such thing as a required button
"required": {
"ignore": true
}
};
internal_ids : any = {
div: "",
button: "",
menu: ""
};
div : JQuery = null;
buttons : JQuery = null;
button : JQuery = null;
arrow : JQuery = null;
menu : JQuery = null;
image : JQuery = null;
clicked : boolean = false;
label_updates : boolean = true;
value : any = null;
/**
* Default menu, so there is something for the widget browser / editor to show
*/
readonly default_menu : string = '<ul> \
<li data-id="opt_1.1"><a href="#">Option-1.1</a></li>\
<li data-id="opt_1.2"><a href="#">Option-1.2</a></li>\
<li data-id="opt_1.3"><a href="#">Option-1.3</a></li>\
<li data-id="opt_1.4"><a href="#">Option-1.4<br>\
<small>with second line</small>\
</a></li>\
<li data-id="opt_1.5"><a href="#">Option-1.5</a></li>\
</ul>';
/**
* Constructor
*
* @memberOf et2_dropdown_button
*/
constructor(_parent?, _attrs? : WidgetConfig, _child? : object) {
super(_parent, _attrs, ClassWithAttributes.extendAttributes(et2_dropdown_button._attributes, _child || {}));
this.clicked = false;
let self = this;
// Create the individual UI elements
// Menu is a UL
this.menu = jQuery(this.default_menu).attr("id",this.internal_ids.menu)
.hide()
.menu({
select: function(event,ui) {
self.onselect.call(self,event,ui.item);
}
});
this.buttons = jQuery(document.createElement("div"))
.addClass("et2_dropdown");
// Main "wrapper" div
this.div = jQuery(document.createElement("div"))
.attr("id", this.internal_ids.div)
.append(this.buttons)
.append(this.menu);
// Left side - activates click action
this.button = jQuery(document.createElement("button"))
.attr("id", this.internal_ids.button)
.attr("type", "button")
.addClass("ui-widget ui-corner-left").removeClass("ui-corner-all")
.appendTo(this.buttons);
// Right side - shows dropdown
this.arrow = jQuery(document.createElement("button"))
.addClass("ui-widget ui-corner-right").removeClass("ui-corner-all")
.attr("type", "button")
.click(function() {
// ignore click on readonly button
if (self.options.readonly) return false;
// Clicking it again hides menu
if(self.menu.is(":visible"))
{
self.menu.hide();
return false;
}
// Show menu dropdown
var menu = self.menu.show().position({
my: "left top",
at: "left bottom",
of: self.buttons
});
// Hide menu if clicked elsewhere
jQuery( document ).one( "click", function() {
menu.hide();
});
return false;
})
// This is the actual down arrow icon
.append("<div class='ui-icon ui-icon-triangle-1-s'/>")
.appendTo(this.buttons);
// Common button UI
this.buttons.children("button")
.addClass("ui-state-default")
.hover(
function() {jQuery(this).addClass("ui-state-hover");},
function() {jQuery(this).removeClass("ui-state-hover");}
);
// Icon
this.image = jQuery(document.createElement("img"));
this.setDOMNode(this.div[0]);
}
destroy() {
// Destroy widget
if(this.menu && this.menu.data('ui-menu')) this.menu.menu("destroy");
// Null children
this.image = null;
this.button = null;
this.arrow = null;
this.buttons = null;
this.menu = null;
// Remove
this.div.empty().remove();
}
set_id(_id) {
super.set_id(_id);
// Update internal IDs - not really needed since we refer by internal
// javascript reference, but good to keep up to date
this.internal_ids = {
div: this.dom_id + "_wrapper",
button: this.dom_id,
menu: this.dom_id + "_menu"
};
for(let key in this.internal_ids)
{
if(this[key] == null) continue;
this[key].attr("id", this.internal_ids[key]);
}
}
/**
* Set if the button label changes to match the selected option
*
* @param updates boolean Turn updating on or off
*/
set_label_updates(updates)
{
this.label_updates = updates;
}
set_accesskey(key)
{
jQuery(this.node).attr("accesskey", key);
}
set_ro_image(_image)
{
if(this.options.readonly)
{
this.set_image(_image);
}
}
set_image(_image)
{
if(!this.isInTree() || this.image == null) return;
if(!_image.trim())
{
this.image.hide();
}
else
{
this.image.show();
}
let src = this.egw().image(_image);
if(src)
{
this.image.attr("src", src);
}
// allow url's too
else if (_image[0] == '/' || _image.substr(0,4) == 'http')
{
this.image.attr('src', _image);
}
else
{
this.image.hide();
}
}
/**
* Overwritten to maintain an internal clicked attribute
*
* @param _ev
* @returns {Boolean}
*/
click(_ev)
{
// ignore click on readonly button
if (this.options.readonly) return false;
this.clicked = true;
if (!super.click(_ev))
{
this.clicked = false;
return false;
}
this.clicked = false;
return true;
}
onselect(event, selected_node)
{
this.set_value(selected_node.attr("data-id"));
this.change(selected_node);
}
attachToDOM()
{
let res = super.attachToDOM();
// Move the parent's handler to the button, or we can't tell the difference between the clicks
jQuery(this.node).unbind("click.et2_baseWidget");
this.button.off().bind("click.et2_baseWidget", this, function(e) {
return e.data.click.call(e.data, this);
});
return res;
}
set_label(_value)
{
if (this.button)
{
this.label = _value;
this.button.text(_value)
.prepend(this.image);
}
}
/**
* Set the options for the dropdown
*
* @param options Object ID => Label pairs
*/
set_select_options(options) {
this.menu.first().empty();
// Allow more complicated content, if passed
if(typeof options == "string")
{
this.menu.append(options);
}
else
{
let add_complex = function(node, options)
{
for(let key in options)
{
let item;
if(typeof options[key] == "string")
{
item = jQuery("<li data-id='"+key+"'><a href='#'>"+options[key]+"</a></li>");
}
else if (options[key]["label"])
{
item =jQuery("<li data-id='"+key+"'><a href='#'>"+options[key]["label"]+"</a></li>");
}
// Optgroup
else
{
item = jQuery("<li><a href='#'>"+key+"</a></li>");
add_complex(node.append("<ul>"), options[key]);
}
node.append(item);
if(item && options[key].icon)
{
// we supply a applicable class for item images
jQuery('a',item).prepend('<img class="et2_button_icon" src="' + (options[key].icon.match(/^(http|https|\/)/) ? options[key].icon : egw.image(options[key].icon)) +'"/>');
}
}
}
add_complex(this.menu.first(), options);
}
this.menu.menu("refresh");
}
/**
* Set tab index
*/
set_tabindex(index)
{
jQuery(this.button).attr("tabindex", index);
}
set_value(new_value)
{
let menu_item = jQuery("[data-id='"+new_value+"']",this.menu);
if(menu_item.length)
{
this.value = new_value;
if(this.label_updates)
{
this.set_label(menu_item.text());
}
}
else
{
this.value = null;
if(this.label_updates)
{
this.set_label(this.options.label);
}
}
}
getValue()
{
return this.value;
}
/**
* Set options.readonly
*
* @param {boolean} _ro
*/
set_readonly(_ro : boolean)
{
if (_ro != this.options.readonly)
{
this.options.readonly = _ro;
// don't make readonly dropdown buttons clickable
if (this.buttons)
{
this.buttons.find('button')
.toggleClass('et2_clickable', !_ro)
.toggleClass('et2_button_ro', _ro)
.css('cursor', _ro ? 'default' : 'pointer');
}
}
}
}
et2_register_widget(et2_dropdown_button, ["dropdown_button"]);

View File

@ -0,0 +1,157 @@
"use strict";
/**
* EGroupware eTemplate2 - JS Dynheight object
*
* @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
* @version $Id$
*/
Object.defineProperty(exports, "__esModule", { value: true });
/*egw:use
/vendor/bower-asset/jquery/dist/jquery.js;
et2_core_inheritance;
*/
/**
* Object which resizes an inner node to the maximum extend of an outer node
* (without creating a scrollbar) - it achieves that by performing some very
* nasty and time consuming calculations.
*/
var et2_dynheight = /** @class */ (function () {
function et2_dynheight(_outerNode, _innerNode, _minHeight) {
this.initialized = false;
this.minHeight = 0;
this.bottomNodes = [];
this.innerMargin = 0;
this.outerMargin = 0;
this.outerNode = jQuery(_outerNode);
this.innerNode = jQuery(_innerNode);
this.minHeight = _minHeight;
}
et2_dynheight.prototype.destroy = function () {
this.outerNode = null;
this.innerNode = null;
this.bottomNodes = [];
};
/**
* Resizes the inner node. When this is done, the callback function is
* called.
*
* @param {function} _callback
* @param {object} _context
*/
et2_dynheight.prototype.update = function (_callback, _context) {
// Check whether the inner node is actually visible - if not, don't
// trigger the callback function
if (this.innerNode.is(":visible")) {
// Initialize the height calculation
this._initialize();
// Get the outer container height and offset, if available
var oh = this.outerNode.height();
var ot = this.outerNode.offset() ? this.outerNode.offset().top : 0;
// Get top and height of the inner node
var it = this.innerNode.offset().top;
// Calculate the height of the "bottomNodes"
var bminTop = this.bottomNodes.length ? Infinity : 0;
var bmaxBot = 0;
for (var i = 0; i < this.bottomNodes.length; i++) {
// Ignore hidden popups
if (this.bottomNodes[i].find('.action_popup').length) {
egw.debug('warn', "Had to skip a hidden popup - it should be removed", this.bottomNodes[i].find('.action_popup'));
continue;
}
// Ignore other hidden nodes
if (!this.bottomNodes[i].is(':visible'))
continue;
// Get height, top and bottom and calculate the maximum/minimum
var bh_1 = this.bottomNodes[i].outerHeight(true);
var bt = this.bottomNodes[i].offset().top;
var bb = bh_1 + bt;
if (i == 0 || bminTop > bt) {
bminTop = bt;
}
if (i == 0 || bmaxBot < bb) {
bmaxBot = bb;
}
}
// Get the height of the bottom container
var bh = Math.max(0, bmaxBot - bminTop);
// Calculate the new height of the inner container
var h = Math.max(this.minHeight, oh + ot - it - bh -
this.innerMargin - this.outerMargin);
this.innerNode.height(h);
// Update the width
// Some checking to make sure it doesn't overflow the width when user
// resizes the window
var w = this.outerNode.width();
if (w > jQuery(window).width()) {
// 50px border, totally arbitrary, but we just need to make sure it's inside
w = jQuery(window).width() - 50;
}
if (w != this.innerNode.outerWidth()) {
this.innerNode.width(w);
}
// Call the callback function
if (typeof _callback != "undefined") {
_callback.call(_context, w, h);
}
}
};
/**
* Function used internally which collects all DOM-Nodes which are located
* below this element.
*
* @param {HTMLElement} _node
* @param {number} _bottom
*/
et2_dynheight.prototype._collectBottomNodes = function (_node, _bottom) {
// Calculate the bottom position of the inner node
if (typeof _bottom == "undefined") {
_bottom = this.innerNode.offset().top + this.innerNode.height();
}
if (_node) {
// Accumulate the outer margin of the parent elements
var node = jQuery(_node);
var ooh = node.outerHeight(true);
var oh = node.height();
this.outerMargin += (ooh - oh) / 2; // Divide by 2 as the value contains margin-top and -bottom
// Iterate over the children of the given node and do the same
// recursively to the parent nodes until the _outerNode or body is
// reached.
var self_1 = this;
jQuery(_node).children().each(function () {
var $this = jQuery(this);
var top = $this.offset().top;
if (this != self_1.innerNode[0] && top >= _bottom) {
self_1.bottomNodes.push($this);
}
});
if (_node != this.outerNode[0] && _node != jQuery("body")[0]) {
this._collectBottomNodes(_node.parentNode, _bottom);
}
}
};
/**
* Used internally to calculate some information which will not change over
* the time.
*/
et2_dynheight.prototype._initialize = function () {
if (!this.initialized) {
// Collect all bottomNodes and calculates the outer margin
this.bottomNodes = [];
this.outerMargin = 0;
this._collectBottomNodes(this.innerNode[0].parentNode);
// Calculate the inner margin
var ioh = this.innerNode.outerHeight(true);
var ih = this.innerNode.height();
this.innerMargin = ioh - ih;
this.initialized = true;
}
};
return et2_dynheight;
}());
exports.et2_dynheight = et2_dynheight;
//# sourceMappingURL=et2_widget_dynheight.js.map

View File

@ -19,32 +19,32 @@
* Object which resizes an inner node to the maximum extend of an outer node
* (without creating a scrollbar) - it achieves that by performing some very
* nasty and time consuming calculations.
*
* @augments Class
*/
var et2_dynheight = (function(){ "use strict"; return Class.extend(
export class et2_dynheight
{
/**
* Constructor for the dynheight object
*
* @param _outerNode is the node which surrounds the _innerNode and to
* which extend the innerNode should be expanded without creating a
* scrollbar. Note: The outer node must be a parent of the inner node.
* @param _innerNode is the node which should be scaled. Call update to
* scale the node.
* @param _minHeight is the minimum height the inner node should have
* @memberOf et2_dynheight
*/
init: function(_outerNode, _innerNode, _minHeight) {
private initialized: boolean = false;
private outerNode: JQuery;
private innerNode: JQuery;
private minHeight: number = 0;
private bottomNodes: any[] = [];
private innerMargin: number = 0;
private outerMargin: number = 0;
constructor(_outerNode, _innerNode, _minHeight)
{
this.outerNode = jQuery(_outerNode);
this.innerNode = jQuery(_innerNode);
this.minHeight = _minHeight;
}
destroy()
{
this.outerNode = null;
this.innerNode = null;
this.bottomNodes = [];
this.initialized = false;
this.innerMargin = 0;
this.outerMargin = 0;
},
}
/**
* Resizes the inner node. When this is done, the callback function is
@ -53,7 +53,8 @@ var et2_dynheight = (function(){ "use strict"; return Class.extend(
* @param {function} _callback
* @param {object} _context
*/
update: function(_callback, _context) {
update( _callback, _context)
{
// Check whether the inner node is actually visible - if not, don't
// trigger the callback function
if (this.innerNode.is(":visible"))
@ -62,16 +63,16 @@ var et2_dynheight = (function(){ "use strict"; return Class.extend(
this._initialize();
// Get the outer container height and offset, if available
var oh = this.outerNode.height();
var ot = this.outerNode.offset() ? this.outerNode.offset().top : 0;
const oh = this.outerNode.height();
const ot = this.outerNode.offset() ? this.outerNode.offset().top : 0;
// Get top and height of the inner node
var it = this.innerNode.offset().top;
const it = this.innerNode.offset().top;
// Calculate the height of the "bottomNodes"
var bminTop = this.bottomNodes.length ? Infinity : 0;
var bmaxBot = 0;
for (var i = 0; i < this.bottomNodes.length; i++)
let bminTop = this.bottomNodes.length ? Infinity : 0;
let bmaxBot = 0;
for (let i = 0; i < this.bottomNodes.length; i++)
{
// Ignore hidden popups
if(this.bottomNodes[i].find('.action_popup').length)
@ -85,9 +86,9 @@ var et2_dynheight = (function(){ "use strict"; return Class.extend(
if(!this.bottomNodes[i].is(':visible')) continue;
// Get height, top and bottom and calculate the maximum/minimum
var bh = this.bottomNodes[i].outerHeight(true);
var bt = this.bottomNodes[i].offset().top;
var bb = bh + bt;
let bh = this.bottomNodes[i].outerHeight(true);
let bt = this.bottomNodes[i].offset().top;
const bb = bh + bt;
if (i == 0 || bminTop > bt)
{
@ -101,17 +102,17 @@ var et2_dynheight = (function(){ "use strict"; return Class.extend(
}
// Get the height of the bottom container
var bh = Math.max(0,bmaxBot - bminTop);
const bh = Math.max(0, bmaxBot - bminTop);
// Calculate the new height of the inner container
var h = Math.max(this.minHeight, oh + ot - it - bh -
const h = Math.max(this.minHeight, oh + ot - it - bh -
this.innerMargin - this.outerMargin);
this.innerNode.height(h);
// Update the width
// Some checking to make sure it doesn't overflow the width when user
// resizes the window
var w = this.outerNode.width();
let w = this.outerNode.width();
if (w > jQuery(window).width())
{
// 50px border, totally arbitrary, but we just need to make sure it's inside
@ -128,16 +129,17 @@ var et2_dynheight = (function(){ "use strict"; return Class.extend(
_callback.call(_context, w, h);
}
}
},
}
/**
* Function used internally which collects all DOM-Nodes which are located
* below this element.
*
* @param {DOMElement} _node
* @param {HTMLElement} _node
* @param {number} _bottom
*/
_collectBottomNodes: function(_node, _bottom) {
_collectBottomNodes( _node : any, _bottom? : number)
{
// Calculate the bottom position of the inner node
if (typeof _bottom == "undefined")
{
@ -147,18 +149,18 @@ var et2_dynheight = (function(){ "use strict"; return Class.extend(
if (_node)
{
// Accumulate the outer margin of the parent elements
var node = jQuery(_node);
var ooh = node.outerHeight(true);
var oh = node.height();
const node = jQuery(_node);
const ooh = node.outerHeight(true);
const oh = node.height();
this.outerMargin += (ooh - oh) / 2; // Divide by 2 as the value contains margin-top and -bottom
// Iterate over the children of the given node and do the same
// recursively to the parent nodes until the _outerNode or body is
// reached.
var self = this;
const self = this;
jQuery(_node).children().each(function() {
var $this = jQuery(this);
var top = $this.offset().top;
const $this = jQuery(this);
const top = $this.offset().top;
if (this != self.innerNode[0] && top >= _bottom)
{
self.bottomNodes.push($this);
@ -170,13 +172,14 @@ var et2_dynheight = (function(){ "use strict"; return Class.extend(
this._collectBottomNodes(_node.parentNode, _bottom);
}
}
},
}
/**
* Used internally to calculate some information which will not change over
* the time.
*/
_initialize: function() {
_initialize( )
{
if (!this.initialized)
{
// Collect all bottomNodes and calculates the outer margin
@ -185,13 +188,12 @@ var et2_dynheight = (function(){ "use strict"; return Class.extend(
this._collectBottomNodes(this.innerNode[0].parentNode);
// Calculate the inner margin
var ioh = this.innerNode.outerHeight(true);
var ih = this.innerNode.height();
const ioh = this.innerNode.outerHeight(true);
const ih = this.innerNode.height();
this.innerMargin = ioh - ih;
this.initialized = true;
}
}
});}).call(this);
}

View File

@ -1,3 +1,4 @@
"use strict";
/*
* Egroupware etemplate2 JS Entry widget
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
@ -5,14 +6,27 @@
* @subpackage api
* @link http://www.egroupware.org
* @author Nathan Gray
* @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
et2_core_valueWidget;
et2_core_valueWidget;
*/
var et2_core_widget_1 = require("./et2_core_widget");
var et2_core_valueWidget_1 = require("./et2_core_valueWidget");
var et2_core_inheritance_1 = require("./et2_core_inheritance");
/**
* A widget to display a value from an entry
*
@ -27,151 +41,129 @@
*
* @augments et2_valueWidget
*/
var et2_entry = (function(){ "use strict"; return et2_valueWidget.extend(
{
attributes: {
field: {
'name': 'Fields',
'description': 'Which entry field to display, or "sum" to add up the alternate_fields',
'type': 'string'
},
compare: {
name: 'Compare',
description: 'if given, the selected field is compared with its value and an X is printed on equality, nothing otherwise',
default: et2_no_init,
type: 'string'
},
alternate_fields: {
name: 'Alternate fields',
description: 'colon (:) separated list of alternative fields. The first non-empty one is used if the selected field is empty, (-) used for subtraction',
type: 'string',
default: et2_no_init
},
precision: {
name: 'Decimals to be shown',
description: 'Specifies the number of decimals for sum of alternates, the default is 2',
type: 'string',
default: '2'
},
regex: {
name: 'Regular expression pattern',
description: 'Only used server-side in a preg_replace with regex_replace to modify the value',
default: et2_no_init,
type: 'string'
},
regex_replace: {
name: 'Regular expression replacement pattern',
description: 'Only used server-side in a preg_replace with regex to modify the value',
default: et2_no_init,
type: 'string'
},
value: {
type: 'any'
},
readonly: {
default: true
}
},
legacyOptions: ["field","compare","alternate_fields"],
prefix: '~',
/**
* Constructor
*
* @memberOf et2_customfields_list
*/
init: function(parent, attrs) {
// Often the ID conflicts, so check prefix
if(attrs.id && attrs.id.indexOf(this.prefix) < 0)
{
attrs.id = this.prefix + attrs.id;
}
var value = attrs.value;
this._super.apply(this, arguments);
// Save value from parsing, but only if set
if(value)
{
this.options.value = value;
}
this.widget = null;
this.setDOMNode(document.createElement('span'));
},
loadFromXML: function(_node) {
// Load the nodes as usual
this._super.apply(this, arguments);
// Do the magic
this.loadField();
},
/**
* Initialize widget for entry field
*/
loadField: function() {
// Create widget of correct type
var attrs = {
id: this.id + (this.options.field ? '[' +this.options.field+']' : ''),
type: 'label',
readonly: this.options.readonly
};
var modifications = this.getArrayMgr("modifications");
if(modifications && this.options.field)
{
jQuery.extend(attrs, modifications.getEntry(attrs.id));
}
// Supress labels on templates
if(attrs.type == 'template' && this.options.label)
{
this.egw().debug('log', "Surpressed label on <" + this._type + ' label="' + this.options.label + '" id="' + this.id + '"...>');
this.options.label = '';
}
var widget = et2_createWidget(attrs.type, attrs, this);
// If value is not set, etemplate takes care of everything
// If value was set, find the record explicitly.
if(typeof this.options.value == 'string')
{
widget.options.value = this.getArrayMgr('content').getEntry(this.id+'['+this.options.field+']') ||
this.getRoot().getArrayMgr('content').getEntry(this.prefix+this.options.value + '['+this.options.field+']');
}
else if (this.options.field && this.options.value && this.options.value[this.options.field])
{
widget.options.value = this.options.value[this.options.field];
}
if(this.options.compare)
{
widget.options.value = widget.options.value == this.options.compare ? 'X' : '';
}
if(this.options.alternate_fields)
{
var sum = 0;
var fields = this.options.alternate_fields.split(':');
for(var i = 0; i < fields.length; i++)
{
var negate = (fields[i][0] == "-");
var value = this.getArrayMgr('content').getEntry(fields[i].replace('-',''))
sum += typeof value === 'undefined' ? 0 : (parseFloat(value) * (negate ? -1 : 1));
if(value && this.options.field !== 'sum')
{
widget.options.value = value;
break;
}
}
if(this.options.field == 'sum')
{
if (this.options.precision && jQuery.isNumeric(sum)) sum = parseFloat(sum).toFixed(this.options.precision);
widget.options.value = sum;
}
}
}
});}).call(this);
et2_register_widget(et2_entry, ["entry", 'contact-value', 'contact-account', 'contact-template', 'infolog-value','tracker-value','records-value']);
var et2_entry = /** @class */ (function (_super) {
__extends(et2_entry, _super);
function et2_entry(_parent, _attrs, _child) {
var _this =
// Call the inherited constructor
_super.call(this, _parent, _attrs, et2_core_inheritance_1.ClassWithAttributes.extendAttributes(et2_entry._attributes, _child || {})) || this;
_this.legacyOptions = ["field", "compare", "alternate_fields"];
_this.widget = null;
// Often the ID conflicts, so check prefix
if (_attrs.id && _attrs.id.indexOf(et2_entry.prefix) < 0) {
_attrs.id = et2_entry.prefix + _attrs.id;
}
var value = _attrs.value;
_this = _super.call(this, _parent, _attrs, et2_core_inheritance_1.ClassWithAttributes.extendAttributes(et2_entry._attributes, _child || {})) || this;
// Save value from parsing, but only if set
if (value) {
_this.options.value = value;
}
_this.widget = null;
_this.setDOMNode(document.createElement('span'));
return _this;
}
et2_entry.prototype.loadFromXML = function (_node) {
// Load the nodes as usual
_super.prototype.loadFromXML.call(this, _node);
// Do the magic
this.loadField();
};
/**
* Initialize widget for entry field
*/
et2_entry.prototype.loadField = function () {
// Create widget of correct type
var attrs = {
id: this.id + (this.options.field ? '[' + this.options.field + ']' : ''),
type: 'label',
readonly: this.options.readonly
};
var modifications = this.getArrayMgr("modifications");
if (modifications && this.options.field) {
jQuery.extend(attrs, modifications.getEntry(attrs.id));
}
// Supress labels on templates
if (attrs.type == 'template' && this.options.label) {
this.egw().debug('log', "Surpressed label on <" + this.getType() + ' label="' + this.options.label + '" id="' + this.id + '"...>');
this.options.label = '';
}
var widget = et2_createWidget(attrs.type, attrs, this);
// If value is not set, etemplate takes care of everything
// If value was set, find the record explicitly.
if (typeof this.options.value == 'string') {
widget.options.value = this.getArrayMgr('content').getEntry(this.id + '[' + this.options.field + ']') ||
this.getRoot().getArrayMgr('content').getEntry(et2_entry.prefix + this.options.value + '[' + this.options.field + ']');
}
else if (this.options.field && this.options.value && this.options.value[this.options.field]) {
widget.options.value = this.options.value[this.options.field];
}
if (this.options.compare) {
widget.options.value = widget.options.value == this.options.compare ? 'X' : '';
}
if (this.options.alternate_fields) {
var sum = 0;
var fields = this.options.alternate_fields.split(':');
for (var i = 0; i < fields.length; i++) {
var negate = (fields[i][0] == "-");
var value = this.getArrayMgr('content').getEntry(fields[i].replace('-', ''));
sum += typeof value === 'undefined' ? 0 : (parseFloat(value) * (negate ? -1 : 1));
if (value && this.options.field !== 'sum') {
widget.options.value = value;
break;
}
}
if (this.options.field == 'sum') {
if (this.options.precision && jQuery.isNumeric(sum))
sum = parseFloat(sum).toFixed(this.options.precision);
widget.options.value = sum;
}
}
};
et2_entry._attributes = {
field: {
'name': 'Fields',
'description': 'Which entry field to display, or "sum" to add up the alternate_fields',
'type': 'string'
},
compare: {
name: 'Compare',
description: 'if given, the selected field is compared with its value and an X is printed on equality, nothing otherwise',
default: et2_no_init,
type: 'string'
},
alternate_fields: {
name: 'Alternate fields',
description: 'colon (:) separated list of alternative fields. The first non-empty one is used if the selected field is empty, (-) used for subtraction',
type: 'string',
default: et2_no_init
},
precision: {
name: 'Decimals to be shown',
description: 'Specifies the number of decimals for sum of alternates, the default is 2',
type: 'string',
default: '2'
},
regex: {
name: 'Regular expression pattern',
description: 'Only used server-side in a preg_replace with regex_replace to modify the value',
default: et2_no_init,
type: 'string'
},
regex_replace: {
name: 'Regular expression replacement pattern',
description: 'Only used server-side in a preg_replace with regex to modify the value',
default: et2_no_init,
type: 'string'
},
value: {
type: 'any'
},
readonly: {
default: true
}
};
return et2_entry;
}(et2_core_valueWidget_1.et2_valueWidget));
et2_core_widget_1.et2_register_widget(et2_entry, ["entry", 'contact-value', 'contact-account', 'contact-template', 'infolog-value', 'tracker-value', 'records-value']);
//# sourceMappingURL=et2_widget_entry.js.map

View File

@ -0,0 +1,179 @@
/*
* Egroupware etemplate2 JS Entry widget
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage api
* @link http://www.egroupware.org
* @author Nathan Gray
*/
/*egw:uses
et2_core_valueWidget;
*/
import {et2_register_widget, WidgetConfig} from "./et2_core_widget";
import {et2_valueWidget} from "./et2_core_valueWidget";
import {ClassWithAttributes} from "./et2_core_inheritance";
/**
* A widget to display a value from an entry
*
* Since we have Etemplate\Widget\Transformer, this client side widget exists
* mostly to resolve the problem where the ID for the entry widget is the same
* as the widget where you actually set the value, which prevents transformer
* from working.
*
* Server side will find the associated entry, and load it into ~<entry_id> to
* avoid overwriting the widget with id="entry_id". This widget will reverse
* that, and the modifications from transformer will be applied.
*
* @augments et2_valueWidget
*/
class et2_entry extends et2_valueWidget
{
static readonly _attributes : any = {
field: {
'name': 'Fields',
'description': 'Which entry field to display, or "sum" to add up the alternate_fields',
'type': 'string'
},
compare: {
name: 'Compare',
description: 'if given, the selected field is compared with its value and an X is printed on equality, nothing otherwise',
default: et2_no_init,
type: 'string'
},
alternate_fields: {
name: 'Alternate fields',
description: 'colon (:) separated list of alternative fields. The first non-empty one is used if the selected field is empty, (-) used for subtraction',
type: 'string',
default: et2_no_init
},
precision: {
name: 'Decimals to be shown',
description: 'Specifies the number of decimals for sum of alternates, the default is 2',
type: 'string',
default: '2'
},
regex: {
name: 'Regular expression pattern',
description: 'Only used server-side in a preg_replace with regex_replace to modify the value',
default: et2_no_init,
type: 'string'
},
regex_replace: {
name: 'Regular expression replacement pattern',
description: 'Only used server-side in a preg_replace with regex to modify the value',
default: et2_no_init,
type: 'string'
},
value: {
type: 'any'
},
readonly: {
default: true
}
};
legacyOptions : string[] = ["field","compare","alternate_fields"];
static readonly prefix: '~';
protected widget = null;
constructor(_parent, _attrs? : WidgetConfig, _child? : object)
{
// Call the inherited constructor
super(_parent, _attrs, ClassWithAttributes.extendAttributes(et2_entry._attributes, _child || {}));
// Often the ID conflicts, so check prefix
if(_attrs.id && _attrs.id.indexOf(et2_entry.prefix) < 0)
{
_attrs.id = et2_entry.prefix + _attrs.id;
}
let value = _attrs.value;
super(_parent, _attrs, ClassWithAttributes.extendAttributes(et2_entry._attributes, _child || {}));
// Save value from parsing, but only if set
if(value)
{
this.options.value = value;
}
this.widget = null;
this.setDOMNode(document.createElement('span'));
}
loadFromXML(_node)
{
// Load the nodes as usual
super.loadFromXML(_node);
// Do the magic
this.loadField();
}
/**
* Initialize widget for entry field
*/
loadField()
{
// Create widget of correct type
let attrs = {
id: this.id + (this.options.field ? '[' +this.options.field+']' : ''),
type: 'label',
readonly: this.options.readonly
};
let modifications = this.getArrayMgr("modifications");
if(modifications && this.options.field)
{
jQuery.extend(attrs, modifications.getEntry(attrs.id));
}
// Supress labels on templates
if(attrs.type == 'template' && this.options.label)
{
this.egw().debug('log', "Surpressed label on <" + this.getType() + ' label="' + this.options.label + '" id="' + this.id + '"...>');
this.options.label = '';
}
let widget = et2_createWidget(attrs.type, attrs, this);
// If value is not set, etemplate takes care of everything
// If value was set, find the record explicitly.
if(typeof this.options.value == 'string')
{
widget.options.value = this.getArrayMgr('content').getEntry(this.id+'['+this.options.field+']') ||
this.getRoot().getArrayMgr('content').getEntry(et2_entry.prefix+this.options.value + '['+this.options.field+']');
}
else if (this.options.field && this.options.value && this.options.value[this.options.field])
{
widget.options.value = this.options.value[this.options.field];
}
if(this.options.compare)
{
widget.options.value = widget.options.value == this.options.compare ? 'X' : '';
}
if(this.options.alternate_fields)
{
let sum : number | string = 0;
let fields = this.options.alternate_fields.split(':');
for(let i = 0; i < fields.length; i++)
{
let negate = (fields[i][0] == "-");
let value = this.getArrayMgr('content').getEntry(fields[i].replace('-',''));
sum += typeof value === 'undefined' ? 0 : (parseFloat(value) * (negate ? -1 : 1));
if(value && this.options.field !== 'sum')
{
widget.options.value = value;
break;
}
}
if(this.options.field == 'sum')
{
if (this.options.precision && jQuery.isNumeric(sum)) sum = parseFloat(<string><unknown>sum).toFixed(this.options.precision);
widget.options.value = sum;
}
}
}
}
et2_register_widget(et2_entry, ["entry", 'contact-value', 'contact-account', 'contact-template', 'infolog-value','tracker-value','records-value']);

View File

@ -1,3 +1,4 @@
"use strict";
/**
* EGroupware eTemplate2 - JS Favorite widget
*
@ -7,14 +8,28 @@
* @link http://www.egroupware.org
* @author Nathan Gray
* @copyright Nathan Gray 2013
* @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
et2_dropdown_button;
et2_extension_nextmatch;
et2_dropdown_button;
et2_extension_nextmatch;
*/
var et2_core_widget_1 = require("./et2_core_widget");
var et2_widget_dropdown_button_1 = require("./et2_widget_dropdown_button");
var et2_core_inheritance_1 = require("./et2_core_inheritance");
/**
* Favorites widget, designed for use with a nextmatch widget
*
@ -42,369 +57,287 @@
*
* @augments et2_dropdown_button
*/
var et2_favorites = (function(){ "use strict"; return et2_dropdown_button.extend([et2_INextmatchHeader],
{
attributes: {
"default_pref": {
"name": "Default preference key",
"type": "string",
"description": "The preference key where default favorite is stored (not the value)"
},
"sidebox_target": {
"name": "Sidebox target",
"type": "string",
"description": "ID of element to insert favorite list into",
"default": "favorite_sidebox"
},
"app": {
"name": "Application",
"type": "string",
"description": "Application to show favorites for"
},
"filters": {
"name": "Extra filters",
"type": "any",
"description": "Array of extra filters to include in the saved favorite"
},
// These are particular to favorites
id: {"default": "favorite"},
label: {"default": ""},
label_updates: { "default": false},
image: {"default": this.egw().image('fav_filter')},
statustext: {"default": "Favorite queries", "type": "string"}
},
// Some convenient variables, used in closures / event handlers
header: null,
nextmatch: null,
favorite_prefix: "favorite_",
stored_filters: {},
// If filter was set server side, we need to remember it until nm is created
nm_filter: false,
/**
* Constructor
*
* @memberOf et2_favorites
*/
init: function() {
this._super.apply(this, arguments);
this.sidebox_target = jQuery("#"+this.options.sidebox_target);
if(this.sidebox_target.length == 0 && egw_getFramework() != null)
{
var egw_fw = egw_getFramework();
this.sidebox_target = jQuery("#"+this.options.sidebox_target,egw_fw.sidemenuDiv);
}
// Store array of sorted items
this.favSortedList = ['blank'];
var apps = egw().user('apps');
this.is_admin = (typeof apps['admin'] != "undefined");
this.stored_filters = this.load_favorites(this.options.app);
this.preferred = egw.preference(this.options.default_pref,this.options.app);
if(!this.preferred || typeof this.stored_filters[this.preferred] == "undefined")
{
this.preferred = "blank";
}
// It helps to have the ID properly set before we get too far
this.set_id(this.id);
this.init_filters(this);
this.menu.addClass("favorites");
// Set the default (button) value
this.set_value(this.preferred,true);
var self = this;
// Add a listener on the radio buttons to set default filter
jQuery(this.menu).on("click","input:radio", function(event){
// Don't do the menu
event.stopImmediatePropagation();
// Save as default favorite - used when you click the button
self.egw().set_preference(self.options.app,self.options.default_pref,jQuery(this).val());
self.preferred = jQuery(this).val();
// Update sidebox, if there
if(self.sidebox_target.length)
{
self.sidebox_target.find("div.ui-icon-heart")
.replaceWith("<div class='sideboxstar'/>");
jQuery("li[data-id='"+self.preferred+"'] div.sideboxstar",self.sidebox_target)
.replaceWith("<div class='ui-icon ui-icon-heart'/>");
}
// Close the menu
self.menu.hide();
// Some user feedback
self.button.addClass("ui-state-active", 500,"swing",function(){
self.button.removeClass("ui-state-active",2000);
});
});
//Sort DomNodes of sidebox fav. menu
var sideBoxDOMNodeSort = function (_favSList) {
var favS = jQuery.isArray(_favSList)?_favSList.slice(0).reverse():[];
for (var i=0; i < favS.length;i++)
{
self.sidebox_target.children().find('[data-id$="' + favS[i] + '"]').prependTo(self.sidebox_target.children());
}
};
//Add Sortable handler to nm fav. menu
jQuery(this.menu).sortable({
items:'li:not([data-id$="add"])',
placeholder:'ui-fav-sortable-placeholder',
delay: 250, //(millisecond) delay before the sorting should start
update: function (event, ui)
{
self.favSortedList = jQuery(this).sortable('toArray', {attribute:'data-id'});
self.egw().set_preference(self.options.app,'fav_sort_pref',self.favSortedList);
sideBoxDOMNodeSort(self.favSortedList);
}
});
// Add a listener on the delete to remove
this.menu.on("click","div.ui-icon-trash", app[self.options.app], function() {
// App instance might not be ready yet, so don't bind directly
app[self.options.app].delete_favorite.apply(this,arguments);
})
// Wrap and unwrap because jQueryUI styles use a parent, and we don't want to change the state of the menu item
// Wrap in a span instead of a div because div gets a border
.on("mouseenter","div.ui-icon-trash", function() {jQuery(this).wrap("<span class='ui-state-active'/>");})
.on("mouseleave","div.ui-icon-trash", function() {jQuery(this).unwrap();});
// Trigger refresh of menu options now that events are registered
// to update sidebox
if(this.sidebox_target.length > 0)
{
this.init_filters(this);
}
},
/**
* Load favorites from preferences
*
* @param app String Load favorites from this application
*/
load_favorites: function(app) {
// Default blank filter
var stored_filters = {
'blank': {
name: this.egw().lang("No filters"),
state: {}
}
};
// Load saved favorites
var preferences = egw.preference("*",app);
for(var pref_name in preferences)
{
if(pref_name.indexOf(this.favorite_prefix) == 0 && typeof preferences[pref_name] == 'object')
{
var name = pref_name.substr(this.favorite_prefix.length);
stored_filters[name] = preferences[pref_name];
// Keep older favorites working - they used to store nm filters in 'filters',not state
if(preferences[pref_name].filters)
{
stored_filters[pref_name].state = preferences[pref_name].filters;
}
}
if (pref_name == 'fav_sort_pref')
{
this.favSortedList = preferences[pref_name];
//Make sure sorted list is always an array, seems some old fav are not array
if (!jQuery.isArray(this.favSortedList)) this.favSortedList = this.favSortedList.split(',');
}
}
if(typeof stored_filters == "undefined" || !stored_filters)
{
stored_filters = {};
}
else
{
for(var name in stored_filters)
{
if (this.favSortedList.indexOf(name) < 0)
{
this.favSortedList.push(name);
}
}
this.egw().set_preference (this.options.app,'fav_sort_pref',this.favSortedList);
if (this.favSortedList.length > 0)
{
var sortedListObj = {};
for (var i=0;i < this.favSortedList.length;i++)
{
if (typeof stored_filters[this.favSortedList[i]] != 'undefined')
{
sortedListObj[this.favSortedList[i]] = stored_filters[this.favSortedList[i]];
}
else
{
this.favSortedList.splice(i,1);
this.egw().set_preference (this.options.app,'fav_sort_pref',this.favSortedList);
}
}
stored_filters = jQuery.extend(sortedListObj,stored_filters);
}
}
return stored_filters;
},
// Create & set filter options for dropdown menu
init_filters: function(widget, filters)
{
if(typeof filters == "undefined")
{
filters = this.stored_filters;
}
var options = {};
for(var name in filters)
{
options[name] = "<input type='radio' name='"+this.internal_ids.menu+"[button][favorite]' value='"+name+"' title='" +
this.egw().lang('Set as default') + "'/>"+
(filters[name].name != undefined ? filters[name].name : name) +
(filters[name].group != false && !this.is_admin || name == 'blank' ? "" :
"<div class='ui-icon ui-icon-trash' title='" + this.egw().lang('Delete') + "'/>");
}
// Only add 'Add current' if we have a nextmatch
if(this.nextmatch)
{
options.add = "<img src='"+this.egw().image("new") +"'/>"+this.egw().lang('Add current');
}
widget.set_select_options.call(widget,options);
// Set radio to current value
jQuery("input[value='"+ this.preferred +"']:radio", this.menu).attr("checked",true);
},
set_nm_filters: function(filters)
{
if(this.nextmatch)
{
this.nextmatch.applyFilters(filters);
}
else
{
console.log(filters);
}
},
onclick: function(node) {
// Apply preferred filter - make sure it's an object, and not a reference
if(this.preferred && this.stored_filters[this.preferred])
{
// use app[appname].setState if available to allow app to overwrite it (eg. change to non-listview in calendar)
if (typeof app[this.options.app] != 'undefined')
{
app[this.options.app].setState(this.stored_filters[this.preferred]);
}
else
{
this.set_nm_filters(jQuery.extend({},this.stored_filters[this.preferred].state));
}
}
else
{
alert(this.egw().lang("No default set"));
}
},
// Apply the favorite when you pick from the list
change: function(selected_node) {
this.value = jQuery(selected_node).attr("data-id");
if(this.value == "add" && this.nextmatch)
{
// Get current filters
var current_filters = jQuery.extend({},this.nextmatch.activeFilters);
// Add in extras
for(var extra in this.options.filters)
{
// Don't overwrite what nm has, chances are nm has more up-to-date value
if(typeof current_filters == 'undefined')
{
current_filters[extra] = this.nextmatch.options.settings[extra];
}
}
// Skip columns for now
delete current_filters.selcolumns;
// Add in application's settings
if(this.filters != true)
{
for(var i = 0; i < this.filters.length; i++)
{
current_filters[this.options.filters[i]] = this.nextmatch.options.settings[this.options.filters[i]];
}
}
// Call framework
app[this.options.app].add_favorite(current_filters);
// Reset value
this.set_value(this.preferred,true);
}
else if (this.value == 'blank')
{
// Reset filters when select no filters
this.set_nm_filters({});
}
},
set_value: function(filter_name, parent) {
if(parent)
{
return this._super.call(this, filter_name);
}
if(filter_name == 'add') return false;
app[this.options.app].setState(this.stored_filters[filter_name]);
return false;
},
getValue: function()
{
return null;
},
/**
* Set the nextmatch to filter
* From et2_INextmatchHeader interface
*
* @param {et2_nextmatch} nextmatch
*/
setNextmatch: function(nextmatch)
{
this.nextmatch = nextmatch;
if(this.nm_filter)
{
this.set_value(this.nm_filter);
this.nm_filter = false;
}
// Re-generate filter list so we can add 'Add current'
this.init_filters(this);
}
});}).call(this);
et2_register_widget(et2_favorites, ["favorites"]);
var et2_favorites = /** @class */ (function (_super) {
__extends(et2_favorites, _super);
/**
* Constructor
*
* @memberOf et2_favorites
*/
function et2_favorites(_parent, _attrs, _child) {
var _this = _super.call(this, _parent, _attrs, et2_core_inheritance_1.ClassWithAttributes.extendAttributes(et2_favorites._attributes, _child || {})) || this;
// Some convenient variables, used in closures / event handlers
_this.header = null;
_this.nextmatch = null;
_this.favSortedList = null;
_this.sidebox_target = null;
// If filter was set server side, we need to remember it until nm is created
_this.nm_filter = false;
_this.sidebox_target = jQuery("#" + _this.options.sidebox_target);
if (_this.sidebox_target.length == 0 && egw_getFramework() != null) {
var egw_fw = egw_getFramework();
_this.sidebox_target = jQuery("#" + _this.options.sidebox_target, egw_fw.sidemenuDiv);
}
// Store array of sorted items
_this.favSortedList = ['blank'];
var apps = egw().user('apps');
et2_favorites.is_admin = (typeof apps['admin'] != "undefined");
_this.stored_filters = _this.load_favorites(_this.options.app);
_this.preferred = egw.preference(_this.options.default_pref, _this.options.app);
if (!_this.preferred || typeof _this.stored_filters[_this.preferred] == "undefined") {
_this.preferred = "blank";
}
// It helps to have the ID properly set before we get too far
_this.set_id(_this.id);
_this.init_filters(_this);
_this.menu.addClass("favorites");
// Set the default (button) value
_this.set_value(_this.preferred, true);
var self = _this;
// Add a listener on the radio buttons to set default filter
jQuery(_this.menu).on("click", "input:radio", function (event) {
// Don't do the menu
event.stopImmediatePropagation();
// Save as default favorite - used when you click the button
self.egw().set_preference(self.options.app, self.options.default_pref, jQuery(this).val());
self.preferred = jQuery(this).val();
// Update sidebox, if there
if (self.sidebox_target.length) {
self.sidebox_target.find("div.ui-icon-heart")
.replaceWith("<div class='sideboxstar'/>");
jQuery("li[data-id='" + self.preferred + "'] div.sideboxstar", self.sidebox_target)
.replaceWith("<div class='ui-icon ui-icon-heart'/>");
}
// Close the menu
self.menu.hide();
// Some user feedback
self.button.addClass("ui-state-active", 500, "swing", function () {
self.button.removeClass("ui-state-active", 2000);
});
});
//Sort DomNodes of sidebox fav. menu
var sideBoxDOMNodeSort = function (_favSList) {
var favS = jQuery.isArray(_favSList) ? _favSList.slice(0).reverse() : [];
for (var i = 0; i < favS.length; i++) {
self.sidebox_target.children().find('[data-id$="' + favS[i] + '"]').prependTo(self.sidebox_target.children());
}
};
//Add Sortable handler to nm fav. menu
jQuery(_this.menu).sortable({
items: 'li:not([data-id$="add"])',
placeholder: 'ui-fav-sortable-placeholder',
delay: 250,
update: function () {
self.favSortedList = jQuery(this).sortable('toArray', { attribute: 'data-id' });
self.egw().set_preference(self.options.app, 'fav_sort_pref', self.favSortedList);
sideBoxDOMNodeSort(self.favSortedList);
}
});
// Add a listener on the delete to remove
_this.menu.on("click", "div.ui-icon-trash", app[self.options.app], function () {
// App instance might not be ready yet, so don't bind directly
app[self.options.app].delete_favorite.apply(this, arguments);
})
// Wrap and unwrap because jQueryUI styles use a parent, and we don't want to change the state of the menu item
// Wrap in a span instead of a div because div gets a border
.on("mouseenter", "div.ui-icon-trash", function () { jQuery(this).wrap("<span class='ui-state-active'/>"); })
.on("mouseleave", "div.ui-icon-trash", function () { jQuery(this).unwrap(); });
// Trigger refresh of menu options now that events are registered
// to update sidebox
if (_this.sidebox_target.length > 0) {
_this.init_filters(_this);
}
return _this;
}
/**
* Load favorites from preferences
*
* @param app String Load favorites from this application
*/
et2_favorites.prototype.load_favorites = function (app) {
// Default blank filter
var stored_filters = {
'blank': {
name: this.egw().lang("No filters"),
state: {}
}
};
// Load saved favorites
var preferences = egw.preference("*", app);
for (var pref_name in preferences) {
if (pref_name.indexOf(this.favorite_prefix) == 0 && typeof preferences[pref_name] == 'object') {
var name_1 = pref_name.substr(this.favorite_prefix.length);
stored_filters[name_1] = preferences[pref_name];
// Keep older favorites working - they used to store nm filters in 'filters',not state
if (preferences[pref_name]["filters"]) {
stored_filters[pref_name]["state"] = preferences[pref_name]["filters"];
}
}
if (pref_name == 'fav_sort_pref') {
this.favSortedList = preferences[pref_name];
//Make sure sorted list is always an array, seems some old fav are not array
if (!jQuery.isArray(this.favSortedList))
this.favSortedList = this.favSortedList.split(',');
}
}
if (typeof stored_filters == "undefined" || !stored_filters) {
stored_filters = {};
}
else {
for (var name_2 in stored_filters) {
if (this.favSortedList.indexOf(name_2) < 0) {
this.favSortedList.push(name_2);
}
}
this.egw().set_preference(this.options.app, 'fav_sort_pref', this.favSortedList);
if (this.favSortedList.length > 0) {
var sortedListObj = {};
for (var i = 0; i < this.favSortedList.length; i++) {
if (typeof stored_filters[this.favSortedList[i]] != 'undefined') {
sortedListObj[this.favSortedList[i]] = stored_filters[this.favSortedList[i]];
}
else {
this.favSortedList.splice(i, 1);
this.egw().set_preference(this.options.app, 'fav_sort_pref', this.favSortedList);
}
}
stored_filters = jQuery.extend(sortedListObj, stored_filters);
}
}
return stored_filters;
};
// Create & set filter options for dropdown menu
et2_favorites.prototype.init_filters = function (widget, filters) {
if (typeof filters == "undefined") {
filters = this.stored_filters;
}
var options = {};
for (var name_3 in filters) {
options[name_3] = "<input type='radio' name='" + this.internal_ids.menu + "[button][favorite]' value='" + name_3 + "' title='" +
this.egw().lang('Set as default') + "'/>" +
(filters[name_3].name != undefined ? filters[name_3].name : name_3) +
(filters[name_3].group != false && !et2_favorites.is_admin || name_3 == 'blank' ? "" :
"<div class='ui-icon ui-icon-trash' title='" + this.egw().lang('Delete') + "'/>");
}
// Only add 'Add current' if we have a nextmatch
if (this.nextmatch) {
options["add"] = "<img src='" + this.egw().image("new") + "'/>" + this.egw().lang('Add current');
}
widget.set_select_options.call(widget, options);
// Set radio to current value
jQuery("input[value='" + this.preferred + "']:radio", this.menu).attr("checked", 1);
};
et2_favorites.prototype.set_nm_filters = function (filters) {
if (this.nextmatch) {
this.nextmatch.applyFilters(filters);
}
else {
console.log(filters);
}
};
et2_favorites.prototype.onclick = function (node) {
// Apply preferred filter - make sure it's an object, and not a reference
if (this.preferred && this.stored_filters[this.preferred]) {
// use app[appname].setState if available to allow app to overwrite it (eg. change to non-listview in calendar)
if (typeof app[this.options.app] != 'undefined') {
app[this.options.app].setState(this.stored_filters[this.preferred]);
}
else {
this.set_nm_filters(jQuery.extend({}, this.stored_filters[this.preferred].state));
}
}
else {
alert(this.egw().lang("No default set"));
}
};
// Apply the favorite when you pick from the list
et2_favorites.prototype.change = function (selected_node) {
this.value = jQuery(selected_node).attr("data-id");
if (this.value == "add" && this.nextmatch) {
// Get current filters
var current_filters = jQuery.extend({}, this.nextmatch.activeFilters);
// Add in extras
for (var extra in this.options.filters) {
// Don't overwrite what nm has, chances are nm has more up-to-date value
if (typeof current_filters == 'undefined') {
current_filters[extra] = this.nextmatch.options.settings[extra];
}
}
// Skip columns for now
delete current_filters.selcolumns;
// Add in application's settings
if (this.filters != true) {
for (var i = 0; i < this.filters.length; i++) {
current_filters[this.options.filters[i]] = this.nextmatch.options.settings[this.options.filters[i]];
}
}
// Call framework
app[this.options.app].add_favorite(current_filters);
// Reset value
this.set_value(this.preferred, true);
}
else if (this.value == 'blank') {
// Reset filters when select no filters
this.set_nm_filters({});
}
};
et2_favorites.prototype.set_value = function (filter_name, parent) {
if (parent) {
return _super.prototype.set_value.call(this, filter_name);
}
if (filter_name == 'add')
return false;
app[this.options.app].setState(this.stored_filters[filter_name]);
return false;
};
et2_favorites.prototype.getValue = function () {
return null;
};
/**
* Set the nextmatch to filter
* From et2_INextmatchHeader interface
*
* @param {et2_nextmatch} nextmatch
*/
et2_favorites.prototype.setNextmatch = function (nextmatch) {
this.nextmatch = nextmatch;
if (this.nm_filter) {
this.set_value(this.nm_filter);
this.nm_filter = false;
}
// Re-generate filter list so we can add 'Add current'
this.init_filters(this);
};
et2_favorites._attributes = {
"default_pref": {
"name": "Default preference key",
"type": "string",
"description": "The preference key where default favorite is stored (not the value)"
},
"sidebox_target": {
"name": "Sidebox target",
"type": "string",
"description": "ID of element to insert favorite list into",
"default": "favorite_sidebox"
},
"app": {
"name": "Application",
"type": "string",
"description": "Application to show favorites for"
},
"filters": {
"name": "Extra filters",
"type": "any",
"description": "Array of extra filters to include in the saved favorite"
},
// These are particular to favorites
id: { "default": "favorite" },
label: { "default": "" },
label_updates: { "default": false },
image: { "default": egw().image('fav_filter') },
statustext: { "default": "Favorite queries", "type": "string" }
};
return et2_favorites;
}(et2_widget_dropdown_button_1.et2_dropdown_button));
et2_core_widget_1.et2_register_widget(et2_favorites, ["favorites"]);
//# sourceMappingURL=et2_widget_favorites.js.map

View File

@ -0,0 +1,425 @@
/**
* EGroupware eTemplate2 - JS Favorite widget
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage api
* @link http://www.egroupware.org
* @author Nathan Gray
* @copyright Nathan Gray 2013
*/
/*egw:uses
et2_dropdown_button;
et2_extension_nextmatch;
*/
import {et2_register_widget, WidgetConfig} from "./et2_core_widget";
import {et2_INextmatchHeader} from "./et2_extension_nextmatch";
import {et2_dropdown_button} from "./et2_widget_dropdown_button";
import {ClassWithAttributes} from "./et2_core_inheritance";
/**
* Favorites widget, designed for use with a nextmatch widget
*
* The primary control is a split/dropdown button. Clicking on the left side of the button filters the
* nextmatch list by the user's default filter. The right side of the button gives a list of
* saved filters, pulled from preferences. Clicking a filter from the dropdown list sets the
* filters as saved.
*
* Favorites can also automatically be shown in the sidebox, using the special ID favorite_sidebox.
* Use the following code to generate the sidebox section:
* display_sidebox($appname,lang('Favorites'),array(
* array(
* 'no_lang' => true,
* 'text'=>'<span id="favorite_sidebox"/>',
* 'link'=>false,
* 'icon' => false
* )
* ));
* This sidebox list will be automatically generated and kept up to date.
*
*
* Favorites are implemented by saving the values for [column] filters. Filters are stored
* in preferences, with the name favorite_<name>. The favorite favorite used for clicking on
* the filter button is stored in nextmatch-<columnselection_pref>-favorite.
*
* @augments et2_dropdown_button
*/
class et2_favorites extends et2_dropdown_button implements et2_INextmatchHeader
{
static readonly _attributes : any = {
"default_pref": {
"name": "Default preference key",
"type": "string",
"description": "The preference key where default favorite is stored (not the value)"
},
"sidebox_target": {
"name": "Sidebox target",
"type": "string",
"description": "ID of element to insert favorite list into",
"default": "favorite_sidebox"
},
"app": {
"name": "Application",
"type": "string",
"description": "Application to show favorites for"
},
"filters": {
"name": "Extra filters",
"type": "any",
"description": "Array of extra filters to include in the saved favorite"
},
// These are particular to favorites
id: {"default": "favorite"},
label: {"default": ""},
label_updates: { "default": false},
image: {"default": egw().image('fav_filter')},
statustext: {"default": "Favorite queries", "type": "string"}
};
// Some convenient variables, used in closures / event handlers
header = null;
nextmatch = null;
favorite_prefix: "favorite_";
private stored_filters: {};
private favSortedList : any = null;
private sidebox_target : JQuery = null;
private preferred;
static is_admin : boolean;
private filters : any;
// If filter was set server side, we need to remember it until nm is created
nm_filter = false;
/**
* Constructor
*
* @memberOf et2_favorites
*/
constructor(_parent?, _attrs? : WidgetConfig, _child? : object)
{
super(_parent, _attrs, ClassWithAttributes.extendAttributes(et2_favorites._attributes, _child || {}));
this.sidebox_target = jQuery("#"+this.options.sidebox_target);
if(this.sidebox_target.length == 0 && egw_getFramework() != null)
{
let egw_fw = egw_getFramework();
this.sidebox_target = jQuery("#"+this.options.sidebox_target,egw_fw.sidemenuDiv);
}
// Store array of sorted items
this.favSortedList = ['blank'];
let apps = egw().user('apps');
et2_favorites.is_admin = (typeof apps['admin'] != "undefined");
this.stored_filters = this.load_favorites(this.options.app);
this.preferred = egw.preference(this.options.default_pref,this.options.app);
if(!this.preferred || typeof this.stored_filters[this.preferred] == "undefined")
{
this.preferred = "blank";
}
// It helps to have the ID properly set before we get too far
this.set_id(this.id);
this.init_filters(this);
this.menu.addClass("favorites");
// Set the default (button) value
this.set_value(this.preferred,true);
let self = this;
// Add a listener on the radio buttons to set default filter
jQuery(this.menu).on("click","input:radio", function(event){
// Don't do the menu
event.stopImmediatePropagation();
// Save as default favorite - used when you click the button
self.egw().set_preference(self.options.app,self.options.default_pref,jQuery(this).val());
self.preferred = jQuery(this).val();
// Update sidebox, if there
if(self.sidebox_target.length)
{
self.sidebox_target.find("div.ui-icon-heart")
.replaceWith("<div class='sideboxstar'/>");
jQuery("li[data-id='"+self.preferred+"'] div.sideboxstar",self.sidebox_target)
.replaceWith("<div class='ui-icon ui-icon-heart'/>");
}
// Close the menu
self.menu.hide();
// Some user feedback
self.button.addClass("ui-state-active", 500,"swing",function(){
self.button.removeClass("ui-state-active",2000);
});
});
//Sort DomNodes of sidebox fav. menu
let sideBoxDOMNodeSort = function (_favSList) {
let favS = jQuery.isArray(_favSList)?_favSList.slice(0).reverse():[];
for (let i=0; i < favS.length;i++)
{
self.sidebox_target.children().find('[data-id$="' + favS[i] + '"]').prependTo(self.sidebox_target.children());
}
};
//Add Sortable handler to nm fav. menu
jQuery(this.menu).sortable({
items:'li:not([data-id$="add"])',
placeholder:'ui-fav-sortable-placeholder',
delay: 250, //(millisecond) delay before the sorting should start
update: function ()
{
self.favSortedList = jQuery(this).sortable('toArray', {attribute:'data-id'});
self.egw().set_preference(self.options.app,'fav_sort_pref',self.favSortedList);
sideBoxDOMNodeSort(self.favSortedList);
}
});
// Add a listener on the delete to remove
this.menu.on("click","div.ui-icon-trash", app[self.options.app], function() {
// App instance might not be ready yet, so don't bind directly
app[self.options.app].delete_favorite.apply(this,arguments);
})
// Wrap and unwrap because jQueryUI styles use a parent, and we don't want to change the state of the menu item
// Wrap in a span instead of a div because div gets a border
.on("mouseenter","div.ui-icon-trash", function() {jQuery(this).wrap("<span class='ui-state-active'/>");})
.on("mouseleave","div.ui-icon-trash", function() {jQuery(this).unwrap();});
// Trigger refresh of menu options now that events are registered
// to update sidebox
if(this.sidebox_target.length > 0)
{
this.init_filters(this);
}
}
/**
* Load favorites from preferences
*
* @param app String Load favorites from this application
*/
load_favorites(app)
{
// Default blank filter
let stored_filters : any = {
'blank': {
name: this.egw().lang("No filters"),
state: {}
}
};
// Load saved favorites
let preferences : any = egw.preference("*",app);
for(let pref_name in preferences)
{
if(pref_name.indexOf(this.favorite_prefix) == 0 && typeof preferences[pref_name] == 'object')
{
let name = pref_name.substr(this.favorite_prefix.length);
stored_filters[name] = preferences[pref_name];
// Keep older favorites working - they used to store nm filters in 'filters',not state
if(preferences[pref_name]["filters"])
{
stored_filters[pref_name]["state"] = preferences[pref_name]["filters"];
}
}
if (pref_name == 'fav_sort_pref')
{
this.favSortedList = preferences[pref_name];
//Make sure sorted list is always an array, seems some old fav are not array
if (!jQuery.isArray(this.favSortedList)) this.favSortedList = this.favSortedList.split(',');
}
}
if(typeof stored_filters == "undefined" || !stored_filters)
{
stored_filters = {};
}
else
{
for(let name in stored_filters)
{
if (this.favSortedList.indexOf(name) < 0)
{
this.favSortedList.push(name);
}
}
this.egw().set_preference (this.options.app,'fav_sort_pref',this.favSortedList);
if (this.favSortedList.length > 0)
{
let sortedListObj = {};
for (let i=0; i < this.favSortedList.length; i++)
{
if (typeof stored_filters[this.favSortedList[i]] != 'undefined')
{
sortedListObj[this.favSortedList[i]] = stored_filters[this.favSortedList[i]];
}
else
{
this.favSortedList.splice(i,1);
this.egw().set_preference (this.options.app,'fav_sort_pref',this.favSortedList);
}
}
stored_filters = jQuery.extend(sortedListObj,stored_filters);
}
}
return stored_filters;
}
// Create & set filter options for dropdown menu
init_filters(widget, filters?)
{
if(typeof filters == "undefined")
{
filters = this.stored_filters;
}
let options = {};
for(let name in filters)
{
options[name] = "<input type='radio' name='"+this.internal_ids.menu+"[button][favorite]' value='"+name+"' title='" +
this.egw().lang('Set as default') + "'/>"+
(filters[name].name != undefined ? filters[name].name : name) +
(filters[name].group != false && !et2_favorites.is_admin || name == 'blank' ? "" :
"<div class='ui-icon ui-icon-trash' title='" + this.egw().lang('Delete') + "'/>");
}
// Only add 'Add current' if we have a nextmatch
if(this.nextmatch)
{
options["add"] = "<img src='"+this.egw().image("new") +"'/>"+this.egw().lang('Add current');
}
widget.set_select_options.call(widget,options);
// Set radio to current value
jQuery("input[value='"+ this.preferred +"']:radio", this.menu).attr("checked",1);
}
set_nm_filters(filters)
{
if(this.nextmatch)
{
this.nextmatch.applyFilters(filters);
}
else
{
console.log(filters);
}
}
onclick(node)
{
// Apply preferred filter - make sure it's an object, and not a reference
if(this.preferred && this.stored_filters[this.preferred])
{
// use app[appname].setState if available to allow app to overwrite it (eg. change to non-listview in calendar)
if (typeof app[this.options.app] != 'undefined')
{
app[this.options.app].setState(this.stored_filters[this.preferred]);
}
else
{
this.set_nm_filters(jQuery.extend({},this.stored_filters[this.preferred].state));
}
}
else
{
alert(this.egw().lang("No default set"));
}
}
// Apply the favorite when you pick from the list
change(selected_node)
{
this.value = jQuery(selected_node).attr("data-id");
if(this.value == "add" && this.nextmatch)
{
// Get current filters
let current_filters = jQuery.extend({},this.nextmatch.activeFilters);
// Add in extras
for(let extra in this.options.filters)
{
// Don't overwrite what nm has, chances are nm has more up-to-date value
if(typeof current_filters == 'undefined')
{
current_filters[extra] = this.nextmatch.options.settings[extra];
}
}
// Skip columns for now
delete current_filters.selcolumns;
// Add in application's settings
if(this.filters != true)
{
for(let i = 0; i < this.filters.length; i++)
{
current_filters[this.options.filters[i]] = this.nextmatch.options.settings[this.options.filters[i]];
}
}
// Call framework
app[this.options.app].add_favorite(current_filters);
// Reset value
this.set_value(this.preferred,true);
}
else if (this.value == 'blank')
{
// Reset filters when select no filters
this.set_nm_filters({});
}
}
set_value(filter_name, parent? : boolean) : void | boolean
{
if(parent)
{
return super.set_value(filter_name);
}
if(filter_name == 'add') return false;
app[this.options.app].setState(this.stored_filters[filter_name]);
return false;
}
getValue()
{
return null;
}
/**
* Set the nextmatch to filter
* From et2_INextmatchHeader interface
*
* @param {et2_nextmatch} nextmatch
*/
setNextmatch(nextmatch)
{
this.nextmatch = nextmatch;
if(this.nm_filter)
{
this.set_value(this.nm_filter);
this.nm_filter = false;
}
// Re-generate filter list so we can add 'Add current'
this.init_filters(this);
}
}
et2_register_widget(et2_favorites, ["favorites"]);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,740 @@
/**
* EGroupware eTemplate2 - JS Number object
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage api
* @link http://www.egroupware.org
* @author Nathan Gray
* @copyright Nathan Gray 2011
* @version $Id$
*/
/*egw:uses
et2_core_inputWidget;
phpgwapi.Resumable.resumable;
*/
import {et2_inputWidget} from "./et2_core_inputWidget";
import {et2_register_widget, WidgetConfig} from "./et2_core_widget";
import {ClassWithAttributes} from "./et2_core_inheritance";
/**
* Class which implements file upload
*
* @augments et2_inputWidget
*/
export class et2_file extends et2_inputWidget
{
static readonly _attributes : any = {
"multiple": {
"name": "Multiple files",
"type": "boolean",
"default": false,
"description": "Allow the user to select more than one file to upload at a time. Subject to browser support."
},
"max_file_size": {
"name": "Maximum file size",
"type": "integer",
"default":0,
"description": "Largest file accepted, in bytes. Subject to server limitations. 8MB = 8388608"
},
"mime": {
"name": "Allowed file types",
"type": "string",
"default": et2_no_init,
"description": "Mime type (eg: image/png) or regex (eg: /^text\//i) for allowed file types"
},
"blur": {
"name": "Placeholder",
"type": "string",
"default": "",
"description": "This text get displayed if an input-field is empty and does not have the input-focus (blur). It can be used to show a default value or a kind of help-text."
},
"progress": {
"name": "Progress node",
"type": "string",
"default": et2_no_init,
"description": "The ID of an alternate node (div) to display progress and results. The Node is fetched with et2 getWidgetById so you MUST use the id assigned in XET-File (it may not be available at creation time, so we (re)check on createStatus time)"
},
"onStart": {
"name": "Start event handler",
"type": "any",
"default": et2_no_init,
"description": "A (js) function called when an upload starts. Return true to continue with upload, false to cancel."
},
"onFinish": {
"name": "Finish event handler",
"type": "any",
"default": et2_no_init,
"description": "A (js) function called when all files to be uploaded are finished."
},
drop_target: {
"name": "Optional, additional drop target for HTML5 uploads",
"type": "string",
"default": et2_no_init,
"description": "The ID of an additional drop target for HTML5 drag-n-drop file uploads"
},
label: {
"name": "Label of file upload",
"type": "string",
"default": "Choose file...",
"description": "String caption to be displayed on file upload span"
},
progress_dropdownlist: {
"name": "List on files in progress like dropdown",
"type": "boolean",
"default": false,
"description": "Style list of files in uploading progress like dropdown list with a total upload progress indicator"
},
onFinishOne: {
"name": "Finish event handler for each one",
"type": "any",
"default": et2_no_init,
"description": "A (js) function called when a file to be uploaded is finished."
},
accept: {
"name": "Acceptable extensions",
"type": "string",
"default": '',
"description": "Define types of files that the server accepts. Multiple types can be seperated by comma and the default is to accept everything."
},
chunk_size: {
"name": "Chunk size",
"type": "integer",
"default": 1024*1024,
"description": "Max chunk size, gets set from server-side PHP (max_upload_size-1M)/2" // last chunk can be up to 2*chunk_size!
}
};
asyncOptions : any = {};
input : JQuery = null;
progress : JQuery = null;
span : JQuery = null;
disabled_buttons : JQuery;
resumable : any;
/**
* Constructor
*
* @memberOf et2_file
*/
constructor(_parent, _attrs? : WidgetConfig, _child? : object)
{
// Call the inherited constructor
super(_parent, _attrs, ClassWithAttributes.extendAttributes(et2_file._attributes, _child || {}));
this.node = null;
this.input = null;
this.progress = null;
this.span = null;
// Contains all submit buttons need to be disabled during upload process
this.disabled_buttons = jQuery("input[type='submit'], button");
if(!this.options.value) this.options.value = {};
if(!this.options.id) {
console.warn("File widget needs an ID. Used 'file_widget'.");
this.options.id = "file_widget";
}
// Legacy - id ending in [] means multiple
if(this.options.id.substr(-2) == "[]")
{
this.options.multiple = true;
}
// If ID ends in /, it's a directory - allow multiple
else if (this.options.id.substr(-1) === "/")
{
this.options.multiple = true;
_attrs.multiple = true;
}
// Set up the URL to have the request ID & the widget ID
var instance = this.getInstanceManager();
let self = this;
this.asyncOptions = jQuery.extend({
// Callbacks
onStart: function(event, file_count) {
return self.onStart(event, file_count);
},
onFinish: function(event, file_count) {
self.onFinish.apply(self, [event, file_count])
},
onStartOne: function(event, file_name, index, file_count) {
},
onFinishOne: function(event, response, name, number, total) { return self.finishUpload(event,response,name,number,total);},
onProgress: function(event, progress, name, number, total) { return self.onProgress(event,progress,name,number,total);},
onError: function(event, name, error) { return self.onError(event,name,error);},
beforeSend: function(form) { return self.beforeSend(form);},
chunkSize: this.options.chunk_size || 1024*1024,
target: egw.ajaxUrl("EGroupware\\Api\\Etemplate\\Widget\\File::ajax_upload"),
query: function(file) {return self.beforeSend(file);},
// Disable checking for already uploaded chunks
testChunks: false
},this.asyncOptions);
this.asyncOptions.fieldName = this.options.id;
this.createInputWidget();
this.set_readonly(this.options.readonly);
}
destroy()
{
super.destroy();
this.set_drop_target(null);
this.node = null;
this.input = null;
this.span = null;
this.progress = null;
}
createInputWidget()
{
this.node = <HTMLElement><unknown>jQuery(document.createElement("div")).addClass("et2_file");
this.span = jQuery(document.createElement("span"))
.addClass('et2_file_span et2_button')
.appendTo (this.node);
if (this.options.label != '') this.span.addClass('et2_button_text');
let span = this.span;
this.input = jQuery(document.createElement("input"))
.attr("type", "file").attr("placeholder", this.options.blur)
.addClass ("et2_file_upload")
.appendTo(this.node)
.hover(function() {
jQuery(span)
.toggleClass('et2_file_spanHover');
})
.on({
mousedown:function (){
jQuery(span).addClass('et2_file_spanActive');
},
mouseup:function (){
jQuery(span).removeClass('et2_file_spanActive');
}
});
if (this.options.accept) this.input.attr('accept', this.options.accept);
let self = this;
// trigger native input upload file
if (!this.options.readonly) this.span.click(function(){self.input.click()});
// Check for File interface, should fall back to normal form submit if missing
if(typeof File != "undefined" && typeof (new XMLHttpRequest()).upload != "undefined")
{
this.resumable = new Resumable(this.asyncOptions);
this.resumable.assignBrowse(this.input);
this.resumable.on('fileAdded', jQuery.proxy(this._fileAdded, this));
this.resumable.on('fileProgress', jQuery.proxy(this._fileProgress, this));
this.resumable.on('fileSuccess', jQuery.proxy(this.finishUpload, this));
this.resumable.on('complete', jQuery.proxy(this.onFinish, this));
}
else
{
// This may be a problem submitting via ajax
}
if(this.options.progress)
{
let widget = this.getRoot().getWidgetById(this.options.progress);
if(widget)
{
//may be not available at createInputWidget time
this.progress = jQuery(widget.getDOMNode());
}
}
if(!this.progress)
{
this.progress = jQuery(document.createElement("div")).appendTo(this.node);
}
this.progress.addClass("progress");
if(this.options.multiple)
{
this.input.attr("multiple","multiple");
}
this.setDOMNode(this.node[0]);
}
/**
* Set a widget or DOM node as a HTML5 file drop target
*
* @param {string} new_target widget ID or DOM node ID to be used as a new target
*/
set_drop_target(new_target : string)
{
// Cancel old drop target
if(this.options.drop_target)
{
let widget = this.getRoot().getWidgetById(this.options.drop_target);
let drop_target = widget && widget.getDOMNode() || document.getElementById(this.options.drop_target);
if(drop_target)
{
this.resumable.unAssignDrop(drop_target);
}
}
this.options.drop_target = new_target;
if(!this.options.drop_target) return;
// Set up new drop target
let widget = this.getRoot().getWidgetById(this.options.drop_target);
let drop_target = widget && widget.getDOMNode() || document.getElementById(this.options.drop_target);
if(drop_target)
{
this.resumable.assignDrop([drop_target]);
}
else
{
this.egw().debug("warn", "Did not find file drop target %s", this.options.drop_target);
}
}
attachToDOM()
{
let res = super.attachToDOM();
// Override parent's change, file widget will fire change when finished uploading
this.input.unbind("change.et2_inputWidget");
return res;
}
getValue()
{
return this.options.value ? this.options.value : this.input.val();
}
/**
* Set the value of the file widget.
*
* If you pass a FileList or list of files, it will trigger the async upload
*
* @param {FileList|File[]|false} value List of files to be uploaded, or false to reset.
* @param {Event} event Most browsers require the user to initiate file transfers in some way.
* Pass the event in, if you have it.
*/
set_value(value, event?) : boolean
{
if (!value || typeof value == "undefined") {
value = {};
}
if (jQuery.isEmptyObject(value)) {
this.options.value = {};
if (this.resumable.progress() == 1) this.progress.empty();
// Reset the HTML element
this.input.wrap('<form>').closest('form').get(0).reset();
this.input.unwrap();
return;
}
let addFile = jQuery.proxy(function (i, file) {
this.resumable.addFile(file, event);
}, this);
if (typeof value == 'object' && value.length && typeof value[0] == 'object' && value[0].name) {
try {
this.input[0].files = value;
jQuery.each(value, addFile);
} catch (e) {
var self = this;
var args = arguments;
jQuery.each(value, addFile);
}
}
}
/**
* Set the value for label
* The label is used as caption for span tag which customize the HTML file upload styling
*
* @param {string} value text value of label
*/
set_label(value)
{
if (this.span != null && value != null)
{
this.span.text(value);
}
}
getInputNode()
{
if (typeof this.input == 'undefined') return <HTMLElement><unknown>false;
return this.input[0];
}
set_mime(mime)
{
if(!mime)
{
this.options.mime = null;
}
if(mime.indexOf("/") != 0)
{
// Lower case it now, if it's not a regex
this.options.mime = mime.toLowerCase();
}
else
{
// Convert into a js regex
var parts = mime.substr(1).match(/(.*)\/([igm]?)$/);
this.options.mime = new RegExp(parts[1],parts.length > 2 ? parts[2] : "");
}
}
set_multiple(_multiple)
{
this.options.multiple = _multiple;
if(_multiple)
{
return this.input.attr("multiple", "multiple");
}
return this.input.removeAttr("multiple");
}
/**
* Check to see if the provided file's mimetype matches
*
* @param f File object
* @return boolean
*/
checkMime(f)
{
// If missing, let the server handle it
if(!this.options.mime || !f.type) return true;
var is_preg = (typeof this.options.mime == "object");
if(!is_preg && f.type.toLowerCase() == this.options.mime || is_preg && this.options.mime.test(f.type))
{
return true;
}
// Not right mime
return false;
}
private _fileAdded(file,event)
{
// Manual additions have no event
if(typeof event == 'undefined')
{
event = {};
}
// Trigger start of uploading, calls callback
if(!this.resumable.isUploading())
{
if (!(this.onStart(event,this.resumable.files.length))) return;
}
// Here 'this' is the input
if(this.checkMime(file.file))
{
if(this.createStatus(event,file))
{
// Disable buttons
this.disabled_buttons
.not("[disabled]")
.attr("disabled", true)
.addClass('et2_button_ro')
.removeClass('et2_clickable')
.css('cursor', 'default');
// Actually start uploading
this.resumable.upload();
}
}
else
{
// Wrong mime type - show in the list of files
return this.createStatus(
this.egw().lang("File is of wrong type (%1 != %2)!", file.file.type, this.options.mime),
file
);
}
}
/**
* Add in the request id
*/
beforeSend(form) {
var instance = this.getInstanceManager();
return {
request_id: instance.etemplate_exec_id,
widget_id: this.id
};
}
/**
* Disables submit buttons while uploading
*/
onStart(event, file_count) {
// Hide any previous errors
this.hideMessage();
event.data = this;
//Add dropdown_progress
if (this.options.progress_dropdownlist)
{
this._build_progressDropDownList();
}
// Callback
if(this.options.onStart) return et2_call(this.options.onStart, event, file_count);
return true;
}
/**
* Re-enables submit buttons when done
*/
onFinish() {
this.disabled_buttons.attr("disabled", 0).css('cursor','pointer').removeClass('et2_button_ro');
var file_count = this.resumable.files.length;
// Remove files from list
while(this.resumable.files.length > 0)
{
this.resumable.removeFile(this.resumable.files[this.resumable.files.length -1]);
}
var event = jQuery.Event('upload');
event.data = this;
var result = false;
//Remove progress_dropDown_fileList class and unbind the click handler from body
if (this.options.progress_dropdownlist)
{
this.progress.removeClass("progress_dropDown_fileList");
jQuery(this.node).find('span').removeClass('totalProgress_loader');
jQuery('body').off('click');
}
if(this.options.onFinish && !jQuery.isEmptyObject(this.getValue()))
{
result = et2_call(this.options.onFinish, event, file_count);
}
else
{
result = (file_count == 0 || !jQuery.isEmptyObject(this.getValue()));
}
if(result)
{
// Fire legacy change action when done
this.change(this.input);
}
}
/**
* Build up dropdown progress with total count indicator
*
* @todo Implement totalProgress bar instead of ajax-loader, in order to show how much percent of uploading is completed
*/
private _build_progressDropDownList()
{
this.progress.addClass("progress_dropDown_fileList");
//Add uploading indicator and bind hover handler on it
jQuery(this.node).find('span').addClass('totalProgress_loader');
jQuery(this.node).find('span.et2_file_span').hover(function(){
jQuery('.progress_dropDown_fileList').show();
});
//Bind click handler to dismiss the dropdown while uploading
jQuery('body').on('click', function(event){
if (event.target.className != 'remove')
{
jQuery('.progress_dropDown_fileList').hide();
}
});
}
/**
* Creates the elements used for displaying the file, and it's upload status, and
* attaches them to the DOM
*
* @param _event Either the event, or an error message
*/
createStatus(_event, file)
{
var error = (typeof _event == "object" ? "" : _event);
if(this.options.max_file_size && file.size > this.options.max_file_size) {
error = this.egw().lang("File too large. Maximum %1", et2_vfsSize.prototype.human_size(this.options.max_file_size));
}
if(this.options.progress)
{
var widget = this.getRoot().getWidgetById(this.options.progress);
if(widget)
{
this.progress = jQuery(widget.getDOMNode());
this.progress.addClass("progress");
}
}
if(this.progress)
{
var fileName = file.fileName || 'file';
var status = jQuery("<li data-file='"+fileName.replace(/'/g, '&quot')+"'>"+fileName
+"<div class='remove'/><span class='progressBar'><p/></span></li>")
.appendTo(this.progress);
jQuery("div.remove",status).on('click', file, jQuery.proxy(this.cancel,this));
if(error != "")
{
status.addClass("message ui-state-error");
status.append("<div>"+error+"</diff>");
jQuery(".progressBar",status).css("display", "none");
}
}
return error == "";
}
private _fileProgress(file)
{
if(this.progress)
{
jQuery("li[data-file='"+file.fileName.replace(/'/g, '&quot')+"'] > span.progressBar > p").css("width", Math.ceil(file.progress()*100)+"%");
}
return true;
}
onError(event, name, error)
{
console.warn(event,name,error);
}
/**
* A file upload is finished, update the UI
*/
finishUpload(file, response)
{
var name = file.fileName || 'file';
if(typeof response == 'string') response = jQuery.parseJSON(response);
if(response.response[0] && typeof response.response[0].data.length == 'undefined') {
if(typeof this.options.value !== 'object' || !this.options.multiple)
{
this.set_value({});
}
for(var key in response.response[0].data) {
if(typeof response.response[0].data[key] == "string")
{
// Message from server - probably error
jQuery("[data-file='"+name.replace(/'/g, '&quot')+"']",this.progress)
.addClass("error")
.css("display", "block")
.text(response.response[0].data[key]);
}
else
{
this.options.value[key] = response.response[0].data[key];
// If not multiple, we already destroyed the status, so re-create it
if(!this.options.multiple)
{
this.createStatus({}, file);
}
if(this.progress)
{
jQuery("[data-file='"+name.replace(/'/g, '&quot')+"']",this.progress).addClass("message success");
}
}
}
}
else if (this.progress)
{
jQuery("[data-file='"+name.replace(/'/g, '&quot')+"']",this.progress)
.addClass("ui-state-error")
.css("display", "block")
.text(this.egw().lang("Server error"));
}
var event = jQuery.Event('upload');
event.data = this;
// Callback
if(this.options.onFinishOne)
{
return et2_call(this.options.onFinishOne,event,response,name);
}
return true;
}
/**
* Remove a file from the list of values
*
* @param {File|string} File object, or file name, to remove
*/
remove_file(file)
{
//console.info(filename);
if(typeof file == 'string')
{
file = {fileName: file};
}
for(var key in this.options.value)
{
if(this.options.value[key].name == file.fileName)
{
delete this.options.value[key];
jQuery('[data-file="'+file.fileName.replace(/'/g, '&quot')+'"]',this.node).remove();
return;
}
}
if(file.isComplete && !file.isComplete() && file.cancel) file.cancel();
}
/**
* Cancel a file - event callback
*/
cancel(e)
{
e.preventDefault();
// Look for file name in list
var target = jQuery(e.target).parents("li");
this.remove_file(e.data);
// In case it didn't make it to the list (error)
target.remove();
jQuery(e.target).remove();
}
/**
* Set readonly
*
* @param {boolean} _ro boolean readonly state, true means readonly
*/
set_readonly(_ro)
{
if (typeof _ro != "undefined")
{
this.options.readonly = _ro;
this.span.toggleClass('et2_file_ro',_ro);
if (this.options.readonly)
{
this.span.unbind('click');
}
else
{
var self = this;
this.span.off().bind('click',function(){self.input.click()});
}
}
}
}
et2_register_widget(et2_file, ["file"]);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,4 @@
"use strict";
/**
* EGroupware eTemplate2 - JS Groupbox object
*
@ -9,56 +10,76 @@
* @copyright Nathan Gray 2012
* @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
et2_core_baseWidget;
et2_core_baseWidget;
*/
var et2_core_widget_1 = require("./et2_core_widget");
var et2_core_baseWidget_1 = require("./et2_core_baseWidget");
var et2_core_inheritance_1 = require("./et2_core_inheritance");
/**
* Class which implements the groupbox tag
*
* @augments et2_baseWidget
*/
var et2_groupbox = (function(){ "use strict"; return et2_baseWidget.extend(
{
/**
* Constructor
*
* @memberOf et2_groupbox
*/
init: function() {
this._super.apply(this, arguments);
this.setDOMNode(document.createElement("fieldset"));
}
});}).call(this);
et2_register_widget(et2_groupbox, ["groupbox"]);
var et2_groupbox = /** @class */ (function (_super) {
__extends(et2_groupbox, _super);
/**
* Constructor
*
* @memberOf et2_groupbox
*/
function et2_groupbox(_parent, _attrs, _child) {
var _this =
// Call the inherited constructor
_super.call(this, _parent, _attrs, et2_core_inheritance_1.ClassWithAttributes.extendAttributes(et2_groupbox._attributes, _child || {})) || this;
_this.setDOMNode(document.createElement("fieldset"));
return _this;
}
return et2_groupbox;
}(et2_core_baseWidget_1.et2_baseWidget));
et2_core_widget_1.et2_register_widget(et2_groupbox, ["groupbox"]);
/**
* @augments et2_baseWidget
*/
var et2_groupbox_legend = (function(){ "use strict"; return et2_baseWidget.extend(
{
attributes: {
"label": {
"name": "Label",
"type": "string",
"default": "",
"description": "Label for group box",
"translate" : true
}
},
/**
* Constructor
*
* @memberOf et2_groupbox_legend
*/
init: function() {
this._super.apply(this, arguments);
var legend = jQuery(document.createElement("legend")).text(this.options.label);
this.setDOMNode(legend[0]);
}
});}).call(this);
et2_register_widget(et2_groupbox_legend, ["caption"]);
var et2_groupbox_legend = /** @class */ (function (_super) {
__extends(et2_groupbox_legend, _super);
/**
* Constructor
*
* @memberOf et2_groupbox_legend
*/
function et2_groupbox_legend(_parent, _attrs, _child) {
var _this =
// Call the inherited constructor
_super.call(this, _parent, _attrs, et2_core_inheritance_1.ClassWithAttributes.extendAttributes(et2_groupbox_legend._attributes, _child || {})) || this;
var legend = jQuery(document.createElement("legend")).text(_this.options.label);
_this.setDOMNode(legend[0]);
return _this;
}
et2_groupbox_legend._attributes = {
"label": {
"name": "Label",
"type": "string",
"default": "",
"description": "Label for group box",
"translate": true
}
};
return et2_groupbox_legend;
}(et2_core_baseWidget_1.et2_baseWidget));
et2_core_widget_1.et2_register_widget(et2_groupbox_legend, ["caption"]);
//# sourceMappingURL=et2_widget_groupbox.js.map

View File

@ -0,0 +1,72 @@
/**
* EGroupware eTemplate2 - JS Groupbox object
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage api
* @link http://www.egroupware.org
* @author Nathan Gray
* @copyright Nathan Gray 2012
* @version $Id$
*/
/*egw:uses
et2_core_baseWidget;
*/
import {et2_register_widget, WidgetConfig} from "./et2_core_widget";
import {et2_baseWidget} from "./et2_core_baseWidget";
import {ClassWithAttributes} from "./et2_core_inheritance";
/**
* Class which implements the groupbox tag
*
* @augments et2_baseWidget
*/
class et2_groupbox extends et2_baseWidget
{
/**
* Constructor
*
* @memberOf et2_groupbox
*/
constructor(_parent, _attrs? : WidgetConfig, _child? : object)
{
// Call the inherited constructor
super(_parent, _attrs, ClassWithAttributes.extendAttributes(et2_groupbox._attributes, _child || {}));
this.setDOMNode(document.createElement("fieldset"));
}
}
et2_register_widget(et2_groupbox, ["groupbox"]);
/**
* @augments et2_baseWidget
*/
class et2_groupbox_legend extends et2_baseWidget
{
static readonly _attributes : any = {
"label": {
"name": "Label",
"type": "string",
"default": "",
"description": "Label for group box",
"translate" : true
}
};
/**
* Constructor
*
* @memberOf et2_groupbox_legend
*/
constructor(_parent, _attrs? : WidgetConfig, _child? : object)
{
// Call the inherited constructor
super(_parent, _attrs, ClassWithAttributes.extendAttributes(et2_groupbox_legend._attributes, _child || {}));
let legend = jQuery(document.createElement("legend")).text(this.options.label);
this.setDOMNode(legend[0]);
}
}
et2_register_widget(et2_groupbox_legend, ["caption"]);

View File

@ -1,3 +1,4 @@
"use strict";
/**
* EGroupware eTemplate2 - JS HBox object
*
@ -6,182 +7,166 @@
* @subpackage api
* @link http://www.egroupware.org
* @author Andreas Stöckel
* @copyright Stylite 2011
* @version $Id: et2_box.js 36147 2011-08-16 13:12:39Z igel457 $
*/
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
/vendor/bower-asset/jquery/dist/jquery.js;
et2_core_baseWidget;
/vendor/bower-asset/jquery/dist/jquery.js;
et2_core_baseWidget;
*/
var et2_core_inheritance_1 = require("./et2_core_inheritance");
var et2_core_widget_1 = require("./et2_core_widget");
var et2_core_baseWidget_1 = require("./et2_core_baseWidget");
/**
* Class which implements hbox tag
*
* @augments et2_baseWidget
*/
var et2_hbox = (function(){ "use strict"; return et2_baseWidget.extend(
{
createNamespace: true,
/**
* Constructor
*
* @memberOf et2_hbox
*/
init: function() {
this._super.apply(this, arguments);
this.alignData = {
"hasAlign": false,
"hasLeft": false,
"hasCenter": false,
"hasRight": false,
"lastAlign": "left"
};
this.leftDiv = null;
this.rightDiv = null;
this.div = jQuery(document.createElement("div"))
.addClass("et2_" + this._type)
.addClass("et2_box_widget");
this.setDOMNode(this.div[0]);
},
_buildAlignCells: function() {
if (this.alignData.hasAlign)
{
// Check whether we have more than one type of align
var mto = (this.alignData.hasLeft && this.alignData.hasRight) ||
(this.alignData.hasLeft && this.alignData.hasCenter) ||
(this.alignData.hasCenter && this.alignData.hasRight);
if (!mto)
{
// If there is only one type of align, we simply have to set
// the align of the top container
if (this.alignData.lastAlign != "left")
{
this.div.addClass("et2_hbox_al_" + this.alignData.lastAlign);
}
}
else
{
// Create an additional container for elements with align type
// "right"
if (this.alignData.hasRight)
{
this.rightDiv = jQuery(document.createElement("div"))
.addClass("et2_hbox_right")
.appendTo(this.div);
}
// Create an additional container for elements with align type
// left, as the top container is used for the centered elements
if (this.alignData.hasCenter)
{
// Create the left div if an element is centered
this.leftDiv = jQuery(document.createElement("div"))
.addClass("et2_hbox_left")
.appendTo(this.div);
this.div.addClass("et2_hbox_al_center");
}
}
}
},
/**
* The overwritten loadFromXML function checks whether any child element has
* a special align value.
*
* @param {object} _node
*/
loadFromXML: function(_node) {
// Check whether any child node has an alignment tag
et2_filteredNodeIterator(_node, function(_node) {
var align = _node.getAttribute("align");
if (!align)
{
align = "left";
}
if (align != "left")
{
this.alignData.hasAlign = true;
}
this.alignData.lastAlign = align;
switch (align)
{
case "left":
this.alignData.hasLeft = true;
break;
case "right":
this.alignData.hasRight = true;
break;
case "center":
this.alignData.hasCenter = true;
break;
}
}, this);
// Build the align cells
this._buildAlignCells();
// Load the nodes as usual
this._super.apply(this, arguments);
},
assign: function(_obj) {
// Copy the align data and the cells from the object which should be
// assigned
this.alignData = et2_cloneObject(_obj.alignData);
this._buildAlignCells();
// Call the inherited assign function
this._super.apply(this, arguments);
},
getDOMNode: function(_sender) {
// Return a special align container if this hbox needs it
if (_sender != this && this.alignData.hasAlign)
{
// Check whether we've create a special container for the widget
var align = (_sender.implements(et2_IAligned) ?
_sender.get_align() : "left");
if (align == "left" && this.leftDiv != null)
{
return this.leftDiv[0];
}
if (align == "right" && this.rightDiv != null)
{
return this.rightDiv[0];
}
}
// Normally simply return the hbox-div
return this._super.apply(this, arguments);
},
/**
* Tables added to the root node need to be inline instead of blocks
*
* @param {et2_widget} child child-widget to add
*/
addChild: function(child) {
this._super.apply(this, arguments);
if(child.instanceOf && child.instanceOf(et2_grid) || child._type == 'et2_grid')
{
jQuery(child.getDOMNode(child)).css("display", "inline-table");
}
}
});}).call(this);
et2_register_widget(et2_hbox, ["hbox"]);
var et2_hbox = /** @class */ (function (_super) {
__extends(et2_hbox, _super);
/**
* Constructor
*
* @memberOf et2_hbox
*/
function et2_hbox(_parent, _attrs, _child) {
var _this = _super.call(this, _parent, _attrs, et2_core_inheritance_1.ClassWithAttributes.extendAttributes(et2_hbox._attributes, _child || {})) || this;
_this.alignData = {
"hasAlign": false,
"hasLeft": false,
"hasCenter": false,
"hasRight": false,
"lastAlign": "left"
};
_this.leftDiv = null;
_this.rightDiv = null;
_this.div = null;
_this.leftDiv = null;
_this.rightDiv = null;
_this.div = jQuery(document.createElement("div"))
.addClass("et2_" + _super.prototype.getType.call(_this))
.addClass("et2_box_widget");
_super.prototype.setDOMNode.call(_this, _this.div[0]);
return _this;
}
et2_hbox.prototype._createNamespace = function () {
return true;
};
et2_hbox.prototype._buildAlignCells = function () {
if (this.alignData.hasAlign) {
// Check whether we have more than one type of align
var mto = (this.alignData.hasLeft && this.alignData.hasRight) ||
(this.alignData.hasLeft && this.alignData.hasCenter) ||
(this.alignData.hasCenter && this.alignData.hasRight);
if (!mto) {
// If there is only one type of align, we simply have to set
// the align of the top container
if (this.alignData.lastAlign != "left") {
this.div.addClass("et2_hbox_al_" + this.alignData.lastAlign);
}
}
else {
// Create an additional container for elements with align type
// "right"
if (this.alignData.hasRight) {
this.rightDiv = jQuery(document.createElement("div"))
.addClass("et2_hbox_right")
.appendTo(this.div);
}
// Create an additional container for elements with align type
// left, as the top container is used for the centered elements
if (this.alignData.hasCenter) {
// Create the left div if an element is centered
this.leftDiv = jQuery(document.createElement("div"))
.addClass("et2_hbox_left")
.appendTo(this.div);
this.div.addClass("et2_hbox_al_center");
}
}
}
};
/**
* The overwritten loadFromXML function checks whether any child element has
* a special align value.
*
* @param {object} _node
*/
et2_hbox.prototype.loadFromXML = function (_node) {
// Check whether any child node has an alignment tag
et2_filteredNodeIterator(_node, function (_node) {
var align = _node.getAttribute("align");
if (!align) {
align = "left";
}
if (align != "left") {
this.alignData.hasAlign = true;
}
this.alignData.lastAlign = align;
switch (align) {
case "left":
this.alignData.hasLeft = true;
break;
case "right":
this.alignData.hasRight = true;
break;
case "center":
this.alignData.hasCenter = true;
break;
}
}, this);
// Build the align cells
this._buildAlignCells();
// Load the nodes as usual
_super.prototype.loadFromXML.call(this, _node);
};
et2_hbox.prototype.assign = function (_obj) {
// Copy the align data and the cells from the object which should be
// assigned
this.alignData = et2_cloneObject(_obj.alignData);
this._buildAlignCells();
// Call the inherited assign function
_super.prototype.assign.call(this, _obj);
};
et2_hbox.prototype.getDOMNode = function (_sender) {
// Return a special align container if this hbox needs it
if (_sender != this && this.alignData.hasAlign) {
// Check whether we've create a special container for the widget
var align = (_sender.implements(et2_IAligned) ?
_sender.get_align() : "left");
if (align == "left" && this.leftDiv != null) {
return this.leftDiv[0];
}
if (align == "right" && this.rightDiv != null) {
return this.rightDiv[0];
}
}
// Normally simply return the hbox-div
return _super.prototype.getDOMNode.call(this, _sender);
};
/**
* Tables added to the root node need to be inline instead of blocks
*
* @param {et2_widget} child child-widget to add
*/
et2_hbox.prototype.addChild = function (child) {
_super.prototype.addChild.call(this, child);
if (child.instanceOf && child.instanceOf(et2_grid) && this.isAttached() || child._type == 'et2_grid' && this.isAttached()) {
jQuery(child.getDOMNode(child)).css("display", "inline-table");
}
};
return et2_hbox;
}(et2_core_baseWidget_1.et2_baseWidget));
et2_core_widget_1.et2_register_widget(et2_hbox, ["hbox"]);
//# sourceMappingURL=et2_widget_hbox.js.map

View File

@ -0,0 +1,197 @@
/**
* EGroupware eTemplate2 - JS HBox object
*
* @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
/vendor/bower-asset/jquery/dist/jquery.js;
et2_core_baseWidget;
*/
import {ClassWithAttributes} from "./et2_core_inheritance";
import {et2_register_widget, WidgetConfig} from "./et2_core_widget";
import {et2_baseWidget} from "./et2_core_baseWidget";
/**
* Class which implements hbox tag
*
* @augments et2_baseWidget
*/
class et2_hbox extends et2_baseWidget
{
alignData : any = {
"hasAlign": false,
"hasLeft": false,
"hasCenter": false,
"hasRight": false,
"lastAlign": "left"
};
leftDiv : JQuery = null;
rightDiv : JQuery = null;
div : JQuery = null;
/**
* Constructor
*
* @memberOf et2_hbox
*/
constructor(_parent?, _attrs? : WidgetConfig, _child? : object)
{
super(_parent, _attrs, ClassWithAttributes.extendAttributes(et2_hbox._attributes, _child || {}));
this.leftDiv = null;
this.rightDiv = null;
this.div = jQuery(document.createElement("div"))
.addClass("et2_" + super.getType())
.addClass("et2_box_widget");
super.setDOMNode(this.div[0]);
}
_createNamespace() : boolean
{
return true;
}
_buildAlignCells() {
if (this.alignData.hasAlign)
{
// Check whether we have more than one type of align
let mto = (this.alignData.hasLeft && this.alignData.hasRight) ||
(this.alignData.hasLeft && this.alignData.hasCenter) ||
(this.alignData.hasCenter && this.alignData.hasRight);
if (!mto)
{
// If there is only one type of align, we simply have to set
// the align of the top container
if (this.alignData.lastAlign != "left")
{
this.div.addClass("et2_hbox_al_" + this.alignData.lastAlign);
}
}
else
{
// Create an additional container for elements with align type
// "right"
if (this.alignData.hasRight)
{
this.rightDiv = jQuery(document.createElement("div"))
.addClass("et2_hbox_right")
.appendTo(this.div);
}
// Create an additional container for elements with align type
// left, as the top container is used for the centered elements
if (this.alignData.hasCenter)
{
// Create the left div if an element is centered
this.leftDiv = jQuery(document.createElement("div"))
.addClass("et2_hbox_left")
.appendTo(this.div);
this.div.addClass("et2_hbox_al_center");
}
}
}
}
/**
* The overwritten loadFromXML function checks whether any child element has
* a special align value.
*
* @param {object} _node
*/
loadFromXML(_node) {
// Check whether any child node has an alignment tag
et2_filteredNodeIterator(_node, function(_node) {
let align = _node.getAttribute("align");
if (!align)
{
align = "left";
}
if (align != "left")
{
this.alignData.hasAlign = true;
}
this.alignData.lastAlign = align;
switch (align)
{
case "left":
this.alignData.hasLeft = true;
break;
case "right":
this.alignData.hasRight = true;
break;
case "center":
this.alignData.hasCenter = true;
break;
}
}, this);
// Build the align cells
this._buildAlignCells();
// Load the nodes as usual
super.loadFromXML(_node);
}
assign(_obj) {
// Copy the align data and the cells from the object which should be
// assigned
this.alignData = et2_cloneObject(_obj.alignData);
this._buildAlignCells();
// Call the inherited assign function
super.assign(_obj);
}
getDOMNode(_sender) {
// Return a special align container if this hbox needs it
if (_sender != this && this.alignData.hasAlign)
{
// Check whether we've create a special container for the widget
let align = (_sender.implements(et2_IAligned) ?
_sender.get_align() : "left");
if (align == "left" && this.leftDiv != null)
{
return this.leftDiv[0];
}
if (align == "right" && this.rightDiv != null)
{
return this.rightDiv[0];
}
}
// Normally simply return the hbox-div
return super.getDOMNode(_sender);
}
/**
* Tables added to the root node need to be inline instead of blocks
*
* @param {et2_widget} child child-widget to add
*/
addChild(child) {
super.addChild(child);
if(child.instanceOf && child.instanceOf(et2_grid) && this.isAttached() || child._type == 'et2_grid' && this.isAttached())
{
jQuery(child.getDOMNode(child)).css("display", "inline-table");
}
}
}
et2_register_widget(et2_hbox, ["hbox"]);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,747 @@
/**
* EGroupware eTemplate2 - JS History log
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage api
* @link http://www.egroupware.org
* @author Nathan Gray
* @copyright 2012 Nathan Gray
*/
/*egw:uses
/vendor/bower-asset/jquery/dist/jquery.js;
/vendor/bower-asset/jquery-ui/jquery-ui.js;
et2_core_valueWidget;
// Include the grid classes
et2_dataview;
*/
import {et2_IDataProvider} from "./et2_dataview_interfaces";
import {et2_register_widget, WidgetConfig} from "./et2_core_widget";
import {ClassWithAttributes} from "./et2_core_inheritance";
import {et2_valueWidget} from "./et2_core_valueWidget";
import {et2_dataview} from "./et2_dataview";
import {et2_dataview_column} from "./et2_dataview_model_columns";
import {et2_dataview_controller} from "./et2_dataview_controller";
import {et2_diff} from "./et2_widget_diff";
/**
* eTemplate history log widget displays a list of changes to the current record.
* The widget is encapsulated, and only needs the record's ID, and a map of
* fields:widgets for display.
*
* It defers its initialization until the tab that it's on is selected, to avoid
* wasting time if the user never looks at it.
*
* @augments et2_valueWidget
*/
export class et2_historylog extends et2_valueWidget implements et2_IDataProvider,et2_IResizeable
{
static readonly _attributes = {
"value": {
"name": "Value",
"type": "any",
"description": "Object {app: ..., id: ..., status-widgets: {}} where status-widgets is a map of fields to widgets used to display those fields"
},
"status_id":{
"name": "status_id",
"type": "string",
"default": "status",
"description": "The history widget is traditionally named 'status'. If you name another widget in the same template 'status', you can use this attribute to re-name the history widget. "
},
"columns": {
"name": "columns",
"type": "string",
"default": "user_ts,owner,status,new_value,old_value",
"description": "Columns to display. Default is user_ts,owner,status,new_value,old_value"
},
"get_rows": {
"name": "get_rows",
"type": "string",
"default": "EGroupware\\Api\\Storage\\History::get_rows",
"description": "Method to get rows"
}
};
legacyOptions = ["status_id"];
protected static columns = [
{'id': 'user_ts', caption: 'Date', 'width': '120px', widget_type: 'date-time', widget: null, nodes: null},
{'id': 'owner', caption: 'User', 'width': '150px', widget_type: 'select-account', widget: null, nodes: null},
{'id': 'status', caption: 'Changed', 'width': '120px', widget_type: 'select', widget: null, nodes: null},
{'id': 'new_value', caption: 'New Value', 'width': '50%', widget: null, nodes: null},
{'id': 'old_value', caption: 'Old Value', 'width': '50%', widget: null, nodes: null}
];
static readonly TIMESTAMP = 0;
static readonly OWNER = 1;
static readonly FIELD = 2;
static readonly NEW_VALUE = 3;
static readonly OLD_VALUE = 4;
private div: JQuery;
private innerDiv: JQuery;
private _filters: { appname: string; record_id: string; get_rows: string; };
private dynheight: et2_dynheight;
private dataview: et2_dataview;
private controller: et2_dataview_controller;
private fields: any;
private diff: et2_diff;
private value: any;
/**
* Constructor
*
* @memberOf et2_historylog
*/
constructor(_parent?, _attrs? : WidgetConfig, _child? : object) {
super(_parent, _attrs, ClassWithAttributes.extendAttributes(et2_historylog._attributes, _child || {}));
this.div = jQuery(document.createElement("div"))
.addClass("et2_historylog");
this.innerDiv = jQuery(document.createElement("div"))
.appendTo(this.div);
}
set_status_id( _new_id)
{
this.options.status_id = _new_id;
}
doLoadingFinished( ) : boolean | JQueryPromise<unknown>
{
super.doLoadingFinished();
// Find the tab
let tab = this.get_tab_info();
if(tab)
{
// Bind the action to when the tab is selected
const handler = function (e) {
e.data.div.unbind("click.history");
// Bind on click tap, because we need to update history size
// after a rezise happend and history log was not the active tab
e.data.div.bind("click.history", {"history": e.data.history, div: tab.flagDiv}, function (e) {
if (e.data.history && e.data.history.dynheight) {
e.data.history.dynheight.update(function (_w, _h) {
e.data.history.dataview.resize(_w, _h);
});
}
});
if (typeof e.data.history.dataview == "undefined") {
e.data.history.finishInit();
if (e.data.history.dynheight) {
e.data.history.dynheight.update(function (_w, _h) {
e.data.history.dataview.resize(_w, _h);
});
}
}
};
tab.flagDiv.bind("click.history",{"history": this, div: tab.flagDiv}, handler);
// Display if history tab is selected
if(tab.contentDiv.is(':visible') && typeof this.dataview == 'undefined')
{
tab.flagDiv.trigger("click.history");
}
}
else
{
this.finishInit();
}
return true;
}
_createNamespace()
{
return true;
}
/**
* Finish initialization which was skipped until tab was selected
*/
finishInit( )
{
// No point with no ID
if(!this.options.value || !this.options.value.id)
{
return;
}
this._filters = {
record_id: this.options.value.id,
appname: this.options.value.app,
get_rows: this.options.get_rows
};
// Warn if status_id is the same as history id, that causes overlap and missing labels
if(this.options.status_id === this.id)
{
this.egw().debug("warn", "status_id attribute should not be the same as historylog ID");
}
// Create the dynheight component which dynamically scales the inner
// container.
this.div.parentsUntil('.et2_tabs').height('100%');
const parent = this.get_tab_info();
this.dynheight = new et2_dynheight(parent ? parent.contentDiv : this.div.parent(),
this.innerDiv, 250
);
// Create the outer grid container
this.dataview = new et2_dataview(this.innerDiv, this.egw());
const dataview_columns = [];
let _columns = typeof this.options.columns === "string" ?
this.options.columns.split(',') : this.options.columns;
for (var i = 0; i < et2_historylog.columns.length; i++)
{
dataview_columns[i] = {
"id": et2_historylog.columns[i].id,
"caption": et2_historylog.columns[i].caption,
"width":et2_historylog.columns[i].width,
"visibility":_columns.indexOf(et2_historylog.columns[i].id) < 0 ?
et2_dataview_column.ET2_COL_VISIBILITY_INVISIBLE : et2_dataview_column.ET2_COL_VISIBILITY_VISIBLE
};
}
this.dataview.setColumns(dataview_columns);
// Create widgets for columns that stay the same, and set up varying widgets
this.createWidgets();
// Create the gridview controller
const linkCallback = function ()
{
};
this.controller = new et2_dataview_controller(null, this.dataview.grid);
this.controller.setContext(this);
this.controller.setDataProvider(this);
this.controller.setLinkCallback(linkCallback);
this.controller.setRowCallback(this.rowCallback);
this.controller.setActionObjectManager(null);
const total = typeof this.options.value.total !== "undefined" ?
this.options.value.total : 0;
// This triggers an invalidate, which updates the grid
this.dataview.grid.setTotalCount(total);
// Insert any data sent from server, so invalidate finds data already
if(this.options.value.rows && this.options.value.num_rows)
{
this.controller.loadInitialData(
this.options.value.dataStorePrefix,
this.options.value.row_id,
this.options.value.rows
);
// Remove, to prevent duplication
delete this.options.value.rows;
// This triggers an invalidate, which updates the grid
this.dataview.grid.setTotalCount(total);
}
else
{
// Trigger the initial update
this.controller.update();
}
// Write something inside the column headers
for (var i = 0; i < et2_historylog.columns.length; i++)
{
jQuery(this.dataview.getHeaderContainerNode(i)).text(et2_historylog.columns[i].caption);
}
// Register a resize callback
const self = this;
jQuery(window).on('resize.' +this.options.value.app + this.options.value.id, function()
{
if (self && typeof self.dynheight != 'undefined') self.dynheight.update(function(_w, _h) {
self.dataview.resize(_w, _h);
});
});
}
/**
* Destroys all
*/
destroy( )
{
// Unbind, if bound
if(this.options.value && !this.options.value.id)
{
jQuery(window).off('.' +this.options.value.app + this.options.value.id);
}
// Free the widgets
for(let i = 0; i < et2_historylog.columns.length; i++)
{
if(et2_historylog.columns[i].widget) et2_historylog.columns[i].widget.destroy();
}
for(let key in this.fields)
{
this.fields[key].widget.destroy();
}
// Free the grid components
if(this.dataview) this.dataview.destroy();
if(this.controller) this.controller.destroy();
if(this.dynheight) this.dynheight.destroy();
super.destroy();
}
/**
* Create all needed widgets for new / old values
*/
createWidgets( )
{
// Constant widgets - first 3 columns
for(let i = 0; i < et2_historylog.columns.length; i++)
{
if(et2_historylog.columns[i].widget_type)
{
// Status ID is allowed to be remapped to something else. Only affects the widget ID though
var attrs = {'readonly': true, 'id': (i == et2_historylog.FIELD ? this.options.status_id : et2_historylog.columns[i].id)};
et2_historylog.columns[i].widget = et2_createWidget(et2_historylog.columns[i].widget_type, attrs, this);
et2_historylog.columns[i].widget.transformAttributes(attrs);
et2_historylog.columns[i].nodes = jQuery(et2_historylog.columns[i].widget.getDetachedNodes());
}
}
// Add in handling for links
if(typeof this.options.value['status-widgets']['~link~'] == 'undefined')
{
et2_historylog.columns[et2_historylog.FIELD].widget.optionValues['~link~'] = this.egw().lang('link');
this.options.value['status-widgets']['~link~'] = 'link';
}
// Add in handling for files
if(typeof this.options.value['status-widgets']['~file~'] == 'undefined')
{
et2_historylog.columns[et2_historylog.FIELD].widget.optionValues['~file~'] = this.egw().lang('File');
this.options.value['status-widgets']['~file~'] = 'vfs';
}
// Add in handling for user-agent & action
if(typeof this.options.value['status-widgets']['user_agent_action'] == 'undefined')
{
et2_historylog.columns[et2_historylog.FIELD].widget.optionValues['user_agent_action'] = this.egw().lang('User-agent & action');
}
// Per-field widgets - new value & old value
this.fields = {};
let labels = et2_historylog.columns[et2_historylog.FIELD].widget.optionValues;
// Custom fields - Need to create one that's all read-only for proper display
let cf_widget = et2_createWidget('customfields', {'readonly':true}, this);
cf_widget.loadFields();
// Override this or it may damage the real values
cf_widget.getValue = function() {return null;};
for(let key in cf_widget.widgets)
{
// Add label
labels[cf_widget.prefix + key] = cf_widget.options.customfields[key].label;
// If it doesn't support detached nodes, just treat it as text
if(cf_widget.widgets[key].getDetachedNodes)
{
var nodes = cf_widget.widgets[key].getDetachedNodes();
for(var i = 0; i < nodes.length; i++)
{
if(nodes[i] == null) nodes.splice(i,1);
}
// Save to use for each row
this.fields[cf_widget.prefix + key] = {
attrs: cf_widget.widgets[key].options,
widget: cf_widget.widgets[key],
nodes: jQuery(nodes)
};
}
}
// Add all cf labels
et2_historylog.columns[et2_historylog.FIELD].widget.set_select_options(labels);
// From app
for(var key in this.options.value['status-widgets'])
{
let attrs = jQuery.extend({'readonly': true, 'id': key}, this.getArrayMgr('modifications').getEntry(key));
const field = attrs.type || this.options.value['status-widgets'][key];
const options = null;
const widget = this._create_widget(key, field, attrs, options);
if(widget === null)
{
continue;
}
if(widget.instanceOf(et2_selectbox)) widget.options.multiple = true;
widget.transformAttributes(attrs);
// Save to use for each row
let nodes = widget._children.length ? [] : jQuery(widget.getDetachedNodes());
for(let i = 0; i < widget._children.length; i++)
{
// @ts-ignore
nodes.push(jQuery(widget._children[i].getDetachedNodes()));
}
this.fields[key] = {
attrs: attrs,
widget: widget,
nodes: nodes
};
}
// Widget for text diffs
const diff = et2_createWidget('diff', {}, this);
this.diff = {
// @ts-ignore
widget: diff,
nodes: jQuery(diff.getDetachedNodes())
};
}
_create_widget(key, field, attrs, options)
{
let widget = null;
// If field has multiple parts (is object) and isn't an obvious select box
if(typeof field === 'object')
{
// Check for multi-part statuses needing multiple widgets
let need_box = false;//!this.getArrayMgr('sel_options').getEntry(key);
for(let j in field)
{
// Require widget to be a widget, to avoid invalid widgets
// (and template, which is a widget and an infolog todo status)
if(et2_registry[field[j]] && ['template'].indexOf(field[j]) < 0)// && (et2_registry[field[j]].prototype.instanceOf(et2_valueWidget))
{
need_box = true;
break;
}
}
if(need_box)
{
// Multi-part value needs multiple widgets
widget = et2_createWidget('vbox', attrs, this);
for(var i in field)
{
let type = field[i];
const child_attrs = jQuery.extend({}, attrs);
if(typeof type === 'object')
{
child_attrs['select_options'] = field[i];
type = 'select';
}
else
{
delete child_attrs['select_options'];
}
child_attrs.id = i;
const child = this._create_widget(i, type, child_attrs, options);
widget.addChild(child);
child.transformAttributes(child_attrs);
}
}
else
{
attrs['select_options'] = field;
}
}
// Check for options after the type, ex: link-entry:infolog
else if (field.indexOf(':') > 0)
{
var options = field.split(':');
field = options.shift();
}
if(widget === null)
{
widget = et2_createWidget(typeof field === 'string' ? field : 'select', attrs, this);
}
if(!widget.instanceOf(et2_IDetachedDOM))
{
this.egw().debug("warn", this, "Invalid widget " + field + " for " + key + ". Status widgets must implement et2_IDetachedDOM.");
return null;
}
// Parse / set legacy options
if(options)
{
const mgr = this.getArrayMgr("content");
for(let i = 0; i < options.length && i < widget.legacyOptions.length; i++)
{
// Not set
if(options[i] === "") continue;
const attr = widget.attributes[widget.legacyOptions[i]];
let attrValue = options[i];
// If the attribute is marked as boolean, parse the
// expression as bool expression.
if (attr.type === "boolean")
{
attrValue = mgr.parseBoolExpression(attrValue);
}
else
{
attrValue = mgr.expandName(attrValue);
}
attrs[widget.legacyOptions[i]] = attrValue;
if(typeof widget['set_'+widget.legacyOptions[i]] === 'function')
{
widget['set_'+widget.legacyOptions[i]].call(widget, attrValue);
}
else
{
widget.options[widget.legacyOptions[i]] = attrValue;
}
}
}
return widget;
}
getDOMNode( _sender)
{
if (_sender == this)
{
return this.div[0];
}
for (let i = 0; i < et2_historylog.columns.length; i++)
{
if (_sender == et2_historylog.columns[i].widget)
{
return this.dataview.getHeaderContainerNode(i);
}
}
return null;
}
dataFetch( _queriedRange, _callback, _context)
{
// Skip getting data if there's no ID
if(!this.value.id) return;
// Set num_rows to fetch via nextmatch
if ( this.options.value['num_rows'] )
_queriedRange['num_rows'] = this.options.value['num_rows'];
const historylog = this;
// Pass the fetch call to the API
this.egw().dataFetch(
this.getInstanceManager().etemplate_exec_id,
_queriedRange,
this._filters,
this.id,
function(_response) {
_callback.call(this,_response);
// This seems to prevent unwanted scrollbars
historylog.div.hide();
window.setTimeout(function() {
historylog.div.show();
}.bind(historylog),100);
},
_context,
[]
);
}
// Needed by interface
dataRegisterUID( _uid, _callback, _context)
{
this.egw().dataRegisterUID(_uid, _callback, _context, this.getInstanceManager().etemplate_exec_id,
this.id);
}
dataUnregisterUID( _uid, _callback, _context)
{
// Needed by interface
}
/**
* The row callback gets called by the gridview controller whenever
* the actual DOM-Nodes for a node with the given data have to be
* created.
*
* @param {type} _data
* @param {type} _row
* @param {type} _idx
* @param {type} _entry
*/
rowCallback( _data, _row, _idx, _entry)
{
let tr = _row.getDOMNode();
jQuery(tr).attr("valign","top");
let row = this.dataview.rowProvider.getPrototype("default");
let self = this;
jQuery("div", row).each(function (i) {
let nodes : any[] | JQuery = [];
let widget = et2_historylog.columns[i].widget;
let value = _data[et2_historylog.columns[i].id];
if(et2_historylog.OWNER === i && _data['share_email'])
{
// Show share email instead of owner
widget = undefined;
value = _data['share_email'];
}
// Get widget from list, unless it needs a diff widget
if(typeof widget == 'undefined' && typeof self.fields[_data.status] != 'undefined' && (
i < et2_historylog.NEW_VALUE ||
i >= et2_historylog.NEW_VALUE && (
self.fields[_data.status].nodes || !self._needsDiffWidget(_data['status'], _data[et2_historylog.columns[et2_historylog.OLD_VALUE].id])
)
))
{
widget = self.fields[_data.status].widget;
if(!widget._children.length)
{
nodes = self.fields[_data.status].nodes.clone();
}
for(var j = 0; j < widget._children.length; j++)
{
// @ts-ignore
nodes.push(self.fields[_data.status].nodes[j].clone());
if(widget._children[j].instanceOf(et2_diff))
{
self._spanValueColumns(jQuery(this));
}
}
}
else if (widget)
{
nodes = et2_historylog.columns[i].nodes.clone();
}
else if ((
// Already parsed & cached
typeof _data[et2_historylog.columns[et2_historylog.NEW_VALUE].id] == "object" &&
typeof _data[et2_historylog.columns[et2_historylog.NEW_VALUE].id] != "undefined" &&
_data[et2_historylog.columns[et2_historylog.NEW_VALUE].id] !== null) || // typeof null === 'object'
// Large old value
self._needsDiffWidget(_data['status'], _data[et2_historylog.columns[et2_historylog.OLD_VALUE].id]) ||
// Large new value
self._needsDiffWidget(_data['status'], _data[et2_historylog.columns[et2_historylog.NEW_VALUE].id]))
{
// Large text value - span both columns, and show a nice diff
let jthis = jQuery(this);
if(i === et2_historylog.NEW_VALUE)
{
// Diff widget
widget = self.diff.widget;
nodes = self.diff.nodes.clone();
if(widget) widget.setDetachedAttributes(nodes, {
value: value,
label: jthis.parents("td").prev().text()
});
self._spanValueColumns(jthis);
}
}
else
{
// No widget fallback - display actual value
nodes = jQuery('<span>').text(value === null ? '' : value);
}
if(widget)
{
if(widget._children.length)
{
// Multi-part values
const box = jQuery(widget.getDOMNode()).clone();
for(var j = 0; j < widget._children.length; j++)
{
const id = widget._children[j].id;
const widget_value = value ? value[id] || "" : "";
widget._children[j].setDetachedAttributes(nodes[j], {value:widget_value});
box.append(nodes[j]);
}
nodes = box;
}
else
{
widget.setDetachedAttributes(nodes, {value:value});
}
}
jQuery(this).append(nodes);
});
jQuery(tr).append(row.children());
return tr;
}
/**
* How to tell if the row needs a diff widget or not
*
* @param {string} columnName
* @param {string} value
* @returns {Boolean}
*/
_needsDiffWidget( columnName, value)
{
if(typeof value !== "string" && value)
{
this.egw().debug("warn", "Crazy diff value", value);
return false;
}
return value === '***diff***';
}
/**
* Make a single row's new value cell span across both new value and old value
* columns. Used for diff widget.
*
* @param {jQuery} row jQuery wrapped row node
*/
_spanValueColumns(row)
{
// Stretch column 4
row.parents("td").attr("colspan", 2)
.css("border-right", "none");
row.css("width", (
this.dataview.getColumnMgr().getColumnWidth(et2_historylog.NEW_VALUE) +
this.dataview.getColumnMgr().getColumnWidth(et2_historylog.OLD_VALUE)-10)+'px');
// Skip column 5
row.parents("td").next().remove();
}
resize(_height)
{
if (typeof this.options != 'undefined' && _height
&& typeof this.options.resize_ratio != 'undefined')
{
// apply the ratio
_height = (this.options.resize_ratio != '')? _height * this.options.resize_ratio: _height;
if (_height != 0)
{
// 250px is the default value for history widget
// if it's not loaded yet and window is resized
// then add the default height with excess_height
if (this.div.height() == 0) _height += 250;
this.div.height(this.div.height() + _height);
// trigger the history registered resize
// in order to update the height with new value
this.div.trigger('resize.' +this.options.value.app + this.options.value.id);
}
}
if(this.dynheight)
{
this.dynheight.update();
}
// Resize diff widgets to match new space
if(this.dataview)
{
const columns = this.dataview.getColumnMgr();
jQuery('.et2_diff', this.div).closest('.innerContainer')
.width(columns.getColumnWidth(et2_historylog.NEW_VALUE) + columns.getColumnWidth(et2_historylog.OLD_VALUE));
}
}
}
et2_register_widget(et2_historylog, ['historylog']);

View File

@ -1,3 +1,4 @@
"use strict";
/**
* EGroupware eTemplate2 - JS HRule object
*
@ -6,31 +7,47 @@
* @subpackage api
* @link http://www.egroupware.org
* @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
et2_core_baseWidget;
et2_core_baseWidget;
*/
var et2_core_widget_1 = require("./et2_core_widget");
var et2_core_baseWidget_1 = require("./et2_core_baseWidget");
var et2_core_inheritance_1 = require("./et2_core_inheritance");
/**
* Class which implements the hrule tag
*
* @augments et2_baseWidget
*/
var et2_hrule = (function(){ "use strict"; return et2_baseWidget.extend(
{
/**
* Constructor
*
* @memberOf et2_hrule
*/
init: function() {
this._super.apply(this, arguments);
this.setDOMNode(document.createElement("hr"));
}
});}).call(this);
et2_register_widget(et2_hrule, ["hrule"]);
var et2_hrule = /** @class */ (function (_super) {
__extends(et2_hrule, _super);
/**
* Constructor
*
* @memberOf et2_hrule
*/
function et2_hrule(_parent, _attrs, _child) {
var _this =
// Call the inherited constructor
_super.call(this, _parent, _attrs, et2_core_inheritance_1.ClassWithAttributes.extendAttributes(et2_hrule._attributes, _child || {})) || this;
_this.setDOMNode(document.createElement("hr"));
return _this;
}
return et2_hrule;
}(et2_core_baseWidget_1.et2_baseWidget));
et2_core_widget_1.et2_register_widget(et2_hrule, ["hrule"]);
//# sourceMappingURL=et2_widget_hrule.js.map

Some files were not shown because too many files have changed in this diff Show More