From 7076f8fa3a0c92b3dd752b75d307281a0582b158 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Tue, 21 Jan 2020 17:51:44 +0100 Subject: [PATCH] tabbox widget --- api/js/etemplate/et2_core_widget.js | 3 +- api/js/etemplate/et2_core_widget.ts | 3 +- api/js/etemplate/et2_types.d.ts | 4 + api/js/etemplate/et2_widget_description.js | 743 +++++++------- api/js/etemplate/et2_widget_tabs.js | 1016 +++++++++----------- api/js/etemplate/et2_widget_tabs.ts | 584 +++++++++++ 6 files changed, 1404 insertions(+), 949 deletions(-) create mode 100644 api/js/etemplate/et2_widget_tabs.ts diff --git a/api/js/etemplate/et2_core_widget.js b/api/js/etemplate/et2_core_widget.js index 36cdcc1ca9..efabd15cd6 100644 --- a/api/js/etemplate/et2_core_widget.js +++ b/api/js/etemplate/et2_core_widget.js @@ -508,10 +508,11 @@ var et2_widget = /** @class */ (function (_super) { * is called with the node. * * @param _node XML node to read + * @param _name XML node name * * @return et2_widget */ - et2_widget.prototype.createElementFromNode = function (_node) { + et2_widget.prototype.createElementFromNode = function (_node, _name) { var attributes = {}; // Parse the "readonly" and "type" flag for this element here, as they // determine which constructor is used diff --git a/api/js/etemplate/et2_core_widget.ts b/api/js/etemplate/et2_core_widget.ts index 177b7ff1d6..bc70f67e24 100644 --- a/api/js/etemplate/et2_core_widget.ts +++ b/api/js/etemplate/et2_core_widget.ts @@ -646,10 +646,11 @@ export class et2_widget extends ClassWithAttributes * is called with the node. * * @param _node XML node to read + * @param _name XML node name * * @return et2_widget */ - createElementFromNode(_node) + createElementFromNode(_node, _name?) { var attributes = {}; diff --git a/api/js/etemplate/et2_types.d.ts b/api/js/etemplate/et2_types.d.ts index 94e230a3d0..349ae11812 100644 --- a/api/js/etemplate/et2_types.d.ts +++ b/api/js/etemplate/et2_types.d.ts @@ -155,4 +155,8 @@ declare var et2_IExposable : any; declare function et2_createWidget(type : string, params : {}, parent? : any) : any; declare function nm_action(_action : {}, _senders : [], _target : any, _ids? : any) : void; declare function 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?); \ No newline at end of file diff --git a/api/js/etemplate/et2_widget_description.js b/api/js/etemplate/et2_widget_description.js index 83cfa4f299..0a56a30180 100644 --- a/api/js/etemplate/et2_widget_description.js +++ b/api/js/etemplate/et2_widget_description.js @@ -1,3 +1,4 @@ +"use strict"; /** * EGroupware eTemplate2 - JS Description object * @@ -6,410 +7,350 @@ * @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_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_DOMWidget_1 = require("./et2_core_DOMWidget"); +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('') - .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); + /** + * 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_core_DOMWidget_1.et2_DOMWidget._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(/^\//, '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')) { + // ToDoExpose: 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('') + .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 })); + }; + et2_description._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" + } + }; + return et2_description; +}(et2_core_baseWidget_1.et2_baseWidget)); +et2_core_widget_1.et2_register_widget(et2_description, ["description", "label"]); diff --git a/api/js/etemplate/et2_widget_tabs.js b/api/js/etemplate/et2_widget_tabs.js index eccfcd6a0a..84933536eb 100644 --- a/api/js/etemplate/et2_widget_tabs.js +++ b/api/js/etemplate/et2_widget_tabs.js @@ -1,3 +1,4 @@ +"use strict"; /** * EGroupware eTemplate2 - JS Tabs object * @@ -6,555 +7,478 @@ * @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 - jsapi.egw; - /vendor/bower-asset/jquery/dist/jquery.js; - et2_core_valueWidget; + jsapi.egw; + /vendor/bower-asset/jquery/dist/jquery.js; + et2_core_valueWidget; */ - +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_valueWidget_1 = require("./et2_core_valueWidget"); +require("./et2_types"); /** * Class which implements the tabbox-tag - * - * @augments et2_valueWidget */ -var et2_tabbox = (function(){ "use strict"; return et2_valueWidget.extend([et2_IInput,et2_IResizeable,et2_IPrint], -{ - attributes: { - 'tabs': { - 'name': 'Tabs', - 'default': et2_no_init, - 'description': "Array of [extra] tabs. Each tab needs {label:..., template:...}. Additional optional keys are prepend, hidden and id, for access into content array" - }, - 'add_tabs': { - 'name': 'Add tabs', - 'default': false, - 'description': 'Set to true if tabs should be added to tabs from read from template, default false if not' - }, - 'tab_height': { - name: 'Tabs innerHeight', - default: '', - description: 'Set the innerHeight for the tab content' - }, - 'align_tabs': { - name: 'Tabs alignment', - type: 'string', - default: 'h', - description: 'Set tabs and their headers arrangment either horizental (h) or vertical (v). Default value is horizental.' - } - }, - - /** - * Currently selected tab - */ - selected_index: 0, - - /** - * Construtor - * - * @memberOf et2_tabbox - */ - init: function() { - // Create the outer tabbox container - this.container = jQuery(document.createElement("div")) - .addClass("et2_tabbox"); - - // Create the upper container for the tab flags - this.flagContainer = jQuery(document.createElement("div")) - .addClass("et2_tabheader") - .appendTo(this.container); - - // Create the lower tab container - this.tabContainer = jQuery(document.createElement("div")) - .addClass("et2_tabs") - .appendTo(this.container); - - this._super.apply(this, arguments); - - this.tabData = []; - }, - - destroy: function() { - this._super.apply(this, arguments); - - this.container = null; - this.flagContainer = null; - this.tabData = []; - }, - - _readTabs: function(tabData, tabs) { - var selected = ""; - this.selected_index = false; - var hidden = {}; - if (this.id) - { - // Set the value for this element - var contentMgr = this.getArrayMgr("content"); - if (contentMgr != null) { - var val = contentMgr.getEntry(this.id); - if (val !== null) - { - selected = val; - } - } - contentMgr = this.getArrayMgr("readonlys"); - if (contentMgr != null) { - var val = contentMgr.getEntry(this.id); - if (val !== null && typeof val !== 'undefined') - { - hidden = val; - } - } - } - var i = 0; - et2_filteredNodeIterator(tabs, function(node, nodeName) { - if (nodeName == "tab") - { - var index_name = et2_readAttrWithDefault(node, "id"); - var hide = false; - var widget_options = {}; - if(index_name) { - if(selected == index_name) this.selected_index = i; - if(hidden[index_name]) { - hide = true; - } - // Get the class attribute and add it as widget_options - var classAttr = et2_readAttrWithDefault(node,"class"); - if (classAttr) - { - widget_options = {'class':classAttr}; - } - } - tabData.push({ - "id": index_name, - "label": this.egw().lang(et2_readAttrWithDefault(node, "label", "Tab")), - "widget": null, - "widget_options": widget_options, - "contentDiv": null, - "flagDiv": null, - "hidden": hide, - "XMLNode": null, - "promise": null - }); - } - else - { - throw("Error while parsing: Invalid tag '" + nodeName + - "' in tabs tag"); - } - i++; - }, this); - - // Make sure we don't try to display a hidden tab - for(var i = 0; i < tabData.length && this.selected_index === false; i++) - { - if(!tabData[i].hidden) this.selected_index = i; - } - }, - - _readTabPanels: function(tabData, tabpanels) { - var i = 0; - et2_filteredNodeIterator(tabpanels, function(node, nodeName) { - if (i < tabData.length) - { - // Store node for later evaluation - tabData[i].XMLNode = node; - } - else - { - throw("Error while reading tabpanels tag, too many widgets!"); - } - i++; - }, this); - }, - - loadFromXML: function(_node) { - // Get the tabs and tabpanels tags - var tabsElems = et2_directChildrenByTagName(_node, "tabs"); - var tabpanelsElems = et2_directChildrenByTagName(_node, "tabpanels"); - var tabData = []; - - // Check for a parent height, we'll apply it to tab panels - var height = et2_readAttrWithDefault(_node.parentNode, "height",null); - if(height) - { - this.tabContainer.css("height", height); - } - - // if no tabs set or they should be added to tabs from xml - if (!this.options.tabs || this.options.add_tabs) - { - if (tabsElems.length == 1 && tabpanelsElems.length == 1) - { - var tabs = tabsElems[0]; - var tabpanels = tabpanelsElems[0]; - - // Parse the "tabs" tag - this._readTabs(tabData, tabs); - - // Read and create the widgets defined in the "tabpanels" - this._readTabPanels(tabData, tabpanels); - } - else - { - this.egw().debug("error","Error while parsing tabbox, none or multiple tabs or tabpanels tags!",this); - } - } - if (this.options.tabs) - { - var readonly = this.getArrayMgr("readonlys").getEntry(this.id) || {}; - for(var i = 0; i < this.options.tabs.length; i++) - { - var tab = this.options.tabs[i]; - var tab_id = tab.id || tab.template; - var tab_options = {id: tab_id, template:tab.template, url: tab.url}; - if(tab.id) - { - tab_options.content = tab.id; - } - tabData[tab.prepend ? 'unshift' : 'push'].call(tabData, { - "id": tab_id, - "label": this.egw().lang(tab.label), - "widget": null, - "widget_options": tab_options, - "contentDiv": null, - "flagDiv": null, - "hidden": typeof tab.hidden != "undefined" ? tab.hidden : readonly[tab_id] || false, - "XMLNode": null, - "promise": null - }); - } - } - - // Create the tab DOM-Nodes - this.createTabs(tabData); - }, - - /** - * Load is finished, set up tabs to load on their own - */ - doLoadingFinished: function() - { - var tab_deferred = jQuery.Deferred(); - var promises = []; - var tabs = this; - - // Specially process the selected index so it shows up right away - this._loadTab(this.selected_index,promises); - - // Apply parent now, which actually puts into the DOM - // This has to be before loading the child, so the dom sub-tree is not - // disconnected, which causes problems for things like CKEditor - this._super.apply(this, arguments); - - // We can do this and not wind up with 2 because child is a template, - // which has special handling - this._children[0].loadingFinished(promises); - - // Defer parsing & loading of other tabs until later - window.setTimeout(function() { - for (var i = 0; i < tabs.tabData.length; i++) - { - if (i == tabs.selected_index) continue; - tabs._loadTab(i,promises); - } - jQuery.when.apply(jQuery,promises).then(function() { - tab_deferred.resolve(); - }); - },0); - - return tab_deferred.promise(); - }, - - /** - * Load & render a tab's content - * - * @param {number} index numerical index of tab in this.tabData array - * @param {array} promises - */ - _loadTab: function(index,promises) { - var tabData = this.tabData[index]; - if(!tabData || tabData.loaded) return; - if(tabData.XMLNode != null) - { - if(tabData.hidden) - { - // Set hidden tab to readonly, so widgets aren't active - // Do not modify the XMLNode, or the change will be cached for all - tabData.XMLNode = tabData.XMLNode.cloneNode(); - tabData.XMLNode.setAttribute('readonly', true); - } - tabData.widget = this.createElementFromNode(tabData.XMLNode,tabData.XMLNode.nodeName.toLowerCase()); - - // Release the XML node - tabData.XMLNode = null; - } - else if (tabData.widget_options) - { - tabData.widget = et2_createWidget('template',tabData.widget_options,this); - } - - // Set loaded flag to not do this again, even if not fully done - tabData.loaded = true; - - // loadingFinished() will be called either when the promise from doLoadingFinished is resolved, - // or during the normal execution - }, - - /** - * Check for custom tabs - * - * @param {object} _attrs - */ - transformAttributes: function(_attrs) { - this._super.apply(this, arguments); - - // Add in settings that are objects - var data = this.getArrayMgr("modifications").getEntry(this.id); - for(var key in data) - { - if(typeof data[key] === 'object' && ! _attrs[key]) _attrs[key] = data[key]; - } - }, - - createTabs: function(tabData) { - this.tabData = tabData; - - this.tabContainer.empty(); - this.flagContainer.empty(); - - for (var i = 0; i < this.tabData.length; i++) - { - var entry = this.tabData[i]; - entry.flagDiv = jQuery(document.createElement("span")) - .addClass("et2_tabflag") - .appendTo(this.flagContainer); - // Class to tab's div container - if (entry.widget_options && typeof entry.widget_options.class != 'undefined') - { - entry.flagDiv.addClass(entry.widget_options.class); - } - entry.flagDiv.text(entry.label || "Tab"); - if(entry.hidden || this.tabData.length === 1) - { - entry.flagDiv.hide(); - } - else - { - entry.flagDiv.click({"tabs": this, "idx": i}, function(e) { - e.data.tabs.setActiveTab(e.data.idx); - }); - } - entry.contentDiv = jQuery(document.createElement("div")) - .addClass("et2_tabcntr") - .appendTo(this.tabContainer); - if (this.options.align_tabs == 'v') { - entry.flagDiv.unbind('click'); - entry.flagDiv.text(""); - jQuery(document.createElement('div')) - .addClass('et2_tabtitle') - .text(entry.label || "Tab") - .click({"tabs": this, "idx": i}, function(e) { - e.data.tabs.flagContainer.children(":eq(" + e.data.idx + ")").toggleClass('active'); - if (e.data.tabs.selected_index != e.data.idx) e.data.tabs.setActiveTab(e.data.idx); - }) - .appendTo(entry.flagDiv); - entry.contentDiv.appendTo(entry.flagDiv); - } - } - - if (this.options.align_tabs == 'v'){ - - this.container.addClass('vertical'); - this.tabContainer.hide(); - } - // Check for a passed in value - if(this.options.value) - { - this.selected_index = 0; - for(var i = 0; i < this.tabData.length; i++) - { - if(this.tabData[i].id == this.options.value) - { - this.selected_index = i; - break; - } - } - } - - this.setActiveTab(this.selected_index); - }, - - /** - * Gets the index of the currently active tab - * - * @returns {number} - */ - get_active_tab: function() { - return this.selected_index; - }, - - /** - * Sets the currently active tab by index - * - * @param {number} _idx - */ - setActiveTab: function(_idx) { - this.selected_index = _idx; - - // Remove the "active" flag from all tabs-flags - jQuery(".et2_tabflag", this.flagContainer).removeClass("active"); - - // Hide all tab containers - this.tabContainer.children().hide(); - - // Set the tab flag with the given index active and show the corresponding - // container - this.flagContainer.children(":eq(" + _idx + ")").addClass("active"); - this.tabContainer.children(":eq(" + _idx + ")").show(); - - // lookup for nm children and trigger a resize, since nm inside inactive - // tabs are not getting render due to tab's deffer loading. - if (this._children.length > 0 && this.tabData && this.tabData.length > 0) { - this.tabData[_idx]['widget'].iterateOver(function(nm){ - if (nm && nm._type == 'nextmatch') nm.resize(); - }, this.tabData[_idx]['widget'], et2_nextmatch); - } - }, - - /** - * Activate the tab containing the given widget - * - * @param {et2_widget} widget - * @return {bool} widget was found in a tab - */ - activateTab: function(widget) { - var tab = widget; - while(tab._parent && tab._parent._type !='tabbox') - { - tab = tab._parent; - } - - var child_index = this._children.indexOf(tab); - for(var i = 0; i < this.tabData.length; i++) - { - if(this.tabData[i].widget == tab) - { - this.setActiveTab(i); - return true; - } - } - return false; - }, - - getDOMNode: function(_sender) { - if (_sender === this || typeof _sender === 'undefined') - { - return this.container[0]; - } - else - { - for (var i = 0; i < this.tabData.length; i++) - { - if (this.tabData[i].widget == _sender) - { - return this.tabData[i].contentDiv[0]; - } - } - - return null; - } - }, - - set_tab_height: function (_height) - { - this.tab_height = _height; - this.tabContainer.css("height", _height); - }, - - set_height: function(_value) { - this.height = _value; - - this.tabContainer.css("height", _value); - }, - - /** - * getValue has to return the value of the input widget - */ - getValue: function() { - return this.tabData[this.selected_index].id; - }, - - /** - * Is dirty returns true if the value of the widget has changed since it - * was loaded. - */ - isDirty: function() { - return this.selected_index != this.value; - }, - - /** - * Causes the dirty flag to be reseted. - */ - resetDirty: function() - { - this.value = this.selected_index; - }, - isValid: function(messages) { - return true; - }, - - resize: function (_height) - { - if(_height) - { - this.set_height(this.tabContainer.height() + _height); - } - //Set the height of tabs with the heighest height - else if(_height === 0) - { - this.set_height(this.tabContainer.height()); - } - }, - - /** - * Set up for printing - * - * @return {undefined|Deferred} Return a jQuery Deferred object if not done setting up - * (waiting for data) - */ - beforePrint: function() - { - // Remove the "active" flag from all tabs-flags - jQuery(".et2_tabflag", this.flagContainer).removeClass("active"); - - // Remove height limit - this.tabContainer.css("height", ''); - - // Show all enabled tabs - for (var i = 0; i < this.tabData.length; i++) - { - var entry = this.tabData[i]; - if(entry.hidden) continue; - entry.flagDiv.insertBefore(entry.contentDiv); - entry.contentDiv.show(); - } - }, - - /** - * Reset after printing - */ - afterPrint: function() - { - for (var i = 0; i < this.tabData.length; i++) - { - var entry = this.tabData[i]; - entry.flagDiv.appendTo(this.flagContainer); - } - this.setActiveTab(this.get_active_tab()); - } -});}).call(this); -et2_register_widget(et2_tabbox, ["tabbox"]); +var et2_tabbox = /** @class */ (function (_super) { + __extends(et2_tabbox, _super); + /** + * Constructor + */ + function et2_tabbox(_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; + /** + * Currently selected tab + */ + _this.selected_index = 0; + _this.tabData = []; + // Create the outer tabbox container + _this.container = jQuery(document.createElement("div")) + .addClass("et2_tabbox"); + // Create the upper container for the tab flags + _this.flagContainer = jQuery(document.createElement("div")) + .addClass("et2_tabheader") + .appendTo(_this.container); + // Create the lower tab container + _this.tabContainer = jQuery(document.createElement("div")) + .addClass("et2_tabs") + .appendTo(_this.container); + return _this; + } + et2_tabbox.prototype.destroy = function () { + _super.prototype.destroy.call(this); + this.container = null; + this.flagContainer = null; + this.tabData = []; + }; + et2_tabbox.prototype._readTabs = function (tabData, tabs) { + var selected = ""; + this.selected_index = false; + var hidden = {}; + if (this.id) { + // Set the value for this element + var contentMgr = this.getArrayMgr("content"); + if (contentMgr != null) { + var val = contentMgr.getEntry(this.id); + if (val !== null) { + selected = val; + } + } + contentMgr = this.getArrayMgr("readonlys"); + if (contentMgr != null) { + var val = contentMgr.getEntry(this.id); + if (val !== null && typeof val !== 'undefined') { + hidden = val; + } + } + } + var i = 0; + et2_filteredNodeIterator(tabs, function (node, nodeName) { + if (nodeName == "tab") { + var index_name = et2_readAttrWithDefault(node, "id"); + var hide = false; + var widget_options = {}; + if (index_name) { + if (selected == index_name) + this.selected_index = i; + if (hidden[index_name]) { + hide = true; + } + // Get the class attribute and add it as widget_options + var classAttr = et2_readAttrWithDefault(node, "class"); + if (classAttr) { + widget_options = { 'class': classAttr }; + } + } + tabData.push({ + "id": index_name, + "label": this.egw().lang(et2_readAttrWithDefault(node, "label", "Tab")), + "widget": null, + "widget_options": widget_options, + "contentDiv": null, + "flagDiv": null, + "hidden": hide, + "XMLNode": null, + "promise": null + }); + } + else { + throw ("Error while parsing: Invalid tag '" + nodeName + + "' in tabs tag"); + } + i++; + }, this); + // Make sure we don't try to display a hidden tab + for (var i = 0; i < tabData.length && this.selected_index === false; i++) { + if (!tabData[i].hidden) + this.selected_index = i; + } + }; + et2_tabbox.prototype._readTabPanels = function (tabData, tabpanels) { + var i = 0; + et2_filteredNodeIterator(tabpanels, function (node, nodeName) { + if (i < tabData.length) { + // Store node for later evaluation + tabData[i].XMLNode = node; + } + else { + throw ("Error while reading tabpanels tag, too many widgets!"); + } + i++; + }, this); + }; + et2_tabbox.prototype.loadFromXML = function (_node) { + // Get the tabs and tabpanels tags + var tabsElems = et2_directChildrenByTagName(_node, "tabs"); + var tabpanelsElems = et2_directChildrenByTagName(_node, "tabpanels"); + var tabData = []; + // Check for a parent height, we'll apply it to tab panels + var height = et2_readAttrWithDefault(_node.parentNode, "height", null); + if (height) { + this.tabContainer.css("height", height); + } + // if no tabs set or they should be added to tabs from xml + if (!this.options.tabs || this.options.add_tabs) { + if (tabsElems.length == 1 && tabpanelsElems.length == 1) { + var tabs = tabsElems[0]; + var tabpanels = tabpanelsElems[0]; + // Parse the "tabs" tag + this._readTabs(tabData, tabs); + // Read and create the widgets defined in the "tabpanels" + this._readTabPanels(tabData, tabpanels); + } + else { + this.egw().debug("error", "Error while parsing tabbox, none or multiple tabs or tabpanels tags!", this); + } + } + if (this.options.tabs) { + var readonly = this.getArrayMgr("readonlys").getEntry(this.id) || {}; + for (var i = 0; i < this.options.tabs.length; i++) { + var tab = this.options.tabs[i]; + var tab_id = tab.id || tab.template; + var tab_options = { id: tab_id, template: tab.template, url: tab.url, content: undefined }; + if (tab.id) { + tab_options.content = tab.id; + } + tabData[tab.prepend ? 'unshift' : 'push'].call(tabData, { + "id": tab_id, + "label": this.egw().lang(tab.label), + "widget": null, + "widget_options": tab_options, + "contentDiv": null, + "flagDiv": null, + "hidden": typeof tab.hidden != "undefined" ? tab.hidden : readonly[tab_id] || false, + "XMLNode": null, + "promise": null + }); + } + } + // Create the tab DOM-Nodes + this.createTabs(tabData); + }; + /** + * Load is finished, set up tabs to load on their own + */ + et2_tabbox.prototype.doLoadingFinished = function () { + var tab_deferred = jQuery.Deferred(); + var promises = []; + var tabs = this; + // Specially process the selected index so it shows up right away + this._loadTab(this.selected_index, promises); + // Apply parent now, which actually puts into the DOM + // This has to be before loading the child, so the dom sub-tree is not + // disconnected, which causes problems for things like CKEditor + _super.prototype.doLoadingFinished.call(this); + // We can do this and not wind up with 2 because child is a template, + // which has special handling + this._children[0].loadingFinished(promises); + // Defer parsing & loading of other tabs until later + window.setTimeout(function () { + for (var i = 0; i < tabs.tabData.length; i++) { + if (i == tabs.selected_index) + continue; + tabs._loadTab(i, promises); + } + jQuery.when.apply(jQuery, promises).then(function () { + tab_deferred.resolve(); + }); + }, 0); + return tab_deferred.promise(); + }; + /** + * Load & render a tab's content + * + * @param {number} index numerical index of tab in this.tabData array + * @param {array} promises + */ + et2_tabbox.prototype._loadTab = function (index, promises) { + var tabData = this.tabData[index]; + if (!tabData || tabData.loaded) + return; + if (tabData.XMLNode != null) { + if (tabData.hidden) { + // Set hidden tab to readonly, so widgets aren't active + // Do not modify the XMLNode, or the change will be cached for all + tabData.XMLNode = tabData.XMLNode.cloneNode(); + tabData.XMLNode.setAttribute('readonly', true); + } + tabData.widget = this.createElementFromNode(tabData.XMLNode, tabData.XMLNode.nodeName.toLowerCase()); + // Release the XML node + tabData.XMLNode = null; + } + else if (tabData.widget_options) { + tabData.widget = et2_core_widget_1.et2_createWidget('template', tabData.widget_options, this); + } + // Set loaded flag to not do this again, even if not fully done + tabData.loaded = true; + // loadingFinished() will be called either when the promise from doLoadingFinished is resolved, + // or during the normal execution + }; + /** + * Check for custom tabs + * + * @param {object} _attrs + */ + et2_tabbox.prototype.transformAttributes = function (_attrs) { + _super.prototype.transformAttributes.call(this, _attrs); + // Add in settings that are objects + var data = this.getArrayMgr("modifications").getEntry(this.id); + for (var key in data) { + if (typeof data[key] === 'object' && !_attrs[key]) + _attrs[key] = data[key]; + } + }; + et2_tabbox.prototype.createTabs = function (tabData) { + this.tabData = tabData; + this.tabContainer.empty(); + this.flagContainer.empty(); + for (var i = 0; i < this.tabData.length; i++) { + var entry = this.tabData[i]; + entry.flagDiv = jQuery(document.createElement("span")) + .addClass("et2_tabflag") + .appendTo(this.flagContainer); + // Class to tab's div container + if (entry.widget_options && typeof entry.widget_options.class != 'undefined') { + entry.flagDiv.addClass(entry.widget_options.class); + } + entry.flagDiv.text(entry.label || "Tab"); + if (entry.hidden || this.tabData.length === 1) { + entry.flagDiv.hide(); + } + else { + entry.flagDiv.click({ "tabs": this, "idx": i }, function (e) { + e.data.tabs.setActiveTab(e.data.idx); + }); + } + entry.contentDiv = jQuery(document.createElement("div")) + .addClass("et2_tabcntr") + .appendTo(this.tabContainer); + if (this.options.align_tabs == 'v') { + entry.flagDiv.unbind('click'); + entry.flagDiv.text(""); + jQuery(document.createElement('div')) + .addClass('et2_tabtitle') + .text(entry.label || "Tab") + .click({ "tabs": this, "idx": i }, function (e) { + e.data.tabs.flagContainer.children(":eq(" + e.data.idx + ")").toggleClass('active'); + if (e.data.tabs.selected_index != e.data.idx) + e.data.tabs.setActiveTab(e.data.idx); + }) + .appendTo(entry.flagDiv); + entry.contentDiv.appendTo(entry.flagDiv); + } + } + if (this.options.align_tabs == 'v') { + this.container.addClass('vertical'); + this.tabContainer.hide(); + } + // Check for a passed in value + if (this.options.value) { + this.selected_index = 0; + for (var i = 0; i < this.tabData.length; i++) { + if (this.tabData[i].id == this.options.value) { + this.selected_index = i; + break; + } + } + } + this.setActiveTab(this.selected_index); + }; + /** + * Gets the index of the currently active tab + * + * @returns {number} + */ + et2_tabbox.prototype.get_active_tab = function () { + return this.selected_index; + }; + /** + * Sets the currently active tab by index + * + * @param {number} _idx + */ + et2_tabbox.prototype.setActiveTab = function (_idx) { + this.selected_index = _idx; + // Remove the "active" flag from all tabs-flags + jQuery(".et2_tabflag", this.flagContainer).removeClass("active"); + // Hide all tab containers + this.tabContainer.children().hide(); + // Set the tab flag with the given index active and show the corresponding + // container + this.flagContainer.children(":eq(" + _idx + ")").addClass("active"); + this.tabContainer.children(":eq(" + _idx + ")").show(); + // lookup for nm children and trigger a resize, since nm inside inactive + // tabs are not getting render due to tab's deffer loading. + if (this._children.length > 0 && this.tabData && this.tabData.length > 0) { + this.tabData[_idx]['widget'].iterateOver(function (nm) { + if (nm && nm._type == 'nextmatch') + nm.resize(); + }, this.tabData[_idx]['widget'], et2_nextmatch); + } + }; + /** + * Activate the tab containing the given widget + * + * @param {et2_widget} widget + * @return {bool} widget was found in a tab + */ + et2_tabbox.prototype.activateTab = function (widget) { + var tab = widget; + while (tab._parent && tab._parent._type != 'tabbox') { + tab = tab._parent; + } + var child_index = this._children.indexOf(tab); + for (var i = 0; i < this.tabData.length; i++) { + if (this.tabData[i].widget == tab) { + this.setActiveTab(i); + return true; + } + } + return false; + }; + et2_tabbox.prototype.getDOMNode = function (_sender) { + if (_sender === this || typeof _sender === 'undefined') { + return this.container[0]; + } + else { + for (var i = 0; i < this.tabData.length; i++) { + if (this.tabData[i].widget == _sender) { + return this.tabData[i].contentDiv[0]; + } + } + return null; + } + }; + et2_tabbox.prototype.set_tab_height = function (_height) { + this.tab_height = _height; + this.tabContainer.css("height", _height); + }; + et2_tabbox.prototype.set_height = function (_value) { + this.height = _value; + this.tabContainer.css("height", _value); + }; + /** + * getValue has to return the value of the input widget + */ + et2_tabbox.prototype.getValue = function () { + return this.selected_index !== false ? this.tabData[this.selected_index].id : undefined; + }; + /** + * Is dirty returns true if the value of the widget has changed since it + * was loaded. + */ + et2_tabbox.prototype.isDirty = function () { + return this.selected_index != this.value; + }; + /** + * Causes the dirty flag to be reseted. + */ + et2_tabbox.prototype.resetDirty = function () { + this.value = this.selected_index; + }; + et2_tabbox.prototype.isValid = function (messages) { + return true; + }; + et2_tabbox.prototype.resize = function (_height) { + if (_height) { + this.set_height(this.tabContainer.height() + _height); + } + //Set the height of tabs with the heighest height + else if (_height === 0) { + this.set_height(this.tabContainer.height()); + } + }; + /** + * Set up for printing + * + * @return {undefined|Deferred} Return a jQuery Deferred object if not done setting up + * (waiting for data) + */ + et2_tabbox.prototype.beforePrint = function () { + // Remove the "active" flag from all tabs-flags + jQuery(".et2_tabflag", this.flagContainer).removeClass("active"); + // Remove height limit + this.tabContainer.css("height", ''); + // Show all enabled tabs + for (var i = 0; i < this.tabData.length; i++) { + var entry = this.tabData[i]; + if (entry.hidden) + continue; + entry.flagDiv.insertBefore(entry.contentDiv); + entry.contentDiv.show(); + } + }; + /** + * Reset after printing + */ + et2_tabbox.prototype.afterPrint = function () { + for (var i = 0; i < this.tabData.length; i++) { + var entry = this.tabData[i]; + entry.flagDiv.appendTo(this.flagContainer); + } + this.setActiveTab(this.get_active_tab()); + }; + et2_tabbox._attributes = { + 'tabs': { + 'name': 'Tabs', + 'default': et2_no_init, + 'description': "Array of [extra] tabs. Each tab needs {label:..., template:...}. Additional optional keys are prepend, hidden and id, for access into content array" + }, + 'add_tabs': { + 'name': 'Add tabs', + 'default': false, + 'description': 'Set to true if tabs should be added to tabs from read from template, default false if not' + }, + 'tab_height': { + name: 'Tabs innerHeight', + default: '', + description: 'Set the innerHeight for the tab content' + }, + 'align_tabs': { + name: 'Tabs alignment', + type: 'string', + default: 'h', + description: 'Set tabs and their headers arrangment either horizental (h) or vertical (v). Default value is horizental.' + } + }; + return et2_tabbox; +}(et2_core_valueWidget_1.et2_valueWidget)); +et2_core_widget_1.et2_register_widget(et2_tabbox, ["tabbox"]); diff --git a/api/js/etemplate/et2_widget_tabs.ts b/api/js/etemplate/et2_widget_tabs.ts new file mode 100644 index 0000000000..e019e15dd5 --- /dev/null +++ b/api/js/etemplate/et2_widget_tabs.ts @@ -0,0 +1,584 @@ +/** + * EGroupware eTemplate2 - JS Tabs 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 + jsapi.egw; + /vendor/bower-asset/jquery/dist/jquery.js; + 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'; + +/** + * Class which implements the tabbox-tag + */ +class et2_tabbox extends et2_valueWidget implements et2_IInput,et2_IResizeable,et2_IPrint +{ + static readonly _attributes : any = { + 'tabs': { + 'name': 'Tabs', + 'default': et2_no_init, + 'description': "Array of [extra] tabs. Each tab needs {label:..., template:...}. Additional optional keys are prepend, hidden and id, for access into content array" + }, + 'add_tabs': { + 'name': 'Add tabs', + 'default': false, + 'description': 'Set to true if tabs should be added to tabs from read from template, default false if not' + }, + 'tab_height': { + name: 'Tabs innerHeight', + default: '', + description: 'Set the innerHeight for the tab content' + }, + 'align_tabs': { + name: 'Tabs alignment', + type: 'string', + default: 'h', + description: 'Set tabs and their headers arrangment either horizental (h) or vertical (v). Default value is horizental.' + } + }; + + /** + * Currently selected tab + */ + selected_index: number|boolean = 0; + tabData: any[] = []; + container: JQuery; + flagContainer: JQuery; + tabContainer: JQuery; + tab_height: number; + value: number | boolean; + + /** + * Constructor + */ + constructor(_parent, _attrs? : WidgetConfig, _child? : object) + { + // Call the inherited constructor + super(_parent, _attrs, ClassWithAttributes.extendAttributes(et2_DOMWidget._attributes, _child || {})); + + // Create the outer tabbox container + this.container = jQuery(document.createElement("div")) + .addClass("et2_tabbox"); + + // Create the upper container for the tab flags + this.flagContainer = jQuery(document.createElement("div")) + .addClass("et2_tabheader") + .appendTo(this.container); + + // Create the lower tab container + this.tabContainer = jQuery(document.createElement("div")) + .addClass("et2_tabs") + .appendTo(this.container); + } + + destroy() + { + super.destroy(); + + this.container = null; + this.flagContainer = null; + this.tabData = []; + } + + _readTabs(tabData, tabs) + { + var selected = ""; + this.selected_index = false; + var hidden = {}; + if (this.id) + { + // Set the value for this element + var contentMgr = this.getArrayMgr("content"); + if (contentMgr != null) { + var val = contentMgr.getEntry(this.id); + if (val !== null) + { + selected = val; + } + } + contentMgr = this.getArrayMgr("readonlys"); + if (contentMgr != null) { + var val = contentMgr.getEntry(this.id); + if (val !== null && typeof val !== 'undefined') + { + hidden = val; + } + } + } + var i = 0; + et2_filteredNodeIterator(tabs, function(node, nodeName) { + if (nodeName == "tab") + { + var index_name = et2_readAttrWithDefault(node, "id"); + var hide = false; + var widget_options = {}; + if(index_name) { + if(selected == index_name) this.selected_index = i; + if(hidden[index_name]) { + hide = true; + } + // Get the class attribute and add it as widget_options + var classAttr = et2_readAttrWithDefault(node,"class"); + if (classAttr) + { + widget_options = {'class':classAttr}; + } + } + tabData.push({ + "id": index_name, + "label": this.egw().lang(et2_readAttrWithDefault(node, "label", "Tab")), + "widget": null, + "widget_options": widget_options, + "contentDiv": null, + "flagDiv": null, + "hidden": hide, + "XMLNode": null, + "promise": null + }); + } + else + { + throw("Error while parsing: Invalid tag '" + nodeName + + "' in tabs tag"); + } + i++; + }, this); + + // Make sure we don't try to display a hidden tab + for(var i = 0; i < tabData.length && this.selected_index === false; i++) + { + if(!tabData[i].hidden) this.selected_index = i; + } + } + + _readTabPanels(tabData, tabpanels) + { + var i = 0; + et2_filteredNodeIterator(tabpanels, function(node, nodeName) { + if (i < tabData.length) + { + // Store node for later evaluation + tabData[i].XMLNode = node; + } + else + { + throw("Error while reading tabpanels tag, too many widgets!"); + } + i++; + }, this); + } + + loadFromXML(_node) + { + // Get the tabs and tabpanels tags + var tabsElems = et2_directChildrenByTagName(_node, "tabs"); + var tabpanelsElems = et2_directChildrenByTagName(_node, "tabpanels"); + var tabData = []; + + // Check for a parent height, we'll apply it to tab panels + var height = et2_readAttrWithDefault(_node.parentNode, "height",null); + if(height) + { + this.tabContainer.css("height", height); + } + + // if no tabs set or they should be added to tabs from xml + if (!this.options.tabs || this.options.add_tabs) + { + if (tabsElems.length == 1 && tabpanelsElems.length == 1) + { + var tabs = tabsElems[0]; + var tabpanels = tabpanelsElems[0]; + + // Parse the "tabs" tag + this._readTabs(tabData, tabs); + + // Read and create the widgets defined in the "tabpanels" + this._readTabPanels(tabData, tabpanels); + } + else + { + this.egw().debug("error","Error while parsing tabbox, none or multiple tabs or tabpanels tags!",this); + } + } + if (this.options.tabs) + { + var readonly = this.getArrayMgr("readonlys").getEntry(this.id) || {}; + for(var i = 0; i < this.options.tabs.length; i++) + { + var tab = this.options.tabs[i]; + var tab_id = tab.id || tab.template; + var tab_options = {id: tab_id, template:tab.template, url: tab.url, content: undefined}; + if(tab.id) + { + tab_options.content = tab.id; + } + tabData[tab.prepend ? 'unshift' : 'push'].call(tabData, { + "id": tab_id, + "label": this.egw().lang(tab.label), + "widget": null, + "widget_options": tab_options, + "contentDiv": null, + "flagDiv": null, + "hidden": typeof tab.hidden != "undefined" ? tab.hidden : readonly[tab_id] || false, + "XMLNode": null, + "promise": null + }); + } + } + + // Create the tab DOM-Nodes + this.createTabs(tabData); + } + + /** + * Load is finished, set up tabs to load on their own + */ + doLoadingFinished() + { + var tab_deferred = jQuery.Deferred(); + var promises = []; + var tabs = this; + + // Specially process the selected index so it shows up right away + this._loadTab(this.selected_index,promises); + + // Apply parent now, which actually puts into the DOM + // This has to be before loading the child, so the dom sub-tree is not + // disconnected, which causes problems for things like CKEditor + super.doLoadingFinished(); + + // We can do this and not wind up with 2 because child is a template, + // which has special handling + this._children[0].loadingFinished(promises); + + // Defer parsing & loading of other tabs until later + window.setTimeout(function() { + for (var i = 0; i < tabs.tabData.length; i++) + { + if (i == tabs.selected_index) continue; + tabs._loadTab(i,promises); + } + jQuery.when.apply(jQuery,promises).then(function() { + tab_deferred.resolve(); + }); + },0); + + return tab_deferred.promise(); + } + + /** + * Load & render a tab's content + * + * @param {number} index numerical index of tab in this.tabData array + * @param {array} promises + */ + _loadTab(index,promises) + { + var tabData = this.tabData[index]; + if(!tabData || tabData.loaded) return; + if(tabData.XMLNode != null) + { + if(tabData.hidden) + { + // Set hidden tab to readonly, so widgets aren't active + // Do not modify the XMLNode, or the change will be cached for all + tabData.XMLNode = tabData.XMLNode.cloneNode(); + tabData.XMLNode.setAttribute('readonly', true); + } + tabData.widget = this.createElementFromNode(tabData.XMLNode, + tabData.XMLNode.nodeName.toLowerCase()); + + // Release the XML node + tabData.XMLNode = null; + } + else if (tabData.widget_options) + { + tabData.widget = et2_createWidget('template',tabData.widget_options,this); + } + + // Set loaded flag to not do this again, even if not fully done + tabData.loaded = true; + + // loadingFinished() will be called either when the promise from doLoadingFinished is resolved, + // or during the normal execution + } + + /** + * Check for custom tabs + * + * @param {object} _attrs + */ + transformAttributes(_attrs) + { + super.transformAttributes(_attrs); + + // Add in settings that are objects + var data = this.getArrayMgr("modifications").getEntry(this.id); + for(var key in data) + { + if(typeof data[key] === 'object' && ! _attrs[key]) _attrs[key] = data[key]; + } + } + + createTabs(tabData) + { + this.tabData = tabData; + + this.tabContainer.empty(); + this.flagContainer.empty(); + + for (var i = 0; i < this.tabData.length; i++) + { + var entry = this.tabData[i]; + entry.flagDiv = jQuery(document.createElement("span")) + .addClass("et2_tabflag") + .appendTo(this.flagContainer); + // Class to tab's div container + if (entry.widget_options && typeof entry.widget_options.class != 'undefined') + { + entry.flagDiv.addClass(entry.widget_options.class); + } + entry.flagDiv.text(entry.label || "Tab"); + if(entry.hidden || this.tabData.length === 1) + { + entry.flagDiv.hide(); + } + else + { + entry.flagDiv.click({"tabs": this, "idx": i}, function(e) { + e.data.tabs.setActiveTab(e.data.idx); + }); + } + entry.contentDiv = jQuery(document.createElement("div")) + .addClass("et2_tabcntr") + .appendTo(this.tabContainer); + if (this.options.align_tabs == 'v') { + entry.flagDiv.unbind('click'); + entry.flagDiv.text(""); + jQuery(document.createElement('div')) + .addClass('et2_tabtitle') + .text(entry.label || "Tab") + .click({"tabs": this, "idx": i}, function(e) { + e.data.tabs.flagContainer.children(":eq(" + e.data.idx + ")").toggleClass('active'); + if (e.data.tabs.selected_index != e.data.idx) e.data.tabs.setActiveTab(e.data.idx); + }) + .appendTo(entry.flagDiv); + entry.contentDiv.appendTo(entry.flagDiv); + } + } + + if (this.options.align_tabs == 'v'){ + + this.container.addClass('vertical'); + this.tabContainer.hide(); + } + // Check for a passed in value + if(this.options.value) + { + this.selected_index = 0; + for(var i = 0; i < this.tabData.length; i++) + { + if(this.tabData[i].id == this.options.value) + { + this.selected_index = i; + break; + } + } + } + + this.setActiveTab(this.selected_index); + } + + /** + * Gets the index of the currently active tab + * + * @returns {number} + */ + get_active_tab() + { + return this.selected_index; + } + + /** + * Sets the currently active tab by index + * + * @param {number} _idx + */ + setActiveTab(_idx) + { + this.selected_index = _idx; + + // Remove the "active" flag from all tabs-flags + jQuery(".et2_tabflag", this.flagContainer).removeClass("active"); + + // Hide all tab containers + this.tabContainer.children().hide(); + + // Set the tab flag with the given index active and show the corresponding + // container + this.flagContainer.children(":eq(" + _idx + ")").addClass("active"); + this.tabContainer.children(":eq(" + _idx + ")").show(); + + // lookup for nm children and trigger a resize, since nm inside inactive + // tabs are not getting render due to tab's deffer loading. + if (this._children.length > 0 && this.tabData && this.tabData.length > 0) { + this.tabData[_idx]['widget'].iterateOver(function(nm){ + if (nm && nm._type == 'nextmatch') nm.resize(); + }, this.tabData[_idx]['widget'], et2_nextmatch); + } + } + + /** + * Activate the tab containing the given widget + * + * @param {et2_widget} widget + * @return {bool} widget was found in a tab + */ + activateTab(widget) + { + var tab = widget; + while(tab._parent && tab._parent._type !='tabbox') + { + tab = tab._parent; + } + + var child_index = this._children.indexOf(tab); + for(var i = 0; i < this.tabData.length; i++) + { + if(this.tabData[i].widget == tab) + { + this.setActiveTab(i); + return true; + } + } + return false; + } + + getDOMNode(_sender) + { + if (_sender === this || typeof _sender === 'undefined') + { + return this.container[0]; + } + else + { + for (var i = 0; i < this.tabData.length; i++) + { + if (this.tabData[i].widget == _sender) + { + return this.tabData[i].contentDiv[0]; + } + } + + return null; + } + } + + set_tab_height(_height) + { + this.tab_height = _height; + this.tabContainer.css("height", _height); + } + + set_height(_value) + { + this.height = _value; + + this.tabContainer.css("height", _value); + } + + /** + * getValue has to return the value of the input widget + */ + getValue() + { + return this.selected_index !== false ? this.tabData[this.selected_index].id : undefined; + } + + /** + * Is dirty returns true if the value of the widget has changed since it + * was loaded. + */ + isDirty() + { + return this.selected_index != this.value; + } + + /** + * Causes the dirty flag to be reseted. + */ + resetDirty() + { + this.value = this.selected_index; + } + + isValid(messages) + { + return true; + } + + resize(_height) + { + if(_height) + { + this.set_height(this.tabContainer.height() + _height); + } + //Set the height of tabs with the heighest height + else if(_height === 0) + { + this.set_height(this.tabContainer.height()); + } + } + + /** + * Set up for printing + * + * @return {undefined|Deferred} Return a jQuery Deferred object if not done setting up + * (waiting for data) + */ + beforePrint() + { + // Remove the "active" flag from all tabs-flags + jQuery(".et2_tabflag", this.flagContainer).removeClass("active"); + + // Remove height limit + this.tabContainer.css("height", ''); + + // Show all enabled tabs + for (var i = 0; i < this.tabData.length; i++) + { + var entry = this.tabData[i]; + if(entry.hidden) continue; + entry.flagDiv.insertBefore(entry.contentDiv); + entry.contentDiv.show(); + } + } + + /** + * Reset after printing + */ + afterPrint() + { + for (var i = 0; i < this.tabData.length; i++) + { + var entry = this.tabData[i]; + entry.flagDiv.appendTo(this.flagContainer); + } + this.setActiveTab(this.get_active_tab()); + } +} +et2_register_widget(et2_tabbox, ["tabbox"]);