blob: 1f2a7ff451c095997083ce915d67813b427866de [file] [log] [blame]
/**
* Copyright 2008 Steve McKay.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Kibbles.Keys is a Javascript library providing simple cross browser
* keyboard event support.
*/
(function(){
var _listening = false;
// code to handler list map.
// Wildcard listeners use magic code wildcards "before" and "after".
var _listeners = {
before: [],
after: []
};
/*
* Map of key names to char code. This map is consulted before
* charCodeAt(0) is used to determine the character code.
*
* This map also serves as a definitive list of supported "special" keys.
* See _codeForEvent for details.
*/
var _CODE_MAP = {
ESC: 27,
ENTER: 13
};
/**
* Register a keypress listener.
*/
function _listen() {
if (_listening) return;
var d = document;
if (d.addEventListener) {
d.addEventListener('keypress', _handleKeyboardEvent, false);
d.addEventListener('keydown', _handleKeyDownEvent, false);
} else if (d.attachEvent) {
d.documentElement.attachEvent('onkeypress', _handleKeyboardEvent);
d.documentElement.attachEvent('onkeydown', _handleKeyDownEvent);
}
_listening = true;
}
/**
* Register a keypress listener for the supplied skip code.
*/
function _addKeyPressListener(spec, handler) {
var code = spec.toLowerCase();
if (code == "before" || code == "after") {
_listeners[code].push(handler);
return;
}
// try to find the character or key code.
code = _CODE_MAP[spec.toUpperCase()];
if (!code) {
code = spec.charCodeAt(0);
}
if (!_listeners[code]) {
_listeners[code] = [];
}
_listeners[code].push(handler);
}
/**
* Our handler for keypress events.
*/
function _handleKeyboardEvent(e) {
// If event is null, this is probably IE.
if (!e) e = window.event;
var source = _getSourceElement(e);
if (_isInputElement(source)) {
return;
}
if (_hasFlakeyModifier(e)) return;
var code = _codeForEvent(e);
if (code == undefined) return;
var payload = {
code: code
};
for (var i = 0; i < _listeners.before.length; i++) {
_listeners.before[i](payload);
}
var listeners = _listeners[code];
if (listeners) {
for (var i = 0; i < listeners.length; i++) {
listeners[i]({
code: code
});
}
}
for (var i = 0; i < _listeners.after.length; i++) {
_listeners.after[i](payload);
}
}
function _handleKeyDownEvent(e) {
if (!e) e = window.event;
var code = _codeForEvent(e);
if (code == _CODE_MAP['ESC'] || code == _CODE_MAP['ENTER']) {
_handleKeyboardEvent(e);
}
}
/**
* Returns the keycode associated with the event.
*/
function _codeForEvent(e) {
return e.keyCode ? e.keyCode : e.which;
}
/**
* Returns true if the supplied event has an associated modifier key
* that we have had trouble with in certain browsers.
*/
function _hasFlakeyModifier(e) {
return e.altKey || e.ctrlKey || e.metaKey;
}
/**
* Returns the source element for the supplied event.
*/
function _getSourceElement(e) {
var element = e.target;
if (!element) {
element = e.srcElement;
}
if (element.shadowRoot) {
// Find the element within the shadowDOM.
const path = e.path || e.composedPath();
element = path[0];
}
// If the source element is a text node, the parent is the object
// we're interested in.
if (element.nodeType == 3) {
element = element.parentNode;
}
return element;
}
/**
* Returns true if the element is a known form input element.
*/
function _isInputElement(element) {
return element.tagName == 'INPUT' || element.tagName == 'TEXTAREA';
}
/*
* A nice little namespace to call our own.
*
* Formalizing Kibbles.Keys as a traditional javascript class caused headaches
* with respect to capturing the context (what is "this" at any point in time).
* So we use a simple script exported via the "kibbles.keys" namespace.
*/
if (!window.kibbles)
window.kibbles = {}
window.kibbles.keys = {
listen: _listen,
addKeyPressListener: _addKeyPressListener
};
})();