
/*
 Tooltipster 3.1.4 | 2014-03-20
 A rockin' custom tooltip jQuery plugin
 
 Developed by Caleb Jacob under the MIT license http://opensource.org/licenses/MIT
 
 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
 */
/*

Tooltipster 3.2.4 | 2014-05-14
A rockin' custom tooltip jQuery plugin

Developed by Caleb Jacob under the MIT license http://opensource.org/licenses/MIT

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

*/
;(function ($, window, document) {

	var pluginName = "tooltipster",
		defaults = {
			animation: 'fade',
			arrow: true,
			arrowColor: '',
			autoClose: true,
			content: null,
			contentAsHTML: false,
			contentCloning: true,
			delay: 200,
			minWidth: 0,
			maxWidth: null,
			functionInit: function(origin, content) {},
			functionBefore: function(origin, continueTooltip) {
				continueTooltip();
			},
			functionReady: function(origin, tooltip) {},
			functionAfter: function(origin) {},
			icon: '(?)',
			iconCloning: true,
			iconDesktop: false,
			iconTouch: false,
			iconTheme: 'tooltipster-icon',
			interactive: false,
			interactiveTolerance: 350,
			multiple: false,
			offsetX: 0,
			offsetY: 0,
			onlyOne: false,
			position: 'top',
			positionTracker: false,
			speed: 350,
			timer: 0,
			theme: 'tooltipster-default',
			touchDevices: true,
			trigger: 'hover',
			updateAnimation: true
		};
	
	function Plugin(element, options) {
		
		// list of instance variables
		
		this.bodyOverflowX;
		// stack of custom callbacks provided as parameters to API methods
		this.callbacks = {
			hide: [],
			show: []
		};
		this.checkInterval = null;
		// this will be the user content shown in the tooltip. A capital "C" is used because there is also a method called content()
		this.Content;
		// this is the original element which is being applied the tooltipster plugin
		this.$el = $(element);
		// this will be the element which triggers the appearance of the tooltip on hover/click/custom events.
		// it will be the same as this.$el if icons are not used (see in the options), otherwise it will correspond to the created icon
		this.$elProxy;
		this.elProxyPosition;
		this.enabled = true;
		this.options = $.extend({}, defaults, options);
		this.mouseIsOverProxy = false;
		// a unique namespace per instance, for easy selective unbinding
		this.namespace = 'tooltipster-'+ Math.round(Math.random()*100000);
		// Status (capital S) can be either : appearing, shown, disappearing, hidden
		this.Status = 'hidden';
		this.timerHide = null;
		this.timerShow = null;
		// this will be the tooltip element (jQuery wrapped HTML element)
		this.$tooltip;
		
		// for backward compatibility
		this.options.iconTheme = this.options.iconTheme.replace('.', '');
		this.options.theme = this.options.theme.replace('.', '');
		
		// launch
		
		this._init();
	}
	
	Plugin.prototype = {
		
		_init: function() {
			
			var self = this;
			
			// disable the plugin on old browsers (including IE7 and lower)
			if (document.querySelector) {
				
				// note : the content is null (empty) by default and can stay that way if the plugin remains initialized but not fed any content. The tooltip will just not appear.
				
				// if content is provided in the options, its has precedence over the title attribute. Remark : an empty string is considered content, only 'null' represents the absence of content.
				if (self.options.content !== null){
					self._content_set(self.options.content);
				}
				else {
					// the same remark as above applies : empty strings (like title="") are considered content and will be shown. Do not define any attribute at all if you want to initialize the plugin without content at start.
					var t = self.$el.attr('title');
					if(typeof t === 'undefined') t = null;
					
					self._content_set(t);
				}
				
				var c = self.options.functionInit.call(self.$el, self.$el, self.Content);
				if(typeof c !== 'undefined') self._content_set(c);
				
				self.$el
					// strip the title off of the element to prevent the default tooltips from popping up
					.removeAttr('title')
					// to be able to find all instances on the page later (upon window events in particular)
					.addClass('tooltipstered');

				// detect if we're changing the tooltip origin to an icon
				// note about this condition : if the device has touch capability and self.options.iconTouch is false, you'll have no icons event though you may consider your device as a desktop if it also has a mouse. Not sure why someone would have this use case though.
				if ((!deviceHasTouchCapability && self.options.iconDesktop) || (deviceHasTouchCapability && self.options.iconTouch)) {
					
					// TODO : the tooltip should be automatically be given an absolute position to be near the origin. Otherwise, when the origin is floating or what, it's going to be nowhere near it and disturb the position flow of the page elements. It will imply that the icon also detects when its origin moves, to follow it : not trivial.
					// Until it's done, the icon feature does not really make sense since the user still has most of the work to do by himself
					
					// if the icon provided is in the form of a string
					if(typeof self.options.icon === 'string'){
						// wrap it in a span with the icon class
						self.$elProxy = $('<span class="'+ self.options.iconTheme +'"></span>');
						self.$elProxy.text(self.options.icon);
					}
					// if it is an object (sensible choice)
					else {
						// (deep) clone the object if iconCloning == true, to make sure every instance has its own proxy. We use the icon without wrapping, no need to. We do not give it a class either, as the user will undoubtedly style the object on his own and since our css properties may conflict with his own
						if (self.options.iconCloning) self.$elProxy = self.options.icon.clone(true);
						else self.$elProxy = self.options.icon;
					}
					
					self.$elProxy.insertAfter(self.$el);
				}
				else {
					self.$elProxy = self.$el;
				}
				
				// for 'click' and 'hover' triggers : bind on events to open the tooltip. Closing is now handled in _showNow() because of its bindings.
				// Notes about touch events :
					// - mouseenter, mouseleave and clicks happen even on pure touch devices because they are emulated. deviceIsPureTouch() is a simple attempt to detect them.
					// - on hybrid devices, we do not prevent touch gesture from opening tooltips. It would be too complex to differentiate real mouse events from emulated ones.
					// - we check deviceIsPureTouch() at each event rather than prior to binding because the situation may change during browsing
				if (self.options.trigger == 'hover') {
					
					// these binding are for mouse interaction only
					self.$elProxy
						.on('mouseenter.'+ self.namespace, function() {
							if (!deviceIsPureTouch() || self.options.touchDevices) {
								self.mouseIsOverProxy = true;
								self._show();
							}
						})
						.on('mouseleave.'+ self.namespace, function() {
							if (!deviceIsPureTouch() || self.options.touchDevices) {
								self.mouseIsOverProxy = false;
							}
						});
					
					// for touch interaction only
					if (deviceHasTouchCapability && self.options.touchDevices) {
						
						// for touch devices, we immediately display the tooltip because we cannot rely on mouseleave to handle the delay
						self.$elProxy.on('touchstart.'+ self.namespace, function() {
							self._showNow();
						});
					}
				}
				else if (self.options.trigger == 'click') {
					
					// note : for touch devices, we do not bind on touchstart, we only rely on the emulated clicks (triggered by taps)
					self.$elProxy.on('click.'+ self.namespace, function() {
						if (!deviceIsPureTouch() || self.options.touchDevices) {
							self._show();
						}
					});
				}
			}
		},
		
		// this function will schedule the opening of the tooltip after the delay, if there is one
		_show: function() {
			
			var self = this;
			
			if (self.Status != 'shown' && self.Status != 'appearing') {
				
				if (self.options.delay) {
					self.timerShow = setTimeout(function(){
						
						// for hover trigger, we check if the mouse is still over the proxy, otherwise we do not show anything
						if (self.options.trigger == 'click' || (self.options.trigger == 'hover' && self.mouseIsOverProxy)) {
							self._showNow();
						}
					}, self.options.delay);
				}
				else self._showNow();
			}
		},
		
		// this function will open the tooltip right away
		_showNow: function(callback) {
			
			var self = this;
			
			// call our constructor custom function before continuing
			self.options.functionBefore.call(self.$el, self.$el, function() {
				
				// continue only if the tooltip is enabled and has any content
				if (self.enabled && self.Content !== null) {
				
					// save the method callback and cancel hide method callbacks
					if (callback) self.callbacks.show.push(callback);
					self.callbacks.hide = [];
					
					//get rid of any appearance timer
					clearTimeout(self.timerShow);
					self.timerShow = null;
					clearTimeout(self.timerHide);
					self.timerHide = null;
					
					// if we only want one tooltip open at a time, close all auto-closing tooltips currently open and not already disappearing
					if (self.options.onlyOne) {
						$('.tooltipstered').not(self.$el).each(function(i,el) {
							
							var $el = $(el),
								nss = $el.data('tooltipster-ns');
							
							// iterate on all tooltips of the element
							$.each(nss, function(i, ns){
								var instance = $el.data(ns),
									// we have to use the public methods here
									s = instance.status(),
									ac = instance.option('autoClose');
								
								if (s !== 'hidden' && s !== 'disappearing' && ac) {
									instance.hide();
								}
							});
						});
					}
					
					var finish = function() {
						self.Status = 'shown';
						
						// trigger any show method custom callbacks and reset them
						$.each(self.callbacks.show, function(i,c) { c.call(self.$el); });
						self.callbacks.show = [];
					};
					
					// if this origin already has its tooltip open
					if (self.Status !== 'hidden') {
						
						// the timer (if any) will start (or restart) right now
						var extraTime = 0;
						
						// if it was disappearing, cancel that
						if (self.Status === 'disappearing') {
							
							self.Status = 'appearing';
							
							if (supportsTransitions()) {
								
								self.$tooltip
									.clearQueue()
									.removeClass('tooltipster-dying')
									.addClass('tooltipster-'+ self.options.animation +'-show');
								
								if (self.options.speed > 0) self.$tooltip.delay(self.options.speed);
								
								self.$tooltip.queue(finish);
							}
							else {
								// in case the tooltip was currently fading out, bring it back to life
								self.$tooltip
									.stop()
									.fadeIn(finish);
							}
						}
						// if the tooltip is already open, we still need to trigger the method custom callback
						else if(self.Status === 'shown') {
							finish();
						}
					}
					// if the tooltip isn't already open, open that sucker up!
					else {
						
						self.Status = 'appearing';
						
						// the timer (if any) will start when the tooltip has fully appeared after its transition
						var extraTime = self.options.speed;
						
						// disable horizontal scrollbar to keep overflowing tooltips from jacking with it and then restore it to its previous value
						self.bodyOverflowX = $('body').css('overflow-x');
						$('body').css('overflow-x', 'hidden');
						
						// get some other settings related to building the tooltip
						var animation = 'tooltipster-' + self.options.animation,
							animationSpeed = '-webkit-transition-duration: '+ self.options.speed +'ms; -webkit-animation-duration: '+ self.options.speed +'ms; -moz-transition-duration: '+ self.options.speed +'ms; -moz-animation-duration: '+ self.options.speed +'ms; -o-transition-duration: '+ self.options.speed +'ms; -o-animation-duration: '+ self.options.speed +'ms; -ms-transition-duration: '+ self.options.speed +'ms; -ms-animation-duration: '+ self.options.speed +'ms; transition-duration: '+ self.options.speed +'ms; animation-duration: '+ self.options.speed +'ms;',
							minWidth = self.options.minWidth ? 'min-width:'+ Math.round(self.options.minWidth) +'px;' : '',
							maxWidth = self.options.maxWidth ? 'max-width:'+ Math.round(self.options.maxWidth) +'px;' : '',
							pointerEvents = self.options.interactive ? 'pointer-events: auto;' : '';
						
						// build the base of our tooltip
						self.$tooltip = $('<div class="tooltipster-base '+ self.options.theme +'" style="'+ minWidth +' '+ maxWidth +' '+ pointerEvents +' '+ animationSpeed +'"><div class="tooltipster-content"></div></div>');
						
						// only add the animation class if the user has a browser that supports animations
						if (supportsTransitions()) self.$tooltip.addClass(animation);
						
						// insert the content
						self._content_insert();
						
						// attach
						self.$tooltip.appendTo('body');
						
						// do all the crazy calculations and positioning
						self.reposition();
						
						// call our custom callback since the content of the tooltip is now part of the DOM
						self.options.functionReady.call(self.$el, self.$el, self.$tooltip);
						
						// animate in the tooltip
						if (supportsTransitions()) {
							
							self.$tooltip.addClass(animation + '-show');
							
							if(self.options.speed > 0) self.$tooltip.delay(self.options.speed);
							
							self.$tooltip.queue(finish);
						}
						else {
							self.$tooltip.css('display', 'none').fadeIn(self.options.speed, finish);
						}
						
						// will check if our tooltip origin is removed while the tooltip is shown
						self._interval_set();
						
						// reposition on scroll (otherwise position:fixed element's tooltips will move away form their origin) and on resize (in case position can/has to be changed)
						$(window).on('scroll.'+ self.namespace +' resize.'+ self.namespace, function() {
							self.reposition();
						});
						
						// auto-close bindings
						if (self.options.autoClose) {
							
							// in case a listener is already bound for autoclosing (mouse or touch, hover or click), unbind it first
							$('body').off('.'+ self.namespace);
							
							// here we'll have to set different sets of bindings for both touch and mouse
							if (self.options.trigger == 'hover') {
								
								// if the user touches the body, hide
								if (deviceHasTouchCapability) {
									// timeout 0 : explanation below in click section
									setTimeout(function() {
										// we don't want to bind on click here because the initial touchstart event has not yet triggered its click event, which is thus about to happen
										$('body').on('touchstart.'+ self.namespace, function() {
											self.hide();
										});
									}, 0);
								}
								
								// if we have to allow interaction
								if (self.options.interactive) {
									
									// touch events inside the tooltip must not close it
									if (deviceHasTouchCapability) {
										self.$tooltip.on('touchstart.'+ self.namespace, function(event) {
											event.stopPropagation();
										});
									}
									
									// as for mouse interaction, we get rid of the tooltip only after the mouse has spent some time out of it
									var tolerance = null;
									
									self.$elProxy.add(self.$tooltip)
										// hide after some time out of the proxy and the tooltip
										.on('mouseleave.'+ self.namespace + '-autoClose', function() {
											clearTimeout(tolerance);
											tolerance = setTimeout(function(){
												self.hide();
											}, self.options.interactiveTolerance);
										})
										// suspend timeout when the mouse is over the proxy or the tooltip
										.on('mouseenter.'+ self.namespace + '-autoClose', function() {
											clearTimeout(tolerance);
										});
								}
								// if this is a non-interactive tooltip, get rid of it if the mouse leaves
								else {
									self.$elProxy.on('mouseleave.'+ self.namespace + '-autoClose', function() {
										self.hide();
									});
								}
							}
							// here we'll set the same bindings for both clicks and touch on the body to hide the tooltip
							else if(self.options.trigger == 'click'){
								
								// use a timeout to prevent immediate closing if the method was called on a click event and if options.delay == 0 (because of bubbling)
								setTimeout(function() {
									$('body').on('click.'+ self.namespace +' touchstart.'+ self.namespace, function() {
										self.hide();
									});
								}, 0);
								
								// if interactive, we'll stop the events that were emitted from inside the tooltip to stop autoClosing
								if (self.options.interactive) {
									
									// note : the touch events will just not be used if the plugin is not enabled on touch devices
									self.$tooltip.on('click.'+ self.namespace +' touchstart.'+ self.namespace, function(event) {
										event.stopPropagation();
									});
								}
							}
						}
					}
					
					// if we have a timer set, let the countdown begin
					if (self.options.timer > 0) {
						
						self.timerHide = setTimeout(function() {
							self.timerHide = null;
							self.hide();
						}, self.options.timer + extraTime);
					}
				}
			});
		},
		
		_interval_set: function() {
			
			var self = this;
			
			self.checkInterval = setInterval(function() {
				
				// if the tooltip and/or its interval should be stopped
				if (
						// if the origin has been removed
						$('body').find(self.$el).length === 0
						// if the elProxy has been removed
					||	$('body').find(self.$elProxy).length === 0
						// if the tooltip has been closed
					||	self.Status == 'hidden'
						// if the tooltip has somehow been removed
					||	$('body').find(self.$tooltip).length === 0
				) {
					// remove the tooltip if it's still here
					if (self.Status == 'shown' || self.Status == 'appearing') self.hide();
					
					// clear this interval as it is no longer necessary
					self._interval_cancel();
				}
				// if everything is alright
				else {
					// compare the former and current positions of the elProxy to reposition the tooltip if need be
					if(self.options.positionTracker){
						
						var p = self._repositionInfo(self.$elProxy),
							identical = false;
						
						// compare size first (a change requires repositioning too)
						if(areEqual(p.dimension, self.elProxyPosition.dimension)){
							
							// for elements with a fixed position, we track the top and left properties (relative to window)
							if(self.$elProxy.css('position') === 'fixed'){
								if(areEqual(p.position, self.elProxyPosition.position)) identical = true;
							}
							// otherwise, track total offset (relative to document)
							else {
								if(areEqual(p.offset, self.elProxyPosition.offset)) identical = true;
							}
						}
						
						if(!identical){
							self.reposition();
						}
					}
				}
			}, 200);
		},
		
		_interval_cancel: function() {
			clearInterval(this.checkInterval);
			// clean delete
			this.checkInterval = null;
		},
		
		_content_set: function(content) {
			// clone if asked. Cloning the object makes sure that each instance has its own version of the content (in case a same object were provided for several instances)
			// reminder : typeof null === object
			if (typeof content === 'object' && content !== null && this.options.contentCloning) {
				content = content.clone(true);
			}
			this.Content = content;
		},
		
		_content_insert: function() {
			
			var self = this,
				$d = this.$tooltip.find('.tooltipster-content');
			
			if (typeof self.Content === 'string' && !self.options.contentAsHTML) {
				$d.text(self.Content);
			}
			else {
				$d
					.empty()
					.append(self.Content);
			}
		},
		
		_update: function(content) {
			
			var self = this;
			
			// change the content
			self._content_set(content);
			
			if (self.Content !== null) {
				
				// update the tooltip if it is open
				if (self.Status !== 'hidden') {
					
					// reset the content in the tooltip
					self._content_insert();
					
					// reposition and resize the tooltip
					self.reposition();
					
					// if we want to play a little animation showing the content changed
					if (self.options.updateAnimation) {
						
						if (supportsTransitions()) {
							
							self.$tooltip.css({
								'width': '',
								'-webkit-transition': 'all ' + self.options.speed + 'ms, width 0ms, height 0ms, left 0ms, top 0ms',
								'-moz-transition': 'all ' + self.options.speed + 'ms, width 0ms, height 0ms, left 0ms, top 0ms',
								'-o-transition': 'all ' + self.options.speed + 'ms, width 0ms, height 0ms, left 0ms, top 0ms',
								'-ms-transition': 'all ' + self.options.speed + 'ms, width 0ms, height 0ms, left 0ms, top 0ms',
								'transition': 'all ' + self.options.speed + 'ms, width 0ms, height 0ms, left 0ms, top 0ms'
							}).addClass('tooltipster-content-changing');
							
							// reset the CSS transitions and finish the change animation
							setTimeout(function() {
								
								if(self.Status != 'hidden'){
									
									self.$tooltip.removeClass('tooltipster-content-changing');
									
									// after the changing animation has completed, reset the CSS transitions
									setTimeout(function() {
										
										if(self.Status !== 'hidden'){
											self.$tooltip.css({
												'-webkit-transition': self.options.speed + 'ms',
												'-moz-transition': self.options.speed + 'ms',
												'-o-transition': self.options.speed + 'ms',
												'-ms-transition': self.options.speed + 'ms',
												'transition': self.options.speed + 'ms'
											});
										}
									}, self.options.speed);
								}
							}, self.options.speed);
						}
						else {
							self.$tooltip.fadeTo(self.options.speed, 0.5, function() {
								if(self.Status != 'hidden'){
									self.$tooltip.fadeTo(self.options.speed, 1);
								}
							});
						}
					}
				}
			}
			else {
				self.hide();
			}
		},
		
		_repositionInfo: function($el) {
			return {
				dimension: {
					height: $el.outerHeight(false),
					width: $el.outerWidth(false)
				},
				offset: $el.offset(),
				position: {
					left: parseInt($el.css('left')),
					top: parseInt($el.css('top'))
				}
			};
		},
		
		hide: function(callback) {
			
			var self = this;
			
			// save the method custom callback and cancel any show method custom callbacks
			if (callback) self.callbacks.hide.push(callback);
			self.callbacks.show = [];
			
			// get rid of any appearance timeout
			clearTimeout(self.timerShow);
			self.timerShow = null;
			clearTimeout(self.timerHide);
			self.timerHide = null;
			
			var finishCallbacks = function() {
				// trigger any hide method custom callbacks and reset them
				$.each(self.callbacks.hide, function(i,c) { c.call(self.$el); });
				self.callbacks.hide = [];
			};
			
			// hide
			if (self.Status == 'shown' || self.Status == 'appearing') {
				
				self.Status = 'disappearing';
				
				var finish = function() {
					
					self.Status = 'hidden';
					
					// detach our content object first, so the next jQuery's remove() call does not unbind its event handlers
					if (typeof self.Content == 'object' && self.Content !== null) {
						self.Content.detach();
					}
					
					self.$tooltip.remove();
					self.$tooltip = null;
					
					// unbind orientationchange, scroll and resize listeners
					$(window).off('.'+ self.namespace);
					
					$('body')
						// unbind any auto-closing click/touch listeners
						.off('.'+ self.namespace)
						.css('overflow-x', self.bodyOverflowX);
					
					// unbind any auto-closing click/touch listeners
					$('body').off('.'+ self.namespace);
					
					// unbind any auto-closing hover listeners
					self.$elProxy.off('.'+ self.namespace + '-autoClose');
					
					// call our constructor custom callback function
					self.options.functionAfter.call(self.$el, self.$el);
					
					// call our method custom callbacks functions
					finishCallbacks();
				};
				
				if (supportsTransitions()) {
					
					self.$tooltip
						.clearQueue()
						.removeClass('tooltipster-' + self.options.animation + '-show')
						// for transitions only
						.addClass('tooltipster-dying');
					
					if(self.options.speed > 0) self.$tooltip.delay(self.options.speed);
					
					self.$tooltip.queue(finish);
				}
				else {
					self.$tooltip
						.stop()
						.fadeOut(self.options.speed, finish);
				}
			}
			// if the tooltip is already hidden, we still need to trigger the method custom callback
			else if(self.Status == 'hidden') {
				finishCallbacks();
			}
			
			return self;
		},
		
		// the public show() method is actually an alias for the private showNow() method
		show: function(callback) {
			this._showNow(callback);
			return this;
		},
		
		// 'update' is deprecated in favor of 'content' but is kept for backward compatibility
		update: function(c) {
			return this.content(c);
		},
		content: function(c) {
			// getter method
			if(typeof c === 'undefined'){
				return this.Content;
			}
			// setter method
			else {
				this._update(c);
				return this;
			}
		},
		
		reposition: function() {
			
			var self = this;
			
			// in case the tooltip has been removed from DOM manually
			if ($('body').find(self.$tooltip).length !== 0) {
				
				// reset width
				self.$tooltip.css('width', '');
				
				// find variables to determine placement
				self.elProxyPosition = self._repositionInfo(self.$elProxy);
				var arrowReposition = null,
					windowWidth = $(window).width(),
					// shorthand
					proxy = self.elProxyPosition,
					tooltipWidth = self.$tooltip.outerWidth(false),
					tooltipInnerWidth = self.$tooltip.innerWidth() + 1, // this +1 stops FireFox from sometimes forcing an additional text line
					tooltipHeight = self.$tooltip.outerHeight(false);
				
				// if this is an <area> tag inside a <map>, all hell breaks loose. Recalculate all the measurements based on coordinates
				if (self.$elProxy.is('area')) {
					var areaShape = self.$elProxy.attr('shape'),
						mapName = self.$elProxy.parent().attr('name'),
						map = $('img[usemap="#'+ mapName +'"]'),
						mapOffsetLeft = map.offset().left,
						mapOffsetTop = map.offset().top,
						areaMeasurements = self.$elProxy.attr('coords') !== undefined ? self.$elProxy.attr('coords').split(',') : undefined;
					
					if (areaShape == 'circle') {
						var areaLeft = parseInt(areaMeasurements[0]),
							areaTop = parseInt(areaMeasurements[1]),
							areaWidth = parseInt(areaMeasurements[2]);
						proxy.dimension.height = areaWidth * 2;
						proxy.dimension.width = areaWidth * 2;
						proxy.offset.top = mapOffsetTop + areaTop - areaWidth;
						proxy.offset.left = mapOffsetLeft + areaLeft - areaWidth;
					}
					else if (areaShape == 'rect') {
						var areaLeft = parseInt(areaMeasurements[0]),
							areaTop = parseInt(areaMeasurements[1]),
							areaRight = parseInt(areaMeasurements[2]),
							areaBottom = parseInt(areaMeasurements[3]);
						proxy.dimension.height = areaBottom - areaTop;
						proxy.dimension.width = areaRight - areaLeft;
						proxy.offset.top = mapOffsetTop + areaTop;
						proxy.offset.left = mapOffsetLeft + areaLeft;
					}
					else if (areaShape == 'poly') {
						var areaXs = [],
							areaYs = [],
							areaSmallestX = 0,
							areaSmallestY = 0,
							areaGreatestX = 0,
							areaGreatestY = 0,
							arrayAlternate = 'even';
						
						for (var i = 0; i < areaMeasurements.length; i++) {
							var areaNumber = parseInt(areaMeasurements[i]);
							
							if (arrayAlternate == 'even') {
								if (areaNumber > areaGreatestX) {
									areaGreatestX = areaNumber;
									if (i === 0) {
										areaSmallestX = areaGreatestX;
									}
								}
								
								if (areaNumber < areaSmallestX) {
									areaSmallestX = areaNumber;
								}
								
								arrayAlternate = 'odd';
							}
							else {
								if (areaNumber > areaGreatestY) {
									areaGreatestY = areaNumber;
									if (i == 1) {
										areaSmallestY = areaGreatestY;
									}
								}
								
								if (areaNumber < areaSmallestY) {
									areaSmallestY = areaNumber;
								}
								
								arrayAlternate = 'even';
							}
						}
					
						proxy.dimension.height = areaGreatestY - areaSmallestY;
						proxy.dimension.width = areaGreatestX - areaSmallestX;
						proxy.offset.top = mapOffsetTop + areaSmallestY;
						proxy.offset.left = mapOffsetLeft + areaSmallestX;
					}
					else {
						proxy.dimension.height = map.outerHeight(false);
						proxy.dimension.width = map.outerWidth(false);
						proxy.offset.top = mapOffsetTop;
						proxy.offset.left = mapOffsetLeft;
					}
				}
				
				// our function and global vars for positioning our tooltip
				var myLeft = 0,
					myLeftMirror = 0,
					myTop = 0,
					offsetY = parseInt(self.options.offsetY),
					offsetX = parseInt(self.options.offsetX),
					// this is the arrow position that will eventually be used. It may differ from the position option if the tooltip cannot be displayed in this position
					practicalPosition = self.options.position;
				
				// a function to detect if the tooltip is going off the screen horizontally. If so, reposition the crap out of it!
				function dontGoOffScreenX() {
				
					var windowLeft = $(window).scrollLeft();
					
					// if the tooltip goes off the left side of the screen, line it up with the left side of the window
					if((myLeft - windowLeft) < 0) {
						arrowReposition = myLeft - windowLeft;
						myLeft = windowLeft;
					}
					
					// if the tooltip goes off the right of the screen, line it up with the right side of the window
					if (((myLeft + tooltipWidth) - windowLeft) > windowWidth) {
						arrowReposition = myLeft - ((windowWidth + windowLeft) - tooltipWidth);
						myLeft = (windowWidth + windowLeft) - tooltipWidth;
					}
				}
				
				// a function to detect if the tooltip is going off the screen vertically. If so, switch to the opposite!
				function dontGoOffScreenY(switchTo, switchFrom) {
					// if it goes off the top off the page
					if(((proxy.offset.top - $(window).scrollTop() - tooltipHeight - offsetY - 12) < 0) && (switchFrom.indexOf('top') > -1)) {
						practicalPosition = switchTo;
					}
					
					// if it goes off the bottom of the page
					if (((proxy.offset.top + proxy.dimension.height + tooltipHeight + 12 + offsetY) > ($(window).scrollTop() + $(window).height())) && (switchFrom.indexOf('bottom') > -1)) {
						practicalPosition = switchTo;
						myTop = (proxy.offset.top - tooltipHeight) - offsetY - 12;
					}
				}
				
				if(practicalPosition == 'top') {
					var leftDifference = (proxy.offset.left + tooltipWidth) - (proxy.offset.left + proxy.dimension.width);
					myLeft = (proxy.offset.left + offsetX) - (leftDifference / 2);
					myTop = (proxy.offset.top - tooltipHeight) - offsetY - 12;
					dontGoOffScreenX();
					dontGoOffScreenY('bottom', 'top');
				}
				
				if(practicalPosition == 'top-left') {
					myLeft = proxy.offset.left + offsetX;
					myTop = (proxy.offset.top - tooltipHeight) - offsetY - 12;
					dontGoOffScreenX();
					dontGoOffScreenY('bottom-left', 'top-left');
				}
				
				if(practicalPosition == 'top-right') {
					myLeft = (proxy.offset.left + proxy.dimension.width + offsetX) - tooltipWidth;
					myTop = (proxy.offset.top - tooltipHeight) - offsetY - 12;
					dontGoOffScreenX();
					dontGoOffScreenY('bottom-right', 'top-right');
				}
				
				if(practicalPosition == 'bottom') {
					var leftDifference = (proxy.offset.left + tooltipWidth) - (proxy.offset.left + proxy.dimension.width);
					myLeft = proxy.offset.left - (leftDifference / 2) + offsetX;
					myTop = (proxy.offset.top + proxy.dimension.height) + offsetY + 12;
					dontGoOffScreenX();
					dontGoOffScreenY('top', 'bottom');
				}
				
				if(practicalPosition == 'bottom-left') {
					myLeft = proxy.offset.left + offsetX;
					myTop = (proxy.offset.top + proxy.dimension.height) + offsetY + 12;
					dontGoOffScreenX();
					dontGoOffScreenY('top-left', 'bottom-left');
				}
				
				if(practicalPosition == 'bottom-right') {
					myLeft = (proxy.offset.left + proxy.dimension.width + offsetX) - tooltipWidth;
					myTop = (proxy.offset.top + proxy.dimension.height) + offsetY + 12;
					dontGoOffScreenX();
					dontGoOffScreenY('top-right', 'bottom-right');
				}
				
				if(practicalPosition == 'left') {
					myLeft = proxy.offset.left - offsetX - tooltipWidth - 12;
					myLeftMirror = proxy.offset.left + offsetX + proxy.dimension.width + 12;
					var topDifference = (proxy.offset.top + tooltipHeight) - (proxy.offset.top + proxy.dimension.height);
					myTop = proxy.offset.top - (topDifference / 2) - offsetY;
					
					// if the tooltip goes off boths sides of the page
					if((myLeft < 0) && ((myLeftMirror + tooltipWidth) > windowWidth)) {
						var borderWidth = parseFloat(self.$tooltip.css('border-width')) * 2,
							newWidth = (tooltipWidth + myLeft) - borderWidth;
						self.$tooltip.css('width', newWidth + 'px');
						
						tooltipHeight = self.$tooltip.outerHeight(false);
						myLeft = proxy.offset.left - offsetX - newWidth - 12 - borderWidth;
						topDifference = (proxy.offset.top + tooltipHeight) - (proxy.offset.top + proxy.dimension.height);
						myTop = proxy.offset.top - (topDifference / 2) - offsetY;
					}
					
					// if it only goes off one side, flip it to the other side
					else if(myLeft < 0) {
						myLeft = proxy.offset.left + offsetX + proxy.dimension.width + 12;
						arrowReposition = 'left';
					}
				}
				
				if(practicalPosition == 'right') {
					myLeft = proxy.offset.left + offsetX + proxy.dimension.width + 12;
					myLeftMirror = proxy.offset.left - offsetX - tooltipWidth - 12;
					var topDifference = (proxy.offset.top + tooltipHeight) - (proxy.offset.top + proxy.dimension.height);
					myTop = proxy.offset.top - (topDifference / 2) - offsetY;
					
					// if the tooltip goes off boths sides of the page
					if(((myLeft + tooltipWidth) > windowWidth) && (myLeftMirror < 0)) {
						var borderWidth = parseFloat(self.$tooltip.css('border-width')) * 2,
							newWidth = (windowWidth - myLeft) - borderWidth;
						self.$tooltip.css('width', newWidth + 'px');
						
						tooltipHeight = self.$tooltip.outerHeight(false);
						topDifference = (proxy.offset.top + tooltipHeight) - (proxy.offset.top + proxy.dimension.height);
						myTop = proxy.offset.top - (topDifference / 2) - offsetY;
					}
						
					// if it only goes off one side, flip it to the other side
					else if((myLeft + tooltipWidth) > windowWidth) {
						myLeft = proxy.offset.left - offsetX - tooltipWidth - 12;
						arrowReposition = 'right';
					}
				}
				
				// if arrow is set true, style it and append it
				if (self.options.arrow) {
	
					var arrowClass = 'tooltipster-arrow-' + practicalPosition;
					
					// set color of the arrow
					if(self.options.arrowColor.length < 1) {
						var arrowColor = self.$tooltip.css('background-color');
					}
					else {
						var arrowColor = self.options.arrowColor;
					}
					
					// if the tooltip was going off the page and had to re-adjust, we need to update the arrow's position
					if (!arrowReposition) {
						arrowReposition = '';
					}
					else if (arrowReposition == 'left') {
						arrowClass = 'tooltipster-arrow-right';
						arrowReposition = '';
					}
					else if (arrowReposition == 'right') {
						arrowClass = 'tooltipster-arrow-left';
						arrowReposition = '';
					}
					else {
						arrowReposition = 'left:'+ Math.round(arrowReposition) +'px;';
					}
					
					// building the logic to create the border around the arrow of the tooltip
					if ((practicalPosition == 'top') || (practicalPosition == 'top-left') || (practicalPosition == 'top-right')) {
						var tooltipBorderWidth = parseFloat(self.$tooltip.css('border-bottom-width')),
							tooltipBorderColor = self.$tooltip.css('border-bottom-color');
					}
					else if ((practicalPosition == 'bottom') || (practicalPosition == 'bottom-left') || (practicalPosition == 'bottom-right')) {
						var tooltipBorderWidth = parseFloat(self.$tooltip.css('border-top-width')),
							tooltipBorderColor = self.$tooltip.css('border-top-color');
					}
					else if (practicalPosition == 'left') {
						var tooltipBorderWidth = parseFloat(self.$tooltip.css('border-right-width')),
							tooltipBorderColor = self.$tooltip.css('border-right-color');
					}
					else if (practicalPosition == 'right') {
						var tooltipBorderWidth = parseFloat(self.$tooltip.css('border-left-width')),
							tooltipBorderColor = self.$tooltip.css('border-left-color');
					}
					else {
						var tooltipBorderWidth = parseFloat(self.$tooltip.css('border-bottom-width')),
							tooltipBorderColor = self.$tooltip.css('border-bottom-color');
					}
					
					if (tooltipBorderWidth > 1) {
						tooltipBorderWidth++;
					}
					
					var arrowBorder = '';
					if (tooltipBorderWidth !== 0) {
						var arrowBorderSize = '',
							arrowBorderColor = 'border-color: '+ tooltipBorderColor +';';
						if (arrowClass.indexOf('bottom') !== -1) {
							arrowBorderSize = 'margin-top: -'+ Math.round(tooltipBorderWidth) +'px;';
						}
						else if (arrowClass.indexOf('top') !== -1) {
							arrowBorderSize = 'margin-bottom: -'+ Math.round(tooltipBorderWidth) +'px;';
						}
						else if (arrowClass.indexOf('left') !== -1) {
							arrowBorderSize = 'margin-right: -'+ Math.round(tooltipBorderWidth) +'px;';
						}
						else if (arrowClass.indexOf('right') !== -1) {
							arrowBorderSize = 'margin-left: -'+ Math.round(tooltipBorderWidth) +'px;';
						}
						arrowBorder = '<span class="tooltipster-arrow-border" style="'+ arrowBorderSize +' '+ arrowBorderColor +';"></span>';
					}
					
					// if the arrow already exists, remove and replace it
					self.$tooltip.find('.tooltipster-arrow').remove();
					
					// build out the arrow and append it		
					var arrowConstruct = '<div class="'+ arrowClass +' tooltipster-arrow" style="'+ arrowReposition +'">'+ arrowBorder +'<span style="border-color:'+ arrowColor +';"></span></div>';
					self.$tooltip.append(arrowConstruct);
				}
				
				// position the tooltip
				self.$tooltip.css({'top': Math.round(myTop) + 'px', 'left': Math.round(myLeft) + 'px'});
			}
			
			return self;
		},
		
		enable: function() {
			this.enabled = true;
			return this;
		},
		
		disable: function() {
			// hide first, in case the tooltip would not disappear on its own (autoClose false)
			this.hide();
			this.enabled = false;
			return this;
		},
		
		destroy: function() {
			
			var self = this;
			
			self.hide();
			
			// remove the icon, if any
			if(self.$el[0] !== self.$elProxy[0]) self.$elProxy.remove();
			
			self.$el
				.removeData(self.namespace)
				.off('.'+ self.namespace);
			
			var ns = self.$el.data('tooltipster-ns');
			
			// if there are no more tooltips on this element
			if(ns.length === 1){
				
				// old school technique when outerHTML is not supported
				var stringifiedContent = (typeof self.Content === 'string') ? self.Content : $('<div></div>').append(self.Content).html();
				
				self.$el
					.removeClass('tooltipstered')
					.attr('title', stringifiedContent)
					.removeData(self.namespace)
					.removeData('tooltipster-ns')
					.off('.'+ self.namespace);
			}
			else {
				// remove the instance namespace from the list of namespaces of tooltips present on the element
				ns = $.grep(ns, function(el, i){
					return el !== self.namespace;
				});
				self.$el.data('tooltipster-ns', ns);
			}
			
			return self;
		},
		
		elementIcon: function() {
			return (this.$el[0] !== this.$elProxy[0]) ? this.$elProxy[0] : undefined;
		},
		
		elementTooltip: function() {
			return this.$tooltip ? this.$tooltip[0] : undefined;
		},
		
		// public methods but for internal use only
		// getter if val is ommitted, setter otherwise
		option: function(o, val) {
			if (typeof val == 'undefined') return this.options[o];
			else {
				this.options[o] = val;
				return this;
			}
		},
		status: function() {
			return this.Status;
		}
	};
	
	$.fn[pluginName] = function () {
		
		// for using in closures
		var args = arguments;
		
		// if we are not in the context of jQuery wrapped HTML element(s) :
		// this happens when calling static methods in the form $.fn.tooltipster('methodName'), or when calling $(sel).tooltipster('methodName or options') where $(sel) does not match anything
		if (this.length === 0) {
			
			// if the first argument is a method name
			if (typeof args[0] === 'string') {
				
				var methodIsStatic = true;
				
				// list static methods here (usable by calling $.fn.tooltipster('methodName');)
				switch (args[0]) {
					
					case 'setDefaults':
						// change default options for all future instances
						$.extend(defaults, args[1]);
						break;
					
					default:
						methodIsStatic = false;
						break;
				}
				
				// $.fn.tooltipster('methodName') calls will return true
				if (methodIsStatic) return true;
				// $(sel).tooltipster('methodName') calls will return the list of objects event though it's empty because chaining should work on empty lists
				else return this;
			}
			// the first argument is undefined or an object of options : we are initalizing but there is no element matched by selector
			else {
				// still chainable : same as above
				return this;
			}
		}
		// this happens when calling $(sel).tooltipster('methodName or options') where $(sel) matches one or more elements
		else {
			
			// method calls
			if (typeof args[0] === 'string') {
				
				var v = '#*$~&';
				
				this.each(function() {
					
					// retrieve the namepaces of the tooltip(s) that exist on that element. We will interact with the first tooltip only.
					var ns = $(this).data('tooltipster-ns'),
						// self represents the instance of the first tooltipster plugin associated to the current HTML object of the loop
						self = ns ? $(this).data(ns[0]) : null;
					
					// if the current element holds a tooltipster instance
					if(self){
						
						if (typeof self[args[0]] === 'function') {
							// note : args[1] and args[2] may not be defined
							var resp = self[args[0]](args[1], args[2]);
						}
						else {
							throw new Error('Unknown method .tooltipster("' + args[0] + '")');
						}
						
						// if the function returned anything other than the instance itself (which implies chaining)
						if (resp !== self){
							v = resp;
							// return false to stop .each iteration on the first element matched by the selector
							return false;
						}
					}
					else {
						throw new Error('You called Tooltipster\'s "' + args[0] + '" method on an uninitialized element');
					}
				});
				
				return (v !== '#*$~&') ? v : this;
			}
			// first argument is undefined or an object : the tooltip is initializing
			else {
				
				var instances = [],
					// is there a defined value for the multiple option in the options object ?
					multipleIsSet = args[0] && typeof args[0].multiple !== 'undefined',
					// if the multople option is set to true, or if it's not defined but set to true in the defaults
					multiple = (multipleIsSet && args[0].multiple) || (!multipleIsSet && defaults.multiple);
				
				// initialize a tooltipster instance for each element if it doesn't already have one or if the multiple option is set, and attach the object to it
				this.each(function () {
					
					var go = false,
						ns = $(this).data('tooltipster-ns'),
						instance = null;
					
					if (!ns) {
						go = true;
					}
					else {
						if(multiple) go = true;
						else console.log('Tooltipster: one or more tooltips are already attached to this element: ignoring. Use the "multiple" option to attach more tooltips.');
					}
					
					if(go) {
						instance = new Plugin(this, args[0]);
						
						// save the reference of the new instance
						if (!ns) ns = [];
						ns.push(instance.namespace);
						$(this).data('tooltipster-ns', ns)
						
						// save the instance itself
						$(this).data(instance.namespace, instance);
					}
					
					instances.push(instance);
				});
				
				if(multiple) return instances;
				else return this;
			}
		}
	};
	
	// quick & dirty compare function (not bijective nor multidimensional)
	function areEqual(a,b) {
		var same = true;
		$.each(a, function(i, el){
			if(typeof b[i] === 'undefined' || a[i] !== b[i]){
				same = false;
				return false;
			}
		});
		return same;
	}
	
	// detect if this device can trigger touch events
	var deviceHasTouchCapability = !!('ontouchstart' in window);
	
	// we'll assume the device has no mouse until we detect any mouse movement
	var deviceHasMouse = false;
	$('body').one('mousemove', function() {
		deviceHasMouse = true;
	});
	
	function deviceIsPureTouch() {
		return (!deviceHasMouse && deviceHasTouchCapability);
	}
	
	// detecting support for CSS transitions
	function supportsTransitions() {
		var b = document.body || document.documentElement,
			s = b.style,
			p = 'transition';
		
		if(typeof s[p] == 'string') {return true; }

		v = ['Moz', 'Webkit', 'Khtml', 'O', 'ms'],
		p = p.charAt(0).toUpperCase() + p.substr(1);
		for(var i=0; i<v.length; i++) {
			if(typeof s[v[i] + p] == 'string') { return true; }
		}
		return false;
	}
})( jQuery, window, document );


/*
 * Inline Form Validation Engine 2.6.2, jQuery plugin
 *
 * Copyright(c) 2010, Cedric Dugas
 * http://www.position-absolute.com
 *
 * 2.0 Rewrite by Olivier Refalo
 * http://www.crionics.com
 *
 * Form validation engine allowing custom regex rules to be added.
 * Licensed under the MIT License
 */
(function($) {
    
    $.fn.validationEngineLanguage = function() {};

            $.validationEngineLanguage = {
                
                newLang: function() {


                    $.validationEngineLanguage.allRules = {
                        "required": {// Add your regex rules here, you can take telephone as an example
                            "regex": "none",
                            "alertText": "* This field is required",
                            "alertTextCheckboxMultiple": "* Please select an option",
                            "alertTextCheckboxe": "* This checkbox is required",
                            "alertTextDateRange": "* Both date range fields are required"
                        },
                        "dateRange": {
                            "regex": /^(Jan|JAN|Feb|FEB|Mar|MAR|Apr|APR|May|MAY|Jun|JUN|Jul|JUL|Aug|AUG|Sep|SEP|Oct|OCT|Nov|NOV|Dec|DEC)[\s{1}|\/|-](3[0-1]|2[0-9]|1[0-9]|0[1-9])[\s{1}|\/|-]\d{4}$/,
                            "alertText": "* Invalid ",
                            "alertText2": "Date Range"
                        },
                        //Create by tmhao2005
                        "validGroupEmail": {
                            "regrex": "none",
                            "alertText": "* This field contaions an invalid E-mail address or is empty"
                        },
                        "dateTimeRange": {
                            "regex": /^(Jan|JAN|Feb|FEB|Mar|MAR|Apr|APR|May|MAY|Jun|JUN|Jul|JUL|Aug|AUG|Sep|SEP|Oct|OCT|Nov|NOV|Dec|DEC)[\s{1}|\/|-](3[0-1]|2[0-9]|1[0-9]|0[1-9])[\s{1}|\/|-]\d{4}$/,
                            "alertText": "* Invalid ",
                            "alertText2": "Date Time Range"
                        },
                        "minSize": {
                            "regex": "none",
                            "alertText": "* Minimum ",
                            "alertText2": " characters required"
                        },
                        "maxSize": {
                            "regex": "none",
                            "alertText": "* Maximum ",
                            "alertText2": " characters allowed"
                        },
                        "groupRequired": {
                            "regex": "none",
                            "alertText": "* You must fill one of the following fields",
                            "alertTextCheckboxMultiple": "* Please select an option",
                            "alertTextCheckboxe": "* This checkbox is required"
                        },
                        "min": {
                            "regex": "none",
                            "alertText": "* Minimum value is "

                        },
                        "max": {
                            "regex": "none",
                            "alertText": "* Maximum value is "
                        },
                        "past": {
                            "regex": /^(Jan|JAN|Feb|FEB|Mar|MAR|Apr|APR|May|MAY|Jun|JUN|Jul|JUL|Aug|AUG|Sep|SEP|Oct|OCT|Nov|NOV|Dec|DEC)[\s{1}|\/|-](3[0-1]|2[0-9]|1[0-9]|0[1-9])[\s{1}|\/|-]\d{4}$/,
                            "alertText": "* Date prior to "
                        },
                        "future": {
                            "regex": /^(Jan|JAN|Feb|FEB|Mar|MAR|Apr|APR|May|MAY|Jun|JUN|Jul|JUL|Aug|AUG|Sep|SEP|Oct|OCT|Nov|NOV|Dec|DEC)[\s{1}|\/|-](3[0-1]|2[0-9]|1[0-9]|0[1-9])[\s{1}|\/|-]\d{4}$/,
                            "alertText": "* Date past "
                        },
                        "maxCheckbox": {
                            "regex": "none",
                            "alertText": "* Maximum ",
                            "alertText2": " options allowed"
                        },
                        "minCheckbox": {
                            "regex": "none",
                            "alertText": "* Please select ",
                            "alertText2": " options"
                        },
                        "equals": {
                            "regex": "none",
                            "alertText": "* Fields do not match"
                        },
                        "phone": {
                            // credit: jquery.h5validate.js / orefalo
                            "regex": /^([\+][0-9]{1,3}[ \.\-])?([\(]{1}[0-9]{2,6}[\)])?([0-9 \.\-\/]{3,20})((x|ext|extension)[ ]?[0-9]{1,4})?$/,
                            "alertText": "* Invalid phone number"
                        },
                        "email": {
                            // Shamelessly lifted from Scott Gonzalez via the Bassistance Validation plugin http://projects.scottsplayground.com/email_address_validation/
                            //                    "regex": /^((([a-z]|\d|[a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))*@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i,
                            
                            "regex": /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/,
                            "alertText": "* Invalid email address"
                        },
                        "integer": {
                            "regex": /^[\-\+]?\d+$/,
                            "alertText": "* Not a valid integer"
                        },
                        "number": {
                            // Number, including positive, negative, and floating decimal. credit: orefalo
                            //"regex": /^[\-\+]?(([0-9]+)([\.,]([0-9]+))?|([\.,]([0-9]+))?)$/,
                            //integer only
                            // "regex": /^[0-9]{1,2}$/,
                            "regex": /^[0-9]+$/,
                            "alertText": "* Invalid floating decimal number"
                        },
                        "date": {
                            //"regex": /^(Jan|JAN|Feb|FEB|Mar|MAR|Apr|APR|May|MAY|Jun|JUN|Jul|JUL|Aug|AUG|Sep|SEP|Oct|OCT|Nov|NOV|Dec|DEC)[\s{1}|\/|-](3[0-1]|2[0-9]|1[0-9]|0[1-9])[\s{1}|\/|-]\d{4}$/,
                            "regex": /^[0,1]?\d{1}\/(([0-2]?\d{1})|([3][0,1]{1}))\/(([1]{1}[9]{1}[9]{1}\d{1})|([2-9]{1}\d{3}))$/, // mm/dd/yyyy
                            "alertText": "* Invalid date, must be in MM/DD/YYYY format"
                        },
                        "ipv4": {
                            "regex": /^((([01]?[0-9]{1,2})|(2[0-4][0-9])|(25[0-5]))[.]){3}(([0-1]?[0-9]{1,2})|(2[0-4][0-9])|(25[0-5]))$/,
                            "alertText": "* Invalid IP address"
                        },
                        "url": {
                            "regex": /^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i,
                            "alertText": "* Invalid URL"
                        },
                        "onlyNumberSp": {
                            "regex": /^[0-9\ ]+$/,
                            "alertText": "* Numbers only"
                        },
                        "onlyLetterSp": {
                            "regex": /^[a-zA-Z\ \']+$/,
                            "alertText": "* Letters only"
                        },
                        "onlyLetterNumber": {
                            "regex": /^[0-9a-zA-Z]+$/,
                            "alertText": "* No special characters allowed"
                        },
                        "notAllowSpecialCharacters": {
                            "regex": "none",
                            "alertText": "* No special characters allowed"
                        },
                        "notAllowSpecialCharactersFolder": {
                            "regex": "none",
                            "alertText": "* The folder name can't contain any of the following characters: /\\:*?”<>|"
                        },
                        // --- CUSTOM RULES -- Those are specific to the demos, they can be removed or changed to your likings
                        "ajaxUserCall": {
                            "url": "ajaxValidateFieldUser",
                            // you may want to pass extra data on the ajax call
                            "extraData": "name=eric",
                            "alertText": "* This user is already taken",
                            "alertTextLoad": "* Validating, please wait"
                        },
                        "ajaxUserCallPhp": {
                            "url": "ajaxvalidate/ajaxValidateFieldUser.php",
                            // you may want to pass extra data on the ajax call
                            "extraData": "name=eric",
                            // if you provide an "alertTextOk", it will show as a green prompt when the field validates
                            "alertTextOk": "* This username is available",
                            "alertText": "* This user is already taken",
                            "alertTextLoad": "* Validating, please wait"
                        },
                        "ajaxNameCall": {
                            // remote json service location
                            "url": "ajaxValidateFieldName",
                            // error
                            "alertText": "* This name is already taken",
                            // if you provide an "alertTextOk", it will show as a green prompt when the field validates
                            "alertTextOk": "* This name is available",
                            // speaks by itself
                            "alertTextLoad": "* Validating, please wait"
                        },
                        "ajaxNameCallPhp": {
                            // remote json service location
                            "url": "ajaxvalidate/ajaxValidateFieldName.php",
                            // error
                            "alertText": "* This name is already taken",
                            //		"alertTextOk": "* This name is available",
                            // speaks by itself
                            "alertTextLoad": "* Validating, please wait"
                        },
                        "ajaxEmailCallPhp": {
                            // remote json service location
                            "url": "ajaxvalidate/ajaxValidateFieldEmail.php",
                            // error
                            "alertText": "* This name is already taken",
                            // speaks by itself
                            "alertTextLoad": "* Validating, please wait"
                        },
                        "validate2fields": {
                            "alertText": "* Please input HELLO"
                        },
                        //tls warning:homegrown not fielded 
                        "dateFormat": {
                            //"regex": /^(Jan|JAN|Feb|FEB|Mar|MAR|Apr|APR|May|MAY|Jun|JUN|Jul|JUL|Aug|AUG|Sep|SEP|Oct|OCT|Nov|NOV|Dec|DEC)[\s{1}|\/|-](3[0-1]|2[0-9]|1[0-9]|0[1-9])[\s{1}|\/|-]\d{4}$/,
                            "regex": /^[0,1]?\d{1}\/(([0-2]?\d{1})|([3][0,1]{1}))\/(([1]{1}[9]{1}[9]{1}\d{1})|([2-9]{1}\d{3}))$/,
                            "alertText": "* Invalid Date"
                        },
                        //tls warning:homegrown not fielded 
                        "dateTimeFormat": {
                            "regex": /^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])\s+(1[012]|0?[1-9]){1}:(0?[1-5]|[0-6][0-9]){1}:(0?[0-6]|[0-6][0-9]){1}\s+(am|pm|AM|PM){1}$|^(?:(?:(?:0?[13578]|1[02])(\/|-)31)|(?:(?:0?[1,3-9]|1[0-2])(\/|-)(?:29|30)))(\/|-)(?:[1-9]\d\d\d|\d[1-9]\d\d|\d\d[1-9]\d|\d\d\d[1-9])$|^((1[012]|0?[1-9]){1}\/(0?[1-9]|[12][0-9]|3[01]){1}\/\d{2,4}\s+(1[012]|0?[1-9]){1}:(0?[1-5]|[0-6][0-9]){1}:(0?[0-6]|[0-6][0-9]){1}\s+(am|pm|AM|PM){1})$/,
                            "alertText": "* Invalid Date or Date Format",
                            "alertText2": "Expected Format: ",
                            "alertText3": "mm/dd/yyyy hh:mm:ss AM|PM or ",
                            "alertText4": "yyyy-mm-dd hh:mm:ss AM|PM"
                        }

                }
            }
        };

        $.validationEngineLanguage.newLang();

    var methods = {
        /**
         * This method is used to update new user's options. It'll override to current options. Author: tmhao2005 
         * @param {type} newuserOptions
         * @returns {Boolean}
         */
        updateOptions: function(newuserOptions) {
            var form = this;
            if (newuserOptions) {
                if (form.data('jqv')) {
                    var userOptions = $.extend(true, {}, form.data('jqv'), newuserOptions);
                    form.data('jqv', userOptions);
                    return true;
                }
                else
                    return false;
            }
            else
                return false;
        },
        /**
         * Kind of the constructor, called before any action
         * @param {Map} user options
         */
        init: function(options) {
            var form = this;
            if (!form.data('jqv') || form.data('jqv') === null) {
                options = methods._saveOptions(form, options);
                // bind all formError elements to close on click
                $(document).on("click", ".formError", function() {
                    $(this).fadeOut(150, function() {
                        // remove prompt once invisible
                        $(this).parent('.formErrorOuter').remove();
                        $(this).remove();
                    });
                });
            }
            return this;
        },
        /**
         * Attachs jQuery.validationEngine to form.submit and field.blur events
         * Takes an optional params: a list of options
         * ie. jQuery("#formID1").validationEngine('attach', {promptPosition : "centerRight"});
         */
        attach: function(userOptions) {

            var form = this;
            var options;

            if (userOptions)
                options = methods._saveOptions(form, userOptions);
            else
                options = form.data('jqv');

            options.validateAttribute = (form.find("[data-validation-engine*=validate]").length) ? "data-validation-engine" : "class";
            if (options.binded) {

                // delegate fields
                form.on(options.validationEventTrigger, "[" + options.validateAttribute + "*=validate]:not([type=checkbox]):not([type=radio]):not(.datepicker)", methods._onFieldEvent);
                form.on("click", "[" + options.validateAttribute + "*=validate][type=checkbox],[" + options.validateAttribute + "*=validate][type=radio]", methods._onFieldEvent);
                form.on(options.validationEventTrigger, "[" + options.validateAttribute + "*=validate][class*=datepicker]", {"delay": 300}, methods._onFieldEvent);
            }
            if (options.autoPositionUpdate) {
                $(window).bind("resize", {
                    "noAnimation": true,
                    "formElem": form
                }, methods.updatePromptsPosition);
            }
            form.on("click", "a[data-validation-engine-skip], a[class*='validate-skip'], button[data-validation-engine-skip], button[class*='validate-skip'], input[data-validation-engine-skip], input[class*='validate-skip']", methods._submitButtonClick);
            form.removeData('jqv_submitButton');

            // bind form.submit
            form.on("submit", methods._onSubmitEvent);
            return this;
        },
        /**
         * Unregisters any bindings that may point to jQuery.validaitonEngine
         */
        detach: function() {

            var form = this;
            var options = form.data('jqv');

            // unbind fields
            form.find("[" + options.validateAttribute + "*=validate]").not("[type=checkbox]").off(options.validationEventTrigger, methods._onFieldEvent);
            form.find("[" + options.validateAttribute + "*=validate][type=checkbox],[class*=validate][type=radio]").off("click", methods._onFieldEvent);

            // unbind form.submit
            form.off("submit", methods._onSubmitEvent);
            form.removeData('jqv');

            form.off("click", "a[data-validation-engine-skip], a[class*='validate-skip'], button[data-validation-engine-skip], button[class*='validate-skip'], input[data-validation-engine-skip], input[class*='validate-skip']", methods._submitButtonClick);
            form.removeData('jqv_submitButton');

            if (options.autoPositionUpdate)
                $(window).off("resize", methods.updatePromptsPosition);

            return this;
        },
        /**
         * Validates either a form or a list of fields, shows prompts accordingly.
         * Note: There is no ajax form validation with this method, only field ajax validation are evaluated
         *
         * @return true if the form validates, false if it fails
         */
        validate: function() {
            var element = $(this);
            var valid = null;

            if (element.is("form") || element.hasClass("validationEngineContainer")) {
                if (element.hasClass('validating')) {
                    // form is already validating.
                    // Should abort old validation and start new one. I don't know how to implement it.
                    return false;
                } else {
                    element.addClass('validating');
                    var options = element.data('jqv');
                    var valid = methods._validateFields(this);

                    // If the form doesn't validate, clear the 'validating' class before the user has a chance to submit again
                    setTimeout(function() {
                        element.removeClass('validating');
                    }, 100);
                    if (valid && options.onSuccess) {
                        options.onSuccess();
                    } else if (!valid && options.onFailure) {
                        options.onFailure();
                    }
                }
            } else if (element.is('form') || element.hasClass('validationEngineContainer')) {
                element.removeClass('validating');
            } else {
                // field validation
                var form = element.closest('form, .validationEngineContainer'),
                        options = (form.data('jqv')) ? form.data('jqv') : $.validationEngine.defaults,
                        valid = methods._validateField(element, options);

                if (valid && options.onFieldSuccess)
                    options.onFieldSuccess();
                else if (options.onFieldFailure && options.InvalidFields.length > 0) {
                    options.onFieldFailure();
                }
            }
            if (options.onValidationComplete) {
                // !! ensures that an undefined return is interpreted as return false but allows a onValidationComplete() to possibly return true and have form continue processing
                return !!options.onValidationComplete(form, valid);
            }
            return valid;
        },
        /**
         *  Redraw prompts position, useful when you change the DOM state when validating
         */
        updatePromptsPosition: function(event) {

            if (event && this === window) {
                var form = event.data.formElem;
                var noAnimation = event.data.noAnimation;
            }
            else
                var form = $(this.closest('form, .validationEngineContainer'));

            var options = form.data('jqv');
            // No option, take default one
            form.find('[' + options.validateAttribute + '*=validate]').not(":disabled").each(function() {
                var field = $(this);
                if (options.prettySelect && field.is(":hidden"))
                    field = form.find("#" + options.usePrefix + field.attr('id') + options.useSuffix);
                var prompt = methods._getPrompt(field);
                var promptText = $(prompt).find(".formErrorContent").html();

                if (prompt)
                    methods._updatePrompt(field, $(prompt), promptText, undefined, false, options, noAnimation);
            });
            return this;
        },
        /**
         * Displays a prompt on a element.
         * Note that the element needs an id!
         *
         * @param {String} promptText html text to display type
         * @param {String} type the type of bubble: 'pass' (green), 'load' (black) anything else (red)
         * @param {String} possible values topLeft, topRight, bottomLeft, centerRight, bottomRight
         */
        showPrompt: function(promptText, type, promptPosition, showArrow) {
            var form = this.closest('form, .validationEngineContainer');
            var options = form.data('jqv');
            // No option, take default one
            if (!options)
                options = methods._saveOptions(this, options);
            if (promptPosition)
                options.promptPosition = promptPosition;
            options.showArrow = showArrow === true;

            methods._showPrompt(this, promptText, type, false, options);
            return this;
        },
        /**
         * Closes form error prompts, CAN be invidual
         */
        hide: function() {
            var form = $(this).closest('form, .validationEngineContainer');
            var options = form.data('jqv');
            var fadeDuration = (options && options.fadeDuration) ? options.fadeDuration : 0.3;
            var closingtag;

            if ($(this).is("form") || $(this).hasClass("validationEngineContainer")) {
                closingtag = "parentForm" + methods._getClassName($(this).attr("id"));
            } else {
                closingtag = methods._getClassName($(this).attr("id")) + "formError";
            }
            $('.' + closingtag).fadeTo(fadeDuration, 0.3, function() {
                $(this).parent('.formErrorOuter').remove();
                $(this).remove();
            });
            return this;
        },
        /**
         * Closes all error prompts on the page
         */
        hideAll: function() {

            var form = this;
            var options = form.data('jqv');
            var duration = options ? options.fadeDuration : 300;
            $('.formError').fadeTo(duration, 300, function() {
                $(this).parent('.formErrorOuter').remove();
                $(this).remove();
            });
            return this;
        },
        /**
         * Typically called when user exists a field using tab or a mouse click, triggers a field
         * validation
         */
        _onFieldEvent: function(event) {
            var field = $(this);
            var form = field.closest('form, .validationEngineContainer');
            var options = form.data('jqv');
            options.eventTrigger = "field";
            // validate the current field
            window.setTimeout(function() {
                methods._validateField(field, options);
                if (options.InvalidFields.length === 0 && options.onFieldSuccess) {
                    options.onFieldSuccess();
                } else if (options.InvalidFields.length > 0 && options.onFieldFailure) {
                    options.onFieldFailure();
                }
            }, (event.data) ? event.data.delay : 0);

        },
        /**
         * Called when the form is submited, shows prompts accordingly
         *
         * @param {jqObject}
         *            form
         * @return false if form submission needs to be cancelled
         */
        _onSubmitEvent: function() {
            var form = $(this);
            var options = form.data('jqv');

            //check if it is trigger from skipped button
            if (form.data("jqv_submitButton")) {
                var submitButton = $("#" + form.data("jqv_submitButton"));
                if (submitButton) {
                    if (submitButton.length > 0) {
                        if (submitButton.hasClass("validate-skip") || submitButton.attr("data-validation-engine-skip") === "true")
                            return true;
                    }
                }
            }

            options.eventTrigger = "submit";

            // validate each field 
            // (- skip field ajax validation, not necessary IF we will perform an ajax form validation)
            var r = methods._validateFields(form);

            if (r && options.ajaxFormValidation) {
                methods._validateFormWithAjax(form, options);
                // cancel form auto-submission - process with async call onAjaxFormComplete
                return false;
            }

            if (options.onValidationComplete) {
                // !! ensures that an undefined return is interpreted as return false but allows a onValidationComplete() to possibly return true and have form continue processing
                return !!options.onValidationComplete(form, r);
            }
            return r;
        },
        /**
         * Return true if the ajax field validations passed so far
         * @param {Object} options
         * @return true, is all ajax validation passed so far (remember ajax is async)
         */
        _checkAjaxStatus: function(options) {
            var status = true;
            $.each(options.ajaxValidCache, function(key, value) {
                if (!value) {
                    status = false;
                    // break the each
                    return false;
                }
            });
            return status;
        },
        /**
         * Return true if the ajax field is validated
         * @param {String} fieldid
         * @param {Object} options
         * @return true, if validation passed, false if false or doesn't exist
         */
        _checkAjaxFieldStatus: function(fieldid, options) {
            return options.ajaxValidCache[fieldid] === true;
        },
        /**
         * Validates form fields, shows prompts accordingly
         *
         * @param {jqObject}
         *            form
         * @param {skipAjaxFieldValidation}
         *            boolean - when set to true, ajax field validation is skipped, typically used when the submit button is clicked
         *
         * @return true if form is valid, false if not, undefined if ajax form validation is done
         */
        _validateFields: function(form) {
            var options = form.data('jqv');

            // this variable is set to true if an error is found
            var errorFound = false;

            // Trigger hook, start validation
            form.trigger("jqv.form.validating");
            // first, evaluate status of non ajax fields
            var first_err = null;
            form.find('[' + options.validateAttribute + '*=validate]').not(":disabled").each(function() {
                var field = $(this);
                var names = [];
                if ($.inArray(field.attr('name'), names) < 0) {
                    errorFound |= methods._validateField(field, options);
                    if (errorFound && first_err === null)
                        if (field.is(":hidden") && options.prettySelect)
                            first_err = field = form.find("#" + options.usePrefix + methods._jqSelector(field.attr('id')) + options.useSuffix);
                        else {

                            //Check if we need to adjust what element to show the prompt on
                            //and and such scroll to instead
                            if (field.data('jqv-prompt-at') instanceof jQuery) {
                                field = field.data('jqv-prompt-at');
                            } else if (field.data('jqv-prompt-at')) {
                                field = $(field.data('jqv-prompt-at'));
                            }
                            first_err = field;
                        }
                    if (options.doNotShowAllErrosOnSubmit)
                        return false;
                    names.push(field.attr('name'));

                    //if option set, stop checking validation rules after one error is found
                    if (options.showOneMessage === true && errorFound) {
                        return false;
                    }
                }
            });

            // second, check to see if all ajax calls completed ok
            // errorFound |= !methods._checkAjaxStatus(options);

            // third, check status and scroll the container accordingly
            form.trigger("jqv.form.result", [errorFound]);

            if (errorFound) {
                if (options.scroll) {
                    var destination = first_err.offset().top;
                    var fixleft = first_err.offset().left;

                    //prompt positioning adjustment support. Usage: positionType:Xshift,Yshift (for ex.: bottomLeft:+20 or bottomLeft:-20,+10)
                    var positionType = options.promptPosition;
                    if (typeof (positionType) === 'string' && positionType.indexOf(":") !== -1)
                        positionType = positionType.substring(0, positionType.indexOf(":"));

                    if (positionType !== "bottomRight" && positionType !== "bottomLeft") {
                        var prompt_err = methods._getPrompt(first_err);
                        if (prompt_err) {
                            destination = prompt_err.offset().top;
                        }
                    }

                    // Offset the amount the page scrolls by an amount in px to accomodate fixed elements at top of page
                    if (options.scrollOffset) {
                        destination -= options.scrollOffset;
                    }

                    // get the position of the first error, there should be at least one, no need to check this
                    //var destination = form.find(".formError:not('.greenPopup'):first").offset().top;
                    if (options.isOverflown) {
                        var overflowDIV = $(options.overflownDIV);
                        if (!overflowDIV.length)
                            return false;
                        var scrollContainerScroll = overflowDIV.scrollTop();
                        var scrollContainerPos = -parseInt(overflowDIV.offset().top);

                        destination += scrollContainerScroll + scrollContainerPos - 5;
                        var scrollContainer = $(options.overflownDIV + ":not(:animated)");

                        scrollContainer.animate({scrollTop: destination}, 1100, function() {
                            if (options.focusFirstField)
                                first_err.focus();
                        });

                    } else {
                        $("html, body").animate({
                            scrollTop: destination
                        }, 1100, function() {
                            if (options.focusFirstField)
                                first_err.focus();
                        });
                        $("html, body").animate({scrollLeft: fixleft}, 1100)
                    }

                } else if (options.focusFirstField)
                    first_err.focus();
                return false;
            }
            return true;
        },
        /**
         * This method is called to perform an ajax form validation.
         * During this process all the (field, value) pairs are sent to the server which returns a list of invalid fields or true
         *
         * @param {jqObject} form
         * @param {Map} options
         */
        _validateFormWithAjax: function(form, options) {

            var data = form.serialize();
            var type = (options.ajaxFormValidationMethod) ? options.ajaxFormValidationMethod : "GET";
            var url = (options.ajaxFormValidationURL) ? options.ajaxFormValidationURL : form.attr("action");
            var dataType = (options.dataType) ? options.dataType : "json";
            $.ajax({
                type: type,
                url: url,
                cache: false,
                dataType: dataType,
                data: data,
                form: form,
                methods: methods,
                options: options,
                beforeSend: function() {
                    return options.onBeforeAjaxFormValidation(form, options);
                },
                error: function(data, transport) {
                    if (options.onFailure) {
                        options.onFailure(data, transport);
                    } else {
                        methods._ajaxError(data, transport);
                    }
                },
                success: function(json) {
                    if ((dataType === "json") && (json !== true)) {
                        // getting to this case doesn't necessary means that the form is invalid
                        // the server may return green or closing prompt actions
                        // this flag helps figuring it out
                        var errorInForm = false;
                        for (var i = 0; i < json.length; i++) {
                            var value = json[i];

                            var errorFieldId = value[0];
                            var errorField = $($("#" + errorFieldId)[0]);

                            // make sure we found the element
                            if (errorField.length === 1) {

                                // promptText or selector
                                var msg = value[2];
                                // if the field is valid
                                if (value[1] === true) {

                                    if (msg === "" || !msg) {
                                        // if for some reason, status==true and error="", just close the prompt
                                        methods._closePrompt(errorField);
                                    } else {
                                        // the field is valid, but we are displaying a green prompt
                                        if (options.allrules[msg]) {
                                            var txt = options.allrules[msg].alertTextOk;
                                            if (txt)
                                                msg = txt;
                                        }
                                        if (options.showPrompts)
                                            methods._showPrompt(errorField, msg, "pass", false, options, true);
                                    }
                                } else {
                                    // the field is invalid, show the red error prompt
                                    errorInForm |= true;
                                    if (options.allrules[msg]) {
                                        var txt = options.allrules[msg].alertText;
                                        if (txt)
                                            msg = txt;
                                    }
                                    if (options.showPrompts)
                                        methods._showPrompt(errorField, msg, "", false, options, true);
                                }
                            }
                        }
                        options.onAjaxFormComplete(!errorInForm, form, json, options);
                    } else
                        options.onAjaxFormComplete(true, form, json, options);

                }
            });

        },
        /**
         * Validates field, shows prompts accordingly
         *
         * @param {jqObject}
         *            field
         * @param {Array[String]}
         *            field's validation rules
         * @param {Map}
         *            user options
         * @return false if field is valid (It is inversed for *fields*, it return false on validate and true on errors.)
         */
        _validateField: function(field, options, skipAjaxValidation) {
            if (!field.attr("id")) {
                field.attr("id", "form-validation-field-" + $.validationEngine.fieldIdCounter);
                ++$.validationEngine.fieldIdCounter;
            }

            if (!options.validateNonVisibleFields && (field.is(":hidden") && !options.prettySelect || field.parent().is(":hidden")))
                return false;

            var rulesParsing = field.attr(options.validateAttribute);
            var getRules = /validate\[(.*)\]/.exec(rulesParsing);

            if (!getRules)
                return false;
            var str = getRules[1];
            var rules = str.split(/\[|,|\]/);

            // true if we ran the ajax validation, tells the logic to stop messing with prompts
            var isAjaxValidator = false;
            var fieldName = field.attr("name");
            var promptText = "";
            var promptType = "";
            var required = false;
            var limitErrors = false;
            options.isError = false;
            options.showArrow = true;

            // If the programmer wants to limit the amount of error messages per field,
            if (options.maxErrorsPerField > 0) {
                limitErrors = true;
            }

            var form = $(field.closest("form, .validationEngineContainer"));
            // Fix for adding spaces in the rules
            for (var i = 0; i < rules.length; i++) {
                rules[i] = rules[i].replace(" ", "");
                // Remove any parsing errors
                if (rules[i] === '') {
                    delete rules[i];
                }
            }

            for (var i = 0, field_errors = 0; i < rules.length; i++) {

                // If we are limiting errors, and have hit the max, break
                if (limitErrors && field_errors >= options.maxErrorsPerField) {
                    // If we haven't hit a required yet, check to see if there is one in the validation rules for this
                    // field and that it's index is greater or equal to our current index
                    if (!required) {
                        var have_required = $.inArray('required', rules);
                        required = (have_required !== -1 && have_required >= i);
                    }
                    break;
                }


                var errorMsg = undefined;
                switch (rules[i]) {

                    case "required":
                        required = true;
                        errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._required);
                        break;
                    case "custom":
                        errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._custom);
                        break;
                    case "groupRequired":
                        // Check is its the first of group, if not, reload validation with first field
                        // AND continue normal validation on present field
                        var classGroup = "[" + options.validateAttribute + "*=" + rules[i + 1] + "]";
                        var firstOfGroup = form.find(classGroup).eq(0);
                        if (firstOfGroup[0] !== field[0]) {

                            methods._validateField(firstOfGroup, options, skipAjaxValidation);
                            options.showArrow = true;

                        }
                        errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._groupRequired);
                        if (errorMsg)
                            required = true;
                        options.showArrow = false;
                        break;
                    case "ajax":
                        // AJAX defaults to returning it's loading message
                        errorMsg = methods._ajax(field, rules, i, options);
                        if (errorMsg) {
                            promptType = "load";
                        }
                        break;
                    case "minSize":
                        errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._minSize);
                        break;
                    case "maxSize":
                        errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._maxSize);
                        break;
                    case "min":
                        errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._min);
                        break;
                    case "max":
                        errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._max);
                        break;
                    case "past":
                        errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._past);
                        break;
                    case "future":
                        errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._future);
                        break;
                    case "dateRange":
                        var classGroup = "[" + options.validateAttribute + "*=" + rules[i + 1] + "]";
                        options.firstOfGroup = form.find(classGroup).eq(0);
                        options.secondOfGroup = form.find(classGroup).eq(1);

                        //if one entry out of the pair has value then proceed to run through validation
                        if (options.firstOfGroup[0].value || options.secondOfGroup[0].value) {
                            errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._dateRange);
                        }
                        if (errorMsg)
                            required = true;
                        options.showArrow = false;
                        break;

                    case "dateTimeRange":
                        var classGroup = "[" + options.validateAttribute + "*=" + rules[i + 1] + "]";
                        options.firstOfGroup = form.find(classGroup).eq(0);
                        options.secondOfGroup = form.find(classGroup).eq(1);

                        //if one entry out of the pair has value then proceed to run through validation
                        if (options.firstOfGroup[0].value || options.secondOfGroup[0].value) {
                            errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._dateTimeRange);
                        }
                        if (errorMsg)
                            required = true;
                        options.showArrow = false;
                        break;
                    case "maxCheckbox":
                        field = $(form.find("input[name='" + fieldName + "']"));
                        errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._maxCheckbox);
                        break;
                    case "minCheckbox":
                        field = $(form.find("input[name='" + fieldName + "']"));
                        errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._minCheckbox);
                        break;
                    case "equals":
                        errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._equals);
                        break;
                    case "funcCall":
                        errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._funcCall);
                        break;
                    case "creditCard":
                        errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._creditCard);
                        break;
                    case "condRequired":
                        errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._condRequired);
                        if (errorMsg !== undefined) {
                            required = true;
                        }
                        break;
                    case "validGroupEmail":
                        errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._validGroupEmail);
                        //errorMsg = methods._validGroupEmail(field, rules, i, options);
                        break;
                    case "notAllowSpecialCharacters":
                        errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._notAllowSpecialCharacters);
                        //errorMsg = methods._notAllowSpecialCharacters(field, rules, i, options);
                        break;
                    case "notAllowSpecialCharactersFolder":
                        errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._notAllowSpecialCharactersFolder);
                        //errorMsg = methods._notAllowSpecialCharactersFolder(field, rules, i, options);
                        break;
                    default:
                }

                var end_validation = false;

                // If we were passed back an message object, check what the status was to determine what to do
                if (typeof errorMsg === "object") {
                    switch (errorMsg.status) {
                        case "_break":
                            end_validation = true;
                            break;
                            // If we have an error message, set errorMsg to the error message
                        case "_error":
                            errorMsg = errorMsg.message;
                            break;
                            // If we want to throw an error, but not show a prompt, return early with true
                        case "_error_no_prompt":
                            return true;
                            break;
                            // Anything else we continue on
                        default:
                            break;
                    }
                }

                // If it has been specified that validation should end now, break
                if (end_validation) {
                    break;
                }

                // If we have a string, that means that we have an error, so add it to the error message.
                if (typeof errorMsg === 'string') {
                    promptText += errorMsg + "<br/>";
                    options.isError = true;
                    field_errors++;
                }
            }
            // If the rules required is not added, an empty field is not validated
            //the 3rd condition is added so that even empty password fields should be equal
            //otherwise if one is filled and another left empty, the "equal" condition would fail
            //which does not make any sense
            if (!required && !(field.val()) && field.val().length < 1 && rules.indexOf("equals") < 0)
                options.isError = false;

            // Hack for radio/checkbox group button, the validation go into the
            // first radio/checkbox of the group
            var fieldType = field.prop("type");
            var positionType = field.data("promptPosition") || options.promptPosition;

            if ((fieldType === "radio" || fieldType === "checkbox") && form.find("input[name='" + fieldName + "']").size() > 1) {
                if (positionType === 'inline') {
                    field = $(form.find("input[name='" + fieldName + "'][type!=hidden]:last"));
                } else {
                    field = $(form.find("input[name='" + fieldName + "'][type!=hidden]:first"));
                }
                options.showArrow = false;
            }

            if (field.is(":hidden") && options.prettySelect) {
                field = form.find("#" + options.usePrefix + methods._jqSelector(field.attr('id')) + options.useSuffix);
            }

            if (options.isError && options.showPrompts) {
                methods._showPrompt(field, promptText, promptType, false, options);
            } else {
                if (!isAjaxValidator)
                    methods._closePrompt(field);
            }

            if (!isAjaxValidator) {
                field.trigger("jqv.field.result", [field, options.isError, promptText]);
            }

            /* Record error */
            var errindex = $.inArray(field[0], options.InvalidFields);
            if (errindex === -1) {
                if (options.isError)
                    options.InvalidFields.push(field[0]);
            } else if (!options.isError) {
                options.InvalidFields.splice(errindex, 1);
            }

            methods._handleStatusCssClasses(field, options);

            /* run callback function for each field */
            if (options.isError && options.onFieldFailure)
                options.onFieldFailure(field);

            if (!options.isError && options.onFieldSuccess)
                options.onFieldSuccess(field);

            return options.isError;
        },
        /**
         * Handling css classes of fields indicating result of validation 
         *
         * @param {jqObject}
         *            field
         * @param {Array[String]}
         *            field's validation rules            
         * @private
         */
        _handleStatusCssClasses: function(field, options) {
            /* remove all classes */
            if (options.addSuccessCssClassToField)
                field.removeClass(options.addSuccessCssClassToField);

            if (options.addFailureCssClassToField)
                field.removeClass(options.addFailureCssClassToField);

            /* Add classes */
            if (options.addSuccessCssClassToField && !options.isError)
                field.addClass(options.addSuccessCssClassToField);

            if (options.addFailureCssClassToField && options.isError)
                field.addClass(options.addFailureCssClassToField);
        },
        /********************
         * _getErrorMessage
         *
         * @param form
         * @param field
         * @param rule
         * @param rules
         * @param i
         * @param options
         * @param originalValidationMethod
         * @return {*}
         * @private
         */
        _getErrorMessage: function(form, field, rule, rules, i, options, originalValidationMethod) {
            // If we are using the custon validation type, build the index for the rule.
            // Otherwise if we are doing a function call, make the call and return the object
            // that is passed back.
            var rule_index = jQuery.inArray(rule, rules);
            if (rule === "custom" || rule === "funcCall") {
                var custom_validation_type = rules[rule_index + 1];
                rule = rule + "[" + custom_validation_type + "]";
                // Delete the rule from the rules array so that it doesn't try to call the
                // same rule over again
                delete(rules[rule_index]);
            }
            // Change the rule to the composite rule, if it was different from the original
            var alteredRule = rule;


            var element_classes = (field.attr("data-validation-engine")) ? field.attr("data-validation-engine") : field.attr("class");
            var element_classes_array = element_classes.split(" ");

            // Call the original validation method. If we are dealing with dates or checkboxes, also pass the form
            var errorMsg;
            if (rule === "future" || rule === "past" || rule === "maxCheckbox" || rule === "minCheckbox") {
                errorMsg = originalValidationMethod(form, field, rules, i, options);
            } else {
                errorMsg = originalValidationMethod(field, rules, i, options);
            }

            // If the original validation method returned an error and we have a custom error message,
            // return the custom message instead. Otherwise return the original error message.
            if (errorMsg !== undefined) {
                var custom_message = methods._getCustomErrorMessage($(field), element_classes_array, alteredRule, options);
                if (custom_message)
                    errorMsg = custom_message;
            }
            return errorMsg;

        },
        _getCustomErrorMessage: function(field, classes, rule, options) {
            var custom_message = false;
            var validityProp = /^custom\[.*\]$/.test(rule) ? methods._validityProp["custom"] : methods._validityProp[rule];
            // If there is a validityProp for this rule, check to see if the field has an attribute for it
            if (validityProp !== undefined) {
                custom_message = field.attr("data-errormessage-" + validityProp);
                // If there was an error message for it, return the message
                if (custom_message !== undefined)
                    return custom_message;
            }
            custom_message = field.attr("data-errormessage");
            // If there is an inline custom error message, return it
            if (custom_message !== undefined)
                return custom_message;
            var id = '#' + field.attr("id");
            // If we have custom messages for the element's id, get the message for the rule from the id.
            // Otherwise, if we have custom messages for the element's classes, use the first class message we find instead.
            if (typeof options.custom_error_messages[id] !== "undefined" &&
                    typeof options.custom_error_messages[id][rule] !== "undefined") {
                custom_message = options.custom_error_messages[id][rule]['message'];
            } else if (classes.length > 0) {
                for (var i = 0; i < classes.length && classes.length > 0; i++) {
                    var element_class = "." + classes[i];
                    if (typeof options.custom_error_messages[element_class] !== "undefined" &&
                            typeof options.custom_error_messages[element_class][rule] !== "undefined") {
                        custom_message = options.custom_error_messages[element_class][rule]['message'];
                        break;
                    }
                }
            }
            if (!custom_message &&
                    typeof options.custom_error_messages[rule] !== "undefined" &&
                    typeof options.custom_error_messages[rule]['message'] !== "undefined") {
                custom_message = options.custom_error_messages[rule]['message'];
            }
            return custom_message;
        },
        _validityProp: {
            "required": "value-missing",
            "custom": "custom-error",
            "groupRequired": "value-missing",
            "ajax": "custom-error",
            "minSize": "range-underflow",
            "maxSize": "range-overflow",
            "min": "range-underflow",
            "max": "range-overflow",
            "past": "type-mismatch",
            "future": "type-mismatch",
            "dateRange": "type-mismatch",
            "dateTimeRange": "type-mismatch",
            "maxCheckbox": "range-overflow",
            "minCheckbox": "range-underflow",
            "equals": "pattern-mismatch",
            "funcCall": "custom-error",
            "creditCard": "pattern-mismatch",
            "condRequired": "value-missing"
        },
        /**
         * Required validation
         *
         * @param {jqObject} field
         * @param {Array[String]} rules
         * @param {int} i rules index
         * @param {Map}
         *            user options
         * @param {bool} condRequired flag when method is used for internal purpose in condRequired check
         * @return an error string if validation failed
         */
        _required: function(field, rules, i, options, condRequired) {
            switch (field.prop("type")) {
                case "text":
                case "password":
                case "textarea":
                case "file":
                case "select-one":
                case "select-multiple":
                default:
                    var field_val = $.trim(field.val());
                    var dv_placeholder = $.trim(field.attr("data-validation-placeholder"));
                    var placeholder = $.trim(field.attr("placeholder"));
                    if (
                            (!field_val)
                            || (dv_placeholder && field_val === dv_placeholder)
                            || (placeholder && field_val === placeholder)
                            ) {
                        return options.allrules[rules[i]].alertText;
                    }
                    break;
                case "radio":
                case "checkbox":
                    // new validation style to only check dependent field
                    if (condRequired) {
                        if (!field.attr('checked')) {
                            return options.allrules[rules[i]].alertTextCheckboxMultiple;
                        }
                        break;
                    }
                    // old validation style
                    var form = field.closest("form, .validationEngineContainer");
                    var name = field.attr("name");
                    if (form.find("input[name='" + name + "']:checked").size() === 0) {
                        if (form.find("input[name='" + name + "']:visible").size() === 1)
                            return options.allrules[rules[i]].alertTextCheckboxe;
                        else
                            return options.allrules[rules[i]].alertTextCheckboxMultiple;
                    }
                    break;
            }
        },
        /**
         * Validate that 1 from the group field is required
         *
         * @param {jqObject} field
         * @param {Array[String]} rules
         * @param {int} i rules index
         * @param {Map}
         *            user options
         * @return an error string if validation failed
         */
        _groupRequired: function(field, rules, i, options) {
            var classGroup = "[" + options.validateAttribute + "*=" + rules[i + 1] + "]";
            var isValid = false;
            // Check all fields from the group
            field.closest("form, .validationEngineContainer").find(classGroup).each(function() {
                if (!methods._required($(this), rules, i, options)) {
                    isValid = true;
                    return false;
                }
            });

            if (!isValid) {
                return options.allrules[rules[i]].alertText;
            }
        },
        /**
         * Validate rules
         *
         * @param {jqObject} field
         * @param {Array[String]} rules
         * @param {int} i rules index
         * @param {Map}
         *            user options
         * @return an error string if validation failed
         */
        _custom: function(field, rules, i, options) {
            var customRule = rules[i + 1];
            var rule = options.allrules[customRule];
            var fn;
            if (!rule) {
                alert("jqv:custom rule not found - " + customRule);
                return;
            }

            if (rule["regex"]) {
                var ex = rule.regex;
                if (!ex) {
                    alert("jqv:custom regex not found - " + customRule);
                    return;
                }
                var pattern = new RegExp(ex);

                if (!pattern.test(field.val()))
                    return options.allrules[customRule].alertText;

            } else if (rule["func"]) {
                fn = rule["func"];

                if (typeof (fn) !== "function") {
                    alert("jqv:custom parameter 'function' is no function - " + customRule);
                    return;
                }

                if (!fn(field, rules, i, options))
                    return options.allrules[customRule].alertText;
            } else {
                alert("jqv:custom type not allowed " + customRule);
                return;
            }
        },
        /**
         * Validate custom function outside of the engine scope
         *
         * @param {jqObject} field
         * @param {Array[String]} rules
         * @param {int} i rules index
         * @param {Map}
         *            user options
         * @return an error string if validation failed
         */
        _funcCall: function(field, rules, i, options) {
            var functionName = rules[i + 1];
            var fn;
            if (functionName.indexOf('.') > -1)
            {
                var namespaces = functionName.split('.');
                var scope = window;
                while (namespaces.length)
                {
                    scope = scope[namespaces.shift()];
                }
                fn = scope;
            }
            else
                fn = window[functionName] || options.customFunctions[functionName];
            if (typeof (fn) === 'function')
                return fn(field, rules, i, options);

        },
        /**
         * Field match
         *
         * @param {jqObject} field
         * @param {Array[String]} rules
         * @param {int} i rules index
         * @param {Map}
         *            user options
         * @return an error string if validation failed
         */
        _equals: function(field, rules, i, options) {
            var equalsField = rules[i + 1];

            if (field.val() !== $("#" + equalsField).val())
                return options.allrules.equals.alertText;
        },
        /**
         * Check the maximum size (in characters)
         *
         * @param {jqObject} field
         * @param {Array[String]} rules
         * @param {int} i rules index
         * @param {Map}
         *            user options
         * @return an error string if validation failed
         */
        _maxSize: function(field, rules, i, options) {
            var max = rules[i + 1];
            var len = field.val().length;

            if (len > max) {
                var rule = options.allrules.maxSize;
                return rule.alertText + max + rule.alertText2;
            }
        },
        /**
         * Check the minimum size (in characters)
         *
         * @param {jqObject} field
         * @param {Array[String]} rules
         * @param {int} i rules index
         * @param {Map}
         *            user options
         * @return an error string if validation failed
         */
        _minSize: function(field, rules, i, options) {
            var min = rules[i + 1];
            var len = field.val().length;

            if (len < min) {
                var rule = options.allrules.minSize;
                return rule.alertText + min + rule.alertText2;
            }
        },
        /**
         * Check number minimum value
         *
         * @param {jqObject} field
         * @param {Array[String]} rules
         * @param {int} i rules index
         * @param {Map}
         *            user options
         * @return an error string if validation failed
         */
        _min: function(field, rules, i, options) {
            var min = parseFloat(rules[i + 1]);
            var len = parseFloat(field.val());

            if (len < min) {
                var rule = options.allrules.min;
                if (rule.alertText2)
                    return rule.alertText + min + rule.alertText2;
                return rule.alertText + min;
            }
        },
        /**
         * Check number maximum value
         *
         * @param {jqObject} field
         * @param {Array[String]} rules
         * @param {int} i rules index
         * @param {Map}
         *            user options
         * @return an error string if validation failed
         */
        _max: function(field, rules, i, options) {
            var max = parseFloat(rules[i + 1]);
            var len = parseFloat(field.val());

            if (len > max) {
                var rule = options.allrules.max;
                if (rule.alertText2)
                    return rule.alertText + max + rule.alertText2;
                //orefalo: to review, also do the translations
                return rule.alertText + max;
            }
        },
        /**
         * Checks date is in the past
         *
         * @param {jqObject} field
         * @param {Array[String]} rules
         * @param {int} i rules index
         * @param {Map}
         *            user options
         * @return an error string if validation failed
         */
        _past: function(form, field, rules, i, options) {
            var p = rules[i + 1];
            var fieldAlt = $(form.find("*[name='" + p.replace(/^#+/, '') + "']"));
            var pdate;

            if (p.toLowerCase() === "now") {
                pdate = new Date();
            } else if (undefined !== fieldAlt.val()) {
                if (fieldAlt.is(":disabled"))
                    return;
                pdate = methods._parseDate(fieldAlt.val());
            } else {
                pdate = methods._parseDate(p);
            }

            var vdate = methods._parseDate(field.val());

            if (vdate > pdate) {
                var rule = options.allrules.past;
                if (rule.alertText2)
                    return rule.alertText + methods._dateToString(pdate) + rule.alertText2;
                return rule.alertText + methods._dateToString(pdate);
            }
        },
        /**
         * Checks date is in the future
         *
         * @param {jqObject} field
         * @param {Array[String]} rules
         * @param {int} i rules index
         * @param {Map}
         *            user options
         * @return an error string if validation failed
         */
        _future: function(form, field, rules, i, options) {

            var p = rules[i + 1];
            var fieldAlt = $(form.find("*[name='" + p.replace(/^#+/, '') + "']"));
            var pdate;

            if (p.toLowerCase() === "now") {
                pdate = new Date();
            } else if (undefined !== fieldAlt.val()) {
                if (fieldAlt.is(":disabled"))
                    return;
                pdate = methods._parseDate(fieldAlt.val());
            } else {
                pdate = methods._parseDate(p);
            }
            var vdate = methods._parseDate(field.val());
            if (vdate < pdate) {
                var rule = options.allrules.future;

                if (rule.alertText2)
                    return rule.alertText + methods._dateToString(pdate) + rule.alertText2;
                return rule.alertText + methods._dateToString(pdate);
            }


        },
        /**
         * Checks if valid date
         *
         * @param {string} date string
         * @return a bool based on determination of valid date
         */
        _isDate: function(value) {
            var dateRegEx = new RegExp(/^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])$|^(?:(?:(?:0?[13578]|1[02])(\/|-)31)|(?:(?:0?[1,3-9]|1[0-2])(\/|-)(?:29|30)))(\/|-)(?:[1-9]\d\d\d|\d[1-9]\d\d|\d\d[1-9]\d|\d\d\d[1-9])$|^(?:(?:0?[1-9]|1[0-2])(\/|-)(?:0?[1-9]|1\d|2[0-8]))(\/|-)(?:[1-9]\d\d\d|\d[1-9]\d\d|\d\d[1-9]\d|\d\d\d[1-9])$|^(0?2(\/|-)29)(\/|-)(?:(?:0[48]00|[13579][26]00|[2468][048]00)|(?:\d\d)?(?:0[48]|[2468][048]|[13579][26]))$/);
            return dateRegEx.test(value);
        },
        /**
         * Checks if valid date time
         *
         * @param {string} date string
         * @return a bool based on determination of valid date time
         */
        _isDateTime: function(value) {
            var dateTimeRegEx = new RegExp(/^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])\s+(1[012]|0?[1-9]){1}:(0?[1-5]|[0-6][0-9]){1}:(0?[0-6]|[0-6][0-9]){1}\s+(am|pm|AM|PM){1}$|^(?:(?:(?:0?[13578]|1[02])(\/|-)31)|(?:(?:0?[1,3-9]|1[0-2])(\/|-)(?:29|30)))(\/|-)(?:[1-9]\d\d\d|\d[1-9]\d\d|\d\d[1-9]\d|\d\d\d[1-9])$|^((1[012]|0?[1-9]){1}\/(0?[1-9]|[12][0-9]|3[01]){1}\/\d{2,4}\s+(1[012]|0?[1-9]){1}:(0?[1-5]|[0-6][0-9]){1}:(0?[0-6]|[0-6][0-9]){1}\s+(am|pm|AM|PM){1})$/);
            return dateTimeRegEx.test(value);
        },
        //Checks if the start date is before the end date
        //returns true if end is later than start
        _dateCompare: function(start, end) {
            return (new Date(start.toString()) < new Date(end.toString()));
        },
        /**
         * Checks date range
         *
         * @param {jqObject} first field name
         * @param {jqObject} second field name
         * @return an error string if validation failed
         */
        _dateRange: function(field, rules, i, options) {
            //are not both populated
            if ((!options.firstOfGroup[0].value && options.secondOfGroup[0].value) || (options.firstOfGroup[0].value && !options.secondOfGroup[0].value)) {
                return options.allrules[rules[i]].alertText + options.allrules[rules[i]].alertText2;
            }

            //are not both dates
            if (!methods._isDate(options.firstOfGroup[0].value) || !methods._isDate(options.secondOfGroup[0].value)) {
                return options.allrules[rules[i]].alertText + options.allrules[rules[i]].alertText2;
            }

            //are both dates but range is off
            if (!methods._dateCompare(options.firstOfGroup[0].value, options.secondOfGroup[0].value)) {
                return options.allrules[rules[i]].alertText + options.allrules[rules[i]].alertText2;
            }
        },
        /**
         * Checks date time range
         *
         * @param {jqObject} first field name
         * @param {jqObject} second field name
         * @return an error string if validation failed
         */
        _dateTimeRange: function(field, rules, i, options) {
            //are not both populated
            if ((!options.firstOfGroup[0].value && options.secondOfGroup[0].value) || (options.firstOfGroup[0].value && !options.secondOfGroup[0].value)) {
                return options.allrules[rules[i]].alertText + options.allrules[rules[i]].alertText2;
            }
            //are not both dates
            if (!methods._isDateTime(options.firstOfGroup[0].value) || !methods._isDateTime(options.secondOfGroup[0].value)) {
                return options.allrules[rules[i]].alertText + options.allrules[rules[i]].alertText2;
            }
            //are both dates but range is off
            if (!methods._dateCompare(options.firstOfGroup[0].value, options.secondOfGroup[0].value)) {
                return options.allrules[rules[i]].alertText + options.allrules[rules[i]].alertText2;
            }
        },
        /**
         * Max number of checkbox selected
         *
         * @param {jqObject} field
         * @param {Array[String]} rules
         * @param {int} i rules index
         * @param {Map}
         *            user options
         * @return an error string if validation failed
         */
        _maxCheckbox: function(form, field, rules, i, options) {

            var nbCheck = rules[i + 1];
            var groupname = field.attr("name");
            var groupSize = form.find("input[name='" + groupname + "']:checked").size();
            if (groupSize > nbCheck) {
                options.showArrow = false;
                if (options.allrules.maxCheckbox.alertText2)
                    return options.allrules.maxCheckbox.alertText + " " + nbCheck + " " + options.allrules.maxCheckbox.alertText2;
                return options.allrules.maxCheckbox.alertText;
            }
        },
        /**
         * Min number of checkbox selected
         *
         * @param {jqObject} field
         * @param {Array[String]} rules
         * @param {int} i rules index
         * @param {Map}
         *            user options
         * @return an error string if validation failed
         */
        _minCheckbox: function(form, field, rules, i, options) {

            var nbCheck = rules[i + 1];
            var groupname = field.attr("name");
            var groupSize = form.find("input[name='" + groupname + "']:checked").size();
            if (groupSize < nbCheck) {
                options.showArrow = false;
                return options.allrules.minCheckbox.alertText + " " + nbCheck + " " + options.allrules.minCheckbox.alertText2;
            }
        },
        /**
         * Checks that it is a valid credit card number according to the
         * Luhn checksum algorithm.
         *
         * @param {jqObject} field
         * @param {Array[String]} rules
         * @param {int} i rules index
         * @param {Map}
         *            user options
         * @return an error string if validation failed
         */
        _creditCard: function(field, rules, i, options) {
            //spaces and dashes may be valid characters, but must be stripped to calculate the checksum.
            var valid = false, cardNumber = field.val().replace(/ +/g, '').replace(/-+/g, '');

            var numDigits = cardNumber.length;
            if (numDigits >= 14 && numDigits <= 16 && parseInt(cardNumber) > 0) {

                var sum = 0, i = numDigits - 1, pos = 1, digit, luhn = new String();
                do {
                    digit = parseInt(cardNumber.charAt(i));
                    luhn += (pos++ % 2 === 0) ? digit * 2 : digit;
                } while (--i >= 0)

                for (i = 0; i < luhn.length; i++) {
                    sum += parseInt(luhn.charAt(i));
                }
                valid = sum % 10 === 0;
            }
            if (!valid)
                return options.allrules.creditCard.alertText;
        },
        /**
         * Ajax field validation
         *
         * @param {jqObject} field
         * @param {Array[String]} rules
         * @param {int} i rules index
         * @param {Map}
         *            user options
         * @return nothing! the ajax validator handles the prompts itself
         */
        _ajax: function(field, rules, i, options) {

            var errorSelector = rules[i + 1];
            var rule = options.allrules[errorSelector];
            var extraData = rule.extraData;
            var extraDataDynamic = rule.extraDataDynamic;
            var data = {
                "fieldId": field.attr("id"),
                "fieldValue": field.val()
            };

            if (typeof extraData === "object") {
                $.extend(data, extraData);
            } else if (typeof extraData === "string") {
                var tempData = extraData.split("&");
                for (var i = 0; i < tempData.length; i++) {
                    var values = tempData[i].split("=");
                    if (values[0] && values[0]) {
                        data[values[0]] = values[1];
                    }
                }
            }

            if (extraDataDynamic) {
                var tmpData = [];
                var domIds = String(extraDataDynamic).split(",");
                for (var i = 0; i < domIds.length; i++) {
                    var id = domIds[i];
                    if ($(id).length) {
                        var inputValue = field.closest("form, .validationEngineContainer").find(id).val();
                        var keyValue = id.replace('#', '') + '=' + escape(inputValue);
                        data[id.replace('#', '')] = inputValue;
                    }
                }
            }

            // If a field change event triggered this we want to clear the cache for this ID
            if (options.eventTrigger === "field") {
                delete(options.ajaxValidCache[field.attr("id")]);
            }

            // If there is an error or if the the field is already validated, do not re-execute AJAX
            if (!options.isError && !methods._checkAjaxFieldStatus(field.attr("id"), options)) {
                $.ajax({
                    type: options.ajaxFormValidationMethod,
                    url: rule.url,
                    cache: false,
                    dataType: "json",
                    data: data,
                    field: field,
                    rule: rule,
                    methods: methods,
                    options: options,
                    beforeSend: function() {
                    },
                    error: function(data, transport) {
                        if (options.onFailure) {
                            options.onFailure(data, transport);
                        } else {
                            methods._ajaxError(data, transport);
                        }
                    },
                    success: function(json) {

                        // asynchronously called on success, data is the json answer from the server
                        var errorFieldId = json[0];
                        //var errorField = $($("#" + errorFieldId)[0]);
                        var errorField = $("#" + errorFieldId).eq(0);

                        // make sure we found the element
                        if (errorField.length === 1) {
                            var status = json[1];
                            // read the optional msg from the server
                            var msg = json[2];
                            if (!status) {
                                // Houston we got a problem - display an red prompt
                                options.ajaxValidCache[errorFieldId] = false;
                                options.isError = true;

                                // resolve the msg prompt
                                if (msg) {
                                    if (options.allrules[msg]) {
                                        var txt = options.allrules[msg].alertText;
                                        if (txt) {
                                            msg = txt;
                                        }
                                    }
                                }
                                else
                                    msg = rule.alertText;

                                if (options.showPrompts)
                                    methods._showPrompt(errorField, msg, "", true, options);
                            } else {
                                options.ajaxValidCache[errorFieldId] = true;

                                // resolves the msg prompt
                                if (msg) {
                                    if (options.allrules[msg]) {
                                        var txt = options.allrules[msg].alertTextOk;
                                        if (txt) {
                                            msg = txt;
                                        }
                                    }
                                }
                                else
                                    msg = rule.alertTextOk;

                                if (options.showPrompts) {
                                    // see if we should display a green prompt
                                    if (msg)
                                        methods._showPrompt(errorField, msg, "pass", true, options);
                                    else
                                        methods._closePrompt(errorField);
                                }

                                // If a submit form triggered this, we want to re-submit the form
                                if (options.eventTrigger === "submit")
                                    field.closest("form").submit();
                            }
                        }
                        errorField.trigger("jqv.field.result", [errorField, options.isError, msg]);
                    }
                });

                return rule.alertTextLoad;
            }
        },
        /**
         * Common method to handle ajax errors
         *
         * @param {Object} data
         * @param {Object} transport
         */
        _ajaxError: function(data, transport) {
            if (data.status === 0 && transport === null)
                alert("The page is not served from a server! ajax call failed");
            else if (typeof console !== "undefined")
                console.log("Ajax error: " + data.status + " " + transport);
        },
        /**
         * date -> string
         *
         * @param {Object} date
         */
        _dateToString: function(date) {
            return ("0" + (date.getMonth() + 1)).slice(-2) + "/" + ("0" + date.getDate()).slice(-2) + "/" + date.getFullYear(); // Edit from v1.5 (new HoGo's format date)
            //return date.getFullYear() + "/" + (date.getMonth() + 1) + "/" + date.getDate(); 
        },
        /**
         * Parses an ISO date
         * @param {String} d (dd/MMM/yyyy)
         */
        _parseDate: function(d) {

//			var dateParts = d.split("-");
//			if(dateParts==d)
//				dateParts = d.split("/");
//			if(dateParts==d) {
//				dateParts = d.split(".");
//				return new Date(dateParts[2], (dateParts[1] - 1), dateParts[0]);
//			}
//			return new Date(dateParts[0], (dateParts[1] - 1) ,dateParts[2]);
//			
            //Added by TMA (Using for current context format date(dd/MMM/yyy))

            var dateParts = d.split("-");

            if (dateParts !== d)
                dateParts = d.split("/");

            switch (dateParts[0]) {
                case 'Jan':
                    dateParts[0] = 1;
                    break;
                case 'Feb':
                    dateParts[0] = 2;
                    break;
                case 'Mar':
                    dateParts[0] = 3;
                    break;
                case 'Apr':
                    dateParts[0] = 4;
                    break;
                case 'May':
                    dateParts[0] = 5;
                    break;
                case 'Jun':
                    dateParts[0] = 6;
                    break;
                case 'Jul':
                    dateParts[0] = 7;
                    break;
                case 'Aug':
                    dateParts[0] = 8;
                    break;
                case 'Sep':
                    dateParts[0] = 9;
                    break;
                case 'Oct':
                    dateParts[0] = 10;
                    break;
                case 'Nov':
                    dateParts[0] = 11;
                    break;
                case 'Dec':
                    dateParts[0] = 12;
                    break;
            }

            return new Date(dateParts[2], (dateParts[0] - 1), dateParts[1]);
        },
        _parseToHoGoDate: function(date) {
            var months = new Array(
                    "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
            if (date !== undefined) {

                var dateObj = new Date(date.replace('-', '/').replace('-', '/'));

                if (Object.prototype.toString.call(dateObj) === "[object Date]") {

                    if (isNaN(dateObj.getTime())) {
                        return date;
                    }
                    else {
                        if (dateObj.getFullYear() === 1970 || dateObj.getFullYear() === 1969) { //Not valid date    
                            return 'N/A'
                        }
                        else {
                            return months[dateObj.getMonth()] + "/" + ("0" + dateObj.getDate()).slice(-2) + "/" + dateObj.getFullYear();
                        }
                    }
                }
                else {
                    //Not format date object
                    return "N/A";
                }
            }
            else {
                return "N/A";
            }
        },
        /**
         * Builds or updates a prompt with the given information
         *
         * @param {jqObject} field
         * @param {String} promptText html text to display type
         * @param {String} type the type of bubble: 'pass' (green), 'load' (black) anything else (red)
         * @param {boolean} ajaxed - use to mark fields than being validated with ajax
         * @param {Map} options user options
         */
        _showPrompt: function(field, promptText, type, ajaxed, options, ajaxform) {
            //Check if we need to adjust what element to show the prompt on
            if (field.data('jqv-prompt-at') instanceof jQuery) {
                field = field.data('jqv-prompt-at');
            } else if (field.data('jqv-prompt-at')) {
                field = $(field.data('jqv-prompt-at'));
            }

            var prompt = methods._getPrompt(field);
            // The ajax submit errors are not see has an error in the form,
            // When the form errors are returned, the engine see 2 bubbles, but those are ebing closed by the engine at the same time
            // Because no error was found befor submitting
            if (ajaxform)
                prompt = false;
            // Check that there is indded text
            if ($.trim(promptText)) {
                if (prompt)
                    methods._updatePrompt(field, prompt, promptText, type, ajaxed, options);
                else
                    methods._buildPrompt(field, promptText, type, ajaxed, options);
            }
        },
        /**
         * Builds and shades a prompt for the given field.
         *
         * @param {jqObject} field
         * @param {String} promptText html text to display type
         * @param {String} type the type of bubble: 'pass' (green), 'load' (black) anything else (red)
         * @param {boolean} ajaxed - use to mark fields than being validated with ajax
         * @param {Map} options user options
         */
        _buildPrompt: function(field, promptText, type, ajaxed, options) {

            // create the prompt
            var prompt = $('<div>');
            prompt.addClass(methods._getClassName(field.attr("id")) + "formError");
            // add a class name to identify the parent form of the prompt
            prompt.addClass("parentForm" + methods._getClassName(field.closest('form, .validationEngineContainer').attr("id")));
            prompt.addClass("formError");

            switch (type) {
                case "pass":
                    prompt.addClass("greenPopup");
                    break;
                case "load":
                    prompt.addClass("blackPopup");
                    break;
                default:
                    /* it has error  */
                    //alert("unknown popup type:"+type);
            }
            if (ajaxed)
                prompt.addClass("ajaxed");

            // create the prompt content
            var promptContent = $('<div>').addClass("formErrorContent").html(promptText).appendTo(prompt);

            // determine position type
            var positionType = field.data("promptPosition") || options.promptPosition;

            // create the css arrow pointing at the field
            // note that there is no triangle on max-checkbox and radio
            if (options.showArrow) {
                var arrow = $('<div>').addClass("formErrorArrow");

                //prompt positioning adjustment support. Usage: positionType:Xshift,Yshift (for ex.: bottomLeft:+20 or bottomLeft:-20,+10)
                if (typeof (positionType) === 'string')
                {
                    var pos = positionType.indexOf(":");
                    if (pos !== -1)
                        positionType = positionType.substring(0, pos);
                }

                switch (positionType) {
                    case "bottomLeft":
                    case "bottomRight":
                        prompt.find(".formErrorContent").before(arrow);
                        arrow.addClass("formErrorArrowBottom").html('<div class="line1"><!-- --></div><div class="line2"><!-- --></div><div class="line3"><!-- --></div><div class="line4"><!-- --></div><div class="line5"><!-- --></div><div class="line6"><!-- --></div><div class="line7"><!-- --></div><div class="line8"><!-- --></div><div class="line9"><!-- --></div><div class="line10"><!-- --></div>');
                        break;
                    case "topLeft":
                    case "topRight":
                        arrow.html('<div class="line10"><!-- --></div><div class="line9"><!-- --></div><div class="line8"><!-- --></div><div class="line7"><!-- --></div><div class="line6"><!-- --></div><div class="line5"><!-- --></div><div class="line4"><!-- --></div><div class="line3"><!-- --></div><div class="line2"><!-- --></div><div class="line1"><!-- --></div>');
                        prompt.append(arrow);
                        break;
                }
            }
            // Add custom prompt class
            if (options.addPromptClass)
                prompt.addClass(options.addPromptClass);

            // Add custom prompt class defined in element
            var requiredOverride = field.attr('data-required-class');
            if (requiredOverride !== undefined) {
                prompt.addClass(requiredOverride);
            } else {
                if (options.prettySelect) {
                    if ($('#' + field.attr('id')).next().is('select')) {
                        var prettyOverrideClass = $('#' + field.attr('id').substr(options.usePrefix.length).substring(options.useSuffix.length)).attr('data-required-class');
                        if (prettyOverrideClass !== undefined) {
                            prompt.addClass(prettyOverrideClass);
                        }
                    }
                }
            }

            prompt.css({
                "opacity": 0
            });
            if (positionType === 'inline') {
                prompt.addClass("inline");
                if (typeof field.attr('data-prompt-target') !== 'undefined' && $('#' + field.attr('data-prompt-target')).length > 0) {
                    prompt.appendTo($('#' + field.attr('data-prompt-target')));
                } else {
                    field.after(prompt);
                }
            } else {
                field.before(prompt);
            }

            var pos = methods._calculatePosition(field, prompt, options);
            prompt.css({
                'position': positionType === 'inline' ? 'relative' : 'absolute',
                "top": pos.callerTopPosition,
                "left": pos.callerleftPosition,
                "marginTop": pos.marginTopSize,
                "opacity": 0
            }).data("callerField", field);


            if (options.autoHidePrompt) {
                setTimeout(function() {
                    prompt.animate({
                        "opacity": 0
                    }, function() {
                        prompt.closest('.formErrorOuter').remove();
                        prompt.remove();
                    });
                }, options.autoHideDelay);
            }
            return prompt.animate({
                "opacity": 0.87
            });
        },
        /**
         * Updates the prompt text field - the field for which the prompt
         * @param {jqObject} field
         * @param {String} promptText html text to display type
         * @param {String} type the type of bubble: 'pass' (green), 'load' (black) anything else (red)
         * @param {boolean} ajaxed - use to mark fields than being validated with ajax
         * @param {Map} options user options
         */
        _updatePrompt: function(field, prompt, promptText, type, ajaxed, options, noAnimation) {

            if (prompt) {
                if (typeof type !== "undefined") {
                    if (type === "pass")
                        prompt.addClass("greenPopup");
                    else
                        prompt.removeClass("greenPopup");

                    if (type === "load")
                        prompt.addClass("blackPopup");
                    else
                        prompt.removeClass("blackPopup");
                }
                if (ajaxed)
                    prompt.addClass("ajaxed");
                else
                    prompt.removeClass("ajaxed");

                prompt.find(".formErrorContent").html(promptText);

                var pos = methods._calculatePosition(field, prompt, options);
                var css = {"top": pos.callerTopPosition,
                    "left": pos.callerleftPosition,
                    "marginTop": pos.marginTopSize};

                if (noAnimation)
                    prompt.css(css);
                else
                    prompt.animate(css);
            }
        },
        /**
         * Closes the prompt associated with the given field
         *
         * @param {jqObject}
         *            field
         */
        _closePrompt: function(field) {
            var prompt = methods._getPrompt(field);
            if (prompt)
                prompt.fadeTo("fast", 0, function() {
                    prompt.parent('.formErrorOuter').remove();
                    prompt.remove();
                });
        },
        closePrompt: function(field) {
            return methods._closePrompt(field);
        },
        /**
         * Returns the error prompt matching the field if any
         *
         * @param {jqObject}
         *            field
         * @return undefined or the error prompt (jqObject)
         */
        _getPrompt: function(field) {
            var formId = $(field).closest('form, .validationEngineContainer').attr('id');
            var className = methods._getClassName(field.attr("id")) + "formError";
            var match = $("." + methods._escapeExpression(className) + '.parentForm' + methods._getClassName(formId))[0];
            if (match)
                return $(match);
        },
        /**
         * Returns the escapade classname
         *
         * @param {selector}
         *            className
         */
        _escapeExpression: function(selector) {
            return selector.replace(/([#;&,\.\+\*\~':"\!\^$\[\]\(\)=>\|])/g, "\\$1");
        },
        /**
         * returns true if we are in a RTLed document
         *
         * @param {jqObject} field
         */
        isRTL: function(field)
        {
            var $document = $(document);
            var $body = $('body');
            var rtl =
                    (field && field.hasClass('rtl')) ||
                    (field && (field.attr('dir') || '').toLowerCase() === 'rtl') ||
                    $document.hasClass('rtl') ||
                    ($document.attr('dir') || '').toLowerCase() === 'rtl' ||
                    $body.hasClass('rtl') ||
                    ($body.attr('dir') || '').toLowerCase() === 'rtl';
            return Boolean(rtl);
        },
        /**
         * Calculates prompt position
         *
         * @param {jqObject}
         *            field
         * @param {jqObject}
         *            the prompt
         * @param {Map}
         *            options
         * @return positions
         */
        _calculatePosition: function(field, promptElmt, options) {

            var promptTopPosition, promptleftPosition, marginTopSize;
            var fieldWidth = field.width();
            var fieldLeft = field.position().left;
            var fieldTop = field.position().top;
            var fieldHeight = field.height();
            var promptHeight = promptElmt.height();


            // is the form contained in an overflown container?
            promptTopPosition = promptleftPosition = 0;
            // compensation for the arrow
            marginTopSize = -promptHeight;


            //prompt positioning adjustment support
            //now you can adjust prompt position
            //usage: positionType:Xshift,Yshift
            //for example:
            //   bottomLeft:+20 means bottomLeft position shifted by 20 pixels right horizontally
            //   topRight:20, -15 means topRight position shifted by 20 pixels to right and 15 pixels to top
            //You can use +pixels, - pixels. If no sign is provided than + is default.
            var positionType = field.data("promptPosition") || options.promptPosition;
            var shift1 = "";
            var shift2 = "";
            var shiftX = 0;
            var shiftY = 0;
            if (typeof (positionType) === 'string') {
                //do we have any position adjustments ?
                if (positionType.indexOf(":") !== -1) {
                    shift1 = positionType.substring(positionType.indexOf(":") + 1);
                    positionType = positionType.substring(0, positionType.indexOf(":"));

                    //if any advanced positioning will be needed (percents or something else) - parser should be added here
                    //for now we use simple parseInt()

                    //do we have second parameter?
                    if (shift1.indexOf(",") !== -1) {
                        shift2 = shift1.substring(shift1.indexOf(",") + 1);
                        shift1 = shift1.substring(0, shift1.indexOf(","));
                        shiftY = parseInt(shift2);
                        if (isNaN(shiftY))
                            shiftY = 0;
                    }
                    ;

                    shiftX = parseInt(shift1);
                    if (isNaN(shift1))
                        shift1 = 0;

                }
                ;
            }
            ;


            switch (positionType) {
                default:
                case "topRight":
                    promptleftPosition += fieldLeft + fieldWidth - 30;
                    promptTopPosition += fieldTop;
                    break;

                case "topLeft":
                    promptTopPosition += fieldTop;
                    promptleftPosition += fieldLeft;
                    break;

                case "centerRight":
                    promptTopPosition = fieldTop + 4;
                    marginTopSize = 0;
                    promptleftPosition = fieldLeft + field.outerWidth(true) + 5;
                    break;
                case "centerLeft":
                    promptleftPosition = fieldLeft - (promptElmt.width() + 2);
                    promptTopPosition = fieldTop + 4;
                    marginTopSize = 0;

                    break;

                case "bottomLeft":
                    promptTopPosition = fieldTop + field.height() + 5;
                    marginTopSize = 0;
                    promptleftPosition = fieldLeft;
                    break;
                case "bottomRight":
                    promptleftPosition = fieldLeft + fieldWidth - 30;
                    promptTopPosition = fieldTop + field.height() + 5;
                    marginTopSize = 0;
                    break;
                case "inline":
                    promptleftPosition = 0;
                    promptTopPosition = 0;
                    marginTopSize = 0;
            }
            ;



            //apply adjusments if any
            promptleftPosition += shiftX;
            promptTopPosition += shiftY;

            return {
                "callerTopPosition": promptTopPosition + "px",
                "callerleftPosition": promptleftPosition + "px",
                "marginTopSize": marginTopSize + "px"
            };
        },
        /**
         * Saves the user options and variables in the form.data
         *
         * @param {jqObject}
         *            form - the form where the user option should be saved
         * @param {Map}
         *            options - the user options
         * @return the user options (extended from the defaults)
         */
        _saveOptions: function(form, options) {

            // is there a language localisation ?
            if ($.validationEngineLanguage) {
                var allRules = $.validationEngineLanguage.allRules;
            }
            else
                $.error("jQuery.validationEngine rules are not loaded, plz add localization files to the page");
            // --- Internals DO NOT TOUCH or OVERLOAD ---
            // validation rules and i18
            $.validationEngine.defaults.allrules = allRules;

            var userOptions = $.extend(true, {}, $.validationEngine.defaults, options);

            form.data('jqv', userOptions);
            return userOptions;
        },
        /**
         * Removes forbidden characters from class name
         * @param {String} className
         */
        _getClassName: function(className) {
            if (className)
                return className.replace(/:/g, "_").replace(/\./g, "_");
        },
        /**
         * Escape special character for jQuery selector
         * http://totaldev.com/content/escaping-characters-get-valid-jquery-id
         * @param {String} selector
         */
        _jqSelector: function(str) {
            return str.replace(/([;&,\.\+\*\~':"\!\^#$%@\[\]\(\)=>\|])/g, '\\$1');
        },
        /**
         * Conditionally required field
         *
         * @param {jqObject} field
         * @param {Array[String]} rules
         * @param {int} i rules index
         * @param {Map}
         * user options
         * @return an error string if validation failed
         */
        _condRequired: function(field, rules, i, options) {
            var idx, dependingField;

            for (idx = (i + 1); idx < rules.length; idx++) {
                dependingField = jQuery("#" + rules[idx]).first();

                /* Use _required for determining wether dependingField has a value.
                 * There is logic there for handling all field types, and default value; so we won't replicate that here
                 * Indicate this special use by setting the last parameter to true so we only validate the dependingField on chackboxes and radio buttons (#462)
                 */
                if (dependingField.length && methods._required(dependingField, ["required"], 0, options, true) === undefined) {
                    /* We now know any of the depending fields has a value,
                     * so we can validate this field as per normal required code
                     */
                    return methods._required(field, ["required"], 0, options);
                }
            }
        },
        _submitButtonClick: function(event) {
            var button = $(this);
            var form = button.closest('form, .validationEngineContainer');
            form.data("jqv_submitButton", button.attr("id"));
        },
        _validGroupEmail: function(field, rules, i, options) {
            //var ex = /^((([a-z]|\d|[a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i;           
            var ex = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
            var pattern = new RegExp(ex);
            var _value=field.attr('value');
            var array=[];
            if(typeof _value!=='undefined')
               array = field.attr('value').split(',');
            var index = 0;
            while (index < array.length) {
                if (!pattern.test($.trim(array[index])))
                    return options.allrules[rules[i]].alertText;
                index++;
            }
        },
        _notAllowSpecialCharacters: function(field, rules, i, options) {
            //var ex= /^[@#!%*&_\/.:{}\[\]$=?\\(\"\'|)+~`^<>-]/;
            //var pattern = new RegExp(ex);
            var value = $.trim(field.attr('value'));
            var specialChars = "<>%*+[]?,:;|\\/=";
            var check = false;
            for (i = 0; i < specialChars.length; i++) {
                if (value.indexOf(specialChars[i]) > -1) {
                    //return true
                    check = true;
                    break;
                }
            }
            if (check) {
                //return options.allrules[rules[i]].alertText;
                return options.allrules.notAllowSpecialCharacters.alertText;
            }
        },
        _notAllowSpecialCharactersFolder: function(field, rules, i, options) {
            //var ex= /^[@#!%*&_\/.:{}\[\]$=?\\(\"\'|)+~`^<>-]/;
            //var pattern = new RegExp(ex);
            var value = $.trim(field.attr('value'));
            var specialChars = "/\\:,*?”<>|";
            var check = false;
            for (i = 0; i < specialChars.length; i++) {
                if (value.indexOf(specialChars[i]) > -1) {
                    //return true
                    check = true;
                    break;
                }
            }
            if (check) {
                //return options.allrules[rules[i]].alertText;
                return options.allrules.notAllowSpecialCharactersFolder.alertText;
            }
        }

    };

    /**
     * Plugin entry point.
     * You may pass an action as a parameter or a list of options.
     * if none, the init and attach methods are being called.
     * Remember: if you pass options, the attached method is NOT called automatically
     *
     * @param {String}
     *            method (optional) action
     */
    $.fn.validationEngine = function(method) {

        var form = $(this);
        if (!form[0])
            return form;  // stop here if the form does not exist

        if (typeof (method) === 'string' && method.charAt(0) !== '_' && methods[method]) {

            // make sure init is called once
            if (method !== "showPrompt" && method !== "hide" && method !== "hideAll")
                methods.init.apply(form);

            return methods[method].apply(form, Array.prototype.slice.call(arguments, 1));
        } else if (typeof method === 'object' || !method) {

            // default constructor with or without arguments
            methods.init.apply(form, arguments);
            return methods.attach.apply(form);
        } else {
            $.error('Method ' + method + ' does not exist in jQuery.validationEngine');
        }
    };

    // LEAK GLOBAL OPTIONS
    $.validationEngine = {fieldIdCounter: 0, defaults: {
            // Name of the event triggering field validation
            validationEventTrigger: "blur",
            // Automatically scroll viewport to the first error
            scroll: true,
            // Focus on the first input
            focusFirstField: true,
            // Show prompts, set to false to disable prompts
            showPrompts: true,
            // Should we attempt to validate non-visible input fields contained in the form? (Useful in cases of tabbed containers, e.g. jQuery-UI tabs)
            validateNonVisibleFields: false,
            // Opening box position, possible locations are: topLeft,
            // topRight, bottomLeft, centerRight, bottomRight, inline
            // inline gets inserted after the validated field or into an element specified in data-prompt-target
            promptPosition: "topRight",
            bindMethod: "bind",
            // internal, automatically set to true when it parse a _ajax rule
            inlineAjax: false,
            // if set to true, the form data is sent asynchronously via ajax to the form.action url (get)
            ajaxFormValidation: false,
            // The url to send the submit ajax validation (default to action)
            ajaxFormValidationURL: false,
            // HTTP method used for ajax validation
            ajaxFormValidationMethod: 'get',
            // Ajax form validation callback method: boolean onComplete(form, status, errors, options)
            // retuns false if the form.submit event needs to be canceled.
            onAjaxFormComplete: $.noop,
            // called right before the ajax call, may return false to cancel
            onBeforeAjaxFormValidation: $.noop,
            // Stops form from submitting and execute function assiciated with it
            onValidationComplete: false,
            // Used when you have a form fields too close and the errors messages are on top of other disturbing viewing messages
            doNotShowAllErrosOnSubmit: false,
            // Object where you store custom messages to override the default error messages
            custom_error_messages: {},
            // true if you want to vind the input fields
            binded: true,
            // set to true, when the prompt arrow needs to be displayed
            showArrow: true,
            // did one of the validation fail ? kept global to stop further ajax validations
            isError: false,
            // Limit how many displayed errors a field can have
            maxErrorsPerField: false,
            // Caches field validation status, typically only bad status are created.
            // the array is used during ajax form validation to detect issues early and prevent an expensive submit
            ajaxValidCache: {},
            // Auto update prompt position after window resize
            autoPositionUpdate: false,
            InvalidFields: [],
            onFieldSuccess: false,
            onFieldFailure: false,
            onSuccess: false,
            onFailure: false,
            validateAttribute: "class",
            addSuccessCssClassToField: "",
            addFailureCssClassToField: "",
            // Auto-hide prompt
            autoHidePrompt: false,
            // Delay before auto-hide
            autoHideDelay: 10000,
            // Fade out duration while hiding the validations
            fadeDuration: 0.3,
            // Use Prettify select library
            prettySelect: false,
            // Add css class on prompt
            addPromptClass: "",
            // Custom ID uses prefix
            usePrefix: "",
            // Custom ID uses suffix
            useSuffix: "",
            // Only show one message per error prompt
            showOneMessage: false
        }};

    $(function() {
        $.validationEngine.defaults.promptPosition = methods.isRTL() ? 'topLeft' : "topRight";
    });

})(jQuery);

/* Placeholder support IE8+ */
(function(window, document, $) {

    // Opera Mini v7 doesn’t support placeholder although its DOM seems to indicate so
    var isOperaMini = Object.prototype.toString.call(window.operamini) == '[object OperaMini]';
    var isInputSupported = 'placeholder' in document.createElement('input') && !isOperaMini;
    var isTextareaSupported = 'placeholder' in document.createElement('textarea') && !isOperaMini;
    var prototype = $.fn;
    var valHooks = $.valHooks;
    var propHooks = $.propHooks;
    var hooks;
    var placeholder;

    if (isInputSupported && isTextareaSupported) {

        placeholder = prototype.placeholder = function() {
            return this;
        };

        placeholder.input = placeholder.textarea = true;

    } else {

        placeholder = prototype.placeholder = function() {
            var $this = this;
            $this
                    .filter((isInputSupported ? 'textarea' : ':input') + '[placeholder]')
                    .not('.placeholder')
                    .bind({
                        'focus.placeholder': clearPlaceholder,
                        'blur.placeholder': setPlaceholder
                    })
                    .data('placeholder-enabled', true)
                    .trigger('blur.placeholder');
            return $this;
        };

        placeholder.input = isInputSupported;
        placeholder.textarea = isTextareaSupported;

        hooks = {
            'get': function(element) {
                var $element = $(element);

                var $passwordInput = $element.data('placeholder-password');
                if ($passwordInput) {
                    return $passwordInput[0].value;
                }

                return $element.data('placeholder-enabled') && $element.hasClass('placeholder') ? '' : element.value;
            },
            'set': function(element, value) {
                var $element = $(element);

                var $passwordInput = $element.data('placeholder-password');
                if ($passwordInput) {
                    return $passwordInput[0].value = value;
                }

                if (!$element.data('placeholder-enabled')) {
                    return element.value = value;
                }
                if (value == '') {
                    element.value = value;
                    // Issue #56: Setting the placeholder causes problems if the element continues to have focus.
                    if (element != safeActiveElement()) {
                        // We can't use `triggerHandler` here because of dummy text/password inputs :(
                        setPlaceholder.call(element);
                    }
                } else if ($element.hasClass('placeholder')) {
                    clearPlaceholder.call(element, true, value) || (element.value = value);
                } else {
                    element.value = value;
                }
                // `set` can not return `undefined`; see http://jsapi.info/jquery/1.7.1/val#L2363
                return $element;
            }
        };

        if (!isInputSupported) {
            valHooks.input = hooks;
            propHooks.value = hooks;
        }
        if (!isTextareaSupported) {
            valHooks.textarea = hooks;
            propHooks.value = hooks;
        }

        $(function() {
            // Look for forms
            $(document).delegate('form', 'submit.placeholder', function() {
                // Clear the placeholder values so they don't get submitted
                var $inputs = $('.placeholder', this).each(clearPlaceholder);
                setTimeout(function() {
                    $inputs.each(setPlaceholder);
                }, 10);
            });
        });

        // Clear placeholder values upon page reload
        $(window).bind('beforeunload.placeholder', function() {
            $('.placeholder').each(function() {
                this.value = '';
            });
        });

    }

    function args(elem) {
        // Return an object of element attributes
        var newAttrs = {};
        var rinlinejQuery = /^jQuery\d+$/;
        $.each(elem.attributes, function(i, attr) {
            if (attr.specified && !rinlinejQuery.test(attr.name)) {
                newAttrs[attr.name] = attr.value;
            }
        });
        return newAttrs;
    }

    function clearPlaceholder(event, value) {
        var input = this;
        var $input = $(input);
        if (input.value == $input.attr('placeholder') && $input.hasClass('placeholder')) {
            if ($input.data('placeholder-password')) {
                $input = $input.hide().next().show().attr('id', $input.removeAttr('id').data('placeholder-id'));
                // If `clearPlaceholder` was called from `$.valHooks.input.set`
                if (event === true) {
                    return $input[0].value = value;
                }
                $input.focus();
            } else {
                input.value = '';
                $input.removeClass('placeholder');
                input == safeActiveElement() && input.select();
            }
        }
    }

    function setPlaceholder() {
        var $replacement;
        var input = this;
        var $input = $(input);
        var id = this.id;
        if (input.value == '') {
            if (input.type == 'password') {
                if (!$input.data('placeholder-textinput')) {
                    try {
                        $replacement = $input.clone().attr({'type': 'text'});
                    } catch (e) {
                        $replacement = $('<input>').attr($.extend(args(this), {'type': 'text'}));
                    }
                    $replacement
                            .removeAttr('name')
                            .data({
                                'placeholder-password': $input,
                                'placeholder-id': id
                            })
                            .bind('focus.placeholder', clearPlaceholder);
                    $input
                            .data({
                                'placeholder-textinput': $replacement,
                                'placeholder-id': id
                            })
                            .before($replacement);
                }
                $input = $input.removeAttr('id').hide().prev().attr('id', id).show();
                // Note: `$input[0] != input` now!
            }
            $input.addClass('placeholder');
            $input[0].value = $input.attr('placeholder');
        } else {
            $input.removeClass('placeholder');
        }
    }

    function safeActiveElement() {
        // Avoid IE9 `document.activeElement` of death
        // https://github.com/mathiasbynens/jquery-placeholder/pull/99
        try {
            return document.activeElement;
        } catch (exception) {
        }
    }

}(this, document, jQuery));

/*
 == malihu jquery custom scrollbars plugin == 
 version: 2.8.2 
 author: malihu (http://manos.malihu.gr) 
 plugin home: http://manos.malihu.gr/jquery-custom-content-scroller 
 */

/*
 Copyright 2010-2013 Manos Malihutsakis 
 
 This program is free software: you can redistribute it and/or modify 
 it under the terms of the GNU Lesser General Public License as published by 
 the Free Software Foundation, either version 3 of the License, or 
 any later version. 
 
 This program is distributed in the hope that it will be useful, 
 but WITHOUT ANY WARRANTY; without even the implied warranty of 
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 GNU Lesser General Public License for more details. 
 
 You should have received a copy of the GNU Lesser General Public License 
 along with this program.  If not, see http://www.gnu.org/licenses/lgpl.html. 
 */
//(function($) {
//    /*plugin script*/
//    var methods = {
//        init: function(options) {
//            var defaults = {
//                set_width: false, /*optional element width: boolean, pixels, percentage*/
//                set_height: false, /*optional element height: boolean, pixels, percentage*/
//                horizontalScroll: false, /*scroll horizontally: boolean*/
//                scrollInertia: 950, /*scrolling inertia: integer (milliseconds)*/
//                mouseWheel: true, /*mousewheel support: boolean*/
//                mouseWheelPixels: "auto", /*mousewheel pixels amount: integer, "auto"*/
//                autoDraggerLength: true, /*auto-adjust scrollbar dragger length: boolean*/
//                autoHideScrollbar: false, /*auto-hide scrollbar when idle*/
//                snapAmount: null, /* optional element always snaps to a multiple of this number in pixels */
//                snapOffset: 0, /* when snapping, snap with this number in pixels as an offset */
//                scrollButtons: {/*scroll buttons*/
//                    enable: false, /*scroll buttons support: boolean*/
//                    scrollType: "continuous", /*scroll buttons scrolling type: "continuous", "pixels"*/
//                    scrollSpeed: "auto", /*scroll buttons continuous scrolling speed: integer, "auto"*/
//                    scrollAmount: 40 /*scroll buttons pixels scroll amount: integer (pixels)*/
//                },
//                advanced: {
//                    updateOnBrowserResize: true, /*update scrollbars on browser resize (for layouts based on percentages): boolean*/
//                    updateOnContentResize: false, /*auto-update scrollbars on content resize (for dynamic content): boolean*/
//                    autoExpandHorizontalScroll: false, /*auto-expand width for horizontal scrolling: boolean*/
//                    autoScrollOnFocus: true, /*auto-scroll on focused elements: boolean*/
//                    normalizeMouseWheelDelta: false /*normalize mouse-wheel delta (-1/1)*/
//                },
//                contentTouchScroll: true, /*scrolling by touch-swipe content: boolean*/
//                callbacks: {
//                    onScrollStart: function() {
//                    }, /*user custom callback function on scroll start event*/
//                    onScroll: function() {
//                    }, /*user custom callback function on scroll event*/
//                    onTotalScroll: function() {
//                    }, /*user custom callback function on scroll end reached event*/
//                    onTotalScrollBack: function() {
//                    }, /*user custom callback function on scroll begin reached event*/
//                    onTotalScrollOffset: 0, /*scroll end reached offset: integer (pixels)*/
//                    onTotalScrollBackOffset: 0, /*scroll begin reached offset: integer (pixels)*/
//                    whileScrolling: function() {
//                    } /*user custom callback function on scrolling event*/
//                },
//                theme: "light" /*"light", "dark", "light-2", "dark-2", "light-thick", "dark-thick", "light-thin", "dark-thin"*/
//            },
//            options = $.extend(true, defaults, options);
//            return this.each(function() {
//                var $this = $(this);
//                /*set element width/height, create markup for custom scrollbars, add classes*/
//                if (options.set_width) {
//                    $this.css("width", options.set_width);
//                }
//                if (options.set_height) {
//                    $this.css("height", options.set_height);
//                }
//                if (!$(document).data("mCustomScrollbar-index")) {
//                    $(document).data("mCustomScrollbar-index", "1");
//                } else {
//                    var mCustomScrollbarIndex = parseInt($(document).data("mCustomScrollbar-index"));
//                    $(document).data("mCustomScrollbar-index", mCustomScrollbarIndex + 1);
//                }
//                $this.wrapInner("<div class='mCustomScrollBox" + " mCS-" + options.theme + "' id='mCSB_" + $(document).data("mCustomScrollbar-index") + "' style='position:relative; height:100%; overflow:hidden; max-width:100%;' />").addClass("mCustomScrollbar _mCS_" + $(document).data("mCustomScrollbar-index"));
//                var mCustomScrollBox = $this.children(".mCustomScrollBox");
//                if (options.horizontalScroll) {
//                    mCustomScrollBox.addClass("mCSB_horizontal").wrapInner("<div class='mCSB_h_wrapper' style='position:relative; left:0; width:999999px;' />");
//                    var mCSB_h_wrapper = mCustomScrollBox.children(".mCSB_h_wrapper");
//                    mCSB_h_wrapper.wrapInner("<div class='mCSB_container' style='position:absolute; left:0;' />").children(".mCSB_container").css({"width": mCSB_h_wrapper.children().outerWidth(), "position": "relative"}).unwrap();
//                } else {
//                    mCustomScrollBox.wrapInner("<div class='mCSB_container' style='position:relative; top:0;' />");
//                }
//                var mCSB_container = mCustomScrollBox.children(".mCSB_container");
//                if ($.support.touch) {
//                    mCSB_container.addClass("mCS_touch");
//                }
//                mCSB_container.after("<div class='mCSB_scrollTools' style='position:absolute;'><div class='mCSB_draggerContainer'><div class='mCSB_dragger' style='position:absolute;' oncontextmenu='return false;'><div class='mCSB_dragger_bar' style='position:relative;'></div></div><div class='mCSB_draggerRail'></div></div></div>");
//                var mCSB_scrollTools = mCustomScrollBox.children(".mCSB_scrollTools"),
//                        mCSB_draggerContainer = mCSB_scrollTools.children(".mCSB_draggerContainer"),
//                        mCSB_dragger = mCSB_draggerContainer.children(".mCSB_dragger");
//                if (options.horizontalScroll) {
//                    mCSB_dragger.data("minDraggerWidth", mCSB_dragger.width());
//                } else {
//                    mCSB_dragger.data("minDraggerHeight", mCSB_dragger.height());
//                }
//                if (options.scrollButtons.enable) {
//                    if (options.horizontalScroll) {
//                        mCSB_scrollTools.prepend("<a class='mCSB_buttonLeft' oncontextmenu='return false;'></a>").append("<a class='mCSB_buttonRight' oncontextmenu='return false;'></a>");
//                    } else {
//                        mCSB_scrollTools.prepend("<a class='mCSB_buttonUp' oncontextmenu='return false;'></a>").append("<a class='mCSB_buttonDown' oncontextmenu='return false;'></a>");
//                    }
//                }
//                /*mCustomScrollBox scrollTop and scrollLeft is always 0 to prevent browser focus scrolling*/
//                mCustomScrollBox.bind("scroll", function() {
//                    if (!$this.is(".mCS_disabled")) { /*native focus scrolling for disabled scrollbars*/
//                        mCustomScrollBox.scrollTop(0).scrollLeft(0);
//                    }
//                });
//                /*store options, global vars/states, intervals*/
//                $this.data({
//                    /*init state*/
//                    "mCS_Init": true,
//                    /*instance index*/
//                    "mCustomScrollbarIndex": $(document).data("mCustomScrollbar-index"),
//                    /*option parameters*/
//                    "horizontalScroll": options.horizontalScroll,
//                    "scrollInertia": options.scrollInertia,
//                    "scrollEasing": "mcsEaseOut",
//                    "mouseWheel": options.mouseWheel,
//                    "mouseWheelPixels": options.mouseWheelPixels,
//                    "autoDraggerLength": options.autoDraggerLength,
//                    "autoHideScrollbar": options.autoHideScrollbar,
//                    "snapAmount": options.snapAmount,
//                    "snapOffset": options.snapOffset,
//                    "scrollButtons_enable": options.scrollButtons.enable,
//                    "scrollButtons_scrollType": options.scrollButtons.scrollType,
//                    "scrollButtons_scrollSpeed": options.scrollButtons.scrollSpeed,
//                    "scrollButtons_scrollAmount": options.scrollButtons.scrollAmount,
//                    "autoExpandHorizontalScroll": options.advanced.autoExpandHorizontalScroll,
//                    "autoScrollOnFocus": options.advanced.autoScrollOnFocus,
//                    "normalizeMouseWheelDelta": options.advanced.normalizeMouseWheelDelta,
//                    "contentTouchScroll": options.contentTouchScroll,
//                    "onScrollStart_Callback": options.callbacks.onScrollStart,
//                    "onScroll_Callback": options.callbacks.onScroll,
//                    "onTotalScroll_Callback": options.callbacks.onTotalScroll,
//                    "onTotalScrollBack_Callback": options.callbacks.onTotalScrollBack,
//                    "onTotalScroll_Offset": options.callbacks.onTotalScrollOffset,
//                    "onTotalScrollBack_Offset": options.callbacks.onTotalScrollBackOffset,
//                    "whileScrolling_Callback": options.callbacks.whileScrolling,
//                    /*events binding state*/
//                    "bindEvent_scrollbar_drag": false,
//                    "bindEvent_content_touch": false,
//                    "bindEvent_scrollbar_click": false,
//                    "bindEvent_mousewheel": false,
//                    "bindEvent_buttonsContinuous_y": false,
//                    "bindEvent_buttonsContinuous_x": false,
//                    "bindEvent_buttonsPixels_y": false,
//                    "bindEvent_buttonsPixels_x": false,
//                    "bindEvent_focusin": false,
//                    "bindEvent_autoHideScrollbar": false,
//                    /*buttons intervals*/
//                    "mCSB_buttonScrollRight": false,
//                    "mCSB_buttonScrollLeft": false,
//                    "mCSB_buttonScrollDown": false,
//                    "mCSB_buttonScrollUp": false
//                });
//                /*max-width/max-height*/
//                if (options.horizontalScroll) {
//                    if ($this.css("max-width") !== "none") {
//                        if (!options.advanced.updateOnContentResize) { /*needs updateOnContentResize*/
//                            options.advanced.updateOnContentResize = true;
//                        }
//                    }
//                } else {
//                    if ($this.css("max-height") !== "none") {
//                        var percentage = false, maxHeight = parseInt($this.css("max-height"));
//                        if ($this.css("max-height").indexOf("%") >= 0) {
//                            percentage = maxHeight,
//                                    maxHeight = $this.parent().height() * percentage / 100;
//                        }
//                        $this.css("overflow", "hidden");
//                        mCustomScrollBox.css("max-height", maxHeight);
//                    }
//                }
//                $this.mCustomScrollbar("update");
//                /*window resize fn (for layouts based on percentages)*/
//                if (options.advanced.updateOnBrowserResize) {
//                    var mCSB_resizeTimeout, currWinWidth = $(window).width(), currWinHeight = $(window).height();
//                    $(window).bind("resize." + $this.data("mCustomScrollbarIndex"), function() {
//                        if (mCSB_resizeTimeout) {
//                            clearTimeout(mCSB_resizeTimeout);
//                        }
//                        mCSB_resizeTimeout = setTimeout(function() {
//                            if (!$this.is(".mCS_disabled") && !$this.is(".mCS_destroyed")) {
//                                var winWidth = $(window).width(), winHeight = $(window).height();
//                                if (currWinWidth !== winWidth || currWinHeight !== winHeight) { /*ie8 fix*/
//                                    if ($this.css("max-height") !== "none" && percentage) {
//                                        mCustomScrollBox.css("max-height", $this.parent().height() * percentage / 100);
//                                    }
//                                    $this.mCustomScrollbar("update");
//                                    currWinWidth = winWidth;
//                                    currWinHeight = winHeight;
//                                }
//                            }
//                        }, 150);
//                    });
//                }
//                /*content resize fn (for dynamically generated content)*/
//                if (options.advanced.updateOnContentResize) {
//                    var mCSB_onContentResize;
//                    if (options.horizontalScroll) {
//                        var mCSB_containerOldSize = mCSB_container.outerWidth();
//                    } else {
//                        var mCSB_containerOldSize = mCSB_container.outerHeight();
//                    }
//                    mCSB_onContentResize = setInterval(function() {
//                        if (options.horizontalScroll) {
//                            if (options.advanced.autoExpandHorizontalScroll) {
//                                mCSB_container.css({"position": "absolute", "width": "auto"}).wrap("<div class='mCSB_h_wrapper' style='position:relative; left:0; width:999999px;' />").css({"width": mCSB_container.outerWidth(), "position": "relative"}).unwrap();
//                            }
//                            var mCSB_containerNewSize = mCSB_container.outerWidth();
//                        } else {
//                            var mCSB_containerNewSize = mCSB_container.outerHeight();
//                        }
//                        if (mCSB_containerNewSize != mCSB_containerOldSize) {
//                            $this.mCustomScrollbar("update");
//                            mCSB_containerOldSize = mCSB_containerNewSize;
//                        }
//                    }, 300);
//                }
//            });
//        },
//        update: function() {
//            var $this = $(this),
//                    mCustomScrollBox = $this.children(".mCustomScrollBox"),
//                    mCSB_container = mCustomScrollBox.children(".mCSB_container");
//            mCSB_container.removeClass("mCS_no_scrollbar");
//            $this.removeClass("mCS_disabled mCS_destroyed");
//            mCustomScrollBox.scrollTop(0).scrollLeft(0); /*reset scrollTop/scrollLeft to prevent browser focus scrolling*/
//            var mCSB_scrollTools = mCustomScrollBox.children(".mCSB_scrollTools"),
//                    mCSB_draggerContainer = mCSB_scrollTools.children(".mCSB_draggerContainer"),
//                    mCSB_dragger = mCSB_draggerContainer.children(".mCSB_dragger");
//            if ($this.data("horizontalScroll")) {
//                var mCSB_buttonLeft = mCSB_scrollTools.children(".mCSB_buttonLeft"),
//                        mCSB_buttonRight = mCSB_scrollTools.children(".mCSB_buttonRight"),
//                        mCustomScrollBoxW = mCustomScrollBox.width();
//                if ($this.data("autoExpandHorizontalScroll")) {
//                    mCSB_container.css({"position": "absolute", "width": "auto"}).wrap("<div class='mCSB_h_wrapper' style='position:relative; left:0; width:999999px;' />").css({"width": mCSB_container.outerWidth(), "position": "relative"}).unwrap();
//                }
//                var mCSB_containerW = mCSB_container.outerWidth();
//            } else {
//                var mCSB_buttonUp = mCSB_scrollTools.children(".mCSB_buttonUp"),
//                        mCSB_buttonDown = mCSB_scrollTools.children(".mCSB_buttonDown"),
//                        mCustomScrollBoxH = mCustomScrollBox.height(),
//                        mCSB_containerH = mCSB_container.outerHeight();
//            }
//            if (mCSB_containerH > mCustomScrollBoxH && !$this.data("horizontalScroll")) { /*content needs vertical scrolling*/
//                mCSB_scrollTools.css("display", "block");
//                var mCSB_draggerContainerH = mCSB_draggerContainer.height();
//                /*auto adjust scrollbar dragger length analogous to content*/
//                if ($this.data("autoDraggerLength")) {
//                    var draggerH = Math.round(mCustomScrollBoxH / mCSB_containerH * mCSB_draggerContainerH),
//                            minDraggerH = mCSB_dragger.data("minDraggerHeight");
//                    if (draggerH <= minDraggerH) { /*min dragger height*/
//                        mCSB_dragger.css({"height": minDraggerH});
//                    } else if (draggerH >= mCSB_draggerContainerH - 10) { /*max dragger height*/
//                        var mCSB_draggerContainerMaxH = mCSB_draggerContainerH - 10;
//                        mCSB_dragger.css({"height": mCSB_draggerContainerMaxH});
//                    } else {
//                        mCSB_dragger.css({"height": draggerH});
//                    }
//                    mCSB_dragger.children(".mCSB_dragger_bar").css({"line-height": mCSB_dragger.height() + "px"});
//                }
//                var mCSB_draggerH = mCSB_dragger.height(),
//                        /*calculate and store scroll amount, add scrolling*/
//                        scrollAmount = (mCSB_containerH - mCustomScrollBoxH) / (mCSB_draggerContainerH - mCSB_draggerH);
//                $this.data("scrollAmount", scrollAmount).mCustomScrollbar("scrolling", mCustomScrollBox, mCSB_container, mCSB_draggerContainer, mCSB_dragger, mCSB_buttonUp, mCSB_buttonDown, mCSB_buttonLeft, mCSB_buttonRight);
//                /*scroll*/
//                var mCSB_containerP = Math.abs(mCSB_container.position().top);
//                $this.mCustomScrollbar("scrollTo", mCSB_containerP, {scrollInertia: 0, trigger: "internal"});
//            } else if (mCSB_containerW > mCustomScrollBoxW && $this.data("horizontalScroll")) { /*content needs horizontal scrolling*/
//                mCSB_scrollTools.css("display", "block");
//                var mCSB_draggerContainerW = mCSB_draggerContainer.width();
//                /*auto adjust scrollbar dragger length analogous to content*/
//                if ($this.data("autoDraggerLength")) {
//                    var draggerW = Math.round(mCustomScrollBoxW / mCSB_containerW * mCSB_draggerContainerW),
//                            minDraggerW = mCSB_dragger.data("minDraggerWidth");
//                    if (draggerW <= minDraggerW) { /*min dragger height*/
//                        mCSB_dragger.css({"width": minDraggerW});
//                    } else if (draggerW >= mCSB_draggerContainerW - 10) { /*max dragger height*/
//                        var mCSB_draggerContainerMaxW = mCSB_draggerContainerW - 10;
//                        mCSB_dragger.css({"width": mCSB_draggerContainerMaxW});
//                    } else {
//                        mCSB_dragger.css({"width": draggerW});
//                    }
//                }
//                var mCSB_draggerW = mCSB_dragger.width(),
//                        /*calculate and store scroll amount, add scrolling*/
//                        scrollAmount = (mCSB_containerW - mCustomScrollBoxW) / (mCSB_draggerContainerW - mCSB_draggerW);
//                $this.data("scrollAmount", scrollAmount).mCustomScrollbar("scrolling", mCustomScrollBox, mCSB_container, mCSB_draggerContainer, mCSB_dragger, mCSB_buttonUp, mCSB_buttonDown, mCSB_buttonLeft, mCSB_buttonRight);
//                /*scroll*/
//                var mCSB_containerP = Math.abs(mCSB_container.position().left);
//                $this.mCustomScrollbar("scrollTo", mCSB_containerP, {scrollInertia: 0, trigger: "internal"});
//            } else { /*content does not need scrolling*/
//                /*unbind events, reset content position, hide scrollbars, remove classes*/
//                mCustomScrollBox.unbind("mousewheel focusin");
//                if ($this.data("horizontalScroll")) {
//                    mCSB_dragger.add(mCSB_container).css("left", 0);
//                } else {
//                    mCSB_dragger.add(mCSB_container).css("top", 0);
//                }
//                mCSB_scrollTools.css("display", "none");
//                mCSB_container.addClass("mCS_no_scrollbar");
//                $this.data({"bindEvent_mousewheel": false, "bindEvent_focusin": false});
//            }
//        },
//        scrolling: function(mCustomScrollBox, mCSB_container, mCSB_draggerContainer, mCSB_dragger, mCSB_buttonUp, mCSB_buttonDown, mCSB_buttonLeft, mCSB_buttonRight) {
//            var $this = $(this);
//            /*scrollbar drag scrolling*/
//            if (!$this.data("bindEvent_scrollbar_drag")) {
//                var mCSB_draggerDragY, mCSB_draggerDragX;
//                if ($.support.msPointer) { /*MSPointer*/
//                    mCSB_dragger.bind("MSPointerDown", function(e) {
//                        e.preventDefault();
//                        $this.data({"on_drag": true});
//                        mCSB_dragger.addClass("mCSB_dragger_onDrag");
//                        var elem = $(this),
//                                elemOffset = elem.offset(),
//                                x = e.originalEvent.pageX - elemOffset.left,
//                                y = e.originalEvent.pageY - elemOffset.top;
//                        if (x < elem.width() && x > 0 && y < elem.height() && y > 0) {
//                            mCSB_draggerDragY = y;
//                            mCSB_draggerDragX = x;
//                        }
//                    });
//                    $(document).bind("MSPointerMove." + $this.data("mCustomScrollbarIndex"), function(e) {
//                        e.preventDefault();
//                        if ($this.data("on_drag")) {
//                            var elem = mCSB_dragger,
//                                    elemOffset = elem.offset(),
//                                    x = e.originalEvent.pageX - elemOffset.left,
//                                    y = e.originalEvent.pageY - elemOffset.top;
//                            scrollbarDrag(mCSB_draggerDragY, mCSB_draggerDragX, y, x);
//                        }
//                    }).bind("MSPointerUp." + $this.data("mCustomScrollbarIndex"), function(e) {
//                        $this.data({"on_drag": false});
//                        mCSB_dragger.removeClass("mCSB_dragger_onDrag");
//                    });
//                } else { /*mouse/touch*/
//                    mCSB_dragger.bind("mousedown touchstart", function(e) {
//                        e.preventDefault();
//                        e.stopImmediatePropagation();
//                        var elem = $(this), elemOffset = elem.offset(), x, y;
//                        if (e.type === "touchstart") {
//                            var touch = e.originalEvent.touches[0] || e.originalEvent.changedTouches[0];
//                            x = touch.pageX - elemOffset.left;
//                            y = touch.pageY - elemOffset.top;
//                        } else {
//                            $this.data({"on_drag": true});
//                            mCSB_dragger.addClass("mCSB_dragger_onDrag");
//                            x = e.pageX - elemOffset.left;
//                            y = e.pageY - elemOffset.top;
//                        }
//                        if (x < elem.width() && x > 0 && y < elem.height() && y > 0) {
//                            mCSB_draggerDragY = y;
//                            mCSB_draggerDragX = x;
//                        }
//                    }).bind("touchmove", function(e) {
//                        e.preventDefault();
//                        e.stopImmediatePropagation();
//                        var touch = e.originalEvent.touches[0] || e.originalEvent.changedTouches[0],
//                                elem = $(this),
//                                elemOffset = elem.offset(),
//                                x = touch.pageX - elemOffset.left,
//                                y = touch.pageY - elemOffset.top;
//                        scrollbarDrag(mCSB_draggerDragY, mCSB_draggerDragX, y, x);
//                    });
//                    $(document).bind("mousemove." + $this.data("mCustomScrollbarIndex"), function(e) {
//                        if ($this.data("on_drag")) {
//                            var elem = mCSB_dragger,
//                                    elemOffset = elem.offset(),
//                                    x = e.pageX - elemOffset.left,
//                                    y = e.pageY - elemOffset.top;
//                            scrollbarDrag(mCSB_draggerDragY, mCSB_draggerDragX, y, x);
//                        }
//                    }).bind("mouseup." + $this.data("mCustomScrollbarIndex"), function(e) {
//                        $this.data({"on_drag": false});
//                        mCSB_dragger.removeClass("mCSB_dragger_onDrag");
//                    });
//                }
//                $this.data({"bindEvent_scrollbar_drag": true});
//            }
//            function scrollbarDrag(mCSB_draggerDragY, mCSB_draggerDragX, y, x) {
//                if ($this.data("horizontalScroll")) {
//                    $this.mCustomScrollbar("scrollTo", (mCSB_dragger.position().left - (mCSB_draggerDragX)) + x, {moveDragger: true, trigger: "internal"});
//                } else {
//                    $this.mCustomScrollbar("scrollTo", (mCSB_dragger.position().top - (mCSB_draggerDragY)) + y, {moveDragger: true, trigger: "internal"});
//                }
//            }
//            /*content touch-drag*/
//            if ($.support.touch && $this.data("contentTouchScroll")) {
//                if (!$this.data("bindEvent_content_touch")) {
//                    var touch,
//                            elem, elemOffset, y, x, mCSB_containerTouchY, mCSB_containerTouchX;
//                    mCSB_container.bind("touchstart", function(e) {
//                        e.stopImmediatePropagation();
//                        touch = e.originalEvent.touches[0] || e.originalEvent.changedTouches[0];
//                        elem = $(this);
//                        elemOffset = elem.offset();
//                        x = touch.pageX - elemOffset.left;
//                        y = touch.pageY - elemOffset.top;
//                        mCSB_containerTouchY = y;
//                        mCSB_containerTouchX = x;
//                    });
//                    mCSB_container.bind("touchmove", function(e) {
//                        e.preventDefault();
//                        e.stopImmediatePropagation();
//                        touch = e.originalEvent.touches[0] || e.originalEvent.changedTouches[0];
//                        elem = $(this).parent();
//                        elemOffset = elem.offset();
//                        x = touch.pageX - elemOffset.left;
//                        y = touch.pageY - elemOffset.top;
//                        if ($this.data("horizontalScroll")) {
//                            $this.mCustomScrollbar("scrollTo", mCSB_containerTouchX - x, {trigger: "internal"});
//                        } else {
//                            $this.mCustomScrollbar("scrollTo", mCSB_containerTouchY - y, {trigger: "internal"});
//                        }
//                    });
//                }
//            }
//            /*dragger rail click scrolling*/
//            if (!$this.data("bindEvent_scrollbar_click")) {
//                mCSB_draggerContainer.bind("click", function(e) {
//                    var scrollToPos = (e.pageY - mCSB_draggerContainer.offset().top) * $this.data("scrollAmount"), target = $(e.target);
//                    if ($this.data("horizontalScroll")) {
//                        scrollToPos = (e.pageX - mCSB_draggerContainer.offset().left) * $this.data("scrollAmount");
//                    }
//                    if (target.hasClass("mCSB_draggerContainer") || target.hasClass("mCSB_draggerRail")) {
//                        $this.mCustomScrollbar("scrollTo", scrollToPos, {trigger: "internal", scrollEasing: "draggerRailEase"});
//                    }
//                });
//                $this.data({"bindEvent_scrollbar_click": true});
//            }
//            /*mousewheel scrolling*/
//            if ($this.data("mouseWheel")) {
//                if (!$this.data("bindEvent_mousewheel")) {
//                    mCustomScrollBox.bind("mousewheel", function(e, delta) {
//                        var scrollTo, mouseWheelPixels = $this.data("mouseWheelPixels"), absPos = Math.abs(mCSB_container.position().top),
//                                draggerPos = mCSB_dragger.position().top, limit = mCSB_draggerContainer.height() - mCSB_dragger.height();
//                        if ($this.data("normalizeMouseWheelDelta")) {
//                            if (delta < 0) {
//                                delta = -1;
//                            } else {
//                                delta = 1;
//                            }
//                        }
//                        if (mouseWheelPixels === "auto") {
//                            mouseWheelPixels = 100 + Math.round($this.data("scrollAmount") / 2);
//                        }
//                        if ($this.data("horizontalScroll")) {
//                            draggerPos = mCSB_dragger.position().left;
//                            limit = mCSB_draggerContainer.width() - mCSB_dragger.width();
//                            absPos = Math.abs(mCSB_container.position().left);
//                        }
//                        if ((delta > 0 && draggerPos !== 0) || (delta < 0 && draggerPos !== limit)) {
//                            e.preventDefault();
//                            e.stopImmediatePropagation();
//                        }
//                        scrollTo = absPos - (delta * mouseWheelPixels);
//                        $this.mCustomScrollbar("scrollTo", scrollTo, {trigger: "internal"});
//                    });
//                    $this.data({"bindEvent_mousewheel": true});
//                }
//            }
//            /*buttons scrolling*/
//            if ($this.data("scrollButtons_enable")) {
//                if ($this.data("scrollButtons_scrollType") === "pixels") { /*scroll by pixels*/
//                    if ($this.data("horizontalScroll")) {
//                        mCSB_buttonRight.add(mCSB_buttonLeft).unbind("mousedown touchstart MSPointerDown mouseup MSPointerUp mouseout MSPointerOut touchend", mCSB_buttonRight_stop, mCSB_buttonLeft_stop);
//                        $this.data({"bindEvent_buttonsContinuous_x": false});
//                        if (!$this.data("bindEvent_buttonsPixels_x")) {
//                            /*scroll right*/
//                            mCSB_buttonRight.bind("click", function(e) {
//                                e.preventDefault();
//                                PixelsScrollTo(Math.abs(mCSB_container.position().left) + $this.data("scrollButtons_scrollAmount"));
//                            });
//                            /*scroll left*/
//                            mCSB_buttonLeft.bind("click", function(e) {
//                                e.preventDefault();
//                                PixelsScrollTo(Math.abs(mCSB_container.position().left) - $this.data("scrollButtons_scrollAmount"));
//                            });
//                            $this.data({"bindEvent_buttonsPixels_x": true});
//                        }
//                    } else {
//                        mCSB_buttonDown.add(mCSB_buttonUp).unbind("mousedown touchstart MSPointerDown mouseup MSPointerUp mouseout MSPointerOut touchend", mCSB_buttonRight_stop, mCSB_buttonLeft_stop);
//                        $this.data({"bindEvent_buttonsContinuous_y": false});
//                        if (!$this.data("bindEvent_buttonsPixels_y")) {
//                            /*scroll down*/
//                            mCSB_buttonDown.bind("click", function(e) {
//                                e.preventDefault();
//                                PixelsScrollTo(Math.abs(mCSB_container.position().top) + $this.data("scrollButtons_scrollAmount"));
//                            });
//                            /*scroll up*/
//                            mCSB_buttonUp.bind("click", function(e) {
//                                e.preventDefault();
//                                PixelsScrollTo(Math.abs(mCSB_container.position().top) - $this.data("scrollButtons_scrollAmount"));
//                            });
//                            $this.data({"bindEvent_buttonsPixels_y": true});
//                        }
//                    }
//                    function PixelsScrollTo(to) {
//                        if (!mCSB_dragger.data("preventAction")) {
//                            mCSB_dragger.data("preventAction", true);
//                            $this.mCustomScrollbar("scrollTo", to, {trigger: "internal"});
//                        }
//                    }
//                } else { /*continuous scrolling*/
//                    if ($this.data("horizontalScroll")) {
//                        mCSB_buttonRight.add(mCSB_buttonLeft).unbind("click");
//                        $this.data({"bindEvent_buttonsPixels_x": false});
//                        if (!$this.data("bindEvent_buttonsContinuous_x")) {
//                            /*scroll right*/
//                            mCSB_buttonRight.bind("mousedown touchstart MSPointerDown", function(e) {
//                                e.preventDefault();
//                                var scrollButtonsSpeed = ScrollButtonsSpeed();
//                                $this.data({"mCSB_buttonScrollRight": setInterval(function() {
//                                        $this.mCustomScrollbar("scrollTo", Math.abs(mCSB_container.position().left) + scrollButtonsSpeed, {trigger: "internal", scrollEasing: "easeOutCirc"});
//                                    }, 17)});
//                            });
//                            var mCSB_buttonRight_stop = function(e) {
//                                e.preventDefault();
//                                clearInterval($this.data("mCSB_buttonScrollRight"));
//                            }
//                            mCSB_buttonRight.bind("mouseup touchend MSPointerUp mouseout MSPointerOut", mCSB_buttonRight_stop);
//                            /*scroll left*/
//                            mCSB_buttonLeft.bind("mousedown touchstart MSPointerDown", function(e) {
//                                e.preventDefault();
//                                var scrollButtonsSpeed = ScrollButtonsSpeed();
//                                $this.data({"mCSB_buttonScrollLeft": setInterval(function() {
//                                        $this.mCustomScrollbar("scrollTo", Math.abs(mCSB_container.position().left) - scrollButtonsSpeed, {trigger: "internal", scrollEasing: "easeOutCirc"});
//                                    }, 17)});
//                            });
//                            var mCSB_buttonLeft_stop = function(e) {
//                                e.preventDefault();
//                                clearInterval($this.data("mCSB_buttonScrollLeft"));
//                            }
//                            mCSB_buttonLeft.bind("mouseup touchend MSPointerUp mouseout MSPointerOut", mCSB_buttonLeft_stop);
//                            $this.data({"bindEvent_buttonsContinuous_x": true});
//                        }
//                    } else {
//                        mCSB_buttonDown.add(mCSB_buttonUp).unbind("click");
//                        $this.data({"bindEvent_buttonsPixels_y": false});
//                        if (!$this.data("bindEvent_buttonsContinuous_y")) {
//                            /*scroll down*/
//                            mCSB_buttonDown.bind("mousedown touchstart MSPointerDown", function(e) {
//                                e.preventDefault();
//                                var scrollButtonsSpeed = ScrollButtonsSpeed();
//                                $this.data({"mCSB_buttonScrollDown": setInterval(function() {
//                                        $this.mCustomScrollbar("scrollTo", Math.abs(mCSB_container.position().top) + scrollButtonsSpeed, {trigger: "internal", scrollEasing: "easeOutCirc"});
//                                    }, 17)});
//                            });
//                            var mCSB_buttonDown_stop = function(e) {
//                                e.preventDefault();
//                                clearInterval($this.data("mCSB_buttonScrollDown"));
//                            }
//                            mCSB_buttonDown.bind("mouseup touchend MSPointerUp mouseout MSPointerOut", mCSB_buttonDown_stop);
//                            /*scroll up*/
//                            mCSB_buttonUp.bind("mousedown touchstart MSPointerDown", function(e) {
//                                e.preventDefault();
//                                var scrollButtonsSpeed = ScrollButtonsSpeed();
//                                $this.data({"mCSB_buttonScrollUp": setInterval(function() {
//                                        $this.mCustomScrollbar("scrollTo", Math.abs(mCSB_container.position().top) - scrollButtonsSpeed, {trigger: "internal", scrollEasing: "easeOutCirc"});
//                                    }, 17)});
//                            });
//                            var mCSB_buttonUp_stop = function(e) {
//                                e.preventDefault();
//                                clearInterval($this.data("mCSB_buttonScrollUp"));
//                            }
//                            mCSB_buttonUp.bind("mouseup touchend MSPointerUp mouseout MSPointerOut", mCSB_buttonUp_stop);
//                            $this.data({"bindEvent_buttonsContinuous_y": true});
//                        }
//                    }
//                    function ScrollButtonsSpeed() {
//                        var speed = $this.data("scrollButtons_scrollSpeed");
//                        if ($this.data("scrollButtons_scrollSpeed") === "auto") {
//                            speed = Math.round(($this.data("scrollInertia") + 100) / 40);
//                        }
//                        return speed;
//                    }
//                }
//            }
//            /*scrolling on element focus (e.g. via TAB key)*/
//            if ($this.data("autoScrollOnFocus")) {
//                if (!$this.data("bindEvent_focusin")) {
//                    mCustomScrollBox.bind("focusin", function() {
//                        mCustomScrollBox.scrollTop(0).scrollLeft(0);
//                        var focusedElem = $(document.activeElement);
//                        if (focusedElem.is("input,textarea,select,button,a[tabindex],area,object")) {
//                            var mCSB_containerPos = mCSB_container.position().top,
//                                    focusedElemPos = focusedElem.position().top,
//                                    visibleLimit = mCustomScrollBox.height() - focusedElem.outerHeight();
//                            if ($this.data("horizontalScroll")) {
//                                mCSB_containerPos = mCSB_container.position().left;
//                                focusedElemPos = focusedElem.position().left;
//                                visibleLimit = mCustomScrollBox.width() - focusedElem.outerWidth();
//                            }
//                            if (mCSB_containerPos + focusedElemPos < 0 || mCSB_containerPos + focusedElemPos > visibleLimit) {
//                                $this.mCustomScrollbar("scrollTo", focusedElemPos, {trigger: "internal"});
//                            }
//                        }
//                    });
//                    $this.data({"bindEvent_focusin": true});
//                }
//            }
//            /*auto-hide scrollbar*/
//            if ($this.data("autoHideScrollbar")) {
//                if (!$this.data("bindEvent_autoHideScrollbar")) {
//                    mCustomScrollBox.bind("mouseenter", function(e) {
//                        mCustomScrollBox.addClass("mCS-mouse-over");
//                        functions.showScrollbar.call(mCustomScrollBox.children(".mCSB_scrollTools"));
//                    }).bind("mouseleave touchend", function(e) {
//                        mCustomScrollBox.removeClass("mCS-mouse-over");
//                        if (e.type === "mouseleave") {
//                            functions.hideScrollbar.call(mCustomScrollBox.children(".mCSB_scrollTools"));
//                        }
//                    });
//                    $this.data({"bindEvent_autoHideScrollbar": true});
//                }
//            }
//        },
//        scrollTo: function(scrollTo, options) {
//            var $this = $(this),
//                    defaults = {
//                        moveDragger: false,
//                        trigger: "external",
//                        callbacks: true,
//                        scrollInertia: $this.data("scrollInertia"),
//                        scrollEasing: $this.data("scrollEasing")
//                    },
//            options = $.extend(defaults, options),
//                    draggerScrollTo,
//                    mCustomScrollBox = $this.children(".mCustomScrollBox"),
//                    mCSB_container = mCustomScrollBox.children(".mCSB_container"),
//                    mCSB_scrollTools = mCustomScrollBox.children(".mCSB_scrollTools"),
//                    mCSB_draggerContainer = mCSB_scrollTools.children(".mCSB_draggerContainer"),
//                    mCSB_dragger = mCSB_draggerContainer.children(".mCSB_dragger"),
//                    contentSpeed = draggerSpeed = options.scrollInertia,
//                    scrollBeginning, scrollBeginningOffset, totalScroll, totalScrollOffset;
//            if (!mCSB_container.hasClass("mCS_no_scrollbar")) {
//                $this.data({"mCS_trigger": options.trigger});
//                if ($this.data("mCS_Init")) {
//                    options.callbacks = false;
//                }
//                if (scrollTo || scrollTo === 0) {
//                    if (typeof (scrollTo) === "number") { /*if integer, scroll by number of pixels*/
//                        if (options.moveDragger) { /*scroll dragger*/
//                            draggerScrollTo = scrollTo;
//                            if ($this.data("horizontalScroll")) {
//                                scrollTo = mCSB_dragger.position().left * $this.data("scrollAmount");
//                            } else {
//                                scrollTo = mCSB_dragger.position().top * $this.data("scrollAmount");
//                            }
//                            draggerSpeed = 0;
//                        } else { /*scroll content by default*/
//                            draggerScrollTo = scrollTo / $this.data("scrollAmount");
//                        }
//                    } else if (typeof (scrollTo) === "string") { /*if string, scroll by element position*/
//                        var target;
//                        if (scrollTo === "top") { /*scroll to top*/
//                            target = 0;
//                        } else if (scrollTo === "bottom" && !$this.data("horizontalScroll")) { /*scroll to bottom*/
//                            target = mCSB_container.outerHeight() - mCustomScrollBox.height();
//                        } else if (scrollTo === "left") { /*scroll to left*/
//                            target = 0;
//                        } else if (scrollTo === "right" && $this.data("horizontalScroll")) { /*scroll to right*/
//                            target = mCSB_container.outerWidth() - mCustomScrollBox.width();
//                        } else if (scrollTo === "first") { /*scroll to first element position*/
//                            target = $this.find(".mCSB_container").find(":first");
//                        } else if (scrollTo === "last") { /*scroll to last element position*/
//                            target = $this.find(".mCSB_container").find(":last");
//                        } else { /*scroll to element position*/
//                            target = $this.find(scrollTo);
//                        }
//                        if (target.length === 1) { /*if such unique element exists, scroll to it*/
//                            if ($this.data("horizontalScroll")) {
//                                scrollTo = target.position().left;
//                            } else {
//                                scrollTo = target.position().top;
//                            }
//                            draggerScrollTo = scrollTo / $this.data("scrollAmount");
//                        } else {
//                            draggerScrollTo = scrollTo = target;
//                        }
//                    }
//                    /*scroll to*/
//                    if ($this.data("horizontalScroll")) {
//                        if ($this.data("onTotalScrollBack_Offset")) { /*scroll beginning offset*/
//                            scrollBeginningOffset = -$this.data("onTotalScrollBack_Offset");
//                        }
//                        if ($this.data("onTotalScroll_Offset")) { /*total scroll offset*/
//                            totalScrollOffset = mCustomScrollBox.width() - mCSB_container.outerWidth() + $this.data("onTotalScroll_Offset");
//                        }
//                        if (draggerScrollTo < 0) { /*scroll start position*/
//                            draggerScrollTo = scrollTo = 0;
//                            clearInterval($this.data("mCSB_buttonScrollLeft"));
//                            if (!scrollBeginningOffset) {
//                                scrollBeginning = true;
//                            }
//                        } else if (draggerScrollTo >= mCSB_draggerContainer.width() - mCSB_dragger.width()) { /*scroll end position*/
//                            draggerScrollTo = mCSB_draggerContainer.width() - mCSB_dragger.width();
//                            scrollTo = mCustomScrollBox.width() - mCSB_container.outerWidth();
//                            clearInterval($this.data("mCSB_buttonScrollRight"));
//                            if (!totalScrollOffset) {
//                                totalScroll = true;
//                            }
//                        } else {
//                            scrollTo = -scrollTo;
//                        }
//                        var snapAmount = $this.data("snapAmount");
//                        if (snapAmount) {
//                            scrollTo = Math.round(scrollTo / snapAmount) * snapAmount - $this.data("snapOffset");
//                        }
//                        /*scrolling animation*/
//                        functions.mTweenAxis.call(this, mCSB_dragger[0], "left", Math.round(draggerScrollTo), draggerSpeed, options.scrollEasing);
//                        functions.mTweenAxis.call(this, mCSB_container[0], "left", Math.round(scrollTo), contentSpeed, options.scrollEasing, {
//                            onStart: function() {
//                                if (options.callbacks && !$this.data("mCS_tweenRunning")) {
//                                    callbacks("onScrollStart");
//                                }
//                                if ($this.data("autoHideScrollbar")) {
//                                    functions.showScrollbar.call(mCSB_scrollTools);
//                                }
//                            },
//                            onUpdate: function() {
//                                if (options.callbacks) {
//                                    callbacks("whileScrolling");
//                                }
//                            },
//                            onComplete: function() {
//                                if (options.callbacks) {
//                                    callbacks("onScroll");
//                                    if (scrollBeginning || (scrollBeginningOffset && mCSB_container.position().left >= scrollBeginningOffset)) {
//                                        callbacks("onTotalScrollBack");
//                                    }
//                                    if (totalScroll || (totalScrollOffset && mCSB_container.position().left <= totalScrollOffset)) {
//                                        callbacks("onTotalScroll");
//                                    }
//                                }
//                                mCSB_dragger.data("preventAction", false);
//                                $this.data("mCS_tweenRunning", false);
//                                if ($this.data("autoHideScrollbar")) {
//                                    if (!mCustomScrollBox.hasClass("mCS-mouse-over")) {
//                                        functions.hideScrollbar.call(mCSB_scrollTools);
//                                    }
//                                }
//                            }
//                        });
//                    } else {
//                        if ($this.data("onTotalScrollBack_Offset")) { /*scroll beginning offset*/
//                            scrollBeginningOffset = -$this.data("onTotalScrollBack_Offset");
//                        }
//                        if ($this.data("onTotalScroll_Offset")) { /*total scroll offset*/
//                            totalScrollOffset = mCustomScrollBox.height() - mCSB_container.outerHeight() + $this.data("onTotalScroll_Offset");
//                        }
//                        if (draggerScrollTo < 0) { /*scroll start position*/
//                            draggerScrollTo = scrollTo = 0;
//                            clearInterval($this.data("mCSB_buttonScrollUp"));
//                            if (!scrollBeginningOffset) {
//                                scrollBeginning = true;
//                            }
//                        } else if (draggerScrollTo >= mCSB_draggerContainer.height() - mCSB_dragger.height()) { /*scroll end position*/
//                            draggerScrollTo = mCSB_draggerContainer.height() - mCSB_dragger.height();
//                            scrollTo = mCustomScrollBox.height() - mCSB_container.outerHeight();
//                            clearInterval($this.data("mCSB_buttonScrollDown"));
//                            if (!totalScrollOffset) {
//                                totalScroll = true;
//                            }
//                        } else {
//                            scrollTo = -scrollTo;
//                        }
//                        var snapAmount = $this.data("snapAmount");
//                        if (snapAmount) {
//                            scrollTo = Math.round(scrollTo / snapAmount) * snapAmount - $this.data("snapOffset");
//                        }
//                        /*scrolling animation*/
//                        functions.mTweenAxis.call(this, mCSB_dragger[0], "top", Math.round(draggerScrollTo), draggerSpeed, options.scrollEasing);
//                        functions.mTweenAxis.call(this, mCSB_container[0], "top", Math.round(scrollTo), contentSpeed, options.scrollEasing, {
//                            onStart: function() {
//                                if (options.callbacks && !$this.data("mCS_tweenRunning")) {
//                                    callbacks("onScrollStart");
//                                }
//                                if ($this.data("autoHideScrollbar")) {
//                                    functions.showScrollbar.call(mCSB_scrollTools);
//                                }
//                            },
//                            onUpdate: function() {
//                                if (options.callbacks) {
//                                    callbacks("whileScrolling");
//                                }
//                            },
//                            onComplete: function() {
//                                if (options.callbacks) {
//                                    callbacks("onScroll");
//                                    if (scrollBeginning || (scrollBeginningOffset && mCSB_container.position().top >= scrollBeginningOffset)) {
//                                        callbacks("onTotalScrollBack");
//                                    }
//                                    if (totalScroll || (totalScrollOffset && mCSB_container.position().top <= totalScrollOffset)) {
//                                        callbacks("onTotalScroll");
//                                    }
//                                }
//                                mCSB_dragger.data("preventAction", false);
//                                $this.data("mCS_tweenRunning", false);
//                                if ($this.data("autoHideScrollbar")) {
//                                    if (!mCustomScrollBox.hasClass("mCS-mouse-over")) {
//                                        functions.hideScrollbar.call(mCSB_scrollTools);
//                                    }
//                                }
//                            }
//                        });
//                    }
//                    if ($this.data("mCS_Init")) {
//                        $this.data({"mCS_Init": false});
//                    }
//                }
//            }
//            /*callbacks*/
//            function callbacks(cb) {
//                this.mcs = {
//                    top: mCSB_container.position().top, left: mCSB_container.position().left,
//                    draggerTop: mCSB_dragger.position().top, draggerLeft: mCSB_dragger.position().left,
//                    topPct: Math.round((100 * Math.abs(mCSB_container.position().top)) / Math.abs(mCSB_container.outerHeight() - mCustomScrollBox.height())),
//                    leftPct: Math.round((100 * Math.abs(mCSB_container.position().left)) / Math.abs(mCSB_container.outerWidth() - mCustomScrollBox.width()))
//                };
//                switch (cb) {
//                    /*start scrolling callback*/
//                    case "onScrollStart":
//                        $this.data("mCS_tweenRunning", true).data("onScrollStart_Callback").call($this, this.mcs);
//                        break;
//                    case "whileScrolling":
//                        $this.data("whileScrolling_Callback").call($this, this.mcs);
//                        break;
//                    case "onScroll":
//                        $this.data("onScroll_Callback").call($this, this.mcs);
//                        break;
//                    case "onTotalScrollBack":
//                        $this.data("onTotalScrollBack_Callback").call($this, this.mcs);
//                        break;
//                    case "onTotalScroll":
//                        $this.data("onTotalScroll_Callback").call($this, this.mcs);
//                        break;
//                }
//            }
//        },
//        stop: function() {
//            var $this = $(this),
//                    mCSB_container = $this.children().children(".mCSB_container"),
//                    mCSB_dragger = $this.children().children().children().children(".mCSB_dragger");
//            functions.mTweenAxisStop.call(this, mCSB_container[0]);
//            functions.mTweenAxisStop.call(this, mCSB_dragger[0]);
//        },
//        disable: function(resetScroll) {
//            var $this = $(this),
//                    mCustomScrollBox = $this.children(".mCustomScrollBox"),
//                    mCSB_container = mCustomScrollBox.children(".mCSB_container"),
//                    mCSB_scrollTools = mCustomScrollBox.children(".mCSB_scrollTools"),
//                    mCSB_dragger = mCSB_scrollTools.children().children(".mCSB_dragger");
//            mCustomScrollBox.unbind("mousewheel focusin mouseenter mouseleave touchend");
//            mCSB_container.unbind("touchstart touchmove")
//            if (resetScroll) {
//                if ($this.data("horizontalScroll")) {
//                    mCSB_dragger.add(mCSB_container).css("left", 0);
//                } else {
//                    mCSB_dragger.add(mCSB_container).css("top", 0);
//                }
//            }
//            mCSB_scrollTools.css("display", "none");
//            mCSB_container.addClass("mCS_no_scrollbar");
//            $this.data({"bindEvent_mousewheel": false, "bindEvent_focusin": false, "bindEvent_content_touch": false, "bindEvent_autoHideScrollbar": false}).addClass("mCS_disabled");
//        },
//        destroy: function() {
//            var $this = $(this);
//            $this.removeClass("mCustomScrollbar _mCS_" + $this.data("mCustomScrollbarIndex")).addClass("mCS_destroyed").children().children(".mCSB_container").unwrap().children().unwrap().siblings(".mCSB_scrollTools").remove();
//            $(document).unbind("mousemove." + $this.data("mCustomScrollbarIndex") + " mouseup." + $this.data("mCustomScrollbarIndex") + " MSPointerMove." + $this.data("mCustomScrollbarIndex") + " MSPointerUp." + $this.data("mCustomScrollbarIndex"));
//            $(window).unbind("resize." + $this.data("mCustomScrollbarIndex"));
//        }
//    },
//    functions = {
//        /*hide/show scrollbar*/
//        showScrollbar: function() {
//            this.stop().animate({opacity: 1}, "fast");
//        },
//        hideScrollbar: function() {
//            this.stop().animate({opacity: 0}, "fast");
//        },
//        /*js animation tween*/
//        mTweenAxis: function(el, prop, to, duration, easing, callbacks) {
//            var callbacks = callbacks || {},
//                    onStart = callbacks.onStart || function() {
//                    }, onUpdate = callbacks.onUpdate || function() {
//            }, onComplete = callbacks.onComplete || function() {
//            };
//            var startTime = _getTime(), _delay, progress = 0, from = el.offsetTop, elStyle = el.style;
//            if (prop === "left") {
//                from = el.offsetLeft;
//            }
//            var diff = to - from;
//            _cancelTween();
//            _startTween();
//            function _getTime() {
//                if (window.performance && window.performance.now) {
//                    return window.performance.now();
//                } else {
//                    if (window.performance && window.performance.webkitNow) {
//                        return window.performance.webkitNow();
//                    } else {
//                        if (Date.now) {
//                            return Date.now();
//                        } else {
//                            return new Date().getTime();
//                        }
//                    }
//                }
//            }
//            function _step() {
//                if (!progress) {
//                    onStart.call();
//                }
//                progress = _getTime() - startTime;
//                _tween();
//                if (progress >= el._time) {
//                    el._time = (progress > el._time) ? progress + _delay - (progress - el._time) : progress + _delay - 1;
//                    if (el._time < progress + 1) {
//                        el._time = progress + 1;
//                    }
//                }
//                if (el._time < duration) {
//                    el._id = _request(_step);
//                } else {
//                    onComplete.call();
//                }
//            }
//            function _tween() {
//                if (duration > 0) {
//                    el.currVal = _ease(el._time, from, diff, duration, easing);
//                    elStyle[prop] = Math.round(el.currVal) + "px";
//                } else {
//                    elStyle[prop] = to + "px";
//                }
//                onUpdate.call();
//            }
//            function _startTween() {
//                _delay = 1000 / 60;
//                el._time = progress + _delay;
//                _request = (!window.requestAnimationFrame) ? function(f) {
//                    _tween();
//                    return setTimeout(f, 0.01);
//                } : window.requestAnimationFrame;
//                el._id = _request(_step);
//            }
//            function _cancelTween() {
//                if (el._id == null) {
//                    return;
//                }
//                if (!window.requestAnimationFrame) {
//                    clearTimeout(el._id);
//                } else {
//                    window.cancelAnimationFrame(el._id);
//                }
//                el._id = null;
//            }
//            function _ease(t, b, c, d, type) {
//                switch (type) {
//                    case "linear":
//                        return c * t / d + b;
//                        break;
//                    case "easeOutQuad":
//                        t /= d;
//                        return -c * t * (t - 2) + b;
//                        break;
//                    case "easeInOutQuad":
//                        t /= d / 2;
//                        if (t < 1)
//                            return c / 2 * t * t + b;
//                        t--;
//                        return -c / 2 * (t * (t - 2) - 1) + b;
//                        break;
//                    case "easeOutCubic":
//                        t /= d;
//                        t--;
//                        return c * (t * t * t + 1) + b;
//                        break;
//                    case "easeOutQuart":
//                        t /= d;
//                        t--;
//                        return -c * (t * t * t * t - 1) + b;
//                        break;
//                    case "easeOutQuint":
//                        t /= d;
//                        t--;
//                        return c * (t * t * t * t * t + 1) + b;
//                        break;
//                    case "easeOutCirc":
//                        t /= d;
//                        t--;
//                        return c * Math.sqrt(1 - t * t) + b;
//                        break;
//                    case "easeOutSine":
//                        return c * Math.sin(t / d * (Math.PI / 2)) + b;
//                        break;
//                    case "easeOutExpo":
//                        return c * (-Math.pow(2, -10 * t / d) + 1) + b;
//                        break;
//                    case "mcsEaseOut":
//                        var ts = (t /= d) * t, tc = ts * t;
//                        return b + c * (0.499999999999997 * tc * ts + -2.5 * ts * ts + 5.5 * tc + -6.5 * ts + 4 * t);
//                        break;
//                    case "draggerRailEase":
//                        t /= d / 2;
//                        if (t < 1)
//                            return c / 2 * t * t * t + b;
//                        t -= 2;
//                        return c / 2 * (t * t * t + 2) + b;
//                        break;
//                }
//            }
//        },
//        /*stop js animation tweens*/
//        mTweenAxisStop: function(el) {
//            if (el._id == null) {
//                return;
//            }
//            if (!window.requestAnimationFrame) {
//                clearTimeout(el._id);
//            } else {
//                window.cancelAnimationFrame(el._id);
//            }
//            el._id = null;
//        },
//        /*detect requestAnimationFrame and polyfill*/
//        rafPolyfill: function() {
//            var pfx = ["ms", "moz", "webkit", "o"], i = pfx.length;
//            while (--i > -1 && !window.requestAnimationFrame) {
//                window.requestAnimationFrame = window[pfx[i] + "RequestAnimationFrame"];
//                window.cancelAnimationFrame = window[pfx[i] + "CancelAnimationFrame"] || window[pfx[i] + "CancelRequestAnimationFrame"];
//            }
//        }
//    }
//    /*detect features*/
//    functions.rafPolyfill.call(); /*requestAnimationFrame*/
//    $.support.touch = !!('ontouchstart' in window); /*touch*/
//    $.support.msPointer = window.navigator.msPointerEnabled; /*MSPointer support*/
//    /*plugin dependencies*/
////	var _dlp=("https:"==document.location.protocol) ? "https:" : "http:";
////	$.event.special.mousewheel || document.write('<script src="'+_dlp+'//cdnjs.cloudflare.com/ajax/libs/jquery-mousewheel/3.0.6/jquery.mousewheel.min.js"><\/script>');
//    /*plugin fn*/
//    $.fn.mCustomScrollbar = function(method) {
//        if (methods[method]) {
//            return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
//        } else if (typeof method === "object" || !method) {
//            return methods.init.apply(this, arguments);
//        } else {
//            $.error("Method " + method + " does not exist");
//        }
//    };
//})(jQuery);

 /*
 * jQuery UI Selectmenu version 1.5.0pre
 *
 * Copyright (c) 2009-2010 filament group, http://filamentgroup.com
 * Copyright (c) 2010-2013 Felix Nagel, http://www.felixnagel.com
 * Licensed under the MIT (MIT-LICENSE.txt)
 *
 * https://github.com/fnagel/jquery-ui/wiki/Selectmenu
 */

//(function( $ ) {
//
//$.widget("ui.selectmenu", {
//	options: {
//		appendTo: "body",
//		typeAhead: 1000,
//		style: 'dropdown',
//		positionOptions: null,
//		width: null,
//		menuWidth: null,
//		handleWidth: 26,
//		maxHeight: null,
//		icons: null,
//		format: null,
//		escapeHtml: false,
//		bgImage: function() {}
//	},
//
//	_create: function() {
//		var self = this, o = this.options;
//
//		// make / set unique id
//		var selectmenuId = this.element.uniqueId().attr( "id" );
//
//		// quick array of button and menu id's
//		this.ids = [ selectmenuId, selectmenuId + '-button', selectmenuId + '-menu' ];
//
//		// define safe mouseup for future toggling
//		this._safemouseup = true;
//		this.isOpen = false;
//
//		// create menu button wrapper
//		this.newelement = $( '<a />', {
//			'class': 'ui-selectmenu ui-widget ui-state-default ui-corner-all',
//			'id' : this.ids[ 1 ],
//			'role': 'button',
//			'href': '#nogo',
//			'tabindex': this.element.attr( 'disabled' ) ? 1 : 0,
//			'aria-haspopup': true,
//			'aria-owns': this.ids[ 2 ]
//		});
//		this.newelementWrap = $( "<span />" )
//			.append( this.newelement )
//			.insertAfter( this.element );
//
//		// transfer tabindex
//		var tabindex = this.element.attr( 'tabindex' );
//		if ( tabindex ) {
//			this.newelement.attr( 'tabindex', tabindex );
//		}
//
//		// save reference to select in data for ease in calling methods
//		this.newelement.data( 'selectelement', this.element );
//
//		// menu icon
//		this.selectmenuIcon = $( '<span class="ui-selectmenu-icon ui-icon"></span>' )
//			.prependTo( this.newelement );
//
//		// append status span to button
//		this.newelement.prepend( '<span class="ui-selectmenu-status" />' );
//
//		// make associated form label trigger focus
//		this.element.bind({
//			'click.selectmenu':  function( event ) {
//				self.newelement.focus();
//				event.preventDefault();
//			}
//		});
//
//		// click toggle for menu visibility
//		this.newelement
//			.bind( 'mousedown.selectmenu', function( event ) {
//				self._toggle( event, true );
//				// make sure a click won't open/close instantly
//				if ( o.style == "popup" ) {
//					self._safemouseup = false;
//					setTimeout( function() { self._safemouseup = true; }, 300 );
//				}
//
//				event.preventDefault();
//			})
//			.bind( 'click.selectmenu', function( event ) {
//				event.preventDefault();
//			})
//			.bind( "keydown.selectmenu", function( event ) {
//				var ret = false;
//				switch ( event.keyCode ) {
//					case $.ui.keyCode.ENTER:
//						ret = true;
//						break;
//					case $.ui.keyCode.SPACE:
//						self._toggle( event );
//						break;
//					case $.ui.keyCode.UP:
//						if ( event.altKey ) {
//							self.open( event );
//						} else {
//							self._moveSelection( -1 );
//						}
//						break;
//					case $.ui.keyCode.DOWN:
//						if ( event.altKey ) {
//							self.open( event );
//						} else {
//							self._moveSelection( 1 );
//						}
//						break;
//					case $.ui.keyCode.LEFT:
//						self._moveSelection( -1 );
//						break;
//					case $.ui.keyCode.RIGHT:
//						self._moveSelection( 1 );
//						break;
//					case $.ui.keyCode.TAB:
//						ret = true;
//						break;
//					case $.ui.keyCode.PAGE_UP:
//					case $.ui.keyCode.HOME:
//						self.index( 0 );
//						break;
//					case $.ui.keyCode.PAGE_DOWN:
//					case $.ui.keyCode.END:
//						self.index( self._optionLis.length );
//						break;
//					default:
//						ret = true;
//				}
//				return ret;
//			})
//			.bind( 'keypress.selectmenu', function( event ) {
//				if ( event.which > 0 ) {
//					self._typeAhead( event.which, 'mouseup' );
//				}
//				return true;
//			})
//			.bind( 'mouseover.selectmenu', function() {
//				if ( !o.disabled ) $( this ).addClass( 'ui-state-hover' );
//			})
//			.bind( 'mouseout.selectmenu', function() {
//				if ( !o.disabled ) $( this ).removeClass( 'ui-state-hover' );
//			})
//			.bind( 'focus.selectmenu', function() {
//				if ( !o.disabled ) $( this ).addClass( 'ui-state-focus' );
//			})
//			.bind( 'blur.selectmenu', function() {
//				if (!o.disabled) $( this ).removeClass( 'ui-state-focus' );
//			});
//
//		// document click closes menu
//		$( document ).bind( "mousedown.selectmenu-" + this.ids[ 0 ], function( event ) {
//			//check if open and if the clicket targes parent is the same
//			if ( self.isOpen && !$( event.target ).closest( "#" + self.ids[ 1 ] ).length ) {
//				self.close( event );
//			}
//		});
//
//		// change event on original selectmenu
//		this.element
//			.bind( "click.selectmenu", function() {
//				self._refreshValue();
//			})
//			// FIXME: newelement can be null under unclear circumstances in IE8
//			// TODO not sure if this is still a problem (fnagel 20.03.11)
//			.bind( "focus.selectmenu", function() {
//				if ( self.newelement ) {
//					self.newelement[ 0 ].focus();
//				}
//			});
//
//		// set width when not set via options
//		if ( !o.width ) {
//			o.width = this.element.outerWidth();
//		}
//		// set menu button width
//		this.newelement.width( o.width );
//
//		// hide original selectmenu element
//		this.element.hide();
//
//		// create menu portion, append to body
//		this.list = $( '<ul />', {
//			'class': 'ui-widget ui-widget-content',
//			'aria-hidden': true,
//			'role': 'listbox',
//			'aria-labelledby': this.ids[ 1 ],
//			'id': this.ids[ 2 ]
//		});
//		this.listWrap = $( "<div />", {
//			'class': 'ui-selectmenu-menu'
//		}).append( this.list ).appendTo( o.appendTo );
//
//		// transfer menu click to menu button
//		this.list
//			.bind("keydown.selectmenu", function(event) {
//				var ret = false;
//				switch ( event.keyCode ) {
//					case $.ui.keyCode.UP:
//						if ( event.altKey ) {
//							self.close( event, true );
//						} else {
//							self._moveFocus( -1 );
//						}
//						break;
//					case $.ui.keyCode.DOWN:
//						if ( event.altKey ) {
//							self.close( event, true );
//						} else {
//							self._moveFocus( 1 );
//						}
//						break;
//					case $.ui.keyCode.LEFT:
//						self._moveFocus( -1 );
//						break;
//					case $.ui.keyCode.RIGHT:
//						self._moveFocus( 1 );
//						break;
//					case $.ui.keyCode.HOME:
//						self._moveFocus( ':first' );
//						break;
//					case $.ui.keyCode.PAGE_UP:
//						self._scrollPage( 'up' );
//						break;
//					case $.ui.keyCode.PAGE_DOWN:
//						self._scrollPage( 'down' );
//						break;
//					case $.ui.keyCode.END:
//						self._moveFocus( ':last' );
//						break;
//					case $.ui.keyCode.ENTER:
//					case $.ui.keyCode.SPACE:
//						self.close( event, true);
//						$( event.target ).parents( 'li:eq(0)' ).trigger( 'mouseup' );
//						break;
//					case $.ui.keyCode.TAB:
//						ret = true;
//						self.close( event, true );
//						$( event.target ).parents( 'li:eq(0)' ).trigger( 'mouseup' );
//						break;
//					case $.ui.keyCode.ESCAPE:
//						self.close( event, true );
//						break;
//					default:
//						ret = true;
//				}
//				return ret;
//			})
//			.bind( 'keypress.selectmenu', function( event ) {
//				if ( event.which > 0 ) {
//					self._typeAhead( event.which, 'focus' );
//				}
//				return true;
//			})
//			// this allows for using the scrollbar in an overflowed list
//			.bind( 'mousedown.selectmenu mouseup.selectmenu', function() { return false; });
//
//		// needed when window is resized
//		$( window ).bind( "resize.selectmenu-" + this.ids[ 0 ], $.proxy( self.close, this ) );
//	},
//
//	_init: function() {
//		var self = this, o = this.options;
//
//		// serialize selectmenu element options
//		var selectOptionData = [];
//		this.element.find( 'option' ).each( function() {
//			var opt = $( this );
//			selectOptionData.push({
//				value: opt.attr( 'value' ),
//				text: self._formatText( opt.text(), opt ),
//				selected: opt.attr( 'selected' ),
//				disabled: opt.attr( 'disabled' ),
//				classes: opt.attr( 'class' ),
//				typeahead: opt.attr( 'typeahead'),
//				parentOptGroup: opt.parent( 'optgroup' ),
//				bgImage: o.bgImage.call( opt )
//			});
//		});
//
//		// active state class is only used in popup style
//		var activeClass = ( self.options.style == "popup" ) ? " ui-state-active" : "";
//
//		// empty list so we can refresh the selectmenu via selectmenu()
//		this.list.html( "" );
//
//		// write li's
//		if ( selectOptionData.length ) {
//			for ( var i = 0; i < selectOptionData.length; i++ ) {
//				var thisLiAttr = { role : 'presentation' };
//				if ( selectOptionData[ i ].disabled ) {
//					thisLiAttr[ 'class' ] = 'ui-state-disabled';
//				}
//				var thisAAttr = {
//					html: selectOptionData[ i ].text || '&nbsp;',
//					href: '#nogo',
//					tabindex : -1,
//					role: 'option',
//					'aria-selected' : false
//				};
//				if ( selectOptionData[ i ].disabled ) {
//					thisAAttr[ 'aria-disabled' ] = true;
//				}
//				if ( selectOptionData[ i ].typeahead ) {
//					thisAAttr[ 'typeahead' ] = selectOptionData[ i ].typeahead;
//				}
//				var thisA = $( '<a/>', thisAAttr )
//					.bind( 'focus.selectmenu', function() {
//						$( this ).parent().mouseover();
//					})
//					.bind( 'blur.selectmenu', function() {
//						$( this ).parent().mouseout();
//					});
//				var thisLi = $( '<li/>', thisLiAttr )
//					.append( thisA )
//					.data( 'index', i )
//					.addClass( selectOptionData[ i ].classes )
//					.data( 'optionClasses', selectOptionData[ i ].classes || '' )
//					.bind( "mouseup.selectmenu", function( event ) {
//						if ( self._safemouseup && !self._disabled( event.currentTarget ) && !self._disabled( $( event.currentTarget ).parents( "ul > li.ui-selectmenu-group " ) ) ) {
//							self.index( $( this ).data( 'index' ) );
//							self.select( event );
//							self.close( event, true );
//						}
//						return false;
//					})
//					.bind( "click.selectmenu", function() {
//						return false;
//					})
//					.bind('mouseover.selectmenu', function( e ) {
//						// no hover if diabled
//						if ( !$( this ).hasClass( 'ui-state-disabled' ) && !$( this ).parent( "ul" ).parent( "li" ).hasClass( 'ui-state-disabled' ) ) {
//							e.optionValue = self.element[ 0 ].options[ $( this ).data( 'index' ) ].value;
//							self._trigger( "hover", e, self._uiHash() );
//							self._selectedOptionLi().addClass( activeClass );
//							self._focusedOptionLi().removeClass( 'ui-selectmenu-item-focus ui-state-hover' );
//							$( this ).removeClass( 'ui-state-active' ).addClass( 'ui-selectmenu-item-focus ui-state-hover' );
//						}
//					})
//					.bind( 'mouseout.selectmenu', function( e ) {
//						if ( $( this ).is( self._selectedOptionLi() ) ) {
//							$( this ).addClass( activeClass );
//						}
//						e.optionValue = self.element[ 0 ].options[ $( this ).data( 'index' ) ].value;
//						self._trigger( "blur", e, self._uiHash() );
//						$( this ).removeClass( 'ui-selectmenu-item-focus ui-state-hover' );
//					});
//
//				// optgroup or not...
//				if ( selectOptionData[ i ].parentOptGroup.length ) {
//					var optGroupName = 'ui-selectmenu-group-' + this.element.find( 'optgroup' ).index( selectOptionData[ i ].parentOptGroup );
//					if ( this.list.find( 'li.' + optGroupName ).length ) {
//						this.list.find( 'li.' + optGroupName + ':last ul' ).append( thisLi );
//					} else {
//						$( '<li role="presentation" class="ui-selectmenu-group ' + optGroupName + ( selectOptionData[ i ].parentOptGroup.attr( "disabled" ) ? ' ' + 'ui-state-disabled" aria-disabled="true"' : '"' ) + '><span class="ui-selectmenu-group-label">' + selectOptionData[ i ].parentOptGroup.attr( 'label' ) + '</span><ul></ul></li>' )
//							.appendTo( this.list )
//							.find( 'ul' )
//							.append( thisLi );
//					}
//				} else {
//					thisLi.appendTo( this.list );
//				}
//
//				// append icon if option is specified
//				if ( o.icons ) {
//					for ( var j in o.icons ) {
//						if (thisLi.is(o.icons[ j ].find)) {
//							thisLi
//								.data( 'optionClasses', selectOptionData[ i ].classes + ' ui-selectmenu-hasIcon' )
//								.addClass( 'ui-selectmenu-hasIcon' );
//							var iconClass = o.icons[ j ].icon || "";
//							thisLi
//								.find( 'a:eq(0)' )
//								.prepend( '<span class="ui-selectmenu-item-icon ui-icon ' + iconClass + '"></span>' );
//							if ( selectOptionData[ i ].bgImage ) {
//								thisLi.find( 'span' ).css( 'background-image', selectOptionData[ i ].bgImage );
//							}
//						}
//					}
//				}
//			}
//		} else {
//			$('<li role="presentation"><a href="#nogo" tabindex="-1" role="option"></a></li>' ).appendTo( this.list );
//		}
//		// we need to set and unset the CSS classes for dropdown and popup style
//		var isDropDown = ( o.style == 'dropdown' );
//		this.newelement
//			.toggleClass( 'ui-selectmenu-dropdown', isDropDown )
//			.toggleClass( 'ui-selectmenu-popup', !isDropDown );
//		this.list
//			.toggleClass( 'ui-selectmenu-menu-dropdown ui-corner-bottom', isDropDown )
//			.toggleClass( 'ui-selectmenu-menu-popup ui-corner-all', !isDropDown )
//			// add corners to top and bottom menu items
//			.find( 'li:first' )
//			.toggleClass( 'ui-corner-top', !isDropDown )
//			.end().find( 'li:last' )
//			.addClass( 'ui-corner-bottom' );
//		this.selectmenuIcon
//			.toggleClass( 'ui-icon-triangle-1-s', isDropDown )
//			.toggleClass( 'ui-icon-triangle-2-n-s', !isDropDown );
//
//		// set menu width to either menuWidth option value, width option value, or select width
//		if ( o.style == 'dropdown' ) {
//			this.list.width( o.menuWidth ? o.menuWidth : o.width );
//		} else {
//			this.list.width( o.menuWidth ? o.menuWidth : o.width - o.handleWidth );
//		}
//
//		// Android 2 (Gingerbread) doesn't support internal scrollable divs.
//		// Don't limit the menu height for that browser.
//		if ( !navigator.userAgent.match( /Android 2/ ) ) {
//			var listH = this.listWrap.height();
//			var winH = $( window ).height();
//			// calculate default max height
//			var maxH = o.maxHeight ? Math.min( o.maxHeight, winH ) : winH / 3;
//			if ( listH > maxH ) this.list.height( maxH );
//		}
//
//		// save reference to actionable li's (not group label li's)
//		this._optionLis = this.list.find( 'li:not(.ui-selectmenu-group)' );
//
//		// transfer disabled state
//		if ( this.element.attr( 'disabled' ) ) {
//			this.disable();
//		} else {
//			this.enable();
//		}
//
//		// update value
//		this._refreshValue();
//
//		// set selected item so movefocus has intial state
//		this._selectedOptionLi().addClass( 'ui-selectmenu-item-focus' );
//
//		// needed when selectmenu is placed at the very bottom / top of the page
//		clearTimeout( this.refreshTimeout );
//		this.refreshTimeout = window.setTimeout( function () {
//			self._refreshPosition();
//		}, 200 );
//	},
//
//	destroy: function() {
//		this.element.removeData( this.widgetName )
//			.removeClass( 'ui-selectmenu-disabled' + ' ' + 'ui-state-disabled' )
//			.removeAttr( 'aria-disabled' )
//			.unbind( ".selectmenu" );
//
//		$( window ).unbind( ".selectmenu-" + this.ids[ 0 ] );
//		$( document ).unbind( ".selectmenu-" + this.ids[ 0 ] );
//
//		this.newelementWrap.remove();
//		this.listWrap.remove();
//
//		// unbind click event and show original select
//		this.element
//			.unbind( ".selectmenu" )
//			.show();
//
//		// call widget destroy function
//		$.Widget.prototype.destroy.apply( this, arguments );
//	},
//
//	_typeAhead: function( code, eventType ) {
//		var self = this,
//			c = String.fromCharCode( code ).toLowerCase(),
//			matchee = null,
//			nextIndex = null;
//
//		// Clear any previous timer if present
//		if ( self._typeAhead_timer ) {
//			window.clearTimeout( self._typeAhead_timer );
//			self._typeAhead_timer = undefined;
//		}
//		// Store the character typed
//		self._typeAhead_chars = ( self._typeAhead_chars === undefined ? "" : self._typeAhead_chars ).concat( c );
//		// Detect if we are in cyciling mode or direct selection mode
//		if ( self._typeAhead_chars.length < 2 || ( self._typeAhead_chars.substr( -2, 1 ) === c && self._typeAhead_cycling ) ) {
//			self._typeAhead_cycling = true;
//			// Match only the first character and loop
//			matchee = c;
//		} else {
//			// We won't be cycling anymore until the timer expires
//			self._typeAhead_cycling = false;
//			// Match all the characters typed
//			matchee = self._typeAhead_chars;
//		}
//
//		// We need to determine the currently active index, but it depends on
//		// the used context: if it's in the element, we want the actual
//		// selected index, if it's in the menu, just the focused one
//		var selectedIndex = ( eventType !== 'focus' ? this._selectedOptionLi().data( 'index' ) : this._focusedOptionLi().data( 'index' )) || 0;
//		for ( var i = 0; i < this._optionLis.length; i++ ) {
//			var thisText = this._optionLis.eq( i ).text().substr( 0, matchee.length ).toLowerCase();
//			if ( thisText === matchee ) {
//				if ( self._typeAhead_cycling ) {
//					if ( nextIndex === null )
//						nextIndex = i;
//					if ( i > selectedIndex ) {
//						nextIndex = i;
//						break;
//					}
//				} else {
//					nextIndex = i;
//				}
//			}
//		}
//
//		if ( nextIndex !== null ) {
//			// Why using trigger() instead of a direct method to select the index? Because we don't what is the exact action to do,
//			// it depends if the user is typing on the element or on the popped up menu
//			this._optionLis.eq( nextIndex ).find( "a" ).trigger( eventType );
//		}
//
//		self._typeAhead_timer = window.setTimeout( function() {
//			self._typeAhead_timer = undefined;
//			self._typeAhead_chars = undefined;
//			self._typeAhead_cycling = undefined;
//		}, self.options.typeAhead );
//	},
//
//	// returns some usefull information, called by callbacks only
//	_uiHash: function() {
//		var index = this.index();
//		return {
//			index: index,
//			option: $( "option", this.element ).get( index ),
//			value: this.element[ 0 ].value
//		};
//	},
//
//	open: function( event ) {
//		if ( this.newelement.attr( "aria-disabled" ) != 'true' ) {
//			var self = this,
//				o = this.options,
//				selected = this._selectedOptionLi(),
//				link = selected.find("a");
//
//			self._closeOthers( event );
//			self.newelement.addClass( 'ui-state-active' );
//			self.list.attr( 'aria-hidden', false );
//			self.listWrap.addClass( 'ui-selectmenu-open' );
//
//			if ( o.style == "dropdown" ) {
//				self.newelement.removeClass( 'ui-corner-all' ).addClass( 'ui-corner-top' );
//			} else {
//				// center overflow and avoid flickering
//				this.list
//					.css( "left", -5000 )
//					.scrollTop( this.list.scrollTop() + selected.position().top - this.list.outerHeight() / 2 + selected.outerHeight() / 2 )
//					.css( "left", "auto" );
//			}
//
//			self._refreshPosition();
//
//			if ( link.length ) {
//				link[ 0 ].focus();
//			}
//
//			self.isOpen = true;
//			self._trigger( "open", event, self._uiHash() );
//		}
//	},
//
//	close: function( event, retainFocus ) {
//		if ( this.newelement.is( '.ui-state-active') ) {
//			this.newelement.removeClass( 'ui-state-active' );
//			this.listWrap.removeClass( 'ui-selectmenu-open' );
//			this.list.attr( 'aria-hidden', true );
//			if ( this.options.style == "dropdown" ) {
//				this.newelement.removeClass( 'ui-corner-top' ).addClass( 'ui-corner-all' );
//			}
//			if ( retainFocus ) {
//				this.newelement.focus();
//			}
//			this.isOpen = false;
//			this._trigger( "close", event, this._uiHash() );
//		}
//	},
//
//	change: function( event ) {
//		this.element.trigger( "change" );
//		this._trigger( "change", event, this._uiHash() );
//	},
//
//	select: function( event ) {
//		if ( this._disabled( event.currentTarget ) ) { return false; }
//		this._trigger( "select", event, this._uiHash() );
//	},
//
//	widget: function() {
//		return this.listWrap.add( this.newelementWrap );
//	},
//
//	_closeOthers: function( event ) {
//		$( '.ui-selectmenu.ui-state-active' ).not( this.newelement ).each( function() {
//			$( this ).data( 'selectelement' ).selectmenu( 'close', event );
//		});
//		$( '.ui-selectmenu.ui-state-hover' ).trigger( 'mouseout' );
//	},
//
//	_toggle: function( event, retainFocus ) {
//		if ( this.isOpen ) {
//			this.close( event, retainFocus );
//		} else {
//			this.open( event );
//		}
//	},
//
//	_formatText: function( text, opt ) {
//		if ( this.options.format ) {
//			text = this.options.format( text, opt );
//		} else if ( this.options.escapeHtml ) {
//			text = $( '<div />' ).text( text ).html();
//		}
//		return text;
//	},
//
//	_selectedIndex: function() {
//		return this.element[ 0 ].selectedIndex;
//	},
//
//	_selectedOptionLi: function() {
//		return this._optionLis.eq( this._selectedIndex() );
//	},
//
//	_focusedOptionLi: function() {
//		return this.list.find( '.ui-selectmenu-item-focus' );
//	},
//
//	_moveSelection: function( amt, recIndex ) {
//		// do nothing if disabled
//		if ( !this.options.disabled ) {
//			var currIndex = parseInt( this._selectedOptionLi().data( 'index' ) || 0, 10 );
//			var newIndex = currIndex + amt;
//			// do not loop when using up key
//			if ( newIndex < 0 ) {
//				newIndex = 0;
//			}
//			if ( newIndex > this._optionLis.size() - 1 ) {
//				newIndex = this._optionLis.size() - 1;
//			}
//			// Occurs when a full loop has been made
//			if ( newIndex === recIndex ) {
//				return false;
//			}
//
//			if ( this._optionLis.eq( newIndex ).hasClass( 'ui-state-disabled' ) ) {
//				// if option at newIndex is disabled, call _moveFocus, incrementing amt by one
//				( amt > 0 ) ? ++amt : --amt;
//				this._moveSelection( amt, newIndex );
//			} else {
//				this._optionLis.eq( newIndex ).trigger( 'mouseover' ).trigger( 'mouseup' );
//			}
//		}
//	},
//
//	_moveFocus: function( amt, recIndex ) {
//		if ( !isNaN( amt ) ) {
//			var currIndex = parseInt( this._focusedOptionLi().data( 'index' ) || 0, 10 );
//			var newIndex = currIndex + amt;
//		} else {
//			var newIndex = parseInt( this._optionLis.filter( amt ).data( 'index' ), 10 );
//		}
//
//		if ( newIndex < 0 ) {
//			newIndex = 0;
//		}
//		if ( newIndex > this._optionLis.size() - 1 ) {
//			newIndex = this._optionLis.size() - 1;
//		}
//
//		//Occurs when a full loop has been made
//		if ( newIndex === recIndex ) {
//			return false;
//		}
//
//		var activeID = 'ui-selectmenu-item-' + Math.round( Math.random() * 1000 );
//
//		this._focusedOptionLi().find( 'a:eq(0)' ).attr( 'id', '' );
//
//		if ( this._optionLis.eq( newIndex ).hasClass( 'ui-state-disabled' ) ) {
//			// if option at newIndex is disabled, call _moveFocus, incrementing amt by one
//			( amt > 0 ) ? ++amt : --amt;
//			this._moveFocus( amt, newIndex );
//		} else {
//			this._optionLis.eq( newIndex ).find( 'a:eq(0)' ).attr( 'id',activeID ).focus();
//		}
//
//		this.list.attr( 'aria-activedescendant', activeID );
//	},
//
//	_scrollPage: function( direction ) {
//		var numPerPage = Math.floor( this.list.outerHeight() / this._optionLis.first().outerHeight() );
//		numPerPage = ( direction == 'up' ? -numPerPage : numPerPage );
//		this._moveFocus( numPerPage );
//	},
//
//	_setOption: function( key, value ) {
//		this.options[ key ] = value;
//		if ( key == 'disabled' ) {
//			if ( value ) this.close();
//			this.element
//				.add( this.newelement )
//				.add( this.list )[ value ? 'addClass' : 'removeClass' ]( 'ui-selectmenu-disabled ' + 'ui-state-disabled' )
//				.attr( "aria-disabled" , value )
//				.attr( "tabindex" , value ? 1 : 0 );
//		}
//	},
//
//	disable: function( index, type ){
//		// if options is not provided, call the parents disable function
//		if ( typeof( index ) == 'undefined' ) {
//			this._setOption( 'disabled', true );
//		} else {
//			this._toggleEnabled( ( type || "option" ), index, false );
//		}
//	},
//
//	enable: function( index, type ) {
//		// if options is not provided, call the parents enable function
//		if ( typeof( index ) == 'undefined' ) {
//			this._setOption( 'disabled', false );
//		} else {
//			this._toggleEnabled( ( type || "option" ), index, true );
//		}
//	},
//
//	_disabled: function( elem ) {
//		return $( elem ).hasClass( 'ui-state-disabled' );
//	},
//
//	// true = enabled, false = disabled
//	_toggleEnabled: function( type, index, flag ) {
//		var element = this.element.find( type ).eq( index ),
//			elements = ( type === "optgroup" ) ? this.list.find( 'li.ui-selectmenu-group-' + index ) : this._optionLis.eq( index );
//
//		if ( elements ) {
//			elements
//				.toggleClass( 'ui-state-disabled', !flag )
//				.attr( "aria-disabled", !flag );
//
//			if ( flag ) {
//				element.removeAttr( "disabled" );
//			} else {
//				element.attr( "disabled", "disabled" );
//			}
//		}
//	},
//
//	index: function( newIndex ) {
//		if ( arguments.length ) {
//			if ( !this._disabled( $( this._optionLis[ newIndex ] ) ) && newIndex != this._selectedIndex() ) {
//				this.element[ 0 ].selectedIndex = newIndex;
//				this._refreshValue();
//				this.change();
//			} else {
//				return false;
//			}
//		} else {
//			return this._selectedIndex();
//		}
//	},
//
//	value: function( newValue ) {
//		if ( arguments.length && newValue != this.element[ 0 ].value ) {
//			this.element[ 0 ].value = newValue;
//			this._refreshValue();
//			this.change();
//		} else {
//			return this.element[ 0 ].value;
//		}
//	},
//
//	_refreshValue: function() {
//		var activeClass = ( this.options.style == "popup" ) ? " ui-state-active" : "";
//		var activeID = 'ui-selectmenu-item-' + Math.round( Math.random() * 1000 );
//		// deselect previous
//		this.list
//			.find( '.ui-selectmenu-item-selected' )
//			.removeClass( "ui-selectmenu-item-selected" + activeClass )
//			.find('a')
//			.attr( 'aria-selected', 'false' )
//			.attr( 'id', '' );
//		// select new
//		this._selectedOptionLi()
//			.addClass( "ui-selectmenu-item-selected" + activeClass )
//			.find( 'a' )
//			.attr( 'aria-selected', 'true' )
//			.attr( 'id', activeID );
//
//		// toggle any class brought in from option
//		var currentOptionClasses = ( this.newelement.data( 'optionClasses' ) ? this.newelement.data( 'optionClasses' ) : "" );
//		var newOptionClasses = ( this._selectedOptionLi().data( 'optionClasses' ) ? this._selectedOptionLi().data( 'optionClasses' ) : "" );
//		this.newelement
//			.removeClass( currentOptionClasses )
//			.data( 'optionClasses', newOptionClasses )
//			.addClass( newOptionClasses )
//			.find( '.ui-selectmenu-status' )
//			.html( this._selectedOptionLi().find( 'a:eq(0)' ).html() );
//
//		this.list.attr( 'aria-activedescendant', activeID );
//	},
//
//	_refreshPosition: function() {
//		var o = this.options,
//			positionDefault = {
//				of: this.newelement,
//				my: "left top",
//				at: "left bottom",
//				collision: 'flip'
//			};
//
//		// if its a pop-up we need to calculate the position of the selected li
//		if ( o.style == "popup" ) {
//			var selected = this._selectedOptionLi();
//			positionDefault.my = "left top" + ( this.list.offset().top - selected.offset().top - ( this.newelement.outerHeight() + selected.outerHeight() ) / 2 );
//			positionDefault.collision = "fit";
//		}
//
//		this.listWrap
//			.removeAttr( 'style' )
//			.zIndex( this.element.zIndex() + 2 )
//			.position( $.extend( positionDefault, o.positionOptions ) );
//	}
//});
//
//})( jQuery );

/*
* jQuery File Download Plugin v1.4.2 
*
* http://www.johnculviner.com
*
* Copyright (c) 2013 - John Culviner
*
* Licensed under the MIT license:
*   http://www.opensource.org/licenses/mit-license.php
*
* !!!!NOTE!!!!
* You must also write a cookie in conjunction with using this plugin as mentioned in the orignal post:
* http://johnculviner.com/jquery-file-download-plugin-for-ajax-like-feature-rich-file-downloads/
* !!!!NOTE!!!!
*/
(function($, window){
	// i'll just put them here to get evaluated on script load
	var htmlSpecialCharsRegEx = /[<>&\r\n"']/gm;
	var htmlSpecialCharsPlaceHolders = {
				'<': 'lt;',
				'>': 'gt;',
				'&': 'amp;',
				'\r': "#13;",
				'\n': "#10;",
				'"': 'quot;',
				"'": 'apos;' /*single quotes just to be safe*/
	};

$.extend({
    //
    //$.fileDownload('/path/to/url/', options)
    //  see directly below for possible 'options'
    fileDownload: function (fileUrl, options) {

        //provide some reasonable defaults to any unspecified options below
        var settings = $.extend({

            //
            //Requires jQuery UI: provide a message to display to the user when the file download is being prepared before the browser's dialog appears
            //
            preparingMessageHtml: null,

            //
            //Requires jQuery UI: provide a message to display to the user when a file download fails
            //
            failMessageHtml: null,

            //
            //the stock android browser straight up doesn't support file downloads initiated by a non GET: http://code.google.com/p/android/issues/detail?id=1780
            //specify a message here to display if a user tries with an android browser
            //if jQuery UI is installed this will be a dialog, otherwise it will be an alert
            //
            androidPostUnsupportedMessageHtml: "Unfortunately your Android browser doesn't support this type of file download. Please try again with a different browser.",

            //
            //Requires jQuery UI: options to pass into jQuery UI Dialog
            //
            dialogOptions: { modal: true },

            //
            //a function to call while the dowload is being prepared before the browser's dialog appears
            //Args:
            //  url - the original url attempted
            //
            prepareCallback: function (url) { },

            //
            //a function to call after a file download dialog/ribbon has appeared
            //Args:
            //  url - the original url attempted
            //
            successCallback: function (url) { },

            //
            //a function to call after a file download dialog/ribbon has appeared
            //Args:
            //  responseHtml    - the html that came back in response to the file download. this won't necessarily come back depending on the browser.
            //                      in less than IE9 a cross domain error occurs because 500+ errors cause a cross domain issue due to IE subbing out the
            //                      server's error message with a "helpful" IE built in message
            //  url             - the original url attempted
            //
            failCallback: function (responseHtml, url) { },

            //
            // the HTTP method to use. Defaults to "GET".
            //
            httpMethod: "GET",

            //
            // if specified will perform a "httpMethod" request to the specified 'fileUrl' using the specified data.
            // data must be an object (which will be $.param serialized) or already a key=value param string
            //
            data: null,

            //
            //a period in milliseconds to poll to determine if a successful file download has occured or not
            //
            checkInterval: 100,

            //
            //the cookie name to indicate if a file download has occured
            //
            cookieName: "fileDownload",

            //
            //the cookie value for the above name to indicate that a file download has occured
            //
            cookieValue: "true",

            //
            //the cookie path for above name value pair
            //
            cookiePath: "/",

            //
            //the title for the popup second window as a download is processing in the case of a mobile browser
            //
            popupWindowTitle: "Initiating file download...",

            //
            //Functionality to encode HTML entities for a POST, need this if data is an object with properties whose values contains strings with quotation marks.
            //HTML entity encoding is done by replacing all &,<,>,',",\r,\n characters.
            //Note that some browsers will POST the string htmlentity-encoded whilst others will decode it before POSTing.
            //It is recommended that on the server, htmlentity decoding is done irrespective.
            //
            encodeHTMLEntities: true
            
        }, options);

        var deferred = new $.Deferred();

        //Setup mobile browser detection: Partial credit: http://detectmobilebrowser.com/
        var userAgent = (navigator.userAgent || navigator.vendor || window.opera).toLowerCase();

        var isIos;                  //has full support of features in iOS 4.0+, uses a new window to accomplish this.
        var isAndroid;              //has full support of GET features in 4.0+ by using a new window. Non-GET is completely unsupported by the browser. See above for specifying a message.
        var isOtherMobileBrowser;   //there is no way to reliably guess here so all other mobile devices will GET and POST to the current window.

        if (/ip(ad|hone|od)/.test(userAgent)) {

            isIos = true;

        } else if (userAgent.indexOf('android') !== -1) {

            isAndroid = true;

        } else {

            isOtherMobileBrowser = /avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|playbook|silk|iemobile|iris|kindle|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(userAgent) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|e\-|e\/|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(di|rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|xda(\-|2|g)|yas\-|your|zeto|zte\-/i.test(userAgent.substr(0, 4));

        }

        var httpMethodUpper = settings.httpMethod.toUpperCase();
        
//        Remove diable in moobile
//        if (isAndroid && httpMethodUpper !== "GET") {
//            //the stock android browser straight up doesn't support file downloads initiated by non GET requests: http://code.google.com/p/android/issues/detail?id=1780
//            if ($().dialog) {
//                $("<div>").html(settings.androidPostUnsupportedMessageHtml).dialog(settings.dialogOptions);
//            } else {
//                alert(settings.androidPostUnsupportedMessageHtml);
//            }
//
//            return deferred.reject();
//        }


        var $preparingDialog = null;

        var internalCallbacks = {

            onPrepare: function (url) {

                //wire up a jquery dialog to display the preparing message if specified
                if (settings.preparingMessageHtml) {
                    $preparingDialog = $("<div>").html(settings.preparingMessageHtml).dialog(settings.dialogOptions);
                    settings.preparingMessageHtml();

                } else if (settings.prepareCallback) {

                    settings.prepareCallback(url);

                }

            },

            onSuccess: function (url) {
                
                //remove the perparing message if it was specified
                if ($preparingDialog) {
                    $preparingDialog.dialog('close');
                };

                settings.successCallback(url);

                deferred.resolve(url);
            },

            onFail: function (responseHtml, url) {

                //remove the perparing message if it was specified
                if ($preparingDialog) {
                    $preparingDialog.dialog('close');
                };

                //wire up a jquery dialog to display the fail message if specified
                if (settings.failMessageHtml) {
                    $("<div>").html(settings.failMessageHtml).dialog(settings.dialogOptions);
                }

                settings.failCallback(responseHtml, url);
                
                deferred.reject(responseHtml, url);
            }
        };

        internalCallbacks.onPrepare(fileUrl);

        //make settings.data a param string if it exists and isn't already
        if (settings.data !== null && typeof settings.data !== "string") {
            settings.data = $.param(settings.data);
        }


        var $iframe,
            downloadWindow,
            formDoc,
            $form;

        if (httpMethodUpper === "GET") {

            if (settings.data !== null) {
                //need to merge any fileUrl params with the data object

                var qsStart = fileUrl.indexOf('?');

                if (qsStart !== -1) {
                    //we have a querystring in the url

                    if (fileUrl.substring(fileUrl.length - 1) !== "&") {
                        fileUrl = fileUrl + "&";
                    }
                } else {

                    fileUrl = fileUrl + "?";
                }

                fileUrl = fileUrl + settings.data;
            }

            if (isIos || isAndroid) {

                downloadWindow = window.open(fileUrl);
                downloadWindow.document.title = settings.popupWindowTitle;
                window.focus();

            } else if (isOtherMobileBrowser) {

                window.location(fileUrl);

            } else {

                //create a temporary iframe that is used to request the fileUrl as a GET request
                $iframe = $("<iframe>")
                    .hide()
                    .prop("src", fileUrl)
                    .appendTo("body");
            }

        } else {

            var formInnerHtml = "";

            if (settings.data !== null) {

                $.each(settings.data.replace(/\+/g, ' ').split("&"), function () {

                    var kvp = this.split("=");

                    var key = settings.encodeHTMLEntities ? htmlSpecialCharsEntityEncode(decodeURIComponent(kvp[0])) : decodeURIComponent(kvp[0]);
                    if (key) {
                        var value = settings.encodeHTMLEntities ? htmlSpecialCharsEntityEncode(decodeURIComponent(kvp[1])) : decodeURIComponent(kvp[1]);
                    formInnerHtml += '<input type="hidden" name="' + key + '" value="' + value + '" />';
                    }
                });
            }

            if (isOtherMobileBrowser) {

                $form = $("<form>").appendTo("body");
                $form.hide()
                    .prop('method', settings.httpMethod)
                    .prop('action', fileUrl)
                    .html(formInnerHtml);

            } else {

                if (isIos) {

                    downloadWindow = window.open("about:blank");
                    downloadWindow.document.title = settings.popupWindowTitle;
                    formDoc = downloadWindow.document;
                    window.focus();

                } else {

                    $iframe = $("<iframe style='display: none' src='about:blank'></iframe>").appendTo("body");
                    formDoc = getiframeDocument($iframe);
                }

                formDoc.write("<html><head></head><body><form method='" + settings.httpMethod + "' action='" + fileUrl + "'>" + formInnerHtml + "</form>" + settings.popupWindowTitle + "</body></html>");
                $form = $(formDoc).find('form');
            }

            $form.submit();
        }


        //check if the file download has completed every checkInterval ms
        setTimeout(checkFileDownloadComplete, settings.checkInterval);


        function checkFileDownloadComplete() {

            //has the cookie been written due to a file download occuring?
            if (document.cookie.indexOf(settings.cookieName + "=" + settings.cookieValue) != -1) {

                //execute specified callback
                internalCallbacks.onSuccess(fileUrl);

                //remove the cookie and iframe
                document.cookie = settings.cookieName + "=; expires=" + new Date(1000).toUTCString() + "; path=" + settings.cookiePath;

                cleanUp(false);

                return;
            }

            //has an error occured?
            //if neither containers exist below then the file download is occuring on the current window
            if (downloadWindow || $iframe) {

                //has an error occured?
                try {

                    var formDoc = downloadWindow ? downloadWindow.document : getiframeDocument($iframe);

                    if (formDoc && formDoc.body != null && formDoc.body.innerHTML.length) {

                        var isFailure = true;

                        if ($form && $form.length) {
                            var $contents = $(formDoc.body).contents().first();

                            if ($contents.length && $contents[0] === $form[0]) {
                                isFailure = false;
                            }
                        }

                        if (isFailure) {
                            internalCallbacks.onFail(formDoc.body.innerHTML, fileUrl);

                            cleanUp(true);

                            return;
                        }
                    }
                }
                catch (err) {

                    //500 error less than IE9
                    internalCallbacks.onFail('', fileUrl);

                    cleanUp(true);

                    return;
                }
            }


            //keep checking...
            setTimeout(checkFileDownloadComplete, settings.checkInterval);
        }

        //gets an iframes document in a cross browser compatible manner
        function getiframeDocument($iframe) {
            var iframeDoc = $iframe[0].contentWindow || $iframe[0].contentDocument;
            if (iframeDoc.document) {
                iframeDoc = iframeDoc.document;
            }
            return iframeDoc;
        }

        function cleanUp(isFailure) {

            setTimeout(function() {

                if (downloadWindow) {

                    if (isAndroid) {
                        downloadWindow.close();
                    }

                    if (isIos) {
                        downloadWindow.focus(); //ios safari bug doesn't allow a window to be closed unless it is focused
                        if (isFailure) {
                            downloadWindow.close();
                        }
                    }
                }
                
                //iframe cleanup appears to randomly cause the download to fail
                //not doing it seems better than failure...
                //if ($iframe) {
                //    $iframe.remove();
                //}

            }, 0);
        }


        function htmlSpecialCharsEntityEncode(str) {
            return str.replace(htmlSpecialCharsRegEx, function(match) {
                return '&' + htmlSpecialCharsPlaceHolders[match];
        	});
        }

        return deferred.promise();
    }
});

})(jQuery, this);

/**
 * AJAX File Upload
 * http://github.com/davgothic/AjaxFileUpload
 * 
 * Copyright (c) 2010-2013 David Hancock (http://davidhancock.co)
 *
 * Thanks to Steven Barnett for his generous contributions
 *
 * Licensed under the MIT license ( http://www.opensource.org/licenses/MIT )
 */

//;(function($) {
//	$.fn.AjaxFileUpload = function(options) {
//		
//		var defaults = {
//			action:     "upload.php",
//			onChange:   function(filename) {},
//			onSubmit:   function(filename) {},
//			onComplete: function(filename, response) {}
//		},
//		settings = $.extend({}, defaults, options),
//		randomId = (function() {
//			var id = 0;
//			return function () {
//				return "_AjaxFileUpload" + id++;
//			};
//		})();
//		
//		return this.each(function() {
//			var $this = $(this);
//			if ($this.is("input") && $this.attr("type") === "file") {
//				$this.bind("change", onChange);
//			}
//		});
//		
//		function onChange(e) {
//			var $element = $(e.target),
//				id       = $element.attr('id'),
//				$clone   = $element.removeAttr('id').clone().attr('id', id).AjaxFileUpload(options),
//				filename = $element.val().replace(/.*(\/|\\)/, ""),
//				iframe   = createIframe(),
//				form     = createForm(iframe);
//
//			// We append a clone since the original input will be destroyed
//			$clone.insertBefore($element);
//
//			settings.onChange.call($clone[0], filename);
//
//			iframe.bind("load", {element: $clone, form: form, filename: filename}, onComplete);
//			
//			form.append($element).bind("submit", {element: $clone, iframe: iframe, filename: filename}, onSubmit).submit();
//		}
//		
//		function onSubmit(e) {
//			var data = settings.onSubmit.call(e.data.element, e.data.filename);
//
//			// If false cancel the submission
//			if (data === false) {
//				// Remove the temporary form and iframe
//				$(e.target).remove();
//				e.data.iframe.remove();
//				return false;
//			} else {
//				// Else, append additional inputs
//				for (var variable in data) {
//					$("<input />")
//						.attr("type", "hidden")
//						.attr("name", variable)
//						.val(data[variable])
//						.appendTo(e.target);
//				}
//			}
//		}
//		
//		function onComplete (e) {
//			var $iframe  = $(e.target),
//				doc      = ($iframe[0].contentWindow || $iframe[0].contentDocument).document,
//				response = doc.body.innerHTML;
//
//			if (response) {
//				response = $.parseJSON(response);
//			} else {
//				response = {};
//			}
//
//			settings.onComplete.call(e.data.element, e.data.filename, response);
//			
//			// Remove the temporary form and iframe
//			e.data.form.remove();
//			$iframe.remove();
//		}
//
//		function createIframe() {
//			var id = randomId();
//
//			// The iframe must be appended as a string otherwise IE7 will pop up the response in a new window
//			// http://stackoverflow.com/a/6222471/268669
//			$("body")
//				.append('<iframe src="javascript:false;" name="' + id + '" id="' + id + '" style="display: none;"></iframe>');
//
//			return $('#' + id);
//		}
//		
//		function createForm(iframe) {
//			return $("<form />")
//				.attr({
//					method: "post",
//					action: settings.action,
//					enctype: "multipart/form-data",
//					target: iframe[0].name
//				})
//				.hide()
//				.appendTo("body");
//		}
//	};
//})(jQuery);


jQuery.extend({
    createUploadIframe: function(id, uri)
    {
        //create frame
        var frameId = 'jUploadFrame' + id;
        var iframeHtml = '<iframe id="' + frameId + '" name="' + frameId + '" style="position:absolute; top:-9999px; left:-9999px"';
        if (window.ActiveXObject)
        {
            if (typeof uri == 'boolean') {
                iframeHtml += ' src="' + 'javascript:false' + '"';

            }
            else if (typeof uri == 'string') {
                iframeHtml += ' src="' + uri + '"';

            }
        }
        iframeHtml += ' />';
        jQuery(iframeHtml).appendTo(document.body);

        return jQuery('#' + frameId).get(0);
    },
    createUploadForm: function(id, fileElementId, data)
    {
        //create form	
        var formId = 'jUploadForm' + id;
        var fileId = 'jUploadFile' + id;
        var form = jQuery('<form  action="" method="POST" name="' + formId + '" id="' + formId + '" enctype="multipart/form-data"></form>');
        if (data)
        {
            for (var i in data)
            {
                jQuery('<input type="hidden" name="' + i + '" value="' + data[i] + '" />').appendTo(form);
            }
        }
        var oldElement = jQuery('#' + fileElementId);
        var newElement = jQuery(oldElement).clone();
        jQuery(oldElement).attr('id', fileId);
        jQuery(oldElement).before(newElement);
        jQuery(oldElement).appendTo(form);



        //set attributes
        jQuery(form).css('position', 'absolute');
        jQuery(form).css('top', '-1200px');
        jQuery(form).css('left', '-1200px');
        jQuery(form).appendTo('body');
        return form;
    },
    ajaxFileUpload: function(s) {
        // TODO introduce global settings, allowing the client to modify them for all requests, not only timeout		
        s = jQuery.extend({}, jQuery.ajaxSettings, s);
        var id = new Date().getTime()
        var form = jQuery.createUploadForm(id, s.fileElementId, (typeof (s.data) == 'undefined' ? false : s.data));
        var io = jQuery.createUploadIframe(id, s.secureuri);
        var frameId = 'jUploadFrame' + id;
        var formId = 'jUploadForm' + id;
        // Watch for a new set of requests
        if (s.global && !jQuery.active++)
        {
            jQuery.event.trigger("ajaxStart");
        }
        var requestDone = false;
        // Create the request object
        var xml = {}
        if (s.global)
            jQuery.event.trigger("ajaxSend", [xml, s]);
        // Wait for a response to come back
        var uploadCallback = function(isTimeout)
        {
            var io = document.getElementById(frameId);
            try
            {
                if (io.contentWindow)
                {
                    xml.responseText = io.contentWindow.document.body ? io.contentWindow.document.body.innerHTML : null;
                    xml.responseXML = io.contentWindow.document.XMLDocument ? io.contentWindow.document.XMLDocument : io.contentWindow.document;

                } else if (io.contentDocument)
                {
                    xml.responseText = io.contentDocument.document.body ? io.contentDocument.document.body.innerHTML : null;
                    xml.responseXML = io.contentDocument.document.XMLDocument ? io.contentDocument.document.XMLDocument : io.contentDocument.document;
                }
            } catch (e)
            {
                jQuery.handleError(s, xml, null, e);
            }
            if (xml || isTimeout == "timeout")
            {
                requestDone = true;
                var status;
                try {
                    status = isTimeout != "timeout" ? "success" : "error";
                    // Make sure that the request was successful or notmodified
                    if (status != "error")
                    {
                        // process the data (runs the xml through httpData regardless of callback)
                        var data = jQuery.uploadHttpData(xml, s.dataType);
                        
                        // If a local callback was specified, fire it and pass it the data
                        if (s.success)
                            s.success(data, status);

                        // Fire the global callback
                        if (s.global)
                            jQuery.event.trigger("ajaxSuccess", [xml, s]);
                    } else
                        jQuery.handleError(s, xml, status);
                } catch (e)
                {   
                    status = "error";
                    jQuery.handleError(s, xml, status, e);
                }

                // The request was completed
                if (s.global)
                    jQuery.event.trigger("ajaxComplete", [xml, s]);

                // Handle the global AJAX counter
                if (s.global && !--jQuery.active)
                    jQuery.event.trigger("ajaxStop");

                // Process result
                if (s.complete)
                    s.complete(xml, status);

                jQuery(io).unbind()

                setTimeout(function()
                {
                    try
                    {
                        jQuery(io).remove();
                        jQuery(form).remove();

                    } catch (e)
                    {
                        jQuery.handleError(s, xml, null, e);
                    }

                }, 100)

                xml = null

            }
        }
        // Timeout checker
        if (s.timeout > 0)
        {
            setTimeout(function() {
                // Check to see if the request is still happening
                if (!requestDone)
                    uploadCallback("timeout");
            }, s.timeout);
        }
        try
        {

            var form = jQuery('#' + formId);
            jQuery(form).attr('action', s.url);
            jQuery(form).attr('method', 'POST');
            jQuery(form).attr('target', frameId);
            if (form.encoding)
            {
                jQuery(form).attr('encoding', 'multipart/form-data');
            }
            else
            {
                jQuery(form).attr('enctype', 'multipart/form-data');
            }
            jQuery(form).submit();

        } catch (e)
        {
            jQuery.handleError(s, xml, null, e);
        }

        jQuery('#' + frameId).load(uploadCallback);
        return {abort: function() {
            }};

    },
    uploadHttpData: function(r, type) {
        
        var data = !type;
        
        data = type == "xml" || data ? r.responseXML : r.responseText;
        // If the type is "script", eval it in global context
        if (type == "script")
            jQuery.globalEval(data);
        // Get the JavaScript object, if JSON is used.
        if (type == "json") {
            
            data = JSON.parse( $( data ).text() );
            // eval("data = " + data);
        }
            
        // evaluate scripts within html
        if (type == "html")
            jQuery("<div>").html(data).evalScripts();

        return data;
    },
    handleError: function(s, xhr, status, e) {
        // If a local callback was specified, fire it
        if (s.error) {
            s.error.call(s.context || window, xhr, status, e);
        }

        // Fire the global callback
        if (s.global) {
            (s.context ? jQuery(s.context) : jQuery.event).trigger("ajaxError", [xhr, s, e]);
        }
    }
})

