/**
 * NS global components
 * Contains components that are used throughout the NS site by various other objects.
 * 
 * @version   1.00.090814
 * @author    LBI Lost Boys
 */
NS(function($){

	/**
	 * SimpleMenu component
	 */
	var STATE_COLD = 1;
	var STATE_HOT  = 2;

	NS.SimpleMenu = function(element, settings){
		$.extend(this, NS.SimpleMenu.Defaults, settings);
		this.root = element;
		this.state = STATE_COLD;
		this.stateChange = null;
		
		var jNode = $(this.root);
		jNode.bind('mouseover', this.mouseover.bind(this));
		jNode.bind('mouseout', this.mouseout.bind(this));

		if(this.keyEnabled) {
			var focusable = $(this.focusType, this.root);
			focusable.bind('focus', this.mouseover.bind(this));
			focusable.bind('blur', this.mouseout.bind(this));
		}
		
		//iframe for covering up select boxes in IE
		this.ieFrame = NS.Interface.createIEFrame();
	};

	NS.SimpleMenu.prototype = {
		toggleMenu:function(item, toggle) {
			$(item)[toggle? 'addClass' : 'removeClass'](this.activeClass);
			
			var menu = $(this.menuType, item).eq( 0);
			this.toggleIEFrame(toggle,menu);
		},
		
		toggleItem:function(item, toggle) {
			$(item)[toggle? 'addClass' : 'removeClass'](this.hoverClass);
		},

		mouseover: function(e) {
			var item = $(e.target).closest(this.itemType)[0];
			if(item && item != this.item) {
				this.setCurrent(item);
				clearTimeout(this.stateChange);
				var self = this, toggle = function(){ self.toggle(item); };

				switch(this.state) {
					case STATE_COLD:
						this.stateChange = setTimeout(toggle, this.openDelay);
					break;
					case STATE_HOT: 
						this.stateChange = setTimeout(toggle, this.switchDelay);
					break;
				}
			}
		},

		mouseout: function(e) {
			var node = e.relatedTarget;
			while(node) {
				if(node == this.root) { 
					return;
				}
				node = node.parentNode;
			}

			this.setCurrent(null);
			clearTimeout(this.stateChange);

			var self = this;
				
			switch(this.state) {
				case STATE_COLD: break;
				case STATE_HOT:
					this.stateChange = setTimeout(function(){ 
						self.toggle(false);
					}, this.closeDelay);
				break;
			}
		},

		toggle: function(item) {
			var tree = item? item.parentNode : this.root;
			var lists = $(tree).find(this.menuType);
			var list;

			for(var i=0; (list=lists[i++]);) {
				if(list.parentNode != item) {
					this.toggleMenu(list.parentNode, false);
				}
			}

			if(item) {
				list = $(item).find(this.menuType)[0];
				if(list) {
					this.toggleMenu(list.parentNode, true);
				}
			}
			
			this.state = item? STATE_HOT : STATE_COLD;
		},

		setCurrent:function(item) {
			if(this.item) {
				this.toggleItem(this.item, false);
			}
			if(item) {
				this.toggleItem(item, true);
			}
			this.item = item;
		},
		
		toggleIEFrame:function(toggle, menu) {
			if(this.ieFrame){
				if(toggle) {
					var offset = menu.offset();
					this.ieFrame.css({
						left: offset.left + 'px',
						top: offset.top + 'px',
						width: (menu[0].offsetWidth) + 'px',
						height: (menu[0].offsetHeight) + 'px'
					});
				} else {
					this.ieFrame.css({
						left: -9999 + 'px',
						top: -9999 + 'px'
					});
				}
			}
		}
	};

	NS.SimpleMenu.Defaults = {
		openDelay:   500,
		switchDelay: 200,
		closeDelay:  1000,
		hoverClass:  'hover',
		activeClass: 'active',
		itemType:    'li',
		menuType:    'ul',
		keyEnabled:  true,
		focusType:   'a'
	};

	/**
	 * Calendar component
	 */
	NS.Calendar = function(node, settings) {
		this.container = node;
		this.allowPast = settings.allowPast;
		this.table = $('table', node);
		this.body = $('tbody', node);
		this.selects = $("select", node);
		this.setAction(function(){});
		
		this.table.bind('mouseup', this.getDate.bind(this));
		this.table.bind('mousemove', this.hoverDate.bind(this));
		this.selects.bind('change', this.upDate.bind(this));

		var now = new Date();
		this.now = new Date(now.getFullYear(), now.getMonth(), now.getDate());
		this.chooseDate(this.selects[0]);
	};

	NS.Calendar.prototype = {
		setAction:function(action) { this.action = action; },
		upDate:function(e) {
			var target = $(e.target).closest('select')[0];
			if(target) {
				this.chooseDate(target);
			}
		},

		getDateFromSelect:function(target) { 
			var year, month;
			if(/year|month/i.test(target.className)) {
				year = parseInt(this.selects[0].value, 10);
				month = parseInt(this.selects[1].value, 10) -1;
			} else {
				var date = target.value.split('/');
				year = parseInt(date[0], 10);
				month = parseInt(date[1] || 1, 10) -1;
			}

			return new Date(year, month, 1);
		},

		chooseDate:function(target) {
			var date = this.getDateFromSelect(target);
			this.setDate(date);
		},

		setSelects:function(date) {
			var selectable = true;
			var selectValue = function(select, value) {
				for (var i=0; i<select.options.length; i++) {
					if(select.options[i].value == value) {
						select.selectedIndex = i;
						return;
					}
				}
			
				selectable = false;
				select.selectedIndex = 0;
			};

			if(this.allowPast) {
				selectValue(this.selects[0], date.getFullYear());
				selectValue(this.selects[1], date.getMonth()+1);
			} else {
				var s = date.getFullYear() + '/' + (/[0-9]{2}$/).exec('0'+ (date.getMonth()+1))[0] + '/01';
				selectValue(this.selects[0], s);
			}
			
			return selectable? date : this.getDateFromSelect(this.selects[0]);
		},

		setDate:function(date, options) {
			if(options) { 
				this.startDate = options.startDate;
				this.endDate = options.endDate;
				this.allowedDates = options.dates? this.parseTimestamps(options.dates) : false;
			}
	
			var selected = this.setSelects(date || this.selectedDate);
			this.selectedDate = new Date(selected.getTime());			
			
			this.createTable();
		},

		parseTimestamps:function(stamps) {
			var dates = stamps.split(',');
			var l = dates.length;
			for(var i=0; i<l; i++) {
				dates[i] = parseInt(dates[i], 10);
			}
			return dates;
		},

		hoverDate:function(e) {
			var target = e.target;
			var day = target.innerHTML;
			if(this.currentCell && (target === this.currentCell[0])) {
				return;
			}

			if(this.currentCell) {
				this.currentCell.removeClass('current');
			}

			if(/td/i.test(target.nodeName) && /^[0-9]+$/.test(day) && !/past/.test(target.className)) {
				this.currentCell = $(target).addClass('current');
			}
		},

		getDate:function(e){
			var target = e.target;
			var day = target.innerHTML;
			if(/td/i.test(target.nodeName) && /^[0-9]+$/.test(day) && !/past/.test(target.className)) {
				var date = this.selectedDate;
				this.action(new Date(date.getFullYear(), date.getMonth(), day));
			}
		},

		createTable:function() {
			var date = new Date(this.selectedDate.getTime()); 
			date.setDate(1);

			var day, row, cell, endmode = false; 
			var month = date.getMonth(); 
			var first = date.getDay(); 
			var days = first + 42;
			
			this.currentCell = null;
			this.body.remove();
			this.body = $('<tbody />');
			this.table.empty();
			this.table.append(this.body);

			for(var i=0; i<days; i++) {
				day = i+1 - first;
				date.setMonth(month);
				if(day < 1) {
					day = ' ';
				} else {
					date.setDate(day);
					if(date.getMonth() != month) {
						day = ' '; endmode = true;
					}
				}

				if(i%7 === 0) {
					if(endmode) {
						break;
					}
					row = this.body[0].insertRow(i/7);
				}	
				
				cell = row.insertCell(i%7);
				cell.innerHTML = day;
				
				if(!this.isAllowedDate(date)) {
					cell.className = 'past';
				}
				
				if(day > 1 && Math.abs(this.selectedDate - date) < 86400000) {
					cell.className += ' today';
				}
				
				if(i%7 === 0 || i%7 === 6) {
					cell.className += ' weekend';
				}
			}
		},

		isAllowedDate:function(date) {
			if((date < this.now && !this.allowPast) ||
			  (this.startDate && date < this.startDate) ||
			  (this.endDate && date > this.endDate)){
				return false;
			}

			if(this.allowedDates) {
				var l = this.allowedDates.length;
				var time = date.getTime();
				for (var i=0; i<l; i++) {
					if(time == this.allowedDates[i]) {
						return true;
					}
				}
				return false;
			}
			return true;
		}
	};

	/**
	 * Animator
	 */
	NS.Animator = function(callBack, callEnd, easing, tick) {
		this.callBack = callBack;
		this.callEnd = callEnd;
		this.easing = easing || NS.Animator.EASEINOUT;
		this.timer = null;
		this.tick = tick || 30;
	};

	NS.Animator.prototype = {
		run:function(from, to, duration) {
			this.stop();
			this.from = from;
			this.to = to;
			this.step = 0;
			this.increment = this.tick/duration;
			this.timer = setInterval(this.animate.bind(this), this.tick);
		},
		
		animate:function() {
			if(this.paused) { return; }
			var factor = Math.min(this.easing(this.step), 1);
			var value = this.from + ((this.to - this.from) * factor);
			this.callBack(value);
			if(this.step >= 1) {
				this.stop();
				if(this.callEnd) {
					this.callEnd();
				}
			}
			this.step += this.increment;
		},

		pause:function(toggle) { 
			this.paused = toggle; 
		},

		stop:function() {
			clearInterval(this.timer);
		}
	};

	NS.Animator.LINEAR = function(n){ return n; };
	NS.Animator.EASEIN = function(n){ return 1- Math.cos(n * Math.PI/2); };
	NS.Animator.EASEOUT = function(n){ return Math.sin(n * Math.PI/2); };
	NS.Animator.EASEINOUT = function(n){ return 1- (Math.cos(n * Math.PI)/2 + 0.5); };

	/**
	 * Static XMLHttp object
	 */
	NS.XMLHttp = {
		load:function(url, func, scope){ 
			return this.sendAndLoad(null, url, func, 'get');
		},

		sendAndLoad:function(doc, url, func, type){
			var xhr = this.getXMLHttp(); 
			var async = func? true:false; 
			var method = type || 'post'; 
			var request = url;
			
			if(!/post/i.test(method) && doc) {
				request += '?' + doc;
			}

			xhr.open(method, request, async);
			if(async) {
				xhr.onreadystatechange = function() {
					if(xhr.readyState == 4) {
						func(xhr.responseXML, xhr.status);
					}
				};
			}
			
			xhr.setRequestHeader('Accept', 'text/xml,application/xml');
			//xhr.setRequestHeader('Referer', document.location.href);
			//xhr.setRequestHeader('User-Agent', navigator.userAgent);
		  //xhr.setRequestHeader('Connection', 'close');

			if(/post/i.test(method)) {
				xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
			}

			xhr.send(/post/i.test(method)? doc : null);
			return async? xhr : xhr.responseXML;
		},
		
		abort:function(xhr) {
			try {
				xhr.onreadystatechange = null;
				xhr.abort();
			} catch (fail) {}
		},

		getXMLHttp:function() {
			if(window.XMLHttpRequest) {
				return new XMLHttpRequest();
			} else if(window.ActiveXObject) {
				var xhr, http = ['Microsoft.XMLHTTP', 'Msxml2.XMLHTTP'], l = http.length;
				while(l--) {
					try {
						xhr = new ActiveXObject(http[l]);
						return xhr;
					} catch (e) {}
				}
			} else {
				return false;
			}
		}
	};

	/**
	 * Simple password strength meter
	 */
	NS.PasswordStrength = function(options){
		this.settings = $.extend({}, NS.PasswordStrength.Defaults, options);
	};

	NS.PasswordStrength.prototype = {
		getStrength:function(pass) {
			var s = this.settings;
			var strength = 0;

			if(s.regLower.test(pass)) { strength ++; }
			if(s.regUpper.test(pass)) { strength ++; }
			if(s.regNumber.test(pass)) { strength ++; }
			if(s.regOther.test(pass)) { strength ++; }
			if(s.regLength.test(pass)) { strength ++; }
			
			return strength;
		},

		getVerdict:function(pass) {
			var s = this.settings;
			var strength = this.getStrength(pass);
			
			return s.prefix + s.labels[strength];
		},
		
		setLabels:function(labels) {
			this.settings.labels = labels;
		}
	};

	NS.PasswordStrength.Defaults = {
		prefix: 'sterkte: ',
		labels: ['zeer zwak','zwak','matig','redelijk','goed','zeer goed'],
		regLower: /[a-z]/,
		regUpper: /[A-Z]/,
		regNumber: /[0-9]/,
		regOther: /[^a-z0-9]/i,
		regLength: /^.{8,}$/
	};


	/**
	 * RoundedImages, applies rounded corners to selected images
	 * 
	 */
	NS.RoundedImages = function() {
		var canvas = document.createElement('canvas');
		var isIE = /msie/i.test(navigator.userAgent) && !canvas.getContext;
		this.roundImage = isIE? this.roundImageByVML : this.roundImageByCanvas;
		
		if(isIE) {
			var NS_VML = "urn:schemas-microsoft-com:vml";
			this.importNS = '';
			if(!document.documentMode || document.documentMode < 8) {
				// IE6, 7 and 8 in compat mode
				document.namespaces.add("vml", NS_VML); 
				document.createStyleSheet().addRule('vml\\:*', "behavior: url(#default#VML);");
			} else if(document.documentMode && document.documentMode >= 8) {
				// IE8 in strict mode
				document.namespaces.add("vml", NS_VML, "#default#VML");
				this.importNS = '<?import namespace="vml" implementation="#default#VML" ?>'; 
			}
		}
	};

	NS.RoundedImages.prototype = {
		replaceImage:function(image, style) {
			if(image.complete) {
				this.roundImage(image, style);
			} else {
				var self = this;
				$(image).bind('load', function(){
					self.roundImage(image, style);
				});
			}
		},

		replaceImages:function(images, style) {
			var settings = $.extend({}, NS.RoundedImages.Defaults, style);
			for(var i=0; i<images.length; i++) {
				this.replaceImage(images[i], settings);
			}
		},

		roundImageByCanvas: function(image, style){
			var canvas = document.createElement('canvas');
			var ctx = canvas.getContext('2d');
			var w = image.width; 
			var h = image.height;
			var r = style.borderRadius;
			
			canvas.width = w;
			canvas.height = h;

			this.roundedRect(ctx, 0, 0, w, h, r);			
			ctx.clip();
			ctx.drawImage(image, 0, 0, w, h);			
			this.transform(image, canvas, style.replaced);
		},

		roundImageByVML: function(image, style){
			var w = parseInt(image.getAttribute('width') || image.width, 10);
			var h = parseInt(image.getAttribute('height') || image.height, 10);
			if(!w || !h) {
				return; // assumed display none, returns 0 for dimensions in IE7
			}

			var shape = [this.importNS];
			var borders = style.borderRadius;
			var span = document.createElement('span');
			var css = span.style;

			css.display = 'inline-block';
			css.position = 'relative';
			css.width = w + 'px';
			css.height = h + 'px';

			var path = this.roundedRect(new VMLPath(), 0, 0, w, h, borders);
			shape.push('<vml:group coordsize="',w,',',h,'" coordorigin="0,0" style="width:',w,'px;height:',h,'px;position:absolute;left:0;top:0;" ><vml:shape coordsize="',w,',',h,'" filled="true" stroked="false" path="', path,'" coordorigin="0,0" style="width:',w,'px;height:',h,'px;position:absolute;left:0;top:0;"><vml:fill src="', image.src, '" type="frame" rotate="true" alignshape="true" /></vml:shape></vml:group>');
			span.insertAdjacentHTML('BeforeEnd', shape.join(''));
			this.transform(image, span, style.replaced);
		},

		roundedRect: function(c, x, y, w, h, r){
			c.beginPath();
			c.moveTo(x, y+r); 
			c.quadraticCurveTo(x, y, x+r, y);
			c.lineTo(x+w-r, y);
			c.quadraticCurveTo(x+w, y, x+w, y+r);
			c.lineTo(x+w, y+h-r);
			c.quadraticCurveTo(x+w, y+h, x+w-r, y+h);
			c.lineTo(x+r, y+h);
			c.quadraticCurveTo(x, y+h, x, y+h-r);
			c.lineTo(x, y + r);
			return c.closePath();
		},

		transform: function(target, fragment, replaced){
			var parent = target.parentNode;
			parent.replaceChild(fragment, target);
			fragment.className = target.className + ' ' + (replaced || '');
			if(/^a$/i.test(parent.nodeName)) {
				fragment.style.cursor = 'hand';
			}
		}
	};

	NS.RoundedImages.Defaults = {
		borderRadius:  8,
		replaced:      'replacedImage'
	};

	function VMLPath() {}
	VMLPath.prototype = {
		beginPath:function(){ this.path = ''; },
		closePath:function(){ this.path += ' x'; return this.path; },
		moveTo:function(x, y){ this.set(x, y); this.path += ' m '+x+', '+y; },
		lineTo:function(x, y){ this.set(x, y); this.path += ' l '+x+', '+y; },
		set:function(x, y) { this.x = x; this.y = y; },
		stroke:function(){ return this.path; },
		quadraticCurveTo:function(cx, cy, x, y){ 
			this.path += ' c '+this.x+','+this.y+','+cx+', '+cy+', '+x+', '+y;
			this.set(x, y);
		}
	};

	/**
	 * Simple googlemap (v3)
	 * 
	 */
	NS.Googlemap = function(node, settings) {
		this.settings = $.extend({}, NS.Googlemap.Defaults, settings);
		this.root = node;
		this.init();
	};

	NS.Googlemap.prototype = {
		init:function() {
			this.nodes = this.findNodes();
			this.markers = this.createMarkers(this.nodes);
			this.map = this.createMap(this.markers);
			this.showAll();
		},

		/**
		 * getters
		 */
		getMap:function() { return this.map; },
		getProperty:function(name) { return this.settings[name]; },
		getNodes:function() { return this.nodes; },
		getMarkers:function() { return this.markers; },
		
		/**
		 * finders
		 */
		findNodes:function() {
			return $(this.settings.data);
		},
		
		findBounds:function(markers) {
			var bounds = new google.maps.LatLngBounds();
			var l = markers.length;
			for(var i=0; i<l; i++) {
				bounds.extend(markers[i].getPosition());
			}

			return bounds;
		},

		findPoint:function(node) {
			var settings = this.settings;
			var parser = settings.parseNode;
			if(parser) {
				return parser(node);
			} 

			var $node = $(node);
			return new google.maps.LatLng(
				$node.find(settings.lat).text(),
				$node.find(settings.lng).text()
			); 
		},
		
		/**
		 * creators 
		 */
		createMarkers:function(nodes) {
			var Marker = google.maps.Marker;
			var markers = [];
			var l = nodes.length;

			for(var i=0; i<l; i++) {
				var node = nodes[i];
				var point = this.findPoint(node);
				
				if(!point) {
					continue;
				}
				
				var marker = new Marker({
					position: point,
					icon: this.settings.icon
				});
				
				marker.set('related-node', node);
				$(node).data('related-marker', marker);
				markers.push(marker);
			}

			return markers;
		},
		
		createMap:function(markers) {
			var map = new google.maps.Map(this.root, {
				zoom: 8,
				center: markers[0].getPosition(),
				mapTypeId: google.maps.MapTypeId.ROADMAP
			});

			var l = markers.length;
			for(var i=0; i<l; i++) {
				markers[i].setMap(map);
			}

			return map;
		},

		showAll:function() {
			var map = this.getMap();
			google.maps.event.trigger(map, 'resize');

			var markers = this.getMarkers();
			var bounds = this.findBounds(markers);
			
			map.fitBounds(bounds);
		}
	};

	NS.Googlemap.Defaults = {
		data: '.geo',
		lat: '.latitude',
		lng: '.longitude',
		icon: null,
		parseNode: null,
		markerClass: null
	};

});
