Created
January 9, 2013 15:07
-
-
Save OlavHN/4493809 to your computer and use it in GitHub Desktop.
Files:
apps/keyboard/style/keyboard.css
apps/keyboard/js/render.js 1. Open Contacts
2. Click Mobile -> Phone
3. Hold down button with alt key I expected it to be a highlighted alternative, but it turns out to render the key above the highlighting or something similar.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Media definitions */ | |
@media screen and (max--moz-device-pixel-ratio: 1.5) { | |
html { | |
font-size: 15px; | |
} | |
} | |
@font-face { | |
font-family: 'Keyboard Symbols'; | |
font-weight: 600; | |
src: url('fonts/Keyboard-Symbols.woff'); | |
} | |
/* Reset */ | |
* { | |
margin: 0; | |
padding: 0; | |
} | |
html { | |
pointer-events: none; | |
} | |
body { | |
overflow: hidden; | |
} | |
button::-moz-focus-inner { | |
padding: 0; | |
border: 0 | |
} | |
.cache { | |
position: absolute; | |
left: 0; | |
bottom: 0; | |
z-index: -1; | |
opacity: 0; | |
overflow: hidden; | |
width: 0; | |
height: 0; | |
display: none; | |
} | |
/* Keyboard */ | |
#keyboard { | |
position: absolute; | |
bottom: 0; | |
z-index: 15; | |
width: 100%; | |
background: #6a6f73; | |
border-top: solid 1px #000; | |
pointer-events: auto; | |
} | |
/* Rows */ | |
.keyboard-row { | |
width: 100%; | |
margin-bottom: 0.5rem; | |
white-space: nowrap; | |
} | |
.keyboard-row.first-row { | |
padding-top: 0.5rem; | |
border-top: solid 1px #a6a9ab; | |
} | |
/* Buttons: logic keys */ | |
.keyboard-key { | |
-moz-box-sizing: border-box; | |
padding: 0 0.1rem; | |
border: none; | |
background: url(images/key-shadow.png) no-repeat center -1px; | |
background-size: 100% 100%; | |
min-width: 2.6rem; | |
display: inline-block; | |
} | |
/* Visible keys */ | |
.keyboard-key > .visual-wrapper { | |
display: inline-block; | |
height: 4rem; | |
width: 100%; | |
-moz-box-sizing: border-box; | |
position: relative; | |
border: solid 1px #000; | |
background: #252220 url(images/key-bg.png) no-repeat center bottom; | |
background-size: 100% 100%; | |
} | |
/* Standard key styles. */ | |
.keyboard-key > .visual-wrapper > span { | |
/* | |
top - begin | top - end | |
bottom - begin | bottom - end | |
*/ | |
background: | |
url(images/key-radius-tb.png) no-repeat left top, | |
url(images/key-radius-te.png) no-repeat right top, | |
url(images/key-radius-bb.png) no-repeat left bottom, | |
url(images/key-radius-be.png) no-repeat right bottom, | |
url(images/key-light.png) repeat-x left 1px; | |
position: absolute; | |
top: -1px; | |
bottom: -1px; | |
left: -1px; | |
right: -1px; | |
font: 2.4rem/3.5rem MozTT, Sans-serif; | |
font-weight: 500; | |
text-align: center; | |
color: #fff; | |
} | |
/* Highlights */ | |
.keyboard-key:not(.special-key).highlighted { | |
position: relative; | |
z-index: 100; | |
} | |
.keyboard-key:not(.special-key).highlighted > .visual-wrapper { | |
background: #6ec5d5; | |
border-radius: 0 0 0.3rem 0.3rem; | |
border: solid 1px #8BD1DD; | |
border-top: none; | |
margin: -0.5rem 0 0; | |
height: 4.3rem; | |
} | |
.keyboard-key:not(.special-key).highlighted > .visual-wrapper:after { | |
content: ""; | |
position: absolute; | |
left: -0.5rem; | |
top: 0.5rem; | |
bottom: -0.5rem; | |
right: -0.5rem; | |
z-index: -1; | |
background: url(images/dark-alpha.png) repeat left top; | |
border-radius: 0.6rem; | |
} | |
.keyboard-key:not(.special-key).highlighted:last-child > .visual-wrapper:after { | |
right: -0.2rem; | |
border-radius: 0.6rem 0 0 0.6rem; | |
} | |
.keyboard-key:not(.special-key).highlighted > .visual-wrapper:before { | |
content: ""; | |
position: absolute; | |
top: 0; | |
left: 0; | |
right: 0; | |
height: 0.5rem; | |
background: #6ec5d5; | |
z-index: 1; | |
} | |
.keyboard-key:not(.special-key).highlighted > .visual-wrapper > span { | |
font: 3.8rem/4.5rem MozTT, Sans-serif; | |
font-weight: 500; | |
background: #6ec5d5; | |
color: #1a3f46; | |
border: solid 1px #8BD1DD; | |
border-radius: 0.3rem; | |
height: 5.2rem; | |
top: -5.3rem; | |
left: -0.8rem; | |
right: -0.8rem; | |
} | |
.keyboard-key:not(.special-key).highlighted > .visual-wrapper > span:after { | |
content: ""; | |
position: absolute; | |
left: -0.5rem; | |
top: -0.5rem; | |
bottom: -0.5rem; | |
right: -0.5rem; | |
z-index: -1; | |
background: url(images/dark-alpha.png) repeat left top; | |
border-radius: 0.6rem; | |
} | |
.keyboard-key:not(.special-key).highlighted:last-child > .visual-wrapper > span:after { | |
right: -0.2rem; | |
border-radius: 0.6rem 0 0 0.6rem; | |
} | |
.keyboard-key:not(.special-key).highlighted:last-child > .visual-wrapper > span { | |
right: -1px; | |
border-bottom-right-radius: 0; | |
} | |
.keyboard-key:not(.special-key).highlighted:last-child #keyboard-accent-char-menu { | |
right: -1px; | |
} | |
.keyboard-key:not(.special-key).highlighted:first-child > .visual-wrapper > span { | |
left: -1px; | |
border-bottom-left-radius: 0; | |
} | |
.keyboard-key:not(.special-key).highlighted:first-child #keyboard-accent-char-menu { | |
left: -1px; | |
} | |
/* Special keys */ | |
.keyboard-key.special-key > .visual-wrapper { | |
border-color: #2a2c2e; | |
background: #43474a; | |
} | |
.keyboard-key.special-key > .visual-wrapper > span { | |
/* | |
top - begin | top - end | |
bottom - begin | bottom - end | |
*/ | |
background: | |
url(images/key-radius-special-tb.png) no-repeat left top, | |
url(images/key-radius-special-te.png) no-repeat right top, | |
url(images/key-radius-special-bb.png) no-repeat left bottom, | |
url(images/key-radius-special-be.png) no-repeat right bottom, | |
url(images/key-light-special.png) repeat-x left 1px; | |
font-size: 1.5rem; | |
line-height: 4rem; | |
font-family: 'Keyboard Symbols', sans-serif; | |
color: #ccc; | |
} | |
/* Highlight for special keys */ | |
.keyboard-key.special-key.highlighted > .visual-wrapper { | |
background: #4AB6CB; | |
border-radius: 0.3rem; | |
border: solid 1px #3f5459; | |
} | |
.keyboard-key.special-key.highlighted > .visual-wrapper > span { | |
background: none; | |
color: #1e4a52; | |
} | |
.keyboard-key.special-key.highlighted > .visual-wrapper:before { | |
content: ""; | |
position: absolute; | |
left: -0.2rem; | |
right: -0.2rem; | |
top: -0.2rem; | |
bottom: -0.25rem; | |
z-index: -1; | |
background: #4495A8; | |
border-radius: 0.3rem; | |
} | |
/*Olav*/ | |
.keyboard-key.highlighted-big-key > .visual-wrapper { | |
background: #4AB6CB; | |
border-radius: 0.3rem; | |
border: solid 1px #3f5459; | |
} | |
.keyboard-key.highlighted-big-key > .visual-wrapper > span { | |
background: none; | |
color: #1e4a52; | |
} | |
.keyboard-key.highlighted-big-key > .visual-wrapper:before { | |
content: ""; | |
position: absolute; | |
left: -0.2rem; | |
right: -0.2rem; | |
top: -0.2rem; | |
bottom: -0.25rem; | |
z-index: -1; | |
background: #4495A8; | |
border-radius: 0.3rem; | |
} | |
/*Olav end*/ | |
/* Spacebar exceptions */ | |
.keyboard-key[data-keycode="32"] > .visual-wrapper { | |
background-position: center 2.2rem; | |
} | |
.keyboard-key.highlighted[data-keycode="32"] > .visual-wrapper { | |
border: solid 1px #000; | |
border-radius: 0.3rem; | |
background: #252220 url(images/key-bg.png) no-repeat center 2.2rem; | |
background-size: 100% 100%; | |
opacity: 0.5; | |
margin: 0; | |
height: 4rem; | |
} | |
.keyboard-key.highlighted[data-keycode="32"] > .visual-wrapper > span { | |
border: none; | |
border-top: solid 1px #514e4d; | |
background: none; | |
position: absolute; | |
top: 0; | |
left: 0; | |
right: 0; | |
bottom: 0; | |
z-index: 1; | |
height: 100%; | |
} | |
.keyboard-key.highlighted[data-keycode="32"] > .visual-wrapper:after, | |
.keyboard-key.highlighted[data-keycode="32"] > .visual-wrapper:before, | |
.keyboard-key.highlighted[data-keycode="32"] > .visual-wrapper > span:after { | |
display: none; | |
} | |
/* Key states */ | |
/* Active */ | |
.keyboard-key.kbr-key-active > .visual-wrapper > span { | |
color: #4495A8; | |
} | |
/* Hold */ | |
.keyboard-key.kbr-key-hold> .visual-wrapper { | |
background: #4AB6CB; | |
border-radius: 0.3rem; | |
border: solid 1px #3f5459; | |
} | |
.keyboard-key.kbr-key-hold> .visual-wrapper > span { | |
background: none; | |
color: #1e4a52; | |
} | |
/* Alt menu enabled */ | |
.keyboard-key.highlighted.kbr-menu-on > .visual-wrapper { | |
background: #6EC5D5; | |
border: 1px solid #92D3E0; | |
border-top: none; | |
height: 4.5rem; | |
} | |
.keyboard-key.highlighted.kbr-menu-on > .visual-wrapper > span:after { | |
display: none; | |
} | |
.keyboard-key.highlighted.kbr-menu-on > .visual-wrapper:before { | |
background: #6EC5D5; | |
} | |
.keyboard-key.highlighted.kbr-menu-on > .visual-wrapper > span { | |
background: transparent; | |
border-color: transparent; | |
} | |
/* Alternatives menu -- this menu appears when you tap and hold a key that has | |
alternative special (accented) characters. The menu "grows" out of the key | |
and displays the list of alternatives. */ | |
#keyboard-accent-char-menu { | |
position: absolute; | |
bottom: 0; | |
left: 0.6rem; | |
margin-bottom: -1px; | |
height: 5.8rem; | |
border-radius: 0.3rem; | |
background: #333; | |
border: 1px solid #000; | |
white-space: nowrap; | |
overflow: visible; | |
display: none; | |
} | |
#keyboard-accent-char-menu:after { | |
content: ""; | |
position: absolute; | |
left: -0.5rem; | |
top: -0.5rem; | |
bottom: -0.6rem; | |
right: -0.5rem; | |
z-index: -1; | |
background: url(images/dark-alpha.png) repeat left top; | |
border-radius: 0.6rem 0.6rem 0.6rem 0; | |
} | |
.keyboard-key:first-child #keyboard-accent-char-menu { | |
border-radius: 0.3rem 0.3rem 0.3rem 0; | |
} | |
.keyboard-key:last-child #keyboard-accent-char-menu { | |
border-radius: 0.3rem 0.3rem 0 0.3rem; | |
} | |
#keyboard-accent-char-menu.show { | |
display: block; | |
} | |
/* Alternatives from right */ | |
#keyboard-accent-char-menu.kbr-menu-right { | |
left: auto; | |
right: 0.6rem; | |
} | |
/* Non keyboard key styles in menu */ | |
#keyboard-accent-char-menu .keyboard-key { | |
background: none; | |
height: 100%; | |
vertical-align: top; | |
top: -1px; | |
position: relative; | |
} | |
#keyboard-accent-char-menu .keyboard-key:first-child { | |
padding-left: 0; | |
} | |
#keyboard-accent-char-menu .keyboard-key.highlighted:first-child > .visual-wrapper { | |
left: -1px; | |
} | |
#keyboard-accent-char-menu .keyboard-key:last-child { | |
padding-right: 0; | |
} | |
#keyboard-accent-char-menu .keyboard-key.highlighted:last-child > .visual-wrapper { | |
border-right: none; | |
right: 1px; | |
} | |
#keyboard-accent-char-menu .keyboard-key > .visual-wrapper { | |
background: none; | |
border: none; | |
border-radius: 0; | |
margin: 0; | |
height: 5.8rem; | |
} | |
#keyboard-accent-char-menu .keyboard-key > .visual-wrapper span { | |
background: none; | |
} | |
#keyboard-accent-char-menu .keyboard-key.highlighted > .visual-wrapper:after, | |
#keyboard-accent-char-menu .keyboard-key.highlighted > .visual-wrapper:before, | |
#keyboard-accent-char-menu .keyboard-key.highlighted > .visual-wrapper > span:after { | |
display: none; | |
} | |
/* Styles for keys in accent menu (not highlighted). */ | |
#keyboard-accent-char-menu .keyboard-key > .visual-wrapper > span { | |
border: none; | |
font: 3rem/5rem MozTT, Sans-serif; | |
font-weight: 500; | |
color: #fff; | |
} | |
/* Highlighted non keboard key style */ | |
#keyboard-accent-char-menu .keyboard-key.highlighted > .visual-wrapper { | |
background: #6ec5d5; | |
border: solid 0.2rem #6ec5d5; | |
-moz-box-sizing: content-box; | |
border-top: solid 1px #8BD1DD; | |
border-bottom: solid 1px #6ec5d5; | |
} | |
/* Highlighted special accent characters. These keys appear in the popover | |
bubble above the key when you tap and hold. */ | |
#keyboard-accent-char-menu .keyboard-key.highlighted > .visual-wrapper > span { | |
top: 0; | |
left: 0; | |
right: 0; | |
z-index: 1; | |
height: 100%; | |
font-weight: normal; | |
color: #1a3f46; | |
background: none; | |
} | |
/* Language alternatives */ | |
#keyboard-accent-char-menu.kbr-menu-lang { | |
position: absolute; | |
bottom: 100%; | |
left: 0; | |
right: auto; | |
top: auto; | |
margin-bottom: -2px; | |
height: auto; | |
max-height: 34rem; | |
white-space: normal; | |
} | |
/* Special key menu enabled */ | |
.keyboard-key.special-key.highlighted.kbr-menu-on { | |
position: relative; | |
z-index: 100; | |
} | |
.keyboard-key.special-key.highlighted.kbr-menu-on > .visual-wrapper { | |
border-radius: 0 0 0.3rem 0.3rem; | |
margin-top: -0.5rem; | |
} | |
.keyboard-key.special-key.highlighted.kbr-menu-on > .visual-wrapper > span { | |
color: #fff; | |
line-height: 5.2rem; | |
border: none; | |
} | |
.keyboard-key.special-key.highlighted.kbr-menu-on > .visual-wrapper:after { | |
content: ""; | |
position: absolute; | |
left: -0.5rem; | |
top: 0.5rem; | |
bottom: -0.5rem; | |
right: -0.5rem; | |
z-index: -1; | |
background: url(images/dark-alpha.png) repeat left top; | |
border-radius: 0.6rem; | |
} | |
.keyboard-key.special-key.highlighted.kbr-menu-on > .visual-wrapper:before { | |
content: ""; | |
position: absolute; | |
top: 0; | |
left: 0; | |
right: 0; | |
height: 0.5rem; | |
z-index: 1; | |
} | |
#keyboard-accent-char-menu.kbr-menu-lang > .keyboard-key { | |
padding: 0; | |
display: block; | |
height: 4rem; | |
top: 0; | |
} | |
#keyboard-accent-char-menu.kbr-menu-lang > .keyboard-key > .visual-wrapper { | |
-moz-box-sizing: border-box; | |
border: none; | |
border-bottom: solid 1px #92D3E0; | |
width: 10rem; | |
height: 4rem; | |
} | |
#keyboard-accent-char-menu.kbr-menu-lang > .keyboard-key:first-child > .visual-wrapper { | |
border-radius: 0.3rem 0.3rem 0 0; | |
} | |
#keyboard-accent-char-menu.kbr-menu-lang > .keyboard-key.highlighted > .visual-wrapper { | |
left: 0; | |
right: 0; | |
} | |
#keyboard-accent-char-menu.kbr-menu-lang > .keyboard-key > .visual-wrapper > span { | |
font-family: 'MozTT', Sans-serif; | |
font-weight: 500; | |
font-size: 1.8rem; | |
line-height: 4rem; | |
} | |
#keyboard-accent-char-menu.kbr-menu-lang > .keyboard-key:last-child { | |
margin-bottom: 0.8rem; | |
} | |
#keyboard-accent-char-menu.kbr-menu-lang > .keyboard-key.kbr-key-hold > .visual-wrapper { | |
background: #333; | |
} | |
#keyboard-accent-char-menu.kbr-menu-lang > .keyboard-key.kbr-key-hold > .visual-wrapper > span { | |
color: #fff; | |
} | |
/* IMEs */ | |
#keyboard-pending-symbol-panel { | |
position: absolute; | |
top: -35px; | |
left: 0; | |
height: 32px; | |
line-height: 32px; | |
font-size: 24px; | |
width: auto; | |
padding: 0 8px; | |
background: rgba(245, 245, 245, 0.7); | |
color: rgb(36, 36, 36); | |
border-top-right-radius: 8px; | |
border-top: 1px solid #d5d5d5; | |
border-right: 1px solid #d5d5d5; | |
white-space: nowrap; | |
} | |
#keyboard-pending-symbol-panel:empty { | |
display: none; | |
} | |
#keyboard-candidate-panel { | |
height: 64px; | |
overflow: auto; | |
white-space: nowrap; | |
color: black; | |
background: rgb(176, 174, 168); | |
border-top: 1px solid #e8e8ff; | |
border-bottom: 1px solid #808098; | |
display: none; | |
margin: 0 -1rem 0 -1rem; | |
} | |
#keyboard.candidate-panel #keyboard-candidate-panel { | |
display: block; | |
width: -moz-calc(100% - 62px + 1rem); | |
overflow-y: hidden; | |
} | |
/* for latin suggestions we don't need such a tall box */ | |
/* and in latin we hide the toggle button, so we can be full-width */ | |
#keyboard.candidate-panel #keyboard-candidate-panel.latin { | |
height: 30px; | |
width: 100%; | |
background: none; | |
margin: 0; | |
border: none; | |
} | |
#keyboard.full-candidate-panel { | |
padding-top: 66px; | |
} | |
#keyboard.full-candidate-panel #keyboard-candidate-panel { | |
display: block; | |
position: absolute; | |
white-space: normal; | |
top: 0; | |
height: 100%; | |
width: 100%; | |
border: none; | |
border-top: 2px solid #a3a3a3; | |
z-index: 1; | |
margin:0; padding:0; | |
overflow-x: hidden; | |
overflow-y: auto; | |
} | |
#keyboard.full-candidate-panel #keyboard-candidate-panel span { | |
border-top: 1px solid #e8e8ff; | |
border-bottom: 1px solid #808098; | |
} | |
#keyboard-candidate-panel[data-truncated]::after { | |
content: '…'; | |
background: none ! important; | |
border-color: transparent ! important; | |
color: #666; | |
} | |
#keyboard-candidate-panel span, | |
#keyboard-candidate-panel[data-truncated]::after { | |
border-left: 1px solid #e8e8ff; | |
border-right: 1px solid #808098; | |
font-size: 32px; | |
line-height: 60px; | |
min-width: 60px; | |
display: inline-block; | |
height: 64px; | |
padding: 0 10px; | |
text-align: center; | |
background: -moz-linear-gradient(top, rgb(191,191,183) 10%, rgb(161,158,153) 90%); | |
} | |
#keyboard-candidate-panel.latin span, | |
#keyboard-candidate-panel[data-truncated].latin::after { | |
font-family: 'MozTT', sans-serif; | |
font-size: 10pt; | |
line-height: 24px; | |
height: 24px; | |
border: none; | |
border-radius: 4px; | |
margin: 3px; | |
} | |
#keyboard-candidate-panel-toggle-button { | |
border-left: 1px solid #e8e8ff; | |
border-right: 1px solid #808098; | |
border-bottom: 1px solid #808098; | |
position: absolute; | |
font-size: 32px; | |
line-height: 60px; | |
right: 0; | |
top: 1px; | |
text-align: center; | |
box-shadow: -4px 0 5px -5px black; | |
width: 60px; | |
height: 64px; | |
color: black; | |
background: -moz-linear-gradient(top, rgb(191,191,183) 10%, rgb(161,158,153) 90%); | |
display: none; | |
} | |
#keyboard.full-candidate-panel #keyboard-candidate-panel-toggle-button { | |
display: block; | |
border-top: 1px solid #e8e8ff; | |
top: 0; | |
z-index: 1; | |
} | |
#keyboard.candidate-panel #keyboard-candidate-panel-toggle-button { | |
display: block; | |
} | |
/* | |
* Don't display the toggle button for latin suggestions. | |
* For now, at least, we only ever get 3 at a time | |
*/ | |
#keyboard.candidate-panel #keyboard-candidate-panel-toggle-button.latin { | |
display: none; | |
} | |
#keyboard-candidate-panel span[data-active], | |
#keyboard-candidate-panel-toggle-button[data-active] { | |
background: -moz-linear-gradient(bottom, rgb(191,191,183) 10%, rgb(161,158,153) 90%); | |
} | |
/* Pending panel highlight */ | |
.keyboard-pending-symbols-highlight-green { | |
background: #33aa33; | |
} | |
.keyboard-pending-symbols-highlight-red { | |
background: #aa3333; | |
} | |
.keyboard-pending-symbols-highlight-blue { | |
background: #3333aa; | |
} | |
/* A note to show on the key to indicate the alternative char */ | |
.alt-note { | |
position: absolute; | |
top: 0.5rem; | |
right: 0.5rem; | |
color: #999; | |
font: 1.6rem/1.6rem "Keyboard Symbols", Sans-serif; | |
} | |
.keyboard-key.highlighted .alt-note { | |
visibility: hidden; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ | |
'use strict'; | |
// Render is in charge of draw and composite HTML elements under requestion | |
// of the IMEController. IMERender is able to read from the layout to improve | |
// its performance but is not allowed to communicate with the controller nor | |
// manager. | |
// | |
// XXX: The only thing worth to be remebered is the KEY element must be the | |
// deepest interactive HTML element on the hierarchy or, if none, simply the | |
// deepest element. This element must contain dataset-keycode and related | |
// attributes. | |
const IMERender = (function() { | |
var ime, menu, pendingSymbolPanel, candidatePanel, candidatePanelToggleButton; | |
var getUpperCaseValue, isSpecialKey; | |
var _menuKey, _altContainer; | |
var layoutWidth = 10; | |
var inputType; | |
var inputMethodName; // used as a CSS class on the candidatePanel | |
// Initiaze the render. It needs some business logic to determine: | |
// 1- The uppercase for a key object | |
// 2- When a key is a special key | |
var init = function kr_init(uppercaseFunction, keyTest) { | |
getUpperCaseValue = uppercaseFunction; | |
isSpecialKey = keyTest; | |
this.ime = document.getElementById('keyboard'); | |
} | |
var setInputMethodName = function(name) { | |
var candidatePanel = document.getElementById('keyboard-candidate-panel'); | |
if (candidatePanel) { | |
if (inputMethodName) | |
candidatePanel.classList.remove(inputMethodName); | |
candidatePanel.classList.add(name); | |
} | |
var togglebutton = | |
document.getElementById('keyboard-candidate-panel-toggle-button'); | |
if (togglebutton) { | |
if (inputMethodName) | |
togglebutton.classList.remove(inputMethodName); | |
togglebutton.classList.add(name); | |
} | |
inputMethodName = name; | |
} | |
// Accepts three values: true / 'locked' / false | |
// Use 'locked' when caps are locked | |
// Use true when uppercase is enabled | |
// Use false when uppercase if disabled | |
var setUpperCaseLock = function kr_setUpperCaseLock(state) { | |
var capsLockKey = document.querySelector( | |
'button[data-keycode="' + KeyboardEvent.DOM_VK_CAPS_LOCK + '"]' | |
); | |
if (!capsLockKey) | |
return; | |
if (state === 'locked') { | |
capsLockKey.classList.remove('kbr-key-active'); | |
capsLockKey.classList.add('kbr-key-hold'); | |
} else if (state) { | |
capsLockKey.classList.add('kbr-key-active'); | |
capsLockKey.classList.remove('kbr-key-hold'); | |
} else { | |
capsLockKey.classList.remove('kbr-key-active'); | |
capsLockKey.classList.remove('kbr-key-hold'); | |
} | |
} | |
// Draw the keyboard and its components. Meat is here. | |
var draw = function kr_draw(layout, flags) { | |
flags = flags || {}; | |
// change scale (Our target screen width is 320px) | |
// TODO get document.documentElement.style.fontSize | |
// and use it for multipling changeScale deppending on the value of pixel | |
// density used in media queries | |
var content = ''; | |
layoutWidth = layout.width || 10; | |
var totalWidth = document.getElementById('keyboard').clientWidth; | |
var placeHolderWidth = totalWidth / layoutWidth; | |
inputType = flags.inputType || 'text'; | |
layout.upperCase = layout.upperCase || {}; | |
var first = true; | |
layout.keys.forEach((function buildKeyboardRow(row, nrow) { | |
var firstRow = ''; | |
if (first) { | |
firstRow = ' first-row'; | |
first = false; | |
} | |
content += '<div class="keyboard-row' + firstRow + '">'; | |
row.forEach((function buildKeyboardColumns(key, ncolumn) { | |
var keyChar = key.value; | |
var overrides = layout[flags.inputType + 'Overrides']; | |
// Handle uppercase | |
if (flags.uppercase) { | |
keyChar = getUpperCaseValue(key); | |
} | |
// Handle override | |
var code; | |
if (overrides && overrides[keyChar]) { | |
keyChar = overrides[keyChar]; | |
code = keyChar.charCodeAt(0); | |
} else { | |
code = key.keyCode || keyChar.charCodeAt(0); | |
} | |
var className = isSpecialKey(key) ? 'special-key' : ''; | |
var ratio = key.ratio || 1; | |
var keyWidth = placeHolderWidth * ratio; | |
var dataset = [{'key': 'row', 'value': nrow}]; | |
dataset.push({'key': 'column', 'value': ncolumn}); | |
dataset.push({'key': 'keycode', 'value': code}); | |
if (key.compositeKey) { | |
dataset.push({'key': 'compositekey', 'value': key.compositeKey}); | |
} | |
content += buildKey(keyChar, className, keyWidth + 'px', | |
dataset, key.altNote); | |
})); | |
content += '</div>'; | |
})); | |
// Append empty accent char menu and key highlight into content HTML | |
content += '<span id="keyboard-accent-char-menu-out">' + | |
'<span id="keyboard-accent-char-menu"></span></span>'; | |
content += '<span id="keyboard-key-highlight"></span>'; | |
this.ime.innerHTML = content; | |
this.menu = document.getElementById('keyboard-accent-char-menu'); | |
// Builds candidate panel | |
if (layout.needsCandidatePanel || flags.showCandidatePanel) { | |
this.ime.insertBefore( | |
candidatePanelToggleButtonCode(), this.ime.firstChild); | |
this.ime.insertBefore(candidatePanelCode(), this.ime.firstChild); | |
this.ime.insertBefore(pendingSymbolPanelCode(), this.ime.firstChild); | |
showPendingSymbols(''); | |
showCandidates([], true); | |
} | |
resizeUI(layout); | |
}; | |
var showIME = function hm_showIME() { | |
delete this.ime.dataset.hidden; | |
this.ime.classList.remove('hide'); | |
} | |
var hideIME = function km_hideIME() { | |
this.ime.classList.add('hide'); | |
this.ime.classList.remove('candidate-panel'); | |
this.ime.dataset.hidden = 'true'; | |
}; | |
// Highlight a key | |
var highlightKey = function kr_updateKeyHighlight(key, alternativeKey) { | |
if(inputType == 'tel' && !key.classList.contains('special-key')) { | |
key.classList.add('highlighted-big-key'); | |
} else { | |
key.classList.add('highlighted'); | |
} | |
if (alternativeKey) { | |
var spanToReplace = key.querySelector('.visual-wrapper span'); | |
spanToReplace.textContent = alternativeKey; | |
} | |
}; | |
// Unhighlight a key | |
var unHighlightKey = function kr_unHighlightKey(key) { | |
if(key.classList.contains('highlighted')) { | |
key.classList.remove('highlighted'); | |
} else { | |
key.classList.remove('highlighted-big-key'); | |
} | |
}; | |
// Show pending symbols with highlight (selection) if provided | |
var showPendingSymbols = function km_showPendingSymbols(symbols, | |
highlightStart, | |
highlightEnd, | |
highlightState) { | |
var HIGHLIGHT_COLOR_TABLE = { | |
'red': 'keyboard-pending-symbols-highlight-red', | |
'green': 'keyboard-pending-symbols-highlight-green', | |
'blue': 'keyboard-pending-symbols-highlight-blue' | |
}; | |
// TODO: Save the element | |
var pendingSymbolPanel = | |
document.getElementById('keyboard-pending-symbol-panel'); | |
if (pendingSymbolPanel) { | |
if (typeof highlightStart === 'undefined' || | |
typeof highlightEnd === 'undefined' || | |
typeof highlightState === 'undefined') { | |
pendingSymbolPanel.textContent = symbols; | |
return; | |
} | |
pendingSymbolPanel.innerHTML = "<span class='" + | |
HIGHLIGHT_COLOR_TABLE[highlightState] + | |
"'>" + | |
symbols.slice( | |
highlightStart, highlightEnd) + | |
'</span>' + | |
symbols.substr(highlightEnd); | |
} | |
}; | |
// Show candidates | |
// Each candidate is a string or an array of two strings | |
var showCandidates = function(candidates, noWindowHeightUpdate) { | |
var ime = document.getElementById('keyboard'); | |
// TODO: Save the element | |
var candidatePanel = document.getElementById('keyboard-candidate-panel'); | |
var isFullView = ime.classList.contains('full-candidate-panel'); | |
if (candidatePanel) { | |
candidatePanel.innerHTML = ''; | |
candidatePanel.scrollTop = candidatePanel.scrollLeft = 0; | |
// If there were too many candidate | |
delete candidatePanel.dataset.truncated; | |
if (candidates.length > 74) { | |
candidates = candidates.slice(0, 74); | |
candidatePanel.dataset.truncated = true; | |
} | |
candidates.forEach(function buildCandidateEntry(candidate) { | |
var span = document.createElement('span'); | |
span.dataset.selection = true; | |
if (typeof candidate === 'string') { | |
span.dataset.data = span.textContent = candidate; | |
} | |
else { | |
span.dataset.data = candidate[1]; | |
span.textContent = candidate[0]; | |
} | |
candidatePanel.appendChild(span); | |
}); | |
} | |
}; | |
// Show keyboard alternatives | |
var showKeyboardAlternatives = function(key, keyboards, current, switchCode) { | |
var dataset, className, content = ''; | |
var menu = this.menu; | |
var cssWidth = key.style.width; | |
menu.classList.add('kbr-menu-lang'); | |
key.classList.add('kbr-menu-on'); | |
var alreadyAdded = {}; | |
for (var i = 0, kbr; kbr = keyboards[i]; i += 1) { | |
if (alreadyAdded[kbr]) | |
continue; | |
className = 'keyboard-key'; | |
if (kbr === current) | |
className += ' kbr-key-hold'; | |
dataset = [ | |
{key: 'keyboard', value: kbr}, | |
{key: 'keycode', value: switchCode} | |
]; | |
content += buildKey( | |
Keyboards[kbr].menuLabel, | |
className, cssWidth + 'px', | |
dataset | |
); | |
alreadyAdded[kbr] = true; | |
} | |
menu.innerHTML = content; | |
// Replace with the container | |
_altContainer = document.createElement('div'); | |
_altContainer.style.display = 'inline-block'; | |
_altContainer.style.width = key.style.width; | |
_altContainer.innerHTML = key.innerHTML; | |
_altContainer.className = key.className; | |
_menuKey = key; | |
key.parentNode.replaceChild(_altContainer, key); | |
_altContainer | |
.querySelectorAll('.visual-wrapper > span')[0] | |
.appendChild(menu); | |
menu.style.display = 'block'; | |
}; | |
// Show char alternatives. | |
var showAlternativesCharMenu = function(key, altChars) { | |
var content = ''; | |
var left = (window.innerWidth / 2 > key.offsetLeft); | |
// Place the menu to the left | |
if (left) { | |
this.menu.classList.add('kbr-menu-left'); | |
// Place menu on the right and reverse key order | |
} else { | |
this.menu.classList.add('kbr-menu-right'); | |
altChars = altChars.reverse(); | |
} | |
// How wide (in characters) is the key that we're displaying | |
// these alternatives for? | |
var keycharwidth = key.dataset.compositeKey ? | |
key.dataset.compositeKey.length : | |
1; | |
// Build a key for each alternative | |
altChars.forEach(function(alt) { | |
var dataset = alt.length == 1 ? | |
[{'key': 'keycode', 'value': alt.charCodeAt(0)}] : | |
[{'key': 'compositekey', 'value': alt}]; | |
// Make each of these alternative keys 75% as wide as the key that | |
// it is an alternative for, but adjust for the relative number of | |
// characters in the original and the alternative | |
var width = 0.75 * key.offsetWidth / keycharwidth * alt.length; | |
// If there is only one alternative, then display it at least as | |
// wide as the original key. | |
if (altChars.length === 1) | |
width = Math.max(width, key.offsetWidth); | |
content += buildKey(alt, '', width + 'px', dataset); | |
}); | |
this.menu.innerHTML = content; | |
// Replace with the container | |
_altContainer = document.createElement('div'); | |
_altContainer.style.display = 'inline-block'; | |
_altContainer.style.width = key.style.width; | |
_altContainer.innerHTML = key.innerHTML; | |
if(key.classList.contains("highlighted-big-key")) { | |
key.classList.remove("highlighted-big-key"); | |
key.classList.add("highlighted"); | |
} | |
_altContainer.className = key.className; | |
_altContainer.classList.add('kbr-menu-on'); | |
_menuKey = key; | |
key.parentNode.replaceChild(_altContainer, key); | |
// Adjust menu style | |
_altContainer | |
.querySelectorAll('.visual-wrapper > span')[0] | |
.appendChild(this.menu); | |
this.menu.style.display = 'block'; | |
// Adjust offset when alternatives menu overflows | |
var alternativesLeft = getWindowLeft(this.menu); | |
var alternativesRight = alternativesLeft + this.menu.offsetWidth; | |
// It overflows on the right | |
if (left && alternativesRight > window.innerWidth) { | |
console.log('overflowing right'); | |
var offset = window.innerWidth - alternativesRight; | |
console.log(offset); | |
this.menu.style.left = offset + 'px'; | |
// It overflows on the left | |
} else if (!left && alternativesLeft < 0) { | |
console.log('overflowing left'); | |
var offset = alternativesLeft; | |
console.log(offset); | |
this.menu.style.right = offset + 'px'; | |
} | |
}; | |
// Hide the alternative menu | |
var hideAlternativesCharMenu = function km_hideAlternativesCharMenu() { | |
this.menu = document.getElementById('keyboard-accent-char-menu'); | |
this.menu.innerHTML = ''; | |
this.menu.className = ''; | |
this.menu.style.display = 'none'; | |
if (_altContainer) | |
_altContainer.parentNode.replaceChild(_menuKey, _altContainer); | |
this.menu.style.left = ''; | |
this.menu.style.right = ''; | |
}; | |
var _keyArray = []; // To calculate proximity info for predictive text | |
// Recalculate dimensions for the current render | |
var resizeUI = function(layout) { | |
var changeScale, scale; | |
// Font size recalc | |
var ime = document.getElementById('keyboard'); | |
if (window.innerWidth <= window.innerHeight) { | |
changeScale = window.innerWidth / 32; | |
document.documentElement.style.fontSize = changeScale + 'px'; | |
ime.classList.remove('landscape'); | |
ime.classList.add('portrait'); | |
} else { | |
changeScale = window.innerWidth / 64; | |
document.documentElement.style.fontSize = changeScale + 'px'; | |
ime.classList.remove('portrait'); | |
ime.classList.add('landscape'); | |
} | |
_keyArray = []; | |
// Width calc | |
if (layout) { | |
layoutWidth = layout.width || 10; | |
var totalWidth = document.getElementById('keyboard').clientWidth; | |
var placeHolderWidth = totalWidth / layoutWidth; | |
var ratio, keys, rows = document.querySelectorAll('.keyboard-row'); | |
for (var r = 0, row; row = rows[r]; r += 1) { | |
keys = row.childNodes; | |
for (var k = 0, key; key = keys[k]; k += 1) { | |
ratio = layout.keys[r][k].ratio || 1; | |
key.style.width = (placeHolderWidth * ratio) + 'px'; | |
// to get the visual width/height of the key | |
// for better proximity info | |
var visualKey = key.querySelector('.visual-wrapper'); | |
_keyArray.push({ | |
code: key.dataset.keycode | 0, | |
x: visualKey.offsetLeft, | |
y: visualKey.offsetTop, | |
width: visualKey.clientWidth, | |
height: visualKey.clientHeight | |
}); | |
} | |
} | |
} | |
}; | |
// | |
// Private Methods | |
// | |
//* | |
// Method that generates the HTML markup for each key | |
// @label: String inside the key | |
// @className: String representing a className to be added to the key | |
// @width: Int to be applied as moz-box-flex | |
// @dataset: Array of Hash with every { 'key': KEY, 'value': VALUE} | |
// to be applied as dataset | |
//* | |
var pendingSymbolPanelCode = function() { | |
var pendingSymbolPanel = document.createElement('div'); | |
pendingSymbolPanel.id = 'keyboard-pending-symbol-panel'; | |
return pendingSymbolPanel; | |
}; | |
var candidatePanelCode = function() { | |
var candidatePanel = document.createElement('div'); | |
candidatePanel.id = 'keyboard-candidate-panel'; | |
if (inputMethodName) | |
candidatePanel.classList.add(inputMethodName); | |
return candidatePanel; | |
}; | |
var candidatePanelToggleButtonCode = function() { | |
var toggleButton = document.createElement('span'); | |
toggleButton.innerHTML = '⇪'; | |
toggleButton.id = 'keyboard-candidate-panel-toggle-button'; | |
if (inputMethodName) | |
toggleButton.classList.add(inputMethodName); | |
toggleButton.dataset.keycode = -4; | |
return toggleButton; | |
}; | |
var buildKey = function buildKey(label, className, width, dataset, altNote) { | |
var altNoteHTML = ''; | |
if (altNote) { | |
altNoteHTML = '<div class="alt-note">' + altNote + '</div>'; | |
} | |
var content = '<button class="keyboard-key ' + className + '"'; | |
dataset.forEach(function(data) { | |
content += ' data-' + data.key + '="' + data.value + '" '; | |
}); | |
content += ' style="width: ' + width + '"'; | |
content += '><span class="visual-wrapper"><span>' + | |
label + '</span>' + altNoteHTML + '</span></button>'; | |
return content; | |
}; | |
var getWidth = function getWidth() { | |
if (!this.ime) | |
return 0; | |
return this.ime.clientWidth; | |
}; | |
var getHeight = function getHeight() { | |
if (!this.ime) | |
return 0; | |
return this.ime.clientHeight; | |
}; | |
var getKeyArray = function getKeyArray() { | |
return _keyArray; | |
}; | |
var getKeyWidth = function getKeyWidth() { | |
if (!this.ime) | |
return 0; | |
return Math.ceil(this.ime.clientWidth / layoutWidth); | |
}; | |
var getKeyHeight = function getKeyHeight() { | |
if (!this.ime) | |
return 0; | |
this.ime.clientHeight; | |
var rows = document.querySelectorAll('.keyboard-row'); | |
var rowCount = rows.length || 3; | |
return Math.ceil(this.ime.clientHeight / rowCount); | |
}; | |
// Exposing pattern | |
return { | |
'init': init, | |
'setInputMethodName': setInputMethodName, | |
'draw': draw, | |
'ime': ime, | |
'hideIME': hideIME, | |
'showIME': showIME, | |
'highlightKey': highlightKey, | |
'unHighlightKey': unHighlightKey, | |
'showAlternativesCharMenu': showAlternativesCharMenu, | |
'showKeyboardAlternatives': showKeyboardAlternatives, | |
'hideAlternativesCharMenu': hideAlternativesCharMenu, | |
'setUpperCaseLock': setUpperCaseLock, | |
'resizeUI': resizeUI, | |
'showCandidates': showCandidates, | |
'showPendingSymbols': showPendingSymbols, | |
'getWidth': getWidth, | |
'getHeight': getHeight, | |
'getKeyArray': getKeyArray, | |
'getKeyWidth': getKeyWidth, | |
'getKeyHeight': getKeyHeight | |
}; | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment