Skip to content

Instantly share code, notes, and snippets.

@svenmeier
Created September 25, 2019 07:54
Show Gist options
  • Save svenmeier/bd4375ab26e330617a834e1d53d86556 to your computer and use it in GitHub Desktop.
Save svenmeier/bd4375ab26e330617a834e1d53d86556 to your computer and use it in GitHub Desktop.
Wicket Ajax suspension
diff --git a/wicket-core/src/main/java/org/apache/wicket/ajax/res/js/wicket-ajax-jquery.js b/wicket-core/src/main/java/org/apache/wicket/ajax/res/js/wicket-ajax-jquery.js
index 70dd69d..bb952bc 100644
--- a/wicket-core/src/main/java/org/apache/wicket/ajax/res/js/wicket-ajax-jquery.js
+++ b/wicket-core/src/main/java/org/apache/wicket/ajax/res/js/wicket-ajax-jquery.js
@@ -933,16 +933,6 @@
*/
processEvaluation: function (context, node) {
- // used to match evaluation scripts which manually call FunctionsExecuter's notify() when ready
- var scriptWithIdentifierR = new RegExp("\\(function\\(\\)\\{([a-zA-Z_]\\w*)\\|((.|\\n)*)?\\}\\)\\(\\);$");
-
- /**
- * A regex used to split the text in (priority-)evaluate elements in the Ajax response
- * when there are scripts which require manual call of 'FunctionExecutor#notify()'
- * @type {RegExp}
- */
- var scriptSplitterR = new RegExp("\\(function\\(\\)\\{[\\s\\S]*?}\\)\\(\\);", 'gi');
-
// get the javascript body
var text = Wicket.DOM.text(node);
@@ -950,61 +940,54 @@
var steps = context.steps;
var log = Wicket.Log;
- var evaluateWithManualNotify = function (parameters, body) {
- return function(notify) {
- var toExecute = "(function(" + parameters + ") {" + body + "})";
-
- try {
- // do the evaluation in global scope
- var f = window.eval(toExecute);
- f(notify);
- } catch (exception) {
- log.error("Wicket.Ajax.Call.processEvaluation: Exception evaluating javascript: %s", text, exception);
- }
- return FunctionsExecuter.ASYNC;
- };
- };
-
var evaluate = function (script) {
return function(notify) {
- // just evaluate the javascript
+ // start with no suspension
+ Wicket.Ajax._suspended = 0;
+ Wicket.Ajax.suspend = function() {
+ // keep track of suspensions
+ Wicket.Ajax._suspended++;
+
+ var released = false;
+
+ return function() {
+ // relase only once
+ if (released == false) {
+ released = true;
+
+ // keep track of suspensions
+ Wicket.Ajax._suspended--;
+
+ if (Wicket.Ajax._suspended == 0) {
+ // last suspension released
+ notify();
+ }
+ }
+ }
+ };
+
+ // evaluate the javascript
try {
// do the evaluation in global scope
window.eval(script);
} catch (exception) {
log.error("Ajax.Call.processEvaluation: Exception evaluating javascript: %s", text, exception);
}
- // continue to next step
- return FunctionsExecuter.DONE;
+
+ if (Wicket.Ajax._suspended) {
+ // suspended
+ return FunctionsExecuter.ASYNC;
+ } else {
+ // no suspension
+ Wicket.Ajax._suspended = undefined;
+ Wicket.Ajax.suspend = undefined;
+
+ // continue to next step
+ return FunctionsExecuter.DONE;
+ }
};
};
-
- // test if the javascript is in form of identifier|code
- // if it is, we allow for letting the javascript decide when the rest of processing will continue
- // by invoking identifier();. This allows usage of some asynchronous/deferred logic before the next script
- // See WICKET-5039
- if (scriptWithIdentifierR.test(text)) {
- var scripts = [];
- var scr;
- while ( (scr = scriptSplitterR.exec(text) ) !== null ) {
- scripts.push(scr[0]);
- }
-
- for (var s = 0; s < scripts.length; s++) {
- var script = scripts[s];
- if (script) {
- var scriptWithIdentifier = script.match(scriptWithIdentifierR);
- if (scriptWithIdentifier) {
- steps.push(evaluateWithManualNotify(scriptWithIdentifier[1], scriptWithIdentifier[2]));
- }
- else {
- steps.push(evaluate(script));
- }
- }
- }
- } else {
- steps.push(evaluate(text));
- }
+ steps.push(evaluate(text));
},
// Adds a closure that processes a header contribution
diff --git a/wicket-core/src/main/java/org/apache/wicket/core/request/handler/IPartialPageRequestHandler.java b/wicket-core/src/main/java/org/apache/wicket/core/request/handler/IPartialPageRequestHandler.java
index da126a2..e7a2a52 100644
--- a/wicket-core/src/main/java/org/apache/wicket/core/request/handler/IPartialPageRequestHandler.java
+++ b/wicket-core/src/main/java/org/apache/wicket/core/request/handler/IPartialPageRequestHandler.java
@@ -64,15 +64,22 @@
void addChildren(MarkupContainer parent, Class<?> childCriteria);
/**
- * Adds javascript that will be evaluated on the client side after components are replaced
- *
- * <p>If the javascript needs to do something asynchronously (i.e. needs to use window.setTimeout(), for example
- * to do animations) then the following special syntax may be used: <code>someFunctionName|myJsLogic(someFunctionName);</code>.
- * Wicket will transform it to: <code>function(someFunctionName){myJsLogic(someFunctionName);}</code> and your code
- * is responsible to execute <em>someFunctionName()</em> when the asynchronous task is finished. Once <em>someFunctionName</em>
- * is executed the next appended script will be executed. <strong>Important</strong>: it is highly recommended to
- * execute your code in try/finally to make sure <em>someFunctionName</em> is executed even an error happens in
- * your code, otherwise all following scripts wont be executed.</p>
+ * Adds JavaScript that will be evaluated on the client side after components are replaced
+ * <p>
+ * If the JavaScript needs to do something asynchronously (i.e. needs to use window.setTimeout(), for example to do animations)
+ * then the global method <code>Wicket.Ajax.suspend()</code> may be used, e.g.:
+ * <pre>
+ * target.prependJavaScript("jQuery('#id').fadeOut(1000, Wicket.Ajax.suspend());");
+ * target.appendJavaScript("jQuery('#id').hide().fadeIn(1000, Wicket.Ajax.suspend());");
+ * </pre>
+ * <code>suspend()</code> returns a JavaScript function which your code is responsible to invoke when the
+ * asynchronous task has finished (in the example above done automatically by jQuery fading methods).
+ * Once this is done the next appended script will be executed.
+ * <p>
+ * <strong>Important</strong>: it is highly recommended to execute your code in try/finally to
+ * make sure the suspension is released even an error happens in your code, otherwise all
+ * following scripts wont be executed.
+ * </p>
*
* @param javascript
*/
diff --git a/wicket-examples/src/main/java/org/apache/wicket/examples/ajax/builtin/EffectsPage.html b/wicket-examples/src/main/java/org/apache/wicket/examples/ajax/builtin/EffectsPage.html
index 241c86e..ea078c7 100644
--- a/wicket-examples/src/main/java/org/apache/wicket/examples/ajax/builtin/EffectsPage.html
+++ b/wicket-examples/src/main/java/org/apache/wicket/examples/ajax/builtin/EffectsPage.html
@@ -21,7 +21,12 @@
counter 2: <span wicket:id="c2"></span>
<br/>
<br/>
-<a href="#" wicket:id="c1-link">counter1++ and shake</a>&#160;&#160;<a href="#" wicket:id="c2-link">counter2++ and highlight</a>
+counter 3: <span wicket:id="c3"></span>
+<br/>
+<br/>
+<a href="#" wicket:id="c1-link">counter1++ and shake</a>&#160;&#160;
+<a href="#" wicket:id="c2-link">counter2++ and highlight</a>&#160;&#160;
+<a href="#" wicket:id="c3-link">counter3++ and fade</a>
</div>
</wicket:extend>
</html>
\ No newline at end of file
diff --git a/wicket-examples/src/main/java/org/apache/wicket/examples/ajax/builtin/EffectsPage.java b/wicket-examples/src/main/java/org/apache/wicket/examples/ajax/builtin/EffectsPage.java
index e286584..4b48677 100644
--- a/wicket-examples/src/main/java/org/apache/wicket/examples/ajax/builtin/EffectsPage.java
+++ b/wicket-examples/src/main/java/org/apache/wicket/examples/ajax/builtin/EffectsPage.java
@@ -36,40 +36,7 @@
{
private int counter1 = 0;
private int counter2 = 0;
-
- /**
- * @return Value of counter1
- */
- public int getCounter1()
- {
- return counter1;
- }
-
- /**
- * @param counter1
- * New value for counter1
- */
- public void setCounter1(int counter1)
- {
- this.counter1 = counter1;
- }
-
- /**
- * @return Value for counter2
- */
- public int getCounter2()
- {
- return counter2;
- }
-
- /**
- * @param counter2
- * New value for counter2
- */
- public void setCounter2(int counter2)
- {
- this.counter2 = counter2;
- }
+ private int counter3 = 0;
/**
* Constructor
@@ -84,6 +51,10 @@
c2.setOutputMarkupId(true);
add(c2);
+ final Label c3 = new Label("c3", new PropertyModel<>(this, "counter3"));
+ c3.setOutputMarkupId(true);
+ add(c3);
+
add(new AjaxLink<Void>("c1-link")
{
@Override
@@ -91,8 +62,7 @@
{
counter1++;
target.add(c1);
- target.appendJavaScript(String.format("jQuery('#%s').effect('shake');",
- c1.getMarkupId()));
+ target.appendJavaScript(String.format("jQuery('#%s').effect('shake');", c1.getMarkupId()));
}
@Override
@@ -112,8 +82,29 @@
counter2++;
targetOptional.ifPresent(target -> {
target.add(c2);
- target.appendJavaScript(String.format("jQuery('#%s').effect('highlight');",
- c2.getMarkupId()));
+ target.appendJavaScript(String.format("jQuery('#%s').effect('highlight');", c2.getMarkupId()));
+ });
+ }
+
+ @Override
+ protected void updateAjaxAttributes(AjaxRequestAttributes attributes)
+ {
+ attributes.setChannel(new AjaxChannel("effects", Type.DROP));
+
+ super.updateAjaxAttributes(attributes);
+ }
+ });
+
+ add(new AjaxFallbackLink<Void>("c3-link")
+ {
+ @Override
+ public void onClick(Optional<AjaxRequestTarget> targetOptional)
+ {
+ counter3++;
+ targetOptional.ifPresent(target -> {
+ target.prependJavaScript(String.format("jQuery('#%s').fadeOut(1000, Wicket.Ajax.suspend());",c3.getMarkupId()));
+ target.add(c3);
+ target.appendJavaScript(String.format("jQuery('#%s').hide().fadeIn(1000, Wicket.Ajax.suspend());", c3.getMarkupId()));
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment