From 6e4137e67b0f6de0d0da6d7e54534c3b3ee35e71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dennis=20H=C3=B6rsch?= Date: Fri, 9 Oct 2015 20:03:20 +0200 Subject: [PATCH 1/2] Allow the Wicket ChannelManager to be used by plain Ajax requests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allows to replace the jQuery ajax function by one that uses Wicket’s channel manager to coordinate ajax requests in third party libraries like “select2”. --- .../wicket/ajax/res/js/wicket-ajax-jquery.js | 52 +++++ wicket-core/src/test/js/all.html | 1 + wicket-core/src/test/js/channeled-ajax.js | 186 ++++++++++++++++++ 3 files changed, 239 insertions(+) create mode 100644 wicket-core/src/test/js/channeled-ajax.js 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 db9d210aea8..e6a4466b314 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 @@ -1253,6 +1253,48 @@ } }; + Wicket.Ajax.ChanneledAjax = Wicket.Class.create(); + + Wicket.Ajax.ChanneledAjax.prototype = { + + initialize: function(channelName, channelType, channelManager, ajaxTransport) { + this.name = channelName; + this.type = channelType; + this.channel = this.name + "|" + this.type; + this.channelManager = channelManager; + this.transport = ajaxTransport? ajaxTransport : jQuery.ajax; + }, + + ajax: function(url, options) { + if (typeof url === "object") { + options = url; + url = undefined; + } + + options = options || {}; + + this._schedule(url, options); + }, + + _schedule: function(url, options) { + this.channelManager.schedule(this.channel, function() { + this.transport(url, this._withDoneOnComplete(options)); + }.bind(this)); + }, + + _withDoneOnComplete: function(options) { + var originalComplete = options.complete, + complete = { + complete: function(jqXHR, textStatus) { + this.channelManager.done(this.channel); + if (originalComplete) { + originalComplete(jqXHR, textStatus); + } + }.bind(this) + }; + return Wicket.merge(options, complete); + } + }; /** * Throttler's purpose is to make sure that ajax requests wont be fired too often. @@ -1968,6 +2010,16 @@ */ redirect: function(url) { window.location = url; + }, + + /** + * Creates an AJAX handler that uses the channelManager and $.ajax to coordinate AJAX requests. + * @param channelName + * @param channelType s (stack/queue), d (drop), a (active) + */ + channeledAjax: function(channelName, channelType) { + var ca = new Wicket.Ajax.ChanneledAjax(channelName, channelType, Wicket.channelManager); + return ca.ajax.bind(ca); } }, diff --git a/wicket-core/src/test/js/all.html b/wicket-core/src/test/js/all.html index 6a03f52fbfb..53c173ba1be 100644 --- a/wicket-core/src/test/js/all.html +++ b/wicket-core/src/test/js/all.html @@ -17,6 +17,7 @@ // uncomment for error reporting from the tests // Wicket.Ajax.DebugWindow.enabled = true; + diff --git a/wicket-core/src/test/js/channeled-ajax.js b/wicket-core/src/test/js/channeled-ajax.js new file mode 100644 index 00000000000..1637cf73221 --- /dev/null +++ b/wicket-core/src/test/js/channeled-ajax.js @@ -0,0 +1,186 @@ +/* + * 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. + */ + +/*global ok: true, start: true, test: true, asyncTest: true, equal: true, deepEqual: true, + QUnit: true, module: true, expect: true, stop: true */ + +jQuery(document).ready(function() { + "use strict"; + + module('Wicket.Ajax.ChanneledAjax'); + + test('schedules ajax request with the ChannelManager', function () { + + expect(2); + + var cm = new TestChannelManager(), + ajaxHandler = new TestAjaxHandler(), + + name = 'my-channel', + type = 'd', + + url = 'URL', + options = { + success: function() {} + }, + + channeledAjax = new Wicket.Ajax.ChanneledAjax(name, type, cm, ajaxHandler.ajax); + + channeledAjax.ajax(url, options); + + equal(cm.scheduled.channel, 'my-channel|d'); + notEqual(cm.scheduled.handler, undefined); + + }); + + test('schedules a callback that calls the used transport when executed', function () { + + expect(2); + + var cm = new TestChannelManager(), + ajaxHandler = new TestAjaxHandler(), + + name = 'my-channel', + type = 'd', + + url = 'URL', + options = { + success: function() { console.log("SUCCESS"); } + }, + + channeledAjax = new Wicket.Ajax.ChanneledAjax(name, type, cm, ajaxHandler.ajax); + + channeledAjax.ajax(url, options); + cm.scheduled.handler(); + + equal(ajaxHandler.calledWith.url, url); + equal(ajaxHandler.calledWith.options.success, options.success); + }); + + test('has a complete callback that is forwarded to the ajax transport and that calls done on the ChannelManager', function () { + + expect(2); + + var cm = new TestChannelManager(), + ajaxHandler = new TestAjaxHandler(), + + name = 'my-channel', + type = 'd', + + url = 'URL', + options = { + success: function() { console.log("SUCCESS"); } + }, + + channeledAjax = new Wicket.Ajax.ChanneledAjax(name, type, cm, ajaxHandler.ajax); + + channeledAjax.ajax(url, options); + cm.scheduled.handler(); + + notEqual(ajaxHandler.calledWith.options.complete, undefined); + + ajaxHandler.calledWith.options.complete(undefined, 'all-right'); + + equal(cm.doneChannel, 'my-channel|d'); + }); + + test('has a complete callback that calls the original complete callback', function () { + + expect(2); + + var cm = new TestChannelManager(), + ajaxHandler = new TestAjaxHandler(), + + name = 'my-channel', + type = 'd', + + originalCompleted = false, + + url = 'URL', + options = { + success: function() { console.log("SUCCESS"); }, + complete: function() { originalCompleted = true } + }, + + channeledAjax = new Wicket.Ajax.ChanneledAjax(name, type, cm, ajaxHandler.ajax); + + channeledAjax.ajax(url, options); + cm.scheduled.handler(); + + ajaxHandler.calledWith.options.complete(undefined, 'all-right'); + + equal(cm.doneChannel, 'my-channel|d'); + equal(originalCompleted, true); + }); + + + // Ajax tests are executed only when run with Web Server + if ( !QUnit.isLocal ) { + + asyncTest('can create a replacement handler for $.ajax that uses the default channelManager', function () { + + expect(5); + + var name = 'my-channel', + type = 'd', + + channeledAjax = Wicket.Ajax.channeledAjax(name, type), + + url = 'data/ajax/evaluationId.xml', + options = { + success: function() { + notEqual(Wicket.channelManager.channels[name], undefined); + ok(true, 'Success handler is executed'); + }, + + complete: function() { + equal(Wicket.channelManager.channels[name], undefined); + ok(true, 'Complete handler is executed'); + } + }; + + equal(Wicket.channelManager.channels[name], undefined); + + channeledAjax(url, options); + }); + } + + function TestChannelManager() { + + this.schedule = function (channel, fn) { + this.scheduled = { + channel: channel, + handler: fn + }; + }; + + this.done = function(channel) { + this.doneChannel = channel; + }; + + } + + function TestAjaxHandler() { + var self = this; + this.ajax = function(url, options) { + self.calledWith = { + url: url, + options: options + }; + } + } +}); From 35870b6fe18aaf3f97ac5b4db500201d31da5056 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dennis=20H=C3=B6rsch?= Date: Sun, 18 Oct 2015 11:41:21 +0200 Subject: [PATCH 2/2] Extracting url from options; merge url and options if both are objects --- .../wicket/ajax/res/js/wicket-ajax-jquery.js | 8 +- wicket-core/src/test/js/channeled-ajax.js | 76 +++++++++++++++++++ 2 files changed, 82 insertions(+), 2 deletions(-) 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 7ae602cfe26..e696933e69f 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 @@ -1285,8 +1285,12 @@ ajax: function(url, options) { if (typeof url === "object") { - options = url; - url = undefined; + if (typeof options === "object") { + options = Wicket.merge(url, options); + } else { + options = url; + } + url = options.u || options.url; } options = options || {}; diff --git a/wicket-core/src/test/js/channeled-ajax.js b/wicket-core/src/test/js/channeled-ajax.js index 1637cf73221..cdd08816be1 100644 --- a/wicket-core/src/test/js/channeled-ajax.js +++ b/wicket-core/src/test/js/channeled-ajax.js @@ -127,6 +127,82 @@ jQuery(document).ready(function() { equal(originalCompleted, true); }); + test('can be called only with options when the URL is named "url"', function () { + + expect(3); + + var cm = new TestChannelManager(), + ajaxHandler = new TestAjaxHandler(), + + name = 'my-channel', + type = 'd', + + options = { + url: 'TEST-URL', + success: function() { console.log("SUCCESS"); } + }, + + channeledAjax = new Wicket.Ajax.ChanneledAjax(name, type, cm, ajaxHandler.ajax); + + channeledAjax.ajax(options); + cm.scheduled.handler(); + + equal(ajaxHandler.calledWith.url, options.url); + equal(ajaxHandler.calledWith.options.url, options.url); + equal(ajaxHandler.calledWith.options.success, options.success); + }); + + test('can be called only with options when the URL is named "u"', function () { + + expect(3); + + var cm = new TestChannelManager(), + ajaxHandler = new TestAjaxHandler(), + + name = 'my-channel', + type = 'd', + + options = { + u: 'TEST-URL', + success: function() { console.log("SUCCESS"); } + }, + + channeledAjax = new Wicket.Ajax.ChanneledAjax(name, type, cm, ajaxHandler.ajax); + + channeledAjax.ajax(options); + cm.scheduled.handler(); + + equal(ajaxHandler.calledWith.url, options.u); + equal(ajaxHandler.calledWith.options.u, options.u); + equal(ajaxHandler.calledWith.options.success, options.success); + }); + + test('can be called with two option objects', function () { + + expect(3); + + var cm = new TestChannelManager(), + ajaxHandler = new TestAjaxHandler(), + + name = 'my-channel', + type = 'd', + + urlOptions = { + url: 'TEST-URL' + }, + options = { + success: function() { console.log("SUCCESS"); } + }, + + channeledAjax = new Wicket.Ajax.ChanneledAjax(name, type, cm, ajaxHandler.ajax); + + channeledAjax.ajax(urlOptions, options); + cm.scheduled.handler(); + + equal(ajaxHandler.calledWith.url, urlOptions.url); + equal(ajaxHandler.calledWith.options.url, urlOptions.url); + equal(ajaxHandler.calledWith.options.success, options.success); + }); // Ajax tests are executed only when run with Web Server if ( !QUnit.isLocal ) {