text: /*!
   * jQuery Templates Plugin 1.0.0pre
   * http://github.com/jquery/jquery-tmpl
   * Requires jQuery 1.4.2
   *
   * Copyright 2011, Software Freedom Conservancy, Inc.
   * Dual licensed under the MIT or GPL Version 2 licenses.
   * http://jquery.org/license
   */
  (function( jQuery, undefined ){
  	var oldManip = jQuery.fn.domManip, tmplItmAtt = "_tmplitem", htmlExpr = /^[^<]*(<[\w\W]+>)[^>]*$|\{\{\! /,
  		newTmplItems = {}, wrappedItems = {}, appendToTmplItems, topTmplItem = { key: 0, data: {} }, itemKey = 0, cloneIndex = 0, stack = [];
  
  	function newTmplItem( options, parentItem, fn, data ) {
  		// Returns a template item data structure for a new rendered instance of a template (a 'template item').
  		// The content field is a hierarchical array of strings and nested items (to be
  		// removed and replaced by nodes field of dom elements, once inserted in DOM).
  		var newItem = {
  			data: data || (data === 0 || data === false) ? data : (parentItem ? parentItem.data : {}),
  			_wrap: parentItem ? parentItem._wrap : null,
  			tmpl: null,
  			parent: parentItem || null,
  			nodes: [],
  			calls: tiCalls,
  			nest: tiNest,
  			wrap: tiWrap,
  			html: tiHtml,
  			update: tiUpdate
  		};
  		if ( options ) {
  			jQuery.extend( newItem, options, { nodes: [], parent: parentItem });
  		}
  		if ( fn ) {
  			// Build the hierarchical content to be used during insertion into DOM
  			newItem.tmpl = fn;
  			newItem._ctnt = newItem._ctnt || newItem.tmpl( jQuery, newItem );
  			newItem.key = ++itemKey;
  			// Keep track of new template item, until it is stored as jQuery Data on DOM element
  			(stack.length ? wrappedItems : newTmplItems)[itemKey] = newItem;
  		}
  		return newItem;
  	}
  
  	// Override appendTo etc., in order to provide support for targeting multiple elements. (This code would disappear if integrated in jquery core).
  	jQuery.each({
  		appendTo: "append",
  		prependTo: "prepend",
  		insertBefore: "before",
  		insertAfter: "after",
  		replaceAll: "replaceWith"
  	}, function( name, original ) {
  		jQuery.fn[ name ] = function( selector ) {
  			var ret = [], insert = jQuery( selector ), elems, i, l, tmplItems,
  				parent = this.length === 1 && this[0].parentNode;
  
  			appendToTmplItems = newTmplItems || {};
  			if ( parent && parent.nodeType === 11 && parent.childNodes.length === 1 && insert.length === 1 ) {
  				insert[ original ]( this[0] );
  				ret = this;
  			} else {
  				for ( i = 0, l = insert.length; i < l; i++ ) {
  					cloneIndex = i;
  					elems = (i > 0 ? this.clone(true) : this).get();
  					jQuery( insert[i] )[ original ]( elems );
  					ret = ret.concat( elems );
  				}
  				cloneIndex = 0;
  				ret = this.pushStack( ret, name, insert.selector );
  			}
  			tmplItems = appendToTmplItems;
  			appendToTmplItems = null;
  			jQuery.tmpl.complete( tmplItems );
  			return ret;
  		};
  	});
  
  	jQuery.fn.extend({
  		// Use first wrapped element as template markup.
  		// Return wrapped set of template items, obtained by rendering template against data.
  		tmpl: function( data, options, parentItem ) {
  			return jQuery.tmpl( this[0], data, options, parentItem );
  		},
  
  		// Find which rendered template item the first wrapped DOM element belongs to
  		tmplItem: function() {
  			return jQuery.tmplItem( this[0] );
  		},
  
  		// Consider the first wrapped element as a template declaration, and get the compiled template or store it as a named template.
  		template: function( name ) {
  			return jQuery.template( name, this[0] );
  		},
  
  		domManip: function( args, table, callback, options ) {
  			if ( args[0] && jQuery.isArray( args[0] )) {
  				var dmArgs = jQuery.makeArray( arguments ), elems = args[0], elemsLength = elems.length, i = 0, tmplItem;
  				while ( i < elemsLength && !(tmplItem = jQuery.data( elems[i++], "tmplItem" ))) {}
  				if ( tmplItem && cloneIndex ) {
  					dmArgs[2] = function( fragClone ) {
  						// Handler called by oldManip when rendered template has been inserted into DOM.
  						jQuery.tmpl.afterManip( this, fragClone, callback );
  					};
  				}
  				oldManip.apply( this, dmArgs );
  			} else {
  				oldManip.apply( this, arguments );
  			}
  			cloneIndex = 0;
  			if ( !appendToTmplItems ) {
  				jQuery.tmpl.complete( newTmplItems );
  			}
  			return this;
  		}
  	});
  
  	jQuery.extend({
  		// Return wrapped set of template items, obtained by rendering template against data.
  		tmpl: function( tmpl, data, options, parentItem ) {
  			var ret, topLevel = !parentItem;
  			if ( topLevel ) {
  				// This is a top-level tmpl call (not from a nested template using {{tmpl}})
  				parentItem = topTmplItem;
  				tmpl = jQuery.template[tmpl] || jQuery.template( null, tmpl );
  				wrappedItems = {}; // Any wrapped items will be rebuilt, since this is top level
  			} else if ( !tmpl ) {
  				// The template item is already associated with DOM - this is a refresh.
  				// Re-evaluate rendered template for the parentItem
  				tmpl = parentItem.tmpl;
  				newTmplItems[parentItem.key] = parentItem;
  				parentItem.nodes = [];
  				if ( parentItem.wrapped ) {
  					updateWrapped( parentItem, parentItem.wrapped );
  				}
  				// Rebuild, without creating a new template item
  				return jQuery( build( parentItem, null, parentItem.tmpl( jQuery, parentItem ) ));
  			}
  			if ( !tmpl ) {
  				return []; // Could throw...
  			}
  			if ( typeof data === "function" ) {
  				data = data.call( parentItem || {} );
  			}
  			if ( options && options.wrapped ) {
  				updateWrapped( options, options.wrapped );
  			}
  			ret = jQuery.isArray( data ) ?
  				jQuery.map( data, function( dataItem ) {
  					return dataItem ? newTmplItem( options, parentItem, tmpl, dataItem ) : null;
  				}) :
  				[ newTmplItem( options, parentItem, tmpl, data ) ];
  			return topLevel ? jQuery( build( parentItem, null, ret ) ) : ret;
  		},
  
  		// Return rendered template item for an element.
  		tmplItem: function( elem ) {
  			var tmplItem;
  			if ( elem instanceof jQuery ) {
  				elem = elem[0];
  			}
  			while ( elem && elem.nodeType === 1 && !(tmplItem = jQuery.data( elem, "tmplItem" )) && (elem = elem.parentNode) ) {}
  			return tmplItem || topTmplItem;
  		},
  
  		// Set:
  		// Use $.template( name, tmpl ) to cache a named template,
  		// where tmpl is a template string, a script element or a jQuery instance wrapping a script element, etc.
  		// Use $( "selector" ).template( name ) to provide access by name to a script block template declaration.
  
  		// Get:
  		// Use $.template( name ) to access a cached template.
  		// Also $( selectorToScriptBlock ).template(), or $.template( null, templateString )
  		// will return the compiled template, without adding a name reference.
  		// If templateString includes at least one HTML tag, $.template( templateString ) is equivalent
  		// to $.template( null, templateString )
  		template: function( name, tmpl ) {
  			if (tmpl) {
  				// Compile template and associate with name
  				if ( typeof tmpl === "string" ) {
  					// This is an HTML string being passed directly in.
  					tmpl = buildTmplFn( tmpl );
  				} else if ( tmpl instanceof jQuery ) {
  					tmpl = tmpl[0] || {};
  				}
  				if ( tmpl.nodeType ) {
  					// If this is a template block, use cached copy, or generate tmpl function and cache.
  					tmpl = jQuery.data( tmpl, "tmpl" ) || jQuery.data( tmpl, "tmpl", buildTmplFn( tmpl.innerHTML ));
  					// Issue: In IE, if the container element is not a script block, the innerHTML will remove quotes from attribute values whenever the value does not include white space.
  					// This means that foo="${x}" will not work if the value of x includes white space: foo="${x}" -> foo=value of x.
  					// To correct this, include space in tag: foo="${ x }" -> foo="value of x"
  				}
  				return typeof name === "string" ? (jQuery.template[name] = tmpl) : tmpl;
  			}
  			// Return named compiled template
  			return name ? (typeof name !== "string" ? jQuery.template( null, name ):
  				(jQuery.template[name] ||
  					// If not in map, and not containing at least on HTML tag, treat as a selector.
  					// (If integrated with core, use quickExpr.exec)
  					jQuery.template( null, htmlExpr.test( name ) ? name : jQuery( name )))) : null;
  		},
  
  		encode: function( text ) {
  			// Do HTML encoding replacing < > & and ' and " by corresponding entities.
  			return ("" + text).split("<").join("&lt;").split(">").join("&gt;").split('"').join("&#34;").split("'").join("&#39;");
  		}
  	});
  
  	jQuery.extend( jQuery.tmpl, {
  		tag: {
  			"tmpl": {
  				_default: { $2: "null" },
  				open: "if($notnull_1){__=__.concat($item.nest($1,$2));}"
  				// tmpl target parameter can be of type function, so use $1, not $1a (so not auto detection of functions)
  				// This means that {{tmpl foo}} treats foo as a template (which IS a function).
  				// Explicit parens can be used if foo is a function that returns a template: {{tmpl foo()}}.
  			},
  			"wrap": {
  				_default: { $2: "null" },
  				open: "$item.calls(__,$1,$2);__=[];",
  				close: "call=$item.calls();__=call._.concat($item.wrap(call,__));"
  			},
  			"each": {
  				_default: { $2: "$index, $value" },
  				open: "if($notnull_1){$.each($1a,function($2){with(this){",
  				close: "}});}"
  			},
  			"if": {
  				open: "if(($notnull_1) && $1a){",
  				close: "}"
  			},
  			"else": {
  				_default: { $1: "true" },
  				open: "}else if(($notnull_1) && $1a){"
  			},
  			"html": {
  				// Unecoded expression evaluation.
  				open: "if($notnull_1){__.push($1a);}"
  			},
  			"=": {
  				// Encoded expression evaluation. Abbreviated form is ${}.
  				_default: { $1: "$data" },
  				open: "if($notnull_1){__.push($.encode($1a));}"
  			},
  			"!": {
  				// Comment tag. Skipped by parser
  				open: ""
  			}
  		},
  
  		// This stub can be overridden, e.g. in jquery.tmplPlus for providing rendered events
  		complete: function( items ) {
  			newTmplItems = {};
  		},
  
  		// Call this from code which overrides domManip, or equivalent
  		// Manage cloning/storing template items etc.
  		afterManip: function afterManip( elem, fragClone, callback ) {
  			// Provides cloned fragment ready for fixup prior to and after insertion into DOM
  			var content = fragClone.nodeType === 11 ?
  				jQuery.makeArray(fragClone.childNodes) :
  				fragClone.nodeType === 1 ? [fragClone] : [];
  
  			// Return fragment to original caller (e.g. append) for DOM insertion
  			callback.call( elem, fragClone );
  
  			// Fragment has been inserted:- Add inserted nodes to tmplItem data structure. Replace inserted element annotations by jQuery.data.
  			storeTmplItems( content );
  			cloneIndex++;
  		}
  	});
  
  	//========================== Private helper functions, used by code above ==========================
  
  	function build( tmplItem, nested, content ) {
  		// Convert hierarchical content into flat string array
  		// and finally return array of fragments ready for DOM insertion
  		var frag, ret = content ? jQuery.map( content, function( item ) {
  			return (typeof item === "string") ?
  				// Insert template item annotations, to be converted to jQuery.data( "tmplItem" ) when elems are inserted into DOM.
  				(tmplItem.key ? item.replace( /(<\w+)(?=[\s>])(?![^>]*_tmplitem)([^>]*)/g, "$1 " + tmplItmAtt + "=\"" + tmplItem.key + "\" $2" ) : item) :
  				// This is a child template item. Build nested template.
  				build( item, tmplItem, item._ctnt );
  		}) :
  		// If content is not defined, insert tmplItem directly. Not a template item. May be a string, or a string array, e.g. from {{html $item.html()}}.
  		tmplItem;
  		if ( nested ) {
  			return ret;
  		}
  
  		// top-level template
  		ret = ret.join("");
  
  		// Support templates which have initial or final text nodes, or consist only of text
  		// Also support HTML entities within the HTML markup.
  		ret.replace( /^\s*([^<\s][^<]*)?(<[\w\W]+>)([^>]*[^>\s])?\s*$/, function( all, before, middle, after) {
  			frag = jQuery( middle ).get();
  
  			storeTmplItems( frag );
  			if ( before ) {
  				frag = unencode( before ).concat(frag);
  			}
  			if ( after ) {
  				frag = frag.concat(unencode( after ));
  			}
  		});
  		return frag ? frag : unencode( ret );
  	}
  
  	function unencode( text ) {
  		// Use createElement, since createTextNode will not render HTML entities correctly
  		var el = document.createElement( "div" );
  		el.innerHTML = text;
  		return jQuery.makeArray(el.childNodes);
  	}
  
  	// Generate a reusable function that will serve to render a template against data
  	function buildTmplFn( markup ) {
  		return new Function("jQuery","$item",
  			// Use the variable __ to hold a string array while building the compiled template. (See https://github.com/jquery/jquery-tmpl/issues#issue/10).
  			"var $=jQuery,call,__=[],$data=$item.data;" +
  
  			// Introduce the data as local variables using with(){}
  			"with($data){__.push('" +
  
  			// Convert the template into pure JavaScript
  			jQuery.trim(markup)
  				.replace( /([\\'])/g, "\\$1" )
  				.replace( /[\r\t\n]/g, " " )
  				.replace( /\$\{([^\}]*)\}/g, "{{= $1}}" )
  				.replace( /\{\{(\/?)(\w+|.)(?:\(((?:[^\}]|\}(?!\}))*?)?\))?(?:\s+(.*?)?)?(\(((?:[^\}]|\}(?!\}))*?)\))?\s*\}\}/g,
  				function( all, slash, type, fnargs, target, parens, args ) {
  					var tag = jQuery.tmpl.tag[ type ], def, expr, exprAutoFnDetect;
  					if ( !tag ) {
  						throw "Unknown template tag: " + type;
  					}
  					def = tag._default || [];
  					if ( parens && !/\w$/.test(target)) {
  						target += parens;
  						parens = "";
  					}
  					if ( target ) {
  						target = unescape( target );
  						args = args ? ("," + unescape( args ) + ")") : (parens ? ")" : "");
  						// Support for target being things like a.toLowerCase();
  						// In that case don't call with template item as 'this' pointer. Just evaluate...
  						expr = parens ? (target.indexOf(".") > -1 ? target + unescape( parens ) : ("(" + target + ").call($item" + args)) : target;
  						exprAutoFnDetect = parens ? expr : "(typeof(" + target + ")==='function'?(" + target + ").call($item):(" + target + "))";
  					} else {
  						exprAutoFnDetect = expr = def.$1 || "null";
  					}
  					fnargs = unescape( fnargs );
  					return "');" +
  						tag[ slash ? "close" : "open" ]
  							.split( "$notnull_1" ).join( target ? "typeof(" + target + ")!=='undefined' && (" + target + ")!=null" : "true" )
  							.split( "$1a" ).join( exprAutoFnDetect )
  							.split( "$1" ).join( expr )
  							.split( "$2" ).join( fnargs || def.$2 || "" ) +
  						"__.push('";
  				}) +
  			"');}return __;"
  		);
  	}
  	function updateWrapped( options, wrapped ) {
  		// Build the wrapped content.
  		options._wrap = build( options, true,
  			// Suport imperative scenario in which options.wrapped can be set to a selector or an HTML string.
  			jQuery.isArray( wrapped ) ? wrapped : [htmlExpr.test( wrapped ) ? wrapped : jQuery( wrapped ).html()]
  		).join("");
  	}
  
  	function unescape( args ) {
  		return args ? args.replace( /\\'/g, "'").replace(/\\\\/g, "\\" ) : null;
  	}
  	function outerHtml( elem ) {
  		var div = document.createElement("div");
  		div.appendChild( elem.cloneNode(true) );
  		return div.innerHTML;
  	}
  
  	// Store template items in jQuery.data(), ensuring a unique tmplItem data data structure for each rendered template instance.
  	function storeTmplItems( content ) {
  		var keySuffix = "_" + cloneIndex, elem, elems, newClonedItems = {}, i, l, m;
  		for ( i = 0, l = content.length; i < l; i++ ) {
  			if ( (elem = content[i]).nodeType !== 1 ) {
  				continue;
  			}
  			elems = elem.getElementsByTagName("*");
  			for ( m = elems.length - 1; m >= 0; m-- ) {
  				processItemKey( elems[m] );
  			}
  			processItemKey( elem );
  		}
  		function processItemKey( el ) {
  			var pntKey, pntNode = el, pntItem, tmplItem, key;
  			// Ensure that each rendered template inserted into the DOM has its own template item,
  			if ( (key = el.getAttribute( tmplItmAtt ))) {
  				while ( pntNode.parentNode && (pntNode = pntNode.parentNode).nodeType === 1 && !(pntKey = pntNode.getAttribute( tmplItmAtt ))) { }
  				if ( pntKey !== key ) {
  					// The next ancestor with a _tmplitem expando is on a different key than this one.
  					// So this is a top-level element within this template item
  					// Set pntNode to the key of the parentNode, or to 0 if pntNode.parentNode is null, or pntNode is a fragment.
  					pntNode = pntNode.parentNode ? (pntNode.nodeType === 11 ? 0 : (pntNode.getAttribute( tmplItmAtt ) || 0)) : 0;
  					if ( !(tmplItem = newTmplItems[key]) ) {
  						// The item is for wrapped content, and was copied from the temporary parent wrappedItem.
  						tmplItem = wrappedItems[key];
  						tmplItem = newTmplItem( tmplItem, newTmplItems[pntNode]||wrappedItems[pntNode] );
  						tmplItem.key = ++itemKey;
  						newTmplItems[itemKey] = tmplItem;
  					}
  					if ( cloneIndex ) {
  						cloneTmplItem( key );
  					}
  				}
  				el.removeAttribute( tmplItmAtt );
  			} else if ( cloneIndex && (tmplItem = jQuery.data( el, "tmplItem" )) ) {
  				// This was a rendered element, cloned during append or appendTo etc.
  				// TmplItem stored in jQuery data has already been cloned in cloneCopyEvent. We must replace it with a fresh cloned tmplItem.
  				cloneTmplItem( tmplItem.key );
  				newTmplItems[tmplItem.key] = tmplItem;
  				pntNode = jQuery.data( el.parentNode, "tmplItem" );
  				pntNode = pntNode ? pntNode.key : 0;
  			}
  			if ( tmplItem ) {
  				pntItem = tmplItem;
  				// Find the template item of the parent element.
  				// (Using !=, not !==, since pntItem.key is number, and pntNode may be a string)
  				while ( pntItem && pntItem.key != pntNode ) {
  					// Add this element as a top-level node for this rendered template item, as well as for any
  					// ancestor items between this item and the item of its parent element
  					pntItem.nodes.push( el );
  					pntItem = pntItem.parent;
  				}
  				// Delete content built during rendering - reduce API surface area and memory use, and avoid exposing of stale data after rendering...
  				delete tmplItem._ctnt;
  				delete tmplItem._wrap;
  				// Store template item as jQuery data on the element
  				jQuery.data( el, "tmplItem", tmplItem );
  			}
  			function cloneTmplItem( key ) {
  				key = key + keySuffix;
  				tmplItem = newClonedItems[key] =
  					(newClonedItems[key] || newTmplItem( tmplItem, newTmplItems[tmplItem.parent.key + keySuffix] || tmplItem.parent ));
  			}
  		}
  	}
  
  	//---- Helper functions for template item ----
  
  	function tiCalls( content, tmpl, data, options ) {
  		if ( !content ) {
  			return stack.pop();
  		}
  		stack.push({ _: content, tmpl: tmpl, item:this, data: data, options: options });
  	}
  
  	function tiNest( tmpl, data, options ) {
  		// nested template, using {{tmpl}} tag
  		return jQuery.tmpl( jQuery.template( tmpl ), data, options, this );
  	}
  
  	function tiWrap( call, wrapped ) {
  		// nested template, using {{wrap}} tag
  		var options = call.options || {};
  		options.wrapped = wrapped;
  		// Apply the template, which may incorporate wrapped content,
  		return jQuery.tmpl( jQuery.template( call.tmpl ), call.data, options, call.item );
  	}
  
  	function tiHtml( filter, textOnly ) {
  		var wrapped = this._wrap;
  		return jQuery.map(
  			jQuery( jQuery.isArray( wrapped ) ? wrapped.join("") : wrapped ).filter( filter || "*" ),
  			function(e) {
  				return textOnly ?
  					e.innerText || e.textContent :
  					e.outerHTML || outerHtml(e);
  			});
  	}
  
  	function tiUpdate() {
  		var coll = this.nodes;
  		jQuery.tmpl( null, null, null, this).insertBefore( coll[0] );
  		jQuery( coll ).remove();
  	}
  })( jQuery );
  /*!
   * jQuery Form Plugin
   * version: 3.09 (16-APR-2012)
   * @requires jQuery v1.3.2 or later
   *
   * Examples and documentation at: http://malsup.com/jquery/form/
   * Project repository: https://github.com/malsup/form
   * Dual licensed under the MIT and GPL licenses:
   *    http://malsup.github.com/mit-license.txt
   *    http://malsup.github.com/gpl-license-v2.txt
   */
  /*global ActiveXObject alert */
  ;(function($) {
  "use strict";
  
  /*
      Usage Note:
      -----------
      Do not use both ajaxSubmit and ajaxForm on the same form.  These
      functions are mutually exclusive.  Use ajaxSubmit if you want
      to bind your own submit handler to the form.  For example,
  
      $(document).ready(function() {
          $('#myForm').on('submit', function(e) {
              e.preventDefault(); // <-- important
              $(this).ajaxSubmit({
                  target: '#output'
              });
          });
      });
  
      Use ajaxForm when you want the plugin to manage all the event binding
      for you.  For example,
  
      $(document).ready(function() {
          $('#myForm').ajaxForm({
              target: '#output'
          });
      });
      
      You can also use ajaxForm with delegation (requires jQuery v1.7+), so the
      form does not have to exist when you invoke ajaxForm:
  
      $('#myForm').ajaxForm({
          delegation: true,
          target: '#output'
      });
      
      When using ajaxForm, the ajaxSubmit function will be invoked for you
      at the appropriate time.
  */
  
  /**
   * Feature detection
   */
  var feature = {};
  feature.fileapi = $("<input type='file'/>").get(0).files !== undefined;
  feature.formdata = (window.FormData !== undefined);
  
  // HTTPS connection is required for XHR upload on MSIE Browsers
  // even if FormData feature available.
  // modified: kim.jihan@navercorp.com (2014-03-05)
  if(feature.formdata && navigator.userAgent.toLowerCase().indexOf("trident") > -1){
      feature.formdata = feature.formdata &&
                        (location.protocol.toLowerCase().indexOf("https") > -1);
  }
  // -- //
  
  /**
   * ajaxSubmit() provides a mechanism for immediately submitting
   * an HTML form using AJAX.
   */
  $.fn.ajaxSubmit = function(options) {
      /*jshint scripturl:true */
  
      // fast fail if nothing selected (http://dev.jquery.com/ticket/2752)
      if (!this.length) {
          log('ajaxSubmit: skipping submit process - no element selected');
          return this;
      }
      
      var method, action, url, $form = this;
  
      if (typeof options == 'function') {
          options = { success: options };
      }
  
      method = this.attr('method');
      action = this.attr('action');
      url = (typeof action === 'string') ? $.trim(action) : '';
      url = url || window.location.href || '';
      if (url) {
          // clean url (don't include hash vaue)
          url = (url.match(/^([^#]+)/)||[])[1];
      }
  
      options = $.extend(true, {
          url:  url,
          success: $.ajaxSettings.success,
          type: method || 'GET',
          iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank'
      }, options);
  
      // hook for manipulating the form data before it is extracted;
      // convenient for use with rich editors like tinyMCE or FCKEditor
      var veto = {};
      this.trigger('form-pre-serialize', [this, options, veto]);
      if (veto.veto) {
          log('ajaxSubmit: submit vetoed via form-pre-serialize trigger');
          return this;
      }
  
      // provide opportunity to alter form data before it is serialized
      if (options.beforeSerialize && options.beforeSerialize(this, options) === false) {
          log('ajaxSubmit: submit aborted via beforeSerialize callback');
          return this;
      }
  
      var traditional = options.traditional;
      if ( traditional === undefined ) {
          traditional = $.ajaxSettings.traditional;
      }
      
      var elements = [];
      var qx, a = this.formToArray(options.semantic, elements);
      if (options.data) {
          options.extraData = options.data;
          qx = $.param(options.data, traditional);
      }
  
      // give pre-submit callback an opportunity to abort the submit
      if (options.beforeSubmit && options.beforeSubmit(a, this, options) === false) {
          log('ajaxSubmit: submit aborted via beforeSubmit callback');
          return this;
      }
  
      // fire vetoable 'validate' event
      this.trigger('form-submit-validate', [a, this, options, veto]);
      if (veto.veto) {
          log('ajaxSubmit: submit vetoed via form-submit-validate trigger');
          return this;
      }
  
      var q = $.param(a, traditional);
      if (qx) {
          q = ( q ? (q + '&' + qx) : qx );
      }    
      if (options.type.toUpperCase() == 'GET') {
          options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q;
          options.data = null;  // data is null for 'get'
      }
      else {
          options.data = q; // data is the query string for 'post'
      }
  
      var callbacks = [];
      if (options.resetForm) {
          callbacks.push(function() { $form.resetForm(); });
      }
      if (options.clearForm) {
          callbacks.push(function() { $form.clearForm(options.includeHidden); });
      }
  
      // perform a load on the target only if dataType is not provided
      if (!options.dataType && options.target) {
          var oldSuccess = options.success || function(){};
          callbacks.push(function(data) {
              var fn = options.replaceTarget ? 'replaceWith' : 'html';
              $(options.target)[fn](data).each(oldSuccess, arguments);
          });
      }
      else if (options.success) {
          callbacks.push(options.success);
      }
  
      options.success = function(data, status, xhr) { // jQuery 1.4+ passes xhr as 3rd arg
          var context = options.context || options;    // jQuery 1.4+ supports scope context 
          for (var i=0, max=callbacks.length; i < max; i++) {
              callbacks[i].apply(context, [data, status, xhr || $form, $form]);
          }
      };
  
      // are there files to upload?
      var fileInputs = $('input:file:enabled[value]', this); // [value] (issue #113)
      var hasFileInputs = fileInputs.length > 0;
      var mp = 'multipart/form-data';
      var multipart = ($form.attr('enctype') == mp || $form.attr('encoding') == mp);
  
      var fileAPI = feature.fileapi && feature.formdata;
      log("fileAPI :" + fileAPI);
      var shouldUseFrame = (hasFileInputs || multipart) && !fileAPI;
  
      // options.iframe allows user to force iframe mode
      // 06-NOV-09: now defaulting to iframe mode if file input is detected
      if (options.iframe !== false && (options.iframe || shouldUseFrame)) {
          // hack to fix Safari hang (thanks to Tim Molendijk for this)
          // see:  http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d
          if (options.closeKeepAlive) {
              $.get(options.closeKeepAlive, function() {
                  fileUploadIframe(a);
              });
          }
            else {
              fileUploadIframe(a);
            }
      }
      else if ((hasFileInputs || multipart) && fileAPI) {
          fileUploadXhr(a);
      }
      else {
          $.ajax(options);
      }
  
      // clear element array
      for (var k=0; k < elements.length; k++)
          elements[k] = null;
  
      // fire 'notify' event
      this.trigger('form-submit-notify', [this, options]);
      return this;
  
       // XMLHttpRequest Level 2 file uploads (big hat tip to francois2metz)
      function fileUploadXhr(a) {
          var formdata = new FormData();
  
          for (var i=0; i < a.length; i++) {
              formdata.append(a[i].name, a[i].value);
          }
  
          if (options.extraData) {
              for (var p in options.extraData)
                  if (options.extraData.hasOwnProperty(p))
                      formdata.append(p, options.extraData[p]);
          }
  
          options.data = null;
  
          var s = $.extend(true, {}, $.ajaxSettings, options, {
              contentType: false,
              processData: false,
              cache: false,
              type: 'POST'
          });
          
          if (options.uploadProgress) {
              // workaround because jqXHR does not expose upload property
              s.xhr = function() {
                  var xhr = jQuery.ajaxSettings.xhr();
                  if (xhr.upload) {
                      xhr.upload.onprogress = function(event) {
                          var percent = 0;
                          var position = event.loaded || event.position; /*event.position is deprecated*/
                          var total = event.total;
                          if (event.lengthComputable) {
                              percent = Math.ceil(position / total * 100);
                          }
                          options.uploadProgress(event, position, total, percent);
                      };
                  }
                  return xhr;
              };
          }
  
          s.data = null;
            var beforeSend = s.beforeSend;
            s.beforeSend = function(xhr, o) {
                o.data = formdata;
              if(beforeSend)
                  beforeSend.call(o, xhr, options);
          };
          $.ajax(s);
      }
  
      // private function for handling file uploads (hat tip to YAHOO!)
      function fileUploadIframe(a) {
          var form = $form[0], el, i, s, g, id, $io, io, xhr, sub, n, timedOut, timeoutHandle;
          var useProp = !!$.fn.prop;
  
          if ($(':input[name=submit],:input[id=submit]', form).length) {
              // if there is an input with a name or id of 'submit' then we won't be
              // able to invoke the submit fn on the form (at least not x-browser)
              alert('Error: Form elements must not have name or id of "submit".');
              return;
          }
          
          if (a) {
              // ensure that every serialized input is still enabled
              for (i=0; i < elements.length; i++) {
                  el = $(elements[i]);
                  if ( useProp )
                      el.prop('disabled', false);
                  else
                      el.removeAttr('disabled');
              }
          }
  
          s = $.extend(true, {}, $.ajaxSettings, options);
          s.context = s.context || s;
          id = 'jqFormIO' + (new Date().getTime());
          if (s.iframeTarget) {
              $io = $(s.iframeTarget);
              n = $io.attr('name');
              if (!n)
                   $io.attr('name', id);
              else
                  id = n;
          }
          else {
              $io = $('<iframe name="' + id + '" src="'+ s.iframeSrc +'" />');
              $io.css({ position: 'absolute', top: '-1000px', left: '-1000px' });
          }
          io = $io[0];
  
  
          xhr = { // mock object
              aborted: 0,
              responseText: null,
              responseXML: null,
              status: 0,
              statusText: 'n/a',
              getAllResponseHeaders: function() {},
              getResponseHeader: function() {},
              setRequestHeader: function() {},
              abort: function(status) {
                  var e = (status === 'timeout' ? 'timeout' : 'aborted');
                  log('aborting upload... ' + e);
                  this.aborted = 1;
                  $io.attr('src', s.iframeSrc); // abort op in progress
                  xhr.error = e;
                  if (s.error)
                      s.error.call(s.context, xhr, e, status);
                  if (g)
                      $.event.trigger("ajaxError", [xhr, s, e]);
                  if (s.complete)
                      s.complete.call(s.context, xhr, e);
              }
          };
  
          g = s.global;
          // trigger ajax global events so that activity/block indicators work like normal
          if (g && 0 === $.active++) {
              $.event.trigger("ajaxStart");
          }
          if (g) {
              $.event.trigger("ajaxSend", [xhr, s]);
          }
  
          if (s.beforeSend && s.beforeSend.call(s.context, xhr, s) === false) {
              if (s.global) {
                  $.active--;
              }
              return;
          }
          if (xhr.aborted) {
              return;
          }
  
          // add submitting element to data if we know it
          sub = form.clk;
          if (sub) {
              n = sub.name;
              if (n && !sub.disabled) {
                  s.extraData = s.extraData || {};
                  s.extraData[n] = sub.value;
                  if (sub.type == "image") {
                      s.extraData[n+'.x'] = form.clk_x;
                      s.extraData[n+'.y'] = form.clk_y;
                  }
              }
          }
          
          var CLIENT_TIMEOUT_ABORT = 1;
          var SERVER_ABORT = 2;
  
          function getDoc(frame) {
              var doc = frame.contentWindow ? frame.contentWindow.document : frame.contentDocument ? frame.contentDocument : frame.document;
              return doc;
          }
          
          // Rails CSRF hack (thanks to Yvan Barthelemy)
          var csrf_token = $('meta[name=csrf-token]').attr('content');
          var csrf_param = $('meta[name=csrf-param]').attr('content');
          if (csrf_param && csrf_token) {
              s.extraData = s.extraData || {};
              s.extraData[csrf_param] = csrf_token;
          }
  
          // take a breath so that pending repaints get some cpu time before the upload starts
          function doSubmit() {
              // make sure form attrs are set
              var t = $form.attr('target'), a = $form.attr('action');
  
              // update form attrs in IE friendly way
              form.setAttribute('target',id);
              if (!method) {
                  form.setAttribute('method', 'POST');
              }
              if (a != s.url) {
                  form.setAttribute('action', s.url);
              }
  
              // ie borks in some cases when setting encoding
              if (! s.skipEncodingOverride && (!method || /post/i.test(method))) {
                  $form.attr({
                      encoding: 'multipart/form-data',
                      enctype:  'multipart/form-data'
                  });
              }
  
              // support timout
              if (s.timeout) {
                  timeoutHandle = setTimeout(function() { timedOut = true; cb(CLIENT_TIMEOUT_ABORT); }, s.timeout);
              }
              
              // look for server aborts
              function checkState() {
                  try {
                      var state = getDoc(io).readyState;
                      log('state = ' + state);
                      if (state && state.toLowerCase() == 'uninitialized')
                          setTimeout(checkState,50);
                  }
                  catch(e) {
                      log('Server abort: ' , e, ' (', e.name, ')');
                      cb(SERVER_ABORT);
                      if (timeoutHandle)
                          clearTimeout(timeoutHandle);
                      timeoutHandle = undefined;
                  }
              }
  
              // add "extra" data to form if provided in options
              var extraInputs = [];
              try {
                  if (s.extraData) {
                      for (var n in s.extraData) {
                          if (s.extraData.hasOwnProperty(n)) {
                              extraInputs.push(
                                  $('<input type="hidden" name="'+n+'">').attr('value',s.extraData[n])
                                      .appendTo(form)[0]);
                          }
                      }
                  }
  
                  if (!s.iframeTarget) {
                      // add iframe to doc and submit the form
                      $io.appendTo('body');
                      if (io.attachEvent)
                          io.attachEvent('onload', cb);
                      else
                          io.addEventListener('load', cb, false);
                  }
                  setTimeout(checkState,15);
                  form.submit();
              }
              finally {
                  // reset attrs and remove "extra" input elements
                  form.setAttribute('action',a);
                  if(t) {
                      form.setAttribute('target', t);
                  } else {
                      $form.removeAttr('target');
                  }
                  $(extraInputs).remove();
              }
          }
  
          if (s.forceSync) {
              doSubmit();
          }
          else {
              setTimeout(doSubmit, 10); // this lets dom updates render
          }
  
          var data, doc, domCheckCount = 50, callbackProcessed;
  
          function cb(e) {
              if (xhr.aborted || callbackProcessed) {
                  return;
              }
              try {
                  doc = getDoc(io);
              }
              catch(ex) {
                  log('cannot access response document: ', ex);
                  e = SERVER_ABORT;
              }
              if (e === CLIENT_TIMEOUT_ABORT && xhr) {
                  xhr.abort('timeout');
                  return;
              }
              else if (e == SERVER_ABORT && xhr) {
                  xhr.abort('server abort');
                  return;
              }
  
              if (!doc || doc.location.href == s.iframeSrc) {
                  // response not received yet
                  if (!timedOut)
                      return;
              }
              if (io.detachEvent)
                  io.detachEvent('onload', cb);
              else    
                  io.removeEventListener('load', cb, false);
  
              var status = 'success', errMsg;
              try {
                  if (timedOut) {
                      throw 'timeout';
                  }
  
                  var isXml = s.dataType == 'xml' || doc.XMLDocument || $.isXMLDoc(doc);
                  log('isXml='+isXml);
                  if (!isXml && window.opera && (doc.body === null || !doc.body.innerHTML)) {
                      if (--domCheckCount) {
                          // in some browsers (Opera) the iframe DOM is not always traversable when
                          // the onload callback fires, so we loop a bit to accommodate
                          log('requeing onLoad callback, DOM not available');
                          setTimeout(cb, 250);
                          return;
                      }
                      // let this fall through because server response could be an empty document
                      //log('Could not access iframe DOM after mutiple tries.');
                      //throw 'DOMException: not available';
                  }
  
                  //log('response detected');
                  var docRoot = doc.body ? doc.body : doc.documentElement;
                  xhr.responseText = docRoot ? docRoot.innerHTML : null;
                  xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc;
                  if (isXml)
                      s.dataType = 'xml';
                  xhr.getResponseHeader = function(header){
                      var headers = {'content-type': s.dataType};
                      return headers[header];
                  };
                  // support for XHR 'status' & 'statusText' emulation :
                  if (docRoot) {
                      xhr.status = Number( docRoot.getAttribute('status') ) || xhr.status;
                      xhr.statusText = docRoot.getAttribute('statusText') || xhr.statusText;
                  }
  
                  var dt = (s.dataType || '').toLowerCase();
                  var scr = /(json|script|text)/.test(dt);
                  if (scr || s.textarea) {
                      // see if user embedded response in textarea
                      var ta = doc.getElementsByTagName('textarea')[0];
                      if (ta) {
                          xhr.responseText = ta.value;
                          // support for XHR 'status' & 'statusText' emulation :
                          xhr.status = Number( ta.getAttribute('status') ) || xhr.status;
                          xhr.statusText = ta.getAttribute('statusText') || xhr.statusText;
                      }
                      else if (scr) {
                          // account for browsers injecting pre around json response
                          var pre = doc.getElementsByTagName('pre')[0];
                          var b = doc.getElementsByTagName('body')[0];
                          if (pre) {
                              xhr.responseText = pre.textContent ? pre.textContent : pre.innerText;
                          }
                          else if (b) {
                              xhr.responseText = b.textContent ? b.textContent : b.innerText;
                          }
                      }
                  }
                  else if (dt == 'xml' && !xhr.responseXML && xhr.responseText) {
                      xhr.responseXML = toXml(xhr.responseText);
                  }
  
                  try {
                      data = httpData(xhr, dt, s);
                  }
                  catch (e) {
                      status = 'parsererror';
                      xhr.error = errMsg = (e || status);
                  }
              }
              catch (e) {
                  log('error caught: ',e);
                  status = 'error';
                  xhr.error = errMsg = (e || status);
              }
  
              if (xhr.aborted) {
                  log('upload aborted');
                  status = null;
              }
  
              if (xhr.status) { // we've set xhr.status
                  status = (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) ? 'success' : 'error';
              }
  
              // ordering of these callbacks/triggers is odd, but that's how $.ajax does it
              if (status === 'success') {
                  if (s.success)
                      s.success.call(s.context, data, 'success', xhr);
                  if (g)
                      $.event.trigger("ajaxSuccess", [xhr, s]);
              }
              else if (status) {
                  if (errMsg === undefined)
                      errMsg = xhr.statusText;
                  if (s.error)
                      s.error.call(s.context, xhr, status, errMsg);
                  if (g)
                      $.event.trigger("ajaxError", [xhr, s, errMsg]);
              }
  
              if (g)
                  $.event.trigger("ajaxComplete", [xhr, s]);
  
              if (g && ! --$.active) {
                  $.event.trigger("ajaxStop");
              }
  
              if (s.complete)
                  s.complete.call(s.context, xhr, status);
  
              callbackProcessed = true;
              if (s.timeout)
                  clearTimeout(timeoutHandle);
  
              // clean up
              setTimeout(function() {
                  if (!s.iframeTarget)
                      $io.remove();
                  xhr.responseXML = null;
              }, 100);
          }
  
          var toXml = $.parseXML || function(s, doc) { // use parseXML if available (jQuery 1.5+)
              if (window.ActiveXObject) {
                  doc = new ActiveXObject('Microsoft.XMLDOM');
                  doc.async = 'false';
                  doc.loadXML(s);
              }
              else {
                  doc = (new DOMParser()).parseFromString(s, 'text/xml');
              }
              return (doc && doc.documentElement && doc.documentElement.nodeName != 'parsererror') ? doc : null;
          };
          var parseJSON = $.parseJSON || function(s) {
              /*jslint evil:true */
              return window['eval']('(' + s + ')');
          };
  
          var httpData = function( xhr, type, s ) { // mostly lifted from jq1.4.4
  
              var ct = xhr.getResponseHeader('content-type') || '',
                  xml = type === 'xml' || !type && ct.indexOf('xml') >= 0,
                  data = xml ? xhr.responseXML : xhr.responseText;
  
              if (xml && data.documentElement.nodeName === 'parsererror') {
                  if ($.error)
                      $.error('parsererror');
              }
              if (s && s.dataFilter) {
                  data = s.dataFilter(data, type);
              }
              if (typeof data === 'string') {
                  if (type === 'json' || !type && ct.indexOf('json') >= 0) {
                      data = parseJSON(data);
                  } else if (type === "script" || !type && ct.indexOf("javascript") >= 0) {
                      $.globalEval(data);
                  }
              }
              return data;
          };
      }
  };
  
  /**
   * ajaxForm() provides a mechanism for fully automating form submission.
   *
   * The advantages of using this method instead of ajaxSubmit() are:
   *
   * 1: This method will include coordinates for <input type="image" /> elements (if the element
   *    is used to submit the form).
   * 2. This method will include the submit element's name/value data (for the element that was
   *    used to submit the form).
   * 3. This method binds the submit() method to the form for you.
   *
   * The options argument for ajaxForm works exactly as it does for ajaxSubmit.  ajaxForm merely
   * passes the options argument along after properly binding events for submit elements and
   * the form itself.
   */
  $.fn.ajaxForm = function(options) {
      options = options || {};
      options.delegation = options.delegation && $.isFunction($.fn.on);
      
      // in jQuery 1.3+ we can fix mistakes with the ready state
      if (!options.delegation && this.length === 0) {
          var o = { s: this.selector, c: this.context };
          if (!$.isReady && o.s) {
              log('DOM not ready, queuing ajaxForm');
              $(function() {
                  $(o.s,o.c).ajaxForm(options);
              });
              return this;
          }
          // is your DOM ready?  http://docs.jquery.com/Tutorials:Introducing_$(document).ready()
          log('terminating; zero elements found by selector' + ($.isReady ? '' : ' (DOM not ready)'));
          return this;
      }
  
      if ( options.delegation ) {
          $(document)
              .off('submit.form-plugin', this.selector, doAjaxSubmit)
              .off('click.form-plugin', this.selector, captureSubmittingElement)
              .on('submit.form-plugin', this.selector, options, doAjaxSubmit)
              .on('click.form-plugin', this.selector, options, captureSubmittingElement);
          return this;
      }
  
      return this.ajaxFormUnbind()
          .bind('submit.form-plugin', options, doAjaxSubmit)
          .bind('click.form-plugin', options, captureSubmittingElement);
  };
  
  // private event handlers    
  function doAjaxSubmit(e) {
      /*jshint validthis:true */
      var options = e.data;
      if (!e.isDefaultPrevented()) { // if event has been canceled, don't proceed
          e.preventDefault();
          $(this).ajaxSubmit(options);
      }
  }
      
  function captureSubmittingElement(e) {
      /*jshint validthis:true */
      var target = e.target;
      var $el = $(target);
      if (!($el.is(":submit,input:image"))) {
          // is this a child element of the submit el?  (ex: a span within a button)
          var t = $el.closest(':submit');
          if (t.length === 0) {
              return;
          }
          target = t[0];
      }
      var form = this;
      form.clk = target;
      if (target.type == 'image') {
          if (e.offsetX !== undefined) {
              form.clk_x = e.offsetX;
              form.clk_y = e.offsetY;
          } else if (typeof $.fn.offset == 'function') {
              var offset = $el.offset();
              form.clk_x = e.pageX - offset.left;
              form.clk_y = e.pageY - offset.top;
          } else {
              form.clk_x = e.pageX - target.offsetLeft;
              form.clk_y = e.pageY - target.offsetTop;
          }
      }
      // clear form vars
      setTimeout(function() { form.clk = form.clk_x = form.clk_y = null; }, 100);
  }
  
  
  // ajaxFormUnbind unbinds the event handlers that were bound by ajaxForm
  $.fn.ajaxFormUnbind = function() {
      return this.unbind('submit.form-plugin click.form-plugin');
  };
  
  /**
   * formToArray() gathers form element data into an array of objects that can
   * be passed to any of the following ajax functions: $.get, $.post, or load.
   * Each object in the array has both a 'name' and 'value' property.  An example of
   * an array for a simple login form might be:
   *
   * [ { name: 'username', value: 'jresig' }, { name: 'password', value: 'secret' } ]
   *
   * It is this array that is passed to pre-submit callback functions provided to the
   * ajaxSubmit() and ajaxForm() methods.
   */
  $.fn.formToArray = function(semantic, elements) {
      var a = [];
      if (this.length === 0) {
          return a;
      }
  
      var form = this[0];
      var els = semantic ? form.getElementsByTagName('*') : form.elements;
      if (!els) {
          return a;
      }
  
      var i,j,n,v,el,max,jmax;
      for(i=0, max=els.length; i < max; i++) {
          el = els[i];
          n = el.name;
          if (!n) {
              continue;
          }
  
          if (semantic && form.clk && el.type == "image") {
              // handle image inputs on the fly when semantic == true
              if(!el.disabled && form.clk == el) {
                  a.push({name: n, value: $(el).val(), type: el.type });
                  a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
              }
              continue;
          }
  
          v = $.fieldValue(el, true);
          if (v && v.constructor == Array) {
              if (elements) 
                  elements.push(el);
              for(j=0, jmax=v.length; j < jmax; j++) {
                  a.push({name: n, value: v[j]});
              }
          }
          else if (feature.fileapi && el.type == 'file' && !el.disabled) {
              if (elements) 
                  elements.push(el);
              var files = el.files;
              if (files.length) {
                  for (j=0; j < files.length; j++) {
                      a.push({name: n, value: files[j], type: el.type});
                  }
              }
              else {
                  // #180
                  a.push({ name: n, value: '', type: el.type });
              }
          }
          else if (v !== null && typeof v != 'undefined') {
              if (elements) 
                  elements.push(el);
              a.push({name: n, value: v, type: el.type, required: el.required});
          }
      }
  
      if (!semantic && form.clk) {
          // input type=='image' are not found in elements array! handle it here
          var $input = $(form.clk), input = $input[0];
          n = input.name;
          if (n && !input.disabled && input.type == 'image') {
              a.push({name: n, value: $input.val()});
              a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
          }
      }
      return a;
  };
  
  /**
   * Serializes form data into a 'submittable' string. This method will return a string
   * in the format: name1=value1&amp;name2=value2
   */
  $.fn.formSerialize = function(semantic) {
      //hand off to jQuery.param for proper encoding
      return $.param(this.formToArray(semantic));
  };
  
  /**
   * Serializes all field elements in the jQuery object into a query string.
   * This method will return a string in the format: name1=value1&amp;name2=value2
   */
  $.fn.fieldSerialize = function(successful) {
      var a = [];
      this.each(function() {
          var n = this.name;
          if (!n) {
              return;
          }
          var v = $.fieldValue(this, successful);
          if (v && v.constructor == Array) {
              for (var i=0,max=v.length; i < max; i++) {
                  a.push({name: n, value: v[i]});
              }
          }
          else if (v !== null && typeof v != 'undefined') {
              a.push({name: this.name, value: v});
          }
      });
      //hand off to jQuery.param for proper encoding
      return $.param(a);
  };
  
  /**
   * Returns the value(s) of the element in the matched set.  For example, consider the following form:
   *
   *  <form><fieldset>
   *      <input name="A" type="text" />
   *      <input name="A" type="text" />
   *      <input name="B" type="checkbox" value="B1" />
   *      <input name="B" type="checkbox" value="B2"/>
   *      <input name="C" type="radio" value="C1" />
   *      <input name="C" type="radio" value="C2" />
   *  </fieldset></form>
   *
   *  var v = $(':text').fieldValue();
   *  // if no values are entered into the text inputs
   *  v == ['','']
   *  // if values entered into the text inputs are 'foo' and 'bar'
   *  v == ['foo','bar']
   *
   *  var v = $(':checkbox').fieldValue();
   *  // if neither checkbox is checked
   *  v === undefined
   *  // if both checkboxes are checked
   *  v == ['B1', 'B2']
   *
   *  var v = $(':radio').fieldValue();
   *  // if neither radio is checked
   *  v === undefined
   *  // if first radio is checked
   *  v == ['C1']
   *
   * The successful argument controls whether or not the field element must be 'successful'
   * (per http://www.w3.org/TR/html4/interact/forms.html#successful-controls).
   * The default value of the successful argument is true.  If this value is false the value(s)
   * for each element is returned.
   *
   * Note: This method *always* returns an array.  If no valid value can be determined the
   *    array will be empty, otherwise it will contain one or more values.
   */
  $.fn.fieldValue = function(successful) {
      for (var val=[], i=0, max=this.length; i < max; i++) {
          var el = this[i];
          var v = $.fieldValue(el, successful);
          if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length)) {
              continue;
          }
          if (v.constructor == Array)
              $.merge(val, v);
          else
              val.push(v);
      }
      return val;
  };
  
  /**
   * Returns the value of the field element.
   */
  $.fieldValue = function(el, successful) {
      var n = el.name, t = el.type, tag = el.tagName.toLowerCase();
      if (successful === undefined) {
          successful = true;
      }
  
      if (successful && (!n || el.disabled || t == 'reset' || t == 'button' ||
          (t == 'checkbox' || t == 'radio') && !el.checked ||
          (t == 'submit' || t == 'image') && el.form && el.form.clk != el ||
          tag == 'select' && el.selectedIndex == -1)) {
              return null;
      }
  
      if (tag == 'select') {
          var index = el.selectedIndex;
          if (index < 0) {
              return null;
          }
          var a = [], ops = el.options;
          var one = (t == 'select-one');
          var max = (one ? index+1 : ops.length);
          for(var i=(one ? index : 0); i < max; i++) {
              var op = ops[i];
              if (op.selected) {
                  var v = op.value;
                  if (!v) { // extra pain for IE...
                      v = (op.attributes && op.attributes['value'] && !(op.attributes['value'].specified)) ? op.text : op.value;
                  }
                  if (one) {
                      return v;
                  }
                  a.push(v);
              }
          }
          return a;
      }
      return $(el).val();
  };
  
  /**
   * Clears the form data.  Takes the following actions on the form's input fields:
   *  - input text fields will have their 'value' property set to the empty string
   *  - select elements will have their 'selectedIndex' property set to -1
   *  - checkbox and radio inputs will have their 'checked' property set to false
   *  - inputs of type submit, button, reset, and hidden will *not* be effected
   *  - button elements will *not* be effected
   */
  $.fn.clearForm = function(includeHidden) {
      return this.each(function() {
          $('input,select,textarea', this).clearFields(includeHidden);
      });
  };
  
  /**
   * Clears the selected form elements.
   */
  $.fn.clearFields = $.fn.clearInputs = function(includeHidden) {
      var re = /^(?:color|date|datetime|email|month|number|password|range|search|tel|text|time|url|week)$/i; // 'hidden' is not in this list
      return this.each(function() {
          var t = this.type, tag = this.tagName.toLowerCase();
          if (re.test(t) || tag == 'textarea') {
              this.value = '';
          }
          else if (t == 'checkbox' || t == 'radio') {
              this.checked = false;
          }
          else if (tag == 'select') {
              this.selectedIndex = -1;
          }
          else if (includeHidden) {
              // includeHidden can be the valud true, or it can be a selector string
              // indicating a special test; for example:
              //  $('#myForm').clearForm('.special:hidden')
              // the above would clean hidden inputs that have the class of 'special'
              if ( (includeHidden === true && /hidden/.test(t)) ||
                   (typeof includeHidden == 'string' && $(this).is(includeHidden)) )
                  this.value = '';
          }
      });
  };
  
  /**
   * Resets the form data.  Causes all form elements to be reset to their original value.
   */
  $.fn.resetForm = function() {
      return this.each(function() {
          // guard against an input with the name of 'reset'
          // note that IE reports the reset function as an 'object'
          if (typeof this.reset == 'function' || (typeof this.reset == 'object' && !this.reset.nodeType)) {
              this.reset();
          }
      });
  };
  
  /**
   * Enables or disables any matching elements.
   */
  $.fn.enable = function(b) {
      if (b === undefined) {
          b = true;
      }
      return this.each(function() {
          this.disabled = !b;
      });
  };
  
  /**
   * Checks/unchecks any matching checkboxes or radio buttons and
   * selects/deselects and matching option elements.
   */
  $.fn.selected = function(select) {
      if (select === undefined) {
          select = true;
      }
      return this.each(function() {
          var t = this.type;
          if (t == 'checkbox' || t == 'radio') {
              this.checked = select;
          }
          else if (this.tagName.toLowerCase() == 'option') {
              var $sel = $(this).parent('select');
              if (select && $sel[0] && $sel[0].type == 'select-one') {
                  // deselect all other options
                  $sel.find('option').selected(false);
              }
              this.selected = select;
          }
      });
  };
  
  // expose debug var
  $.fn.ajaxSubmit.debug = false;
  
  // helper fn for console logging
  function log() {
      if (!$.fn.ajaxSubmit.debug) 
          return;
      var msg = '[jquery.form] ' + Array.prototype.join.call(arguments,'');
      if (window.console && window.console.log) {
          window.console.log(msg);
      }
      else if (window.opera && window.opera.postError) {
          window.opera.postError(msg);
      }
  }
  
  })(jQuery);
  /**
   * jQuery Validation Plugin 1.9.0
   *
   * http://bassistance.de/jquery-plugins/jquery-plugin-validation/
   * http://docs.jquery.com/Plugins/Validation
   *
   * Copyright (c) 2006 - 2011 Jörn Zaefferer
   *
   * Dual licensed under the MIT and GPL licenses:
   *   http://www.opensource.org/licenses/mit-license.php
   *   http://www.gnu.org/licenses/gpl.html
   */
  
  (function($) {
  
  $.extend($.fn, {
  	// http://docs.jquery.com/Plugins/Validation/validate
  	validate: function( options ) {
  
  		// if nothing is selected, return nothing; can't chain anyway
  		if (!this.length) {
  			options && options.debug && window.console && console.warn( "nothing selected, can't validate, returning nothing" );
  			return;
  		}
  
  		// check if a validator for this form was already created
  		var validator = $.data(this[0], 'validator');
  		if ( validator ) {
  			return validator;
  		}
  
  		// Add novalidate tag if HTML5.
  		this.attr('novalidate', 'novalidate');
  
  		validator = new $.validator( options, this[0] );
  		$.data(this[0], 'validator', validator);
  
  		if ( validator.settings.onsubmit ) {
  
  			var inputsAndButtons = this.find("input, button");
  
  			// allow suppresing validation by adding a cancel class to the submit button
  			inputsAndButtons.filter(".cancel").click(function () {
  				validator.cancelSubmit = true;
  			});
  
  			// when a submitHandler is used, capture the submitting button
  			if (validator.settings.submitHandler) {
  				inputsAndButtons.filter(":submit").click(function () {
  					validator.submitButton = this;
  				});
  			}
  
  			// validate the form on submit
  			this.submit( function( event ) {
  				if ( validator.settings.debug )
  					// prevent form submit to be able to see console output
  					event.preventDefault();
  
  				function handle() {
  					if ( validator.settings.submitHandler ) {
  						if (validator.submitButton) {
  							// insert a hidden input as a replacement for the missing submit button
  							var hidden = $("<input type='hidden'/>").attr("name", validator.submitButton.name).val(validator.submitButton.value).appendTo(validator.currentForm);
  						}
  						validator.settings.submitHandler.call( validator, validator.currentForm );
  						if (validator.submitButton) {
  							// and clean up afterwards; thanks to no-block-scope, hidden can be referenced
  							hidden.remove();
  						}
  						return false;
  					}
  					return true;
  				}
  
  				// prevent submit for invalid forms or custom submit handlers
  				if ( validator.cancelSubmit ) {
  					validator.cancelSubmit = false;
  					return handle();
  				}
  				if ( validator.form() ) {
  					if ( validator.pendingRequest ) {
  						validator.formSubmitted = true;
  						return false;
  					}
  					return handle();
  				} else {
  					validator.focusInvalid();
  					return false;
  				}
  			});
  		}
  
  		return validator;
  	},
  	// http://docs.jquery.com/Plugins/Validation/valid
  	valid: function() {
          if ( $(this[0]).is('form')) {
              return this.validate().form();
          } else {
              var valid = true;
              var validator = $(this[0].form).validate();
              this.each(function() {
  				valid &= validator.element(this);
              });
              return valid;
          }
      },
  	// attributes: space seperated list of attributes to retrieve and remove
  	removeAttrs: function(attributes) {
  		var result = {},
  			$element = this;
  		$.each(attributes.split(/\s/), function(index, value) {
  			result[value] = $element.attr(value);
  			$element.removeAttr(value);
  		});
  		return result;
  	},
  	// http://docs.jquery.com/Plugins/Validation/rules
  	rules: function(command, argument) {
  		var element = this[0];
  
  		if (command) {
  			var settings = $.data(element.form, 'validator').settings;
  			var staticRules = settings.rules;
  			var existingRules = $.validator.staticRules(element);
  			switch(command) {
  			case "add":
  				$.extend(existingRules, $.validator.normalizeRule(argument));
  				staticRules[element.name] = existingRules;
  				if (argument.messages)
  					settings.messages[element.name] = $.extend( settings.messages[element.name], argument.messages );
  				break;
  			case "remove":
  				if (!argument) {
  					delete staticRules[element.name];
  					return existingRules;
  				}
  				var filtered = {};
  				$.each(argument.split(/\s/), function(index, method) {
  					filtered[method] = existingRules[method];
  					delete existingRules[method];
  				});
  				return filtered;
  			}
  		}
  
  		var data = $.validator.normalizeRules(
  		$.extend(
  			{},
  			$.validator.metadataRules(element),
  			$.validator.classRules(element),
  			$.validator.attributeRules(element),
  			$.validator.staticRules(element)
  		), element);
  
  		// make sure required is at front
  		if (data.required) {
  			var param = data.required;
  			delete data.required;
  			data = $.extend({required: param}, data);
  		}
  
  		return data;
  	}
  });
  
  // Custom selectors
  $.extend($.expr[":"], {
  	// http://docs.jquery.com/Plugins/Validation/blank
  	blank: function(a) {return !$.trim("" + a.value);},
  	// http://docs.jquery.com/Plugins/Validation/filled
  	filled: function(a) {return !!$.trim("" + a.value);},
  	// http://docs.jquery.com/Plugins/Validation/unchecked
  	unchecked: function(a) {return !a.checked;}
  });
  
  // constructor for validator
  $.validator = function( options, form ) {
  	this.settings = $.extend( true, {}, $.validator.defaults, options );
  	this.currentForm = form;
  	this.init();
  };
  
  $.validator.format = function(source, params) {
  	if ( arguments.length == 1 )
  		return function() {
  			var args = $.makeArray(arguments);
  			args.unshift(source);
  			return $.validator.format.apply( this, args );
  		};
  	if ( arguments.length > 2 && params.constructor != Array  ) {
  		params = $.makeArray(arguments).slice(1);
  	}
  	if ( params.constructor != Array ) {
  		params = [ params ];
  	}
  	$.each(params, function(i, n) {
  		source = source.replace(new RegExp("\\{" + i + "\\}", "g"), n);
  	});
  	return source;
  };
  
  $.extend($.validator, {
  
  	defaults: {
  		messages: {},
  		groups: {},
  		rules: {},
  		errorClass: "error",
  		validClass: "valid",
  		errorElement: "label",
  		focusInvalid: true,
  		errorContainer: $( [] ),
  		errorLabelContainer: $( [] ),
  		onsubmit: true,
  		ignore: ":hidden",
  		ignoreTitle: false,
  		onfocusin: function(element, event) {
  			this.lastActive = element;
  
  			// hide error label and remove error class on focus if enabled
  			if ( this.settings.focusCleanup && !this.blockFocusCleanup ) {
  				this.settings.unhighlight && this.settings.unhighlight.call( this, element, this.settings.errorClass, this.settings.validClass );
  				this.addWrapper(this.errorsFor(element)).hide();
  			}
  		},
  		onfocusout: function(element, event) {
  			if ( !this.checkable(element) && (element.name in this.submitted || !this.optional(element)) ) {
  				this.element(element);
  			}
  		},
  		onkeyup: function(element, event) {
  			if ( element.name in this.submitted || element == this.lastElement ) {
  				this.element(element);
  			}
  		},
  		onclick: function(element, event) {
  			// click on selects, radiobuttons and checkboxes
  			if ( element.name in this.submitted )
  				this.element(element);
  			// or option elements, check parent select in that case
  			else if (element.parentNode.name in this.submitted)
  				this.element(element.parentNode);
  		},
  		highlight: function(element, errorClass, validClass) {
  			if (element.type === 'radio') {
  				this.findByName(element.name).addClass(errorClass).removeClass(validClass);
  			} else {
  				$(element).addClass(errorClass).removeClass(validClass);
  			}
  		},
  		unhighlight: function(element, errorClass, validClass) {
  			if (element.type === 'radio') {
  				this.findByName(element.name).removeClass(errorClass).addClass(validClass);
  			} else {
  				$(element).removeClass(errorClass).addClass(validClass);
  			}
  		}
  	},
  
  	// http://docs.jquery.com/Plugins/Validation/Validator/setDefaults
  	setDefaults: function(settings) {
  		$.extend( $.validator.defaults, settings );
  	},
  
  	messages: {
  		required: "This field is required.",
  		remote: "Please fix this field.",
  		email: "Please enter a valid email address.",
  		url: "Please enter a valid URL.",
  		date: "Please enter a valid date.",
  		dateISO: "Please enter a valid date (ISO).",
  		number: "Please enter a valid number.",
  		digits: "Please enter only digits.",
  		creditcard: "Please enter a valid credit card number.",
  		equalTo: "Please enter the same value again.",
  		accept: "Please enter a value with a valid extension.",
  		maxlength: $.validator.format("Please enter no more than {0} characters."),
  		minlength: $.validator.format("Please enter at least {0} characters."),
  		rangelength: $.validator.format("Please enter a value between {0} and {1} characters long."),
  		range: $.validator.format("Please enter a value between {0} and {1}."),
  		max: $.validator.format("Please enter a value less than or equal to {0}."),
  		min: $.validator.format("Please enter a value greater than or equal to {0}.")
  	},
  
  	autoCreateRanges: false,
  
  	prototype: {
  
  		init: function() {
  			this.labelContainer = $(this.settings.errorLabelContainer);
  			this.errorContext = this.labelContainer.length && this.labelContainer || $(this.currentForm);
  			this.containers = $(this.settings.errorContainer).add( this.settings.errorLabelContainer );
  			this.submitted = {};
  			this.valueCache = {};
  			this.pendingRequest = 0;
  			this.pending = {};
  			this.invalid = {};
  			this.reset();
  
  			var groups = (this.groups = {});
  			$.each(this.settings.groups, function(key, value) {
  				$.each(value.split(/\s/), function(index, name) {
  					groups[name] = key;
  				});
  			});
  			var rules = this.settings.rules;
  			$.each(rules, function(key, value) {
  				rules[key] = $.validator.normalizeRule(value);
  			});
  
  			function delegate(event) {
  				var validator = $.data(this[0].form, "validator"),
  					eventType = "on" + event.type.replace(/^validate/, "");
  				validator.settings[eventType] && validator.settings[eventType].call(validator, this[0], event);
  			}
  			$(this.currentForm)
  			       .validateDelegate("[type='text'], [type='password'], [type='file'], select, textarea, " +
  						"[type='number'], [type='search'] ,[type='tel'], [type='url'], " +
  						"[type='email'], [type='datetime'], [type='date'], [type='month'], " +
  						"[type='week'], [type='time'], [type='datetime-local'], " +
  						"[type='range'], [type='color'] ",
  						"focusin focusout keyup", delegate)
  				.validateDelegate("[type='radio'], [type='checkbox'], select, option", "click", delegate);
  
  			if (this.settings.invalidHandler)
  				$(this.currentForm).bind("invalid-form.validate", this.settings.invalidHandler);
  		},
  
  		// http://docs.jquery.com/Plugins/Validation/Validator/form
  		form: function() {
  			this.checkForm();
  			$.extend(this.submitted, this.errorMap);
  			this.invalid = $.extend({}, this.errorMap);
  			if (!this.valid())
  				$(this.currentForm).triggerHandler("invalid-form", [this]);
  			this.showErrors();
  			return this.valid();
  		},
  
  		checkForm: function() {
  			this.prepareForm();
  			for ( var i = 0, elements = (this.currentElements = this.elements()); elements[i]; i++ ) {
  				this.check( elements[i] );
  			}
  			return this.valid();
  		},
  
  		// http://docs.jquery.com/Plugins/Validation/Validator/element
  		element: function( element ) {
  			element = this.validationTargetFor( this.clean( element ) );
  			this.lastElement = element;
  			this.prepareElement( element );
  			this.currentElements = $(element);
  			var result = this.check( element );
  			if ( result ) {
  				delete this.invalid[element.name];
  			} else {
  				this.invalid[element.name] = true;
  			}
  			if ( !this.numberOfInvalids() ) {
  				// Hide error containers on last error
  				this.toHide = this.toHide.add( this.containers );
  			}
  			this.showErrors();
  			return result;
  		},
  
  		// http://docs.jquery.com/Plugins/Validation/Validator/showErrors
  		showErrors: function(errors) {
  			if(errors) {
  				// add items to error list and map
  				$.extend( this.errorMap, errors );
  				this.errorList = [];
  				for ( var name in errors ) {
  					this.errorList.push({
  						message: errors[name],
  						element: this.findByName(name)[0]
  					});
  				}
  				// remove items from success list
  				this.successList = $.grep( this.successList, function(element) {
  					return !(element.name in errors);
  				});
  			}
  			this.settings.showErrors
  				? this.settings.showErrors.call( this, this.errorMap, this.errorList )
  				: this.defaultShowErrors();
  		},
  
  		// http://docs.jquery.com/Plugins/Validation/Validator/resetForm
  		resetForm: function() {
  			if ( $.fn.resetForm )
  				$( this.currentForm ).resetForm();
  			this.submitted = {};
  			this.lastElement = null;
  			this.prepareForm();
  			this.hideErrors();
  			this.elements().removeClass( this.settings.errorClass );
  		},
  
  		numberOfInvalids: function() {
  			return this.objectLength(this.invalid);
  		},
  
  		objectLength: function( obj ) {
  			var count = 0;
  			for ( var i in obj )
  				count++;
  			return count;
  		},
  
  		hideErrors: function() {
  			this.addWrapper( this.toHide ).hide();
  		},
  
  		valid: function() {
  			return this.size() == 0;
  		},
  
  		size: function() {
  			return this.errorList.length;
  		},
  
  		focusInvalid: function() {
  			if( this.settings.focusInvalid ) {
  				try {
  					$(this.findLastActive() || this.errorList.length && this.errorList[0].element || [])
  					.filter(":visible")
  					.focus()
  					// manually trigger focusin event; without it, focusin handler isn't called, findLastActive won't have anything to find
  					.trigger("focusin");
  				} catch(e) {
  					// ignore IE throwing errors when focusing hidden elements
  				}
  			}
  		},
  
  		findLastActive: function() {
  			var lastActive = this.lastActive;
  			return lastActive && $.grep(this.errorList, function(n) {
  				return n.element.name == lastActive.name;
  			}).length == 1 && lastActive;
  		},
  
  		elements: function() {
  			var validator = this,
  				rulesCache = {};
  
  			// select all valid inputs inside the form (no submit or reset buttons)
  			return $(this.currentForm)
  			.find("input, select, textarea")
  			.not(":submit, :reset, :image, [disabled]")
  			.not( this.settings.ignore )
  			.filter(function() {
  				!this.name && validator.settings.debug && window.console && console.error( "%o has no name assigned", this);
  
  				// select only the first element for each name, and only those with rules specified
  				if ( this.name in rulesCache || !validator.objectLength($(this).rules()) )
  					return false;
  
  				rulesCache[this.name] = true;
  				return true;
  			});
  		},
  
  		clean: function( selector ) {
  			return $( selector )[0];
  		},
  
  		errors: function() {
  			return $( this.settings.errorElement + "." + this.settings.errorClass, this.errorContext );
  		},
  
  		reset: function() {
  			this.successList = [];
  			this.errorList = [];
  			this.errorMap = {};
  			this.toShow = $([]);
  			this.toHide = $([]);
  			this.currentElements = $([]);
  		},
  
  		prepareForm: function() {
  			this.reset();
  			this.toHide = this.errors().add( this.containers );
  		},
  
  		prepareElement: function( element ) {
  			this.reset();
  			this.toHide = this.errorsFor(element);
  		},
  
  		check: function( element ) {
  			element = this.validationTargetFor( this.clean( element ) );
  
  			var rules = $(element).rules();
  			var dependencyMismatch = false;
  			for (var method in rules ) {
  				var rule = { method: method, parameters: rules[method] };
  				try {
  					var result = $.validator.methods[method].call( this, element.value.replace(/\r/g, ""), element, rule.parameters );
  
  					// if a method indicates that the field is optional and therefore valid,
  					// don't mark it as valid when there are no other rules
  					if ( result == "dependency-mismatch" ) {
  						dependencyMismatch = true;
  						continue;
  					}
  					dependencyMismatch = false;
  
  					if ( result == "pending" ) {
  						this.toHide = this.toHide.not( this.errorsFor(element) );
  						return;
  					}
  
  					if( !result ) {
  						this.formatAndAdd( element, rule );
  						return false;
  					}
  				} catch(e) {
  					this.settings.debug && window.console && console.log("exception occured when checking element " + element.id
  						 + ", check the '" + rule.method + "' method", e);
  					throw e;
  				}
  			}
  			if (dependencyMismatch)
  				return;
  			if ( this.objectLength(rules) )
  				this.successList.push(element);
  			return true;
  		},
  
  		// return the custom message for the given element and validation method
  		// specified in the element's "messages" metadata
  		customMetaMessage: function(element, method) {
  			if (!$.metadata)
  				return;
  
  			var meta = this.settings.meta
  				? $(element).metadata()[this.settings.meta]
  				: $(element).metadata();
  
  			return meta && meta.messages && meta.messages[method];
  		},
  
  		// return the custom message for the given element name and validation method
  		customMessage: function( name, method ) {
  			var m = this.settings.messages[name];
  			return m && (m.constructor == String
  				? m
  				: m[method]);
  		},
  
  		// return the first defined argument, allowing empty strings
  		findDefined: function() {
  			for(var i = 0; i < arguments.length; i++) {
  				if (arguments[i] !== undefined)
  					return arguments[i];
  			}
  			return undefined;
  		},
  
  		defaultMessage: function( element, method) {
  			return this.findDefined(
  				this.customMessage( element.name, method ),
  				this.customMetaMessage( element, method ),
  				// title is never undefined, so handle empty string as undefined
  				!this.settings.ignoreTitle && element.title || undefined,
  				$.validator.messages[method],
  				"<strong>Warning: No message defined for " + element.name + "</strong>"
  			);
  		},
  
  		formatAndAdd: function( element, rule ) {
  			var message = this.defaultMessage( element, rule.method ),
  				theregex = /\$?\{(\d+)\}/g;
  			if ( typeof message == "function" ) {
  				message = message.call(this, rule.parameters, element);
  			} else if (theregex.test(message)) {
  				message = jQuery.format(message.replace(theregex, '{$1}'), rule.parameters);
  			}
  			this.errorList.push({
  				message: message,
  				element: element
  			});
  
  			this.errorMap[element.name] = message;
  			this.submitted[element.name] = message;
  		},
  
  		addWrapper: function(toToggle) {
  			if ( this.settings.wrapper )
  				toToggle = toToggle.add( toToggle.parent( this.settings.wrapper ) );
  			return toToggle;
  		},
  
  		defaultShowErrors: function() {
  			for ( var i = 0; this.errorList[i]; i++ ) {
  				var error = this.errorList[i];
  				this.settings.highlight && this.settings.highlight.call( this, error.element, this.settings.errorClass, this.settings.validClass );
  				this.showLabel( error.element, error.message );
  			}
  			if( this.errorList.length ) {
  				this.toShow = this.toShow.add( this.containers );
  			}
  			if (this.settings.success) {
  				for ( var i = 0; this.successList[i]; i++ ) {
  					this.showLabel( this.successList[i] );
  				}
  			}
  			if (this.settings.unhighlight) {
  				for ( var i = 0, elements = this.validElements(); elements[i]; i++ ) {
  					this.settings.unhighlight.call( this, elements[i], this.settings.errorClass, this.settings.validClass );
  				}
  			}
  			this.toHide = this.toHide.not( this.toShow );
  			this.hideErrors();
  			this.addWrapper( this.toShow ).show();
  		},
  
  		validElements: function() {
  			return this.currentElements.not(this.invalidElements());
  		},
  
  		invalidElements: function() {
  			return $(this.errorList).map(function() {
  				return this.element;
  			});
  		},
  
  		showLabel: function(element, message) {
  			var label = this.errorsFor( element );
  			if ( label.length ) {
  				// refresh error/success class
  				label.removeClass( this.settings.validClass ).addClass( this.settings.errorClass );
  
  				// check if we have a generated label, replace the message then
  				label.attr("generated") && label.html(message);
  			} else {
  				// create label
  				label = $("<" + this.settings.errorElement + "/>")
  					.attr({"for":  this.idOrName(element), generated: true})
  					.addClass(this.settings.errorClass)
  					.html(message || "");
  				if ( this.settings.wrapper ) {
  					// make sure the element is visible, even in IE
  					// actually showing the wrapped element is handled elsewhere
  					label = label.hide().show().wrap("<" + this.settings.wrapper + "/>").parent();
  				}
  				if ( !this.labelContainer.append(label).length )
  					this.settings.errorPlacement
  						? this.settings.errorPlacement(label, $(element) )
  						: label.insertAfter(element);
  			}
  			if ( !message && this.settings.success ) {
  				label.text("");
  				typeof this.settings.success == "string"
  					? label.addClass( this.settings.success )
  					: this.settings.success( label );
  			}
  			this.toShow = this.toShow.add(label);
  		},
  
  		errorsFor: function(element) {
  			var name = this.idOrName(element);
      		return this.errors().filter(function() {
  				return $(this).attr('for') == name;
  			});
  		},
  
  		idOrName: function(element) {
  			return this.groups[element.name] || (this.checkable(element) ? element.name : element.id || element.name);
  		},
  
  		validationTargetFor: function(element) {
  			// if radio/checkbox, validate first element in group instead
  			if (this.checkable(element)) {
  				element = this.findByName( element.name ).not(this.settings.ignore)[0];
  			}
  			return element;
  		},
  
  		checkable: function( element ) {
  			return /radio|checkbox/i.test(element.type);
  		},
  
  		findByName: function( name ) {
  			// select by name and filter by form for performance over form.find("[name=...]")
  			var form = this.currentForm;
  			return $(document.getElementsByName(name)).map(function(index, element) {
  				return element.form == form && element.name == name && element  || null;
  			});
  		},
  
  		getLength: function(value, element) {
  			switch( element.nodeName.toLowerCase() ) {
  			case 'select':
  				return $("option:selected", element).length;
  			case 'input':
  				if( this.checkable( element) )
  					return this.findByName(element.name).filter(':checked').length;
  			}
  			return value.length;
  		},
  
  		depend: function(param, element) {
  			return this.dependTypes[typeof param]
  				? this.dependTypes[typeof param](param, element)
  				: true;
  		},
  
  		dependTypes: {
  			"boolean": function(param, element) {
  				return param;
  			},
  			"string": function(param, element) {
  				return !!$(param, element.form).length;
  			},
  			"function": function(param, element) {
  				return param(element);
  			}
  		},
  
  		optional: function(element) {
  			return !$.validator.methods.required.call(this, $.trim(element.value), element) && "dependency-mismatch";
  		},
  
  		startRequest: function(element) {
  			if (!this.pending[element.name]) {
  				this.pendingRequest++;
  				this.pending[element.name] = true;
  			}
  		},
  
  		stopRequest: function(element, valid) {
  			this.pendingRequest--;
  			// sometimes synchronization fails, make sure pendingRequest is never < 0
  			if (this.pendingRequest < 0)
  				this.pendingRequest = 0;
  			delete this.pending[element.name];
  			if ( valid && this.pendingRequest == 0 && this.formSubmitted && this.form() ) {
  				$(this.currentForm).submit();
  				this.formSubmitted = false;
  			} else if (!valid && this.pendingRequest == 0 && this.formSubmitted) {
  				$(this.currentForm).triggerHandler("invalid-form", [this]);
  				this.formSubmitted = false;
  			}
  		},
  
  		previousValue: function(element) {
  			return $.data(element, "previousValue") || $.data(element, "previousValue", {
  				old: null,
  				valid: true,
  				message: this.defaultMessage( element, "remote" )
  			});
  		}
  
  	},
  
  	classRuleSettings: {
  		required: {required: true},
  		email: {email: true},
  		url: {url: true},
  		date: {date: true},
  		dateISO: {dateISO: true},
  		dateDE: {dateDE: true},
  		number: {number: true},
  		numberDE: {numberDE: true},
  		digits: {digits: true},
  		creditcard: {creditcard: true}
  	},
  
  	addClassRules: function(className, rules) {
  		className.constructor == String ?
  			this.classRuleSettings[className] = rules :
  			$.extend(this.classRuleSettings, className);
  	},
  
  	classRules: function(element) {
  		var rules = {};
  		var classes = $(element).attr('class');
  		classes && $.each(classes.split(' '), function() {
  			if (this in $.validator.classRuleSettings) {
  				$.extend(rules, $.validator.classRuleSettings[this]);
  			}
  		});
  		return rules;
  	},
  
  	attributeRules: function(element) {
  		var rules = {};
  		var $element = $(element);
  
  		for (var method in $.validator.methods) {
  			var value;
  			// If .prop exists (jQuery >= 1.6), use it to get true/false for required
  			if (method === 'required' && typeof $.fn.prop === 'function') {
  				value = $element.prop(method);
  			} else {
  				value = $element.attr(method);
  			}
  			if (value) {
  				rules[method] = value;
  			} else if ($element[0].getAttribute("type") === method) {
  				rules[method] = true;
  			}
  		}
  
  		// maxlength may be returned as -1, 2147483647 (IE) and 524288 (safari) for text inputs
  		if (rules.maxlength && /-1|2147483647|524288/.test(rules.maxlength)) {
  			delete rules.maxlength;
  		}
  
  		return rules;
  	},
  
  	metadataRules: function(element) {
  		if (!$.metadata) return {};
  
  		var meta = $.data(element.form, 'validator').settings.meta;
  		return meta ?
  			$(element).metadata()[meta] :
  			$(element).metadata();
  	},
  
  	staticRules: function(element) {
  		var rules = {};
  		var validator = $.data(element.form, 'validator');
  		if (validator.settings.rules) {
  			rules = $.validator.normalizeRule(validator.settings.rules[element.name]) || {};
  		}
  		return rules;
  	},
  
  	normalizeRules: function(rules, element) {
  		// handle dependency check
  		$.each(rules, function(prop, val) {
  			// ignore rule when param is explicitly false, eg. required:false
  			if (val === false) {
  				delete rules[prop];
  				return;
  			}
  			if (val.param || val.depends) {
  				var keepRule = true;
  				switch (typeof val.depends) {
  					case "string":
  						keepRule = !!$(val.depends, element.form).length;
  						break;
  					case "function":
  						keepRule = val.depends.call(element, element);
  						break;
  				}
  				if (keepRule) {
  					rules[prop] = val.param !== undefined ? val.param : true;
  				} else {
  					delete rules[prop];
  				}
  			}
  		});
  
  		// evaluate parameters
  		$.each(rules, function(rule, parameter) {
  			rules[rule] = $.isFunction(parameter) ? parameter(element) : parameter;
  		});
  
  		// clean number parameters
  		$.each(['minlength', 'maxlength', 'min', 'max'], function() {
  			if (rules[this]) {
  				rules[this] = Number(rules[this]);
  			}
  		});
  		$.each(['rangelength', 'range'], function() {
  			if (rules[this]) {
  				rules[this] = [Number(rules[this][0]), Number(rules[this][1])];
  			}
  		});
  
  		if ($.validator.autoCreateRanges) {
  			// auto-create ranges
  			if (rules.min && rules.max) {
  				rules.range = [rules.min, rules.max];
  				delete rules.min;
  				delete rules.max;
  			}
  			if (rules.minlength && rules.maxlength) {
  				rules.rangelength = [rules.minlength, rules.maxlength];
  				delete rules.minlength;
  				delete rules.maxlength;
  			}
  		}
  
  		// To support custom messages in metadata ignore rule methods titled "messages"
  		if (rules.messages) {
  			delete rules.messages;
  		}
  
  		return rules;
  	},
  
  	// Converts a simple string to a {string: true} rule, e.g., "required" to {required:true}
  	normalizeRule: function(data) {
  		if( typeof data == "string" ) {
  			var transformed = {};
  			$.each(data.split(/\s/), function() {
  				transformed[this] = true;
  			});
  			data = transformed;
  		}
  		return data;
  	},
  
  	// http://docs.jquery.com/Plugins/Validation/Validator/addMethod
  	addMethod: function(name, method, message) {
  		$.validator.methods[name] = method;
  		$.validator.messages[name] = message != undefined ? message : $.validator.messages[name];
  		if (method.length < 3) {
  			$.validator.addClassRules(name, $.validator.normalizeRule(name));
  		}
  	},
  
  	methods: {
  
  		// http://docs.jquery.com/Plugins/Validation/Methods/required
  		required: function(value, element, param) {
  			// check if dependency is met
  			if ( !this.depend(param, element) )
  				return "dependency-mismatch";
  			switch( element.nodeName.toLowerCase() ) {
  			case 'select':
  				// could be an array for select-multiple or a string, both are fine this way
  				var val = $(element).val();
  				return val && val.length > 0;
  			case 'input':
  				if ( this.checkable(element) )
  					return this.getLength(value, element) > 0;
  			default:
  				return $.trim(value).length > 0;
  			}
  		},
  
  		// http://docs.jquery.com/Plugins/Validation/Methods/remote
  		remote: function(value, element, param) {
  			if ( this.optional(element) )
  				return "dependency-mismatch";
  
  			var previous = this.previousValue(element);
  			if (!this.settings.messages[element.name] )
  				this.settings.messages[element.name] = {};
  			previous.originalMessage = this.settings.messages[element.name].remote;
  			this.settings.messages[element.name].remote = previous.message;
  
  			param = typeof param == "string" && {url:param} || param;
  
  			if ( this.pending[element.name] ) {
  				return "pending";
  			}
  			if ( previous.old === value ) {
  				return previous.valid;
  			}
  
  			previous.old = value;
  			var validator = this;
  			this.startRequest(element);
  			var data = {};
  			data[element.name] = value;
  			$.ajax($.extend(true, {
  				url: param,
  				mode: "abort",
  				port: "validate" + element.name,
  				dataType: "json",
  				data: data,
  				success: function(response) {
  					validator.settings.messages[element.name].remote = previous.originalMessage;
  					var valid = response === true;
  					if ( valid ) {
  						var submitted = validator.formSubmitted;
  						validator.prepareElement(element);
  						validator.formSubmitted = submitted;
  						validator.successList.push(element);
  						validator.showErrors();
  					} else {
  						var errors = {};
  						var message = response || validator.defaultMessage( element, "remote" );
  						errors[element.name] = previous.message = $.isFunction(message) ? message(value) : message;
  						validator.showErrors(errors);
  					}
  					previous.valid = valid;
  					validator.stopRequest(element, valid);
  				}
  			}, param));
  			return "pending";
  		},
  
  		// http://docs.jquery.com/Plugins/Validation/Methods/minlength
  		minlength: function(value, element, param) {
  			return this.optional(element) || this.getLength($.trim(value), element) >= param;
  		},
  
  		// http://docs.jquery.com/Plugins/Validation/Methods/maxlength
  		maxlength: function(value, element, param) {
  			return this.optional(element) || this.getLength($.trim(value), element) <= param;
  		},
  
  		// http://docs.jquery.com/Plugins/Validation/Methods/rangelength
  		rangelength: function(value, element, param) {
  			var length = this.getLength($.trim(value), element);
  			return this.optional(element) || ( length >= param[0] && length <= param[1] );
  		},
  
  		// http://docs.jquery.com/Plugins/Validation/Methods/min
  		min: function( value, element, param ) {
  			return this.optional(element) || value >= param;
  		},
  
  		// http://docs.jquery.com/Plugins/Validation/Methods/max
  		max: function( value, element, param ) {
  			return this.optional(element) || value <= param;
  		},
  
  		// http://docs.jquery.com/Plugins/Validation/Methods/range
  		range: function( value, element, param ) {
  			return this.optional(element) || ( value >= param[0] && value <= param[1] );
  		},
  
  		// http://docs.jquery.com/Plugins/Validation/Methods/email
  		email: function(value, element) {
  			// contributed by Scott Gonzalez: http://projects.scottsplayground.com/email_address_validation/
  			return this.optional(element) || /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i.test(value);
  		},
  
  		// http://docs.jquery.com/Plugins/Validation/Methods/url
  		url: function(value, element) {
  			// contributed by Scott Gonzalez: http://projects.scottsplayground.com/iri/
  			return this.optional(element) || /^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i.test(value);
  		},
  
  		// http://docs.jquery.com/Plugins/Validation/Methods/date
  		date: function(value, element) {
  			return this.optional(element) || !/Invalid|NaN/.test(new Date(value));
  		},
  
  		// http://docs.jquery.com/Plugins/Validation/Methods/dateISO
  		dateISO: function(value, element) {
  			return this.optional(element) || /^\d{4}[\/-]\d{1,2}[\/-]\d{1,2}$/.test(value);
  		},
  
  		// http://docs.jquery.com/Plugins/Validation/Methods/number
  		number: function(value, element) {
  			return this.optional(element) || /^-?(?:\d+|\d{1,3}(?:,\d{3})+)(?:\.\d+)?$/.test(value);
  		},
  
  		// http://docs.jquery.com/Plugins/Validation/Methods/digits
  		digits: function(value, element) {
  			return this.optional(element) || /^\d+$/.test(value);
  		},
  
  		// http://docs.jquery.com/Plugins/Validation/Methods/creditcard
  		// based on http://en.wikipedia.org/wiki/Luhn
  		creditcard: function(value, element) {
  			if ( this.optional(element) )
  				return "dependency-mismatch";
  			// accept only spaces, digits and dashes
  			if (/[^0-9 -]+/.test(value))
  				return false;
  			var nCheck = 0,
  				nDigit = 0,
  				bEven = false;
  
  			value = value.replace(/\D/g, "");
  
  			for (var n = value.length - 1; n >= 0; n--) {
  				var cDigit = value.charAt(n);
  				var nDigit = parseInt(cDigit, 10);
  				if (bEven) {
  					if ((nDigit *= 2) > 9)
  						nDigit -= 9;
  				}
  				nCheck += nDigit;
  				bEven = !bEven;
  			}
  
  			return (nCheck % 10) == 0;
  		},
  
  		// http://docs.jquery.com/Plugins/Validation/Methods/accept
  		accept: function(value, element, param) {
  			param = typeof param == "string" ? param.replace(/,/g, '|') : "png|jpe?g|gif";
  			return this.optional(element) || value.match(new RegExp(".(" + param + ")$", "i"));
  		},
  
  		// http://docs.jquery.com/Plugins/Validation/Methods/equalTo
  		equalTo: function(value, element, param) {
  			// bind to the blur event of the target in order to revalidate whenever the target field is updated
  			// TODO find a way to bind the event just once, avoiding the unbind-rebind overhead
  			var target = $(param).unbind(".validate-equalTo").bind("blur.validate-equalTo", function() {
  				$(element).valid();
  			});
  			return value == target.val();
  		}
  
  	}
  
  });
  
  // deprecated, use $.validator.format instead
  $.format = $.validator.format;
  
  })(jQuery);
  
  // ajax mode: abort
  // usage: $.ajax({ mode: "abort"[, port: "uniqueport"]});
  // if mode:"abort" is used, the previous request on that port (port can be undefined) is aborted via XMLHttpRequest.abort()
  ;(function($) {
  	var pendingRequests = {};
  	// Use a prefilter if available (1.5+)
  	if ( $.ajaxPrefilter ) {
  		$.ajaxPrefilter(function(settings, _, xhr) {
  			var port = settings.port;
  			if (settings.mode == "abort") {
  				if ( pendingRequests[port] ) {
  					pendingRequests[port].abort();
  				}
  				pendingRequests[port] = xhr;
  			}
  		});
  	} else {
  		// Proxy ajax
  		var ajax = $.ajax;
  		$.ajax = function(settings) {
  			var mode = ( "mode" in settings ? settings : $.ajaxSettings ).mode,
  				port = ( "port" in settings ? settings : $.ajaxSettings ).port;
  			if (mode == "abort") {
  				if ( pendingRequests[port] ) {
  					pendingRequests[port].abort();
  				}
  				return (pendingRequests[port] = ajax.apply(this, arguments));
  			}
  			return ajax.apply(this, arguments);
  		};
  	}
  })(jQuery);
  
  // provides cross-browser focusin and focusout events
  // IE has native support, in other browsers, use event caputuring (neither bubbles)
  
  // provides delegate(type: String, delegate: Selector, handler: Callback) plugin for easier event delegation
  // handler is only called when $(event.target).is(delegate), in the scope of the jquery-object for event.target
  /*
  ;(function($) {
  	// only implement if not provided by jQuery core (since 1.4)
  	// TODO verify if jQuery 1.4's implementation is compatible with older jQuery special-event APIs
  	if (!jQuery.event.special.focusin && !jQuery.event.special.focusout && document.addEventListener) {
  		$.each({
  			focus: 'focusin',
  			blur: 'focusout'
  		}, function( original, fix ){
  			$.event.special[fix] = {
  				setup:function() {
  					this.addEventListener( original, handler, true );
  				},
  				teardown:function() {
  					this.removeEventListener( original, handler, true );
  				},
  				handler: function(e) {
  					arguments[0] = $.event.fix(e);
  					arguments[0].type = fix;
  					return $.event.handle.apply(this, arguments);
  				}
  			};
  			function handler(e) {
  				e = $.event.fix(e);
  				e.type = fix;
  				return $.event.handle.call(this, e);
  			}
  		});
  	};
  	$.extend($.fn, {
  		validateDelegate: function(delegate, type, handler) {
  			return this.bind(type, function(event) {
  				var target = $(event.target);
  				if (target.is(delegate)) {
  					return handler.apply(target, arguments);
  				}
  			});
  		}
  	});
  })(jQuery);
  *//**
   * jQuery.requestAs
   * https://github.com/nforge/jquery-plugin-requestAs
   *
   * Copyright (c) 2013 JiHan Kim
   * Date: 2013/08/30
   *
   * @author JiHan Kim <laziel@naver.com>
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *   http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  !function($){
  
      "use strict";
    
      var RequestAs = function(elContainer, htOptions){
          this.init(elContainer, htOptions);
  
          // jQuery style interfaces
          this.options = this._htData;
          this.on = this._attachCustomEvent;
          this.off = this._detachCustomEvent;
      };
      
      RequestAs.prototype = {
          "constructor": RequestAs,
          
          /**
           * Attach event handler on available target elements
           */
          "init": function(elContainer, htOptions){
              var welTarget = $(elContainer);
  
              this._htData = this._getRequestOptions(welTarget, htOptions || {});
              this._htHandlers = {};
  
              // if target element is anchor and request method is GET
              // plain HTML will works enough.
              if(this._htData.sMethod === "get" && el.tagName.toLowerCase() === "a"){
                  return;
              }
  
              // legacy compatibility
              if(typeof this._htData.fOnLoad === "function"){
                  this._attachCustomEvent("load", htOptions.fOnLoad);
              }
  
              if(typeof this._htData.fOnError === "function"){
                  this._attachCustomEvent("error", htOptions.fOnError);
              }
  
              // Set cursor style on target
              welTarget.css("cursor", "pointer");
              
              // Send request on click or keydown enterkey
              welTarget.on("click keydown", $.proxy(this._onClickTarget, this));
  
              // public interfaces
              return {
                  "options": this._htData,
                  "on"     : this._attachCustomEvent,
                  "off"    : this._detachCustomEvent
              };
          },
          
          /**
           * Event Handler on target element
           * 
           * @param {Wrapped Event} weEvt
           */
          "_onClickTarget": function(weEvt){
              if((weEvt.type === "keydown" && weEvt.keyCode !== 13) || !this._htData){
                  return;
              }
  
              this._sendRequest(this._htData);
              weEvt.preventDefault();
              weEvt.stopPropagation();
              
              return false;
          },
          
          /**
           * Send request
           * 
           * @param {Hash Table} htData
           */
          "_sendRequest": function(htData){
              var htReqOpt = {
                  "method"  : htData.sMethod,
                  "success" : $.proxy(this._onSuccessRequest, this),
                  "error"   : $.proxy(this._onErrorRequest, this),
                  "cache"   : false,
                  "dataType": "text"
              };
              
              // if htAjaxOpt exists, it overrides current Ajax Options
              // $.ajax "data" option could be used with this
              if(htData.htAjaxOpt instanceof Object){
                  htReqOpt = $.extend(htReqOpt, htData.htAjaxOpt);
              }
  
              // fire custom event "beforeRequest"
              // if any event handler returns explicitly false(Boolean),
              // no request will be sent.
              var bCustomEventResult = this._fireEvent("beforeRequest", htReqOpt);
              if(bCustomEventResult === false){
                  return false;
              }
  
              $.ajax(htData.sHref, htReqOpt);
          },
          
          /**
           * Get request options from target element
           * 
           * @param {HTMLElement} elTarget
           * @param {Hash Table} htOptions
           */
          "_getRequestOptions": function(elTarget, htOptions){
              var welTarget = $(elTarget);
  
              return {
                  "sMethod" : htOptions.sMethod  || welTarget.data("request-method") || "get",
                  "sHref"   : htOptions.sHref    || welTarget.data("request-uri") || welTarget.attr("href"),
                  "fOnLoad" : htOptions.fOnLoad  || undefined,
                  "fOnError": htOptions.fOnError || undefined
              };
          },
  
          /**
           * Default callback for handle request success event
           * redirects to URL if server responses JSON data
           * (e.g. {"url":"http://www.foobar.com/"})
           * or just reload current page
           * 
           * @param {Object} oRes
           * @param {String} sStatus
           * @param {Object} oXHR
           */
          "_onSuccessRequest": function(oRes, sStatus, oXHR){
              // fire custom event "load"
              // if any event handler returns explicitly false(Boolean),
              // default action will be prevented.
              var bCustomEventResult = this._fireEvent("load", {
                  "oRes"   : oRes,
                  "oXHR"   : oXHR,
                  "sStatus": sStatus
              });
              if(bCustomEventResult === false){
                  return;
              }
  
              // default action below:
              var sLocation = oXHR.getResponseHeader("Location");
              
              if(oXHR.status === 204 && sLocation){
                  document.location.href = sLocation;
              } else {
                  document.location.reload();
              }
          },
          
          /**
           * Default callback for handle request error event
           * 
           * @param {Object} oXHR
           */
          "_onErrorRequest": function(oXHR){
              // fire custom event "load"
              // if any event handler returns explicitly false(Boolean),
              // default action will be prevented.
              this._fireEvent("error", {
                  "oXHR": oXHR
              });
  
              // default action below:
              switch(oXHR.status){
                  case 200:
                      // if server responses ok(200) but cannot determine redirect URL,
                      // reload current page.
                      document.location.reload();
                      break;
                      
                  case 204:
                      // if server responses 204, it is client error.
                      // in this case, check AJAX dataType option on _sendRequest
                      document.location.href = oXHR.getResponseHeader("Location");
                      break;
              }
  
              // need to do something else?
          },
  
          /**
           * Attach custom event handler
           *
           * @param {String} sEventName
           * @param {Function} fHandler
           * @param {String} sNamespace
           * @example
           * $("#button").data("requestAs").on("eventName", function(){});
           * // or
           * $("#button").data("requestAs").on({
          *    "event1st": function(){},
          *    "event2nd": function(){}
          * });
           */
          "_attachCustomEvent": function(sEventName, fHandler){
              if(typeof sEventName === "object"){
                  for(var sKey in sEventName){
                      this._htHandlers[sKey] = this._htHandlers[sKey] || [];
                      this._htHandlers[sKey].push(sEventName[sKey]);
                  }
              } else {
                  this._htHandlers[sEventName] = this._htHandlers[sEventName] || [];
                  this._htHandlers[sEventName].push(fHandler);
              }
          },
  
          /**
           * Detach custom event handler
           * clears all handler of sEventName when fHandler is empty
           *
           * @param {String} sEventName
           * @param {Function} fHandler
           */
          "_detachCustomEvent": function(sEventName, fHandler){
              if(!fHandler){
                  this._htHandlers[sEventName] = [];
                  return;
              }
  
              var aHandlers = this._htHandlers[sEventName];
              var nIndex = aHandlers ? aHandlers.indexOf(fHandler) : -1;
  
              if(nIndex > -1){
                  this._htHandlers[sEventName].splice(nIndex, 1);
              }
          },
  
          /**
           * Run specified custom event handlers
           *
           * @param {String} sEventName
           * @param {Object} oData
           */
          "_fireEvent": function(sEventName, oData){
              var aHandlers = this._htHandlers[sEventName];
  
              if ((aHandlers instanceof Array) === false) {
                  return;
              }
  
              var bResult = undefined;
  
              aHandlers.forEach(function(fHandler){
                  bResult = bResult || fHandler(oData);
              });
  
              return bResult;
          }
      };
      
      // RequestAs Plugin Definition
      var old = $.fn.requestAs;
      
      $.fn.requestAs = function(htOptions){
          return this.each(function(){
              var $this = $(this);
              var data = $this.data("requestAs");
              
              if(!data){
                  $this.data("requestAs", data = new RequestAs(this, htOptions));
              }
              if(typeof htOptions == "string") {
                  data[htOptions]();
              }
          });
      };
  
      // RequestAs No Conflict
      $.fn.requestAs.noConflict = function(){
          $.fn.requestAs = old;
          return this;
      };
      
      // RequestAs DATA-API
      $(document).ready(function(){
          $("[data-request-method]").requestAs();
      });
  }(window.jQuery);
  /**
   * Yobi, Project Hosting SW
   *
   * Copyright 2014 NAVER Corp.
   * http://yobi.io
   *
   * @author Insanehong
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *   http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  !function ($) {
  
    "use strict"; // jshint ;_;
  
    var Search = function (element, options) {
      this.$element = $(element);
      this.options = $.extend({}, $.fn.search.defaults, options);
      this.item = this.options.item || this.$element.data('items');
      this.items = $('[data-item="'+this.item+'"]');
      this.lookup();
      this.searchTimer;
    }
  
    Search.prototype = {
      constructor: Search
      , lookup : function() {
          this.$element.on('keyup', $.proxy(this.keyup, this));
          this.$element.on('keydown', $.proxy(this.kedown, this));
      }
      , keyup : function() {
          this.searchTimer = setTimeout(
              $.proxy(this.search, this), 
              this.options.delay, 
              this.$element.val().toLowerCase()
          );
      }
      , keydown : function() {
          clearTimeout(this.searchTimer);
      }  
      , search : function(filter) {
          var $this = this;
  
          if(!$.trim(filter)) {
              this.items.show();
              return ;        
          }
  
          this.items.each(function(index, item) {
              ($this.matcher(filter, item)) ? $(item).show() : $(item).hide();
          });
      } 
      , matcher : function(filter, item) {
          return ($(item).data('value').toLowerCase().indexOf(filter) !== -1);
      }
      
    }
  
    $.fn.search = function ( option ) {
      return this.each(function () {
        var $this = $(this)
          , data = $this.data('search')
          , options = typeof option == 'object' && option
        if (!data) $this.data('search', (data = new Search(this, options)))
        if (typeof option == 'string') data[option]()
      })
    }
  
    $.fn.search.defaults ={
      items : "",
      delay : 200
    }
  
  
    $(document).on('focus' , '[data-toggle="item-search"]', function(e) {
      var $this = $(this);
      if ($this.data('search')) return;
      $this.search($this.data());
    });
  
  }(window.jQuery);
  /*
   * zClip :: jQuery ZeroClipboard v1.1.1
   * http://steamdev.com/zclip
   *
   * Copyright 2011, SteamDev
   * Released under the MIT license.
   * http://www.opensource.org/licenses/mit-license.php
   *
   * Date: Wed Jun 01, 2011
   */
  
  (function(a){a.fn.zclip=function(c){if(typeof c=="object"&&!c.length){var b=a.extend({path:"ZeroClipboard.swf",copy:null,beforeCopy:null,afterCopy:null,clickAfter:true,setHandCursor:true,setCSSEffects:true},c);return this.each(function(){var e=a(this);if(e.is(":visible")&&(typeof b.copy=="string"||a.isFunction(b.copy))){ZeroClipboard.setMoviePath(b.path);var d=new ZeroClipboard.Client();if(a.isFunction(b.copy)){e.bind("zClip_copy",b.copy)}if(a.isFunction(b.beforeCopy)){e.bind("zClip_beforeCopy",b.beforeCopy)}if(a.isFunction(b.afterCopy)){e.bind("zClip_afterCopy",b.afterCopy)}d.setHandCursor(b.setHandCursor);d.setCSSEffects(b.setCSSEffects);d.addEventListener("mouseOver",function(f){e.trigger("mouseenter")});d.addEventListener("mouseOut",function(f){e.trigger("mouseleave")});d.addEventListener("mouseDown",function(f){e.trigger("mousedown");if(!a.isFunction(b.copy)){d.setText(b.copy)}else{d.setText(e.triggerHandler("zClip_copy"))}if(a.isFunction(b.beforeCopy)){e.trigger("zClip_beforeCopy")}});d.addEventListener("complete",function(f,g){if(a.isFunction(b.afterCopy)){e.trigger("zClip_afterCopy")}else{if(g.length>500){g=g.substr(0,500)+"...\n\n("+(g.length-500)+" characters not shown)"}e.removeClass("hover");alert("Copied text to clipboard:\n\n "+g)}if(b.clickAfter){e.trigger("click")}});d.glue(e[0],e.parent()[0]);a(window).bind("load resize",function(){d.reposition()})}})}else{if(typeof c=="string"){return this.each(function(){var f=a(this);c=c.toLowerCase();var e=f.data("zclipId");var d=a("#"+e+".zclip");if(c=="remove"){d.remove();f.removeClass("active hover")}else{if(c=="hide"){d.hide();f.removeClass("active hover")}else{if(c=="show"){d.show()}}}})}}}})(jQuery);var ZeroClipboard={version:"1.0.7",clients:{},moviePath:"ZeroClipboard.swf",nextId:1,$:function(a){if(typeof(a)=="string"){a=document.getElementById(a)}if(!a.addClass){a.hide=function(){this.style.display="none"};a.show=function(){this.style.display=""};a.addClass=function(b){this.removeClass(b);this.className+=" "+b};a.removeClass=function(d){var e=this.className.split(/\s+/);var b=-1;for(var c=0;c<e.length;c++){if(e[c]==d){b=c;c=e.length}}if(b>-1){e.splice(b,1);this.className=e.join(" ")}return this};a.hasClass=function(b){return !!this.className.match(new RegExp("\\s*"+b+"\\s*"))}}return a},setMoviePath:function(a){this.moviePath=a},dispatch:function(d,b,c){var a=this.clients[d];if(a){a.receiveEvent(b,c)}},register:function(b,a){this.clients[b]=a},getDOMObjectPosition:function(c,a){var b={left:0,top:0,width:c.width?c.width:c.offsetWidth,height:c.height?c.height:c.offsetHeight};if(c&&(c!=a)){b.left+=c.offsetLeft;b.top+=c.offsetTop}return b},Client:function(a){this.handlers={};this.id=ZeroClipboard.nextId++;this.movieId="ZeroClipboardMovie_"+this.id;ZeroClipboard.register(this.id,this);if(a){this.glue(a)}}};ZeroClipboard.Client.prototype={id:0,ready:false,movie:null,clipText:"",handCursorEnabled:true,cssEffects:true,handlers:null,glue:function(d,b,e){this.domElement=ZeroClipboard.$(d);var f=99;if(this.domElement.style.zIndex){f=parseInt(this.domElement.style.zIndex,10)+1}if(typeof(b)=="string"){b=ZeroClipboard.$(b)}else{if(typeof(b)=="undefined"){b=document.getElementsByTagName("body")[0]}}var c=ZeroClipboard.getDOMObjectPosition(this.domElement,b);this.div=document.createElement("div");this.div.className="zclip";this.div.id="zclip-"+this.movieId;$(this.domElement).data("zclipId","zclip-"+this.movieId);var a=this.div.style;a.position="absolute";a.left=""+c.left+"px";a.top=""+c.top+"px";a.width=""+c.width+"px";a.height=""+c.height+"px";a.zIndex=f;if(typeof(e)=="object"){for(addedStyle in e){a[addedStyle]=e[addedStyle]}}b.appendChild(this.div);this.div.innerHTML=this.getHTML(c.width,c.height)},getHTML:function(d,a){var c="";var b="id="+this.id+"&width="+d+"&height="+a;if(navigator.userAgent.match(/MSIE/)){var e=location.href.match(/^https/i)?"https://":"http://";c+='<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="'+e+'download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,0,0" width="'+d+'" height="'+a+'" id="'+this.movieId+'" align="middle"><param name="allowScriptAccess" value="always" /><param name="allowFullScreen" value="false" /><param name="movie" value="'+ZeroClipboard.moviePath+'" /><param name="loop" value="false" /><param name="menu" value="false" /><param name="quality" value="best" /><param name="bgcolor" value="#ffffff" /><param name="flashvars" value="'+b+'"/><param name="wmode" value="transparent"/></object>'}else{c+='<embed id="'+this.movieId+'" src="'+ZeroClipboard.moviePath+'" loop="false" menu="false" quality="best" bgcolor="#ffffff" width="'+d+'" height="'+a+'" name="'+this.movieId+'" align="middle" allowScriptAccess="always" allowFullScreen="false" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" flashvars="'+b+'" wmode="transparent" />'}return c},hide:function(){if(this.div){this.div.style.left="-2000px"}},show:function(){this.reposition()},destroy:function(){if(this.domElement&&this.div){this.hide();this.div.innerHTML="";var a=document.getElementsByTagName("body")[0];try{a.removeChild(this.div)}catch(b){}this.domElement=null;this.div=null}},reposition:function(c){if(c){this.domElement=ZeroClipboard.$(c);if(!this.domElement){this.hide()}}if(this.domElement&&this.div){var b=ZeroClipboard.getDOMObjectPosition(this.domElement);var a=this.div.style;a.left=""+b.left+"px";a.top=""+b.top+"px"}},setText:function(a){this.clipText=a;if(this.ready){this.movie.setText(a)}},addEventListener:function(a,b){a=a.toString().toLowerCase().replace(/^on/,"");if(!this.handlers[a]){this.handlers[a]=[]}this.handlers[a].push(b)},setHandCursor:function(a){this.handCursorEnabled=a;if(this.ready){this.movie.setHandCursor(a)}},setCSSEffects:function(a){this.cssEffects=!!a},receiveEvent:function(d,f){d=d.toString().toLowerCase().replace(/^on/,"");switch(d){case"load":this.movie=document.getElementById(this.movieId);if(!this.movie){var c=this;setTimeout(function(){c.receiveEvent("load",null)},1);return}if(!this.ready&&navigator.userAgent.match(/Firefox/)&&navigator.userAgent.match(/Windows/)){var c=this;setTimeout(function(){c.receiveEvent("load",null)},100);this.ready=true;return}this.ready=true;try{this.movie.setText(this.clipText)}catch(h){}try{this.movie.setHandCursor(this.handCursorEnabled)}catch(h){}break;case"mouseover":if(this.domElement&&this.cssEffects){this.domElement.addClass("hover");if(this.recoverActive){this.domElement.addClass("active")}}break;case"mouseout":if(this.domElement&&this.cssEffects){this.recoverActive=false;if(this.domElement.hasClass("active")){this.domElement.removeClass("active");this.recoverActive=true}this.domElement.removeClass("hover")}break;case"mousedown":if(this.domElement&&this.cssEffects){this.domElement.addClass("active")}break;case"mouseup":if(this.domElement&&this.cssEffects){this.domElement.removeClass("active");this.recoverActive=false}break}if(this.handlers[d]){for(var b=0,a=this.handlers[d].length;b<a;b++){var g=this.handlers[d][b];if(typeof(g)=="function"){g(this,f)}else{if((typeof(g)=="object")&&(g.length==2)){g[0][g[1]](this,f)}else{if(typeof(g)=="string"){window[g](this,f)}}}}}}};/*! http://mths.be/placeholder v2.0.7 by @mathias */
  ;(function(f,h,$){var a='placeholder' in h.createElement('input'),d='placeholder' in h.createElement('textarea'),i=$.fn,c=$.valHooks,k,j;if(a&&d){j=i.placeholder=function(){return this};j.input=j.textarea=true}else{j=i.placeholder=function(){var l=this;l.filter((a?'textarea':':input')+'[placeholder]').not('.placeholder').bind({'focus.placeholder':b,'blur.placeholder':e}).data('placeholder-enabled',true).trigger('blur.placeholder');return l};j.input=a;j.textarea=d;k={get:function(m){var l=$(m);return l.data('placeholder-enabled')&&l.hasClass('placeholder')?'':m.value},set:function(m,n){var l=$(m);if(!l.data('placeholder-enabled')){return m.value=n}if(n==''){m.value=n;if(m!=h.activeElement){e.call(m)}}else{if(l.hasClass('placeholder')){b.call(m,true,n)||(m.value=n)}else{m.value=n}}return l}};a||(c.input=k);d||(c.textarea=k);$(function(){$(h).delegate('form','submit.placeholder',function(){var l=$('.placeholder',this).each(b);setTimeout(function(){l.each(e)},10)})});$(f).bind('beforeunload.placeholder',function(){$('.placeholder').each(function(){this.value=''})})}function g(m){var l={},n=/^jQuery\d+$/;$.each(m.attributes,function(p,o){if(o.specified&&!n.test(o.name)){l[o.name]=o.value}});return l}function b(m,n){var l=this,o=$(l);if(l.value==o.attr('placeholder')&&o.hasClass('placeholder')){if(o.data('placeholder-password')){o=o.hide().next().show().attr('id',o.removeAttr('id').data('placeholder-id'));if(m===true){return o[0].value=n}o.focus()}else{l.value='';o.removeClass('placeholder');l==h.activeElement&&l.select()}}}function e(){var q,l=this,p=$(l),m=p,o=this.id;if(l.value==''){if(l.type=='password'){if(!p.data('placeholder-textinput')){try{q=p.clone().attr({type:'text'})}catch(n){q=$('<input>').attr($.extend(g(this),{type:'text'}))}q.removeAttr('name').data({'placeholder-password':true,'placeholder-id':o}).bind('focus.placeholder',b);p.data({'placeholder-textinput':q,'placeholder-id':o}).before(q)}p=p.removeAttr('id').hide().prev().attr('id',o).show()}p.addClass('placeholder');p[0].value=p.attr('placeholder')}else{p.removeClass('placeholder')}}}(this,document,jQuery));/*
   *  Yona, 21st Century Project Hosting SW
   *  <p>
   *  Copyright Yona & Yobi Authors & NAVER Corp.
   *  https://yona.io
   *
   * ===================================================
   * Copyright 2012 Twitter, Inc.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   * http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   * ========================================================== */
  
  
  !function ($) {
  
    "use strict"; // jshint ;_;
  
  
    /* CSS TRANSITION SUPPORT (http://www.modernizr.com/)
     * ======================================================= */
  
    $(function () {
  
      $.support.transition = (function () {
  
        var transitionEnd = (function () {
  
          var el = document.createElement('bootstrap')
            , transEndEventNames = {
                 'WebkitTransition' : 'webkitTransitionEnd'
              ,  'MozTransition'    : 'transitionend'
              ,  'OTransition'      : 'oTransitionEnd otransitionend'
              ,  'transition'       : 'transitionend'
              }
            , name
  
          for (name in transEndEventNames){
            if (el.style[name] !== undefined) {
              return transEndEventNames[name]
            }
          }
  
        }())
  
        return transitionEnd && {
          end: transitionEnd
        }
  
      })()
  
    })
  
  }(window.jQuery);/* ==========================================================
   * bootstrap-alert.js v2.3.1
   * http://twitter.github.com/bootstrap/javascript.html#alerts
   * ==========================================================
   * Copyright 2012 Twitter, Inc.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   * http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   * ========================================================== */
  
  
  !function ($) {
  
    "use strict"; // jshint ;_;
  
  
   /* ALERT CLASS DEFINITION
    * ====================== */
  
    var dismiss = '[data-dismiss="alert"]'
      , Alert = function (el) {
          $(el).on('click', dismiss, this.close)
        }
  
    Alert.prototype.close = function (e) {
      var $this = $(this)
        , selector = $this.attr('data-target')
        , $parent
  
      if (!selector) {
        selector = $this.attr('href')
        selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
      }
  
      $parent = $(selector)
  
      e && e.preventDefault()
  
      $parent.length || ($parent = $this.hasClass('alert') ? $this : $this.parent())
  
      $parent.trigger(e = $.Event('close'))
  
      if (e.isDefaultPrevented()) return
  
      $parent.removeClass('in')
  
      function removeElement() {
        $parent
          .trigger('closed')
          .remove()
      }
  
      $.support.transition && $parent.hasClass('fade') ?
        $parent.on($.support.transition.end, removeElement) :
        removeElement()
    }
  
  
   /* ALERT PLUGIN DEFINITION
    * ======================= */
  
    var old = $.fn.alert
  
    $.fn.alert = function (option) {
      return this.each(function () {
        var $this = $(this)
          , data = $this.data('alert')
        if (!data) $this.data('alert', (data = new Alert(this)))
        if (typeof option == 'string') data[option].call($this)
      })
    }
  
    $.fn.alert.Constructor = Alert
  
  
   /* ALERT NO CONFLICT
    * ================= */
  
    $.fn.alert.noConflict = function () {
      $.fn.alert = old
      return this
    }
  
  
   /* ALERT DATA-API
    * ============== */
  
    $(document).on('click.alert.data-api', dismiss, Alert.prototype.close)
  
  }(window.jQuery);/* ============================================================
   * bootstrap-button.js v2.3.1
   * http://twitter.github.com/bootstrap/javascript.html#buttons
   * ============================================================
   * Copyright 2012 Twitter, Inc.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   * http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   * ============================================================ */
  
  
  !function ($) {
  
    "use strict"; // jshint ;_;
  
  
   /* BUTTON PUBLIC CLASS DEFINITION
    * ============================== */
  
    var Button = function (element, options) {
      this.$element = $(element)
      this.options = $.extend({}, $.fn.button.defaults, options)
    }
  
    Button.prototype.setState = function (state) {
      var d = 'disabled'
        , $el = this.$element
        , data = $el.data()
        , val = $el.is('input') ? 'val' : 'html'
  
      state = state + 'Text'
      data.resetText || $el.data('resetText', $el[val]())
  
      $el[val](data[state] || this.options[state])
  
      // push to event loop to allow forms to submit
      setTimeout(function () {
        state == 'loadingText' ?
          $el.addClass(d).attr(d, d) :
          $el.removeClass(d).removeAttr(d)
      }, 0)
    }
  
    Button.prototype.toggle = function () {
      var $parent = this.$element.closest('[data-toggle="buttons-radio"]')
  
      $parent && $parent
        .find('.active')
        .removeClass('active')
  
      this.$element.toggleClass('active')
    }
  
  
   /* BUTTON PLUGIN DEFINITION
    * ======================== */
  
    var old = $.fn.button
  
    $.fn.button = function (option) {
      return this.each(function () {
        var $this = $(this)
          , data = $this.data('button')
          , options = typeof option == 'object' && option
        if (!data) $this.data('button', (data = new Button(this, options)))
        if (option == 'toggle') data.toggle()
        else if (option) data.setState(option)
      })
    }
  
    $.fn.button.defaults = {
      loadingText: 'loading...'
    }
  
    $.fn.button.Constructor = Button
  
  
   /* BUTTON NO CONFLICT
    * ================== */
  
    $.fn.button.noConflict = function () {
      $.fn.button = old
      return this
    }
  
  
   /* BUTTON DATA-API
    * =============== */
  
    $(document).on('click.button.data-api', '[data-toggle^=button]', function (e) {
      var $btn = $(e.target)
      if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn')
      $btn.button('toggle')
    })
  
  }(window.jQuery);/* ==========================================================
   * bootstrap-carousel.js v2.3.1
   * http://twitter.github.com/bootstrap/javascript.html#carousel
   * ==========================================================
   * Copyright 2012 Twitter, Inc.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   * http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   * ========================================================== */
  
  
  !function ($) {
  
    "use strict"; // jshint ;_;
  
  
   /* CAROUSEL CLASS DEFINITION
    * ========================= */
  
    var Carousel = function (element, options) {
      this.$element = $(element)
      this.$indicators = this.$element.find('.carousel-indicators')
      this.options = options
      this.options.pause == 'hover' && this.$element
        .on('mouseenter', $.proxy(this.pause, this))
        .on('mouseleave', $.proxy(this.cycle, this))
    }
  
    Carousel.prototype = {
  
      cycle: function (e) {
        if (!e) this.paused = false
        if (this.interval) clearInterval(this.interval);
        this.options.interval
          && !this.paused
          && (this.interval = setInterval($.proxy(this.next, this), this.options.interval))
        return this
      }
  
    , getActiveIndex: function () {
        this.$active = this.$element.find('.item.active')
        this.$items = this.$active.parent().children()
        return this.$items.index(this.$active)
      }
  
    , to: function (pos) {
        var activeIndex = this.getActiveIndex()
          , that = this
  
        if (pos > (this.$items.length - 1) || pos < 0) return
  
        if (this.sliding) {
          return this.$element.one('slid', function () {
            that.to(pos)
          })
        }
  
        if (activeIndex == pos) {
          return this.pause().cycle()
        }
  
        return this.slide(pos > activeIndex ? 'next' : 'prev', $(this.$items[pos]))
      }
  
    , pause: function (e) {
        if (!e) this.paused = true
        if (this.$element.find('.next, .prev').length && $.support.transition.end) {
          this.$element.trigger($.support.transition.end)
          this.cycle(true)
        }
        clearInterval(this.interval)
        this.interval = null
        return this
      }
  
    , next: function () {
        if (this.sliding) return
        return this.slide('next')
      }
  
    , prev: function () {
        if (this.sliding) return
        return this.slide('prev')
      }
  
    , slide: function (type, next) {
        var $active = this.$element.find('.item.active')
          , $next = next || $active[type]()
          , isCycling = this.interval
          , direction = type == 'next' ? 'left' : 'right'
          , fallback  = type == 'next' ? 'first' : 'last'
          , that = this
          , e
  
        this.sliding = true
  
        isCycling && this.pause()
  
        $next = $next.length ? $next : this.$element.find('.item')[fallback]()
  
        e = $.Event('slide', {
          relatedTarget: $next[0]
        , direction: direction
        })
  
        if ($next.hasClass('active')) return
  
        if (this.$indicators.length) {
          this.$indicators.find('.active').removeClass('active')
          this.$element.one('slid', function () {
            var $nextIndicator = $(that.$indicators.children()[that.getActiveIndex()])
            $nextIndicator && $nextIndicator.addClass('active')
          })
        }
  
        if ($.support.transition && this.$element.hasClass('slide')) {
          this.$element.trigger(e)
          if (e.isDefaultPrevented()) return
          $next.addClass(type)
          $next[0].offsetWidth // force reflow
          $active.addClass(direction)
          $next.addClass(direction)
          this.$element.one($.support.transition.end, function () {
            $next.removeClass([type, direction].join(' ')).addClass('active')
            $active.removeClass(['active', direction].join(' '))
            that.sliding = false
            setTimeout(function () { that.$element.trigger('slid') }, 0)
          })
        } else {
          this.$element.trigger(e)
          if (e.isDefaultPrevented()) return
          $active.removeClass('active')
          $next.addClass('active')
          this.sliding = false
          this.$element.trigger('slid')
        }
  
        isCycling && this.cycle()
  
        return this
      }
  
    }
  
  
   /* CAROUSEL PLUGIN DEFINITION
    * ========================== */
  
    var old = $.fn.carousel
  
    $.fn.carousel = function (option) {
      return this.each(function () {
        var $this = $(this)
          , data = $this.data('carousel')
          , options = $.extend({}, $.fn.carousel.defaults, typeof option == 'object' && option)
          , action = typeof option == 'string' ? option : options.slide
        if (!data) $this.data('carousel', (data = new Carousel(this, options)))
        if (typeof option == 'number') data.to(option)
        else if (action) data[action]()
        else if (options.interval) data.pause().cycle()
      })
    }
  
    $.fn.carousel.defaults = {
      interval: 5000
    , pause: 'hover'
    }
  
    $.fn.carousel.Constructor = Carousel
  
  
   /* CAROUSEL NO CONFLICT
    * ==================== */
  
    $.fn.carousel.noConflict = function () {
      $.fn.carousel = old
      return this
    }
  
   /* CAROUSEL DATA-API
    * ================= */
  
    $(document).on('click.carousel.data-api', '[data-slide], [data-slide-to]', function (e) {
      var $this = $(this), href
        , $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
        , options = $.extend({}, $target.data(), $this.data())
        , slideIndex
  
      $target.carousel(options)
  
      if (slideIndex = $this.attr('data-slide-to')) {
        $target.data('carousel').pause().to(slideIndex).cycle()
      }
  
      e.preventDefault()
    })
  
  }(window.jQuery);/* =============================================================
   * bootstrap-collapse.js v2.3.1
   * http://twitter.github.com/bootstrap/javascript.html#collapse
   * =============================================================
   * Copyright 2012 Twitter, Inc.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   * http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   * ============================================================ */
  
  
  !function ($) {
  
    "use strict"; // jshint ;_;
  
  
   /* COLLAPSE PUBLIC CLASS DEFINITION
    * ================================ */
  
    var Collapse = function (element, options) {
      this.$element = $(element)
      this.options = $.extend({}, $.fn.collapse.defaults, options)
  
      if (this.options.parent) {
        this.$parent = $(this.options.parent)
      }
  
      this.options.toggle && this.toggle()
    }
  
    Collapse.prototype = {
  
      constructor: Collapse
  
    , dimension: function () {
        var hasWidth = this.$element.hasClass('width')
        return hasWidth ? 'width' : 'height'
      }
  
    , show: function () {
        var dimension
          , scroll
          , actives
          , hasData
  
        if (this.transitioning || this.$element.hasClass('in')) return
  
        dimension = this.dimension()
        scroll = $.camelCase(['scroll', dimension].join('-'))
        actives = this.$parent && this.$parent.find('> .accordion-group > .in')
  
        if (actives && actives.length) {
          hasData = actives.data('collapse')
          if (hasData && hasData.transitioning) return
          actives.collapse('hide')
          hasData || actives.data('collapse', null)
        }
  
        this.$element[dimension](0)
        this.transition('addClass', $.Event('show'), 'shown')
        $.support.transition && this.$element[dimension](this.$element[0][scroll])
      }
  
    , hide: function () {
        var dimension
        if (this.transitioning || !this.$element.hasClass('in')) return
        dimension = this.dimension()
        this.reset(this.$element[dimension]())
        this.transition('removeClass', $.Event('hide'), 'hidden')
        this.$element[dimension](0)
      }
  
    , reset: function (size) {
        var dimension = this.dimension()
  
        this.$element
          .removeClass('collapse')
          [dimension](size || 'auto')
          [0].offsetWidth
  
        this.$element[size !== null ? 'addClass' : 'removeClass']('collapse')
  
        return this
      }
  
    , transition: function (method, startEvent, completeEvent) {
        var that = this
          , complete = function () {
              if (startEvent.type == 'show') that.reset()
              that.transitioning = 0
              that.$element.trigger(completeEvent)
            }
  
        this.$element.trigger(startEvent)
  
        if (startEvent.isDefaultPrevented()) return
  
        this.transitioning = 1
  
        this.$element[method]('in')
  
        $.support.transition && this.$element.hasClass('collapse') ?
          this.$element.one($.support.transition.end, complete) :
          complete()
      }
  
    , toggle: function () {
        this[this.$element.hasClass('in') ? 'hide' : 'show']()
      }
  
    }
  
  
   /* COLLAPSE PLUGIN DEFINITION
    * ========================== */
  
    var old = $.fn.collapse
  
    $.fn.collapse = function (option) {
      return this.each(function () {
        var $this = $(this)
          , data = $this.data('collapse')
          , options = $.extend({}, $.fn.collapse.defaults, $this.data(), typeof option == 'object' && option)
        if (!data) $this.data('collapse', (data = new Collapse(this, options)))
        if (typeof option == 'string') data[option]()
      })
    }
  
    $.fn.collapse.defaults = {
      toggle: true
    }
  
    $.fn.collapse.Constructor = Collapse
  
  
   /* COLLAPSE NO CONFLICT
    * ==================== */
  
    $.fn.collapse.noConflict = function () {
      $.fn.collapse = old
      return this
    }
  
  
   /* COLLAPSE DATA-API
    * ================= */
  
    $(document).on('click.collapse.data-api', '[data-toggle=collapse]', function (e) {
      var $this = $(this), href
        , target = $this.attr('data-target')
          || e.preventDefault()
          || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7
        , option = $(target).data('collapse') ? 'toggle' : $this.data()
      $this[$(target).hasClass('in') ? 'addClass' : 'removeClass']('collapsed')
      $(target).collapse(option)
    })
  
  }(window.jQuery);/* ============================================================
   * bootstrap-dropdown.js v2.3.1
   * http://twitter.github.com/bootstrap/javascript.html#dropdowns
   * ============================================================
   * Copyright 2012 Twitter, Inc.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   * http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   * ============================================================ */
  
  
  !function ($) {
  
    "use strict"; // jshint ;_;
  
  
   /* DROPDOWN CLASS DEFINITION
    * ========================= */
  
    var toggle = '[data-toggle=dropdown]'
      , Dropdown = function (element) {
          var $el = $(element).on('click.dropdown.data-api', this.toggle)
          $('html').on('click.dropdown.data-api', function () {
            $el.parent().removeClass('open')
          })
        }
  
    Dropdown.prototype = {
  
      constructor: Dropdown
  
    , toggle: function (e) {
        var $this = $(this)
          , $parent
          , isActive
  
        if ($this.is('.disabled, :disabled')) return
  
        $parent = getParent($this)
  
        isActive = $parent.hasClass('open')
  
        clearMenus()
  
        if (!isActive) {
          $parent.toggleClass('open')
        }
  
        $this.focus()
  
        return false
      }
  
    , keydown: function (e) {
        var $this
          , $items
          , $active
          , $parent
          , isActive
          , index
  
        if (!/(38|40|27)/.test(e.keyCode)) return
  
        $this = $(this)
  
        e.preventDefault()
        e.stopPropagation()
  
        if ($this.is('.disabled, :disabled')) return
  
        $parent = getParent($this)
  
        isActive = $parent.hasClass('open')
  
        if (!isActive || (isActive && e.keyCode == 27)) {
          if (e.which == 27) $parent.find(toggle).focus()
          return $this.click()
        }
  
        $items = $('[role=menu] li:not(.divider):visible a', $parent)
  
        if (!$items.length) return
  
        index = $items.index($items.filter(':focus'))
  
        if (e.keyCode == 38 && index > 0) index--                                        // up
        if (e.keyCode == 40 && index < $items.length - 1) index++                        // down
        if (!~index) index = 0
  
        $items
          .eq(index)
          .focus()
      }
  
    }
  
    function clearMenus() {
      $(toggle).each(function () {
        getParent($(this)).removeClass('open')
      })
    }
  
    function getParent($this) {
      var selector = $this.attr('data-target')
        , $parent
  
      if (!selector) {
        selector = $this.attr('href')
        selector = selector && /#/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
      }
  
      $parent = selector && $(selector)
  
      if (!$parent || !$parent.length) $parent = $this.parent()
  
      return $parent
    }
  
  
    /* DROPDOWN PLUGIN DEFINITION
     * ========================== */
  
    var old = $.fn.dropdown
  
    $.fn.dropdown = function (option) {
      return this.each(function () {
        var $this = $(this)
          , data = $this.data('dropdown')
        if (!data) $this.data('dropdown', (data = new Dropdown(this)))
        if (typeof option == 'string') data[option].call($this)
      })
    }
  
    $.fn.dropdown.Constructor = Dropdown
  
  
   /* DROPDOWN NO CONFLICT
    * ==================== */
  
    $.fn.dropdown.noConflict = function () {
      $.fn.dropdown = old
      return this
    }
  
  
    /* APPLY TO STANDARD DROPDOWN ELEMENTS
     * =================================== */
  
    $(document)
      .on('click.dropdown.data-api', clearMenus)
      .on('click.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() })
      .on('click.dropdown-menu', function (e) { e.stopPropagation() })
      .on('click.dropdown.data-api'  , toggle, Dropdown.prototype.toggle)
      .on('keydown.dropdown.data-api', toggle + ', [role=menu]' , Dropdown.prototype.keydown)
  
  }(window.jQuery);
  /* =========================================================
   * bootstrap-modal.js v2.3.1
   * http://twitter.github.com/bootstrap/javascript.html#modals
   * =========================================================
   * Copyright 2012 Twitter, Inc.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   * http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   * ========================================================= */
  
  
  !function ($) {
  
    "use strict"; // jshint ;_;
  
  
   /* MODAL CLASS DEFINITION
    * ====================== */
  
    var Modal = function (element, options) {
      this.options = options
      this.$element = $(element)
        .delegate('[data-dismiss="modal"]', 'click.dismiss.modal', $.proxy(this.hide, this))
      this.options.remote && this.$element.find('.modal-body').load(this.options.remote)
    }
  
    Modal.prototype = {
  
        constructor: Modal
  
      , toggle: function () {
          return this[!this.isShown ? 'show' : 'hide']()
        }
  
      , show: function () {
          var that = this
            , e = $.Event('show')
  
          this.$element.trigger(e)
  
          if (this.isShown || e.isDefaultPrevented()) return
  
          this.isShown = true
  
          this.escape()
  
          this.backdrop(function () {
            var transition = $.support.transition && that.$element.hasClass('fade')
  
            if (!that.$element.parent().length) {
              that.$element.appendTo(document.body) //don't move modals dom position
            }
  
            that.$element.show()
  
            if (transition) {
              that.$element[0].offsetWidth // force reflow
            }
  
            that.$element
              .addClass('in')
              .attr('aria-hidden', false)
  
            that.enforceFocus()
  
            transition ?
              that.$element.one($.support.transition.end, function () { that.$element.focus().trigger('shown') }) :
              that.$element.focus().trigger('shown')
  
          })
        }
  
      , hide: function (e) {
          e && e.preventDefault()
  
          var that = this
  
          e = $.Event('hide')
  
          this.$element.trigger(e)
  
          if (!this.isShown || e.isDefaultPrevented()) return
  
          this.isShown = false
  
          this.escape()
  
          $(document).off('focusin.modal')
  
          this.$element
            .removeClass('in')
            .attr('aria-hidden', true)
  
          $.support.transition && this.$element.hasClass('fade') ?
            this.hideWithTransition() :
            this.hideModal()
        }
  
      , enforceFocus: function () {
          var that = this
          $(document).on('focusin.modal', function (e) {
            if (that.$element[0] !== e.target && !that.$element.has(e.target).length) {
              that.$element.focus()
            }
          })
        }
  
      , escape: function () {
          var that = this
          if (this.isShown && this.options.keyboard) {
            this.$element.on('keyup.dismiss.modal', function ( e ) {
              e.which == 27 && that.hide()
            })
          } else if (!this.isShown) {
            this.$element.off('keyup.dismiss.modal')
          }
        }
  
      , hideWithTransition: function () {
          var that = this
            , timeout = setTimeout(function () {
                that.$element.off($.support.transition.end)
                that.hideModal()
              }, 500)
  
          this.$element.one($.support.transition.end, function () {
            clearTimeout(timeout)
            that.hideModal()
          })
        }
  
      , hideModal: function () {
          var that = this
          this.$element.hide()
          this.backdrop(function () {
            that.removeBackdrop()
            that.$element.trigger('hidden')
          })
        }
  
      , removeBackdrop: function () {
          this.$backdrop && this.$backdrop.remove()
          this.$backdrop = null
        }
  
      , backdrop: function (callback) {
          var that = this
            , animate = this.$element.hasClass('fade') ? 'fade' : ''
  
          if (this.isShown && this.options.backdrop) {
            var doAnimate = $.support.transition && animate
  
            this.$backdrop = $('<div class="modal-backdrop ' + animate + '" />')
              .appendTo(document.body)
  
            this.$backdrop.click(
              this.options.backdrop == 'static' ?
                $.proxy(this.$element[0].focus, this.$element[0])
              : $.proxy(this.hide, this)
            )
  
            if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
  
            this.$backdrop.addClass('in')
  
            if (!callback) return
  
            doAnimate ?
              this.$backdrop.one($.support.transition.end, callback) :
              callback()
  
          } else if (!this.isShown && this.$backdrop) {
            this.$backdrop.removeClass('in')
  
            $.support.transition && this.$element.hasClass('fade')?
              this.$backdrop.one($.support.transition.end, callback) :
              callback()
  
          } else if (callback) {
            callback()
          }
        }
    }
  
  
   /* MODAL PLUGIN DEFINITION
    * ======================= */
  
    var old = $.fn.modal
  
    $.fn.modal = function (option) {
      return this.each(function () {
        var $this = $(this)
          , data = $this.data('modal')
          , options = $.extend({}, $.fn.modal.defaults, $this.data(), typeof option == 'object' && option)
        if (!data) $this.data('modal', (data = new Modal(this, options)))
        if (typeof option == 'string') data[option]()
        else if (options.show) data.show()
      })
    }
  
    $.fn.modal.defaults = {
        backdrop: true
      , keyboard: true
      , show: true
    }
  
    $.fn.modal.Constructor = Modal
  
  
   /* MODAL NO CONFLICT
    * ================= */
  
    $.fn.modal.noConflict = function () {
      $.fn.modal = old
      return this
    }
  
  
   /* MODAL DATA-API
    * ============== */
  
    $(document).on('click.modal.data-api', '[data-toggle="modal"]', function (e) {
      var $this = $(this)
        , href = $this.attr('href')
        , $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) //strip for ie7
        , option = $target.data('modal') ? 'toggle' : $.extend({ remote:!/#/.test(href) && href }, $target.data(), $this.data())
  
      e.preventDefault()
  
      $target
        .modal(option)
        .one('hide', function () {
          $this.focus()
        })
    })
  
  }(window.jQuery);
  /* ===========================================================
   * bootstrap-tooltip.js v2.3.1
   * http://twitter.github.com/bootstrap/javascript.html#tooltips
   * Inspired by the original jQuery.tipsy by Jason Frame
   * ===========================================================
   * Copyright 2012 Twitter, Inc.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   * http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   * ========================================================== */
  
  
  !function ($) {
  
    "use strict"; // jshint ;_;
  
  
   /* TOOLTIP PUBLIC CLASS DEFINITION
    * =============================== */
  
    var Tooltip = function (element, options) {
      this.init('tooltip', element, options)
    }
  
    Tooltip.prototype = {
  
      constructor: Tooltip
  
    , init: function (type, element, options) {
        var eventIn
          , eventOut
          , triggers
          , trigger
          , i
  
        this.type = type
        this.$element = $(element)
        this.options = this.getOptions(options)
        this.enabled = true
  
        triggers = this.options.trigger.split(' ')
  
        for (i = triggers.length; i--;) {
          trigger = triggers[i]
          if (trigger == 'click') {
            this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this))
          } else if (trigger != 'manual') {
            eventIn = trigger == 'hover' ? 'mouseenter' : 'focus'
            eventOut = trigger == 'hover' ? 'mouseleave' : 'blur'
            this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this))
            this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this))
          }
        }
  
        this.options.selector ?
          (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :
          this.fixTitle()
      }
  
    , getOptions: function (options) {
        options = $.extend({}, $.fn[this.type].defaults, this.$element.data(), options)
  
        if (options.delay && typeof options.delay == 'number') {
          options.delay = {
            show: options.delay
          , hide: options.delay
          }
        }
  
        return options
      }
  
    , enter: function (e) {
        var defaults = $.fn[this.type].defaults
          , options = {}
          , self
  
        this._options && $.each(this._options, function (key, value) {
          if (defaults[key] != value) options[key] = value
        }, this)
  
        self = $(e.currentTarget)[this.type](options).data(this.type)
  
        if (!self.options.delay || !self.options.delay.show) return self.show()
  
        clearTimeout(this.timeout)
        self.hoverState = 'in'
        this.timeout = setTimeout(function() {
          if (self.hoverState == 'in') self.show()
        }, self.options.delay.show)
      }
  
    , leave: function (e) {
        var self = $(e.currentTarget)[this.type](this._options).data(this.type)
  
        if (this.timeout) clearTimeout(this.timeout)
        if (!self.options.delay || !self.options.delay.hide) return self.hide()
  
        self.hoverState = 'out'
        this.timeout = setTimeout(function() {
          if (self.hoverState == 'out') self.hide()
        }, self.options.delay.hide)
      }
  
    , show: function () {
        var $tip
          , pos
          , actualWidth
          , actualHeight
          , placement
          , tp
          , e = $.Event('show')
  
        if (this.hasContent() && this.enabled) {
          this.$element.trigger(e)
          if (e.isDefaultPrevented()) return
          $tip = this.tip()
          this.setContent()
  
          if (this.options.animation) {
            $tip.addClass('fade')
          }
  
          placement = typeof this.options.placement == 'function' ?
            this.options.placement.call(this, $tip[0], this.$element[0]) :
            this.options.placement
  
          $tip
            .detach()
            .css({ top: 0, left: 0, display: 'block' })
  
          this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element)
  
          pos = this.getPosition()
  
          actualWidth = $tip[0].offsetWidth
          actualHeight = $tip[0].offsetHeight
  
          switch (placement) {
            case 'bottom':
              tp = {top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2}
              break
            case 'top':
              tp = {top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2}
              break
            case 'left':
              tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth}
              break
            case 'right':
              tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width}
              break
          }
  
          this.applyPlacement(tp, placement)
          this.$element.trigger('shown')
        }
      }
  
    , applyPlacement: function(offset, placement){
        var $tip = this.tip()
          , width = $tip[0].offsetWidth
          , height = $tip[0].offsetHeight
          , actualWidth
          , actualHeight
          , delta
          , replace
  
        $tip
          .offset(offset)
          .addClass(placement)
          .addClass('in')
  
        actualWidth = $tip[0].offsetWidth
        actualHeight = $tip[0].offsetHeight
  
        if (placement == 'top' && actualHeight != height) {
          offset.top = offset.top + height - actualHeight
          replace = true
        }
  
        if (placement == 'bottom' || placement == 'top') {
          delta = 0
  
          if (offset.left < 0){
            delta = offset.left * -2
            offset.left = 0
            $tip.offset(offset)
            actualWidth = $tip[0].offsetWidth
            actualHeight = $tip[0].offsetHeight
          }
  
          this.replaceArrow(delta - width + actualWidth, actualWidth, 'left')
        } else {
          this.replaceArrow(actualHeight - height, actualHeight, 'top')
        }
  
        if (replace) $tip.offset(offset)
      }
  
    , replaceArrow: function(delta, dimension, position){
        this
          .arrow()
          .css(position, delta ? (50 * (1 - delta / dimension) + "%") : '')
      }
  
    , setContent: function () {
        var $tip = this.tip()
          , title = this.getTitle()
  
        $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title)
        $tip.removeClass('fade in top bottom left right')
      }
  
    , hide: function () {
        var that = this
          , $tip = this.tip()
          , e = $.Event('hide')
  
        this.$element.trigger(e)
        if (e.isDefaultPrevented()) return
  
        $tip.removeClass('in')
  
        function removeWithAnimation() {
          var timeout = setTimeout(function () {
            $tip.off($.support.transition.end).detach()
          }, 500)
  
          $tip.one($.support.transition.end, function () {
            clearTimeout(timeout)
            $tip.detach()
          })
        }
  
        $.support.transition && this.$tip.hasClass('fade') ?
          removeWithAnimation() :
          $tip.detach()
  
        this.$element.trigger('hidden')
  
        return this
      }
  
    , fixTitle: function () {
        var $e = this.$element
        if ($e.attr('title') || typeof($e.attr('data-original-title')) != 'string') {
          $e.attr('data-original-title', $e.attr('title') || '').attr('title', '')
        }
      }
  
    , hasContent: function () {
        return this.getTitle()
      }
  
    , getPosition: function () {
        var el = this.$element[0]
        return $.extend({}, (typeof el.getBoundingClientRect == 'function') ? el.getBoundingClientRect() : {
          width: el.offsetWidth
        , height: el.offsetHeight
        }, this.$element.offset())
      }
  
    , getTitle: function () {
        var title
          , $e = this.$element
          , o = this.options
  
        title = $e.attr('data-original-title')
          || (typeof o.title == 'function' ? o.title.call($e[0]) :  o.title)
  
        return title
      }
  
    , tip: function () {
        return this.$tip = this.$tip || $(this.options.template)
      }
  
    , arrow: function(){
        return this.$arrow = this.$arrow || this.tip().find(".tooltip-arrow")
      }
  
    , validate: function () {
        if (!this.$element[0].parentNode) {
          this.hide()
          this.$element = null
          this.options = null
        }
      }
  
    , enable: function () {
        this.enabled = true
      }
  
    , disable: function () {
        this.enabled = false
      }
  
    , toggleEnabled: function () {
        this.enabled = !this.enabled
      }
  
    , toggle: function (e) {
        var self = e ? $(e.currentTarget)[this.type](this._options).data(this.type) : this
        self.tip().hasClass('in') ? self.hide() : self.show()
      }
  
    , destroy: function () {
        this.hide().$element.off('.' + this.type).removeData(this.type)
      }
  
    }
  
  
   /* TOOLTIP PLUGIN DEFINITION
    * ========================= */
  
    var old = $.fn.tooltip
  
    $.fn.tooltip = function ( option ) {
      return this.each(function () {
        var $this = $(this)
          , data = $this.data('tooltip')
          , options = typeof option == 'object' && option
        if (!data) $this.data('tooltip', (data = new Tooltip(this, options)))
        if (typeof option == 'string') data[option]()
      })
    }
  
    $.fn.tooltip.Constructor = Tooltip
  
    $.fn.tooltip.defaults = {
      animation: true
    , placement: 'top'
    , selector: false
    , template: '<div class="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>'
    , trigger: 'hover focus'
    , title: ''
    , delay: 0
    , html: false
    , container: false
    }
  
  
   /* TOOLTIP NO CONFLICT
    * =================== */
  
    $.fn.tooltip.noConflict = function () {
      $.fn.tooltip = old
      return this
    }
  
  }(window.jQuery);
  /* ===========================================================
   * bootstrap-popover.js v2.3.1
   * http://twitter.github.com/bootstrap/javascript.html#popovers
   * ===========================================================
   * Copyright 2012 Twitter, Inc.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   * http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   * =========================================================== */
  
  
  !function ($) {
  
    "use strict"; // jshint ;_;
  
  
   /* POPOVER PUBLIC CLASS DEFINITION
    * =============================== */
  
    var Popover = function (element, options) {
      this.init('popover', element, options)
    }
  
  
    /* NOTE: POPOVER EXTENDS BOOTSTRAP-TOOLTIP.js
       ========================================== */
  
    Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype, {
  
      constructor: Popover
  
    , setContent: function () {
        var $tip = this.tip()
          , title = this.getTitle()
          , content = this.getContent()
  
        $tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title)
        $tip.find('.popover-content')[this.options.html ? 'html' : 'text'](content)
  
        $tip.removeClass('fade top bottom left right in')
      }
  
    , hasContent: function () {
        return this.getTitle() || this.getContent()
      }
  
    , getContent: function () {
        var content
          , $e = this.$element
          , o = this.options
  
        content = (typeof o.content == 'function' ? o.content.call($e[0]) :  o.content)
          || $e.attr('data-content')
  
        return content
      }
  
    , tip: function () {
        if (!this.$tip) {
          this.$tip = $(this.options.template)
        }
        return this.$tip
      }
  
    , destroy: function () {
        this.hide().$element.off('.' + this.type).removeData(this.type)
      }
  
    })
  
  
   /* POPOVER PLUGIN DEFINITION
    * ======================= */
  
    var old = $.fn.popover
  
    $.fn.popover = function (option) {
      return this.each(function () {
        var $this = $(this)
          , data = $this.data('popover')
          , options = typeof option == 'object' && option
        if (!data) $this.data('popover', (data = new Popover(this, options)))
        if (typeof option == 'string') data[option]()
      })
    }
  
    $.fn.popover.Constructor = Popover
  
    $.fn.popover.defaults = $.extend({} , $.fn.tooltip.defaults, {
      placement: 'right'
    , trigger: 'click'
    , content: ''
    , template: '<div class="popover"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'
    })
  
  
   /* POPOVER NO CONFLICT
    * =================== */
  
    $.fn.popover.noConflict = function () {
      $.fn.popover = old
      return this
    }
  
  }(window.jQuery);
  /* =============================================================
   * bootstrap-scrollspy.js v2.3.1
   * http://twitter.github.com/bootstrap/javascript.html#scrollspy
   * =============================================================
   * Copyright 2012 Twitter, Inc.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   * http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   * ============================================================== */
  
  
  !function ($) {
  
    "use strict"; // jshint ;_;
  
  
   /* SCROLLSPY CLASS DEFINITION
    * ========================== */
  
    function ScrollSpy(element, options) {
      var process = $.proxy(this.process, this)
        , $element = $(element).is('body') ? $(window) : $(element)
        , href
      this.options = $.extend({}, $.fn.scrollspy.defaults, options)
      this.$scrollElement = $element.on('scroll.scroll-spy.data-api', process)
      this.selector = (this.options.target
        || ((href = $(element).attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
        || '') + ' .nav li > a'
      this.$body = $('body')
      this.refresh()
      this.process()
    }
  
    ScrollSpy.prototype = {
  
        constructor: ScrollSpy
  
      , refresh: function () {
          var self = this
            , $targets
  
          this.offsets = $([])
          this.targets = $([])
  
          $targets = this.$body
            .find(this.selector)
            .map(function () {
              var $el = $(this)
                , href = $el.data('target') || $el.attr('href')
                , $href = /^#\w/.test(href) && $(href)
              return ( $href
                && $href.length
                && [[ $href.position().top + (!$.isWindow(self.$scrollElement.get(0)) && self.$scrollElement.scrollTop()), href ]] ) || null
            })
            .sort(function (a, b) { return a[0] - b[0] })
            .each(function () {
              self.offsets.push(this[0])
              self.targets.push(this[1])
            })
        }
  
      , process: function () {
          var scrollTop = this.$scrollElement.scrollTop() + this.options.offset
            , scrollHeight = this.$scrollElement[0].scrollHeight || this.$body[0].scrollHeight
            , maxScroll = scrollHeight - this.$scrollElement.height()
            , offsets = this.offsets
            , targets = this.targets
            , activeTarget = this.activeTarget
            , i
  
          if (scrollTop >= maxScroll) {
            return activeTarget != (i = targets.last()[0])
              && this.activate ( i )
          }
  
          for (i = offsets.length; i--;) {
            activeTarget != targets[i]
              && scrollTop >= offsets[i]
              && (!offsets[i + 1] || scrollTop <= offsets[i + 1])
              && this.activate( targets[i] )
          }
        }
  
      , activate: function (target) {
          var active
            , selector
  
          this.activeTarget = target
  
          $(this.selector)
            .parent('.active')
            .removeClass('active')
  
          selector = this.selector
            + '[data-target="' + target + '"],'
            + this.selector + '[href="' + target + '"]'
  
          active = $(selector)
            .parent('li')
            .addClass('active')
  
          if (active.parent('.dropdown-menu').length)  {
            active = active.closest('li.dropdown').addClass('active')
          }
  
          active.trigger('activate')
        }
  
    }
  
  
   /* SCROLLSPY PLUGIN DEFINITION
    * =========================== */
  
    var old = $.fn.scrollspy
  
    $.fn.scrollspy = function (option) {
      return this.each(function () {
        var $this = $(this)
          , data = $this.data('scrollspy')
          , options = typeof option == 'object' && option
        if (!data) $this.data('scrollspy', (data = new ScrollSpy(this, options)))
        if (typeof option == 'string') data[option]()
      })
    }
  
    $.fn.scrollspy.Constructor = ScrollSpy
  
    $.fn.scrollspy.defaults = {
      offset: 10
    }
  
  
   /* SCROLLSPY NO CONFLICT
    * ===================== */
  
    $.fn.scrollspy.noConflict = function () {
      $.fn.scrollspy = old
      return this
    }
  
  
   /* SCROLLSPY DATA-API
    * ================== */
  
    $(window).on('load', function () {
      $('[data-spy="scroll"]').each(function () {
        var $spy = $(this)
        $spy.scrollspy($spy.data())
      })
    })
  
  }(window.jQuery);/* ========================================================
   * bootstrap-tab.js v2.3.1
   * http://twitter.github.com/bootstrap/javascript.html#tabs
   * ========================================================
   * Copyright 2012 Twitter, Inc.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   * http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   * ======================================================== */
  
  
  !function ($) {
  
    "use strict"; // jshint ;_;
  
  
   /* TAB CLASS DEFINITION
    * ==================== */
  
    var Tab = function (element) {
      this.element = $(element)
    }
  
    Tab.prototype = {
  
      constructor: Tab
  
    , show: function () {
        var $this = this.element
          , $ul = $this.closest('ul:not(.dropdown-menu)')
          , selector = $this.attr('data-target')
          , previous
          , $target
          , e
  
        if (!selector) {
          selector = $this.attr('href')
          selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
        }
  
        if ( $this.parent('li').hasClass('active') ) return
  
        previous = $ul.find('.active:last a')[0]
  
        e = $.Event('show', {
          relatedTarget: previous
        })
  
        $this.trigger(e)
  
        if (e.isDefaultPrevented()) return
  
        $target = $(selector)
  
        this.activate($this.parent('li'), $ul)
        this.activate($target, $target.parent(), function () {
          $this.trigger({
            type: 'shown'
          , relatedTarget: previous
          })
        })
      }
  
    , activate: function ( element, container, callback) {
        var $active = container.find('> .active')
          , transition = callback
              && $.support.transition
              && $active.hasClass('fade')
  
        function next() {
          $active
            .removeClass('active')
            .find('> .dropdown-menu > .active')
            .removeClass('active')
  
          element.addClass('active')
  
          if (transition) {
            element[0].offsetWidth // reflow for transition
            element.addClass('in')
          } else {
            element.removeClass('fade')
          }
  
          if ( element.parent('.dropdown-menu') ) {
            element.closest('li.dropdown').addClass('active')
          }
  
          callback && callback()
        }
  
        transition ?
          $active.one($.support.transition.end, next) :
          next()
  
        $active.removeClass('in')
      }
    }
  
  
   /* TAB PLUGIN DEFINITION
    * ===================== */
  
    var old = $.fn.tab
  
    $.fn.tab = function ( option ) {
      return this.each(function () {
        var $this = $(this)
          , data = $this.data('tab')
        if (!data) $this.data('tab', (data = new Tab(this)))
        if (typeof option == 'string') data[option]()
      })
    }
  
    $.fn.tab.Constructor = Tab
  
  
   /* TAB NO CONFLICT
    * =============== */
  
    $.fn.tab.noConflict = function () {
      $.fn.tab = old
      return this
    }
  
  
   /* TAB DATA-API
    * ============ */
  
    $(document).on('click.tab.data-api', '[data-toggle="tab"], [data-toggle="pill"]', function (e) {
      e.preventDefault()
      $(this).tab('show')
    })
  
  }(window.jQuery);/* =============================================================
   * bootstrap-typeahead.js v2.3.1
   * http://twitter.github.com/bootstrap/javascript.html#typeahead
   * =============================================================
   * Copyright 2012 Twitter, Inc.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   * http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   * ============================================================ */
  
  
  !function($){
  
    "use strict"; // jshint ;_;
  
  
   /* TYPEAHEAD PUBLIC CLASS DEFINITION
    * ================================= */
  
    var Typeahead = function (element, options) {
      this.$element = $(element)
      this.options = $.extend({}, $.fn.typeahead.defaults, options)
      this.matcher = this.options.matcher || this.matcher
      this.sorter = this.options.sorter || this.sorter
      this.highlighter = this.options.highlighter || this.highlighter
      this.updater = this.options.updater || this.updater
      this.source = this.options.source
      this.$menu = $(this.options.menu)
      this.shown = false
      this.listen()
    }
  
    Typeahead.prototype = {
  
      constructor: Typeahead
  
    , select: function () {
        var val = this.$menu.find('.active').attr('data-value')
        this.$element
          .val(this.updater(val))
          .change()
        return this.hide()
      }
  
    , updater: function (item) {
        return item
      }
  
    , show: function () {
        var pos = $.extend({}, this.$element.position(), {
          height: this.$element[0].offsetHeight
        })
  
        this.$menu
          .insertAfter(this.$element)
          .css({
            top: pos.top + pos.height
          , left: pos.left
          })
          .show()
  
        this.shown = true
        return this
      }
  
    , hide: function () {
        this.$menu.hide()
        this.shown = false
        return this
      }
  
    , lookup: function (event) {
        var items
  
        this.query = this.$element.val()
  
        if (!this.query || this.query.length < this.options.minLength) {
          return this.shown ? this.hide() : this
        }
  
        items = $.isFunction(this.source) ? this.source(this.query, $.proxy(this.process, this)) : this.source
  
        return items ? this.process(items) : this
      }
  
    , process: function (items) {
        var that = this
  
        items = $.grep(items, function (item) {
          return that.matcher(item)
        })
  
        items = this.sorter(items)
  
        if (!items.length) {
          return this.shown ? this.hide() : this
        }
  
        return this.render(items.slice(0, this.options.items)).show()
      }
  
    , matcher: function (item) {
        return ~item.toLowerCase().indexOf(this.query.toLowerCase())
      }
  
    , sorter: function (items) {
        var beginswith = []
          , caseSensitive = []
          , caseInsensitive = []
          , item
  
        while (item = items.shift()) {
          if (!item.toLowerCase().indexOf(this.query.toLowerCase())) beginswith.push(item)
          else if (~item.indexOf(this.query)) caseSensitive.push(item)
          else caseInsensitive.push(item)
        }
  
        return beginswith.concat(caseSensitive, caseInsensitive)
      }
  
    , highlighter: function (item) {
        var query = this.query.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&')
        return item.replace(new RegExp('(' + query + ')', 'ig'), function ($1, match) {
          return '<strong>' + match + '</strong>'
        })
      }
  
    , render: function (items) {
        var that = this
  
        items = $(items).map(function (i, item) {
          i = $(that.options.item).attr('data-value', item)
          i.find('a').html(that.highlighter(item))
          return i[0]
        })
  
        items.first().addClass('active')
        this.$menu.html(items)
        return this
      }
  
    , next: function (event) {
        var active = this.$menu.find('.active').removeClass('active')
          , next = active.next()
  
        if (!next.length) {
          next = $(this.$menu.find('li')[0])
        }
  
        next.addClass('active')
      }
  
    , prev: function (event) {
        var active = this.$menu.find('.active').removeClass('active')
          , prev = active.prev()
  
        if (!prev.length) {
          prev = this.$menu.find('li').last()
        }
  
        prev.addClass('active')
      }
  
    , listen: function () {
        this.$element
          .on('focus',    $.proxy(this.focus, this))
          .on('blur',     $.proxy(this.blur, this))
          .on('keypress', $.proxy(this.keypress, this))
          .on('keyup',    $.proxy(this.keyup, this))
  
        if (this.eventSupported('keydown')) {
          this.$element.on('keydown', $.proxy(this.keydown, this))
        }
  
        this.$menu
          .on('click', $.proxy(this.click, this))
          .on('mouseenter', 'li', $.proxy(this.mouseenter, this))
          .on('mouseleave', 'li', $.proxy(this.mouseleave, this))
      }
  
    , eventSupported: function(eventName) {
        var isSupported = eventName in this.$element
        if (!isSupported) {
          this.$element.setAttribute(eventName, 'return;')
          isSupported = typeof this.$element[eventName] === 'function'
        }
        return isSupported
      }
  
    , move: function (e) {
        if (!this.shown) return
  
        switch(e.keyCode) {
          case 9: // tab
          case 13: // enter
          case 27: // escape
            e.preventDefault()
            break
  
          case 38: // up arrow
            e.preventDefault()
            this.prev()
            break
  
          case 40: // down arrow
            e.preventDefault()
            this.next()
            break
        }
  
        e.stopPropagation()
      }
  
    , keydown: function (e) {
        this.suppressKeyPressRepeat = ~$.inArray(e.keyCode, [40,38,9,13,27])
        this.move(e)
      }
  
    , keypress: function (e) {
        if (this.suppressKeyPressRepeat) return
        this.move(e)
      }
  
    , keyup: function (e) {
        switch(e.keyCode) {
          case 40: // down arrow
          case 38: // up arrow
          case 16: // shift
          case 17: // ctrl
          case 18: // alt
            break
  
          case 9: // tab
          case 13: // enter
            if (!this.shown) return
            this.select()
            break
  
          case 27: // escape
            if (!this.shown) return
            this.hide()
            break
  
          default:
            this.lookup()
        }
  
        e.stopPropagation()
        e.preventDefault()
    }
  
    , focus: function (e) {
        this.focused = true
      }
  
    , blur: function (e) {
        this.focused = false
        if (!this.mousedover && this.shown) this.hide()
      }
  
    , click: function (e) {
        e.stopPropagation()
        e.preventDefault()
        this.select()
        this.$element.focus()
      }
  
    , mouseenter: function (e) {
        this.mousedover = true
        this.$menu.find('.active').removeClass('active')
        $(e.currentTarget).addClass('active')
      }
  
    , mouseleave: function (e) {
        this.mousedover = false
        if (!this.focused && this.shown) this.hide()
      }
  
    }
  
  
    /* TYPEAHEAD PLUGIN DEFINITION
     * =========================== */
  
    var old = $.fn.typeahead
  
    $.fn.typeahead = function (option) {
      return this.each(function () {
        var $this = $(this)
          , data = $this.data('typeahead')
          , options = typeof option == 'object' && option
        if (!data) $this.data('typeahead', (data = new Typeahead(this, options)))
        if (typeof option == 'string') data[option]()
      })
    }
  
    $.fn.typeahead.defaults = {
      source: []
    , items: 8
    , menu: '<ul class="typeahead dropdown-menu"></ul>'
    , item: '<li><a href="#"></a></li>'
    , minLength: 1
    }
  
    $.fn.typeahead.Constructor = Typeahead
  
  
   /* TYPEAHEAD NO CONFLICT
    * =================== */
  
    $.fn.typeahead.noConflict = function () {
      $.fn.typeahead = old
      return this
    }
  
  
   /* TYPEAHEAD DATA-API
    * ================== */
  
    $(document).on('focus.typeahead.data-api', '[data-provide="typeahead"]', function (e) {
      var $this = $(this)
      if ($this.data('typeahead')) return
      $this.typeahead($this.data())
    })
  
  }(window.jQuery);
  /* ==========================================================
   * bootstrap-affix.js v2.3.1
   * http://twitter.github.com/bootstrap/javascript.html#affix
   * ==========================================================
   * Copyright 2012 Twitter, Inc.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   * http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   * ========================================================== */
  
  
  !function ($) {
  
    "use strict"; // jshint ;_;
  
  
   /* AFFIX CLASS DEFINITION
    * ====================== */
  
    var Affix = function (element, options) {
      this.options = $.extend({}, $.fn.affix.defaults, options)
      this.$window = $(window)
        .on('scroll.affix.data-api', $.proxy(this.checkPosition, this))
        .on('click.affix.data-api',  $.proxy(function () { setTimeout($.proxy(this.checkPosition, this), 1) }, this))
      this.$element = $(element)
      this.checkPosition()
    }
  
    Affix.prototype.checkPosition = function () {
      if (!this.$element.is(':visible')) return
  
      var scrollHeight = $(document).height()
        , scrollTop = this.$window.scrollTop()
        , position = this.$element.offset()
        , offset = this.options.offset
        , offsetBottom = offset.bottom
        , offsetTop = offset.top
        , reset = 'affix affix-top affix-bottom'
        , affix
  
      if (typeof offset != 'object') offsetBottom = offsetTop = offset
      if (typeof offsetTop == 'function') offsetTop = offset.top()
      if (typeof offsetBottom == 'function') offsetBottom = offset.bottom()
  
      affix = this.unpin != null && (scrollTop + this.unpin <= position.top) ?
        false    : offsetBottom != null && (position.top + this.$element.height() >= scrollHeight - offsetBottom) ?
        'bottom' : offsetTop != null && scrollTop <= offsetTop ?
        'top'    : false
  
      if (this.affixed === affix) return
  
      this.affixed = affix
      this.unpin = affix == 'bottom' ? position.top - scrollTop : null
  
      this.$element.removeClass(reset).addClass('affix' + (affix ? '-' + affix : ''))
    }
  
  
   /* AFFIX PLUGIN DEFINITION
    * ======================= */
  
    var old = $.fn.affix
  
    $.fn.affix = function (option) {
      return this.each(function () {
        var $this = $(this)
          , data = $this.data('affix')
          , options = typeof option == 'object' && option
        if (!data) $this.data('affix', (data = new Affix(this, options)))
        if (typeof option == 'string') data[option]()
      })
    }
  
    $.fn.affix.Constructor = Affix
  
    $.fn.affix.defaults = {
      offset: 0
    }
  
  
   /* AFFIX NO CONFLICT
    * ================= */
  
    $.fn.affix.noConflict = function () {
      $.fn.affix = old
      return this
    }
  
  
   /* AFFIX DATA-API
    * ============== */
  
    $(window).on('load', function () {
      $('[data-spy="affix"]').each(function () {
        var $spy = $(this)
          , data = $spy.data()
  
        data.offset = data.offset || {}
  
        data.offsetBottom && (data.offset.bottom = data.offsetBottom)
        data.offsetTop && (data.offset.top = data.offsetTop)
  
        $spy.affix(data)
      })
    })
  
  
  }(window.jQuery);/**
   * A class to parse color values
   * @author Stoyan Stefanov <sstoo@gmail.com>
   * @link   http://www.phpied.com/rgb-color-parser-in-javascript/
   * @license Use it if you like it
   */
  function RGBColor(color_string)
  {
      this.ok = false;
  
      // strip any leading #
      if (color_string.charAt(0) == '#') { // remove # if any
          color_string = color_string.substr(1,6);
      }
  
      color_string = color_string.replace(/ /g,'');
      color_string = color_string.toLowerCase();
  
      // before getting into regexps, try simple matches
      // and overwrite the input
      var simple_colors = {
          aliceblue: 'f0f8ff',
          antiquewhite: 'faebd7',
          aqua: '00ffff',
          aquamarine: '7fffd4',
          azure: 'f0ffff',
          beige: 'f5f5dc',
          bisque: 'ffe4c4',
          black: '000000',
          blanchedalmond: 'ffebcd',
          blue: '0000ff',
          blueviolet: '8a2be2',
          brown: 'a52a2a',
          burlywood: 'deb887',
          cadetblue: '5f9ea0',
          chartreuse: '7fff00',
          chocolate: 'd2691e',
          coral: 'ff7f50',
          cornflowerblue: '6495ed',
          cornsilk: 'fff8dc',
          crimson: 'dc143c',
          cyan: '00ffff',
          darkblue: '00008b',
          darkcyan: '008b8b',
          darkgoldenrod: 'b8860b',
          darkgray: 'a9a9a9',
          darkgreen: '006400',
          darkkhaki: 'bdb76b',
          darkmagenta: '8b008b',
          darkolivegreen: '556b2f',
          darkorange: 'ff8c00',
          darkorchid: '9932cc',
          darkred: '8b0000',
          darksalmon: 'e9967a',
          darkseagreen: '8fbc8f',
          darkslateblue: '483d8b',
          darkslategray: '2f4f4f',
          darkturquoise: '00ced1',
          darkviolet: '9400d3',
          deeppink: 'ff1493',
          deepskyblue: '00bfff',
          dimgray: '696969',
          dodgerblue: '1e90ff',
          feldspar: 'd19275',
          firebrick: 'b22222',
          floralwhite: 'fffaf0',
          forestgreen: '228b22',
          fuchsia: 'ff00ff',
          gainsboro: 'dcdcdc',
          ghostwhite: 'f8f8ff',
          gold: 'ffd700',
          goldenrod: 'daa520',
          gray: '808080',
          green: '008000',
          greenyellow: 'adff2f',
          honeydew: 'f0fff0',
          hotpink: 'ff69b4',
          indianred : 'cd5c5c',
          indigo : '4b0082',
          ivory: 'fffff0',
          khaki: 'f0e68c',
          lavender: 'e6e6fa',
          lavenderblush: 'fff0f5',
          lawngreen: '7cfc00',
          lemonchiffon: 'fffacd',
          lightblue: 'add8e6',
          lightcoral: 'f08080',
          lightcyan: 'e0ffff',
          lightgoldenrodyellow: 'fafad2',
          lightgrey: 'd3d3d3',
          lightgreen: '90ee90',
          lightpink: 'ffb6c1',
          lightsalmon: 'ffa07a',
          lightseagreen: '20b2aa',
          lightskyblue: '87cefa',
          lightslateblue: '8470ff',
          lightslategray: '778899',
          lightsteelblue: 'b0c4de',
          lightyellow: 'ffffe0',
          lime: '00ff00',
          limegreen: '32cd32',
          linen: 'faf0e6',
          magenta: 'ff00ff',
          maroon: '800000',
          mediumaquamarine: '66cdaa',
          mediumblue: '0000cd',
          mediumorchid: 'ba55d3',
          mediumpurple: '9370d8',
          mediumseagreen: '3cb371',
          mediumslateblue: '7b68ee',
          mediumspringgreen: '00fa9a',
          mediumturquoise: '48d1cc',
          mediumvioletred: 'c71585',
          midnightblue: '191970',
          mintcream: 'f5fffa',
          mistyrose: 'ffe4e1',
          moccasin: 'ffe4b5',
          navajowhite: 'ffdead',
          navy: '000080',
          oldlace: 'fdf5e6',
          olive: '808000',
          olivedrab: '6b8e23',
          orange: 'ffa500',
          orangered: 'ff4500',
          orchid: 'da70d6',
          palegoldenrod: 'eee8aa',
          palegreen: '98fb98',
          paleturquoise: 'afeeee',
          palevioletred: 'd87093',
          papayawhip: 'ffefd5',
          peachpuff: 'ffdab9',
          peru: 'cd853f',
          pink: 'ffc0cb',
          plum: 'dda0dd',
          powderblue: 'b0e0e6',
          purple: '800080',
          red: 'ff0000',
          rosybrown: 'bc8f8f',
          royalblue: '4169e1',
          saddlebrown: '8b4513',
          salmon: 'fa8072',
          sandybrown: 'f4a460',
          seagreen: '2e8b57',
          seashell: 'fff5ee',
          sienna: 'a0522d',
          silver: 'c0c0c0',
          skyblue: '87ceeb',
          slateblue: '6a5acd',
          slategray: '708090',
          snow: 'fffafa',
          springgreen: '00ff7f',
          steelblue: '4682b4',
          tan: 'd2b48c',
          teal: '008080',
          thistle: 'd8bfd8',
          tomato: 'ff6347',
          turquoise: '40e0d0',
          violet: 'ee82ee',
          violetred: 'd02090',
          wheat: 'f5deb3',
          white: 'ffffff',
          whitesmoke: 'f5f5f5',
          yellow: 'ffff00',
          yellowgreen: '9acd32'
      };
      for (var key in simple_colors) {
          if (color_string == key) {
              color_string = simple_colors[key];
          }
      }
      // emd of simple type-in colors
  
      // array of color definition objects
      var color_defs = [
          {
              re: /^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/,
              example: ['rgb(123, 234, 45)', 'rgb(255,234,245)'],
              process: function (bits){
                  return [
                      parseInt(bits[1]),
                      parseInt(bits[2]),
                      parseInt(bits[3])
                  ];
              }
          },
          {
              re: /^(\w{2})(\w{2})(\w{2})$/,
              example: ['#00ff00', '336699'],
              process: function (bits){
                  return [
                      parseInt(bits[1], 16),
                      parseInt(bits[2], 16),
                      parseInt(bits[3], 16)
                  ];
              }
          },
          {
              re: /^(\w{1})(\w{1})(\w{1})$/,
              example: ['#fb0', 'f0f'],
              process: function (bits){
                  return [
                      parseInt(bits[1] + bits[1], 16),
                      parseInt(bits[2] + bits[2], 16),
                      parseInt(bits[3] + bits[3], 16)
                  ];
              }
          }
      ];
  
      // search through the definitions to find a match
      for (var i = 0; i < color_defs.length; i++) {
          var re = color_defs[i].re;
          var processor = color_defs[i].process;
          var bits = re.exec(color_string);
          if (bits) {
              channels = processor(bits);
              this.r = channels[0];
              this.g = channels[1];
              this.b = channels[2];
              this.ok = true;
          }
  
      }
  
      // validate/cleanup values
      this.r = (this.r < 0 || isNaN(this.r)) ? 0 : ((this.r > 255) ? 255 : this.r);
      this.g = (this.g < 0 || isNaN(this.g)) ? 0 : ((this.g > 255) ? 255 : this.g);
      this.b = (this.b < 0 || isNaN(this.b)) ? 0 : ((this.b > 255) ? 255 : this.b);
  
      // some getters
      this.toRGB = function () {
          return 'rgb(' + this.r + ', ' + this.g + ', ' + this.b + ')';
      }
      this.toHex = function () {
          var r = this.r.toString(16);
          var g = this.g.toString(16);
          var b = this.b.toString(16);
          if (r.length == 1) r = '0' + r;
          if (g.length == 1) g = '0' + g;
          if (b.length == 1) b = '0' + b;
          return '#' + r + g + b;
      }
  
      // help
      this.getHelpXML = function () {
  
          var examples = new Array();
          // add regexps
          for (var i = 0; i < color_defs.length; i++) {
              var example = color_defs[i].example;
              for (var j = 0; j < example.length; j++) {
                  examples[examples.length] = example[j];
              }
          }
          // add type-in colors
          for (var sc in simple_colors) {
              examples[examples.length] = sc;
          }
  
          var xml = document.createElement('ul');
          xml.setAttribute('id', 'rgbcolor-examples');
          for (var i = 0; i < examples.length; i++) {
              try {
                  var list_item = document.createElement('li');
                  var list_color = new RGBColor(examples[i]);
                  var example_div = document.createElement('div');
                  example_div.style.cssText =
                          'margin: 3px; '
                          + 'border: 1px solid black; '
                          + 'background:' + list_color.toHex() + '; '
                          + 'color:' + list_color.toHex()
                  ;
                  example_div.appendChild(document.createTextNode('test'));
                  var list_item_value = document.createTextNode(
                      ' ' + examples[i] + ' -> ' + list_color.toRGB() + ' -> ' + list_color.toHex()
                  );
                  list_item.appendChild(example_div);
                  list_item.appendChild(list_item_value);
                  xml.appendChild(list_item);
  
              } catch(e){}
          }
          return xml;
  
      }
  
  }
  
  /*
  Copyright (c) 2012 Tai-Jin Lee http://www.taijinlee.com
  
  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.
  */
  
  (function() {
  
    // Baseline setup
    // --------------
  
    // Establish the root object, `window` in the browser, or `global` on the server.
    var root = this;
  
    // Save the previous value of the `humanize` variable.
    var previousHumanize = root.humanize;
  
    var humanize = {};
  
    if (typeof exports !== 'undefined') {
      if (typeof module !== 'undefined' && module.exports) {
        exports = module.exports = humanize;
      }
      exports.humanize = humanize;
    } else {
      if (typeof define === 'function' && define.amd) {
        define('humanize', function() {
          return humanize;
        });
      }
      root.humanize = humanize;
    }
  
    humanize.noConflict = function() {
      root.humanize = previousHumanize;
      return this;
    };
  
    humanize.pad = function(str, count, padChar, type) {
      str += '';
      if (!padChar) {
        padChar = ' ';
      } else if (padChar.length > 1) {
        padChar = padChar.charAt(0);
      }
      type = (type === undefined) ? 'left' : 'right';
  
      if (type === 'right') {
        while (str.length < count) {
          str = str + padChar;
        }
      } else {
        // default to left
        while (str.length < count) {
          str = padChar + str;
        }
      }
  
      return str;
    };
  
    // gets current unix time
    humanize.time = function() {
      return new Date().getTime() / 1000;
    };
  
    /**
     * PHP-inspired date
     */
  
                          /*  jan  feb  mar  apr  may  jun  jul  aug  sep  oct  nov  dec */
    var dayTableCommon = [ 0,   0,  31,  59,  90, 120, 151, 181, 212, 243, 273, 304, 334 ];
    var dayTableLeap   = [ 0,   0,  31,  60,  91, 121, 152, 182, 213, 244, 274, 305, 335 ];
    // var mtable_common[13] = {  0,  31,  28,  31,  30,  31,  30,  31,  31,  30,  31,  30,  31 };
    // static int ml_table_leap[13]   = {  0,  31,  29,  31,  30,  31,  30,  31,  31,  30,  31,  30,  31 };
  
  
    humanize.date = function(format, timestamp) {
      var jsdate = ((timestamp === undefined) ? new Date() : // Not provided
                    (timestamp instanceof Date) ? new Date(timestamp) : // JS Date()
                    new Date(timestamp * 1000) // UNIX timestamp (auto-convert to int)
                   );
  
      var formatChr = /\\?([a-z])/gi;
      var formatChrCb = function (t, s) {
        return f[t] ? f[t]() : s;
      };
  
      var shortDayTxt = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
      var monthTxt = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
  
      var f = {
        /* Day */
        // Day of month w/leading 0; 01..31
        d: function () { return humanize.pad(f.j(), 2, '0'); },
  
        // Shorthand day name; Mon..Sun
        D: function () { return f.l().slice(0, 3); },
  
        // Day of month; 1..31
        j: function () { return jsdate.getDate(); },
  
        // Full day name; Monday..Sunday
        l: function () { return shortDayTxt[f.w()]; },
  
        // ISO-8601 day of week; 1[Mon]..7[Sun]
        N: function () { return f.w() || 7; },
  
        // Ordinal suffix for day of month; st, nd, rd, th
        S: function () {
          var j = f.j();
          return j > 4 && j < 21 ? 'th' : {1: 'st', 2: 'nd', 3: 'rd'}[j % 10] || 'th';
        },
  
        // Day of week; 0[Sun]..6[Sat]
        w: function () { return jsdate.getDay(); },
  
        // Day of year; 0..365
        z: function () {
          return (f.L() ? dayTableLeap[f.n()] : dayTableCommon[f.n()]) + f.j() - 1;
        },
  
        /* Week */
        // ISO-8601 week number
        W: function () {
          // days between midweek of this week and jan 4
          // (f.z() - f.N() + 1 + 3.5) - 3
          var midWeekDaysFromJan4 = f.z() - f.N() + 1.5;
          // 1 + number of weeks + rounded week
          return humanize.pad(1 + Math.floor(Math.abs(midWeekDaysFromJan4) / 7) + (midWeekDaysFromJan4 % 7 > 3.5 ? 1 : 0), 2, '0');
        },
  
        /* Month */
        // Full month name; January..December
        F: function () { return monthTxt[jsdate.getMonth()]; },
  
        // Month w/leading 0; 01..12
        m: function () { return humanize.pad(f.n(), 2, '0'); },
  
        // Shorthand month name; Jan..Dec
        M: function () { return f.F().slice(0, 3); },
  
        // Month; 1..12
        n: function () { return jsdate.getMonth() + 1; },
  
        // Days in month; 28..31
        t: function () { return (new Date(f.Y(), f.n(), 0)).getDate(); },
  
        /* Year */
        // Is leap year?; 0 or 1
        L: function () { return new Date(f.Y(), 1, 29).getMonth() === 1 ? 1 : 0; },
  
        // ISO-8601 year
        o: function () {
          var n = f.n();
          var W = f.W();
          return f.Y() + (n === 12 && W < 9 ? -1 : n === 1 && W > 9);
        },
  
        // Full year; e.g. 1980..2010
        Y: function () { return jsdate.getFullYear(); },
  
        // Last two digits of year; 00..99
        y: function () { return (String(f.Y())).slice(-2); },
  
        /* Time */
        // am or pm
        a: function () { return jsdate.getHours() > 11 ? 'pm' : 'am'; },
  
        // AM or PM
        A: function () { return f.a().toUpperCase(); },
  
        // Swatch Internet time; 000..999
        B: function () {
          var unixTime = jsdate.getTime() / 1000;
          var secondsPassedToday = unixTime % 86400 + 3600; // since it's based off of UTC+1
          if (secondsPassedToday < 0) { secondsPassedToday += 86400; }
          var beats = ((secondsPassedToday) / 86.4) % 1000;
          if (unixTime < 0) {
            return Math.ceil(beats);
          }
          return Math.floor(beats);
        },
  
        // 12-Hours; 1..12
        g: function () { return f.G() % 12 || 12; },
  
        // 24-Hours; 0..23
        G: function () { return jsdate.getHours(); },
  
        // 12-Hours w/leading 0; 01..12
        h: function () { return humanize.pad(f.g(), 2, '0'); },
  
        // 24-Hours w/leading 0; 00..23
        H: function () { return humanize.pad(f.G(), 2, '0'); },
  
        // Minutes w/leading 0; 00..59
        i: function () { return humanize.pad(jsdate.getMinutes(), 2, '0'); },
  
        // Seconds w/leading 0; 00..59
        s: function () { return humanize.pad(jsdate.getSeconds(), 2, '0'); },
  
        // Microseconds; 000000-999000
        u: function () { return humanize.pad(jsdate.getMilliseconds() * 1000, 6, '0'); },
  
        // Whether or not the date is in daylight savings time
        /*
        I: function () {
          // Compares Jan 1 minus Jan 1 UTC to Jul 1 minus Jul 1 UTC.
          // If they are not equal, then DST is observed.
          var Y = f.Y();
          return 0 + ((new Date(Y, 0) - Date.UTC(Y, 0)) !== (new Date(Y, 6) - Date.UTC(Y, 6)));
        },
        */
  
        // Difference to GMT in hour format; e.g. +0200
        O: function () {
          var tzo = jsdate.getTimezoneOffset();
          var tzoNum = Math.abs(tzo);
          return (tzo > 0 ? '-' : '+') + humanize.pad(Math.floor(tzoNum / 60) * 100 + tzoNum % 60, 4, '0');
        },
  
        // Difference to GMT w/colon; e.g. +02:00
        P: function () {
          var O = f.O();
          return (O.substr(0, 3) + ':' + O.substr(3, 2));
        },
  
        // Timezone offset in seconds (-43200..50400)
        Z: function () { return -jsdate.getTimezoneOffset() * 60; },
  
        // Full Date/Time, ISO-8601 date
        c: function () { return 'Y-m-d\\TH:i:sP'.replace(formatChr, formatChrCb); },
  
        // RFC 2822
        r: function () { return 'D, d M Y H:i:s O'.replace(formatChr, formatChrCb); },
  
        // Seconds since UNIX epoch
        U: function () { return jsdate.getTime() / 1000 || 0; }
      };    
  
      return format.replace(formatChr, formatChrCb);
    };
  
  
    /**
     * format number by adding thousands separaters and significant digits while rounding
     */
    humanize.numberFormat = function(number, decimals, decPoint, thousandsSep) {
      decimals = isNaN(decimals) ? 2 : Math.abs(decimals);
      decPoint = (decPoint === undefined) ? '.' : decPoint;
      thousandsSep = (thousandsSep === undefined) ? ',' : thousandsSep;
  
      var sign = number < 0 ? '-' : '';
      number = Math.abs(+number || 0);
  
      var intPart = parseInt(number.toFixed(decimals), 10) + '';
      var j = intPart.length > 3 ? intPart.length % 3 : 0;
  
      return sign + (j ? intPart.substr(0, j) + thousandsSep : '') + intPart.substr(j).replace(/(\d{3})(?=\d)/g, '$1' + thousandsSep) + (decimals ? decPoint + Math.abs(number - intPart).toFixed(decimals).slice(2) : '');
    };
  
  
    /**
     * For dates that are the current day or within one day, return 'today', 'tomorrow' or 'yesterday', as appropriate.
     * Otherwise, format the date using the passed in format string.
     *
     * Examples (when 'today' is 17 Feb 2007):
     * 16 Feb 2007 becomes yesterday.
     * 17 Feb 2007 becomes today.
     * 18 Feb 2007 becomes tomorrow.
     * Any other day is formatted according to given argument or the DATE_FORMAT setting if no argument is given.
     */
    humanize.naturalDay = function(timestamp, format) {
      timestamp = (timestamp === undefined) ? humanize.time() : timestamp;
      format = (format === undefined) ? 'Y-m-d' : format;
  
      var oneDay = 86400;
      var d = new Date();
      var today = (new Date(d.getFullYear(), d.getMonth(), d.getDate())).getTime() / 1000;
  
      if (timestamp < today && timestamp >= today - oneDay) {
        return 'yesterday';
      } else if (timestamp >= today && timestamp < today + oneDay) {
        return 'today';
      } else if (timestamp >= today + oneDay && timestamp < today + 2 * oneDay) {
        return 'tomorrow';
      }
  
      return humanize.date(format, timestamp);
    };
  
    /**
     * returns a string representing how many seconds, minutes or hours ago it was or will be in the future
     * Will always return a relative time, most granular of seconds to least granular of years. See unit tests for more details
     */
    humanize.relativeTime = function(timestamp) {
      timestamp = (timestamp === undefined) ? humanize.time() : timestamp;
  
      var currTime = humanize.time();
      var timeDiff = currTime - timestamp;
  
      // within 2 seconds
      if (timeDiff < 2 && timeDiff > -2) {
        return (timeDiff >= 0 ? 'just ' : '') + 'now';
      }
  
      // within a minute
      if (timeDiff < 60 && timeDiff > -60) {
        return (timeDiff >= 0 ? Math.floor(timeDiff) + ' seconds ago' : 'in ' + Math.floor(-timeDiff) + ' seconds');
      }
  
      // within 2 minutes
      if (timeDiff < 120 && timeDiff > -120) {
        return (timeDiff >= 0 ? 'about a minute ago' : 'in about a minute');
      }
  
      // within an hour
      if (timeDiff < 3600 && timeDiff > -3600) {
        return (timeDiff >= 0 ? Math.floor(timeDiff / 60) + ' minutes ago' : 'in ' + Math.floor(-timeDiff / 60) + ' minutes');
      }
  
      // within 2 hours
      if (timeDiff < 7200 && timeDiff > -7200) {
        return (timeDiff >= 0 ? 'about an hour ago' : 'in about an hour');
      }
  
      // within 24 hours
      if (timeDiff < 86400 && timeDiff > -86400) {
        return (timeDiff >= 0 ? Math.floor(timeDiff / 3600) + ' hours ago' : 'in ' + Math.floor(-timeDiff / 3600) + ' hours');
      }
  
      // within 2 days
      var days2 = 2 * 86400;
      if (timeDiff < days2 && timeDiff > -days2) {
        return (timeDiff >= 0 ? '1 day ago' : 'in 1 day');
      }
  
      // within 29 days
      var days29 = 29 * 86400;
      if (timeDiff < days29 && timeDiff > -days29) {
        return (timeDiff >= 0 ? Math.floor(timeDiff / 86400) + ' days ago' : 'in ' + Math.floor(-timeDiff / 86400) + ' days');
      }
  
      // within 60 days
      var days60 = 60 * 86400;
      if (timeDiff < days60 && timeDiff > -days60) {
        return (timeDiff >= 0 ? 'about a month ago' : 'in about a month');
      }
  
      var currTimeYears = parseInt(humanize.date('Y', currTime), 10);
      var timestampYears = parseInt(humanize.date('Y', timestamp), 10);
      var currTimeMonths = currTimeYears * 12 + parseInt(humanize.date('n', currTime), 10);
      var timestampMonths = timestampYears * 12 + parseInt(humanize.date('n', timestamp), 10);
  
      // within a year
      var monthDiff = currTimeMonths - timestampMonths;
      if (monthDiff < 12 && monthDiff > -12) {
        return (monthDiff >= 0 ? monthDiff + ' months ago' : 'in ' + (-monthDiff) + ' months');
      }
  
      var yearDiff = currTimeYears - timestampYears;
      if (yearDiff < 2 && yearDiff > -2) {
        return (yearDiff >= 0 ? 'a year ago' : 'in a year');
      }
  
      return (yearDiff >= 0 ? yearDiff + ' years ago' : 'in ' + (-yearDiff) + ' years');
    };
  
    /**
     * Converts an integer to its ordinal as a string.
     *
     * 1 becomes 1st
     * 2 becomes 2nd
     * 3 becomes 3rd etc
     */
    humanize.ordinal = function(number) {
      number = parseInt(number, 10);
      number = isNaN(number) ? 0 : number;
      var sign = number < 0 ? '-' : '';
      number = Math.abs(number);
  
      return sign + number + (number > 4 && number < 21 ? 'th' : {1: 'st', 2: 'nd', 3: 'rd'}[number % 10] || 'th');
    };
  
    /**
     * Formats the value like a 'human-readable' file size (i.e. '13 KB', '4.1 MB', '102 bytes', etc).
     *
     * For example:
     * If value is 123456789, the output would be 117.7 MB.
     */
    humanize.filesize = function(filesize, kilo, decimals, decPoint, thousandsSep) {
      kilo = (kilo === undefined) ? 1024 : kilo;
      decimals = isNaN(decimals) ? 2 : Math.abs(decimals);
      decPoint = (decPoint === undefined) ? '.' : decPoint;
      thousandsSep = (thousandsSep === undefined) ? ',' : thousandsSep;
      if (filesize <= 0) { return '0 bytes'; }
  
      var thresholds = [1];
      var units = ['bytes', 'Kb', 'Mb', 'Gb', 'Tb', 'Pb'];
      if (filesize < kilo) { return humanize.numberFormat(filesize, 0) + ' ' + units[0]; }
  
      for (var i = 1; i < units.length; i++) {
        thresholds[i] = thresholds[i-1] * kilo;
        if (filesize < thresholds[i]) {
          return humanize.numberFormat(filesize / thresholds[i-1], decimals, decPoint, thousandsSep) + ' ' + units[i-1];
        }
      }
  
      // use the last unit if we drop out to here
      return humanize.numberFormat(filesize / thresholds[units.length - 1], decimals, decPoint, thousandsSep) + ' ' + units[units.length - 1];
    };
  
    /**
     * Replaces line breaks in plain text with appropriate HTML
     * A single newline becomes an HTML line break (<br />) and a new line followed by a blank line becomes a paragraph break (</p>).
     * 
     * For example:
     * If value is Joel\nis a\n\nslug, the output will be <p>Joel<br />is a</p><p>slug</p>
     */
    humanize.linebreaks = function(str) {
      // remove beginning and ending newlines
      str = str.replace(/^([\n|\r]*)/, '');
      str = str.replace(/([\n|\r]*)$/, '');
  
      // normalize all to \n
      str = str.replace(/(\r\n|\n|\r)/g, "\n");
  
      // any consecutive new lines more than 2 gets turned into p tags
      str = str.replace(/(\n{2,})/g, '</p><p>');
  
      // any that are singletons get turned into br
      str = str.replace(/\n/g, '<br />');
      return '<p>' + str + '</p>';
    };
  
    /**
     * Converts all newlines in a piece of plain text to HTML line breaks (<br />).
     */
    humanize.nl2br = function(str) {
      return str.replace(/(\r\n|\n|\r)/g, '<br />');
    };
  
    /**
     * Truncates a string if it is longer than the specified number of characters.
     * Truncated strings will end with a translatable ellipsis sequence ('…').
     */
    humanize.truncatechars = function(string, length) {
      if (string.length <= length) { return string; }
      return string.substr(0, length) + '…';
    };
  
    /**
     * Truncates a string after a certain number of words.
     * Newlines within the string will be removed.
     */
    humanize.truncatewords = function(string, numWords) {
      var words = string.split(' ');
      if (words.length < numWords) { return string; }
      return words.slice(0, numWords).join(' ') + '…';
    };
  
  }).call(this);
  /*
   * validate.js 1.1
   * Copyright (c) 2011 Rick Harrison, http://rickharrison.me
   * validate.js is open sourced under the MIT license.
   * Portions of validate.js are inspired by CodeIgniter.
   * http://rickharrison.github.com/validate.js
   */
  
  // applied patches because original developer doesn't maintain anymore
  // https://github.com/rickharrison/validate.js/pull/36
  // https://github.com/rickharrison/validate.js/pull/31
  // https://github.com/rickharrison/validate.js/pull/41
  // -- SW.CHAE
  
  (function(window, document, undefined) {
      /*
       * If you would like an application-wide config, change these defaults.
       * Otherwise, use the setMessage() function to configure form specific messages.
       */
  
      var defaults = {
          messages: {
              required: 'The %s field is required.',
              matches: 'The %s field does not match the %s field.',
              valid_email: 'The %s field must contain a valid email address.',
              valid_emails: 'The %s field must contain all valid email addresses.',
              min_length: 'The %s field must be at least %s characters in length.',
              max_length: 'The %s field must not exceed %s characters in length.',
              exact_length: 'The %s field must be exactly %s characters in length.',
              greater_than: 'The %s field must contain a number greater than %s.',
              less_than: 'The %s field must contain a number less than %s.',
              alpha: 'The %s field must only contain alphabetical characters.',
              alpha_numeric: 'The %s field must only contain alpha-numeric characters.',
              alpha_dash: 'The %s field must only contain alpha-numeric characters, underscores, and dashes.',
              numeric: 'The %s field must contain only numbers.',
              integer: 'The %s field must contain an integer.',
              decimal: 'The %s field must contain a decimal number.',
              is_natural: 'The %s field must contain only positive numbers.',
              is_natural_no_zero: 'The %s field must contain a number greater than zero.',
              valid_ip: 'The %s field must contain a valid IP.',
              valid_base64: 'The %s field must contain a base64 string.'
          },
          callback: function(errors) {
  
          }
      };
  
      /*
       * Define the regular expressions that will be used
       */
  
      var ruleRegex = /^(.+?)\[(.+)\]$/,
          numericRegex = /^[0-9]+$/,
          integerRegex = /^\-?[0-9]+$/,
          decimalRegex = /^\-?[0-9]*\.?[0-9]+$/,
          emailRegex = /[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?/i,
          alphaRegex = /^[a-z]+$/i,
          alphaNumericRegex = /^[a-z0-9]+$/i,
          alphaDashRegex = /^[a-z0-9_-]+$/i,
          naturalRegex = /^[0-9]+$/i,
          naturalNoZeroRegex = /^[1-9][0-9]*$/i,
          ipRegex = /^((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[0-9]{1,2})\.){3}(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[0-9]{1,2})$/i,
          base64Regex = /[^a-zA-Z0-9\/\+=]/i;
  
      /*
       * The exposed public object to validate a form:
       *
       * @param formName - String - The name attribute of the form (i.e. <form name="myForm"></form>)
       * @param fields - Array - [{
       *     name: The name of the element (i.e. <input name="myField" />)
       *     display: 'Field Name'
       *     rules: required|matches[password_confirm]
       * }]
       * @param callback - Function - The callback after validation has been performed.
       *     @argument errors - An array of validation errors
       *     @argument event - The javascript event
       */
  
      var FormValidator = function(formName, fields, callback) {
          this.callback = callback || defaults.callback;
          this.errors = [];
          this.fields = {};
          this.form = document.forms[formName] || {};
          this.messages = {};
          this.handlers = {};
  
          for (var i = 0, fieldLength = fields.length; i < fieldLength; i++) {
              var field = fields[i];
  
              // If passed in incorrectly, we need to skip the field.
              if (!field.name || !field.rules) {
                  continue;
              }
  
              /*
               * Build the master fields array that has all the information needed to validate
               */
  
              this.fields[field.name] = {
                  name: field.name,
                  display: field.display || field.name,
                  rules: field.rules,
                  id: null,
                  type: null,
                  value: null,
                  checked: null
              };
          }
  
          /*
           * Attach an event callback for the form submission
           */
  
          this.form.onsubmit = (function(that) {
              return function(event) {
                  try {
                      return that._validateForm(event);
                  } catch(e) {
                      console.error(e);
                      return false;
                  }
              }
          })(this);
      };
  
      /*
       * @public
       * Sets a custom message for one of the rules
       */
  
      FormValidator.prototype.setMessage = function(rule, message) {
          this.messages[rule] = message;
  
          // return this for chaining
          return this;
      };
  
      /*
       * @public
       * Registers a callback for a custom rule (i.e. callback_username_check)
       */
  
      FormValidator.prototype.registerCallback = function(name, handler) {
          if (name && typeof name === 'string' && handler && typeof handler === 'function') {
              this.handlers[name] = handler;
          }
  
          // return this for chaining
          return this;
      };
  
      /*
       * @private
       * Runs the validation when the form is submitted.
       */
  
      FormValidator.prototype._validateForm = function(event) {
          this.errors = [];
  
          for (var key in this.fields) {
              if (this.fields.hasOwnProperty(key)) {
                  var field = this.fields[key] || {},
                      element = this.form[field.name];
  
                  if (element && element !== undefined) {
                      field.id = element.id;
                      field.type = element.type;
                      field.value = element.value;
                      field.checked = element.checked;
                  }
  
                  /*
                   * Run through the rules for each field.
                   */
  
                  this._validateField(field);
              }
          }
  
          if (typeof this.callback === 'function') {
              var callbackReturn = this.callback(this.errors, event);
              if(typeof callbackReturn === 'boolean'){
                  return callbackReturn;
              }
          }
  
          if (this.errors.length > 0) {
              if (event && event.preventDefault) {
                  event.preventDefault();
              } else {
                  // IE6 doesn't pass in an event parameter so return false
                  return false;
              }
          }
  
          return true;
      };
  
      /*
       * @private
       * Looks at the fields value and evaluates it against the given rules
       */
  
      FormValidator.prototype._validateField = function(field) {
          var rules = field.rules.split('|');
  
          /*
           * If the value is null and not required, we don't need to run through validation
           */
  
          if (field.rules.indexOf('required') === -1 && (!field.value || field.value === '' || field.value === undefined)) {
              return;
          }
  
          /*
           * Run through the rules and execute the validation methods as needed
           */
  
          for (var i = 0, ruleLength = rules.length; i < ruleLength; i++) {
              var method = rules[i],
                  param = null,
                  failed = false;
  
              /*
               * If the rule has a parameter (i.e. matches[param]) split it out
               */
  
              if (parts = ruleRegex.exec(method)) {
                  method = parts[1];
                  param = parts[2];
              }
  
              /*
               * If the hook is defined, run it to find any validation errors
               */
  
              if (typeof this._hooks[method] === 'function') {
                  if (!this._hooks[method].apply(this, [field, param])) {
                      failed = true;
                  }
              } else if (method.substring(0, 9) === 'callback_') {
                  // Custom method. Execute the handler if it was registered
                  method = method.substring(9, method.length);
  
                  if (typeof this.handlers[method] === 'function') {
                      if (this.handlers[method].apply(this, [field.value]) === false) {
                          failed = true;
                      }
                  }
              }
  
              /*
               * If the hook failed, add a message to the errors array
               */
  
              if (failed) {
                  // Make sure we have a message for this rule
                  var source = this.messages[method] || defaults.messages[method],
                      message = 'An error has occurred with the ' + field.display + ' field.';
  
                  if (source) {
                      message = source.replace('%s', field.display);
  
                      if (param) {
                          message = message.replace('%s', (this.fields[param]) ? this.fields[param].display : param);
                      }
                  }
  
                  this.errors.push({
                      id: field.id,
                      name: field.name,
                      message: message
                  });
  
                  // Break out so as to not spam with validation errors (i.e. required and valid_email)
                  break;
              }
          }
      };
  
      /*
       * @private
       * Object containing all of the validation hooks
       */
  
      FormValidator.prototype._hooks = {
          required: function(field) {
              var value = field.value;
  
              if (field.type === 'checkbox') {
                  return (field.checked === true);
              }
  
              return (value !== null && value !== '');
          },
  
          matches: function(field, matchName) {
              if (el = this.form[matchName]) {
                  return field.value === el.value;
              }
  
              return false;
          },
  
          valid_email: function(field) {
              return emailRegex.test(field.value);
          },
  
          valid_emails: function(field) {
              var result = field.value.split(",");
  
              for (var i = 0; i < result.length; i++) {
                  if (!emailRegex.test(result[i])) {
                      return false;
                  }
              }
  
              return true;
          },
  
          min_length: function(field, length) {
              if (!numericRegex.test(length)) {
                  return false;
              }
  
              return (field.value.length >= parseInt(length, 10));
          },
  
          max_length: function(field, length) {
              if (!numericRegex.test(length)) {
                  return false;
              }
  
              return (field.value.length <= parseInt(length, 10));
          },
  
          exact_length: function(field, length) {
              if (!numericRegex.test(length)) {
                  return false;
              }
  
              return (field.value.length === parseInt(length, 10));
          },
  
          greater_than: function(field, param) {
              if (!decimalRegex.test(field.value)) {
                  return false;
              }
  
              return (parseFloat(field.value) > parseFloat(param));
          },
  
          less_than: function(field, param) {
              if (!decimalRegex.test(field.value)) {
                  return false;
              }
  
              return (parseFloat(field.value) < parseFloat(param));
          },
  
          alpha: function(field) {
              return (alphaRegex.test(field.value));
          },
  
          alpha_numeric: function(field) {
              return (alphaNumericRegex.test(field.value));
          },
  
          alpha_dash: function(field) {
              return (alphaDashRegex.test(field.value));
          },
  
          numeric: function(field) {
              return (decimalRegex.test(field.value));
          },
  
          integer: function(field) {
              return (integerRegex.test(field.value));
          },
  
          decimal: function(field) {
              return (decimalRegex.test(field.value));
          },
  
          is_natural: function(field) {
              return (naturalRegex.test(field.value));
          },
  
          is_natural_no_zero: function(field) {
              return (naturalNoZeroRegex.test(field.value));
          },
  
          valid_ip: function(field) {
              return (ipRegex.test(field.value));
          },
  
          valid_base64: function(field) {
              return (base64Regex.test(field.value));
          }
      };
  
      window.FormValidator = FormValidator;
  
  })(window, document);
  /**
   * Created with IntelliJ IDEA.
   * User: nhn
   * Date: 13. 1. 15
   * Time: 오후 4:20
   * To change this template use File | Settings | File Templates.
   */
  /*!
   * Copyright (c) 2010 Chris O'Hara <cohara87@gmail.com>
   *
   * 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.
   */
  
  // follow Universal Module Definition (UMD) pattern for defining module as AMD, CommonJS, and Browser compatible
  (function (root, factory) {
      if (typeof define === 'function' && define.amd) {
          // AMD. Register as an anonymous module.
          define(['exports'], factory);
      } else if (typeof exports === 'object') {
          // CommonJS
          factory(exports);
      } else {
          // Browser globals
          // N.B. Here is a slight difference to regular UMD as the current API for node-validator in browser adds each export directly to the window
          // rather than to a namespaced object such as window.nodeValidator, which would be better practice, but would break backwards compatibility
          // as such unable to use build tools like grunt-umd
          factory(root);
      }
  }(this, function(exports) {
  
      var entities = {
          '&nbsp;': '\u00a0',
          '&iexcl;': '\u00a1',
          '&cent;': '\u00a2',
          '&pound;': '\u00a3',
          '&curren;': '\u20ac',
          '&yen;': '\u00a5',
          '&brvbar;': '\u0160',
          '&sect;': '\u00a7',
          '&uml;': '\u0161',
          '&copy;': '\u00a9',
          '&ordf;': '\u00aa',
          '&laquo;': '\u00ab',
          '&not;': '\u00ac',
          '&shy;': '\u00ad',
          '&reg;': '\u00ae',
          '&macr;': '\u00af',
          '&deg;': '\u00b0',
          '&plusmn;': '\u00b1',
          '&sup2;': '\u00b2',
          '&sup3;': '\u00b3',
          '&acute;': '\u017d',
          '&micro;': '\u00b5',
          '&para;': '\u00b6',
          '&middot;': '\u00b7',
          '&cedil;': '\u017e',
          '&sup1;': '\u00b9',
          '&ordm;': '\u00ba',
          '&raquo;': '\u00bb',
          '&frac14;': '\u0152',
          '&frac12;': '\u0153',
          '&frac34;': '\u0178',
          '&iquest;': '\u00bf',
          '&Agrave;': '\u00c0',
          '&Aacute;': '\u00c1',
          '&Acirc;': '\u00c2',
          '&Atilde;': '\u00c3',
          '&Auml;': '\u00c4',
          '&Aring;': '\u00c5',
          '&AElig;': '\u00c6',
          '&Ccedil;': '\u00c7',
          '&Egrave;': '\u00c8',
          '&Eacute;': '\u00c9',
          '&Ecirc;': '\u00ca',
          '&Euml;': '\u00cb',
          '&Igrave;': '\u00cc',
          '&Iacute;': '\u00cd',
          '&Icirc;': '\u00ce',
          '&Iuml;': '\u00cf',
          '&ETH;': '\u00d0',
          '&Ntilde;': '\u00d1',
          '&Ograve;': '\u00d2',
          '&Oacute;': '\u00d3',
          '&Ocirc;': '\u00d4',
          '&Otilde;': '\u00d5',
          '&Ouml;': '\u00d6',
          '&times;': '\u00d7',
          '&Oslash;': '\u00d8',
          '&Ugrave;': '\u00d9',
          '&Uacute;': '\u00da',
          '&Ucirc;': '\u00db',
          '&Uuml;': '\u00dc',
          '&Yacute;': '\u00dd',
          '&THORN;': '\u00de',
          '&szlig;': '\u00df',
          '&agrave;': '\u00e0',
          '&aacute;': '\u00e1',
          '&acirc;': '\u00e2',
          '&atilde;': '\u00e3',
          '&auml;': '\u00e4',
          '&aring;': '\u00e5',
          '&aelig;': '\u00e6',
          '&ccedil;': '\u00e7',
          '&egrave;': '\u00e8',
          '&eacute;': '\u00e9',
          '&ecirc;': '\u00ea',
          '&euml;': '\u00eb',
          '&igrave;': '\u00ec',
          '&iacute;': '\u00ed',
          '&icirc;': '\u00ee',
          '&iuml;': '\u00ef',
          '&eth;': '\u00f0',
          '&ntilde;': '\u00f1',
          '&ograve;': '\u00f2',
          '&oacute;': '\u00f3',
          '&ocirc;': '\u00f4',
          '&otilde;': '\u00f5',
          '&ouml;': '\u00f6',
          '&divide;': '\u00f7',
          '&oslash;': '\u00f8',
          '&ugrave;': '\u00f9',
          '&uacute;': '\u00fa',
          '&ucirc;': '\u00fb',
          '&uuml;': '\u00fc',
          '&yacute;': '\u00fd',
          '&thorn;': '\u00fe',
          '&yuml;': '\u00ff',
          '&quot;': '\u0022',
          '&lt;': '\u003c',
          '&gt;': '\u003e',
          '&apos;': '\u0027',
          '&minus;': '\u2212',
          '&circ;': '\u02c6',
          '&tilde;': '\u02dc',
          '&Scaron;': '\u0160',
          '&lsaquo;': '\u2039',
          '&OElig;': '\u0152',
          '&lsquo;': '\u2018',
          '&rsquo;': '\u2019',
          '&ldquo;': '\u201c',
          '&rdquo;': '\u201d',
          '&bull;': '\u2022',
          '&ndash;': '\u2013',
          '&mdash;': '\u2014',
          '&trade;': '\u2122',
          '&scaron;': '\u0161',
          '&rsaquo;': '\u203a',
          '&oelig;': '\u0153',
          '&Yuml;': '\u0178',
          '&fnof;': '\u0192',
          '&Alpha;': '\u0391',
          '&Beta;': '\u0392',
          '&Gamma;': '\u0393',
          '&Delta;': '\u0394',
          '&Epsilon;': '\u0395',
          '&Zeta;': '\u0396',
          '&Eta;': '\u0397',
          '&Theta;': '\u0398',
          '&Iota;': '\u0399',
          '&Kappa;': '\u039a',
          '&Lambda;': '\u039b',
          '&Mu;': '\u039c',
          '&Nu;': '\u039d',
          '&Xi;': '\u039e',
          '&Omicron;': '\u039f',
          '&Pi;': '\u03a0',
          '&Rho;': '\u03a1',
          '&Sigma;': '\u03a3',
          '&Tau;': '\u03a4',
          '&Upsilon;': '\u03a5',
          '&Phi;': '\u03a6',
          '&Chi;': '\u03a7',
          '&Psi;': '\u03a8',
          '&Omega;': '\u03a9',
          '&alpha;': '\u03b1',
          '&beta;': '\u03b2',
          '&gamma;': '\u03b3',
          '&delta;': '\u03b4',
          '&epsilon;': '\u03b5',
          '&zeta;': '\u03b6',
          '&eta;': '\u03b7',
          '&theta;': '\u03b8',
          '&iota;': '\u03b9',
          '&kappa;': '\u03ba',
          '&lambda;': '\u03bb',
          '&mu;': '\u03bc',
          '&nu;': '\u03bd',
          '&xi;': '\u03be',
          '&omicron;': '\u03bf',
          '&pi;': '\u03c0',
          '&rho;': '\u03c1',
          '&sigmaf;': '\u03c2',
          '&sigma;': '\u03c3',
          '&tau;': '\u03c4',
          '&upsilon;': '\u03c5',
          '&phi;': '\u03c6',
          '&chi;': '\u03c7',
          '&psi;': '\u03c8',
          '&omega;': '\u03c9',
          '&thetasym;': '\u03d1',
          '&upsih;': '\u03d2',
          '&piv;': '\u03d6',
          '&ensp;': '\u2002',
          '&emsp;': '\u2003',
          '&thinsp;': '\u2009',
          '&zwnj;': '\u200c',
          '&zwj;': '\u200d',
          '&lrm;': '\u200e',
          '&rlm;': '\u200f',
          '&sbquo;': '\u201a',
          '&bdquo;': '\u201e',
          '&dagger;': '\u2020',
          '&Dagger;': '\u2021',
          '&hellip;': '\u2026',
          '&permil;': '\u2030',
          '&prime;': '\u2032',
          '&Prime;': '\u2033',
          '&oline;': '\u203e',
          '&frasl;': '\u2044',
          '&euro;': '\u20ac',
          '&image;': '\u2111',
          '&weierp;': '\u2118',
          '&real;': '\u211c',
          '&alefsym;': '\u2135',
          '&larr;': '\u2190',
          '&uarr;': '\u2191',
          '&rarr;': '\u2192',
          '&darr;': '\u2193',
          '&harr;': '\u2194',
          '&crarr;': '\u21b5',
          '&lArr;': '\u21d0',
          '&uArr;': '\u21d1',
          '&rArr;': '\u21d2',
          '&dArr;': '\u21d3',
          '&hArr;': '\u21d4',
          '&forall;': '\u2200',
          '&part;': '\u2202',
          '&exist;': '\u2203',
          '&empty;': '\u2205',
          '&nabla;': '\u2207',
          '&isin;': '\u2208',
          '&notin;': '\u2209',
          '&ni;': '\u220b',
          '&prod;': '\u220f',
          '&sum;': '\u2211',
          '&lowast;': '\u2217',
          '&radic;': '\u221a',
          '&prop;': '\u221d',
          '&infin;': '\u221e',
          '&ang;': '\u2220',
          '&and;': '\u2227',
          '&or;': '\u2228',
          '&cap;': '\u2229',
          '&cup;': '\u222a',
          '&int;': '\u222b',
          '&there4;': '\u2234',
          '&sim;': '\u223c',
          '&cong;': '\u2245',
          '&asymp;': '\u2248',
          '&ne;': '\u2260',
          '&equiv;': '\u2261',
          '&le;': '\u2264',
          '&ge;': '\u2265',
          '&sub;': '\u2282',
          '&sup;': '\u2283',
          '&nsub;': '\u2284',
          '&sube;': '\u2286',
          '&supe;': '\u2287',
          '&oplus;': '\u2295',
          '&otimes;': '\u2297',
          '&perp;': '\u22a5',
          '&sdot;': '\u22c5',
          '&lceil;': '\u2308',
          '&rceil;': '\u2309',
          '&lfloor;': '\u230a',
          '&rfloor;': '\u230b',
          '&lang;': '\u2329',
          '&rang;': '\u232a',
          '&loz;': '\u25ca',
          '&spades;': '\u2660',
          '&clubs;': '\u2663',
          '&hearts;': '\u2665',
          '&diams;': '\u2666'
      };
  
      var decode = function (str) {
          if (!~str.indexOf('&')) return str;
  
          //Decode literal entities
          for (var i in entities) {
              str = str.replace(new RegExp(i, 'g'), entities[i]);
          }
  
          //Decode hex entities
          str = str.replace(/&#x(0*[0-9a-f]{2,5});?/gi, function (m, code) {
              return String.fromCharCode(parseInt(+code, 16));
          });
  
          //Decode numeric entities
          str = str.replace(/&#([0-9]{2,4});?/gi, function (m, code) {
              return String.fromCharCode(+code);
          });
  
          str = str.replace(/&amp;/g, '&');
  
          return str;
      }
  
      var encode = function (str) {
          str = str.replace(/&/g, '&amp;');
  
          //IE doesn't accept &apos;
          str = str.replace(/'/g, '&#39;');
  
          //Encode literal entities
          for (var i in entities) {
              str = str.replace(new RegExp(entities[i], 'g'), i);
          }
  
          return str;
      }
  
      exports.entities = {
          encode: encode,
          decode: decode
      }
  
      //This module is adapted from the CodeIgniter framework
      //The license is available at http://codeigniter.com/
  
      var never_allowed_str = {
          'document.cookie':              '',
          'document.write':               '',
          '.parentNode':                  '',
          '.innerHTML':                   '',
          'window.location':              '',
          '-moz-binding':                 '',
          '<!--':                         '&lt;!--',
          '-->':                          '--&gt;',
          '<![CDATA[':                    '&lt;![CDATA['
      };
  
      var never_allowed_regex = {
          'javascript\\s*:':              '',
          'expression\\s*(\\(|&\\#40;)':  '',
          'vbscript\\s*:':                '',
          'Redirect\\s+302':              ''
      };
  
      var non_displayables = [
          /%0[0-8bcef]/g,           // url encoded 00-08, 11, 12, 14, 15
          /%1[0-9a-f]/g,            // url encoded 16-31
          /[\x00-\x08]/g,           // 00-08
          /\x0b/g, /\x0c/g,         // 11,12
          /[\x0e-\x1f]/g            // 14-31
      ];
  
      var compact_words = [
          'javascript', 'expression', 'vbscript',
          'script', 'applet', 'alert', 'document',
          'write', 'cookie', 'window'
      ];
  
      exports.xssClean = function(str, is_image) {
  
          //Recursively clean objects and arrays
          if (typeof str === 'object') {
              for (var i in str) {
                  str[i] = exports.xssClean(str[i]);
              }
              return str;
          }
  
          //Remove invisible characters
          str = remove_invisible_characters(str);
  
          //Protect query string variables in URLs => 901119URL5918AMP18930PROTECT8198
          str = str.replace(/\&([a-z\_0-9]+)\=([a-z\_0-9]+)/i, xss_hash() + '$1=$2');
  
          //Validate standard character entities - add a semicolon if missing.  We do this to enable
          //the conversion of entities to ASCII later.
          str = str.replace(/(&\#?[0-9a-z]{2,})([\x00-\x20])*;?/i, '$1;$2');
  
          //Validate UTF16 two byte encoding (x00) - just as above, adds a semicolon if missing.
          str = str.replace(/(&\#x?)([0-9A-F]+);?/i, '$1;$2');
  
          //Un-protect query string variables
          str = str.replace(xss_hash(), '&');
  
          //Decode just in case stuff like this is submitted:
          //<a href="http://%77%77%77%2E%67%6F%6F%67%6C%65%2E%63%6F%6D">Google</a>
          
          //Convert character entities to ASCII - this permits our tests below to work reliably.
          //We only convert entities that are within tags since these are the ones that will pose security problems.
          str = str.replace(/[a-z]+=([\'\"]).*?\1/gi, function(m, match) {
              return m.replace(match, convert_attribute(match));
          });
  
          //Remove invisible characters again
          str = remove_invisible_characters(str);
  
          //Convert tabs to spaces
          str = str.replace('\t', ' ');
  
          //Captured the converted string for later comparison
          var converted_string = str;
  
          //Remove strings that are never allowed
          for (var i in never_allowed_str) {
              str = str.replace(i, never_allowed_str[i]);
          }
  
          //Remove regex patterns that are never allowed
          for (var i in never_allowed_regex) {
              str = str.replace(new RegExp(i, 'i'), never_allowed_regex[i]);
          }
  
          //Compact any exploded words like:  j a v a s c r i p t
          // We only want to do this when it is followed by a non-word character
          for (var i in compact_words) {
              var spacified = compact_words[i].split('').join('\\s*')+'\\s*';
  
              str = str.replace(new RegExp('('+spacified+')(\\s)', 'ig'), function(m, compat, after) {
                  return compat.replace(/\s+/g, '') + after;
              });
          }
  
          //Remove disallowed Javascript in links, img tags or video tags
          do {
              var original = str;
  
              if (str.match(/<a/i)) {
                  str = str.replace(/<a\s+([^>]*?)(>|$)/gi, function(m, attributes, end_tag) {
                      attributes = filter_attributes(attributes.replace('<','').replace('>',''));
                      return m.replace(attributes, attributes.replace(/href=.*?(alert\(|alert&\#40;|javascript\:|charset\=|window\.|document\.|\.cookie|<script|<xss|base64\s*,)/gi, ''));
                  });
              }
  
              if (str.match(/<img/i)) {
                  str = str.replace(/<img\s+([^>]*?)(\s?\/?>|$)/gi, function(m, attributes, end_tag) {
                      attributes = filter_attributes(attributes.replace('<','').replace('>',''));
                      return m.replace(attributes, attributes.replace(/src=.*?(alert\(|alert&\#40;|javascript\:|charset\=|window\.|document\.|\.cookie|<script|<xss|base64\s*,)/gi, ''));
                  });
              }
  
              if (str.match(/<video/i)) {
                  str = str.replace(/<video\s+([^>]*?)(\s?\/?>|$)/gi, function(m, attributes, end_tag) {
                      attributes = filter_attributes(attributes.replace('<','').replace('>',''));
                      return m.replace(attributes, attributes.replace(/poster=.*?(alert\(|alert&\#40;|javascript\:|charset\=|window\.|document\.|\.cookie|<script|<xss|base64\s*,)/gi, ''));
                  });
              }
  
              if (str.match(/script/i) || str.match(/xss/i)) {
                  str = str.replace(/<(\/*)(script|xss)(.*?)\>/gi, '');
              }
  
          } while(original != str);
  
          //Remove JavaScript Event Handlers - Note: This code is a little blunt.  It removes the event
          //handler and anything up to the closing >, but it's unlikely to be a problem.
          event_handlers = ['[^a-z_\-]on\\w*'];
  
          //Adobe Photoshop puts XML metadata into JFIF images, including namespacing,
          //so we have to allow this for images
          if (!is_image) {
              event_handlers.push('xmlns');
          }
  
          str = str.replace(new RegExp("<([^><]+?)("+event_handlers.join('|')+")(\\s*=\\s*[^><]*)([><]*)", 'i'), '<$1$4');
  
          //Sanitize naughty HTML elements
          //If a tag containing any of the words in the list
          //below is found, the tag gets converted to entities.
          //So this: <blink>
          //Becomes: &lt;blink&gt;
          naughty = 'alert|applet|audio|basefont|base|behavior|bgsound|blink|body|embed|expression|form|frameset|frame|head|html|ilayer|iframe|input|isindex|layer|link|meta|object|plaintext|style|script|textarea|title|xml|xss';
          str = str.replace(new RegExp('<(/*\\s*)('+naughty+')([^><]*)([><]*)', 'gi'), function(m, a, b, c, d) {
              return '&lt;' + a + b + c + d.replace('>','&gt;').replace('<','&lt;');
          });
  
          //Sanitize naughty scripting elements Similar to above, only instead of looking for
          //tags it looks for PHP and JavaScript commands that are disallowed.  Rather than removing the
          //code, it simply converts the parenthesis to entities rendering the code un-executable.
          //For example:    eval('some code')
          //Becomes:        eval&#40;'some code'&#41;
          str = str.replace(/(alert|cmd|passthru|eval|exec|expression|system|fopen|fsockopen|file|file_get_contents|readfile|unlink)(\s*)\((.*?)\)/gi, '$1$2&#40;$3&#41;');
  
          //This adds a bit of extra precaution in case something got through the above filters
          for (var i in never_allowed_str) {
              str = str.replace(i, never_allowed_str[i]);
          }
          for (var i in never_allowed_regex) {
              str = str.replace(new RegExp(i, 'i'), never_allowed_regex[i]);
          }
  
          //Images are handled in a special way
          if (is_image && str !== converted_string) {
              throw new Error('Image may contain XSS');
          }
  
          return str;
      }
  
      function remove_invisible_characters(str) {
          for (var i in non_displayables) {
              str = str.replace(non_displayables[i], '');
          }
          return str;
      }
  
      function xss_hash() {
          //TODO: Create a random hash
          return '!*$^#(@*#&';
      }
  
      function convert_attribute(str) {
          return str.replace('>','&gt;').replace('<','&lt;').replace('\\','\\\\');
      }
  
      //Filter Attributes - filters tag attributes for consistency and safety
      function filter_attributes(str) {
          var comments = /\/\*.*?\*\//g;
          return str.replace(/\s*[a-z-]+\s*=\s*'[^']*'/gi, function (m) {
              return m.replace(comments, '');
          }).replace(/\s*[a-z-]+\s*=\s*"[^"]*"/gi, function (m) {
              return m.replace(comments, '');
          }).replace(/\s*[a-z-]+\s*=\s*[^\s]+/gi, function (m) {
              return m.replace(comments, '');
          });
      }
  
      var Validator = exports.Validator = function() {}
  
      Validator.prototype.check = function(str, fail_msg) {
          this.str = typeof( str ) === 'undefined' || str === null || (isNaN(str) && str.length === undefined) ? '' : str+'';
          this.msg = fail_msg;
          this._errors = this._errors || [];
          return this;
      }
  
      function internal_is_ipv4(str) {
          if (/^(\d?\d?\d)\.(\d?\d?\d)\.(\d?\d?\d)\.(\d?\d?\d)$/.test(str)) {
              var parts = str.split('.').sort();
              // no need to check for < 0 as regex won't match in that case
              if (parts[3] > 255) {
                  return false;
              }
              return true;
          }
          return false;
      }
  
      function internal_is_ipv6(str) {
          if (/^::|^::1|^([a-fA-F0-9]{1,4}::?){1,7}([a-fA-F0-9]{1,4})$/.test(str)) {
              return true;
          }
          return false;
      }
  
      //Create some aliases - may help code readability
      Validator.prototype.validate = Validator.prototype.check;
      Validator.prototype.assert = Validator.prototype.check;
  
      Validator.prototype.error = function(msg) {
          throw new Error(msg);
      }
  
      function toDate(date) {
          if (date instanceof Date) {
              return date;
          }
          var intDate = Date.parse(date);
          if (isNaN(intDate)) {
              return null;
          }
          return new Date(intDate);
      }
  
      Validator.prototype.isAfter = function(date) {
          date = date || new Date();
          var origDate = toDate(this.str)
            , compDate = toDate(date);
          if (!(origDate && compDate && origDate >= compDate)) {
              return this.error(this.msg || 'Invalid date');
          }
          return this;
      };
  
      Validator.prototype.isBefore = function(date) {
          date = date || new Date();
          var origDate = toDate(this.str)
            , compDate = toDate(date);
          if (!(origDate && compDate && origDate <= compDate)) {
              return this.error(this.msg || 'Invalid date');
          }
          return this;
      };
  
      Validator.prototype.isEmail = function() {
          if (!this.str.match(/^(?:[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+\.)*[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+@(?:(?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9\-](?!\.)){0,61}[a-zA-Z0-9]?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9\-](?!$)){0,61}[a-zA-Z0-9]?)|(?:\[(?:(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\.){3}(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\]))$/)) {
              return this.error(this.msg || 'Invalid email');
          }
          return this;
      }
  
    //Will work against Visa, MasterCard, American Express, Discover, Diners Club, and JCB card numbering formats
    Validator.prototype.isCreditCard = function() {
      this.str = this.str.replace(/[^0-9]+/g, ''); //remove all dashes, spaces, etc.
          if (!this.str.match(/^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\d{3})\d{11})$/)) {
              return this.error(this.msg || 'Invalid credit card');
          }
          // Doing Luhn check
          var sum = 0;
          var digit;
          var tmpNum;
          var shouldDouble = false;
          for (var i = this.length - 1; i >= 0; i--) {
                  digit = this.substring(i, (i + 1));
                  tmpNum = parseInt(digit, 10);
                  if (shouldDouble) {
                      tmpNum *= 2;
                      if (tmpNum >= 10) {
                          sum += ((tmpNum % 10) + 1);
                      }
                      else {
                          sum += tmpNum;
                      }
                  }
                  else {
                      sum += tmpNum;
                  }
                  if (shouldDouble) {
                      shouldDouble = false;
                  }
                  else {
                      shouldDouble = true;
                  }
              }
              if ((sum % 10) !== 0) {
                  return this.error(this.msg || 'Invalid credit card');
              }
          return this;
      }
  
      Validator.prototype.isUrl = function() {
          if (!this.str.match(/^(?!mailto:)(?:(?:https?|ftp):\/\/)?(?:\S+(?::\S*)?@)?(?:(?:(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]+-?)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]+-?)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))|localhost)(?::\d{2,5})?(?:\/[^\s]*)?$/i) || this.str.length > 2083) {
              return this.error(this.msg || 'Invalid URL');
          }
          return this;
      }
  
      Validator.prototype.isIPv4 = function() {
          if (internal_is_ipv4(this.str)) {
              return this;
          }
          return this.error(this.msg || 'Invalid IP');
      }
  
      Validator.prototype.isIPv6 = function() {
          if (internal_is_ipv6(this.str)) {
              return this;
          }
          return this.error(this.msg || 'Invalid IP');
      }
  
      Validator.prototype.isIP = function() {
          if (internal_is_ipv4(this.str) || internal_is_ipv6(this.str)) {
              return this;
          }
          return this.error(this.msg || 'Invalid IP');
      }
  
      Validator.prototype.isAlpha = function() {
          if (!this.str.match(/^[a-zA-Z]+$/)) {
              return this.error(this.msg || 'Invalid characters');
          }
          return this;
      }
  
      Validator.prototype.isAlphanumeric = function() {
          if (!this.str.match(/^[a-zA-Z0-9]+$/)) {
              return this.error(this.msg || 'Invalid characters');
          }
          return this;
      }
  
      Validator.prototype.isNumeric = function() {
          if (!this.str.match(/^-?[0-9]+$/)) {
              return this.error(this.msg || 'Invalid number');
          }
          return this;
      }
  
      Validator.prototype.isHexadecimal = function() {
          if (!this.str.match(/^[0-9a-fA-F]+$/)) {
              return this.error(this.msg || 'Invalid hexadecimal');
          }
          return this;
      }
  
      Validator.prototype.isHexColor = function() {
          if (!this.str.match(/^#?([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/)) {
              return this.error(this.msg || 'Invalid hexcolor');
          }
          return this;
      }
  
      Validator.prototype.isLowercase = function() {
          if (this.str !== this.str.toLowerCase()) {
              return this.error(this.msg || 'Invalid characters');
          }
          return this;
      }
  
      Validator.prototype.isUppercase = function() {
          if (this.str !== this.str.toUpperCase()) {
              return this.error(this.msg || 'Invalid characters');
          }
          return this;
      }
  
      Validator.prototype.isInt = function() {
          if (!this.str.match(/^(?:-?(?:0|[1-9][0-9]*))$/)) {
              return this.error(this.msg || 'Invalid integer');
          }
          return this;
      }
  
      Validator.prototype.isDecimal = function() {
          if (!this.str.match(/^(?:-?(?:0|[1-9][0-9]*))?(?:\.[0-9]*)?$/)) {
              return this.error(this.msg || 'Invalid decimal');
          }
          return this;
      }
  
      Validator.prototype.isDivisibleBy = function(n) {
          return (parseFloat(this.str) % parseInt(n, 10)) === 0;
      }
  
      Validator.prototype.isFloat = function() {
          return this.isDecimal();
      }
  
      Validator.prototype.notNull = function() {
          if (this.str === '') {
              return this.error(this.msg || 'String is empty');
          }
          return this;
      }
  
      Validator.prototype.isNull = function() {
          if (this.str !== '') {
              return this.error(this.msg || 'String is not empty');
          }
          return this;
      }
  
      Validator.prototype.notEmpty = function() {
          if (this.str.match(/^[\s\t\r\n]*$/)) {
              return this.error(this.msg || 'String is whitespace');
          }
          return this;
      }
  
      Validator.prototype.equals = function(equals) {
          if (this.str != equals) {
              return this.error(this.msg || 'Not equal');
          }
          return this;
      }
  
      Validator.prototype.contains = function(str) {
          if (this.str.indexOf(str) === -1 || !str) {
              return this.error(this.msg || 'Invalid characters');
          }
          return this;
      }
  
      Validator.prototype.notContains = function(str) {
          if (this.str.indexOf(str) >= 0) {
              return this.error(this.msg || 'Invalid characters');
          }
          return this;
      }
  
      Validator.prototype.regex = Validator.prototype.is = function(pattern, modifiers) {
          if (Object.prototype.toString.call(pattern).slice(8, -1) !== 'RegExp') {
              pattern = new RegExp(pattern, modifiers);
          }
          if (! this.str.match(pattern)) {
              return this.error(this.msg || 'Invalid characters');
          }
          return this;
      }
  
      Validator.prototype.notRegex = Validator.prototype.not = function(pattern, modifiers) {
          if (Object.prototype.toString.call(pattern).slice(8, -1) !== 'RegExp') {
              pattern = new RegExp(pattern, modifiers);
          }
          if (this.str.match(pattern)) {
              this.error(this.msg || 'Invalid characters');
          }
          return this;
      }
  
      Validator.prototype.len = function(min, max) {
          if (this.str.length < min) {
              return this.error(this.msg || 'String is too small');
          }
          if (typeof max !== undefined && this.str.length > max) {
              return this.error(this.msg || 'String is too large');
          }
          return this;
      }
  
      //Thanks to github.com/sreuter for the idea.
      Validator.prototype.isUUID = function(version) {
          var pattern;
          if (version == 3 || version == 'v3') {
              pattern = /[0-9A-F]{8}-[0-9A-F]{4}-3[0-9A-F]{3}-[0-9A-F]{4}-[0-9A-F]{12}$/i;
          } else if (version == 4 || version == 'v4') {
              pattern = /[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i;
          } else if (version == 5 || version == 'v5') {
              pattern = /^[0-9A-F]{8}-[0-9A-F]{4}-5[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i;
          } else {
              pattern = /[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}$/i;
          }
          if (!this.str.match(pattern)) {
              return this.error(this.msg || 'Not a UUID');
          }
          return this;
      }
  
      Validator.prototype.isUUIDv3 = function() {
          return this.isUUID(3);
      }
  
      Validator.prototype.isUUIDv4 = function() {
          return this.isUUID(4);
      }
  
      Validator.prototype.isUUIDv5 = function() {
          return this.isUUID(5);
      }
  
      Validator.prototype.isDate = function() {
          var intDate = Date.parse(this.str);
          if (isNaN(intDate)) {
              return this.error(this.msg || 'Not a date');
          }
          return this;
      }
  
      Validator.prototype.isIn = function(options) {
          if (options && typeof options.indexOf === 'function') {
              if (!~options.indexOf(this.str)) {
                  return this.error(this.msg || 'Unexpected value');
              }
              return this;
          } else {
              return this.error(this.msg || 'Invalid in() argument');
          }
      }
  
      Validator.prototype.notIn = function(options) {
          if (options && typeof options.indexOf === 'function') {
              if (options.indexOf(this.str) !== -1) {
                  return this.error(this.msg || 'Unexpected value');
              }
              return this;
          } else {
              return this.error(this.msg || 'Invalid notIn() argument');
          }
      }
  
      Validator.prototype.min = function(val) {
          var number = parseFloat(this.str);
  
          if (!isNaN(number) && number < val) {
              return this.error(this.msg || 'Invalid number');
          }
  
          return this;
      }
  
      Validator.prototype.max = function(val) {
          var number = parseFloat(this.str);
          if (!isNaN(number) && number > val) {
              return this.error(this.msg || 'Invalid number');
          }
          return this;
      }
  
      var Filter = exports.Filter = function() {}
  
      var whitespace = '\\r\\n\\t\\s';
  
      Filter.prototype.modify = function(str) {
          this.str = str;
      }
  
      //Create some aliases - may help code readability
      Filter.prototype.convert = Filter.prototype.sanitize = function(str) {
          this.str = str == null ? '' : str + '';
          return this;
      }
  
      Filter.prototype.xss = function(is_image) {
          this.modify(exports.xssClean(this.str, is_image));
          return this.str;
      }
  
      Filter.prototype.defence = function(str) {
          return this.sanitize(str).xss();
      }
  
      Filter.prototype.entityDecode = function() {
          this.modify(decode(this.str));
          return this.str;
      }
  
      Filter.prototype.entityEncode = function() {
          this.modify(encode(this.str));
          return this.str;
      }
  
      Filter.prototype.escape = function() {
          this.modify(this.str.replace(/&/g, '&amp;')
                              .replace(/"/g, '&quot;')
                              .replace(/</g, '&lt;')
                              .replace(/>/g, '&gt;'));
          return this.str;
      };
  
      Filter.prototype.ltrim = function(chars) {
          chars = chars || whitespace;
          this.modify(this.str.replace(new RegExp('^['+chars+']+', 'g'), ''));
          return this.str;
      }
  
      Filter.prototype.rtrim = function(chars) {
          chars = chars || whitespace;
          this.modify(this.str.replace(new RegExp('['+chars+']+$', 'g'), ''));
          return this.str;
      }
  
      Filter.prototype.trim = function(chars) {
          chars = chars || whitespace;
          this.modify(this.str.replace(new RegExp('^['+chars+']+|['+chars+']+$', 'g'), ''));
          return this.str;
      }
  
      Filter.prototype.ifNull = function(replace) {
          if (!this.str || this.str === '') {
              this.modify(replace);
          }
          return this.str;
      }
  
      Filter.prototype.toFloat = function() {
          this.modify(parseFloat(this.str));
          return this.str;
      }
  
      Filter.prototype.toInt = function(radix) {
          radix = radix || 10;
          this.modify(parseInt(this.str, radix));
          return this.str;
      }
  
      //Any strings with length > 0 (except for '0' and 'false') are considered true,
      //all other strings are false
      Filter.prototype.toBoolean = function() {
          if (!this.str || this.str == '0' || this.str == 'false' || this.str == '') {
              this.modify(false);
          } else {
              this.modify(true);
          }
          return this.str;
      }
  
      //String must be equal to '1' or 'true' to be considered true, all other strings
      //are false
      Filter.prototype.toBooleanStrict = function() {
          if (this.str == '1' || this.str == 'true') {
              this.modify(true);
          } else {
              this.modify(false);
          }
          return this.str;
      }
  
      //Quick access methods
      exports.sanitize = exports.convert = function(str) {
          var filter = new exports.Filter();
          return filter.sanitize(str);
      }
  
      exports.check = exports.validate = exports.assert = function(str, fail_msg) {
          var validator = new exports.Validator();
          return validator.check(str, fail_msg);
      }
  
      return exports;
  
  }));
  