PHP前端开发

H5完成用户注册自动校验的实例详解

百变鹏仔 3个月前 (10-18) #H5教程
文章标签 用户注册

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

05-24 10:49:46作者:php中文网

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

以下是效果截图:

1.页面代码:usersRegister.hbs

XML/HTML Code复制内容到剪贴板
  1. nbsp;html>     

  2.      

  3.     div class="main" >     

  4.         div style="height:5px;text-align:center;font-size:25px"> 欢迎您注册!div>     

  5.              

  6.         form id="my-form" class="myform">     

  7.             div>     

  8.                 label>用户名:label>input id="username" name="username" type="text" />     

  9.             div>     

  10.             div>     

  11.                      

  12.                 label>密码:label>input id="pass" name="password" type="text" />     

  13.             div>     

  14.             div>     

  15.                 label>邮箱:label>input id="email" name="email"     

  16.                                          data-ideal="required email" type="email" />     

  17.             div>     

  18.             div>     

  19.                 label>电话:label>input id="telephone" type="text" name="phone" data-ideal="phone" />     

  20.             div>     

  21.             div>     

  22.                 label>供应商V码:label>input id="vCode" type="text" name="vCode" data-ideal="vCode" />     

  23.             div>     

  24.             div>     

  25.                 label>真实姓名:label>input id="trueName" type="text" name="trueName" data-ideal="trueName" />     

  26.             div>     

  27.             div>     

  28.                 label>手机验证码:label>input id="telCode" type="text" name="telCode" data-ideal="telCode" />     

  29.             div>     

  30.             div style="margin-bottom:5px;">     

  31.                 button id="getTelCode" type="button" style="margin-left:160px; margin-right:auto;" >获取手机校验码button>     

  32.                 hr style="margin-top:5px; margin-bottom:5px;" />     

  33.             div>     

  34.                  

  35.     div>     

  36. script type="text/javascript">     

  37.     var options = {     

  38.         onFail : function() {     

  39.             alert($myform.getInvalid().length + ' invalid fields.')     

  40.         },     

  41.         inputs : {     

  42.             'password' : {     

  43.                 filters : 'required pass'     

  44.             },     

  45.             'username' : {     

  46.                 filters : 'required username'     

  47.             },     

  48.             'email' : {     

  49.                 filters : 'required email'     

  50.             },     

  51.             'phone' : {     

  52.                 filters : 'required phone'     

  53.             },     

  54.             'trueName' : {     

  55.                 filters : 'required'     

  56.             },     

  57.             'vCode' : {     

  58.                 filters : 'required'     

  59.             },     

  60.             'telCode' : {     

  61.                 filters : 'required'     

  62.             }     

  63.             /*     

  64.             'age' : {     

  65.                 filters : 'required digits',     

  66.                 data : {     

  67.                    min : 16,     

  68.                    max : 70     

  69.                 }     

  70.             },     

  71.             'file' : {     

  72.                 filters : 'extension',     

  73.                 data : {     

  74.                     extension : [ 'jpg' ]     

  75.                 }     

  76.             },     

  77.             'comments' : {     

  78.                 filters : 'min max',     

  79.                 data : {     

  80.                     min : 50,     

  81.                     max : 200     

  82.                 }     

  83.             },     

  84.             'states' : {     

  85.                 filters : 'exclude',     

  86.                 data : {     

  87.                     exclude : [ 'default' ]     

  88.                 },     

  89.                 errors : {     

  90.                     exclude : '选择国籍.'     

  91.                 }     

  92.             },     

  93.             'langs[]' : {     

  94.                 filters : 'min max',     

  95.                 data : {     

  96.                     min : 2,     

  97.                     max : 3     

  98.                 },     

  99.                 errors : {     

  100.                     min : 'Check at least strong>2strong> options.',     

  101.                     max : 'No more than strong>3strong> options allowed.'     

  102.                 }     

  103.             }     

  104.             */     

  105.         }     

  106.     };     

  107.     $('#getTelCode').click(function() {     

  108.         var telephone = document.getElementById("telephone").value;   //手机号码     

  109.         if (telephone == null || telephone == ""){     

  110.             alert("手机号码不能为空!");     

  111.         }     

  112.         else{     

  113.             $.ajax({     

  114.                 type : "GET",     

  115.                 dataType : "json",     

  116.                 url : "../api/getTelCode?telephone="+ telephone,     

  117.                 success : function(msg) {     

  118.                 },     

  119.                 error : function(e) {     

  120.                     alert("获取手机校验码失败!" + e);     

  121.                 }     

  122.             });     

  123.         }     

  124.     });     

  125.     var $myform = $('#my-form').idealforms(options).data('idealforms');     

  126.     $('#submit').click(function() {     

  127.         var username = document.getElementById("username").value; //用户名     

  128.         var password = document.getElementById("pass").value;    //密码     

  129.         var email = document.getElementById("email").value;     //邮箱     

  130.         var telephone = document.getElementById("telephone").value;     //手机号码     

  131.         var vCode = document.getElementById("vCode").value;     //公司V码     

  132.         var telCode = document.getElementById("telCode").value;     //手机校验码     

  133.         var trueName = document.getElementById("trueName").value;     //真实姓名     

  134.         $.ajax({     

  135.             type : "GET",     

  136.             url : "../api/usersRegister?username="+ username +"password="+ password +"email="+ email +"telephone="+ telephone +"vCode="+ vCode +"telCode="+ telCode +"trueName="+ trueName,     

  137.             success : function(msg) {     

  138.                //获取当前网址,如: http://localhost:8083/uimcardprj/share/meun.jsp     

  139.                var curWwwPath = window.document.location.href;     

  140.                //获取主机地址之后的目录,如: uimcardprj/share/meun.jsp     

  141.                var pathName = window.document.location.pathname;     

  142.                var pos = curWwwPath.indexOf(pathName);     

  143.                //获取主机地址,如: http://localhost:8083     

  144.                var localhostPaht = curWwwPath.substring(0, pos);     

  145.                //获取带"/"的项目名,如:/uimcardprj     

  146.                var projectName = pathName.substring(0, pathName.substr(1).indexOf('/') + 1);     

  147.                window.location.href = projectName + "/login";     

  148.                alert("注册成功!");     

  149.             },     

  150.             error : function(e) {     

  151.                 alert("注册失败!" + e);     

  152.             }     

  153.         });     

  154.     });     

  155.     $('#reset').click(function() {     

  156.         $myform.reset().fresh().focusFirst();     

  157.     });     

  158. script>     

  159. body>     

  160. 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地址. (例: user@gmail.com)'

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

以下是整个代码文件:

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 ===&nbspnbsp;'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 = 0l = 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 = '+     

  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 = '+ name +'" name="'+ name +'" value="'+ value +'">textarea>'     

  146.     }     

  147.     // Select     

  148.     if ( /select/.test( type ) ) {     

  149.       items = []     

  150.       for ( i = 0len = 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.         '+ name +'" name="'+ name +'">'+     

  157.           items.join('') +     

  158.         'select>'     

  159.     }     

  160.     // Radiocheck     

  161.     if ( /(radio|checkbox)/.test( type ) ) {     

  162.       items = []     

  163.       for ( i = 0len = 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.         '

  193. '
  194. +     

  195.           'span>' + name + 'span>'+     

  196.           'i class="ideal-tabs-tab-counter ideal-tabs-tab-counter-zero">0i>'+     

  197.         'li>'     

  198.       tabs.push(html)     

  199.     })     

  200.     return $(tabs.join(''))     

  201.   }()),     

  202.   Actions = {     

  203.     getCurIdx: function () {     

  204.       return $tabs     

  205.         .filter('.ideal-tabs-tab-active')     

  206.         .index()     

  207.     },     

  208.     getTabIdxByName: function (name) {     

  209.       var re = new RegExp(name, 'i')     

  210.       var $tab = $tabs.filter(function () {     

  211.         return re.test($(this).text())     

  212.       })     

  213.       return $tab.index()     

  214.     }     

  215.   },     

  216.   /**    

  217.    * Public methods    

  218.    */     

  219.   Methods = {     

  220.     /**    

  221.      * Switch tab    

  222.      */     

  223.     switchTab: function (nameOrIdx) {     

  224.       var idx = Utils.isString(nameOrIdx)     

  225.         ? Actions.getTabIdxByName(nameOrIdx)     

  226.         : nameOrIdx     

  227.       $tabs.removeClass('ideal-tabs-tab-active')     

  228.       $tabs.eq(idx).addClass('ideal-tabs-tab-active')     

  229.       $contents.hide().eq(idx).show()     

  230.     },     

  231.     nextTab: function () {     

  232.       var idx = Actions.getCurIdx() + 1     

  233.       idx > $tabs.length - 1     

  234.         ? Methods.firstTab()     

  235.         : Methods.switchTab(idx)     

  236.     },     

  237.     prevTab: function () {     

  238.       Methods.switchTab(Actions.getCurIdx() - 1)     

  239.     },     

  240.     firstTab: function () {     

  241.       Methods.switchTab(0)     

  242.     },     

  243.     lastTab: function () {     

  244.       Methods.switchTab($tabs.length - 1)     

  245.     },     

  246.     updateCounter: function (nameOrIdx, text) {     

  247.       var idx = !isNaN(nameOrIdx) ? nameOrIdx : Actions.getTabIdxByName(name),     

  248.           $counter = $tabs.eq(idx).find('.ideal-tabs-tab-counter')     

  249.       $counter.removeClass('ideal-tabs-tab-counter-zero')     

  250.       if (!text) {     

  251.         $counter.addClass('ideal-tabs-tab-counter-zero')     

  252.       }     

  253.       $counter.html(text)     

  254.     }     

  255.   }     

  256.   // Attach methods     

  257.   for (var m in Methods)     

  258.     $contents[m] = Methods[m]     

  259.   // Init     

  260.   $tabs.first()     

  261.     .addClass('ideal-tabs-tab-active')     

  262.     .end()     

  263.     .click(function () {     

  264.       var name = $(this).text()     

  265.       $contents.switchTab(name)     

  266.     })     

  267.   // Insert in DOM & Events     

  268.   $wrapper.append($tabs).appendTo($container)     

  269.   $contents.addClass('ideal-tabs-content')     

  270.   $contents.each(function () {     

  271.     var $this = $(this), name = $(this).attr('name')     

  272.     $this.data('ideal-tabs-content-name', name)     

  273.       .removeAttr('name')     

  274.   })     

  275.   $contents.hide().first().show() // Start fresh     

  276.   return $contents     

  277. }     

  278. /**    

  279.  * A custom select> menu jQuery plugin    

  280.  * @example `$('select').idealSelect()`    

  281.  */     

  282. $.fn.idealSelect = function () {     

  283.   return this.each(function () {     

  284.     var     

  285.     $select = $(this),     

  286.     $options&nbnbsp;= $select.find('option')     

  287.     /**    

  288.      * Generate markup and return elements of custom select    

  289.      * @memberOf $.fn.toCustomSelect    

  290.      * @returns {object} All elements of the new select replacement    

  291.      */     

  292.     var idealSelect = (function () {     

  293.       var     

  294.       $wrap = $('ul class="ideal-select '+ $select.attr('name') +'"/>'),     

  295.       $menu = $(     

  296.         'li>span class="ideal-select-title">' +     

  297.           $options.filter(':selected').text() +     

  298.         'span>li>'     

  299.       ),     

  300.       items = (function () {     

  301.         var items = []     

  302.         $options.each(function () {     

  303.           var $this = $(this)     

  304.           items.push('li class="ideal-select-item">' + $this.text() + 'li>')     

  305.         })     

  306.         return items     

  307.       }())     

  308.       $menu.append('ul class="ideal-select-sub">' + items.join('') + 'ul>')     

  309.       $wrap.append($menu)     

  310.       return {     

  311.         select: $wrap,     

  312.         title: $menu.find('.ideal-select-title'),     

  313.         sub: $menu.find('.ideal-select-sub'),     

  314.         items: $menu.find('.ideal-select-item')     

  315.       }     

  316.     }())     

  317.     /**    

  318.      * @namespace Methods of custom select    

  319.      * @memberOf $.fn.toCustomSelect    

  320.      */     

  321.     var Actions = {     

  322.       getSelectedIdx: function () {     

  323.         return idealSelect.items     

  324.           .filter('.ideal-select-item-selected').index()     

  325.       },     

  326.       /**    

  327.        * @private    

  328.        */     

  329.       init: (function () {     

  330.         $select.css({     

  331.           position: 'absolute',     

  332.           left: '-9999px'     

  333.         })     

  334.         idealSelect.sub.hide()     

  335.         idealSelect.select.insertAfter($select)     

  336.         idealSelect.select.css(     

  337.           'min-width',     

  338.           Utils.getMaxWidth(idealSelect.items)     

  339.         )     

  340.         idealSelect.items     

  341.           .eq($options.filter(':selected').index())     

  342.           .addClass('ideal-select-item-selected')     

  343.       }()),     

  344.       noWindowScroll: function (e) {     

  345.         if (e.which === 40 || e.which === 38 || e.which === 13) {     

  346.           e.preventDefault()     

  347.         }     

  348.       },     

  349.       // Fix loosing focus when scrolling     

  350.       // and selecting item with keyboard     

  351.       focusHack: function () {     

  352.         setTimeout(function () {     

  353.           $select.trigger('focus')     

  354.         }, 1)     

  355.       },     

  356.       focus: function () {     

  357.         idealSelect.select.addClass('ideal-select-focus')     

  358.         $(document).on('keydown.noscroll', Actions.noWindowScroll)     

  359.       },     

  360.       blur: function () {     

  361.         idealSelect.select     

  362.           .removeClass('ideal-select-open ideal-select-focus')     

  363.         $(document).off('.noscroll')     

  364.       },     

  365.       scrollIntoView: function (dir) {     

  366.         var     

  367.         $selected = idealSelect.items.filter('.ideal-select-item-selected'),     

  368.         itemHeight = idealSelect.items.outerHeight(),     

  369.         menuHeight = idealSelect.sub.outerHeight(),     

  370.         isInView = (function () {     

  371.           // relative position to the submenu     

  372.           var elPos = $selected.position().top + itemHeight     

  373.           return dir === 'down'     

  374.             ? elPos = menuHeight     

  375.             : elPos > 0     

  376.         }())     

  377.         if (!isInView) {     

  378.           itemHeight = (dir === 'down')     

  379.             ? itemHeight // go down     

  380.             : -itemHeight // go up     

  381.           idealSelect.sub     

  382.             .scrollTop(idealSelect.sub.scrollTop() + itemHeight)     

  383.         }     

  384.       },     

  385.       scrollToItem: function () {     

  386.         var idx = Actions.getSelectedIdx(),     

  387.             height = idealSelect.items.outerHeight(),     

  388.             nItems = idealSelect.items.length,     

  389.             allHeight = height * nItems,     

  390.             curHeight = height * (nItems - idx)     

  391.         idealSelect.sub.scrollTop(allHeight - curHeight)     

  392.       },     

  393.       showMenu: function () {     

  394.         idealSelect.sub.fadeIn('fast')     

  395.         idealSelect.select.addClass('ideal-select-open')     

  396.         Actions.select(Actions.getSelectedIdx())     

  397.         Actions.scrollToItem()     

  398.       },     

  399.       hideMenu: function () {     

  400.         idealSelect.sub.hide()     

  401.         idealSelect.select.removeClass('ideal-select-open')     

  402.       },     

  403.       select: function (idx) {     

  404.         idealSelect.items     

  405.           .removeClass('ideal-select-item-selected')     

  406.         idealSelect.items     

  407.           .eq(idx).addClass('ideal-select-item-selected')     

  408.       },     

  409.       change: function (idx) {     

  410.         var text = idealSelect.items.eq(idx).text()     

  411.         Actions.select(idx)     

  412.         idealSelect.title.text(text)     

  413.         $options.eq(idx).prop('selected', true)     

  414.         $select.trigger('change')     

  415.       },     

  416.       keydown: function (key) {     

  417.         var     

  418.         idx = Actions.getSelectedIdx(),     

  419.         isMenu = idealSelect.select.is('.ideal-select-menu'),     

  420.         isOpen = idealSelect.select.is('.ideal-select-open')     

  421.         /**    

  422.          * @namespace Key pressed    

  423.          */     

  424.         var keys = {     

  425.           9: function () { // TAB     

  426.             if (isMenu) {     

  427.               Actions.blur()     

  428.               Actions.hideMenu()     

  429.             }     

  430.           },     

  431.           13: function () { // ENTER     

  432.             if (isMenu)     

  433.               isOpen     

  434.                 ? Actions.hideMenu()     

  435.                 : Actions.showMenu()     

  436.             Actions.change(idx)     

  437.           },     

  438.           27: function () { // ESC     

  439.             if (isMenu) Actions.hideMenu()     

  440.           },     

  441.           40: function () { // DOWN     

  442.             if (idx  $options.length - 1) {     

  443.               isOpen     

  444.                 ? Actions.select(idx + 1)     

  445.                 : Actions.change(idx + 1)     

  446.             }     

  447.             Actions.scrollIntoView('down')     

  448.           },     

  449.           38: function () { // UP     

  450.             if (idx > 0) {     

  451.               isOpen     

  452.                 ? Actions.select(idx - 1)     

  453.                 : Actions.change(idx - 1)     

  454.             }     

  455.             Actions.scrollIntoView('up')     

  456.           },     

  457.           'default': function () { // Letter     

  458.             var     

  459.             letter = String.fromCharCode(key),     

  460.             $matches = idealSelect.items     

  461.               .filter(function () {     

  462.                 return /^\w+$/i.test( letter ) && // not allow modifier keys ( ctrl, cmd, meta, super... )     

  463.                   new RegExp('^' + letter, 'i').test( $(this).text() ) // find first match     

  464.               }),     

  465.             nMatches = $matches.length,     

  466.             counter = idealSelect.select.data('counter') + 1 || 0,     

  467.             curKey = idealSelect.select.data('key') || key,     

  468.             newIdx = $matches.eq(counter).index()     

  469.             if (!nMatches) // No matches     

  470.               return false     

  471.             // If more matches with same letter     

  472.             if (curKey === key) {     

  473.               if (counter  nMatches) {     

  474.                 idealSelect.select.data('counter', counter)     

  475.               }     

  476.               else {     

  477.                 idealSelect.select.data('counter', 0)     

  478.                 newIdx = $matches.eq(0).index()     

  479.               }     

  480.             }     

  481.             // If new letter     

  482.             else {     

  483.              &nnbsp;idealSelect.select.data('counter', 0)     

  484.               newIdx = $matches.eq(0).index()     

  485.             }     

  486.             if (isOpen)     

  487.               Actions.select(newIdx)     

  488.             else     

  489.               Actions.change(newIdx)     

  490.             idealSelect.select.data('key', key)     

  491.             Actions.scrollToItem()     

  492.             Actions.focusHack()     

  493.           }     

  494.         }     

  495.         keys[key]     

  496.           ? keys[key]()     

  497.           : keys['default']()     

  498.       }     

  499.     }     

  500.     /**    

  501.      * @namespace Holds all events of custom select for "menu mode" and "list mode"    

  502.      * @memberOf $.fn.toCustomSelect    

  503.      */     

  504.     var events = {     

  505.       focus: Actions.focus,     

  506.       'blur.menu': function () {     

  507.         Actions.blur()     

  508.         Actions.hideMenu()     

  509.       },     

  510.       'blur.list': function () {     

  511.         Actions.blur()     

  512.       },     

  513.       keydown: function (e) {     

  514.         Actions.keydown(e.which)     

  515.       },     

  516.       'clickItem.menu': function () {     

  517.         Actions.change($(this).index())     

  518.         Actions.hideMenu()     

  519.       },     

  520.       'clickItem.list': function () {     

  521.         Actions.change($(this).index())     

  522.       },     

  523.       'clickTitle.menu': function () {     

  524.         Actions.focus()     

  525.         Actions.showMenu()     

  526.         $select.trigger('focus')     

  527.       },     

  528.       'hideOutside.menu': function () {     

  529.         $select.off('blur.menu')     

  530.         $(document).on('mousedown.ideal', function (evt) {     

  531.           if (!$(evt.target).closest(idealSelect.select).length) {     

  532.             $(document).off('mousedown.ideal')     

  533.             $select.on('blur.menu', events['blur.menu'])     

  534.           } else {     

  535.             Actions.focusHack()     

  536.           }     

  537.         })     

  538.       },     

  539.       'mousedown.list': function () {     

  540.         Actions.focusHack()     

  541.       }     

  542.     }     

  543.     // Reset events     

  544.     var disableEvents = function () {     

  545.       idealSelect.select.removeClass('ideal-select-menu ideal-select-list')     

  546.       $select.off('.menu .list')     

  547.       idealSelect.items.off('.menu .list')     

  548.       idealSelect.select.off('.menu .list')     

  549.       idealSelect.title.off('.menu .list')     

  550.     }     

  551.     // Menu mode     

  552.     idealSelect.select.on('menu', function () {     

  553.       disableEvents()     

  554.       idealSelect.select.addClass('ideal-select-menu')     

  555.       Actions.hideMenu()     

  556.       $select.on({     

  557.         'blur.menu': events['blur.menu'],     

  558.         'focus.menu': events.focus,     

  559.         'keydown.menu': events.keydown     

  560.       })     

  561.       idealSelect.select.on('mousedown.menu', events['hideOutside.menu'])     

  562.       idealSelect.items.on('click.menu', events['clickItem.menu'])     

  563.       idealSelect.title.on('click.menu', events['clickTitle.menu'])     

  564.     })     

  565.     // List mode     

  566.     idealSelect.select.on('list', function () {     

  567.       disableEvents()     

  568.       idealSelect.select.addClass('ideal-select-list')     

  569.       Actions.showMenu()     

  570.       $select.on({     

  571.         'blur.list': events['blur.list'],     

  572.         'focus.list': events.focus,     

  573.         'keydown.list': events.keydown     

  574.       })     

  575.       idealSelect.select.on('mousedown.list', events['mousedown.list'])     

  576.       idealSelect.items.on('mousedown.list', events['clickItem.list'])     

  577.     })     

  578.     $select.keydown(function (e) {     

  579.       // Prevent default keydown event     

  580.       // to avoid bugs with Ideal Select events     

  581.       if (e.which !== 9) e.preventDefault()     

  582.     })     

  583.     // Reset     

  584.     idealSelect.select.on('reset', function(){     

  585.       Actions.change(0)     

  586.     })     

  587.     idealSelect.select.trigger('menu') // Default to "menu mode"     

  588.   })     

  589. }     

  590. /*    

  591.  * idealRadioCheck: jQuery plguin for checkbox and radio replacement    

  592.  * Usage: $('input[type=checkbox], input[type=radio]').idealRadioCheck()    

  593.  */     

  594. $.fn.idealRadioCheck = function() {     

  595.   return this.each(function() {     

  596.     var $this = $(this)     

  597.     var $span = $('span/>')     

  598.     $span.addClass( 'ideal-'+ ( $this.is(':checkbox') ? 'check' : 'radio' ) )     

  599.     $this.is(':checked') && $span.addClass('checked') // init     

  600.     $span.insertAfter( $this )     

  601.     $this.parent('label').addClass('ideal-radiocheck-label')     

  602.       .attr('onclick', '') // Fix clicking label in iOS     

  603.     $this.css({ position: 'absolute', left: '-9999px' }) // hide by shifting left     

  604.     // Events     

  605.     $this.on({     

  606.       change: function() {     

  607.         var $this = $(this)     

  608.         if ( $this.is('input[type="radio"]') ) {     

  609.           $this.parent().siblings('label').find('.ideal-radio').removeClass('checked')     

  610.         }     

  611.         $span.toggleClass( 'checked', $this.is(':checked') )     

  612.       },     

  613.       focus: function() { $span.addClass('focus') },     

  614.       blur: function() { $span.removeClass('focus') },     

  615.       click: function() { $(this).trigger('focus') }     

  616.     })     

  617.   })     

  618. }     

  619. ;(function( $ ) {     

  620.   // Browser supports HTML5 multiple file?     

  621.   var multipleSupport = typeof $('input/>')[0].multiple !== 'undefined',     

  622.       isIE = /msie/i.test( navigator.userAgent )     

  623.   $.fn.idealFile = function() {     

  624.     return this.each(function() {     

  625.       var $file = $(this).addClass('ideal-file'), // the original file input     

  626.           // label that will be used for IE hack     

  627.           $wrap = $('div class="ideal-file-wrap">'),     

  628.           $input = $('input type="text" class="ideal-file-filename" />'),     

  629.           // Button that will be used in non-IE browsers     

  630.           $button = $('button type="button" class="ideal-file-upload">Openbutton>'),     

  631.           // Hack for IE     

  632.           $label = $('label class="ideal-file-upload" for="'+ $file[0].id +'">Openlabel>')     

  633.       // Hide by shifting to the left so we     

  634.       // can still trigger events     

  635.       $file.css({     

  636.         position: 'absolute',     

  637.         left: '-9999px'     

  638.       })     

  639.       $wrap.append( $input, ( isIE ? $label : $button ) ).insertAfter( $file )     

  640.       // Prevent focus     

  641.       $file.attr('tabIndex', -1)     

  642.       $button.attr('tabIndex', -1)     

  643.       $button.click(function () {     

  644.         $file.focus().click() // Open dialog     

  645.       })     

  646.       $file.change(function() {     

  647.         var files = [], fileArr, filename     

  648.         // If multiple is supported then extract     

  649.         // all filenames from the file array     

  650.         if ( multipleSupport ) {     

  651.           fileArr = $file[0].files     

  652.           for ( var i = 0len = fileArr.length; i  len; i++ ) {     

  653.             files.push( fileArr[i].name )     

  654.           }     

  655.           filename = files.join(', ')     

  656.         // If not supported then just take the value     

  657.         // and remove the path to just show the filename     

  658.         } else {     

  659.           filename = $file.val().split('\\').pop()     

  660.         }     

  661.         $input.val( filename ) // Set the value     

  662.           .attr( 'title', filename ) // Show filename in title tootlip     

  663.       })     

  664.       $input.on({     

  665.         focus: function () { $file.trigger('change') },     

  666.         blur: function () { $file.trigger('blur') },     

  667.         keydown: function( e ) {     

  668.           if ( e.which === 13 ) { // Enter     

  669.             if ( !isIE ) { $file.trigger('click') }     

  670.           } else if ( e.which === 8 || e.which === 46 ) { // Backspace & Del     

  671.             // On some browsers the value is read-only     

  672.             // with this trick we remove the old input and add     

  673.             // a clean clone with all the original events attached     

  674.             $file.replaceWith( $file = $file.val('').clone( true ) )     

  675.             $file.trigger('change')     

  676.             $input.val('')     

  677.           } else if ( e.which === 9 ){ // TAB     

  678.             return     

  679.           } else { // All other keys     

  680.             return false     

  681.           }     

  682.         }     

  683.       })     

  684.     })     

  685.   }     

  686. }( jQuery ))     

  687. /**    

  688.  * @namespace Errors    

  689.  * @locale en    

  690.  */     

  691. $.idealforms.errors = {     

  692.   required: '此处是必填的.',     

  693.   number: '必须是数字.',     

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

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

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

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

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

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

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

  701.   zip: 'Must be a valid US zip code. em>(e.g. 33245 or 33245-0003)em>',     

  702.   url: 'Must be a valid URL. em>(e.g. www.google.com)em>',     

  703.   minChar: 'Must be at least strong>{0}strong> characters long.',     

  704.   minOption: 'Check at least strong>{0}strong> options.',     

  705.   maxChar: 'No more than strong>{0}strong> characters long.',     

  706.   maxOption: 'No more than strong>{0}strong> options allowed.',     

  707.   range: 'Must be a number between {0} and {1}.',     

  708.   date: 'Must be a valid date. em>(e.g. {0})em>',     

  709.   dob: 'Must be a valid date of birth.',     

  710.   exclude: '"{0}" is not available.',     

  711.   excludeOption: '{0}',     

  712.   equalto: 'Must be the same value as strong>"{0}"strong>',     

  713.   extension: 'File(s) must have a valid extension. em>(e.g. "{0}")em>',     

  714.   ajaxSuccess: 'strong>{0}strong> is not available.',     

  715.   ajaxError: 'Server error...'     

  716. }     

  717. /**    

  718.  * Get all default filters    

  719.  * @returns object    

  720.  */     

  721. var getFilters = function() {     

  722.   var filters = {     

  723.     required: {     

  724.       regex: /.+/,     

  725.       error: $.idealforms.errors.required     

  726.     },     

  727.     number: {     

  728.       regex: function( i, v ) { return !isNaN(v) },     

  729.       error: $.idealforms.errors.number     

  730.     },     

  731.     digits: {     

  732.       regex: /^\d+$/,     

  733.       error: $.idealforms.errors.digits     

  734.     },     

  735.     name: {     

  736.       regex: /^[A-Za-z]{3,}$/,     

  737.       error: $.idealforms.errors.name     

  738.     },     

  739.     username: {     

  740.       regex: /^[a-z](?=[\w.]{4,30}$)\w*\.?\w*$/i,     

  741.       error: $.idealforms.errors.username     

  742.     },     

  743.     pass: {     

  744.       regex: /(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{6,}/,     

  745.       error: $.idealforms.errors.pass     

  746.     },     

  747.     strongpass: {     

  748.       regex: /(?=^.{8,}$)((?=.*\d)|(?=.*\W+))(?![.\n])(?=.*[A-Z])(?=.*[a-z]).*$/,     

  749.       error: $.idealforms.errors.strongpass     

  750.     },     

  751.     email: {     

  752.       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})?$/,     

  753.       error: $.idealforms.errors.email     

  754.     },     

  755.     phone: {     

  756.       //regex: /^((13[0-9])|(15[0-9])|(17[0-9])|(18[0-9]))\\d{8}$/,     

  757.       regex: /^(0|86|17951)?(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$/,     

  758.       error: $.idealforms.errors.phone     

  759.     },     

  760.     zip: {     

  761.       regex: /^\d{5}$|^\d{5}-\d{4}$/,     

  762.       error: $.idealforms.errors.zip     

  763.     },     

  764.     url: {     

  765.       regex: /^(?:(ftp|http|https):\/\/)?(?:[\w\-]+\.)+[a-z]{2,6}([\:\/?#].*)?$/i,     

  766.       error: $.idealforms.errors.url     

  767.     },     

  768.     min: {     

  769.       regex: function( input, value ) {     

  770.         var $inputinput = input.input,     

  771.             min = input.userOptions.data.min,     

  772.             isRadioCheck = $input.is('[type="checkbox"], [type="radio"]')     

  773.         if ( isRadioCheck ) {     

  774.           this.error = $.idealforms.errors.minOption.replace( '{0}', min )     

  775.           return $input.filter(':checked').length >= min     

  776.         }     

  777.         this.error = $.idealforms.errors.minChar.replace( '{0}', min )     

  778.         return value.length >= min     

  779.       }     

  780.     },     

  781.     max: {     

  782.       regex: function( input, value ) {     

  783.         var $inputinput = input.input,     

  784.             max = input.userOptions.data.max,     

  785.             isRadioCheck = $input.is('[type="checkbox"], [type="radio"]')     

  786.         if ( isRadioCheck ) {     

  787.           this.error = $.idealforms.errors.maxOption.replace( '{0}', max )     

  788.           return $input.filter(':checked').length = max     

  789.         }     

  790.         this.error = $.idealforms.errors.maxChar.replace( '{0}', max )     

  791.         return value.length = max     

  792.       }     

  793.     },     

  794.     range: {     

  795.       regex: function( input, value ) {     

  796.         var range = input.userOptions.data.range,     

  797.             val = +value     

  798.         this.error = $.idealforms.errors.range     

  799.           .replace( '{0}', range[0] )     

  800.           .replace( '{1}', range[1] )     

  801.         return val >= range[0] && val = range[1]     

  802.       }     

  803.     },     

  804.     date: {     

  805.       regex: function( input, value ) {     

  806.         var     

  807.         userFormat =     

  808.           input.userOptions.data && input.userOptions.data.date     

  809.             ? input.userOptions.data.date     

  810.             : 'mm/dd/yyyy', // default format     

  811.         delimiter = /[^mdy]/.exec( userFormat )[0],     

  812.         theFormat = userFormat.split(delimiter),     

  813.         theDate = value.split(delimiter),     

  814.         isDate = function( date, format ) {     

  815.           var m, d, y     

  816.           for ( var i = 0len = format.length; i  len; i++ ) {     

  817.             if ( /m/.test( format[i]) ) m = date[i]     

  818.             if ( /d/.test( format[i]) ) d = date[i]     

  819.             if ( /y/.test( format[i]) ) y = date[i]     

  820.           }     

  821.           return (     

  822.             m > 0 && m  13 &&     

  823.             y && y.length === 4 &&     

  824.             d > 0 && d = ( new Date( y, m, 0 ) ).getDate()     

  825.           )     

  826.         }     

  827.         this.error = $.idealforms.errors.date.replace( '{0}', userFormat )     

  828.         return isDate( theDate, theFormat )     

  829.       }     

  830.     },     

  831.     dob: {     

  832.       regex: function( input, value ) {     

  833.         var     

  834.         userFormat =     

  835.           input.userOptions.data && input.userOptions.data.dob     

  836.             ? input.userOptions.data.dob     

  837.             : 'mm/dd/yyyy', // default format     

  838.         // Simulate a date input     

  839.         dateInput = {     

  840.           input: input.input,     

  841.           userOptions: {     

  842.             data: { date: userFormat }     

  843.           }     

  844.         },     

  845.         // Use internal date filter to validate the date     

  846.         isDate = filters.date.regex( dateInput, value ),     

  847.         // DOB     

  848.         theYear = /\d{4}/.exec( value ),     

  849.         maxYear = new Date().getFullYear(), // Current year     

  850.         minYear = maxYear - 100     

  851.         this.error = $.idealforms.errors.dob     

  852.         return isDate && theYear >= minYear && theYear = maxYear     

  853.       }     

  854.     },     

  855.     exclude: {     

  856.       regex: function( input, value ) {     

  857.         var $inputinput = input.input,     

  858.             exclude = input.userOptions.data.exclude,     

  859.             isOption = $input.is('[type="checkbox"], [type="radio"], select')     

  860.         this.error = isOption     

  861.           ? $.idealforms.errors.excludeOption.replace( '{0}', value )     

  862.           : this.error = $.idealforms.errors.exclude.replace( '{0}', value )     

  863.         return $.inArray( value, exclude ) === -1     

  864.       }     

  865.     },     

  866.     equalto: {     

  867.       regex: function( input, value ) {     

  868.         var $equals = $( input.userOptions.data.equalto ),     

  869.             $inputinput = input.input,     

  870.             name = $equals.attr('name') || $equals.attr('id'),     

  871.             isValid = $equals.parents('.ideal-field')     

  872.               .filter(function(){ return $(this).data('ideal-isvalid') === true })     

  873.               .length     

  874.         if ( !isValid ) { return false }     

  875.         this.error = $.idealforms.errors.equalto.replace( '{0}', name )     

  876.         return $input.val() === $equals.val()     

  877.       }     

  878.     },     

  879.     extension: {     

  880.       regex: function( input, value ) {     

  881.         nbsp;var files = input.input[0].files || [{ name: value }],     

  882.             extensions = input.userOptions.data.extension,     

  883.             re = new RegExp( '\\.'+ extensions.join('|') +'$', 'i' ),     

  884.             valid = false     

  885.         for ( var i = 0len = files.length; i  len; i++ ) {     

  886.           valid = re.test( files[i].name );     

  887.         }     

  888.         this.error = $.idealforms.errors.extension.replace( '{0}', extensions.join('", "') )     

  889.         return valid     

  890.       }     

  891.     },     

  892.     ajax: {     

  893.       regex: function( input, value, showOrHideError ) {     

  894.         var self = this     

  895.         var $inputinput = input.input     

  896.         var userOptions = input.userOptions     

  897.         var name = $input.attr('name')     

  898.         var $field = $input.parents('.ideal-field')     

  899.         var valid = false     

  900.         var customErrors = userOptions.errors && userOptions.errors.ajax     

  901.         self.error = {}     

  902.         self.error.success = customErrors && customErrors.success     

  903.           ? customErrors.success     

  904.           : $.idealforms.errors.ajaxSuccess.replace( '{0}', value )     

  905.         self.error.fail = customErrors && customErrors.error     

  906.           ? customErrors.error     

  907.           : $.idealforms.errors.ajaxError     

  908.         // Send input name as $_POST[name]     

  909.         var data = {}     

  910.         data[ name ] = $.trim( value )     

  911.         // Ajax options defined by the user     

  912.         var userAjaxOps = input.userOptions.data.ajax     

  913.         var ajaxOps = {     

  914.           type: 'post',     

  915.           dataType: 'json',     

  916.           data: data,     

  917.           success: function( resp, text, xhr ) {     

  918.           console.log(resp)     

  919.             showOrHideError( self.error.success, true )     

  920.             $input.data({     

  921.               'ideal-ajax-resp': resp,     

  922.               'ideal-ajax-error': self.error.success     

  923.             })     

  924.             $input.trigger('change') // to update counter     

  925.             $field.removeClass('ajax')     

  926.             // Run custom success callback     

  927.             if( userAjaxOps._success ) {     

  928.               userAjaxOps._success( resp, text, xhr )     

  929.             }     

  930.           },     

  931.           error: function( xhr, text, error ) {     

  932.             if ( text !== 'abort' ) {     

  933.               showOrHideError( self.error.fail, false )     

  934.               $input.data( 'ideal-ajax-error', self.error.fail )     

  935.               $field.removeClass('ajax')     

  936.               // Run custom error callback     

  937.               if ( userAjaxOps._error ) {     

  938.                 userAjaxOps._error( xhr, text, error )     

  939.               }     

  940.             }     

  941.           }     

  942.         }     

  943.         $.extend( ajaxOps, userAjaxOps )     

  944.         // Init     

  945.         $input.removeData('ideal-ajax-error')     

  946.         $input.removeData('ideal-ajax-resp')     

  947.         $field.addClass('ajax')     

  948.         // Run request and save it to be able to abort it     

  949.         // so requests don't bubble     

  950.         $.idealforms.ajaxRequests[ name ] = $.ajax( ajaxOps )     

  951.       }     

  952.     }     

  953.   }     

  954.   return filters     

  955. }     

  956. $.idealforms.flags = {     

  957.   noerror: function (i) {     

  958.     i.parent().siblings('.ideal-error').hide()     

  959.   },     

  960.   noicons: function (i) {     

  961.     i.siblings('.ideal-icon-valid, .ideal-icon-invalid').hide()     

  962.   },     

  963.   novalidicon: function (i) {     

  964.     i.siblings('.ideal-icon-valid').hide()     

  965.   },     

  966.   noinvalidicon: function (i) {     

  967.     i.siblings('.ideal-icon-invalid').hide()     

  968.   },     

  969.   noclass: function (i) {     

  970.     i.parents('.ideal-field').removeClass('valid invalid')     

  971.   },     

  972.   novalidclass: function (i) {     

  973.     i.parents('.ideal-field').removeClass('valid')     

  974.   },     

  975.   noinvalidclass: function (i) {     

  976.     i.parents('.ideal-field').removeClass('invalid')     

  977.   }     

  978. }     

  979. /*    

  980.  * Ideal Forms plugin    

  981.  */     

  982. var _defaults = {     

  983.   inputs: {},     

  984.   customFilters: {},     

  985.   customFlags: {},     

  986.   globalFlags: '',     

  987.   onSuccess: function(e) { alert('Thank you...') },     

  988.   onFail: function() { alert('Invalid!') },     

  989.   responsiveAt: 'auto',     

  990.   disableCustom: ''     

  991. }     

  992. // Constructor     

  993. var IdealForms = function( element, options ) {     

  994.   var self = this     

  995.   self.$form = $( element )     

  996.   self.opts = $.extend( {}, _defaults, options )     

  997.   self.$tabs = self.$form.find('section')     

  998.   // Set localized filters     

  999.   $.extend( $.idealforms.filters, getFilters() )     

  1000.   self._init()     

  1001. }     

  1002. // Plugin     

  1003. $.fn.idealforms = function( options ) {     

  1004.   return this.each(function() {     

  1005.     if ( !$.data( this, 'idealforms' ) ) {     

  1006.       $.data( this, 'idealforms', new IdealForms( this, options ) )     

  1007.     }     

  1008.   })     

  1009. }     

  1010. // Get LESS variables     

  1011. var LessVars = {     

  1012.   fieldWidth: Utils.getLessVar( 'ideal-field-width', 'width' )     

  1013. }     

  1014. /*    

  1015.  * Private Methods    

  1016.  */     

  1017. $.extend( IdealForms.prototype, {     

  1018.   _init: function() {     

  1019.     var self = this     

  1020.     var o = self.opts     

  1021.     var formElements = self._getFormElements()     

  1022.     self.$form.css( 'visibility', 'visible' )     

  1023.       .addClass('ideal-form')     

  1024.       .attr( 'novalidate', 'novalidate' ) // disable HTML5 validation     

  1025.     // Do markup     

  1026.     formElements.inputs     

  1027.       .add( formElements.headings )     

  1028.       .add( formElements.separators )     

  1029.       .each(function(){ self._doMarkup( $(this) ) })     

  1030.     // Generate tabs     

  1031.     if ( self.$tabs.length ) {     

  1032.       var $tabContainer = $('div class="ideal-wrap ideal-tabs ideal-full-width"/>')     

  1033.       self.$form.prepend( $tabContainer )     

  1034.       self.$tabs.idealTabs( $tabContainer )     

  1035.     }     

  1036.     // Always show datepicker below the input     

  1037.     if ( jQuery.ui ) {     

  1038.       $.datepicker._checkOffset = function( a,b,c ) { return b }     

  1039.     }     

  1040.     // Add inputs specified by data-ideal     

  1041.     // to the list of user inputs     

  1042.     self.$form.find('[data-ideal]').each(function() {     

  1043.       var userInput = o.inputs[ this.name ]     

  1044.       o.inputs[ this.name ] = userInput || { filters: $(this).data('ideal') }     

  1045.     })     

  1046.    // Responsive     

  1047.     if ( o.responsiveAt ) {     

  1048.       $(window).resize(function(){ self._responsive() })     

  1049.       self._responsive()     

  1050.     }     

  1051.     // Form events     

  1052.     self.$form.on({     

  1053.       keydown: function( e ) {     

  1054.         // Prevent submit when pressing enter     

  1055.         // but exclude textareas     

  1056.         if ( e.which === 13 && e.target.nodeName !== 'TEXTAREA' ) {     

  1057.           e.preventDefault()     

  1058.         }     

  1059.       },     

  1060.       submit: function( e ) {     

  1061.         if ( !self.isValid() ) {     

  1062.           e.preventDefault()     

  1063.           o.onFail()     

  1064.           self.focusFirstInvalid()     

  1065.         } else {     

  1066.           o.onSuccess( e )     

  1067.         }     

  1068.       }     

  1069.     })     

  1070.     self._adjust()     

  1071.     self._attachEvents()     

  1072.     self.fresh() // Start fresh     

  1073.   },     

  1074.   _getFormElements: function() {     

  1075.     return {     

  1076.       inputs: this.$form.find('input, select, textarea, :button'),     

  1077.       labels: this.$form.find('div > label:first-child'),     

  1078.       text: this.$form.find('input:not([type="checkbox"], [type="radio"], [type="submit"]), textarea'),     

  1079.       select: this.$form.find('select'),     

  1080.       radiocheck: this.$form.find('input[type="radio"], input[type="checkbox"]'),     

  1081.       buttons: this.$form.find(':button'),     

  1082.       file: this.$form.find('input[type="file"]'),     

  1083.       headings: this.$form.find('h1, h2, h3, h4, h5, h6'),     

  1084.       separators: this.$form.find('hr'),     

  1085.       hidden: this.$form.find('input:hidden')     

  1086.     }     

  1087.   },     

  1088.   _getUserInputs: function() {     

  1089.     return this.$form.find('[name="'+ Utils.getKeys( this.opts.inputs ).join('"], [name="') +'"]')     

  1090.   },     

  1091.   _getTab: function( nameOrIdx ) {     

  1092.     var self = this     

  1093.     var isNumber = !isNaN( nameOrIdx )     

  1094.     if ( isNumber ) {     

  1095.       return self.$tabs.eq( nameOrIdx )     

  1096.     }     

  1097.     return self.$tabs.filter(function() {     

  1098.       var re = new RegExp( nameOrIdx, 'i' )     

  1099.       return re.test( nbsp;$(this).data('ideal-tabs-content-name') )     

  1100.     })     

  1101.   },     

  1102.   _getCurrentTabIdx: function() {     

  1103.     return this.$tabs.index( this.$form.find('.ideal-tabs-content:visible') )     

  1104.   },     

  1105.   _updateTabsCounter: function() {     

  1106.     var self = this     

  1107.     self.$tabs.each(function( i ) {     

  1108.       var invalid = self.getInvalidInTab( i ).length     

  1109.       self.$tabs.updateCounter( i, invalid )     

  1110.     })     

  1111.   },     

  1112.   _adjust: function() {     

  1113.     var self = this     

  1114.     var o = self.opts     

  1115.     var formElements = self._getFormElements()     

  1116.     var curTab = self._getCurrentTabIdx()     

  1117.     // Autocomplete causes some problems...     

  1118.     formElements.inputs.attr('autocomplete', 'off')     

  1119.     // Show tabs to calculate dimensions     

  1120.     if ( self.$tabs.length ) { self.$tabs.show() }     

  1121.     // Adjust labels     

  1122.     var labels = formElements.labels     

  1123.     labels.removeAttr('style').width( Utils.getMaxWidth( labels ) )     

  1124.     // Adjust headings and separators     

  1125.     if ( self.$tabs.length ) {     

  1126.       this.$tabs.each(function(){     

  1127.         $( this ).find('.ideal-heading:first').addClass('first-child')     

  1128.       })     

  1129.     } else {     

  1130.       self.$form.find('.ideal-heading:first').addClass('first-child')     

  1131.     }     

  1132.     self._setDatepicker()     

  1133.     // Done calculating hide tabs     

  1134.     if ( self.$tabs.length ) {     

  1135.       self.$tabs.hide()     

  1136.       self.switchTab( curTab )     

  1137.     }     

  1138.   },     

  1139.   _setDatepicker: function() {     

  1140.     var o = this.opts     

  1141.     var $datepicker = this.$form.find('input.datepicker')     

  1142.     if ( jQuery.ui && $datepicker.length ) {     

  1143.       $datepicker.each(function() {     

  1144.         var userInput = o.inputs[ this.name ]     

  1145.         var data = userInput && userInput.data && userInput.data.date     

  1146.         var format = data ? data.replace( 'yyyy', 'yy' ) : 'mm/dd/yy'     

  1147.         $(this).datepicker({     

  1148.           dateFormat: format,     

  1149.           beforeShow: function( input ) {     

  1150.             $( input ).addClass('open')     

  1151.           },     

  1152.           onChangeMonthYear: function() {     

  1153.             // Hack to fix IE9 not resizing     

  1154.             var $this = $(this)     

  1155.             var w = $this.outerWidth() // cache first!     

  1156.             setTimeout(function() {     

  1157.               $this.datepicker('widget').css( 'width', w )     

  1158.             }, 1)     

  1159.           },     

  1160.           onClose: function() { $(this).removeClass('open') }     

  1161.         })     

  1162.       })     

  1163.       // Adjust width     

  1164.       $datepicker.on('focus keyup', function() {     

  1165.         var t = $(this), w = t.outerWidth()     

  1166.         t.datepicker('widget').css( 'width', w )     

  1167.       })     

  1168.       $datepicker.parent().siblings('.ideal-error').addClass('hidden')     

  1169.     }     

  1170.   },     

  1171.   _doMarkup: function( $element ) {     

  1172.     var o = this.opts     

  1173.     var elementType = Utils.getIdealType( $element )     

  1174.     // Validation elements     

  1175.     var $field = $('span class="ideal-field"/>')     

  1176.     var $error = $('span class="ideal-error" />')     

  1177.     var $valid = $('i class="ideal-icon ideal-icon-valid" />')     

  1178.     var $invalid = $('i class="ideal-icon ideal-icon-invalid"/>')     

  1179.       .click(function(){     

  1180.         $(this).parent().find('input:first, textarea, select').focus()     

  1181.       })     

  1182.     // Basic markup     

  1183.     $element.closest('div').addClass('ideal-wrap')     

  1184.       .children('label:first-child').addClass('ideal-label')     

  1185.     var idealElements = {     

  1186.       _defaultInput: function() {     

  1187.         $element.wrapAll( $field ).after( $valid, $invalid )     

  1188.           .parent().after( $error )     

  1189.       },     

  1190.       text: function() { idealElements._defaultInput() },     

  1191.       radiocheck: function() {     

  1192.         // Check if input is already wrapped so we don't     

  1193.         // wrap radios and checks more than once     

  1194.         var isWrapped = $element.parents('.ideal-field').length     

  1195.         if ( !isWrapped ) {     

  1196.           $element.parent().nextAll().andSelf().wrapAll( $field.addClass('ideal-radiocheck') )     

  1197.           $element.parents('.ideal-field').append( $valid, $invalid ).after( $error )     

  1198.         }     

  1199.         if ( !/radiocheck/.test( o.disableCustom ) ) {     

  1200.           $element.idealRadioCheck()     

  1201.         }     

  1202.       },     

  1203.       select: function() {     

  1204.         idealElements._defaultInput()     

  1205.         if ( !/select/.test( o.disableCustom ) ) {     

  1206.           $element.idealSelect()     

  1207.         }     

  1208.       },     

  1209.       file: function() {     

  1210.         idealElements._defaultInput()     

  1211.         if ( !/file/.test( o.disableCustom ) ) {     

  1212.           $element.idealFile()     

  1213.         }     

  1214.       },     

  1215.       button: function() {     

  1216.         if ( !/button/.test( o.disableCustom ) ) {     

  1217.           $element.addClass('ideal-button')     

  1218.         }     

  1219.       },     

  1220.       hidden: function() {     

  1221.         $element.closest('div').addClass('ideal-hidden')     

  1222.       },     

  1223.       heading: function() {     

  1224.         $element.closest('div').addClass('ideal-full-width')     

  1225.         $element.parent().children().wrapAll('span class="ideal-heading"/>')     

  1226.       },     

  1227.       separator: function() {     

  1228.         $element.closest('div').addClass('ideal-full-width')     

  1229.         $element.wrapAll('div class="ideal-separator"/>')     

  1230.       }     

  1231.     }     

  1232.     // Generate markup for current element type     

  1233.     idealElements[ elementType ] ? idealElements[ elementType ]() : $.noop()     

  1234.     $error.add( $valid ).add( $invalid ).hide() // Start fresh     

  1235.   },     

  1236.   /** Validates an input and shows or hides error and icon    

  1237.    * @memberOf Actions    

  1238.    * @param {object} $input jQuery object    

  1239.    * @param {string} e The JavaScript event    

  1240.    */     

  1241.   _validate: function( $input, e ) {     

  1242.     var self = this     

  1243.     var o = this.opts     

  1244.     var userOptions = o.inputs[ $input.attr('name') ]     

  1245.     var userFilters = userOptions.filters && userOptions.filters.split(/\s/)     

  1246.     var name = $input.attr('name')     

  1247.     var value = $input.val()     

  1248.     var ajaxRequest = $.idealforms.ajaxRequests[ name ]     

  1249.     var isRadioCheck = $input.is('[type="checkbox"], [type="radio"]')     

  1250.     var inputData = {     

  1251.       // If is radio or check validate all inputs related by name     

  1252.       input: isRadioCheck ? self.$form.find('[name="' + name + '"]') : $input,     

  1253.       userOptions: userOptions     

  1254.     }     

  1255.     // Validation elements     

  1256.     var $field = $input.parents('.ideal-field')     

  1257.     var $error = $field.siblings('.ideal-error')     

  1258.     var $invalid = isRadioCheck     

  1259.       ? $input.parent().siblings('.ideal-icon-invalid')     

  1260.       : $input.siblings('.ideal-icon-invalid')     

  1261.     var $valid = isRadioCheck     

  1262.       ? $input.parent().siblings('.ideal-icon-valid')     

  1263.       : $input.siblings('.ideal-icon-valid')     

  1264.     function resetError() {     

  1265.       $field.removeClass('valid invalid').removeData('ideal-isvalid')     

  1266.       $error.add( $invalid ).add( $valid ).hide()     

  1267.     }     

  1268.     function showOrHideError( error, valid ) {     

  1269.       resetError()     

  1270.       valid ? $valid.show() : $invalid.show()     

  1271.       $field.addClass( valid ? 'valid' : 'invalid' )     

  1272.       $field.data( 'ideal-isvalid', valid )     

  1273.       if ( !valid ) {     

  1274.         $error.html( error ).toggle( $field.is('.ideal-field-focus') )     

  1275.       }     

  1276.     }     

  1277.     // Prevent validation when typing but not introducing any new characters     

  1278.     // This is mainly to prevent multiple AJAX requests     

  1279.     var oldValue = $input.data('ideal-value') || 0     

  1280.     $input.data( 'ideal-value', value )     

  1281.     if ( e.type === 'keyup' && value === oldValue ) { return false }     

  1282.     // Validate     

  1283.     if ( userFilters ) {     

  1284.       $.each( userFilters, function( i, filter ) {     

  1285.         var theFilter = $.idealforms.filters[ filter ]     

  1286.         var customError = userOptions.errors && userOptions.errors[ filter ]     

  1287.         var error = ''     

  1288.         // If field is empty and not required     

  1289.         if ( !value && filter !== 'required' ) {     

  1290.           resetError()     

  1291.           return false     

  1292.         }     

  1293.         if ( theFilter ) {     

  1294.           // Abort and reset ajax if there's a request pending     

  1295.           if ( e.type === 'keyup' && ajaxRequest ) {     

  1296.             ajaxRequest.abort()     

  1297.             $field.removeClass('ajax')     

  1298.           }     

  1299.           // AJAX     

  1300.           if ( filter === 'ajax' ) {     

  1301.  nbsp;           showOrHideError( error, false ) // set invalid till response comes back     

  1302.             $error.hide()     

  1303.             if ( e.type === 'keyup' ) {     

  1304.               theFilter.regex( inputData, value, showOrHideError ) // runs the ajax callback     

  1305.             } else {     

  1306.               var ajaxError = $input.data('ideal-ajax-error')     

  1307.               if ( ajaxError ) {     

  1308.                 showOrHideError( ajaxError, $input.data('ideal-ajax-resp') || false )     

  1309.               }     

  1310.             }     

  1311.           }     

  1312.           // All other filters     

  1313.           else {     

  1314.             var valid = Utils.isRegex( theFilter.regex ) && theFilter.regex.test( value ) ||     

  1315.                         Utils.isFunction( theFilter.regex ) && theFilter.regex( inputData, value )     

  1316.             error = customError || theFilter.error // assign error after calling regex()     

  1317.             showOrHideError( error, valid )     

  1318.             if ( !valid ) { return false }     

  1319.           }     

  1320.         }     

  1321.       })     

  1322.     }     

  1323.     // Reset if there are no filters     

  1324.     else {     

  1325.       resetError()     

  1326.     }     

  1327.     // Flags     

  1328.     var flags = (function(){     

  1329.       var f = userOptions.flags && userOptions.flags.split(' ') || []     

  1330.       if ( o.globalFlags ) {     

  1331.         $.each( o.globalFlags.split(' '), function( i,v ) { f.push(v) })     

  1332.       }     

  1333.       return f     

  1334.     }())     

  1335.     if ( flags.length ) {     

  1336.       $.each(flags, function( i,f ) {     

  1337.         var theFlag = $.idealforms.flags[f]     

  1338.         if ( theFlag ) { theFlag( $input, e.type ) }     

  1339.       })     

  1340.     }     

  1341.     // Update counter     

  1342.     if ( self.$tabs.length ) {     

  1343.       self._updateTabsCounter( self._getCurrentTabIdx() )     

  1344.     }     

  1345.   },     

  1346.   _attachEvents: function() {     

  1347.     var self = this     

  1348.     self._getUserInputs().on('keyup change focus blur', function(e) {     

  1349.       var $this = $(this)     

  1350.       var $field = $this.parents('.ideal-field')     

  1351.       var isFile = $this.is('input[type=file]')     

  1352.       // Trigger on change if type=file cuz custom file     

  1353.       // disables focus on original file input (tabIndex = -1)     

  1354.       if ( e.type === 'focus' || isFile && e.type === 'change' ) {     

  1355.         $field.addClass('ideal-field-focus')     

  1356.       }     

  1357.       if ( e.type === 'blur' ) {     

  1358.         $field.removeClass('ideal-field-focus')     

  1359.       }     

  1360.       self._validate( $this, e )     

  1361.     })     

  1362.   },     

  1363.   _responsive: function() {     

  1364.     var formElements = this._getFormElements()     

  1365.     var maxWidth = LessVars.fieldWidth + formElements.labels.outerWidth()     

  1366.     var $emptyLabel = formElements.labels.filter(function() {     

  1367.       return $(this).html() === ' '     

  1368.     })     

  1369.     var $customSelect = this.$form.find('.ideal-select')     

  1370.     this.opts.responsiveAt === 'auto'     

  1371.       ? this.$form.toggleClass( 'stack', this.$form.width()  maxWidth )     

  1372.       : this.$form.toggleClass( 'stack', $(window).width()  this.opts.responsiveAt )     

  1373.     var isStack = this.$form.is('.stack')     

  1374.     $emptyLabel.toggle( !isStack )     

  1375.     $customSelect.trigger( isStack ? 'list' : 'menu' )     

  1376.     // Hide datePicker     

  1377.     var $datePicker = this.$form.find('input.hasDatepicker')     

  1378.     if ( $datePicker.length ) { $datePicker.datepicker('hide') }     

  1379.   }     

  1380. })     

  1381. /*    

  1382.  * Public Methods    

  1383.  */     

  1384. $.extend( IdealForms.prototype, {     

  1385.   getInvalid: function() {     

  1386.     return this.$form.find('.ideal-field').filter(function() {     

  1387.       return $(this).data('ideal-isvalid') === false     

  1388.     })     

  1389.   },     

  1390.   getInvalidInTab: function( nameOrIdx ) {     

  1391.     return this._getTab( nameOrIdx ).find('.ideal-field').filter(function() {     

  1392.       return $(this).data('ideal-isvalid') === false     

  1393.     })     

  1394.   },     

  1395.   isValid: function() {     

  1396.     return !this.getInvalid().length     

  1397.   },     

  1398.   isValidField: function( field ) {     

  1399.     var $input = Utils.getByNameOrId( field )     

  1400.     return $input.parents('.ideal-field').data('ideal-isvalid') === true     

  1401.   },     

  1402.   focusFirst: function() {     

  1403.     if ( this.$tabs.length ) {     

  1404.       this.$tabs.filter(':visible')     

  1405.         .find('.ideal-field:first')     

  1406.         .find('input:first, select, textarea').focus()     

  1407.     } else {     

  1408.       this.$form.find('.ideal-field:first')     

  1409.         .find('input:first, select, textarea').focus()     

  1410.     }     

  1411.     return this     

  1412.   },     

  1413.   focusFirstInvalid: function() {     

  1414.     var $first = this.getInvalid().first().find('input:first, select, textarea')     

  1415.     var tabName = $first.parents('.ideal-tabs-content').data('ideal-tabs-content-name')     

  1416.     if ( this.$tabs.length ) {     

  1417.       this.switchTab( tabName )     

  1418.     }     

  1419.     $first.focus()     

  1420.     return this     

  1421.   },     

  1422.   switchTab: function( nameOrIdx ) {     

  1423.     this.$tabs.switchTab( nameOrIdx )     

  1424.     return this     

  1425.   },     

  1426.   nextTab: function() {     

  1427.     this.$tabs.nextTab()     

  1428.     return this     

  1429.   },     

  1430.   prevTab: function() {     

  1431.     this.$tabs.prevTab()     

  1432.     return this     

  1433.   },     

  1434.   firstTab: function() {     

  1435.     this.$tabs.firstTab()     

  1436.     return this     

  1437.   },     

  1438.   lastTab: function() {     

  1439.     this.$tabs.lastTab()     

  1440.     return this     

  1441.   },     

  1442.   fresh: function() {     

  1443.     this._getUserInputs().change().parents('.ideal-field')     

  1444.       .removeClass('valid invalid')     

  1445.     return this     

  1446.   },     

  1447.   freshFields: function( fields ) {     

  1448.     fields = Utils.convertToArray( fields )     

  1449.     $.each( fields, function( i ) {     

  1450.       var $input = Utils.getByNameOrId( fields[ i ] )     

  1451.       $input.change().parents('.ideal-field').removeClass('valid invalid')     

  1452.     })     

  1453.     return this     

  1454.   },     

  1455.   reload: function() {     

  1456.     this._adjust()     

  1457.     this._attachEvents()     

  1458.     return this     

  1459.   },     

  1460.   reset: function() {     

  1461.     var formElements = this._getFormElements()     

  1462.     formElements.text.val('') // text inputs     

  1463.     formElements.radiocheck.removeAttr('checked') // radio & check     

  1464.     // Select and custom select     

  1465.     formElements.select.find('option').first().prop( 'selected', true )     

  1466.     this.$form.find('.ideal-select').trigger('reset')     

  1467.     if ( this.$tabs.length ) { this.firstTab() }     

  1468.     this.focusFirst().fresh()     

  1469.     return this     

  1470.   },     

  1471.   resetFields: function( fields ) {     

  1472.     fields = Utils.convertToArray( fields )     

  1473.     var formElements = this._getFormElements()     

  1474.     $.each( fields, function( i, v ) {     

  1475.       var $input = Utils.getByNameOrId( v )     

  1476.       var type = Utils.getIdealType( $input )     

  1477.       if ( type === 'text' || type === 'file' ) {     

  1478.         $input.val('')     

  1479.       }     

  1480.       if ( type === 'radiocheck' ) {     

  1481.         $input.removeAttr('checked') // radio & check     

  1482.       }     

  1483.       if ( type === 'select' ) {     

  1484.         $input.find('option').first().prop( 'selected', true )     

  1485.         $input.next('.ideal-select').trigger('reset')     

  1486.       }     

  1487.       $input.change()     

  1488.     })     

  1489.     this.freshFields( fields )     

  1490.     return this     

  1491.   },     

  1492.   toggleFields: function( fields ) {     

  1493.     fields = Utils.convertToArray( fields )     

  1494.     var self = this     

  1495.     var $fields = Utils.getFieldsFromArray( fields )     

  1496.     $fields.each(function() {     

  1497.       var $this = $(this)     

  1498.       var name = $this.attr('name') || $this.attr('id')     

  1499.       var input = self.opts.inputs[ name ]     

  1500.       var filters = input && input.filters     

  1501.       var dataFilters = $this.data('ideal-filters') || ''     

  1502.       $this.data( 'ideal-filters', filters )     

  1503.       $this.closest('.ideal-wrap').toggle()     

  1504.       self.setFieldOptions( name, { filters: dataFilters } )     

  1505.     })     

  1506.     return this     

  1507.   },     

  1508.   setOptions: function( options ) {     

  1509.     $.extend( true, this.opts, options )     

  1510.     this.reload().fresh()     

  1511.     return this     

  1512.   },     

  1513.   setFieldOptions: function( name, options ) {     

  1514.     $.extend( true, this.opts.inputs[ name ], options )     

  1515.     this.reload().freshFields([ name ])     

  1516.     return this     

  1517.   },     

  1518.   addFields: function( fields ) {     

  1519.     fields = Utils.convertToArray( fields )     

  1520.     var self = this     

  1521.     // Save names of all inputs in Array     

  1522.     // to use methods that take names ie. fresh()     

  1523.     var allNames = []     

  1524.     // Add an input to the DOM     

  1525.     function add( ops ) {     

  1526.       var name = ops.name     

  1527. &nbspnbsp;     var userOptions = {     

  1528.         filters: ops.filters || '',     

  1529.         data: ops.data || {},     

  1530.         errors: ops.errors || {},     

  1531.         flags: ops.flags || ''     

  1532.       }     

  1533.       var label = ops.label || ''     

  1534.       var type = ops.type     

  1535.       var list = ops.list || []     

  1536.       var placeholder = ops.placeholder || ''     

  1537.       var value = ops.value || ''     

  1538.       var $field = $('div>'+     

  1539.           'label>'+ label +':label>'+     

  1540.           Utils.makeInput( name, value, type, list, placeholder ) +     

  1541.         'div>')     

  1542.       var $input = $field.find('input, select, textarea, :button')     

  1543.       // Add inputs with filters to the list     

  1544.       // of user inputs to validate     

  1545.       if ( userOptions.filters ) { self.opts.inputs[ name ] = userOptions }     

  1546.       self._doMarkup( $input )     

  1547.       // Insert in DOM     

  1548.       if ( ops.addAfter ) {     

  1549.         $field.insertAfter(     

  1550.           $( Utils.getByNameOrId( ops.addAfter ) ).parents('.ideal-wrap')     

  1551.         )     

  1552.       } else if ( ops.addBefore ) {     

  1553.         $field.insertBefore(     

  1554.           $(Utils.getByNameOrId( ops.addBefore ))     

  1555.           .parents('.ideal-wrap')     

  1556.         )     

  1557.       } else if ( ops.appendToTab ) {     

  1558.         $field.insertAfter(     

  1559.           self._getTab( ops.appendToTab ).find('.ideal-wrap:last-child')     

  1560.         )     

  1561.       } else {     

  1562.         $field.insertAfter( self.$form.find('.ideal-wrap').last() )     

  1563.       }     

  1564.       // Add current field name to list of names     

  1565.       allNames.push( name )     

  1566.     }     

  1567.     // Run through each input     

  1568.     $.each( fields, function( i, ops ) { add( ops ) })     

  1569.     self.reload()     

  1570.     self.freshFields( allNames )     

  1571.     self._responsive()     

  1572.     return this     

  1573.   },     

  1574.   removeFields: function( fields ) {     

  1575.     fields = Utils.convertToArray( fields )     

  1576.     var $fields = Utils.getFieldsFromArray( fields )     

  1577.     $fields.parents('.ideal-wrap').remove()     

  1578.     this.reload()     

  1579.     return this     

  1580.   }     

  1581. })     

  1582. }( jQuery, window, document ))