/**
 * 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.table.find('tbody').remove();
			this.body = $('<tbody />');
			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()
			);
		},

		getMarkerIcon:function(node, index) {
			var settings = this.settings;
			var markerIcon = settings.getMarkerIcon;
			if(markerIcon) {
				return markerIcon(node, index);
			}
			return settings.icon;
		},

		/**
		 * 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 icon = this.getMarkerIcon(node, i);

				var marker = new Marker({
					position: point,
					icon: 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,
		getMarkerIcon: null
	};

	/**
	 * Slider
	 *
	 */

	NS.Slider = function(node, settings) {
		this.settings = $.extend({}, NS.Slider.Defaults, settings);
		this.document = $(document);
		this.node = $(node);

		this.initialize();
	};

	NS.Slider.prototype = {
		initialize: function() {
			this.offsetx = this.node.offset().left;
			this.node.hide();

			this.create();
			this.populate();
			this.insert();

			this.snap(this.node[0].selectedIndex);
		},

		create: function() {
			this.slider = $(this.settings.template);
			this.slider.width(this.settings.width);
			this.slider.click(this.updateTracker.bind(this));

			this.fill = $(this.settings.fill);
			this.fill.appendTo(this.slider);

			this.values = $(this.settings.values);
			this.values.appendTo(this.slider);

			this.tracker = $(this.settings.tracker);
			this.tracker.mousedown(this.onTrackerDown.bind(this));
			this.tracker.appendTo(this.slider);
		},

		populate: function() {
			var options = this.node.find('option'),
				i, l = options.length, value, snap;

			this.values.width(this.settings.width / (l - 1) * l);
			this.values.css('left', -1 * this.settings.width / (l - 1) * l / l / 2);

			this.snaps = [];

			for (i = 0; i < l; i++) {
				value = this.parseOption(options[i]);
				value.width(this.settings.width / (l - 1));
				this.values.append(value);
				var snap = (i === 0) ? [0, this.settings.width / (l - 1) / 2] : [i * this.settings.width / (l - 1) - this.settings.width / (l - 1) / 2, i * this.settings.width / (l - 1) + this.settings.width / (l - 1) / 2];
				this.snaps.push(snap);
			}
		},

		parseOption: function(option) {
			var $option = $(option),
				value = $('<li></li>');

			value.text($option.text());

			return value;
		},

		insert: function() {
			this.slider.insertAfter(this.node);
		},

		onSliderClick: function(e) {
			this.updateTracker(e);
		},

		onTrackerDown: function(e) {
			e.preventDefault();
			this.document.bind('mousemove', this.onTrackerMove.bind(this));
			this.document.bind('mouseup', this.onTrackerUp.bind(this));
		},

		onTrackerMove: function(e) {
			e.preventDefault();
			this.updateTracker(e);
		},

		onTrackerUp: function(e) {
			this.document.unbind('mousemove');
			this.document.unbind('mouseup');
		},

		updateTracker: function(e) {
			var cursorx = e.pageX - this.offsetx;
			var x = (cursorx < 0) ? 0 : (cursorx > this.settings.width) ? this.settings.width : cursorx,
				i, l = this.snaps.length, snap;

			if (x <= 0) {
				this.snap(0);
				return;
			}

			if (x >= this.settings.width) {
				this.snap(this.snaps.length - 1);
				return;
			}

			for (i = 0; i < l; i++) {
				snap = this.snaps[i];
				if (snap[0] < x && x < snap[1]) {
					this.snap(i);
					return;
				}
			}

			//this.slide(x);
		},

		slide: function(x) {
			this.tracker.css('left', x - 5);
			this.fill.css('width', x);
		},

		snap: function(index) {
			var x = index * this.settings.width / (this.snaps.length - 1);
			this.tracker.css('left', x - 5);
			this.node[0].selectedIndex = index;
		}
	};

	NS.Slider.Defaults = {
		template: '<div class="nsslider"><div class="path-left"></div><div class="path-right"></div></div>',
		tracker: '<div class="tracker"></div>',
		fill: '<div class="fill"></div>',
		values: '<ul class="values"></ul>',
		width: 300
	};

});
