From 62054546c8dde8d7423a8ef514f8ab603c9c1f73 Mon Sep 17 00:00:00 2001 From: Matthew Miller Date: Sat, 2 Nov 2013 09:48:08 -0500 Subject: [PATCH 1/5] Updated README with latest documentation --- README.md | 320 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 319 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index af47071..b03b96a 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,320 @@ -jquery-autotab +jQuery Autotab ============== +Autotab is a jQuery plugin that provides auto tabbing and filtering on text fields in a form. Once the maximum number of characters has been reached within a text field, the focus is automatically set to a defined element. Likewise, clearing out the text field's content by pressing backspace eventually places the focus on a previous element. + + +## Why jQuery Autotab? +* Auto tabbing behaves logically, in both tabbing forward and tabbing backwards. +* Allow your users to easily modify your text in a tab that would otherwise auto tab away. +* Reduce the amount of bad data submitted in a form by filtering text fields. +* Populate multiple text fields by pasting into one. +* It is small, fast, easy to load and built on the powerful jQuery library. + +_Pasting support has basic functionality and has a lot of room for improvement, so use at your own risk._ + + +## Table of Contents +* [Requirements](#requirements) +* [Installation](#installation) +* [Setup and Usage](#setup-and-usage) + * [Auto Tabbing](#auto-tabbing) + * [Examples](#examples) + * [Filtering](#filtering) + * [Examples](#examples-1) +* [Options](#options) +* [Filter Formats](#filter-formats) +* [Feedback](#feedback) +* [Copyright and License](#copyright-and-license) + + +## Requirements + +Autotab works in both jQuery 1.7+ and 2.x. If your site supports Internet Explorer 6, 7, and/or 8, use jQuery 1.7+ since 2.x drops support for these browsers. + + +## Installation + +Add a reference to jquery.autotab.js. + +```html + +``` + + +## Setup and Usage + +Autotab can be setup in several different ways within jQuery's `$(document).ready()` event. The two components that make up Autotab, auto tabbing and filtering, can be managed independently from one another, providing numerous ways of achieving the same result, depending on how indepth you want to setup your form. + + +### Auto Tabbing + +__Note:__ If the selector matches multiple elements, the target and previous fields will be overwritten if previously set. + + + + + + + + + + + + + + +
.autotab()Accepts no arguments and applies auto tabbing rules only.
.autotab(string)string: Defines the filter format that will be used on all matched elements.
.autotab(object)object: Applies the specified options to all matched elements.
+ + +#### Examples + +Establishes auto tabbing rules only and forces each field to have a `maxlength` of 1. + +```js +$('.answer').autotab({ maxlength: 1 }); +``` +```html +
+ + - + + - + + - +
+``` + +Automatically establishes auto tabbing order and number filtering. + +```js +$('.number').autotab('number'); +``` +```html +
+ + - + - + +
+``` + +Manually defines auto tabbing order and alphanumeric filtering. + +```js +$('#alphanumeric1').autotab({ format: 'alphanumeric', target: '#alphanumeric2' }); +$('#alphanumeric2').autotab({ format: 'alphanumeric', target: '#alphanumeric3', previous: '#alphanumeric1' }); +$('#alphanumeric3').autotab({ format: 'alphanumeric', target: '#alphanumeric4', previous: '#alphanumeric2' }); +$('#alphanumeric4').autotab({ format: 'alphanumeric', target: '#alphanumeric5', previous: '#alphanumeric3' }); +$('#alphanumeric5').autotab({ format: 'alphanumeric', previous: '#alphanumeric4' }); +``` +```html +
+ + - + - + - + - + +
+``` + + +### Filtering + +__Note:__ If passing an object, the target and previous fields are ignored, if included, to preserve previously established auto tab rules. Use `.autotab(object)` to modify the target and previous fields. + + + + + + + + + + +
.autotab('filter', string)string: Applies the specified filter format to all matched elements.
.autotab('filter', object) + object: Applies the specified filter options to all matched elements. The target and previous fields are ignored. +
+ + +#### Examples + +Manually defines text filtering. + +```js +$('#salt').autotab('filter', 'text'); +``` +```html +
+ + +
+``` + +Manually defines alphanumeric filtering and converts all alpha characters to uppercase format. + +```js +$('.alphanumeric').autotab('filter', { format: 'alphanumeric', uppercase: true }); +``` +```html +
+ + - + - + - + - + +
+``` + +Other random JavaScript examples + +```js +$('input[type=text]').autotab(); +$('#username').autotab({ format: 'alphanumeric', target: '#password' }); +$('#password').autotab({ previous: '#username', target: '#confirm' }); +$('#product-key').autotab('filter', { format: 'alphanumeric', uppercase: true }); +$('#ip-address').autotab('filter', { format: 'custom', pattern: '[^0-9\.]' }); +$('#function').autotab('filter', function (text) { alert(text); }); +$('#number1, #number2, #number3').autotab('filter', 'number'); +``` + + +## Options + +```js +var options = { + format: string|function, + pattern: string, + uppercase: boolean, + lowercase: boolean, + nospace: boolean, + maxlength: integer, + target: string|element, + previous: string|element +}; +``` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
format + string: Speficies which format rule to use on a text box's value. +
+ function: If a single regular expression is insufficient, a function can be used for custom formatting. +
+ Note: Go to Filter Formats to see each available format option. +
pattern + string: Used only when the custom format is specified, and it must be a string. +
uppercase + boolean: Forces all alpha characters to uppercase format. +
lowercase + boolean: Forces all alpha characters to lowercase format. +
nospace + boolean: Determines if spaces are allowed or not. +
+ Note: Space and underscore are not alpha characters and are not included in the alpha and alphanumeric format options. Use the custom format to allow these characters. +
maxlength + integer: The maximum number of characters allowed in a text box. Maxlength can be specified with the HTML attribute maxlength. +
+ Note: The maxlength attribute value can be overwritten if the maxlength field is passed to autotab(). +
target + When auto tabbing occurs, target is the element that will gain focus. +
+ string: A selector identifying the next element. +
+ element: The JavaScript or jQuery element. +
previous + When backspacing or reverse tabbing, previous is the element that will gain focus. +
+ string: A selector identifying the next element. +
+ element: The JavaScript or jQuery element. +
+ + +## Filter Formats + +Autotab has several filter formats available. If none of the formats meet your needs, Autotab also supports a `custom` option where you can pass either a regular expression or a function. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
format: 'all'Allows any and all characters.
format: 'text'Allows all characters, including special characters, except numbers.
format: 'alpha'Allows only letters.
format: 'number|numeric'Allows only numbers.
format: 'alphanumeric'Allows only letters and numbers.
+ format: 'custom', +
+ pattern: string +
+ Allows a developer to provide a custom regular expression: new RegExp(pattern, 'g'); +
+ Note: Requires pattern: string, ie: "[^0-9\.]" +
format: functionAllows a developer to provide a their own function in case a regular expression is insufficient.
+ + +## Feedback + +Please provide feature requests and bug reports here on [GitHub](../../issues). + +You can also reach out to me on twitter: [@mathachew](http://www.twitter.com/mathachew) + +## Copyright and license + +© 2013 Matthew Miller + +Licensed under the MIT licensing: http://www.opensource.org/licenses/mit-license.php From 8b9c21e2c3a84d4f15ea153f345d371e6518dc68 Mon Sep 17 00:00:00 2001 From: Matthew Miller Date: Sat, 2 Nov 2013 09:49:01 -0500 Subject: [PATCH 2/5] 1.2 --- js/jquery.autotab.js | 407 +++++++++++++++++++++++++++++++++++++++ js/jquery.autotab.min.js | 20 ++ 2 files changed, 427 insertions(+) create mode 100644 js/jquery.autotab.js create mode 100644 js/jquery.autotab.min.js diff --git a/js/jquery.autotab.js b/js/jquery.autotab.js new file mode 100644 index 0000000..d50420d --- /dev/null +++ b/js/jquery.autotab.js @@ -0,0 +1,407 @@ +/** + * Autotab - jQuery plugin 1.2 + * http://www.mathachew.com/sandbox/jquery-autotab + * + * Copyright (c) 2013 Matthew Miller + * + * Licensed under the MIT licensing: + * http://www.opensource.org/licenses/mit-license.php + */ + +(function ($) { + var settings = { + tabPause: 800, + focusChange: null + }; + + var getDefaults = function () { + var platform = navigator.platform; + + return { + loaded: false, + format: 'all', + pattern: null, + uppercase: false, + lowercase: false, + nospace: false, + maxlength: 2147483647, + target: null, + previous: null, + iOS: (platform === 'iPad' || platform === 'iPhone' || platform === 'iPod') + }; + }; + + $.autotab = function () { }; + + $.autotab.next = function () { + var e = $(document.activeElement); + + if (e.length) { + e.trigger('autotab-next'); + } + }; + + $.autotab.previous = function () { + var e = $(document.activeElement); + + if (e.length) { + e.trigger('autotab-previous'); + } + }; + + $.fn.autotab = function (method, options) { + // Apply filter options + if (method == 'filter') { + if (typeof options == 'string' || typeof options == 'function') { + options = { format: options }; + } + + for (var i = 0, length = this.length; i < length; i++) { + var defaults = $(this[i]).data('autotab-defaults'); + + // Retain the established target/previous values as this area is for filtering only + options.target = defaults.target; + options.previous = defaults.previous; + + $.extend(defaults, options); + $(this[i]).data('autotab-defaults', defaults); + } + } + else { + if (method == null) { + options = {}; + } + else if (typeof method == 'string') { + options = { format: method }; + } + else if (typeof method == 'object') { + options = method; + } + + // Bind key events to element(s) passed + if (this.length > 1) { + for (var i = 0, length = this.length; i < length; i++) { + var n = i + 1, + p = i - 1, + newOptions = options; + + if (i > 0 && n < length) { + newOptions.target = this[n]; + newOptions.previous = this[p]; + } + else if (i > 0) { + newOptions.target = null; + newOptions.previous = this[p]; + } + else { + newOptions.target = this[n]; + newOptions.previous = null; + } + + autotabBind(this[i], newOptions); + } + } + else { + autotabBind(this, options); + } + } + + return this; + }; + + var filterValue = function (value, defaults) { + if (typeof defaults.format == 'function') { + return defaults.format(value); + } + + switch (defaults.format) { + case 'text': + var pattern = new RegExp('[0-9]+', 'g'); + value = value.replace(pattern, ''); + break; + + case 'alpha': + var pattern = new RegExp('[^a-zA-Z]+', 'g'); + value = value.replace(pattern, ''); + break; + + case 'number': + case 'numeric': + var pattern = new RegExp('[^0-9]+', 'g'); + value = value.replace(pattern, ''); + break; + + case 'alphanumeric': + var pattern = new RegExp('[^0-9a-zA-Z]+', 'g'); + value = value.replace(pattern, ''); + break; + + case 'custom': + var pattern = new RegExp(defaults.pattern, 'g'); + value = value.replace(pattern, ''); + break; + + case 'all': + default: + break; + } + + if (defaults.nospace) { + var pattern = new RegExp('[ ]+', 'g'); + value = value.replace(pattern, ''); + } + + if (defaults.uppercase) { + value = value.toUpperCase(); + } + + if (defaults.lowercase) { + value = value.toLowerCase(); + } + + return value; + }; + + var autotabBind = function (element, options) { + var defaults = $(element).data('autotab-defaults') || getDefaults(); + $.extend(defaults, options); + + // Sets targets to element based on the name or ID passed if they are not currently objects + if (typeof defaults.target == 'string' || !(defaults.target instanceof jQuery)) { + defaults.target = $(defaults.target); + } + + if (typeof defaults.previous == 'string' || !(defaults.previous instanceof jQuery)) { + defaults.previous = $(defaults.previous); + } + + var oldMaxlength = element.maxLength; + + // defaults.maxlength has not changed and maxlength was specified + if (defaults.maxlength == 2147483647 && oldMaxlength != 2147483647) { + defaults.maxlength = oldMaxlength; + } + // defaults.maxlength overrides maxlength + else if (defaults.maxlength > 0) { + element.maxLength = defaults.maxlength; + } + // defaults.maxlength and maxlength have not been specified + // A target cannot be used since there is no defined maxlength + else { + defaults.target = null; + } + + $(element).data('autotab-defaults', defaults); + + if (!$(element).data('autotab-defaults').loaded) { + defaults.loaded = true; + $(element).data('autotab-defaults', defaults); + } + else { + return $(element); + } + + return $(element).on('autotab-next', function (event, defaults) { + if (defaults.target.length) { + // Using focus on iOS devices is a pain, so use the browser's next/previous buttons to proceed + if (!defaults.iOS) { + defaults.target.focus().select(); + settings.focusChange = new Date(); + } + } + }).on('autotab-previous', function (event, defaults) { + if (defaults.previous.length) { + defaults.previous.focus(); + + // When setting value = value, Firefox will not place the cursor at the end of a textbox + // when the cursor was last at any point before the final character within the same textbox + if (this.setSelectionRange) { + var length = defaults.previous.val().length; + defaults.previous[0].setSelectionRange(length, length); + } + else { + defaults.previous.val(defaults.previous.val()); + } + + settings.focusChange = null; + } + }).on('keydown', function (e) { + var defaults = $(this).data('autotab-defaults'), + keyCode = e.which || e.charCode; + + // Go to the previous element when backspace + // is pressed in an empty input field + if (keyCode == 8 && this.value.length == 0 && defaults.previous.length) { + $(this).trigger('autotab-previous', defaults); + } + else if (keyCode == 9 && settings.focusChange != null) { + // Tab backwards + if (e.shiftKey) { + settings.focusChange = null; + return; + } + + if ((new Date().getTime() - settings.focusChange.getTime()) < settings.tabPause) { + settings.focusChange = null; + return false; + } + } + else { + settings.focusChange = null; + } + }).on('keypress', function (e) { + var defaults = $(this).data('autotab-defaults'), + keyCode = e.which || e.keyCode, + keyChar = filterValue(String.fromCharCode(keyCode), defaults), + hasValue = document.selection && document.selection.createRange ? true : (e.charCode > 0); + + if (e.ctrlKey || e.altKey) { + return true; + } + + if (hasValue && (keyChar == null || keyChar == '')) { + // Returns true whenever a paste is occurring + // Speficially added for Firefox + return e.ctrlKey; + } + + // Many, many thanks to Tim Down for this solution: http://stackoverflow.com/a/3923320/94656 + if (hasValue && (this.value.length <= this.maxLength)) { + var start, end; + + if (typeof this.selectionStart == 'number' && typeof this.selectionEnd == 'number') { + // Non-IE browsers and IE 9 + start = this.selectionStart; + end = this.selectionEnd; + + // Text is fully selected, so it needs to be replaced + if (start == 0 && end == this.value.length) { + this.value = keyChar; + } + else { + if (this.value.length == this.maxLength) { + $(this).trigger('autotab-next', defaults); + return false; + } + + this.value = this.value.slice(0, start) + keyChar + this.value.slice(end); + } + + // Move the caret + this.selectionStart = this.selectionEnd = start + 1; + } + else if (document.selection && document.selection.createRange) { + // For IE up to version 8 + var selectionRange = document.selection.createRange(), + textInputRange = this.createTextRange(), + precedingRange = this.createTextRange(), + bookmark = selectionRange.getBookmark(); + textInputRange.moveToBookmark(bookmark); + precedingRange.setEndPoint("EndToStart", textInputRange); + start = precedingRange.text.length; + end = start + selectionRange.text.length; + + // Text is fully selected, so it needs to be replaced + if (start == 0 && end == this.value.length) { + this.value = keyChar; + } + else { + if (this.value.length == this.maxLength) { + $(this).trigger('autotab-next', defaults); + return false; + } + + this.value = this.value.slice(0, start) + keyChar + this.value.slice(end); + } + + start++; + + // Move the caret + textInputRange = this.createTextRange(); + textInputRange.collapse(true); + textInputRange.move("character", start - (this.value.slice(0, start).split("\r\n").length - 1)); + textInputRange.select(); + } + } + + // Firefox doesn't behave properly when trying to backspace or move through + // a text box with the arrow keys + var keys = '8,9,16,17,18,19,20,27,33,34,35,36,37,38,39,40,45,46,144,145'; + + if (keys.indexOf(keyCode) == -1 && keyCode != undefined) { + this.value = filterValue(this.value, defaults); + + if (this.value.length == defaults.maxlength) { + $(this).trigger('autotab-next', defaults); + } + return false; + } + }).on('paste', function (e) { + var handlePaste = function (e, originalValue, previousValue) { + if (!e) { + return; + } + + var defaults = $(e).data('autotab-defaults'), + maxLength = e.maxLength; + + $(e).data('autotab-defaults', defaults); + + e.maxLength = 2147483647; + + setTimeout(function () { + if (originalValue == null) { + originalValue = e.value; + } + + var filteredValue; + + // Truncate the pasted text up to the previous element's filtered value + if (previousValue != null) { + filteredValue = filterValue(originalValue.substr(originalValue.indexOf(previousValue) + previousValue.length), defaults).substr(0, maxLength); + } + else { + filteredValue = filterValue(originalValue, defaults).substr(0, maxLength); + } + + e.maxLength = maxLength; + + if (!filteredValue) { + e.value = ''; + return; + } + + e.value = filteredValue; + + if (filteredValue.length == maxLength) { + $(e).trigger('autotab-next', defaults); + + if (!defaults.iOS) { + handlePaste(defaults.target[0], originalValue, filteredValue); + } + } + }, 1); + }; + + handlePaste(this, null, null); + }); + }; + + // Backwards compatibility + $.fn.autotab_magic = function (focus) { + $(this).autotab(); + }; + $.fn.autotab_filter = function (options) { + var defaults = {}; + + if (typeof options == 'string' || typeof options == 'function') { + defaults.format = options; + } + else { + $.extend(defaults, options); + } + + $(this).autotab('filter', defaults); + }; + +})(jQuery); \ No newline at end of file diff --git a/js/jquery.autotab.min.js b/js/jquery.autotab.min.js new file mode 100644 index 0000000..8bb434c --- /dev/null +++ b/js/jquery.autotab.min.js @@ -0,0 +1,20 @@ +/** + * Autotab - jQuery plugin 1.2 + * http://www.mathachew.com/sandbox/jquery-autotab + * + * Copyright (c) 2013 Matthew Miller + * + * Licensed under the MIT licensing: + * http://www.opensource.org/licenses/mit-license.php + */ + +(function(c){var f=null,p=function(){var b=navigator.platform;return{loaded:!1,format:"all",pattern:null,uppercase:!1,lowercase:!1,nospace:!1,maxlength:2147483647,target:null,previous:null,iOS:"iPad"===b||"iPhone"===b||"iPod"===b}};c.autotab=function(){};c.autotab.next=function(){var b=c(document.activeElement);b.length&&b.trigger("autotab-next")};c.autotab.previous=function(){var b=c(document.activeElement);b.length&&b.trigger("autotab-previous")};c.fn.autotab=function(b,d){if("filter"==b){if("string"== +typeof d||"function"==typeof d)d={format:d};for(var a=0,f=this.length;a(new Date).getTime()-f.getTime())return f=null,!1}else f=null}).on("keypress",function(a){var b=c(this).data("autotab-defaults"),g=a.which||a.keyCode,d=m(String.fromCharCode(g),b),l=document.selection&&document.selection.createRange?!0:0 Date: Sat, 2 Nov 2013 09:49:19 -0500 Subject: [PATCH 3/5] Added demo --- demo.html | 217 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 217 insertions(+) create mode 100644 demo.html diff --git a/demo.html b/demo.html new file mode 100644 index 0000000..a0272e7 --- /dev/null +++ b/demo.html @@ -0,0 +1,217 @@ + + + + + jQuery Autotab Demo + + + + + + + + + +
+

jQuery Autotab Demo

+

Autotab's documentation can be found in ReadMe.md on GitHub.

+ +
+ +
+
+ + + + - + + - + +
+
+                    // Example 1
+                    $('input[type=text]').autotab();
+                    $('.number').autotab('filter', 'number');
+
+                    // Example 2
+                    $('.number').autotab();
+                    $('.number').autotab('filter', 'number');
+
+                    // Example 3
+                    $('#number1').autotab({ format: 'number', target: '#number2' });
+                    $('#number2').autotab({ format: 'number', target: '#number3', previous: '#number1' });
+                    $('#number3').autotab({ format: 'number', target: '#ssn1', previous: '#number2' });
+
+                    // Example 4
+                    // Note: Applies number formatting to all elements, but the last element lacks a target
+                    $('.number').autotab({ format: 'number' });
+                    $('#number3').autotab({ target: '#ssn1' });
+                
+
+ +
+

Examples from this point on will assume $('input[type=text]').autotab() is called.

+
+ +
+ +
+
+ + + + - + + - + +
+
+                    $('.ssn').autotab('filter', 'number');
+                
+
+ +
+ +
+
+ + + + - + + - + +
+
+                    $('.text').autotab('filter', 'text');
+                
+
+ +
+ +
+
+ + + + - + + - + + - + + - + +
+
+                    $('.alpha').autotab('filter', 'alpha');
+                
+
+ +
+ +
+
+ + + + - + + - + + - + + - + +
+
+                    $('.alphanumeric').autotab('filter', 'alphanumeric');
+                
+
+ +
+ +
+
+ + + + - + + - + + - + + - + +
+
+                    // Note: This call is not necessary as 'all' is the default format
+                    $('.all').autotab('filter', 'all');
+                
+
+ +
+ +
+
+ + +
+
+                    $('#regex').autotab({ format: 'custom', pattern: '[^0-9\.]' });
+                
+
+
+ + \ No newline at end of file From 316ed32a47c1324c1d2a66f3417d8d9024941baa Mon Sep 17 00:00:00 2001 From: Matthew Miller Date: Sat, 2 Nov 2013 11:48:07 -0500 Subject: [PATCH 4/5] Added check for auto settings when calling autotab-next and autotab-previous events --- js/jquery.autotab.js | 8 ++++++++ js/jquery.autotab.min.js | 12 ++++++------ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/js/jquery.autotab.js b/js/jquery.autotab.js index d50420d..72f67db 100644 --- a/js/jquery.autotab.js +++ b/js/jquery.autotab.js @@ -202,6 +202,10 @@ } return $(element).on('autotab-next', function (event, defaults) { + if (!defaults) { + defaults = $(this).data('autotab-defaults') + } + if (defaults.target.length) { // Using focus on iOS devices is a pain, so use the browser's next/previous buttons to proceed if (!defaults.iOS) { @@ -210,6 +214,10 @@ } } }).on('autotab-previous', function (event, defaults) { + if (!defaults) { + defaults = $(this).data('autotab-defaults') + } + if (defaults.previous.length) { defaults.previous.focus(); diff --git a/js/jquery.autotab.min.js b/js/jquery.autotab.min.js index 8bb434c..ba3523e 100644 --- a/js/jquery.autotab.min.js +++ b/js/jquery.autotab.min.js @@ -12,9 +12,9 @@ typeof d||"function"==typeof d)d={format:d};for(var a=0,f=this.length;a(new Date).getTime()-f.getTime())return f=null,!1}else f=null}).on("keypress",function(a){var b=c(this).data("autotab-defaults"),g=a.which||a.keyCode,d=m(String.fromCharCode(g),b),l=document.selection&&document.selection.createRange?!0:0(new Date).getTime()-f.getTime())return f=null,!1}else f=null}).on("keypress",function(a){var b=c(this).data("autotab-defaults"),g=a.which||a.keyCode,d=m(String.fromCharCode(g),b),l=document.selection&&document.selection.createRange?!0:0 Date: Sat, 2 Nov 2013 11:49:49 -0500 Subject: [PATCH 5/5] Added complete changelog history for Autotab --- CHANGELOG.md | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..e97f5a0 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,39 @@ +## 1.2 (2013-11-02) + +Features: + +* updated `autotab()` to be the primary means of setting up auto tabbing and filtering (see documentation for more details) +* greatly improved filtering by preventing illegal characters from appearing by changing `keyup` to `keypress` +* improved functionality when making multiple calls for auto tab and/or filtering that prevents multiple triggers of the auto tabbing and filtering from occuring +* added `$.autotab.next()` and `$.autotab.previous()`, which can be used to manually trigger the `autotab-next` and `autotab-previous` events respectively +* added support to prevent double tabbing using a timer, ie. pressing tab immediately after auto tabbing has occurred +* added basic support for pasting +* improved auto tabbing by moving to the next element when the current one reaches its maxlength while holding down a key +* auto tabbing forward will select the target element's value (#1) +* while auto tabbing does not work on iOS devices, the behavior of the script has improved, including input validation +* fully backwards compatible with Autotab 1.1b + +Bug fixes: + +* if rapidly typing illegal characters, the next element received focused despite the previous element not reaching it's maxlength +* addressed sevearl behavioral issues in some of the latest versions of Firefox + + +## 1.1b (2008-09-10) + +Features: + +* refactored auto tabbing and filtering functionality so that one or the other could be applied +* added `autotab_magic()`, simplifying the setup of `target` and `previous` elements +* added `custom` format option, which requires a regular expression to be passed in the `pattern` field +* updated `format` to support functions + +Bug fixes: + +* in Safari, pressing backspace in an empty text box would not focus on the `previous` element +* in IE, pressing the left arrow key would force the cursor to the end of the field's content + + +## 1.0 (2008-05-22) + +Initial Release