
//http://prototype.conio.net/
var Class = {
  create: function() {
    return function() {
      this.initialize.apply(this, arguments);
    }
  }
}

Function.prototype.bind = function() {
  var __method = this, args = $A(arguments), object = args.shift();
  return function() {
    return __method.apply(object, args.concat($A(arguments)));
  }
}

String.prototype.trim = function() { return this.replace(/^\s+|\s+$/g, ''); }

function $id(id, element, type)
{
    /// <param name="id" type="String"></param>
    /// <param name="element" domElement="true" optional="true" mayBeNull="true"></param>

		if(type) return $partial(id, type);
    if (!element) return document.getElementById(id);
    if (element.getElementById) return element.getElementById(id);

    var nodeQueue = [];
    var childNodes = element.childNodes;
    if(!childNodes) return null;
    for (var i = 0; i < childNodes.length; i++)
    {
        var node = childNodes[i];
        if (node.nodeType == 1)
        {
            nodeQueue[nodeQueue.length] = node;
        }
    }

    while (nodeQueue.length)
    {
        node = nodeQueue.shift();
        if (node.id == id)
        {
            return node;
        }
        childNodes = node.childNodes;
        for (i = 0; i < childNodes.length; i++)
        {
            node = childNodes[i];
            if (node.nodeType == 1)
            {
                nodeQueue[nodeQueue.length] = node;
            }
        }
    }

    return null;
}

function $partial(id,type)
{
   var Collection = document.getElementsByTagName(type);
   for(var i=0; i < Collection.length; i++)
   {
      if(Collection[i].id.indexOf(id)!=-1)
         return Collection[i];
   }

   return null;
}

/*
 * Returns an array of elements from the page of type with the given server id.
 * Optional third parameter is an element that the search will be conducted in.
 * NAQ 07-18-07
 */
function $col(id,type,lookin)
{
   var Collection;
   if( typeof(lookin) != "undefined" && lookin != null ) {
      Collection = lookin.getElementsByTagName(type);
   } else {
      Collection = document.getElementsByTagName(type);
   }

   var retCol = [];
   for(var i=0; i < Collection.length; i++)
   {
      if(Collection[i].id.indexOf(id) == Collection[i].id.length - id.length) {
         retCol[retCol.length] = Collection[i];
      }
   }

   return retCol;
}

function $ele(tagName, id, className)
{
	var ele = document.createElement(tagName);
	if(id) ele.id = id;
	if(className) ele.className = className;
	
	return ele;
	
}

var $A = Array.from = function(iterable) {
  if (!iterable) return [];
  if (iterable.toArray) {
    return iterable.toArray();
  } else {
    var results = [];
    for (var i = 0; i < iterable.length; i++)
      results.push(iterable[i]);
    return results;
  }
}

var Sd = Class.create();


//
//
//		Sd.Common
//
//

Sd.Common = Class.create();
Sd.Common.prototype =
{

}
	 Sd.Common.events = [];

	Sd.Common.BrowserAgent = navigator.userAgent.toLowerCase();
	Sd.Common.Browser =
	{
		ver: (Sd.Common.BrowserAgent.match(/.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/) || [])[1],
		verNum: parseFloat((Sd.Common.BrowserAgent.match(/.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/) || [])[1]),
		safari: /webkit/.test(Sd.Common.BrowserAgent),
		opera: /opera/.test(Sd.Common.BrowserAgent),
		ie: /msie/.test(Sd.Common.BrowserAgent) && !/opera/.test(Sd.Common.BrowserAgent),
		moz: /mozilla/.test(Sd.Common.BrowserAgent) && !/(compatible|webkit)/.test(Sd.Common.BrowserAgent)
	};

   Sd.Common.cancelEvent = function(e)
   {
      var e = e || window.event;
      if(!e) return false;

      e.cancelBubble = true; // for IE
      if (typeof e.stopPropagation == 'function')
         e.stopPropagation();

      e.returnValue = false; // for IE
      if (typeof e.preventDefault == 'function')
         e.preventDefault();

      return false;
   }

   //Modified from http://weblogs.asp.net/asmith/archive/2003/10/06/30744.aspx
   Sd.Common.addEvent = function(target, eventName, func, handlerName)
   {
      if (target.addEventListener)
      {
         target.addEventListener(eventName, func, false);
         Sd.Common.events[handlerName] = func;
      }
      else if (target.attachEvent)
      {
         Sd.Common.events[handlerName] = func.bind(target);
         target.attachEvent('on' + eventName, Sd.Common.events[handlerName], false);
      }
   }

   Sd.Common.removeEvent = function(target, eventName, handlerName)
   {
      if (target.addEventListener)
      {
         target.removeEventListener(eventName, Sd.Common.events[handlerName], false);
      }
      else if (target.detachEvent)
      {
         target.detachEvent('on' + eventName, Sd.Common.events[handlerName]);
      }
   }

  Sd.Common.addDocumentReadyEvent = function(func)
  {
    if(Sd.Common.Browser.moz || Sd.Common.Browser.opera)
    {
    	document.addEventListener("DOMContentLoaded", func, false);
    	return;
    }
    
    if(Sd.Common.Browser.ie)
    {
      var deferScript = $id('__init_script');

      if(!deferScript)
      {
      	document.write('<script id="__init_script" defer="true" src="//Loading..."><\/script>');
      	deferScript = $id('__init_script');
			}

      if (deferScript)
      {
        deferScript.onreadystatechange = function()
        {
          if (this.readyState == 'complete')
          {
          	if(!func.called && !func.called == true)
            {
							func.called = true;
              func();
            }
          }
        };

        deferScript.onreadystatechange();

        // clear reference to prevent leaks in IE
        deferScript = null;
        
        return;
      }
    }

		// for other browsers
   	window.onload = func;
  
  }

  Sd.Common.addDocumentReadyEventEx = function(func)
  {
		if(!Sd.Common.readyEventAdded) Sd.Common.readyEventAdded = false;
		if(!Sd.Common.readyEvents) Sd.Common.readyEvents = [];
		
		Sd.Common.readyEvents.push(func);
		
    if(Sd.Common.Browser.moz || Sd.Common.Browser.opera)
    {
    	if(Sd.Common.readyEventAdded == false)
    	{
    		Sd.Common.readyEventAdded = true;
    		document.addEventListener("DOMContentLoaded", Sd.Common.executeReadyEvents, false);
    	}
    	return;
    }
    
    if(Sd.Common.Browser.ie)
    {
      var ieScript = $id('__init_scriptEx');

      if(!ieScript)
      {
      	document.write('<script id="__init_scriptEx" defer="true" src="//Loading..."><\/script>');
      	ieScript = $id('__init_scriptEx');
			}

      if (ieScript) 
      {
      	if(!ieScript.onreadystatechange)
      	{
	        ieScript.onreadystatechange = function()
	        {
	          if (this.readyState == 'complete')
	          {
	          	Sd.Common.executeReadyEvents();
	          }
	        };
      	}

        ieScript.onreadystatechange();

        // clear reference to prevent leaks in IE
        ieScript = null;
        
        return;
      }
    }

		// for other browsers
   	window.onload = Sd.Common.executeReadyEvents;
  
  }
  
  Sd.Common.executeReadyEvents = function()
  {
   	for(var i = 0; i < Sd.Common.readyEvents.length; i++)
  	{
  		var func = Sd.Common.readyEvents[i];
    	if(!func.called && !func.called == true)
      {
				func.called = true;
        func();
      }
  	}
  }

	Sd.Common.getObjectByPartialID = function(id,type)
	{
	   return $partial(id, type);
	}

	Sd.Common.strDup = function(number,str)
	{
	   var result = '';
	   for(var i=0; i < number; i++)
	   {
	      result += str;
	   }

	   return result;
	}

	Sd.Common.isNumeric = function(value)
	{
		for (var i=0; i < value.length; i++)
		{
			a = parseInt(value.charAt(i));
			if (isNaN(a))
			{
			 return false;
			 break;
			}
		}
 		return true;
	}

	Sd.Common.getElementPos = function(el)
	{
		//return an element absolute position
		var elmnt=el;
		if(!elmnt) return;

		var pos = new Object;

		//get width and height
		pos.width=elmnt.offsetWidth;
		pos.height=elmnt.offsetHeight;

		//get left and top
		pos.left = 0;
		pos.top = 0;

		// ie 5.0 counts the body as well with the full win width, so we need to stop on body
		while(elmnt != null && elmnt.nodeName != "BODY")
		{
			pos.left += elmnt.offsetLeft;
			pos.top += elmnt.offsetTop;
			elmnt=elmnt.offsetParent;
		}

		//right and bottom
		pos.right = (pos.left + pos.width);
		pos.bottom = (pos.top + pos.height);

		return pos;
	}

	Sd.Common.getDocumentRect = function()
	{
		var w = window;
		var d = w.document, m = d.compatMode == 'CSS1Compat', b = d.body, de = d.documentElement;

		var rect = new Object();
		rect.left = w.pageXOffset || (m ? de.scrollLeft : b.scrollLeft);
		rect.top = w.pageYOffset || (m ? de.scrollTop : b.scrollTop);
		rect.width = w.innerWidth || (m ? de.clientWidth : b.clientWidth);
		rect.height = w.innerHeight || (m ? de.clientHeight : b.clientHeight);

		return rect;

//		return {
//			left : w.pageXOffset || (m ? de.scrollLeft : b.scrollLeft),
//			top : w.pageYOffset || (m ? de.scrollTop : b.scrollTop),
//			width : w.innerWidth || (m ? de.clientWidth : b.clientWidth),
//			height : w.innerHeight || (m ? de.clientHeight : b.clientHeight)
//		};
	}

	Sd.Common.formatMoney = function(num)
	{
		num = num.toString().replace(/\$|\,/g,'');
		if(isNaN(num))
		num = "0";
		sign = (num == (num = Math.abs(num)));
		num = Math.floor(num*100+0.50000000001);
		cents = num%100;
		num = Math.floor(num/100).toString();
		if(cents<10)
		cents = "0" + cents;
		for (var i = 0; i < Math.floor((num.length-(1+i))/3); i++)
		num = num.substring(0,num.length-(4*i+3))+','+
		num.substring(num.length-(4*i+3));
		return (((sign)?'':'-') + '$' + num + '.' + cents);
	}

	Sd.Common.hideElement = function(ele)
	{
	  if(ele.className.indexOf(' Hide') < 0) ele.className += ' Hide';
	}

	Sd.Common.showElement = function(ele)
	{
	  var className = ele.className;
	  className = className.replace(/Hide/, '');
	  className = className.replace(/ Hide/, '');
	  className = className.replace(/hide/, '');
	  className = className.replace(/ hide/, '');
	  ele.className = className;
	}

	Sd.Common.removeChildren = function(node)
	{
		while (node.hasChildNodes())
		{
		  node.removeChild(node.firstChild);
		}
  }

//
//
//		Sd.Common.UI
//
//

Sd.Common.UI = Class.create();
Sd.Common.UI.prototype =
{
   initialize: function()
   {
   }
}

	Sd.Common.UI.setSelectValue = function(select, value)
	{
		for(var index = 0; index < select.length; index++)
		{
			if(select[index].value == value)
		 		select.selectedIndex = index;
		}
	}

//
//
//		Sd.Validate
//
//

Sd.Validate = Class.create();
Sd.Validate.prototype =
{
   initialize: function()
   {
   }
}

	// require fields to match
	Sd.Validate.isMatch = function(fld,confirmfld)
	{
	if(!fld || !confirmfld) return false;
	  if(fld.disabled) return true;
	  if(fld.value != confirmfld.value) return false;
	  return true;
	}

	// simple email check
	Sd.Validate.isEmail = function(fld)
	{
		if(!fld) return false;
	  if(fld.disabled) return true; // blank fields are the domain of requireValue
	  var phony= /@(\w+\.)*example\.(com|net|org)$/i;
	  if(phony.test(fld.value))
	  { return false; }
	  var emailfmt= /^\w+([.-]\w+)*@\w+([.-]\w+)*\.\w{2,8}$/;
	  if(!emailfmt.test(fld.value))
	  { return false; }
	  return true;
	}

	// tenacious phone # correction
	Sd.Validate.fixPhone = function(fld,defaultAreaCode,sep,noext)
	{
		if(!fld) return false;
	  if(fld.disabled) return true; // blank fields are the domain of requireValue
	  if(typeof(sep)=='undefined') sep= '-';
	  if(typeof(defaultAreaCode)!='undefined') defaultAreaCode= defaultAreaCode + sep;
	  var ext= '', val= fld.value.toLowerCase();
	  if(val.indexOf('x') > 0)
	  {
	    if(!noext) ext= ' x'+val.substr(val.indexOf('x')).replace(/\D/g,'');
	    val= val.substr(0,val.indexOf('x'));
	  }
	  val= val.replace(/\D/g,'');
	  if(val.length == 7)
	  {
	    fld.value= defaultAreaCode + val.substring(0,3) + sep + val.substring(3,20) + ext;
	    return true;
	  }
	  if(val.length == 10)
	  {
	    fld.value= val.substring(0,3) + sep + val.substring(3,6) + sep + val.substring(6,20) + ext;
	    return true;
	  }
	  if(val.length < 7) return false;
	  if(val.length > 10) return false;

	  //status= 'The phone number you supplied for the '+fieldname(fld)+' field was wrong.';
	  return false;
	}

	// tenacious credit card correction; fieldname isn't a big consideration, probably only one card per form
	Sd.Validate.fixCreditCard = function(fld)
	{
		if(!fld) return false;
	  if(fld.disabled) return true; // blank fields are the domain of requireValue
	  var val= fld.value, ctype= 'credit card';
	  val= val.replace(/\D/g,'');
	  var prefix2= parseInt(val.substr(0,2));
	  if( val.substr(0,1) == '4' )
	  { // Visa
	    ctype= 'Visa\xae';
	    if( val.length == 16 );
	    else if( val.length == 13 ); // very old #, should be reassigned
	    else if( val.length < 13 )
	    { status= 'The Visa\xae number you provided is not long enough.'; return false; }
	    else if( val.length > 16 )
	    { status= 'The Visa\xae number you provided is too long.'; return false; }
	    else
	    { status= 'The Visa\xae number you provided is either not long enough, or too long.'; return false; }
	  }
	  else if( prefix2 >= 51 && prefix2 <= 55 )
	  { // MC
	    ctype= 'MasterCard\xae';
	    if( val.length < 16 )
	    { status= 'The MasterCard\xae number you provided is not long enough.'; return false; }
	    else if( val.length > 16 )
	    { status= 'The MasterCard\xae number you provided is too long.'; return false; }
	  }
	  else if( (prefix2 == 34) || (prefix2 == 37) )
	  { // AmEx
	    ctype= 'American Express\xae card';
	    if( val.length < 15 )
	    { status= 'The American Express\xae card number you provided is not long enough.'; return false; }
	    else if( val.length > 15 )
	    { status= 'The American Express\xae card number you provided is too long.'; return false; }
	  }
	  else if( val.substr(0,4) == '6011' )
	  { // Novus/Discover
	    ctype= 'Discover\xae card';
	    if( val.length < 16 )
	    { status= 'The Discover\xae card number you provided is not long enough.'; return false; }
	    else if( val.length > 16 )
	    { status= 'The Discover\xae card number you provided is too long.'; return false; }
	  }
	  else
	  { // other
	    if( val.length < 13 )
	    { status= 'The credit card number you provided is not long enough.'; return false; }
	    if( val.length > 19 )
	    { status= 'The credit card number you provided is too long.'; return false; }
	  }
	  var sum= 0, dbl= false;
	  for(var i= val.length-1; i >= 0; i--)
	  {
	    var digit= parseInt(val.charAt(i))*((dbl=!dbl)?1:2);
	    sum+= ( digit > 9 ? (digit%10)+1 : digit );
	  }
	  if(sum%10)
	  {
	    status= 'The '+ctype+' number you provided is not valid.\nPlease double-check it and try again.';
	    return false;
	  }
	  fld.value= val;
	  return true;
	}

	// set minimum and/or maximum field lengths
	Sd.Validate.isValidLength = function(fld, min, max)
	{
		if(!fld) return false;
	  if(fld.tagName.toLowerCase() == 'select'){ return fld.selectedIndex >= 0; }
	  if(fld.disabled) return true; // blank fields are the domain of requireValue
	  var len= fld.value.length;
	  if(min > -1 && len < min)
	  { return false; }

	  if(max)
	  {
		  if(max > -1 && len > max)
		  { return false; }
		}
	  return true;
	}

	// disallow a blank field
	Sd.Validate.requireValue = function(fld)
	{
		if(!fld) return false;
		if(fld.tagName.toLowerCase() == 'select')
		{
				if(fld.selectedIndex < 0) return false;

				var value = fld.options[fld.selectedIndex].value;
				if(value.trim() == '') return false;
				return true;
		}

	  if(fld.disabled) return true;
	  if(fld.value.trim() == '') return false;
	  if(!fld.value.length) return false;
	  return true;
	}

	Sd.Validate.addError = function(ele, message)
	{
		Sd.Validate.inputError(ele);
		Sd.Validate.inputErrorMessage(ele, message);
	}
	
	Sd.Validate.remError = function(ele)
	{
		Sd.Validate.inputNoError(ele);
		Sd.Validate.inputNoErrorMessage(ele);		
	}

	Sd.Validate.inputError = function(ele)
	{
	  ele.className += ' Input_Error';
	}
	
	Sd.Validate.inputNoError = function(ele)
	{
	  var className = ele.className;
	  className = className.replace(/Input\_Error/, '');
	  ele.className = className;
	}

	Sd.Validate.inputErrorMessage = function(ele, message)
	{
	  var id = ele.id + '_Error';
	  var pos = Sd.Common.getElementPos(ele);
	  var parent = ele.parentNode;
	  var parentPos = Sd.Common.getElementPos(parent);
	        
	  if($id(id)) ele.parentNode.removeChild($id(id));
	  
	  var div = document.createElement('div');
	  div.className = 'Input_ErrorMessage';
	  div.innerHTML = message;
	  div.id = id;
	
	  parent.appendChild(div);
	
	  var divPos = Sd.Common.getElementPos(div);
	  if(!parent.prevError)
	  {
	     parent.style.height = (parentPos.height + divPos.height) + 'px';
	     parent.prevError = true;
	  }
	}  
	
	Sd.Validate.inputNoErrorMessage = function(ele)
	{
	  var div = $id(ele.id + '_Error');
	  if(div) ele.parentNode.removeChild(div);
	  if(parent.prevError) ele.parentNode.prevError = null;
	}

//
//
//		Sd.Vector
//
//

Sd.Vector = Class.create();
Sd.Vector.prototype =
{
   initialize: function(increment)
   {
      increment = increment ? increment : 0;
      if (increment == 0) increment = 100;

	   this.data = new Array(increment);
	   this.increment = increment;
	   this.size = 0;
   },

   // getCapacity() -- returns the number of elements the vector can hold
   getCapacity: function()
   {
	   return this.data.length;
   },

   // getSize() -- returns the current size of the vector
   getSize: function()
   {
	   return this.size;
   },

   length: function(){ return this.getSize(); },

   // isEmpty() -- checks to see if the Vector has any elements
   isEmpty: function()
   {
	   return this.getSize() == 0;
   },

   // getLastElement() -- returns the last element
   getLastElement: function()
   {
	   if (this.data[this.getSize() - 1] != null)
		   return this.data[this.getSize() - 1];
   },

   // getFirstElement() -- returns the first element
   getFirstElement: function()
   {
	   if (this.data[0] != null)
		   return this.data[0];
   },

   // getElementAt() -- returns an element at a specified index
   getElementAt: function(i)
   {
	   try
	   {
		   return this.data[i];
	   }
	   catch (e) { return "Exception " + e + " occured when accessing " + i; }
   },

   // addElement() -- adds a element at the end of the Vector
   addElement: function(obj)
   {
	   if(this.getSize() == this.data.length)
		   this.resize();

	   this.data[this.size++] = obj;
   },

   // insertElementAt() -- inserts an element at a given position
   insertElementAt: function(obj, index)
   {
	   try
	   {
		   if (this.size == this.capacity)
			   this.resize();

		   for (var i=this.getSize(); i > index; i--)
		   {
			   this.data[i] = this.data[i-1];
		   }

		   this.data[index] = obj;
		   this.size++;

	   }
	   catch (e) { return "Invalid index " + i; }
   },

   // removeElementAt() -- removes an element at a specific index
   removeElementAt: function(index)
   {
	   try
	   {
		   var element = this.data[index];

		   for(var i=index; i<(this.getSize()-1); i++)
		   {
			   this.data[i] = this.data[i+1];
		   }

		   this.data[this.getSize()-1] = null;
		   this.size--;
		   return element;
	   }
	   catch(e) { alert('Sd.Vector Error - ' + e); return "Invalid index " + index; }
   },

   // removeAllElements() -- removes all elements in the Vector
   removeAllElements: function()
   {
	   this.size = 0;

	   for (var i=0; i<this.data.length; i++)
	   {
		   this.data[i] = null;
	   }
   },

   // indexOf() -- returns the index of a searched element
   indexOf: function(obj)
   {
	   for (var i=0; i<this.getSize(); i++)
	   {
		   if (this.data[i] == obj)
			   return i;
	   }

	   return -1;
   },

   // contains() -- returns true if the element is in the Vector, otherwise false
   contains: function(obj)
   {
	   for (var i=0; i<this.getSize(); i++)
	   {
		   if (this.data[i] == obj)
			   return true;
	   }
	   return false;
   },

   // resize() -- increases the size of the Vector
   resize: function()
   {
	   newData = new Array(this.data.length + this.increment);

	   for (var i=0; i< this.data.length; i++)
	   {
		   newData[i] = this.data[i];
	   }

	   this.data = newData;
   },


   // trimToSize() -- trims the vector down to it's size
   trimToSize: function()
   {
	   var temp = new Array(this.getSize());

	   for (var i = 0; i < this.getSize(); i++)
	   {
		   temp[i] = this.data[i];
	   }

	   this.size = temp.length - 1;
	   this.data = temp;
   } ,

   // sort() - sorts the collection based on a field name - f
   sort: function(f)
   {
	   var i, j;
	   var currentValue;
	   var currentObj;
	   var compareObj;
	   var compareValue;

	   for(i=1; i<this.getSize();i++)
	   {
		   currentObj = this.data[i];
		   currentValue = currentObj[f];

		   j= i-1;
		   compareObj = this.data[j];
		   compareValue = compareObj[f];

		   while(j >=0 && compareValue > currentValue)
		   {
			   this.data[j+1] = this.data[j];
			   j--;
			   if (j >=0)
			   {
				   compareObj = this.data[j];
				   compareValue = compareObj[f];
			   }
		   }
		   this.data[j+1] = currentObj;
	   }
   },

   // clone() -- copies the contents of a Vector to another Vector returning the new Vector.
   clone: function()
   {
	   var newVector = new Vector(this.size);

	   for (var i=0; i<this.size; i++)
	   {
		   newVector.addElement(this.data[i]);
	   }

	   return newVector;
   },

   // toString() -- returns a string rep. of the Vector
   toString: function()
   {
	   var str = "Vector Object properties:\n" +
	             "Increment: " + this.increment + "\n" +
	             "Size: " + this.size + "\n" +
	             "Elements:\n";

	   for (var i=0; i<getSize(); i++) {
		   for (var prop in this.data[i]) {
			   var obj = this.data[i];
			   str += "\tObject." + prop + " = " + obj[prop] + "\n";
		   }
	   }
	   return str;
   },

   // overwriteElementAt() - overwrites the element with an object at the specific index.
   overwriteElementAt: function(obj, index)
   {
	   this.data[index] = obj;
   }

}
