");
return sb.toString();
}
// modify the low/high values of slider 'name'
function setHSlider(name, low, high) {
var info = __hsldr.infoTbl[name];
if (info.slider == undefined)
initInfo(info);
var valid = (low != undefined);
if (!valid)
low = high = (info.min + info.max) / 2;
info.valid = valid;
info.low = low;
info.high = high;
var lowPos = valToPos(info, low);
var highPos = valToPos(info, high);
info.lmEl.style.left = (lowPos - __hsldr.mkrW) + "px";
info.lvEl.style.left = (valToPos(info, info.low) - __hsldr.valBoxW) + "px";
info.hmEl.style.left = info.hvEl.style.left = highPos + "px";
info.rgEl.style.left = lowPos + "px";
info.rgEl.style.width = scale(info, info.high - info.low) + "px";
if (valid) {
info.lvEl.innerHTML = getDispVal(info, info.low);
info.hvEl.innerHTML = getDispVal(info, info.high);
info.lmEl.style.background = "url(" + __hsldr.lowMkrImg + ")";
info.hmEl.style.background = "url(" + __hsldr.highMkrImg + ")";
} else {
info.lvEl.innerHTML = "";
info.hvEl.innerHTML = "";
info.lmEl.style.background = "url(" + __hsldr.noMkrImg + ")";
info.hmEl.style.background = "url(" + __hsldr.noMkrImg + ")";
}
}
// get a [low, high] array with the current values
function getSliderValues(name) {
var info = __hsldr.infoTbl[name];
return [info.low, info.high];
}
// return the display value for 'val' using 'info'
function getDispVal(info, val) {
var res;
if (info.isTimer) {
res = valToDate(val);
} else {
res = roundTo(val, info.dec);
}
return res;
}
////
// Screen positioning methods
////
// convert a value range into a pixel length
function scale(info, len) {
return len * info.scaleFactor;
}
// convert a slider value into a pixel position
function valToPos(info, val) {
return __hsldr.leftMargin + scale(info, val - info.min);
}
// convert a pixel position into a slider value
function posToVal(info, pos) {
return (pos / info.scaleFactor) + info.min;
}
// convert a value to an iso date
function valToDate(val) {
return (val == 0) ? "" : tsToIsoDateTime(val * 1000, true, false);
}
////
// mouse event handlers
////
// handle the initial mousedown on the slider box.
// from here, decide where it goes (we use this instead of direct
// handlers on elements because of the way they hide each other)
function hdlMsDn(name, ev) {
var info = __hsldr.infoTbl[name];
if (!info.slider)
initInfo(info);
var msCoords = getEvCoords(ev);
if (isHit(msCoords, info.bgEl)) {
startDrag(info, ev);
} else if (info.valid) {
if (isHit(msCoords, info.lvEl)) {
startEdit(info, "low");
} else if (isHit(msCoords, info.hvEl)) {
startEdit(info, "high");
}
}
}
function startDrag(info, ev) {
// if currently editing something, terminate that first
if (__hsldr.editInfo)
endEdit();
// get the current slider clicked on
__hsldr.dragInfo = info;
var valid = info.valid;
// setup mouse events on the document to catch all ms movements and release
document.onmousemove = hdlSliderMv;
document.onmouseup = hdlSliderUp;
// if needed create initial forecast
info.msValOffset = 0;
var val = eventToVal(info, ev);
if (!valid) {
info.valid = true;
info.lmEl.style.background = "url(" + __hsldr.lowMkrImg + ")";
info.hmEl.style.background = "url(" + __hsldr.highMkrImg + ")";
var halfRge = info.maxRge / 2;
if (val < info.min + halfRge)
val = info.min + halfRge;
if (val > info.max - halfRge)
val = info.max - halfRge;
info.low = val - halfRge;
info.high = val + halfRge;
info.bgEl.innerHTML = "";
}
// decide for which marker this is for and update the slider value
if (val <= info.low) {
info.what = "low";
info.slider.style.cursor = 'w-resize';
if ((info.low - val) < info.maxMsValOffset)
info.msValOffset = info.low - val;
} else if (val < info.high) {
info.what = "drag";
info.slider.style.cursor = __hsldr.dragCursorName;
info.msValOffset = ((info.high + info.low) / 2) - val;
} else {
info.what = "high";
info.slider.style.cursor = 'e-resize';
if ((val - info.high) < info.maxMsValOffset)
info.msValOffset = info.high - val;
}
val += info.msValOffset;
updateRange(info, val);
if (info.callback)
info.callback(info, "start");
}
function hdlSliderMv(ev) {
var info = __hsldr.dragInfo;
updateRange(info, eventToVal(info, ev));
if (info.callback)
info.callback(info, "move");
}
function hdlSliderUp(ev) {
var info = __hsldr.dragInfo;
__hsldr.dragInfo = null;
updateRange(info, eventToVal(info, ev));
document.onmousemove = null;
document.onmouseup = null;
if (info.callback)
info.callback(info, "done");
info.slider.style.cursor = 'auto';
}
// click on a value box, transform it in an input field
function startEdit(info, hl) {
// already in an edit?
if (__hsldr.editInfo) {
// ignore clicks on self if already editing
if (info == __hsldr.editInfo)
return;
// otherwise terminate other edit first
endEdit();
}
// meaningless click
if (!info.valid)
return;
// start editing
__hsldr.editInfo = info;
info.what = hl;
if (hl == "low") {
info.lvEl.innerHTML = "";
document.getElementById("__hsldr_input").focus();
} else {
info.hvEl.innerHTML = "";
document.getElementById("__hsldr_input").focus();
}
}
// this is called when any of these occur:
// - the active value input field looses focus
// - the return key is pressed
// - a slider or a value box is clicked
// it tries to accept the current input value
function endEdit() {
var info = __hsldr.editInfo;
if (!info)
return;
__hsldr.editInfo = null;
var el = document.getElementById("__hsldr_input");
var val = new Number(el.value);
if (info.what == "low") {
info.lvEl.innerHTML = getDispVal(info, info.low);
if (isNaN(val) || (val < info.min) || (val > (info.max - info.minRge))) {
highlight(info.lvEl.id);
return;
}
} else {
info.hvEl.innerHTML = getDispVal(info, info.high);
if (isNaN(val) || (val > info.max) || (val < (info.min + info.minRge))) {
highlight(info.hvEl.id);
return;
}
}
updateRange(info, val, true);
}
// cause CR or NL to terminate editing a value
function hdlKeyUp(ev) {
if ((ev.keyCode == 10) || (ev.keyCode == 13))
endEdit();
}
function initInfo(info) {
info.slider = document.getElementById("__hsldr_" + info.name);
info.bgEl = document.getElementById("__hsldrBg_" + info.name);
info.rgEl = document.getElementById("__hsldrRg_" + info.name);
info.lmEl = document.getElementById("__hsldrLM_" + info.name);
info.hmEl = document.getElementById("__hsldrHM_" + info.name);
info.lvEl = document.getElementById("__hsldrLV_" + info.name);
info.hvEl = document.getElementById("__hsldrHV_" + info.name);
}
// return the value (not pos) of event 'ev' for the slider defined in info.
// add info.msValOffset to the actual value pointed to by the ms
// Note: for IE, get the current window event.
function eventToVal(info, ev) {
var x = xlateCoords(ev, info.bgEl).x;
var val = posToVal(info, x) + info.msValOffset;
if (val < info.min)
val = info.min;
else if (val > info.max)
val = info.max;
return val;
}
// update a slider by moving either low or high mkr to new value
function updateRange(info, newVal, ignoreTicks) {
if (info.ticks && !ignoreTicks) {
var ticks = info.ticks;
var r = newVal % ticks;
if (r < (ticks / 2))
newVal -= r;
else
newVal += ticks - r;
}
if (info[info.what] == newVal)
return;
var updLow = false;
var updHigh = false;
switch (info.what) {
case "low":
updLow = true;
if (newVal > (info.max - info.minRge))
newVal = (info.max - info.minRge);
info.low = newVal;
if ((newVal + info.minRge) > info.high) {
updHigh = true;
info.high = (newVal + info.minRge);
} else if ((newVal + info.maxRge) < info.high) {
updHigh = true;
info.high = (newVal + info.maxRge);
}
break;
case "drag":
updHigh = updLow = true;
var halfRge = (info.high - info.low) / 2;
if (newVal < (info.min + halfRge))
newVal = info.min + halfRge;
else if (newVal > (info.max - halfRge))
newVal = info.max - halfRge;
info.low = Math.max(info.min, newVal - halfRge);
info.high = Math.min(info.max, newVal + halfRge);
break;
case "high":
updHigh = true;
info.high = newVal;
if (newVal < (info.min + info.minRge))
newVal = (info.min + info.minRge);
info.high = newVal;
if (info.low > (newVal - info.minRge)) {
updLow = true;
info.low = (newVal - info.minRge);
} else if (info.low < (newVal - info.maxRge)) {
updLow = true;
info.low = (newVal - info.maxRge);
}
break;
}
var lowPos = valToPos(info, info.low);
var highPos = valToPos(info, info.high);
if (updLow) {
info.lmEl.style.left = (lowPos - __hsldr.mkrW) + "px";
info.lvEl.style.left = (valToPos(info, info.low) - __hsldr.valBoxW) + "px";
info.lvEl.innerHTML = getDispVal(info, info.low);
}
if (updHigh) {
info.hmEl.style.left = info.hvEl.style.left = highPos + "px";
info.hvEl.innerHTML = getDispVal(info, info.high);
}
info.rgEl.style.left = lowPos + "px";
info.rgEl.style.width = scale(info, info.high - info.low) + "px";
}
////
// Mouse and element coordinates
////
// get the coordinates of 'ev' ralative to element 'el'
function xlateCoords(ev, el) {
ev = ev || window.event;
var msCoords = getEvCoords(ev);
var elCoords = getElCoords(el);
return {x:msCoords.x - elCoords.x, y:msCoords.y - elCoords.y};
}
// return true iff coords are within el's area
function isHit(coords, el) {
var elCoords = getElCoords(el);
return ((coords.x < elCoords.x) || (coords.x >= (elCoords.x + el.offsetWidth)) ||
(coords.y < elCoords.y) || (coords.y >= (elCoords.y + el.offsetHeight)))
? false
: true;
}
// get an element's coords relative to document top left
function getElCoords(el) {
var left = 0;
var top = 0;
while (el.offsetParent) {
left += el.offsetLeft;
top += el.offsetTop;
el = el.offsetParent;
}
left += el.offsetLeft;
top += el.offsetTop;
return {x:left, y:top};
}
// return event coordinates relative to document top left
function getEvCoords(ev) {
if (ev.pageX || ev.pageY) {
return {x:ev.pageX, y:ev.pageY};
}
return {
x:ev.clientX + document.body.scrollLeft - document.body.clientLeft,
y:ev.clientY + document.body.scrollTop - document.body.clientTop
};
}
////
// debug utils
////
var dumpFunctions = false;
var dumpNull = false;
var skipKeys = {"innerText": true, "outerText": true, "innerHTML":true, "outerHTML": true, "textContent": true};
function dump(dict, tag) {
var a = [];
for (var k in dict) {
if (!k)
continue;
if (skipKeys[k])
continue;
if ((tag != undefined) && (k.toLowerCase().indexOf(tag) < 0))
continue;
var v = dict[k];
if ((!dumpNull) && !v)
continue;
if (("" + v).indexOf("function ") == 0) {
if (!dumpFunctions)
continue;
v = "*func*";
}
a.push([k,v]);
}
a.sort();
var rowCnt = Math.ceil(a.length / 3) + 1;
var sb = new StringBuffer();
sb.append("
");
for (var n in a) {
if ((n % rowCnt) == (rowCnt - 1))
sb.append("
");
var e = a[n];
sb.append("" + e[0] + ":" + e[1] + " ");
}
sb.append("