From 5c6f73a26ee17cf4504f6f13510926bf920eba11 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Tue, 21 Jan 2020 15:54:26 +0100 Subject: [PATCH] textbox and button widget --- api/js/etemplate/et2_core_inputWidget.ts | 2 +- api/js/etemplate/et2_core_interfaces.ts | 2 +- api/js/etemplate/et2_types.d.ts | 5 +- api/js/etemplate/et2_widget_button.js | 809 +++++++++++------------ api/js/etemplate/et2_widget_button.ts | 459 +++++++++++++ api/js/etemplate/et2_widget_textbox.js | 46 +- api/js/etemplate/et2_widget_textbox.ts | 48 +- 7 files changed, 898 insertions(+), 473 deletions(-) create mode 100644 api/js/etemplate/et2_widget_button.ts diff --git a/api/js/etemplate/et2_core_inputWidget.ts b/api/js/etemplate/et2_core_inputWidget.ts index 819cdbc9e0..f3ea3d778a 100644 --- a/api/js/etemplate/et2_core_inputWidget.ts +++ b/api/js/etemplate/et2_core_inputWidget.ts @@ -67,7 +67,7 @@ export class et2_inputWidget extends et2_valueWidget implements et2_IInput, et2_ } } - private _oldValue: any; + protected _oldValue: any; onchange: Function; /** diff --git a/api/js/etemplate/et2_core_interfaces.ts b/api/js/etemplate/et2_core_interfaces.ts index bb8a31182f..3f8ebf5928 100644 --- a/api/js/etemplate/et2_core_interfaces.ts +++ b/api/js/etemplate/et2_core_interfaces.ts @@ -106,7 +106,7 @@ interface et2_IResizeable /** * Called whenever the window is resized */ - resize() : void + resize(number) : void } var et2_IResizeable = "et2_IResizeable"; function implements_et2_IResizeable(obj : et2_widget) diff --git a/api/js/etemplate/et2_types.d.ts b/api/js/etemplate/et2_types.d.ts index d74a881a53..4953e5712b 100644 --- a/api/js/etemplate/et2_types.d.ts +++ b/api/js/etemplate/et2_types.d.ts @@ -14,6 +14,10 @@ declare class et2_tabbox extends et2_valueWidget { tabData : any; activateTab(et2_widget); } +declare class et2_button extends et2_DOMWidget { + click() : boolean + onclick: Function +} declare var et2_surroundingsMgr : any; declare var et2_arrayMgr : any; declare var et2_readonlysArrayMgr : any; @@ -68,7 +72,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; diff --git a/api/js/etemplate/et2_widget_button.js b/api/js/etemplate/et2_widget_button.js index 73d6590523..6961dd20ef 100644 --- a/api/js/etemplate/et2_widget_button.js +++ b/api/js/etemplate/et2_widget_button.js @@ -1,3 +1,4 @@ +"use strict"; /** * EGroupware eTemplate2 - JS Button object * @@ -6,440 +7,386 @@ * @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_DOMWidget_1 = require("./et2_core_DOMWidget"); +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 + 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_core_DOMWidget_1.et2_DOMWidget._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" + }, + "accesskey": { + "name": "Access Key", + "type": "string", + "default": et2_no_init, + "description": "Alt + 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"]); diff --git a/api/js/etemplate/et2_widget_button.ts b/api/js/etemplate/et2_widget_button.ts new file mode 100644 index 0000000000..31bf8952e4 --- /dev/null +++ b/api/js/etemplate/et2_widget_button.ts @@ -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_widget, et2_createWidget, et2_register_widget, WidgetConfig } from "./et2_core_widget"; +import { et2_DOMWidget } from './et2_core_DOMWidget' +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" + }, + "accesskey": { + "name": "Access Key", + "type": "string", + "default": et2_no_init, + "description": "Alt + 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_DOMWidget._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"]); diff --git a/api/js/etemplate/et2_widget_textbox.js b/api/js/etemplate/et2_widget_textbox.js index 67d59932b3..f24bbda4b1 100644 --- a/api/js/etemplate/et2_widget_textbox.js +++ b/api/js/etemplate/et2_widget_textbox.js @@ -39,15 +39,15 @@ require("./et2_types"); * * @augments et2_inputWidget */ -var et2_textbox = /** @class */ (function (_super_1) { - __extends(et2_textbox, _super_1); +var et2_textbox = /** @class */ (function (_super) { + __extends(et2_textbox, _super); /** * Constructor */ function et2_textbox(_parent, _attrs, _child) { var _this = // Call the inherited constructor - _super_1.call(this, _parent, _attrs, et2_core_inheritance_1.ClassWithAttributes.extendAttributes(et2_core_DOMWidget_1.et2_DOMWidget._attributes, _child || {})) || this; + _super.call(this, _parent, _attrs, et2_core_inheritance_1.ClassWithAttributes.extendAttributes(et2_core_DOMWidget_1.et2_DOMWidget._attributes, _child || {})) || this; _this.legacyOptions = ["size", "maxlength", "validator"]; _this.input = null; _this.input = null; @@ -112,7 +112,7 @@ var et2_textbox = /** @class */ (function (_super_1) { * @returns {undefined} */ et2_textbox.prototype.set_id = function (_value) { - _super_1.prototype.set_id.call(this, _value); + _super.prototype.set_id.call(this, _value); // Remove the name attribute inorder to affect autocomplete="off" // for no password save. ATM seems all browsers ignore autocomplete for // input field inside the form @@ -124,12 +124,12 @@ var et2_textbox = /** @class */ (function (_super_1) { var node = this.getInputNode(); if (node) jQuery(node).unbind("keypress"); - _super_1.prototype.destroy.call(this); + _super.prototype.destroy.call(this); }; et2_textbox.prototype.getValue = function () { if (this.options && this.options.blur && this.input.val() == this.options.blur) return ""; - return _super_1.prototype.getValue.call(this); + return _super.prototype.getValue.call(this); }; /** * Clientside validation using regular expression in "validator" attribute @@ -155,7 +155,7 @@ var et2_textbox = /** @class */ (function (_super_1) { _messages.push(this.egw().lang("'%1' has an invalid format !!!", value)); } } - return _super_1.prototype.isValid.call(this, _messages) && ok; + return _super.prototype.isValid.call(this, _messages) && ok; }; /** * Set input widget size @@ -165,7 +165,7 @@ var et2_textbox = /** @class */ (function (_super_1) { if (this.options.multiline || this.options.rows > 1 || this.options.cols > 1) { this.input.css('width', _size + "em"); } - else if (typeof _size != 'undefined' && _size != this.input.attr("size")) { + else if (typeof _size != 'undefined' && _size != parseInt(this.input.attr("size"))) { this.size = _size; this.input.attr("size", this.size); } @@ -175,7 +175,7 @@ var et2_textbox = /** @class */ (function (_super_1) { * @param _size Max characters allowed */ et2_textbox.prototype.set_maxlength = function (_size) { - if (typeof _size != 'undefined' && _size != this.input.attr("maxlength")) { + if (typeof _size != 'undefined' && _size != parseInt(this.input.attr("maxlength"))) { this.maxLength = _size; this.input.attr("maxLength", this.maxLength); } @@ -286,21 +286,22 @@ var et2_textbox = /** @class */ (function (_super_1) { }; return et2_textbox; }(et2_core_inputWidget_1.et2_inputWidget)); +exports.et2_textbox = et2_textbox; et2_core_widget_1.et2_register_widget(et2_textbox, ["textbox", "passwd", "hidden"]); /** * et2_textbox_ro is the dummy readonly implementation of the textbox. * * @augments et2_valueWidget */ -var et2_textbox_ro = /** @class */ (function (_super_1) { - __extends(et2_textbox_ro, _super_1); +var et2_textbox_ro = /** @class */ (function (_super) { + __extends(et2_textbox_ro, _super); /** * Constructor */ function et2_textbox_ro(_parent, _attrs, _child) { var _this = // Call the inherited constructor - _super_1.call(this, _parent, _attrs, et2_core_inheritance_1.ClassWithAttributes.extendAttributes(et2_core_DOMWidget_1.et2_DOMWidget._attributes, _child || {})) || this; + _super.call(this, _parent, _attrs, et2_core_inheritance_1.ClassWithAttributes.extendAttributes(et2_core_DOMWidget_1.et2_DOMWidget._attributes, _child || {})) || this; _this.value = ""; _this.span = jQuery(document.createElement("label")) .addClass("et2_label"); @@ -388,15 +389,16 @@ et2_core_widget_1.et2_register_widget(et2_textbox_ro, ["textbox_ro"]); * et2_searchbox is a widget which provides a collapsable input search * with on searching indicator and clear handler regardless of any browser limitation. */ -var et2_searchbox = /** @class */ (function (_super_1) { - __extends(et2_searchbox, _super_1); +var et2_searchbox = /** @class */ (function (_super) { + __extends(et2_searchbox, _super); /** * Constructor */ function et2_searchbox(_parent, _attrs, _child) { var _this = // Call the inherited constructor - _super_1.call(this, _parent, _attrs, et2_core_inheritance_1.ClassWithAttributes.extendAttributes(et2_core_DOMWidget_1.et2_DOMWidget._attributes, _child || {})) || this; + _super.call(this, _parent, _attrs, et2_core_inheritance_1.ClassWithAttributes.extendAttributes(et2_core_DOMWidget_1.et2_DOMWidget._attributes, _child || {})) || this; + _this.value = ""; _this.value = ""; _this.div = jQuery(document.createElement('div')) .addClass('et2_searchbox'); @@ -459,8 +461,8 @@ var et2_searchbox = /** @class */ (function (_super_1) { } }, mousedown: function (event) { - if (event.target.type == 'span') - event.stopImmidatePropagation(); + if (event.target.tagName == 'span') + event.stopImmediatePropagation(); } }); this.flex.append(this.search.getDOMNode()); @@ -518,13 +520,13 @@ var et2_searchbox = /** @class */ (function (_super_1) { */ et2_searchbox.prototype.change = function () { this._searchToggleState(); - this._super.apply(this, arguments); + _super.prototype.change.apply(this, arguments); }; et2_searchbox.prototype.get_value = function () { return this.search.input.val(); }; et2_searchbox.prototype.set_value = function (_value) { - _super_1.prototype.set_value.call(this, _value); + _super.prototype.set_value.call(this, _value); if (this.search) this.search.input.val(_value); }; @@ -532,7 +534,7 @@ var et2_searchbox = /** @class */ (function (_super_1) { * override doLoadingFinished in order to set initial state */ et2_searchbox.prototype.doLoadingFinished = function () { - _super_1.prototype.doLoadingFinished.call(this); + var ret = _super.prototype.doLoadingFinished.call(this); if (!this.get_value()) { this._show_hide(false); } @@ -540,16 +542,18 @@ var et2_searchbox = /** @class */ (function (_super_1) { this._show_hide(!this.options.overlay); this._searchToggleState(); } + return ret; }; /** * Overrride attachToDOM in order to unbind change handler */ et2_searchbox.prototype.attachToDOM = function () { - _super_1.prototype.attachToDOM.call(this); + var ret = _super.prototype.attachToDOM.call(this); var node = this.getInputNode(); if (node) { jQuery(node).off('.et2_inputWidget'); } + return ret; }; /** * Advanced attributes diff --git a/api/js/etemplate/et2_widget_textbox.ts b/api/js/etemplate/et2_widget_textbox.ts index f8e96b8a8d..e11ecc7bdf 100644 --- a/api/js/etemplate/et2_widget_textbox.ts +++ b/api/js/etemplate/et2_widget_textbox.ts @@ -20,6 +20,7 @@ import { et2_widget, et2_createWidget, et2_register_widget, WidgetConfig } from import { et2_DOMWidget } from './et2_core_DOMWidget' import { et2_valueWidget } from './et2_core_valueWidget' import { et2_inputWidget } from './et2_core_inputWidget' +import { et2_button } from './et2_widget_button' import './et2_types'; /** @@ -27,7 +28,7 @@ import './et2_types'; * * @augments et2_inputWidget */ -class et2_textbox extends et2_inputWidget implements et2_IResizeable +export class et2_textbox extends et2_inputWidget implements et2_IResizeable { static readonly _attributes : any = { "multiline": { @@ -89,8 +90,8 @@ class et2_textbox extends et2_inputWidget implements et2_IResizeable legacyOptions: string[] = ["size", "maxlength", "validator"]; input: JQuery = null; - size: number|string; - maxLength: number|string; + size: number; + maxLength: number; /** * Constructor @@ -100,7 +101,6 @@ class et2_textbox extends et2_inputWidget implements et2_IResizeable // Call the inherited constructor super(_parent, _attrs, ClassWithAttributes.extendAttributes(et2_DOMWidget._attributes, _child || {})); - this.input = null; this.createInputWidget(); @@ -236,13 +236,13 @@ class et2_textbox extends et2_inputWidget implements et2_IResizeable * Set input widget size * @param _size Rather arbitrary size units, approximately characters */ - set_size(_size : number|string) + set_size(_size : number) { if (this.options.multiline || this.options.rows > 1 || this.options.cols > 1) { this.input.css('width', _size + "em"); } - else if (typeof _size != 'undefined' && _size != this.input.attr("size")) + else if (typeof _size != 'undefined' && _size != parseInt(this.input.attr("size"))) { this.size = _size; this.input.attr("size", this.size); @@ -253,9 +253,9 @@ class et2_textbox extends et2_inputWidget implements et2_IResizeable * Set maximum characters allowed * @param _size Max characters allowed */ - set_maxlength(_size : number|string) + set_maxlength(_size : number) { - if (typeof _size != 'undefined' && _size != this.input.attr("maxlength")) + if (typeof _size != 'undefined' && _size != parseInt(this.input.attr("maxlength"))) { this.maxLength = _size; this.input.attr("maxLength", this.maxLength); @@ -277,7 +277,7 @@ class et2_textbox extends et2_inputWidget implements et2_IResizeable { if(_value) { this.input.attr("placeholder", this.egw().lang(_value) + ""); // HTML5 - if(!this.input[0].placeholder) { + if (!(this.input[0]).placeholder) { // Not HTML5 if(this.input.val() == "") this.input.val(this.egw().lang(this.options.blur)); this.input.focus(this,function(e) { @@ -299,7 +299,7 @@ class et2_textbox extends et2_inputWidget implements et2_IResizeable this.input.attr('autocomplete', _value); } - resize(_height) + resize(_height : number) { if (_height && this.options.multiline) { @@ -349,6 +349,9 @@ class et2_textbox_ro extends et2_valueWidget implements et2_IDetachedDOM "ignore": true } }; + value: string = ""; + span: JQuery; + value_span: JQuery; /** * Constructor @@ -358,7 +361,6 @@ class et2_textbox_ro extends et2_valueWidget implements et2_IDetachedDOM // Call the inherited constructor super(_parent, _attrs, ClassWithAttributes.extendAttributes(et2_DOMWidget._attributes, _child || {})); - this.value = ""; this.span = jQuery(document.createElement("label")) .addClass("et2_label"); this.value_span = jQuery(document.createElement("span")) @@ -456,6 +458,13 @@ class et2_searchbox extends et2_textbox description:"Define wheter the searchbox should be a fix input field or flexible search button. Default is true (fix)." } } + value: string = ""; + div: JQuery; + flex: JQuery; + button: et2_button; + search: et2_textbox; + oldValue: any; + clear: JQuery; /** * Constructor @@ -486,7 +495,7 @@ class et2_searchbox extends et2_textbox // no need to create search button if it's a fix search field if (!this.options.fix) { - this.button = et2_createWidget('button',{image:"search","background_image":"1"},this); + this.button = et2_createWidget('button',{image:"search","background_image":"1"},this); this.button.onclick= function(){ self._show_hide(jQuery(self.flex).hasClass('hide')); self.search.input.focus(); @@ -494,7 +503,7 @@ class et2_searchbox extends et2_textbox this.div.prepend(this.button.getDOMNode()); } // input field - this.search = et2_createWidget('textbox',{"blur":egw.lang("search"), + this.search = et2_createWidget('textbox',{"blur":egw.lang("search"), onkeypress:function(event) { if(event.which == 13) { @@ -533,7 +542,7 @@ class et2_searchbox extends et2_textbox } }, mousedown:function(event){ - if (event.target.type == 'span') event.stopImmidatePropagation(); + if (event.target.tagName == 'span') event.stopImmediatePropagation(); } }); this.flex.append(this.search.getDOMNode()); @@ -600,7 +609,7 @@ class et2_searchbox extends et2_textbox { this._searchToggleState(); - this._super.apply(this,arguments); + super.change.apply(this,arguments); } @@ -620,14 +629,16 @@ class et2_searchbox extends et2_textbox */ doLoadingFinished() { - super.doLoadingFinished(); + let ret = super.doLoadingFinished(); + if (!this.get_value()) { this._show_hide(false); } - else{ + else { this._show_hide(!this.options.overlay); this._searchToggleState(); } + return ret; } /** @@ -635,13 +646,14 @@ class et2_searchbox extends et2_textbox */ attachToDOM() { - super.attachToDOM(); + let ret = super.attachToDOM(); var node = this.getInputNode(); if (node) { jQuery(node).off('.et2_inputWidget'); } + return ret; } } et2_register_widget(et2_searchbox, ["searchbox"]);