/*
* The MIT License
*
* Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Daniel Dyer, Yahoo! Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
//
initOptionalBlock//
// JavaScript for Hudson
// See http://www.ibm.com/developerworks/web/library/wa-memleak/?ca=dgr-lnxw97JavascriptLeaks
// for memory leak patterns and how to prevent them.
//
// create a new object whose prototype is the given object
function object(o) {
function F() {}
F.prototype = o;
return new F();
}
// id generator
var iota = 0;
// Form check code
//========================================================
var FormChecker = {
// pending requests
queue : [],
// conceptually boolean, but doing so create concurrency problem.
// that is, during unit tests, the AJAX.send works synchronously, so
// the onComplete happens before the send method returns. On a real environment,
// more likely it's the other way around. So setting a boolean flag to true or false
// won't work.
inProgress : 0,
/**
* Schedules a form field check. Executions are serialized to reduce the bandwidth impact.
*
* @param url
* Remote doXYZ URL that performs the check. Query string should include the field value.
* @param method
* HTTP method. GET or POST. I haven't confirmed specifics, but some browsers seem to cache GET requests.
* @param target
* HTML element whose innerHTML will be overwritten when the check is completed.
*/
delayedCheck : function(url, method, target) {
if(url==null || method==null || target==null)
return; // don't know whether we should throw an exception or ignore this. some broken plugins have illegal parameters
this.queue.push({url:url, method:method, target:target});
this.schedule();
},
sendRequest : function(url, params) {
if (params.method == "post") {
var idx = url.indexOf('?');
params.parameters = url.substring(idx + 1);
url = url.substring(0, idx);
}
new Ajax.Request(url, params);
},
schedule : function() {
if (this.inProgress>0) return;
if (this.queue.length == 0) return;
var next = this.queue.shift();
this.sendRequest(next.url, {
method : next.method,
onComplete : function(x) {
next.target.innerHTML = x.responseText;
FormChecker.inProgress--;
FormChecker.schedule();
}
});
this.inProgress++;
}
}
// find the nearest ancestor node that has the given tag name
function findAncestor(e, tagName) {
do {
e = e.parentNode;
} while(e.tagName!=tagName);
return e;
}
function findFollowingTR(input, className) {
// identify the parent TR
var tr = input;
while (tr.tagName != "TR")
tr = tr.parentNode;
// then next TR that matches the CSS
do {
tr = tr.nextSibling;
} while (tr.tagName != "TR" || tr.className != className);
return tr;
}
/**
* Traverses a form in the reverse document order starting from the given element (but excluding it),
* until the given filter matches, or run out of an element.
*/
function findPrevious(src,filter) {
function prev(e) {
var p = e.previousSibling;
if(p==null) return e.parentNode;
while(p.lastChild!=null)
p = p.lastChild;
return p;
}
while(src!=null) {
src = prev(src);
if(filter(src))
return src;
}
return null;
}
/**
* Traverses a form in the reverse document order and finds an INPUT element that matches the given name.
*/
function findPreviousFormItem(src,name) {
var name2 = "_."+name; // handles notation silently
return findPrevious(src,function(e){ return e.tagName=="INPUT" && (e.name==name || e.name==name2); });
}
// shared tooltip object
var tooltip;
// Behavior rules
//========================================================
// using tag names in CSS selector makes the processing faster
function registerValidator(e) {
e.targetElement = findFollowingTR(e, "validation-error-area").firstChild.nextSibling;
e.targetUrl = function() {
return eval(this.getAttribute("checkUrl"));
};
var method = e.getAttribute("checkMethod");
if (!method) method = "get";
FormChecker.delayedCheck(e.targetUrl(), method, e.targetElement);
var checker = function() {
var target = this.targetElement;
FormChecker.sendRequest(this.targetUrl(), {
method : method,
onComplete : function(x) {
target.innerHTML = x.responseText;
}
});
}
var oldOnchange = e.onchange;
if(typeof oldOnchange=="function") {
e.onchange = function() { checker.call(this); oldOnchange.call(this); }
} else
e.onchange = checker;
e.onblur = checker;
e = null; // avoid memory leak
}
/**
* Wraps a