").append( jQuery.parseHTML( responseText ) ).find( selector ) :
+
+ // Otherwise use the full result
+ responseText );
+
+ }).complete( callback && function( jqXHR, status ) {
+ self.each( callback, response || [ jqXHR.responseText, status, jqXHR ] );
+ });
+ }
+
+ return this;
+};
+
+
+
+
+// Attach a bunch of functions for handling common AJAX events
+jQuery.each( [ "ajaxStart", "ajaxStop", "ajaxComplete", "ajaxError", "ajaxSuccess", "ajaxSend" ], function( i, type ) {
+ jQuery.fn[ type ] = function( fn ) {
+ return this.on( type, fn );
+ };
+});
+
+
+
+
+jQuery.expr.filters.animated = function( elem ) {
+ return jQuery.grep(jQuery.timers, function( fn ) {
+ return elem === fn.elem;
+ }).length;
+};
+
+
+
+
+
+var docElem = window.document.documentElement;
+
+/**
+ * Gets a window from an element
+ */
+function getWindow( elem ) {
+ return jQuery.isWindow( elem ) ?
+ elem :
+ elem.nodeType === 9 ?
+ elem.defaultView || elem.parentWindow :
+ false;
+}
+
+jQuery.offset = {
+ setOffset: function( elem, options, i ) {
+ var curPosition, curLeft, curCSSTop, curTop, curOffset, curCSSLeft, calculatePosition,
+ position = jQuery.css( elem, "position" ),
+ curElem = jQuery( elem ),
+ props = {};
+
+ // set position first, in-case top/left are set even on static elem
+ if ( position === "static" ) {
+ elem.style.position = "relative";
+ }
+
+ curOffset = curElem.offset();
+ curCSSTop = jQuery.css( elem, "top" );
+ curCSSLeft = jQuery.css( elem, "left" );
+ calculatePosition = ( position === "absolute" || position === "fixed" ) &&
+ jQuery.inArray("auto", [ curCSSTop, curCSSLeft ] ) > -1;
+
+ // need to be able to calculate position if either top or left is auto and position is either absolute or fixed
+ if ( calculatePosition ) {
+ curPosition = curElem.position();
+ curTop = curPosition.top;
+ curLeft = curPosition.left;
+ } else {
+ curTop = parseFloat( curCSSTop ) || 0;
+ curLeft = parseFloat( curCSSLeft ) || 0;
+ }
+
+ if ( jQuery.isFunction( options ) ) {
+ options = options.call( elem, i, curOffset );
+ }
+
+ if ( options.top != null ) {
+ props.top = ( options.top - curOffset.top ) + curTop;
+ }
+ if ( options.left != null ) {
+ props.left = ( options.left - curOffset.left ) + curLeft;
+ }
+
+ if ( "using" in options ) {
+ options.using.call( elem, props );
+ } else {
+ curElem.css( props );
+ }
+ }
+};
+
+jQuery.fn.extend({
+ offset: function( options ) {
+ if ( arguments.length ) {
+ return options === undefined ?
+ this :
+ this.each(function( i ) {
+ jQuery.offset.setOffset( this, options, i );
+ });
+ }
+
+ var docElem, win,
+ box = { top: 0, left: 0 },
+ elem = this[ 0 ],
+ doc = elem && elem.ownerDocument;
+
+ if ( !doc ) {
+ return;
+ }
+
+ docElem = doc.documentElement;
+
+ // Make sure it's not a disconnected DOM node
+ if ( !jQuery.contains( docElem, elem ) ) {
+ return box;
+ }
+
+ // If we don't have gBCR, just use 0,0 rather than error
+ // BlackBerry 5, iOS 3 (original iPhone)
+ if ( typeof elem.getBoundingClientRect !== strundefined ) {
+ box = elem.getBoundingClientRect();
+ }
+ win = getWindow( doc );
+ return {
+ top: box.top + ( win.pageYOffset || docElem.scrollTop ) - ( docElem.clientTop || 0 ),
+ left: box.left + ( win.pageXOffset || docElem.scrollLeft ) - ( docElem.clientLeft || 0 )
+ };
+ },
+
+ position: function() {
+ if ( !this[ 0 ] ) {
+ return;
+ }
+
+ var offsetParent, offset,
+ parentOffset = { top: 0, left: 0 },
+ elem = this[ 0 ];
+
+ // fixed elements are offset from window (parentOffset = {top:0, left: 0}, because it is its only offset parent
+ if ( jQuery.css( elem, "position" ) === "fixed" ) {
+ // we assume that getBoundingClientRect is available when computed position is fixed
+ offset = elem.getBoundingClientRect();
+ } else {
+ // Get *real* offsetParent
+ offsetParent = this.offsetParent();
+
+ // Get correct offsets
+ offset = this.offset();
+ if ( !jQuery.nodeName( offsetParent[ 0 ], "html" ) ) {
+ parentOffset = offsetParent.offset();
+ }
+
+ // Add offsetParent borders
+ parentOffset.top += jQuery.css( offsetParent[ 0 ], "borderTopWidth", true );
+ parentOffset.left += jQuery.css( offsetParent[ 0 ], "borderLeftWidth", true );
+ }
+
+ // Subtract parent offsets and element margins
+ // note: when an element has margin: auto the offsetLeft and marginLeft
+ // are the same in Safari causing offset.left to incorrectly be 0
+ return {
+ top: offset.top - parentOffset.top - jQuery.css( elem, "marginTop", true ),
+ left: offset.left - parentOffset.left - jQuery.css( elem, "marginLeft", true)
+ };
+ },
+
+ offsetParent: function() {
+ return this.map(function() {
+ var offsetParent = this.offsetParent || docElem;
+
+ while ( offsetParent && ( !jQuery.nodeName( offsetParent, "html" ) && jQuery.css( offsetParent, "position" ) === "static" ) ) {
+ offsetParent = offsetParent.offsetParent;
+ }
+ return offsetParent || docElem;
+ });
+ }
+});
+
+// Create scrollLeft and scrollTop methods
+jQuery.each( { scrollLeft: "pageXOffset", scrollTop: "pageYOffset" }, function( method, prop ) {
+ var top = /Y/.test( prop );
+
+ jQuery.fn[ method ] = function( val ) {
+ return access( this, function( elem, method, val ) {
+ var win = getWindow( elem );
+
+ if ( val === undefined ) {
+ return win ? (prop in win) ? win[ prop ] :
+ win.document.documentElement[ method ] :
+ elem[ method ];
+ }
+
+ if ( win ) {
+ win.scrollTo(
+ !top ? val : jQuery( win ).scrollLeft(),
+ top ? val : jQuery( win ).scrollTop()
+ );
+
+ } else {
+ elem[ method ] = val;
+ }
+ }, method, val, arguments.length, null );
+ };
+});
+
+// Add the top/left cssHooks using jQuery.fn.position
+// Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084
+// getComputedStyle returns percent when specified for top/left/bottom/right
+// rather than make the css module depend on the offset module, we just check for it here
+jQuery.each( [ "top", "left" ], function( i, prop ) {
+ jQuery.cssHooks[ prop ] = addGetHookIf( support.pixelPosition,
+ function( elem, computed ) {
+ if ( computed ) {
+ computed = curCSS( elem, prop );
+ // if curCSS returns percentage, fallback to offset
+ return rnumnonpx.test( computed ) ?
+ jQuery( elem ).position()[ prop ] + "px" :
+ computed;
+ }
+ }
+ );
+});
+
+
+// Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods
+jQuery.each( { Height: "height", Width: "width" }, function( name, type ) {
+ jQuery.each( { padding: "inner" + name, content: type, "": "outer" + name }, function( defaultExtra, funcName ) {
+ // margin is only for outerHeight, outerWidth
+ jQuery.fn[ funcName ] = function( margin, value ) {
+ var chainable = arguments.length && ( defaultExtra || typeof margin !== "boolean" ),
+ extra = defaultExtra || ( margin === true || value === true ? "margin" : "border" );
+
+ return access( this, function( elem, type, value ) {
+ var doc;
+
+ if ( jQuery.isWindow( elem ) ) {
+ // As of 5/8/2012 this will yield incorrect results for Mobile Safari, but there
+ // isn't a whole lot we can do. See pull request at this URL for discussion:
+ // https://github.com/jquery/jquery/pull/764
+ return elem.document.documentElement[ "client" + name ];
+ }
+
+ // Get document width or height
+ if ( elem.nodeType === 9 ) {
+ doc = elem.documentElement;
+
+ // Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height], whichever is greatest
+ // unfortunately, this causes bug #3838 in IE6/8 only, but there is currently no good, small way to fix it.
+ return Math.max(
+ elem.body[ "scroll" + name ], doc[ "scroll" + name ],
+ elem.body[ "offset" + name ], doc[ "offset" + name ],
+ doc[ "client" + name ]
+ );
+ }
+
+ return value === undefined ?
+ // Get width or height on the element, requesting but not forcing parseFloat
+ jQuery.css( elem, type, extra ) :
+
+ // Set width or height on the element
+ jQuery.style( elem, type, value, extra );
+ }, type, chainable ? margin : undefined, chainable, null );
+ };
+ });
+});
+
+
+// The number of elements contained in the matched element set
+jQuery.fn.size = function() {
+ return this.length;
+};
+
+jQuery.fn.andSelf = jQuery.fn.addBack;
+
+
+
+
+// Register as a named AMD module, since jQuery can be concatenated with other
+// files that may use define, but not via a proper concatenation script that
+// understands anonymous AMD modules. A named AMD is safest and most robust
+// way to register. Lowercase jquery is used because AMD module names are
+// derived from file names, and jQuery is normally delivered in a lowercase
+// file name. Do this after creating the global so that if an AMD module wants
+// to call noConflict to hide this version of jQuery, it will work.
+
+// Note that for maximum portability, libraries that are not jQuery should
+// declare themselves as anonymous modules, and avoid setting a global if an
+// AMD loader is present. jQuery is a special case. For more information, see
+// https://github.com/jrburke/requirejs/wiki/Updating-existing-libraries#wiki-anon
+
+if ( typeof define === "function" && define.amd ) {
+ define( "jquery", [], function() {
+ return jQuery;
+ });
+}
+
+
+
+
+var
+ // Map over jQuery in case of overwrite
+ _jQuery = window.jQuery,
+
+ // Map over the $ in case of overwrite
+ _$ = window.$;
+
+jQuery.noConflict = function( deep ) {
+ if ( window.$ === jQuery ) {
+ window.$ = _$;
+ }
+
+ if ( deep && window.jQuery === jQuery ) {
+ window.jQuery = _jQuery;
+ }
+
+ return jQuery;
+};
+
+// Expose jQuery and $ identifiers, even in
+// AMD (#7102#comment:10, https://github.com/jquery/jquery/pull/557)
+// and CommonJS for browser emulators (#13566)
+if ( typeof noGlobal === strundefined ) {
+ window.jQuery = window.$ = jQuery;
+}
+
+
+
+
+return jQuery;
+
+}));
+(function($, undefined) {
+
+/**
+ * Unobtrusive scripting adapter for jQuery
+ * https://github.com/rails/jquery-ujs
+ *
+ * Requires jQuery 1.8.0 or later.
+ *
+ * Released under the MIT license
+ *
+ */
+
+ // Cut down on the number of issues from people inadvertently including jquery_ujs twice
+ // by detecting and raising an error when it happens.
+ if ( $.rails !== undefined ) {
+ $.error('jquery-ujs has already been loaded!');
+ }
+
+ // Shorthand to make it a little easier to call public rails functions from within rails.js
+ var rails;
+ var $document = $(document);
+
+ $.rails = rails = {
+ // Link elements bound by jquery-ujs
+ linkClickSelector: 'a[data-confirm], a[data-method], a[data-remote], a[data-disable-with], a[data-disable]',
+
+ // Button elements bound by jquery-ujs
+ buttonClickSelector: 'button[data-remote]:not(form button), button[data-confirm]:not(form button)',
+
+ // Select elements bound by jquery-ujs
+ inputChangeSelector: 'select[data-remote], input[data-remote], textarea[data-remote]',
+
+ // Form elements bound by jquery-ujs
+ formSubmitSelector: 'form',
+
+ // Form input elements bound by jquery-ujs
+ formInputClickSelector: 'form input[type=submit], form input[type=image], form button[type=submit], form button:not([type]), input[type=submit][form], input[type=image][form], button[type=submit][form], button[form]:not([type])',
+
+ // Form input elements disabled during form submission
+ disableSelector: 'input[data-disable-with]:enabled, button[data-disable-with]:enabled, textarea[data-disable-with]:enabled, input[data-disable]:enabled, button[data-disable]:enabled, textarea[data-disable]:enabled',
+
+ // Form input elements re-enabled after form submission
+ enableSelector: 'input[data-disable-with]:disabled, button[data-disable-with]:disabled, textarea[data-disable-with]:disabled, input[data-disable]:disabled, button[data-disable]:disabled, textarea[data-disable]:disabled',
+
+ // Form required input elements
+ requiredInputSelector: 'input[name][required]:not([disabled]),textarea[name][required]:not([disabled])',
+
+ // Form file input elements
+ fileInputSelector: 'input[type=file]',
+
+ // Link onClick disable selector with possible reenable after remote submission
+ linkDisableSelector: 'a[data-disable-with], a[data-disable]',
+
+ // Button onClick disable selector with possible reenable after remote submission
+ buttonDisableSelector: 'button[data-remote][data-disable-with], button[data-remote][data-disable]',
+
+ // Make sure that every Ajax request sends the CSRF token
+ CSRFProtection: function(xhr) {
+ var token = $('meta[name="csrf-token"]').attr('content');
+ if (token) xhr.setRequestHeader('X-CSRF-Token', token);
+ },
+
+ // making sure that all forms have actual up-to-date token(cached forms contain old one)
+ refreshCSRFTokens: function(){
+ var csrfToken = $('meta[name=csrf-token]').attr('content');
+ var csrfParam = $('meta[name=csrf-param]').attr('content');
+ $('form input[name="' + csrfParam + '"]').val(csrfToken);
+ },
+
+ // Triggers an event on an element and returns false if the event result is false
+ fire: function(obj, name, data) {
+ var event = $.Event(name);
+ obj.trigger(event, data);
+ return event.result !== false;
+ },
+
+ // Default confirm dialog, may be overridden with custom confirm dialog in $.rails.confirm
+ confirm: function(message) {
+ return confirm(message);
+ },
+
+ // Default ajax function, may be overridden with custom function in $.rails.ajax
+ ajax: function(options) {
+ return $.ajax(options);
+ },
+
+ // Default way to get an element's href. May be overridden at $.rails.href.
+ href: function(element) {
+ return element.attr('href');
+ },
+
+ // Submits "remote" forms and links with ajax
+ handleRemote: function(element) {
+ var method, url, data, elCrossDomain, crossDomain, withCredentials, dataType, options;
+
+ if (rails.fire(element, 'ajax:before')) {
+ elCrossDomain = element.data('cross-domain');
+ crossDomain = elCrossDomain === undefined ? null : elCrossDomain;
+ withCredentials = element.data('with-credentials') || null;
+ dataType = element.data('type') || ($.ajaxSettings && $.ajaxSettings.dataType);
+
+ if (element.is('form')) {
+ method = element.attr('method');
+ url = element.attr('action');
+ data = element.serializeArray();
+ // memoized value from clicked submit button
+ var button = element.data('ujs:submit-button');
+ if (button) {
+ data.push(button);
+ element.data('ujs:submit-button', null);
+ }
+ } else if (element.is(rails.inputChangeSelector)) {
+ method = element.data('method');
+ url = element.data('url');
+ data = element.serialize();
+ if (element.data('params')) data = data + "&" + element.data('params');
+ } else if (element.is(rails.buttonClickSelector)) {
+ method = element.data('method') || 'get';
+ url = element.data('url');
+ data = element.serialize();
+ if (element.data('params')) data = data + "&" + element.data('params');
+ } else {
+ method = element.data('method');
+ url = rails.href(element);
+ data = element.data('params') || null;
+ }
+
+ options = {
+ type: method || 'GET', data: data, dataType: dataType,
+ // stopping the "ajax:beforeSend" event will cancel the ajax request
+ beforeSend: function(xhr, settings) {
+ if (settings.dataType === undefined) {
+ xhr.setRequestHeader('accept', '*/*;q=0.5, ' + settings.accepts.script);
+ }
+ if (rails.fire(element, 'ajax:beforeSend', [xhr, settings])) {
+ element.trigger('ajax:send', xhr);
+ } else {
+ return false;
+ }
+ },
+ success: function(data, status, xhr) {
+ element.trigger('ajax:success', [data, status, xhr]);
+ },
+ complete: function(xhr, status) {
+ element.trigger('ajax:complete', [xhr, status]);
+ },
+ error: function(xhr, status, error) {
+ element.trigger('ajax:error', [xhr, status, error]);
+ },
+ crossDomain: crossDomain
+ };
+
+ // There is no withCredentials for IE6-8 when
+ // "Enable native XMLHTTP support" is disabled
+ if (withCredentials) {
+ options.xhrFields = {
+ withCredentials: withCredentials
+ };
+ }
+
+ // Only pass url to `ajax` options if not blank
+ if (url) { options.url = url; }
+
+ return rails.ajax(options);
+ } else {
+ return false;
+ }
+ },
+
+ // Handles "data-method" on links such as:
+ //
Delete
+ handleMethod: function(link) {
+ var href = rails.href(link),
+ method = link.data('method'),
+ target = link.attr('target'),
+ csrfToken = $('meta[name=csrf-token]').attr('content'),
+ csrfParam = $('meta[name=csrf-param]').attr('content'),
+ form = $('
'),
+ metadataInput = '
';
+
+ if (csrfParam !== undefined && csrfToken !== undefined) {
+ metadataInput += '
';
+ }
+
+ if (target) { form.attr('target', target); }
+
+ form.hide().append(metadataInput).appendTo('body');
+ form.submit();
+ },
+
+ // Helper function that returns form elements that match the specified CSS selector
+ // If form is actually a "form" element this will return associated elements outside the from that have
+ // the html form attribute set
+ formElements: function(form, selector) {
+ return form.is('form') ? $(form[0].elements).filter(selector) : form.find(selector);
+ },
+
+ /* Disables form elements:
+ - Caches element value in 'ujs:enable-with' data store
+ - Replaces element text with value of 'data-disable-with' attribute
+ - Sets disabled property to true
+ */
+ disableFormElements: function(form) {
+ rails.formElements(form, rails.disableSelector).each(function() {
+ rails.disableFormElement($(this));
+ });
+ },
+
+ disableFormElement: function(element) {
+ var method, replacement;
+
+ method = element.is('button') ? 'html' : 'val';
+ replacement = element.data('disable-with');
+
+ element.data('ujs:enable-with', element[method]());
+ if (replacement !== undefined) {
+ element[method](replacement);
+ }
+
+ element.prop('disabled', true);
+ },
+
+ /* Re-enables disabled form elements:
+ - Replaces element text with cached value from 'ujs:enable-with' data store (created in `disableFormElements`)
+ - Sets disabled property to false
+ */
+ enableFormElements: function(form) {
+ rails.formElements(form, rails.enableSelector).each(function() {
+ rails.enableFormElement($(this));
+ });
+ },
+
+ enableFormElement: function(element) {
+ var method = element.is('button') ? 'html' : 'val';
+ if (element.data('ujs:enable-with')) element[method](element.data('ujs:enable-with'));
+ element.prop('disabled', false);
+ },
+
+ /* For 'data-confirm' attribute:
+ - Fires `confirm` event
+ - Shows the confirmation dialog
+ - Fires the `confirm:complete` event
+
+ Returns `true` if no function stops the chain and user chose yes; `false` otherwise.
+ Attaching a handler to the element's `confirm` event that returns a `falsy` value cancels the confirmation dialog.
+ Attaching a handler to the element's `confirm:complete` event that returns a `falsy` value makes this function
+ return false. The `confirm:complete` event is fired whether or not the user answered true or false to the dialog.
+ */
+ allowAction: function(element) {
+ var message = element.data('confirm'),
+ answer = false, callback;
+ if (!message) { return true; }
+
+ if (rails.fire(element, 'confirm')) {
+ answer = rails.confirm(message);
+ callback = rails.fire(element, 'confirm:complete', [answer]);
+ }
+ return answer && callback;
+ },
+
+ // Helper function which checks for blank inputs in a form that match the specified CSS selector
+ blankInputs: function(form, specifiedSelector, nonBlank) {
+ var inputs = $(), input, valueToCheck,
+ selector = specifiedSelector || 'input,textarea',
+ allInputs = form.find(selector);
+
+ allInputs.each(function() {
+ input = $(this);
+ valueToCheck = input.is('input[type=checkbox],input[type=radio]') ? input.is(':checked') : input.val();
+ // If nonBlank and valueToCheck are both truthy, or nonBlank and valueToCheck are both falsey
+ if (!valueToCheck === !nonBlank) {
+
+ // Don't count unchecked required radio if other radio with same name is checked
+ if (input.is('input[type=radio]') && allInputs.filter('input[type=radio]:checked[name="' + input.attr('name') + '"]').length) {
+ return true; // Skip to next input
+ }
+
+ inputs = inputs.add(input);
+ }
+ });
+ return inputs.length ? inputs : false;
+ },
+
+ // Helper function which checks for non-blank inputs in a form that match the specified CSS selector
+ nonBlankInputs: function(form, specifiedSelector) {
+ return rails.blankInputs(form, specifiedSelector, true); // true specifies nonBlank
+ },
+
+ // Helper function, needed to provide consistent behavior in IE
+ stopEverything: function(e) {
+ $(e.target).trigger('ujs:everythingStopped');
+ e.stopImmediatePropagation();
+ return false;
+ },
+
+ // replace element's html with the 'data-disable-with' after storing original html
+ // and prevent clicking on it
+ disableElement: function(element) {
+ var replacement = element.data('disable-with');
+
+ element.data('ujs:enable-with', element.html()); // store enabled state
+ if (replacement !== undefined) {
+ element.html(replacement);
+ }
+
+ element.bind('click.railsDisable', function(e) { // prevent further clicking
+ return rails.stopEverything(e);
+ });
+ },
+
+ // restore element to its original state which was disabled by 'disableElement' above
+ enableElement: function(element) {
+ if (element.data('ujs:enable-with') !== undefined) {
+ element.html(element.data('ujs:enable-with')); // set to old enabled state
+ element.removeData('ujs:enable-with'); // clean up cache
+ }
+ element.unbind('click.railsDisable'); // enable element
+ }
+ };
+
+ if (rails.fire($document, 'rails:attachBindings')) {
+
+ $.ajaxPrefilter(function(options, originalOptions, xhr){ if ( !options.crossDomain ) { rails.CSRFProtection(xhr); }});
+
+ // This event works the same as the load event, except that it fires every
+ // time the page is loaded.
+ //
+ // See https://github.com/rails/jquery-ujs/issues/357
+ // See https://developer.mozilla.org/en-US/docs/Using_Firefox_1.5_caching
+ $(window).on("pageshow.rails", function () {
+ $($.rails.enableSelector).each(function () {
+ var element = $(this);
+
+ if (element.data("ujs:enable-with")) {
+ $.rails.enableFormElement(element);
+ }
+ });
+
+ $($.rails.linkDisableSelector).each(function () {
+ var element = $(this);
+
+ if (element.data("ujs:enable-with")) {
+ $.rails.enableElement(element);
+ }
+ });
+ });
+
+ $document.delegate(rails.linkDisableSelector, 'ajax:complete', function() {
+ rails.enableElement($(this));
+ });
+
+ $document.delegate(rails.buttonDisableSelector, 'ajax:complete', function() {
+ rails.enableFormElement($(this));
+ });
+
+ $document.delegate(rails.linkClickSelector, 'click.rails', function(e) {
+ var link = $(this), method = link.data('method'), data = link.data('params'), metaClick = e.metaKey || e.ctrlKey;
+ if (!rails.allowAction(link)) return rails.stopEverything(e);
+
+ if (!metaClick && link.is(rails.linkDisableSelector)) rails.disableElement(link);
+
+ if (link.data('remote') !== undefined) {
+ if (metaClick && (!method || method === 'GET') && !data) { return true; }
+
+ var handleRemote = rails.handleRemote(link);
+ // response from rails.handleRemote() will either be false or a deferred object promise.
+ if (handleRemote === false) {
+ rails.enableElement(link);
+ } else {
+ handleRemote.fail( function() { rails.enableElement(link); } );
+ }
+ return false;
+
+ } else if (method) {
+ rails.handleMethod(link);
+ return false;
+ }
+ });
+
+ $document.delegate(rails.buttonClickSelector, 'click.rails', function(e) {
+ var button = $(this);
+
+ if (!rails.allowAction(button)) return rails.stopEverything(e);
+
+ if (button.is(rails.buttonDisableSelector)) rails.disableFormElement(button);
+
+ var handleRemote = rails.handleRemote(button);
+ // response from rails.handleRemote() will either be false or a deferred object promise.
+ if (handleRemote === false) {
+ rails.enableFormElement(button);
+ } else {
+ handleRemote.fail( function() { rails.enableFormElement(button); } );
+ }
+ return false;
+ });
+
+ $document.delegate(rails.inputChangeSelector, 'change.rails', function(e) {
+ var link = $(this);
+ if (!rails.allowAction(link)) return rails.stopEverything(e);
+
+ rails.handleRemote(link);
+ return false;
+ });
+
+ $document.delegate(rails.formSubmitSelector, 'submit.rails', function(e) {
+ var form = $(this),
+ remote = form.data('remote') !== undefined,
+ blankRequiredInputs,
+ nonBlankFileInputs;
+
+ if (!rails.allowAction(form)) return rails.stopEverything(e);
+
+ // skip other logic when required values are missing or file upload is present
+ if (form.attr('novalidate') == undefined) {
+ blankRequiredInputs = rails.blankInputs(form, rails.requiredInputSelector);
+ if (blankRequiredInputs && rails.fire(form, 'ajax:aborted:required', [blankRequiredInputs])) {
+ return rails.stopEverything(e);
+ }
+ }
+
+ if (remote) {
+ nonBlankFileInputs = rails.nonBlankInputs(form, rails.fileInputSelector);
+ if (nonBlankFileInputs) {
+ // slight timeout so that the submit button gets properly serialized
+ // (make it easy for event handler to serialize form without disabled values)
+ setTimeout(function(){ rails.disableFormElements(form); }, 13);
+ var aborted = rails.fire(form, 'ajax:aborted:file', [nonBlankFileInputs]);
+
+ // re-enable form elements if event bindings return false (canceling normal form submission)
+ if (!aborted) { setTimeout(function(){ rails.enableFormElements(form); }, 13); }
+
+ return aborted;
+ }
+
+ rails.handleRemote(form);
+ return false;
+
+ } else {
+ // slight timeout so that the submit button gets properly serialized
+ setTimeout(function(){ rails.disableFormElements(form); }, 13);
+ }
+ });
+
+ $document.delegate(rails.formInputClickSelector, 'click.rails', function(event) {
+ var button = $(this);
+
+ if (!rails.allowAction(button)) return rails.stopEverything(event);
+
+ // register the pressed submit button
+ var name = button.attr('name'),
+ data = name ? {name:name, value:button.val()} : null;
+
+ button.closest('form').data('ujs:submit-button', data);
+ });
+
+ $document.delegate(rails.formSubmitSelector, 'ajax:send.rails', function(event) {
+ if (this == event.target) rails.disableFormElements($(this));
+ });
+
+ $document.delegate(rails.formSubmitSelector, 'ajax:complete.rails', function(event) {
+ if (this == event.target) rails.enableFormElements($(this));
+ });
+
+ $(function(){
+ rails.refreshCSRFTokens();
+ });
+ }
+
+})( jQuery );
+/*!
+ * jQuery Validation Plugin v1.13.1
+ *
+ * http://jqueryvalidation.org/
+ *
+ * Copyright (c) 2014 Jörn Zaefferer
+ * Released under the MIT license
+ */
+
+(function( factory ) {
+ if ( typeof define === "function" && define.amd ) {
+ define( ["jquery"], factory );
+ } else {
+ factory( jQuery );
+ }
+}(function( $ ) {
+
+$.extend($.fn, {
+ // http://jqueryvalidation.org/validate/
+ validate: function( options ) {
+
+ // if nothing is selected, return nothing; can't chain anyway
+ if ( !this.length ) {
+ if ( 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 ) {
+
+ this.validateDelegate( ":submit", "click", function( event ) {
+ if ( validator.settings.submitHandler ) {
+ validator.submitButton = event.target;
+ }
+ // allow suppressing validation by adding a cancel class to the submit button
+ if ( $( event.target ).hasClass( "cancel" ) ) {
+ validator.cancelSubmit = true;
+ }
+
+ // allow suppressing validation by adding the html5 formnovalidate attribute to the submit button
+ if ( $( event.target ).attr( "formnovalidate" ) !== undefined ) {
+ validator.cancelSubmit = true;
+ }
+ });
+
+ // 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() {
+ var hidden, result;
+ if ( validator.settings.submitHandler ) {
+ if ( validator.submitButton ) {
+ // insert a hidden input as a replacement for the missing submit button
+ hidden = $( "
" )
+ .attr( "name", validator.submitButton.name )
+ .val( $( validator.submitButton ).val() )
+ .appendTo( validator.currentForm );
+ }
+ result = validator.settings.submitHandler.call( validator, validator.currentForm, event );
+ if ( validator.submitButton ) {
+ // and clean up afterwards; thanks to no-block-scope, hidden can be referenced
+ hidden.remove();
+ }
+ if ( result !== undefined ) {
+ return result;
+ }
+ 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://jqueryvalidation.org/valid/
+ valid: function() {
+ var valid, validator;
+
+ if ( $( this[ 0 ] ).is( "form" ) ) {
+ valid = this.validate().form();
+ } else {
+ valid = true;
+ validator = $( this[ 0 ].form ).validate();
+ this.each( function() {
+ valid = validator.element( this ) && valid;
+ });
+ }
+ return valid;
+ },
+ // attributes: space separated 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://jqueryvalidation.org/rules/
+ rules: function( command, argument ) {
+ var element = this[ 0 ],
+ settings, staticRules, existingRules, data, param, filtered;
+
+ if ( command ) {
+ settings = $.data( element.form, "validator" ).settings;
+ staticRules = settings.rules;
+ existingRules = $.validator.staticRules( element );
+ switch ( command ) {
+ case "add":
+ $.extend( existingRules, $.validator.normalizeRule( argument ) );
+ // remove messages from rules, but allow them to be set separately
+ delete existingRules.messages;
+ 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;
+ }
+ filtered = {};
+ $.each( argument.split( /\s/ ), function( index, method ) {
+ filtered[ method ] = existingRules[ method ];
+ delete existingRules[ method ];
+ if ( method === "required" ) {
+ $( element ).removeAttr( "aria-required" );
+ }
+ });
+ return filtered;
+ }
+ }
+
+ data = $.validator.normalizeRules(
+ $.extend(
+ {},
+ $.validator.classRules( element ),
+ $.validator.attributeRules( element ),
+ $.validator.dataRules( element ),
+ $.validator.staticRules( element )
+ ), element );
+
+ // make sure required is at front
+ if ( data.required ) {
+ param = data.required;
+ delete data.required;
+ data = $.extend( { required: param }, data );
+ $( element ).attr( "aria-required", "true" );
+ }
+
+ // make sure remote is at back
+ if ( data.remote ) {
+ param = data.remote;
+ delete data.remote;
+ data = $.extend( data, { remote: param });
+ }
+
+ return data;
+ }
+});
+
+// Custom selectors
+$.extend( $.expr[ ":" ], {
+ // http://jqueryvalidation.org/blank-selector/
+ blank: function( a ) {
+ return !$.trim( "" + $( a ).val() );
+ },
+ // http://jqueryvalidation.org/filled-selector/
+ filled: function( a ) {
+ return !!$.trim( "" + $( a ).val() );
+ },
+ // http://jqueryvalidation.org/unchecked-selector/
+ unchecked: function( a ) {
+ return !$( a ).prop( "checked" );
+ }
+});
+
+// constructor for validator
+$.validator = function( options, form ) {
+ this.settings = $.extend( true, {}, $.validator.defaults, options );
+ this.currentForm = form;
+ this.init();
+};
+
+// http://jqueryvalidation.org/jQuery.validator.format/
+$.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" ), function() {
+ return n;
+ });
+ });
+ return source;
+};
+
+$.extend( $.validator, {
+
+ defaults: {
+ messages: {},
+ groups: {},
+ rules: {},
+ errorClass: "error",
+ validClass: "valid",
+ errorElement: "label",
+ focusCleanup: false,
+ focusInvalid: true,
+ errorContainer: $( [] ),
+ errorLabelContainer: $( [] ),
+ onsubmit: true,
+ ignore: ":hidden",
+ ignoreTitle: false,
+ onfocusin: function( element ) {
+ this.lastActive = element;
+
+ // Hide error label and remove error class on focus if enabled
+ if ( this.settings.focusCleanup ) {
+ if ( this.settings.unhighlight ) {
+ this.settings.unhighlight.call( this, element, this.settings.errorClass, this.settings.validClass );
+ }
+ this.hideThese( this.errorsFor( element ) );
+ }
+ },
+ onfocusout: function( element ) {
+ if ( !this.checkable( element ) && ( element.name in this.submitted || !this.optional( element ) ) ) {
+ this.element( element );
+ }
+ },
+ onkeyup: function( element, event ) {
+ if ( event.which === 9 && this.elementValue( element ) === "" ) {
+ return;
+ } else 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://jqueryvalidation.org/jQuery.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.",
+ 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 = {} ),
+ rules;
+ $.each( this.settings.groups, function( key, value ) {
+ if ( typeof value === "string" ) {
+ value = value.split( /\s/ );
+ }
+ $.each( value, function( index, name ) {
+ groups[ name ] = key;
+ });
+ });
+ 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/, "" ),
+ settings = validator.settings;
+ if ( settings[ eventType ] && !this.is( settings.ignore ) ) {
+ settings[ eventType ].call( validator, this[ 0 ], event );
+ }
+ }
+ $( this.currentForm )
+ .validateDelegate( ":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'], [type='radio'], [type='checkbox']",
+ "focusin focusout keyup", delegate)
+ // Support: Chrome, oldIE
+ // "select" is provided as event.target when clicking a option
+ .validateDelegate("select, option, [type='radio'], [type='checkbox']", "click", delegate);
+
+ if ( this.settings.invalidHandler ) {
+ $( this.currentForm ).bind( "invalid-form.validate", this.settings.invalidHandler );
+ }
+
+ // Add aria-required to any Static/Data/Class required fields before first validation
+ // Screen readers require this attribute to be present before the initial submission http://www.w3.org/TR/WCAG-TECHS/ARIA2.html
+ $( this.currentForm ).find( "[required], [data-rule-required], .required" ).attr( "aria-required", "true" );
+ },
+
+ // http://jqueryvalidation.org/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://jqueryvalidation.org/Validator.element/
+ element: function( element ) {
+ var cleanElement = this.clean( element ),
+ checkElement = this.validationTargetFor( cleanElement ),
+ result = true;
+
+ this.lastElement = checkElement;
+
+ if ( checkElement === undefined ) {
+ delete this.invalid[ cleanElement.name ];
+ } else {
+ this.prepareElement( checkElement );
+ this.currentElements = $( checkElement );
+
+ result = this.check( checkElement ) !== false;
+ if ( result ) {
+ delete this.invalid[ checkElement.name ];
+ } else {
+ this.invalid[ checkElement.name ] = true;
+ }
+ }
+ // Add aria-invalid status for screen readers
+ $( element ).attr( "aria-invalid", !result );
+
+ if ( !this.numberOfInvalids() ) {
+ // Hide error containers on last error
+ this.toHide = this.toHide.add( this.containers );
+ }
+ this.showErrors();
+ return result;
+ },
+
+ // http://jqueryvalidation.org/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 );
+ });
+ }
+ if ( this.settings.showErrors ) {
+ this.settings.showErrors.call( this, this.errorMap, this.errorList );
+ } else {
+ this.defaultShowErrors();
+ }
+ },
+
+ // http://jqueryvalidation.org/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 )
+ .removeData( "previousValue" )
+ .removeAttr( "aria-invalid" );
+ },
+
+ numberOfInvalids: function() {
+ return this.objectLength( this.invalid );
+ },
+
+ objectLength: function( obj ) {
+ /* jshint unused: false */
+ var count = 0,
+ i;
+ for ( i in obj ) {
+ count++;
+ }
+ return count;
+ },
+
+ hideErrors: function() {
+ this.hideThese( this.toHide );
+ },
+
+ hideThese: function( errors ) {
+ errors.not( this.containers ).text( "" );
+ this.addWrapper( errors ).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], [readonly]" )
+ .not( this.settings.ignore )
+ .filter( function() {
+ if ( !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() {
+ var errorClass = this.settings.errorClass.split( " " ).join( "." );
+ return $( this.settings.errorElement + "." + 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 );
+ },
+
+ elementValue: function( element ) {
+ var val,
+ $element = $( element ),
+ type = element.type;
+
+ if ( type === "radio" || type === "checkbox" ) {
+ return $( "input[name='" + element.name + "']:checked" ).val();
+ } else if ( type === "number" && typeof element.validity !== "undefined" ) {
+ return element.validity.badInput ? false : $element.val();
+ }
+
+ val = $element.val();
+ if ( typeof val === "string" ) {
+ return val.replace(/\r/g, "" );
+ }
+ return val;
+ },
+
+ check: function( element ) {
+ element = this.validationTargetFor( this.clean( element ) );
+
+ var rules = $( element ).rules(),
+ rulesCount = $.map( rules, function( n, i ) {
+ return i;
+ }).length,
+ dependencyMismatch = false,
+ val = this.elementValue( element ),
+ result, method, rule;
+
+ for ( method in rules ) {
+ rule = { method: method, parameters: rules[ method ] };
+ try {
+
+ result = $.validator.methods[ method ].call( this, val, 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" && rulesCount === 1 ) {
+ 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 ) {
+ if ( this.settings.debug && window.console ) {
+ console.log( "Exception occurred 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 HTML5 data attribute
+ // return the generic message if present and no method specific message is present
+ customDataMessage: function( element, method ) {
+ return $( element ).data( "msg" + method.charAt( 0 ).toUpperCase() +
+ method.substring( 1 ).toLowerCase() ) || $( element ).data( "msg" );
+ },
+
+ // 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.customDataMessage( element, method ),
+ // title is never undefined, so handle empty string as undefined
+ !this.settings.ignoreTitle && element.title || undefined,
+ $.validator.messages[ method ],
+ "
Warning: No message defined for " + element.name + ""
+ );
+ },
+
+ 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 = $.validator.format( message.replace( theregex, "{$1}" ), rule.parameters );
+ }
+ this.errorList.push({
+ message: message,
+ element: element,
+ method: rule.method
+ });
+
+ 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() {
+ var i, elements, error;
+ for ( i = 0; this.errorList[ i ]; i++ ) {
+ error = this.errorList[ i ];
+ if ( 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 ( i = 0; this.successList[ i ]; i++ ) {
+ this.showLabel( this.successList[ i ] );
+ }
+ }
+ if ( this.settings.unhighlight ) {
+ for ( 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 place, group, errorID,
+ error = this.errorsFor( element ),
+ elementID = this.idOrName( element ),
+ describedBy = $( element ).attr( "aria-describedby" );
+ if ( error.length ) {
+ // refresh error/success class
+ error.removeClass( this.settings.validClass ).addClass( this.settings.errorClass );
+ // replace message on existing label
+ error.html( message );
+ } else {
+ // create error element
+ error = $( "<" + this.settings.errorElement + ">" )
+ .attr( "id", elementID + "-error" )
+ .addClass( this.settings.errorClass )
+ .html( message || "" );
+
+ // Maintain reference to the element to be placed into the DOM
+ place = error;
+ if ( this.settings.wrapper ) {
+ // make sure the element is visible, even in IE
+ // actually showing the wrapped element is handled elsewhere
+ place = error.hide().show().wrap( "<" + this.settings.wrapper + "/>" ).parent();
+ }
+ if ( this.labelContainer.length ) {
+ this.labelContainer.append( place );
+ } else if ( this.settings.errorPlacement ) {
+ this.settings.errorPlacement( place, $( element ) );
+ } else {
+ place.insertAfter( element );
+ }
+
+ // Link error back to the element
+ if ( error.is( "label" ) ) {
+ // If the error is a label, then associate using 'for'
+ error.attr( "for", elementID );
+ } else if ( error.parents( "label[for='" + elementID + "']" ).length === 0 ) {
+ // If the element is not a child of an associated label, then it's necessary
+ // to explicitly apply aria-describedby
+
+ errorID = error.attr( "id" ).replace( /(:|\.|\[|\])/g, "\\$1");
+ // Respect existing non-error aria-describedby
+ if ( !describedBy ) {
+ describedBy = errorID;
+ } else if ( !describedBy.match( new RegExp( "\\b" + errorID + "\\b" ) ) ) {
+ // Add to end of list if not already present
+ describedBy += " " + errorID;
+ }
+ $( element ).attr( "aria-describedby", describedBy );
+
+ // If this element is grouped, then assign to all elements in the same group
+ group = this.groups[ element.name ];
+ if ( group ) {
+ $.each( this.groups, function( name, testgroup ) {
+ if ( testgroup === group ) {
+ $( "[name='" + name + "']", this.currentForm )
+ .attr( "aria-describedby", error.attr( "id" ) );
+ }
+ });
+ }
+ }
+ }
+ if ( !message && this.settings.success ) {
+ error.text( "" );
+ if ( typeof this.settings.success === "string" ) {
+ error.addClass( this.settings.success );
+ } else {
+ this.settings.success( error, element );
+ }
+ }
+ this.toShow = this.toShow.add( error );
+ },
+
+ errorsFor: function( element ) {
+ var name = this.idOrName( element ),
+ describer = $( element ).attr( "aria-describedby" ),
+ selector = "label[for='" + name + "'], label[for='" + name + "'] *";
+
+ // aria-describedby should directly reference the error element
+ if ( describer ) {
+ selector = selector + ", #" + describer.replace( /\s+/g, ", #" );
+ }
+ return this
+ .errors()
+ .filter( selector );
+ },
+
+ 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 );
+ }
+
+ // Always apply ignore filter
+ return $( element ).not( this.settings.ignore )[ 0 ];
+ },
+
+ checkable: function( element ) {
+ return ( /radio|checkbox/i ).test( element.type );
+ },
+
+ findByName: function( name ) {
+ return $( this.currentForm ).find( "[name='" + name + "']" );
+ },
+
+ 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 ) {
+ return param;
+ },
+ "string": function( param, element ) {
+ return !!$( param, element.form ).length;
+ },
+ "function": function( param, element ) {
+ return param( element );
+ }
+ },
+
+ optional: function( element ) {
+ var val = this.elementValue( element );
+ return !$.validator.methods.required.call( this, val, 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 },
+ number: { number: true },
+ digits: { digits: true },
+ creditcard: { creditcard: true }
+ },
+
+ addClassRules: function( className, rules ) {
+ if ( className.constructor === String ) {
+ this.classRuleSettings[ className ] = rules;
+ } else {
+ $.extend( this.classRuleSettings, className );
+ }
+ },
+
+ classRules: function( element ) {
+ var rules = {},
+ classes = $( element ).attr( "class" );
+
+ if ( classes ) {
+ $.each( classes.split( " " ), function() {
+ if ( this in $.validator.classRuleSettings ) {
+ $.extend( rules, $.validator.classRuleSettings[ this ]);
+ }
+ });
+ }
+ return rules;
+ },
+
+ attributeRules: function( element ) {
+ var rules = {},
+ $element = $( element ),
+ type = element.getAttribute( "type" ),
+ method, value;
+
+ for ( method in $.validator.methods ) {
+
+ // support for
in both html5 and older browsers
+ if ( method === "required" ) {
+ value = element.getAttribute( method );
+ // Some browsers return an empty string for the required attribute
+ // and non-HTML5 browsers might have required="" markup
+ if ( value === "" ) {
+ value = true;
+ }
+ // force non-HTML5 browsers to return bool
+ value = !!value;
+ } else {
+ value = $element.attr( method );
+ }
+
+ // convert the value to a number for number inputs, and for text for backwards compability
+ // allows type="date" and others to be compared as strings
+ if ( /min|max/.test( method ) && ( type === null || /number|range|text/.test( type ) ) ) {
+ value = Number( value );
+ }
+
+ if ( value || value === 0 ) {
+ rules[ method ] = value;
+ } else if ( type === method && type !== "range" ) {
+ // exception: the jquery validate 'range' method
+ // does not test for the html5 'range' type
+ 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;
+ },
+
+ dataRules: function( element ) {
+ var method, value,
+ rules = {}, $element = $( element );
+ for ( method in $.validator.methods ) {
+ value = $element.data( "rule" + method.charAt( 0 ).toUpperCase() + method.substring( 1 ).toLowerCase() );
+ if ( value !== undefined ) {
+ rules[ method ] = value;
+ }
+ }
+ return rules;
+ },
+
+ staticRules: function( element ) {
+ var rules = {},
+ 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" ], function() {
+ if ( rules[ this ] ) {
+ rules[ this ] = Number( rules[ this ] );
+ }
+ });
+ $.each([ "rangelength", "range" ], function() {
+ var parts;
+ if ( rules[ this ] ) {
+ if ( $.isArray( rules[ this ] ) ) {
+ rules[ this ] = [ Number( rules[ this ][ 0 ]), Number( rules[ this ][ 1 ] ) ];
+ } else if ( typeof rules[ this ] === "string" ) {
+ parts = rules[ this ].replace(/[\[\]]/g, "" ).split( /[\s,]+/ );
+ rules[ this ] = [ Number( parts[ 0 ]), Number( parts[ 1 ] ) ];
+ }
+ }
+ });
+
+ if ( $.validator.autoCreateRanges ) {
+ // auto-create ranges
+ if ( rules.min != null && rules.max != null ) {
+ rules.range = [ rules.min, rules.max ];
+ delete rules.min;
+ delete rules.max;
+ }
+ if ( rules.minlength != null && rules.maxlength != null ) {
+ rules.rangelength = [ rules.minlength, rules.maxlength ];
+ delete rules.minlength;
+ delete rules.maxlength;
+ }
+ }
+
+ 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://jqueryvalidation.org/jQuery.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://jqueryvalidation.org/required-method/
+ required: function( value, element, param ) {
+ // check if dependency is met
+ if ( !this.depend( param, element ) ) {
+ return "dependency-mismatch";
+ }
+ if ( element.nodeName.toLowerCase() === "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;
+ }
+ if ( this.checkable( element ) ) {
+ return this.getLength( value, element ) > 0;
+ }
+ return $.trim( value ).length > 0;
+ },
+
+ // http://jqueryvalidation.org/email-method/
+ email: function( value, element ) {
+ // From http://www.whatwg.org/specs/web-apps/current-work/multipage/states-of-the-type-attribute.html#e-mail-state-%28type=email%29
+ // Retrieved 2014-01-14
+ // If you have a problem with this implementation, report a bug against the above spec
+ // Or use custom methods to implement your own email validation
+ return this.optional( element ) || /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[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])?)*$/.test( value );
+ },
+
+ // http://jqueryvalidation.org/url-method/
+ url: function( value, element ) {
+ // contributed by Scott Gonzalez: http://projects.scottsplayground.com/iri/
+ return this.optional( element ) || /^(https?|s?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://jqueryvalidation.org/date-method/
+ date: function( value, element ) {
+ return this.optional( element ) || !/Invalid|NaN/.test( new Date( value ).toString() );
+ },
+
+ // http://jqueryvalidation.org/dateISO-method/
+ dateISO: function( value, element ) {
+ return this.optional( element ) || /^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])$/.test( value );
+ },
+
+ // http://jqueryvalidation.org/number-method/
+ number: function( value, element ) {
+ return this.optional( element ) || /^-?(?:\d+|\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/.test( value );
+ },
+
+ // http://jqueryvalidation.org/digits-method/
+ digits: function( value, element ) {
+ return this.optional( element ) || /^\d+$/.test( value );
+ },
+
+ // http://jqueryvalidation.org/creditcard-method/
+ // 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,
+ n, cDigit;
+
+ value = value.replace( /\D/g, "" );
+
+ // Basing min and max length on
+ // http://developer.ean.com/general_info/Valid_Credit_Card_Types
+ if ( value.length < 13 || value.length > 19 ) {
+ return false;
+ }
+
+ for ( n = value.length - 1; n >= 0; n--) {
+ cDigit = value.charAt( n );
+ nDigit = parseInt( cDigit, 10 );
+ if ( bEven ) {
+ if ( ( nDigit *= 2 ) > 9 ) {
+ nDigit -= 9;
+ }
+ }
+ nCheck += nDigit;
+ bEven = !bEven;
+ }
+
+ return ( nCheck % 10 ) === 0;
+ },
+
+ // http://jqueryvalidation.org/minlength-method/
+ minlength: function( value, element, param ) {
+ var length = $.isArray( value ) ? value.length : this.getLength( value, element );
+ return this.optional( element ) || length >= param;
+ },
+
+ // http://jqueryvalidation.org/maxlength-method/
+ maxlength: function( value, element, param ) {
+ var length = $.isArray( value ) ? value.length : this.getLength( value, element );
+ return this.optional( element ) || length <= param;
+ },
+
+ // http://jqueryvalidation.org/rangelength-method/
+ rangelength: function( value, element, param ) {
+ var length = $.isArray( value ) ? value.length : this.getLength( value, element );
+ return this.optional( element ) || ( length >= param[ 0 ] && length <= param[ 1 ] );
+ },
+
+ // http://jqueryvalidation.org/min-method/
+ min: function( value, element, param ) {
+ return this.optional( element ) || value >= param;
+ },
+
+ // http://jqueryvalidation.org/max-method/
+ max: function( value, element, param ) {
+ return this.optional( element ) || value <= param;
+ },
+
+ // http://jqueryvalidation.org/range-method/
+ range: function( value, element, param ) {
+ return this.optional( element ) || ( value >= param[ 0 ] && value <= param[ 1 ] );
+ },
+
+ // http://jqueryvalidation.org/equalTo-method/
+ 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 );
+ if ( this.settings.onfocusout ) {
+ target.unbind( ".validate-equalTo" ).bind( "blur.validate-equalTo", function() {
+ $( element ).valid();
+ });
+ }
+ return value === target.val();
+ },
+
+ // http://jqueryvalidation.org/remote-method/
+ remote: function( value, element, param ) {
+ if ( this.optional( element ) ) {
+ return "dependency-mismatch";
+ }
+
+ var previous = this.previousValue( element ),
+ validator, data;
+
+ 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 ( previous.old === value ) {
+ return previous.valid;
+ }
+
+ previous.old = value;
+ validator = this;
+ this.startRequest( element );
+ data = {};
+ data[ element.name ] = value;
+ $.ajax( $.extend( true, {
+ url: param,
+ mode: "abort",
+ port: "validate" + element.name,
+ dataType: "json",
+ data: data,
+ context: validator.currentForm,
+ success: function( response ) {
+ var valid = response === true || response === "true",
+ errors, message, submitted;
+
+ validator.settings.messages[ element.name ].remote = previous.originalMessage;
+ if ( valid ) {
+ submitted = validator.formSubmitted;
+ validator.prepareElement( element );
+ validator.formSubmitted = submitted;
+ validator.successList.push( element );
+ delete validator.invalid[ element.name ];
+ validator.showErrors();
+ } else {
+ errors = {};
+ message = response || validator.defaultMessage( element, "remote" );
+ errors[ element.name ] = previous.message = $.isFunction( message ) ? message( value ) : message;
+ validator.invalid[ element.name ] = true;
+ validator.showErrors( errors );
+ }
+ previous.valid = valid;
+ validator.stopRequest( element, valid );
+ }
+ }, param ) );
+ return "pending";
+ }
+
+ }
+
+});
+
+$.format = function deprecated() {
+ throw "$.format has been deprecated. Please use $.validator.format instead.";
+};
+
+// 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()
+
+var pendingRequests = {},
+ ajax;
+// 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
+ 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();
+ }
+ pendingRequests[port] = ajax.apply(this, arguments);
+ return pendingRequests[port];
+ }
+ return ajax.apply(this, arguments);
+ };
+}
+
+// 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
+
+$.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 Validation Plugin v1.13.1
+ *
+ * http://jqueryvalidation.org/
+ *
+ * Copyright (c) 2014 Jörn Zaefferer
+ * Released under the MIT license
+ */
+
+(function( factory ) {
+ if ( typeof define === "function" && define.amd ) {
+ define( ["jquery", "./jquery.validate"], factory );
+ } else {
+ factory( jQuery );
+ }
+}(function( $ ) {
+
+(function() {
+
+ function stripHtml(value) {
+ // remove html tags and space chars
+ return value.replace(/<.[^<>]*?>/g, " ").replace(/ | /gi, " ")
+ // remove punctuation
+ .replace(/[.(),;:!?%#$'\"_+=\/\-“”’]*/g, "");
+ }
+
+ $.validator.addMethod("maxWords", function(value, element, params) {
+ return this.optional(element) || stripHtml(value).match(/\b\w+\b/g).length <= params;
+ }, $.validator.format("Please enter {0} words or less."));
+
+ $.validator.addMethod("minWords", function(value, element, params) {
+ return this.optional(element) || stripHtml(value).match(/\b\w+\b/g).length >= params;
+ }, $.validator.format("Please enter at least {0} words."));
+
+ $.validator.addMethod("rangeWords", function(value, element, params) {
+ var valueStripped = stripHtml(value),
+ regex = /\b\w+\b/g;
+ return this.optional(element) || valueStripped.match(regex).length >= params[0] && valueStripped.match(regex).length <= params[1];
+ }, $.validator.format("Please enter between {0} and {1} words."));
+
+}());
+
+// Accept a value from a file input based on a required mimetype
+$.validator.addMethod("accept", function(value, element, param) {
+ // Split mime on commas in case we have multiple types we can accept
+ var typeParam = typeof param === "string" ? param.replace(/\s/g, "").replace(/,/g, "|") : "image/*",
+ optionalValue = this.optional(element),
+ i, file;
+
+ // Element is optional
+ if (optionalValue) {
+ return optionalValue;
+ }
+
+ if ($(element).attr("type") === "file") {
+ // If we are using a wildcard, make it regex friendly
+ typeParam = typeParam.replace(/\*/g, ".*");
+
+ // Check if the element has a FileList before checking each file
+ if (element.files && element.files.length) {
+ for (i = 0; i < element.files.length; i++) {
+ file = element.files[i];
+
+ // Grab the mimetype from the loaded file, verify it matches
+ if (!file.type.match(new RegExp( ".?(" + typeParam + ")$", "i"))) {
+ return false;
+ }
+ }
+ }
+ }
+
+ // Either return true because we've validated each file, or because the
+ // browser does not support element.files and the FileList feature
+ return true;
+}, $.validator.format("Please enter a value with a valid mimetype."));
+
+$.validator.addMethod("alphanumeric", function(value, element) {
+ return this.optional(element) || /^\w+$/i.test(value);
+}, "Letters, numbers, and underscores only please");
+
+/*
+ * Dutch bank account numbers (not 'giro' numbers) have 9 digits
+ * and pass the '11 check'.
+ * We accept the notation with spaces, as that is common.
+ * acceptable: 123456789 or 12 34 56 789
+ */
+$.validator.addMethod("bankaccountNL", function(value, element) {
+ if (this.optional(element)) {
+ return true;
+ }
+ if (!(/^[0-9]{9}|([0-9]{2} ){3}[0-9]{3}$/.test(value))) {
+ return false;
+ }
+ // now '11 check'
+ var account = value.replace(/ /g, ""), // remove spaces
+ sum = 0,
+ len = account.length,
+ pos, factor, digit;
+ for ( pos = 0; pos < len; pos++ ) {
+ factor = len - pos;
+ digit = account.substring(pos, pos + 1);
+ sum = sum + factor * digit;
+ }
+ return sum % 11 === 0;
+}, "Please specify a valid bank account number");
+
+$.validator.addMethod("bankorgiroaccountNL", function(value, element) {
+ return this.optional(element) ||
+ ($.validator.methods.bankaccountNL.call(this, value, element)) ||
+ ($.validator.methods.giroaccountNL.call(this, value, element));
+}, "Please specify a valid bank or giro account number");
+
+/**
+ * BIC is the business identifier code (ISO 9362). This BIC check is not a guarantee for authenticity.
+ *
+ * BIC pattern: BBBBCCLLbbb (8 or 11 characters long; bbb is optional)
+ *
+ * BIC definition in detail:
+ * - First 4 characters - bank code (only letters)
+ * - Next 2 characters - ISO 3166-1 alpha-2 country code (only letters)
+ * - Next 2 characters - location code (letters and digits)
+ * a. shall not start with '0' or '1'
+ * b. second character must be a letter ('O' is not allowed) or one of the following digits ('0' for test (therefore not allowed), '1' for passive participant and '2' for active participant)
+ * - Last 3 characters - branch code, optional (shall not start with 'X' except in case of 'XXX' for primary office) (letters and digits)
+ */
+$.validator.addMethod("bic", function(value, element) {
+ return this.optional( element ) || /^([A-Z]{6}[A-Z2-9][A-NP-Z1-2])(X{3}|[A-WY-Z0-9][A-Z0-9]{2})?$/.test( value );
+}, "Please specify a valid BIC code");
+
+/*
+ * Código de identificación fiscal ( CIF ) is the tax identification code for Spanish legal entities
+ * Further rules can be found in Spanish on http://es.wikipedia.org/wiki/C%C3%B3digo_de_identificaci%C3%B3n_fiscal
+ */
+$.validator.addMethod( "cifES", function( value ) {
+ "use strict";
+
+ var num = [],
+ controlDigit, sum, i, count, tmp, secondDigit;
+
+ value = value.toUpperCase();
+
+ // Quick format test
+ if ( !value.match( "((^[A-Z]{1}[0-9]{7}[A-Z0-9]{1}$|^[T]{1}[A-Z0-9]{8}$)|^[0-9]{8}[A-Z]{1}$)" ) ) {
+ return false;
+ }
+
+ for ( i = 0; i < 9; i++ ) {
+ num[ i ] = parseInt( value.charAt( i ), 10 );
+ }
+
+ // Algorithm for checking CIF codes
+ sum = num[ 2 ] + num[ 4 ] + num[ 6 ];
+ for ( count = 1; count < 8; count += 2 ) {
+ tmp = ( 2 * num[ count ] ).toString();
+ secondDigit = tmp.charAt( 1 );
+
+ sum += parseInt( tmp.charAt( 0 ), 10 ) + ( secondDigit === "" ? 0 : parseInt( secondDigit, 10 ) );
+ }
+
+ /* The first (position 1) is a letter following the following criteria:
+ * A. Corporations
+ * B. LLCs
+ * C. General partnerships
+ * D. Companies limited partnerships
+ * E. Communities of goods
+ * F. Cooperative Societies
+ * G. Associations
+ * H. Communities of homeowners in horizontal property regime
+ * J. Civil Societies
+ * K. Old format
+ * L. Old format
+ * M. Old format
+ * N. Nonresident entities
+ * P. Local authorities
+ * Q. Autonomous bodies, state or not, and the like, and congregations and religious institutions
+ * R. Congregations and religious institutions (since 2008 ORDER EHA/451/2008)
+ * S. Organs of State Administration and regions
+ * V. Agrarian Transformation
+ * W. Permanent establishments of non-resident in Spain
+ */
+ if ( /^[ABCDEFGHJNPQRSUVW]{1}/.test( value ) ) {
+ sum += "";
+ controlDigit = 10 - parseInt( sum.charAt( sum.length - 1 ), 10 );
+ value += controlDigit;
+ return ( num[ 8 ].toString() === String.fromCharCode( 64 + controlDigit ) || num[ 8 ].toString() === value.charAt( value.length - 1 ) );
+ }
+
+ return false;
+
+}, "Please specify a valid CIF number." );
+
+/* NOTICE: Modified version of Castle.Components.Validator.CreditCardValidator
+ * Redistributed under the the Apache License 2.0 at http://www.apache.org/licenses/LICENSE-2.0
+ * Valid Types: mastercard, visa, amex, dinersclub, enroute, discover, jcb, unknown, all (overrides all other settings)
+ */
+$.validator.addMethod("creditcardtypes", function(value, element, param) {
+ if (/[^0-9\-]+/.test(value)) {
+ return false;
+ }
+
+ value = value.replace(/\D/g, "");
+
+ var validTypes = 0x0000;
+
+ if (param.mastercard) {
+ validTypes |= 0x0001;
+ }
+ if (param.visa) {
+ validTypes |= 0x0002;
+ }
+ if (param.amex) {
+ validTypes |= 0x0004;
+ }
+ if (param.dinersclub) {
+ validTypes |= 0x0008;
+ }
+ if (param.enroute) {
+ validTypes |= 0x0010;
+ }
+ if (param.discover) {
+ validTypes |= 0x0020;
+ }
+ if (param.jcb) {
+ validTypes |= 0x0040;
+ }
+ if (param.unknown) {
+ validTypes |= 0x0080;
+ }
+ if (param.all) {
+ validTypes = 0x0001 | 0x0002 | 0x0004 | 0x0008 | 0x0010 | 0x0020 | 0x0040 | 0x0080;
+ }
+ if (validTypes & 0x0001 && /^(5[12345])/.test(value)) { //mastercard
+ return value.length === 16;
+ }
+ if (validTypes & 0x0002 && /^(4)/.test(value)) { //visa
+ return value.length === 16;
+ }
+ if (validTypes & 0x0004 && /^(3[47])/.test(value)) { //amex
+ return value.length === 15;
+ }
+ if (validTypes & 0x0008 && /^(3(0[012345]|[68]))/.test(value)) { //dinersclub
+ return value.length === 14;
+ }
+ if (validTypes & 0x0010 && /^(2(014|149))/.test(value)) { //enroute
+ return value.length === 15;
+ }
+ if (validTypes & 0x0020 && /^(6011)/.test(value)) { //discover
+ return value.length === 16;
+ }
+ if (validTypes & 0x0040 && /^(3)/.test(value)) { //jcb
+ return value.length === 16;
+ }
+ if (validTypes & 0x0040 && /^(2131|1800)/.test(value)) { //jcb
+ return value.length === 15;
+ }
+ if (validTypes & 0x0080) { //unknown
+ return true;
+ }
+ return false;
+}, "Please enter a valid credit card number.");
+
+/**
+ * Validates currencies with any given symbols by @jameslouiz
+ * Symbols can be optional or required. Symbols required by default
+ *
+ * Usage examples:
+ * currency: ["£", false] - Use false for soft currency validation
+ * currency: ["$", false]
+ * currency: ["RM", false] - also works with text based symbols such as "RM" - Malaysia Ringgit etc
+ *
+ *
+ *
+ * Soft symbol checking
+ * currencyInput: {
+ * currency: ["$", false]
+ * }
+ *
+ * Strict symbol checking (default)
+ * currencyInput: {
+ * currency: "$"
+ * //OR
+ * currency: ["$", true]
+ * }
+ *
+ * Multiple Symbols
+ * currencyInput: {
+ * currency: "$,£,¢"
+ * }
+ */
+$.validator.addMethod("currency", function(value, element, param) {
+ var isParamString = typeof param === "string",
+ symbol = isParamString ? param : param[0],
+ soft = isParamString ? true : param[1],
+ regex;
+
+ symbol = symbol.replace(/,/g, "");
+ symbol = soft ? symbol + "]" : symbol + "]?";
+ regex = "^[" + symbol + "([1-9]{1}[0-9]{0,2}(\\,[0-9]{3})*(\\.[0-9]{0,2})?|[1-9]{1}[0-9]{0,}(\\.[0-9]{0,2})?|0(\\.[0-9]{0,2})?|(\\.[0-9]{1,2})?)$";
+ regex = new RegExp(regex);
+ return this.optional(element) || regex.test(value);
+
+}, "Please specify a valid currency");
+
+$.validator.addMethod("dateFA", function(value, element) {
+ return this.optional(element) || /^[1-4]\d{3}\/((0?[1-6]\/((3[0-1])|([1-2][0-9])|(0?[1-9])))|((1[0-2]|(0?[7-9]))\/(30|([1-2][0-9])|(0?[1-9]))))$/.test(value);
+}, "Please enter a correct date");
+
+/**
+ * Return true, if the value is a valid date, also making this formal check dd/mm/yyyy.
+ *
+ * @example $.validator.methods.date("01/01/1900")
+ * @result true
+ *
+ * @example $.validator.methods.date("01/13/1990")
+ * @result false
+ *
+ * @example $.validator.methods.date("01.01.1900")
+ * @result false
+ *
+ * @example
+ * @desc Declares an optional input element whose value must be a valid date.
+ *
+ * @name $.validator.methods.dateITA
+ * @type Boolean
+ * @cat Plugins/Validate/Methods
+ */
+$.validator.addMethod("dateITA", function(value, element) {
+ var check = false,
+ re = /^\d{1,2}\/\d{1,2}\/\d{4}$/,
+ adata, gg, mm, aaaa, xdata;
+ if ( re.test(value)) {
+ adata = value.split("/");
+ gg = parseInt(adata[0], 10);
+ mm = parseInt(adata[1], 10);
+ aaaa = parseInt(adata[2], 10);
+ xdata = new Date(aaaa, mm - 1, gg, 12, 0, 0, 0);
+ if ( ( xdata.getUTCFullYear() === aaaa ) && ( xdata.getUTCMonth () === mm - 1 ) && ( xdata.getUTCDate() === gg ) ) {
+ check = true;
+ } else {
+ check = false;
+ }
+ } else {
+ check = false;
+ }
+ return this.optional(element) || check;
+}, "Please enter a correct date");
+
+$.validator.addMethod("dateNL", function(value, element) {
+ return this.optional(element) || /^(0?[1-9]|[12]\d|3[01])[\.\/\-](0?[1-9]|1[012])[\.\/\-]([12]\d)?(\d\d)$/.test(value);
+}, "Please enter a correct date");
+
+// Older "accept" file extension method. Old docs: http://docs.jquery.com/Plugins/Validation/Methods/accept
+$.validator.addMethod("extension", 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"));
+}, $.validator.format("Please enter a value with a valid extension."));
+
+/**
+ * Dutch giro account numbers (not bank numbers) have max 7 digits
+ */
+$.validator.addMethod("giroaccountNL", function(value, element) {
+ return this.optional(element) || /^[0-9]{1,7}$/.test(value);
+}, "Please specify a valid giro account number");
+
+/**
+ * IBAN is the international bank account number.
+ * It has a country - specific format, that is checked here too
+ */
+$.validator.addMethod("iban", function(value, element) {
+ // some quick simple tests to prevent needless work
+ if (this.optional(element)) {
+ return true;
+ }
+
+ // remove spaces and to upper case
+ var iban = value.replace(/ /g, "").toUpperCase(),
+ ibancheckdigits = "",
+ leadingZeroes = true,
+ cRest = "",
+ cOperator = "",
+ countrycode, ibancheck, charAt, cChar, bbanpattern, bbancountrypatterns, ibanregexp, i, p;
+
+ if (!(/^([a-zA-Z0-9]{4} ){2,8}[a-zA-Z0-9]{1,4}|[a-zA-Z0-9]{12,34}$/.test(iban))) {
+ return false;
+ }
+
+ // check the country code and find the country specific format
+ countrycode = iban.substring(0, 2);
+ bbancountrypatterns = {
+ "AL": "\\d{8}[\\dA-Z]{16}",
+ "AD": "\\d{8}[\\dA-Z]{12}",
+ "AT": "\\d{16}",
+ "AZ": "[\\dA-Z]{4}\\d{20}",
+ "BE": "\\d{12}",
+ "BH": "[A-Z]{4}[\\dA-Z]{14}",
+ "BA": "\\d{16}",
+ "BR": "\\d{23}[A-Z][\\dA-Z]",
+ "BG": "[A-Z]{4}\\d{6}[\\dA-Z]{8}",
+ "CR": "\\d{17}",
+ "HR": "\\d{17}",
+ "CY": "\\d{8}[\\dA-Z]{16}",
+ "CZ": "\\d{20}",
+ "DK": "\\d{14}",
+ "DO": "[A-Z]{4}\\d{20}",
+ "EE": "\\d{16}",
+ "FO": "\\d{14}",
+ "FI": "\\d{14}",
+ "FR": "\\d{10}[\\dA-Z]{11}\\d{2}",
+ "GE": "[\\dA-Z]{2}\\d{16}",
+ "DE": "\\d{18}",
+ "GI": "[A-Z]{4}[\\dA-Z]{15}",
+ "GR": "\\d{7}[\\dA-Z]{16}",
+ "GL": "\\d{14}",
+ "GT": "[\\dA-Z]{4}[\\dA-Z]{20}",
+ "HU": "\\d{24}",
+ "IS": "\\d{22}",
+ "IE": "[\\dA-Z]{4}\\d{14}",
+ "IL": "\\d{19}",
+ "IT": "[A-Z]\\d{10}[\\dA-Z]{12}",
+ "KZ": "\\d{3}[\\dA-Z]{13}",
+ "KW": "[A-Z]{4}[\\dA-Z]{22}",
+ "LV": "[A-Z]{4}[\\dA-Z]{13}",
+ "LB": "\\d{4}[\\dA-Z]{20}",
+ "LI": "\\d{5}[\\dA-Z]{12}",
+ "LT": "\\d{16}",
+ "LU": "\\d{3}[\\dA-Z]{13}",
+ "MK": "\\d{3}[\\dA-Z]{10}\\d{2}",
+ "MT": "[A-Z]{4}\\d{5}[\\dA-Z]{18}",
+ "MR": "\\d{23}",
+ "MU": "[A-Z]{4}\\d{19}[A-Z]{3}",
+ "MC": "\\d{10}[\\dA-Z]{11}\\d{2}",
+ "MD": "[\\dA-Z]{2}\\d{18}",
+ "ME": "\\d{18}",
+ "NL": "[A-Z]{4}\\d{10}",
+ "NO": "\\d{11}",
+ "PK": "[\\dA-Z]{4}\\d{16}",
+ "PS": "[\\dA-Z]{4}\\d{21}",
+ "PL": "\\d{24}",
+ "PT": "\\d{21}",
+ "RO": "[A-Z]{4}[\\dA-Z]{16}",
+ "SM": "[A-Z]\\d{10}[\\dA-Z]{12}",
+ "SA": "\\d{2}[\\dA-Z]{18}",
+ "RS": "\\d{18}",
+ "SK": "\\d{20}",
+ "SI": "\\d{15}",
+ "ES": "\\d{20}",
+ "SE": "\\d{20}",
+ "CH": "\\d{5}[\\dA-Z]{12}",
+ "TN": "\\d{20}",
+ "TR": "\\d{5}[\\dA-Z]{17}",
+ "AE": "\\d{3}\\d{16}",
+ "GB": "[A-Z]{4}\\d{14}",
+ "VG": "[\\dA-Z]{4}\\d{16}"
+ };
+
+ bbanpattern = bbancountrypatterns[countrycode];
+ // As new countries will start using IBAN in the
+ // future, we only check if the countrycode is known.
+ // This prevents false negatives, while almost all
+ // false positives introduced by this, will be caught
+ // by the checksum validation below anyway.
+ // Strict checking should return FALSE for unknown
+ // countries.
+ if (typeof bbanpattern !== "undefined") {
+ ibanregexp = new RegExp("^[A-Z]{2}\\d{2}" + bbanpattern + "$", "");
+ if (!(ibanregexp.test(iban))) {
+ return false; // invalid country specific format
+ }
+ }
+
+ // now check the checksum, first convert to digits
+ ibancheck = iban.substring(4, iban.length) + iban.substring(0, 4);
+ for (i = 0; i < ibancheck.length; i++) {
+ charAt = ibancheck.charAt(i);
+ if (charAt !== "0") {
+ leadingZeroes = false;
+ }
+ if (!leadingZeroes) {
+ ibancheckdigits += "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ".indexOf(charAt);
+ }
+ }
+
+ // calculate the result of: ibancheckdigits % 97
+ for (p = 0; p < ibancheckdigits.length; p++) {
+ cChar = ibancheckdigits.charAt(p);
+ cOperator = "" + cRest + "" + cChar;
+ cRest = cOperator % 97;
+ }
+ return cRest === 1;
+}, "Please specify a valid IBAN");
+
+$.validator.addMethod("integer", function(value, element) {
+ return this.optional(element) || /^-?\d+$/.test(value);
+}, "A positive or negative non-decimal number please");
+
+$.validator.addMethod("ipv4", function(value, element) {
+ return this.optional(element) || /^(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)$/i.test(value);
+}, "Please enter a valid IP v4 address.");
+
+$.validator.addMethod("ipv6", function(value, element) {
+ return this.optional(element) || /^((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(([0-9A-Fa-f]{1,4}:){0,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))$/i.test(value);
+}, "Please enter a valid IP v6 address.");
+
+$.validator.addMethod("lettersonly", function(value, element) {
+ return this.optional(element) || /^[a-z]+$/i.test(value);
+}, "Letters only please");
+
+$.validator.addMethod("letterswithbasicpunc", function(value, element) {
+ return this.optional(element) || /^[a-z\-.,()'"\s]+$/i.test(value);
+}, "Letters or punctuation only please");
+
+$.validator.addMethod("mobileNL", function(value, element) {
+ return this.optional(element) || /^((\+|00(\s|\s?\-\s?)?)31(\s|\s?\-\s?)?(\(0\)[\-\s]?)?|0)6((\s|\s?\-\s?)?[0-9]){8}$/.test(value);
+}, "Please specify a valid mobile number");
+
+/* For UK phone functions, do the following server side processing:
+ * Compare original input with this RegEx pattern:
+ * ^\(?(?:(?:00\)?[\s\-]?\(?|\+)(44)\)?[\s\-]?\(?(?:0\)?[\s\-]?\(?)?|0)([1-9]\d{1,4}\)?[\s\d\-]+)$
+ * Extract $1 and set $prefix to '+44
' if $1 is '44', otherwise set $prefix to '0'
+ * Extract $2 and remove hyphens, spaces and parentheses. Phone number is combined $prefix and $2.
+ * A number of very detailed GB telephone number RegEx patterns can also be found at:
+ * http://www.aa-asterisk.org.uk/index.php/Regular_Expressions_for_Validating_and_Formatting_GB_Telephone_Numbers
+ */
+$.validator.addMethod("mobileUK", function(phone_number, element) {
+ phone_number = phone_number.replace(/\(|\)|\s+|-/g, "");
+ return this.optional(element) || phone_number.length > 9 &&
+ phone_number.match(/^(?:(?:(?:00\s?|\+)44\s?|0)7(?:[1345789]\d{2}|624)\s?\d{3}\s?\d{3})$/);
+}, "Please specify a valid mobile number");
+
+/*
+ * The número de identidad de extranjero ( NIE )is a code used to identify the non-nationals in Spain
+ */
+$.validator.addMethod( "nieES", function( value ) {
+ "use strict";
+
+ value = value.toUpperCase();
+
+ // Basic format test
+ if ( !value.match( "((^[A-Z]{1}[0-9]{7}[A-Z0-9]{1}$|^[T]{1}[A-Z0-9]{8}$)|^[0-9]{8}[A-Z]{1}$)" ) ) {
+ return false;
+ }
+
+ // Test NIE
+ //T
+ if ( /^[T]{1}/.test( value ) ) {
+ return ( value[ 8 ] === /^[T]{1}[A-Z0-9]{8}$/.test( value ) );
+ }
+
+ //XYZ
+ if ( /^[XYZ]{1}/.test( value ) ) {
+ return (
+ value[ 8 ] === "TRWAGMYFPDXBNJZSQVHLCKE".charAt(
+ value.replace( "X", "0" )
+ .replace( "Y", "1" )
+ .replace( "Z", "2" )
+ .substring( 0, 8 ) % 23
+ )
+ );
+ }
+
+ return false;
+
+}, "Please specify a valid NIE number." );
+
+/*
+ * The Número de Identificación Fiscal ( NIF ) is the way tax identification used in Spain for individuals
+ */
+$.validator.addMethod( "nifES", function( value ) {
+ "use strict";
+
+ value = value.toUpperCase();
+
+ // Basic format test
+ if ( !value.match("((^[A-Z]{1}[0-9]{7}[A-Z0-9]{1}$|^[T]{1}[A-Z0-9]{8}$)|^[0-9]{8}[A-Z]{1}$)") ) {
+ return false;
+ }
+
+ // Test NIF
+ if ( /^[0-9]{8}[A-Z]{1}$/.test( value ) ) {
+ return ( "TRWAGMYFPDXBNJZSQVHLCKE".charAt( value.substring( 8, 0 ) % 23 ) === value.charAt( 8 ) );
+ }
+ // Test specials NIF (starts with K, L or M)
+ if ( /^[KLM]{1}/.test( value ) ) {
+ return ( value[ 8 ] === String.fromCharCode( 64 ) );
+ }
+
+ return false;
+
+}, "Please specify a valid NIF number." );
+
+$.validator.addMethod("nowhitespace", function(value, element) {
+ return this.optional(element) || /^\S+$/i.test(value);
+}, "No white space please");
+
+/**
+* Return true if the field value matches the given format RegExp
+*
+* @example $.validator.methods.pattern("AR1004",element,/^AR\d{4}$/)
+* @result true
+*
+* @example $.validator.methods.pattern("BR1004",element,/^AR\d{4}$/)
+* @result false
+*
+* @name $.validator.methods.pattern
+* @type Boolean
+* @cat Plugins/Validate/Methods
+*/
+$.validator.addMethod("pattern", function(value, element, param) {
+ if (this.optional(element)) {
+ return true;
+ }
+ if (typeof param === "string") {
+ param = new RegExp("^(?:" + param + ")$");
+ }
+ return param.test(value);
+}, "Invalid format.");
+
+/**
+ * Dutch phone numbers have 10 digits (or 11 and start with +31).
+ */
+$.validator.addMethod("phoneNL", function(value, element) {
+ return this.optional(element) || /^((\+|00(\s|\s?\-\s?)?)31(\s|\s?\-\s?)?(\(0\)[\-\s]?)?|0)[1-9]((\s|\s?\-\s?)?[0-9]){8}$/.test(value);
+}, "Please specify a valid phone number.");
+
+/* For UK phone functions, do the following server side processing:
+ * Compare original input with this RegEx pattern:
+ * ^\(?(?:(?:00\)?[\s\-]?\(?|\+)(44)\)?[\s\-]?\(?(?:0\)?[\s\-]?\(?)?|0)([1-9]\d{1,4}\)?[\s\d\-]+)$
+ * Extract $1 and set $prefix to '+44' if $1 is '44', otherwise set $prefix to '0'
+ * Extract $2 and remove hyphens, spaces and parentheses. Phone number is combined $prefix and $2.
+ * A number of very detailed GB telephone number RegEx patterns can also be found at:
+ * http://www.aa-asterisk.org.uk/index.php/Regular_Expressions_for_Validating_and_Formatting_GB_Telephone_Numbers
+ */
+$.validator.addMethod("phoneUK", function(phone_number, element) {
+ phone_number = phone_number.replace(/\(|\)|\s+|-/g, "");
+ return this.optional(element) || phone_number.length > 9 &&
+ phone_number.match(/^(?:(?:(?:00\s?|\+)44\s?)|(?:\(?0))(?:\d{2}\)?\s?\d{4}\s?\d{4}|\d{3}\)?\s?\d{3}\s?\d{3,4}|\d{4}\)?\s?(?:\d{5}|\d{3}\s?\d{3})|\d{5}\)?\s?\d{4,5})$/);
+}, "Please specify a valid phone number");
+
+/**
+ * matches US phone number format
+ *
+ * where the area code may not start with 1 and the prefix may not start with 1
+ * allows '-' or ' ' as a separator and allows parens around area code
+ * some people may want to put a '1' in front of their number
+ *
+ * 1(212)-999-2345 or
+ * 212 999 2344 or
+ * 212-999-0983
+ *
+ * but not
+ * 111-123-5434
+ * and not
+ * 212 123 4567
+ */
+$.validator.addMethod("phoneUS", function(phone_number, element) {
+ phone_number = phone_number.replace(/\s+/g, "");
+ return this.optional(element) || phone_number.length > 9 &&
+ phone_number.match(/^(\+?1-?)?(\([2-9]([02-9]\d|1[02-9])\)|[2-9]([02-9]\d|1[02-9]))-?[2-9]([02-9]\d|1[02-9])-?\d{4}$/);
+}, "Please specify a valid phone number");
+
+/* For UK phone functions, do the following server side processing:
+ * Compare original input with this RegEx pattern:
+ * ^\(?(?:(?:00\)?[\s\-]?\(?|\+)(44)\)?[\s\-]?\(?(?:0\)?[\s\-]?\(?)?|0)([1-9]\d{1,4}\)?[\s\d\-]+)$
+ * Extract $1 and set $prefix to '+44' if $1 is '44', otherwise set $prefix to '0'
+ * Extract $2 and remove hyphens, spaces and parentheses. Phone number is combined $prefix and $2.
+ * A number of very detailed GB telephone number RegEx patterns can also be found at:
+ * http://www.aa-asterisk.org.uk/index.php/Regular_Expressions_for_Validating_and_Formatting_GB_Telephone_Numbers
+ */
+//Matches UK landline + mobile, accepting only 01-3 for landline or 07 for mobile to exclude many premium numbers
+$.validator.addMethod("phonesUK", function(phone_number, element) {
+ phone_number = phone_number.replace(/\(|\)|\s+|-/g, "");
+ return this.optional(element) || phone_number.length > 9 &&
+ phone_number.match(/^(?:(?:(?:00\s?|\+)44\s?|0)(?:1\d{8,9}|[23]\d{9}|7(?:[1345789]\d{8}|624\d{6})))$/);
+}, "Please specify a valid uk phone number");
+
+/**
+ * Matches a valid Canadian Postal Code
+ *
+ * @example jQuery.validator.methods.postalCodeCA( "H0H 0H0", element )
+ * @result true
+ *
+ * @example jQuery.validator.methods.postalCodeCA( "H0H0H0", element )
+ * @result false
+ *
+ * @name jQuery.validator.methods.postalCodeCA
+ * @type Boolean
+ * @cat Plugins/Validate/Methods
+ */
+$.validator.addMethod( "postalCodeCA", function( value, element ) {
+ return this.optional( element ) || /^[ABCEGHJKLMNPRSTVXY]\d[A-Z] \d[A-Z]\d$/.test( value );
+}, "Please specify a valid postal code" );
+
+/*
+* Valida CEPs do brasileiros:
+*
+* Formatos aceitos:
+* 99999-999
+* 99.999-999
+* 99999999
+*/
+$.validator.addMethod("postalcodeBR", function(cep_value, element) {
+ return this.optional(element) || /^\d{2}.\d{3}-\d{3}?$|^\d{5}-?\d{3}?$/.test( cep_value );
+}, "Informe um CEP válido.");
+
+/* Matches Italian postcode (CAP) */
+$.validator.addMethod("postalcodeIT", function(value, element) {
+ return this.optional(element) || /^\d{5}$/.test(value);
+}, "Please specify a valid postal code");
+
+$.validator.addMethod("postalcodeNL", function(value, element) {
+ return this.optional(element) || /^[1-9][0-9]{3}\s?[a-zA-Z]{2}$/.test(value);
+}, "Please specify a valid postal code");
+
+// Matches UK postcode. Does not match to UK Channel Islands that have their own postcodes (non standard UK)
+$.validator.addMethod("postcodeUK", function(value, element) {
+ return this.optional(element) || /^((([A-PR-UWYZ][0-9])|([A-PR-UWYZ][0-9][0-9])|([A-PR-UWYZ][A-HK-Y][0-9])|([A-PR-UWYZ][A-HK-Y][0-9][0-9])|([A-PR-UWYZ][0-9][A-HJKSTUW])|([A-PR-UWYZ][A-HK-Y][0-9][ABEHMNPRVWXY]))\s?([0-9][ABD-HJLNP-UW-Z]{2})|(GIR)\s?(0AA))$/i.test(value);
+}, "Please specify a valid UK postcode");
+
+/*
+ * Lets you say "at least X inputs that match selector Y must be filled."
+ *
+ * The end result is that neither of these inputs:
+ *
+ *
+ *
+ *
+ * ...will validate unless at least one of them is filled.
+ *
+ * partnumber: {require_from_group: [1,".productinfo"]},
+ * description: {require_from_group: [1,".productinfo"]}
+ *
+ * options[0]: number of fields that must be filled in the group
+ * options[1]: CSS selector that defines the group of conditionally required fields
+ */
+$.validator.addMethod("require_from_group", function(value, element, options) {
+ var $fields = $(options[1], element.form),
+ $fieldsFirst = $fields.eq(0),
+ validator = $fieldsFirst.data("valid_req_grp") ? $fieldsFirst.data("valid_req_grp") : $.extend({}, this),
+ isValid = $fields.filter(function() {
+ return validator.elementValue(this);
+ }).length >= options[0];
+
+ // Store the cloned validator for future validation
+ $fieldsFirst.data("valid_req_grp", validator);
+
+ // If element isn't being validated, run each require_from_group field's validation rules
+ if (!$(element).data("being_validated")) {
+ $fields.data("being_validated", true);
+ $fields.each(function() {
+ validator.element(this);
+ });
+ $fields.data("being_validated", false);
+ }
+ return isValid;
+}, $.validator.format("Please fill at least {0} of these fields."));
+
+/*
+ * Lets you say "either at least X inputs that match selector Y must be filled,
+ * OR they must all be skipped (left blank)."
+ *
+ * The end result, is that none of these inputs:
+ *
+ *
+ *
+ *
+ *
+ * ...will validate unless either at least two of them are filled,
+ * OR none of them are.
+ *
+ * partnumber: {skip_or_fill_minimum: [2,".productinfo"]},
+ * description: {skip_or_fill_minimum: [2,".productinfo"]},
+ * color: {skip_or_fill_minimum: [2,".productinfo"]}
+ *
+ * options[0]: number of fields that must be filled in the group
+ * options[1]: CSS selector that defines the group of conditionally required fields
+ *
+ */
+$.validator.addMethod("skip_or_fill_minimum", function(value, element, options) {
+ var $fields = $(options[1], element.form),
+ $fieldsFirst = $fields.eq(0),
+ validator = $fieldsFirst.data("valid_skip") ? $fieldsFirst.data("valid_skip") : $.extend({}, this),
+ numberFilled = $fields.filter(function() {
+ return validator.elementValue(this);
+ }).length,
+ isValid = numberFilled === 0 || numberFilled >= options[0];
+
+ // Store the cloned validator for future validation
+ $fieldsFirst.data("valid_skip", validator);
+
+ // If element isn't being validated, run each skip_or_fill_minimum field's validation rules
+ if (!$(element).data("being_validated")) {
+ $fields.data("being_validated", true);
+ $fields.each(function() {
+ validator.element(this);
+ });
+ $fields.data("being_validated", false);
+ }
+ return isValid;
+}, $.validator.format("Please either skip these fields or fill at least {0} of them."));
+
+/* Validates US States and/or Territories by @jdforsythe
+ * Can be case insensitive or require capitalization - default is case insensitive
+ * Can include US Territories or not - default does not
+ * Can include US Military postal abbreviations (AA, AE, AP) - default does not
+ *
+ * Note: "States" always includes DC (District of Colombia)
+ *
+ * Usage examples:
+ *
+ * This is the default - case insensitive, no territories, no military zones
+ * stateInput: {
+ * caseSensitive: false,
+ * includeTerritories: false,
+ * includeMilitary: false
+ * }
+ *
+ * Only allow capital letters, no territories, no military zones
+ * stateInput: {
+ * caseSensitive: false
+ * }
+ *
+ * Case insensitive, include territories but not military zones
+ * stateInput: {
+ * includeTerritories: true
+ * }
+ *
+ * Only allow capital letters, include territories and military zones
+ * stateInput: {
+ * caseSensitive: true,
+ * includeTerritories: true,
+ * includeMilitary: true
+ * }
+ *
+ *
+ *
+ */
+
+jQuery.validator.addMethod("stateUS", function(value, element, options) {
+ var isDefault = typeof options === "undefined",
+ caseSensitive = ( isDefault || typeof options.caseSensitive === "undefined" ) ? false : options.caseSensitive,
+ includeTerritories = ( isDefault || typeof options.includeTerritories === "undefined" ) ? false : options.includeTerritories,
+ includeMilitary = ( isDefault || typeof options.includeMilitary === "undefined" ) ? false : options.includeMilitary,
+ regex;
+
+ if (!includeTerritories && !includeMilitary) {
+ regex = "^(A[KLRZ]|C[AOT]|D[CE]|FL|GA|HI|I[ADLN]|K[SY]|LA|M[ADEINOST]|N[CDEHJMVY]|O[HKR]|PA|RI|S[CD]|T[NX]|UT|V[AT]|W[AIVY])$";
+ } else if (includeTerritories && includeMilitary) {
+ regex = "^(A[AEKLPRSZ]|C[AOT]|D[CE]|FL|G[AU]|HI|I[ADLN]|K[SY]|LA|M[ADEINOPST]|N[CDEHJMVY]|O[HKR]|P[AR]|RI|S[CD]|T[NX]|UT|V[AIT]|W[AIVY])$";
+ } else if (includeTerritories) {
+ regex = "^(A[KLRSZ]|C[AOT]|D[CE]|FL|G[AU]|HI|I[ADLN]|K[SY]|LA|M[ADEINOPST]|N[CDEHJMVY]|O[HKR]|P[AR]|RI|S[CD]|T[NX]|UT|V[AIT]|W[AIVY])$";
+ } else {
+ regex = "^(A[AEKLPRZ]|C[AOT]|D[CE]|FL|GA|HI|I[ADLN]|K[SY]|LA|M[ADEINOST]|N[CDEHJMVY]|O[HKR]|PA|RI|S[CD]|T[NX]|UT|V[AT]|W[AIVY])$";
+ }
+
+ regex = caseSensitive ? new RegExp(regex) : new RegExp(regex, "i");
+ return this.optional(element) || regex.test(value);
+},
+"Please specify a valid state");
+
+// TODO check if value starts with <, otherwise don't try stripping anything
+$.validator.addMethod("strippedminlength", function(value, element, param) {
+ return $(value).text().length >= param;
+}, $.validator.format("Please enter at least {0} characters"));
+
+$.validator.addMethod("time", function(value, element) {
+ return this.optional(element) || /^([01]\d|2[0-3])(:[0-5]\d){1,2}$/.test(value);
+}, "Please enter a valid time, between 00:00 and 23:59");
+
+$.validator.addMethod("time12h", function(value, element) {
+ return this.optional(element) || /^((0?[1-9]|1[012])(:[0-5]\d){1,2}(\ ?[AP]M))$/i.test(value);
+}, "Please enter a valid time in 12-hour am/pm format");
+
+// same as url, but TLD is optional
+$.validator.addMethod("url2", function(value, element) {
+ 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);
+}, $.validator.messages.url);
+
+/**
+ * Return true, if the value is a valid vehicle identification number (VIN).
+ *
+ * Works with all kind of text inputs.
+ *
+ * @example
+ * @desc Declares a required input element whose value must be a valid vehicle identification number.
+ *
+ * @name $.validator.methods.vinUS
+ * @type Boolean
+ * @cat Plugins/Validate/Methods
+ */
+$.validator.addMethod("vinUS", function(v) {
+ if (v.length !== 17) {
+ return false;
+ }
+
+ var LL = [ "A", "B", "C", "D", "E", "F", "G", "H", "J", "K", "L", "M", "N", "P", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" ],
+ VL = [ 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 7, 9, 2, 3, 4, 5, 6, 7, 8, 9 ],
+ FL = [ 8, 7, 6, 5, 4, 3, 2, 10, 0, 9, 8, 7, 6, 5, 4, 3, 2 ],
+ rs = 0,
+ i, n, d, f, cd, cdv;
+
+ for (i = 0; i < 17; i++) {
+ f = FL[i];
+ d = v.slice(i, i + 1);
+ if (i === 8) {
+ cdv = d;
+ }
+ if (!isNaN(d)) {
+ d *= f;
+ } else {
+ for (n = 0; n < LL.length; n++) {
+ if (d.toUpperCase() === LL[n]) {
+ d = VL[n];
+ d *= f;
+ if (isNaN(cdv) && n === 8) {
+ cdv = LL[n];
+ }
+ break;
+ }
+ }
+ }
+ rs += d;
+ }
+ cd = rs % 11;
+ if (cd === 10) {
+ cd = "X";
+ }
+ if (cd === cdv) {
+ return true;
+ }
+ return false;
+}, "The specified vehicle identification number (VIN) is invalid.");
+
+$.validator.addMethod("zipcodeUS", function(value, element) {
+ return this.optional(element) || /^\d{5}(-\d{4})?$/.test(value);
+}, "The specified US ZIP Code is invalid");
+
+$.validator.addMethod("ziprange", function(value, element) {
+ return this.optional(element) || /^90[2-5]\d\{2\}-\d{4}$/.test(value);
+}, "Your ZIP-code must be in the range 902xx-xxxx to 905xx-xxxx");
+
+}));
+(function() {
+ var CSRFToken, Click, ComponentUrl, EVENTS, Link, ProgressBar, browserIsntBuggy, browserSupportsCustomEvents, browserSupportsPushState, browserSupportsTurbolinks, bypassOnLoadPopstate, cacheCurrentPage, cacheSize, changePage, clone, constrainPageCacheTo, createDocument, crossOriginRedirect, currentState, enableProgressBar, enableTransitionCache, executeScriptTags, extractTitleAndBody, fetch, fetchHistory, fetchReplacement, historyStateIsDefined, initializeTurbolinks, installDocumentReadyPageEventTriggers, installHistoryChangeHandler, installJqueryAjaxSuccessPageUpdateTrigger, loadedAssets, manuallyTriggerHashChangeForFirefox, pageCache, pageChangePrevented, pagesCached, popCookie, processResponse, progressBar, recallScrollPosition, ref, referer, reflectNewUrl, reflectRedirectedUrl, rememberCurrentState, rememberCurrentUrl, rememberReferer, removeNoscriptTags, requestMethodIsSafe, resetScrollPosition, setAutofocusElement, transitionCacheEnabled, transitionCacheFor, triggerEvent, visit, xhr,
+ indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; },
+ extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
+ hasProp = {}.hasOwnProperty,
+ slice = [].slice,
+ bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
+
+ pageCache = {};
+
+ cacheSize = 10;
+
+ transitionCacheEnabled = false;
+
+ progressBar = null;
+
+ currentState = null;
+
+ loadedAssets = null;
+
+ referer = null;
+
+ xhr = null;
+
+ EVENTS = {
+ BEFORE_CHANGE: 'page:before-change',
+ FETCH: 'page:fetch',
+ RECEIVE: 'page:receive',
+ CHANGE: 'page:change',
+ UPDATE: 'page:update',
+ LOAD: 'page:load',
+ RESTORE: 'page:restore',
+ BEFORE_UNLOAD: 'page:before-unload',
+ EXPIRE: 'page:expire'
+ };
+
+ fetch = function(url) {
+ var cachedPage;
+ url = new ComponentUrl(url);
+ rememberReferer();
+ cacheCurrentPage();
+ if (progressBar != null) {
+ progressBar.start();
+ }
+ if (transitionCacheEnabled && (cachedPage = transitionCacheFor(url.absolute))) {
+ fetchHistory(cachedPage);
+ return fetchReplacement(url, null, false);
+ } else {
+ return fetchReplacement(url, resetScrollPosition);
+ }
+ };
+
+ transitionCacheFor = function(url) {
+ var cachedPage;
+ cachedPage = pageCache[url];
+ if (cachedPage && !cachedPage.transitionCacheDisabled) {
+ return cachedPage;
+ }
+ };
+
+ enableTransitionCache = function(enable) {
+ if (enable == null) {
+ enable = true;
+ }
+ return transitionCacheEnabled = enable;
+ };
+
+ enableProgressBar = function(enable) {
+ if (enable == null) {
+ enable = true;
+ }
+ if (!browserSupportsTurbolinks) {
+ return;
+ }
+ if (enable) {
+ return progressBar != null ? progressBar : progressBar = new ProgressBar('html');
+ } else {
+ if (progressBar != null) {
+ progressBar.uninstall();
+ }
+ return progressBar = null;
+ }
+ };
+
+ fetchReplacement = function(url, onLoadFunction, showProgressBar) {
+ if (showProgressBar == null) {
+ showProgressBar = true;
+ }
+ triggerEvent(EVENTS.FETCH, {
+ url: url.absolute
+ });
+ if (xhr != null) {
+ xhr.abort();
+ }
+ xhr = new XMLHttpRequest;
+ xhr.open('GET', url.withoutHashForIE10compatibility(), true);
+ xhr.setRequestHeader('Accept', 'text/html, application/xhtml+xml, application/xml');
+ xhr.setRequestHeader('X-XHR-Referer', referer);
+ xhr.onload = function() {
+ var doc;
+ triggerEvent(EVENTS.RECEIVE, {
+ url: url.absolute
+ });
+ if (doc = processResponse()) {
+ reflectNewUrl(url);
+ reflectRedirectedUrl();
+ changePage.apply(null, extractTitleAndBody(doc));
+ manuallyTriggerHashChangeForFirefox();
+ if (typeof onLoadFunction === "function") {
+ onLoadFunction();
+ }
+ return triggerEvent(EVENTS.LOAD);
+ } else {
+ return document.location.href = crossOriginRedirect() || url.absolute;
+ }
+ };
+ if (progressBar && showProgressBar) {
+ xhr.onprogress = (function(_this) {
+ return function(event) {
+ var percent;
+ percent = event.lengthComputable ? event.loaded / event.total * 100 : progressBar.value + (100 - progressBar.value) / 10;
+ return progressBar.advanceTo(percent);
+ };
+ })(this);
+ }
+ xhr.onloadend = function() {
+ return xhr = null;
+ };
+ xhr.onerror = function() {
+ return document.location.href = url.absolute;
+ };
+ return xhr.send();
+ };
+
+ fetchHistory = function(cachedPage) {
+ if (xhr != null) {
+ xhr.abort();
+ }
+ changePage(cachedPage.title, cachedPage.body);
+ recallScrollPosition(cachedPage);
+ return triggerEvent(EVENTS.RESTORE);
+ };
+
+ cacheCurrentPage = function() {
+ var currentStateUrl;
+ currentStateUrl = new ComponentUrl(currentState.url);
+ pageCache[currentStateUrl.absolute] = {
+ url: currentStateUrl.relative,
+ body: document.body,
+ title: document.title,
+ positionY: window.pageYOffset,
+ positionX: window.pageXOffset,
+ cachedAt: new Date().getTime(),
+ transitionCacheDisabled: document.querySelector('[data-no-transition-cache]') != null
+ };
+ return constrainPageCacheTo(cacheSize);
+ };
+
+ pagesCached = function(size) {
+ if (size == null) {
+ size = cacheSize;
+ }
+ if (/^[\d]+$/.test(size)) {
+ return cacheSize = parseInt(size);
+ }
+ };
+
+ constrainPageCacheTo = function(limit) {
+ var cacheTimesRecentFirst, i, key, len, pageCacheKeys, results;
+ pageCacheKeys = Object.keys(pageCache);
+ cacheTimesRecentFirst = pageCacheKeys.map(function(url) {
+ return pageCache[url].cachedAt;
+ }).sort(function(a, b) {
+ return b - a;
+ });
+ results = [];
+ for (i = 0, len = pageCacheKeys.length; i < len; i++) {
+ key = pageCacheKeys[i];
+ if (!(pageCache[key].cachedAt <= cacheTimesRecentFirst[limit])) {
+ continue;
+ }
+ triggerEvent(EVENTS.EXPIRE, pageCache[key]);
+ results.push(delete pageCache[key]);
+ }
+ return results;
+ };
+
+ changePage = function(title, body, csrfToken, runScripts) {
+ triggerEvent(EVENTS.BEFORE_UNLOAD);
+ document.title = title;
+ document.documentElement.replaceChild(body, document.body);
+ if (csrfToken != null) {
+ CSRFToken.update(csrfToken);
+ }
+ setAutofocusElement();
+ if (runScripts) {
+ executeScriptTags();
+ }
+ currentState = window.history.state;
+ if (progressBar != null) {
+ progressBar.done();
+ }
+ triggerEvent(EVENTS.CHANGE);
+ return triggerEvent(EVENTS.UPDATE);
+ };
+
+ executeScriptTags = function() {
+ var attr, copy, i, j, len, len1, nextSibling, parentNode, ref, ref1, script, scripts;
+ scripts = Array.prototype.slice.call(document.body.querySelectorAll('script:not([data-turbolinks-eval="false"])'));
+ for (i = 0, len = scripts.length; i < len; i++) {
+ script = scripts[i];
+ if (!((ref = script.type) === '' || ref === 'text/javascript')) {
+ continue;
+ }
+ copy = document.createElement('script');
+ ref1 = script.attributes;
+ for (j = 0, len1 = ref1.length; j < len1; j++) {
+ attr = ref1[j];
+ copy.setAttribute(attr.name, attr.value);
+ }
+ if (!script.hasAttribute('async')) {
+ copy.async = false;
+ }
+ copy.appendChild(document.createTextNode(script.innerHTML));
+ parentNode = script.parentNode, nextSibling = script.nextSibling;
+ parentNode.removeChild(script);
+ parentNode.insertBefore(copy, nextSibling);
+ }
+ };
+
+ removeNoscriptTags = function(node) {
+ node.innerHTML = node.innerHTML.replace(/