You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@wicket.apache.org by kn...@apache.org on 2008/08/19 00:57:39 UTC

svn commit: r686891 - /wicket/sandbox/knopp/experimental/wicket/src/main/java/org/apache/wicket/ajax/wicket-ajax-ng.js

Author: knopp
Date: Mon Aug 18 15:57:38 2008
New Revision: 686891

URL: http://svn.apache.org/viewvc?rev=686891&view=rev
Log:
just some crazy stuff

Modified:
    wicket/sandbox/knopp/experimental/wicket/src/main/java/org/apache/wicket/ajax/wicket-ajax-ng.js

Modified: wicket/sandbox/knopp/experimental/wicket/src/main/java/org/apache/wicket/ajax/wicket-ajax-ng.js
URL: http://svn.apache.org/viewvc/wicket/sandbox/knopp/experimental/wicket/src/main/java/org/apache/wicket/ajax/wicket-ajax-ng.js?rev=686891&r1=686890&r2=686891&view=diff
==============================================================================
--- wicket/sandbox/knopp/experimental/wicket/src/main/java/org/apache/wicket/ajax/wicket-ajax-ng.js (original)
+++ wicket/sandbox/knopp/experimental/wicket/src/main/java/org/apache/wicket/ajax/wicket-ajax-ng.js Mon Aug 18 15:57:38 2008
@@ -1,9 +1,21 @@
 (function() {
-		
+	
+	/*
+	 * YUI Shortcuts 
+	 */
+	var D = YAHOO.util.Dom;
+	var E = YAHOO.util.Event;
+	var L = YAHOO.lang;
+	var UA = YAHOO.env.ua;
+	
 	var oldWicket = window.Wicket;	
 	
 	Wicket = { };
 
+	/*
+	 * Wicket.$, Wicket.$$
+	 */
+	
 	Wicket.$ = function(arg) 
 	{
 		if (arg == null || typeof(arg) == "undefined") 
@@ -29,8 +41,10 @@
 		}
 	}
 
-	var bind = function(method, object) {		
-		return function() {
+	var bind = function(method, object) 
+	{		
+		return function() 
+		{
 			return method.apply(object, arguments);
 		}
 	}
@@ -42,37 +56,22 @@
 	 */
 	Wicket.$$ = function(element) 
 	{	
-		if (typeof(element) == "string") 
-		{		
-			element = Wicket.$(element);
-		}	
-		if (element == null || typeof(element) == "undefined" ||
-		    element.tagName == null || typeof(element.tagName) == "undefined") 
+		if (L.isString(element)) 
 		{
-		    return true;
+			return Wicket.$(element) != null;
+		}
+		else 
+		{	
+			return D.inDocument(element);
 		}
-		
-		var id = element.getAttribute('id');
-		if (typeof(id) == "undefined" || id == null || id == "")
-			return element.ownerDocument == document;
-		else
-			return document.getElementById(id) == element;
-	}
-	
-	var ua = YAHOO.env.ua;
-	
-	var DummyLogger = 
-	{
-		trace: function() { },
-		debug: function() { },
-		info: function() { 	},
-		error: function() { }
 	}
 	
-	var prepend = function(element, array)
+	/*
+	 * Utility
+	 */
+	var copyArray = function(array)
 	{
 		var res = new Array();
-		res.push(element);
 		for (var i = 0; i < array.length; ++i)
 		{
 			res.push(array[i]);
@@ -80,93 +79,214 @@
 		return res;
 	}
 	
+	var iterateArray = function(array, itemFunc)
+	{
+		if (L.isArray(array)) 
+		{
+			for(var i = 0; i < array.length; ++i)
+			{
+				var res = itemFunc(array[i]); 
+				if (res != null)
+				{
+					return res;
+				}
+			}
+		}
+		else if (!L.isUndefined(array) && !L.isNull(array))
+		{
+			return itemFunc(array);
+		}		
+		return null;
+	}
+	
+	/*
+	 * Logging
+	 */
+	var DummyLogger = 
+	{
+		trace: function() { },
+		debug: function() { },
+		info: function() { 	},
+		error: function() { }
+	}		
+	
 	var FirebugLogger =
 	{
-		trace: function()
+		arg: function(type, args)
 		{			
-			console.debug.apply(this, prepend("TRACE:", arguments));
+			var first = args[0];			
+			var a = copyArray(args).slice(1);
+			if (args.length > 1)
+			{
+				a.unshift("|");
+			}
+			a.unshift(first);
+			a.unshift("|");			
+			a.unshift(type);
+			return a;
+		},
+		trace: function()
+		{						
+			console.debug.apply(this, FirebugLogger.arg("TRACE", arguments));
 		},
 		debug: function() 
 		{
-			console.debug.apply(this, prepend("DEBUG:", arguments));
+			console.debug.apply(this, FirebugLogger.arg("DEBUG", arguments));
 		},
 		info: function() 
 		{
-			console.info.apply(this, prepend("INFO:", arguments));
+			console.info.apply(this, FirebugLogger.arg("INFO", arguments));
 		},
 		error: function() 
 		{
-			console.error.apply(this, prepend("ERROR:", arguments));
+			console.error.apply(this, FirebugLogger.arg("ERROR", arguments));
 		}
 	}
 	
 	var logger = DummyLogger;
 	
-	if (ua.gecko && typeof(console) !== "undefined" && logger === DummyLogger)
+	if (UA.gecko && typeof(console) !== "undefined" && logger === DummyLogger)
 	{
 		logger = FirebugLogger;
 	}	
 	
-	var logConfig = { trace: true, debug: true, info: true, error: true };
+	var logConfig = { trace: true, debug: true, info: true, error: true, "trace:GarbageCollector": false };
 	
-	var l = 
+	Wicket.Log  = 
 	{
 		trace: function()
 		{
-			if (logConfig.trace)
+			if (logConfig.trace && logConfig[arguments[0]] != false && logConfig["trace:" + arguments[0]] != false)
 				logger.trace.apply(this, arguments);
 		},
 		debug: function()
 		{
-			if (logConfig.debug)
+			if (logConfig.debug && logConfig[arguments[0]] != false && logConfig["debug:" + arguments[0]] != false)
 				logger.debug.apply(this, arguments);
 		},
 		info: function()
 		{
-			if (logConfig.info)
+			if (logConfig.info && logConfig[arguments[0]] != false && logConfig["info:" + arguments[0]] != false)
 				logger.info.apply(this, arguments);
 		},
 		error: function()
 		{
-			if (logConfig.error)
+			if (logConfig.error && logConfig[arguments[0]] != false && logConfig["error:" + arguments[0]] != false)
 				logger.error.apply(this, arguments);
+		},
+		setLogger: function(newLogger)
+		{
+			logger = newLogger;
+		},
+		getLogger: function()
+		{
+			return logger;
+		},
+		isDummyLogger: function()
+		{
+			return logger == DummyLogger;
+		},
+		getLogConfig: function() 
+		{
+			return logConfig;
 		}
 	};
 		
-	Wicket.Log = l;
+	// convenience shortcut
+	var log = Wicket.Log;
+	
+	/*
+	 * Garbage Collection (for removing event listeners from elements removed from DOM)
+	 */
 	
 	/**
 	 * YAHOO event cleanups the listeners only on page unload. However, if the page lives long
 	 * enough the elements removed from document that have listener attached cause IE GC not free the
 	 * memory. So we manually register each element with listener and then periodically check 
 	 * whether the element is still in document. If it's not the element's listeners are removed.
-	 */
-	var elementsWithListeners = new Array();
+	 */	
+	var GarbageCollector = function(purgeInterval) 
+	{
+		this.elementsWithListeners = new Array();
+		
+		// temporary array of elements being processed during purge
+		this.beingPurged = null;
+		
+		// count of purged elements (debug)
+		this.purgedCount = 0;
+		
+		this.purgeInterval = purgeInterval;
+		
+		window.setInterval(bind(this.purgeInactiveListeners, this), purgeInterval);
+	};
 
-	var purgeInactiveListeners = function() 
+	GarbageCollector.prototype = 
 	{
-		var c = 0;
-		var a = elementsWithListeners;
-		for (var i = 0; i < a.length; ++i) 
-		{
-			var e = a[i];
-			if (e != null && Wicket.$$(e) == false) 
-			{		
-				l.trace("Events: Purging listeners from element ", e);
-				E.purgeElement(e);
-				a[i] = null;
-				++c;
+		// periodically called to initiate the purging process
+		purgeInactiveListeners : function() 
+		{		
+			// if purge is in progress don't do anything
+			if (this.beingPurged != null) {
+				return;
 			}
-		}
-		if (c > 0)
+			
+			// the the elements
+			this.beingPurged = this.elementsWithListeners;
+			this.elementsWithListeners = new Array();
+			
+			log.trace("GarbageCollector", "Purge begin");
+			
+			this.purgedCount = 0;
+			
+			// start the process
+			this.purge();
+		},
+		
+		purge: function() 
 		{
-			l.debug("Purged listeners from " + c + " element(s) removed from the document.");
-		}		
-	};
-
-	window.setInterval(purgeInactiveListeners, 60000);	
+			if (this.beingPurged != null)
+			{
+				var done = 0;
+				
+				// it is necessary to limit amount of items being purged in one go otherwise
+				// IE will complain about script being slow
+				var max = 50;
+				
+				var a = this.beingPurged;
+				for (var i = 0; i < a.length && done < 50; ++i)
+				{
+					var e = a[i];
+					if (e != null)
+					{
+						++done;
+						if (!Wicket.$$(e)) {
+							E.purgeElement(e);
+							++this.purgedCount;
+						} else {
+							// element is still in document, return it
+							this.elementsWithListeners.push(e);
+						}
+						a[i] = null;
+					}			
+				}
+								
+				if (i == a.length)
+				{
+					// we are done with purging
+					this.beingPurged = null;
+					
+					log.trace("GarbageCollector", "Purge End; purged: " + this.purgedCount + ", total: " + this.elementsWithListeners.length);
+				}		
+				else
+				{
+					// not yet done, continue after 50ms
+					window.setTimeout(bind(this.purge, this), 50);
+				}
+			}
+		}	
+	}
 	
-	var E = YAHOO.util.Event;
+	var garbageCollector = new GarbageCollector(5000);
 	
 	var oldAddListener = E.addListener;
 	
@@ -176,17 +296,20 @@
 	 */
 	E.addListener = function(el)
 	{		
-		l.trace("Events: Adding event listeners", arguments);
+		log.trace("Events", "Adding event listeners", arguments);
 		oldAddListener.apply(this, arguments);
 		if (el !== window && el !== document)
 		{
-			var a = elementsWithListeners;
-			var i = a.length;
-			a[i] = Wicket.$(el);
+			var a = garbageCollector.elementsWithListeners;
+			a.push(Wicket.$(el));			
 		}
 	};
 	
-	E.addListener(window, "unload", function() { elementsWithListeners = null; } );
+	E.addListener(window, "unload", function() { garbageCollector = null; } );
+	
+	/*
+	 * Throttler
+	 */
 	
 	var ThrottlerEntry = function(func)
 	{
@@ -198,17 +321,11 @@
 	 * executed more often that the specified interval. Throttler can be used to reduce number
 	 * of AJAX requests. To match function with previously executed functions a (string) token
 	 * is used.
-	 * 
-	 * @param postponeTimerOnUpdate is an optional parameter. If it is set to true, then the timer is
-     *    reset each time the throttle function gets called. Use this behaviour if you want something
-     *    to happen at X milliseconds after the *last* call to throttle.
-     *    If the parameter is not set, or set to false, then the timer is not reset.
 	 */
-	var Throttler = function(postponeTimerOnUpdate)
+	var Throttler = function()
 	{
 		this.entries = { };
 		this.executionTimes = { };
-		this.postponeTimerOnUpdate = postponeTimerOnUpdate || false;
 	};
 	
 	Throttler.prototype = 
@@ -220,12 +337,20 @@
 		 * @param token - string token to match previously executed function with the one specified now
 		 * @param millis - how much to postpone the function after the last invocation
 		 * @param func - function
+		 * @param postponeTimerOnUpdate is an optional parameter. If it is set to true, then the timer is
+	     *    reset each time the throttle function gets called. Use this behavior if you want something
+	     *    to happen at X milliseconds after the *last* call to throttle.
+	     *    If the parameter is not set, or set to false, then the timer is not reset.
+	     *    This can be useful for throttling events based on keyboard input. The function can be called
+	     *    for example 2 seconds after the last key stroke.
 		 */
-		throttle: function(token, millis, func)
+		throttle: function(token, millis, func, postponeTimerOnUpdate)
 		{
+			postponeTimerOnUpdate = postponeTimerOnUpdate || false;
+
 			// check if throttling is necessary. thottling is always necessary when postponeTimerOnUpdate 
 		    // is true
-			if (!this.postponeTimerOnUpdate && this.checkLastExecutionTime(token, millis, func))
+			if (!postponeTimerOnUpdate && this.checkLastExecutionTime(token, millis, func))
 			{
 				return;
 			}
@@ -235,20 +360,20 @@
 				entry = new ThrottlerEntry(func);
 				entry.timeout = window.setTimeout(bind(function() { this.execute(token) }, this), millis);
 				this.entries[token] = entry;
-				l.trace("Throttler: Setting throttle, token:", token, ", millis:", millis, ", func:", func);
+				log.trace("Throttler", "Setting throttle, token:", token, ", millis:", millis, ", func:", func);
 			}
 			else
 			{
 				entry.func = func;
-				if (this.postponeTimerOnUpdate)
+				if (postponeTimerOnUpdate)
 				{
 					window.clearTimeout(entry.timeout);
 					entry.timeout = window.setTimeout(bind(this.execute, this), millis);
-					l.trace("Throttler: Postponing throttle, token:", token, ", millis:", millis, ", func:", func);
+					log.trace("Throttler", "Postponing throttle, token:", token, ", millis:", millis, ", func:", func);
 				}
 				else
 				{
-					l.trace("Throttler: Replacing throttle, token:", token, ", millis:", millis, ", func:", func);
+					log.trace("Throttler", "Replacing throttle, token:", token, ", millis:", millis, ", func:", func);
 				}
 			}
 		},
@@ -261,7 +386,7 @@
 			var now = new Date().getTime();
 			if (e == null || (e + millis) < now)
 			{
-				l.trace("Throttler: Executing function immediately, token:", token, ", millis:", millis, ", func:", func);
+				log.trace("Throttler", "Executing function immediately, token:", token, ", millis:", millis, ", func:", func);
 				this.executionTimes[token] = now;
 				this.entries[token] = null;
 				func();
@@ -279,7 +404,7 @@
 			if (entry != null)
 			{				
 				var f = entry.func;
-				l.trace("Invoking throttled function, token:", token, "func:", f);
+				log.trace("Throttler", "Invoking throttled function, token:", token, "func:", f);
 				this.entries[token] = null;
 				this.executionTimes[token] = new Date().getTime();
 				f();				
@@ -287,10 +412,19 @@
 		}
 	};
 	
-	Wicket.Throttler = Throttler;
+	var isNonEmpty = function(string) 
+	{
+		return L.isString(string) && string.length > 0;
+	}
+	
+	Wicket.Throttler = Throttler;	
+	
+	/*
+	 * AJAX
+	 */
 	
 	/**
-	 * An item in a RequestQueue. Item constructor get a map of configuration attribute. Each attribute 
+	 * An item in a RequestQueue. Item constructor get a map of configuration attributes. Each attribute 
 	 * can be either specified by a full name or by a shortcut.   
 	 * 
 	 * Possible attributes:
@@ -302,6 +436,9 @@
 	 *                                        for the component the attribute can contain a string 
 	 *                                        (Component#getMarkupId()). The string is then used to find
 	 *                                        the matching component on the server side.
+	 *                                        
+	 *                                        The element is also used as precondition. The element must 
+	 *                                        be part of the DOM tree otherwise the item will be ignored.
 	 *                                         
 	 *   f, formId               - String     Form ID if the AJAX request should submit a form or null
 	 *                                        if the request doesn't involve form submission
@@ -333,11 +470,9 @@
 	 *                                        Used to identify previous items (items with same token) that 
 	 *                                        will be removed when this item is added and removePrevious 
 	 *                                        is true. Also required when throttle attribute is used.
-	 *                                        Token is also used to invoke global precondition/success/error
-	 *                                        handlers that are registered for certain token(s).
 	 *        
 	 *   r, removePrevious       - Boolean    Optional. If there are previous items with same token in the 
-	 *                                        queue they will be removed if removePrevisious is true. This 
+	 *                                        queue they will be removed if removePrevious is true. This 
 	 *                                        can be useful when the items are added in queue faster
 	 *                                        than they are processed and only the latest request matters.
 	 *                                        An example of this could be periodically updated component.
@@ -350,17 +485,33 @@
 	 *                                        item per n milliseconds where n is the value of throttle 
 	 *                                        attribute. Useful to limit the number of AJAX requests that 
 	 *                                        are triggered by a user action such as typing into a text 
-	 *                                        field.    
+	 *                                        field. 
+	 *                                        Throttle attribute only applies when token is specified.   
+	 *                                        
+	 *   thp, throttlePostpone   - Boolean    Optional. Only applicable when throttle attribute is set.
+	 *                                        Defaults to false. Causes the throttle timer reset each time
+	 *                                        item with same token is being added to queue. 
+	 *                                        Example: Event is fired by user typing in a textfield. 
+	 *                                        Throttle value is 2000 (ms), throttle postpone is true.
+	 *                                        The event will be fired 2000ms after user typed the last 
+	 *                                        character. 
+	 *                                        If throttle postpone is false, The event is fired immediately 
+	 *                                        after user starts typing and then every two seconds as long
+	 *                                        as user keeps typing.
 	 *   
-	 *   pr, precondition        - Method(s)  Optional. Method or array of methods that is/are invoked 
+	 *   pr, preconditions       - Method(s)  Optional. Method or array of methods that is/are invoked 
 	 *                                        before the request executes. The method(s) will get this 
 	 *                                        RequestItem passed as fist argument and have to return 
-	 *                                        a boolean value. If any of these methods return false the 
-	 *                                        request is cancelled.
+	 *                                        a boolean value. If any of these methods returns false the 
+	 *                                        request is canceled.
 	 *                                        
-	 *   s, successHandlers      - Method(s)  Optional. Method or array of methods that is/are invoked after 
-	 *                                        the request is successfully processed. The method(s) will get 
-	 *                                        this RequestQueueItem passed as fist argument.  
+	 *   be, beforeHandlers      - Method(s)  Optional. Method or array of methods that is/are invoked
+	 *                                        before the actual AJAX request. This invocation only 
+	 *                                        happens when all precondition methods return true.  
+	 *                                        
+	 *   s, successHandlers      - Method(s)  Optional. Method or array of methods that is/are invoked 
+	 *                                        after the request is successfully processed. The method(s) 
+	 *                                        will get this RequestQueueItem passed as fist argument.  
 	 *                                        
 	 *   e, errorHandlers        - Method(s)  Optional. Method or array of methods that is/are invoked when 
 	 *                                        an error happens during the AJAX request or the processing 
@@ -377,28 +528,250 @@
      *                                        will be appended to the URL before postprocessing it. This is 
      *                                        simpler alternative to urlPostProcessor or urlArgumentMethods.
      *                                        
-     *   uam, urlArgumentMethods - Method(s)  Optional. Method or array of methods that produce additional URL
-     *                                        arguments. Each of the methods will get this RequestQueueItem
-     *                                        passed and must return a Map<String, String> (Object).
+     *   uam, urlArgumentMethods - Method(s)  Optional. Method or array of methods that produce additional 
+     *                                        URL arguments. Each of the methods will get this 
+     *                                        RequestQueueItem passed and must return a 
+     *                                        Map<String, String> (Object).
 	 */
 	var RequestQueueItem = function(attributes)
 	{
-		this.attributes = attributes;
+		var a = attributes;
+		var b = function(value) 
+		{
+			if (value) 
+				return true;
+			else
+				return false;
+		}
+		var createMethodArray = function(methodOrArray) 
+		{			
+			if (L.isFunction(methodOrArray))
+				return [methodOrArray];
+			else if (L.isArray(methodOrArray))
+				return methodOrArray;
+			else {				
+				return [];
+			}
+		}
+		var m = function(m1, m2)
+		{
+			m1 = createMethodArray(m1);
+			m2 = createMethodArray(m2);
+			return m1.concat(m2);
+		}
+		var gs = Wicket.ajax.globalSettings;
+		
+		this.attributes  = 
+		{
+			component:            a.component          || a.c,
+			formId:               a.formId             || a.f,
+			multipart:          b(a.multipart          || a.m),
+			requestTimeout:       a.requestTimeout     || a.t    || gs.defaultRequestTimeout,
+			processingTimeout:    a.processingTimeout  || a.pt   || gs.defaultProcessingTimeout,
+			pageId:               a.pageId             || a.p    || gs.defaultPageId,
+			listenerInterface:    a.listenerInterface  || a.l    || gs.defaultToken,
+			behaviorIndex:        a.behaviorIndex      || a.b,
+			token:                a.token              || a.t,			
+			removePrevious:     b(a.removePrevious     || a.r    || gs.defaultRemovePrevious),
+			throttle:             a.throttle           || a.th,
+			throttlePostpone:   b(a.throttlePostpone   || a.thp),
+			preconditions:      m(a.preconditions      || a.pr,  gs.preconditions),
+			beforeHandlers:     m(a.beforeHandlers     || a.be,  gs.beforeHandlers),
+			successHandlers:    m(a.successHandlers    || a.s,   gs.successHandlers),
+			errorHandlers:      m(a.errorHandlers      || a.e,   gs.errorHandlers),
+			urlPostProcessors:  m(a.urlPostProcessors  || a.u,   gs.urlPostProcessors),
+			urlArguments:         a.urlArguments       || a.ua,
+			urlArgumentMethods: m(a.urlArgumentMethods || a.uam, gs.urlArgumentMethods)
+		}
+		
+		log.trace("RequestQueue", "Creating New Item", this.attributes);
 	}
 	
+	RequestQueueItem.prototype = 
+	{
+		checkPreconditions: function() 
+		{
+			var res = iterateArray(this.preconditions, function(precondition) 
+			{
+				try 
+				{
+					if (precondition(this) == false)
+					{
+						return false;
+					}
+				} 
+				catch (exception) 
+				{
+					log.error("RequestQueue", "Error evaluating precondition ", precondition, "Exception: ", exception);
+					return false;
+				}
+			});
+			if (res == null)
+			{
+				return true;
+			}
+			else
+			{
+				return false;
+			}
+		},
+		
+		execute: function(next)
+		{
+		}
+	};
+	
 	var RequestQueue = function()
 	{
+		this.queue = new Array();
+		this.throttler = new Throttler();
 	};
 	
 	RequestQueue.prototype = 
 	{
+		addInternal: function(item) 
+		{			
+			var a = item.attributes;
+			if (a.removePrevious) 
+			{
+				if (!isNonEmpty(a.token)) 
+				{
+					log.warn("RequestQueue", "Item ", item, " has removePrevious set but no token specified - ignored");
+				} 
+				else 
+				{
+					this.removeByToken(a.token);
+				}
+			}
+			this.queue.push(item);
+			
+			if (this.currentItem == null) 
+			{
+				this.next();
+			}
+		},
+		
+		removeByToken: function(token) 
+		{
+			if (token != null) 
+			{
+				for (var i = 0; i < this.queue.length; ++i) 
+				{
+					var item = this.queue[i];
+					if (item.attributes.token == token) 
+					{
+						this.queue[i] = null;
+					}
+				}
+				var q = this.queue;
+				this.queue = new Array();
+				for (var i = 0; i < q.length; ++i) 
+				{
+					if (q[i] != null) 
+					{
+						this.queue.push(q[i]);
+					}
+				}
+			}
+		},
+		
+		add: function(item) 
+		{
+			if (item == null) 
+			{
+				log.error("RequestQueue", "Argument 'item' must not be null.");
+				return;
+			}
+			else if (item.attributes == null) 
+			{
+				log.error("RequestQueue", "Item ", item, " must contain attributes.");
+				return;
+			}
+			var a = item.attributes;			
+			if (a.throttlePostpone == true && a.throttle == null) 
+			{
+				log.warn("RequestQueue", "Item ", item, " has throttlePostpone set but no throttle specified - ignored.");
+			} 
+			else if (a.throttle != null && !isNonEmpty(a.token)) 
+			{
+				log.warn("RequestQueue", "Item ", item, " has throttle set but no token specified - ignored.");
+			}
+			else if (a.throttle != null) 
+			{
+				var f = bind(function() { this.addInternal(item);}, this);
+				this.throttler.throttle(a.token, a.throttle, f, a.throttlePostpone);
+				return;
+			}
+			addInternal(item);				
+		},
+		
+		nextInternal: function() 
+		{
+			this.currentItem = null;
+			
+			if (this.queue.length > 0)
+			{
+				var i = this.queue.shift();
+				if (i.checkPreconditions())
+				{
+					this.currentItem = i;
+					var s = bind(function() { this.skip(i); }, this);
+					var a = i.attributes;
+					var t = a.requestTimeout + a.processingTimeout;
+					
+					// failsafe timeout, in case the request queue item fails to call next() 
+					window.setTimeout(s, t);
+					
+					var next = bind(this.next, this);
+					i.execute(next);
+				}
+				else
+				{
+					this.next();
+				}
+			}
+		},		
+		
+		next: function() 
+		{		
+			window.setTimeout(bind(this.nextInternal, this), 0);
+		},
+		
+		skip: function(item) 
+		{
+			if (this.currentItem == item)
+			{
+				log.error("RequestQueue", "Timeout exceeded, skipping item", item);
+				this.currentItem = null;
+				this.next();
+			}
+		}	
 		
 	};
 	
+	var globalSettings = 
+	{
+		defaultRequestTimeout: 60000,
+		defaultProcessingTimeout: 60000,
+		defaultPageId: -1,
+		defaultToken: null,
+		defaultRemovePrevious: false,
+		beforeHandlers: [],
+		preconditions: [],
+		successHandlers: [],
+		errorHandlers: [],
+		urlPostProcessors: [],
+		urlArgumentMethods: []
+	};
 	
+	var Ajax = function() 
+	{
+		this.globalSettings = globalSettings;
+	};
 	
-	new f();
-	// ===================== REVERT THE OLD WICKET OBJECT ===================== 
+	Wicket.ajax = new Ajax();
+
+	// ===================== REVERT THE OLD WICKET OBJECT ===================== 		
 	
 	WicketNG = Wicket;	
 	Wicket = oldWicket;