var Generation = {
	namespace: function(name, separator){
		var ns = name.split(separator || '.'), p = window, i;
		for (i = 0; i < ns.length; i++) {
			p = p[ns[i]] = p[ns[i]] || {};
		}
		return p;
	},
	extend: function(object, source, override){
		for(var k in source) if(override || typeof object[k] == "undefined") object[k] = source[k];
	},
	override: function(object, source){
		Generation.extend(object, source, true);
	},
	define: function(namespace, options){
		var obj = Generation.namespace(namespace);
		Generation.extend(obj, options);
	}
};

Generation.BrowserInfo = new (function(){
	var isOpera, isWebKit, isIE, isIE6, isGecko;
	var d = document, w = window;
	isOpera = !!(w.opera && opera.buildNumber);
	isWebKit = !!(w.devicePixelRatio);
	isOldWebKit = isWebKit && !(w.getSelection().getRangeAt);
	isIE = !(!d.all) && !(!w.ActiveXObject);
	isIE6 = isIE && !(w.XMLHttpRequest);
	isGecko = !!(d.getBoxObjectFor);
	Generation.extend(this, {
		opera: isOpera,
		webkit: isWebKit,
		oldwebkit: isOldWebKit,
		ie: isIE,
		ie6: isIE6,
		gecko: isGecko
	});
})();

Generation.define("Generation.Css", {
	styleSheet: function(){
		/*
		var tag = document.createElement("style");
		tag.setAttribute("type", "text/css");
		Generation.extend(this, {
			addRule: function(a, b){
				
			},
			addLine: function(line){
				
			}
		});
		*/
	}
});

Generation.define("Generation.Utils", {
	getType: function(obj){
		if(obj === null) return false;
		if(typeof obj == "undefined") return typeof obj;
		if(obj.call) return "function";
		switch (obj.constructor){
			case Array:
				return "array";
			case RegExp:
				return "regex";
			default:
				return (typeof obj);
		}
	},
	buildQueryString: function(params){
		var array = []
		for(var k in params){
			var value = params[k];
			var type = Generation.Utils.getType( value );
			if( type != "array")
				array.push(k+"="+value);
			else {
				for(var i = 0, l = value.length; i < l; i++){
					array.push(k+"[]="+value[i]);
				}
			}
		}
		if(array.length != 0)
			return array.join("&");
		return '';
	},
	isEmpty: function(object){
		if(Generation.Utils.getType(object) == "array")
			return (object.length == 0)
		for(var i in object)
			return false;
		return true;
	},
	bind: function(fn, bind){
		return function(){
			fn.apply(bind, arguments);
		}
	}
});

Generation.define("Generation.Event", {
	addListener: function(ev, fn, el){
		el = el || window;
		if(ev.substr(0,2) == "on") ev = ev.substr(2);
		
		if(el.addEventListener){
			el.addEventListener(ev, function(evt){
				var ret = fn.call(el, evt);
				if(ret === false){
				    if(evt.stopPropagation) evt.stopPropagation();
				    else evt.cancelBubble = true;
					if(evt.preventDefault) evt.preventDefault();
					else evt.returnValue = false;
					return false;
				}
			}, false);
		} else if (el.attachEvent){
			el.attachEvent('on'+ev, function(){
				var ret = fn.call(el, window.event);
				if(ret === false)
					return false;
			});
		}
	}
});

Generation.define("Generation.Element", {
	get: function(id){
		return Generation.Element.extend(document.getElementById(id));
	},
	extend: function(el){
		if(!el) return el;
		for(var m in Generation.Element.Utils){
			if(el[m]) continue;
			el[m] = Generation.Element.Utils[m];
		}
		return el;
	}
});
Generation.define("Generation.Element.Utils", {
	addClass: function(cls){
		if(!Generation.Element.Utils.hasClass.call(this, cls)){
			var classes = Generation.Element.Utils.getClasses.call(this);
			classes.push(cls);
			this.className = classes.join(" ");
		}
	},
	removeClass: function(cls){
		var classes = Generation.Element.Utils.getClasses.call(this);
		var klasses = [];
		for(var i = 0, l = classes.length; i < l; i++)
			if(classes[i] != cls)
				klasses.push(classes[i]);
		this.className = klasses.join(" ");
	},
	hasClass: function(cls){
		var classes = Generation.Element.Utils.getClasses.call(this);
		for(var i = 0, l = classes.length; i < l; i++)
			if(classes[i] == cls)
				return true;
		return false;
	},
	toggleClass: function(cls){
		if(Generation.Element.Utils.hasClass.call(this, cls))
			Generation.Element.Utils.removeClass.call(this, cls);
		else
			Generation.Element.Utils.addClass.call(this, cls);
	},
	getClasses: function(){
		return (''+this.className).split(/\s+/);
	},
	addListener: function(ev, fn){
		Generation.Event.addListener(ev, fn, this);
	},
	clear: function(){
		var garbage = document.createElement("div");
		while(this.childNodes.length)
			garbage.appendChild(this.childNodes[0]);
		//garbage.innerHTML = "";
	}
});

Generation.define("Generation.Ajax", {
	xhr: function(){
		return (window.ActiveXObject) ? new ActiveXObject("Microsoft.XMLHTTP") : new XMLHttpRequest();
	},
	Request: function(options){
		this.xhr = Generation.Ajax.xhr();
		
		var self = this;
		var empty = function(){};
		self.change = options.change || empty;
		self.complete = options.complete || empty;
		self.success = options.success || empty;
		self.error = options.error || empty;
		
		this.xhr.onreadystatechange = function(){
			self.change(self.xhr);
			if (self.xhr.readyState == 4){
				self.complete(self.xhr.responseText, self.xhr.responseXML);
				if(self.xhr.status == 200 || self.xhr.status == 304) {
					self.success(self.xhr.responseText, self.xhr.responseXML);
				} else {
					self.error(self.xhr);
				}
			}
		}
		var method = (options.method) ? options.method.toUpperCase() : "GET";
		var action = (''+options.url);
		var contentType = options.contentType || "application/x-www-form-urlencoded";
		var paramsType = Generation.Utils.getType( options.params );
		var params;
		if(paramsType == "string")
			params = options.params;
		else
			params = Generation.Utils.buildQueryString( options.params || {} );
		
      Generation.extend(this, {
         url: action,
         query: params,
         params: options.params || {},
         method: method,
         contentType: contentType
      });
      
		this.send = function()
			{
			if(method == 'GET' && params.length != 0){
				if(action.indexOf('?') != -1)
					action += '&' + params;
				else 
					action += '?' + params;
			}
         if(action.indexOf('?') != -1)
            action += '&';
         else
            action += '?';
         action += 'tstamp=' + new Date().getTime();
			this.xhr.open(method, action, true);
         this.xhr.setRequestHeader("Content-type", contentType);
			if(method == 'POST')
				this.xhr.send(params);
			else
				this.xhr.send("");
		}
	},
	SubmitForm: function(form, callback){
		var action = form.action || form.getAttribute('action') || (''+window.location);
		var elems = Generation.Shortcuts.Form.getElements(form);
		var params = {};
		var el;
		for(var i = 0, l = elems.length; i < l; i++)
			{
			el = elems[i];
			var name = el.name;
			var value = (el.value || '');
			var type = (el.getAttribute("type")+'').toLowerCase();
			var skip = false;
			switch(type){
				case "radio":
				case "checkbox":
					if(!el.checked) skip = true;
					break;
			}
			if(skip) continue;
			if(name){
				var array = false;
				if(name.substr(name.length-2) == '[]'){
					name = name.substr(0, name.length-2)
					array = true;
				}
				if(!array){
					params[name] = value
				} else {
					if(!params[name]) params[name] = [];
					params[name].push( value );
				}
			}
		}
		var req = new Generation.Ajax.Request({
			url: ''+form.action,
			method: (form.method || 'get'),
			params: params,
			success: callback
		});
		req.send();
	}
});

Generation.define("Generation.Ajax.CrossDomain", {
   Request: function(options){
      Generation.extend(this, new Generation.Ajax.Request(options));
      console.log( this );
      var self = this;
      this.send = function(){
         var iframe;
         var id = "Generation_Ajax_CrossDomain_Request";
         iframe = document.getElementById(id);
         var body = document.body || document.getElementsByTagName("body")[0];
         if(!iframe){
            if(Generation.BrowserInfo.ie){
               document.write('<iframe id="' + id + '" style="display:none; border:0;"></iframe>');
            } else {
               iframe = document.createElement("iframe");
               iframe.id = id;
               iframe.setAttribute("style", "display:none; border:0;");
               body.appendChild(iframe);
            }
         }
         
         var doc = iframe.contentDocument || iframe.document;
         var form = Generation.Element.extend(doc.createElement("form"));
         form.setAttribute("action", self.url);
         form.setAttribute("method", self.method);
         
         for(var name in self.params){
            var hid = doc.createElement("input");
            hid.setAttribute("name", name);
            hid.setAttribute("value", self.params[name]);
            form.appendChild(hid);
         }
         
         var doc_body = doc.body || doc.getElementsByTagName("body")[0];
         doc_body.appendChild(form);
         
         var doc_body = Generation.Element.extend( doc_body );
         
         iframe = Generation.Element.extend( iframe );
         console.log( iframe );
         console.log( doc_body );
         console.log( form );
         
         var win = iframe.contentWindow || iframe.window;
         
         iframe.addListener("load", function(ev){
            // cant access anything
         });
         form.submit();
         var d = document.createElement("div");
         d.appendChild( iframe );
      }
   }
});

Generation.define("Generation.Json", {
	replaceChars: function(chr){
		return JSON.$specialChars[chr] || '\\u00' + Math.floor(chr.charCodeAt() / 16).toString(16) + (chr.charCodeAt() % 16).toString(16);
	},
	encode: function(obj){
		var $type = Generation.Utils.getType(obj);
		switch ($type){
			case 'string':
				return '"' + obj.replace(/[\x00-\x1f\\"]/g, Generation.Json.replaceChars) + '"';
			case 'array':
				var array = [];
				for(var i = 0, l = obj.length; i < l; i++){
					var item = Generation.Json.encode(obj[i]);
					if(typeof item != "undefined")
						array[i] = item;
				}
				return '[' + String(array) + ']';
			case 'object':
				var string = [];
				for(var key in obj){
					var json = Generation.Json.encode(obj[key]);
					if(json) string.push(Generation.Json.encode(key) + ':' + json);
				}
				return '{' + string + '}';
			case 'number': case 'boolean': return String(obj);
			case false: return 'null';
		}
		return null;
	},
	decode: function(json){
		if(window.JSON && JSON.decode){
			return JSON.decode(json);
		} else {
			var obj;
			try {
				obj = eval( '(' + json + ')' );
			} catch (err) { }
			return obj || {};
		}
	}
});

Generation.define("Generation.Selectors", {
	XPath: function(node, expr){
		var iterator, item;
		if(window.XPathEvaluator){
			var xpe = new XPathEvaluator();
			var nsResolver = xpe.createNSResolver(node.ownerDocument == null ? node.documentElement : node.ownerDocument.documentElement);
			iterator = xpe.evaluate(expr, node, nsResolver, 0, null);
		} else if (window.ActiveXObject){
			
		}
		var found = [];
		if(iterator)
			while (item = iterator.iterateNext())
				found.push(item);
		return found;
	}
});


(function(){
	if (!document.ELEMENT_NODE) {
		document.ELEMENT_NODE = 1;
		document.ATTRIBUTE_NODE = 2;
		document.TEXT_NODE = 3;
		document.CDATA_SECTION_NODE = 4;
		document.ENTITY_REFERENCE_NODE = 5;
		document.ENTITY_NODE = 6;
		document.PROCESSING_INSTRUCTION_NODE = 7;
		document.COMMENT_NODE = 8;
		document.DOCUMENT_NODE = 9;
		document.DOCUMENT_TYPE_NODE = 10;
		document.DOCUMENT_FRAGMENT_NODE = 11;
		document.NOTATION_NODE = 12;
	}
}());
Generation.define("Generation.Xml", {
	parseXml: function(text){
		if(window.ActiveXObject){
			var doc = new ActiveXObject("Microsoft.XMLDOM");
			doc.async = "false";
			doc.loadXML(text);
			return doc;
		} else if (window.DOMParser){
			return (new DOMParser()).parseFromString(text, "text/xml");
		}
		return null;
	},
   createInput: function(attributes){
      var el, attr = {};
      for(var i = 0, l = attributes.length; i < l; i++){
         var a = attributes[i];
         attr[a.nodeName.toLowerCase()] = a.nodeValue;
      }
      try {
         var str = '<input' +(attr.type?' type='+attr.type+'':'')+'>';
         el = document.createElement(str);
      } catch(err){
         el = document.createElement('input');
         if (attr.type) el.type=attr.type;
      }
      for(var a in attr){
         if(a == 'type') continue;
         el.setAttribute(a, attr[a]);
      }
      return el;
   },
	importNode: function(node, allChildren, doc){
		doc = doc || document;
		switch (node.nodeType) {
			case document.ELEMENT_NODE:
            var nodeName = node.nodeName.toLowerCase();
            var isTable = false;
            var newNode = doc.createElement(node.nodeName);
            if (node.attributes && node.attributes.length > 0)
               for (var i = 0, il = node.attributes.length; i < il; i++){
                  var attributeName = node.attributes[i].nodeName.toLowerCase();
                  var attributeValue = node.getAttribute(node.attributes[i].nodeName);
                  switch(attributeName){
                     case "class":
                        newNode.className = attributeValue;
                        break;
                        
                     case "cellspacing":
                        newNode.cellSpacing = attributeValue;
                        break
                     case "cellpadding":
                        newNode.cellPadding = attributeValue;
                        break
                        
                     case "valign":
                        newNode.vAlign = attributeValue;
                        break;
                        
                     
                     case "colspan":
                        newNode.colSpan = attributeValue;
                        break;
                     case "rowspan":
                        newNode.rowSpan = attributeValue;
                        break;
                     
                     case "for":
                        newNode.htmlFor = attributeValue;
                        break;
                        
                     case "border":
                     case "id":
                     case "name":
                     case "type":
                        newNode[attributeName] = attributeValue;
                        break;
                        
                     default:
                        newNode.setAttribute(node.attributes[i].nodeName, attributeValue);
                        break;
                  }
               }
            if (allChildren && node.childNodes && node.childNodes.length > 0){
               if(newNode.nodeName.toLowerCase() == 'table'){
                  isTable = true;
                  var table = newNode;
                  
                  var firstElement = node.firstChild;
                  while(firstElement.nodeType != document.ELEMENT_NODE && firstElement.nextSibling)
                     firstElement = firstElement.nextSibling;
                  if(firstElement.nodeName.toLowerCase() != 'tbody'){
                     var tbody = document.createElement('tbody');
                     table.appendChild(tbody);
                     newNode = tbody;
                  }
               }
               for (var i = 0, il = node.childNodes.length; i < il; i++){
                  newNode.appendChild(Generation.Xml.importNode(node.childNodes[i], allChildren));
               }
            }
            if(isTable)
               newNode = table;
            
            return newNode;
            break;
			case document.TEXT_NODE:
			case document.CDATA_SECTION_NODE:
			case document.COMMENT_NODE:
            // cannot use pre tag because of a whitespace bug in IE
                var txt = node.nodeValue;
                if(txt != "\u00A0")
                    txt = txt.replace(/\s+/, ' ');
				return document.createTextNode(txt);
				break;
		}
	}
});


Generation.define("Generation.Validation.Expressions", {
	isNaInt: /[^\d]+/,
	isNaN: /[^\d\.,]+/,
	email: /\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/,
	codePostal: /(\w\d\w)\s*(\d\w\d)/,
	usCodePostal: /(\d{5}-\d{4})|(\d{5})/,
	phone: /(1)?\W*(\d{3})\W*(\d{3})\W*(\d{4})\W*(\d{2,6})?/
});
Generation.namespace("Generation.Validation.Custom");
Generation.define("Generation.Validation", {
	custom: function(value, regex){
		return regex.test( value );
	},
	empty: function(value){
		return (value === null || typeof value == "undefined" || (value.constructor && value.length == 0));
	},
	email: function(value){
		return Generation.Validation.custom(value, Generation.Validation.Expressions.email);
	},
	codePostal: function(value){
		return Generation.Validation.custom(value, Generation.Validation.Expressions.codePostal);
	},
	usCodePostal: function(value){
		return Generation.Validation.custom(value, Generation.Validation.Expressions.usCodePostal);
	},
	phone: function(value){
		return Generation.Validation.custom(value, Generation.Validation.Expressions.phone);
	},
	number: function(value){
		return !Generation.Validation.custom(value, Generation.Validation.Expressions.isNaN);
	},
	integer: function(value){
		return !Generation.Validation.custom(value, Generation.Validation.Expressions.isNaInt);
	}
});

Generation.define("Generation.Validation.Regular", {
	R: function(value){ return !Generation.Validation.empty(value); },
	E: Generation.Validation.email,
	CP: Generation.Validation.codePostal,
	USCP: Generation.Validation.usCodePostal,
	P: Generation.Validation.phone,
	N: Generation.Validation.number,
	F: Generation.Validation.number,
	I: Generation.Validation.integer
});

Generation.define("Generation.Shortcuts.Form", {
	getElements: function(form){
		var types = ['input', 'select', 'textarea'];
		var elems, all = [];
		for(var i = 0, l = types.length; i < l; i++){
			elems = form.getElementsByTagName(types[i]);
			for(var e = 0, len = elems.length; e < len; e++){
				all.push( elems[e] );
			}
		}
		return all;
	},
	validate: function(form){
		var elems = Generation.Shortcuts.Form.getElements(form);
		var i = 0;
		var errors = {};
		for(var i = 0, l = elems.length; i < l; i++)
			{
			var el = elems[i];
			if(!el.name) continue;
			var ret = Generation.Shortcuts.Element.validate(el);
			if(ret !== true){
				errors[el.name] = ret;
				//alert(ret);
         }
		}

		if(!Generation.Utils.isEmpty(errors))
			return errors;

		return true;
	},
	showErrors: function(validation){
		for(var id in validation){
			var opt = id.split("_");
			var name = opt[opt.length - 1];
			var errors = validation[id];
			for(var i = 0, l = errors.length; i < l; i++){
				var el = Generation.Element.get(["ERR", errors[i], name].join("_"));
				if(el){
					el.removeClass("hide");
				}
			}
		}
	}
});

Generation.define("Generation.Shortcuts.Ajax.Element", {
	append: function(el, url, callback){
		var req = new Generation.Ajax.Request({
			url: url,
			success: function(text, xml){
				var error = false;
				try {
					if(!xml || xml && !xml.documentElement){
						xml = Generation.Xml.parseXml(text);
					}
					if(xml && xml.documentElement && xml.documentElement.tagName != "parsererror"){
						var newNode, importedNode;
						newNode = xml.documentElement;
						if (newNode.nodeType != document.ELEMENT_NODE)
							newNode = newNode.nextSibling;
						if (newNode) {
							importedNode = Generation.Xml.importNode(newNode, true);
							el.appendChild(importedNode);
							if (!document.importNode)
								el.innerHTML = el.innerHTML;
						}
					} else {
						error = true;
					}
				} catch(err) { error = true; }
				if(error){
					el.innerHTML = text;
				}
				if(callback && callback.call) callback();
			}
		});
		req.send();
	}
});

Generation.define("Generation.Shortcuts.Element", {
	validate: function(el){
		var errors = [];
		var name = (''+el.name);
		var options = name.split(/_+/);
		//console.log([ options, /[a-z0-9]+/mi.exec( name ) ]);
		for(var o = 0, len = options.length-1; o < len; o++){
			var option = options[o];
            var multiple = option.split('-');
			var pass = false;
            for(var y = 0, length = multiple.length; y < length; y++){
                if( Generation.Validation.Regular[multiple[y]] ){
                    if( Generation.Validation.Regular[multiple[y]](el.value) ){
                        pass = true;
                        break;
                    }
                }
            }
            /*
            if(Generation.Validation.Regular[option])
                pass = Generation.Validation.Regular[option](el.value);
             */
			
			regex = /C\d+/i
			if(regex.test(option)){
				var custom = Generation.Validation.Custom[option];
				if(custom){
					var type = Generation.Utils.getType(custom);
					if(type == "regex")
						pass = Generation.Validation.custom(el.value, custom);
					else if (type == "function")
						pass = custom(el.value);
				} else 
					pass = false;
			}
			if(!pass) errors.push(option);
		}
		if(errors.length == 0) return true;
		return errors;
	},
	hideErrors: function(element){
		var options = (''+element.name).split(/_+/);
		var name = options[options.length - 1];
		for(var o = 0, len = options.length - 1; o < len; o++){
			var option = options[o];
			var el = Generation.Element.get(['ERR', option, name].join("_"));
			if(el){
				el.removeClass("hide");
				el.addClass("hide");
			}
		}
	}
});