kanbanworkflowstimelinescrumrubyroadmapproject-planningproject-managementopenprojectangularissue-trackerifcgantt-chartganttbug-trackerboardsbcf
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
203 lines
6.2 KiB
203 lines
6.2 KiB
/*!
|
|
Copyright (c) 2011, 2012 Julien Wajsberg <felash@gmail.com>
|
|
All rights reserved.
|
|
|
|
Official repository: https://github.com/julienw/jquery-trap-input
|
|
License is there: https://github.com/julienw/jquery-trap-input/blob/master/LICENSE
|
|
This is version 1.2.0.
|
|
*/
|
|
|
|
(function( $, undefined ){
|
|
|
|
/*
|
|
(this comment is after the first line of code so that uglifyjs removes it)
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted without condition.
|
|
|
|
Although that's not an obligation, I would appreciate that you provide a
|
|
link to the official repository.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
|
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES ARE DISCLAIMED.
|
|
*/
|
|
|
|
/*jshint boss: true, bitwise: true, curly: true, expr: true, newcap: true, noarg: true, nonew: true, latedef: true, regexdash: true */
|
|
|
|
var DATA_ISTRAPPING_KEY = "trap.isTrapping";
|
|
|
|
function onkeypress(e) {
|
|
if (e.keyCode === 9) {
|
|
var goReverse = !!(e.shiftKey);
|
|
if (processTab(this, e.target, goReverse)) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
}
|
|
}
|
|
}
|
|
|
|
// will return true if we could process the tab event
|
|
// otherwise, return false
|
|
function processTab(container, elt, goReverse) {
|
|
var $focussable = getFocusableElementsInContainer(container),
|
|
curElt = elt,
|
|
index, nextIndex, prevIndex, lastIndex;
|
|
|
|
do {
|
|
index = $focussable.index(curElt);
|
|
nextIndex = index + 1;
|
|
prevIndex = index - 1;
|
|
lastIndex = $focussable.length - 1;
|
|
|
|
switch(index) {
|
|
case -1:
|
|
return false; // that's strange, let the browser do its job
|
|
case 0:
|
|
prevIndex = lastIndex;
|
|
break;
|
|
case lastIndex:
|
|
nextIndex = 0;
|
|
break;
|
|
}
|
|
|
|
if (goReverse) {
|
|
nextIndex = prevIndex;
|
|
}
|
|
|
|
curElt = $focussable.get(nextIndex);
|
|
if (!curElt || curElt === elt) { return true; }
|
|
|
|
try {
|
|
curElt.focus();
|
|
} catch(e) { // IE sometimes throws when an element is not visible
|
|
return true;
|
|
}
|
|
|
|
} while ($focussable.length > 1 && elt === elt.ownerDocument.activeElement);
|
|
|
|
return true;
|
|
}
|
|
|
|
function filterKeepSpeciallyFocusable() {
|
|
return this.tabIndex > 0;
|
|
}
|
|
|
|
function filterKeepNormalElements() {
|
|
return !this.tabIndex; // true if no tabIndex or tabIndex == 0
|
|
}
|
|
|
|
function sortFocusable(a, b) {
|
|
return (a.t - b.t) || (a.i - b.i);
|
|
}
|
|
|
|
function getFocusableElementsInContainer(container) {
|
|
var $container = $(container);
|
|
var result = [],
|
|
cnt = 0;
|
|
|
|
fixIndexSelector.enable && fixIndexSelector.enable();
|
|
|
|
// leaving away command and details for now
|
|
$container.find("a[href], link[href], [draggable=true], [contenteditable=true], :input:enabled, [tabindex=0]")
|
|
.filter(":visible")
|
|
.filter(filterKeepNormalElements)
|
|
.each(function(i, val) {
|
|
result.push({
|
|
v: val, // value
|
|
t: 0, // tabIndex
|
|
i: cnt++ // index for stable sort
|
|
});
|
|
});
|
|
|
|
$container
|
|
.find("[tabindex]")
|
|
.filter(":visible")
|
|
.filter(filterKeepSpeciallyFocusable)
|
|
.each(function(i, val) {
|
|
result.push({
|
|
v: val, // value
|
|
t: val.tabIndex, // tabIndex
|
|
i: cnt++ // index
|
|
});
|
|
});
|
|
|
|
fixIndexSelector.disable && fixIndexSelector.disable();
|
|
|
|
result = $.map(result.sort(sortFocusable), // needs stable sort
|
|
function(val) {
|
|
return val.v;
|
|
}
|
|
);
|
|
|
|
|
|
return $(result);
|
|
|
|
}
|
|
|
|
function trap() {
|
|
this.keydown(onkeypress);
|
|
this.data(DATA_ISTRAPPING_KEY, true);
|
|
return this;
|
|
}
|
|
|
|
function untrap() {
|
|
this.unbind('keydown', onkeypress);
|
|
this.removeData(DATA_ISTRAPPING_KEY);
|
|
return this;
|
|
}
|
|
|
|
function isTrapping() {
|
|
return !!this.data(DATA_ISTRAPPING_KEY);
|
|
}
|
|
|
|
$.fn.extend({
|
|
trap: trap,
|
|
untrap: untrap,
|
|
isTrapping: isTrapping
|
|
});
|
|
|
|
// jQuery 1.6.x tabindex attr hooks management
|
|
// this triggers problems for tabindex attribute
|
|
// selectors in IE7-
|
|
// see https://github.com/julienw/jquery-trap-input/issues/3
|
|
|
|
var fixIndexSelector = {};
|
|
|
|
if ($.find.find && $.find.attr !== $.attr) {
|
|
// jQuery uses Sizzle (this is jQuery >= 1.3)
|
|
// sizzle uses its own attribute handling (in jq 1.6.x and below)
|
|
(function() {
|
|
var tabindexKey = "tabindex";
|
|
var sizzleAttrHandle = $.expr.attrHandle;
|
|
|
|
// this function comes directly from jQuery 1.7.2 (propHooks.tabIndex.get)
|
|
// we have to put it here if we want to support jQuery < 1.6 which
|
|
// doesn't have an attrHooks object to reference.
|
|
function getTabindexAttr(elem) {
|
|
// elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
|
|
// http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
|
|
var attributeNode = elem.getAttributeNode(tabindexKey);
|
|
|
|
return attributeNode && attributeNode.specified ?
|
|
parseInt( attributeNode.value, 10 ) :
|
|
undefined;
|
|
}
|
|
|
|
function fixSizzleAttrHook() {
|
|
// in jQ <= 1.6.x, we add to Sizzle the attrHook from jQuery's attr method
|
|
sizzleAttrHandle[tabindexKey] = sizzleAttrHandle.tabIndex = getTabindexAttr;
|
|
}
|
|
|
|
function unfixSizzleAttrHook() {
|
|
delete sizzleAttrHandle[tabindexKey];
|
|
delete sizzleAttrHandle.tabIndex;
|
|
}
|
|
|
|
|
|
fixIndexSelector = {
|
|
enable: fixSizzleAttrHook,
|
|
disable: unfixSizzleAttrHook
|
|
};
|
|
})();
|
|
}
|
|
})( jQuery ); |