/**
 * @author jason
 useful links :: support for JS 1.6 http://www.robertnyman.com/javascript/javascript-1.6.html
http://www.cross-browser.com/toys/
//https://developer.mozilla.org/en/DOM/document
//http://www.w3.org/TR/DOM-Level-3-Core/core.html#i-Document

 */
//two methods to extend the string object >> even I know this is bad practise
//http://stackoverflow.com/questions/202605/repeat-string-javascript



if(!String.repeat){
	String.prototype.repeat = function(l){
		return new Array(l+1).join(this);//javascript join joins together all elements in an array
	};
}

if(!String.trim){
	String.prototype.trim = function(){
		return this.replace(/^\s+|\s+$/g,'');
	};
}

(function (){
//start main function
//set up namespace
if(!window.JC){window['JC'] = {};};

function isCompatible(other){
	if(other===false || !Array.prototype.push || !Object.hasOwnProperty  || !document.createElement || !document.getElementsByTagName){
		return false;
	}
	return true;
};
window['JC']['isCompatible'] = isCompatible;

function id(){
	var elements = new Array();
	//find all elements suplied as arguments
	for(var i=0;i<arguments.length;i++){
		var element = arguments[i];
		if(typeof element == 'string'){
			element = document.getElementById(element);
		}
		//return if only one argument
		if(arguments.length == 1){
			return element;
		}
		elements.push(element);
	}//for
	return elements;
};
window['JC']['id'] = id;

//http://ejohn.org/blog/flexible-javascript-events/
function addEvent(node,type,listener){
	if(!isCompatible()){return false;}
	if(!(node = id(node))) return false;
	if(node.addEventListener){
		node.addEventListener(type,listener,false);
		return true;
	}else if(node.attachEvent){
	/*Microsoft model has two important drawbacks:
   1. Events always bubble, no capturing possibility.
   2. The event handling function is referenced, not copied, so the this keyword always refers to the window and 		is completely useless.
	http://www.quirksmode.org/js/events_advanced.html*/
	node['e'+type+listener] = listener;//makes function child of the specified object
	node[type+listener] = function(){
		node['e'+type+listener](window.event);
		};//create anon function to fire previously attached listener
	node.attachEvent( 'on'+type, node[type+listener] );
	return true;
	}
	return false;//they have neither
};
window['JC']['addEvent'] = addEvent;

function removeEvent(node,type,listener){
	if(!(node = id(node))){return false;}
	if (node.removeEventListener) {
		node.removeEventListener(type, listener, false);
		return true;
	}
	else
		if (node.detachEvent) {
			// MSIE method
			node.detachEvent('on' + type, node[type + listener]);
			node[type + listener] = null;
			return true;
		}
	return false;
};
window['JC']['removeEvent'] = removeEvent;

function getElementsByClass(className,tag,parent){
	parent = parent || document;
	if(!(parent = id(parent))){return false;}
	if(document.getElementsByClassName && Array.prototype.filter){
		//if has dom method
		//probably a better way of returning this
		var ele = parent.getElementsByClassName(className);
		if(tag != "*" || tag == ''){//need to redo this> assume method has tag is better
			return ele;
		}else{
			return Array.filter(ele, function(e){
					return e.nodeName == tag.toUpperCase();
   				});
		}
	}else{
		//do it the other way
		var allTags = (tag == "*" && parent.all) ? parent.all : parent.getElementsByTagName(tag);
		var matchingElements = new Array();

		className = className.replace(/\-/g, "\\-");
		var regex = new RegExp("(^|\\s)" + className + "(\\s|$)");

		var element;
		for(var i = 0;i<allTags.length;i++){
			element = allTags[i];
			if (regex.test(element.className)) {
				matchingElements.push(element);
			}
		}
		return matchingElements;
	}//else
};
window['JC']['getElementsByClass'] = getElementsByClass;

function toggle(node,value){
	//used to switch display states
	if(!(node = id(node))) { return false; }
	if(node.style.display != 'none'){
		node.style.display = 'none';
	}else {
		node.style.display = value || '';
	}
	return true;
};
window['JC']['toggle'] = toggle;

/*add a few dom method for inserting nodes/removing etc*/
function insertAfter(node,referenceNode){
	//method to insert after referenceNode required node to be present
	if(!(node = id(node))) { return false; }
	if(!(referenceNode = id(referenceNode))) return false;
		return referenceNode.parentNode.insertBefore(node,referenceNode.nextSibling);
};
window['JC']['insertAfter'] = insertAfter;

function removeChildren(parent){
	//remove all child of the parent
	if(!(parent = id(parent))){return false;}
	while(parent.firstChild){
		parent.firstChild.parentNode.removeChild(parent.firstChild);
	}
	return parent;
};
window['JC']['removeChildren'] = removeChildren;

function prependChild(parent,newChild){
	//prepend child before existing children
	//new child must exist before
	if(!(parent = id(parent))){return false;}
	if(!(newChild = id(newChild))){return false;}

	if(parent.firstChild){
		//if child insert before
		parent.insertBefore(newChild,parent.firstChild);
	}else{//no children
		parent.appendChild(newChild);
	}
};
window['JC']['prependChild'] = prependChild;

//function for using apply
function bindFunction(obj, func) {
	return function() {
	func.apply(obj,arguments);
	};
};
window['JC']['bindFunction'] = bindFunction;

function getBrowserWindowSize() {
	var de = document.documentElement;
	return {
		'width':(
		window.innerWidth
		|| (de && de.clientWidth )
		|| document.body.clientWidth),
		'height':(
		window.innerHeight
		|| (de && de.clientHeight )
		|| document.body.clientHeight)
		};
};
window['JC']['getBrowserWindowSize'] = getBrowserWindowSize;


//set up constants for using nodes
window['JC']['node'] = {
    ELEMENT_NODE    			: 1,
    ATTRIBUTE_NODE              : 2,
    TEXT_NODE                   : 3,
    CDATA_SECTION_NODE          : 4,
    ENTITY_REFERENCE_NODE       : 5,
    ENTITY_NODE                 : 6,
    PROCESSING_INSTRUCTION_NODE : 7,
    COMMENT_NODE                : 8,
    DOCUMENT_NODE               : 9,
    DOCUMENT_TYPE_NODE          : 10,
    DOCUMENT_FRAGMENT_NODE      : 11,
    NOTATION_NODE               : 12
};


//travseral methods
//http://www.javascriptkit.com/dhtmltutors/treewalker.shtml  treewalker supported by FF etc
//http://snipplr.com/view/8212/dom-traversal/ lots of traversal methods
//https://developer.mozilla.org/en/DOM/element
//https://developer.mozilla.org/en/DOM/document.createTreeWalker

//http://www.javascriptkit.com/domref/documentmethods.shtml

//http://www.javascriptkit.com/domref/documentmethods.shtml

//http://www.webreference.com/programming/javascript/definitive/chap17/14.html

function walkElementsLinear(func,node){
    var root = node || window.document;
    var nodes = root.getElementsByTagName("*");
   for(var i=0;i<nodes.length;i++){
        func.call(nodes[i]);
    };
}
window['JC']['walkElementsLinear'] = walkElementsLinear;

//these traversal methods are rubbish >> better to assume not looking for text nodes
//text nodes have no children !!!!!
function walkTheDomRecursive(func,node,depth,returnedFromParent){
	var root = node || window.document;
	var returnedFromParent = func.call(root,depth++,returnedFromParent);
	var node = root.firstChild;//in nav its a text node
	while(node){
        walkTheDomRecursive(func,node,depth,returnedFromParent);
        node = node.nextSibling;
	}
};
window['JC']['walkTheDomRecursive'] = walkTheDomRecursive;

function walkTheDOMWithAttributes(func,node,depth,returnedFromParent){
	var root = node || window.document;
	var returnedFromParent = func.call(root,depth++,prefix);
	if(root.attributes){
		for(var i=0;i<root.attributes.length;i++){
			walkTheDOMWithAttributes(root.attributes[i],func,depth-1,returnedFromParent);
		}
	}//if
	if(root.nodeType != JC.node.ATTRIBUTE_NODE){
		var node = root.firstChild;
		while(node){
			walkTheDOMWithAttributes(node,func,depth,returnedFromParent);
			node = node.nextSibling;
		}//while
	}//if
};
window['JC']['walkTheDOMWithAttributes'] = walkTheDOMWithAttributes;

function camelize(s){
	return s.replace(/-(\w)/g,function(strMatch, p1){
		return p1.toUpperCase();
	});
};
window['JC']['camelize'] = camelize;

function uncamelize(s, sep) {
    sep = sep || '-';
    return s.replace(/([a-z])([A-Z])/g, function (strMatch, p1, p2){
        return p1 + sep + p2.toLowerCase();
    });
}
window['JC']['uncamelize'] = uncamelize;

function stopPropogation(e){
	//e = e || getEventObject(e);//remember this is different
	if (!e) var e = window.event;
	if (e.stopPropagation) {
		e.stopPropagation();
	}
	else {
		e.cancelBubble = true;
	}
};
window['JC']['preventDefault'] = preventDefault;

function preventDefault(e){
//	e = e || getEventObject(e);//remember this is different
	if (!e) var e = window.event;
	if(e.preventDefault){
		e.preventDefault();
	}else{
		e.returnValue = false;
	}
};
window['JC']['preventDefault'] = preventDefault;


//method explained here http://dean.edwards.name/weblog/2005/09/busted/
//don't need to wait for all images to load
function addLoadEvent(loadEvent,waitForImages){
	if(!isCompatible()) return false;

	//if wait flag is true then use load event
	if(waitForImages){
		return addEvent('window','load',loadEvent);
	}
	//else wait
	var init = function(){
	 if(arguments.callee)return;
	//https://developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Functions_and_function_scope/arguments/callee
	 	arguments.callee.done = true;
	 	loadEvent.apply(document,arguments);
	 };//init()

	if(document.addEventListener){
		document.addEventListener("DOMcontentLoaded",init,false);
		//DOMcontentLoaded = invoked when the dom markup is loaded
	}
	    // For Safari, use a setInterval() to see if the document has loaded
    if (/WebKit/i.test(navigator.userAgent)) {
        var _timer = setInterval(function() {
            if (/loaded|complete/.test(document.readyState)) {
                clearInterval(_timer);
                init();
            }
        },10);
    }
    // For Internet Explorer (using conditional comments) attach a script
    // that is deferred to the end of the load process and then check to see
    // if it has loaded
    /*@cc_on @*/
    /*@if (@_win32)
    document.write("<script id=__ie_onload defer src=javascript:void(0)><\/script>");
    var script = document.getElementById("__ie_onload");
    script.onreadystatechange = function() {
        if (this.readyState == "complete") {
            init();
        }
    };
    /*@end @*/
    return true;

}
window['JC']['addLoadEvent'] = addLoadEvent;
//read page 186
function testIe(e){
	alert("testing ie;");

	e = e || getEventObject(e);
}
window['JC']['testIe'] = testIe;
function getEventObject(e) {
	//alert("calling event object");
    var e = e || window.event;
	return e;
}
window['JC']['getEventObject'] = getEventObject;
//accessing the event target - 192
//http://www.quirksmode.org/js/events_properties.html
//http://www.quirksmode.org/dom/events/index.html
function getTarget(e){
	var target;
	if (!e) var e = window.event;
	if (e.target) target = e.target;
	else if (e.srcElement) target = e.srcElement;
	if (target.nodeType == 3) // defeat Safari bug
		target = target.parentNode;
	return target;

}
window['JC']['getTarget'] = getTarget;
//194
function getMouseButton(e){
	if (!e) var e = window.event;

	//ini buttons
	var buttons = {left:'false',middle:'false',right:'false'};
	//deterbuttons //w3c events have a toString method
	//ie has different set of number starts at 1

	if(e.button != null){
                    if(e.toString && e.toString().indexOf('MouseEvent') != -1){
        //alert("here");
                switch(e.button){
                    case 0: buttons.left = true; break;
                    case 1: buttons.middle = true; break;
                    case 2: buttons.right = true; break;
                    default:break;
                   }//switch
            }else{
                //probably only need 3 cases as the rest are not needed
                switch(e.button){
                    case 1:buttons.left = true;break;
                    case 2:buttons.right = true;break;
                    case 3:button.left = true;button.right=true;break;
                    case 4:buttons.middle = true;break;
                    case 5:buttons.left = true;button.middle = true;break;
                    case 6:buttons.middle = true;button.right = true;break;
                    case 7:buttons.left=true;button.middle=true;button.right = true;break;
                    default:break;
                }//switch
            }//else
	}else{
		return false;
	}
    //cannot we just return one
	return buttons;
}
window['JC']['getMouseButton'] = getMouseButton;

function getPointerPositionInDocument(e){
	if (!e) var e = window.event;
	var x = e.pageX || (e.clientX + (document.documentElement.scrollLeft || document.body.scrollLeft));
	var y = e.pageY ||(e.clientY + (document.documentElement.scrollTop || document.body.scrollTop));
	return {'x':x,'y':y};
}
window['JC']['getPointerPositionInDocument'] = getPointerPositionInDocument;

function getKeyPressed(e){
	if (!e) var e = window.event;
	var code = e.keyCode;
	var value = String.fromCharCode(code);
	return {'code':code,'value':value};
}
window['JC']['getKeyPressed'] = getKeyPressed;


//style stuff is really needed?
//setStylebyId -- 216
function setStyleById(ele,styles){
    //style can be an array
    if(!(ele = id(ele))) return false;
        for(prop in styles){
     if(!styles.hasOwnProperty(prop)) continue;
         if(ele.style.setProperty){
            ele.style.setProperty(uncamelize(prop,'-'),styles[prop],null);
        }else{
              ele.style[camelize(prop)] = styles[prop];
        }
     }
    return true;
}
window['JC']['setStyleById'] = setStyleById;



function getStyle(element,property) {
    if(!(element = id(element)) || !property) return false;
    // Check for the value in the element's style property
    var value = element.style[camelize(property)];
    if (!value) {
        // Retrieve the computed style value
        if (document.defaultView && document.defaultView.getComputedStyle) {
            // The DOM method
            var css = document.defaultView.getComputedStyle(element, null);
            value = css ? css.getPropertyValue(property) : null;
        } else if (element.currentStyle) {
            // The MSIE method
            value = element.currentStyle[camelize(property)];
        }
    }
    // Return an empty string rather than auto so that you don't
    // have to check for auto values
    return value == 'auto' ? '' : value;
}
window['JC']['getStyle'] = getStyle;
window['JC']['getStyleById'] = getStyle;

function hasClass(element,classText) {
    return element.className.match(new RegExp('(\\s|^)'+classText+'(\\s|$)'));
}
window['JC']['hasClass'] = hasClass;

function removeClass(element,classText) {
    if (hasClass(element,classText)) {
        var reg = new RegExp('(\\s|^)'+classText+'(\\s|$)');
        element.className=element.className.replace(reg,' ');
    }
}
window['JC']['removeClass'] = removeClass;


//AJAX stuff
//314
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]);
				}//
			}//for
		}//if
	}//walk
	return filter(k,v);
	//3 stage filter
	//first test against a reqular expression
	 if (/^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/.test(s)){
		 try{
			 j = eval('(' + s + ')' );
		 }catch(e){
			 throw new SyntaxError("parseJSON");
		 }
	 }else{
		 throw new SyntaxError("parseJSON");
	 }
	 //good way of testing
	 if(typeof filter == 'function'){
		 j = walk('',filter);
	 }
	return j;
}
window['JC']['parseJson'] = parseJson;

function getRequestObject(url,options){
	var req = false;
	if(window.XMLHttpRequest){
		req = new window.XMLHttpRequest();
	}else if(window.activeXRequest){
		req = new window.activeXRequest('Microsoft.XMLHTTP');
	}//elif
	if(!req)return false;
	//set default options
	JC.log.write(req);
	options = options || {};
	options.method = options.method || 'GET';
	options.send = options.send || null;
	//check
	JC.log.write(options.send);
	JC.log.write(options.method);

	//define various states
	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:
		//compete
		try{
			if(req.status && req.status == '200'){
				//start a list of specific listeners for different reponses
				// Specific listeners for content-type
				// The Content-Type header can include the charset:
				// Content-Type: text/html; charset=ISO-8859-4
				// So we'll use a match to extract the part we need.
				var contentType = req.req.getResponseHeader('Content-Type');
				var mimeType = contentType.match(/\s*([^;]+)\s*(;|$)/i)[1];


				//call on req (obj)
				switch (mimeType){
				case 'text/javascript':
				case 'application/javascript':
					//javascript
					if(options.jsResponseListener){
						options.jsResponseListener.call(req,req.responseText);
					}
					break;
				case 'application/json':
					if(options.jsonResponseListener){
						try{
							var json = parseJson(req.responseText);
						}catch(e){
							var json = false;
						}
						options.jsonResponseListener.call(req,json);
					}
					break;
				case 'text/xml':
				case 'application/xml':
				case 'application/xhtml+xml':
					//response is xml
					if(options.XMLresponseListener){
						options.XMLresponseListener.call(req,req.responseXML);
					}
					break;
				case 'text/html':
					//reponse html
					if(options.HTMLResponseListener){
						options.HTMLResponseListener.call(req,req.responseText);
					}
					break;
				}//switch
				//A complete lister exists
				if(options.completeLister){
					options.completeLister.call(req,arguments);
				}
			}else{//req status is not 200 or 400
				if(options.errorListener) {
					options.errorListener.apply(req,arguments);
				}
			}
		}catch(e){
			//ignore errors
		}//catch
		break;
		}
	};//readyStateChange

	req.open(options.method,url,true);
	req.setRequestHeader('X-JC-Ajax-Request','AjaxRequest');
	return req;
}
window['JC']['getRequestObject'] = getRequestObject;

function ajaxRequest(url,options){
	var req = getRequestObject(url,options);
	return req.send(options.send);;
}
window['JC']['ajaxRequest'] = ajaxRequest;

//OWN STUFF STARTS
//cross browser set attribute//cribbed from Resig
function setAttr(ele, attr, value) {
	if ( !attr || attr.constructor != String ) return '';
		attr = { 'for': 'htmlFor', 'class': 'className' }[attr] || attr;
	if ( typeof attr != 'undefined' ) {
		ele[attr] = value;
	}else {
		ele.setAttribute(attr,value);
	}
	return ele[attr] || ele.getAttribute(attr) || '';
};
window['JC']['setAttr'] = setAttr;


function getElementsByAttribute( attrib, value, context_node, tag ) {
    var nodes = [];
    if ( context_node == null )
        context_node = this;
    if ( tag == null )
        tag = '*';
    var elems = context_node.getElementsByTagName(tag);

    for ( var i = 0; i < elems.length; i += 1 ) {
        if ( value ) {
            if ( elems[i].hasAttribute(attrib) && elems[i].getAttribute(attrib) == value )
                nodes.push(elems[i]);
        } else {
            if ( elems[i].hasAttribute(attrib) )
                nodes.push(elems[i]);
        }
    }
    return nodes;
}
window['JC']['getElementsByAttribute'] = getElementsByAttribute;

function getPosition(obj){
    var pos = {'top':false,'left':false};
    while(obj) {
        // leftValue+= obj.offsetLeft;
         pos['left'] += obj.offsetLeft;
         pos['top'] += obj.offsetTop;

        // topValue+= obj.offsetTop;
         obj= obj.offsetParent;
    }
    // finalvalue = leftValue + "," + topValue;
     return pos;
}
window['JC']['getPosition'] = getPosition;

function setStylesOnElement(ele, styles, parent) {
    parent = $(parent) || document;
    if(!(ele || id(ele)))return;

    setStyleById(ele, styles);

}
window['JC']['setStylesOnElement'] = setStylesOnElement;

function getComputedStyle(element,property) {
    if(!(element = id(element)) || !property) return false;
    // Check for the value in the element's style property
    var value = element.style[camelize(property)];
    if (!value) {
        // Retrieve the computed style value
        if (document.defaultView && document.defaultView.getComputedStyle) {
            // The DOM method
            var css = document.defaultView.getComputedStyle(element, null);
            value = css ? css.getPropertyValue(property) : null;
        } else if (element.currentStyle) {
            // The MSIE method
            value = element.currentStyle[camelize(property)];
        }
    }
    // Return an empty string rather than auto so that you don't
    // have to check for auto values
    return value == 'auto' ? '' : value;
}
window['JC']['getComputedStyle'] = getComputedStyle;
window['JC']['getComputedStyle'] = getComputedStyle;

//http://www.hiteshagrawal.com/javascript/must-have-functions-in-javascript
//http://evolt.org/node/26852

//return index of in an array will be useful when you want to remove a node
//also some new array methods
//also array filter



/*start validation methods*/
//http://rgagnon.com/jsdetails/js-0063.html
var validator = {
        trimAll:function (value) {
         var objRegExp = /^(\s*)$/;
        //check for all spaces
        if(objRegExp.test(value)) {
           value = value.replace(objRegExp, '');
           if( value.length == 0)
              return value;
        }
       //check for leading & trailing spaces
       objRegExp = /^(\s*)([\W\w]*)(\b\s*$)/;
       if(objRegExp.test(value)) {
           //remove leading and trailing whitespace characters
           value = value.replace(objRegExp, '$2');
        }
      return value;
    },
    email:function(value){
            var emailPattern = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/;
            return emailPattern.test(value);
    },
    url:function(value){
            var regexp = /(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/
            return regexp.test(s);
    },
    text:function(value){

    },
    textarea:function(value){
       var strTemp = value;
       strTemp = this.trimAll(strTemp);
       if(strTemp.length > 0){
         return true;
       }
       return false;
  }





}


window['JC']['validate'] = validator;



function testFunc(arg){
   // JC.log.write("here" + arg);
    for(prop in arg){
        JC.log.write(prop + " " + arg[prop]);
    }


}
window['JC']['testFunc'] = testFunc;


window['JC']['old'] =  {

		cEle : function(e){return document.createElement(e);},
		tNode : function(t){return document.createTextNode(t);},
		aChild :function(n,e){return (e ||document).appendChild(n);},
		iBefore : function(e,n,b){(e).insertBefore( n, b);},
			 createQCObject :function() {

	   var req;
	   if(window.XMLHttpRequest){
	      // Firefox, Safari, Opera...
	      req = new XMLHttpRequest();
	   } else if(window.ActiveXObject) {
	      // Internet Explorer 5+
	      req = new ActiveXObject("Microsoft.XMLHTTP");
	   } else {
	      alert('Problem creating the XMLHttpRequest object');
	   }
	   return req;
		}
};




//end of main function
})();




