Html5实现用户注册自动校验功能实例代码

(编辑:jimmy 日期: 2024/10/30 浏览:2)

抽时间写了一个带有自动校验功能的Html5用户注册Demo。使用到Handlebars模板技术和手机验证码校验。

以下是效果截图:

Html5实现用户注册自动校验功能实例代码

1.页面代码:usersRegister.hbs

XML/HTML Code复制内容到剪贴板
  1. <!DOCTYPE html>     
  2. <!--[if IE 8 ]> <html lang="en" class="ie8"> <![endif]-->     
  3. <!--[if IE 9 ]> <html lang="en" class="ie9"> <![endif]-->     
  4. <!--[if (gt IE 9)|!(IE)]><!-->     
  5. <html lang="en">     
  6. <!--<![endif]-->     
  7. <head>     
  8.     <meta http-equiv="Content-Type" content="text/html; charset=utf-8">     
  9.     <meta http-equiv="X-UA-Compatible" content="IE=edge" />     
  10.     <title>用户注册</title>     
  11.     <!--[if lt IE 9]>     
  12.     <script src="/assets/scripts/html5shiv.js"></script>     
  13.     <![endif]-->     
  14.     <link href="/assets/styles/jquery.idealforms.min.css" rel="stylesheet" media="screen" />     
  15.     <style type="text/css">     
  16.         body {     
  17.             font: normal 15px/1.5 Arial, Helvetica, Free Sans, sans-serif;     
  18.             color: #222;     
  19.             overflow-y: scroll;     
  20.             padding: 60px 0 0 0;     
  21.         }     
  22.         .main {     
  23.             width: 560px;     
  24.             height: 480px;     
  25.             margin: -50px auto;     
  26.         }     
  27.         #my-form {     
  28.             width: 560px;     
  29.             height: 450px;     
  30.             margin: 0 auto;     
  31.             border: 1px solid #ccc;     
  32.             padding: 3em;     
  33.             border-radius: 3px;     
  34.             box-shadow: 0 0 2px rgba(0, 0, 0, .2);     
  35.         }     
  36.     </style>     
  37.     <script type="text/javascript" src="/assets/scripts/jquery-1.8.2.min.js"></script>     
  38.     <script type="text/javascript" src="/assets/scripts/jquery.idealforms.js"></script>     
  39. </head>     
  40. <body>     
  41. <!-- style="background-image: url(static/image/bg.jpg) -->     
  42.     <div class="main" >     
  43.         <div style="height:5px;text-align:center;font-size:25px"> 欢迎您注册!</div>     
  44.         <!-- Begin Form -->     
  45.         <form id="my-form" class="myform">     
  46.             <div>     
  47.                 <label>用户名:</label><input id="username" name="username" type="text" />     
  48.             </div>     
  49.             <div>     
  50.                 <!-- <label>密码:</label><input id="pass" name="password" type="password" /> -->     
  51.                 <label>密码:</label><input id="pass" name="password" type="text" />     
  52.             </div>     
  53.             <div>     
  54.                 <label>邮箱:</label><input id="email" name="email"     
  55.                                          data-ideal="required email" type="email" />     
  56.             </div>     
  57.             <div>     
  58.                 <label>电话:</label><input id="telephone" type="text" name="phone" data-ideal="phone" />     
  59.             </div>     
  60.             <div>     
  61.                 <label>供应商V码:</label><input id="vCode" type="text" name="vCode" data-ideal="vCode" />     
  62.             </div>     
  63.             <div>     
  64.                 <label>真实姓名:</label><input id="trueName" type="text" name="trueName" data-ideal="trueName" />     
  65.             </div>     
  66.             <div>     
  67.                 <label>手机验证码:</label><input id="telCode" type="text" name="telCode" data-ideal="telCode" />     
  68.             </div>     
  69.             <div style="margin-bottom:5px;">     
  70.                 <button id="getTelCode" type="button" style="margin-left:160px; margin-right:auto;" >获取手机校验码</button>     
  71.                 <hr style="margin-top:5px; margin-bottom:5px;" />     
  72.             </div>     
  73.             <!--<div>     
  74.                 <label>性别:</label>     
  75.                 <select id="sex" name="sex">     
  76.                     <option value="男">男</option>     
  77.                     <option value="女">女</option>     
  78.                 </select>     
  79.             </div>     
  80.             <div>     
  81.                 <label>昵称:</label><input id="nickName" type="text" name="nickName" data-ideal="nickName" />     
  82.             </div>     
  83.             <div>     
  84.                 <label>年龄:</label><input id="age" type="text" name="age" data-ideal="age" />     
  85.             </div>-->     
  86.             <!-- <div>     
  87.                 <label>地址:</label><input type="text" name="address" data-ideal="address" />     
  88.             </div>     
  89.             <div>     
  90.                 <label>QQ:</label><input type="text" name="qq" data-ideal="qq" />     
  91.             </div>     
  92.             <div>     
  93.                 <label>邮编:</label><input type="text" name="zip" data-ideal="zip" />     
  94.             </div>     
  95.             <div>     
  96.                 <label>传真:</label><input type="text" name="fax" data-ideal="fax" />     
  97.             </div>     
  98.             <div>     
  99.                 <label>身份证:</label><input type="text" name="creditID" data-ideal="creditID" />     
  100.             </div>     
  101.             <div>     
  102.                 <label>出生日期:</label><input name="date" class="datepicker"     
  103.                     data-ideal="date" type="text" placeholder="月/日/年" />     
  104.             </div>     
  105.             <div>     
  106.                 <label>上传头像:</label><input id="file" name="file" multiple     
  107.                     type="file" />     
  108.             </div>     
  109.             <div>     
  110.                 <label>个人主页:</label><input name="website" data-ideal="url"     
  111.                     type="text" />     
  112.             </div>     
  113.             <div>     
  114.                 <label>备注:</label>     
  115.                 <textarea id="comments" name="comments"></textarea>     
  116.             </div>     
  117.             -->     
  118.             <!-- <div id="languages">     
  119.                 <label>语言:</label> <label><input type="checkbox"     
  120.                     name="langs[]" value="English" />英文</label> <label><input     
  121.                     type="checkbox" name="langs[]" value="Chinese" />中文</label> <label><input     
  122.                     type="checkbox" name="langs[]" value="Spanish" />西班牙文</label> <label><input     
  123.                     type="checkbox" name="langs[]" value="French" />法文</label>     
  124.             </div>     
  125.             <div>     
  126.                 <label>精通几门:</label> <label><input type="radio"     
  127.                     name="radio" checked />1</label> <label><input type="radio"     
  128.                     name="radio" />2</label> <label><input type="radio" name="radio" />3</label>     
  129.                 <label><input type="radio" name="radio" />4</label>     
  130.             </div>     
  131.             <div>     
  132.                 <label>国籍:</label> <select id="states" name="states">     
  133.                     <option value="default">– 选择国籍 –</option>     
  134.                     <option value="AL">阿拉伯</option>     
  135.                     <option value="AK">中国</option>     
  136.                     <option value="AZ">美国</option>     
  137.                     <option value="AR">法国</option>     
  138.                     <option value="CA">英国</option>     
  139.                     <option value="CO">德国</option>     
  140.                     <option value="CT">西班牙</option>     
  141.                     <option value="DE">俄罗斯</option>     
  142.                 </select>     
  143.             </div> -->     
  144.             <div style="margin-top:10px; margin-left:100px;margin-right:100px;">     
  145.                 <button type="button" id="submit" class="submit">提交</button>     
  146.                 <button id="reset" type="button" >重置</button>     
  147.             </div>     
  148.         </form>     
  149.         <!-- End Form -->     
  150.     </div>     
  151. <script type="text/javascript">     
  152.     var options = {     
  153.         onFail : function() {     
  154.             alert($myform.getInvalid().length + ' invalid fields.')     
  155.         },     
  156.         inputs : {     
  157.             'password' : {     
  158.                 filters : 'required pass'     
  159.             },     
  160.             'username' : {     
  161.                 filters : 'required username'     
  162.             },     
  163.             'email' : {     
  164.                 filters : 'required email'     
  165.             },     
  166.             'phone' : {     
  167.                 filters : 'required phone'     
  168.             },     
  169.             'trueName' : {     
  170.                 filters : 'required'     
  171.             },     
  172.             'vCode' : {     
  173.                 filters : 'required'     
  174.             },     
  175.             'telCode' : {     
  176.                 filters : 'required'     
  177.             }     
  178.             /*     
  179.             'age' : {     
  180.                 filters : 'required digits',     
  181.                 data : {     
  182.                    min : 16,     
  183.                    max : 70     
  184.                 }     
  185.             },     
  186.             'file' : {     
  187.                 filters : 'extension',     
  188.                 data : {     
  189.                     extension : [ 'jpg' ]     
  190.                 }     
  191.             },     
  192.             'comments' : {     
  193.                 filters : 'min max',     
  194.                 data : {     
  195.                     min : 50,     
  196.                     max : 200     
  197.                 }     
  198.             },     
  199.             'states' : {     
  200.                 filters : 'exclude',     
  201.                 data : {     
  202.                     exclude : [ 'default' ]     
  203.                 },     
  204.                 errors : {     
  205.                     exclude : '选择国籍.'     
  206.                 }     
  207.             },     
  208.             'langs[]' : {     
  209.                 filters : 'min max',     
  210.                 data : {     
  211.                     min : 2,     
  212.                     max : 3     
  213.                 },     
  214.                 errors : {     
  215.                     min : 'Check at least <strong>2</strong> options.',     
  216.                     max : 'No more than <strong>3</strong> options allowed.'     
  217.                 }     
  218.             }     
  219.             */     
  220.         }     
  221.     };     
  222.     $('#getTelCode').click(function() {     
  223.         var telephone = document.getElementById("telephone").value;   //手机号码     
  224.         if (telephone == null || telephone == ""){     
  225.             alert("手机号码不能为空!");     
  226.         }     
  227.         else{     
  228.             $.ajax({     
  229.                 type : "GET",     
  230.                 dataType : "json",     
  231.                 url : "../api/getTelCode?telephone="+ telephone,     
  232.                 success : function(msg) {     
  233.                 },     
  234.                 error : function(e) {     
  235.                     alert("获取手机校验码失败!" + e);     
  236.                 }     
  237.             });     
  238.         }     
  239.     });     
  240.     var $myform = $('#my-form').idealforms(options).data('idealforms');     
  241.     $('#submit').click(function() {     
  242.         var username = document.getElementById("username").value; //用户名     
  243.         var password = document.getElementById("pass").value;    //密码     
  244.         var email = document.getElementById("email").value;     //邮箱     
  245.         var telephone = document.getElementById("telephone").value;     //手机号码     
  246.         var vCode = document.getElementById("vCode").value;     //公司V码     
  247.         var telCode = document.getElementById("telCode").value;     //手机校验码     
  248.         var trueName = document.getElementById("trueName").value;     //真实姓名     
  249.         $.ajax({     
  250.             type : "GET",     
  251.             url : "../api/usersRegister?username="+ username +"password="+ password +"email="+ email +"telephone="+ telephone +"vCode="+ vCode +"telCode="+ telCode +"trueName="+ trueName,     
  252.             success : function(msg) {     
  253.                //获取当前网址,如: http://localhost:8083/uimcardprj/share/meun.jsp     
  254.                var curWwwPath = window.document.location.href;     
  255.                //获取主机地址之后的目录,如: uimcardprj/share/meun.jsp     
  256.                var pathName = window.document.location.pathname;     
  257.                var pos = curWwwPath.indexOf(pathName);     
  258.                //获取主机地址,如: http://localhost:8083     
  259.                var localhostPaht = curWwwPath.substring(0, pos);     
  260.                //获取带"/"的项目名,如:/uimcardprj     
  261.                var projectName = pathName.substring(0, pathName.substr(1).indexOf('/') + 1);     
  262.                window.location.href = projectName + "/login";     
  263.                alert("注册成功!");     
  264.             },     
  265.             error : function(e) {     
  266.                 alert("注册失败!" + e);     
  267.             }     
  268.         });     
  269.     });     
  270.     $('#reset').click(function() {     
  271.         $myform.reset().fresh().focusFirst();     
  272.     });     
  273. </script>     
  274. </body>     
  275. </html>    

2.jq输入校验:jquery.idealforms.js

该js校验初始版本来自Cedric Ruiz,我略有修改。

部分校验的规则如下:

required: '此处是必填的.'

number: '必须是数字.',

digits: '必须是唯一的数字.'

name: '必须至少有3个字符长,并且只能包含字母.'

username: '用户名最短5位,最长30位,请使用英文字母、数字、中文和下划线. 用户名首字符必须为字母、数字、中文,不能为全数字.中文最长21个字.'

pass: '密码的位数必须的在6-15位之间,并且至少包含一个数字,一个大写字母和一个小写字母.'

strongpass: '必须至少为8个字符长,至少包含一个大写字母和一个小写字母和一个数字或特殊字符.'

email: '必须是一个有效的email地址. <em>(例: user@gmail.com)</em>'

phone: '必须是一个有效的手机号码. <em>(例: 18723101212)</em>'

以下是整个代码文件:

XML/HTML Code复制内容到剪贴板
  1. /*--------------------------------------------------------------------------    
  2.   jq-idealforms 2.1    
  3.   * Author: Cedric Ruiz    
  4.   * License: GPL or MIT    
  5.   * Demo: http://elclanrs.github.com/jq-idealforms/    
  6.   *    
  7. --------------------------------------------------------------------------*/     
  8. ;(function ( $, window, document, undefined ) {     
  9.   'use strict';     
  10.   // Global Ideal Forms namespace     
  11.   $.idealforms = {}     
  12.   $.idealforms.filters = {}     
  13.   $.idealforms.errors = {}     
  14.   $.idealforms.flags = {}     
  15.   $.idealforms.ajaxRequests = {}     
  16. /*--------------------------------------------------------------------------*/     
  17. /**    
  18.  * @namespace A chest for various Utils    
  19.  */     
  20. var Utils = {     
  21.   /**    
  22.    * Get width of widest element in the collection.    
  23.    * @memberOf Utils    
  24.    * @param {jQuery object} $elms    
  25.    * @returns {number}    
  26.    */     
  27.   getMaxWidth: function( $elms ) {     
  28.     var maxWidth = 0     
  29.     $elms.each(function() {     
  30.       var width = $(this).outerWidth()     
  31.       if ( width > maxWidth ) {     
  32.         maxWidth = width     
  33.       }     
  34.     })     
  35.     return maxWidth     
  36.   },     
  37.   /**    
  38.    * Hacky way of getting LESS variables    
  39.    * @memberOf Utils    
  40.    * @param {string} name The name of the LESS class.    
  41.    * @param {string} prop The css property where the data is stored.    
  42.    * @returns {number, string}    
  43.    */     
  44.   getLessVar: function( name, prop ) {     
  45.     var value = $('<p class="' + name + '"></p>').hide().appendTo('body').css( prop )     
  46.     $('.' + name).remove()     
  47.     return ( /^\d+/.test( value ) ? parseInt( value, 10 ) : value )     
  48.   },     
  49.   /**    
  50.    * Like ES5 Object.keys    
  51.    */     
  52.   getKeys: function( obj ) {     
  53.     var keys = []     
  54.     for(var key in obj) {     
  55.       if ( obj.hasOwnProperty( key ) ) {     
  56.         keys.push( key )     
  57.       }     
  58.     }     
  59.     return keys     
  60.   },     
  61.   // Get lenght of an object     
  62.   getObjSize: function( obj ) {     
  63.     var size = 0, key;     
  64.     for ( key in obj ) {     
  65.       if ( obj.hasOwnProperty( key ) ) {     
  66.         size++;     
  67.       }     
  68.     }     
  69.     return size;     
  70.   },     
  71.   isFunction: function( obj ) {     
  72.     return typeof obj === 'function'     
  73.   },     
  74.   isRegex: function( obj ) {     
  75.     return obj instanceof RegExp     
  76.   },     
  77.   isString: function( obj ) {     
  78.     return typeof obj === 'string'     
  79.   },     
  80.   getByNameOrId: function( str ) {     
  81.     var $el = $('[name="'+ str +'"]').length     
  82.       ? $('[name="'+ str +'"]') // by name     
  83.       : $('#'+ str) // by id     
  84.     return $el.length     
  85.       ? $el     
  86.       : $.error('The field "'+ str + '" doesn\'t exist.')     
  87.   },     
  88.   getFieldsFromArray: function( fields ) {     
  89.     var f = []     
  90.     for ( var i = 0, l = fields.length; i < l; i++ ) {     
  91.       f.push( Utils.getByNameOrId( fields[i] ).get(0) )     
  92.     }     
  93.     return $( f )     
  94.   },     
  95.   convertToArray: function( obj ) {     
  96.     return Object.prototype.toString.call( obj ) === '[object Array]'     
  97.       ? obj : [ obj ]     
  98.   },     
  99.   /**    
  100.    * Determine type of any Ideal Forms element    
  101.    * @param $input jQuery $input object    
  102.    */     
  103.   getIdealType: function( $el ) {     
  104.     var type = $el.attr('type') || $el[0].tagName.toLowerCase()     
  105.     return (     
  106.       /(text|password|email|number|search|url|tel|textarea)/.test( type ) && 'text' ||     
  107.       /file/.test( type ) && 'file' ||     
  108.       /select/.test( type ) && 'select' ||     
  109.       /(radio|checkbox)/.test( type ) && 'radiocheck' ||     
  110.       /(button|submit|reset)/.test( type ) && 'button' ||     
  111.       /h\d/.test( type ) && 'heading' ||     
  112.       /hr/.test( type ) && 'separator' ||     
  113.       /hidden/.test( type ) && 'hidden'     
  114.     )     
  115.   },     
  116.   /**    
  117.    * Generates an input    
  118.    * @param name `name` attribute of the input    
  119.    * @param type `type` or `tagName` of the input    
  120.    */     
  121.   makeInput: function( name, value, type, list, placeholder ) {     
  122.     var markup, items = [], item, i, len     
  123.     function splitValue( str ) {     
  124.       var item, value, arr     
  125.       if ( /::/.test( str ) ) {     
  126.         arr = str.split('::')     
  127.         item = arr[ 0 ]     
  128.         value = arr[ 1 ]     
  129.       } else {     
  130.         item = value = str     
  131.       }     
  132.       return { item: item, value: value }     
  133.     }     
  134.     // Text & file     
  135.     if ( /^(text|password|email|number|search|url|tel|file|hidden)$/.test(type) )     
  136.       markup = '<input '+     
  137.         'type="'+ type +'" '+     
  138.         'id="'+ name +'" '+     
  139.         'name="'+ name +'" '+     
  140.         'value="'+ value +'" '+     
  141.         (placeholder && 'placeholder="'+ placeholder +'"') +     
  142.         '/>'     
  143.     // Textarea     
  144.     if ( /textarea/.test( type ) ) {     
  145.       markup = '<textarea id="'+ name +'" name="'+ name +'" value="'+ value +'"></textarea>'     
  146.     }     
  147.     // Select     
  148.     if ( /select/.test( type ) ) {     
  149.       items = []     
  150.       for ( i = 0, len = list.length; i < len; i++ ) {     
  151.         item = splitValue( list[ i ] ).item     
  152.         value = splitValue( list[ i ] ).value     
  153.         items.push('<option value="'+ value +'">'+ item +'</option>')     
  154.       }     
  155.       markup =     
  156.         '<select id="'+ name +'" name="'+ name +'">'+     
  157.           items.join('') +     
  158.         '</select>'     
  159.     }     
  160.     // Radiocheck     
  161.     if ( /(radio|checkbox)/.test( type ) ) {     
  162.       items = []     
  163.       for ( i = 0, len = list.length; i < len; i++ ) {     
  164.         item = splitValue( list[ i ] ).item     
  165.         value = splitValue( list[ i ] ).value     
  166.         items.push(     
  167.           '<label>'+     
  168.             '<input type="'+ type +'" name="'+ name +'" value="'+ value +'" />'+     
  169.             item +     
  170.           '</label>'     
  171.         )     
  172.       }     
  173.       markup = items.join('')     
  174.     }     
  175.     return markup     
  176.   }     
  177. }     
  178. /**    
  179.  * Custom tabs for Ideal Forms    
  180.  */     
  181. $.fn.idealTabs = function (container) {     
  182.   var     
  183.   // Elements     
  184.   $contents = this,     
  185.   $containercontainer = container,     
  186.   $wrapper = $('<ul class="ideal-tabs-wrap"/>'),     
  187.   $tabs = (function () {     
  188.     var tabs = []     
  189.     $contents.each(function () {     
  190.       var name = $(this).attr('name')     
  191.       var html =     
  192.         '<li class="ideal-tabs-tab">'+     
  193.           '<span>' + name + '</span>'+     
  194.           '<i class="ideal-tabs-tab-counter ideal-tabs-tab-counter-zero">0</i>'+     
  195.         '</li>'     
  196.       tabs.push(html)     
  197.     })     
  198.     return $(tabs.join(''))     
  199.   }()),     
  200.   Actions = {     
  201.     getCurIdx: function () {     
  202.       return $tabs     
  203.         .filter('.ideal-tabs-tab-active')     
  204.         .index()     
  205.     },     
  206.     getTabIdxByName: function (name) {     
  207.       var re = new RegExp(name, 'i')     
  208.       var $tab = $tabs.filter(function () {     
  209.         return re.test($(this).text())     
  210.       })     
  211.       return $tab.index()     
  212.     }     
  213.   },     
  214.   /**    
  215.    * Public methods    
  216.    */     
  217.   Methods = {     
  218.     /**    
  219.      * Switch tab    
  220.      */     
  221.     switchTab: function (nameOrIdx) {     
  222.       var idx = Utils.isString(nameOrIdx)     
  223.         ? Actions.getTabIdxByName(nameOrIdx)     
  224.         : nameOrIdx     
  225.       $tabs.removeClass('ideal-tabs-tab-active')     
  226.       $tabs.eq(idx).addClass('ideal-tabs-tab-active')     
  227.       $contents.hide().eq(idx).show()     
  228.     },     
  229.     nextTab: function () {     
  230.       var idx = Actions.getCurIdx() + 1     
  231.       idx > $tabs.length - 1     
  232.         ? Methods.firstTab()     
  233.         : Methods.switchTab(idx)     
  234.     },     
  235.     prevTab: function () {     
  236.       Methods.switchTab(Actions.getCurIdx() - 1)     
  237.     },     
  238.     firstTab: function () {     
  239.       Methods.switchTab(0)     
  240.     },     
  241.     lastTab: function () {     
  242.       Methods.switchTab($tabs.length - 1)     
  243.     },     
  244.     updateCounter: function (nameOrIdx, text) {     
  245.       var idx = !isNaN(nameOrIdx) ? nameOrIdx : Actions.getTabIdxByName(name),     
  246.           $counter = $tabs.eq(idx).find('.ideal-tabs-tab-counter')     
  247.       $counter.removeClass('ideal-tabs-tab-counter-zero')     
  248.       if (!text) {     
  249.         $counter.addClass('ideal-tabs-tab-counter-zero')     
  250.       }     
  251.       $counter.html(text)     
  252.     }     
  253.   }     
  254.   // Attach methods     
  255.   for (var m in Methods)     
  256.     $contents[m] = Methods[m]     
  257.   // Init     
  258.   $tabs.first()     
  259.     .addClass('ideal-tabs-tab-active')     
  260.     .end()     
  261.     .click(function () {     
  262.       var name = $(this).text()     
  263.       $contents.switchTab(name)     
  264.     })     
  265.   // Insert in DOM & Events     
  266.   $wrapper.append($tabs).appendTo($container)     
  267.   $contents.addClass('ideal-tabs-content')     
  268.   $contents.each(function () {     
  269.     var $this = $(this), name = $(this).attr('name')     
  270.     $this.data('ideal-tabs-content-name', name)     
  271.       .removeAttr('name')     
  272.   })     
  273.   $contents.hide().first().show() // Start fresh     
  274.   return $contents     
  275. }     
  276. /**    
  277.  * A custom <select> menu jQuery plugin    
  278.  * @example `$('select').idealSelect()`    
  279.  */     
  280. $.fn.idealSelect = function () {     
  281.   return this.each(function () {     
  282.     var     
  283.     $select = $(this),     
  284.     $options = $select.find('option')     
  285.     /**    
  286.      * Generate markup and return elements of custom select    
  287.      * @memberOf $.fn.toCustomSelect    
  288.      * @returns {object} All elements of the new select replacement    
  289.      */     
  290.     var idealSelect = (function () {     
  291.       var     
  292.       $wrap = $('<ul class="ideal-select '+ $select.attr('name') +'"/>'),     
  293.       $menu = $(     
  294.         '<li><span class="ideal-select-title">' +     
  295.           $options.filter(':selected').text() +     
  296.         '</span></li>'     
  297.       ),     
  298.       items = (function () {     
  299.         var items = []     
  300.         $options.each(function () {     
  301.           var $this = $(this)     
  302.           items.push('<li class="ideal-select-item">' + $this.text() + '</li>')     
  303.         })     
  304.         return items     
  305.       }())     
  306.       $menu.append('<ul class="ideal-select-sub">' + items.join('') + '</ul>')     
  307.       $wrap.append($menu)     
  308.       return {     
  309.         select: $wrap,     
  310.         title: $menu.find('.ideal-select-title'),     
  311.         sub: $menu.find('.ideal-select-sub'),     
  312.         items: $menu.find('.ideal-select-item')     
  313.       }     
  314.     }())     
  315.     /**    
  316.      * @namespace Methods of custom select    
  317.      * @memberOf $.fn.toCustomSelect    
  318.      */     
  319.     var Actions = {     
  320.       getSelectedIdx: function () {     
  321.         return idealSelect.items     
  322.           .filter('.ideal-select-item-selected').index()     
  323.       },     
  324.       /**    
  325.        * @private    
  326.        */     
  327.       init: (function () {     
  328.         $select.css({     
  329.           position: 'absolute',     
  330.           left: '-9999px'     
  331.         })     
  332.         idealSelect.sub.hide()     
  333.         idealSelect.select.insertAfter($select)     
  334.         idealSelect.select.css(     
  335.           'min-width',     
  336.           Utils.getMaxWidth(idealSelect.items)     
  337.         )     
  338.         idealSelect.items     
  339.           .eq($options.filter(':selected').index())     
  340.           .addClass('ideal-select-item-selected')     
  341.       }()),     
  342.       noWindowScroll: function (e) {     
  343.         if (e.which === 40 || e.which === 38 || e.which === 13) {     
  344.           e.preventDefault()     
  345.         }     
  346.       },     
  347.       // Fix loosing focus when scrolling     
  348.       // and selecting item with keyboard     
  349.       focusHack: function () {     
  350.         setTimeout(function () {     
  351.           $select.trigger('focus')     
  352.         }, 1)     
  353.       },     
  354.       focus: function () {     
  355.         idealSelect.select.addClass('ideal-select-focus')     
  356.         $(document).on('keydown.noscroll', Actions.noWindowScroll)     
  357.       },     
  358.       blur: function () {     
  359.         idealSelect.select     
  360.           .removeClass('ideal-select-open ideal-select-focus')     
  361.         $(document).off('.noscroll')     
  362.       },     
  363.       scrollIntoView: function (dir) {     
  364.         var     
  365.         $selected = idealSelect.items.filter('.ideal-select-item-selected'),     
  366.         itemHeight = idealSelect.items.outerHeight(),     
  367.         menuHeight = idealSelect.sub.outerHeight(),     
  368.         isInView = (function () {     
  369.           // relative position to the submenu     
  370.           var elPos = $selected.position().top + itemHeight     
  371.           return dir === 'down'     
  372.             ? elPos <= menuHeight     
  373.             : elPos > 0     
  374.         }())     
  375.         if (!isInView) {     
  376.           itemHeight = (dir === 'down')     
  377.             ? itemHeight // go down     
  378.             : -itemHeight // go up     
  379.           idealSelect.sub     
  380.             .scrollTop(idealSelect.sub.scrollTop() + itemHeight)     
  381.         }     
  382.       },     
  383.       scrollToItem: function () {     
  384.         var idx = Actions.getSelectedIdx(),     
  385.             height = idealSelect.items.outerHeight(),     
  386.             nItems = idealSelect.items.length,     
  387.             allHeight = height * nItems,     
  388.             curHeight = height * (nItems - idx)     
  389.         idealSelect.sub.scrollTop(allHeight - curHeight)     
  390.       },     
  391.       showMenu: function () {     
  392.         idealSelect.sub.fadeIn('fast')     
  393.         idealSelect.select.addClass('ideal-select-open')     
  394.         Actions.select(Actions.getSelectedIdx())     
  395.         Actions.scrollToItem()     
  396.       },     
  397.       hideMenu: function () {     
  398.         idealSelect.sub.hide()     
  399.         idealSelect.select.removeClass('ideal-select-open')     
  400.       },     
  401.       select: function (idx) {     
  402.         idealSelect.items     
  403.           .removeClass('ideal-select-item-selected')     
  404.         idealSelect.items     
  405.           .eq(idx).addClass('ideal-select-item-selected')     
  406.       },     
  407.       change: function (idx) {     
  408.         var text = idealSelect.items.eq(idx).text()     
  409.         Actions.select(idx)     
  410.         idealSelect.title.text(text)     
  411.         $options.eq(idx).prop('selected', true)     
  412.         $select.trigger('change')     
  413.       },     
  414.       keydown: function (key) {     
  415.         var     
  416.         idx = Actions.getSelectedIdx(),     
  417.         isMenu = idealSelect.select.is('.ideal-select-menu'),     
  418.         isOpen = idealSelect.select.is('.ideal-select-open')     
  419.         /**    
  420.          * @namespace Key pressed    
  421.          */     
  422.         var keys = {     
  423.           9: function () { // TAB     
  424.             if (isMenu) {     
  425.               Actions.blur()     
  426.               Actions.hideMenu()     
  427.             }     
  428.           },     
  429.           13: function () { // ENTER     
  430.             if (isMenu)     
  431.               isOpen     
  432.                 ? Actions.hideMenu()     
  433.                 : Actions.showMenu()     
  434.             Actions.change(idx)     
  435.           },     
  436.           27: function () { // ESC     
  437.             if (isMenu) Actions.hideMenu()     
  438.           },     
  439.           40: function () { // DOWN     
  440.             if (idx < $options.length - 1) {     
  441.               isOpen     
  442.                 ? Actions.select(idx + 1)     
  443.                 : Actions.change(idx + 1)     
  444.             }     
  445.             Actions.scrollIntoView('down')     
  446.           },     
  447.           38: function () { // UP     
  448.             if (idx > 0) {     
  449.               isOpen     
  450.                 ? Actions.select(idx - 1)     
  451.                 : Actions.change(idx - 1)     
  452.             }     
  453.             Actions.scrollIntoView('up')     
  454.           },     
  455.           'default': function () { // Letter     
  456.             var     
  457.             letter = String.fromCharCode(key),     
  458.             $matches = idealSelect.items     
  459.               .filter(function () {     
  460.                 return /^\w+$/i.test( letter ) && // not allow modifier keys ( ctrl, cmd, meta, super... )     
  461.                   new RegExp('^' + letter, 'i').test( $(this).text() ) // find first match     
  462.               }),     
  463.             nMatches = $matches.length,     
  464.             counter = idealSelect.select.data('counter') + 1 || 0,     
  465.             curKey = idealSelect.select.data('key') || key,     
  466.             newIdx = $matches.eq(counter).index()     
  467.             if (!nMatches) // No matches     
  468.               return false     
  469.             // If more matches with same letter     
  470.             if (curKey === key) {     
  471.               if (counter < nMatches) {     
  472.                 idealSelect.select.data('counter', counter)     
  473.               }     
  474.               else {     
  475.                 idealSelect.select.data('counter', 0)     
  476.                 newIdx = $matches.eq(0).index()     
  477.               }     
  478.             }     
  479.             // If new letter     
  480.             else {     
  481.               idealSelect.select.data('counter', 0)     
  482.               newIdx = $matches.eq(0).index()     
  483.             }     
  484.             if (isOpen)     
  485.               Actions.select(newIdx)     
  486.             else     
  487.               Actions.change(newIdx)     
  488.             idealSelect.select.data('key', key)     
  489.             Actions.scrollToItem()     
  490.             Actions.focusHack()     
  491.           }     
  492.         }     
  493.         keys[key]     
  494.           ? keys[key]()     
  495.           : keys['default']()     
  496.       }     
  497.     }     
  498.     /**    
  499.      * @namespace Holds all events of custom select for "menu mode" and "list mode"    
  500.      * @memberOf $.fn.toCustomSelect    
  501.      */     
  502.     var events = {     
  503.       focus: Actions.focus,     
  504.       'blur.menu': function () {     
  505.         Actions.blur()     
  506.         Actions.hideMenu()     
  507.       },     
  508.       'blur.list': function () {     
  509.         Actions.blur()     
  510.       },     
  511.       keydown: function (e) {     
  512.         Actions.keydown(e.which)     
  513.       },     
  514.       'clickItem.menu': function () {     
  515.         Actions.change($(this).index())     
  516.         Actions.hideMenu()     
  517.       },     
  518.       'clickItem.list': function () {     
  519.         Actions.change($(this).index())     
  520.       },     
  521.       'clickTitle.menu': function () {     
  522.         Actions.focus()     
  523.         Actions.showMenu()     
  524.         $select.trigger('focus')     
  525.       },     
  526.       'hideOutside.menu': function () {     
  527.         $select.off('blur.menu')     
  528.         $(document).on('mousedown.ideal', function (evt) {     
  529.           if (!$(evt.target).closest(idealSelect.select).length) {     
  530.             $(document).off('mousedown.ideal')     
  531.             $select.on('blur.menu', events['blur.menu'])     
  532.           } else {     
  533.             Actions.focusHack()     
  534.           }     
  535.         })     
  536.       },     
  537.       'mousedown.list': function () {     
  538.         Actions.focusHack()     
  539.       }     
  540.     }     
  541.     // Reset events     
  542.     var disableEvents = function () {     
  543.       idealSelect.select.removeClass('ideal-select-menu ideal-select-list')     
  544.       $select.off('.menu .list')     
  545.       idealSelect.items.off('.menu .list')     
  546.       idealSelect.select.off('.menu .list')     
  547.       idealSelect.title.off('.menu .list')     
  548.     }     
  549.     // Menu mode     
  550.     idealSelect.select.on('menu', function () {     
  551.       disableEvents()     
  552.       idealSelect.select.addClass('ideal-select-menu')     
  553.       Actions.hideMenu()     
  554.       $select.on({     
  555.         'blur.menu': events['blur.menu'],     
  556.         'focus.menu': events.focus,     
  557.         'keydown.menu': events.keydown     
  558.       })     
  559.       idealSelect.select.on('mousedown.menu', events['hideOutside.menu'])     
  560.       idealSelect.items.on('click.menu', events['clickItem.menu'])     
  561.       idealSelect.title.on('click.menu', events['clickTitle.menu'])     
  562.     })     
  563.     // List mode     
  564.     idealSelect.select.on('list', function () {     
  565.       disableEvents()     
  566.       idealSelect.select.addClass('ideal-select-list')     
  567.       Actions.showMenu()     
  568.       $select.on({     
  569.         'blur.list': events['blur.list'],     
  570.         'focus.list': events.focus,     
  571.         'keydown.list': events.keydown     
  572.       })     
  573.       idealSelect.select.on('mousedown.list', events['mousedown.list'])     
  574.       idealSelect.items.on('mousedown.list', events['clickItem.list'])     
  575.     })     
  576.     $select.keydown(function (e) {     
  577.       // Prevent default keydown event     
  578.       // to avoid bugs with Ideal Select events     
  579.       if (e.which !== 9) e.preventDefault()     
  580.     })     
  581.     // Reset     
  582.     idealSelect.select.on('reset', function(){     
  583.       Actions.change(0)     
  584.     })     
  585.     idealSelect.select.trigger('menu') // Default to "menu mode"     
  586.   })     
  587. }     
  588. /*    
  589.  * idealRadioCheck: jQuery plguin for checkbox and radio replacement    
  590.  * Usage: $('input[type=checkbox], input[type=radio]').idealRadioCheck()    
  591.  */     
  592. $.fn.idealRadioCheck = function() {     
  593.   return this.each(function() {     
  594.     var $this = $(this)     
  595.     var $span = $('<span/>')     
  596.     $span.addClass( 'ideal-'+ ( $this.is(':checkbox') ? 'check' : 'radio' ) )     
  597.     $this.is(':checked') && $span.addClass('checked') // init     
  598.     $span.insertAfter( $this )     
  599.     $this.parent('label').addClass('ideal-radiocheck-label')     
  600.       .attr('onclick', '') // Fix clicking label in iOS     
  601.     $this.css({ position: 'absolute', left: '-9999px' }) // hide by shifting left     
  602.     // Events     
  603.     $this.on({     
  604.       change: function() {     
  605.         var $this = $(this)     
  606.         if ( $this.is('input[type="radio"]') ) {     
  607.           $this.parent().siblings('label').find('.ideal-radio').removeClass('checked')     
  608.         }     
  609.         $span.toggleClass( 'checked', $this.is(':checked') )     
  610.       },     
  611.       focus: function() { $span.addClass('focus') },     
  612.       blur: function() { $span.removeClass('focus') },     
  613.       click: function() { $(this).trigger('focus') }     
  614.     })     
  615.   })     
  616. }     
  617. ;(function( $ ) {     
  618.   // Browser supports HTML5 multiple file?     
  619.   var multipleSupport = typeof $('<input/>')[0].multiple !== 'undefined',     
  620.       isIE = /msie/i.test( navigator.userAgent )     
  621.   $.fn.idealFile = function() {     
  622.     return this.each(function() {     
  623.       var $file = $(this).addClass('ideal-file'), // the original file input     
  624.           // label that will be used for IE hack     
  625.           $wrap = $('<div class="ideal-file-wrap">'),     
  626.           $input = $('<input type="text" class="ideal-file-filename" />'),     
  627.           // Button that will be used in non-IE browsers     
  628.           $button = $('<button type="button" class="ideal-file-upload">Open</button>'),     
  629.           // Hack for IE     
  630.           $label = $('<label class="ideal-file-upload" for="'+ $file[0].id +'">Open</label>')     
  631.       // Hide by shifting to the left so we     
  632.       // can still trigger events     
  633.       $file.css({     
  634.         position: 'absolute',     
  635.         left: '-9999px'     
  636.       })     
  637.       $wrap.append( $input, ( isIE ? $label : $button ) ).insertAfter( $file )     
  638.       // Prevent focus     
  639.       $file.attr('tabIndex', -1)     
  640.       $button.attr('tabIndex', -1)     
  641.       $button.click(function () {     
  642.         $file.focus().click() // Open dialog     
  643.       })     
  644.       $file.change(function() {     
  645.         var files = [], fileArr, filename     
  646.         // If multiple is supported then extract     
  647.         // all filenames from the file array     
  648.         if ( multipleSupport ) {     
  649.           fileArr = $file[0].files     
  650.           for ( var i = 0, len = fileArr.length; i < len; i++ ) {     
  651.             files.push( fileArr[i].name )     
  652.           }     
  653.           filename = files.join(', ')     
  654.         // If not supported then just take the value     
  655.         // and remove the path to just show the filename     
  656.         } else {     
  657.           filename = $file.val().split('\\').pop()     
  658.         }     
  659.         $input.val( filename ) // Set the value     
  660.           .attr( 'title', filename ) // Show filename in title tootlip     
  661.       })     
  662.       $input.on({     
  663.         focus: function () { $file.trigger('change') },     
  664.         blur: function () { $file.trigger('blur') },     
  665.         keydown: function( e ) {     
  666.           if ( e.which === 13 ) { // Enter     
  667.             if ( !isIE ) { $file.trigger('click') }     
  668.           } else if ( e.which === 8 || e.which === 46 ) { // Backspace & Del     
  669.             // On some browsers the value is read-only     
  670.             // with this trick we remove the old input and add     
  671.             // a clean clone with all the original events attached     
  672.             $file.replaceWith( $file = $file.val('').clone( true ) )     
  673.             $file.trigger('change')     
  674.             $input.val('')     
  675.           } else if ( e.which === 9 ){ // TAB     
  676.             return     
  677.           } else { // All other keys     
  678.             return false     
  679.           }     
  680.         }     
  681.       })     
  682.     })     
  683.   }     
  684. }( jQuery ))     
  685. /**    
  686.  * @namespace Errors    
  687.  * @locale en    
  688.  */     
  689. $.idealforms.errors = {     
  690.   required: '此处是必填的.',     
  691.   number: '必须是数字.',     
  692.   digits: '必须是唯一的数字.',     
  693.   name: '必须至少有3个字符长,并且只能包含字母.',     
  694.   username: '用户名最短5位,最长30位,请使用英文字母、数字、中文和下划线.用户名首字符必须为字母、数字、中文,不能为全数字.中文最长21个字.',     
  695.   pass: '密码的位数必须的在6-15位之间,并且至少包含一个数字,一个大写字母和一个小写字母.',     
  696.   strongpass: '必须至少为8个字符长,至少包含一个大写字母和一个小写字母和一个数字或特殊字符.',     
  697.   email: '必须是一个有效的email地址. <em>(例: user@gmail.com)</em>',     
  698.   phone: '必须是一个有效的手机号码. <em>(例: 18723101212)</em>',     
  699.   zip: 'Must be a valid US zip code. <em>(e.g. 33245 or 33245-0003)</em>',     
  700.   url: 'Must be a valid URL. <em>(e.g. www.google.com)</em>',     
  701.   minChar: 'Must be at least <strong>{0}</strong> characters long.',     
  702.   minOption: 'Check at least <strong>{0}</strong> options.',     
  703.   maxChar: 'No more than <strong>{0}</strong> characters long.',     
  704.   maxOption: 'No more than <strong>{0}</strong> options allowed.',     
  705.   range: 'Must be a number between {0} and {1}.',     
  706.   date: 'Must be a valid date. <em>(e.g. {0})</em>',     
  707.   dob: 'Must be a valid date of birth.',     
  708.   exclude: '"{0}" is not available.',     
  709.   excludeOption: '{0}',     
  710.   equalto: 'Must be the same value as <strong>"{0}"</strong>',     
  711.   extension: 'File(s) must have a valid extension. <em>(e.g. "{0}")</em>',     
  712.   ajaxSuccess: '<strong>{0}</strong> is not available.',     
  713.   ajaxError: 'Server error...'     
  714. }     
  715. /**    
  716.  * Get all default filters    
  717.  * @returns object    
  718.  */     
  719. var getFilters = function() {     
  720.   var filters = {     
  721.     required: {     
  722.       regex: /.+/,     
  723.       error: $.idealforms.errors.required     
  724.     },     
  725.     number: {     
  726.       regex: function( i, v ) { return !isNaN(v) },     
  727.       error: $.idealforms.errors.number     
  728.     },     
  729.     digits: {     
  730.       regex: /^\d+$/,     
  731.       error: $.idealforms.errors.digits     
  732.     },     
  733.     name: {     
  734.       regex: /^[A-Za-z]{3,}$/,     
  735.       error: $.idealforms.errors.name     
  736.     },     
  737.     username: {     
  738.       regex: /^[a-z](?=[\w.]{4,30}$)\w*\.?\w*$/i,     
  739.       error: $.idealforms.errors.username     
  740.     },     
  741.     pass: {     
  742.       regex: /(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{6,}/,     
  743.       error: $.idealforms.errors.pass     
  744.     },     
  745.     strongpass: {     
  746.       regex: /(?=^.{8,}$)((?=.*\d)|(?=.*\W+))(?![.\n])(?=.*[A-Z])(?=.*[a-z]).*$/,     
  747.       error: $.idealforms.errors.strongpass     
  748.     },     
  749.     email: {     
  750.       regex: /^([a-zA-Z0-9]*[-_.]?[a-zA-Z0-9]+)*@([a-zA-Z0-9]*[-_]?[a-zA-Z0-9]+)+[\\.][A-Za-z]{2,3}([\\.][A-Za-z]{2})?$/,     
  751.       error: $.idealforms.errors.email     
  752.     },     
  753.     phone: {     
  754.       //regex: /^((13[0-9])|(15[0-9])|(17[0-9])|(18[0-9]))\\d{8}$/,     
  755.       regex: /^(0|86|17951)?(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$/,     
  756.       error: $.idealforms.errors.phone     
  757.     },     
  758.     zip: {     
  759.       regex: /^\d{5}$|^\d{5}-\d{4}$/,     
  760.       error: $.idealforms.errors.zip     
  761.     },     
  762.     url: {     
  763.       regex: /^(?:(ftp|http|https):\/\/)?(?:[\w\-]+\.)+[a-z]{2,6}([\:\/?#].*)?$/i,     
  764.       error: $.idealforms.errors.url     
  765.     },     
  766.     min: {     
  767.       regex: function( input, value ) {     
  768.         var $inputinput = input.input,     
  769.             min = input.userOptions.data.min,     
  770.             isRadioCheck = $input.is('[type="checkbox"], [type="radio"]')     
  771.         if ( isRadioCheck ) {     
  772.           this.error = $.idealforms.errors.minOption.replace( '{0}', min )     
  773.           return $input.filter(':checked').length >= min     
  774.         }     
  775.         this.error = $.idealforms.errors.minChar.replace( '{0}', min )     
  776.         return value.length >= min     
  777.       }     
  778.     },     
  779.     max: {     
  780.       regex: function( input, value ) {     
  781.         var $inputinput = input.input,     
  782.             max = input.userOptions.data.max,     
  783.             isRadioCheck = $input.is('[type="checkbox"], [type="radio"]')     
  784.         if ( isRadioCheck ) {     
  785.           this.error = $.idealforms.errors.maxOption.replace( '{0}', max )     
  786.           return $input.filter(':checked').length <= max     
  787.         }     
  788.         this.error = $.idealforms.errors.maxChar.replace( '{0}', max )     
  789.         return value.length <= max     
  790.       }     
  791.     },     
  792.     range: {     
  793.       regex: function( input, value ) {     
  794.         var range = input.userOptions.data.range,     
  795.             val = +value     
  796.         this.error = $.idealforms.errors.range     
  797.           .replace( '{0}', range[0] )     
  798.           .replace( '{1}', range[1] )     
  799.         return val >= range[0] && val <= range[1]     
  800.       }     
  801.     },     
  802.     date: {     
  803.       regex: function( input, value ) {     
  804.         var     
  805.         userFormat =     
  806.           input.userOptions.data && input.userOptions.data.date     
  807.             ? input.userOptions.data.date     
  808.             : 'mm/dd/yyyy', // default format     
  809.         delimiter = /[^mdy]/.exec( userFormat )[0],     
  810.         theFormat = userFormat.split(delimiter),     
  811.         theDate = value.split(delimiter),     
  812.         isDate = function( date, format ) {     
  813.           var m, d, y     
  814.           for ( var i = 0, len = format.length; i < len; i++ ) {     
  815.             if ( /m/.test( format[i]) ) m = date[i]     
  816.             if ( /d/.test( format[i]) ) d = date[i]     
  817.             if ( /y/.test( format[i]) ) y = date[i]     
  818.           }     
  819.           return (     
  820.             m > 0 && m < 13 &&     
  821.             y && y.length === 4 &&     
  822.             d > 0 && d <= ( new Date( y, m, 0 ) ).getDate()     
  823.           )     
  824.         }     
  825.         this.error = $.idealforms.errors.date.replace( '{0}', userFormat )     
  826.         return isDate( theDate, theFormat )     
  827.       }     
  828.     },     
  829.     dob: {     
  830.       regex: function( input, value ) {     
  831.         var     
  832.         userFormat =     
  833.           input.userOptions.data && input.userOptions.data.dob     
  834.             ? input.userOptions.data.dob     
  835.             : 'mm/dd/yyyy', // default format     
  836.         // Simulate a date input     
  837.         dateInput = {     
  838.           input: input.input,     
  839.           userOptions: {     
  840.             data: { date: userFormat }     
  841.           }     
  842.         },     
  843.         // Use internal date filter to validate the date     
  844.         isDate = filters.date.regex( dateInput, value ),     
  845.         // DOB     
  846.         theYear = /\d{4}/.exec( value ),     
  847.         maxYear = new Date().getFullYear(), // Current year     
  848.         minYear = maxYear - 100     
  849.         this.error = $.idealforms.errors.dob     
  850.         return isDate && theYear >= minYear && theYear <= maxYear     
  851.       }     
  852.     },     
  853.     exclude: {     
  854.       regex: function( input, value ) {     
  855.         var $inputinput = input.input,     
  856.             exclude = input.userOptions.data.exclude,     
  857.             isOption = $input.is('[type="checkbox"], [type="radio"], select')     
  858.         this.error = isOption     
  859.           ? $.idealforms.errors.excludeOption.replace( '{0}', value )     
  860.           : this.error = $.idealforms.errors.exclude.replace( '{0}', value )     
  861.         return $.inArray( value, exclude ) === -1     
  862.       }     
  863.     },     
  864.     equalto: {     
  865.       regex: function( input, value ) {     
  866.         var $equals = $( input.userOptions.data.equalto ),     
  867.             $inputinput = input.input,     
  868.             name = $equals.attr('name') || $equals.attr('id'),     
  869.             isValid = $equals.parents('.ideal-field')     
  870.               .filter(function(){ return $(this).data('ideal-isvalid') === true })     
  871.               .length     
  872.         if ( !isValid ) { return false }     
  873.         this.error = $.idealforms.errors.equalto.replace( '{0}', name )     
  874.         return $input.val() === $equals.val()     
  875.       }     
  876.     },     
  877.     extension: {     
  878.       regex: function( input, value ) {     
  879.         var files = input.input[0].files || [{ name: value }],     
  880.             extensions = input.userOptions.data.extension,     
  881.             re = new RegExp( '\\.'+ extensions.join('|') +'$', 'i' ),     
  882.             valid = false     
  883.         for ( var i = 0, len = files.length; i < len; i++ ) {     
  884.           valid = re.test( files[i].name );     
  885.         }     
  886.         this.error = $.idealforms.errors.extension.replace( '{0}', extensions.join('", "') )     
  887.         return valid     
  888.       }     
  889.     },     
  890.     ajax: {     
  891.       regex: function( input, value, showOrHideError ) {     
  892.         var self = this     
  893.         var $inputinput = input.input     
  894.         var userOptions = input.userOptions     
  895.         var name = $input.attr('name')     
  896.         var $field = $input.parents('.ideal-field')     
  897.         var valid = false     
  898.         var customErrors = userOptions.errors && userOptions.errors.ajax     
  899.         self.error = {}     
  900.         self.error.success = customErrors && customErrors.success     
  901.           ? customErrors.success     
  902.           : $.idealforms.errors.ajaxSuccess.replace( '{0}', value )     
  903.         self.error.fail = customErrors && customErrors.error     
  904.           ? customErrors.error     
  905.           : $.idealforms.errors.ajaxError     
  906.         // Send input name as $_POST[name]     
  907.         var data = {}     
  908.         data[ name ] = $.trim( value )     
  909.         // Ajax options defined by the user     
  910.         var userAjaxOps = input.userOptions.data.ajax     
  911.         var ajaxOps = {     
  912.           type: 'post',     
  913.           dataType: 'json',     
  914.           data: data,     
  915.           success: function( resp, text, xhr ) {     
  916.           console.log(resp)     
  917.             showOrHideError( self.error.success, true )     
  918.             $input.data({     
  919.               'ideal-ajax-resp': resp,     
  920.               'ideal-ajax-error': self.error.success     
  921.             })     
  922.             $input.trigger('change') // to update counter     
  923.             $field.removeClass('ajax')     
  924.             // Run custom success callback     
  925.             if( userAjaxOps._success ) {     
  926.               userAjaxOps._success( resp, text, xhr )     
  927.             }     
  928.           },     
  929.           error: function( xhr, text, error ) {     
  930.             if ( text !== 'abort' ) {     
  931.               showOrHideError( self.error.fail, false )     
  932.               $input.data( 'ideal-ajax-error', self.error.fail )     
  933.               $field.removeClass('ajax')     
  934.               // Run custom error callback     
  935.               if ( userAjaxOps._error ) {     
  936.                 userAjaxOps._error( xhr, text, error )     
  937.               }     
  938.             }     
  939.           }     
  940.         }     
  941.         $.extend( ajaxOps, userAjaxOps )     
  942.         // Init     
  943.         $input.removeData('ideal-ajax-error')     
  944.         $input.removeData('ideal-ajax-resp')     
  945.         $field.addClass('ajax')     
  946.         // Run request and save it to be able to abort it     
  947.         // so requests don't bubble     
  948.         $.idealforms.ajaxRequests[ name ] = $.ajax( ajaxOps )     
  949.       }     
  950.     }     
  951.   }     
  952.   return filters     
  953. }     
  954. $.idealforms.flags = {     
  955.   noerror: function (i) {     
  956.     i.parent().siblings('.ideal-error').hide()     
  957.   },     
  958.   noicons: function (i) {     
  959.     i.siblings('.ideal-icon-valid, .ideal-icon-invalid').hide()     
  960.   },     
  961.   novalidicon: function (i) {     
  962.     i.siblings('.ideal-icon-valid').hide()     
  963.   },     
  964.   noinvalidicon: function (i) {     
  965.     i.siblings('.ideal-icon-invalid').hide()     
  966.   },     
  967.   noclass: function (i) {     
  968.     i.parents('.ideal-field').removeClass('valid invalid')     
  969.   },     
  970.   novalidclass: function (i) {     
  971.     i.parents('.ideal-field').removeClass('valid')     
  972.   },     
  973.   noinvalidclass: function (i) {     
  974.     i.parents('.ideal-field').removeClass('invalid')     
  975.   }     
  976. }     
  977. /*    
  978.  * Ideal Forms plugin    
  979.  */     
  980. var _defaults = {     
  981.   inputs: {},     
  982.   customFilters: {},     
  983.   customFlags: {},     
  984.   globalFlags: '',     
  985.   onSuccess: function(e) { alert('Thank you...') },     
  986.   onFail: function() { alert('Invalid!') },     
  987.   responsiveAt: 'auto',     
  988.   disableCustom: ''     
  989. }     
  990. // Constructor     
  991. var IdealForms = function( element, options ) {     
  992.   var self = this     
  993.   self.$form = $( element )     
  994.   self.opts = $.extend( {}, _defaults, options )     
  995.   self.$tabs = self.$form.find('section')     
  996.   // Set localized filters     
  997.   $.extend( $.idealforms.filters, getFilters() )     
  998.   self._init()     
  999. }     
  1000. // Plugin     
  1001. $.fn.idealforms = function( options ) {     
  1002.   return this.each(function() {     
  1003.     if ( !$.data( this, 'idealforms' ) ) {     
  1004.       $.data( this, 'idealforms', new IdealForms( this, options ) )     
  1005.     }     
  1006.   })     
  1007. }     
  1008. // Get LESS variables     
  1009. var LessVars = {     
  1010.   fieldWidth: Utils.getLessVar( 'ideal-field-width', 'width' )     
  1011. }     
  1012. /*    
  1013.  * Private Methods    
  1014.  */     
  1015. $.extend( IdealForms.prototype, {     
  1016.   _init: function() {     
  1017.     var self = this     
  1018.     var o = self.opts     
  1019.     var formElements = self._getFormElements()     
  1020.     self.$form.css( 'visibility', 'visible' )     
  1021.       .addClass('ideal-form')     
  1022.       .attr( 'novalidate', 'novalidate' ) // disable HTML5 validation     
  1023.     // Do markup     
  1024.     formElements.inputs     
  1025.       .add( formElements.headings )     
  1026.       .add( formElements.separators )     
  1027.       .each(function(){ self._doMarkup( $(this) ) })     
  1028.     // Generate tabs     
  1029.     if ( self.$tabs.length ) {     
  1030.       var $tabContainer = $('<div class="ideal-wrap ideal-tabs ideal-full-width"/>')     
  1031.       self.$form.prepend( $tabContainer )     
  1032.       self.$tabs.idealTabs( $tabContainer )     
  1033.     }     
  1034.     // Always show datepicker below the input     
  1035.     if ( jQuery.ui ) {     
  1036.       $.datepicker._checkOffset = function( a,b,c ) { return b }     
  1037.     }     
  1038.     // Add inputs specified by data-ideal     
  1039.     // to the list of user inputs     
  1040.     self.$form.find('[data-ideal]').each(function() {     
  1041.       var userInput = o.inputs[ this.name ]     
  1042.       o.inputs[ this.name ] = userInput || { filters: $(this).data('ideal') }     
  1043.     })     
  1044.    // Responsive     
  1045.     if ( o.responsiveAt ) {     
  1046.       $(window).resize(function(){ self._responsive() })     
  1047.       self._responsive()     
  1048.     }     
  1049.     // Form events     
  1050.     self.$form.on({     
  1051.       keydown: function( e ) {     
  1052.         // Prevent submit when pressing enter     
  1053.         // but exclude textareas     
  1054.         if ( e.which === 13 && e.target.nodeName !== 'TEXTAREA' ) {     
  1055.           e.preventDefault()     
  1056.         }     
  1057.       },     
  1058.       submit: function( e ) {     
  1059.         if ( !self.isValid() ) {     
  1060.           e.preventDefault()     
  1061.           o.onFail()     
  1062.           self.focusFirstInvalid()     
  1063.         } else {     
  1064.           o.onSuccess( e )     
  1065.         }     
  1066.       }     
  1067.     })     
  1068.     self._adjust()     
  1069.     self._attachEvents()     
  1070.     self.fresh() // Start fresh     
  1071.   },     
  1072.   _getFormElements: function() {     
  1073.     return {     
  1074.       inputs: this.$form.find('input, select, textarea, :button'),     
  1075.       labels: this.$form.find('div > label:first-child'),     
  1076.       text: this.$form.find('input:not([type="checkbox"], [type="radio"], [type="submit"]), textarea'),     
  1077.       select: this.$form.find('select'),     
  1078.       radiocheck: this.$form.find('input[type="radio"], input[type="checkbox"]'),     
  1079.       buttons: this.$form.find(':button'),     
  1080.       file: this.$form.find('input[type="file"]'),     
  1081.       headings: this.$form.find('h1, h2, h3, h4, h5, h6'),     
  1082.       separators: this.$form.find('hr'),     
  1083.       hidden: this.$form.find('input:hidden')     
  1084.     }     
  1085.   },     
  1086.   _getUserInputs: function() {     
  1087.     return this.$form.find('[name="'+ Utils.getKeys( this.opts.inputs ).join('"], [name="') +'"]')     
  1088.   },     
  1089.   _getTab: function( nameOrIdx ) {     
  1090.     var self = this     
  1091.     var isNumber = !isNaN( nameOrIdx )     
  1092.     if ( isNumber ) {     
  1093.       return self.$tabs.eq( nameOrIdx )     
  1094.     }     
  1095.     return self.$tabs.filter(function() {     
  1096.       var re = new RegExp( nameOrIdx, 'i' )     
  1097.       return re.test( $(this).data('ideal-tabs-content-name') )     
  1098.     })     
  1099.   },     
  1100.   _getCurrentTabIdx: function() {     
  1101.     return this.$tabs.index( this.$form.find('.ideal-tabs-content:visible') )     
  1102.   },     
  1103.   _updateTabsCounter: function() {     
  1104.     var self = this     
  1105.     self.$tabs.each(function( i ) {     
  1106.       var invalid = self.getInvalidInTab( i ).length     
  1107.       self.$tabs.updateCounter( i, invalid )     
  1108.     })     
  1109.   },     
  1110.   _adjust: function() {     
  1111.     var self = this     
  1112.     var o = self.opts     
  1113.     var formElements = self._getFormElements()     
  1114.     var curTab = self._getCurrentTabIdx()     
  1115.     // Autocomplete causes some problems...     
  1116.     formElements.inputs.attr('autocomplete', 'off')     
  1117.     // Show tabs to calculate dimensions     
  1118.     if ( self.$tabs.length ) { self.$tabs.show() }     
  1119.     // Adjust labels     
  1120.     var labels = formElements.labels     
  1121.     labels.removeAttr('style').width( Utils.getMaxWidth( labels ) )     
  1122.     // Adjust headings and separators     
  1123.     if ( self.$tabs.length ) {     
  1124.       this.$tabs.each(function(){     
  1125.         $( this ).find('.ideal-heading:first').addClass('first-child')     
  1126.       })     
  1127.     } else {     
  1128.       self.$form.find('.ideal-heading:first').addClass('first-child')     
  1129.     }     
  1130.     self._setDatepicker()     
  1131.     // Done calculating hide tabs     
  1132.     if ( self.$tabs.length ) {     
  1133.       self.$tabs.hide()     
  1134.       self.switchTab( curTab )     
  1135.     }     
  1136.   },     
  1137.   _setDatepicker: function() {     
  1138.     var o = this.opts     
  1139.     var $datepicker = this.$form.find('input.datepicker')     
  1140.     if ( jQuery.ui && $datepicker.length ) {     
  1141.       $datepicker.each(function() {     
  1142.         var userInput = o.inputs[ this.name ]     
  1143.         var data = userInput && userInput.data && userInput.data.date     
  1144.         var format = data ? data.replace( 'yyyy', 'yy' ) : 'mm/dd/yy'     
  1145.         $(this).datepicker({     
  1146.           dateFormat: format,     
  1147.           beforeShow: function( input ) {     
  1148.             $( input ).addClass('open')     
  1149.           },     
  1150.           onChangeMonthYear: function() {     
  1151.             // Hack to fix IE9 not resizing     
  1152.             var $this = $(this)     
  1153.             var w = $this.outerWidth() // cache first!     
  1154.             setTimeout(function() {     
  1155.               $this.datepicker('widget').css( 'width', w )     
  1156.             }, 1)     
  1157.           },     
  1158.           onClose: function() { $(this).removeClass('open') }     
  1159.         })     
  1160.       })     
  1161.       // Adjust width     
  1162.       $datepicker.on('focus keyup', function() {     
  1163.         var t = $(this), w = t.outerWidth()     
  1164.         t.datepicker('widget').css( 'width', w )     
  1165.       })     
  1166.       $datepicker.parent().siblings('.ideal-error').addClass('hidden')     
  1167.     }     
  1168.   },     
  1169.   _doMarkup: function( $element ) {     
  1170.     var o = this.opts     
  1171.     var elementType = Utils.getIdealType( $element )     
  1172.     // Validation elements     
  1173.     var $field = $('<span class="ideal-field"/>')     
  1174.     var $error = $('<span class="ideal-error" />')     
  1175.     var $valid = $('<i class="ideal-icon ideal-icon-valid" />')     
  1176.     var $invalid = $('<i class="ideal-icon ideal-icon-invalid"/>')     
  1177.       .click(function(){     
  1178.         $(this).parent().find('input:first, textarea, select').focus()     
  1179.       })     
  1180.     // Basic markup     
  1181.     $element.closest('div').addClass('ideal-wrap')     
  1182.       .children('label:first-child').addClass('ideal-label')     
  1183.     var idealElements = {     
  1184.       _defaultInput: function() {     
  1185.         $element.wrapAll( $field ).after( $valid, $invalid )     
  1186.           .parent().after( $error )     
  1187.       },     
  1188.       text: function() { idealElements._defaultInput() },     
  1189.       radiocheck: function() {     
  1190.         // Check if input is already wrapped so we don't     
  1191.         // wrap radios and checks more than once     
  1192.         var isWrapped = $element.parents('.ideal-field').length     
  1193.         if ( !isWrapped ) {     
  1194.           $element.parent().nextAll().andSelf().wrapAll( $field.addClass('ideal-radiocheck') )     
  1195.           $element.parents('.ideal-field').append( $valid, $invalid ).after( $error )     
  1196.         }     
  1197.         if ( !/radiocheck/.test( o.disableCustom ) ) {     
  1198.           $element.idealRadioCheck()     
  1199.         }     
  1200.       },     
  1201.       select: function() {     
  1202.         idealElements._defaultInput()     
  1203.         if ( !/select/.test( o.disableCustom ) ) {     
  1204.           $element.idealSelect()     
  1205.         }     
  1206.       },     
  1207.       file: function() {     
  1208.         idealElements._defaultInput()     
  1209.         if ( !/file/.test( o.disableCustom ) ) {     
  1210.           $element.idealFile()     
  1211.         }     
  1212.       },     
  1213.       button: function() {     
  1214.         if ( !/button/.test( o.disableCustom ) ) {     
  1215.           $element.addClass('ideal-button')     
  1216.         }     
  1217.       },     
  1218.       hidden: function() {     
  1219.         $element.closest('div').addClass('ideal-hidden')     
  1220.       },     
  1221.       heading: function() {     
  1222.         $element.closest('div').addClass('ideal-full-width')     
  1223.         $element.parent().children().wrapAll('<span class="ideal-heading"/>')     
  1224.       },     
  1225.       separator: function() {     
  1226.         $element.closest('div').addClass('ideal-full-width')     
  1227.         $element.wrapAll('<div class="ideal-separator"/>')     
  1228.       }     
  1229.     }     
  1230.     // Generate markup for current element type     
  1231.     idealElements[ elementType ] ? idealElements[ elementType ]() : $.noop()     
  1232.     $error.add( $valid ).add( $invalid ).hide() // Start fresh     
  1233.   },     
  1234.   /** Validates an input and shows or hides error and icon    
  1235.    * @memberOf Actions    
  1236.    * @param {object} $input jQuery object    
  1237.    * @param {string} e The JavaScript event    
  1238.    */     
  1239.   _validate: function( $input, e ) {     
  1240.     var self = this     
  1241.     var o = this.opts     
  1242.     var userOptions = o.inputs[ $input.attr('name') ]     
  1243.     var userFilters = userOptions.filters && userOptions.filters.split(/\s/)     
  1244.     var name = $input.attr('name')     
  1245.     var value = $input.val()     
  1246.     var ajaxRequest = $.idealforms.ajaxRequests[ name ]     
  1247.     var isRadioCheck = $input.is('[type="checkbox"], [type="radio"]')     
  1248.     var inputData = {     
  1249.       // If is radio or check validate all inputs related by name     
  1250.       input: isRadioCheck ? self.$form.find('[name="' + name + '"]') : $input,     
  1251.       userOptions: userOptions     
  1252.     }     
  1253.     // Validation elements     
  1254.     var $field = $input.parents('.ideal-field')     
  1255.     var $error = $field.siblings('.ideal-error')     
  1256.     var $invalid = isRadioCheck     
  1257.       ? $input.parent().siblings('.ideal-icon-invalid')     
  1258.       : $input.siblings('.ideal-icon-invalid')     
  1259.     var $valid = isRadioCheck     
  1260.       ? $input.parent().siblings('.ideal-icon-valid')     
  1261.       : $input.siblings('.ideal-icon-valid')     
  1262.     function resetError() {     
  1263.       $field.removeClass('valid invalid').removeData('ideal-isvalid')     
  1264.       $error.add( $invalid ).add( $valid ).hide()     
  1265.     }     
  1266.     function showOrHideError( error, valid ) {     
  1267.       resetError()     
  1268.       valid ? $valid.show() : $invalid.show()     
  1269.       $field.addClass( valid ? 'valid' : 'invalid' )     
  1270.       $field.data( 'ideal-isvalid', valid )     
  1271.       if ( !valid ) {     
  1272.         $error.html( error ).toggle( $field.is('.ideal-field-focus') )     
  1273.       }     
  1274.     }     
  1275.     // Prevent validation when typing but not introducing any new characters     
  1276.     // This is mainly to prevent multiple AJAX requests     
  1277.     var oldValue = $input.data('ideal-value') || 0     
  1278.     $input.data( 'ideal-value', value )     
  1279.     if ( e.type === 'keyup' && value === oldValue ) { return false }     
  1280.     // Validate     
  1281.     if ( userFilters ) {     
  1282.       $.each( userFilters, function( i, filter ) {     
  1283.         var theFilter = $.idealforms.filters[ filter ]     
  1284.         var customError = userOptions.errors && userOptions.errors[ filter ]     
  1285.         var error = ''     
  1286.         // If field is empty and not required     
  1287.         if ( !value && filter !== 'required' ) {     
  1288.           resetError()     
  1289.           return false     
  1290.         }     
  1291.         if ( theFilter ) {     
  1292.           // Abort and reset ajax if there's a request pending     
  1293.           if ( e.type === 'keyup' && ajaxRequest ) {     
  1294.             ajaxRequest.abort()     
  1295.             $field.removeClass('ajax')     
  1296.           }     
  1297.           // AJAX     
  1298.           if ( filter === 'ajax' ) {     
  1299.             showOrHideError( error, false ) // set invalid till response comes back     
  1300.             $error.hide()     
  1301.             if ( e.type === 'keyup' ) {     
  1302.               theFilter.regex( inputData, value, showOrHideError ) // runs the ajax callback     
  1303.             } else {     
  1304.               var ajaxError = $input.data('ideal-ajax-error')     
  1305.               if ( ajaxError ) {     
  1306.                 showOrHideError( ajaxError, $input.data('ideal-ajax-resp') || false )     
  1307.               }     
  1308.             }     
  1309.           }     
  1310.           // All other filters     
  1311.           else {     
  1312.             var valid = Utils.isRegex( theFilter.regex ) && theFilter.regex.test( value ) ||     
  1313.                         Utils.isFunction( theFilter.regex ) && theFilter.regex( inputData, value )     
  1314.             error = customError || theFilter.error // assign error after calling regex()     
  1315.             showOrHideError( error, valid )     
  1316.             if ( !valid ) { return false }     
  1317.           }     
  1318.         }     
  1319.       })     
  1320.     }     
  1321.     // Reset if there are no filters     
  1322.     else {     
  1323.       resetError()     
  1324.     }     
  1325.     // Flags     
  1326.     var flags = (function(){     
  1327.       var f = userOptions.flags && userOptions.flags.split(' ') || []     
  1328.       if ( o.globalFlags ) {     
  1329.         $.each( o.globalFlags.split(' '), function( i,v ) { f.push(v) })     
  1330.       }     
  1331.       return f     
  1332.     }())     
  1333.     if ( flags.length ) {     
  1334.       $.each(flags, function( i,f ) {     
  1335.         var theFlag = $.idealforms.flags[f]     
  1336.         if ( theFlag ) { theFlag( $input, e.type ) }     
  1337.       })     
  1338.     }     
  1339.     // Update counter     
  1340.     if ( self.$tabs.length ) {     
  1341.       self._updateTabsCounter( self._getCurrentTabIdx() )     
  1342.     }     
  1343.   },     
  1344.   _attachEvents: function() {     
  1345.     var self = this     
  1346.     self._getUserInputs().on('keyup change focus blur', function(e) {     
  1347.       var $this = $(this)     
  1348.       var $field = $this.parents('.ideal-field')     
  1349.       var isFile = $this.is('input[type=file]')     
  1350.       // Trigger on change if type=file cuz custom file     
  1351.       // disables focus on original file input (tabIndex = -1)     
  1352.       if ( e.type === 'focus' || isFile && e.type === 'change' ) {     
  1353.         $field.addClass('ideal-field-focus')     
  1354.       }     
  1355.       if ( e.type === 'blur' ) {     
  1356.         $field.removeClass('ideal-field-focus')     
  1357.       }     
  1358.       self._validate( $this, e )     
  1359.     })     
  1360.   },     
  1361.   _responsive: function() {     
  1362.     var formElements = this._getFormElements()     
  1363.     var maxWidth = LessVars.fieldWidth + formElements.labels.outerWidth()     
  1364.     var $emptyLabel = formElements.labels.filter(function() {     
  1365.       return $(this).html() === ' '     
  1366.     })     
  1367.     var $customSelect = this.$form.find('.ideal-select')     
  1368.     this.opts.responsiveAt === 'auto'     
  1369.       ? this.$form.toggleClass( 'stack', this.$form.width() < maxWidth )     
  1370.       : this.$form.toggleClass( 'stack', $(window).width() < this.opts.responsiveAt )     
  1371.     var isStack = this.$form.is('.stack')     
  1372.     $emptyLabel.toggle( !isStack )     
  1373.     $customSelect.trigger( isStack ? 'list' : 'menu' )     
  1374.     // Hide datePicker     
  1375.     var $datePicker = this.$form.find('input.hasDatepicker')     
  1376.     if ( $datePicker.length ) { $datePicker.datepicker('hide') }     
  1377.   }     
  1378. })     
  1379. /*    
  1380.  * Public Methods    
  1381.  */     
  1382. $.extend( IdealForms.prototype, {     
  1383.   getInvalid: function() {     
  1384.     return this.$form.find('.ideal-field').filter(function() {     
  1385.       return $(this).data('ideal-isvalid') === false     
  1386.     })     
  1387.   },     
  1388.   getInvalidInTab: function( nameOrIdx ) {     
  1389.     return this._getTab( nameOrIdx ).find('.ideal-field').filter(function() {     
  1390.       return $(this).data('ideal-isvalid') === false     
  1391.     })     
  1392.   },     
  1393.   isValid: function() {     
  1394.     return !this.getInvalid().length     
  1395.   },     
  1396.   isValidField: function( field ) {     
  1397.     var $input = Utils.getByNameOrId( field )     
  1398.     return $input.parents('.ideal-field').data('ideal-isvalid') === true     
  1399.   },     
  1400.   focusFirst: function() {     
  1401.     if ( this.$tabs.length ) {     
  1402.       this.$tabs.filter(':visible')     
  1403.         .find('.ideal-field:first')     
  1404.         .find('input:first, select, textarea').focus()     
  1405.     } else {     
  1406.       this.$form.find('.ideal-field:first')     
  1407.         .find('input:first, select, textarea').focus()     
  1408.     }     
  1409.     return this     
  1410.   },     
  1411.   focusFirstInvalid: function() {     
  1412.     var $first = this.getInvalid().first().find('input:first, select, textarea')     
  1413.     var tabName = $first.parents('.ideal-tabs-content').data('ideal-tabs-content-name')     
  1414.     if ( this.$tabs.length ) {     
  1415.       this.switchTab( tabName )     
  1416.     }     
  1417.     $first.focus()     
  1418.     return this     
  1419.   },     
  1420.   switchTab: function( nameOrIdx ) {     
  1421.     this.$tabs.switchTab( nameOrIdx )     
  1422.     return this     
  1423.   },     
  1424.   nextTab: function() {     
  1425.     this.$tabs.nextTab()     
  1426.     return this     
  1427.   },     
  1428.   prevTab: function() {     
  1429.     this.$tabs.prevTab()     
  1430.     return this     
  1431.   },     
  1432.   firstTab: function() {     
  1433.     this.$tabs.firstTab()     
  1434.     return this     
  1435.   },     
  1436.   lastTab: function() {     
  1437.     this.$tabs.lastTab()     
  1438.     return this     
  1439.   },     
  1440.   fresh: function() {     
  1441.     this._getUserInputs().change().parents('.ideal-field')     
  1442.       .removeClass('valid invalid')     
  1443.     return this     
  1444.   },     
  1445.   freshFields: function( fields ) {     
  1446.     fields = Utils.convertToArray( fields )     
  1447.     $.each( fields, function( i ) {     
  1448.       var $input = Utils.getByNameOrId( fields[ i ] )     
  1449.       $input.change().parents('.ideal-field').removeClass('valid invalid')     
  1450.     })     
  1451.     return this     
  1452.   },     
  1453.   reload: function() {     
  1454.     this._adjust()     
  1455.     this._attachEvents()     
  1456.     return this     
  1457.   },     
  1458.   reset: function() {     
  1459.     var formElements = this._getFormElements()     
  1460.     formElements.text.val('') // text inputs     
  1461.     formElements.radiocheck.removeAttr('checked') // radio & check     
  1462.     // Select and custom select     
  1463.     formElements.select.find('option').first().prop( 'selected', true )     
  1464.     this.$form.find('.ideal-select').trigger('reset')     
  1465.     if ( this.$tabs.length ) { this.firstTab() }     
  1466.     this.focusFirst().fresh()     
  1467.     return this     
  1468.   },     
  1469.   resetFields: function( fields ) {     
  1470.     fields = Utils.convertToArray( fields )     
  1471.     var formElements = this._getFormElements()     
  1472.     $.each( fields, function( i, v ) {     
  1473.       var $input = Utils.getByNameOrId( v )     
  1474.       var type = Utils.getIdealType( $input )     
  1475.       if ( type === 'text' || type === 'file' ) {     
  1476.         $input.val('')     
  1477.       }     
  1478.       if ( type === 'radiocheck' ) {     
  1479.         $input.removeAttr('checked') // radio & check     
  1480.       }     
  1481.       if ( type === 'select' ) {     
  1482.         $input.find('option').first().prop( 'selected', true )     
  1483.         $input.next('.ideal-select').trigger('reset')     
  1484.       }     
  1485.       $input.change()     
  1486.     })     
  1487.     this.freshFields( fields )     
  1488.     return this     
  1489.   },     
  1490.   toggleFields: function( fields ) {     
  1491.     fields = Utils.convertToArray( fields )     
  1492.     var self = this     
  1493.     var $fields = Utils.getFieldsFromArray( fields )     
  1494.     $fields.each(function() {     
  1495.       var $this = $(this)     
  1496.       var name = $this.attr('name') || $this.attr('id')     
  1497.       var input = self.opts.inputs[ name ]     
  1498.       var filters = input && input.filters     
  1499.       var dataFilters = $this.data('ideal-filters') || ''     
  1500.       $this.data( 'ideal-filters', filters )     
  1501.       $this.closest('.ideal-wrap').toggle()     
  1502.       self.setFieldOptions( name, { filters: dataFilters } )     
  1503.     })     
  1504.     return this     
  1505.   },     
  1506.   setOptions: function( options ) {     
  1507.     $.extend( true, this.opts, options )     
  1508.     this.reload().fresh()     
  1509.     return this     
  1510.   },     
  1511.   setFieldOptions: function( name, options ) {     
  1512.     $.extend( true, this.opts.inputs[ name ], options )     
  1513.     this.reload().freshFields([ name ])     
  1514.     return this     
  1515.   },     
  1516.   addFields: function( fields ) {     
  1517.     fields = Utils.convertToArray( fields )     
  1518.     var self = this     
  1519.     // Save names of all inputs in Array     
  1520.     // to use methods that take names ie. fresh()     
  1521.     var allNames = []     
  1522.     // Add an input to the DOM     
  1523.     function add( ops ) {     
  1524.       var name = ops.name     
  1525.       var userOptions = {     
  1526.         filters: ops.filters || '',     
  1527.         data: ops.data || {},     
  1528.         errors: ops.errors || {},     
  1529.         flags: ops.flags || ''     
  1530.       }     
  1531.       var label = ops.label || ''     
  1532.       var type = ops.type     
  1533.       var list = ops.list || []     
  1534.       var placeholder = ops.placeholder || ''     
  1535.       var value = ops.value || ''     
  1536.       var $field = $('<div>'+     
  1537.           '<label>'+ label +':</label>'+     
  1538.           Utils.makeInput( name, value, type, list, placeholder ) +     
  1539.         '</div>')     
  1540.       var $input = $field.find('input, select, textarea, :button')     
  1541.       // Add inputs with filters to the list     
  1542.       // of user inputs to validate     
  1543.       if ( userOptions.filters ) { self.opts.inputs[ name ] = userOptions }     
  1544.       self._doMarkup( $input )     
  1545.       // Insert in DOM     
  1546.       if ( ops.addAfter ) {     
  1547.         $field.insertAfter(     
  1548.           $( Utils.getByNameOrId( ops.addAfter ) ).parents('.ideal-wrap')     
  1549.         )     
  1550.       } else if ( ops.addBefore ) {     
  1551.         $field.insertBefore(     
  1552.           $(Utils.getByNameOrId( ops.addBefore ))     
  1553.           .parents('.ideal-wrap')     
  1554.         )     
  1555.       } else if ( ops.appendToTab ) {     
  1556.         $field.insertAfter(     
  1557.           self._getTab( ops.appendToTab ).find('.ideal-wrap:last-child')     
  1558.         )     
  1559.       } else {     
  1560.         $field.insertAfter( self.$form.find('.ideal-wrap').last() )     
  1561.       }     
  1562.       // Add current field name to list of names     
  1563.       allNames.push( name )     
  1564.     }     
  1565.     // Run through each input     
  1566.     $.each( fields, function( i, ops ) { add( ops ) })     
  1567.     self.reload()     
  1568.     self.freshFields( allNames )     
  1569.     self._responsive()     
  1570.     return this     
  1571.   },     
  1572.   removeFields: function( fields ) {     
  1573.     fields = Utils.convertToArray( fields )     
  1574.     var $fields = Utils.getFieldsFromArray( fields )     
  1575.     $fields.parents('.ideal-wrap').remove()     
  1576.     this.reload()     
  1577.     return this     
  1578.   }     
  1579. })     
  1580. }( jQuery, window, document ))    

以上所述是本文的全部内容希望对大家有所帮助!