Project import generated by Copybara.
GitOrigin-RevId: d9e9e3fb4e31372ec1fb43b178994ca78fa8fe70
diff --git a/static/third_party/js/keys.js b/static/third_party/js/keys.js
new file mode 100644
index 0000000..1f2a7ff
--- /dev/null
+++ b/static/third_party/js/keys.js
@@ -0,0 +1,192 @@
+/**
+ * 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
+};
+
+})();