正在显示
4 个修改的文件
包含
2163 行增加
和
12 行删除
此 diff 太大无法显示。
@@ -306,6 +306,16 @@ define(['jquery', 'bootstrap', 'toastr', 'layer', 'lang', 'config'], function ($ | @@ -306,6 +306,16 @@ define(['jquery', 'bootstrap', 'toastr', 'layer', 'lang', 'config'], function ($ | ||
306 | } | 306 | } |
307 | return val; | 307 | return val; |
308 | }); | 308 | }); |
309 | + }, | ||
310 | + init: function () { | ||
311 | + //后台的公用代码 | ||
312 | + //点击包含.btn-dialog的元素时弹出dialog | ||
313 | + $(document).on('click', '.btn-dialog', function (e) { | ||
314 | + Backend.api.open(Backend.api.fixurl($(this).attr('href')), $(this).attr('title')); | ||
315 | + e.preventDefault(); | ||
316 | + }); | ||
317 | + //支持data-bind-url方式进行渲染select元素 | ||
318 | + | ||
309 | } | 319 | } |
310 | }; | 320 | }; |
311 | //将Layer暴露到全局中去 | 321 | //将Layer暴露到全局中去 |
@@ -318,10 +328,6 @@ define(['jquery', 'bootstrap', 'toastr', 'layer', 'lang', 'config'], function ($ | @@ -318,10 +328,6 @@ define(['jquery', 'bootstrap', 'toastr', 'layer', 'lang', 'config'], function ($ | ||
318 | window.Backend = Backend; | 328 | window.Backend = Backend; |
319 | //Toastr定义 | 329 | //Toastr定义 |
320 | Toastr.options = Backend.config.toastr; | 330 | Toastr.options = Backend.config.toastr; |
321 | - //点击包含.btn-dialog的元素时弹出dialog | ||
322 | - $(document).on('click', '.btn-dialog', function (e) { | ||
323 | - Backend.api.open(Backend.api.fixurl($(this).attr('href')), $(this).attr('title')); | ||
324 | - e.preventDefault(); | ||
325 | - }); | 331 | + |
326 | return Backend; | 332 | return Backend; |
327 | }); | 333 | }); |
@@ -45,7 +45,7 @@ require.config({ | @@ -45,7 +45,7 @@ require.config({ | ||
45 | 'slimscroll': '../libs/jquery-slimscroll/jquery.slimscroll', | 45 | 'slimscroll': '../libs/jquery-slimscroll/jquery.slimscroll', |
46 | 'crontab': '../libs/jqcron/src/jqCron.cn', | 46 | 'crontab': '../libs/jqcron/src/jqCron.cn', |
47 | 'summernote': '../libs/summernote/dist/lang/summernote-zh-CN.min', | 47 | 'summernote': '../libs/summernote/dist/lang/summernote-zh-CN.min', |
48 | - 'validator': '../libs/nice-validator/dist/jquery.validator.js?local=zh-CN', | 48 | + 'validator': '../libs/nice-validator/dist/jquery.validator', |
49 | 'plupload': '../libs/plupload/js/plupload.min', | 49 | 'plupload': '../libs/plupload/js/plupload.min', |
50 | 'toastr': '../libs/toastr/toastr', | 50 | 'toastr': '../libs/toastr/toastr', |
51 | 'jstree': '../libs/jstree/dist/jstree.min', | 51 | 'jstree': '../libs/jstree/dist/jstree.min', |
@@ -62,7 +62,7 @@ require.config({ | @@ -62,7 +62,7 @@ require.config({ | ||
62 | 'slimscroll': '../libs/jquery-slimscroll/jquery.slimscroll', | 62 | 'slimscroll': '../libs/jquery-slimscroll/jquery.slimscroll', |
63 | 'crontab': '../libs/jqcron/src/jqCron.cn', | 63 | 'crontab': '../libs/jqcron/src/jqCron.cn', |
64 | 'summernote': '../libs/summernote/dist/lang/summernote-zh-CN.min', | 64 | 'summernote': '../libs/summernote/dist/lang/summernote-zh-CN.min', |
65 | - 'validator': '../libs/nice-validator/dist/jquery.validator.js?local=zh-CN', | 65 | + 'validator': '../libs/nice-validator/dist/jquery.validator', |
66 | 'plupload': '../libs/plupload/js/plupload.min', | 66 | 'plupload': '../libs/plupload/js/plupload.min', |
67 | 'toastr': '../libs/toastr/toastr', | 67 | 'toastr': '../libs/toastr/toastr', |
68 | 'jstree': '../libs/jstree/dist/jstree.min', | 68 | 'jstree': '../libs/jstree/dist/jstree.min', |
@@ -2220,6 +2220,16 @@ define('backend',['jquery', 'bootstrap', 'toastr', 'layer', 'lang', 'config'], f | @@ -2220,6 +2220,16 @@ define('backend',['jquery', 'bootstrap', 'toastr', 'layer', 'lang', 'config'], f | ||
2220 | } | 2220 | } |
2221 | return val; | 2221 | return val; |
2222 | }); | 2222 | }); |
2223 | + }, | ||
2224 | + init: function () { | ||
2225 | + //后台的公用代码 | ||
2226 | + //点击包含.btn-dialog的元素时弹出dialog | ||
2227 | + $(document).on('click', '.btn-dialog', function (e) { | ||
2228 | + Backend.api.open(Backend.api.fixurl($(this).attr('href')), $(this).attr('title')); | ||
2229 | + e.preventDefault(); | ||
2230 | + }); | ||
2231 | + //支持data-bind-url方式进行渲染select元素 | ||
2232 | + | ||
2223 | } | 2233 | } |
2224 | }; | 2234 | }; |
2225 | //将Layer暴露到全局中去 | 2235 | //将Layer暴露到全局中去 |
@@ -2232,11 +2242,7 @@ define('backend',['jquery', 'bootstrap', 'toastr', 'layer', 'lang', 'config'], f | @@ -2232,11 +2242,7 @@ define('backend',['jquery', 'bootstrap', 'toastr', 'layer', 'lang', 'config'], f | ||
2232 | window.Backend = Backend; | 2242 | window.Backend = Backend; |
2233 | //Toastr定义 | 2243 | //Toastr定义 |
2234 | Toastr.options = Backend.config.toastr; | 2244 | Toastr.options = Backend.config.toastr; |
2235 | - //点击包含.btn-dialog的元素时弹出dialog | ||
2236 | - $(document).on('click', '.btn-dialog', function (e) { | ||
2237 | - Backend.api.open(Backend.api.fixurl($(this).attr('href')), $(this).attr('title')); | ||
2238 | - e.preventDefault(); | ||
2239 | - }); | 2245 | + |
2240 | return Backend; | 2246 | return Backend; |
2241 | }); | 2247 | }); |
2242 | //! moment.js | 2248 | //! moment.js |
@@ -8492,6 +8498,2145 @@ define('upload',['jquery', 'bootstrap', 'backend', 'config', 'plupload'], functi | @@ -8492,6 +8498,2145 @@ define('upload',['jquery', 'bootstrap', 'backend', 'config', 'plupload'], functi | ||
8492 | 8498 | ||
8493 | return Upload; | 8499 | return Upload; |
8494 | }); | 8500 | }); |
8501 | +/*! nice-validator 1.0.10 | ||
8502 | + * (c) 2012-2017 Jony Zhang <niceue@live.com>, MIT Licensed | ||
8503 | + * https://github.com/niceue/nice-validator | ||
8504 | + */ | ||
8505 | +;(function(factory) { | ||
8506 | + typeof module === "object" && module.exports ? module.exports = factory( require( "jquery" ) ) : | ||
8507 | + typeof define === 'function' && define.amd ? define('validator',['jquery'], factory) : | ||
8508 | + factory(jQuery); | ||
8509 | +}(function($, undefined) { | ||
8510 | + "use strict"; | ||
8511 | + | ||
8512 | + var NS = 'validator', | ||
8513 | + CLS_NS = '.' + NS, | ||
8514 | + CLS_NS_RULE = '.rule', | ||
8515 | + CLS_NS_FIELD = '.field', | ||
8516 | + CLS_NS_FORM = '.form', | ||
8517 | + CLS_WRAPPER = 'nice-' + NS, | ||
8518 | + CLS_MSG_BOX = 'msg-box', | ||
8519 | + ARIA_REQUIRED = 'aria-required', | ||
8520 | + ARIA_INVALID = 'aria-invalid', | ||
8521 | + DATA_RULE = 'data-rule', | ||
8522 | + DATA_MSG = 'data-msg', | ||
8523 | + DATA_TIP = 'data-tip', | ||
8524 | + DATA_OK = 'data-ok', | ||
8525 | + DATA_TIMELY = 'data-timely', | ||
8526 | + DATA_TARGET = 'data-target', | ||
8527 | + DATA_DISPLAY = 'data-display', | ||
8528 | + DATA_MUST = 'data-must', | ||
8529 | + NOVALIDATE = 'novalidate', | ||
8530 | + INPUT_SELECTOR = ':verifiable', | ||
8531 | + | ||
8532 | + rRules = /(&)?(!)?\b(\w+)(?:\[\s*(.*?\]?)\s*\]|\(\s*(.*?\)?)\s*\))?\s*(;|\|)?/g, | ||
8533 | + rRule = /(\w+)(?:\[\s*(.*?\]?)\s*\]|\(\s*(.*?\)?)\s*\))?/, | ||
8534 | + rDisplay = /(?:([^:;\(\[]*):)?(.*)/, | ||
8535 | + rDoubleBytes = /[^\x00-\xff]/g, | ||
8536 | + rPos = /top|right|bottom|left/, | ||
8537 | + rAjaxType = /(?:(cors|jsonp):)?(?:(post|get):)?(.+)/i, | ||
8538 | + rUnsafe = /[<>'"`\\]|&#x?\d+[A-F]?;?|%3[A-F]/gmi, | ||
8539 | + | ||
8540 | + noop = $.noop, | ||
8541 | + proxy = $.proxy, | ||
8542 | + trim = $.trim, | ||
8543 | + isFunction = $.isFunction, | ||
8544 | + isString = function(s) { | ||
8545 | + return typeof s === 'string'; | ||
8546 | + }, | ||
8547 | + isObject = function(o) { | ||
8548 | + return o && Object.prototype.toString.call(o) === '[object Object]'; | ||
8549 | + }, | ||
8550 | + isIE = document.documentMode || +(navigator.userAgent.match(/MSIE (\d+)/) && RegExp.$1), | ||
8551 | + attr = function(el, key, value) { | ||
8552 | + if (!el || !el.tagName) return null; | ||
8553 | + if (value !== undefined) { | ||
8554 | + if (value === null) el.removeAttribute(key); | ||
8555 | + else el.setAttribute(key, '' + value); | ||
8556 | + } else { | ||
8557 | + return el.getAttribute(key); | ||
8558 | + } | ||
8559 | + }, | ||
8560 | + novalidateonce, | ||
8561 | + preinitialized = {}, | ||
8562 | + | ||
8563 | + defaults = { | ||
8564 | + debug: 0, | ||
8565 | + theme: 'default', | ||
8566 | + ignore: '', | ||
8567 | + focusInvalid: true, | ||
8568 | + focusCleanup: false, | ||
8569 | + stopOnError: false, | ||
8570 | + beforeSubmit: null, | ||
8571 | + valid: null, | ||
8572 | + invalid: null, | ||
8573 | + validation: null, | ||
8574 | + formClass: 'n-default', | ||
8575 | + validClass: 'n-valid', | ||
8576 | + invalidClass: 'n-invalid', | ||
8577 | + bindClassTo: null | ||
8578 | + }, | ||
8579 | + fieldDefaults = { | ||
8580 | + timely: 1, | ||
8581 | + display: null, | ||
8582 | + target: null, | ||
8583 | + ignoreBlank: false, | ||
8584 | + showOk: true, | ||
8585 | + // Translate ajax response to validation result | ||
8586 | + dataFilter: function (data) { | ||
8587 | + if ( isString(data) || ( isObject(data) && ('error' in data || 'ok' in data) ) ) { | ||
8588 | + return data; | ||
8589 | + } | ||
8590 | + }, | ||
8591 | + msgMaker: function(opt) { | ||
8592 | + var html; | ||
8593 | + html = '<span role="alert" class="msg-wrap n-'+ opt.type + '">' + opt.arrow; | ||
8594 | + if (opt.result) { | ||
8595 | + $.each(opt.result, function(i, obj){ | ||
8596 | + html += '<span class="n-'+ obj.type +'">' + opt.icon + '<span class="n-msg">' + obj.msg + '</span></span>'; | ||
8597 | + }); | ||
8598 | + } else { | ||
8599 | + html += opt.icon + '<span class="n-msg">' + opt.msg + '</span>'; | ||
8600 | + } | ||
8601 | + html += '</span>'; | ||
8602 | + return html; | ||
8603 | + }, | ||
8604 | + msgWrapper: 'span', | ||
8605 | + msgArrow: '', | ||
8606 | + msgIcon: '<span class="n-icon"></span>', | ||
8607 | + msgClass: 'n-right', | ||
8608 | + msgStyle: '', | ||
8609 | + msgShow: null, | ||
8610 | + msgHide: null | ||
8611 | + }, | ||
8612 | + themes = {}; | ||
8613 | + | ||
8614 | + /** jQuery Plugin | ||
8615 | + * @param {Object} options | ||
8616 | + debug {Boolean} 0 Whether to enable debug mode | ||
8617 | + timely {Number} 1 Whether to enable timely validation | ||
8618 | + theme {String} 'default' Theme name | ||
8619 | + stopOnError {Boolean} false Whether to stop validate when found an error input | ||
8620 | + focusCleanup {Boolean} false Whether to clean up the field message when focus the field | ||
8621 | + focusInvalid {Boolean} true Whether to focus the field that is invalid | ||
8622 | + ignoreBlank {Boolean} false When the field has no value, whether to ignore validation | ||
8623 | + ignore {jqSelector} '' Ignored fields (Using jQuery selector) | ||
8624 | + | ||
8625 | + beforeSubmit {Function} Do something before submit form | ||
8626 | + dataFilter {Function} Convert ajax results | ||
8627 | + valid {Function} Triggered when the form is valid | ||
8628 | + invalid {Function} Triggered when the form is invalid | ||
8629 | + validClass {String} 'n-valid' Add this class name to a valid field | ||
8630 | + invalidClass {String} 'n-invalid' Add this class name to a invalid field | ||
8631 | + bindClassTo {jqSelector} ':verifiable' Which element should the className binding to | ||
8632 | + | ||
8633 | + display {Function} Callback function to get dynamic display | ||
8634 | + target {Function} Callback function to get dynamic target | ||
8635 | + msgShow {Function} Trigger this callback when show message | ||
8636 | + msgHide {Function} Trigger this callback when hide message | ||
8637 | + msgWrapper {String} 'span' Message wrapper tag name | ||
8638 | + msgMaker {Function} Callback function to make message HTML | ||
8639 | + msgArrow {String} Message arrow template | ||
8640 | + msgIcon {String} Message icon template | ||
8641 | + msgStyle {String} Custom message css style | ||
8642 | + msgClass {String} Additional added to the message class names | ||
8643 | + formClass {String} Additional added to the form class names | ||
8644 | + | ||
8645 | + messages {Object} Custom messages for the current instance | ||
8646 | + rules {Object} Custom rules for the current instance | ||
8647 | + fields {Object} Field validation configuration | ||
8648 | + {String} key name|#id | ||
8649 | + {String|Object} value Rule string or an object which can pass more arguments | ||
8650 | + | ||
8651 | + fields[key][rule] {String} Rule string | ||
8652 | + fields[key][display] {String|Function} | ||
8653 | + fields[key][tip] {String} Custom tip message | ||
8654 | + fields[key][ok] {String} Custom success message | ||
8655 | + fields[key][msg] {Object} Custom error message | ||
8656 | + fields[key][msgStyle] {String} Custom message style | ||
8657 | + fields[key][msgClass] {String} A className which added to message placeholder element | ||
8658 | + fields[key][msgWrapper] {String} Tag name of the message placeholder element | ||
8659 | + fields[key][msgMaker] {Function} A function to custom message HTML | ||
8660 | + fields[key][dataFilter] {Function} A function to convert ajax results | ||
8661 | + fields[key][valid] {Function} A function triggered when field is valid | ||
8662 | + fields[key][invalid] {Function} A function triggered when field is invalid | ||
8663 | + fields[key][must] {Boolean} If set true, we always check the field even has remote checking | ||
8664 | + fields[key][timely] {Boolean} Whether to enable timely validation | ||
8665 | + fields[key][target] {jqSelector} Define placement of a message | ||
8666 | + */ | ||
8667 | + $.fn.validator = function(options) { | ||
8668 | + var that = this, | ||
8669 | + args = arguments; | ||
8670 | + | ||
8671 | + if (that.is(INPUT_SELECTOR)) return that; | ||
8672 | + if (!that.is('form')) that = this.find('form'); | ||
8673 | + if (!that.length) that = this; | ||
8674 | + | ||
8675 | + that.each(function() { | ||
8676 | + var instance = $(this).data(NS); | ||
8677 | + | ||
8678 | + if (instance) { | ||
8679 | + if ( isString(options) ) { | ||
8680 | + if ( options.charAt(0) === '_' ) return; | ||
8681 | + instance[options].apply(instance, [].slice.call(args, 1)); | ||
8682 | + } | ||
8683 | + else if (options) { | ||
8684 | + instance._reset(true); | ||
8685 | + instance._init(this, options); | ||
8686 | + } | ||
8687 | + } else { | ||
8688 | + new Validator(this, options); | ||
8689 | + } | ||
8690 | + }); | ||
8691 | + | ||
8692 | + return this; | ||
8693 | + }; | ||
8694 | + | ||
8695 | + | ||
8696 | + // Validate a field, or an area | ||
8697 | + $.fn.isValid = function(callback, hideMsg) { | ||
8698 | + var me = _getInstance(this[0]), | ||
8699 | + hasCallback = isFunction(callback), | ||
8700 | + ret, opt; | ||
8701 | + | ||
8702 | + if (!me) return true; | ||
8703 | + if (!hasCallback && hideMsg === undefined) hideMsg = callback; | ||
8704 | + me.checkOnly = !!hideMsg; | ||
8705 | + opt = me.options; | ||
8706 | + | ||
8707 | + ret = me._multiValidate( | ||
8708 | + this.is(INPUT_SELECTOR) ? this : this.find(INPUT_SELECTOR), | ||
8709 | + function(isValid){ | ||
8710 | + if (!isValid && opt.focusInvalid && !me.checkOnly) { | ||
8711 | + // navigate to the error element | ||
8712 | + me.$el.find('[' + ARIA_INVALID + ']:first').focus(); | ||
8713 | + } | ||
8714 | + if (hasCallback) { | ||
8715 | + if (callback.length) { | ||
8716 | + callback(isValid); | ||
8717 | + } else if (isValid) { | ||
8718 | + callback(); | ||
8719 | + } | ||
8720 | + } | ||
8721 | + me.checkOnly = false; | ||
8722 | + } | ||
8723 | + ); | ||
8724 | + | ||
8725 | + // If you pass a callback, we maintain the jQuery object chain | ||
8726 | + return hasCallback ? this : ret; | ||
8727 | + }; | ||
8728 | + | ||
8729 | + $.extend($.expr.pseudos || $.expr[':'], { | ||
8730 | + // A faster selector than ":input:not(:submit,:button,:reset,:image,:disabled,[contenteditable])" | ||
8731 | + verifiable: function(elem) { | ||
8732 | + var name = elem.nodeName.toLowerCase(); | ||
8733 | + | ||
8734 | + return ( name === 'input' && !({submit: 1, button: 1, reset: 1, image: 1})[elem.type] || | ||
8735 | + name === 'select' || | ||
8736 | + name === 'textarea' || | ||
8737 | + elem.contentEditable === 'true' | ||
8738 | + ) && !elem.disabled; | ||
8739 | + }, | ||
8740 | + // any value, but not only whitespace | ||
8741 | + filled: function(elem) { | ||
8742 | + return !!trim($(elem).val()); | ||
8743 | + } | ||
8744 | + }); | ||
8745 | + | ||
8746 | + /** | ||
8747 | + * Creates a new Validator | ||
8748 | + * | ||
8749 | + * @class | ||
8750 | + * @param {Element} element - form element | ||
8751 | + * @param {Object} options - options for validator | ||
8752 | + */ | ||
8753 | + function Validator(element, options) { | ||
8754 | + var me = this; | ||
8755 | + | ||
8756 | + if ( !(me instanceof Validator) ) { | ||
8757 | + return new Validator(element, options); | ||
8758 | + } | ||
8759 | + | ||
8760 | + if (Validator.pending) { | ||
8761 | + $(window).on('validatorready', init); | ||
8762 | + } else { | ||
8763 | + init(); | ||
8764 | + } | ||
8765 | + | ||
8766 | + function init() { | ||
8767 | + me.$el = $(element); | ||
8768 | + if (me.$el.length) { | ||
8769 | + me._init(me.$el[0], options); | ||
8770 | + } | ||
8771 | + else if (isString(element)) { | ||
8772 | + preinitialized[element] = options; | ||
8773 | + } | ||
8774 | + } | ||
8775 | + } | ||
8776 | + | ||
8777 | + Validator.prototype = { | ||
8778 | + _init: function(element, options) { | ||
8779 | + var me = this, | ||
8780 | + opt, themeOpt, dataOpt; | ||
8781 | + | ||
8782 | + // Initialization options | ||
8783 | + if ( isFunction(options) ) { | ||
8784 | + options = { | ||
8785 | + valid: options | ||
8786 | + }; | ||
8787 | + } | ||
8788 | + options = me._opt = options || {}; | ||
8789 | + dataOpt = attr(element, 'data-'+ NS +'-option'); | ||
8790 | + dataOpt = me._dataOpt = dataOpt && dataOpt.charAt(0) === '{' ? (new Function("return " + dataOpt))() : {}; | ||
8791 | + themeOpt = me._themeOpt = themes[ options.theme || dataOpt.theme || defaults.theme ]; | ||
8792 | + opt = me.options = $.extend({}, defaults, fieldDefaults, themeOpt, me.options, options, dataOpt); | ||
8793 | + | ||
8794 | + me.rules = new Rules(opt.rules, true); | ||
8795 | + me.messages = new Messages(opt.messages, true); | ||
8796 | + me.Field = _createFieldFactory(me); | ||
8797 | + me.elements = me.elements || {}; | ||
8798 | + me.deferred = {}; | ||
8799 | + me.errors = {}; | ||
8800 | + me.fields = {}; | ||
8801 | + // Initialization fields | ||
8802 | + me._initFields(opt.fields); | ||
8803 | + | ||
8804 | + // Initialization events and make a cache | ||
8805 | + if ( !me.$el.data(NS) ) { | ||
8806 | + me.$el.data(NS, me).addClass(CLS_WRAPPER +' '+ opt.formClass) | ||
8807 | + .on('form-submit-validate', function(e, a, $form, opts, veto) { | ||
8808 | + me.vetoed = veto.veto = !me.isValid; | ||
8809 | + me.ajaxFormOptions = opts; | ||
8810 | + }) | ||
8811 | + .on('submit'+ CLS_NS +' validate'+ CLS_NS, proxy(me, '_submit')) | ||
8812 | + .on('reset'+ CLS_NS, proxy(me, '_reset')) | ||
8813 | + .on('showmsg'+ CLS_NS, proxy(me, '_showmsg')) | ||
8814 | + .on('hidemsg'+ CLS_NS, proxy(me, '_hidemsg')) | ||
8815 | + .on('focusin'+ CLS_NS + ' click'+ CLS_NS, INPUT_SELECTOR, proxy(me, '_focusin')) | ||
8816 | + .on('focusout'+ CLS_NS +' validate'+ CLS_NS, INPUT_SELECTOR, proxy(me, '_focusout')) | ||
8817 | + .on('keyup'+ CLS_NS +' input'+ CLS_NS + ' compositionstart compositionend', INPUT_SELECTOR, proxy(me, '_focusout')) | ||
8818 | + .on('click'+ CLS_NS, ':radio,:checkbox', 'click', proxy(me, '_focusout')) | ||
8819 | + .on('change'+ CLS_NS, 'select,input[type="file"]', 'change', proxy(me, '_focusout')); | ||
8820 | + | ||
8821 | + // cache the novalidate attribute value | ||
8822 | + me._NOVALIDATE = attr(element, NOVALIDATE); | ||
8823 | + // Initialization is complete, stop off default HTML5 form validation | ||
8824 | + // If use "jQuery.attr('novalidate')" in IE7 will complain: "SCRIPT3: Member not found." | ||
8825 | + attr(element, NOVALIDATE, NOVALIDATE); | ||
8826 | + } | ||
8827 | + | ||
8828 | + // Display all messages in target container | ||
8829 | + if ( isString(opt.target) ) { | ||
8830 | + me.$el.find(opt.target).addClass('msg-container'); | ||
8831 | + } | ||
8832 | + }, | ||
8833 | + | ||
8834 | + // Guess whether the form use ajax submit | ||
8835 | + _guessAjax: function(form) { | ||
8836 | + var me = this; | ||
8837 | + | ||
8838 | + if ( !(me.isAjaxSubmit = !!me.options.valid) ) { | ||
8839 | + // if there is a "valid.form" event | ||
8840 | + var events = ($._data || $.data)(form, "events"); | ||
8841 | + me.isAjaxSubmit = issetEvent(events, 'valid', 'form') || issetEvent(events, 'submit', 'form-plugin'); | ||
8842 | + } | ||
8843 | + | ||
8844 | + function issetEvent(events, name, namespace) { | ||
8845 | + if ( events && events[name] && | ||
8846 | + $.map(events[name], function(e){ | ||
8847 | + return ~e.namespace.indexOf(namespace) ? 1 : null; | ||
8848 | + }).length | ||
8849 | + ) { | ||
8850 | + return true; | ||
8851 | + } | ||
8852 | + return false; | ||
8853 | + } | ||
8854 | + }, | ||
8855 | + | ||
8856 | + _initFields: function(fields) { | ||
8857 | + var me = this, k, arr, i, | ||
8858 | + clear = fields === null; | ||
8859 | + | ||
8860 | + // Processing field information | ||
8861 | + if (clear) fields = me.fields; | ||
8862 | + | ||
8863 | + if ( isObject(fields) ) { | ||
8864 | + for (k in fields) { | ||
8865 | + if (~k.indexOf(',')) { | ||
8866 | + arr = k.split(','); | ||
8867 | + i = arr.length; | ||
8868 | + while (i--) { | ||
8869 | + initField(trim(arr[i]), fields[k]); | ||
8870 | + } | ||
8871 | + } else { | ||
8872 | + initField(k, fields[k]); | ||
8873 | + } | ||
8874 | + } | ||
8875 | + } | ||
8876 | + | ||
8877 | + // Parsing DOM rules | ||
8878 | + me.$el.find(INPUT_SELECTOR).each(function() { | ||
8879 | + me._parse(this); | ||
8880 | + }); | ||
8881 | + | ||
8882 | + function initField(k, v) { | ||
8883 | + // delete a field from settings | ||
8884 | + if ( v === null || clear ) { | ||
8885 | + var el = me.elements[k]; | ||
8886 | + if (el) me._resetElement(el, true); | ||
8887 | + delete me.fields[k]; | ||
8888 | + } else { | ||
8889 | + me.fields[k] = new me.Field(k, isString(v) ? {rule: v} : v, me.fields[k]); | ||
8890 | + } | ||
8891 | + } | ||
8892 | + }, | ||
8893 | + | ||
8894 | + // Parsing a field | ||
8895 | + _parse: function(el) { | ||
8896 | + var me = this, | ||
8897 | + field, | ||
8898 | + key = el.name, | ||
8899 | + display, | ||
8900 | + timely, | ||
8901 | + dataRule = attr(el, DATA_RULE); | ||
8902 | + | ||
8903 | + dataRule && attr(el, DATA_RULE, null); | ||
8904 | + | ||
8905 | + // If the field has passed the key as id mode, or it doesn't has a name | ||
8906 | + if ( el.id && ( | ||
8907 | + ('#' + el.id in me.fields) || | ||
8908 | + !key || | ||
8909 | + // If dataRule and element are diffrent from old's, we use ID mode. | ||
8910 | + (dataRule !== null && (field = me.fields[key]) && dataRule !== field.rule && el.id !== field.key) | ||
8911 | + ) | ||
8912 | + ) { | ||
8913 | + key = '#' + el.id; | ||
8914 | + } | ||
8915 | + // doesn't verify a field that has neither id nor name | ||
8916 | + if (!key) return; | ||
8917 | + | ||
8918 | + field = me.getField(key, true); | ||
8919 | + // The priority of passing parameter by DOM is higher than by JS. | ||
8920 | + field.rule = dataRule || field.rule; | ||
8921 | + | ||
8922 | + if (display = attr(el, DATA_DISPLAY)) { | ||
8923 | + field.display = display; | ||
8924 | + } | ||
8925 | + if (field.rule) { | ||
8926 | + if ( attr(el, DATA_MUST) !== null || /\b(?:match|checked)\b/.test(field.rule) ) { | ||
8927 | + field.must = true; | ||
8928 | + } | ||
8929 | + if ( /\brequired\b/.test(field.rule) ) { | ||
8930 | + field.required = true; | ||
8931 | + attr(el, ARIA_REQUIRED, true); | ||
8932 | + } | ||
8933 | + if (timely = attr(el, DATA_TIMELY)) { | ||
8934 | + field.timely = +timely; | ||
8935 | + } else if (field.timely > 3) { | ||
8936 | + attr(el, DATA_TIMELY, field.timely); | ||
8937 | + } | ||
8938 | + me._parseRule(field); | ||
8939 | + field.old = {}; | ||
8940 | + } | ||
8941 | + if ( isString(field.target) ) { | ||
8942 | + attr(el, DATA_TARGET, field.target); | ||
8943 | + } | ||
8944 | + if ( isString(field.tip) ) { | ||
8945 | + attr(el, DATA_TIP, field.tip); | ||
8946 | + } | ||
8947 | + | ||
8948 | + return me.fields[key] = field; | ||
8949 | + }, | ||
8950 | + | ||
8951 | + // Parsing field rules | ||
8952 | + _parseRule: function(field) { | ||
8953 | + var arr = rDisplay.exec(field.rule); | ||
8954 | + | ||
8955 | + if (!arr) return; | ||
8956 | + // current rule index | ||
8957 | + field._i = 0; | ||
8958 | + if (arr[1]) { | ||
8959 | + field.display = arr[1]; | ||
8960 | + } | ||
8961 | + if (arr[2]) { | ||
8962 | + field._rules = []; | ||
8963 | + arr[2].replace(rRules, function(){ | ||
8964 | + var args = arguments; | ||
8965 | + args[4] = args[4] || args[5]; | ||
8966 | + field._rules.push({ | ||
8967 | + and: args[1] === "&", | ||
8968 | + not: args[2] === "!", | ||
8969 | + or: args[6] === "|", | ||
8970 | + method: args[3], | ||
8971 | + params: args[4] ? $.map( args[4].split(', '), trim ) : undefined | ||
8972 | + }); | ||
8973 | + }); | ||
8974 | + } | ||
8975 | + }, | ||
8976 | + | ||
8977 | + // Verify a zone | ||
8978 | + _multiValidate: function($inputs, doneCallback){ | ||
8979 | + var me = this, | ||
8980 | + opt = me.options; | ||
8981 | + | ||
8982 | + me.hasError = false; | ||
8983 | + | ||
8984 | + if (opt.ignore) { | ||
8985 | + $inputs = $inputs.not(opt.ignore); | ||
8986 | + } | ||
8987 | + | ||
8988 | + $inputs.each(function() { | ||
8989 | + me._validate(this); | ||
8990 | + if (me.hasError && opt.stopOnError) { | ||
8991 | + // stop the validation | ||
8992 | + return false; | ||
8993 | + } | ||
8994 | + }); | ||
8995 | + | ||
8996 | + // Need to wait for all fields validation complete, especially asynchronous validation | ||
8997 | + if (doneCallback) { | ||
8998 | + me.validating = true; | ||
8999 | + $.when.apply( | ||
9000 | + null, | ||
9001 | + $.map(me.deferred, function(v){return v;}) | ||
9002 | + ).done(function(){ | ||
9003 | + doneCallback.call(me, !me.hasError); | ||
9004 | + me.validating = false; | ||
9005 | + }); | ||
9006 | + } | ||
9007 | + | ||
9008 | + // If the form does not contain asynchronous validation, the return value is correct. | ||
9009 | + // Otherwise, you should detect form validation result through "doneCallback". | ||
9010 | + return !$.isEmptyObject(me.deferred) ? undefined : !me.hasError; | ||
9011 | + }, | ||
9012 | + | ||
9013 | + // Validate the whole form | ||
9014 | + _submit: function(e) { | ||
9015 | + var me = this, | ||
9016 | + opt = me.options, | ||
9017 | + form = e.target, | ||
9018 | + canSubmit = e.type === 'submit' && !e.isDefaultPrevented(); | ||
9019 | + | ||
9020 | + e.preventDefault(); | ||
9021 | + | ||
9022 | + if ( | ||
9023 | + novalidateonce && ~(novalidateonce = false) || | ||
9024 | + // Prevent duplicate submission | ||
9025 | + me.submiting || | ||
9026 | + // Receive the "validate" event only from the form. | ||
9027 | + e.type === 'validate' && me.$el[0] !== form || | ||
9028 | + // trigger the beforeSubmit callback. | ||
9029 | + isFunction(opt.beforeSubmit) && opt.beforeSubmit.call(me, form) === false | ||
9030 | + ) { | ||
9031 | + return; | ||
9032 | + } | ||
9033 | + | ||
9034 | + if (me.isAjaxSubmit === undefined) { | ||
9035 | + me._guessAjax(form); | ||
9036 | + } | ||
9037 | + | ||
9038 | + me._debug('log', '\n<<< event: ' + e.type); | ||
9039 | + | ||
9040 | + me._reset(); | ||
9041 | + me.submiting = true; | ||
9042 | + | ||
9043 | + me._multiValidate( | ||
9044 | + me.$el.find(INPUT_SELECTOR), | ||
9045 | + function(isValid){ | ||
9046 | + var ret = (isValid || opt.debug === 2) ? 'valid' : 'invalid', | ||
9047 | + errors; | ||
9048 | + | ||
9049 | + if (!isValid) { | ||
9050 | + if (opt.focusInvalid) { | ||
9051 | + // navigate to the error element | ||
9052 | + me.$el.find('[' + ARIA_INVALID + ']:first').focus(); | ||
9053 | + } | ||
9054 | + errors = $.map(me.errors, function(err){return err;}); | ||
9055 | + } | ||
9056 | + | ||
9057 | + // releasing submit | ||
9058 | + me.submiting = false; | ||
9059 | + me.isValid = isValid; | ||
9060 | + | ||
9061 | + // trigger callback and event | ||
9062 | + isFunction(opt[ret]) && opt[ret].call(me, form, errors); | ||
9063 | + me.$el.trigger(ret + CLS_NS_FORM, [form, errors]); | ||
9064 | + | ||
9065 | + me._debug('log', '>>> ' + ret); | ||
9066 | + | ||
9067 | + if (!isValid) return; | ||
9068 | + // For jquery.form plugin | ||
9069 | + if (me.vetoed) { | ||
9070 | + $(form).ajaxSubmit(me.ajaxFormOptions); | ||
9071 | + } | ||
9072 | + else if (canSubmit && !me.isAjaxSubmit) { | ||
9073 | + document.createElement('form').submit.call(form); | ||
9074 | + } | ||
9075 | + } | ||
9076 | + ); | ||
9077 | + }, | ||
9078 | + | ||
9079 | + _reset: function(e) { | ||
9080 | + var me = this; | ||
9081 | + | ||
9082 | + me.errors = {}; | ||
9083 | + if (e) { | ||
9084 | + me.reseting = true; | ||
9085 | + me.$el.find(INPUT_SELECTOR).each( function(){ | ||
9086 | + me._resetElement(this); | ||
9087 | + }); | ||
9088 | + delete me.reseting; | ||
9089 | + } | ||
9090 | + }, | ||
9091 | + | ||
9092 | + _resetElement: function(el, all) { | ||
9093 | + this._setClass(el, null); | ||
9094 | + this.hideMsg(el); | ||
9095 | + if (all) { | ||
9096 | + attr(el, ARIA_REQUIRED, null); | ||
9097 | + } | ||
9098 | + }, | ||
9099 | + | ||
9100 | + // Handle events: "focusin/click" | ||
9101 | + _focusin: function(e) { | ||
9102 | + var me = this, | ||
9103 | + opt = me.options, | ||
9104 | + el = e.target, | ||
9105 | + timely, | ||
9106 | + msg; | ||
9107 | + | ||
9108 | + if ( me.validating || ( e.type==='click' && document.activeElement === el ) ) { | ||
9109 | + return; | ||
9110 | + } | ||
9111 | + | ||
9112 | + if (opt.focusCleanup) { | ||
9113 | + if ( attr(el, ARIA_INVALID) === 'true' ) { | ||
9114 | + me._setClass(el, null); | ||
9115 | + me.hideMsg(el); | ||
9116 | + } | ||
9117 | + } | ||
9118 | + | ||
9119 | + msg = attr(el, DATA_TIP); | ||
9120 | + | ||
9121 | + if (msg) { | ||
9122 | + me.showMsg(el, { | ||
9123 | + type: 'tip', | ||
9124 | + msg: msg | ||
9125 | + }); | ||
9126 | + } else { | ||
9127 | + if (attr(el, DATA_RULE)) { | ||
9128 | + me._parse(el); | ||
9129 | + } | ||
9130 | + if (timely = attr(el, DATA_TIMELY)) { | ||
9131 | + if ( timely === 8 || timely === 9 ) { | ||
9132 | + me._focusout(e); | ||
9133 | + } | ||
9134 | + } | ||
9135 | + } | ||
9136 | + }, | ||
9137 | + | ||
9138 | + // Handle events: "focusout/validate/keyup/click/change/input/compositionstart/compositionend" | ||
9139 | + _focusout: function(e) { | ||
9140 | + var me = this, | ||
9141 | + opt = me.options, | ||
9142 | + el = e.target, | ||
9143 | + etype = e.type, | ||
9144 | + etype0, | ||
9145 | + focusin = etype === 'focusin', | ||
9146 | + special = etype === 'validate', | ||
9147 | + elem, | ||
9148 | + field, | ||
9149 | + old, | ||
9150 | + value, | ||
9151 | + timestamp, | ||
9152 | + key, specialKey, | ||
9153 | + timely, | ||
9154 | + timer = 0; | ||
9155 | + | ||
9156 | + if (etype === 'compositionstart') { | ||
9157 | + me.pauseValidate = true; | ||
9158 | + } | ||
9159 | + if (etype === 'compositionend') { | ||
9160 | + me.pauseValidate = false; | ||
9161 | + } | ||
9162 | + if (me.pauseValidate) { | ||
9163 | + return; | ||
9164 | + } | ||
9165 | + | ||
9166 | + // For checkbox and radio | ||
9167 | + elem = el.name && _checkable(el) ? me.$el.find('input[name="'+ el.name +'"]').get(0) : el; | ||
9168 | + // Get field | ||
9169 | + if (!(field = me.getField(elem)) || !field.rule) { | ||
9170 | + return; | ||
9171 | + } | ||
9172 | + // Cache event type | ||
9173 | + etype0 = field._e; | ||
9174 | + field._e = etype; | ||
9175 | + timely = field.timely; | ||
9176 | + | ||
9177 | + if (!special) { | ||
9178 | + if (!timely || (_checkable(el) && etype !== 'click')) { | ||
9179 | + return; | ||
9180 | + } | ||
9181 | + | ||
9182 | + value = field.getValue(); | ||
9183 | + | ||
9184 | + // not validate field unless fill a value | ||
9185 | + if ( field.ignoreBlank && !value && !focusin ) { | ||
9186 | + me.hideMsg(el); | ||
9187 | + return; | ||
9188 | + } | ||
9189 | + | ||
9190 | + if ( etype === 'focusout' ) { | ||
9191 | + if (etype0 === 'change') { | ||
9192 | + return; | ||
9193 | + } | ||
9194 | + if ( timely === 2 || timely === 8 ) { | ||
9195 | + old = field.old; | ||
9196 | + if (value && old) { | ||
9197 | + if (field.isValid && !old.showOk) { | ||
9198 | + me.hideMsg(el); | ||
9199 | + } else { | ||
9200 | + me._makeMsg(el, field, old); | ||
9201 | + } | ||
9202 | + } else { | ||
9203 | + return; | ||
9204 | + } | ||
9205 | + } | ||
9206 | + } | ||
9207 | + else { | ||
9208 | + if ( timely < 2 && !e.data ) { | ||
9209 | + return; | ||
9210 | + } | ||
9211 | + | ||
9212 | + // mark timestamp to reduce the frequency of the received event | ||
9213 | + timestamp = +new Date(); | ||
9214 | + if ( timestamp - (el._ts || 0) < 100 ) { | ||
9215 | + return; | ||
9216 | + } | ||
9217 | + el._ts = timestamp; | ||
9218 | + | ||
9219 | + // handle keyup | ||
9220 | + if ( etype === 'keyup' ) { | ||
9221 | + if (etype0 === 'input') { | ||
9222 | + return; | ||
9223 | + } | ||
9224 | + key = e.keyCode; | ||
9225 | + specialKey = { | ||
9226 | + 8: 1, // Backspace | ||
9227 | + 9: 1, // Tab | ||
9228 | + 16: 1, // Shift | ||
9229 | + 32: 1, // Space | ||
9230 | + 46: 1 // Delete | ||
9231 | + }; | ||
9232 | + | ||
9233 | + // only gets focus, no validation | ||
9234 | + if ( key === 9 && !value ) { | ||
9235 | + return; | ||
9236 | + } | ||
9237 | + | ||
9238 | + // do not validate, if triggered by these keys | ||
9239 | + if ( key < 48 && !specialKey[key] ) { | ||
9240 | + return; | ||
9241 | + } | ||
9242 | + } | ||
9243 | + if ( !focusin ) { | ||
9244 | + // keyboard events, reducing the frequency of validation | ||
9245 | + timer = timely <100 ? (etype === 'click' || el.tagName === 'SELECT') ? 0 : 400 : timely; | ||
9246 | + } | ||
9247 | + } | ||
9248 | + } | ||
9249 | + | ||
9250 | + // if the current field is ignored | ||
9251 | + if ( opt.ignore && $(el).is(opt.ignore) ) { | ||
9252 | + return; | ||
9253 | + } | ||
9254 | + | ||
9255 | + clearTimeout(field._t); | ||
9256 | + | ||
9257 | + if (timer) { | ||
9258 | + field._t = setTimeout(function() { | ||
9259 | + me._validate(el, field); | ||
9260 | + }, timer); | ||
9261 | + } else { | ||
9262 | + if (special) field.old = {}; | ||
9263 | + me._validate(el, field); | ||
9264 | + } | ||
9265 | + }, | ||
9266 | + | ||
9267 | + _setClass: function(el, isValid) { | ||
9268 | + var $el = $(el), opt = this.options; | ||
9269 | + if (opt.bindClassTo) { | ||
9270 | + $el = $el.closest(opt.bindClassTo); | ||
9271 | + } | ||
9272 | + $el.removeClass( opt.invalidClass + ' ' + opt.validClass ); | ||
9273 | + if (isValid !== null) { | ||
9274 | + $el.addClass( isValid ? opt.validClass : opt.invalidClass ); | ||
9275 | + } | ||
9276 | + }, | ||
9277 | + | ||
9278 | + _showmsg: function(e, type, msg) { | ||
9279 | + var me = this, | ||
9280 | + el = e.target; | ||
9281 | + | ||
9282 | + if ( me.$el.is(el) ) { | ||
9283 | + if (isObject(type)) { | ||
9284 | + me.showMsg(type) | ||
9285 | + } | ||
9286 | + else if ( type === 'tip' ) { | ||
9287 | + me.$el.find(INPUT_SELECTOR +"["+ DATA_TIP +"]", el).each(function(){ | ||
9288 | + me.showMsg(this, {type: type, msg: msg}); | ||
9289 | + }); | ||
9290 | + } | ||
9291 | + } | ||
9292 | + else { | ||
9293 | + me.showMsg(el, {type: type, msg: msg}); | ||
9294 | + } | ||
9295 | + }, | ||
9296 | + | ||
9297 | + _hidemsg: function(e) { | ||
9298 | + var $el = $(e.target); | ||
9299 | + | ||
9300 | + if ( $el.is(INPUT_SELECTOR) ) { | ||
9301 | + this.hideMsg($el); | ||
9302 | + } | ||
9303 | + }, | ||
9304 | + | ||
9305 | + // Validated a field | ||
9306 | + _validatedField: function(el, field, ret) { | ||
9307 | + var me = this, | ||
9308 | + opt = me.options, | ||
9309 | + isValid = field.isValid = ret.isValid = !!ret.isValid, | ||
9310 | + callback = isValid ? 'valid' : 'invalid'; | ||
9311 | + | ||
9312 | + ret.key = field.key; | ||
9313 | + ret.ruleName = field._r; | ||
9314 | + ret.id = el.id; | ||
9315 | + ret.value = field.value; | ||
9316 | + | ||
9317 | + me.elements[field.key] = ret.element = el; | ||
9318 | + me.isValid = me.$el[0].isValid = isValid ? me.isFormValid() : isValid; | ||
9319 | + | ||
9320 | + if (isValid) { | ||
9321 | + ret.type = 'ok'; | ||
9322 | + } else { | ||
9323 | + if (me.submiting) { | ||
9324 | + me.errors[field.key] = ret.msg; | ||
9325 | + } | ||
9326 | + me.hasError = true; | ||
9327 | + } | ||
9328 | + | ||
9329 | + // cache result | ||
9330 | + field.old = ret; | ||
9331 | + | ||
9332 | + // trigger callback | ||
9333 | + isFunction(field[callback]) && field[callback].call(me, el, ret); | ||
9334 | + isFunction(opt.validation) && opt.validation.call(me, el, ret); | ||
9335 | + | ||
9336 | + // trigger event | ||
9337 | + $(el).attr( ARIA_INVALID, isValid ? null : true ) | ||
9338 | + .trigger( callback + CLS_NS_FIELD, [ret, me] ); | ||
9339 | + me.$el.triggerHandler('validation', [ret, me]); | ||
9340 | + | ||
9341 | + if (me.checkOnly) return; | ||
9342 | + // set className | ||
9343 | + me._setClass(el, ret.skip || ret.type === 'tip' ? null : isValid); | ||
9344 | + me._makeMsg.apply(me, arguments); | ||
9345 | + }, | ||
9346 | + | ||
9347 | + _makeMsg: function(el, field, ret) { | ||
9348 | + // show or hide the message | ||
9349 | + if (field.msgMaker) { | ||
9350 | + ret = $.extend({}, ret); | ||
9351 | + if (field._e === 'focusin') { | ||
9352 | + ret.type = 'tip'; | ||
9353 | + } | ||
9354 | + this[ ret.showOk || ret.msg || ret.type === 'tip' ? 'showMsg' : 'hideMsg' ](el, ret, field); | ||
9355 | + } | ||
9356 | + }, | ||
9357 | + | ||
9358 | + // Validated a rule | ||
9359 | + _validatedRule: function(el, field, ret, msgOpt) { | ||
9360 | + field = field || me.getField(el); | ||
9361 | + msgOpt = msgOpt || {}; | ||
9362 | + | ||
9363 | + var me = this, | ||
9364 | + msg, | ||
9365 | + rule, | ||
9366 | + method = field._r, | ||
9367 | + timely = field.timely, | ||
9368 | + special = timely === 9 || timely === 8, | ||
9369 | + transfer, | ||
9370 | + temp, | ||
9371 | + isValid = false; | ||
9372 | + | ||
9373 | + // use null to break validation from a field | ||
9374 | + if (ret === null) { | ||
9375 | + me._validatedField(el, field, {isValid: true, skip: true}); | ||
9376 | + field._i = 0; | ||
9377 | + return; | ||
9378 | + } | ||
9379 | + else if (ret === undefined) { | ||
9380 | + transfer = true; | ||
9381 | + } | ||
9382 | + else if (ret === true || ret === '') { | ||
9383 | + isValid = true; | ||
9384 | + } | ||
9385 | + else if (isString(ret)) { | ||
9386 | + msg = ret; | ||
9387 | + } | ||
9388 | + else if (isObject(ret)) { | ||
9389 | + if (ret.error) { | ||
9390 | + msg = ret.error; | ||
9391 | + } else { | ||
9392 | + msg = ret.ok; | ||
9393 | + isValid = true; | ||
9394 | + } | ||
9395 | + } | ||
9396 | + | ||
9397 | + rule = field._rules[field._i]; | ||
9398 | + if (rule.not) { | ||
9399 | + msg = undefined; | ||
9400 | + isValid = method === "required" || !isValid; | ||
9401 | + } | ||
9402 | + if (rule.or) { | ||
9403 | + if (isValid) { | ||
9404 | + while ( field._i < field._rules.length && field._rules[field._i].or ) { | ||
9405 | + field._i++; | ||
9406 | + } | ||
9407 | + } else { | ||
9408 | + transfer = true; | ||
9409 | + } | ||
9410 | + } | ||
9411 | + else if (rule.and) { | ||
9412 | + if (!field.isValid) transfer = true; | ||
9413 | + } | ||
9414 | + | ||
9415 | + if (transfer) { | ||
9416 | + isValid = true; | ||
9417 | + } | ||
9418 | + // message analysis, and throw rule level event | ||
9419 | + else { | ||
9420 | + if (isValid) { | ||
9421 | + if (field.showOk !== false) { | ||
9422 | + temp = attr(el, DATA_OK); | ||
9423 | + msg = temp === null ? isString(field.ok) ? field.ok : msg : temp; | ||
9424 | + if (!isString(msg) && isString(field.showOk)) { | ||
9425 | + msg = field.showOk; | ||
9426 | + } | ||
9427 | + if (isString(msg)) { | ||
9428 | + msgOpt.showOk = isValid; | ||
9429 | + } | ||
9430 | + } | ||
9431 | + } | ||
9432 | + if (!isValid || special) { | ||
9433 | + /* rule message priority: | ||
9434 | + 1. custom DOM message | ||
9435 | + 2. custom field message; | ||
9436 | + 3. global defined message; | ||
9437 | + 4. rule returned message; | ||
9438 | + 5. default message; | ||
9439 | + */ | ||
9440 | + msg = (_getDataMsg(el, field, msg || rule.msg || me.messages[method]) || me.messages.fallback).replace(/\{0\|?([^\}]*)\}/, function(m, defaultDisplay){ | ||
9441 | + return me._getDisplay(el, field.display) || defaultDisplay || me.messages[0]; | ||
9442 | + }); | ||
9443 | + } | ||
9444 | + if (!isValid) field.isValid = isValid; | ||
9445 | + msgOpt.msg = msg; | ||
9446 | + $(el).trigger( (isValid ? 'valid' : 'invalid') + CLS_NS_RULE, [method, msg]); | ||
9447 | + } | ||
9448 | + | ||
9449 | + if (special && (!transfer || rule.and)) { | ||
9450 | + if (!isValid && !field._m) field._m = msg; | ||
9451 | + field._v = field._v || []; | ||
9452 | + field._v.push({ | ||
9453 | + type: isValid ? !transfer ? 'ok' : 'tip' : 'error', | ||
9454 | + msg: msg || rule.msg | ||
9455 | + }); | ||
9456 | + } | ||
9457 | + | ||
9458 | + me._debug('log', ' ' + field._i + ': ' + method + ' => ' + (isValid || msg)); | ||
9459 | + | ||
9460 | + // the current rule has passed, continue to validate | ||
9461 | + if ( (isValid || special) && field._i < field._rules.length - 1) { | ||
9462 | + field._i++; | ||
9463 | + me._checkRule(el, field); | ||
9464 | + } | ||
9465 | + // field was invalid, or all fields was valid | ||
9466 | + else { | ||
9467 | + field._i = 0; | ||
9468 | + | ||
9469 | + if (special) { | ||
9470 | + msgOpt.isValid = field.isValid; | ||
9471 | + msgOpt.result = field._v; | ||
9472 | + msgOpt.msg = field._m || ''; | ||
9473 | + if (!field.value && (field._e === 'focusin')) { | ||
9474 | + msgOpt.type = 'tip'; | ||
9475 | + } | ||
9476 | + } else { | ||
9477 | + msgOpt.isValid = isValid; | ||
9478 | + } | ||
9479 | + | ||
9480 | + me._validatedField(el, field, msgOpt); | ||
9481 | + delete field._m; | ||
9482 | + delete field._v; | ||
9483 | + } | ||
9484 | + }, | ||
9485 | + | ||
9486 | + // Verify a rule form a field | ||
9487 | + _checkRule: function(el, field) { | ||
9488 | + var me = this, | ||
9489 | + ret, | ||
9490 | + fn, | ||
9491 | + old, | ||
9492 | + key = field.key, | ||
9493 | + rule = field._rules[field._i], | ||
9494 | + method = rule.method, | ||
9495 | + params = rule.params; | ||
9496 | + | ||
9497 | + // request has been sent, wait it | ||
9498 | + if (me.submiting && me.deferred[key]) { | ||
9499 | + return; | ||
9500 | + } | ||
9501 | + old = field.old; | ||
9502 | + field._r = method; | ||
9503 | + | ||
9504 | + if (old && !field.must && !rule.must && rule.result !== undefined && | ||
9505 | + old.ruleName === method && old.id === el.id && | ||
9506 | + field.value && old.value === field.value ) | ||
9507 | + { | ||
9508 | + // get result from cache | ||
9509 | + ret = rule.result; | ||
9510 | + } | ||
9511 | + else { | ||
9512 | + // get result from current rule | ||
9513 | + fn = _getDataRule(el, method) || me.rules[method] || noop; | ||
9514 | + ret = fn.call(field, el, params, field); | ||
9515 | + if (fn.msg) rule.msg = fn.msg; | ||
9516 | + } | ||
9517 | + | ||
9518 | + // asynchronous validation | ||
9519 | + if (isObject(ret) && isFunction(ret.then)) { | ||
9520 | + me.deferred[key] = ret; | ||
9521 | + | ||
9522 | + // whether the field valid is unknown | ||
9523 | + field.isValid = undefined; | ||
9524 | + | ||
9525 | + // show loading message | ||
9526 | + !me.checkOnly && me.showMsg(el, { | ||
9527 | + type: 'loading', | ||
9528 | + msg: me.messages.loading | ||
9529 | + }, field); | ||
9530 | + | ||
9531 | + // waiting to parse the response data | ||
9532 | + ret.then( | ||
9533 | + function(d, textStatus, jqXHR) { | ||
9534 | + var data = trim(jqXHR.responseText), | ||
9535 | + result, | ||
9536 | + dataFilter = field.dataFilter; | ||
9537 | + | ||
9538 | + // detect if data is json or jsonp format | ||
9539 | + if (/jsonp?/.test(this.dataType)) { | ||
9540 | + data = d; | ||
9541 | + } else if (data.charAt(0) === '{') { | ||
9542 | + data = $.parseJSON(data); | ||
9543 | + } | ||
9544 | + | ||
9545 | + // filter data | ||
9546 | + result = dataFilter.call(this, data, field); | ||
9547 | + if (result === undefined) result = dataFilter.call(this, data.data, field); | ||
9548 | + | ||
9549 | + rule.data = this.data; | ||
9550 | + rule.result = field.old ? result : undefined; | ||
9551 | + me._validatedRule(el, field, result); | ||
9552 | + }, | ||
9553 | + function(jqXHR, textStatus){ | ||
9554 | + me._validatedRule(el, field, me.messages[textStatus] || textStatus); | ||
9555 | + } | ||
9556 | + ).always(function(){ | ||
9557 | + delete me.deferred[key]; | ||
9558 | + }); | ||
9559 | + } | ||
9560 | + // other result | ||
9561 | + else { | ||
9562 | + me._validatedRule(el, field, ret); | ||
9563 | + } | ||
9564 | + }, | ||
9565 | + | ||
9566 | + // Processing the validation | ||
9567 | + _validate: function(el, field) { | ||
9568 | + var me = this; | ||
9569 | + | ||
9570 | + // doesn't validate the element that has "disabled" or "novalidate" attribute | ||
9571 | + if ( el.disabled || attr(el, NOVALIDATE) !== null ) { | ||
9572 | + return; | ||
9573 | + } | ||
9574 | + | ||
9575 | + field = field || me.getField(el); | ||
9576 | + if (!field) return; | ||
9577 | + if (!field._rules) me._parse(el); | ||
9578 | + if (!field._rules) return; | ||
9579 | + | ||
9580 | + me._debug('info', field.key); | ||
9581 | + | ||
9582 | + field.isValid = true; | ||
9583 | + field.element = el; | ||
9584 | + // Cache the value | ||
9585 | + field.value = field.getValue(); | ||
9586 | + | ||
9587 | + // if the field is not required, and that has a blank value | ||
9588 | + if (!field.required && !field.must && !field.value) { | ||
9589 | + if (!_checkable(el)) { | ||
9590 | + me._validatedField(el, field, {isValid: true}); | ||
9591 | + return true; | ||
9592 | + } | ||
9593 | + } | ||
9594 | + | ||
9595 | + me._checkRule(el, field); | ||
9596 | + return field.isValid; | ||
9597 | + }, | ||
9598 | + | ||
9599 | + _debug: function(type, messages) { | ||
9600 | + if (window.console && this.options.debug) { | ||
9601 | + console[type](messages); | ||
9602 | + } | ||
9603 | + }, | ||
9604 | + | ||
9605 | + /** | ||
9606 | + * Detecting whether the value of an element that matches a rule | ||
9607 | + * | ||
9608 | + * @method test | ||
9609 | + * @param {Element} el - input element | ||
9610 | + * @param {String} rule - rule name | ||
9611 | + */ | ||
9612 | + test: function(el, rule) { | ||
9613 | + var me = this, | ||
9614 | + ret, | ||
9615 | + parts = rRule.exec(rule), | ||
9616 | + field, | ||
9617 | + method, | ||
9618 | + params; | ||
9619 | + | ||
9620 | + if (parts) { | ||
9621 | + method = parts[1]; | ||
9622 | + if (method in me.rules) { | ||
9623 | + params = parts[2] || parts[3]; | ||
9624 | + params = params ? params.split(', ') : undefined; | ||
9625 | + field = me.getField(el, true); | ||
9626 | + field._r = method; | ||
9627 | + field.value = field.getValue(); | ||
9628 | + ret = me.rules[method].call(field, el, params); | ||
9629 | + } | ||
9630 | + } | ||
9631 | + | ||
9632 | + return ret === true || ret === undefined || ret === null; | ||
9633 | + }, | ||
9634 | + | ||
9635 | + _getDisplay: function(el, str) { | ||
9636 | + return !isString(str) ? isFunction(str) ? str.call(this, el) : '' : str; | ||
9637 | + }, | ||
9638 | + | ||
9639 | + _getMsgOpt: function(obj, field) { | ||
9640 | + var opt = field ? field : this.options; | ||
9641 | + return $.extend({ | ||
9642 | + type: 'error', | ||
9643 | + pos: _getPos(opt.msgClass), | ||
9644 | + target: opt.target, | ||
9645 | + wrapper: opt.msgWrapper, | ||
9646 | + style: opt.msgStyle, | ||
9647 | + cls: opt.msgClass, | ||
9648 | + arrow: opt.msgArrow, | ||
9649 | + icon: opt.msgIcon | ||
9650 | + }, isString(obj) ? {msg: obj} : obj); | ||
9651 | + }, | ||
9652 | + | ||
9653 | + _getMsgDOM: function(el, msgOpt) { | ||
9654 | + var $el = $(el), $msgbox, datafor, tgt, container; | ||
9655 | + | ||
9656 | + if ( $el.is(INPUT_SELECTOR) ) { | ||
9657 | + tgt = msgOpt.target || attr(el, DATA_TARGET); | ||
9658 | + if (tgt) { | ||
9659 | + tgt = isFunction(tgt) ? tgt.call(this, el) : this.$el.find(tgt); | ||
9660 | + if (tgt.length) { | ||
9661 | + if ( tgt.is(INPUT_SELECTOR) ) { | ||
9662 | + $el = tgt | ||
9663 | + el = tgt.get(0); | ||
9664 | + } else if ( tgt.hasClass(CLS_MSG_BOX) ) { | ||
9665 | + $msgbox = tgt; | ||
9666 | + } else { | ||
9667 | + container = tgt; | ||
9668 | + } | ||
9669 | + } | ||
9670 | + } | ||
9671 | + if (!$msgbox) { | ||
9672 | + datafor = (!_checkable(el) || !el.name) && el.id ? el.id : el.name; | ||
9673 | + $msgbox = this.$el.find(msgOpt.wrapper + '.' + CLS_MSG_BOX + '[for="' + datafor + '"]'); | ||
9674 | + } | ||
9675 | + } else { | ||
9676 | + $msgbox = $el; | ||
9677 | + } | ||
9678 | + | ||
9679 | + // Create new message box | ||
9680 | + if (!msgOpt.hide && !$msgbox.length) { | ||
9681 | + $msgbox = $('<'+ msgOpt.wrapper + '>').attr({ | ||
9682 | + 'class': CLS_MSG_BOX + (msgOpt.cls ? ' ' + msgOpt.cls : ''), | ||
9683 | + 'style': msgOpt.style || undefined, | ||
9684 | + 'for': datafor | ||
9685 | + }); | ||
9686 | + | ||
9687 | + if ( _checkable(el) ) { | ||
9688 | + var $parent = $el.parent(); | ||
9689 | + $msgbox.appendTo( $parent.is('label') ? $parent.parent() : $parent ); | ||
9690 | + } else { | ||
9691 | + if (container) { | ||
9692 | + $msgbox.appendTo(container); | ||
9693 | + } else { | ||
9694 | + $msgbox[!msgOpt.pos || msgOpt.pos === 'right' ? 'insertAfter' : 'insertBefore']($el); | ||
9695 | + } | ||
9696 | + } | ||
9697 | + } | ||
9698 | + | ||
9699 | + return $msgbox; | ||
9700 | + }, | ||
9701 | + | ||
9702 | + /** | ||
9703 | + * Show validation message | ||
9704 | + * | ||
9705 | + * @method showMsg | ||
9706 | + * @param {Element} el - input element | ||
9707 | + * @param {Object} msgOpt | ||
9708 | + */ | ||
9709 | + showMsg: function(el, msgOpt, /*INTERNAL*/ field) { | ||
9710 | + if (!el) return; | ||
9711 | + var me = this, | ||
9712 | + opt = me.options, | ||
9713 | + msgShow, | ||
9714 | + msgMaker, | ||
9715 | + temp, | ||
9716 | + $msgbox; | ||
9717 | + | ||
9718 | + if (isObject(el) && !el.jquery && !msgOpt) { | ||
9719 | + $.each(el, function(key, msg) { | ||
9720 | + var el = me.elements[key] || me.$el.find(_key2selector(key))[0]; | ||
9721 | + me.showMsg(el, msg); | ||
9722 | + }); | ||
9723 | + return; | ||
9724 | + } | ||
9725 | + | ||
9726 | + if ($(el).is(INPUT_SELECTOR)) { | ||
9727 | + field = field || me.getField(el); | ||
9728 | + } | ||
9729 | + | ||
9730 | + if (!(msgMaker = (field || opt).msgMaker)) { | ||
9731 | + return; | ||
9732 | + } | ||
9733 | + | ||
9734 | + msgOpt = me._getMsgOpt(msgOpt, field); | ||
9735 | + el = (el.name && _checkable(el) ? me.$el.find('input[name="'+ el.name +'"]') : $(el)).get(0); | ||
9736 | + | ||
9737 | + // ok or tip | ||
9738 | + if (!msgOpt.msg && msgOpt.type !== 'error') { | ||
9739 | + temp = attr(el, 'data-' + msgOpt.type); | ||
9740 | + if (temp !== null) msgOpt.msg = temp; | ||
9741 | + } | ||
9742 | + | ||
9743 | + if ( !isString(msgOpt.msg) ) { | ||
9744 | + return; | ||
9745 | + } | ||
9746 | + | ||
9747 | + $msgbox = me._getMsgDOM(el, msgOpt); | ||
9748 | + | ||
9749 | + !rPos.test($msgbox[0].className) && $msgbox.addClass(msgOpt.cls); | ||
9750 | + if ( isIE === 6 && msgOpt.pos === 'bottom' ) { | ||
9751 | + $msgbox[0].style.marginTop = $(el).outerHeight() + 'px'; | ||
9752 | + } | ||
9753 | + $msgbox.html( msgMaker.call(me, msgOpt) )[0].style.display = ''; | ||
9754 | + | ||
9755 | + if (isFunction(msgShow = field && field.msgShow || opt.msgShow)) { | ||
9756 | + msgShow.call(me, $msgbox, msgOpt.type); | ||
9757 | + } | ||
9758 | + }, | ||
9759 | + | ||
9760 | + /** | ||
9761 | + * Hide validation message | ||
9762 | + * | ||
9763 | + * @method hideMsg | ||
9764 | + * @param {Element} el - input element | ||
9765 | + * @param {Object} msgOpt optional | ||
9766 | + */ | ||
9767 | + hideMsg: function(el, msgOpt, /*INTERNAL*/ field) { | ||
9768 | + var me = this, | ||
9769 | + opt = me.options, | ||
9770 | + msgHide, | ||
9771 | + $msgbox; | ||
9772 | + | ||
9773 | + el = $(el).get(0); | ||
9774 | + if ($(el).is(INPUT_SELECTOR)) { | ||
9775 | + field = field || me.getField(el); | ||
9776 | + if (field) { | ||
9777 | + if (field.isValid || me.reseting) attr(el, ARIA_INVALID, null); | ||
9778 | + } | ||
9779 | + } | ||
9780 | + | ||
9781 | + msgOpt = me._getMsgOpt(msgOpt, field); | ||
9782 | + msgOpt.hide = true; | ||
9783 | + | ||
9784 | + $msgbox = me._getMsgDOM(el, msgOpt); | ||
9785 | + if (!$msgbox.length) return; | ||
9786 | + | ||
9787 | + if ( isFunction(msgHide = field && field.msgHide || opt.msgHide) ) { | ||
9788 | + msgHide.call(me, $msgbox, msgOpt.type); | ||
9789 | + } else { | ||
9790 | + $msgbox[0].style.display = 'none'; | ||
9791 | + $msgbox[0].innerHTML = null; | ||
9792 | + } | ||
9793 | + }, | ||
9794 | + | ||
9795 | + /** | ||
9796 | + * Get field information | ||
9797 | + * | ||
9798 | + * @method getField | ||
9799 | + * @param {Element} - input element | ||
9800 | + * @return {Object} field | ||
9801 | + */ | ||
9802 | + getField: function(el, must) { | ||
9803 | + var me = this, | ||
9804 | + key, | ||
9805 | + field; | ||
9806 | + | ||
9807 | + if (isString(el)) { | ||
9808 | + key = el; | ||
9809 | + el = undefined; | ||
9810 | + } else { | ||
9811 | + if (attr(el, DATA_RULE)) { | ||
9812 | + return me._parse(el); | ||
9813 | + } | ||
9814 | + if (el.id && '#' + el.id in me.fields || !el.name) { | ||
9815 | + key = '#' + el.id; | ||
9816 | + } else { | ||
9817 | + key = el.name; | ||
9818 | + } | ||
9819 | + } | ||
9820 | + | ||
9821 | + if ( (field = me.fields[key]) || must && (field = new me.Field(key)) ) { | ||
9822 | + field.element = el; | ||
9823 | + } | ||
9824 | + | ||
9825 | + return field; | ||
9826 | + }, | ||
9827 | + | ||
9828 | + /** | ||
9829 | + * Config a field | ||
9830 | + * | ||
9831 | + * @method: setField | ||
9832 | + * @param {String} key | ||
9833 | + * @param {Object} obj | ||
9834 | + */ | ||
9835 | + setField: function(key, obj) { | ||
9836 | + var fields = {}; | ||
9837 | + | ||
9838 | + if (!key) return; | ||
9839 | + | ||
9840 | + // update this field | ||
9841 | + if (isString(key)) { | ||
9842 | + fields[key] = obj; | ||
9843 | + } | ||
9844 | + // update fields | ||
9845 | + else { | ||
9846 | + fields = key; | ||
9847 | + } | ||
9848 | + | ||
9849 | + this._initFields(fields); | ||
9850 | + }, | ||
9851 | + | ||
9852 | + /** | ||
9853 | + * Detecting whether the form is valid | ||
9854 | + * | ||
9855 | + * @method isFormValid | ||
9856 | + * @return {Boolean} | ||
9857 | + */ | ||
9858 | + isFormValid: function() { | ||
9859 | + var fields = this.fields, k, field; | ||
9860 | + for (k in fields) { | ||
9861 | + field = fields[k]; | ||
9862 | + if (!field._rules || !field.required && !field.must && !field.value) continue; | ||
9863 | + if (!field.isValid) return false; | ||
9864 | + } | ||
9865 | + return true; | ||
9866 | + }, | ||
9867 | + | ||
9868 | + /** | ||
9869 | + * Prevent submission form | ||
9870 | + * | ||
9871 | + * @method holdSubmit | ||
9872 | + * @param {Boolean} hold - If set to false, will release the hold | ||
9873 | + */ | ||
9874 | + holdSubmit: function(hold) { | ||
9875 | + this.submiting = hold === undefined || hold; | ||
9876 | + }, | ||
9877 | + | ||
9878 | + /** | ||
9879 | + * Clean validation messages | ||
9880 | + * | ||
9881 | + * @method cleanUp | ||
9882 | + */ | ||
9883 | + cleanUp: function() { | ||
9884 | + this._reset(1); | ||
9885 | + }, | ||
9886 | + | ||
9887 | + /** | ||
9888 | + * Destroy the validation | ||
9889 | + * | ||
9890 | + * @method destroy | ||
9891 | + */ | ||
9892 | + destroy: function() { | ||
9893 | + this._reset(1); | ||
9894 | + this.$el.off(CLS_NS).removeData(NS); | ||
9895 | + attr(this.$el[0], NOVALIDATE, this._NOVALIDATE); | ||
9896 | + } | ||
9897 | + }; | ||
9898 | + | ||
9899 | + /** | ||
9900 | + * Create Field Factory | ||
9901 | + * | ||
9902 | + * @class | ||
9903 | + * @param {Object} context | ||
9904 | + * @return {Function} Factory | ||
9905 | + */ | ||
9906 | + function _createFieldFactory(context) { | ||
9907 | + function FieldFactory() { | ||
9908 | + var options = this.options; | ||
9909 | + for (var i in options) { | ||
9910 | + if (i in fieldDefaults) this[i] = options[i]; | ||
9911 | + } | ||
9912 | + $.extend(this, { | ||
9913 | + _valHook: function() { | ||
9914 | + return this.element.contentEditable === 'true' ? 'text' : 'val'; | ||
9915 | + }, | ||
9916 | + getValue: function() { | ||
9917 | + var elem = this.element; | ||
9918 | + if (elem.type === "number" && elem.validity && elem.validity.badInput) { | ||
9919 | + return 'NaN'; | ||
9920 | + } | ||
9921 | + return $(elem)[this._valHook()](); | ||
9922 | + }, | ||
9923 | + setValue: function(value) { | ||
9924 | + $(this.element)[this._valHook()](this.value = value); | ||
9925 | + }, | ||
9926 | + // Get a range of validation messages | ||
9927 | + getRangeMsg: function(value, params, suffix) { | ||
9928 | + if (!params) return; | ||
9929 | + | ||
9930 | + var me = this, | ||
9931 | + msg = me.messages[me._r] || '', | ||
9932 | + result, | ||
9933 | + p = params[0].split('~'), | ||
9934 | + e = params[1] === 'false', | ||
9935 | + a = p[0], | ||
9936 | + b = p[1], | ||
9937 | + c = 'rg', | ||
9938 | + args = [''], | ||
9939 | + isNumber = trim(value) && +value === +value; | ||
9940 | + | ||
9941 | + function compare(large, small) { | ||
9942 | + return !e ? large >= small : large > small; | ||
9943 | + } | ||
9944 | + | ||
9945 | + if (p.length === 2) { | ||
9946 | + if (a && b) { | ||
9947 | + if (isNumber && compare(value, +a) && compare(+b, value)) { | ||
9948 | + result = true; | ||
9949 | + } | ||
9950 | + args = args.concat(p); | ||
9951 | + c = e ? 'gtlt' : 'rg'; | ||
9952 | + } | ||
9953 | + else if (a && !b) { | ||
9954 | + if (isNumber && compare(value, +a)) { | ||
9955 | + result = true; | ||
9956 | + } | ||
9957 | + args.push(a); | ||
9958 | + c = e ? 'gt' : 'gte'; | ||
9959 | + } | ||
9960 | + else if (!a && b) { | ||
9961 | + if (isNumber && compare(+b, value)) { | ||
9962 | + result = true; | ||
9963 | + } | ||
9964 | + args.push(b); | ||
9965 | + c = e ? 'lt' : 'lte'; | ||
9966 | + } | ||
9967 | + } | ||
9968 | + else { | ||
9969 | + if (value === +a) { | ||
9970 | + result = true; | ||
9971 | + } | ||
9972 | + args.push(a); | ||
9973 | + c = 'eq'; | ||
9974 | + } | ||
9975 | + | ||
9976 | + if (msg) { | ||
9977 | + if (suffix && msg[c + suffix]) { | ||
9978 | + c += suffix; | ||
9979 | + } | ||
9980 | + args[0] = msg[c]; | ||
9981 | + } | ||
9982 | + | ||
9983 | + return result || me._rules && ( me._rules[me._i].msg = me.renderMsg.apply(null, args) ); | ||
9984 | + }, | ||
9985 | + // Render message template | ||
9986 | + renderMsg: function() { | ||
9987 | + var args = arguments, | ||
9988 | + tpl = args[0], | ||
9989 | + i = args.length; | ||
9990 | + | ||
9991 | + if (!tpl) return; | ||
9992 | + | ||
9993 | + while (--i) { | ||
9994 | + tpl = tpl.replace('{' + i + '}', args[i]); | ||
9995 | + } | ||
9996 | + | ||
9997 | + return tpl; | ||
9998 | + } | ||
9999 | + }); | ||
10000 | + } | ||
10001 | + function Field(key, obj, oldField) { | ||
10002 | + this.key = key; | ||
10003 | + this.validator = context; | ||
10004 | + $.extend(this, oldField, obj); | ||
10005 | + } | ||
10006 | + | ||
10007 | + FieldFactory.prototype = context; | ||
10008 | + Field.prototype = new FieldFactory(); | ||
10009 | + | ||
10010 | + return Field; | ||
10011 | + } | ||
10012 | + | ||
10013 | + /** | ||
10014 | + * Create Rules | ||
10015 | + * | ||
10016 | + * @class | ||
10017 | + * @param {Object} obj rules | ||
10018 | + * @param {Object} context context | ||
10019 | + */ | ||
10020 | + function Rules(obj, context) { | ||
10021 | + if (!isObject(obj)) return; | ||
10022 | + | ||
10023 | + var k, that = context ? context === true ? this : context : Rules.prototype; | ||
10024 | + | ||
10025 | + for (k in obj) { | ||
10026 | + if (_checkRuleName(k)) | ||
10027 | + that[k] = _getRule(obj[k]); | ||
10028 | + } | ||
10029 | + } | ||
10030 | + | ||
10031 | + /** | ||
10032 | + * Create Messages | ||
10033 | + * | ||
10034 | + * @class | ||
10035 | + * @param {Object} obj rules | ||
10036 | + * @param {Object} context context | ||
10037 | + */ | ||
10038 | + function Messages(obj, context) { | ||
10039 | + if (!isObject(obj)) return; | ||
10040 | + | ||
10041 | + var k, that = context ? context === true ? this : context : Messages.prototype; | ||
10042 | + | ||
10043 | + for (k in obj) { | ||
10044 | + that[k] = obj[k]; | ||
10045 | + } | ||
10046 | + } | ||
10047 | + | ||
10048 | + // Rule converted factory | ||
10049 | + function _getRule(fn) { | ||
10050 | + switch ($.type(fn)) { | ||
10051 | + case 'function': | ||
10052 | + return fn; | ||
10053 | + case 'array': | ||
10054 | + var f = function() { | ||
10055 | + return fn[0].test(this.value) || fn[1] || false; | ||
10056 | + }; | ||
10057 | + f.msg = fn[1]; | ||
10058 | + return f; | ||
10059 | + case 'regexp': | ||
10060 | + return function() { | ||
10061 | + return fn.test(this.value); | ||
10062 | + }; | ||
10063 | + } | ||
10064 | + } | ||
10065 | + | ||
10066 | + // Get instance by an element | ||
10067 | + function _getInstance(el) { | ||
10068 | + var wrap, k, options; | ||
10069 | + | ||
10070 | + if (!el || !el.tagName) return; | ||
10071 | + | ||
10072 | + switch (el.tagName) { | ||
10073 | + case 'INPUT': | ||
10074 | + case 'SELECT': | ||
10075 | + case 'TEXTAREA': | ||
10076 | + case 'BUTTON': | ||
10077 | + case 'FIELDSET': | ||
10078 | + wrap = el.form || $(el).closest('.' + CLS_WRAPPER); | ||
10079 | + break; | ||
10080 | + case 'FORM': | ||
10081 | + wrap = el; | ||
10082 | + break; | ||
10083 | + default: | ||
10084 | + wrap = $(el).closest('.' + CLS_WRAPPER); | ||
10085 | + } | ||
10086 | + | ||
10087 | + for (k in preinitialized) { | ||
10088 | + if ($(wrap).is(k)) { | ||
10089 | + options = preinitialized[k]; | ||
10090 | + break; | ||
10091 | + } | ||
10092 | + } | ||
10093 | + | ||
10094 | + return $(wrap).data(NS) || $(wrap)[NS](options).data(NS); | ||
10095 | + } | ||
10096 | + | ||
10097 | + // Get custom rules on the node | ||
10098 | + function _getDataRule(el, method) { | ||
10099 | + var fn = trim(attr(el, DATA_RULE + '-' + method)); | ||
10100 | + | ||
10101 | + if ( fn && (fn = new Function("return " + fn)()) ) { | ||
10102 | + return _getRule(fn); | ||
10103 | + } | ||
10104 | + } | ||
10105 | + | ||
10106 | + // Get custom messages on the node | ||
10107 | + function _getDataMsg(el, field, m) { | ||
10108 | + var msg = field.msg, | ||
10109 | + item = field._r; | ||
10110 | + | ||
10111 | + if ( isObject(msg) ) msg = msg[item]; | ||
10112 | + if ( !isString(msg) ) { | ||
10113 | + msg = attr(el, DATA_MSG + '-' + item) || attr(el, DATA_MSG) || ( m ? isString(m) ? m : m[item] : ''); | ||
10114 | + } | ||
10115 | + | ||
10116 | + return msg; | ||
10117 | + } | ||
10118 | + | ||
10119 | + // Get message position | ||
10120 | + function _getPos(str) { | ||
10121 | + var pos; | ||
10122 | + | ||
10123 | + if (str) pos = rPos.exec(str); | ||
10124 | + return pos && pos[0]; | ||
10125 | + } | ||
10126 | + | ||
10127 | + // Check whether the element is checkbox or radio | ||
10128 | + function _checkable(el) { | ||
10129 | + return el.tagName === 'INPUT' && el.type === 'checkbox' || el.type === 'radio'; | ||
10130 | + } | ||
10131 | + | ||
10132 | + // Parse date string to timestamp | ||
10133 | + function _parseDate(str) { | ||
10134 | + return Date.parse(str.replace(/\.|\-/g, '/')); | ||
10135 | + } | ||
10136 | + | ||
10137 | + // Rule name only allows alphanumeric characters and underscores | ||
10138 | + function _checkRuleName(name) { | ||
10139 | + return /^\w+$/.test(name); | ||
10140 | + } | ||
10141 | + | ||
10142 | + // Translate field key to jQuery selector. | ||
10143 | + function _key2selector(key) { | ||
10144 | + var isID = key.charAt(0) === "#"; | ||
10145 | + key = key.replace(/([:.{(|)}/\[\]])/g, "\\$1"); | ||
10146 | + return isID ? key : '[name="'+ key +'"]:first'; | ||
10147 | + } | ||
10148 | + | ||
10149 | + | ||
10150 | + // Fixed a issue cause by refresh page in IE. | ||
10151 | + $(window).on('beforeunload', function(){ | ||
10152 | + this.focus(); | ||
10153 | + }); | ||
10154 | + | ||
10155 | + $(document) | ||
10156 | + .on('click', ':submit', function(){ | ||
10157 | + var input = this, attrNode; | ||
10158 | + if (!input.form) return; | ||
10159 | + // Shim for "formnovalidate" | ||
10160 | + attrNode = input.getAttributeNode('formnovalidate'); | ||
10161 | + if (attrNode && attrNode.nodeValue !== null || attr(input, NOVALIDATE)!== null) { | ||
10162 | + novalidateonce = true; | ||
10163 | + } | ||
10164 | + }) | ||
10165 | + // Automatic initializing form validation | ||
10166 | + .on('focusin submit validate', 'form,.'+CLS_WRAPPER, function(e) { | ||
10167 | + if ( attr(this, NOVALIDATE) !== null ) return; | ||
10168 | + var $form = $(this), me; | ||
10169 | + | ||
10170 | + if ( !$form.data(NS) && (me = _getInstance(this)) ) { | ||
10171 | + if ( !$.isEmptyObject(me.fields) ) { | ||
10172 | + // Execute event handler | ||
10173 | + if (e.type === 'focusin') { | ||
10174 | + me._focusin(e); | ||
10175 | + } else { | ||
10176 | + me._submit(e); | ||
10177 | + } | ||
10178 | + } else { | ||
10179 | + attr(this, NOVALIDATE, NOVALIDATE); | ||
10180 | + $form.off(CLS_NS).removeData(NS); | ||
10181 | + } | ||
10182 | + } | ||
10183 | + }); | ||
10184 | + | ||
10185 | + new Messages({ | ||
10186 | + fallback: "This field is not valid.", | ||
10187 | + loading: 'Validating...' | ||
10188 | + }); | ||
10189 | + | ||
10190 | + | ||
10191 | + // Built-in rules (global) | ||
10192 | + new Rules({ | ||
10193 | + | ||
10194 | + /** | ||
10195 | + * required | ||
10196 | + * | ||
10197 | + * @example: | ||
10198 | + required | ||
10199 | + required(anotherRule) | ||
10200 | + required(not, -1) | ||
10201 | + required(from, .contact) | ||
10202 | + */ | ||
10203 | + required: function(element, params) { | ||
10204 | + var me = this, | ||
10205 | + val = trim(me.value), | ||
10206 | + isValid = true; | ||
10207 | + | ||
10208 | + if (params) { | ||
10209 | + if ( params.length === 1 ) { | ||
10210 | + if ( !_checkRuleName(params[0]) ) { | ||
10211 | + if (!val && !$(params[0], me.$el).length ) { | ||
10212 | + return null; | ||
10213 | + } | ||
10214 | + } | ||
10215 | + else if ( me.rules[params[0]] ) { | ||
10216 | + if ( !val && !me.test(element, params[0]) ) { | ||
10217 | + attr(element, ARIA_REQUIRED, null); | ||
10218 | + return null; | ||
10219 | + } else { | ||
10220 | + attr(element, ARIA_REQUIRED, true); | ||
10221 | + } | ||
10222 | + } | ||
10223 | + } | ||
10224 | + else if ( params[0] === 'not' ) { | ||
10225 | + $.each(params.slice(1), function() { | ||
10226 | + return (isValid = val !== trim(this)); | ||
10227 | + }); | ||
10228 | + } | ||
10229 | + else if ( params[0] === 'from' ) { | ||
10230 | + var $elements = me.$el.find(params[1]), | ||
10231 | + VALIDATED = '_validated_', | ||
10232 | + ret; | ||
10233 | + | ||
10234 | + isValid = $elements.filter(function(){ | ||
10235 | + var field = me.getField(this); | ||
10236 | + return field && !!trim(field.getValue()); | ||
10237 | + }).length >= (params[2] || 1); | ||
10238 | + | ||
10239 | + if (isValid) { | ||
10240 | + if (!val) ret = null; | ||
10241 | + } else { | ||
10242 | + ret = _getDataMsg($elements[0], me) || false; | ||
10243 | + } | ||
10244 | + | ||
10245 | + if ( !$(element).data(VALIDATED) ) { | ||
10246 | + $elements.data(VALIDATED, 1).each(function(){ | ||
10247 | + if (element !== this) { | ||
10248 | + me._validate(this); | ||
10249 | + } | ||
10250 | + }).removeData(VALIDATED); | ||
10251 | + } | ||
10252 | + | ||
10253 | + return ret; | ||
10254 | + } | ||
10255 | + } | ||
10256 | + | ||
10257 | + return isValid && !!val; | ||
10258 | + }, | ||
10259 | + | ||
10260 | + /** | ||
10261 | + * integer | ||
10262 | + * | ||
10263 | + * @example: | ||
10264 | + integer | ||
10265 | + integer[+] | ||
10266 | + integer[+0] | ||
10267 | + integer[-] | ||
10268 | + integer[-0] | ||
10269 | + */ | ||
10270 | + integer: function(element, params) { | ||
10271 | + var re, z = '0|', | ||
10272 | + p = '[1-9]\\d*', | ||
10273 | + key = params ? params[0] : '*'; | ||
10274 | + | ||
10275 | + switch (key) { | ||
10276 | + case '+': | ||
10277 | + re = p; | ||
10278 | + break; | ||
10279 | + case '-': | ||
10280 | + re = '-' + p; | ||
10281 | + break; | ||
10282 | + case '+0': | ||
10283 | + re = z + p; | ||
10284 | + break; | ||
10285 | + case '-0': | ||
10286 | + re = z + '-' + p; | ||
10287 | + break; | ||
10288 | + default: | ||
10289 | + re = z + '-?' + p; | ||
10290 | + } | ||
10291 | + re = '^(?:' + re + ')$'; | ||
10292 | + | ||
10293 | + return new RegExp(re).test(this.value) || this.messages.integer[key]; | ||
10294 | + }, | ||
10295 | + | ||
10296 | + /** | ||
10297 | + * match another field | ||
10298 | + * | ||
10299 | + * @example: | ||
10300 | + match[password] Match the password field (two values must be the same) | ||
10301 | + match[eq, password] Ditto | ||
10302 | + match[neq, count] The value must be not equal to the value of the count field | ||
10303 | + match[lt, count] The value must be less than the value of the count field | ||
10304 | + match[lte, count] The value must be less than or equal to the value of the count field | ||
10305 | + match[gt, count] The value must be greater than the value of the count field | ||
10306 | + match[gte, count] The value must be greater than or equal to the value of the count field | ||
10307 | + match[gte, startDate, date] | ||
10308 | + match[gte, startTime, time] | ||
10309 | + **/ | ||
10310 | + match: function(element, params) { | ||
10311 | + if (!params) return; | ||
10312 | + | ||
10313 | + var me = this, | ||
10314 | + a, b, | ||
10315 | + key, msg, type = 'eq', parser, | ||
10316 | + selector2, elem2, field2; | ||
10317 | + | ||
10318 | + if (params.length === 1) { | ||
10319 | + key = params[0]; | ||
10320 | + } else { | ||
10321 | + type = params[0]; | ||
10322 | + key = params[1]; | ||
10323 | + } | ||
10324 | + | ||
10325 | + selector2 = _key2selector(key); | ||
10326 | + elem2 = me.$el.find(selector2)[0]; | ||
10327 | + // If the compared field is not exist | ||
10328 | + if (!elem2) return; | ||
10329 | + field2 = me.getField(elem2); | ||
10330 | + a = me.value; | ||
10331 | + b = field2.getValue(); | ||
10332 | + | ||
10333 | + if (!me._match) { | ||
10334 | + me.$el.on('valid'+CLS_NS_FIELD+CLS_NS, selector2, function(){ | ||
10335 | + $(element).trigger('validate'); | ||
10336 | + }); | ||
10337 | + me._match = field2._match = 1; | ||
10338 | + } | ||
10339 | + | ||
10340 | + // If both fields are blank | ||
10341 | + if (!me.required && a === "" && b === "") { | ||
10342 | + return null; | ||
10343 | + } | ||
10344 | + | ||
10345 | + parser = params[2]; | ||
10346 | + if (parser) { | ||
10347 | + if (/^date(time)?$/i.test(parser)) { | ||
10348 | + a = _parseDate(a); | ||
10349 | + b = _parseDate(b); | ||
10350 | + } else if (parser === 'time') { | ||
10351 | + a = +a.replace(/:/g, ''); | ||
10352 | + b = +b.replace(/:/g, ''); | ||
10353 | + } | ||
10354 | + } | ||
10355 | + | ||
10356 | + // If the compared field is incorrect, we only ensure that this field is correct. | ||
10357 | + if (type !== "eq" && !isNaN(+a) && isNaN(+b)) { | ||
10358 | + return true; | ||
10359 | + } | ||
10360 | + | ||
10361 | + msg = me.messages.match[type].replace( '{1}', me._getDisplay( element, field2.display || key ) ); | ||
10362 | + | ||
10363 | + switch (type) { | ||
10364 | + case 'lt': | ||
10365 | + return (+a < +b) || msg; | ||
10366 | + case 'lte': | ||
10367 | + return (+a <= +b) || msg; | ||
10368 | + case 'gte': | ||
10369 | + return (+a >= +b) || msg; | ||
10370 | + case 'gt': | ||
10371 | + return (+a > +b) || msg; | ||
10372 | + case 'neq': | ||
10373 | + return (a !== b) || msg; | ||
10374 | + default: | ||
10375 | + return (a === b) || msg; | ||
10376 | + } | ||
10377 | + }, | ||
10378 | + | ||
10379 | + /** | ||
10380 | + * range numbers | ||
10381 | + * | ||
10382 | + * @example: | ||
10383 | + range[0~99] Number 0-99 | ||
10384 | + range[0~] Number greater than or equal to 0 | ||
10385 | + range[~100] Number less than or equal to 100 | ||
10386 | + **/ | ||
10387 | + range: function(element, params) { | ||
10388 | + return this.getRangeMsg(this.value, params); | ||
10389 | + }, | ||
10390 | + | ||
10391 | + /** | ||
10392 | + * how many checkbox or radio inputs that checked | ||
10393 | + * | ||
10394 | + * @example: | ||
10395 | + checked; no empty, same to required | ||
10396 | + checked[1~3] 1-3 items | ||
10397 | + checked[1~] greater than 1 item | ||
10398 | + checked[~3] less than 3 items | ||
10399 | + checked[3] 3 items | ||
10400 | + **/ | ||
10401 | + checked: function(element, params) { | ||
10402 | + if ( !_checkable(element) ) return; | ||
10403 | + | ||
10404 | + var me = this, | ||
10405 | + elem, count; | ||
10406 | + | ||
10407 | + if (element.name) { | ||
10408 | + count = me.$el.find('input[name="' + element.name + '"]').filter(function() { | ||
10409 | + var el = this; | ||
10410 | + if (!elem && _checkable(el)) elem = el; | ||
10411 | + return !el.disabled && el.checked; | ||
10412 | + }).length; | ||
10413 | + } else { | ||
10414 | + elem = element; | ||
10415 | + count = elem.checked; | ||
10416 | + } | ||
10417 | + | ||
10418 | + if (params) { | ||
10419 | + return me.getRangeMsg(count, params); | ||
10420 | + } else { | ||
10421 | + return !!count || _getDataMsg(elem, me, '') || me.messages.required; | ||
10422 | + } | ||
10423 | + }, | ||
10424 | + | ||
10425 | + /** | ||
10426 | + * length of a characters (You can pass the second parameter "true", will calculate the length in bytes) | ||
10427 | + * | ||
10428 | + * @example: | ||
10429 | + length[6~16] 6-16 characters | ||
10430 | + length[6~] Greater than 6 characters | ||
10431 | + length[~16] Less than 16 characters | ||
10432 | + length[~16, true] Less than 16 characters, non-ASCII characters calculating two-character | ||
10433 | + **/ | ||
10434 | + length: function(element, params) { | ||
10435 | + var value = this.value, | ||
10436 | + len = (params[1] === 'true' ? value.replace(rDoubleBytes, 'xx') : value).length; | ||
10437 | + | ||
10438 | + return this.getRangeMsg(len, params, (params[1] ? '_2' : '')); | ||
10439 | + }, | ||
10440 | + | ||
10441 | + /** | ||
10442 | + * remote validation | ||
10443 | + * | ||
10444 | + * @description | ||
10445 | + * remote([get:]url [, name1, [name2 ...]]); | ||
10446 | + * Adaptation three kinds of results (Front for the successful, followed by a failure): | ||
10447 | + 1. text: | ||
10448 | + '' 'Error Message' | ||
10449 | + 2. json: | ||
10450 | + {"ok": ""} {"error": "Error Message"} | ||
10451 | + 3. json wrapper: | ||
10452 | + {"status": 1, "data": {"ok": ""}} {"status": 1, "data": {"error": "Error Message"}} | ||
10453 | + * @example | ||
10454 | + The simplest: remote(path/to/server); | ||
10455 | + With parameters: remote(path/to/server, name1, name2, ...); | ||
10456 | + By GET: remote(get:path/to/server, name1, name2, ...); | ||
10457 | + Name proxy: remote(path/to/server, name1, proxyname2:name2, proxyname3:#id3, ...) | ||
10458 | + Query String remote(path/to/server, foo=1&bar=2, name1, name2, ...) | ||
10459 | + */ | ||
10460 | + remote: function(element, params) { | ||
10461 | + if (!params) return; | ||
10462 | + | ||
10463 | + var me = this, | ||
10464 | + arr = rAjaxType.exec(params[0]), | ||
10465 | + rule = me._rules[me._i], | ||
10466 | + data = {}, | ||
10467 | + queryString = '', | ||
10468 | + url = arr[3], | ||
10469 | + type = arr[2] || 'POST', // GET / POST | ||
10470 | + rType = (arr[1]||'').toLowerCase(), // CORS / JSONP | ||
10471 | + dataType; | ||
10472 | + | ||
10473 | + rule.must = true; | ||
10474 | + data[element.name] = me.value; | ||
10475 | + | ||
10476 | + // There are extra fields | ||
10477 | + if (params[1]) { | ||
10478 | + $.map(params.slice(1), function(name) { | ||
10479 | + var arr, key; | ||
10480 | + if (~name.indexOf('=')) { | ||
10481 | + queryString += '&' + name; | ||
10482 | + } else { | ||
10483 | + arr = name.split(':'); | ||
10484 | + name = trim(arr[0]); | ||
10485 | + key = trim(arr[1]) || name; | ||
10486 | + data[ name ] = me.$el.find( _key2selector(key) ).val(); | ||
10487 | + } | ||
10488 | + }); | ||
10489 | + } | ||
10490 | + | ||
10491 | + data = $.param(data) + queryString; | ||
10492 | + if (!me.must && rule.data && rule.data === data) { | ||
10493 | + return rule.result; | ||
10494 | + } | ||
10495 | + | ||
10496 | + // Cross-domain request, force jsonp dataType | ||
10497 | + if (rType !== 'cors' && /^https?:/.test(url) && !~url.indexOf(location.host)) { | ||
10498 | + dataType = 'jsonp'; | ||
10499 | + } | ||
10500 | + | ||
10501 | + // Asynchronous validation need return jqXHR objects | ||
10502 | + return $.ajax({ | ||
10503 | + url: url, | ||
10504 | + type: type, | ||
10505 | + data: data, | ||
10506 | + dataType: dataType | ||
10507 | + }); | ||
10508 | + }, | ||
10509 | + | ||
10510 | + /** | ||
10511 | + * filter characters, direct filtration without prompting error (support custom regular expressions) | ||
10512 | + * | ||
10513 | + * @example | ||
10514 | + * filter filtering unsafe characters | ||
10515 | + * filter(regexp) filtering the "regexp" matched characters | ||
10516 | + */ | ||
10517 | + filter: function(element, params) { | ||
10518 | + var value = this.value, | ||
10519 | + temp = value.replace( params ? (new RegExp("[" + params[0] + "]", "gm")) : rUnsafe, '' ); | ||
10520 | + if (temp !== value) this.setValue(temp); | ||
10521 | + } | ||
10522 | + }); | ||
10523 | + | ||
10524 | + | ||
10525 | + /** | ||
10526 | + * Config global options | ||
10527 | + * | ||
10528 | + * @static config | ||
10529 | + * @param {Object} options | ||
10530 | + */ | ||
10531 | + Validator.config = function(key, value) { | ||
10532 | + if (isObject(key)) { | ||
10533 | + $.each(key, _config); | ||
10534 | + } | ||
10535 | + else if (isString(key)) { | ||
10536 | + _config(key, value); | ||
10537 | + } | ||
10538 | + | ||
10539 | + function _config(k, o) { | ||
10540 | + if (k === 'rules') { | ||
10541 | + new Rules(o); | ||
10542 | + } | ||
10543 | + else if (k === 'messages') { | ||
10544 | + new Messages(o); | ||
10545 | + } | ||
10546 | + else if (k in fieldDefaults) { | ||
10547 | + fieldDefaults[k] = o; | ||
10548 | + } | ||
10549 | + else { | ||
10550 | + defaults[k] = o; | ||
10551 | + } | ||
10552 | + } | ||
10553 | + }; | ||
10554 | + | ||
10555 | + /** | ||
10556 | + * Config themes | ||
10557 | + * | ||
10558 | + * @static setTheme | ||
10559 | + * @param {String|Object} name | ||
10560 | + * @param {Object} obj | ||
10561 | + * @example | ||
10562 | + .setTheme( themeName, themeOptions ) | ||
10563 | + .setTheme( multiThemes ) | ||
10564 | + */ | ||
10565 | + Validator.setTheme = function(name, obj) { | ||
10566 | + if ( isObject(name) ) { | ||
10567 | + $.extend(true, themes, name); | ||
10568 | + } | ||
10569 | + else if ( isString(name) && isObject(obj) ) { | ||
10570 | + themes[name] = $.extend(themes[name], obj); | ||
10571 | + } | ||
10572 | + }; | ||
10573 | + | ||
10574 | + /** | ||
10575 | + * Resource loader | ||
10576 | + * | ||
10577 | + * @static load | ||
10578 | + * @param {String} str | ||
10579 | + * @example | ||
10580 | + .load('local=zh-CN') // load: local/zh-CN.js and jquery.validator.css | ||
10581 | + .load('local=zh-CN&css=') // load: local/zh-CN.js | ||
10582 | + .load('local&css') // load: local/en.js (set <html lang="en">) and jquery.validator.css | ||
10583 | + .load('local') // dito | ||
10584 | + */ | ||
10585 | + Validator.load = function(str) { | ||
10586 | + if (!str) return; | ||
10587 | + var doc = document, | ||
10588 | + params = {}, | ||
10589 | + node = doc.scripts[0], | ||
10590 | + dir, el, ONLOAD; | ||
10591 | + | ||
10592 | + str.replace(/([^?=&]+)=([^&#]*)/g, function(m, key, value){ | ||
10593 | + params[key] = value; | ||
10594 | + }); | ||
10595 | + | ||
10596 | + dir = params.dir || Validator.dir; | ||
10597 | + | ||
10598 | + if (!Validator.css && params.css !== '') { | ||
10599 | + el = doc.createElement('link'); | ||
10600 | + el.rel = 'stylesheet'; | ||
10601 | + el.href = Validator.css = dir + 'jquery.validator.css'; | ||
10602 | + node.parentNode.insertBefore(el, node); | ||
10603 | + } | ||
10604 | + if (!Validator.local && ~str.indexOf('local') && params.local !== '') { | ||
10605 | + Validator.local = (params.local || doc.documentElement.lang || 'en').replace('_','-'); | ||
10606 | + Validator.pending = 1; | ||
10607 | + el = doc.createElement('script'); | ||
10608 | + el.src = dir + 'local/' + Validator.local + '.js'; | ||
10609 | + ONLOAD = 'onload' in el ? 'onload' : 'onreadystatechange'; | ||
10610 | + el[ONLOAD] = function() { | ||
10611 | + if (!el.readyState || /loaded|complete/.test(el.readyState)) { | ||
10612 | + el = el[ONLOAD] = null; | ||
10613 | + delete Validator.pending; | ||
10614 | + $(window).triggerHandler('validatorready'); | ||
10615 | + } | ||
10616 | + }; | ||
10617 | + node.parentNode.insertBefore(el, node); | ||
10618 | + } | ||
10619 | + }; | ||
10620 | + | ||
10621 | + // Auto loading resources | ||
10622 | + (function(){ | ||
10623 | + var scripts = document.scripts, | ||
10624 | + i = scripts.length, node, arr, | ||
10625 | + re = /(.*validator(?:\.min)?.js)(\?.*(?:local|css|dir)(?:=[\w\-]*)?)?/; | ||
10626 | + | ||
10627 | + while (i-- && !arr) { | ||
10628 | + node = scripts[i]; | ||
10629 | + arr = (node.hasAttribute ? node.src : node.getAttribute('src',4)||'').match(re); | ||
10630 | + } | ||
10631 | + | ||
10632 | + if (!arr) return; | ||
10633 | + Validator.dir = arr[1].split('/').slice(0, -1).join('/')+'/'; | ||
10634 | + Validator.load(arr[2]); | ||
10635 | + })(); | ||
10636 | + | ||
10637 | + return $[NS] = Validator; | ||
10638 | +})); | ||
10639 | + | ||
8495 | define('form',['jquery', 'bootstrap', 'backend', 'config', 'toastr', 'upload', 'validator'], function ($, undefined, Backend, Config, Toastr, Upload, Validator) { | 10640 | define('form',['jquery', 'bootstrap', 'backend', 'config', 'toastr', 'upload', 'validator'], function ($, undefined, Backend, Config, Toastr, Upload, Validator) { |
8496 | var Form = { | 10641 | var Form = { |
8497 | config: { | 10642 | config: { |
-
请 注册 或 登录 后发表评论