var _testing = false; // The index into _keyTable of the key currently being tested. var _currKey = 0; var _keysTotal = 0; var _keysGood = 0; var _keysBad = 0; var _keysSkipped = 0; var _modifierMode = "None"; var _keydownCapture = []; var _keyupCapture = []; var CAPTURE_KEYCODE = 0; var CAPTURE_CODE = 1; var CAPTURE_KEY = 2; var CAPTURE_SHIFTKEY = 3; var CAPTURE_CONTROLKEY = 4; var CAPTURE_ALTKEY = 5; var CAPTURE_METAKEY = 6; // An array of KeyInfo for each key to be tested. var _keyTable = []; // KeyInfo fields. var KEYINFO_CODE = 0; // |code| for this key var KEYINFO_ROW = 1; // Keyboard row var KEYINFO_TYPE = 2; // Key type (see below) var KEYINFO_WIDTH = 3; // Width of key: 0=normal var KEYINFO_KEYCAP = 4; // Keycap string to display var KEYINFO_KEY = 5; // Unmodified key value var KEYINFO_KEY_SHIFT = 6; // Shifted key value var KEYTYPE_NORMAL = 0; var KEYTYPE_DISABLED = 1; // Key cannot be tested: e.g., CapsLock var KEYTYPE_END = 2; // Used to mark end of KeyTable var KEYTYPE_MODIFIER = 3; // Modifer key function clearChildren(e) { while (e.firstChild !== null) { e.removeChild(e.firstChild); } } function setText(e, text) { clearChildren(e); e.appendChild(document.createTextNode(text)); } function setUserAgent() { var userAgent = navigator.userAgent; uaDiv = document.getElementById("useragent"); setText(uaDiv, userAgent); } function addEventListener(obj, etype, handler) { if (obj.addEventListener) { obj.addEventListener(etype, handler, false); } else if (obj.attachEvent) { obj.attachEvent("on"+etype, handler); } else { obj["on"+etype] = handler; } } function addClass(obj, className) { obj.classList.add(className); } function removeClass(obj, className) { obj.classList.remove(className); } function addInnerText(obj, text) { obj.appendChild(document.createTextNode(text)); } function calcLocation(loc) { if (loc == 1) return "LEFT"; if (loc == 2) return "RIGHT"; if (loc == 3) return "NUMPAD"; return loc; } function isModifierKey(e) { // Shift, Control, Alt if (e.keyCode >= 16 && e.keyCode <= 18) { return true; } // Windows, Command or Meta key. if (e.keyCode == 224 // Right/Left: Gecko || e.keyCode == 91 // Left: WebKit/Blink || e.keyCode == 93 // Right: WebKit/Blink ) { return true; } return false; } function init(title, keytable) { _keyTable = keytable; createBody(title, keytable); setUserAgent(); var input = document.getElementById("input"); input.disabled = true; addEventListener(input, "keydown", onKeyDown); addEventListener(input, "keyup", onKeyUp); //addEventListener(input, "beforeInput", onBeforeInput); //addEventListener(input, "input", onInput); } function onKeyDown(e) { // Ignore modifier keys when checking modifier combinations. if (_modifierMode != "None" && isModifierKey(e)) { return; } _keydownInfo = [e.keyCode, e.code, e.key, e.shiftKey, e.ctrlKey, e.altKey, e.metaKey]; if (e.keyCode == 9 || e.code == "Tab") { e.preventDefault(); } } function onKeyUp(e) { // Ignore modifier keys when checking modifier combinations. if (_modifierMode != "None" && isModifierKey(e)) { return; } _keyupInfo = [e.keyCode, e.code, e.key, e.shiftKey, e.ctrlKey, e.altKey, e.metaKey]; if (_testing) { verifyKey(); nextKey(); } } function onBeforeInput(e) { } function onInput(e) { } function addError(elem, str) { var p = document.createElement('p'); p.classList.add("error2"); p.textContent = str; elem.appendChild(p); } function addErrorIncorrect(elem, eventName, attrName, keyEventInfo, attr, expected) { addError(elem, "Incorrect " + eventName + " |" + attrName + "| = " + keyEventInfo[attr] + " - Expected " + expected); } function verifyKeyEventFields(eventName, keyEventInfo, code, key, error) { var verifyCode = document.getElementById("opt_attr_code").checked; var verifyKey = document.getElementById("opt_attr_key").checked; var verifyModifiers = document.getElementById("opt_attr_modifiers").checked; var good = true; if (!verifyCode && !verifyKey && !verifyModifiers) { good = false; addError(error, "Invalid test: At least one attribute must be selected for testing."); } if (verifyCode && keyEventInfo[CAPTURE_CODE] != code) { good = false; addErrorIncorrect(error, eventName, "code", keyEventInfo, CAPTURE_CODE, code); } if (verifyKey && keyEventInfo[CAPTURE_KEY] != key) { good = false; addErrorIncorrect(error, eventName, "key", keyEventInfo, CAPTURE_KEY, key); } if (verifyModifiers) { if (keyEventInfo[CAPTURE_SHIFTKEY] != (_modifierMode == "Shift")) { good = false; addErrorIncorrect(error, eventName, "shiftKey", keyEventInfo, CAPTURE_SHIFTKEY, false); } if (keyEventInfo[CAPTURE_CONTROLKEY]) { good = false; addErrorIncorrect(error, eventName, "controlKey", keyEventInfo, CAPTURE_CONTROLKEY, false); } if (keyEventInfo[CAPTURE_ALTKEY]) { good = false; addErrorIncorrect(error, eventName, "altKey", keyEventInfo, CAPTURE_ALTKEY, false); } if (keyEventInfo[CAPTURE_METAKEY]) { good = false; addErrorIncorrect(error, eventName, "metaKey", keyEventInfo, CAPTURE_METAKEY, false); } } return good; } function verifyKey() { _keysTotal++; var keyInfo = _keyTable[_currKey]; var code = keyInfo[KEYINFO_CODE]; var key = keyInfo[KEYINFO_KEY]; var keyShift = keyInfo[KEYINFO_KEY_SHIFT]; var keyCheck = key; if (_modifierMode == "Shift") { keyCheck = keyShift; } var verifyKeydown = document.getElementById("opt_event_keydown").checked; var verifyKeyup = document.getElementById("opt_event_keyup").checked; var error = document.createElement('div'); error.classList.add("error"); var good = true; if (verifyKeydown) { good = verifyKeyEventFields("keydown", _keydownInfo, code, keyCheck, error); } if (verifyKeyup) { good = verifyKeyEventFields("keyup", _keyupInfo, code, keyCheck, error); } if (!verifyKeydown && !verifyKeyup) { good = false; addError(error, "Invalid test: At least one event must be selected for testing."); } // Allow Escape key to skip the current key. var skipped = false; if (_keydownInfo[CAPTURE_KEYCODE] == 27 || _keydownInfo[CAPTURE_CODE] == "Escape") { good = true; skipped = true; } if (!good) { var p = document.createElement('p'); p.classList.add("error1"); p.textContent = "Error : " + code; error.insertBefore(p, error.firstChild); } removeNextKeyHilight(); if (skipped) { _keysSkipped++; document.getElementById(code).classList.add("skippedKey") } else if (good) { _keysGood++; document.getElementById(code).classList.add("goodKey") } else { _keysBad++; document.getElementById(code).classList.add("badKey") } updateTestSummary(good ? null : error); } function updateTestSummary(error) { document.getElementById("keys-total").textContent = _keysTotal; document.getElementById("keys-good").textContent = _keysGood; document.getElementById("keys-bad").textContent = _keysBad; document.getElementById("keys-skipped").textContent = _keysSkipped; if (error) { var errors = document.getElementById("errors"); errors.insertBefore(error, errors.firstChild); } } function resetTest() { _keysTotal = 0; _keysGood = 0; _keysBad = 0; _currKey = -1; nextKey(); updateTestSummary(); // Remove previous test results. clearChildren(document.getElementById("errors")); // Remove highlighting from keys. for (var i = 0; i < _keyTable.length; i++) { var code = _keyTable[i][KEYINFO_CODE]; var type = _keyTable[i][KEYINFO_TYPE]; if (type != KEYTYPE_END) { var key = document.getElementById(code); key.classList.remove("goodKey"); key.classList.remove("badKey"); key.classList.remove("skippedKey"); } } } function startTest() { if (_testing) { // Cancel the currently running test. endTest(); return; } resetTest(); _testing = true; document.getElementById("start").value = "Stop Test" var input = document.getElementById("input"); input.value = ""; input.disabled = false; input.focus(); // Show test instructions and info. document.getElementById("test-info").style.display = 'block'; document.getElementById("instructions").style.display = 'block'; document.getElementById("test-done").style.display = 'none'; } function endTest() { _testing = false; removeNextKeyHilight(); document.getElementById("start").value = "Restart Test" document.getElementById("input").disabled = true; document.getElementById("instructions").style.display = 'none'; document.getElementById("test-done").style.display = 'block'; } function removeNextKeyHilight() { var curr = document.getElementById(_keyTable[_currKey][KEYINFO_CODE]); if (curr) { removeClass(curr, "nextKey") } } function addNextKeyHilight() { var curr = document.getElementById(_keyTable[_currKey][KEYINFO_CODE]); if (curr) { addClass(curr, "nextKey") } } function nextKey() { var keyInfo; var keepLooking = true; do { _currKey++; keyInfo = _keyTable[_currKey]; var type = keyInfo[KEYINFO_TYPE]; // Skip over disabled keys. keepLooking = (type == KEYTYPE_DISABLED); // Skip over modifier keys if we're testing modifier combinations. if (_modifierMode != "None" && type == KEYTYPE_MODIFIER) { keepLooking = true; } // Skip over keys in disabled rows. if (type != KEYTYPE_END) { var row = keyInfo[KEYINFO_ROW]; var rowEnabled = document.getElementById("opt_row_" + row).checked; keepLooking = keepLooking || !rowEnabled; } } while (keepLooking); if (keyInfo[KEYINFO_TYPE] == KEYTYPE_END) { endTest(); } else { addNextKeyHilight(); } } function toggleOptions() { var link = document.getElementById("optionstoggle"); var options = document.getElementById("options"); clearChildren(link); if (options.style.display == "block") { options.style.display = "none"; addInnerText(link, "Show Options"); } else { options.style.display = "block"; addInnerText(link, "Hide Options"); } } function toggleHelp() { var link = document.getElementById("helptoggle"); var help = document.getElementById("help"); clearChildren(link); if (help.style.display == "block") { help.style.display = "none"; addInnerText(link, "Show Help"); } else { help.style.display = "block"; addInnerText(link, "Hide Help"); } } function createBody(title, keytable) { var body = document.getElementsByTagName("body")[0]; var p; var span; var h1 = document.createElement('h1'); h1.textContent = "Keyboard Event Manual Test - " + title; body.appendChild(h1); // Display useragent. p = document.createElement('p'); p.textContent = "UserAgent: "; var useragent = document.createElement('span'); useragent.id = "useragent"; p.appendChild(useragent); body.appendChild(p); // Display input textedit. p = document.createElement('p'); p.textContent = "Test Input: "; var input1 = document.createElement('input'); input1.id = "input"; input1.type = "text"; input1.size = 80; p.appendChild(input1); p.appendChild(document.createTextNode(" ")); var input2 = document.createElement('input'); input2.id = "start"; input2.type = "button"; input2.onclick = function() { startTest(); return false; } input2.value = "Start Test"; p.appendChild(input2); p.appendChild(document.createTextNode(" ")); var optionsToggle = document.createElement('a'); optionsToggle.id = "optionstoggle"; optionsToggle.href = "javascript:toggleOptions()"; optionsToggle.textContent = "Show Options"; p.appendChild(optionsToggle); p.appendChild(document.createTextNode(" ")); var helpToggle = document.createElement('a'); helpToggle.id = "helptoggle"; helpToggle.href = "javascript:toggleHelp()"; helpToggle.textContent = "Show Help"; p.appendChild(helpToggle); body.appendChild(p); createOptions(body); createHelp(body); createKeyboard(body, keytable); // Test info and summary. var test_info = document.createElement('div'); test_info.id = "test-info"; test_info.style.display = "none"; var instructions = document.createElement('div'); instructions.id = "instructions"; p = document.createElement('p'); p.textContent = "Press the highlighted key."; instructions.appendChild(p); test_info.appendChild(instructions); var test_done = document.createElement('div'); test_done.id = "test-done"; p = document.createElement('p'); p.textContent = "Test complete!"; test_done.appendChild(p); test_info.appendChild(test_done); var summary = document.createElement('div'); summary.id = "summary"; p = document.createElement('p'); summary.appendChild(document.createTextNode("Keys Tested: ")); span = document.createElement('span'); span.id = "keys-total"; span.textContent = 0; summary.appendChild(span); summary.appendChild(document.createTextNode("; Passed ")); span = document.createElement('span'); span.id = "keys-good"; span.textContent = 0; summary.appendChild(span); summary.appendChild(document.createTextNode("; Failed ")); span = document.createElement('span'); span.id = "keys-bad"; span.textContent = 0; summary.appendChild(span); summary.appendChild(document.createTextNode("; Skipped ")); span = document.createElement('span'); span.id = "keys-skipped"; span.textContent = 0; summary.appendChild(span); test_info.appendChild(summary); var errors = document.createElement('div'); errors.id = "errors"; test_info.appendChild(errors); body.appendChild(test_info); } function addOptionTitle(cell, title) { var span = document.createElement('span'); span.classList.add("opttitle"); span.textContent = title; cell.appendChild(span); cell.appendChild(document.createElement("br")); } function addOptionCheckbox(cell, id, text) { var label = document.createElement("label"); var input = document.createElement("input"); input.type = "checkbox"; input.id = id; input.checked = true; label.appendChild(input); label.appendChild(document.createTextNode(" " + text)); cell.appendChild(label); cell.appendChild(document.createElement("br")); } function addOptionRadio(cell, group, text, handler, checked) { var label = document.createElement("label"); var input = document.createElement("input"); input.type = "radio"; input.name = group; input.value = text; input.onclick = handler; input.checked = checked; label.appendChild(input); label.appendChild(document.createTextNode(" " + text)); cell.appendChild(label); cell.appendChild(document.createElement("br")); } function handleModifierGroup() { var radio = document.querySelector("input[name=opt_modifier]:checked"); var oldMode = _modifierMode; _modifierMode = radio.value; if (oldMode == "Shift") { document.getElementById("ShiftLeft").classList.remove("activeModifierKey"); document.getElementById("ShiftRight").classList.remove("activeModifierKey"); } if (_modifierMode == "Shift") { document.getElementById("ShiftLeft").classList.add("activeModifierKey"); document.getElementById("ShiftRight").classList.add("activeModifierKey"); } } function createOptions(body) { var options = document.createElement('div'); options.id = "options"; options.style.display = "none"; var table = document.createElement('table'); table.classList.add("opttable"); var row = document.createElement('tr'); var cell; cell = document.createElement('td'); cell.classList.add("optcell"); addOptionTitle(cell, "Keyboard Rows"); addOptionCheckbox(cell, "opt_row_0", "Row E (top)"); addOptionCheckbox(cell, "opt_row_1", "Row D"); addOptionCheckbox(cell, "opt_row_2", "Row C"); addOptionCheckbox(cell, "opt_row_3", "Row B"); addOptionCheckbox(cell, "opt_row_4", "Row A (bottom)"); row.appendChild(cell); cell = document.createElement('td'); cell.classList.add("optcell"); addOptionTitle(cell, "Events"); addOptionCheckbox(cell, "opt_event_keydown", "keydown"); addOptionCheckbox(cell, "opt_event_keyup", "keyup"); row.appendChild(cell); cell = document.createElement('td'); cell.classList.add("optcell"); addOptionTitle(cell, "Attributes"); addOptionCheckbox(cell, "opt_attr_code", "code"); addOptionCheckbox(cell, "opt_attr_key", "key"); addOptionCheckbox(cell, "opt_attr_modifiers", "modifiers"); row.appendChild(cell); cell = document.createElement('td'); cell.classList.add("optcell"); addOptionTitle(cell, "Modifiers"); addOptionRadio(cell, "opt_modifier", "None", handleModifierGroup, true); addOptionRadio(cell, "opt_modifier", "Shift", handleModifierGroup, false); row.appendChild(cell); table.appendChild(row); options.appendChild(table); body.appendChild(options); } function addHelpText(div, text) { var p = document.createElement('p'); p.classList.add("help"); p.textContent = text; div.appendChild(p); } function createHelp(body) { var help = document.createElement('div'); help.id = "help"; help.style.display = "none"; addHelpText(help, "Click on the \"Start Test\" button to begin testing."); addHelpText(help, "Press the hilighted key to test it."); addHelpText(help, "Clicking anywhere outside the \"Test Input\" editbox will pause testing. To resume, click back inside the editbox."); addHelpText(help, "To skip a key while testing, press Escape."); addHelpText(help, "When testing with modifier keys, the modifier must be pressed before the keydown and released after the keyup of the key being tested."); body.appendChild(help); } function createKeyboard(body, keytable) { var keyboard = document.createElement('div'); keyboard.classList.add("keyboard"); var currRow = 0; var row = document.createElement('div'); row.classList.add("key-row"); for (var i = 0; i < keytable.length; i++) { var code = keytable[i][KEYINFO_CODE]; var rowId = keytable[i][KEYINFO_ROW]; var type = keytable[i][KEYINFO_TYPE]; var width = keytable[i][KEYINFO_WIDTH]; var keyCap = keytable[i][KEYINFO_KEYCAP]; if (type == KEYTYPE_END) { continue; } if (rowId != currRow) { keyboard.appendChild(row); row = document.createElement('div'); row.classList.add("key-row"); currRow = rowId; } var key = document.createElement('div'); key.id = code; key.classList.add("key"); if (width != 0) { key.classList.add("wide" + width); } key.textContent = keyCap; row.appendChild(key); } keyboard.appendChild(row); body.appendChild(keyboard); }