| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161 |
- /*global document:true, jQuery:true */
- // Set up namespace, if needed
- var JoelPurra = JoelPurra || {};
- (function(document, $, namespace, pluginName) {
- "use strict";
- var eventNamespace = "." + pluginName,
- // TODO: get code for :focusable, :tabbable from jQuery UI?
- focusable = ":input, a[href]",
- // Keep a reference to the last focused element, use as a last resort.
- lastFocusedElement = null,
- // Private methods
- internal = {
- escapeSelectorName: function(str) {
- // Based on http://api.jquery.com/category/selectors/
- // Still untested
- return str.replace(/(!"#$%&'\(\)\*\+,\.\/:;<=>\?@\[\]^`\{\|\}~)/g, "\\\\$1");
- },
- findNextFocusable: function($from, offset) {
- var $focusable = $(focusable)
- .not(":disabled")
- .not(":hidden")
- .not("a[href]:empty");
- if ($from[0].tagName === "INPUT" && $from[0].type === "radio" && $from[0].name !== "") {
- var name = internal.escapeSelectorName($from[0].name);
- $focusable = $focusable
- .not("input[type=radio][name=" + name + "]")
- .add($from);
- }
- var currentIndex = $focusable.index($from);
- var nextIndex = (currentIndex + offset) % $focusable.length;
- if (nextIndex <= -1) {
- nextIndex = $focusable.length + nextIndex;
- }
- var $next = $focusable.eq(nextIndex);
- return $next;
- },
- focusInElement: function(event) {
- lastFocusedElement = event.target;
- },
- tryGetElementAsNonEmptyJQueryObject: function(selector) {
- try {
- var $element = $(selector);
- if ( !! $element && $element.size() !== 0) {
- return $element;
- }
- } catch (e) {
- // Could not use element. Do nothing.
- }
- return null;
- },
- // Fix for EmulateTab Issue #2
- // https://github.com/joelpurra/emulatetab/issues/2
- // Combined function to get the focused element, trying as long as possible.
- // Extra work done trying to avoid problems with security features around
- // <input type="file" /> in Firefox (tested using 10.0.1).
- // http://stackoverflow.com/questions/9301310/focus-returns-no-element-for-input-type-file-in-firefox
- // Problem: http://jsfiddle.net/joelpurra/bzsv7/
- // Fixed: http://jsfiddle.net/joelpurra/bzsv7/2/
- getFocusedElement: function() {
- // 1. Try the well-known, recommended method first.
- //
- // 2. Fall back to a fast method that might fail.
- // Known to fail for Firefox (tested using 10.0.1) with
- // Permission denied to access property "nodeType".
- //
- // 3. As a last resort, use the last known focused element.
- // Has not been tested enough to be sure it works as expected
- // in all browsers and scenarios.
- //
- // 4. Empty fallback
- var $focused = internal.tryGetElementAsNonEmptyJQueryObject(":focus") || internal.tryGetElementAsNonEmptyJQueryObject(document.activeElement) || internal.tryGetElementAsNonEmptyJQueryObject(lastFocusedElement) || $();
- return $focused;
- },
- emulateTabbing: function($from, offset) {
- var $next = internal.findNextFocusable($from, offset);
- $next.focus();
- },
- initializeAtLoad: function() {
- // Start listener that keep track of the last focused element.
- $(document)
- .on("focusin" + eventNamespace, internal.focusInElement);
- }
- },
- plugin = {
- tab: function($from, offset) {
- // Tab from focused element with offset, .tab(-1)
- if ($.isNumeric($from)) {
- offset = $from;
- $from = undefined;
- }
- $from = $from || plugin.getFocused();
- offset = offset || +1;
- internal.emulateTabbing($from, offset);
- },
- forwardTab: function($from) {
- return plugin.tab($from, +1);
- },
- reverseTab: function($from) {
- return plugin.tab($from, -1);
- },
- getFocused: function() {
- return internal.getFocusedElement();
- }
- },
- installJQueryExtensions = function() {
- $.extend({
- emulateTab: function($from, offset) {
- return plugin.tab($from, offset);
- }
- });
- $.fn.extend({
- emulateTab: function(offset) {
- return plugin.tab(this, offset);
- }
- });
- },
- init = function() {
- namespace[pluginName] = plugin;
- installJQueryExtensions();
- // EmulateTab initializes listener(s) when jQuery is ready
- $(internal.initializeAtLoad);
- };
- init();
- }(document, jQuery, JoelPurra, "EmulateTab"));
|