// Copyright 2000-2007, Newsfutures, Inc. All Rights Reserved. // Confidential and Proprietary Information of Newsfutures, Inc. // various constants __hsldr = { leftMargin: 30, topMargin: 30, botMargin: 0, rightMargin: 30, mkrH: 10, mkrW: 10, valBoxW: 100, sliderImg: '/elab/imgs/slider/slider.gif', lowMkrImg: '/elab/imgs/slider/leftMarker.gif', highMkrImg: '/elab/imgs/slider/rightMarker.gif', noMkrImg: '/elab/imgs/clear.gif', dragCursorName: (document.all ? 'hand' : 'pointer'), // table of slider infos, key: slider name infoTbl : {}, // marker being dragged (if any) and associated slider info dragMkr: undefined, dragInfo: undefined, editInfo: undefined }; // get the html necessary to create horizontal slider 'name'. // width, height: the width and height of the slider bar in px // min, max: the minimum and maximum values of the slider // low, high: starting low, high forecast values // callback: if !null, callback(name, low, high) is called when the slider is changed function genHSlider(name, width, height, min, max, minRge, maxRge, low, high, callback, decimal, isTimer, ticks) { var sb = new StringBuffer(); var valid = (low != undefined); if (!valid) low = high = (min + max) / 2; var info = { name: name, width: width, height: height, min: min, max: max, minRge: minRge, maxRge: maxRge, low: low, high: high, scaleFactor: width / (max - min), maxMsValOffset: __hsldr.mkrW * (max - min) / width, callback: callback, dec: (decimal ? decimal : 0), valid: valid, isTimer: isTimer, ticks: ticks }; __hsldr.infoTbl[name] = info; // slider box sb.append("
" ) ; // sliderBg sb.append("
" + (valid ? "" : "click here to make a forecast") + "
"); // slider sb.append("
"); // low Marker sb.append("
"); // high Marker sb.append("
 
"); // low Value sb.append("
" + (valid ? getDispVal(info, info.low) : "") + "
"); // high Value sb.append("
" + (valid ? getDispVal(info, info.high) : "") + "
"); sb.append("
"); 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("
"); return sb.toString(); }