reiern70 commented on code in PR #1634:
URL: https://github.com/apache/wicket/pull/1634#discussion_r4166935578


##########
wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/veil/wicket-veil.js:
##########
@@ -0,0 +1,324 @@
+/*
+ * 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.
+ */
+
+/*
+ * Veils the page, or a single component, while Ajax requests are in flight.
+ *
+ * The veil is transparent and only blocks the mouse. If a request is still 
running after the
+ * target's spinner delay, the veil gets the 'wicket-veil-busy' class, which 
shows a spinner;
+ * once shown, the spinner stays for at least the target's minimum time, so it 
does not flicker.
+ * A request carrying the extra parameter 'wicket_nb' is never veiled.
+ *
+ * A local veil can also be raised by the server, for a component it is about 
to update through a
+ * WebSocket push: a WebSocket text message {"wicketVeil":"show","id":"<markup 
id>"} raises it,
+ * Wicket.Veil.hide(id) - evaluated after the pushed update - or 
{"wicketVeil":"hide",...} lowers it.
+ */
+;(function (undefined) {
+       'use strict';
+
+       if (typeof(Wicket.Veil) === "object") {
+               return;
+       }
+
+       const NO_VEIL_PARAMETER = 'wicket_nb';
+       const VEIL_CLASS = 'wicket-veil';
+       const BUSY_CLASS = 'wicket-veil-busy';
+       const HOST_CLASS = 'wicket-veil-host';
+       const WEBSOCKET_MESSAGE_TOPIC = '/websocket/message';
+       const MESSAGE_PREFIX = '{"wicketVeil"';
+
+       let pageTarget = null;
+       let localTargets = {};
+       let subscribed = false;
+
+       function createTarget(id, options) {
+               return {
+                       id: id,
+                       delay: options.delay,
+                       minimum: options.minimum,
+                       count: 0,
+                       host: null,
+                       veil: null,
+                       shownAt: -1,
+                       spinnerTimer: null,
+                       hideTimer: null
+               };
+       }
+
+       function configure(target, options) {
+               target.delay = options.delay;
+               target.minimum = options.minimum;
+       }
+
+       function isOptedOut(attrs) {
+               const ep = attrs.ep;
+               if (Array.isArray(ep)) {
+                       return ep.some(function (parameter) {
+                               return parameter && parameter.name === 
NO_VEIL_PARAMETER;
+                       });
+               }
+               return !!ep && typeof(ep) === "object" &&
+                       Object.prototype.hasOwnProperty.call(ep, 
NO_VEIL_PARAMETER);
+       }
+
+       function findTarget(attrs) {
+               let node = attrs.c ? document.getElementById(attrs.c) : null;
+               for (; node && node !== document; node = node.parentNode) {
+                       if (node.id && localTargets[node.id]) {
+                               return localTargets[node.id];
+                       }
+               }
+               return pageTarget;
+       }
+
+       function dropStaleTargets() {
+               for (const id in localTargets) {
+                       if (Object.prototype.hasOwnProperty.call(localTargets, 
id) &&
+                               localTargets[id].count === 0 && 
!document.getElementById(id)) {
+                               delete localTargets[id];
+                       }
+               }
+       }
+
+       function hide(target) {
+               const clock = Wicket.Veil._clock;
+               clock.clearTimeout(target.spinnerTimer);
+               clock.clearTimeout(target.hideTimer);
+               target.spinnerTimer = null;
+               target.hideTimer = null;
+               target.shownAt = -1;
+               if (target.veil && target.veil.parentNode) {
+                       target.veil.parentNode.removeChild(target.veil);
+               }
+               if (target.host && target !== pageTarget) {
+                       target.host.classList.remove(HOST_CLASS);
+               }
+               target.veil = null;
+               target.host = null;
+       }
+
+       function show(target) {
+               const clock = Wicket.Veil._clock;
+               if (target.hideTimer !== null) {
+                       if (document.body.contains(target.veil)) {
+                               // the previous request's spinner is still on 
its minimum time: carry on with it
+                               clock.clearTimeout(target.hideTimer);
+                               target.hideTimer = null;
+                               return;
+                       }
+                       hide(target);
+               }
+
+               const host = target === pageTarget ? document.body : 
document.getElementById(target.id);
+               const veil = document.createElement('div');
+               veil.className = VEIL_CLASS;
+               if (target !== pageTarget) {
+                       host.classList.add(HOST_CLASS);
+               }
+               host.appendChild(veil);
+               target.host = host;
+               target.veil = veil;
+
+               target.spinnerTimer = clock.setTimeout(function () {
+                       target.spinnerTimer = null;
+                       veil.classList.add(BUSY_CLASS);
+                       target.shownAt = clock.now();
+               }, target.delay);
+       }
+
+       function reattach(target) {

Review Comment:
   Done in 7ec250509d: the veil subscribes to `/dom/node/added` and reattaches 
every local veil whose element was replaced, so it moves onto the new element 
at once, for an Ajax update as well as a pushed one; QUnit tests cover both.



##########
wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/veil/wicket-veil.js:
##########
@@ -0,0 +1,324 @@
+/*
+ * 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.
+ */
+
+/*
+ * Veils the page, or a single component, while Ajax requests are in flight.
+ *
+ * The veil is transparent and only blocks the mouse. If a request is still 
running after the
+ * target's spinner delay, the veil gets the 'wicket-veil-busy' class, which 
shows a spinner;
+ * once shown, the spinner stays for at least the target's minimum time, so it 
does not flicker.
+ * A request carrying the extra parameter 'wicket_nb' is never veiled.
+ *
+ * A local veil can also be raised by the server, for a component it is about 
to update through a
+ * WebSocket push: a WebSocket text message {"wicketVeil":"show","id":"<markup 
id>"} raises it,
+ * Wicket.Veil.hide(id) - evaluated after the pushed update - or 
{"wicketVeil":"hide",...} lowers it.
+ */
+;(function (undefined) {
+       'use strict';
+
+       if (typeof(Wicket.Veil) === "object") {
+               return;
+       }
+
+       const NO_VEIL_PARAMETER = 'wicket_nb';
+       const VEIL_CLASS = 'wicket-veil';
+       const BUSY_CLASS = 'wicket-veil-busy';
+       const HOST_CLASS = 'wicket-veil-host';
+       const WEBSOCKET_MESSAGE_TOPIC = '/websocket/message';
+       const MESSAGE_PREFIX = '{"wicketVeil"';
+
+       let pageTarget = null;
+       let localTargets = {};
+       let subscribed = false;
+
+       function createTarget(id, options) {
+               return {
+                       id: id,
+                       delay: options.delay,
+                       minimum: options.minimum,
+                       count: 0,
+                       host: null,
+                       veil: null,
+                       shownAt: -1,
+                       spinnerTimer: null,
+                       hideTimer: null
+               };
+       }
+
+       function configure(target, options) {
+               target.delay = options.delay;
+               target.minimum = options.minimum;
+       }
+
+       function isOptedOut(attrs) {
+               const ep = attrs.ep;
+               if (Array.isArray(ep)) {
+                       return ep.some(function (parameter) {
+                               return parameter && parameter.name === 
NO_VEIL_PARAMETER;
+                       });
+               }
+               return !!ep && typeof(ep) === "object" &&
+                       Object.prototype.hasOwnProperty.call(ep, 
NO_VEIL_PARAMETER);
+       }
+
+       function findTarget(attrs) {
+               let node = attrs.c ? document.getElementById(attrs.c) : null;
+               for (; node && node !== document; node = node.parentNode) {
+                       if (node.id && localTargets[node.id]) {
+                               return localTargets[node.id];
+                       }
+               }
+               return pageTarget;
+       }
+
+       function dropStaleTargets() {
+               for (const id in localTargets) {
+                       if (Object.prototype.hasOwnProperty.call(localTargets, 
id) &&
+                               localTargets[id].count === 0 && 
!document.getElementById(id)) {
+                               delete localTargets[id];
+                       }
+               }
+       }
+
+       function hide(target) {
+               const clock = Wicket.Veil._clock;
+               clock.clearTimeout(target.spinnerTimer);
+               clock.clearTimeout(target.hideTimer);
+               target.spinnerTimer = null;
+               target.hideTimer = null;
+               target.shownAt = -1;
+               if (target.veil && target.veil.parentNode) {
+                       target.veil.parentNode.removeChild(target.veil);
+               }
+               if (target.host && target !== pageTarget) {
+                       target.host.classList.remove(HOST_CLASS);
+               }
+               target.veil = null;
+               target.host = null;
+       }
+
+       function show(target) {
+               const clock = Wicket.Veil._clock;
+               if (target.hideTimer !== null) {
+                       if (document.body.contains(target.veil)) {
+                               // the previous request's spinner is still on 
its minimum time: carry on with it
+                               clock.clearTimeout(target.hideTimer);
+                               target.hideTimer = null;
+                               return;
+                       }
+                       hide(target);
+               }
+
+               const host = target === pageTarget ? document.body : 
document.getElementById(target.id);
+               const veil = document.createElement('div');
+               veil.className = VEIL_CLASS;
+               if (target !== pageTarget) {
+                       host.classList.add(HOST_CLASS);
+               }
+               host.appendChild(veil);
+               target.host = host;
+               target.veil = veil;
+
+               target.spinnerTimer = clock.setTimeout(function () {
+                       target.spinnerTimer = null;
+                       veil.classList.add(BUSY_CLASS);
+                       target.shownAt = clock.now();
+               }, target.delay);
+       }
+
+       function reattach(target) {
+               if (target === pageTarget || !target.veil || 
document.body.contains(target.veil)) {
+                       return;
+               }
+               const host = document.getElementById(target.id);
+               if (host) {
+                       host.classList.add(HOST_CLASS);
+                       host.appendChild(target.veil);
+                       target.host = host;
+               }
+       }
+
+       function release(target) {
+               const clock = Wicket.Veil._clock;
+               if (target.shownAt < 0) {
+                       hide(target);
+                       return;
+               }
+               const remaining = target.minimum - (clock.now() - 
target.shownAt);
+               if (remaining > 0) {
+                       // the update may have replaced the element, and the 
veil with it
+                       reattach(target);
+                       target.hideTimer = clock.setTimeout(function () {
+                               hide(target);
+                       }, remaining);
+               } else {
+                       hide(target);
+               }
+       }
+
+       function acquire(target) {
+               target.count++;
+               if (target.count === 1) {
+                       show(target);
+               }
+       }
+
+       function releaseOne(target) {
+               if (target.count > 0) {
+                       target.count--;
+                       if (target.count === 0) {
+                               release(target);
+                       }
+               }
+       }
+
+       function onBeforeSend(jqEvent, attrs) {
+               if (!attrs || isOptedOut(attrs)) {
+                       return;
+               }
+               dropStaleTargets();
+               const target = findTarget(attrs);
+               if (target === null) {
+                       return;
+               }
+               attrs.wicketVeil = target;
+               acquire(target);
+       }
+
+       function onDone(jqEvent, attrs) {

Review Comment:
   Done in 7ec250509d with the first option: both engines now pass 
`context.isRedirecting` to `/ajax/call/done` as a boolean after `attrs` 
(documented in the guide, with core QUnit tests), the veil stays up on a 
redirect, and it is lowered on a `pageshow` with `persisted` so a page restored 
from the back-forward cache is not left veiled.



##########
wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/veil/wicket-veil.js:
##########
@@ -0,0 +1,324 @@
+/*
+ * 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.
+ */
+
+/*
+ * Veils the page, or a single component, while Ajax requests are in flight.
+ *
+ * The veil is transparent and only blocks the mouse. If a request is still 
running after the
+ * target's spinner delay, the veil gets the 'wicket-veil-busy' class, which 
shows a spinner;
+ * once shown, the spinner stays for at least the target's minimum time, so it 
does not flicker.
+ * A request carrying the extra parameter 'wicket_nb' is never veiled.
+ *
+ * A local veil can also be raised by the server, for a component it is about 
to update through a
+ * WebSocket push: a WebSocket text message {"wicketVeil":"show","id":"<markup 
id>"} raises it,
+ * Wicket.Veil.hide(id) - evaluated after the pushed update - or 
{"wicketVeil":"hide",...} lowers it.
+ */
+;(function (undefined) {
+       'use strict';
+
+       if (typeof(Wicket.Veil) === "object") {
+               return;
+       }
+
+       const NO_VEIL_PARAMETER = 'wicket_nb';
+       const VEIL_CLASS = 'wicket-veil';
+       const BUSY_CLASS = 'wicket-veil-busy';
+       const HOST_CLASS = 'wicket-veil-host';
+       const WEBSOCKET_MESSAGE_TOPIC = '/websocket/message';
+       const MESSAGE_PREFIX = '{"wicketVeil"';
+
+       let pageTarget = null;
+       let localTargets = {};
+       let subscribed = false;
+
+       function createTarget(id, options) {
+               return {
+                       id: id,
+                       delay: options.delay,
+                       minimum: options.minimum,
+                       count: 0,
+                       host: null,
+                       veil: null,
+                       shownAt: -1,
+                       spinnerTimer: null,
+                       hideTimer: null
+               };
+       }
+
+       function configure(target, options) {
+               target.delay = options.delay;
+               target.minimum = options.minimum;
+       }
+
+       function isOptedOut(attrs) {
+               const ep = attrs.ep;
+               if (Array.isArray(ep)) {
+                       return ep.some(function (parameter) {
+                               return parameter && parameter.name === 
NO_VEIL_PARAMETER;
+                       });
+               }
+               return !!ep && typeof(ep) === "object" &&
+                       Object.prototype.hasOwnProperty.call(ep, 
NO_VEIL_PARAMETER);
+       }
+
+       function findTarget(attrs) {
+               let node = attrs.c ? document.getElementById(attrs.c) : null;
+               for (; node && node !== document; node = node.parentNode) {
+                       if (node.id && localTargets[node.id]) {
+                               return localTargets[node.id];
+                       }
+               }
+               return pageTarget;
+       }
+
+       function dropStaleTargets() {
+               for (const id in localTargets) {
+                       if (Object.prototype.hasOwnProperty.call(localTargets, 
id) &&
+                               localTargets[id].count === 0 && 
!document.getElementById(id)) {
+                               delete localTargets[id];
+                       }
+               }
+       }
+
+       function hide(target) {
+               const clock = Wicket.Veil._clock;
+               clock.clearTimeout(target.spinnerTimer);
+               clock.clearTimeout(target.hideTimer);
+               target.spinnerTimer = null;
+               target.hideTimer = null;
+               target.shownAt = -1;
+               if (target.veil && target.veil.parentNode) {
+                       target.veil.parentNode.removeChild(target.veil);
+               }
+               if (target.host && target !== pageTarget) {
+                       target.host.classList.remove(HOST_CLASS);
+               }
+               target.veil = null;
+               target.host = null;
+       }
+
+       function show(target) {
+               const clock = Wicket.Veil._clock;
+               if (target.hideTimer !== null) {
+                       if (document.body.contains(target.veil)) {
+                               // the previous request's spinner is still on 
its minimum time: carry on with it
+                               clock.clearTimeout(target.hideTimer);
+                               target.hideTimer = null;
+                               return;
+                       }
+                       hide(target);
+               }
+
+               const host = target === pageTarget ? document.body : 
document.getElementById(target.id);
+               const veil = document.createElement('div');
+               veil.className = VEIL_CLASS;
+               if (target !== pageTarget) {
+                       host.classList.add(HOST_CLASS);
+               }
+               host.appendChild(veil);
+               target.host = host;
+               target.veil = veil;
+
+               target.spinnerTimer = clock.setTimeout(function () {
+                       target.spinnerTimer = null;
+                       veil.classList.add(BUSY_CLASS);
+                       target.shownAt = clock.now();
+               }, target.delay);
+       }
+
+       function reattach(target) {
+               if (target === pageTarget || !target.veil || 
document.body.contains(target.veil)) {
+                       return;
+               }
+               const host = document.getElementById(target.id);
+               if (host) {
+                       host.classList.add(HOST_CLASS);
+                       host.appendChild(target.veil);
+                       target.host = host;
+               }
+       }
+
+       function release(target) {
+               const clock = Wicket.Veil._clock;
+               if (target.shownAt < 0) {
+                       hide(target);
+                       return;
+               }
+               const remaining = target.minimum - (clock.now() - 
target.shownAt);
+               if (remaining > 0) {
+                       // the update may have replaced the element, and the 
veil with it
+                       reattach(target);
+                       target.hideTimer = clock.setTimeout(function () {
+                               hide(target);
+                       }, remaining);
+               } else {
+                       hide(target);
+               }
+       }
+
+       function acquire(target) {
+               target.count++;
+               if (target.count === 1) {
+                       show(target);
+               }
+       }
+
+       function releaseOne(target) {
+               if (target.count > 0) {
+                       target.count--;
+                       if (target.count === 0) {
+                               release(target);
+                       }
+               }
+       }
+
+       function onBeforeSend(jqEvent, attrs) {
+               if (!attrs || isOptedOut(attrs)) {
+                       return;
+               }
+               dropStaleTargets();
+               const target = findTarget(attrs);
+               if (target === null) {
+                       return;
+               }
+               attrs.wicketVeil = target;
+               acquire(target);
+       }
+
+       function onDone(jqEvent, attrs) {
+               const target = attrs && attrs.wicketVeil;
+               if (!target) {
+                       return;
+               }
+               delete attrs.wicketVeil;
+               releaseOne(target);
+       }
+
+       function onWebSocketMessage(jqEvent, message) {
+               if (typeof(message) !== "string" || 
message.indexOf(MESSAGE_PREFIX) !== 0) {
+                       return;
+               }
+               let command;
+               try {
+                       command = JSON.parse(message);
+               } catch (e) {
+                       return;
+               }
+               if (command.wicketVeil === 'show') {
+                       Wicket.Veil.show(command.id);
+               } else if (command.wicketVeil === 'hide') {
+                       Wicket.Veil.hide(command.id);
+               }
+       }
+
+       function subscribe() {
+               if (subscribed === false) {
+                       subscribed = true;
+                       
Wicket.Event.subscribe(Wicket.Event.Topic.AJAX_CALL_BEFORE_SEND, onBeforeSend);
+                       
Wicket.Event.subscribe(Wicket.Event.Topic.AJAX_CALL_DONE, onDone);
+                       Wicket.Event.subscribe(WEBSOCKET_MESSAGE_TOPIC, 
onWebSocketMessage);
+               }
+       }
+
+       Wicket.Veil = {
+
+               /**
+                * Veils the whole page during every Ajax request that no local 
veil claims.
+                *
+                * @param options {Object} - 'delay': milliseconds before the 
spinner shows,
+                *      'minimum': milliseconds the spinner stays once shown
+                */
+               page: function (options) {
+                       subscribe();
+                       if (pageTarget === null) {
+                               pageTarget = createTarget(null, options);
+                       } else {
+                               configure(pageTarget, options);
+                       }
+               },
+
+               /**
+                * Veils only the element with the given id, during the Ajax 
requests fired by
+                * components inside it.
+                *
+                * @param id {String} - the markup id of the element to veil
+                * @param options {Object} - as for page()
+                */
+               local: function (id, options) {
+                       subscribe();
+                       const target = localTargets[id];
+                       if (target) {
+                               configure(target, options);
+                       } else {
+                               localTargets[id] = createTarget(id, options);
+                       }
+               },
+
+               /**
+                * Raises the local veil registered for the given id, as an 
Ajax request from inside it
+                * would, with the same timings. Each call has to be matched by 
a call to hide().
+                *
+                * @param id {String} - the markup id of a component with a 
local veil
+                */
+               show: function (id) {
+                       const target = localTargets[id];
+                       if (target && document.getElementById(id)) {
+                               acquire(target);
+                       }
+               },
+
+               /**
+                * Lowers the local veil raised by show(), respecting the 
spinner's minimum time. Calls
+                * without a matching show() are ignored.
+                *
+                * @param id {String} - the markup id of a component with a 
local veil
+                */
+               hide: function (id) {

Review Comment:
   Done in 7ec250509d: server `show()`/`hide()` are counted in `raised`, so an 
unmatched hide is ignored and leaves the veil of a running request alone.



##########
wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/veil/wicket-veil.js:
##########
@@ -0,0 +1,324 @@
+/*
+ * 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.
+ */
+
+/*
+ * Veils the page, or a single component, while Ajax requests are in flight.
+ *
+ * The veil is transparent and only blocks the mouse. If a request is still 
running after the
+ * target's spinner delay, the veil gets the 'wicket-veil-busy' class, which 
shows a spinner;
+ * once shown, the spinner stays for at least the target's minimum time, so it 
does not flicker.
+ * A request carrying the extra parameter 'wicket_nb' is never veiled.
+ *
+ * A local veil can also be raised by the server, for a component it is about 
to update through a
+ * WebSocket push: a WebSocket text message {"wicketVeil":"show","id":"<markup 
id>"} raises it,
+ * Wicket.Veil.hide(id) - evaluated after the pushed update - or 
{"wicketVeil":"hide",...} lowers it.
+ */
+;(function (undefined) {
+       'use strict';
+
+       if (typeof(Wicket.Veil) === "object") {
+               return;
+       }
+
+       const NO_VEIL_PARAMETER = 'wicket_nb';
+       const VEIL_CLASS = 'wicket-veil';
+       const BUSY_CLASS = 'wicket-veil-busy';
+       const HOST_CLASS = 'wicket-veil-host';
+       const WEBSOCKET_MESSAGE_TOPIC = '/websocket/message';
+       const MESSAGE_PREFIX = '{"wicketVeil"';
+
+       let pageTarget = null;
+       let localTargets = {};
+       let subscribed = false;
+
+       function createTarget(id, options) {
+               return {
+                       id: id,
+                       delay: options.delay,
+                       minimum: options.minimum,
+                       count: 0,
+                       host: null,
+                       veil: null,
+                       shownAt: -1,
+                       spinnerTimer: null,
+                       hideTimer: null
+               };
+       }
+
+       function configure(target, options) {
+               target.delay = options.delay;
+               target.minimum = options.minimum;
+       }
+
+       function isOptedOut(attrs) {
+               const ep = attrs.ep;
+               if (Array.isArray(ep)) {
+                       return ep.some(function (parameter) {
+                               return parameter && parameter.name === 
NO_VEIL_PARAMETER;
+                       });
+               }
+               return !!ep && typeof(ep) === "object" &&
+                       Object.prototype.hasOwnProperty.call(ep, 
NO_VEIL_PARAMETER);
+       }
+
+       function findTarget(attrs) {
+               let node = attrs.c ? document.getElementById(attrs.c) : null;
+               for (; node && node !== document; node = node.parentNode) {
+                       if (node.id && localTargets[node.id]) {
+                               return localTargets[node.id];
+                       }
+               }
+               return pageTarget;
+       }

Review Comment:
   Done in 7ec250509d: `findTarget` starts from `attrs.event.target` as core 
does and falls back to `attrs.c` when that element is no longer connected.



##########
wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/veil/wicket-veil.js:
##########
@@ -0,0 +1,324 @@
+/*
+ * 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.
+ */
+
+/*
+ * Veils the page, or a single component, while Ajax requests are in flight.
+ *
+ * The veil is transparent and only blocks the mouse. If a request is still 
running after the
+ * target's spinner delay, the veil gets the 'wicket-veil-busy' class, which 
shows a spinner;
+ * once shown, the spinner stays for at least the target's minimum time, so it 
does not flicker.
+ * A request carrying the extra parameter 'wicket_nb' is never veiled.
+ *
+ * A local veil can also be raised by the server, for a component it is about 
to update through a
+ * WebSocket push: a WebSocket text message {"wicketVeil":"show","id":"<markup 
id>"} raises it,
+ * Wicket.Veil.hide(id) - evaluated after the pushed update - or 
{"wicketVeil":"hide",...} lowers it.
+ */
+;(function (undefined) {
+       'use strict';
+
+       if (typeof(Wicket.Veil) === "object") {
+               return;
+       }
+
+       const NO_VEIL_PARAMETER = 'wicket_nb';
+       const VEIL_CLASS = 'wicket-veil';
+       const BUSY_CLASS = 'wicket-veil-busy';
+       const HOST_CLASS = 'wicket-veil-host';
+       const WEBSOCKET_MESSAGE_TOPIC = '/websocket/message';
+       const MESSAGE_PREFIX = '{"wicketVeil"';
+
+       let pageTarget = null;
+       let localTargets = {};

Review Comment:
   Done in 7ec250509d: `localTargets` is a `Map`, combined with the 
event-target lookup, with a test for ids named `toString` and `constructor`.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to