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
+};
+
+})();