抽时间写了一个带有自动校验功能的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 ))    

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

华山资源网 Design By www.eoogi.com
广告合作:本站广告合作请联系QQ:858582 申请时备注:广告合作(否则不回)
免责声明:本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除!
华山资源网 Design By www.eoogi.com

稳了!魔兽国服回归的3条重磅消息!官宣时间再确认!

昨天有一位朋友在大神群里分享,自己亚服账号被封号之后居然弹出了国服的封号信息对话框。

这里面让他访问的是一个国服的战网网址,com.cn和后面的zh都非常明白地表明这就是国服战网。

而他在复制这个网址并且进行登录之后,确实是网易的网址,也就是我们熟悉的停服之后国服发布的暴雪游戏产品运营到期开放退款的说明。这是一件比较奇怪的事情,因为以前都没有出现这样的情况,现在突然提示跳转到国服战网的网址,是不是说明了简体中文客户端已经开始进行更新了呢?