1 
  2 /** 
  3   * Global object container for RichFaces API.
  4   * All classes should be defined here.
  5   * @class
  6   * @name RichFaces
  7   * @static
  8   *
  9   * */
 10 window.RichFaces = {};
 11 
 12 (function(jQuery, richfaces) {
 13 	
 14 	// get DOM element by id or DOM element or jQuery object
 15 	richfaces.getDomElement = function (source) {
 16 		var type = typeof source;
 17 		var element;
 18 		if (type == "string") {
 19 			// id
 20 			element = document.getElementById(source);
 21 		} else if (type == "object") {
 22 			if (source.nodeType) {
 23 				// DOM element
 24 				element = source;
 25 			} else 
 26 			if (source instanceof jQuery) {
 27 				// jQuery object
 28 				element = source.get(0);
 29 			}
 30 		}
 31 		return element;
 32 	}
 33 	
 34 	// get RichFaces component object by component id or DOM element or jQuery object
 35 	richfaces.$ = function (source) {
 36 		var element = richfaces.getDomElement(source);
 37 
 38 		if (element) {
 39 			return (element["richfaces"] || {})["component"];
 40 		}
 41 	}
 42 	
 43 	// find component and call his method
 44 	richfaces.invokeMethod = function(source, method) {
 45 		var c = richfaces.$(source);
 46 		if (c) {
 47 			var f = c[method];
 48 			if (typeof f == "function") {
 49 				return f.apply(c, Array.prototype.slice.call(arguments, 2));
 50 			}
 51 		}
 52 	}
 53 
 54 	//dom cleaner
 55 	richfaces.cleanDom = function(source) {
 56 		var e = (typeof source == "string") ? document.getElementById(source) : jQuery('body').get(0);
 57 		if (e) {
 58 			var elements = e.getElementsByTagName("*");
 59 			if (elements.length) {
 60 				jQuery.cleanData(elements);
 61 				jQuery.cleanData([e]);
 62 				jQuery.each(elements, function(index) {
 63 					richfaces.invokeMethod(this, "destroy");
 64 				});
 65 				richfaces.invokeMethod(e, "destroy");
 66 			}
 67 		}
 68 	}
 69 	
 70 	//form.js
 71 	richfaces.submitForm = function(form, parameters, target) {
 72 		if (typeof form === "string") { form = jQuery(form) };
 73 		var initialTarget = form.attr("target");
 74 		var parameterInputs = new Array();
 75 		try {
 76 			form.attr("target", target);
 77 
 78 			if (parameters) {
 79 				for (var parameterName in parameters) {
 80 					var parameterValue = parameters[parameterName];
 81 
 82 					var input = jQuery("input[name='" + parameterName + "']", form);
 83 					if (input.length == 0) {
 84 						var newInput = jQuery("<input />").attr({type: 'hidden', name: parameterName, value: parameterValue});
 85 						if (parameterName === 'javax.faces.portletbridge.STATE_ID' /* fix for fileUpload in portlets */) {
 86 							input = newInput.prependTo(form);
 87 						} else {
 88 							input = newInput.appendTo(form);
 89 						}
 90 					} else {
 91 						input.val(parameterValue);
 92 					}
 93 
 94 					input.each(function() {parameterInputs.push(this)});
 95 				}
 96 			}
 97 
 98 			//TODO: inline onsubmit handler is not triggered - http://dev.jquery.com/ticket/4930
 99 			form.trigger("submit");
100 		} finally {
101 			form.attr("target", initialTarget);
102 			jQuery(parameterInputs).remove();
103 		}
104 	};
105 	//
106 
107 	//utils.js
108 	jQuery.fn.toXML = function () {
109 		var out = '';
110 
111 		if (this.length > 0) {
112 			if (typeof XMLSerializer == 'function' ||
113 				typeof XMLSerializer == 'object') {
114 
115 				var xs = new XMLSerializer();
116 				this.each(function() { out += xs.serializeToString(this); });
117 			} else if (this[0].xml !== undefined) {
118 				this.each(function() { out += this.xml; });
119 			} else {
120 				this.each( function() { out += this; } );
121 			}
122 		}
123 
124 		return out;
125 	};
126 
127     //there is the same pattern in server-side code: 
128 	//org.ajax4jsf.javascript.ScriptUtils.escapeCSSMetachars(String)
129 	var CSS_METACHARS_PATTERN = /([#;&,.+*~':"!^$[\]()=>|\/])/g;
130 
131 	/**
132      * Escapes CSS meta-characters in string according to 
133      *  <a href="http://api.jquery.com/category/selectors/">jQuery selectors</a> document.
134      * 
135      * @param s - string to escape meta-characters in
136      * @return string with meta-characters escaped
137 	 */
138 	richfaces.escapeCSSMetachars = function(s) {
139 		//TODO nick - cache results
140 		
141 		return s.replace(CSS_METACHARS_PATTERN, "\\$1");
142 	};
143 	
144 	richfaces.log = (function(jQuery) {
145 		var LOG_LEVELS = {'debug': 1, 'info': 2, 'warn': 3, 'error': 4};
146 		var LOG_LEVEL_COLORS = {'debug': 'darkblue', 'info': 'blue', 'warn': 'gold', 'error': 'red'};
147 		var DEFAULT_LOG_LEVEL = LOG_LEVELS['info'];
148 		var currentLogLevel = DEFAULT_LOG_LEVEL;
149 
150 		var consoleInitialized = false;
151 
152 		var setLevel = function(level) {
153 			currentLogLevel = LOG_LEVELS[level] || DEFAULT_LOG_LEVEL;
154 			if (consoleInitialized) {
155 				getConsole().find("select.rich-log-element").val(currentLogLevel);
156 			}
157 			clear();
158 		};
159 
160 		var setLevelFromSelect = function(iLevel) {
161 			currentLogLevel = iLevel || DEFAULT_LOG_LEVEL;
162 		};
163 
164 		var clear = function() {
165 			if (window.console && useBrowserConsole) {
166 				window.console.clear();
167 			} else {
168 				var console = getConsole();
169 				console.children(".rich-log-contents").children().remove();
170 			}
171 		};
172 
173 		var getConsole = function() {
174 			var console = jQuery('#richfaces\\.log');
175 			if (console.length != 0) {
176 				if (!consoleInitialized) {
177 					consoleInitialized = true;
178 
179 					var clearBtn = console.children("button.rich-log-element");
180 					if (clearBtn.length == 0) {
181 						clearBtn = jQuery("<button type='button' class='rich-log-element'>Clear</button>").appendTo(console);
182 					}
183 					clearBtn.click(clear);
184 
185 					var levelSelect = console.children("select.rich-log-element");
186 					if (levelSelect.length == 0) {
187 						levelSelect = jQuery("<select class='rich-log-element' />").appendTo(console);
188 					}
189 
190 					if (levelSelect.children().length == 0) {
191 						for (var level in LOG_LEVELS) {
192 							jQuery("<option value='" + LOG_LEVELS[level]+ "'>" + level + "</option>").appendTo(levelSelect);
193 						}
194 					}
195 
196 					levelSelect.val(currentLogLevel);
197 					levelSelect.change(function(event) { clear(); setLevelFromSelect(parseInt(jQuery(this).val(), 10)); return false;});
198 
199 					var consoleEntries = console.children(".rich-log-contents");
200 					if (consoleEntries.length == 0) {
201 						consoleEntries = jQuery("<div class='rich-log-contents'></div>").appendTo(console);
202 					}
203 				}
204 			}
205 
206 			return console;
207 		};
208 
209 		var useBrowserConsole = false;
210 
211 		var AM_PM = /(\s*(?:a|p)m)$/i;
212 
213 		var getMessagePrefix = function(level) {
214 			var date = new Date();
215 			var formattedDate = date.toLocaleTimeString();
216 
217 			var ms = (date.getMilliseconds() + 1000).toString().substr(1);
218 			if (AM_PM.test(formattedDate)) {
219 				formattedDate = formattedDate.replace(AM_PM, "." + ms + "$1");
220 			} else {
221 				formattedDate += "." + ms;
222 			}
223 
224 			return level + '[' + formattedDate.replace() + ']: ';
225 		};
226 
227 		var appendConsoleEntry = function(level, messagePrefix, messageText, console) {
228 			//TODO - cache jQuery("<element>")?
229 			var newEntry = jQuery(document.createElement("div")).appendTo(console.children(".rich-log-contents"));
230 			jQuery("<span style='color: " + LOG_LEVEL_COLORS[level] + "'></span>").appendTo(newEntry).text(messagePrefix);
231 
232 			var entrySpan = jQuery(document.createElement("span")).appendTo(newEntry);
233 			if (typeof messageText != 'object' || !messageText.appendTo) {
234 				entrySpan.text(messageText);
235 			} else {
236 				messageText.appendTo(entrySpan);
237 			}
238 		};
239 
240 		var appendBrowserConsoleEntry = function(level, text) {
241 			window.console[level]();
242 		};
243 
244 		var appendMessage = function(level, message) {
245 			if (window.console && useBrowserConsole) {
246 				var text = getMessagePrefix(level) + message;
247 				appendBrowserConsoleEntry(level, text);
248 			} else {
249 				var console = getConsole();
250 				if (LOG_LEVELS[level] >= currentLogLevel && console.length != 0) {
251 					var messagePrefix = getMessagePrefix(level);
252 					appendConsoleEntry(level, messagePrefix, message, console);
253 				}
254 			}
255 		};
256 
257 		var methods = {setLevel: setLevel, clear: clear};
258 		for (var logLevel in LOG_LEVELS) {
259 			var f = function(text) {
260 				appendMessage(arguments.callee.logLevel, text);
261 			};
262 			f.logLevel = logLevel;
263 			methods[logLevel] = f;
264 		}
265 		return methods;
266 	}(jQuery));
267 
268 	/**
269 	 * Evaluates chained properties for the "base" object.
270 	 * For example, window.document.location is equivalent to
271 	 * "propertyNamesString" = "document.location" and "base" = window
272 	 * Evaluation is safe, so it stops on the first null or undefined object
273 	 *
274 	 * @param propertyNamesArray - array of strings that contains names of the properties to evaluate
275 	 * @param base - base object to evaluate properties on
276 	 * @return returns result of evaluation or empty string
277 	 */
278 	richfaces.getValue = function(propertyNamesArray, base) {
279 		var result = base;
280 		var c = 0;
281 		do {
282 			result = result[propertyNamesArray[c++]];
283 		} while (result && c != propertyNamesArray.length);
284 
285 		return result;
286 	};
287 
288 	var VARIABLE_NAME_PATTERN_STRING = "[_A-Z,a-z]\\w*";
289 	var VARIABLES_CHAIN = new RegExp("^\\s*"+VARIABLE_NAME_PATTERN_STRING+"(?:\\s*\\.\\s*"+VARIABLE_NAME_PATTERN_STRING+")*\\s*$");
290 	var DOT_SEPARATOR = /\s*\.\s*/;
291 
292 	richfaces.evalMacro = function(macro, base) {
293 		var value = "";
294 		// variable evaluation
295 		if (VARIABLES_CHAIN.test(macro)) {
296 			// object's variable evaluation
297 			var propertyNamesArray = jQuery.trim(macro).split(DOT_SEPARATOR);
298 			value = richfaces.getValue(propertyNamesArray, base);
299 			if (!value) {
300 				value = richfaces.getValue(propertyNamesArray, window);
301 			}
302 		} else {
303 			//js string evaluation
304 			try {
305 				if (base.eval) {
306 					value = base.eval(macro);
307 				} else with (base) {
308 					value = eval(macro) ;
309 				}
310 			} catch (e) {
311 				richfaces.log.warn("Exception: " + e.message + "\n[" + macro + "]");
312 			}
313 		}
314 
315 		if (typeof value == 'function') {
316 			value = value(base);
317 		}
318 		//TODO 0 and false are also treated as null values
319 		return value || "";
320 	};
321 
322 	var ALPHA_NUMERIC_MULTI_CHAR_REGEXP = /^\w+$/;
323 
324 	richfaces.interpolate = function (placeholders, context) {
325 		var contextVarsArray = new Array();
326 		for (var contextVar in context) {
327 			if (ALPHA_NUMERIC_MULTI_CHAR_REGEXP.test(contextVar)) {
328 				//guarantees that no escaping for the below RegExp is necessary
329 				contextVarsArray.push(contextVar);
330 			}
331 		}
332 
333 		var regexp = new RegExp("\\{(" + contextVarsArray.join("|") + ")\\}", "g");
334 		return placeholders.replace(regexp, function(str, contextVar) {return context[contextVar];});
335 	};
336 
337 	richfaces.clonePosition = function(element, baseElement, positioning, offset) {
338 
339 	};
340 	//
341 
342 	var pollTracker = {};
343 		richfaces.startPoll =  function(options) {
344 		var pollId = options.pollId;
345 		var interval = options.pollinterval;
346 		var ontimer = options.ontimer;
347 		richfaces.stopPoll(pollId);
348 	
349 		richfaces.setZeroRequestDelay(options);
350 	
351 		pollTracker[pollId] = window.setTimeout(function(){
352 			var pollElement = document.getElementById(pollId);
353 				try {
354 					ontimer.call(pollElement || window);
355 					richfaces.startPoll(options);
356 				} catch (e) {
357 					// TODO: handle exception
358 				}
359 		},interval);
360 	};
361 
362 	richfaces.stopPoll =  function(id) {
363 		if(pollTracker[id]){
364 			window.clearTimeout(pollTracker[id]);
365 			delete pollTracker[id];
366 		}
367 	};
368 	
369 	var pushTracker = {};
370 
371 	richfaces.startPush = function(options) {
372 		var clientId = options.clientId;
373 		var pushResourceUrl = options.pushResourceUrl;
374 		var pushId = options.pushId;
375 		var interval = options.interval;
376 		var ondataavailable = options.ondataavailable;
377 		richfaces.setZeroRequestDelay(options);
378 		
379 		richfaces.stopPush(pushId);
380 
381 		pushTracker[pushId] = setTimeout(function() { // TODO: define this function in richfaces object to avoid definition every time when call startPush
382 			var ajaxOptions = {
383 				type: "HEAD",
384 				//TODO - encodeURIComponent; URL sessionId handling check
385 				//TODO - add pushUri supports
386 				url: pushResourceUrl + "?id=" + pushId,
387 				dataType: "text",
388 				complete: function(xhr) {
389 					var isPushActive = !!pushTracker[pushId];
390 
391 					//TODO may someone wish to stop push from dataavailable handler?
392 					delete pushTracker[pushId];
393 
394 					if (xhr.status == 200 && xhr.getResponseHeader("Ajax-Push-Status") == "READY") {
395 						var pushElement = document.getElementById(clientId);
396 						try {
397 							ondataavailable.call(pushElement || window);
398 						} catch (e) {
399 							// TODO: handle exception
400 						}
401 					}
402 
403 					if (isPushActive) {
404 						richfaces.startPush(options);
405 					}
406 				}
407 			};
408 
409 			if (options.timeout) {
410 				ajaxOptions.timeout = options.timeout;
411 			}
412 
413 			jQuery.ajax(ajaxOptions);
414 		}, interval);
415 	};
416 
417 	richfaces.stopPush = function(id) {
418 		if (pushTracker[id]){
419 			window.clearTimeout(pushTracker[id]);
420 			delete pushTracker[id];
421 		}
422 	};
423 
424 	var jsfEventsAdapterEventNames = {
425 		event: {
426 			'begin': ['begin'],
427 			'complete': ['beforedomupdate'],
428 			'success': ['success', 'complete']
429 		},
430 		error: ['error', 'complete']
431 	};
432 
433 	richfaces.createJSFEventsAdapter = function(handlers) {
434 		//hash of handlers
435 		//supported are:
436 		// - begin
437 		// - beforedomupdate
438 		// - success
439 		// - error
440 		// - complete
441 		handlers = handlers || {};
442 
443 		return function(eventData) {
444 			var source = eventData.source;
445 			//that's request status, not status control data
446 			var status = eventData.status;
447 			var type = eventData.type;
448 
449 			var typeHandlers = jsfEventsAdapterEventNames[type];
450 			var handlerNames = (typeHandlers || {})[status] || typeHandlers;
451 
452 			if (handlerNames) {
453 				for (var i = 0; i < handlerNames.length; i++) {
454 					var eventType = handlerNames[i];
455 					var handler = handlers[eventType];
456 					if (handler) {
457 						var event = {};
458 						jQuery.extend(event, eventData);
459 						event.type = eventType;
460 						if (type != 'error') {
461 							delete event.status;
462 						}
463 
464 						handler.call(source, event);
465 					}
466 				}
467 			}
468 		};
469 	};
470 
471 	var setGlobalStatusNameVariable = function(statusName) {
472 		//TODO: parallel requests
473 		if (statusName) {
474 			richfaces['statusName'] = statusName;
475 		} else {
476 			delete richfaces['statusName'];
477 		}
478 	}
479 
480 	richfaces.setZeroRequestDelay = function(options) {
481 		if (typeof options.requestDelay == "undefined") {
482 			options.requestDelay = 0;
483 		}
484 	};
485 	
486 	var getGlobalStatusNameVariable = function() {
487 		return richfaces.statusName;
488 	}
489 
490 	var chain = function() {
491 		var functions = arguments;
492 		if (functions.length == 1) {
493 			return functions[0];
494 		} else {
495 			return function() {
496 				var callResult;
497 				for (var i = 0; i < functions.length; i++) {
498 					var f = functions[i];
499 					callResult = f.apply(this, arguments);
500 				}
501 
502 				return callResult;
503 			};
504 		}
505 	};
506 
507 	/**
508 	 * curry (g, a) (b) -> g(a, b)
509 	 */
510 	var curry = function(g, a) {
511 		var _g = g;
512 		var _a = a;
513 
514 		return function(b) {
515 			_g(_a, b);
516 		};
517 	};
518 
519 	var createEventHandler = function(handlerCode) {
520 		if (handlerCode) {
521 			return new Function("event", "data", handlerCode);
522 		}
523 
524 		return null;
525 	};
526 
527 	//TODO take events just from .java code using EL-expression
528 	var AJAX_EVENTS = (function() {
529 		var serverEventHandler = function(clientHandler, event) {
530 			var xml = jQuery("partial-response > extension#org\\.richfaces\\.extension", event.responseXML);
531 
532 			var serverHandler = createEventHandler(xml.children(event.type).text());
533 			xml.end();
534 
535 			var dataString = xml.children("data").text();
536 			xml.end();
537 
538 			var data = null;
539 			if (dataString) {
540 				try {
541 					data = window["eval"]("(" + dataString + ")");
542 				} catch (e) {
543 					richfaces.log.warn("Error evaluating custom response data: " + e.message);
544 				}
545 			}
546 
547 			if (serverHandler) {
548 				serverHandler.call(window, event, data);
549 			} else if (clientHandler) {
550 				clientHandler.call(window, event, data);
551 			}
552 		};
553 
554 		return {
555 			'begin': null,
556 			'complete': serverEventHandler,
557 			'beforedomupdate': serverEventHandler
558 		}
559 	}());
560 
561 	richfaces.ajax = function(source, event, options) {
562 		var sourceId = (typeof source == 'object' && source.id) ? source.id : source;
563 		var sourceElt = (typeof source == 'object') ? source : document.getElementById(sourceId);
564 		
565 		options = options || {};
566 
567 		parameters = options.parameters || {}; // TODO: change "parameters" to "richfaces.ajax.params"
568 		parameters.execute = "@component";
569 		parameters.render = "@component";
570 
571 		if (!parameters["org.richfaces.ajax.component"]) {
572 			parameters["org.richfaces.ajax.component"] = sourceId;
573 		}
574 
575 		var eventHandlers;
576 
577 		for (var eventName in AJAX_EVENTS) {
578 			var handler = createEventHandler(options[eventName]);
579 
580 			var serverHandler = AJAX_EVENTS[eventName];
581 			if (serverHandler) {
582 				handler = curry(serverHandler, handler);
583 			}
584 
585 			if (handler) {
586 				eventHandlers = eventHandlers || {};
587 				eventHandlers[eventName] = handler;
588 			}
589 		}
590 
591 		if (options.status) {
592 			var namedStatusEventHandler = function() { setGlobalStatusNameVariable(options.status); };
593 
594 			//TODO add support for options.submit
595 			eventHandlers = eventHandlers || {};
596 			if (eventHandlers.begin) {
597 				eventHandlers.begin = chain(namedStatusEventHandler, eventHandlers.begin);
598 			} else {
599 				eventHandlers.begin = namedStatusEventHandler;
600 			}
601 		}
602 
603 		if (!jQuery(sourceElt).is(":input:not(:submit, :button, :image, :reset)")) {
604 			parameters[sourceId] = sourceId;
605 		}
606 
607 		if (eventHandlers) {
608 			var eventsAdapter = richfaces.createJSFEventsAdapter(eventHandlers);
609 			parameters['onevent'] = eventsAdapter;
610 			parameters['onerror'] = eventsAdapter;
611 		}
612 		
613 		if (richfaces.queue) {
614 			parameters.queueId = options.queueId;
615 		}
616 
617 		jsf.ajax.request(source, event, parameters);
618 	};
619 
620 	var RICHFACES_AJAX_STATUS = "richfaces:ajaxStatus";
621 
622 	var getStatusDataAttributeName = function(statusName) {
623 		return statusName ? (RICHFACES_AJAX_STATUS + "@" + statusName) : RICHFACES_AJAX_STATUS;
624 	};
625 
626 	var statusAjaxEventHandler = function(data, methodName) {
627 		if (methodName) {
628 			//global status name
629 			var statusName = getGlobalStatusNameVariable();
630 			var source = data.source;
631 
632 			var statusApplied = false;
633 			var statusDataAttribute = getStatusDataAttributeName(statusName);
634 
635 			var statusContainers;
636 			if (statusName) {
637 				statusContainers = [jQuery(document)];
638 			} else {
639 				statusContainers = [jQuery(source).parents('form'), jQuery(document)];
640 			}
641 
642 			for (var containerIdx = 0; containerIdx < statusContainers.length && !statusApplied;
643 				containerIdx++) {
644 
645 				var statusContainer = statusContainers[containerIdx];
646 				var statuses = statusContainer.data(statusDataAttribute);
647 				if (statuses) {
648 					for (var statusId in statuses) {
649 						var status = statuses[statusId];
650 						var result = status[methodName].apply(status, arguments);
651 						if (result) {
652 							statusApplied = true;
653 						} else {
654 							delete statuses[statusId];
655 						}
656 					}
657 
658 					if (!statusApplied) {
659 						statusContainer.removeData(statusDataAttribute);
660 					}
661 				}
662 			}
663 		}
664 	};
665 
666 	var initializeStatuses = function() {
667 		var thisFunction = arguments.callee;
668 		if (!thisFunction.initialized) {
669 			thisFunction.initialized = true;
670 
671 			var jsfEventsListener = richfaces.createJSFEventsAdapter({
672 				begin: function(event) { statusAjaxEventHandler(event, 'start'); },
673 				error: function(event) { statusAjaxEventHandler(event, 'error'); },
674 				success: function(event) { statusAjaxEventHandler(event, 'success'); },
675 				complete: function() { setGlobalStatusNameVariable(null); }
676 			});
677 
678 			jsf.ajax.addOnEvent(jsfEventsListener);
679 			//TODO blocks default alert error handler
680 			jsf.ajax.addOnError(jsfEventsListener);
681 		}
682 	};
683 
684 	richfaces.status = function(statusId, options) {
685 		this.statusId = statusId;
686 		this.options = options || {};
687 		this.register();
688 	};
689 
690 	jQuery.extend(richfaces.status.prototype, (function() {
691 		//TODO - support for parallel requests
692 
693 		var getElement = function() {
694 			var elt = document.getElementById(this.statusId);
695 			return elt ? jQuery(elt) : null;
696 		};
697 
698 		var showHide = function(selector) {
699 			var element = getElement.call(this);
700 			if (element) {
701 				var statusElts = element.children();
702 				statusElts.each(function() {
703 					var t = jQuery(this);
704 					t.css('display', t.is(selector) ? '': 'none');
705 				});
706 
707 				return true;
708 			}
709 
710 			return false;
711 		};
712 
713 		return {
714 			register: function() {
715 				initializeStatuses();
716 
717 				var statusName = this.options.statusName;
718 				var dataStatusAttribute = getStatusDataAttributeName(statusName);
719 
720 				var container;
721 				if (statusName) {
722 					container = jQuery(document);
723 				} else {
724 					container = getElement.call(this).parents('form');
725 					if (container.length == 0) {
726 						container = jQuery(document);
727 					};
728 				}
729 
730 				var statuses = container.data(dataStatusAttribute);
731 				if (!statuses) {
732 					statuses = {};
733 					container.data(dataStatusAttribute, statuses);
734 				}
735 
736 				statuses[this.statusId] = this;
737 			},
738 
739 			start: function() {
740 				if (this.options.onstart) {
741 					this.options.onstart.apply(this, arguments);
742 				}
743 
744 				return showHide.call(this, '.rich-status-start');
745 			},
746 
747 			stop: function() {
748 				if (this.options.onstop) {
749 					this.options.onstop.apply(this, arguments);
750 				}
751 			},
752 
753 			success: function() {
754 				if (this.options.onsuccess) {
755 					this.options.onsuccess.apply(this, arguments);
756 				}
757 				this.stop();
758 
759 				return showHide.call(this, '.rich-status-stop');
760 			},
761 
762 			error: function() {
763 				if (this.options.onerror) {
764 					this.options.onerror.apply(this, arguments);
765 				}
766 				this.stop();
767 
768 				return showHide.call(this, ':not(.rich-status-error) + .rich-status-stop, .rich-status-error');
769 			}
770 		};
771 	}()));
772 	
773 	var ajaxOnComplete = function (data) {
774 		var type = data.type;
775 		var responseXML = data.responseXML;
776 
777 		if (data.type == 'event' && data.status == 'complete' && responseXML) {
778 			var partialResponse = jQuery(responseXML).children("partial-response");
779 			if (partialResponse && partialResponse.length) {
780 				var elements = partialResponse.children('changes').children('update, delete');
781 				jQuery.each(elements, function () {
782 					richfaces.cleanDom(jQuery(this).attr('id'));
783 				});
784 			}
785 		}
786 	};
787 	// move this code to somewhere
788 	if (typeof jsf != 'undefined') {
789 		jsf.ajax.addOnEvent(ajaxOnComplete);
790 	}
791 	if (window.addEventListener) {
792 		window.addEventListener("unload", richfaces.cleanDom, false);
793 	} else {
794 		window.attachEvent("onunload", richfaces.cleanDom);
795 	}
796 }(jQuery, RichFaces));
797 
798