Run npm install followed by npm start to run Keystroke Count
90 |
91 |
92 |
98 |
99 |
100 |
101 |
102 |
--------------------------------------------------------------------------------
/app/index.js:
--------------------------------------------------------------------------------
1 | var appVersion = "1.0.0";
2 |
3 | const { ipcRenderer } = require("electron");
4 |
5 | // Get the localKeys from localStorage
6 | // localKeys is the keys array from keys.js
7 | var localKeys = localStorage.getItem("localKeys");
8 |
9 | // If localKeys is not found in localStorage
10 | if (localKeys === null) {
11 | // Set the keys array to localStorage by id of "localKeys"
12 | localStorage.setItem("localKeys", JSON.stringify(keys));
13 | console.log("Set keys array to localStorage");
14 | }
15 |
16 | // When a key press is detected in the main process this happens
17 | ipcRenderer.on("keydownEvent", (event, keyCodesPressed) => {
18 | // console.log(keyCodesPressed);
19 | // Set localKeys to "localKeys" from localStorage
20 | localKeys = JSON.parse(localStorage.getItem("localKeys"));
21 | // If localKeys doesn't exist in localStorage, we initialize it to the keys array from keys.js
22 |
23 | // Loop over all the objects in the keys array (from keys.js)
24 | for (let i in localKeys) {
25 | // If the keyCode in the keys array matches the last pressed key
26 | if (localKeys[i].keyCode == keyCodesPressed[0]) {
27 | // For that key, the timesClicked in that object is incremented by 1
28 | localKeys[i].timesClicked += 1;
29 | // Now update the localKeys array in localStorage to reflect the +1 in timesClicked
30 | localStorage.setItem("localKeys", JSON.stringify(localKeys));
31 | // This updates all the keys from setData.js and heatmap.js
32 | reloadKeys();
33 | // Break out of the loop
34 | break;
35 | }
36 | }
37 | });
38 |
39 | // For mouse clicks
40 | // When left click is detected
41 | ipcRenderer.on("leftClickEvent", (event, leftClicks) => {
42 | // Get the keys array from localStorage
43 | localKeys = JSON.parse(localStorage.getItem("localKeys"));
44 |
45 | // Loop over all the objects in the keys array (from keys.js)
46 | for (let i in localKeys) {
47 | // Check if the 'i'th keyCode is 134761167, which is the keyCode for leftClick (this code is randomly set by me in the keys array)
48 | if (localKeys[i].keyCode == 134761167) {
49 | // Increment the timesClicked for leftClick
50 | localKeys[i].timesClicked += 1;
51 | // Update the keys array in localStorage
52 | localStorage.setItem("localKeys", JSON.stringify(localKeys));
53 | // This shows the total number of mouse clicks today (from setData.js)
54 | setTotalMouseClicks();
55 | // Break out of the loop
56 | break;
57 | }
58 | }
59 | });
60 |
61 | // When right click is detected
62 | ipcRenderer.on("rightClickEvent", (event, rightClicks) => {
63 | // Get the keys array from localStorage
64 | localKeys = JSON.parse(localStorage.getItem("localKeys"));
65 |
66 | // Loop over all the objects in the keys array (from keys.js)
67 | for (let i in localKeys) {
68 | // Check if the 'i'th keyCode is 4164761167, which is the keyCode for rightClick (this code is randomly set by me in the keys array)
69 | if (localKeys[i].keyCode == 4164761167) {
70 | // Increment the timesClicked for rightClick
71 | localKeys[i].timesClicked += 1;
72 | // Update the keys array in localStorage
73 | localStorage.setItem("localKeys", JSON.stringify(localKeys));
74 | // This shows the total number of mouse clicks today (from setData.js)
75 | setTotalMouseClicks();
76 | // Break out of the loop
77 | break;
78 | }
79 | }
80 | });
81 |
82 | // For app update, if an update is available, the updateAvailable in the RemoteJSON repo will be updated to yes. That will result in the code below being executed
83 | fetch("https://virejdasani.github.io/RemoteJSON/KeystrokeCount/index.html")
84 | .then((response) => {
85 | return response.json();
86 | })
87 | .then((data) => {
88 | // If update is available, and this version is not the latest one, the update div will no longer be empty. It will have the following HTML
89 | if (data.updateAvailable == "yes" && data.latestVersion != appVersion) {
90 | document.getElementById("update").innerHTML = `
91 |
92 | ${data.updateText}
93 | Download update here
94 |
95 | `;
96 | }
97 | })
98 | .catch((err) => {
99 | // console.log(err)
100 | });
101 |
--------------------------------------------------------------------------------
/app/keyboard.js:
--------------------------------------------------------------------------------
1 | function virtualKeyClick() {
2 | // Get the array from localStorage
3 | let keysArr = JSON.parse(localStorage.getItem("localKeys"));
4 |
5 | // This removes the leftClick and rightClick keys from keysArr
6 | keysArr = removeByAttr(keysArr, "keyCode", 134761167);
7 | keysArr = removeByAttr(keysArr, "keyCode", 4164761167);
8 |
9 | // Loop over all keys from keysArr
10 | for (let i in keysArr) {
11 | // This is the current or 'i'th object in the array
12 | let thisKey = keysArr[i];
13 |
14 | // Check if thisKey is an element in the html
15 | if (document.getElementById(thisKey.keyName)) {
16 | // Check if the button is not disabled. Only if it isn't disabled, addEventListener
17 | if (
18 | !document.getElementById(thisKey.keyName).className.includes("disabled")
19 | ) {
20 | // When that key is pressed, to this
21 | document
22 | .getElementById(thisKey.keyName)
23 | .addEventListener("click", () => {
24 | // This is the innerHTML of the key
25 | let keyInnerHTML = document.getElementById(
26 | thisKey.keyName
27 | ).innerHTML;
28 | if (keyInnerHTML.includes("")) {
29 | // The innerHTML is changed to the times that key is pressed
30 | document.getElementById(
31 | thisKey.keyName
32 | ).innerHTML = `${thisKey.timesClicked}`;
33 | // In the 5 seconds, the initial innerHTML is restored in that key
34 | setTimeout(() => {
35 | document.getElementById(thisKey.keyName).innerHTML =
36 | keyInnerHTML;
37 | }, 5000);
38 | // If it is an arrow key, do this because for some reason without this, it doesn't work
39 | } else if (
40 | document
41 | .getElementById(thisKey.keyName)
42 | .className.includes("Arrow")
43 | ) {
44 | var arrow;
45 |
46 | // For arrow keys, set the arrow code to the arrow variable
47 | if (
48 | document
49 | .getElementById(thisKey.keyName)
50 | .className.includes("leftArrow")
51 | ) {
52 | arrow = "◀";
53 | } else if (
54 | document
55 | .getElementById(thisKey.keyName)
56 | .className.includes("downArrow")
57 | ) {
58 | arrow = "▼";
59 | } else if (
60 | document
61 | .getElementById(thisKey.keyName)
62 | .className.includes("upArrow")
63 | ) {
64 | arrow = "▲";
65 | } else if (
66 | document
67 | .getElementById(thisKey.keyName)
68 | .className.includes("rightArrow")
69 | ) {
70 | arrow = "▶";
71 | }
72 |
73 | // The innerHTML is changed to the times that key is pressed
74 | document.getElementById(
75 | thisKey.keyName
76 | ).innerHTML = `${thisKey.timesClicked}`;
77 | // In the 5 seconds, the initial innerHTML is restored in that key
78 | setTimeout(() => {
79 | document.getElementById(
80 | thisKey.keyName
81 | ).innerHTML = `${arrow}`;
82 | }, 5000);
83 | } else if (
84 | document
85 | .getElementById(thisKey.keyName)
86 | .className.includes("option")
87 | ) {
88 | // The innerHTML is changed to the times that key is pressed
89 | document.getElementById(
90 | thisKey.keyName
91 | ).innerHTML = `${thisKey.timesClicked}`;
92 | // In the 5 seconds, the initial innerHTML is restored in that key
93 | setTimeout(() => {
94 | document.getElementById(thisKey.keyName).innerHTML =
95 | "option";
96 | }, 5000);
97 | } else {
98 | // The innerHTML is changed to the times that key is pressed
99 | document.getElementById(
100 | thisKey.keyName
101 | ).innerHTML = `${thisKey.timesClicked}`;
102 | // In the 5 seconds, the initial innerHTML is restored in that key
103 | setTimeout(() => {
104 | document.getElementById(thisKey.keyName).innerHTML =
105 | keyInnerHTML;
106 | }, 5000);
107 | }
108 | });
109 | }
110 | }
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/app/heatmap.js:
--------------------------------------------------------------------------------
1 | const heatmapColors = [
2 | "#fa6e6e",
3 | "#fc7290",
4 | "#f57cb0",
5 | "#e78acc",
6 | "#d399e2",
7 | "#bca7f0",
8 | "#a5b3f7",
9 | "#94bef8",
10 | "#8ac7f3",
11 | "#8bceec",
12 | ];
13 |
14 | // This calculates and sets colors to the keyboard keys based on how many times they have been clicked
15 | function setHeatmap() {
16 | // Get the array from localStorage
17 | let keysArr = JSON.parse(localStorage.getItem("localKeys"));
18 |
19 | // This removes the leftClick and rightClick keys from keysArr
20 | keysArr = removeByAttr(keysArr, "keyCode", 134761167);
21 | keysArr = removeByAttr(keysArr, "keyCode", 4164761167);
22 |
23 | // This sorts keysArr in descending order of timeClicked and sets it to sortedArr
24 | let sortedArr = keysArr.sort((a, b) => b.timesClicked - a.timesClicked);
25 |
26 | // This gets the first item from sortedArr i.e. key with most presses
27 | let topKey = sortedArr[0].timesClicked;
28 |
29 | // Loop over all keys from keysArr
30 | for (let i in keysArr) {
31 | // This is the current or 'i'th object in the array
32 | let thisKey = keysArr[i];
33 |
34 | // Check if thisKey exits on the keyboard
35 | if (document.getElementById(thisKey.keyName)) {
36 | // This is to give colors to keys based on how many relative clicks to the topKey they have
37 | // The topKey is always the most intense color
38 | if (thisKey.timesClicked > 0.9 * topKey) {
39 | document.getElementById(thisKey.keyName).style.backgroundColor =
40 | heatmapColors[0];
41 | document.getElementById(thisKey.keyName).style.textShadow =
42 | "0px 1px 0px #4b4848";
43 | document.getElementById(thisKey.keyName).style.color = "#fff";
44 | } else if (thisKey.timesClicked > 0.8 * topKey) {
45 | document.getElementById(thisKey.keyName).style.backgroundColor =
46 | heatmapColors[1];
47 | document.getElementById(thisKey.keyName).style.textShadow =
48 | "0px 1px 0px #4b4848";
49 | document.getElementById(thisKey.keyName).style.color = "#fff";
50 | } else if (thisKey.timesClicked > 0.7 * topKey) {
51 | document.getElementById(thisKey.keyName).style.backgroundColor =
52 | heatmapColors[2];
53 | document.getElementById(thisKey.keyName).style.textShadow =
54 | "0px 1px 0px #4b4848";
55 | document.getElementById(thisKey.keyName).style.color = "#fff";
56 | } else if (thisKey.timesClicked > 0.6 * topKey) {
57 | document.getElementById(thisKey.keyName).style.backgroundColor =
58 | heatmapColors[3];
59 | document.getElementById(thisKey.keyName).style.textShadow =
60 | "0px 1px 0px #4b4848";
61 | document.getElementById(thisKey.keyName).style.color = "#fff";
62 | } else if (thisKey.timesClicked > 0.5 * topKey) {
63 | document.getElementById(thisKey.keyName).style.backgroundColor =
64 | heatmapColors[4];
65 | document.getElementById(thisKey.keyName).style.textShadow =
66 | "0px 1px 0px #4b4848";
67 | document.getElementById(thisKey.keyName).style.color = "#fff";
68 | } else if (thisKey.timesClicked > 0.4 * topKey) {
69 | document.getElementById(thisKey.keyName).style.backgroundColor =
70 | heatmapColors[5];
71 | document.getElementById(thisKey.keyName).style.textShadow =
72 | "0px 1px 0px #4b4848";
73 | document.getElementById(thisKey.keyName).style.color = "#fff";
74 | } else if (thisKey.timesClicked > 0.3 * topKey) {
75 | document.getElementById(thisKey.keyName).style.backgroundColor =
76 | heatmapColors[6];
77 | document.getElementById(thisKey.keyName).style.textShadow =
78 | "0px 1px 0px #4b4848";
79 | document.getElementById(thisKey.keyName).style.color = "#fff";
80 | } else if (thisKey.timesClicked > 0.2 * topKey) {
81 | document.getElementById(thisKey.keyName).style.backgroundColor =
82 | heatmapColors[7];
83 | document.getElementById(thisKey.keyName).style.textShadow =
84 | "0px 1px 0px #4b4848";
85 | document.getElementById(thisKey.keyName).style.color = "#fff";
86 | } else if (thisKey.timesClicked > 0.1 * topKey) {
87 | document.getElementById(thisKey.keyName).style.backgroundColor =
88 | heatmapColors[8];
89 | document.getElementById(thisKey.keyName).style.textShadow =
90 | "0px 1px 0px #4b4848";
91 | document.getElementById(thisKey.keyName).style.color = "#fff";
92 | } else if (thisKey.timesClicked > 0) {
93 | document.getElementById(thisKey.keyName).style.backgroundColor =
94 | heatmapColors[9];
95 | document.getElementById(thisKey.keyName).style.textShadow =
96 | "0px 1px 0px #4b4848";
97 | document.getElementById(thisKey.keyName).style.color = "#fff";
98 | } else if (thisKey.timesClicked == 0) {
99 | // console.log("thisKey never clicked")
100 | } else {
101 | // console.log("KEYERR");
102 | }
103 | } else {
104 | // console.log("This key not found:");
105 | // console.log(thisKey.keyName);
106 | }
107 | }
108 | }
109 |
110 | // For heatmapColors
111 | // https://colordesigner.io/gradient-generator
112 |
--------------------------------------------------------------------------------
/app/main.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const {
4 | app,
5 | BrowserWindow,
6 | Tray,
7 | globalShortcut,
8 | Menu,
9 | ipcMain,
10 | } = require("electron");
11 |
12 | // This is the npm package `open`, it is used here to open all links in an external browser
13 | const open = require("open");
14 |
15 | const path = require("path");
16 |
17 | const ioHook = require("iohook");
18 |
19 | const assetsDirectory = path.join(__dirname, "assets");
20 |
21 | let tray = undefined;
22 | let window = undefined;
23 |
24 | // Hide the menu and dev tools
25 | Menu.setApplicationMenu(null);
26 |
27 | // Quit the app when the window is closed
28 | app.on("window-all-closed", () => {
29 | hideWindow();
30 | });
31 |
32 | const createTray = () => {
33 | tray = new Tray(
34 | path.join(assetsDirectory, "trayIcons/keystrokeCountTrayIcon@2x.png")
35 | );
36 | tray.on("right-click", toggleWindow);
37 | tray.on("double-click", toggleWindow);
38 | tray.on("click", function (event) {
39 | toggleWindow();
40 | });
41 | };
42 |
43 | const createWindow = () => {
44 | window = new BrowserWindow({
45 | width: 900,
46 | height: 780,
47 | minWidth: 800,
48 | minHeight: 700,
49 | show: false,
50 | frame: false,
51 |
52 | // fullscreenable: false,
53 | // resizable: false,
54 | // alwaysOnTop: true,
55 | webPreferences: {
56 | nodeIntegration: true,
57 | contextIsolation: false,
58 | enableRemoteModule: true,
59 | },
60 | });
61 |
62 | // This is so that app shows up on all desktops/workspaces
63 | window.setVisibleOnAllWorkspaces(true);
64 |
65 | // Load index.html
66 | window.loadURL(`file://${path.join(__dirname, "index.html")}`);
67 |
68 | // If 'esc' is pressed, hide the app window
69 | window.webContents.on("before-input-event", (event, input) => {
70 | if (input.key === "Escape") {
71 | hideWindow();
72 | }
73 | });
74 |
75 | // This opens all links with `target="_blank"` in external browser
76 | window.webContents.on("new-window", function (event, url) {
77 | event.preventDefault();
78 | open(url);
79 | });
80 |
81 | // Hide the window when it loses focus
82 | window.on("blur", () => {
83 | hideWindow();
84 | });
85 |
86 | // This is a global shortcut to activate app with hotkey(s)
87 | globalShortcut.register("Alt+Shift+k", () => {
88 | if (window.isVisible()) {
89 | hideWindow();
90 | } else {
91 | showWindow();
92 | }
93 | });
94 | if (process.platform == "darwin") {
95 | // Don't show the app in the dock for macOS
96 | app.dock.hide();
97 | } else {
98 | // To hide the app in the dock for windows and linux
99 | window.setSkipTaskbar(true);
100 | }
101 | };
102 |
103 | const toggleWindow = () => {
104 | if (window.isVisible()) {
105 | hideWindow();
106 | } else {
107 | showWindow();
108 | }
109 | };
110 |
111 | const showWindow = () => {
112 | window.show();
113 | window.reload();
114 | };
115 |
116 | const hideWindow = () => {
117 | // This is required because app.hide() is not defined in windows and linux
118 | if (process.platform == "darwin") {
119 | // This is so that when reopening the window, the previous state is not remembered
120 | // window.reload()
121 | // Both of these are needed because they help restore focus back to the previous window
122 | app.hide();
123 | window.hide();
124 | } else {
125 | // This is so that when reopening the window, the previous state is not remembered
126 | // window.reload()
127 | // Both of these are needed because they help restore focus back to the previous window
128 | window.minimize();
129 | window.hide();
130 | }
131 | };
132 |
133 | // ioHook
134 | // docs: https://wilix-team.github.io/iohook/usage.html#generic-node-application
135 |
136 | var leftClicks = 0;
137 | var rightClicks = 0;
138 | var keysPressed = [];
139 |
140 | app.on("ready", () => {
141 | createTray();
142 | createWindow();
143 |
144 | // When a mouse button is clicked
145 | ioHook.on("mouseclick", (event) => {
146 | // Check if it is a left-click
147 | if (event["button"] === 1) {
148 | leftClicks += 1;
149 | // This sends the clicks to index.js where this num of clicks are handled
150 | window.webContents.send("leftClickEvent", leftClicks);
151 | }
152 |
153 | // Check if it is a right-click
154 | else if (event["button"] === 2) {
155 | rightClicks += 1;
156 | // This sends the clicks to index.js where this num of clicks are handled
157 | window.webContents.send("rightClickEvent", rightClicks);
158 | }
159 | });
160 |
161 | // When a keyboard button is pressed
162 | ioHook.on("keydown", (event) => {
163 | // console.log(event);
164 | // Append the keycode for the key pressed to the keysPressed array
165 | keysPressed.unshift(event["keycode"]);
166 | // Send the message with the new array to the renderer process
167 | window.webContents.send("keydownEvent", keysPressed);
168 | });
169 |
170 | // Register and start hook
171 | ioHook.start();
172 |
173 | // Alternatively, pass true to start in DEBUG mode.
174 | ioHook.start(true);
175 |
176 | // False to disable DEBUG. Cleaner terminal output.
177 | // ioHook.start(false)
178 | });
179 |
--------------------------------------------------------------------------------
/app/keyboard.css:
--------------------------------------------------------------------------------
1 | * {
2 | margin: 0;
3 | padding: 0;
4 | }
5 |
6 | #keyboard {
7 | z-index: 20;
8 | margin: 20px auto 40px;
9 | width: 800px;
10 | height: 315px;
11 | background: #bbb;
12 | background-image: -webkit-gradient(linear,
13 | left bottom,
14 | left top,
15 | color-stop(0.27, rgb(212, 216, 219)),
16 | color-stop(0.64, rgb(213, 217, 220)),
17 | color-stop(0.95, rgb(230, 233, 235)),
18 | color-stop(1, rgb(191, 191, 191)));
19 | background-image: -moz-linear-gradient(center bottom,
20 | rgb(212, 216, 219) 27%,
21 | rgb(213, 217, 220) 64%,
22 | rgb(230, 233, 235) 95%,
23 | rgb(191, 191, 191) 100%);
24 | -moz-border-radius-topleft: 7px 21px;
25 | -moz-border-radius-topright: 7px 21px;
26 | -moz-border-radius-bottomright: 10px;
27 | -moz-border-radius-bottomleft: 10px;
28 | border-top-left-radius: 7px 21px;
29 | border-top-right-radius: 7px 21px;
30 | border-bottom-right-radius: 10px;
31 | border-bottom-left-radius: 10px;
32 | padding: 50px 0 0;
33 | }
34 |
35 | ul {
36 | list-style-type: none;
37 | width: 784px;
38 | margin: 0 auto;
39 | }
40 |
41 | li {
42 | float: left;
43 | }
44 |
45 | .key {
46 | display: block;
47 | color: #aaa;
48 | font: bold 9pt arial;
49 | text-decoration: none;
50 | text-align: center;
51 | width: 44px;
52 | height: 41px;
53 | margin: 5px;
54 | background: #eff0f2;
55 | -moz-border-radius: 4px;
56 | border-radius: 4px;
57 | border-top: 1px solid #696969;
58 | box-shadow:
59 | 0 1px 0 #c3c3c3,
60 | 0 2px 0 #c9c9c9,
61 | 0 2px 3px #333;
62 | text-shadow: 0px 1px 0px #f5f5f5;
63 | filter: dropshadow(color=#f5f5f5, offx=0, offy=1);
64 | }
65 |
66 | .key:active,
67 | .keydown {
68 | color: #aaa;
69 | background: #ebeced;
70 | margin: 7px 5px 3px;
71 | box-shadow:
72 | 0 0 3px #333;
73 | border-top: 1px solid #eee;
74 | }
75 |
76 | .fn span {
77 | display: block;
78 | margin: 14px 5px 0 0;
79 | text-align: right;
80 | font: bold 6pt arial;
81 | text-transform: uppercase;
82 | }
83 |
84 | #numbers li a span {
85 | display: block;
86 | }
87 |
88 | #numbers li a b {
89 | margin: 3px 0 3px;
90 | display: block;
91 | }
92 |
93 | #numbers li .alt b {
94 | display: block;
95 | margin: 0 0 3px;
96 | }
97 |
98 | #numbers li #Backspace span {
99 | text-align: right;
100 | margin: 23px 10px 0 0;
101 | font-size: 7.5pt;
102 | text-transform: lowercase;
103 | }
104 |
105 | #qwerty li a span {
106 | display: block;
107 | margin: 13px 0 0;
108 | text-transform: uppercase;
109 | }
110 |
111 | #qwerty li #Tab span {
112 | text-align: left;
113 | margin: 23px 0 0 10px;
114 | font-size: 7.5pt;
115 | text-transform: lowercase;
116 | }
117 |
118 | #qwerty li .alt b {
119 | display: block;
120 | margin: 3px 0 0;
121 | }
122 |
123 | #qwerty li .alt span {
124 | margin: 2px 0 0;
125 | }
126 |
127 |
128 | #asdfg li a span {
129 | display: block;
130 | margin: 13px 0 0;
131 | text-transform: uppercase;
132 | }
133 |
134 | #asdfg li .alt span {
135 | margin: 0;
136 | text-transform: lowercase;
137 | }
138 |
139 | #asdfg li .alt b {
140 | display: block;
141 | margin: 3px 0 0;
142 | }
143 |
144 | #asdfg li #CapsLock b {
145 | display: block;
146 | background: #999;
147 | width: 4px;
148 | height: 4px;
149 | border-radius: 10px;
150 | margin: 9px 0 0 10px;
151 | -webkit-box-shadow: inset 0 1px 0 #666;
152 | -moz-box-shadow: inset 0 1px 0 #666;
153 | box-shadow: inset 0 1px 0 #666;
154 | }
155 |
156 | #asdfg li #CapsLock span {
157 | text-align: left;
158 | margin: 10px 0 0 10px;
159 | font-size: 7.5pt;
160 | }
161 |
162 | #asdfg li #Enter span {
163 | text-align: right;
164 | margin: 23px 10px 0 0;
165 | font-size: 7.5pt;
166 | }
167 |
168 |
169 | #zxcvb li a span {
170 | display: block;
171 | margin: 13px 0 0;
172 | text-transform: uppercase;
173 | }
174 |
175 | #zxcvb li .shiftleft span {
176 | text-align: left;
177 | margin: 23px 0 0 10px;
178 | font-size: 7.5pt;
179 | text-transform: lowercase;
180 | }
181 |
182 | #zxcvb li .shiftright span {
183 | text-align: right;
184 | margin: 23px 10px 0 0;
185 | font-size: 7.5pt;
186 | text-transform: lowercase;
187 | }
188 |
189 | #zxcvb li .alt b {
190 | display: block;
191 | margin: 4px 0 0;
192 | }
193 |
194 | #zxcvb li .alt span {
195 | margin: 0;
196 | }
197 |
198 | #bottomrow li #fn span,
199 | #bottomrow li #LeftControl span,
200 | #bottomrow li #LeftAlt span,
201 | #bottomrow li #LeftCommand span {
202 | display: block;
203 | text-align: left;
204 | margin: 31px 0 0 8px;
205 | font-size: 7.5pt;
206 | text-transform: lowercase;
207 | }
208 |
209 | #bottomrow li #RightAlt span,
210 | #bottomrow li #RightCommand span {
211 | display: block;
212 | text-align: right;
213 | margin: 31px 8px 0 0;
214 | font-size: 7.5pt;
215 | text-transform: lowercase;
216 | }
217 |
218 | #bottomrow ol li #LeftArrow span,
219 | #bottomrow ol li #RightArrow span,
220 | #bottomrow ol li #UpArrow span,
221 | #bottomrow ol li #DownArrow span {
222 | display: block;
223 | margin: 9px 0 0;
224 | }
225 |
226 | .fn {
227 | height: 26px;
228 | width: 46px;
229 | }
230 |
231 | #Backspace {
232 | width: 72px;
233 | }
234 |
235 | #Tab {
236 | width: 72px;
237 | }
238 |
239 | #CapsLock {
240 | width: 85px;
241 | }
242 |
243 | #Enter {
244 | width: 85px;
245 | }
246 |
247 | .shiftleft,
248 | .shiftright {
249 | width: 112px;
250 | }
251 |
252 | #fn,
253 | #LeftControl,
254 | .option,
255 | .command,
256 | #Space {
257 | height: 49px;
258 | }
259 |
260 | #LeftControl {
261 | width: 56px;
262 | }
263 |
264 | .option {
265 | width: 46px;
266 | }
267 |
268 | .command {
269 | width: 67px;
270 | }
271 |
272 | #Space {
273 | width: 226px;
274 | }
275 |
276 | #LeftArrow img,
277 | #UpArrow img,
278 | #DownArrow img,
279 | #RightArrow img {
280 | border: none;
281 | }
282 |
283 | ul ol {
284 | list-style-type: none;
285 | }
286 |
287 | #DownArrow {
288 | height: 23px;
289 | font-size: 8px;
290 | }
291 |
292 | #UpArrow,
293 | #LeftArrow,
294 | #RightArrow {
295 | height: 24px;
296 | font-size: 8px;
297 | }
298 |
299 | #LeftArrow,
300 | #RightArrow {
301 | margin: 30px 5px 5px;
302 | }
303 |
304 | #LeftArrow:active,
305 | #RightArrow:active {
306 | margin: 32px 5px 3px;
307 | }
308 |
309 | #UpArrow {
310 | margin: 5px 5px 1px;
311 | border-bottom-right-radius: 0px;
312 | border-bottom-left-radius: 0px;
313 | }
314 |
315 | #UpArrow:active {
316 | margin: 8px 5px -2px;
317 | }
318 |
319 | #DownArrow {
320 | margin: 0 5px 5px;
321 | border-top-left-radius: 0px;
322 | border-top-right-radius: 0px;
323 | }
324 |
325 | #DownArrow:Active {
326 | margin: 3px 5px 4px;
327 | }
328 |
329 | #timesClickedKeyText {
330 | color: #333;
331 | }
332 |
333 | .disabledButton {
334 | background-color: #a4a4a4;
335 | text-decoration: line-through;
336 | color: rgb(81, 81, 81);
337 | box-shadow: none;
338 | }
339 |
340 | .disabledButton:active {
341 | background-color: #a4a4a4;
342 | text-decoration: line-through;
343 | color: rgb(81, 81, 81);
344 | box-shadow: none;
345 | }
--------------------------------------------------------------------------------
/test/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Document
9 |
10 |
11 |
12 |
13 |
14 |
15 |