Created
January 22, 2016 21:18
-
-
Save beatak/4b8ed159c2c95f77f789 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function ($) { | |
/** | |
* utility plugin for working on aria-attributes. | |
* the way it's returning the value is same as .attr(), meaning | |
* getter only works for the first matched but setter will set | |
* all matched elements. | |
* aria attributes | |
* | |
* TODO | |
* - massage get/set val types, according to | |
* https://www.w3.org/TR/wai-aria/states_and_properties | |
* - validation for Token characters? | |
*/ | |
/** | |
* ARIA value types: | |
* https://www.w3.org/TR/wai-aria/states_and_properties#propcharacteristic_value | |
* After all, representation is always string, but when you get or set, you wanna have it as the | |
*/ | |
var ARIA_BOOLEAN = 'Boolean'; | |
var ARIA_TRISTATE = 'String'; // this is to be equivalent to the visual queue of HTML checkbox | |
var ARIA_BOOLEAN_UNDEFINED = 'UndefinableBoolean'; // this is the more-computational tri-state, where undefined is usually the default, and means something | |
var ARIA_ID_REFERENCE = 'String'; | |
var ARIA_ID_REFERENCE_LIST = 'Array'; | |
var ARIA_INTEGER = 'Number'; | |
var ARIA_NUMBER = 'Number'; | |
var ARIA_STRING = 'String'; | |
var ARIA_TOKEN = 'String'; | |
var ARIA_TOKEN_LIST = 'Array'; | |
/** | |
* Keys of ARIA_PROP are generated by the following code: | |
* https://www.w3.org/TR/wai-aria/states_and_properties | |
* | |
* [].map.apply( | |
* document.getElementById('index_state_prop') | |
* .querySelectorAll('dt'), | |
* [function (elm) { | |
* return elm.textContent.trim().substring(5).split(' ')[0]; | |
* }] | |
* ).sort(); | |
*/ | |
var ARIA_PROP = { | |
'activedescendant': ARIA_ID_REFERENCE, | |
'atomic': ARIA_BOOLEAN, | |
'autocomplete': ARIA_TOKEN, | |
'busy': ARIA_BOOLEAN, | |
'checked': ARIA_TRISTATE, | |
'controls': ARIA_ID_REFERENCE_LIST, | |
'describedby': ARIA_ID_REFERENCE_LIST, | |
'disabled': ARIA_BOOLEAN, | |
'dropeffect': ARIA_TOKEN_LIST, | |
'expanded': ARIA_BOOLEAN_UNDEFINED, | |
'flowto': ARIA_ID_REFERENCE_LIST, | |
'grabbed': ARIA_BOOLEAN_UNDEFINED, | |
'haspopup': ARIA_BOOLEAN, | |
'hidden': ARIA_BOOLEAN, | |
'invalid': ARIA_TOKEN, | |
'label': ARIA_STRING, | |
'labelledby': ARIA_ID_REFERENCE_LIST, | |
'level': ARIA_INTEGER, | |
'live': ARIA_TOKEN, | |
'multiline': ARIA_BOOLEAN, | |
'multiselectable': ARIA_BOOLEAN, | |
'orientation': ARIA_TOKEN, | |
'owns': ARIA_ID_REFERENCE_LIST, | |
'posinset': ARIA_INTEGER, | |
'pressed': ARIA_TRISTATE, | |
'readonly': ARIA_BOOLEAN, | |
'relevant': ARIA_TOKEN_LIST, | |
'required': ARIA_BOOLEAN, | |
'selected': ARIA_BOOLEAN_UNDEFINED, | |
'setsize': ARIA_INTEGER, | |
'sort': ARIA_TOKEN, | |
'valuemax': ARIA_NUMBER, | |
'valuemin': ARIA_NUMBER, | |
'valuenow': ARIA_NUMBER, | |
'valuetext': ARIA_STRING | |
}; | |
var validateKey = function (_k) { | |
var key = _k.toLowerCase(); | |
if (ARIA_PROP[key] === undefined) { | |
throw new Error('Not legit key: ' + _k); | |
} | |
return key; | |
}; | |
var checkTrue = function (str) { | |
return str.toLowerCase() === 'true' ? true : false; | |
}; | |
// FIXME this is not that great | |
var normalizeVal = function (val, valtype) { | |
var result; | |
console.log(valtype + ': ' + val); | |
if (valtype === 'UndefinableBoolean') { | |
result = (val.toLowerCase() === 'undefined' ? undefined : checkTrue(val)); | |
} | |
else if (valtype === 'Boolean') { | |
console.log('bool: ' + val); | |
result = checkTrue(val); | |
} | |
else if (valtype === 'Number') { | |
result = parseFloat(val); | |
} | |
else if(valtype === 'Array') { | |
result = val.split(' '); | |
} | |
else { | |
result = val; | |
} | |
console.log( ' ==> ' + result); | |
return result; | |
}; | |
var ariaGetter = function (elm, _k) { | |
// FIXME massage value | |
var key = validateKey(_k); | |
var val = normalizeVal($(elm).attr('aria-' + key), ARIA_PROP[key]); | |
return val; | |
}; | |
var ariaSetter = function (elm, _k, val) { | |
// FIXME massage value here | |
var key = validateKey(_k); | |
$(elm).attr('aria-' + key, '' + val); | |
return elm; | |
}; | |
$.fn.aria = function (key, val) { | |
var result, | |
is_getting = (arguments.length === 1); | |
if (is_getting) { | |
result = ariaGetter(this[0], key); | |
} | |
else { | |
result = []; | |
$.each(this, function (i, elm) { | |
result.push(ariaSetter(elm, key, val)); | |
}); | |
result = $(result); | |
} | |
return result; | |
}; | |
})(jQuery); | |
// ================================================================ | |
// ================================================================ | |
$(function () { | |
var $mainButton = $('#main-button-link'); | |
var $popupContainer = $('#popup-container'); | |
var $subnavContainer = $('#subnav-region-list2 [data-foo="menu"]'); | |
var $thisButton = $('#this-button'); | |
var $subnavTogglers = $('#subnav-region-list1 a'); | |
var togglePopup = function () { | |
var expanded = $mainButton.aria('expanded'); | |
$popupContainer.toggleClass('hide'); | |
if (expanded) { | |
// closing | |
$mainButton.aria('expanded', false); | |
$popupContainer.removeAttr('tabindex'); | |
} | |
else { | |
// opening | |
$mainButton.aria('expanded', true); | |
$popupContainer.attr('tabindex', '-1').focus(); | |
} | |
}; | |
var displaySubmenu = function (elm) { | |
var $elm = $(elm); | |
var target_id = $elm.aria('controls'); | |
console.log('display submenu: ' + target_id); | |
$subnavTogglers | |
.aria('selected', false); | |
$elm.aria('selected', true); | |
$subnavContainer | |
.addClass('hide') | |
.removeAttr('tabindex'); | |
$('#sub-nav-shop-menuitem-orders').aria('flowto', target_id); | |
$('#' + target_id) | |
.removeClass('hide') | |
.attr('tabindex', '-1') | |
.focus(); | |
// .find('a:first').focus(); | |
}; | |
$subnavTogglers.on( | |
'click', | |
function (ev) { | |
ev.stopPropagation(); | |
ev.preventDefault(); | |
displaySubmenu(this); | |
} | |
); | |
$popupContainer.on('click', function () { | |
togglePopup(); | |
}); | |
$mainButton.on('click', function (ev) { | |
ev.preventDefault(); | |
togglePopup(); | |
}); | |
$thisButton.on( | |
'click', | |
function (ev) { | |
ev.preventDefault(); | |
ev.stopPropagation(); | |
var msg = 'this button is useless.'; | |
if (window.speechSynthesis) { | |
window.speechSynthesis.speak(new SpeechSynthesisUtterance(msg)); | |
} | |
else { | |
console.log(msg); | |
} | |
} | |
); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment