| 
									
										
										
										
											2007-03-26 12:44:40 +08:00
										 |  |  | // create a new object whose prototype is the given object
 | 
					
						
							|  |  |  | function object(o) { | 
					
						
							|  |  |  |     function F() {} | 
					
						
							|  |  |  |     F.prototype = o; | 
					
						
							|  |  |  |     return new F(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-12-31 05:03:57 +08:00
										 |  |  | // id generator
 | 
					
						
							|  |  |  | var iota = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-11-06 05:16:01 +08:00
										 |  |  | // Form check code
 | 
					
						
							|  |  |  | //========================================================
 | 
					
						
							| 
									
										
										
										
											2007-03-12 02:19:38 +08:00
										 |  |  | var FormChecker = { | 
					
						
							| 
									
										
										
										
											2006-11-06 05:16:01 +08:00
										 |  |  |   // pending requests
 | 
					
						
							|  |  |  |   queue : [], | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   inProgress : false, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-03-12 02:46:31 +08:00
										 |  |  |   delayedCheck : function(url,method,target) { | 
					
						
							|  |  |  |     this.queue.push({url:url, method:method, target:target}); | 
					
						
							| 
									
										
										
										
											2006-11-06 05:16:01 +08:00
										 |  |  |     this.schedule(); | 
					
						
							|  |  |  |   }, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-03-12 02:46:31 +08:00
										 |  |  |   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); | 
					
						
							|  |  |  |   }, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-11-06 05:16:01 +08:00
										 |  |  |   schedule : function() { | 
					
						
							|  |  |  |     if(this.inProgress)  return; | 
					
						
							|  |  |  |     if(this.queue.length==0) return; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-03-12 02:46:31 +08:00
										 |  |  |     var next = this.queue.shift(); | 
					
						
							| 
									
										
										
										
											2006-11-06 05:16:01 +08:00
										 |  |  |     this.inProgress = true; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-03-12 02:46:31 +08:00
										 |  |  |     this.sendRequest(next.url, { | 
					
						
							|  |  |  |         method : next.method, | 
					
						
							| 
									
										
										
										
											2006-11-06 05:16:01 +08:00
										 |  |  |         onComplete : function(x) { | 
					
						
							|  |  |  |           next.target.innerHTML = x.responseText; | 
					
						
							| 
									
										
										
										
											2007-03-12 02:19:38 +08:00
										 |  |  |           FormChecker.inProgress = false; | 
					
						
							|  |  |  |           FormChecker.schedule(); | 
					
						
							| 
									
										
										
										
											2006-11-06 05:16:01 +08:00
										 |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Behavior rules
 | 
					
						
							|  |  |  | //========================================================
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var hudsonRules = { | 
					
						
							|  |  |  |   ".advancedButton" : function(e) { | 
					
						
							|  |  |  |     e.onclick = function() { | 
					
						
							|  |  |  |       var link = this.parentNode; | 
					
						
							|  |  |  |       link.style.display = "none"; // hide the button
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       var container = link.nextSibling.firstChild; // TABLE -> TBODY
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       var tr = link; | 
					
						
							|  |  |  |       while(tr.tagName!="TR") | 
					
						
							|  |  |  |         tr = tr.parentNode; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       // move the contents of the advanced portion into the main table
 | 
					
						
							|  |  |  |       while(container.lastChild!=null) { | 
					
						
							|  |  |  |         tr.parentNode.insertBefore(container.lastChild,tr.nextSibling); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   ".pseudoLink" : function(e) { | 
					
						
							|  |  |  |     e.onmouseover = function() { | 
					
						
							|  |  |  |       this.style.textDecoration="underline"; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     e.onmouseout = function() { | 
					
						
							|  |  |  |       this.style.textDecoration="none"; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // form fields that are validated via AJAX call to the server
 | 
					
						
							|  |  |  |   // elements with this class should have two attributes 'checkUrl' that evaluates to the server URL.
 | 
					
						
							|  |  |  |   ".validated" : function(e) { | 
					
						
							|  |  |  |     e.targetElement = findFollowingTR(e,"validation-error-area").firstChild.nextSibling; | 
					
						
							|  |  |  |     e.targetUrl = function() {return eval(this.getAttribute("checkUrl"));}; | 
					
						
							| 
									
										
										
										
											2007-03-12 02:46:31 +08:00
										 |  |  |     var method = e.getAttribute("checkMethod"); | 
					
						
							|  |  |  |     if(!method) method="get"; | 
					
						
							| 
									
										
										
										
											2006-11-06 05:16:01 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-03-12 02:46:31 +08:00
										 |  |  |     FormChecker.delayedCheck(e.targetUrl(), method, e.targetElement); | 
					
						
							| 
									
										
										
										
											2006-11-06 05:16:01 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     e.onchange = function() { | 
					
						
							| 
									
										
										
										
											2007-03-12 02:46:31 +08:00
										 |  |  |       FormChecker.sendRequest(this.targetUrl(), { | 
					
						
							|  |  |  |           method : method, | 
					
						
							| 
									
										
										
										
											2006-11-06 05:16:01 +08:00
										 |  |  |           onComplete : function(x) {e.targetElement.innerHTML = x.responseText;} | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // validate form values to be a number
 | 
					
						
							|  |  |  |   "input.number" : function(e) { | 
					
						
							|  |  |  |     e.targetElement = findFollowingTR(e,"validation-error-area").firstChild.nextSibling; | 
					
						
							|  |  |  |     e.onchange = function() { | 
					
						
							| 
									
										
										
										
											2007-04-30 01:05:28 +08:00
										 |  |  |       if(this.value.match(/^(\d+|)$/)) { | 
					
						
							| 
									
										
										
										
											2006-11-06 05:16:01 +08:00
										 |  |  |         this.targetElement.innerHTML=""; | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         this.targetElement.innerHTML="<div class=error>Not a number</div>"; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   ".help-button" : function(e) { | 
					
						
							|  |  |  |     e.onclick = function() { | 
					
						
							|  |  |  |       tr = findFollowingTR(this,"help-area"); | 
					
						
							|  |  |  |       div = tr.firstChild.nextSibling.firstChild; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if(div.style.display!="block") { | 
					
						
							|  |  |  |         div.style.display="block"; | 
					
						
							|  |  |  |         // make it visible
 | 
					
						
							|  |  |  |         new Ajax.Request( | 
					
						
							|  |  |  |             this.getAttribute("helpURL"), | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |               method : 'get', | 
					
						
							|  |  |  |               onComplete : function(x) { | 
					
						
							|  |  |  |                 div.innerHTML = x.responseText; | 
					
						
							|  |  |  |               } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         div.style.display = "none"; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       return false; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2006-12-31 05:03:57 +08:00
										 |  |  |   }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // deferred client-side clickable map.
 | 
					
						
							|  |  |  |   // this is useful where the generation of <map> element is time consuming
 | 
					
						
							|  |  |  |   "IMG[lazymap]" : function(e) { | 
					
						
							|  |  |  |       new Ajax.Request( | 
					
						
							|  |  |  |           e.getAttribute("lazymap"), | 
					
						
							|  |  |  |           { | 
					
						
							|  |  |  |             method : 'get', | 
					
						
							|  |  |  |             onComplete : function(x) { | 
					
						
							|  |  |  |               var div = document.createElement("div"); | 
					
						
							|  |  |  |               document.body.appendChild(div); | 
					
						
							|  |  |  |               div.innerHTML = x.responseText; | 
					
						
							|  |  |  |               var id = "map"+(iota++); | 
					
						
							|  |  |  |               div.firstChild.setAttribute("name",id); | 
					
						
							|  |  |  |               e.setAttribute("usemap","#"+id); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |       ); | 
					
						
							| 
									
										
										
										
											2007-03-26 12:44:40 +08:00
										 |  |  |   }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // button to add a new repeatable block
 | 
					
						
							|  |  |  |     "INPUT.repeatable-add" : function(e) { | 
					
						
							|  |  |  |         e.onclick = function() { | 
					
						
							|  |  |  |             repetableSupport.onAdd(this); | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     "INPUT.repeatable-delete" : function(e) { | 
					
						
							|  |  |  |         e.onclick = function() { | 
					
						
							|  |  |  |             repetableSupport.onDelete(this); | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2006-11-06 05:16:01 +08:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Behaviour.register(hudsonRules); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // used by editableDescription.jelly to replace the description field with a form
 | 
					
						
							|  |  |  | function replaceDescription() { | 
					
						
							|  |  |  |     var d = document.getElementById("description"); | 
					
						
							|  |  |  |     d.firstChild.nextSibling.innerHTML = "<div class='spinner-right'>loading...</div>"; | 
					
						
							|  |  |  |     new Ajax.Request( | 
					
						
							|  |  |  |         "./descriptionForm", | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           method : 'get', | 
					
						
							|  |  |  |           onComplete : function(x) { | 
					
						
							|  |  |  |             d.innerHTML = x.responseText; | 
					
						
							| 
									
										
										
										
											2007-01-21 23:08:00 +08:00
										 |  |  |             d.getElementsByTagName("TEXTAREA")[0].focus(); | 
					
						
							| 
									
										
										
										
											2006-11-06 05:16:01 +08:00
										 |  |  |           } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     return false; | 
					
						
							| 
									
										
										
										
											2006-11-17 00:46:18 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // used by optionalBlock.jelly to update the form status
 | 
					
						
							|  |  |  | //   @param sid     ID of the start marker
 | 
					
						
							|  |  |  | //   @param sid     ID of the end marker
 | 
					
						
							|  |  |  | //   @param cid     ID of the check box
 | 
					
						
							|  |  |  | function updateOptionalBlock(sid, eid, cid) { | 
					
						
							|  |  |  |     var tbl = document.getElementById(sid).parentNode; | 
					
						
							|  |  |  |     var i = false; | 
					
						
							|  |  |  |     var o = false; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     var checked = document.getElementById(cid).checked; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (var j = 0; tbl.rows[j]; j++) { | 
					
						
							|  |  |  |         var n = tbl.rows[j]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (n.id == eid) | 
					
						
							|  |  |  |             o = true; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (i && !o) { | 
					
						
							|  |  |  |             if (checked) | 
					
						
							|  |  |  |                 n.style.display = ""; | 
					
						
							|  |  |  |             else | 
					
						
							|  |  |  |                 n.style.display = "none"; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (n.id == sid) { | 
					
						
							|  |  |  |             if (n.getAttribute('hasHelp') == 'true') | 
					
						
							|  |  |  |                 j++; | 
					
						
							|  |  |  |             i = true; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2007-01-16 03:20:32 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // Auto-scroll support for progressive log output.
 | 
					
						
							|  |  |  | //   See http://radio.javaranch.com/pascarello/2006/08/17/1155837038219.html
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | function AutoScroller(scrollContainer) { | 
					
						
							|  |  |  |     // get the height of the viewport.
 | 
					
						
							|  |  |  |     // See http://www.howtocreate.co.uk/tutorials/javascript/browserwindow
 | 
					
						
							|  |  |  |     function getViewportHeight() { | 
					
						
							|  |  |  |         if (typeof( window.innerWidth ) == 'number') { | 
					
						
							|  |  |  |             //Non-IE
 | 
					
						
							|  |  |  |             return window.innerHeight; | 
					
						
							|  |  |  |         } else if (document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight )) { | 
					
						
							|  |  |  |             //IE 6+ in 'standards compliant mode'
 | 
					
						
							|  |  |  |             return document.documentElement.clientHeight; | 
					
						
							|  |  |  |         } else if (document.body && ( document.body.clientWidth || document.body.clientHeight )) { | 
					
						
							|  |  |  |             //IE 4 compatible
 | 
					
						
							|  |  |  |             return document.body.clientHeight; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return null; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return { | 
					
						
							|  |  |  |         bottomThreshold : 25, | 
					
						
							|  |  |  |         scrollContainer: scrollContainer, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         getCurrentHeight : function() { | 
					
						
							|  |  |  |             var scrollDiv = $(this.scrollContainer); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (scrollDiv.scrollHeight > 0) | 
					
						
							|  |  |  |                 return scrollDiv.scrollHeight; | 
					
						
							|  |  |  |             else | 
					
						
							|  |  |  |                 if (objDiv.offsetHeight > 0) | 
					
						
							|  |  |  |                     return scrollDiv.offsetHeight; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             return null; // huh?
 | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // return true if we are in the "stick to bottom" mode
 | 
					
						
							|  |  |  |         isSticking : function() { | 
					
						
							|  |  |  |             var scrollDiv = $(this.scrollContainer); | 
					
						
							|  |  |  |             var currentHeight = this.getCurrentHeight(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // when used with the BODY tag, the height needs to be the viewport height, instead of
 | 
					
						
							|  |  |  |             // the element height.
 | 
					
						
							|  |  |  |             //var height = ((scrollDiv.style.pixelHeight) ? scrollDiv.style.pixelHeight : scrollDiv.offsetHeight);
 | 
					
						
							|  |  |  |             var height = getViewportHeight(); | 
					
						
							|  |  |  |             var diff = currentHeight - scrollDiv.scrollTop - height; | 
					
						
							|  |  |  |             // window.alert("currentHeight=" + currentHeight + ",scrollTop=" + scrollDiv.scrollTop + ",height=" + height);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             return diff < this.bottomThreshold; | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         scrollToBottom : function() { | 
					
						
							|  |  |  |             var scrollDiv = $(this.scrollContainer); | 
					
						
							|  |  |  |             scrollDiv.scrollTop = this.getCurrentHeight(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2007-01-25 23:47:33 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // used in expandableTextbox.jelly to change a input field into a text area
 | 
					
						
							|  |  |  | function expandTextArea(button,id) { | 
					
						
							|  |  |  |     button.style.display="none"; | 
					
						
							|  |  |  |     var field = document.getElementById(id); | 
					
						
							|  |  |  |     var value = field.value.replace(/ +/g,'\n'); | 
					
						
							| 
									
										
										
										
											2007-02-15 23:44:20 +08:00
										 |  |  |     var n = field; | 
					
						
							|  |  |  |     while(n.tagName!="TABLE") | 
					
						
							|  |  |  |         n = n.parentNode; | 
					
						
							|  |  |  |     n.parentNode.innerHTML = | 
					
						
							| 
									
										
										
										
											2007-01-25 23:47:33 +08:00
										 |  |  |         "<textarea rows=8 class='setting-input' name='"+field.name+"'>"+value+"</textarea>"; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2007-02-04 23:22:26 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // refresh a part of the HTML specified by the given ID,
 | 
					
						
							|  |  |  | // by using the contents fetched from the given URL.
 | 
					
						
							|  |  |  | function refreshPart(id,url) { | 
					
						
							|  |  |  |     window.setTimeout(function() { | 
					
						
							|  |  |  |         new Ajax.Request(url, { | 
					
						
							| 
									
										
										
										
											2007-02-07 10:13:37 +08:00
										 |  |  |             method: "post", | 
					
						
							| 
									
										
										
										
											2007-02-07 10:34:48 +08:00
										 |  |  |             onSuccess: function(rsp) { | 
					
						
							| 
									
										
										
										
											2007-02-04 23:22:26 +08:00
										 |  |  |                 var hist = $(id); | 
					
						
							|  |  |  |                 var p = hist.parentNode; | 
					
						
							|  |  |  |                 var next = hist.nextSibling; | 
					
						
							|  |  |  |                 p.removeChild(hist); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 var div = document.createElement('div'); | 
					
						
							|  |  |  |                 div.innerHTML = rsp.responseText; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 p.insertBefore(div.firstChild, next); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |     }, 5000); | 
					
						
							| 
									
										
										
										
											2007-02-14 01:06:10 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* | 
					
						
							|  |  |  |     Perform URL encode. | 
					
						
							|  |  |  |     Taken from http://www.cresc.co.jp/tech/java/URLencoding/JavaScript_URLEncoding.htm
 | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | function encode(str){ | 
					
						
							|  |  |  |     var s, u; | 
					
						
							|  |  |  |     var s0 = "";                // encoded str
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (var i = 0; i < str.length; i++){   // scan the source
 | 
					
						
							|  |  |  |         s = str.charAt(i); | 
					
						
							|  |  |  |         u = str.charCodeAt(i);          // get unicode of the char
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (s == " "){s0 += "+";}       // SP should be converted to "+"
 | 
					
						
							|  |  |  |         else { | 
					
						
							|  |  |  |             if ( u == 0x2a || u == 0x2d || u == 0x2e || u == 0x5f || ((u >= 0x30) && (u <= 0x39)) || ((u >= 0x41) && (u <= 0x5a)) || ((u >= 0x61) && (u <= 0x7a))){     // check for escape
 | 
					
						
							|  |  |  |                 s0 = s0 + s;           // don't escape
 | 
					
						
							|  |  |  |             } else {                      // escape
 | 
					
						
							|  |  |  |                 if ((u >= 0x0) && (u <= 0x7f)){     // single byte format
 | 
					
						
							|  |  |  |                     s = "0"+u.toString(16); | 
					
						
							|  |  |  |                     s0 += "%"+ s.substr(s.length-2); | 
					
						
							|  |  |  |                 } else | 
					
						
							|  |  |  |                 if (u > 0x1fffff){     // quaternary byte format (extended)
 | 
					
						
							|  |  |  |                     s0 += "%" + (0xF0 + ((u & 0x1c0000) >> 18)).toString(16); | 
					
						
							|  |  |  |                     s0 += "%" + (0x80 + ((u & 0x3f000) >> 12)).toString(16); | 
					
						
							|  |  |  |                     s0 += "%" + (0x80 + ((u & 0xfc0) >> 6)).toString(16); | 
					
						
							|  |  |  |                     s0 += "%" + (0x80 + (u & 0x3f)).toString(16); | 
					
						
							|  |  |  |                 } else | 
					
						
							|  |  |  |                 if (u > 0x7ff){        // triple byte format
 | 
					
						
							|  |  |  |                     s0 += "%" + (0xe0 + ((u & 0xf000) >> 12)).toString(16); | 
					
						
							|  |  |  |                     s0 += "%" + (0x80 + ((u & 0xfc0) >> 6)).toString(16); | 
					
						
							|  |  |  |                     s0 += "%" + (0x80 + (u & 0x3f)).toString(16); | 
					
						
							|  |  |  |                 } else {                      // double byte format
 | 
					
						
							|  |  |  |                     s0 += "%" + (0xc0 + ((u & 0x7c0) >> 6)).toString(16); | 
					
						
							|  |  |  |                     s0 += "%" + (0x80 + (u & 0x3f)).toString(16); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return s0; | 
					
						
							| 
									
										
										
										
											2007-03-11 07:51:23 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-03-12 01:36:11 +08:00
										 |  |  | // create a tooltip that applies to the element of the specified ID.
 | 
					
						
							|  |  |  | // @param text
 | 
					
						
							|  |  |  | //    HTML text of the tooltip
 | 
					
						
							| 
									
										
										
										
											2007-03-11 07:51:23 +08:00
										 |  |  | function makeTooltip(id,text) { | 
					
						
							|  |  |  |     new YAHOO.widget.Tooltip("tooltip-"+id, { | 
					
						
							|  |  |  |       context:id, | 
					
						
							|  |  |  |       text:text, | 
					
						
							|  |  |  |       showDelay:500 } ); | 
					
						
							| 
									
										
										
										
											2007-03-12 02:19:38 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // when there are multiple form elements of the same name,
 | 
					
						
							|  |  |  | // this method returns the input field of the given name that pairs up
 | 
					
						
							|  |  |  | // with the specified 'base' input element.
 | 
					
						
							|  |  |  | Form.findMatchingInput = function(base, name) { | 
					
						
							|  |  |  |     // find the FORM element that owns us
 | 
					
						
							|  |  |  |     var f = base; | 
					
						
							|  |  |  |     while (f.tagName != "FORM") | 
					
						
							|  |  |  |         f = f.parentNode; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     var bases = Form.getInputs(f, null, base.name); | 
					
						
							|  |  |  |     var targets = Form.getInputs(f, null, name); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (var i=0; i<bases.length; i++) { | 
					
						
							|  |  |  |         if (bases[i] == base) | 
					
						
							|  |  |  |             return targets[i]; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return null;        // not found
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-03-20 14:01:11 +08:00
										 |  |  | // used witih <dropdownList> and <dropdownListBlock> to control visibility
 | 
					
						
							|  |  |  | function updateDropDownList(sel) { | 
					
						
							|  |  |  |     // alert('Yay! '+sel.value+' '+sel.selectedIndex);
 | 
					
						
							|  |  |  |     for (var i = 0; i < sel.forms.length; i++) { | 
					
						
							|  |  |  |         var show = sel.selectedIndex == i; | 
					
						
							|  |  |  |         var f = sel.forms[i]; | 
					
						
							|  |  |  |         var td = f.start; | 
					
						
							|  |  |  |         while (true) { | 
					
						
							|  |  |  |             td.style.display = (show ? "" : "none"); | 
					
						
							|  |  |  |             if (td == f.end) break; | 
					
						
							|  |  |  |             td = td.nextSibling; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2007-03-26 12:44:40 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // code for supporting repeatable.jelly
 | 
					
						
							|  |  |  | var repetableSupport = { | 
					
						
							|  |  |  |     // set by the inherited instance to the insertion point DIV
 | 
					
						
							|  |  |  |     insertionPoint: null, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // HTML text of the repeated chunk
 | 
					
						
							|  |  |  |     blockHTML: null, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // containing <div>.
 | 
					
						
							|  |  |  |     container: null, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // do the initialization
 | 
					
						
							|  |  |  |     init : function(container,master,insertionPoint) { | 
					
						
							|  |  |  |         this.container = $(container); | 
					
						
							|  |  |  |         this.container.tag = this; | 
					
						
							|  |  |  |         master = $(master); | 
					
						
							|  |  |  |         this.blockHTML = master.innerHTML; | 
					
						
							|  |  |  |         master.parentNode.removeChild(master); | 
					
						
							|  |  |  |         this.insertionPoint = $(insertionPoint); | 
					
						
							|  |  |  |         this.update(); | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // insert one more block at the insertion position
 | 
					
						
							|  |  |  |     expand : function() { | 
					
						
							|  |  |  |         // importNode isn't supported in IE.
 | 
					
						
							|  |  |  |         // nc = document.importNode(node,true);
 | 
					
						
							|  |  |  |         var nc = document.createElement("div"); | 
					
						
							|  |  |  |         nc.className = "repeated-chunk"; | 
					
						
							|  |  |  |         nc.innerHTML = this.blockHTML; | 
					
						
							|  |  |  |         this.insertionPoint.parentNode.insertBefore(nc, this.insertionPoint); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Behaviour.applySubtree(nc); | 
					
						
							|  |  |  |         this.update(); | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // update CSS classes associated with repeated items.
 | 
					
						
							|  |  |  |     update : function() { | 
					
						
							|  |  |  |         var children = []; | 
					
						
							|  |  |  |         for( var n=this.container.firstChild; n!=null; n=n.nextSibling ) | 
					
						
							|  |  |  |             if(Element.hasClassName(n,"repeated-chunk")) | 
					
						
							|  |  |  |                 children.push(n); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-03-30 14:25:18 +08:00
										 |  |  |         if(children.length==0) { | 
					
						
							|  |  |  |             // noop
 | 
					
						
							|  |  |  |         } else | 
					
						
							| 
									
										
										
										
											2007-03-26 12:44:40 +08:00
										 |  |  |         if(children.length==1) { | 
					
						
							|  |  |  |             children[0].className = "repeated-chunk first last only"; | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             children[0].className = "repeated-chunk first"; | 
					
						
							|  |  |  |             for(var i=1; i<children.length-1; i++) | 
					
						
							|  |  |  |                 children[i].className = "repeated-chunk middle"; | 
					
						
							|  |  |  |             children[children.length-1].className = "repeated-chunk last"; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // these are static methods that don't rely on 'this'
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // called when 'delete' button is clicked
 | 
					
						
							|  |  |  |     onDelete : function(n) { | 
					
						
							|  |  |  |         while (!Element.hasClassName(n,"repeated-chunk")) | 
					
						
							|  |  |  |             n = n.parentNode; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         var p = n.parentNode; | 
					
						
							|  |  |  |         p.removeChild(n); | 
					
						
							|  |  |  |         p.tag.update(); | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // called when 'add' button is clicked
 | 
					
						
							|  |  |  |     onAdd : function(n) { | 
					
						
							|  |  |  |         while(n.tag==null) | 
					
						
							|  |  |  |             n = n.parentNode; | 
					
						
							|  |  |  |         n.tag.expand(); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2007-04-06 22:23:09 +08:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-06-06 12:15:17 +08:00
										 |  |  | // Used by radioBlock.jelly to wire up expandable radio block
 | 
					
						
							|  |  |  | function addRadioBlock(id) { | 
					
						
							|  |  |  |     // prototype object to be duplicated for each radio button group
 | 
					
						
							|  |  |  |     var radioBlockSupport = { | 
					
						
							|  |  |  |         buttons : null, | 
					
						
							| 
									
										
										
										
											2007-06-06 12:13:04 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-06-06 12:15:17 +08:00
										 |  |  |         updateButtons : function() { | 
					
						
							|  |  |  |             for( var i=0; i<this.buttons.length; i++ ) | 
					
						
							|  |  |  |                 this.buttons[i](); | 
					
						
							|  |  |  |         }, | 
					
						
							| 
									
										
										
										
											2007-06-06 12:13:04 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-06-06 12:15:17 +08:00
										 |  |  |         // update one block based on the status of the given radio button
 | 
					
						
							|  |  |  |         updateSingleButton : function(radio,blockStart,blockEnd) { | 
					
						
							|  |  |  |             var tbl = blockStart.parentNode; | 
					
						
							|  |  |  |             var i = false; | 
					
						
							|  |  |  |             var o = false; | 
					
						
							|  |  |  |             var show = radio.checked; | 
					
						
							| 
									
										
										
										
											2007-06-06 12:13:04 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-06-06 12:15:34 +08:00
										 |  |  |             for( var j=0; tbl.rows[j]; j++ ) { | 
					
						
							| 
									
										
										
										
											2007-06-06 12:15:17 +08:00
										 |  |  |               var n = tbl.rows[j]; | 
					
						
							| 
									
										
										
										
											2007-06-06 12:13:04 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-06-06 12:15:17 +08:00
										 |  |  |               if(n==blockEnd) | 
					
						
							|  |  |  |                 o = true; | 
					
						
							| 
									
										
										
										
											2007-06-06 12:13:04 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-06-06 12:15:17 +08:00
										 |  |  |               if( i && !o ) { | 
					
						
							|  |  |  |                 if( show ) | 
					
						
							|  |  |  |                   n.style.display = ""; | 
					
						
							|  |  |  |                 else | 
					
						
							|  |  |  |                   n.style.display = "none"; | 
					
						
							|  |  |  |               } | 
					
						
							| 
									
										
										
										
											2007-06-06 12:13:04 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-06-06 12:15:17 +08:00
										 |  |  |               if(n==blockStart) | 
					
						
							|  |  |  |                 i = true; | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2007-06-06 12:13:04 +08:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2007-06-06 12:15:17 +08:00
										 |  |  |     }; | 
					
						
							| 
									
										
										
										
											2007-06-06 12:13:04 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-06-06 12:15:17 +08:00
										 |  |  |     // when one radio button is clicked, we need to update foldable block for
 | 
					
						
							|  |  |  |     // other radio buttons with the same name. To do this, group all the
 | 
					
						
							|  |  |  |     // radio buttons with the same name together and hang it under the form object
 | 
					
						
							| 
									
										
										
										
											2007-06-06 12:13:04 +08:00
										 |  |  |     var r = document.getElementById('Rb' + id); | 
					
						
							|  |  |  |     var f = r.form; | 
					
						
							|  |  |  |     var radios = f.radios; | 
					
						
							|  |  |  |     if (radios == null) | 
					
						
							|  |  |  |         f.radios = radios = {}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     var g = radios[r.name]; | 
					
						
							|  |  |  |     if (g == null) { | 
					
						
							|  |  |  |         radios[r.name] = g = object(radioBlockSupport); | 
					
						
							|  |  |  |         g.buttons = []; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     var u = function() { | 
					
						
							|  |  |  |         g.updateSingleButton(r, | 
					
						
							|  |  |  |             document.getElementById("rb_s"+id), | 
					
						
							|  |  |  |             document.getElementById("rb_e"+id)); | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     g.buttons.push(u); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // apply the initial visibility
 | 
					
						
							|  |  |  |     u(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // install event handlers to update visibility.
 | 
					
						
							|  |  |  |     // needs to use onclick and onchange for Safari compatibility
 | 
					
						
							|  |  |  |     r.onclick = r.onchange = function() { g.updateButtons(); }; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-04-06 22:23:09 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | function updateBuildHistory(nBuild) { | 
					
						
							|  |  |  |     $('buildHistory').headers = ["n",nBuild]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     function updateBuilds() { | 
					
						
							|  |  |  |         var bh = $('buildHistory'); | 
					
						
							|  |  |  |         new Ajax.Request("./ajaxBuildHistoryUpdate", { | 
					
						
							|  |  |  |             requestHeaders: bh.headers, | 
					
						
							|  |  |  |             onSuccess: function(rsp, _) { | 
					
						
							|  |  |  |                 var rows = bh.rows; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 //delete rows with transitive data
 | 
					
						
							|  |  |  |                 while (rows.length > 2 && Element.hasClassName(rows[1], "transitive")) | 
					
						
							|  |  |  |                     Element.remove(rows[1]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 // insert new rows
 | 
					
						
							|  |  |  |                 var div = document.createElement('div'); | 
					
						
							|  |  |  |                 div.innerHTML = rsp.responseText; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 var pivot = rows[0]; | 
					
						
							|  |  |  |                 var newRows = div.firstChild.rows; | 
					
						
							|  |  |  |                 for (var i = newRows.length - 1; i >= 0; i--) { | 
					
						
							|  |  |  |                     pivot.parentNode.insertBefore(newRows[i], pivot.nextSibling); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 // next update
 | 
					
						
							|  |  |  |                 bh.headers = ["n",rsp.getResponseHeader("n")]; | 
					
						
							|  |  |  |                 window.setTimeout(updateBuilds, 5000); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     window.setTimeout(updateBuilds, 5000); | 
					
						
							|  |  |  | } |