diff --git a/api/js/etemplate/et2_core_inputWidget.ts b/api/js/etemplate/et2_core_inputWidget.ts index c75f25115b..8b0dc6260c 100644 --- a/api/js/etemplate/et2_core_inputWidget.ts +++ b/api/js/etemplate/et2_core_inputWidget.ts @@ -21,7 +21,7 @@ import {et2_valueWidget} from './et2_core_valueWidget' import {et2_IInput, et2_ISubmitListener} from "./et2_core_interfaces"; import {et2_compileLegacyJS} from "./et2_core_legacyJSFunctions"; // fixing circular dependencies by only importing the type (not in compiled .js) -import type {et2_tabbox} from "./et2_widget_tabs"; +import type {Et2Tabs} from "./Layout/Et2Tabs/Et2Tabs"; export interface et2_input { @@ -283,11 +283,11 @@ export class et2_inputWidget extends et2_valueWidget implements et2_IInput, et2_ // If on a tab, switch to that tab so user can see it let widget: et2_widget = this; - while (widget.getParent() && widget.getType() != 'tabbox') + while (widget.getParent() && widget.getType() !== 'et2-tabbox') { widget = widget.getParent(); } - if (widget.getType() == 'tabbox') (widget).activateTab(this); + if (widget.getType() == 'et2-tabbox') (widget).activateTab(this); } } } diff --git a/api/js/etemplate/et2_widget_color.ts b/api/js/etemplate/et2_widget_color.ts deleted file mode 100644 index d4575355cd..0000000000 --- a/api/js/etemplate/et2_widget_color.ts +++ /dev/null @@ -1,150 +0,0 @@ -/** - * EGroupware eTemplate2 - JS Color picker object - * - * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License - * @package etemplate - * @subpackage api - * @link https://www.egroupware.org - * @author Nathan Gray - * @copyright Nathan Gray 2012 - */ - -/*egw:uses - /vendor/bower-asset/jquery/dist/jquery.js; - et2_core_inputWidget; - et2_core_valueWidget; -*/ - -import {et2_register_widget, WidgetConfig} from "./et2_core_widget"; -import {et2_valueWidget} from "./et2_core_valueWidget"; -import {et2_inputWidget} from "./et2_core_inputWidget"; -import {ClassWithAttributes} from "./et2_core_inheritance"; -import {et2_IDetachedDOM} from "./et2_core_interfaces"; - -/** - * Class which implements the "colorpicker" XET-Tag - * - */ -export class et2_color extends et2_inputWidget -{ - private input: JQuery; - private span: JQuery; - private clear: JQuery; - private image: JQuery; - private cleared: boolean = true; - /** - * Constructor - */ - constructor(_parent, _attrs? : WidgetConfig, _child? : object) - { - // Call the inherited constructor - super(_parent, _attrs, ClassWithAttributes.extendAttributes(et2_color._attributes, _child || {})); - // included via etemplate2.css - //this.egw().includeCSS("phpgwapi/js/jquery/jpicker/css/jPicker-1.1.6.min.css"); - this.span = jQuery(""); - this.image = >jQuery("") - .appendTo(this.span) - .on("click", function() { - this.input.trigger('click') - }.bind(this)); - this.input = jQuery("").appendTo(this.span) - .on('change', function() { - this.cleared = false; - this.image.hide(); - }.bind(this)); - if (!this.options.readonly && !this.options.needed) - { - this.clear = jQuery("") - .appendTo(this.span) - .on("click", function() - { - this.set_value(''); - return false; - }.bind(this) - ); - } - this.setDOMNode(this.span[0]); - } - - getValue( ) - { - var value = this.input.val(); - if(this.cleared || value === '#FFFFFF' || value === '#ffffff') - { - return ''; - } - return value; - } - - set_value( color) - { - if(!color) - { - color = ''; - } - this.cleared = !color; - this.image.toggle(!color); - - this.input.val(color); - } -} -et2_register_widget(et2_color, ["colorpicker"]); - -/** - * et2_textbox_ro is the dummy readonly implementation of the textbox. - * @augments et2_valueWidget - */ -export class et2_color_ro extends et2_valueWidget implements et2_IDetachedDOM -{ - protected value: string; - private $node: JQuery; - /** - * Constructor - * - * @memberOf et2_color_ro - */ - constructor(_parent, _attrs? : WidgetConfig, _child? : object) - { - // Call the inherited constructor - super(_parent, _attrs, _child || {}); - - this.value = ""; - this.$node = jQuery(document.createElement("div")) - .addClass("et2_color"); - - this.setDOMNode(this.$node[0]); - } - - set_value( _value : string) - { - this.value = _value; - - if(!_value) _value = "inherit"; - this.$node.css("background-color", _value); - } - /** - * Code for implementing et2_IDetachedDOM - * - * @param {array} _attrs array to add further attributes to - */ - getDetachedAttributes(_attrs) - { - _attrs.push("value"); - } - - getDetachedNodes() - { - return [this.node]; - } - - setDetachedAttributes(_nodes, _values) - { - this.$node = jQuery(_nodes[0]); - if(typeof _values["value"] != 'undefined') - { - this.set_value(_values["value"]); - } - } -} -et2_register_widget(et2_color_ro, ["colorpicker_ro"]); - diff --git a/api/js/etemplate/et2_widget_tabs.ts b/api/js/etemplate/et2_widget_tabs.ts index b6f24df517..35595d2d21 100644 --- a/api/js/etemplate/et2_widget_tabs.ts +++ b/api/js/etemplate/et2_widget_tabs.ts @@ -8,599 +8,9 @@ * @author Andreas Stöckel */ -/*egw:uses - jsapi.egw; - /vendor/bower-asset/jquery/dist/jquery.js; - et2_core_valueWidget; -*/ - -import {ClassWithAttributes} from "./et2_core_inheritance"; -import {et2_createWidget, et2_register_widget, et2_widget, WidgetConfig} from "./et2_core_widget"; -import {et2_valueWidget} from './et2_core_valueWidget' -import {et2_nextmatch} from "./et2_extension_nextmatch"; -import {et2_no_init} from "./et2_core_common"; -import {et2_IInput, et2_IPrint, et2_IResizeable} from "./et2_core_interfaces"; -import {et2_directChildrenByTagName, et2_filteredNodeIterator, et2_readAttrWithDefault} from "./et2_core_xml"; +import {Et2Tabs} from "./Layout/Et2Tabs/Et2Tabs"; /** - * Class which implements the tabbox-tag + * @deprecated use Et2Tabs */ -export 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_tabbox._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("ul")) - .addClass("et2_tabheader") - .attr("role","tablist") - .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") - { - const 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 - const 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 promises = []; - var tabs = this; - - // Specially process the selected index so it shows up right away - this._loadTab(this.selected_index, promises); - - // Avoid reloading if tabs were modified by data - if(this.isInTree() && this.isAttached()) - { - return; - } - - // 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); - - let tab_deferred = new Promise((resolve, reject) => - { - // 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); - } - Promise.all(promises).then(function() - { - resolve(); - tabs.resetDirty(); - }); - }, 0); - }); - return tab_deferred; - } - - /** - * 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; - - // Set loaded flag to not do this again, even if not fully done - tabData.loaded = true; - - 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); - } - - - // 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("li")) - .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.html("" + (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") - .attr("role","tabpanel") - .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") - .attr("aria-selected","false"); - - // 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") - .attr("aria-selected","true"); - 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() - { - // We consider tab changes are not real changes - return false; - } - - /** - * 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"]); +export type et2_tabbox = Et2Tabs; \ No newline at end of file diff --git a/api/js/etemplate/et2_widget_url.ts b/api/js/etemplate/et2_widget_url.ts deleted file mode 100644 index 7e80cabcb1..0000000000 --- a/api/js/etemplate/et2_widget_url.ts +++ /dev/null @@ -1,557 +0,0 @@ -/** - * EGroupware eTemplate2 - JS URL object - * - * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License - * @package etemplate - * @subpackage api - * @link https://www.egroupware.org - * @author Nathan Gray - * @copyright Nathan Gray 2011 - */ - -/*egw:uses - et2_textbox; - et2_valueWidget; - /api/js/jquery/jquery.base64.js; -*/ - -import {et2_register_widget, WidgetConfig} from "./et2_core_widget"; -import {et2_textbox} from "./et2_widget_textbox"; -import {ClassWithAttributes} from "./et2_core_inheritance"; -import {et2_valueWidget} from "./et2_core_valueWidget"; -import {et2_no_init} from "./et2_core_common"; -import {egw} from "../jsapi/egw_global"; - -/** - * Class which implements the "url" XET-Tag, which covers URLs, email & phone - * - * @augments et2_textbox - */ -export class et2_url extends et2_textbox -{ - static readonly _attributes : any = { - "multiline": { - "ignore": true - }, - "allow_path": { - type: "boolean", - name: "Allow path", - description: "Allow a path instead of a URL, path must start with /", - default: false - }, - "trailing_slash": { - type: "boolean", - name: "Trailing slash", - description: "Require (or forbid) that the path ends with a /", - default: et2_no_init - } - }; - - /** - * Regexes for validating email addresses incl. email in angle-brackets eg. - * + "Ralf Becker " - * + "Ralf Becker (Stylite AG) " - * + "" or "rb@stylite.de" - * + '"Becker, Ralf" ' - * + "'Becker, Ralf' " - * but NOT: - * - "Becker, Ralf " (contains comma outside " or ' enclosed block) - * - "Becker < Ralf " (contains < ----------- " ---------------) - * - * About umlaut or IDN domains: we currently only allow German umlauts in domain part! - * We forbid all non-ascii chars in local part, as Horde does not yet support SMTPUTF8 extension (rfc6531) - * and we get a "SMTP server does not support internationalized header data" error otherwise. - * - * Using \042 instead of " to NOT stall minifyer! - * - * Similar, but not identical, preg is in Etemplate\Widget\Url PHP class! - * We can not use "(?@,;:\042\[\]\x80-\xff]+@([a-z0-9ÄÖÜäöüß](|[a-z0-9ÄÖÜäöüß_-]*[a-z0-9ÄÖÜäöüß])\.)+[a-z]{2,}>?$/i); - - private _button : JQuery = null; - value: string = ""; - - /** - * @memberOf et2_url - */ - createInputWidget() { - this.input = jQuery(document.createElement("input")) - .blur(this,this.validate) - .blur(this,function(e){e.data.set_value(e.data.getValue());}); - - this._button = null; - - if(this.size) { - this.set_size(this.size); - } - - this.setDOMNode(this.input[0]); - } - - destroy() { - if(this.input) { - this.input.unbind(); - } - this._button = null; - super.destroy(); - } - - /** - * Override parent to update href of 'button' - * - * @param _value value to set - */ - set_value(_value: any) { - this.update_button(_value); - super.set_value(_value); - } - - update_button(_value) { - if(this.value == _value) return; - if(_value) - { - // Create button if it doesn't exist yet - if(this._button == null) - { - this._button = jQuery(document.createElement("a")).addClass("et2_url"); - this.getSurroundings().insertDOMNode(this._button[0]); - this.getSurroundings().update(); - } - this._button.removeClass("url phone email").removeAttr("href"); - _value = this.get_link(this.getType(), _value); - switch(this.getType()) - { - case "url": - // Silently use http if no protocol - this._button.attr("href", _value).attr("target", "_blank").addClass("url"); - break; - case "url-phone": - case "url-fax": - if(_value) { - if(typeof _value == 'function') - { - this._button.click(this, _value).addClass("phone").show(); - } - else - { - this._button.attr("href", _value).addClass("phone").show(); - } - } else if (_value === false) { - // Can't make a good handler, hide button - this._button.hide(); - } - break; - case "url-email": - if(typeof _value == 'function') - { - this._button.click(this, _value).addClass("email"); - } - else - { - this._button.attr("href", _value).addClass("email"); - } - break; - } - } - else - { - if(this._button) this._button.hide(); - if(this._button && this.getSurroundings && this.getSurroundings().removeDOMNode) - { - this.getSurroundings().removeDOMNode(this._button[0]); - } - this._button = null; - } - } - - get_link(type, value) { - if(!value) return false; - // convert fax numbers to email, if configured - if (type === 'url-fax' && this.egw().config('fax_email') && - (value = value.replace('♥','').replace('(0)','').replace(/[^0-9+]/g, ''))) - { - value = value.replace(new RegExp(this.egw().config('fax_email_regexp')||'(.*)'), - this.egw().config('fax_email')); - type = 'url-email'; - } - switch(type) - { - case "url": - // Silently use http if no protocol - if(value.indexOf("://") == -1) value = "http://"+value; - break; - case "url-phone": - case "url-fax": - // Clean number - value = value.replace('♥','').replace('(0)',''); - value = value.replace(/[abc]/gi,2).replace(/[def]/gi,3).replace(/[ghi]/gi,4).replace(/[jkl]/gi,5).replace(/[mno]/gi,6); - value = value.replace(/[pqrs]/gi,7).replace(/[tuv]/gi,8).replace(/[wxyz]/gi,9); - // remove everything but numbers and plus, as telephon software might not like it - value = value.replace(/[^0-9+]/g, ''); - - // movile Webkit (iPhone, Android) have precedence over server configuration! - if (navigator.userAgent.indexOf('AppleWebKit') !== -1 && - (navigator.userAgent.indexOf("iPhone") !== -1 || navigator.userAgent.indexOf("Android") !== -1) && - value.indexOf("tel:") == -1) - { - value = "tel:"+value; - } - else if (this.egw().config("call_link")) - { - var link = this.egw().config("call_link") - // tel: links use no URL encoding according to rfc3966 section-5.1.4 - .replace("%1", this.egw().config("call_link").substr(0, 4) == 'tel:' ? - value : encodeURIComponent(value)) - .replace("%u",this.egw().user('account_lid')) - .replace("%t",this.egw().user('account_phone')); - var popup = this.egw().config("call_popup"); - value = function(ev : Event) { - if (popup && popup !== '_self' || !link.match(/^https?:/)) // execute non-http(s) links eg. tel: like before - { - egw.open_link(link, '_phonecall', popup); - } - else - { - // No popup, use AJAX. We don't care about the response. - (egw.window?egw.window.jQuery:jQuery).ajax({ - url: link, - async: true, - dataType: 'json', - type: "GET" - }); - ev.preventDefault(); - return false; - } - }; - } - else { - // Can't make a good handler - return false; - } - break; - case "url-email": - if(value.indexOf("mailto:") == -1) - { - value = "mailto:"+value; - } - if((this.egw().user('apps').mail || this.egw().user('apps').felamimail) && - this.egw().preference('force_mailto','addressbook') != '1' ) - { - return function() {egw.open_link(value);}; - } - break; - } - - return value; - } - - validate(e) { - e.data.hideMessage(); - - if(e.data._super) { - e.data._super.apply(this, arguments); - } - - // Check value, and correct if possible - var value = jQuery.trim(e.data.getValue()); - if(value == "") return; - switch(e.data._type) { - case "url": - if(value.indexOf("://") == -1 && !e.data.options.allow_path) { - e.data.set_value("http://"+value); - e.data.showMessage(e.data.egw().lang("Protocol is required"), "hint", true); - } - else if (e.data.options.allow_path && value[0] !== '/') - { - e.data.showMessage(e.data.egw().lang("Path must start with '/'"), "hint", true); - } - - // Adjust trailing slash - required or forbidden - if(e.data.options.trailing_slash === true && value[value.length-1] !== '/' ) - { - e.data.set_value(value+'/'); - } - else if (e.data.options.trailing_slash === false && value[value.length-1] === '/' ) - { - e.data.set_value(value.substr(0,value.length-1)); - } - break; - case "url-email": - if(!et2_url.EMAIL_PREG.test(value) || - // If they use Text , make sure the <> match - (value.indexOf("<") > 0 && value.indexOf(">") != value.length-1) || - (value.indexOf(">") > 0 && value.indexOf("<") < 0) - ) - { - e.data.showMessage("Invalid email","validation_error",true); - } - } - } - - attachToDOM() : boolean - { - let res = super.attachToDOM(); - - if (this.input[0].parentNode) jQuery(this.input[0].parentNode).addClass('et2_url_span'); - return res; - } -} -et2_register_widget(et2_url, ["url", "url-email", "url-phone", "url-fax"]); - -/** -* et2_url_ro is the readonly implementation of the url, email & phone. -* It renders things as links, when possible -* -* @augments et2_valueWidget -*/ -export class et2_url_ro extends et2_valueWidget -{ - static readonly _attributes : any = { - "contact_plus": { - "name": "Add contact button", - "type": "boolean", - "default": false, - "description": "Allow to add email as contact to addressbook" - }, - "full_email": { - "name": "Show full email address", - "type": "boolean", - "default": true, - "description": "Allow to show full email address if ture otherwise show only name" - } - }; - - value : string = ""; - span : JQuery = null; - static email_cache : string[] = []; - - /** - * Constructor - * - * @memberOf et2_url_ro - */ - constructor(_parent, _attrs? : WidgetConfig, _child? : object) - { - // Call the inherited constructor - super(_parent, _attrs, ClassWithAttributes.extendAttributes(et2_url_ro._attributes, _child || {})); - - this.value = ""; - this.span = jQuery(document.createElement("a")) - .addClass("et2_textbox readonly"); - // Do not a tag if no call_link is set and not in mobile, empty a tag may conflict - // with some browser telephony addons (eg. telify in FF) - if (!egw.config('call_link') && this.getType() == 'url-phone' && !egwIsMobile()) { - this.span = jQuery(document.createElement("span")) - .addClass("et2_textbox readonly"); - } - if(this.getType() == 'url-email') - { - this.span.addClass('et2_email'); - } - this.setDOMNode(this.span[0]); - } - - /** - * Set the text of the link to the label - * - * @param _value - */ - set_label(_value) - { - if(this.span && this.options.label !== _value) - { - this.options.label = _value; - this.span.text(_value); - } - } - - set_value(_value) - { - this.value = _value; - - let link = et2_url.prototype.get_link(this.getType(), _value); - - if(!link) - { - this.span.text(_value); - this.span.removeAttr("href"); - return; - } - this.span.text(this.options.label || _value); - switch(this.getType()) - { - case "url": - this.span.attr("href", link).attr("target", "_blank"); - break; - case "url-phone": - case "url-fax": - if(typeof link == 'function') - { - this.span.off('click.et2_url'); - this.span.on('click.et2_url', link); - this.span.attr("href", "#"); - } - else if (link) - { - this.span.attr("href", link); - } - break; - case "url-email": - if(typeof link == 'function') - { - this.span.off('click.et2_url'); - this.span.on('click.et2_url', link); - this.span.removeAttr("href"); - } - else - { - this.span.attr("href", link); - if(!this.span.attr("target")) - { - this.span.attr("target", "_blank"); - } - } - // wrap email address if there's a name - if (this.span.text() && this.span.text().split("<") && this.options.full_email) - { - let val : any = this.span.text().split("<"); - val = val[0] != ""? val[0]: val[2]; - - // need to preserve the original value somehow - // as it's been used for add contact plus feature - this.span.attr('title',_value); - - this.span.text(val.replace(/"/g,'')); - this.span.append(""+ - _value.replace(val,'') - .replace(//g, '>') - +""); - } - - // Add contact_plus button - if (this.options.contact_plus) - { - // If user doesn't have access to addressbook, stop - if(!egw.app('addressbook')) return; - - // Bind onmouseenter event on tag in order to add contact plus - // Need to keep span & value so it works inside nextmatch - this.span.on ('mouseenter', jQuery.proxy(function (event) { - event.stopImmediatePropagation(); - this.widget._add_contact_tooltip.call(this, et2_url_ro.email_cache[this.value]); - if(typeof et2_url_ro.email_cache[this.value] === 'undefined') - { - // Ask server if we know this email - this.widget.egw().jsonq('EGroupware\\Api\\Etemplate\\Widget\\Url::ajax_contact', - this.value, this.widget._add_contact_tooltip, this - ); - } - },{widget: this, span: this.span, value: this.value})); - this.span.on('mouseout', function(){ - if(jQuery(this).tooltip('instance')) { - jQuery(this).tooltip('close'); - } - }); - } - break; - } - } - - /** - * Add a button to add the email address as a contact - * - * @param {boolean} email_exists True, or else nothing happens - */ - private _add_contact_tooltip(email_exists) - { - var value = this.value || this.widget.value || null; - et2_url_ro.email_cache[value] = email_exists; - - // Close all the others - jQuery('.et2_email').each(function() { - if(jQuery(this).tooltip('instance')) { - jQuery(this).tooltip('close'); - } - }); - - if(email_exists || !this.span.is(':hover')) return; - - this.span.tooltip({ - items: 'a.et2_email', - position: {my:"right top", at:"left top", collision:"flipfit"}, - tooltipClass: "et2_email_popup", - content() - { - // Here we could do all sorts of things - var extra = { - 'presets[email]': jQuery(this).attr('title') ? jQuery(this).attr('title') : jQuery(this).text() - }; - - return jQuery('') - .on('click', function() { - egw.open('','addressbook','add',extra); - }); - }, - close( event, ui ) - { - ui.tooltip.hover( - function () { - jQuery(this).stop(true).fadeTo(400, 1); - //.fadeIn("slow"); // doesn't work because of stop() - }, - function () { - jQuery(this).fadeOut("400", function(){ jQuery(this).remove();}); - } - ); - } - }) - .tooltip("open"); - - jQuery('.egwGridView_scrollarea').one('scroll', jQuery.proxy(function() { - if(this.tooltip("instance")) - { - this.tooltip('destroy'); - } - }, this.span)); - } - - /** - * Code for implementing et2_IDetachedDOM - * - * @param {array} _attrs array to add further attributes to - */ - getDetachedAttributes(_attrs) - { - _attrs.push("value", "class", "statustext"); - } - - getDetachedNodes() - { - return [this.span[0]]; - } - - setDetachedAttributes(_nodes, _values) - { - // Update the properties - this.span = jQuery(_nodes[0]); - if (typeof _values["value"] != "undefined") - { - this.set_value(_values["value"]); - } - if (typeof _values["class"] != "undefined") - { - _nodes[0].setAttribute("class", _values["class"]); - } - - // Set to original status text if not set for this row - this.span.attr('title',_values.statustext ? _values.statustext : this.options.statustext); - } -} -et2_register_widget(et2_url_ro, ["url_ro", "url-email_ro", "url-phone_ro", "url-fax_ro"]); - diff --git a/api/js/etemplate/etemplate2.ts b/api/js/etemplate/etemplate2.ts index 4eebbcec0b..abde8acbd3 100644 --- a/api/js/etemplate/etemplate2.ts +++ b/api/js/etemplate/etemplate2.ts @@ -20,7 +20,6 @@ import {et2_checkType} from "./et2_core_common"; import {et2_compileLegacyJS} from "./et2_core_legacyJSFunctions"; import {et2_loadXMLFromURL} from "./et2_core_xml"; import {et2_nextmatch, et2_nextmatch_header_bar} from "./et2_extension_nextmatch"; -import {et2_tabbox} from "./et2_widget_tabs"; import '../jsapi/egw_json.js'; import {egwIsMobile} from "../egw_action/egw_action_common.js"; import './Layout/Et2Box/Et2Box'; @@ -104,11 +103,9 @@ import './et2_widget_box'; import './et2_widget_hbox'; import './et2_widget_groupbox'; import './et2_widget_button'; -import './et2_widget_color'; import './et2_widget_entry'; import './et2_widget_textbox'; import './et2_widget_number'; -import './et2_widget_url'; import './et2_widget_selectbox'; import './et2_widget_radiobox'; import './et2_widget_date'; @@ -119,7 +116,6 @@ import './et2_widget_styles'; import './et2_widget_favorites'; import './et2_widget_html'; import './et2_widget_htmlarea'; -import './et2_widget_tabs'; import './et2_widget_taglist'; import './et2_widget_toolbar'; import './et2_widget_tree';