mirror of
https://github.com/EGroupware/egroupware.git
synced 2025-01-03 04:29:28 +01:00
Revert "Bring web-component work into master branch"
To many things are not working :(
- addressbook, infolog, even timesheet index lacks at least r/o textbox and problably other widgets
- smallPART (with many extra widgets) is completly unusable
- *box widget seems not to skip empty boxes
--> we need more testing and progress, before we can merge that
This reverts commit 9cee681b94
.
This commit is contained in:
parent
a566599b7f
commit
582793d52b
@ -1,123 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* API: loading for web-components modified eTemplate from server
|
|
||||||
*
|
|
||||||
* Usage: /egroupware/api/etemplate.php/<app>/templates/default/<name>.xet
|
|
||||||
*
|
|
||||||
* @link https://www.egroupware.org
|
|
||||||
* @author Ralf Becker <rb@egroupware-org>
|
|
||||||
* @package api
|
|
||||||
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
|
||||||
*/
|
|
||||||
|
|
||||||
use EGroupware\Api;
|
|
||||||
|
|
||||||
// add et2- prefix to following widgets/tags
|
|
||||||
const ADD_ET2_PREFIX_REGEXP = '#<((/?)([vh]?box|textbox|textarea|button))(/?|\s[^>]*)>#m';
|
|
||||||
|
|
||||||
// switch evtl. set output-compression off, as we cant calculate a Content-Length header with transparent compression
|
|
||||||
ini_set('zlib.output_compression', 0);
|
|
||||||
|
|
||||||
$GLOBALS['egw_info'] = array(
|
|
||||||
'flags' => array(
|
|
||||||
'currentapp' => 'api',
|
|
||||||
'noheader' => true,
|
|
||||||
// miss-use session creation callback to send the template, in case we have no session
|
|
||||||
'autocreate_session_callback' => 'send_template',
|
|
||||||
'nocachecontrol' => true,
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
$start = microtime(true);
|
|
||||||
include '../header.inc.php';
|
|
||||||
|
|
||||||
send_template();
|
|
||||||
|
|
||||||
function send_template()
|
|
||||||
{
|
|
||||||
$header_include = microtime(true);
|
|
||||||
|
|
||||||
// release session, as we don't need it and it blocks parallel requests
|
|
||||||
$GLOBALS['egw']->session->commit_session();
|
|
||||||
|
|
||||||
header('Content-Type: application/xml; charset=UTF-8');
|
|
||||||
|
|
||||||
//$path = EGW_SERVER_ROOT.$_SERVER['PATH_INFO'];
|
|
||||||
// check for customized template in VFS
|
|
||||||
list(, $app, , $template, $name) = explode('/', $_SERVER['PATH_INFO']);
|
|
||||||
$path = Api\Etemplate::rel2path(Api\Etemplate::relPath($app . '.' . basename($name, '.xet'), $template));
|
|
||||||
if(empty($path) || !file_exists($path) || !is_readable($path))
|
|
||||||
{
|
|
||||||
http_response_code(404);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
/* disable caching for now, as you need to delete the cache, once you change ADD_ET2_PREFIX_REGEXP
|
|
||||||
$cache = $GLOBALS['egw_info']['server']['temp_dir'].'/egw_cache/eT2-Cache-'.$GLOBALS['egw_info']['server']['install_id'].$_SERVER['PATH_INFO'];
|
|
||||||
if (file_exists($cache) && filemtime($cache) > filemtime($path) &&
|
|
||||||
($str = file_get_contents($cache)) !== false)
|
|
||||||
{
|
|
||||||
$cache_read = microtime(true);
|
|
||||||
}
|
|
||||||
else*/
|
|
||||||
if(($str = file_get_contents($path)) !== false)
|
|
||||||
{
|
|
||||||
// fix <menulist...><menupopup type="select-*"/></menulist> --> <select type="select-*" .../>
|
|
||||||
$str = preg_replace('#<menulist([^>]*)>[\r\n\s]*<menupopup([^>]+>)[\r\n\s]*</menulist>#', '<select$1$2', $str);
|
|
||||||
|
|
||||||
// fix <textbox multiline="true" .../> --> <textarea .../> (et2-prefix and self-closing is handled below)
|
|
||||||
$str = preg_replace('#<textbox(.*?)\smultiline="true"(.*?)/>#u', '<textarea$1$2/>', $str);
|
|
||||||
|
|
||||||
$str = preg_replace_callback(ADD_ET2_PREFIX_REGEXP, static function (array $matches)
|
|
||||||
{
|
|
||||||
return '<' . $matches[2] . 'et2-' . $matches[3] .
|
|
||||||
// web-components must not be self-closing (no "<et2-button .../>", but "<et2-button ...></et2-button>")
|
|
||||||
(substr($matches[4], -1) === '/' ? substr($matches[4], 0, -1) . '></et2-' . $matches[3] : $matches[4]) . '>';
|
|
||||||
}, $str);
|
|
||||||
|
|
||||||
$processing = microtime(true);
|
|
||||||
|
|
||||||
if(isset($cache) && (file_exists($cache_dir = dirname($cache)) || mkdir($cache_dir, 0755, true)))
|
|
||||||
{
|
|
||||||
file_put_contents($cache, $str);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// stop here for not existing file path-traversal for both file and cache here
|
|
||||||
if(empty($str) || strpos($path, '..') !== false)
|
|
||||||
{
|
|
||||||
http_response_code(404);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
// headers to allow caching, egw_framework specifies etag on url to force reload, even with Expires header
|
|
||||||
Api\Session::cache_control(86400); // cache for one day
|
|
||||||
$etag = '"' . md5($str) . '"';
|
|
||||||
Header('ETag: ' . $etag);
|
|
||||||
|
|
||||||
// if servers send a If-None-Match header, response with 304 Not Modified, if etag matches
|
|
||||||
if(isset($_SERVER['HTTP_IF_NONE_MATCH']) && $_SERVER['HTTP_IF_NONE_MATCH'] === $etag)
|
|
||||||
{
|
|
||||||
header("HTTP/1.1 304 Not Modified");
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
// we run our own gzip compression, to set a correct Content-Length of the encoded content
|
|
||||||
if(function_exists('gzencode') && in_array('gzip', explode(',', $_SERVER['HTTP_ACCEPT_ENCODING']), true))
|
|
||||||
{
|
|
||||||
$gzip_start = microtime(true);
|
|
||||||
$str = gzencode($str);
|
|
||||||
header('Content-Encoding: gzip');
|
|
||||||
$gziping = microtime(true) - $gzip_start;
|
|
||||||
}
|
|
||||||
header('X-Timing: header-include=' . number_format($header_include - $GLOBALS['start'], 3) .
|
|
||||||
(empty($processing) ? ', cache-read=' . number_format($cache_read - $header_include, 3) :
|
|
||||||
', processing=' . number_format($processing - $header_include, 3)) .
|
|
||||||
(!empty($gziping) ? ', gziping=' . number_format($gziping, 3) : '') .
|
|
||||||
', total=' . number_format(microtime(true) - $GLOBALS['start'], 3)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Content-Length header is important, otherwise browsers dont cache!
|
|
||||||
Header('Content-Length: ' . bytes($str));
|
|
||||||
echo $str;
|
|
||||||
|
|
||||||
exit; // stop further processing eg. redirect to login
|
|
||||||
}
|
|
@ -1,97 +0,0 @@
|
|||||||
/**
|
|
||||||
* EGroupware eTemplate2 - Box widget
|
|
||||||
*
|
|
||||||
* @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
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
import {css, html, LitElement} from "@lion/core";
|
|
||||||
import {Et2Widget} from "../Et2Widget/Et2Widget";
|
|
||||||
|
|
||||||
export class Et2Box extends Et2Widget(LitElement)
|
|
||||||
{
|
|
||||||
static get styles()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
css`
|
|
||||||
:host {
|
|
||||||
display: block;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
:host > div {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: nowrap;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: stretch;
|
|
||||||
}
|
|
||||||
/* CSS for child elements */
|
|
||||||
::slotted(*) {
|
|
||||||
margin: 0px 2px;
|
|
||||||
flex: 1 0 auto;
|
|
||||||
}
|
|
||||||
::slotted([align="left"]) {
|
|
||||||
margin-right: auto;
|
|
||||||
}
|
|
||||||
::slotted([align="right"]) {
|
|
||||||
margin-left: auto;
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
render()
|
|
||||||
{
|
|
||||||
return html`
|
|
||||||
<div class="et2_box" ${this.id ? html`id="${this.id}"` : ''}>
|
|
||||||
<slot></slot>
|
|
||||||
</div> `;
|
|
||||||
}
|
|
||||||
|
|
||||||
set_label(new_label)
|
|
||||||
{
|
|
||||||
// Boxes don't have labels
|
|
||||||
}
|
|
||||||
|
|
||||||
_createNamespace() : boolean
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
customElements.define("et2-box", Et2Box);
|
|
||||||
|
|
||||||
export class Et2HBox extends Et2Box
|
|
||||||
{
|
|
||||||
static get styles()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
...super.styles,
|
|
||||||
css`
|
|
||||||
:host > div {
|
|
||||||
flex-direction: row;
|
|
||||||
}`
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
customElements.define("et2-hbox", Et2HBox);
|
|
||||||
|
|
||||||
export class Et2VBox extends Et2Box
|
|
||||||
{
|
|
||||||
static get styles()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
...super.styles,
|
|
||||||
css`
|
|
||||||
:host > div {
|
|
||||||
flex-direction: column;
|
|
||||||
}`
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
customElements.define("et2-vbox", Et2VBox);
|
|
@ -1,32 +0,0 @@
|
|||||||
/**
|
|
||||||
* Test file for Etemplate webComponent base widget Et2Box
|
|
||||||
*/
|
|
||||||
import {assert, fixture} from '@open-wc/testing';
|
|
||||||
import {Et2Box} from "../Et2Box";
|
|
||||||
import {html} from "lit-element";
|
|
||||||
|
|
||||||
describe("Box widget", () =>
|
|
||||||
{
|
|
||||||
// Reference to component under test
|
|
||||||
let element : Et2Box;
|
|
||||||
|
|
||||||
// Setup run before each test
|
|
||||||
beforeEach(async() =>
|
|
||||||
{
|
|
||||||
// Create an element to test with, and wait until it's ready
|
|
||||||
element = await fixture<Et2Box>(html`
|
|
||||||
<et2-box></et2-box>
|
|
||||||
`);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('is defined', () =>
|
|
||||||
{
|
|
||||||
assert.instanceOf(element, Et2Box);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('has no label', () =>
|
|
||||||
{
|
|
||||||
element.set_label("Nope");
|
|
||||||
assert.isEmpty(element.shadowRoot.querySelectorAll('.et2_label'));
|
|
||||||
})
|
|
||||||
});
|
|
@ -1,183 +0,0 @@
|
|||||||
/**
|
|
||||||
* EGroupware eTemplate2 - Button widget
|
|
||||||
*
|
|
||||||
* @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
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
import {css, html} from "@lion/core";
|
|
||||||
import {LionButton} from "@lion/button";
|
|
||||||
import {SlotMixin} from "@lion/core";
|
|
||||||
import {Et2InputWidget} from "../Et2InputWidget/Et2InputWidget";
|
|
||||||
|
|
||||||
export class Et2Button extends Et2InputWidget(SlotMixin(LionButton))
|
|
||||||
{
|
|
||||||
protected _created_icon_node : HTMLImageElement;
|
|
||||||
protected clicked : boolean = false;
|
|
||||||
private _image : string;
|
|
||||||
|
|
||||||
static get styles()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
...super.styles,
|
|
||||||
css`
|
|
||||||
:host {
|
|
||||||
padding: 1px 8px;
|
|
||||||
/* These should probably come from somewhere else */
|
|
||||||
border-radius: 3px;
|
|
||||||
background-color: #e6e6e6;
|
|
||||||
max-width: 125px;
|
|
||||||
}
|
|
||||||
:host([readonly]) {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
/* Set size for icon */
|
|
||||||
::slotted([slot="icon"]) {
|
|
||||||
width: 20px;
|
|
||||||
padding-right: 3px;
|
|
||||||
}`,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
static get properties()
|
|
||||||
{
|
|
||||||
return {
|
|
||||||
...super.properties,
|
|
||||||
image: {type: String}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get slots()
|
|
||||||
{
|
|
||||||
return {
|
|
||||||
...super.slots,
|
|
||||||
icon: () =>
|
|
||||||
{
|
|
||||||
return document.createElement("img");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor()
|
|
||||||
{
|
|
||||||
super();
|
|
||||||
|
|
||||||
// Property default values
|
|
||||||
this._image = '';
|
|
||||||
|
|
||||||
// Do not add icon here, no children can be added in constructor
|
|
||||||
|
|
||||||
// Define a default click handler
|
|
||||||
// If a different one gets set via attribute, it will be used instead
|
|
||||||
this.onclick = (typeof this.onclick === "function") ? this.onclick : () =>
|
|
||||||
{
|
|
||||||
return this.getInstanceManager().submit();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
connectedCallback()
|
|
||||||
{
|
|
||||||
super.connectedCallback();
|
|
||||||
|
|
||||||
//this.classList.add("et2_button")
|
|
||||||
}
|
|
||||||
|
|
||||||
set image(new_image : string)
|
|
||||||
{
|
|
||||||
let oldValue = this._image;
|
|
||||||
if(new_image.indexOf("http") >= 0)
|
|
||||||
{
|
|
||||||
this._image = new_image
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this._image = this.egw().image(new_image, 'etemplate');
|
|
||||||
}
|
|
||||||
this.requestUpdate("image", oldValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
_handleClick(event : MouseEvent) : boolean
|
|
||||||
{
|
|
||||||
// ignore click on readonly button
|
|
||||||
if(this.disabled || this.readonly)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.clicked = true;
|
|
||||||
|
|
||||||
// Cancel buttons don't trigger the close confirmation prompt
|
|
||||||
if(this.classList.contains("et2_button_cancel"))
|
|
||||||
{
|
|
||||||
this.getInstanceManager()?.skip_close_prompt();
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!super._handleClick(event))
|
|
||||||
{
|
|
||||||
this.clicked = false;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.clicked = false;
|
|
||||||
this.getInstanceManager()?.skip_close_prompt(false);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
render()
|
|
||||||
{
|
|
||||||
if(this.readonly)
|
|
||||||
{
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
this._iconNode.src = this._image;
|
|
||||||
|
|
||||||
return html`
|
|
||||||
<div class="button-content et2_button" id="${this._buttonId}">
|
|
||||||
<slot name="icon"></slot>
|
|
||||||
<slot>${this._label}</slot>
|
|
||||||
</div> `;
|
|
||||||
}
|
|
||||||
|
|
||||||
get _iconNode() : HTMLImageElement
|
|
||||||
{
|
|
||||||
return <HTMLImageElement>(Array.from(this.children)).find(
|
|
||||||
el => (<HTMLElement>el).slot === "icon",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implementation of the et2_IInput interface
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Always return false as a button is never dirty
|
|
||||||
*/
|
|
||||||
isDirty()
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
resetDirty()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
getValue()
|
|
||||||
{
|
|
||||||
if(this.clicked)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If "null" is returned, the result is not added to the submitted
|
|
||||||
// array.
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// @ts-ignore TypeScript is not recognizing that Et2Button is a LitElement
|
|
||||||
customElements.define("et2-button", Et2Button);
|
|
@ -1,69 +0,0 @@
|
|||||||
/**
|
|
||||||
* Test file for Etemplate webComponent base widget Et2Box
|
|
||||||
*/
|
|
||||||
import {assert, fixture} from '@open-wc/testing';
|
|
||||||
import {Et2Button} from "../Et2Button";
|
|
||||||
import type {Et2Widget} from "../../Et2Widget/Et2Widget";
|
|
||||||
import {html} from "lit-element";
|
|
||||||
import * as sinon from 'sinon';
|
|
||||||
|
|
||||||
describe("Button widget", () =>
|
|
||||||
{
|
|
||||||
// Reference to component under test
|
|
||||||
let element : Et2Button;
|
|
||||||
|
|
||||||
|
|
||||||
// Setup run before each test
|
|
||||||
beforeEach(async() =>
|
|
||||||
{
|
|
||||||
// Create an element to test with, and wait until it's ready
|
|
||||||
element = await fixture<Et2Button>(html`
|
|
||||||
<et2-button label="I'm a button"></et2-button>
|
|
||||||
`);
|
|
||||||
|
|
||||||
// Stub egw()
|
|
||||||
sinon.stub(element, "egw").returns({
|
|
||||||
tooltipUnbind: () => {},
|
|
||||||
// Image always give check mark. Use data URL to avoid having to serve an actual image
|
|
||||||
image: i => "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxNS4wLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+DQo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkViZW5lXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4Ig0KCSB3aWR0aD0iMzJweCIgaGVpZ2h0PSIzMnB4IiB2aWV3Qm94PSIwIDAgMzIgMzIiIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDMyIDMyIiB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBmaWxsPSIjNjk2OTY5IiBkPSJNNi45NDMsMjguNDUzDQoJYzAuOTA2LDAuNzY1LDIuMDk3LDEuMTI3LDMuMjg2LDEuMTA5YzAuNDMsMC4wMTQsMC44NTItMC4wNjgsMS4yNjUtMC4yMDdjMC42NzktMC4xOCwxLjMyOC0wLjQ1LDEuODY2LTAuOTAyTDI5LjQwMywxNC45DQoJYzEuNzcyLTEuNDk4LDEuNzcyLTMuOTI1LDAtNS40MjJjLTEuNzcyLTEuNDk3LTQuNjQ2LTEuNDk3LTYuNDE4LDBMMTAuMTE5LDIwLjM0OWwtMi4zODktMi40MjRjLTEuNDQtMS40NTctMy43NzItMS40NTctNS4yMTIsMA0KCWMtMS40MzgsMS40Ni0xLjQzOCwzLjgyNSwwLDUuMjgxQzIuNTE4LDIzLjIwNiw1LjQ3NCwyNi45NDcsNi45NDMsMjguNDUzeiIvPg0KPC9zdmc+DQo="
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Make sure it works
|
|
||||||
it('is defined', () =>
|
|
||||||
{
|
|
||||||
assert.instanceOf(element, Et2Button);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('has a label', () =>
|
|
||||||
{
|
|
||||||
element.set_label("Label set");
|
|
||||||
|
|
||||||
assert.equal(element.textContent, "Label set");
|
|
||||||
})
|
|
||||||
|
|
||||||
it("click happens", () =>
|
|
||||||
{
|
|
||||||
// Setup
|
|
||||||
let clickSpy = sinon.spy();
|
|
||||||
element.onclick = clickSpy;
|
|
||||||
|
|
||||||
// Click
|
|
||||||
element.dispatchEvent(new MouseEvent("click"));
|
|
||||||
|
|
||||||
// Check for once & only once
|
|
||||||
assert(clickSpy.calledOnce, "Click only once");
|
|
||||||
})
|
|
||||||
|
|
||||||
it("gets an icon", async() =>
|
|
||||||
{
|
|
||||||
element.image = "check";
|
|
||||||
|
|
||||||
// Wait for the render to finish
|
|
||||||
await element.updateComplete;
|
|
||||||
|
|
||||||
let image = element.querySelectorAll("img");
|
|
||||||
assert.equal(image.length, 1);
|
|
||||||
assert.equal(image[0].src, element.egw().image("check"));
|
|
||||||
})
|
|
||||||
});
|
|
@ -1,40 +0,0 @@
|
|||||||
/**
|
|
||||||
* EGroupware eTemplate2 - Colorpicker widget (WebComponent)
|
|
||||||
*
|
|
||||||
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
|
||||||
* @package etemplate
|
|
||||||
* @subpackage api
|
|
||||||
* @link https://www.egroupware.org
|
|
||||||
* @author Hadi Nategh
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
import {html} from "@lion/core";
|
|
||||||
import {LionInput} from "@lion/input";
|
|
||||||
import {Et2Widget} from "../Et2Widget/Et2Widget";
|
|
||||||
import {Et2InputWidget} from "../Et2InputWidget/Et2InputWidget";
|
|
||||||
|
|
||||||
export class Et2Colorpicker extends Et2InputWidget(Et2Widget(LionInput))
|
|
||||||
{
|
|
||||||
|
|
||||||
constructor()
|
|
||||||
{
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
connectedCallback()
|
|
||||||
{
|
|
||||||
super.connectedCallback();
|
|
||||||
}
|
|
||||||
|
|
||||||
render()
|
|
||||||
{
|
|
||||||
return html`
|
|
||||||
<div class="et2_colorpicker" id="${this.id}">
|
|
||||||
<input class="et2_colorpicker" type="color" />
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
customElements.define('et2-colorpicker', Et2Colorpicker);
|
|
@ -1,187 +0,0 @@
|
|||||||
/**
|
|
||||||
* EGroupware eTemplate2 - Date widget (WebComponent)
|
|
||||||
*
|
|
||||||
* @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
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
import {css, html} from "@lion/core";
|
|
||||||
import {LionInputDatepicker} from "@lion/input-datepicker";
|
|
||||||
import {Unparseable} from "@lion/form-core";
|
|
||||||
import {Et2InputWidget} from "../Et2InputWidget/Et2InputWidget";
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* To parse a date into the right format
|
|
||||||
*
|
|
||||||
* @param {string} dateString
|
|
||||||
* @returns {Date | undefined}
|
|
||||||
*/
|
|
||||||
export function parseDate(dateString)
|
|
||||||
{
|
|
||||||
// First try the server format
|
|
||||||
try
|
|
||||||
{
|
|
||||||
let date = new Date(dateString);
|
|
||||||
if(date instanceof Date)
|
|
||||||
{
|
|
||||||
return date;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(e)
|
|
||||||
{
|
|
||||||
// Nope, that didn't parse directly
|
|
||||||
}
|
|
||||||
|
|
||||||
let formatString = <string>(egw.preference("dateformat") || 'Y-m-d');
|
|
||||||
formatString = formatString.replaceAll(/-\/\./ig, '-');
|
|
||||||
let parsedString = "";
|
|
||||||
switch(formatString)
|
|
||||||
{
|
|
||||||
case 'd-m-Y':
|
|
||||||
case 'd/m/Y':
|
|
||||||
parsedString = `${dateString.slice(6, 10)}/${dateString.slice(
|
|
||||||
3,
|
|
||||||
5,
|
|
||||||
)}/${dateString.slice(0, 2)}`;
|
|
||||||
break;
|
|
||||||
case 'm-d-Y':
|
|
||||||
parsedString = `${dateString.slice(6, 10)}/${dateString.slice(
|
|
||||||
0,
|
|
||||||
2,
|
|
||||||
)}/${dateString.slice(3, 5)}`;
|
|
||||||
break;
|
|
||||||
case 'Y-m-d':
|
|
||||||
case 'Y/m/d':
|
|
||||||
parsedString = `${dateString.slice(0, 4)}/${dateString.slice(
|
|
||||||
5,
|
|
||||||
7,
|
|
||||||
)}/${dateString.slice(8, 10)}`;
|
|
||||||
break;
|
|
||||||
case 'd-M-Y':
|
|
||||||
parsedString = `${dateString.slice(6, 10)}/${dateString.slice(
|
|
||||||
3,
|
|
||||||
5,
|
|
||||||
)}/${dateString.slice(0, 2)}`;
|
|
||||||
default:
|
|
||||||
parsedString = '0000/00/00';
|
|
||||||
}
|
|
||||||
|
|
||||||
const [year, month, day] = parsedString.split('/').map(Number);
|
|
||||||
const parsedDate = new Date(Date.UTC(year, month - 1, day));
|
|
||||||
|
|
||||||
// Check if parsedDate is not `Invalid Date` or that the date has changed (e.g. the not existing 31.02.2020)
|
|
||||||
if (
|
|
||||||
year > 0 &&
|
|
||||||
month > 0 &&
|
|
||||||
day > 0 &&
|
|
||||||
parsedDate.getDate() === day &&
|
|
||||||
parsedDate.getMonth() === month - 1
|
|
||||||
)
|
|
||||||
{
|
|
||||||
return parsedDate;
|
|
||||||
}
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Format dates according to user preference
|
|
||||||
*
|
|
||||||
* @param {Date} date
|
|
||||||
* @param {import('@lion/localize/types/LocalizeMixinTypes').FormatDateOptions} [options] Intl options are available
|
|
||||||
* set 'dateFormat': "Y-m-d" to specify a particular format
|
|
||||||
* @returns {string}
|
|
||||||
*/
|
|
||||||
export function formatDate(date: Date, options): string
|
|
||||||
{
|
|
||||||
if (!date || !(date instanceof Date))
|
|
||||||
{
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
let _value = '';
|
|
||||||
// Add timezone offset back in, or formatDate will lose those hours
|
|
||||||
let formatDate = new Date(date.valueOf() + date.getTimezoneOffset() * 60 * 1000);
|
|
||||||
|
|
||||||
let dateformat = options.dateFormat || <string>egw.preference("dateformat") || 'Y-m-d';
|
|
||||||
|
|
||||||
var replace_map = {
|
|
||||||
d: (date.getUTCDate() < 10 ? "0" : "") + date.getUTCDate(),
|
|
||||||
m: (date.getUTCMonth() < 9 ? "0" : "") + (date.getUTCMonth() + 1),
|
|
||||||
Y: "" + date.getUTCFullYear()
|
|
||||||
}
|
|
||||||
var re = new RegExp(Object.keys(replace_map).join("|"), "gi");
|
|
||||||
_value = dateformat.replace(re, function (matched)
|
|
||||||
{
|
|
||||||
return replace_map[matched];
|
|
||||||
});
|
|
||||||
return _value;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Et2Date extends Et2InputWidget(LionInputDatepicker)
|
|
||||||
{
|
|
||||||
static get styles()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
...super.styles,
|
|
||||||
css`
|
|
||||||
/* Custom CSS */
|
|
||||||
`,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
static get properties()
|
|
||||||
{
|
|
||||||
return {
|
|
||||||
...super.properties
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor()
|
|
||||||
{
|
|
||||||
super();
|
|
||||||
this.parser = parseDate;
|
|
||||||
this.formatter = formatDate;
|
|
||||||
}
|
|
||||||
|
|
||||||
connectedCallback()
|
|
||||||
{
|
|
||||||
super.connectedCallback();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {Date} modelValue
|
|
||||||
*/
|
|
||||||
// eslint-disable-next-line class-methods-use-this
|
|
||||||
serializer(modelValue : Date)
|
|
||||||
{
|
|
||||||
// isValidDate() is hidden inside LionInputDate, and not exported
|
|
||||||
// @ts-ignore Can't call isNan(Date), but we're just checking
|
|
||||||
if(!(modelValue instanceof Date) || isNaN(modelValue))
|
|
||||||
{
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
// modelValue is localized, so we take the timezone offset in milliseconds and subtract it
|
|
||||||
// before converting it to ISO string.
|
|
||||||
const offset = modelValue.getTimezoneOffset() * 60000;
|
|
||||||
return new Date(modelValue.getTime() - offset).toJSON().replace(/\.\d{3}Z$/, 'Z');
|
|
||||||
}
|
|
||||||
|
|
||||||
getValue()
|
|
||||||
{
|
|
||||||
// The supplied value was not understandable, return null
|
|
||||||
if(this.modelValue instanceof Unparseable)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// It isn't always the case that we want the serializer value, but for Et2Date we do
|
|
||||||
return this.serializer(this.modelValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// @ts-ignore TypeScript is not recognizing that Et2Date is a LitElement
|
|
||||||
customElements.define("et2-date", Et2Date);
|
|
@ -1,146 +0,0 @@
|
|||||||
import {et2_IInput, et2_IInputNode} from "../et2_core_interfaces";
|
|
||||||
import {Et2Widget} from "../Et2Widget/Et2Widget";
|
|
||||||
import {dedupeMixin} from "@lion/core";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This mixin will allow any LitElement to become an Et2InputWidget
|
|
||||||
*
|
|
||||||
* Usage:
|
|
||||||
* export class Et2Button extends Et2InputWidget(LitWidget)) {...}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Need to define the interface first, to get around TypeScript issues with protected/public
|
|
||||||
* This must match the public API for Et2InputWidgetClass
|
|
||||||
* @see https://lit.dev/docs/composition/mixins/#typing-the-subclass
|
|
||||||
*/
|
|
||||||
export declare class Et2InputWidgetInterface
|
|
||||||
{
|
|
||||||
readOnly : boolean;
|
|
||||||
protected value : string | number | Object;
|
|
||||||
|
|
||||||
public set_value(any) : void;
|
|
||||||
|
|
||||||
public get_value() : any;
|
|
||||||
|
|
||||||
public getValue() : any;
|
|
||||||
|
|
||||||
public isDirty() : boolean;
|
|
||||||
|
|
||||||
public resetDirty() : void;
|
|
||||||
|
|
||||||
public isValid(messages : string[]) : boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Et2InputWidgetMixin = (superclass) =>
|
|
||||||
{
|
|
||||||
class Et2InputWidgetClass extends Et2Widget(superclass) implements et2_IInput, et2_IInputNode
|
|
||||||
{
|
|
||||||
protected value : string | number | Object;
|
|
||||||
protected _oldValue : string | number | Object;
|
|
||||||
|
|
||||||
/** WebComponent **/
|
|
||||||
static get styles()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
...super.styles
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
static get properties()
|
|
||||||
{
|
|
||||||
return {
|
|
||||||
...super.properties,
|
|
||||||
// readOnly is what the property is in Lion, readonly is the attribute
|
|
||||||
readOnly: {
|
|
||||||
type: Boolean,
|
|
||||||
attribute: 'readonly',
|
|
||||||
reflect: true,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(...args : any[])
|
|
||||||
{
|
|
||||||
super(...args);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
set_value(new_value)
|
|
||||||
{
|
|
||||||
this.value = new_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
get_value()
|
|
||||||
{
|
|
||||||
return this.getValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
getValue()
|
|
||||||
{
|
|
||||||
return typeof this.serializedValue !== "undefined" ? this.serializedValue : this.modalValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
isDirty()
|
|
||||||
{
|
|
||||||
let value = this.getValue();
|
|
||||||
if(typeof value !== typeof this._oldValue)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if(this._oldValue === value)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
switch(typeof this._oldValue)
|
|
||||||
{
|
|
||||||
case "object":
|
|
||||||
if(Array.isArray(this._oldValue) &&
|
|
||||||
this._oldValue.length !== value.length
|
|
||||||
)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
for(let key in this._oldValue)
|
|
||||||
{
|
|
||||||
if(this._oldValue[key] !== value[key])
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
default:
|
|
||||||
return this._oldValue != value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
resetDirty()
|
|
||||||
{
|
|
||||||
this._oldValue = this.getValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
isValid(messages)
|
|
||||||
{
|
|
||||||
var ok = true;
|
|
||||||
|
|
||||||
// Check for required
|
|
||||||
if(this.options && this.options.needed && !this.options.readonly && !this.disabled &&
|
|
||||||
(this.getValue() == null || this.getValue().valueOf() == ''))
|
|
||||||
{
|
|
||||||
messages.push(this.egw().lang('Field must not be empty !!!'));
|
|
||||||
ok = false;
|
|
||||||
}
|
|
||||||
return ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
getInputNode()
|
|
||||||
{
|
|
||||||
// From LionInput
|
|
||||||
return this._inputNode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Et2InputWidgetClass;
|
|
||||||
}
|
|
||||||
export const Et2InputWidget = dedupeMixin(Et2InputWidgetMixin);
|
|
@ -1,39 +0,0 @@
|
|||||||
/**
|
|
||||||
* Common base for easily running some standard tests on all input widgets
|
|
||||||
*
|
|
||||||
* This file should not get run on its own, extend it
|
|
||||||
*
|
|
||||||
* TODO: Not sure exactly how to make this happen yet. Maybe:
|
|
||||||
* https://github.com/mochajs/mocha/wiki/Shared-Behaviours
|
|
||||||
* <code>
|
|
||||||
* shared:
|
|
||||||
* exports.shouldBehaveLikeAUser = function() {
|
|
||||||
* it('should have .name.first', function() {
|
|
||||||
* this.user.name.first.should.equal('tobi');
|
|
||||||
* })
|
|
||||||
*
|
|
||||||
* it('should have .name.last', function() {
|
|
||||||
* this.user.name.last.should.equal('holowaychuk');
|
|
||||||
* })
|
|
||||||
*
|
|
||||||
* describe('.fullname()', function() {
|
|
||||||
* it('should return the full name', function() {
|
|
||||||
* this.user.fullname().should.equal('tobi holowaychuk');
|
|
||||||
* })
|
|
||||||
* })
|
|
||||||
* };
|
|
||||||
* test.js:
|
|
||||||
*
|
|
||||||
* var User = require('./user').User
|
|
||||||
* , Admin = require('./user').Admin
|
|
||||||
* , shared = require('./shared');
|
|
||||||
*
|
|
||||||
* describe('User', function() {
|
|
||||||
* beforeEach(function() {
|
|
||||||
* this.user = new User('tobi', 'holowaychuk');
|
|
||||||
* })
|
|
||||||
*
|
|
||||||
* shared.shouldBehaveLikeAUser();
|
|
||||||
* })
|
|
||||||
* </code>
|
|
||||||
*/
|
|
@ -1,83 +0,0 @@
|
|||||||
/**
|
|
||||||
* EGroupware eTemplate2 - Textbox widget (WebComponent)
|
|
||||||
*
|
|
||||||
* @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
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
import {css, html} from "@lion/core";
|
|
||||||
import {LionTextarea} from "@lion/textarea";
|
|
||||||
import {Et2InputWidget} from "../Et2InputWidget/Et2InputWidget";
|
|
||||||
import {Et2Widget} from "../Et2Widget/Et2Widget";
|
|
||||||
|
|
||||||
|
|
||||||
export class Et2Textarea extends Et2InputWidget(LionTextarea)
|
|
||||||
{
|
|
||||||
static get styles()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
...super.styles,
|
|
||||||
css`
|
|
||||||
:host {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
static get properties()
|
|
||||||
{
|
|
||||||
return {
|
|
||||||
...super.properties,
|
|
||||||
/**
|
|
||||||
* Specify the width of the text area.
|
|
||||||
* If not set, it will expand to fill the space available.
|
|
||||||
*/
|
|
||||||
width: {type: String, reflect: true},
|
|
||||||
/**
|
|
||||||
* Specify the height of the text area.
|
|
||||||
* If not set, it will expand to fill the space available.
|
|
||||||
*/
|
|
||||||
height: {type: String, reflect: true},
|
|
||||||
onkeypress: Function,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor()
|
|
||||||
{
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
connectedCallback()
|
|
||||||
{
|
|
||||||
super.connectedCallback();
|
|
||||||
}
|
|
||||||
|
|
||||||
set width(value)
|
|
||||||
{
|
|
||||||
if(this._inputNode)
|
|
||||||
{
|
|
||||||
this._inputNode.style.width = value;
|
|
||||||
}
|
|
||||||
this.resizeTextarea();
|
|
||||||
}
|
|
||||||
|
|
||||||
set height(value)
|
|
||||||
{
|
|
||||||
if(this._inputNode)
|
|
||||||
{
|
|
||||||
this._inputNode.style.height = value;
|
|
||||||
}
|
|
||||||
this.resizeTextarea();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// @ts-ignore TypeScript is not recognizing that Et2Textarea is a LitElement
|
|
||||||
customElements.define("et2-textarea", Et2Textarea);
|
|
@ -1,49 +0,0 @@
|
|||||||
/**
|
|
||||||
* EGroupware eTemplate2 - Textbox widget (WebComponent)
|
|
||||||
*
|
|
||||||
* @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
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
import {css, html} from "@lion/core";
|
|
||||||
import {LionInput} from "@lion/input";
|
|
||||||
import {Et2InputWidget} from "../Et2InputWidget/Et2InputWidget";
|
|
||||||
|
|
||||||
export class Et2Textbox extends Et2InputWidget(LionInput)
|
|
||||||
{
|
|
||||||
|
|
||||||
static get styles()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
...super.styles,
|
|
||||||
css`
|
|
||||||
/* Custom CSS */
|
|
||||||
`,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
static get properties()
|
|
||||||
{
|
|
||||||
return {
|
|
||||||
...super.properties,
|
|
||||||
onkeypress: Function,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(...args : any[])
|
|
||||||
{
|
|
||||||
super(...args);
|
|
||||||
}
|
|
||||||
|
|
||||||
connectedCallback()
|
|
||||||
{
|
|
||||||
super.connectedCallback();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// @ts-ignore TypeScript is not recognizing that Et2Textbox is a LitElement
|
|
||||||
customElements.define("et2-textbox", Et2Textbox);
|
|
@ -1,32 +0,0 @@
|
|||||||
/**
|
|
||||||
* Test file for Etemplate webComponent Textbox
|
|
||||||
*/
|
|
||||||
import {assert, fixture} from '@open-wc/testing';
|
|
||||||
import {Et2Textbox} from "../Et2Textbox";
|
|
||||||
import {html} from "lit-element";
|
|
||||||
|
|
||||||
describe("Textbox widget", () =>
|
|
||||||
{
|
|
||||||
// Reference to component under test
|
|
||||||
let element : Et2Textbox;
|
|
||||||
|
|
||||||
// Setup run before each test
|
|
||||||
beforeEach(async() =>
|
|
||||||
{
|
|
||||||
// Create an element to test with, and wait until it's ready
|
|
||||||
element = await fixture<Et2Textbox>(html`
|
|
||||||
<et2-textbox></et2-textbox>
|
|
||||||
`);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('is defined', () =>
|
|
||||||
{
|
|
||||||
assert.instanceOf(element, Et2Textbox);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('has a label', () =>
|
|
||||||
{
|
|
||||||
element.set_label("Yay label");
|
|
||||||
assert.isEmpty(element.shadowRoot.querySelectorAll('.et2_label'));
|
|
||||||
})
|
|
||||||
});
|
|
@ -1,34 +0,0 @@
|
|||||||
/**
|
|
||||||
* Testing Et2Textbox input / values
|
|
||||||
*/
|
|
||||||
|
|
||||||
import {assert, fixture} from "@open-wc/testing";
|
|
||||||
import {html} from "lit-html";
|
|
||||||
import {Et2Textbox} from "../Et2Textbox";
|
|
||||||
|
|
||||||
describe("Textbox input / values", () =>
|
|
||||||
{
|
|
||||||
// Reference to component under test
|
|
||||||
let element : Et2Textbox;
|
|
||||||
|
|
||||||
// Setup run before each test
|
|
||||||
beforeEach(async() =>
|
|
||||||
{
|
|
||||||
// Create an element to test with, and wait until it's ready
|
|
||||||
element = await fixture<Et2Textbox>(html`
|
|
||||||
<et2-textbox></et2-textbox>
|
|
||||||
`);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("Takes a value", () =>
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
Complains about set_value() being missing?
|
|
||||||
let test_value = "test value";
|
|
||||||
debugger;
|
|
||||||
element.set_value(test_value);
|
|
||||||
assert(document.querySelector('input').should.have.text(test_value))
|
|
||||||
|
|
||||||
*/
|
|
||||||
})
|
|
||||||
});
|
|
@ -1,910 +0,0 @@
|
|||||||
import {et2_IDOMNode, et2_implements_registry} from "../et2_core_interfaces";
|
|
||||||
import {et2_arrayMgr} from "../et2_core_arrayMgr";
|
|
||||||
import {et2_attribute_registry, et2_registry, et2_widget} from "../et2_core_widget";
|
|
||||||
import type {etemplate2} from "../etemplate2";
|
|
||||||
import {et2_compileLegacyJS} from "../et2_core_legacyJSFunctions";
|
|
||||||
import {et2_cloneObject, et2_csvSplit} from "../et2_core_common";
|
|
||||||
// @ts-ignore
|
|
||||||
import type {IegwAppLocal} from "../../jsapi/egw_global";
|
|
||||||
import {ClassWithAttributes, ClassWithInterfaces} from "../et2_core_inheritance";
|
|
||||||
import {dedupeMixin} from "@lion/core";
|
|
||||||
import type {et2_container} from "../et2_core_baseWidget";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This mixin will allow any LitElement to become an Et2Widget
|
|
||||||
*
|
|
||||||
* Usage:
|
|
||||||
* @example
|
|
||||||
* export class Et2Loading extends Et2Widget(BXLoading) { ... }
|
|
||||||
* @example
|
|
||||||
* export class Et2Button extends Et2InputWidget(Et2Widget(BXButton)) { ... }
|
|
||||||
*
|
|
||||||
* @see Mixin explanation https://lit.dev/docs/composition/mixins/
|
|
||||||
*/
|
|
||||||
function applyMixins(derivedCtor : any, baseCtors : any[])
|
|
||||||
{
|
|
||||||
baseCtors.forEach(baseCtor =>
|
|
||||||
{
|
|
||||||
Object.getOwnPropertyNames(baseCtor.prototype).forEach(name =>
|
|
||||||
{
|
|
||||||
if(name !== 'constructor')
|
|
||||||
{
|
|
||||||
derivedCtor.prototype[name] = baseCtor.prototype[name];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const Et2WidgetMixin = (superClass) =>
|
|
||||||
{
|
|
||||||
class Et2WidgetClass extends superClass implements et2_IDOMNode
|
|
||||||
{
|
|
||||||
|
|
||||||
/** et2_widget compatability **/
|
|
||||||
protected _mgrs : et2_arrayMgr[] = [];
|
|
||||||
protected _parent : Et2WidgetClass | et2_widget | null = null;
|
|
||||||
private _inst : etemplate2 | null = null;
|
|
||||||
private supportedWidgetClasses = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Not actually required by et2_widget, but needed to keep track of non-webComponent children
|
|
||||||
*/
|
|
||||||
private _legacy_children : et2_widget[] = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Properties - default values, and actually creating them as fields
|
|
||||||
*/
|
|
||||||
protected _label : string = "";
|
|
||||||
private statustext : string = "";
|
|
||||||
protected disabled : Boolean = false;
|
|
||||||
|
|
||||||
|
|
||||||
/** WebComponent **/
|
|
||||||
static get properties()
|
|
||||||
{
|
|
||||||
return {
|
|
||||||
...super.properties,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Defines whether this widget is visible.
|
|
||||||
* Not to be confused with an input widget's HTML attribute 'disabled'.",
|
|
||||||
*/
|
|
||||||
disabled: {
|
|
||||||
type: Boolean,
|
|
||||||
reflect: true
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tooltip which is shown for this element on hover
|
|
||||||
*/
|
|
||||||
statustext: {type: String},
|
|
||||||
|
|
||||||
// Defined in parent hierarchy
|
|
||||||
//label: {type: String},
|
|
||||||
|
|
||||||
onclick: {
|
|
||||||
type: Function
|
|
||||||
},
|
|
||||||
|
|
||||||
/*** Style type attributes ***/
|
|
||||||
/**
|
|
||||||
* Used by Et2Box to determine alignment.
|
|
||||||
* Allowed values are left, right
|
|
||||||
*/
|
|
||||||
align: {
|
|
||||||
type: String,
|
|
||||||
reflect: true
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Allow styles to be set on widgets.
|
|
||||||
* Any valid CSS will work. Mostly for testing, maybe we won't use this?
|
|
||||||
* That kind of style should normally go in the app.css
|
|
||||||
*/
|
|
||||||
style: {
|
|
||||||
type: String,
|
|
||||||
reflect: true
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Widget Mixin constructor
|
|
||||||
*
|
|
||||||
* Note the ...args parameter and super() call
|
|
||||||
*
|
|
||||||
* @param args
|
|
||||||
*/
|
|
||||||
constructor(...args : any[])
|
|
||||||
{
|
|
||||||
super(...args);
|
|
||||||
|
|
||||||
this.addEventListener("click", this._handleClick.bind(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
connectedCallback()
|
|
||||||
{
|
|
||||||
super.connectedCallback();
|
|
||||||
|
|
||||||
this.set_label(this._label);
|
|
||||||
|
|
||||||
if(this.statustext)
|
|
||||||
{
|
|
||||||
this.egw().tooltipBind(this, this.statustext);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
disconnectedCallback()
|
|
||||||
{
|
|
||||||
this.egw()?.tooltipUnbind(this);
|
|
||||||
|
|
||||||
this.removeEventListener("click", this._handleClick.bind(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* NOT the setter, since we cannot add to the DOM before connectedCallback()
|
|
||||||
*
|
|
||||||
* TODO: This is not best practice. Should just set property, DOM modification should be done in render
|
|
||||||
* https://lit-element.polymer-project.org/guide/templates#design-a-performant-template
|
|
||||||
*
|
|
||||||
* @param value
|
|
||||||
*/
|
|
||||||
set_label(value : string)
|
|
||||||
{
|
|
||||||
let oldValue = this._label;
|
|
||||||
|
|
||||||
// Remove old
|
|
||||||
let oldLabels = this.getElementsByClassName("et2_label");
|
|
||||||
while(oldLabels[0])
|
|
||||||
{
|
|
||||||
this.removeChild(oldLabels[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
this._label = value;
|
|
||||||
if(value)
|
|
||||||
{
|
|
||||||
let label = document.createElement("span");
|
|
||||||
label.classList.add("et2_label");
|
|
||||||
label.textContent = this._label;
|
|
||||||
// We should have a slot in the template for the label
|
|
||||||
//label.slot="label";
|
|
||||||
this.appendChild(label);
|
|
||||||
this.requestUpdate('label', oldValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
set label(value : string)
|
|
||||||
{
|
|
||||||
let oldValue = this.label;
|
|
||||||
this._label = value;
|
|
||||||
this.requestUpdate('label', oldValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Event handlers
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Click handler calling custom handler set via onclick attribute to this.onclick
|
|
||||||
*
|
|
||||||
* @param _ev
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
_handleClick(_ev : MouseEvent) : boolean
|
|
||||||
{
|
|
||||||
if (typeof this.onclick == 'function')
|
|
||||||
{
|
|
||||||
// Make sure function gets a reference to the widget, splice it in as 2. argument if not
|
|
||||||
var args = Array.prototype.slice.call(arguments);
|
|
||||||
if(args.indexOf(this) == -1) args.splice(1, 0, this);
|
|
||||||
|
|
||||||
return this.onclick.apply(this, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** et2_widget compatability **/
|
|
||||||
destroy()
|
|
||||||
{
|
|
||||||
// Not really needed, use the disconnectedCallback() and let the browser handle it
|
|
||||||
}
|
|
||||||
|
|
||||||
isInTree() : boolean
|
|
||||||
{
|
|
||||||
// TODO: Probably should watch the state or something
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads the widget tree from an XML node
|
|
||||||
*
|
|
||||||
* @param _node xml node
|
|
||||||
*/
|
|
||||||
loadFromXML(_node)
|
|
||||||
{
|
|
||||||
// Load the child nodes.
|
|
||||||
for(var i = 0; i < _node.childNodes.length; i++)
|
|
||||||
{
|
|
||||||
var node = _node.childNodes[i];
|
|
||||||
var widgetType = node.nodeName.toLowerCase();
|
|
||||||
|
|
||||||
if(widgetType == "#comment")
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(widgetType == "#text")
|
|
||||||
{
|
|
||||||
if(node.data.replace(/^\s+|\s+$/g, ''))
|
|
||||||
{
|
|
||||||
this.innerText = node.data;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the new element
|
|
||||||
this.createElementFromNode(node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a et2_widget from an XML node.
|
|
||||||
*
|
|
||||||
* First the type and attributes are read from the node. Then the readonly & modifications
|
|
||||||
* arrays are checked for changes specific to the loaded data. Then the appropriate
|
|
||||||
* constructor is called. After the constructor returns, the widget has a chance to
|
|
||||||
* further initialize itself from the XML node when the widget's loadFromXML() method
|
|
||||||
* is called with the node.
|
|
||||||
*
|
|
||||||
* @param _node XML node to read
|
|
||||||
* @param _name XML node name
|
|
||||||
*
|
|
||||||
* @return et2_widget
|
|
||||||
*/
|
|
||||||
createElementFromNode(_node, _name?)
|
|
||||||
{
|
|
||||||
var attributes = {};
|
|
||||||
|
|
||||||
// Parse the "readonly" and "type" flag for this element here, as they
|
|
||||||
// determine which constructor is used
|
|
||||||
let _nodeName = attributes["type"] = _node.getAttribute("type") ?
|
|
||||||
_node.getAttribute("type") : _node.nodeName.toLowerCase();
|
|
||||||
const readonly = attributes["readonly"] = this.getArrayMgr("readonlys") ?
|
|
||||||
(<any>this.getArrayMgr("readonlys")).isReadOnly(
|
|
||||||
_node.getAttribute("id"), _node.getAttribute("readonly"),
|
|
||||||
typeof this.readonly !== "undefined" ? this.readonly : false) : false;
|
|
||||||
|
|
||||||
// Check to see if modifications change type
|
|
||||||
var modifications = this.getArrayMgr("modifications");
|
|
||||||
if(modifications && _node.getAttribute("id"))
|
|
||||||
{
|
|
||||||
let entry : any = modifications.getEntry(_node.getAttribute("id"));
|
|
||||||
if(entry == null)
|
|
||||||
{
|
|
||||||
// Try again, but skip the fancy stuff
|
|
||||||
// TODO: Figure out why the getEntry() call doesn't always work
|
|
||||||
entry = modifications.data[_node.getAttribute("id")];
|
|
||||||
if(entry)
|
|
||||||
{
|
|
||||||
this.egw().debug("warn", "getEntry(" + _node.getAttribute("id") + ") failed, but the data is there.", modifications, entry);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Try the root, in case a namespace got missed
|
|
||||||
entry = modifications.getRoot().getEntry(_node.getAttribute("id"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(entry && entry.type && typeof entry.type === 'string')
|
|
||||||
{
|
|
||||||
_nodeName = attributes["type"] = entry.type;
|
|
||||||
}
|
|
||||||
entry = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if _nodeName / type-attribute contains something to expand (eg. type="@${row}[type]"),
|
|
||||||
// we need to expand it now as it defines the constructor and by that attributes parsed via parseXMLAttrs!
|
|
||||||
if(_nodeName.charAt(0) == '@' || _nodeName.indexOf('$') >= 0)
|
|
||||||
{
|
|
||||||
_nodeName = attributes["type"] = this.getArrayMgr('content').expandName(_nodeName);
|
|
||||||
}
|
|
||||||
|
|
||||||
let widget = null;
|
|
||||||
if(undefined == window.customElements.get(_nodeName))
|
|
||||||
{
|
|
||||||
// Get the constructor - if the widget is readonly, use the special "_ro"
|
|
||||||
// constructor if it is available
|
|
||||||
var constructor = et2_registry[typeof et2_registry[_nodeName] == "undefined" ? 'placeholder' : _nodeName];
|
|
||||||
if(readonly === true && typeof et2_registry[_nodeName + "_ro"] != "undefined")
|
|
||||||
{
|
|
||||||
constructor = et2_registry[_nodeName + "_ro"];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse the attributes from the given XML attributes object
|
|
||||||
this.parseXMLAttrs(_node.attributes, attributes, constructor.prototype);
|
|
||||||
|
|
||||||
// Do an sanity check for the attributes
|
|
||||||
ClassWithAttributes.generateAttributeSet(et2_attribute_registry[constructor.name], attributes);
|
|
||||||
|
|
||||||
// Creates the new widget, passes this widget as an instance and
|
|
||||||
// passes the widgetType. Then it goes on loading the XML for it.
|
|
||||||
widget = new constructor(this, attributes);
|
|
||||||
|
|
||||||
// Load the widget itself from XML
|
|
||||||
widget.loadFromXML(_node);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if(readonly === true && typeof window.customElements.get(_nodeName + "_ro") != "undefined")
|
|
||||||
{
|
|
||||||
_nodeName += "_ro";
|
|
||||||
}
|
|
||||||
widget = loadWebComponent(_nodeName, _node, this);
|
|
||||||
|
|
||||||
if(this.addChild)
|
|
||||||
{
|
|
||||||
// webcomponent going into old et2_widget
|
|
||||||
this.addChild(widget);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return widget;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The parseXMLAttrs function takes an XML DOM attributes object
|
|
||||||
* and adds the given attributes to the _target associative array. This
|
|
||||||
* function also parses the legacyOptions.
|
|
||||||
*
|
|
||||||
* @param _attrsObj is the XML DOM attributes object
|
|
||||||
* @param {object} _target is the object to which the attributes should be written.
|
|
||||||
* @param {et2_widget} _proto prototype with attributes and legacyOptions attribute
|
|
||||||
*/
|
|
||||||
parseXMLAttrs(_attrsObj, _target, _proto)
|
|
||||||
{
|
|
||||||
// Check whether the attributes object is really existing, if not abort
|
|
||||||
if(typeof _attrsObj == "undefined")
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Iterate over the given attributes and parse them
|
|
||||||
var mgr = this.getArrayMgr("content");
|
|
||||||
for(var i = 0; i < _attrsObj.length; i++)
|
|
||||||
{
|
|
||||||
var attrName = _attrsObj[i].name;
|
|
||||||
var attrValue = _attrsObj[i].value;
|
|
||||||
|
|
||||||
// Special handling for the legacy options
|
|
||||||
if(attrName == "options" && _proto.constructor.legacyOptions && _proto.constructor.legacyOptions.length > 0)
|
|
||||||
{
|
|
||||||
let legacy = _proto.constructor.legacyOptions || [];
|
|
||||||
let attrs = et2_attribute_registry[Object.getPrototypeOf(_proto).constructor.name] || {};
|
|
||||||
// Check for modifications on legacy options here. Normal modifications
|
|
||||||
// are handled in widget constructor, but it's too late for legacy options then
|
|
||||||
if(_target.id && this.getArrayMgr("modifications").getEntry(_target.id))
|
|
||||||
{
|
|
||||||
var mod : any = this.getArrayMgr("modifications").getEntry(_target.id);
|
|
||||||
if(typeof mod.options != "undefined") attrValue = _attrsObj[i].value = mod.options;
|
|
||||||
}
|
|
||||||
// expand legacyOptions with content
|
|
||||||
if(attrValue.charAt(0) == '@' || attrValue.indexOf('$') != -1)
|
|
||||||
{
|
|
||||||
attrValue = mgr.expandName(attrValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse the legacy options (as a string, other types not allowed)
|
|
||||||
var splitted = et2_csvSplit(attrValue + "");
|
|
||||||
|
|
||||||
for(var j = 0; j < splitted.length && j < legacy.length; j++)
|
|
||||||
{
|
|
||||||
// Blank = not set, unless there's more legacy options provided after
|
|
||||||
if(splitted[j].trim().length === 0 && legacy.length >= splitted.length) continue;
|
|
||||||
|
|
||||||
// Check to make sure we don't overwrite a current option with a legacy option
|
|
||||||
if(typeof _target[legacy[j]] === "undefined")
|
|
||||||
{
|
|
||||||
attrValue = splitted[j];
|
|
||||||
|
|
||||||
/**
|
|
||||||
If more legacy options than expected, stuff them all in the last legacy option
|
|
||||||
Some legacy options take a comma separated list.
|
|
||||||
*/
|
|
||||||
if(j == legacy.length - 1 && splitted.length > legacy.length)
|
|
||||||
{
|
|
||||||
attrValue = splitted.slice(j);
|
|
||||||
}
|
|
||||||
|
|
||||||
var attr = et2_attribute_registry[_proto.constructor.name][legacy[j]] || {};
|
|
||||||
|
|
||||||
// If the attribute is marked as boolean, parse the
|
|
||||||
// expression as bool expression.
|
|
||||||
if(attr.type == "boolean")
|
|
||||||
{
|
|
||||||
attrValue = mgr.parseBoolExpression(attrValue);
|
|
||||||
}
|
|
||||||
else if(typeof attrValue != "object")
|
|
||||||
{
|
|
||||||
attrValue = mgr.expandName(attrValue);
|
|
||||||
}
|
|
||||||
_target[legacy[j]] = attrValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(attrName == "readonly" && typeof _target[attrName] != "undefined")
|
|
||||||
{
|
|
||||||
// do NOT overwrite already evaluated readonly attribute
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
let attrs = et2_attribute_registry[_proto.constructor.name] || {};
|
|
||||||
if(mgr != null && typeof attrs[attrName] != "undefined")
|
|
||||||
{
|
|
||||||
var attr = attrs[attrName];
|
|
||||||
|
|
||||||
// If the attribute is marked as boolean, parse the
|
|
||||||
// expression as bool expression.
|
|
||||||
if(attr.type == "boolean")
|
|
||||||
{
|
|
||||||
attrValue = mgr.parseBoolExpression(attrValue);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
attrValue = mgr.expandName(attrValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the attribute
|
|
||||||
_target[attrName] = attrValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
iterateOver(_callback : Function, _context, _type)
|
|
||||||
{
|
|
||||||
if(et2_implements_registry[_type] && et2_implements_registry[_type](this))
|
|
||||||
{
|
|
||||||
_callback.call(_context, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Webcomponent children
|
|
||||||
for(let child of Array.from(this.children))
|
|
||||||
{
|
|
||||||
if(typeof child.iterateOver == "function")
|
|
||||||
{
|
|
||||||
child.iterateOver(_callback, _context, _type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Legacy children
|
|
||||||
for(let i = 0; i < this._legacy_children.length; i++)
|
|
||||||
{
|
|
||||||
this._legacy_children[i].iterateOver(_callback, _context, _type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Needed for legacy compatability.
|
|
||||||
*
|
|
||||||
* @param {Promise[]} promises List of promises from widgets that are not done. Pass an empty array, it will be filled if needed.
|
|
||||||
*/
|
|
||||||
loadingFinished(promises)
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* This is needed mostly as a bridge between non-WebComponent widgets and
|
|
||||||
* connectedCallback(). It's not really needed if the whole tree is WebComponent.
|
|
||||||
* WebComponents can be added as children immediately after creation, and they handle the
|
|
||||||
* rest themselves with their normal lifecycle (especially connectedCallback(), which is kind
|
|
||||||
* of the equivalent of doLoadingFinished()
|
|
||||||
*/
|
|
||||||
if(this.getParent() instanceof et2_widget && this.getParent().getDOMNode(this))
|
|
||||||
{
|
|
||||||
this.getParent().getDOMNode(this).append(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
for(let i = 0; i < this._legacy_children.length; i++)
|
|
||||||
{
|
|
||||||
let child = this._legacy_children[i];
|
|
||||||
let child_node = typeof child.getDOMNode !== "undefined" ? child.getDOMNode(child) : null;
|
|
||||||
if(child_node && child_node !== this)
|
|
||||||
{
|
|
||||||
this.append(child_node);
|
|
||||||
}
|
|
||||||
child.loadingFinished(promises);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getWidgetById(_id)
|
|
||||||
{
|
|
||||||
if(this.id == _id)
|
|
||||||
{
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setParent(new_parent : Et2WidgetClass | et2_widget)
|
|
||||||
{
|
|
||||||
this._parent = new_parent;
|
|
||||||
|
|
||||||
if(this.id)
|
|
||||||
{
|
|
||||||
// Create a namespace for this object
|
|
||||||
if(this._createNamespace())
|
|
||||||
{
|
|
||||||
this.checkCreateNamespace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
getParent() : HTMLElement | et2_widget
|
|
||||||
{
|
|
||||||
let parentNode = this.parentNode;
|
|
||||||
|
|
||||||
// If parent is an old et2_widget, use it
|
|
||||||
if(this._parent)
|
|
||||||
{
|
|
||||||
return this._parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
return <HTMLElement>parentNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
addChild(child : et2_widget | Et2WidgetClass)
|
|
||||||
{
|
|
||||||
if(child instanceof et2_widget)
|
|
||||||
{
|
|
||||||
child._parent = this;
|
|
||||||
|
|
||||||
// During legacy widget creation, the child's DOM node won't be available yet.
|
|
||||||
this._legacy_children.push(child);
|
|
||||||
let child_node = null;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
child_node = typeof child.getDOMNode !== "undefined" ? child.getDOMNode(child) : null;
|
|
||||||
}
|
|
||||||
catch(e)
|
|
||||||
{
|
|
||||||
// Child did not give up its DOM node nicely but errored instead
|
|
||||||
}
|
|
||||||
if(child_node && child_node !== this)
|
|
||||||
{
|
|
||||||
this.append(child_node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this.append(child);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get [legacy] children
|
|
||||||
* Use <obj>.children to get web component children
|
|
||||||
* @returns {et2_widget[]}
|
|
||||||
*/
|
|
||||||
getChildren()
|
|
||||||
{
|
|
||||||
return this._legacy_children;
|
|
||||||
}
|
|
||||||
|
|
||||||
getType() : string
|
|
||||||
{
|
|
||||||
return this.nodeName;
|
|
||||||
}
|
|
||||||
|
|
||||||
getDOMNode() : HTMLElement
|
|
||||||
{
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a copy of this widget.
|
|
||||||
*
|
|
||||||
* @param {et2_widget} _parent parent to set for clone, default null
|
|
||||||
*/
|
|
||||||
clone(_parent?) : Et2WidgetClass
|
|
||||||
{
|
|
||||||
// Default _parent to null
|
|
||||||
if(typeof _parent == "undefined")
|
|
||||||
{
|
|
||||||
_parent = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the copy
|
|
||||||
var copy = <Et2WidgetClass>this.cloneNode(true);
|
|
||||||
|
|
||||||
// Create a clone of all child widgets of the given object
|
|
||||||
for(var i = 0; i < copy.getChildren().length; i++)
|
|
||||||
{
|
|
||||||
copy.addChild(copy.getChildren()[i].clone(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy a reference to the content array manager
|
|
||||||
copy.setArrayMgrs(this.getArrayMgrs());
|
|
||||||
|
|
||||||
return copy;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the array manager for the given part
|
|
||||||
*
|
|
||||||
* @param {string} _part which array mgr to set
|
|
||||||
* @param {object} _mgr
|
|
||||||
*/
|
|
||||||
setArrayMgr(_part : string, _mgr : et2_arrayMgr)
|
|
||||||
{
|
|
||||||
this._mgrs[_part] = _mgr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the array manager object for the given part
|
|
||||||
*
|
|
||||||
* @param {string} managed_array_type name of array mgr to return
|
|
||||||
*/
|
|
||||||
getArrayMgr(managed_array_type : string) : et2_arrayMgr | null
|
|
||||||
{
|
|
||||||
if(this._mgrs && typeof this._mgrs[managed_array_type] != "undefined")
|
|
||||||
{
|
|
||||||
return this._mgrs[managed_array_type];
|
|
||||||
}
|
|
||||||
else if(this.getParent())
|
|
||||||
{
|
|
||||||
return this.getParent().getArrayMgr(managed_array_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets all array manager objects - this function can be used to set the
|
|
||||||
* root array managers of the container object.
|
|
||||||
*
|
|
||||||
* @param {object} _mgrs
|
|
||||||
*/
|
|
||||||
setArrayMgrs(_mgrs)
|
|
||||||
{
|
|
||||||
this._mgrs = <et2_arrayMgr[]>et2_cloneObject(_mgrs);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an associative array containing the top-most array managers.
|
|
||||||
*
|
|
||||||
* @param _mgrs is used internally and should not be supplied.
|
|
||||||
*/
|
|
||||||
getArrayMgrs(_mgrs? : object)
|
|
||||||
{
|
|
||||||
if(typeof _mgrs == "undefined")
|
|
||||||
{
|
|
||||||
_mgrs = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add all managers of this object to the result, if they have not already
|
|
||||||
// been set in the result
|
|
||||||
for(var key in this._mgrs)
|
|
||||||
{
|
|
||||||
if(typeof _mgrs[key] == "undefined")
|
|
||||||
{
|
|
||||||
_mgrs[key] = this._mgrs[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Recursively applies this function to the parent widget
|
|
||||||
if(this._parent)
|
|
||||||
{
|
|
||||||
this._parent.getArrayMgrs(_mgrs);
|
|
||||||
}
|
|
||||||
|
|
||||||
return _mgrs;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks whether a namespace exists for this element in the content array.
|
|
||||||
* If yes, an own perspective of the content array is created. If not, the
|
|
||||||
* parent content manager is used.
|
|
||||||
*
|
|
||||||
* Constructor attributes are passed in case a child needs to make decisions
|
|
||||||
*/
|
|
||||||
checkCreateNamespace()
|
|
||||||
{
|
|
||||||
// Get the content manager
|
|
||||||
var mgrs = this.getArrayMgrs();
|
|
||||||
|
|
||||||
for(var key in mgrs)
|
|
||||||
{
|
|
||||||
var mgr = mgrs[key];
|
|
||||||
|
|
||||||
// Get the original content manager if we have already created a
|
|
||||||
// perspective for this node
|
|
||||||
if(typeof this._mgrs[key] != "undefined" && mgr.perspectiveData.owner == this)
|
|
||||||
{
|
|
||||||
mgr = mgr.parentMgr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check whether the manager has a namespace for the id of this object
|
|
||||||
var entry = mgr.getEntry(this.id);
|
|
||||||
if(typeof entry === 'object' && entry !== null || this.id)
|
|
||||||
{
|
|
||||||
// The content manager has an own node for this object, so
|
|
||||||
// create an own perspective.
|
|
||||||
this._mgrs[key] = mgr.openPerspective(this, this.id);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// The current content manager does not have an own namespace for
|
|
||||||
// this element, so use the content manager of the parent.
|
|
||||||
delete (this._mgrs[key]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the instance manager
|
|
||||||
*
|
|
||||||
* @return {etemplate2}
|
|
||||||
*/
|
|
||||||
getInstanceManager()
|
|
||||||
{
|
|
||||||
if(this._inst != null)
|
|
||||||
{
|
|
||||||
return this._inst;
|
|
||||||
}
|
|
||||||
else if(this.getParent())
|
|
||||||
{
|
|
||||||
return this.getParent().getInstanceManager();
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the base widget
|
|
||||||
* Usually this is the same as getInstanceManager().widgetContainer
|
|
||||||
*/
|
|
||||||
getRoot() : et2_container
|
|
||||||
{
|
|
||||||
if(this.getParent() != null)
|
|
||||||
{
|
|
||||||
return this.getParent().getRoot();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return <et2_container><unknown>this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the path into the data array. By default, array manager takes care of
|
|
||||||
* this, but some extensions need to override this
|
|
||||||
*/
|
|
||||||
getPath()
|
|
||||||
{
|
|
||||||
var path = this.getArrayMgr("content").getPath();
|
|
||||||
|
|
||||||
// Prevent namespaced widgets with value from going an extra layer deep
|
|
||||||
if(this.id && this._createNamespace() && path[path.length - 1] == this.id) path.pop();
|
|
||||||
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
_createNamespace() : boolean
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
egw() : IegwAppLocal
|
|
||||||
{
|
|
||||||
if(this.getParent() != null && !(this.getParent() instanceof HTMLElement))
|
|
||||||
{
|
|
||||||
return (<et2_widget>this.getParent()).egw();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the window this object belongs to
|
|
||||||
var wnd = null;
|
|
||||||
// @ts-ignore Technically this doesn't have implements(), but it's mixed in
|
|
||||||
if(this.implements(et2_IDOMNode))
|
|
||||||
{
|
|
||||||
var node = (<et2_IDOMNode><unknown>this).getDOMNode();
|
|
||||||
if(node && node.ownerDocument)
|
|
||||||
{
|
|
||||||
wnd = node.ownerDocument.parentNode || node.ownerDocument.defaultView;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we're the root object, return the phpgwapi API instance
|
|
||||||
return typeof egw === "function" ? egw('phpgwapi', wnd) : null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Add some more stuff in
|
|
||||||
applyMixins(Et2WidgetClass, [ClassWithInterfaces]);
|
|
||||||
|
|
||||||
return Et2WidgetClass;
|
|
||||||
}
|
|
||||||
export const Et2Widget = dedupeMixin(Et2WidgetMixin);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load a Web Component
|
|
||||||
* @param _nodeName
|
|
||||||
* @param _template_node
|
|
||||||
*/
|
|
||||||
export function loadWebComponent(_nodeName : string, _template_node, parent : Et2Widget | et2_widget) : HTMLElement
|
|
||||||
{
|
|
||||||
// @ts-ignore
|
|
||||||
let widget = <Et2Widget>document.createElement(_nodeName);
|
|
||||||
widget.textContent = _template_node.textContent;
|
|
||||||
|
|
||||||
const widget_class = window.customElements.get(_nodeName);
|
|
||||||
if(!widget_class)
|
|
||||||
{
|
|
||||||
throw Error("Unknown or unregistered WebComponent '" + _nodeName + "', could not find class");
|
|
||||||
}
|
|
||||||
widget.setParent(parent);
|
|
||||||
var mgr = widget.getArrayMgr("content");
|
|
||||||
|
|
||||||
// Set read-only. Doesn't really matter if it's a ro widget, but otherwise it needs set
|
|
||||||
widget.readonly = parent.getArrayMgr("readonlys") ?
|
|
||||||
(<any>parent.getArrayMgr("readonlys")).isReadOnly(
|
|
||||||
_template_node.getAttribute("id"), _template_node.getAttribute("readonly"),
|
|
||||||
typeof parent.readonly !== "undefined" ? parent.readonly : false) : false;
|
|
||||||
|
|
||||||
// Apply any set attributes - widget will do its own coercion
|
|
||||||
_template_node.getAttributeNames().forEach(attribute =>
|
|
||||||
{
|
|
||||||
let attrValue = _template_node.getAttribute(attribute);
|
|
||||||
|
|
||||||
// If there is not attribute set, ignore it. Widget sets its own default.
|
|
||||||
if(typeof attrValue === "undefined") return;
|
|
||||||
const property = widget_class.getPropertyOptions(attribute);
|
|
||||||
|
|
||||||
switch(property.type)
|
|
||||||
{
|
|
||||||
case Boolean:
|
|
||||||
// If the attribute is marked as boolean, parse the
|
|
||||||
// expression as bool expression.
|
|
||||||
attrValue = mgr.parseBoolExpression(attrValue);
|
|
||||||
break;
|
|
||||||
case Function:
|
|
||||||
// We parse it into a function here so we can pass in the widget as context.
|
|
||||||
// Leaving it to the LitElement conversion loses the widget as context
|
|
||||||
if(typeof attrValue !== "function")
|
|
||||||
{
|
|
||||||
attrValue = et2_compileLegacyJS(attrValue, widget, widget);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
attrValue = mgr.expandName(attrValue);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set as attribute or property, as appropriate
|
|
||||||
if(widget.getAttributeNames().indexOf(attribute) >= 0)
|
|
||||||
{
|
|
||||||
// Set as attribute (reflected in DOM)
|
|
||||||
widget.setAttribute(attribute, attrValue);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Set as property, not attribute
|
|
||||||
widget[attribute] = attrValue;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if(widget_class.getPropertyOptions("value") && widget.set_value)
|
|
||||||
{
|
|
||||||
if(mgr != null)
|
|
||||||
{
|
|
||||||
let val = mgr.getEntry(widget.id, false, true);
|
|
||||||
if(val !== null)
|
|
||||||
{
|
|
||||||
widget.set_value(val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Children need to be loaded
|
|
||||||
widget.loadFromXML(_template_node);
|
|
||||||
|
|
||||||
return widget;
|
|
||||||
}
|
|
21
api/js/etemplate/et2-button.ts
Normal file
21
api/js/etemplate/et2-button.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* EGroupware eTemplate2 - Button widget
|
||||||
|
*
|
||||||
|
* @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
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Commented out while we work on rollup
|
||||||
|
import {LitElement,html} from "https://cdn.skypack.dev/lit-element";
|
||||||
|
import {SlButton} from "https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@2.0.0-beta.44/dist/shoelace.js";
|
||||||
|
|
||||||
|
export class Et2Button extends SlButton
|
||||||
|
{
|
||||||
|
size='small';
|
||||||
|
}
|
||||||
|
customElements.define("et2-button",Et2Button);
|
||||||
|
|
||||||
|
*/
|
@ -12,10 +12,73 @@
|
|||||||
et2_core_common;
|
et2_core_common;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {egw, IegwAppLocal} from "../jsapi/egw_global";
|
import {egw} from "../jsapi/egw_global";
|
||||||
import {et2_checkType, et2_cloneObject, et2_no_init, et2_validateAttrib} from "./et2_core_common";
|
import {et2_checkType, et2_no_init, et2_validateAttrib} from "./et2_core_common";
|
||||||
import {et2_IDOMNode, et2_IInput, et2_IInputNode, et2_implements_registry} from "./et2_core_interfaces";
|
import {et2_implements_registry} from "./et2_core_interfaces";
|
||||||
|
|
||||||
|
// Needed for mixin
|
||||||
|
export function mix (superclass)
|
||||||
|
{
|
||||||
|
return new MixinBuilder(superclass);
|
||||||
|
}
|
||||||
|
export class MixinBuilder {
|
||||||
|
constructor(superclass) {
|
||||||
|
this.superclass = superclass;
|
||||||
|
}
|
||||||
|
|
||||||
|
with(...mixins) {
|
||||||
|
return mixins.reduce(this.applyMixins, this.superclass);
|
||||||
|
}
|
||||||
|
applyMixins(derivedConstructor: any, baseConstructor: any) {
|
||||||
|
Object.getOwnPropertyNames(baseConstructor.prototype)
|
||||||
|
.forEach(name => {
|
||||||
|
Object.defineProperty(derivedConstructor.prototype,
|
||||||
|
name,
|
||||||
|
Object.
|
||||||
|
getOwnPropertyDescriptor(
|
||||||
|
baseConstructor.prototype,
|
||||||
|
name
|
||||||
|
)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
copyProperties(target, source) {
|
||||||
|
for (let key of Reflect.ownKeys(source)) {
|
||||||
|
if (key !== "constructor" && key !== "prototype" && key !== "name") {
|
||||||
|
let desc = Object.getOwnPropertyDescriptor(source, key);
|
||||||
|
Object.defineProperty(target, key, desc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This one from Typescript docs
|
||||||
|
export function applyMixins(derivedCtor: any, constructors: any[]) {
|
||||||
|
constructors.forEach((baseCtor) => {
|
||||||
|
Object.getOwnPropertyNames(baseCtor.prototype).forEach((name) => {
|
||||||
|
Object.defineProperty(
|
||||||
|
derivedCtor.prototype,
|
||||||
|
name,
|
||||||
|
Object.getOwnPropertyDescriptor(baseCtor.prototype, name) ||
|
||||||
|
Object.create(null)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Experiments in using mixins to combine et2_widget & LitElement
|
||||||
|
Note that this "works", in that it mixes the code properly.
|
||||||
|
It does not work in that the resulting class does not work with et2's inheritance & class checking stuff
|
||||||
|
|
||||||
|
// This one to make TypeScript happy?
|
||||||
|
interface et2_textbox extends et2_textbox, LitElement {}
|
||||||
|
// This one to make the inheritance magic happen
|
||||||
|
applyMixins(et2_textbox, [et2_textbox,LitElement]);
|
||||||
|
// Make it a real WebComponent
|
||||||
|
customElements.define("et2-textbox",et2_textbox);
|
||||||
|
|
||||||
|
*/
|
||||||
export class ClassWithInterfaces
|
export class ClassWithInterfaces
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
@ -70,19 +133,13 @@ export class ClassWithAttributes extends ClassWithInterfaces
|
|||||||
getAttribute(_name)
|
getAttribute(_name)
|
||||||
{
|
{
|
||||||
if (typeof this.attributes[_name] != "undefined" &&
|
if (typeof this.attributes[_name] != "undefined" &&
|
||||||
!this.attributes[_name].ignore)
|
!this.attributes[_name].ignore) {
|
||||||
{
|
if (typeof this["get_" + _name] == "function") {
|
||||||
if (typeof this["get_" + _name] == "function")
|
|
||||||
{
|
|
||||||
return this["get_" + _name]();
|
return this["get_" + _name]();
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
return this[_name];
|
return this[_name];
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
egw.debug("error", this, "Attribute '" + _name + "' does not exist!");
|
egw.debug("error", this, "Attribute '" + _name + "' does not exist!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -99,30 +156,22 @@ export class ClassWithAttributes extends ClassWithInterfaces
|
|||||||
*/
|
*/
|
||||||
setAttribute(_name, _value, _override)
|
setAttribute(_name, _value, _override)
|
||||||
{
|
{
|
||||||
if (typeof this.attributes[_name] != "undefined")
|
if (typeof this.attributes[_name] != "undefined") {
|
||||||
{
|
if (!this.attributes[_name].ignore) {
|
||||||
if (!this.attributes[_name].ignore)
|
if (typeof _override == "undefined") {
|
||||||
{
|
|
||||||
if (typeof _override == "undefined")
|
|
||||||
{
|
|
||||||
_override = true;
|
_override = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
var val = et2_checkType(_value, this.attributes[_name].type,
|
var val = et2_checkType(_value, this.attributes[_name].type,
|
||||||
_name, this);
|
_name, this);
|
||||||
|
|
||||||
if (typeof this["set_" + _name] == "function")
|
if (typeof this["set_" + _name] == "function") {
|
||||||
{
|
|
||||||
this["set_" + _name](val);
|
this["set_" + _name](val);
|
||||||
}
|
} else if (_override || typeof this[_name] == "undefined") {
|
||||||
else if (_override || typeof this[_name] == "undefined")
|
|
||||||
{
|
|
||||||
this[_name] = val;
|
this[_name] = val;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
egw.debug("warn", this, "Attribute '" + _name + "' does not exist!");
|
egw.debug("warn", this, "Attribute '" + _name + "' does not exist!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -137,18 +186,13 @@ export class ClassWithAttributes extends ClassWithInterfaces
|
|||||||
static generateAttributeSet(widget, _attrs)
|
static generateAttributeSet(widget, _attrs)
|
||||||
{
|
{
|
||||||
// Sanity check and validation
|
// Sanity check and validation
|
||||||
for (var key in _attrs)
|
for (var key in _attrs) {
|
||||||
{
|
if (typeof widget[key] != "undefined") {
|
||||||
if (typeof widget[key] != "undefined")
|
if (!widget[key].ignore) {
|
||||||
{
|
|
||||||
if (!widget[key].ignore)
|
|
||||||
{
|
|
||||||
_attrs[key] = et2_checkType(_attrs[key], widget[key].type,
|
_attrs[key] = et2_checkType(_attrs[key], widget[key].type,
|
||||||
key, this);
|
key, this);
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
// Key does not exist - delete it and issue a warning
|
// Key does not exist - delete it and issue a warning
|
||||||
delete (_attrs[key]);
|
delete (_attrs[key]);
|
||||||
egw.debug("warn", this, "Attribute '" + key +
|
egw.debug("warn", this, "Attribute '" + key +
|
||||||
@ -157,13 +201,10 @@ export class ClassWithAttributes extends ClassWithInterfaces
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Include default values or already set values for this attribute
|
// Include default values or already set values for this attribute
|
||||||
for (var key in widget)
|
for (var key in widget) {
|
||||||
{
|
if (typeof _attrs[key] == "undefined") {
|
||||||
if (typeof _attrs[key] == "undefined")
|
|
||||||
{
|
|
||||||
var _default = widget[key]["default"];
|
var _default = widget[key]["default"];
|
||||||
if (_default == et2_no_init)
|
if (_default == et2_no_init) {
|
||||||
{
|
|
||||||
_default = undefined;
|
_default = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,10 +225,8 @@ export class ClassWithAttributes extends ClassWithInterfaces
|
|||||||
*/
|
*/
|
||||||
initAttributes(_attrs)
|
initAttributes(_attrs)
|
||||||
{
|
{
|
||||||
for (var key in _attrs)
|
for (var key in _attrs) {
|
||||||
{
|
if (typeof this.attributes[key] != "undefined" && !this.attributes[key].ignore && !(_attrs[key] == undefined)) {
|
||||||
if (typeof this.attributes[key] != "undefined" && !this.attributes[key].ignore && !(_attrs[key] == undefined))
|
|
||||||
{
|
|
||||||
this.setAttribute(key, _attrs[key], false);
|
this.setAttribute(key, _attrs[key], false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -198,16 +237,13 @@ export class ClassWithAttributes extends ClassWithInterfaces
|
|||||||
let class_tree = [];
|
let class_tree = [];
|
||||||
let attributes = {};
|
let attributes = {};
|
||||||
let n = 0;
|
let n = 0;
|
||||||
do
|
do {
|
||||||
{
|
|
||||||
n++;
|
n++;
|
||||||
class_tree.push(class_prototype);
|
class_tree.push(class_prototype);
|
||||||
class_prototype = Object.getPrototypeOf(class_prototype);
|
class_prototype = Object.getPrototypeOf(class_prototype);
|
||||||
}
|
} while (class_prototype !== ClassWithAttributes && n < 50);
|
||||||
while (class_prototype !== ClassWithAttributes && n < 50);
|
|
||||||
|
|
||||||
for (let i = class_tree.length - 1; i >= 0; i--)
|
for (let i = class_tree.length - 1; i >= 0; i--) {
|
||||||
{
|
|
||||||
attributes = ClassWithAttributes.extendAttributes(attributes, class_tree[i]._attributes);
|
attributes = ClassWithAttributes.extendAttributes(attributes, class_tree[i]._attributes);
|
||||||
}
|
}
|
||||||
return attributes;
|
return attributes;
|
||||||
@ -228,19 +264,15 @@ export class ClassWithAttributes extends ClassWithInterfaces
|
|||||||
var result = {};
|
var result = {};
|
||||||
|
|
||||||
// Copy the new object
|
// Copy the new object
|
||||||
if (typeof _new != "undefined")
|
if (typeof _new != "undefined") {
|
||||||
{
|
for (var key in _new) {
|
||||||
for (var key in _new)
|
|
||||||
{
|
|
||||||
result[key] = _new[key];
|
result[key] = _new[key];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Merge the old object
|
// Merge the old object
|
||||||
for (var key in _old)
|
for (var key in _old) {
|
||||||
{
|
if (typeof result[key] == "undefined") {
|
||||||
if (typeof result[key] == "undefined")
|
|
||||||
{
|
|
||||||
result[key] = _old[key];
|
result[key] = _old[key];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -251,27 +283,23 @@ export class ClassWithAttributes extends ClassWithInterfaces
|
|||||||
var attributes = {};
|
var attributes = {};
|
||||||
|
|
||||||
// Copy the old attributes
|
// Copy the old attributes
|
||||||
for (var key in _attributes)
|
for (var key in _attributes) {
|
||||||
{
|
|
||||||
attributes[key] = _copyMerge({}, _attributes[key]);
|
attributes[key] = _copyMerge({}, _attributes[key]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the old attributes to the new ones. If the attributes already
|
// Add the old attributes to the new ones. If the attributes already
|
||||||
// exist, they are merged.
|
// exist, they are merged.
|
||||||
for (var key in _parent)
|
for (var key in _parent) {
|
||||||
{
|
|
||||||
var _old = _parent[key];
|
var _old = _parent[key];
|
||||||
|
|
||||||
attributes[key] = _copyMerge(attributes[key], _old);
|
attributes[key] = _copyMerge(attributes[key], _old);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate the attributes
|
// Validate the attributes
|
||||||
for (var key in attributes)
|
for (var key in attributes) {
|
||||||
{
|
|
||||||
et2_validateAttrib(key, attributes[key]);
|
et2_validateAttrib(key, attributes[key]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return attributes;
|
return attributes;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,16 +18,14 @@ import {et2_no_init} from "./et2_core_common";
|
|||||||
import { ClassWithAttributes } from "./et2_core_inheritance";
|
import { ClassWithAttributes } from "./et2_core_inheritance";
|
||||||
import { et2_widget, WidgetConfig } from "./et2_core_widget";
|
import { et2_widget, WidgetConfig } from "./et2_core_widget";
|
||||||
import { et2_valueWidget } from './et2_core_valueWidget'
|
import { et2_valueWidget } from './et2_core_valueWidget'
|
||||||
import {et2_IInput, et2_IInputNode, et2_ISubmitListener} from "./et2_core_interfaces";
|
import {et2_IInput, et2_ISubmitListener} from "./et2_core_interfaces";
|
||||||
import {et2_compileLegacyJS} from "./et2_core_legacyJSFunctions";
|
import {et2_compileLegacyJS} from "./et2_core_legacyJSFunctions";
|
||||||
// fixing circular dependencies by only importing the type (not in compiled .js)
|
// fixing circular dependencies by only importing the type (not in compiled .js)
|
||||||
import type {et2_tabbox} from "./et2_widget_tabs";
|
import type {et2_tabbox} from "./et2_widget_tabs";
|
||||||
|
|
||||||
export interface et2_input
|
export interface et2_input {
|
||||||
{
|
|
||||||
getInputNode() : HTMLInputElement|HTMLElement;
|
getInputNode() : HTMLInputElement|HTMLElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* et2_inputWidget derrives from et2_simpleWidget and implements the IInput
|
* et2_inputWidget derrives from et2_simpleWidget and implements the IInput
|
||||||
* interface. When derriving from this class, call setDOMNode with an input
|
* interface. When derriving from this class, call setDOMNode with an input
|
||||||
@ -143,12 +141,10 @@ export class et2_inputWidget extends et2_valueWidget implements et2_IInput, et2_
|
|||||||
{
|
{
|
||||||
jQuery(node)
|
jQuery(node)
|
||||||
.off('.et2_inputWidget')
|
.off('.et2_inputWidget')
|
||||||
.bind("change.et2_inputWidget", this, function (e)
|
.bind("change.et2_inputWidget", this, function(e) {
|
||||||
{
|
|
||||||
e.data.change.call(e.data, this);
|
e.data.change.call(e.data, this);
|
||||||
})
|
})
|
||||||
.bind("focus.et2_inputWidget", this, function (e)
|
.bind("focus.et2_inputWidget", this, function(e) {
|
||||||
{
|
|
||||||
e.data.focus.call(e.data, this);
|
e.data.focus.call(e.data, this);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -184,9 +180,7 @@ export class et2_inputWidget extends et2_valueWidget implements et2_IInput, et2_
|
|||||||
if(args.indexOf(this) == -1) args.push(this);
|
if(args.indexOf(this) == -1) args.push(this);
|
||||||
|
|
||||||
return this.onchange.apply(this, args);
|
return this.onchange.apply(this, args);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
return (et2_compileLegacyJS(this.options.onchange, this, _node))();
|
return (et2_compileLegacyJS(this.options.onchange, this, _node))();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -255,12 +249,9 @@ export class et2_inputWidget extends et2_valueWidget implements et2_IInput, et2_
|
|||||||
var node = this.getInputNode();
|
var node = this.getInputNode();
|
||||||
if (node)
|
if (node)
|
||||||
{
|
{
|
||||||
if (_value && !this.options.readonly)
|
if(_value && !this.options.readonly) {
|
||||||
{
|
|
||||||
jQuery(node).attr("required", "required");
|
jQuery(node).attr("required", "required");
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
node.removeAttribute("required");
|
node.removeAttribute("required");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -391,3 +382,4 @@ export class et2_inputWidget extends et2_valueWidget implements et2_IInput, et2_
|
|||||||
return valid;
|
return valid;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,10 +22,10 @@ import {egw, IegwAppLocal} from "../jsapi/egw_global";
|
|||||||
import {et2_cloneObject, et2_csvSplit} from "./et2_core_common";
|
import {et2_cloneObject, et2_csvSplit} from "./et2_core_common";
|
||||||
import {et2_compileLegacyJS} from "./et2_core_legacyJSFunctions";
|
import {et2_compileLegacyJS} from "./et2_core_legacyJSFunctions";
|
||||||
import {et2_IDOMNode, et2_IInputNode} from "./et2_core_interfaces";
|
import {et2_IDOMNode, et2_IInputNode} from "./et2_core_interfaces";
|
||||||
import {loadWebComponent} from "./Et2Widget/Et2Widget";
|
|
||||||
// fixing circular dependencies by only importing type
|
// fixing circular dependencies by only importing type
|
||||||
import type {et2_container} from "./et2_core_baseWidget";
|
import type {et2_container} from "./et2_core_baseWidget";
|
||||||
import type {et2_inputWidget} from "./et2_core_inputWidget";
|
import type {et2_inputWidget, et2_input} from "./et2_core_inputWidget";
|
||||||
|
import {decorateLanguageService} from "ts-lit-plugin/lib/decorate-language-service";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The registry contains all XML tag names and the corresponding widget
|
* The registry contains all XML tag names and the corresponding widget
|
||||||
@ -119,12 +119,10 @@ export function et2_createWidget(_name: string, _attrs: object, _parent?: any):
|
|||||||
// make et2_createWidget publicly available as we need to call it from stylite/js/gantt.js (maybe others)
|
// make et2_createWidget publicly available as we need to call it from stylite/js/gantt.js (maybe others)
|
||||||
if (typeof window.et2_createWidget === 'undefined') window['et2_createWidget'] = et2_createWidget;
|
if (typeof window.et2_createWidget === 'undefined') window['et2_createWidget'] = et2_createWidget;
|
||||||
|
|
||||||
export interface WidgetConfig
|
export interface WidgetConfig {
|
||||||
{
|
|
||||||
type?: string;
|
type?: string;
|
||||||
readonly?: boolean;
|
readonly?: boolean;
|
||||||
width?: number;
|
width?: number;
|
||||||
|
|
||||||
[propName: string]: any;
|
[propName: string]: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -216,18 +214,15 @@ export class et2_widget extends ClassWithAttributes
|
|||||||
this.attributes = ClassWithAttributes.extendAttributes(et2_widget._attributes, _child || {});
|
this.attributes = ClassWithAttributes.extendAttributes(et2_widget._attributes, _child || {});
|
||||||
|
|
||||||
// Check whether all attributes are available
|
// Check whether all attributes are available
|
||||||
if (typeof _parent == "undefined")
|
if (typeof _parent == "undefined") {
|
||||||
{
|
|
||||||
_parent = null;
|
_parent = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof _attrs == "undefined")
|
if (typeof _attrs == "undefined") {
|
||||||
{
|
|
||||||
_attrs = {};
|
_attrs = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_attrs.attributes)
|
if (_attrs.attributes) {
|
||||||
{
|
|
||||||
jQuery.extend(_attrs, _attrs.attributes);
|
jQuery.extend(_attrs, _attrs.attributes);
|
||||||
}
|
}
|
||||||
// Initialize all important parameters
|
// Initialize all important parameters
|
||||||
@ -238,26 +233,22 @@ export class et2_widget extends ClassWithAttributes
|
|||||||
this.id = _attrs["id"];
|
this.id = _attrs["id"];
|
||||||
|
|
||||||
// Add this widget to the given parent widget
|
// Add this widget to the given parent widget
|
||||||
if (_parent != null)
|
if (_parent != null) {
|
||||||
{
|
|
||||||
_parent.addChild(this);
|
_parent.addChild(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
// The supported widget classes array defines a whitelist for all widget
|
// The supported widget classes array defines a whitelist for all widget
|
||||||
// classes or interfaces child widgets have to support.
|
// classes or interfaces child widgets have to support.
|
||||||
this.supportedWidgetClasses = [et2_widget, HTMLElement];
|
this.supportedWidgetClasses = [et2_widget];
|
||||||
|
|
||||||
if (_attrs["id"])
|
if (_attrs["id"]) {
|
||||||
{
|
|
||||||
// Create a namespace for this object
|
// Create a namespace for this object
|
||||||
if (this._createNamespace())
|
if (this._createNamespace()) {
|
||||||
{
|
|
||||||
this.checkCreateNamespace(_attrs);
|
this.checkCreateNamespace(_attrs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.id)
|
if (this.id) {
|
||||||
{
|
|
||||||
//this.id = this.id.replace(/\[/g,'[').replace(/]/g,']');
|
//this.id = this.id.replace(/\[/g,'[').replace(/]/g,']');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -280,22 +271,18 @@ export class et2_widget extends ClassWithAttributes
|
|||||||
destroy()
|
destroy()
|
||||||
{
|
{
|
||||||
// Call the destructor of all children
|
// Call the destructor of all children
|
||||||
for (var i = this._children.length - 1; i >= 0; i--)
|
for (var i = this._children.length - 1; i >= 0; i--) {
|
||||||
{
|
|
||||||
this._children[i].destroy();
|
this._children[i].destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove this element from the parent, if it exists
|
// Remove this element from the parent, if it exists
|
||||||
if (typeof this._parent != "undefined" && this._parent !== null)
|
if (typeof this._parent != "undefined" && this._parent !== null) {
|
||||||
{
|
|
||||||
this._parent.removeChild(this);
|
this._parent.removeChild(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Free the array managers if they belong to this widget
|
// Free the array managers if they belong to this widget
|
||||||
for (var key in this._mgrs)
|
for (var key in this._mgrs) {
|
||||||
{
|
if (this._mgrs[key] && this._mgrs[key].owner == this) {
|
||||||
if (this._mgrs[key] && this._mgrs[key].owner == this)
|
|
||||||
{
|
|
||||||
this._mgrs[key].destroy();
|
this._mgrs[key].destroy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -321,8 +308,7 @@ export class et2_widget extends ClassWithAttributes
|
|||||||
clone(_parent)
|
clone(_parent)
|
||||||
{
|
{
|
||||||
// Default _parent to null
|
// Default _parent to null
|
||||||
if (typeof _parent == "undefined")
|
if (typeof _parent == "undefined") {
|
||||||
{
|
|
||||||
_parent = null;
|
_parent = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -337,14 +323,12 @@ export class et2_widget extends ClassWithAttributes
|
|||||||
|
|
||||||
assign(_obj)
|
assign(_obj)
|
||||||
{
|
{
|
||||||
if (typeof _obj._children == "undefined")
|
if (typeof _obj._children == "undefined") {
|
||||||
{
|
|
||||||
this.egw().debug("log", "Foo!");
|
this.egw().debug("log", "Foo!");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a clone of all child elements of the given object
|
// Create a clone of all child elements of the given object
|
||||||
for (var i = 0; i < _obj._children.length; i++)
|
for (var i = 0; i < _obj._children.length; i++) {
|
||||||
{
|
|
||||||
_obj._children[i].clone(this);
|
_obj._children[i].clone(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -377,12 +361,9 @@ export class et2_widget extends ClassWithAttributes
|
|||||||
*/
|
*/
|
||||||
getRoot() : et2_container
|
getRoot() : et2_container
|
||||||
{
|
{
|
||||||
if (this._parent != null)
|
if (this._parent != null) {
|
||||||
{
|
|
||||||
return this._parent.getRoot();
|
return this._parent.getRoot();
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
return <et2_container><unknown>this;
|
return <et2_container><unknown>this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -408,19 +389,15 @@ export class et2_widget extends ClassWithAttributes
|
|||||||
insertChild(_node : et2_widget, _idx: number)
|
insertChild(_node : et2_widget, _idx: number)
|
||||||
{
|
{
|
||||||
// Check whether the node is one of the supported widget classes.
|
// Check whether the node is one of the supported widget classes.
|
||||||
if (this.isOfSupportedWidgetClass(_node))
|
if (this.isOfSupportedWidgetClass(_node)) {
|
||||||
{
|
|
||||||
// Remove the node from its original parent
|
// Remove the node from its original parent
|
||||||
if (_node._parent)
|
if (_node._parent) {
|
||||||
{
|
|
||||||
_node._parent.removeChild(_node);
|
_node._parent.removeChild(_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
_node._parent = this;
|
_node._parent = this;
|
||||||
this._children.splice(_idx, 0, _node);
|
this._children.splice(_idx, 0, _node);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
this.egw().debug("error", "Widget " + _node._type + " is not supported by this widget class", this);
|
this.egw().debug("error", "Widget " + _node._type + " is not supported by this widget class", this);
|
||||||
// throw("Widget is not supported by this widget class!");
|
// throw("Widget is not supported by this widget class!");
|
||||||
}
|
}
|
||||||
@ -436,8 +413,7 @@ export class et2_widget extends ClassWithAttributes
|
|||||||
// Retrieve the child from the child list
|
// Retrieve the child from the child list
|
||||||
var idx = this._children.indexOf(_node);
|
var idx = this._children.indexOf(_node);
|
||||||
|
|
||||||
if (idx >= 0)
|
if (idx >= 0) {
|
||||||
{
|
|
||||||
// This element is no longer parent of the child
|
// This element is no longer parent of the child
|
||||||
_node._parent = null;
|
_node._parent = null;
|
||||||
|
|
||||||
@ -452,27 +428,22 @@ export class et2_widget extends ClassWithAttributes
|
|||||||
*/
|
*/
|
||||||
getWidgetById(_id) : et2_widget | null
|
getWidgetById(_id) : et2_widget | null
|
||||||
{
|
{
|
||||||
if (this.id == _id)
|
if (this.id == _id) {
|
||||||
{
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
if (!this._children) return null;
|
if (!this._children) return null;
|
||||||
|
|
||||||
for (var i = 0; i < this._children.length; i++)
|
for (var i = 0; i < this._children.length; i++) {
|
||||||
{
|
|
||||||
var elem = this._children[i].getWidgetById(_id);
|
var elem = this._children[i].getWidgetById(_id);
|
||||||
|
|
||||||
if (elem != null)
|
if (elem != null) {
|
||||||
{
|
|
||||||
return elem;
|
return elem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this.id && _id.indexOf('[') > -1 && this._children.length)
|
if (this.id && _id.indexOf('[') > -1 && this._children.length) {
|
||||||
{
|
|
||||||
var ids = (new et2_arrayMgr()).explodeKey(_id);
|
var ids = (new et2_arrayMgr()).explodeKey(_id);
|
||||||
var widget : et2_widget = this;
|
var widget : et2_widget = this;
|
||||||
for (var i = 0; i < ids.length && widget !== null; i++)
|
for (var i = 0; i < ids.length && widget !== null; i++) {
|
||||||
{
|
|
||||||
widget = widget.getWidgetById(ids[i]);
|
widget = widget.getWidgetById(ids[i]);
|
||||||
}
|
}
|
||||||
return widget;
|
return widget;
|
||||||
@ -491,18 +462,15 @@ export class et2_widget extends ClassWithAttributes
|
|||||||
*/
|
*/
|
||||||
iterateOver(_callback, _context, _type?)
|
iterateOver(_callback, _context, _type?)
|
||||||
{
|
{
|
||||||
if (typeof _type == "undefined")
|
if (typeof _type == "undefined") {
|
||||||
{
|
|
||||||
_type = et2_widget;
|
_type = et2_widget;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.isInTree() && this.instanceOf(_type))
|
if (this.isInTree() && this.instanceOf(_type)) {
|
||||||
{
|
|
||||||
_callback.call(_context, this);
|
_callback.call(_context, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i = 0; i < this._children.length; i++)
|
for (var i = 0; i < this._children.length; i++) {
|
||||||
{
|
|
||||||
this._children[i].iterateOver(_callback, _context, _type);
|
this._children[i].iterateOver(_callback, _context, _type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -520,13 +488,11 @@ export class et2_widget extends ClassWithAttributes
|
|||||||
*/
|
*/
|
||||||
isInTree(_sender?, _vis? : boolean)
|
isInTree(_sender?, _vis? : boolean)
|
||||||
{
|
{
|
||||||
if (typeof _vis == "undefined")
|
if (typeof _vis == "undefined") {
|
||||||
{
|
|
||||||
_vis = true;
|
_vis = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._parent)
|
if (this._parent) {
|
||||||
{
|
|
||||||
return _vis && this._parent.isInTree(this);
|
return _vis && this._parent.isInTree(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -535,10 +501,8 @@ export class et2_widget extends ClassWithAttributes
|
|||||||
|
|
||||||
isOfSupportedWidgetClass(_obj)
|
isOfSupportedWidgetClass(_obj)
|
||||||
{
|
{
|
||||||
for (var i = 0; i < this.supportedWidgetClasses.length; i++)
|
for (var i = 0; i < this.supportedWidgetClasses.length; i++) {
|
||||||
{
|
if (_obj instanceof this.supportedWidgetClasses[i]) {
|
||||||
if (_obj instanceof this.supportedWidgetClasses[i])
|
|
||||||
{
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -557,55 +521,47 @@ export class et2_widget extends ClassWithAttributes
|
|||||||
parseXMLAttrs(_attrsObj, _target, _proto)
|
parseXMLAttrs(_attrsObj, _target, _proto)
|
||||||
{
|
{
|
||||||
// Check whether the attributes object is really existing, if not abort
|
// Check whether the attributes object is really existing, if not abort
|
||||||
if (typeof _attrsObj == "undefined")
|
if (typeof _attrsObj == "undefined") {
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Iterate over the given attributes and parse them
|
// Iterate over the given attributes and parse them
|
||||||
var mgr = this.getArrayMgr("content");
|
var mgr = this.getArrayMgr("content");
|
||||||
for (var i = 0; i < _attrsObj.length; i++)
|
for (var i = 0; i < _attrsObj.length; i++) {
|
||||||
{
|
|
||||||
var attrName = _attrsObj[i].name;
|
var attrName = _attrsObj[i].name;
|
||||||
var attrValue = _attrsObj[i].value;
|
var attrValue = _attrsObj[i].value;
|
||||||
|
|
||||||
// Special handling for the legacy options
|
// Special handling for the legacy options
|
||||||
if (attrName == "options" && _proto.constructor.legacyOptions && _proto.constructor.legacyOptions.length > 0)
|
if (attrName == "options" && _proto.constructor.legacyOptions && _proto.constructor.legacyOptions.length > 0) {
|
||||||
{
|
|
||||||
let legacy = _proto.constructor.legacyOptions || [];
|
let legacy = _proto.constructor.legacyOptions || [];
|
||||||
let attrs = et2_attribute_registry[Object.getPrototypeOf(_proto).constructor.name] || {};
|
let attrs = et2_attribute_registry[Object.getPrototypeOf(_proto).constructor.name] || {};
|
||||||
// Check for modifications on legacy options here. Normal modifications
|
// Check for modifications on legacy options here. Normal modifications
|
||||||
// are handled in widget constructor, but it's too late for legacy options then
|
// are handled in widget constructor, but it's too late for legacy options then
|
||||||
if (_target.id && this.getArrayMgr("modifications").getEntry(_target.id))
|
if (_target.id && this.getArrayMgr("modifications").getEntry(_target.id)) {
|
||||||
{
|
|
||||||
var mod : any = this.getArrayMgr("modifications").getEntry(_target.id);
|
var mod : any = this.getArrayMgr("modifications").getEntry(_target.id);
|
||||||
if (typeof mod.options != "undefined") attrValue = _attrsObj[i].value = mod.options;
|
if (typeof mod.options != "undefined") attrValue = _attrsObj[i].value = mod.options;
|
||||||
}
|
}
|
||||||
// expand legacyOptions with content
|
// expand legacyOptions with content
|
||||||
if (attrValue.charAt(0) == '@' || attrValue.indexOf('$') != -1)
|
if (attrValue.charAt(0) == '@' || attrValue.indexOf('$') != -1) {
|
||||||
{
|
|
||||||
attrValue = mgr.expandName(attrValue);
|
attrValue = mgr.expandName(attrValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse the legacy options (as a string, other types not allowed)
|
// Parse the legacy options (as a string, other types not allowed)
|
||||||
var splitted = et2_csvSplit(attrValue + "");
|
var splitted = et2_csvSplit(attrValue + "");
|
||||||
|
|
||||||
for (var j = 0; j < splitted.length && j < legacy.length; j++)
|
for (var j = 0; j < splitted.length && j < legacy.length; j++) {
|
||||||
{
|
|
||||||
// Blank = not set, unless there's more legacy options provided after
|
// Blank = not set, unless there's more legacy options provided after
|
||||||
if (splitted[j].trim().length === 0 && legacy.length >= splitted.length) continue;
|
if (splitted[j].trim().length === 0 && legacy.length >= splitted.length) continue;
|
||||||
|
|
||||||
// Check to make sure we don't overwrite a current option with a legacy option
|
// Check to make sure we don't overwrite a current option with a legacy option
|
||||||
if (typeof _target[legacy[j]] === "undefined")
|
if (typeof _target[legacy[j]] === "undefined") {
|
||||||
{
|
|
||||||
attrValue = splitted[j];
|
attrValue = splitted[j];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
If more legacy options than expected, stuff them all in the last legacy option
|
If more legacy options than expected, stuff them all in the last legacy option
|
||||||
Some legacy options take a comma separated list.
|
Some legacy options take a comma separated list.
|
||||||
*/
|
*/
|
||||||
if (j == legacy.length - 1 && splitted.length > legacy.length)
|
if (j == legacy.length - 1 && splitted.length > legacy.length) {
|
||||||
{
|
|
||||||
attrValue = splitted.slice(j);
|
attrValue = splitted.slice(j);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -613,37 +569,26 @@ export class et2_widget extends ClassWithAttributes
|
|||||||
|
|
||||||
// If the attribute is marked as boolean, parse the
|
// If the attribute is marked as boolean, parse the
|
||||||
// expression as bool expression.
|
// expression as bool expression.
|
||||||
if (attr.type == "boolean")
|
if (attr.type == "boolean") {
|
||||||
{
|
|
||||||
attrValue = mgr.parseBoolExpression(attrValue);
|
attrValue = mgr.parseBoolExpression(attrValue);
|
||||||
}
|
} else if (typeof attrValue != "object") {
|
||||||
else if (typeof attrValue != "object")
|
|
||||||
{
|
|
||||||
attrValue = mgr.expandName(attrValue);
|
attrValue = mgr.expandName(attrValue);
|
||||||
}
|
}
|
||||||
_target[legacy[j]] = attrValue;
|
_target[legacy[j]] = attrValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else if (attrName == "readonly" && typeof _target[attrName] != "undefined") {
|
||||||
else if (attrName == "readonly" && typeof _target[attrName] != "undefined")
|
|
||||||
{
|
|
||||||
// do NOT overwrite already evaluated readonly attribute
|
// do NOT overwrite already evaluated readonly attribute
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
let attrs = et2_attribute_registry[_proto.constructor.name] || {};
|
let attrs = et2_attribute_registry[_proto.constructor.name] || {};
|
||||||
if (mgr != null && typeof attrs[attrName] != "undefined")
|
if (mgr != null && typeof attrs[attrName] != "undefined") {
|
||||||
{
|
|
||||||
var attr = attrs[attrName];
|
var attr = attrs[attrName];
|
||||||
|
|
||||||
// If the attribute is marked as boolean, parse the
|
// If the attribute is marked as boolean, parse the
|
||||||
// expression as bool expression.
|
// expression as bool expression.
|
||||||
if (attr.type == "boolean")
|
if (attr.type == "boolean") {
|
||||||
{
|
|
||||||
attrValue = mgr.parseBoolExpression(attrValue);
|
attrValue = mgr.parseBoolExpression(attrValue);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
attrValue = mgr.expandName(attrValue);
|
attrValue = mgr.expandName(attrValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -664,26 +609,20 @@ export class et2_widget extends ClassWithAttributes
|
|||||||
{
|
{
|
||||||
|
|
||||||
// Apply the content of the modifications array
|
// Apply the content of the modifications array
|
||||||
if (this.id)
|
if (this.id) {
|
||||||
{
|
if (typeof this.id != "string") {
|
||||||
if (typeof this.id != "string")
|
|
||||||
{
|
|
||||||
console.log(this.id);
|
console.log(this.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.getArrayMgr("modifications"))
|
if (this.getArrayMgr("modifications")) {
|
||||||
{
|
|
||||||
var data = this.getArrayMgr("modifications").getEntry(this.id);
|
var data = this.getArrayMgr("modifications").getEntry(this.id);
|
||||||
|
|
||||||
// Check for already inside namespace
|
// Check for already inside namespace
|
||||||
if (this._createNamespace() && this.getArrayMgr("modifications").perspectiveData.owner == this)
|
if (this._createNamespace() && this.getArrayMgr("modifications").perspectiveData.owner == this) {
|
||||||
{
|
|
||||||
data = this.getArrayMgr("modifications").data;
|
data = this.getArrayMgr("modifications").data;
|
||||||
}
|
}
|
||||||
if (typeof data === 'object')
|
if (typeof data === 'object') {
|
||||||
{
|
for (var key in data) {
|
||||||
for (var key in data)
|
|
||||||
{
|
|
||||||
_attrs[key] = data[key];
|
_attrs[key] = data[key];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -691,13 +630,10 @@ export class et2_widget extends ClassWithAttributes
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Translate the attributes
|
// Translate the attributes
|
||||||
for (var key in _attrs)
|
for (var key in _attrs) {
|
||||||
{
|
if (_attrs[key] && typeof this.attributes[key] != "undefined") {
|
||||||
if (_attrs[key] && typeof this.attributes[key] != "undefined")
|
|
||||||
{
|
|
||||||
if (this.attributes[key].translate === true ||
|
if (this.attributes[key].translate === true ||
|
||||||
(this.attributes[key].translate === "!no_lang" && !_attrs["no_lang"]))
|
(this.attributes[key].translate === "!no_lang" && !_attrs["no_lang"])) {
|
||||||
{
|
|
||||||
let value = _attrs[key];
|
let value = _attrs[key];
|
||||||
// allow statustext to contain multiple translated sub-strings eg: {Firstname}.{Lastname}
|
// allow statustext to contain multiple translated sub-strings eg: {Firstname}.{Lastname}
|
||||||
if (value.indexOf('{') !== -1)
|
if (value.indexOf('{') !== -1)
|
||||||
@ -746,26 +682,20 @@ export class et2_widget extends ClassWithAttributes
|
|||||||
|
|
||||||
// Check to see if modifications change type
|
// Check to see if modifications change type
|
||||||
var modifications = this.getArrayMgr("modifications");
|
var modifications = this.getArrayMgr("modifications");
|
||||||
if (modifications && _node.getAttribute("id"))
|
if (modifications && _node.getAttribute("id")) {
|
||||||
{
|
|
||||||
var entry : any = modifications.getEntry(_node.getAttribute("id"));
|
var entry : any = modifications.getEntry(_node.getAttribute("id"));
|
||||||
if (entry == null)
|
if (entry == null) {
|
||||||
{
|
|
||||||
// Try again, but skip the fancy stuff
|
// Try again, but skip the fancy stuff
|
||||||
// TODO: Figure out why the getEntry() call doesn't always work
|
// TODO: Figure out why the getEntry() call doesn't always work
|
||||||
var entry = modifications.data[_node.getAttribute("id")];
|
var entry = modifications.data[_node.getAttribute("id")];
|
||||||
if (entry)
|
if (entry) {
|
||||||
{
|
|
||||||
this.egw().debug("warn", "getEntry(" + _node.getAttribute("id") + ") failed, but the data is there.", modifications, entry);
|
this.egw().debug("warn", "getEntry(" + _node.getAttribute("id") + ") failed, but the data is there.", modifications, entry);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
// Try the root, in case a namespace got missed
|
// Try the root, in case a namespace got missed
|
||||||
entry = modifications.getRoot().getEntry(_node.getAttribute("id"));
|
entry = modifications.getRoot().getEntry(_node.getAttribute("id"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (entry && entry.type && typeof entry.type === 'string')
|
if (entry && entry.type && typeof entry.type === 'string') {
|
||||||
{
|
|
||||||
_nodeName = attributes["type"] = entry.type;
|
_nodeName = attributes["type"] = entry.type;
|
||||||
}
|
}
|
||||||
entry = null;
|
entry = null;
|
||||||
@ -773,27 +703,25 @@ export class et2_widget extends ClassWithAttributes
|
|||||||
|
|
||||||
// if _nodeName / type-attribute contains something to expand (eg. type="@${row}[type]"),
|
// if _nodeName / type-attribute contains something to expand (eg. type="@${row}[type]"),
|
||||||
// we need to expand it now as it defines the constructor and by that attributes parsed via parseXMLAttrs!
|
// we need to expand it now as it defines the constructor and by that attributes parsed via parseXMLAttrs!
|
||||||
if (_nodeName.charAt(0) == '@' || _nodeName.indexOf('$') >= 0)
|
if (_nodeName.charAt(0) == '@' || _nodeName.indexOf('$') >= 0) {
|
||||||
{
|
|
||||||
_nodeName = attributes["type"] = this.getArrayMgr('content').expandName(_nodeName);
|
_nodeName = attributes["type"] = this.getArrayMgr('content').expandName(_nodeName);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the constructor - if the widget is readonly, use the special "_ro"
|
// Get the constructor - if the widget is readonly, use the special "_ro"
|
||||||
// constructor if it is available
|
// constructor if it is available
|
||||||
var constructor = et2_registry[typeof et2_registry[_nodeName] == "undefined" ? 'placeholder' : _nodeName];
|
var constructor = et2_registry[typeof et2_registry[_nodeName] == "undefined" ? 'placeholder' : _nodeName];
|
||||||
if (readonly === true && typeof et2_registry[_nodeName + "_ro"] != "undefined")
|
if (readonly === true && typeof et2_registry[_nodeName + "_ro"] != "undefined") {
|
||||||
{
|
|
||||||
constructor = et2_registry[_nodeName + "_ro"];
|
constructor = et2_registry[_nodeName + "_ro"];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (undefined == window.customElements.get(_nodeName))
|
|
||||||
{
|
|
||||||
// Parse the attributes from the given XML attributes object
|
// Parse the attributes from the given XML attributes object
|
||||||
this.parseXMLAttrs(_node.attributes, attributes, constructor.prototype);
|
this.parseXMLAttrs(_node.attributes, attributes, constructor.prototype);
|
||||||
|
|
||||||
// Do an sanity check for the attributes
|
// Do an sanity check for the attributes
|
||||||
ClassWithAttributes.generateAttributeSet(et2_attribute_registry[constructor.name], attributes);
|
ClassWithAttributes.generateAttributeSet(et2_attribute_registry[constructor.name], attributes);
|
||||||
|
|
||||||
|
if(undefined == window.customElements.get(_nodeName))
|
||||||
|
{
|
||||||
// Creates the new widget, passes this widget as an instance and
|
// Creates the new widget, passes this widget as an instance and
|
||||||
// passes the widgetType. Then it goes on loading the XML for it.
|
// passes the widgetType. Then it goes on loading the XML for it.
|
||||||
var widget = new constructor(this, attributes);
|
var widget = new constructor(this, attributes);
|
||||||
@ -803,7 +731,7 @@ export class et2_widget extends ClassWithAttributes
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
widget = loadWebComponent(_nodeName, _node, this);
|
widget = this.loadWebComponent(_nodeName, _node, attributes);
|
||||||
|
|
||||||
if(this.addChild)
|
if(this.addChild)
|
||||||
{
|
{
|
||||||
@ -814,6 +742,22 @@ export class et2_widget extends ClassWithAttributes
|
|||||||
return widget;
|
return widget;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load a Web Component
|
||||||
|
* @param _nodeName
|
||||||
|
* @param _node
|
||||||
|
*/
|
||||||
|
loadWebComponent(_nodeName : string, _node, attributes : Object) : HTMLElement
|
||||||
|
{
|
||||||
|
let widget = document.createElement(_nodeName);
|
||||||
|
widget.textContent = _node.textContent;
|
||||||
|
|
||||||
|
// Apply any set attributes
|
||||||
|
_node.getAttributeNames().forEach(attribute => widget.setAttribute(attribute, attributes[attribute]));
|
||||||
|
|
||||||
|
return widget;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads the widget tree from an XML node
|
* Loads the widget tree from an XML node
|
||||||
*
|
*
|
||||||
@ -822,20 +766,16 @@ export class et2_widget extends ClassWithAttributes
|
|||||||
loadFromXML(_node)
|
loadFromXML(_node)
|
||||||
{
|
{
|
||||||
// Load the child nodes.
|
// Load the child nodes.
|
||||||
for (var i = 0; i < _node.childNodes.length; i++)
|
for (var i = 0; i < _node.childNodes.length; i++) {
|
||||||
{
|
|
||||||
var node = _node.childNodes[i];
|
var node = _node.childNodes[i];
|
||||||
var widgetType = node.nodeName.toLowerCase();
|
var widgetType = node.nodeName.toLowerCase();
|
||||||
|
|
||||||
if (widgetType == "#comment")
|
if (widgetType == "#comment") {
|
||||||
{
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (widgetType == "#text")
|
if (widgetType == "#text") {
|
||||||
{
|
if (node.data.replace(/^\s+|\s+$/g, '')) {
|
||||||
if (node.data.replace(/^\s+|\s+$/g, ''))
|
|
||||||
{
|
|
||||||
this.loadContent(node.data);
|
this.loadContent(node.data);
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
@ -883,39 +823,29 @@ export class et2_widget extends ClassWithAttributes
|
|||||||
|
|
||||||
// Make sure promises is defined to avoid errors.
|
// Make sure promises is defined to avoid errors.
|
||||||
// We'll warn (below) if programmer should have passed it.
|
// We'll warn (below) if programmer should have passed it.
|
||||||
if (typeof promises == "undefined")
|
if (typeof promises == "undefined") {
|
||||||
{
|
|
||||||
promises = [];
|
promises = [];
|
||||||
var warn_if_deferred = true;
|
var warn_if_deferred = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
var loadChildren = function ()
|
var loadChildren = function () {
|
||||||
{
|
|
||||||
// Descend recursively into the tree
|
// Descend recursively into the tree
|
||||||
for (var i = 0; i < this._children.length; i++)
|
for (var i = 0; i < this._children.length; i++) {
|
||||||
{
|
try {
|
||||||
try
|
|
||||||
{
|
|
||||||
this._children[i].loadingFinished(promises);
|
this._children[i].loadingFinished(promises);
|
||||||
}
|
} catch (e) {
|
||||||
catch (e)
|
|
||||||
{
|
|
||||||
egw.debug("error", "There was an error with a widget:\nError:%o\nProblem widget:%o", e.valueOf(), this._children[i], e.stack);
|
egw.debug("error", "There was an error with a widget:\nError:%o\nProblem widget:%o", e.valueOf(), this._children[i], e.stack);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var result = this.doLoadingFinished();
|
var result = this.doLoadingFinished();
|
||||||
if (typeof result == "boolean" && result)
|
if (typeof result == "boolean" && result) {
|
||||||
{
|
|
||||||
// Simple widget finishes nicely
|
// Simple widget finishes nicely
|
||||||
loadChildren.apply(this, arguments);
|
loadChildren.apply(this, arguments);
|
||||||
}
|
} else if (typeof result == "object" && result.done) {
|
||||||
else if (typeof result == "object" && result.done)
|
|
||||||
{
|
|
||||||
// Warn if list was not provided
|
// Warn if list was not provided
|
||||||
if (warn_if_deferred)
|
if (warn_if_deferred) {
|
||||||
{
|
|
||||||
// Might not be a problem, but if you need the widget to be really loaded, it could be
|
// Might not be a problem, but if you need the widget to be really loaded, it could be
|
||||||
egw.debug("warn", "Loading was deferred for widget %o, but creator is not checking. Pass a list to loadingFinished().", this);
|
egw.debug("warn", "Loading was deferred for widget %o, but creator is not checking. Pass a list to loadingFinished().", this);
|
||||||
}
|
}
|
||||||
@ -938,14 +868,11 @@ export class et2_widget extends ClassWithAttributes
|
|||||||
*/
|
*/
|
||||||
initAttributes(_attrs)
|
initAttributes(_attrs)
|
||||||
{
|
{
|
||||||
for (var key in _attrs)
|
for (var key in _attrs) {
|
||||||
{
|
if (typeof this.attributes[key] != "undefined" && !this.attributes[key].ignore && !(_attrs[key] == undefined)) {
|
||||||
if (typeof this.attributes[key] != "undefined" && !this.attributes[key].ignore && !(_attrs[key] == undefined))
|
|
||||||
{
|
|
||||||
var val = _attrs[key];
|
var val = _attrs[key];
|
||||||
// compile string values of attribute type "js" to functions
|
// compile string values of attribute type "js" to functions
|
||||||
if (this.attributes[key].type == 'js' && typeof _attrs[key] == 'string')
|
if (this.attributes[key].type == 'js' && typeof _attrs[key] == 'string') {
|
||||||
{
|
|
||||||
val = et2_compileLegacyJS(val, this,
|
val = et2_compileLegacyJS(val, this,
|
||||||
this.implements(et2_IInputNode) ? (<et2_inputWidget><unknown>this).getInputNode() :
|
this.implements(et2_IInputNode) ? (<et2_inputWidget><unknown>this).getInputNode() :
|
||||||
(this.implements(et2_IDOMNode) ? (<et2_IDOMNode><unknown>this).getDOMNode() : null));
|
(this.implements(et2_IDOMNode) ? (<et2_IDOMNode><unknown>this).getDOMNode() : null));
|
||||||
@ -979,20 +906,16 @@ export class et2_widget extends ClassWithAttributes
|
|||||||
egw() : IegwAppLocal
|
egw() : IegwAppLocal
|
||||||
{
|
{
|
||||||
// The _egw property is not set
|
// The _egw property is not set
|
||||||
if (typeof this._egw === 'undefined')
|
if (typeof this._egw === 'undefined') {
|
||||||
{
|
if (this._parent != null) {
|
||||||
if (this._parent != null)
|
|
||||||
{
|
|
||||||
return this._parent.egw();
|
return this._parent.egw();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the window this object belongs to
|
// Get the window this object belongs to
|
||||||
var wnd = null;
|
var wnd = null;
|
||||||
if (this.implements(et2_IDOMNode))
|
if (this.implements(et2_IDOMNode)) {
|
||||||
{
|
|
||||||
var node = (<et2_IDOMNode><unknown>this).getDOMNode();
|
var node = (<et2_IDOMNode><unknown>this).getDOMNode();
|
||||||
if (node && node.ownerDocument)
|
if (node && node.ownerDocument) {
|
||||||
{
|
|
||||||
wnd = node.ownerDocument.parentNode || node.ownerDocument.defaultView;
|
wnd = node.ownerDocument.parentNode || node.ownerDocument.defaultView;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1035,24 +958,20 @@ export class et2_widget extends ClassWithAttributes
|
|||||||
*/
|
*/
|
||||||
getArrayMgrs(_mgrs? : object)
|
getArrayMgrs(_mgrs? : object)
|
||||||
{
|
{
|
||||||
if (typeof _mgrs == "undefined")
|
if (typeof _mgrs == "undefined") {
|
||||||
{
|
|
||||||
_mgrs = {};
|
_mgrs = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add all managers of this object to the result, if they have not already
|
// Add all managers of this object to the result, if they have not already
|
||||||
// been set in the result
|
// been set in the result
|
||||||
for (var key in this._mgrs)
|
for (var key in this._mgrs) {
|
||||||
{
|
if (typeof _mgrs[key] == "undefined") {
|
||||||
if (typeof _mgrs[key] == "undefined")
|
|
||||||
{
|
|
||||||
_mgrs[key] = this._mgrs[key];
|
_mgrs[key] = this._mgrs[key];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recursively applies this function to the parent widget
|
// Recursively applies this function to the parent widget
|
||||||
if (this._parent)
|
if (this._parent) {
|
||||||
{
|
|
||||||
this._parent.getArrayMgrs(_mgrs);
|
this._parent.getArrayMgrs(_mgrs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1077,12 +996,9 @@ export class et2_widget extends ClassWithAttributes
|
|||||||
*/
|
*/
|
||||||
getArrayMgr(managed_array_type : string) : et2_arrayMgr | null
|
getArrayMgr(managed_array_type : string) : et2_arrayMgr | null
|
||||||
{
|
{
|
||||||
if (this._mgrs && typeof this._mgrs[managed_array_type] != "undefined")
|
if (this._mgrs && typeof this._mgrs[managed_array_type] != "undefined") {
|
||||||
{
|
|
||||||
return this._mgrs[managed_array_type];
|
return this._mgrs[managed_array_type];
|
||||||
}
|
} else if (this._parent) {
|
||||||
else if (this._parent)
|
|
||||||
{
|
|
||||||
return this._parent.getArrayMgr(managed_array_type);
|
return this._parent.getArrayMgr(managed_array_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1101,27 +1017,22 @@ export class et2_widget extends ClassWithAttributes
|
|||||||
// Get the content manager
|
// Get the content manager
|
||||||
var mgrs = this.getArrayMgrs();
|
var mgrs = this.getArrayMgrs();
|
||||||
|
|
||||||
for (var key in mgrs)
|
for (var key in mgrs) {
|
||||||
{
|
|
||||||
var mgr = mgrs[key];
|
var mgr = mgrs[key];
|
||||||
|
|
||||||
// Get the original content manager if we have already created a
|
// Get the original content manager if we have already created a
|
||||||
// perspective for this node
|
// perspective for this node
|
||||||
if (typeof this._mgrs[key] != "undefined" && mgr.perspectiveData.owner == this)
|
if (typeof this._mgrs[key] != "undefined" && mgr.perspectiveData.owner == this) {
|
||||||
{
|
|
||||||
mgr = mgr.parentMgr;
|
mgr = mgr.parentMgr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check whether the manager has a namespace for the id of this object
|
// Check whether the manager has a namespace for the id of this object
|
||||||
var entry = mgr.getEntry(this.id);
|
var entry = mgr.getEntry(this.id);
|
||||||
if (typeof entry === 'object' && entry !== null || this.id)
|
if (typeof entry === 'object' && entry !== null || this.id) {
|
||||||
{
|
|
||||||
// The content manager has an own node for this object, so
|
// The content manager has an own node for this object, so
|
||||||
// create an own perspective.
|
// create an own perspective.
|
||||||
this._mgrs[key] = mgr.openPerspective(this, this.id);
|
this._mgrs[key] = mgr.openPerspective(this, this.id);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
// The current content manager does not have an own namespace for
|
// The current content manager does not have an own namespace for
|
||||||
// this element, so use the content manager of the parent.
|
// this element, so use the content manager of the parent.
|
||||||
delete (this._mgrs[key]);
|
delete (this._mgrs[key]);
|
||||||
@ -1166,12 +1077,9 @@ export class et2_widget extends ClassWithAttributes
|
|||||||
*/
|
*/
|
||||||
getInstanceManager()
|
getInstanceManager()
|
||||||
{
|
{
|
||||||
if (this._inst != null)
|
if (this._inst != null) {
|
||||||
{
|
|
||||||
return this._inst;
|
return this._inst;
|
||||||
}
|
} else if (this._parent) {
|
||||||
else if (this._parent)
|
|
||||||
{
|
|
||||||
return this._parent.getInstanceManager();
|
return this._parent.getInstanceManager();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ import {et2_register_widget, WidgetConfig} from "./et2_core_widget";
|
|||||||
import {et2_baseWidget} from './et2_core_baseWidget'
|
import {et2_baseWidget} from './et2_core_baseWidget'
|
||||||
import {et2_inputWidget} from "./et2_core_inputWidget";
|
import {et2_inputWidget} from "./et2_core_inputWidget";
|
||||||
import {expose} from "./expose";
|
import {expose} from "./expose";
|
||||||
import {et2_IDetachedDOM, et2_IExposable, et2_IInputNode} from "./et2_core_interfaces";
|
import {et2_IDetachedDOM, et2_IExposable} from "./et2_core_interfaces";
|
||||||
import {egw} from "../jsapi/egw_global";
|
import {egw} from "../jsapi/egw_global";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -168,10 +168,10 @@ export class et2_description extends expose(class et2_description extends et2_ba
|
|||||||
(for_widget = this.getRoot().getWidgetById(this.options.for))
|
(for_widget = this.getRoot().getWidgetById(this.options.for))
|
||||||
) && for_widget && for_widget.id)
|
) && for_widget && for_widget.id)
|
||||||
{
|
{
|
||||||
if(for_widget.dom_id || for_widget.getDOMNode().id)
|
if(for_widget.dom_id)
|
||||||
{
|
{
|
||||||
for_id = for_widget.dom_id || for_widget.getDOMNode().id;
|
for_id = for_widget.dom_id;
|
||||||
if(for_widget.instanceOf(et2_IInputNode) && for_widget.getInputNode() && for_id !== for_widget.getInputNode().id)
|
if(for_widget.instanceOf(et2_inputWidget) && for_widget.getInputNode() && for_widget.dom_id !== for_widget.getInputNode().id)
|
||||||
{
|
{
|
||||||
for_id = for_widget.getInputNode().id;
|
for_id = for_widget.getInputNode().id;
|
||||||
}
|
}
|
||||||
|
@ -119,7 +119,6 @@ export class et2_grid extends et2_DOMWidget implements et2_IDetachedDOM, et2_IAl
|
|||||||
private lastRowNode: null;
|
private lastRowNode: null;
|
||||||
|
|
||||||
private sortablejs : Sortable = null;
|
private sortablejs : Sortable = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
*
|
*
|
||||||
@ -140,8 +139,7 @@ export class et2_grid extends et2_DOMWidget implements et2_IDetachedDOM, et2_IAl
|
|||||||
.appendTo(this.table);
|
.appendTo(this.table);
|
||||||
}
|
}
|
||||||
|
|
||||||
_initCells(_colData : ColumnEntry[], _rowData : RowEntry[])
|
_initCells(_colData : ColumnEntry[], _rowData : RowEntry[]) {
|
||||||
{
|
|
||||||
// Copy the width and height
|
// Copy the width and height
|
||||||
const w = _colData.length;
|
const w = _colData.length;
|
||||||
const h = _rowData.length;
|
const h = _rowData.length;
|
||||||
@ -236,8 +234,7 @@ export class et2_grid extends et2_DOMWidget implements et2_IDetachedDOM, et2_IAl
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Parse the columns tag
|
// Parse the columns tag
|
||||||
et2_filteredNodeIterator(columns, function(node, nodeName)
|
et2_filteredNodeIterator(columns, function(node, nodeName) {
|
||||||
{
|
|
||||||
const colDataEntry = this._getColDataEntry();
|
const colDataEntry = this._getColDataEntry();
|
||||||
// This cannot be done inside a nm, it will expand it later
|
// This cannot be done inside a nm, it will expand it later
|
||||||
colDataEntry["disabled"] = nm ?
|
colDataEntry["disabled"] = nm ?
|
||||||
@ -269,8 +266,7 @@ export class et2_grid extends et2_DOMWidget implements et2_IDetachedDOM, et2_IAl
|
|||||||
}, this);
|
}, this);
|
||||||
|
|
||||||
// Parse the rows tag
|
// Parse the rows tag
|
||||||
et2_filteredNodeIterator(rows, function(node, nodeName)
|
et2_filteredNodeIterator(rows, function(node, nodeName) {
|
||||||
{
|
|
||||||
const rowDataEntry = this._getRowDataEntry();
|
const rowDataEntry = this._getRowDataEntry();
|
||||||
rowDataEntry["disabled"] = this.getArrayMgr("content")
|
rowDataEntry["disabled"] = this.getArrayMgr("content")
|
||||||
.parseBoolExpression(et2_readAttrWithDefault(node, "disabled", ""));
|
.parseBoolExpression(et2_readAttrWithDefault(node, "disabled", ""));
|
||||||
@ -332,42 +328,34 @@ export class et2_grid extends et2_DOMWidget implements et2_IDetachedDOM, et2_IAl
|
|||||||
|
|
||||||
// Not in a nextmatch, so we can expand with abandon
|
// Not in a nextmatch, so we can expand with abandon
|
||||||
const currentPerspective = jQuery.extend({}, content.perspectiveData);
|
const currentPerspective = jQuery.extend({}, content.perspectiveData);
|
||||||
const check = function(node, nodeName)
|
const check = function (node, nodeName) {
|
||||||
{
|
if (nodeName == 'box' || nodeName == 'hbox' || nodeName == 'vbox') {
|
||||||
if(nodeName == 'box' || nodeName == 'hbox' || nodeName == 'vbox')
|
|
||||||
{
|
|
||||||
return et2_filteredNodeIterator(node, check, this);
|
return et2_filteredNodeIterator(node, check, this);
|
||||||
}
|
}
|
||||||
content.perspectiveData.row = rowIndex;
|
content.perspectiveData.row = rowIndex;
|
||||||
for(let attr in node.attributes)
|
for (let attr in node.attributes) {
|
||||||
{
|
|
||||||
const value = et2_readAttrWithDefault(node, node.attributes[attr].name, "");
|
const value = et2_readAttrWithDefault(node, node.attributes[attr].name, "");
|
||||||
// Don't include first char, those should be handled by normal means
|
// Don't include first char, those should be handled by normal means
|
||||||
// and it would break nextmatch
|
// and it would break nextmatch
|
||||||
if(value.indexOf('@') > 0 || value.indexOf('$') > 0)
|
if (value.indexOf('@') > 0 || value.indexOf('$') > 0) {
|
||||||
{
|
|
||||||
// Ok, we found something. How many? Check for values.
|
// Ok, we found something. How many? Check for values.
|
||||||
let ident = content.expandName(value);
|
let ident = content.expandName(value);
|
||||||
// expandName() handles index into content (@), but we have to look up
|
// expandName() handles index into content (@), but we have to look up
|
||||||
// regular values
|
// regular values
|
||||||
if(value[0] != '@')
|
if (value[0] != '@') {
|
||||||
{
|
|
||||||
// Returns null if there isn't an actual value
|
// Returns null if there isn't an actual value
|
||||||
ident = content.getEntry(ident, false, true);
|
ident = content.getEntry(ident, false, true);
|
||||||
}
|
}
|
||||||
while(ident != null && rowIndex < 1000)
|
while (ident != null && rowIndex < 1000) {
|
||||||
{
|
|
||||||
rowData[rowIndex] = jQuery.extend({}, rowDataEntry);
|
rowData[rowIndex] = jQuery.extend({}, rowDataEntry);
|
||||||
content.perspectiveData.row = ++rowIndex;
|
content.perspectiveData.row = ++rowIndex;
|
||||||
ident = content.expandName(value);
|
ident = content.expandName(value);
|
||||||
if(value[0] != '@')
|
if (value[0] != '@') {
|
||||||
{
|
|
||||||
// Returns null if there isn't an actual value
|
// Returns null if there isn't an actual value
|
||||||
ident = content.getEntry(ident, false, true);
|
ident = content.getEntry(ident, false, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(rowIndex >= 1000)
|
if (rowIndex >= 1000) {
|
||||||
{
|
|
||||||
egw.debug("error", "Problem in autorepeat fallback: too many rows for '%s'. Use a nextmatch, or start debugging.", value);
|
egw.debug("error", "Problem in autorepeat fallback: too many rows for '%s'. Use a nextmatch, or start debugging.", value);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@ -393,8 +381,7 @@ export class et2_grid extends et2_DOMWidget implements et2_IDetachedDOM, et2_IAl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_fillCells(cells, columns : object[], rows : object[])
|
_fillCells(cells, columns : object[], rows : object[]) {
|
||||||
{
|
|
||||||
const h = cells.length;
|
const h = cells.length;
|
||||||
const w = (h > 0) ? cells[0].length : 0;
|
const w = (h > 0) ? cells[0].length : 0;
|
||||||
const currentPerspective = jQuery.extend({}, this.getArrayMgr("content").perspectiveData);
|
const currentPerspective = jQuery.extend({}, this.getArrayMgr("content").perspectiveData);
|
||||||
@ -402,11 +389,9 @@ export class et2_grid extends et2_DOMWidget implements et2_IDetachedDOM, et2_IAl
|
|||||||
// Read the elements inside the columns
|
// Read the elements inside the columns
|
||||||
let x = 0;
|
let x = 0;
|
||||||
|
|
||||||
et2_filteredNodeIterator(columns, function(node, nodeName)
|
et2_filteredNodeIterator(columns, function(node, nodeName) {
|
||||||
{
|
|
||||||
|
|
||||||
function _readColNode(node, nodeName)
|
function _readColNode(node, nodeName) {
|
||||||
{
|
|
||||||
if (y >= h)
|
if (y >= h)
|
||||||
{
|
{
|
||||||
this.egw().debug("warn", "Skipped grid cell in column, '" +
|
this.egw().debug("warn", "Skipped grid cell in column, '" +
|
||||||
@ -470,11 +455,9 @@ export class et2_grid extends et2_DOMWidget implements et2_IDetachedDOM, et2_IAl
|
|||||||
nm = (widget.getType() == 'nextmatch');
|
nm = (widget.getType() == 'nextmatch');
|
||||||
widget = widget.getParent();
|
widget = widget.getParent();
|
||||||
}
|
}
|
||||||
et2_filteredNodeIterator(rows, function(node, nodeName)
|
et2_filteredNodeIterator(rows, function(node, nodeName) {
|
||||||
{
|
|
||||||
|
|
||||||
readRowNode = function _readRowNode(node, nodeName)
|
readRowNode = function _readRowNode(node, nodeName) {
|
||||||
{
|
|
||||||
if (x >= w)
|
if (x >= w)
|
||||||
{
|
{
|
||||||
if(nodeName != "description")
|
if(nodeName != "description")
|
||||||
@ -612,8 +595,7 @@ export class et2_grid extends et2_DOMWidget implements et2_IDetachedDOM, et2_IAl
|
|||||||
}, this);
|
}, this);
|
||||||
|
|
||||||
// Extra content rows
|
// Extra content rows
|
||||||
for(y; y < h; y++)
|
for(y; y < h; y++) {
|
||||||
{
|
|
||||||
x = 0;
|
x = 0;
|
||||||
|
|
||||||
et2_filteredNodeIterator(this.lastRowNode, readRowNode, this);
|
et2_filteredNodeIterator(this.lastRowNode, readRowNode, this);
|
||||||
@ -673,7 +655,6 @@ export class et2_grid extends et2_DOMWidget implements et2_IDetachedDOM, et2_IAl
|
|||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* As the does not fit very well into the default widget structure, we're
|
* As the does not fit very well into the default widget structure, we're
|
||||||
* overwriting the loadFromXML function and doing a two-pass reading -
|
* overwriting the loadFromXML function and doing a two-pass reading -
|
||||||
@ -681,8 +662,7 @@ export class et2_grid extends et2_DOMWidget implements et2_IDetachedDOM, et2_IAl
|
|||||||
*
|
*
|
||||||
* @param {object} _node xml node to process
|
* @param {object} _node xml node to process
|
||||||
*/
|
*/
|
||||||
loadFromXML(_node)
|
loadFromXML(_node ) {
|
||||||
{
|
|
||||||
// Keep the node for later changing / reloading
|
// Keep the node for later changing / reloading
|
||||||
this.template_node = _node;
|
this.template_node = _node;
|
||||||
|
|
||||||
@ -786,16 +766,8 @@ export class et2_grid extends et2_DOMWidget implements et2_IDetachedDOM, et2_IAl
|
|||||||
if (cell.disabled)
|
if (cell.disabled)
|
||||||
{
|
{
|
||||||
td.hide();
|
td.hide();
|
||||||
// Need to do different things with webComponents
|
|
||||||
if(typeof cell.widget.options !== "undefined")
|
|
||||||
{
|
|
||||||
cell.widget.options.disabled = cell.disabled;
|
cell.widget.options.disabled = cell.disabled;
|
||||||
}
|
}
|
||||||
else if(cell.widget.nodeName)
|
|
||||||
{
|
|
||||||
cell.widget.disabled = cell.disabled;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cell.width != "auto")
|
if (cell.width != "auto")
|
||||||
{
|
{
|
||||||
@ -819,13 +791,11 @@ export class et2_grid extends et2_DOMWidget implements et2_IDetachedDOM, et2_IAl
|
|||||||
const rs = (y == h - 1) ? h - y : Math.min(h - y, cell.rowSpan);
|
const rs = (y == h - 1) ? h - y : Math.min(h - y, cell.rowSpan);
|
||||||
|
|
||||||
// Set the col and row span values
|
// Set the col and row span values
|
||||||
if(cs > 1)
|
if (cs > 1) {
|
||||||
{
|
|
||||||
td.attr("colspan", cs);
|
td.attr("colspan", cs);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(rs > 1)
|
if (rs > 1) {
|
||||||
{
|
|
||||||
td.attr("rowspan", rs);
|
td.attr("rowspan", rs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -919,8 +889,7 @@ export class et2_grid extends et2_DOMWidget implements et2_IDetachedDOM, et2_IAl
|
|||||||
}
|
}
|
||||||
wrapper.css('overflow', _value);
|
wrapper.css('overflow', _value);
|
||||||
|
|
||||||
if(wrapper.length && (!_value || _value === 'visible'))
|
if(wrapper.length && (!_value || _value === 'visible')) {
|
||||||
{
|
|
||||||
this.table.unwrap();
|
this.table.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -999,22 +968,17 @@ export class et2_grid extends et2_DOMWidget implements et2_IDetachedDOM, et2_IAl
|
|||||||
filter: this.options.sortable_cancel,
|
filter: this.options.sortable_cancel,
|
||||||
ghostClass: this.options.sortable_placeholder,
|
ghostClass: this.options.sortable_placeholder,
|
||||||
dataIdAttr: 'id',
|
dataIdAttr: 'id',
|
||||||
onAdd: function(event)
|
onAdd:function (event) {
|
||||||
{
|
if (typeof self.options.sortable_recieveCallback == 'function') {
|
||||||
if(typeof self.options.sortable_recieveCallback == 'function')
|
|
||||||
{
|
|
||||||
self.options.sortable_recieveCallback.call(self, event, this, self.id);
|
self.options.sortable_recieveCallback.call(self, event, this, self.id);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onStart: function(event, ui)
|
onStart: function (event, ui) {
|
||||||
{
|
if (typeof self.options.sortable_startCallback == 'function') {
|
||||||
if(typeof self.options.sortable_startCallback == 'function')
|
|
||||||
{
|
|
||||||
self.options.sortable_startCallback.call(self, event, this, self.id);
|
self.options.sortable_startCallback.call(self, event, this, self.id);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onSort: function(event)
|
onSort: function (event) {
|
||||||
{
|
|
||||||
self.egw().json(sortable,[
|
self.egw().json(sortable,[
|
||||||
self.getInstanceManager().etemplate_exec_id,
|
self.getInstanceManager().etemplate_exec_id,
|
||||||
self.sortablejs.toArray(),
|
self.sortablejs.toArray(),
|
||||||
@ -1040,8 +1004,7 @@ export class et2_grid extends et2_DOMWidget implements et2_IDetachedDOM, et2_IAl
|
|||||||
let objectManager = egw_getAppObjectManager(true, this.getInstanceManager().app);
|
let objectManager = egw_getAppObjectManager(true, this.getInstanceManager().app);
|
||||||
objectManager = objectManager.getObjectById(this.getInstanceManager().uniqueId,2) || objectManager;
|
objectManager = objectManager.getObjectById(this.getInstanceManager().uniqueId,2) || objectManager;
|
||||||
let widget_object = objectManager.getObjectById(this.id);
|
let widget_object = objectManager.getObjectById(this.id);
|
||||||
if(widget_object == null)
|
if (widget_object == null) {
|
||||||
{
|
|
||||||
// Add a new container to the object manager which will hold the widget
|
// Add a new container to the object manager which will hold the widget
|
||||||
// objects
|
// objects
|
||||||
widget_object = objectManager.insertObject(false, new egwActionObject(
|
widget_object = objectManager.insertObject(false, new egwActionObject(
|
||||||
@ -1166,10 +1129,7 @@ export class et2_grid extends et2_DOMWidget implements et2_IDetachedDOM, et2_IAl
|
|||||||
if (!row[c].widget) continue;
|
if (!row[c].widget) continue;
|
||||||
|
|
||||||
let found = row[c].widget === _sender;
|
let found = row[c].widget === _sender;
|
||||||
if(!found) row[c].widget.iterateOver(function(_widget)
|
if (!found) row[c].widget.iterateOver(function(_widget) {if (_widget === _sender) found = true;});
|
||||||
{
|
|
||||||
if(_widget === _sender) found = true;
|
|
||||||
});
|
|
||||||
if (found)
|
if (found)
|
||||||
{
|
{
|
||||||
// return a fake row object allowing to iterate over it's children
|
// return a fake row object allowing to iterate over it's children
|
||||||
@ -1180,8 +1140,7 @@ export class et2_grid extends et2_DOMWidget implements et2_IDetachedDOM, et2_IAl
|
|||||||
}
|
}
|
||||||
row_obj.isInTree = jQuery.proxy(this.isInTree, this);
|
row_obj.isInTree = jQuery.proxy(this.isInTree, this);
|
||||||
// we must not free the children!
|
// we must not free the children!
|
||||||
row_obj.destroy = function()
|
row_obj.destroy = function(){
|
||||||
{
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
delete row_obj._children;
|
delete row_obj._children;
|
||||||
};
|
};
|
||||||
@ -1194,12 +1153,10 @@ export class et2_grid extends et2_DOMWidget implements et2_IDetachedDOM, et2_IAl
|
|||||||
/**
|
/**
|
||||||
* Needed for the align interface, but we're not doing anything with it...
|
* Needed for the align interface, but we're not doing anything with it...
|
||||||
*/
|
*/
|
||||||
get_align() : string
|
get_align(): string {
|
||||||
{
|
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
et2_register_widget(et2_grid, ["grid"]);
|
et2_register_widget(et2_grid, ["grid"]);
|
||||||
|
|
||||||
interface ColumnEntry
|
interface ColumnEntry
|
||||||
|
@ -23,12 +23,7 @@ import {et2_nextmatch, et2_nextmatch_header_bar} from "./et2_extension_nextmatch
|
|||||||
import {et2_tabbox} from "./et2_widget_tabs";
|
import {et2_tabbox} from "./et2_widget_tabs";
|
||||||
import '../jsapi/egw_json.js';
|
import '../jsapi/egw_json.js';
|
||||||
import {egwIsMobile} from "../egw_action/egw_action_common.js";
|
import {egwIsMobile} from "../egw_action/egw_action_common.js";
|
||||||
import './Et2Box/Et2Box';
|
//import './et2-button';
|
||||||
import './Et2Button/Et2Button';
|
|
||||||
import './Et2Date/Et2Date';
|
|
||||||
import './Et2Textarea/Et2Textarea';
|
|
||||||
import './Et2Textbox/Et2Textbox';
|
|
||||||
import './Et2Colorpicker/Et2Colorpicker';
|
|
||||||
/* Include all widget classes here, we only care about them registering, not importing anything*/
|
/* Include all widget classes here, we only care about them registering, not importing anything*/
|
||||||
import './et2_widget_vfs'; // Vfs must be first (before et2_widget_file) due to import cycle
|
import './et2_widget_vfs'; // Vfs must be first (before et2_widget_file) due to import cycle
|
||||||
import './et2_widget_template';
|
import './et2_widget_template';
|
||||||
@ -169,15 +164,13 @@ export class etemplate2
|
|||||||
*
|
*
|
||||||
* @param {jQuery.event} e
|
* @param {jQuery.event} e
|
||||||
*/
|
*/
|
||||||
public resize(e)
|
public resize(e) {
|
||||||
{
|
|
||||||
const event = e;
|
const event = e;
|
||||||
const self = this;
|
const self = this;
|
||||||
let excess_height: number | boolean = false;
|
let excess_height: number | boolean = false;
|
||||||
|
|
||||||
// Check if the framework has an specific excess height calculation
|
// Check if the framework has an specific excess height calculation
|
||||||
if (typeof window.framework != 'undefined' && typeof window.framework.get_wExcessHeight != 'undefined')
|
if (typeof window.framework != 'undefined' && typeof window.framework.get_wExcessHeight != 'undefined') {
|
||||||
{
|
|
||||||
excess_height = window.framework.get_wExcessHeight(window);
|
excess_height = window.framework.get_wExcessHeight(window);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -513,15 +506,12 @@ export class etemplate2
|
|||||||
|
|
||||||
// require necessary translations from server AND the app.js file, if not already loaded
|
// require necessary translations from server AND the app.js file, if not already loaded
|
||||||
let promisses = [window.egw_ready]; // to wait for legacy-loaded JS
|
let promisses = [window.egw_ready]; // to wait for legacy-loaded JS
|
||||||
if (Array.isArray(_data.langRequire))
|
if (Array.isArray(_data.langRequire)) {
|
||||||
{
|
|
||||||
promisses.push(egw(currentapp, window).langRequire(window, _data.langRequire));
|
promisses.push(egw(currentapp, window).langRequire(window, _data.langRequire));
|
||||||
}
|
}
|
||||||
return Promise.all(promisses).catch((err) =>
|
return Promise.all(promisses).catch((err) => {
|
||||||
{
|
|
||||||
console.log("et2.load(): error loading lang-files and app.js: "+err.message);
|
console.log("et2.load(): error loading lang-files and app.js: "+err.message);
|
||||||
}).then(() =>
|
}).then(() => {
|
||||||
{
|
|
||||||
this.clear();
|
this.clear();
|
||||||
|
|
||||||
// Initialize application js
|
// Initialize application js
|
||||||
@ -573,8 +563,7 @@ export class etemplate2
|
|||||||
const _load = function ()
|
const _load = function ()
|
||||||
{
|
{
|
||||||
egw.debug("log", "Loading template...");
|
egw.debug("log", "Loading template...");
|
||||||
if (egw.debug_level() >= 4 && console.timeStamp)
|
if (egw.debug_level() >= 4 && console.timeStamp) {
|
||||||
{
|
|
||||||
console.timeStamp("Begin rendering template");
|
console.timeStamp("Begin rendering template");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -680,12 +669,10 @@ export class etemplate2
|
|||||||
// Profiling
|
// Profiling
|
||||||
if (egw.debug_level() >= 4)
|
if (egw.debug_level() >= 4)
|
||||||
{
|
{
|
||||||
if (console.timeEnd)
|
if (console.timeEnd) {
|
||||||
{
|
|
||||||
console.timeEnd(_name);
|
console.timeEnd(_name);
|
||||||
}
|
}
|
||||||
if (console.profileEnd)
|
if (console.profileEnd) {
|
||||||
{
|
|
||||||
console.profileEnd(_name);
|
console.profileEnd(_name);
|
||||||
}
|
}
|
||||||
const end_time = (new Date).getTime();
|
const end_time = (new Date).getTime();
|
||||||
@ -831,8 +818,7 @@ export class etemplate2
|
|||||||
indexes = [indexes.shift(), indexes.join('[')];
|
indexes = [indexes.shift(), indexes.join('[')];
|
||||||
indexes[1] = indexes[1].substring(0, indexes[1].length - 1);
|
indexes[1] = indexes[1].substring(0, indexes[1].length - 1);
|
||||||
const children = indexes[1].split('][');
|
const children = indexes[1].split('][');
|
||||||
if (children.length)
|
if (children.length) {
|
||||||
{
|
|
||||||
indexes = jQuery.merge([indexes[0]], children);
|
indexes = jQuery.merge([indexes[0]], children);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1000,8 +986,7 @@ export class etemplate2
|
|||||||
_root.iterateOver(function (_widget)
|
_root.iterateOver(function (_widget)
|
||||||
{
|
{
|
||||||
// The widget must have an id to be included in the values array
|
// The widget must have an id to be included in the values array
|
||||||
if (_widget.id === undefined || _widget.id === "")
|
if (_widget.id === undefined || _widget.id === "") {
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1016,8 +1001,7 @@ export class etemplate2
|
|||||||
indexes = [indexes.shift(), indexes.join('[')];
|
indexes = [indexes.shift(), indexes.join('[')];
|
||||||
indexes[1] = indexes[1].substring(0, indexes[1].length - 1);
|
indexes[1] = indexes[1].substring(0, indexes[1].length - 1);
|
||||||
const children = indexes[1].split('][');
|
const children = indexes[1].split('][');
|
||||||
if (children.length)
|
if (children.length) {
|
||||||
{
|
|
||||||
indexes = jQuery.merge([indexes[0]], children);
|
indexes = jQuery.merge([indexes[0]], children);
|
||||||
}
|
}
|
||||||
path = path.concat(indexes);
|
path = path.concat(indexes);
|
||||||
@ -1419,48 +1403,38 @@ export class etemplate2
|
|||||||
* @returns {Boolean} Handled by this plugin
|
* @returns {Boolean} Handled by this plugin
|
||||||
* @throws Invalid parameters if the required res.data parameters are missing
|
* @throws Invalid parameters if the required res.data parameters are missing
|
||||||
*/
|
*/
|
||||||
public handle_assign(type, res, req)
|
public handle_assign(type, res, req) {
|
||||||
{
|
|
||||||
//type, req; // unused, but required by plugin signature
|
//type, req; // unused, but required by plugin signature
|
||||||
|
|
||||||
//Check whether all needed parameters have been passed and call the alertHandler function
|
//Check whether all needed parameters have been passed and call the alertHandler function
|
||||||
if ((typeof res.data.id != 'undefined') &&
|
if ((typeof res.data.id != 'undefined') &&
|
||||||
(typeof res.data.key != 'undefined') &&
|
(typeof res.data.key != 'undefined') &&
|
||||||
(typeof res.data.value != 'undefined')
|
(typeof res.data.value != 'undefined')
|
||||||
)
|
) {
|
||||||
{
|
|
||||||
if (typeof res.data.etemplate_exec_id == 'undefined' ||
|
if (typeof res.data.etemplate_exec_id == 'undefined' ||
|
||||||
res.data.etemplate_exec_id != this._etemplate_exec_id)
|
res.data.etemplate_exec_id != this._etemplate_exec_id) {
|
||||||
{
|
|
||||||
// Not for this etemplate, but not an error
|
// Not for this etemplate, but not an error
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (res.data.key == 'etemplate_exec_id')
|
if (res.data.key == 'etemplate_exec_id') {
|
||||||
{
|
|
||||||
this._etemplate_exec_id = res.data.value;
|
this._etemplate_exec_id = res.data.value;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (this._widgetContainer == null)
|
if (this._widgetContainer == null) {
|
||||||
{
|
|
||||||
// Right etemplate, but it's already been cleared.
|
// Right etemplate, but it's already been cleared.
|
||||||
egw.debug('warn', "Tried to call assign on an un-loaded etemplate", res.data);
|
egw.debug('warn', "Tried to call assign on an un-loaded etemplate", res.data);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const widget = this._widgetContainer.getWidgetById(res.data.id);
|
const widget = this._widgetContainer.getWidgetById(res.data.id);
|
||||||
if (widget)
|
if (widget) {
|
||||||
{
|
if (typeof widget['set_' + res.data.key] != 'function') {
|
||||||
if (typeof widget['set_' + res.data.key] != 'function')
|
|
||||||
{
|
|
||||||
egw.debug('warn', "Cannot set %s attribute %s via JSON assign, no set_%s()", res.data.id, res.data.key, res.data.key);
|
egw.debug('warn', "Cannot set %s attribute %s via JSON assign, no set_%s()", res.data.id, res.data.key, res.data.key);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
try
|
try {
|
||||||
{
|
|
||||||
widget['set_' + res.data.key].call(widget, res.data.value);
|
widget['set_' + res.data.key].call(widget, res.data.value);
|
||||||
return true;
|
return true;
|
||||||
}
|
} catch (e) {
|
||||||
catch (e)
|
|
||||||
{
|
|
||||||
egw.debug("error", "When assigning %s on %s via AJAX, \n" + (e.message || e + ""), res.data.key, res.data.id, widget);
|
egw.debug("error", "When assigning %s on %s via AJAX, \n" + (e.message || e + ""), res.data.key, res.data.id, widget);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -161,11 +161,11 @@ egw.extend('tooltip', egw.MODULE_WND_LOCAL, function(_app, _wnd)
|
|||||||
* It is important to remove all tooltips from all elements which are
|
* It is important to remove all tooltips from all elements which are
|
||||||
* no longer needed, in order to prevent memory leaks.
|
* no longer needed, in order to prevent memory leaks.
|
||||||
*
|
*
|
||||||
* @param _elem is the element to which the tooltip should get bound.
|
* @param _elem is the element to which the tooltip should get bound. It
|
||||||
|
* has to be a jQuery node.
|
||||||
* @param _html is the html code which should be shown as tooltip.
|
* @param _html is the html code which should be shown as tooltip.
|
||||||
*/
|
*/
|
||||||
tooltipBind: function(_elem, _html, _isHtml) {
|
tooltipBind: function(_elem, _html, _isHtml) {
|
||||||
_elem = jQuery(_elem);
|
|
||||||
if (_html != '')
|
if (_html != '')
|
||||||
{
|
{
|
||||||
_elem.bind('mouseenter.tooltip', function(e) {
|
_elem.bind('mouseenter.tooltip', function(e) {
|
||||||
@ -222,7 +222,6 @@ egw.extend('tooltip', egw.MODULE_WND_LOCAL, function(_app, _wnd)
|
|||||||
* removed. _elem has to be a jQuery node.
|
* removed. _elem has to be a jQuery node.
|
||||||
*/
|
*/
|
||||||
tooltipUnbind: function(_elem) {
|
tooltipUnbind: function(_elem) {
|
||||||
_elem = jQuery(_elem);
|
|
||||||
if (current_elem == _elem)
|
if (current_elem == _elem)
|
||||||
{
|
{
|
||||||
hide();
|
hide();
|
||||||
|
@ -76,4 +76,4 @@ class Button extends Etemplate\Widget
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Etemplate\Widget::registerWidget(__NAMESPACE__.'\\Button', array('et2-button','button','buttononly'));
|
Etemplate\Widget::registerWidget(__NAMESPACE__.'\\Button', array('button','buttononly'));
|
||||||
|
@ -87,20 +87,14 @@ class Date extends Transformer
|
|||||||
* @param array $expand
|
* @param array $expand
|
||||||
* @param array $data Row data
|
* @param array $data Row data
|
||||||
*/
|
*/
|
||||||
public function set_row_value($cname, array $expand, array &$data)
|
public function set_row_value($cname, Array $expand, Array &$data)
|
||||||
{
|
{
|
||||||
if($this->type == 'date-duration')
|
if($this->type == 'date-duration') return;
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$form_name = self::form_name($cname, $this->id, $expand);
|
$form_name = self::form_name($cname, $this->id, $expand);
|
||||||
$value =& $this->get_array($data, $form_name, true);
|
$value =& $this->get_array($data, $form_name, true);
|
||||||
|
|
||||||
if(true)
|
if (true) $value = $this->format_date($value);
|
||||||
{
|
|
||||||
$value = $this->format_date($value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -110,10 +104,7 @@ class Date extends Transformer
|
|||||||
*/
|
*/
|
||||||
public function format_date($value)
|
public function format_date($value)
|
||||||
{
|
{
|
||||||
if(!$value)
|
if (!$value) return $value; // otherwise we will get current date or 1970-01-01 instead of an empty value
|
||||||
{
|
|
||||||
return $value;
|
|
||||||
} // otherwise we will get current date or 1970-01-01 instead of an empty value
|
|
||||||
|
|
||||||
// for DateTime objects (regular PHP and Api\DateTime ones), set user timezone
|
// for DateTime objects (regular PHP and Api\DateTime ones), set user timezone
|
||||||
if ($value instanceof \DateTime)
|
if ($value instanceof \DateTime)
|
||||||
@ -167,10 +158,7 @@ class Date extends Transformer
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if(substr($value, -1) === 'Z')
|
if (substr($value, -1) === 'Z') $value = substr($value, 0, -1);
|
||||||
{
|
|
||||||
$value = substr($value, 0, -1);
|
|
||||||
}
|
|
||||||
$date = new Api\DateTime($value);
|
$date = new Api\DateTime($value);
|
||||||
}
|
}
|
||||||
catch(\Exception $e)
|
catch(\Exception $e)
|
||||||
@ -205,11 +193,7 @@ class Date extends Transformer
|
|||||||
elseif (preg_match('/[+-][[:digit:]]+[ymwd]/',$this->attrs['min']))
|
elseif (preg_match('/[+-][[:digit:]]+[ymwd]/',$this->attrs['min']))
|
||||||
{
|
{
|
||||||
// Relative date with periods
|
// Relative date with periods
|
||||||
$min = new Api\DateTime(strtotime(str_replace(array('y', 'm', 'w', 'd'), array('years', 'months',
|
$min = new Api\DateTime(strtotime(str_replace(array('y','m','w','d'), array('years','months','weeks','days'), $this->attrs['min'])));
|
||||||
'weeks',
|
|
||||||
'days'), $this->attrs['min'])
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -220,8 +204,7 @@ class Date extends Transformer
|
|||||||
self::set_validation_error($form_name,lang(
|
self::set_validation_error($form_name,lang(
|
||||||
"Value has to be at least '%1' !!!",
|
"Value has to be at least '%1' !!!",
|
||||||
$min->format($this->type != 'date')
|
$min->format($this->type != 'date')
|
||||||
), ''
|
),'');
|
||||||
);
|
|
||||||
$value = $min;
|
$value = $min;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -234,11 +217,7 @@ class Date extends Transformer
|
|||||||
elseif (preg_match('/[+-][[:digit:]]+[ymwd]/',$this->attrs['max']))
|
elseif (preg_match('/[+-][[:digit:]]+[ymwd]/',$this->attrs['max']))
|
||||||
{
|
{
|
||||||
// Relative date with periods
|
// Relative date with periods
|
||||||
$max = new Api\DateTime(strtotime(str_replace(array('y', 'm', 'w', 'd'), array('years', 'months',
|
$max = new Api\DateTime(strtotime(str_replace(array('y','m','w','d'), array('years','months','weeks','days'), $this->attrs['max'])));
|
||||||
'weeks',
|
|
||||||
'days'), $this->attrs['max'])
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -249,8 +228,7 @@ class Date extends Transformer
|
|||||||
self::set_validation_error($form_name,lang(
|
self::set_validation_error($form_name,lang(
|
||||||
"Value has to be at maximum '%1' !!!",
|
"Value has to be at maximum '%1' !!!",
|
||||||
$max->format($this->type != 'date')
|
$max->format($this->type != 'date')
|
||||||
), ''
|
),'');
|
||||||
);
|
|
||||||
$value = $max;
|
$value = $max;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -281,5 +259,4 @@ class Date extends Transformer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
\EGroupware\Api\Etemplate\Widget::registerWidget(__NAMESPACE__.'\\Date', array('time_or_date'));
|
||||||
\EGroupware\Api\Etemplate\Widget::registerWidget(__NAMESPACE__ . '\\Date', array('et2-date', 'time_or_date'));
|
|
@ -205,15 +205,29 @@ class Template extends Etemplate\Widget
|
|||||||
/**
|
/**
|
||||||
* Convert relative template path from relPath to an url incl. cache-buster modification time postfix
|
* Convert relative template path from relPath to an url incl. cache-buster modification time postfix
|
||||||
*
|
*
|
||||||
* This adds the server-side modification of eTemplates for web-components /api/etemplate.php.
|
|
||||||
*
|
|
||||||
* @param string $path
|
* @param string $path
|
||||||
* @return string url
|
* @return string url
|
||||||
*/
|
*/
|
||||||
public static function rel2url($path)
|
public static function rel2url($path)
|
||||||
{
|
{
|
||||||
return $GLOBALS['egw_info']['server']['webserver_url'].'/api/etemplate.php'.
|
if ($path)
|
||||||
($path[0] === '/' ? $path : Api\Vfs::parse_url($path, PHP_URL_PATH)).'?'.filemtime(self::rel2path($path));
|
{
|
||||||
|
if ($path[0] === '/')
|
||||||
|
{
|
||||||
|
$url = $GLOBALS['egw_info']['server']['webserver_url'].$path.'?'.filemtime(self::rel2path($path));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$url = Api\Vfs::download_url($path);
|
||||||
|
|
||||||
|
if ($url[0] == '/') $url = Api\Framework::link($url);
|
||||||
|
|
||||||
|
// mtime postfix has to use '?download=', as our WebDAV treats everything else literal and not ignore them like Apache for static files!
|
||||||
|
$url .= '?download='.filemtime($path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//error_log(__METHOD__."('$path') returning $url");
|
||||||
|
return $url;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -179,6 +179,4 @@ class Textbox extends Etemplate\Widget
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Etemplate\Widget::registerWidget(__NAMESPACE__ . '\\Textbox', array('et2-textarea', 'et2-textbox', 'textbox', 'text',
|
Etemplate\Widget::registerWidget(__NAMESPACE__.'\\Textbox', array('textbox','text','int','integer','float','hidden','colorpicker','hidden'));
|
||||||
'int', 'integer', 'float', 'hidden', 'colorpicker',
|
|
||||||
'hidden'));
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<!-- $Id$ -->
|
<!-- $Id$ -->
|
||||||
<overlay>
|
<overlay>
|
||||||
<template id="infolog.edit.description" template="" lang="" group="0" version="1.6.001">
|
<template id="infolog.edit.description" template="" lang="" group="0" version="1.6.001">
|
||||||
<et2-textarea id="info_des" no_lang="1" width="99.7%" height="245px"/>
|
<textbox multiline="true" id="info_des" no_lang="1" width="99.7%" height="245px"/>
|
||||||
<checkbox id="clean_history"/>
|
<checkbox id="clean_history"/>
|
||||||
</template>
|
</template>
|
||||||
<template id="infolog.edit.links" template="" lang="" group="0" version="1.3.001">
|
<template id="infolog.edit.links" template="" lang="" group="0" version="1.3.001">
|
||||||
@ -154,7 +154,7 @@
|
|||||||
<rows>
|
<rows>
|
||||||
<row class="dialogHeader">
|
<row class="dialogHeader">
|
||||||
<description value="Title" for="info_subject"/>
|
<description value="Title" for="info_subject"/>
|
||||||
<et2-textbox statustext="a short subject for the entry" id="info_subject" class="et2_fullWidth et2_required" maxlength="255" span="4" tabindex="1"></et2-textbox>
|
<textbox statustext="a short subject for the entry" id="info_subject" class="et2_fullWidth et2_required" maxlength="255" span="4" tabindex="1"/>
|
||||||
<textbox type="integer" id="info_number" readonly="true"/>
|
<textbox type="integer" id="info_number" readonly="true"/>
|
||||||
<appicon src="infolog" for="info_number"/>
|
<appicon src="infolog" for="info_number"/>
|
||||||
</row>
|
</row>
|
||||||
@ -165,7 +165,7 @@
|
|||||||
</menulist>
|
</menulist>
|
||||||
<description/>
|
<description/>
|
||||||
<description value="Startdate" for="info_startdate"/>
|
<description value="Startdate" for="info_startdate"/>
|
||||||
<et2-date statustext="when should the ToDo or Phonecall be started, it shows up from that date in the filter open or own open (startpage)" id="info_startdate" class="et2_fullWidth"></et2-date>
|
<date-time statustext="when should the ToDo or Phonecall be started, it shows up from that date in the filter open or own open (startpage)" id="info_startdate" class="et2_fullWidth"/>
|
||||||
</row>
|
</row>
|
||||||
<row class="dialogHeader3">
|
<row class="dialogHeader3">
|
||||||
<description value="Contact"/>
|
<description value="Contact"/>
|
||||||
@ -217,21 +217,25 @@
|
|||||||
</row>
|
</row>
|
||||||
<row disabled="!@info_owner" class="dialogOperators">
|
<row disabled="!@info_owner" class="dialogOperators">
|
||||||
<description value="Owner"/>
|
<description value="Owner"/>
|
||||||
<et2-hbox>
|
<hbox width="100%">
|
||||||
<select-account id="info_owner" readonly="true"/>
|
<menulist>
|
||||||
|
<menupopup type="select-account" id="info_owner" readonly="true"/>
|
||||||
|
</menulist>
|
||||||
<date-time id="info_created" readonly="true" align="right"/>
|
<date-time id="info_created" readonly="true" align="right"/>
|
||||||
</et2-hbox>
|
</hbox>
|
||||||
<description/>
|
<description/>
|
||||||
<description value="Last modified"/>
|
<description value="Last modified"/>
|
||||||
<et2-hbox>
|
<hbox width="100%">
|
||||||
<select-account id="info_modifier" readonly="true"/>
|
<menulist>
|
||||||
|
<menupopup type="select-account" id="info_modifier" readonly="true"/>
|
||||||
|
</menulist>
|
||||||
<date-time id="info_datemodified" readonly="true" align="right"/>
|
<date-time id="info_datemodified" readonly="true" align="right"/>
|
||||||
</et2-hbox>
|
</hbox>
|
||||||
</row>
|
</row>
|
||||||
<row class="dialogFooterToolbar">
|
<row class="dialogFooterToolbar">
|
||||||
<hbox span="6">
|
<hbox span="6">
|
||||||
<button statustext="Saves this entry" label="Save" id="button[save]" image="save" background_image="1"/>
|
<button statustext="Saves this entry" label="Save" id="button[save]" image="save" background_image="1"/>
|
||||||
<et2-button statustext="Apply the changes" label="Apply" id="button[apply]" image="apply" background_image="1"></et2-button>
|
<button statustext="Apply the changes" label="Apply" id="button[apply]" image="apply" background_image="1"/>
|
||||||
<button statustext="leave without saveing the entry" label="Cancel" id="button[cancel]" onclick="window.close();" image="cancel" background_image="1"/>
|
<button statustext="leave without saveing the entry" label="Cancel" id="button[cancel]" onclick="window.close();" image="cancel" background_image="1"/>
|
||||||
<menulist>
|
<menulist>
|
||||||
<menupopup statustext="Execute a further action for this entry" id="action" onchange="app.infolog.edit_actions()" options="Actions..."/>
|
<menupopup statustext="Execute a further action for this entry" id="action" onchange="app.infolog.edit_actions()" options="Actions..."/>
|
||||||
|
47
lit/index.html
Normal file
47
lit/index.html
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
<!--
|
||||||
|
@license
|
||||||
|
|
||||||
|
Copyright IBM Corp. 2019
|
||||||
|
|
||||||
|
This source code is licensed under the Apache-2.0 license found in the
|
||||||
|
LICENSE file in the root directory of this source tree.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>carbon-web-components example</title>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<style type="text/css">
|
||||||
|
body {
|
||||||
|
font-family: 'IBM Plex Sans', 'Helvetica Neue', Arial, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
#app {
|
||||||
|
width: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
bx-dropdown,
|
||||||
|
bx-dropdown-item {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
bx-dropdown:defined,
|
||||||
|
bx-dropdown-item:defined {
|
||||||
|
visibility: inherit;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Hello World! 👋</h1>
|
||||||
|
<div id="app">
|
||||||
|
<bx-dropdown trigger-content="Select an item">
|
||||||
|
<bx-dropdown-item value="all">Option 1</bx-dropdown-item>
|
||||||
|
<bx-dropdown-item value="cloudFoundry">Option 2</bx-dropdown-item>
|
||||||
|
<bx-dropdown-item value="staging">Option 3</bx-dropdown-item>
|
||||||
|
<bx-dropdown-item value="dea">Option 4</bx-dropdown-item>
|
||||||
|
<bx-dropdown-item value="router">Option 5</bx-dropdown-item>
|
||||||
|
</bx-dropdown>
|
||||||
|
</div>
|
||||||
|
<script type="module" src="js/app.min.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
11
lit/js/app.js
Normal file
11
lit/js/app.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp. 2020
|
||||||
|
*
|
||||||
|
* This source code is licensed under the Apache-2.0 license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import 'carbon-web-components/es/components/dropdown/dropdown.js';
|
||||||
|
import 'carbon-web-components/es/components/dropdown/dropdown-item.js';
|
6178
package-lock.json
generated
6178
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
18
package.json
18
package.json
@ -6,25 +6,16 @@
|
|||||||
"repository": {},
|
"repository": {},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "rollup -c",
|
"build": "rollup -c",
|
||||||
"build:watch": "rollup -cw",
|
"build:watch": "rollup -cw"
|
||||||
"jstest": "web-test-runner",
|
|
||||||
"jstest:watch": "web-test-runner --watch"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.14.6",
|
"@babel/core": "^7.14.6",
|
||||||
"@babel/preset-typescript": "^7.14.5",
|
"@babel/preset-typescript": "^7.14.5",
|
||||||
"@open-wc/testing": "^2.5.33",
|
|
||||||
"@rollup/plugin-babel": "^5.3.0",
|
"@rollup/plugin-babel": "^5.3.0",
|
||||||
"@rollup/plugin-node-resolve": "^13.0.0",
|
"@rollup/plugin-node-resolve": "^13.0.0",
|
||||||
"@rollup/plugin-typescript": "^8.2.1",
|
"@rollup/plugin-typescript": "^8.2.1",
|
||||||
"@types/chai": "^4.2.21",
|
|
||||||
"@types/jquery": "^3.5.5",
|
"@types/jquery": "^3.5.5",
|
||||||
"@types/jqueryui": "^1.12.14",
|
"@types/jqueryui": "^1.12.14",
|
||||||
"@types/mocha": "^8.2.3",
|
|
||||||
"@web/dev-server-esbuild": "^0.2.14",
|
|
||||||
"@web/dev-server-rollup": "^0.3.9",
|
|
||||||
"@web/test-runner": "^0.13.16",
|
|
||||||
"@web/test-runner-playwright": "^0.8.8",
|
|
||||||
"grunt": "^1.3.0",
|
"grunt": "^1.3.0",
|
||||||
"grunt-contrib-cssmin": "^2.2.1",
|
"grunt-contrib-cssmin": "^2.2.1",
|
||||||
"grunt-newer": "^1.3.0",
|
"grunt-newer": "^1.3.0",
|
||||||
@ -32,7 +23,6 @@
|
|||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"rollup": "^2.52.2",
|
"rollup": "^2.52.2",
|
||||||
"rollup-plugin-terser": "^7.0.2",
|
"rollup-plugin-terser": "^7.0.2",
|
||||||
"sinon": "^11.1.2",
|
|
||||||
"terser": "^4.8.0",
|
"terser": "^4.8.0",
|
||||||
"typescript": "^3.9.7"
|
"typescript": "^3.9.7"
|
||||||
},
|
},
|
||||||
@ -56,12 +46,6 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@andxor/jquery-ui-touch-punch-fix": "^1.0.2",
|
"@andxor/jquery-ui-touch-punch-fix": "^1.0.2",
|
||||||
"@lion/button": "^0.14.2",
|
|
||||||
"@lion/core": "^0.18.2",
|
|
||||||
"@lion/input": "^0.15.4",
|
|
||||||
"@lion/input-date": "^0.12.6",
|
|
||||||
"@lion/input-datepicker": "^0.23.6",
|
|
||||||
"@lion/textarea": "^0.13.4",
|
|
||||||
"jquery-ui-dist": "^1.12.1",
|
"jquery-ui-dist": "^1.12.1",
|
||||||
"jquery-ui-themes": "^1.12.0",
|
"jquery-ui-themes": "^1.12.0",
|
||||||
"jquery-ui-timepicker-addon": "^1.6.3",
|
"jquery-ui-timepicker-addon": "^1.6.3",
|
||||||
|
@ -21,19 +21,7 @@ import resolve from '@rollup/plugin-node-resolve';
|
|||||||
rimraf.sync('./chunks/');
|
rimraf.sync('./chunks/');
|
||||||
|
|
||||||
// Turn on minification
|
// Turn on minification
|
||||||
const do_minify = false;
|
const do_minify = true;
|
||||||
|
|
||||||
function isBareSpecifier (id) {
|
|
||||||
if (id.startsWith("./") || id.startsWith("../") || id.startsWith("/"))
|
|
||||||
return false;
|
|
||||||
try {
|
|
||||||
new URL(id);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
treeshake: false,
|
treeshake: false,
|
||||||
@ -70,11 +58,6 @@ const config = {
|
|||||||
},
|
},
|
||||||
plugins: [{
|
plugins: [{
|
||||||
resolveId (id, parentId) {
|
resolveId (id, parentId) {
|
||||||
// Delegate bare specifiers to node_modules resolver
|
|
||||||
if (isBareSpecifier(id))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!parentId || parentId.indexOf(path.sep + 'node_modules' + path.sep) !== -1)
|
if (!parentId || parentId.indexOf(path.sep + 'node_modules' + path.sep) !== -1)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@ -103,9 +86,7 @@ const config = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
// resolve (external) node modules from node_modules directory
|
// resolve (external) node modules from node_modules directory
|
||||||
resolve({
|
resolve(),
|
||||||
browser: true
|
|
||||||
}),
|
|
||||||
{
|
{
|
||||||
transform (code, id) {
|
transform (code, id) {
|
||||||
if (id.endsWith('.ts'))
|
if (id.endsWith('.ts'))
|
||||||
|
@ -1,73 +0,0 @@
|
|||||||
/**
|
|
||||||
* This is the configuration file for automatic TypeScript testing
|
|
||||||
*
|
|
||||||
* It uses "web-test-runner" to run the tests, which are written using
|
|
||||||
* Mocha (https://mochajs.org/) & Chai Assertion Library (https://www.chaijs.com/api/assert/)
|
|
||||||
* Playwright (https://playwright.dev/docs/intro) runs the tests in actual browsers.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import fs from 'fs';
|
|
||||||
import {playwrightLauncher} from '@web/test-runner-playwright';
|
|
||||||
import {esbuildPlugin} from '@web/dev-server-esbuild';
|
|
||||||
|
|
||||||
// Get tests for web components (in their own directory)
|
|
||||||
const webComponents = fs.readdirSync('api/js/etemplate')
|
|
||||||
.filter(
|
|
||||||
dir => fs.statSync(`api/js/etemplate/${dir}`).isDirectory() && fs.existsSync(`api/js/etemplate/${dir}/test`),
|
|
||||||
)
|
|
||||||
.map(dir => `api/js/etemplate/${dir}/test`);
|
|
||||||
|
|
||||||
// Add any test files in app/js/test/
|
|
||||||
const appJS = fs.readdirSync('.')
|
|
||||||
.filter(
|
|
||||||
dir => fs.existsSync(`${dir}/js`) && fs.existsSync(`${dir}/js/test`) && fs.statSync(`${dir}/js/test`).isDirectory(),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
export default {
|
|
||||||
nodeResolve: true,
|
|
||||||
coverageConfig: {
|
|
||||||
report: true,
|
|
||||||
reportDir: 'coverage',
|
|
||||||
threshold: {
|
|
||||||
statements: 90,
|
|
||||||
branches: 65,
|
|
||||||
functions: 80,
|
|
||||||
lines: 90,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
testFramework: {
|
|
||||||
config: {
|
|
||||||
timeout: '3000',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
browsers: [
|
|
||||||
playwrightLauncher({product: 'firefox', concurrency: 1}),
|
|
||||||
playwrightLauncher({product: 'chromium'}),
|
|
||||||
// Dependant on specific versions of shared libraries (libicuuc.so.66, latest is .67)
|
|
||||||
//playwrightLauncher({ product: 'webkit' }),
|
|
||||||
],
|
|
||||||
groups:
|
|
||||||
webComponents.map(pkg =>
|
|
||||||
{
|
|
||||||
return {
|
|
||||||
name: `${pkg}`,
|
|
||||||
files: `${pkg}/*.test.ts`
|
|
||||||
};
|
|
||||||
}).concat(
|
|
||||||
appJS.map(app =>
|
|
||||||
{
|
|
||||||
return {
|
|
||||||
name: app,
|
|
||||||
files: `${app}/js/**/*.test.ts`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
,
|
|
||||||
|
|
||||||
plugins: [
|
|
||||||
// Handles typescript
|
|
||||||
esbuildPlugin({ts: true})
|
|
||||||
],
|
|
||||||
};
|
|
Loading…
Reference in New Issue
Block a user