/* Modernizr 2.0.6 (Custom Build) | MIT & BSD
 * Contains: fontface | backgroundsize | borderimage | borderradius | boxshadow | flexbox | hsla | multiplebgs | opacity | rgba | textshadow | cssanimations | csscolumns | generatedcontent | cssgradients | cssreflections | csstransforms | csstransforms3d | csstransitions | canvas | canvastext | hashchange | history | touch | iepp | cssclasses | addtest | prefixed | teststyles | testprop | testallprops | hasevent | prefixes | domprefixes | load
 */
;window.Modernizr=function(a,b,c){function E(a,b){var c=a.charAt(0).toUpperCase()+a.substr(1),d=(a+" "+p.join(c+" ")+c).split(" ");return D(d,b)}function D(a,b){for(var d in a)if(k[a[d]]!==c)return b=="pfx"?a[d]:!0;return!1}function C(a,b){return!!~(""+a).indexOf(b)}function B(a,b){return typeof a===b}function A(a,b){return z(o.join(a+";")+(b||""))}function z(a){k.cssText=a}var d="2.0.6",e={},f=!0,g=b.documentElement,h=b.head||b.getElementsByTagName("head")[0],i="modernizr",j=b.createElement(i),k=j.style,l,m=":)",n=Object.prototype.toString,o=" -webkit- -moz- -o- -ms- -khtml- ".split(" "),p="Webkit Moz O ms Khtml".split(" "),q={},r={},s={},t=[],u=function(a,c,d,e){var f,h,j,k=b.createElement("div");if(parseInt(d,10))while(d--)j=b.createElement("div"),j.id=e?e[d]:i+(d+1),k.appendChild(j);f=["&shy;","<style>",a,"</style>"].join(""),k.id=i,k.innerHTML+=f,g.appendChild(k),h=c(k,a),k.parentNode.removeChild(k);return!!h},v=function(){function d(d,e){e=e||b.createElement(a[d]||"div"),d="on"+d;var f=d in e;f||(e.setAttribute||(e=b.createElement("div")),e.setAttribute&&e.removeAttribute&&(e.setAttribute(d,""),f=B(e[d],"function"),B(e[d],c)||(e[d]=c),e.removeAttribute(d))),e=null;return f}var a={select:"input",change:"input",submit:"form",reset:"form",error:"img",load:"img",abort:"img"};return d}(),w,x={}.hasOwnProperty,y;!B(x,c)&&!B(x.call,c)?y=function(a,b){return x.call(a,b)}:y=function(a,b){return b in a&&B(a.constructor.prototype[b],c)};var F=function(c,d){var f=c.join(""),g=d.length;u(f,function(c,d){var f=b.styleSheets[b.styleSheets.length-1],h=f.cssRules&&f.cssRules[0]?f.cssRules[0].cssText:f.cssText||"",i=c.childNodes,j={};while(g--)j[i[g].id]=i[g];e.touch="ontouchstart"in a||j.touch.offsetTop===9,e.csstransforms3d=j.csstransforms3d.offsetLeft===9,e.generatedcontent=j.generatedcontent.offsetHeight>=1,e.fontface=/src/i.test(h)&&h.indexOf(d.split(" ")[0])===0},g,d)}(['@font-face {font-family:"font";src:url("https://")}',["@media (",o.join("touch-enabled),("),i,")","{#touch{top:9px;position:absolute}}"].join(""),["@media (",o.join("transform-3d),("),i,")","{#csstransforms3d{left:9px;position:absolute}}"].join(""),['#generatedcontent:after{content:"',m,'";visibility:hidden}'].join("")],["fontface","touch","csstransforms3d","generatedcontent"]);q.flexbox=function(){function c(a,b,c,d){a.style.cssText=o.join(b+":"+c+";")+(d||"")}function a(a,b,c,d){b+=":",a.style.cssText=(b+o.join(c+";"+b)).slice(0,-b.length)+(d||"")}var d=b.createElement("div"),e=b.createElement("div");a(d,"display","box","width:42px;padding:0;"),c(e,"box-flex","1","width:10px;"),d.appendChild(e),g.appendChild(d);var f=e.offsetWidth===42;d.removeChild(e),g.removeChild(d);return f},q.canvas=function(){var a=b.createElement("canvas");return!!a.getContext&&!!a.getContext("2d")},q.canvastext=function(){return!!e.canvas&&!!B(b.createElement("canvas").getContext("2d").fillText,"function")},q.touch=function(){return e.touch},q.hashchange=function(){return v("hashchange",a)&&(b.documentMode===c||b.documentMode>7)},q.history=function(){return!!a.history&&!!history.pushState},q.rgba=function(){z("background-color:rgba(150,255,150,.5)");return C(k.backgroundColor,"rgba")},q.hsla=function(){z("background-color:hsla(120,40%,100%,.5)");return C(k.backgroundColor,"rgba")||C(k.backgroundColor,"hsla")},q.multiplebgs=function(){z("background:url(https://),url(https://),red url(https://)");return/(url\s*\(.*?){3}/.test(k.background)},q.backgroundsize=function(){return E("backgroundSize")},q.borderimage=function(){return E("borderImage")},q.borderradius=function(){return E("borderRadius")},q.boxshadow=function(){return E("boxShadow")},q.textshadow=function(){return b.createElement("div").style.textShadow===""},q.opacity=function(){A("opacity:.55");return/^0.55$/.test(k.opacity)},q.cssanimations=function(){return E("animationName")},q.csscolumns=function(){return E("columnCount")},q.cssgradients=function(){var a="background-image:",b="gradient(linear,left top,right bottom,from(#9f9),to(white));",c="linear-gradient(left top,#9f9, white);";z((a+o.join(b+a)+o.join(c+a)).slice(0,-a.length));return C(k.backgroundImage,"gradient")},q.cssreflections=function(){return E("boxReflect")},q.csstransforms=function(){return!!D(["transformProperty","WebkitTransform","MozTransform","OTransform","msTransform"])},q.csstransforms3d=function(){var a=!!D(["perspectiveProperty","WebkitPerspective","MozPerspective","OPerspective","msPerspective"]);a&&"webkitPerspective"in g.style&&(a=e.csstransforms3d);return a},q.csstransitions=function(){return E("transitionProperty")},q.fontface=function(){return e.fontface},q.generatedcontent=function(){return e.generatedcontent};for(var G in q)y(q,G)&&(w=G.toLowerCase(),e[w]=q[G](),t.push((e[w]?"":"no-")+w));e.addTest=function(a,b){if(typeof a=="object")for(var d in a)y(a,d)&&e.addTest(d,a[d]);else{a=a.toLowerCase();if(e[a]!==c)return;b=typeof b=="boolean"?b:!!b(),g.className+=" "+(b?"":"no-")+a,e[a]=b}return e},z(""),j=l=null,a.attachEvent&&function(){var a=b.createElement("div");a.innerHTML="<elem></elem>";return a.childNodes.length!==1}()&&function(a,b){function s(a){var b=-1;while(++b<g)a.createElement(f[b])}a.iepp=a.iepp||{};var d=a.iepp,e=d.html5elements||"abbr|article|aside|audio|canvas|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",f=e.split("|"),g=f.length,h=new RegExp("(^|\\s)("+e+")","gi"),i=new RegExp("<(/*)("+e+")","gi"),j=/^\s*[\{\}]\s*$/,k=new RegExp("(^|[^\\n]*?\\s)("+e+")([^\\n]*)({[\\n\\w\\W]*?})","gi"),l=b.createDocumentFragment(),m=b.documentElement,n=m.firstChild,o=b.createElement("body"),p=b.createElement("style"),q=/print|all/,r;d.getCSS=function(a,b){if(a+""===c)return"";var e=-1,f=a.length,g,h=[];while(++e<f){g=a[e];if(g.disabled)continue;b=g.media||b,q.test(b)&&h.push(d.getCSS(g.imports,b),g.cssText),b="all"}return h.join("")},d.parseCSS=function(a){var b=[],c;while((c=k.exec(a))!=null)b.push(((j.exec(c[1])?"\n":c[1])+c[2]+c[3]).replace(h,"$1.iepp_$2")+c[4]);return b.join("\n")},d.writeHTML=function(){var a=-1;r=r||b.body;while(++a<g){var c=b.getElementsByTagName(f[a]),d=c.length,e=-1;while(++e<d)c[e].className.indexOf("iepp_")<0&&(c[e].className+=" iepp_"+f[a])}l.appendChild(r),m.appendChild(o),o.className=r.className,o.id=r.id,o.innerHTML=r.innerHTML.replace(i,"<$1font")},d._beforePrint=function(){p.styleSheet.cssText=d.parseCSS(d.getCSS(b.styleSheets,"all")),d.writeHTML()},d.restoreHTML=function(){o.innerHTML="",m.removeChild(o),m.appendChild(r)},d._afterPrint=function(){d.restoreHTML(),p.styleSheet.cssText=""},s(b),s(l);d.disablePP||(n.insertBefore(p,n.firstChild),p.media="print",p.className="iepp-printshim",a.attachEvent("onbeforeprint",d._beforePrint),a.attachEvent("onafterprint",d._afterPrint))}(a,b),e._version=d,e._prefixes=o,e._domPrefixes=p,e.hasEvent=v,e.testProp=function(a){return D([a])},e.testAllProps=E,e.testStyles=u,e.prefixed=function(a){return E(a,"pfx")},g.className=g.className.replace(/\bno-js\b/,"")+(f?" js "+t.join(" "):"");return e}(this,this.document),function(a,b,c){function k(a){return!a||a=="loaded"||a=="complete"}function j(){var a=1,b=-1;while(p.length- ++b)if(p[b].s&&!(a=p[b].r))break;a&&g()}function i(a){var c=b.createElement("script"),d;c.src=a.s,c.onreadystatechange=c.onload=function(){!d&&k(c.readyState)&&(d=1,j(),c.onload=c.onreadystatechange=null)},m(function(){d||(d=1,j())},H.errorTimeout),a.e?c.onload():n.parentNode.insertBefore(c,n)}function h(a){var c=b.createElement("link"),d;c.href=a.s,c.rel="stylesheet",c.type="text/css";if(!a.e&&(w||r)){var e=function(a){m(function(){if(!d)try{a.sheet.cssRules.length?(d=1,j()):e(a)}catch(b){b.code==1e3||b.message=="security"||b.message=="denied"?(d=1,m(function(){j()},0)):e(a)}},0)};e(c)}else c.onload=function(){d||(d=1,m(function(){j()},0))},a.e&&c.onload();m(function(){d||(d=1,j())},H.errorTimeout),!a.e&&n.parentNode.insertBefore(c,n)}function g(){var a=p.shift();q=1,a?a.t?m(function(){a.t=="c"?h(a):i(a)},0):(a(),j()):q=0}function f(a,c,d,e,f,h){function i(){!o&&k(l.readyState)&&(r.r=o=1,!q&&j(),l.onload=l.onreadystatechange=null,m(function(){u.removeChild(l)},0))}var l=b.createElement(a),o=0,r={t:d,s:c,e:h};l.src=l.data=c,!s&&(l.style.display="none"),l.width=l.height="0",a!="object"&&(l.type=d),l.onload=l.onreadystatechange=i,a=="img"?l.onerror=i:a=="script"&&(l.onerror=function(){r.e=r.r=1,g()}),p.splice(e,0,r),u.insertBefore(l,s?null:n),m(function(){o||(u.removeChild(l),r.r=r.e=o=1,j())},H.errorTimeout)}function e(a,b,c){var d=b=="c"?z:y;q=0,b=b||"j",C(a)?f(d,a,b,this.i++,l,c):(p.splice(this.i++,0,a),p.length==1&&g());return this}function d(){var a=H;a.loader={load:e,i:0};return a}var l=b.documentElement,m=a.setTimeout,n=b.getElementsByTagName("script")[0],o={}.toString,p=[],q=0,r="MozAppearance"in l.style,s=r&&!!b.createRange().compareNode,t=r&&!s,u=s?l:n.parentNode,v=a.opera&&o.call(a.opera)=="[object Opera]",w="webkitAppearance"in l.style,x=w&&"async"in b.createElement("script"),y=r?"object":v||x?"img":"script",z=w?"img":y,A=Array.isArray||function(a){return o.call(a)=="[object Array]"},B=function(a){return Object(a)===a},C=function(a){return typeof a=="string"},D=function(a){return o.call(a)=="[object Function]"},E=[],F={},G,H;H=function(a){function f(a){var b=a.split("!"),c=E.length,d=b.pop(),e=b.length,f={url:d,origUrl:d,prefixes:b},g,h;for(h=0;h<e;h++)g=F[b[h]],g&&(f=g(f));for(h=0;h<c;h++)f=E[h](f);return f}function e(a,b,e,g,h){var i=f(a),j=i.autoCallback;if(!i.bypass){b&&(b=D(b)?b:b[a]||b[g]||b[a.split("/").pop().split("?")[0]]);if(i.instead)return i.instead(a,b,e,g,h);e.load(i.url,i.forceCSS||!i.forceJS&&/css$/.test(i.url)?"c":c,i.noexec),(D(b)||D(j))&&e.load(function(){d(),b&&b(i.origUrl,h,g),j&&j(i.origUrl,h,g)})}}function b(a,b){function c(a){if(C(a))e(a,h,b,0,d);else if(B(a))for(i in a)a.hasOwnProperty(i)&&e(a[i],h,b,i,d)}var d=!!a.test,f=d?a.yep:a.nope,g=a.load||a.both,h=a.callback,i;c(f),c(g),a.complete&&b.load(a.complete)}var g,h,i=this.yepnope.loader;if(C(a))e(a,0,i,0);else if(A(a))for(g=0;g<a.length;g++)h=a[g],C(h)?e(h,0,i,0):A(h)?H(h):B(h)&&b(h,i);else B(a)&&b(a,i)},H.addPrefix=function(a,b){F[a]=b},H.addFilter=function(a){E.push(a)},H.errorTimeout=1e4,b.readyState==null&&b.addEventListener&&(b.readyState="loading",b.addEventListener("DOMContentLoaded",G=function(){b.removeEventListener("DOMContentLoaded",G,0),b.readyState="complete"},0)),a.yepnope=d()}(this,this.document),Modernizr.load=function(){yepnope.apply(window,[].slice.call(arguments,0))};;
(function($) {

  /**
   * Spoofs placeholders in browsers that don't support them (eg Firefox 3)
   * 
   * Copyright 2011 Dan Bentley
   * Licensed under the Apache License 2.0
   *
   * Author: Dan Bentley [github.com/danbentley]
   */

  // Return if native support is available.
  if ("placeholder" in document.createElement("input")) return;

  $(document).ready(function(){
    $(':input[placeholder]').not(':password').each(function() {
      setupPlaceholder($(this));
    });

    $(':password[placeholder]').each(function() {
      setupPasswords($(this));
    });

    $('form').submit(function(e) {
      clearPlaceholdersBeforeSubmit($(this));
    });
  });

  function setupPlaceholder(input) {

    var placeholderText = input.attr('placeholder');

    setPlaceholderOrFlagChanged(input, placeholderText);
    input.focus(function(e) {
      if (input.data('changed') === true) return;
      if (input.val() === placeholderText) input.val('');
    }).blur(function(e) {
      if (input.val() === '') input.val(placeholderText); 
    }).change(function(e) {
      input.data('changed', input.val() !== '');
    });
  }

  function setPlaceholderOrFlagChanged(input, text) {
    (input.val() === '') ? input.val(text) : input.data('changed', true);
  }

  function setupPasswords(input) {
    var passwordPlaceholder = createPasswordPlaceholder(input);
    input.after(passwordPlaceholder);

    (input.val() === '') ? input.hide() : passwordPlaceholder.hide();

    $(input).blur(function(e) {
      if (input.val() !== '') return;
      input.hide();
      passwordPlaceholder.show();
    });

    $(passwordPlaceholder).focus(function(e) {
      input.show().focus();
      passwordPlaceholder.hide();
    });
  }

  function createPasswordPlaceholder(input) {
    return $('<input>').attr({
      placeholder: input.attr('placeholder'),
      value: input.attr('placeholder'),
      id: input.attr('id'),
      readonly: true
    }).addClass(input.attr('class'));
  }

  function clearPlaceholdersBeforeSubmit(form) {
    form.find(':input[placeholder]').each(function() {
      if ($(this).data('changed') === true) return;
      if ($(this).val() === $(this).attr('placeholder')) $(this).val('');
    });
  }
})(jQuery);

;
/**
 * jQuery Validation Plugin 1.8.1
 *
 * 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
 *
 * Note: Max Edmands from Marker Seven added messagesComplete handler
 */

(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;
    }

    validator = new $.validator( options, this[0] );
    $.data(this[0], 'validator', validator);

    if ( validator.settings.onsubmit ) {

      // allow suppresing validation by adding a cancel class to the submit button
      this.find("input, button").filter(".cancel").click(function() {
        validator.cancelSubmit = true;
      });

      // when a submitHandler is used, capture the submitting button
      if (validator.settings.submitHandler) {
        this.find("input, button").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: [],
    ignoreTitle: false,
    onfocusin: function(element) {
      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) {
      if ( !this.checkable(element) && (element.name in this.submitted || !this.optional(element)) ) {
        this.element(element);
      }
    },
    onkeyup: function(element) {
      if ( element.name in this.submitted || element == this.lastElement ) {
        this.element(element);
      }
    },
    onclick: function(element) {
      // 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] );
      }
      $(this.currentForm)
        .validateDelegate(":text, :password, :file, select, textarea", "focusin focusout keyup", delegate)
        .validateDelegate(":radio, :checkbox, select, option", "click", delegate);

      if (this.settings.invalidHandler)
        $(this.currentForm).bind("invalid-form.validate", this.settings.invalidHandler);

      if (this.settings.messagesComplete)
        $(this.currentForm).bind("messages-complete", this.settings.messagesComplete);
    },

    // 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.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.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.clean( element );

      // if radio/checkbox, validate first element in group instead
      if (this.checkable(element)) {
        element = this.findByName( element.name ).not(this.settings.ignore)[0];
      }

      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();

      $(this.currentForm).triggerHandler("messages-complete", [this]);
    },

    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().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);
    },

    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 = $element.attr(method);
      if (value) {
        rules[method] = value;
      }
    }

    // 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 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);

;
/* This script is for the site-wide search. */
(function($, undefined) {
  if(window.EB === undefined) window.EB = {};
  var EB = window.EB;

  var settings = {
    searches: [
      {
        name: 'Entire Site',
        action: '/search'
      },
      {
        name: 'Recipes',
        action: '/search/recipes'
      },
      {
        name: 'Products',
        action: '/search/products'
      },
      {
        name: 'News & Events',
        action: '/search/news'
      }
    ],
    formReference: '#search-box',
    searchField: '#search-box .searchq',
    selectorBoxId: 'selectors',
    selectorRadioName: 'selectors-radio',
    selectorItemClass: 'search-option',
    selectorItemLabelSeparator: '_search-',
    selectorItemBoxSeparator: '_box',
    selectorItemBoxClass: 'search-option-box'
  };
  
  EB.search = {
    settings: settings,
    selectorBox: undefined,
    mouseCurrentlyOver: false,
    searchChoicesReference: settings.formReference + ' .' + settings.selectorItemClass,
    placeholder: undefined,

    initialize: function() {
      var $formReference = $(settings.formReference);
      var $radios = $(EB.search.searchChoicesReference);
      var $searchField = $(settings.searchField);

      $formReference.live('focusin', function() {
        EB.search.showSelectors();
      });
      $formReference.live('focusout', function() {
        EB.search.checkForLostFocus();
      });
      $formReference.live('submit', function() {
        EB.search.submit();
      });

      $radios.live('click', EB.search.searchClicked);
      $radios.live('change', EB.search.searchChanged);

      $searchField.live('keydown', EB.search.keyPressed);

      return this;
    },

    checkForLostFocus: function() {
      var searchIsActive = $(document.activeElement).parents(settings.formReference).length > 0;

      if(!EB.search.mouseCurrentlyOver && !searchIsActive) {
        EB.search.hideSelectors();
      }
    },

    mouseOverBox: function(event) {
      EB.search.mouseCurrentlyOver = true;
    },

    mouseOutBox: function(event) {
      EB.search.mouseCurrentlyOver = false;
      EB.search.checkForLostFocus();
    },

    showSelectors: function() {
      if(EB.search.selectorBox === undefined) {
        EB.search.constructSelectors();
      }
      
      $(settings.searchField).attr('placeholder', '');
      $(settings.formReference).addClass('showing-selectors');
    },

    hideSelectors: function() {
      if(EB.search.selectorBox !== undefined) {
        $(settings.formReference).removeClass('showing-selectors');
        $(settings.searchField).attr('placeholder', EB.search.placeholder);
      }
    },

    getCurrentSearch: function() {
      var $choice = $(EB.search.searchChoicesReference).filter(':checked');
      return $choice.data('search');
    },

    getNextSearch: function() {
      var current = EB.search.getCurrentSearch();
      if(current === undefined || current.index == settings.searches.length - 1) {
        return undefined;
      }
      else {
        return settings.searches[current.index + 1];
      }
    },

    getPrevSearch: function() {
      var current = EB.search.getCurrentSearch();
      if(current === undefined || current.index === 0) {
        return undefined;
      }
      else {
        return settings.searches[current.index - 1];
      }
    },

    searchChanged: function() {
      var selectorItemBoxClass = '.' + settings.selectorItemBoxClass;
      var $form = $(settings.formReference);
      var $choice = $(EB.search.searchChoicesReference).filter(':checked');
      var $box = $choice.parents(selectorItemBoxClass);

      $form.find(selectorItemBoxClass).not($box).removeClass('on');
      $box.addClass('on');

      $form.attr('action', $choice.val());
    },

    keyPressed: function(event) {
      // Up arrow
      if(event.which === 38) {
        EB.search.setActive(EB.search.getPrevSearch());
        event.preventDefault();
      }

      // Down arrow
      if(event.which === 40) {
        EB.search.setActive(EB.search.getNextSearch());
        event.preventDefault();
      }

      // Escape
      if(event.which === 27) {
        $(settings.searchField).blur();
        event.preventDefault();
      }
    },

    setActive: function(search) {
      if(search !== undefined) {
        search.selector.attr('checked', true);
        EB.search.searchChanged();
      }
    },

    searchClicked: function() {
      var $field = $(settings.searchField);
      var $form = $(settings.formReference);

      EB.search.searchChanged();

      if($field.val() == '') {
        $field.focus();
      }
      else {
        $form.submit();
      }
    },

    constructSelectors: function() {
      var selectorBox = EB.search.selectorBox =
        $('<div>').attr('id', settings.selectorBoxId);
      var defaultAction = $(settings.formReference).attr('action');
      
      $.each(settings.searches, function(i, search) {
        var labelId = settings.selectorBoxId + settings.selectorItemLabelSeparator + i;

        var label = $('<label>')
          .attr('for', labelId)
          .html(search.name);

        var selector = $('<input>')
          .attr('type', 'radio')
          .attr('id', labelId)
          .attr('value', search.action)
          .attr('name', settings.selectorRadioName)
          .addClass(settings.selectorItemClass)
          .data('search', search);

        var labelBox = $('<div>')
          .attr('id', labelId + settings.selectorItemBoxSeparator)
          .addClass(settings.selectorItemBoxClass)
          .append(selector)
          .append(label)
          .appendTo(selectorBox);

        // Augment the search index object with extra info
        search.index = i;
        search.selector = selector;
      });

      selectorBox.appendTo($(settings.formReference));
      selectorBox.bind('mouseenter', EB.search.mouseOverBox);
      selectorBox.bind('mouseleave', EB.search.mouseOutBox);

      // Select the default action
      selectorBox.find('[value=\'' + defaultAction + '\']').attr('checked', true);
      EB.search.searchChanged();

      // Save the placeholder
      EB.search.placeholder = $(settings.searchField).attr('placeholder');

      // Disable autocomplete
      $(settings.searchField).attr('autocomplete', 'off');
    },

    submit: function() {
      if(EB.search.selectorBox !== undefined) {
        EB.search.selectorBox.remove();
      }
    }
  };

  EB.search.initialize();
})(jQuery);
;
/* This script makes the menu dropdowns at the top of the page work the way they should.
 * Specifically, it makes it so that if you are hovering over a nav item, and you move your mouse
 * down and accross to get to the next nav item, the nav will not switch.
 *
 * Also, here is a buffalo:
 *
 *              _.-````'-,_
 *    _,.,_ ,-'`           `'-.,_
 *  /)     (\                   '``-.
 * ((      ) )                      `\
 *  \)    (_/                        )\
 *   |       /)           '    ,'    / \
 *   `\    ^'            '     (    /  ))
 *     |      _/\ ,     /    ,,`\   (  "`
 *      \Y,   |  \  \  | ````| / \_ \
 *        `)_/    \  \  )    ( >  ( >
 *                 \( \(     |/   |/
 *                /_(/_(    /_(  /_(
 */

(function($, undefined) {
  if(window.EB === undefined) window.EB = {};
  var EB = window.EB;
  var settings = {
    timeout: 100, // time that the user can spend out of a nav before it switches
    speed: 50, // time to spend between checks as to whether user moved mouse up or down
    tolerance: -10 // how far must the user move the mouse to prevent the nav from switching?
  };

  window.EB.dd = {
    settings: settings,
    mousePositionAtLastTimeout: undefined,
    activeNavItem: undefined,
    lastSeenHovering: undefined,
    lastMouseSpeedTimeout: undefined,
    currentlyHoveringNavItem: undefined,
    currentMousePosition: undefined,
    currentTimeoutEvent: undefined,

    initialize: function() {
      $('#primary > li').live('mouseenter', EB.dd.mouseenter);
      return this;
    },

    mouseenter: function(event) {
      EB.dd.enter(event.target);
    },

    enter: function(target) {
      if(EB.dd.activeNavItem === undefined) {
        target = $(target);

        if(!target.is('#primary > li')) {
          target = target.closest('#primary > li');
          if(target.length === 0) return;
        }

        EB.dd.activeNavItem = target;
        target.addClass('hovering');

        // Keep track of mouse movements
        $('body').one('mousemove', EB.dd.mouseMove);
      }

    },

    leave: function(target) {
      if($(target).length > 0) {
        target.removeClass('hovering');
        EB.dd.activeNavItem = undefined;
        EB.dd.mousePositionAtLastTimeout = undefined;
        EB.dd.lastSeenHovering = undefined;
        EB.dd.lastMouseSpeedTimeout = undefined;
      }
    },

    isHoveringOverRecentNavItem: function() {
      var hoveringOver = EB.dd.currentlyHoveringNavItem;
      var active = EB.dd.activeNavItem;
      var isHovering =  hoveringOver.length > 0 && active.length > 0 && hoveringOver.get(0) === active.get(0);

      if(isHovering) {
        EB.dd.lastSeenHovering = new Date().getTime();
      }

      return isHovering;
    },

    isMovingDown: function() {
      var currentTime = new Date().getTime();
      var timeoutOffset = currentTime - EB.dd.lastSeenHovering;
      var mouseYOffset = EB.dd.currentMousePosition - EB.dd.mousePositionAtLastTimeout;

      if(EB.dd.lastMouseSpeedTimeout === undefined) {
        EB.dd.lastMouseSpeedTimeout = currentTime;
      }

      // If we haven't reached the speed timeout yet, then ignore until we have reached it
      var speedTimeoutOffset = currentTime - EB.dd.lastMouseSpeedTimeout;
      if(speedTimeoutOffset !== 0 && speedTimeoutOffset < EB.dd.settings.speed) {
        return true;
      }

      // Reset the speed timeout
      EB.dd.lastMouseSpeedTimeout = currentTime;

      // Create a timeout event
      if(EB.dd.currentTimeoutEvent !== undefined) {
        window.clearTimeout(EB.dd.currentTimeoutEvent);
      }
      EB.dd.currentTimeoutEvent = window.setTimeout(EB.dd.movementTimeout, EB.dd.settings.speed);

      // Our two tests:
      var hasGlobalTimeoutPassed = timeoutOffset > EB.dd.settings.timeout;
      var hasUserMovedMouseEnough = mouseYOffset >= EB.dd.settings.tolerance;

      return !hasGlobalTimeoutPassed && hasUserMovedMouseEnough;
    },

    mouseMove: function(event) {
      EB.dd.currentlyHoveringNavItem = $(event.target).closest('#primary > li');
      EB.dd.currentMousePosition = event.pageY;
      EB.dd.checkMovement();
    },

    movementTimeout: function() {
      if(EB.dd.activeNavItem !== undefined) {
        EB.dd.checkMovement();
      }
    },

    checkMovement: function() {
      var isOverANavItem = EB.dd.currentlyHoveringNavItem.length > 0;

      if(isOverANavItem && (EB.dd.isHoveringOverRecentNavItem() || EB.dd.isMovingDown())) {
        EB.dd.mousePositionAtLastTimeout = EB.dd.currentMousePosition;
        $('body').one('mousemove', EB.dd.mouseMove);
      }

      else {
        EB.dd.leave(EB.dd.activeNavItem);
        EB.dd.enter(EB.dd.currentlyHoveringNavItem);
      }

    }
  };
  
  EB.dd.initialize();

})(jQuery);

;
(function($, undefined) {
  if(window.EB === undefined) window.EB = {};
  var EB = window.EB;
  var settings = {
    emailSelector: 'input.email'
  };

  Drupal.behaviors.webformColorbox = {
    attach: function(context, settings) {
      if(!$.isFunction($.colorbox)) {
        return;
      }
      $('a.webform-colorbox', context).once('webform-colorbox-load', function() {
        var settings = {};

        // Give loaded href the colorbox attribute
        settings.href = $(this).attr('href');
        if(settings.href.match(/\?/)) {
          settings.href += '&colorbox=yes';
        }
        else {
          settings.href += '?colorbox=yes';
        }

        // Some static settings
        settings.width = 620;
        settings.scrolling = false;

        // Events
        settings.onComplete = EB.webformColorbox.completed;

        $(this).colorbox($.extend({}, settings.colorbox, settings));
      });
    }
  }

  EB.webformColorbox = {
    completed: function() {
      var $form = $('#cboxLoadedContent form');
      if($form.length > 0) {
        // Process the form
        EB.webformColorbox.process($form);

        // Focus on the first form element
        // (not doing this at the moment because there is no label, only a placeholder attribute)
        //$form.find('.form-item input').filter(':first').focus();
      }
    },
    process: function($form) {
      var action = $form.attr('action');
      var $emailFields = $form.find(settings.emailSelector);

      $form.attr('action', action.replace(/.colorbox\=yes/, ''));
      $form.validate({
        messagesComplete: EB.webformColorbox.invalidMessagesComplete
      });
      
      if($emailFields.length > 0) {
        $emailFields.rules('add', {
          email: true
        });
      }
    },
    invalidMessagesComplete: function(event, validator) {
      if(!$(document.activeElement).is('input, textarea')) {
        $.colorbox.resize();
      }
    }
  }

})(jQuery);

;
/**
 * @license 
 * jQuery Tools @VERSION Tooltip - UI essentials
 * 
 * NO COPYRIGHTS OR LICENSES. DO WHAT YOU LIKE.
 * 
 * http://flowplayer.org/tools/tooltip/
 *
 * Since: November 2008
 * Date: @DATE 
 */
(function($) {  
  // static constructs
  $.tools = $.tools || {version: '@VERSION'};
  
  $.tools.tooltip = {
    
    conf: { 
      
      // default effect variables
      effect: 'toggle',     
      fadeOutSpeed: "fast",
      predelay: 0,
      delay: 30,
      opacity: 1,     
      tip: 0,
            fadeIE: false, // enables fade effect in IE
      
      // 'top', 'bottom', 'right', 'left', 'center'
      position: ['top', 'center'], 
      offset: [0, 0],
      relative: false,
      cancelDefault: true,
      
      // type to event mapping 
      events: {
        def:      "mouseenter,mouseleave",
        input:    "focus,blur",
        widget:   "focus mouseenter,blur mouseleave",
        tooltip:    "mouseenter,mouseleave"
      },
      
      // 1.2
      layout: '<div/>',
      tipClass: 'tooltip',
      tipInner: ''
    },
    
    addEffect: function(name, loadFn, hideFn) {
      effects[name] = [loadFn, hideFn]; 
    } 
  };
  
  
  var effects = { 
    toggle: [ 
      function(done) { 
        var conf = this.getConf(), tip = this.getTip(), o = conf.opacity;
        if (o < 1) { tip.css({opacity: o}); }
        tip.show();
        done.call();
      },
      
      function(done) { 
        this.getTip().hide();
        done.call();
      } 
    ],
    
    fade: [
      function(done) {
        var conf = this.getConf();
        if (!$.browser.msie || conf.fadeIE) {
          this.getTip().fadeTo(conf.fadeInSpeed, conf.opacity, done);
        }
        else {
          this.getTip().show();
          done();
        }
      },
      function(done) {
        var conf = this.getConf();
        if (!$.browser.msie || conf.fadeIE) {
          this.getTip().fadeOut(conf.fadeOutSpeed, done);
        }
        else {
          this.getTip().hide();
          done();
        }
      }
    ]   
  };   

    
  /* calculate tip position relative to the trigger */    
  function getPosition(trigger, tip, conf) {  

    
    // get origin top/left position 
    var top = conf.relative ? trigger.position().top : trigger.offset().top, 
       left = conf.relative ? trigger.position().left : trigger.offset().left,
       pos = conf.position[0];

    top  -= tip.outerHeight() - conf.offset[0];
    left += trigger.outerWidth() + conf.offset[1];
    
    // iPad position fix
    if (/iPad/i.test(navigator.userAgent)) {
      top -= $(window).scrollTop();
    }
    
    // adjust Y   
    var height = tip.outerHeight() + trigger.outerHeight();
    if (pos == 'center')  { top += height / 2; }
    if (pos == 'bottom')  { top += height; }
    
    
    // adjust X
    pos = conf.position[1];   
    var width = tip.outerWidth() + trigger.outerWidth();
    if (pos == 'center')  { left -= width / 2; }
    if (pos == 'left')    { left -= width; }   
    
    return {top: top, left: left};
  }   

  
  
  function Tooltip(trigger, conf) {

    var self = this, 
       fire = trigger.add(self),
       tip,
       timer = 0,
       pretimer = 0, 
       title = trigger.attr("title"),
       tipAttr = trigger.attr("data-tooltip"),
       effect = effects[conf.effect],
       shown,
         
       // get show/hide configuration
       isInput = trigger.is(":input"), 
       isWidget = isInput && trigger.is(":checkbox, :radio, select, :button, :submit"),     
       type = trigger.attr("type"),
       evt = conf.events[type] || conf.events[isInput ? (isWidget ? 'widget' : 'input') : 'def']; 
    
    
    // check that configuration is sane
    if (!effect) { throw "Nonexistent effect \"" + conf.effect + "\""; }          
    
    evt = evt.split(/,\s*/); 
    if (evt.length != 2) { throw "Tooltip: bad events configuration for " + type; } 
    
    
    // trigger --> show  
    trigger.bind(evt[0], function(e) {

      clearTimeout(timer);
      if (conf.predelay) {
        pretimer = setTimeout(function() { self.show(e); }, conf.predelay); 
        
      } else {
        self.show(e); 
      }
      
    // trigger --> hide
    }).bind(evt[1], function(e)  {
      clearTimeout(pretimer);
      if (conf.delay)  {
        timer = setTimeout(function() { self.hide(e); }, conf.delay); 
        
      } else {
        self.hide(e);   
      }
      
    }); 
    
    
    // remove default title
    if (title && conf.cancelDefault) { 
      trigger.removeAttr("title");
      trigger.data("title", title);     
    }   
    
    $.extend(self, {
        
      show: function(e) {  

        // tip not initialized yet
        if (!tip) {
          
          // data-tooltip 
          if (tipAttr) {
            tip = $(tipAttr);

          // single tip element for all
          } else if (conf.tip) { 
            tip = $(conf.tip).eq(0);
            
          // autogenerated tooltip
          } else if (title) { 
            tip = $(conf.layout).addClass(conf.tipClass).appendTo(document.body).hide();

            if(conf.tipInner) {
              tip.find('.' + conf.tipInner).html(title);
            }
            else {
              tip.append(title);
            }

          // manual tooltip
          } else {  
            tip = trigger.next();  
            if (!tip.length) { tip = trigger.parent().next(); }    
          }
          
          if (!tip.length) { throw "Cannot find tooltip for " + trigger;  }
        } 
        
        if (self.isShown()) { return self; }  
        
        // stop previous animation
        tip.stop(true, true);         
        
        // get position
        var pos = getPosition(trigger, tip, conf);      
    
        // restore title for single tooltip element
        if (conf.tip) {
          tip.html(trigger.data("title"));
        }

        // onBeforeShow
        e = $.Event();
        e.type = "onBeforeShow";
        fire.trigger(e, [pos]);       
        if (e.isDefaultPrevented()) { return self; }
    
        
        // onBeforeShow may have altered the configuration
        pos = getPosition(trigger, tip, conf);
        
        // set position
        tip.css({position:'absolute', top: pos.top, left: pos.left});         
        
        shown = true;
        
        // invoke effect 
        effect[0].call(self, function() {
          e.type = "onShow";
          shown = 'full';
          fire.trigger(e);     
        });         

    
        // tooltip events       
        var event = conf.events.tooltip.split(/,\s*/);

        if (!tip.data("__set")) {
          
          tip.unbind(event[0]).bind(event[0], function() { 
            clearTimeout(timer);
            clearTimeout(pretimer);
          });
          
          if (event[1] && !trigger.is("input:not(:checkbox, :radio), textarea")) {          
            tip.unbind(event[1]).bind(event[1], function(e) {
  
              // being moved to the trigger element
              if (e.relatedTarget != trigger[0]) {
                trigger.trigger(evt[1].split(" ")[0]);
              }
            }); 
          } 
          
          // bind agein for if same tip element
          if (!conf.tip) tip.data("__set", true);
        }
        
        return self;
      },
      
      hide: function(e) {

        if (!tip || !self.isShown()) { return self; }
      
        // onBeforeHide
        e = $.Event();
        e.type = "onBeforeHide";
        fire.trigger(e);        
        if (e.isDefaultPrevented()) { return; }
  
        shown = false;
        
        effects[conf.effect][1].call(self, function() {
          e.type = "onHide";
          fire.trigger(e);     
        });
        
        return self;
      },
      
      isShown: function(fully) {
        return fully ? shown == 'full' : shown; 
      },
        
      getConf: function() {
        return conf;  
      },
        
      getTip: function() {
        return tip; 
      },
      
      getTrigger: function() {
        return trigger; 
      }   

    });   

    // callbacks  
    $.each("onHide,onBeforeShow,onShow,onBeforeHide".split(","), function(i, name) {
        
      // configuration
      if ($.isFunction(conf[name])) { 
        $(self).bind(name, conf[name]); 
      }

      // API
      self[name] = function(fn) {
        if (fn) { $(self).bind(name, fn); }
        return self;
      };
    });
    
  }
    
  
  // jQuery plugin implementation
  $.fn.tooltip = function(conf) {
    
    // return existing instance
    var api = this.data("tooltip");
    if (api) { return api; }

    conf = $.extend(true, {}, $.tools.tooltip.conf, conf);
    
    // position can also be given as string
    if (typeof conf.position == 'string') {
      conf.position = conf.position.split(/,?\s/);  
    }
    
    // install tooltip for each entry in jQuery object
    this.each(function() {
      api = new Tooltip($(this), conf); 
      $(this).data("tooltip", api); 
    });
    
    return conf.api ? api: this;     
  };
    
}) (jQuery);

    
/**
 * @license 
 * jQuery Tools @VERSION / Tooltip Dynamic Positioning
 * 
 * NO COPYRIGHTS OR LICENSES. DO WHAT YOU LIKE.
 * 
 * http://flowplayer.org/tools/tooltip/dynamic.html
 *
 * Since: July 2009
 * Date: @DATE 
 */
(function($) { 

  // version number
  var t = $.tools.tooltip;
  
  t.dynamic = {
    conf: {
      classNames: "top right bottom left"
    }
  };
    
  /* 
   * See if element is on the viewport. Returns an boolean array specifying which
   * edges are hidden. Edges are in following order:
   * 
   * [top, right, bottom, left]
   * 
   * For example following return value means that top and right edges are hidden
   * 
   * [true, true, false, false]
   * 
   */
  function getCropping(el) {
    
    var w = $(window); 
    var right = w.width() + w.scrollLeft();
    var bottom = w.height() + w.scrollTop();    
    
    return [
      el.offset().top <= w.scrollTop(),             // top
      right <= el.offset().left + el.width(),       // right
      bottom <= el.offset().top + el.height(),      // bottom
      w.scrollLeft() >= el.offset().left          // left
    ]; 
  }
  
  /*
    Returns true if all edges of an element are on viewport. false if not
    
    @param crop the cropping array returned by getCropping function
   */
  function isVisible(crop) {
    var i = crop.length;
    while (i--) {
      if (crop[i]) { return false; }  
    }
    return true;
  }
  
  // dynamic plugin
  $.fn.dynamic = function(conf) {
    
    if (typeof conf == 'number') { conf = {speed: conf}; }
    
    conf = $.extend({}, t.dynamic.conf, conf);
    
    var confOrigin = $.extend(true,{},conf),
        cls = conf.classNames.split(/\s/), 
        orig;
      
    this.each(function() {    
        
      var api = $(this).tooltip().onBeforeShow(function(e, pos) {       

        // get nessessary variables
        var tip = this.getTip(), tipConf = this.getConf();  

        /*
          We store the original configuration and use it to restore back to the original state.
        */          
        if (!orig) {
          orig = [
            tipConf.position[0], 
            tipConf.position[1], 
            tipConf.offset[0], 
            tipConf.offset[1], 
            $.extend({}, tipConf)
          ];
        }
        
        /*
          display tip in it's default position and by setting visibility to hidden.
          this way we can check whether it will be on the viewport
        */
        $.extend(tipConf, orig[4]);
        tipConf.position = [orig[0], orig[1]];
        tipConf.offset = [orig[2], orig[3]];

        tip.css({
          visibility: 'hidden',
          position: 'absolute',
          top: pos.top,
          left: pos.left 
        }).show(); 
        
        var conf = $.extend(true,{},confOrigin),
        
            // now let's see for hidden edges
            crop = getCropping(tip);    
                
        // possibly alter the configuration
        if (!isVisible(crop)) {
          
          // change the position and add class
          if (crop[2]) { $.extend(tipConf, conf.top);   tipConf.position[0] = 'top';    tip.addClass(cls[0]); }
          if (crop[3]) { $.extend(tipConf, conf.right); tipConf.position[1] = 'right';  tip.addClass(cls[1]); }         
          if (crop[0]) { $.extend(tipConf, conf.bottom);  tipConf.position[0] = 'bottom'; tip.addClass(cls[2]); } 
          if (crop[1]) { $.extend(tipConf, conf.left);    tipConf.position[1] = 'left';   tip.addClass(cls[3]); }         
          
          // vertical offset
          if (crop[0] || crop[2]) { tipConf.offset[0] *= -1; }
          
          // horizontal offset
          if (crop[1] || crop[3]) { tipConf.offset[1] *= -1; }
        }  
        
        tip.css({visibility: 'visible'}).hide();
    
      });
      
      // restore positioning as soon as possible
      api.onBeforeShow(function() {
        var c = this.getConf(), tip = this.getTip();     
        setTimeout(function() { 
          c.position = [orig[0], orig[1]];
          c.offset = [orig[2], orig[3]];
        }, 0);
      });
      
      // remove custom class names and restore original effect
      api.onHide(function() {
        var tip = this.getTip(); 
        tip.removeClass(conf.classNames);
      });
        
      ret = api;
      
    });
    
    return conf.api ? ret : this;
  };  
  
}) (jQuery);
;
(function($, undefined) {

  Drupal.behaviors.trademark = {
    locations: [
      '.product-category-title',
      '.views-field-field-content-title'
    ],

    replacers: [
      '&#174;',
      '&#xae;',
      '&reg;',
      "\u00AE"
    ],

    attach: function(context) {
      $.each(Drupal.behaviors.trademark.locations, function(index, location) {
        Drupal.behaviors.trademark.traverseForTextNodes($(context).find(location));
      });
    },

    traverseForTextNodes: function($elements) {
      $.each($elements.contents(), function(index, element) {
        var $element = $(element);
        if(element.nodeType === 3) Drupal.behaviors.trademark.updateText(element);
        else Drupal.behaviors.trademark.traverseForTextNodes($element);
      });
    },

    updateText: function(element) {
      var $element = $(element);
      var text = $element.text();
      var changed = false;
      $.each(Drupal.behaviors.trademark.replacers, function(index, replacer) {
        if(text.indexOf(replacer) != -1) {
          text = text.replace(replacer, '<sup>' + replacer + '</sup>');
          changed = true;
        }
      });

      if(changed) $element.replaceWith(text);
    }
  }

})(jQuery);
;

