抽時間寫了一個帶有自動校驗功能的Html5用戶注冊Demo。使用到Handlebars模板技術和手機驗證碼校驗。
以下是效果截圖:
1.頁面代碼:usersRegister.hbs
XML/HTML Code復制內容到剪貼板
- <!DOCTYPE html>
- <!--[if IE 8 ]> <html lang="en" class="ie8"> <![endif]-->
- <!--[if IE 9 ]> <html lang="en" class="ie9"> <![endif]-->
- <!--[if (gt IE 9)|!(IE)]><!-->
- <html lang="en">
- <!--<![endif]-->
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
- <meta http-equiv="X-UA-Compatible" content="IE=edge" />
- <title>用戶注冊</title>
- <!--[if lt IE 9]>
- <script src="/assets/scripts/html5shiv.js"></script>
- <![endif]-->
- <link href="/assets/styles/jquery.idealforms.min.css" rel="stylesheet" media="screen" />
- <style type="text/css">
- body {
- font: normal 15px/1.5 Arial, Helvetica, Free Sans, sans-serif;
- color: #222;
- overflow-y: scroll;
- padding: 60px 0 0 0;
- }
- .main {
- width: 560px;
- height: 480px;
- margin: -50px auto;
- }
- #my-form {
- width: 560px;
- height: 450px;
- margin: 0 auto;
- border: 1px solid #ccc;
- padding: 3em;
- border-radius: 3px;
- box-shadow: 0 0 2px rgba(0, 0, 0, .2);
- }
- </style>
- <script type="text/javascript" src="/assets/scripts/jquery-1.8.2.min.js"></script>
- <script type="text/javascript" src="/assets/scripts/jquery.idealforms.js"></script>
- </head>
- <body>
- <!-- style="background-image: url(static/image/bg.jpg) -->
- <div class="main" >
- <div style="height:5px;text-align:center;font-size:25px"> 歡迎您注冊!</div>
- <!-- Begin Form -->
- <form id="my-form" class="myform">
- <div>
- <label>用戶名:</label><input id="username" name="username" type="text" />
- </div>
- <div>
- <!-- <label>密碼:</label><input id="pass" name="password" type="password" /> -->
- <label>密碼:</label><input id="pass" name="password" type="text" />
- </div>
- <div>
- <label>郵箱:</label><input id="email" name="email"
- data-ideal="required email" type="email" />
- </div>
- <div>
- <label>電話:</label><input id="telephone" type="text" name="phone" data-ideal="phone" />
- </div>
- <div>
- <label>供應商V碼:</label><input id="vCode" type="text" name="vCode" data-ideal="vCode" />
- </div>
- <div>
- <label>真實姓名:</label><input id="trueName" type="text" name="trueName" data-ideal="trueName" />
- </div>
- <div>
- <label>手機驗證碼:</label><input id="telCode" type="text" name="telCode" data-ideal="telCode" />
- </div>
- <div style="margin-bottom:5px;">
- <button id="getTelCode" type="button" style="margin-left:160px; margin-right:auto;" >獲取手機校驗碼</button>
- <hr style="margin-top:5px; margin-bottom:5px;" />
- </div>
- <!--<div>
- <label>性別:</label>
- <select id="sex" name="sex">
- <option value="男">男</option>
- <option value="女">女</option>
- </select>
- </div>
- <div>
- <label>昵稱:</label><input id="nickName" type="text" name="nickName" data-ideal="nickName" />
- </div>
- <div>
- <label>年齡:</label><input id="age" type="text" name="age" data-ideal="age" />
- </div>-->
- <!-- <div>
- <label>地址:</label><input type="text" name="address" data-ideal="address" />
- </div>
- <div>
- <label>QQ:</label><input type="text" name="qq" data-ideal="qq" />
- </div>
- <div>
- <label>郵編:</label><input type="text" name="zip" data-ideal="zip" />
- </div>
- <div>
- <label>傳真:</label><input type="text" name="fax" data-ideal="fax" />
- </div>
- <div>
- <label>身份證:</label><input type="text" name="creditID" data-ideal="creditID" />
- </div>
- <div>
- <label>出生日期:</label><input name="date" class="datepicker"
- data-ideal="date" type="text" placeholder="月/日/年" />
- </div>
- <div>
- <label>上傳頭像:</label><input id="file" name="file" multiple
- type="file" />
- </div>
- <div>
- <label>個人主頁:</label><input name="website" data-ideal="url"
- type="text" />
- </div>
- <div>
- <label>備注:</label>
- <textarea id="comments" name="comments"></textarea>
- </div>
- -->
- <!-- <div id="languages">
- <label>語言:</label> <label><input type="checkbox"
- name="langs[]" value="English" />英文</label> <label><input
- type="checkbox" name="langs[]" value="Chinese" />中文</label> <label><input
- type="checkbox" name="langs[]" value="Spanish" />西班牙文</label> <label><input
- type="checkbox" name="langs[]" value="French" />法文</label>
- </div>
- <div>
- <label>精通幾門:</label> <label><input type="radio"
- name="radio" checked />1</label> <label><input type="radio"
- name="radio" />2</label> <label><input type="radio" name="radio" />3</label>
- <label><input type="radio" name="radio" />4</label>
- </div>
- <div>
- <label>國籍:</label> <select id="states" name="states">
- <option value="default">– 選擇國籍 –</option>
- <option value="AL">阿拉伯</option>
- <option value="AK">中國</option>
- <option value="AZ">美國</option>
- <option value="AR">法國</option>
- <option value="CA">英國</option>
- <option value="CO">德國</option>
- <option value="CT">西班牙</option>
- <option value="DE">俄羅斯</option>
- </select>
- </div> -->
- <div style="margin-top:10px; margin-left:100px;margin-right:100px;">
- <button type="button" id="submit" class="submit">提交</button>
- <button id="reset" type="button" >重置</button>
- </div>
- </form>
- <!-- End Form -->
- </div>
- <script type="text/javascript">
- var options = {
- onFail : function() {
- alert($myform.getInvalid().length + ' invalid fields.')
- },
- inputs : {
- 'password' : {
- filters : 'required pass'
- },
- 'username' : {
- filters : 'required username'
- },
- 'email' : {
- filters : 'required email'
- },
- 'phone' : {
- filters : 'required phone'
- },
- 'trueName' : {
- filters : 'required'
- },
- 'vCode' : {
- filters : 'required'
- },
- 'telCode' : {
- filters : 'required'
- }
- /*
- 'age' : {
- filters : 'required digits',
- data : {
- min : 16,
- max : 70
- }
- },
- 'file' : {
- filters : 'extension',
- data : {
- extension : [ 'jpg' ]
- }
- },
- 'comments' : {
- filters : 'min max',
- data : {
- min : 50,
- max : 200
- }
- },
- 'states' : {
- filters : 'exclude',
- data : {
- exclude : [ 'default' ]
- },
- errors : {
- exclude : '選擇國籍.'
- }
- },
- 'langs[]' : {
- filters : 'min max',
- data : {
- min : 2,
- max : 3
- },
- errors : {
- min : 'Check at least <strong>2</strong> options.',
- max : 'No more than <strong>3</strong> options allowed.'
- }
- }
- */
- }
- };
- $('#getTelCode').click(function() {
- var telephone = document.getElementById("telephone").value; //手機號碼
- if (telephone == null || telephone == ""){
- alert("手機號碼不能為空!");
- }
- else{
- $.ajax({
- type : "GET",
- dataType : "json",
- url : "../api/getTelCode?telephone="+ telephone,
- success : function(msg) {
- },
- error : function(e) {
- alert("獲取手機校驗碼失敗!" + e);
- }
- });
- }
- });
- var $myform = $('#my-form').idealforms(options).data('idealforms');
- $('#submit').click(function() {
- var username = document.getElementById("username").value; //用戶名
- var password = document.getElementById("pass").value; //密碼
- var email = document.getElementById("email").value; //郵箱
- var telephone = document.getElementById("telephone").value; //手機號碼
- var vCode = document.getElementById("vCode").value; //公司V碼
- var telCode = document.getElementById("telCode").value; //手機校驗碼
- var trueName = document.getElementById("trueName").value; //真實姓名
- $.ajax({
- type : "GET",
- url : "../api/usersRegister?username="+ username +"password="+ password +"email="+ email +"telephone="+ telephone +"vCode="+ vCode +"telCode="+ telCode +"trueName="+ trueName,
- success : function(msg) {
- //獲取當前網址,如: http://localhost:8083/uimcardprj/share/meun.jsp
- var curWwwPath = window.document.location.href;
- //獲取主機地址之後的目錄,如: uimcardprj/share/meun.jsp
- var pathName = window.document.location.pathname;
- var pos = curWwwPath.indexOf(pathName);
- //獲取主機地址,如: http://localhost:8083
- var localhostPaht = curWwwPath.substring(0, pos);
- //獲取帶"/"的項目名,如:/uimcardprj
- var projectName = pathName.substring(0, pathName.substr(1).indexOf('/') + 1);
- window.location.href = projectName + "/login";
- alert("注冊成功!");
- },
- error : function(e) {
- alert("注冊失敗!" + e);
- }
- });
- });
- $('#reset').click(function() {
- $myform.reset().fresh().focusFirst();
- });
- </script>
- </body>
- </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復制內容到剪貼板
- /*--------------------------------------------------------------------------
- jq-idealforms 2.1
- * Author: Cedric Ruiz
- * License: GPL or MIT
- * Demo: http://elclanrs.github.com/jq-idealforms/
- *
- --------------------------------------------------------------------------*/
- ;(function ( $, window, document, undefined ) {
- 'use strict';
- // Global Ideal Forms namespace
- $.idealforms = {}
- $.idealforms.filters = {}
- $.idealforms.errors = {}
- $.idealforms.flags = {}
- $.idealforms.ajaxRequests = {}
- /*--------------------------------------------------------------------------*/
- /**
- * @namespace A chest for various Utils
- */
- var Utils = {
- /**
- * Get width of widest element in the collection.
- * @memberOf Utils
- * @param {jQuery object} $elms
- * @returns {number}
- */
- getMaxWidth: function( $elms ) {
- var maxWidth = 0
- $elms.each(function() {
- var width = $(this).outerWidth()
- if ( width > maxWidth ) {
- maxWidth = width
- }
- })
- return maxWidth
- },
- /**
- * Hacky way of getting LESS variables
- * @memberOf Utils
- * @param {string} name The name of the LESS class.
- * @param {string} prop The css property where the data is stored.
- * @returns {number, string}
- */
- getLessVar: function( name, prop ) {
- var value = $('<p class="' + name + '"></p>').hide().appendTo('body').css( prop )
- $('.' + name).remove()
- return ( /^\d+/.test( value ) ? parseInt( value, 10 ) : value )
- },
- /**
- * Like ES5 Object.keys
- */
- getKeys: function( obj ) {
- var keys = []
- for(var key in obj) {
- if ( obj.hasOwnProperty( key ) ) {
- keys.push( key )
- }
- }
- return keys
- },
- // Get lenght of an object
- getObjSize: function( obj ) {
- var size = 0, key;
- for ( key in obj ) {
- if ( obj.hasOwnProperty( key ) ) {
- size++;
- }
- }
- return size;
- },
- isFunction: function( obj ) {
- return typeof obj === 'function'
- },
- isRegex: function( obj ) {
- return obj instanceof RegExp
- },
- isString: function( obj ) {
- return typeof obj === 'string'
- },
- getByNameOrId: function( str ) {
- var $el = $('[name="'+ str +'"]').length
- ? $('[name="'+ str +'"]') // by name
- : $('#'+ str) // by id
- return $el.length
- ? $el
- : $.error('The field "'+ str + '" doesn\'t exist.')
- },
- getFieldsFromArray: function( fields ) {
- var f = []
- for ( var i = 0, l = fields.length; i < l; i++ ) {
- f.push( Utils.getByNameOrId( fields[i] ).get(0) )
- }
- return $( f )
- },
- convertToArray: function( obj ) {
- return Object.prototype.toString.call( obj ) === '[object Array]'
- ? obj : [ obj ]
- },
- /**
- * Determine type of any Ideal Forms element
- * @param $input jQuery $input object
- */
- getIdealType: function( $el ) {
- var type = $el.attr('type') || $el[0].tagName.toLowerCase()
- return (
- /(text|password|email|number|search|url|tel|textarea)/.test( type ) && 'text' ||
- /file/.test( type ) && 'file' ||
- /select/.test( type ) && 'select' ||
- /(radio|checkbox)/.test( type ) && 'radiocheck' ||
- /(button|submit|reset)/.test( type ) && 'button' ||
- /h\d/.test( type ) && 'heading' ||
- /hr/.test( type ) && 'separator' ||
- /hidden/.test( type ) && 'hidden'
- )
- },
- /**
- * Generates an input
- * @param name `name` attribute of the input
- * @param type `type` or `tagName` of the input
- */
- makeInput: function( name, value, type, list, placeholder ) {
- var markup, items = [], item, i, len
- function splitValue( str ) {
- var item, value, arr
- if ( /::/.test( str ) ) {
- arr = str.split('::')
- item = arr[ 0 ]
- value = arr[ 1 ]
- } else {
- item = value = str
- }
- return { item: item, value: value }
- }
- // Text & file
- if ( /^(text|password|email|number|search|url|tel|file|hidden)$/.test(type) )
- markup = '<input '+
- 'type="'+ type +'" '+
- 'id="'+ name +'" '+
- 'name="'+ name +'" '+
- 'value="'+ value +'" '+
- (placeholder && 'placeholder="'+ placeholder +'"') +
- '/>'
- // Textarea
- if ( /textarea/.test( type ) ) {
- markup = '<textarea id="'+ name +'" name="'+ name +'" value="'+ value +'"></textarea>'
- }
- // Select
- if ( /select/.test( type ) ) {
- items = []
- for ( i = 0, len = list.length; i < len; i++ ) {
- item = splitValue( list[ i ] ).item
- value = splitValue( list[ i ] ).value
- items.push('<option value="'+ value +'">'+ item +'</option>')
- }
- markup =
- '<select id="'+ name +'" name="'+ name +'">'+
- items.join('') +
- '</select>'
- }
- // Radiocheck
- if ( /(radio|checkbox)/.test( type ) ) {
- items = []
- for ( i = 0, len = list.length; i < len; i++ ) {
- item = splitValue( list[ i ] ).item
- value = splitValue( list[ i ] ).value
- items.push(
- '<label>'+
- '<input type="'+ type +'" name="'+ name +'" value="'+ value +'" />'+
- item +
- '</label>'
- )
- }
- markup = items.join('')
- }
- return markup
- }
- }
- /**
- * Custom tabs for Ideal Forms
- */
- $.fn.idealTabs = function (container) {
- var
- // Elements
- $contents = this,
- $containercontainer = container,
- $wrapper = $('<ul class="ideal-tabs-wrap"/>'),
- $tabs = (function () {
- var tabs = []
- $contents.each(function () {
- var name = $(this).attr('name')
- var html =
- '<li class="ideal-tabs-tab">'+
- '<span>' + name + '</span>'+
- '<i class="ideal-tabs-tab-counter ideal-tabs-tab-counter-zero">0</i>'+
- '</li>'
- tabs.push(html)
- })
- return $(tabs.join(''))
- }()),
- Actions = {
- getCurIdx: function () {
- return $tabs
- .filter('.ideal-tabs-tab-active')
- .index()
- },
- getTabIdxByName: function (name) {
- var re = new RegExp(name, 'i')
- var $tab = $tabs.filter(function () {
- return re.test($(this).text())
- })
- return $tab.index()
- }
- },
- /**
- * Public methods
- */
- Methods = {
- /**
- * Switch tab
- */
- switchTab: function (nameOrIdx) {
- var idx = Utils.isString(nameOrIdx)
- ? Actions.getTabIdxByName(nameOrIdx)
- : nameOrIdx
- $tabs.removeClass('ideal-tabs-tab-active')
- $tabs.eq(idx).addClass('ideal-tabs-tab-active')
- $contents.hide().eq(idx).show()
- },
- nextTab: function () {
- var idx = Actions.getCurIdx() + 1
- idx > $tabs.length - 1
- ? Methods.firstTab()
- : Methods.switchTab(idx)
- },
- prevTab: function () {
- Methods.switchTab(Actions.getCurIdx() - 1)
- },
- firstTab: function () {
- Methods.switchTab(0)
- },
- lastTab: function () {
- Methods.switchTab($tabs.length - 1)
- },
- updateCounter: function (nameOrIdx, text) {
- var idx = !isNaN(nameOrIdx) ? nameOrIdx : Actions.getTabIdxByName(name),
- $counter = $tabs.eq(idx).find('.ideal-tabs-tab-counter')
- $counter.removeClass('ideal-tabs-tab-counter-zero')
- if (!text) {
- $counter.addClass('ideal-tabs-tab-counter-zero')
- }
- $counter.html(text)
- }
- }
- // Attach methods
- for (var m in Methods)
- $contents[m] = Methods[m]
- // Init
- $tabs.first()
- .addClass('ideal-tabs-tab-active')
- .end()
- .click(function () {
- var name = $(this).text()
- $contents.switchTab(name)
- })
- // Insert in DOM & Events
- $wrapper.append($tabs).appendTo($container)
- $contents.addClass('ideal-tabs-content')
- $contents.each(function () {
- var $this = $(this), name = $(this).attr('name')
- $this.data('ideal-tabs-content-name', name)
- .removeAttr('name')
- })
- $contents.hide().first().show() // Start fresh
- return $contents
- }
- /**
- * A custom <select> menu jQuery plugin
- * @example `$('select').idealSelect()`
- */
- $.fn.idealSelect = function () {
- return this.each(function () {
- var
- $select = $(this),
- $options = $select.find('option')
- /**
- * Generate markup and return elements of custom select
- * @memberOf $.fn.toCustomSelect
- * @returns {object} All elements of the new select replacement
- */
- var idealSelect = (function () {
- var
- $wrap = $('<ul class="ideal-select '+ $select.attr('name') +'"/>'),
- $menu = $(
- '<li><span class="ideal-select-title">' +
- $options.filter(':selected').text() +
- '</span></li>'
- ),
- items = (function () {
- var items = []
- $options.each(function () {
- var $this = $(this)
- items.push('<li class="ideal-select-item">' + $this.text() + '</li>')
- })
- return items
- }())
- $menu.append('<ul class="ideal-select-sub">' + items.join('') + '</ul>')
- $wrap.append($menu)
- return {
- select: $wrap,
- title: $menu.find('.ideal-select-title'),
- sub: $menu.find('.ideal-select-sub'),
- items: $menu.find('.ideal-select-item')
- }
- }())
- /**
- * @namespace Methods of custom select
- * @memberOf $.fn.toCustomSelect
- */
- var Actions = {
- getSelectedIdx: function () {
- return idealSelect.items
- .filter('.ideal-select-item-selected').index()
- },
- /**
- * @private
- */
- init: (function () {
- $select.css({
- position: 'absolute',
- left: '-9999px'
- })
- idealSelect.sub.hide()
- idealSelect.select.insertAfter($select)
- idealSelect.select.css(
- 'min-width',
- Utils.getMaxWidth(idealSelect.items)
- )
- idealSelect.items
- .eq($options.filter(':selected').index())
- .addClass('ideal-select-item-selected')
- }()),
- noWindowScroll: function (e) {
- if (e.which === 40 || e.which === 38 || e.which === 13) {
- e.preventDefault()
- }
- },
- // Fix loosing focus when scrolling
- // and selecting item with keyboard
- focusHack: function () {
- setTimeout(function () {
- $select.trigger('focus')
- }, 1)
- },
- focus: function () {
- idealSelect.select.addClass('ideal-select-focus')
- $(document).on('keydown.noscroll', Actions.noWindowScroll)
- },
- blur: function () {
- idealSelect.select
- .removeClass('ideal-select-open ideal-select-focus')
- $(document).off('.noscroll')
- },
- scrollIntoView: function (dir) {
- var
- $selected = idealSelect.items.filter('.ideal-select-item-selected'),
- itemHeight = idealSelect.items.outerHeight(),
- menuHeight = idealSelect.sub.outerHeight(),
- isInView = (function () {
- // relative position to the submenu
- var elPos = $selected.position().top + itemHeight
- return dir === 'down'
- ? elPos <= menuHeight
- : elPos > 0
- }())
- if (!isInView) {
- itemHeight = (dir === 'down')
- ? itemHeight // go down
- : -itemHeight // go up
- idealSelect.sub
- .scrollTop(idealSelect.sub.scrollTop() + itemHeight)
- }
- },
- scrollToItem: function () {
- var idx = Actions.getSelectedIdx(),
- height = idealSelect.items.outerHeight(),
- nItems = idealSelect.items.length,
- allHeight = height * nItems,
- curHeight = height * (nItems - idx)
- idealSelect.sub.scrollTop(allHeight - curHeight)
- },
- showMenu: function () {
- idealSelect.sub.fadeIn('fast')
- idealSelect.select.addClass('ideal-select-open')
- Actions.select(Actions.getSelectedIdx())
- Actions.scrollToItem()
- },
- hideMenu: function () {
- idealSelect.sub.hide()
- idealSelect.select.removeClass('ideal-select-open')
- },
- select: function (idx) {
- idealSelect.items
- .removeClass('ideal-select-item-selected')
- idealSelect.items
- .eq(idx).addClass('ideal-select-item-selected')
- },
- change: function (idx) {
- var text = idealSelect.items.eq(idx).text()
- Actions.select(idx)
- idealSelect.title.text(text)
- $options.eq(idx).prop('selected', true)
- $select.trigger('change')
- },
- keydown: function (key) {
- var
- idx = Actions.getSelectedIdx(),
- isMenu = idealSelect.select.is('.ideal-select-menu'),
- isOpen = idealSelect.select.is('.ideal-select-open')
- /**
- * @namespace Key pressed
- */
- var keys = {
- 9: function () { // TAB
- if (isMenu) {
- Actions.blur()
- Actions.hideMenu()
- }
- },
- 13: function () { // ENTER
- if (isMenu)
- isOpen
- ? Actions.hideMenu()
- : Actions.showMenu()
- Actions.change(idx)
- },
- 27: function () { // ESC
- if (isMenu) Actions.hideMenu()
- },
- 40: function () { // DOWN
- if (idx < $options.length - 1) {
- isOpen
- ? Actions.select(idx + 1)
- : Actions.change(idx + 1)
- }
- Actions.scrollIntoView('down')
- },
- 38: function () { // UP
- if (idx > 0) {
- isOpen
- ? Actions.select(idx - 1)
- : Actions.change(idx - 1)
- }
- Actions.scrollIntoView('up')
- },
- 'default': function () { // Letter
- var
- letter = String.fromCharCode(key),
- $matches = idealSelect.items
- .filter(function () {
- return /^\w+$/i.test( letter ) && // not allow modifier keys ( ctrl, cmd, meta, super... )
- new RegExp('^' + letter, 'i').test( $(this).text() ) // find first match
- }),
- nMatches = $matches.length,
- counter = idealSelect.select.data('counter') + 1 || 0,
- curKey = idealSelect.select.data('key') || key,
- newIdx = $matches.eq(counter).index()
- if (!nMatches) // No matches
- return false
- // If more matches with same letter
- if (curKey === key) {
- if (counter < nMatches) {
- idealSelect.select.data('counter', counter)
- }
- else {
- idealSelect.select.data('counter', 0)
- newIdx = $matches.eq(0).index()
- }
- }
- // If new letter
- else {
- idealSelect.select.data('counter', 0)
- newIdx = $matches.eq(0).index()
- }
- if (isOpen)
- Actions.select(newIdx)
- else
- Actions.change(newIdx)
- idealSelect.select.data('key', key)
- Actions.scrollToItem()
- Actions.focusHack()
- }
- }
- keys[key]
- ? keys[key]()
- : keys['default']()
- }
- }
- /**
- * @namespace Holds all events of custom select for "menu mode" and "list mode"
- * @memberOf $.fn.toCustomSelect
- */
- var events = {
- focus: Actions.focus,
- 'blur.menu': function () {
- Actions.blur()
- Actions.hideMenu()
- },
- 'blur.list': function () {
- Actions.blur()
- },
- keydown: function (e) {
- Actions.keydown(e.which)
- },
- 'clickItem.menu': function () {
- Actions.change($(this).index())
- Actions.hideMenu()
- },
- 'clickItem.list': function () {
- Actions.change($(this).index())
- },
- 'clickTitle.menu': function () {
- Actions.focus()
- Actions.showMenu()
- $select.trigger('focus')
- },
- 'hideOutside.menu': function () {
- $select.off('blur.menu')
- $(document).on('mousedown.ideal', function (evt) {
- if (!$(evt.target).closest(idealSelect.select).length) {
- $(document).off('mousedown.ideal')
- $select.on('blur.menu', events['blur.menu'])
- } else {
- Actions.focusHack()
- }
- })
- },
- 'mousedown.list': function () {
- Actions.focusHack()
- }
- }
- // Reset events
- var disableEvents = function () {
- idealSelect.select.removeClass('ideal-select-menu ideal-select-list')
- $select.off('.menu .list')
- idealSelect.items.off('.menu .list')
- idealSelect.select.off('.menu .list')
- idealSelect.title.off('.menu .list')
- }
- // Menu mode
- idealSelect.select.on('menu', function () {
- disableEvents()
- idealSelect.select.addClass('ideal-select-menu')
- Actions.hideMenu()
- $select.on({
- 'blur.menu': events['blur.menu'],
- 'focus.menu': events.focus,
- 'keydown.menu': events.keydown
- })
- idealSelect.select.on('mousedown.menu', events['hideOutside.menu'])
- idealSelect.items.on('click.menu', events['clickItem.menu'])
- idealSelect.title.on('click.menu', events['clickTitle.menu'])
- })
- // List mode
- idealSelect.select.on('list', function () {
- disableEvents()
- idealSelect.select.addClass('ideal-select-list')
- Actions.showMenu()
- $select.on({
- 'blur.list': events['blur.list'],
- 'focus.list': events.focus,
- 'keydown.list': events.keydown
- })
- idealSelect.select.on('mousedown.list', events['mousedown.list'])
- idealSelect.items.on('mousedown.list', events['clickItem.list'])
- })
- $select.keydown(function (e) {
- // Prevent default keydown event
- // to avoid bugs with Ideal Select events
- if (e.which !== 9) e.preventDefault()
- })
- // Reset
- idealSelect.select.on('reset', function(){
- Actions.change(0)
- })
- idealSelect.select.trigger('menu') // Default to "menu mode"
- })
- }
- /*
- * idealRadioCheck: jQuery plguin for checkbox and radio replacement
- * Usage: $('input[type=checkbox], input[type=radio]').idealRadioCheck()
- */
- $.fn.idealRadioCheck = function() {
- return this.each(function() {
- var $this = $(this)
- var $span = $('<span/>')
- $span.addClass( 'ideal-'+ ( $this.is(':checkbox') ? 'check' : 'radio' ) )
- $this.is(':checked') && $span.addClass('checked') // init
- $span.insertAfter( $this )
- $this.parent('label').addClass('ideal-radiocheck-label')
- .attr('onclick', '') // Fix clicking label in iOS
- $this.css({ position: 'absolute', left: '-9999px' }) // hide by shifting left
- // Events
- $this.on({
- change: function() {
- var $this = $(this)
- if ( $this.is('input[type="radio"]') ) {
- $this.parent().siblings('label').find('.ideal-radio').removeClass('checked')
- }
- $span.toggleClass( 'checked', $this.is(':checked') )
- },
- focus: function() { $span.addClass('focus') },
- blur: function() { $span.removeClass('focus') },
- click: function() { $(this).trigger('focus') }
- })
- })
- }
- ;(function( $ ) {
- // Browser supports HTML5 multiple file?
- var multipleSupport = typeof $('<input/>')[0].multiple !== 'undefined',
- isIE = /msie/i.test( navigator.userAgent )
- $.fn.idealFile = function() {
- return this.each(function() {
- var $file = $(this).addClass('ideal-file'), // the original file input
- // label that will be used for IE hack
- $wrap = $('<div class="ideal-file-wrap">'),
- $input = $('<input type="text" class="ideal-file-filename" />'),
- // Button that will be used in non-IE browsers
- $button = $('<button type="button" class="ideal-file-upload">Open</button>'),
- // Hack for IE
- $label = $('<label class="ideal-file-upload" for="'+ $file[0].id +'">Open</label>')
- // Hide by shifting to the left so we
- // can still trigger events
- $file.css({
- position: 'absolute',
- left: '-9999px'
- })
- $wrap.append( $input, ( isIE ? $label : $button ) ).insertAfter( $file )
- // Prevent focus
- $file.attr('tabIndex', -1)
- $button.attr('tabIndex', -1)
- $button.click(function () {
- $file.focus().click() // Open dialog
- })
- $file.change(function() {
- var files = [], fileArr, filename
- // If multiple is supported then extract
- // all filenames from the file array
- if ( multipleSupport ) {
- fileArr = $file[0].files
- for ( var i = 0, len = fileArr.length; i < len; i++ ) {
- files.push( fileArr[i].name )
- }
- filename = files.join(', ')
- // If not supported then just take the value
- // and remove the path to just show the filename
- } else {
- filename = $file.val().split('\\').pop()
- }
- $input.val( filename ) // Set the value
- .attr( 'title', filename ) // Show filename in title tootlip
- })
- $input.on({
- focus: function () { $file.trigger('change') },
- blur: function () { $file.trigger('blur') },
- keydown: function( e ) {
- if ( e.which === 13 ) { // Enter
- if ( !isIE ) { $file.trigger('click') }
- } else if ( e.which === 8 || e.which === 46 ) { // Backspace & Del
- // On some browsers the value is read-only
- // with this trick we remove the old input and add
- // a clean clone with all the original events attached
- $file.replaceWith( $file = $file.val('').clone( true ) )
- $file.trigger('change')
- $input.val('')
- } else if ( e.which === 9 ){ // TAB
- return
- } else { // All other keys
- return false
- }
- }
- })
- })
- }
- }( jQuery ))
- /**
- * @namespace Errors
- * @locale en
- */
- $.idealforms.errors = {
- 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>',
- zip: 'Must be a valid US zip code. <em>(e.g. 33245 or 33245-0003)</em>',
- url: 'Must be a valid URL. <em>(e.g. www.google.com)</em>',
- minChar: 'Must be at least <strong>{0}</strong> characters long.',
- minOption: 'Check at least <strong>{0}</strong> options.',
- maxChar: 'No more than <strong>{0}</strong> characters long.',
- maxOption: 'No more than <strong>{0}</strong> options allowed.',
- range: 'Must be a number between {0} and {1}.',
- date: 'Must be a valid date. <em>(e.g. {0})</em>',
- dob: 'Must be a valid date of birth.',
- exclude: '"{0}" is not available.',
- excludeOption: '{0}',
- equalto: 'Must be the same value as <strong>"{0}"</strong>',
- extension: 'File(s) must have a valid extension. <em>(e.g. "{0}")</em>',
- ajaxSuccess: '<strong>{0}</strong> is not available.',
- ajaxError: 'Server error...'
- }
- /**
- * Get all default filters
- * @returns object
- */
- var getFilters = function() {
- var filters = {
- required: {
- regex: /.+/,
- error: $.idealforms.errors.required
- },
- number: {
- regex: function( i, v ) { return !isNaN(v) },
- error: $.idealforms.errors.number
- },
- digits: {
- regex: /^\d+$/,
- error: $.idealforms.errors.digits
- },
- name: {
- regex: /^[A-Za-z]{3,}$/,
- error: $.idealforms.errors.name
- },
- username: {
- regex: /^[a-z](?=[\w.]{4,30}$)\w*\.?\w*$/i,
- error: $.idealforms.errors.username
- },
- pass: {
- regex: /(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{6,}/,
- error: $.idealforms.errors.pass
- },
- strongpass: {
- regex: /(?=^.{8,}$)((?=.*\d)|(?=.*\W+))(?![.\n])(?=.*[A-Z])(?=.*[a-z]).*$/,
- error: $.idealforms.errors.strongpass
- },
- email: {
- 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})?$/,
- error: $.idealforms.errors.email
- },
- phone: {
- //regex: /^((13[0-9])|(15[0-9])|(17[0-9])|(18[0-9]))\\d{8}$/,
- regex: /^(0|86|17951)?(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$/,
- error: $.idealforms.errors.phone
- },
- zip: {
- regex: /^\d{5}$|^\d{5}-\d{4}$/,
- error: $.idealforms.errors.zip
- },
- url: {
- regex: /^(?:(ftp|http|https):\/\/)?(?:[\w\-]+\.)+[a-z]{2,6}([\:\/?#].*)?$/i,
- error: $.idealforms.errors.url
- },
- min: {
- regex: function( input, value ) {
- var $inputinput = input.input,
- min = input.userOptions.data.min,
- isRadioCheck = $input.is('[type="checkbox"], [type="radio"]')
- if ( isRadioCheck ) {
- this.error = $.idealforms.errors.minOption.replace( '{0}', min )
- return $input.filter(':checked').length >= min
- }
- this.error = $.idealforms.errors.minChar.replace( '{0}', min )
- return value.length >= min
- }
- },
- max: {
- regex: function( input, value ) {
- var $inputinput = input.input,
- max = input.userOptions.data.max,
- isRadioCheck = $input.is('[type="checkbox"], [type="radio"]')
- if ( isRadioCheck ) {
- this.error = $.idealforms.errors.maxOption.replace( '{0}', max )
- return $input.filter(':checked').length <= max
- }
- this.error = $.idealforms.errors.maxChar.replace( '{0}', max )
- return value.length <= max
- }
- },
- range: {
- regex: function( input, value ) {
- var range = input.userOptions.data.range,
- val = +value
- this.error = $.idealforms.errors.range
- .replace( '{0}', range[0] )
- .replace( '{1}', range[1] )
- return val >= range[0] && val <= range[1]
- }
- },
- date: {
- regex: function( input, value ) {
- var
- userFormat =
- input.userOptions.data && input.userOptions.data.date
- ? input.userOptions.data.date
- : 'mm/dd/yyyy', // default format
- delimiter = /[^mdy]/.exec( userFormat )[0],
- theFormat = userFormat.split(delimiter),
- theDate = value.split(delimiter),
- isDate = function( date, format ) {
- var m, d, y
- for ( var i = 0, len = format.length; i < len; i++ ) {
- if ( /m/.test( format[i]) ) m = date[i]
- if ( /d/.test( format[i]) ) d = date[i]
- if ( /y/.test( format[i]) ) y = date[i]
- }
- return (
- m > 0 && m < 13 &&
- y && y.length === 4 &&
- d > 0 && d <= ( new Date( y, m, 0 ) ).getDate()
- )
- }
- this.error = $.idealforms.errors.date.replace( '{0}', userFormat )
- return isDate( theDate, theFormat )
- }
- },
- dob: {
- regex: function( input, value ) {
- var
- userFormat =
- input.userOptions.data && input.userOptions.data.dob
- ? input.userOptions.data.dob
- : 'mm/dd/yyyy', // default format
- // Simulate a date input
- dateInput = {
- input: input.input,
- userOptions: {
- data: { date: userFormat }
- }
- },
- // Use internal date filter to validate the date
- isDate = filters.date.regex( dateInput, value ),
- // DOB
- theYear = /\d{4}/.exec( value ),
- maxYear = new Date().getFullYear(), // Current year
- minYear = maxYear - 100
- this.error = $.idealforms.errors.dob
- return isDate && theYear >= minYear && theYear <= maxYear
- }
- },
- exclude: {
- regex: function( input, value ) {
- var $inputinput = input.input,
- exclude = input.userOptions.data.exclude,
- isOption = $input.is('[type="checkbox"], [type="radio"], select')
- this.error = isOption
- ? $.idealforms.errors.excludeOption.replace( '{0}', value )
- : this.error = $.idealforms.errors.exclude.replace( '{0}', value )
- return $.inArray( value, exclude ) === -1
- }
- },
- equalto: {
- regex: function( input, value ) {
- var $equals = $( input.userOptions.data.equalto ),
- $inputinput = input.input,
- name = $equals.attr('name') || $equals.attr('id'),
- isValid = $equals.parents('.ideal-field')
- .filter(function(){ return $(this).data('ideal-isvalid') === true })
- .length
- if ( !isValid ) { return false }
- this.error = $.idealforms.errors.equalto.replace( '{0}', name )
- return $input.val() === $equals.val()
- }
- },
- extension: {
- regex: function( input, value ) {
- var files = input.input[0].files || [{ name: value }],
- extensions = input.userOptions.data.extension,
- re = new RegExp( '\\.'+ extensions.join('|') +'$', 'i' ),
- valid = false
- for ( var i = 0, len = files.length; i < len; i++ ) {
- valid = re.test( files[i].name );
- }
- this.error = $.idealforms.errors.extension.replace( '{0}', extensions.join('", "') )
- return valid
- }
- },
- ajax: {
- regex: function( input, value, showOrHideError ) {
- var self = this
- var $inputinput = input.input
- var userOptions = input.userOptions
- var name = $input.attr('name')
- var $field = $input.parents('.ideal-field')
- var valid = false
- var customErrors = userOptions.errors && userOptions.errors.ajax
- self.error = {}
- self.error.success = customErrors && customErrors.success
- ? customErrors.success
- : $.idealforms.errors.ajaxSuccess.replace( '{0}', value )
- self.error.fail = customErrors && customErrors.error
- ? customErrors.error
- : $.idealforms.errors.ajaxError
- // Send input name as $_POST[name]
- var data = {}
- data[ name ] = $.trim( value )
- // Ajax options defined by the user
- var userAjaxOps = input.userOptions.data.ajax
- var ajaxOps = {
- type: 'post',
- dataType: 'json',
- data: data,
- success: function( resp, text, xhr ) {
- console.log(resp)
- showOrHideError( self.error.success, true )
- $input.data({
- 'ideal-ajax-resp': resp,
- 'ideal-ajax-error': self.error.success
- })
- $input.trigger('change') // to update counter
- $field.removeClass('ajax')
- // Run custom success callback
- if( userAjaxOps._success ) {
- userAjaxOps._success( resp, text, xhr )
- }
- },
- error: function( xhr, text, error ) {
- if ( text !== 'abort' ) {
- showOrHideError( self.error.fail, false )
- $input.data( 'ideal-ajax-error', self.error.fail )
- $field.removeClass('ajax')
- // Run custom error callback
- if ( userAjaxOps._error ) {
- userAjaxOps._error( xhr, text, error )
- }
- }
- }
- }
- $.extend( ajaxOps, userAjaxOps )
- // Init
- $input.removeData('ideal-ajax-error')
- $input.removeData('ideal-ajax-resp')
- $field.addClass('ajax')
- // Run request and save it to be able to abort it
- // so requests don't bubble
- $.idealforms.ajaxRequests[ name ] = $.ajax( ajaxOps )
- }
- }
- }
- return filters
- }
- $.idealforms.flags = {
- noerror: function (i) {
- i.parent().siblings('.ideal-error').hide()
- },
- noicons: function (i) {
- i.siblings('.ideal-icon-valid, .ideal-icon-invalid').hide()
- },
- novalidicon: function (i) {
- i.siblings('.ideal-icon-valid').hide()
- },
- noinvalidicon: function (i) {
- i.siblings('.ideal-icon-invalid').hide()
- },
- noclass: function (i) {
- i.parents('.ideal-field').removeClass('valid invalid')
- },
- novalidclass: function (i) {
- i.parents('.ideal-field').removeClass('valid')
- },
- noinvalidclass: function (i) {
- i.parents('.ideal-field').removeClass('invalid')
- }
- }
- /*
- * Ideal Forms plugin
- */
- var _defaults = {
- inputs: {},
- customFilters: {},
- customFlags: {},
- globalFlags: '',
- onSuccess: function(e) { alert('Thank you...') },
- onFail: function() { alert('Invalid!') },
- responsiveAt: 'auto',
- disableCustom: ''
- }
- // Constructor
- var IdealForms = function( element, options ) {
- var self = this
- self.$form = $( element )
- self.opts = $.extend( {}, _defaults, options )
- self.$tabs = self.$form.find('section')
- // Set localized filters
- $.extend( $.idealforms.filters, getFilters() )
- self._init()
- }
- // Plugin
- $.fn.idealforms = function( options ) {
- return this.each(function() {
- if ( !$.data( this, 'idealforms' ) ) {
- $.data( this, 'idealforms', new IdealForms( this, options ) )
- }
- })
- }
- // Get LESS variables
- var LessVars = {
- fieldWidth: Utils.getLessVar( 'ideal-field-width', 'width' )
- }
- /*
- * Private Methods
- */
- $.extend( IdealForms.prototype, {
- _init: function() {
- var self = this
- var o = self.opts
- var formElements = self._getFormElements()
- self.$form.css( 'visibility', 'visible' )
- .addClass('ideal-form')
- .attr( 'novalidate', 'novalidate' ) // disable HTML5 validation
- // Do markup
- formElements.inputs
- .add( formElements.headings )
- .add( formElements.separators )
- .each(function(){ self._doMarkup( $(this) ) })
- // Generate tabs
- if ( self.$tabs.length ) {
- var $tabContainer = $('<div class="ideal-wrap ideal-tabs ideal-full-width"/>')
- self.$form.prepend( $tabContainer )
- self.$tabs.idealTabs( $tabContainer )
- }
- // Always show datepicker below the input
- if ( jQuery.ui ) {
- $.datepicker._checkOffset = function( a,b,c ) { return b }
- }
- // Add inputs specified by data-ideal
- // to the list of user inputs
- self.$form.find('[data-ideal]').each(function() {
- var userInput = o.inputs[ this.name ]
- o.inputs[ this.name ] = userInput || { filters: $(this).data('ideal') }
- })
- // Responsive
- if ( o.responsiveAt ) {
- $(window).resize(function(){ self._responsive() })
- self._responsive()
- }
- // Form events
- self.$form.on({
- keydown: function( e ) {
- // Prevent submit when pressing enter
- // but exclude textareas
- if ( e.which === 13 && e.target.nodeName !== 'TEXTAREA' ) {
- e.preventDefault()
- }
- },
- submit: function( e ) {
- if ( !self.isValid() ) {
- e.preventDefault()
- o.onFail()
- self.focusFirstInvalid()
- } else {
- o.onSuccess( e )
- }
- }
- })
- self._adjust()
- self._attachEvents()
- self.fresh() // Start fresh
- },
- _getFormElements: function() {
- return {
- inputs: this.$form.find('input, select, textarea, :button'),
- labels: this.$form.find('div > label:first-child'),
- text: this.$form.find('input:not([type="checkbox"], [type="radio"], [type="submit"]), textarea'),
- select: this.$form.find('select'),
- radiocheck: this.$form.find('input[type="radio"], input[type="checkbox"]'),
- buttons: this.$form.find(':button'),
- file: this.$form.find('input[type="file"]'),
- headings: this.$form.find('h1, h2, h3, h4, h5, h6'),
- separators: this.$form.find('hr'),
- hidden: this.$form.find('input:hidden')
- }
- },
- _getUserInputs: function() {
- return this.$form.find('[name="'+ Utils.getKeys( this.opts.inputs ).join('"], [name="') +'"]')
- },
- _getTab: function( nameOrIdx ) {
- var self = this
- var isNumber = !isNaN( nameOrIdx )
- if ( isNumber ) {
- return self.$tabs.eq( nameOrIdx )
- }
- return self.$tabs.filter(function() {
- var re = new RegExp( nameOrIdx, 'i' )
- return re.test( $(this).data('ideal-tabs-content-name') )
- })
- },
- _getCurrentTabIdx: function() {
- return this.$tabs.index( this.$form.find('.ideal-tabs-content:visible') )
- },
- _updateTabsCounter: function() {
- var self = this
- self.$tabs.each(function( i ) {
- var invalid = self.getInvalidInTab( i ).length
- self.$tabs.updateCounter( i, invalid )
- })
- },
- _adjust: function() {
- var self = this
- var o = self.opts
- var formElements = self._getFormElements()
- var curTab = self._getCurrentTabIdx()
- // Autocomplete causes some problems...
- formElements.inputs.attr('autocomplete', 'off')
- // Show tabs to calculate dimensions
- if ( self.$tabs.length ) { self.$tabs.show() }
- // Adjust labels
- var labels = formElements.labels
- labels.removeAttr('style').width( Utils.getMaxWidth( labels ) )
- // Adjust headings and separators
- if ( self.$tabs.length ) {
- this.$tabs.each(function(){
- $( this ).find('.ideal-heading:first').addClass('first-child')
- })
- } else {
- self.$form.find('.ideal-heading:first').addClass('first-child')
- }
- self._setDatepicker()
- // Done calculating hide tabs
- if ( self.$tabs.length ) {
- self.$tabs.hide()
- self.switchTab( curTab )
- }
- },
- _setDatepicker: function() {
- var o = this.opts
- var $datepicker = this.$form.find('input.datepicker')
- if ( jQuery.ui && $datepicker.length ) {
- $datepicker.each(function() {
- var userInput = o.inputs[ this.name ]
- var data = userInput && userInput.data && userInput.data.date
- var format = data ? data.replace( 'yyyy', 'yy' ) : 'mm/dd/yy'
- $(this).datepicker({
- dateFormat: format,
- beforeShow: function( input ) {
- $( input ).addClass('open')
- },
- onChangeMonthYear: function() {
- // Hack to fix IE9 not resizing
- var $this = $(this)
- var w = $this.outerWidth() // cache first!
- setTimeout(function() {
- $this.datepicker('widget').css( 'width', w )
- }, 1)
- },
- onClose: function() { $(this).removeClass('open') }
- })
- })
- // Adjust width
- $datepicker.on('focus keyup', function() {
- var t = $(this), w = t.outerWidth()
- t.datepicker('widget').css( 'width', w )
- })
- $datepicker.parent().siblings('.ideal-error').addClass('hidden')
- }
- },
- _doMarkup: function( $element ) {
- var o = this.opts
- var elementType = Utils.getIdealType( $element )
- // Validation elements
- var $field = $('<span class="ideal-field"/>')
- var $error = $('<span class="ideal-error" />')
- var $valid = $('<i class="ideal-icon ideal-icon-valid" />')
- var $invalid = $('<i class="ideal-icon ideal-icon-invalid"/>')
- .click(function(){
- $(this).parent().find('input:first, textarea, select').focus()
- })
- // Basic markup
- $element.closest('div').addClass('ideal-wrap')
- .children('label:first-child').addClass('ideal-label')
- var idealElements = {
- _defaultInput: function() {
- $element.wrapAll( $field ).after( $valid, $invalid )
- .parent().after( $error )
- },
- text: function() { idealElements._defaultInput() },
- radiocheck: function() {
- // Check if input is already wrapped so we don't
- // wrap radios and checks more than once
- var isWrapped = $element.parents('.ideal-field').length
- if ( !isWrapped ) {
- $element.parent().nextAll().andSelf().wrapAll( $field.addClass('ideal-radiocheck') )
- $element.parents('.ideal-field').append( $valid, $invalid ).after( $error )
- }
- if ( !/radiocheck/.test( o.disableCustom ) ) {
- $element.idealRadioCheck()
- }
- },
- select: function() {
- idealElements._defaultInput()
- if ( !/select/.test( o.disableCustom ) ) {
- $element.idealSelect()
- }
- },
- file: function() {
- idealElements._defaultInput()
- if ( !/file/.test( o.disableCustom ) ) {
- $element.idealFile()
- }
- },
- button: function() {
- if ( !/button/.test( o.disableCustom ) ) {
- $element.addClass('ideal-button')
- }
- },
- hidden: function() {
- $element.closest('div').addClass('ideal-hidden')
- },
- heading: function() {
- $element.closest('div').addClass('ideal-full-width')
- $element.parent().children().wrapAll('<span class="ideal-heading"/>')
- },
- separator: function() {
- $element.closest('div').addClass('ideal-full-width')
- $element.wrapAll('<div class="ideal-separator"/>')
- }
- }
- // Generate markup for current element type
- idealElements[ elementType ] ? idealElements[ elementType ]() : $.noop()
- $error.add( $valid ).add( $invalid ).hide() // Start fresh
- },
- /** Validates an input and shows or hides error and icon
- * @memberOf Actions
- * @param {object} $input jQuery object
- * @param {string} e The JavaScript event
- */
- _validate: function( $input, e ) {
- var self = this
- var o = this.opts
- var userOptions = o.inputs[ $input.attr('name') ]
- var userFilters = userOptions.filters && userOptions.filters.split(/\s/)
- var name = $input.attr('name')
- var value = $input.val()
- var ajaxRequest = $.idealforms.ajaxRequests[ name ]
- var isRadioCheck = $input.is('[type="checkbox"], [type="radio"]')
- var inputData = {
- // If is radio or check validate all inputs related by name
- input: isRadioCheck ? self.$form.find('[name="' + name + '"]') : $input,
- userOptions: userOptions
- }
- // Validation elements
- var $field = $input.parents('.ideal-field')
- var $error = $field.siblings('.ideal-error')
- var $invalid = isRadioCheck
- ? $input.parent().siblings('.ideal-icon-invalid')
- : $input.siblings('.ideal-icon-invalid')
- var $valid = isRadioCheck
- ? $input.parent().siblings('.ideal-icon-valid')
- : $input.siblings('.ideal-icon-valid')
- function resetError() {
- $field.removeClass('valid invalid').removeData('ideal-isvalid')
- $error.add( $invalid ).add( $valid ).hide()
- }
- function showOrHideError( error, valid ) {
- resetError()
- valid ? $valid.show() : $invalid.show()
- $field.addClass( valid ? 'valid' : 'invalid' )
- $field.data( 'ideal-isvalid', valid )
- if ( !valid ) {
- $error.html( error ).toggle( $field.is('.ideal-field-focus') )
- }
- }
- // Prevent validation when typing but not introducing any new characters
- // This is mainly to prevent multiple AJAX requests
- var oldValue = $input.data('ideal-value') || 0
- $input.data( 'ideal-value', value )
- if ( e.type === 'keyup' && value === oldValue ) { return false }
- // Validate
- if ( userFilters ) {
- $.each( userFilters, function( i, filter ) {
- var theFilter = $.idealforms.filters[ filter ]
- var customError = userOptions.errors && userOptions.errors[ filter ]
- var error = ''
- // If field is empty and not required
- if ( !value && filter !== 'required' ) {
- resetError()
- return false
- }
- if ( theFilter ) {
- // Abort and reset ajax if there's a request pending
- if ( e.type === 'keyup' && ajaxRequest ) {
- ajaxRequest.abort()
- $field.removeClass('ajax')
- }
- // AJAX
- if ( filter === 'ajax' ) {
- showOrHideError( error, false ) // set invalid till response comes back
- $error.hide()
- if ( e.type === 'keyup' ) {
- theFilter.regex( inputData, value, showOrHideError ) // runs the ajax callback
- } else {
- var ajaxError = $input.data('ideal-ajax-error')
- if ( ajaxError ) {
- showOrHideError( ajaxError, $input.data('ideal-ajax-resp') || false )
- }
- }
- }
- // All other filters
- else {
- var valid = Utils.isRegex( theFilter.regex ) && theFilter.regex.test( value ) ||
- Utils.isFunction( theFilter.regex ) && theFilter.regex( inputData, value )
- error = customError || theFilter.error // assign error after calling regex()
- showOrHideError( error, valid )
- if ( !valid ) { return false }
- }
- }
- })
- }
- // Reset if there are no filters
- else {
- resetError()
- }
- // Flags
- var flags = (function(){
- var f = userOptions.flags && userOptions.flags.split(' ') || []
- if ( o.globalFlags ) {
- $.each( o.globalFlags.split(' '), function( i,v ) { f.push(v) })
- }
- return f
- }())
- if ( flags.length ) {
- $.each(flags, function( i,f ) {
- var theFlag = $.idealforms.flags[f]
- if ( theFlag ) { theFlag( $input, e.type ) }
- })
- }
- // Update counter
- if ( self.$tabs.length ) {
- self._updateTabsCounter( self._getCurrentTabIdx() )
- }
- },
- _attachEvents: function() {
- var self = this
- self._getUserInputs().on('keyup change focus blur', function(e) {
- var $this = $(this)
- var $field = $this.parents('.ideal-field')
- var isFile = $this.is('input[type=file]')
- // Trigger on change if type=file cuz custom file
- // disables focus on original file input (tabIndex = -1)
- if ( e.type === 'focus' || isFile && e.type === 'change' ) {
- $field.addClass('ideal-field-focus')
- }
- if ( e.type === 'blur' ) {
- $field.removeClass('ideal-field-focus')
- }
- self._validate( $this, e )
- })
- },
- _responsive: function() {
- var formElements = this._getFormElements()
- var maxWidth = LessVars.fieldWidth + formElements.labels.outerWidth()
- var $emptyLabel = formElements.labels.filter(function() {
- return $(this).html() === ' '
- })
- var $customSelect = this.$form.find('.ideal-select')
- this.opts.responsiveAt === 'auto'
- ? this.$form.toggleClass( 'stack', this.$form.width() < maxWidth )
- : this.$form.toggleClass( 'stack', $(window).width() < this.opts.responsiveAt )
- var isStack = this.$form.is('.stack')
- $emptyLabel.toggle( !isStack )
- $customSelect.trigger( isStack ? 'list' : 'menu' )
- // Hide datePicker
- var $datePicker = this.$form.find('input.hasDatepicker')
- if ( $datePicker.length ) { $datePicker.datepicker('hide') }
- }
- })
- /*
- * Public Methods
- */
- $.extend( IdealForms.prototype, {
- getInvalid: function() {
- return this.$form.find('.ideal-field').filter(function() {
- return $(this).data('ideal-isvalid') === false
- })
- },
- getInvalidInTab: function( nameOrIdx ) {
- return this._getTab( nameOrIdx ).find('.ideal-field').filter(function() {
- return $(this).data('ideal-isvalid') === false
- })
- },
- isValid: function() {
- return !this.getInvalid().length
- },
- isValidField: function( field ) {
- var $input = Utils.getByNameOrId( field )
- return $input.parents('.ideal-field').data('ideal-isvalid') === true
- },
- focusFirst: function() {
- if ( this.$tabs.length ) {
- this.$tabs.filter(':visible')
- .find('.ideal-field:first')
- .find('input:first, select, textarea').focus()
- } else {
- this.$form.find('.ideal-field:first')
- .find('input:first, select, textarea').focus()
- }
- return this
- },
- focusFirstInvalid: function() {
- var $first = this.getInvalid().first().find('input:first, select, textarea')
- var tabName = $first.parents('.ideal-tabs-content').data('ideal-tabs-content-name')
- if ( this.$tabs.length ) {
- this.switchTab( tabName )
- }
- $first.focus()
- return this
- },
- switchTab: function( nameOrIdx ) {
- this.$tabs.switchTab( nameOrIdx )
- return this
- },
- nextTab: function() {
- this.$tabs.nextTab()
- return this
- },
- prevTab: function() {
- this.$tabs.prevTab()
- return this
- },
- firstTab: function() {
- this.$tabs.firstTab()
- return this
- },
- lastTab: function() {
- this.$tabs.lastTab()
- return this
- },
- fresh: function() {
- this._getUserInputs().change().parents('.ideal-field')
- .removeClass('valid invalid')
- return this
- },
- freshFields: function( fields ) {
- fields = Utils.convertToArray( fields )
- $.each( fields, function( i ) {
- var $input = Utils.getByNameOrId( fields[ i ] )
- $input.change().parents('.ideal-field').removeClass('valid invalid')
- })
- return this
- },
- reload: function() {
- this._adjust()
- this._attachEvents()
- return this
- },
- reset: function() {
- var formElements = this._getFormElements()
- formElements.text.val('') // text inputs
- formElements.radiocheck.removeAttr('checked') // radio & check
- // Select and custom select
- formElements.select.find('option').first().prop( 'selected', true )
- this.$form.find('.ideal-select').trigger('reset')
- if ( this.$tabs.length ) { this.firstTab() }
- this.focusFirst().fresh()
- return this
- },
- resetFields: function( fields ) {
- fields = Utils.convertToArray( fields )
- var formElements = this._getFormElements()
- $.each( fields, function( i, v ) {
- var $input = Utils.getByNameOrId( v )
- var type = Utils.getIdealType( $input )
- if ( type === 'text' || type === 'file' ) {
- $input.val('')
- }
- if ( type === 'radiocheck' ) {
- $input.removeAttr('checked') // radio & check
- }
- if ( type === 'select' ) {
- $input.find('option').first().prop( 'selected', true )
- $input.next('.ideal-select').trigger('reset')
- }
- $input.change()
- })
- this.freshFields( fields )
- return this
- },
- toggleFields: function( fields ) {
- fields = Utils.convertToArray( fields )
- var self = this
- var $fields = Utils.getFieldsFromArray( fields )
- $fields.each(function() {
- var $this = $(this)
- var name = $this.attr('name') || $this.attr('id')
- var input = self.opts.inputs[ name ]
- var filters = input && input.filters
- var dataFilters = $this.data('ideal-filters') || ''
- $this.data( 'ideal-filters', filters )
- $this.closest('.ideal-wrap').toggle()
- self.setFieldOptions( name, { filters: dataFilters } )
- })
- return this
- },
- setOptions: function( options ) {
- $.extend( true, this.opts, options )
- this.reload().fresh()
- return this
- },
- setFieldOptions: function( name, options ) {
- $.extend( true, this.opts.inputs[ name ], options )
- this.reload().freshFields([ name ])
- return this
- },
- addFields: function( fields ) {
- fields = Utils.convertToArray( fields )
- var self = this
- // Save names of all inputs in Array
- // to use methods that take names ie. fresh()
- var allNames = []
- // Add an input to the DOM
- function add( ops ) {
- var name = ops.name
- var userOptions = {
- filters: ops.filters || '',
- data: ops.data || {},
- errors: ops.errors || {},
- flags: ops.flags || ''
- }
- var label = ops.label || ''
- var type = ops.type
- var list = ops.list || []
- var placeholder = ops.placeholder || ''
- var value = ops.value || ''
- var $field = $('<div>'+
- '<label>'+ label +':</label>'+
- Utils.makeInput( name, value, type, list, placeholder ) +
- '</div>')
- var $input = $field.find('input, select, textarea, :button')
- // Add inputs with filters to the list
- // of user inputs to validate
- if ( userOptions.filters ) { self.opts.inputs[ name ] = userOptions }
- self._doMarkup( $input )
- // Insert in DOM
- if ( ops.addAfter ) {
- $field.insertAfter(
- $( Utils.getByNameOrId( ops.addAfter ) ).parents('.ideal-wrap')
- )
- } else if ( ops.addBefore ) {
- $field.insertBefore(
- $(Utils.getByNameOrId( ops.addBefore ))
- .parents('.ideal-wrap')
- )
- } else if ( ops.appendToTab ) {
- $field.insertAfter(
- self._getTab( ops.appendToTab ).find('.ideal-wrap:last-child')
- )
- } else {
- $field.insertAfter( self.$form.find('.ideal-wrap').last() )
- }
- // Add current field name to list of names
- allNames.push( name )
- }
- // Run through each input
- $.each( fields, function( i, ops ) { add( ops ) })
- self.reload()
- self.freshFields( allNames )
- self._responsive()
- return this
- },
- removeFields: function( fields ) {
- fields = Utils.convertToArray( fields )
- var $fields = Utils.getFieldsFromArray( fields )
- $fields.parents('.ideal-wrap').remove()
- this.reload()
- return this
- }
- })
- }( jQuery, window, document ))
以上所述是本文的全部內容希望對大家有所幫助!