/* * 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. */ // // // 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; // crumb information var crumb = { fieldName: null, value: null, init: function(crumbField, crumbValue) { this.fieldName = crumbField; this.value = crumbValue; }, /** * Adds the crumb value into the given hash and returns the hash. */ wrap: function(hash) { if(this.fieldName!=null) hash[this.fieldName]=this.value; return hash; }, /** * Puts a hidden input field to the form so that the form submission will have the crumb value */ appendToForm : function(form) { if(this.fieldName==null) return; // noop var div = document.createElement("div"); div.innerHTML = ""; form.appendChild(div); } } // 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++; } } function toValue(e) { // compute the form validation value to be sent to the server var type = e.getAttribute("type"); if(type!=null && type.toLowerCase()=="checkbox") return e.checked; return encodeURIComponent(e.value); } // find the nearest ancestor node that has the given tag name function findAncestor(e, tagName) { do { e = e.parentNode; } while (e != null && 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(src==null) break; 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.tagName=="TEXTAREA") && (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 } function registerRegexpValidator(e,regexp,message) { e.targetElement = findFollowingTR(e, "validation-error-area").firstChild.nextSibling; var checkMessage = e.getAttribute('checkMessage'); if (checkMessage) message = checkMessage; var oldOnchange = e.onchange; e.onchange = function() { var set = oldOnchange != null ? oldOnchange.call(this) : false; if (this.value.match(regexp)) { if (!set) this.targetElement.innerHTML = ""; } else { this.targetElement.innerHTML = "
" + message + "
"; set = true; } return set; } e.onchange.call(e); e = null; // avoid memory leak } /** * Wraps a