/* * 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. */ /** * Wicket Ajax Support * * @author Matej Knopp */ (function() { YUI().use('*', function(Y) { /* * YUI Shortcuts */ var E = Y.Event; var L = Y.Lang; var UA = Y.UA; var W = { }; // Publish the current YUI instance. // Creating new YUI instance every time it is needed can be quite expensive W.Y = Y; /* * $, $$ */ W.$ = function(arg) { if (arg == null || typeof(arg) == "undefined") { return null; } if (arguments.length > 1) { var e=[]; for (var i=0; i 1) { a.unshift("|"); } a.unshift(first); a.unshift("|"); a.unshift(type); for (var i = 0; i < a.length; ++i) { if (L.isString(a[i]) && a[i].length > 100) { var s = a[i]; a[i] = { value: s, toString: function() { return s.substring(0, 30) + "..."; } }; } } return a; }, trace: function() { console.debug.apply(this, FirebugLogger.arg("TRACE", arguments)); }, debug: function() { console.debug.apply(this, FirebugLogger.arg("DEBUG", arguments)); }, info: function() { console.info.apply(this, FirebugLogger.arg("INFO", arguments)); }, error: function() { console.error.apply(this, FirebugLogger.arg("ERROR", arguments)); }, warn: function() { console.warn.apply(this, FirebugLogger.arg("WARN", arguments)); } } var logger = DummyLogger; if (!UA.webkit && typeof(window.console) !== "undefined" && L.isFunction(console.log) && logger === DummyLogger) { logger = FirebugLogger; if (typeof(console.error) == "undefined") console.error = console.log; if (typeof(console.info) == "undefined") console.info = console.log; if (typeof(console.debug) == "undefined") console.debug = console.log; if (typeof(console.warn) == "undefined") console.warn = console.log; } var logConfig = { disableAll: false, trace: true, debug: true, info: true, error: true, warn: true, "trace:GarbageCollector": false, "trace:Contribution":false, "trace:Events":false, "trace:Focus":false, "trace:RequestQueue": false, "trace:General":false, "trace:Throttler": false }; W.Log = { trace: function() { if (!logConfig.disableAll && logConfig.trace && logConfig[arguments[0]] != false && logConfig["trace:" + arguments[0]] != false) logger.trace.apply(this, arguments); }, debug: function() { if (!logConfig.disableAll && logConfig.debug && logConfig[arguments[0]] != false && logConfig["debug:" + arguments[0]] != false) logger.debug.apply(this, arguments); }, info: function() { if (!logConfig.disableAll && logConfig.info && logConfig[arguments[0]] != false && logConfig["info:" + arguments[0]] != false) logger.info.apply(this, arguments); }, error: function() { if (!logConfig.disableAll && logConfig.error && logConfig[arguments[0]] != false && logConfig["error:" + arguments[0]] != false) logger.error.apply(this, arguments); }, warn: function() { if (!logConfig.disableAll && logConfig.warn && logConfig[arguments[0]] != false && logConfig["warn:" + arguments[0]] != false) logger.warn.apply(this, arguments); }, setLogger: function(newLogger) { logger = newLogger; }, getLogger: function() { return logger; }, isDummyLogger: function() { return logger == DummyLogger; }, getConfig: function() { return logConfig; } }; // convenience shortcut var log = W.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 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); }; GarbageCollector.prototype = { // periodically called to initiate the purging process purgeInactiveListeners : function() { // if purge is in progress don't do anything if (this.beingPurged != null) { return; } // 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() { 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; var i; for (i = 0; i < a.length && done < 50; ++i) { var e = a[i]; if (e != null) { ++done; if (!W.$$(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 garbageCollector = new GarbageCollector(5000); // We need to intercept addListener for current YUI instance as well as for all subsequent instances var oldAddListener = Y.Event.addListener; /** * Intercept the YAHOO.util.Event.addListener method and append the element * to elementsWithListeners array so that we can purge it once it get removed from DOM; */ Y.Event.addListener = function(el) { log.trace("Events", "Adding event listeners", arguments); var res = oldAddListener.apply(this, arguments); if (el !== window && el !== document) { var a = garbageCollector.elementsWithListeners; a.push(W.$(el)); } return res; }; // This intercepts addListener in other YUI instances YUI.add("event-dom-fix", function(YY) { var oldAddListener = YY.Event.addListener; /** * Intercept the YAHOO.util.Event.addListener method and append the element * to elementsWithListeners array so that we can purge it once it get removed from DOM; */ YY.Event.addListener = function(el) { log.trace("Events", "Adding event listeners", arguments); oldAddListener.apply(this, arguments); if (el !== window && el !== document) { var a = garbageCollector.elementsWithListeners; a.push(W.$(el)); } }; }, "1.0.0", { use: [ "event-dom" ] } ); Y.on("unload", function() { garbageCollector = null; }, window ); /** * Functions executor takes array of functions and executes them. Each function gets * the notify object, which needs to be called for the next function to be executed. * This way the functions can be executed synchronously. Each function has to call * the notify object at some point, otherwise the functions after it wont be executed. * After the FunctionExecutor is initialize, the start methods triggers the * first function. */ var FunctionsExecutor = function(functions) { this.functions = functions; this.current = 0; this.depth = 0; // we need to limit call stack depth } FunctionsExecutor.prototype = { processNext: function() { if (this.current < this.functions.length) { var f = this.functions[this.current]; var run = bind(function() { var notify = bind(this.notify, this); try { f(notify); } catch (ex) { log.error("FunctionsExecutor", "Error execution function: ", f, ex); notify(); } }, this); this.current++; if (this.depth > 50 || UA.webkit) { // to prevent khtml bug that crashes entire browser // or to prevent stack overflow (safari has small call stack) this.depth = 0; window.setTimeout(run, 1); } else { this.depth ++; run(); } } }, start: function() { this.processNext(); }, notify: function() { this.processNext(); } } var getFunction = function(evalstring) { var str = "W.__tmp=" + evalstring; eval(str); var res = W.__tmp; W.__tmp = null; return res; } var replaceOuterHtmlIE = function(element, text) { // replaces all