vendor/assets/javascripts/webshims/shims/combos/24.js in webshims-rails-1.10.11 vs vendor/assets/javascripts/webshims/shims/combos/24.js in webshims-rails-1.11.1

- old
+ new

@@ -2,10 +2,14 @@ var isWebkit = 'webkitURL' in window; var chromeBugs = isWebkit && Modernizr.formvalidation && !webshims.bugs.bustedValidity; var webkitVersion = chromeBugs && parseFloat((navigator.userAgent.match(/Safari\/([\d\.]+)/) || ['', '999999'])[1], 10); var invalidClass = options.iVal.errorClass || 'user-error'; var validClass = options.iVal.successClass || 'user-success'; + + var invalidWrapperClass = options.iVal.errorWrapperClass || 'ws-invalid'; + var successWrapperClass = options.iVal.successWrapperClass || 'ws-success'; + var errorBoxClass = options.iVal.errorBoxClass || 'ws-errorbox'; var checkTypes = {checkbox: 1, radio: 1}; var emptyJ = $([]); var isValid = function(elem){ return ($.prop(elem, 'validity') || {valid: 1}).valid; @@ -91,18 +95,23 @@ (chromeBugs && (e.type == 'change' || webkitVersion < 537.36) && noFocusWidgets[shadowType] && $(e.target).is(':focus')) || (e.type == 'focusout' && elem.type == 'radio' && isInGroup(elem.name)) ){ return; } + if(webshims.refreshCustomValidityRules){ + if(webshims.refreshCustomValidityRules(elem) == 'async'){ + $(elem).one('refreshvalidityui', switchValidityClass); + return; + } + } + var validity = $.prop(elem, 'validity'); var addClass, removeClass, trigger, generaltrigger, validityCause; - if(webshims.refreshCustomValidityRules){ - webshims.refreshCustomValidityRules(elem); - } + if(validity.valid){ if(!shadowElem.hasClass(validClass)){ addClass = validClass; removeClass = invalidClass; generaltrigger = 'changedvaliditystate'; @@ -455,14 +464,14 @@ webshims.errorbox = { create: function(elem, fieldWrapper){ if(!fieldWrapper){ fieldWrapper = this.getFieldWrapper(elem); } - var errorBox = $('div.ws-errorbox', fieldWrapper); + var errorBox = $('div.'+errorBoxClass, fieldWrapper); if(!errorBox.length){ - errorBox = $('<div class="ws-errorbox" hidden="hidden">'); + errorBox = $('<div class="'+ errorBoxClass +'" hidden="hidden">'); fieldWrapper.append(errorBox); } fieldWrapper.data('errorbox', errorBox); return errorBox; @@ -479,43 +488,87 @@ if(!fieldWrapper){ fieldWrapper = $(elem).parent().closest(':not(span, label, em, strong, b, i, mark, p)'); } return fieldWrapper; }, + _createContentMessage: (function(){ + var fields = {}; + var getErrorName = function(elem){ + var ret = $(elem).data('errortype'); + if(!ret){ + $.each(fields, function(errorName, cNames){ + if($(elem).is(cNames)){ + ret = errorName; + return false; + } + }); + } + return ret || 'defaultMessage'; + }; + $(function(){ + $.each($('<input />').prop('validity'), function(name){ + if(name != 'valid'){ + var cName = name.replace(/[A-Z]/, function(c){ + return '-'+(c).toLowerCase(); + }); + fields[name] = '.'+cName+', .'+name+', .'+(name).toLowerCase()+', [data-errortype="'+ name +'"]'; + } + }); + }); + return function(elem, errorBox){ + var extended = false; + var errorMessages = $(elem).data('errormessage') || {}; + if(typeof errorMessages == 'string'){ + errorMessages = {defaultMessage: errorMessages}; + } + $('> *', errorBox).each(function(){ + var name = getErrorName(this); + if(!errorMessages[name]){ + extended = true; + errorMessages[name] = $(this).html(); + } + }); + if(extended){ + $(elem).data('errormessage', errorMessages); + } + }; + })(), get: function(elem, fieldWrapper){ if(!fieldWrapper){ fieldWrapper = this.getFieldWrapper(elem); } var errorBox = fieldWrapper.data('errorbox'); if(!errorBox){ errorBox = this.create(elem, fieldWrapper); + this._createContentMessage(elem, errorBox); } else if(typeof errorBox == 'string'){ errorBox = $('#'+errorBox); fieldWrapper.data('errorbox', errorBox); + this._createContentMessage(elem, errorBox); } return errorBox; }, addSuccess: function(elem, fieldWrapper){ var type = $.prop(elem, 'type'); var check = function(){ var hasVal = checkTypes[type] ? $.prop(elem, 'checked') : $(elem).val(); - fieldWrapper[hasVal ? 'addClass' : 'removeClass']('ws-success'); + fieldWrapper[hasVal ? 'addClass' : 'removeClass'](successWrapperClass); }; var evt = changeTypes[type] ? 'change' : 'blur'; $(elem).off('.recheckvalid').on(evt+'.recheckinvalid', check); check(); }, hideError: function(elem, reset){ var fieldWrapper = this.getFieldWrapper(elem); - var errorBox = fieldWrapper.data('errorbox'); + var errorBox = fieldWrapper.hasClass(invalidWrapperClass) ? this.get(elem, fieldWrapper) : fieldWrapper.data('errorbox'); if(errorBox && errorBox.jquery){ - fieldWrapper.removeClass('ws-invalid'); + fieldWrapper.removeClass(invalidWrapperClass); errorBox.message = ''; $(elem).filter('input').off('.recheckinvalid'); - errorBox.slideUp(function(){ + errorBox[fx[options.iVal.fx].hide](function(){ $(this).attr({hidden: 'hidden'}); }); } if(!reset){ this.addSuccess(elem, fieldWrapper); @@ -526,47 +579,53 @@ if(options.iVal.recheckDelay && options.iVal.recheckDelay > 90){ var timer; var throttle = function(){ switchValidityClass({type: 'input', target: input}); }; - $(input).filter('input:not([type="checkbox"], [type="radio"])').off('.recheckinvalid').on('input.recheckinvalid', function(){ - clearTimeout(timer); - timer = setTimeout(throttle, options.iVal.recheckDelay); - }); + $(input) + .filter('input:not([type="checkbox"], [type="radio"])') + .off('.recheckinvalid') + .on('input.recheckinvalid', function(){ + clearTimeout(timer); + timer = setTimeout(throttle, options.iVal.recheckDelay); + }) + .on('focusout.recheckinvalid', function(){ + clearTimeout(timer); + }) + ; } }, - showError: function(elem, message){ + showError: function(elem){ var fieldWrapper = this.getFieldWrapper(elem); var box = this.get(elem, fieldWrapper); - + var message = $(elem).getErrorMessage(); if(box.message != message){ box.stop(true, true).html('<p>'+ message +'</p>'); box.message = message; - fieldWrapper.addClass('ws-invalid').removeClass('ws-success'); - if(box.is('[hidden]')){ + fieldWrapper.addClass(invalidWrapperClass).removeClass(successWrapperClass); + if(box.is('[hidden]') || box.css('display') == 'none'){ this.recheckInvalidInput(elem); box .css({display: 'none'}) .removeAttr('hidden') [fx[options.iVal.fx].show]() ; } } - fieldWrapper.removeClass('ws-success'); + fieldWrapper.removeClass(successWrapperClass); $(elem).off('.recheckvalid'); return fieldWrapper; }, reset: function(elem){ - this.hideError(elem, true).removeClass('ws-success'); + this.hideError(elem, true).removeClass(successWrapperClass); }, toggle: function(elem){ - var message = $(elem).getErrorMessage(); - if(message){ - this.showError(elem, message); + if($(elem).is(':invalid')){ + this.showError(elem); } else { - this.hideError(elem, message); + this.hideError(elem); } } }; $(document.body) @@ -597,20 +656,72 @@ } } } }, submit: function(e){ - if(options.iVal.sel && $(e.target).is(options.iVal.sel) && $.prop(e.target, 'noValidate') && !$(e.target).checkValidity()){ + if(options.iVal.sel && !options.iVal.noSubmitCheck &&$(e.target).is(options.iVal.sel) && $.prop(e.target, 'noValidate') && !$(e.target).checkValidity()){ e.stopImmediatePropagation(); return false; } } }) ; webshims.modules["form-core"].getGroupElements = getGroupElements; + + if(options.replaceValidationUI){ + webshims.ready('DOM forms', function(){ + $(document).on('firstinvalid', function(e){ + if(!e.isInvalidUIPrevented()){ + e.preventDefault(); + webshims.validityAlert.showFor( e.target ); + } + }); + }); + } + + /* extension, but also used to fix native implementation workaround/bugfixes */ + (function(){ + var firstEvent, + invalids = [], + stopSubmitTimer, + form + ; + + $(document).on('invalid', function(e){ + if(e.wrongWebkitInvalid){return;} + var jElm = $(e.target); + + + if(!firstEvent){ + //trigger firstinvalid + firstEvent = $.Event('firstinvalid'); + firstEvent.isInvalidUIPrevented = e.isDefaultPrevented; + var firstSystemInvalid = $.Event('firstinvalidsystem'); + $(document).triggerHandler(firstSystemInvalid, {element: e.target, form: e.target.form, isInvalidUIPrevented: e.isDefaultPrevented}); + jElm.trigger(firstEvent); + } + + //if firstinvalid was prevented all invalids will be also prevented + if( firstEvent && firstEvent.isDefaultPrevented() ){ + e.preventDefault(); + } + invalids.push(e.target); + e.extraData = 'fix'; + clearTimeout(stopSubmitTimer); + stopSubmitTimer = setTimeout(function(){ + var lastEvent = {type: 'lastinvalid', cancelable: false, invalidlist: $(invalids)}; + //reset firstinvalid + firstEvent = false; + invalids = []; + $(e.target).trigger(lastEvent, [lastEvent]); + }, 9); + jElm = null; + }); + })(); + //see: https://bugs.webkit.org/show_bug.cgi?id=113377 if (chromeBugs && webkitVersion < 540) { (function(){ var elems = /^(?:textarea|input)$/i; var form = false; @@ -647,81 +758,100 @@ var blockCustom; var initTest; var onEventTest = function(e){ webshims.refreshCustomValidityRules(e.target); }; + var noValidate = function(){ + return !noValidate.types[this.type]; + }; + noValidate.types = { + hidden: 1, + image: 1, + button: 1, + reset: 1, + submit: 1 + }; - webshims.customErrorMessages = {}; webshims.addCustomValidityRule = function(name, test, defaultMessage){ customValidityRules[name] = test; if(!webshims.customErrorMessages[name]){ webshims.customErrorMessages[name] = []; webshims.customErrorMessages[name][''] = defaultMessage || name; } - if($.isReady && formReady){ - $('input, select, textarea').each(function(){ - testValidityRules(this); - }); + if(formReady){ + $('input, select, textarea') + .filter(noValidate) + .each(function(){ + testValidityRules(this); + }) + ; } }; webshims.refreshCustomValidityRules = function(elem){ - if(!elem.form || (!initTest && !$.prop(elem, 'willValidate')) ){return;} - blockCustom = true; - var customMismatchedRule = $.data(elem, 'customMismatchedRule'); + if(!initTest){return;} + + var data = $(elem).data() || $.data(elem, {}); + var customMismatchedRule = data.customMismatchedRule; var validity = $.prop(elem, 'validity') || {}; var message = ''; - if(customMismatchedRule || !validity.customError){ + var setMessage = function(message, errorType){ + blockCustom = true; + data.customMismatchedRule = message ? errorType : ''; + + if(typeof message != 'string'){ + message = $(elem).data('errormessage') || elem.getAttribute('x-moz-errormessage') || webshims.customErrorMessages[errorType][webshims.activeLang()] || webshims.customErrorMessages[errorType]['']; + } + + if(typeof message == 'object'){ + message = message[errorType] || message.customError || message.defaultMessage; + } + $(elem).setCustomValidity(message); + blockCustom = false; + }; + if(customMismatchedRule || validity.valid || (data.dependentValidation && !data.dependentValidation._init)){ var val = $(elem).val(); $.each(customValidityRules, function(name, test){ - message = test(elem, val) || ''; + message = test(elem, val, data, setMessage) || ''; customMismatchedRule = name; if(message){ - - if(typeof message != 'string'){ - message = $(elem).data('errormessage') || elem.getAttribute('x-moz-errormessage') || webshims.customErrorMessages[name][webshims.activeLang()] || webshims.customErrorMessages[name]['']; - } - - if(typeof message == 'object'){ - message = message[name] || message.customError || message.defaultMessage; - } return false; } }); - - if(message){ - $.data(elem, 'customMismatchedRule', customMismatchedRule); + if(message != 'async'){ + setMessage(message, customMismatchedRule); } - $(elem).setCustomValidity(message); } - blockCustom = false; + return message; }; var testValidityRules = webshims.refreshCustomValidityRules; webshims.ready('forms form-validation', function(){ - - var oldCustomValidity = $.fn.setCustomValidity; - - - $.fn.setCustomValidity = function(message){ - if(!blockCustom){ - this.data('customMismatchedRule', ''); + $.propHooks.setCustomValidity = { + get: function(elem){ + if(!blockCustom){ + $.data(elem, 'customMismatchedRule', ''); + } + return null; } - return oldCustomValidity.apply(this, arguments); }; + setTimeout(function(){ webshims.addReady(function(context, selfElement){ initTest = true; - $('input, select, textarea', context).add(selfElement.filter('input, select, textarea')).each(function(){ - testValidityRules(this); - }); - initTest = false; + $('input, select, textarea', context).add(selfElement.filter('input, select, textarea')) + .filter(noValidate) + .each(function(){ + testValidityRules(this); + }) + ; + formReady = true; }); - $(document).on('refreshCustomValidityRules change', onEventTest); + $(document).on('refreshCustomValidityRules', onEventTest); }, 9); }); })(); @@ -735,21 +865,24 @@ * - <input type="date" id="start" data-dependent-validation='{"from": "end", "prop": "max"}' /> <input type="date" id="end" data-dependent-validation='{"from": "start", "prop": "min"}' /> * - <input type="checkbox" id="check" /> <input data-dependent-validation='checkbox' /> */ (function(){ - var addCustomValidityRule = $.webshims.addCustomValidityRule; - addCustomValidityRule('partialPattern', function(elem, val){ - if(!val || !elem.getAttribute('data-partial-pattern')){return;} - var pattern = $(elem).data('partial-pattern'); - if(!pattern){return;} + var addCustomValidityRule = webshims.addCustomValidityRule; + var getId = function(name){ + return document.getElementById(name); + }; + addCustomValidityRule('partialPattern', function(elem, val, pattern){ + pattern = pattern.partialPattern; + if(!val || !pattern){return;} return !(new RegExp('(' + pattern + ')', 'i').test(val)); }, 'This format is not allowed here.'); - addCustomValidityRule('tooShort', function(elem, val){ - if(!val || !elem.getAttribute('data-minlength')){return;} - return $(elem).data('minlength') > val.length; + + addCustomValidityRule('tooShort', function(elem, val, data){ + if(!val || !data.minlength){return;} + return data.minlength > val.length; }, 'Entered value is too short.'); var groupTimer = {}; addCustomValidityRule('group-required', function(elem, val){ var name = elem.name; @@ -763,11 +896,11 @@ checkboxes .addClass('group-required') .unbind('click.groupRequired') .bind('click.groupRequired', function(){ checkboxes.filter('.group-required').each(function(){ - $.webshims.refreshCustomValidityRules(this); + webshims.refreshCustomValidityRules(this); }); }) ; }, 9); @@ -802,23 +935,20 @@ }; var getGroupElements = function(elem) { return $(elem.form[elem.name]).filter('[type="radio"]'); }; - $.webshims.ready('form-validation', function(){ - if($.webshims.modules){ - getGroupElements = $.webshims.modules["form-core"].getGroupElements || getGroupElements; + webshims.ready('form-validation', function(){ + if(webshims.modules){ + getGroupElements = webshims.modules["form-core"].getGroupElements || getGroupElements; } }); - addCustomValidityRule('dependent', function(elem, val){ - - if( !elem.getAttribute('data-dependent-validation') ){return;} - - var data = $(elem).data('dependentValidation'); + addCustomValidityRule('dependent', function(elem, val, data){ + data = data.dependentValidation; + if( !data ){return;} var specialVal; - if(!data){return;} var depFn = function(e){ var val = $.prop(data.masterElement, data["from-prop"]); if(specialVal){ val = $.inArray(val, specialVal) !== -1; } @@ -863,11 +993,11 @@ if(data.prop !== "value" || specialVal){ $(data.masterElement.type === 'radio' && getGroupElements(data.masterElement) || data.masterElement).bind('change', depFn); } else { $(data.masterElement).bind('change', function(){ - $.webshims.refreshCustomValidityRules(elem); + webshims.refreshCustomValidityRules(elem); $(elem).getShadowElement().filter('.user-error, .user-success').trigger('refreshvalidityui'); }); } } @@ -877,8 +1007,123 @@ depFn(); return ''; } }, 'The value of this field does not repeat the value of the other field'); + + + if(window.JSON){ + addCustomValidityRule('ajaxvalidate', function(elem, val, data){ + if(!val || !data.ajaxvalidate){return;} + var opts; + if(!data.remoteValidate){ + if(typeof data.ajaxvalidate == 'string'){ + data.ajaxvalidate = {url: data.ajaxvalidate, depends: $([])}; + } else { + data.ajaxvalidate.depends = data.ajaxvalidate.depends ? $(data.ajaxvalidate.depends).map(getId) : $([]); + } + + data.ajaxvalidate.depends.on('refreshCustomValidityRules', function(){ + webshims.refreshCustomValidityRules(elem); + }); + + opts = data.ajaxvalidate; + + var remoteValidate = { + ajaxLoading: false, + restartAjax: false, + message: 'async', + cache: {}, + update: function(remoteData){ + if(this.ajaxLoading){ + this.restartAjax = remoteData; + } else { + this.restartAjax = false; + this.ajaxLoading = true; + $.ajax( + $.extend({}, opts, { + url: opts.url, + dataType: 'json', + depData: remoteData, + data: opts.fullForm ? + $(elem).jProp('form').serializeArray() : + remoteData, + success: this.getResponse, + complete: this._complete + }) + ); + } + }, + _complete: function(){ + remoteValidate.ajaxLoading = false; + if(remoteValidate.restartAjax){ + this.update(remoteValidate.restartAjax); + } + remoteValidate.restartAjax = false; + }, + getResponse: function(data){ + var old = webshims.refreshCustomValidityRules; + if(!data){ + data = {message: '', valid: true}; + } else if(typeof data == 'string'){ + data = JSON.parse(data); + } + + remoteValidate.message = ('message' in data) ? data.message : !data.valid; + remoteValidate.lastMessage = remoteValidate.message; + remoteValidate.blockUpdate = true; + $(elem).triggerHandler('refreshvalidityui'); + remoteValidate.message = 'async'; + remoteValidate.blockUpdate = false; + }, + getData: function(){ + var data; + data = {}; + data[$.prop(elem, 'name') || $.prop(elem, 'id')] = $(elem).val(); + opts.depends.each(function(){ + if($(this).is(':invalid')){ + data = false; + return false; + } + data[$.prop(this, 'name') || $.prop(this, 'id')] = $(this).val(); + }); + return data; + }, + getTempMessage: function(){ + var message = 'async'; + var remoteData, dataStr; + if(!data.remoteValidate.blockUpdate){ + remoteData = this.getData(); + if(!remoteData){ + message = ''; + } else { + try { + dataStr = JSON.stringify(remoteData); + } catch(er){} + + if(dataStr === this.lastString){ + message = this.ajaxLoading ? 'async' : this.lastMessage; + } else { + this.lastString = dataStr; + this.lastMessage = 'async'; + clearTimeout(data.remoteValidate.timer); + data.remoteValidate.timer = setTimeout(function(){ + data.remoteValidate.update(remoteData); + }, 9); + } + + } + } else { + message = remoteValidate.message; + } + return message; + } + }; + data.remoteValidate = remoteValidate; + } + + return data.remoteValidate.getTempMessage(); + }, 'remote error'); + } })(); });