project files added
This commit is contained in:
+53
@@ -0,0 +1,53 @@
|
||||
/* the keyboard we used for Lexus */
|
||||
.keyboard-container {
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
|
||||
background: #000000;
|
||||
color: #FFFFFF;
|
||||
font-family: sans-serif;
|
||||
font-size: 26px;
|
||||
padding: 56px 24px;
|
||||
box-sizing: border-box;
|
||||
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.keyboard-container-hidden {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.keyboard-row {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.keyboard-key {
|
||||
width: 5vh;
|
||||
height: 5vh;
|
||||
line-height: 5hv;
|
||||
text-align: center;
|
||||
margin: 4px;
|
||||
display: inline-block;
|
||||
background: #636363;
|
||||
transition: background 100ms ease;
|
||||
}
|
||||
.keyboard-key.active {
|
||||
background: #777777;
|
||||
}
|
||||
.keyboard-close-button {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
padding: 24px 24px;
|
||||
box-sizing: border-box;
|
||||
text-align: right;
|
||||
color: #676767;
|
||||
line-height: 1;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// symbol: The character or word to be inserted to
|
||||
Keyboard.layout.number = [
|
||||
[
|
||||
{"symbol": "7"},
|
||||
{"symbol": "8"},
|
||||
{"symbol": "9"},
|
||||
{"label": "\u21E6", "symbol": "backspace"}
|
||||
],
|
||||
[
|
||||
{"label": "+/-", "symbol": "flipSign"},
|
||||
{"symbol": "4"},
|
||||
{"symbol": "5"},
|
||||
{"symbol": "6"}
|
||||
],
|
||||
[
|
||||
{"symbol": "0"},
|
||||
{"symbol": "1"},
|
||||
{"symbol": "2"},
|
||||
{"symbol": "3"}
|
||||
]
|
||||
];
|
||||
+363
@@ -0,0 +1,363 @@
|
||||
if( !exports ) var exports = {};
|
||||
(function(exports, global){
|
||||
"use strict";
|
||||
|
||||
function splice(str, index, count, add) {
|
||||
return str.slice(0, index) + (add || "") + str.slice(index + count);
|
||||
}
|
||||
|
||||
function checkSelectionSupport(type){
|
||||
if (["text", "search", "URL", "tel", "password"].indexOf(type) == -1 ) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
function dispatchKeyboardOpenEvent(){
|
||||
document.dispatchEvent(new CustomEvent('keyboard-open'));
|
||||
}
|
||||
|
||||
function dispatchKeyboardCloseEvent(){
|
||||
document.dispatchEvent(new CustomEvent('keyboard-close'));
|
||||
}
|
||||
|
||||
function Element(Keyboard, el){
|
||||
|
||||
this.Keyboard = Keyboard;
|
||||
|
||||
el.layout = global.Keyboard.layout[el.type] ? el.type : '_default';
|
||||
|
||||
function dispatchEvent(event, keyInfo){
|
||||
|
||||
var event = new KeyboardEvent(event,{
|
||||
key: keyInfo.symbol,
|
||||
code: 'Key' + keyInfo.symbol,
|
||||
keyCode : keyInfo.symbol.charCodeAt(0),
|
||||
which : keyInfo.symbol.charCodeAt(0),
|
||||
altKey : false,
|
||||
ctrlKey : false,
|
||||
shiftKey : false,
|
||||
metaKey : false
|
||||
});
|
||||
event.virtual = true;
|
||||
el.dispatchEvent(event);
|
||||
}
|
||||
|
||||
this.onEvent = function(keyInfo){
|
||||
dispatchEvent('keydown', keyInfo);
|
||||
dispatchEvent('keypress', keyInfo);
|
||||
dispatchEvent('keyup', keyInfo);
|
||||
dispatchEvent('input', keyInfo);
|
||||
dispatchEvent('change', keyInfo);
|
||||
};
|
||||
|
||||
this.keydownfunc = function(e){
|
||||
|
||||
// If keydown is authentic, skip our internal update and let the default play out
|
||||
if (!e.virtual) return;
|
||||
e.preventDefault();
|
||||
|
||||
var selectionSupported = checkSelectionSupport(this.type);
|
||||
|
||||
if (e.key == "backspace") {
|
||||
if ( !selectionSupported ) {
|
||||
this.value = this.value.slice(0, -1);
|
||||
} else {
|
||||
var pos = el.selectionStart;
|
||||
var offset = el.selectionEnd-el.selectionStart? 0:1;
|
||||
this.value = this.value.substring(0, el.selectionStart-offset) + this.value.slice(el.selectionEnd);
|
||||
this.setSelectionRange(pos-offset, pos-offset);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Toggle will change the layout to a different one that is pre-determined
|
||||
// An empty toggle key will toggle the keyboard back to default
|
||||
if (e.key.substring(0, 6) == "toggle") {
|
||||
var newType = e.key.substring(6);
|
||||
|
||||
if(newType.length >= 0 && global.Keyboard.layout[newType]) {
|
||||
return this.Keyboard.Keyboard.show(newType);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if( this.value.length >= this.maxLength && this.maxLength != -1 ){
|
||||
return;
|
||||
}
|
||||
var update = e.key || String.fromCharCode(e.keyCode);
|
||||
|
||||
// Support an input case that will capitalize letters as needed
|
||||
if(( e.target.dataset.case === 'sentence' && (this.value.length === 0 || this.value.slice(-1) === "." || this.value.slice(-2) === ". " ))
|
||||
|| ( e.target.dataset.case === 'capitalize' && this.value.slice(-1) === " " ))
|
||||
{
|
||||
update = update.toUpperCase();
|
||||
}
|
||||
|
||||
// TODO Mimic selection for input elements that don't support selection api as well
|
||||
// IDEA: selection-polyfill? get caret's pixel location instead?
|
||||
if ( !selectionSupported ) {
|
||||
this.value += update;
|
||||
} else {
|
||||
var pos = el.selectionStart + update.length;
|
||||
this.value = splice(
|
||||
this.value,
|
||||
el.selectionStart,
|
||||
el.selectionEnd-el.selectionStart,
|
||||
update
|
||||
);
|
||||
this.setSelectionRange(pos, pos); // reset the position after the splice
|
||||
}
|
||||
// TODO Calculate scroll amount based on caret position
|
||||
this.scrollLeft = this.scrollWidth;
|
||||
};
|
||||
|
||||
el.addEventListener('focus', this.focus.bind(this));
|
||||
el.addEventListener('blur', this.blur.bind(this));
|
||||
el.addEventListener('keydown', this.keydownfunc);
|
||||
}
|
||||
|
||||
Element.prototype.focus = function(e){
|
||||
this.Keyboard.show(e.target.layout);
|
||||
this.Keyboard.on('key', this.onEvent);
|
||||
};
|
||||
Element.prototype.blur = function(e){
|
||||
this.Keyboard.hide(e.target.layout);
|
||||
this.Keyboard.off('key', this.onEvent);
|
||||
};
|
||||
|
||||
function Keyboard(inputs, holder){
|
||||
|
||||
var self = this;
|
||||
|
||||
Array.prototype.slice.call(inputs, 0).forEach(function(input){
|
||||
input.Keyboard = new Element(self, input);
|
||||
});
|
||||
|
||||
this.active = false;
|
||||
this.listeners = {key:[]};
|
||||
this.keyboardEl = null;
|
||||
this.layout = null;
|
||||
this.keyboardEl = document.createElement("div");
|
||||
this.keyboardEl.classList.add("keyboard-container");
|
||||
this.keyboardEl.addEventListener("mousedown", handleKeydownEvents.bind(this));
|
||||
this.keyboardEl.addEventListener("mouseup", handleKeyupEvents.bind(this));
|
||||
// TODO - find a way to enable this touchstart event again.
|
||||
// It prevents the :active state from being triggered on keys
|
||||
this.keyboardEl.addEventListener("touchstart", handleKeydownEvents.bind(this));
|
||||
this.keyboardEl.addEventListener("touchend", handleKeyupEvents.bind(this));
|
||||
|
||||
// Generate keyboard HTML, bind events, insert them to given element
|
||||
this.show = function (layout) {
|
||||
// Clear any timers relating to keyhold
|
||||
this.clearKeyHoldTimers();
|
||||
|
||||
if (!global.Keyboard.layout[layout]) throw new Error("keyboard initiation: Missing layout: " + layout);
|
||||
if( self.layout && layout === self.layout && this.active){
|
||||
return;
|
||||
}
|
||||
this.active = true;
|
||||
|
||||
self.layout = layout;
|
||||
this.keyboardEl.innerHTML = "";
|
||||
this.keyboardEl.classList.remove('keyboard-container-hidden');
|
||||
|
||||
var closeButton = document.createElement("span");
|
||||
closeButton.classList.add('keyboard-close-button');
|
||||
closeButton.innerHTML = '✖';
|
||||
this.keyboardEl.appendChild(closeButton);
|
||||
closeButton.onclick = function() {
|
||||
document.activeElement.blur()
|
||||
}.bind(this);
|
||||
closeButton.ontouchstart = function() {
|
||||
document.activeElement.blur()
|
||||
}.bind(this);
|
||||
|
||||
function foreachLayout(row, rowIndex, layout) {
|
||||
|
||||
var rowEl = document.createElement("div");
|
||||
rowEl.classList.add("keyboard-row");
|
||||
rowEl.classList.add("keyboard-row--" + rowIndex);
|
||||
|
||||
function foreachRow(key, keyIndex, row) {
|
||||
var keyEl = document.createElement("div");
|
||||
keyEl.classList.add("keyboard-key");
|
||||
keyEl.classList.add("keyboard-key--" + keyIndex);
|
||||
// Parse the layout configuration
|
||||
for (var dataName in key) {
|
||||
switch (dataName) {
|
||||
case "symbol":
|
||||
if (!key.label) keyEl.innerHTML = key[dataName];
|
||||
keyEl.dataset.symbol = key[dataName];
|
||||
break;
|
||||
case "label":
|
||||
keyEl.innerHTML = key[dataName];
|
||||
break;
|
||||
default:
|
||||
keyEl.dataset[dataName] = key[dataName];
|
||||
}
|
||||
}
|
||||
rowEl.appendChild(keyEl);
|
||||
}
|
||||
row.forEach(foreachRow);
|
||||
self.keyboardEl.appendChild(rowEl);
|
||||
}
|
||||
|
||||
global.Keyboard.layout[self.layout].forEach(foreachLayout);
|
||||
// Append keys to el
|
||||
|
||||
// Send an event signifying open of keyboard
|
||||
dispatchKeyboardOpenEvent()
|
||||
holder.appendChild(self.keyboardEl);
|
||||
};
|
||||
|
||||
this.hide = function(){
|
||||
self.active = false;
|
||||
// Clear any timers relating to keyhold to cleanup
|
||||
this.clearKeyHoldTimers();
|
||||
// Send an event signifying close of keyboard
|
||||
dispatchKeyboardCloseEvent()
|
||||
setTimeout(function(){
|
||||
if( self.active ) return;
|
||||
self.keyboardEl.classList.add("keyboard-container-hidden");
|
||||
self.keyboardEl.innerHTML = "";
|
||||
},25);
|
||||
}
|
||||
|
||||
this.on = function(evt, action){
|
||||
if( !this.listeners[evt] ){
|
||||
this.listeners[evt] = [];
|
||||
}
|
||||
this.listeners[evt].push(action);
|
||||
};
|
||||
this.off = function(evt, action){
|
||||
if( !this.listeners[evt] ) return;
|
||||
this.listeners[evt] = this.listeners[evt].filter(function(listener){
|
||||
return action.toString() !== listener.toString();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Add input(s) after the fact
|
||||
* @param inputs
|
||||
*/
|
||||
this.add = function(inputs){
|
||||
|
||||
if(!Array.isArray(inputs))
|
||||
inputs = [inputs];
|
||||
|
||||
Array.prototype.slice.call(inputs, 0).forEach(function(input){
|
||||
input.Keyboard = new Element(self, input);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Clear timer events bound to the keyboard
|
||||
*/
|
||||
this.clearKeyHoldTimers = function () {
|
||||
// Clear timeout to make sure multiple keypress does not start
|
||||
if(this.keyHoldTimeout) {
|
||||
window.clearTimeout(this.keyHoldTimeout);
|
||||
}
|
||||
// Clear interval to make sure multiple keypress does not continue
|
||||
if(this.keyHoldInterval) {
|
||||
window.clearInterval(this.keyHoldInterval);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
// We would like to pipe all keyboard events through one handler
|
||||
var handleKeydownEvents = function (e) {
|
||||
var self = this;
|
||||
e.preventDefault();
|
||||
// Check to make sure it's a key that's pressed
|
||||
if (!e.target.classList.contains("keyboard-key")) return;
|
||||
|
||||
// the :active pseudo class is not working with touch events
|
||||
// use active class to get around this issue
|
||||
e.target.classList.add('active');
|
||||
|
||||
var keyInfo = e.target.dataset;
|
||||
|
||||
self.listeners['key'].forEach(function(action){
|
||||
action(keyInfo);
|
||||
});
|
||||
|
||||
// Allow for a key to be input multiple times by holding it down
|
||||
// Ensure that any current timers are cleared before continuing
|
||||
self.clearKeyHoldTimers();
|
||||
// Timeout will provide a delay to prevent accidental holding
|
||||
// After that, the interval will provide repeated input
|
||||
self.keyHoldTimeout = window.setTimeout(function() {
|
||||
self.keyHoldInterval = window.setInterval(function() {
|
||||
self.listeners['key'].forEach(function(action){
|
||||
action(keyInfo);
|
||||
});
|
||||
}, 100)
|
||||
}, 400)
|
||||
};
|
||||
|
||||
var handleKeyupEvents = function (e) {
|
||||
e.target.classList.remove('active');
|
||||
|
||||
// Clear timeout to make sure multiple keypress does not start
|
||||
this.clearKeyHoldTimers();
|
||||
}
|
||||
|
||||
global.Keyboard = Keyboard;
|
||||
global.Keyboard.layout = {
|
||||
_default: [
|
||||
[
|
||||
{"symbol": "Q"},
|
||||
{"symbol": "W"},
|
||||
{"symbol": "E"},
|
||||
{"symbol": "R"},
|
||||
{"symbol": "T"},
|
||||
{"symbol": "Y"},
|
||||
{"symbol": "U"},
|
||||
{"symbol": "I"},
|
||||
{"symbol": "O"},
|
||||
{"symbol": "P"},
|
||||
{"label": ".com", "symbol": ".COM"},
|
||||
{"symbol": "7"},
|
||||
{"symbol": "8"},
|
||||
{"symbol": "9"},
|
||||
{"label": "\u21E6", "symbol":"backspace"}
|
||||
],
|
||||
[
|
||||
// {"label": "tab", "func": "tab"},
|
||||
{"symbol": "A"},
|
||||
{"symbol": "S"},
|
||||
{"symbol": "D"},
|
||||
{"symbol": "F"},
|
||||
{"symbol": "G"},
|
||||
{"symbol": "H"},
|
||||
{"symbol": "J"},
|
||||
{"symbol": "K"},
|
||||
{"symbol": "L"},
|
||||
{"symbol": "@"},
|
||||
{"label": ".net", "symbol": ".NET"},
|
||||
{"symbol": "4"},
|
||||
{"symbol": "5"},
|
||||
{"symbol": "6"}
|
||||
],
|
||||
[
|
||||
{"symbol": "Z"},
|
||||
{"symbol": "X"},
|
||||
{"symbol": "C"},
|
||||
{"symbol": "V"},
|
||||
{"symbol": "B"},
|
||||
{"symbol": "N"},
|
||||
{"symbol": "M"},
|
||||
{"symbol": "."},
|
||||
{"symbol": "_"},
|
||||
{"symbol": "-"},
|
||||
{"symbol": " ", "label": "space"},
|
||||
{"symbol": "0"},
|
||||
{"symbol": "1"},
|
||||
{"symbol": "2"},
|
||||
{"symbol": "3"}
|
||||
]
|
||||
]
|
||||
};
|
||||
})(exports || {}, window);
|
||||
Reference in New Issue
Block a user