2007-09-04 01:58:19 +08:00
|
|
|
//
|
|
|
|
|
// 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.
|
|
|
|
|
//
|
|
|
|
|
|
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 = {
|
2007-09-04 03:38:52 +08:00
|
|
|
// pending requests
|
|
|
|
|
queue : [],
|
2006-11-06 05:16:01 +08:00
|
|
|
|
2007-09-04 03:38:52 +08:00
|
|
|
inProgress : false,
|
2006-11-06 05:16:01 +08:00
|
|
|
|
2007-09-04 03:38:52 +08:00
|
|
|
delayedCheck : function(url, method, target) {
|
|
|
|
|
this.queue.push({url:url, method:method, target:target});
|
|
|
|
|
this.schedule();
|
|
|
|
|
},
|
2006-11-06 05:16:01 +08:00
|
|
|
|
2007-09-04 03:38:52 +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);
|
2006-11-06 05:16:01 +08:00
|
|
|
}
|
2007-09-04 03:38:52 +08:00
|
|
|
new Ajax.Request(url, params);
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
schedule : function() {
|
|
|
|
|
if (this.inProgress) return;
|
|
|
|
|
if (this.queue.length == 0) return;
|
|
|
|
|
|
|
|
|
|
var next = this.queue.shift();
|
|
|
|
|
this.inProgress = true;
|
|
|
|
|
|
|
|
|
|
this.sendRequest(next.url, {
|
|
|
|
|
method : next.method,
|
|
|
|
|
onComplete : function(x) {
|
|
|
|
|
next.target.innerHTML = x.responseText;
|
|
|
|
|
FormChecker.inProgress = false;
|
|
|
|
|
FormChecker.schedule();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
2006-11-06 05:16:01 +08:00
|
|
|
}
|
|
|
|
|
|
2007-09-04 03:38:52 +08:00
|
|
|
function findFollowingTR(input, className) {
|
|
|
|
|
// identify the parent TR
|
|
|
|
|
var tr = input;
|
|
|
|
|
while (tr.tagName != "TR")
|
|
|
|
|
tr = tr.parentNode;
|
2006-11-06 05:16:01 +08:00
|
|
|
|
2007-09-04 03:38:52 +08:00
|
|
|
// then next TR that matches the CSS
|
|
|
|
|
do {
|
|
|
|
|
tr = tr.nextSibling;
|
|
|
|
|
} while (tr.tagName != "TR" || tr.className != className);
|
2006-11-06 05:16:01 +08:00
|
|
|
|
2007-09-04 03:38:52 +08:00
|
|
|
return tr;
|
2006-11-06 05:16:01 +08:00
|
|
|
}
|
|
|
|
|
|
2007-06-13 12:31:20 +08:00
|
|
|
// shared tooltip object
|
|
|
|
|
var tooltip;
|
2006-11-06 05:16:01 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Behavior rules
|
|
|
|
|
//========================================================
|
2007-09-04 00:46:57 +08:00
|
|
|
// using tag names in CSS selector makes the processing faster
|
2007-09-04 00:46:13 +08:00
|
|
|
function registerValidator(e) {
|
2007-09-04 03:38:52 +08:00
|
|
|
e.targetElement = findFollowingTR(e, "validation-error-area").firstChild.nextSibling;
|
|
|
|
|
e.targetUrl = function() {
|
|
|
|
|
return eval(this.getAttribute("checkUrl"));
|
|
|
|
|
};
|
2007-09-04 00:46:13 +08:00
|
|
|
var method = e.getAttribute("checkMethod");
|
2007-09-04 03:38:52 +08:00
|
|
|
if (!method) method = "get";
|
2007-09-04 00:46:13 +08:00
|
|
|
|
|
|
|
|
FormChecker.delayedCheck(e.targetUrl(), method, e.targetElement);
|
|
|
|
|
|
2007-09-04 07:39:37 +08:00
|
|
|
e.onchange = e.onblur = function() {
|
2007-09-04 03:38:52 +08:00
|
|
|
var target = this.targetElement;
|
|
|
|
|
FormChecker.sendRequest(this.targetUrl(), {
|
|
|
|
|
method : method,
|
|
|
|
|
onComplete : function(x) {
|
|
|
|
|
target.innerHTML = x.responseText;
|
|
|
|
|
}
|
|
|
|
|
});
|
2007-09-04 00:46:13 +08:00
|
|
|
}
|
2007-09-04 01:58:19 +08:00
|
|
|
e = null; // avoid memory leak
|
2007-09-04 03:38:52 +08:00
|
|
|
}
|
2007-09-04 00:46:13 +08:00
|
|
|
|
2006-11-06 05:16:01 +08:00
|
|
|
var hudsonRules = {
|
2007-09-04 03:38:52 +08:00
|
|
|
"BODY" : function() {
|
|
|
|
|
tooltip = new YAHOO.widget.Tooltip("tt", {context:[]});
|
|
|
|
|
},
|
2007-06-13 12:31:20 +08:00
|
|
|
|
2007-09-04 07:19:56 +08:00
|
|
|
"TABLE.sortable" : function(e) {// sortable table
|
|
|
|
|
ts_makeSortable(e);
|
|
|
|
|
},
|
|
|
|
|
|
2007-09-06 14:10:35 +08:00
|
|
|
"TABLE.progress-bar" : function(e) {// sortable table
|
|
|
|
|
e.onclick = function() {
|
|
|
|
|
var href = this.getAttribute("href");
|
2007-09-06 14:38:21 +08:00
|
|
|
if(href!=null) window.location = href;
|
2007-09-06 14:10:35 +08:00
|
|
|
}
|
|
|
|
|
e = null; // avoid memory leak
|
|
|
|
|
},
|
|
|
|
|
|
2007-09-04 03:38:52 +08:00
|
|
|
"INPUT.advancedButton" : function(e) {
|
|
|
|
|
e.onclick = function() {
|
|
|
|
|
var link = this.parentNode;
|
|
|
|
|
link.style.display = "none"; // hide the button
|
2006-11-06 05:16:01 +08:00
|
|
|
|
2007-09-04 03:38:52 +08:00
|
|
|
var container = link.nextSibling.firstChild; // TABLE -> TBODY
|
2006-11-06 05:16:01 +08:00
|
|
|
|
2007-09-04 03:38:52 +08:00
|
|
|
var tr = link;
|
|
|
|
|
while (tr.tagName != "TR")
|
|
|
|
|
tr = tr.parentNode;
|
2006-11-06 05:16:01 +08:00
|
|
|
|
2007-09-04 03:38:52 +08:00
|
|
|
// move the contents of the advanced portion into the main table
|
|
|
|
|
while (container.lastChild != null)
|
|
|
|
|
tr.parentNode.insertBefore(container.lastChild, tr.nextSibling);
|
|
|
|
|
}
|
|
|
|
|
e = null; // avoid memory leak
|
|
|
|
|
},
|
2006-11-06 05:16:01 +08:00
|
|
|
|
2007-09-04 03:38:52 +08:00
|
|
|
// scripting for having default value in the input field
|
|
|
|
|
"INPUT.has-default-text" : function(e) {
|
|
|
|
|
var defaultValue = e.value;
|
|
|
|
|
Element.addClassName(e, "defaulted");
|
|
|
|
|
e.onfocus = function() {
|
|
|
|
|
if (this.value == defaultValue) {
|
|
|
|
|
this.value = "";
|
2007-09-04 07:35:22 +08:00
|
|
|
Element.removeClassName(this, "defaulted");
|
2007-09-04 03:38:52 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
e.onblur = function() {
|
|
|
|
|
if (this.value == "") {
|
|
|
|
|
this.value = defaultValue;
|
2007-09-04 07:35:22 +08:00
|
|
|
Element.addClassName(this, "defaulted");
|
2007-09-04 03:38:52 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
e = null; // avoid memory leak
|
|
|
|
|
},
|
2006-11-06 05:16:01 +08:00
|
|
|
|
2007-09-29 09:26:57 +08:00
|
|
|
// <label> that doesn't use ID, so that it can be copied in <repeatable>
|
|
|
|
|
"LABEL.attach-previous" : function(e) {
|
|
|
|
|
e.onclick = function() {
|
|
|
|
|
var e = this;
|
|
|
|
|
while(e.tagName!="INPUT")
|
|
|
|
|
e=e.previousSibling;
|
|
|
|
|
e.click();
|
|
|
|
|
}
|
|
|
|
|
e = null;
|
|
|
|
|
},
|
|
|
|
|
|
2007-09-04 03:38:52 +08:00
|
|
|
// 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.
|
|
|
|
|
"INPUT.validated" : registerValidator,
|
|
|
|
|
"SELECT.validated" : registerValidator,
|
|
|
|
|
"TEXTAREA.validated" : registerValidator,
|
|
|
|
|
|
|
|
|
|
// validate form values to be a number
|
|
|
|
|
"INPUT.number" : function(e) {
|
|
|
|
|
e.targetElement = findFollowingTR(e, "validation-error-area").firstChild.nextSibling;
|
|
|
|
|
e.onchange = function() {
|
|
|
|
|
if (this.value.match(/^(\d+|)$/)) {
|
|
|
|
|
this.targetElement.innerHTML = "";
|
|
|
|
|
} else {
|
|
|
|
|
this.targetElement.innerHTML = "<div class=error>Not a number</div>";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
e = null; // avoid memory leak
|
|
|
|
|
},
|
2006-11-06 05:16:01 +08:00
|
|
|
|
2007-09-04 03:38:52 +08:00
|
|
|
"A.help-button" : function(e) {
|
|
|
|
|
e.onclick = function() {
|
|
|
|
|
var tr = findFollowingTR(this, "help-area");
|
|
|
|
|
var div = tr.firstChild.nextSibling.firstChild;
|
2006-11-06 05:16:01 +08:00
|
|
|
|
2007-09-04 03:38:52 +08:00
|
|
|
if (div.style.display != "block") {
|
|
|
|
|
div.style.display = "block";
|
2006-11-06 05:16:01 +08:00
|
|
|
// make it visible
|
2007-09-04 03:38:52 +08:00
|
|
|
new Ajax.Request(
|
|
|
|
|
this.getAttribute("helpURL"),
|
|
|
|
|
{
|
|
|
|
|
method : 'get',
|
|
|
|
|
onComplete : function(x) {
|
|
|
|
|
div.innerHTML = x.responseText;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
div.style.display = "none";
|
2006-11-06 05:16:01 +08:00
|
|
|
}
|
|
|
|
|
|
2007-09-04 03:38:52 +08:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
e = null; // avoid memory leak
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
|
};
|
2007-09-04 01:58:19 +08:00
|
|
|
e = null; // avoid memory leak
|
2007-03-26 12:44:40 +08:00
|
|
|
},
|
|
|
|
|
|
|
|
|
|
"INPUT.repeatable-delete" : function(e) {
|
|
|
|
|
e.onclick = function() {
|
|
|
|
|
repetableSupport.onDelete(this);
|
|
|
|
|
};
|
2007-09-04 01:58:19 +08:00
|
|
|
e = null; // avoid memory leak
|
2007-06-13 09:39:48 +08:00
|
|
|
},
|
|
|
|
|
|
|
|
|
|
"DIV.repeated-container" : function(e) {
|
|
|
|
|
// compute the insertion point
|
|
|
|
|
var ip = e.lastChild;
|
2007-09-04 03:38:52 +08:00
|
|
|
while (!Element.hasClassName(ip, "repeatable-insertion-point"))
|
2007-06-13 09:39:48 +08:00
|
|
|
ip = ip.previousSibling;
|
|
|
|
|
// set up the logic
|
2007-09-04 03:38:52 +08:00
|
|
|
object(repetableSupport).init(e, e.firstChild, ip);
|
2007-06-13 12:31:20 +08:00
|
|
|
},
|
|
|
|
|
|
2007-09-27 11:42:55 +08:00
|
|
|
// resizable text area
|
|
|
|
|
"TEXTAREA" : function(textarea) {
|
2007-09-27 12:30:31 +08:00
|
|
|
var handle = textarea.nextSibling;
|
|
|
|
|
if(handle==null || handle.className!="textarea-handle") return;
|
|
|
|
|
|
|
|
|
|
var Event = YAHOO.util.Event;
|
|
|
|
|
|
2007-09-27 11:42:55 +08:00
|
|
|
handle.onmousedown = function(ev) {
|
2007-09-27 12:30:31 +08:00
|
|
|
ev = Event.getEvent(ev);
|
|
|
|
|
var offset = textarea.offsetHeight-Event.getPageY(ev);
|
2007-09-27 11:42:55 +08:00
|
|
|
textarea.style.opacity = 0.5;
|
|
|
|
|
document.onmousemove = function(ev) {
|
2007-09-27 12:30:31 +08:00
|
|
|
ev = Event.getEvent(ev);
|
2007-09-27 11:42:55 +08:00
|
|
|
function max(a,b) { if(a<b) return b; else return a; }
|
2007-09-27 12:30:31 +08:00
|
|
|
textarea.style.height = max(32, offset + Event.getPageY(ev)) + 'px';
|
|
|
|
|
return false;
|
2007-09-27 11:42:55 +08:00
|
|
|
};
|
|
|
|
|
document.onmouseup = function() {
|
|
|
|
|
document.onmousemove = null;
|
|
|
|
|
document.onmouseup = null;
|
|
|
|
|
textarea.style.opacity = 1;
|
|
|
|
|
}
|
2007-09-27 12:30:31 +08:00
|
|
|
};
|
|
|
|
|
handle.ondblclick = function() {
|
|
|
|
|
textarea.style.height = "";
|
|
|
|
|
textarea.rows = textarea.value.split("\n").length;
|
2007-09-27 11:42:55 +08:00
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
2007-09-28 12:33:44 +08:00
|
|
|
// structured form submission
|
|
|
|
|
"FORM" : function(form) {
|
2007-09-28 12:39:06 +08:00
|
|
|
// add the hidden 'json' input field, which receives the form structure in JSON
|
2007-09-28 12:33:44 +08:00
|
|
|
var div = document.createElement("div");
|
|
|
|
|
div.innerHTML = "<input type=hidden name=json>";
|
|
|
|
|
form.appendChild(div);
|
|
|
|
|
|
|
|
|
|
form.onsubmit = function() { buildFormTree(this) };
|
|
|
|
|
form = null; // memory leak prevention
|
|
|
|
|
},
|
|
|
|
|
|
2007-10-21 12:27:56 +08:00
|
|
|
// hook up tooltip.
|
|
|
|
|
// add nodismiss="" if you'd like to display the tooltip forever as long as the mouse is on the element.
|
2007-06-13 12:31:20 +08:00
|
|
|
"[tooltip]" : function(e) {
|
|
|
|
|
// copied from YAHOO.widget.Tooltip.prototype.configContext to efficiently add a new element
|
2007-09-05 14:50:13 +08:00
|
|
|
// event registration via YAHOO.util.Event.addListener leaks memory, so do it by ourselves here
|
2007-10-21 12:27:56 +08:00
|
|
|
e.onmouseover = function(ev) {
|
|
|
|
|
var delay = this.getAttribute("nodismiss")!=null ? 99999999 : 5000;
|
|
|
|
|
tooltip.cfg.setProperty("autodismissdelay",delay);
|
|
|
|
|
return tooltip.onContextMouseOver.call(this,YAHOO.util.Event.getEvent(ev),tooltip);
|
|
|
|
|
}
|
2007-09-27 23:28:37 +08:00
|
|
|
e.onmousemove = function(ev) { return tooltip.onContextMouseMove.call(this,YAHOO.util.Event.getEvent(ev),tooltip); }
|
|
|
|
|
e.onmouseout = function(ev) { return tooltip.onContextMouseOut .call(this,YAHOO.util.Event.getEvent(ev),tooltip); }
|
2007-06-13 12:31:20 +08:00
|
|
|
e.title = e.getAttribute("tooltip");
|
2007-09-05 14:50:13 +08:00
|
|
|
e = null; // avoid memory leak
|
2007-03-26 12:44:40 +08:00
|
|
|
}
|
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-09-27 11:42:55 +08:00
|
|
|
Behaviour.applySubtree(d);
|
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
|
|
|
}
|
|
|
|
|
|
2007-09-04 11:42:53 +08:00
|
|
|
function applyNameRef(s,e,id) {
|
|
|
|
|
$(id).groupingNode = true;
|
|
|
|
|
// s contains the node itself
|
|
|
|
|
for(var x=s.nextSibling; x!=e; x=x.nextSibling)
|
|
|
|
|
x.setAttribute("nameRef",id);
|
|
|
|
|
}
|
|
|
|
|
|
2007-09-29 08:58:19 +08:00
|
|
|
function initOptionalBlock(sid, eid, cid) {
|
|
|
|
|
applyNameRef($(sid),$(eid),cid);
|
2007-09-30 22:25:33 +08:00
|
|
|
updateOptionalBlock($(cid),false);
|
2007-09-04 11:42:53 +08:00
|
|
|
}
|
2006-11-17 00:46:18 +08:00
|
|
|
|
|
|
|
|
// used by optionalBlock.jelly to update the form status
|
2007-09-29 09:26:57 +08:00
|
|
|
// @param c checkbox element
|
2007-09-30 22:25:33 +08:00
|
|
|
function updateOptionalBlock(c,scroll) {
|
2007-09-29 09:26:57 +08:00
|
|
|
// find the start TR
|
|
|
|
|
var s = c;
|
|
|
|
|
while(!s.hasClassName("optional-block-start"))
|
|
|
|
|
s = s.parentNode;
|
|
|
|
|
|
|
|
|
|
var tbl = s.parentNode;
|
2006-11-17 00:46:18 +08:00
|
|
|
var i = false;
|
|
|
|
|
var o = false;
|
|
|
|
|
|
2007-09-29 09:26:57 +08:00
|
|
|
var checked = c.checked;
|
2007-10-01 06:29:52 +08:00
|
|
|
var lastRow = null;
|
2006-11-17 00:46:18 +08:00
|
|
|
|
|
|
|
|
for (var j = 0; tbl.rows[j]; j++) {
|
|
|
|
|
var n = tbl.rows[j];
|
|
|
|
|
|
2007-09-29 09:26:57 +08:00
|
|
|
if (i && n.hasClassName("optional-block-end"))
|
2006-11-17 00:46:18 +08:00
|
|
|
o = true;
|
|
|
|
|
|
|
|
|
|
if (i && !o) {
|
2007-09-30 22:25:33 +08:00
|
|
|
if (checked) {
|
2006-11-17 00:46:18 +08:00
|
|
|
n.style.display = "";
|
2007-10-01 06:29:52 +08:00
|
|
|
lastRow = n;
|
2007-09-30 22:25:33 +08:00
|
|
|
} else
|
2006-11-17 00:46:18 +08:00
|
|
|
n.style.display = "none";
|
|
|
|
|
}
|
|
|
|
|
|
2007-09-29 09:26:57 +08:00
|
|
|
if (n==s) {
|
2006-11-17 00:46:18 +08:00
|
|
|
if (n.getAttribute('hasHelp') == 'true')
|
|
|
|
|
j++;
|
|
|
|
|
i = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
2007-09-30 22:25:33 +08:00
|
|
|
|
2007-10-01 06:29:52 +08:00
|
|
|
if(checked && scroll) {
|
|
|
|
|
var D = YAHOO.util.Dom;
|
|
|
|
|
|
|
|
|
|
var r = D.getRegion(s);
|
|
|
|
|
if(lastRow!=null) r = r.union(D.getRegion(lastRow));
|
|
|
|
|
scrollIntoView(r);
|
|
|
|
|
}
|
2006-11-17 00:46:18 +08:00
|
|
|
}
|
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
|
|
|
|
2007-09-30 22:25:33 +08:00
|
|
|
// scroll the current window to display the given element or the region.
|
|
|
|
|
function scrollIntoView(e) {
|
|
|
|
|
function calcDelta(ex1,ex2,vx1,vw) {
|
|
|
|
|
var vx2=vx1+vw;
|
|
|
|
|
var a;
|
|
|
|
|
a = Math.min(vx1-ex1,vx2-ex2);
|
|
|
|
|
if(a>0) return -a;
|
|
|
|
|
a = Math.min(ex1-vx1,ex2-vx2);
|
|
|
|
|
if(a>0) return a;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var D = YAHOO.util.Dom;
|
|
|
|
|
|
|
|
|
|
var r;
|
|
|
|
|
if(e.tagName!=null) r = D.getRegion(e);
|
|
|
|
|
else r = e;
|
|
|
|
|
|
|
|
|
|
var dx = calcDelta(r.left,r.right, document.body.scrollLeft, D.getViewportWidth());
|
|
|
|
|
var dy = calcDelta(r.top, r.bottom,document.body.scrollTop, D.getViewportHeight());
|
|
|
|
|
window.scrollBy(dx,dy);
|
|
|
|
|
}
|
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;
|
|
|
|
|
|
2007-06-13 12:31:20 +08:00
|
|
|
var node = div.firstChild;
|
|
|
|
|
p.insertBefore(node, next);
|
2007-09-04 03:35:50 +08:00
|
|
|
|
2007-06-13 12:31:20 +08:00
|
|
|
Behaviour.applySubtree(node);
|
2007-09-04 03:35:50 +08:00
|
|
|
|
|
|
|
|
refreshPart(id,url);
|
2007-02-04 23:22:26 +08:00
|
|
|
}
|
|
|
|
|
});
|
2007-09-06 00:57:59 +08:00
|
|
|
}, 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 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,
|
|
|
|
|
|
2007-09-29 11:58:25 +08:00
|
|
|
// block name for structured HTML
|
|
|
|
|
name : null,
|
|
|
|
|
|
2007-03-26 12:44:40 +08:00
|
|
|
// 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);
|
2007-09-29 11:58:25 +08:00
|
|
|
this.name = master.getAttribute("name");
|
2007-03-26 12:44:40 +08:00
|
|
|
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";
|
2007-09-29 11:58:25 +08:00
|
|
|
nc.setAttribute("name",name);
|
2007-03-26 12:44:40 +08:00
|
|
|
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
|
2007-09-04 11:42:53 +08:00
|
|
|
updateSingleButton : function(radio, blockStart, blockEnd) {
|
2007-06-06 12:15:17 +08:00
|
|
|
var tbl = blockStart.parentNode;
|
|
|
|
|
var i = false;
|
|
|
|
|
var o = false;
|
|
|
|
|
var show = radio.checked;
|
2007-06-06 12:13:04 +08:00
|
|
|
|
2007-09-04 11:42:53 +08:00
|
|
|
for (var j = 0; tbl.rows[j]; j++) {
|
|
|
|
|
var n = tbl.rows[j];
|
2007-06-06 12:13:04 +08:00
|
|
|
|
2007-09-04 11:42:53 +08:00
|
|
|
if (n == blockEnd)
|
|
|
|
|
o = true;
|
2007-06-06 12:13:04 +08:00
|
|
|
|
2007-09-04 11:42:53 +08:00
|
|
|
if (i && !o) {
|
|
|
|
|
if (show)
|
|
|
|
|
n.style.display = "";
|
|
|
|
|
else
|
|
|
|
|
n.style.display = "none";
|
|
|
|
|
}
|
2007-06-06 12:13:04 +08:00
|
|
|
|
2007-09-04 11:42:53 +08:00
|
|
|
if (n == blockStart)
|
|
|
|
|
i = true;
|
2007-06-06 12:15:17 +08:00
|
|
|
}
|
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 = [];
|
|
|
|
|
}
|
|
|
|
|
|
2007-09-04 11:42:53 +08:00
|
|
|
var s = document.getElementById("rb_s"+id);
|
|
|
|
|
var e = document.getElementById("rb_e"+id);
|
|
|
|
|
|
2007-06-06 12:13:04 +08:00
|
|
|
var u = function() {
|
2007-09-04 11:42:53 +08:00
|
|
|
g.updateSingleButton(r,s,e);
|
2007-06-06 12:13:04 +08:00
|
|
|
};
|
2007-09-04 11:42:53 +08:00
|
|
|
applyNameRef(s,e,'Rb'+id);
|
2007-06-06 12:13:04 +08:00
|
|
|
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
|
|
|
|
2007-10-09 21:45:30 +08:00
|
|
|
function updateBuildHistory(ajaxUrl,nBuild) {
|
2007-04-06 22:23:09 +08:00
|
|
|
$('buildHistory').headers = ["n",nBuild];
|
|
|
|
|
|
|
|
|
|
function updateBuilds() {
|
|
|
|
|
var bh = $('buildHistory');
|
2007-10-09 21:45:30 +08:00
|
|
|
new Ajax.Request(ajaxUrl, {
|
2007-04-06 22:23:09 +08:00
|
|
|
requestHeaders: bh.headers,
|
2007-09-04 00:46:13 +08:00
|
|
|
onSuccess: function(rsp) {
|
2007-04-06 22:23:09 +08:00
|
|
|
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;
|
2007-07-30 01:29:45 +08:00
|
|
|
Behaviour.applySubtree(div);
|
2007-04-06 22:23:09 +08:00
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
2007-07-28 13:31:33 +08:00
|
|
|
|
|
|
|
|
// send async request to the given URL (which will send back serialized ListBoxModel object),
|
|
|
|
|
// then use the result to fill the list box.
|
|
|
|
|
function updateListBox(listBox,url) {
|
|
|
|
|
new Ajax.Request(url, {
|
|
|
|
|
method: "post",
|
2007-09-04 00:46:13 +08:00
|
|
|
onSuccess: function(rsp) {
|
2007-07-28 13:31:33 +08:00
|
|
|
var l = $(listBox);
|
|
|
|
|
while(l.length>0) l.options[0] = null;
|
|
|
|
|
|
|
|
|
|
var opts = eval('('+rsp.responseText+')').values;
|
|
|
|
|
for( var i=0; i<opts.length; i++ ) {
|
|
|
|
|
l.options[i] = new Option(opts[i].name,opts[i].value);
|
|
|
|
|
if(opts[i].selected)
|
|
|
|
|
l.selectedIndex = i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
2007-08-05 09:48:03 +08:00
|
|
|
}
|
|
|
|
|
|
2007-08-05 13:43:17 +08:00
|
|
|
// get the cascaded computed style value. 'a' is the style name like 'backgroundColor'
|
|
|
|
|
function getStyle(e,a){
|
|
|
|
|
if(document.defaultView && document.defaultView.getComputedStyle)
|
|
|
|
|
return document.defaultView.getComputedStyle(e,null).getPropertyValue(a.replace(/([A-Z])/g, "-$1"));
|
|
|
|
|
if(e.currentStyle)
|
2007-08-05 13:44:17 +08:00
|
|
|
return e.currentStyle[a];
|
|
|
|
|
return null;
|
2007-08-05 13:43:17 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// set up logic behind the search box
|
2007-08-05 09:48:03 +08:00
|
|
|
function createSearchBox(searchURL) {
|
|
|
|
|
var ds = new YAHOO.widget.DS_XHR(searchURL+"suggest",["suggestions","name"]);
|
|
|
|
|
ds.queryMatchCase = false;
|
|
|
|
|
var ac = new YAHOO.widget.AutoComplete("search-box","search-box-completion",ds);
|
|
|
|
|
ac.typeAhead = false;
|
2007-08-05 13:43:17 +08:00
|
|
|
|
|
|
|
|
var box = $("search-box");
|
|
|
|
|
var sizer = $("search-box-sizer");
|
|
|
|
|
var comp = $("search-box-completion");
|
|
|
|
|
var minW = $("search-box-minWidth");
|
|
|
|
|
|
|
|
|
|
Behaviour.addLoadEvent(function(){
|
|
|
|
|
// make sure all three components have the same font settings
|
|
|
|
|
function copyFontStyle(s,d) {
|
|
|
|
|
var ds = d.style;
|
|
|
|
|
ds.fontFamily = getStyle(s,"fontFamily");
|
|
|
|
|
ds.fontSize = getStyle(s,"fontSize");
|
|
|
|
|
ds.fontStyle = getStyle(s,"fontStyle");
|
|
|
|
|
ds.fontWeight = getStyle(s,"fontWeight");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
copyFontStyle(box,sizer);
|
|
|
|
|
copyFontStyle(box,minW);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// update positions and sizes of the components relevant to search
|
|
|
|
|
function updatePos() {
|
|
|
|
|
function max(a,b) { if(a>b) return a; else return b; }
|
|
|
|
|
|
|
|
|
|
sizer.innerHTML = box.value;
|
|
|
|
|
var w = max(sizer.offsetWidth,minW.offsetWidth);
|
|
|
|
|
box.style.width =
|
|
|
|
|
comp.style.width =
|
|
|
|
|
comp.firstChild.style.width = (w+60)+"px";
|
|
|
|
|
|
|
|
|
|
var pos = YAHOO.util.Dom.getXY(box);
|
|
|
|
|
pos[1] += YAHOO.util.Dom.get(box).offsetHeight + 2;
|
|
|
|
|
YAHOO.util.Dom.setXY(comp, pos);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
updatePos();
|
|
|
|
|
box.onkeyup = updatePos;
|
2007-09-01 22:21:54 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// structured form submission handling
|
|
|
|
|
// see http://hudson.gotdns.com/wiki/display/HUDSON/Structured+Form+Submission
|
|
|
|
|
function buildFormTree(form) {
|
|
|
|
|
// I initially tried to use an associative array with DOM elemnets as keys
|
|
|
|
|
// but that doesn't seem to work neither on IE nor Firefox.
|
|
|
|
|
// so I switch back to adding a dynamic property on DOM.
|
|
|
|
|
form.formDom = {}; // root object
|
|
|
|
|
|
|
|
|
|
var doms = []; // DOMs that we added 'formDom' for.
|
|
|
|
|
doms.push(form);
|
|
|
|
|
|
|
|
|
|
function addProperty(parent,name,value) {
|
2007-09-04 11:42:53 +08:00
|
|
|
// abc.def.ghi -> ghi
|
|
|
|
|
var idx = name.lastIndexOf('.');
|
|
|
|
|
if(idx>=0) name = name.substring(idx+1);
|
|
|
|
|
|
2007-09-01 22:21:54 +08:00
|
|
|
if(parent[name]!=null) {
|
2007-09-29 10:21:34 +08:00
|
|
|
if(parent[name].push==null) // is this array?
|
2007-09-01 22:21:54 +08:00
|
|
|
parent[name] = [ parent[name] ];
|
|
|
|
|
parent[name].push(value);
|
|
|
|
|
} else {
|
|
|
|
|
parent[name] = value;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// find the grouping parent node, which will have @name.
|
|
|
|
|
// then return the corresponding object in the map
|
|
|
|
|
function findParent(e) {
|
|
|
|
|
while(e!=form) {
|
|
|
|
|
e = e.parentNode;
|
2007-09-04 11:42:53 +08:00
|
|
|
|
|
|
|
|
// this is used to create a group where no single containing parent node exists,
|
|
|
|
|
// like <optionalBlock>
|
|
|
|
|
var nameRef = e.getAttribute("nameRef");
|
|
|
|
|
if(nameRef!=null)
|
|
|
|
|
e = $(nameRef);
|
|
|
|
|
|
2007-09-01 22:21:54 +08:00
|
|
|
var name = e.getAttribute("name");
|
|
|
|
|
if(name!=null) {
|
2007-09-04 11:42:53 +08:00
|
|
|
if(e.tagName=="INPUT" && !e.checked)
|
|
|
|
|
return {}; // field is not active
|
|
|
|
|
|
2007-09-01 22:21:54 +08:00
|
|
|
var m = e.formDom;
|
|
|
|
|
if(m==null) {
|
|
|
|
|
// this is a new grouping node
|
|
|
|
|
doms.push(e);
|
|
|
|
|
e.formDom = m = {};
|
|
|
|
|
addProperty(findParent(e), name, m);
|
|
|
|
|
}
|
|
|
|
|
return m;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return form.formDom; // guaranteed non-null
|
|
|
|
|
}
|
|
|
|
|
|
2007-09-28 12:33:44 +08:00
|
|
|
var jsonElement = null;
|
|
|
|
|
|
2007-09-01 22:21:54 +08:00
|
|
|
for( var i=0; i<form.elements.length; i++ ) {
|
|
|
|
|
var e = form.elements[i];
|
2007-09-28 12:33:44 +08:00
|
|
|
if(e.name=="json") {
|
|
|
|
|
jsonElement = e;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2007-09-01 22:21:54 +08:00
|
|
|
var p;
|
2007-09-04 11:42:53 +08:00
|
|
|
var type = e.getAttribute("type");
|
|
|
|
|
if(type==null) type="";
|
|
|
|
|
switch(type.toLowerCase()) {
|
2007-09-01 22:21:54 +08:00
|
|
|
case "button":
|
2007-09-04 11:42:53 +08:00
|
|
|
case "submit":
|
2007-09-01 22:21:54 +08:00
|
|
|
break;
|
|
|
|
|
case "checkbox":
|
|
|
|
|
p = findParent(e);
|
2007-09-04 11:42:53 +08:00
|
|
|
if(!e.groupingNode)
|
|
|
|
|
addProperty(p, e.name, e.checked);
|
|
|
|
|
else {
|
|
|
|
|
if(e.checked)
|
|
|
|
|
addProperty(p, e.name, e.formDom = {});
|
|
|
|
|
}
|
2007-09-01 22:21:54 +08:00
|
|
|
break;
|
|
|
|
|
case "radio":
|
|
|
|
|
if(!e.checked) break;
|
2007-09-04 11:42:53 +08:00
|
|
|
if(e.groupingNode) {
|
|
|
|
|
p = findParent(e);
|
|
|
|
|
addProperty(p, e.name, e.formDom = { value: e.value });
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2007-09-01 22:21:54 +08:00
|
|
|
// otherwise fall through
|
|
|
|
|
default:
|
|
|
|
|
p = findParent(e);
|
|
|
|
|
addProperty(p, e.name, e.value);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2007-09-28 12:33:44 +08:00
|
|
|
jsonElement.value = Object.toJSON(form.formDom);
|
2007-09-01 22:21:54 +08:00
|
|
|
|
|
|
|
|
// clean up
|
|
|
|
|
for( i=0; i<doms.length; i++ )
|
|
|
|
|
doms[i].formDom = null;
|
|
|
|
|
|
|
|
|
|
|
2007-09-29 10:06:50 +08:00
|
|
|
return jsonElement.value;
|
2007-09-03 23:28:53 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// this used to be in prototype.js but it must have been removed somewhere between 1.4.0 to 1.5.1
|
|
|
|
|
String.prototype.trim = function() {
|
2007-09-04 03:38:52 +08:00
|
|
|
var temp = this;
|
|
|
|
|
var obj = /^(\s*)([\W\w]*)(\b\s*$)/;
|
|
|
|
|
if (obj.test(temp))
|
|
|
|
|
temp = temp.replace(obj, '$2');
|
|
|
|
|
obj = / /g;
|
|
|
|
|
while (temp.match(obj))
|
|
|
|
|
temp = temp.replace(obj, " ");
|
|
|
|
|
return temp;
|
2007-09-03 23:28:53 +08:00
|
|
|
}
|