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

- old
+ new

@@ -1,1129 +1,1821 @@ -webshims.register('form-validation', function($, webshims, window, document, undefined, options){ - 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; - }; - - var nonFormFilter = function(){ - return !$.prop(this, 'form'); - }; - var getGroupElements = function(elem){ - elem = $(elem); - var name; - var form; - var ret = emptyJ; - if(elem[0].type == 'radio'){ - form = elem.prop('form'); - name = elem[0].name; - if(!name){ - ret = elem; - } else if(form){ - ret = $(form).jProp(name); - } else { - ret = $(document.getElementsByName(name)).filter(nonFormFilter); - } - ret = ret.filter('[type="radio"]'); - } - return ret; - }; - - - var returnValidityCause = function(validity, elem){ - var ret; - $.each(validity, function(name, value){ - if(value){ - ret = name + $.prop(elem, 'validationMessage'); - return false; - } - }); - return ret; - }; - - var isInGroup = function(name){ - var ret; - try { - ret = document.activeElement.name === name; - } catch(e){} - return ret; - }; - //actually we could always use the change event, but chrome messed it up and does not respect the commit action definition of the html spec - //see: http://code.google.com/p/chromium/issues/detail?id=155747 - var changeTypes = { +webshims.register('form-shim-extend', function($, webshims, window, document, undefined, options){ +"use strict"; +webshims.inputTypes = webshims.inputTypes || {}; +//some helper-functions +var cfg = webshims.cfg.forms; +var bugs = webshims.bugs; +var isSubmit; + +var isNumber = function(string){ + return (typeof string == 'number' || (string && string == string * 1)); + }, + typeModels = webshims.inputTypes, + checkTypes = { radio: 1, - checkbox: 1, - 'select-one': 1, - 'select-multiple': 1, - file: 1, - date: 1, - month: 1, - week: 1, - text: 1 - }; - //see: http://code.google.com/p/chromium/issues/detail?id=179708 and bug above - var noFocusWidgets = { - time: 1, - date: 1, - month: 1, - datetime: 1, - week: 1, - 'datetime-local': 1 - }; - var switchValidityClass = function(e){ - if(!options.iVal.sel){return;} - var elem, timer, shadowElem, shadowType; - if(!e.target){return;} - elem = $(e.target).getNativeElement()[0]; - shadowElem = $(elem).getShadowElement(); - if(elem.type == 'submit' || !$.prop(elem, 'willValidate') || (e.type == 'change' && (shadowType = shadowElem.prop('type')) && !changeTypes[shadowType])){return;} - timer = $.data(elem, 'webshimsswitchvalidityclass'); - var switchClass = function(){ - if(!shadowType){ - shadowType = shadowElem.prop('type'); - } - if( - (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(validity.valid){ - if(!shadowElem.hasClass(validClass)){ - addClass = validClass; - removeClass = invalidClass; - generaltrigger = 'changedvaliditystate'; - trigger = 'changedvalid'; - if(checkTypes[elem.type] && elem.checked){ - getGroupElements(elem).not(elem).removeClass(removeClass).addClass(addClass).removeAttr('aria-invalid'); + checkbox: 1 + }, + getType = function(elem){ + return (elem.getAttribute('type') || elem.type || '').toLowerCase(); + } +; + +(function(){ + if('querySelector' in document){ + try { + bugs.findRequired = !($('<form action="#" style="width: 1px; height: 1px; overflow: hidden;"><select name="b" required="" /></form>')[0].querySelector('select:required')); + } catch(er){ + bugs.findRequired = false; + } + + if (bugs.bustedValidity || bugs.findRequired) { + (function(){ + var find = $.find; + var matchesSelector = $.find.matchesSelector; + + var regExp = /(\:valid|\:invalid|\:optional|\:required|\:in-range|\:out-of-range)(?=[\s\[\~\.\+\>\:\#*]|$)/ig; + var regFn = function(sel){ + return sel + '-element'; + }; + + $.find = (function(){ + var slice = Array.prototype.slice; + var fn = function(sel){ + var ar = arguments; + ar = slice.call(ar, 1, ar.length); + ar.unshift(sel.replace(regExp, regFn)); + return find.apply(this, ar); + }; + for (var i in find) { + if(find.hasOwnProperty(i)){ + fn[i] = find[i]; + } } - shadowElem.removeAttr('aria-invalid'); - $.removeData(elem, 'webshimsinvalidcause'); + return fn; + })(); + if(!Modernizr.prefixed || Modernizr.prefixed("matchesSelector", document.documentElement)){ + $.find.matchesSelector = function(node, expr){ + expr = expr.replace(regExp, regFn); + return matchesSelector.call(this, node, expr); + }; } - } else { - validityCause = returnValidityCause(validity, elem); - if($.data(elem, 'webshimsinvalidcause') != validityCause){ - $.data(elem, 'webshimsinvalidcause', validityCause); - generaltrigger = 'changedvaliditystate'; - } - if(!shadowElem.hasClass(invalidClass)){ - addClass = invalidClass; - removeClass = validClass; - if (checkTypes[elem.type] && !elem.checked) { - getGroupElements(elem).not(elem).removeClass(removeClass).addClass(addClass).attr('aria-invalid', 'true'); - } - shadowElem.attr('aria-invalid', 'true'); - trigger = 'changedinvalid'; - } - } - - if(addClass){ - shadowElem.addClass(addClass).removeClass(removeClass); - //jQuery 1.6.1 IE9 bug (doubble trigger bug) - setTimeout(function(){ - $(elem).trigger(trigger); - }, 0); - } - if(generaltrigger){ - setTimeout(function(){ - $(elem).trigger(generaltrigger); - }, 0); - } - - $.removeData(elem, 'webshimsswitchvalidityclass'); - }; - - if(timer){ - clearTimeout(timer); + + })(); } - if(e.type == 'refreshvalidityui'){ - switchClass(); + } +})(); + +//API to add new input types +webshims.addInputType = function(type, obj){ + typeModels[type] = obj; +}; + +//contsrain-validation-api +var validityPrototype = { + customError: false, + + typeMismatch: false, + badInput: false, + rangeUnderflow: false, + rangeOverflow: false, + stepMismatch: false, + tooLong: false, + patternMismatch: false, + valueMissing: false, + + valid: true +}; + +var isPlaceholderOptionSelected = function(select){ + if(select.type == 'select-one' && select.size < 2){ + var option = $('> option:first-child', select); + return !!option.prop('selected'); + } + return false; +}; + +var emptyJ = $([]); +var getGroupElements = function(elem){ + elem = $(elem); + var name; + var form; + var ret = emptyJ; + if(elem[0].type == 'radio'){ + form = elem.prop('form'); + name = elem[0].name; + if(!name){ + ret = elem; + } else if(form){ + ret = $(form[name]); } else { - $.data(elem, 'webshimsswitchvalidityclass', setTimeout(switchClass, 9)); + ret = $(document.getElementsByName(name)).filter(function(){ + return !$.prop(this, 'form'); + }); } - }; - - $(document.body || 'html') - .on(options.validityUIEvents || 'focusout change refreshvalidityui invalid', switchValidityClass) - .on('reset resetvalui', function(e){ - var elems = $(e.target); - if(e.type == 'reset'){ - elems = elems.filter('form').jProp('elements'); + ret = ret.filter('[type="radio"]'); + } + return ret; +}; +var validityRules = { + valueMissing: function(input, val, cache){ + if(!input.prop('required')){return false;} + var ret = false; + if(!('type' in cache)){ + cache.type = getType(input[0]); } - elems - .filter('.user-error, .user-success') - .removeAttr('aria-invalid') - .removeClass('user-error') - .removeClass('user-success') - .getNativeElement() - .each(function(){ - $.removeData(this, 'webshimsinvalidcause'); - }) - .trigger('resetvalidityui') - ; - }) - ; - - var setRoot = function(){ - webshims.scrollRoot = (isWebkit || document.compatMode == 'BackCompat') ? - $(document.body) : - $(document.documentElement) - ; - }; - var minWidth = (Modernizr.boxSizing || Modernizr['display-table'] || $.support.getSetAttribute || $.support.boxSizing) ? - 'minWidth' : - 'width' - ; - var hasTransition = ('transitionDelay' in document.documentElement.style); - var resetPos = {display: 'inline-block', left: 0, top: 0, marginTop: 0, marginLeft: 0, marginRight: 0, marginBottom: 0}; - - setRoot(); - webshims.ready('DOM', setRoot); - - - webshims.getRelOffset = function(posElem, relElem, opts){ - var offset, bodyOffset, dirs; - posElem = $(posElem); - $.swap($(posElem)[0], resetPos, function(){ - if($.position && opts && $.position.getScrollInfo){ - if(!opts.of){ - opts.of = relElem; - } - - opts.using = function(calced, data){ - posElem.attr({'data-horizontal': data.horizontal, 'data-vertical': data.vertical}); - offset = calced; - }; - posElem.attr({ - 'data-horizontal': '', - 'data-vertical': '', - 'data-my': opts.my || 'center', - 'data-at': opts.at || 'center' - }); - posElem.position(opts); - + if(cache.nodeName == 'select'){ + ret = (!val && (input[0].selectedIndex < 0 || isPlaceholderOptionSelected(input[0]) )); + } else if(checkTypes[cache.type]){ + ret = (cache.type == 'checkbox') ? !input.is(':checked') : !getGroupElements(input).filter(':checked')[0]; } else { - offset = $(relElem).offset(); - bodyOffset = posElem.offset(); - offset.top -= bodyOffset.top; - offset.left -= bodyOffset.left; - - offset.top += relElem.outerHeight(); + ret = !(val); } - - }); + return ret; + }, + tooLong: function(){ + return false; + }, + patternMismatch: function(input, val, cache) { + if(val === '' || cache.nodeName == 'select'){return false;} + var pattern = input.attr('pattern'); + if(!pattern){return false;} + try { + pattern = new RegExp('^(?:' + pattern + ')$'); + } catch(er){ + webshims.error('invalid pattern value: "'+ pattern +'" | '+ er); + pattern = false; + } + if(!pattern){return false;} + return !(pattern.test(val)); + } + } +; + +$.each({typeMismatch: 'mismatch', badInput: 'bad'}, function(name, fn){ + validityRules[name] = function (input, val, cache){ + if(val === '' || cache.nodeName == 'select'){return false;} + var ret = false; + if(!('type' in cache)){ + cache.type = getType(input[0]); + } - return offset; + if(typeModels[cache.type] && typeModels[cache.type][fn]){ + ret = typeModels[cache.type][fn](val, input); + } else if('validity' in input[0] && ('name' in input[0].validity)){ + ret = input[0].validity[name] || false; + } + return ret; }; - - $.extend(webshims.wsPopover, { +}); + +webshims.addValidityRule = function(type, fn){ + validityRules[type] = fn; +}; + +$.event.special.invalid = { + add: function(){ + $.event.special.invalid.setup.call(this.form || this); + }, + setup: function(){ + var form = this.form || this; + if( $.data(form, 'invalidEventShim') ){ + form = null; + return; + } + $(form) + .data('invalidEventShim', true) + .on('submit', $.event.special.invalid.handler) + ; + webshims.moveToFirstEvent(form, 'submit'); + if(webshims.bugs.bustedValidity && $.nodeName(form, 'form')){ + (function(){ + var noValidate = form.getAttribute('novalidate'); + form.setAttribute('novalidate', 'novalidate'); + webshims.data(form, 'bustedNoValidate', (noValidate == null) ? null : noValidate); + })(); + } + form = null; + }, + teardown: $.noop, + handler: function(e, d){ + if( e.type != 'submit' || e.testedValidity || !e.originalEvent || !$.nodeName(e.target, 'form') || $.prop(e.target, 'noValidate') ){return;} - isInElement: function(container, contained){ - return container == contained || $.contains(container, contained); - }, - show: function(element){ - if(this.isVisible){return;} - var e = $.Event('wspopoverbeforeshow'); - this.element.trigger(e); - if(e.isDefaultPrevented()){return;} - this.isVisible = true; - element = $(element || this.options.prepareFor).getNativeElement() ; + isSubmit = true; + e.testedValidity = true; + var notValid = !($(e.target).checkValidity()); + if(notValid){ + e.stopImmediatePropagation(); + isSubmit = false; + return false; + } + isSubmit = false; + } +}; + +$.event.special.submit = $.event.special.submit || {setup: function(){return false;}}; +var submitSetup = $.event.special.submit.setup; +$.extend($.event.special.submit, { + setup: function(){ + if($.nodeName(this, 'form')){ + $(this).on('invalid', $.noop); + } else { + $('form', this).on('invalid', $.noop); + } + return submitSetup.apply(this, arguments); + } +}); +webshims.ready('form-shim-extend2 WINDOWLOAD', function(){ + $(window).on('invalid', $.noop); +}); + + +webshims.addInputType('email', { + mismatch: (function(){ + //taken from http://www.whatwg.org/specs/web-apps/current-work/multipage/states-of-the-type-attribute.html#valid-e-mail-address + var test = cfg.emailReg || /^[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])?)*$/; + var splitReg = /\s*,\s*/g; + return function(val, input){ + var ret = false; + val = $(input).prop('multiple') ? val.split(splitReg) : [val]; - var that = this; - var visual = $(element).getShadowElement(); - var delayedRepos = function(e){ - clearTimeout(that.timers.repos); - that.timers.repos = setTimeout(function(){ - that.position(visual); - }, e && e.type == 'pospopover' ? 4 : 200); - }; + for(var i = 0; i < val.length; i++){ + if(!test.test(val[i])){ + ret = true; + break; + } + } + return ret; + }; + })() +}); - this.clear(); - this.element.removeClass('ws-po-visible').css('display', 'none'); +webshims.addInputType('url', { + mismatch: (function(){ + //taken from scott gonzales + var test = cfg.urlReg || /^([a-z]([a-z]|\d|\+|-|\.)*):(\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?((\[(|(v[\da-f]{1,}\.(([a-z]|\d|-|\.|_|~)|[!\$&'\(\)\*\+,;=]|:)+))\])|((\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])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=])*)(:\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})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)|((([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})|[!\$&'\(\)\*\+,;=]|:|@)){0})(\?((([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; + return function(val){ + return !test.test(val); + }; + })() +}); + +webshims.defineNodeNameProperty('input', 'type', { + prop: { + get: function(){ + var elem = this; + var type = (elem.getAttribute('type') || '').toLowerCase(); + return (webshims.inputTypes[type]) ? type : elem.type; + } + } +}); + +// IDLs for constrain validation API +//ToDo: add object to this list +webshims.defineNodeNamesProperties(['button', 'fieldset', 'output'], { + checkValidity: { + value: function(){return true;} + }, + willValidate: { + value: false + }, + setCustomValidity: { + value: $.noop + }, + validity: { + writeable: false, + get: function(){ + return $.extend({}, validityPrototype); + } + } +}, 'prop'); + +var baseCheckValidity = function(elem){ + var e, + v = $.prop(elem, 'validity') + ; + if(v){ + $.data(elem, 'cachedValidity', v); + } else { + return true; + } + if( !v.valid ){ + e = $.Event('invalid'); + var jElm = $(elem).trigger(e); + if(isSubmit && !baseCheckValidity.unhandledInvalids && !e.isDefaultPrevented()){ + webshims.validityAlert.showFor(jElm); + baseCheckValidity.unhandledInvalids = true; + } + } + $.removeData(elem, 'cachedValidity'); + return v.valid; +}; +var rsubmittable = /^(?:select|textarea|input)/i; +webshims.defineNodeNameProperty('form', 'checkValidity', { + prop: { + value: function(){ - this.prepareFor(element, visual); + var ret = true, + elems = $($.prop(this, 'elements')).filter(function(){ + if(!rsubmittable.test(this.nodeName)){return false;} + var shadowData = webshims.data(this, 'shadowData'); + return !shadowData || !shadowData.nativeElement || shadowData.nativeElement === this; + }) + ; - this.position(visual); - that.timers.show = setTimeout(function(){ - that.element.css('display', ''); - that.timers.show = setTimeout(function(){ - that.element.addClass('ws-po-visible').trigger('wspopovershow'); - }, 9); - }, 9); - - $(document).on('focusin'+this.eventns+' mousedown'+this.eventns, function(e){ - if(that.options.hideOnBlur && !that.stopBlur && !that.isInElement(that.lastElement[0] || document.body, e.target) && !that.isInElement(element[0] || document.body, e.target) && !that.isInElement(that.element[0], e.target)){ - that.hide(); + baseCheckValidity.unhandledInvalids = false; + for(var i = 0, len = elems.length; i < len; i++){ + if( !baseCheckValidity(elems[i]) ){ + ret = false; } - }); - - this.element.off('pospopover').on('pospopover', delayedRepos); - $(window).on('resize'+this.eventns + ' pospopover'+this.eventns, delayedRepos); - }, - prepareFor: function(element, visual){ - var onBlur; - var that = this; - var css = {}; - var opts = $.extend(true, {}, this.options, $(element.prop('form') || []).data('wspopover') || {}, element.data('wspopover')); - this.lastOpts = opts; - this.lastElement = $(element).getShadowFocusElement(); - if(!this.prepared || !this.options.prepareFor){ - if(opts.appendTo == 'element'){ - this.element.insertAfter(element); - } else { - this.element.appendTo(opts.appendTo); + } + return ret; + } + } +}); + +webshims.defineNodeNamesProperties(['input', 'textarea', 'select'], { + checkValidity: { + value: function(){ + baseCheckValidity.unhandledInvalids = false; + return baseCheckValidity($(this).getNativeElement()[0]); + } + }, + setCustomValidity: { + value: function(error){ + $.removeData(this, 'cachedValidity'); + webshims.data(this, 'customvalidationMessage', ''+error); + } + }, + willValidate: { + writeable: false, + get: (function(){ + var types = { + button: 1, + reset: 1, + hidden: 1, + image: 1 } + ; + return function(){ + var elem = $(this).getNativeElement()[0]; + //elem.name && <- we don't use to make it easier for developers + return !!(!elem.disabled && !elem.readOnly && !types[elem.type] ); + }; + })() + }, + validity: { + writeable: false, + get: function(){ + var jElm = $(this).getNativeElement(); + var elem = jElm[0]; + var validityState = $.data(elem, 'cachedValidity'); + if(validityState){ + return validityState; } + validityState = $.extend({}, validityPrototype); - this.element.attr({ - 'data-class': element.prop('className'), - 'data-id': element.prop('id') - }); - - css[minWidth] = opts.constrainWidth ? visual.outerWidth() : ''; - - this.element.css(css); - - if(opts.hideOnBlur){ - onBlur = function(e){ - if(that.stopBlur){ - e.stopImmediatePropagation(); - } else { - that.hide(); - } - }; - - that.timers.bindBlur = setTimeout(function(){ - that.lastElement.off(that.eventns).on('focusout'+that.eventns + ' blur'+that.eventns, onBlur); - that.lastElement.getNativeElement().off(that.eventns); - }, 10); - - + if( !$.prop(elem, 'willValidate') || elem.type == 'submit' ){ + return validityState; } + var val = jElm.val(), + cache = {nodeName: elem.nodeName.toLowerCase()} + ; - if(!this.prepared && $.fn.bgIframe){ - this.element.bgIframe(); + validityState.customError = !!(webshims.data(elem, 'customvalidationMessage')); + if( validityState.customError ){ + validityState.valid = false; } - this.prepared = true; - }, - clear: function(){ - $(window).off(this.eventns); - $(document).off(this.eventns); - this.element.off('transitionend'+this.eventns); - this.stopBlur = false; - this.lastOpts = false; - $.each(this.timers, function(timerName, val){ - clearTimeout(val); - }); - }, - hide: function(){ - var e = $.Event('wspopoverbeforehide'); - this.element.trigger(e); - if(e.isDefaultPrevented() || !this.isVisible){return;} - this.isVisible = false; - var that = this; - var forceHide = function(e){ - if(!(e && e.type == 'transitionend' && (e = e.originalEvent) && e.target == that.element[0] && that.element.css('visibility') == 'hidden')){ - that.element.off('transitionend'+that.eventns).css('display', 'none').attr({'data-id': '', 'data-class': '', 'hidden': 'hidden'}); - clearTimeout(that.timers.forcehide); - $(window).off('resize'+that.eventns); + + $.each(validityRules, function(rule, fn){ + if (fn(jElm, val, cache)) { + validityState[rule] = true; + validityState.valid = false; } - }; - this.clear(); - this.element.removeClass('ws-po-visible').trigger('wspopoverhide'); - $(window).on('resize'+this.eventns, forceHide); - if(hasTransition){ - this.element.off('transitionend'+this.eventns).on('transitionend'+this.eventns, forceHide); + }); + $(this).getShadowFocusElement().attr('aria-invalid', validityState.valid ? 'false' : 'true'); + jElm = null; + elem = null; + return validityState; + } + } +}, 'prop'); + +webshims.defineNodeNamesBooleanProperty(['input', 'textarea', 'select'], 'required', { + set: function(value){ + $(this).getShadowFocusElement().attr('aria-required', !!(value)+''); + }, + initAttr: Modernizr.localstorage //only if we have aria-support +}); +webshims.defineNodeNamesBooleanProperty(['input'], 'multiple'); + +if(webshims.bugs.bustedValidity){ + + webshims.defineNodeNameProperty('form', 'novalidate', { + attr: { + set: function(val){ + webshims.data(this, 'bustedNoValidate', ''+val); + }, + get: function(){ + var ret = webshims.data(this, 'bustedNoValidate'); + return ret == null ? undefined : ret; } - - that.timers.forcehide = setTimeout(forceHide, hasTransition ? 600 : 40); }, - position: function(element){ - var offset = webshims.getRelOffset(this.element.removeAttr('hidden'), element, (this.lastOpts || this.options).position); - - this.element.css(offset); + removeAttr: { + value: function(){ + webshims.data(this, 'bustedNoValidate', null); + } } }); + $.each(['rangeUnderflow', 'rangeOverflow', 'stepMismatch'], function(i, name){ + validityRules[name] = function(elem){ + return (elem[0].validity || {})[name] || false; + }; + }); - - /* some extra validation UI */ - webshims.validityAlert = (function(){ +} + +webshims.defineNodeNameProperty('form', 'noValidate', { + prop: { + set: function(val){ + val = !!val; + if(val){ + $.attr(this, 'novalidate', 'novalidate'); + } else { + $(this).removeAttr('novalidate'); + } + }, + get: function(){ + return $.attr(this, 'novalidate') != null; + } + } +}); + +if(Modernizr.inputtypes.date && /webkit/i.test(navigator.userAgent)){ + (function(){ - options.messagePopover.position = $.extend({}, { - at: 'left bottom', - my: 'left top', - collision: 'none' - }, options.messagePopover.position || {}); - - var focusTimer = false; - - var api = webshims.objectCreate(webshims.wsPopover, {}, options.messagePopover); - var boundHide = api.hide.bind(api); - - api.element.addClass('validity-alert').attr({role: 'alert'}); - $.extend(api, { - hideDelay: 5000, - showFor: function(elem, message, noFocusElem, noBubble){ - - elem = $(elem).getNativeElement(); - this.clear(); - this.hide(); - if(!noBubble){ - this.getMessage(elem, message); - - this.show(elem); - if(this.hideDelay){ - this.timers.delayedHide = setTimeout(boundHide, this.hideDelay); - } - - } - - if(!noFocusElem){ - this.setFocus(elem); - } + var noInputTriggerEvts = {updateInput: 1, input: 1}, + fixInputTypes = { + date: 1, + time: 1, + month: 1, + week: 1, + "datetime-local": 1 }, - setFocus: function(element){ - var focusElem = $(element).getShadowFocusElement(); - var scrollTop = webshims.scrollRoot.scrollTop(); - var elemTop = focusElem.offset().top - 30; - var focus = function(){ - try { - focusElem[0].focus(); - } catch(e){} - $(window).triggerHandler('pospopover'+this.eventns); - }; - - if(scrollTop > elemTop){ - webshims.scrollRoot.animate( - {scrollTop: elemTop - 5}, - { - queue: false, - duration: Math.max( Math.min( 600, (scrollTop - elemTop) * 1.5 ), 80 ), - complete: focus + noFocusEvents = { + focusout: 1, + blur: 1 + }, + changeEvts = { + updateInput: 1, + change: 1 + }, + observe = function(input){ + var timer, + focusedin = true, + lastInputVal = input.prop('value'), + lastChangeVal = lastInputVal, + trigger = function(e){ + //input === null + if(!input){return;} + var newVal = input.prop('value'); + + if(newVal !== lastInputVal){ + lastInputVal = newVal; + if(!e || !noInputTriggerEvts[e.type]){ + input.trigger('input'); + } } - ); - - } else { - focus(); - } + if(e && changeEvts[e.type]){ + lastChangeVal = newVal; + } + if(!focusedin && newVal !== lastChangeVal){ + input.trigger('change'); + } + }, + extraTimer, + extraTest = function(){ + clearTimeout(extraTimer); + extraTimer = setTimeout(trigger, 9); + }, + unbind = function(e){ + clearInterval(timer); + setTimeout(function(){ + if(e && noFocusEvents[e.type]){ + focusedin = false; + } + if(input){ + input.unbind('focusout blur', unbind).unbind('input change updateInput', trigger); + trigger(); + } + input = null; + }, 1); + + } + ; - }, - getMessage: function(elem, message){ - if (!message) { - message = elem.getErrorMessage(); - } - if (message) { - api.contentElement.text(message); - } else { - this.hide(); - } + clearInterval(timer); + timer = setInterval(trigger, 160); + extraTest(); + input + .off({ + 'focusout blur': unbind, + 'input change updateInput': trigger + }) + .on({ + 'focusout blur': unbind, + 'input updateInput change': trigger + }) + ; } - }); + ; - return api; + $(document) + .on('focusin', function(e){ + if( e.target && fixInputTypes[e.target.type] && !e.target.readOnly && !e.target.disabled ){ + observe($(e.target)); + } + }) + ; + + })(); +} + +webshims.addReady(function(context, contextElem){ + //start constrain-validation + var focusElem; + $('form', context) + .add(contextElem.filter('form')) + .bind('invalid', $.noop) + ; - var fx = { - slide: { - show: 'slideDown', - hide: 'slideUp' - }, - fade: { - show: 'fadeIn', - hide: 'fadeOut' + try { + if(context == document && !('form' in (document.activeElement || {}))) { + focusElem = $('input[autofocus], select[autofocus], textarea[autofocus]', context).eq(0).getShadowFocusElement()[0]; + if (focusElem && focusElem.offsetHeight && focusElem.offsetWidth) { + focusElem.focus(); + } } - }; - if(!fx[options.iVal.fx]){ - options.iVal.fx = 'slide'; - } - webshims.errorbox = { - create: function(elem, fieldWrapper){ - if(!fieldWrapper){ - fieldWrapper = this.getFieldWrapper(elem); + } + catch (er) {} + +}); + +if(!Modernizr.input.list){ + webshims.defineNodeNameProperty('datalist', 'options', { + prop: { + writeable: false, + get: function(){ + var elem = this; + var select = $('select', elem); + var options; + if(select[0]){ + options = $.makeArray(select[0].options || []); + } else { + options = $('option', elem).get(); + if(options.length){ + webshims.warn('you should wrap your option-elements for a datalist in a select element to support IE and other old browsers.'); + } + } + return options; } - var errorBox = $('div.'+errorBoxClass, fieldWrapper); + } + }); +} + + + +var submitterTypes = {submit: 1, button: 1, image: 1}; +var formSubmitterDescriptors = {}; +[ + { + name: "enctype", + limitedTo: { + "application/x-www-form-urlencoded": 1, + "multipart/form-data": 1, + "text/plain": 1 + }, + defaultProp: "application/x-www-form-urlencoded", + proptype: "enum" + }, + { + name: "method", + limitedTo: { + "get": 1, + "post": 1 + }, + defaultProp: "get", + proptype: "enum" + }, + { + name: "action", + proptype: "url" + }, + { + name: "target" + }, + { + name: "novalidate", + propName: "noValidate", + proptype: "boolean" + } +].forEach(function(desc){ + var propName = 'form'+ (desc.propName || desc.name).replace(/^[a-z]/, function(f){ + return f.toUpperCase(); + }); + var attrName = 'form'+ desc.name; + var formName = desc.name; + var eventName = 'click.webshimssubmittermutate'+formName; + + var changeSubmitter = function(){ + var elem = this; + if( !('form' in elem) || !submitterTypes[elem.type] ){return;} + var form = $.prop(elem, 'form'); + if(!form){return;} + var attr = $.attr(elem, attrName); + if(attr != null && ( !desc.limitedTo || attr.toLowerCase() === $.prop(elem, propName))){ - if(!errorBox.length){ - errorBox = $('<div class="'+ errorBoxClass +'" hidden="hidden">'); - fieldWrapper.append(errorBox); - } + var oldAttr = $.attr(form, formName); - fieldWrapper.data('errorbox', errorBox); - return errorBox; - }, - getFieldWrapper: function(elem){ - var fieldWrapper; - if(options.iVal.fieldWrapper){ - fieldWrapper = (typeof options.iVal.fieldWrapper == "function") ? options.iVal.fieldWrapper.apply(this, arguments) : $(elem).parent().closest(options.iVal.fieldWrapper); - if(!fieldWrapper.length){ - fieldWrapper = false; - webshims.error("could not find fieldwrapper: "+ options.iVal.fieldWrapper); + $.attr(form, formName, attr); + setTimeout(function(){ + if(oldAttr != null){ + $.attr(form, formName, oldAttr); + } else { + try { + $(form).removeAttr(formName); + } catch(er){ + form.removeAttribute(formName); + } } - } - 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; + }, 9); + } + }; + + + +switch(desc.proptype) { + case "url": + var urlForm = document.createElement('form'); + formSubmitterDescriptors[propName] = { + prop: { + set: function(value){ + $.attr(this, attrName, value); + }, + get: function(){ + var value = $.attr(this, attrName); + if(value == null){return '';} + urlForm.setAttribute('action', value); + return urlForm.action; + } + } + }; + break; + case "boolean": + formSubmitterDescriptors[propName] = { + prop: { + set: function(val){ + val = !!val; + if(val){ + $.attr(this, 'formnovalidate', 'formnovalidate'); + } else { + $(this).removeAttr('formnovalidate'); } - }); + }, + get: function(){ + return $.attr(this, 'formnovalidate') != null; + } } - 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 +'"]'; + break; + case "enum": + formSubmitterDescriptors[propName] = { + prop: { + set: function(value){ + $.attr(this, attrName, value); + }, + get: function(){ + var value = $.attr(this, attrName); + return (!value || ( (value = value.toLowerCase()) && !desc.limitedTo[value] )) ? desc.defaultProp : value; } - }); - }); - 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(); + }; + break; + default: + formSubmitterDescriptors[propName] = { + prop: { + set: function(value){ + $.attr(this, attrName, value); + }, + get: function(){ + var value = $.attr(this, attrName); + return (value != null) ? value : ""; } - }); - 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; + } + + + if(!formSubmitterDescriptors[attrName]){ + formSubmitterDescriptors[attrName] = {}; + } + formSubmitterDescriptors[attrName].attr = { + set: function(value){ + formSubmitterDescriptors[attrName].attr._supset.call(this, value); + $(this).unbind(eventName).on(eventName, changeSubmitter); }, - 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'](successWrapperClass); - }; - var evt = changeTypes[type] ? 'change' : 'blur'; + get: function(){ + return formSubmitterDescriptors[attrName].attr._supget.call(this); + } + }; + formSubmitterDescriptors[attrName].initAttr = true; + formSubmitterDescriptors[attrName].removeAttr = { + value: function(){ + $(this).unbind(eventName); + formSubmitterDescriptors[attrName].removeAttr._supvalue.call(this); + } + }; +}); + +webshims.defineNodeNamesProperties(['input', 'button'], formSubmitterDescriptors); + +}); //webshims.ready end + +webshims.register('form-shim-extend2', function($, webshims, window, document, undefined, options){ +"use strict"; +var emptyJ = $([]); +var getGroupElements = function(elem){ + elem = $(elem); + var name; + var form; + var ret = emptyJ; + if(elem[0].type == 'radio'){ + form = elem.prop('form'); + name = elem[0].name; + if(!name){ + ret = elem; + } else if(form){ + ret = $(form[name]); + } else { + ret = $(document.getElementsByName(name)).filter(function(){ + return !$.prop(this, 'form'); + }); + } + ret = ret.filter('[type="radio"]'); + } + return ret; +}; +//submitbubbles for IE6-IE8 +var supportSubmitBubbles = !('submitBubbles' in $.support) || $.support.submitBubbles; +var addSubmitBubbles = function(form){ + if (!supportSubmitBubbles && form && typeof form == 'object' && !form._submit_attached ) { + + $.event.add( form, 'submit._submit', function( event ) { + event._submit_bubble = true; + }); + + form._submit_attached = true; + } +}; +if(!supportSubmitBubbles && $.event.special.submit){ + + $.event.special.submit.setup = function() { + // Only need this for delegated form submit events + if ( $.nodeName( this, "form" ) ) { + return false; + } + + // Lazy-add a submit handler when a descendant form may potentially be submitted + $.event.add( this, "click._submit keypress._submit", function( e ) { + // Node name check avoids a VML-related crash in IE (#9807) + var elem = e.target, + form = $.nodeName( elem, 'input' ) || $.nodeName( elem, 'button' ) ? $.prop(elem, 'form') : undefined; + addSubmitBubbles(form); - $(elem).off('.recheckvalid').on(evt+'.recheckinvalid', check); - check(); - }, - hideError: function(elem, reset){ - var fieldWrapper = this.getFieldWrapper(elem); - var errorBox = fieldWrapper.hasClass(invalidWrapperClass) ? this.get(elem, fieldWrapper) : fieldWrapper.data('errorbox'); - - if(errorBox && errorBox.jquery){ - fieldWrapper.removeClass(invalidWrapperClass); - errorBox.message = ''; - $(elem).filter('input').off('.recheckinvalid'); - errorBox[fx[options.iVal.fx].hide](function(){ - $(this).attr({hidden: 'hidden'}); + }); + // return undefined since we don't need an event listener + }; +} + + + +webshims.defineNodeNamesBooleanProperty(['input', 'textarea', 'select'], 'required', { + set: function(value){ + $(this).getShadowFocusElement().attr('aria-required', !!(value)+''); + }, + initAttr: Modernizr.localstorage //only if we have aria-support +}); + +webshims.reflectProperties(['input'], ['pattern']); + + +if( !('maxLength' in document.createElement('textarea')) ){ + var constrainMaxLength = (function(){ + var timer; + var curLength = 0; + var lastElement = $([]); + var max = 1e9; + var constrainLength = function(){ + var nowValue = lastElement.prop('value'); + var nowLen = nowValue.length; + if(nowLen > curLength && nowLen > max){ + nowLen = Math.max(curLength, max); + lastElement.prop('value', nowValue.substr(0, nowLen )); + } + curLength = nowLen; + }; + var remove = function(){ + clearTimeout(timer); + lastElement.unbind('.maxlengthconstraint'); + }; + return function(element, maxLength){ + remove(); + if(maxLength > -1){ + max = maxLength; + curLength = $.prop(element, 'value').length; + lastElement = $(element); + lastElement.on({ + 'keydown.maxlengthconstraint keypress.maxlengthconstraint paste.maxlengthconstraint cut.maxlengthconstraint': function(e){ + setTimeout(constrainLength, 0); + }, + 'keyup.maxlengthconstraint': constrainLength, + 'blur.maxlengthconstraint': remove }); + timer = setInterval(constrainLength, 200); } - if(!reset){ - this.addSuccess(elem, fieldWrapper); + }; + })(); + + constrainMaxLength.update = function(element, maxLength){ + if($(element).is(':focus')){ + if(!maxLength){ + maxLength = $.prop(element, 'maxlength'); } - return fieldWrapper; - }, - recheckInvalidInput: function(input){ - 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); - }) - .on('focusout.recheckinvalid', function(){ - clearTimeout(timer); - }) - ; + constrainMaxLength(element, maxLength); + } + }; + + $(document).on('focusin', function(e){ + var maxLength; + if(e.target.nodeName == "TEXTAREA" && (maxLength = $.prop(e.target, 'maxlength')) > -1){ + constrainMaxLength(e.target, maxLength); + } + }); + + webshims.defineNodeNameProperty('textarea', 'maxlength', { + attr: { + set: function(val){ + this.setAttribute('maxlength', ''+val); + constrainMaxLength.update(this); + }, + get: function(){ + var ret = this.getAttribute('maxlength'); + return ret == null ? undefined : ret; } }, - 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(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]() - ; + prop: { + set: function(val){ + if(isNumber(val)){ + if(val < 0){ + throw('INDEX_SIZE_ERR'); + } + val = parseInt(val, 10); + this.setAttribute('maxlength', val); + constrainMaxLength.update(this, val); + return; } + this.setAttribute('maxlength', '0'); + constrainMaxLength.update(this, 0); + }, + get: function(){ + var val = this.getAttribute('maxlength'); + return (isNumber(val) && val >= 0) ? parseInt(val, 10) : -1; + } - fieldWrapper.removeClass(successWrapperClass); - $(elem).off('.recheckvalid'); - - return fieldWrapper; - }, - reset: function(elem){ - this.hideError(elem, true).removeClass(successWrapperClass); - }, - toggle: function(elem){ - if($(elem).is(':invalid')){ - this.showError(elem); - } else { - this.hideError(elem); + } + }); + webshims.defineNodeNameProperty('textarea', 'maxLength', { + prop: { + set: function(val){ + $.prop(this, 'maxlength', val); + }, + get: function(){ + return $.prop(this, 'maxlength'); } } - }; - - $(document.body) - .on({ - 'changedvaliditystate': function(e){ - if(options.iVal.sel){ - var form = $(e.target).jProp('form'); - if(form.is(options.iVal.sel)){ - webshims.errorbox.toggle(e.target); - } - } + }); +} + +if(!$.support.getSetAttribute && $('<form novalidate></form>').attr('novalidate') == null){ + webshims.defineNodeNameProperty('form', 'novalidate', { + attr: { + set: function(val){ + this.setAttribute('novalidate', ''+val); }, - resetvalidityui: function(e){ - if (options.iVal.sel) { - var form = $(e.target).jProp('form'); - if (form.is(options.iVal.sel)) { - webshims.errorbox.reset(e.target); + get: function(){ + var ret = this.getAttribute('novalidate'); + return ret == null ? undefined : ret; + } + } + }); +} + + +if(Modernizr.formattribute === false || !Modernizr.fieldsetdisabled){ + (function(){ + + (function(prop, undefined){ + $.prop = function(elem, name, value){ + var ret; + if(elem && elem.nodeType == 1 && value === undefined && $.nodeName(elem, 'form') && elem.id){ + ret = document.getElementsByName(name); + if(!ret || !ret.length){ + ret = document.getElementById(name); } - } - }, - firstinvalid: function(e){ - if(options.iVal.sel && options.iVal.handleBubble){ - var form = $(e.target).jProp('form'); - if(form.is(options.iVal.sel)){ - e.preventDefault(); - if(options.iVal.handleBubble != 'none'){ - webshims.validityAlert.showFor( e.target, false, false, options.iVal.handleBubble == 'hide' ); + if(ret){ + ret = $(ret).filter(function(){ + return $.prop(this, 'form') == elem; + }).get(); + if(ret.length){ + return ret.length == 1 ? ret[0] : ret; } } } - }, - submit: function(e){ - if(options.iVal.sel && !options.iVal.noSubmitCheck &&$(e.target).is(options.iVal.sel) && $.prop(e.target, 'noValidate') && !$(e.target).checkValidity()){ - e.stopImmediatePropagation(); - return false; - } + return prop.apply(this, arguments); + }; + })($.prop, undefined); + + var removeAddedElements = function(form){ + var elements = $.data(form, 'webshimsAddedElements'); + if(elements){ + elements.remove(); + $.removeData(form, 'webshimsAddedElements'); } - }) - ; - - 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 ); + }; + + + if(!Modernizr.formattribute){ + webshims.defineNodeNamesProperty(['input', 'textarea', 'select', 'button', 'fieldset'], 'form', { + prop: { + get: function(){ + var form = webshims.contentAttr(this, 'form'); + if(form){ + form = document.getElementById(form); + if(form && !$.nodeName(form, 'form')){ + form = null; + } + } + return form || this.form; + }, + writeable: false } }); - }); - } - - /* 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); - } + webshims.defineNodeNamesProperty(['form'], 'elements', { + prop: { + get: function(){ + var id = this.id; + var elements = $.makeArray(this.elements); + if(id){ + elements = $(elements).add('input[form="'+ id +'"], select[form="'+ id +'"], textarea[form="'+ id +'"], button[form="'+ id +'"], fieldset[form="'+ id +'"]').not('.webshims-visual-hide > *').get(); + } + return elements; + }, + writeable: false + } + }); + + + + $(function(){ + var stopPropagation = function(e){ + e.stopPropagation(); + }; + var submitters = { + image: 1, + submit: 1 + }; + $(document).on('submit', function(e){ + + if(!e.isDefaultPrevented()){ + var form = e.target; + var id = form.id; + var elements; + + + if(id){ + removeAddedElements(form); + + elements = $('input[form="'+ id +'"], select[form="'+ id +'"], textarea[form="'+ id +'"]') + .filter(function(){ + return !this.disabled && this.name && this.form != form; + }) + .clone() + ; + if(elements.length){ + $.data(form, 'webshimsAddedElements', $('<div class="webshims-visual-hide" />').append(elements).appendTo(form)); + setTimeout(function(){ + removeAddedElements(form); + }, 9); + } + elements = null; + } + } + }); + + + $(document).on('click', function(e){ + if(submitters[e.target.type] && !e.isDefaultPrevented() && $(e.target).is('input[form], button[form]')){ + var trueForm = $.prop(e.target, 'form'); + var formIn = e.target.form; + var clone; + if(trueForm && trueForm != formIn){ + clone = $(e.target) + .clone() + .removeAttr('form') + .addClass('webshims-visual-hide') + .on('click', stopPropagation) + .appendTo(trueForm) + ; + if(formIn){ + e.preventDefault(); + } + addSubmitBubbles(trueForm); + clone.trigger('click'); + setTimeout(function(){ + clone.remove(); + clone = null; + }, 9); + } + } + }); + }); + } + + if(!Modernizr.fieldsetdisabled){ + webshims.defineNodeNamesProperty(['fieldset'], 'elements', { + prop: { + get: function(){ + //add listed elements without keygen, object, output + return $('input, select, textarea, button, fieldset', this).get() || []; + }, + writeable: false + } + }); + } + + if(!$.fn.finish && parseFloat($.fn.jquery, 10) < 1.9){ + var rCRLF = /\r?\n/g, + rinput = /^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i, + rselectTextarea = /^(?:select|textarea)/i; + $.fn.serializeArray = function() { + return this.map(function(){ + var elements = $.prop(this, 'elements'); + return elements ? $.makeArray( elements ) : this; + }) + .filter(function(){ + return this.name && !this.disabled && + ( this.checked || rselectTextarea.test( this.nodeName ) || + rinput.test( this.type ) ); + }) + .map(function( i, elem ){ + var val = $( this ).val(); + + return val == null ? + null : + $.isArray( val ) ? + $.map( val, function( val, i ){ + return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + }) : + { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + }).get(); + }; + } + + })(); +} - //if firstinvalid was prevented all invalids will be also prevented - if( firstEvent && firstEvent.isDefaultPrevented() ){ - e.preventDefault(); + if($('<input />').prop('labels') == null){ + webshims.defineNodeNamesProperty('button, input, keygen, meter, output, progress, select, textarea', 'labels', { + prop: { + get: function(){ + if(this.type == 'hidden'){return null;} + var id = this.id; + var labels = $(this) + .closest('label') + .filter(function(){ + var hFor = (this.attributes['for'] || {}); + return (!hFor.specified || hFor.value == id); + }) + ; + + if(id) { + labels = labels.add('label[for="'+ id +'"]'); + } + return labels.get(); + }, + writeable: false } - 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) { + if(!('value' in document.createElement('progress'))){ (function(){ - var elems = /^(?:textarea|input)$/i; - var form = false; - document.addEventListener('contextmenu', function(e){ - if (elems.test(e.target.nodeName || '') && (form = e.target.form)) { - setTimeout(function(){ - form = false; - }, 1); - } - }, false); + var nan = parseInt('NaN', 10); - $(window).on('invalid', function(e){ - if (e.originalEvent && form && form == e.target.form) { - e.wrongWebkitInvalid = true; - e.stopImmediatePropagation(); + var updateProgress = function(progress){ + var position; + + + position = $.prop(progress, 'position'); + + $.attr(progress, 'data-position', position); + $('> span', progress).css({width: (position < 0 ? 100 : position * 100) +'%'}); + }; + var desc = { + position: { + prop: { + get: function(){ + var max; + //jQuery 1.8.x try's to normalize "0" to 0 + var val = this.getAttribute('value'); + var ret = -1; + + val = val ? (val * 1) : nan; + if(!isNaN(val)){ + max = $.prop(this, 'max'); + ret = Math.max(Math.min(val / max, 1), 0); + if(updateProgress.isInChange){ + $.attr(this, 'aria-valuenow', ret * 100); + if(updateProgress.isInChange == 'max'){ + $.attr(this, 'aria-valuemax', max); + } + } + } else if(updateProgress.isInChange) { + $(this).removeAttr('aria-valuenow'); + } + return ret; + }, + writeable: false + } } + }; + + $.each({value: 0, max: 1}, function(name, defValue){ + var removeProp = (name == 'value' && !$.fn.finish); + desc[name] = { + attr: { + set: function(value){ + var ret = desc[name].attr._supset.call(this, value); + updateProgress.isInChange = name; + updateProgress(this); + updateProgress.isInChange = false; + return ret; + } + }, + removeAttr: { + value: function(){ + this.removeAttribute(name); + if(removeProp){ + try { + delete this.value; + } catch(er){} + } + updateProgress.isInChange = name; + updateProgress(this); + updateProgress.isInChange = false; + } + }, + prop: { + get: function(){ + var max; + var ret = (desc[name].attr.get.call(this) * 1); + if(ret < 0 || isNaN(ret)){ + ret = defValue; + } else if(name == 'value'){ + ret = Math.min(ret, $.prop(this, 'max')); + } else if(ret === 0){ + ret = defValue; + } + return ret; + }, + set: function(value){ + value = value * 1; + if(isNaN(value)){ + webshims.error('Floating-point value is not finite.'); + } + return desc[name].attr.set.call(this, value); + } + } + }; }); + webshims.createElement( + 'progress', + function(){ + var labels = $(this) + .attr({role: 'progressbar', 'aria-valuemin': '0'}) + .html('<span class="progress-value" />') + .jProp('labels') + .map(function(){ + return webshims.getID(this); + }) + .get() + ; + if(labels.length){ + $.attr(this, 'aria-labelledby', labels.join(' ')); + } else { + webshims.info("you should use label elements for your prgogress elements"); + } + + updateProgress.isInChange = 'max'; + updateProgress(this); + updateProgress.isInChange = false; + }, + desc + ); + })(); } -}); -webshims.register('form-validators', function($, webshims, window, document, undefined, options){ -"use strict"; - -(function(){ - if(webshims.refreshCustomValidityRules){ - webshims.error("form-validators already included. please remove custom-validity.js"); - } - - var customValidityRules = {}; - var formReady = false; - 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(formReady){ - $('input, select, textarea') - .filter(noValidate) - .each(function(){ - testValidityRules(this); - }) - ; - } - }; - webshims.refreshCustomValidityRules = function(elem){ - if(!initTest){return;} - - var data = $(elem).data() || $.data(elem, {}); - var customMismatchedRule = data.customMismatchedRule; - var validity = $.prop(elem, 'validity') || {}; - var message = ''; - 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]['']; +try { + document.querySelector(':checked'); +} catch(er){ + (function(){ + $('html').addClass('no-csschecked'); + var checkInputs = { + radio: 1, + checkbox: 1 + }; + var selectChange = function(){ + var options = this.options || []; + var i, len, option; + for(i = 0, len = options.length; i < len; i++){ + option = $(options[i]); + option[$.prop(options[i], 'selected') ? 'addClass' : 'removeClass']('prop-checked'); } + }; + var checkChange = function(){ + var fn = $.prop(this, 'checked') ? 'addClass' : 'removeClass'; + var className = this.className || ''; + var parent; - if(typeof message == 'object'){ - message = message[errorType] || message.customError || message.defaultMessage; + //IE8- has problems to update styles, we help + if( (className.indexOf('prop-checked') == -1) == (fn == 'addClass')){ + $(this)[fn]('prop-checked'); + if((parent = this.parentNode)){ + parent.className = parent.className; + } } - $(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, data, setMessage) || ''; - customMismatchedRule = name; - if(message){ - return false; - } - }); - if(message != 'async'){ - setMessage(message, customMismatchedRule); + + + webshims.onNodeNamesPropertyModify('select', 'value', selectChange); + webshims.onNodeNamesPropertyModify('select', 'selectedIndex', selectChange); + webshims.onNodeNamesPropertyModify('option', 'selected', function(){ + $(this).closest('select').each(selectChange); + }); + webshims.onNodeNamesPropertyModify('input', 'checked', function(value, boolVal){ + var type = this.type; + if(type == 'radio' && boolVal){ + getGroupElements(this).each(checkChange); + } else if(checkInputs[type]) { + $(this).each(checkChange); } - } - return message; - }; - var testValidityRules = webshims.refreshCustomValidityRules; - - webshims.ready('forms form-validation', function(){ + }); - $.propHooks.setCustomValidity = { - get: function(elem){ - if(!blockCustom){ - $.data(elem, 'customMismatchedRule', ''); + $(document).on('change', function(e){ + + if(checkInputs[e.target.type]){ + if(e.target.type == 'radio'){ + getGroupElements(e.target).each(checkChange); + } else { + $(e.target)[$.prop(e.target, 'checked') ? 'addClass' : 'removeClass']('prop-checked'); } - return null; + } else if(e.target.nodeName.toLowerCase() == 'select'){ + $(e.target).each(selectChange); } - }; + }); - - setTimeout(function(){ - webshims.addReady(function(context, selfElement){ - initTest = true; - $('input, select, textarea', context).add(selfElement.filter('input, select, textarea')) - .filter(noValidate) - .each(function(){ - testValidityRules(this); - }) - ; - - formReady = true; - }); - $(document).on('refreshCustomValidityRules', onEventTest); - }, 9); - - }); - -})(); - -/* - * adds support for HTML5 constraint validation - * - partial pattern: <input data-partial-pattern="RegExp" /> - * - creditcard-validation: <input class="creditcard-input" /> - * - several dependent-validation patterns (examples): - * - <input type="email" id="mail" /> <input data-dependent-validation='mail' /> - * - <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; - 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, 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; - if(!name || elem.type !== 'checkbox' || !$(elem).hasClass('group-required')){return;} - var checkboxes = $( (elem.form && elem.form[name]) || document.getElementsByName(name)); - var isValid = checkboxes.filter(':checked:enabled'); - if(groupTimer[name]){ - clearTimeout(groupTimer[name]); - } - groupTimer[name] = setTimeout(function(){ - checkboxes - .addClass('group-required') - .unbind('click.groupRequired') - .bind('click.groupRequired', function(){ - checkboxes.filter('.group-required').each(function(){ - webshims.refreshCustomValidityRules(this); - }); + webshims.addReady(function(context, contextElem){ + $('option, input', context) + .add(contextElem.filter('option, input')) + .each(function(){ + var prop; + if(checkInputs[this.type]){ + prop = 'checked'; + } else if(this.nodeName.toLowerCase() == 'option'){ + prop = 'selected'; + } + if(prop){ + $(this)[$.prop(this, prop) ? 'addClass' : 'removeClass']('prop-checked'); + } + }) ; - }, 9); + }); + })(); +} + +(function(){ + var bustedPlaceholder; + Modernizr.textareaPlaceholder = !!('placeholder' in $('<textarea />')[0]); + if(Modernizr.input.placeholder && options.overridePlaceholder){ + bustedPlaceholder = true; + } + if(Modernizr.input.placeholder && Modernizr.textareaPlaceholder && !bustedPlaceholder){ + (function(){ + var ua = navigator.userAgent; + + if(ua.indexOf('Mobile') != -1 && ua.indexOf('Safari') != -1){ + $(window).on('orientationchange', (function(){ + var timer; + var retVal = function(i, value){ + return value; + }; + + var set = function(){ + $('input[placeholder], textarea[placeholder]').attr('placeholder', retVal); + }; + return function(e){ + clearTimeout(timer); + timer = setTimeout(set, 9); + }; + })()); + } + })(); - return !(isValid[0]); - }, 'Please check one of these checkboxes.'); + //abort + return; + } - // based on https://sites.google.com/site/abapexamples/javascript/luhn-validation - addCustomValidityRule('creditcard', function(elem, value){ - if(!value || !$(elem).hasClass('creditcard-input')){return;} - value = value.replace(/\-/g, ""); - //if it's not numeric return true >- for invalid - if(value != value * 1){return true;} - var len = value.length; - var sum = 0; - var mul = 1; - var ca; + var isOver = (webshims.cfg.forms.placeholderType == 'over'); + var isResponsive = (webshims.cfg.forms.responsivePlaceholder); + var polyfillElements = ['textarea']; + var debug = webshims.debug !== false; + if(!Modernizr.input.placeholder || bustedPlaceholder){ + polyfillElements.push('input'); + } - while (len--) { - ca = parseInt(value.charAt(len),10) * mul; - sum += ca - (ca>9)*9;// sum += ca - (-(ca>9))|9 - // 1 <--> 2 toggle. - mul ^= 3; // (mul = 3 - mul); - } - return !((sum%10 === 0) && (sum > 0)); - }, 'Please enter a valid credit card number'); - - var dependentDefaults = { - //"from": "IDREF || UniqueNAMEREF", //required property: element - "prop": "value", //default: value||disabled (last if "from-prop" is checked) - "from-prop": "value", //default: value||checked (last if element checkbox or radio) - "toggle": false + var setSelection = function(elem){ + try { + if(elem.setSelectionRange){ + elem.setSelectionRange(0, 0); + return true; + } else if(elem.createTextRange){ + var range = elem.createTextRange(); + range.collapse(true); + range.moveEnd('character', 0); + range.moveStart('character', 0); + range.select(); + return true; + } + } catch(er){} }; - 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; - } - }); - - addCustomValidityRule('dependent', function(elem, val, data){ - data = data.dependentValidation; - if( !data ){return;} - var specialVal; - var depFn = function(e){ - var val = $.prop(data.masterElement, data["from-prop"]); - if(specialVal){ - val = $.inArray(val, specialVal) !== -1; + var hidePlaceholder = function(elem, data, value, _onFocus){ + if(value === false){ + value = $.prop(elem, 'value'); } - if(data.toggle){ - val = !val; + + if(!isOver && elem.type != 'password'){ + if(!value && _onFocus && setSelection(elem)){ + var selectTimer = setTimeout(function(){ + setSelection(elem); + }, 9); + $(elem) + .off('.placeholderremove') + .on({ + 'keydown.placeholderremove keypress.placeholderremove paste.placeholderremove input.placeholderremove': function(e){ + if(e && (e.keyCode == 17 || e.keyCode == 16)){return;} + elem.value = $.prop(elem, 'value'); + data.box.removeClass('placeholder-visible'); + clearTimeout(selectTimer); + $(elem).unbind('.placeholderremove'); + }, + 'mousedown.placeholderremove drag.placeholderremove select.placeholderremove': function(e){ + setSelection(elem); + clearTimeout(selectTimer); + selectTimer = setTimeout(function(){ + setSelection(elem); + }, 9); + }, + 'blur.placeholderremove': function(){ + clearTimeout(selectTimer); + $(elem).unbind('.placeholderremove'); + } + }) + ; + return; + } else if(!_onFocus && !value && elem.value){ //especially on submit + elem.value = value; + } + } else if(!value && _onFocus){ + $(elem) + .off('.placeholderremove') + .on({ + 'keydown.placeholderremove keypress.placeholderremove paste.placeholderremove input.placeholderremove': function(e){ + if(e && (e.keyCode == 17 || e.keyCode == 16)){return;} + data.box.removeClass('placeholder-visible'); + $(elem).unbind('.placeholderremove'); + }, + 'blur.placeholderremove': function(){ + $(elem).unbind('.placeholderremove'); + } + }) + ; + return; } - $.prop( elem, data.prop, val); - if(e){ - $(elem).getShadowElement().filter('.user-error, .user-success').trigger('refreshvalidityui'); + data.box.removeClass('placeholder-visible'); + }, + showPlaceholder = function(elem, data, placeholderTxt){ + if(placeholderTxt === false){ + placeholderTxt = $.prop(elem, 'placeholder'); } - }; - - if(!data._init || !data.masterElement){ - if(typeof data == 'string'){ - data = {"from": data}; + if(!isOver && elem.type != 'password'){ + elem.value = placeholderTxt; } + data.box.addClass('placeholder-visible'); + }, + changePlaceholderVisibility = function(elem, value, placeholderTxt, data, type){ + if(!data){ + data = $.data(elem, 'placeHolder'); + if(!data){return;} + } + var isVisible = $(elem).hasClass('placeholder-visible'); + if(placeholderTxt === false){ + placeholderTxt = $.attr(elem, 'placeholder') || ''; + } + $(elem).unbind('.placeholderremove'); - data.masterElement = document.getElementById(data["from"]) || (document.getElementsByName(data["from"] || [])[0]); + if(value === false){ + value = $.prop(elem, 'value'); + } - if (!data.masterElement || !data.masterElement.form) {return;} - - if(/radio|checkbox/i.test(data.masterElement.type)){ - if(!data["from-prop"]){ - data["from-prop"] = 'checked'; + if(!value && (type == 'focus' || (!type && $(elem).is(':focus')))){ + if(elem.type == 'password' || isOver || isVisible){ + hidePlaceholder(elem, data, '', true); } - if(!data.prop && data["from-prop"] == 'checked'){ - data.prop = 'disabled'; - } - } else if(!data["from-prop"]){ - data["from-prop"] = 'value'; + return; } - if(data["from-prop"].indexOf('value:') === 0){ - specialVal = data["from-prop"].replace('value:', '').split('||'); - data["from-prop"] = 'value'; - + if(value){ + hidePlaceholder(elem, data, value); + return; } - data = $.data(elem, 'dependentValidation', $.extend({_init: true}, dependentDefaults, data)); - - if(data.prop !== "value" || specialVal){ - $(data.masterElement.type === 'radio' && getGroupElements(data.masterElement) || data.masterElement).bind('change', depFn); + if(placeholderTxt && !value){ + showPlaceholder(elem, data, placeholderTxt); } else { - $(data.masterElement).bind('change', function(){ - webshims.refreshCustomValidityRules(elem); - $(elem).getShadowElement().filter('.user-error, .user-success').trigger('refreshvalidityui'); - }); + hidePlaceholder(elem, data, value); } - } - - if(data.prop == "value" && !specialVal){ - return ($.prop(data.masterElement, 'value') != val); - } else { - 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) : $([]); + }, + hasLabel = function(elem){ + elem = $(elem); + return !!(elem.prop('title') || elem.attr('aria-labelledby') || elem.attr('aria-label') || elem.jProp('labels').length); + }, + createPlaceholder = function(elem){ + elem = $(elem); + return $( hasLabel(elem) ? '<span class="placeholder-text"></span>' : '<label for="'+ elem.prop('id') +'" class="placeholder-text"></label>'); + }, + pHolder = (function(){ + var delReg = /\n|\r|\f|\t/g, + allowedPlaceholder = { + text: 1, + search: 1, + url: 1, + email: 1, + password: 1, + tel: 1, + number: 1 } - - 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; + ; + if(webshims.modules["form-number-date-ui"].loaded){ + delete allowedPlaceholder.number; + } + + return { + create: function(elem){ + var data = $.data(elem, 'placeHolder'); + var form; + var responsiveElem; + if(data){return data;} + data = $.data(elem, 'placeHolder', {}); + + $(elem).on('focus.placeholder blur.placeholder', function(e){ + changePlaceholderVisibility(this, false, false, data, e.type ); + data.box[e.type == 'focus' ? 'addClass' : 'removeClass']('placeholder-focused'); + }); + + if((form = $.prop(elem, 'form'))){ + $(elem).onWSOff('reset.placeholder', function(e){ + setTimeout(function(){ + changePlaceholderVisibility(elem, false, false, data, e.type ); + }, 0); + }, false, form); + } + + if(elem.type == 'password' || isOver){ + data.text = createPlaceholder(elem); + if(isResponsive || $(elem).is('.responsive-width') || (elem.currentStyle || {width: ''}).width.indexOf('%') != -1){ + responsiveElem = true; + data.box = data.text; } 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 - }) - ); + data.box = $(elem) + .wrap('<span class="placeholder-box placeholder-box-'+ (elem.nodeName || '').toLowerCase() +' placeholder-box-'+$.css(elem, 'float')+'" />') + .parent() + ; } - }, - _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; + data.text + .insertAfter(elem) + .on('mousedown.placeholder', function(){ + changePlaceholderVisibility(this, false, false, data, 'focus'); + try { + setTimeout(function(){ + elem.focus(); + }, 0); + } catch(e){} return false; + }) + ; + + + $.each(['lineHeight', 'fontSize', 'fontFamily', 'fontWeight'], function(i, style){ + var prop = $.css(elem, style); + if(data.text.css(style) != prop){ + data.text.css(style, prop); } - 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); + $.each(['Left', 'Top'], function(i, side){ + var size = (parseInt($.css(elem, 'padding'+ side), 10) || 0) + Math.max((parseInt($.css(elem, 'margin'+ side), 10) || 0), 0) + (parseInt($.css(elem, 'border'+ side +'Width'), 10) || 0); + data.text.css('padding'+ side, size); + }); + + $(elem) + .onWSOff('updateshadowdom', function(){ + var height, width; + if((width = elem.offsetWidth) || (height = elem.offsetHeight)){ + data.text + .css({ + width: width, + height: height + }) + .css($(elem).position()) + ; } + }, true) + ; + + } else { + var reset = function(e){ + if($(elem).hasClass('placeholder-visible')){ + hidePlaceholder(elem, data, ''); + setTimeout(function(){ + if(!e || e.type != 'submit' || e.isDefaultPrevented()){ + changePlaceholderVisibility(elem, false, false, data ); + } + }, 9); } - } else { - message = remoteValidate.message; + }; + + $(elem).onWSOff('beforeunload', reset, false, window); + data.box = $(elem); + if(form){ + $(elem).onWSOff('submit', reset, false, form); } - return message; } - }; - data.remoteValidate = remoteValidate; + + return data; + }, + update: function(elem, val){ + var type = ($.attr(elem, 'type') || $.prop(elem, 'type') || '').toLowerCase(); + if(!allowedPlaceholder[type] && !$.nodeName(elem, 'textarea')){ + webshims.warn('placeholder not allowed on input[type="'+type+'"], but it is a good fallback :-)'); + return; + } + + + var data = pHolder.create(elem); + if(data.text){ + data.text.text(val); + } + + changePlaceholderVisibility(elem, false, val, data); + } + }; + })() + ; + + $.webshims.publicMethods = { + pHolder: pHolder + }; + polyfillElements.forEach(function(nodeName){ + var desc = webshims.defineNodeNameProperty(nodeName, 'placeholder', { + attr: { + set: function(val){ + var elem = this; + if(bustedPlaceholder){ + webshims.data(elem, 'bustedPlaceholder', val); + elem.placeholder = ''; + } else { + webshims.contentAttr(elem, 'placeholder', val); + } + pHolder.update(elem, val); + }, + get: function(){ + var placeholder; + if(bustedPlaceholder){ + placeholder = webshims.data(this, 'bustedPlaceholder'); + } + return placeholder || webshims.contentAttr(this, 'placeholder'); + } + }, + reflect: true, + initAttr: true + }); + }); + + + polyfillElements.forEach(function(name){ + var placeholderValueDesc = {}; + var desc; + ['attr', 'prop'].forEach(function(propType){ + placeholderValueDesc[propType] = { + set: function(val){ + var elem = this; + var placeholder; + if(bustedPlaceholder){ + placeholder = webshims.data(elem, 'bustedPlaceholder'); + } + if(!placeholder){ + placeholder = webshims.contentAttr(elem, 'placeholder'); + } + $.removeData(elem, 'cachedValidity'); + var ret = desc[propType]._supset.call(elem, val); + if(placeholder && 'value' in elem){ + changePlaceholderVisibility(elem, val, placeholder); + } + return ret; + }, + get: function(){ + var elem = this; + return $(elem).hasClass('placeholder-visible') ? '' : desc[propType]._supget.call(elem); + } + }; + }); + desc = webshims.defineNodeNameProperty(name, 'value', placeholderValueDesc); + }); + +})(); + + (function(){ + var doc = document; + if( 'value' in document.createElement('output') ){return;} + + webshims.defineNodeNameProperty('output', 'value', { + prop: { + set: function(value){ + var setVal = $.data(this, 'outputShim'); + if(!setVal){ + setVal = outputCreate(this); + } + setVal(value); + }, + get: function(){ + return webshims.contentAttr(this, 'value') || $(this).text() || ''; + } } + }); + + + webshims.onNodeNamesPropertyModify('input', 'value', function(value, boolVal, type){ + if(type == 'removeAttr'){return;} + var setVal = $.data(this, 'outputShim'); + if(setVal){ + setVal(value); + } + }); + + var outputCreate = function(elem){ + if(elem.getAttribute('aria-live')){return;} + elem = $(elem); + var value = (elem.text() || '').trim(); + var id = elem.prop('id'); + var htmlFor = elem.attr('for'); + var shim = $('<input class="output-shim" type="text" disabled name="'+ (elem.attr('name') || '')+'" value="'+value+'" style="display: none !important;" />').insertAfter(elem); + var form = shim[0].form || doc; + var setValue = function(val){ + shim[0].value = val; + val = shim[0].value; + elem.text(val); + webshims.contentAttr(elem[0], 'value', val); + }; - return data.remoteValidate.getTempMessage(); - }, 'remote error'); - } -})(); + elem[0].defaultValue = value; + webshims.contentAttr(elem[0], 'value', value); + + elem.attr({'aria-live': 'polite'}); + if(id){ + shim.attr('id', id); + elem.attr('aria-labelledby', elem.jProp('labels').map(function(){ + return webshims.getID(this); + }).get().join(' ')); + } + if(htmlFor){ + id = webshims.getID(elem); + htmlFor.split(' ').forEach(function(control){ + control = document.getElementById(control); + if(control){ + control.setAttribute('aria-controls', id); + } + }); + } + elem.data('outputShim', setValue ); + shim.data('outputShim', setValue ); + return setValue; + }; + + webshims.addReady(function(context, contextElem){ + $('output', context).add(contextElem.filter('output')).each(function(){ + outputCreate(this); + }); + }); + + /* + * Implements input event in all browsers + */ + (function(){ + var noInputTriggerEvts = {updateInput: 1, input: 1}, + noInputTypes = { + radio: 1, + checkbox: 1, + submit: 1, + button: 1, + image: 1, + reset: 1, + file: 1 + + //pro forma + ,color: 1 + //,range: 1 + }, + observe = function(input){ + var timer, + lastVal = input.prop('value'), + trigger = function(e){ + //input === null + if(!input){return;} + var newVal = input.prop('value'); + if(newVal !== lastVal){ + lastVal = newVal; + if(!e || !noInputTriggerEvts[e.type]){ + webshims.triggerInlineForm && webshims.triggerInlineForm(input[0], 'input'); + } + } + }, + extraTimer, + extraTest = function(){ + clearTimeout(extraTimer); + extraTimer = setTimeout(trigger, 9); + }, + unbind = function(){ + input.unbind('focusout', unbind).unbind('keyup keypress keydown paste cut', extraTest).unbind('input change updateInput', trigger); + clearInterval(timer); + setTimeout(function(){ + trigger(); + input = null; + }, 1); + + } + ; + + clearInterval(timer); + timer = setInterval(trigger, 200); + extraTest(); + input.on({ + 'keyup keypress keydown paste cut': extraTest, + focusout: unbind, + 'input updateInput change': trigger + }); + } + ; + + $(doc) + .on('focusin', function(e){ + if( e.target && !e.target.readOnly && !e.target.disabled && (e.target.nodeName || '').toLowerCase() == 'input' && !noInputTypes[e.target.type] && !(webshims.data(e.target, 'implemented') || {}).inputwidgets){ + observe($(e.target)); + } + }) + ; + })(); + })(); -}); + +}); \ No newline at end of file