You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by lo...@apache.org on 2021/06/17 06:49:20 UTC
[myfaces-tobago] branch master updated: refactor: Typescript submit
impl
This is an automated email from the ASF dual-hosted git repository.
lofwyr pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/myfaces-tobago.git
The following commit(s) were added to refs/heads/master by this push:
new 9c951fd refactor: Typescript submit impl
9c951fd is described below
commit 9c951fd4de4667b69ca1e33c4ceb27c184843671
Author: Udo Schnurpfeil <ud...@irian.eu>
AuthorDate: Thu Jun 17 08:41:33 2021 +0200
refactor: Typescript submit impl
* removing unused code
* refactor static calls to class methods
issue: TOBAGO-1633
---
.../30-concept/23-transition/Transition.xhtml | 2 +-
.../tobago-theme-standard/src/main/js/tobago.js | 1710 ++++++++++----------
.../src/main/js/tobago.js.map | 2 +-
.../src/main/js/tobago.min.js | 4 +-
.../src/main/js/tobago.min.js.map | 2 +-
.../src/main/ts/tobago-command.ts | 185 +--
.../src/main/ts/tobago-page.ts | 17 +-
7 files changed, 875 insertions(+), 1047 deletions(-)
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/30-concept/23-transition/Transition.xhtml b/tobago-example/tobago-example-demo/src/main/webapp/content/30-concept/23-transition/Transition.xhtml
index 3c9e068..a0f8a9c 100644
--- a/tobago-example/tobago-example-demo/src/main/webapp/content/30-concept/23-transition/Transition.xhtml
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/30-concept/23-transition/Transition.xhtml
@@ -38,7 +38,7 @@ action="\#{transitionController.sleep5sAndRedirect}"/>
<tc:link label="link with transition OFF" transition="false"
action="\#{transitionController.sleep5sAndRedirect}"/></demo-highlight>
- <tc:link label="link with transition ON" action="#{transitionController.sleep5sAndRedirect}"/>
+ <tc:link label="link with transition ON" action="#{transitionController.sleep5sAndRedirect}"/> (default)
<br/>
<tc:link label="link with transition OFF" transition="false" action="#{transitionController.sleep5sAndRedirect}"/>
</tc:section>
diff --git a/tobago-theme/tobago-theme-standard/src/main/js/tobago.js b/tobago-theme/tobago-theme-standard/src/main/js/tobago.js
index 4f22a9e..001683d 100644
--- a/tobago-theme/tobago-theme-standard/src/main/js/tobago.js
+++ b/tobago-theme/tobago-theme-standard/src/main/js/tobago.js
@@ -2913,6 +2913,339 @@
Config.set("Tobago.waitOverlayDelay", 1000);
Config.set("Ajax.waitOverlayDelay", 1000);
+ /*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ class Page extends HTMLElement {
+ constructor() {
+ super();
+ this.submitActive = false;
+ }
+ /**
+ * The Tobago root element
+ */
+ static page(element) {
+ const rootNode = element.getRootNode();
+ const pages = rootNode.querySelectorAll("tobago-page");
+ if (pages.length > 0) {
+ if (pages.length >= 2) {
+ console.warn("Found more than one tobago-page element!");
+ }
+ return pages.item(0);
+ }
+ console.warn("Found no tobago page!");
+ return null;
+ }
+ /**
+ * "a:b" -> "a"
+ * "a:b:c" -> "a:b"
+ * "a" -> null
+ * null -> null
+ * "a:b::sub-component" -> "a"
+ * "a::sub-component:b" -> "a::sub-component" // should currently not happen in Tobago
+ *
+ * @param clientId The clientId of a component.
+ * @return The clientId of the naming container.
+ */
+ static getNamingContainerId(clientId) {
+ if (clientId == null || clientId.lastIndexOf(":") === -1) {
+ return null;
+ }
+ let id = clientId;
+ while (true) {
+ const sub = id.lastIndexOf("::");
+ if (sub == -1) {
+ break;
+ }
+ if (sub + 1 == id.lastIndexOf(":")) {
+ id = id.substring(0, sub);
+ }
+ else {
+ break;
+ }
+ }
+ return id.substring(0, id.lastIndexOf(":"));
+ }
+ connectedCallback() {
+ this.registerAjaxListener();
+ this.form.addEventListener("submit", this.beforeSubmit.bind(this));
+ window.addEventListener("unload", this.onUnload.bind(this));
+ this.addEventListener("keypress", (event) => {
+ let code = event.which; // XXX deprecated
+ if (code === 0) {
+ code = event.keyCode;
+ }
+ if (code === 13) {
+ const target = event.target;
+ if (target.tagName === "A" || target.tagName === "BUTTON") {
+ return;
+ }
+ if (target.tagName === "TEXTAREA") {
+ if (!event.metaKey && !event.ctrlKey) {
+ return;
+ }
+ }
+ const name = target.getAttribute("name");
+ let id = name ? name : target.id;
+ while (id != null) {
+ const command = document.querySelector(`[data-tobago-default='${id}']`);
+ if (command) {
+ command.dispatchEvent(new MouseEvent("click"));
+ break;
+ }
+ id = Page.getNamingContainerId(id);
+ }
+ return false;
+ }
+ });
+ }
+ beforeSubmit() {
+ this.submitActive = true;
+ if (this.transition) {
+ new Overlay(this);
+ }
+ this.transition = this.oldTransition;
+ }
+ /**
+ * Wrapper function to call application generated onunload function
+ */
+ onUnload() {
+ console.info("on unload");
+ if (Page.page(this).submitActive) {
+ if (this.transition) {
+ new Overlay(this);
+ }
+ this.transition = this.oldTransition;
+ }
+ }
+ registerAjaxListener() {
+ jsf.ajax.addOnEvent(this.jsfResponse.bind(this));
+ }
+ jsfResponse(event) {
+ console.timeEnd("[tobago-jsf] jsf-ajax");
+ console.time("[tobago-jsf] jsf-ajax");
+ console.debug("[tobago-jsf] JSF event status: '%s'", event.status);
+ if (event.status === "success") {
+ event.responseXML.querySelectorAll("update").forEach(this.jsfResponseSuccess.bind(this));
+ }
+ else if (event.status === "complete") {
+ event.responseXML.querySelectorAll("update").forEach(this.jsfResponseComplete.bind(this));
+ }
+ }
+ jsfResponseSuccess(update) {
+ const id = update.id;
+ let rootNode = this.getRootNode();
+ // XXX in case of "this" is tobago-page (e.g. ajax exception handling) rootNode is not set correctly???
+ if (!rootNode.getElementById) {
+ rootNode = document;
+ }
+ console.debug("[tobago-jsf] Update after jsf.ajax success: %s", id);
+ }
+ jsfResponseComplete(update) {
+ const id = update.id;
+ if (JsfParameter.isJsfId(id)) {
+ console.debug("[tobago-jsf] Update after jsf.ajax complete: #", id);
+ Overlay.destroy(id);
+ }
+ }
+ get form() {
+ return this.querySelector("form");
+ }
+ get locale() {
+ let locale = this.getAttribute("locale");
+ if (!locale) {
+ locale = document.documentElement.lang;
+ }
+ return locale;
+ }
+ }
+ document.addEventListener("tobago.init", (event) => {
+ if (window.customElements.get("tobago-page") == null) {
+ window.customElements.define("tobago-page", Page);
+ }
+ });
+ class JsfParameter {
+ static isJsfId(id) {
+ switch (id) {
+ case JsfParameter.VIEW_STATE:
+ case JsfParameter.CLIENT_WINDOW:
+ case JsfParameter.VIEW_ROOT:
+ case JsfParameter.VIEW_HEAD:
+ case JsfParameter.VIEW_BODY:
+ case JsfParameter.RESOURCE:
+ return false;
+ default:
+ return true;
+ }
+ }
+ static isJsfBody(id) {
+ switch (id) {
+ case JsfParameter.VIEW_ROOT:
+ case JsfParameter.VIEW_BODY:
+ return true;
+ default:
+ return false;
+ }
+ }
+ }
+ JsfParameter.VIEW_STATE = "javax.faces.ViewState";
+ JsfParameter.CLIENT_WINDOW = "javax.faces.ClientWindow";
+ JsfParameter.VIEW_ROOT = "javax.faces.ViewRoot";
+ JsfParameter.VIEW_HEAD = "javax.faces.ViewHead";
+ JsfParameter.VIEW_BODY = "javax.faces.ViewBody";
+ JsfParameter.RESOURCE = "javax.faces.Resource";
+
+ /*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ class DatePicker extends HTMLElement {
+ constructor() {
+ super();
+ }
+ connectedCallback() {
+ if (this.type == "date") {
+ console.debug("check input type=date support", DatePicker.SUPPORTS_INPUT_TYPE_DATE);
+ if (!DatePicker.SUPPORTS_INPUT_TYPE_DATE) {
+ this.setAttribute("type", "text");
+ this.initVanillaDatePicker();
+ }
+ }
+ }
+ initVanillaDatePicker() {
+ var _a;
+ const field = this.field;
+ const locale = Page.page(this).locale;
+ const i18n = this.i18n;
+ i18n.titleFormat = "MM y"; // todo i18n
+ i18n.format = this.pattern;
+ Datepicker.locales[locale] = i18n;
+ const options = {
+ buttonClass: "btn",
+ orientation: "auto",
+ autohide: true,
+ language: locale,
+ todayBtn: this.todayButton,
+ todayBtnMode: 1,
+ minDate: this.min,
+ maxDate: this.max,
+ // todo readonly
+ // todo show week numbers
+ };
+ const datepicker = new Datepicker(field, options);
+ // XXX these listeners are needed as long as we have a solution for:
+ // XXX https://github.com/mymth/vanillajs-datepicker/issues/13
+ // XXX the 2nd point is missing the "normal" change event on the input element
+ field.addEventListener("keyup", (event) => {
+ // console.info("event -----> ", event.type);
+ if (event.metaKey || event.key.length > 1 && event.key !== "Backspace" && event.key !== "Delete") {
+ return;
+ }
+ // back up user's input when user types printable character or backspace/delete
+ const target = event.target;
+ target._oldValue = target.value;
+ });
+ field.addEventListener("focus", (event) => {
+ // console.info("event -----> ", event.type);
+ this.lastValue = field.value;
+ });
+ field.addEventListener("blur", (event) => {
+ // console.info("event -----> ", event.type);
+ const target = event.target;
+ // no-op when user goes to another window or the input field has no backed-up value
+ if (document.hasFocus() && target._oldValue !== undefined) {
+ if (target._oldValue !== target.value) {
+ target.datepicker.setDate(target._oldValue || { clear: true });
+ }
+ delete target._oldValue;
+ }
+ if (this.lastValue !== field.value) {
+ field.dispatchEvent(new Event("change"));
+ }
+ });
+ datepicker.element.addEventListener("changeDate", (event) => {
+ // console.info("event -----> ", event.type);
+ field.dispatchEvent(new Event("change"));
+ });
+ // simple solution for the picker: currently only open, not close is implemented
+ (_a = this.querySelector(".tobago-date-picker")) === null || _a === void 0 ? void 0 : _a.addEventListener("click", (event) => {
+ this.field.focus();
+ });
+ }
+ get todayButton() {
+ return this.hasAttribute("today-button");
+ }
+ set todayButton(todayButton) {
+ if (todayButton) {
+ this.setAttribute("today-button", "");
+ }
+ else {
+ this.removeAttribute("today-button");
+ }
+ }
+ get type() {
+ var _a;
+ return (_a = this.field) === null || _a === void 0 ? void 0 : _a.getAttribute("type");
+ }
+ get min() {
+ var _a;
+ return (_a = this.field) === null || _a === void 0 ? void 0 : _a.getAttribute("min");
+ }
+ get max() {
+ var _a;
+ return (_a = this.field) === null || _a === void 0 ? void 0 : _a.getAttribute("max");
+ }
+ get pattern() {
+ let pattern = this.getAttribute("pattern");
+ return pattern ? pattern : "yyyy-mm-dd";
+ }
+ get i18n() {
+ const i18n = this.getAttribute("i18n");
+ return i18n ? JSON.parse(i18n) : undefined;
+ }
+ get field() {
+ const rootNode = this.getRootNode();
+ return rootNode.getElementById(this.id + "::field");
+ }
+ }
+ DatePicker.SUPPORTS_INPUT_TYPE_DATE = (() => {
+ const input = document.createElement("input");
+ input.setAttribute("type", "date");
+ const thisIsNoDate = "this is not a date";
+ input.setAttribute("value", thisIsNoDate);
+ return input.value !== thisIsNoDate;
+ })();
+ document.addEventListener("tobago.init", function (event) {
+ if (window.customElements.get("tobago-date") == null) {
+ window.customElements.define("tobago-date", DatePicker);
+ }
+ });
+
var top = 'top';
var bottom = 'bottom';
var right = 'right';
@@ -9331,679 +9664,284 @@
}
if (callback) {
- callback();
- }
- } // Static
-
-
- static jQueryInterface(config) {
- return this.each(function () {
- const data = Data.get(this, DATA_KEY$1) || new Tab$1(this);
-
- if (typeof config === 'string') {
- if (typeof data[config] === 'undefined') {
- throw new TypeError(`No method named "${config}"`);
- }
-
- data[config]();
- }
- });
- }
-
- }
- /**
- * ------------------------------------------------------------------------
- * Data Api implementation
- * ------------------------------------------------------------------------
- */
-
-
- EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
- if (['A', 'AREA'].includes(this.tagName)) {
- event.preventDefault();
- }
-
- if (isDisabled(this)) {
- return;
- }
-
- const data = Data.get(this, DATA_KEY$1) || new Tab$1(this);
- data.show();
- });
- /**
- * ------------------------------------------------------------------------
- * jQuery
- * ------------------------------------------------------------------------
- * add .Tab to jQuery only if jQuery is present
- */
-
- defineJQueryPlugin(Tab$1);
-
- /**
- * --------------------------------------------------------------------------
- * Bootstrap (v5.0.1): toast.js
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
- * --------------------------------------------------------------------------
- */
- /**
- * ------------------------------------------------------------------------
- * Constants
- * ------------------------------------------------------------------------
- */
-
- const NAME = 'toast';
- const DATA_KEY = 'bs.toast';
- const EVENT_KEY = `.${DATA_KEY}`;
- const EVENT_CLICK_DISMISS = `click.dismiss${EVENT_KEY}`;
- const EVENT_MOUSEOVER = `mouseover${EVENT_KEY}`;
- const EVENT_MOUSEOUT = `mouseout${EVENT_KEY}`;
- const EVENT_FOCUSIN = `focusin${EVENT_KEY}`;
- const EVENT_FOCUSOUT = `focusout${EVENT_KEY}`;
- const EVENT_HIDE = `hide${EVENT_KEY}`;
- const EVENT_HIDDEN = `hidden${EVENT_KEY}`;
- const EVENT_SHOW = `show${EVENT_KEY}`;
- const EVENT_SHOWN = `shown${EVENT_KEY}`;
- const CLASS_NAME_FADE = 'fade';
- const CLASS_NAME_HIDE = 'hide';
- const CLASS_NAME_SHOW = 'show';
- const CLASS_NAME_SHOWING = 'showing';
- const DefaultType = {
- animation: 'boolean',
- autohide: 'boolean',
- delay: 'number'
- };
- const Default = {
- animation: true,
- autohide: true,
- delay: 5000
- };
- const SELECTOR_DATA_DISMISS = '[data-bs-dismiss="toast"]';
- /**
- * ------------------------------------------------------------------------
- * Class Definition
- * ------------------------------------------------------------------------
- */
-
- class Toast extends BaseComponent {
- constructor(element, config) {
- super(element);
- this._config = this._getConfig(config);
- this._timeout = null;
- this._hasMouseInteraction = false;
- this._hasKeyboardInteraction = false;
-
- this._setListeners();
- } // Getters
-
-
- static get DefaultType() {
- return DefaultType;
- }
-
- static get Default() {
- return Default;
- }
-
- static get NAME() {
- return NAME;
- } // Public
-
-
- show() {
- const showEvent = EventHandler.trigger(this._element, EVENT_SHOW);
-
- if (showEvent.defaultPrevented) {
- return;
- }
-
- this._clearTimeout();
-
- if (this._config.animation) {
- this._element.classList.add(CLASS_NAME_FADE);
- }
-
- const complete = () => {
- this._element.classList.remove(CLASS_NAME_SHOWING);
-
- this._element.classList.add(CLASS_NAME_SHOW);
-
- EventHandler.trigger(this._element, EVENT_SHOWN);
-
- this._maybeScheduleHide();
- };
-
- this._element.classList.remove(CLASS_NAME_HIDE);
-
- reflow(this._element);
-
- this._element.classList.add(CLASS_NAME_SHOWING);
-
- this._queueCallback(complete, this._element, this._config.animation);
- }
-
- hide() {
- if (!this._element.classList.contains(CLASS_NAME_SHOW)) {
- return;
- }
-
- const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE);
-
- if (hideEvent.defaultPrevented) {
- return;
- }
-
- const complete = () => {
- this._element.classList.add(CLASS_NAME_HIDE);
-
- EventHandler.trigger(this._element, EVENT_HIDDEN);
- };
-
- this._element.classList.remove(CLASS_NAME_SHOW);
-
- this._queueCallback(complete, this._element, this._config.animation);
- }
-
- dispose() {
- this._clearTimeout();
-
- if (this._element.classList.contains(CLASS_NAME_SHOW)) {
- this._element.classList.remove(CLASS_NAME_SHOW);
- }
-
- super.dispose();
- } // Private
-
-
- _getConfig(config) {
- config = { ...Default,
- ...Manipulator.getDataAttributes(this._element),
- ...(typeof config === 'object' && config ? config : {})
- };
- typeCheckConfig(NAME, config, this.constructor.DefaultType);
- return config;
- }
-
- _maybeScheduleHide() {
- if (!this._config.autohide) {
- return;
- }
-
- if (this._hasMouseInteraction || this._hasKeyboardInteraction) {
- return;
- }
-
- this._timeout = setTimeout(() => {
- this.hide();
- }, this._config.delay);
- }
-
- _onInteraction(event, isInteracting) {
- switch (event.type) {
- case 'mouseover':
- case 'mouseout':
- this._hasMouseInteraction = isInteracting;
- break;
-
- case 'focusin':
- case 'focusout':
- this._hasKeyboardInteraction = isInteracting;
- break;
- }
-
- if (isInteracting) {
- this._clearTimeout();
-
- return;
- }
-
- const nextElement = event.relatedTarget;
-
- if (this._element === nextElement || this._element.contains(nextElement)) {
- return;
- }
-
- this._maybeScheduleHide();
- }
-
- _setListeners() {
- EventHandler.on(this._element, EVENT_CLICK_DISMISS, SELECTOR_DATA_DISMISS, () => this.hide());
- EventHandler.on(this._element, EVENT_MOUSEOVER, event => this._onInteraction(event, true));
- EventHandler.on(this._element, EVENT_MOUSEOUT, event => this._onInteraction(event, false));
- EventHandler.on(this._element, EVENT_FOCUSIN, event => this._onInteraction(event, true));
- EventHandler.on(this._element, EVENT_FOCUSOUT, event => this._onInteraction(event, false));
- }
-
- _clearTimeout() {
- clearTimeout(this._timeout);
- this._timeout = null;
+ callback();
+ }
} // Static
static jQueryInterface(config) {
return this.each(function () {
- let data = Data.get(this, DATA_KEY);
-
- const _config = typeof config === 'object' && config;
-
- if (!data) {
- data = new Toast(this, _config);
- }
+ const data = Data.get(this, DATA_KEY$1) || new Tab$1(this);
if (typeof config === 'string') {
if (typeof data[config] === 'undefined') {
throw new TypeError(`No method named "${config}"`);
}
- data[config](this);
+ data[config]();
}
});
}
}
- /**
- * ------------------------------------------------------------------------
- * jQuery
- * ------------------------------------------------------------------------
- * add .Toast to jQuery only if jQuery is present
- */
-
-
- defineJQueryPlugin(Toast);
-
- /*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- class Popup extends HTMLElement {
- constructor() {
- super();
- }
- connectedCallback() {
- const options = {};
- this.modal = new Modal(this, options);
- if (!this.collapsed) {
- this.show();
- }
- }
- disconnectedCallback() {
- this.hide();
- this.modal.dispose();
- }
- show(behaviorMode) {
- console.log("show");
- if (behaviorMode == null || behaviorMode == BehaviorMode.client) {
- this.modal.show();
- }
- }
- hide(behaviorMode) {
- console.log("hide");
- if (behaviorMode == null || behaviorMode == BehaviorMode.client) {
- this.modal.hide();
- }
- }
- get collapsed() {
- return JSON.parse(Collapse.findHidden(this).value);
- }
- }
- document.addEventListener("tobago.init", function (event) {
- if (window.customElements.get("tobago-popup") == null) {
- window.customElements.define("tobago-popup", Popup);
- }
- });
- class Collapse {
- static findHidden(element) {
- const rootNode = element.getRootNode();
- return rootNode.getElementById(element.id + "::collapse");
- }
- }
- Collapse.execute = function (operation, target, behaviorMode) {
- const hidden = Collapse.findHidden(target);
- let newCollapsed;
- switch (operation) {
- case CollapseOperation.hide:
- newCollapsed = true;
- break;
- case CollapseOperation.show:
- newCollapsed = false;
- break;
- default:
- console.error("unknown operation: '%s'", operation);
- }
- if (newCollapsed) {
- if (target instanceof Popup) {
- target.hide(behaviorMode);
- }
- else {
- target.classList.add("tobago-collapsed");
- }
- }
- else {
- if (target instanceof Popup) {
- target.show(behaviorMode);
- }
- else {
- target.classList.remove("tobago-collapsed");
- }
- }
- hidden.value = newCollapsed;
- };
-
- /*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- class Behavior extends HTMLElement {
- constructor() {
- super();
- }
- connectedCallback() {
- switch (this.event) {
- case "load": // this is a special case, because the "load" is too late now.
- this.callback();
- break;
- case "resize":
- document.body.addEventListener(this.event, this.callback.bind(this));
- break;
- default:
- const eventElement = this.eventElement;
- if (eventElement) {
- eventElement.addEventListener(this.event, this.callback.bind(this));
- }
- else {
- // if the clientId doesn't exists in DOM, it's probably the id of tobago-behavior custom element
- this.parentElement.addEventListener(this.event, this.callback.bind(this));
- // todo: not sure if this warning can be removed;
- console.warn("Can't find an element for the event. Use parentElement instead.", this);
- }
- }
- }
- callback(event) {
- if (this.collapseOperation && this.collapseTarget) {
- const rootNode = this.getRootNode();
- Collapse.execute(this.collapseOperation, rootNode.getElementById(this.collapseTarget), this.mode);
- }
- switch (this.mode) {
- case BehaviorMode.ajax:
- if (this.render) {
- // prepare overlay for all by AJAX reloaded elements
- const partialIds = this.render.split(" ");
- for (let i = 0; i < partialIds.length; i++) {
- const partialElement = document.getElementById(partialIds[i]);
- if (partialElement) {
- new Overlay(partialElement, true);
- }
- else {
- console.warn("No element found by id='%s' for overlay!", partialIds[i]);
- }
- }
- }
- jsf.ajax.request(this.actionElement, event, {
- "javax.faces.behavior.event": this.event,
- execute: this.execute,
- render: this.render
- });
- break;
- case BehaviorMode.full:
- setTimeout(this.submit.bind(this), this.delay);
- break;
- // nothing to do
- }
- }
- submit() {
- const id = this.fieldId != null ? this.fieldId : this.clientId;
- CommandHelper.submitAction(this, id, this.decoupled, this.target);
- }
- get mode() {
- if (this.render || this.execute) {
- return BehaviorMode.ajax;
- }
- else if (!this.omit) {
- return BehaviorMode.full;
- }
- else {
- return BehaviorMode.client;
- }
- }
- get event() {
- return this.getAttribute("event");
- }
- set event(event) {
- this.setAttribute("event", event);
- }
- get clientId() {
- return this.getAttribute("client-id");
- }
- set clientId(clientId) {
- this.setAttribute("client-id", clientId);
- }
- get fieldId() {
- return this.getAttribute("field-id");
- }
- set fieldId(fieldId) {
- this.setAttribute("field-id", fieldId);
- }
- get execute() {
- return this.getAttribute("execute");
- }
- set execute(execute) {
- this.setAttribute("execute", execute);
- }
- get render() {
- return this.getAttribute("render");
- }
- set render(render) {
- this.setAttribute("render", render);
- }
- get delay() {
- return parseInt(this.getAttribute("delay")) || 0;
- }
- set delay(delay) {
- this.setAttribute("delay", String(delay));
- }
- get omit() {
- return this.hasAttribute("omit");
- }
- set omit(omit) {
- if (omit) {
- this.setAttribute("omit", "");
- }
- else {
- this.removeAttribute("omit");
- }
- }
- get target() {
- return this.getAttribute("target");
- }
- set target(target) {
- this.setAttribute("target", target);
- }
- get confirmation() {
- return this.getAttribute("confirmation");
- }
- set confirmation(confirmation) {
- this.setAttribute("confirmation", confirmation);
+ /**
+ * ------------------------------------------------------------------------
+ * Data Api implementation
+ * ------------------------------------------------------------------------
+ */
+
+
+ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
+ if (['A', 'AREA'].includes(this.tagName)) {
+ event.preventDefault();
+ }
+
+ if (isDisabled(this)) {
+ return;
+ }
+
+ const data = Data.get(this, DATA_KEY$1) || new Tab$1(this);
+ data.show();
+ });
+ /**
+ * ------------------------------------------------------------------------
+ * jQuery
+ * ------------------------------------------------------------------------
+ * add .Tab to jQuery only if jQuery is present
+ */
+
+ defineJQueryPlugin(Tab$1);
+
+ /**
+ * --------------------------------------------------------------------------
+ * Bootstrap (v5.0.1): toast.js
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
+ * --------------------------------------------------------------------------
+ */
+ /**
+ * ------------------------------------------------------------------------
+ * Constants
+ * ------------------------------------------------------------------------
+ */
+
+ const NAME = 'toast';
+ const DATA_KEY = 'bs.toast';
+ const EVENT_KEY = `.${DATA_KEY}`;
+ const EVENT_CLICK_DISMISS = `click.dismiss${EVENT_KEY}`;
+ const EVENT_MOUSEOVER = `mouseover${EVENT_KEY}`;
+ const EVENT_MOUSEOUT = `mouseout${EVENT_KEY}`;
+ const EVENT_FOCUSIN = `focusin${EVENT_KEY}`;
+ const EVENT_FOCUSOUT = `focusout${EVENT_KEY}`;
+ const EVENT_HIDE = `hide${EVENT_KEY}`;
+ const EVENT_HIDDEN = `hidden${EVENT_KEY}`;
+ const EVENT_SHOW = `show${EVENT_KEY}`;
+ const EVENT_SHOWN = `shown${EVENT_KEY}`;
+ const CLASS_NAME_FADE = 'fade';
+ const CLASS_NAME_HIDE = 'hide';
+ const CLASS_NAME_SHOW = 'show';
+ const CLASS_NAME_SHOWING = 'showing';
+ const DefaultType = {
+ animation: 'boolean',
+ autohide: 'boolean',
+ delay: 'number'
+ };
+ const Default = {
+ animation: true,
+ autohide: true,
+ delay: 5000
+ };
+ const SELECTOR_DATA_DISMISS = '[data-bs-dismiss="toast"]';
+ /**
+ * ------------------------------------------------------------------------
+ * Class Definition
+ * ------------------------------------------------------------------------
+ */
+
+ class Toast extends BaseComponent {
+ constructor(element, config) {
+ super(element);
+ this._config = this._getConfig(config);
+ this._timeout = null;
+ this._hasMouseInteraction = false;
+ this._hasKeyboardInteraction = false;
+
+ this._setListeners();
+ } // Getters
+
+
+ static get DefaultType() {
+ return DefaultType;
+ }
+
+ static get Default() {
+ return Default;
+ }
+
+ static get NAME() {
+ return NAME;
+ } // Public
+
+
+ show() {
+ const showEvent = EventHandler.trigger(this._element, EVENT_SHOW);
+
+ if (showEvent.defaultPrevented) {
+ return;
}
- get collapseOperation() {
- return CollapseOperation[this.getAttribute("collapse-operation")];
+
+ this._clearTimeout();
+
+ if (this._config.animation) {
+ this._element.classList.add(CLASS_NAME_FADE);
}
- set collapseOperation(collapseOperation) {
- this.setAttribute("collapse-operation", CollapseOperation[collapseOperation]);
+
+ const complete = () => {
+ this._element.classList.remove(CLASS_NAME_SHOWING);
+
+ this._element.classList.add(CLASS_NAME_SHOW);
+
+ EventHandler.trigger(this._element, EVENT_SHOWN);
+
+ this._maybeScheduleHide();
+ };
+
+ this._element.classList.remove(CLASS_NAME_HIDE);
+
+ reflow(this._element);
+
+ this._element.classList.add(CLASS_NAME_SHOWING);
+
+ this._queueCallback(complete, this._element, this._config.animation);
+ }
+
+ hide() {
+ if (!this._element.classList.contains(CLASS_NAME_SHOW)) {
+ return;
}
- get collapseTarget() {
- return this.getAttribute("collapse-target");
+
+ const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE);
+
+ if (hideEvent.defaultPrevented) {
+ return;
}
- set collapseTarget(collapseTarget) {
- this.setAttribute("collapse-target", collapseTarget);
+
+ const complete = () => {
+ this._element.classList.add(CLASS_NAME_HIDE);
+
+ EventHandler.trigger(this._element, EVENT_HIDDEN);
+ };
+
+ this._element.classList.remove(CLASS_NAME_SHOW);
+
+ this._queueCallback(complete, this._element, this._config.animation);
+ }
+
+ dispose() {
+ this._clearTimeout();
+
+ if (this._element.classList.contains(CLASS_NAME_SHOW)) {
+ this._element.classList.remove(CLASS_NAME_SHOW);
}
- get decoupled() {
- return this.hasAttribute("decoupled");
+
+ super.dispose();
+ } // Private
+
+
+ _getConfig(config) {
+ config = { ...Default,
+ ...Manipulator.getDataAttributes(this._element),
+ ...(typeof config === 'object' && config ? config : {})
+ };
+ typeCheckConfig(NAME, config, this.constructor.DefaultType);
+ return config;
+ }
+
+ _maybeScheduleHide() {
+ if (!this._config.autohide) {
+ return;
}
- set decoupled(decoupled) {
- if (decoupled) {
- this.setAttribute("decoupled", "");
- }
- else {
- this.removeAttribute("decoupled");
- }
+
+ if (this._hasMouseInteraction || this._hasKeyboardInteraction) {
+ return;
}
- get actionElement() {
- const rootNode = this.getRootNode();
- const id = this.clientId;
- return rootNode.getElementById(id);
+
+ this._timeout = setTimeout(() => {
+ this.hide();
+ }, this._config.delay);
+ }
+
+ _onInteraction(event, isInteracting) {
+ switch (event.type) {
+ case 'mouseover':
+ case 'mouseout':
+ this._hasMouseInteraction = isInteracting;
+ break;
+
+ case 'focusin':
+ case 'focusout':
+ this._hasKeyboardInteraction = isInteracting;
+ break;
}
- get eventElement() {
- const rootNode = this.getRootNode();
- const id = this.fieldId ? this.fieldId : this.clientId;
- return rootNode.getElementById(id);
+
+ if (isInteracting) {
+ this._clearTimeout();
+
+ return;
}
- }
- document.addEventListener("tobago.init", function (event) {
- if (window.customElements.get("tobago-behavior") == null) {
- window.customElements.define("tobago-behavior", Behavior);
+
+ const nextElement = event.relatedTarget;
+
+ if (this._element === nextElement || this._element.contains(nextElement)) {
+ return;
}
- });
- class CommandHelper {
- }
- CommandHelper.isSubmit = false;
- /**
- * Submitting the page with specified actionId.
- * @param source
- * @param actionId
- * @param decoupled
- * @param target
- */
- CommandHelper.submitAction = function (source, actionId, decoupled = false, target) {
- Transport.request(function () {
- if (!CommandHelper.isSubmit) {
- CommandHelper.isSubmit = true;
- const form = document.getElementsByTagName("form")[0];
- const oldTarget = form.getAttribute("target");
- const sourceHidden = document.getElementById("javax.faces.source");
- sourceHidden.disabled = false;
- sourceHidden.value = actionId;
- if (target) {
- form.setAttribute("target", target);
- }
- const listenerOptions = {
- source: source,
- actionId: actionId /*,
- options: commandHelper*/
- };
- const onSubmitResult = CommandHelper.onSubmit(listenerOptions);
- if (onSubmitResult) {
- try {
- form.submit();
- // reset the source field after submit, to be prepared for possible next AJAX with decoupled=true
- sourceHidden.disabled = true;
- sourceHidden.value = "";
- }
- catch (e) {
- Overlay.destroy(Page.page(form).id);
- CommandHelper.isSubmit = false;
- alert("Submit failed: " + e); // XXX localization, better error handling
- }
- }
- if (target) {
- if (oldTarget) {
- form.setAttribute("target", oldTarget);
- }
- else {
- form.removeAttribute("target");
- }
- }
- if (target || decoupled || !onSubmitResult) {
- CommandHelper.isSubmit = false;
- Transport.pageSubmitted = false;
- }
- }
- if (!CommandHelper.isSubmit) {
- Transport.requestComplete(); // remove this from queue
+
+ this._maybeScheduleHide();
+ }
+
+ _setListeners() {
+ EventHandler.on(this._element, EVENT_CLICK_DISMISS, SELECTOR_DATA_DISMISS, () => this.hide());
+ EventHandler.on(this._element, EVENT_MOUSEOVER, event => this._onInteraction(event, true));
+ EventHandler.on(this._element, EVENT_MOUSEOUT, event => this._onInteraction(event, false));
+ EventHandler.on(this._element, EVENT_FOCUSIN, event => this._onInteraction(event, true));
+ EventHandler.on(this._element, EVENT_FOCUSOUT, event => this._onInteraction(event, false));
+ }
+
+ _clearTimeout() {
+ clearTimeout(this._timeout);
+ this._timeout = null;
+ } // Static
+
+
+ static jQueryInterface(config) {
+ return this.each(function () {
+ let data = Data.get(this, DATA_KEY);
+
+ const _config = typeof config === 'object' && config;
+
+ if (!data) {
+ data = new Toast(this, _config);
+ }
+
+ if (typeof config === 'string') {
+ if (typeof data[config] === 'undefined') {
+ throw new TypeError(`No method named "${config}"`);
}
- }, true);
- };
- CommandHelper.onSubmit = function (listenerOptions) {
- CommandHelper.isSubmit = true;
- const element = document.documentElement; // XXX this might be the wrong element in case of shadow dom
- Page.page(element).onBeforeUnload();
- return true;
- };
- class Transport {
+
+ data[config](this);
+ }
+ });
+ }
+
}
- Transport.requests = [];
- Transport.currentActionId = null;
- Transport.pageSubmitted = false;
/**
- * @return true if the request is queued.
+ * ------------------------------------------------------------------------
+ * jQuery
+ * ------------------------------------------------------------------------
+ * add .Toast to jQuery only if jQuery is present
*/
- Transport.request = function (req, submitPage, actionId) {
- let index = 0;
- if (submitPage) {
- Transport.pageSubmitted = true;
- index = Transport.requests.push(req);
- //console.debug('index = ' + index)
- }
- else if (!Transport.pageSubmitted) { // AJAX case
- console.debug("Current ActionId='%s' action='%s'", Transport.currentActionId, actionId);
- if (actionId && Transport.currentActionId === actionId) {
- console.info("Ignoring request");
- // If actionId equals currentActionId assume double request: do nothing
- return false;
- }
- index = Transport.requests.push(req);
- //console.debug('index = ' + index)
- Transport.currentActionId = actionId;
- }
- else {
- console.debug("else case");
- return false;
- }
- console.debug("index='%s'", index);
- if (index === 1) {
- console.info("Execute request!");
- Transport.startTime = new Date();
- Transport.requests[0]();
- }
- else {
- console.info("Request queued!");
- }
- return true;
- };
- // TBD XXX REMOVE is this called in non AJAX case?
- Transport.requestComplete = function () {
- Transport.requests.shift();
- Transport.currentActionId = null;
- console.debug("Request complete! Duration: %s ms; "
- + "Queue size : %s", new Date().getTime() - Transport.startTime.getTime(), Transport.requests.length);
- if (Transport.requests.length > 0) {
- console.debug("Execute request!");
- Transport.startTime = new Date();
- Transport.requests[0]();
- }
- };
+
+
+ defineJQueryPlugin(Toast);
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
@@ -10021,179 +9959,79 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
- class Page extends HTMLElement {
+ class Popup extends HTMLElement {
constructor() {
super();
}
- /**
- * The Tobago root element
- */
- static page(element) {
- const rootNode = element.getRootNode();
- const pages = rootNode.querySelectorAll("tobago-page");
- if (pages.length > 0) {
- if (pages.length >= 2) {
- console.warn("Found more than one tobago-page element!");
- }
- return pages.item(0);
- }
- console.warn("Found no tobago page!");
- return null;
- }
- /**
- * "a:b" -> "a"
- * "a:b:c" -> "a:b"
- * "a" -> null
- * null -> null
- * "a:b::sub-component" -> "a"
- * "a::sub-component:b" -> "a::sub-component" // should currently not happen in Tobago
- *
- * @param clientId The clientId of a component.
- * @return The clientId of the naming container.
- */
- static getNamingContainerId(clientId) {
- if (clientId == null || clientId.lastIndexOf(":") === -1) {
- return null;
- }
- let id = clientId;
- while (true) {
- const sub = id.lastIndexOf("::");
- if (sub == -1) {
- break;
- }
- if (sub + 1 == id.lastIndexOf(":")) {
- id = id.substring(0, sub);
- }
- else {
- break;
- }
- }
- return id.substring(0, id.lastIndexOf(":"));
- }
connectedCallback() {
- this.registerAjaxListener();
- this.querySelector("form").addEventListener("submit", CommandHelper.onSubmit);
- window.addEventListener("unload", this.onUnload.bind(this));
- this.addEventListener("keypress", (event) => {
- let code = event.which; // XXX deprecated
- if (code === 0) {
- code = event.keyCode;
- }
- if (code === 13) {
- const target = event.target;
- if (target.tagName === "A" || target.tagName === "BUTTON") {
- return;
- }
- if (target.tagName === "TEXTAREA") {
- if (!event.metaKey && !event.ctrlKey) {
- return;
- }
- }
- const name = target.getAttribute("name");
- let id = name ? name : target.id;
- while (id != null) {
- const command = document.querySelector(`[data-tobago-default='${id}']`);
- if (command) {
- command.dispatchEvent(new MouseEvent("click"));
- break;
- }
- id = Page.getNamingContainerId(id);
- }
- return false;
- }
- });
- }
- onBeforeUnload() {
- if (this.transition) {
- new Overlay(this);
- }
- this.transition = this.oldTransition;
- }
- /**
- * Wrapper function to call application generated onunload function
- */
- onUnload() {
- console.info("on onload");
- if (CommandHelper.isSubmit) {
- if (this.transition) {
- new Overlay(this);
- }
- this.transition = this.oldTransition;
+ const options = {};
+ this.modal = new Modal(this, options);
+ if (!this.collapsed) {
+ this.show();
}
}
- registerAjaxListener() {
- jsf.ajax.addOnEvent(this.jsfResponse.bind(this));
- }
- jsfResponse(event) {
- console.timeEnd("[tobago-jsf] jsf-ajax");
- console.time("[tobago-jsf] jsf-ajax");
- console.debug("[tobago-jsf] JSF event status: '%s'", event.status);
- if (event.status === "success") {
- event.responseXML.querySelectorAll("update").forEach(this.jsfResponseSuccess.bind(this));
- }
- else if (event.status === "complete") {
- event.responseXML.querySelectorAll("update").forEach(this.jsfResponseComplete.bind(this));
- }
+ disconnectedCallback() {
+ this.hide();
+ this.modal.dispose();
}
- jsfResponseSuccess(update) {
- const id = update.id;
- let rootNode = this.getRootNode();
- // XXX in case of "this" is tobago-page (e.g. ajax exception handling) rootNode is not set correctly???
- if (!rootNode.getElementById) {
- rootNode = document;
+ show(behaviorMode) {
+ console.log("show");
+ if (behaviorMode == null || behaviorMode == BehaviorMode.client) {
+ this.modal.show();
}
- console.info("[tobago-jsf] Update after jsf.ajax success: %s", id);
}
- jsfResponseComplete(update) {
- const id = update.id;
- if (JsfParameter.isJsfId(id)) {
- console.debug("[tobago-jsf] Update after jsf.ajax complete: #", id);
- Overlay.destroy(id);
+ hide(behaviorMode) {
+ console.log("hide");
+ if (behaviorMode == null || behaviorMode == BehaviorMode.client) {
+ this.modal.hide();
}
}
- get locale() {
- let locale = this.getAttribute("locale");
- if (!locale) {
- locale = document.documentElement.lang;
- }
- return locale;
+ get collapsed() {
+ return JSON.parse(Collapse.findHidden(this).value);
}
}
- document.addEventListener("tobago.init", (event) => {
- if (window.customElements.get("tobago-page") == null) {
- window.customElements.define("tobago-page", Page);
+ document.addEventListener("tobago.init", function (event) {
+ if (window.customElements.get("tobago-popup") == null) {
+ window.customElements.define("tobago-popup", Popup);
}
});
- class JsfParameter {
- static isJsfId(id) {
- switch (id) {
- case JsfParameter.VIEW_STATE:
- case JsfParameter.CLIENT_WINDOW:
- case JsfParameter.VIEW_ROOT:
- case JsfParameter.VIEW_HEAD:
- case JsfParameter.VIEW_BODY:
- case JsfParameter.RESOURCE:
- return false;
- default:
- return true;
- }
+ class Collapse {
+ static findHidden(element) {
+ const rootNode = element.getRootNode();
+ return rootNode.getElementById(element.id + "::collapse");
}
- static isJsfBody(id) {
- switch (id) {
- case JsfParameter.VIEW_ROOT:
- case JsfParameter.VIEW_BODY:
- return true;
- default:
- return false;
+ }
+ Collapse.execute = function (operation, target, behaviorMode) {
+ const hidden = Collapse.findHidden(target);
+ let newCollapsed;
+ switch (operation) {
+ case CollapseOperation.hide:
+ newCollapsed = true;
+ break;
+ case CollapseOperation.show:
+ newCollapsed = false;
+ break;
+ default:
+ console.error("unknown operation: '%s'", operation);
+ }
+ if (newCollapsed) {
+ if (target instanceof Popup) {
+ target.hide(behaviorMode);
+ }
+ else {
+ target.classList.add("tobago-collapsed");
}
}
- }
- JsfParameter.VIEW_STATE = "javax.faces.ViewState";
- JsfParameter.CLIENT_WINDOW = "javax.faces.ClientWindow";
- JsfParameter.VIEW_ROOT = "javax.faces.ViewRoot";
- JsfParameter.VIEW_HEAD = "javax.faces.ViewHead";
- JsfParameter.VIEW_BODY = "javax.faces.ViewBody";
- JsfParameter.RESOURCE = "javax.faces.Resource";
+ else {
+ if (target instanceof Popup) {
+ target.show(behaviorMode);
+ }
+ else {
+ target.classList.remove("tobago-collapsed");
+ }
+ }
+ hidden.value = newCollapsed;
+ };
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
@@ -10211,125 +10049,213 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
- class DatePicker extends HTMLElement {
+ class Behavior extends HTMLElement {
constructor() {
super();
}
connectedCallback() {
- if (this.type == "date") {
- console.debug("check input type=date support", DatePicker.SUPPORTS_INPUT_TYPE_DATE);
- if (!DatePicker.SUPPORTS_INPUT_TYPE_DATE) {
- this.setAttribute("type", "text");
- this.initVanillaDatePicker();
- }
+ switch (this.event) {
+ case "load": // this is a special case, because the "load" is too late now.
+ this.callback();
+ break;
+ case "resize":
+ document.body.addEventListener(this.event, this.callback.bind(this));
+ break;
+ default:
+ const eventElement = this.eventElement;
+ if (eventElement) {
+ eventElement.addEventListener(this.event, this.callback.bind(this));
+ }
+ else {
+ // if the clientId doesn't exists in DOM, it's probably the id of tobago-behavior custom element
+ this.parentElement.addEventListener(this.event, this.callback.bind(this));
+ // todo: not sure if this warning can be removed;
+ console.warn("Can't find an element for the event. Use parentElement instead.", this);
+ }
}
}
- initVanillaDatePicker() {
- var _a;
- const field = this.field;
- const locale = Page.page(this).locale;
- const i18n = this.i18n;
- i18n.titleFormat = "MM y"; // todo i18n
- i18n.format = this.pattern;
- Datepicker.locales[locale] = i18n;
- const options = {
- buttonClass: "btn",
- orientation: "auto",
- autohide: true,
- language: locale,
- todayBtn: this.todayButton,
- todayBtnMode: 1,
- minDate: this.min,
- maxDate: this.max,
- // todo readonly
- // todo show week numbers
- };
- const datepicker = new Datepicker(field, options);
- // XXX these listeners are needed as long as we have a solution for:
- // XXX https://github.com/mymth/vanillajs-datepicker/issues/13
- // XXX the 2nd point is missing the "normal" change event on the input element
- field.addEventListener("keyup", (event) => {
- // console.info("event -----> ", event.type);
- if (event.metaKey || event.key.length > 1 && event.key !== "Backspace" && event.key !== "Delete") {
- return;
+ callback(event) {
+ if (this.collapseOperation && this.collapseTarget) {
+ const rootNode = this.getRootNode();
+ Collapse.execute(this.collapseOperation, rootNode.getElementById(this.collapseTarget), this.mode);
+ }
+ switch (this.mode) {
+ case BehaviorMode.ajax:
+ if (this.render) {
+ // prepare overlay for all by AJAX reloaded elements
+ const partialIds = this.render.split(" ");
+ for (let i = 0; i < partialIds.length; i++) {
+ const partialElement = document.getElementById(partialIds[i]);
+ if (partialElement) {
+ new Overlay(partialElement, true);
+ }
+ else {
+ console.warn("No element found by id='%s' for overlay!", partialIds[i]);
+ }
+ }
+ }
+ jsf.ajax.request(this.actionElement, event, {
+ "javax.faces.behavior.event": this.event,
+ execute: this.execute,
+ render: this.render
+ });
+ break;
+ case BehaviorMode.full:
+ setTimeout(this.submit.bind(this), this.delay);
+ break;
+ // nothing to do
+ }
+ }
+ /**
+ * Submitting the page (= the form).
+ */
+ submit() {
+ console.info("Execute submit!");
+ const page = Page.page(this);
+ if (!page.submitActive) {
+ page.submitActive = true;
+ const actionId = this.fieldId != null ? this.fieldId : this.clientId;
+ const form = page.form;
+ const oldTarget = form.getAttribute("target");
+ const sourceHidden = document.getElementById("javax.faces.source");
+ sourceHidden.disabled = false;
+ sourceHidden.value = actionId;
+ if (this.target) {
+ form.setAttribute("target", this.target);
}
- // back up user's input when user types printable character or backspace/delete
- const target = event.target;
- target._oldValue = target.value;
- });
- field.addEventListener("focus", (event) => {
- // console.info("event -----> ", event.type);
- this.lastValue = field.value;
- });
- field.addEventListener("blur", (event) => {
- // console.info("event -----> ", event.type);
- const target = event.target;
- // no-op when user goes to another window or the input field has no backed-up value
- if (document.hasFocus() && target._oldValue !== undefined) {
- if (target._oldValue !== target.value) {
- target.datepicker.setDate(target._oldValue || { clear: true });
+ page.beforeSubmit();
+ try {
+ form.submit();
+ // reset the source field after submit, to be prepared for possible next AJAX with decoupled=true
+ sourceHidden.disabled = true;
+ sourceHidden.value = "";
+ }
+ catch (e) {
+ console.error("Submit failed!", e);
+ Overlay.destroy(page.id);
+ page.submitActive = false;
+ alert(`Submit failed: ${e}`); // XXX localization, better error handling
+ }
+ if (this.target) {
+ if (oldTarget) {
+ form.setAttribute("target", oldTarget);
+ }
+ else {
+ form.removeAttribute("target");
}
- delete target._oldValue;
}
- if (this.lastValue !== field.value) {
- field.dispatchEvent(new Event("change"));
+ if (this.target || this.decoupled) {
+ page.submitActive = false;
}
- });
- datepicker.element.addEventListener("changeDate", (event) => {
- // console.info("event -----> ", event.type);
- field.dispatchEvent(new Event("change"));
- });
- // simple solution for the picker: currently only open, not close is implemented
- (_a = this.querySelector(".tobago-date-picker")) === null || _a === void 0 ? void 0 : _a.addEventListener("click", (event) => {
- this.field.focus();
- });
+ }
}
- get todayButton() {
- return this.hasAttribute("today-button");
+ get mode() {
+ if (this.render || this.execute) {
+ return BehaviorMode.ajax;
+ }
+ else if (!this.omit) {
+ return BehaviorMode.full;
+ }
+ else {
+ return BehaviorMode.client;
+ }
}
- set todayButton(todayButton) {
- if (todayButton) {
- this.setAttribute("today-button", "");
+ get event() {
+ return this.getAttribute("event");
+ }
+ set event(event) {
+ this.setAttribute("event", event);
+ }
+ get clientId() {
+ return this.getAttribute("client-id");
+ }
+ set clientId(clientId) {
+ this.setAttribute("client-id", clientId);
+ }
+ get fieldId() {
+ return this.getAttribute("field-id");
+ }
+ set fieldId(fieldId) {
+ this.setAttribute("field-id", fieldId);
+ }
+ get execute() {
+ return this.getAttribute("execute");
+ }
+ set execute(execute) {
+ this.setAttribute("execute", execute);
+ }
+ get render() {
+ return this.getAttribute("render");
+ }
+ set render(render) {
+ this.setAttribute("render", render);
+ }
+ get delay() {
+ return parseInt(this.getAttribute("delay")) || 0;
+ }
+ set delay(delay) {
+ this.setAttribute("delay", String(delay));
+ }
+ get omit() {
+ return this.hasAttribute("omit");
+ }
+ set omit(omit) {
+ if (omit) {
+ this.setAttribute("omit", "");
}
else {
- this.removeAttribute("today-button");
+ this.removeAttribute("omit");
}
}
- get type() {
- var _a;
- return (_a = this.field) === null || _a === void 0 ? void 0 : _a.getAttribute("type");
+ get target() {
+ return this.getAttribute("target");
}
- get min() {
- var _a;
- return (_a = this.field) === null || _a === void 0 ? void 0 : _a.getAttribute("min");
+ set target(target) {
+ this.setAttribute("target", target);
}
- get max() {
- var _a;
- return (_a = this.field) === null || _a === void 0 ? void 0 : _a.getAttribute("max");
+ get confirmation() {
+ return this.getAttribute("confirmation");
}
- get pattern() {
- let pattern = this.getAttribute("pattern");
- return pattern ? pattern : "yyyy-mm-dd";
+ set confirmation(confirmation) {
+ this.setAttribute("confirmation", confirmation);
}
- get i18n() {
- const i18n = this.getAttribute("i18n");
- return i18n ? JSON.parse(i18n) : undefined;
+ get collapseOperation() {
+ return CollapseOperation[this.getAttribute("collapse-operation")];
}
- get field() {
+ set collapseOperation(collapseOperation) {
+ this.setAttribute("collapse-operation", CollapseOperation[collapseOperation]);
+ }
+ get collapseTarget() {
+ return this.getAttribute("collapse-target");
+ }
+ set collapseTarget(collapseTarget) {
+ this.setAttribute("collapse-target", collapseTarget);
+ }
+ get decoupled() {
+ return this.hasAttribute("decoupled");
+ }
+ set decoupled(decoupled) {
+ if (decoupled) {
+ this.setAttribute("decoupled", "");
+ }
+ else {
+ this.removeAttribute("decoupled");
+ }
+ }
+ get actionElement() {
const rootNode = this.getRootNode();
- return rootNode.getElementById(this.id + "::field");
+ const id = this.clientId;
+ return rootNode.getElementById(id);
+ }
+ get eventElement() {
+ const rootNode = this.getRootNode();
+ const id = this.fieldId ? this.fieldId : this.clientId;
+ return rootNode.getElementById(id);
}
}
- DatePicker.SUPPORTS_INPUT_TYPE_DATE = (() => {
- const input = document.createElement("input");
- input.setAttribute("type", "date");
- const thisIsNoDate = "this is not a date";
- input.setAttribute("value", thisIsNoDate);
- return input.value !== thisIsNoDate;
- })();
document.addEventListener("tobago.init", function (event) {
- if (window.customElements.get("tobago-date") == null) {
- window.customElements.define("tobago-date", DatePicker);
+ if (window.customElements.get("tobago-behavior") == null) {
+ window.customElements.define("tobago-behavior", Behavior);
}
});
diff --git a/tobago-theme/tobago-theme-standard/src/main/js/tobago.js.map b/tobago-theme/tobago-theme-standard/src/main/js/tobago.js.map
index 5b59908..97a2325 100644
--- a/tobago-theme/tobago-theme-standard/src/main/js/tobago.js.map
+++ b/tobago-theme/tobago-theme-standard/src/main/js/tobago.js.map
@@ -1 +1 @@
-{"version":3,"file":"tobago.js","sources":["../ts/tobago-utils.ts","../ts/tobago-bar.ts","../ts/tobago-behavior-mode.ts","../ts/tobago-collapsible-operation.ts","../ts/tobago-dropdown.ts","../../../../node_modules/vanillajs-datepicker/js/lib/utils.js","../../../../node_modules/vanillajs-datepicker/js/lib/date.js","../../../../node_modules/vanillajs-datepicker/js/lib/date-format.js","../../../../node_modules/vanillajs-datepicker/js/lib/event.js","../../../../node_modules/vanillajs-datepic [...]
\ No newline at end of file
+{"version":3,"file":"tobago.js","sources":["../ts/tobago-utils.ts","../ts/tobago-bar.ts","../ts/tobago-behavior-mode.ts","../ts/tobago-collapsible-operation.ts","../ts/tobago-dropdown.ts","../../../../node_modules/vanillajs-datepicker/js/lib/utils.js","../../../../node_modules/vanillajs-datepicker/js/lib/date.js","../../../../node_modules/vanillajs-datepicker/js/lib/date-format.js","../../../../node_modules/vanillajs-datepicker/js/lib/event.js","../../../../node_modules/vanillajs-datepic [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/js/tobago.min.js b/tobago-theme/tobago-theme-standard/src/main/js/tobago.min.js
index bba2635..80439db 100644
--- a/tobago-theme/tobago-theme-standard/src/main/js/tobago.min.js
+++ b/tobago-theme/tobago-theme-standard/src/main/js/tobago.min.js
@@ -1,8 +1,8 @@
-!function(e){"function"==typeof define&&define.amd?define(e):e()}((function(){"use strict";class e{static selfOrQuerySelectorAll(e,t){const s=new Array;e||(e=document.documentElement),e.matches(t)&&s.push(e);for(const i of e.querySelectorAll(t))s.push(i);return s}static getTransitionTime(e){const t=window.getComputedStyle(e);return 1e3*(Number.parseFloat(t.transitionDelay)+Number.parseFloat(t.transitionDuration))}}class t extends HTMLElement{constructor(){super(),this.CssClass={SHOW:"sho [...]
+!function(e){"function"==typeof define&&define.amd?define(e):e()}((function(){"use strict";class e{static selfOrQuerySelectorAll(e,t){const s=new Array;e||(e=document.documentElement),e.matches(t)&&s.push(e);for(const i of e.querySelectorAll(t))s.push(i);return s}static getTransitionTime(e){const t=window.getComputedStyle(e);return 1e3*(Number.parseFloat(t.transitionDelay)+Number.parseFloat(t.transitionDuration))}}class t extends HTMLElement{constructor(){super(),this.CssClass={SHOW:"sho [...]
/*!
* Bootstrap v5.0.1 (https://getbootstrap.com/)
* Copyright 2011-2021 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
-const ws={find:(e,t=document.documentElement)=>[].concat(...Element.prototype.querySelectorAll.call(t,e)),findOne:(e,t=document.documentElement)=>Element.prototype.querySelector.call(t,e),children:(e,t)=>[].concat(...e.children).filter((e=>e.matches(t))),parents(e,t){const s=[];let i=e.parentNode;for(;i&&i.nodeType===Node.ELEMENT_NODE&&3!==i.nodeType;)i.matches(t)&&s.push(i),i=i.parentNode;return s},prev(e,t){let s=e.previousElementSibling;for(;s;){if(s.matches(t))return[s];s=s.previousE [...]
+const Ss={find:(e,t=document.documentElement)=>[].concat(...Element.prototype.querySelectorAll.call(t,e)),findOne:(e,t=document.documentElement)=>Element.prototype.querySelector.call(t,e),children:(e,t)=>[].concat(...e.children).filter((e=>e.matches(t))),parents(e,t){const s=[];let i=e.parentNode;for(;i&&i.nodeType===Node.ELEMENT_NODE&&3!==i.nodeType;)i.matches(t)&&s.push(i),i=i.parentNode;return s},prev(e,t){let s=e.previousElementSibling;for(;s;){if(s.matches(t))return[s];s=s.previousE [...]
//# sourceMappingURL=tobago.min.js.map
diff --git a/tobago-theme/tobago-theme-standard/src/main/js/tobago.min.js.map b/tobago-theme/tobago-theme-standard/src/main/js/tobago.min.js.map
index 2d1ee05..0b48653 100644
--- a/tobago-theme/tobago-theme-standard/src/main/js/tobago.min.js.map
+++ b/tobago-theme/tobago-theme-standard/src/main/js/tobago.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"tobago.min.js","sources":["../ts/tobago-utils.ts","../ts/tobago-bar.ts","../ts/tobago-behavior-mode.ts","../ts/tobago-collapsible-operation.ts","../ts/tobago-dropdown.ts","../../../../node_modules/vanillajs-datepicker/js/lib/utils.js","../../../../node_modules/vanillajs-datepicker/js/lib/date.js","../../../../node_modules/vanillajs-datepicker/js/lib/date-format.js","../../../../node_modules/vanillajs-datepicker/js/lib/event.js","../../../../node_modules/vanillajs-dat [...]
\ No newline at end of file
+{"version":3,"file":"tobago.min.js","sources":["../ts/tobago-utils.ts","../ts/tobago-bar.ts","../ts/tobago-behavior-mode.ts","../ts/tobago-collapsible-operation.ts","../ts/tobago-dropdown.ts","../../../../node_modules/vanillajs-datepicker/js/lib/utils.js","../../../../node_modules/vanillajs-datepicker/js/lib/date.js","../../../../node_modules/vanillajs-datepicker/js/lib/date-format.js","../../../../node_modules/vanillajs-datepicker/js/lib/event.js","../../../../node_modules/vanillajs-dat [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/ts/tobago-command.ts b/tobago-theme/tobago-theme-standard/src/main/ts/tobago-command.ts
index 987bd1c..b51494f 100644
--- a/tobago-theme/tobago-theme-standard/src/main/ts/tobago-command.ts
+++ b/tobago-theme/tobago-theme-standard/src/main/ts/tobago-command.ts
@@ -87,9 +87,48 @@ class Behavior extends HTMLElement {
}
}
+ /**
+ * Submitting the page (= the form).
+ */
submit(): void {
- const id = this.fieldId != null ? this.fieldId : this.clientId;
- CommandHelper.submitAction(this, id, this.decoupled, this.target);
+ console.info("Execute submit!");
+ const page = Page.page(this);
+ if (!page.submitActive) {
+ page.submitActive = true;
+ const actionId = this.fieldId != null ? this.fieldId : this.clientId;
+ const form = page.form;
+ const oldTarget = form.getAttribute("target");
+ const sourceHidden = document.getElementById("javax.faces.source") as HTMLInputElement;
+ sourceHidden.disabled = false;
+ sourceHidden.value = actionId;
+ if (this.target) {
+ form.setAttribute("target", this.target);
+ }
+
+ page.beforeSubmit();
+
+ try {
+ form.submit();
+ // reset the source field after submit, to be prepared for possible next AJAX with decoupled=true
+ sourceHidden.disabled = true;
+ sourceHidden.value = "";
+ } catch (e) {
+ console.error("Submit failed!", e);
+ Overlay.destroy(page.id);
+ page.submitActive = false;
+ alert(`Submit failed: ${e}`); // XXX localization, better error handling
+ }
+ if (this.target) {
+ if (oldTarget) {
+ form.setAttribute("target", oldTarget);
+ } else {
+ form.removeAttribute("target");
+ }
+ }
+ if (this.target || this.decoupled) {
+ page.submitActive = false;
+ }
+ }
}
get mode(): BehaviorMode {
@@ -217,21 +256,6 @@ class Behavior extends HTMLElement {
const id = this.fieldId ? this.fieldId : this.clientId;
return rootNode.getElementById(id);
}
-
- /* XXX todo:
- get element(): HTMLElement {
- let e = this.parentElement;
- // XXX special case, using the row, but <tobago-behavior> can't be a child of <tr>
- while (e.matches("td.tobago-sheet-cell-markup-filler")
- // XXX fix position of <tobago-behavior> inside of input-group
- || e.matches(".input-group")
- || e.matches(".tobago-input-group-outer")) {
- e = e.parentElement;
- }
- return e;
- }
- */
-
}
document.addEventListener("tobago.init", function (event: Event): void {
@@ -239,130 +263,3 @@ document.addEventListener("tobago.init", function (event: Event): void {
window.customElements.define("tobago-behavior", Behavior);
}
});
-
-export class CommandHelper {
- static isSubmit: boolean = false;
-
- /**
- * Submitting the page with specified actionId.
- * @param source
- * @param actionId
- * @param decoupled
- * @param target
- */
- public static submitAction = function (
- source: HTMLElement, actionId: string, decoupled: boolean = false, target?: string): void {
-
- Transport.request(function (): void {
- if (!CommandHelper.isSubmit) {
- CommandHelper.isSubmit = true;
- const form = document.getElementsByTagName("form")[0] as HTMLFormElement;
- const oldTarget = form.getAttribute("target");
- const sourceHidden = document.getElementById("javax.faces.source") as HTMLInputElement;
- sourceHidden.disabled = false;
- sourceHidden.value = actionId;
- if (target) {
- form.setAttribute("target", target);
- }
- const listenerOptions = {
- source: source,
- actionId: actionId/*,
- options: commandHelper*/
- };
- const onSubmitResult = CommandHelper.onSubmit(listenerOptions);
- if (onSubmitResult) {
- try {
- form.submit();
- // reset the source field after submit, to be prepared for possible next AJAX with decoupled=true
- sourceHidden.disabled = true;
- sourceHidden.value = "";
- } catch (e) {
- Overlay.destroy(Page.page(form).id);
- CommandHelper.isSubmit = false;
- alert("Submit failed: " + e); // XXX localization, better error handling
- }
- }
- if (target) {
- if (oldTarget) {
- form.setAttribute("target", oldTarget);
- } else {
- form.removeAttribute("target");
- }
- }
- if (target || decoupled || !onSubmitResult) {
- CommandHelper.isSubmit = false;
- Transport.pageSubmitted = false;
- }
- }
- if (!CommandHelper.isSubmit) {
- Transport.requestComplete(); // remove this from queue
- }
- }, true);
- };
-
- static onSubmit = function (listenerOptions: any): boolean {
-
- CommandHelper.isSubmit = true;
-
- const element: HTMLElement = document.documentElement; // XXX this might be the wrong element in case of shadow dom
- Page.page(element).onBeforeUnload();
-
- return true;
- };
-
-}
-
-class Transport {
- static requests = [];
- static currentActionId = null;
- static pageSubmitted = false;
- static startTime: Date;
-
- /**
- * @return true if the request is queued.
- */
- static request = function (req: () => void, submitPage: boolean, actionId?: string): boolean {
- let index = 0;
- if (submitPage) {
- Transport.pageSubmitted = true;
- index = Transport.requests.push(req);
- //console.debug('index = ' + index)
- } else if (!Transport.pageSubmitted) { // AJAX case
- console.debug("Current ActionId='%s' action='%s'", Transport.currentActionId, actionId);
- if (actionId && Transport.currentActionId === actionId) {
- console.info("Ignoring request");
- // If actionId equals currentActionId assume double request: do nothing
- return false;
- }
- index = Transport.requests.push(req);
- //console.debug('index = ' + index)
- Transport.currentActionId = actionId;
- } else {
- console.debug("else case");
- return false;
- }
- console.debug("index='%s'", index);
- if (index === 1) {
- console.info("Execute request!");
- Transport.startTime = new Date();
- Transport.requests[0]();
- } else {
- console.info("Request queued!");
- }
- return true;
- };
-
- // TBD XXX REMOVE is this called in non AJAX case?
-
- static requestComplete = function (): void {
- Transport.requests.shift();
- Transport.currentActionId = null;
- console.debug("Request complete! Duration: %s ms; "
- + "Queue size : %s", new Date().getTime() - Transport.startTime.getTime(), Transport.requests.length);
- if (Transport.requests.length > 0) {
- console.debug("Execute request!");
- Transport.startTime = new Date();
- Transport.requests[0]();
- }
- };
-}
diff --git a/tobago-theme/tobago-theme-standard/src/main/ts/tobago-page.ts b/tobago-theme/tobago-theme-standard/src/main/ts/tobago-page.ts
index 7ad4611..0201ad2 100644
--- a/tobago-theme/tobago-theme-standard/src/main/ts/tobago-page.ts
+++ b/tobago-theme/tobago-theme-standard/src/main/ts/tobago-page.ts
@@ -15,13 +15,13 @@
* limitations under the License.
*/
-import {CommandHelper} from "./tobago-command";
import {Overlay} from "./tobago-overlay";
export class Page extends HTMLElement {
private transition: boolean;
private oldTransition: boolean;
+ submitActive: boolean = false;
/**
* The Tobago root element
@@ -78,7 +78,7 @@ export class Page extends HTMLElement {
this.registerAjaxListener();
- this.querySelector("form").addEventListener("submit", CommandHelper.onSubmit);
+ this.form.addEventListener("submit", this.beforeSubmit.bind(this));
window.addEventListener("unload", this.onUnload.bind(this));
@@ -112,7 +112,8 @@ export class Page extends HTMLElement {
});
}
- onBeforeUnload(): void {
+ beforeSubmit(): void {
+ this.submitActive = true;
if (this.transition) {
new Overlay(this);
}
@@ -123,8 +124,8 @@ export class Page extends HTMLElement {
* Wrapper function to call application generated onunload function
*/
onUnload(): void {
- console.info("on onload");
- if (CommandHelper.isSubmit) {
+ console.info("on unload");
+ if (Page.page(this).submitActive) {
if (this.transition) {
new Overlay(this);
}
@@ -154,7 +155,7 @@ export class Page extends HTMLElement {
if (!rootNode.getElementById) {
rootNode = document;
}
- console.info("[tobago-jsf] Update after jsf.ajax success: %s", id);
+ console.debug("[tobago-jsf] Update after jsf.ajax success: %s", id);
}
jsfResponseComplete(update: Element): void {
@@ -165,6 +166,10 @@ export class Page extends HTMLElement {
}
}
+ get form(): HTMLFormElement {
+ return this.querySelector("form");
+ }
+
get locale(): string {
let locale = this.getAttribute("locale");
if (!locale) {