/*
Date Input 1.2.1
Requires jQuery version: >= 1.2.6
 
Copyright (c) 2007-2008 Jonathan Leighton & Torchbox Ltd
 
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
 
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
 
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.
*/
 
DateInput = (function($) { 
 
    function DateInput(element, options) {
        if (typeof(opts) != "object") options = {};
        $.extend(this, DateInput.DEFAULT_OPTS, options);
 
        var button = $('<span class="jFormComponentDateButton">Find Date</span>');
 
        this.input = $(element);
        this.input.after(button);
        this.button = $(element).parent().find('span.jFormComponentDateButton');
        this.bindMethodsToObj("show", "hide", "hideIfClickOutside", "keydownHandler", "selectDate");
  
        this.build();
        this.selectDate();
        this.hide();
    };
    DateInput.DEFAULT_OPTS = {
        jFormComponentDateSelectorMonthNames: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
        short_jFormComponentDateSelectorMonthNames: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
        short_day_names: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
        start_of_week: 0
    };
    DateInput.prototype = {
        build: function() {
            var monthNav = $('<p class="jFormComponentDateSelectorMonthNavigator">' +
                '<span class="jFormComponentDateSelectorButton jFormComponentDateSelectorPrevious" title="[Page-Up]">&#171;</span>' +
                ' <span class="jFormComponentDateSelectorMonthName"></span> ' +
                '<span class="jFormComponentDateSelectorButton jFormComponentDateSelectorNext" title="[Page-Down]">&#187;</span>' +
                '</p>');
            this.monthNameSpan = $(".jFormComponentDateSelectorMonthName", monthNav);
            $(".jFormComponentDateSelectorPrevious", monthNav).click(this.bindToObj(function() {
                this.moveMonthBy(-1);
            }));
            $(".jFormComponentDateSelectorNext", monthNav).click(this.bindToObj(function() {
                this.moveMonthBy(1);
            }));
    
            var yearNav = $('<p class="jFormComponentDateSelectorYearNavigator">' +
                '<span class="jFormComponentDateSelectorButton jFormComponentDateSelectorPrevious" title="[Ctrl+Page-Up]">&#171;</span>' +
                ' <span class="jFormComponentDateSelectorYearName"></span> ' +
                '<span class="jFormComponentDateSelectorButton jFormComponentDateSelectorNext" title="[Ctrl+Page-Down]">&#187;</span>' +
                '</p>');
            this.yearNameSpan = $(".jFormComponentDateSelectorYearName", yearNav);
            $(".jFormComponentDateSelectorPrevious", yearNav).click(this.bindToObj(function() {
                this.moveMonthBy(-12);
            }));
            $(".jFormComponentDateSelectorNext", yearNav).click(this.bindToObj(function() {
                this.moveMonthBy(12);
            }));
    
            var nav = $('<div class="jFormComponentDateSelectorNavigator"></div>').append(monthNav, yearNav);
    
            var tableShell = "<table><thead><tr>";
            $(this.adjustDays(this.short_day_names)).each(function() {
                tableShell += "<th>" + this + "</th>";
            });
            tableShell += "</tr></thead><tbody></tbody></table>";
    
            this.dateSelector = this.rootLayers = $('<div class="jFormComponentDateSelector"></div>').append(nav, tableShell).insertAfter(this.input);
    
            if ($.browser.msie && $.browser.version < 7) {
      
                this.ieframe = $('<iframe class="jFormComponentDateSelectorIEFrame" frameborder="0" src="#"></iframe>').insertBefore(this.dateSelector);
                this.rootLayers = this.rootLayers.add(this.ieframe);
      
                $(".jFormComponentDateSelectorButton", nav).mouseover(function() {
                    $(this).addClass("hover")
                });
                $(".jFormComponentDateSelectorButton", nav).mouseout(function() {
                    $(this).removeClass("hover")
                });
            };
    
            this.tbody = $("tbody", this.dateSelector);
    
            this.input.change(this.bindToObj(function() {
                this.selectDate();
            }));
            this.selectDate();
        },
 
        selectMonth: function(date) {
            var newMonth = new Date(date.getFullYear(), date.getMonth(), 1);
    
            if (!this.currentMonth || !(this.currentMonth.getFullYear() == newMonth.getFullYear() &&
                this.currentMonth.getMonth() == newMonth.getMonth())) {
      
                this.currentMonth = newMonth;
      
                var rangeStart = this.rangeStart(date), rangeEnd = this.rangeEnd(date);
                var numDays = this.daysBetween(rangeStart, rangeEnd);
                var dayCells = "";
      
                for (var i = 0; i <= numDays; i++) {
                    var currentDay = new Date(rangeStart.getFullYear(), rangeStart.getMonth(), rangeStart.getDate() + i, 12, 00);
        
                    if (this.isFirstDayOfWeek(currentDay)) dayCells += "<tr>";
        
                    if (currentDay.getMonth() == date.getMonth()) {
                        dayCells += '<td class="jFormComponentDateSelectorSelectedDay" date="' + this.dateToString(currentDay) + '">' + currentDay.getDate() + '</td>';
                    } else {
                        dayCells += '<td class="jFormComponentDateSelectorUnselectedMonth" date="' + this.dateToString(currentDay) + '">' + currentDay.getDate() + '</td>';
                    };
        
                    if (this.isLastDayOfWeek(currentDay)) dayCells += "</tr>";
                };
                this.tbody.empty().append(dayCells);
      
                this.monthNameSpan.empty().append(this.monthName(date));
                this.yearNameSpan.empty().append(this.currentMonth.getFullYear());
      
                $(".jFormComponentDateSelectorSelectedDay", this.tbody).click(this.bindToObj(function(event) {
                    this.changeInput($(event.target).attr("date"));
                }));
      
                $("td[date=" + this.dateToString(new Date()) + "]", this.tbody).addClass("jFormComponentDateSelectorToday");
      
                $("td.jFormComponentDateSelectorSelectedDay", this.tbody).mouseover(function() {
                    $(this).addClass("hover")
                });
                $("td.jFormComponentDateSelectorSelectedDay", this.tbody).mouseout(function() {
                    $(this).removeClass("hover")
                });
            };
    
            $('.jFormComponentDateSelectorSelected', this.tbody).removeClass("jFormComponentDateSelectorSelected");
            $('td[date=' + this.selectedDateString + ']', this.tbody).addClass("jFormComponentDateSelectorSelected");
        },
  
        selectDate: function(date) {
            if (typeof(date) == "undefined") {
                date = this.stringToDate(this.input.val());
            };
            if (!date) date = new Date();
    
            this.selectedDate = date;
            this.selectedDateString = this.dateToString(this.selectedDate);
            this.selectMonth(this.selectedDate);
        },
  
        changeInput: function(dateString) {
            this.input.val(dateString).change();
            this.hide();
        },
  
        show: function() {
            this.rootLayers.css("display", "block");
            this.button.unbind("click", this.show);
            this.input.unbind("focus", this.show);
            $(document.body).keydown(this.keydownHandler);
            $([window, document.body]).click(this.hideIfClickOutside);
            this.setPosition();
        },
  
        hide: function() {
            this.rootLayers.css("display", "none");
            $([window, document.body]).unbind("click", this.hideIfClickOutside);
            this.button.click(this.show);
            this.input.focus(this.show);
            $(document.body).unbind("keydown", this.keydownHandler);
        },
  
        hideIfClickOutside: function(event) {
            if (event.target != this.input[0] && event.target != this.button[0] && !this.insideSelector(event)) {
                this.hide();
            };
        },
  
        insideSelector: function(event) {
            var offset = this.dateSelector.offset();
            offset.right = offset.left + this.dateSelector.outerWidth();
            offset.bottom = offset.top + this.dateSelector.outerHeight();
 
    
            return event.pageY < offset.bottom &&
            event.pageY > offset.top &&
            event.pageX < offset.right &&
            event.pageX > offset.left;
        },
  
        keydownHandler: function(event) {
            switch (event.keyCode)
            {
                case 9:
                case 27:
                    this.hide();
                    return;
                    break;
                case 13:
                    this.changeInput(this.selectedDateString);
                    break;
                case 33:
                    this.moveDateMonthBy(event.ctrlKey ? -12 : -1);
                    break;
                case 34:
                    this.moveDateMonthBy(event.ctrlKey ? 12 : 1);
                    break;
                case 38:
                    this.moveDateBy(-7);
                    break;
                case 40:
                    this.moveDateBy(7);
                    break;
                case 37:
                    this.moveDateBy(-1);
                    break;
                case 39:
                    this.moveDateBy(1);
                    break;
                default:
                    return;
            }
            event.preventDefault();
        },
  
        stringToDate: function(string) {
            string = string.replace(/[^\d]/g, '/');
            if (string.match(/^(\d{1,2})\/(\d{1,2})\/(\d{4,4})$/)) {
                return new Date(string);
            } else {
                return null;
            };
        },
  
        dateToString: function(date) {
            return padString(date.getMonth()+1) +'/'+ padString(date.getDate()) +"/"+ date.getFullYear();
 
            function padString(number){
                number = '' + number;
                if(number.length == 1){
                    number = '0'+number;
                }
                return number;
            }
        },
  
        setPosition: function() {
            var offset = this.button.position();
            this.rootLayers.css({
                top: offset.top,
                left: offset.left + this.button.outerWidth() + 4
            });
            if (this.ieframe) {
                this.ieframe.css({
                    width: this.dateSelector.outerWidth(),
                    height: this.dateSelector.outerHeight()
                });
            }
            var bottom = offset.top + this.dateSelector.outerHeight() + 12;
            var top = '';
            if(jFormerUtility.isSet(window.scrollY)) {
                top = window.scrollY;
            }
            else { // IE FTL
                top = document.documentElement.scrollTop;
            }
            if(top + $(window).height() > bottom) {
            } else {
                $.scrollTo(bottom - $(window).height() + 'px', 250, {
                    axis:'y'
                });
            }
        },
  
        moveDateBy: function(amount) {
            var newDate = new Date(this.selectedDate.getFullYear(), this.selectedDate.getMonth(), this.selectedDate.getDate() + amount);
            this.selectDate(newDate);
        },
  
        moveDateMonthBy: function(amount) {
            var newDate = new Date(this.selectedDate.getFullYear(), this.selectedDate.getMonth() + amount, this.selectedDate.getDate());
            if (newDate.getMonth() == this.selectedDate.getMonth() + amount + 1) {
      
                newDate.setDate(0);
            };
            this.selectDate(newDate);
        },
  
        moveMonthBy: function(amount) {
            var newMonth = new Date(this.currentMonth.getFullYear(), this.currentMonth.getMonth() + amount, this.currentMonth.getDate());
            this.selectMonth(newMonth);
        },
  
        monthName: function(date) {
            return this.jFormComponentDateSelectorMonthNames[date.getMonth()];
        },
  
        bindToObj: function(fn) {
            var self = this;
            return function() {
                return fn.apply(self, arguments)
            };
        },
  
        bindMethodsToObj: function() {
            for (var i = 0; i < arguments.length; i++) {
                this[arguments[i]] = this.bindToObj(this[arguments[i]]);
            };
        },
  
        indexFor: function(array, value) {
            for (var i = 0; i < array.length; i++) {
                if (value == array[i]) return i;
            };
        },
  
        monthNum: function(jFormComponentDateSelectorMonthName) {
            return this.indexFor(this.jFormComponentDateSelectorMonthNames, jFormComponentDateSelectorMonthName);
        },
  
        shortMonthNum: function(jFormComponentDateSelectorMonthName) {
            return this.indexFor(this.short_jFormComponentDateSelectorMonthNames, jFormComponentDateSelectorMonthName);
        },
  
        shortDayNum: function(day_name) {
            return this.indexFor(this.short_day_names, day_name);
        },
  
        daysBetween: function(start, end) {
            start = Date.UTC(start.getFullYear(), start.getMonth(), start.getDate());
            end = Date.UTC(end.getFullYear(), end.getMonth(), end.getDate());
            return (end - start) / 86400000;
        },
  
        changeDayTo: function(dayOfWeek, date, direction) {
            var difference = direction * (Math.abs(date.getDay() - dayOfWeek - (direction * 7)) % 7);
            return new Date(date.getFullYear(), date.getMonth(), date.getDate() + difference);
        },
  
        rangeStart: function(date) {
            return this.changeDayTo(this.start_of_week, new Date(date.getFullYear(), date.getMonth()), -1);
        },
  
        rangeEnd: function(date) {
            return this.changeDayTo((this.start_of_week - 1) % 7, new Date(date.getFullYear(), date.getMonth() + 1, 0), 1);
        },
  
        isFirstDayOfWeek: function(date) {
            return date.getDay() == this.start_of_week;
        },
  
        isLastDayOfWeek: function(date) {
            return date.getDay() == (this.start_of_week - 1) % 7;
        },
  
        adjustDays: function(days) {
            var newDays = [];
            for (var i = 0; i < days.length; i++) {
                newDays[i] = days[(i + this.start_of_week) % 7];
            };
            return newDays;
        }
    };
 
    $.fn.date_input = function(opts) {
        return this.each(function() {
            new DateInput(this, opts);
        });
    };
    $.date_input = {
        initialize: function(opts) {
            $("input.date_input").date_input(opts);
        }
    };
 
    return DateInput;
})(jQuery); 
/// <reference path="../../../lib/jquery-1.2.6.js" /> 
/*
	Masked Input plugin for jQuery
	Copyright (c) 2007-2009 Josh Bush (digitalbush.com)
	Licensed under the MIT license (http://digitalbush.com/projects/masked-input-plugin/#license)
	Version: 1.2.2 (03/09/2009 22:39:06)
*/
(function($) {
	var pasteEventName = ($.browser.msie ? 'paste' : 'input') + ".mask";
	var iPhone = (window.orientation != undefined);
 
	$.mask = {
		//Predefined character definitions
		definitions: {
			'9': "[0-9]",
			'a': "[A-Za-z]",
			'*': "[A-Za-z0-9]"
		}
	};
 
	$.fn.extend({
		//Helper Function for Caret positioning
		caret: function(begin, end) {
			if (this.length == 0) return;
			if (typeof begin == 'number') {
				end = (typeof end == 'number') ? end : begin;
				return this.each(function() {
					if (this.setSelectionRange) {
						this.focus();
						this.setSelectionRange(begin, end);
					} else if (this.createTextRange) {
						var range = this.createTextRange();
						range.collapse(true);
						range.moveEnd('character', end);
						range.moveStart('character', begin);
						range.select();
					}
				});
			} else {
				if (this[0].setSelectionRange) {
					begin = this[0].selectionStart;
					end = this[0].selectionEnd;
				} else if (document.selection && document.selection.createRange) {
					var range = document.selection.createRange();
					begin = 0 - range.duplicate().moveStart('character', -100000);
					end = begin + range.text.length;
				}
				return { begin: begin, end: end };
			}
		},
		unmask: function() { return this.trigger("unmask"); },
		mask: function(mask, settings) {
			if (!mask && this.length > 0) {
				var input = $(this[0]);
				var tests = input.data("tests");
				return $.map(input.data("buffer"), function(c, i) {
					return tests[i] ? c : null;
				}).join('');
			}
			settings = $.extend({
				placeholder: "_",
				completed: null
			}, settings);
 
			var defs = $.mask.definitions;
			var tests = [];
			var partialPosition = mask.length;
			var firstNonMaskPos = null;
			var len = mask.length;
 
			$.each(mask.split(""), function(i, c) {
				if (c == '?') {
					len--;
					partialPosition = i;
				} else if (defs[c]) {
					tests.push(new RegExp(defs[c]));
					if(firstNonMaskPos==null)
						firstNonMaskPos =  tests.length - 1;
				} else {
					tests.push(null);
				}
			});
 
			return this.each(function() {
				var input = $(this);
				var buffer = $.map(mask.split(""), function(c, i) { if (c != '?') return defs[c] ? settings.placeholder : c });
				var ignore = false;  			//Variable for ignoring control keys
				var focusText = input.val();
 
				input.data("buffer", buffer).data("tests", tests);
 
				function seekNext(pos) {
					while (++pos <= len && !tests[pos]);
					return pos;
				};
 
				function shiftL(pos) {
					while (!tests[pos] && --pos >= 0);
					for (var i = pos; i < len; i++) {
						if (tests[i]) {
							buffer[i] = settings.placeholder;
							var j = seekNext(i);
							if (j < len && tests[i].test(buffer[j])) {
								buffer[i] = buffer[j];
							} else
								break;
						}
					}
					writeBuffer();
					input.caret(Math.max(firstNonMaskPos, pos));
				};
 
				function shiftR(pos) {
					for (var i = pos, c = settings.placeholder; i < len; i++) {
						if (tests[i]) {
							var j = seekNext(i);
							var t = buffer[i];
							buffer[i] = c;
							if (j < len && tests[j].test(t))
								c = t;
							else
								break;
						}
					}
				};
 
				function keydownEvent(e) {
					var pos = $(this).caret();
					var k = e.keyCode;
					ignore = (k < 16 || (k > 16 && k < 32) || (k > 32 && k < 41));
                                        if(!e.shiftKey){
                                            if(k==36){
                                                e.preventDefault();
                                                $(this).caret(seekNext(0));
                                            }
 
                                            if(k==35){
                                                e.preventDefault();
                                                var tempPos = input.val().indexOf(' ');
                                                var tempLength = input.val().length;
                                                while (tests[tempPos] == null || input.val().charAt(tempPos) != ' '){
                                                    tempPos = tempPos + 1;
                                                    if (tempPos == tempLength){
                                                        break;
                                                    }
                                                }
                                                $(this).caret(tempPos);
                                                return false;
                                            }
                                        }
 
					//delete selection before proceeding
					if ((pos.begin - pos.end) != 0 && (!ignore || k == 8 || k == 46))
						clearBuffer(pos.begin, pos.end);
 
					//backspace, delete, and escape get special treatment
					if (k == 8 || k == 46 || (iPhone && k == 127)) {//backspace/delete
						shiftL(pos.begin + (k == 46 ? 0 : -1));
						return false;
					} else if (k == 27) {//escape
						input.val(focusText);
						input.caret(0, checkVal());
						return false;
					}
				};
 
				function keypressEvent(e) {
					if (ignore) {
						ignore = false;
						//Fixes Mac FF bug on backspace
						return (e.keyCode == 8) ? false : null;
					}
					e = e || window.event;
					var k = e.charCode || e.keyCode || e.which;
					var pos = $(this).caret();
 
					if (e.ctrlKey || e.altKey || e.metaKey) {//Ignore
						return true;
					} else if ((k >= 32 && k <= 125) || k > 186) {//typeable characters
                                            var p = seekNext(pos.begin - 1);
                                            if (p < len) {
                                                var c = String.fromCharCode(k);
                                                if (tests[p].test(c)) {
                                                    shiftR(p);
                                                    buffer[p] = c;
                                                    writeBuffer();
                                                    var next = seekNext(p);
                                                    $(this).caret(next);
                                                    if (settings.completed && next == len)
                                                        settings.completed.call(input);
                                                }
                                            }
					}
					return false;
				};
 
				function clearBuffer(start, end) {
					for (var i = start; i < end && i < len; i++) {
						if (tests[i])
							buffer[i] = settings.placeholder;
					}
				};
 
				function writeBuffer() { return input.val(buffer.join('')).val(); };
 
				function checkVal(allow) {
					//try to place characters where they belong
					var test = input.val();
					var lastMatch = -1;
					for (var i = 0, pos = 0; i < len; i++) {
						if (tests[i]) {
							buffer[i] = settings.placeholder;
							while (pos++ < test.length) {
								var c = test.charAt(pos - 1);
								if (tests[i].test(c)) {
									buffer[i] = c;
									lastMatch = i;
									break;
								}
							}
							if (pos > test.length)
								break;
						} else if (buffer[i] == test[pos] && i!=partialPosition) {
							pos++;
							lastMatch = i;
						}
					}
					if (!allow && lastMatch + 1 < partialPosition) {
						input.val("");
						clearBuffer(0, len);
					} else if (allow || lastMatch + 1 >= partialPosition) {
						writeBuffer();
						if (!allow) input.val(input.val().substring(0, lastMatch + 1));
					}
					return (partialPosition ? i : firstNonMaskPos);
				};
 
				if (!input.attr("readonly"))
					input
					.one("unmask", function() {
						input
							.unbind(".mask")
							.removeData("buffer")
							.removeData("tests");
					})
					.bind("focus.mask", function() {
						focusText = input.val();
						var pos = checkVal();
						writeBuffer();
						setTimeout(function() {
							if (pos == mask.length)
								input.caret(0, pos);
							else
								input.caret(pos);
						}, 0);
					})
					.bind("blur.mask", function() {
						checkVal();
						if (input.val() != focusText)
							input.change();
					})
					.bind("keydown.mask", keydownEvent)
					.bind("keypress.mask", keypressEvent)
					.bind(pasteEventName, function() {
						setTimeout(function() { input.caret(checkVal(true)); }, 0);
					});
 
				checkVal(); //Perform initial check for existing values
			});
		}
	});
})(jQuery);/**
 * jQuery.ScrollTo - Easy element scrolling using jQuery.
 * Copyright (c) 2007-2009 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com
 * Dual licensed under MIT and GPL.
 * Date: 3/9/2009
 * @author Ariel Flesler
 * @version 1.4.1
 *
 * http://flesler.blogspot.com/2007/10/jqueryscrollto.html
 */
;(function($){var m=$.scrollTo=function(b,h,f){$(window).scrollTo(b,h,f)};m.defaults={axis:'xy',duration:parseFloat($.fn.jquery)>=1.3?0:1};m.window=function(b){return $(window).scrollable()};$.fn.scrollable=function(){return this.map(function(){var b=this,h=!b.nodeName||$.inArray(b.nodeName.toLowerCase(),['iframe','#document','html','body'])!=-1;if(!h)return b;var f=(b.contentWindow||b).document||b.ownerDocument||b;return $.browser.safari||f.compatMode=='BackCompat'?f.body:f.documentElement})};$.fn.scrollTo=function(l,j,a){if(typeof j=='object'){a=j;j=0}if(typeof a=='function')a={onAfter:a};if(l=='max')l=9e9;a=$.extend({},m.defaults,a);j=j||a.speed||a.duration;a.queue=a.queue&&a.axis.length>1;if(a.queue)j/=2;a.offset=n(a.offset);a.over=n(a.over);return this.scrollable().each(function(){var k=this,o=$(k),d=l,p,g={},q=o.is('html,body');switch(typeof d){case'number':case'string':if(/^([+-]=)?\d+(\.\d+)?(px)?$/.test(d)){d=n(d);break}d=$(d,this);case'object':if(d.is||d.style)p=(d=$(d)).offset()}$.each(a.axis.split(''),function(b,h){var f=h=='x'?'Left':'Top',i=f.toLowerCase(),c='scroll'+f,r=k[c],s=h=='x'?'Width':'Height';if(p){g[c]=p[i]+(q?0:r-o.offset()[i]);if(a.margin){g[c]-=parseInt(d.css('margin'+f))||0;g[c]-=parseInt(d.css('border'+f+'Width'))||0}g[c]+=a.offset[i]||0;if(a.over[i])g[c]+=d[s.toLowerCase()]()*a.over[i]}else g[c]=d[i];if(/^\d+$/.test(g[c]))g[c]=g[c]<=0?0:Math.min(g[c],u(s));if(!b&&a.queue){if(r!=g[c])t(a.onAfterFirst);delete g[c]}});t(a.onAfter);function t(b){o.animate(g,j,a.easing,b&&function(){b.call(this,l,a)})};function u(b){var h='scroll'+b;if(!q)return k[h];var f='client'+b,i=k.ownerDocument.documentElement,c=k.ownerDocument.body;return Math.max(i[h],c[h])-Math.min(i[f],c[f])}}).end()};function n(b){return typeof b=='object'?b:{top:b,left:b}}})(jQuery);
 
/**
 * jQuery.LocalScroll - Animated scrolling navigation, using anchors.
 * Copyright (c) 2007-2009 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com
 * Dual licensed under MIT and GPL.
 * Date: 3/11/2009
 * @author Ariel Flesler
 * @version 1.2.7
 **/
;(function($){var l=location.href.replace(/#.*/,'');var g=$.localScroll=function(a){$('body').localScroll(a)};g.defaults={duration:1e3,axis:'y',event:'click',stop:true,target:window,reset:true};g.hash=function(a){if(location.hash){a=$.extend({},g.defaults,a);a.hash=false;if(a.reset){var e=a.duration;delete a.duration;$(a.target).scrollTo(0,a);a.duration=e}i(0,location,a)}};$.fn.localScroll=function(b){b=$.extend({},g.defaults,b);return b.lazy?this.bind(b.event,function(a){var e=$([a.target,a.target.parentNode]).filter(d)[0];if(e)i(a,e,b)}):this.find('a,area').filter(d).bind(b.event,function(a){i(a,this,b)}).end().end();function d(){return!!this.href&&!!this.hash&&this.href.replace(this.hash,'')==l&&(!b.filter||$(this).is(b.filter))}};function i(a,e,b){var d=e.hash.slice(1),f=document.getElementById(d)||document.getElementsByName(d)[0];if(!f)return;if(a)a.preventDefault();var h=$(b.target);if(b.lock&&h.is(':animated')||b.onBefore&&b.onBefore.call(b,a,f,h)===false)return;if(b.stop)h.stop(true);if(b.hash){var j=f.id==d?'id':'name',k=$('<a> </a>').attr(j,d).css({position:'absolute',top:$(window).scrollTop(),left:$(window).scrollLeft()});f[j]='';$('body').prepend(k);location=e.hash;k.remove();f[j]=d}h.scrollTo(f,b).trigger('notify.serialScroll',[f])}})(jQuery);
 
/**
 * jQuery[a] - Animated scrolling of series
 * Copyright (c) 2007-2008 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com
 * Dual licensed under MIT and GPL.
 * Date: 3/20/2008
 * @author Ariel Flesler
 * @version 1.2.1
 *
 * http://flesler.blogspot.com/2008/02/jqueryserialscroll.html
 */
;(function($){var a='serialScroll',b='.'+a,c='bind',C=$[a]=function(b){$.scrollTo.window()[a](b)};C.defaults={duration:1e3,axis:'x',event:'click',start:0,step:1,lock:1,cycle:1,constant:1};$.fn[a]=function(y){y=$.extend({},C.defaults,y);var z=y.event,A=y.step,B=y.lazy;return this.each(function(){var j=y.target?this:document,k=$(y.target||this,j),l=k[0],m=y.items,o=y.start,p=y.interval,q=y.navigation,r;if(!B)m=w();if(y.force)t({},o);$(y.prev||[],j)[c](z,-A,s);$(y.next||[],j)[c](z,A,s);if(!l.ssbound)k[c]('prev'+b,-A,s)[c]('next'+b,A,s)[c]('goto'+b,t);if(p)k[c]('start'+b,function(e){if(!p){v();p=1;u()}})[c]('stop'+b,function(){v();p=0});k[c]('notify'+b,function(e,a){var i=x(a);if(i>-1)o=i});l.ssbound=1;if(y.jump)(B?k:w())[c](z,function(e){t(e,x(e.target))});if(q)q=$(q,j)[c](z,function(e){e.data=Math.round(w().length/q.length)*q.index(this);t(e,this)});function s(e){e.data+=o;t(e,this)};function t(e,a){if(!isNaN(a)){e.data=a;a=l}var c=e.data,n,d=e.type,f=y.exclude?w().slice(0,-y.exclude):w(),g=f.length,h=f[c],i=y.duration;if(d)e.preventDefault();if(p){v();r=setTimeout(u,y.interval)}if(!h){n=c<0?0:n=g-1;if(o!=n)c=n;else if(!y.cycle)return;else c=g-n-1;h=f[c]}if(!h||d&&o==c||y.lock&&k.is(':animated')||d&&y.onBefore&&y.onBefore.call(a,e,h,k,w(),c)===!1)return;if(y.stop)k.queue('fx',[]).stop();if(y.constant)i=Math.abs(i/A*(o-c));k.scrollTo(h,i,y).trigger('notify'+b,[c])};function u(){k.trigger('next'+b)};function v(){clearTimeout(r)};function w(){return $(m,l)};function x(a){if(!isNaN(a))return a;var b=w(),i;while((i=b.index(a))==-1&&a!=l)a=a.parentNode;return i}})}})(jQuery);/**
 * jquery.simpletip 1.3.1. A simple tooltip plugin
 *
 * Copyright (c) 2009 Craig Thompson
 * http://craigsworks.com
 *
 * Licensed under GPLv3
 * http://www.opensource.org/licenses/gpl-3.0.html
 *
 * Launch  : February 2009
 * Version : 1.3.1
 * Released: February 5, 2009 - 11:04am
 */
(function($){
 
    function Simpletip(elem, conf)
    {
        var self = this;
        elem = jQuery(elem);
      
        var wrappedContent = ['<span class="tipArrow"></span><div class="tipContent">',conf.content.html(),'</div>'].join('');
 
        var tooltip = jQuery(conf.content)
        .addClass(conf.baseClass)
        .addClass( (conf.fixed) ? conf.fixedClass : '' )
        .addClass( (conf.persistent) ? conf.persistentClass : '' )
        .html(wrappedContent);
 
        // Add an event listener that listens for a window resize and repositions the element
        jQuery(window).resize(function(){
            if(tooltip.is(':visible')) {
                self.updatePos();
            }
            
        });
 
        if(!conf.hidden) tooltip.show();
        else tooltip.hide();
 
        if(!conf.persistent)
        {
            elem.hover(
                function(event){
                    self.show(event)
                },
                function(){
                    self.hide()
                }
                );
 
            if(!conf.fixed)
            {
                elem.mousemove( function(event){
                    if(tooltip.css('display') !== 'none') self.updatePos(event);
                });
            };
        }
        else
        {
            elem.click(function(event)
            {
                if(event.target === elem.get(0))
                {
            //if(tooltip.css('display') !== 'none')
            // self.hide();
            // else
            //self.show();
            }
            });
 
            jQuery(window).mousedown(function(event)
            {
                if(tooltip.css('display') !== 'none')
                {
                    var check = (conf.focus) ? jQuery(event.target).parents('.tooltip').andSelf().filter(function(){
                        return this === tooltip.get(0)
                    }).length : 0;
                //if(check === 0) self.hide();
                };
            });
        };
 
 
        jQuery.extend(self,
        {
            getVersion: function()
            {
                return [1, 2, 0];
            },
 
            getParent: function()
            {
                return elem;
            },
 
            getTooltip: function()
            {
                return tooltip;
            },
 
            getPos: function()
            {
                return tooltip.position();
            },
 
            setPos: function(posX, posY)
            {
                var elemPos = elem.position();
 
                if(typeof posX == 'string') posX = parseInt(posX) + elemPos.left;
                if(typeof posY == 'string') posY = parseInt(posY) + elemPos.top;
 
                tooltip.css({
                    left: posX,
                    top: posY
                });
 
                return self;
            },
 
            show: function(event)
            {
                var onbefore = conf.onBeforeShow();
                if(onbefore === false){
                    return false;
                }
                self.updatePos( (conf.fixed) ? null : event );
 
                switch(conf.showEffect)
                {
                    case 'fade':
                        tooltip.fadeIn(conf.showTime); break;
                    case 'slide':
                        tooltip.slideDown(conf.showTime, self.updatePos); break;
                    case 'custom':
                        conf.showCustom.call(tooltip, conf.showTime); break;
                    default:
                    case 'none':
                        tooltip.show(); break;
                };
 
                tooltip.addClass(conf.activeClass);
 
                conf.onShow.call(self);
 
                jQuery(document).trigger('blurTip', [tooltip, 'show']);
 
                return self;
            },
 
            hide: function()
            {
                conf.onBeforeHide.call(self);
 
                switch(conf.hideEffect)
                {
                    case 'fade':
                        tooltip.fadeOut(conf.hideTime); break;
                    case 'slide':
                        tooltip.slideUp(conf.hideTime); break;
                    case 'custom':
                        conf.hideCustom.call(tooltip, conf.hideTime); break;
                    default:
                    case 'none':
                        tooltip.hide(); break;
                };
 
                tooltip.removeClass(conf.activeClass);
 
                conf.onHide.call(self);
 
                jQuery(document).trigger('blurTip', [tooltip, 'hide']);
 
                return self;
            },
 
            update: function(content)
            {
                
                //tooltip.html(content);
 
                return self;
            },
 
            load: function(uri, data)
            {
                conf.beforeContentLoad.call(self);
 
                tooltip.load(uri, data, function(){
                    conf.onContentLoad.call(self);
                });
 
                return self;
            },
 
            boundryCheck: function(posX, posY)
            {
                var newX = posX + tooltip.outerWidth();
                var newY = posY + tooltip.outerHeight();
 
                var windowWidth = jQuery(window).width() + jQuery(window).scrollLeft();
                var windowHeight = jQuery(window).height() + jQuery(window).scrollTop();
 
                return [(newX >= windowWidth), (newY >= windowHeight)];
            },
 
            updatePos: function(event)
            {
                var tooltipWidth = tooltip.outerWidth();
                var tooltipHeight = tooltip.outerHeight();
 
                if(!event && conf.fixed)
                {
                    if(conf.position.constructor == Array)
                    {
                        posX = parseInt(conf.position[0]);
                        posY = parseInt(conf.position[1]);
                    }
                    else if(jQuery(conf.position).attr('nodeType') === 1)
                    {
                        var offset = jQuery(conf.position).position();
                        posX = offset.left;
                        posY = offset.top;
                    }
                    else
                    {
                        var elemPos = elem.position();
                        var elemWidth = elem.outerWidth();
                        var elemHeight = elem.outerHeight();
                        var posX = '';
                        var posY = '';
                        switch(conf.position)
                        {
                            case 'top':
                                posX = elemPos.left - (tooltipWidth / 2) + (elemWidth / 2);
                                posY = elemPos.top - tooltipHeight;
                                break;
 
                            case 'bottom':
                                posX = elemPos.left - (tooltipWidth / 2) + (elemWidth / 2);
                                posY = elemPos.top + elemHeight;
                                break;
 
                            case 'left':
                                posX = elemPos.left - tooltipWidth;
                                posY = elemPos.top - (tooltipHeight / 2) + (elemHeight / 2);
                                break;
 
                            case 'right':
                                posX = elemPos.left + elemWidth;
                                posY = elemPos.top - (tooltipHeight / 2) + (elemHeight / 2);
                                break;
 
                            case 'topRight':
                                posX = elemPos.left + elemWidth;
                                posY = elemPos.top;
                                break;
 
                            default:
                            case 'default':
                                posX = (elemWidth / 2) + elemPos.left + 20;
                                posY = elemPos.top;
                                break;
                        };
                    };
                }
                else
                {
                    var posX = event.pageX;
                    var posY = event.pageY;
                };
 
                if(typeof conf.position != 'object')
                {
                    posX = posX + conf.offset[0];
                    posY = posY + conf.offset[1];
 
                    if(conf.boundryCheck)
                    {
                        var overflow = self.boundryCheck(posX, posY);
 
                        if(overflow[0]) posX = posX - (tooltipWidth / 2) - (2 * conf.offset[0]);
                        if(overflow[1]) posY = posY - (tooltipHeight / 2) - (2 * conf.offset[1]);
                    }
                }
                else
                {
                    if(typeof conf.position[0] == "string") posX = String(posX);
                    if(typeof conf.position[1] == "string") posY = String(posY);
                };
 
                self.setPos(posX, posY);
 
                return self;
            }
        });
    };
 
    jQuery.fn.simpletip = function(conf)
    {
        // Check if a simpletip is already present
        var api = jQuery(this).eq(typeof conf == 'number' ? conf : 0).data("simpletip");
        if(api) return api;
 
        // Default configuration
        var defaultConf = {
            // Basics
            content: 'A simple tooltip',
            persistent: false,
            focus: false,
            hidden: true,
 
            // Positioning
            position: 'default',
            offset: [0, 0],
            boundryCheck: false,
            fixed: true,
 
            // Effects
            showEffect: 'fade',
            showTime: 150,
            showCustom: null,
            hideEffect: 'fade',
            hideTime: 150,
            hideCustom: null,
 
            // Selectors and classes
            baseClass: 'tooltip',
            activeClass: 'active',
            fixedClass: 'fixed',
            persistentClass: 'persistent',
            focusClass: 'focus',
 
            // Callbacks
            onBeforeShow: function(){
                return true;
            },
            onShow: function(){},
            onBeforeHide: function(){},
            onHide: function(){},
            beforeContentLoad: function(){},
            onContentLoad: function(){}
        };
        jQuery.extend(defaultConf, conf);
 
        this.each(function()
        {
            var el = new Simpletip(jQuery(this), defaultConf);
            jQuery(this).data("simpletip", el);
        });
 
        return this;
    };
})();/**
 * jFormer is the steward of the form. Holds base functions which are not specific to any page, section, or component.
 * jFormer is initialized on top of the existing HTML and handles validation, tool tip management, dependencies, instances, triggers, pages, and form submission.
 *
 * @author Kirk Ouimet <kirk@kirkouimet.com> 
 * @author Seth Jensen <seth@sethjdesign.com> 
 * @version .5
 */
JFormer = Class.extend({
    init: function(formId, options) {
        // Update the options object
        this.options = $.extend({
            pageNavigator: false,
            saveState: false,
            splashPage: false,
            progressBar: false,
            alertsEnabled: true,
            clientSideValidation: true,
            submitButtonText: 'Submit',
            onSubmitStart: function() {return true;},
            onSubmitFinish: function() {return true;}
        }, options.options || {});
 
        // Show number of binds
        if(this.options.trackBind){
            jQuery.fn.bind = function(bind) {
                return function () {
                    console.count("jQuery Bind Count");
                    console.log("jQuery Bind %o", arguments[0] , this);
                    return bind.apply(this, arguments);
                };
            }(jQuery.fn.bind);
        }
 
        // Class variables
        this.id = formId;
        this.form = $(['form#',this.id].join(''));
        this.formData = {};
        this.jFormPageWrapper = this.form.find('div.jFormPageWrapper');
        this.jFormPageScroller = this.form.find('div.jFormPageScroller');
        this.jFormPageNavigator = null;
        this.jFormPages = {};
        this.currentJFormPage = null;
        this.maxJFormPageIdArrayIndexReached = null;
        this.jFormPageIdArray = [];
        this.currentJFormPageIdArrayIndex = null;
        this.blurredTips = [];
 
        // Stats
        this.initializationTime = (new Date().getTime()) / 1000;
        this.durationInSeconds = 0;
        this.jFormComponentCount = 0;
 
        // Controls
        this.controlNextLi = this.form.find('ul.jFormerControl li.nextLi');
        this.controlNextButton = this.controlNextLi.find('button.nextButton');
        this.controlPreviousLi = this.form.find('ul.jFormerControl li.previousLi');
        this.controlPreviousButton = this.controlPreviousLi.find('button.previousButton');
 
        // Save states
        this.saveIntervalSetTimeoutId = null;
 
        // Initialize all of the pages
        this.initPages(options.jFormPages);
 
        // Add a splash page if enabled
        if(this.options.splashPage !== false || this.options.saveState !== false) {
            if(this.options.splashPage == false) {
                this.options.splashPage = {};
            }
            this.addSplashPage();
        }
        // Set the current page
        else {
            this.currentJFormPageIdArrayIndex = 0;
            this.maxJFormPageIdArrayIndexReached = 0;
            this.currentJFormPage = this.jFormPages[this.jFormPageIdArray[0]];
            this.currentJFormPage.active = true;
            this.currentJFormPage.startTime = (new Date().getTime() / 1000 );
            // Add the page navigator
            if(this.options.pageNavigator !== false) {
                this.addPageNavigator();
            }
        }
 
        // Setup the page scroller - mainly CSS changes to width and height
        this.setupPageScroller();
        
        // Hide all inactive pages
        this.hideInactivePages();
 
        // Setup the control buttons
        this.setupControl();
 
        // Add a submit button listener
        this.addSubmitListener();
 
        // Add enter key listener
        this.addEnterKeyListener();
 
        // The blur tip listener
        this.addBlurTipListener();
 
        // Analytics
        this.recordAnalytics();
    },
 
    initPages: function(jFormPages) {
        var self = this
        var each = $.each;
        
        each(jFormPages, function(jFormPageKey, jFormPageValue) {
            var jFormPage = new JFormPage(self, jFormPageKey, jFormPageValue.options);
            jFormPage.show();
            each(jFormPageValue.jFormSections, function(jFormSectionKey, jFormSectionValue) {
                var jFormSection = new JFormSection(jFormPage, jFormSectionKey, jFormSectionValue.options);
                each(jFormSectionValue.jFormComponents, function(jFormComponentKey, jFormComponentValue) {
                    self.jFormComponentCount = self.jFormComponentCount + 1;
                    jFormSection.addComponent(new window[jFormComponentValue.type](jFormSection, jFormComponentKey, jFormComponentValue.type, jFormComponentValue.options));
                });
                jFormPage.addSection(jFormSection);
            });
            self.addPage(jFormPage);
        });
    },
 
    select: function(jFormComponentId) {
        var componentFound = false,
        component = null;
        $.each(this.jFormPages, function(jFormPageKey, jFormPage){
            $.each(jFormPage.jFormSections, function(sectionKey, sectionObject){
                $.each(sectionObject.jFormComponents, function(componentKey, componentObject){
                    if (componentObject.id == jFormComponentId){
                        component = componentObject;
                        componentFound = true;
                    }
                    return !componentFound;
                });
                return !componentFound;
            });
            return !componentFound;
        });
        return component;
    },
 
    addSplashPage: function() {
        var self = this;
 
        // Setup the jFormPage for the splash page
        this.options.splashPage.jFormPage = new JFormPage(this, this.form.find('div.jFormerSplashPage').attr('id'));
        this.options.splashPage.jFormPage.addSection(new JFormSection(this.options.splashPage.jFormPage, this.form.find('div.jFormerSplashPage').attr('id') + '-section'));
        this.options.splashPage.jFormPage.page.width(this.form.width());
        this.options.splashPage.jFormPage.active = true;
        this.options.splashPage.jFormPage.startTime = (new Date().getTime() / 1000 );
 
        // Set the splash page as the current page
        this.currentJFormPage = this.options.splashPage.jFormPage;
 
        // Set the height of the page wrapper to the height of the splash page
        this.jFormPageWrapper.height(this.options.splashPage.jFormPage.page.outerHeight());
 
        // If they have a custom button
        if(this.options.splashPage.customButtonId) {
            this.options.splashPage.controlSplashLi = this.form.find('#'+this.options.splashPage.customButtonId);
            this.options.splashPage.controlSplashButton = this.form.find('#'+this.options.splashPage.customButtonId);
        }
        // Use the native control buttons
        else {
            this.options.splashPage.controlSplashLi = this.form.find('li.splashLi');
            this.options.splashPage.controlSplashButton = this.form.find('button.splashButton');
        }
 
        // Hide the other native controls
        this.setupControl();
 
        // Handle save state options on the splash page
        if(this.options.saveState !== false) {
            self.addSaveStateToSplashPage();
        }
        // If there is no save state, just setup the button to start the form
        else {
            this.options.splashPage.controlSplashButton.bind('click', function(event) {
                event.preventDefault();
                self.beginFormFromSplashPage(false);
            });
        }
    },
 
    beginFormFromSplashPage: function(initSaveState, loadForm) {
        var self = this;
 
        // Add the page navigator
        if(this.options.pageNavigator !== false && this.jFormPageNavigator == null) {
            this.addPageNavigator();
        } else if(this.options.pageNavigator !== false) {
            this.jFormPageNavigator.show();
        }
 
        // Find all of the pages
        var pages = this.form.find('.jFormPage');
 
        // Set the width of each page
        pages.css('width', this.form.find('.jFormWrapperContainer').width());
        
        // Mark the splash page as inactive
        self.options.splashPage.jFormPage.active = false;
 
        // Run any custom JavaScript after the splash page is moved away from
        if(self.options.splashPage.onAfterJs != undefined){
            eval(self.options.splashPage.onAfterJs);
        }
 
        if(!loadForm){
            // Set the current page index
            self.currentJFormPageIdArrayIndex = 0;
 
            // Scroll to the new page, hide the old page when it is finished
            self.jFormPages[self.jFormPageIdArray[0]].scrollTo({onAfter: function() {self.options.splashPage.jFormPage.hide();}});
        }
 
        // Initialize the save state is set
        if(initSaveState) {
            self.initSaveState();
        }
    },
 
    addSaveStateToSplashPage: function() {
        var self = this;
        // Initialize the three save state components
        
        var sectionId = self.options.splashPage.jFormPage.id + '-section';
        $.each(self.options.saveState.components, function(jFormComponentId, jFormComponentOptions) {
            self.options.splashPage.jFormPage.jFormSections[sectionId].addComponent(new window[jFormComponentOptions.type](self.options.splashPage.jFormPage.jFormSections[sectionId], jFormComponentId, jFormComponentOptions.type, jFormComponentOptions.options));
        });
 
        // When they change the option from new to resume, alter the label and peform maintenance
        var formState = 'newForm'; // Default value
        var saveStateJFormComponents = this.options.splashPage.jFormPage.jFormSections[sectionId].jFormComponents;
        saveStateJFormComponents.saveStateStatus.component.find('input:option').bind('click', {context: this}, function(event) {
            // Remove any failure notices
            self.form.find('li.jFormerFailureNotice').remove();
 
            formState = $(event.target).val();
            // Change the form to reflect building a new form
            if(formState == 'newForm') {
                saveStateJFormComponents.saveStatePassword.component.find('label').html('Create password: <span class="jFormComponentLabelRequiredStar"> *</span>');
                self.options.splashPage.controlSplashButton.text('Begin');
            }
            // Change the form to reflect resuming a form
            else if(formState == 'resumeForm') {
                saveStateJFormComponents.saveStatePassword.component.find('label').html('Form password: <span class="jFormComponentLabelRequiredStar"> *</span>');
                self.options.splashPage.controlSplashButton.text('Resume');
            }
        });
 
        // Add a special event listener to the splash page start button
        self.options.splashPage.controlSplashButton.bind('click', {context: this}, function(event) {
            event.preventDefault();
 
            // Remove any failure notice
            self.form.find('li.jFormerFailureNotice').remove();
 
            var validateSaveStateIdentifier = saveStateJFormComponents.saveStateIdentifier.validate();
            var validateSaveStatePassword = saveStateJFormComponents.saveStatePassword.validate();
            if(validateSaveStateIdentifier && validateSaveStatePassword) {
                // Set the form button text
                if(formState == 'newForm') {
                    //console.log('newForm');
                    self.options.splashPage.controlSplashButton.text('Creating form...');
                    var formJson = {};
                    formJson.meta = {};
                    formJson.meta.totalTime = 0;
                    formJson.meta.currentPage = self.getActivePage().id;
                    formJson.meta.maxPageIndex = self.maxJFormPageIdArrayIndexReached;
                    formJson.form = {};
                    //console.log('newForm', jFormerUtility.jsonEncode(formJson));
                }
                else {
                    self.options.splashPage.controlSplashButton.text('Loading form...');
                }
 
                $(event.target).attr('disabled', 'disabled');
                $.ajax({
                    url: self.form.attr('action'),
                    type: 'post',
                    data: {
                        'jFormerTask': 'initializeSaveState',
                        'identifier': saveStateJFormComponents.saveStateIdentifier.getValue(),
                        'password': saveStateJFormComponents.saveStatePassword.getValue(),
                        'formState' : formState,
                        'formData' : jFormerUtility.jsonEncode(formJson)
                    },
                    dataType: 'json',
                    success: function(json) {
                        // If the form was successfully initialized
                        if(json.status == 'success'){
                            if(formState == 'newForm'){
                                self.beginFormFromSplashPage(true, false);
                            }
                            else if(formState == 'resumeForm') {
                                self.beginFormFromSplashPage(true, true);
 
                                // Set the duration from the form save state
                                self.durationInSeconds = json.response.meta.totalTime;
                                
                                // Load the data from the save state
                                self.setData(json.response.form);
 
                                //setup the pageNavigator
                                self.maxJFormPageIdArrayIndexReached = json.response.meta.maxPageIndex;
                                if(self.options.pageNavigator != null) {
                                    self.updatePageNavigator();
                                }
 
                                // Scroll to the active page, set in the form save state
                                if(self.jFormPages[json.response.meta.currentPage] == undefined){
                                    json.response.meta.currentPage = self.jFormPages[self.jFormPageIdArray[0]].id;
                                }
 
                                if(self.jFormPages[json.response.meta.currentPage].active === false) {
                                    self.currentJFormPageIdArrayIndex = self.jFormPageIdArray.indexOf(json.response.meta.currentPage);
                                    self.jFormPages[json.response.meta.currentPage].scrollTo({
                                        onAfter: function() {
                                            self.options.splashPage.jFormPage.hide();
                                        }
                                    });
                                }
                                
                            }
                        }
                        // If the form already exists
                        else if(json.status == 'exists') {
                            // Set the form button text
                            if(formState == 'newForm') {
                                self.options.splashPage.controlSplashButton.text('Begin');
                            }
                            else {
                                self.options.splashPage.controlSplashButton.text('Resume');
                            }
 
                            if(json.response.failureNoticeHtml) {
                                self.form.find('.jFormerControl').append($('<li class="jFormerFailureNotice jFormComponentValidationFailed">'+json.response.failureNoticeHtml+'</li>'));
                                
                            }
                            $(event.target).removeAttr('disabled');
                        }
                        // If the request failed
                        else if(json.status == 'failure') {
                            // Set the form button text
                            if(formState == 'newForm') {
                                self.options.splashPage.controlSplashButton.text('Begin');
                            }
                            else {
                                self.options.splashPage.controlSplashButton.text('Resume');
                            }
 
                            // Set the failure notice
                            if(json.response.failureNoticeHtml){
                                self.form.find('.jFormerControl').append($(['<li class="jFormerFailureNotice jFormComponentValidationFailed">',json.response.failureNoticeHtml,'</li>'].join('')));
                                
                            }
                            // Execute any failure javascript
                            if(json.response.failureJs){
                                eval(json.response.failureJs);
                            }
                            $(event.target).removeAttr('disabled');
                            
                        }
                    },
                    error: function(XMLHttpRequest, textStatus, errorThrown){
                        self.showAlert('There was a problem initializing the form.');
                        self.setupControl();
                    }
                });
           }
           // If the save state form does not validate, focus on the first failed component
           else {
               self.options.splashPage.jFormPage.focusOnFirstFailedComponent();
           }
        });
    },
 
    addPageNavigator: function(){
        var self = this;
 
        this.jFormPageNavigator = this.form.find('.jFormPageNavigator');
 
        this.jFormPageNavigator.find('.jFormPageNavigatorLink:first').click(function(event) {
            // Don't scroll to the page if you already on it
            if(self.currentJFormPageIdArrayIndex != 0) {
                self.currentJFormPageIdArrayIndex = 0;
 
                self.scrollToPage(self.jFormPageIdArray[0], {onAfter: function(){
                    if(self.form.find('.jFormPageNavigatorLink:first').is('.jFormPageNavigatorLinkWarning')){
                        self.jFormPages[self.jFormPageIdArray[0]].focusOnFirstFailedComponent();
                    }
                }});
            }
        });
 
        if(this.options.pageNavigator.position == 'right'){
            this.form.find('.jFormWrapperContainer').width(this.form.width() - this.jFormPageNavigator.width() - 30);
        }
    },
 
    updatePageNavigator: function() {
        var self = this, pageCount, pageIndex;
        for(var i = 1; i <= this.maxJFormPageIdArrayIndexReached + 1; i++) {
            pageCount = i;
            var link = $('#navigatePage'+pageCount);
            if(this.currentJFormPageIdArrayIndex != pageCount -1) {
                link.removeClass('jFormPageNavigatorLinkActive');
            }
            else {
                link.addClass('jFormPageNavigatorLinkActive');
            }
            if(link.hasClass('jFormPageNavigatorLinkLocked')){
                link.removeClass('jFormPageNavigatorLinkLocked').addClass('jFormPageNavigatorLinkUnlocked');
                link.click(function(event) {
                    var target = $(event.target);
                    if(!target.is('li')){
                        target = target.closest('li');
                    }
                    pageIndex = target.attr('id').match(/[0-9]+$/)
                    pageIndex = parseInt(pageIndex) - 1;
                    self.getActivePage().validate(true);
                    // Don't scroll to the page if you already on it
                    if(self.currentJFormPageIdArrayIndex != pageIndex) {
                        self.scrollToPage(self.jFormPageIdArray[pageIndex]);
                    }
                    self.currentJFormPageIdArrayIndex = pageIndex;
                    if(link.hasClass('jFormPageNavigatorLinkWarning')){
                        self.jFormPages[self.jFormPageIdArray[pageIndex]].focusOnFirstFailedComponent();
                    }
                });
            }
        }
    },
    
    addPage: function(jFormPage) {
        this.jFormPageIdArray.push(jFormPage.id);
        this.jFormPages[jFormPage.id] = jFormPage;
    },
 
    addEnterKeyListener: function() {
        var self = this;
 
        // Prevent the default submission on key down
        this.form.bind('keydown', {context:this}, function(event) {
            if(event.keyCode === 13 || event.charCode === 13) {
                if($(event.target).is('textarea')){
                    return;
                }
                event.preventDefault();
            }
        });
 
        this.form.bind('keyup', {context:this}, function(event) {
            // Get the current page, check to see if you are on the splash page
            var currentPage = self.getActivePage().page;
 
            // Listen for the enter key keycode
            if(event.keyCode === 13 || event.charCode === 13) {
                var target = $(event.target);
                // Do nothing if you are on a text area
                if(target.is('textarea')){
                    return;
                }
 
                // If you are on a button, press it
                if(target.is('button')){
                    event.preventDefault();
                    target.trigger('click').blur();
                }
                // If you are on a field where pressing enter submits
                else if(target.is('.jFormComponentEnterSubmits')){
                    event.preventDefault();
                    //target.blur();
                    self.controlNextButton.trigger('click');
                }
                // If you are on an input that is a check box or radio button, select it
                else if(target.is('input:checkbox')) {
                    event.preventDefault();
                    target.trigger('click');
                }
                // If the next input is a remember me button, just submit the form
                else if(currentPage.find(':input').eq(currentPage.find(':input').index(event.target) + 1).val() == 'remember') {
                    event.preventDefault();
                    target.blur();
                    self.controlNextButton.trigger('click');
                }
                // If you are the last input and you are a password input, submit the form
                else if(target.is('input:password')) {
                    event.preventDefault();
                    target.blur();
 
                    // Handle if you are on the splash page
                    if(self.options.splashPage !== null && self.currentJFormPage.id == self.options.splashPage.jFormPage.id) {
                        self.options.splashPage.controlSplashButton.trigger('click');
                    }
                    else {
                        self.controlNextButton.trigger('click');
                    }
                }
 
            }
        });
    },
 
    addSubmitListener: function(){
        var self = this;
        this.form.bind('submit', {context: this}, function(event) {
            event.preventDefault();
            self.submitEvent(event);
        });
    },
 
    initSaveState: function() {
        var self = this, interval = this.options.saveState.interval * 1000;
        if(this.options.saveState === null){
            return;
        }
        this.saveIntervalSetTimeoutId = setInterval(function(){
            self.saveState(self.options.saveState.showSavingAlert);
        }, interval);
        this.saveStateInitialized = true;
        return;
    },
 
    saveState: function(showMessage) {
        if(this.saveRunning == true){
            return true;
        }
        this.saveRunning = true;
        var self = this;
        var tempDurationInSeconds = this.durationInSeconds + this.getTimeActive();
        var formJson = {};
        formJson.meta = {};
        formJson.meta.totalTime = tempDurationInSeconds;
        formJson.meta.currentPage = this.getActivePage().id;
        formJson.meta.maxPageIndex = this.maxJFormPageIdArrayIndexReached;
        formJson.form = this.getData();
        $.ajax({
            url: self.form.attr('action'),
            type: 'post',
            data: {
                'jFormerTask': 'saveState',
                'formData': jFormerUtility.jsonEncode(formJson)
            },
            dataType: 'json',
            success: function(json) {
                if(showMessage === true){
                    self.showAlert('Saving...');
                }
                self.saveRunning = false;
            },
            error: function(XMLHttpRequest, textStatus, errorThrown){
                if(textStatus != 'error'){
                    errorThrown = textStatus ? textStatus : 'unknown';
                }
                self.showAlert('There was an error saving your form, we\'ll try again : '+ errorThrown, 'error');
                self.saveRunning = false;
            }
        });
    },
 
    getData: function() {
        var self = this;
        this.formData = {};
        $.each(this.jFormPages, function(jFormKey, jFormPage) {
            self.formData[jFormKey] = jFormPage.getData();
        });
        return this.formData;
    },
 
    setData: function(data) {
        
        var self = this;
        this.formData = data;
        $.each(data, function(key, page) {
            if(self.jFormPages[key] != undefined){
                self.jFormPages[key].setData(page);
            } else {
                return;
            }
        });
        return this.formData;
    },
 
    setupPageScroller: function() {
        var self = this;
 
        // Find all of the pages
        var pages = this.form.find('.jFormPage');
 
        // Set the width of each page
        pages.css('width', this.form.find('.jFormWrapperContainer').width()).show();
 
        // Set the width of the scroller
        self.jFormPageScroller.css('width', $('.jFormPageWrapper').width() * (pages.length + 1));
 
        // Set the height of the wrapper
        self.jFormPageWrapper.height(self.getActivePage().page.outerHeight());
 
        // Scroll to the current page (prevent weird Firefox bug where the page wasn't displaying
        // I think this is all fixed now
        //self.jFormPageScroller.scrollTo(self.currentJFormPage.page); // Doesn't work in chrome
        //self.currentJFormPage.scrollTo();
    },
 
    setupControl: function() {
        var self = this;
       // console.log(this.currentJFormPageIdArrayIndex);
        // Setup event listener for next button
        this.controlNextButton.unbind().click(function(event) {
            event.preventDefault();
            event['context'] = self;
            self.submitEvent(event);
 
        }).removeAttr('disabled');
 
        // Setup event listener for previous button
        this.controlPreviousButton.unbind().click(function(event) {
            event.preventDefault();
 
            // Be able to return to the splash page
            if(self.options.splashPage !== false && self.currentJFormPageIdArrayIndex === 0) {
                self.currentJFormPageIdArrayIndex = null;
                if(self.jFormPageNavigator){
                    self.jFormPageNavigator.hide();
                }
                self.options.splashPage.jFormPage.scrollTo();
                if(self.options.splashPage.onReturnJs != undefined){
                    eval(self.options.splashPage.onReturnJs);
                }
            }
            // Scroll to the previous page
            else {
                self.currentJFormPageIdArrayIndex = self.currentJFormPageIdArrayIndex - 1;
                self.scrollToPage(self.jFormPageIdArray[self.currentJFormPageIdArrayIndex]);
            }
        });
 
        // First page with more pages after, or splash page
        if(this.currentJFormPageIdArrayIndex === 0 && this.currentJFormPageIdArrayIndex != this.jFormPageIdArray.length - 1) {
            this.controlNextButton.html('Next');
            this.controlNextLi.show();
            this.controlPreviousLi.hide();
            this.controlPreviousButton.attr('disabled', 'disabled');
        }
        // Last page
        else if(self.currentJFormPageIdArrayIndex == this.jFormPageIdArray.length - 1) {
            this.controlNextButton.html(this.options.submitButtonText);
            this.controlNextLi.show();
 
            // First page is the last page
            if(self.currentJFormPageIdArrayIndex === 0) {
                // Hide the previous button
                this.controlPreviousLi.hide();
                this.controlPreviousButton.attr('disabled', '');
            }
            // There is a previous page
            else if(self.currentJFormPageIdArrayIndex > 0) {
                this.controlPreviousButton.removeAttr('disabled');
                this.controlPreviousLi.show();
            }
        }
        // Middle page with a previous and a next
        else { 
            this.controlNextButton.html('Next');
            this.controlNextLi.show();
            this.controlPreviousButton.removeAttr('disabled');
            this.controlPreviousLi.show();
        }
 
        // Splash page
        if(this.options.splashPage !== false) {
            // If you are on the splash page
            if(this.options.splashPage.jFormPage.active) {
                this.options.splashPage.controlSplashLi.show();
                this.controlNextLi.hide();
                this.controlPreviousLi.hide();
                this.controlPreviousButton.attr('disabled', 'disabled');
            }
            // If you aren't on the splash page, don't show the splash button
            else {
                this.options.splashPage.controlSplashLi.hide();
            }
 
            // If you are on the first page
            if(this.currentJFormPageIdArrayIndex === 0  && this.options.saveState == false) {
                this.controlPreviousButton.removeAttr('disabled');
                this.controlPreviousLi.show();
            }
        }
 
        // Failure page
        if(this.form.find('.jFormerControl .startOver').length == 1){
            // Hide the other buttons
            this.controlNextLi.hide();
            this.controlPreviousLi.hide();
 
            // Bind an event listener to the start over button
            this.form.find('.jFormerControl .startOver').click(function(event){
                event.preventDefault();
                self.scrollToPage(self.jFormPageIdArray[0], {
                    onAfter: function(){
                        $(event.target).parent().remove();
                        self.form.find('.jFormPageFailure').remove();
                        self.setupControl();
                    }
                });
            });
        }
    },
    
    scrollToPage: function(jFormPageId, options) {
        this.controlNextButton.attr('disabled', true);
        this.controlPreviousButton.attr('disabled', true);
        var self = this;
        var oldJFormPage = this.getActivePage();
        oldJFormPage.durationActiveInSeconds = oldJFormPage.durationActiveInSeconds + oldJFormPage.getTimeActive();
 
        // Show every page so you can see them as you scroll through
        $.each(this.jFormPages, function(jFormPageKey, jFormPage) {
            jFormPage.show();
            jFormPage.active = false;
        });
 
        // Set the current page to the splash page
        if(self.options.splashPage !== false && jFormPageId == self.options.splashPage.jFormPage.id) {
            self.currentJFormPage = self.options.splashPage.jFormPage;
            self.currentJFormPage.show();
        }
        // Set the current page to the new page
        else {
            this.currentJFormPage = this.jFormPages[jFormPageId];
        }
 
        // Mark the current page as active
        this.currentJFormPage.active = true;
 
        this.jFormPageWrapper.scrollTo(
            self.currentJFormPage.page,
            375,
            {
                axis:'xy',
                onAfter: function(){
                    if($(self.pageWrapper).queue('fx').length <= 1 ) {
                        self.hideInactivePages(self.getActivePage());
                    }
                    if(self.maxJFormPageIdArrayIndexReached < self.currentJFormPageIdArrayIndex) {
                        self.maxJFormPageIdArrayIndexReached = self.currentJFormPageIdArrayIndex;
                    }
                    self.updatePageNavigator();
                    
                    self.adjustHeight();
                    self.currentJFormPage.startTime = (new Date().getTime()/1000);
                    
 
                    // Run any special functions
                    if(options) {
                        if(options.onAfter) {
                            options.onAfter();
                        }
                    }
 
 
                    // Setup the controls
                    self.setupControl();
                    self.controlNextButton.removeAttr('disabled').blur();
                    self.controlPreviousButton.removeAttr('disabled').blur();
                    
                    
                }
            }
        );
 
       this.scrollToTop();
       
 
        // Scroll to the top of the page
 
    },
 
    scrollToTop: function() {
        var self = this;
 
        if($(window).scrollTop() > this.form.position().top) {
            $(document).scrollTo(this.form, 500, {
                offset: {
                    top: -10
                }
            });
        }
 
    },
 
    getActivePage: function() {
        // if active page has not been set
        return this.currentJFormPage;
    },
 
    getTimeActive: function(){
        var currentTotal = 0;
        $.each(this.jFormPages, function(key, page){
           currentTotal = currentTotal + page.durationActiveInSeconds;
        });
        currentTotal = currentTotal + this.getActivePage().getTimeActive();
        return currentTotal;
    },
 
    hideInactivePages: function(){
        $.each(this.jFormPages, function(jFormPageKey, jFormPage){
            jFormPage.hide();
        });
    },
 
    clearValidation: function() {
        $.each(this.jFormPages, function(jFormPageKey, jFormPage){
            jFormPage.clearValidation();
        });
    },
 
    submitEvent: function(event) {
        var self = this;
 
        // Stop the event no matter what
        event.stopPropagation();
        event.preventDefault();
 
        // Remove any failure notices
        self.form.find('.jFormerControl .jFormerFailureNotice').remove();
        self.form.find('.jFormerFailure').remove();
 
        // Run a custom function at beginning of the form submission
        var onSubmitStartResult;
        if(typeof(self.options.onSubmitStart) != 'function') {
            onSubmitStartResult = eval(self.options.onSubmitStart);
        }
        else {
            onSubmitStartResult = self.options.onSubmitStart();
        }
 
        // Validate the current page if you are not the last page
        var clientSideValidationPassed = false;
        if(this.options.clientSideValidation) {
            if(self.currentJFormPageIdArrayIndex < self.jFormPageIdArray.length - 1) {
                //console.log('Validating single page.');
                clientSideValidationPassed = self.getActivePage().validate();
            }
            else {
                //console.log('Validating whole form.');
                clientSideValidationPassed = self.validateAll();
            }
        }
        // Ignore client side validation
        else {
            this.clearValidation();
            clientSideValidationPassed = true;
        }
 
        // Run any custom functions at the end of the validation
        var onSubmitFinishResult = self.options.onSubmitFinish();
 
        // If the custom finish function returns false, do not submit the form
        if(onSubmitFinishResult) {
            // Last page, submit the form
            if(clientSideValidationPassed && self.currentJFormPageIdArrayIndex == self.jFormPageIdArray.length - 1) {
                self.submitForm(event);
            }
            // Not lost page, scroll to the next page
            else if(clientSideValidationPassed && self.currentJFormPageIdArrayIndex < self.jFormPageIdArray.length - 1) {
                self.currentJFormPageIdArrayIndex = self.currentJFormPageIdArrayIndex + 1;
                self.scrollToPage(self.jFormPageIdArray[self.currentJFormPageIdArrayIndex]);
            }
        }
    },
 
    validateAll: function(){
        var self = this;
        var validationPassed = true;
        $.each(this.jFormPages, function(jFormPageKey, jFormPage) {
            if(!jFormPage.validate()) {
                validationPassed = false;
                return false; // Break out of the .each
            }
        });
        return validationPassed;
    },
 
    adjustHeight: function() {
        this.jFormPageWrapper.animate( {
            'height' : this.getActivePage().page.outerHeight()
        }, 250 );
    },
 
    submitForm: function(event) {
        var self = this;
        //console.log(utility.jsonEncode(this.getData()));
 
        // Use a temporary form targeted to the iframe to submit the results
        var formClone = this.form.clone(false).attr('id', $(this.form).attr('id')+'-clone').html('').hide().insertAfter($(this.form)); // Kage bunshin no jutsu!
 
        // Wrap all of the form responses into an object based on the component jFormComponentType
        var formData = $('<input name="jFormer" />').attr('value', encodeURI(jFormerUtility.jsonEncode(this.getData()))).appendTo(formClone); // Set all non-file values in one form object
        // Add any file components for submission
        this.form.find('input:file').each(function(index, fileInput) {
            if($(fileInput).val() != '') {
                // grab the IDs needed to pass
                var sectionId = $(fileInput).closest('.jFormSection').attr('id');
                var pageId = $(fileInput).closest('.jFormPage').attr('id');
                var clone = $(fileInput).clone()
 
                // do find out the section instance index
                if($(fileInput).attr('id').match(/-section[0-9]+/)){
                    var sectionInstance = null;
                    var section = $(fileInput).closest('.jFormSection');
                    // grab the base id of the section to find all sister sections
                    var sectionBaseId = section.attr('id').replace(/-section[0-9]+/, '') ;
                    sectionId = sectionId.replace(/-section[0-9]+/, '');
                    // Find out which instance it is
                    section.closest('.jFormPage').find('div[id*='+sectionBaseId+']').each(function(index, fileSection){
                        if(section.attr('id') == $(fileSection).attr('id')){
                            sectionInstance = index + 1;
                            return false;
                        }
                        return true;
                    });
                     clone.attr('name', clone.attr('name').replace(/-section[0-9]+/, '-section'+sectionInstance));
                }
 
                // do find out the component instance index
                if($(fileInput).attr('id').match(/-instance[0-9]+/)){
                    // grab the base id of the component to find all sister components
                    var baseId = $(fileInput).attr('id').replace(/-instance[0-9]+/, '')
                    var instance = null;
                    // Find out which instance it is
                    $(fileInput).closest('.jFormSection').find('input[id*='+baseId+']').each(function(index, fileComponent){
                        if($(fileComponent).attr('id') == $(fileInput).attr('id')){
                            instance = index + 1;
                            return false;
                        }
                        return true;
                    });
                     clone.attr('name', clone.attr('name').replace(/-instance[0-9]+/, '-instance'+instance));
                }
 
                clone.attr('name', clone.attr('name')+':'+pageId+':'+sectionId);
                clone.appendTo(formClone);
            }
        });
        
        // Submit the form
        formClone.submit();
        formClone.remove(); // Ninja vanish!
 
        // Find the submit button and the submit response
        $(this.form).find('.jFormerControl .nextButton').text('').attr('disabled', 'disabled');
    },
 
    handleFormSubmissionResponse: function(json) {
        var self = this;
        
        // Form failed processing
        if(json.status == 'failure') {
            // Handle validation failures
            if(json.response.validationFailed) {
                $.each(json.response.validationFailed, function(jFormPageKey, jFormPageValues){
                    $.each(jFormPageValues, function(jFormSectionKey, jFormSectionValues){
                        // Handle section instances
                        if($.isArray(jFormSectionValues)) {
                            $.each(jFormSectionValues, function(jFormSectionInstanceIndex, jFormSectionInstanceValues){
                                var sectionKey;
                                if(jFormSectionInstanceIndex != 0) {
                                    sectionKey = '-section'+(jFormSectionInstanceIndex + 1);
                                }
                                else {
                                    sectionKey = '';
                                }
                                $.each(jFormSectionInstanceValues, function(jFormComponentKey, jFormComponentErrors) {
                                    self.jFormPages[jFormPageKey].jFormSections[jFormSectionKey].instanceArray[jFormSectionInstanceIndex].jFormComponents[jFormComponentKey + sectionKey].handleServerValidationResponse(jFormComponentErrors);
                                });
                            });
                        }
                        // There are no section instances
                        else {
                            $.each(jFormSectionValues, function(jFormComponentKey, jFormComponentErrors){
                                self.jFormPages[jFormPageKey].jFormSections[jFormSectionKey].jFormComponents[jFormComponentKey].handleServerValidationResponse(jFormComponentErrors);
                            });
                        }
                    });
                });
            }
 
            // Show the failureHtml if there was a problem
            if(json.response.failureHtml) {
                // Update the failure HTML
                this.form.find('.jFormerControl .jFormerFailure').remove();
                this.form.find('.jFormerControl').after('<div class="jFormerFailure">'+json.response.failureHtml+'</div>');
            }
 
            // Strip the script out of the iframe
            this.form.find('iframe').contents().find('body script').remove();
            if(this.form.find('iframe').contents().find('body').html() !== null) {
                this.form.find('.jFormerFailure').append('<p>Output:</p>'+this.form.find('iframe').contents().find('body').html().trim());
            }
 
            // Reset the page, focus on the first failed component
            this.controlNextButton.text(this.options.submitButtonText);
            this.controlNextButton.removeAttr('disabled');
            this.getActivePage().focusOnFirstFailedComponent();
        }
        // Form passed processing
        else if(json.status == 'success'){
            // Show a success page
            if(json.response.successPageHtml){
                // Stop saving the form
                clearInterval(this.saveIntervalSetTimeoutId);
 
                // Create the success page
                var successPageDiv = $('<div class="jFormPage jFormPageSuccess">'+json.response.successPageHtml+'</div>');
                successPageDiv.width(this.jFormPages[this.jFormPageIdArray[0]].page.width());
                this.jFormPageScroller.append(successPageDiv);
 
                // Hide the page navigator and controls
                this.form.find('.jFormerControl').hide();
                this.form.find('.jFormPageNavigator').hide();
 
                // Scroll to the page
                this.jFormPageWrapper.scrollTo(successPageDiv, 375, {axis:'xy'});
                this.jFormPageWrapper.animate( {
                    'height' : self.form.find('.jFormPageSuccess').outerHeight()
                }, 250 );
                this.scrollToTop();
            }
            // Show a failure page that allows you to go back
            else if(json.response.failurePageHtml){
                // Create the failure page
                var failurePageDiv = $('<div class="jFormPage jFormPageFailure">'+json.response.failurePageHtml+'</div>');
                failurePageDiv.width(this.jFormPages[this.jFormPageIdArray[0]].page.width());
                this.jFormPageScroller.append(failurePageDiv);
 
                // Create a start over button
                this.form.find('.jFormerControl').append($('<li class="startOver"><button class="startOverButton">Start Over</button></li>'));
 
                // Scroll to the failure page and reset the controls
                this.jFormPageWrapper.scrollTo(failurePageDiv, 375, {axis:'xy'});
                this.jFormPageWrapper.animate( {
                    'height' : self.form.find('.jFormPageFailure').outerHeight()
                }, 250 );
                this.scrollToTop();
                this.setupControl();
            }
            // Show a failure notice on the same page
            if(json.response.failureNoticeHtml){
                this.form.find('.jFormerControl .jFormerFailureNotice').remove();
                this.form.find('.jFormerControl').append('<li class="jFormerFailureNotice">'+json.response.failureNoticeHtml+'</li>');
                this.controlNextButton.text(this.options.submitButtonText);
                this.controlNextButton.removeAttr('disabled');
            }
 
            // Show a large failure response on the same page
            if(json.response.failureHtml){
                this.form.find('.jFormerControl .jFormerFailure').remove();
                this.form.find('.jFormerControl').after('<div class="jFormerFailure">'+json.response.failureHtml+'</div>');
                this.controlNextButton.text(this.options.submitButtonText);
                this.controlNextButton.removeAttr('disabled');
            }
 
            // Evaluate any failure or successful javascript
            if(json.response.successJs){
                eval(json.response.successJs);
            }
            else if(json.response.failureJs){
                eval(json.response.failureJs);
            }
 
            // Redirect the user
            if(json.response.redirect){
                this.form.find('.jFormerControl .nextButton').html('Redirecting...');
                document.location = json.response.redirect;
            }
        }
    },
 
    showAlert: function(message, jFormComponentType, modal){
        if(!this.options.alertsEnabled){
            return;
        }
 
        var alertWrapper = this.form.find('.jFormerAlertWrapper');
        var alertDiv = this.form.find('.jFormerAlert');
        
        alertDiv.addClass(jFormComponentType);
        alertDiv.text(message);
 
        // Show the message
        alertWrapper.slideDown('slow', function(){
            setTimeout(function(){
                alertWrapper.slideUp('slow', function() {
                });
            }, 1000);
        });
    },
 
    showModal: function(header, content, className) {
        // Get the modal wrapper div element
        var modalWrapper = this.form.find('.jFormerModalWrapper');
 
        // If there is no modal wrapper, add it
        if(modalWrapper.length == 0) {
            var modalTransparency = $('<div class="jFormerModalTransparency"></div>');
            modalWrapper = $('<div style="display: none;" class="jFormerModalWrapper"><div class="jFormerModal"><div class="jFormerModalHeader">'+header+'</div><div class="jFormerModalContent">'+content+'</div><div class="jFormerModalFooter"><button>Okay</button></div></div></div>');
 
            // Add the modal wrapper after the alert
            this.form.find('.jFormerAlertWrapper').after(modalTransparency);
            this.form.find('.jFormerAlertWrapper').after(modalWrapper);
 
            // Add any custom classes
            if(className != '') {
                modalWrapper.addClass(className);
            }
 
            // Add the onclick event for the Okay button
            modalWrapper.find('button').click(function(event) {
                $('.jFormerModalWrapper').remove();
                $('.jFormerModalTransparency').remove();
                $('body').css('overflow','auto');
            });
        }
 
        // Get the modal div element
        var modal = modalWrapper.find('.jFormerModal');
        modal.css({'position':'absolute'});
        var varWindow = $(window);
        $('body').css('overflow','hidden');
        // Add window resize and scroll events
        varWindow.resize(function(event) {
            leftMargin = (varWindow.width() / 2) - (modal.width() / 2);
            topMargin = (varWindow.height() / 2) - (modal.height() / 2) + varWindow.scrollTop();
            modal.css({'top': topMargin, 'left': leftMargin});
            $('.jFormerModalTransparency').width(varWindow.width()).height(varWindow.height());
        });
 
        // If they click away from the modal (on the modal wrapper), remove it
        $('.jFormerModalTransparency').click(function(event) {
            if($(event.target).is('.jFormerModalTransparency')) {
                modalWrapper.remove();
                $('.jFormerModalTransparency').remove();
                $('body').css('overflow','auto');
            }
        });
 
        // Show the wrapper
        //modalWrapper.width(varWindow.width()).height(varWindow.height()*1.1).css('top', varWindow.scrollTop());
        modalWrapper.show();
 
        // Set the position
        var leftMargin = (varWindow.width() / 2) - (modal.width() / 2);
        var topMargin = (varWindow.height() / 2) - (modal.height() / 2) + varWindow.scrollTop();
        $('.jFormerModalTransparency').width(varWindow.width()).height(varWindow.height()*1.1).css('top', varWindow.scrollTop());
        modal.css({'top': topMargin, 'left': leftMargin});
    },
 
    recordAnalytics: function() {
        var jsProtocol = "https:" == document.location.protocol ? "https://ssl." : "http://www.";
        var image = $('<img src="'+jsProtocol+'.jformer.com/analytics/analytics.gif?pageCount='+this.jFormPageIdArray.length+'&componentCount='+this.jFormComponentCount+'&formId='+this.id+'" style="display: none;" />');
        this.form.append(image);
        image.remove();
    },
 
    updateProgressBar: function() {
        var totalRequired = 0;
        var totalRequiredCompleted = 0;
        $.each(this.jFormPages, function(pageKey, pageObject){
            $.each(pageObject.jFormSections, function(sectionKey, sectionObject){
                $.each(sectionObject.jFormComponents, function(componentKey, componentObject){
                    if(componentObject.isRequired === true && (componentObject.disabledByDependency === false && sectionObject.disabledByDependency === false)) {
                        if(componentObject.type != 'JFormComponentLikert'){
                            totalRequired = totalRequired + 1;
                            if(componentObject.requiredCompleted === true){
                                totalRequiredCompleted = totalRequiredCompleted + 1;
                            }
                        }
                    }
                });
            });
        });
 
        var percentCompleted = parseInt((totalRequiredCompleted / totalRequired) * 100);
 
        this.form.find('.jFormerProgressBar').animate({
            'width': percentCompleted+'%'
        }, 500)
        .html('<p>'+percentCompleted + '%</p>');
    },
 
    addBlurTipListener: function(){
        var self = this;
        $(document).bind('blurTip', function(event, tipElement, action){    
            if(action == 'hide'){
                self.blurredTips = $.map(self.blurredTips, function(tip, index){
                    if($(tip).attr('id') == tipElement.attr('id')){
                        return null
                    } else {
                        return tip;
                    }
                });
                if(self.blurredTips[self.blurredTips.length-1] != undefined){
                    self.blurredTips[self.blurredTips.length-1].removeClass('jFormerTipBlurred');
                }
            } else if(action == 'show'){
                if(self.blurredTips.length > 0){
                    $.each(self.blurredTips, function(index, tip){
                        $(tip).addClass('jFormerTipBlurred')
                    })
                }
                self.blurredTips.push(tipElement)
                tipElement.removeClass('jFormerTipBlurred');
            }
        });
        //console.log('blurring tips', tipElement, action);
        
        //console.log(this.blurredTips);
    }
});JFormerUtility = function() {
    }
 
$.extend(JFormerUtility.prototype, {
    isSet: function() {
        var a = arguments;
        var l = a.length;
        var i = 0;
        if(l == 0) {
            throw new Error('Empty isSet.');
        }
        while(i != l) {
            if(typeof(a[i]) == 'undefined' || a[i] === null) {
                return false;
            }
            else {
                i++;
            }
        }
        return true;
    },
 
    empty: function(mixedVariable) {
        var key;
        if(mixedVariable === ""
            || mixedVariable === 0
            || mixedVariable === "0"
            || mixedVariable === null
            || mixedVariable === false
            || mixedVariable === undefined
            ) {
            return true;
        }
        if(typeof mixedVariable == 'object') {
            for(key in mixedVariable) {
                if(typeof mixedVariable[key] !== 'function') {
                    return false;
                }
            }
            return true;
        }
        return false;
    },
 
    getExtraWidth: function(element) {
        var element = $(element);
        var totalWidth = 0;
        totalWidth += parseInt(element.css("padding-left"), 10) + parseInt(element.css("padding-right"), 10); //Total Padding Width
        totalWidth += parseInt(element.css("margin-left"), 10) + parseInt(element.css("margin-right"), 10); //Total Margin Width
        totalWidth += parseInt(element.css("borderLeftWidth"), 10) + parseInt(element.css("borderRightWidth"), 10); //Total Border Width
        return totalWidth;
    },
 
    jsonEncode: function(mixed_val) {
        // http://kevin.vanzonneveld.net
        // +      original by: Public Domain (http://www.json.org/json2.js)
        // + reimplemented by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
        // + improved by: T.J. Leahy
        // *     example 1: json_encode(['e', {pluribus: 'unum'}]);
        // *     returns 1: '[\n    "e",\n    {\n    "pluribus": "unum"\n}\n]'
 
        /*
        http://www.JSON.org/json2.js
        2008-11-19
        Public Domain.
        NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
        See http://www.JSON.org/js.html
    */
        var json = window.JSON;
        if (typeof json === 'object' && typeof json.stringify === 'function') {
            return json.stringify(mixed_val);
        }
 
        var value = mixed_val;
 
        var quote = function (string) {
            var escapable = /[\\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
            var meta = {    // table of character substitutions
                '\b': '\\b',
                '\t': '\\t',
                '\n': '\\n',
                '\f': '\\f',
                '\r': '\\r',
                '"' : '\\"',
                '\\': '\\\\'
            };
 
            escapable.lastIndex = 0;
            return escapable.test(string) ?
            '"' + string.replace(escapable, function (a) {
                var c = meta[a];
                return typeof c === 'string' ? c :
                '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
            }) + '"' :
            '"' + string + '"';
        };
 
        var str = function (key, holder) {
            var gap = '';
            var indent = '    ';
            var i = 0;          // The loop counter.
            var k = '';          // The member key.
            var v = '';          // The member value.
            var length = 0;
            var mind = gap;
            var partial = [];
            var value = holder[key];
 
            // If the value has a toJSON method, call it to obtain a replacement value.
            if (value && typeof value === 'object' &&
                typeof value.toJSON === 'function') {
                value = value.toJSON(key);
            }
 
            // What happens next depends on the value's type.
            switch (typeof value) {
                case 'string':
                    return quote(value);
 
                case 'number':
                    // JSON numbers must be finite. Encode non-finite numbers as null.
                    return isFinite(value) ? String(value) : 'null';
 
                case 'boolean':
                case 'null':
                    // If the value is a boolean or null, convert it to a string. Note:
                    // typeof null does not produce 'null'. The case is included here in
                    // the remote chance that this gets fixed someday.
 
                    return String(value);
 
                case 'object':
                    // If the type is 'object', we might be dealing with an object or an array or
                    // null.
                    // Due to a specification blunder in ECMAScript, typeof null is 'object',
                    // so watch out for that case.
                    if (!value) {
                        return 'null';
                    }
 
                    // Make an array to hold the partial results of stringifying this object value.
                    gap += indent;
                    partial = [];
 
                    // Is the value an array?
                    if (Object.prototype.toString.apply(value) === '[object Array]') {
                        // The value is an array. Stringify every element. Use null as a placeholder
                        // for non-JSON values.
 
                        length = value.length;
                        for (i = 0; i < length; i += 1) {
                            partial[i] = str(i, value) || 'null';
                        }
 
                        // Join all of the elements together, separated with commas, and wrap them in
                        // brackets.
                        v = partial.length === 0 ? '[]' :
                        gap ? '[\n' + gap +
                        partial.join(',\n' + gap) + '\n' +
                        mind + ']' :
                        '[' + partial.join(',') + ']';
                        gap = mind;
                        return v;
                    }
 
                    // Iterate through all of the keys in the object.
                    for (k in value) {
                        if (Object.hasOwnProperty.call(value, k)) {
                            v = str(k, value);
                            if (v) {
                                partial.push(quote(k) + (gap ? ': ' : ':') + v);
                            }
                        }
                    }
 
                    // Join all of the member texts together, separated with commas,
                    // and wrap them in braces.
                    v = partial.length === 0 ? '{}' :
                    gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
                    mind + '}' : '{' + partial.join(',') + '}';
                    gap = mind;
                    return v;
            }
        };
 
        // Make a fake root object containing our value under the key of ''.
        // Return the result of stringifying the value.
        return str('', {
            '': value
        });
    }
 
});
 
jFormerUtility = new JFormerUtility();
 
// Simple class creation and inheritance
// Inspired by base2 and Prototype
(function(){
    var initializing = false, fnTest = /xyz/.test(function(){
        xyz;
    }) ? /\b_super\b/ : /.*/;
 
    // The base Class implementation (does nothing)
    this.Class = function(){};
 
    // Create a new Class that inherits from this class
    Class.extend = function(prop) {
        var _super = this.prototype;
 
        // Instantiate a base class (but only create the instance,
        // don't run the init constructor)
        initializing = true;
        var prototype = new this();
        initializing = false;
 
        // Copy the properties over onto the new prototype
        for (var name in prop) {
            // Check if we're overwriting an existing function
            prototype[name] = typeof prop[name] == "function" &&
            typeof _super[name] == "function" && fnTest.test(prop[name]) ?
            (function(name, fn){
                return function() {
                    var tmp = this._super;
 
                    // Add a new ._super() method that is the same method
                    // but on the super-class
                    this._super = _super[name];
 
                    // The method only need to be bound temporarily, so we
                    // remove it when we're done executing
                    var ret = fn.apply(this, arguments);
                    this._super = tmp;
 
                    return ret;
                };
            })(name, prop[name]) :
            prop[name];
        }
 
        // The dummy class constructor
        function Class() {
            // All construction is actually done in the init method
            if ( !initializing && this.init )
                this.init.apply(this, arguments);
        }
 
        // Populate our constructed prototype object
        Class.prototype = prototype;
 
        // Enforce the constructor to be what we expect
        Class.constructor = Class;
 
        // And make this class extendable
        Class.extend = arguments.callee;
 
        return Class;
    };
})();/**
 * jFormPage handles all functions on the page level, including page validation.
 *
 */
JFormPage = Class.extend({
    init: function(jFormer, pageId, options) {
        this.options = $.extend({
 
            }, options || {});
 
        // Class variables
        this.jFormer = jFormer;
        this.id = pageId;
        this.page = $('#'+pageId);
        this.jFormSections = {};
        this.formData = {};
        this.active = false;
        this.validationPassed = false;
        this.durationActiveInSeconds = 0;
    },
 
    addSection: function(section) {
        this.jFormSections[section.id] = section;
        return this;
    },
 
    getData: function() {
        //console.log('getting data for page');
        var self = this;
        this.formData = {};
        $.each(this.jFormSections, function(jFormSectionKey, jFormSection) {
            self.formData[jFormSectionKey] = jFormSection.getData();
        });
        return this.formData;
    },
 
    setData: function(data) {
        var self = this;
        $.each(data, function(key, values) {
            if(self.jFormSections[key] != undefined){
                self.jFormSections[key].setData(values);
            } else {
                data[key] = undefined;
            }
        });
        this.formData = data;
        return this.formData;
    },
 
    getTimeActive: function(){
        var currentActiveTime =(new Date().getTime() / 1000) -  this.startTime ;
        return currentActiveTime;
    },
 
    validate: function(silent) {
        var self = this;
        var each = $.each;
        
        self.validationPassed = true;
        each(this.jFormSections, function(sectionKey, section) {
           each(section.instanceArray, function(instanceIndex, sectionInstance){
                each(sectionInstance.jFormComponents, function(componentKey, component) {
                
                    if(component.type == 'JFormComponentLikert'){
                        return;
                    }
                    each(component.instanceArray, function(instanceIndex, instance) {
                        instance.validate();
                        if(instance.validationPassed == false) {
                        
                            self.validationPassed = false;
                        }
                    });
                });
            });
        }); 
        if (self.validationPassed){
            $('#navigatePage'+(self.jFormer.currentJFormPageIdArrayIndex + 1)).removeClass('jFormPageNavigatorLinkWarning');
        } else if (!silent) {
           this.focusOnFirstFailedComponent();
        } 
        return self.validationPassed;
    },
 
    clearValidation: function() {
        $.each(this.jFormSections, function(sectionKey, section) {
            section.clearValidation();
        });
    },
 
    focusOnFirstFailedComponent: function() {
        var each = $.each,
        validationPassed = true;
        each(this.jFormSections, function(sectionLabel, section){
            each(section.instanceArray, function(sectionInstanceIndex, sectionInstance){
                each(sectionInstance.jFormComponents, function(componentLabel, component){
                    each(component.instanceArray, function(instanceLabel, instance){
                        if(!instance.validationPassed || instance.errorMessageArray.length > 0){
 
                            var offset = instance.component.offset().top - 30;
                            var top = $(window).scrollTop()
                            if(top < offset && top + $(window).height() > instance.component.position().top) {
                                instance.component.find(':input:first').focus();
                                //instance.highlight();
                            } else {
                                $.scrollTo(offset + 'px', 500, {
                                    onAfter: function() {
                                        instance.component.find(':input:first').focus();
                                        //instance.highlight();
                                    }
                                });
                            }
                            validationPassed = false;
                        }
                        return validationPassed;
                    });
                    return validationPassed;
                });
                return validationPassed;
            });
            return validationPassed;
        });
    },
 
    scrollTo: function(options) {
        this.jFormer.scrollToPage(this.id, options);
        return this;
    },
 
    show: function(){
        if(this.page.hasClass('jFormPageInactive')){
            this.page.removeClass('jFormPageInactive');
        }
    },
 
    hide:function() {
        if(!this.active){
            this.page.addClass('jFormPageInactive');
        }
    }
 
});/**
 * jFormSection handles all functions on the section level, including dependencies and instances. A section groups components.
 *
 */
JFormSection = Class.extend({
    init: function(parentJFormPage, sectionId, options) {
        this.options = $.extend({
            dependencyDisplay: 'hidden',        // 'hidden' or 'locked' - Used to determine how the section is displayed when dependencies are not met
            dependencyConditionObject: null,       // {'jFormComponentId', 'value' or 'jFormComponentValidationPassed'} - Used to setup dependencies to the values or validation status of one or many components
            //      [[{'component1': 'Yes' } , {'component4': 'validates'}], {'component4': 'validates'}]
            //      Grouped dependencies in an array must all be met, or it will move to the next dependency set
            instancesAllowed: 1,                 // 0 or greater - 0 indicates unlimited, otherwise the number of allowed instances will be set to this number
            instanceAddText: 'Add Another',
            instanceRemoveText: 'Remove'
        }, options || {});
 
 
        // Class variables
        this.parentJFormPage = parentJFormPage;
        this.id = sectionId;
        this.section = $('#'+sectionId);
        this.jFormComponents = {};
        this.formData = null;                       // Will be an object is there is just one instance, will be an array if there is more than one instance
        this.disabledByDependency = false
        this.addDependencyListeners();
 
        if(this.options.isInstance){
            this.instanceArray = null;
            this.clone = null;                  // clone of the original html.. only initiates if instances are turned on...
 
        } else { // do parentInstance functions
            this.clone = this.section.clone();
            this.instanceArray = [this];
            this.createInstanceButton();
            this.iterations = 1;
        }
 
    },
 
    addDependencyListeners: function() {
        var self = this;
        if(this.options.dependencyConditionObject) {
            this.toggleDependencyLock(true);
            var inputKey;
            $.each(this.options.dependencyConditionObject, function(key, condition) {
                if(key == 'and') {
                    $.each(condition, function(andKey, andCondition){
                        if(andCondition == ':validate' || andCondition == ':required'){
                            inputKey = self.parentJFormPage.jFormer.form.find(':input[id^='+andKey+']');
                        } else {
                            inputKey = $('#'+andKey)
                        }
                        addDependencyListener($('#'+andKey));
                    });
                }
                else {
                    if(condition == ':validate' || condition == ':required'){
                        inputKey = self.parentJFormPage.jFormer.form.find(':input[id^='+key+']');
                    } else {
                        inputKey = $('#'+key);
                    }
                    addDependencyListener(inputKey);
                }
            });
        }
 
        function addDependencyListener(input) {
            input.bind('jFormComponent:changed', function() {
                self.checkDependencies();
            });
            // Force check dependencies on keyup for
            if(input.is('input:text, textarea')) {
                input.bind('keyup', function(event) {
                    self.checkDependencies();
                });
            }
        }
    },
 
    toggleDependencyLock: function(hide) {
        // Hide or lock the dependency
        var page = this.parentJFormPage.jFormer.getActivePage();
        if(hide) {
            this.disabledByDependency = true;
            if(this.options.dependencyDisplay == 'hidden'){
                this.section.hide();
                if(page !== null){
                    this.parentJFormPage.jFormer.adjustHeight();
                }
            }
            else {
                this.section.addClass('jFormSectionDependencyDisabled').find(':input').each(function(index, input){
                    input.disabled = true;
                });
            }
        }
        else {
            this.disabledByDependency = false;
            if(this.options.dependencyDisplay == 'hidden'){
                this.section.show();
                this.parentJFormPage.jFormer.adjustHeight();
            }
            else {
                this.section.removeClass('jFormSectionDependencyDisabled').find(':input').each(function(index, input){
                    input.disabled = false;
                });
            }
        }
        return this;
    },
 
    checkDependencies: function() {
        // Hide or show the section based on whether or not the dependency requirements are met
        var self = this,
        hide = true;
        $.each(this.options.dependencyConditionObject, function(key, condition) {
            if(key == 'and') {
                $.each(condition, function(andKey, andCondition) {
                    if((andCondition == ':required' && !jFormerUtility.empty(self.parentJFormPage.jFormer.select(andKey).getValue())) ||
                        (andCondition == ':validate' && self.parentJFormPage.jFormer.select(andKey).validate(true)) ||
                        (andCondition == ':checked' && $('#'+andKey).is(':checked') == true) ||
                        $('#'+andKey).val() == andCondition ) {
                        hide = false;
                    }
                    else {
                        hide = true;
                        return false;
                    }
                });
                if(hide === false){
                    return false;
                }
            }
            else if((condition == ':required' && !jFormerUtility.empty(self.parentJFormPage.jFormer.select(key).getValue())) ||
                (condition == ':validate' && self.parentJFormPage.jFormer.select(key).validate(true)) ||
                (condition == ':checked' && $('#'+key).is(':checked')) ||
                $('#'+key).val() == condition){
                
                hide = false;
                return false;
            }
            else {
                hide = true;
            }
        });
        this.toggleDependencyLock(hide);
 
        return this;
    },
 
    createInstanceButton:function() {
        var self =  this;
 
      if  (this.options.instancesAllowed != 1){
          var buttonId = this.id+'-addInstance',
          addButton = '<button id="'+buttonId+'" class="jFormSectionAddInstanceButton">' + this.options.instanceAddText + '</button>';
          this.section.after(addButton);
          this.parentJFormPage.page.find('#'+buttonId).bind('click', function(event){
              event.preventDefault();
              self.addSectionInstance();
          });
      }
    },
 
    addSectionInstance: function() {
 
        var parent = this;
        if (this.instanceArray.length < this.options.instancesAllowed || this.options.instancesAllowed === 0) {
            this.iterations++;
            var instanceClone = this.clone.clone(),
            buttonId = this.id+'-removeInstance',
            removeButton = '<button id="'+buttonId+'" class="jFormSectionRemoveInstanceButton">' + this.options.instanceRemoveText + '</button>';
            $(instanceClone).append(removeButton);
                instanceClone.find('#'+buttonId).bind('click', function(event){
                var target = $(event.target);
                event.preventDefault();
                parent.instanceArray = $.map(parent.instanceArray, function(cloneId, index){
                   if (cloneId.section.attr('id') ==  target.parent().attr('id')){
                        cloneId = null;
                   }
                   return cloneId;
                });
                target.parent().remove();
                target.remove();
                if (parent.instanceArray.length <= parent.options.instancesAllowed || parent.options.instancesAllowed === 0) {
                    parent.parentJFormPage.page.find('#'+parent.id+'-addInstance').show();
                }
                parent.relabelSectionInstances(parent.instanceArray);
            });
 
            this.parentJFormPage.page.find('#'+this.id+'-addInstance').before(instanceClone);
            this.nameSectionInstance(instanceClone);
            var instanceObject = this.createSectionInstanceObject(instanceClone, this.options);
            this.instanceArray.push(instanceObject);
            this.relabelSectionInstances(this.instanceArray);
            if (this.instanceArray.length >= this.options.instancesAllowed && this.options.instancesAllowed !== 0) {
                this.parentJFormPage.page.find('#'+this.id+'-addInstance').hide();
            }
 
        }
        return this;
    },
 
    removeInstance: function() {
        return this;
    },
 
    nameSectionInstance: function(component) {
        var self = this,
        ending = '';
        $(component).attr('id', $(component).attr('id')+ '-section'+this.iterations);
        $(component).find('*').each(function(key, child){
            if($(child).attr('id')){
                changeName(child, 'id');
            }
            if($(child).attr('for')){
                changeName(child, 'for');
            }
            if($(child).attr('name')){
                changeName(child, 'name');
            }
        });
        function changeName(child, attribute){
            ending = getEnding($(child).attr(attribute)) ;
                if(ending == ''){
                    $(child).attr(attribute, $(child).attr(attribute) +'-section'+self.iterations+ending);
                }else {
                    $(child).attr(attribute, $(child).attr(attribute).replace(ending, '-section'+self.iterations+ending));
                }
        }
 
        function getEnding(identifier){
            var ending = '';
            if(identifier.match(/(\-[A-Za-z0-9]+)&?/)){
                ending = identifier.match(/(\-[A-Za-z0-9]+)&?/)[1];
            } else {
 
            }
            return ending;
        }
 
        return component;
    },
 
    createSectionInstanceObject:function(instanceClone, options){
        var tempOptions = $.extend(true, {}, options);
        tempOptions.isInstance = true;
        var self = this,
        instanceObject = new JFormSection(this.parentJFormPage, this.id+'-section'+this.iterations, tempOptions);
        $.each(this.jFormComponents, function(key, component){
            var componentTempOptions = $.extend(true, {}, component.options);
            componentTempOptions.isInstance = false;
           var componentClone = new window[component.type](instanceObject, component.id+'-section'+self.iterations, component.type, componentTempOptions);
           instanceObject.addComponent(componentClone);
        });
        return instanceObject;
    },
 
    relabelSectionInstances:function(instanceArray){
        $.each(instanceArray, function(key, instance){
            if( key!== 0) {
                var count = key+1,
                label = instance.section.find('.jFormSectionTitle').children(':first');
                if(label.length > 0){
                    if (label.text().match(/(\([0-9]+\))$/)){
                        label.text(label.text().replace(/(\([0-9]+\))$/, '('+count+')'));
                    } else {
                        label.text(label.text() + ' ('+count+')');
                    }
                    
                }
            }
       });
       this.parentJFormPage.jFormer.adjustHeight();
    },
 
    addComponent: function(component) {
        this.jFormComponents[component.id] = component;
        return this;
    },
 
    clearValidation: function() {
        $.each(this.jFormComponents, function(componentKey, component) {
            component.clearValidation();
        });
    },
 
    getData: function() {
        var self = this;
        if(this.instanceArray.length > 1) {
            this.formData = [];
            $.each(this.instanceArray, function(instanceIndex, instanceJFormSection) {
                var sectionData = {};
                $.each(instanceJFormSection.jFormComponents, function(jFormComponentKey, jFormComponent) {
                    if(jFormComponent.type != 'JFormComponentLikertStatement'){
                    jFormComponentKey = jFormComponentKey.replace(/-section[0-9]+/, '');
                    sectionData[jFormComponentKey] = jFormComponent.getData();
                    }
                });
                self.formData.push(sectionData);
            });
        }
        else {
            this.formData = {};
            $.each(this.jFormComponents, function(key, component) {
                if(component.type != 'JFormComponentLikertStatement'){
                    self.formData[key] = component.getData();
                }
            });
        }
        return this.formData;
    },
 
    setData: function(data) {
        var self = this;
        if ($.isArray(data)){
            $.each(data, function(index, instance){
               if(index !== 0 && self.instanceArray[index] == undefined){
                   self.addSectionInstance();
               }
               $.each(instance, function(key, componentData){
                   if(index !== 0){
                    key = key + '-section'+(index+1);
                   }
                   if(self.instanceArray[index].jFormComponents[key] != undefined){
                       self.instanceArray[index].jFormComponents[key].setData(componentData)
                   }
               });
               /*$.each(self.instanceArray[index].jFormComponents, function(key, component){
                   
                   component.setData(instance[key]);
               });*/
            });
        } else {
            $.each(data, function(key, componentData) {
                if(self.jFormComponents[key] != undefined){
                    self.jFormComponents[key].setData(componentData);
                }
                
            });
        }
    }
});/**
 *  jFormComponent is the base class for all components in the form. all specific components extend off of this class
 *  Handles instances, dependencies and trigger bases
 *
 */
JFormComponent = Class.extend({
    init: function(parentJFormSection, jFormComponentId, jFormComponentType, options) {
        this.options = $.extend({
            validationOptions: [],                // 'required', 'email', etc... - An array of validation keys used by this.validate() and jFormerValidator
            triggerFunction: null,              // set to a function name, is a function
            dependencyDisplay: 'hidden',        // 'hidden' or 'locked' - Used to determine how the section is displayed when dependencies are not met
            dependencyConditionObject: null,       // {'jFormComponentId', 'value' or 'jFormComponentValidationPassed'} - Used to setup dependencies to the values or validation status of one or many components
            //      [[{'component1': 'Yes' } , {'component4': 'validate'}], {'component4': 'validate'}]
            //      Grouped dependencies in an array must all be met, or it will move to the next dependency set
            instancesAllowed: 1,                // 0 or greater - 0 indicates unlimited, otherwise the number of allowed instances will be set to this number
            instanceAddText: 'Add Another',
            instanceRemoveText: 'Remove',
            tipTargetPosition: 'rightMiddle',   // 'rightMiddle' - Where the tooltip will be placed in relation to the component
            tipCornerPosition: 'leftTop',       // 'leftTop' - The corner of the tip that will point to the tip target position
            isInstance: false
 
        }, options || {});
 
        //console.count(jFormComponentType);
        // Class variables
        this.parentJFormSection = parentJFormSection;
        this.id = jFormComponentId;
        this.component = $('#'+jFormComponentId+'-wrapper');
        this.formData = null;                       // Will be an object is there is just one instance, will be an array if there is more than one instance
        this.type = jFormComponentType;                       // 'SingleLineText', 'TextArea', etc... - The component jFormComponentType
        this.errorMessageArray = [];            // Used to store error messages displayed in tips or appended to the description
        this.tip = null;
        this.tipDiv = this.component.find('#'+this.id+'-tip');
        this.tipTarget = null;                  // The ID of the element where the tip will be targeted
        this.validationPassed = true;
        this.disabledByDependency = false;
        this.isRequired = false;
        this.requiredCompleted = false;
        this.validationFunctions = {
            'required': function(options) {
                var errorMessageArray = ['Required.'];
                return options.value != '' ? 'success' : errorMessageArray;
            }
        }
 
        if(this.options.triggerFunction !== null){
            var triggerFunc = this.options.triggerFunction;
            this.options.triggerFunction = function() {eval(triggerFunc)};
        }
 
        if(this.options.isInstance){
            this.instanceArray = null;
            this.clone = null; // Clone of the original HTML, only initiates if instances are turned on
        }
        else { // do parentInstance functions
            if(this.options.instancesAllowed != 1){
                this.clone = this.component.clone();
                this.iterations = 1;
            }
            else {
                this.clone = null;
            }
            this.instanceArray = [this];
            this.createInstanceButton();
            
        }
 
        // Intitialize the implemented component
        this.initialize();
        this.reformValidations();
 
        // Initiation functions
        this.addHighlightListeners();
        this.defineComponentChangedEventListener();
        this.catchComponentChangedEventListener();
        this.addDependencyListeners();
 
        // Add a tip if there is content to add
        if($.trim(this.tipDiv.html()) !== '') {
            this.addTip();
        }
 
        // Tip listeners
        this.addTipListeners();
    },
 
    addHighlightListeners: function() {
        var self = this;
 
        // Focus
        this.component.find(':input:not(button):not(hidden)').each(function(key, input) {
            $(input).bind('focus', function() {
                self.highlight();
            } );
            $(input).bind('blur', function(event) {
                self.removeHighlight();
 
                // Handle multifield highlight and validation
                if((self.type == 'JFormComponentName' || self.type == 'JFormComponentAddress' ) && self.changed === true){
                    self.validate();
                }
            });
        });
 
        // Multiple choice
        if(this.component.find('input:checkbox, input:radio').length > 0) {
            this.component.mouseenter(function(event) {
                self.highlight();
 
            });
            this.component.mouseleave(function(event) {
                self.removeHighlight();
            });
        }
 
        return this;
    },
 
    reformValidations: function() {
        var reformedValidations = {},
        self = this;
        $.each(this.options.validationOptions, function(validationFunction, validationOptions) {
            // Check to see if this component is required, take not of it in the options - used to track which components are required for progress bar
            if(validationOptions == 'required'){
                self.isRequired = true;
            }
 
            // Check to see if the name of the function is actually an array index
            if(validationFunction >= 0) {
                // The function is not an index, it becomes the name of the option with the value of an empty object
                reformedValidations[validationOptions] = {'component': self.component};
            }
            // If the validationOptions is a string
            else if(typeof(validationOptions) != 'object') {
                reformedValidations[validationFunction] = {'component': self.component};
                reformedValidations[validationFunction][validationFunction] = validationOptions;
            }
            // If validationOptions is an object
            else if(typeof(validationOptions) == 'object') {
                if(validationOptions[0] != undefined){
                    reformedValidations[validationFunction] = {}
                    reformedValidations[validationFunction][validationFunction] = validationOptions;
                } else {
                    reformedValidations[validationFunction] = validationOptions;
                }
                reformedValidations[validationFunction].component = self.component;
            }
        });
 
        this.options.validationOptions = reformedValidations;
    },
 
 
    defineComponentChangedEventListener: function() {
        var self = this;
 
        // Handle IE events
        this.component.find('input:checkbox, input:radio').each(function(key, input) {
            $(input).bind('click', function(event) {
                $(this).trigger('jFormComponent:changed', self);
            });
        });
 
        this.component.find(':input:not(button, :checkbox, :radio)').each(function(key, input) {
            $(input).bind('change', function(event) {
                $(this).trigger('jFormComponent:changed', self);
            });
        });
    },
 
    catchComponentChangedEventListener: function() {
        var self = this;
        this.component.bind('jFormComponent:changed', function(event) {
            // Run a trigger on change if there is one
            if(self.options.triggerFunction !== null) {
                self.options.triggerFunction();
            }
            if(self.type == 'JFormComponentName' || self.type == 'JFormComponentAddress' || self.type == 'JFormComponentLikert'){
                self.changed = true;
            }
            // Validate the component on change
            if(self.parentJFormSection.parentJFormPage.jFormer.options.clientSideValidation) {
                self.validate();
            }
            
            // Update the progress bar
            if(self.parentJFormSection.parentJFormPage.jFormer.options.progressBar !== false) {
                self.parentJFormSection.parentJFormPage.jFormer.updateProgressBar();
            }
        });
    },
 
    highlight: function() {
        // Add the highlight class and trigger the highlight
        this.component.addClass('jFormComponentHighlight').trigger('jFormComponent:highlighted', this.component);
        this.component.trigger('jFormComponent:showTip', this.component);
    },
 
    removeHighlight: function() {
        var self = this;
        this.component.removeClass('jFormComponentHighlight').trigger('jFormComponent:highlightRemoved', this.component);
 
        // Wait just a microsecond to see if you are still on the same component
        setTimeout(function() {
            if(!self.component.hasClass('jFormComponentHighlight')){
                self.component.trigger('jFormComponent:hideTip', self.component);
            }
        }, 1);
    },
 
    getData: function() {
        var self = this;
        if(this.instanceArray.length > 1) {
            this.formData = [];
            $.each(this.instanceArray, function(index, component) {
                var componentValue = component.getValue();
                    self.formData.push(componentValue);
            });
        }
        else {
            this.formData = this.getValue();
        }
        return this.formData;
    },
 
    setData: function(data) {
        var self = this;
        if($.isArray(data)) {
            $.each(data, function(index, value) {
                if((self.type == 'JFormComponentMultipleChoice' && ($.isArray(value) ||  self.multipeChoiceType == 'radio')) || self.type != 'JFormComponentMultipleChoice'){
                    if(index !== 0 && self.instanceArray[index] == undefined){
                        self.addInstance();
                    }
                    self.instanceArray[index].setValue(value);
                }
                else {
                    self.setValue(data);
                    return false;
                }
            });
        }
        else {
            this.setValue(data);
        }
    },
 
    addDependencyListeners: function() {
        var self = this;
        var inputKey;
        if(this.options.dependencyConditionObject) {
            this.toggleDependencyLock(true);
            $.each(this.options.dependencyConditionObject, function(key, condition) {
                if(key == 'and') {
                    $.each(condition, function(andKey, andCondition){
                        if(andCondition == ':validate' || andCondition == ':required'){
                            inputKey = self.parentJFormSection.parentJFormPage.jFormer.form.find(':input[id^='+andKey+']');
                        } else {
                            inputKey = $('#'+andKey)
                        }
                        addDependencyListener(inputKey);
                    });
                }
                else {
                    if(condition == ':validate' || condition == ':required'){
                        inputKey = self.parentJFormSection.parentJFormPage.jFormer.form.find(':input[id^='+key+']');
                    } else {
                        inputKey = $('#'+key);
                    }
                    addDependencyListener(inputKey);
                }
            });
        }
 
        function addDependencyListener(inputarray) {
            inputarray.each( function(key, input){
                $(input).bind('jFormComponent:changed', function() {
                    self.checkDependencies();
                });
 
                // Force check dependencies on keyup for
                if($(input).is('input:text, textarea')) {
                    $(input).bind('keyup', function(event) {
                        self.checkDependencies();
                    });
                }
            });
            
        }
    },
 
    toggleDependencyLock: function(hide) {
        // Hide or lock the dependency
        var page = this.parentJFormSection.parentJFormPage.jFormer.getActivePage();
        if(hide) {
            this.disabledByDependency = true;
            if(this.options.dependencyDisplay == 'hidden'){
                if(this.component.is(':visible')){
                    this.component.hide();
                    if(page !== null){
                        this.parentJFormSection.parentJFormPage.jFormer.adjustHeight();
                    }
                }
            }
            else {
                if(!this.component.hasClass('jFormComponentDependencyDisabled')){
                    this.component.addClass('jFormComponentDependencyDisabled').find('input, select, textarea').each(function(index, input){
                        input.disabled = true;
                    });
                }
            }
        }
        else {
            this.disabledByDependency = false;
            if(this.options.dependencyDisplay == 'hidden'){
                if(this.component.is(':hidden')){
                    this.component.show();
                    this.parentJFormSection.parentJFormPage.jFormer.adjustHeight();
                }
            }
            else {
                if(this.component.hasClass('jFormComponentDependencyDisabled')){
                    this.component.removeClass('jFormComponentDependencyDisabled').find('input, select, textarea').each(function(index, input){
                        input.disabled = false;
                    });
                }
            }
        }
        return this;
    },
 
    checkDependencies: function() {
        // Hide or show the section based on whether or not the dependency requirements are met
        var self = this
        var hide = true;
        $.each(this.options.dependencyConditionObject, function(key, condition) {
            if(key == 'and') {
                $.each(condition, function(andKey, andCondition) {
                    if((andCondition == ':required' && !jFormerUtility.empty(self.parentJFormSection.parentJFormPage.jFormer.select(andKey).getValue())) ||
                        (andCondition == ':validate' && self.parentJFormSection.parentJFormPage.jFormer.select(andKey).validate(true)) ||
                        (andCondition == ':checked' && $('#'+andKey).is(':checked') == true) ||
                        $('#'+andKey).val() == andCondition ) {
                            hide = false;
                    }
                    else {
                        hide = true;
                        return false;
                    }
                });
                if(hide === false){
                    return false;
                }
            }
            else if((condition == ':required' && !jFormerUtility.empty(self.parentJFormSection.parentJFormPage.jFormer.select(key).getValue())) ||
                (condition == ':validate' && self.parentJFormSection.parentJFormPage.jFormer.select(key).validate(true)) ||
                (condition == ':checked' && $('#'+key).is(':checked')) ||
                $('#'+key).val() == condition){
                hide = false;
                return false;
            }
            else {
                hide = true;
            }
        });
        this.toggleDependencyLock(hide);
 
        return this;
    },
 
    createInstanceButton:function() {
        var self =  this;
        if(this.options.instancesAllowed != 1){
          this.component.after('<button id="'+this.id+'-addInstance" class="jFormComponentAddInstanceButton">'+this.options.instanceAddText+'</button>');
          this.parentJFormSection.section.find('#'+this.id+'-addInstance').bind('click', function(event){
              event.preventDefault();
              self.addInstance();
          });
      }
    },
 
    addInstance: function() {
        var parent = this;
        if(this.instanceArray.length < this.options.instancesAllowed || this.options.instancesAllowed === 0) {
            var instanceClone = this.clone.clone();
            var addButton = this.parentJFormSection.section.find('#'+this.id+'-addInstance');
 
            // Create the remove button
            $(instanceClone).append('<button id="'+this.id+'-removeInstance" class="jFormComponentRemoveInstanceButton">'+this.options.instanceRemoveText+'</button>');
 
            // Add an event listener on the remove button
            instanceClone.find('#'+this.id+'-removeInstance').bind('click', function(event){
                var target = $(event.target);
                event.preventDefault();
                
                parent.instanceArray = $.map(parent.instanceArray, function(cloneId, index){
                   if(cloneId.component.attr('id') ==  target.parent().attr('id')){
                       if(cloneId.tip != null){
                            cloneId.tip.hide();
                       }
                       cloneId = null;
                   }
                   return cloneId;
                });
                target.parent().remove();
                target.remove();
 
                if(parent.instanceArray.length <= parent.options.instancesAllowed || parent.options.instancesAllowed === 0) {
                    addButton.show();
                }
                parent.relabelInstances(parent.instanceArray);
            });
 
            // Insert the clone right before the add button
            addButton.before(instanceClone);
 
            this.nameInstance(instanceClone);
            
            var instanceObject = this.createInstanceObject(instanceClone, this.options);
            this.instanceArray.push(instanceObject);
            this.relabelInstances(this.instanceArray);
            if(this.instanceArray.length == this.options.instancesAllowed && this.options.instancesAllowed !== 0) {
                addButton.hide();
            }
 
            // Resize the page
            //parent.parentJFormSection.parentJFormPage.scrollTo();
        }
        return this;
    },
 
    nameInstance: function(component) {
        component = $(component);
        var self = this,
        ending = '';
        this.iterations++;
        component.attr('id', component.attr('id').replace('-wrapper', '-instance'+this.iterations+'-wrapper'));
        component.find('*').each(function(key, child){
            if($(child).attr('id')){
                changeName(child, 'id');
            }
            if($(child).attr('for')){
                changeName(child, 'for');
            }
            if($(child).attr('name')){
                changeName(child, 'name');
            }
        });
        function changeName(child, attribute){
            ending = getEnding($(child).attr(attribute)) ;
            if(ending == ''){
                $(child).attr(attribute, $(child).attr(attribute) +'-instance'+self.iterations+ending);
            }else {
                $(child).attr(attribute, $(child).attr(attribute).replace(ending, '-instance'+self.iterations+ending));
            }
        }
        function getEnding(identifier){
            var ending = '';
            if(identifier.match(/\-(div|label|tip|removeInstance)\b/)){
                ending = identifier.match(/\-(div|label|tip|removeInstance)\b/)[0];
            } else {
 
            }
            return ending;
        }
        return component;
    },
 
    createInstanceObject:function(instanceClone, options){
        var tempOptions = {};
        $.each(options, function(optionKey, optionValue){
            tempOptions[optionKey] = optionValue;
        })
        tempOptions.isInstance = true;
        tempOptions.triggerFunction = null;
        var instanceObject = new window[this.type](this.parentJFormSection, this.id+'-instance'+this.iterations, this.type, tempOptions);
        return instanceObject;
    },
 
    relabelInstances:function(instanceArray){
        $.each(instanceArray, function(key, instance){
            if( key!== 0) {
                var count = key+1,
                label = instance.component.find('#'+instance.component.attr('id').replace('-wrapper','-label'));
                if(label.length > 0) {
                    var star = label.find('span.jFormComponentLabelRequiredStar');
                    if(star.length > 0){
                        star.remove()
                    }
                    if(label.html().match(/:$/)){
                        label.html(label.html().replace(/(\([0-9]+\))?:/, ' ('+count+'):'));
                    } else {
                        if (label.text().match(/(\([0-9]+\))$/)){
                            label.text(label.text().replace(/(\([0-9]+\))$/, '('+count+')'));
                        } else {
                            label.text(label.text() + ' ('+count+')');
                        }
                    }
                    label.append(star);
                } else {
                    label = instance.component.find('label');
                    var star = label.find('span.jFormComponentLabelRequiredStar');
                    if(star.length > 0){
                        star.remove()
                    }
                    if (label.text().match(/(\([0-9]+\))$/)){
                        label.text(label.text().replace(/(\([0-9]+\))$/, '('+count+')'));
                    } else {
                        label.text(label.text() + ' ('+count+')');
                    }
                    label.append(star);
                }
 
            }
        });
       this.parentJFormSection.parentJFormPage.jFormer.adjustHeight();
    },
 
    addTip: function() {
        var self = this;
 
        // Check to see if the tip already exists
        if(typeof(this.tip) !== 'function') {
            // Create the tip
            var tip = this.tipTarget.simpletip({
                persistent: true,
                focus: true,
                position: 'topRight',
                content: self.tipDiv,
                baseClass: 'jFormerTip',
                hideEffect: 'none',
                onBeforeShow: function(){
                    if(self.tipDiv.find('.tipContent').text() == ''){
                        return false;
                    }
                },
                onShow: function(){
                    var height = $(window).height();
                    var offset = this.getTooltip().offset().top + this.getTooltip().outerHeight() + 12;
                    if($(window).scrollTop() + height < offset) {
                        $.scrollTo(offset - height + 'px', 250, {axis:'y'});
                    }
                }
            });
            this.tip = tip.simpletip();
        }
    },
 
    addTipListeners: function() {
        var self = this;
 
        // Show a tip
        this.component.bind('jFormComponent:showTip', function(event) {
            // Make sure the tip exists and display the tip if it is not empty
            if(self.tip && typeof(self.tip) == 'object' && $.trim(self.tipDiv.html()) !== '') {
                self.tip.show();
            }
            
        });
 
        // Hide a tip
        this.component.bind('jFormComponent:hideTip', function(event) {
            // Make sure the tip exists
            if(self.tip && typeof(self.tip) == 'object') {
                self.tip.hide();
            }
        });
 
        return this;
    },
 
    clearValidation: function() {
        // Reset the error message array and validation passes boolean
        this.errorMessageArray = [];
        this.validationPassed = true;
 
        // Reset the classes
        this.component.removeClass('jFormComponentValidationFailed');
        this.component.addClass('jFormComponentValidationPassed');
 
        // Remove any tipErrorUl from the tip div
        this.component.find('.tipErrorUl').remove();
 
        // Handle tip display
        if(this.tip && typeof(this.tip) == 'object') {
            // Update the tip content
            this.tip.update(this.tipDiv.html());
 
            // Hide the tip if the tip is empty
            if($.trim(this.tipDiv.find('.tipContent').html()) == ''){
                this.tipDiv.hide();
            }
        }
    },
 
    // Abstract functions
    initialize: function() { },
    getValue: function() { },
    setValue: function() { },
 
    validate: function(silent) {
        // If there are no validations, return true
        if(this.options.validationOptions.length < 1) {
            return true;
        }
        if(silent){
            var silentValidationPassed = true;
        }
 
        var self = this;
        this.clearValidation();
        var value = this.getValue();
 
        if(value === null){
            return true;
        }
 
        $.each(this.options.validationOptions, function(validationType, validationOptions){
            validationOptions['value'] = value;
            var validation = self.validationFunctions[validationType](validationOptions);
 
            if(validation == 'success') {
                if(validationType.match('required')){
                    self.requiredCompleted = true;
                }
                return true;
            }
            else {
                if(validationType.match('required')){
                    self.requiredCompleted = false;
                    if(self.parentJFormSection.parentJFormPage.jFormer.options.pageNavigator != false){
                        $('#navigatePage'+(self.parentJFormSection.parentJFormPage.jFormer.currentJFormPageIdArrayIndex + 1)).addClass('jFormPageNavigatorLinkWarning');
                    }
                }
                if(silent){
                    silentValidationPassed = false;
                } else {
                    $.merge(self.errorMessageArray, validation);   
                }
            }
        });
        if(silent) {
            return silentValidationPassed;
        }
        else {
            if(this.errorMessageArray.length > 0 ) {
                this.handleErrors();
                this.validationPassed = false;
            }
            return this.validationPassed;
        }
    },
 
    handleServerValidationResponse: function(errorMessageArray) {
        // Clear the validation
        $.each(this.instanceArray, function(instanceKey, instance) {
            instance.clearValidation();
        });
 
        // If there are errors
        if(errorMessageArray != null && errorMessageArray.length > 0) {
            // If there are instances
            if(this.instanceArray.length != 1) {
                // Go through each of the instances and assign the error messages
                $.each(this.instanceArray, function(instanceKey, instance) {
                    if(!jFormerUtility.empty(errorMessageArray[instanceKey])){
                        $.each(errorMessageArray[instanceKey], function(errorMessageArrayIndex, errorMessage){
                            if(errorMessage != '') {
                                instance.errorMessageArray.push(errorMessage);
                            }
                        });
                        if(instance.errorMessageArray.length > 0) {
                            instance.validationPassed = false;
                            instance.handleErrors();
                        }
                    }
                });
            }
            // If there aren't instances
            else {
                this.errorMessageArray = errorMessageArray;
                this.validationPassed = false;
                this.handleErrors();
            }
        }
    },
 
    handleErrors: function() {
        var self = this;
 
        // Change classes
        this.component.removeClass('jFormComponentValidationPassed');
        this.component.addClass('jFormComponentValidationFailed');
 
        // Add a tip div and tip neccesary
        if(this.tipDiv.length == 0) {
            this.createTipDiv();
        }
 
        // Put the error list into the tip
        var tipErrorUl = $('<ul id="'+this.id+'-tipErrorUl" class="tipErrorUl"></ul>');
        $.each(this.errorMessageArray, function(index, errorMessage){
            tipErrorUl.append("<li>"+errorMessage+"</li>");
        });
        this.tipDiv.find('.tipContent').append(tipErrorUl);
 
        // Update the tip content
        this.tip.update(self.tipDiv.html());
 
        // Show the tip if you are currently on it
        if(this.component.hasClass('jFormComponentHighlight')) {
            this.tip.show();
        }
    },
 
    createTipDiv: function() {
        // Create a tip div and tip neccesary
        this.tipDiv = $('<div id="'+this.id+'-tip" style="display: none;"></div>');
        this.component.append(this.tipDiv);
        this.addTip();
    }
});JFormComponentAddress = JFormComponent.extend({
    init: function(parentJFormSection, jFormComponentId, jFormComponentType, options) {
        this._super(parentJFormSection, jFormComponentId, jFormComponentType, options);
    },
 
    initialize: function(){
        this.tipTarget = this.component;
        if(this.options.emptyValue){
            this.addEmptyValues();
        }
        this.validationFunctions = {
            //Name Validations
            'required': function(options) {
                var errorMessageArray = [];
                if(options.value.addressLine1 == '') {
                    errorMessageArray.push(['Street Address is required.']);
                }
                if(options.value.city == '') {
                    errorMessageArray.push(['City is required.']);
                }
                if(options.value.state == '') {
                    errorMessageArray.push(['State is required.']);
                }
                if(options.value.zip == '') {
                    errorMessageArray.push(['Zip is required.']);
                }
                if(options.value.country == '') {
                    errorMessageArray.push(['Country is required.']);
                }
                return errorMessageArray.length < 1 ? 'success' : errorMessageArray;
            }
        }
        
        this.changed = false;
    },
 
    setValue: function(data) {
        var self = this;
        if(this.options.emptyValue){
            if(data.addressLine1 != this.options.emptyValue.addressLine1){
                self.component.find(':input[id*=addressLine1]').removeClass('defaultValue').val(data.addressLine1).blur();
            }
            if(data.addressLine2 != this.options.emptyValue.addressLine2){
                self.component.find(':input[id*=addressLine2]').removeClass('defaultValue').val(data.addressLine2).blur();
            }
            if(data.city != this.options.emptyValue.city){
                self.component.find(':input[id*=city]').removeClass('defaultValue').val(data.city).blur();
            }
            if(data.state != this.options.emptyValue.state || this.options.emptyValue.state == undefined){
                self.component.find(':input[id*=state]').removeClass('defaultValue').val(data.state).blur();
            }
            if(data.zip != this.options.emptyValue.zip){
                self.component.find(':input[id*=zip]').removeClass('defaultValue').val(data.zip).blur();
            }
        }
        else {
            self.component.find(':input[id*=addressLine1]').val(data.addressLine1);
            self.component.find(':input[id*=addressLine2]').val(data.addressLine2);
            self.component.find(':input[id*=city]').val(data.city);
            self.component.find(':input[id*=state]').val(data.state);
            self.component.find(':input[id*=zip]').val(data.zip);
        }
        self.component.find(':input[id*=country]').val(data.country);
        this.validate(true);
        /*
        $.each(data, function(key, value){
            if(self.options.emptyValue[key] != undefined && data[key] != self.options.emptyValue[key]){
                self.component.find(':input[id*='+key+']').removeClass('defaultValue').val(value).trigger('component:changed').blur();
            } else if (self.options.emptyValue[key] == undefined) {
                self.component.find(':input[id*='+key+']').val(value).trigger('component:changed');
            } else {
                self.component.find(':input[id*='+key+']').val(value).trigger('component:changed');
            }
        });*/
    },
 
    getValue: function() {
        if(this.disabledByDependency || this.parentJFormSection.disabledByDependency){
           return null;
        }
        var address = {},
        self = this;
 
        // Get the values
        address.addressLine1 = self.component.find(':input[id*=addressLine1]').val();
        address.addressLine2 = self.component.find(':input[id*=addressLine2]').val();
        address.city = self.component.find(':input[id*=city]').val();
        address.state = self.component.find(':input[id*=state]').val();
        address.zip = self.component.find(':input[id*=zip]').val();
        address.country = self.component.find(':input[id*=country]').val();
 
        this.component.find(':input').each(function(index, input) {
            address[$(input).attr('id').replace(self.id+'-', '')] = $(input).val();
        });
        if(this.options.emptyValue){
            if(address.addressLine1 == this.options.emptyValue.addressLine1){
                address.addressLine1 = '';
            }
            if(address.addressLine2 == this.options.emptyValue.addressLine2){
                address.addressLine2 = '';
            }
            if(address.city == this.options.emptyValue.city){
                address.city = '';
            }
            if(address.state == this.options.emptyValue.state){
                address.state = '';
            }
            if(address.zip == this.options.emptyValue.zip){
                address.zip = '';
            }
        }
 
        return address;
    },
 
    validate: function(){
        var self = this;
        if(!this.changed){
            this._super();
        }
        
        setTimeout(function() {
            if(!self.component.hasClass('jFormComponentHighlight')){
                if(self.options.validationOptions.length < 1){
                    return true;
                }
                self.clearValidation();
                $.each(self.options.validationOptions, function(validationType, validationOptions){
                    validationOptions['value'] = self.getValue();
                    var validation = self.validationFunctions[validationType](validationOptions);
                    if(validation == 'success'){
                        return;
                    }
                    else {
                        $.merge(self.errorMessageArray, validation);
                        self.validationPassed = false;
                    }
                });
                if(self.errorMessageArray.length > 0 ){
                    self.handleErrors();
                }
                self.changed = false;
                return self.validationPassed;
            }
        }, 1);
 
    },
 
    addEmptyValues: function(){
        var self = this,
        emptyValue = this.options.emptyValue;
        $.each(emptyValue, function(key, value){
            var input = self.component.find('input[id*='+key+']');
            input.addClass('defaultValue');
            input.focus(function(event){
                if ($.trim($(event.target).val()) == value ){
                    $(event.target).val('');
                    $(event.target).removeClass('defaultValue');
                }
            });
            input.blur(function(event){
                if ($.trim($(event.target).val()) == '' ){
                    $(event.target).addClass('defaultValue');
                    $(event.target).val(value);
                }
            });
            input.trigger('blur');
        });
    }
});JFormComponentDate = JFormComponent.extend({
    init: function(parentJFormSection, jFormComponentId, jFormComponentType, options) {
        this._super(parentJFormSection, jFormComponentId, jFormComponentType, options);
    },
 
    initialize: function(){
        this.addCalendar();
        this.tipTarget = this.component.find('.jFormComponentDateSelector');
        if(this.tipTarget == undefined){
            this.tipTarget = this.component;
        }
        if(this.options.validationOptions.length == 0){
           this.options.validationOptions.push('date');
           this.reformValidations();
        }
        this.validationFunctions = {
            //Date validations
            'required': function(options) {
                var errorMessageArray = [];
                if(options.value.month == '' || options.value.day == '' || options.value.year == '' || options.value == null){
                    errorMessageArray.push(['Required.']);
                }
                return errorMessageArray.length < 1 ? 'success' : errorMessageArray;
            },
            'date': function(options) {
                var errorMessageArray = [];
                var month = parseInt(options.value.month);
                var day = parseInt(options.value.day);
                var year = options.value.year;
                var badDay = false;
                if(options.value.month == '' || options.value.day == '' || options.value.year == ''){
                    return true;
                }
                if(!year.match(/[\d]{4}/)){
                    errorMessageArray.push(['you must enter a valid year.']);
                }
                if(month < 0 || month > 12){
                    errorMessageArray.push(['you must enter a valid month.']);
                }
                if (month==4 || month==6 || month==9 || month==11) {
                    if(day > 30){
                        badDay = true;
                    }
                }
		else if (month==2) {
                    year = parseInt(year);
                    var days = ((year % 4 == 0) && ( (!(year % 100 == 0)) || (year % 400 == 0))) ? 29 : 28
                    if(day > days){
                        badDay = true;
                    }
                }
                if (day > 31){
                    badDay = true;
                }
                if(badDay){
                    errorMessageArray.push(['you must enter a valid day.']);
                }
                return errorMessageArray.length < 1 ? 'success' : errorMessageArray;
            },
            'teenager': function(options) {
                var errorMessageArray = 'You must be at least 13 years old to use this site.',
                birthday = new Date(options.value.year, options.value.month, options.value.day),
                now = new Date(),
                limit = new Date(now.getFullYear() - 13 , now.getMonth(), now.getDate()),
                timeDifference = (limit - birthday);
                return options.value == '' || timeDifference >= 0  ? 'success' : errorMessageArray;
            }
        }
    },
 
    highlight: function() {
        var self = this
        // Add the highlight class and trigger the highlight
        this.component.addClass('jFormComponentHighlight').trigger('jFormComponent:highlighted', this.component);
        setTimeout(function(){
            self.component.trigger('jFormComponent:showTip', self.component);
        }, 1);
 
    },
 
    addCalendar: function(){
        var input = this.component.find('input:text');
        var datePicker = input.date_input();
        input.bind('keyup', function(event){
            if (event.keyCode == 9 || event.keyCode == 27 || event.keyCode == 13 || event.keyCode == 33 || event.keyCode == 34 || event.keyCode == 38 || event.keyCode == 40 || event.keyCode == 37 || event.keyCode == 39 ){
                return;
            } else if(input.val().length == 10){
                input.trigger('change');
            }
        });
    },
 
    getValue: function() {
        if(this.disabledByDependency || this.parentJFormSection.disabledByDependency){
           return null;
        }
        var date = {'month': '' , 'day': '', 'year':''};
        var value = $('#'+this.id).val();
        if(value != ''){
            value = value.split(value.match(/[^\d]/));
            if (value[0] != undefined){
                date.month = value[0];
            } 
            if(value[1] != undefined) {
                date.day = value[1];
            }
            if(value[2] != undefined){
                date.year = value[2];
            }
        }
        
        
 
        return date;
    },
 
    setValue: function(value) {
 
        if(value == null || value.month == 'undefined' || value.year == 'undefined' || value.day == 'undefined'){
            $('#'+this.id).val('');
            return;
        } else {
            $('#'+this.id).val(padString(value.month) +'/'+ padString(value.day) +"/"+ value.year)
            if($('#'+this.id).val() == '//'){
                $('#'+this.id).val('');
            }
        }
        
        this.validate(true);
        return ;
 
        function padString(number){
            if(number == '' || number == 'undefined'){
                return '';
            }
            number = '' + number;
            if(number.length == 1){
                number = '0'+number;
            }
            return number;
        }
 
    }
 
});JFormComponentDropDown = JFormComponent.extend({
    init: function(parentJFormSection, jFormComponentId, jFormComponentType, options) {
        this._super(parentJFormSection, jFormComponentId, jFormComponentType, options);
    },
 
    initialize: function(){
       this.tipTarget = this.component.find('select:last');
    },
 
    getValue: function() {
        if(this.disabledByDependency || this.parentJFormSection.disabledByDependency){
           return null;
        }
            var dropDownValue = $('#'+this.id).val();
            return dropDownValue;
    },
 
    setValue: function(value){
        $('#'+this.id).val(value).trigger('jFormComponent:changed');
      //this.component.find('option[value=\''+value+'\']').attr('selected', 'selected').trigger('jFormComponent:changed');
      this.validate(true);
    }
 
});JFormComponentFile = JFormComponent.extend({
    init: function(parentJFormSection, jFormComponentId, jFormComponentType, options) {
        this._super(parentJFormSection, jFormComponentId, jFormComponentType, options);
    },
    
    initialize: function(){
        var tipTarget = this.component.find('button').parent();
        if (tipTarget.length < 1){
            tipTarget = this.component.find('input:file');
        }
        this.tipTarget = tipTarget;
        if(this.options.customStyle){
            this.setOnChange();
        }
        this.validationFunctions = {
            'required': function(options) {
                var errorMessageArray = ['Required.'];
                return options.value != '' ? 'success' : errorMessageArray;
            },
            'extension': function(options) {
                var errorMessageArray = ['Must have the .'+options.extension+' extension.'];
                var extensionRegex = new RegExp('\\.'+options.extension+'$');
                return options.value == '' || options.value.match(extensionRegex) ? 'success' : errorMessageArray;
            },
            'extensionType': function(options) {
                var extensionType;
                var errorMessageArray = ['Incorrect file type.'];
                if($.isArray(options.extensionType)){
                    extensionType = new RegExp('\\.('+options.extensionType.join('|')+')$');
                }
                else {
                    var extensionObject = {};
                    extensionObject.image = '\\.(bmp|gif|jpg|png|psd|psp|thm|tif)$';
                    extensionObject.document = '\\.(doc|docx|log|msg|pages|rtf|txt|wpd|wps)$';
                    extensionObject.audio = '\\.(aac|aif|iff|m3u|mid|midi|mp3|mpa|ra|wav|wma)$';
                    extensionObject.video = '\\.(3g2|3gp|asf|asx|avi|flv|mov|mp4|mpg|rm|swf|vob|wmv)$';
                    extensionObject.web = '\\.(asp|css|htm|html|js|jsp|php|rss|xhtml)$';
                    extensionType = new RegExp(extensionObject[options.extensionType]);
                    errorMessageArray = ['Must be an '+options.extensionType+' file type.'];
                }
                return options.value == '' || options.value.match(extensionType) ? 'success' : errorMessageArray;
            },
            'size' : function(options){
                return true;
            },
            'imageDimensions' : function(options){
                return true;
            }
        }
    },
 
    setOnChange: function(){
        var self = this;
 
        this.component.find('input:file').change(function(event){
            var value = event.target.value.replace(/.+\\/, '');
            self.component.find('input:text').val(value);
        });
        
    },
 
    setValue: function() {
        return false;
    },
 
    getValue: function() {
        if(this.disabledByDependency || this.parentJFormSection.disabledByDependency){
           return null;
        }
        return this.component.find('input:file').val();
    },
 
    validate: function() {
        this._super();
    }
});JFormComponentHidden = JFormComponent.extend({
    init: function(parentJFormSection, jFormComponentId, jFormComponentType, options) {
        this._super(parentJFormSection, jFormComponentId, jFormComponentType, options);
    },
 
    getValue: function() {
        if(this.disabledByDependency || this.parentJFormSection.disabledByDependency){
           return null;
        }
        return $('#'+this.id).val();
    },
 
    validate: function() {
        this._super();
    }
});
JFormComponentLikert = JFormComponent.extend({
    init: function(parentJFormSection, jFormComponentId, jFormComponentType, options) {
        this._super(parentJFormSection, jFormComponentId, jFormComponentType, options);
    },
 
    initialize: function(){
        var self = this;
        this.changed = false;
        this.tipTarget = this.component;
        this.statementComponentArray = {};
 
        $.each(this.options.statementArray, function(statementName, statementOptions){
            if(!jFormerUtility.empty(self.options.validationOptions)){
                statementOptions.validationOptions = self.options.validationOptions;
            }
            var newLikertStatment = new JFormComponentLikertStatement(self.parentJFormSection, statementName, 'JFormComponentLikertStatement', statementOptions);
            newLikertStatment.id = self.id+'-'+newLikertStatment.id
            self.parentJFormSection.addComponent(newLikertStatment);
            self.statementComponentArray[statementName] = newLikertStatment;
        });
    },
 
    clearValidation: function (){
      $.each(this.statementComponentArray, function(index, statement){
          statement.clearValidation();
      });
    },
 
    setValue: function(data) {
        var self = this;
        return
        /*
        $.each(data, function(key, value){
            if(data[key] != self.options.emptyValue[key]){
                self.component.find('input[id*='+key+']').removeClass('defaultValue').val(value).blur().trigger('component:changed');
            }
        });*/
    },
 
    catchComponentChangedEventListener: function() { return null },
    addHighlightListeners: function() { return null },
    defineComponentChangedEventListener: function() { return null },
    
    addTipListeners: function() { return null },
 
 
 
    getValue: function() {
        var value = {};
        $.each(this.statementComponentArray, function(key, component){
            value[key] = component.getValue();
        })
 
 
        return value;
    },
 
 
    handleErrors: function() {
        var self = this;
        return true;
    },
 
    handleServerValidationResponse: function(errorMessageArray) {
        var self = this;
        if(errorMessageArray.length > 0) {
            $.each(this.instanceArray, function(key, instance){    
                $.each(errorMessageArray, function(index, passedErrorArray){
                    $.each(passedErrorArray, function(statementKey, statementError){
                        var likertStatement = self.parentJFormSection.jFormComponents[instance.id+'-'+statementKey];
                        if(likertStatement != undefined){
                            likertStatement.errorMessageArray = [statementError];
                            likertStatement.validationPassed = false;
                            likertStatement.handleErrors();
                        }
                    }) ;
                });
            });
        }
    },
 
    validate: function(){
        var self = this;
        return true;
    }
        
});
JFormComponentLikertStatement = JFormComponent.extend({
    init: function(parentJFormSection, jFormComponentId, jFormComponentType, options) {
        this._super(parentJFormSection, jFormComponentId, jFormComponentType, options);
    },
 
    initialize: function(){
        var self = this
        this.changed = false;
        this.component = $('input[name='+this.id+']:first').closest('tr');
        this.tipTarget = this.component;
        this.tipDiv = this.component.find('div.jFormComponentLikertStatementTip');
        this.component.find('td').click(function(event){
                event.preventDefault();
                $(event.target).find('input').attr("checked", "checked").trigger('click');
                
        });
        this.validationFunctions = {
            'required': function(options) {
                var errorMessageArray = ['Required.'];
                return options.value.length > 0 ? 'success' : errorMessageArray;
            }
        }
 
    },
 
    setValue: function(data) {
        var self = this;
            self.component.find('input').val([data]);
            this.validate(true);
        /*
        $.each(data, function(key, value){
            if(data[key] != self.options.emptyValue[key]){
                self.component.find('input[id*='+key+']').removeClass('defaultValue').val(value).blur().trigger('component:changed');
            }
        });*/
    },
 
    validate: function(){
        this._super();
    },
 
    getValue: function() {
        if(this.disabledByDependency || this.parentJFormSection.disabledByDependency){
            return null;
        }      
        var value = this.component.find('input:checked');
        if(value.length > 0){
            value = value.val()
        } else {
            value = '';
        }
        return value;
    }
});JFormComponentMultipleChoice = JFormComponent.extend({
    init: function(parentJFormSection, jFormComponentId, jFormComponentType, options) {
        this._super(parentJFormSection, jFormComponentId, jFormComponentType, options);
    },
 
    initialize: function(){
        this.tipTarget = this.component;
        this.addChoiceTips();
        this.validationFunctions = {
            //MultipleChoice validations
            'required': function(options) {
                var errorMessageArray = ['Required.'];
                return options.value.length > 0 ? 'success' : errorMessageArray;
            },
            'minOptions': function(options) {
                var errorMessageArray = ['You must select more than '+ options.minOptions +' options'];
                return options.value.length == 0 || options.value.length > options.minOptions ? 'success' : errorMessageArray;
            },
            'maxOptions': function(options) {
                var errorMessageArray = ['You may select up to '+ options.maxOptions +' options. You have selected '+ options.value.length + '.'];
                return options.value.length == 0 || options.value.length <= options.maxOptions ? 'success' : errorMessageArray;
            }
        }
    },
 
    addChoiceTips: function(){
        var self = this;
        var tips = this.component.find('div.jFormComponentMultipleChoiceTip');
        if(tips.length > 0) {
            tips.each(function(index, tip) {
                var tipTarget = $(tip).prev('label').find('.jFormComponentMultipleChoiceTipIcon');
                if (tipTarget.length == 0){
                    tipTarget = $(tip).parent();
                }
                tipTarget.simpletip({
                    position: 'topRight',
                    content: $(tip),
                    baseClass: 'jFormerTip jFormComponentMultipleChoiceTip',
                    hideEffect: 'none'
                });
            });
        }
    },
 
    getValue: function() {
        if(this.disabledByDependency || this.parentJFormSection.disabledByDependency){
           return null;
        }
        var multipleChoiceValue
        if(this.options.multipleChoiceType == 'checkbox') {
            multipleChoiceValue = [];
            this.component.find('input:checked').each(function(index, input){
                multipleChoiceValue.push($(input).val());
            });
        }
        else {
            if(this.component.find('input:checked').length > 0){
                multipleChoiceValue = this.component.find('input:checked').val();
            }
            else {
                multipleChoiceValue = '';
            }
        }
        return multipleChoiceValue;
    },
 
    setValue: function(data) {
        var self = this;
        if(this.options.multipleChoiceType == 'checkbox') {
            $.each(data, function(key, value){
                self.component.find('input[value=\''+value+'\']').attr('checked', 'checked').trigger('component:changed');
            });
        }
        else {
            this.component.find('input[value=\''+data+'\']').attr('checked', 'checked').trigger('component:changed');
        }
        this.validate(true);
    }
});JFormComponentName = JFormComponent.extend({
    init: function(parentJFormSection, jFormComponentId, jFormComponentType, options) {
        this._super(parentJFormSection, jFormComponentId, jFormComponentType, options);
    },
 
    initialize: function(){
        this.tipTarget = this.component.find('input:last');
        if(this.options.emptyValue){
            this.addEmptyValues();
        }
        this.changed = false;
        this.validationFunctions = {
            'required': function(options) {
                var errorMessageArray = [];
                if(options.value.firstName == '') {
                    errorMessageArray.push(['First name is required.']);
                }
                if(options.value.lastName == '') {
                    errorMessageArray.push(['Last name is required.']);
                }
                return errorMessageArray.length < 1 ? 'success' : errorMessageArray;
            }
        }
    },
 
    setValue: function(data) {
        var self = this;
        if(this.options.emptyValue){
            if(data.firstName != self.options.emptyValue.firstName){
                self.component.find('input[id*=firstName]').removeClass('defaultValue').val(data.firstName).blur();
            }
            self.component.find('input[id*=middleInitial]').removeClass('defaultValue').val(data.middleInitial).blur();
            if(data.lastName != self.options.emptyValue.lastName){
                self.component.find('input[id*=lastName]').removeClass('defaultValue').val(data.lastName).blur();
            }
        } else {
            self.component.find('input[id*=firstName]').val(data.firstName)
            self.component.find('input[id*=middleInitial]').val(data.middleInitial);
            self.component.find('input[id*=lastName]').val(data.lastName);
        }
        this.validate(true);
 
        /*
        $.each(data, function(key, value){
            if(data[key] != self.options.emptyValue[key]){
                self.component.find('input[id*='+key+']').removeClass('defaultValue').val(value).blur().trigger('component:changed');
            }
        });*/
    },
 
    getValue: function() {
        if(this.disabledByDependency || this.parentJFormSection.disabledByDependency){
           return null;
        }
        var name = {},
        self = this;
        name.firstName = this.component.find('input[id*=firstName]').val();
        name.middleInitial = this.component.find('input[id*=middleInitial]').val();
        name.lastName = this.component.find('input[id*=lastName]').val();
 
        if(this.options.emptyValue){
            if(name.firstName == this.options.emptyValue.firstName){
                name.firstName = '';
            }
            if(this.component.find('input[id$=middleInitial]').hasClass('defaultValue') ){
                name.middleInitial = '';
            }
            if(name.lastName == this.options.emptyValue.lastName){
                name.lastName = '';
            }
        }
 
        return name;
    },
 
    validate: function(){
        var self = this;
        if(!this.changed){
            this._super();
        }
        
        setTimeout(function() {
            if(!self.component.hasClass('jFormComponentHighlight')){
                if(self.options.validationOptions.length < 1){
                    return true;
                }
                self.clearValidation();
                $.each(self.options.validationOptions, function(validationType, validationOptions){
                    validationOptions['value'] = self.getValue();
                    var validation = self.validationFunctions[validationType](validationOptions);
                    if(validation == 'success'){
                        return;
                    }
                    else {
                        $.merge(self.errorMessageArray, validation);
                        self.validationPassed = false;
                    }
                });
                if(self.errorMessageArray.length > 0 ){
                    self.handleErrors();
                }
                self.changed = false;
                return self.validationPassed;
            }
        }, 1);
    },
 
    addEmptyValues: function(){
        var self = this,
        emptyValue = this.options.emptyValue;
        $.each(emptyValue, function(key, value){
            var input = self.component.find('input[id*='+key+']');
            input.addClass('defaultValue');
            input.focus(function(event){
                if ($.trim($(event.target).val()) == value ){
                    $(event.target).val('');
                    $(event.target).removeClass('defaultValue');
                }
            });
            input.blur(function(event){
                if ($.trim($(event.target).val()) == '' ){
                    $(event.target).addClass('defaultValue');
                    $(event.target).val(value);
                }
            });
            input.trigger('blur');
        });
    }
});
JFormComponentSingleLineText = JFormComponent.extend({
    init: function(parentJFormSection, jFormComponentId, jFormComponentType ,options) {
        this._super(parentJFormSection, jFormComponentId, jFormComponentType ,options);
    },
 
    initialize: function() {
        this.tipTarget = this.component.find('input:last');
        this.enterSubmits = false;
        if(this.options.mask) {
            this.addMask();
        }
        if(this.options.emptyValue) {
            this.addEmptyValue();
        }
        if(this.component.find('input:password').length == 1 && this.options.showStrength){
            this.addPasswordStrength();
        }
        this.validationFunctions = {
            'alpha': function(options) {
                var errorMessageArray = ['Must only contain letters.'];
                return options.value == '' || options.value.match(/^[A-Za-z]+$/i)  ? 'success' : errorMessageArray;
            },
            'alphaDecimal': function(options) {
                var errorMessageArray = ['Must only contain letters, numbers, or periods.'];
                return options.value == '' || options.value.match(/^[A-Za-z0-9\.]+$/i)  ? 'success' : errorMessageArray;
            },
            'alphaNumeric': function(options) {
                var errorMessageArray = ['Must only contain letters or numbers.'];
                return options.value == '' || options.value.match(/^[A-Za-z0-9]+$/i)  ? 'success' : errorMessageArray;
            },
            'blank': function(options) {
                var errorMessageArray = ['Must be blank.'];
                return $.trim(options.value).length == 0 ? 'success' : errorMessageArray;
            },
            'canadianPostal': function(options) {
                var errorMessageArray = ['Must be a valid Canadian postal code.'];
                return options.value == '' || options.value.match(/^[ABCEGHJKLMNPRSTVXY][0-9][A-Z] [0-9][A-Z][0-9]$/)  ? 'success' : errorMessageArray;
            },
            'date': function(options) {
                var errorMessageArray = ['Must be a date in the mm/dd/yyyy format.'];
                return options.value == '' || options.value.match(/^(0?[1-9]|1[012])[\- \/.](0?[1-9]|[12][0-9]|3[01])[\- \/.](19|20)[0-9]{2}$/)  ? 'success' : errorMessageArray;
            },
            'dateTime': function(options) {
                var errorMessageArray = ['Must be a date in the mm/dd/yyyy hh:mm:ss tt format. ss and tt are optional.'];
                return options.value == '' || options.value.match(/^(0?[1-9]|1[012])[\- \/.](0?[1-9]|[12][0-9]|3[01])[\- \/.](19|20)?[0-9]{2} [0-2]?\d:[0-5]\d(:[0-5]\d)?( ?(a|p)m)?$/i)  ? 'success' : errorMessageArray;
            },
            'decimal': function(options) {
                // Can be negative and have a decimal value
                // Do not accept commas in value as the DB does not accept them
                var errorMessageArray = ['Must be a number without any commas. Decimal is optional.'];
                return options.value == '' || options.value.match(/^-?((\d+(\.\d+)?)|(\.\d+))$/) ? 'success' : errorMessageArray;
            },
            'decimalNegative': function(options) {
                // Must be negative and have a decimal value
                var errorMessageArray = ['Must be a negative number without any commas. Decimal is optional.'];
                var isDecimal = this.decimal(options);
                return options.value == '' || (isDecimal == 'success' && (parseFloat(options.value) < 0)) ? 'success' : errorMessageArray;
            },
            'decimalPositive': function(options) {
                // Must be positive and have a decimal value
                var errorMessageArray = ['Must be a positive number without any commas. Decimal is optional.'];
                var isDecimal = this.decimal(options);
                return options.value == '' ||  (isDecimal == 'success' && (parseFloat(options.value) > 0)) ? 'success' : errorMessageArray;
            },
            'decimalZeroNegative': function(options) {
                // Must be negative and have a decimal value
                var errorMessageArray = ['Must be zero or a negative number without any commas. Decimal is optional.'];
                var isDecimal = self.validations.decimal({
                    "value":options.value
                    });
                return options.value == '' || (isDecimal == 'success' && (parseFloat(options.value) <= 0)) ? 'success' : errorMessageArray;
            },
            'decimalZeroPositive': function(options) {
                // Must be positive and have a decimal value
                var errorMessageArray = ['Must be zero or a positive number without any commas. Decimal is optional.'];
                var isDecimal = this.decimal(options);
                return options.value == '' || (isDecimal == 'success' && (parseFloat(options.value) >= 0)) ? 'success' : errorMessageArray;
            },
            'email': function(options) {
                var errorMessageArray = ['Must be a valid e-mail address.'];
                return options.value == '' || options.value.match(/^[A-Z0-9._%-\+]+@(?:[A-Z0-9\-]+\.)+[A-Z]{2,4}$/i)  ? 'success' : errorMessageArray;
            },
            'integer': function(options) {
                var errorMessageArray = ['Must be a whole number.'];
                return options.value == '' || options.value.match(/^-?\d+$/) ? 'success' : errorMessageArray;
            },
            'integerNegative': function(options) {
                var errorMessageArray = ['Must be a negative whole number.'];
                var isInteger = this.integer(options);
                return options.value == '' || (isInteger == 'success' && (parseInt(options.value, 10) < 0)) ? 'success' : errorMessageArray;
            },
            'integerPositive': function(options) {
                var errorMessageArray = ['Must be a positive whole number.'];
                var isInteger = this.integer(options);
                return options.value == '' || (isInteger == 'success' && (parseInt(options.value, 10) > 0)) ? 'success' : errorMessageArray;
            },
            'integerZeroNegative': function(options) {
                var errorMessageArray = ['Must be zero or a negative whole number.'];
                var isInteger = this.integer(options);
                return options.value == '' || (isInteger == 'success' && (parseInt(options.value, 10) <= 0)) ? 'success' : errorMessageArray;
            },
            'integerZeroPositive': function(options) {
                var errorMessageArray = ['Must be zero or a positive whole number.'];
                var isInteger = this.integer(options);
                return options.value == '' || (isInteger == 'success' && (parseInt(options.value, 10) >= 0)) ? 'success' : errorMessageArray;
            },
            'isbn': function(options) {
                //Match an ISBN
                var errorMessageArray = ['Must be a valid ISBN and consist of either ten or thirteen characters.'];
                //For ISBN-10
                if(options.value.match(/^(?=.{13}$)\d{1,5}([\- ])\d{1,7}\1\d{1,6}\1(\d|X)$/)) {
                    errorMessageArray = 'sucess';
                }
                if(options.value.match(/^\d{9}(\d|X)$/)) {
                    errorMessageArray = 'sucess';
                }
                //For ISBN-13
                if(options.value.match(/^(?=.{17}$)\d{3}([\- ])\d{1,5}\1\d{1,7}\1\d{1,6}\1(\d|X)$/)) {
                    errorMessageArray = 'sucess';
                }
                if(options.value.match(/^\d{3}[\- ]\d{9}(\d|X)$/)) {
                    errorMessageArray = 'sucess';
                }
                //ISBN-13 without starting delimiter (Not a valid ISBN but less strict validation was requested)
                if(options.value.match(/^\d{12}(\d|X)$/)) {
                    errorMessageArray = 'sucess';
                }
                return errorMessageArray;
            },
            'length' : function(options) {
                var errorMessageArray = ['Must be exactly ' + options.length + ' characters long. Current value is '+ options.value.length +' characters.'];
                return options.value == '' || options.value.length == options.length  ? 'success' : errorMessageArray;
            },
            'matches': function(options) {
                var errorMessageArray = ['Does not match.'];
 
                // If the match should occur within the same section instances, both the source and destination fields are stored in the same section
                var idToMatch = options.matches;
 
                // If it is matching to section instances
                if(options.sectionInstances) {
                    var sectionId = options.component.attr('id').match(/-section[\d]+/);
                    if(sectionId) {
                        idToMatch = options.matches + sectionId;
                    }
                }
 
                return options.value == $('#'+idToMatch).val() ? 'success' : errorMessageArray;
            },
            'maxLength' : function(options) {
                var errorMessageArray = ['Must be less than ' + options.maxLength + ' characters long. Current value is '+ options.value.length +' characters.'];
                return options.value == '' || options.value.length <= options.maxLength  ? 'success' : errorMessageArray;
            },
            'maxFloat': function(options) {
                //Value cannot have more digits then specified in maxFloat
                var errorMessageArray = 'Must be numeric and cannot have more than ' + options.maxFloat + ' decimal place(s).',
                maxFloatPattern = new RegExp('^-?((\\d+(\\.\\d{0,'+ options.maxFloat + '})?)|(\\.\\d{0,' + options + '}))$');
                return options.value == '' || options.value.match(maxFloatPattern) ? 'success' : errorMessageArray;
            },
            'maxValue': function(options) {
                var errorMessageArray = ['Must be numeric and less than ' + options.maxValue + '.'];
                return (options.value <= options.maxValue) ? 'success' : errorMessageArray;
            },			
            'minLength' : function(options) {
                var errorMessageArray = ['Must be at least ' + options.minLength + ' characters long. Current value is '+ options.value.length +' characters.'];
                return options.value == '' || options.value.length >= options.minLength  ? 'success' : errorMessageArray;
            },
            'money' : function(options) {
                var errorMessageArray = ['Must be a valid dollar value.'];
                return options.value == '' || options.value.match(/^((-?\$)|(\$-?)|(-))?((\d+(\.\d{2})?)|(\.\d{2}))$/)  ? 'success' : errorMessageArray;
            },
            'moneyNegative' : function(options) {
                var errorMessageArray = ['Must be a valid negative dollar value.'];
                return options.value == '' || options.value.match(/^((-?\$)|(\$-?)|(-))?((\d+(\.\d{2})?)|(\.\d{2}))$/) && RegExp.$5 < 0  ? 'success' : errorMessageArray;
            },
            'moneyPositive' : function(options) {
                var errorMessageArray = ['Must be a valid positive dollar value.'];
                return options.value == '' || options.value.match(/^((-?\$)|(\$-?)|(-))?((\d+(\.\d{2})?)|(\.\d{2}))$/) && RegExp.$5 > 0  ? 'success' : errorMessageArray;
            },
            'moneyZeroNegative' : function(options) {
                var errorMessageArray = ['Must be zero or a valid negative dollar value.'];
                return options.value == '' || options.value.match(/^((-?\$)|(\$-?)|(-))?((\d+(\.\d{2})?)|(\.\d{2}))$/) && RegExp.$5 <= 0  ? 'success' : errorMessageArray;
            },
            'moneyZeroPositive' : function(options) {
                var errorMessageArray = ['Must be zero or a valid positive dollar value.'];
                return options.value == '' || options.value.match(/^((-?\$)|(\$-?)|(-))?((\d+(\.\d{2})?)|(\.\d{2}))$/) && RegExp.$5 >= 0  ? 'success' : errorMessageArray;
            },
            'password': function(options) {
                var errorMessageArray = ['Must be between 4 and 32 characters.'];
                return options.value == '' || options.value.match(/^.{4,32}$/)  ? 'success' : errorMessageArray;
            },
            'phone': function(options) {
                var errorMessageArray = ['Must be a 10 digit phone number.'];
                return options.value == '' || options.value.match(/^(1[\-. ]?)?\(?[0-9]{3}\)?[\-. ]?[0-9]{3}[\-. ]?[0-9]{4}$/)  ? 'success' : errorMessageArray ;
            },
            'postalZip': function(options) {
                var errorMessageArray = ['Must be a valid United States zip code, Canadian postal code, or United Kingdom postal code.']
                return options.value == '' || this.zip(options) == 'success' || this.canadianPostal(options) == 'success' || this.ukPostal() == 'success' ? 'success' : errorMessageArray;
            },
            'required': function(options) {
                var errorMessageArray = ['Required.'];
                return options.value != '' ? 'success' : errorMessageArray;
            },
            'serverSide': function(options) {
                if(options.value == '') {
                    return 'success'
                }
 
                // options: value, url, data
                var errorMessageArray = [];
 
                options.component.addClass('jFormComponentServerSideCheck');
                $.ajax({
                    url: options.url,
                    type: 'post',
                    data:{
                        'task': options.task,
                        'value': options.value
                    },
                    dataType: 'json' ,
                    cache: false,
                    async: false,
                    success: function(json) {
                        if(json.status != 'success') {
                            errorMessageArray = json.response;
                        }
 
                        options.component.removeClass('jFormComponentServerSideCheck');
                    },
                    error: function(XMLHttpRequest, textStatus, errorThrown){
                        if(textStatus != 'error'){
                            errorThrown = textStatus ? textStatus : 'Unknown error';
                        }
                        errorMessageArray = ['There was an error during server side validation: '+ errorThrown];
                        options.component.removeClass('jFormComponentServerSideCheck');
                    }
                });
 
                return errorMessageArray.length < 1 ? 'success' : errorMessageArray;
            },
            'ssn': function(options) {
                var errorMessageArray = ['Must be a valid United States social security number.'];
                return options.value == '' || options.value.match(/^\d{3}-?\d{2}-?\d{4}$/i)  ? 'success' : errorMessageArray;
            },
            'teenager': function(options) {
                var errorMessageArray = 'Must be at least 13 years old.',
                birthday = new Date(options.value),
                now = new Date(),
                limit = new Date(now.getFullYear() - 13 , now.getMonth(), now.getDate()),
                timeDifference = (limit - birthday);
                return options.value == '' || timeDifference >= 0  ? 'success' : errorMessageArray;
            },
            'time': function(options) {
                var errorMessageArray = ['Must be a time in the hh:mm:ss tt format. ss and tt are optional.'];
                return options.value == '' || options.value.match(/^[0-2]?\d:[0-5]\d(:[0-5]\d)?( ?(a|p)m)?$/i)  ? 'success' : errorMessageArray;
            },
            'ukPostal' : function(options) {
                var errorMessageArray = ['Must be a valid United Kingdom postal code.'];
                return options.value == '' || options.value.match(/^[A-Z]{1,2}[0-9][A-Z0-9]? [0-9][ABD-HJLNP-UW-Z]{2}$/)  ? 'success' : errorMessageArray;
            },
            'url': function(options) {
                var errorMessageArray = ['Must be a valid Internet address.'];
                return options.value == '' || options.value.match(/^((ht|f)tp(s)?:\/\/|www\.)?([\-A-Z0-9.]+)(\.[a-zA-Z]{2,4})(\/[\-A-Z0-9+&@#\/%=~_|!:,.;]*)?(\?[\-A-Z0-9+&@#\/%=~_|!:,.;]*)?$/i)  ? 'success' : errorMessageArray;
            },
            'username': function(options) {
                var errorMessageArray = ['Must use 4 to 32 characters and start with a letter.'];
                return options.value == '' || options.value.match(/^[A-Za-z](?=[A-Za-z0-9_.]{3,31}$)[a-zA-Z0-9_]*\.?[a-zA-Z0-9_]*$/)  ? 'success' : errorMessageArray;
            },
            'zip': function(options) {
                var errorMessageArray = ['Must be a valid United States zip code.'];
                return options.value == '' || options.value.match(/^[0-9]{5}(?:-[0-9]{4})?$/)  ? 'success' : errorMessageArray;
            }
        }
    },
 
    addMask: function(){
        this.component.find('input').mask("?"+this.options.mask, {
                placeholder:' '
            });
    },
 
    addPasswordStrength: function(){
        var self = this,
        component = this.component;
 
        var strengthComponent = "<p id='"+this.id+"-strength' > Strength: <b> " + this.getPasswordStrength().strength + " </b> </p>";
        component.find('div.jFormComponentTip').append(strengthComponent);
        component.find('input:password').bind('keyup', function(event){
           component.find('#'+self.id+'-strength b').text(self.getPasswordStrength().strength);
           self.tip.update(component.find('div.jFormComponentTip').html());
        });
    },
 
    getPasswordStrength: function() {
        var value = this.getValue(),
        score = 0,
        strength = 'None';
 
        if(value.length >= 6) {
            score = (score + 1); // at least six characters
        }
        if(value.length >= 10) {
            score = (score + 1); // 10 characters+ bonus
        }
        if(value.match(/[a-z]/)) { // [verified] at least one lower case letter
            score = (score + 1);
        }
        if(value.match(/[A-Z]/)) { // [verified] at least one upper case letter
            score = (score + 1);
        }
        if(value.match(/\d+/)) { // [verified] at least one number
            score = (score + 1);
        }
        if(value.match(/(\d.*\d)/)) { // [verified] at least two numbers
            score = (score + 1);
        }
        if(value.match(/[!,@#$%\^&*?_~]/)) { // [verified] at least one special character
            score = (score + 1);
        }
        if(value.match(/([!,@#$%\^&*?_~].*[!,@#$%\^&*?_~])/)) { // [verified] at least two special characters
            score = (score + 1);
        }
        if(value.match(/[a-z]/) && value.match(/[A-Z]/)) { // [verified] both upper and lower case
            score = (score + 1);
        }
        if(value.match(/\d/) && value.match(/\D/)) { // [verified] both letters and numbers
            score = (score + 1);
        }
        if(value.match(/[a-z]/) && value.match(/[A-Z]/) && value.match(/\d/) && value.match(/[!,@#$%\^&*?_~]/)) {
            score = (score + 1);
        }
 
        if(score === 0) {
            strength = 'None';
        }
        else if(score <= 1) {
            strength = 'Very Weak';
        }
        else if(score <= 3) {
            strength = 'Weak';
        }
        else if(score <= 5) {
            strength = 'Good';
        }
        else if(score <= 7) {
            strength = 'Strong';
        }
        else if(score > 7) {
            strength = 'Very Strong';
        }
 
        return {
            'score': score,
            'strength': strength
        };
    },
 
    getValue: function() {
        if(this.disabledByDependency || this.parentJFormSection.disabledByDependency){
           return null;
        }
        var input = $('#'+this.id).val();
        if(this.options.emptyValue){
            if( input == this.options.emptyValue){
                return '';
            }
            else {
                return input;
            }
        } else {
            return input;
        }
    },
 
    setValue: function(value) {
        $('#'+this.id).val(value).removeClass('defaultValue');
        this.validate(true);
    },
 
     addEmptyValue: function(){
        var emptyValue = this.options.emptyValue,
        input = this.component.find('input');
        input.addClass('defaultValue');
        input.val(emptyValue);
 
        var target ='';
        input.focus(function(event){
            target = $(event.target);
            if ($.trim(target.val()) == emptyValue ){
                target.val('');
                target.removeClass('defaultValue');
            }
        });
        input.blur(function(event){
            target = $(event.target);
            if ($.trim(target.val()) == '' ){
                target.addClass('defaultValue');
                target.val(emptyValue);
            }
        });
 
    }
 
});
 
JFormComponentTextArea = JFormComponent.extend({
    init: function(parentJFormSection, jFormComponentId, jFormComponentType, options) {
        this._super(parentJFormSection, jFormComponentId, jFormComponentType, options);
        
        if(this.options.allowTabbing) {
            this.allowTabbing();
        }
    },
 
    initialize: function() {
        this.tipTarget = this.component.find('textarea');
        if(this.options.emptyValue) {
            this.addEmptyValue();
        }
    },
 
    allowTabbing: function() {
        this.component.find('textarea').bind('keydown', function(event) {
            if(event != null) {
                if(event.keyCode == 9) {  // tab character
                    if(this.setSelectionRange) {
                        var sS = this.selectionStart;
                        var sE = this.selectionEnd;
                        this.value = this.value.substring(0, sS) + "\t" + this.value.substr(sE);
                        this.setSelectionRange(sS + 1, sS + 1);
                        this.focus();
                    }
                    else if (this.createTextRange) {
                        document.selection.createRange().text = "\t";
                        event.returnValue = false;
                    }
                    if(event.preventDefault) {
                        event.preventDefault();
                    }
                    return false;
                }
            }
        });
    },
 
    getValue: function() {
        if(this.disabledByDependency || this.parentJFormSection.disabledByDependency){
            return null;
        }
        return $('#'+this.id).val();
    },
 
    setValue: function(value) {
        $('#'+this.id).val(value);
        this.validate(true);
    }
 
});