data tables are going to need some work yet, but the other stuff seems to be working 100%
501 lines
No EOL
11 KiB
JavaScript
501 lines
No EOL
11 KiB
JavaScript
(function () {
|
|
|
|
var Event = YAHOO.util.Event,
|
|
Dom = YAHOO.util.Dom,
|
|
Lang = YAHOO.lang,
|
|
UA = YAHOO.env.ua,
|
|
|
|
CarouselPrototype = YAHOO.widget.Carousel.prototype,
|
|
fnCarouselInitAttributes = CarouselPrototype.initAttributes,
|
|
|
|
m_oToolbarButtons = {},
|
|
m_oCurrentButtonIndex = {},
|
|
|
|
m_bUseARIA = (UA.gecko && UA.gecko >= 1.9) || (UA.ie && UA.ie >= 8),
|
|
|
|
m_aFocusableElements = {
|
|
|
|
"A": true,
|
|
"SELECT": true,
|
|
"TEXTAREA": true,
|
|
"BUTTON": true,
|
|
"INPUT": true
|
|
|
|
},
|
|
|
|
_USE_ARIA = "usearia",
|
|
_ARIA_PREFIX = "aria-",
|
|
_HIDDEN = "hidden",
|
|
_ASTERIX = "*",
|
|
_ID = "id",
|
|
_ROLE = "role",
|
|
_OPTION = "option",
|
|
_LISTBOX = "listbox",
|
|
_LI = "li",
|
|
_SELECTED_ITEM = "selectedItem",
|
|
_SELECTED = "selected",
|
|
_TOOLBAR = "toolbar",
|
|
_KEY_DOWN = "keydown",
|
|
_PRESENTATION = "presentation",
|
|
_BUTTON = "button",
|
|
_HREF = "href",
|
|
_UL = "UL",
|
|
_A = "A",
|
|
_RENDER = "render",
|
|
_SELECTED_ITEM_CHANGE = "selectedItemChange",
|
|
_SHOW = "show",
|
|
_PAGE_CHANGE = "pageChange",
|
|
_KEY_PRESS = "keypress",
|
|
_CONTROLS = "controls",
|
|
_LABELLED_BY = "labelledby",
|
|
_DESCRIBED_BY = "describedby",
|
|
_CURRENT_PAGE = " (Current Page)";
|
|
|
|
|
|
|
|
var setARIARole = function (element, role) {
|
|
|
|
element.setAttribute(_ROLE, role);
|
|
|
|
};
|
|
|
|
var setARIAProperty = function (element, property, value) {
|
|
|
|
element.setAttribute((_ARIA_PREFIX + property), value);
|
|
|
|
};
|
|
|
|
var getCarouselListElement = function () {
|
|
|
|
var aCarouselEl = this.getElementsByClassName(this.CLASSES.CAROUSEL_EL);
|
|
|
|
if (aCarouselEl.length === 1) {
|
|
return aCarouselEl[0];
|
|
}
|
|
|
|
};
|
|
|
|
|
|
var getCarouselNavElement = function () {
|
|
|
|
var aNavigationEl = this.getElementsByClassName(this.CLASSES.NAVIGATION);
|
|
|
|
if (aNavigationEl.length === 1) {
|
|
return aNavigationEl[0];
|
|
}
|
|
|
|
};
|
|
|
|
var isFocusableElement = function (element) {
|
|
|
|
return ((element.tabIndex > -1 || m_aFocusableElements[element.nodeName.toUpperCase()]) &&
|
|
element.type !== _HIDDEN && !element.disabled);
|
|
|
|
};
|
|
|
|
|
|
var addToTabIndex = function (element) {
|
|
|
|
element.tabIndex = 0;
|
|
|
|
};
|
|
|
|
|
|
var removeFromTabIndex = function (element) {
|
|
|
|
element.tabIndex = -1;
|
|
|
|
};
|
|
|
|
|
|
var enableCarouselItem = function (index) {
|
|
|
|
var oItem = this.getItem(index);
|
|
|
|
if (oItem) {
|
|
Dom.getElementsBy(isFocusableElement, _ASTERIX, oItem.id, addToTabIndex);
|
|
}
|
|
|
|
};
|
|
|
|
|
|
var disableCarouselItem = function (index) {
|
|
|
|
var oItem = this.getItem(index);
|
|
|
|
if (oItem) {
|
|
Dom.getElementsBy(isFocusableElement, _ASTERIX, oItem.id, removeFromTabIndex);
|
|
}
|
|
|
|
};
|
|
|
|
|
|
var onSelectedItemChange = function (event) {
|
|
|
|
var nPreviousItemIndex = event.prevValue;
|
|
|
|
if (Lang.isNumber(nPreviousItemIndex)) {
|
|
disableCarouselItem.call(this, nPreviousItemIndex);
|
|
}
|
|
|
|
enableCarouselItem.call(this, event.newValue);
|
|
|
|
setARIAProperty(this.getElementForItem(event.newValue), _SELECTED, true);
|
|
|
|
};
|
|
|
|
|
|
var onKeyDown = function (event) {
|
|
|
|
Event.stopPropagation(event);
|
|
|
|
var sId = this.get(_ID),
|
|
aToolBarButtons = m_oToolbarButtons[sId],
|
|
nCurrentButtonIndex = m_oCurrentButtonIndex[sId],
|
|
oNextButton,
|
|
oCurrentButton;
|
|
|
|
|
|
switch (Event.getCharCode(event)) {
|
|
|
|
case 0x25: // left arrow
|
|
case 0x26: // up arrow
|
|
|
|
oCurrentButton = aToolBarButtons[nCurrentButtonIndex];
|
|
oCurrentButton.tabIndex = -1;
|
|
|
|
nCurrentButtonIndex = nCurrentButtonIndex - 1;
|
|
|
|
oNextButton = aToolBarButtons[nCurrentButtonIndex];
|
|
|
|
if (!oNextButton) {
|
|
nCurrentButtonIndex = aToolBarButtons.length - 1;
|
|
oNextButton = aToolBarButtons[nCurrentButtonIndex];
|
|
}
|
|
|
|
m_oCurrentButtonIndex[sId] = nCurrentButtonIndex;
|
|
|
|
oNextButton.tabIndex = 0;
|
|
oNextButton.focus();
|
|
|
|
break;
|
|
|
|
case 0x27: // right arrow
|
|
case 0x28: // down arrow
|
|
|
|
oCurrentButton = aToolBarButtons[nCurrentButtonIndex];
|
|
oCurrentButton.tabIndex = -1;
|
|
|
|
nCurrentButtonIndex = nCurrentButtonIndex + 1;
|
|
|
|
oNextButton = aToolBarButtons[nCurrentButtonIndex];
|
|
|
|
if (!oNextButton) {
|
|
nCurrentButtonIndex = 0;
|
|
oNextButton = aToolBarButtons[nCurrentButtonIndex];
|
|
}
|
|
|
|
m_oCurrentButtonIndex[sId] = nCurrentButtonIndex;
|
|
|
|
oNextButton.tabIndex = 0;
|
|
oNextButton.focus();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
var addOptionRole = function (element) {
|
|
|
|
setARIARole(element, _OPTION);
|
|
|
|
Dom.getElementsBy(isFocusableElement, _ASTERIX, element, removeFromTabIndex);
|
|
|
|
};
|
|
|
|
|
|
var onRender = function () {
|
|
|
|
var oList = getCarouselListElement.call(this),
|
|
nSelectedItemIndex,
|
|
oNavigation;
|
|
|
|
|
|
if (oList) {
|
|
|
|
setARIARole(oList, _LISTBOX);
|
|
Dom.batch(oList.getElementsByTagName(_LI), addOptionRole);
|
|
|
|
|
|
nSelectedItemIndex = this.get(_SELECTED_ITEM);
|
|
|
|
if (Lang.isNumber(nSelectedItemIndex)) {
|
|
setARIAProperty(this.getElementForItem(nSelectedItemIndex), _SELECTED, true);
|
|
enableCarouselItem.call(this, nSelectedItemIndex);
|
|
}
|
|
|
|
|
|
oNavigation = getCarouselNavElement.call(this);
|
|
|
|
if (oNavigation) {
|
|
setARIARole(oNavigation, _TOOLBAR);
|
|
Event.on(oNavigation, _KEY_DOWN, onKeyDown, null, this);
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
var setupPageButton = function (element, object) {
|
|
|
|
var oCarousel = object.carousel,
|
|
aToolBarButtons = object.toolbarButtons;
|
|
|
|
setARIARole(element, _PRESENTATION);
|
|
|
|
var oAnchor = Dom.getFirstChild(element);
|
|
|
|
setARIARole(oAnchor, _BUTTON);
|
|
setARIAProperty(oAnchor, _CONTROLS, object.listID);
|
|
|
|
oAnchor.value = oAnchor.getAttribute(_HREF, 2);
|
|
oAnchor.removeAttribute(_HREF);
|
|
|
|
var nTabIndex = Dom.hasClass(element, oCarousel.CLASSES.SELECTED_NAV) ? 0 : -1;
|
|
|
|
oAnchor.tabIndex = nTabIndex;
|
|
|
|
var nToolBarIndex = aToolBarButtons.length;
|
|
var oEM = Dom.getFirstChild(oAnchor);
|
|
|
|
if (nTabIndex === 0) {
|
|
m_oCurrentButtonIndex[oCarousel.get(_ID)] = nToolBarIndex;
|
|
oEM.innerHTML = oEM.innerHTML + _CURRENT_PAGE;
|
|
}
|
|
|
|
aToolBarButtons[nToolBarIndex] = oAnchor;
|
|
|
|
};
|
|
|
|
|
|
var setupNextPrevPageButton = function (element, object) {
|
|
|
|
var aToolbarButtons = object.toolbarButtons;
|
|
|
|
setARIARole(element, _PRESENTATION);
|
|
setARIAProperty(element, _CONTROLS, object.listID);
|
|
|
|
var oInput = Dom.getFirstChild(element);
|
|
|
|
if (!oInput.disabled) {
|
|
oInput.tabIndex = -1;
|
|
aToolbarButtons[aToolbarButtons.length] = oInput;
|
|
}
|
|
|
|
};
|
|
|
|
|
|
var updatePagingNav = function (event) {
|
|
|
|
var oNavigation = getCarouselNavElement.call(this),
|
|
aToolBarButtons = [],
|
|
nCurrentButtonIndex,
|
|
oFirstChild,
|
|
oList,
|
|
sListID;
|
|
|
|
|
|
if (oNavigation) {
|
|
|
|
oFirstChild = Dom.getFirstChild(oNavigation);
|
|
oList = getCarouselListElement.call(this);
|
|
|
|
|
|
if (oFirstChild && oList) {
|
|
|
|
sListID = oList.id || Dom.generatId(oList);
|
|
|
|
if (oFirstChild.nodeName.toUpperCase() === _UL) {
|
|
|
|
setARIARole(oFirstChild, _PRESENTATION);
|
|
|
|
Dom.batch(oFirstChild.getElementsByTagName(_LI), setupPageButton,
|
|
{ carousel: this, toolbarButtons: aToolBarButtons, listID: sListID });
|
|
|
|
}
|
|
else {
|
|
|
|
oFirstChild.tabIndex = 0;
|
|
setARIAProperty(oFirstChild, _CONTROLS, sListID);
|
|
|
|
}
|
|
|
|
Dom.batch(this.getElementsByClassName(this.CLASSES.BUTTON),
|
|
setupNextPrevPageButton, { toolbarButtons: aToolBarButtons, listID: sListID });
|
|
|
|
m_oToolbarButtons[this.get(_ID)] = aToolBarButtons;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
var onPageChange = function () {
|
|
|
|
Lang.later(0, this, updatePagingNav);
|
|
|
|
};
|
|
|
|
|
|
var onKeyPress = function (event) {
|
|
|
|
var oTarget = Event.getTarget(event),
|
|
nCharCode = Event.getCharCode(event);
|
|
|
|
if (oTarget.nodeName.toUpperCase() === _A &&
|
|
Dom.getAncestorByClassName(oTarget, this.CLASSES.NAVIGATION) &&
|
|
(nCharCode === 13 || nCharCode === 32)) {
|
|
|
|
this._pagerClickHandler(event);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
var setLabelledByOnRender = function (event, id) {
|
|
|
|
this.set(_LABELLED_BY, id, true);
|
|
this.removeListener(_RENDER, setLabelledByOnRender);
|
|
|
|
};
|
|
|
|
|
|
var setDescribedByOnRender = function (event, id) {
|
|
|
|
this.set(_DESCRIBED_BY, id, true);
|
|
this.removeListener(_RENDER, setDescribedByOnRender);
|
|
|
|
};
|
|
|
|
|
|
Lang.augmentObject(CarouselPrototype, {
|
|
|
|
_setLabelledBy: function (id) {
|
|
|
|
var oNav = getCarouselNavElement.call(this),
|
|
oList = getCarouselListElement.call(this);
|
|
|
|
if (this.get(_USE_ARIA) && oNav && oList) {
|
|
setARIAProperty(oNav, _LABELLED_BY, id);
|
|
setARIAProperty(oList, _LABELLED_BY, id);
|
|
}
|
|
else {
|
|
this.on(_RENDER, setLabelledByOnRender, id);
|
|
}
|
|
|
|
},
|
|
|
|
_setDescribedBy: function (id) {
|
|
|
|
var oNav = getCarouselNavElement.call(this),
|
|
oList = getCarouselListElement.call(this);
|
|
|
|
if (this.get(_USE_ARIA) && oNav && oList) {
|
|
setARIAProperty(oNav, _DESCRIBED_BY, id);
|
|
setARIAProperty(oList, _DESCRIBED_BY, id);
|
|
}
|
|
else {
|
|
this.on(_RENDER, setDescribedByOnRender, id);
|
|
}
|
|
|
|
},
|
|
|
|
_setUseARIA: function (p_bUseARIA) {
|
|
|
|
if (p_bUseARIA) {
|
|
|
|
this.on(_RENDER, onRender);
|
|
this.on(_SELECTED_ITEM_CHANGE, onSelectedItemChange);
|
|
this.on(_SHOW, updatePagingNav);
|
|
this.on(_PAGE_CHANGE, onPageChange);
|
|
this.on(_KEY_PRESS, onKeyPress);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
initAttributes: function (p_oAttributes) {
|
|
|
|
/**
|
|
* @attribute usearia
|
|
* @description Boolean indicating if use of the WAI-ARIA Roles and States should
|
|
* be enabled.
|
|
* @type Boolean
|
|
* @default true for Firefox 3 and IE 8, false for all other browsers.
|
|
*/
|
|
this.setAttributeConfig(_USE_ARIA, {
|
|
|
|
value: p_oAttributes.usearia || m_bUseARIA,
|
|
validator: Lang.isBoolean,
|
|
writeOnce: true,
|
|
method: this._setUseARIA
|
|
|
|
});
|
|
|
|
|
|
/**
|
|
* @attribute labelledby
|
|
* @description String representing the id of the element that labels the Carousel's
|
|
* navigation (<code><div class="yui-carousel-nav"></code>) and content
|
|
* (<code><div class="yui-carousel-content"></code>) elements.
|
|
* Maps directly to the <a href="http://www.w3.org/TR/wai-aria/#labelledby">
|
|
* <code>aria-labelledby</code></a> attribute.
|
|
* @type String
|
|
* @default null
|
|
*/
|
|
this.setAttributeConfig(_LABELLED_BY, {
|
|
|
|
value: p_oAttributes.labelledby,
|
|
validator: Lang.isString,
|
|
method: this._setLabelledBy
|
|
|
|
});
|
|
|
|
|
|
/**
|
|
* @attribute describedby
|
|
* @description String representing the id of the element that describes the Carousel's
|
|
* navigation (<code><div class="yui-carousel-nav"></code>) and content
|
|
* (<code><div class="yui-carousel-content"></code>) elements.
|
|
* Maps directly to the <a href="http://www.w3.org/TR/wai-aria/#describedby">
|
|
* <code>aria-describedby</code></a> attribute.
|
|
* @type String
|
|
* @default null
|
|
*/
|
|
this.setAttributeConfig(_DESCRIBED_BY, {
|
|
|
|
value: p_oAttributes.describedby,
|
|
validator: Lang.isString,
|
|
method: this._setDescribedBy
|
|
|
|
});
|
|
|
|
|
|
fnCarouselInitAttributes.apply(this, arguments);
|
|
|
|
|
|
if (m_bUseARIA) {
|
|
this.set(_USE_ARIA, true);
|
|
}
|
|
|
|
}
|
|
|
|
}, "initAttributes", "_setUseARIA", "_setLabelledBy", "_setDescribedBy");
|
|
|
|
}()); |