// CIC Library.
(function(){

  // The CIC namespace.
  if(!window.cic) {window['cic'] = {} }
  
  function isCompatible(other) {
    if(other === false
       || !Array.prototype.push
       || !Object.hasOwnProperty
       || !document.createElement
       || !document.getElementByTagName
    ) {
      return false;
    }
    
    return true;
  };
  window['cic']['isCompatible'] = isCompatible;
  
  function $() {
    var element = new Array();
    
    // Find all the elements supplied as arguments
    for(var i = 0; i < arguments.length; i++) {
      var element = arguments[i];
      
      if(typeof element == 'string') {
        element = document.getElementById(element);
      }
      
      if(arguments.length == 1) {
        return element;
      } else {
        elements.push(element)
      }
      
      return elements;
    }
  };
  window['cic']['$'] = $;
  
 function addEvent(node, type, listener) {
    if(node.addEventListener) {
      node.addEventListener(type, listener, false);
      return true;
    } else if(node.attachEvent) {
      node['e'+type+listener] = listener;
      node[type+listener] = function() {
                                  node['e'+type+listener](window.event);
                                }
      node.attachEvent('on'+type, node[type+listener]);                                
      return true;                                
    }
    return false;
  };
  window['cic']['addEvent'] = addEvent;
  
  function createElement(elementType, className, id) {
      elementType = elementType || 'div';
      
    var W3CElement = document.createElement(elementType)
      if(className) W3CElement.className = className;
      if(id) W3CElement.setAttribute("id", id);
      
    return W3CElement;    
  }   
  window['cic']['createElement'] = createElement;
  
/**
 * Stop the propagation of an event
 */
	function stopPropagation(eventObject) {
    eventObject = eventObject || getEventObject(eventObject);
    if(eventObject.stopPropagation) {
        eventObject.stopPropagation();
    } else {
        eventObject.cancelBubble = true;
    }
	}
	window['cic']['stopPropagation'] = stopPropagation;

  function createInputElement(type, name, value, className, id, state) {
      elementType = type || 'text';
      
      var W3CString             = '<input type = "' + type + '" ';
       if(name) W3CString      += 'name = "' + name+ '" ';
       if(className) W3CString += 'class = "' + className + '" ';
       if(id) W3CString        +=  'id = "' + id + '" ';
       if(value) W3CString     +=  'value = "' + value + '" ';
       if((elementType.toLowerCase() == 'radio' || elementType.toLowerCase() == 'checkbox') && state) W3CString +=  'checked = "' + state + '" ';
       
       W3CString += '>'
       
     return W3CString;    
  }   
  window['cic']['createInputElement'] = createInputElement;

  function createLabel (text, labelfor) {
    var text = text || 'empty string'
    var label = document.createElement("label");
        if(labelfor) label.setAttribute("for", labelfor);
       if(text) label.appendChild(document.createTextNode(text));
    return label;
  }
  window['cic']['createLabel'] = createLabel;
  
  function removeEvent(node, type, listener) {
    if(!(node= $(node))) { return false;}
    
    if(node.removeEventListener) {
      node.removeEventListener(type, listener, false);
      return true;
    } else if(node.detachEvent) {
      node.detachEvent('on'+type, node[type+listener]);
      return true;
    }
    
    return false;
  };
  window['cic']['removeEvent'] = removeEvent;

  function isEmail(string) {
		var pattern = /^([a-zA-Z0-9_.+-])+@(([a-zA-Z0-9-])+.)+([a-zA-Z0-9]{2,6})+$/;
		return new Boolean(pattern.exec(string)).valueOf();
	}
	window['cic']['isEmail'] = isEmail;

  function getElementsByClassName(className, tag, parent) {
/*    parent = parent || document;
    tag = tag || '*';

    if(!(parent = $(parent))) {return false;}
    
    var allTags = (tag == "*" && parent.all) ? parent.all : parent.getElementsByTagName(tag);
    var matchingElement = new Array();
    
    // regular expression to determin the class name.
    className = className.replace(/\-/g, "\\-");
    var regex = new RegExp("(^|\\s)" + className + "(\\|$)");
    var element;
    
    for(var i=0; i<allTags.length; i++) {
      element = allTags[i];
      if(regex.test(element.className)) {
        matchingElement.push(element);
      }
    }
*/
var classElements = new Array();
	
	if ( parent == null );
		parent = document;
	if ( tag == null )
		tag = '*';
	var els = parent.getElementsByTagName(tag);
	var elsLen = els.length;
	var pattern = new RegExp('(^|\\\\s)'+parent+'(\\\\s|$)');
	for (i = 0, j = 0; i < elsLen; i++) {
		if ( pattern.test(els[i].className) ) {
			classElements[j] = els[i];
			j++;
		}
	}
	return classElements;

//    console.info(matchingElement.length)
//    return matchingElement;  
  };
  window['cic']['getElementsByClassName'] = getElementsByClassName;
  
	function bind(object, method) {
  	return function() {
    	return method.apply(object, arguments);
  	};
	}
	window['cic']['bind'] = bind;

	function getPointerPosition(e) {
		var W3CEvent = e || window.event;
		var x = W3CEvent.pageX || (W3CEvent.clientX + (document.documentElement.scrollLeft || document.body.scrollLeft));
		var y = W3CEvent.pageY || (W3CEvent.clientY + (document.documentElement.scrollTop || document.body.scrollTop));

		// x any y now contain the coordinates of mouse relative to the document origin.
		return {'x':x, 'y':y}
	}
	window['cic']['getPointerPosition'] = getPointerPosition;

	function getElementPosition(W3CElement) {
		var x = 0; var y = 0;
		while(W3CElement	!=null	)	{
    		x += W3CElement.offsetLeft || 0;
    		y += W3CElement.offsetTop  || 0;
    		W3CElement = W3CElement.offsetParent;
				 if (W3CElement.style['position'] == 'relative' || W3CElement.tagName == 'BODY') break;
		}
		return {'x':x, 'y':y};
	}
	window['cic']['getElementPosition'] = getElementPosition;

  function scrollTo(id) {
    var element = $(id);
    var height = element.offsetHeight;
    var top = 0; left = 0;
    do {
      top += element.offsetTop  || 0;
      left += element.offsetLeft || 0;
      element = element.offsetParent;
    } while (element);
    
    
    window.scrollTo(0, top - height);
    
  }
  window['cic']['scrollTo'] = scrollTo;
  
	function isIE() {
		return (navigator.appName.indexOf("Microsoft") != -1);
	}
	window['cic']['isIE'] = isIE;

  function toggleDisplay(node, value) {
    if(!(node = $(node))) {return false;}
    
    if(node.style.display != 'none') {
      node.style.display = 'none';
    } else {
      node.style.display = value || '';
    }
  };
  window['cic']['toggleDisplay'] = toggleDisplay;
  
  function insertAfter(node, referenceNode) { 
    if(!(node = $(node))) {return false;}
    if(!(referenceNode = $(referenceNode))) {return false;}
    
    return referenceNode.parentNode.insertBefore(node, referenceNode.nextSilbing);
  };
  window['cic']['insertAfter'] = insertAfter;
  
  function removeChildren(parent) { 
    if(!(parent = $(parent))) {return false;}
    
    //while there is a child remove it.
    while(parent.firstChild) {
      parent.firstChild.parentNode.removeChild(parent.firstChild);
    }
    
    // for method chain return parent again.
    return parent;
  };
  window['cic']['removeChildren'] = removeChildren;
  
  function prependChild(parent, newChild) { 
     if(!(parent = $(parent))) {return false;}
     if(!(newChild = $(newChild))) {return false;}
     
     if(parent.firstChild) {
      parent.insertBefore(newChild, parent.firstChild);
     } else {
      parent.appendChild(newChild);
     }
  };
  window['cic']['prependChild'] = prependChild;
  /*
  function parseJSON(string, filter)
  A slightly modified version of the public domain method
  at http://www.json.org/json.js This method parses a JSON Text
  to produce an object or array. It can throw a SyntaxError exception.
  */
  function parseJSON(s, filter) {
    var j;
    
    function walk(k, v) {
      var i;
      if(v && typeof v === 'object') {
        for(i in v) {
          if(v.hasOwnProperty(i)) {
            v[i] = walk(i, v[i]);
          }
        }
      }
      return filter(k,v);
    }
    
    /*
     * Parsing happens in three stages. In the first stage, we run the text against
     * a regular expression which looks for non-JSON characters. We are specially
     * concerned with '()' and 'new' because they can cause invocation and '=' bacause
     * it can cause mutation. But just to be safe, we will reject all unexpected characters.               
     */     
     
     if(/^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/.test(s)) {
        
      /*
       * In seceond stage we use the eval function to compile the text into a Javascript structure. the '{'
       * operator is subject to a syntatic ambiguity in Javascript: it can begin a block or an object literal.
       * We wrap the text in parse to eliminate the embiguity.              
       */       
       try {
        j = eval('(' + s + ')');
       } catch(e) {
        throw new SyntaxError("parseJSON");
       }
       
       /*
        * In the opetional thrid stage, we recursively walk the new structure,
        * passing each name/value pair to a filter function for possible
        * transformation.                
        */        
        if(typeof filter === 'function') {
          j = walk('', j);
        }
        
        return j;
     }
  };
  window['cic']['parseJSON'] = parseJSON;
  
  function getRequestObject(url, options) {
   var req = false;
   
   if(window.XMLHttpRequest) {
    var req = new window.XMLHttpRequest();
   } else if(window.ActiveXObject) {
    var req = new window.ActiveXObject('Microsoft.XMLHTTP');
   }
   
   if(!req) return false;
   
   //define the default options.
   options = options || {};
   options.method = options.method || 'GET';
   options.send = options.send || null;
   
   //Define the various listeners for each state of the request.
   req.onreadystatechange = function() {

    switch(req.readyState) {
      case 1: 
      // Loading
      if(options.loadListener) {
        options.loadListener.apply(req, arguments);
      }
      break;
      
      case 2:
      //Loaded
      if(options.loadedListener) {
        options.loadedListener.apply(req, arguments);
      }
      break;
      
      case 3:
      //Interactive
      if(options.interactiveListener) {
        options.interactiveListener.apply(req, arguments);
      }
      break;
      
      case 4:
      //Complete
      // If aborted FF throws error
      try {
        if(req.status && req.status == 200) {
          // specific listeners for content-type
          //Content-Type: text/html; charset=ISO-8859-4
          // So we will use a match to extract the part we need.
          var contentType = req.getResponseHeader('Content-Type');
          var mimeType = contentType.match(/\s*([^;]+)\s*(;|$)/i)[1]
          
          switch(mimeType) {
            case 'text/javascript':
            case 'application/javascript':
            //The response is javascript so use req.responseText as the argument
            //to the callBack
            if(options.jsResponseListener) {
              options.jsResponseListener.call(req, req.responseText);
            } break;

            case 'application/json':
            //The response is javascript so parse the req.responseText using anonymous function
            // which simply returns the JSON object for arguments to the callback.
            
            if(options.jsonResponseListener) {
              options.jsonResponseListener.call(req, req.responseText);
            } break;
            
            case 'text/xml':
            case 'application/xml':
            case 'application/xhtml+xml':
            
            //The response is XML so use the req.responseXML as the argument
            //to the callBack
            if(options.xmlResponseListener) {
              options.xmlResponseListener.call(req, req.responseXML);
            } break;
            
            case 'text/html':
            //The response is HTML so use the req.responseText as the argument
            //to the callBack
            if(options.htmlResponseListener) {
              options.htmlResponseListener.call(req, req.responseXML);
            } break;
          }
            
            //A Complete listener
            if(options.onComplete) {
              options.onComplete.apply(req, arguments);
            } 
           } else {
            // response completed but there was an error.
            if(options.errorListener) {
              options.errorListener.apply(req, arguments)
            }
            }
          } catch(e) {
            // Ignore errors
          }
          break;
        }
      };
      
      // Open the request
      req.open(options.method, url, true);
      
      // Add a special header to identify the requests.
      req.setRequestHeader('X-CIC-Ajax-Request', 'AjaxRequest');
      
      return req;
    }
    window['cic']['getRequestObject'] = getRequestObject;
    
    function ajaxRequest(url, options) {
      var req = getRequestObject(url, options);
      req.send(options.send);
    }
    window['cic']['ajaxRequest'] = ajaxRequest;
    
    /*
     *Cross-Site scripting.
     *
     */          
     var XssHttpRequestCount = 0;
     //A cross-site <script> tag implementation of the XMLHttprequest object
     
     var XssHttpRequest = function() {
      this.requestID = 'XSS_HTTP_REQUEST_' + (++XssHttpRequestCount);
     }
     XssHttpRequest.prototype = {
      url:null,
      scriptObject:null,
      responseJSON:null,
      status:0,
      readyState:0,
      timeout:30000,
      onreadystatechange: function() {},
      
      setReadyState: function(newReadyState) {
        // Only update the ready state if it's newer than the current state.
        if(this.readyState < newReadyState || newReadyState==0) {
          this.readyState = newReadyState;
          this.onreadystatechange();
        }
      },
      
      open: function(url, timeout) {
        this.timeout = timeout || 30000;
        //Append a special variable to URl call
        // XSS_HTTP_REQUEST_CALLBACK that contains the name 
        // of the callback function for the request.
        
        this.url = url
            + ((url.indexOf('?') != -1) ? '&' : '?')
            + 'XSS_HTTP_REQUEST_CALLBACK='
            + this.requestID
            + '_CALLBACK';
        this.setReadyState(0);             
      },
      
      send: function() {
        var requestObject = this;
        
        //Create a new script object to lead the external data.
        this.scriptObject = document.createElement('script');
        this.scriptObject.setAttribute('id', this.requestID);
        this.scriptObject.setAttribute('type', 'text/javascript');
        
        var timeoutWatcher = setTimeout(function() {
          // Populate the winodw method with an empty method
          // in case the script loads later on after we've assumed it stalled.
          window[requestObject.requestID + '_CALLBACK'] = function() {};

        //Remove the script to prevent it from loading further.
        requestObject.scriptObject.parentNode.removeChild(requestObject.scriptObject);
        
        //set the status to error.
        requestObject.status = 2;
        requestObject.statusText = 'Timeout after' + requestObject.timeout + ' milliseconds.';
        
        requestObject.setReadyState(2);
        requestObject.setReadyState(3);
        requestObject.setReadyState(4);
        
        }, this.timeout);
        
        // create a method in the window object that matches the callback in the 
        // request. When called it will process the rest of the request.
        
        window[this.requestID + '_CALLBACK'] = function(JSON) {
          //When the script loads this method will execute, passing in the desired
          // JSON Object.
          
          //Clear the timeoutWatcher method as the request loaded successfully.
          clearTimeout(timeoutWatcher);

          //Update the state.
          requestObject.setReadyState(2);
          requestObject.setReadyState(3);
         //Set the status to success.
         requestObject.responseJSON = JSON;
         requestObject.status = 1;
         requestObject.statusText = 'Loaded';
         
         //Update the state
         requestObject.setReadyState(4);
        }
        
        //set the initial state.
        this.setReadyState(1);
        
        //Now set the src property and append to the document's head.
        // this will load the script.
        this.scriptObject.setAttribute('src', this.url);
        var head = document.getElementsByTagName('head')[0];
        head.appendChild(this.scriptObject);        
      }
     }
     window['cic']['XssHttpRequest'] = XssHttpRequest;
     
     function getXssRequestObject(url, options) {
      var req = new XssHttpRequest();
      
      options = options || {};
      
      //Default timeout of 30 Sec.
      options.timeout = options.timeout || 30000;
      
      req.onreadystatechange = function() {
        switch(req.readyState) {
          case 1:
          //Loading
          if(options.loadlistener) {
            options.loadListener.apply(req, arguments);
          } break;
          
          case 2:
          //Loaded
          if(options.loadedListener) {
            options.loadedListener.apply(req, arguments);
          } break;
          
          case 3:
          //Interactive
          if(options.interactiveListener) {
            options.interactiveListener.apply(req, arguments);
          } break;
          
          case 4:
          //Complete.
          if(req.status == 1) {
            if(options.onComplete) {
              options.onComplete.apply(req, arguments);
            }
          } else {
            if(options.errorListener) {
              options.errorListener.apply(req, arguments);
            }
          }
          break;
        }
      };
      req.open(url, options.timeout);
      
      return req;
     }
     window['cic']['getXssRequestObject'] = getXssRequestObject;
     
     /* send an XssHttpRequest */
     function xssRequest(url, options) {
      var req = getXssRequestObject(url, options);
      return req.send(null);
     }
     window['cic']['xssRequest'] = xssRequest;

		function prventDefault(e) {
			if(cic.isIE)
				e.returnValue = false;
			else
				e.preventDefault(); 	
		}
		window['cic']['preventDefault'] = prventDefault;

		function targetElement(event) {
			return (cic.isIE()) ? event.srcElement: event.target;
		}
		window['cic']['targetElement'] = targetElement;


		function getValue(node, isText) {

			isText = isText || false;
			if(!node.type || !(node= $(node))) { return false;}
			type = node.type.toLowerCase()
			if(type === "text" || type === "hidden")
				return node.value;
			else if (type === "radio" ) {
					var group = document.getElementsByName(node.name);
					var value = "";
					for(i = 0; i < group.length; i++) {
						if(group[i].checked) value = group[i].value;
					}
					return value;
			}
			else if (type === "checkbox" && node.checked)
				return node.value;
			else if (type === "select-one" && !isText) {
				return node.options[node.selectedIndex].value;
			}	else if(type === "select-one" && isText) {
				return node.options[node.selectedIndex].text;
			}
			else
				return "";
		}
		window['cic']['getValue'] = getValue;
		
		function cumulativeOffset(element) {
			var valueT = 0, valueL = 0;
    	do {
      	valueT += element.offsetTop  || 0;
      	valueL += element.offsetLeft || 0;
      	element = element.offsetParent;
    	} while (element);
		
			return {y: valueT, x:valueL}
		}
		window['cic']['cumulativeOffset'] = cumulativeOffset;

	function iframeHack(element) {
		var offsetValues = cic.cumulativeOffset(element);

		var iFrame = document.getElementById("iframe_hack");
				iFrame.style.left 		=	offsetValues.x + 'px';
				iFrame.style.top 			=	offsetValues.y + 'px';
				iFrame.style.width 		=	element.offsetWidth + 'px';
				iFrame.style.height 	=	element.offsetHeight + 'px';
				iFrame.style.display 	=	'block';

				return iFrame
		}
	window['cic']['iframeHack'] = iframeHack;
	
	function delay(millis)	{
		var date = new Date();
		var curDate = null;
		do { 
				curDate = new Date(); 
		} while(curDate-date < millis);
	}
	window['cic']['delay'] = delay;

	function visible(element) {
		isVisible = true;
   do {
     isVisible = (element.style.display != 'none');
      element = element.parentNode;
    } while (element && element != document.body && isVisible);
		return isVisible;		
	}
	window['cic']['visible'] = visible;

})();

