//--------------------------------------------
//              General Utils
//--------------------------------------------

var GeneralUtils = {

//-----------------------------------------------------
getPos : function(o) {
var pos = {};
var leftX = 0;
var leftY = 0;
if(o.offsetParent) {
	while(o.offsetParent) {
		leftX += o.offsetLeft;
		leftY += o.offsetTop;
		o = o.offsetParent;
	}
} else if(o.x) {
	leftX += o.x;
	leftY += o.y;
}
pos.x = leftX;
pos.y = leftY;
return pos;
},

//-----------------------------------------------------
// move a abs positioned element to an x y location
moveElem : function(o, x, y) {
o=o.style;
o.left=x + 'px';
o.top=y + 'px';
},

//-----------------------------------------------------
getWindowDimensions : function () {
var out = {};
if (window.pageXOffset) {
	out.scrollX = window.pageXOffset;
	out.scrollY = window.pageYOffset;
} else if (document.documentElement) {
	out.scrollX = document.body.scrollLeft + document.documentElement.scrollLeft; 
	out.scrollY = document.body.scrollTop + document.documentElement.scrollTop;
} else if (document.body.scrollLeft >= 0) {
	out.scrollX = document.body.scrollLeft;
	out.scrollY = document.body.scrollTop;
}

if (document.compatMode == "BackCompat") {
  out.width = document.body.clientWidth;
	out.height = document.body.clientHeight;
} else {
	out.width = document.documentElement.clientWidth;
	out.height = document.documentElement.clientHeight;
}
return out;
},

//-------------------------------------------------
getElementFromEvent : function (evt) {
if (!evt)
	evt = window.event;
return ((evt.target) ? evt.target : evt.srcElement);
},

//-------------------------------------------------
getMousePosFromEvent : function (evt) {
if (!evt)
	evt = window.event;
var pos = {};
if (evt.pageX) 	{
  pos.x = evt.pageX;
  pos.y = evt.pageY;
}
else if (evt.clientX) 	{
  pos.x = evt.clientX + document.body.scrollLeft
    + document.documentElement.scrollLeft;
  pos.y = evt.clientY + document.body.scrollTop
    + document.documentElement.scrollTop;
}
return pos;
},

//-------------------------------------------------
removeListener : function (element, eventType, handler, capture) {
if (element.removeEventListener) {
	element.removeEventListener(eventType, handler, capture);
	return true;
}
else if(element.detachEvent)
	return element.detachEvent('on' + eventType, handler);
else
	element["on" + eventType] = null;
},

//------------------------------------------------------
modifyClassName : function (elem, add, string) {
var s = (elem.className) ? elem.className : "";
if (add)
	s += " " + string;
else {
  var a = s.split(" ");
  s = "";
  for (var i=0; i<a.length; i++)
    if (a[i] != string)
      s += a[i] + " "; 
}
elem.className = s;
},

//------------------------------------------------------
createElement : function(a) {
var i = 2, j, s, p, c;
var elem = a[0];

if (typeof(elem)=="string") {
  elem = document.createElement(elem);
}
 
if ((c = a[1]) != null) {
  if (c.length || c.html || c.tagName) {
    i = 1;
  }
  else {
    s = c.style;
    if (s)
      for (j in s)
        elem.style[j] = s[j];
    for (j in c) {
      p = c[j];
    if (p != s)
      elem[j] = p;
    }
    c = a[2];
  }
  while (i<a.length) {
    if (typeof(c) == "object") {
      if (c.length) {
        var t = c;
        c = GeneralUtils.createElement(c);
        t[0] = c;
      } else if (c.html) {
        var d = document.createElement ("div");
        d.innerHTML = c.html;
        var arr = [], j, len = d.childNodes.length;
        for (j=len-1; j >= 0; j--)
          arr[j] = d.removeChild(d.childNodes[j]);
        for (j=0; j < len; j++)
          elem.appendChild(arr[j]);
        i++;
        c = a[i];
        continue;
      }
      // else: already dom element
    } else
      c = document.createTextNode(c);
    elem.appendChild(c);
    i++;
    c = a[i];
  }
}
return elem;
},

//------------------------------------------------------
createDomElems : function(array, innerElems) {
var e = GeneralUtils.createElement (array);
for (var i in innerElems)
 if (innerElems[i][0])
  innerElems[i] = innerElems[i][0];
return e;
},

addMembers : function (to, from) {
for (var i in from)
    to[i] = from[i];
},


createElementOld : function(elem,properties) {
var i, s, p, c;
if (typeof(elem)=="string")
	elem = document.createElement(elem);
if (properties) {
	s = properties.style;
	if (s)
		for (i in s)
			elem.style[i] = s[i];
	for (i in properties) {
		p = properties[i];
		if (p != s)
			elem[i] = p;
		}
	}
for (i=2; i<arguments.length; i++) {
	c = arguments[i];
	if (typeof(c) == "string")
		c = document.createTextNode(c);
	elem.appendChild(c);
	}
	

return elem;
},


//prevents IE memory leak, nulls all functions attached to a dom element and its children
purgeEventHandlers : function (elem) {
var a, i, l,
    events = ["onmouseover", "onmouseout", "onclick", "onmousedown", "onmouseup", "onmousemove", "onkeyup", "onkeydown", "onkeypress"];
if (elem.mouseEventHandler || elem.aaEvtHandler) {
  elem.mouseEventHandler = null;
  elem.aaEvtHandler = null;
  l = events.length;
  for (i=0; i<l; i++)
    elem[events[i]] = null;
}
a = elem.childNodes;
if (a) {
  l = a.length;
  for (i = 0; i < l; i++) {
    GeneralUtils.purgeEventHandlers(elem.childNodes[i]);
  }
}
},

//------------------------------------------------
// callbacks can be an array, with members as follows:
//
// 0: function pointer
// 1: arguments (array or single object/variable)
// 2: "this" object (may be null)
// 3: forced return value (null to return whatever callback returns)
// 4: true to force it to pass an array (item 1) as a single object
//    rather than individual parameters
//
// this function can have additional arguments, which will be passed
// to the callback *after* the user argument(s).  If you need to send
// a variable number of user args (i.e. some are optional), best to 
// pass them packed into a single object

doCallback : function (callback) {
var ret;
try {
  if (callback.length != null) { // is it an array?
    // is "user args" an array (that should be passed as individual args)
    if (callback[1] != null && callback[1].length && callback[4] == null) {
      if (arguments.length > 1)  { // additional api args
        var i, a = [];
        // user args
        for (i=0; i < callback[1].length; i++)
          a.push(callback[1][i]);
        // api args
        for (var i=1; i < arguments.length; i++)
          a.push(arguments[i]);
        ret = callback[0].apply (callback[2], a);
      }
      else // no api args
        ret = callback[0].apply (callback[2], callback[1]);
    } else { // "user args" is a single object/variable
      if (arguments.length > 1)  { // additional api args
        var i, a = [];
        // user arg
        a.push(callback[1]);
        // api args
        for (var i=1; i < arguments.length; i++)
          a.push(arguments[i]);
        ret = callback[0].apply (callback[2], a);
      }
      else // no api args
        ret = callback[0].call (callback[2], callback[1]);
    }
    if (callback[3] != null) // forced return value
      return callback[3];
    }
  else { // callback just a function
    ret = callback ();
    }
} catch (e) {
  //alert("callback error (" + e + "): " +
    //    GeneralUtils.objectToString(callback, 0),8);
  return;
}
return ret;
},

makeUnitVector : function (p1, p2) {
var uv = GeneralUtils.makeVector(p1, p2),
    mag = GeneralUtils.getMagnitude(uv);
uv.x /= mag;
uv.y /= mag;
return uv;
},

makeVector : function (p1, p2) {
var v = {};
v.x = p1.x - p2.x;
v.y = p1.y - p2.y;
return v;
},

dotProduct : function (v1, v2) {
return (v1.x*v2.x) + (v1.y*v2.y);
},

projectPoint : function (p, uv, d) {
var pp = {};
pp.x = (uv.x*d) + p.x;
pp.y = (uv.y*d) + p.y;
return pp;
},

getDistance : function (p1, p2) {
return Math.sqrt(((p2.x-p1.x)*(p2.x-p1.x)) + ((p2.y-p1.y)*(p2.y-p1.y)));
},

getMagnitude : function (v) {
return Math.sqrt((v.x*v.x)+(v.y*v.y));
},

//--------------------------------------------------
// interpolates/extrapolates values from a provided table, x values of map argument are expected to be in ascending order
getInterpolatedValue : function(x, map) {
	for (var i=0;i<map.length;i++) {
		if ((x<map[0][0])?(x<map[0][0]):(map[i][0]<=x && map[i+1][0]>x) || i==map.length-2) {
			return (map[i][1])+(((x-(map[i][0]))/((map[i+1][0])-(map[i][0])))*((map[i+1][1])-(map[i][1])));
		}
	}
},

//------------------------------------------------------
scrollWindowOneIncrement : function (elem, ss) {
var diff = ss.yTarget - ss.currY;
if (diff < ss.scrollSpeed)	{
	window.scrollTo (0, ss.currY + diff);
	if (ss.callback)
		ss.callback.apply (null, ss.callbackArgs);
	}
else {
	ss.currY += ss.scrollSpeed;
	window.scrollTo (0, ss.currY);
	EventHandlers.setTimerFunction ([GeneralUtils.scrollWindowOneIncrement, [elem, ss]], 20);
	}
},

//------------------------------------------------------
scrollWindowToElement : function (elem, callback, callbackArgs) {
var speed,
	dims = GeneralUtils.getWindowDimensions(),
	ss = {dims: dims,
		currY: dims.scrollY,
		callback: callback,
		callbackArgs: callbackArgs},
	y = GeneralUtils.getPos(elem).y, h = dims.height, bottomOfWindow = ss.currY + h, bottomOfElem = (y + elem.offsetHeight	+ 4), diff = bottomOfElem - bottomOfWindow;

if (diff <= 0) {
	if (ss.callback)
		ss.callback.apply (null, ss.callbackArgs);
	return;
	}
ss.yTarget = bottomOfElem - h;
ss.scrollSpeed = GeneralUtils.getInterpolatedValue (diff, [[100, 20], [2000, 400], [3000, 800]]);
GeneralUtils.scrollWindowOneIncrement (elem, ss);
},


/* not needed by color picker */
//--------------------------------------------------------------
// get an element within another by its tag type and its classname
// (or partial classname, for instance if the classname is "whatever"
// you can get it by "what" but not "ever")
getDecendantElem : function (container, type, classname) {
if (container != null) {
	var i, e, len, elems = container.getElementsByTagName(type); 
	len = elems.length; 
	for (i = 0; i < len; i++) 
		{
		e = elems.item(i);
		if (e.className && e.className.indexOf(classname)==0)
			return e;
		}
	}
return null;
},

//-------------------------------------------------
getCookie : function(name) {
if(name.length > 0 && document.cookie.length > 0) {
	var begin = document.cookie.indexOf(name+"="); 
	if(begin != -1) {
		begin += name.length+1; 
		var end = document.cookie.indexOf(";", begin);
		if(end == -1)
			end = document.cookie.length;
		return unescape(document.cookie.substring(begin, end));
		} 
	}
return null; 
},

//-------------------------------------------------
setCookie : function(cookieName, cookieValue, path, nDays) {
var today = new Date();
var expire = new Date();
expire.setTime(today.getTime() + 3600000*24*nDays);
document.cookie = cookieName + "=" + escape(cookieValue) +
	((path) ? "; path=" + path : "") +			
	";expires=" + expire.toGMTString();
},

//-------------------------------------------------
deleteCookie : function(name, path) {
K.setCookie(name, "", path, -1);
}

};


///////////////////////////////////////////////////////////////////////////////////////////////////

//--------------------------------------------
//             Event Handlers
//--------------------------------------------
var EventHandlers = {
//-------------------------------------------
setMouseEventHandler : function(elem, callback, moreParams) {
elem.mouseEventHandler = {callback: callback};
elem.onmouseover = EventHandlers.mouseEventHandlers.over;
elem.onmouseout = EventHandlers.mouseEventHandlers.out;
if (moreParams && moreParams.blockSelect)
  elem.mouseEventHandler.blockSelect = true;
if (moreParams && moreParams.draggable)
  elem.onmousedown = EventHandlers.mouseEventHandlers.startdrag;
else  {
  elem.onclick = EventHandlers.mouseEventHandlers.click;
  elem.onmousedown = EventHandlers.mouseEventHandlers.down;   
}
return elem;
},

mouseEventHandlers : {
dragElement : null,

click : function(evt) {
var o = this.mouseEventHandler;
return GeneralUtils.doCallback (o.callback, "click", this, ((evt)?evt:window.event));
},

down : function(evt) {
var o = this.mouseEventHandler;
if (!evt)
  evt = window.event;
if (o.blockSelect) {
  if (document.all) {
    evt.returnValue = false;
    evt.cancelBubble = true;
  } else
    evt.preventDefault();
}
evt.returnValue = false;
evt.cancelBubble = true;
GeneralUtils.doCallback (o.callback, "down", this, ((evt)?evt:window.event));
return false;
},

startdrag : function(evt) {
EventHandlers.mouseEventHandlers.dragElement = this;
EventHandlers.setListener (document, "mousemove", EventHandlers.mouseEventHandlers.drag, true);
EventHandlers.setListener (document, "mouseup", EventHandlers.mouseEventHandlers.enddrag, true);
var o = this.mouseEventHandler;
if (!evt)
  evt = window.event;
if (o.blockSelect) {
  if (document.all) {
    evt.returnValue = false;
    evt.cancelBubble = true;
  } else
    evt.preventDefault();
} else {
  evt.returnValue = false;
  evt.cancelBubble = true;
}
return GeneralUtils.doCallback (o.callback, "startdrag", this, evt);
},

drag : function(evt) {
var elem = EventHandlers.mouseEventHandlers.dragElement;
var o = elem.mouseEventHandler;
if (!evt) {
  evt = window.event;
}
if (evt.preventDefault) {
  if (document.all) {
    evt.returnValue = false;
    evt.cancelBubble = true;
  } else
    evt.preventDefault();
} else {
  evt.returnValue = false;
  evt.cancelBubble = true;
}
return GeneralUtils.doCallback (o.callback, "drag", elem, evt);
},

enddrag : function(evt) { 
var elem = EventHandlers.mouseEventHandlers.dragElement;
var o = elem.mouseEventHandler;
EventHandlers.cancelListener (document, "mousemove", EventHandlers.mouseEventHandlers.drag);
EventHandlers.cancelListener (document, "mouseup", EventHandlers.mouseEventHandlers.enddrag);
if (o.queueOverOut)
  GeneralUtils.doCallback (o.callback, (o.queueOverOut==1)?"over":"out", this, ((evt)?evt:window.event));
EventHandlers.mouseEventHandlers.dragElement = null;
delete (EventHandlers.mouseEventHandlers.dragElement);
return GeneralUtils.doCallback (o.callback, "enddrag", elem, ((evt)?evt:window.event));
},

over : function(evt) {
var o = this.mouseEventHandler;
if (EventHandlers.mouseEventHandlers.dragElement == this)
  o.queueOverOut = 1;
else {
  delete (o.queueOverOut);
  return GeneralUtils.doCallback (o.callback, "over", this, ((evt)?evt:window.event));
}
},

out : function(evt) {
var o = this.mouseEventHandler;
if (EventHandlers.mouseEventHandlers.dragElement == this)
  o.queueOverOut = 2;
else {
  delete (o.queueOverOut);
  return GeneralUtils.doCallback (o.callback, "out", this, ((evt)?evt:window.event));
}
}
},

//-------------------------------------------
setEventHandler : function(elem, type, callback) {
if (!elem.aaEvtHandler)
	elem.aaEvtHandler = {};
elem.aaEvtHandler[type] = callback;
if (EventHandlers[type])
	elem[type] = EventHandlers[type];
else
	elem[type] = function(evt) {
		return EventHandlers.genericEventHandler(elem, type, ((evt)?evt:window.event));
		};
return elem; // for chaining calls
},

genericEventHandler : function(elem, type, evt) {
var eh, c;
if ((eh = elem.aaEvtHandler) != null) {
  eh.event = evt;
  if ((c = eh[type]) != null) {
    var ret = GeneralUtils.doCallback (c);
    delete eh.event;
    return ret;
  }
}
return false;
},

// having common ones declared here saves memory vs. using closures
onclick : function(evt) {
 return EventHandlers.genericEventHandler(this, "onclick", ((evt)?evt:window.event));},
onkeypress : function(evt) {
 return EventHandlers.genericEventHandler(this, "onkeypress", ((evt)?evt:window.event));},
onmouseover : function(evt) {
 return EventHandlers.genericEventHandler(this, "onmouseover",((evt)?evt:window.event));},
onmouseout : function(evt) {
 return EventHandlers.genericEventHandler(this, "onmouseout",((evt)?evt:window.event));},

setTimerFunction : function(callback, milliseconds) {
var id, au = EventHandlers;
for (id=0; id < au.timeoutData.length; id++)	{
	if (au.timeoutData[id] == null) {
		break;
  }
}
var td = au.timeoutData[id] = {};
td.callback = callback;
td.handle = setTimeout ("EventHandlers.callbackForSetTimeout(" + id + ")", milliseconds);
return id;
},

cancelTimerFunction : function(id) {
var a = EventHandlers.timeoutData, td = a[id];
if (td) {
  clearTimeout (td.handle);
	if (id == a.length-1)
		a.pop();
	else
		delete a[id];
}
},

cancelTimerFunctionByFunction : function(func) {
var id, au = EventHandlers;
for (id=0; id < au.timeoutData.length; id++) {
	if ((td = au.timeoutData[id]) != null && td.callback[0] == func)
		au.cancelTimerFunction (id);
}
},

callbackForSetTimeout : function(id) {
var	 a = EventHandlers.timeoutData, td = a[id];
GeneralUtils.doCallback (td.callback);
if (id == a.length-1)
	a.pop();
else
	delete a[id];
},

timeoutData : [],

//-------------------------------------------------
//cross-browser event handling with listeners(if available)
setListener : function(element, eventType, handler) {
if(window.addEventListener) {
	element.addEventListener(eventType, handler, false);
	return true;
} else if(window.attachEvent) {
	element.attachEvent('on' + eventType, handler);
	return true;
}
},

//-------------------------------------------------
cancelListener : function(element, eventType, handler) {
if(window.removeEventListener) {
	element.removeEventListener(eventType, handler, false);
	return true;
} else if(window.detachEvent) {
	element.detachEvent('on' + eventType, handler);
	return true;
}
}

};


///////////////////////////////////////////////////////////////////////////////////////////////////

//--------------------------------------------
//             Popup
//--------------------------------------------
var Popup = function
(
parentElement,
position,
parentHiliteClassname,
backgroundClassname,
isMenu
) {
var i, o, c, p, GU = GeneralUtils, EH = EventHandlers;
var st = Popup.stack;

if (!Popup.containerElem)
	Popup.containerElem = document.body;

// kill any existing popups, but....
// if the parent element is contained in an existing popup,
// do not kill that popup or any up the stack from it

FOUND_PARENT:
for(i=st.length-1; i>=0; i--) {
	c = st[i].element;
	o = parentElement;
	if(parentElement != null) {
		while(o != null) {
			// found one
			if(c == o) {
				//p(parentElement.innerHTML, true)
				break FOUND_PARENT;
			}
			o = o.parentNode;
		}
		p = st[i];
		if (!p.noKillByClick)
  			p.kill ();
	}
}

var newElement = document.createElement("div");
newElement.style.position = "absolute";
newElement.style.zIndex = Popup.getZ();
newElement.style.visibility = "hidden";

newElement.karmaticsPopup = this;

var dims = GU.getWindowDimensions();
GU.moveElem(newElement, dims.scrollX+1, dims.scrollY+1); //upper left
Popup.containerElem.appendChild(newElement);
this.element = newElement;
this.position = position;
if(parentElement != null && parentHiliteClassname)
	this.parentHiliteClassname = parentHiliteClassname;
this.parentElement = parentElement;
this.stackIndex = st.length;
st[st.length] = this;
if(st.length == 1)
	EH.setListener(document, 'mousedown', Popup.clickListener, false);
if (isMenu) {
  this.isMenu = true;
  GU.removeListener(document, 'mousedown', Popup.clickListener, false);
  EH.setTimerFunction([EH.setListener, [document, 'mousedown', Popup.clickListener, false]], 10);
}
  
EH.setListener (this.element, 'mouseover', Popup.mouseOver, false);	
EH.setListener (this.element, 'mouseout', Popup.mouseOut, false);	
if (parentElement) {
	parentElement.karmaticsPopupChild = this;
	EH.setListener (parentElement, 'mouseover', Popup.mouseOver, false);	
	EH.setListener (parentElement, 'mouseout', Popup.mouseOut, false);
}
if (backgroundClassname)
	this.backgroundClassname = backgroundClassname;
return this;
};

GeneralUtils.addMembers(Popup, {
zIndex : 100,

getZ : function () {
return Popup.zIndex += 100;
},

//---------------------------------------------------
mouseOver : function () {
var p, elem = ((document.all)?window.event.srcElement:this);
if (elem.karmaticsPopup)
	p = elem.karmaticsPopup;
else if (elem.karmaticsPopupChild)
	p = elem.karmaticsPopupChild;
else
	return;
p.mouseIsIn = true;
},


//---------------------------------------------------
mouseOut : function () {
var p, elem = ((document.all)?window.event.srcElement:this);;
if (elem.karmaticsPopup)
	p = elem.karmaticsPopup;
else if (elem.karmaticsPopupChild)
	p = elem.karmaticsPopupChild;
else
	return;
p.mouseIsIn = false;
if (p.killOnMouseOut)
	EventHandlers.setTimerFunction([Popup.mouseOutKill, [p]], 200);
},

//---------------------------------------------------
mouseOutKill : function (popup) {
if (!popup.isDead && !popup.mouseIsIn)
	popup.kill();
},

//-------------------------------------------------
getByIndex : function(index) {
return this.stack[index];
},
	
//-------------------------------------------------
clickListener : function(evt) {
var GU = GeneralUtils, i = -1, elem = GU.getElementFromEvent(evt);
Popup.killPopups (elem);
},

//-------------------------------------------------
killPopups : function(clickedElement) {
var GU = GeneralUtils, i = -1;
if(clickedElement) {
  var p = Popup.getFromContainedElement(clickedElement);
  if(p != null)
    i = p.stackIndex;
}   
var st = Popup.stack;

for(var j=st.length-1; j>i; j--) {
  var currP = Popup.stack[j];
  if (currP.noKillByClick)
  	return;
  if(currP.killCallback && currP.killCallback!= null)
    currP.killCallback(currP);
  currP.kill();
}
},

//-------------------------------------------------
getFromContainedElement : function(element) {
var o = element, curr, i, GU = GeneralUtils;
var st = Popup.stack;

while(o != null) {
	for(i=0; i<st.length; i++) {
		curr = st[i].element;
		if(curr == o)
			return st[i];
	}
	o = o.parentNode;
}
return null;
},

stack : [],

containerElem : null
}); // end class methods and properties

GeneralUtils.addMembers(Popup.prototype, {

//--------------------------------------------
show : function() {
var diff, GU = GeneralUtils;
if(this.stackIndex < Popup.stack.length) {
	var parent = this.parentElement,
		dims = GU.getWindowDimensions(),
		pos = ((parent)?GU.getPos(parent):(GU.getPos(this.element))),
		bottom = dims.scrollY + dims.height;
	if(this.parentHiliteClassname)
		GU.modifyClassName(this.parentElement, true, this.parentHiliteClassname);
	
	switch(this.position) {
    case 0: //on the immediate right of textElem
      pos.x += parent.offsetWidth+2;
    case 1: //displayColor covering swatchElem
      pos.x += 28;
      pos.y -= 11//(this.element.offsetHeight/2)-(parent.offsetHeight/2);
      break;
    case 3: // optionMenu
      if(parent != null) {
        var change = this.element.offsetHeight * ((this.optionIndex)/this.element.menu.data.length);
        pos.y -= change;
        this.element.style.width = parent.offsetWidth+"px";
      }
    
      break;
     case 4: 
      pos.y +=	parent.offsetHeight;
      break;
	}
	diff = (pos.y + this.element.offsetHeight) - bottom;
	if((pos.x + this.element.offsetWidth) > dims.width)
		pos.x = (dims.width-5) - (this.element.offsetWidth);
	
	if (this.offsetPos) {
		pos.x += this.offsetPos.x;
		pos.y += this.offsetPos.y;
	}
	var w = dims.width - 15;
	if((pos.x + this.element.offsetWidth) > w)
		pos.x = w - (this.element.offsetWidth);
				
	if(pos.x < dims.scrollX)
		pos.x = dims.scrollX;
				
	GU.moveElem(this.element, pos.x, pos.y);
	this.element.style.visibility = "visible";
	this.element.style.display = "";
	
	if (this.backgroundClassname) {
		var e = GeneralUtils.createDomElems(["div", {className: this.backgroundClassname, style: {zIndex: (parseInt(this.element.style.zIndex)-1).toString(), position: "absolute", width: this.element.offsetWidth + "px", 	height: this.element.offsetHeight + "px"}}]);
		GU.moveElem (e, pos.x, pos.y);
		Popup.containerElem.appendChild (e);
		this.backgroundElement = e;
	}
}
},

//-------------------------------------------------
kill : function() {
if (this.isDead)
	return;
var GU = GeneralUtils, EH = EventHandlers, stack = Popup.stack;
if (!this.cancel && this.cp) {
  this.cp.startRgb = this.cp.colors.rgb;
  this.cp.colors.hex = ColorPicker.rgbToHex(this.cp.colors.rgb);
  if (this.cp.elements.textElem)
    this.cp.elements.textElem.value = this.cp.hexPrefix + this.cp.colors.hex;
  this.cp.callback("final", this.cp.elements, this.cp.colors);
  this.cp.open = false;
}
GeneralUtils.purgeEventHandlers(this.element);
this.element.innerHTML = "";
Popup.containerElem.removeChild (this.element);
if (this.backgroundElement)
	Popup.containerElem.removeChild (this.backgroundElement);
if(this.parentElement && this.parentHiliteClassname)
	GU.modifyClassName(this.parentElement, false, this.parentHiliteClassname);
if(this.parentElement) {
	GU.removeListener(this.parentElement, 'mouseover', Popup.mouseOver, false);
	GU.removeListener(this.parentElement, 'mouseout', Popup.mouseOut, false);
	this.parentElement.karmaticsPopupChild = null;
}

stack.length--;
delete(stack[this.stackIndex]);
if(stack.length == 0)
	GU.removeListener(document, 'mousedown', Popup.clickListener, false);
this.isDead = true;
}

});


///////////////////////////////////////////////////////////////////////////////////////////////////

//--------------------------------------------
//             Option Menu
//--------------------------------------------
var OptionMenu = function (callback, text, className, data, index, thisObject) {
this.callback = callback;
this.className = className;
this.data = data;
this.thisObject = thisObject;
this.index = index;

var innerElems = {};
this.button = GeneralUtils.createDomElems (
  ["table", {cellSpacing:"0", cellPadding:"0", className: className},
    ["tbody",
      ["tr",
        ["td", {className: className+"-left"}],
        ["td", {className: className+"-middle"},
          innerElems.content = 
          ["div", {html: text}]
        ],
        ["td", {className: className+"-right"}]
      ]
    ]
  ], innerElems);
this.content = innerElems.content;
innerElems = null;

EventHandlers.setEventHandler(this.content, "onmouseover", [OptionMenu.menuMouseHandler, [this.content, "over", {mode:0}], this]);
EventHandlers.setEventHandler(this.content, "onmouseout", [OptionMenu.menuMouseHandler, [this.content, "out", {mode:0}], this]);

EventHandlers.setMouseEventHandler (this.button, [OptionMenu.initMenu, [], this], {blockSelect: true}); 
return this
};

GeneralUtils.addMembers(OptionMenu, {

//---------------------------------------------------
// menu click handler, sends menu item data to optionMenu callback and kills the popup
kill : function (data, p){
GeneralUtils.doCallback([this.callback, [data], this.thisObject]);
p.kill();
return false;
},

//---------------------------------------------------
mouseup : function (data, p, e) {
EventHandlers.setEventHandler(e, "onmouseup", [OptionMenu.kill, [data, p], this]);
return false;
},

//---------------------------------------------------
// menu optionMenu initialize, builds the menu popup and displays it
initMenu : function (args, type) {
switch (type) {
  case "down":
    var p = new Popup(this.button, 3, "hidden", "kcs-menuTranslucentBG", true);
    p.element.style.cursor = "pointer";
    p.element.className = "kcs-menu";
    p.element.menu = {data:this.data};
    p.optionIndex = this.index;
    for (var i=0; i<this.data.length; i++) {
      var e = GeneralUtils.createDomElems(["a", {
          href: "#",
          menuIndex: i,
          menuData: this.data[i]
          }]);
      var current = false;
      EventHandlers.setEventHandler(e, "onmousemove", [OptionMenu.mouseup, [this.data[i], p, e], this]);
      EventHandlers.setEventHandler(e, "onclick", [OptionMenu.kill, [this.data[i],p], this]);
      e.style.textAlign = "center";
      if (i==this.index) {
        e.className = "current";
        current = true;
        GeneralUtils.doCallback([OptionMenu.menuMouseHandler, [e, "over", {current:true, mode:1}], this]);
      } 
      EventHandlers.setEventHandler(e, "onmouseover", [OptionMenu.menuMouseHandler, [e, "over", {current:current, mode:1}], this]);
      EventHandlers.setEventHandler(e, "onmouseout", [OptionMenu.menuMouseHandler, [e, "out", {current:current, mode:1}], this]);             
      e.innerHTML = this.data[i][0];
      p.element.appendChild (e);
    }
    p.show();
    break;
}
},

//---------------------------------------------------
menuMouseHandler : function (elem, type, args) {
var color = "#ddd", background = "", border = "transparent", img = "white";
if (args.mode==0) {
  switch (type) {
    case "over":
      color = "#ffe888";
      img = "hilite";
    case "out":
      elem.style.color = color;
      elem.style.backgroundImage = "url('http://karmatics.com/new/chameleon/img/downarrow"+img+"tiny.png')";
      break;
  }
} else {
  switch (type) {
    case "over":
      color = "#eee";
      background = "#000";
      border = "#eee transparent";
      img = "hilite"
    case "out":
      elem.style.color = color;
      elem.style.backgroundColor = background;
      elem.style.borderColor = border;
      if (args.current)
        elem.style.backgroundImage = "url('http://karmatics.com/new/chameleon/img/rightarrow"+img+"tiny.png')";
      break;
  }
}
}
});